Commit Graph

69 Commits

Author SHA1 Message Date
72d85d681c depend on the registry instead of DebrisSystem in the AI path
getAllDebrisInfo and collectOne only ever touched EntityAdmin — DebrisSystem
holds nothing else — so they become free functions over the registry. That lets
AiSystem, SalvagerSystem and SalvageScrapEvaluator drop their DebrisSystem&
parameters entirely; SalvagerSystem already held the admin, and the other two
were handed it alongside.

No system in lib/ecs/system takes another system now. Every tick signature names
the data it works on: the registry, the factory state, or both.

DebrisSystem keeps spawn, tickDespawn and consume — the first two are genuine
tick behaviour rather than lookups.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-05 07:16:36 +02:00
a86ba3428a make deconstruction its own system
DeconstructionSystem takes over the demolition queue: it runs the front entry's
timer and, when it elapses, removes the building, releases its tiles and credits
the partial refund. Simulation::tick calls it directly, in the position
tickDeconstruction held.

It needs no BeltSystem, unlike its construction counterpart: a belt, splitter or
tunnel end is unregistered the moment it is queued, not when the timer completes.
It does need the refund sink, so it takes the same addBuildingBlocks callback
BuildingSystem holds.

startFrontDeconstruction becomes a shared free function rather than moving:
BuildingSystem::deconstruct starts the timer when it queues the first entry, and
the system restarts it after each completion.

Stubbing the refund sink out in the test helper made two tests fail on the
refund not arriving — correctly. runTicks now threads the caller's stock through
instead.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-05 07:01:22 +02:00
56b7248ac7 make construction its own system
ConstructionSystem takes over the construction queue: it runs the front site's
timer and, when it elapses, builds the Building itself — ports, buffers, belt
registration, and starting the next queued site. Simulation::tick calls it
directly, in the same position tickConstruction held.

No handoff. The earlier sketch had it return the completed site for BuildingSystem
to materialise, which put an intermediate value in Simulation and made the two
calls correct only when adjacent and ordered. Once the queries and the buffer
helpers became free functions there was nothing left on BuildingSystem that
materialisation needed, so the system does the whole job and the invariant
disappears rather than being documented.

Holds only the config; the world arrives per tick, like the systems in
lib/ecs/system.

Verified with a golden-checksum capture before and after — all four sample ticks
identical, which is the check that matters here since this moves a call in the
tick order.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-05 06:49:20 +02:00
a90218f5c0 make BuildingSystem stateless: FactoryState becomes a parameter
The member reference is gone. All 23 methods that read or write the factory now
take FactoryState& (const for the two item walks and the checksum fold), so a
BuildingSystem is no longer bound to one state and its signatures say which data
each call touches. It holds only config, belts, rng and the callbacks — the same
shape as AiSystem and CombatSystem.

This completes what phase 2 set out to do; the ownership move landed earlier, but
the systems kept reaching the data through a member until the queries were off
them.

Seeding the asteroid bound moved with the state, and that broke four tests: the
fixtures build their own FactoryState, which defaulted the bound to 0 and refused
every placement on the asteroid. Rather than fix the four call sites, makeFactoryState()
now creates a run's state from the config, and Simulation, ArenaSimulation and the
test fixtures all use it — there is one place that knows what a fresh factory
looks like.

Verified with a golden-checksum capture before and after — all four sample ticks
identical — and by re-running the declaration/definition check over the header.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-04 23:00:09 +02:00
b3d6264ed3 move the placement rules and the config-dependent queries off BuildingSystem
isPlacementValid, findRotateInPlaceTarget and the bodyCellsWithinWorldBounds
helper become PlacementRules.h — where a building may go and what already sits
on those tiles, answered from the factory state and the config. getInputPorts
and getSiteSplitterInfo join FactoryQueries.h, whose header comment now says
plainly that the last two also take the config because answering them means
reading a building definition.

computeInputPorts goes to PortGeometry.h alongside outputBodyTile/inputBodyTile:
it needs only Port and QPoint, so it belongs in core rather than in sim.

BuildingSystem is left with no query that reads the factory — its remaining const
methods are the emerging/incoming item walks, the checksum fold, and the buffer
initialisers. It changes the factory now; it no longer describes it.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-04 22:31:16 +02:00
df7f60c898 migrate every factory query off BuildingSystem onto the free functions
The eleven forwarding members added last commit are gone; callers now read the
data directly through FactoryQueries.h. Simulation and ArenaSimulation expose
getFactoryState() so the UI, the balancing view and the tests can reach it.

isQueuedForDeconstruction joined the free functions along the way — it only
reaches findBuilding, so it was state-pure too.

No facade was introduced. The chained form was the reason one looked attractive,
but rewriting sim.getBuildings().findBuilding(id) to findBuilding(sim.getFactoryState(), id)
turned out to be mechanical, and the result says which data is read rather than
which system happens to own it.

BuildingSystem.cpp is down to 1735 lines and no longer answers questions about
the factory — it only changes it. What remains on it are the mutators, the tick
phases, and the queries that also need GameConfig.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-04 21:52:49 +02:00
bb50f527d6 drop CombatSystem's unused BuildingSystem parameter
The parameter was already commented out in the definition — combat resolution
never touched it. Removing it also removes the last reference to BuildingSystem
from CombatSystem, so the forward declaration goes too.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-04 21:34:20 +02:00
7540c21d5c depend on factory data instead of BuildingSystem in the AI path
Ten queries that read nothing but FactoryState become free functions in
FactoryQueries.h; the BuildingSystem methods stay as one-line forwards, so no
existing caller moves yet.

That lets the AI path drop its dependency on the system entirely. AiSystem,
SalvagerSystem, DeliverScrapEvaluator and DeliverScrapExecutor took a
BuildingSystem& purely to call findBuilding, findNearestBuilding and
deliverScrapToSalvageBay — all three are state-pure — so they now take
FactoryState& and say what they actually read. Four forward declarations of
BuildingSystem go with them.

No facade: the queries are plain free functions over the data. A facade was
considered to spare the ~180 UI call sites, but the AI needed only the data and
would have been given GameConfig it has no use for.

isProductionBuildingType moves to BuildingType.h beside isAutoRecipeBuildingType
and isBeltSubsystemType rather than being copied into the new file.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-04 21:26:49 +02:00
2522a8c974 move FactoryState ownership out of BuildingSystem to Simulation
Simulation (and ArenaSimulation in the balancing tool) now owns the factory's
world data; BuildingSystem holds a reference to it. This is what lets the systems
that operate on the data be handed the same state — phase 3's construction and
deconstruction systems, and later the ecs/system/ classes that today take a
BuildingSystem& only to query it.

reset() clears the state alongside m_admin and m_beltSystem, matching how the
subsystems were already rebuilt from scratch.

Falls short of the tick-argument form I sketched: BuildingSystem still reaches
the data through a member reference rather than a parameter. Making it truly
stateless means the const query surface has to find the data some other way, and
that surface is large — findBuilding alone has 64 call sites, with findSite,
getAllBuildings, getAllSites, isTileOccupied and the rest behind it. Doing that
needs a queries facade behind Simulation::getBuildings() so the callers do not
all move, which is its own decision rather than a side effect of this one.

The constructor gains a parameter, so the four owners and the three test fixtures
that build a BuildingSystem directly are updated; the 33 files that only use one
are untouched.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-04 20:55:32 +02:00
10ba226af7 wire Simulation to forward schematic/unlock queries to UnlockState
Simulation now owns an UnlockState member (constructed before
initializeSubsystems(), since BuildingSystem's spawn-gating lambda
calls into it via isSchematicUnlocked instead of poking the old
m_schematicLevels map directly). The public isXUnlocked accessors
become one-line forwards, applySchematicChoice's group-awarding block
becomes a single awardUnlockGroup() call, and generateSchematicChoices
(which still owns m_rng and must not change call ordering) now reads
group state and builds options through UnlockState.

The checksum fold at computeStateChecksum's schematic/unlock section
had to move together with the containers it reads; UnlockState::
appendChecksum makes the identical seven appendSchematicMap/
appendStringSet calls in the identical order, so the fold is
unaffected. Verified via a temporary golden-checksum test case
(added, checked, then removed) that tick1/100/999/1999 checksums for
seed 12345 are byte-identical to pre-refactor.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-04 14:10:52 +02:00
92a4f02cef extract Simulation::initializeSubsystems
The constructor and reset() held a character-for-character identical
26-line subsystem construction block, including three capturing lambdas.
Both run before the first tick, so the closures can be shared. Order is
unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GH8ZMRY3vhxxXcaUxBqxkk
2026-08-02 20:44:43 +02:00
8b71fe1a03 Rename ship/station scrap drop entities to "debris" 2026-07-23 20:51:05 +02:00
e20a0bba67 Rename Demolish to Deconstruct 2026-07-22 21:40:42 +02:00
b2ce20e6ad Add deconstruction queue 2026-07-22 21:37:56 +02:00
a9082c57f3 Implement config-driven unlock groups so that multiple things can be unlocked at once (including buildings) 2026-07-22 21:34:59 +02:00
9622fa4345 Use std::optional instead of sentinel values for absent data 2026-07-20 20:27:20 +02:00
d412c69f82 Prefix all getters with "get" 2026-07-19 21:17:38 +02:00
6a8c456aa1 Animate items emerging from building output ports 2026-07-14 20:18:47 +02:00
6274b5ce60 List unlocked recipes with tooltips in schematic choice dialog instead of unlocked item names 2026-07-09 20:22:17 +02:00
16ed6a5695 Show schematic id for all three types of drops in choice dialog 2026-07-09 20:18:38 +02:00
e8786c3922 implement cost formula for asteroid expansion 2026-07-08 20:33:19 +02:00
6ea0655eaf implement unlock dependencies 2026-07-03 08:35:45 +02:00
18e732ae99 Remove schematic upgrades and module and ship levels 2026-07-02 21:32:36 +02:00
81b1c7a66b Derive ship scrap drop from threat 2026-07-02 21:30:38 +02:00
0a7e9a34ef Add artifact win condition (#3)
Reviewed-on: #3
Co-authored-by: Malte Langkabel <malte.langkabel@gmail.com>
Co-committed-by: Malte Langkabel <malte.langkabel@gmail.com>
2026-07-01 20:27:29 +00:00
d74ba5bfad Replay: deterministic record & playback (#4)
Add deterministic record/playback for a run.

Recording captures `(seed, config hash, ordered tick-tagged commands)` and re-simulates on playback — no state snapshots. `DotaFactory.exe --replay <file>` re-plays a recorded run view-only with manual speed/pause.

Reviewed-on: #4
Co-authored-by: Malte Langkabel <malte.langkabel@gmail.com>
Co-committed-by: Malte Langkabel <malte.langkabel@gmail.com>
2026-07-01 19:20:08 +00:00
e5017ab3c5 fix issue where construction sites could be placed outside of game world and add tests 2026-06-22 21:13:01 +02:00
9573b9789a change repair_tool application and add beams for salvager and repair_tool 2026-06-19 21:15:47 +02:00
ac97652c60 make ships claim targets 2026-06-16 21:18:28 +02:00
e8dd73bcb0 refactor AI system 2026-06-15 09:16:56 +02:00
1ea1cc59fb show threat rate in debug output 2026-06-14 13:39:10 +02:00
3716c2b734 show implicitly unlocked items in schematic unlock dialog 2026-06-13 18:19:25 +02:00
5317f35198 switch to using own event system 2026-06-13 17:52:22 +02:00
49f7129bd5 schematic selection dialog 2026-06-13 14:19:51 +02:00
1641189b75 explicit recipe unlocking 2026-06-12 17:15:06 +02:00
54a6056b77 implicit item locking 2026-06-12 16:14:21 +02:00
af96b95f61 allow to unlock modules when destroying defence stations 2026-06-10 22:37:38 +02:00
aad094f842 allow to configure when which schematic gets unlockable 2026-06-10 21:09:03 +02:00
8dad554800 show threat budget in debug text overlay 2026-06-06 21:07:18 +02:00
6b95619806 add units in config files 2026-06-06 20:46:36 +02:00
7669245229 use meters in config 2026-06-05 20:09:20 +02:00
4e3e3ac715 replace combined stateUpdated signal with individual events 2026-06-05 18:27:46 +02:00
9677133c54 add event system and use it to propagate to print traces 2026-06-05 17:18:17 +02:00
abc261c03a add tracing for performance profiling 2026-06-05 16:38:36 +02:00
15d8fa4f2c add boss wave counter and countdown to the title bar 2026-06-03 22:44:56 +02:00
b5185b0906 boss waves 2026-06-03 22:14:31 +02:00
9d0a60a93b define ship roles via added modules and allow multiple weapons 2026-06-01 23:05:21 +02:00
25ff3c56c5 move ecs related code to own folder 2026-05-25 09:10:54 +02:00
0cd0529468 split MovementSystem to MovementIntentSystem and DynamicBodySystem 2026-05-25 07:25:54 +02:00
f5f4453e2c rename behavior components 2026-05-23 08:26:10 +02:00