To catch “this unit cast Storm” people count the storm sprite, or watch for the energy to drop. Neither is necessary: a unit writes down what it is doing, continuously, in its own struct.
| order (0x4D) | Which order it is. |
|---|---|
| orderState (0x4E) | How far through that order it is - this is the one that matters. |
| orderSignal (0x4F) | Whether an animation is playing, or has finished. |
| secondaryOrder (0xA6) | What it is doing as well: cloaking, producing a unit, spreading creep. |
Nearly every spell has the same shape. orderState 1 is moving into range, 2 is the cast itself, and 2 with orderSignal 2 is the cast animation playing.
// 스톰 시전을 정확히 한 번 잡는다
// catches the Storm cast exactly once
if (unit.order == 142 && unit.orderState == 2) {
// 시전자도 대상 지점도 여기서 읽을 수 있습니다
// the caster and the target point are both readable here
}| 2 | An image is being played because the action needs it. |
|---|---|
| 4 | That image finished - the completion edge for a morph, burrow, siege or lift. |
| 109 | The unit is cloaked, Protoss or not, including the burrow invisibility bug. |
There is a trap on signal 4: a unit that was pre-placed, hallucinated or created by a trigger never clears it back to 0. Treat 4 as an edge you see once, not as a state you can poll.
userActionFlags at 0x93 tells you whether a human asked for this. It is 0 at creation and becomes 2 when a user-issued order is accepted.
| 0 → 2 | A user-issued order was accepted. |
|---|---|
| 2 → 0 | A ground unit that has to move to carry it out. Buildings and air units stay at 2. |
| +1 (→3) | A second order given while the current one cannot be interrupted - the queued-order marker. |
| -1 | When that queued order is picked up. |
A rally-point move never sets 2, and neither does a right-click rally on a building, though R-then-click does. Lift and land are excluded as well. It is a usable “the player is actively driving this unit” signal and a bad “the player gave an order” one.
| 0 (Die) | orderState 0 is death by another unit; storm, an Infested Terran self-destructing, consume and being crushed are all 0/0. Burn-out, Scourge, a hallucination expiring, a Broodling dying naturally, Irradiate and Yamato are not. |
|---|---|
| 23 | “inside something” - a unit in a transport, an Interceptor in its Carrier, a carried powerup, a resting building and a Map Revealer are all 23. You have to look at the unit type too. |
| 44 · 48 · 45 | A Terran, Protoss or Zerg building under construction; orderState 0 to 6 is the progress. |
| 42 · 41 | Egg and cocoon morph; orderState 2 with orderSignal 4 is “morph complete”. |
| 85~90 | The mineral and gas harvest cycle - where the extra-resource trick lives. |
| 98 · 99 | Siege and Tank mode; orderState 3 with orderSignal 1 is “finished transforming”. |
| 116~118 | Burrow and unburrow; 117 with signal 109 is “burrowed and sitting”. |
| 166 | Critter random movement: 0 picks, 1 moves, 2 arrived. |
A computer-owned unit runs an entirely different set of orders: 156 is the AI's “decide what to do next” branch, 160 is “arrived”, 2 is its Stop, and an idle AI Arbiter loops 160→156→130. Classification code tested only on human-owned units misreads every one of them - which is exactly the case on a map whose mobs belong to the computer.
This is a survey of reading those values, not of what happens when you write them - a separate question. It was measured by hand, unit by unit, so confirm whatever you depend on. And the order queue itself (0x74, 0x78) is EUD-unsupported, so nobody has ever looked inside it.
Order 0 is death, but on its own it does not give you either half of the usual job: running something once when a unit dies, and knowing how it died.
Running it once first. A dead unit keeps reading as order 0 until its slot is reused, so a plain test fires every cycle. Keep one group per unit type and remove the unit on death and you need neither a flag nor a type comparison - at the price of keeping the group correct yourself. Nothing adds or removes a unit from a group automatically.
For self-destruct versus being killed there is one well-known wrong answer: “the ground weapon cooldown went up, so it exploded” detects Scourge and fails on an Infested Terran. What works is this - it is dead and its current HP is still at least 1. A unit killed by damage reaches 0; one that removed itself does not.
unit.move_cp(0x8 / 4);
if (DeathsX(CurrentPlayer, AtLeast, 256, 0, 0x7FFFFF00)) { // hp >= 1
// 스스로 터졌다 / it blew itself up
}If you are creating something in the dead unit's place, check whether 0x628438 is 0 first. Zero is the “cannot” case with the unit table full, and CreateUnit then quietly does nothing while your code believes it worked.
Three ways to read the struct wrongHolding on to many units