Add a painting entrance, Freedoom combat and Doom-style HUD. Persist the one-time clear reward in signed saves without changing file sizes. Include native regression checks and synchronized game/launcher builds.
509 lines
13 KiB
PHP
509 lines
13 KiB
PHP
.data
|
|
hfTestStage dd 1
|
|
hfTestIo dd 0
|
|
hfTestFrame db 'build\scratch\hell-entry.bgra',0
|
|
hfTestCourt db 'build\scratch\hell-court.bgra',0
|
|
hfTestHall db 'build\scratch\hell-portal.bgra',0
|
|
hfTestWin db 'build\scratch\hell-victory.bgra',0
|
|
hfTestPreview db 'build\scratch\hf-prev.dat',0
|
|
hfTestProfileDir db 'build\scratch',0
|
|
hfTestProfileName db 'hell-profile.dat',0
|
|
hfTestBoss dd 0
|
|
hfTestStrafe dd 5000
|
|
.code
|
|
HFTestAimBoss PROC uses esi edi ebx
|
|
local vx:DWORD,vy:DWORD,best:DWORD
|
|
mov esi,hfTestBoss
|
|
mov eax,[esi].HFEnemy.x
|
|
sub eax,playerX
|
|
sar eax,8
|
|
mov vx,eax
|
|
mov eax,[esi].HFEnemy.y
|
|
sub eax,playerY
|
|
sar eax,8
|
|
mov vy,eax
|
|
mov best,80000000h
|
|
xor edi,edi
|
|
hf_test_aim_angle:
|
|
mov eax,tableCos[edi*4]
|
|
sar eax,8
|
|
imul eax,vx
|
|
mov ebx,eax
|
|
mov eax,tableSin[edi*4]
|
|
sar eax,8
|
|
imul eax,vy
|
|
add eax,ebx
|
|
cmp eax,best
|
|
jle @F
|
|
mov best,eax
|
|
mov playerAngle,edi
|
|
@@:
|
|
inc edi
|
|
cmp edi,512
|
|
jb hf_test_aim_angle
|
|
ret
|
|
HFTestAimBoss ENDP
|
|
|
|
HFTestCapture PROC uses ebx path:DWORD
|
|
invoke CreateFileA,path,40000000h,0,0,2,80h,0
|
|
mov ebx,eax
|
|
invoke WriteFile,ebx,OFFSET screenBuffer,SCREEN_SIZE*4,OFFSET hfTestIo,0
|
|
invoke CloseHandle,ebx
|
|
ret
|
|
HFTestCapture ENDP
|
|
|
|
RunHellFPSTest PROC uses esi edi ebx
|
|
call InitHall3D
|
|
call LoadTextures3D
|
|
call LoadHallAssets3D
|
|
call InitializeCasinoSprites3D
|
|
mov hfTestStage,2
|
|
call HFLoad
|
|
test eax,eax
|
|
jz hf_test_fail
|
|
mov playerX,HF_PORTAL_X
|
|
mov playerY,29*FP_1
|
|
mov playerAngle,128
|
|
mov engineMode,MODE_WALK
|
|
call UpdateCamera3D
|
|
call CheckHallInteractions3D
|
|
; Approach must be within the standard interaction distance.
|
|
mov playerY,FP_1*294/10
|
|
call CheckHallInteractions3D
|
|
cmp interactTargetId,INTERACT_HELL_FPS
|
|
jne hf_test_fail
|
|
invoke CanWalk3D,HF_PORTAL_X,FP_1*294/10
|
|
cmp eax,1
|
|
jne hf_test_fail
|
|
; Old standing frame no longer blocks the west aisle.
|
|
invoke CanWalk3D,FP_1*25/10,11*FP_1
|
|
cmp eax,1
|
|
jne hf_test_fail
|
|
mov playerX,4*FP_1
|
|
mov playerY,12*FP_1
|
|
mov playerAngle,256
|
|
call UpdateCamera3D
|
|
mov interactTargetId,0
|
|
call CheckHallInteractions3D
|
|
cmp interactTargetId,INTERACT_HELL_FPS
|
|
je hf_test_fail
|
|
mov playerX,HF_PORTAL_X
|
|
mov playerY,FP_1*255/10
|
|
mov playerAngle,128
|
|
call UpdateCamera3D
|
|
call RenderCeilingAndFloor
|
|
call RenderWalls
|
|
call PrepareCityDepth3D
|
|
call RenderHall3D
|
|
invoke HFTestCapture,OFFSET hfTestHall
|
|
mov hfOldX,HF_PORTAL_X
|
|
mov hfOldY,FP_1*294/10
|
|
mov hfOldAngle,128
|
|
mov hfOldMap,OFFSET casinoMap
|
|
mov hfOldFloor,100
|
|
mov hfOldLevel,0
|
|
mov hfOldEye,HALL_STANDING_EYE
|
|
mov hfOldCeiling,FP_1*5/2
|
|
call HFReset
|
|
mov hfTestStage,3
|
|
call RenderHellFPS3D
|
|
invoke HFTestCapture,OFFSET hfTestFrame
|
|
invoke HFLineClear,3*FP_1+FP_HALF,26*FP_1+FP_HALF,7*FP_1+FP_HALF,26*FP_1+FP_HALF
|
|
cmp eax,1
|
|
jne hf_test_fail
|
|
invoke HFLineClear,3*FP_1+FP_HALF,26*FP_1+FP_HALF,13*FP_1,26*FP_1+FP_HALF
|
|
test eax,eax
|
|
jnz hf_test_fail
|
|
invoke CanWalk3D,0,0
|
|
test eax,eax
|
|
jnz hf_test_fail
|
|
mov hfTestStage,4
|
|
; Same hitscan must stop at wall, then damage the nearest target in open space.
|
|
mov hfEnemies.x,12*FP_1
|
|
mov hfEnemies.y,26*FP_1+FP_HALF
|
|
mov hfEnemies.hp,100
|
|
mov hfEnemies[SIZEOF HFEnemy].hp,0
|
|
call HFFire
|
|
cmp hfEnemies.hp,100
|
|
jne hf_test_fail
|
|
mov hfEnemies.x,7*FP_1
|
|
mov hfCooldown,0
|
|
call HFFire
|
|
cmp hfEnemies.hp,100
|
|
jae hf_test_fail
|
|
cmp hfBullets,78
|
|
jne hf_test_fail
|
|
mov hfTestStage,5
|
|
mov bankroll,12345
|
|
invoke HFPlayerCanWalk,hfEnemies.x,hfEnemies.y
|
|
test eax,eax
|
|
jnz hf_test_fail
|
|
mov hfArmor,20
|
|
invoke HFDamagePlayer,10
|
|
cmp hfHealth,95
|
|
jne hf_test_fail
|
|
cmp hfArmor,15
|
|
jne hf_test_fail
|
|
mov hfArmor,0
|
|
invoke HFDamagePlayer,1000
|
|
cmp hfState,1
|
|
jne hf_test_fail
|
|
call HFReset
|
|
cmp hfState,0
|
|
jne hf_test_fail
|
|
cmp hfHealth,100
|
|
jne hf_test_fail
|
|
cmp bankroll,12345
|
|
jne hf_test_fail
|
|
; Natural shotgun pickup and consumption; no injected weapon ownership.
|
|
mov playerX,3*FP_1+FP_HALF
|
|
mov playerY,27*FP_1+FP_HALF
|
|
call HFPickups
|
|
cmp hfHasShotgun,1
|
|
jne hf_test_fail
|
|
cmp hfShells,16
|
|
jne hf_test_fail
|
|
call HFFire
|
|
cmp hfShells,15
|
|
jne hf_test_fail
|
|
mov hfTestStage,12
|
|
; Three separated targets: pistol hits centre only; one shell hits all three.
|
|
call HFReset
|
|
mov esi,OFFSET hfEnemies
|
|
mov ecx,HF_ENEMIES
|
|
@@:
|
|
mov [esi].HFEnemy.hp,0
|
|
add esi,SIZEOF HFEnemy
|
|
loop @B
|
|
mov hfEnemies.x,8*FP_1
|
|
mov hfEnemies.y,FP_1*103/4
|
|
mov hfEnemies.hp,1000
|
|
mov hfEnemies[SIZEOF HFEnemy].x,8*FP_1
|
|
mov hfEnemies[SIZEOF HFEnemy].y,FP_1*106/4
|
|
mov hfEnemies[SIZEOF HFEnemy].hp,1000
|
|
mov hfEnemies[2*SIZEOF HFEnemy].x,8*FP_1
|
|
mov hfEnemies[2*SIZEOF HFEnemy].y,FP_1*109/4
|
|
mov hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
|
call HFFire
|
|
cmp hfEnemies.hp,1000
|
|
jne hf_test_fail
|
|
cmp hfEnemies[SIZEOF HFEnemy].hp,1000
|
|
jae hf_test_fail
|
|
cmp hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
|
jne hf_test_fail
|
|
mov hfCooldown,0
|
|
mov hfWeapon,1
|
|
mov hfShells,2
|
|
call HFFire
|
|
cmp hfShells,1
|
|
jne hf_test_fail
|
|
cmp hfEnemies.hp,1000
|
|
jae hf_test_fail
|
|
cmp hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
|
jae hf_test_fail
|
|
mov esi,OFFSET hfImpacts
|
|
mov ecx,HF_SHOT_PELLETS
|
|
@@:
|
|
cmp [esi].HFImpact.life,8
|
|
jne hf_test_fail
|
|
add esi,SIZEOF HFImpact
|
|
loop @B
|
|
; The whole pellet cone must stop at the separating wall.
|
|
mov hfEnemies.x,12*FP_1
|
|
mov hfEnemies.hp,1000
|
|
mov hfEnemies[SIZEOF HFEnemy].x,12*FP_1
|
|
mov hfEnemies[SIZEOF HFEnemy].hp,1000
|
|
mov hfEnemies[2*SIZEOF HFEnemy].x,12*FP_1
|
|
mov hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
|
mov hfCooldown,0
|
|
call HFFire
|
|
cmp hfEnemies.hp,1000
|
|
jne hf_test_fail
|
|
cmp hfEnemies[SIZEOF HFEnemy].hp,1000
|
|
jne hf_test_fail
|
|
cmp hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
|
jne hf_test_fail
|
|
cmp hfShells,0
|
|
jne hf_test_fail
|
|
mov hfCooldown,0
|
|
call HFFire
|
|
cmp hfCooldown,0
|
|
jne hf_test_fail
|
|
call HFReset
|
|
mov hfTestStage,6
|
|
mov playerX,21*FP_1+FP_HALF
|
|
mov playerY,6*FP_1+FP_HALF
|
|
call HFUse
|
|
cmp hfMap[6*32+22],188
|
|
jne hf_test_fail
|
|
mov playerX,20*FP_1+FP_HALF
|
|
mov playerY,3*FP_1+FP_HALF
|
|
call HFPickups
|
|
cmp hfKey,1
|
|
jne hf_test_fail
|
|
mov playerX,21*FP_1+FP_HALF
|
|
mov playerY,6*FP_1+FP_HALF
|
|
call HFUse
|
|
cmp hfMap[6*32+22],0
|
|
jne hf_test_fail
|
|
mov playerX,5*FP_1+FP_HALF
|
|
mov playerY,13*FP_1+FP_HALF
|
|
call HFUse
|
|
call HFUse
|
|
cmp hfSecrets,1
|
|
jne hf_test_fail
|
|
mov playerX,23*FP_1
|
|
mov playerY,27*FP_1
|
|
call HFUse
|
|
cmp hfSecrets,2
|
|
jne hf_test_fail
|
|
mov hfTestStage,7
|
|
; Real AI/projectile/pickup loop stress; no alternate Python simulation.
|
|
call HFReset
|
|
mov playerX,14*FP_1
|
|
mov playerY,21*FP_1
|
|
mov playerAngle,430
|
|
mov hfWeapon,1
|
|
mov hfHasShotgun,1
|
|
mov hfShells,80
|
|
mov hfFireHeld,1
|
|
mov ebx,1050
|
|
hf_test_combat:
|
|
mov hfHealth,100
|
|
mov hfState,0
|
|
call HFTick
|
|
dec ebx
|
|
jnz hf_test_combat
|
|
mov hfFireHeld,0
|
|
call RenderHellFPS3D
|
|
invoke HFTestCapture,OFFSET hfTestCourt
|
|
mov hfTestStage,11
|
|
; Isolated boss encounter: real pickups, finite ammo, normal damage and ticks.
|
|
call HFReset
|
|
mov esi,OFFSET hfEnemies
|
|
mov ecx,HF_ENEMIES
|
|
hf_test_isolate_boss:
|
|
cmp [esi].HFEnemy.kind,3
|
|
jne hf_test_disable_enemy
|
|
mov hfTestBoss,esi
|
|
jmp @F
|
|
hf_test_disable_enemy:
|
|
mov [esi].HFEnemy.hp,0
|
|
@@:
|
|
add esi,SIZEOF HFEnemy
|
|
loop hf_test_isolate_boss
|
|
mov playerX,3*FP_1+FP_HALF
|
|
mov playerY,27*FP_1+FP_HALF
|
|
call HFPickups
|
|
mov playerX,4*FP_1+FP_HALF
|
|
mov playerY,28*FP_1+FP_HALF
|
|
call HFPickups
|
|
mov playerX,8*FP_1+FP_HALF
|
|
mov playerY,14*FP_1+FP_HALF
|
|
call HFPickups
|
|
mov playerX,27*FP_1+FP_HALF
|
|
mov playerY,24*FP_1+FP_HALF
|
|
mov playerAngle,128
|
|
mov hfFireHeld,1
|
|
mov ebx,1400
|
|
hf_test_boss_fight:
|
|
cmp playerX,28*FP_1+FP_HALF
|
|
jl @F
|
|
mov hfTestStrafe,-5000
|
|
@@:
|
|
cmp playerX,26*FP_1+FP_HALF
|
|
jg @F
|
|
mov hfTestStrafe,5000
|
|
@@:
|
|
mov eax,playerX
|
|
add eax,hfTestStrafe
|
|
push eax
|
|
invoke HFPlayerCanWalk,eax,playerY
|
|
pop ecx
|
|
test eax,eax
|
|
jz @F
|
|
mov playerX,ecx
|
|
@@:
|
|
call HFTestAimBoss
|
|
call HFTick
|
|
cmp hfState,0
|
|
jne hf_test_fail
|
|
cmp hfKills,1
|
|
je hf_test_boss_killed
|
|
dec ebx
|
|
jnz hf_test_boss_fight
|
|
jmp hf_test_fail
|
|
hf_test_boss_killed:
|
|
mov hfFireHeld,0
|
|
mov hfTestStage,8
|
|
; Locked exit, then one-time reward, then repeat use cannot pay twice.
|
|
call HFReset
|
|
mov bankroll,10000
|
|
mov hfCompleted,0
|
|
mov playerX,28*FP_1
|
|
mov playerY,28*FP_1
|
|
call HFUse
|
|
cmp hfState,0
|
|
jne hf_test_fail
|
|
mov esi,OFFSET hfEnemies
|
|
mov ecx,HF_ENEMIES
|
|
@@:
|
|
mov [esi].HFEnemy.hp,0
|
|
add esi,SIZEOF HFEnemy
|
|
loop @B
|
|
call HFUse
|
|
cmp hfState,2
|
|
jne hf_test_fail
|
|
cmp bankroll,16660
|
|
jne hf_test_fail
|
|
call HFUse
|
|
cmp bankroll,16660
|
|
jne hf_test_fail
|
|
mov playerX,26*FP_1
|
|
mov playerY,24*FP_1
|
|
mov playerAngle,96
|
|
mov hfTicks,18000
|
|
mov hfKills,28
|
|
mov hfSecrets,2
|
|
call RenderHellFPS3D
|
|
invoke HFTestCapture,OFFSET hfTestWin
|
|
mov hfTestStage,9
|
|
; Save extension survives native signed profile round trip.
|
|
mov nativeProfilesDirPtr3D,OFFSET hfTestProfileDir
|
|
mov nativeProfileActive3D,1
|
|
invoke CopyCString,OFFSET saveFileName,OFFSET hfTestProfileName
|
|
call BuildNativeProfileDir3D
|
|
call SaveGame
|
|
mov hfCompleted,0
|
|
call LoadSave
|
|
cmp hfCompleted,1
|
|
jne hf_test_fail
|
|
cmp bankroll,16660
|
|
jne hf_test_fail
|
|
mov hfTestStage,10
|
|
; Preview save uses the same new bit without changing its 40-byte format.
|
|
mov nativeProfileActive3D,0
|
|
mov arrivalConfirmed3D,1
|
|
invoke CopyCString,OFFSET savePath3D,OFFSET hfTestPreview
|
|
call SaveProgress3D
|
|
mov hfCompleted,0
|
|
call LoadProgress3D
|
|
cmp hfCompleted,1
|
|
jne hf_test_fail
|
|
cmp bankroll,16660
|
|
jne hf_test_fail
|
|
mov arrivalConfirmed3D,0
|
|
call LeaveHellFPS3D
|
|
cmp engineMode,MODE_WALK
|
|
jne hf_test_fail
|
|
cmp playerX,HF_PORTAL_X
|
|
jne hf_test_fail
|
|
cmp hallLevel3D,0
|
|
jne hf_test_fail
|
|
call FreeHellFPS3D
|
|
xor eax,eax
|
|
ret
|
|
hf_test_fail:
|
|
mov eax,hfTestStage
|
|
ret
|
|
RunHellFPSTest ENDP
|
|
|
|
IFDEF HELL_FPS_WINDOW_TEST
|
|
.data
|
|
hfWindowShot db 'build\scratch\hell-window.bgra',0
|
|
hfMusicStatus db 'status hellmusic mode',0
|
|
hfAudioMode db 32 dup(0)
|
|
.code
|
|
RunHellFPSWindowTest PROC uses esi ebx
|
|
call InitPaths3D
|
|
call Create3DWindow
|
|
test eax,eax
|
|
jz hf_window_failed
|
|
; Keep test window modest and avoid taking keyboard/mouse focus.
|
|
cmp fullscreen3D,0
|
|
je @F
|
|
call ToggleFullscreen3D
|
|
@@:
|
|
invoke SetWindowPos,h3DWnd,0,80,80,800,640,14h
|
|
call InitHall3D
|
|
call LoadTextures3D
|
|
call LoadHallAssets3D
|
|
call InitializeCasinoSprites3D
|
|
mov playerX,HF_PORTAL_X
|
|
mov playerY,FP_1*294/10
|
|
mov playerAngle,128
|
|
mov engineMode,MODE_WALK
|
|
call BeginHellFPS3D
|
|
cmp engineMode,MODE_HELL_FPS
|
|
jne hf_window_failed
|
|
cmp hfMusicError,0
|
|
jne hf_window_failed
|
|
cmp hfSoundFailures,0
|
|
jne hf_window_failed
|
|
cmp hfVolumeError,0
|
|
jne hf_window_failed
|
|
mov settingsVolume,10
|
|
call HFUpdateMusicVolume
|
|
cmp hfVolumeError,0
|
|
jne hf_window_failed
|
|
mov eax,SIZEOF hfVolumePrefix-1
|
|
cmp DWORD PTR hfVolumeCommand[eax],00303533h ; "350", not full volume 1000
|
|
jne hf_window_failed
|
|
mov settingsVolume,0
|
|
call HFUpdateMusicVolume
|
|
cmp hfVolumeError,0
|
|
jne hf_window_failed
|
|
mov eax,SIZEOF hfVolumePrefix-1
|
|
cmp WORD PTR hfVolumeCommand[eax],0030h
|
|
jne hf_window_failed
|
|
mov settingsVolume,5
|
|
call HFUpdateMusicVolume
|
|
cmp hfVolumeError,0
|
|
jne hf_window_failed
|
|
invoke ShowWindow,h3DWnd,4
|
|
mov esi,120
|
|
hf_window_frame:
|
|
invoke PeekMessageA,OFFSET msg3D,0,0,0,PM_REMOVE
|
|
test eax,eax
|
|
jz hf_window_simulate
|
|
invoke TranslateMessage,OFFSET msg3D
|
|
invoke DispatchMessageA,OFFSET msg3D
|
|
jmp hf_window_frame
|
|
hf_window_simulate:
|
|
mov deltaTime3D,17
|
|
add animationClock3D,17
|
|
cmp esi,110
|
|
jne @F
|
|
invoke WindowEvents3D,h3DWnd,201h,1,0
|
|
@@:
|
|
cmp esi,35
|
|
jne @F
|
|
invoke WindowEvents3D,h3DWnd,202h,0,0
|
|
@@:
|
|
call HandleHellFPS3D
|
|
call RenderHellFPS3D
|
|
call Present3D
|
|
invoke Sleep,17
|
|
dec esi
|
|
jnz hf_window_frame
|
|
invoke HFTestCapture,OFFSET hfWindowShot
|
|
cmp hfBullets,80
|
|
jae hf_window_failed
|
|
cmp hfFireHeld,0
|
|
jne hf_window_failed
|
|
invoke mciSendStringA,OFFSET hfMusicStatus,OFFSET hfAudioMode,32,0
|
|
test eax,eax
|
|
jnz hf_window_failed
|
|
cmp DWORD PTR hfAudioMode,'yalp'
|
|
jne hf_window_failed
|
|
call LeaveHellFPS3D
|
|
call FreeHellFPS3D
|
|
invoke DestroyWindow,h3DWnd
|
|
xor eax,eax
|
|
ret
|
|
hf_window_failed:
|
|
call FreeHellFPS3D
|
|
mov eax,111
|
|
ret
|
|
RunHellFPSWindowTest ENDP
|
|
ENDIF
|