; Fixed-tick player simulation. Wire coordinates remain raw world units. .code StartCutscene PROTO StopCutscene PROTO UpdateCutscene PROTO IsGameplayBlocked PROC mov eax, gameplayTickBlocked or eax, isPlayerDead or eax, isCutsceneActive or eax, cutscenePending or eax, fadeState or eax, isGamePaused or eax, gameDialogActive ret IsGameplayBlocked ENDP ResetMovementFractions PROC mov playerFractionX, 0 mov playerFractionY, 0 ret ResetMovementFractions ENDP ; Test every overlapped cell, not only the leading edge's center. Portal cells ; block rays but deliberately remain walkable for players/camera handoff. CanOccupyPlayer PROC uses ebx ecx edx esi edi, x:DWORD, y:DWORD LOCAL minX:DWORD, maxX:DWORD, maxY:DWORD, row:DWORD mov eax, x sub eax, playerRadius sar eax, CELL_SHIFT mov minX, eax mov eax, x add eax, playerRadius sar eax, CELL_SHIFT mov maxX, eax mov eax, y sub eax, playerRadius sar eax, CELL_SHIFT mov row, eax mov eax, y add eax, playerRadius sar eax, CELL_SHIFT mov maxY, eax body_row: mov ebx, minX body_cell: invoke GetMapCell, ebx, row cmp eax, CELL_PORTAL je body_next invoke IsSolidCell, eax test eax, eax jnz body_blocked body_next: inc ebx cmp ebx, maxX jle body_cell inc row mov eax, row cmp eax, maxY jle body_row mov eax, 1 ret body_blocked: xor eax, eax ret CanOccupyPlayer ENDP UpdatePlayer PROC uses ebx ecx edx esi edi LOCAL forward:SDWORD, strafe:SDWORD, speed:DWORD LOCAL moveX:SDWORD, moveY:SDWORD, steps:DWORD mov gameplayTickBlocked, 1 cmp isGamePaused, 0 jne player_done cmp gameDialogActive, 0 jne player_done cmp isPlayerDead, 0 jne player_done cmp fadeState, 0 jne player_done cmp cutscenePending, 0 je @F call StartCutscene @@: cmp keyCutscene, 0 je @F cmp isCutsceneActive, 0 jne @F call StartCutscene @@: cmp isCutsceneActive, 0 je player_active cmp keySkipScene, 0 je player_scene call StopCutscene ret player_scene: call UpdateCutscene ret player_active: mov gameplayTickBlocked, 0 movzx eax, keyTurnRight movzx ebx, keyTurnLeft sub eax, ebx imul eax, playerTurnSpeed add eax, playerAngle and eax, ANGLE_MASK mov playerAngle, eax cmp keyAction, 0 je @F call InteractDoor call IsGameplayBlocked test eax, eax jnz player_done @@: movzx eax, keyForward movzx ebx, keyBackward sub eax, ebx mov forward, eax movzx eax, keyStrafeRight movzx ebx, keyStrafeLeft sub eax, ebx mov strafe, eax or eax, forward jz player_done mov eax, playerMoveSpeed cmp keyRun, 0 je @F mov eax, playerRunSpeed @@: shl eax, 16 cmp forward, 0 je speed_ready cmp strafe, 0 je speed_ready ; 1/sqrt(2), 16.16. Product may exceed 32 bits. mov ebx, 46341 imul ebx shrd eax, edx, 16 speed_ready: mov speed, eax mov ecx, playerAngle mov eax, [tableCos+ecx*4] imul eax, forward mov ebx, [tableSin+ecx*4] imul ebx, strafe sub eax, ebx imul speed mov ebx, FIXED_ONE idiv ebx add eax, playerFractionX cdq mov ebx, FIXED_ONE idiv ebx mov moveX, eax mov playerFractionX, edx mov eax, [tableSin+ecx*4] imul eax, forward mov ebx, [tableCos+ecx*4] imul ebx, strafe add eax, ebx imul speed mov ebx, FIXED_ONE idiv ebx add eax, playerFractionY cdq mov ebx, FIXED_ONE idiv ebx mov moveY, eax mov playerFractionY, edx ; Unit substeps prevent tunneling even when scripts increase speed. mov steps, 0 player_step: mov eax, moveX or eax, moveY jz player_done inc steps cmp steps, 64 ja player_done cmp moveX, 0 je step_y mov esi, 1 jg @F mov esi, -1 @@: mov ebx, playerX add ebx, esi invoke CanOccupyPlayer, ebx, playerY test eax, eax jz blocked_x mov playerX, ebx sub moveX, esi jmp step_y blocked_x: mov moveX, 0 mov playerFractionX, 0 step_y: cmp moveY, 0 je player_step mov esi, 1 jg @F mov esi, -1 @@: mov ebx, playerY add ebx, esi invoke CanOccupyPlayer, playerX, ebx test eax, eax jz blocked_y mov playerY, ebx sub moveY, esi jmp player_step blocked_y: mov moveY, 0 mov playerFractionY, 0 jmp player_step player_done: call IsGameplayBlocked test eax, eax jnz @F call CheckPortalTriggers @@: ret UpdatePlayer ENDP