mirror of
https://github.com/JHDev2006/Super-Mario-Bros.-Remastered-Public.git
synced 2025-10-22 15:38:14 +00:00
added the game
This commit is contained in:
95
Scripts/Classes/Blocks/BlockClass.gd
Normal file
95
Scripts/Classes/Blocks/BlockClass.gd
Normal file
@@ -0,0 +1,95 @@
|
||||
@icon("res://Assets/Sprites/Editor/Block.png")
|
||||
class_name Block
|
||||
extends AnimatableBody2D
|
||||
signal player_block_hit(player: Player)
|
||||
signal shell_block_hit(shell: Shell)
|
||||
|
||||
@export var visuals: Node = null
|
||||
const EMPTY_BLOCK = ("res://Scenes/Prefabs/Blocks/EmptyBlock.tscn")
|
||||
@export var item: PackedScene = null
|
||||
@export var destructable := true
|
||||
@export var destruction_particle_scene: PackedScene = null
|
||||
@export_range(1, 99) var item_amount := 1
|
||||
@export var combo_meter_amount := 25
|
||||
@export var mushroom_if_small := false
|
||||
const SUPER_MUSHROOM = ("res://Scenes/Prefabs/Entities/Items/SuperMushroom.tscn")
|
||||
var can_hit := true
|
||||
var bouncing := false
|
||||
|
||||
const NO_SFX_ITEMS := ["res://Scenes/Prefabs/Entities/Items/SpinningRedCoin.tscn","res://Scenes/Prefabs/Entities/Items/SpinningCoin.tscn", "res://Scenes/Prefabs/Entities/Items/Vine.tscn" ]
|
||||
|
||||
@export var start_z := -1
|
||||
|
||||
signal block_emptied
|
||||
signal block_destroyed
|
||||
|
||||
func _enter_tree() -> void:
|
||||
z_index = start_z
|
||||
sync_to_physics = false
|
||||
if item != null:
|
||||
if item.resource_path.contains(Global.current_level.scene_file_path):
|
||||
Global.log_error("ITEM SCENE IS NULL! BLOCK NAME: " + str(name) + " PLEASE REPORT!")
|
||||
|
||||
func dispense_item() -> void:
|
||||
if can_hit == false:
|
||||
return
|
||||
can_hit = false
|
||||
await get_tree().create_timer(0.1, false).timeout
|
||||
DiscoLevel.combo_meter += combo_meter_amount
|
||||
var item_to_dispense = player_mushroom_check(get_tree().get_first_node_in_group("Players"))
|
||||
var node = item_to_dispense.instantiate()
|
||||
if node is PowerUpItem or node.has_meta("is_item"):
|
||||
for i in get_tree().get_nodes_in_group("Players"):
|
||||
node.position = position + Vector2(0, -1)
|
||||
node.hide()
|
||||
add_sibling(node)
|
||||
if node is PowerUpItem:
|
||||
if Global.connected_players > 1:
|
||||
AudioManager.play_sfx("item_appear", global_position)
|
||||
node.player_multiplayer_launch_spawn(i)
|
||||
else:
|
||||
node.block_dispense_tween()
|
||||
else:
|
||||
if item.resource_path == "res://Scenes/Prefabs/Entities/Items/SpinningRedCoin.tscn":
|
||||
if has_meta("r_coin_id"):
|
||||
node.id = get_meta("r_coin_id", 0)
|
||||
var parent = get_parent()
|
||||
node.global_position = global_position + Vector2(0, -8) + node.get_meta("block_spawn_offset", Vector2.ZERO)
|
||||
if get_parent().get_parent() is TrackRider:
|
||||
parent = get_parent().get_parent().get_parent()
|
||||
parent.add_child(node)
|
||||
parent.move_child(node, get_index() - 1)
|
||||
print("FUCK: " + str(item.resource_path))
|
||||
if NO_SFX_ITEMS.has(item.resource_path) == false:
|
||||
AudioManager.play_sfx("item_appear", global_position)
|
||||
node.set("velocity", Vector2(0, node.get_meta("block_launch_velocity", -150)))
|
||||
can_hit = true
|
||||
item_amount -= 1
|
||||
if item_amount == 1:
|
||||
if has_meta("red_coin") == true:
|
||||
item = load("res://Scenes/Prefabs/Entities/Items/SpinningRedCoin.tscn")
|
||||
if item_amount <= 0:
|
||||
spawn_empty_block()
|
||||
|
||||
func player_mushroom_check(player: Player = null) -> PackedScene:
|
||||
if player.power_state.hitbox_size == "Small" and mushroom_if_small:
|
||||
return load(SUPER_MUSHROOM)
|
||||
return item
|
||||
|
||||
func spawn_empty_block() -> void:
|
||||
var block = load(EMPTY_BLOCK).instantiate()
|
||||
block.position = position
|
||||
add_sibling(block)
|
||||
if get_parent().get_parent() is TrackRider:
|
||||
get_parent().get_parent().attached_entity = block
|
||||
block_emptied.emit()
|
||||
queue_free()
|
||||
|
||||
func destroy() -> void:
|
||||
block_destroyed.emit()
|
||||
DiscoLevel.combo_meter += combo_meter_amount
|
||||
AudioManager.play_sfx("block_break", global_position)
|
||||
var particles = destruction_particle_scene.instantiate()
|
||||
particles.global_position = global_position
|
||||
add_sibling(particles)
|
||||
queue_free()
|
1
Scripts/Classes/Blocks/BlockClass.gd.uid
Executable file
1
Scripts/Classes/Blocks/BlockClass.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://b5ejlbl0vp1gm
|
43
Scripts/Classes/Blocks/BooOnOffBlock.gd
Normal file
43
Scripts/Classes/Blocks/BooOnOffBlock.gd
Normal file
@@ -0,0 +1,43 @@
|
||||
extends StaticBody2D
|
||||
|
||||
@export var active := false
|
||||
|
||||
@onready var start_active = not active
|
||||
|
||||
var player_in_area := false
|
||||
|
||||
var player_stuck := false
|
||||
|
||||
var awaiting_exit := false
|
||||
|
||||
@export var hurtbox: CollisionShape2D = null
|
||||
|
||||
func on_switch_hit() -> void:
|
||||
player_stuck = false
|
||||
active = not active
|
||||
if player_in_area:
|
||||
player_stuck = true
|
||||
return
|
||||
update()
|
||||
|
||||
func update() -> void:
|
||||
if active:
|
||||
$Sprite.play("On")
|
||||
else:
|
||||
$Sprite.play("Off")
|
||||
$Collision.set_deferred("disabled", not active)
|
||||
if hurtbox != null:
|
||||
hurtbox.set_deferred("disabled", not active)
|
||||
|
||||
func damage_player(player: Player) -> void:
|
||||
player.damage()
|
||||
|
||||
func on_player_entered(_player: Player) -> void:
|
||||
player_in_area = true
|
||||
|
||||
|
||||
func on_player_exited(_player: Player) -> void:
|
||||
player_in_area = false
|
||||
if player_stuck and active:
|
||||
player_stuck = false
|
||||
update()
|
1
Scripts/Classes/Blocks/BooOnOffBlock.gd.uid
Executable file
1
Scripts/Classes/Blocks/BooOnOffBlock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://be2x40pxmueyo
|
33
Scripts/Classes/Blocks/BooOnOffSwitch.gd
Normal file
33
Scripts/Classes/Blocks/BooOnOffSwitch.gd
Normal file
@@ -0,0 +1,33 @@
|
||||
extends Block
|
||||
|
||||
var active := false
|
||||
|
||||
static var has_hit := false
|
||||
|
||||
func _ready() -> void:
|
||||
can_hit = true
|
||||
has_hit = false
|
||||
|
||||
func on_block_hit() -> void:
|
||||
if can_hit == false or has_hit:
|
||||
return
|
||||
has_hit = true
|
||||
AudioManager.play_sfx("switch", global_position)
|
||||
can_hit = false
|
||||
get_tree().call_group("BooBlocks", "on_switch_hit")
|
||||
await get_tree().create_timer(0.25, false).timeout
|
||||
can_hit = true
|
||||
has_hit = false
|
||||
|
||||
func on_switch_hit() -> void:
|
||||
active = not active
|
||||
if active:
|
||||
$Sprite.play("On")
|
||||
else:
|
||||
$Sprite.play("Off")
|
||||
|
||||
func on_boo_hit() -> void:
|
||||
if active:
|
||||
return
|
||||
AudioManager.play_global_sfx("switch")
|
||||
get_tree().call_group("BooBlocks", "on_switch_hit")
|
1
Scripts/Classes/Blocks/BooOnOffSwitch.gd.uid
Executable file
1
Scripts/Classes/Blocks/BooOnOffSwitch.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bm72ggve5kda5
|
31
Scripts/Classes/Blocks/BrickBlock.gd
Normal file
31
Scripts/Classes/Blocks/BrickBlock.gd
Normal file
@@ -0,0 +1,31 @@
|
||||
class_name BrickBlock
|
||||
extends Block
|
||||
|
||||
var ticking_down := false
|
||||
|
||||
func _ready() -> void:
|
||||
$PSwitcher.enabled = item == null
|
||||
if item_amount == 10 and item.resource_path == "res://Scenes/Prefabs/Entities/Items/SpinningCoin.tscn" and is_instance_valid(Global.level_editor) == false:
|
||||
Global.log_warning("Coin Brick Block is wrong! please report!: " + name)
|
||||
|
||||
func on_block_hit(player: Player) -> void:
|
||||
if player.power_state.hitbox_size == "Big":
|
||||
if item == null:
|
||||
await get_tree().physics_frame
|
||||
destroy()
|
||||
Global.score += 50
|
||||
if item != null:
|
||||
if mushroom_if_small:
|
||||
item = player_mushroom_check(player)
|
||||
dispense_item()
|
||||
|
||||
func on_shell_block_hit(_shell: Shell) -> void:
|
||||
if item == null:
|
||||
await get_tree().physics_frame
|
||||
destroy()
|
||||
Global.score += 50
|
||||
else:
|
||||
dispense_item()
|
||||
|
||||
func set_coin_count() -> void:
|
||||
item_amount = 2
|
1
Scripts/Classes/Blocks/BrickBlock.gd.uid
Executable file
1
Scripts/Classes/Blocks/BrickBlock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://y53avulrmfx1
|
37
Scripts/Classes/Blocks/DonutBlock.gd
Normal file
37
Scripts/Classes/Blocks/DonutBlock.gd
Normal file
@@ -0,0 +1,37 @@
|
||||
extends StaticBody2D
|
||||
|
||||
var falling := false
|
||||
var can_fall := false
|
||||
|
||||
const FALL_SPEED := 96
|
||||
|
||||
@onready var starting_position := global_position
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if falling:
|
||||
global_position.y += FALL_SPEED * delta
|
||||
if $PlayerDetection.is_player_in_area():
|
||||
$Sprite.play("Fall")
|
||||
elif not falling:
|
||||
$Sprite.play("Idle")
|
||||
|
||||
func start_falling() -> void:
|
||||
falling = true
|
||||
$Collision.set_deferred("one_way_collision", true)
|
||||
$FallTimer.start()
|
||||
|
||||
|
||||
func respawn() -> void:
|
||||
$Collision.set_deferred("one_way_collision", false)
|
||||
can_fall = true
|
||||
falling = false
|
||||
global_position = starting_position
|
||||
$AnimationPlayer.play("Grow")
|
||||
|
||||
|
||||
func on_player_entered() -> void:
|
||||
$AnimationPlayer.play("Shake")
|
||||
|
||||
|
||||
func on_player_exited() -> void:
|
||||
$AnimationPlayer.play("RESET")
|
1
Scripts/Classes/Blocks/DonutBlock.gd.uid
Executable file
1
Scripts/Classes/Blocks/DonutBlock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://dbu4juvw4veul
|
11
Scripts/Classes/Blocks/FallThroughBlock.gd
Normal file
11
Scripts/Classes/Blocks/FallThroughBlock.gd
Normal file
@@ -0,0 +1,11 @@
|
||||
extends StaticBody2D
|
||||
|
||||
|
||||
func on_player_entered(_player: Player) -> void:
|
||||
$Sprite.play("Turn")
|
||||
await get_tree().physics_frame
|
||||
$Collision.set_deferred("disabled", true)
|
||||
|
||||
func on_player_exited(_player: Player) -> void:
|
||||
$Sprite.play("Idle")
|
||||
$Collision.set_deferred("disabled", false)
|
1
Scripts/Classes/Blocks/FallThroughBlock.gd.uid
Normal file
1
Scripts/Classes/Blocks/FallThroughBlock.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cn8jml0dmf128
|
10
Scripts/Classes/Blocks/InvisibleQuestionBlock.gd
Normal file
10
Scripts/Classes/Blocks/InvisibleQuestionBlock.gd
Normal file
@@ -0,0 +1,10 @@
|
||||
extends Block
|
||||
|
||||
func on_area_entered(area: Area2D) -> void:
|
||||
if area.owner is Player:
|
||||
var player: Player = area.owner
|
||||
if player.velocity.y < 0 and player.global_position.y > $Hitbox.global_position.y and abs(player.global_position.x - global_position.x) < 8:
|
||||
player_block_hit.emit(area.owner)
|
||||
player.velocity.y = 0
|
||||
player.bump_ceiling()
|
||||
$Collision.set_deferred("disabled", false)
|
1
Scripts/Classes/Blocks/InvisibleQuestionBlock.gd.uid
Executable file
1
Scripts/Classes/Blocks/InvisibleQuestionBlock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://s5e3ps0g7nya
|
55
Scripts/Classes/Blocks/MusicNoteBlock.gd
Normal file
55
Scripts/Classes/Blocks/MusicNoteBlock.gd
Normal file
@@ -0,0 +1,55 @@
|
||||
extends NoteBlock
|
||||
|
||||
const INTRUMENT_SFX := [preload("uid://dia0bsspwrqsn"), preload("uid://d2elbhakm1yfq"), preload("uid://vxox7t6qvyvu"), preload("uid://w44jys81bxjj"), preload("uid://b2lj4akov8ami"), preload("uid://c03nay4r4a2lm"), preload("uid://d0pdbnpfcm80i"), preload("uid://dodww1no4v6qh")]
|
||||
|
||||
var pitch := 0.0
|
||||
var sfx_stream = null
|
||||
|
||||
static var can_play := false
|
||||
|
||||
@export var play_on_load := false
|
||||
|
||||
@export_enum("Bass", "Flute", "Marimba", "Piano", "Rhodes", "Steel", "Trumpet", "Violin") var instrument := 0:
|
||||
set(value):
|
||||
sfx_stream = INTRUMENT_SFX[value]
|
||||
instrument = value
|
||||
play_sfx_preview()
|
||||
|
||||
@export_enum("A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#") var note := 3:
|
||||
set(value):
|
||||
note = value
|
||||
pitch = get_pitch_scale()
|
||||
play_sfx_preview()
|
||||
|
||||
@export_range(1, 5) var octave := 2:
|
||||
set(value):
|
||||
octave = value
|
||||
pitch = get_pitch_scale()
|
||||
play_sfx_preview()
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().create_timer(0.1, true).timeout
|
||||
can_play = true
|
||||
|
||||
func _exit_tree() -> void:
|
||||
can_play = false
|
||||
|
||||
func get_pitch_scale() -> float:
|
||||
var semitone_offset = (octave - 2) * 12 + (note - 3) # C4 is the base note (note index 3)
|
||||
return 2.0 ** (semitone_offset / 12.0)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
%Note.frame = note
|
||||
%Octave.frame = octave + 12
|
||||
|
||||
func play_sfx_preview() -> void:
|
||||
if get_node_or_null("Instrument") != null and can_play:
|
||||
print($Instrument.pitch_scale)
|
||||
$Instrument.stream = sfx_stream
|
||||
$Instrument.pitch_scale = pitch
|
||||
$Instrument.play()
|
||||
|
||||
|
||||
func on_screen_entered() -> void:
|
||||
if play_on_load and LevelEditor.playing_level:
|
||||
play_sfx_preview()
|
1
Scripts/Classes/Blocks/MusicNoteBlock.gd.uid
Normal file
1
Scripts/Classes/Blocks/MusicNoteBlock.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://2gxl5hj6mf6a
|
70
Scripts/Classes/Blocks/NoteBlock.gd
Normal file
70
Scripts/Classes/Blocks/NoteBlock.gd
Normal file
@@ -0,0 +1,70 @@
|
||||
class_name NoteBlock
|
||||
extends Block
|
||||
|
||||
var bodies: Array[CharacterBody2D] = []
|
||||
|
||||
signal bounced
|
||||
var animating := false
|
||||
@export var play_sfx := true
|
||||
|
||||
func bounce_up() -> void:
|
||||
if bouncing or animating:
|
||||
return
|
||||
bounced.emit()
|
||||
bouncing = true
|
||||
animating = true
|
||||
%Animations.play("BounceUp")
|
||||
dispense_item(-1)
|
||||
await %Animations.animation_finished
|
||||
bouncing = false
|
||||
animating = false
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
for i in %Area.get_overlapping_areas():
|
||||
if i.owner is CharacterBody2D:
|
||||
bounce_down(i.owner)
|
||||
|
||||
func bounce_down(body: PhysicsBody2D) -> void:
|
||||
if bouncing or animating:
|
||||
return
|
||||
animating = true
|
||||
bounced.emit()
|
||||
if play_sfx:
|
||||
AudioManager.play_sfx("note_block", global_position)
|
||||
bodies.append(body)
|
||||
if body is Player:
|
||||
body.normal_state.jump_queued = false
|
||||
body.spring_bouncing = true
|
||||
%Animations.play("BounceDown")
|
||||
dispense_item(1)
|
||||
await %Animations.animation_finished
|
||||
animating = false
|
||||
bouncing = false
|
||||
|
||||
func bounce_bodies() -> void:
|
||||
for i in bodies:
|
||||
if i is Player:
|
||||
i.spring_bouncing = false
|
||||
if Global.player_action_pressed("jump", i.player_id):
|
||||
i.jump_cancelled = false
|
||||
i.has_jumped = true
|
||||
i.velocity.y = -350
|
||||
i.gravity = i.JUMP_GRAVITY
|
||||
else:
|
||||
i.velocity.y = -300
|
||||
i.gravity = i.FALL_GRAVITY
|
||||
else:
|
||||
i.velocity.y = -200
|
||||
if i is Thwomp:
|
||||
i.velocity = Vector2.ZERO
|
||||
bodies.clear()
|
||||
|
||||
func dispense_item(direction := -1) -> void:
|
||||
if item == null or item_amount <= 0:
|
||||
return
|
||||
item_amount -= 1
|
||||
var node = item.instantiate()
|
||||
node.global_position = global_position + Vector2(0, 8 * direction)
|
||||
node.set("velocity", Vector2(0, (100 if direction == 1 else -150)))
|
||||
add_sibling(node)
|
||||
AudioManager.play_sfx("item_appear", global_position)
|
1
Scripts/Classes/Blocks/NoteBlock.gd.uid
Normal file
1
Scripts/Classes/Blocks/NoteBlock.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c0diue1hemrxq
|
22
Scripts/Classes/Blocks/SpinningTurnBlock.gd
Normal file
22
Scripts/Classes/Blocks/SpinningTurnBlock.gd
Normal file
@@ -0,0 +1,22 @@
|
||||
extends Node2D
|
||||
const TURN_BLOCK = ("uid://bn651dli8j2rj")
|
||||
|
||||
var can_turn_back := false
|
||||
|
||||
func _ready() -> void:
|
||||
$Sprite.frame = 1
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
if can_turn_back:
|
||||
if $PlayerDetectionArea.get_overlapping_areas().any(func(area: Area2D) -> bool: return area.owner is Player) == false:
|
||||
spawn_block()
|
||||
can_turn_back = false
|
||||
|
||||
func on_timeout() -> void:
|
||||
can_turn_back = true
|
||||
|
||||
func spawn_block() -> void:
|
||||
var block = load(TURN_BLOCK).instantiate()
|
||||
block.position = position
|
||||
add_sibling(block)
|
||||
queue_free()
|
1
Scripts/Classes/Blocks/SpinningTurnBlock.gd.uid
Executable file
1
Scripts/Classes/Blocks/SpinningTurnBlock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://7evxxqgv6e3j
|
16
Scripts/Classes/Blocks/SpringBlock.gd
Normal file
16
Scripts/Classes/Blocks/SpringBlock.gd
Normal file
@@ -0,0 +1,16 @@
|
||||
extends StaticBody2D
|
||||
|
||||
@export var is_super := false
|
||||
|
||||
func on_player_entered(player: Player) -> void:
|
||||
player.enemy_bounce_off(false)
|
||||
play_animation()
|
||||
AudioManager.play_sfx("spring", global_position)
|
||||
if is_super:
|
||||
await get_tree().physics_frame
|
||||
player.velocity.y *= 1.5
|
||||
|
||||
func play_animation() -> void:
|
||||
$Sprite.play("Bounce")
|
||||
await $Sprite.animation_finished
|
||||
$Sprite.play("Idle")
|
1
Scripts/Classes/Blocks/SpringBlock.gd.uid
Executable file
1
Scripts/Classes/Blocks/SpringBlock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cdlufmypvdtun
|
53
Scripts/Classes/Blocks/TimedBooBlock.gd
Normal file
53
Scripts/Classes/Blocks/TimedBooBlock.gd
Normal file
@@ -0,0 +1,53 @@
|
||||
class_name TimedBooBlock
|
||||
extends Block
|
||||
|
||||
var time := 3
|
||||
var active := false
|
||||
|
||||
static var main_block = null
|
||||
|
||||
static var can_tick := true:
|
||||
set(value):
|
||||
can_tick = value
|
||||
|
||||
func _ready() -> void:
|
||||
main_block = self
|
||||
$Timer.start()
|
||||
|
||||
func on_timeout() -> void:
|
||||
if can_tick == false or BooRaceHandler.countdown_active: return
|
||||
time = clamp(time - 1, 0, 3)
|
||||
if main_block == self:
|
||||
if time <= 0:
|
||||
get_tree().call_group("BooBlocks", "on_switch_hit")
|
||||
elif time < 3:
|
||||
AudioManager.play_global_sfx("timer_beep")
|
||||
if active:
|
||||
$Sprite.play("On" + str(time))
|
||||
else:
|
||||
$Sprite.play("Off" + str(time))
|
||||
|
||||
func block_hit() -> void:
|
||||
if not can_hit:
|
||||
return
|
||||
can_hit = false
|
||||
get_tree().call_group("BooBlocks", "on_switch_hit")
|
||||
await get_tree().create_timer(0.25, false).timeout
|
||||
can_hit = true
|
||||
|
||||
func _exit_tree() -> void:
|
||||
can_tick = true
|
||||
|
||||
func on_switch_hit() -> void:
|
||||
AudioManager.play_global_sfx("switch")
|
||||
$Timer.stop()
|
||||
time = 4
|
||||
active = not active
|
||||
if active:
|
||||
$Sprite.play("BlueToRed")
|
||||
else:
|
||||
$Sprite.play("RedToBlue")
|
||||
await $Sprite.animation_finished
|
||||
$Timer.start()
|
||||
time = 4
|
||||
on_timeout()
|
1
Scripts/Classes/Blocks/TimedBooBlock.gd.uid
Executable file
1
Scripts/Classes/Blocks/TimedBooBlock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://du5hnwdhv7x66
|
22
Scripts/Classes/Blocks/TurnBlock.gd
Normal file
22
Scripts/Classes/Blocks/TurnBlock.gd
Normal file
@@ -0,0 +1,22 @@
|
||||
extends Block
|
||||
const SPINNING_TURN_BLOCK = preload("uid://b8dalotrk2oci")
|
||||
func on_block_hit(player: Player) -> void:
|
||||
if item != null:
|
||||
if mushroom_if_small:
|
||||
item = player_mushroom_check(player)
|
||||
dispense_item()
|
||||
else:
|
||||
spin()
|
||||
|
||||
func on_shell_block_hit(_shell: Shell) -> void:
|
||||
if item != null:
|
||||
dispense_item()
|
||||
else:
|
||||
spin()
|
||||
|
||||
func spin() -> void:
|
||||
await get_tree().create_timer(0.15, false).timeout
|
||||
var spinning = SPINNING_TURN_BLOCK.instantiate()
|
||||
spinning.position = position
|
||||
add_sibling(spinning)
|
||||
queue_free()
|
1
Scripts/Classes/Blocks/TurnBlock.gd.uid
Executable file
1
Scripts/Classes/Blocks/TurnBlock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://dm44kedpj4m80
|
97
Scripts/Classes/BooRaceHandler.gd
Normal file
97
Scripts/Classes/BooRaceHandler.gd
Normal file
@@ -0,0 +1,97 @@
|
||||
class_name BooRaceHandler
|
||||
extends Node
|
||||
|
||||
@export var boo: Node2D = null
|
||||
static var boo_colour := 0
|
||||
@export var boo_block_times := [5, 4, 4.5, 3, 3]
|
||||
|
||||
@export var level_id := 0
|
||||
|
||||
static var current_level_id := 0
|
||||
|
||||
@export var is_custom := false
|
||||
|
||||
static var countdown_active := false
|
||||
|
||||
static var best_times := [
|
||||
-1.0, -1.0, -1.0, -1.0,
|
||||
-1.0, -1.0, -1.0, -1.0
|
||||
]
|
||||
|
||||
static var cleared_boo_levels := "00000000"
|
||||
const SILENCE = preload("res://Assets/Audio/BGM/Silence.json")
|
||||
func _ready() -> void:
|
||||
SpeedrunHandler.show_timer = true
|
||||
SpeedrunHandler.timer = 0
|
||||
SpeedrunHandler.timer_active = false
|
||||
SpeedrunHandler.best_time = best_times[level_id]
|
||||
TimedBooBlock.can_tick = false
|
||||
current_level_id = level_id
|
||||
if is_custom == false:
|
||||
Global.current_game_mode = Global.GameMode.BOO_RACE
|
||||
do_countdown()
|
||||
|
||||
|
||||
func do_countdown() -> void:
|
||||
var old_music = Global.current_level.music
|
||||
Global.current_level.music = SILENCE
|
||||
countdown_active = true
|
||||
get_tree().paused = false
|
||||
await get_tree().physics_frame
|
||||
TimedBooBlock.can_tick = false
|
||||
$Animation.play("CountdownBeep")
|
||||
Global.can_time_tick = false
|
||||
for i in get_tree().get_nodes_in_group("Players"):
|
||||
i.state_machine.transition_to("Freeze")
|
||||
await get_tree().create_timer(3, false).timeout
|
||||
Global.can_time_tick = true
|
||||
for i in get_tree().get_nodes_in_group("Players"):
|
||||
i.state_machine.transition_to("Normal")
|
||||
$Timer.wait_time = boo_block_times[boo_colour]
|
||||
$Timer.start()
|
||||
countdown_active = false
|
||||
SpeedrunHandler.start_timer()
|
||||
boo.move_tween()
|
||||
TimedBooBlock.can_tick = true
|
||||
await get_tree().create_timer(0.5, false).timeout
|
||||
Global.current_level.music = old_music
|
||||
|
||||
func tally_time() -> void:
|
||||
pass
|
||||
|
||||
func player_win_race() -> void:
|
||||
SpeedrunHandler.run_finished()
|
||||
run_best_time_check()
|
||||
TimedBooBlock.can_tick = false
|
||||
if int(BooRaceHandler.cleared_boo_levels[level_id]) <= BooRaceHandler.boo_colour:
|
||||
BooRaceHandler.cleared_boo_levels[level_id] = str(BooRaceHandler.boo_colour + 1)
|
||||
print(BooRaceHandler.cleared_boo_levels)
|
||||
SaveManager.write_save(Global.current_campaign)
|
||||
boo.flag_die()
|
||||
if cleared_boo_levels.contains("0") == false:
|
||||
match Global.current_campaign:
|
||||
"SMB1": Global.unlock_achievement(Global.AchievementID.SMB1_BOO)
|
||||
"SMBLL": Global.unlock_achievement(Global.AchievementID.SMBLL_BOO)
|
||||
"SMBS": Global.unlock_achievement(Global.AchievementID.SMBS_BOO)
|
||||
if cleared_boo_levels == "55555555":
|
||||
match Global.current_campaign:
|
||||
"SMB1": Global.unlock_achievement(Global.AchievementID.SMB1_GOLD_BOO)
|
||||
"SMBLL": Global.unlock_achievement(Global.AchievementID.SMBLL_GOLD_BOO)
|
||||
"SMBS": Global.unlock_achievement(Global.AchievementID.SMBS_GOLD_BOO)
|
||||
await tree_exiting
|
||||
if boo_colour < 4:
|
||||
boo_colour += 1
|
||||
|
||||
func run_best_time_check() -> void:
|
||||
if SpeedrunHandler.timer <= best_times[level_id] or best_times[level_id] < 0:
|
||||
best_times[level_id] = SpeedrunHandler.timer
|
||||
|
||||
func _exit_tree() -> void:
|
||||
countdown_active = false
|
||||
|
||||
func on_timeout() -> void:
|
||||
if boo.moving:
|
||||
boo.play_laugh_animation()
|
||||
AudioManager.play_global_sfx("boo_laugh")
|
||||
await get_tree().create_timer(1, false).timeout
|
||||
get_tree().call_group("BooSwitchBlocks", "on_boo_hit")
|
1
Scripts/Classes/BooRaceHandler.gd.uid
Executable file
1
Scripts/Classes/BooRaceHandler.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://toi3fh8d7cs4
|
11
Scripts/Classes/CoinHeaven.gd
Normal file
11
Scripts/Classes/CoinHeaven.gd
Normal file
@@ -0,0 +1,11 @@
|
||||
class_name CoinHeaven
|
||||
extends Level
|
||||
|
||||
@export var all_coins_check: AllCoinsCollectedCheck = null
|
||||
|
||||
func warp_back(player: Player) -> void:
|
||||
player.state_machine.transition_to("Freeze")
|
||||
if all_coins_check != null:
|
||||
await all_coins_check.check()
|
||||
await get_tree().create_timer(1, false).timeout
|
||||
Global.transition_to_scene(Level.vine_return_level)
|
1
Scripts/Classes/CoinHeaven.gd.uid
Executable file
1
Scripts/Classes/CoinHeaven.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cyexnn0e723f2
|
10
Scripts/Classes/Components/AchievementProgressCalculator.gd
Normal file
10
Scripts/Classes/Components/AchievementProgressCalculator.gd
Normal file
@@ -0,0 +1,10 @@
|
||||
class_name AchievementProgressCalculator
|
||||
extends Node
|
||||
|
||||
@export var target_number := 8
|
||||
|
||||
func _ready() -> void:
|
||||
get_progress()
|
||||
|
||||
func get_progress() -> int:
|
||||
return 0
|
@@ -0,0 +1 @@
|
||||
uid://dwrso5q5r5bak
|
17
Scripts/Classes/Components/AnimationPauser.gd
Normal file
17
Scripts/Classes/Components/AnimationPauser.gd
Normal file
@@ -0,0 +1,17 @@
|
||||
class_name AnimationPauser
|
||||
extends Node
|
||||
|
||||
@export var animation_player: AnimationPlayer = null
|
||||
|
||||
@export var paused := false
|
||||
|
||||
signal just_paused
|
||||
signal resumed
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
animation_player.speed_scale = int(not paused)
|
||||
|
||||
func on_switch_hit() -> void:
|
||||
paused = not paused
|
||||
if paused: just_paused.emit()
|
||||
else: resumed.emit()
|
1
Scripts/Classes/Components/AnimationPauser.gd.uid
Normal file
1
Scripts/Classes/Components/AnimationPauser.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cb0spbe0l8mof
|
75
Scripts/Classes/Components/BasicEnemyMovement.gd
Normal file
75
Scripts/Classes/Components/BasicEnemyMovement.gd
Normal file
@@ -0,0 +1,75 @@
|
||||
class_name BasicEnemyMovement
|
||||
extends Node
|
||||
|
||||
@export var ledge_detection_cast: RayCast2D = null
|
||||
|
||||
var can_move := true
|
||||
|
||||
@export var auto_call := true
|
||||
|
||||
@export var move_speed := 32
|
||||
@export var second_quest_speed := 36
|
||||
|
||||
@onready var current_speed := move_speed
|
||||
@export var bounce_on_land := false
|
||||
@export var bounce_height := -200
|
||||
@export var visuals: Node2D
|
||||
|
||||
@export var follow_player := false
|
||||
|
||||
var can_hit := true
|
||||
|
||||
var can_bounce := true
|
||||
|
||||
var active := true
|
||||
|
||||
func _ready() -> void:
|
||||
if owner is CharacterBody2D:
|
||||
owner.floor_constant_speed = true
|
||||
owner.floor_max_angle = 0.80
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if auto_call:
|
||||
handle_movement(delta)
|
||||
if visuals != null:
|
||||
visuals.scale.x = owner.direction
|
||||
|
||||
func handle_movement(delta: float) -> void:
|
||||
if active == false: return
|
||||
if Global.second_quest and owner is Enemy:
|
||||
move_speed = second_quest_speed
|
||||
apply_gravity(delta)
|
||||
if owner.is_on_wall():
|
||||
wall_hit()
|
||||
elif ledge_detection_cast != null and owner.is_on_floor():
|
||||
ledge_detection_cast.floor_normal = owner.get_floor_normal()
|
||||
ledge_detection_cast.position.x = abs(ledge_detection_cast.position.x) * owner.direction
|
||||
if ledge_detection_cast.is_colliding() == false:
|
||||
wall_hit()
|
||||
if follow_player and owner.is_on_floor():
|
||||
player_direction_check()
|
||||
current_speed = abs(owner.velocity.x)
|
||||
if current_speed < move_speed:
|
||||
current_speed = move_speed
|
||||
if owner.is_on_floor():
|
||||
current_speed = move_speed
|
||||
if bounce_on_land:
|
||||
owner.velocity.y = bounce_height
|
||||
owner.velocity.x = (current_speed if can_move else 0) * owner.direction
|
||||
owner.move_and_slide()
|
||||
|
||||
func apply_gravity(delta: float) -> void:
|
||||
owner.velocity.y += (Global.entity_gravity / delta) * delta
|
||||
owner.velocity.y = clamp(owner.velocity.y, -INF, Global.entity_max_fall_speed)
|
||||
|
||||
func player_direction_check() -> void:
|
||||
var target_player = get_tree().get_first_node_in_group("Players")
|
||||
owner.direction = sign(target_player.global_position.x - owner.global_position.x)
|
||||
|
||||
func wall_hit() -> void:
|
||||
if can_hit == false:
|
||||
return
|
||||
can_hit = false
|
||||
owner.direction *= -1
|
||||
await get_tree().create_timer(0.1, false).timeout
|
||||
can_hit = true
|
1
Scripts/Classes/Components/BasicEnemyMovement.gd.uid
Executable file
1
Scripts/Classes/Components/BasicEnemyMovement.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://dlq6o2rg1x7in
|
20
Scripts/Classes/Components/BasicStaticMovement.gd
Normal file
20
Scripts/Classes/Components/BasicStaticMovement.gd
Normal file
@@ -0,0 +1,20 @@
|
||||
class_name BasicStaticMovement
|
||||
extends Node
|
||||
|
||||
@export var auto_call := true
|
||||
|
||||
@export var visuals: Node2D = null
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if auto_call:
|
||||
handle_movement(delta)
|
||||
|
||||
func handle_movement(delta: float) -> void:
|
||||
apply_gravity(delta)
|
||||
if owner.is_on_floor():
|
||||
owner.velocity.x = lerpf(owner.velocity.x, 0, delta * 20)
|
||||
owner.move_and_slide()
|
||||
|
||||
func apply_gravity(delta: float) -> void:
|
||||
owner.velocity.y += (Global.entity_gravity / delta) * delta
|
||||
owner.velocity.y = clamp(owner.velocity.y, -INF, Global.entity_max_fall_speed)
|
1
Scripts/Classes/Components/BasicStaticMovement.gd.uid
Executable file
1
Scripts/Classes/Components/BasicStaticMovement.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bx6r8sjar6cwr
|
4
Scripts/Classes/Components/BlockAnimations.gd
Executable file
4
Scripts/Classes/Components/BlockAnimations.gd
Executable file
@@ -0,0 +1,4 @@
|
||||
extends Node
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
1
Scripts/Classes/Components/BlockAnimations.gd.uid
Executable file
1
Scripts/Classes/Components/BlockAnimations.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cfcyhay33ctae
|
33
Scripts/Classes/Components/BlockBouncingDetection.gd
Normal file
33
Scripts/Classes/Components/BlockBouncingDetection.gd
Normal file
@@ -0,0 +1,33 @@
|
||||
class_name BlockBouncingDetection
|
||||
extends Node
|
||||
|
||||
@export_enum("Collision", "Hitbox") var detection_type := 0
|
||||
@export var hitbox: Area2D = null
|
||||
|
||||
@export var can_change_direction := false
|
||||
|
||||
signal block_bounced(block: Block)
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
if detection_type == 0:
|
||||
collision_detect()
|
||||
else:
|
||||
hitbox_detect()
|
||||
|
||||
func collision_detect() -> void:
|
||||
var collision: KinematicCollision2D = owner.move_and_collide(Vector2.DOWN, true)
|
||||
if is_instance_valid(collision):
|
||||
if collision.get_collider() is Block:
|
||||
if collision.get_collider().bouncing:
|
||||
block_bounced.emit(collision.get_collider())
|
||||
return
|
||||
|
||||
func hitbox_detect() -> void:
|
||||
if is_instance_valid(hitbox) == false: return
|
||||
for i in hitbox.get_overlapping_bodies():
|
||||
if i is Block:
|
||||
if i.bouncing:
|
||||
block_bounced.emit(i)
|
||||
if can_change_direction:
|
||||
owner.direction = sign(owner.global_position.x - i.global_position.x)
|
||||
return
|
1
Scripts/Classes/Components/BlockBouncingDetection.gd.uid
Executable file
1
Scripts/Classes/Components/BlockBouncingDetection.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cmg61722ktg2m
|
23
Scripts/Classes/Components/BlockHitter.gd
Normal file
23
Scripts/Classes/Components/BlockHitter.gd
Normal file
@@ -0,0 +1,23 @@
|
||||
class_name BlockHitter
|
||||
extends Node
|
||||
|
||||
@export var hitbox: Area2D = null
|
||||
@export var can_break_bricks := false
|
||||
@export var enabled := true:
|
||||
set(value):
|
||||
enabled = value
|
||||
set_physics_process(value)
|
||||
|
||||
signal block_hit(block: Block)
|
||||
|
||||
func _ready() -> void:
|
||||
hitbox.set_collision_mask_value(3, true)
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
for i in hitbox.get_overlapping_bodies():
|
||||
if i is Block and i.global_position.y < owner.global_position.y:
|
||||
i.shell_block_hit.emit(self)
|
||||
block_hit.emit(i)
|
||||
if i is BrickBlock:
|
||||
if i.item == null:
|
||||
i.destroy()
|
1
Scripts/Classes/Components/BlockHitter.gd.uid
Normal file
1
Scripts/Classes/Components/BlockHitter.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dkjpfs3sm3go3
|
99
Scripts/Classes/Components/EditorPropertyExposer.gd
Normal file
99
Scripts/Classes/Components/EditorPropertyExposer.gd
Normal file
@@ -0,0 +1,99 @@
|
||||
class_name PropertyExposer
|
||||
extends Node
|
||||
|
||||
@export var properties: Array[String] = []
|
||||
@export var filters: Dictionary[String, String] = {}
|
||||
|
||||
@export var properties_force_selector: Dictionary[String, PackedScene] = {}
|
||||
|
||||
const base64_charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
static var entity_map := {}
|
||||
|
||||
signal modifier_applied
|
||||
|
||||
func _ready() -> void:
|
||||
name = "EditorPropertyExposer"
|
||||
if entity_map.is_empty():
|
||||
entity_map = JSON.parse_string(FileAccess.open(EntityIDMapper.MAP_PATH, FileAccess.READ).get_as_text())
|
||||
|
||||
func get_string() -> String:
|
||||
var string = ""
|
||||
for i in properties:
|
||||
string += ","
|
||||
if owner is Track:
|
||||
if owner.get(i) is Array:
|
||||
for x in owner.get(i):
|
||||
string += base64_charset[(Track.DIRECTIONS.find(x))]
|
||||
if owner.get(i) is String:
|
||||
string += owner.get(i).replace(",", "&")
|
||||
elif owner.get(i) is PackedScene:
|
||||
var key = EntityIDMapper.get_map_id(owner.get(i).resource_path)
|
||||
if key == null or key == "":
|
||||
key = "!!"
|
||||
string += key
|
||||
elif owner.get(i) is int:
|
||||
if owner.get(i) >= 64:
|
||||
string += encode_to_base64_2char(owner.get(i))
|
||||
else:
|
||||
string += base64_charset[owner.get(i)]
|
||||
elif owner.get(i) is bool:
|
||||
string += base64_charset[int(owner.get(i))]
|
||||
elif owner.get(i) == null:
|
||||
string += "!!"
|
||||
|
||||
return string
|
||||
|
||||
func apply_string(entity_string := "") -> void:
|
||||
var idx := 2
|
||||
var slice = entity_string.split(",")
|
||||
for i in properties:
|
||||
if slice.size() <= idx:
|
||||
return
|
||||
var value = slice[idx]
|
||||
if owner is Track:
|
||||
if owner.get(i) is Array:
|
||||
for x in value:
|
||||
owner.get(i).append(Track.DIRECTIONS[base64_charset.find(x)])
|
||||
owner._ready()
|
||||
if owner.get(i) is String:
|
||||
owner.set(i, value.replace("&", ","))
|
||||
if owner.get(i) is PackedScene or (owner.get(i) == null and i == "item"):
|
||||
var scene = entity_map.get(value)
|
||||
if scene != null:
|
||||
owner.set(i, load(entity_map.get(value)[0]))
|
||||
elif owner.get(i) is int:
|
||||
var num = value
|
||||
if value.length() > 1:
|
||||
num = decode_from_base64_2char(value)
|
||||
else:
|
||||
num = base64_charset.find(value)
|
||||
owner.set(i, num)
|
||||
elif owner.get(i) is bool:
|
||||
owner.set(i, bool(base64_charset.find(value)))
|
||||
idx += 1
|
||||
modifier_applied.emit()
|
||||
|
||||
func encode_to_base64_2char(value: int) -> String:
|
||||
if value < 0 or value >= 4096:
|
||||
push_error("Value out of range for 2-char base64 encoding.")
|
||||
return ""
|
||||
|
||||
var char1 = base64_charset[(value >> 6) & 0b111111] # Top 6 bits
|
||||
var char2 = base64_charset[value & 0b111111] # Bottom 6 bits
|
||||
|
||||
return char1 + char2
|
||||
|
||||
func decode_from_base64_2char(encoded: String) -> int:
|
||||
if encoded.length() != 2:
|
||||
push_error("Encoded string must be exactly 2 characters.")
|
||||
return -1
|
||||
|
||||
var char1_val = base64_charset.find(encoded[0])
|
||||
var char2_val = base64_charset.find(encoded[1])
|
||||
|
||||
if char1_val == -1 or char2_val == -1:
|
||||
push_error("Invalid character in base64 string.")
|
||||
return -1
|
||||
|
||||
return (char1_val << 6) | char2_val
|
1
Scripts/Classes/Components/EditorPropertyExposer.gd.uid
Executable file
1
Scripts/Classes/Components/EditorPropertyExposer.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ctfbuoxtnnl0q
|
25
Scripts/Classes/Components/EnemyPlayerDetection.gd
Normal file
25
Scripts/Classes/Components/EnemyPlayerDetection.gd
Normal file
@@ -0,0 +1,25 @@
|
||||
class_name EnemyPlayerDetection
|
||||
extends Node
|
||||
|
||||
@export var hitbox: Area2D = null
|
||||
|
||||
@export var height := 4
|
||||
|
||||
signal player_hit(player: Player)
|
||||
signal player_stomped_on(player: Player)
|
||||
signal invincible_player_hit(player: Player)
|
||||
|
||||
func _ready() -> void:
|
||||
hitbox.area_entered.connect(area_entered)
|
||||
|
||||
func area_entered(area: Area2D) -> void:
|
||||
if area.owner is Player:
|
||||
player_entered(area.owner)
|
||||
|
||||
func player_entered(player: Player) -> void:
|
||||
if player.is_invincible or player.has_hammer:
|
||||
invincible_player_hit.emit(player)
|
||||
elif (player.velocity.y >= 15 or (player.global_position.y + height < owner.global_position.y)) and player.in_water == false:
|
||||
player_stomped_on.emit(player)
|
||||
else:
|
||||
player_hit.emit(player)
|
1
Scripts/Classes/Components/EnemyPlayerDetection.gd.uid
Executable file
1
Scripts/Classes/Components/EnemyPlayerDetection.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://chj8hu207lrh
|
13
Scripts/Classes/Components/ExplosionDetection.gd
Executable file
13
Scripts/Classes/Components/ExplosionDetection.gd
Executable file
@@ -0,0 +1,13 @@
|
||||
class_name ExplosionDetection
|
||||
extends Node
|
||||
|
||||
@export var hitbox: Area2D = null
|
||||
signal explosion_entered(explosion: Node2D)
|
||||
|
||||
func _ready() -> void:
|
||||
if hitbox != null:
|
||||
hitbox.area_entered.connect(area_entered)
|
||||
|
||||
func area_entered(area: Area2D) -> void:
|
||||
if area.owner is Explosion:
|
||||
explosion_entered.emit(area.owner)
|
1
Scripts/Classes/Components/ExplosionDetection.gd.uid
Executable file
1
Scripts/Classes/Components/ExplosionDetection.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ba18grqjixded
|
15
Scripts/Classes/Components/FireballDetection.gd
Executable file
15
Scripts/Classes/Components/FireballDetection.gd
Executable file
@@ -0,0 +1,15 @@
|
||||
class_name FireballDetection
|
||||
extends Node
|
||||
|
||||
@export var hitbox: Area2D = null
|
||||
@export var play_sfx_on_hit := false
|
||||
signal fireball_hit(fireball: FireBall)
|
||||
|
||||
func _ready() -> void:
|
||||
if hitbox != null:
|
||||
hitbox.area_entered.connect(area_entered)
|
||||
|
||||
func area_entered(area: Area2D) -> void:
|
||||
if area.owner is FireBall:
|
||||
fireball_hit.emit(area.owner)
|
||||
area.owner.hit(play_sfx_on_hit)
|
1
Scripts/Classes/Components/FireballDetection.gd.uid
Executable file
1
Scripts/Classes/Components/FireballDetection.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://dri2d5jtu0fbq
|
47
Scripts/Classes/Components/GibSpawner.gd
Normal file
47
Scripts/Classes/Components/GibSpawner.gd
Normal file
@@ -0,0 +1,47 @@
|
||||
class_name GibSpawner
|
||||
extends Node
|
||||
|
||||
@export var visuals: Node = null
|
||||
@export_enum("Spin", "Drop", "Poof") var gib_type := 0
|
||||
@export var play_death_sfx := true
|
||||
const ENTITY_GIB = preload("res://Scenes/Prefabs/Entities/EntityGib.tscn")
|
||||
|
||||
signal gib_about_to_spawn
|
||||
|
||||
|
||||
func summon_gib(direction := 1, play_sfx := play_death_sfx, override_gib_type := gib_type) -> void:
|
||||
gib_about_to_spawn.emit()
|
||||
if play_sfx:
|
||||
play_die_sfx()
|
||||
if override_gib_type == 2:
|
||||
summon_poof()
|
||||
return
|
||||
var node = ENTITY_GIB.instantiate()
|
||||
visuals.show()
|
||||
if visuals.has_node("ResourceSetterNew"):
|
||||
visuals.get_node("ResourceSetterNew").update_on_spawn = false
|
||||
node.visuals = visuals.duplicate()
|
||||
node.visuals.set_process(false)
|
||||
node.global_position = visuals.global_position
|
||||
node.visuals.position = Vector2.ZERO
|
||||
node.visuals.offset = Vector2.ZERO
|
||||
node.gib_type = override_gib_type
|
||||
node.direction = direction
|
||||
owner.add_sibling(node)
|
||||
|
||||
func play_die_sfx() -> void:
|
||||
AudioManager.play_sfx("kick", owner.global_position)
|
||||
|
||||
const SMOKE_PARTICLE = preload("uid://d08nv4qtfouv1")
|
||||
|
||||
func summon_poof() -> void:
|
||||
var particle = SMOKE_PARTICLE.instantiate()
|
||||
particle.global_position = visuals.global_position + Vector2(0, 8)
|
||||
owner.add_sibling(particle)
|
||||
|
||||
func stomp_die(player: Player, add_combo := true) -> void:
|
||||
DiscoLevel.combo_amount += 1
|
||||
AudioManager.play_sfx("enemy_stomp", owner.global_position)
|
||||
player.enemy_bounce_off(add_combo)
|
||||
summon_gib(1, false, 1)
|
||||
owner.queue_free()
|
1
Scripts/Classes/Components/GibSpawner.gd.uid
Executable file
1
Scripts/Classes/Components/GibSpawner.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://c3gg32ivrlq8n
|
15
Scripts/Classes/Components/IcicleDetection.gd
Executable file
15
Scripts/Classes/Components/IcicleDetection.gd
Executable file
@@ -0,0 +1,15 @@
|
||||
class_name IcicleDetection
|
||||
extends Node
|
||||
|
||||
@export var hitbox: Area2D = null
|
||||
|
||||
signal icicle_detected(icicle: Icicle)
|
||||
|
||||
func _ready() -> void:
|
||||
if hitbox != null:
|
||||
hitbox.area_entered.connect(area_entered)
|
||||
|
||||
func area_entered(area: Area2D) -> void:
|
||||
if area.owner is Icicle:
|
||||
if area.owner.falling:
|
||||
icicle_detected.emit(area.owner)
|
1
Scripts/Classes/Components/IcicleDetection.gd.uid
Executable file
1
Scripts/Classes/Components/IcicleDetection.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://dq860i312isk
|
23
Scripts/Classes/Components/LedgeDetectionCast.gd
Normal file
23
Scripts/Classes/Components/LedgeDetectionCast.gd
Normal file
@@ -0,0 +1,23 @@
|
||||
class_name LedgeDetectionCast
|
||||
extends RayCast2D
|
||||
|
||||
@export var floor_normal := Vector2.UP
|
||||
@export var ray_length := 24
|
||||
var floor_direction := 1
|
||||
var direction := 1
|
||||
|
||||
## Hypotenuse = floor_angle
|
||||
## Opposite = ???
|
||||
## Adjacent = position.x
|
||||
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
target_position.y = ray_length
|
||||
if floor_normal.x > 0:
|
||||
floor_direction = 1
|
||||
elif floor_normal.x < 0:
|
||||
floor_direction = -1
|
||||
else:
|
||||
position.y = -(ray_length / 2.0)
|
||||
return
|
||||
position.y = ((-floor_normal.y * (position.x)) * (floor_direction)) - (ray_length / 2.0)
|
1
Scripts/Classes/Components/LedgeDetectionCast.gd.uid
Normal file
1
Scripts/Classes/Components/LedgeDetectionCast.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://blfnd65xcx78c
|
29
Scripts/Classes/Components/LevelPersistance.gd
Normal file
29
Scripts/Classes/Components/LevelPersistance.gd
Normal file
@@ -0,0 +1,29 @@
|
||||
class_name LevelPersistance
|
||||
extends Node
|
||||
|
||||
static var active_nodes := [[], []]
|
||||
|
||||
var active := false
|
||||
|
||||
@onready var path := get_path_string()
|
||||
|
||||
signal enabled
|
||||
signal enabled_2
|
||||
|
||||
static func reset_states() -> void:
|
||||
active_nodes = [[], []]
|
||||
Checkpoint.old_state = [[], []]
|
||||
|
||||
func _ready() -> void:
|
||||
return
|
||||
|
||||
func set_as_active() -> void:
|
||||
if owner.has_meta("no_persist"): return
|
||||
active_nodes[0].append(path)
|
||||
|
||||
func set_as_active_2() -> void:
|
||||
if owner.has_meta("no_persist"): return
|
||||
active_nodes[1].append(path)
|
||||
|
||||
func get_path_string() -> String:
|
||||
return Global.current_level.scene_file_path + str(Vector2i(owner.global_position / 8))
|
1
Scripts/Classes/Components/LevelPersistance.gd.uid
Normal file
1
Scripts/Classes/Components/LevelPersistance.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://maqpreddu5kg
|
18
Scripts/Classes/Components/OffScreenDespawner.gd
Normal file
18
Scripts/Classes/Components/OffScreenDespawner.gd
Normal file
@@ -0,0 +1,18 @@
|
||||
class_name OffScreenDespawner
|
||||
extends Node
|
||||
|
||||
var can_despawn := false
|
||||
|
||||
func _ready() -> void:
|
||||
can_despawn = false
|
||||
await get_tree().create_timer(0.5, false).timeout
|
||||
can_despawn = true
|
||||
|
||||
func on_screen_exited() -> void:
|
||||
if Global.level_editor != null:
|
||||
if Global.level_editor.current_state == LevelEditor.EditorState.PLAYTESTING or Global.current_game_mode == Global.GameMode.CUSTOM_LEVEL:
|
||||
await get_tree().physics_frame
|
||||
if can_despawn:
|
||||
owner.queue_free()
|
||||
else:
|
||||
owner.queue_free()
|
1
Scripts/Classes/Components/OffScreenDespawner.gd.uid
Normal file
1
Scripts/Classes/Components/OffScreenDespawner.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://33no4mylhh1r
|
28
Scripts/Classes/Components/PSwitcher.gd
Normal file
28
Scripts/Classes/Components/PSwitcher.gd
Normal file
@@ -0,0 +1,28 @@
|
||||
class_name PSwitcher
|
||||
extends Node
|
||||
|
||||
var enabled := true
|
||||
@export_file("*.tscn") var new_scene := ""
|
||||
@export var new_offset := Vector2.ZERO
|
||||
|
||||
@export var properties := []
|
||||
|
||||
var is_switched := false
|
||||
|
||||
func _ready() -> void:
|
||||
Global.p_switch_toggle.connect(switch_to_other)
|
||||
if Global.p_switch_active and not is_switched:
|
||||
switch_to_other()
|
||||
|
||||
func switch_to_other() -> void:
|
||||
if enabled == false: return
|
||||
if new_scene != "":
|
||||
var new = load(new_scene).instantiate()
|
||||
new.global_position = owner.global_position + new_offset
|
||||
if new.has_node("PSwitcher"):
|
||||
new.get_node("PSwitcher").new_scene = owner.scene_file_path
|
||||
new.get_node("PSwitcher").is_switched = true
|
||||
for i in properties:
|
||||
new.set(i, owner.get(i))
|
||||
owner.call_deferred("add_sibling", new)
|
||||
owner.queue_free()
|
1
Scripts/Classes/Components/PSwitcher.gd.uid
Executable file
1
Scripts/Classes/Components/PSwitcher.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bul1nbd2in1gn
|
11
Scripts/Classes/Components/PackStreamPlayer.gd
Normal file
11
Scripts/Classes/Components/PackStreamPlayer.gd
Normal file
@@ -0,0 +1,11 @@
|
||||
class_name PackStreamPlayer
|
||||
extends AudioStreamPlayer
|
||||
|
||||
@onready var resource_getter = ResourceGetter.new()
|
||||
|
||||
func _ready() -> void:
|
||||
update()
|
||||
Global.level_theme_changed.connect(update)
|
||||
|
||||
func update() -> void:
|
||||
stream = resource_getter.get_resource(stream)
|
1
Scripts/Classes/Components/PackStreamPlayer.gd.uid
Normal file
1
Scripts/Classes/Components/PackStreamPlayer.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dmtw1jesw1vl7
|
70
Scripts/Classes/Components/ResourceGetter.gd
Normal file
70
Scripts/Classes/Components/ResourceGetter.gd
Normal file
@@ -0,0 +1,70 @@
|
||||
class_name ResourceGetter
|
||||
extends Node
|
||||
|
||||
var original_resource: Resource = null
|
||||
|
||||
static var cache := {}
|
||||
|
||||
func get_resource(resource: Resource) -> Resource:
|
||||
if resource == null:
|
||||
return null
|
||||
|
||||
if original_resource == null:
|
||||
original_resource = resource
|
||||
|
||||
if cache.has(original_resource.resource_path) and resource is not AtlasTexture:
|
||||
return cache.get(original_resource.resource_path)
|
||||
|
||||
var path := ""
|
||||
if original_resource is AtlasTexture:
|
||||
path = get_resource_path(original_resource.atlas.resource_path)
|
||||
else:
|
||||
path = get_resource_path(original_resource.resource_path)
|
||||
|
||||
if path == original_resource.resource_path:
|
||||
return original_resource
|
||||
|
||||
if original_resource is Texture:
|
||||
var new_resource = null
|
||||
if path.contains("user://"):
|
||||
new_resource = ImageTexture.create_from_image(Image.load_from_file(path))
|
||||
else:
|
||||
new_resource = load(path)
|
||||
send_to_cache(original_resource.resource_path, new_resource)
|
||||
if original_resource is AtlasTexture:
|
||||
var atlas = AtlasTexture.new()
|
||||
atlas.atlas = new_resource
|
||||
atlas.region = original_resource.region
|
||||
return atlas
|
||||
return new_resource
|
||||
|
||||
elif original_resource is AudioStream:
|
||||
if path.get_file().contains(".wav"):
|
||||
var new_resource = AudioStreamWAV.load_from_file(path)
|
||||
send_to_cache(original_resource.resource_path, new_resource)
|
||||
return new_resource
|
||||
elif path.get_file().contains(".mp3"):
|
||||
var new_resource = AudioStreamMP3.load_from_file(path)
|
||||
send_to_cache(original_resource.resource_path, new_resource)
|
||||
return new_resource
|
||||
|
||||
elif original_resource is Font:
|
||||
var new_font = FontFile.new()
|
||||
new_font.load_bitmap_font(path)
|
||||
send_to_cache(original_resource.resource_path, new_font)
|
||||
return new_font
|
||||
|
||||
send_to_cache(original_resource.resource_path, original_resource)
|
||||
|
||||
return original_resource
|
||||
|
||||
func send_to_cache(resource_path := "", resource_to_cache: Resource = null) -> void:
|
||||
if cache.has(resource_path) == false:
|
||||
cache.set(resource_path, resource_to_cache)
|
||||
|
||||
func get_resource_path(resource_path := "") -> String:
|
||||
for i in Settings.file.visuals.resource_packs:
|
||||
var test = resource_path.replace("res://Assets/", "user://resource_packs/" + i + "/")
|
||||
if FileAccess.file_exists(test):
|
||||
return test
|
||||
return resource_path
|
1
Scripts/Classes/Components/ResourceGetter.gd.uid
Normal file
1
Scripts/Classes/Components/ResourceGetter.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bsp584niccobr
|
146
Scripts/Classes/Components/ResourceSetter.gd
Normal file
146
Scripts/Classes/Components/ResourceSetter.gd
Normal file
@@ -0,0 +1,146 @@
|
||||
class_name ResourceSetter
|
||||
extends Node
|
||||
|
||||
@export var node_to_affect: Node = null
|
||||
@export var property_name := ""
|
||||
@export var themed_resource: ThemedResource = null
|
||||
@export var use_classic_theming := false
|
||||
@export var use_cache := true
|
||||
|
||||
signal sprites_updated
|
||||
|
||||
static var cache := {}
|
||||
|
||||
func _enter_tree() -> void:
|
||||
Global.level_theme_changed.connect(update_sprites)
|
||||
Global.level_time_changed.connect(update_sprites)
|
||||
|
||||
func _ready() -> void:
|
||||
update_sprites()
|
||||
|
||||
func update_sprites() -> void:
|
||||
cache.clear()
|
||||
if themed_resource == null:
|
||||
node_to_affect.set(property_name, null)
|
||||
return
|
||||
var resource = get_resource(themed_resource, node_to_affect, true, use_cache)
|
||||
node_to_affect.set(property_name, resource)
|
||||
if node_to_affect is AnimatedSprite2D:
|
||||
node_to_affect.play()
|
||||
sprites_updated.emit()
|
||||
|
||||
static func get_resource(resource: Resource, node: Node = null, assign := false, cache_enabled := true) -> RefCounted:
|
||||
if resource == null:
|
||||
return resource
|
||||
var og_path = resource.resource_path
|
||||
if resource is AtlasTexture:
|
||||
og_path = resource.atlas.resource_path
|
||||
if resource is ThemedResource:
|
||||
if resource.get(Global.level_theme) != null:
|
||||
resource = get_resource(resource.get(Global.level_theme))
|
||||
else:
|
||||
resource = get_resource(resource.Overworld)
|
||||
if resource is CampaignResource:
|
||||
if resource.get(Global.current_campaign) != null:
|
||||
resource = get_resource(resource.get(Global.current_campaign))
|
||||
else:
|
||||
resource = get_resource(resource.SMB1)
|
||||
|
||||
if assign:
|
||||
if resource is AtlasTexture:
|
||||
resource.filter_clip = true
|
||||
if resource is SpriteFrames:
|
||||
if node is not AnimatedSprite2D:
|
||||
resource = resource.get_frame_texture(resource.get_animation_names()[0], 0)
|
||||
if Settings.file.visuals.resource_packs.is_empty() == false:
|
||||
for i in Settings.file.visuals.resource_packs:
|
||||
resource = get_override_resource(resource, i)
|
||||
if cache.has(og_path) == false:
|
||||
cache[og_path] = resource.duplicate()
|
||||
if resource == null:
|
||||
pass
|
||||
return resource
|
||||
|
||||
static func get_override_resource(resource: Resource = null, resource_pack := "") -> Object:
|
||||
if resource == null:
|
||||
return
|
||||
if resource_pack == "":
|
||||
return
|
||||
var original_resource_path = resource.resource_path
|
||||
var resource_path = get_override_resource_path(resource.resource_path, resource_pack)
|
||||
if FileAccess.file_exists(resource_path):
|
||||
if resource is Texture:
|
||||
resource = create_image_from_path(resource_path)
|
||||
elif resource is SpriteFrames:
|
||||
resource = create_new_sprite_frames(resource, resource_pack)
|
||||
if resource is AudioStream:
|
||||
if resource_path.contains(".mp3"):
|
||||
var resource_loops = resource.has_loop()
|
||||
resource = AudioStreamMP3.load_from_file(resource_path)
|
||||
resource.set_loop(resource_loops)
|
||||
elif resource_path.contains(".wav"):
|
||||
resource = AudioStreamWAV.load_from_file(resource_path)
|
||||
if resource is FontVariation:
|
||||
resource_path = get_override_resource_path(resource.base_font.resource_path, resource_pack)
|
||||
if FileAccess.file_exists(resource_path):
|
||||
var new_font = FontFile.new()
|
||||
var variation = resource.duplicate()
|
||||
new_font.load_bitmap_font(resource_path.replace(".png", ".fnt"))
|
||||
variation.base_font = new_font
|
||||
resource = variation
|
||||
else:
|
||||
if resource is SpriteFrames:
|
||||
resource = create_new_sprite_frames(resource, resource_pack)
|
||||
if resource is AtlasTexture:
|
||||
resource_path = get_override_resource_path(resource.atlas.resource_path, resource_pack)
|
||||
if FileAccess.file_exists(resource_path):
|
||||
var new_resource = AtlasTexture.new()
|
||||
new_resource.atlas = create_image_from_path(get_override_resource_path(resource.atlas.resource_path, resource_pack))
|
||||
new_resource.region = resource.region
|
||||
return new_resource
|
||||
if resource is AudioStreamInteractive:
|
||||
resource = get_override_resource(resource.get_clip_stream(0), resource_pack)
|
||||
if resource is FontVariation:
|
||||
resource_path = get_override_resource_path(resource.base_font.resource_path, resource_pack)
|
||||
if FileAccess.file_exists(resource_path):
|
||||
var new_font = FontFile.new()
|
||||
var variation = resource.duplicate()
|
||||
new_font.load_bitmap_font(resource_path.replace(".png", ".fnt"))
|
||||
variation.base_font = new_font
|
||||
resource = variation
|
||||
return resource
|
||||
|
||||
static func create_image_from_path(file_path := "") -> ImageTexture:
|
||||
var image = Image.new()
|
||||
image.load(file_path)
|
||||
return ImageTexture.create_from_image(image)
|
||||
|
||||
static func create_new_sprite_frames(old_sprite_frames: SpriteFrames, resource_pack := "") -> SpriteFrames:
|
||||
var new_frames = SpriteFrames.new()
|
||||
new_frames.remove_animation("default")
|
||||
for i in old_sprite_frames.get_animation_names():
|
||||
new_frames.add_animation(i)
|
||||
for x in old_sprite_frames.get_frame_count(i):
|
||||
var frame = AtlasTexture.new()
|
||||
var old_frame = old_sprite_frames.get_frame_texture(i, x)
|
||||
frame.atlas = get_override_resource(old_frame.atlas, resource_pack)
|
||||
frame.region = old_frame.region
|
||||
new_frames.add_frame(i, frame, old_sprite_frames.get_frame_duration(i, x))
|
||||
new_frames.set_animation_loop(i, old_sprite_frames.get_animation_loop(i))
|
||||
new_frames.set_animation_speed(i, old_sprite_frames.get_animation_speed(i))
|
||||
return new_frames
|
||||
|
||||
static func get_pure_resource_path(resource_path := "") -> String:
|
||||
if Settings.file.visuals.resource_packs.is_empty() == false:
|
||||
for i in Settings.file.visuals.resource_packs:
|
||||
var new_path = get_override_resource_path(resource_path, i)
|
||||
new_path = new_path.replace("user://custom_characters/", "user://resource_packs/" + new_path + "/Sprites/Players/CustomCharacters/")
|
||||
if FileAccess.file_exists(new_path):
|
||||
return new_path
|
||||
return resource_path
|
||||
|
||||
static func get_override_resource_path(resource_path := "", resource_pack := "") -> String:
|
||||
if resource_pack != "":
|
||||
return resource_path.replace("res://Assets", "user://resource_packs/" + resource_pack)
|
||||
else:
|
||||
return resource_path
|
1
Scripts/Classes/Components/ResourceSetter.gd.uid
Executable file
1
Scripts/Classes/Components/ResourceSetter.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cq6f682453q6o
|
313
Scripts/Classes/Components/ResourceSetterNew.gd
Normal file
313
Scripts/Classes/Components/ResourceSetterNew.gd
Normal file
@@ -0,0 +1,313 @@
|
||||
class_name ResourceSetterNew
|
||||
extends Node
|
||||
|
||||
@export var node_to_affect: Node = null
|
||||
@export var property_node: Node = null
|
||||
@export var property_name := ""
|
||||
@export var mode: ResourceMode = ResourceMode.SPRITE_FRAMES
|
||||
@export var resource_json: JSON = null:
|
||||
set(value):
|
||||
resource_json = value
|
||||
update_resource()
|
||||
|
||||
enum ResourceMode {SPRITE_FRAMES, TEXTURE, AUDIO, RAW}
|
||||
@export var use_cache := true
|
||||
|
||||
static var cache := {}
|
||||
static var property_cache := {}
|
||||
|
||||
var current_json_path := ""
|
||||
|
||||
static var state := [0, 0]
|
||||
|
||||
static var pack_configs := {}
|
||||
|
||||
var config_to_use := {}
|
||||
|
||||
var is_random := false
|
||||
|
||||
signal updated
|
||||
|
||||
@export var force_properties := {}
|
||||
var update_on_spawn := true
|
||||
|
||||
func _init() -> void:
|
||||
set_process_mode(Node.PROCESS_MODE_ALWAYS)
|
||||
|
||||
func _ready() -> void:
|
||||
safety_check()
|
||||
if update_on_spawn:
|
||||
update_resource()
|
||||
Global.level_time_changed.connect(update_resource)
|
||||
Global.level_theme_changed.connect(update_resource)
|
||||
|
||||
|
||||
func safety_check() -> void:
|
||||
if Settings.file.visuals.resource_packs.has("BaseAssets") == false:
|
||||
Settings.file.visuals.resource_packs.append("BaseAssets")
|
||||
|
||||
func update_resource() -> void:
|
||||
randomize()
|
||||
if is_inside_tree() == false or is_queued_for_deletion() or resource_json == null or node_to_affect == null:
|
||||
return
|
||||
if state != [Global.level_theme, Global.theme_time]:
|
||||
cache.clear()
|
||||
property_cache.clear()
|
||||
if node_to_affect != null:
|
||||
var resource = get_resource(resource_json)
|
||||
node_to_affect.set(property_name, resource)
|
||||
if node_to_affect is AnimatedSprite2D:
|
||||
node_to_affect.play()
|
||||
state = [Global.level_theme, Global.theme_time]
|
||||
updated.emit()
|
||||
|
||||
func get_resource(json_file: JSON) -> Resource:
|
||||
if cache.has(json_file.resource_path) and use_cache and force_properties.is_empty():
|
||||
if property_cache.has(json_file.resource_path):
|
||||
apply_properties(property_cache[json_file.resource_path])
|
||||
return cache[json_file.resource_path]
|
||||
|
||||
var resource: Resource = null
|
||||
var resource_path = json_file.resource_path
|
||||
config_to_use = {}
|
||||
for i in Settings.file.visuals.resource_packs:
|
||||
resource_path = get_resource_pack_path(resource_path, i)
|
||||
|
||||
var source_json = JSON.parse_string(FileAccess.open(resource_path, FileAccess.READ).get_as_text())
|
||||
if source_json == null:
|
||||
Global.log_error("Error parsing " + resource_path + "!")
|
||||
return
|
||||
var json = source_json.duplicate()
|
||||
var source_resource_path = ""
|
||||
if json.has("variations"):
|
||||
json = get_variation_json(json.variations)
|
||||
if json.has("source"):
|
||||
if json.get("source") is String:
|
||||
source_resource_path = json_file.resource_path.replace(json_file.resource_path.get_file(), json.source)
|
||||
else:
|
||||
Global.log_error("Error getting variations! " + resource_path)
|
||||
return
|
||||
for i in Settings.file.visuals.resource_packs:
|
||||
source_resource_path = get_resource_pack_path(source_resource_path, i)
|
||||
if json.has("rect"):
|
||||
resource = load_image_from_path(source_resource_path)
|
||||
var atlas = AtlasTexture.new()
|
||||
atlas.atlas = resource
|
||||
atlas.region = Rect2(json.rect[0], json.rect[1], json.rect[2], json.rect[3])
|
||||
resource = atlas
|
||||
if json.has("properties"):
|
||||
apply_properties(json.get("properties"))
|
||||
if use_cache:
|
||||
property_cache[json_file.resource_path] = json.properties.duplicate()
|
||||
elif source_json.has("properties"):
|
||||
apply_properties(source_json.get("properties"))
|
||||
if use_cache:
|
||||
property_cache[json_file.resource_path] = source_json.properties.duplicate()
|
||||
match mode:
|
||||
ResourceMode.SPRITE_FRAMES:
|
||||
var animation_json = {}
|
||||
if json.has("animations"):
|
||||
animation_json = json.get("animations")
|
||||
elif source_json.has("animations"):
|
||||
animation_json = source_json.get("animations")
|
||||
if animation_json != {}:
|
||||
resource = load_image_from_path(source_resource_path)
|
||||
if json.has("rect"):
|
||||
var atlas = AtlasTexture.new()
|
||||
atlas.atlas = resource
|
||||
atlas.region = Rect2(json.rect[0], json.rect[1], json.rect[2], json.rect[3])
|
||||
resource = atlas
|
||||
resource = create_sprite_frames_from_image(resource, animation_json)
|
||||
else:
|
||||
resource = load_image_from_path(source_resource_path)
|
||||
if json.has("rect"):
|
||||
var atlas = AtlasTexture.new()
|
||||
atlas.atlas = resource
|
||||
atlas.region = Rect2(json.rect[0], json.rect[1], json.rect[2], json.rect[3])
|
||||
resource = atlas
|
||||
var sprite_frames = SpriteFrames.new()
|
||||
sprite_frames.add_frame("default", resource)
|
||||
resource = sprite_frames
|
||||
ResourceMode.TEXTURE:
|
||||
if json.get("source") is Array:
|
||||
resource = AnimatedTexture.new()
|
||||
resource.frames = json.get("source").size()
|
||||
var idx := 0
|
||||
for i in json.get("source"):
|
||||
var frame_path = ResourceSetter.get_pure_resource_path(json_file.resource_path.replace(json_file.resource_path.get_file(), i))
|
||||
print(frame_path)
|
||||
resource.set_frame_texture(idx, load_image_from_path(frame_path))
|
||||
idx += 1
|
||||
else:
|
||||
resource = load_image_from_path(source_resource_path)
|
||||
if json.has("rect"):
|
||||
var rect = json.rect
|
||||
var atlas = AtlasTexture.new()
|
||||
atlas.atlas = resource
|
||||
atlas.region = Rect2(rect[0], rect[1], rect[2], rect[3])
|
||||
resource = atlas
|
||||
ResourceMode.AUDIO:
|
||||
resource = load_audio_from_path(source_resource_path)
|
||||
ResourceMode.RAW:
|
||||
pass
|
||||
if cache.has(json_file.resource_path) == false and use_cache and not is_random:
|
||||
cache[json_file.resource_path] = resource
|
||||
return resource
|
||||
|
||||
func apply_properties(properties := {}) -> void:
|
||||
if property_node == null:
|
||||
return
|
||||
for i in properties.keys():
|
||||
property_node.set(i, properties[i])
|
||||
|
||||
func get_variation_json(json := {}) -> Dictionary:
|
||||
var level_theme = Global.level_theme
|
||||
if force_properties.has("Theme"):
|
||||
level_theme = force_properties.Theme
|
||||
|
||||
for i in json.keys().filter(func(key): return key.contains("config:")):
|
||||
if config_to_use != {}:
|
||||
var option_name = i.get_slice(":", 1)
|
||||
if config_to_use.options.has(option_name):
|
||||
json = get_variation_json(json[i][config_to_use.options[option_name]])
|
||||
break
|
||||
|
||||
if json.has(level_theme) == false:
|
||||
level_theme = "default"
|
||||
if json.has(level_theme):
|
||||
if json.get(level_theme).has("link"):
|
||||
json = get_variation_json(json[json.get(level_theme).get("link")])
|
||||
else:
|
||||
json = get_variation_json(json[level_theme])
|
||||
|
||||
var level_time = Global.theme_time
|
||||
if force_properties.has("Time"):
|
||||
level_time = force_properties.Time
|
||||
if json.has(level_time):
|
||||
json = get_variation_json(json[level_time])
|
||||
|
||||
var campaign = Global.current_campaign
|
||||
if force_properties.has("Campaign"):
|
||||
is_random = true
|
||||
campaign = force_properties.Campaign
|
||||
if json.has(campaign) == false:
|
||||
campaign = "SMB1"
|
||||
if json.has(campaign):
|
||||
if json.get(campaign).has("link"):
|
||||
json = get_variation_json(json[json.get(campaign).get("link")])
|
||||
else:
|
||||
json = get_variation_json(json[campaign])
|
||||
|
||||
if json.has("choices"):
|
||||
is_random = true
|
||||
json = get_variation_json(json.choices.pick_random())
|
||||
|
||||
var world = "World" + str(Global.world_num)
|
||||
if force_properties.has("World"):
|
||||
is_random = true
|
||||
world = "World" + str(force_properties.World)
|
||||
print(world)
|
||||
if json.has(world) == false:
|
||||
world = "World1"
|
||||
if json.has(world):
|
||||
if json.get(world).has("link"):
|
||||
json = get_variation_json(json[json.get(world).get("link")])
|
||||
else:
|
||||
json = get_variation_json(json[world])
|
||||
|
||||
var level_string = "Level" + str(Global.level_num)
|
||||
if json.has(level_string) == false:
|
||||
level_string = "Level1"
|
||||
if json.has(level_string):
|
||||
if json.get(level_string).has("link"):
|
||||
json = get_variation_json(json[json.get(level_string).get("link")])
|
||||
else:
|
||||
json = get_variation_json(json[level_string])
|
||||
|
||||
var game_mode = "GameMode:" + Global.game_mode_strings[Global.current_game_mode]
|
||||
if json.has(game_mode) == false:
|
||||
game_mode = "GameMode:" + Global.game_mode_strings[0]
|
||||
if json.has(game_mode):
|
||||
if json.get(game_mode).has("link"):
|
||||
json = get_variation_json(json[json.get(game_mode).get("link")])
|
||||
else:
|
||||
json = get_variation_json(json[game_mode])
|
||||
|
||||
var chara = "Character:" + Player.CHARACTERS[int(Global.player_characters[0])]
|
||||
if json.has(chara) == false:
|
||||
chara = "Character:Mario"
|
||||
if json.has(chara):
|
||||
if json.get(chara).has("link"):
|
||||
json = get_variation_json(json[json.get(chara).get("link")])
|
||||
else:
|
||||
json = get_variation_json(json[chara])
|
||||
|
||||
var boo = "RaceBoo:" + str(BooRaceHandler.boo_colour)
|
||||
if json.has(boo) == false:
|
||||
boo = "RaceBoo:0"
|
||||
if force_properties.has("RaceBoo"):
|
||||
boo = "RaceBoo:" + str(force_properties["RaceBoo"])
|
||||
if json.has(boo):
|
||||
if json.get(boo).has("link"):
|
||||
json = get_variation_json(json[json.get(boo).get("link")])
|
||||
else:
|
||||
json = get_variation_json(json[boo])
|
||||
|
||||
return json
|
||||
|
||||
func get_resource_pack_path(res_path := "", resource_pack := "") -> String:
|
||||
var user_path := res_path.replace("res://Assets", "user://resource_packs/" + resource_pack)
|
||||
user_path = user_path.replace("user://custom_characters/", "user://resource_packs/" + resource_pack + "/Sprites/Players/CustomCharacters/")
|
||||
if FileAccess.file_exists(user_path):
|
||||
if FileAccess.file_exists("user://resource_packs/" + resource_pack + "/config.json"):
|
||||
config_to_use = JSON.parse_string(FileAccess.open("user://resource_packs/" + resource_pack + "/config.json", FileAccess.READ).get_as_text())
|
||||
if config_to_use == null:
|
||||
Global.log_error("Error parsing Config File! (" + resource_pack + ")")
|
||||
config_to_use = {}
|
||||
return user_path
|
||||
else:
|
||||
return res_path
|
||||
|
||||
func create_sprite_frames_from_image(image: Resource, animation_json := {}) -> SpriteFrames:
|
||||
var sprite_frames = SpriteFrames.new()
|
||||
sprite_frames.remove_animation("default")
|
||||
for anim_name in animation_json.keys():
|
||||
sprite_frames.add_animation(anim_name)
|
||||
for frame in animation_json[anim_name].frames:
|
||||
var frame_texture = AtlasTexture.new()
|
||||
frame_texture.atlas = image
|
||||
frame_texture.region = Rect2(frame[0], frame[1], frame[2], frame[3])
|
||||
frame_texture.filter_clip = true
|
||||
sprite_frames.add_frame(anim_name, frame_texture)
|
||||
sprite_frames.set_animation_loop(anim_name, animation_json[anim_name].loop)
|
||||
sprite_frames.set_animation_speed(anim_name, animation_json[anim_name].speed)
|
||||
|
||||
return sprite_frames
|
||||
|
||||
func clear_cache() -> void:
|
||||
for i in cache.keys():
|
||||
if cache[i] == null:
|
||||
cache.erase(i)
|
||||
cache.clear()
|
||||
property_cache.clear()
|
||||
|
||||
func load_image_from_path(path := "") -> ImageTexture:
|
||||
if path.contains("res://"):
|
||||
if path.contains("NULL"):
|
||||
return null
|
||||
return load(path)
|
||||
var image = Image.new()
|
||||
if path == "":
|
||||
print([path, owner.name])
|
||||
image.load(path)
|
||||
return ImageTexture.create_from_image(image)
|
||||
|
||||
func load_audio_from_path(path := "") -> AudioStream:
|
||||
var stream = null
|
||||
if path.contains("res://"):
|
||||
return load(path)
|
||||
if path.contains(".wav"):
|
||||
stream = AudioStreamWAV.load_from_file(path)
|
||||
elif path.contains(".mp3"):
|
||||
stream = AudioStreamMP3.load_from_file(path)
|
||||
return stream
|
1
Scripts/Classes/Components/ResourceSetterNew.gd.uid
Executable file
1
Scripts/Classes/Components/ResourceSetterNew.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cbal8ms2oe1ik
|
12
Scripts/Classes/Components/ScalableCollisionPolygon.gd
Normal file
12
Scripts/Classes/Components/ScalableCollisionPolygon.gd
Normal file
@@ -0,0 +1,12 @@
|
||||
@tool
|
||||
extends CollisionPolygon2D
|
||||
|
||||
@export var offset := Vector2.ZERO
|
||||
@export var height := 0.0
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
update()
|
||||
|
||||
func update() -> void:
|
||||
var height_to_use = height
|
||||
position.y = -height_to_use / 2 * scale.y - offset.y
|
@@ -0,0 +1 @@
|
||||
uid://cwti0ks5sfov3
|
17
Scripts/Classes/Components/ScalableCollisionShape.gd
Normal file
17
Scripts/Classes/Components/ScalableCollisionShape.gd
Normal file
@@ -0,0 +1,17 @@
|
||||
@tool
|
||||
extends CollisionShape2D
|
||||
|
||||
@export var offset := Vector2.ZERO
|
||||
@export var link: Node2D
|
||||
|
||||
func _ready() -> void:
|
||||
set_process(Engine.is_editor_hint())
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
update()
|
||||
|
||||
func update() -> void:
|
||||
var height_to_use = shape.size.y
|
||||
if link != null:
|
||||
height_to_use *= link.scale.y * link.scale.y
|
||||
position.y = -height_to_use / 2 * scale.y - offset.y
|
1
Scripts/Classes/Components/ScalableCollisionShape.gd.uid
Normal file
1
Scripts/Classes/Components/ScalableCollisionShape.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cp1bh6fi6tpa5
|
27
Scripts/Classes/Components/ScoreNoteSpawner.gd
Normal file
27
Scripts/Classes/Components/ScoreNoteSpawner.gd
Normal file
@@ -0,0 +1,27 @@
|
||||
class_name ScoreNoteSpawner
|
||||
extends Node
|
||||
const ONE_UP_NOTE = preload("res://Scenes/Parts/OneUpNote.tscn")
|
||||
const SCORE_NOTE = preload("res://Scenes/Parts/ScoreNote.tscn")
|
||||
@export var note_offset := Vector2(0, -8)
|
||||
@export var add_score := false
|
||||
@export var play_sfx := false
|
||||
|
||||
func spawn_note(amount = 100, amount_2 := 0) -> void:
|
||||
if amount is not int or amount_2 != 0:
|
||||
amount = amount_2
|
||||
var note = SCORE_NOTE.instantiate()
|
||||
note.global_position = owner.global_position + note_offset
|
||||
if add_score:
|
||||
Global.score += amount
|
||||
note.get_node("Container/Label").text = str(amount)
|
||||
if play_sfx:
|
||||
play_death_sfx()
|
||||
Global.current_level.add_child(note)
|
||||
|
||||
func play_death_sfx() -> void:
|
||||
AudioManager.play_sfx("kick", owner.global_position)
|
||||
|
||||
func spawn_one_up_note() -> void:
|
||||
var note = ONE_UP_NOTE.instantiate()
|
||||
note.global_position = owner.global_position + note_offset
|
||||
owner.add_sibling(note)
|
1
Scripts/Classes/Components/ScoreNoteSpawner.gd.uid
Executable file
1
Scripts/Classes/Components/ScoreNoteSpawner.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://5octqlf4ohel
|
21
Scripts/Classes/Components/SecondQuestReplaceComponent.gd
Normal file
21
Scripts/Classes/Components/SecondQuestReplaceComponent.gd
Normal file
@@ -0,0 +1,21 @@
|
||||
class_name SecondQuestReplacer
|
||||
extends Node
|
||||
|
||||
@export_file("*.tscn") var new_scene := ""
|
||||
@export var properties: Array[String] = []
|
||||
|
||||
func _ready() -> void:
|
||||
if Global.second_quest and new_scene != "" and new_scene != owner.scene_file_path:
|
||||
if owner.owner != null:
|
||||
await owner.owner.ready
|
||||
var node = load(new_scene).instantiate()
|
||||
node.global_position = owner.global_position
|
||||
node.global_rotation = owner.global_rotation
|
||||
for i in properties:
|
||||
node.set(i, owner.get(i))
|
||||
owner.add_sibling(node)
|
||||
if owner is RopeElevatorPlatform:
|
||||
owner.linked_platform.linked_platform = node
|
||||
owner.queue_free()
|
||||
else:
|
||||
queue_free()
|
@@ -0,0 +1 @@
|
||||
uid://d0mqkvopasu8k
|
18
Scripts/Classes/Components/ShellDetection.gd
Normal file
18
Scripts/Classes/Components/ShellDetection.gd
Normal file
@@ -0,0 +1,18 @@
|
||||
class_name ShellDetection
|
||||
extends Node
|
||||
|
||||
@export var hitbox: Area2D = null
|
||||
|
||||
signal moving_shell_entered(shell: Node2D)
|
||||
|
||||
func _ready() -> void:
|
||||
hitbox.area_entered.connect(area_entered)
|
||||
|
||||
func area_entered(area: Area2D) -> void:
|
||||
if area.owner is Shell and area.owner != owner:
|
||||
if abs(area.owner.velocity.x) > 0:
|
||||
moving_shell_entered.emit(area.owner)
|
||||
area.owner.add_combo()
|
||||
|
||||
func destroy_shell(shell: Shell) -> void:
|
||||
shell.die_from_object(owner)
|
1
Scripts/Classes/Components/ShellDetection.gd.uid
Executable file
1
Scripts/Classes/Components/ShellDetection.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bbww34oiexbx2
|
10
Scripts/Classes/Components/TileGrabber.gd
Executable file
10
Scripts/Classes/Components/TileGrabber.gd
Executable file
@@ -0,0 +1,10 @@
|
||||
class_name TileGrabber
|
||||
extends Node
|
||||
|
||||
@export var value_name := "item"
|
||||
@export var saved_node: Node = null
|
||||
@export var delete_grabbed := false
|
||||
|
||||
func tile_grabbed(tile: Node) -> void:
|
||||
saved_node = tile
|
||||
owner.set(value_name, saved_node)
|
1
Scripts/Classes/Components/TileGrabber.gd.uid
Executable file
1
Scripts/Classes/Components/TileGrabber.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cxucnfgd5yivr
|
21
Scripts/Classes/Components/TilesetTextureSetter.gd
Normal file
21
Scripts/Classes/Components/TilesetTextureSetter.gd
Normal file
@@ -0,0 +1,21 @@
|
||||
class_name TilesetTextureSetter
|
||||
extends Node
|
||||
|
||||
@export var tile_map: TileMapLayer
|
||||
@export var texture: Texture = null:
|
||||
set(value):
|
||||
texture = value
|
||||
texture_changed.emit()
|
||||
|
||||
signal texture_changed
|
||||
|
||||
@export var atlas_id := 0
|
||||
|
||||
func _ready() -> void:
|
||||
update()
|
||||
texture_changed.connect(update)
|
||||
|
||||
func update() -> void:
|
||||
var source = tile_map.tile_set.get_source(atlas_id)
|
||||
if source != null:
|
||||
source.texture = texture
|
1
Scripts/Classes/Components/TilesetTextureSetter.gd.uid
Executable file
1
Scripts/Classes/Components/TilesetTextureSetter.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://73oviwf6bbys
|
33
Scripts/Classes/Components/TimerSprite.gd
Normal file
33
Scripts/Classes/Components/TimerSprite.gd
Normal file
@@ -0,0 +1,33 @@
|
||||
class_name TimerSprite
|
||||
extends Sprite2D
|
||||
|
||||
@export var max_value := 1.0
|
||||
@export var value_name := ""
|
||||
|
||||
@export_enum("Global", "Player", "Timer") var object := 0
|
||||
@export var timer: Timer = null
|
||||
|
||||
@export var warn_sfx: AudioStreamPlayer = null
|
||||
|
||||
@export var warn_threshold := 0.7
|
||||
|
||||
var can_warn := false
|
||||
|
||||
func _ready() -> void:
|
||||
texture = ResourceSetter.get_resource(texture, self)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
var node = owner if object == 1 else Global
|
||||
if object == 2:
|
||||
node = timer
|
||||
var value = node.get(value_name)
|
||||
var percent = inverse_lerp(max_value, 0, value)
|
||||
percent = clamp(percent, 0, 1)
|
||||
get_parent().visible = percent < 1 and Settings.file.visuals.visible_timers
|
||||
frame = lerp(0, 6, percent)
|
||||
if percent >= warn_threshold and Settings.file.audio.extra_sfx == 1:
|
||||
if can_warn:
|
||||
can_warn = false
|
||||
AudioManager.play_global_sfx("timer_warning")
|
||||
else:
|
||||
can_warn = true
|
1
Scripts/Classes/Components/TimerSprite.gd.uid
Normal file
1
Scripts/Classes/Components/TimerSprite.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dn5efttgugwvb
|
12
Scripts/Classes/Components/TimerStarter.gd
Executable file
12
Scripts/Classes/Components/TimerStarter.gd
Executable file
@@ -0,0 +1,12 @@
|
||||
class_name TimerStarter
|
||||
extends Node
|
||||
|
||||
func _ready() -> void:
|
||||
if Global.level_editor != null:
|
||||
Global.level_editor.level_start.connect(start_timers)
|
||||
start_timers()
|
||||
|
||||
func start_timers() -> void:
|
||||
for i in get_children():
|
||||
if i is Timer:
|
||||
i.start()
|
1
Scripts/Classes/Components/TimerStarter.gd.uid
Executable file
1
Scripts/Classes/Components/TimerStarter.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cucfssmfmttbk
|
19
Scripts/Classes/Components/TrackJoint.gd
Normal file
19
Scripts/Classes/Components/TrackJoint.gd
Normal file
@@ -0,0 +1,19 @@
|
||||
class_name TrackJoint
|
||||
extends Node
|
||||
|
||||
signal attached
|
||||
|
||||
@export var offset := Vector2(0, 8)
|
||||
@export var movement_node: Node = null
|
||||
@export var disable_physics := true
|
||||
var rider: TrackRider = null
|
||||
var is_attached := false
|
||||
|
||||
func detach() -> void:
|
||||
if rider == null: return
|
||||
owner.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_INHERIT
|
||||
rider.attached_entity = null
|
||||
rider.queue_free()
|
||||
get_parent().reparent(rider.get_parent())
|
||||
owner.reset_physics_interpolation()
|
||||
|
1
Scripts/Classes/Components/TrackJoint.gd.uid
Normal file
1
Scripts/Classes/Components/TrackJoint.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d4a7yp6e55u8t
|
33
Scripts/Classes/Editor/EditorSelectorScroller.gd
Executable file
33
Scripts/Classes/Editor/EditorSelectorScroller.gd
Executable file
@@ -0,0 +1,33 @@
|
||||
class_name EditorSelectorScroller
|
||||
extends Control
|
||||
|
||||
@export var selected_index := 0
|
||||
|
||||
var selectors: Array[Control] = []
|
||||
|
||||
func _ready() -> void:
|
||||
for i in get_children():
|
||||
if i is EditorTileSelector:
|
||||
selectors.append(i)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
handle_inputs()
|
||||
for i in selectors.size():
|
||||
selectors[i].visible = i == selected_index
|
||||
selectors[i].notification(NOTIFICATION_MOUSE_ENTER)
|
||||
|
||||
func handle_inputs() -> void:
|
||||
var hovered = false
|
||||
for i in selectors:
|
||||
if i.get_node("Button").is_hovered():
|
||||
hovered = true
|
||||
break
|
||||
if not hovered:
|
||||
return
|
||||
if Input.is_action_just_pressed("scroll_up"):
|
||||
selected_index += 1
|
||||
warp_mouse(get_local_mouse_position())
|
||||
if Input.is_action_just_pressed("scroll_down"):
|
||||
selected_index -= 1
|
||||
warp_mouse(get_local_mouse_position())
|
||||
selected_index = clamp(selected_index, 0, selectors.size() - 1)
|
1
Scripts/Classes/Editor/EditorSelectorScroller.gd.uid
Executable file
1
Scripts/Classes/Editor/EditorSelectorScroller.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://c7xreamx2wvdq
|
89
Scripts/Classes/Editor/EditorTileSelector.gd
Normal file
89
Scripts/Classes/Editor/EditorTileSelector.gd
Normal file
@@ -0,0 +1,89 @@
|
||||
class_name EditorTileSelector
|
||||
extends Control
|
||||
|
||||
@export var tile_name := ""
|
||||
@export_enum("Tile", "Entity", "Terrain") var type := 0
|
||||
@export var icon_texture: Resource = null
|
||||
@export var icon_region_override := Rect2(0, 0, 0, 0)
|
||||
|
||||
@export var secondary_icon_texture: Resource = null
|
||||
@export var secondary_icon_region_override := Rect2(0, 0, 0, 0)
|
||||
|
||||
@export_category("Entity")
|
||||
@export var entity_scene: PackedScene = null
|
||||
@export var tile_offset := Vector2i(0, 8)
|
||||
|
||||
@export_category("Tile")
|
||||
@export var source_id := 0
|
||||
@export var terrain_id := 0
|
||||
@export var tile_coords := Vector2i.ZERO
|
||||
@export var flip_h := false
|
||||
@export var flip_v := false
|
||||
|
||||
var texture_rect_region := Rect2(0, 0, 0, 0)
|
||||
|
||||
signal tile_selected(selector: EditorTileSelector)
|
||||
|
||||
|
||||
var mouse_hovered := false
|
||||
|
||||
var disabled := false
|
||||
|
||||
func _ready() -> void:
|
||||
set_icon_texture()
|
||||
set_second_icon_texture()
|
||||
update_visuals()
|
||||
set_process(false)
|
||||
if tile_selected.is_connected(owner.on_tile_selected) == false:
|
||||
tile_selected.connect(owner.on_tile_selected)
|
||||
%NameLabel.text = tile_name
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
var target_position = get_viewport().get_mouse_position()
|
||||
target_position.x = clamp(target_position.x, %Panel.size.x / 2, (get_viewport().get_visible_rect().size.x) - %Panel.size.x / 2)
|
||||
%NamePanel.position = target_position
|
||||
|
||||
func set_icon_texture():
|
||||
if icon_texture == null:
|
||||
return
|
||||
if icon_texture is JSON:
|
||||
$ResourceSetterNew.resource_json = icon_texture
|
||||
$ResourceSetterNew.update_resource()
|
||||
else:
|
||||
%Icon.texture = ResourceSetter.get_resource(icon_texture, %Icon)
|
||||
|
||||
func set_second_icon_texture():
|
||||
if secondary_icon_texture == null:
|
||||
return
|
||||
if secondary_icon_texture is JSON:
|
||||
$ResourceSetterNew2.resource_json = secondary_icon_texture
|
||||
$ResourceSetterNew2.update_resource()
|
||||
elif secondary_icon_texture is ThemedResource:
|
||||
%SecondaryIcon.texture = ResourceSetter.get_resource(secondary_icon_texture, %SecondaryIcon)
|
||||
else:
|
||||
%SecondaryIcon.texture = secondary_icon_texture
|
||||
|
||||
|
||||
func on_pressed() -> void:
|
||||
tile_selected.emit(self)
|
||||
|
||||
|
||||
func update_visuals() -> void:
|
||||
if icon_region_override != Rect2(0, 0, 0, 0):
|
||||
%Icon.region_rect = icon_region_override
|
||||
if secondary_icon_region_override != Rect2(0, 0, 0, 0):
|
||||
%SecondaryIcon.region_rect = secondary_icon_region_override
|
||||
modulate = Color.WHITE if not disabled else Color.DIM_GRAY
|
||||
|
||||
func set_mouse_hovered(hovered := false) -> void:
|
||||
%NamePanel.visible = hovered and tile_name.is_empty() == false
|
||||
mouse_hovered = hovered
|
||||
$Button.disabled = disabled
|
||||
set_process(hovered)
|
||||
|
||||
func on_mouse_entered() -> void:
|
||||
set_mouse_hovered(true)
|
||||
|
||||
|
||||
func on_mouse_exited() -> void:
|
||||
set_mouse_hovered(false)
|
1
Scripts/Classes/Editor/EditorTileSelector.gd.uid
Executable file
1
Scripts/Classes/Editor/EditorTileSelector.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://dx0yj4sc1lnpu
|
691
Scripts/Classes/Editor/LevelEditor.gd
Normal file
691
Scripts/Classes/Editor/LevelEditor.gd
Normal file
@@ -0,0 +1,691 @@
|
||||
class_name LevelEditor
|
||||
extends Node
|
||||
|
||||
const CAM_MOVE_SPEED_SLOW := 128
|
||||
const CAM_MOVE_SPEED_FAST := 256
|
||||
|
||||
var cursor_tile_position := Vector2i.ZERO
|
||||
|
||||
const CURSOR_OFFSET := Vector2(-8, -8)
|
||||
|
||||
var mode := 0
|
||||
var current_entity_selector: EditorTileSelector = null
|
||||
var current_entity_scene: PackedScene = null
|
||||
var current_spawn_offset := Vector2i.ZERO
|
||||
var current_tile_source := 0
|
||||
var current_tile_coords := Vector2i.ZERO
|
||||
var current_tile_flip := Vector2.ZERO ## 1 = true, 0 = false, x = hori, y = vert
|
||||
|
||||
var menu_open := false
|
||||
var testing_level := false
|
||||
var entity_tiles := [{}, {}, {}, {}, {}]
|
||||
|
||||
static var playing_level := false
|
||||
|
||||
var tile_list: Array[EditorTileSelector] = []
|
||||
|
||||
var tile_offsets := {}
|
||||
|
||||
signal level_start
|
||||
|
||||
var selected_tile_index := 0
|
||||
|
||||
var can_move_cam := true
|
||||
|
||||
var music_track_list: Array[String] = [ "res://Assets/Audio/BGM/Silence.json","res://Assets/Audio/BGM/Athletic.json", "res://Assets/Audio/BGM/Autumn.json", "res://Assets/Audio/BGM/Beach.json", "res://Assets/Audio/BGM/Bonus.json", "res://Assets/Audio/BGM/Bowser.json", "res://Assets/Audio/BGM/FinalBowser.json", "res://Assets/Audio/BGM/Castle.json", "res://Assets/Audio/BGM/CoinHeaven.json", "res://Assets/Audio/BGM/Desert.json", "res://Assets/Audio/BGM/Garden.json", "res://Assets/Audio/BGM/GhostHouse.json", "res://Assets/Audio/BGM/Jungle.json", "res://Assets/Audio/BGM/Mountain.json", "res://Assets/Audio/BGM/Overworld.json", "res://Assets/Audio/BGM/Pipeland.json", "res://Assets/Audio/BGM/BooRace.json", "res://Assets/Audio/BGM/Sky.json", "res://Assets/Audio/BGM/Snow.json", "res://Assets/Audio/BGM/Space.json", "res://Assets/Audio/BGM/Underground.json", "res://Assets/Audio/BGM/Underwater.json", "res://Assets/Audio/BGM/Volcano.json", "res://Assets/Audio/BGM/Airship.json"]
|
||||
var music_track_names: Array[String] = ["BGM_NONE", "BGM_ATHLETIC", "BGM_AUTUMN", "BGM_BEACH", "BGM_BONUS", "BGM_BOWSER", "BGM_FINALBOWSER", "BGM_CASTLE", "BGM_COINHEAVEN", "BGM_DESERT", "BGM_GARDEN", "BGM_GHOSTHOUSE", "BGM_JUNGLE", "BGM_MOUNTAIN", "BGM_OVERWORLD", "BGM_PIPELAND", "BGM_RACE", "BGM_SKY", "BGM_SNOW", "BGM_SPACE", "BGM_UNDERGROUND", "BGM_UNDERWATER", "BGM_VOLCANO", "BGM_AIRSHIP"]
|
||||
|
||||
|
||||
var bgm_id := 0
|
||||
|
||||
const MUSIC_TRACK_DIR := "res://Assets/Audio/BGM/"
|
||||
|
||||
var select_start := Vector2i.ZERO
|
||||
var select_end := Vector2i.ZERO
|
||||
|
||||
signal close_confirm(save: bool)
|
||||
|
||||
var sub_level_id := 0
|
||||
|
||||
const BLANK_FILE := {"Info": {}, "Levels": [{}, {}, {}, {}, {}]}
|
||||
|
||||
static var level_file = {"Info": {}, "Levels": [{}, {}, {}, {}, {}]}
|
||||
|
||||
var current_layer := 0
|
||||
@onready var tile_layer_nodes: Array[TileMapLayer] = [%TileLayer1, %TileLayer2, %TileLayer3, %TileLayer4, %TileLayer5]
|
||||
@onready var entity_layer_nodes := [%EntityLayer1, %EntityLayer2, %EntityLayer3, %EntityLayer4, %EntityLayer5]
|
||||
var saved_entity_layers := [null, null, null, null, null]
|
||||
|
||||
var copied_node: Node = null
|
||||
var copied_tile_offset := Vector2.ZERO
|
||||
var copied_tile_source_id := -1
|
||||
var copied_tile_atlas_coors := Vector2i.ZERO
|
||||
var copied_tile_terrain_id := -1
|
||||
|
||||
|
||||
const CURSOR_ERASOR := preload("uid://d0j1my4kuapgb")
|
||||
const CURSOR_PEN = preload("uid://bt0brcjv0efmw")
|
||||
const CURSOR_PENCIL = preload("uid://c8oyhfvlv2gvh")
|
||||
const CURSOR_RULER = preload("uid://cg2wkxnmjgplf")
|
||||
const CURSOR_INSPECT = preload("uid://1l3foyjqeej")
|
||||
|
||||
var multi_selecting := false
|
||||
|
||||
var inspect_mode := false
|
||||
var inspect_menu_open := false
|
||||
var current_inspect_tile: Node = null
|
||||
|
||||
var selection_filter := ""
|
||||
|
||||
static var level_author := ""
|
||||
static var level_desc := ""
|
||||
static var level_name := ""
|
||||
static var difficulty := 0
|
||||
|
||||
var current_terrain_id := 0
|
||||
|
||||
static var load_play := false
|
||||
|
||||
signal tile_selected(tile_selector: EditorTileSelector)
|
||||
|
||||
var tile_menu_open := false
|
||||
|
||||
signal editor_start
|
||||
|
||||
enum EditorState{IDLE, TILE_MENU, MODIFYING_TILE, SAVE_MENU, SELECTING_TILE_SCENE, QUITTING, PLAYTESTING, TRACK_EDITING}
|
||||
|
||||
var current_state := EditorState.IDLE
|
||||
|
||||
static var play_pipe_transition := false
|
||||
static var play_door_transition := false
|
||||
|
||||
const BOUNDARY_CONNECT_TILE := Vector2i.ZERO
|
||||
|
||||
var undo_redo = UndoRedo.new()
|
||||
|
||||
func _ready() -> void:
|
||||
$TileMenu.hide()
|
||||
Global.set_discord_status("In The Level Editor...")
|
||||
Global.level_editor = self
|
||||
playing_level = false
|
||||
menu_open = $TileMenu.visible
|
||||
Global.get_node("GameHUD").hide()
|
||||
Global.can_time_tick = false
|
||||
for i in get_tree().get_nodes_in_group("Selectors"):
|
||||
tile_list.append(i)
|
||||
var idx := 0
|
||||
for i in music_track_list:
|
||||
if i == "": continue
|
||||
$%LevelMusic.add_item(tr(music_track_names[idx]).to_upper())
|
||||
idx += 1
|
||||
await get_tree().process_frame
|
||||
Level.start_level_path = scene_file_path
|
||||
var layer_idx := 0
|
||||
for i in entity_layer_nodes:
|
||||
for x in i.get_children():
|
||||
entity_tiles[layer_idx][x.get_meta("tile_position")] = x
|
||||
if level_file != {}:
|
||||
Level.can_set_time = true
|
||||
$LevelLoader.load_level(Checkpoint.sublevel_id)
|
||||
if Global.current_game_mode == Global.GameMode.CUSTOM_LEVEL:
|
||||
$Info.hide()
|
||||
%Grid.hide()
|
||||
play_level()
|
||||
else:
|
||||
Global.current_game_mode = Global.GameMode.LEVEL_EDITOR
|
||||
else:
|
||||
Global.current_game_mode = Global.GameMode.LEVEL_EDITOR
|
||||
for i: Player in get_tree().get_nodes_in_group("Players"):
|
||||
i.recenter_camera()
|
||||
%LevelName.text = level_name
|
||||
%LevelAuthor.text = level_author
|
||||
%Description.text = level_desc
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if current_state == EditorState.IDLE:
|
||||
handle_tile_cursor()
|
||||
if [EditorState.IDLE, EditorState.TRACK_EDITING].has(current_state):
|
||||
handle_camera(delta)
|
||||
%ThemeName.text = Global.level_theme
|
||||
handle_hud()
|
||||
if Input.is_action_just_pressed("editor_open_menu"):
|
||||
if current_state == EditorState.IDLE:
|
||||
open_tile_menu()
|
||||
elif current_state == EditorState.TILE_MENU:
|
||||
close_tile_menu()
|
||||
if Input.is_action_just_pressed("editor_play") and (current_state == EditorState.IDLE or current_state == EditorState.PLAYTESTING) and Global.current_game_mode == Global.GameMode.LEVEL_EDITOR:
|
||||
Checkpoint.passed = false
|
||||
if current_state == EditorState.PLAYTESTING:
|
||||
stop_testing()
|
||||
else:
|
||||
play_level()
|
||||
handle_layers()
|
||||
|
||||
func handle_hud() -> void:
|
||||
$TileCursor.visible = current_state == EditorState.IDLE
|
||||
$Info.visible = not playing_level
|
||||
%Grid.visible = not playing_level
|
||||
|
||||
func quit_editor() -> void:
|
||||
%QuitDialog.show()
|
||||
|
||||
signal level_saved
|
||||
|
||||
func open_tile_menu() -> void:
|
||||
$TileMenu.visible = true
|
||||
current_state = EditorState.TILE_MENU
|
||||
for i in get_tree().get_nodes_in_group("Selectors"):
|
||||
i.disabled = false
|
||||
i.update_visuals()
|
||||
|
||||
func close_tile_menu() -> void:
|
||||
$TileMenu.visible = false
|
||||
current_state = EditorState.IDLE
|
||||
for i in get_tree().get_nodes_in_group("Selectors"):
|
||||
i.disabled = false
|
||||
|
||||
func save_level_before_exit() -> void:
|
||||
tile_menu_open = true
|
||||
open_save_dialog()
|
||||
await level_saved
|
||||
go_back_to_menu()
|
||||
|
||||
func copy_node(tile_position := Vector2i.ZERO) -> void:
|
||||
if tile_layer_nodes[current_layer].get_used_cells().has(tile_position):
|
||||
var terrain_id = BetterTerrain.get_cell(tile_layer_nodes[current_layer], tile_position)
|
||||
if terrain_id != -2:
|
||||
copied_tile_terrain_id = terrain_id
|
||||
return
|
||||
mode = 0
|
||||
copied_tile_source_id = tile_layer_nodes[current_layer].get_cell_source_id(tile_position)
|
||||
copied_tile_atlas_coors = tile_layer_nodes[current_layer].get_cell_atlas_coords(tile_position)
|
||||
elif entity_tiles[current_layer].has(tile_position):
|
||||
copied_node = entity_tiles[current_layer][tile_position].duplicate()
|
||||
copied_tile_offset = entity_tiles[current_layer][tile_position].get_meta("tile_offset")
|
||||
|
||||
func cut_node(tile_position := Vector2i.ZERO) -> void:
|
||||
var old_copy = copied_node
|
||||
copy_node(tile_position)
|
||||
if copied_node != old_copy:
|
||||
remove_tile(tile_position)
|
||||
|
||||
func paste_node(tile_position := Vector2i.ZERO) -> void:
|
||||
place_tile(tile_position, true)
|
||||
|
||||
func go_back_to_menu() -> void:
|
||||
Global.transition_to_scene("res://Scenes/Levels/CustomLevelMenu.tscn")
|
||||
|
||||
func open_bindings_menu() -> void:
|
||||
$TileMenu/EditorKeybindsView.open()
|
||||
current_state = EditorState.SAVE_MENU
|
||||
await $TileMenu/EditorKeybindsView.closed
|
||||
current_state = EditorState.TILE_MENU
|
||||
|
||||
func open_save_dialog() -> void:
|
||||
current_state = EditorState.SAVE_MENU
|
||||
can_move_cam = false
|
||||
%SaveLevelDialog.show()
|
||||
menu_open = true
|
||||
|
||||
func stop_testing() -> void:
|
||||
cleanup()
|
||||
return_to_editor()
|
||||
|
||||
|
||||
func cleanup() -> void:
|
||||
get_tree().paused = false
|
||||
Global.p_switch_timer = 0
|
||||
Global.cancel_score_tally()
|
||||
playing_level = !playing_level
|
||||
play_pipe_transition = false
|
||||
play_door_transition = false
|
||||
Door.unlocked_doors = []
|
||||
LevelPersistance.reset_states()
|
||||
KeyItem.total_collected = 0
|
||||
Global.get_node("GameHUD").visible = playing_level
|
||||
Global.p_switch_active = false
|
||||
if Global.current_game_mode == Global.GameMode.LEVEL_EDITOR:
|
||||
Global.time = $Level.time_limit
|
||||
elif Level.can_set_time and playing_level:
|
||||
Global.time = $Level.time_limit
|
||||
Global.can_time_tick = playing_level
|
||||
print(Global.can_time_tick)
|
||||
|
||||
func update_music() -> void:
|
||||
if music_track_list[bgm_id] != "":
|
||||
$Level.music = load(music_track_list[bgm_id].replace(".remap", ""))
|
||||
else:
|
||||
$Level.music = null
|
||||
|
||||
func play_level() -> void:
|
||||
$TileMenu.hide()
|
||||
menu_open = false
|
||||
update_music()
|
||||
reset_values_for_play()
|
||||
%Camera.enabled = false
|
||||
level_start.emit()
|
||||
get_tree().call_group("Players", "editor_level_start")
|
||||
parse_tiles()
|
||||
if Global.current_game_mode != Global.GameMode.CUSTOM_LEVEL:
|
||||
level_file = await $LevelSaver.save_level(level_name, level_author, level_desc, difficulty)
|
||||
current_state = EditorState.PLAYTESTING
|
||||
handle_hud()
|
||||
|
||||
func parse_tiles() -> void:
|
||||
saved_entity_layers = [null, null, null, null, null]
|
||||
var idx := 0
|
||||
for i in entity_layer_nodes:
|
||||
if is_instance_valid(i) == false:
|
||||
continue
|
||||
saved_entity_layers[idx] = i.duplicate(DUPLICATE_USE_INSTANTIATION)
|
||||
if i is Player:
|
||||
i.direction = 1
|
||||
i.velocity = Vector2.ZERO
|
||||
i.global_position = i.global_position.snapped(Vector2(8, 8))
|
||||
i.ready.emit()
|
||||
i.set_process_mode(Node.PROCESS_MODE_INHERIT)
|
||||
idx += 1
|
||||
|
||||
func return_to_editor() -> void:
|
||||
AudioManager.stop_all_music()
|
||||
$Level.music = null
|
||||
%Camera.global_position = get_viewport().get_camera_2d().get_screen_center_position()
|
||||
%Camera.reset_physics_interpolation()
|
||||
return_editor_tiles()
|
||||
%Camera.enabled = true
|
||||
%Camera.make_current()
|
||||
editor_start.emit()
|
||||
current_state = EditorState.IDLE
|
||||
handle_hud()
|
||||
|
||||
func return_editor_tiles() -> void:
|
||||
for i in entity_layer_nodes:
|
||||
i.queue_free()
|
||||
var idx := 0
|
||||
for i in entity_tiles:
|
||||
i.clear()
|
||||
$Level.add_child(saved_entity_layers[idx])
|
||||
entity_layer_nodes[idx] = saved_entity_layers[idx]
|
||||
idx += 1
|
||||
var layer_idx = 0
|
||||
for x in entity_layer_nodes:
|
||||
x.process_mode = PROCESS_MODE_DISABLED
|
||||
for i in x.get_children():
|
||||
i.owner = self
|
||||
var _tile_position = (Vector2i(i.global_position) - Vector2i(8, 8)) / 16
|
||||
entity_tiles[layer_idx].set(i.get_meta("tile_position", Vector2i.ZERO), i)
|
||||
layer_idx += 1
|
||||
|
||||
func handle_camera(delta: float) -> void:
|
||||
var input_vector = Input.get_vector("editor_cam_left", "editor_cam_right", "editor_cam_up", "editor_cam_down")
|
||||
%Camera.global_position += input_vector * (CAM_MOVE_SPEED_FAST if Input.is_action_pressed("editor_cam_fast") else CAM_MOVE_SPEED_SLOW) * delta
|
||||
%Camera.global_position.y = clamp(%Camera.global_position.y, $Level.vertical_height + (get_viewport().get_visible_rect().size.y / 2), 32 - (get_viewport().get_visible_rect().size.y / 2))
|
||||
%Camera.global_position.x = clamp(%Camera.global_position.x, -256 + (get_viewport().get_visible_rect().size.x / 2), INF)
|
||||
|
||||
func handle_layers() -> void:
|
||||
if Input.is_action_just_pressed("layer_up"):
|
||||
current_layer += 1
|
||||
if Input.is_action_just_pressed("layer_down"):
|
||||
current_layer -= 1
|
||||
current_layer = clamp(current_layer, 0, entity_layer_nodes.size() - 1)
|
||||
var idx := 0
|
||||
for i in entity_layer_nodes:
|
||||
i.z_index = 0 if current_layer == idx or playing_level else -1
|
||||
i.modulate = Color(1, 1, 1, 1) if current_layer == idx or playing_level else Color(1, 1, 1, 0.5)
|
||||
tile_layer_nodes[idx].modulate = i.modulate
|
||||
tile_layer_nodes[idx].z_index = i.z_index - 1
|
||||
%LayerDisplay.get_child(idx).modulate = Color.WHITE if current_layer == idx else Color(0.1, 0.1, 0.1, 0.5)
|
||||
idx += 1
|
||||
%LayerLabel.text = "Layer " + str(current_layer + 1)
|
||||
|
||||
func save_level() -> void:
|
||||
level_author = %LevelAuthor.text
|
||||
level_desc = %Description.text
|
||||
level_name = %LevelName.text
|
||||
difficulty = %DifficultySlider.value
|
||||
var file_name = level_name.to_pascal_case() + ".lvl"
|
||||
%SaveLevelDialog.hide()
|
||||
menu_open = false
|
||||
level_file = $LevelSaver.save_level(level_name, level_author, level_desc, difficulty)
|
||||
$LevelSaver.write_file(level_file, file_name)
|
||||
%SaveDialog.text = str("'") + file_name + "'" + " Saved."
|
||||
%SaveAnimation.play("Show")
|
||||
level_saved.emit()
|
||||
|
||||
func close_save_menu() -> void:
|
||||
can_move_cam = true
|
||||
%SaveLevelDialog.hide()
|
||||
menu_open = false
|
||||
current_state = EditorState.TILE_MENU
|
||||
|
||||
const CUSTOM_LEVEL_DIR := "user://custom_levels/"
|
||||
|
||||
func handle_tile_cursor() -> void:
|
||||
Input.set_custom_mouse_cursor(null)
|
||||
var snapped_position = ((%TileCursor.get_global_mouse_position() - CURSOR_OFFSET).snapped(Vector2(16, 16))) + CURSOR_OFFSET
|
||||
%TileCursor.global_position = (snapped_position)
|
||||
var old_index := selected_tile_index
|
||||
var tile_position = global_position_to_tile_position(snapped_position + Vector2(-8, -8))
|
||||
tile_position.y = clamp(tile_position.y, -30, 1)
|
||||
tile_position.x = clamp(tile_position.x, -16, INF)
|
||||
cursor_tile_position = tile_position
|
||||
|
||||
inspect_mode = Input.is_action_pressed("editor_inspect") and not multi_selecting
|
||||
if inspect_mode and current_state == EditorState.IDLE:
|
||||
handle_inspection(tile_position)
|
||||
return
|
||||
|
||||
if Input.is_action_pressed("mb_left"):
|
||||
if Input.is_action_pressed("editor_select") and not multi_selecting:
|
||||
multi_select_start(tile_position)
|
||||
elif Input.is_action_pressed("editor_select") == false:
|
||||
multi_selecting = false
|
||||
place_tile(tile_position)
|
||||
Input.set_custom_mouse_cursor(CURSOR_PENCIL)
|
||||
|
||||
if Input.is_action_pressed("mb_right"):
|
||||
if Input.is_action_pressed("editor_select") and not multi_selecting:
|
||||
multi_select_start(tile_position)
|
||||
Input.set_custom_mouse_cursor(CURSOR_RULER)
|
||||
elif Input.is_action_pressed("editor_select") == false:
|
||||
multi_selecting = false
|
||||
remove_tile(tile_position)
|
||||
Input.set_custom_mouse_cursor(CURSOR_ERASOR)
|
||||
|
||||
if current_state == EditorState.IDLE:
|
||||
if Input.is_action_just_pressed("scroll_up"):
|
||||
selected_tile_index += 1
|
||||
if Input.is_action_just_pressed("scroll_down"):
|
||||
selected_tile_index -= 1
|
||||
|
||||
if Input.is_action_just_pressed("editor_copy"):
|
||||
copy_node(tile_position)
|
||||
elif Input.is_action_just_pressed("editor_cut"):
|
||||
cut_node(tile_position)
|
||||
elif Input.is_action_pressed("ui_paste"):
|
||||
paste_node(tile_position)
|
||||
|
||||
if Input.is_action_just_pressed("pick_tile"):
|
||||
pick_tile(tile_position)
|
||||
|
||||
handle_multi_selecting(tile_position)
|
||||
if old_index != selected_tile_index:
|
||||
selected_tile_index = wrap(selected_tile_index, 0, tile_list.size())
|
||||
on_tile_selected(tile_list[selected_tile_index])
|
||||
show_scroll_preview()
|
||||
|
||||
func pick_tile(tile_position := Vector2i.ZERO) -> void:
|
||||
if tile_layer_nodes[current_layer].get_used_cells().has(tile_position):
|
||||
var terrain_id = BetterTerrain.get_cell(tile_layer_nodes[current_layer], tile_position)
|
||||
if terrain_id != -2:
|
||||
mode = 2
|
||||
current_terrain_id = terrain_id
|
||||
return
|
||||
mode = 0
|
||||
current_tile_source = tile_layer_nodes[current_layer].get_cell_source_id(tile_position)
|
||||
current_tile_coords = tile_layer_nodes[current_layer].get_cell_atlas_coords(tile_position)
|
||||
elif entity_tiles[current_layer].has(tile_position):
|
||||
mode = 1
|
||||
current_entity_scene = load(entity_tiles[current_layer][tile_position].scene_file_path)
|
||||
current_spawn_offset = entity_tiles[current_layer][tile_position].get_meta("tile_offset")
|
||||
|
||||
func handle_inspection(tile_position := Vector2i.ZERO) -> void:
|
||||
Input.set_custom_mouse_cursor(CURSOR_INSPECT)
|
||||
if Input.is_action_just_pressed("mb_left"):
|
||||
if entity_tiles[current_layer].get(tile_position) != null:
|
||||
open_tile_properties(entity_tiles[current_layer][tile_position])
|
||||
|
||||
func open_tile_properties(tile: Node2D) -> void:
|
||||
var properties = get_tile_properties(tile)
|
||||
if properties.is_empty():
|
||||
return
|
||||
|
||||
current_inspect_tile = tile
|
||||
%TileModifierMenu.override_scenes = tile.get_node("EditorPropertyExposer").properties_force_selector
|
||||
%TileModifierMenu.properties = properties
|
||||
%TileModifierMenu.editing_node = current_inspect_tile
|
||||
%TileModifierMenu.open()
|
||||
current_state = EditorState.MODIFYING_TILE
|
||||
%TileModifierMenu.position = tile.get_global_transform_with_canvas().origin
|
||||
%TileModifierMenu.position.x = clamp(%TileModifierMenu.position.x, 0, get_viewport().get_visible_rect().size.x - %TileModifierMenu.size.x - 2)
|
||||
%TileModifierMenu.position.y = clamp(%TileModifierMenu.position.y, 0, get_viewport().get_visible_rect().size.y - %TileModifierMenu.size.y - 2)
|
||||
|
||||
await %TileModifierMenu.closed
|
||||
current_state = EditorState.IDLE
|
||||
|
||||
func multi_select_start(tile_position := Vector2i.ZERO) -> void:
|
||||
select_start = tile_position
|
||||
multi_selecting = true
|
||||
|
||||
func handle_multi_selecting(tile_position := Vector2i.ZERO) -> void:
|
||||
select_end = tile_position
|
||||
%MultiSelectRect.visible = multi_selecting
|
||||
var top_corner := select_start
|
||||
if select_start.x > select_end.x:
|
||||
top_corner.x = select_end.x
|
||||
if select_start.y > select_end.y:
|
||||
top_corner.y = select_end.y
|
||||
%MultiSelectRect.global_position = top_corner * 16
|
||||
%MultiSelectRect.size = abs(select_end - select_start) * 16 + Vector2i(16, 16)
|
||||
if multi_selecting:
|
||||
Input.set_custom_mouse_cursor(CURSOR_RULER)
|
||||
if Input.is_action_just_released("mb_left"):
|
||||
for x in abs(select_end.x - select_start.x) + 1:
|
||||
for y in abs(select_end.y - select_start.y) + 1:
|
||||
var position = top_corner + Vector2i(x, y)
|
||||
place_tile(position)
|
||||
multi_selecting = false
|
||||
if Input.is_action_just_released("mb_right"):
|
||||
for x in abs(select_end.x - select_start.x) + 1:
|
||||
for y in abs(select_end.y - select_start.y) + 1:
|
||||
var position = top_corner + Vector2i(x, y)
|
||||
remove_tile(position)
|
||||
multi_selecting = false
|
||||
|
||||
func show_scroll_preview() -> void:
|
||||
$TileCursor/Previews.show()
|
||||
for i in [$"TileCursor/Previews/-2", $"TileCursor/Previews/-1", $"TileCursor/Previews/0", $"TileCursor/Previews/1", $"TileCursor/Previews/2"]:
|
||||
var position = selected_tile_index + int(i.name)
|
||||
var selector = tile_list[wrap(position, 0, tile_list.size())]
|
||||
i.texture = selector.get_node("%Icon").texture
|
||||
i.get_node("Overlay").texture = selector.get_node("%SecondaryIcon").texture
|
||||
i.get_node("Overlay").region_rect = selector.get_node("%SecondaryIcon").region_rect
|
||||
i.region_rect = selector.get_node("%Icon").region_rect
|
||||
$TileCursor/Timer.start()
|
||||
await $TileCursor/Timer.timeout
|
||||
$TileCursor/Previews.hide()
|
||||
|
||||
func open_tile_selection_menu_scene_ref(selector: TilePropertySceneRef) -> void:
|
||||
open_tile_menu()
|
||||
current_state = EditorState.SELECTING_TILE_SCENE
|
||||
selection_filter = selector.editing_node.get_node("EditorPropertyExposer").filters[selector.tile_property_name]
|
||||
for i in get_tree().get_nodes_in_group("Selectors"):
|
||||
i.disabled = !i.has_meta(selection_filter)
|
||||
i.update_visuals()
|
||||
await tile_selected
|
||||
if is_instance_valid(selector) == false:
|
||||
return
|
||||
selector.set_scene(current_entity_selector)
|
||||
close_tile_menu()
|
||||
current_state = EditorState.MODIFYING_TILE
|
||||
func on_tile_selected(selector: EditorTileSelector) -> void:
|
||||
mode = selector.type
|
||||
current_entity_selector = selector
|
||||
selected_tile_index = tile_list.find(selector)
|
||||
print(selected_tile_index)
|
||||
if selector.type == 1:
|
||||
current_entity_scene = selector.entity_scene
|
||||
current_spawn_offset = selector.tile_offset
|
||||
elif selector.type == 2:
|
||||
current_terrain_id = selector.terrain_id
|
||||
else:
|
||||
current_tile_source = selector.source_id
|
||||
current_tile_coords = selector.tile_coords
|
||||
current_tile_flip = Vector2(selector.flip_h, selector.flip_v)
|
||||
tile_selected.emit(selector)
|
||||
|
||||
func reset_values_for_play() -> void:
|
||||
Global.score = 0
|
||||
Global.lives = 0
|
||||
Global.coins = 0
|
||||
cleanup()
|
||||
|
||||
func place_tile(tile_position := Vector2i.ZERO, use_copy := false) -> void:
|
||||
$TileCursor/Previews.hide()
|
||||
var mode_to_use = mode
|
||||
if use_copy:
|
||||
if copied_node != null:
|
||||
mode_to_use = 1
|
||||
elif copied_tile_terrain_id != -1:
|
||||
mode_to_use = 2
|
||||
else:
|
||||
mode_to_use = 0
|
||||
if mode_to_use == 0:
|
||||
var alt_tile := 0
|
||||
if current_tile_flip.x != 0:
|
||||
alt_tile += TileSetAtlasSource.TRANSFORM_FLIP_H
|
||||
if current_tile_flip.y != 0:
|
||||
alt_tile += TileSetAtlasSource.TRANSFORM_FLIP_V
|
||||
remove_tile(tile_position)
|
||||
check_connect_boundary_tiles(tile_position, current_layer)
|
||||
var source = current_tile_source
|
||||
var atlas = current_tile_coords
|
||||
if use_copy:
|
||||
source = copied_tile_source_id
|
||||
atlas = copied_tile_atlas_coors
|
||||
tile_layer_nodes[current_layer].set_cell(tile_position, source, atlas, alt_tile)
|
||||
elif mode_to_use == 2:
|
||||
var terrain_id = current_terrain_id
|
||||
if use_copy:
|
||||
terrain_id = copied_tile_terrain_id
|
||||
remove_tile(tile_position)
|
||||
check_connect_boundary_tiles(tile_position, current_layer)
|
||||
BetterTerrain.set_cell(tile_layer_nodes[current_layer], tile_position, terrain_id)
|
||||
else:
|
||||
var overlapping_tile = null
|
||||
if entity_tiles[current_layer].get(tile_position) != null and current_entity_scene != null:
|
||||
overlapping_tile = entity_tiles[current_layer][tile_position]
|
||||
if overlapping_tile.scene_file_path == current_entity_scene.resource_path:
|
||||
return
|
||||
remove_tile(tile_position)
|
||||
var node: Node = null
|
||||
if use_copy and copied_node != null:
|
||||
node = copied_node.duplicate()
|
||||
var offset := Vector2i.ZERO
|
||||
var off_string = EntityIDMapper.map[EntityIDMapper.get_map_id(copied_node.scene_file_path)][1].split(",")
|
||||
offset = Vector2i(int(off_string[0]), int(off_string[1]))
|
||||
|
||||
node.global_position = (tile_position * 16) + (Vector2i(8, 8) + offset)
|
||||
else:
|
||||
node = current_entity_scene.instantiate()
|
||||
node.global_position = (tile_position * 16) + (Vector2i(8, 8) + current_spawn_offset)
|
||||
node.set_meta("tile_position", tile_position)
|
||||
node.set_meta("tile_offset", current_spawn_offset)
|
||||
entity_layer_nodes[current_layer].add_child(node)
|
||||
node.reset_physics_interpolation()
|
||||
entity_tiles[current_layer].set(tile_position, node)
|
||||
BetterTerrain.update_terrain_cell(tile_layer_nodes[current_layer], tile_position, true)
|
||||
|
||||
func check_connect_boundary_tiles(tile_position := Vector2i.ZERO, layer := 0) -> void:
|
||||
if tile_position.y > 0:
|
||||
tile_layer_nodes[layer].set_cell(tile_position + Vector2i.DOWN, 6, BOUNDARY_CONNECT_TILE)
|
||||
if tile_position.x <= -16:
|
||||
tile_layer_nodes[layer].set_cell(tile_position + Vector2i.LEFT, 6, BOUNDARY_CONNECT_TILE)
|
||||
if tile_position.y > 0 and tile_position.x <= -16:
|
||||
tile_layer_nodes[layer].set_cell(tile_position + Vector2i.LEFT + Vector2i.DOWN, 6, BOUNDARY_CONNECT_TILE)
|
||||
|
||||
func remove_tile(tile_position := Vector2i.ZERO) -> void:
|
||||
$TileCursor/Previews.hide()
|
||||
tile_layer_nodes[current_layer].set_cell(tile_position, -1)
|
||||
if entity_tiles[current_layer].get(tile_position) != null:
|
||||
if entity_tiles[current_layer].get(tile_position) is Player:
|
||||
return
|
||||
entity_tiles[current_layer].get(tile_position).queue_free()
|
||||
entity_tiles[current_layer].erase(tile_position)
|
||||
BetterTerrain.update_terrain_cell(tile_layer_nodes[current_layer], tile_position, true)
|
||||
|
||||
func global_position_to_tile_position(position := Vector2.ZERO) -> Vector2i:
|
||||
return Vector2i(position / 16)
|
||||
|
||||
func theme_selected(theme_idx := 0) -> void:
|
||||
ResourceSetterNew.cache.clear()
|
||||
AudioManager.current_level_theme = ""
|
||||
$Level.theme = Level.THEME_IDXS[theme_idx]
|
||||
Global.level_theme = $Level.theme
|
||||
Global.level_theme_changed.emit()
|
||||
|
||||
func time_selected(time_idx := 0) -> void:
|
||||
ResourceSetterNew.cache.clear()
|
||||
AudioManager.current_level_theme = ""
|
||||
$Level.theme_time = ["Day", "Night"][time_idx]
|
||||
Global.theme_time = ["Day", "Night"][time_idx]
|
||||
$Level/LevelBG.time_of_day = time_idx
|
||||
Global.level_theme_changed.emit()
|
||||
|
||||
func music_selected(music_idx := 0) -> void:
|
||||
bgm_id = music_idx
|
||||
|
||||
func campaign_selected(campaign_idx := 0) -> void:
|
||||
ResourceSetterNew.cache.clear()
|
||||
Global.current_campaign = ["SMB1", "SMBLL", "SMBS", "SMBANN"][campaign_idx]
|
||||
$Level.campaign = Global.current_campaign
|
||||
Global.level_theme_changed.emit()
|
||||
|
||||
func backscroll_toggled(new_value := false) -> void:
|
||||
$Level.can_backscroll = new_value
|
||||
|
||||
func height_limit_changed(new_value := 0) -> void:
|
||||
$Level.vertical_height = -new_value
|
||||
|
||||
func time_limit_changed(new_value := 0) -> void:
|
||||
$Level.time_limit = new_value
|
||||
|
||||
func low_gravity_toggled(new_value := false) -> void:
|
||||
Global.entity_gravity = 10 if new_value == false else 5
|
||||
for i: Player in get_tree().get_nodes_in_group("Players"):
|
||||
i.low_gravity = new_value
|
||||
|
||||
func transition_to_sublevel(sub_lvl_idx := 0) -> void:
|
||||
Global.can_pause = false
|
||||
var play_transition = playing_level
|
||||
if play_transition:
|
||||
await Global.do_fake_transition()
|
||||
else:
|
||||
level_file = $LevelSaver.save_level(level_name, level_author, level_desc, difficulty)
|
||||
LevelPersistance.reset_states()
|
||||
sub_level_id = sub_lvl_idx
|
||||
$LevelLoader.load_level(sub_lvl_idx)
|
||||
await get_tree().physics_frame
|
||||
if (play_pipe_transition or play_door_transition) and play_transition:
|
||||
parse_tiles()
|
||||
if play_pipe_transition:
|
||||
get_tree().call_group("Pipes", "run_pipe_check")
|
||||
if play_door_transition:
|
||||
get_tree().call_group("Doors", "run_door_check")
|
||||
update_music()
|
||||
PipeArea.exiting_pipe_id = -1
|
||||
Global.can_pause = true
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventJoypadButton or event is InputEventJoypadMotion:
|
||||
%ControllerInputWarning.show()
|
||||
else:
|
||||
%ControllerInputWarning.hide()
|
||||
|
||||
func get_tile_properties(tile: Node) -> Array:
|
||||
var properties := []
|
||||
var old_properties := []
|
||||
if tile.get_node_or_null("EditorPropertyExposer") == null:
|
||||
return []
|
||||
|
||||
var property_exposer: PropertyExposer = tile.get_node_or_null("EditorPropertyExposer")
|
||||
old_properties = tile.get_property_list()
|
||||
for i in old_properties:
|
||||
if property_exposer.properties.has(i.name):
|
||||
properties.append(i)
|
||||
return properties
|
||||
|
||||
|
||||
func on_tree_exited() -> void:
|
||||
pass # Replace with function body.
|
1
Scripts/Classes/Editor/LevelEditor.gd.uid
Executable file
1
Scripts/Classes/Editor/LevelEditor.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://c2lnc7vnq3xth
|
11
Scripts/Classes/Editor/OptionButtonThatIsntFuckingBlurry.gd
Normal file
11
Scripts/Classes/Editor/OptionButtonThatIsntFuckingBlurry.gd
Normal file
@@ -0,0 +1,11 @@
|
||||
class_name BetterOptionButton
|
||||
extends OptionButton
|
||||
|
||||
## SO. AS GODOT IS THE BIGGEST LOAD OF SHIT, ITS POPUP MENUS ARE FILTERED, AS SEEN HERE
|
||||
## https://github.com/godotengine/godot/issues/103552
|
||||
## EVEN THOUGH, ITS MARKED AS FIXED AND COMPLETE, IT CLEARLY FUCKING ISNT, SO I HAVE TO USE
|
||||
## THIS BULLSHIT, TO MAKE IT NOT BLURRY.
|
||||
|
||||
func _ready() -> void:
|
||||
get_popup().get_viewport().canvas_item_default_texture_filter = Viewport.DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST
|
||||
get_popup().canvas_item_default_texture_filter = Viewport.DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST
|
@@ -0,0 +1 @@
|
||||
uid://codql1f5eca56
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user