119d13ba1f5c193feb92ada540c9f4a7fcafba5c
Builder, blueprint and deconstruct were three independent flags, and every entry point cleared the other two by hand. The copies had drifted, and one had a real bug: enterBuilderMode reset the blueprint directly instead of calling exitBlueprintMode, so BlueprintModeExitedEvent never fired and BlueprintPanel never ran clearActiveBlueprintButton. Activating a blueprint and then picking a building left the blueprint button highlighted for a mode that had ended. Exclusivity is now structural. One mode is active, and every transition runs through enterMode(), which exits whatever was active first. Which mode the player switches to can no longer change what the mode they left announces - a test covers all four crossings, plus the blueprint regression above. The controller also owns the state that belongs to a mode and had to be cleared with it: ghost tile/rotation/validity, the resolved tunnel end and its completion partner, the belt drag, and the deconstruct hover. Those were the things the hand-written resets kept forgetting. Everything needing the simulation stays in GameWorldView - placement validity, tunnel matching, belt path building - and is handed back through setGhostValidity, setTunnelGhost and setBeltDragPath. That is what keeps the controller a plain value with tests. Two smaller behaviour changes, both dropping redundant events. Restart now announces only the mode that was actually active rather than all three exits unconditionally; and entering builder or blueprint mode no longer publishes DeconstructModeChangedEvent(false) when deconstruct mode was not on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
Description
No description provided
Languages
C++
96%
CMake
2.2%
Python
1.2%
C
0.6%