Files
puzzle-quest/CLAUDE.md
T
Vaillant Jeremy 81e6ceb003 Silence Godot 4 editor warnings (debugger)
Address every warning that surfaced in the running game's debugger:

- Drop deprecated 'graph_offset = Vector2(...)' lines from the three
  in-tree VisualShader resources (red.tres, green.tres,
  Summary.tscn). The property is editor-only graph pan, ignored at
  runtime but warns at load.
- Add android/.gdignore so Godot stops scanning the build template
  copies of red.tres/green.tres (which still had graph_offset). Tighten
  .gitignore to keep tracking the .gdignore marker only.
- Drop the broken 'nodes/fragment/connections = ...' line from the
  inline VisualShader in Summary.tscn — connections referenced
  out-of-bounds ports (e.g. port 1 on a 1-output node). The
  pre-compiled 'code = ...' string is kept so rendering is unaffected.
- Drop the orphaned 'ext_resource WarCraft.lmbake' from WarCraft.tscn:
  the LightmapGI node no longer references it but Godot still loaded
  the (Godot-3-format) blob from the ext_resource declaration alone,
  triggering '(p_data.size() % 4) != 0'.
- Animation tracks: SlideReset (Template.tscn), BorderAnim
  (Loading.tscn), and ObjectFindAll (ListObjects.tscn) each had a
  bezier track with empty PackedFloat32Array keys, which
  AnimationMixer rejects in Godot 4. Drop the empty x track in each
  (the y track held the actual motion).
- Re-save 57 .mesh files via scripts/migrate_misc.gd so the surface
  format is the current Godot 4 variant. sm_stackgold.mesh in
  particular triggered the deprecation warning every load.

GDScript: rename function parameters and locals that shadowed
Node.name / Node.value in:
- db/MScene.gd (set_lock, set_mesh, set_tick_reference, set_value)
- scripts/Setting.gd untouched (no shadow, false positive earlier)
- scenes/UI/choose_scenes/ChooseScene.gd (_load_scene, _build_path,
  _build_method)
- scenes/levels/Levels.gd (_create_animation_slide,
  _create_animation_warning, _add_animation_to_player,
  _create_button_info, _node_object_list)

lod plugin: Godot 4 added 'lod_bias' as a native property on
VisualInstance3D, which collided with the plugin's 'lod_bias' member
across all five lod_*.gd files. Rename to 'lod_distance_bias' so the
scripts parse again. Also drop Light3D.shadow_color writes in
lod_omni_light.gd and lod_spot_light.gd — that property was removed
in Godot 4, so the related shadow_value computation became dead code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 21:28:22 +02:00

9.0 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

Build pipeline is in releases/.drone.yml. The Docker images still pin barichello/godot-ci:3.3.2 and a custom devcrea/godot-ci:3.3.2-androidbump these to a 4.x image before relying on CI builds again. Butler push targets dev-crea/ahog:windows|android|linux|mac on itch.io.

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).