EBLAN_ENGINE/tests/stability_engine_checks.inc
Игорь Федосеев f42679fe62 fix: stabilize gameplay and IDE source writes
Trace shots against current map geometry instead of stale render depth. Preserve outline counters across volatile pixel calls and freeze gameplay on cinematic, transition and death ticks.

Clamp packed HP before narrowing and match legacy archetype rules. Publish IDE saves atomically; failed saves block file switches and builds. Drain compiler output while hidden.

Verify runtime/editor builds, 112 editor checks, 148 ASM checks and live preview parity. Record remaining stages in docs/STABILIZATION.md.
2026-10-31 21:56:54 +03:00

202 lines
7.6 KiB
PHP

; Real gameplay procedures, deliberately without Render3DScene between shots.
.code
ResetCombatFixture PROC uses eax ecx edi
mov edi, OFFSET gameMap
mov ecx, MAP_SIZE
xor eax, eax
rep stosb
mov fadeState, 0
mov isCutsceneActive, 0
mov cutscenePending, 0
mov isPlayerDead, 0
mov playerHealth, 100
mov playerAmmo, 50
mov playerScore, 0
mov playerAttackCooldown, 0
mov playerFlashTimer, 0
mov playerHitFlash, 0
mov playerX, 160
mov playerY, 160
mov playerAngle, 0
mov keyForward, 0
mov keyBackward, 0
mov keyStrafeLeft, 0
mov keyStrafeRight, 0
mov keyTurnLeft, 0
mov keyTurnRight, 0
mov keyCutscene, 0
mov keySkipScene, 0
mov keyAction, 0
mov keyRestart, 0
mov keyAttack, 1
mov transitionCount, 0
mov spriteCount, 1
mov [spriteList].SpriteStruct.x, 288
mov [spriteList].SpriteStruct.y, 160
mov [spriteList].SpriteStruct.textureId, 13
mov [spriteList].SpriteStruct.scale, FIXED_ONE
mov [spriteList].SpriteStruct.flags, 0
call InitEntities
call UpdatePlayer
ret
ResetCombatFixture ENDP
RunStabilityChecks PROC
mov edi, OFFSET screenBuffer
mov ecx, FRAME_BUFFER_SIZE/4
mov eax, 00123456h
rep stosd
invoke DrawRectOutline, 68, 42, 60, 8, COLOR_WHITE
CHECK DWORD PTR [screenBuffer+42*SCREEN_PITCH+127*4], COLOR_WHITE ; white outline reaches top right
CHECK DWORD PTR [screenBuffer+49*SCREEN_PITCH+100*4], COLOR_WHITE ; white outline draws bottom edge
CHECK DWORD PTR [screenBuffer+45*SCREEN_PITCH+68*4], COLOR_WHITE ; white outline draws left edge
CHECK DWORD PTR [screenBuffer+45*SCREEN_PITCH+100*4], 00123456h ; outline keeps interior unchanged
invoke DrawRectOutline, 68, 42, 60, 8, 0
CHECK DWORD PTR [screenBuffer+49*SCREEN_PITCH+100*4], 0 ; black outline terminates and draws bottom
invoke DrawRectOutline, -2, -2, 5, 5, COLOR_GREEN
CHECK DWORD PTR [screenBuffer+2*SCREEN_PITCH], COLOR_GREEN ; outline clips offscreen coordinates
invoke DrawRectOutline, 100, 45, 0, 3, COLOR_WHITE
CHECK DWORD PTR [screenBuffer+45*SCREEN_PITCH+100*4], 00123456h ; zero width outline draws nothing
call ResetCombatFixture
mov DWORD PTR [depthBuffer+160*4], 0
call UpdateCombat
CHECK entityHealth, 65 ; hitscan works without a rendered depth buffer
CHECK playerAmmo, 49 ; one shot consumes one round
mov playerAttackCooldown, 0
mov byte ptr [gameMap+2*MAP_W+3], CELL_BRICK
mov DWORD PTR [depthBuffer+160*4], 999999
call UpdateCombat
CHECK entityHealth, 65 ; newly placed wall blocks a shot despite stale depth
mov playerAttackCooldown, 0
mov byte ptr [gameMap+2*MAP_W+3], CELL_DOOR_OPEN
call UpdateCombat
CHECK entityHealth, 30 ; newly opened door permits a shot without rendering
mov playerAttackCooldown, 0
mov byte ptr [gameMap+2*MAP_W+3], CELL_GLASS
call UpdateCombat
CHECK entityHealth, 30 ; solid glass blocks gameplay shots
call ResetCombatFixture
mov [spriteList].SpriteStruct.x, 800
mov [spriteList].SpriteStruct.y, 180
mov DWORD PTR [depthBuffer+160*4], 999999
call UpdateCombat
CHECK entityHealth, 100 ; distant target does not acquire a larger world hitbox
call ResetCombatFixture
mov [spriteList].SpriteStruct.x, 160
mov [spriteList].SpriteStruct.y, 288
mov playerAngle, ANGLE_90
mov DWORD PTR [depthBuffer+160*4], 0
call UpdateCombat
CHECK entityHealth, 65 ; cardinal turn uses current camera instead of previous frame
call ResetCombatFixture
mov playerAngle, ANGLE_180
mov [spriteList].SpriteStruct.x, 32
call UpdateCombat
CHECK entityHealth, 65 ; negative cardinal shot traverses the current map
call ResetCombatFixture
mov playerAngle, ANGLE_45
mov [spriteList].SpriteStruct.y, 288
call UpdateCombat
CHECK entityHealth, 65 ; diagonal shot reaches unobstructed target
mov playerAttackCooldown, 0
mov byte ptr [gameMap+2*MAP_W+3], CELL_BRICK
call UpdateCombat
CHECK entityHealth, 65 ; diagonal ray cannot pass through a wall corner
call ResetCombatFixture
mov spriteCount, 2
mov esi, OFFSET spriteList+sizeof SpriteStruct
mov [esi].SpriteStruct.x, 224
mov [esi].SpriteStruct.y, 160
mov [esi].SpriteStruct.textureId, 13
mov [esi].SpriteStruct.scale, FIXED_ONE
mov [esi].SpriteStruct.flags, 0
call InitEntities
call UpdateCombat
CHECK entityHealth, 100 ; hitscan does not damage farther overlapping guard
CHECK entityHealth[4], 65 ; hitscan selects nearest live guard independent of insertion order
call ResetCombatFixture
mov [spriteList].SpriteStruct.y, 180
mov [spriteList].SpriteStruct.scale, 2*FIXED_ONE
call UpdateCombat
CHECK entityHealth, 65 ; authored sprite scale affects world-space hitbox
mov playerAttackCooldown, 0
mov [spriteList].SpriteStruct.scale, 0
call UpdateCombat
CHECK entityHealth, 65 ; zero sprite scale retains legacy one-unit scale
call ResetCombatFixture
mov isCutsceneActive, 1
mov playerAttackCooldown, 4
call UpdateEntities
call UpdateCombat
CHECK [spriteList].SpriteStruct.x, 288 ; cinematic camera cannot attract guards
CHECK playerAmmo, 50 ; cinematic camera cannot fire
CHECK playerAttackCooldown, 4 ; cinematic pauses weapon timers
mov isCutsceneActive, 0
mov fadeState, 1
call UpdateEntities
call UpdateCombat
CHECK [spriteList].SpriteStruct.x, 288 ; transition freezes guards
CHECK playerAmmo, 50 ; transition blocks firing
mov fadeState, 0
mov cutscenePending, 1
call UpdateEntities
call UpdateCombat
CHECK [spriteList].SpriteStruct.x, 288 ; pending autoplay blocks gameplay before first scene tick
CHECK playerAmmo, 50 ; pending autoplay blocks firing
call ResetCombatFixture
mov isCutsceneActive, 1
mov keySkipScene, 1
call UpdatePlayer
call UpdateEntities
call UpdateCombat
CHECK isCutsceneActive, 0 ; skip stops cinematic playback
CHECK [spriteList].SpriteStruct.x, 288 ; skip tick does not resume guards midway through tick
CHECK playerAmmo, 50 ; skip tick cannot fire stale scene input
call ResetCombatFixture
invoke SwitchSublevel, OFFSET arenaPath, 2, 2, 0, 0
call UpdateCombat
CHECK playerAmmo, 50 ; instant transition cannot fire into newly loaded room
call ResetCombatFixture
mov [spriteList].SpriteStruct.x, 160
mov [spriteList].SpriteStruct.textureId, 14
call InitEntities
mov playerHealth, 50
mov isCutsceneActive, 1
call UpdateEntities
CHECK playerHealth, 50 ; cinematic camera cannot collect a medkit
CHECK [spriteList].SpriteStruct.textureId, 14 ; cinematic leaves pickup active
mov isCutsceneActive, 0
call UpdateEntities
CHECK playerHealth, 75 ; normal gameplay collects medkit
call ResetCombatFixture
mov isPlayerDead, 1
mov keyForward, 1
mov keyTurnRight, 1
call UpdatePlayer
call UpdateCombat
CHECK playerX, 160 ; death blocks movement
CHECK playerAngle, 0 ; death blocks turning
CHECK playerAmmo, 50 ; death blocks firing
mov isPlayerDead, 0
mov keyForward, 0
mov keyTurnRight, 0
call UpdatePlayer
mov [spriteList].SpriteStruct.flags, 0602FF01h
call InitEntities
CHECK entityHealth, 255 ; packed guard health preserves maximum byte
mov [spriteList].SpriteStruct.flags, 06026400h
call InitEntities
CHECK entityType, ENTITY_TYPE_PROP ; nonzero packed Prop never falls back to texture
mov [spriteList].SpriteStruct.flags, 0
call InitEntities
CHECK entityType, ENTITY_TYPE_GUARD ; zero flags keep legacy texture fallback
ret
RunStabilityChecks ENDP