Dev Log: 2026-10-06¶
Build 0.1.9.110. Yesterday's keyboard work left a few gaps that a tester pointed out, so the first half of this post closes them: limit breaks, WASD, starting a stage, and aiming Nadeshiko's and Maria's limit breaks. The second half is about what happened when I let an AI playtester loose on the web build. It filed a long list of suspected bugs, and the ones with clean reproductions turned out to be real.
The keyboard, finished¶
Yesterday ended with the shortcuts covering selecting, routing, queueing, undo, aiming, and zoom. Four holes remained, and each was a request from someone who had actually tried to play with their hands on the keys.
L fires a limit break. With a unit selected, it fires that unit's limit break, if the gauge is full. With nothing selected it picks the first ready one in the order Katyusha, Nadeshiko, Maria. Like every shortcut, it presses the same gauge button the mouse does, so the confirmation dialog, the tutorial blocks, and the "gauge hidden in this mode" cases all behave exactly as before.
WASD as a second set of arrows. This is the one gamers expect, and it turned out to need a small design decision. The settings model holds one key per action, so rather than turn it into a list of keys per action, I added four extra actions (left, right, up, down alternates) and registered each as an additional key on its arrow action. They show up as four more rows in the Controls page, each remappable on its own, and everything that listens for the arrows (routes, the aiming cursor, queue browsing, the world map) gets them for free.
Starting a stage from the keyboard. A tester asked how to launch a stage without the mouse. I reused the target key: on the world map, T or Tab selects the next unlocked stage and draws a pulsing cyan frame around it, and E or Enter starts it exactly as clicking the tile would. The order is stage number, one to ten, wrapping around and skipping locked stages, so with only one stage unlocked it simply stays selected. The first press lands on the first stage you have not cleared in the current mode, which is almost always the one you want.
Building it surfaced an older bug. The world map's check for "is a popup open" only looked at its direct children, and the confirm dialog lives on another layer. So the existing chapter and difficulty keys could fire behind an open confirmation. It now checks the dialog too.
Aiming limit breaks. Nadeshiko's and Maria's limit breaks ask for a target cell, which until now meant the mouse. The arrow keys and WASD now move a cursor one cell at a time over any terrain, including the water and mountains the unit could never cross. Whenever the cursor lands on a cell the break can hit, either directly or after the caster repositions, that cell becomes the previewed target, exactly as tapping it would. Cells nothing can reach just move the cursor, so you can steer past them without a stream of "out of range" notices. E fires, C backs out one step at a time.
if next in _limit_target_cells or hits_drone \
or _approach_for_lb_target_cell(selected, next) != Vector2i(-1, -1):
if phase == Phase.LIMIT_TARGET:
_handle_limit_target_click(next)
else:
_handle_limit_preview_click(next)
The trick was reusing the click handlers rather than writing a second targeting path. One wrinkle: stepping back onto the current target would count as a second click and fire the break, so that case only re-previews.
An AI playtester¶
I let an automated playtester, an AI agent driving the itch.io web build through a simulated desktop, play chapters 1 and 2 and keep a running bug list as a GitHub issue. It was prolific, and a mixed blessing. Many of its reports were really about how a bot types: keys pressed during animations are ignored, a second click at the same coordinates lands on whatever is under it next, and so on. I wrote the bot a reply sorting its list into fixed, by design, and "I need real reproduction steps", with a required format: a save code taken right before the first step, a numbered input list with the info line quoted after each step, and before and after screenshots.
That last part paid off at once. Within hours it filed a handful of tight reproductions with numbers I could check against the code. Four of them were real bugs.
The damage preview was missing two things. The tester planned a Katyusha route that previewed 37 HP of loss and cost 58. The gap, 21, was exactly 5% of her 414 HP: a mine on the end tile. The preview simply did not know mines existed. The second gap was subtler. The preview counted a pursuing drone only if it was already in range of the end tile, but in the real game every alerted pursuer closes one cell after each step of your route and then fires once at the end.
# A chasing drone closes one cell per route step after it alerts (the
# real pursuit tick runs after every step), so it can reach firing
# range from further away than its current position suggests.
var steps_left: int = (path.size() - 1) - int(chase_from.get(did, 1)) + 1
if dist - maxi(0, steps_left) <= drone.attack_range:
pursuit.append(drone)
The preview now walks the route in order, so a heal pickup only restores what is missing and a mine costs its share of max HP either way. An old test asserted that a patrol three cells away never fires. It encoded the very bug, so I rewrote it.
A fallen unit's header in the undo browser. Browsing undo states drew a dead unit as a ghost on the board but left its header at 0 HP, because the HUD refresh only visited living units. Confirming the undo restored it correctly, which is why it looked like a display problem. The ghost now paints its own panel from the checkpoint.
Looking at that report also showed a gameplay leak. The death penalty (a share of the stage's XP) was applied when the unit died, and undoing the death never gave it back, because XP is kept through undo on purpose. A revived unit stayed hundreds of XP poorer for a death that no longer happened. Undoing a death now refunds the penalty, and a death that predates the checkpoint keeps it.
"LIMIT BREAK READY!" for the dead. Every death produced the notice. The killing blow still charges the limit gauge before the death is processed, so the ready signal fired for a unit that was already gone. It is now skipped for a unit at 0 HP.
There is one report I could not close. After a death, a header sometimes showed the unit's pre-death HP again. The numbers are a clue: they were the saved HP and saved max HP, not the live ones, and only two paths paint a panel from saved state. I hardened both, so a deployed unit that is gone from the field is always painted as defeated and never as benched, and a rebuilt HUD repaints every panel. I could not reproduce the revert on its own, so the tracker issue stays open with questions for the tester. I would rather say that plainly than call it solved.
Smaller things¶
- Switching units mid-route. Pressing K, N, or M while a route was previewed did nothing, so the old unit stayed selected. It now drops the plan and switches, the same as pressing C and then the key. A tester had found that workaround on their own.
- A warning before the last memory log. Walking onto a memory log collects it the moment you arrive, and for a collect objective that ends the sector, with any remaining enemies and their kill points lost. The extraction zone already asks first. A log cannot ask after the fact, so the question comes before the move: confirming or queueing a route that would collect the last needed log while drones remain now shows the dialog, with Stay returning you to the preview.
- The Play button that moved. In the stage dialog the leaderboard's "Loading..." label hid itself when the rows arrived, which shrank the panel and slid the Play button up by about 38 pixels, away from a click already on its way. The label now sits in a fixed-height holder.
Bug fixes¶
- Damage previews under-reported by ignoring mines, heal pickups, and drones that chase and catch up.
- Undo showed a fallen unit at 0 HP in every earlier state. It now shows the unit as it was.
- Undoing a death kept its XP penalty. The penalty is refunded now.
- Dead units announced "LIMIT BREAK READY!" and a defeated unit's header could fall back to its saved HP after a refresh.
- K, N, and M did nothing during a route preview, so the previous unit stayed selected.
- The stage dialog's Play button moved once the leaderboard loaded.
- Keys fired behind an open confirm dialog on the world map.
Miscellaneous¶
- New tests for the limit-break key and its gates, WASD alternates and remapping, world map stage selection and launch, limit-break aiming, unit keys during a preview, the memory log warning, the preview's mine, heal, and pursuit math, and the undo header and XP refund.
- GDScript test suite: 1,562 passing (up from 1,513).
- Python test suite: 680 tests, all passing (18 skipped: opt-in slow suites).
- Build 0.1.9.110 goes out to Steam, Google Play, and itch.io.
Two of today's finds were the same kind of mistake in two places. The damage preview and the undo header both showed a tidy number that was simply missing a piece of the real state, a mine, a chasing drone, a unit that was only a ghost, and a tidy number reads as a fact. The fix each time was less about the arithmetic than about asking what the game actually does between "you press execute" and "the screen settles", and putting that in front of the player before they commit.