From c17769246f53883c4604d4ebcd69d0c791a37eb8 Mon Sep 17 00:00:00 2001 From: Vaillant Jeremy Date: Sat, 16 May 2026 21:58:11 +0200 Subject: [PATCH] Replace M* table-wrapper classes with typed Resources; add type hints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related cleanups from the best-practice audit: Task 3 — typed Resources instead of MBase / MScene / MLevel / MSetting The old model classes wrapped the godot_db_manager Table API: each row read went through table.get_data_at_row_idx(int), Cell.get_data(), and 'as int' / 'as String' casts that don't actually parse anything in Godot 4. m_value, m_lock, m_label, ... members shadowed the cell indirection. Setters round-tripped through table.edit_data() + Global.database.save_db(). That's a lot of plumbing for what is, in the end, three flat tables of static strings. Introduce three @export-typed Resources: db/scene_entry.gd class_name SceneEntry db/level_entry.gd class_name LevelEntry db/settings_data.gd class_name SettingsData Rewrite scripts/Database.gd so Database.DB holds: settings: SettingsData levels: Array[LevelEntry] scenes: Array[SceneEntry] Build them once at startup from ahog.json, and serialise back to the same JSON shape on save() so existing progress files keep working. LevelEntry carries its own object_to_find / object_finding / reset methods (talking to Global.database for cross-table lookups), and SceneEntry carries its own mesh_path / audio_sound. Per-scene dissolve state (value, tick_reference, dissolved) lives on SceneEntry as non-exported runtime fields. Delete db/MBase.gd / db/MScene.gd / db/MLevel.gd / db/MSetting.gd. Update consumers: - scripts/Setting.gd: read/write Global.database.settings directly, call Global.database.save() after each setter. - scenes/levels/Levels.gd: iterate Global.database.scenes_for_level( current_scene_int) instead of mscene.new(i) for every row; scene state reads (scene.lock, scene.mesh, scene.counter, ...) replace scene.lock() / scene.mesh() / scene.counter() method calls; runtime dissolve state lives on the SceneEntry instance instead of mutable m_value / m_tick_reference members on MScene; 'dissolved' flag replaces set_mesh(null) signalling. - scenes/UI/choose_scenes/ChooseScene.gd: iterate Global.database .levels; level.name / level.thumb property access in place of level.name() / level.thumbnail(). configure_reset() loses its redundant index argument (LevelEntry knows its own index). - scripts/event.gd: _on_reset_level signature now takes LevelEntry, reset path drops index forwarding. Task 2 — type hints across the remaining scripts scripts/Global.gd, scenes/Main.gd, scenes/UI/ending/Ending.gd, scenes/UI/loading/Loading.gd, scenes/UI/settings/Settings.gd: add typed parameters and -> return annotations. current_scene_int is now 'int = -1' (sentinel) so callers don't fall into Variant comparisons; event.gd:_on_reset_level resets it to -1 instead of null. Settings.gd no longer wraps button_pressed in int() before passing to the now-typed bool setters. Co-Authored-By: Claude Opus 4.7 (1M context) --- db/MBase.gd | 11 - db/MBase.gd.uid | 1 - db/MLevel.gd | 57 ----- db/MLevel.gd.uid | 1 - db/MScene.gd | 99 --------- db/MScene.gd.uid | 1 - db/MSetting.gd | 70 ------- db/MSetting.gd.uid | 1 - db/level_entry.gd | 22 ++ db/level_entry.gd.uid | 1 + db/scene_entry.gd | 25 +++ db/scene_entry.gd.uid | 1 + db/settings_data.gd | 13 ++ db/settings_data.gd.uid | 1 + scenes/Main.gd | 36 ++-- scenes/UI/choose_scenes/ChooseScene.gd | 67 +++--- scenes/UI/ending/Ending.gd | 6 +- scenes/UI/loading/Loading.gd | 2 +- scenes/UI/settings/Settings.gd | 48 ++--- scenes/levels/Levels.gd | 277 ++++++++++++------------- scripts/Database.gd | 237 +++++++++++---------- scripts/Global.gd | 53 ++--- scripts/Setting.gd | 85 ++++---- scripts/event.gd | 6 +- 24 files changed, 462 insertions(+), 659 deletions(-) delete mode 100644 db/MBase.gd delete mode 100644 db/MBase.gd.uid delete mode 100644 db/MLevel.gd delete mode 100644 db/MLevel.gd.uid delete mode 100644 db/MScene.gd delete mode 100644 db/MScene.gd.uid delete mode 100644 db/MSetting.gd delete mode 100644 db/MSetting.gd.uid create mode 100644 db/level_entry.gd create mode 100644 db/level_entry.gd.uid create mode 100644 db/scene_entry.gd create mode 100644 db/scene_entry.gd.uid create mode 100644 db/settings_data.gd create mode 100644 db/settings_data.gd.uid diff --git a/db/MBase.gd b/db/MBase.gd deleted file mode 100644 index 8d01c67..0000000 --- a/db/MBase.gd +++ /dev/null @@ -1,11 +0,0 @@ -extends Node - -var table = null - -func _get_data(datas, index): - return datas[index].get_data() - -func _set_data(prop_id, row_id, data): - table.edit_data(prop_id, row_id, str(data)) - Global.database.save_db() - return data diff --git a/db/MBase.gd.uid b/db/MBase.gd.uid deleted file mode 100644 index ed3faf7..0000000 --- a/db/MBase.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bbyxkqilmfqeh diff --git a/db/MLevel.gd b/db/MLevel.gd deleted file mode 100644 index c92375c..0000000 --- a/db/MLevel.gd +++ /dev/null @@ -1,57 +0,0 @@ -extends "res://db/MBase.gd" - -var m_name = null -var m_thumb = null -var m_level = null -var mscene = load("res://db/MScene.gd") - -func _init(row_idx): - m_level = row_idx - table = Global.database.get_table_by_name("levels") - - var datas = table.get_data_at_row_idx(m_level) - m_name = _get_name(datas) - m_thumb = _get_thumb(datas) - -func object_to_find(): - var count = 0 - var datas = _scenes().get_data_by_prop_name_and_data("level", str(m_level)) - - if datas.size() != 0: - count = datas.size() - - return str(count) - -func reset(): - var scene_detail = null - var t = Global.database.get_table_by_name("scenes") - - for row_index in range(0, t.m_rows_count): - scene_detail = mscene.new(row_index) - if scene_detail.label() != null: - scene_detail.set_lock(int(false)) - -func _scenes(): - return Global.database.get_table_by_name("scenes") - -func object_finding(): - var count = 0 - - for datas in _scenes().get_dictionary_by_prop_name_and_data("level", str(m_level)): - if int(datas['lock']) == 1: - count = count + 1 - - return str(count) - -func name(): - return m_name - -func thumbnail(): - return m_thumb - -## PRIVATE -func _get_name(datas): - return str(_get_data(datas, 0)) - -func _get_thumb(datas): - return str(_get_data(datas, 1)) diff --git a/db/MLevel.gd.uid b/db/MLevel.gd.uid deleted file mode 100644 index 39c48d6..0000000 --- a/db/MLevel.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dtjnnc3165bhc diff --git a/db/MScene.gd b/db/MScene.gd deleted file mode 100644 index 337938d..0000000 --- a/db/MScene.gd +++ /dev/null @@ -1,99 +0,0 @@ -extends "res://db/MBase.gd" - -var m_value = 0 -var m_lock = null -var m_label = null -var m_label_counter = null -var m_tick_reference = 0 -var m_key = null -var m_level = null -var m_mesh = null -var m_counter = null -var m_row_id = null - -const LOCK_ID = 0 -const LABEL_ID = 1 -const KEY_ID = 2 -const LEVEL_ID = 3 -const MESH_ID = 4 -const LABEL_COUNTER = 5 -const COUNTER_ID = 6 - -func _init(row_index): - table = Global.database.get_table_by_name("scenes") - m_row_id = row_index - var datas = table.get_data_at_row_idx(m_row_id) - - if _get_level(datas) == Global.current_scene_int: - m_key = _get_key(datas) - m_lock = _get_lock(datas) - m_label = _get_label(datas) - m_label_counter = _get_label_counter(datas) - m_mesh = _get_mesh(datas) - m_counter = _get_counter(datas) - -func key(): - return m_key - -func label(): - return m_label - -func label_counter(): - return m_label_counter - -func lock(): - return m_lock - -func set_lock(p_value): - m_lock = _set_data(LOCK_ID, m_row_id, p_value) - -func mesh(): - return m_mesh - -func tween(): - return m_mesh + "/Tween" - -func set_mesh(p_value): - m_mesh = p_value - -func tick_reference(): - return m_tick_reference - -func set_tick_reference(p_value): - m_tick_reference = p_value - -func value(): - return m_value - -func set_value(p_value): - m_value = p_value - -func audio_sound(): - var stream = load("res://assets/sounds/objects/" + label() + ".ogg") - stream.set_loop(false) - return stream - -func counter(): - return m_counter - -## PRIVATE -func _get_lock(datas): - return bool(int(_get_data(datas, LOCK_ID))) - -func _get_label(datas): - return str(_get_data(datas, LABEL_ID)) - -func _get_key(datas): - return str(_get_data(datas, KEY_ID)) - -func _get_level(datas): - return int(_get_data(datas, LEVEL_ID)) - -func _get_mesh(datas): - return "HiddenObjectsItems/" + str(_get_data(datas, MESH_ID)) - -func _get_label_counter(datas): - return str(_get_data(datas, LABEL_COUNTER)) - -func _get_counter(datas): - return int(_get_data(datas, COUNTER_ID)) diff --git a/db/MScene.gd.uid b/db/MScene.gd.uid deleted file mode 100644 index 2db11ec..0000000 --- a/db/MScene.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cntdl3tocn0tv diff --git a/db/MSetting.gd b/db/MSetting.gd deleted file mode 100644 index 51a933c..0000000 --- a/db/MSetting.gd +++ /dev/null @@ -1,70 +0,0 @@ -extends "res://db/MBase.gd" - -var m_langue = null -var m_gyroscope = null -var m_ambient_sound = null -var m_resolution = null -var m_fullscreen = null -var m_version = null - -const ROW_ID = 0 -const LANGUE_ID = 0 -const GYRSOCPE_ID = 1 -const AMBIENT_SOUND = 2 -const RESOLUTION = 3 -const FULLSCREEN = 4 -const VERSION = 5 - -func _init(): - table = Global.database.get_table_by_name("settings") - var datas = table.get_data_at_row_idx(ROW_ID) - - m_langue = _get_data(datas, LANGUE_ID) - m_gyroscope = _get_data(datas, GYRSOCPE_ID) - m_ambient_sound = _get_data(datas, AMBIENT_SOUND) - m_resolution = _get_data(datas, RESOLUTION) - m_fullscreen = _get_data(datas, FULLSCREEN) - m_version = _get_data(datas, VERSION) - -func get_langue(): - return int(m_langue) - -func get_gyroscope(): - return bool(int(m_gyroscope)) - -func get_ambient_sound(): - return bool(int(m_ambient_sound)) - -func get_resolution(): - return m_resolution.split(" x ") - -func get_fullscreen(): - return bool(int(m_fullscreen)) - -func get_version(): - return "v" + str(m_version) - -func set_langue(value): - m_langue = _set_data(LANGUE_ID, ROW_ID, value) - - return get_langue() - -func set_gyroscope(value): - m_gyroscope = _set_data(GYRSOCPE_ID, ROW_ID, value) - - return get_gyroscope() - -func set_ambient_sound(value): - m_ambient_sound = _set_data(AMBIENT_SOUND, ROW_ID, value) - - return get_ambient_sound() - -func set_resolution(value): - m_resolution = _set_data(RESOLUTION, ROW_ID, value) - - return get_resolution() - -func set_fullscreen(value): - m_fullscreen = _set_data(FULLSCREEN, ROW_ID, value) - - return get_fullscreen() diff --git a/db/MSetting.gd.uid b/db/MSetting.gd.uid deleted file mode 100644 index 2c53958..0000000 --- a/db/MSetting.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bf50wtagbmftd diff --git a/db/level_entry.gd b/db/level_entry.gd new file mode 100644 index 0000000..749937f --- /dev/null +++ b/db/level_entry.gd @@ -0,0 +1,22 @@ +class_name LevelEntry extends Resource + +# A single row from the 'levels' table of ahog.json. + +@export var index: int = -1 +@export var name: String = "" +@export var thumb: String = "" + +func object_to_find() -> String: + return str(Global.database.scenes_for_level(index).size()) + +func object_finding() -> String: + var count := 0 + for s in Global.database.scenes_for_level(index): + if s.lock: + count += 1 + return str(count) + +func reset() -> void: + for s in Global.database.scenes_for_level(index): + s.lock = false + Global.database.save() diff --git a/db/level_entry.gd.uid b/db/level_entry.gd.uid new file mode 100644 index 0000000..70ab7cc --- /dev/null +++ b/db/level_entry.gd.uid @@ -0,0 +1 @@ +uid://dp7cvr2c75gj5 diff --git a/db/scene_entry.gd b/db/scene_entry.gd new file mode 100644 index 0000000..50eb58a --- /dev/null +++ b/db/scene_entry.gd @@ -0,0 +1,25 @@ +class_name SceneEntry extends Resource + +# A single hidden-object row from the 'scenes' table of ahog.json. + +@export var lock: bool = false +@export var label: String = "" +@export var key: String = "" +@export var level: int = -1 +@export var mesh: String = "" +@export var label_counter: String = "" +@export var counter: int = 1 + +# Runtime-only state for the dissolve animation tracker (not persisted). +var dissolve_value: float = 0.0 +var dissolve_tick_reference: int = 0 +var dissolved: bool = false + +func mesh_path() -> String: + return "HiddenObjectsItems/" + mesh + +func audio_sound() -> AudioStream: + var stream: AudioStream = load("res://assets/sounds/objects/%s.ogg" % label) + if stream is AudioStreamOggVorbis: + stream.loop = false + return stream diff --git a/db/scene_entry.gd.uid b/db/scene_entry.gd.uid new file mode 100644 index 0000000..bd5094b --- /dev/null +++ b/db/scene_entry.gd.uid @@ -0,0 +1 @@ +uid://ciro4yspcb6bi diff --git a/db/settings_data.gd b/db/settings_data.gd new file mode 100644 index 0000000..bfa4dd2 --- /dev/null +++ b/db/settings_data.gd @@ -0,0 +1,13 @@ +class_name SettingsData extends Resource + +# Single-row 'settings' table from ahog.json. + +@export var langue: int = 0 +@export var gyroscope: bool = false +@export var ambient_sound: bool = false +@export var resolution: String = "1280 x 720" +@export var fullscreen: bool = false +@export var version: String = "1.0.0" + +func resolution_split() -> PackedStringArray: + return resolution.split(" x ") diff --git a/db/settings_data.gd.uid b/db/settings_data.gd.uid new file mode 100644 index 0000000..1efadca --- /dev/null +++ b/db/settings_data.gd.uid @@ -0,0 +1 @@ +uid://dbgb8gs3fddt0 diff --git a/scenes/Main.gd b/scenes/Main.gd index 1e42f5e..a28899b 100644 --- a/scenes/Main.gd +++ b/scenes/Main.gd @@ -4,55 +4,49 @@ extends Node @export var setting: PackedScene = load("res://scenes/UI/settings/Settings.tscn") @export var choose_scene: PackedScene = load("res://scenes/UI/choose_scenes/ChooseScene.tscn") -@onready var current_scene = "title" -@onready var stream_button = preload("res://assets/sounds/click-button.ogg") -@onready var home = $MarginContainer/HBoxContainer/UI_summary/PanelWood/VBoxContainer/CenterContainer/TextureRect +@onready var stream_button: AudioStream = preload("res://assets/sounds/click-button.ogg") +@onready var home: TextureButton = $MarginContainer/HBoxContainer/UI_summary/PanelWood/VBoxContainer/CenterContainer/TextureRect -func _ready(): +func _ready() -> void: _translation() - home.set_focus_mode(2) + home.focus_mode = Control.FOCUS_ALL home.grab_focus() _apply_scene(title) _configure_sound() -func _configure_sound(): - stream_button.set_loop(false) +func _configure_sound() -> void: + if stream_button is AudioStreamOggVorbis: + stream_button.loop = false $MarginContainer/HBoxContainer/UI_summary/ClickButton.stream = stream_button ## PRIVATE -func _translation(): +func _translation() -> void: $MarginContainer/HBoxContainer/UI_summary/PanelWood/VBoxContainer/ButtonPuzzle/Label.text = tr("MAIN_BUTTON_PUZZLES") $MarginContainer/HBoxContainer/UI_summary/PanelWood/VBoxContainer/ButtonSetting/Label.text = tr("MAIN_BUTTON_SETTINGS") $MarginContainer/HBoxContainer/UI_summary/PanelWood/VBoxContainer/ButtonQuit/Label.text = tr("MAIN_BUTTON_QUIT") $MarginContainer/HBoxContainer/UI_summary/PanelWood/VBoxContainer/ContainerVersion/LabelVersion.text = Setting.get_setting_version() -func _apply_scene(actual_scene): - var node = get_node("MarginContainer/HBoxContainer/MarginContainer/") - +func _apply_scene(actual_scene: PackedScene) -> void: + var node := get_node("MarginContainer/HBoxContainer/MarginContainer/") if node.get_child_count() != 0: node.get_child(0).queue_free() - node.add_child(actual_scene.instantiate()) -# Load scene for select game -func _on_ButtonPuzzle_pressed(): +func _on_ButtonPuzzle_pressed() -> void: _sound_button() _apply_scene(choose_scene) -# Load scene settings -func _on_ButtonSetting_pressed(): +func _on_ButtonSetting_pressed() -> void: _sound_button() _apply_scene(setting) -# Click to icon game -func _on_TextureRect_pressed(): +func _on_TextureRect_pressed() -> void: _sound_button() _apply_scene(title) -# Quit the game -func _on_ButtonQuit_pressed(): +func _on_ButtonQuit_pressed() -> void: _sound_button() get_tree().quit(0) -func _sound_button(): +func _sound_button() -> void: $MarginContainer/HBoxContainer/UI_summary/ClickButton.play() diff --git a/scenes/UI/choose_scenes/ChooseScene.gd b/scenes/UI/choose_scenes/ChooseScene.gd index 8ce99f7..1a0b245 100644 --- a/scenes/UI/choose_scenes/ChooseScene.gd +++ b/scenes/UI/choose_scenes/ChooseScene.gd @@ -2,68 +2,57 @@ extends Control @export var template: PackedScene = load("res://scenes/UI/choose_scenes/parts/Template.tscn") -func _ready(): - _load_button_access_scenes() +func _ready() -> void: + for level in Global.database.levels: + _apply_scene(level) ## PRIVATE -func _load_button_access_scenes(): - for row_index in range(0, Global.database.get_table_by_name("levels").m_rows_count): - _apply_scene(load("res://db/MLevel.gd").new(row_index), row_index) - -func _apply_scene(level, index): - $MarginContainer.add_child(_load_scene(level.name())) - var node = _build_path(level.name()) - +func _apply_scene(level: LevelEntry) -> void: + $MarginContainer.add_child(_load_scene(level.name)) + var node := _build_path(level.name) _configure_select(level, node) - configure_reset(level, node, index, false) + configure_reset(level, node, false) configure_counter(level, node) -func _load_scene(p_name): - var template_instance = template.instantiate() +func _load_scene(p_name: String) -> Node: + var template_instance := template.instantiate() template_instance.set_name(p_name) - return template_instance -func _build_path(p_name): +func _build_path(p_name: String) -> String: return "MarginContainer/" + p_name -func _load_texture(thumbnail): - return load(thumbnail) - -func _configure_select(level, node): - var selector = get_node(node+"/MarginContainer/CenterAlign/MainButton") - var thumbnail = get_node(node+"/MarginContainer/CenterAlign/MainButton/MarginStich/ThumbnailLevel") - - thumbnail.set_texture(_load_texture(level.thumbnail())) - var handler = Event.level_pressed(level.name()) +func _configure_select(level: LevelEntry, node: String) -> void: + var selector := get_node(node + "/MarginContainer/CenterAlign/MainButton") + var thumbnail: TextureRect = get_node(node + "/MarginContainer/CenterAlign/MainButton/MarginStich/ThumbnailLevel") + thumbnail.texture = load(level.thumb) + var handler := Event.level_pressed(level.name) if handler.is_valid(): selector.pressed.connect(handler) -func configure_reset(level, node, index, animate): - var reset = get_node(node+"/MarginContainer/CenterAlign/MainButton/TabAlign/ButtonReset") - var animation = get_node(node+"/AnimationPlayer") +func configure_reset(level: LevelEntry, node: String, animate: bool) -> void: + var reset: BaseButton = get_node(node + "/MarginContainer/CenterAlign/MainButton/TabAlign/ButtonReset") + var animation: AnimationPlayer = get_node(node + "/AnimationPlayer") if int(level.object_finding()) == 0: _configure_reset_disable(animation, reset, animate) else: - _configure_reset_enable(animation, reset, level, node, index) + _configure_reset_enable(animation, reset, level, node) -func _configure_reset_disable(animation, reset, animate = false): +func _configure_reset_disable(animation: AnimationPlayer, reset: BaseButton, animate: bool = false) -> void: animation.play("SlideReset") - if !animate: + if not animate: animation.seek(1, false) - reset.set_disabled(true) - reset.set_default_cursor_shape(CURSOR_ARROW) + reset.mouse_default_cursor_shape = CURSOR_ARROW for c in reset.pressed.get_connections(): reset.pressed.disconnect(c["callable"]) -func _configure_reset_enable(animation, reset, level, node, index): +func _configure_reset_enable(animation: AnimationPlayer, reset: BaseButton, level: LevelEntry, node: String) -> void: animation.play_backwards("SlideReset") reset.set_disabled(false) - reset.set_default_cursor_shape(CURSOR_POINTING_HAND) - reset.pressed.connect(Event._on_reset_level.bind(level, node, index, self)) + reset.mouse_default_cursor_shape = CURSOR_POINTING_HAND + reset.pressed.connect(Event._on_reset_level.bind(level, node, level.index, self)) -func configure_counter(level, node): - var count = get_node(node+"/MarginContainer/CenterAlign/MainButton/TabAlign/ButtonCount/MarginBottom/Label") - - count.set_text(level.object_finding()+" / "+level.object_to_find()) +func configure_counter(level: LevelEntry, node: String) -> void: + var count: Label = get_node(node + "/MarginContainer/CenterAlign/MainButton/TabAlign/ButtonCount/MarginBottom/Label") + count.text = level.object_finding() + " / " + level.object_to_find() diff --git a/scenes/UI/ending/Ending.gd b/scenes/UI/ending/Ending.gd index 833326c..a95a074 100644 --- a/scenes/UI/ending/Ending.gd +++ b/scenes/UI/ending/Ending.gd @@ -1,9 +1,7 @@ extends CenterContainer -func _ready(): - print("[ending_#ready]") +func _ready() -> void: $AudioStreamPlayer.play() -func _on_Timer_timeout(): - print("[ending#_on_Timer_timeout]") +func _on_Timer_timeout() -> void: Global.goto_scene("res://scenes/Main.tscn") diff --git a/scenes/UI/loading/Loading.gd b/scenes/UI/loading/Loading.gd index 71ee549..558436b 100644 --- a/scenes/UI/loading/Loading.gd +++ b/scenes/UI/loading/Loading.gd @@ -1,4 +1,4 @@ extends Control -func _ready(): +func _ready() -> void: get_node("LabelLoading").text = tr("LOADING") diff --git a/scenes/UI/settings/Settings.gd b/scenes/UI/settings/Settings.gd index 6366b15..80b018b 100644 --- a/scenes/UI/settings/Settings.gd +++ b/scenes/UI/settings/Settings.gd @@ -1,6 +1,6 @@ extends Node -func _ready(): +func _ready() -> void: _apply_translation() _apply_settings_language() _apply_settings_gyroscope() @@ -9,50 +9,46 @@ func _ready(): _apply_settings_fullscreen() ## PRIVATE -func _apply_translation(): +func _apply_translation() -> void: $VBoxContainer/langue/VBoxContainer/Label.text = tr("SETTINGS_LABEL_LANGUE") $VBoxContainer/gyroscope/HBoxContainer/Label.text = tr("SETTINGS_LABEL_GYROSCOPE") -func _apply_settings_language(): - var data = $VBoxContainer/langue/VBoxContainer/data - +func _apply_settings_language() -> void: + var data: ItemList = $VBoxContainer/langue/VBoxContainer/data data.add_item("English", load("res://assets/ui/flags/english.png"), true) # id : 0 data.add_item("Français", load("res://assets/ui/flags/french.png"), true) # id : 1 - data.select(Setting.get_setting_language()) -func _apply_settings_gyroscope(): +func _apply_settings_gyroscope() -> void: $VBoxContainer/gyroscope/HBoxContainer/data.button_pressed = Setting.get_setting_gyrosocpe() -func _apply_settings_sound_ambient(): +func _apply_settings_sound_ambient() -> void: $VBoxContainer/ambient_sound/HBoxContainer/data.button_pressed = Setting.get_setting_ambient_sound() -func _apply_settings_resolution(): - var data = $VBoxContainer/resolution/VBoxContainer/data - - data.add_item("2560 x 1440", null, true) # id : 0 - data.add_item("1920 x 1080", null, true) # id : 1 - data.add_item("1280 x 720", null, true) # id : 2 - data.add_item("854 x 576", null, true) # id : 3 - +func _apply_settings_resolution() -> void: + var data: ItemList = $VBoxContainer/resolution/VBoxContainer/data + data.add_item("2560 x 1440", null, true) + data.add_item("1920 x 1080", null, true) + data.add_item("1280 x 720", null, true) + data.add_item("854 x 576", null, true) for index in range(4): - if data.get_item_text(index).split(' x ') == Setting.get_setting_resolution(): + if data.get_item_text(index).split(" x ") == Setting.get_setting_resolution(): data.select(index) -func _apply_settings_fullscreen(): +func _apply_settings_fullscreen() -> void: $VBoxContainer/fullscreen/HBoxContainer/data.button_pressed = Setting.get_setting_fullscreen() -func _on_gyroscope_pressed(): - Setting.set_setting_gyroscope(int($VBoxContainer/gyroscope/HBoxContainer/data.button_pressed)) +func _on_gyroscope_pressed() -> void: + Setting.set_setting_gyroscope($VBoxContainer/gyroscope/HBoxContainer/data.button_pressed) -func _on_ambient_sound_pressed(): - Setting.set_setting_ambient_sound(int($VBoxContainer/ambient_sound/HBoxContainer/data.button_pressed)) +func _on_ambient_sound_pressed() -> void: + Setting.set_setting_ambient_sound($VBoxContainer/ambient_sound/HBoxContainer/data.button_pressed) -func _on_langue_item_selected(index): +func _on_langue_item_selected(index: int) -> void: Setting.set_setting_language(index) -func _on_resolution_item_selected(index): +func _on_resolution_item_selected(index: int) -> void: Setting.set_setting_resolution($VBoxContainer/resolution/VBoxContainer/data.get_item_text(index)) -func _on_fullscreen_item_selected(): - Setting.set_setting_fullscreen(int($VBoxContainer/fullscreen/HBoxContainer/data.button_pressed)) +func _on_fullscreen_item_selected() -> void: + Setting.set_setting_fullscreen($VBoxContainer/fullscreen/HBoxContainer/data.button_pressed) diff --git a/scenes/levels/Levels.gd b/scenes/levels/Levels.gd index e627a45..a0d79e1 100644 --- a/scenes/levels/Levels.gd +++ b/scenes/levels/Levels.gd @@ -1,79 +1,65 @@ extends Node3D -const TIME_MAX = 3000 # msec -const GYROSCOPE_MAX_DIFF = 0.5 -const OFFSET_CAMERA_MAX = 0.12 -const OFFSET_STEP_CHANGE = 0.01 -const RAY_LENGTH = 1000 +const TIME_MAX := 3000 # msec +const GYROSCOPE_MAX_DIFF := 0.5 +const OFFSET_CAMERA_MAX := 0.12 +const OFFSET_STEP_CHANGE := 0.01 +const RAY_LENGTH := 1000 @export var object_first: PackedScene = load("res://scenes/levels/parts/ObjectListFirst.tscn") @export var object_std: PackedScene = load("res://scenes/levels/parts/ObjectListStandard.tscn") @export var object_last: PackedScene = load("res://scenes/levels/parts/ObjectListLast.tscn") -@onready var gyroscope_value_old = Vector3(0, 0, 0) -@onready var table = Global.database.get_table_by_name("scenes") -@onready var meshes = {} -@onready var from = null -@onready var to = null -@onready var mlevel = load("res://db/MLevel.gd") -@onready var mscene = load("res://db/MScene.gd") -@onready var victory_condition = 0 -@onready var victory_progress = 0 -@onready var last_button = null -@onready var animation_player = null +var gyroscope_value_old := Vector3(0, 0, 0) +var meshes: Dictionary[String, SceneEntry] = {} +var from = null +var to = null +var victory_condition: String = "0" +var victory_progress: String = "0" +var last_button: Node = null -func _ready(): - _load_translations() +func _ready() -> void: _load_meshes() _load_back_button() _load_prepare_victory_condition() _load_hud_menu() _load_ambient_sound() -func _load_translations(): - pass +func _load_meshes() -> void: + for scene in Global.database.scenes_for_level(Global.current_scene_int): + meshes[scene.key] = scene + create_dissolve_mesh(scene.key) -func _load_meshes(): - var scene_detail = null - - for row_index in range(0, table.m_rows_count): - scene_detail = mscene.new(row_index) - - if scene_detail.key() != null: - meshes[scene_detail.key()] = scene_detail - create_dissolve_mesh(scene_detail.key()) - -func _load_back_button(): +func _load_back_button() -> void: $Quit/TextureButton.pressed.connect(Event._on_main_scene_pressed) -func _load_prepare_victory_condition(): - var level = mlevel.new(Global.current_scene_int) - +func _load_prepare_victory_condition() -> void: + var level := Global.database.level_by_index(Global.current_scene_int) + if level == null: + return victory_condition = level.object_to_find() victory_progress = level.object_finding() -func _load_hud_menu(): - var counter = 0 - var scene = null - var label_counter = null - +func _load_hud_menu() -> void: + var counter := 0 + var label_counter: String = "" for key in meshes: - scene = meshes[key] + var scene: SceneEntry = meshes[key] _create_button_info(scene, counter, label_counter) - label_counter = scene.label_counter() - counter = counter + 1 + label_counter = scene.label_counter + counter += 1 -func _load_ambient_sound(): +func _load_ambient_sound() -> void: if Setting.get_setting_ambient_sound(): $AmbientSound.play() $AmbientSound.stream_paused = false -func _create_button_info(scene, counter, label_counter): - var button = _search_button_to_use(counter) - var label_name = scene.label() +func _create_button_info(scene: SceneEntry, counter: int, label_counter: String) -> void: + var button := _search_button_to_use(counter) + var label_name := scene.label - if label_counter != null and label_counter == scene.label_counter(): - label_name = last_button.get_node("Label").text + " " + str(scene.counter()) + if label_counter != "" and label_counter == scene.label_counter: + label_name = last_button.get_node("Label").text + " " + str(scene.counter) _configure_button_object(last_button, scene, label_name) _create_animation_warning(_get_node_animated().get_node("Label"), label_name) else: @@ -83,194 +69,189 @@ func _create_button_info(scene, counter, label_counter): last_button = button -func _get_node_animated(): - return $ListObjects/ListContainer.get_child($ListObjects/ListContainer.get_child_count() - 1 ) +func _get_node_animated() -> Node: + return $ListObjects/ListContainer.get_child($ListObjects/ListContainer.get_child_count() - 1) -func _search_button_to_use(counter): +func _search_button_to_use(counter: int) -> Node: if counter == 0: return object_first.instantiate() elif counter == meshes.size() - 1: return object_last.instantiate() - else: - return object_std.instantiate() + return object_std.instantiate() -func _configure_button_object(button, scene, label): +func _configure_button_object(button: Node, scene: SceneEntry, label: String) -> void: button.get_node("Label").set_text(label) button.set_meta("animation", label) - button.set_meta("name", scene.label()) - button.set_meta("counter", scene.counter()) + button.set_meta("name", scene.label) + button.set_meta("counter", scene.counter) button.set_meta("counted", 0) -func _create_animation_slide(node, p_name): +func _create_animation_slide(node: Node, p_name: String) -> void: _add_animation_to_player(p_name, GameAnimation.level_hud_slide(node)) -func _create_animation_warning(node, p_name): +func _create_animation_warning(node: Node, p_name: String) -> void: _add_animation_to_player(p_name, GameAnimation.level_hud_warning(node)) func _add_animation_to_player(p_name: String, anim: Animation) -> void: - var player = $ListObjects/AnimationPlayer - var lib = player.get_animation_library("") + var player: AnimationPlayer = $ListObjects/AnimationPlayer + var lib := player.get_animation_library("") if lib == null: lib = AnimationLibrary.new() player.add_animation_library("", lib) lib.add_animation(p_name, anim) -func _process(_delta): +func _process(_delta: float) -> void: _check_dissolve_mesh() _check_change_angle_camera() _check_victory_condition() -func create_dissolve_mesh(key): - var mesh = _node_to_mesh(key) +func create_dissolve_mesh(key: String) -> void: + var mesh := _node_to_mesh(key) if mesh == null: return - var material = mesh.get_active_material(0) + var material := mesh.get_active_material(0) if material == null: return material.set_shader_parameter("dissolve_amount", 0.0) -func _check_dissolve_mesh(): +func _check_dissolve_mesh() -> void: # Event dissolve in object searched by gamer for key in meshes: - if bool(meshes[key].lock()) == true and meshes[key].mesh() != null: - var mesh = _node_to_mesh(key) - if mesh == null: - continue - if meshes[key].tick_reference() == 0: - meshes[key].set_tick_reference(Time.get_ticks_msec()) - _node_object_list(key) - GameAnimation.start_dissolve(mesh, mesh.get_active_material(0)) + var scene: SceneEntry = meshes[key] + if not scene.lock or scene.dissolved: + continue + var mesh := _node_to_mesh(key) + if mesh == null: + continue + if scene.dissolve_tick_reference == 0: + scene.dissolve_tick_reference = Time.get_ticks_msec() + _node_object_list(key) + GameAnimation.start_dissolve(mesh, mesh.get_active_material(0)) + if Time.get_ticks_msec() < scene.dissolve_tick_reference + TIME_MAX: + scene.dissolve_value += 0.01 + else: + _clean_mesh(key) - if Time.get_ticks_msec() < meshes[key].tick_reference() + TIME_MAX: - meshes[key].set_value(meshes[key].value() + 0.01) - else: - _clean_mesh(key) +func _clean_mesh(key: String) -> void: + var level := Global.database.level_by_index(Global.current_scene_int) + if level != null: + victory_progress = level.object_finding() + var mesh := _node_to_mesh(key) + if mesh != null: + mesh.call_deferred("free") + meshes[key].dissolved = true -func _clean_mesh(key): - victory_progress = mlevel.new(Global.current_scene_int).object_finding() - _node_to_mesh(key).call_deferred("free") - meshes[key].set_mesh(null) +func _check_change_angle_camera() -> void: + var camera: Camera3D = $"MainCamera" + var gyroscope := Input.get_gyroscope() -func _check_change_angle_camera(): - var camera = $"MainCamera" - var gyroscope = Input.get_gyroscope() - if camera.h_offset >= -OFFSET_CAMERA_MAX: if _action_pressed("ui_left") or _action_gyroscope("left", gyroscope): - print("[warcraft#_ready] move camera angle to left") $"Main Camera3D".h_offset -= OFFSET_STEP_CHANGE - + if camera.h_offset <= OFFSET_CAMERA_MAX: if _action_pressed("ui_right") or _action_gyroscope("right", gyroscope): - print("[warcraft#_ready] move camera angle to right") $"Main Camera3D".h_offset += OFFSET_STEP_CHANGE - + if camera.v_offset >= -OFFSET_CAMERA_MAX: if _action_pressed("ui_down") or _action_gyroscope("down", gyroscope): - print("[warcraft#_ready] move camera angle to down") $"Main Camera3D".v_offset -= OFFSET_STEP_CHANGE - + if camera.v_offset <= OFFSET_CAMERA_MAX: if _action_pressed("ui_up") or _action_gyroscope("up", gyroscope): - print("[warcraft#_ready] move camera angle to up") $"Main Camera3D".v_offset += OFFSET_STEP_CHANGE - + gyroscope_value_old = gyroscope -func _action_pressed(action): +func _action_pressed(action: String) -> bool: return Input.is_action_pressed(action) -func _action_gyroscope(action, gyroscope): - if Setting.get_setting_gyrosocpe(): - var expression = Expression.new() - - expression.parse("_gyroscope_changed_"+action+"(gyroscope)", ["gyroscope"]) - - if expression.execute([gyroscope], self): - return true - else: - return false - else: +func _action_gyroscope(action: String, gyroscope: Vector3) -> bool: + if not Setting.get_setting_gyrosocpe(): return false + var expression := Expression.new() + expression.parse("_gyroscope_changed_" + action + "(gyroscope)", ["gyroscope"]) + return bool(expression.execute([gyroscope], self)) -func _gyroscope_changed_left(gyroscope): +func _gyroscope_changed_left(gyroscope: Vector3) -> bool: return (gyroscope.abs().y - gyroscope_value_old.abs().y) > GYROSCOPE_MAX_DIFF and \ gyroscope.y < gyroscope_value_old.y -func _gyroscope_changed_right(gyroscope): +func _gyroscope_changed_right(gyroscope: Vector3) -> bool: return (gyroscope.abs().y - gyroscope_value_old.abs().y) > GYROSCOPE_MAX_DIFF and \ gyroscope.y > gyroscope_value_old.y -func _gyroscope_changed_down(gyroscope): +func _gyroscope_changed_down(gyroscope: Vector3) -> bool: return (gyroscope.abs().z - gyroscope_value_old.abs().z) > GYROSCOPE_MAX_DIFF and \ gyroscope.z > gyroscope_value_old.z or \ (gyroscope.abs().x - gyroscope_value_old.abs().x) > GYROSCOPE_MAX_DIFF and \ gyroscope.x > gyroscope_value_old.x -func _gyroscope_changed_up(gyroscope): +func _gyroscope_changed_up(gyroscope: Vector3) -> bool: return (gyroscope.abs().z - gyroscope_value_old.abs().z) > GYROSCOPE_MAX_DIFF and \ gyroscope.z < gyroscope_value_old.z or \ (gyroscope.abs().x - gyroscope_value_old.abs().x) > GYROSCOPE_MAX_DIFF and \ gyroscope.x < gyroscope_value_old.x -func _start_dissolve(key): - if bool(meshes[key].lock()) == false: - meshes[key].set_lock(int(true)) - $ObjectFind.stream = meshes[key].audio_sound() - $ObjectFind.play() +func _start_dissolve(key: String) -> void: + if not meshes.has(key): + return + var scene: SceneEntry = meshes[key] + if scene.lock: + return + scene.lock = true + Global.database.save() + $ObjectFind.stream = scene.audio_sound() + $ObjectFind.play() -func _check_victory_condition(): +func _check_victory_condition() -> void: if victory_condition == victory_progress: - print("[levels#_check_victory_condition] \\o/\\o/ \\o/ \\o/ \\o/\\o/") - print("[levels#_check_victory_condition] Win !!") Global.goto_scene("res://scenes/UI/ending/Ending.tscn") -func _node_to_mesh(key): - return get_node_or_null(meshes[key].mesh()) - -func _node_to_area(key): - return get_node_or_null(meshes[key].mesh() + "/Area3D") - -func _node_object_list(key): - var animation_played = null +func _node_to_mesh(key: String) -> MeshInstance3D: + if not meshes.has(key): + return null + return get_node_or_null(meshes[key].mesh_path()) as MeshInstance3D +func _node_object_list(key: String) -> void: + var animation_played: String = "" for child in $ListObjects/ListContainer.get_children(): - if child.has_meta("name"): - if child.get_meta("name") == meshes[key].label(): - child.set_meta("counted", child.get_meta("counted") + 1) - - if child.get_meta("counter") == child.get_meta("counted"): - animation_played = child.get_meta("name") - else: - var diff = child.get_meta("counter") - child.get_meta("counted") - var txt = child.get_meta("name") - if diff != 1: - txt = txt + " " + str(diff) - animation_played = child.get_meta("animation") - child.get_node("Label").set_text(txt) - - $ListObjects/AnimationPlayer.queue(animation_played) + if not child.has_meta("name"): + continue + if child.get_meta("name") != meshes[key].label: + continue + child.set_meta("counted", child.get_meta("counted") + 1) + if child.get_meta("counter") == child.get_meta("counted"): + animation_played = child.get_meta("name") + else: + var diff: int = child.get_meta("counter") - child.get_meta("counted") + var txt: String = child.get_meta("name") + if diff != 1: + txt = txt + " " + str(diff) + animation_played = child.get_meta("animation") + child.get_node("Label").set_text(txt) + $ListObjects/AnimationPlayer.queue(animation_played) -func _input(event): +func _input(event: InputEvent) -> void: if event is InputEventMouseButton or event is InputEventScreenTouch: - var camera = $"MainCamera" + var camera: Camera3D = $"MainCamera" from = camera.project_ray_origin(event.position) to = from + camera.project_ray_normal(event.position) * RAY_LENGTH -func _physics_process(_delta): - var space_state = get_world_3d().direct_space_state +func _physics_process(_delta: float) -> void: + var space_state := get_world_3d().direct_space_state if from != null and to != null: _check_collider(space_state) -func _check_collider(space_state): - var query = PhysicsRayQueryParameters3D.create(from, to) +func _check_collider(space_state: PhysicsDirectSpaceState3D) -> void: + var query := PhysicsRayQueryParameters3D.create(from, to) query.collision_mask = 1 query.collide_with_bodies = false query.collide_with_areas = true - var result = space_state.intersect_ray(query) + var result := space_state.intersect_ray(query) from = null to = null if result.has("collider"): - var node = result["collider"].get_parent() + var node: Node = result["collider"].get_parent() if node != null: _start_dissolve(node.name) diff --git a/scripts/Database.gd b/scripts/Database.gd index 1087807..6c09155 100644 --- a/scripts/Database.gd +++ b/scripts/Database.gd @@ -1,128 +1,155 @@ extends Node -# Lightweight replacement for the godot_db_manager plugin (incompatible with -# Godot 4). Keeps the ahog.json format used by the rest of the project and -# exposes the same surface API the M* model classes expect: +# Loads ahog.json once into typed Resource arrays: +# Global.database.settings -> SettingsData +# Global.database.levels -> Array[LevelEntry] +# Global.database.scenes -> Array[SceneEntry] # -# Global.database.get_table_by_name(name) -> Table -# Global.database.save_db() -# table.get_data_at_row_idx(row_id) -> [Cell, Cell, ...] -# table.edit_data(prop_id, row_id, value) -# table.m_rows_count -# table.get_data_by_prop_name_and_data(prop_name, value) -> Array -# table.get_dictionary_by_prop_name_and_data(prop_name, value) -> Array[Dict] -# cell.get_data() -> String +# save() round-trips back to the same JSON format so game progress +# (per-scene lock state) persists across runs. -class Cell: - var _value - func _init(value): - _value = value - func get_data(): - return _value - -class Table: - var name: String - var props: Array - var data: Array - var m_rows_count: int - - func _init(table_dict: Dictionary): - name = table_dict["table_name"] - props = table_dict["props"] - data = table_dict["data"] - m_rows_count = data.size() / max(1, props.size()) - - func _col_count() -> int: - return props.size() - - func _find_prop_idx(prop_name: String) -> int: - for i in range(props.size()): - if props[i]["name"] == prop_name: - return i - return -1 - - func get_data_at_row_idx(row_idx: int) -> Array: - var start = row_idx * _col_count() - var cells = [] - for i in range(_col_count()): - cells.append(Cell.new(data[start + i])) - return cells - - func edit_data(prop_id: int, row_id: int, value) -> void: - data[row_id * _col_count() + prop_id] = str(value) - - func get_data_by_prop_name_and_data(prop_name: String, value) -> Array: - var prop_idx = _find_prop_idx(prop_name) - var matches = [] - if prop_idx == -1: - return matches - for row in range(m_rows_count): - if data[row * _col_count() + prop_idx] == str(value): - matches.append(row) - return matches - - func get_dictionary_by_prop_name_and_data(prop_name: String, value) -> Array: - var prop_idx = _find_prop_idx(prop_name) - var results = [] - if prop_idx == -1: - return results - for row in range(m_rows_count): - if data[row * _col_count() + prop_idx] == str(value): - var d = {} - for i in range(_col_count()): - d[props[i]["name"]] = data[row * _col_count() + i] - results.append(d) - return results - - func to_dict() -> Dictionary: - return { - "table_name": name, - "props": props, - "data": data, - } - -class DB: - var version: String - var db_name: String - var tables: Dictionary = {} +class DB extends RefCounted: + var settings: SettingsData + var levels: Array[LevelEntry] = [] + var scenes: Array[SceneEntry] = [] var _path: String - func _init(path: String): + const _SETTINGS_PROPS := [ + {"name": "langue", "type": "1", "auto_increment": "0"}, + {"name": "gyroscope", "type": "0", "auto_increment": "0"}, + {"name": "ambient_sound", "type": "0", "auto_increment": "0"}, + {"name": "resolution", "type": "3", "auto_increment": "0"}, + {"name": "fullscreen", "type": "0", "auto_increment": "0"}, + {"name": "version", "type": "0", "auto_increment": "0"}, + ] + const _LEVELS_PROPS := [ + {"name": "name", "type": "3", "auto_increment": "0"}, + {"name": "thumb", "type": "4", "auto_increment": "0"}, + ] + const _SCENES_PROPS := [ + {"name": "lock", "type": "0", "auto_increment": "0"}, + {"name": "label", "type": "3", "auto_increment": "0"}, + {"name": "key", "type": "3", "auto_increment": "0"}, + {"name": "level", "type": "table", "table_name": "levels", "auto_increment": "0"}, + {"name": "mesh", "type": "3", "auto_increment": "0"}, + {"name": "label_counter", "type": "1", "auto_increment": "0"}, + {"name": "counter", "type": "1", "auto_increment": "0"}, + ] + + func _init(path: String) -> void: _path = path - var f = FileAccess.open(path, FileAccess.READ) + var f := FileAccess.open(path, FileAccess.READ) if f == null: push_error("[Database] Cannot open " + path) return - var raw = f.get_as_text() + var doc = JSON.parse_string(f.get_as_text()) f.close() - var doc = JSON.parse_string(raw) - if doc == null: + if not (doc is Dictionary): push_error("[Database] Invalid JSON in " + path) return - version = doc.get("GDDB_ver", "") - db_name = doc.get("db_name", "") - for t in doc.get("tables", []): - var table = Table.new(t) - tables[table.name] = table + for table in doc.get("tables", []): + match table.get("table_name", ""): + "settings": settings = _load_settings(table.get("data", [])) + "levels": levels = _load_levels(table.get("data", [])) + "scenes": scenes = _load_scenes(table.get("data", [])) - func get_table_by_name(name: String): - return tables.get(name) + func _load_settings(data: Array) -> SettingsData: + var s := SettingsData.new() + if data.size() >= 6: + s.langue = int(data[0]) + s.gyroscope = bool(int(data[1])) + s.ambient_sound = bool(int(data[2])) + s.resolution = str(data[3]) + s.fullscreen = bool(int(data[4])) + s.version = str(data[5]) + return s - func save_db() -> void: - var doc = { - "GDDB_ver": version, - "db_name": db_name, - "tables": [], + func _load_levels(data: Array) -> Array[LevelEntry]: + var result: Array[LevelEntry] = [] + const W := 2 + for i in range(data.size() / W): + var l := LevelEntry.new() + l.index = i + l.name = str(data[i * W + 0]) + l.thumb = str(data[i * W + 1]) + result.append(l) + return result + + func _load_scenes(data: Array) -> Array[SceneEntry]: + var result: Array[SceneEntry] = [] + const W := 7 + for i in range(data.size() / W): + var s := SceneEntry.new() + s.lock = bool(int(data[i * W + 0])) + s.label = str(data[i * W + 1]) + s.key = str(data[i * W + 2]) + s.level = int(data[i * W + 3]) + s.mesh = str(data[i * W + 4]) + s.label_counter = str(data[i * W + 5]) + s.counter = int(data[i * W + 6]) + result.append(s) + return result + + func scenes_for_level(level_idx: int) -> Array[SceneEntry]: + var result: Array[SceneEntry] = [] + for s in scenes: + if s.level == level_idx: + result.append(s) + return result + + func level_by_index(idx: int) -> LevelEntry: + for l in levels: + if l.index == idx: + return l + return null + + func save() -> void: + var doc := { + "GDDB_ver": "2.0", + "db_name": "ahog", + "tables": [ + {"table_name": "settings", "props": _SETTINGS_PROPS, "data": _dump_settings()}, + {"table_name": "levels", "props": _LEVELS_PROPS, "data": _dump_levels()}, + {"table_name": "scenes", "props": _SCENES_PROPS, "data": _dump_scenes()}, + ], } - for tname in tables: - doc["tables"].append(tables[tname].to_dict()) - var f = FileAccess.open(_path, FileAccess.WRITE) + var f := FileAccess.open(_path, FileAccess.WRITE) if f == null: push_error("[Database] Cannot write " + _path) return f.store_string(JSON.stringify(doc)) f.close() + func _dump_settings() -> Array: + return [ + str(settings.langue), + str(int(settings.gyroscope)), + str(int(settings.ambient_sound)), + settings.resolution, + str(int(settings.fullscreen)), + settings.version, + ] + + func _dump_levels() -> Array: + var out: Array = [] + for l in levels: + out.append_array([l.name, l.thumb]) + return out + + func _dump_scenes() -> Array: + var out: Array = [] + for s in scenes: + out.append_array([ + str(int(s.lock)), + s.label, + s.key, + str(s.level), + s.mesh, + s.label_counter, + str(s.counter), + ]) + return out + func initialize() -> DB: return DB.new(save_database_in()) @@ -141,8 +168,8 @@ func normal_path() -> String: func copy_database() -> void: if FileAccess.file_exists(android_path()): return - var src = FileAccess.open(normal_path(), FileAccess.READ) - var dst = FileAccess.open(android_path(), FileAccess.WRITE) + var src := FileAccess.open(normal_path(), FileAccess.READ) + var dst := FileAccess.open(android_path(), FileAccess.WRITE) dst.store_string(src.get_as_text()) dst.close() src.close() diff --git a/scripts/Global.gd b/scripts/Global.gd index 71ef79e..74c538d 100644 --- a/scripts/Global.gd +++ b/scripts/Global.gd @@ -1,77 +1,70 @@ extends Control -@onready var current_scene = null -@onready var current_scene_int = null -@onready var wait_frames = 1 -@onready var database = null -@onready var loaded = false -@onready var animation = Loading.get_node("AnimLoading") +# Application root: async scene loading + reference to the loaded Database. +@onready var animation: AnimationPlayer = Loading.get_node("AnimLoading") + +var current_scene: Node = null +var current_scene_int: int = -1 +var wait_frames: int = 1 +var database: RefCounted = null +var loaded: bool = false var _loading_path: String = "" -func _ready(): +func _ready() -> void: database = load("res://scripts/Database.gd").new().initialize() - _initialize_current_scene() _initialize_loading_scene() -func _initialize_loading_scene(): +func _initialize_loading_scene() -> void: animation.animation_started.connect(Event._loading_is_started) animation.animation_finished.connect(Event._loading_is_finished) -func goto_scene(path): - print("[global#goto_scene] : load scene " + str(path)) - var err = ResourceLoader.load_threaded_request(path) +func goto_scene(path: String) -> void: + var err := ResourceLoader.load_threaded_request(path) if err != OK: - print("Error loading ....") + push_error("[Global] failed to start threaded load for " + path) return - _loading_path = path Loading.show() animation.play("BorderAnim") - set_process(true) current_scene.queue_free() wait_frames = 1 Loading.get_node("LoadingBare/VBoxContainer/HBoxContainer/ProgressBar").set_max(1.0) -func _process(_delta): +func _process(_delta: float) -> void: if _loading_path == "": set_process(false) return - if wait_frames > 0: wait_frames -= 1 - if not loaded: return - - var progress := [] - var status = ResourceLoader.load_threaded_get_status(_loading_path, progress) + var progress: Array = [] + var status := ResourceLoader.load_threaded_get_status(_loading_path, progress) match status: ResourceLoader.THREAD_LOAD_IN_PROGRESS: _update_progress(progress[0] if progress.size() > 0 else 0.0) ResourceLoader.THREAD_LOAD_LOADED: _set_new_scene() ResourceLoader.THREAD_LOAD_FAILED, ResourceLoader.THREAD_LOAD_INVALID_RESOURCE: - print("Error loading ....") + push_error("[Global] failed to load " + _loading_path) _loading_path = "" ## PRIVATE -func _initialize_current_scene(): - print("[global#_initialize_current_scene]") - var root = get_tree().get_root() +func _initialize_current_scene() -> void: + var root := get_tree().get_root() current_scene = root.get_child(root.get_child_count() - 1) - if current_scene.name != "Main": - get_node("/root/Loading").hide() + Loading.hide() -func _update_progress(value: float): +func _update_progress(value: float) -> void: Loading.visible = true Loading.get_node("LoadingBare/VBoxContainer/HBoxContainer/ProgressBar").set_value(value) -func _set_new_scene(): - var resource = ResourceLoader.load_threaded_get(_loading_path) +func _set_new_scene() -> void: + var resource: PackedScene = ResourceLoader.load_threaded_get(_loading_path) _loading_path = "" current_scene = resource.instantiate() get_node("/root").add_child(current_scene) diff --git a/scripts/Setting.gd b/scripts/Setting.gd index 144c896..1a38c18 100644 --- a/scripts/Setting.gd +++ b/scripts/Setting.gd @@ -1,66 +1,69 @@ extends Node -@onready var setting = load("res://db/MSetting.gd").new() +# Reads / writes the SettingsData held by Global.database. Applies locale, +# resolution, and fullscreen state on startup and on any setter call. -func _ready(): +func _ready() -> void: apply_language(translate_int_to_locale(get_setting_language())) apply_resolution() apply_fullscreen() -func apply_language(local): - TranslationServer.set_locale(local) +func apply_language(locale: String) -> void: + TranslationServer.set_locale(locale) -func translate_int_to_locale(id): - var lang = "en" +func translate_int_to_locale(id: int) -> String: + match id: + 0: return "en" + 1: return "fr" + return "en" - if id == 0: - lang = "en" +func apply_resolution() -> void: + var res := get_setting_resolution() + get_window().content_scale_size = Vector2i(int(res[0]), int(res[1])) - if id == 1: - lang = "fr" +func apply_fullscreen() -> void: + get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if get_setting_fullscreen() else Window.MODE_WINDOWED - return lang +func _data() -> SettingsData: + return Global.database.settings -func apply_resolution(): - var res = get_setting_resolution() - var screen_size = Vector2i(int(res[0]), int(res[1])) - get_window().content_scale_size = screen_size +func get_setting_language() -> int: + return _data().langue -func apply_fullscreen(): - get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if (get_setting_fullscreen()) else Window.MODE_WINDOWED - -func get_setting_language(): - return setting.get_langue() - -func set_setting_language(value): - setting.set_langue(value) +func set_setting_language(value: int) -> void: + _data().langue = value + Global.database.save() apply_language(translate_int_to_locale(value)) -func get_setting_gyrosocpe(): - return setting.get_gyroscope() +func get_setting_gyrosocpe() -> bool: + return _data().gyroscope -func set_setting_gyroscope(value): - setting.set_gyroscope(value) +func set_setting_gyroscope(value: bool) -> void: + _data().gyroscope = value + Global.database.save() -func get_setting_ambient_sound(): - return setting.get_ambient_sound() +func get_setting_ambient_sound() -> bool: + return _data().ambient_sound -func set_setting_ambient_sound(value): - setting.set_ambient_sound(value) +func set_setting_ambient_sound(value: bool) -> void: + _data().ambient_sound = value + Global.database.save() -func get_setting_resolution(): - return setting.get_resolution() +func get_setting_resolution() -> PackedStringArray: + return _data().resolution_split() -func set_setting_resolution(value): - setting.set_resolution(value) +func set_setting_resolution(value: String) -> void: + _data().resolution = value + Global.database.save() apply_resolution() -func get_setting_fullscreen(): - return setting.get_fullscreen() +func get_setting_fullscreen() -> bool: + return _data().fullscreen -func set_setting_fullscreen(value): - setting.set_fullscreen(value) +func set_setting_fullscreen(value: bool) -> void: + _data().fullscreen = value + Global.database.save() apply_fullscreen() -func get_setting_version(): - return setting.get_version() +func get_setting_version() -> String: + return "v" + _data().version diff --git a/scripts/event.gd b/scripts/event.gd index e0e810b..b8aaea4 100644 --- a/scripts/event.gd +++ b/scripts/event.gd @@ -22,12 +22,12 @@ static func _on_home_pressed() -> void: Global.current_scene_int = 1 Global.goto_scene("res://scenes/levels/home/Home.tscn") -static func _on_reset_level(level, node: String, index: int, parent) -> void: +static func _on_reset_level(level: LevelEntry, node: String, index: int, parent) -> void: Global.current_scene_int = index level.reset() - parent.configure_reset(level, node, index, true) + parent.configure_reset(level, node, true) parent.configure_counter(level, node) - Global.current_scene_int = null + Global.current_scene_int = -1 static func _on_main_scene_pressed() -> void: Global.goto_scene("res://scenes/Main.tscn")