Fixes a real crash confirmed via a live traceback: Board.add_target/add_ally's id auto-assignment raised
StopIteration once 26 entities of a group existed — a regression from an earlier fix that moved the id sequence from
per-type to per-group. This was the actual cause of "Accept as" / "Accept all" silently doing nothing.
_next_free_id() now rolls over to two-letter ids instead of raising.
New TargetType.STRIKE_REQUEST, replacing a reuse of STRIKE for the bearing/distance-offset "taking fire"
fire-support-request entries, so a radioed-in request is never confused with a strike the player placed themselves.
"Accept as..." on a detected map marker now uses the same icon grid the entity "Change type" popover uses, instead
of a plain unfiltered text list (which also wrongly offered Strike/Strike Request as pickable types).
Real, confirmed lag fix: assignment/alive/shell changes in the firing panel no longer trigger a full
solver+dedupe+canvas+panel rebuild — none of the three can affect the solver or dedupe, and none change which cards
exist. New FiringPanel._rebuild_one() updates just the one changed card; a new lighter on_visual_change callback
handles the two of these three that actually affect the map. Previously every click rebuilt every card of every
target on the board.
Map right-click menu: added "Mark destroyed"/"Mark alive" on Targets, reusing the same cheap-refresh path.
packaging/windows/: a from-scratch (untested against a real boot) MSYS2 + WiX .msi build pipeline for Windows,
driven from Linux via dockur/windows — no Windows machine or GitHub required. See its own README for status/caveats.
README: dropped the "Known issues" section — map screenshot reading is working noticeably better now.
The packaging/windows/ pipeline is not covered by tests or a real run — flagged as such in its own README.
Summary
- Fixes a real crash confirmed via a live traceback: Board.add_target/add_ally's id auto-assignment raised
StopIteration once 26 entities of a group existed — a regression from an earlier fix that moved the id sequence from
per-type to per-group. This was the actual cause of "Accept as" / "Accept all" silently doing nothing.
_next_free_id() now rolls over to two-letter ids instead of raising.
- New TargetType.STRIKE_REQUEST, replacing a reuse of STRIKE for the bearing/distance-offset "taking fire"
fire-support-request entries, so a radioed-in request is never confused with a strike the player placed themselves.
- "Accept as..." on a detected map marker now uses the same icon grid the entity "Change type" popover uses, instead
of a plain unfiltered text list (which also wrongly offered Strike/Strike Request as pickable types).
- Real, confirmed lag fix: assignment/alive/shell changes in the firing panel no longer trigger a full
solver+dedupe+canvas+panel rebuild — none of the three can affect the solver or dedupe, and none change which cards
exist. New FiringPanel._rebuild_one() updates just the one changed card; a new lighter on_visual_change callback
handles the two of these three that actually affect the map. Previously every click rebuilt every card of every
target on the board.
- Map right-click menu: added "Mark destroyed"/"Mark alive" on Targets, reusing the same cheap-refresh path.
- packaging/windows/: a from-scratch (untested against a real boot) MSYS2 + WiX .msi build pipeline for Windows,
driven from Linux via dockur/windows — no Windows machine or GitHub required. See its own README for status/caveats.
- README: dropped the "Known issues" section — map screenshot reading is working noticeably better now.
Testing
44/44 tests pass (pytest). New/updated coverage: id-namespace sharing + the 26-entity overflow regression
(tests/test_models.py), the StrikeRequest split (tests/test_ocr.py), warp_to_map's img_scale param
(tests/test_map_vision_warp.py).
The packaging/windows/ pipeline is not covered by tests or a real run — flagged as such in its own README.
rerender on assign/alive/shell, add Windows build tooling
- Board.add_target/add_ally's id auto-assignment used a bare
next(c for c in string.ascii_uppercase if c not in used), which
raises StopIteration once 26 entities of a group exist -- a real
crash confirmed via a live traceback, and a direct regression from
moving that sequence from per-type to per-group. This was the actual
cause of "Accept as"/"Accept all" silently doing nothing. Fixed with
_next_free_id(), which rolls over to two-letter ids instead of
raising.
- New TargetType.STRIKE_REQUEST: the bearing/distance-offset "taking
fire" fire-support request (see the earlier two-entity split) now
creates this instead of reusing STRIKE, so a radioed-in request is
never confused with a strike the player placed themselves. Same
crosshair icon, excluded from type pickers/dedupe like STRIKE.
- The "Accept as..." popover on a detected map marker now uses the
same icon grid the entity-edit "Change type" popover does (was a
plain unfiltered text list of every TargetType, which also wrongly
offered STRIKE/STRIKE_REQUEST as pickable).
- Firing panel: _cycle_assignment/_toggle_alive/_pick_shell no longer
route through app.py's full solver+dedupe+canvas+panel refresh --
none of the three can affect the solver or dedupe, and none change
which cards exist or their order (except _toggle_alive in
hide/sort_later mode). New FiringPanel._rebuild_one() rebuilds just
the one changed card; on_visual_change is a new, lighter callback
(just a map redraw) for the two of these three that actually affect
it. This was a real, confirmed lag source with many units on the
board: every click on any of these was previously rebuilding every
card of every target.
- Map right-click entity menu: added "Mark destroyed"/"Mark alive",
reusing the same cheap-refresh path (new
FiringPanel.refresh_after_alive_change).
- packaging/windows/: a from-scratch (untested against a real boot)
MSYS2 + WiX .msi build pipeline for Windows, driven from Linux via
dockur/windows (KVM-in-container), no Windows machine or GitHub
required. See its own README for status/caveats.
- New/updated tests: id-namespace sharing + the 26-entity overflow
regression (tests/test_models.py), StrikeRequest split
(tests/test_ocr.py), warp_to_map's img_scale param
(tests/test_map_vision_warp.py). 44/44 passing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Map screenshot reading (grid + unit detection) is working noticeably
better now; not sure exactly which of the recent fixes did it, but the
blanket "unreliable/fails often" caveat is no longer accurate enough
to keep. TODO.md still has the fuller fixed/open list.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dodox
merged commit bd69b183ad into master2026-08-11 21:12:57 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
StopIteration once 26 entities of a group existed — a regression from an earlier fix that moved the id sequence from
per-type to per-group. This was the actual cause of "Accept as" / "Accept all" silently doing nothing.
_next_free_id() now rolls over to two-letter ids instead of raising.
fire-support-request entries, so a radioed-in request is never confused with a strike the player placed themselves.
of a plain unfiltered text list (which also wrongly offered Strike/Strike Request as pickable types).
solver+dedupe+canvas+panel rebuild — none of the three can affect the solver or dedupe, and none change which cards
exist. New FiringPanel._rebuild_one() updates just the one changed card; a new lighter on_visual_change callback
handles the two of these three that actually affect the map. Previously every click rebuilt every card of every
target on the board.
driven from Linux via dockur/windows — no Windows machine or GitHub required. See its own README for status/caveats.
Testing
44/44 tests pass (pytest). New/updated coverage: id-namespace sharing + the 26-entity overflow regression
(tests/test_models.py), the StrikeRequest split (tests/test_ocr.py), warp_to_map's img_scale param
(tests/test_map_vision_warp.py).
The packaging/windows/ pipeline is not covered by tests or a real run — flagged as such in its own README.