mirror of
https://github.com/JHDev2006/Super-Mario-Bros.-Remastered-Public.git
synced 2025-10-22 07:28:14 +00:00
added the game
This commit is contained in:
284
Scripts/Classes/Singletons/AudioManager.gd
Normal file
284
Scripts/Classes/Singletons/AudioManager.gd
Normal 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
|
||||
|
1
Scripts/Classes/Singletons/AudioManager.gd.uid
Executable file
1
Scripts/Classes/Singletons/AudioManager.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://db0uolj5jvlca
|
107
Scripts/Classes/Singletons/ChallengeModeHandler.gd
Normal file
107
Scripts/Classes/Singletons/ChallengeModeHandler.gd
Normal 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)
|
1
Scripts/Classes/Singletons/ChallengeModeHandler.gd.uid
Executable file
1
Scripts/Classes/Singletons/ChallengeModeHandler.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://csd5i1m4l5at2
|
14
Scripts/Classes/Singletons/GameBananaManager.gd
Executable file
14
Scripts/Classes/Singletons/GameBananaManager.gd
Executable 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)
|
1
Scripts/Classes/Singletons/GameBananaManager.gd.uid
Executable file
1
Scripts/Classes/Singletons/GameBananaManager.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://7mdyke5t6lns
|
420
Scripts/Classes/Singletons/Global.gd
Normal file
420
Scripts/Classes/Singletons/Global.gd
Normal 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
|
1
Scripts/Classes/Singletons/Global.gd.uid
Executable file
1
Scripts/Classes/Singletons/Global.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bbxqn16ekbpcl
|
182
Scripts/Classes/Singletons/SaveManager.gd
Normal file
182
Scripts/Classes/Singletons/SaveManager.gd
Normal 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()
|
1
Scripts/Classes/Singletons/SaveManager.gd.uid
Executable file
1
Scripts/Classes/Singletons/SaveManager.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ccqj86t3c0eof
|
113
Scripts/Classes/Singletons/SettingsManager.gd
Normal file
113
Scripts/Classes/Singletons/SettingsManager.gd
Normal 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
|
1
Scripts/Classes/Singletons/SettingsManager.gd.uid
Executable file
1
Scripts/Classes/Singletons/SettingsManager.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ckwnap31rqfru
|
371
Scripts/Classes/Singletons/SpeedrunHandler.gd
Normal file
371
Scripts/Classes/Singletons/SpeedrunHandler.gd
Normal 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)
|
1
Scripts/Classes/Singletons/SpeedrunHandler.gd.uid
Executable file
1
Scripts/Classes/Singletons/SpeedrunHandler.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cpy12nxnlkpav
|
Reference in New Issue
Block a user