Creature spawning (creature_spawn_template / FUN_00430af0)¶
creature_spawn_template(template_id, pos_xy, heading) is the primary translation layer from
"spawn ids" (quests, tutorial timelines, and other scripted spawners) to initialized creature_t
entries.
It is not a static struct/table: it always performs base creature initialization, runs a large template switch, may allocate additional creatures and/or spawn-slot entries, and then applies shared tail modifiers (difficulty/hardcore, demo gating, etc).
For the pure, unit-testable model we use while porting templates, see: spawn_plan.md.
Inputs¶
template_id(akaparam_1): spawn id used by quest tables and other spawners.pos_xy(akaparam_2): spawn position (two floats).heading(akaparam_3): radians; sentinel-100.0means "randomize heading".- Globals consulted:
- RNG stream:
crt_rand()(MSVCRTrand()). demo_mode_active: skips the spawn burst effect when nonzero.terrain_texture_width/terrain_texture_height: bounds check for the burst effect.config_blob.hardcoreandDAT_00487194(difficulty level): final stat modifiers.
Outputs and side effects¶
-
Returns a pointer into
creature_pool(creature_t *). Some templates return the last creature allocated (for formations), not necessarily the base creature. -
May allocate additional
creature_poolentries (formation spawns, escorts). - May allocate and configure spawn-slot entries (deferred child spawns driven by
creature_update_all). - May spawn a burst effect at the spawn position (skipped in demo mode or when out of bounds).
Algorithm sketch (high level)¶
1) Base init (always)¶
- Allocates a creature slot (
creature_alloc_slot()), then writes base fields: ai_mode = 0,pos_xy,vel_xy = 0active = 1,state_flag = 1- collision defaults (
collision_flag = 0,collision_timer = 0) hitbox_size = 16,attack_cooldown = 0- Seeds a transient random heading early:
crt_rand() % 0x13a * 0.01. - If
heading == -100.0, randomizes the final heading:crt_rand() % 0x274 * 0.01. creature_alloc_slot()itself consumes RNG to seed per-creature defaults (notablyphase_seed).
2) Template switch (template-specific)¶
Large switch/if-chain on template_id assigns template-specific constants and behavior:
-
Stats:
type_id,flags,health,move_speed,reward_value,size,tint_rgba,ai_mode, and various AI/link fields. -
Formation spawners: allocate N linked children and arrange them using circular offsets (
cos/sin) and AI link modes (e.g.ai_mode = 3withlink_index = parent). -
Spawn-slot spawners: allocate a slot (
FUN_00430ad0()), store the slot index inlink_index, and configurecreature_spawn_slot_*arrays (timer/count/limit/interval/template/owner).
3) Tail modifiers (shared end-of-function)¶
Applied after the template switch to the returned creature:
- If not in demo mode and inside terrain bounds:
effect_spawn_burst(pos, 8). max_health = health.-
Note: difficulty/hardcore scaling applies to
healthafter this assignment, somax_healthretains the pre-scaled value. -
Spider SP1 special case: when
type_id == 3and flags do not include0x10or0x80, sets0x80, clearslink_index, and applies amove_speed *= 1.2buff. -
Template
0x38special case: in hardcore, appliesmove_speed *= 0.7. - Overwrites
headingwith the final (possibly randomized) heading argument. - Difficulty / hardcore scaling:
-
Non-hardcore:
- For flag
0x4spawners:spawn_slot_interval += 0.2. - If
DAT_00487194 > 0, scales reward/speed/contact/health, and for flag0x4spawners addsmin(3.0, difficulty * 0.35)tospawn_slot_interval.
- For flag
-
Hardcore:
- Clears difficulty (
DAT_00487194 = 0). - Buffs speed/contact/health.
- For flag
0x4spawners:spawn_slot_interval -= 0.2clamped to>= 0.1.
- Clears difficulty (
Spawn id porting checklist (rewrite)¶
This tracks our creature_spawn_template rewrite coverage.
- Ported: implemented in
build_spawn_plan(pure plan builder). - Verified: covered by unit tests in
tests/test_spawn_plan.py. - Legend: β complete Β· π§ in progress Β· β¬ not started
- Note: spawn id
0x02does not appear in the decompile extracts and is omitted.
Generated by uv run scripts/gen_spawn_templates.py.
| Spawn id | Creature | Ported | Verified |
|---|---|---|---|
0x0 |
zombie |
β | β |
0x1 |
spider_sp2 |
β | β |
0x3 |
spider_sp1 |
β | β |
0x4 |
lizard |
β | β |
0x5 |
spider_sp2 |
β | β |
0x6 |
alien |
β | β |
0x7 |
alien |
β | β |
0x8 |
alien |
β | β |
0x9 |
alien |
β | β |
0xa |
alien |
β | β |
0xb |
alien |
β | β |
0xc |
alien |
β | β |
0xd |
alien |
β | β |
0xe |
alien |
β | β |
0xf |
alien |
β | β |
0x10 |
alien |
β | β |
0x11 |
lizard |
β | β |
0x12 |
alien |
β | β |
0x13 |
alien |
β | β |
0x14 |
alien |
β | β |
0x15 |
alien |
β | β |
0x16 |
lizard |
β | β |
0x17 |
spider_sp1 |
β | β |
0x18 |
alien |
β | β |
0x19 |
alien |
β | β |
0x1a |
alien |
β | β |
0x1b |
spider_sp1 |
β | β |
0x1c |
lizard |
β | β |
0x1d |
alien |
β | β |
0x1e |
alien |
β | β |
0x1f |
alien |
β | β |
0x20 |
alien |
β | β |
0x21 |
alien |
β | β |
0x22 |
alien |
β | β |
0x23 |
alien |
β | β |
0x24 |
alien |
β | β |
0x25 |
alien |
β | β |
0x26 |
alien |
β | β |
0x27 |
alien |
β | β |
0x28 |
alien |
β | β |
0x29 |
alien |
β | β |
0x2a |
alien |
β | β |
0x2b |
alien |
β | β |
0x2c |
alien |
β | β |
0x2d |
alien |
β | β |
0x2e |
lizard |
β | β |
0x2f |
lizard |
β | β |
0x30 |
lizard |
β | β |
0x31 |
lizard |
β | β |
0x32 |
spider_sp1 |
β | β |
0x33 |
spider_sp1 |
β | β |
0x34 |
spider_sp1 |
β | β |
0x35 |
spider_sp2 |
β | β |
0x36 |
alien |
β | β |
0x37 |
spider_sp2 |
β | β |
0x38 |
spider_sp1 |
β | β |
0x39 |
spider_sp1 |
β | β |
0x3a |
spider_sp1 |
β | β |
0x3b |
spider_sp1 |
β | β |
0x3c |
spider_sp1 |
β | β |
0x3d |
spider_sp1 |
β | β |
0x3e |
spider_sp1 |
β | β |
0x3f |
spider_sp1 |
β | β |
0x40 |
spider_sp1 |
β | β |
0x41 |
zombie |
β | β |
0x42 |
zombie |
β | β |
0x43 |
zombie |
β | β |
Spawn template ids (direct type/flags map)¶
The large template switch inside creature_spawn_template assigns type_id and sometimes
flags. The table below lists only these direct assignments (useful for labeling).
It does not capture randomized parameters, formation spawns, spawn slots, or tail modifiers.
Generated by uv run scripts/gen_spawn_templates.py.
| Spawn id (template_id) | Type id | Creature | Flags (creature_flags) | Anim note |
|---|---|---|---|---|
0x0 |
0 |
zombie |
0x44 |
long strip (0x40 overrides 0x4) |
0x1 |
4 |
spider_sp2 |
0x8 |
|
0x3 |
3 |
spider_sp1 |
`` | |
0x4 |
1 |
lizard |
`` | |
0x5 |
4 |
spider_sp2 |
`` | |
0x6 |
2 |
alien |
`` | |
0x7 |
2 |
alien |
0x4 |
short strip (ping-pong) |
0x8 |
2 |
alien |
0x4 |
short strip (ping-pong) |
0x9 |
2 |
alien |
0x4 |
short strip (ping-pong) |
0xa |
2 |
alien |
0x4 |
short strip (ping-pong) |
0xb |
2 |
alien |
0x4 |
short strip (ping-pong) |
0xc |
2 |
alien |
0x4 |
short strip (ping-pong) |
0xd |
2 |
alien |
0x4 |
short strip (ping-pong) |
0xe |
2 |
alien |
0x4 |
short strip (ping-pong) |
0xf |
2 |
alien |
`` | |
0x10 |
2 |
alien |
0x4 |
short strip (ping-pong) |
0x11 |
1 |
lizard |
`` | |
0x12 |
2 |
alien |
`` | |
0x13 |
2 |
alien |
`` | |
0x14 |
2 |
alien |
`` | |
0x15 |
2 |
alien |
`` | |
0x16 |
1 |
lizard |
`` | |
0x17 |
3 |
spider_sp1 |
`` | |
0x18 |
2 |
alien |
`` | |
0x19 |
2 |
alien |
`` | |
0x1a |
2 |
alien |
`` | |
0x1b |
3 |
spider_sp1 |
`` | |
0x1c |
1 |
lizard |
`` | |
0x1d |
2 |
alien |
`` | |
0x1e |
2 |
alien |
`` | |
0x1f |
2 |
alien |
`` | |
0x20 |
2 |
alien |
`` | |
0x21 |
2 |
alien |
`` | |
0x22 |
2 |
alien |
`` | |
0x23 |
2 |
alien |
`` | |
0x24 |
2 |
alien |
`` | |
0x25 |
2 |
alien |
`` | |
0x26 |
2 |
alien |
`` | |
0x27 |
2 |
alien |
0x400 |
bonus_id=WEAPON (3), duration_override=5 (packed in link_index) |
0x28 |
2 |
alien |
`` | |
0x29 |
2 |
alien |
`` | |
0x2a |
2 |
alien |
`` | |
0x2b |
2 |
alien |
`` | |
0x2c |
2 |
alien |
`` | |
0x2d |
2 |
alien |
`` | |
0x2e |
1 |
lizard |
`` | |
0x2f |
1 |
lizard |
`` | |
0x30 |
1 |
lizard |
`` | |
0x31 |
1 |
lizard |
`` | |
0x32 |
3 |
spider_sp1 |
`` | |
0x33 |
3 |
spider_sp1 |
`` | |
0x34 |
3 |
spider_sp1 |
`` | |
0x35 |
4 |
spider_sp2 |
`` | |
0x36 |
2 |
alien |
`` | |
0x37 |
4 |
spider_sp2 |
0x100 |
|
0x38 |
3 |
spider_sp1 |
0x80 |
|
0x39 |
3 |
spider_sp1 |
0x80 |
|
0x3a |
3 |
spider_sp1 |
0x10 |
projectile_type=9 |
0x3b |
3 |
spider_sp1 |
`` | |
0x3c |
3 |
spider_sp1 |
0x100 |
projectile_type=26 (packed in orbit_radius) |
0x3d |
3 |
spider_sp1 |
`` | |
0x3e |
3 |
spider_sp1 |
`` | |
0x3f |
3 |
spider_sp1 |
`` | |
0x40 |
3 |
spider_sp1 |
`` | |
0x41 |
0 |
zombie |
`` | |
0x42 |
0 |
zombie |
`` | |
0x43 |
0 |
zombie |
`` |
Notes:
src/crimson/creatures/spawn.pycontains both the spawn-id labeling index (direct type/flags) and the pure plan builder (formations/spawn slots/tail modifiers).
Spawn id sources (call sites)¶
template_id is supplied by a mix of scripted spawners and data tables:
-
demo_setup_variant_0(FUN_00402ed0),demo_setup_variant_2(FUN_00402fe0),demo_setup_variant_1(FUN_004030f0),demo_setup_variant_3(FUN_00403250): mode setup helpers called fromdemo_mode_start(FUN_00403390) (hardβcoded spawn ids like0x34,0x35,0x38,0x41,0x24,0x25). -
survival_update(FUN_00407cd0): milestone spawns using0x12,0x2b,0x2c,0x35,0x38,0x3a,0x3c, and1. Regular enemy waves are spawned viasurvival_spawn_creature(FUN_00407510), which selects type/stats based onplayer_experience(not a spawn id). Python models:advance_survival_spawn_stage,tick_survival_wave_spawns,build_survival_spawn_creature. -
Rush mode (
rush_mode_update,FUN_004072b0): spawns edge waves viacreature_spawn(type ids2/3), notcreature_spawn_template. Python models:tick_rush_mode_spawns,build_rush_mode_spawn_creature. -
Tutorial timeline (
tutorial_timeline_update,FUN_00408990): scripted spawns using0x24,0x26,0x27,0x28,0x40. Python models:build_tutorial_stage3_fire_spawns,build_tutorial_stage4_clear_spawns,build_tutorial_stage5_repeat_spawns,build_tutorial_stage6_perks_done_spawns. -
Quest/timeline spawner (
quest_spawn_timeline_update,FUN_00434250): pulls spawn ids from the table atDAT_004857a8(pfVar4[3]) with counts inpfVar4[5]. Python model:crimson.quests.timeline.tick_quest_spawn_timeline(see alsotick_quest_mode_spawnsfor thequest_mode_updategating). -
AI subspawns (
creature_update_all): periodic spawns using&DAT_00484fe4 + iVar6 * 0x18, which is seeded for some template ids insidecreature_spawn_template(FUN_00430af0).
Quest spawn table (DAT_004857a8)¶
Quests populate a fixed table at DAT_004857a8 (entry size 0x18, count in
DAT_00482b08). quest_spawn_timeline_update (FUN_00434250) walks the table and
spawns entries whose trigger time has elapsed.
Entry layout (dwords):
| Offset | Field | Notes |
|---|---|---|
| 0x00 | x | base spawn X; if offscreen, the group spreads along Y instead of X. |
| 0x04 | y | base spawn Y. |
| 0x08 | heading | passed as heading to creature_spawn_template. |
| 0x0c | spawn id | cast to int and passed as template_id to creature_spawn_template. |
| 0x10 | trigger time | compared against DAT_00486fd0 (quest clock). |
| 0x14 | count | number of spawns in the group; decremented to 0 after firing. |
Notes:
-
quest_start_selected(FUN_0043a790) chooses a quest builder from the table atDAT_00484730. The function pointer lives at&DAT_0048474c; when null, it falls back toquest_build_fallback(FUN_004343e0) (two entries with spawn id0x40, counts 10/0x14, trigger times 500/5000). -
quest_build_zombie_time(0x00437d70),quest_build_lizard_raze(0x00438840), andquest_build_surrounded_by_reptiles(0x00438940) are examples of quest builders that write multipleDAT_004857a8entries with varying spawn ids and timings.
Repo references¶
- Decompile extracts (used for reconciliation):
artifacts/creature_spawn_template/ghidra.cartifacts/creature_spawn_template/ida.cartifacts/creature_spawn_template/binja-hlil.txt- Creature pool + spawn-slot fields:
docs/creatures/struct.md - Rewrite model (pure plan builder):
src/crimson/creatures/spawn.py - Survival mode (pure models):
src/crimson/creatures/spawn.py advance_survival_spawn_stage,tick_survival_wave_spawns,build_survival_spawn_creature- Tests:
tests/test_survival_milestones.py,tests/test_survival_wave.py,tests/test_survival_spawn.py - Rush mode (pure models):
src/crimson/creatures/spawn.py tick_rush_mode_spawns,build_rush_mode_spawn_creature- Tests:
tests/test_rush_mode_spawn.py - Tutorial timeline (pure models):
src/crimson/creatures/spawn.py -
build_tutorial_stage3_fire_spawns,build_tutorial_stage4_clear_spawns,build_tutorial_stage5_repeat_spawns,build_tutorial_stage6_perks_done_spawns -
Tests:
tests/test_tutorial_timeline_spawns.py - Quest timeline (pure model):
src/crimson/quests/timeline.py tick_quest_spawn_timeline,tick_quest_mode_spawns,quest_spawn_table_empty- Tests:
tests/test_quest_spawn_timeline.py,tests/test_quest_mode_spawns.py - MSVCRT-compatible RNG for deterministic replays:
src/grim/rand.py(Crand)