Repository navigation
Research Report R4 Ai Spawn Waves
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.
| 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.
-
maxActiveAIs(onidTarget_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'smaxActiveAIs. 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→ActionTriggerchain 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
maxActiveAIsas 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.
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):
- One new
.cmcollision box for the trigger volume (existing round-trip writer). - One new
idTriggerentity:inherit="triggers/trigger",class="idTrigger",renderModelInfo.model=NULL,clipModelInfo.clipModelName= the new box,edit.targets={num=1; item[0]="<spawner>"}. - One new
idTarget_AISpawnentity, modeled on thec1v3_venus...retail precedent above:edit.targetsnaming N new hidden AI (corpus-typical wave size 3–8, mean 5.28),maxActiveAIsoptional. - N new
idAI2entities,flags.hide=true, reusing an AI definition already mounted for the kit map, placed on its already-validated navmesh-backed area. - 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.
- 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.
- Whether a brand-new kiscule archive entry (Recipe B) is structurally acceptable to the loader at all.
-
reuseDelay/maxActiveAIsomission-default semantics (does omittingmaxActiveAIsmean unlimited, ortargets.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.
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.
English Wiki generated from the measured research notes in docs/. Evidence grades and uncertainty are preserved intentionally.
- WolfSDK Loader · DE
- WolfSDK Studio · DE
- FAQ · DE
- Mod format · DE
- Modding Guide
- Modding Surface
- CVar Catalog
- CVar Validation (old scrape)
- Texture Database
- Developer Mode
- Dev Menu
- Custom-Map Status