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

9.1 KiB

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/valignhorizontal_alignment/vertical_alignment.
  • Button: pressed (property) → button_pressed; set_pressed() → assignment to button_pressed. The pressed signal still exists.
  • ScrollContainer: scroll_horizontal_enabled = falsehorizontal_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 = trueexpand_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_skysky; background_energybackground_energy_multiplier; fog_colorfog_light_color; fog_height_minfog_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).