Files
jakub cab998fb64 Combine Workshop addon and fix prison startup
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.
2026-09-20 16:42:52 +02:00

3.9 KiB

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.