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.
9.3 KiB
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.pyfails the build on these. - Only vanilla Arma Reforger resources and resources owned by this repository
may be referenced. Every
{GUID}pathmust either be indocs/vanilla-resources.jsonor be owned by a.metafile 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,mapdepend oncore;chatdepends on all.coremust never referenceERR_HQor anything from another addon. Core defines interfaces (seeERR_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 inai.
Performance rules — the whole point of this work
- 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. - No allocation inside loops. Reuse member arrays; clear rather than
reallocate. Do not allocate an
array<AIAgent>per unit per tick. - Spatial bucketing for proximity. 500 m cells, as already done in
ERR_HQNetwork.Add/Build. Never do an all-pairs distance scan. - Hard caps everywhere, with a logged refusal when hit. Never let a count grow unbounded.
- Prefer vanilla behaviour over custom steering. Issue a waypoint and let the engine drive; do not write per-frame movement code.
- 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_GameModeor add another 100-field object.
Validation — required before you report done
python3 tools/check.pymust pass.- Add Python tests using
tests/policy_harness.py:body(path, signature)extracts a real function body from the.cfile andrun(code)compiles and executes it as C++. Test the pure rule classes this way. Follow the pattern intests/test_admin_session.py. - 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.cERR_Resources.SearchWaypoint/MoveWaypoint/CycleWaypoint/DefendWaypoint— waypoint prefab GUIDsERR_SiteService/ERR_SiteDefinition/ERR_LocationComponent— authored sites and their guardsERR_HQNetwork— the 500 m spatial cell pattern to copyERR_Settings.Get("key")— integer settings; add new keys hereERR_HQJournalviaERR_HQ.Event(kind, message)— ai addon only; core code logs withPrintFormat("Reintroduced [ledger]: ...")