Files
Vaillant Jeremy 410c135870 Replace Drone CI with Gitea Actions workflow
Drone server is gone; CI now lives in .gitea/workflows/build.yml.
Three jobs: GDScript validation (godot --headless --import + error
grep), desktop matrix (Windows / Linux / macOS), and Android (JDK 17
+ SDK installed at runtime, keystore from ANDROID_KEYSTORE_BASE64
secret or generated). Build only — Butler / itch.io deploy not wired.

Notes in .gitea/workflows/README.md cover the Godot-4 pre-reqs
(macOS preset to add, Linux/X11Debug likely renamed on first 4.x
save, Docker image tag) and how to plug Butler back in later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 12:36:34 +02:00

174 lines
9.1 KiB
Markdown

# 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/<Item>` `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).