# Puzzle Quest Hidden-object game built with **Godot 4.6** (single renderer: Forward+). Branched from a Godot 3.3 codebase — migration is recent, expect leftover Godot-3-isms to occasionally surface. ## Run ``` godot --editor # open project in the editor godot # run the main scene (scenes/Main.tscn) ``` The system package `godot` (Arch `extra/godot`) is the project's Godot. There is no in-repo Godot binary. ## Architecture **Autoloads** (declared in `project.godot [autoload]`, loaded in this order): | Name | Source | Role | |------------------|---------------------------------------|-------------------------------------------------------------------| | `Loading` | `scenes/UI/loading/Loading.tscn` | Full-screen transition overlay (BG + animated border + progress) | | `Global` | `scripts/Global.gd` | Async scene loader (`goto_scene(path)`) + `database` reference | | `Setting` | `scripts/Setting.gd` | Reads/writes the settings table; applies locale/resolution/fullscreen | | `Event` | `scripts/Event.gd` | Static handlers connected to scene buttons (Warcraft / Home / reset / back) | | `GlobalAnimation`| `scripts/Animation.gd` | Tween-based dissolve + HUD slide/warning animations | **Database (`scripts/Database.gd`)** - Plain JSON at `db/ahog.json`. Three tables: `settings`, `levels`, `scenes`. - On Android the file is copied once to `user://database.json` and written there (game saves modify lock state per item). - `Global.database` is a `Database.DB` instance exposing `get_table_by_name(name)` and `save_db()`. Each `Table` exposes `get_data_at_row_idx(row_id)`, `edit_data(prop_id, row_id, value)`, `m_rows_count`, `get_data_by_prop_name_and_data(prop_name, value)`, `get_dictionary_by_prop_name_and_data(prop_name, value)`. - `db/M*.gd` are typed accessors (MBase / MScene / MLevel / MSetting) that read rows via the API above — extend these, not the JSON shape directly. **Scene transitions** (`Global.goto_scene(path)`) Uses `ResourceLoader.load_threaded_request` + `load_threaded_get_status` + `load_threaded_get` (Godot 4 API; the original `load_interactive`/`poll()` was removed). The Loading overlay (autoload) plays `BorderAnim` while the new scene loads in a background thread; `Event._loading_is_started/finished` flip `Global.loaded` so the poll only runs once the entry animation has set the boolean. Don't call `current_scene.queue_free()` outside this function — it will leak the resource and break the loader state. **Levels.tscn / level scenes** `scenes/levels/Levels.tscn` is the shared shell; concrete levels (`warcraft/WarCraft.tscn`, `home/Home.tscn`) inherit from it. Each scene has a `HiddenObjectsItems/` `MeshInstance3D` with an `Area3D` collision shape; clicking it triggers `Levels._check_collider` → `_start_dissolve(name)` → `GlobalAnimation.start_dissolve(mesh, material)` which runs the dissolve shader on `material.dissolve_amount`. The runtime hidden-object list lives under `scenes/levels/parts/ListObjects.tscn` and is populated dynamically from the `scenes` table for the current `Global.current_scene_int`. ## Migration notes (Godot 3 → 4) The conversion is mostly done but a few classes of mistake keep surfacing because `godot --convert-3to4` does not handle them: - **Label**: `align/valign` → `horizontal_alignment/vertical_alignment`. - **Button**: `pressed` (property) → `button_pressed`; `set_pressed()` → assignment to `button_pressed`. The `pressed` *signal* still exists. - **ScrollContainer**: `scroll_horizontal_enabled = false` → `horizontal_scroll_mode = 0` (and same for vertical). ScrollContainer also ships a non-empty default panel style in Godot 4 — set `theme_override_styles/panel = StyleBoxEmpty` if you need transparency. - **TextureRect / TextureButton**: `expand = true` → `expand_mode = 1`. `stretch_mode` enum values **shifted down by 1** between 3 and 4 (the deprecated "Scale on Expand" at index 0 was removed): G3's Tile (2) is G4's Tile (1), G3's Keep (3) is G4's Keep (2), etc. - **Environment**: `background_mode` enum changed (G3 3=Sky → G4 2=Sky; G4 3 is Canvas which renders black). `background_sky` → `sky`; `background_energy` → `background_energy_multiplier`; `fog_color` → `fog_light_color`; `fog_height_min` → `fog_height`; `fog_mode = 1` (Depth) needed to keep `fog_depth_*` semantics, otherwise Godot 4 falls back to dense `fog_density` exponential fog. - **AnimationPlayer**: `add_animation(name, anim)` removed — go through `get_animation_library("")` (create one with `add_animation_library("")` if it returns null) and call `lib.add_animation(name, anim)`. - **Tween**: no longer a Node — call `create_tween()` from any Node and use `tween.tween_method(...)`. Delete any `[node type="Tween"]` from older `.tscn` files (the loader will crash otherwise). - **PhysicsRayQuery**: `space_state.intersect_ray(from, to, ...)` → `PhysicsRayQueryParameters3D.create(from, to)`, set fields, then `space_state.intersect_ray(query)`. - **PackedScene.instance()** → `instantiate()`. - **String / cast operators**: `String(x)` constructor doesn't exist — use `str(x)`. `x as int/String/bool` does **not** parse strings — use `int(s)`, `str(x)`, `bool(int(s))`. - **Internationalization**: project setting is `[internationalization] locale/translations=...`, not `[locale] translations=...`. Locale codes must match `.po` filenames exactly (`fr.po` → `"fr"`; G3-style `fr_FR` no longer auto-falls-back). - **Inherited scenes**: nodes from an instanced child scene need `layout_mode = 1` + `anchors_preset = 15` explicitly set in the parent scene file, otherwise Godot 4 treats them as Position-mode and zeroes the anchors. Watch for stale per-Label `layout_mode = 0` / `anchor_right = 0` overrides in Main.tscn-style parent scenes. ## Plugins - `addons/lod/` — Calinou's Level-of-Detail plugin (Spatial / OmniLight / SpotLight / Particles → 3D variants). Class declarations use the Godot 4 form (`@icon("...") class_name X` + `extends Node3D` separately). - `addons/godot_db_manager/` — **removed during migration**. Don't restore it; it relies on `WindowDialog`/`Tabs`/`PopupPanel` which were dropped in Godot 4. The replacement is `scripts/Database.gd` (see above). ## Custom shaders / materials Drop-in shaders live in `.material` resources (binary `RSCC` format) next to each prop. `scripts/migrate_shaders.gd` is a one-shot tool that walks `assets/` and rewrites the embedded shader code (Godot 3 → 4 keyword renames: `depth_draw_alpha_prepass`, `hint_color`, `NORMALMAP`). Re-run it with `godot --headless --script scripts/migrate_shaders.gd` after adding new materials authored in Godot 3. `assets/fonts/text_outline.material` is a VisualShader graph from Godot 3 that doesn't connect cleanly in 4 (out-of-bounds `p_from_port` errors, COLOR never written). It has been detached from the Summary menu labels; re-author it with Godot 4's built-in `theme_override_constants/outline_size` + `theme_override_colors/font_outline_color` instead. ## Known visual issues to revisit - **Baked lightmap is gone.** `scenes/levels/warcraft/WarCraft.lmbake` is in Godot 3's binary format (incompatible). The `LightmapGI` node still exists in `WarCraft.tscn` but its `light_data` reference is cleared. Open the scene in the editor and re-bake (Scene → Bake Lightmaps). - **`assets/ui/themes/tab_select/UI-level-btn-shadow.png`** is 83% opaque black with feathered edges (a drop-shadow texture). In Godot 4 it renders stretched and opaque, which looked like a black square below each level tile, so its `BackgroundTile` TextureRect is currently `visible = false`. Replace with a proper 9-patch / shader if the shadow effect is wanted. - `developers/aurelien/` is a sandbox — files there (`ui_scrolls.tscn`, `ui_tile.tscn`, `CheckLightmap.tscn`) still use Godot-3 property names in places. Not on the main flow, low priority. ## CI Gitea Actions workflow at `.gitea/workflows/build.yml`, documented in `.gitea/workflows/README.md`. Three jobs: GDScript validation (`godot --headless --import` + error grep), desktop matrix (Windows / Linux / macOS), and Android. Build only — no Butler / itch.io deploy currently wired (channels used historically were `dev-crea/ahog:windows|android|linux|mac`). Drone pipeline removed. Branches: default `dev`, releases from `main`. Long-running migration work on `feature/godot-migration`. ## Conventions - Don't commit `db/ahog.json` runtime mutations (lock progress saved during play). The README documents using `git update-index --skip-worktree` but the cleaner alternative is just to `git checkout db/ahog.json` before staging. - `android/`, `.godot/`, and `releases/` are in `.gitignore`. The `.import` sidecars next to assets **are** committed (they carry the stable UIDs Godot 4 generates). - Commit messages: imperative, English, explain the *why* (especially for migration commits — the next person reading the diff won't have the Godot 3 context).