Skip to content

Town AI Lifecycle Reference

rayswaynl edited this page Jul 3, 2026 · 1 revision

Town AI Lifecycle Reference

This page maps town AI activation, garrison spawning, patrol/defense stance, static-defense manning, mop-up cleanup and deactivation.

Snapshot: origin/claude/build84-cmdcon36@5fca32390 on 2026-07-03. Paths below use source Chernarus as the canonical root: Missions/[55-2hc]warfarev2_073v48co.chernarus/. Maintained Takistan/Zargabad copies should be handled through Tools/LoadoutManager for source PRs; this page is wiki-only and changed no mission files.

Fast Route

Question Start here
Why did a town wake up? Server/FSM/server_town_ai.sqf:118-153 scans nearby entities, ignores town/static defender AI, filters hostile sides for owned towns, and records town_activation_scan timing.
Why did a town spawn groups? server_town_ai.sqf:156-171,211-221 keeps wfbe_inactivity fresh, uses wfbe_episode_spawned to avoid duplicate episodes, and chooses attacker or defender group selectors.
Why did air-only contact spawn different groups? server_town_ai.sqf:227-236 sets wfbe_active_air and calls the selectors with _aa_get = true. Current selector AA handling has a pending fix in PR #484.
Why did a garrison switch from patrol to defense? Server/FSM/server_town_patrol.sqf:27-47 switches to SAD defense when SV drops below the previous/start value or ownership changes. PR #486 adds a default-off contested-only gate.
Why did a town clean up? server_town_ai.sqf:399-453 deactivates after no enemies for WFBE_C_TOWNS_UNITS_INACTIVE, tells delegated clients/HCs to clean up, deletes server-local AI safely, removes static-defense operators, and clears the episode latch.
Why did capture spawn a cleanup squad? Server/FSM/server_town.sqf:364-485 runs the lazy post-capture path for WEST/EAST owned towns: no inherited GUER statics, a T+60 mop-up infantry squad, and a scan loop that stands down after clear scans. PR #487 adds a TTL cap.

State Variables

Variable Owner Meaning
wfbe_active server_town_ai.sqf Ground activation is live for this town. Counts toward the active-town budget.
wfbe_active_air server_town_ai.sqf Air-only activation is live. Selectors are called with _aa_get = true when this is first set.
wfbe_inactivity server_town_ai.sqf Last time live enemies were detected; deactivation waits until this is older than WFBE_C_TOWNS_UNITS_INACTIVE.
wfbe_episode_spawned server_town_ai.sqf Per-episode latch. It is set when groups spawn and cleared only after cleanup completes, preventing double-spawn during deletion.
wfbe_town_teams server_town_ai.sqf plus delegation callbacks Groups currently owned by the town activation episode. Server cleanup and delegated cleanup both key from this.
wfbe_active_vehicles server_town_ai.sqf Vehicles returned by town-unit creation/delegation. Current source deletes only local vehicles with zero player crew.
wfbe_contested server_town.sqf Local, non-broadcast stamp written on capture pressure transitions. PR #486 reuses it as an optional patrol-defense gate.
wfbe_mopup_group / wfbe_mopup_units server_town.sqf Post-capture cleanup squad references used by the lazy garrison path.

Activation Flow

server_town_ai.sqf seeds every town with inactive state, empty team/vehicle arrays, and a clear episode latch at :50-65. Startup pacing is already tunable through WFBE_C_TOWNS_STARTUP_SLEEP: the script reads it at :3, clamps <= 0 to the legacy 0.01, and sleeps in both initialization passes at :10 and :65. PR #343 documents that as an existing default-preserving hook, not a source gap.

Each sweep:

  1. The script computes active-town and GUER group budgets from WFBE_C_TOWNS_ACTIVE_MAX, pop-tier overrides, and WFBE_C_GUER_GROUPS_MAX (:33-45 plus the sweep body).
  2. For each town, it picks inactive or active detection range from WFBE_C_TOWNS_DETECTION_RANGE_COEF / WFBE_C_TOWNS_DETECTION_RANGE_ACTIVE_COEF, scans Man, Car, Motorcycle, Tank, Air, and Ship, then applies unitsBelowHeight 20 (:118-119). PR #327 is the adjacent measurement/audit lane for this broad scan.
  3. Units tagged WFBE_IsTownDefenderAI are ignored so existing town/static defenders do not wake their own or neighboring towns (:121-132).
  4. For WEST/EAST owned towns, friendly passers-by are filtered out before enemy counting; GUER/UNKNOWN towns keep broader behavior (:134-149).
  5. When enemies are present, wfbe_inactivity is refreshed and the episode latch decides whether a new spawn is legal (:156-171).
  6. Ground contact sets wfbe_active, increments the active-town counter, and calls WFBE_SE_FNC_GetTownGroups or WFBE_SE_FNC_GetTownGroupsDefender (:211-221).
  7. Air-only contact sets wfbe_active_air and calls the same selector family with _aa_get = true (:227-236).

Group Selection

Group selection is split:

Situation Function Notes
WEST/EAST town occupation Server/Functions/Server_GetTownGroups.sqf Scales by supplyValue, side upgrades and WFBE_C_TOWNS_UNITS_COEF; see Town AI group composition catalog.
GUER/resistance defender town Server/Functions/Server_GetTownGroupsDefender.sqf Scales by wfbe_town_type and WFBE_C_TOWNS_UNITS_DEFENDER_COEF; see Town AI group composition catalog.
Air-only wake Either selector with _aa_get = true Current target caps _groups_max at 3 and keeps only AA_Light, AA_Heavy, or Team_AA (Server_GetTownGroups.sqf:143-155, defender equivalent).

Current target caveat: the normal selector path excludes AA groups when wfbe_active_air is already true (Server_GetTownGroups.sqf:154, Server_GetTownGroupsDefender.sqf:114). Draft PR #484 flips that filter so active-air towns keep AA eligible while the explicit _aa_get path remains AA-only.

Both selectors include spawn-time infantry group merging to reduce group-brain load while leaving vehicle rosters unmerged. Attacker merge logic starts at Server_GetTownGroups.sqf:214-244; defender has the sibling merge block and defender-specific cap constants. Use Town AI group composition catalog before changing templates, weights or merge constants.

Creation And Delegation

After group selection, the activation script chooses camp/town-adjacent positions and preallocates town groups (server_town_ai.sqf:246-262). Creation can happen three ways:

Delegation mode Route Lifecycle rule
Client delegation (WFBE_C_AI_DELEGATION = 1) WFBE_SE_FNC_DelegateAITown at server_town_ai.sqf:267-273 Server stores fallback groups/vehicles returned by the delegate route; delegated machines report their own groups back.
Headless delegation (WFBE_C_AI_DELEGATION = 2) live-HC gate at :275-282 HC path is used only when live HC registry groups have non-null, alive leaders.
Server fallback WFBE_CO_FNC_CreateTownUnits at :286-292 Server-created groups and vehicles are appended to wfbe_town_teams and wfbe_active_vehicles.

Static-defense manning is a separate call immediately after town group creation (server_town_ai.sqf:295-296). Server_OperateTownDefensesUnits.sqf creates per-town, per-side gunner groups capped at 12 members (:21-45), can delegate static-defense gunners to an HC when one is live (:55-67), tags operators as WFBE_IsTownDefenderAI (:78-80), and removes them on cleanup (:108-123). Use Static-defense manning for the dedicated static-gunner route.

Patrol Versus Defense

server_town_patrol.sqf is the per-town-team stance worker. It is not the side-upgrade Patrols v2 driver.

Current target behavior:

  • starts in "patrol" and records the town's current/start SV (:11-17);
  • exits once the team is gone (:18-25);
  • switches to "defense" when current SV drops below the previous SV, current SV is below starting SV, or town ownership changed (:27-31);
  • patrol mode uses WaypointPatrolTown or focused WaypointPatrol (:40-45);
  • defense mode uses a SAD waypoint at WFBE_C_TOWNS_DEFENSE_RANGE (:46-47);
  • the monitor sleeps 30 seconds between checks (:56).

Adjacent draft PR #486 adds default-off WFBE_C_TOWNS_PATROL_CONTESTED_ONLY. With that flag enabled, supply-loss defense flips require the local wfbe_contested stamp from server_town.sqf:64-90; ownership changes still force defense. Until that PR merges, current target remains the legacy SV/ownership mode switch.

Capture, Lazy Garrison And Mop-Up

Town capture is owned by server_town.sqf, not by server_town_ai.sqf. On each capture:

  • the capture loop sets the new sideID and broadcasts TownCaptured (server_town.sqf:239-300);
  • it clears wfbe_active, wfbe_active_air and wfbe_episode_spawned so the new owner can re-garrison immediately (:294-298);
  • old defender cleanup has a WFBE_C_TOWNS_DEFENDER_LINGER delay before removing old-side defense units if the town stayed captured (:356-360);
  • WEST/EAST captures of GUER towns delete inherited GUER statics so the captor cannot keep them (:383-392);
  • WEST/EAST owned towns use the lazy path: T+60 seconds, spawn one owner-side infantry squad, then wait for two clear 30-second scans before standing it down (:394-485);
  • resistance recapture keeps the existing delayed defender path using WFBE_C_TOWNS_DEFENSE_SPAWN_DELAY (:366-381).

Current target caveat: the mop-up scan loop has no explicit lifetime cap. It exits on town flip/deactivation, empty group, game over, or two clear scans. Draft PR #487 adds WFBE_C_TOWNS_MOPUP_TTL = 600 so persistent resistance or reactivation cannot keep the mop-up worker alive indefinitely.

Deactivation And Cleanup

When no enemies are detected and time - wfbe_inactivity exceeds WFBE_C_TOWNS_UNITS_INACTIVE, server_town_ai.sqf deactivates the town:

  • decrement active-town count if the town was ground-active (:399-407);
  • clear wfbe_active and wfbe_active_air (:407-408);
  • broadcast cleanup-townai so delegated clients/HCs can delete local groups where deleteGroup works (:412-413);
  • delete only server-local non-player units and delete groups only when no non-local units remain (:415-430);
  • delete only local active vehicles with zero player crew, avoiding the old passenger/gunner deletion bug (:432-441);
  • clear town team/vehicle arrays (:443-445);
  • remove town static-defense operators (:447-448);
  • clear wfbe_episode_spawned only after cleanup completes (:450-453).

This cleanup path is why Town AI vehicle despawn safety is still important when auditing older branches: current target source Chernarus has the player-crew guard, but older branch matrices and generated roots have carried drift before.

Pending Adjacent PRs

PR Lane Relevance to this page
#327 Lane 106 town activation scan Measures the real server_town_ai broad scan cost and documents current range/cadence.
#343 Lane 115 startup pacing Documents the existing WFBE_C_TOWNS_STARTUP_SLEEP hook; no source change.
#484 Lane 191 AA active-air gate Fixes the normal selector path so active-air towns keep AA groups eligible.
#486 Lane 190 patrol contested-only Adds a default-off WFBE_C_TOWNS_PATROL_CONTESTED_ONLY gate for SV-loss defense flips.
#487 Lane 200 mop-up TTL Adds a default 600 second lifetime cap to post-capture mop-up squads.

Treat these as pending draft behavior until their PRs are merged into the target branch. Do not write wiki wording that makes PR-only behavior sound live.

Practical Rules For Agents

  • Do not patch server_town_ai.sqf, server_town.sqf, server_town_patrol.sqf, group selectors or static-defense manning as one blob. They are separate lifecycle surfaces.
  • If source changes touch maintained terrain copies, start in source Chernarus and propagate with Tools/LoadoutManager unless a release owner explicitly approves another path.
  • If source is hot or already covered by an open PR, prefer docs/status clarification over a duplicate source PR.
  • Check wfbe_episode_spawned before adding any spawn path; clearing it too early can double-spawn during cleanup.
  • Check WFBE_IsTownDefenderAI before changing activation scans; defender AI should not wake towns.
  • Keep PR-only behavior labelled as pending until merged into origin/claude/build84-cmdcon36.

Source Anchors

Topic Anchor
Startup pacing and state seed Server/FSM/server_town_ai.sqf:3-65; Common/Init/Init_CommonConstants.sqf:255
Activation scan and hostile filtering Server/FSM/server_town_ai.sqf:118-153
Episode spawn gate and ground/air routes Server/FSM/server_town_ai.sqf:156-236
Creation/delegation/static-defense spawn Server/FSM/server_town_ai.sqf:246-296; Server/Functions/Server_OperateTownDefensesUnits.sqf:21-85
Capture contested stamp Server/FSM/server_town.sqf:45-90
Capture-side transition and active flag reset Server/FSM/server_town.sqf:239-301
Lazy garrison and mop-up Server/FSM/server_town.sqf:356-485
Patrol/defense stance worker Server/FSM/server_town_patrol.sqf:1-57
Group selector AA/merge behavior Server/Functions/Server_GetTownGroups.sqf:140-155,214-244; Server/Functions/Server_GetTownGroupsDefender.sqf:140-160
Cleanup and despawn Server/FSM/server_town_ai.sqf:399-453; Server/Functions/Server_OperateTownDefensesUnits.sqf:108-123

Continue Reading

Previous: Towns, camps and capture atlas | Next: Town AI group composition catalog

Main map: Home | Fast path: Quickstart | Agent pack: LLM agent entry pack

Sidebar

Clone this wiki locally