Skip to content

Research Report R4 Ai Spawn Waves

LopeKinz edited this page Oct 1, 2026 · 1 revision

AI Spawn Waves: Trigger → Spawner → Wave

Research pass asking how a trigger volume reaches an AI spawner, what maxActiveAIs/respawn/reinforcement fields actually gate, how multi-wave encounters are built, and what the minimal AI-wave setup for the custom kit map would be. Everything below is read directly from the retail text corpus (a 29-map/84-archive census) or derived from already-proven writers — the game was never launched; nothing here has been built and tested live. Two real wiring routes exist in retail, one almost exclusively used (kiscule-mediated) and one rare but genuinely present without any kiscule involvement at all; the minimal, lowest-risk kit-map recipe uses only the rare, kiscule-free route, because the kit map currently has no kiscule entity left to attach a script-based version to.

The two wiring routes, corpus-wide

Route Share Mechanism
Kiscule-mediated 99.4% (3,862/3,884 triggers) The idTrigger entity carries no targets field at all; a kiscule TriggerNotification node fires ActionTrigger.Activate, which holds an EntityReference to the spawner.
Direct entity-layer 0.6% (20/3,884 raw instances, 9 distinct trigger entities) The idTrigger's own edit.targets list names the idTarget_AISpawn directly — no kiscule involved in the chain at all, or dual-wired (kiscule also independently references the same trigger, belt-and-suspenders).

Of the 9 distinct direct-entity-layer triggers, 7 are dual-wired and only 2 are genuinely kiscule-free, confirmed two independent ways (a typed kiscule-graph reference scan, and a raw full-text substring search across every script either host map loads — zero hits in both):

c02p0_combat_triggers_trigger_12              -> c02p0_combat_target_ai_spawn_10   (1-AI wave)
c1v3_venus_combat_trigger_before_boss_spawn    -> c1v3_venus_combat_target_ai_spawn_1 (4-AI wave, no maxActiveAIs)

These two are the retail precedent a kiscule-free kit-map trigger should be modeled on.

What the gating fields do

  • maxActiveAIs (on idTarget_AISpawn, set on 504/983 = 51% of all spawners corpus-wide): throttles how many of that spawner's own targets may be alive simultaneously. -1 = unlimited (83 uses); small integers 2–15 are typical (mode 12). It does not gate a shared pool across spawners — idEncounterGroupMgr (independently re-verified by reading a real retail block) carries no member list, only a name/alarm-system pointer, so cross-spawner limits are not visible in the data at all.
  • idScenePoint_Spawn.reuseDelay (2,764 scene points corpus-wide, mean 4.49 s, range 0–20 s): lets the same world position hand out another AI from its template list after a cooldown, gated by the owning spawner's maxActiveAIs. This is the respawn-point mechanic.
  • idAlarmSystem.alarmSpawner (present on 195/196 = 99.5% of all alarm systems): the reinforcement mechanic. Retail always keeps a separate, otherwise-idle spawner dedicated to reinforcements — it never reuses a combat spawner that triggers already feed.
  • Observed pattern, not proven mechanically: retail's proven multi-wave pattern is one dedicated spawner per wave, never re-triggering the same spawner twice. A kiscule AiKilledNotification → Counter → Delay → ActionTrigger chain fires the next wave's separate spawner. This exact pattern is not just inferred — it has already been built and structurally verified (37/37 checks) as a working two-wave container elsewhere in this project.
  • Whether a single spawner honors maxActiveAIs as a live gate on repeated firing (true re-trigger/respawn of the same spawner) versus being one-shot per name was not settled by disassembly — the field is read through the engine's generic reflection-table binding, not a single named code site, so a targeted disassembly attempt hit a wall. The practical answer is the data pattern itself: retail never does this, so a mod shouldn't rely on unverified re-trigger semantics either.

Minimal recipe for the kit map

The kit map today has zero triggers, spawners, alarms, scene points, or kiscule entities — the skeleton-reduction rules that build it strip idKisculeEntity, all AI classes, and all trigger/logic entities outright. This makes a kiscule-free recipe the natural, lower-risk fit:

Recipe A — entity-layer only, no kiscule (recommended):

  1. One new .cm collision box for the trigger volume (existing round-trip writer).
  2. One new idTrigger entity: inherit="triggers/trigger", class="idTrigger", renderModelInfo.model=NULL, clipModelInfo.clipModelName = the new box, edit.targets={num=1; item[0]="<spawner>"}.
  3. One new idTarget_AISpawn entity, modeled on the c1v3_venus... retail precedent above: edit.targets naming N new hidden AI (corpus-typical wave size 3–8, mean 5.28), maxActiveAIs optional.
  4. N new idAI2 entities, flags.hide=true, reusing an AI definition already mounted for the kit map, placed on its already-validated navmesh-backed area.
  5. No kiscule entity, no idEncounterGroupMgr/idAlarmSystem/idScenePoint_* needed — all proven optional by the retail precedent above.

This adds zero new decl/archive-entry names — only a .entities text edit (existing byte-exact round-trip proof) and one new .cm entry (existing round-trip proof) — which is why it is lower-risk, not merely shorter.

Recipe B — kiscule multi-wave (more faithful, higher risk): reuses the already-built-and-verified kill-counter → delay → next-wave pattern verbatim, but every kiscule write this project has ever proven appends to the payload of a kiscule entry a retail container already has. The kit map has no idKisculeEntity left to append to, so Recipe B needs either restoring one retail kiscule entity wholesale and stripping it down (untried), or authoring a genuinely new kiscule archive entry under a name no shipped container has — a step the project's tools can technically perform but which no report has actually exercised. This is the single open question that would decide whether Recipe B is viable at all.

Failed / corrected

  • An earlier pass's two cited "kiscule-free" retail precedents (commanderarea_secondfloor/thirdfloor) are actually dual-wired — kiscule also independently reaches them. The two retail precedents used above (c02p0_combat_triggers_trigger_12, c1v3_venus_combat_trigger_before_boss_spawn) are the corrected, confirmed-bare pair.

Open questions

  • Whether a brand-new kiscule archive entry (Recipe B) is structurally acceptable to the loader at all.
  • reuseDelay/maxActiveAIs omission-default semantics (does omitting maxActiveAIs mean unlimited, or targets.num?) — unresolved, needs disassembly support not yet available.
  • A small residual (~17%) of sampled kiscule-only triggers whose name a reference scan could not find referenced anywhere — most likely a method gap (a reference mechanism the scan doesn't model) rather than more undiscovered kiscule-free triggers, but not conclusively ruled out.
  • Everything here is data/structure only: no AI spawn, wave, or alarm has been observed in a running game in this project yet.

In-game test (not yet run)

Install a kit-map variant containing Recipe A's new trigger + spawner + hidden AI, start the game with developer mode enabled, load the test map, and walk into the trigger volume. If the previously-invisible AI appears exactly on crossing the trigger box and engages, that is the first runtime evidence for the pure entity-layer spawn route in this project — until then it rests only on retail precedent, never built or started.

Related: Mission Anatomy, Mission Kiscule Scripting, Kiscule Scripting, TNC Modding Surface.

Clone this wiki locally