Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
94 lines
3.9 KiB
Markdown
94 lines
3.9 KiB
Markdown
# T1 — Force ledger and route simulation
|
|
|
|
Read `docs/living-island/README.md` first, including the rules section. Follow it.
|
|
|
|
## Goal
|
|
|
|
Introduce a server-owned record of every enemy formation on the island that does
|
|
not require those formations to exist as entities. Dormant records move along
|
|
routes using arithmetic on a slow tick. This is the foundation for everything
|
|
else in the living-island work.
|
|
|
|
## What to build
|
|
|
|
Create `addons/core/Scripts/Game/ERR/ERR_ForceLedger.c`:
|
|
|
|
```
|
|
enum ERR_ForceKind { GARRISON, PATROL, CHECKPOINT, CONVOY }
|
|
enum ERR_ForceDuty { HOLD, PATROL_ROUTE, MOVE_TO, SEARCH, RETURN }
|
|
|
|
class ERR_ForceRoute // ordered waypoints + loop flag
|
|
class ERR_ForceRecord // id, kind, position, heading, route, progress,
|
|
// strength, casualties, duty, dutyTarget,
|
|
// importance, radioCapability, group (null when dormant),
|
|
// lastSimulatedAt, stateSince
|
|
class ERR_ForceLedger // owns records; register/unregister/find/count/status
|
|
```
|
|
|
|
Create `addons/core/Scripts/Game/ERR/ERR_ForceSimulation.c` with a **pure rule
|
|
class** plus the tick that applies it:
|
|
|
|
```
|
|
class ERR_ForceRules
|
|
{
|
|
// pure, engine-free, unit-testable
|
|
static float Speed(ERR_ForceKind kind, ERR_ForceDuty duty); // m/s
|
|
static int Advance(...); // returns new route index
|
|
static vector Step(vector from, vector to, float speed, float seconds, out bool arrived);
|
|
static bool Due(int id, float now, float lastSimulatedAt, float interval); // staggered
|
|
}
|
|
```
|
|
|
|
Rules:
|
|
|
|
- Dormant records advance along their route polyline by `speed * elapsed`.
|
|
Walking speed default 1.4 m/s, vehicle convoy 11 m/s, a holding garrison 0.
|
|
- **Do not raycast, do not query the navmesh, do not touch the world** for
|
|
dormant records. Straight-line interpolation between authored route points is
|
|
the intended fidelity. Authored routes follow roads; that is the content's job,
|
|
not the simulation's.
|
|
- Y coordinate: keep the authored route point's Y. Only call `ERR_Actors.Ground()`
|
|
at the moment a record is materialised (that is T2's job, not yours).
|
|
- Tick interval default 2000 ms, staggered by `id % 8` so records do not all
|
|
update on the same frame.
|
|
- Cap the ledger at 128 records. Refuse beyond that and log once.
|
|
|
|
## Wiring
|
|
|
|
- `ERR_GameMode` owns the ledger, created next to the existing services
|
|
(`m_Sites`, `m_Discovery`, …). Expose `ERR_ForceLedger Forces()`.
|
|
- Tick it from the existing game-mode tick path. **Do not add a new timer or
|
|
`EOnFrame`.**
|
|
- `ERR_SiteService` registers a `GARRISON` record for each site guard group it
|
|
already creates, so the ledger has real content from day one. The site's
|
|
existing patrol cycle waypoints become the record's `ERR_ForceRoute`.
|
|
- Records whose group dies must be marked eliminated and stop simulating.
|
|
|
|
## Acceptance criteria
|
|
|
|
1. With five authored sites, the ledger holds five garrison records after
|
|
preparation, and `Status()` reports them.
|
|
2. A record with a 4-point looping route returns to its start after the expected
|
|
time, within 5% — verified by the Python test, not by eye.
|
|
3. Advancing 128 records for a simulated hour performs no world queries and no
|
|
per-record allocation.
|
|
4. `python3 tools/check.py` passes.
|
|
|
|
## Tests to write
|
|
|
|
`tests/test_force_ledger.py`, using `tests/policy_harness.py`:
|
|
|
|
- Extract `ERR_ForceRules.Step` and `Advance` with `body()` and run them as C++:
|
|
a 4-leg loop advanced in 2 s steps arrives at each leg in the expected order
|
|
and wraps correctly.
|
|
- `Speed` returns 0 for `HOLD`, and a convoy outruns a foot patrol.
|
|
- `Due` staggers: across ids 0..7 at the same `now`, not all are due.
|
|
- Off-by-one on the final leg of a non-looping route ends at the last point and
|
|
reports arrival exactly once.
|
|
|
|
## Do not
|
|
|
|
- Do not spawn or despawn anything in this task. T2 owns materialisation.
|
|
- Do not reference `ERR_HQ` — this is core.
|
|
- Do not add a `ref array` member that grows without a cap.
|