agentleFS
willy92winswilly92wins/DayZ-Modding-Knowledge-Pack/skills/dayz-ai-patterns/SKILL.md

dayz-ai-patterns

Skill20 starsChanged 41 days agoMITView on GitHub

Use this skill when working with DayZ Expansion AI (eAI module): FSM states (Dormant, Fighting), combat hysteresis, threat/targeting, LOS/FOV, pathfinding, group formation, squad target distribution, or the NoiseSystem. Also trigger when a user asks why AI bots are not attacking, not patrolling, walking through doors, ignoring sounds, all focusing the same enemy, not spreading into formation, or when debugging eAIBase, eAIGroup, eAIState, or ExpansionPathHandler. Trigger on: eAI, DayZ AI, AI FSM, FSM pathfinding, group AI, squad AI, threat targeting, LOS FOV AI, Expansion AI, AI bots. Also 1.30: sandstorm, MINDSTATE_COWER, AIParams, GetEnvironmentNoiseReduction, animal group AI, DZMouflonGroupBeh, DZDogGroupBeh.

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Do not scatter DisableSimulation calls across logic — let the FSM state own that toggle.

    line 61
  2. Distinctive:

    When no player is within the AI's visibility range, its update interval degrades from ~50 ms to ~250 ms.

    line 164
  3. Very specific:

    If all bots are chasing the same player, check whether num_ai_in_group_not_tracking logic is intact and AddTarget is being called rather …

    line 205

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

DayZ Expansion AI Patterns (eAI)

Material source: salutesh/DayZ-Expansion-Scripts @ 8f75d554fda209b257c00deb8f01c181e67c980a Research: dual-arm (Claude + Codex), adversarially verified. All path:line cites are confirmed.

Overview

12 patterns covering the irreducible AI architecture of Expansion eAI:

IDPatternFile reference
PC-5Dormant state: DisableSimulation via FSMeaistate_dormant.c:1-17
PC-11Fighting FSM: hysteresis + guard clauseseaistate_fighting.c:18-38
PC-13FSM loaded from XML + sub-FSM compositioneAIBase.c:661-666, Master.xml:3-8
PC-9aFOV pre-filter before LOS raycasteAIBase.c:8156-8168
PC-9bLOS throttle adaptive to update intervaleAIBase.c:8089-8097
PC-14Threat cache with jitter 250-300 mseAITargetInformationState.c:58-67
PC-7Staggered target scan + jitter 0.1-0.2 seAIBase.c:2962-2966
PC-2Backoff 5x: no players in visibilityExpansionWorld.c:247-250
PC-15Pathfinding: recalc thresholds + costExpansionPathHandler.c:581-592
PC-16Path filters: navmesh costs (door=10000)expansionpathfilters.c:131-147
PC-17Group targets: distribution by trackingeAIGroup.c:726, eAIBase.c:3497-3505
PC-18Formation positions: cached + jitter 2-4seAIGroup.c:822-830

Server-side scheduling/throttling principles (budget-per-frame, FPS-adaptive interval, rolling average, modulo-gating): canonical server-perf write-up: enforce-script-reference/references/server-performance.md (PC-2/PC-7/PC-9b in THIS skill are the eAI-specific instances). For #ifdef SERVER vs IsServer() side checks and ScriptInvoker lifecycle, enforce-script-reference does apply (hard rules 17-20, 28).


FSM Architecture

PC-13 — FSM loaded from XML; sub-FSM composition

eAIBase.c:661-666 + Scripts/FSM/Master.xml:3-8

LoadFSM() calls ExpansionFSMType.LoadXML(...), then Spawn(...) and StartDefault(). Master.xml includes sub-FSMs for Vehicles, Fighting, and Reloading. Dormant is a state in the master.

Key implication: state composition changes via XML, not Enforce code. Adding or removing states does not require a code recompile.

Full detail: references/eai-fsm-combat.md — PC-13.

PC-5 — Dormant state: DisableSimulation managed by FSM

eaistate_dormant.c:1-17

On enter: unit.DisableSimulation(true). On exit: unit.DisableSimulation(false). Entry conditions checked by FSM transitions: no combat, no players within visibility range, no pending waypoints, no active movement, leader is an AI unit.

Do not scatter DisableSimulation calls across logic — let the FSM state own that toggle. The FSM exit restores simulation; no caller needs to remember to re-enable it.

Full detail: references/eai-fsm-combat.md — PC-5.

Vanilla infected mind-state: MINDSTATE_COWER (since 1.30 Exp)

This is not Expansion eAI. Vanilla DayZInfected gained DayZInfectedConstants.MINDSTATE_COWER after MINDSTATE_FIGHT (exp\scripts\scripts\3_Game\Entities\DayZInfected.c:18). ZombieBase syncs m_MindState with range -1..5 (ZombieBase.c:54). On the sandstorm cower state the infected holds the calm idle pose (SetIdleState(0)); native AI already stopped movement (ZombieBase.c:474-478). [EXACT] blocks: references/dayz-1-30-ai.md.


Combat FSM

PC-11 — Fighting FSM: hysteresis thresholds and guard clause chain

eaistate_fighting.c:18-38 (enter/exit logic) eaistate_fighting_fireweapon.c:78 (fire sub-FSM guards)

Enter combat when GetThreatToSelf() >= 0.4. Exit when it drops below 0.2. The gap prevents oscillation when threat hovers around a single value.

Guard clauses before enter: IsRestrained, IsUnconscious, IsInTransport, GetRunningAction. The fire-weapon sub-FSM has its own chain: IsRestrained -> GetTarget -> IsFighting. IsFightingFSM flag is set so external systems can query combat state without inspecting the FSM directly.

Hysteresis rule: always set enter > exit. A single threshold creates a flip-flop bug in practice.

Full detail: references/eai-fsm-combat.md — PC-11.


Threat & Targeting

PC-14 — Threat cache with jitter 250-300 ms

eAITargetInformationState.c:58-67

if (force || diff > Math.RandomIntInclusive(250, 300))
{
    m_ThreatLevelUpdateTimestamp = time;
    m_ThreatLevel = m_Info.CalculateThreat(m_AI, this);

Each target information state carries its own timestamp and a per-call random cooldown. CalculateThreat runs at most once per ~275 ms on average. The jitter means AIs do not all recalculate threat on the same server tick.

Full detail: references/eai-pathfinding-groups.md — PC-14.

PC-7 — Staggered target scan: persistent index + jitter 0.1-0.2 s

eAIBase.c:2962-2966

m_eAI_UpdateTargetsTick += pDt;
if (m_eAI_CurrentPotentialTargetIndex >= m_eAI_PotentialTargetEntities.Count()
    && m_eAI_UpdateTargetsTick > Math.RandomFloat(0.1, 0.2))
{
    m_eAI_UpdateTargetsTick = 0;
    m_eAI_CurrentPotentialTargetIndex = 0;

UpdateTargets advances one index per tick rather than scanning the entire list. A full refresh only happens when the index is exhausted AND the random interval has passed. Prevents all AIs from finishing their scan simultaneously.

Full detail: references/eai-pathfinding-groups.md — PC-7.


LOS / FOV

PC-9a — FOV pre-filter before LOS raycast

eAIBase.c:8156-8168

eAI_CalculateFOVHalfAngleH produces the effective half-angle (stance-adjusted). If angleDiffH > threshAngleH, the candidate is discarded before a raycast is issued. Angle comparison costs nanoseconds; a raycast costs microseconds to milliseconds depending on scene complexity.

Apply cheap spatial tests (angle, range band) before expensive ones (raycast, physics query).

Full detail: references/eai-fsm-combat.md — PC-9 (Codex angle).

PC-9b — LOS throttle adaptive to current update interval

eAIBase.c:8089-8097

if (s_eAI_FTO < 2)
    interval = LOS_CHECK_INTERVAL;
else
    interval = LOS_CHECK_INTERVAL * (pDt / ExpansionWorld.AI_UPDATE_INTERVAL);
if (m_eAI_LOSCheckDT < interval && !m_eAI_TargetChanged)
    return state.m_LOS;

In scheduler mode (s_eAI_FTO >= 2) the LOS interval scales with pDt. An AI updated at 5x the base interval does not get 5x the LOS check rate — the effective check frequency remains constant. Setting m_eAI_TargetChanged = true forces a fresh check, bypassing the cache.

Full detail: references/eai-pathfinding-groups.md — PC-9 (Claude angle).

PC-2 — Backoff 5x when no players within visibility distance (AI-specific)

ExpansionWorld.c:247-250

if (eAIBase.s_eAI_FTO >= 2 && ai.m_eAI_PlayersWithinVisibilityDistanceLimit.Count() == 0)
    updateIntervalCurrent = AI_UPDATE_INTERVAL * 5;
else
    updateIntervalCurrent = updateInterval;

When no player is within the AI's visibility range, its update interval degrades from ~50 ms to ~250 ms. Logic is not disabled — it runs at one-fifth frequency. Condition is AI-local: each unit decides its own backoff independently.

This is the AI-specific implementation of the backoff principle (degrade frequency by relevance). Canonical server-perf write-up: enforce-script-reference/references/server-performance.md (PC-2/PC-7/PC-9b are the eAI-specific instances).


Pathfinding

PC-15 — Path recalculation with thresholds and measured cost

ExpansionPathHandler.c:581-592

ExpansionPathHandler does not recalculate on every target update. Three guards:

  • PATH_RECALCULATE_THRESHOLD: minimum positional delta before path is stale
  • m_MinTimeUntilNextUpdate: cooldown between recalcs
  • m_TimeIt.GetElapsed(): actual cost measurement per recalc (feedback for adaptive scheduling)

Legacy path: eAIPathFinding, activated with #define EAI_USE_LEGACY_PATHFINDING. Default is ExpansionPathHandler.

Full detail: references/eai-pathfinding-groups.md — PC-15.

PC-16 — Path filters with navmesh costs; DOOR_OPENED = 10 000

expansionpathfilters.c:131-147

SetFilterCost per area type: CRAWL, CROUCH, JUMP, DEEP_WATER = 10. DOOR_OPENED = 10 000. Cost 10 000 is effectively prohibitive — the pathfinder routes around an open door rather than through it (avoids collision with door physics geometry).

Filters encode behavioral preferences, not just include/exclude flags. A cost of 10 000 is a soft prohibition without a hard constraint.

Full detail: references/eai-pathfinding-groups.md — PC-16.


Group & Formation

PC-17 — Group target distribution: prevents squad over-concentration

eAIGroup.c:726 + eAIBase.c:3497-3505

eAIGroup.AddTarget distributes target data to members. Before assigning target X to a member, eAIBase checks num_ai_in_group_not_tracking: if it reaches 0 (everyone already tracks X), returns false. The squad naturally distributes across multiple enemies without explicit assignment logic.

If all bots are chasing the same player, check whether num_ai_in_group_not_tracking logic is intact and AddTarget is being called rather than bypassed.

Full detail: references/eai-pathfinding-groups.md — PC-17.

PC-18 — Formation positions cached with per-AI jitter 2-4 s

eAIGroup.c:822-830

GetFormationPosition checks m_eAI_FormationPositionUpdateTime with Math.RandomFloat(2.0, 4.0). Each member has its own jitter window. When the leader moves, members do not all recalculate simultaneously — they spread the recalc over a 2 s window.

Formation recalc is per-AI, not per-group. The group does not broadcast "recalculate now" to all members.

Full detail: references/eai-pathfinding-groups.md — PC-18.


Animal & Group AI Templates (vanilla, since 1.30 Exp)

Nasdara ships native group-behaviour templates in GroupBehaviourTemplates (exp\nasdara__data_nasdara\DZ\data_takistan\aiconfigs\config.cpp:14-132). These are not Expansion eAI FSMs.

  • DZMouflonGroupBeh — type = "DomesticHerbivores": graze/rest lifecycle, safeKeeperIntervalMin/Max, catch-up radii (:16-67).
  • DZDogGroupBeh — type = "Predators": zoneType = "HuntingGround", siegeAttackCountdownMin/Max, huntAttackCountdownMin/Max, safeKeeperIntervalMin/Max (:68-127).

[DESIGN] Custom maps that want the same packs copy these classes (or inherit them) in their own aiconfigs rather than reimplementing predator/herbivore FSMs in script. [EXACT] blocks: references/dayz-1-30-ai.md.


References

  • references/eai-fsm-combat.md — PC-5, PC-11, PC-13, PC-9a
  • references/eai-pathfinding-groups.md — PC-14, PC-7, PC-2, PC-9b, PC-15, PC-16, PC-17, PC-18
  • references/dayz-1-30-ai.md — 1.30 Exp: native noise attenuation, MINDSTATE_COWER, AIParams, animal group templates

NoiseSystem — feeding noise the AI can hear (added 2026-06-06)

Source-verified vs vanilla v1.24 (3_game/noise.c:1-23):

  • Server-side API: g_Game.GetNoiseSystem().AddNoise(entity, params, mult) / AddNoisePos(entity, pos, params, mult) / AddNoiseTarget(pos, lifetimeSec, params, mult).
  • AddNoiseTarget creates a positional decoy with a duration — infected/AI investigate a position with no entity attached (distraction devices, impacts).
  • NoiseParams.Load("CfgNoises_entry") or LoadFromPath(path).
  • (hasta 1.29: Rain/wind reduce effective noise: NoiseAIEvaluate.GetNoiseReduction(g_Game.GetWeather()) (player steps use it, dayzplayerimplement.c:3204-3208).) (desde 1.30 Exp: weather dampening is native. Query the factor the AI uses with NoiseSystem.GetEnvironmentNoiseReduction(vector pos) — exp\scripts\scripts\3_Game\Noise.c:13. NoiseAIEvaluate.GetNoiseReduction and Weather.GetNoiseReductionByWeather are [Obsolete] (SensesAIEvaluate.c:92-93, Weather.c:454-455). Player steps no longer pass a weather multiplier: noiseMultiplier = NoiseAIEvaluate.GetNoiseMultiplier(this) then AddNoise(noiseParams, noiseMultiplier) (DayZPlayerImplement.c:3472-3474). Infected voice noise is AddNoise(this, sound_event.m_NoiseParams) with no climate multiplier (ZombieBase.c:597-600). Tune rain/snow/fog/wind/sandstorm in class AIParams (exp\dz\DZ\data\aiconfigs\config.cpp:19-27). Do not also multiply AddNoise by a script weather factor — the engine applies native attenuation on the coordinate and you would double-dampen.)
  • Weapon shots are config-side: class NoiseShoot { strength = 82; type = "shot"; } in the weapon config (real mod example: IMPWMODPart2 MCXSpear config.cpp:73-77).

DayZ 1.30 Exp (build 1.30.164014)

What changes, what breaks, and the migration checklist. [EXACT] blocks: references/dayz-1-30-ai.md.

What changes

  • Native C++ noise attenuation. New proto GetEnvironmentNoiseReduction(vector pos) on NoiseSystem (Noise.c:12-13). HUD/debug reads it via PluginPresenceNotifier.c:155-158.
  • class AIParams gained snowfallToNoiseMultiplier = 5.0, fogToNoiseMultiplier = 10.0, windToNoiseMultiplier = 10.0, sandstormToNoiseMultiplier = 10.0 (1.29 had rain + sea only). [CHANGELOG] official text says weather handling is native and 'Weather.GetNoiseReductionByWeather' was removed; the method still exists as [Obsolete] (Weather.c:454-467) plus GetNoiseReductionByWeatherEx(object) (Weather.c:407-419).
  • Vanilla infected: MINDSTATE_COWER for sandstorm; net-sync range of m_MindState is 5.
  • Nasdara animal group templates DZMouflonGroupBeh / DZDogGroupBeh.
  • [CHANGELOG] navmesh file version has no backwards compatibility — regenerate navmesh for custom terrains (matters to eAI PC-15/PC-16). Navmesh can use a hide animation source via EntityType Navmesh config.

What breaks for a 1.29 AI / stealth mod

  1. Compile warning [Obsolete("Use Weather.GetNoiseReductionByWeatherEx instead!")] if you still call GetNoiseReductionByWeather / NoiseAIEvaluate.GetNoiseReduction.
  2. Overrides of GetNoiseReductionByWeather no longer change what native infected/animal AI actually hear — they hear the native AIParams curve.
  3. Passing a weather-derived multiplier into AddNoise and letting the engine apply native reduction double-damps the event.
  4. Custom terrains: old navmesh files will not load. [CHANGELOG]

Migration checklist

  • For the factor AI actually uses: GetGame().GetNoiseSystem().GetEnvironmentNoiseReduction(pos).
  • For HUD / old script math on a specific object: weather.GetNoiseReductionByWeatherEx(object).
  • To change how rain/snow/fog/wind/sandstorm dampen zombie hearing: edit class AIParams in DZ\data\aiconfigs\config.cpp (rainToNoiseMultiplier, snowfallToNoiseMultiplier, sandstormToNoiseMultiplier, …).
  • Call AddNoise(entity, params) without folding climate into external_strenght_multiplier.
  • If you spawn custom infected, keep m_MindState sync range covering MINDSTATE_COWER (5).
  • Custom maps: regenerate navmesh. Copy/inherit DZMouflonGroupBeh / DZDogGroupBeh if you want those packs.
  • Expansion eAI PC-5..PC-18 in this extract were not re-verified (Expansion scripts are not in exp\). Treat those cites as 1.29 Expansion research.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.