mirror of
https://github.com/JHDev2006/Super-Mario-Bros.-Remastered-Public.git
synced 2025-10-22 15:38:14 +00:00
added the game
This commit is contained in:
72
Scripts/UI/AchievementContainer.gd
Normal file
72
Scripts/UI/AchievementContainer.gd
Normal file
@@ -0,0 +1,72 @@
|
||||
class_name AchievementContainer
|
||||
extends HBoxContainer
|
||||
|
||||
var achievement_id := 0
|
||||
|
||||
var selected := false
|
||||
|
||||
var unlocked := false
|
||||
|
||||
const ICON_RECTS := [
|
||||
Vector2i(0, 0), Vector2i(1, 0), Vector2i(2, 0), Vector2i(3, 0),
|
||||
Vector2i(0, 1), Vector2i(1, 1), Vector2i(2, 1),
|
||||
Vector2i(0, 2), Vector2i(1, 2), Vector2i(2, 2),
|
||||
Vector2i(0, 3), Vector2i(1, 3), Vector2i(2, 3),
|
||||
Vector2i(0, 4), Vector2i(1, 4), Vector2i(2, 4),
|
||||
Vector2i(0, 5), Vector2i(1, 5), Vector2i(2, 5),
|
||||
Vector2i(0, 6), Vector2i(1, 6), Vector2i(2, 6),
|
||||
Vector2i(0, 7), Vector2i(1, 7), Vector2i(2, 7),
|
||||
Vector2i(3, 1), Vector2i(3, 2), Vector2i(3, 4)
|
||||
]
|
||||
|
||||
const ACHIEVEMENT_NAMES := [
|
||||
"TITLE_SMB1_CLEAR", "TITLE_SMBLL_CLEAR", "TITLE_SMBS_CLEAR", "TITLE_SMBANN_CLEAR",
|
||||
"TITLE_SMB1_CHALLENGE", "TITLE_SMBLL_CHALLENGE", "TITLE_SMBS_CHALLENGE",
|
||||
"TITLE_SMB1_BOO", "TITLE_SMBLL_BOO", "TITLE_SMBS_BOO",
|
||||
"TITLE_SMB1_GOLD_BOO", "TITLE_SMBLL_GOLD_BOO", "TITLE_SMBS_GOLD_BOO",
|
||||
"TITLE_SMB1_BRONZE", "TITLE_SMBLL_BRONZE", "TITLE_SMBS_BRONZE",
|
||||
"TITLE_SMB1_SILVER", "TITLE_SMBLL_SILVER", "TITLE_SMBS_SILVER",
|
||||
"TITLE_SMB1_GOLD", "TITLE_SMBLL_GOLD", "TITLE_SMBS_GOLD",
|
||||
"TITLE_SMB1_RUN", "TITLE_SMBLL_RUN", "TITLE_SMBS_RUN",
|
||||
"TITLE_ANN_PRANK", "TITLE_SMBLL_WORLD9", "TITLE_COMPLETION"
|
||||
]
|
||||
|
||||
const ACHIEVEMENT_DESCS := [
|
||||
"DESC_SMB1_CLEAR", "DESC_SMBLL_CLEAR", "DESC_SMBS_CLEAR", "DESC_SMBANN_CLEAR",
|
||||
"DESC_SMB1_CHALLENGE", "DESC_SMBLL_CHALLENGE", "DESC_SMBS_CHALLENGE",
|
||||
"DESC_SMB1_BOO", "DESC_SMBLL_BOO", "DESC_SMBS_BOO",
|
||||
"DESC_SMB1_GOLD_BOO", "DESC_SMBLL_GOLD_BOO", "DESC_SMBS_GOLD_BOO",
|
||||
"DESC_SMB1_BRONZE", "DESC_SMBLL_BRONZE", "DESC_SMBS_BRONZE",
|
||||
"DESC_SMB1_SILVER", "DESC_SMBLL_SILVER", "DESC_SMBS_SILVER",
|
||||
"DESC_SMB1_GOLD", "DESC_SMBLL_GOLD", "DESC_SMBS_GOLD",
|
||||
"DESC_SMB1_RUN", "DESC_SMBLL_RUN", "DESC_SMBS_RUN",
|
||||
"DESC_ANN_PRANK", "DESC_SMBLL_WORLD9", "DESC_COMPLETION"
|
||||
]
|
||||
|
||||
var progress := 0
|
||||
var total_needed := 0
|
||||
|
||||
func _ready() -> void:
|
||||
setup_visuals()
|
||||
set_active(false)
|
||||
|
||||
func setup_visuals() -> void:
|
||||
var achievement_name = "TITLE_LOCKED_ACHIEVEMENT"
|
||||
var rect = Vector2i(3, 3)
|
||||
if unlocked:
|
||||
achievement_name = ACHIEVEMENT_NAMES[achievement_id]
|
||||
rect = ICON_RECTS[achievement_id]
|
||||
$PanelContainer.modulate = Color.WHITE
|
||||
%Title.text = achievement_name
|
||||
%Description.text = ACHIEVEMENT_DESCS[achievement_id]
|
||||
%Icon.region_rect = Rect2(rect * 32, Vector2(32, 32))
|
||||
%Progress.visible = not unlocked and total_needed > 0
|
||||
%ProgressBar.max_value = total_needed
|
||||
%ProgressBar.value = progress
|
||||
%TotalGot.text = str(progress)
|
||||
%TotalNeeded.text = "/" + str(total_needed)
|
||||
|
||||
func set_active(active := false) -> void:
|
||||
$Cursor.modulate.a = int(active)
|
||||
$PanelContainer/MarginContainer/HBoxContainer/VBoxContainer/AutoScrollContainer.is_active = active
|
||||
$PanelContainer/MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/AutoScrollContainer2.is_active = active
|
1
Scripts/UI/AchievementContainer.gd.uid
Normal file
1
Scripts/UI/AchievementContainer.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cfoivmjplms0g
|
43
Scripts/UI/AchievementMenu.gd
Normal file
43
Scripts/UI/AchievementMenu.gd
Normal file
@@ -0,0 +1,43 @@
|
||||
class_name AchievementMenu
|
||||
extends Node
|
||||
|
||||
const ACHIEVEMENT_CONTAINER = ("uid://8wnmuhtwu8ib")
|
||||
|
||||
var total_unlocked := 0
|
||||
|
||||
static var unlocked_achievements := "0000000000000000000000000000"
|
||||
|
||||
func _ready() -> void:
|
||||
unlocked_achievements = Global.achievements
|
||||
spawn_achievement_containers()
|
||||
$BG/Border/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer.get_child(0).grab_focus()
|
||||
Global.get_node("GameHUD").hide()
|
||||
var percent = int((float(total_unlocked) / Global.achievements.length()) * 100)
|
||||
%Progress.text = str(percent) + "% "
|
||||
if percent == 100:
|
||||
%Progress.modulate = Color("FFB259")
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_back"):
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
|
||||
func spawn_achievement_containers() -> void:
|
||||
var idx := 0
|
||||
for i in Global.achievements:
|
||||
if Global.HIDDEN_ACHIEVEMENTS.has(idx) and Global.achievements[idx] == "0":
|
||||
idx += 1
|
||||
continue
|
||||
var container = load(ACHIEVEMENT_CONTAINER).instantiate()
|
||||
container.achievement_id = idx
|
||||
container.unlocked = i == "1" or Global.debug_mode
|
||||
if i == "1":
|
||||
total_unlocked += 1
|
||||
else:
|
||||
if $ProgressCalculators.has_node(str(idx)):
|
||||
container.total_needed = $ProgressCalculators.get_node(str(idx)).target_number
|
||||
container.progress = $ProgressCalculators.get_node(str(idx)).get_progress()
|
||||
$BG/Border/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer.add_child(container)
|
||||
idx += 1
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Global.get_node("GameHUD").show()
|
1
Scripts/UI/AchievementMenu.gd.uid
Normal file
1
Scripts/UI/AchievementMenu.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d3jebf1trkaor
|
25
Scripts/UI/AchievementUnlockPopup.gd
Normal file
25
Scripts/UI/AchievementUnlockPopup.gd
Normal file
@@ -0,0 +1,25 @@
|
||||
extends Control
|
||||
|
||||
signal finished
|
||||
|
||||
func show_popup(achievements: Array) -> void:
|
||||
var idx := 0
|
||||
$Control/Panel/MarginContainer/VBoxContainer/Label.text = "NEW ACHIEVEMENT!" if achievements.size() == 1 else "NEW ACHIEVEMENTS!"
|
||||
for i in [%Icon, %Icon2, %Icon3, %Icon4]:
|
||||
i.hide()
|
||||
i.visible = achievements.size() > idx
|
||||
if idx == 3 and achievements.size() > 4:
|
||||
i.hide()
|
||||
%Extra.show()
|
||||
%Extra.text = "+" + str(achievements.size() - 3)
|
||||
if i.visible:
|
||||
i.region_rect = Rect2(AchievementContainer.ICON_RECTS[achievements[idx]] * 32, Vector2(32, 32))
|
||||
idx += 1
|
||||
%AchievementName.visible = achievements.size() == 1
|
||||
%AchievementName.text = AchievementContainer.ACHIEVEMENT_NAMES[achievements[0]]
|
||||
if %AchievementName.text.length() > 16:
|
||||
$AnimationPlayer.play("AppearLong")
|
||||
else:
|
||||
$AnimationPlayer.play("Appear")
|
||||
await $AnimationPlayer.animation_finished
|
||||
finished.emit()
|
1
Scripts/UI/AchievementUnlockPopup.gd.uid
Normal file
1
Scripts/UI/AchievementUnlockPopup.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dmp6qqm3q16pc
|
40
Scripts/UI/AudioAdjuster.gd
Normal file
40
Scripts/UI/AudioAdjuster.gd
Normal file
@@ -0,0 +1,40 @@
|
||||
extends Node
|
||||
|
||||
func master_changed(new_value := 0) -> void:
|
||||
AudioServer.set_bus_volume_linear(0, float(new_value) / 10)
|
||||
Settings.file.audio.master = new_value
|
||||
|
||||
func music_changed(new_value := 0) -> void:
|
||||
AudioServer.set_bus_volume_linear(1, float(new_value) / 10)
|
||||
Settings.file.audio.music = new_value
|
||||
|
||||
func sfx_changed(new_value := 0) -> void:
|
||||
AudioServer.set_bus_volume_linear(2, float(new_value) / 10)
|
||||
Settings.file.audio.sfx = new_value
|
||||
|
||||
func athletic_changed(new_value := 0) -> void:
|
||||
Settings.file.audio.extra_bgm = new_value
|
||||
|
||||
func skid_changed(new_value := 0) -> void:
|
||||
Settings.file.audio.skid_sfx = new_value
|
||||
|
||||
func extra_sfx_changed(new_value := 0) -> void:
|
||||
Settings.file.audio.extra_sfx = new_value
|
||||
|
||||
func menu_bgm_changed(new_value := 0) -> void:
|
||||
Settings.file.audio.menu_bgm = new_value
|
||||
|
||||
func blank(_hello := 0) -> void:
|
||||
pass
|
||||
|
||||
func set_value(value_name := "", value := 0) -> void:
|
||||
{
|
||||
"master": master_changed,
|
||||
"music": music_changed,
|
||||
"sfx": sfx_changed,
|
||||
"athletic_bgm": blank,
|
||||
"extra_bgm": athletic_changed,
|
||||
"skid_sfx": skid_changed,
|
||||
"extra_sfx": extra_sfx_changed,
|
||||
"menu_bgm": menu_bgm_changed
|
||||
}[value_name].call(value)
|
1
Scripts/UI/AudioAdjuster.gd.uid
Executable file
1
Scripts/UI/AudioAdjuster.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bdgvsycico544
|
65
Scripts/UI/AutoScrollContainer.gd
Normal file
65
Scripts/UI/AutoScrollContainer.gd
Normal file
@@ -0,0 +1,65 @@
|
||||
@tool
|
||||
class_name AutoScrollContainer
|
||||
extends ScrollContainer
|
||||
|
||||
var is_focused := false
|
||||
|
||||
@export_enum("Wave", "Endless") var mode := 0
|
||||
@export_enum("Horizontal", "Vertical") var direction := 0
|
||||
|
||||
var scroll_direction := "scroll_vertical"
|
||||
|
||||
var scroll := 0.0
|
||||
|
||||
@export var is_active := false
|
||||
@export var auto_connect_focus := true
|
||||
@export var auto_minimum_resize := false
|
||||
|
||||
func _ready() -> void:
|
||||
scroll_direction = "scroll_horizontal" if direction == 0 else "scroll_vertical"
|
||||
set_focused(is_active)
|
||||
if auto_connect_focus:
|
||||
owner.focus_entered.connect(set_focused.bind(true))
|
||||
owner.focus_exited.connect(set_focused.bind(false))
|
||||
if auto_minimum_resize:
|
||||
get_child(0).resized.connect(update_sizing)
|
||||
|
||||
func set_focused(enabled := false) -> void:
|
||||
is_focused = enabled
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
wave(delta)
|
||||
|
||||
func update_sizing() -> void:
|
||||
custom_minimum_size.x = clamp(get_child(0).size.x, 0, 100)
|
||||
|
||||
var scroll_pos := 0.0
|
||||
var scroll_speed := 16.0 # pixels per second
|
||||
var move_direction := 1
|
||||
|
||||
func wave(delta: float) -> void:
|
||||
if not is_focused:
|
||||
scroll_pos = 0
|
||||
set_deferred(scroll_direction, -1)
|
||||
|
||||
var total_range := 0.0
|
||||
if direction == 0:
|
||||
total_range = get_child(0).size.x - size.x
|
||||
else:
|
||||
total_range = (get_child(0).size.y) - (size.y + 8)
|
||||
|
||||
if total_range <= 0:
|
||||
return
|
||||
if scroll_pos > total_range + 16 or scroll_pos <= -16:
|
||||
move_direction *= -1
|
||||
|
||||
scroll_pos += scroll_speed * move_direction * delta
|
||||
if direction == 0:
|
||||
scroll_horizontal = scroll_pos
|
||||
else:
|
||||
scroll_vertical = scroll_pos
|
||||
|
||||
func endless(delta: float) -> void:
|
||||
scroll = wrap(scroll - delta, 0, 1)
|
||||
var amount = lerpf(0.0, get_child(0).size.x - size.x, scroll)
|
||||
scroll_horizontal = amount
|
1
Scripts/UI/AutoScrollContainer.gd.uid
Normal file
1
Scripts/UI/AutoScrollContainer.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d63p6qr5a748
|
120
Scripts/UI/BooRaceMenu.gd
Normal file
120
Scripts/UI/BooRaceMenu.gd
Normal file
@@ -0,0 +1,120 @@
|
||||
extends Node
|
||||
|
||||
static var selected_index := 0
|
||||
|
||||
var active := true
|
||||
var boo_index := 0
|
||||
|
||||
const levels := {
|
||||
"SMB1": SMB1_LEVELS,
|
||||
"SMBLL": SMBLL_LEVELS,
|
||||
"SMBS": SMBS_LEVELS
|
||||
}
|
||||
|
||||
const SMB1_LEVELS := [
|
||||
"res://Scenes/Levels/SMB1/YouVSBoo/Boo1-1.tscn",
|
||||
"res://Scenes/Levels/SMB1/YouVSBoo/Boo1-2.tscn",
|
||||
"res://Scenes/Levels/SMB1/YouVSBoo/Boo1-3.tscn",
|
||||
"res://Scenes/Levels/SMB1/YouVSBoo/Boo1-4.tscn",
|
||||
"res://Scenes/Levels/SMB1/YouVSBoo/Boo2-1.tscn",
|
||||
"res://Scenes/Levels/SMB1/YouVSBoo/Boo2-2.tscn",
|
||||
"res://Scenes/Levels/SMB1/YouVSBoo/Boo2-3.tscn",
|
||||
"res://Scenes/Levels/SMB1/YouVSBoo/Boo2-4.tscn"
|
||||
]
|
||||
|
||||
const SMBLL_LEVELS := [
|
||||
"res://Scenes/Levels/SMBLL/YouVSBoo/Boo1-1.tscn",
|
||||
"res://Scenes/Levels/SMBLL/YouVSBoo/Boo1-2.tscn",
|
||||
"res://Scenes/Levels/SMBLL/YouVSBoo/Boo1-3.tscn",
|
||||
"res://Scenes/Levels/SMBLL/YouVSBoo/Boo1-4.tscn",
|
||||
"res://Scenes/Levels/SMBLL/YouVSBoo/Boo2-1.tscn",
|
||||
"res://Scenes/Levels/SMBLL/YouVSBoo/Boo2-2.tscn",
|
||||
"res://Scenes/Levels/SMBLL/YouVSBoo/Boo2-3.tscn",
|
||||
"res://Scenes/Levels/SMBLL/YouVSBoo/Boo2-4.tscn",
|
||||
]
|
||||
|
||||
const SMBS_LEVELS := [
|
||||
"res://Scenes/Levels/SMBS/YouVsBoo/Boo1-1.tscn",
|
||||
"res://Scenes/Levels/SMBS/YouVsBoo/Boo1-2.tscn",
|
||||
"res://Scenes/Levels/SMBS/YouVsBoo/Boo1-3.tscn",
|
||||
"res://Scenes/Levels/SMBS/YouVsBoo/Boo1-4.tscn",
|
||||
"res://Scenes/Levels/SMBS/YouVsBoo/Boo2-1.tscn",
|
||||
"res://Scenes/Levels/SMBS/YouVsBoo/Boo2-2.tscn",
|
||||
"res://Scenes/Levels/SMBS/YouVsBoo/Boo2-3.tscn",
|
||||
"res://Scenes/Levels/SMBS/YouVsBoo/Boo2-4.tscn"
|
||||
]
|
||||
|
||||
func _ready() -> void:
|
||||
AudioManager.stop_all_music()
|
||||
Global.player_power_states = "0000"
|
||||
Global.get_node("GameHUD").hide()
|
||||
boo_index = BooRaceHandler.boo_colour
|
||||
Global.current_game_mode = Global.GameMode.BOO_RACE
|
||||
Global.reset_values()
|
||||
LevelPersistance.reset_states()
|
||||
Level.first_load = true
|
||||
Level.can_set_time = true
|
||||
setup_visuals()
|
||||
%LevelLabels.get_child(BooRaceHandler.current_level_id).grab_focus()
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Global.get_node("GameHUD").show()
|
||||
|
||||
func setup_visuals() -> void:
|
||||
for i in %LevelLabels.get_child_count():
|
||||
if i >= 1:
|
||||
var level_unlocked = int(BooRaceHandler.cleared_boo_levels[i - 1]) > 0
|
||||
%LevelLabels.get_child(i).modulate = Color.WHITE if level_unlocked else Color.DIM_GRAY
|
||||
%LevelLabels.get_child(i).get_node("Control/Sprite2D").visible = level_unlocked
|
||||
if int(BooRaceHandler.cleared_boo_levels[i]) > 0:
|
||||
%LevelLabels.get_child(i).get_node("Control/Sprite2D").frame = clamp(int(BooRaceHandler.cleared_boo_levels[i]), 0, 4)
|
||||
%LevelLabels.get_child(i).get_node("Control/Sprite2D").modulate = Color.DIM_GRAY if int(BooRaceHandler.cleared_boo_levels[i]) >= 5 else Color.WHITE
|
||||
for i in %Boos.get_children():
|
||||
if i is Node2D:
|
||||
i.visible = $BooSelect.selected_boo == int(i.name)
|
||||
i.modulate = Color.BLACK if int(BooRaceHandler.cleared_boo_levels[selected_index]) < int(i.name) else Color.WHITE
|
||||
if int(BooRaceHandler.cleared_boo_levels[selected_index]) > int(i.name):
|
||||
i.modulate = Color.DIM_GRAY
|
||||
i.play("Lose" if int(BooRaceHandler.cleared_boo_levels[selected_index]) > int(i.name) else "Idle")
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
handle_input()
|
||||
$BooSelect.lvl_idx = selected_index
|
||||
|
||||
func open() -> void:
|
||||
active = true
|
||||
|
||||
func set_current_level_idx(new_idx := 0) -> void:
|
||||
selected_index = new_idx
|
||||
update_pb()
|
||||
|
||||
func update_pb() -> void:
|
||||
var pb_string := "--:--:--"
|
||||
if BooRaceHandler.best_times[selected_index] >= 0:
|
||||
pb_string = SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(BooRaceHandler.best_times[selected_index]))
|
||||
%PB.text = "PB: " + pb_string
|
||||
|
||||
func handle_input() -> void:
|
||||
if active == false:
|
||||
return
|
||||
if Input.is_action_just_pressed("ui_back"):
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
level_selected()
|
||||
|
||||
func regrab_focus() -> void:
|
||||
%LevelLabels.get_child(selected_index).grab_focus()
|
||||
|
||||
func level_selected() -> void:
|
||||
if selected_index > 0:
|
||||
if int(BooRaceHandler.cleared_boo_levels[selected_index - 1]) <= 0 and not Global.debug_mode:
|
||||
AudioManager.play_global_sfx("bump")
|
||||
return
|
||||
active = false
|
||||
Global.reset_values()
|
||||
Global.clear_saved_values()
|
||||
ResourceSetter.cache.clear()
|
||||
ResourceSetterNew.cache.clear()
|
||||
$BooSelect.open()
|
||||
await $CharacterSelect.selected
|
||||
Global.transition_to_scene(levels[Global.current_campaign][selected_index])
|
1
Scripts/UI/BooRaceMenu.gd.uid
Executable file
1
Scripts/UI/BooRaceMenu.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://b3ol0iyjcb5n1
|
64
Scripts/UI/BooSelect.gd
Normal file
64
Scripts/UI/BooSelect.gd
Normal file
@@ -0,0 +1,64 @@
|
||||
extends Control
|
||||
@onready var cursor: Label = %Cursor
|
||||
|
||||
var selected_boo := 0
|
||||
|
||||
var active := false
|
||||
|
||||
var lvl_idx := 0
|
||||
|
||||
signal boo_selected
|
||||
signal cancelled
|
||||
|
||||
signal boo_changed
|
||||
|
||||
@onready var boos := [%Boo1, %Boo2, %Boo3, %Boo4, %Boo5]
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active:
|
||||
handle_input()
|
||||
BooRaceHandler.boo_colour = selected_boo
|
||||
for i in boos:
|
||||
i.get_node("Cursor").visible = selected_boo == i.get_index()
|
||||
|
||||
func open() -> void:
|
||||
grab_focus()
|
||||
selected_boo = int(BooRaceHandler.cleared_boo_levels[lvl_idx])
|
||||
update_visuals()
|
||||
show()
|
||||
await get_tree().process_frame
|
||||
active = true
|
||||
|
||||
func update_visuals() -> void:
|
||||
var idx := 0
|
||||
for i in boos:
|
||||
i.modulate = Color.WHITE if (int(BooRaceHandler.cleared_boo_levels[lvl_idx]) >= idx or Global.debug_mode) else Color.DIM_GRAY
|
||||
idx += 1
|
||||
|
||||
func handle_input() -> void:
|
||||
var old_colour = selected_boo
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected_boo -= 1
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
selected_boo += 1
|
||||
selected_boo = clamp(selected_boo, 0, 4)
|
||||
BooRaceHandler.boo_colour = selected_boo
|
||||
if old_colour != selected_boo:
|
||||
print(selected_boo)
|
||||
boo_changed.emit()
|
||||
if Input.is_action_just_pressed("ui_back"):
|
||||
cancelled.emit()
|
||||
close()
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
if int(BooRaceHandler.cleared_boo_levels[lvl_idx]) < selected_boo and not Global.debug_mode:
|
||||
AudioManager.play_sfx("bump")
|
||||
else:
|
||||
select_world()
|
||||
|
||||
func select_world() -> void:
|
||||
boo_selected.emit()
|
||||
close()
|
||||
|
||||
func close() -> void:
|
||||
active = false
|
||||
hide()
|
1
Scripts/UI/BooSelect.gd.uid
Executable file
1
Scripts/UI/BooSelect.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://di5w6db0ha2oa
|
101
Scripts/UI/CampaignSelect.gd
Normal file
101
Scripts/UI/CampaignSelect.gd
Normal file
@@ -0,0 +1,101 @@
|
||||
extends Control
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
signal selected
|
||||
signal cancelled
|
||||
var active := false
|
||||
|
||||
@export var campaign_icons: Array[Texture2D] = []
|
||||
|
||||
var old_campaign := ""
|
||||
|
||||
@export var campaign := ["SMB1", "SMBLL", "SMBS", "SMBANN", "Custom"]
|
||||
|
||||
func _ready() -> void:
|
||||
get_starting_position()
|
||||
handle_visuals()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active:
|
||||
handle_input()
|
||||
handle_visuals()
|
||||
|
||||
func handle_visuals() -> void:
|
||||
%Left.texture = campaign_icons[wrap(selected_index - 1, 0, campaign_icons.size())]
|
||||
%Right.texture = campaign_icons[wrap(selected_index + 1, 0, campaign_icons.size())]
|
||||
%Middle.texture = campaign_icons[selected_index]
|
||||
%BarLabel.text = generate_text()
|
||||
for i in %CampaignNames.get_child_count():
|
||||
%CampaignNames.get_child(i).visible = selected_index == i
|
||||
|
||||
func generate_text() -> String:
|
||||
var string := ""
|
||||
string += "◄"
|
||||
for i in 5:
|
||||
if i == selected_index:
|
||||
string += "┼"
|
||||
else:
|
||||
string += "-"
|
||||
string += "►"
|
||||
return string
|
||||
|
||||
func open() -> void:
|
||||
old_campaign = Global.current_campaign
|
||||
Global.current_game_mode = Global.GameMode.NONE
|
||||
get_starting_position()
|
||||
handle_visuals()
|
||||
show()
|
||||
await get_tree().process_frame
|
||||
active = true
|
||||
await selected
|
||||
hide()
|
||||
|
||||
func get_starting_position() -> void:
|
||||
if CustomLevelMenu.has_entered or selected_index == 4:
|
||||
selected_index = 4
|
||||
else:
|
||||
selected_index = campaign.find(Global.current_campaign)
|
||||
|
||||
func handle_input() -> void:
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected_index -= 1
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
selected_index += 1
|
||||
selected_index = wrap(selected_index, 0, campaign.size())
|
||||
Global.current_campaign = campaign[selected_index]
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
select()
|
||||
elif Input.is_action_just_pressed("ui_back"):
|
||||
close()
|
||||
Global.current_campaign = old_campaign
|
||||
cancelled.emit()
|
||||
return
|
||||
|
||||
func select() -> void:
|
||||
CustomLevelMenu.has_entered = false
|
||||
if selected_index == 4:
|
||||
Global.current_campaign = "SMB1"
|
||||
Global.transition_to_scene("res://Scenes/Levels/CustomLevelMenu.tscn")
|
||||
return
|
||||
active = false
|
||||
Settings.file.game.campaign = Global.current_campaign
|
||||
SaveManager.apply_save(SaveManager.load_save(campaign[selected_index]))
|
||||
if Global.current_campaign != "SMBANN":
|
||||
SpeedrunHandler.load_best_times()
|
||||
Settings.save_settings()
|
||||
selected.emit()
|
||||
hide()
|
||||
if old_campaign != Global.current_campaign:
|
||||
Global.freeze_screen()
|
||||
ResourceSetter.cache.clear()
|
||||
ResourceSetterNew.cache.clear()
|
||||
Global.level_theme_changed.emit()
|
||||
for i in 2:
|
||||
await get_tree().process_frame
|
||||
Global.close_freeze()
|
||||
|
||||
func close() -> void:
|
||||
CustomLevelMenu.has_entered = false
|
||||
active = false
|
||||
hide()
|
1
Scripts/UI/CampaignSelect.gd.uid
Executable file
1
Scripts/UI/CampaignSelect.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cae4v54jq1gs3
|
118
Scripts/UI/ChallengeModeResults.gd
Normal file
118
Scripts/UI/ChallengeModeResults.gd
Normal file
@@ -0,0 +1,118 @@
|
||||
extends Node
|
||||
|
||||
|
||||
@export var can_exit := false
|
||||
|
||||
func _enter_tree() -> void:
|
||||
Global.get_node("GameHUD").hide()
|
||||
|
||||
var coin_medal := true
|
||||
var score_medal := false
|
||||
var yoshi_medal := false
|
||||
|
||||
var exiting := false
|
||||
|
||||
func _ready() -> void:
|
||||
var your_results = tr("CHALLENGE_DIALOGUE_RESULTS").split(" ")
|
||||
$SpeechBubble/Your.text = your_results[0]
|
||||
$SpeechBubble/Your/Results.text = your_results[1]
|
||||
coin_medal = int(ChallengeModeHandler.red_coins_collected[Global.world_num - 1][Global.level_num - 1]) & 0b011111 == 0b011111
|
||||
score_medal = ChallengeModeHandler.top_challenge_scores[Global.world_num -1][Global.level_num - 1] >= ChallengeModeHandler.CHALLENGE_TARGETS[Global.current_campaign][Global.world_num -1][Global.level_num -1]
|
||||
yoshi_medal = ChallengeModeHandler.is_coin_collected(ChallengeModeHandler.CoinValues.YOSHI_EGG, ChallengeModeHandler.red_coins_collected[Global.world_num - 1][Global.level_num - 1])
|
||||
setup_results()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if can_exit and Input.is_action_just_pressed("jump_0"):
|
||||
can_exit = false
|
||||
exiting = true
|
||||
save_results()
|
||||
$Music.stop()
|
||||
$Music.stream = preload("res://Assets/Audio/BGM/ChallengeEnd.mp3")
|
||||
$Music.play()
|
||||
await $Music.finished
|
||||
open_menu()
|
||||
Engine.time_scale = 5 if Input.is_action_pressed("jump_0") and can_exit == false and exiting == false else 1
|
||||
|
||||
func open_menu() -> void:
|
||||
$CanvasLayer/PauseMenu.open()
|
||||
|
||||
func save_results() -> void:
|
||||
var index := 0
|
||||
ChallengeModeHandler.red_coins_collected[Global.world_num - 1][Global.level_num - 1] = int(ChallengeModeHandler.red_coins_collected[Global.world_num - 1][Global.level_num - 1]) | ChallengeModeHandler.current_run_red_coins_collected
|
||||
if Global.score >= ChallengeModeHandler.top_challenge_scores[Global.world_num - 1][Global.level_num - 1]:
|
||||
ChallengeModeHandler.top_challenge_scores[Global.world_num - 1][Global.level_num - 1] = Global.score
|
||||
ChallengeModeHandler.new().check_for_achievement()
|
||||
SaveManager.write_save()
|
||||
|
||||
func retry_level() -> void:
|
||||
Global.player_power_states = "0000"
|
||||
ChallengeModeHandler.current_run_red_coins_collected = ChallengeModeHandler.red_coins_collected[Global.world_num - 1][Global.level_num - 1]
|
||||
Global.score = 0
|
||||
LevelTransition.level_to_transition_to = Level.get_scene_string(Global.world_num, Global.level_num)
|
||||
Global.transition_to_scene("res://Scenes/Levels/LevelTransition.tscn")
|
||||
|
||||
func go_to_title_screen() -> void:
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Global.get_node("GameHUD").show()
|
||||
Engine.time_scale = 1
|
||||
|
||||
func setup_results() -> void:
|
||||
$Sprite2D3/RedCoins.visible = coin_medal
|
||||
$Sprite2D3/Score.visible = score_medal
|
||||
$Sprite2D3/YoshiEgg.visible = yoshi_medal
|
||||
$SpeechBubble/Score/ScoreLabel.text = str(Global.score)
|
||||
var idx = 0
|
||||
for i in $Sprite2D/Sprite2D3/Coins.get_children():
|
||||
if ChallengeModeHandler.is_coin_collected(idx, ChallengeModeHandler.red_coins_collected[Global.world_num - 1][Global.level_num - 1]):
|
||||
i.frame = 1
|
||||
else:
|
||||
i.frame = 0
|
||||
idx += 1
|
||||
idx = 0
|
||||
for i in $SpeechBubble/Coins/Node2D.get_children():
|
||||
i.frame = int(ChallengeModeHandler.is_coin_collected(idx))
|
||||
idx += 1
|
||||
$Sprite2D/Sprite2D3/ScoreText/Target.text = "/ " + str(ChallengeModeHandler.CHALLENGE_TARGETS[Global.current_campaign][Global.world_num - 1][Global.level_num - 1])
|
||||
$WorldLevel.text = str(Global.world_num) + "-" + str(Global.level_num)
|
||||
$Yoshi.play(["Green", "Yellow", "Red", "Blue"][Global.level_num - 1])
|
||||
|
||||
func update_coins_display() -> void:
|
||||
var idx := 0
|
||||
for i in $Sprite2D/Sprite2D3/Coins.get_children():
|
||||
if ChallengeModeHandler.is_coin_collected(idx):
|
||||
i.frame = 1
|
||||
idx += 1
|
||||
|
||||
func update_score() -> void:
|
||||
$Sprite2D/Sprite2D3/ScoreText.text = str(Global.score)
|
||||
|
||||
func give_red_coin_medal() -> void:
|
||||
const mask = (1 << ChallengeModeHandler.CoinValues.R_COIN_1) | (1 << ChallengeModeHandler.CoinValues.R_COIN_2) | (1 << ChallengeModeHandler.CoinValues.R_COIN_3) | (1 << ChallengeModeHandler.CoinValues.R_COIN_4) | (1 << ChallengeModeHandler.CoinValues.R_COIN_5)
|
||||
var valid := (ChallengeModeHandler.current_run_red_coins_collected & mask) == mask
|
||||
if valid and not coin_medal:
|
||||
do_medal_give_animation($Sprite2D3/RedCoins)
|
||||
|
||||
func give_score_medal() -> void:
|
||||
if Global.score >= ChallengeModeHandler.CHALLENGE_TARGETS[Global.current_campaign][Global.world_num - 1][Global.level_num - 1] and not score_medal:
|
||||
do_medal_give_animation($Sprite2D3/Score)
|
||||
|
||||
func give_yoshi_medal() -> void:
|
||||
if ChallengeModeHandler.is_coin_collected(ChallengeModeHandler.CoinValues.YOSHI_EGG):
|
||||
$SmokeParticle.play()
|
||||
$Yoshi/AudioStreamPlayer2D.play()
|
||||
$Yoshi.show()
|
||||
if yoshi_medal == false:
|
||||
await get_tree().create_timer(0.5, false).timeout
|
||||
do_medal_give_animation($Sprite2D3/YoshiEgg)
|
||||
|
||||
func do_medal_give_animation(medal: Node) -> void:
|
||||
$AudioStreamPlayer2.play()
|
||||
get_tree().paused = true
|
||||
for i in 4:
|
||||
medal.hide()
|
||||
await get_tree().create_timer(0.1, true).timeout
|
||||
medal.show()
|
||||
await get_tree().create_timer(0.1, true).timeout
|
||||
get_tree().paused = false
|
1
Scripts/UI/ChallengeModeResults.gd.uid
Executable file
1
Scripts/UI/ChallengeModeResults.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bqhrq7sabq811
|
108
Scripts/UI/CharacterSelect.gd
Normal file
108
Scripts/UI/CharacterSelect.gd
Normal file
@@ -0,0 +1,108 @@
|
||||
extends Control
|
||||
@onready var cursor: TextureRect = %Cursor
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
signal selected
|
||||
signal cancelled
|
||||
var active := false
|
||||
|
||||
var player_id := 0
|
||||
|
||||
var character_sprite_jsons := [
|
||||
"res://Assets/Sprites/Players/Mario/Small.json",
|
||||
"res://Assets/Sprites/Players/Luigi/Small.json",
|
||||
"res://Assets/Sprites/Players/Toad/Small.json",
|
||||
"res://Assets/Sprites/Players/Toadette/Small.json"
|
||||
]
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active:
|
||||
handle_input()
|
||||
|
||||
func _ready() -> void:
|
||||
update_sprites()
|
||||
|
||||
func get_custom_characters() -> void:
|
||||
Player.CHARACTERS = ["Mario", "Luigi", "Toad", "Toadette"]
|
||||
Player.CHARACTER_NAMES = ["CHAR_MARIO", "CHAR_LUIGI", "CHAR_TOAD", "CHAR_TOADETTE"]
|
||||
AudioManager.character_sfx_map.clear()
|
||||
|
||||
var idx := 0
|
||||
for i in Player.CHARACTERS:
|
||||
var path = ResourceSetter.get_pure_resource_path("res://Assets/Sprites/Players/" + i + "/CharacterInfo.json")
|
||||
print(path)
|
||||
if FileAccess.file_exists(path):
|
||||
var json = JSON.parse_string(FileAccess.open(path, FileAccess.READ).get_as_text())
|
||||
Player.CHARACTER_NAMES[idx] = json.name
|
||||
path = ResourceSetter.get_pure_resource_path("res://Assets/Sprites/Players/" + i + "/CharacterColour.json")
|
||||
if FileAccess.file_exists(path):
|
||||
Player.CHARACTER_COLOURS[idx] = load(path)
|
||||
idx += 1
|
||||
print(Player.CHARACTER_NAMES)
|
||||
|
||||
DirAccess.make_dir_recursive_absolute("user://custom_characters")
|
||||
for i in DirAccess.get_directories_at("user://custom_characters"):
|
||||
if FileAccess.file_exists("user://custom_characters/" + i + "/CharacterInfo.json"):
|
||||
var char_path = "user://custom_characters/" + i + "/"
|
||||
var json = JSON.parse_string(FileAccess.open(char_path + "CharacterInfo.json", FileAccess.READ).get_as_text())
|
||||
Player.CHARACTERS.append(i)
|
||||
Player.CHARACTER_NAMES.append(json.name)
|
||||
if FileAccess.file_exists(char_path + "CharacterColour.json"):
|
||||
Player.CHARACTER_COLOURS.append(load(char_path + "CharacterColour.json"))
|
||||
if FileAccess.file_exists(char_path + "LifeIcon.json"):
|
||||
GameHUD.character_icons.append(load(char_path + "LifeIcon.json"))
|
||||
if FileAccess.file_exists(char_path + "ColourPalette.json"):
|
||||
Player.CHARACTER_PALETTES.append(load(char_path + "ColourPalette.json"))
|
||||
if FileAccess.file_exists(char_path + "SFX.json"):
|
||||
AudioManager.character_sfx_map[i] = JSON.parse_string(FileAccess.open(char_path + "SFX.json", FileAccess.READ).get_as_text())
|
||||
|
||||
func open() -> void:
|
||||
get_custom_characters()
|
||||
show()
|
||||
grab_focus()
|
||||
selected_index = int(Global.player_characters[player_id])
|
||||
update_sprites()
|
||||
await get_tree().physics_frame
|
||||
active = true
|
||||
|
||||
func handle_input() -> void:
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected_index = wrap(selected_index - 1, 0, Player.CHARACTERS.size())
|
||||
update_sprites()
|
||||
elif Input.is_action_just_pressed("ui_right"):
|
||||
selected_index = wrap(selected_index + 1, 0, Player.CHARACTERS.size())
|
||||
update_sprites()
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
Global.player_characters[player_id] = str(selected_index)
|
||||
var characters := Global.player_characters
|
||||
for i in characters:
|
||||
if int(i) > 3:
|
||||
characters = "0000"
|
||||
Settings.file.game.characters = characters
|
||||
Settings.save_settings()
|
||||
selected.emit()
|
||||
close()
|
||||
elif Input.is_action_just_pressed("ui_back"):
|
||||
close()
|
||||
cancelled.emit()
|
||||
|
||||
func update_sprites() -> void:
|
||||
%Left.force_character = Player.CHARACTERS[wrap(selected_index - 1, 0, Player.CHARACTERS.size())]
|
||||
%Selected.force_character = Player.CHARACTERS[wrap(selected_index, 0, Player.CHARACTERS.size())]
|
||||
%Right.force_character = Player.CHARACTERS[wrap(selected_index + 1, 0, Player.CHARACTERS.size())]
|
||||
for i in [%Left, %Selected, %Right]:
|
||||
i.update()
|
||||
i.play("Pose" if i == %Selected else "FaceForward")
|
||||
%PlayerColourTexture.resource_json = Player.CHARACTER_COLOURS[selected_index]
|
||||
%CharacterName.text = tr(Player.CHARACTER_NAMES[selected_index])
|
||||
$Panel/MarginContainer/VBoxContainer/CharacterName/TextShadowColourChanger/ColourPaletteSampler.texture = %ColourPaletteSampler.texture
|
||||
$Panel/MarginContainer/VBoxContainer/CharacterName/TextShadowColourChanger.handle_shadow_colours()
|
||||
func select() -> void:
|
||||
selected.emit()
|
||||
hide()
|
||||
active = false
|
||||
|
||||
func close() -> void:
|
||||
active = false
|
||||
hide()
|
1
Scripts/UI/CharacterSelect.gd.uid
Executable file
1
Scripts/UI/CharacterSelect.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://d0f4edmfxs2pu
|
50
Scripts/UI/Credits.gd
Normal file
50
Scripts/UI/Credits.gd
Normal file
@@ -0,0 +1,50 @@
|
||||
class_name CreditsLevel
|
||||
extends Level
|
||||
|
||||
func _enter_tree() -> void:
|
||||
pass
|
||||
|
||||
static var go_to_title_screen := true
|
||||
|
||||
func _ready() -> void:
|
||||
for i in $Labels.get_children():
|
||||
i.hide()
|
||||
AudioManager.stop_all_music()
|
||||
Global.get_node("GameHUD").hide()
|
||||
await get_tree().create_timer(1, false).timeout
|
||||
do_sequence()
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Global.get_node("GameHUD").show()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
if $Skip.visible:
|
||||
exit()
|
||||
else:
|
||||
$Skip.show()
|
||||
await get_tree().create_timer(2, false).timeout
|
||||
$Skip.hide()
|
||||
|
||||
func exit() -> void:
|
||||
if go_to_title_screen:
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
else:
|
||||
LevelTransition.level_to_transition_to = Level.get_scene_string(9, 1)
|
||||
Global.world_num = 8
|
||||
Global.world_num = 4
|
||||
update_next_level_info()
|
||||
transition_to_next_level()
|
||||
|
||||
func do_sequence() -> void:
|
||||
$Music.play()
|
||||
for i in $Labels.get_children():
|
||||
i.show()
|
||||
if i.has_meta("time"):
|
||||
await get_tree().create_timer(i.get_meta("time"), false).timeout
|
||||
else:
|
||||
await get_tree().create_timer(4, false).timeout
|
||||
i.hide()
|
||||
await get_tree().create_timer(0.5, false).timeout
|
||||
await get_tree().create_timer(5, false).timeout
|
||||
exit()
|
1
Scripts/UI/Credits.gd.uid
Normal file
1
Scripts/UI/Credits.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b3stoe2kp1nv1
|
72
Scripts/UI/CustomLevelContainer.gd
Normal file
72
Scripts/UI/CustomLevelContainer.gd
Normal file
@@ -0,0 +1,72 @@
|
||||
class_name CustomLevelContainer
|
||||
extends Control
|
||||
|
||||
signal selected(this: CustomLevelContainer)
|
||||
|
||||
var level_name := ""
|
||||
var level_author := ""
|
||||
var level_desc := ""
|
||||
var level_theme := "Overworld"
|
||||
var level_time := 0
|
||||
var game_style := "SMBLL"
|
||||
var difficulty := 0
|
||||
var file_path := ""
|
||||
|
||||
var idx := 0
|
||||
|
||||
const CAMPAIGN_RECTS := {
|
||||
"SMB1": Rect2(0, 0, 42, 16),
|
||||
"SMBLL": Rect2(0, 16, 42, 16),
|
||||
"SMBS": Rect2(0, 32, 42, 16),
|
||||
"SMBANN": Rect2(0, 0, 42, 16)
|
||||
}
|
||||
|
||||
const ICON_TEXTURES := [
|
||||
preload("uid://chtjq1vr0rpso"),
|
||||
preload("uid://cn8bcncfmdikq")
|
||||
]
|
||||
|
||||
const THEME_RECTS := {
|
||||
"Overworld": Rect2(0, 0, 32, 32),
|
||||
"Underground": Rect2(32, 0, 32, 32),
|
||||
"Desert": Rect2(64, 0, 32, 32),
|
||||
"Snow": Rect2(96, 0, 32, 32),
|
||||
"Jungle": Rect2(128, 0, 32, 32),
|
||||
"Beach": Rect2(0, 32, 32, 32),
|
||||
"Garden": Rect2(32, 32, 32, 32),
|
||||
"Mountain": Rect2(64, 32, 32, 32),
|
||||
"Skyland": Rect2(96, 32, 32, 32),
|
||||
"Autumn": Rect2(128, 32, 32, 32),
|
||||
"Pipeland": Rect2(0, 64, 32, 32),
|
||||
"Space": Rect2(32, 64, 32, 32),
|
||||
"Underwater": Rect2(64, 64, 32, 32),
|
||||
"Volcano": Rect2(96, 64, 32, 32),
|
||||
"GhostHouse": Rect2(128, 64, 32, 32),
|
||||
"Castle": Rect2(0, 96, 32, 32),
|
||||
"CastleWater": Rect2(32, 96, 32, 32),
|
||||
"Mystery": Rect2(96, 96, 32, 32),
|
||||
"Airship": Rect2(128, 96, 32, 32),
|
||||
"Bonus": Rect2(0, 128, 32, 32)
|
||||
}
|
||||
|
||||
func _ready() -> void:
|
||||
set_process(false)
|
||||
update_visuals()
|
||||
|
||||
func update_visuals() -> void:
|
||||
%LevelIcon.texture = ResourceSetter.get_resource(ICON_TEXTURES[level_time])
|
||||
%LevelIcon.region_rect = THEME_RECTS[level_theme]
|
||||
|
||||
%LevelName.text = level_name if level_name != "" else "(Unnamed Level)"
|
||||
%LevelAuthor.text = "By " + (level_author if level_author != "" else "Player")
|
||||
|
||||
%CampaignIcon.region_rect = CAMPAIGN_RECTS[game_style]
|
||||
|
||||
var idx := 0
|
||||
for i in %DifficultyStars.get_children():
|
||||
i.region_rect.position.x = 24 if idx > difficulty else [0, 0, 8, 8, 16][difficulty]
|
||||
idx += 1
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_accept") and visible:
|
||||
selected.emit(self)
|
1
Scripts/UI/CustomLevelContainer.gd.uid
Executable file
1
Scripts/UI/CustomLevelContainer.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://0pxa4836hn6c
|
78
Scripts/UI/CustomLevelList.gd
Normal file
78
Scripts/UI/CustomLevelList.gd
Normal file
@@ -0,0 +1,78 @@
|
||||
extends VBoxContainer
|
||||
|
||||
signal level_selected(container: CustomLevelContainer)
|
||||
|
||||
const CUSTOM_LEVEL_CONTAINER = preload("uid://dt20tjug8m6oh")
|
||||
|
||||
const CUSTOM_LEVEL_PATH := "user://custom_levels/"
|
||||
const base64_charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
signal closed
|
||||
|
||||
var containers := []
|
||||
|
||||
var selected_lvl_idx := -1
|
||||
|
||||
func open(refresh_list := true) -> void:
|
||||
show()
|
||||
if refresh_list:
|
||||
refresh()
|
||||
if selected_lvl_idx >= 0:
|
||||
%LevelContainers.get_child(selected_lvl_idx).grab_focus()
|
||||
else:
|
||||
$TopBit/Button.grab_focus()
|
||||
await get_tree().process_frame
|
||||
set_process(true)
|
||||
|
||||
func open_folder() -> void:
|
||||
OS.shell_show_in_file_manager(ProjectSettings.globalize_path(CUSTOM_LEVEL_PATH))
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_back"):
|
||||
closed.emit()
|
||||
|
||||
func close() -> void:
|
||||
hide()
|
||||
set_process(false)
|
||||
|
||||
func refresh() -> void:
|
||||
%LevelContainers.get_node("Label").show()
|
||||
for i in %LevelContainers.get_children():
|
||||
if i is CustomLevelContainer:
|
||||
i.queue_free()
|
||||
containers.clear()
|
||||
get_levels("user://custom_levels")
|
||||
get_levels("user://custom_levels/downloaded")
|
||||
|
||||
func get_levels(path := "user://custom_levels") -> void:
|
||||
DirAccess.make_dir_recursive_absolute(path)
|
||||
var idx := 0
|
||||
for i in DirAccess.get_files_at(path):
|
||||
if i.contains(".lvl") == false:
|
||||
continue
|
||||
%LevelContainers.get_node("Label").hide()
|
||||
var container = CUSTOM_LEVEL_CONTAINER.instantiate()
|
||||
var file = FileAccess.open(path + "/" + i, FileAccess.READ)
|
||||
var json = JSON.parse_string(file.get_as_text())
|
||||
file.close()
|
||||
var data = json["Levels"][0]["Data"].split("=")
|
||||
var info = json["Info"]
|
||||
container.level_name = info["Name"]
|
||||
container.level_author = info["Author"]
|
||||
container.level_desc = info["Description"]
|
||||
container.idx = idx
|
||||
container.file_path = path + "/" + i
|
||||
container.level_theme = Level.THEME_IDXS[base64_charset.find(data[0])]
|
||||
container.level_time = base64_charset.find(data[1])
|
||||
container.game_style = Global.CAMPAIGNS[base64_charset.find(data[3])]
|
||||
container.selected.connect(container_selected)
|
||||
containers.append(container)
|
||||
print(data)
|
||||
if info.has("Difficulty"):
|
||||
container.difficulty = info["Difficulty"]
|
||||
%LevelContainers.add_child(container)
|
||||
idx += 1
|
||||
|
||||
func container_selected(container: CustomLevelContainer) -> void:
|
||||
level_selected.emit(container)
|
||||
selected_lvl_idx = container.get_index()
|
1
Scripts/UI/CustomLevelList.gd.uid
Normal file
1
Scripts/UI/CustomLevelList.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cv5avutyesjxt
|
86
Scripts/UI/CustomLevelMenu.gd
Normal file
86
Scripts/UI/CustomLevelMenu.gd
Normal file
@@ -0,0 +1,86 @@
|
||||
class_name CustomLevelMenu
|
||||
extends Node
|
||||
|
||||
static var current_level_file := ""
|
||||
|
||||
static var has_entered := false
|
||||
|
||||
var selected_lvl_idx := 0
|
||||
const CUSTOM_LEVEL_PATH := "user://custom_levels/"
|
||||
const base64_charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
func _ready() -> void:
|
||||
has_entered = true
|
||||
ResourceSetterNew.cache.clear()
|
||||
ResourceSetter.cache.clear()
|
||||
Global.get_node("GameHUD").hide()
|
||||
Checkpoint.passed = false
|
||||
Global.world_num = 1
|
||||
Global.level_num = 1
|
||||
Checkpoint.sublevel_id = 0
|
||||
Global.current_campaign = "SMB1"
|
||||
AudioManager.stop_all_music()
|
||||
Global.second_quest = false
|
||||
%LevelList.open(true)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Global.get_node("GameHUD").show()
|
||||
|
||||
func new_level() -> void:
|
||||
Global.current_game_mode = Global.GameMode.LEVEL_EDITOR
|
||||
LevelEditor.load_play = false
|
||||
LevelEditor.level_name = ""
|
||||
LevelEditor.level_author = ""
|
||||
LevelEditor.level_desc = ""
|
||||
LevelEditor.difficulty = 0
|
||||
LevelEditor.level_file = LevelEditor.BLANK_FILE.duplicate(true)
|
||||
Global.transition_to_scene("res://Scenes/Levels/LevelEditor.tscn")
|
||||
|
||||
func back_to_title_screen() -> void:
|
||||
if Global.transitioning_scene:
|
||||
await Global.transition_finished
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
|
||||
func edit_level() -> void:
|
||||
Global.current_game_mode = Global.GameMode.LEVEL_EDITOR
|
||||
LevelEditor.load_play = false
|
||||
Global.transition_to_scene("res://Scenes/Levels/LevelEditor.tscn")
|
||||
|
||||
func play_level() -> void:
|
||||
Global.current_game_mode = Global.GameMode.CUSTOM_LEVEL
|
||||
Settings.file.difficulty.inf_lives = 1
|
||||
LevelEditor.load_play = true
|
||||
$CharacterSelect.open()
|
||||
await $CharacterSelect.selected
|
||||
LevelTransition.level_to_transition_to = ("res://Scenes/Levels/LevelEditor.tscn")
|
||||
Global.transition_to_scene("res://Scenes/Levels/LevelTransition.tscn")
|
||||
|
||||
|
||||
func delete_level() -> void:
|
||||
DirAccess.remove_absolute(current_level_file)
|
||||
go_back_to_list()
|
||||
%LevelList.refresh()
|
||||
if %LevelList.containers.is_empty() == false:
|
||||
%LevelList.containers[0].grab_focus()
|
||||
else:
|
||||
$BG/Border/Levels/VBoxContainer/LevelList/TopBit/Button.grab_focus()
|
||||
|
||||
func go_back_to_list() -> void:
|
||||
$BG/Border/Levels/VBoxContainer/LevelList.show()
|
||||
%LevelInfo.hide()
|
||||
|
||||
func open_lss_browser() -> void:
|
||||
$BG/Border/Levels/VBoxContainer/LevelList.hide()
|
||||
%LSSBrowser.open()
|
||||
|
||||
func show_lss_level_info(container: OnlineLevelContainer) -> void:
|
||||
for i in ["level_name", "level_author", "level_theme", "level_id", "thumbnail_url"]:
|
||||
%SelectedOnlineLevel.set(i, container.get(i))
|
||||
%SelectedOnlineLevel.setup_visuals()
|
||||
LevelEditor.level_name = container.level_name
|
||||
LevelEditor.level_author = container.level_author
|
||||
%LSSDescription.text = "Fetching Description..."
|
||||
$BG/Border/Levels/VBoxContainer/LSSBrowser.hide()
|
||||
%LSSLevelInfo.show()
|
||||
await get_tree().physics_frame
|
||||
%Download.grab_focus()
|
1
Scripts/UI/CustomLevelMenu.gd.uid
Executable file
1
Scripts/UI/CustomLevelMenu.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bxu6kcun4m6uo
|
68
Scripts/UI/DataDeletionNode.gd
Normal file
68
Scripts/UI/DataDeletionNode.gd
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
extends HBoxContainer
|
||||
|
||||
@export var title := ""
|
||||
@export var selected := false
|
||||
@export var campaigns: Array[String]
|
||||
@export var extra_confirm := false
|
||||
signal deleted(campaign: String)
|
||||
var confirming := false
|
||||
|
||||
var confirm_2 := false
|
||||
|
||||
|
||||
var selected_index := 0:
|
||||
set(value):
|
||||
selected_index = value
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if selected:
|
||||
handle_inputs()
|
||||
else:
|
||||
confirm_2 = false
|
||||
confirming = false
|
||||
$Cursor.modulate.a = int(selected)
|
||||
for i in [$AutoScrollContainer, %AutoScrollContainer2]:
|
||||
i.is_focused = selected
|
||||
%Title.text = tr(title) + ":"
|
||||
if not confirming:
|
||||
%Value.modulate = Color.WHITE
|
||||
%Value.text = tr(str(campaigns[selected_index]))
|
||||
else:
|
||||
if confirm_2:
|
||||
%Value.text = tr("DELETION_CONFIRM_2")
|
||||
else:
|
||||
%Value.text = tr("DELETION_CONFIRM")
|
||||
%Value.modulate = Color.RED
|
||||
%LeftArrow.modulate.a = int(selected and selected_index > 0)
|
||||
%RightArrow.modulate.a = int(selected and selected_index < campaigns.size() - 1)
|
||||
|
||||
func set_selected(active := false) -> void:
|
||||
selected = active
|
||||
|
||||
func handle_inputs() -> void:
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
confirming = false
|
||||
confirm_2 = false
|
||||
selected_index -= 1
|
||||
if Settings.file.audio.extra_sfx == 1:
|
||||
AudioManager.play_global_sfx("menu_move")
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
confirming = false
|
||||
confirm_2 = false
|
||||
selected_index += 1
|
||||
if Settings.file.audio.extra_sfx == 1:
|
||||
AudioManager.play_global_sfx("menu_move")
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
if confirming or confirm_2:
|
||||
if extra_confirm and confirm_2 == false:
|
||||
confirm_2 = true
|
||||
else:
|
||||
AudioManager.play_global_sfx("cannon")
|
||||
confirm_2 = false
|
||||
confirming = false
|
||||
deleted.emit(campaigns[selected_index])
|
||||
else:
|
||||
confirm_2 = false
|
||||
confirming = true
|
||||
selected_index = clamp(selected_index, 0, campaigns.size() - 1)
|
1
Scripts/UI/DataDeletionNode.gd.uid
Normal file
1
Scripts/UI/DataDeletionNode.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cmxqrlwdt6tst
|
48
Scripts/UI/DiscoResults.gd
Normal file
48
Scripts/UI/DiscoResults.gd
Normal file
@@ -0,0 +1,48 @@
|
||||
extends Control
|
||||
|
||||
const RANK_MESSAGES = ["F_RANK_MESSAGE", "D_RANK_MESSAGE", "C_RANK_MESSAGE", "B_RANK_MESSAGE", "A_RANK_MESSAGE", "S_RANK_MESSAGE", "P_RANK_MESSAGE"]
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
|
||||
func open() -> void:
|
||||
setup_visuals()
|
||||
show()
|
||||
set_focus(true)
|
||||
await get_tree().physics_frame
|
||||
[%Continue, %Retry, %LevelSelect, %ReturnMenu][selected_index].grab_focus()
|
||||
|
||||
func setup_visuals() -> void:
|
||||
%Score.text = str(Global.score)
|
||||
var rank_idx = DiscoLevel.RANK_IDs.find(DiscoLevel.current_rank)
|
||||
%Medal.region_rect.position.x = 16 * (rank_idx + 1)
|
||||
%RankMessage.text = RANK_MESSAGES[rank_idx]
|
||||
%RankMessage.modulate = GameHUD.RANK_COLOURS[DiscoLevel.current_rank]
|
||||
|
||||
func close() -> void:
|
||||
hide()
|
||||
|
||||
func set_focus(enabled := false) -> void:
|
||||
for i in [%Continue, %Retry, %LevelSelect, %ReturnMenu]:
|
||||
i.focus_mode = 0 if enabled == false else 2
|
||||
|
||||
func continue_to_next_level() -> void:
|
||||
Global.current_level.transition_to_next_level()
|
||||
Global.disco_level_continued.emit()
|
||||
close()
|
||||
|
||||
func set_index(idx := 0) -> void:
|
||||
selected_index = idx
|
||||
|
||||
func restart_level() -> void:
|
||||
LevelTransition.level_to_transition_to = Level.get_scene_string(Global.world_num, Global.level_num)
|
||||
Global.reset_values()
|
||||
DiscoLevel.reset_values()
|
||||
Global.transition_to_scene("res://Scenes/Levels/LevelTransition.tscn")
|
||||
close()
|
||||
|
||||
func go_to_menu() -> void:
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
close()
|
1
Scripts/UI/DiscoResults.gd.uid
Normal file
1
Scripts/UI/DiscoResults.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b5u7ht2124lfs
|
24
Scripts/UI/EditorPopupMenu.gd
Normal file
24
Scripts/UI/EditorPopupMenu.gd
Normal file
@@ -0,0 +1,24 @@
|
||||
extends Control
|
||||
|
||||
var active := false
|
||||
|
||||
signal closed
|
||||
|
||||
func _ready() -> void:
|
||||
set_process(false)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_cancel") or Input.is_action_just_pressed("editor_open_menu"):
|
||||
close()
|
||||
|
||||
func open() -> void:
|
||||
set_process(true)
|
||||
show()
|
||||
active = true
|
||||
|
||||
func close() -> void:
|
||||
set_process(false)
|
||||
hide()
|
||||
active = false
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
closed.emit()
|
1
Scripts/UI/EditorPopupMenu.gd.uid
Normal file
1
Scripts/UI/EditorPopupMenu.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d1gpliy41d31n
|
4
Scripts/UI/GameBananaLevelDisplay.gd
Executable file
4
Scripts/UI/GameBananaLevelDisplay.gd
Executable file
@@ -0,0 +1,4 @@
|
||||
extends Control
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
1
Scripts/UI/GameBananaLevelDisplay.gd.uid
Executable file
1
Scripts/UI/GameBananaLevelDisplay.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://b32rbwuvfqn6x
|
42
Scripts/UI/GhostSelect.gd
Normal file
42
Scripts/UI/GhostSelect.gd
Normal file
@@ -0,0 +1,42 @@
|
||||
extends Control
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
var active := false
|
||||
|
||||
signal selected
|
||||
signal cancelled
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
|
||||
func open() -> void:
|
||||
show()
|
||||
await get_tree().physics_frame
|
||||
active = true
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active == false: return
|
||||
if Input.is_action_just_pressed("ui_down"):
|
||||
selected_index += 1
|
||||
elif Input.is_action_just_pressed("ui_up"):
|
||||
selected_index -= 1
|
||||
selected_index = clamp(selected_index, 0, 1)
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
selected.emit()
|
||||
SpeedrunHandler.ghost_enabled = bool(selected_index)
|
||||
close()
|
||||
elif Input.is_action_just_pressed("ui_back"):
|
||||
close()
|
||||
cancelled.emit()
|
||||
var idx := 0
|
||||
for i in [%NoGhost, %Ghost]:
|
||||
i.get_node("Cursor").modulate.a = int(selected_index == idx)
|
||||
idx += 1
|
||||
|
||||
func load_ghost() -> void:
|
||||
SpeedrunHandler.load_best_marathon()
|
||||
|
||||
func close() -> void :
|
||||
hide()
|
||||
active = false
|
1
Scripts/UI/GhostSelect.gd.uid
Normal file
1
Scripts/UI/GhostSelect.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://btp7od8thj3mk
|
25
Scripts/UI/LevelEditorTab.gd
Normal file
25
Scripts/UI/LevelEditorTab.gd
Normal file
@@ -0,0 +1,25 @@
|
||||
extends MarginContainer
|
||||
|
||||
static var current_tab = null
|
||||
|
||||
@export var icon: Texture = null
|
||||
@export var title := ""
|
||||
@export var linked_control: Control = null
|
||||
@export var first_pick := false
|
||||
|
||||
func _ready() -> void:
|
||||
if first_pick:
|
||||
tab_clicked()
|
||||
$HBoxContainer/Label.text = title
|
||||
$HBoxContainer/TextureRect.texture = icon
|
||||
update()
|
||||
|
||||
func update() -> void:
|
||||
print(current_tab == self)
|
||||
$HBoxContainer/Label.visible = current_tab == self
|
||||
$Selected.visible = current_tab == self
|
||||
linked_control.visible = current_tab == self
|
||||
|
||||
func tab_clicked() -> void:
|
||||
current_tab = self
|
||||
get_tree().call_group("EditorTabs", "update")
|
1
Scripts/UI/LevelEditorTab.gd.uid
Normal file
1
Scripts/UI/LevelEditorTab.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://kvlxhhkgtuk2
|
55
Scripts/UI/LevelInfo.gd
Normal file
55
Scripts/UI/LevelInfo.gd
Normal file
@@ -0,0 +1,55 @@
|
||||
extends VBoxContainer
|
||||
|
||||
signal closed
|
||||
|
||||
var file_path := ""
|
||||
|
||||
var active := false
|
||||
|
||||
func _ready() -> void:
|
||||
set_process(false)
|
||||
|
||||
signal level_play
|
||||
signal level_edit
|
||||
|
||||
func open(container: CustomLevelContainer = null) -> void:
|
||||
if container != null:
|
||||
for i in ["level_name", "level_author", "level_theme", "game_style", "level_time", "difficulty"]:
|
||||
%SelectedLevel.set(i, container.get(i))
|
||||
%SelectedLevel.update_visuals()
|
||||
LevelEditor.level_name = container.level_name
|
||||
CustomLevelMenu.current_level_file = container.file_path
|
||||
LevelEditor.level_author = container.level_author
|
||||
file_path = container.file_path
|
||||
LevelEditor.level_desc = container.level_desc
|
||||
%Description.text = container.level_desc
|
||||
show()
|
||||
await get_tree().physics_frame
|
||||
active = true
|
||||
set_process(true)
|
||||
%Play.grab_focus()
|
||||
|
||||
func reopen() -> void:
|
||||
show()
|
||||
await get_tree().physics_frame
|
||||
active = true
|
||||
set_process(true)
|
||||
%Play.grab_focus()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_back") and active:
|
||||
closed.emit()
|
||||
close()
|
||||
|
||||
func level_selected() -> void:
|
||||
LevelEditor.level_file = JSON.parse_string(FileAccess.open(file_path, FileAccess.READ).get_as_text())
|
||||
level_play.emit()
|
||||
active = false
|
||||
|
||||
func level_edited() -> void:
|
||||
LevelEditor.level_file = JSON.parse_string(FileAccess.open(file_path, FileAccess.READ).get_as_text())
|
||||
level_edit.emit()
|
||||
|
||||
func close() -> void:
|
||||
hide()
|
||||
set_process(false)
|
1
Scripts/UI/LevelInfo.gd.uid
Normal file
1
Scripts/UI/LevelInfo.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bsmrnnpjkhidw
|
189
Scripts/UI/LevelSelect.gd
Normal file
189
Scripts/UI/LevelSelect.gd
Normal file
@@ -0,0 +1,189 @@
|
||||
extends Control
|
||||
|
||||
var selected_level := 0
|
||||
|
||||
signal level_selected
|
||||
signal cancelled
|
||||
var active := false
|
||||
|
||||
var starting_value := -1
|
||||
|
||||
@export var has_speedrun_stuff := false
|
||||
@export var has_disco_stuff := false
|
||||
|
||||
const LEVEL_ICONS := {
|
||||
"SMB1": SMB1_ICONS,
|
||||
"SMBLL": SMBLL_ICONS,
|
||||
"SMBS": SMBS_ICONS,
|
||||
"SMBANN": SMB1_ICONS
|
||||
}
|
||||
|
||||
const SMB1_ICONS := [
|
||||
"0123",
|
||||
"0453",
|
||||
"0023",
|
||||
"0163",
|
||||
"8893",
|
||||
"8893",
|
||||
"8AB3",
|
||||
"8883"
|
||||
]
|
||||
|
||||
const SMBLL_ICONS := [
|
||||
"0123",
|
||||
"0053",
|
||||
"0423",
|
||||
"0023",
|
||||
"8193",
|
||||
"8AB3",
|
||||
"8993",
|
||||
"88D3",
|
||||
"8888",
|
||||
"0123",
|
||||
"0423",
|
||||
"0523",
|
||||
"0003"
|
||||
]
|
||||
|
||||
const SMBS_ICONS := [
|
||||
"0123",
|
||||
"0453",
|
||||
"0023",
|
||||
"0163",
|
||||
"8893",
|
||||
"8893",
|
||||
"8AB3",
|
||||
"CA13"
|
||||
]
|
||||
|
||||
const NUMBER_Y := [
|
||||
"Overworld",
|
||||
"Underground",
|
||||
"Castle",
|
||||
"Snow",
|
||||
"Space",
|
||||
"Volcano"
|
||||
]
|
||||
|
||||
func _ready() -> void:
|
||||
for i in %SlotContainer.get_children():
|
||||
i.focus_entered.connect(slot_selected.bind(i.get_index()))
|
||||
for i in [$Panel/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/SlotContainer/Slot1/Icon/RankMedal/SRankParticles, $Panel/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/SlotContainer/Slot1/Icon/RankMedal/PRankParticles, $Panel/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/SlotContainer/Slot2/Icon/RankMedal/SRankParticles, $Panel/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/SlotContainer/Slot2/Icon/RankMedal/PRankParticles, $Panel/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/SlotContainer/Slot3/Icon/RankMedal/SRankParticles, $Panel/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/SlotContainer/Slot3/Icon/RankMedal/PRankParticles, $Panel/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/SlotContainer/Slot4/Icon/RankMedal/SRankParticles, $Panel/MarginContainer/VBoxContainer/HBoxContainer/ScrollContainer/SlotContainer/Slot4/Icon/RankMedal/PRankParticles]:
|
||||
start_particle(i)
|
||||
|
||||
func start_particle(particle: GPUParticles2D) -> void:
|
||||
await get_tree().create_timer(randf_range(0, 5)).timeout
|
||||
particle.emitting = true
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active:
|
||||
handle_input()
|
||||
Global.level_num = selected_level + 1
|
||||
|
||||
func open() -> void:
|
||||
if starting_value == -1:
|
||||
starting_value = Global.level_num
|
||||
print([Global.level_num, starting_value])
|
||||
selected_level = Global.level_num - 1
|
||||
setup_visuals()
|
||||
update_pb()
|
||||
show()
|
||||
$%SlotContainer.get_child(selected_level).grab_focus()
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
active = true
|
||||
|
||||
const CHARSET := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
var visited_levels := "0000"
|
||||
|
||||
func setup_visuals() -> void:
|
||||
%MarathonBits.visible = Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE
|
||||
var idx := 0
|
||||
for i in %SlotContainer.get_children():
|
||||
if i.visible == false:
|
||||
continue
|
||||
var level_theme = Global.LEVEL_THEMES[Global.current_campaign][Global.world_num - 1]
|
||||
visited_levels = (SaveManager.visited_levels.substr((Global.world_num - 1) * 4, 4))
|
||||
var level_visited = SaveManager.visited_levels[SaveManager.get_level_idx(Global.world_num, idx + 1)] != "0" or Global.debug_mode
|
||||
var num = CHARSET.find(LEVEL_ICONS[Global.current_campaign][Global.world_num - 1][idx])
|
||||
if level_visited == false:
|
||||
num = 7
|
||||
i.get_node("ChallengeModeBits").visible = Global.current_game_mode == Global.GameMode.CHALLENGE
|
||||
if Global.current_game_mode == Global.GameMode.CHALLENGE:
|
||||
setup_challenge_mode_bits(i.get_node("ChallengeModeBits"), idx + 1)
|
||||
i.get_node("Icon").region_rect = Rect2((num % 4) * 56, (num / 4) * 32, 56, 32)
|
||||
i.get_node("Icon/Number").region_rect.position.y = clamp(NUMBER_Y.find(level_theme) * 12, 0, 9999)
|
||||
i.get_node("Icon/Number").region_rect.position.x = (idx) * 12
|
||||
i.get_node("Icon/RankMedal").visible = Global.current_campaign == "SMBANN"
|
||||
if Global.current_campaign == "SMBANN":
|
||||
i.get_node("Icon/RankMedal").frame = "ZFDCBASP".find(DiscoLevel.level_ranks[SaveManager.get_level_idx(Global.world_num, idx + 1)])
|
||||
i.get_node("Icon/RankMedal/SRankParticles").visible = i.get_node("Icon/RankMedal").frame == 6
|
||||
i.get_node("Icon/RankMedal/PRankParticles").visible = i.get_node("Icon/RankMedal").frame == 7
|
||||
idx += 1
|
||||
|
||||
func setup_challenge_mode_bits(container: HBoxContainer, level_num := 1) -> void:
|
||||
for i in [container.get_node("1"), container.get_node("2"), container.get_node("3"), container.get_node("4"), container.get_node("5"), container.get_node("6")]:
|
||||
var collected = ChallengeModeHandler.is_coin_collected(int(i.name) - 1, ChallengeModeHandler.red_coins_collected[Global.world_num - 1][level_num - 1])
|
||||
i.get_node("Full").visible = collected
|
||||
container.get_node("Score/Full").visible = ChallengeModeHandler.top_challenge_scores[Global.world_num - 1][level_num - 1] >= ChallengeModeHandler.CHALLENGE_TARGETS[Global.current_campaign][Global.world_num - 1][level_num - 1]
|
||||
|
||||
func update_pb() -> void:
|
||||
if has_speedrun_stuff == false: return
|
||||
var best_warpless_time = SpeedrunHandler.best_level_warpless_times[Global.world_num - 1][selected_level]
|
||||
print(SpeedrunHandler.best_level_warpless_times)
|
||||
var best_any_time = SpeedrunHandler.best_level_any_times.get(str(Global.world_num) + "-" + str(selected_level + 1), -1)
|
||||
%FullRunPB.text = "--:--:--" if best_warpless_time == -1 else SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(best_warpless_time))
|
||||
%WarpRunPB.text = "--:--:--" if best_any_time == -1 else SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(best_any_time))
|
||||
%Flag.visible = selected_level < 3
|
||||
%Axe.visible = selected_level >= 3
|
||||
$Panel/MarginContainer/VBoxContainer/MarathonBits/VBoxContainer/Warp.modulate = Color.WHITE if SpeedrunHandler.WARP_LEVELS[Global.current_campaign].has(str(Global.world_num) + "-" + str(selected_level + 1)) else Color(0.25, 0.25, 0.25)
|
||||
var gold_warpless_time = SpeedrunHandler.LEVEL_GOLD_WARPLESS_TIMES[Global.current_campaign][Global.world_num - 1][selected_level]
|
||||
var gold_any_time := -1.0
|
||||
if SpeedrunHandler.LEVEL_GOLD_ANY_TIMES[Global.current_campaign].has(str(Global.world_num) + "-" + str(selected_level + 1)):
|
||||
gold_any_time = SpeedrunHandler.LEVEL_GOLD_ANY_TIMES[Global.current_campaign][str(Global.world_num) + "-" + str(selected_level + 1)]
|
||||
for i in %FullRunMedals.get_children():
|
||||
var target_time = gold_warpless_time * SpeedrunHandler.MEDAL_CONVERSIONS[i.get_index()]
|
||||
i.get_node("Full").visible = best_warpless_time <= target_time and best_warpless_time > 0
|
||||
if gold_any_time != -1:
|
||||
for i in %WarpRunMedals.get_children():
|
||||
var target_time = gold_any_time * SpeedrunHandler.MEDAL_CONVERSIONS[i.get_index()]
|
||||
i.get_node("Full").visible = best_any_time <= target_time and best_any_time > 0
|
||||
else:
|
||||
for i in %WarpRunMedals.get_children():
|
||||
i.get_node("Full").hide()
|
||||
|
||||
func handle_input() -> void:
|
||||
selected_level = clamp(selected_level, 0, 3)
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
if visited_levels[selected_level] == "0" and selected_level != 0 and not Global.debug_mode:
|
||||
AudioManager.play_sfx("bump")
|
||||
else:
|
||||
select_world()
|
||||
elif Input.is_action_just_pressed("ui_back"):
|
||||
close()
|
||||
cleanup()
|
||||
cancelled.emit()
|
||||
return
|
||||
|
||||
func select_world() -> void:
|
||||
if owner is Level:
|
||||
owner.level_id = selected_level + 1
|
||||
Global.level_num = selected_level + 1
|
||||
level_selected.emit()
|
||||
close()
|
||||
|
||||
func slot_selected(idx := 0) -> void:
|
||||
selected_level = idx
|
||||
update_pb()
|
||||
|
||||
func cleanup() -> void:
|
||||
await get_tree().process_frame
|
||||
Global.level_num = starting_value
|
||||
starting_value = -1
|
||||
Global.level_num = clamp(Global.level_num, 1, 4)
|
||||
if owner is Level:
|
||||
owner.level_id = clamp(owner.level_id, 1, 8)
|
||||
|
||||
func close() -> void:
|
||||
active = false
|
||||
hide()
|
1
Scripts/UI/LevelSelect.gd.uid
Executable file
1
Scripts/UI/LevelSelect.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bupc8brk48aqi
|
102
Scripts/UI/LssBrowser.gd
Normal file
102
Scripts/UI/LssBrowser.gd
Normal file
@@ -0,0 +1,102 @@
|
||||
extends VBoxContainer
|
||||
|
||||
var page_number := 1
|
||||
@onready var http_request: HTTPRequest = $HTTPRequest
|
||||
|
||||
const LSS_URL := "https://levelsharesquare.com"
|
||||
|
||||
signal closed
|
||||
signal level_selected(container: OnlineLevelContainer)
|
||||
|
||||
var list := {}
|
||||
const ONLINE_LEVEL_CONTAINER = preload("uid://cr2pku7fjkgpo")
|
||||
|
||||
var filter = 0
|
||||
var selected_lvl_idx := -1
|
||||
var sort := -1
|
||||
|
||||
func _ready() -> void:
|
||||
set_process(false)
|
||||
|
||||
func open(refresh_list := true) -> void:
|
||||
show()
|
||||
if refresh_list:
|
||||
grab_levels()
|
||||
await get_tree().physics_frame
|
||||
if selected_lvl_idx >= 0:
|
||||
%OnlineLevelList.get_child(selected_lvl_idx).grab_focus()
|
||||
else:
|
||||
%RefreshList.grab_focus()
|
||||
set_process(true)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_back"):
|
||||
closed.emit()
|
||||
close()
|
||||
|
||||
func close() -> void:
|
||||
set_process(false)
|
||||
hide()
|
||||
|
||||
func grab_levels() -> void:
|
||||
selected_lvl_idx = -1
|
||||
%OverloadMSG.hide()
|
||||
%ErrorMSG.hide()
|
||||
http_request.cancel_request()
|
||||
for i in %OnlineLevelList.get_children():
|
||||
i.queue_free()
|
||||
$LoadingMSG.show()
|
||||
var filter_str = ["", "", "&sort=plays", "&sort=rating"][filter]
|
||||
var get_type = ["featured?", "get?", "get?", "get?"][filter]
|
||||
var page_str = "&page=" + str(page_number)
|
||||
var url = LSS_URL + "/api/levels/filter/" + get_type + "game=" + str(Global.LSS_GAME_ID) + "&authors=1" + filter_str + page_str + "&sortType=" + str(sort)
|
||||
http_request.request(url, [], HTTPClient.METHOD_GET)
|
||||
|
||||
func level_list_retrieved(result := 0, response_code := 0, headers: PackedStringArray = [], body: PackedByteArray = []) -> void:
|
||||
$LoadingMSG.hide()
|
||||
var string = body.get_string_from_utf8()
|
||||
if response_code != HTTPClient.RESPONSE_OK:
|
||||
%ErrorMSG.show()
|
||||
return
|
||||
if string == "Too many requests, please slow down!":
|
||||
%OverloadMSG.show()
|
||||
return
|
||||
var json = JSON.parse_string(string)
|
||||
list = json
|
||||
print(list)
|
||||
spawn_containers()
|
||||
%Page.values.clear()
|
||||
for i in json.numberOfPages:
|
||||
%Page.values.append(str(int(i + 1)))
|
||||
|
||||
func spawn_containers() -> void:
|
||||
$HSeparator.show()
|
||||
for i in list.levels:
|
||||
var container = ONLINE_LEVEL_CONTAINER.instantiate()
|
||||
container.level_name = i.name
|
||||
if i.has("status"):
|
||||
container.featured = i.status == "Featured"
|
||||
container.level_author = i.author.username
|
||||
container.difficulty = i.difficulty
|
||||
container.level_id = i._id
|
||||
container.level_selected.connect(show_info)
|
||||
if i.has("thumbnail"):
|
||||
if i.thumbnail != null:
|
||||
container.thumbnail_url = i.thumbnail
|
||||
%OnlineLevelList.add_child(container)
|
||||
|
||||
func show_info(container: OnlineLevelContainer) -> void:
|
||||
selected_lvl_idx = container.get_index()
|
||||
level_selected.emit(container)
|
||||
|
||||
func set_filter(filter_idx := 0) -> void:
|
||||
filter = filter_idx
|
||||
grab_levels()
|
||||
|
||||
func set_page(page_idx := 0) -> void:
|
||||
page_number = page_idx + 1
|
||||
grab_levels()
|
||||
|
||||
func set_order(order_idx := 0) -> void:
|
||||
sort = [-1, 1][order_idx]
|
||||
grab_levels()
|
1
Scripts/UI/LssBrowser.gd.uid
Normal file
1
Scripts/UI/LssBrowser.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://crmjepbqu408q
|
89
Scripts/UI/LssLevelInfo.gd
Normal file
89
Scripts/UI/LssLevelInfo.gd
Normal file
@@ -0,0 +1,89 @@
|
||||
extends VBoxContainer
|
||||
|
||||
signal closed
|
||||
|
||||
const LEVEL_INFO_URL := "https://levelsharesquare.com/api/levels/"
|
||||
|
||||
var level_id := ""
|
||||
|
||||
var has_downloaded := false
|
||||
|
||||
signal level_play
|
||||
|
||||
func _ready() -> void:
|
||||
set_process(false)
|
||||
|
||||
func open(container: OnlineLevelContainer) -> void:
|
||||
has_downloaded = FileAccess.file_exists("user://custom_levels/downloaded/" + container.level_id + ".lvl")
|
||||
show()
|
||||
%Download.text = "DOWNLOAD"
|
||||
if has_downloaded:
|
||||
%OnlinePlay.grab_focus()
|
||||
else:
|
||||
%Download.grab_focus()
|
||||
setup_visuals(container)
|
||||
level_id = container.level_id
|
||||
await get_tree().physics_frame
|
||||
set_process(true)
|
||||
|
||||
func setup_visuals(container: OnlineLevelContainer) -> void:
|
||||
$Panel/AutoScrollContainer.scroll_pos = 0
|
||||
$Panel/AutoScrollContainer.move_direction = -1
|
||||
%LSSDescription.text = "Fetching Description..."
|
||||
%SelectedOnlineLevel.level_name = container.level_name
|
||||
%SelectedOnlineLevel.level_author = container.level_author
|
||||
%SelectedOnlineLevel.level_id = container.level_id
|
||||
%SelectedOnlineLevel.thumbnail_url = container.thumbnail_url
|
||||
%SelectedOnlineLevel.level_thumbnail = container.level_thumbnail
|
||||
%SelectedOnlineLevel.difficulty = container.difficulty
|
||||
%SelectedOnlineLevel.setup_visuals()
|
||||
$Description.request(LEVEL_INFO_URL + container.level_id)
|
||||
%Download.visible = not has_downloaded
|
||||
%OnlinePlay.visible = has_downloaded
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_back"):
|
||||
close()
|
||||
|
||||
func close() -> void:
|
||||
hide()
|
||||
closed.emit()
|
||||
set_process(false)
|
||||
|
||||
func download_level() -> void:
|
||||
DirAccess.make_dir_recursive_absolute("user://custom_levels/downloaded")
|
||||
var url = "https://levelsharesquare.com/api/levels/" + level_id + "/code"
|
||||
print(url)
|
||||
$DownloadLevel.request(url, [], HTTPClient.METHOD_GET)
|
||||
%Download.text = "DOWNLOADING..."
|
||||
|
||||
func open_lss() -> void:
|
||||
OS.shell_open("https://levelsharesquare.com/levels/" + str(level_id))
|
||||
|
||||
func on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
var string = body.get_string_from_utf8()
|
||||
var json = JSON.parse_string(string)
|
||||
%LSSDescription.text = Global.sanitize_string(json["level"]["description"])
|
||||
|
||||
func level_downloaded(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
var string = body.get_string_from_utf8()
|
||||
var json = JSON.parse_string(string)
|
||||
var file = FileAccess.open("user://custom_levels/downloaded/" + level_id + ".lvl", FileAccess.WRITE)
|
||||
var data = null
|
||||
if json.levelData is Array:
|
||||
data = get_json_from_bytes(json.levelData)
|
||||
else:
|
||||
data = json.levelData
|
||||
file.store_string(JSON.stringify(data))
|
||||
file.close()
|
||||
%Download.hide()
|
||||
%OnlinePlay.show()
|
||||
%OnlinePlay.grab_focus()
|
||||
|
||||
func play_level() -> void:
|
||||
var file_path := "user://custom_levels/downloaded/" + level_id + ".lvl"
|
||||
LevelEditor.level_file = JSON.parse_string(FileAccess.open(file_path, FileAccess.READ).get_as_text())
|
||||
level_play.emit()
|
||||
|
||||
func get_json_from_bytes(json := []) -> String:
|
||||
return PackedByteArray(json).get_string_from_ascii()
|
1
Scripts/UI/LssLevelInfo.gd.uid
Normal file
1
Scripts/UI/LssLevelInfo.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c4x7i885h4si0
|
13
Scripts/UI/MarathonOptionHandler.gd
Executable file
13
Scripts/UI/MarathonOptionHandler.gd
Executable file
@@ -0,0 +1,13 @@
|
||||
extends Node
|
||||
func restart_level() -> void:
|
||||
Global.checkpoint_passed = false
|
||||
Level.first_load = true
|
||||
Global.speed_run_timer = 0
|
||||
Global.speed_run_timer_active = false
|
||||
Global.reset_values()
|
||||
AudioManager.main_level_music.stop()
|
||||
Global.death_load = true
|
||||
Global.current_level.reload_level()
|
||||
|
||||
func quit_to_menu() -> void:
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
1
Scripts/UI/MarathonOptionHandler.gd.uid
Executable file
1
Scripts/UI/MarathonOptionHandler.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://c6k7fcjod18yi
|
90
Scripts/UI/MarathonResults.gd
Normal file
90
Scripts/UI/MarathonResults.gd
Normal file
@@ -0,0 +1,90 @@
|
||||
extends Control
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
func setup_visuals() -> void:
|
||||
%Time.text = SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(SpeedrunHandler.timer))
|
||||
var best_time = SpeedrunHandler.best_time
|
||||
if best_time <= 0 or SpeedrunHandler.best_time > SpeedrunHandler.timer:
|
||||
best_time = SpeedrunHandler.timer
|
||||
AudioManager.stop_all_music()
|
||||
$PBSfx.play()
|
||||
%PB.text = SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(best_time))
|
||||
%NewPB.visible = SpeedrunHandler.timer < SpeedrunHandler.best_time or SpeedrunHandler.best_time <= 0
|
||||
var target_time = -1
|
||||
%LevelSelect.visible = Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE
|
||||
if Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
|
||||
if SpeedrunHandler.is_warp_run:
|
||||
target_time = SpeedrunHandler.LEVEL_GOLD_ANY_TIMES[Global.current_campaign][str(Global.world_num) + "-" + str(Global.level_num)]
|
||||
else:
|
||||
target_time = SpeedrunHandler.LEVEL_GOLD_WARPLESS_TIMES[Global.current_campaign][Global.world_num - 1][Global.level_num - 1]
|
||||
else:
|
||||
if SpeedrunHandler.is_warp_run:
|
||||
target_time = SpeedrunHandler.GOLD_ANY_TIMES[Global.current_campaign]
|
||||
else:
|
||||
target_time = SpeedrunHandler.GOLD_WARPLESS_TIMES[Global.current_campaign]
|
||||
%Target.text = SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(target_time))
|
||||
var medal_index := -1
|
||||
if SpeedrunHandler.timer < target_time:
|
||||
medal_index = 2
|
||||
elif SpeedrunHandler.timer < target_time * SpeedrunHandler.MEDAL_CONVERSIONS[1]:
|
||||
medal_index = 1
|
||||
elif SpeedrunHandler.timer < target_time * SpeedrunHandler.MEDAL_CONVERSIONS[0]:
|
||||
medal_index = 0
|
||||
%Medal.get_node("Full").visible = medal_index >= 0
|
||||
%Medal.get_node("Full").region_rect.position = Vector2(8 * medal_index, 0)
|
||||
if medal_index >= 0:
|
||||
%Time.modulate = [Color("C6691D"), Color("BCBCBC"), Color("FFB259")][medal_index]
|
||||
else:
|
||||
%Time.modulate = Color.WHITE
|
||||
|
||||
func open() -> void:
|
||||
set_focus(true)
|
||||
setup_visuals()
|
||||
show()
|
||||
return_focus()
|
||||
|
||||
func return_focus() -> void:
|
||||
await get_tree().physics_frame
|
||||
[%Restart, %LevelSelect, %Return][selected_index].grab_focus()
|
||||
|
||||
func check_for_warp() -> void:
|
||||
SpeedrunHandler.is_warp_run = false
|
||||
if SpeedrunHandler.WARP_LEVELS[Global.current_campaign].has(str(Global.world_num) + "-" + str(Global.level_num)) or Global.current_game_mode == Global.GameMode.MARATHON:
|
||||
$SpeedrunTypeSelect.open()
|
||||
else:
|
||||
restart_level()
|
||||
|
||||
func set_focus(enabled := false) -> void:
|
||||
for i in [%Restart, %LevelSelect, %Return]:
|
||||
i.focus_mode = 0 if enabled == false else 2
|
||||
|
||||
func check_for_warp_level_select_edition() -> void:
|
||||
SpeedrunHandler.is_warp_run = false
|
||||
if SpeedrunHandler.WARP_LEVELS[Global.current_campaign].has(str(Global.world_num) + "-" + str(Global.level_num)):
|
||||
$SpeedrunTypeSelectLevelSelect.open()
|
||||
else:
|
||||
restart_level()
|
||||
|
||||
func restart_level() -> void:
|
||||
var path := ""
|
||||
SpeedrunHandler.timer = 0
|
||||
Global.reset_values()
|
||||
Global.clear_saved_values()
|
||||
if Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
|
||||
path = Level.get_scene_string(Global.world_num, Global.level_num)
|
||||
else:
|
||||
Global.world_num = 1
|
||||
Global.level_num = 1
|
||||
path = Level.get_scene_string(1, 1)
|
||||
SpeedrunHandler.best_time = SpeedrunHandler.get_best_time()
|
||||
Level.start_level_path = path
|
||||
LevelTransition.level_to_transition_to = path
|
||||
Global.transition_to_scene("res://Scenes/Levels/LevelTransition.tscn")
|
||||
close()
|
||||
|
||||
func go_to_menu() -> void:
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
|
||||
func close() -> void:
|
||||
hide()
|
1
Scripts/UI/MarathonResults.gd.uid
Normal file
1
Scripts/UI/MarathonResults.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://djuvlj1dt0aay
|
54
Scripts/UI/MarathonSelect.gd
Normal file
54
Scripts/UI/MarathonSelect.gd
Normal file
@@ -0,0 +1,54 @@
|
||||
extends Control
|
||||
|
||||
signal full_run_selected
|
||||
signal level_run_selected
|
||||
signal cancelled
|
||||
|
||||
var selected := 0
|
||||
|
||||
var active := false
|
||||
|
||||
func open() -> void:
|
||||
setup_visuals()
|
||||
[$PanelContainer/VBoxContainer/HBoxContainer/Full, $PanelContainer/VBoxContainer/HBoxContainer/Level][selected].grab_focus()
|
||||
show()
|
||||
await get_tree().process_frame
|
||||
active = true
|
||||
|
||||
func close() -> void:
|
||||
hide()
|
||||
active = false
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active:
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
[full_run_selected, level_run_selected][selected].emit()
|
||||
Global.current_game_mode = [Global.GameMode.MARATHON, Global.GameMode.MARATHON_PRACTICE][selected]
|
||||
close()
|
||||
elif Input.is_action_just_pressed("ui_back"):
|
||||
close()
|
||||
cancelled.emit()
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected -= 1
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
selected += 1
|
||||
selected = clamp(selected, 0, 1)
|
||||
%MarathonName.text = ["MARATHON_FULL", "MARATHON_LEVEL"][selected]
|
||||
for i in [$PanelContainer/VBoxContainer/HBoxContainer/Full, $PanelContainer/VBoxContainer/HBoxContainer/Level]:
|
||||
i.get_node("Label").visible = selected == i.get_index()
|
||||
|
||||
func setup_visuals() -> void:
|
||||
if SpeedrunHandler.marathon_best_warpless_time <= 0:
|
||||
%FullRunPB.text = "--:--:--"
|
||||
else:
|
||||
%FullRunPB.text = SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(SpeedrunHandler.marathon_best_warpless_time))
|
||||
|
||||
if SpeedrunHandler.marathon_best_any_time <= 0:
|
||||
%WarpedRunPB.text = "--:--:--"
|
||||
else:
|
||||
%WarpedRunPB.text = SpeedrunHandler.gen_time_string(SpeedrunHandler.format_time(SpeedrunHandler.marathon_best_any_time))
|
||||
|
||||
for i in %FullMedals.get_children():
|
||||
i.get_node("Full").visible = SpeedrunHandler.marathon_best_warpless_time <= SpeedrunHandler.GOLD_WARPLESS_TIMES[Global.current_campaign] * SpeedrunHandler.MEDAL_CONVERSIONS[i.get_index()] and SpeedrunHandler.marathon_best_warpless_time > 0
|
||||
for i in %WarpedMedals.get_children():
|
||||
i.get_node("Full").visible = SpeedrunHandler.marathon_best_any_time <= SpeedrunHandler.GOLD_ANY_TIMES[Global.current_campaign] * SpeedrunHandler.MEDAL_CONVERSIONS[i.get_index()] and SpeedrunHandler.marathon_best_any_time > 0
|
1
Scripts/UI/MarathonSelect.gd.uid
Normal file
1
Scripts/UI/MarathonSelect.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bpqtkfuv6a2sx
|
65
Scripts/UI/OnlineLevelContainer.gd
Normal file
65
Scripts/UI/OnlineLevelContainer.gd
Normal file
@@ -0,0 +1,65 @@
|
||||
class_name OnlineLevelContainer
|
||||
extends Button
|
||||
|
||||
var level_name := ""
|
||||
var level_author := ""
|
||||
var level_thumbnail = null
|
||||
var level_id := ""
|
||||
var thumbnail_url := ""
|
||||
|
||||
var difficulty := "Easy"
|
||||
var featured = false
|
||||
|
||||
signal level_selected(container: OnlineLevelContainer)
|
||||
|
||||
const DIFFICULTY_TO_STAR_TRANSLATION := {
|
||||
"Easy": 0,
|
||||
"Medium": 2,
|
||||
"Hard": 3,
|
||||
"Extreme": 4
|
||||
}
|
||||
|
||||
static var cached_thumbnails := {}
|
||||
|
||||
func _ready() -> void:
|
||||
set_process(false)
|
||||
setup_visuals()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_accept") and visible:
|
||||
level_selected.emit(self)
|
||||
|
||||
func setup_visuals() -> void:
|
||||
%LevelName.text = Global.sanitize_string(level_name)
|
||||
%LevelAuthor.text = level_author
|
||||
if featured:
|
||||
self_modulate = Color.YELLOW
|
||||
var idx := 0
|
||||
print(difficulty)
|
||||
var difficulty_int = DIFFICULTY_TO_STAR_TRANSLATION[difficulty]
|
||||
for i in %DifficultyStars.get_children():
|
||||
i.region_rect.position.x = 24 if idx > difficulty_int else [0, 0, 8, 8, 16][difficulty_int]
|
||||
idx += 1
|
||||
get_thumbnail()
|
||||
|
||||
func get_thumbnail() -> void:
|
||||
if cached_thumbnails.has(level_id):
|
||||
%LevelIcon.texture = cached_thumbnails[level_id]
|
||||
$MarginContainer/HBoxContainer/HSplitContainer/LeftHalf/LevelIcon/Error.hide()
|
||||
return
|
||||
if thumbnail_url == "":
|
||||
$MarginContainer/HBoxContainer/HSplitContainer/LeftHalf/LevelIcon/Label.hide()
|
||||
$MarginContainer/HBoxContainer/HSplitContainer/LeftHalf/LevelIcon/Error.show()
|
||||
return
|
||||
$ThumbnailDownloader.request(thumbnail_url, [], HTTPClient.METHOD_GET)
|
||||
|
||||
func on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
var image = Image.new()
|
||||
if thumbnail_url.contains(".webp"):
|
||||
image.load_webp_from_buffer(body)
|
||||
elif thumbnail_url.contains(".jpeg") or thumbnail_url.contains(".jpg"):
|
||||
image.load_jpg_from_buffer(body)
|
||||
else:
|
||||
image.load_png_from_buffer(body)
|
||||
%LevelIcon.texture = ImageTexture.create_from_image(image)
|
||||
cached_thumbnails[level_id] = %LevelIcon.texture
|
1
Scripts/UI/OnlineLevelContainer.gd.uid
Normal file
1
Scripts/UI/OnlineLevelContainer.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d486p6delid2
|
39
Scripts/UI/QuestSelect.gd
Normal file
39
Scripts/UI/QuestSelect.gd
Normal file
@@ -0,0 +1,39 @@
|
||||
extends Control
|
||||
|
||||
signal selected
|
||||
signal cancelled
|
||||
var active := false
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active:
|
||||
handle_input()
|
||||
|
||||
func open() -> void:
|
||||
show()
|
||||
await get_tree().process_frame
|
||||
[%FirstQuest, %SecondQuest][int(Global.second_quest)].grab_focus()
|
||||
active = true
|
||||
|
||||
func handle_input() -> void:
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
select()
|
||||
close()
|
||||
elif Input.is_action_just_pressed("ui_back"):
|
||||
Global.second_quest = false
|
||||
close()
|
||||
cancelled.emit()
|
||||
return
|
||||
|
||||
func set_index(idx := false) -> void:
|
||||
selected_index = int(idx)
|
||||
|
||||
func select() -> void:
|
||||
Global.second_quest = bool(selected_index)
|
||||
selected.emit()
|
||||
close()
|
||||
|
||||
func close() -> void:
|
||||
active = false
|
||||
hide()
|
1
Scripts/UI/QuestSelect.gd.uid
Normal file
1
Scripts/UI/QuestSelect.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dwiic1tdol4wh
|
17
Scripts/UI/QuitDialog.gd
Normal file
17
Scripts/UI/QuitDialog.gd
Normal file
@@ -0,0 +1,17 @@
|
||||
extends Control
|
||||
|
||||
signal cancelled
|
||||
var is_active := false
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
modulate.a = lerpf(modulate.a, int(is_active), delta * 15)
|
||||
if Input.is_action_just_released("ui_back") and is_active:
|
||||
cancelled.emit()
|
||||
is_active = false
|
||||
$AnimationPlayer.stop()
|
||||
|
||||
func start() -> void:
|
||||
$AnimationPlayer.play("Animation")
|
||||
is_active = true
|
||||
await $AnimationPlayer.animation_finished
|
||||
get_tree().quit()
|
1
Scripts/UI/QuitDialog.gd.uid
Normal file
1
Scripts/UI/QuitDialog.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://fwgm5xy4jvwf
|
67
Scripts/UI/ResourcePackConfigMenu.gd
Normal file
67
Scripts/UI/ResourcePackConfigMenu.gd
Normal file
@@ -0,0 +1,67 @@
|
||||
extends Control
|
||||
|
||||
var config_json := {}
|
||||
const RESOURCE_PACK_CONFIG_OPTION_NODE = preload("uid://c5ea03ob6ncq7")
|
||||
|
||||
signal closed
|
||||
|
||||
|
||||
var selected_index := 0
|
||||
var active := false
|
||||
|
||||
var json_path := ""
|
||||
|
||||
func open() -> void:
|
||||
if active: return
|
||||
clear_options()
|
||||
spawn_options()
|
||||
show()
|
||||
await get_tree().process_frame
|
||||
%Options.active = true
|
||||
active = true
|
||||
|
||||
func clear_options() -> void:
|
||||
for i in %Options.options:
|
||||
i.queue_free()
|
||||
%Options.options.clear()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_back") and active:
|
||||
close()
|
||||
|
||||
func spawn_options() -> void:
|
||||
for i in config_json.options:
|
||||
var node = RESOURCE_PACK_CONFIG_OPTION_NODE.instantiate()
|
||||
node.config_name = i
|
||||
if config_json.options[i] is bool:
|
||||
node.values = ["SETTING_OFF", "SETTING_ON"]
|
||||
node.selected_index = int(config_json.options[i])
|
||||
node.is_bool = true
|
||||
else:
|
||||
node.values = config_json.value_keys[i]
|
||||
node.selected_index = config_json.value_keys[i].find(config_json.options[i])
|
||||
%Options.add_child(node)
|
||||
node.value_changed.connect(value_changed)
|
||||
%Options.options.append(node)
|
||||
|
||||
func value_changed(option: PackConfigOption) -> void:
|
||||
if option.is_bool:
|
||||
config_json.options[option.config_name] = bool(option.selected_index)
|
||||
else:
|
||||
config_json.options[option.config_name] = option.values[option.selected_index]
|
||||
update_json()
|
||||
|
||||
func update_json() -> void:
|
||||
var file = FileAccess.open(json_path, FileAccess.WRITE)
|
||||
file.store_string(JSON.stringify(config_json, "\t", false))
|
||||
file.close()
|
||||
|
||||
func close() -> void:
|
||||
ResourceSetter.cache.clear()
|
||||
ResourceSetterNew.cache.clear()
|
||||
Global.level_theme_changed.emit()
|
||||
closed.emit()
|
||||
clear_options()
|
||||
hide()
|
||||
%Options.active = false
|
||||
active = false
|
1
Scripts/UI/ResourcePackConfigMenu.gd.uid
Normal file
1
Scripts/UI/ResourcePackConfigMenu.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://le6t1opa0sr0
|
32
Scripts/UI/ResourcePackConfigOptionNode.gd
Normal file
32
Scripts/UI/ResourcePackConfigOptionNode.gd
Normal file
@@ -0,0 +1,32 @@
|
||||
class_name PackConfigOption
|
||||
extends HBoxContainer
|
||||
|
||||
@export var selected := false
|
||||
|
||||
var values := []
|
||||
|
||||
signal value_changed(this: PackConfigOption)
|
||||
|
||||
var config_name := ""
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
var is_bool := false
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if selected:
|
||||
handle_inputs()
|
||||
$Cursor.modulate.a = int(selected)
|
||||
$Title.text = tr(config_name) + ":"
|
||||
$Value.text = ("◄" if selected_index > 0 and selected else " ") + tr(str(values[selected_index])) + ("►" if selected_index < values.size() - 1 and selected else " ")
|
||||
|
||||
func handle_inputs() -> void:
|
||||
var old := selected_index
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected_index -= 1
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
selected_index += 1
|
||||
selected_index = clamp(selected_index, 0, values.size() - 1)
|
||||
if old != selected_index:
|
||||
value_changed.emit(self)
|
||||
|
1
Scripts/UI/ResourcePackConfigOptionNode.gd.uid
Normal file
1
Scripts/UI/ResourcePackConfigOptionNode.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cu43fwblwup82
|
72
Scripts/UI/ResourcePackContainer.gd
Normal file
72
Scripts/UI/ResourcePackContainer.gd
Normal file
@@ -0,0 +1,72 @@
|
||||
class_name ResourcePackContainer
|
||||
extends HBoxContainer
|
||||
const RESOURCE_PACK_CONFIG_MENU = preload("uid://bom2rstlk8fws")
|
||||
var pack_json := {"name": "Hello",
|
||||
"description": "Hi :"}
|
||||
var icon: Texture = null
|
||||
|
||||
var pack_name := ""
|
||||
|
||||
var loaded := false
|
||||
var selected := false
|
||||
var load_order := 0
|
||||
var config := {}
|
||||
|
||||
var config_path := ""
|
||||
|
||||
signal resource_pack_selected()
|
||||
|
||||
signal open_config(pack: ResourcePackContainer)
|
||||
|
||||
func _ready() -> void:
|
||||
setup_visuals()
|
||||
|
||||
func setup_visuals() -> void:
|
||||
%Title.text = pack_json.name.to_upper()
|
||||
%Description.text = pack_json.description.to_upper()
|
||||
%Icon.texture = icon
|
||||
%LoadedOrder.text = str(load_order)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
loaded = Settings.file.visuals.resource_packs.has(pack_name)
|
||||
%Cursor.modulate.a = int(selected)
|
||||
%LoadedOrder.visible = loaded
|
||||
%LoadedOrder.text = str(load_order + 1)
|
||||
load_order = Settings.file.visuals.resource_packs.find(pack_name)
|
||||
$ResourcePackContainer.self_modulate = Color.GREEN if loaded else Color.WHITE
|
||||
$Edit/EditLabel.visible = selected and config != {}
|
||||
for i in [%TitleScroll, %DescScroll]:
|
||||
i.is_focused = selected
|
||||
if selected:
|
||||
focus_mode = Control.FOCUS_ALL
|
||||
grab_focus()
|
||||
else:
|
||||
focus_mode = Control.FOCUS_NONE
|
||||
if Input.is_action_just_pressed("jump_0") and selected and visible:
|
||||
select()
|
||||
elif Input.is_action_just_pressed("ui_right") and selected and visible and config != {}:
|
||||
open_config_menu()
|
||||
|
||||
func open_config_menu() -> void:
|
||||
open_config.emit(self)
|
||||
|
||||
func select() -> void:
|
||||
print(ResourceSetter.cache)
|
||||
ResourceSetter.cache.clear()
|
||||
print(ResourceSetter.cache)
|
||||
ResourceSetterNew.cache.clear()
|
||||
ResourceGetter.cache.clear()
|
||||
AudioManager.current_level_theme = ""
|
||||
loaded = not loaded
|
||||
if loaded and Settings.file.visuals.resource_packs.has(pack_name) == false:
|
||||
Settings.file.visuals.resource_packs.push_front(pack_name)
|
||||
if config != {}:
|
||||
ResourceSetterNew.pack_configs[pack_name] = config
|
||||
else:
|
||||
ResourceSetterNew.pack_configs.erase(pack_name)
|
||||
Settings.file.visuals.resource_packs.erase(pack_name)
|
||||
Global.level_theme_changed.emit()
|
||||
if loaded:
|
||||
AudioManager.play_global_sfx("coin")
|
||||
else:
|
||||
AudioManager.play_global_sfx("bump")
|
1
Scripts/UI/ResourcePackContainer.gd.uid
Executable file
1
Scripts/UI/ResourcePackContainer.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cbc02k2pmw13p
|
307
Scripts/UI/RomAssetEditor.gd
Normal file
307
Scripts/UI/RomAssetEditor.gd
Normal file
@@ -0,0 +1,307 @@
|
||||
class_name AssetEditor
|
||||
extends AssetRipper
|
||||
|
||||
const CUR_SPRITE_TEXT: String = "Current Sprite:\n%s"
|
||||
|
||||
var list_index: int = 0
|
||||
var sprite_list: Array
|
||||
var cached_sprites: Dictionary[String, Dictionary]
|
||||
|
||||
var tile_atlas: Texture
|
||||
var rom_provided: bool
|
||||
|
||||
var source_path: String
|
||||
var tile_index: int = 0
|
||||
var columns: int = 4
|
||||
var sheet_size := Vector2i(16, 16)
|
||||
|
||||
var palette_base: String = "Tile"
|
||||
var palettes: Dictionary
|
||||
var tiles: Dictionary
|
||||
|
||||
@onready var rom_required: Label = %RomRequired
|
||||
@onready var file_dialog: FileDialog = %FileDialog
|
||||
|
||||
@onready var image_preview: TextureRect = %ImagePreview
|
||||
@onready var green_preview: TextureRect = %GreenPreview
|
||||
@onready var tiles_preview: TextureRect = %TilesPreview
|
||||
@onready var cur_sprite: Label = %CurSprite
|
||||
|
||||
@onready var preview: Sprite2D = %Preview
|
||||
@onready var buttons: HBoxContainer = %Buttons
|
||||
@onready var palette_override: LineEdit = %PaletteOverride
|
||||
|
||||
@onready var scroll_container: ScrollContainer = %ScrollContainer
|
||||
@onready var json_container: VBoxContainer = %JSONContainer
|
||||
@onready var json_edit: TextEdit = %JSONEdit
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Global.get_node("GameHUD").hide()
|
||||
if Global.rom_path != "":
|
||||
on_file_selected(Global.rom_path)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Global.get_node("GameHUD").show()
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if not rom_provided:
|
||||
return
|
||||
|
||||
if event is InputEventMouseMotion:
|
||||
var is_snapped = not Input.is_action_pressed("editor_cam_fast")
|
||||
var mouse_pos: Vector2 = event.position + Vector2(
|
||||
scroll_container.scroll_horizontal,
|
||||
scroll_container.scroll_vertical
|
||||
) + Vector2(-4, -8)
|
||||
preview.position = Vector2i(mouse_pos).snappedi(8 if is_snapped else 1)
|
||||
|
||||
preview.visible = true
|
||||
if preview.position.x + 8 > sheet_size.x:
|
||||
preview.visible = false
|
||||
if preview.position.y + 8 > sheet_size.y:
|
||||
preview.visible = false
|
||||
|
||||
if not preview.is_visible_in_tree(): return
|
||||
|
||||
var direction: int = 0
|
||||
if event.is_action_pressed("editor_cam_left"): direction = -1
|
||||
if event.is_action_pressed("editor_cam_right"): direction = 1
|
||||
if event.is_action_pressed("pick_tile"):
|
||||
if tiles.has(preview.position):
|
||||
tile_index = tiles[preview.position].index
|
||||
preview.flip_h = tiles[preview.position].flip_h
|
||||
preview.flip_v = tiles[preview.position].flip_v
|
||||
if Input.is_action_pressed("editor_select"):
|
||||
if tiles[preview.position].has("palette"):
|
||||
palette_override.text = tiles[preview.position]["palette"]
|
||||
else:
|
||||
palette_override.text = ""
|
||||
preview.region_rect = Rect2i(index_to_coords(tile_index, 32) * 8, Vector2i(8, 8))
|
||||
if direction != 0:
|
||||
var multiply = 1 if not Input.is_action_pressed("editor_cam_fast") else 8
|
||||
tile_index = wrapi(tile_index + direction * multiply, 0, 512)
|
||||
preview.region_rect = Rect2i(index_to_coords(tile_index, 32) * 8, Vector2i(8, 8))
|
||||
|
||||
if event.is_action_pressed("jump_0"): preview.flip_h = not preview.flip_h
|
||||
if event.is_action_pressed("run_0"): preview.flip_v = not preview.flip_v
|
||||
|
||||
var left_click: bool = event.is_action_pressed("mb_left")
|
||||
var right_click: bool = event.is_action_pressed("mb_right")
|
||||
if left_click or right_click:
|
||||
if left_click:
|
||||
tiles[preview.position] = {
|
||||
"index": tile_index,
|
||||
"flip_h": preview.flip_h,
|
||||
"flip_v": preview.flip_v
|
||||
}
|
||||
if not palette_override.text.is_empty():
|
||||
tiles[preview.position]["palette"] = palette_override.text
|
||||
else:
|
||||
tiles.erase(preview.position)
|
||||
|
||||
update_edited_image()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_pressed("editor_select") == false:
|
||||
return
|
||||
var left_click: bool = Input.is_action_pressed("mb_left")
|
||||
var right_click: bool = Input.is_action_pressed("mb_right")
|
||||
if left_click or right_click:
|
||||
if left_click:
|
||||
tiles[preview.position] = {
|
||||
"index": tile_index,
|
||||
"flip_h": preview.flip_h,
|
||||
"flip_v": preview.flip_v
|
||||
}
|
||||
if not palette_override.text.is_empty():
|
||||
tiles[preview.position]["palette"] = palette_override.text
|
||||
else:
|
||||
tiles.erase(preview.position)
|
||||
|
||||
update_edited_image()
|
||||
|
||||
func update_edited_image() -> void:
|
||||
var tiles_images: Array[Image] = get_tile_images(image_preview.texture.get_image().get_size(), tiles, palettes)
|
||||
tiles_preview.texture = ImageTexture.create_from_image(tiles_images[0])
|
||||
green_preview.texture = ImageTexture.create_from_image(tiles_images[1])
|
||||
|
||||
func get_tile_images(
|
||||
img_size: Vector2i, tile_list: Dictionary, palette_lists: Dictionary
|
||||
) -> Array[Image]:
|
||||
var image := Image.create(img_size.x, img_size.y, false, Image.FORMAT_RGBA8)
|
||||
var green_image := Image.create(img_size.x, img_size.y, false, Image.FORMAT_RGBA8)
|
||||
|
||||
for palette_name in palette_lists.keys():
|
||||
var cur_column: int = 0
|
||||
var offset := Vector2.ZERO
|
||||
|
||||
var pal_json: String = FileAccess.get_file_as_string(
|
||||
PALETTES_FOLDER % [DEFAULT_PALETTE_GROUP, palette_name])
|
||||
var pal_dict: Dictionary = JSON.parse_string(pal_json).palettes
|
||||
|
||||
for palette_id: String in palette_lists[palette_name]:
|
||||
var palette: Array = pal_dict.get(palette_id, PREVIEW_PALETTE)
|
||||
|
||||
for tile_pos: Vector2 in tile_list:
|
||||
var tile_dict: Dictionary = tile_list[tile_pos]
|
||||
var tile_palette: String = tile_dict.get("palette", palette_base)
|
||||
if tile_palette == palette_name:
|
||||
var destination: Vector2 = tile_pos + offset
|
||||
if destination.x < img_size.x and destination.y < img_size.y:
|
||||
draw_green(green_image, destination)
|
||||
draw_tile(
|
||||
false,
|
||||
image,
|
||||
tile_dict.get("index", 0),
|
||||
destination,
|
||||
palette,
|
||||
tile_dict.get("flip_h", false),
|
||||
tile_dict.get("flip_v", false)
|
||||
)
|
||||
cur_column += 1
|
||||
if cur_column >= columns:
|
||||
cur_column = 0
|
||||
offset.x = 0
|
||||
offset.y += sheet_size.y
|
||||
else:
|
||||
offset.x += sheet_size.x
|
||||
return [image, green_image]
|
||||
|
||||
func on_file_selected(path: String) -> void:
|
||||
rom = FileAccess.get_file_as_bytes(path)
|
||||
prg_rom_size = rom[4] * 16384
|
||||
chr_rom = rom.slice(16 + prg_rom_size)
|
||||
|
||||
rom_provided = true
|
||||
rom_required.hide()
|
||||
buttons.show()
|
||||
|
||||
# setup sprite atlas for placing tiles
|
||||
var atlas := Image.create(256, 256, false, Image.FORMAT_RGBA8)
|
||||
for index in range(512):
|
||||
var pos: Vector2i = index_to_coords(index, 32) * 8
|
||||
draw_tile(false, atlas, index, pos, PREVIEW_PALETTE)
|
||||
preview.texture = ImageTexture.create_from_image(atlas)
|
||||
#
|
||||
|
||||
var list_json: String = FileAccess.get_file_as_string(SPRITE_LIST_PATH)
|
||||
var list_dict: Dictionary = JSON.parse_string(list_json)
|
||||
sprite_list = list_dict.get("sprites", [])
|
||||
|
||||
cycle_list(0)
|
||||
|
||||
func load_sprite(sprite_dict: Dictionary) -> void:
|
||||
source_path = sprite_dict.source_path
|
||||
|
||||
if source_path.begins_with("res://"):
|
||||
image_preview.texture = load(source_path)
|
||||
else:
|
||||
var image := Image.load_from_file(source_path)
|
||||
image_preview.texture = ImageTexture.create_from_image(image)
|
||||
cur_sprite.text = CUR_SPRITE_TEXT % source_path.get_file()
|
||||
|
||||
columns = str_to_var(sprite_dict.get("columns", "4"))
|
||||
sheet_size = str_to_var(sprite_dict.get("sheet_size", "Vector2i(16, 16)"))
|
||||
palette_base = sprite_dict.get("palette_base", "Tile")
|
||||
|
||||
var palettes_var: Variant = str_to_var(sprite_dict.get("palettes", var_to_str(get_default_palettes())))
|
||||
if typeof(palettes_var) == TYPE_ARRAY:
|
||||
palettes = {}
|
||||
palettes[palette_base] = palettes_var
|
||||
elif typeof(palettes_var) == TYPE_DICTIONARY:
|
||||
palettes = palettes_var
|
||||
|
||||
tiles = str_to_var(sprite_dict.get("tiles", "{}"))
|
||||
|
||||
update_palettes()
|
||||
update_edited_image()
|
||||
|
||||
func save_sprite() -> void:
|
||||
var sprite_dict: Dictionary = get_as_dict()
|
||||
var destination_path: String = png_path_to_json(sprite_dict.source_path)
|
||||
|
||||
var json_string: String = JSON.stringify(sprite_dict)
|
||||
DirAccess.make_dir_recursive_absolute(destination_path.get_base_dir())
|
||||
var file: FileAccess = FileAccess.open(destination_path, FileAccess.WRITE)
|
||||
file.store_line(json_string)
|
||||
file.close()
|
||||
|
||||
# save green over original image
|
||||
var base_image: Image = image_preview.texture.get_image()
|
||||
var green_image: Image = green_preview.texture.get_image()
|
||||
for y in range(green_image.get_size().y):
|
||||
for x in range(green_image.get_size().x):
|
||||
var found_color: Color = green_image.get_pixel(x, y)
|
||||
if found_color.a > 0:
|
||||
base_image.set_pixel(x, y, found_color)
|
||||
base_image.save_png(sprite_dict.source_path)
|
||||
|
||||
func cycle_list(add_index: int) -> void:
|
||||
if add_index != 0:
|
||||
cached_sprites[sprite_list[list_index]] = get_as_dict()
|
||||
list_index = wrapi(list_index + add_index, 0, sprite_list.size())
|
||||
|
||||
if sprite_list[list_index] in cached_sprites:
|
||||
load_sprite(cached_sprites[sprite_list[list_index]])
|
||||
else:
|
||||
var json_path: String = sprite_list[list_index].replace(
|
||||
"res://Assets/Sprites/", "res://Resources/AssetRipper/Sprites/"
|
||||
).replace(".png", ".json")
|
||||
if FileAccess.file_exists(json_path):
|
||||
var json_string: String = FileAccess.get_file_as_string(json_path)
|
||||
load_sprite(JSON.parse_string(json_string))
|
||||
else:
|
||||
load_sprite({"source_path": sprite_list[list_index]})
|
||||
|
||||
func get_as_dict() -> Dictionary:
|
||||
return {
|
||||
"source_path": source_path,
|
||||
"columns": var_to_str(columns),
|
||||
"sheet_size": var_to_str(sheet_size),
|
||||
"palette_base": palette_base,
|
||||
"palettes": var_to_str(palettes),
|
||||
"tiles": var_to_str(tiles)
|
||||
}
|
||||
|
||||
func get_default_palettes() -> Dictionary:
|
||||
var pal_json: String = FileAccess.get_file_as_string(
|
||||
PALETTES_FOLDER % [DEFAULT_PALETTE_GROUP, palette_base])
|
||||
var pal_dict: Dictionary = JSON.parse_string(pal_json)
|
||||
var default_palettes: Array = pal_dict.get("palettes", []).keys()
|
||||
return {palette_base: default_palettes}
|
||||
|
||||
func update_palettes(load_textedit: bool = false) -> void:
|
||||
if load_textedit:
|
||||
var dict: Dictionary = JSON.parse_string(json_edit.text)
|
||||
columns = dict.get("columns", 1)
|
||||
var size_array: Array = dict.get("sheet_size", [16, 16])
|
||||
sheet_size = Vector2i(size_array[0], size_array[1])
|
||||
palette_base = dict.get("palette_base", "Tile")
|
||||
var pal_var: Variant = dict.get("palettes", get_default_palettes())
|
||||
if typeof(pal_var) == TYPE_ARRAY:
|
||||
palettes = {}
|
||||
palettes[palette_base] = pal_var
|
||||
elif typeof(pal_var) == TYPE_DICTIONARY:
|
||||
palettes = pal_var
|
||||
update_edited_image()
|
||||
|
||||
json_edit.text = JSON.stringify(
|
||||
{
|
||||
"columns": columns,
|
||||
"sheet_size": [sheet_size.x, sheet_size.y],
|
||||
"palette_base": palette_base,
|
||||
"palettes": palettes
|
||||
}, "\t", false)
|
||||
|
||||
func toggle_palettes_view(toggled_on: bool) -> void:
|
||||
json_container.visible = toggled_on
|
||||
scroll_container.visible = not toggled_on
|
||||
|
||||
func draw_green(
|
||||
image: Image,
|
||||
pos: Vector2i
|
||||
) -> void:
|
||||
for y in range(8):
|
||||
for x in range(8):
|
||||
image.set_pixelv(Vector2i(x, y) + pos, Color.GREEN)
|
1
Scripts/UI/RomAssetEditor.gd.uid
Normal file
1
Scripts/UI/RomAssetEditor.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bhplwsmbalqng
|
129
Scripts/UI/RomResourceGenerator.gd
Normal file
129
Scripts/UI/RomResourceGenerator.gd
Normal file
@@ -0,0 +1,129 @@
|
||||
class_name ResourceGenerator
|
||||
extends AssetRipper
|
||||
|
||||
@onready var progress_bar: ProgressBar = %ProgressBar
|
||||
@onready var error: Label = %Error
|
||||
|
||||
func _ready() -> void:
|
||||
Global.get_node("GameHUD").hide()
|
||||
|
||||
rom = FileAccess.get_file_as_bytes(Global.rom_path)
|
||||
prg_rom_size = rom[4] * 16384
|
||||
chr_rom = rom.slice(16 + prg_rom_size)
|
||||
await get_tree().create_timer(1, false).timeout
|
||||
generate_resource_pack()
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Global.get_node("GameHUD").show()
|
||||
|
||||
|
||||
|
||||
func done() -> void:
|
||||
if not Settings.file.visuals.resource_packs.has(Global.ROM_PACK_NAME):
|
||||
Settings.file.visuals.resource_packs.insert(0, Global.ROM_PACK_NAME)
|
||||
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
|
||||
func generate_resource_pack() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(Global.ROM_ASSETS_PATH)
|
||||
|
||||
var pack_json: String = FileAccess.get_file_as_string("res://Resources/AssetRipper/ResourcePack/pack_info.json")
|
||||
var pack_dict: Dictionary = JSON.parse_string(pack_json)
|
||||
pack_dict.set("version", Global.ROM_ASSETS_VERSION)
|
||||
|
||||
var pack_file := FileAccess.open(Global.ROM_ASSETS_PATH + "/pack_info.json", FileAccess.WRITE)
|
||||
pack_file.store_line(JSON.stringify(pack_dict))
|
||||
pack_file.close()
|
||||
|
||||
var list_json: String = FileAccess.get_file_as_string(SPRITE_LIST_PATH)
|
||||
var list_dict: Dictionary = JSON.parse_string(list_json)
|
||||
|
||||
var sprite_list: Array = list_dict.get("sprites", [])
|
||||
progress_bar.max_value = sprite_list.size()
|
||||
|
||||
var sprites_handled: int = 0
|
||||
for sprite_path in sprite_list:
|
||||
var json_path: String = png_path_to_json(sprite_path)
|
||||
var sprite_image: Image
|
||||
print("Running:" + sprite_path)
|
||||
if sprite_path.begins_with("res://"):
|
||||
sprite_image = load(sprite_path).get_image()
|
||||
else:
|
||||
sprite_image = Image.load_from_file(sprite_path)
|
||||
sprite_image.convert(Image.FORMAT_RGBA8)
|
||||
if FileAccess.file_exists(json_path):
|
||||
var json_string: String = FileAccess.get_file_as_string(json_path)
|
||||
var json_dict: Dictionary = JSON.parse_string(json_string)
|
||||
|
||||
paste_sprite(sprite_image, json_dict)
|
||||
|
||||
var destination_path: String = get_destination_path(sprite_path)
|
||||
if not DirAccess.dir_exists_absolute(destination_path.get_base_dir()):
|
||||
DirAccess.make_dir_recursive_absolute(destination_path.get_base_dir())
|
||||
sprite_image.save_png(destination_path)
|
||||
|
||||
sprites_handled += 1
|
||||
progress_bar.value = sprites_handled
|
||||
await get_tree().process_frame
|
||||
|
||||
if sprites_handled < sprite_list.size():
|
||||
error.show()
|
||||
## uncomment this once the initial jsons are fully setup so that the game won't
|
||||
## boot if the resource pack loading is borked
|
||||
OS.move_to_trash(Global.ROM_ASSETS_PATH)
|
||||
else:
|
||||
done()
|
||||
|
||||
func paste_sprite(sprite_image: Image, json_dict: Dictionary):
|
||||
var columns: int = str_to_var(json_dict.get("columns", "4"))
|
||||
var sheet_size: Vector2i = str_to_var(json_dict.get("sheet_size", "Vector2i(16, 16)"))
|
||||
var palette_base: String = json_dict.get("palette_base", "Tile")
|
||||
|
||||
var palette_var: Variant = str_to_var(json_dict.get("palettes", "{}"))
|
||||
var palette_lists: Dictionary
|
||||
if typeof(palette_var) == TYPE_ARRAY:
|
||||
palette_lists[palette_base] = palette_var
|
||||
elif typeof(palette_var) == TYPE_DICTIONARY:
|
||||
palette_lists = palette_var
|
||||
|
||||
var tile_list: Dictionary = str_to_var(json_dict.get("tiles", "{}"))
|
||||
var img_size: Vector2i = sprite_image.get_size()
|
||||
|
||||
for palette_name in palette_lists.keys():
|
||||
var cur_column: int = 0
|
||||
var offset := Vector2.ZERO
|
||||
|
||||
var pal_json: String = FileAccess.get_file_as_string(
|
||||
PALETTES_FOLDER % [DEFAULT_PALETTE_GROUP, palette_name])
|
||||
var pal_dict: Dictionary = JSON.parse_string(pal_json).palettes
|
||||
|
||||
for palette_id: String in palette_lists[palette_name]:
|
||||
var palette: Array = pal_dict.get(palette_id, PREVIEW_PALETTE)
|
||||
|
||||
for tile_pos: Vector2 in tile_list:
|
||||
var tile_dict: Dictionary = tile_list[tile_pos]
|
||||
var tile_palette: String = tile_dict.get("palette", palette_base)
|
||||
if tile_palette == palette_name:
|
||||
var destination: Vector2 = tile_pos + offset
|
||||
if destination.x < img_size.x and destination.y < img_size.y:
|
||||
draw_tile(
|
||||
true,
|
||||
sprite_image,
|
||||
tile_dict.get("index", 0),
|
||||
destination,
|
||||
palette,
|
||||
tile_dict.get("flip_h", false),
|
||||
tile_dict.get("flip_v", false)
|
||||
)
|
||||
|
||||
cur_column += 1
|
||||
if cur_column >= columns:
|
||||
cur_column = 0
|
||||
offset.x = 0
|
||||
offset.y += sheet_size.y
|
||||
else:
|
||||
offset.x += sheet_size.x
|
||||
|
||||
func get_destination_path(sprite_path: String) -> String:
|
||||
return sprite_path.replace("res://Assets/", Global.ROM_ASSETS_PATH + "/")
|
1
Scripts/UI/RomResourceGenerator.gd.uid
Normal file
1
Scripts/UI/RomResourceGenerator.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bwlrdsclxd15j
|
55
Scripts/UI/RomVerifier.gd
Normal file
55
Scripts/UI/RomVerifier.gd
Normal file
@@ -0,0 +1,55 @@
|
||||
class_name ROMVerifier
|
||||
extends Node
|
||||
|
||||
const VALID_HASH := "c9b34443c0414f3b91ef496d8cfee9fdd72405d673985afa11fb56732c96152b"
|
||||
|
||||
func _ready() -> void:
|
||||
Global.get_node("GameHUD").hide()
|
||||
get_window().files_dropped.connect(on_file_dropped)
|
||||
await get_tree().physics_frame
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
|
||||
|
||||
|
||||
func on_file_dropped(files: PackedStringArray) -> void:
|
||||
for i in files:
|
||||
if is_valid_rom(i):
|
||||
Global.rom_path = i
|
||||
verified()
|
||||
copy_rom(i)
|
||||
return
|
||||
error()
|
||||
|
||||
func copy_rom(file_path := "") -> void:
|
||||
DirAccess.copy_absolute(file_path, Global.ROM_PATH)
|
||||
|
||||
static func get_hash(file_path := "") -> String:
|
||||
var file_bytes = FileAccess.open(file_path, FileAccess.READ).get_buffer(40976)
|
||||
var data = file_bytes.slice(16)
|
||||
return Marshalls.raw_to_base64(data).sha256_text()
|
||||
|
||||
static func is_valid_rom(rom_path := "") -> bool:
|
||||
return get_hash(rom_path) == VALID_HASH
|
||||
|
||||
func error() -> void:
|
||||
%Error.show()
|
||||
$ErrorSFX.play()
|
||||
|
||||
func verified() -> void:
|
||||
$BGM.queue_free()
|
||||
%DefaultText.queue_free()
|
||||
%SuccessMSG.show()
|
||||
$SuccessSFX.play()
|
||||
await get_tree().create_timer(3, false).timeout
|
||||
if not Global.rom_assets_exist:
|
||||
Global.transition_to_scene("res://Scenes/Levels/RomResourceGenerator.tscn")
|
||||
else:
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Global.get_node("GameHUD").show()
|
||||
|
||||
func create_file_pointer(file_path := "") -> void:
|
||||
var pointer = FileAccess.open(Global.ROM_POINTER_PATH, FileAccess.WRITE)
|
||||
pointer.store_string(file_path)
|
||||
pointer.close()
|
1
Scripts/UI/RomVerifier.gd.uid
Normal file
1
Scripts/UI/RomVerifier.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dhxt5av5njyiv
|
45
Scripts/UI/SaveDeletionWarning.gd
Normal file
45
Scripts/UI/SaveDeletionWarning.gd
Normal file
@@ -0,0 +1,45 @@
|
||||
extends Control
|
||||
@onready var cursor: TextureRect = $Cursor
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
signal selected
|
||||
signal cancelled
|
||||
|
||||
var active := false
|
||||
|
||||
@onready var choices := [$PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/Yes, $PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/No]
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active:
|
||||
handle_input()
|
||||
cursor.global_position.x = choices[selected_index].global_position.x - 6
|
||||
|
||||
func open() -> void:
|
||||
show()
|
||||
AudioManager.play_global_sfx("pause")
|
||||
selected_index = 1
|
||||
await get_tree().process_frame
|
||||
active = true
|
||||
await selected
|
||||
hide()
|
||||
|
||||
func handle_input() -> void:
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected_index -= 1
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
selected_index += 1
|
||||
if Input.is_action_just_pressed("ui_back"):
|
||||
close()
|
||||
cancelled.emit()
|
||||
selected_index = clamp(selected_index, 0, 1)
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
select()
|
||||
|
||||
func select() -> void:
|
||||
selected.emit()
|
||||
close()
|
||||
|
||||
func close() -> void:
|
||||
active = false
|
||||
hide()
|
1
Scripts/UI/SaveDeletionWarning.gd.uid
Executable file
1
Scripts/UI/SaveDeletionWarning.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cqjtevrvgmcl0
|
147
Scripts/UI/SelectableInputOptionNode.gd
Normal file
147
Scripts/UI/SelectableInputOptionNode.gd
Normal file
@@ -0,0 +1,147 @@
|
||||
class_name SelectableInputOption
|
||||
extends HBoxContainer
|
||||
|
||||
@export var settings_category := "video"
|
||||
@export var selected := false
|
||||
|
||||
@export var action_name := ""
|
||||
@export var title := ""
|
||||
|
||||
@export_enum("Keyboard", "Controller") var type := 0
|
||||
@export var player_idx := 0
|
||||
|
||||
signal input_changed(action_name: String, input_event: InputEvent)
|
||||
|
||||
var awaiting_input := false
|
||||
|
||||
static var rebinding_input := false
|
||||
|
||||
var event_name := ""
|
||||
|
||||
var can_remap := true
|
||||
|
||||
var current_device_brand := 0
|
||||
|
||||
var input_event: InputEvent = null
|
||||
|
||||
const button_id_translation := [
|
||||
["A", "B", "~"],
|
||||
["B", "A", "&"],
|
||||
["X", "Y", "%"],
|
||||
["Y", "X", "{"],
|
||||
["Select", "-", "Share"],
|
||||
"Home",
|
||||
["Start", "+", "Options"],
|
||||
["LS Push", "LS Push", "L3"],
|
||||
["RS Push", "RS Push", "R3"],
|
||||
["LB", "L", "L1"],
|
||||
["RB", "R", "R1"],
|
||||
"DPad U",
|
||||
"DPad D",
|
||||
"DPad L",
|
||||
"DPad R"
|
||||
]
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if selected:
|
||||
handle_inputs()
|
||||
$Cursor.modulate.a = int(selected)
|
||||
$Title.text = tr(title) + ":"
|
||||
$Value.text = get_event_string(input_event) if not awaiting_input else "Press Any..."
|
||||
|
||||
func handle_inputs() -> void:
|
||||
if selected and can_remap:
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
begin_remap()
|
||||
|
||||
func begin_remap() -> void:
|
||||
$Timer.stop()
|
||||
$Timer.start()
|
||||
rebinding_input = true
|
||||
can_remap = false
|
||||
get_parent().can_input = false
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
awaiting_input = true
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if awaiting_input == false: return
|
||||
|
||||
if event.is_pressed() == false:
|
||||
return
|
||||
|
||||
if event is InputEventKey:
|
||||
if event.as_text_physical_keycode() == "Escape":
|
||||
cancel_remap()
|
||||
return
|
||||
|
||||
if type == 0 and event is InputEventKey:
|
||||
map_event_to_action(event)
|
||||
elif type == 1 and (event is InputEventJoypadButton or event is InputEventJoypadMotion):
|
||||
if event is InputEventJoypadMotion:
|
||||
event.axis_value = sign(event.axis_value)
|
||||
map_event_to_action(event)
|
||||
|
||||
func map_event_to_action(event) -> void:
|
||||
var action = action_name + "_" + str(player_idx)
|
||||
var events = InputMap.action_get_events(action).duplicate()
|
||||
events[type] = event
|
||||
InputMap.action_erase_events(action)
|
||||
for i in events:
|
||||
InputMap.action_add_event(action, i)
|
||||
input_changed.emit(action, event)
|
||||
input_event = event
|
||||
awaiting_input = false
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
rebinding_input = false
|
||||
get_parent().can_input = true
|
||||
can_remap = true
|
||||
|
||||
func get_event_string(event: InputEvent) -> String:
|
||||
var event_string := ""
|
||||
if event is InputEventKey:
|
||||
event_string = OS.get_keycode_string(event.keycode)
|
||||
elif event is InputEventJoypadButton:
|
||||
var translation = button_id_translation[event.button_index]
|
||||
if translation is Array:
|
||||
translation = translation[current_device_brand]
|
||||
event_string = translation
|
||||
elif event is InputEventJoypadMotion:
|
||||
var stick = "LS"
|
||||
var direction = "Left"
|
||||
if event.axis == JOY_AXIS_TRIGGER_LEFT:
|
||||
return ["LT", "ZL", "L2"][current_device_brand]
|
||||
elif event.axis == JOY_AXIS_TRIGGER_RIGHT:
|
||||
return ["RT", "ZR", "R2"][current_device_brand]
|
||||
|
||||
if event.axis == JOY_AXIS_RIGHT_X or event.axis == JOY_AXIS_RIGHT_Y:
|
||||
stick = "RS"
|
||||
if (event.axis == JOY_AXIS_LEFT_X or event.axis == JOY_AXIS_RIGHT_X):
|
||||
if event.axis_value < 0:
|
||||
direction = "Left"
|
||||
else:
|
||||
direction = "Right"
|
||||
elif (event.axis == JOY_AXIS_LEFT_Y or event.axis == JOY_AXIS_RIGHT_Y):
|
||||
if event.axis_value < 0:
|
||||
direction = "Up"
|
||||
else:
|
||||
direction = "Down"
|
||||
event_string = stick + " " + direction
|
||||
return event_string
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is not InputEventJoypadButton and event is not InputEventJoypadMotion:
|
||||
return
|
||||
var device_name = Input.get_joy_name(event.device)
|
||||
if device_name.to_upper().contains("NINTENDO") or device_name.to_upper().contains("SWITCH") or device_name.to_upper().contains("WII"):
|
||||
current_device_brand = 1
|
||||
elif device_name.to_upper().contains("PS") or device_name.to_upper().contains("PLAYSTATION"):
|
||||
current_device_brand = 2
|
||||
else:
|
||||
current_device_brand = 0
|
||||
|
||||
func cancel_remap() -> void:
|
||||
awaiting_input = false
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
rebinding_input = false
|
||||
get_parent().can_input = true
|
||||
can_remap = true
|
1
Scripts/UI/SelectableInputOptionNode.gd.uid
Executable file
1
Scripts/UI/SelectableInputOptionNode.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://w6t6q5vfow5p
|
10
Scripts/UI/SelectableLabel.gd
Normal file
10
Scripts/UI/SelectableLabel.gd
Normal file
@@ -0,0 +1,10 @@
|
||||
extends Label
|
||||
|
||||
signal pressed
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
pressed.emit()
|
||||
|
||||
func toggle_process(enabled := false) -> void:
|
||||
set_process(enabled)
|
1
Scripts/UI/SelectableLabel.gd.uid
Normal file
1
Scripts/UI/SelectableLabel.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c8xv30jxw6uum
|
22
Scripts/UI/SelectableOptionButton.gd
Normal file
22
Scripts/UI/SelectableOptionButton.gd
Normal file
@@ -0,0 +1,22 @@
|
||||
extends HBoxContainer
|
||||
|
||||
@export var title := ""
|
||||
@export var selected := false
|
||||
|
||||
signal button_pressed
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
@export var press_sfx := "beep"
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if selected:
|
||||
handle_inputs()
|
||||
$Cursor.modulate.a = int(selected)
|
||||
$Title.text = tr(title)
|
||||
|
||||
func handle_inputs() -> void:
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
button_pressed.emit()
|
||||
if press_sfx != "":
|
||||
AudioManager.play_global_sfx(press_sfx)
|
1
Scripts/UI/SelectableOptionButton.gd.uid
Executable file
1
Scripts/UI/SelectableOptionButton.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://b8vx7oox0ca8p
|
30
Scripts/UI/SelectableOptionLabel.gd
Normal file
30
Scripts/UI/SelectableOptionLabel.gd
Normal file
@@ -0,0 +1,30 @@
|
||||
extends HBoxContainer
|
||||
|
||||
var selected := false
|
||||
@export var title := ""
|
||||
var selected_index := 0
|
||||
@export var values: Array[String] = []
|
||||
@export var add_colon := true
|
||||
|
||||
signal value_changed(new_index: int)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if selected:
|
||||
handle_inputs()
|
||||
$Title/Cursor.visible = (selected)
|
||||
$Title.text = tr(title) + (":" if add_colon else "")
|
||||
$Value.text = ("◄" if selected_index > 0 and selected else " ") + tr(str(values[selected_index])) + ("►" if selected_index < values.size() - 1 and selected else " ")
|
||||
|
||||
func set_selected(active := false) -> void:
|
||||
selected = active
|
||||
|
||||
func handle_inputs() -> void:
|
||||
var old := selected_index
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected_index -= 1
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
selected_index += 1
|
||||
selected_index = clamp(selected_index, 0, values.size() - 1)
|
||||
if old != selected_index:
|
||||
value_changed.emit(selected_index)
|
||||
|
1
Scripts/UI/SelectableOptionLabel.gd.uid
Normal file
1
Scripts/UI/SelectableOptionLabel.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d0tcqr0x101j
|
55
Scripts/UI/SelectableOptionNode.gd
Normal file
55
Scripts/UI/SelectableOptionNode.gd
Normal file
@@ -0,0 +1,55 @@
|
||||
extends HBoxContainer
|
||||
|
||||
@export var option_key := ""
|
||||
@export var title := ""
|
||||
@export var value_descs: Array[String] = []
|
||||
@export var values := []
|
||||
@export var settings_category := "video"
|
||||
@export var selected := false
|
||||
|
||||
signal value_changed(new_value)
|
||||
|
||||
var selected_index := 0:
|
||||
set(value):
|
||||
selected_index = value
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
update_starting_values()
|
||||
|
||||
func update_starting_values() -> void:
|
||||
if Settings.file.has(settings_category):
|
||||
if Settings.file[settings_category].has(option_key):
|
||||
if Settings.file[settings_category][option_key] is String:
|
||||
selected_index = values.find(Settings.file[settings_category][option_key])
|
||||
else:
|
||||
selected_index = Settings.file[settings_category][option_key]
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if selected:
|
||||
handle_inputs()
|
||||
$Cursor.modulate.a = int(selected)
|
||||
for i in [$AutoScrollContainer, %AutoScrollContainer2]:
|
||||
i.is_focused = selected
|
||||
%Title.text = tr(title) + ":"
|
||||
%Value.text = tr(str(values[selected_index]))
|
||||
%LeftArrow.modulate.a = int(selected and selected_index > 0)
|
||||
%RightArrow.modulate.a = int(selected and selected_index < values.size() - 1)
|
||||
|
||||
func set_selected(active := false) -> void:
|
||||
selected = active
|
||||
|
||||
func handle_inputs() -> void:
|
||||
var old := selected_index
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected_index -= 1
|
||||
if Settings.file.audio.extra_sfx == 1:
|
||||
AudioManager.play_global_sfx("menu_move")
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
selected_index += 1
|
||||
if Settings.file.audio.extra_sfx == 1:
|
||||
AudioManager.play_global_sfx("menu_move")
|
||||
selected_index = clamp(selected_index, 0, values.size() - 1)
|
||||
if old != selected_index:
|
||||
value_changed.emit(selected_index)
|
||||
|
1
Scripts/UI/SelectableOptionNode.gd.uid
Executable file
1
Scripts/UI/SelectableOptionNode.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://vm4py6u87p6d
|
52
Scripts/UI/SelectableSliderNode.gd
Normal file
52
Scripts/UI/SelectableSliderNode.gd
Normal file
@@ -0,0 +1,52 @@
|
||||
extends HBoxContainer
|
||||
|
||||
@export var option_key := ""
|
||||
@export var title := ""
|
||||
|
||||
@export var selected := false
|
||||
|
||||
signal value_changed(new_value: int)
|
||||
|
||||
var selected_index := 5
|
||||
var value := 0.5
|
||||
@onready var sfx: AudioStreamPlayer = $SFX
|
||||
|
||||
func _ready() -> void:
|
||||
update_starting_values()
|
||||
|
||||
func update_starting_values() -> void:
|
||||
selected_index = Settings.file.audio[option_key]
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if selected:
|
||||
handle_inputs()
|
||||
$Cursor.modulate.a = int(selected)
|
||||
$Value.text = generate_text()
|
||||
%Title.text = tr(title) + ":"
|
||||
$AutoScrollContainer.is_focused = selected
|
||||
|
||||
func generate_text() -> String:
|
||||
var string := ""
|
||||
string += "◄" if selected and selected_index > 0 else " "
|
||||
string += "├"
|
||||
for i in 11:
|
||||
if i == selected_index:
|
||||
string += "┼"
|
||||
else:
|
||||
string += "-"
|
||||
string += "┤"
|
||||
string += "►" if selected and selected_index < 10 else " "
|
||||
return string
|
||||
|
||||
func handle_inputs() -> void:
|
||||
var old := selected_index
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
selected_index -= 1
|
||||
sfx.play()
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
selected_index += 1
|
||||
sfx.play()
|
||||
selected_index = clamp(selected_index, 0, 10)
|
||||
if old != selected_index:
|
||||
value_changed.emit(selected_index)
|
||||
|
1
Scripts/UI/SelectableSliderNode.gd.uid
Executable file
1
Scripts/UI/SelectableSliderNode.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cufb1mxff7b8f
|
50
Scripts/UI/SettingsCategory.gd
Normal file
50
Scripts/UI/SettingsCategory.gd
Normal file
@@ -0,0 +1,50 @@
|
||||
extends VBoxContainer
|
||||
|
||||
@export var category_name := "Hi"
|
||||
@export var options: Array[Control] = []
|
||||
|
||||
var selected_index := -1
|
||||
|
||||
@export var minimum_idx := -1
|
||||
|
||||
@export var active := true
|
||||
|
||||
@export var description_node: Control = null
|
||||
@export var scroll_container: ScrollContainer = null
|
||||
@export var scroll_step := 8
|
||||
|
||||
var can_input := true
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
visible = active
|
||||
if active and can_input:
|
||||
handle_input()
|
||||
var idx := 0
|
||||
for i in options:
|
||||
if i != null:
|
||||
i.selected = selected_index == idx and active and can_input
|
||||
idx += 1
|
||||
if description_node != null and selected_index >= 0 and options[selected_index] != null:
|
||||
description_node.text = options[selected_index].value_descs[options[selected_index].selected_index]
|
||||
if not active:
|
||||
selected_index = minimum_idx
|
||||
|
||||
func handle_input() -> void:
|
||||
if Input.is_action_just_pressed("ui_down"):
|
||||
selected_index += 1
|
||||
if Settings.file.audio.extra_sfx == 1:
|
||||
AudioManager.play_global_sfx("menu_move")
|
||||
if Input.is_action_just_pressed("ui_up"):
|
||||
selected_index -= 1
|
||||
if Settings.file.audio.extra_sfx == 1:
|
||||
AudioManager.play_global_sfx("menu_move")
|
||||
if scroll_container != null:
|
||||
scroll_container.scroll_vertical = float(lerpf(0.0, scroll_container.get_v_scroll_bar().max_value, inverse_lerp(0.0, options.size() - 1, selected_index - 2)))
|
||||
selected_index = clamp(selected_index, minimum_idx, options.size() - 1)
|
||||
|
||||
func auto_get_options() -> void:
|
||||
options.clear()
|
||||
selected_index = 0
|
||||
for i in get_children():
|
||||
if i is HBoxContainer:
|
||||
options.append(i)
|
1
Scripts/UI/SettingsCategory.gd.uid
Executable file
1
Scripts/UI/SettingsCategory.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://dcmjifllvi3qd
|
87
Scripts/UI/SettingsMenu.gd
Normal file
87
Scripts/UI/SettingsMenu.gd
Normal file
@@ -0,0 +1,87 @@
|
||||
extends Control
|
||||
|
||||
@onready var current_container: Control = $PanelContainer/MarginContainer/VBoxContainer/Video
|
||||
|
||||
@export var containers: Array[Control]
|
||||
@export var disabled_containers: Array[Control]
|
||||
|
||||
var category_select_active := false
|
||||
var category_index := 0
|
||||
|
||||
signal closed
|
||||
|
||||
var can_move := true
|
||||
|
||||
var active = false
|
||||
|
||||
signal opened
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
category_select_active = current_container.selected_index == -1 and active
|
||||
%Category.text = tr(current_container.category_name)
|
||||
%Icon.region_rect.position.x = category_index * 24
|
||||
|
||||
for i in [%LeftArrow, %RightArrow]:
|
||||
i.modulate.a = int(current_container.selected_index == -1)
|
||||
|
||||
for i in containers.size():
|
||||
containers[i].active = category_index == i and active
|
||||
if SelectableInputOption.rebinding_input == false:
|
||||
containers[i].can_input = can_move
|
||||
for i in disabled_containers:
|
||||
i.active = false
|
||||
if category_select_active and active and can_move:
|
||||
handle_inputs()
|
||||
if Input.is_action_just_pressed("ui_back") and active and current_container.can_input and can_move:
|
||||
close()
|
||||
|
||||
func handle_inputs() -> void:
|
||||
var direction := 0
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
category_index -= 1
|
||||
direction = -1
|
||||
if Settings.file.audio.extra_sfx == 1:
|
||||
AudioManager.play_global_sfx("menu_move")
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
category_index += 1
|
||||
direction += 1
|
||||
if Settings.file.audio.extra_sfx == 1:
|
||||
AudioManager.play_global_sfx("menu_move")
|
||||
category_index = wrap(category_index, 0, containers.size())
|
||||
current_container = containers[category_index]
|
||||
if disabled_containers.has(current_container):
|
||||
category_index = wrap(category_index + direction, 0, containers.size())
|
||||
|
||||
func open_pack_config_menu(pack: ResourcePackContainer) -> void:
|
||||
$ResourcePackConfigMenu.config_json = pack.config
|
||||
$ResourcePackConfigMenu.json_path = pack.config_path
|
||||
$ResourcePackConfigMenu.open()
|
||||
can_move = false
|
||||
await $ResourcePackConfigMenu.closed
|
||||
can_move = true
|
||||
|
||||
func open() -> void:
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
opened.emit()
|
||||
update_all_starting()
|
||||
$PanelContainer/MarginContainer/VBoxContainer/KeyboardControls.selected_index = -1
|
||||
$PanelContainer/MarginContainer/VBoxContainer/Controller.selected_index = -1
|
||||
show()
|
||||
update_minimum_size()
|
||||
current_container.show()
|
||||
current_container.active = true
|
||||
await get_tree().process_frame
|
||||
active = true
|
||||
|
||||
func update_all_starting() -> void:
|
||||
get_tree().call_group("Options", "update_starting_values")
|
||||
%Flag.region_rect.position.x = Global.lang_codes.find(TranslationServer.get_locale()) * 16
|
||||
$PanelContainer/MarginContainer/VBoxContainer/Video/Language.selected_index = Global.lang_codes.find(Settings.file.game.lang)
|
||||
|
||||
func close() -> void:
|
||||
hide()
|
||||
active = false
|
||||
closed.emit()
|
||||
await get_tree().process_frame
|
||||
Settings.save_settings()
|
||||
process_mode = Node.PROCESS_MODE_DISABLED
|
1
Scripts/UI/SettingsMenu.gd.uid
Executable file
1
Scripts/UI/SettingsMenu.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cj6858gmexp11
|
41
Scripts/UI/SpeedrunTypeSelect.gd
Normal file
41
Scripts/UI/SpeedrunTypeSelect.gd
Normal file
@@ -0,0 +1,41 @@
|
||||
extends Control
|
||||
|
||||
|
||||
signal selected
|
||||
signal cancelled
|
||||
|
||||
var selected_index := 0
|
||||
var active := false
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if active:
|
||||
if Input.is_action_just_pressed("ui_down"):
|
||||
selected_index += 1
|
||||
elif Input.is_action_just_pressed("ui_up"):
|
||||
selected_index -= 1
|
||||
selected_index = clamp(selected_index, 0, 1)
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
SpeedrunHandler.is_warp_run = bool(selected_index)
|
||||
selected.emit()
|
||||
close()
|
||||
elif Input.is_action_just_pressed("ui_back"):
|
||||
cancelled.emit()
|
||||
close()
|
||||
var idx := 0
|
||||
for i in [$Panel/VBoxContainer/Warpless/Cursor, $Panel/VBoxContainer/Any/Cursor]:
|
||||
i.modulate.a = int(selected_index == idx)
|
||||
idx += 1
|
||||
|
||||
func open() -> void:
|
||||
show()
|
||||
$Panel/VBoxContainer/Warpless.grab_focus()
|
||||
await get_tree().create_timer(0.1, false).timeout
|
||||
active = true
|
||||
grab_focus()
|
||||
|
||||
func close() -> void:
|
||||
active = false
|
||||
hide()
|
1
Scripts/UI/SpeedrunTypeSelect.gd.uid
Normal file
1
Scripts/UI/SpeedrunTypeSelect.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b8nv6ojpgvo0r
|
4
Scripts/UI/SpinBoxContextDisabler.gd
Normal file
4
Scripts/UI/SpinBoxContextDisabler.gd
Normal file
@@ -0,0 +1,4 @@
|
||||
extends SpinBox
|
||||
|
||||
func _ready() -> void:
|
||||
get_line_edit().context_menu_enabled = false
|
1
Scripts/UI/SpinBoxContextDisabler.gd.uid
Normal file
1
Scripts/UI/SpinBoxContextDisabler.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://1m5otytedbp4
|
59
Scripts/UI/StoryPause.gd
Normal file
59
Scripts/UI/StoryPause.gd
Normal file
@@ -0,0 +1,59 @@
|
||||
extends Control
|
||||
|
||||
var selected_index := 0
|
||||
|
||||
@export var options: Array[Label]
|
||||
@onready var cursor: TextureRect = $Control/Cursor
|
||||
|
||||
var active := false
|
||||
|
||||
@export var is_pause := true
|
||||
|
||||
signal option_1_selected
|
||||
signal option_2_selected
|
||||
signal option_3_selected
|
||||
signal option_4_selected
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if active:
|
||||
handle_inputs()
|
||||
cursor.global_position.y = options[selected_index].global_position.y + 4
|
||||
cursor.global_position.x = options[selected_index].global_position.x - 10
|
||||
|
||||
func handle_inputs() -> void:
|
||||
if Input.is_action_just_pressed("ui_down"):
|
||||
selected_index += 1
|
||||
if Input.is_action_just_pressed("ui_up"):
|
||||
selected_index -= 1
|
||||
selected_index = clamp(selected_index, 0, options.size() - 1)
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
option_selected()
|
||||
if Input.is_action_just_pressed("pause"):
|
||||
close()
|
||||
|
||||
func option_selected() -> void:
|
||||
emit_signal("option_" + str(selected_index + 1) + "_selected")
|
||||
|
||||
func open_settings() -> void:
|
||||
active = false
|
||||
$SettingsMenu.open()
|
||||
await $SettingsMenu.closed
|
||||
active = true
|
||||
|
||||
func open() -> void:
|
||||
if is_pause:
|
||||
Global.game_paused = true
|
||||
AudioManager.play_global_sfx("pause")
|
||||
get_tree().paused = true
|
||||
show()
|
||||
await get_tree().physics_frame
|
||||
active = true
|
||||
|
||||
func close() -> void:
|
||||
active = false
|
||||
selected_index = 0
|
||||
hide()
|
||||
for i in 2:
|
||||
await get_tree().physics_frame
|
||||
Global.game_paused = false
|
||||
get_tree().paused = false
|
1
Scripts/UI/StoryPause.gd.uid
Executable file
1
Scripts/UI/StoryPause.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://by48a8oa5hefr
|
19
Scripts/UI/StoryPauseOptionHandler.gd
Normal file
19
Scripts/UI/StoryPauseOptionHandler.gd
Normal file
@@ -0,0 +1,19 @@
|
||||
extends Node
|
||||
|
||||
func restart_level() -> void:
|
||||
Global.player_power_states = "0000"
|
||||
Global.checkpoint_passed = false
|
||||
Level.first_load = true
|
||||
Global.speed_run_timer = 0
|
||||
Global.marathon_mode = false
|
||||
Global.marathon_practice = false
|
||||
Global.speed_run_timer_enabled = false
|
||||
Global.boo_race = false
|
||||
Global.speed_run_timer_active = false
|
||||
Global.reset_values()
|
||||
AudioManager.main_level_music.stop()
|
||||
Global.death_load = true
|
||||
Global.current_level.reload_level()
|
||||
|
||||
func quit_to_menu() -> void:
|
||||
Global.transition_to_scene("res://Scenes/Levels/TitleScreen.tscn")
|
1
Scripts/UI/StoryPauseOptionHandler.gd.uid
Executable file
1
Scripts/UI/StoryPauseOptionHandler.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ju8kkqfm3qb6
|
18
Scripts/UI/SwapContainer.gd
Normal file
18
Scripts/UI/SwapContainer.gd
Normal file
@@ -0,0 +1,18 @@
|
||||
class_name SwapContainer
|
||||
extends BoxContainer
|
||||
|
||||
@export var test_node: Control = null
|
||||
@export var dummy_node: Control = null
|
||||
|
||||
func _ready() -> void:
|
||||
resized.connect(check)
|
||||
|
||||
func check() -> void:
|
||||
print(size.x >= test_node.size.x)
|
||||
print([size.x, test_node.size.x])
|
||||
if size.x > test_node.size.x:
|
||||
test_node.show()
|
||||
dummy_node.hide()
|
||||
else:
|
||||
test_node.hide()
|
||||
dummy_node.show()
|
1
Scripts/UI/SwapContainer.gd.uid
Normal file
1
Scripts/UI/SwapContainer.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bb8ldpxo5h71i
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user