Advertisement
Guest User

Explaining AoD's trigger bug

a guest
Jan 10th, 2019
523
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.48 KB | None | 0 0
  1. I examined the P3 executable's code a bit to find out more about trigger bugs. I'd like to share what we've discovered here. Let me start by explaining how the game works normally.
  2.  
  3. The animation system (which is most likely skeletal animation) is implemented in the function called AO_Animate (AO probably means Animate(d) Object). In this, there is a call to the function to AO_AnimateHierachy (sic) which has a call to the function mathQuatSlerp at 0x49F234. If you disable it, every character will move in a stiff position without doing any animations.
  4. AO_Animate also handles updating the player character's hitbox, through the function AO_UpdateBounds. This hitbox is used up in both trigger and hit detection—for example, you wouldn't be able to crouch under the lasers in Louvre without this function because the hitbox would not be shrinked appropriately. AO_UpdateBounds is called once per frame during side flips and swan dives (this is different for other animations: during standing up from crouching this function is called two times in the duration of a frame).
  5. After this is done, the game calls mapBroadcastNodeMsg at 0x503F87 in mapProcess. Some of these nodes mapBroadcastNodeMsg loops through contain the triggers on the map. If the node contains a trigger, the following functions are called: mapSendNodeMsg, mapTrigNodeReceiveMsg (called in mapSendNodeMsg at 0x50D0C7), mapUpdateTrigger, mapIsCharacterInTrigger (called at 0x501C1B), mapBoxBoxCollide. The last function, mapBoxBoxCollide, is responsible for determining if the player character's hitbox is in the node's trigger box, if it is, it returns 1 in EAX, else it returns zero. The game goes through every trigger on the map every frame to check if the player character is in it or not.
  6. Before mapIsCharacterInTrigger is called, the game calls the self-explanatory playIsPlayerDead. If the player is dead, the game skips trigger checks.
  7.  
  8. Now let's discuss where trigger bugs come from. Things go wrong in mathQuatSlerp, see the following code:
  9.  
  10. TRAOD_P3.exe+8B8BF - fld dword ptr [TRAOD_P3.exe+53EC2C] // loads 1 to st(0)
  11. TRAOD_P3.exe+8B8C5 - fld st(0) // loads 1 to st(0)
  12. TRAOD_P3.exe+8B8C7 - fsub st(0),st(2) // st(0)=1-cos(x), x=the angle that the two quaternion encloses
  13. TRAOD_P3.exe+8B8C9 - fcomp dword ptr [TRAOD_P3.exe+53EC28] // [TRAOD_P3.exe+53EC28] = 0.01
  14. // checks if st(0) is smaller or bigger than 0.01, pops st(0), sets FPU flags accordingly:
  15. // - if 1-cos(x) > 0.01, then c3 = 0 and c0 = 0
  16. // - if 1-cos(x) = 0.01, then c3 = 1 and c0 = 0
  17. // - if 1-cos(x) < 0.01 then c3 = 0 and c0 = 1
  18. TRAOD_P3.exe+8B8CF - fnstsw ax // FPU flags get written out to ax
  19. TRAOD_P3.exe+8B8D1 - sahf // ah gets written into eflags
  20. TRAOD_P3.exe+8B8D2 - jae TRAOD_P3.exe+8B8EB // jumps if cf = c0 = 0, if 0.99 >= cos(x) (or 1-cos(x) > 0.01)
  21.  
  22. If the jump happens, the game slerps, if it doesn't, the game lerps. However, when trigger bugs happen, 1-cos(x) < 0.01 is true, but for some reason the game slerps anyway. No idea why. The game divides by zero with the instruction divss xmm0,[esp+14] at 0x48B939 and at 0x48B967 during the slerp. After this, AO_UpdateBounds updates the player character's hitbox to the incorrect value (the z coordinate turns into a nan). While the hitbox is broken, mapBoxBoxCollide keeps returning 1 for every trigger node, and since the game updates the hitbox on the next frame only, the game code determines that the player character is in every trigger on the map.
  23.  
  24. A possible fix for the bug is to change [TRAOD_P3.exe+53EC28] (v49) to 0.9 (this can be done with a simple memory editor), so the game will only slerp to large angles. This also fixes legbreaks.
  25.  
  26. In the end, I'd like to refute my previous theory about trigger bugs, which is as follows:
  27. "The trigger bug comes from the broken coordinate calculation in the P3 executable, which causes Lara's meshes to stretch across triggers on the map. You can see the stretching when Lara disappears for a frame or two."
  28. If this was true, mapBoxBoxCollide would return 1 for every trigger on the map while the player character is disappeared. I tried the following on The Archaeological Dig with the trigger bug we do in any% runs: during Lara's invisibility, I inserted a breakpoint to 0x502557 (just after mapBoxBoxCollide returned), and I kept the breakpoint active until the game updated the frame. mapBoxBoxCollide returned 1 for only one trigger: that's the eol trigger that puts you to Galleries Under Siege.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement