Adds the etc2 dest path + "etc2_astc" import format to every prop
texture sidecar. Generated by `godot --headless --import` after
flipping rendering/textures/vram_compression/import_etc2_astc to
true in project.godot (commit 46ab28f). Without these sidecars,
re-running --import on a clean checkout would regenerate them
anyway, but committing keeps imported_formats deterministic and
avoids spurious diffs on every CI / dev machine.
39 .import files touched, all under assets/props/. No texture
binaries changed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Godot 4.6 produces an unsigned APK in headless mode on this
runner — verified by apksigner: "DOES NOT VERIFY, Missing
META-INF/MANIFEST.MF". The internal sign step seems to bail
silently (likely because the apksigner subprocess can't locate
java without inheriting JAVA_HOME). The export step still
reports success.
Sign the APK ourselves after the export with the same debug
keystore the workflow provisions. Idempotent if Godot ever
starts signing again; mandatory until then.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two fixes that finally land the Android export. Verified locally
with godot 4.6.2-arch + downloaded 4.6 templates + Android SDK.
- project.godot: add
rendering/textures/vram_compression/import_etc2_astc=true.
Without it Godot refuses the Android export with "La plateforme
cible nécessite une compression de texture « ETC2/ASTC »". In
headless mode this surfaces as an *empty* configuration-errors
block, which is what kept us stuck in CI.
- workflow: write Godot editor settings to
editor_settings-${GODOT_VERSION}.tres (i.e. -4.6.tres on 4.6),
not -4.tres. Godot 4.5+ uses a minor-version-suffixed settings
file; the major-only name is ignored by 4.6, so our injected
android_sdk_path / java_sdk_path / debug_keystore never reached
the export plugin.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Android export preset relied on Godot's $genname placeholder
("org.godotengine.$genname"). With the project name "Puzzle
Quest" containing a space, Godot 4 refuses the headless export
with:
The project name does not meet the requirement for the package
name format and will be updated to "puzzlequest". Please
explicitly specify the package name if needed.
Pin both Android presets (AndroidTesting, AndroidDebug) to
com.devcrea.puzzlequest — clean, follows reverse-DNS, matches
the DevCrea organization.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three follow-ups from run #12:
- export-desktop: comment out the macOS matrix entry. The preset
is still missing in export_presets.cfg; the entry can be
uncommented once the macOS preset is added in the Godot editor.
- export Windows: `application/icon` referenced
res://releases/windows/project.ico, which has never been in the
repo (releases/windows/ holds only .keep). Godot rejected the
export with "Invalid icon path". Cleared the field in both
WindowsDebug and WindowsTesting presets — Godot falls back to
its built-in icon.
- export-android: Godot validation requires all three of
keystore/debug{,_user,_password} to be set together (or all
empty). The keystore step only patched keystore/debug and left
the other two empty, triggering
"Either Debug Keystore, Debug User AND Debug Password must be
configured OR none of them". Patch all three now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three follow-up fixes from the first non-container run (#10):
- lint: Ubuntu 24.04 / Python 3.12 enforces PEP 668, so
`pip install --user` exits with externally-managed-environment.
Install gdtoolkit into a throwaway venv at /tmp/gdlint-venv
instead.
- exports: drop the .godot/ artifact cache shared between
`validate` and the export jobs. Gitea's artifact service returns
404 on actions/download-artifact@v3 (v3 protocol incomplete on
this server). Each export job now runs its own
`godot --headless --import` before exporting — costs ~30s but
avoids the failing cross-job artifact dependency.
- export-android: `yes | sdkmanager --licenses` returned 141
(SIGPIPE) under `set -o pipefail` because sdkmanager closes
stdin after accepting all licenses while `yes` keeps writing.
Feed a finite `printf 'y\n%.0s' {1..50}` instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The first Gitea Actions runs failed at actions/checkout@v4 because
barichello/godot-ci:4.6 ships without Node.js, which the JS-based
checkout action requires. Rather than chase a Godot CI image that
bundles Node, drop the container: blocks entirely: the default
catthehacker/ubuntu:act-latest runner image already has Node /
Python / git / JDK, and Godot is installed per-job from the
official GitHub release.
Pulled the install logic into a local composite action at
.gitea/actions/setup-godot/ to avoid duplicating 15 lines of wget +
unzip across the three Godot-using jobs. Inputs:
- version (default 4.6)
- templates (default false — export jobs flip to true)
Other tweaks:
- export-android now puts $ANDROID_HOME under $GITHUB_WORKSPACE so
no sudo is needed; editor_settings-4.tres interpolates that path.
- export-android writes the keystore under /tmp instead of /root
(catthehacker runners don't run as root).
README updated: jobs table reflects the new "Tooling installed by the
job" column, prerequisites no longer mention the Docker image, and
known-issue #1 is closed out with the dated fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a "Known issues from first runs" section to the workflow
README capturing what broke on 2026-05-17:
- Container jobs (validate, export-*) fail at actions/checkout@v4
with `node: not found` because barichello/godot-ci:4.6 has no Node.
Workflow fix pending.
- The earlier `actions/checkout` URL-prefix problem (Gitea behind
YunoHost at /gitea/) is now resolved server-side by moving Gitea
to the root; the note records this for future reference.
- Default-branch mismatch (Gitea = main, CLAUDE.md says dev).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Temporary addition so the workflow can run before the branch is
merged into dev. Remove this branch from the push: list once dev
has the workflow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Global.gd: move @onready var animation below the regular vars
(gdlint class-definitions-order expects onready vars after
public/private vars).
- Database.gd::DB: move the three _*_PROPS constants above the vars
(constants come before vars in a class body).
- Levels.gd:_search_button_to_use: drop the elif after a branch
that returns (no-elif-return).
- Levels.gd:_gyroscope_changed_{down,up}: continuation lines of the
multi-line return mixed two tabs + three spaces; normalised to
pure tabs.
No semantic change: @onready is property-level so source ordering
doesn't affect init; elif after a return is equivalent to if; the
continuation indent is cosmetic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lints scripts/, db/, scenes/ via gdtoolkit==4.* on ubuntu-latest
(Python, no Godot needed), in parallel with the validate job.
addons/ (third-party LOD plugin) and developers/ (sandbox) are
left out. Non-blocking for now: the export jobs still only need
validate, so a lint regression won't break builds while the
Godot-3 leftover code is being cleaned up.
gdlintrc bumps max-line-length from 100 to 140 because Godot
$-style node paths and typed signatures routinely push past 100
without that being a real readability problem.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
The developers/aurelien sandbox scenes still reference legacy non-LOD
mesh paths (sm_book.mesh, sm_candlestick.mesh, ...) that don't exist
anymore — the production scenes were migrated to *_lod0/1/2.mesh.
Godot's full-project file scanner reads every .tscn at editor open
and emits 'Cannot open file' errors for each broken ext_resource,
even for scenes that the game never loads.
Mark developers/ as out-of-scope for Godot's filesystem walker with
a .gdignore at its root. The .import / .uid sidecars Godot had
generated for files inside (CheckLightmap.exr.import,
CheckLightmap.gd.uid) are auto-removed by the editor since they are
now orphaned metadata — only the source files (.tscn, .gd, .exr,
.lmbake) remain. Re-enabling the sandbox later just means deleting
the .gdignore; Godot will regenerate the sidecars.
The other diffs in this commit are Godot 4 canonicalising whatever
.tscn / .tres files were touched in the editor session that ran the
LightmapGI bake attempt: ext_resource uid= attributes added, format
bumped 2 -> 3, property order normalised. No behavioural change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- scenes/levels/Levels.gd: replace 'var level := Global.database
.level_by_index(...)' with explicit 'var level: LevelEntry = ...'.
Global.database is typed RefCounted so the parser can't see DB's
return types through the walrus operator. Two callsites.
- scripts/Database.gd: annotate the two 'range(data.size() / W)'
loops with @warning_ignore('integer_division'). The division is
intentional (row count = bytes / row width); Godot 4 warns by
default in case the slash was a typo.
- env: ambient_light_energy 0.4 -> 1.0 in WarCraft.tscn, Home.tscn
and env_warcraft.tres. 0.4 left the floor pitch-black; 1.0 is a
compromise between the original 1.55 (oversaturated) and this.
Re-baking the lightmap in the editor is still the right fix —
this commit just keeps the scene playable in the meantime.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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) <noreply@anthropic.com>
Two of the five autoloads were stateless helper bundles, not the
persistent application-wide systems that the Godot 4 best-practice
guide reserves autoload slots for. Convert them to plain classes with
all-static methods so they no longer need a Node living under /root.
- scripts/Animation.gd -> scripts/game_animation.gd (renamed:
conflict with the built-in Animation class; the autoload sidestepped
it by being called GlobalAnimation). class_name GameAnimation,
every helper static. The dissolve tween now captures its target
material in a lambda closure, eliminating the current_material
member that the autoload kept as shared mutable state.
- scripts/Event.gd -> scripts/event.gd, class_name Event, every
handler static. Added Event.level_pressed(name) -> Callable for the
one place ChooseScene needed dynamic lookup of a per-level handler
(replaces the old _build_method('_on_' + name + '_pressed') +
Callable(autoload, string) reflection trick with an explicit match).
Updated call sites:
- scripts/Global.gd: animation.connect('animation_started',
Callable(Event, '...')) -> animation.animation_started.connect(
Event._loading_is_started). Same for animation_finished.
- scenes/levels/Levels.gd: GlobalAnimation.* -> GameAnimation.*;
Quit/TextureButton.connect('pressed', Callable(Event, '...')) ->
pressed.connect(Event._on_main_scene_pressed).
- scenes/UI/choose_scenes/ChooseScene.gd: per-level button connects
go through Event.level_pressed(); reset button's bound callable now
reads Event._on_reset_level.bind(...); cleanup no longer relies on
is_connected with an unbound Callable (which would never match a
bound one anyway) -- iterates reset.pressed.get_connections()
instead. Drop the now-dead _build_method helper.
Also: organise scripts/ by promoting the one-shot migration helpers
(migrate_shaders, migrate_misc, resave_scenes, devisualize_shaders,
find_visualshaders) into scripts/migration/. They are not part of
the runtime and should not be browsable next to the autoloads.
Autoload count goes 5 -> 3 (Loading, Global, Setting remain --
Setting will move to a Resource in a later refactor).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Godot 4 emits two unavoidable warnings every time it reconstructs a
Godot-3-authored VisualShader graph: 'graph_offset property is
deprecated' (the editor's pan position, removed in 4.x) and
'connect_nodes_forced: Index p_from_port = 1 is out of bounds'
(node-graph ports that no longer line up). The compiled GLSL inside
each ShaderMaterial is correct; only the graph reconstruction is
broken.
scripts/devisualize_shaders.gd walks every .material, .tres, and
.mesh under res:// and replaces ShaderMaterial.shader from
VisualShader to a plain Shader carrying the same .code. 15 materials
converted (font outline, dissolve, color tints, book/candle/godet/
growler/parchment/rock-floor/stool/table). Godot now loads the
compiled shader directly with no graph reconstruction → both
warnings gone, rendering identical.
Also:
- scripts/resave_scenes.gd: load + re-save every .tscn so inline
ArrayMesh sub-resources (notably WarCraft.tscn::17, the baked
floor) move from Godot-3 PoolByteArray to Godot-4 PackedByteArray
surface format. Silences 'Mesh uses old surface format'.
- scripts/find_visualshaders.gd: companion audit tool that lists
every Resource still backed by a VisualShader. Useful if new
legacy materials get added.
- Drop the now-orphan inline VisualShader / ShaderMaterial sub-
resources (id 1..17) from Summary.tscn. The PanelWood lost its
screen-blend tint material; a plain Panel renders fine and the
effect can be re-authored as a hand-written shader if wanted.
- Add releases/.gdignore and tighten .gitignore so Godot stops
warning 'Detected another project.godot at res://releases'. The
releases/ directory has its own placeholder project.godot which
Godot would otherwise flag at every editor open.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Summary.tscn was correctly set up (Label anchored to full button rect,
horizontal/vertical_alignment = 1). But Main.tscn — which instances
Summary.tscn as UI_summary — had per-Label overrides that reset the
anchors back to 0 and shrunk the Label rect to 1x23 px (legacy from
Godot 3 where margin-based positioning was the default), making the
text render in the top-left 1 px corner.
Drop the layout_mode=0 / anchor_right=0 / anchor_bottom=0 / offset_*
overrides on each ButtonPuzzle/Setting/Credits/Quit Label so they
inherit Summary.tscn's full-rect anchors and the centered alignment
becomes visible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In Godot 3 the TextureRect stretch_mode enum included a deprecated
"Scale on Expand" at value 0, so Tile=2. Godot 4 dropped that value
and shifted everything down: Tile is now 1. The convert-3to4 tool
does not remap enum values, so the separator at the right edge of
the menu kept Godot 3's Tile=2, which Godot 4 reads as Keep — the
texture rendered once at natural size at the top instead of tiling
down the full menu height.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Discovered while playtesting the migrated build. Each fix has a
specific Godot-3-to-4 cause that the --convert-3to4 tool did not catch.
Fonts:
- Replace 4 .tres font wrappers (MKX_Base, kirsty_base/medium/title)
with FontVariation pointing at the .otf/.ttf source. Godot 3
DynamicFont with size= and font_data= is invalid in Godot 4 (those
properties don't exist on FontFile).
- Apply size 46 via theme_override_font_sizes/font_size on the 5
Labels that used kirsty_title.tres (LabelLoading + 4 in Summary).
- Drop the broken VisualShader text_outline.material from the 4
Labels in Summary — Index p_from_port errors in Godot 4 meant
COLOR was never written, so text rendered invisible.
- Migrate VisualShader nodes inside text_outline.material (compressed
asset; updated by the inspect/migrate scripts).
Translations:
- Move project.godot section from [locale] (Godot 3) to
[internationalization] with locale/translations=... (Godot 4). The
old section was silently ignored so tr() returned the raw msgid.
- Adjust Setting.translate_int_to_locale to return "en"/"fr" to
match what the .po files declare (was "en_GB"/"fr_FR", which Godot
3 fell back from automatically but Godot 4 does not).
Label alignment (Godot 3 names not auto-renamed):
- align -> horizontal_alignment, valign -> vertical_alignment across
9 .tscn files.
Background / Loading / ChooseScene layout:
- Rewrite Background.tscn from a VisualShader-textured Panel to a
plain TextureRect (the visual shader connection ports went out of
bounds in Godot 4, leaving the panel grey on scene reload).
- Set layout_mode=1 + anchors_preset=15 on BackgroundPicture
instances in Main.tscn and Loading.tscn — Godot 4 inheritance no
longer applies the .tscn-root's anchors to an instanced child
unless layout_mode is set to Anchors mode explicitly.
- Replace scroll_horizontal_enabled (Godot 3) with
horizontal_scroll_mode/vertical_scroll_mode (Godot 4) on
ChooseScene's ScrollContainer.
- Add theme_override_styles/panel = StyleBoxEmpty on the
ScrollContainer (Godot 4 ScrollContainer ships a default dark
panel style that Godot 3 did not).
- Hide BackgroundTile in Template.tscn — it points at
UI-level-btn-shadow.png (228x228, 83% opaque black) which used to
render at natural size in Godot 3 but is stretched to fill the
456px rect in Godot 4, leaving a big black square below each
thumbnail. The proper drop-shadow needs a shader or 9-patch.
- TextureRect.expand=true (Godot 3) -> expand_mode=1 (Godot 4) on
ThumbnailLevel and TopPart's content rects.
Lighting:
- background_mode 3 (which used to mean Sky in Godot 3) -> 2 (Sky in
Godot 4; 3 is now Canvas, producing a black background).
- background_sky -> sky, background_energy -> background_energy_multiplier,
fog_color -> fog_light_color, fog_height_min -> fog_height; drop
fog_height_enabled / fog_height_max / fog_height_curve /
dof_blur_far_distance (no longer exist).
- Add fog_mode = 1 so Godot 4 uses fog_depth_* instead of the new
default fog_density-based exponential fog (which rendered as a
thick white cloud).
- ambient_light_energy 1.55 -> 0.4 — the new pipeline interprets
the value much more strongly so 1.55 produced a saturated pink scene.
- Remove the light_data = ExtResource(41) reference to
WarCraft.lmbake — Godot 3 baked lightmap binary format is
incompatible with Godot 4. Re-bake in editor when ready.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Catch-all commit for everything the --convert-3to4 tool missed during a
manual playtest of the game. All errors raised by clicking through Main
-> Puzzles -> level were fixed.
GDScript:
- PackedScene.instance() -> instantiate() (ChooseScene.gd)
- String(x) constructor doesn't exist -> str(x) (MBase, MScene,
MLevel, Animation, Levels)
- 'x as int/String/bool' doesn't parse strings -> explicit
int()/str()/bool(int()) (MScene, MLevel, MSetting)
- BaseButton.pressed (property) -> button_pressed; set_pressed() ->
direct assignment (Settings.gd)
- AnimationPlayer.add_animation() removed -> go through
AnimationLibrary (Levels.gd)
- PhysicsDirectSpaceState3D.intersect_ray(from, to, ...) ->
PhysicsRayQueryParameters3D.create() (Levels.gd)
- @export with type-hint-in-comment ('# (String, ...)') -> explicit
@export_enum (candle.gd)
- Get effective material with get_active_material() instead of
get_surface_override_material(), with null guard (Levels.gd)
- get_node() -> get_node_or_null() so missing items from ahog.json
(e.g. sm_super_dager in Home) don't crash (Levels.gd)
Scenes/resources:
- Remove 14 Tween nodes from WarCraft.tscn — Tween is no longer a
Node in Godot 4. Rewrite Animation.start_dissolve to use
create_tween().tween_method().
- Rename property material/N -> surface_material_override/N in every
.tscn (10 files) — Godot 3 -> 4 rename that --convert-3to4 missed.
Without this, MeshInstance3D.get_active_material(0) returned the
glTF-imported StandardMaterial3D instead of the project's custom
dissolve ShaderMaterial.
Shaders:
- One-shot scripts/migrate_shaders.gd walks every .material under
assets/ and fixes Godot 3 -> 4 shader code in-place. Fixed 17
materials: depth_draw_alpha_prepass -> depth_prepass_alpha,
hint_color -> source_color, NORMALMAP -> NORMAL_MAP.
Result: Main, Settings, ChooseScene, and the WarCraft level all run
without script or shader errors. Remaining noise is non-blocking
(visual_shader graph in text_outline.material, baked lightmap binary
format from Godot 3, and empty animation tracks).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
godot_db_manager was incompatible with Godot 4 (used WindowDialog, Tabs,
PopupPanel which were all removed). Replace with a minimal Database.gd
that parses the same ahog.json format and exposes the same surface API
(get_table_by_name, get_data_at_row_idx, edit_data, save_db, etc.) used
by the M* model classes — no changes needed in MBase/MScene/MLevel/MSetting.
Also port the lod plugin: fix class_name syntax (Godot 4 uses @icon
separately from class_name extends) and Particles -> GPUParticles3D.
Rewrite Global.gd async scene loading: the convert-3to4 tool mapped
load_interactive -> load_threaded_request but those have different APIs
(stage count, poll vs. status enum). Reimplement using the new
load_threaded_get_status / load_threaded_get pair.
Clean project.godot: drop the old _global_script_classes table (Godot 4
uses inline class_name declarations), remove gddb_* autoloads, and
remove the godot_db_manager entry from editor_plugins.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Open project with Godot 3.5.3 to update project.godot to the new
config format (adds physical_scancode field to InputEventKey).
Intermediate step before --convert-3to4 to Godot 4.x.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>