feat: update casino environments
This commit is contained in:
parent
5dedaf459f
commit
7e4f7d7be1
36 changed files with 2332 additions and 11 deletions
BIN
assets/sprites/backrooms/Backrooms_ceiling.jpg
Normal file
BIN
assets/sprites/backrooms/Backrooms_ceiling.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 846 KiB |
BIN
assets/sprites/backrooms/Backrooms_ceiling_lamp.jpg
Normal file
BIN
assets/sprites/backrooms/Backrooms_ceiling_lamp.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 MiB |
BIN
assets/sprites/backrooms/Backrooms_floor.jpg
Normal file
BIN
assets/sprites/backrooms/Backrooms_floor.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 MiB |
BIN
assets/sprites/backrooms/Backrooms_wall.jpg
Normal file
BIN
assets/sprites/backrooms/Backrooms_wall.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
BIN
assets/sprites/backrooms/materials.bin
Normal file
BIN
assets/sprites/backrooms/materials.bin
Normal file
Binary file not shown.
BIN
bin/poker.exe
BIN
bin/poker.exe
Binary file not shown.
|
|
@ -8,7 +8,7 @@ namespace AssemblerCasinoLauncher
|
|||
{
|
||||
static class Program
|
||||
{
|
||||
const string ExpectedPokerSha256 = "25FBDF78EDF3DFD3F6B72657E78C3156F74931A656B4CBDB15260D496C96849A";
|
||||
const string ExpectedPokerSha256 = "B0F5CE31D9A2467CFE8AF66CA90CC545F75318F11D149473A4377A2A278C8B23";
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
|
|
|
|||
Binary file not shown.
108
casino3d.inc
108
casino3d.inc
|
|
@ -170,7 +170,8 @@ include src_graphics\native_coin_data.inc
|
|||
include src_graphics\native_dice_data.inc
|
||||
include src_graphics\scratch_data.inc
|
||||
include src_graphics\hell_tower_data.inc
|
||||
include src_graphics\charms_data.inc
|
||||
include src_graphics\charms_data.inc
|
||||
include src_graphics\backrooms_data.inc
|
||||
include src_graphics\observer_data.inc
|
||||
include src_graphics\darts_data.inc
|
||||
include src_graphics\jackbox_data.inc
|
||||
|
|
@ -471,7 +472,7 @@ screenBuffer dd SCREEN_SIZE dup(?)
|
|||
ALIGN 4
|
||||
zBuffer dd SCREEN_W dup(?)
|
||||
ALIGN 4
|
||||
texturesBuffer dd (104 * 128 * 128) dup(?)
|
||||
texturesBuffer dd (185 * 128 * 128) dup(?)
|
||||
|
||||
hudNumBuffer db 32 dup(?)
|
||||
|
||||
|
|
@ -3249,6 +3250,25 @@ dice_start_roll:
|
|||
invoke RandomRange3D, 6
|
||||
inc eax
|
||||
mov dice3d_val2, eax
|
||||
call BackroomsLuck3D
|
||||
cmp eax,-1
|
||||
je br_dice_fixed
|
||||
xor eax,1
|
||||
mov ecx,dice3d_choice
|
||||
dec ecx
|
||||
xor eax,ecx
|
||||
mov edx,dice3d_val1
|
||||
add edx,dice3d_val2
|
||||
and edx,1
|
||||
cmp eax,edx
|
||||
je br_dice_fixed
|
||||
xor edx,edx
|
||||
mov eax,dice3d_val2
|
||||
mov ecx,6
|
||||
div ecx
|
||||
inc edx
|
||||
mov dice3d_val2,edx
|
||||
br_dice_fixed:
|
||||
invoke PlayCasinoSound3D, OFFSET soundDiceRoll, 0, 1
|
||||
call SaveProgress3D
|
||||
ret
|
||||
|
|
@ -3485,6 +3505,21 @@ InitSkullsGame ENDP
|
|||
FinalizeSkullsGuess PROC uses ebx
|
||||
cmp skulls3d_state,3
|
||||
jne skulls_guess_done
|
||||
cmp brSkullsLuck3D,-1
|
||||
je br_skulls_fixed
|
||||
mov eax,skulls3d_choice
|
||||
cmp brSkullsLuck3D,1
|
||||
je @F
|
||||
inc eax
|
||||
xor edx,edx
|
||||
mov ecx,3
|
||||
div ecx
|
||||
mov eax,edx
|
||||
@@:
|
||||
mov skulls3d_ballPos,eax
|
||||
; Reality distortion explicitly covers relocation before the reveal.
|
||||
mov skulls3d_timer,0
|
||||
br_skulls_fixed:
|
||||
mov eax, skulls3d_choice
|
||||
cmp eax, skulls3d_ballPos
|
||||
jne skulls_guess_wrong
|
||||
|
|
@ -3568,6 +3603,8 @@ skulls_bet_down:
|
|||
ret
|
||||
|
||||
skulls_start_game:
|
||||
call BackroomsLuck3D
|
||||
mov brSkullsLuck3D,eax
|
||||
mov eax, skulls3d_bet
|
||||
cmp eax, bankroll
|
||||
jle @F
|
||||
|
|
@ -3716,6 +3753,16 @@ RenderSkullsGame PROC uses ebx esi edi
|
|||
jne @F
|
||||
invoke DrawString8x8, 28, 62, OFFSET textSkullsPhase3, 00FFFF00h
|
||||
jmp skulls_draw_idle_cups
|
||||
@@:
|
||||
; Short artifact glitch, then reveal the actual ball and matching payout.
|
||||
cmp brSkullsLuck3D,-1
|
||||
je @F
|
||||
cmp skulls3d_timer,12
|
||||
jae @F
|
||||
inc skulls3d_timer
|
||||
invoke DrawBox,8,76,304,122,00130F20h
|
||||
invoke DrawString8x8,108,128,OFFSET brRealityGlitch3D,00FF88CCh
|
||||
jmp skulls_render_controls
|
||||
@@:
|
||||
; Phase 4: Reveal outcome
|
||||
mov eax, skulls3d_win
|
||||
|
|
@ -4633,6 +4680,8 @@ check_total_movement:
|
|||
mov playerY, eax
|
||||
|
||||
input_action:
|
||||
cmp brActive3D,0
|
||||
jne player_input_complete
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je player_input_complete
|
||||
invoke Pressed3D, 45h
|
||||
|
|
@ -4706,9 +4755,12 @@ CheckInteractions ENDP
|
|||
; (Legacy console arcade bezel bridge removed)
|
||||
|
||||
Trigger3DInteraction PROC
|
||||
cmp brActive3D,0
|
||||
jne trigger_backrooms_station
|
||||
call TriggerHallInteraction3D
|
||||
test eax, eax
|
||||
jnz trigger_done
|
||||
trigger_backrooms_station:
|
||||
mov eax, interactTargetId
|
||||
test eax, eax
|
||||
jz trigger_done
|
||||
|
|
@ -5200,7 +5252,8 @@ include src_graphics\hell_tower.inc
|
|||
include src_graphics\charms.inc
|
||||
include src_graphics\observer.inc
|
||||
include src_graphics\darts.inc
|
||||
include src_graphics\jackbox.inc
|
||||
include src_graphics\jackbox.inc
|
||||
include src_graphics\backrooms.inc
|
||||
|
||||
; --- Main Loop of the 3D Casino Engine ---
|
||||
StartHellTower3D PROC uses ebx esi edi
|
||||
|
|
@ -5279,6 +5332,9 @@ engine_observer_skip:
|
|||
cmp engineMode, MODE_WALK
|
||||
jne leave_table
|
||||
pause_interior_walk:
|
||||
cmp brCutscene3D,0
|
||||
jne engine_tick
|
||||
mov brEntryHold3D,0
|
||||
xor paused3D, 1
|
||||
jmp engine_tick
|
||||
leave_table:
|
||||
|
|
@ -5343,10 +5399,15 @@ check_arrival_mode:
|
|||
check_walk_mode:
|
||||
cmp engineMode, MODE_WALK
|
||||
jne check_slots_mode
|
||||
cmp brActive3D,0
|
||||
jne engine_backrooms
|
||||
call UpdateHall3D
|
||||
call UpdateCamera3D
|
||||
call CheckInteractions
|
||||
call HandlePlayerInput
|
||||
call BackroomsEntry3D
|
||||
cmp brActive3D,0
|
||||
jne engine_backrooms
|
||||
call HandleJukeboxKeys3D
|
||||
call UpdateInteriorTravel3D
|
||||
call UpdateInteriorEye3D
|
||||
|
|
@ -5365,6 +5426,14 @@ walk_world_ready:
|
|||
call RenderHUD
|
||||
call RenderMinimap
|
||||
jmp present_frame
|
||||
engine_backrooms:
|
||||
call HandleBackrooms3D
|
||||
cmp brActive3D,0
|
||||
je check_walk_mode
|
||||
cmp engineMode,MODE_WALK
|
||||
jne check_slots_mode
|
||||
call RenderBackrooms3D
|
||||
jmp present_frame
|
||||
check_slots_mode:
|
||||
cmp engineMode,MODE_JACKBOX
|
||||
jne check_jackbox_done
|
||||
|
|
@ -5531,6 +5600,7 @@ check_russian_mode:
|
|||
call RenderRussianGame3D
|
||||
jmp present_frame
|
||||
draw_pause:
|
||||
mov brEntryHold3D,0
|
||||
invoke DrawBoxBorder, 28, 36, 264, 168, 00151C21h, 00D8BC6Ah, 2
|
||||
invoke DrawString8x8, 44, 48, OFFSET textPause3D, 00FFFFFFh
|
||||
cmp displayFill3D, 0
|
||||
|
|
@ -5559,7 +5629,10 @@ pause_draw_quality_value:
|
|||
invoke DrawString8x8, 44, 130, OFFSET textSoundLevel3D, 00BBBBBBh
|
||||
invoke FormatNumber, settingsVolume, OFFSET hudNumBuffer
|
||||
invoke DrawString8x8, 124, 130, OFFSET hudNumBuffer, 00FFFFFFh
|
||||
invoke DrawString8x8, 44, 150, OFFSET textFullscreenKey3D, 00A0A0A0h
|
||||
cmp brOwned3D,0
|
||||
jne @F
|
||||
invoke DrawString8x8, 44, 150, OFFSET textFullscreenKey3D, 00A0A0A0h
|
||||
@@:
|
||||
invoke DrawString8x8, 44, 170, OFFSET textResume3D, 00D8BC6Ah
|
||||
invoke DrawString8x8, 44, 188, OFFSET textQuit3D, 00BBBBBBh
|
||||
invoke Pressed3D, 31h
|
||||
|
|
@ -5597,12 +5670,37 @@ pause_low_spec_saved:
|
|||
call ApplySoundVolume
|
||||
call SaveProgress3D
|
||||
@@:
|
||||
cmp brOwned3D,0
|
||||
je br_pause_inventory_done
|
||||
invoke DrawString8x8,44,150,OFFSET brToggle3D,00D8BC6Ah
|
||||
invoke DrawString8x8,36,214,OFFSET brInventory3D,00D8BC6Ah
|
||||
mov edx,OFFSET brDisabledText3D
|
||||
cmp brEnabled3D,0
|
||||
je @F
|
||||
mov edx,OFFSET brEnabledText3D
|
||||
@@:
|
||||
invoke DrawString8x8,248,150,edx,00FFFFFFh
|
||||
invoke Pressed3D,52h
|
||||
test eax,eax
|
||||
jz br_pause_inventory_done
|
||||
xor brEnabled3D,1
|
||||
call SaveProgress3D
|
||||
br_pause_inventory_done:
|
||||
invoke Pressed3D, 51h
|
||||
test eax, eax
|
||||
jz @F
|
||||
call SaveProgress3D
|
||||
jmp engine_shutdown
|
||||
present_frame:
|
||||
cmp brActive3D,0
|
||||
je br_record_clock_done
|
||||
cmp engineMode,MODE_WALK
|
||||
je br_record_clock_done
|
||||
cmp paused3D,0
|
||||
jne br_record_clock_done
|
||||
mov eax,deltaTime3D
|
||||
add brClock3D,eax
|
||||
br_record_clock_done:
|
||||
call UpdateJukeboxAudio3D
|
||||
call RenderTowerInGameBadge
|
||||
call RenderJukeboxVolumeHUD
|
||||
|
|
@ -5638,6 +5736,7 @@ present_frame:
|
|||
invoke Sleep, ecx
|
||||
jmp engine_loop
|
||||
engine_idle:
|
||||
mov brEntryHold3D,0
|
||||
call UnlockMouse3D
|
||||
invoke Sleep, 50
|
||||
jmp engine_loop
|
||||
|
|
@ -5659,6 +5758,7 @@ engine_shutdown:
|
|||
@@:
|
||||
call SaveProgress3D
|
||||
invoke PlayCasinoSound3D, 0, 0, 0
|
||||
invoke FreeAnimPack3D,OFFSET brMaterials3D
|
||||
call FreeRussianAssets3D
|
||||
call FreeDarts3D
|
||||
call FreeStartupAssets3D
|
||||
|
|
|
|||
BIN
dist/Assembler Casino.exe
vendored
BIN
dist/Assembler Casino.exe
vendored
Binary file not shown.
24
poker.asm
24
poker.asm
|
|
@ -820,7 +820,14 @@ save_stats_played_loop:
|
|||
mov DWORD PTR [saveBuffer+296],eax
|
||||
mov eax,charmEnabledMask3D
|
||||
mov DWORD PTR [saveBuffer+300],eax
|
||||
mov DWORD PTR [saveBuffer+304],2
|
||||
mov eax,brOwned3D
|
||||
shl eax,16
|
||||
mov ecx,brEnabled3D
|
||||
and ecx,brOwned3D
|
||||
shl ecx,17
|
||||
or eax,ecx
|
||||
or eax,2
|
||||
mov DWORD PTR [saveBuffer+304],eax
|
||||
invoke ArtifactSaveSignature3D,OFFSET saveBuffer
|
||||
mov DWORD PTR [saveBuffer+308],eax
|
||||
call BuildSaveFullPath
|
||||
|
|
@ -887,6 +894,14 @@ load_file_opened:
|
|||
mov charmDevilUses,eax
|
||||
mov eax,DWORD PTR [saveBuffer+300]
|
||||
mov charmEnabledMask3D,eax
|
||||
mov eax,DWORD PTR [saveBuffer+304]
|
||||
shr eax,16
|
||||
and eax,1
|
||||
mov brOwned3D,eax
|
||||
mov eax,DWORD PTR [saveBuffer+304]
|
||||
shr eax,17
|
||||
and eax,1
|
||||
mov brEnabled3D,eax
|
||||
load_signature_ok:
|
||||
mov eax, DWORD PTR [saveBuffer+4]
|
||||
mov bankroll, eax
|
||||
|
|
@ -2043,7 +2058,14 @@ test_error_exit_engine:
|
|||
RunHellTowerTest3D ENDP
|
||||
ENDIF
|
||||
|
||||
IFDEF BACKROOMS_TEST
|
||||
include tools/tests/backrooms_test.inc
|
||||
ENDIF
|
||||
main PROC
|
||||
IFDEF BACKROOMS_TEST
|
||||
call RunBackroomsTest3D
|
||||
invoke ExitProcess,eax
|
||||
ENDIF
|
||||
IFDEF HELL_TOWER_TEST
|
||||
call RunHellTowerTest3D
|
||||
invoke ExitProcess,eax
|
||||
|
|
|
|||
BIN
poker.exe
BIN
poker.exe
Binary file not shown.
BIN
poker.obj
BIN
poker.obj
Binary file not shown.
|
|
@ -1,4 +1,7 @@
|
|||
HandleArtifactShop3D PROC uses ebx
|
||||
call HandleBackroomsInventory3D
|
||||
test eax,eax
|
||||
jnz artifact_shop_done
|
||||
invoke Pressed3D,09h
|
||||
test eax,eax
|
||||
jz @F
|
||||
|
|
@ -95,6 +98,11 @@ HandleArtifactShop3D ENDP
|
|||
|
||||
RenderArtifactShop3D PROC uses ebx esi edi
|
||||
local y:DWORD,x:DWORD,ink:DWORD
|
||||
cmp brInventoryOpen3D,0
|
||||
je @F
|
||||
call RenderBackroomsInventory3D
|
||||
ret
|
||||
@@:
|
||||
invoke DrawBox,0,0,320,240,0015191Bh
|
||||
invoke DrawBox,0,0,320,24,002B302Ah
|
||||
invoke DrawString8x8,8,7,OFFSET artifactShopTitle3D,00EED5A0h
|
||||
|
|
@ -140,7 +148,13 @@ artifact_shop_card:
|
|||
invoke DrawString8x8,120,77,charmDescs3D[ebx*4],00FFFFFFh
|
||||
; Detail descriptions use full width below cards to avoid clipped text.
|
||||
invoke DrawString8x8,8,216,artifactRules3D[ebx*4],00DDCDAAh
|
||||
cmp brOwned3D,0
|
||||
je @F
|
||||
invoke DrawString8x8,8,228,OFFSET brInventoryHelp3D,00D8BC6Ah
|
||||
jmp br_shop_help_done
|
||||
@@:
|
||||
invoke DrawString8x8,8,228,OFFSET artifactHelp3D,0099AAA5h
|
||||
br_shop_help_done:
|
||||
invoke DrawString8x8,120,96,OFFSET artifactPack3D,0099AAA5h
|
||||
invoke FormatNumber,charmUsesPack3D[ebx*4],OFFSET artifactNum3D
|
||||
invoke DrawString8x8,280,96,OFFSET artifactNum3D,00FFFFFFh
|
||||
|
|
|
|||
|
|
@ -32,8 +32,15 @@ ValidateArtifactSave3D PROC uses esi edi source:DWORD
|
|||
mov esi,source
|
||||
cmp DWORD PTR [esi+280],32465241h
|
||||
jne artifact_save_invalid
|
||||
cmp DWORD PTR [esi+304],2
|
||||
mov eax,[esi+304]
|
||||
and eax,0FFFCFFFFh
|
||||
cmp eax,2
|
||||
jne artifact_save_invalid
|
||||
test DWORD PTR [esi+304],20000h
|
||||
jz @F
|
||||
test DWORD PTR [esi+304],10000h
|
||||
jz artifact_save_invalid
|
||||
@@:
|
||||
invoke ArtifactSaveSignature3D,esi
|
||||
cmp eax,[esi+308]
|
||||
jne artifact_save_invalid
|
||||
|
|
@ -65,6 +72,8 @@ ArtifactCredit3D PROC amount:DWORD
|
|||
ArtifactCredit3D ENDP
|
||||
|
||||
ResetArtifacts3D PROC
|
||||
mov brOwned3D,0
|
||||
mov brEnabled3D,1
|
||||
mov charmCloverUses,0
|
||||
mov charmHorseshoeUses,0
|
||||
mov charmSkullUses,0
|
||||
|
|
|
|||
1068
src_graphics/backrooms.inc
Normal file
1068
src_graphics/backrooms.inc
Normal file
File diff suppressed because it is too large
Load diff
86
src_graphics/backrooms_data.inc
Normal file
86
src_graphics/backrooms_data.inc
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
; Dedicated world/session. All coordinates are 16.16, all time is active ms.
|
||||
HALL_BACKROOMS equ 6
|
||||
BR_WALL equ 180
|
||||
BR_FLOOR equ 181
|
||||
BR_CEILING equ 182
|
||||
BR_LAMP equ 183
|
||||
BR_EXIT equ 184
|
||||
BackroomsEntry3D PROTO
|
||||
BeginBackrooms3D PROTO
|
||||
GenerateBackrooms3D PROTO
|
||||
HandleBackrooms3D PROTO
|
||||
RenderBackrooms3D PROTO
|
||||
LeaveBackrooms3D PROTO :DWORD
|
||||
BackroomsLuck3D PROTO
|
||||
BackroomsWheelLuck3D PROTO :DWORD
|
||||
BackroomsDeck3D PROTO :DWORD,:DWORD,:DWORD
|
||||
BackroomsBlackjackDeck3D PROTO
|
||||
BackroomsPokerDeck3D PROTO
|
||||
BackroomsDurakDeck3D PROTO
|
||||
BackroomsBuckshotLuck3D PROTO
|
||||
BackroomsRussianLuck3D PROTO
|
||||
BackroomsRussianCylinder3D PROTO
|
||||
LoadBackroomsAssets3D PROTO
|
||||
HandleBackroomsInventory3D PROTO
|
||||
RenderBackroomsInventory3D PROTO
|
||||
.data
|
||||
brActive3D dd 0
|
||||
brEntryHold3D dd 0
|
||||
brEntryAngle3D dd 0
|
||||
brCutscene3D dd 0
|
||||
brClock3D dd 0
|
||||
brSkullsLuck3D dd -1
|
||||
brSlotLuck3D dd -1
|
||||
brRussianLuck3D dd -1
|
||||
brBuckshotLuck3D dd -1
|
||||
brDeckTargets3D dd 16 dup(0)
|
||||
brDialogue3D dd 0
|
||||
brOwned3D dd 0
|
||||
brEnabled3D dd 1
|
||||
brVhs3D dd 1
|
||||
brExitCell3D dd 0
|
||||
brTarget3D dd -1
|
||||
brInventoryOpen3D dd 0
|
||||
brMap3D db 1024 dup(BR_WALL)
|
||||
brDistance3D dd 1024 dup(-1)
|
||||
brStack3D dd 1024 dup(0)
|
||||
brCandidates3D dd 4 dup(0)
|
||||
brSteps3D dd -64,2,64,-2
|
||||
brGames3D dd INTERACT_COIN,INTERACT_DICE,INTERACT_SLOTS_777,INTERACT_BLACKJACK,INTERACT_ROULETTE,INTERACT_SLOTS_CLOVER
|
||||
brGameCells3D dd 6 dup(0)
|
||||
brGamePacks3D dd OFFSET hallCardFurniture3D,OFFSET hallCardFurniture3D,OFFSET hallSlotFurniture3D,OFFSET hallCardFurniture3D,OFFSET hallWheelFurniture3D,OFFSET hallSlotFurniture3D
|
||||
brGameFrames3D dd 0,0,0,0,0,1
|
||||
brGamePrompts3D dd OFFSET brCoinPrompt3D,OFFSET brDicePrompt3D,OFFSET brSlotPrompt3D,OFFSET brCardPrompt3D,OFFSET brWheelPrompt3D,OFFSET brCloverPrompt3D
|
||||
brCoinPrompt3D db 'E: COIN',0
|
||||
brDicePrompt3D db 'E: DICE',0
|
||||
brSlotPrompt3D db 'E: 777',0
|
||||
brCardPrompt3D db 'E: BLACKJACK',0
|
||||
brWheelPrompt3D db 'E: ROULETTE',0
|
||||
brCloverPrompt3D db 'E: CLOVERPIT',0
|
||||
brProp3D CityProp3D <0,0,FP_1*3/5,FP_1*4/5,0,OFFSET hallCardFurniture3D>
|
||||
brMaterials3D AnimPack3D <0>
|
||||
brMaterialPath3D db "assets\sprites\backrooms\materials.bin",0
|
||||
brRec3D db 'REC',0
|
||||
brTime3D db '00:00:00',0
|
||||
brExitPrompt3D db 'E: EXIT',0
|
||||
brHelp3D db 'V: VHS ESC: PAUSE',0
|
||||
brInventory3D db 'REALITY ERROR: 90% / INFINITE',0
|
||||
brToggle3D db 'R: TOGGLE REALITY ERROR',0
|
||||
brEnabledText3D db 'ON',0
|
||||
brDisabledText3D db 'OFF',0
|
||||
brRealityGlitch3D db 'REALITY ERROR',0
|
||||
brAward3D db 'REALITY ERROR / INFINITE / 90%',0
|
||||
brInventoryHelp3D db 'R: REALITY ERROR / E: TOGGLE',0
|
||||
brInventoryReturn3D db 'R / TAB: BACK',0
|
||||
brInventoryRules1 db '90% FAVORABLE RANDOM OUTCOME',0
|
||||
brInventoryRules2 db 'NO CHARGES / NO DUPLICATES',0
|
||||
brInventoryRules3 db 'NORMAL CASINO ONLY',0
|
||||
brInventoryRules4 db 'OFF IN TOWER / BACKROOMS',0
|
||||
brInventoryRules5 db 'SKILL / CARD RULES STILL APPLY',0
|
||||
brInventoryRules6 db 'BILLIARDS/BOWLING/DARTS UNCHANGED',0
|
||||
brGlyph3D db 0,0
|
||||
; Exact CP1251 dialogue, wrapped only at spaces by the renderer.
|
||||
brText3D db 221,242,238,32,237,229,32,242,243,242,44,32,242,251,32,237,229,32
|
||||
db 228,238,235,230,229,237,32,225,251,242,252,32,231,228,229,241,252,44,32
|
||||
db 241,250,229,225,232,32,239,238,32,225,240,224,242,241,234,232,32,239,230,0
|
||||
.code
|
||||
|
|
@ -97,6 +97,7 @@ bs_reload_next:
|
|||
inc bs3d_blankCount
|
||||
dec bs3d_liveCount
|
||||
@@:
|
||||
call BackroomsBuckshotLuck3D
|
||||
; The loading motion loops once per actual cartridge, then racks the pump.
|
||||
mov eax, bs3d_shellCount
|
||||
imul eax, 330
|
||||
|
|
|
|||
|
|
@ -5,6 +5,19 @@ cityRayY3D dd SCREEN_W dup(?)
|
|||
; Exterior-only materials do not consume or overwrite the hall's texture slots.
|
||||
ResolveCitySurface3D PROC material:DWORD
|
||||
mov eax,material
|
||||
cmp hallLevel3D,HALL_BACKROOMS
|
||||
jne br_surface_other_world
|
||||
cmp eax,BR_WALL
|
||||
jb @F
|
||||
cmp eax,BR_EXIT
|
||||
ja @F
|
||||
shl eax,16
|
||||
add eax,OFFSET texturesBuffer
|
||||
mov ecx,128
|
||||
mov edx,128
|
||||
ret
|
||||
@@:
|
||||
br_surface_other_world:
|
||||
cmp eax,BW_MATERIAL
|
||||
jb @F
|
||||
cmp eax,BW_LAST_MATERIAL
|
||||
|
|
@ -290,6 +303,27 @@ city_vertical_along:
|
|||
dec edx
|
||||
and eax, edx
|
||||
@@:
|
||||
; Door lettering faces the viewer on either wall face. Other worlds keep UVs.
|
||||
cmp hallLevel3D,HALL_BACKROOMS
|
||||
jne br_door_uv_done
|
||||
cmp material,BR_EXIT
|
||||
jne br_door_uv_done
|
||||
cmp axis,0
|
||||
jne br_door_uv_y
|
||||
mov edx,playerX
|
||||
cmp edx,coordinate
|
||||
jle br_door_uv_done
|
||||
jmp br_door_uv_flip
|
||||
br_door_uv_y:
|
||||
mov edx,playerY
|
||||
cmp edx,coordinate
|
||||
jge br_door_uv_done
|
||||
br_door_uv_flip:
|
||||
mov edx,textureW
|
||||
dec edx
|
||||
sub edx,eax
|
||||
mov eax,edx
|
||||
br_door_uv_done:
|
||||
mov sourceX, eax
|
||||
mov eax, viewEye3D
|
||||
sub eax, zHigh
|
||||
|
|
|
|||
|
|
@ -19,7 +19,13 @@ hall_plane_row:
|
|||
sub ecx, eax
|
||||
mov eax, viewCeiling3D
|
||||
sub eax, viewEye3D
|
||||
mov material, 101
|
||||
mov material, 101
|
||||
cmp brActive3D,0
|
||||
je @F
|
||||
cmp hallLevel3D,HALL_BACKROOMS
|
||||
jne @F
|
||||
mov material,BR_CEILING
|
||||
@@:
|
||||
cmp hallLevel3D,2
|
||||
jne @F
|
||||
mov material,125
|
||||
|
|
@ -130,7 +136,27 @@ hall_plane_crossroad:
|
|||
jl hall_plane_sample
|
||||
hall_plane_sidewalk:
|
||||
mov ebx, 72
|
||||
hall_plane_sample:
|
||||
hall_plane_sample:
|
||||
cmp brActive3D,0
|
||||
je br_plane_sample_done
|
||||
cmp hallLevel3D,HALL_BACKROOMS
|
||||
jne br_plane_sample_done
|
||||
mov eax,row
|
||||
cmp eax,viewHorizon3D
|
||||
jae br_plane_sample_done
|
||||
mov ebx,BR_CEILING
|
||||
mov eax,wx
|
||||
shr eax,16
|
||||
and eax,3
|
||||
cmp eax,1
|
||||
jne br_plane_sample_done
|
||||
mov eax,wy
|
||||
shr eax,16
|
||||
and eax,3
|
||||
cmp eax,1
|
||||
jne br_plane_sample_done
|
||||
mov ebx,BR_LAMP
|
||||
br_plane_sample_done:
|
||||
cmp outdoorRender3D, 0
|
||||
jne hall_plane_low_sample
|
||||
mov eax, row
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ StartNativeBlackjack3D PROC
|
|||
mov cbDealerCount3D, 2
|
||||
mov cbCursor3D, 4
|
||||
invoke ShuffleCards3D, OFFSET cbDeck3D
|
||||
call BackroomsBlackjackDeck3D
|
||||
mov eax, cbDeck3D[0]
|
||||
mov cbPlayer3D[0], eax
|
||||
mov eax, cbDeck3D[4]
|
||||
|
|
|
|||
|
|
@ -104,6 +104,41 @@ clp_start_rows:
|
|||
inc esi
|
||||
cmp esi,5
|
||||
jb clp_start_column
|
||||
call BackroomsLuck3D
|
||||
cmp eax,-1
|
||||
je br_clover_fixed
|
||||
mov ebx,eax
|
||||
xor esi,esi
|
||||
br_clover_row:
|
||||
xor edi,edi
|
||||
br_clover_column:
|
||||
mov eax,esi
|
||||
imul eax,2
|
||||
add eax,edi
|
||||
xor edx,edx
|
||||
mov ecx,7
|
||||
div ecx
|
||||
cmp ebx,1
|
||||
jne @F
|
||||
mov edx,1
|
||||
@@:
|
||||
mov eax,esi
|
||||
imul eax,5
|
||||
add eax,edi
|
||||
mov clpGrid3D[eax*4],edx
|
||||
mov eax,clpSteps3D[edi*4]
|
||||
add eax,esi
|
||||
mov ecx,edi
|
||||
shl ecx,8
|
||||
lea ecx,clpTape3D[ecx]
|
||||
mov [ecx+eax*4],edx
|
||||
inc edi
|
||||
cmp edi,5
|
||||
jb br_clover_column
|
||||
inc esi
|
||||
cmp esi,3
|
||||
jb br_clover_row
|
||||
br_clover_fixed:
|
||||
call SaveProgress3D
|
||||
invoke PlayCasinoSound3D,OFFSET soundSlotSpin,0,1
|
||||
clp_start_done:
|
||||
|
|
|
|||
|
|
@ -308,6 +308,16 @@ coin_native_start:
|
|||
invoke RandomRange3D,2
|
||||
inc eax
|
||||
mov coin3d_result,eax
|
||||
call BackroomsLuck3D
|
||||
cmp eax,-1
|
||||
je br_coin_fixed
|
||||
xor eax,1
|
||||
mov ecx,coin3d_choice
|
||||
dec ecx
|
||||
xor eax,ecx
|
||||
inc eax
|
||||
mov coin3d_result,eax
|
||||
br_coin_fixed:
|
||||
call SaveProgress3D
|
||||
invoke PlayCasinoSound3D,OFFSET soundCoin,0,1
|
||||
call ClearKeys3D
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ dk_compact:
|
|||
inc esi
|
||||
cmp esi, 52
|
||||
jb dk_compact
|
||||
call BackroomsDurakDeck3D
|
||||
mov eax, dkDeck3D[35*4]
|
||||
mov durakTrumpCard, eax
|
||||
invoke DurakCardSuit, eax
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ StartNativePoker3D PROC uses ebx esi
|
|||
mov pkRaise3D, eax
|
||||
mov pkCurrent3D, eax
|
||||
invoke ShuffleCards3D, OFFSET pkDeck3D
|
||||
call BackroomsPokerDeck3D
|
||||
mov esi, pkButton3D
|
||||
xor esi, 1
|
||||
shl esi, 3 ; first card goes to the non-button player
|
||||
|
|
|
|||
|
|
@ -69,11 +69,22 @@ StartNativeSlots3D PROC uses ebx esi
|
|||
mov slotLeaveAfter3D,0
|
||||
mov slotError3D,0
|
||||
mov slotEffects3D,0
|
||||
call BackroomsLuck3D
|
||||
mov brSlotLuck3D,eax
|
||||
xor esi,esi
|
||||
slot_start_reel:
|
||||
mov eax,slotStops3D[esi*4]
|
||||
mov slotStartStops3D[esi*4],eax
|
||||
invoke RandomRange3D,20
|
||||
cmp brSlotLuck3D,-1
|
||||
je br_slot_target
|
||||
mov eax,0
|
||||
cmp brSlotLuck3D,1
|
||||
je br_slot_target
|
||||
; Distinct symbols, with no refund pair, for the unlucky 10%.
|
||||
mov eax,esi
|
||||
inc eax
|
||||
br_slot_target:
|
||||
mov slotTargets3D[esi*4],eax
|
||||
mov edx,slotStrip3D[eax*4]
|
||||
mov slotReel1[esi*4],edx
|
||||
|
|
|
|||
|
|
@ -352,6 +352,7 @@ wheel_regular_spin:
|
|||
mov roulState, 1
|
||||
mov wheelTime3D, 0
|
||||
invoke RandomRange3D, 37
|
||||
invoke BackroomsWheelLuck3D,eax
|
||||
mov wheelResult3D, eax
|
||||
call SaveProgress3D
|
||||
invoke PlayCasinoSound3D, OFFSET soundRoulette,0,1
|
||||
|
|
|
|||
|
|
@ -137,6 +137,12 @@ event_key_up:
|
|||
cmp ecx, 255
|
||||
ja event_done
|
||||
mov keys3D[ecx], 0
|
||||
cmp ecx,57h
|
||||
je event_entry_release
|
||||
cmp ecx,26h
|
||||
jne event_done
|
||||
event_entry_release:
|
||||
mov brEntryHold3D,0
|
||||
jmp event_done
|
||||
event_fullscreen:
|
||||
test lp, 40000000h
|
||||
|
|
@ -144,6 +150,7 @@ event_fullscreen:
|
|||
call ToggleFullscreen3D
|
||||
jmp event_done
|
||||
event_focus_lost:
|
||||
mov brEntryHold3D,0
|
||||
mov dtHolding3D,0
|
||||
mov dtMouseReleased3D,0
|
||||
call ClearKeys3D
|
||||
|
|
@ -505,9 +512,12 @@ UpdateCamera3D ENDP
|
|||
|
||||
; Four corners of a 0.3-tile-wide body; bounds checked before every map read.
|
||||
CanWalk3D PROC uses ebx esi edi px:DWORD, py:DWORD
|
||||
cmp brActive3D,0
|
||||
jne br_generic_collision
|
||||
invoke CanWalkHall3D, px, py
|
||||
test eax, eax
|
||||
jz walk_blocked
|
||||
br_generic_collision:
|
||||
mov esi, -9830
|
||||
walk_corner_x:
|
||||
mov edi, -9830
|
||||
|
|
@ -550,6 +560,8 @@ LoadProgress3D PROC uses ebx
|
|||
mov charmSkullUses, 0
|
||||
mov charmDevilUses, 0
|
||||
mov charmEnabledMask3D,7
|
||||
mov brOwned3D,0
|
||||
mov brEnabled3D,1
|
||||
invoke CreateFileA, OFFSET savePath3D, 80000000h, 1, 0, 3, 80h, 0
|
||||
cmp eax, -1
|
||||
je progress_loaded
|
||||
|
|
@ -566,7 +578,23 @@ LoadProgress3D PROC uses ebx
|
|||
cmp eax,saveData3D[36]
|
||||
jne progress_loaded
|
||||
mov eax,saveData3D[32]
|
||||
mov charmEnabledMask3D,eax
|
||||
test eax,0FFFCFFF0h
|
||||
jnz progress_loaded
|
||||
test eax,20000h
|
||||
jz @F
|
||||
test eax,10000h
|
||||
jz progress_loaded
|
||||
@@:
|
||||
mov ecx,eax
|
||||
and ecx,15
|
||||
mov charmEnabledMask3D,ecx
|
||||
shr eax,16
|
||||
and eax,1
|
||||
mov brOwned3D,eax
|
||||
mov eax,saveData3D[32]
|
||||
shr eax,17
|
||||
and eax,1
|
||||
mov brEnabled3D,eax
|
||||
jmp progress_version_ok
|
||||
progress_legacy_version:
|
||||
cmp saveData3D, 33443343h
|
||||
|
|
@ -653,7 +681,15 @@ SaveProgress3D PROC uses ebx
|
|||
mov saveData3D[24], eax
|
||||
mov eax, charmDevilUses
|
||||
mov saveData3D[28], eax
|
||||
mov eax,charmEnabledMask3D
|
||||
mov eax,brOwned3D
|
||||
and eax,1
|
||||
shl eax,16
|
||||
mov ecx,brEnabled3D
|
||||
and ecx,brOwned3D
|
||||
and ecx,1
|
||||
shl ecx,17
|
||||
or eax,ecx
|
||||
or eax,charmEnabledMask3D
|
||||
mov saveData3D[32],eax
|
||||
invoke PreviewArtifactSignature3D,OFFSET saveData3D
|
||||
mov saveData3D[36],eax
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ rr_party_cartridge:
|
|||
jb rr_party_cartridge
|
||||
invoke RandomRange3D,6
|
||||
mov rr3d_chamber,eax
|
||||
cmp rrPartyReloads3D,0
|
||||
je @F
|
||||
call BackroomsRussianCylinder3D
|
||||
@@:
|
||||
inc rrPartyReloads3D
|
||||
ret
|
||||
RRLoadCylinder3D ENDP
|
||||
|
|
|
|||
|
|
@ -98,9 +98,11 @@ StartRussianRound3D PROC uses ebx esi
|
|||
sub bankroll, eax
|
||||
mov rr3d_payout, 0
|
||||
mov rr3d_error, 0
|
||||
mov brRussianLuck3D,-1
|
||||
call RRLoadCylinder3D
|
||||
invoke RandomRange3D, rrPartySize3D
|
||||
mov rr3d_turn, eax
|
||||
call BackroomsRussianLuck3D
|
||||
mov rr3d_state, RR_LOAD
|
||||
mov rr3d_timer, 0
|
||||
invoke PlayCasinoSound3D, OFFSET rrLoadSound3D, 0, 20003h
|
||||
|
|
|
|||
|
|
@ -246,6 +246,16 @@ BuyScratch3D PROC uses ebx esi edi
|
|||
jmp scratch_buy_save
|
||||
@@:
|
||||
invoke RandomRange3D,1000
|
||||
push eax
|
||||
call BackroomsLuck3D
|
||||
pop edx
|
||||
cmp eax,-1
|
||||
je br_scratch_original
|
||||
imul eax,650
|
||||
jmp br_scratch_fixed
|
||||
br_scratch_original:
|
||||
mov eax,edx
|
||||
br_scratch_fixed:
|
||||
cmp eax,600
|
||||
jb scratch_losing_bag
|
||||
xor esi,esi
|
||||
|
|
|
|||
|
|
@ -321,6 +321,16 @@ portrait_fill:
|
|||
jb portrait_fill
|
||||
mov scrPortraitOutcome3D,0
|
||||
invoke RandomRange3D,1000
|
||||
push eax
|
||||
call BackroomsLuck3D
|
||||
pop edx
|
||||
cmp eax,-1
|
||||
je br_portrait_original
|
||||
imul eax,920
|
||||
jmp br_portrait_fixed
|
||||
br_portrait_original:
|
||||
mov eax,edx
|
||||
br_portrait_fixed:
|
||||
cmp eax,600
|
||||
jb portrait_build_payout
|
||||
mov ebx,eax
|
||||
|
|
|
|||
32
tools/generators/make_backrooms_assets.py
Normal file
32
tools/generators/make_backrooms_assets.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
"""Offline-only texture packing; runtime generation/rendering stays MASM."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def main():
|
||||
source = ROOT / 'assets/sprites/backrooms'
|
||||
frames = []
|
||||
for name in ('wall', 'floor', 'ceiling', 'ceiling_lamp'):
|
||||
image = Image.open(source / f'Backrooms_{name}.jpg').convert('RGBA')
|
||||
image = image.resize((128, 128), Image.Resampling.LANCZOS)
|
||||
frames.append(image.tobytes('raw', 'BGRA'))
|
||||
# Exit is an offline-generated material, not a second game implementation.
|
||||
image = Image.new('RGBA', (128, 128), (36, 60, 38, 255))
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.rectangle((9, 4, 119, 127), fill=(61, 84, 55), outline=(143, 160, 98), width=5)
|
||||
# Eye-level sign stays visible even when standing in the exit cell.
|
||||
draw.rectangle((18, 45, 110, 73), fill=(32, 170, 77))
|
||||
font = ImageFont.truetype('arialbd.ttf', 23)
|
||||
draw.text((64, 59), 'EXIT', fill='white', font=font, anchor='mm')
|
||||
draw.rectangle((94, 76, 99, 82), fill=(240, 211, 101))
|
||||
frames.append(image.tobytes('raw', 'BGRA'))
|
||||
out = source / 'materials.bin'
|
||||
out.write_bytes(struct.pack('<5I', 0x314D4E41, 128, 128, len(frames), len(frames)*65536) + b''.join(frames))
|
||||
print(f'Packed {len(frames)} Backrooms materials: {out.relative_to(ROOT)}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
391
tools/tests/backrooms_extended_test.inc
Normal file
391
tools/tests/backrooms_extended_test.inc
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
; Additional native invariants. No player files or runtime writes.
|
||||
RunBackroomsExtendedTest3D PROC uses ebx esi edi
|
||||
local rounds:DWORD,wins:DWORD,kind:DWORD,party:DWORD,bullets:DWORD,steps:DWORD
|
||||
mov brOwned3D,1
|
||||
mov brEnabled3D,0
|
||||
mov rngSeed3D,87654
|
||||
call BackroomsLuck3D
|
||||
cmp eax,-1
|
||||
jne br_extended_fail
|
||||
cmp rngSeed3D,87654
|
||||
jne br_extended_fail
|
||||
mov brEnabled3D,1
|
||||
mov obsActive3D,1
|
||||
call BackroomsLuck3D
|
||||
cmp eax,-1
|
||||
jne br_extended_fail
|
||||
mov obsActive3D,0
|
||||
mov brTestStage,111
|
||||
; Actual blackjack party dealing, all guest counts.
|
||||
mov party,0
|
||||
br_ext_bj_party:
|
||||
mov wins,0
|
||||
mov rounds,1000
|
||||
br_ext_bj_round:
|
||||
mov bankroll,1000000
|
||||
mov eax,party
|
||||
mov cbBotCount3D,eax
|
||||
mov cbState3D,CB_IDLE
|
||||
call StartNativeBlackjack3D
|
||||
cmp cbPlayerScore3D,21
|
||||
jne @F
|
||||
inc wins
|
||||
cmp cbDealerScore3D,19
|
||||
jne br_extended_fail
|
||||
@@:
|
||||
dec rounds
|
||||
jnz br_ext_bj_round
|
||||
cmp wins,860
|
||||
jb br_extended_fail
|
||||
cmp wins,940
|
||||
ja br_extended_fail
|
||||
inc party
|
||||
cmp party,4
|
||||
jb br_ext_bj_party
|
||||
mov cbBotCount3D,0
|
||||
; Every scratch design, generated by the actual purchase path.
|
||||
mov scrReady3D,1
|
||||
mov brTestStage,112
|
||||
mov kind,0
|
||||
br_ext_ticket_type:
|
||||
mov eax,kind
|
||||
add eax,120
|
||||
mov brTestStage,eax
|
||||
mov wins,0
|
||||
mov rounds,1000
|
||||
br_ext_ticket:
|
||||
mov bankroll,1000000
|
||||
mov scrState3D,0
|
||||
mov scrPriceIndex3D,0
|
||||
mov eax,kind
|
||||
mov scrType3D,eax
|
||||
call BuyScratch3D
|
||||
mov eax,scrGross3D
|
||||
test eax,eax
|
||||
jz @F
|
||||
inc wins
|
||||
@@:
|
||||
cmp kind,4
|
||||
jb @F
|
||||
call ScratchPortraitPayout3D
|
||||
cmp eax,scrGross3D
|
||||
jne br_extended_fail
|
||||
@@:
|
||||
dec rounds
|
||||
jnz br_ext_ticket
|
||||
cmp wins,860
|
||||
jb br_extended_fail
|
||||
cmp wins,940
|
||||
ja br_extended_fail
|
||||
inc kind
|
||||
cmp kind,7
|
||||
jb br_ext_ticket_type
|
||||
; Roulette: a real straight-up bet, including actual multiplier matching.
|
||||
lea edi,wheelBets3D
|
||||
mov brTestStage,113
|
||||
xor eax,eax
|
||||
mov ecx,49
|
||||
rep stosd
|
||||
mov wheelBets3D[7*4],100
|
||||
mov wheelStake3D,100
|
||||
mov wins,0
|
||||
mov rounds,1000
|
||||
br_ext_wheel:
|
||||
invoke BackroomsWheelLuck3D,0
|
||||
cmp eax,36
|
||||
ja br_extended_fail
|
||||
invoke WheelBetMultiplier3D,7,eax
|
||||
test eax,eax
|
||||
jz @F
|
||||
inc wins
|
||||
@@:
|
||||
dec rounds
|
||||
jnz br_ext_wheel
|
||||
cmp wins,860
|
||||
jb br_extended_fail
|
||||
cmp wins,940
|
||||
ja br_extended_fail
|
||||
; Durak: 36 distinct legal cards, correctly exposed trump on favorable deals.
|
||||
mov rounds,1000
|
||||
br_ext_durak:
|
||||
mov brTestStage,114
|
||||
mov bankroll,1000000
|
||||
mov dkState3D,DK_IDLE
|
||||
call StartNativeDurak3D
|
||||
xor esi,esi
|
||||
br_ext_durak_card:
|
||||
mov eax,dkDeck3D[esi*4]
|
||||
cmp eax,52
|
||||
jae br_extended_fail
|
||||
invoke DurakCardRank,eax
|
||||
cmp eax,4
|
||||
jb br_extended_fail
|
||||
lea edi,[esi+1]
|
||||
br_ext_durak_unique:
|
||||
cmp edi,36
|
||||
jae br_ext_durak_next
|
||||
mov eax,dkDeck3D[edi*4]
|
||||
cmp eax,dkDeck3D[esi*4]
|
||||
je br_extended_fail
|
||||
inc edi
|
||||
jmp br_ext_durak_unique
|
||||
br_ext_durak_next:
|
||||
inc esi
|
||||
cmp esi,36
|
||||
jb br_ext_durak_card
|
||||
cmp dkDeck3D,33
|
||||
jne @F
|
||||
cmp durakTrumpSuit,2
|
||||
jne br_extended_fail
|
||||
@@:
|
||||
dec rounds
|
||||
jnz br_ext_durak
|
||||
; Russian roulette: real party elimination/reload rules, all 1..3 guests/bullets.
|
||||
mov party,1
|
||||
mov brTestStage,115
|
||||
br_ext_rr_party:
|
||||
mov bullets,1
|
||||
br_ext_rr_bullets:
|
||||
mov rounds,300
|
||||
mov wins,0
|
||||
br_ext_rr_round:
|
||||
mov brTestStage,151
|
||||
mov eax,party
|
||||
mov rrPartyCount3D,eax
|
||||
call RRPartyReset3D
|
||||
mov eax,bullets
|
||||
mov rr3d_bullets,eax
|
||||
mov rrPartySettled3D,0
|
||||
mov rrPartyStake3D,50
|
||||
mov rr3d_bet,50
|
||||
mov rr3d_turn,0
|
||||
call BackroomsRussianLuck3D
|
||||
mov rrPartyReloads3D,1
|
||||
mov steps,0
|
||||
br_ext_rr_shot:
|
||||
mov eax,rr3d_mask
|
||||
mov ecx,rr3d_chamber
|
||||
bt eax,ecx
|
||||
jnc br_ext_rr_advance
|
||||
call RRPartyHit3D
|
||||
cmp rrPartySettled3D,0
|
||||
jne br_ext_rr_outcome
|
||||
br_ext_rr_advance:
|
||||
call RRPartyNext3D
|
||||
inc steps
|
||||
cmp steps,100
|
||||
jae br_extended_fail
|
||||
jmp br_ext_rr_shot
|
||||
br_ext_rr_outcome:
|
||||
mov brTestStage,152
|
||||
cmp rr3d_state,RR_WIN
|
||||
jne @F
|
||||
inc wins
|
||||
cmp brRussianLuck3D,1
|
||||
jne br_extended_fail
|
||||
@@:
|
||||
cmp brRussianLuck3D,1
|
||||
jne @F
|
||||
mov eax,party
|
||||
imul eax,10
|
||||
add eax,bullets
|
||||
add eax,200
|
||||
mov brTestStage,eax
|
||||
cmp rr3d_state,RR_WIN
|
||||
jne br_extended_fail
|
||||
@@:
|
||||
dec rounds
|
||||
jnz br_ext_rr_round
|
||||
mov brTestStage,154
|
||||
cmp wins,240
|
||||
jb br_extended_fail
|
||||
cmp wins,295
|
||||
ja br_extended_fail
|
||||
inc bullets
|
||||
cmp bullets,4
|
||||
jb br_ext_rr_bullets
|
||||
inc party
|
||||
cmp party,4
|
||||
jb br_ext_rr_party
|
||||
; Buckshot permutations retain cartridge counts and valid binary identities.
|
||||
mov rounds,1000
|
||||
br_ext_bs:
|
||||
mov brTestStage,116
|
||||
call BuckshotReload3D
|
||||
xor edi,edi
|
||||
xor esi,esi
|
||||
br_ext_bs_shell:
|
||||
mov eax,bs3d_shells[esi*4]
|
||||
cmp eax,1
|
||||
ja br_extended_fail
|
||||
add edi,eax
|
||||
inc esi
|
||||
cmp esi,bs3d_shellCount
|
||||
jb br_ext_bs_shell
|
||||
cmp edi,bs3d_liveCount
|
||||
jne br_extended_fail
|
||||
add edi,bs3d_blankCount
|
||||
cmp edi,bs3d_shellCount
|
||||
jne br_extended_fail
|
||||
dec rounds
|
||||
jnz br_ext_bs
|
||||
xor eax,eax
|
||||
ret
|
||||
br_extended_fail:
|
||||
mov eax,brTestStage
|
||||
ret
|
||||
RunBackroomsExtendedTest3D ENDP
|
||||
|
||||
RunBackroomsSaveTest3D PROC uses ebx esi edi
|
||||
; Signed but impossible enabled-without-owned flag must be rejected.
|
||||
mov DWORD PTR saveBuffer[304],20002h
|
||||
invoke ArtifactSaveSignature3D,OFFSET saveBuffer
|
||||
mov DWORD PTR saveBuffer[308],eax
|
||||
invoke ValidateArtifactSave3D,OFFSET saveBuffer
|
||||
test eax,eax
|
||||
jnz br_save_fail
|
||||
; Native preview keeps its 40-byte, signed backward-compatible layout.
|
||||
invoke CopyCString,OFFSET savePath3D,OFFSET brTestPreviewPath
|
||||
mov nativeProfileActive3D,0
|
||||
mov arrivalConfirmed3D,1
|
||||
mov brOwned3D,1
|
||||
mov brEnabled3D,1
|
||||
call SaveProgress3D
|
||||
mov brOwned3D,0
|
||||
mov brEnabled3D,0
|
||||
call LoadProgress3D
|
||||
cmp brOwned3D,1
|
||||
jne br_save_fail
|
||||
cmp brEnabled3D,1
|
||||
jne br_save_fail
|
||||
mov brActive3D,1
|
||||
mov brEnabled3D,0
|
||||
invoke LeaveBackrooms3D,1
|
||||
cmp brOwned3D,1
|
||||
jne br_save_fail
|
||||
cmp brEnabled3D,0
|
||||
jne br_save_fail
|
||||
call LoadProgress3D
|
||||
cmp brEnabled3D,0
|
||||
jne br_save_fail
|
||||
xor eax,eax
|
||||
ret
|
||||
br_save_fail:
|
||||
mov eax,102
|
||||
ret
|
||||
RunBackroomsSaveTest3D ENDP
|
||||
|
||||
.data
|
||||
brContextModes dd MODE_COIN,MODE_DICE,MODE_SLOTS,MODE_BLACKJACK,MODE_ROULETTE,MODE_CLOVER
|
||||
brContextMap db 1024 dup(0)
|
||||
.code
|
||||
RunBackroomsContextTest3D PROC uses ebx esi edi
|
||||
local index:DWORD,x:DWORD,y:DWORD
|
||||
call GenerateBackrooms3D
|
||||
mov brActive3D,1
|
||||
mov brCutscene3D,0
|
||||
mov hallLevel3D,HALL_BACKROOMS
|
||||
mov worldMap3D,OFFSET brMap3D
|
||||
lea esi,brMap3D
|
||||
lea edi,brContextMap
|
||||
mov ecx,256
|
||||
rep movsd
|
||||
mov index,0
|
||||
br_context_game:
|
||||
mov esi,index
|
||||
mov ebx,brGameCells3D[esi*4]
|
||||
mov eax,ebx
|
||||
and eax,31
|
||||
shl eax,16
|
||||
add eax,FP_HALF
|
||||
mov x,eax
|
||||
mov playerX,eax
|
||||
mov eax,ebx
|
||||
shr eax,5
|
||||
shl eax,16
|
||||
add eax,FP_HALF
|
||||
mov y,eax
|
||||
mov playerY,eax
|
||||
mov playerAngle,77
|
||||
mov engineMode,MODE_WALK
|
||||
mov mouseLocked3D,0
|
||||
mov deltaTime3D,16
|
||||
call ClearKeys3D
|
||||
mov pressedKeys3D[45h],1
|
||||
call HandleBackrooms3D
|
||||
mov esi,index
|
||||
mov eax,brContextModes[esi*4]
|
||||
cmp eax,engineMode
|
||||
jne br_context_fail
|
||||
cmp eax,MODE_DICE
|
||||
jne @F
|
||||
call DiceEscape3D
|
||||
jmp br_context_returned
|
||||
@@:
|
||||
call CardTableEscape3D
|
||||
br_context_returned:
|
||||
cmp engineMode,MODE_WALK
|
||||
jne br_context_fail
|
||||
mov eax,x
|
||||
cmp playerX,eax
|
||||
jne br_context_fail
|
||||
mov eax,y
|
||||
cmp playerY,eax
|
||||
jne br_context_fail
|
||||
cmp playerAngle,77
|
||||
jne br_context_fail
|
||||
cmp worldMap3D,OFFSET brMap3D
|
||||
jne br_context_fail
|
||||
cmp hallLevel3D,HALL_BACKROOMS
|
||||
jne br_context_fail
|
||||
lea esi,brMap3D
|
||||
lea edi,brContextMap
|
||||
mov ecx,1024
|
||||
repe cmpsb
|
||||
jne br_context_fail
|
||||
inc index
|
||||
cmp index,6
|
||||
jb br_context_game
|
||||
mov brActive3D,0
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
xor eax,eax
|
||||
ret
|
||||
br_context_fail:
|
||||
mov eax,103
|
||||
ret
|
||||
RunBackroomsContextTest3D ENDP
|
||||
|
||||
RunBackroomsWallTest3D PROC uses esi
|
||||
mov brActive3D,0
|
||||
mov engineMode,MODE_WALK
|
||||
mov mouseLocked3D,0
|
||||
mov playerX,16*FP_1+FP_HALF
|
||||
mov playerY,27*FP_1+FP_HALF
|
||||
mov playerAngle,128
|
||||
mov deltaTime3D,16
|
||||
mov brEntryHold3D,0
|
||||
call ClearKeys3D
|
||||
mov keys3D[57h],1
|
||||
mov esi,500
|
||||
br_wall_walk:
|
||||
call HandlePlayerInput
|
||||
call BackroomsEntry3D
|
||||
cmp brActive3D,1
|
||||
je br_wall_entered
|
||||
dec esi
|
||||
jnz br_wall_walk
|
||||
mov eax,104
|
||||
ret
|
||||
br_wall_entered:
|
||||
cmp playerY,30*FP_1
|
||||
jae br_wall_fail
|
||||
cmp playerY,30*FP_1-15000
|
||||
jb br_wall_fail
|
||||
call ClearKeys3D
|
||||
xor eax,eax
|
||||
ret
|
||||
br_wall_fail:
|
||||
mov eax,105
|
||||
ret
|
||||
RunBackroomsWallTest3D ENDP
|
||||
288
tools/tests/backrooms_test.inc
Normal file
288
tools/tests/backrooms_test.inc
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
.data
|
||||
brTestPath db 'build\scratch\backrooms-native.bin',0
|
||||
brTestFrame db 'build\scratch\backrooms-frame.bin',0
|
||||
brTestExitFrame db 'build\scratch\backrooms-exit.bin',0
|
||||
brTestIo dd 0
|
||||
brTestSeeds dd 256
|
||||
brTestStage dd 91
|
||||
brTestRows dd 0
|
||||
brTestLuckPath db 'build\scratch\backrooms-luck.bin',0
|
||||
brTestProfileDir db 'build\scratch',0
|
||||
brTestProfileName db 'backrooms-profile.dat',0
|
||||
brTestProfilePath db 'build\scratch\backrooms-profile.dat',0
|
||||
brTestPreviewPath db 'build\scratch\br-preview.dat',0
|
||||
brTestFacesPath db 'build\scratch\backrooms-exit-faces.bin',0
|
||||
brTestFaceNeighbors dd -32,32,-1,1
|
||||
brTestFaceAngles dd 384,128,256,0
|
||||
.code
|
||||
include tools/tests/backrooms_extended_test.inc
|
||||
RunBackroomsTest3D PROC uses ebx esi edi
|
||||
invoke CreateFileA,OFFSET brTestPath,40000000h,0,0,2,80h,0
|
||||
mov ebx,eax
|
||||
cmp eax,-1
|
||||
je br_test_fail
|
||||
mov rngSeed3D,12345
|
||||
br_test_seed:
|
||||
call GenerateBackrooms3D
|
||||
invoke WriteFile,ebx,OFFSET brMap3D,1024,OFFSET brTestIo,0
|
||||
invoke WriteFile,ebx,OFFSET brExitCell3D,4,OFFSET brTestIo,0
|
||||
invoke WriteFile,ebx,OFFSET brGameCells3D,24,OFFSET brTestIo,0
|
||||
dec brTestSeeds
|
||||
jnz br_test_seed
|
||||
invoke CloseHandle,ebx
|
||||
mov brTestStage,92
|
||||
mov obsActive3D,0
|
||||
mov tournamentMode,0
|
||||
mov brActive3D,0
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,0
|
||||
mov playerX,16*FP_1+FP_HALF
|
||||
mov playerY,30*FP_1-11000
|
||||
mov playerAngle,128
|
||||
mov keys3D[57h],1
|
||||
mov deltaTime3D,50
|
||||
mov esi,39
|
||||
br_test_entry:
|
||||
call BackroomsEntry3D
|
||||
cmp brActive3D,0
|
||||
jne br_test_fail
|
||||
dec esi
|
||||
jnz br_test_entry
|
||||
cmp brEntryHold3D,1950
|
||||
jne br_test_fail
|
||||
mov brTestStage,93
|
||||
mov playerAngle,129
|
||||
call BackroomsEntry3D
|
||||
cmp brEntryHold3D,0
|
||||
jne br_test_fail
|
||||
mov playerAngle,128
|
||||
call BackroomsEntry3D
|
||||
cmp brEntryHold3D,50
|
||||
jne br_test_fail
|
||||
mov keys3D[57h],0
|
||||
call BackroomsEntry3D
|
||||
cmp brEntryHold3D,0
|
||||
jne br_test_fail
|
||||
mov brTestStage,94
|
||||
mov keys3D[57h],1
|
||||
mov brEntryHold3D,1950
|
||||
mov brEntryAngle3D,128
|
||||
call BackroomsEntry3D
|
||||
cmp brActive3D,1
|
||||
jne br_test_fail
|
||||
cmp brCutscene3D,1
|
||||
jne br_test_fail
|
||||
mov brClock3D,900
|
||||
call HandleBackrooms3D
|
||||
mov brTestStage,95
|
||||
invoke CanWalk3D,FP_1+FP_HALF,FP_1+FP_HALF
|
||||
cmp eax,1
|
||||
jne br_test_fail
|
||||
invoke CanWalk3D,FP_HALF,FP_HALF
|
||||
test eax,eax
|
||||
jnz br_test_fail
|
||||
mov brTestStage,96
|
||||
; Set up standard projection LUT through the real hall initializer first.
|
||||
call InitHall3D
|
||||
call RunBackroomsWallTest3D
|
||||
test eax,eax
|
||||
jnz br_test_return
|
||||
mov brActive3D,1
|
||||
mov hallLevel3D,HALL_BACKROOMS
|
||||
mov worldMap3D,OFFSET brMap3D
|
||||
mov floorTexture3D,BR_FLOOR
|
||||
mov viewHorizon3D,HALF_H
|
||||
mov brClock3D,8100
|
||||
mov brDialogue3D,2500
|
||||
mov brCutscene3D,0
|
||||
mov playerX,FP_1+FP_HALF
|
||||
mov playerY,FP_1+FP_HALF
|
||||
mov playerAngle,64
|
||||
call RenderBackrooms3D
|
||||
invoke CreateFileA,OFFSET brTestFrame,40000000h,0,0,2,80h,0
|
||||
mov ebx,eax
|
||||
invoke WriteFile,ebx,OFFSET screenBuffer,SCREEN_SIZE*4,OFFSET brTestIo,0
|
||||
invoke CloseHandle,ebx
|
||||
; Door capture from its reachable exit cell, facing a neighboring wall.
|
||||
mov ebx,brExitCell3D
|
||||
mov eax,ebx
|
||||
and eax,31
|
||||
shl eax,16
|
||||
add eax,FP_HALF
|
||||
mov playerX,eax
|
||||
mov eax,ebx
|
||||
shr eax,5
|
||||
shl eax,16
|
||||
add eax,FP_HALF
|
||||
mov playerY,eax
|
||||
mov playerAngle,384
|
||||
cmp brMap3D[ebx-32],0
|
||||
jne br_test_exit_camera
|
||||
mov playerAngle,128
|
||||
cmp brMap3D[ebx+32],0
|
||||
jne br_test_exit_camera
|
||||
mov playerAngle,256
|
||||
cmp brMap3D[ebx-1],0
|
||||
jne br_test_exit_camera
|
||||
mov playerAngle,0
|
||||
br_test_exit_camera:
|
||||
mov brDialogue3D,10000
|
||||
call RenderBackrooms3D
|
||||
invoke CreateFileA,OFFSET brTestExitFrame,40000000h,0,0,2,80h,0
|
||||
mov ebx,eax
|
||||
invoke WriteFile,ebx,OFFSET screenBuffer,SCREEN_SIZE*4,OFFSET brTestIo,0
|
||||
invoke CloseHandle,ebx
|
||||
mov arrivalConfirmed3D,0
|
||||
call RunBackroomsDoorFrames3D
|
||||
invoke LeaveBackrooms3D,1
|
||||
cmp brOwned3D,1
|
||||
jne br_test_fail
|
||||
cmp brActive3D,0
|
||||
jne br_test_fail
|
||||
cmp hallLevel3D,0
|
||||
jne br_test_fail
|
||||
mov brTestStage,97
|
||||
call RunBackroomsContextTest3D
|
||||
test eax,eax
|
||||
jnz br_test_return
|
||||
mov brActive3D,1
|
||||
call BackroomsLuck3D
|
||||
cmp eax,-1
|
||||
jne br_test_fail
|
||||
mov brActive3D,0
|
||||
mov tournamentMode,1
|
||||
call BackroomsLuck3D
|
||||
cmp eax,-1
|
||||
jne br_test_fail
|
||||
mov tournamentMode,0
|
||||
xor edi,edi
|
||||
mov esi,100000
|
||||
br_test_luck:
|
||||
call BackroomsLuck3D
|
||||
add edi,eax
|
||||
dec esi
|
||||
jnz br_test_luck
|
||||
cmp edi,89000
|
||||
jb br_test_fail
|
||||
cmp edi,91000
|
||||
ja br_test_fail
|
||||
call RunBackroomsExtendedTest3D
|
||||
test eax,eax
|
||||
jnz br_test_return
|
||||
mov brTestStage,98
|
||||
call LoadNativeCoinArt3D
|
||||
call InitNativeSlots3D
|
||||
call InitNativeClover3D
|
||||
call InitNativeBlackjack3D
|
||||
call InitNativePoker3D
|
||||
invoke CreateFileA,OFFSET brTestLuckPath,40000000h,0,0,2,80h,0
|
||||
mov ebx,eax
|
||||
mov brTestRows,1000
|
||||
br_test_rounds:
|
||||
mov bankroll,1000000
|
||||
mov coin3d_choice,1
|
||||
mov coin3d_state,COIN_IDLE
|
||||
mov pressedKeys3D[20h],1
|
||||
call HandleNativeCoin3D
|
||||
invoke WriteFile,ebx,OFFSET coin3d_result,4,OFFSET brTestIo,0
|
||||
mov dice3d_state,0
|
||||
mov dice3d_choice,1
|
||||
mov pressedKeys3D[20h],1
|
||||
call HandleDiceRules3D
|
||||
invoke WriteFile,ebx,OFFSET dice3d_val1,8,OFFSET brTestIo,0
|
||||
mov slotSpinState,SLOT_READY
|
||||
call StartNativeSlots3D
|
||||
invoke WriteFile,ebx,OFFSET slotReel1,12,OFFSET brTestIo,0
|
||||
mov clpState3D,SLOT_READY
|
||||
call StartNativeClover3D
|
||||
call CloverEvaluate3D
|
||||
invoke WriteFile,ebx,OFFSET clpTotal3D,4,OFFSET brTestIo,0
|
||||
mov cbBotCount3D,0
|
||||
mov cbState3D,CB_IDLE
|
||||
call StartNativeBlackjack3D
|
||||
invoke WriteFile,ebx,OFFSET cbPlayerScore3D,4,OFFSET brTestIo,0
|
||||
invoke WriteFile,ebx,OFFSET cbDeck3D,208,OFFSET brTestIo,0
|
||||
mov pkState3D,PK_IDLE
|
||||
call StartNativePoker3D
|
||||
invoke WriteFile,ebx,OFFSET pkDeck3D,208,OFFSET brTestIo,0
|
||||
invoke WriteFile,ebx,OFFSET pkButton3D,4,OFFSET brTestIo,0
|
||||
dec brTestRows
|
||||
jnz br_test_rounds
|
||||
invoke CloseHandle,ebx
|
||||
mov brTestStage,99
|
||||
mov nativeProfilesDirPtr3D,OFFSET brTestProfileDir
|
||||
invoke CopyCString,OFFSET saveFileName,OFFSET brTestProfileName
|
||||
mov nativeProfileActive3D,1
|
||||
mov brOwned3D,1
|
||||
mov brEnabled3D,0
|
||||
call SaveGame
|
||||
mov brOwned3D,0
|
||||
mov brEnabled3D,1
|
||||
call LoadSave
|
||||
cmp brOwned3D,1
|
||||
jne br_test_fail
|
||||
cmp brEnabled3D,0
|
||||
jne br_test_fail
|
||||
invoke ValidateGraphicProfile3D,OFFSET brTestProfilePath
|
||||
cmp eax,1
|
||||
jne br_test_fail
|
||||
call RunBackroomsSaveTest3D
|
||||
test eax,eax
|
||||
jnz br_test_return
|
||||
xor eax,eax
|
||||
ret
|
||||
br_test_fail:
|
||||
mov eax,brTestStage
|
||||
br_test_return:
|
||||
ret
|
||||
RunBackroomsTest3D ENDP
|
||||
|
||||
; Identical door/camera distance on every face, VHS off/on pairs.
|
||||
RunBackroomsDoorFrames3D PROC uses ebx esi edi
|
||||
local face:DWORD,effect:DWORD
|
||||
invoke CreateFileA,OFFSET brTestFacesPath,40000000h,0,0,2,80h,0
|
||||
mov ebx,eax
|
||||
mov face,0
|
||||
br_test_door_face:
|
||||
lea edi,brMap3D
|
||||
xor eax,eax
|
||||
mov ecx,256
|
||||
rep stosd
|
||||
; Keep all rays inside the fixture map, including peripheral FOV rays.
|
||||
xor esi,esi
|
||||
br_test_door_border:
|
||||
mov brMap3D[esi],BR_WALL
|
||||
mov brMap3D[esi+31*32],BR_WALL
|
||||
mov eax,esi
|
||||
shl eax,5
|
||||
mov brMap3D[eax],BR_WALL
|
||||
mov brMap3D[eax+31],BR_WALL
|
||||
inc esi
|
||||
cmp esi,32
|
||||
jb br_test_door_border
|
||||
mov brExitCell3D,16*32+16
|
||||
mov esi,face
|
||||
mov eax,brTestFaceNeighbors[esi*4]
|
||||
mov brMap3D[eax+16*32+16],BR_WALL
|
||||
mov playerX,16*FP_1+FP_HALF
|
||||
mov playerY,16*FP_1+FP_HALF
|
||||
mov eax,brTestFaceAngles[esi*4]
|
||||
mov playerAngle,eax
|
||||
mov viewEye3D,HALL_STANDING_EYE
|
||||
mov viewHorizon3D,HALF_H
|
||||
mov brDialogue3D,10000
|
||||
mov effect,0
|
||||
br_test_door_effect:
|
||||
mov eax,effect
|
||||
mov brVhs3D,eax
|
||||
call RenderBackrooms3D
|
||||
invoke WriteFile,ebx,OFFSET screenBuffer,SCREEN_SIZE*4,OFFSET brTestIo,0
|
||||
inc effect
|
||||
cmp effect,2
|
||||
jb br_test_door_effect
|
||||
inc face
|
||||
cmp face,4
|
||||
jb br_test_door_face
|
||||
invoke CloseHandle,ebx
|
||||
ret
|
||||
RunBackroomsDoorFrames3D ENDP
|
||||
128
tools/tests/check_backrooms.py
Normal file
128
tools/tests/check_backrooms.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
"""Execute shipping MASM maze/entry/collision/render, inspect native output."""
|
||||
from collections import deque
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import subprocess
|
||||
from PIL import Image
|
||||
from check_artifacts import toolchain
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
OUT = ROOT / 'build/scratch'
|
||||
|
||||
|
||||
def check_exit_faces():
|
||||
raw = (OUT/'backrooms-exit-faces.bin').read_bytes()
|
||||
assert len(raw) == 8*640*480*4
|
||||
frames = [Image.frombytes('RGBA', (640, 480), raw[i:i+640*480*4],
|
||||
'raw', 'BGRA').convert('RGB')
|
||||
for i in range(0, len(raw), 640*480*4)]
|
||||
# Source glyph at runtime's exact nearest-neighbor UV scale. Compare both
|
||||
# orientations rather than merely counting green pixels (mirrors pass that).
|
||||
pack = (ROOT/'assets/sprites/backrooms/materials.bin').read_bytes()
|
||||
art = Image.frombytes('RGBA', (128, 128), pack[20+4*65536:], 'raw', 'BGRA')
|
||||
glyph = [(x, y) for y in range(48, 70) for x in range(27, 101)
|
||||
if min(art.getpixel((x, y))[:3]) > 240]
|
||||
for face, name in enumerate(('north', 'south', 'west', 'east')):
|
||||
clean, vhs = frames[2*face:2*face+2]
|
||||
clean.save(OUT/f'backrooms-exit-{name}.png')
|
||||
vhs.save(OUT/f'backrooms-exit-{name}-vhs.png')
|
||||
white = [(x, y) for y in range(0, 300) for x in range(60, 580)
|
||||
if min(clean.getpixel((x, y))) > 240]
|
||||
assert white, f'{name}: EXIT missing'
|
||||
x0, x1 = min(x for x, y in white), max(x for x, y in white)
|
||||
y0, y1 = min(y for x, y in white), max(y for x, y in white)
|
||||
gx0, gx1 = min(x for x, y in glyph), max(x for x, y in glyph)
|
||||
gy0, gy1 = min(y for x, y in glyph), max(y for x, y in glyph)
|
||||
def score(mirror):
|
||||
hits = 0
|
||||
for x, y in glyph:
|
||||
u = (x-gx0)/(gx1-gx0)
|
||||
if mirror:
|
||||
u = 1-u
|
||||
px = round(x0+u*(x1-x0))
|
||||
py = round(y0+(y-gy0)/(gy1-gy0)*(y1-y0))
|
||||
hits += min(clean.getpixel((px, py))) > 240
|
||||
return hits/len(glyph)
|
||||
normal, mirrored = score(False), score(True)
|
||||
assert normal > .85 and normal > mirrored+.12, (name, normal, mirrored)
|
||||
# White glyphs may darken on scanlines, never wrap into black/color noise.
|
||||
corrupt = sum(min(vhs.getpixel(p)) < 180 or
|
||||
max(vhs.getpixel(p))-min(vhs.getpixel(p)) > 12 for p in white)
|
||||
assert corrupt == 0, (name, 'VHS corrupt white pixels', corrupt, len(white))
|
||||
|
||||
|
||||
def main():
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
ml, link = toolchain()
|
||||
sdk = Path('C:/Program Files (x86)/Windows Kits/10/Lib')
|
||||
lib = next(p/'um/x86' for p in sorted(sdk.iterdir(), reverse=True)
|
||||
if (p/'um/x86/kernel32.lib').exists())
|
||||
for args in ([ml, '/c', '/coff', '/Cp', '/nologo', '/I.', '/DBACKROOMS_TEST=1',
|
||||
'/Fobuild/scratch/backrooms.obj', 'poker.asm'],
|
||||
[link, '/nologo', '/SUBSYSTEM:CONSOLE', '/LIBPATH:'+str(lib),
|
||||
'/OUT:build/scratch/backrooms-test.exe', 'build/scratch/backrooms.obj',
|
||||
'kernel32.lib', 'winmm.lib', 'user32.lib', 'ws2_32.lib', 'gdi32.lib']):
|
||||
subprocess.run(list(map(str, args)), cwd=ROOT, check=True, timeout=120)
|
||||
subprocess.run([str(OUT/'backrooms-test.exe')], cwd=ROOT, check=True, timeout=90)
|
||||
raw = (OUT/'backrooms-native.bin').read_bytes()
|
||||
assert len(raw) == 256 * 1052
|
||||
fingerprints = set()
|
||||
minimum = 10000
|
||||
for offset in range(0, len(raw), 1052):
|
||||
grid = raw[offset:offset+1024]
|
||||
exit_cell, *games = struct.unpack_from('<7I', raw, offset+1024)
|
||||
assert all(grid[x] for x in range(32))
|
||||
assert all(grid[31*32+x] for x in range(32))
|
||||
assert all(grid[y*32] and grid[y*32+31] for y in range(32))
|
||||
distance, queue = {33: 0}, deque([33])
|
||||
while queue:
|
||||
cell = queue.popleft()
|
||||
for neighbor in (cell-32, cell+1, cell+32, cell-1):
|
||||
if grid[neighbor] == 0 and neighbor not in distance:
|
||||
distance[neighbor] = distance[cell]+1
|
||||
queue.append(neighbor)
|
||||
assert len(distance) == grid.count(0), 'Disconnected open component'
|
||||
assert distance[exit_cell] == max(distance.values())
|
||||
assert len(set(games)) == 6 and exit_cell not in games and 33 not in games
|
||||
assert all(cell in distance for cell in games)
|
||||
minimum = min(minimum, distance[exit_cell])
|
||||
fingerprints.add(grid)
|
||||
assert len(fingerprints) == 256
|
||||
assert minimum >= 40, minimum
|
||||
luck = (OUT/'backrooms-luck.bin').read_bytes()
|
||||
assert len(luck) == 1000*452
|
||||
wins = dict.fromkeys(('coin', 'dice', '777', 'clover', 'blackjack', 'poker'), 0)
|
||||
for row in struct.iter_unpack('<113I', luck):
|
||||
coin, d1, d2, s1, s2, s3, clover, bj = row[:8]
|
||||
assert coin in (1, 2) and 1 <= d1 <= 6 and 1 <= d2 <= 6
|
||||
wins['coin'] += coin == 1
|
||||
wins['dice'] += (d1+d2) % 2 == 0
|
||||
assert (s1, s2, s3) in ((1, 1, 1), (2, 5, 3))
|
||||
wins['777'] += s1 == s2 == s3
|
||||
assert clover in (0, 78)
|
||||
wins['clover'] += clover > 0
|
||||
deck, poker, button = row[8:60], row[60:112], row[112]
|
||||
assert sorted(deck) == sorted(poker) == list(range(52)), 'Duplicate/missing card'
|
||||
assert bj in (19, 21)
|
||||
assert bj == (21 if deck[0] == 12 else 19)
|
||||
wins['blackjack'] += bj == 21
|
||||
# Actual non-button-first deal order, plus actual burn/flop/turn/river offsets.
|
||||
seat = button ^ 1
|
||||
hand = [poker[seat], poker[seat+2], *(poker[i] for i in (5, 6, 7, 9, 11))]
|
||||
wins['poker'] += set(range(8, 13)).issubset(hand)
|
||||
assert all(860 <= n <= 940 for n in wins.values()), wins
|
||||
image = Image.frombytes('RGBA', (640, 480), (OUT/'backrooms-frame.bin').read_bytes(), 'raw', 'BGRA').convert('RGB')
|
||||
image.save(OUT/'backrooms-frame.png')
|
||||
assert len(image.getcolors(640*480)) > 100
|
||||
image = Image.frombytes('RGBA', (640, 480), (OUT/'backrooms-exit.bin').read_bytes(), 'raw', 'BGRA').convert('RGB')
|
||||
image.save(OUT/'backrooms-exit.png')
|
||||
assert sum(g > 1.4*r and g > 1.2*b for r, g, b in image.get_flattened_data()) > 100, 'Exit not visibly green'
|
||||
check_exit_faces()
|
||||
print('PASS: readable non-mirrored EXIT on all four wall faces; VHS preserves white glyphs')
|
||||
print(f'PASS: 256 native seeds; sealed connected mazes; farthest exit >= {minimum} steps; unique reachable games; entry hold/reset; collision; isolation; profile roundtrip; render')
|
||||
print(f'PASS: 1000 production rounds per game, valid unique decks, wins: {wins}')
|
||||
print('PASS: all blackjack party sizes; 7 scratch designs; roulette; Durak conservation/trump; 9 Russian party/bullet setups; Buckshot counts; preview saves; repeat reward')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Reference in a new issue