Dev Log: 2026-05-29¶
Branch: rc/v0.1.3-dev
Achievement system¶
A full achievement system landed across several commits. Achievements are
defined in a data file and tracked by AchievementManager against named
stats. Three types are supported: cumulative (e.g. total drones killed),
level-based (unit reaches a given level), and chapter-completion. Newly
unlocked achievements surface on the stage result screen and are viewable
in a dedicated dialog. Steam achievements are wired up via the Steamworks
SDK: the achievement definitions carry a Steam key and AchievementManager
calls the Steamworks API on unlock.
A progress display bug found during playtesting was fixed: the cumulative and level counters were showing raw stat values (e.g. 7/1 when the player had killed seven drones but the threshold was one). Both counters are now capped at the threshold, so progress always reads at most N/N.
A standalone Python package (steam_achievement_bulk_upload) was extracted
from the local upload script and wired into the tooling so achievement
metadata can be pushed to Steamworks without the Godot editor running.
AA/AG/both icons on drone sprites¶
Each combat drone now shows a small badge icon in its lower-right corner
indicating its target type: an upward arrow for anti-air, a downward arrow
for anti-ground, a diamond for all-targets. The badges are generated by
ui_gen.py as 14x14 pixel-art sprites with a 1px black shape outline.
In a follow-up pass the badge colors were aligned to the range overlay
colors: the anti-air badge now uses the same magenta-pink as the AA range
fill, the anti-ground badge uses the reddish-orange of the AG range fill,
and the all-targets badge uses the all-range red. This creates a direct
visual link between the corner icon and the colored zone the drone projects
on the map. The Canvas.shape_outline() helper was added to canvas.py
to support the pixel-accurate outline expansion.
Tutorial text in Chapter 1 stages 2 and 4 was updated to introduce the icon and connect it to the range color.
Per-cell AG/AA/both range coloring¶
The threat-range overlay now colors each cell by which unit types it threatens rather than using a single color for all covered cells. Cells threatened only by anti-ground drones show orange, anti-air-only cells show pink, and cells threatened by both show red. This makes it possible to read the full threat map without inspecting individual drones.
When a unit is selected, the coloring switches to that unit's mobility class so the overlay reflects only the threats relevant to the active unit.
Range stays visible after drone inspect¶
After closing a drone's info panel, its range overlay now remains on screen so the player can study it unobstructed by the dialog. Tapping anywhere (empty cell, a unit, another drone) dismisses the locked range and returns to the default all-drones view. This was a frequent request from playtesting: the info panel and the range it was covering were the two things players most needed to see at the same time.
Tutorial text in stage 1 was updated to teach the behavior on first contact with the long-press inspect.
Limit Break button in unit detail dialog¶
The unit detail/inspect panel now shows a Limit Break button at the very
top of the dialog (above the scroll area) when the selected unit's limit
gauge is full. Tapping it closes the panel and enters limit break targeting
mode directly. This addresses a common mobile friction point: players were
accidentally opening the info panel instead of the HUD limit break button,
then having to close it manually before the HUD button became reachable.
The button uses the existing limit_break_requested signal path so no new
stage-controller logic was needed.
The German translation of "Limit Break" was corrected across 13 strings: the previous translation "Limitbruch" is not conventional German; the correct form is the English loanword "Limit Break", which is standard in German-speaking game communities.
View action log on web¶
On web builds the "Export Action Log" button (stage result screen) is replaced by "View Action Log", which shows the JSON in a read-only text overlay with a copy-to-clipboard button. File saving is not available on web, so the native save dialog was not a usable option. Desktop builds are unchanged.
Smoother animations¶
Unit sprites now face the direction of travel before a projectile fires.
A turn_to_face() method was added to unit.gd, which plays a brief
rotation to the correct cardinal direction. The projectile table in
stage_controller.gd was extended to cover all typed drone variants
(interceptor_ag, interceptor_aa, cruiser_ag, cruiser_aa, guard_tower_ag,
guard_tower_aa) that were previously falling through to a default.
Map border extension¶
The playable grid is now visually extended left and right with ghost terrain columns so the background does not cut off abruptly at the playfield edge when the camera is zoomed out. Ten ghost columns are stamped on each side using the edge terrain type. The extension is generated at stage load time and requires no changes to stage data.
Drag-and-drop cooldown¶
A 1-second cooldown was added after a drag-to-route gesture ends. During
the cooldown the tap-to-confirm shortcut is suppressed so players can lift
their finger after a drag without accidentally committing the route before
reviewing it. The timestamp is stored on StageController and checked by
both confirm paths.
Compact pause and options menus¶
The pause menu and options menu were slimmed down. Redundant spacing was removed and several controls were tightened. The options menu layout was simplified from a multi-column grid to a single-column flow that works better at both desktop and mobile viewport sizes.
Multi-reflex-attack bug fix¶
A bug caused a unit to fire multiple reflex counter-attacks against the
same drone in a single reactive turn when the drone moved through several
cells in range. The fix tracks which drones have already been countered
within a single player action and skips repeat triggers. Regression guard
tests were added to test_reflex_counter.gd and solver tick.py was
updated to match.
Text clipping fixes¶
Two text clipping issues found on low-resolution test devices were fixed:
- Achievement grid tiles were overflowing their cells at 5 columns; the grid was reduced to 4 columns and achievement name labels got ellipsis overflow handling.
- The stage objective banner was clipping the bottom of descender glyphs
at 26px font size. Vertical padding was increased and the label received
a
custom_minimum_sizeheight to guarantee enough layout space.
Crash fix: freed drone reference¶
GridManager.get_occupant was crashing when a drone was freed (via
queue_free) while still referenced in the _occupied dictionary. The fix
adds an is_instance_valid check that auto-evicts stale entries on read, so
the dictionary self-heals without requiring callers to manually unregister
every destroyed node.
HUD button sizing¶
HUD route-control buttons (Confirm, Queue, Cancel) now size themselves from
actual font metrics rather than hardcoded pixel widths. A _btn_natural_w()
helper measures the button text plus icon and pads to a minimum. This fixed
the German "Überspringen" skip button being clipped at the right edge in
the cutscene player, and ensures button widths remain correct if font or
text changes later.
Minor¶
- Solver carry-state JSONs for Chapters 1 and 2 were refreshed after the reflex-attack fix changed outcome XP on several stages.
- Translation overlays for all three locales were audited and missing or outdated keys were filled in.
- Doc structure was updated to reflect the confirmed six-chapter scope.