.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 hfTestWeaponBase0 db 'build\scratch\hell-weapon-pistol-base.bgra',0 hfTestWeaponBase1 db 'build\scratch\hell-weapon-shotgun-base.bgra',0 hfTestWeapon0 db 'build\scratch\hell-weapon-0.bgra',0 hfTestWeapon1 db 'build\scratch\hell-weapon-1.bgra',0 hfTestWeapon2 db 'build\scratch\hell-weapon-2.bgra',0 hfTestWeapon3 db 'build\scratch\hell-weapon-3.bgra',0 hfTestWeapon4 db 'build\scratch\hell-weapon-4.bgra',0 hfTestWeapon5 db 'build\scratch\hell-weapon-5.bgra',0 hfTestWeapon6 db 'build\scratch\hell-weapon-6.bgra',0 hfTestWeapon7 db 'build\scratch\hell-weapon-7.bgra',0 hfTestWeapon8 db 'build\scratch\hell-weapon-8.bgra',0 ; Weapon, cooldown (-1 hides both packs for a clean reference), capture path. hfTestWeaponCases dd 0,-1,OFFSET hfTestWeaponBase0 dd 0,0,OFFSET hfTestWeapon0 dd 0,9,OFFSET hfTestWeapon1 dd 0,7,OFFSET hfTestWeapon2 dd 0,5,OFFSET hfTestWeapon3 dd 0,3,OFFSET hfTestWeapon4 dd 1,-1,OFFSET hfTestWeaponBase1 dd 1,0,OFFSET hfTestWeapon5 dd 1,29,OFFSET hfTestWeapon6 dd 1,18,OFFSET hfTestWeapon7 dd 1,10,OFFSET hfTestWeapon8 HFTestCapture PROTO :DWORD .code HFTestWeaponFrames PROC uses esi ebx local pistolPixels:DWORD,shotgunPixels:DWORD mov eax,hfPistol.pixels mov pistolPixels,eax mov eax,hfShotgun.pixels mov shotgunPixels,eax call HFReset mov hfHasShotgun,1 mov esi,OFFSET hfTestWeaponCases mov ebx,11 hf_test_weapon_frame: mov eax,[esi] mov hfWeapon,eax mov eax,[esi+4] mov hfCooldown,eax cmp eax,-1 jne @F mov hfCooldown,0 mov hfPistol.pixels,0 mov hfShotgun.pixels,0 @@: call RenderHellFPS3D invoke HFTestCapture,DWORD PTR [esi+8] mov eax,pistolPixels mov hfPistol.pixels,eax mov eax,shotgunPixels mov hfShotgun.pixels,eax add esi,12 dec ebx jnz hf_test_weapon_frame call HFReset ret HFTestWeaponFrames ENDP 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 HFTestWeaponFrames 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