added the game

This commit is contained in:
JHDev2006
2025-09-13 16:30:32 +01:00
parent 5ef689109b
commit 3773bdaf64
3616 changed files with 263702 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
class_name BetterAnimatedSprite2D
extends AnimatedSprite2D
@export var do_offset := true
func _process(_delta: float) -> void:
if do_offset:
on_frame_changed()
func on_frame_changed() -> void:
if sprite_frames == null: return
var texture = sprite_frames.get_frame_texture(animation, frame)
if texture != null:
position.y = -(texture.get_height() / 2.0)

View File

@@ -0,0 +1 @@
uid://caq1qiwmy0mox

View File

@@ -0,0 +1,21 @@
extends Node2D
func _ready() -> void:
set_process(false)
if owner is Block:
$Joint.remote_path = $Joint.get_path_to(owner.visuals)
func _process(_delta: float) -> void:
owner.visuals.z_index = z_index
func bounce_block() -> void:
set_process(true)
owner.visuals.show()
owner.visuals.z_index = 3
owner.get_parent().move_child(owner, -1)
owner.bouncing = true
$Animations.play("BlockHit")
await $Animations.animation_finished
owner.visuals.z_index = 0
owner.bouncing = false
set_process(false)

View File

@@ -0,0 +1 @@
uid://bna1qn33m01d5

View File

@@ -0,0 +1,19 @@
extends Node2D
@onready var particles := [$TL, $TR, $BL, $BR]
var particle_directions := [Vector2(-1, -3), Vector2(1, -3), Vector2(-1, -1), Vector2(1, -1)]
var particle_velocities := [Vector2.ZERO, Vector2.ZERO, Vector2.ZERO, Vector2.ZERO]
var particle_rotations := [0.0, 0.0, 0.0, 0.0]
func _ready() -> void:
for i in 4:
particle_velocities[i] = 70 * particle_directions[i]
func _physics_process(delta: float) -> void:
for i in 4:
particles[i].global_position += particle_velocities[i] * delta
particle_velocities[i] += Vector2(0, 15 / delta) * delta
particle_rotations[i] += (1080 * particle_directions[i].x) * delta
particles[i].global_rotation_degrees = snapped(particle_rotations[i], 90)

View File

@@ -0,0 +1 @@
uid://f50767fxajwh

View File

@@ -0,0 +1,14 @@
extends Node2D
var can_kill := false
func _ready() -> void:
await get_tree().create_timer(0.5, false).timeout
can_kill = true
func _physics_process(delta: float) -> void:
global_position.y -= 32 * delta
if global_position.y < -176:
queue_free()
elif $WaterDetection.get_overlapping_bodies().is_empty() and can_kill:
queue_free()

View File

@@ -0,0 +1 @@
uid://denfttv4kn7f6

View File

@@ -0,0 +1,42 @@
extends Node2D
@export var item: PackedScene = preload("res://Scenes/Prefabs/Entities/Enemies/BulletBill.tscn")
var timer := 15
const MAX_TIME := 15
const HARD_TIME := 7
func _physics_process(_delta: float) -> void:
if randi_range(0, 8) == 8:
timer -= 1
if timer <= 0:
if Global.second_quest:
timer = HARD_TIME
else:
timer = MAX_TIME
fire()
func fire() -> void:
if BulletBill.amount >= 3 or $PlayerDetect.get_overlapping_areas().any(func(area: Area2D): return area.owner is Player) or is_inside_tree() == false:
return
var player: Player = get_tree().get_first_node_in_group("Players")
var direction = sign(player.global_position.x - global_position.x)
$BlockCheck.scale.x = direction
$BlockCheck/RayCast2D.force_raycast_update()
if $BlockCheck/RayCast2D.is_colliding():
return
var node = item.instantiate()
node.global_position = global_position + Vector2(0, 8)
node.set("direction", direction)
if node is CharacterBody2D:
node.position.x += 8 * direction
node.set("velocity", Vector2(100 * direction, 0))
if node is not BulletBill:
AudioManager.play_sfx("cannon", global_position)
else:
node.cannon = true
add_sibling(node)
func flag_die() -> void:
queue_free()

View File

@@ -0,0 +1 @@
uid://0btfo4kjnnrg

View File

@@ -0,0 +1,148 @@
class_name CameraHandler
extends Node2D
@onready var last_position = global_position
@export var camera: Camera2D = null
@export var camera_center_joint: Node2D = null
var camera_position := Vector2.ZERO
var camera_offset := Vector2(8, 0)
var camera_right_limit := 9999999
var player_offset := 0.0
var can_scroll_left := true
var can_scroll_right := true
static var cam_locked := false
var scrolling := false
var cam_direction := 1
func _exit_tree() -> void:
cam_locked = false
func _physics_process(delta: float) -> void:
handle_camera(delta)
last_position = global_position
func handle_camera(delta: float) -> void:
can_scroll_left = camera_position.x + camera_offset.x > -255
can_scroll_right = camera_position.x + camera_offset.x < camera_right_limit - 1
if ["Pipe", "Climb", "FlagPole"].has(owner.state_machine.state.name):
handle_vertical_scrolling(delta)
do_limits()
camera.global_position = camera_position + camera_offset
return
if not cam_locked:
handle_horizontal_scrolling(delta)
handle_vertical_scrolling(delta)
handle_offsets(delta)
do_limits()
camera.global_position = camera_position + camera_offset
update_camera_barriers()
func update_camera_barriers() -> void:
if get_viewport() != null:
camera_center_joint.global_position = get_viewport().get_camera_2d().get_screen_center_position()
camera_center_joint.get_node("LeftWall").position.x = -(get_viewport_rect().size.x / 2)
camera_center_joint.get_node("RightWall").position.x = (get_viewport_rect().size.x / 2)
func handle_horizontal_scrolling(delta: float) -> void:
scrolling = false
var true_velocity = (global_position - last_position) / delta
var true_vel_dir = sign(true_velocity.x)
if (owner.is_on_wall() and owner.direction == -owner.get_wall_normal().x):
true_vel_dir = 0
true_velocity.x = 0
## RIGHT MOVEMENT
if true_vel_dir == 1 and can_scroll_right:
cam_direction = 1
if global_position.x >= camera_position.x:
var offset = 0
scrolling = true
if camera_position.x <= global_position.x - 4:
offset = camera_position.x - global_position.x + abs(true_velocity.x * delta)
camera_position.x = global_position.x + offset
elif global_position.x >= camera_position.x - get_viewport_rect().size.x / 8:
if true_velocity.x > 75:
camera_position.x += true_velocity.x * delta / 2
else:
camera_position.x += true_velocity.x * delta
## LEFT MOVEMENT
elif true_vel_dir == -1 and can_scroll_left and Global.current_level.can_backscroll:
cam_direction = -1
if global_position.x <= camera_position.x:
scrolling = true
var offset = 0
if camera_position.x >= global_position.x + 4:
offset = camera_position.x - global_position.x - abs(true_velocity.x * delta)
camera_position.x = global_position.x + offset
elif global_position.x <= camera_position.x + get_viewport_rect().size.x / 4:
if true_velocity.x < -75:
camera_position.x += true_velocity.x * delta / 2
else:
camera_position.x += true_velocity.x * delta
func handle_vertical_scrolling(_delta: float) -> void:
## VERTICAL MOVEMENT
if global_position.y < camera_position.y and owner.is_on_floor():
camera_position.y = move_toward(camera_position.y, global_position.y, 3)
elif global_position.y < camera_position.y - 64:
camera_position.y = global_position.y + 64
elif global_position.y > camera_position.y + 32:
camera_position.y = global_position.y - 32
func tween_ahead() -> void:
if scrolling == false: return
await get_tree().create_timer(0.25).timeout
var tween = create_tween()
tween.tween_property(self, "camera_position:x", camera_position.x + (32 * cam_direction), 0.25)
func recenter_camera() -> void:
camera_position = global_position
last_position = camera_position
camera_position += camera_offset
do_limits()
camera.global_position = camera_position
func handle_offsets(delta: float) -> void:
var true_velocity = (global_position - last_position) / delta
var true_vel_dir = sign(true_velocity.x)
if owner.velocity.x == 0 or (owner.is_on_wall() and owner.direction == -owner.get_wall_normal().x):
true_vel_dir = 0
true_velocity.x = 0
if Global.current_level.can_backscroll:
if true_vel_dir != 0 and abs(true_velocity.x) > 80:
if abs(camera_position.x - global_position.x) <= 64:
camera_offset.x = move_toward(camera_offset.x, 8 * true_vel_dir, abs(true_velocity.x) / 200)
else:
camera_offset.x = 8
func do_limits() -> void:
camera_right_limit = clamp(Player.camera_right_limit, -256 + (get_viewport().get_visible_rect().size.x), INF)
camera_position.x = clamp(camera_position.x, point_to_camera_limit(-256 - camera_offset.x, -1), point_to_camera_limit(camera_right_limit - camera_offset.x, 1))
camera_position.y = clamp(camera_position.y, point_to_camera_limit_y(Global.current_level.vertical_height, -1), point_to_camera_limit_y(32, 1))
var wall_enabled := true
if is_instance_valid(Global.level_editor):
if Global.level_editor.playing_level == false:
wall_enabled = false
$"../CameraCenterJoint/LeftWall".set_collision_layer_value(1, wall_enabled)
var level_exit = false
if owner.state_machine != null:
level_exit = owner.state_machine.state.name == "LevelExit"
$"../CameraCenterJoint/RightWall".set_collision_layer_value(1, wall_enabled and level_exit == false)
func point_to_camera_limit(point := 0, point_dir := -1) -> float:
return point + ((get_viewport_rect().size.x / 2.0) * -point_dir)
func point_to_camera_limit_y(point := 0, point_dir := -1) -> float:
return point + ((get_viewport_rect().size.y / 2.0) * -point_dir)

View File

@@ -0,0 +1 @@
uid://dgmaoklmc0wfa

View File

@@ -0,0 +1,22 @@
class_name CameraRightLimit
extends Node2D
@export var reset_on_delete := true
@export var lock_camera := false
func _enter_tree() -> void:
Player.camera_right_limit = int(global_position.x)
func _exit_tree() -> void:
if reset_on_delete:
Player.camera_right_limit = int(99999999)
func return_camera_to_normal() -> void:
for i in get_tree().get_nodes_in_group("Players"):
CameraHandler.cam_locked = false
i.reset_camera_to_center()
func on_screen_entered() -> void:
if lock_camera:
CameraHandler.cam_locked = true

View File

@@ -0,0 +1 @@
uid://bdq0373j5n5o0

View File

@@ -0,0 +1,107 @@
extends Node2D
signal victory_begin
const CASTLE_COMPLETE = preload("res://Assets/Audio/BGM/CastleComplete.mp3")
var cam_move := false
@export_range(8, 20, 1) var length := 13
@export var end_timer := false
@export var do_tally := true
signal axe_touched
var bowser_present := true
func _ready() -> void:
await get_tree().physics_frame
$Axe/CameraRightLimit._enter_tree()
func on_area_entered(area: Area2D) -> void:
if area.owner is Player:
destroy_bridge(area.owner)
func destroy_bridge(player: Player) -> void:
Global.can_pause = false
for i in get_tree().get_nodes_in_group("Enemies"):
if i is BowserFlame:
i.queue_free()
elif i is Hammer:
i.queue_free()
if (end_timer and Global.current_game_mode == Global.GameMode.MARATHON) or Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
SpeedrunHandler.run_finished()
if end_timer:
if Global.world_num > 8:
Global.unlock_achievement(Global.AchievementID.SMBLL_WORLD9)
match Global.current_campaign:
"SMB1": Global.unlock_achievement(Global.AchievementID.SMB1_CLEAR)
"SMBLL": Global.unlock_achievement(Global.AchievementID.SMBLL_CLEAR)
"SMBS": Global.unlock_achievement(Global.AchievementID.SMBS_CLEAR)
"SMBANN": Global.unlock_achievement(Global.AchievementID.SMBANN_CLEAR)
bowser_present = get_tree().get_first_node_in_group("Bowser") != null
player.velocity = Vector2.ZERO
Global.can_time_tick = false
axe_touched.emit()
$Axe.queue_free()
if bowser_present:
for i in get_tree().get_nodes_in_group("Bowser"):
i.bridge_fall()
get_tree().paused = true
await get_tree().create_timer(0.5).timeout
for i in $Bridge.get_children():
if i.visible:
AudioManager.play_sfx("block_break", i.global_position)
if Settings.file.visuals.bridge_animation == 0:
bridge_piece_break(i)
else:
bridge_piece_fall(i)
await get_tree().create_timer(0.1).timeout
await get_tree().create_timer(1.5).timeout
get_tree().paused = false
victory_sequence(player)
func bridge_piece_fall(node: Node2D) -> void:
var tween = create_tween()
tween.set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_SINE)
tween.tween_property(node, "global_position:y", node.global_position.y + 128, 0.5)
const BRIDGE_DESTRUCTION_PARTICLE = preload("uid://cwfjdgsyh35h6")
func bridge_piece_break(node: Node2D) -> void:
var particle = BRIDGE_DESTRUCTION_PARTICLE.instantiate()
particle.global_position = node.global_position
particle.process_mode = Node.PROCESS_MODE_ALWAYS
add_sibling(particle)
node.modulate.a = 0
func _physics_process(delta: float) -> void:
if cam_move and $Camera.global_position.x < Player.camera_right_limit:
$Camera.global_position.x += 96 * delta
$Camera.global_position.x = clamp($Camera.global_position.x, -INF, Player.camera_right_limit)
func victory_sequence(player: Player) -> void:
get_tree().call_group("Enemies", "flag_die")
Global.level_complete_begin.emit()
victory_begin.emit()
cam_move = true
$Camera.limit_right = Player.camera_right_limit
$Camera.global_position = get_viewport().get_camera_2d().get_screen_center_position()
$Camera.reset_physics_interpolation()
player.state_machine.transition_to("LevelExit")
$Camera.make_current()
if Global.current_game_mode == Global.GameMode.BOO_RACE:
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.RACE_WIN, 99, false)
await AudioManager.music_override_player.finished
Global.current_level.transition_to_next_level()
else:
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.CASTLE_COMPLETE, 99, false)
await get_tree().create_timer(1, false).timeout
if do_tally:
Global.tally_time()
func on_victory_begin() -> void:
pass # Replace with function body.

View File

@@ -0,0 +1 @@
uid://drigdwsriqtma

View File

@@ -0,0 +1,13 @@
extends Node2D
@export var play_end_music := false
var can_menu := false
const ENDING = preload("res://Assets/Audio/BGM/Ending.mp3")
func begin() -> void:
for player in get_tree().get_nodes_in_group("Players"):
player.z_index = -2
$CameraRightLimit._enter_tree()
await get_tree().create_timer(1, false).timeout
Global.tally_time()
await get_tree().create_timer(6, false).timeout
Global.transition_to_scene("res://Scenes/Levels/ChallengeModeCastleResults.tscn")

View File

@@ -0,0 +1 @@
uid://v5rwovoqjpag

View File

@@ -0,0 +1,88 @@
extends Node2D
@export var play_end_music := false
var can_menu := false
const ENDING = preload("res://Assets/Audio/BGM/Ending.mp3")
func _ready() -> void:
if $Sprite is AnimatedSprite2D and Global.current_campaign == "SMBANN":
$Sprite.play("Idle")
Global.level_complete_begin.connect(begin)
for i in [$SpeedrunMSG/ThankYou, $StandardMSG/ThankYou]:
i.text = tr(i.text).replace("{PLAYER}", tr(Player.CHARACTER_NAMES[int(Global.player_characters[0])]))
func begin() -> void:
$StaticBody2D/CollisionShape2D.set_deferred("disabled", false)
%PBMessage.modulate.a = int(SpeedrunHandler.timer < SpeedrunHandler.best_time)
if play_end_music:
Global.game_beaten = true
SaveManager.write_save()
play_music()
%Time.text = tr(%Time.text).replace("{TIME}", SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(SpeedrunHandler.timer)))
$CameraRightLimit._enter_tree()
await get_tree().create_timer(3, false).timeout
if Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE or (Global.current_game_mode == Global.GameMode.MARATHON and play_end_music):
show_message($SpeedrunMSG)
else:
show_message($StandardMSG)
if not play_end_music:
await get_tree().create_timer(7, false).timeout
exit_level()
func exit_level() -> void:
match Global.current_game_mode:
Global.GameMode.MARATHON_PRACTICE:
Global.open_marathon_results()
Global.GameMode.CUSTOM_LEVEL:
Global.transition_to_scene("res://Scenes/Levels/CustomLevelMenu.tscn")
Global.GameMode.LEVEL_EDITOR:
Global.level_editor.stop_testing()
_:
if Global.current_campaign == "SMBANN":
Global.open_disco_results()
return
if Global.world_num < 1:
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
else:
Global.current_level.transition_to_next_level()
func do_tally() -> void:
pass
func play_music() -> void:
await AudioManager.music_override_player.finished
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.ENDING, 999999, false)
if [Global.GameMode.MARATHON, Global.GameMode.MARATHON_PRACTICE].has(Global.current_game_mode) == false:
show_message($EndingSpeech)
await get_tree().create_timer(5, false).timeout
can_menu = true
else:
can_menu = true
func _process(_delta: float) -> void:
if can_menu and Input.is_action_just_pressed("jump_0"):
can_menu = false
peach_level_exit()
func show_message(message_node: Node) -> void:
for i in message_node.get_children():
i.show()
await get_tree().create_timer(1).timeout
func peach_level_exit() -> void:
match Global.current_game_mode:
Global.GameMode.MARATHON:
Global.open_marathon_results()
Global.GameMode.MARATHON_PRACTICE:
Global.open_marathon_results()
Global.GameMode.CUSTOM_LEVEL:
Global.transition_to_scene("res://Scenes/Levels/CustomLevelMenu.tscn")
Global.GameMode.LEVEL_EDITOR:
Global.level_editor.play_toggle()
_:
if Global.current_campaign == "SMBLL" and Global.world_num == 8:
Global.current_level.transition_to_next_level()
elif Global.current_game_mode == Global.GameMode.CAMPAIGN:
CreditsLevel.go_to_title_screen = true
Global.transition_to_scene("res://Scenes/Levels/Credits.tscn")
else: Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")

View File

@@ -0,0 +1 @@
uid://j76u1aqhwey4

View File

@@ -0,0 +1,10 @@
class_name CastleVisual
extends Node2D
@export var sprite: Sprite2D = null
var use_sprite := false
func _process(_delta: float) -> void:
$Tiles.visible = not use_sprite
$Sprite.visible = use_sprite

View File

@@ -0,0 +1 @@
uid://cdvxqkeqa7en5

View File

@@ -0,0 +1,20 @@
class_name ChallengeNodes
extends Node
@export var nodes_to_delete: Array[Node]
@export var no_report := false
@export var force_on := false
func _ready() -> void:
if force_on and Global.current_game_mode == Global.GameMode.NONE:
Global.current_game_mode = Global.GameMode.CHALLENGE
if Global.current_game_mode != Global.GameMode.CHALLENGE:
queue_free()
else:
ChallengeModeHandler.red_coins = 0
for i in 5:
if ChallengeModeHandler.is_coin_collected([0, 1, 2, 3, 4][i]):
ChallengeModeHandler.red_coins += 1
for i in nodes_to_delete:
if i != null:
i.queue_free()

View File

@@ -0,0 +1 @@
uid://cgm3opb5qudc1

View File

@@ -0,0 +1,60 @@
class_name Checkpoint
extends Node2D
@export var nodes_to_delete: Array[Node] = []
@export var optional := false
signal crossed(player: Player)
signal respawned
static var passed := false
static var respawn_position := Vector2.ZERO
static var level := ""
static var sublevel_id := 0
static var old_state := [[], []]
func _enter_tree() -> void:
if passed:
LevelPersistance.active_nodes = old_state.duplicate(true)
func _ready() -> void:
if [Global.GameMode.CHALLENGE, Global.GameMode.MARATHON_PRACTICE].has(Global.current_game_mode):
queue_free()
return
if has_meta("is_flag") == false:
hide()
if Settings.file.difficulty.checkpoint_style != 0:
queue_free()
if passed and PipeArea.exiting_pipe_id == -1 and Global.current_game_mode != Global.GameMode.LEVEL_EDITOR and Level.vine_return_level == "":
for i in nodes_to_delete:
i.queue_free()
for i in get_tree().get_nodes_in_group("Players"):
i.global_position = self.global_position
i.reset_physics_interpolation()
i.recenter_camera()
respawned.emit()
func _exit_tree() -> void:
pass
func on_area_entered(area: Area2D) -> void:
if area.owner is Player and not passed:
var player: Player = area.owner
player.passed_checkpoint()
passed = true
old_state = LevelPersistance.active_nodes.duplicate(true)
Level.start_level_path = Global.current_level.scene_file_path
if Global.current_game_mode == Global.GameMode.LEVEL_EDITOR or Global.current_game_mode == Global.GameMode.CUSTOM_LEVEL:
sublevel_id = Global.level_editor.sub_level_id
if Settings.file.difficulty.checkpoint_style == 2 and has_meta("is_flag"):
if player.power_state.state_name == "Small":
player.get_power_up("Big")
respawn_position = global_position
crossed.emit(area.owner)
func on_tree_exiting() -> void:
pass # Replace with function body.

View File

@@ -0,0 +1 @@
uid://dtdc4oaygllfa

View File

@@ -0,0 +1,8 @@
class_name ClassicThemeNode
extends Node
@export_enum("Overworld", "Underground", "Desert", "Snow", "Jungle", "Beach", "Garden", "Mountain", "Skyland", "Autumn", "Pipeland", "Space", "Underwater", "Volcano", "Castle") var classic_theme := "Overworld"
@export var nodes_to_delete: Array[Node] = []
func _ready() -> void:
queue_free()

View File

@@ -0,0 +1 @@
uid://don436dsjhc8w

View File

@@ -0,0 +1,26 @@
class_name AllCoinsCollectedCheck
extends Node
signal checked
func check() -> void:
if get_tree().get_nodes_in_group("Coins").is_empty() and Global.current_game_mode == Global.GameMode.CHALLENGE:
await get_tree().create_timer(1, false).timeout
$CanvasLayer.show()
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.COIN_HEAVEN_BONUS, 99, false, false)
await get_tree().create_timer(1, false).timeout
await score_tween()
await get_tree().create_timer(1, false).timeout
await get_tree().process_frame
checked.emit()
func score_tween() -> void:
Global.tallying_score = true
Global.get_node("ScoreTally").play()
var tween = create_tween()
tween.tween_property(Global, "score", Global.score + 10000, 2)
await tween.finished
Global.get_node("ScoreTallyEnd").play()
Global.get_node("ScoreTally").stop()
Global.tallying_score = false
return

View File

@@ -0,0 +1 @@
uid://ds3idw2a2udai

View File

@@ -0,0 +1,13 @@
class_name CoinHeavenWarpPoint
extends Node2D
@export_file("*.tscn") var heaven_scene := ""
func _ready() -> void:
Level.vine_warp_level = heaven_scene
if Level.in_vine_level and PipeArea.exiting_pipe_id == -1:
Level.in_vine_level = false
for i in get_tree().get_nodes_in_group("Players"):
i.global_position = global_position
i.reset_physics_interpolation()
i.recenter_camera()

View File

@@ -0,0 +1 @@
uid://pfwgmuchergf

View File

@@ -0,0 +1,25 @@
class_name ColourPaletteSampler
extends Node
@export var texture: Texture2D = null:
set(value):
texture = value
update()
signal updated
@export var coords := Vector2i.ZERO
@export var node_to_affect: Node = null
@export var value_to_set := ""
func _ready() -> void:
update()
Global.level_theme_changed.connect(update)
func update() -> void:
if node_to_affect == null or texture == null:
return
var colour_to_sample: Color = Color.WHITE
var image = texture.get_image()
colour_to_sample = image.get_pixelv(coords)
node_to_affect.set(value_to_set, colour_to_sample)
updated.emit()

View File

@@ -0,0 +1 @@
uid://dwtrmo82e12ud

5
Scripts/Parts/DeathPit.gd Executable file
View File

@@ -0,0 +1,5 @@
extends Area2D
func area_entered(area: Area2D) -> void:
if area.owner is Player and area.owner.state_machine.state.name != "Dead":
area.owner.die(true)

1
Scripts/Parts/DeathPit.gd.uid Executable file
View File

@@ -0,0 +1 @@
uid://c50qnhtmrqlot

View File

@@ -0,0 +1,43 @@
extends Node
func damage_style_changed(new_value := 0) -> void:
Settings.file.difficulty.damage_style = new_value
func checkpoint_changed(new_value := 0) -> void:
Settings.file.difficulty.checkpoint_style = new_value
func inf_lives_changed(new_value := 0) -> void:
Settings.file.difficulty.inf_lives = new_value
func flag_lives_changed(new_value := 0) -> void:
Settings.file.difficulty.flagpole_lives = new_value
func time_limit_changed(new_value := 0) -> void:
Settings.file.difficulty.time_limit = new_value
func game_over_changed(new_value := 0) -> void:
Settings.file.difficulty.game_over_behaviour = new_value
func backscroll_changed(new_value := 0) -> void:
Settings.file.difficulty.back_scroll = new_value
func level_design_changed(new_value := 0) -> void:
Settings.file.difficulty.level_design = new_value
func extra_checkpoints_changed(new_value := 0) -> void:
Settings.file.difficulty.extra_checkpoints = new_value
func lakitu_style_changed(new_value := 0) -> void:
Settings.file.difficulty.lakitu_style = new_value
func set_value(value_name := "", value := 0) -> void:
{
"damage_style": damage_style_changed,
"checkpoint_style": checkpoint_changed,
"inf_lives": inf_lives_changed,
"flagpole_lives": flag_lives_changed,
"game_over": game_over_changed,
"level_design": level_design_changed,
"extra_checkpoints": extra_checkpoints_changed,
"back_scroll": backscroll_changed
}[value_name].call(value)

View File

@@ -0,0 +1 @@
uid://cxkumlifwb0s6

View File

@@ -0,0 +1,32 @@
extends Node
var can_skip := false
@export var default_font: Font = null
func _enter_tree() -> void:
if Settings.file.game.lang != "jp":
for i in [$Title, $"1", $"2", $Enjoy]:
i.remove_theme_font_override("font")
i.uppercase = true
func _ready() -> void:
Global.debugged_in = false
Global.get_node("GameHUD").hide()
await get_tree().create_timer(1, false).timeout
can_skip = true
func _exit_tree() -> void:
Global.get_node("GameHUD").show()
func _process(_delta: float) -> void:
if Input.is_action_just_pressed("jump_0") and can_skip:
go_to_menu()
func go_to_menu() -> void:
if Global.rom_path == "":
Global.transition_to_scene("res://Scenes/Levels/RomVerifier.tscn")
elif not Global.rom_assets_exist:
Global.transition_to_scene("res://Scenes/Levels/RomResourceGenerator.tscn")
else:
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")

View File

@@ -0,0 +1 @@
uid://bjsdmqxwrb6sd

46
Scripts/Parts/DiscoBg.gd Normal file
View File

@@ -0,0 +1,46 @@
extends Parallax2D
var level_bpm := 120
@export var level := 0.5
var is_star := false
var beats := -1
var tween: Tween = null
func _ready() -> void:
modulate.a = 0
AudioManager.music_beat.connect(on_timeout)
on_timeout()
func _physics_process(delta: float) -> void:
scroll_offset.y = lerpf(scroll_offset.y, lerpf(128, 0, level), delta * 5)
modulate.a = lerpf(modulate.a, lerpf(0, 1, level), delta * 5)
$TextureRect.position.y = move_toward($TextureRect.position.y, -64, delta * (level_bpm / 10))
func on_timeout(idx := 0) -> void:
beats = idx
if is_star == false:
tween_back()
if beats % 4 == 0:
$AudioStreamPlayer.pitch_scale = 2
else:
$AudioStreamPlayer.pitch_scale = 1
$AudioStreamPlayer.play()
func tween_back() -> void:
if tween != null:
tween.kill()
tween = create_tween().set_trans(Tween.TRANS_CUBIC)
tween.tween_property($TextureRect, "position:y", -160, 0.05)
func on_starttimeout() -> void:
if is_star:
tween_back()

1
Scripts/Parts/DiscoBg.gd.uid Executable file
View File

@@ -0,0 +1 @@
uid://d1noy0etws3gd

135
Scripts/Parts/DiscoLevel.gd Normal file
View File

@@ -0,0 +1,135 @@
class_name DiscoLevel
extends Node
@export var combo_meter_rate := 1.0
@export var max_combo := 30.0
@export var disco_lighting: Parallax2D = null
static var combo_amount := 0:
set(value):
if value > combo_amount:
combo_meter = 100
combo_amount = value
static var score_mult := 0
static var combo_meter := 0.0
static var in_disco_level := false
static var combo_breaks := 0
static var giving_score := false
static var max_combo_amount := 0.0
static var first_load := true
static var can_meter_tick := true
const RANK_AMOUNTS := {0: "F", 0.25: "D", 0.45: "C", 0.6: "B", 0.8: "A", 1: "S"}
const RANKS := "FDCBASP"
static var current_rank := ""
static var active := false
const S_RANK_SCORES := [
[45000, 40000, 25000, 12000],
[52500, 25000, 25000, 12000],
[45000, 45000, 25000, 12000],
[45000, 45000, 25000, 12000],
[45000, 40000, 30000, 12000],
[30000, 45000, 20000, 12000],
[45000, 25000, 30000, 12000],
[45000, 45000, 45000, 12000]
]
static var level_ranks := "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
const RANK_IDs := ["F", "D", "C", "B", "A", "S", "P"]
func _ready() -> void:
active = true
Global.current_campaign = "SMBANN"
if get_parent().get_node_or_null("EndFlagpole") != null:
get_parent().get_node("EndFlagpole").player_reached.connect(level_finished)
if get_parent().get_node_or_null("CastleBridge") != null:
get_parent().get_node("CastleBridge").victory_begin.connect(level_finished)
can_meter_tick = true
if DiscoLevel.first_load == true:
max_combo_amount = max_combo
reset_values()
DiscoLevel.first_load = false
in_disco_level = true
static func reset_values() -> void:
combo_amount = 0
combo_meter = 0
first_load = false
if Global.current_campaign == "SMBANN":
Global.score = 0
combo_breaks = 0
current_rank = "F"
Player.times_hit = 0
Global.player_power_states = "0000"
func _physics_process(delta: float) -> void:
if not active:
return
if can_meter_tick:
combo_meter = clamp(combo_meter - 24 * combo_meter_rate * delta, 0, 100)
if combo_meter <= 0 and combo_amount > 0 and not giving_score:
if combo_amount > 2 or current_rank == "P" or current_rank == "S":
AudioManager.play_global_sfx("combo_lost")
give_points(combo_amount)
combo_amount = 0
combo_breaks += 1
var old_rank = current_rank
current_rank = "F"
for i in RANK_AMOUNTS.keys():
if (Global.score + (combo_amount * 500) + (Global.time * 50)) >= (S_RANK_SCORES[Global.world_num - 1][Global.level_num - 1] * i):
current_rank = RANK_AMOUNTS[i]
if current_rank == "S" and combo_breaks <= 0 and combo_amount >= 1:
current_rank = "P"
if RANKS.find(current_rank) > RANKS.find(old_rank):
if current_rank == "S" or current_rank == "P":
AudioManager.play_global_sfx("rank_up_2")
elif combo_amount > 0:
AudioManager.play_global_sfx("rank_up_1")
elif RANKS.find(current_rank) < RANKS.find(old_rank):
AudioManager.play_global_sfx("rank_down")
func give_points(amount := 0) -> void:
await get_tree().create_timer(0.5, false).timeout
for i in amount * 4:
Global.score += 125
AudioManager.play_global_sfx("score")
await get_tree().physics_frame
AudioManager.play_global_sfx("score_end")
AudioManager.kill_sfx("score")
func _exit_tree() -> void:
Global.tallying_score = false
AudioManager.kill_sfx("score")
func level_finished() -> void:
if Global.world_num != 8 && Global.level_num != 4:
SaveManager.visited_levels[SaveManager.get_level_idx(Global.world_num, Global.level_num) + 1] = "1"
Global.score += (combo_amount * 500)
combo_meter = 100
_physics_process(0)
can_meter_tick = false
await Global.score_tally_finished
active = false
write_final_rank()
func write_final_rank() -> void:
if ChallengeModeHandler.top_challenge_scores[Global.world_num - 1][Global.level_num - 1] < Global.score:
ChallengeModeHandler.top_challenge_scores[Global.world_num - 1][Global.level_num - 1] = Global.score
if RANK_IDs.find(level_ranks[SaveManager.get_level_idx(Global.world_num, Global.level_num)]) < RANK_IDs.find(current_rank):
level_ranks[SaveManager.get_level_idx(Global.world_num, Global.level_num)] = current_rank
check_for_p_rank_achievement()
SaveManager.write_save("SMBANN")
func check_for_p_rank_achievement() -> void:
if level_ranks == "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP":
Global.unlock_achievement(Global.AchievementID.ANN_PRANK)

View File

@@ -0,0 +1 @@
uid://bjs5mlc3xrxud

View File

@@ -0,0 +1,25 @@
extends Node2D
var shadow_colour := Color.BLACK
@export var offset := Vector2(0, 0)
var shadow_texture: Texture2D = null
func _process(_delta: float) -> void:
hide()
if Settings.file.video.drop_shadows == 0:
return
show()
if is_instance_valid(get_tree().current_scene):
if is_instance_valid(get_tree().current_scene.get_viewport().get_camera_2d()):
$SubViewportContainer/SubViewport.world_2d = get_tree().current_scene.get_viewport().get_camera_2d().get_world_2d()
else:
return
else:
return#
$SubViewportContainer.material.set_shader_parameter("shadow_colour", shadow_colour)
$SubViewportContainer/SubViewport.size = get_viewport().get_visible_rect().size + Vector2(9, 2)
global_position = get_viewport().get_camera_2d().get_screen_center_position() + offset
$SubViewportContainer/SubViewport/Camera2D.global_position = get_viewport().get_camera_2d().get_screen_center_position()
queue_redraw()

View File

@@ -0,0 +1 @@
uid://c3krxjp4ye4ko

View File

@@ -0,0 +1,26 @@
extends Node
@onready var sub_viewport: SubViewport = %SubViewport
@onready var camera: Camera2D = %Camera
@onready var point: Node2D = %Point
var enabled := true
const day_colour := Color("000000")
const night_colour := Color("5e5e5e")
func _ready() -> void:
await get_tree().physics_frame
sub_viewport.set_world_2d(get_viewport().get_world_2d())
func _physics_process(_delta: float) -> void:
if get_viewport().get_camera_2d() != null:
camera.global_position = get_viewport().get_camera_2d().get_screen_center_position()
camera.zoom = Vector2i(Vector2.ONE / $"%Container".scale)
point.global_position = camera.global_position
var colour := day_colour
$%Container.material.set_shader_parameter("shadow_colour", colour)
func _exit_tree() -> void:
pass
#sub_viewport.set_world_2d(null)

View File

@@ -0,0 +1 @@
uid://cit6racuc8c60

View File

@@ -0,0 +1,15 @@
class_name LevelEditorToggleDetection
extends Node
signal toggled
signal level_start
signal editor_start
func _ready() -> void:
await get_tree().physics_frame
if is_instance_valid(Global.level_editor):
Global.level_editor.level_start.connect(toggled.emit)
Global.level_editor.level_start.connect(level_start.emit)
Global.level_editor.editor_start.connect(editor_start.emit)
Global.level_editor.editor_start.connect(toggled.emit)

View File

@@ -0,0 +1 @@
uid://b8vmbtoaaq5nc

View File

@@ -0,0 +1,11 @@
class_name LevelEditorVisibleNode
extends Node2D
func _ready() -> void:
update()
if Global.level_editor != null:
Global.level_editor.editor_start.connect(update)
Global.level_editor.level_start.connect(update)
func update() -> void:
visible = !LevelEditor.playing_level and Global.current_game_mode == Global.GameMode.LEVEL_EDITOR

View File

@@ -0,0 +1 @@
uid://cpwloakvp672a

113
Scripts/Parts/EndCastle.gd Normal file
View File

@@ -0,0 +1,113 @@
extends Node2D
var time_save := 0
signal finished_sequence
const FIREWORK = preload("res://Scenes/Prefabs/Particles/Firework.tscn")
var tally_finished := false
var music_finished := false
var tree = null
var show_walls := false
var doing_sequence := false
var can_transition := false
static var is_transitioning := false
func _ready() -> void:
await Global.level_complete_begin
$Overlay.show()
$OverlaySprite.show()
$Overlay/PlayerDetection.set_collision_layer_value(1, true)
Global.score_tally_finished.connect(on_tally_finished)
if Global.current_game_mode == Global.GameMode.BOO_RACE:
get_tree().create_timer(3.5, false).timeout.connect(on_music_finished)
else:
get_tree().create_timer(5.5, false).timeout.connect(on_music_finished)
time_save = Global.time
func update_cam_limit() -> void:
$CameraRightLimit._enter_tree()
func _process(_delta: float) -> void:
$Overlay.modulate.a = int($SmallCastleVisual.use_sprite == false)
if get_node_or_null("Wall") != null:
%Wall.visible = show_walls
func on_music_finished() -> void:
do_sequence()
func on_tally_finished() -> void:
$FlagJoint/Flag/AnimationPlayer.play("Raise")
func do_sequence() -> void:
if Global.current_game_mode != Global.GameMode.BOO_RACE:
await get_tree().create_timer(1, false).timeout
if Global.current_campaign == "SMBLL":
await do_lost_levels_firework_check()
else:
await do_firework_check()
await get_tree().create_timer(1, false).timeout
if is_transitioning == false:
is_transitioning = true
exit_level()
func do_firework_check() -> void:
var digit = time_save % 10
if [1, 3, 6].has(digit):
await show_fireworks(digit)
return
func do_lost_levels_firework_check() -> void:
var coin_digit = Global.coins % 10
var time_digit = time_save % 10
if coin_digit == time_digit:
if coin_digit % 2 == 0:
await show_fireworks(6)
if coin_digit % 11 == 0:
spawn_one_up_note()
AudioManager.play_sfx("1_up", global_position)
Global.lives += 1
else:
await show_fireworks(3)
const ONE_UP_NOTE = preload("uid://dopxwjj37gu0l")
func spawn_one_up_note() -> void:
var note = ONE_UP_NOTE.instantiate()
note.global_position = global_position + Vector2(0, -16)
owner.add_sibling(note)
func _exit_tree() -> void:
is_transitioning = false
func show_fireworks(amount := 0) -> void:
for i in amount:
spawn_firework()
await get_tree().create_timer(0.5, false).timeout
func spawn_firework() -> void:
var node = FIREWORK.instantiate()
Global.score += 500
node.position.x = randf_range(-48, 48)
node.position.y = randf_range(-112, -150)
add_child(node)
AudioManager.play_sfx("firework", node.global_position)
func exit_level() -> void:
await Global.frame_rule
match Global.current_game_mode:
Global.GameMode.MARATHON_PRACTICE:
Global.reset_values()
Global.open_marathon_results()
Global.GameMode.CUSTOM_LEVEL:
Global.transition_to_scene("res://Scenes/Levels/CustomLevelMenu.tscn")
Global.GameMode.LEVEL_EDITOR:
Global.level_editor.stop_testing()
_:
if Global.current_campaign == "SMBANN":
Global.open_disco_results()
else:
Global.current_level.transition_to_next_level()

1
Scripts/Parts/EndCastle.gd.uid Executable file
View File

@@ -0,0 +1 @@
uid://qq26qw7ltflb

View File

@@ -0,0 +1,58 @@
extends Node2D
const FLAG_POINTS := [100, 400, 800, 2000, 5000]
signal player_reached
signal sequence_begin
func _ready() -> void:
if Settings.file.difficulty.flagpole_lives == 0:
print(Settings.file.difficulty)
$Top.queue_free()
func on_area_entered(area: Area2D) -> void:
if area.owner is Player:
player_touch(area.owner)
func player_touch(player: Player) -> void:
player_reached.emit()
if Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
SpeedrunHandler.is_warp_run = false
SpeedrunHandler.run_finished()
Global.can_pause = false
if get_node_or_null("Top") != null:
$Top.queue_free()
$Hitbox.queue_free()
get_tree().call_group("Enemies", "flag_die")
give_points(player)
Global.can_time_tick = false
player.z_index = -2
player.global_position.x = $Flag.global_position.x + 3
$Animation.play("FlagDown")
player.state_machine.transition_to("FlagPole")
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.FLAG_POLE, 99, false)
await get_tree().create_timer(1.5, false).timeout
sequence_begin.emit()
if Global.current_game_mode == Global.GameMode.BOO_RACE:
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.RACE_WIN, 99, false)
else:
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.LEVEL_COMPLETE, 99, false)
Global.level_complete_begin.emit()
await get_tree().create_timer(1, false).timeout
if [Global.GameMode.BOO_RACE].has(Global.current_game_mode) == false:
Global.tally_time()
func give_points(player: Player) -> void:
var value = clamp(int(lerp(0, 4, (player.global_position.y / -144))), 0, 4)
var nearest_value = FLAG_POINTS[value]
$Score.text = str(nearest_value)
Global.score += nearest_value
$Score/Animation2.play("ScoreRise")
func on_player_entered(player: Player) -> void:
player_touch(player)
Global.lives += 1
AudioManager.play_sfx("1_up", global_position)

View File

@@ -0,0 +1 @@
uid://dwfnvpioh2kvi

View File

@@ -0,0 +1,41 @@
extends Node
var style := "Overworld"
var level_seed := "8923589235890"
var level_length := 10
const PIECE_FOLDER := "res://Scenes/LevelPieces/"
const OVERWORLD_STYLES := ["Overworld", "Desert", "Snow", "Jungle", "Garden", "Beach", "Mountain", "Autumn"]
@onready var pieces: Node2D = $"../Pieces"
func _enter_tree() -> void:
owner.theme = OVERWORLD_STYLES.pick_random()
Global.level_theme = owner.theme
print(owner.theme)
func _ready() -> void:
seed(int(level_seed))
await owner.ready
build_level()
func build_level() -> void:
var piece_spawn_point := -96
var last_piece = self
for i in level_length:
var piece = get_next_piece()
piece.position.x = piece_spawn_point
piece_spawn_point += piece.length
$"../Pieces".add_child(piece)
last_piece = piece
func get_next_piece() -> LevelPiece:
var piece_num := 0
var amount_of_pieces := DirAccess.get_files_at(PIECE_FOLDER + style + "/").size()
piece_num = randi_range(1, amount_of_pieces)
var path = PIECE_FOLDER + style + "/" + str(piece_num) + ".tscn"
var next_piece = load(path).instantiate()
return next_piece

View File

@@ -0,0 +1 @@
uid://dl2nxlgmoa5t2

View File

@@ -0,0 +1,44 @@
class_name EntityGenerator
extends Node2D
var spawn_meter := 0.0
@export var threshold := 2.0
var active := false
@export_enum("Target Player", "Random Height") var y_pos := 0
@export_enum("Right", "Bottom") var direction := 0
@export var entity_scene: PackedScene = null
func _physics_process(delta: float) -> void:
if active:
spawn_meter += delta
if spawn_meter >= threshold:
spawn_entity()
spawn_meter = randf_range(-2, 0)
func activate() -> void:
if not active:
active = true
spawn_meter = 0
spawn_entity()
func deactivate_all_generators() -> void:
for i in get_tree().get_nodes_in_group("EntityGenerators"):
i.active = false
i.deactivate()
func deactivate() -> void:
pass
func spawn_entity() -> void:
if entity_scene == null: return
var node = entity_scene.instantiate()
if direction == 1:
node.global_position.x = get_viewport().get_camera_2d().get_screen_center_position().x + [ -32 ,-64, -96, -128].pick_random()
node.global_position.y = 48
else:
if y_pos == 0:
node.global_position.y = get_tree().get_first_node_in_group("Players").global_position.y + randi_range(-4, 4)
else:
node.global_position.y = randf_range(-56, -120)
node.global_position.x = get_viewport().get_camera_2d().get_screen_center_position().x + ((get_viewport().get_visible_rect().size.x / 2) + 4)
add_sibling(node)

View File

@@ -0,0 +1 @@
uid://bq0pc2vhp35t2

View File

@@ -0,0 +1,42 @@
extends Node2D
@export_enum("Spin", "Drop", "Poof") var gib_type := 0
var visuals: Node = null
var velocity := Vector2(0, 0)
var direction := 1
var entity_rotation := 0.0
func _ready() -> void:
if visuals == null:
queue_free()
return
visuals.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_OFF
add_child(visuals)
visuals.process_mode = Node.PROCESS_MODE_DISABLED
visuals.position = Vector2.ZERO
match gib_type:
0:
velocity = Vector2(100 * direction, -200)
func _physics_process(delta: float) -> void:
match gib_type:
0:
spin_move(delta)
1:
velocity.y += (15 / delta) * delta
velocity.y = clamp(velocity.y, -INF, Global.entity_max_fall_speed)
scale.y = -1
global_position += velocity * delta
func spin_move(delta: float) -> void:
velocity.y += (15 / delta) * delta
velocity.y = clamp(velocity.y, -INF, Global.entity_max_fall_speed)
entity_rotation = (180 * direction)
visuals.global_rotation_degrees = snapped(entity_rotation, 45)
velocity.x = lerpf(velocity.x, 0, delta / 2)

1
Scripts/Parts/EntityGib.gd.uid Executable file
View File

@@ -0,0 +1 @@
uid://sfu6fevr0yop

View File

@@ -0,0 +1,85 @@
@tool
class_name EntityIDMapper
extends Node
@export_tool_button("Update ID's") var button = update_map
@export var auto_update := true
static var map := {}
const MAP_PATH := "res://EntityIDMap.json"
const base64_charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
var selectors_to_add := []
func _ready() -> void:
map = JSON.parse_string(FileAccess.open(MAP_PATH, FileAccess.READ).get_as_text())
if Engine.is_editor_hint() == false and OS.is_debug_build() and auto_update:
update_map()
func update_map() -> void:
map = JSON.parse_string(FileAccess.open(MAP_PATH, FileAccess.READ).get_as_text())
get_ids()
save_to_json()
print("done")
func clear_map() -> void:
map = {}
save_to_json()
func get_ids() -> void:
var id := 0
for i: EditorTileSelector in get_tree().get_nodes_in_group("Selectors"):
if i.type != 1 or i.entity_scene == null:
continue
var selector_id := encode_to_base64_2char(id)
var value = get_selector_info_arr(i)
id += 1
if map.has(selector_id):
if map.values().find(value) != -1:
continue
else:
selector_id = encode_to_base64_2char(map.size())
map.set(selector_id, get_selector_info_arr(i))
static func get_selector_info_arr(selector: EditorTileSelector) -> Array:
return [selector.entity_scene.resource_path, str(selector.tile_offset.x) + "," + str(selector.tile_offset.y)]
func save_to_json() -> void:
var file = FileAccess.open(MAP_PATH, FileAccess.WRITE)
file.store_string(JSON.stringify(map, "\t", false))
file.close()
static func get_map_id(entity_scene := "") -> String:
var idx := 0
for i in map.values():
if i[0] == entity_scene:
return map.keys()[idx]
idx += 1
return ""
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 idx1 = base64_charset.find(encoded[0])
var idx2 = base64_charset.find(encoded[1])
if idx1 == -1 or idx2 == -1:
push_error("Invalid character in base64 string.")
return -1
return (idx1 << 6) | idx2

View File

@@ -0,0 +1 @@
uid://c08y0c8163fec

View File

@@ -0,0 +1,4 @@
class_name EntityTileContainer
extends Node2D
var entity: Node = null

View File

@@ -0,0 +1 @@
uid://bg8681ssontei

View File

@@ -0,0 +1,8 @@
class_name ExtraBGM
extends Node
@export var extra_track: JSON = null
func _ready() -> void:
if Settings.file.audio.extra_bgm == 1:
owner.music = extra_track

View File

@@ -0,0 +1 @@
uid://cusgxgerctsft

View File

@@ -0,0 +1,22 @@
class_name FontUpdater
extends Node
@onready var resource_getter_smb1 := ResourceGetter.new()
@onready var resource_getter_smbll := ResourceGetter.new()
@onready var resource_getter_score := ResourceGetter.new()
@onready var FONT_LL_MAIN = load("uid://djxdgxy1iv8yv")
@onready var FONT_MAIN = load("uid://bl7sbw4nx3l1t")
@onready var SCORE_FONT = load("uid://cflgloiossd8a")
static var current_font: Font = null
func _ready() -> void:
update_fonts()
Global.level_theme_changed.connect(update_fonts)
func update_fonts() -> void:
FONT_MAIN.base_font = resource_getter_smb1.get_resource(FONT_MAIN.base_font)
FONT_LL_MAIN.base_font = resource_getter_smbll.get_resource(FONT_LL_MAIN.base_font)
SCORE_FONT.base_font = resource_getter_score.get_resource(SCORE_FONT.base_font)

View File

@@ -0,0 +1 @@
uid://ctsjagoa5t33f

66
Scripts/Parts/GameOver.gd Normal file
View File

@@ -0,0 +1,66 @@
extends Node
@export var reset_level := false
@export var has_menu := false
var can_continue := false
func _enter_tree() -> void:
Global.level_theme = "Underground"
Global.level_theme_changed.emit()
AudioManager.stop_all_music()
func _ready() -> void:
get_tree().paused = false
Global.lives = clamp(Global.lives, 0, 99)
SpeedrunHandler.timer_active = false
await get_tree().create_timer(0.1).timeout
can_continue = true
func _process(_delta: float) -> void:
print(can_continue)
if Input.is_action_just_pressed("jump_0") and can_continue:
go_back_to_title()
can_continue = false
func go_back_to_title() -> void:
if has_menu:
$Timer.queue_free()
has_menu = false
$CanvasLayer/VBoxContainer.show()
$CanvasLayer/VBoxContainer/SelectableLabel.grab_focus()
elif not reset_level:
quit_to_menu()
else:
continue_on()
func continue_on() -> void:
reset_values()
LevelTransition.level_to_transition_to = Level.get_scene_string(Global.world_num, Global.level_num)
Global.transition_to_scene("res://Scenes/Levels/LevelTransition.tscn")
func quit_to_menu() -> void:
reset_values()
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
func reset_values() -> void:
if Global.world_num <= 8:
ChallengeModeHandler.current_run_red_coins_collected = ChallengeModeHandler.red_coins_collected[Global.world_num - 1][Global.level_num - 1]
Global.lives = 3
Global.score = 0
Global.player_power_states = "0000"
Global.coins = 0
if Global.current_game_mode == Global.GameMode.CHALLENGE:
return
match Settings.file.difficulty.game_over_behaviour:
0:
Global.level_num = 1
1:
pass
2:
Global.level_num = 1
Global.world_num = 1
Global.reset_values()
SaveManager.write_save()

1
Scripts/Parts/GameOver.gd.uid Executable file
View File

@@ -0,0 +1 @@
uid://b1dc01fk1eomt

View File

@@ -0,0 +1,12 @@
class_name SecondQuestNode
extends Node
@export var enabled := true
@export var nodes_to_delete: Array[Node] = []
func _ready() -> void:
if Global.second_quest or enabled:
for i in nodes_to_delete:
i.queue_free()
else:
queue_free()

View File

@@ -0,0 +1 @@
uid://co8w6sjp0t2dm

View File

@@ -0,0 +1,29 @@
extends EntityGenerator
const new_vector = Vector2.UP
func activate() -> void:
for i in get_tree().get_nodes_in_group("Players"):
on_player_entered(i)
func deactivate() -> void:
for i in get_tree().get_nodes_in_group("Players"):
on_player_exited(i)
func on_player_entered(player: Player) -> void:
if player.gravity_vector == new_vector:
return
player.gravity_vector = new_vector
player.global_position.y -= 16
player.global_rotation = -player.gravity_vector.angle() + deg_to_rad(90)
player.reset_physics_interpolation()
func on_player_exited(player: Player) -> void:
if player.gravity_vector == Vector2.DOWN:
return
player.gravity_vector = Vector2.DOWN
player.global_position.y += 16
player.velocity.y *= 1.1
player.global_rotation = -player.gravity_vector.angle() + deg_to_rad(90)
player.reset_physics_interpolation()

View File

@@ -0,0 +1 @@
uid://dgqdo5cvl41l0

View File

@@ -0,0 +1,33 @@
class_name LabelFontChanger
extends Node
@export var labels: Array[Label]
const SMB1 = preload("uid://bl7sbw4nx3l1t")
const SMBLL = preload("uid://djxdgxy1iv8yv")
const SCORE_FONT = preload("uid://bk0no5p6sifgu")
@export var use_score_font := false
static var current_font: Font = null
func _ready() -> void:
refresh_font()
Global.level_theme_changed.connect(refresh_font)
func refresh_font() -> void:
if Global.current_campaign == "SMBLL":
current_font = SMBLL
else:
current_font = SMB1
update_labels()
func update_labels() -> void:
var font_to_use = current_font
if use_score_font:
font_to_use = SCORE_FONT
for i in labels:
if i == null:
continue
i.remove_theme_font_override("font")
i.add_theme_font_override("font", font_to_use)

View File

@@ -0,0 +1 @@
uid://co6tjg3w6qpd8

View File

@@ -0,0 +1,7 @@
extends Node
@export var labels: Dictionary[Label, Label] = {}
func _process(_delta: float) -> void:
for i in labels.keys():
labels[i].text = i.text

View File

@@ -0,0 +1 @@
uid://ctbxx8n6icrui

View File

@@ -0,0 +1,18 @@
extends Area2D
static var meter := 0.0
const LEAPING_CHEEP_CHEEP = preload("res://Scenes/Prefabs/Entities/Enemies/LeapingCheepCheep.tscn")
func _physics_process(delta: float) -> void:
if get_overlapping_areas().any(func(area: Area2D): return area.owner is Player) != false:
meter += 1 * delta
if meter >= 1:
meter = 0
spawn_cheep_cheep()
func spawn_cheep_cheep() -> void:
var node = LEAPING_CHEEP_CHEEP.instantiate()
node.global_position.x = get_viewport().get_camera_2d().get_screen_center_position().x + [ -32 ,-64, -96, -128].pick_random()
node.global_position.y = 48
add_sibling(node)

View File

@@ -0,0 +1 @@
uid://dxt4s20sjndph

34
Scripts/Parts/LevelBG.gd Executable file
View File

@@ -0,0 +1,34 @@
extends Node2D
func _enter_tree() -> void:
setup_bg_scrolling()
var repeat_times := 1:
set(value):
if repeat_times != value:
repeat_times = value
update_repeats()
@export var scroll_scale := 0.5
func _process(_delta: float) -> void:
repeat_times = ceil(get_viewport_rect().size.x / 512) + 1
func update_repeats() -> void:
for i in get_children():
if i is Parallax2D:
i.repeat_times = repeat_times
func setup_bg_scrolling() -> void:
var scr_scale = scroll_scale
match Global.parallax_style:
0:
scr_scale = 1
1:
scr_scale = scroll_scale
2:
return
for i in get_children():
if i is Parallax2D:
if i.scroll_scale.x < 1:
i.scroll_scale.x = scr_scale

1
Scripts/Parts/LevelBG.gd.uid Executable file
View File

@@ -0,0 +1 @@
uid://dyifmk51nnr8l

View File

@@ -0,0 +1,11 @@
@tool
class_name LevelGuide
extends Sprite2D
func _ready() -> void:
if Engine.is_editor_hint() == false:
queue_free()
else:
position = Vector2(-256, -208)
centered = false
modulate.a = 0.5

View File

@@ -0,0 +1 @@
uid://b64va0yqgoixo

View File

@@ -0,0 +1,174 @@
extends Node
var entity_map := {}
@onready var editor: LevelEditor = owner
const base64_charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
@onready var level: Level = $"../Level"
@onready var level_bg: LevelBG = $"../Level/LevelBG"
var sub_level_file = null
func _ready() -> void:
load_entity_map()
func load_level(level_idx := 0) -> void:
clear_level()
sub_level_file = editor.level_file["Levels"][level_idx]
build_level()
func clear_level() -> void:
for layer in 5:
for i in editor.entity_layer_nodes[layer].get_children():
if i is Player:
reset_player(i)
continue
i.queue_free()
var connect_tiles = editor.tile_layer_nodes[layer].get_used_cells_by_id(0, Vector2i(13, 8))
editor.tile_layer_nodes[layer].clear()
for i in connect_tiles:
editor.tile_layer_nodes[layer].set_cell(i, 0, Vector2i(13, 8))
editor.entity_tiles = [{}, {}, {}, {}, {}]
func load_entity_map() -> void:
entity_map = JSON.parse_string(FileAccess.open(EntityIDMapper.MAP_PATH, FileAccess.READ).get_as_text())
func build_level() -> void:
if sub_level_file.is_empty():
return
var layer_id := 0
for layer in sub_level_file["Layers"]:
for chunk_id in layer:
var chunk = layer[chunk_id]
add_tiles(LevelSaver.decompress_string(chunk["Tiles"]), int(chunk_id), int(layer_id))
add_entities(LevelSaver.decompress_string(chunk["Entities"]), int(chunk_id), int(layer_id))
layer_id += 1
apply_level_data(sub_level_file["Data"])
apply_bg_data(sub_level_file["BG"])
func add_tiles(chunk := "", chunk_id := 0, layer := 0) -> void:
for tile in chunk.split("=", false):
var tile_position := Vector2i.ZERO
var tile_atlas_position := Vector2i.ZERO
var source_id := 0
tile_position = decode_tile_position_from_chars(tile[0], tile[1], chunk_id)
source_id = base64_charset.find(tile[4])
tile_atlas_position = Vector2i(base64_charset.find(tile[2]), base64_charset.find(tile[3]))
editor.tile_layer_nodes[layer].set_cell(tile_position, source_id, tile_atlas_position)
func add_entities(chunk := "", chunk_id := 0, layer := 0) -> void:
for entity in chunk.split("=", false):
var entity_id = entity.get_slice(",", 1)
var entity_chunk_position = entity.get_slice(",", 0)
var entity_tile_position = decode_tile_position_from_chars(entity_chunk_position[0], entity_chunk_position[1], chunk_id)
var entity_node: Node = null
if entity_map.has(entity_id) == false:
Global.log_error("MISSING ENTITY ID!!!! JOE FORGOT TO UPDATE THE MAP AGAIN :(")
continue
if entity_map[entity_id][0] != "res://Scenes/Prefabs/Entities/Player.tscn":
entity_node = load(entity_map[entity_id][0]).instantiate()
else:
entity_node = get_tree().get_first_node_in_group("Players")
var offset = entity_map[entity_id][1].split(",")
entity_node.global_position = entity_tile_position * 16 + (Vector2i(8, 8) + Vector2i(int(offset[0]), int(offset[1])))
editor.entity_layer_nodes[layer].add_child(entity_node)
entity_node.reset_physics_interpolation()
entity_node.owner = editor
editor.entity_tiles[layer][entity_tile_position] = entity_node
entity_node.set_meta("tile_position", entity_tile_position)
entity_node.set_meta("tile_offset", Vector2(int(offset[0]), int(offset[1])))
if entity_node.has_node("EditorPropertyExposer"):
entity_node.get_node("EditorPropertyExposer").apply_string(entity)
func reset_player(player: Player) -> void: ## Function literally here to just reset the player back to default starting, if loading into a level file, that hasnt been written yet (pipes)
player.show()
player.state_machine.transition_to("Normal")
player.global_position = Vector2(-232, 0)
func gzip_encode(text: String) -> String:
var bytes = Marshalls.base64_to_raw(text)
bytes.compress(FileAccess.COMPRESSION_GZIP)
return Marshalls.raw_to_base64(bytes)
func gzip_decode(text: String) -> String:
var bytes = Marshalls.base64_to_raw(text)
bytes.decompress_dynamic(-1, FileAccess.COMPRESSION_GZIP)
return Marshalls.raw_to_base64(bytes)
func apply_level_data(data := "") -> void:
var split = data.split("=")
var values := []
for i in split:
if i.length() == 2:
values.append(decode_from_base64_2char(i))
elif i.length() == 1:
values.append(base64_charset.find(i))
else:
values.append(i)
level.theme = Level.THEME_IDXS[values[0]]
Global.level_theme = level.theme
level.theme_time = ["Day", "Night"][values[1]]
Global.theme_time = level.theme_time
editor.bgm_id = values[2]
level.campaign = ["SMB1", "SMBLL", "SMBS", "SMBANN"][values[3]]
Global.current_campaign = level.campaign
level.can_backscroll = bool(values[4])
level.vertical_height = -int(values[5])
level.time_limit = int(values[6])
%ThemeTime.selected = values[1]
%LevelMusic.selected = values[2]
%Campaign.selected = values[3]
%BackScroll.set_pressed_no_signal(bool(values[4]))
%HeightLimit.value = values[5]
%TimeLimit.value = values[6]
%SubLevelID.selected = editor.sub_level_id
ResourceSetterNew.cache.clear()
Global.level_theme_changed.emit()
func apply_bg_data(data := "") -> void:
var split = data.split("=", false)
var id := 0
const BG_VALUES := ["primary_layer", "second_layer", "second_layer_offset", "time_of_day", "particles", "liquid_layer", "overlay_clouds"]
var SELECTORS = [%PrimaryLayer, %SecondLayer, %SecondLayerOffset, %TimeOfDay, %Particles, %LiquidLayer, %OverlayClouds]
for i in split:
var value := 0
if i.length() > 1:
value = (decode_from_base64_2char(i))
else:
value = (base64_charset.find(i))
if SELECTORS[id] is SpinBox:
SELECTORS[id].value = value
elif SELECTORS[id] is Button:
SELECTORS[id].set_pressed_no_signal(bool(value))
else:
SELECTORS[id].selected = value
level_bg.set_value(value, BG_VALUES[id])
id += 1
func decode_tile_position_from_chars(char_x: String, char_y: String, chunk_idx: int) -> Vector2i:
var local_x = base64_charset.find(char_x)
var local_y = base64_charset.find(char_y)
return Vector2i(local_x + (chunk_idx * 32), local_y - 30)
func decode_from_base64_2char(encoded: String) -> int:
if encoded.length() != 2:
push_error("Encoded string must be exactly 2 characters.")
return -1
var idx1 = base64_charset.find(encoded[0])
var idx2 = base64_charset.find(encoded[1])
if idx1 == -1 or idx2 == -1:
push_error("Invalid character in base64 string.")
return -1
return (idx1 << 6) | idx2
func tile_to_chunk_idx(tile_position := Vector2i.ZERO) -> int:
return floor(tile_position.x / 32.0)

View File

@@ -0,0 +1 @@
uid://clsnunmd42u6d

149
Scripts/Parts/LevelSaver.gd Normal file
View File

@@ -0,0 +1,149 @@
class_name LevelSaver
extends Node
var sub_level_file := {"Layers": [{}, {}, {}, {}, {}], "Data": "", "BG": ""}
static var level_file := {"Info": {}, "Levels": [{}, {}, {}, {}, {}]}
@onready var editor: LevelEditor = owner
const chunk_template := {"Tiles": "", "Entities": ""}
const base64_charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
@onready var level: Level = $"../Level"
@onready var level_bg: LevelBG = $"../Level/LevelBG"
var entity_map := {}
const tile_blacklist := []
func _ready() -> void:
load_entity_map()
func save_level(level_name := "Unnamed Level", level_author := "You", level_desc := "No Desc", difficulty := 0) -> Dictionary:
level_file = editor.level_file
sub_level_file = {"Layers": [{}, {}, {}, {}, {}], "Data": "", "BG": ""}
get_tiles()
get_entities()
save_bg_data()
save_level_data()
level_file["Levels"][editor.sub_level_id] = sub_level_file.duplicate()
level_file["Info"] = {"Name": level_name, "Author": level_author, "Description": level_desc, "Difficulty": difficulty}
level_file["Version"] = Global.version_number
return level_file
func write_file(json := {}, lvl_file_name := "") -> void:
DirAccess.make_dir_absolute(LevelEditor.CUSTOM_LEVEL_DIR)
for i in "<>:?!/":
lvl_file_name = lvl_file_name.replace(i, "")
var file = FileAccess.open(LevelEditor.CUSTOM_LEVEL_DIR + lvl_file_name, FileAccess.WRITE)
file.store_string(JSON.stringify(json, "", false))
file.close()
print("saved")
func load_entity_map() -> void:
entity_map = JSON.parse_string(FileAccess.open(EntityIDMapper.MAP_PATH, FileAccess.READ).get_as_text())
func get_tiles() -> void:
for layer in 5:
for tile in editor.tile_layer_nodes[layer].get_used_cells():
if tile_blacklist.has(editor.tile_layer_nodes[layer].get_cell_atlas_coords(tile)) or editor.tile_layer_nodes[layer].get_cell_source_id(tile) == 6:
continue
var tile_string := ""
var chunk_tile = Vector2i(wrap(tile.x, 0, 32), wrap(tile.y + 30, -1, 32))
tile_string += base64_charset[chunk_tile.x]
tile_string += base64_charset[chunk_tile.y]
tile_string += ""
tile_string += base64_charset[editor.tile_layer_nodes[layer].get_cell_atlas_coords(tile).x]
tile_string += base64_charset[editor.tile_layer_nodes[layer].get_cell_atlas_coords(tile).y]
tile_string += base64_charset[editor.tile_layer_nodes[layer].get_cell_source_id(tile)]
var tile_chunk_idx = tile_to_chunk_idx(tile)
var tile_chunk := {}
if sub_level_file["Layers"][layer].has(tile_chunk_idx):
tile_chunk = sub_level_file["Layers"][layer][tile_chunk_idx]
else:
tile_chunk = chunk_template.duplicate(true)
tile_chunk["Tiles"] += tile_string + "="
sub_level_file["Layers"][layer][tile_chunk_idx] = tile_chunk
for i in sub_level_file["Layers"][layer]:
sub_level_file["Layers"][layer][i]["Tiles"] = compress_string(sub_level_file["Layers"][layer][i]["Tiles"])
static func compress_string(buffer := "") -> String:
var bytes = buffer.to_ascii_buffer()
var compressed_bytes = bytes.compress(FileAccess.CompressionMode.COMPRESSION_DEFLATE)
var b64_buffer = Marshalls.raw_to_base64(compressed_bytes)
# workaround since for some reason .replace() decided not to work today
b64_buffer = b64_buffer.replace("=", "%")
return b64_buffer
static func decompress_string(buffer := "") -> String:
if buffer.is_empty():
return buffer
buffer = buffer.replace("%", "=")
var compressed = Marshalls.base64_to_raw(buffer)
var decompressed = compressed.decompress_dynamic(-1, FileAccess.CompressionMode.COMPRESSION_DEFLATE)
var ret = decompressed.get_string_from_ascii()
return ret
func get_entities() -> void:
for layer in 5:
for entity in editor.entity_layer_nodes[layer].get_children():
if entity.has_meta("tile_position") == false:
continue
var entity_string := ""
var chunk_position = Vector2i(wrap(entity.get_meta("tile_position").x, 0, 32), wrap(entity.get_meta("tile_position").y + 30, 0, 32))
entity_string += base64_charset[chunk_position.x]
entity_string += base64_charset[chunk_position.y]
entity_string += ","
entity_string += EntityIDMapper.get_map_id(entity.scene_file_path)
if entity.has_node("EditorPropertyExposer"):
entity_string += entity.get_node("EditorPropertyExposer").get_string()
var entity_chunk_idx = tile_to_chunk_idx(entity.get_meta("tile_position"))
var tile_chunk := {}
if sub_level_file["Layers"][layer].has(entity_chunk_idx):
tile_chunk = sub_level_file["Layers"][layer][entity_chunk_idx]
else:
tile_chunk = chunk_template.duplicate(true)
tile_chunk["Entities"] += entity_string + "="
sub_level_file["Layers"][layer][entity_chunk_idx] = tile_chunk
for i in sub_level_file["Layers"][layer]:
sub_level_file["Layers"][layer][i]["Entities"] = compress_string(sub_level_file["Layers"][layer][i]["Entities"])
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 save_level_data() -> void:
var string := ""
for i in [Level.THEME_IDXS.find(level.theme), ["Day", "Night"].find(level.theme_time), editor.bgm_id, ["SMB1", "SMBLL", "SMBS", "SMBANN"].find(level.campaign), level.can_backscroll, abs(level.vertical_height), level.time_limit]:
var key := ""
if int(i) >= 64:
key = encode_to_base64_2char(int(i))
else:
key = base64_charset[int(i)]
string += key + "="
sub_level_file["Data"] = string
func save_bg_data() -> void:
var string := ""
for i in [level_bg.primary_layer, level_bg.second_layer, level_bg.second_layer_offset.y, level_bg.time_of_day, level_bg.particles, level_bg.liquid_layer, level_bg.overlay_clouds]:
var key := ""
i = int(i)
if abs(i) >= 64:
key = encode_to_base64_2char(abs(i))
else:
key = base64_charset[abs(i)]
string += key + "="
sub_level_file["BG"] = string
func tile_to_chunk_idx(tile_position := Vector2i.ZERO) -> int:
return floor(tile_position.x / 32.0)

View File

@@ -0,0 +1 @@
uid://b77kpu65i243a

View File

@@ -0,0 +1,116 @@
class_name LevelTransition
extends Node
const PIPE_CUTSCENE_LEVELS := {
"SMB1": [[1, 2], [2, 2], [4, 2], [7, 2]],
"SMBLL": [[1, 2], [3, 2], [5, 2], [6, 2], [10, 2], [11, 2]],
"SMBS": [[1, 2], [2, 2], [3, 1], [7, 2], [8, 3]],
"SMBANN": []
}
const PIPE_CUTSCENE_OVERRIDE := {
"SMB1": {[2, 2]: "res://Scenes/Levels/PipeCutsceneWater.tscn", [7, 2]: "res://Scenes/Levels/PipeCutsceneWater.tscn"},
"SMBLL": {[3, 2]: "res://Scenes/Levels/PipeCutsceneWater.tscn", [11, 2]: "res://Scenes/Levels/PipeCutsceneWater.tscn"},
"SMBS": {[3, 1]: "res://Scenes/Levels/SMBS/SPCastlePipeCutscene.tscn", [7, 2]: "res://Scenes/Levels/PipeCutsceneWater.tscn"},
"SMBANN": {}
}
var can_transition := false
var level_best_time := 0.0
static var level_to_transition_to := "res://Scenes/Levels/World1/1-1.tscn":
set(value):
level_to_transition_to = value
pass
@export var text_shadows: Array[Label] = []
func _ready() -> void:
WarpPipeArea.has_warped = false
Global.level_theme = "Underground"
$BG/Control/MarathonPB.visible = Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE
$BG/Control/LivesCount.visible = Global.current_game_mode != Global.GameMode.MARATHON_PRACTICE
Level.can_set_time = true
ResourceSetterNew.cache.clear()
ResourceSetterNew.property_cache.clear()
AudioManager.current_level_theme = ""
Level.vine_return_level = ""
Level.vine_warp_level = ""
Level.in_vine_level = false
Global.p_switch_active = false
Lakitu.present = false
Global.p_switch_timer = -1
if Global.current_campaign == "SMBANN":
DiscoLevel.reset_values()
DiscoLevel.first_load = true
if Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
Global.clear_saved_values()
if SpeedrunHandler.ghost_enabled:
SpeedrunHandler.load_best_marathon()
SpeedrunHandler.ghost_active = false
show_best_time()
Level.first_load = true
SpeedrunHandler.ghost_idx = -1
SpeedrunHandler.timer_active = false
SpeedrunHandler.timer = 0
get_tree().call_group("PlayerGhosts", "delete")
get_tree().paused = false
$Timer.start()
AudioManager.stop_music_override(AudioManager.MUSIC_OVERRIDES.NONE, true)
AudioManager.music_player.stop()
PipeArea.exiting_pipe_id = -1
var world_num = str(Global.world_num)
if world_num == "-1":
world_num = " "
if Global.world_num >= 10:
world_num = ["A", "B", "C", "D"][Global.world_num % 10]
var lvl_idx := SaveManager.get_level_idx(Global.world_num, Global.level_num)
SaveManager.visited_levels[lvl_idx] = "1"
if Global.current_game_mode == Global.GameMode.CAMPAIGN:
SaveManager.write_save(Global.current_campaign)
Global.set_discord_status("Playing " + Global.current_campaign + ": " + str(world_num) + "-" + str(Global.level_num))
$BG/Control/WorldNum.text = str(world_num) +"-" + str(Global.level_num)
if Settings.file.difficulty.inf_lives:
$BG/Control/LivesCount.text = "* ∞"
elif Global.lives < 100:
$BG/Control/LivesCount.text = "* " + (str(Global.lives).lpad(2, " "))
else:
$BG/Control/LivesCount.text = "* ♕"
if Global.current_game_mode == Global.GameMode.CUSTOM_LEVEL:
$BG/Control/World.hide()
$BG/Control/WorldNum.hide()
%CustomLevelAuthor.show()
%CustomLevelName.show()
%CustomLevelAuthor.text = "By " + LevelEditor.level_author
%CustomLevelName.text = LevelEditor.level_name
await get_tree().create_timer(0.1, false).timeout
can_transition = true
func transition() -> void:
Global.can_time_tick = true
if PIPE_CUTSCENE_LEVELS[Global.current_campaign].has([Global.world_num, Global.level_num]) and not PipeCutscene.seen_cutscene and Global.current_game_mode != Global.GameMode.MARATHON_PRACTICE and Global.current_game_mode !=Global.GameMode.BOO_RACE:
if PIPE_CUTSCENE_OVERRIDE[Global.current_campaign].has([Global.world_num, Global.level_num]):
Global.transition_to_scene(PIPE_CUTSCENE_OVERRIDE[Global.current_campaign][[Global.world_num, Global.level_num]])
else:
Global.transition_to_scene("res://Scenes/Levels/PipeCutscene.tscn")
else:
Global.transition_to_scene(level_to_transition_to)
func show_best_time() -> void:
var best_time = SpeedrunHandler.best_time
if SpeedrunHandler.best_time <= 0:
$BG/Control/MarathonPB.text = "\nNO PB"
return
var string = "PB\n" + SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(SpeedrunHandler.best_time))
$BG/Control/MarathonPB.text = string
func _process(_delta: float) -> void:
if can_transition:
if Input.is_action_just_pressed("jump_0"):
transition()
func _exit_tree() -> void:
Global.death_load = false

View File

@@ -0,0 +1 @@
uid://vwe7y2scu8a1

View File

@@ -0,0 +1,51 @@
extends Level
var can_exit := false
var seen := false
func _enter_tree() -> void:
update_next_level_info()
seen = Global.game_beaten
Global.game_beaten = true
Global.can_time_tick = false
Global.current_level = self
if Global.world_num > 8:
Global.extra_worlds_win = true
update_theme()
Global.current_campaign = campaign
func _ready() -> void:
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.ENDING, 9999999, false)
Global.can_time_tick = false
SaveManager.visited_levels[SaveManager.get_level_idx(9, 1)] = "1"
$Text2/Hero.text = tr("CUTSCENE_LL_PEACH_4" if Global.player_characters[0] != "3" else "CUTSCENE_LL_PEACH_4F")
$Text2/Hurrah.text = tr("CUTSCENE_LL_PEACH_3").replace("{PLAYER}", tr(Player.CHARACTER_NAMES[int(Global.player_characters[0])]))
$ThankYou.text = tr("CUTSCENE_CASTLE_PEACH_1").replace("{PLAYER}", tr(Player.CHARACTER_NAMES[int(Global.player_characters[0])]))
func _process(_delta: float) -> void:
if can_exit and Input.is_action_just_pressed("jump_0"):
SaveManager.write_save()
if seen or Global.current_campaign == "SMBANN" or Global.world_num > 8 or Global.current_game_mode != Global.GameMode.CAMPAIGN:
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
else:
CreditsLevel.go_to_title_screen = false
Global.transition_to_scene("res://Scenes/Levels/Credits.tscn")
$LevelBG.combo_progress = 1
DiscoLevel.can_meter_tick = false
func tally_score() -> void:
add_lives_to_score()
func add_lives_to_score() -> void:
for i in Global.lives:
AudioManager.play_global_sfx("1_up")
Global.score += 100000
await get_tree().create_timer(0.5, false).timeout
func show_toads() -> void:
for i in $Toads.get_children():
i.show()
AudioManager.play_global_sfx("coin")
await get_tree().create_timer(0.7, false).timeout
await get_tree().create_timer(1, false).timeout
can_exit = true

View File

@@ -0,0 +1 @@
uid://caugelxhwlvl0

View File

@@ -0,0 +1,15 @@
extends Node2D
@export_file("*.tscn") var scene := ""
func begin() -> void:
$CameraRightLimit._enter_tree()
await AudioManager.music_override_player.finished
await get_tree().create_timer(1, false).timeout
if Global.current_game_mode == Global.GameMode.MARATHON or Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
Global.open_marathon_results()
return
$StaticBody2D.queue_free()
await get_tree().create_timer(2, false).timeout
Global.transition_to_scene(scene)

View File

@@ -0,0 +1 @@
uid://d23r1hm6jbgox

View File

@@ -0,0 +1,9 @@
extends StaticBody2D
var valid := false
func _physics_process(_delta: float) -> void:
var player = get_tree().get_first_node_in_group("Players")
if player.is_on_floor() == false and not $Area2D.get_overlapping_areas().any(func(area: Area2D): return area.owner is Player):
valid = (player.direction == -1 and player.crouching and player.power_state.hitbox_size == "Big")
$CollisionShape2D.set_deferred("one_way_collision", valid)

View File

@@ -0,0 +1 @@
uid://b41sx3mmdq1pi

View File

@@ -0,0 +1,16 @@
class_name MoonGravity
extends Node
@export var new_gravity := 5
const OLD_GRAVITY := 10
func _ready() -> void:
Global.entity_gravity = new_gravity
for i: Player in get_tree().get_nodes_in_group("Players"):
i.low_gravity = true
func _exit_tree() -> void:
Global.entity_gravity = OLD_GRAVITY

View File

@@ -0,0 +1 @@
uid://dhtqnyrj3ndnj

Some files were not shown because too many files have changed in this diff Show More