Human07 Mission Troubleshooting
Scope
This document records the Human07 (Maps\\Campaign\\Human07.w3m) lifecycle findings: the
mission-end check, SuicidePlayer, runtime-created buildings, the Town Hall Birth
regression, and the evidence used to distinguish the failures. It separates confirmed
engine fixes from observations that still require a complete in-game replay.
The map script is authoritative for the mission objective. Engine behavior must make the script's queries and lifecycle operations observable; it must not replace the script with a Human07-specific completion rule.
Authoritative data and runtime path
The map was extracted from the local Warcraft III archive with:
The relevant generated war3map.j code is CheckGreenBuildings. It counts the
qualifying Player 6 structures and explicitly excludes the rawcodes uzg1 and
uzig. The optional Crypt handle gg_unit_usep_0049 is removed by the map script on
Easy and Normal. Those are authored exceptions, not a general rule that all neutral
or unfinished structures should be ignored.
The runtime path for a JASS-created building is:
war3map.j CreateUnit
-> unit_create
-> SP_SpawnAtLocationNoBirth
-> SP_CallSpawn
-> SP_SpawnUnit
-> collision initialization
-> gi.LinkEntity
-> server broad-phase bounds / BoxEdicts
The no-birth path is specific to immediate JASS CreateUnit. Construction, training,
and callers that own a Birth or construction lifecycle continue to use
SP_SpawnAtLocation().
Problems and solutions
| Problem | Confirmed cause | Solution |
|---|---|---|
| Human07 did not finish after the visible enemy force was destroyed | RemoveUnit freed the edict while JASS trigger/group/entity iteration was still using the handle. The map's objective query could therefore observe stale or invalid state. |
Hide the unit immediately, remove it from JASS groups and command state, then defer G_FreeEdict until the entity iteration has completed. G_RunDeferredFrees() runs after collision solving. |
| The campaign script could not launch its authored assault | OpenRealm did not expose the SuicidePlayer AI native used by the campaign AI path. |
Implement and register G_BotSuicidePlayer: validate the AI player, captain, and check_full; issue attack orders against valid units owned by the target player, falling back to the staged point when no target is visible; launch live captain members and update captain state. |
| A runtime-created building could be walked through | SP_SpawnAtLocationInternal() linked the entity before SP_CallSpawn(). SP_SpawnUnit() populated building collision during spawn, but SV_LinkEntity() had already calculated zero-sized broad-phase bounds. BoxEdicts() consequently could not return the building as a blocker. |
Call SP_CallSpawn() first and link afterward. This preserves the authored collision in the server spatial index. |
| The Town Hall could remain on the Birth model or retain a birth delay | unit_create() used the presentation-aware Birth path and then immediately requested Stand. Stand changed the animation but did not clear edict.wait, which still contained the authored build time. |
Use SP_SpawnAtLocationNoBirth() in unit_create(), request Stand once, apply facing, and activate food. Immediate JASS creation now starts ready. |
| The deferred replacement Town Hall could render as an unselectable construction-shaped model | A replacement entity could receive an entity delta without an explicit scale. The client baseline and interpolated render state then treated the omitted scale as zero, collapsing the model and its picking shape. | Seed client baselines with scale 1.0 and clamp interpolated non-positive scales to 1.0 in V_AddClientEntity(). This preserves the normal model and selection behavior when deferred-release entities are replaced. |
The map-placed spawn path already called SP_CallSpawn() before linking. The
link-order defect therefore affected dynamically created units, not ordinary
map-placed entities.
Implementation locations
g_spawn.cowns the post-spawn link ordering and the Birth/no-Birth spawn entry points.m_unit.cowns the JASSCreateUnitimmediate-ready path.g_utils.cowns deferred edict retirement;g_main.cdrains it after the frame's entity iteration and collision solve.api_group.hkeeps deferred removals out of JASS unit enumerations while their edicts remain live for the rest of the frame.g_bot.c,api_ai.h, andapi_module.cown theSuicidePlayernative implementation and registration.cl_parse.cseeds entity baseline scale andcl_view.cprotects interpolated render scale so deferred replacement entities retain their normal presentation and picking.
Confirmed fixes
The following evidence is reproducible in the current tree:
wc3_api.removeunit_hides_before_deferred_edict_release,wc3_api.createunit_does_not_reuse_deferred_dead_unit, andwc3_api.killunit_processes_deferred_unit_handlecover deferred unit removal and stale-handle behavior.wc3_api.createunit_starts_ready_without_birth_delaypasses in both Classic and TFT data modes. It verifies Stand andwait == 0for an immediateCreateUnit.wc3_api.createunit_links_building_collision_boundspasses in both Classic and TFT data modes. It verifies collision-sized linked bounds and thatBoxEdicts()returns the runtime-created building.wc3_api.human04_intro_cancel_preserves_unit_lifecycle_until_frame_endreproduces the Human04 cancellation order: a temporaryugolis killed, the worker/building handles are removed, deferred state is checked before the drain, and a replacement building is selected afterward.wc3_jass_map.human04_cancel_replaces_townhall_after_difficulty_removalfollows the extracted Human04war3map.jorder: Normal difficulty removesgg_unit_usep_0087,EVENT_PLAYER_END_CINEMATICdispatches the cancellation trigger, and that trigger removes the old building before creating a replacementhtow. It counts buildings from inside the cancellation callback and verifies the replacement remains selectable while both removed handles await deferred release. The client baseline/render-scale fix is required for the same replacement to remain visible and selectable in the live renderer; it is not controlled by a runtime cvar.wc3_jass_map.human07_normal_removal_is_absent_from_green_building_countfollows Human07's Normal-difficultyusepremoval and itsCheckGreenBuildingsquery; it verifies the Crypt handle remains deferred but is absent from the count.- The focused API tests passed 4/4 for the no-birth lifecycle and 11/11 for the building-link contract in each data mode.
make test-wc3-enginepassed25784/25784assertions in1235tests in both Classic and TFT modes.make -B openwarcraft3completed successfully andgit diff --checkreported no whitespace errors.
The lifecycle regression tests are in
games/warcraft-3/game/tests/t_api.c
and
games/warcraft-3/game/tests/t_jass_map.c;
the AI behavior tests are in
games/warcraft-3/game/tests/t_bot.c.
Confirmed regressions
These were real behavior regressions introduced by intermediate fixes and are now covered or corrected:
- Replacing
CreateUnit's Birth path with an immediate Stand request without also clearing the birth wait caused a Town Hall to appear stuck at Birth. The corrected no-birth path andcreateunit_starts_ready_without_birth_delaytest address it. - Linking a spawned building before its class spawn initializer caused the building
to be visually present but absent from broad-phase collision. The corrected link
order and
createunit_links_building_collision_boundstest address it.
Warsmash comparison and remaining gap
The Warsmash sources in screenshots/WarsmashModEngine distinguish pure simulation
creation from presentation/construction creation:
- JASS
CreateUnitreaches an immediate-ready creation path. - A simple summoned unit may use Birth followed by a queued Stand.
- A building construction path explicitly enters constructing state and owns the Birth presentation.
OpenRealm now matches the important CreateUnit and building-construction ownership
boundary. The summon path still deserves a separate parity test because
s_summon.c uses the presentation-aware spawn path and then requests Stand directly;
that is a potential lifecycle difference, but no confirmed regression from it has
been recorded.
Verification commands
Run focused tests independently when diagnosing either lifecycle issue:
make test-wc3-engine WC3_PATTERN='wc3_api.createunit_starts_ready_without_birth_delay'
make test-wc3-engine WC3_PATTERN='wc3_api.createunit_links_building_collision_bounds'
make test-wc3-engine WC3_PATTERN='wc3_api.human04_intro_cancel_preserves_unit_lifecycle_until_frame_end'
make test-wc3-engine WC3_PATTERN='wc3_jass_map.human04_cancel_replaces_townhall_after_difficulty_removal'
make test-wc3-engine WC3_PATTERN='wc3_jass_map.human07_normal_removal_is_absent_from_green_building_count'
make test-wc3-engine
The automated tests prove state, handle, and spatial-index contracts. A complete Human07 replay remains useful for validating the final camera, trigger, and campaign transition presentation because those visual effects are outside the focused harness.