Files
EscapistsReintroduced/docs/living-island/README.md
T
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

9.3 KiB
Raw Blame History

Living island — plan and agent rules

Goal: Everon should feel occupied — patrols that actually move, manned roadblocks, soldiers doing something other than standing — without raising the active AI count. The target is a constant, bounded agent budget (default 60 enemy agents) regardless of how much "activity" exists on the map.

The core idea: simulate many, spawn few

Most of the island's forces exist as records, not entities. A record has a position, a route, a strength and a duty. A slow tick advances records along their routes with plain arithmetic — no AI, no navmesh, no entity. A record is materialised into a real SCR_AIGroup only when a player is close enough to observe it, or when HQ needs it in contact. When the player leaves, it goes back to being a record, keeping its position and losses.

This is what buys the "living island" feeling cheaply: twelve patrols can be crossing the map at once while only the two near players cost anything.

ERR_ForceLedger        records: id, kind, position, route, strength, duty, importance
   |
   |-- ERR_ForceSimulation   advances dormant records along routes (2 s tick, pure math)
   |-- ERR_PresenceBudget    decides which records are physical right now (3 s tick)
   |        |-- spawns  -> SCR_AIGroup.SpawnMembers()
   |        |-- dormant -> SCR_AIGroup.DespawnMembers()
   |
   +-- consumed by ERR_HQ (ai addon) for tasking, ETA and availability

Hard invariant — fairness: only physical units observe and report contacts. A dormant record never generates a sighting. This protects the HQ design, where enemy knowledge must be earned through line of sight and radio.

Layer 2: presence without population

A site feels occupied because of evidence, not head count. Evidence is free — static props cost no CPU. So roadblocks, barriers, parked vehicles and lit windows are permanent; the soldiers manning them are conditional.

Layer 3: soldiers doing something

Vanilla smart actions (SCR_AISmartActionComponent) let an existing soldier lean on a wall, smoke, or work on a vehicle instead of standing at a waypoint. This costs no extra agents — it changes what the agents you already pay for are doing.

Task order

Do these in order; each builds on the previous one. One agent per task.

Task File Depends on
T1 T1-force-ledger.md — records + route simulation —
T2 T2-presence-budget.md — spawn/dormant arbitration under a cap T1
T3 T3-checkpoints.md — roadblocks and manned checkpoints T1, T2
T4 T4-smart-actions.md — idle activity at sites T2
T5 T5-hq-integration.md — HQ tasks ledger units, gets ETAs T1, T2
T6 T6-ambient-signals.md — cheap signs of life T2
T7 T7-performance.md — caps, metrics, thrash prevention all
T8 T8-hq-memory.md — HQ belief: decaying contacts, searched ground T5
T9 T9-pursuit.md — alert states, hunting, air search, cordons T5, T8
T10 T10-ai-migration.md — port the old AI mod's proven behaviour under HQ T5, T8, T9
O1–O6 OCCUPATION.md — how the island gets occupied after the alarm T1, T2

T3 and T4 can run in parallel once T2 lands. T6 can run in parallel with T5. T8 and T9 are what make the island hunt rather than merely be occupied; T9 depends on T8, because HQ cannot search for what it does not remember.


Rules every agent must follow

Paste this section into each agent along with its task file.

Licensing — non-negotiable

This project is an independent successor. The original Escapists mod and its ScrtCore / ScrtSharedContent dependencies are under SCRT-ND (an APL-ND variant) which forbids derivatives. Therefore:

  • Never copy code, config, layouts or assets from the original Escapists mod, from ScrtCore/ScrtSharedContent, or from /home/jakub/devel/escapists.
  • Never introduce identifiers matching ESCT*, SCRTC_*, SCRT_*, EAI_*. tools/check.py fails the build on these.
  • Only vanilla Arma Reforger resources and resources owned by this repository may be referenced. Every {GUID}path must either be in docs/vanilla-resources.json or be owned by a .meta file in this repo.
  • Reimplementing a mechanic from scratch is fine. Copying an implementation is not.

Project constraints

  • Five addons only: core, ai, intel, chat, map. Do not add addons or external dependencies.
  • Dependency direction: ai, intel, map depend on core; chat depends on all. core must never reference ERR_HQ or anything from another addon. Core defines interfaces (see ERR_CommandService); companions implement them.
  • The living-island ledger belongs in core, because the base scenario must work without the AI addon. HQ integration (T5) goes in ai.

Performance rules — the whole point of this work

  1. No per-frame work. Every tick in this subsystem is ≥ 1 s. Stagger work by unit id (id % N) so all units never update on the same frame.
  2. No allocation inside loops. Reuse member arrays; clear rather than reallocate. Do not allocate an array<AIAgent> per unit per tick.
  3. Spatial bucketing for proximity. 500 m cells, as already done in ERR_HQNetwork.Add/Build. Never do an all-pairs distance scan.
  4. Hard caps everywhere, with a logged refusal when hit. Never let a count grow unbounded.
  5. Prefer vanilla behaviour over custom steering. Issue a waypoint and let the engine drive; do not write per-frame movement code.
  6. Hysteresis and dwell time. Spawn and despawn radii must differ, and a unit must stay in its state a minimum time before flipping. Thrash is the main performance risk in this design.

Code style

  • Match the surrounding file's indentation (older files use tabs).
  • New lines stay under 120 characters. The repo already has 420 lines over 200 characters; do not add more.
  • Put decision logic in pure static rule classes (see ERR_DiscoveryMapRules, ERR_MissionRules) so it can be tested without the engine. Entity manipulation stays in the service class.
  • Small classes. Do not extend ERR_GameMode or add another 100-field object.

Validation — required before you report done

  1. python3 tools/check.py must pass.
  2. Add Python tests using tests/policy_harness.py: body(path, signature) extracts a real function body from the .c file and run(code) compiles and executes it as C++. Test the pure rule classes this way. Follow the pattern in tests/test_admin_session.py.
  3. Report honestly. This repo separates "compiles and passes scripted fixtures" from "verified in the real game". If you did not run it in Workbench, say so. Do not write evidence files claiming runtime verification you did not perform.

Verified engine APIs you will need

All confirmed present in the installed vanilla scripts (1.6.30):

API Where Notes
SCR_AIGroup.SetLifecyclePolicy(policy, spawnDist, despawnDist, veryNearBlockDist) Entities/SCR_AIGroup.c:2921 Manual (default) or ProximityDriven
SCR_AIGroup.SpawnMembers() / DespawnMembers() :2783 / :2870 Dormant group keeps its entity and records alive/dead counts, so a later SpawnMembers() re-materialises the right number
ChimeraAIGroup.SetImportance(int) / AIGroup.GetImportance() generated/AI/ChimeraAIGroup.c:22 Pass SCR_EAISpawnImportance values
SCR_EAISpawnImportance AI/SCR_EAISpawnImportance.c LOW 0.50, NORMAL 0.70, HIGH 0.90, CRITICAL 1.00 — fraction of AIWorld's active-AI limit the tier may consume, enforced by AIWorld::CanActivateGroup
SCR_EAIGroupLifecyclePolicy AI/SCR_EAIGroupLifecyclePolicy.c Manual = we drive it; ProximityDriven = group self-manages against ObserversSystem
ObserversSystem.GetObservers(out array<vector>) generated/System/ObserversSystem.c:59 Cheap observer positions; also GetObserversMP
SCR_AIGroup.SetEliminateWhenReached(true) + GetOnEliminatedWhenReached() used at SCR_AmbientPatrolSpawnPointComponent.c:212 If a player reaches a patrol that could not spawn (budget full), the area counts as cleared and the group retires instead of popping in later
SCR_AISmartActionComponent (ReserveAction, ReleaseAction, GetOnActionEnd, GetOnActionFailed) AI/Components/SCR_AISmartActionComponent.c Idle activities
SCR_SmartActionWaypoint.SetSmartActionEntity(IEntity, string tag) AI/Behavior/SCR_SmartActionWaypoint.c:31 Sends a group to perform a tagged smart action

Reference implementation worth reading (vanilla, not the original mod — safe to read and learn from): Components/Locations/SCR_AmbientPatrolSpawnPointComponent.c and Systems/SCR_AmbientPatrolSystem.c.

Existing repo helpers — use these, do not reinvent

  • ERR_Actors.Spawn/DeleteSpawned/Ground/Players/Alive/CanFight/Group — addons/core/Scripts/Game/ERR/ERR_Actors.c
  • ERR_Resources.SearchWaypoint/MoveWaypoint/CycleWaypoint/DefendWaypoint — waypoint prefab GUIDs
  • ERR_SiteService / ERR_SiteDefinition / ERR_LocationComponent — authored sites and their guards
  • ERR_HQNetwork — the 500 m spatial cell pattern to copy
  • ERR_Settings.Get("key") — integer settings; add new keys here
  • ERR_HQJournal via ERR_HQ.Event(kind, message) — ai addon only; core code logs with PrintFormat("Reintroduced [ledger]: ...")