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,284 @@
extends Node
const DEFAULT_SFX_LIBRARY := {
"small_jump": ("res://Assets/Audio/SFX/SmallJump.wav"),
"big_jump": ("res://Assets/Audio/SFX/BigJump.wav"),
"coin": ("res://Assets/Audio/SFX/Coin.wav"),
"bump": ("res://Assets/Audio/SFX/Bump.wav"),
"pipe": ("res://Assets/Audio/SFX/Pipe.wav"),
"damage": ("res://Assets/Audio/SFX/Damage.wav"),
"power_up": ("res://Assets/Audio/SFX/Powerup.wav"),
"item_appear": ("res://Assets/Audio/SFX/ItemAppear.wav"),
"block_break": ("res://Assets/Audio/SFX/BreakBlock.wav"),
"enemy_stomp": ("res://Assets/Audio/SFX/Stomp.wav"),
"kick": ("res://Assets/Audio/SFX/Kick.wav"),
"fireball": ("res://Assets/Audio/SFX/Fireball.wav"),
"1_up": ("res://Assets/Audio/SFX/1up.wav"),
"cannon": ("res://Assets/Audio/SFX/Cannon.wav"),
"checkpoint": ("res://Assets/Audio/SFX/Checkpoint.wav"),
"magic": ("res://Assets/Audio/SFX/Magic.wav"),
"beep": ("res://Assets/Audio/SFX/Score.wav"),
"switch": ("res://Assets/Audio/SFX/Switch.wav"),
"boo_laugh": ("res://Assets/Audio/SFX/BooLaugh.wav"),
"icicle_fall": ("res://Assets/Audio/SFX/IcicleFall.wav"),
"icicle_break": ("res://Assets/Audio/SFX/IcicleCrash.wav"),
"score": "res://Assets/Audio/SFX/ScoreLoop.wav",
"score_end": "res://Assets/Audio/SFX/Score.wav",
"pause": ("res://Assets/Audio/SFX/Pause.wav"),
"spring": ("res://Assets/Audio/SFX/Spring.wav"),
"swim": ("res://Assets/Audio/SFX/Swim.wav"),
"dry_bones_crumble": ("res://Assets/Audio/SFX/DryBonesCrumble.wav"),
"clock_get": ("res://Assets/Audio/SFX/ClockGet.wav"),
"bowser_flame": ("res://Assets/Audio/SFX/BowserFire.wav"),
"correct": ("res://Assets/Audio/SFX/Correct.wav"),
"note_block": ("res://Assets/Audio/SFX/NoteBlock.wav"),
"podoboo": ("res://Assets/Audio/SFX/Podoboo.wav"),
"hammer_throw": ("res://Assets/Audio/SFX/HammerThrow.wav"),
"firework": "res://Assets/Audio/SFX/Firework.wav",
"timer_beep": "res://Assets/Audio/SFX/TimerBeep.wav",
"hachisuke": "res://Assets/Audio/SFX/Hachisuke.wav",
"burner": "res://Assets/Audio/SFX/Burner.wav",
"rank_up_1": "res://Assets/Audio/SFX/RankUpCBA.wav",
"rank_up_2": "res://Assets/Audio/SFX/RankUpSP.wav",
"rank_down": "res://Assets/Audio/SFX/RankDown.wav",
"combo_lost": "res://Assets/Audio/SFX/ComboMeterLoss.wav",
"lakitu_throw": "res://Assets/Audio/SFX/LakituThrow.wav",
"lift_fall": "res://Assets/Audio/SFX/LiftFall.wav",
"cheep_cheep": "res://Assets/Audio/SFX/CheepCheepJump.wav",
"menu_move": "res://Assets/Audio/SFX/MenuNavigate.wav",
"timer_warning": "res://Assets/Audio/SFX/TimerRunningLow.wav",
"door_open": "res://Assets/Audio/SFX/DoorOpen.wav",
"door_close": "res://Assets/Audio/SFX/DoorClose.wav",
"key_collect": "res://Assets/Audio/SFX/KeyCollect.wav",
"lucky_star": "res://Assets/Audio/SFX/LuckyStar.wav",
"bumper": "res://Assets/Audio/SFX/Bumper.wav",
"bumper_high": "res://Assets/Audio/SFX/BumperHigh.wav",
"door_unlock": "res://Assets/Audio/SFX/DoorUnlock.wav",
"door_locked": "res://Assets/Audio/SFX/DoorLocked.wav"
}
@onready var sfx_library = DEFAULT_SFX_LIBRARY.duplicate()
@onready var music_player: AudioStreamPlayer = $Music
@onready var music_override_player: AudioStreamPlayer = $MusicOverride
var music_override_priority := -1
var active_sfxs := {}
var current_level_theme := ""
var current_clip_idx := 0
signal music_beat
var queued_sfxs := []
var current_music_override: MUSIC_OVERRIDES
enum MUSIC_OVERRIDES{NONE=-1, STAR=0, DEATH, PSWITCH, BOWSER, TIME_WARNING, LEVEL_COMPLETE, CASTLE_COMPLETE, ENDING, FLAG_POLE, HAMMER, RACE_LOSE, RACE_WIN, WING, COIN_HEAVEN_BONUS}
const OVERRIDE_STREAMS := [
("res://Assets/Audio/BGM/StarMan.json"),
("res://Assets/Audio/BGM/PlayerDie.json"),
("res://Assets/Audio/BGM/PSwitch.json"),
"res://Assets/Audio/BGM/Bowser.json",
"res://Assets/Audio/BGM/Hurry.json",
"res://Assets/Audio/BGM/LevelFinish.json",
"res://Assets/Audio/BGM/CastleFinish.json",
"res://Assets/Audio/BGM/Ending.json",
"res://Assets/Audio/SFX/FlagSlide.wav",
"res://Assets/Audio/BGM/Hammer.mp3",
("res://Assets/Audio/BGM/LoseRace.json"),
("res://Assets/Audio/BGM/WinRace.json"),
"res://Assets/Audio/BGM/Wing.json",
"res://Assets/Audio/BGM/PerfectCoinHeaven.mp3"
]
const MUSIC_BASE = preload("uid://da4vqkrpqnma0")
var character_sfx_map := {}
var audio_override_queue := []
func play_sfx(stream_name = "", position := Vector2.ZERO, pitch := 1.0) -> void:
if queued_sfxs.has(stream_name):
return
queued_sfxs.append(stream_name)
if stream_name is String:
if active_sfxs.has(stream_name):
active_sfxs[stream_name].queue_free()
var player = AudioStreamPlayer2D.new()
player.global_position = position
var stream = stream_name
var is_custom = false
if stream_name is String:
is_custom = sfx_library[stream_name].contains("user://custom_characters")
stream = import_stream(sfx_library[stream_name])
if is_custom == false:
player.stream = ResourceSetter.get_resource(stream, player)
else:
player.stream = stream
player.autoplay = true
player.pitch_scale = pitch
player.max_distance = 99999
player.bus = "SFX"
add_child(player)
active_sfxs[stream_name] = player
queued_sfxs.erase(stream_name)
await player.finished
active_sfxs.erase(stream_name)
player.queue_free()
func play_global_sfx(stream_name := "") -> void:
if get_viewport().get_camera_2d() == null:
return
play_sfx(stream_name, get_viewport().get_camera_2d().get_screen_center_position())
func _process(_delta: float) -> void:
handle_music()
func on_beat(idx := 0) -> void:
music_beat.emit(idx)
func stop_all_music() -> void:
AudioManager.music_player.stop()
if Global.current_level != null:
Global.current_level.music = null
AudioManager.audio_override_queue.clear()
AudioManager.stop_music_override(MUSIC_OVERRIDES.NONE, true)
func kill_sfx(sfx_name := "") -> void:
print(active_sfxs)
if active_sfxs.has(sfx_name):
active_sfxs[sfx_name].queue_free()
active_sfxs.erase(sfx_name)
func set_music_override(stream: MUSIC_OVERRIDES, priority := 0, stop_on_finish := true, restart := true) -> void:
if audio_override_queue.has(stream):
if current_music_override == stream and restart:
music_override_player.play()
return
if music_override_priority > priority:
audio_override_queue.push_front(stream)
return
else:
audio_override_queue.append(stream)
current_music_override = stream
print(OVERRIDE_STREAMS[stream])
music_override_player.stream = create_stream_from_json(OVERRIDE_STREAMS[stream])
music_override_player.bus = "Music" if stream != MUSIC_OVERRIDES.FLAG_POLE else "SFX"
music_override_player.play()
music_override_priority = priority
if stop_on_finish:
await music_override_player.finished
stop_music_override(stream)
func stop_music_override(stream: MUSIC_OVERRIDES, force := false) -> void:
if not force:
if stream == null:
return
elif stream != current_music_override:
audio_override_queue.erase(stream)
return
audio_override_queue.pop_back()
current_music_override = MUSIC_OVERRIDES.NONE
music_override_player.stop()
music_override_priority = -1
if audio_override_queue.is_empty():
audio_override_queue.clear()
music_override_priority = -1
current_music_override = MUSIC_OVERRIDES.NONE
music_override_player.stop()
else:
set_music_override(audio_override_queue[audio_override_queue.size() - 1])
func load_sfx_map(json := {}) -> void:
sfx_library = DEFAULT_SFX_LIBRARY.duplicate()
for i in json:
sfx_library[i] = json[i]
print(json)
func handle_music() -> void:
if Global.in_title_screen:
current_level_theme = ""
AudioServer.set_bus_effect_enabled(1, 0, Global.game_paused)
if is_instance_valid(Global.current_level):
if Global.current_level.music == null or current_music_override != MUSIC_OVERRIDES.NONE:
music_player.stop()
handle_music_override()
return
music_player.stream_paused = false
if current_level_theme != Global.current_level.music.resource_path and Global.current_level.music != null:
var stream = create_stream_from_json(Global.current_level.music.resource_path)
music_player.stream = stream
current_level_theme = Global.current_level.music.resource_path
if music_player.is_playing() == false and current_music_override == MUSIC_OVERRIDES.NONE:
music_player.stop()
current_music_override = MUSIC_OVERRIDES.NONE
music_player.play()
if music_player.stream is AudioStreamInteractive and music_player.is_playing():
if Global.time <= 100:
if music_player.get_stream_playback().get_current_clip_index() != 1:
music_player.get_stream_playback().switch_to_clip(1)
elif music_player.get_stream_playback().get_current_clip_index() != 0:
music_player.get_stream_playback().switch_to_clip(0)
if DiscoLevel.in_disco_level:
music_player.pitch_scale = 2
func handle_music_override() -> void:
if music_override_player.stream is AudioStreamInteractive and music_override_player.is_playing():
if Global.time <= 100:
if music_override_player.get_stream_playback().get_current_clip_index() != 1:
music_override_player.get_stream_playback().switch_to_clip(1)
elif music_override_player.get_stream_playback().get_current_clip_index() != 0:
music_override_player.get_stream_playback().switch_to_clip(0)
func create_stream_from_json(json_path := "") -> AudioStream:
if json_path.contains(".json") == false:
var path = ResourceSetter.get_pure_resource_path(json_path)
if path.contains("user://"):
match json_path.get_slice(".", 1):
"wav":
return AudioStreamWAV.load_from_file(ResourceSetter.get_pure_resource_path(json_path))
"mp3":
return AudioStreamMP3.load_from_file(ResourceSetter.get_pure_resource_path(json_path))
elif path.contains("res://"):
return load(path)
var bgm_file = $ResourceSetterNew.get_variation_json(JSON.parse_string(FileAccess.open(ResourceSetter.get_pure_resource_path(json_path), FileAccess.READ).get_as_text()).variations).source
var path = json_path.replace(json_path.get_file(), bgm_file)
path = ResourceSetter.get_pure_resource_path(path)
var stream = null
if path.get_file().contains(".bgm"):
stream = generate_interactive_stream(JSON.parse_string(FileAccess.open(path, FileAccess.READ).get_as_text()))
else:
if path.contains("res://"):
stream = load(path)
else:
stream = AudioStreamMP3.load_from_file(path)
return stream
func generate_interactive_stream(bgm_file := {}) -> AudioStreamInteractive:
var stream = MUSIC_BASE.duplicate()
var normal_path = ResourceSetter.get_pure_resource_path("res://Assets/Audio/BGM/" + bgm_file.Normal.source)
var hurry_path = ResourceSetter.get_pure_resource_path("res://Assets/Audio/BGM/" + bgm_file.Hurry.source)
stream.set_clip_stream(0, import_stream(normal_path, bgm_file.Normal.loop))
stream.set_clip_stream(1, import_stream(hurry_path, bgm_file.Hurry.loop))
return stream
func import_stream(file_path := "", loop_point := -1.0) -> AudioStream:
var path = file_path
var stream = null
if path.contains("res://"):
stream = load(path)
elif path.contains(".mp3"):
stream = AudioStreamMP3.load_from_file(ResourceSetter.get_pure_resource_path(file_path))
elif path.contains(".wav"):
stream = AudioStreamWAV.load_from_file(path)
print([path, stream])
if path.contains(".mp3"):
stream.set_loop(loop_point >= 0)
stream.set_loop_offset(loop_point)
return stream

View File

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

View File

@@ -0,0 +1,107 @@
class_name ChallengeModeHandler
extends Node
static var challenge_mode := false
static var red_coins := 0
static var yoshi_egg_found := false
static var yoshi_egg_id := 1
static var current_run_red_coins_collected := 0
enum CoinValues{R_COIN_1 = 0, R_COIN_2 = 1, R_COIN_3 = 2, R_COIN_4 = 3, R_COIN_5 = 4, YOSHI_EGG = 5}
const BIT_VALUES := [1, 2, 4, 8, 16, 32, 64, 128]
static var top_challenge_scores := [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
]
const CHALLENGE_TARGETS := {
"SMB1": SMB1_CHALLENGE_SCORE_TARGETS,
"SMBLL": SMBLL_CHALLENGE_SCORE_TARGETS,
"SMBS": SMBS_CHALLENGE_SCORE_TARGETS,
"SMBANN": []
}
const SMB1_CHALLENGE_SCORE_TARGETS := [
[26000, 26000, 19000, 12000],
[40000, 25000, 23000, 14000],
[40000, 45000, 21000, 13000],
[32000, 33000, 24000, 17000],
[80000, 36000, 23000, 13000],
[32000, 30000, 21000, 12000],
[32000, 24000, 28000, 16000],
[40000, 28000, 28000, 18000],
]
const SMBLL_CHALLENGE_SCORE_TARGETS := [
[26000, 26000, 19000, 12000],
[30000, 60000, 23000, 14000],
[30000, 28000, 21000, 13000],
[25000, 33000, 24000, 17000],
[30000, 36000, 23000, 13000],
[32000, 30000, 21000, 12000],
[32000, 24000, 22000, 16000],
[30000, 28000, 28000, 18000],
]
const SMBS_CHALLENGE_SCORE_TARGETS := [
[26000, 26000, 19000, 12000],
[40000, 25000, 23000, 14000],
[30000, 35000, 21000, 13000],
[32000, 33000, 24000, 17000],
[55000, 36000, 23000, 13000],
[32000, 30000, 21000, 12000],
[32000, 24000, 28000, 16000],
[30000, 28000, 28000, 18000],
]
static var red_coins_collected := [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
static func set_value(coin_id := CoinValues.R_COIN_1, value := false) -> void:
if value:
current_run_red_coins_collected |= (1 << coin_id)
else:
current_run_red_coins_collected &= ~(1 << coin_id)
static func is_coin_collected(coin_id: CoinValues = CoinValues.R_COIN_1, num := current_run_red_coins_collected) -> bool:
return num & (1 << coin_id) != 0
func check_for_achievement() -> void:
for x in red_coins_collected:
for i in x:
if i != 63:
return
var target
match Global.current_campaign:
"SMBLL": target = SMBLL_CHALLENGE_SCORE_TARGETS
"SMBS": target = SMBS_CHALLENGE_SCORE_TARGETS
_: target = SMB1_CHALLENGE_SCORE_TARGETS
var world := 0
for x in top_challenge_scores:
var level := 0
for i in x:
if top_challenge_scores[world][level] < target[world][level]:
return
level += 1
world += 1
match Global.current_campaign:
"SMB1": Global.unlock_achievement(Global.AchievementID.SMB1_CHALLENGE)
"SMBLL": Global.unlock_achievement(Global.AchievementID.SMBLL_CHALLENGE)
"SMBS": Global.unlock_achievement(Global.AchievementID.SMBS_CHALLENGE)

View File

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

View File

@@ -0,0 +1,14 @@
extends Node
enum Type{LEVEL = 22962, RESOURCE_PACK = 25680}
enum Sort{RECENT, DOWNLOADS, FEATURED}
signal response_recieved(response: Dictionary)
signal response_failed()
@onready var http = HTTPRequest.new()
const GAME_ID = 7692
func _ready() -> void:
add_child(http)

View File

@@ -0,0 +1 @@
uid://7mdyke5t6lns

View File

@@ -0,0 +1,420 @@
extends Node
var level_theme := "Overworld":
set(value):
level_theme = value
level_theme_changed.emit()
var theme_time := "Day":
set(value):
theme_time = value
level_time_changed.emit()
signal level_theme_changed
signal level_time_changed
const BASE64_CHARSET := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
const VERSION_CHECK_URL := "https://raw.githubusercontent.com/JHDev2006/smb1r-version/refs/heads/main/version.txt"
var entity_gravity := 10.0
var entity_max_fall_speed := 280
var level_editor: LevelEditor = null
var current_level: Level = null
var second_quest := false
var extra_worlds_win := false
const lang_codes := ["en", "fr", "es", "de", "it", "pt", "pl", "tr", "ru", "jp", "fil", "id", "ga"]
var rom_path := ""
var rom_assets_exist := false
const ROM_POINTER_PATH := "user://rom_pointer.smb"
const ROM_PATH := "user://baserom.nes"
const ROM_ASSETS_PATH := "user://resource_packs/BaseAssets"
const ROM_PACK_NAME := "BaseAssets"
const ROM_ASSETS_VERSION := 0
var server_version := -1
var current_version := -1
var version_number := ""
const LEVEL_THEMES := {
"SMB1": SMB1_LEVEL_THEMES,
"SMBLL": SMB1_LEVEL_THEMES,
"SMBANN": SMB1_LEVEL_THEMES,
"SMBS": SMBS_LEVEL_THEMES
}
const SMB1_LEVEL_THEMES := ["Overworld", "Desert", "Snow", "Jungle", "Desert", "Snow", "Jungle", "Overworld", "Space", "Autumn", "Pipeland", "Skyland", "Volcano"]
const SMBS_LEVEL_THEMES := ["Overworld", "Garden", "Beach", "Mountain", "Garden", "Beach", "Mountain", "Overworld", "Autumn", "Pipeland", "Skyland", "Volcano", "Fuck"]
const FORCE_NIGHT_THEMES := ["Space"]
const FORCE_DAY_THEMES := []
signal text_shadow_changed
@onready var player_ghost: PlayerGhost = $PlayerGhost
var debugged_in := true
var score_tween = create_tween()
var time_tween = create_tween()
var score := 0:
set(value):
if disco_mode == true:
if value > score:
var diff = value - score
score = score + (diff * 1)
else:
score = value
else:
score = value
var coins := 0:
set(value):
coins = value
if coins >= 100:#
coins = coins % 100
if Settings.file.difficulty.inf_lives == 0 and (Global.current_game_mode != Global.GameMode.CHALLENGE and Global.current_campaign != "SMBANN"):
lives += floor(coins / 100.0)
AudioManager.play_sfx("1_up", get_viewport().get_camera_2d().get_screen_center_position())
var time := 300
var lives := 3
var world_num := 1
var level_num := 1
var disco_mode := false
signal transition_finished
var transitioning_scene := false
var awaiting_transition := false
signal level_complete_begin
signal score_tally_finished
var achievements := "0000000000000000000000000000"
const LSS_GAME_ID := 5
enum AchievementID{
SMB1_CLEAR, SMBLL_CLEAR, SMBS_CLEAR, SMBANN_CLEAR,
SMB1_CHALLENGE, SMBLL_CHALLENGE, SMBS_CHALLENGE,
SMB1_BOO, SMBLL_BOO, SMBS_BOO,
SMB1_GOLD_BOO, SMBLL_GOLD_BOO, SMBS_GOLD_BOO,
SMB1_BRONZE, SMBLL_BRONZE, SMBS_BRONZE,
SMB1_SILVER, SMBLL_SILVER, SMBS_SILVER,
SMB1_GOLD, SMBLL_GOLD, SMBS_GOLD,
SMB1_RUN, SMBLL_RUN, SMBS_RUN,
ANN_PRANK, SMBLL_WORLD9,
COMPLETIONIST
}
const HIDDEN_ACHIEVEMENTS := [AchievementID.COMPLETIONIST]
var can_time_tick := true:
set(value):
can_time_tick = value
if value == false:
pass
var player_power_states := "0000"
var connected_players := 1
const CAMPAIGNS := ["SMB1", "SMBLL", "SMBS", "SMBANN"]
var player_characters := "0000":
set(value):
player_characters = value
player_characters_changed.emit()
signal player_characters_changed
signal disco_level_continued
signal frame_rule
var hard_mode := false
var current_campaign := "SMB1"
var death_load := false
var tallying_score := false
var in_title_screen := false
var game_paused := false
var can_pause := true
var fade_transition := true
enum GameMode{NONE, CAMPAIGN, BOO_RACE, CHALLENGE, MARATHON, MARATHON_PRACTICE, LEVEL_EDITOR, CUSTOM_LEVEL, DISCO}
const game_mode_strings := ["Default", "Campaign", "BooRace", "Challenge", "Marathon", "MarathonPractice", "LevelEditor", "CustomLevel", "Disco"]
var current_game_mode: GameMode = GameMode.NONE
var high_score := 0
var game_beaten := false
signal p_switch_toggle
var p_switch_active := false
var p_switch_timer := 0.0
var p_switch_timer_paused := false
var debug_mode := false
func _ready() -> void:
current_version = get_version_number()
get_server_version()
if OS.is_debug_build():
debug_mode = false
setup_discord_rpc()
check_for_rom()
func check_for_rom() -> void:
if FileAccess.file_exists(Global.ROM_PATH) == false:
return
var path = Global.ROM_PATH
if FileAccess.file_exists(path):
if ROMVerifier.is_valid_rom(path):
rom_path = path
if DirAccess.dir_exists_absolute(ROM_ASSETS_PATH):
var pack_json: String = FileAccess.get_file_as_string(ROM_ASSETS_PATH + "/pack_info.json")
var pack_dict: Dictionary = JSON.parse_string(pack_json)
if pack_dict.get("version", -1) == ROM_ASSETS_VERSION:
rom_assets_exist = true
else:
OS.move_to_trash(ROM_ASSETS_PATH)
func _process(delta: float) -> void:
if Input.is_action_just_pressed("debug_reload"):
ResourceSetter.cache.clear()
ResourceSetterNew.cache.clear()
ResourceGetter.cache.clear()
AudioManager.current_level_theme = ""
level_theme_changed.emit()
log_comment("Reloaded resource packs!")
handle_p_switch(delta)
if Input.is_key_label_pressed(KEY_F11) and debug_mode == false:
AudioManager.play_global_sfx("switch")
debug_mode = true
log_comment("Debug Mode enabled! some bugs may occur!")
func handle_p_switch(delta: float) -> void:
if p_switch_active and get_tree().paused == false:
if p_switch_timer_paused == false:
p_switch_timer -= delta
if p_switch_timer <= 0:
p_switch_active = false
p_switch_toggle.emit()
AudioManager.stop_music_override(AudioManager.MUSIC_OVERRIDES.PSWITCH)
func get_build_time() -> void:
print(int(Time.get_unix_time_from_system()))
func get_version_number() -> int:
var number = (FileAccess.open("res://version.txt", FileAccess.READ).get_as_text())
version_number = str(number)
return int(number)
func player_action_pressed(action := "", player_id := 0) -> bool:
return Input.is_action_pressed(action + "_" + str(player_id))
func player_action_just_pressed(action := "", player_id := 0) -> bool:
return Input.is_action_just_pressed(action + "_" + str(player_id))
func player_action_just_released(action := "", player_id := 0) -> bool:
return Input.is_action_just_released(action + "_" + str(player_id))
func tally_time() -> void:
if tallying_score:
return
$ScoreTally.play()
tallying_score = true
var target_score = score + (time * 50)
score_tween = create_tween()
time_tween = create_tween()
var duration = float(time) / 120
score_tween.tween_property(self, "score", target_score, duration)
time_tween.tween_property(self, "time", 0, duration)
await score_tween.finished
tallying_score = false
$ScoreTally.stop()
$ScoreTallyEnd.play()
score_tally_finished.emit()
func cancel_score_tally() -> void:
score_tween.kill()
time_tween.kill()
tallying_score = false
$ScoreTally.stop()
func activate_p_switch() -> void:
if p_switch_active == false:
p_switch_toggle.emit()
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.PSWITCH, 99, false)
p_switch_timer = 10
p_switch_active = true
func reset_values() -> void:
PlayerGhost.idx = 0
Checkpoint.passed = false
Checkpoint.sublevel_id = 0
Level.start_level_path = Level.get_scene_string(Global.world_num, Global.level_num)
LevelPersistance.reset_states()
Level.first_load = true
Level.can_set_time = true
Level.in_vine_level = false
Level.vine_return_level = ""
Level.vine_warp_level = ""
func clear_saved_values() -> void:
coins = 0
score = 0
lives = 3
player_power_states = "0000"
func transition_to_scene(scene_path := "") -> void:
Global.fade_transition = bool(Settings.file.visuals.transition_animation)
if transitioning_scene:
return
transitioning_scene = true
if fade_transition:
$Transition/AnimationPlayer.play("FadeIn")
await $Transition/AnimationPlayer.animation_finished
await get_tree().create_timer(0.1, true).timeout
else:
%TransitionBlock.modulate.a = 1
$Transition.show()
await get_tree().create_timer(0.1, true).timeout
get_tree().change_scene_to_file(scene_path)
await get_tree().scene_changed
await get_tree().create_timer(0.15, true).timeout
if fade_transition:
$Transition/AnimationPlayer.play_backwards("FadeIn")
else:
$Transition/AnimationPlayer.play("RESET")
$Transition.hide()
transitioning_scene = false
func do_fake_transition() -> void:
if fade_transition:
$Transition/AnimationPlayer.play("FadeIn")
await $Transition/AnimationPlayer.animation_finished
await get_tree().create_timer(0.2, false).timeout
$Transition/AnimationPlayer.play_backwards("FadeIn")
else:
%TransitionBlock.modulate.a = 1
$Transition.show()
await get_tree().create_timer(0.25, false).timeout
$Transition.hide()
func freeze_screen() -> void:
if Settings.file.video.visuals == 1:
return
$Transition.show()
$Transition/Freeze.show()
$Transition/Freeze.texture = ImageTexture.create_from_image(get_viewport().get_texture().get_image())
func close_freeze() -> void:
$Transition/Freeze.hide()
$Transition.hide()
var recording_dir = "user://marathon_recordings/"
func setup_discord_rpc() -> void:
DiscordRPC.app_id = 1331261692381757562
DiscordRPC.start_timestamp = int(Time.get_unix_time_from_system())
DiscordRPC.details = "In Title Screen.."
if DiscordRPC.get_is_discord_working():
DiscordRPC.refresh()
func set_discord_status(details := "") -> void:
DiscordRPC.details = details
if DiscordRPC.get_is_discord_working():
DiscordRPC.refresh()
func update_game_status() -> void:
var lives_str := str(Global.lives)
if Settings.file.difficulty.inf_lives == 1:
lives_str = ""
var string := "Coins = " + str(Global.coins) + " Lives = " + lives_str
DiscordRPC.large_image = (Global.level_theme + Global.theme_time).to_lower()
DiscordRPC.small_image = Global.current_campaign.to_lower()
DiscordRPC.state = string
func refresh_discord_rpc() -> void:
if DiscordRPC.get_is_discord_working() == false:
return
update_game_status()
DiscordRPC.refresh()
func open_marathon_results() -> void:
get_node("GameHUD/MarathonResults").open()
func open_disco_results() -> void:
get_node("GameHUD/DiscoResults").open()
func on_score_sfx_finished() -> void:
if tallying_score:
$ScoreTally.play()
func get_server_version() -> void:
var http = HTTPRequest.new()
add_child(http)
http.request_completed.connect(version_got)
http.request(VERSION_CHECK_URL, [], HTTPClient.METHOD_GET)
func version_got(_result, response_code, _headers, body) -> void:
if response_code == 200:
server_version = int(body.get_string_from_utf8())
else:
server_version = -2
func log_error(msg := "") -> void:
var error_message = $CanvasLayer/VBoxContainer/ErrorMessage.duplicate()
error_message.text = "Error - " + msg
error_message.visible = true
$CanvasLayer/VBoxContainer.add_child(error_message)
await get_tree().create_timer(10, false).timeout
error_message.queue_free()
func log_warning(msg := "") -> void:
var error_message = $CanvasLayer/VBoxContainer/Warning.duplicate()
error_message.text = "Warning - " + msg
error_message.visible = true
$CanvasLayer/VBoxContainer.add_child(error_message)
await get_tree().create_timer(10, false).timeout
error_message.queue_free()
func log_comment(msg := "") -> void:
var error_message = $CanvasLayer/VBoxContainer/Comment.duplicate()
error_message.text = msg
error_message.visible = true
$CanvasLayer/VBoxContainer.add_child(error_message)
await get_tree().create_timer(2, false).timeout
error_message.queue_free()
func unlock_achievement(achievement_id := AchievementID.SMB1_CLEAR) -> void:
achievements[achievement_id] = "1"
if achievement_id != AchievementID.COMPLETIONIST:
check_completionist_achievement()
SaveManager.write_achievements()
func check_completionist_achievement() -> void:
if achievements.count("0") == 1:
unlock_achievement(AchievementID.COMPLETIONIST)
const FONT = preload("uid://cd221873lbtj1")
func sanitize_string(string := "") -> String:
string = string.to_upper()
for i in string.length():
if FONT.has_char(string.unicode_at(i)) == false and string[i] != "\n":
string = string.replace(string[i], " ")
return string

View File

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

View File

@@ -0,0 +1,182 @@
extends Node
const SAVE_DIR := "user://saves/CAMPAIGN.sav"
var visited_levels := "1000000000000000000000000000000010000000000000000000"
var current_file := {}
const SAVE_TEMPLATE := {
"World": 1,
"Level": 1,
"Lives": 3,
"Coins": 0,
"Score": 0,
"GameWin": false,
"PowerStates": "0000",
"LevelsVisited": "1000000000000000000000000000000000000000000000000000",
"BestAnyTime": 0.0,
"BestWarplessTime": 0.0,
"ClearedBooLevels": "00000000",
"ChallengeScores": [
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0]
],
"RedCoins": [
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0]
],
"BooBestTimes": [
-1.0, -1.0, -1.0, -1.0,
-1.0, -1.0, -1.0, -1.0
],
"HighScore": 0,
"ExtraWorldWin": false
}
func _ready() -> void:
verify_saves()
load_achievements()
func load_save(campaign := "SMB1") -> Dictionary:
if FileAccess.file_exists(SAVE_DIR.replace("CAMPAIGN", campaign)) == false:
write_save(campaign)
var file = FileAccess.open(SAVE_DIR.replace("CAMPAIGN", campaign), FileAccess.READ)
var json = JSON.parse_string(file.get_as_text())
print(file.get_as_text())
current_file = json
file.close()
return json
func verify_saves() -> void:
for campaign in Global.CAMPAIGNS:
if FileAccess.file_exists(SAVE_DIR.replace("CAMPAIGN", campaign)) == false:
write_save(campaign, true)
func write_save(campaign: String = Global.current_campaign, force := false) -> void:
if Global.debugged_in and not force:
return
var save = null
DirAccess.make_dir_recursive_absolute("user://saves")
var save_json = {}
var path = "user://saves/" + campaign + ".sav"
if FileAccess.file_exists(path):
save = FileAccess.open("user://saves/" + campaign + ".sav", FileAccess.READ)
save_json = JSON.parse_string(save.get_as_text())
save.close()
else:
save_json = SAVE_TEMPLATE.duplicate(true)
match Global.current_game_mode:
Global.GameMode.CAMPAIGN:
if Global.high_score < Global.score:
Global.high_score = Global.score
save_json["World"] = Global.world_num
save_json["Level"] = Global.level_num
save_json["Coins"] = Global.coins
save_json["Score"] = Global.score
save_json["GameWin"] = Global.game_beaten
save_json["PowerStates"] = Global.player_power_states
save_json["LevelsVisited"] = visited_levels
save_json["HighScore"] = Global.high_score
save_json["ExtraWorldWin"] = Global.extra_worlds_win
Global.GameMode.CHALLENGE:
save_json["ChallengeScores"] = ChallengeModeHandler.top_challenge_scores
save_json["RedCoins"] = ChallengeModeHandler.red_coins_collected
Global.GameMode.BOO_RACE:
save_json["ClearedBooLevels"] = BooRaceHandler.cleared_boo_levels
save_json["BooBestTimes"] = BooRaceHandler.best_times
Global.GameMode.MARATHON:
save_json["BestAnyTime"] = SpeedrunHandler.marathon_best_any_time
save_json["BestWarplessTime"] = SpeedrunHandler.marathon_best_warpless_time
_:
pass
if campaign == "SMBANN":
save_json["Ranks"] = DiscoLevel.level_ranks
write_save_to_file(save_json, path)
func write_save_to_file(json := {}, path := "") -> void:
var file = FileAccess.open(path, FileAccess.WRITE)
file.store_string(JSON.stringify(json, "\t", false, false))
file.close()
func apply_save(json := {}) -> void:
Global.world_num = clamp(json["World"], 1, 8)
Global.level_num = json.get_or_add("Level", 1)
Global.lives = json["Lives"]
Global.coins = json["Coins"]
Global.score = json["Score"]
ChallengeModeHandler.red_coins_collected = json["RedCoins"]
ChallengeModeHandler.top_challenge_scores = json["ChallengeScores"]
BooRaceHandler.cleared_boo_levels = json["ClearedBooLevels"]
Global.player_power_states = json["PowerStates"]
Global.game_beaten = json["GameWin"]
for i in json["LevelsVisited"].length():
visited_levels[i] = json["LevelsVisited"][i]
Global.extra_worlds_win = json.get("ExtraWorldWin", false)
SpeedrunHandler.marathon_best_any_time = json.get("BestAnyTime", -1)
SpeedrunHandler.marathon_best_warpless_time = json.get("BestWarplessTime", -1.0)
Global.high_score = json["HighScore"]
if json.has("Ranks"):
DiscoLevel.level_ranks = json.get("Ranks")
if json.has("BooBestTimes"):
BooRaceHandler.best_times = json.get("BooBestTimes").duplicate()
func clear_save() -> void:
for i in [BooRaceHandler.cleared_boo_levels, ChallengeModeHandler.top_challenge_scores, ChallengeModeHandler.red_coins_collected, visited_levels]:
if i is Array:
clear_array(i)
else:
i = clear_text(i)
visited_levels[0][0] = "1"
var save = SAVE_TEMPLATE.duplicate(true)
apply_save(save)
DirAccess.remove_absolute("user://saves/" + Global.current_campaign + ".sav")
write_save(Global.current_campaign)
func clear_array(arr := []) -> void:
for i in arr.size():
if arr[i] is Array:
clear_array(arr[i])
elif arr[i] is bool:
arr[i] = false
else:
arr[i] = 0
func clear_text(text := "") -> String:
for i in text.length():
if text[i].is_valid_int():
text[i] = "0"
return text
func get_level_idx(world_num := 1, level_num := 1) -> int:
return ((world_num - 1) * 4) + (level_num - 1)
func load_achievements() -> void:
if FileAccess.file_exists("user://achievements.sav") == false:
write_achievements()
var file = FileAccess.open("user://achievements.sav", FileAccess.READ)
var idx := 0
for i in file.get_as_text():
Global.achievements[idx] = i
idx += 1
AchievementMenu.unlocked_achievements = Global.achievements
file.close()
func write_achievements() -> void:
var file = FileAccess.open("user://achievements.sav", FileAccess.WRITE)
file.store_string(Global.achievements)
file.close()

View File

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

View File

@@ -0,0 +1,113 @@
extends Node
var file := {
"video": {
"mode": 0,
"size": 0,
"vsync": 1,
"drop_shadows": 1,
"scaling": 0,
"visuals": 0,
"hud_size": 0
},
"audio": {
"master": 10.0,
"music": 10.0,
"sfx": 10.0,
"athletic_bgm": 1,
"extra_bgm": 1,
"skid_sfx": 1,
"extra_sfx": 0,
"menu_bgm": 0
},
"game": {
"campaign": "SMB1",
"lang": "en",
"character": "0000"
},
"keyboard":
{
"jump": "Z",
"run": "X",
"action": "X",
"move_left": "Left",
"move_right": "Right",
"move_up": "Up",
"move_down": "Down"
},
"controller":
{
"jump": 0,
"run": 2,
"action": 2,
"move_left": "0,-1",
"move_right": "0,1",
"move_up": "1,-1",
"move_down": "1,1"
},
"visuals":
{
"parallax_style": 2,
"resource_packs": [Global.ROM_PACK_NAME],
"modern_hud": 0,
"rainbow_style": 0,
"extra_bgs": 1,
"bg_particles": 1,
"transform_style": 0,
"athletic_bgm": 1,
"skid_sfx": 1,
"text_shadows": 1,
"bridge_animation": 0,
"visible_timers": 0,
"transition_animation": 0,
"colour_pipes": 1
},
"difficulty":
{
"damage_style": 1,
"checkpoint_style": 0,
"inf_lives": 0,
"flagpole_lives": 0,
"game_over_behaviour": 0,
"level_design": 0,
"extra_checkpoints": 0,
"back_scroll": 0,
"time_limit": 1,
"lakitu_style": 0
}
}
const SETTINGS_DIR := "user://settings.cfg"
func _enter_tree() -> void:
DirAccess.make_dir_absolute("user://resource_packs")
load_settings()
await get_tree().physics_frame
apply_settings()
TranslationServer.set_locale(Settings.file.game.lang)
func save_settings() -> void:
var cfg_file = ConfigFile.new()
for section in file.keys():
for key in file[section].keys():
cfg_file.set_value(section, key, file[section][key])
cfg_file.set_value("game", "seen_disclaimer", true)
cfg_file.set_value("game", "campaign", Global.current_campaign)
cfg_file.save(SETTINGS_DIR)
func load_settings() -> void:
if FileAccess.file_exists(SETTINGS_DIR) == false:
save_settings()
var cfg_file = ConfigFile.new()
cfg_file.load(SETTINGS_DIR)
for section in cfg_file.get_sections():
for key in cfg_file.get_section_keys(section):
file[section][key] = cfg_file.get_value(section, key)
func apply_settings() -> void:
for i in file.video.keys():
$Apply/Video.set_value(i, file.video[i])
for i in file.audio.keys():
$Apply/Audio.set_value(i, file.audio[i])
if Settings.file.game.has("characters"):
Global.player_characters = Settings.file.game.characters

View File

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

View File

@@ -0,0 +1,371 @@
extends Node
var timer := 0.0
var best_time := 0.0
var marathon_best_any_time := 0.0
var marathon_best_warpless_time := 0.0
var timer_active := false
var show_timer := false
signal level_finished
var start_time := 0.0
const GHOST_RECORDING_TEMPLATE := {
"position": Vector2.ZERO,
"character": "Mario",
"power_state": "Small",
"animation": "Idle",
"frame": 0,
"direction": 1,
"level": ""
}
var enable_recording := false
var current_recording := ""
var ghost_recording := ""
var ghost_active := false
var ghost_idx := -1
var ghost_visible := false
var ghost_enabled := false
var levels := []
var anim_list := []
var show_pb_diff := true
var is_warp_run := false
var ghost_path := []
var best_time_campaign := ""
var best_level_any_times := {}
var best_level_warpless_times := [
[-1, -1, -1, -1],
[-1, -1, -1, -1],
[-1, -1, -1, -1],
[-1, -1, -1, -1],
[-1, -1, -1, -1],
[-1, -1, -1, -1],
[-1, -1, -1, -1],
[-1, -1, -1, -1]
]
const GOLD_ANY_TIMES := {
"SMB1": 390,
"SMBLL": 660,
"SMBS": 1440
}
const GOLD_WARPLESS_TIMES := {
"SMB1": 1320,
"SMBLL": 1380,
"SMBS": 1440
}
const WARP_LEVELS := {
"SMB1": SMB1_WARP_LEVELS,
"SMBLL": SMBLL_WARP_LEVELS,
"SMBS": SMBS_WARP_LEVELS
}
const LEVEL_GOLD_WARPLESS_TIMES := {
"SMB1": SMB1_LEVEL_GOLD_WARPLESS_TIMES,
"SMBLL": SMBLL_LEVEL_GOLD_WARPLESS_TIMES,
"SMBS": SMBS_LEVEL_GOLD_TIMES
}
const LEVEL_GOLD_ANY_TIMES := {
"SMB1": SMB1_LEVEL_GOLD_ANY_TIMES,
"SMBLL": SMBLL_LEVEL_GOLD_ANY_TIMES,
"SMBS": SMBS_LEVEL_GOLD_ANY_TIMES
}
const SMB1_LEVEL_GOLD_WARPLESS_TIMES := [
[17, 24, 17, 16], # World 1
[23, 38, 25, 16], # World 2
[23, 23, 17, 16], # World 3
[24, 25, 16, 22], # World 4
[22, 22, 17, 16], # World 5
[21, 25, 18, 16], # World 6
[20, 38, 25, 23], # World 7
[40, 24, 24, 50] # World 8
]
const SMBLL_LEVEL_GOLD_WARPLESS_TIMES := [
[21, 25, 19, 17],
[26, 34, 21, 18],
[21, 39, 21, 20],
[22, 23, 21, 25],
[43, 28, 25, 24],
[28, 39, 23, 29],
[21, 26, 32, 36],
[24, 27, 25, 60],
]
const SMB1_LEVEL_GOLD_ANY_TIMES := {
"1-2": 25,
"4-2": 26
}
const SMBLL_LEVEL_GOLD_ANY_TIMES := {
"1-2": 40,
"3-1": 22,
"5-1": 52,
"5-2": 35,
"8-1": 44
}
const SMBS_LEVEL_GOLD_ANY_TIMES := {
"1-2": 25,
"4-2": 30
}
const SMBS_LEVEL_GOLD_TIMES := [
[28, 21, 32, 19],
[27, 40, 31, 19],
[31, 11, 16, 20],
[26, 30, 25, 32],
[28, 26, 19, 19],
[24, 21, 23, 20],
[24, 40, 30, 27],
[30, 35, 30, 43],
]
const SMB1_WARP_LEVELS := ["1-2", "4-2"]
const SMBLL_WARP_LEVELS := ["1-2", "3-1", "5-1", "5-2", "8-1"]
const SMBS_WARP_LEVELS := ["4-2"]
const MEDAL_CONVERSIONS := [2, 1.5, 1]
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
func _physics_process(_delta: float) -> void:
if timer_active:
timer = abs(start_time - Time.get_ticks_msec()) / 1000
if enable_recording:
if get_tree().get_first_node_in_group("Players") != null:
record_frame(get_tree().get_first_node_in_group("Players"))
Global.player_ghost.visible = ghost_visible
if ghost_active and ghost_enabled:
ghost_idx += 1
if ghost_idx >= ghost_path.size():
ghost_active = false
return
Global.player_ghost.apply_data(ghost_path[ghost_idx])
func start_timer() -> void:
timer = 0
timer_active = true
show_timer = true
start_time = Time.get_ticks_msec()
func record_frame(player: Player) -> void:
var data := ""
if levels.has(Global.current_level.scene_file_path) == false:
levels.append(Global.current_level.scene_file_path)
data += str(int(player.global_position.x)) + "="
data += str(int(player.global_position.y)) + "="
data += str(["Small", "Big", "Fire"].find(player.power_state.state_name)) + "="
if anim_list.has(player.sprite.animation) == false:
anim_list.append(player.sprite.animation)
data += str(anim_list.find(player.sprite.animation)) + "="
data += str(player.sprite.frame) + "="
data += str(player.sprite.scale.x) + "="
data += str(levels.find(Global.current_level.scene_file_path))
current_recording += data + ","
func format_time(time_time := 0.0) -> Dictionary:
var mils = abs(fmod(time_time, 1) * 100)
var secs = abs(fmod(time_time, 60))
var mins = abs(time_time / 60)
return {"mils": int(mils), "secs": int(secs), "mins": int(mins)}
func gen_time_string(timer_dict := {}) -> String:
return str(int(timer_dict["mins"])).pad_zeros(2) + ":" + str(int(timer_dict["secs"])).pad_zeros(2) + ":" + str(int(timer_dict["mils"])).pad_zeros(2)
func save_recording() -> void:
var recording := [timer, current_recording, levels, str(["Mario", "Luigi", "Toad", "Toadette"].find(get_tree().get_first_node_in_group("Players").character)), anim_list]
var recording_dir = "user://marathon_recordings/" + Global.current_campaign
DirAccess.make_dir_recursive_absolute(recording_dir)
var file = FileAccess.open(recording_dir + "/" + str(Global.world_num) + "-" + str(Global.level_num) + ("warp" if is_warp_run else "") + ".json", FileAccess.WRITE)
file.store_string(compress_recording(JSON.stringify(recording, "", false, true)))
current_recording = ""
file.close()
levels.clear()
func compress_recording(recording := "") -> String:
print(recording)
var bytes = recording.to_ascii_buffer()
var compressed_bytes = bytes.compress(FileAccess.CompressionMode.COMPRESSION_DEFLATE)
var b64 = Marshalls.raw_to_base64(compressed_bytes)
return b64
func decompress_recording(recording := "") -> Array:
var compressed_bytes = Marshalls.base64_to_raw(recording)
var bytes = compressed_bytes.decompress_dynamic(-1, FileAccess.COMPRESSION_DEFLATE)
var string = bytes.get_string_from_ascii()
var json = JSON.parse_string(string)
return json
func load_best_marathon() -> void:
var recording = load_recording(Global.world_num, Global.level_num, not is_warp_run, Global.current_campaign)
if recording == []:
best_time = -1
ghost_active = false
ghost_recording = ""
ghost_path = []
levels = []
anim_list = []
else:
ghost_active = true
ghost_recording = recording[1]
ghost_path = ghost_recording.split(",", false)
levels = recording[2].duplicate()
anim_list = recording[4].duplicate()
func load_recording(world_num := 0, level_num := 0, is_warpless := true, campaign := "SMB1") -> Array:
var recording_dir = "user://marathon_recordings/" + campaign
var path = recording_dir + "/" + str(world_num) + "-" + str(level_num) + ("" if is_warpless else "warp") + ".json"
print(path)
if FileAccess.file_exists(path) == false:
return []
var file = FileAccess.open(path, FileAccess.READ)
var text = decompress_recording(file.get_as_text())
file.close()
return text
func load_best_times(campaign = Global.current_campaign) -> void:
if best_time_campaign == campaign:
return
best_time_campaign = campaign
best_level_any_times.clear()
for world_num in 8:
for level_num in 4:
var path = "user://marathon_recordings/" + campaign + "/" + str(world_num + 1) + "-" + str(level_num + 1) + ".json"
if FileAccess.file_exists(path):
best_level_warpless_times[world_num][level_num] = load_recording(world_num + 1, level_num + 1, true, campaign)[0]
else:
best_level_warpless_times[world_num][level_num] = -1
path = "user://marathon_recordings/" + campaign + "/" + str(world_num + 1) + "-" + str(level_num + 1) +"warp" + ".json"
if FileAccess.file_exists(path):
best_level_any_times[str(world_num + 1) + "-" + str(level_num + 1)] = load_recording(world_num + 1, level_num + 1, false, campaign)[0]
check_for_medal_achievement()
func run_finished() -> void:
if timer_active == false:
return
SpeedrunHandler.ghost_active = false
SpeedrunHandler.ghost_idx = -1
SpeedrunHandler.timer_active = false
if Global.current_game_mode == Global.GameMode.BOO_RACE:
pass
else:
var best = best_level_warpless_times[Global.world_num - 1][Global.level_num - 1]
if is_warp_run:
best = best_level_any_times.get(str(Global.world_num) + "-" + str(Global.level_num), -1)
if best <= 0 or best > timer:
if Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
save_recording()
if is_warp_run:
best_level_any_times[str(Global.world_num) + "-" + str(Global.level_num)] = timer
else:
best_level_warpless_times[Global.world_num - 1][Global.level_num - 1] = timer
else:
if is_warp_run:
marathon_best_any_time = timer
else:
marathon_best_warpless_time = timer
if Global.current_game_mode == Global.GameMode.MARATHON:
match Global.current_campaign:
"SMB1": Global.unlock_achievement(Global.AchievementID.SMB1_RUN)
"SMBLL": Global.unlock_achievement(Global.AchievementID.SMBLL_RUN)
"SMBS": Global.unlock_achievement(Global.AchievementID.SMBS_RUN)
check_for_medal_achievement()
SaveManager.write_save(Global.current_campaign)
func get_best_time() -> float:
if Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
if is_warp_run:
return best_level_any_times[str(Global.world_num) + "-" + str(Global.level_num)]
else:
return best_level_warpless_times[Global.world_num - 1][Global.level_num - 1]
else:
if is_warp_run:
return marathon_best_any_time
else:
return marathon_best_warpless_time
func check_for_medal_achievement() -> void:
var has_gold_warp := true
var has_gold_warpless := true
var has_silver_warpless := true
var has_silver_warp := true
var has_bronze_warpless := true
var has_bronze_warp := true
var has_gold_full := false
var has_silver_full := false
var has_bronze_full := false
if Global.current_campaign == "SMBANN":
return
for i in LEVEL_GOLD_ANY_TIMES[Global.current_campaign]:
if best_level_any_times.has(i):
if best_level_any_times[i] > LEVEL_GOLD_ANY_TIMES[Global.current_campaign][i]:
has_gold_warp = false
if best_level_any_times[i] > LEVEL_GOLD_ANY_TIMES[Global.current_campaign][i] * MEDAL_CONVERSIONS[1]:
has_silver_warp = false
if best_level_any_times[i] > LEVEL_GOLD_ANY_TIMES[Global.current_campaign][i] * MEDAL_CONVERSIONS[0]:
has_bronze_warp = false
var world := 0
for i in best_level_warpless_times:
var level := 0
for x in i:
if x < 0:
has_gold_warpless = false
has_silver_warpless = false
has_bronze_warpless = false
if x > LEVEL_GOLD_WARPLESS_TIMES[Global.current_campaign][world][level]:
has_gold_warpless = false
if x > LEVEL_GOLD_WARPLESS_TIMES[Global.current_campaign][world][level] * MEDAL_CONVERSIONS[1]:
has_silver_warpless = false
if x > LEVEL_GOLD_WARPLESS_TIMES[Global.current_campaign][world][level] * MEDAL_CONVERSIONS[0]:
has_bronze_warpless = false
level += 1
world += 1
if marathon_best_any_time <= GOLD_ANY_TIMES[Global.current_campaign] and marathon_best_warpless_time <= GOLD_WARPLESS_TIMES[Global.current_campaign]:
has_gold_full = true
if marathon_best_any_time <= GOLD_ANY_TIMES[Global.current_campaign] * MEDAL_CONVERSIONS[1] and marathon_best_warpless_time <= GOLD_WARPLESS_TIMES[Global.current_campaign] * MEDAL_CONVERSIONS[1]:
has_silver_full = true
if marathon_best_any_time <= GOLD_ANY_TIMES[Global.current_campaign] * MEDAL_CONVERSIONS[0] and marathon_best_warpless_time <= GOLD_WARPLESS_TIMES[Global.current_campaign] * MEDAL_CONVERSIONS[0]:
has_bronze_full = true
if has_gold_warp and has_gold_warpless and has_gold_full:
match Global.current_campaign:
"SMB1": Global.unlock_achievement(Global.AchievementID.SMB1_GOLD)
"SMBLL": Global.unlock_achievement(Global.AchievementID.SMBLL_GOLD)
"SMBS": Global.unlock_achievement(Global.AchievementID.SMBS_GOLD)
if has_silver_warp and has_silver_warpless and has_silver_full:
match Global.current_campaign:
"SMB1": Global.unlock_achievement(Global.AchievementID.SMB1_SILVER)
"SMBLL": Global.unlock_achievement(Global.AchievementID.SMBLL_SILVER)
"SMBS": Global.unlock_achievement(Global.AchievementID.SMBS_SILVER)
if has_bronze_warp and has_bronze_warpless and has_bronze_full:
match Global.current_campaign:
"SMB1": Global.unlock_achievement(Global.AchievementID.SMB1_BRONZE)
"SMBLL": Global.unlock_achievement(Global.AchievementID.SMBLL_BRONZE)
"SMBS": Global.unlock_achievement(Global.AchievementID.SMBS_BRONZE)

View File

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