Air and ground is not one flag. Four separate places decide it and each governs something different. Set all four and you get a real ground unit; set some of them and you get things the game never shows you.
| 0x97 movementState | Air 7, ground 10. The movement state machine itself. Write 10 once and it runs the ground cycle from then on: 10 at rest, 25 and 24 while moving, 9 on stopping and back to 10. |
|---|---|
| 0x105 pathingFlags | Air 0, ground 1. Pathfinding. On its own it does not block, it makes things hard - a unit can get into water and fail to get out. |
| 0xDC의 0x4 (In Air) | Decides only whether an attacker uses its air weapon or its ground weapon. It has nothing to do with movement. |
| 0xDC의 0x100000 (Is Normal) | Air 0, ground 1. Collision with units and with terrain. |
// 진짜 지상 유닛으로 / a real ground unit SetMemoryXEPD(epd + 0x97/4, SetTo, 0xA000000, 0xFF000000); SetMemoryXEPD(epd + 0x105/4, SetTo, 0x100, 0xFF00); SetMemoryXEPD(epd + 0xDC/4, SetTo, 0x100000, 0x100004);
The three movement fields have to be written together. Change one and you get a unit that passes some tiles and grinds to a halt on others - a state you cannot patch up.
The useful part is that 0x4 is genuinely independent of the other three, which makes both half-measures work properly.
- Drop 0x4 from the mask and you get something that collides and is stopped by cliffs, but can still only be hit by air weapons.
- Write only 0x4 and it flies exactly as before, and Zerglings can attack it.
The "gets stuck in water" behaviour from setting 0x105 alone is a material, not a bug: an aquarium, a sea, an area a particular unit cannot leave. Note that clicking somewhere with no vision makes it fly across normally - pathfinding cannot block terrain it does not know about.
A later observation from the same author: 0x97 and 0x100000 alone already give ordinary ground collision against both terrain and units. And the whole thing in reverse turns a ground unit into a proper flyer.
Three ways to read the struct wrongMoving a unit yourself