Extend profiles from 280 to 312 bytes with legacy compatibility. Keep tower relics run-local and share the profile bankroll.
502 lines
14 KiB
PHP
502 lines
14 KiB
PHP
; Native regression: real production procedures, isolated output paths.
|
|
.data
|
|
artifactTestPath3D db "build\artifacts\profile.dat",0
|
|
artifactTestDir3D db "build\artifacts",0
|
|
artifactTestPreview3D db "build\artifacts\preview.dat",0
|
|
artifactTestLegacy3D db "build\artifacts\legacy.dat",0
|
|
artifactTestProfileName3D db "profile.dat",0
|
|
artifactTestLegacyName3D db "legacy.dat",0
|
|
artifactTestBmp3D db "build\artifacts\shop.bmp",0
|
|
artifactTestRewardBmp3D db "build\artifacts\rewards.bmp",0
|
|
artifactTestHeader3D db 42h,4Dh
|
|
dd 1228854
|
|
dw 0,0
|
|
dd 54,40,640,-480
|
|
dw 1,32
|
|
dd 0,1228800,0,0,0,0
|
|
.code
|
|
ArtifactTestCapture3D PROC uses ebx path:DWORD
|
|
local io:DWORD
|
|
invoke CreateFileA,path,40000000h,0,0,2,80h,0
|
|
mov ebx,eax
|
|
invoke WriteFile,ebx,OFFSET artifactTestHeader3D,54,ADDR io,0
|
|
invoke WriteFile,ebx,OFFSET screenBuffer,SCREEN_SIZE*4,ADDR io,0
|
|
invoke CloseHandle,ebx
|
|
ret
|
|
ArtifactTestCapture3D ENDP
|
|
|
|
RunArtifactTest3D PROC uses ebx esi edi
|
|
local savedBank:DWORD,oldPath[260]:BYTE,io:DWORD
|
|
mov obsActive3D,0
|
|
mov tournamentMode,0
|
|
mov nativeProfileActive3D,0
|
|
mov arrivalConfirmed3D,0
|
|
call ResetArtifacts3D
|
|
mov bankroll,10000
|
|
invoke BuyMerchantCharm3D,0
|
|
cmp bankroll,9100
|
|
jne artifact_fail_1
|
|
cmp charmCloverUses,3
|
|
jne artifact_fail_1
|
|
invoke TriggerCharmLossProtection,100
|
|
cmp bankroll,9150
|
|
jne artifact_fail_1
|
|
cmp charmCloverUses,2
|
|
jne artifact_fail_1
|
|
invoke TriggerCharmLossProtection,0
|
|
cmp charmCloverUses,2
|
|
jne artifact_fail_1
|
|
mov charmHorseshoeUses,5
|
|
invoke TriggerCharmWinBonus,1000
|
|
cmp eax,350
|
|
jne artifact_fail_2
|
|
cmp bankroll,9500
|
|
jne artifact_fail_2
|
|
mov charmDevilUses,2
|
|
invoke ToggleArtifact3D,3
|
|
cmp charmEnabledMask3D,12
|
|
jne artifact_fail_2
|
|
invoke TriggerCharmWinBonus,100
|
|
cmp eax,100
|
|
jne artifact_fail_2
|
|
cmp charmDevilUses,1
|
|
jne artifact_fail_2
|
|
invoke TriggerCharmLossProtection,100
|
|
cmp eax,0
|
|
jne artifact_fail_2
|
|
cmp charmDevilUses,0
|
|
jne artifact_fail_2
|
|
mov charmEnabledMask3D,7
|
|
mov bankroll,999999999
|
|
invoke TriggerCharmWinBonus,1000000000
|
|
cmp eax,1
|
|
jne artifact_fail_3
|
|
cmp bankroll,1000000000
|
|
jne artifact_fail_3
|
|
mov eax,charmHorseshoeUses
|
|
mov ebx,eax
|
|
invoke TriggerCharmWinBonus,100
|
|
cmp charmHorseshoeUses,ebx
|
|
jne artifact_fail_3
|
|
mov charmSkullUses,1
|
|
mov obsActive3D,1
|
|
invoke TriggerCharmBulletProtection
|
|
test eax,eax
|
|
jnz artifact_fail_4
|
|
cmp charmSkullUses,1
|
|
jne artifact_fail_4
|
|
mov obsActive3D,0
|
|
mov tournamentMode,1
|
|
invoke TriggerCharmLossProtection,100
|
|
test eax,eax
|
|
jnz artifact_fail_4
|
|
invoke TriggerCharmBulletProtection
|
|
test eax,eax
|
|
jnz artifact_fail_4
|
|
mov towerBulletUses3D,1
|
|
invoke TriggerCharmBulletProtection
|
|
cmp eax,1
|
|
jne artifact_fail_4
|
|
cmp charmSkullUses,1
|
|
jne artifact_fail_4
|
|
cmp towerBulletUses3D,0
|
|
jne artifact_fail_4
|
|
mov tournamentMode,0
|
|
mov charmCloverUses,98
|
|
mov bankroll,10000
|
|
invoke BuyMerchantCharm3D,0
|
|
cmp bankroll,10000
|
|
jne artifact_fail_5
|
|
cmp charmCloverUses,98
|
|
jne artifact_fail_5
|
|
mov charmCloverUses,0
|
|
mov bankroll,0
|
|
invoke BuyMerchantCharm3D,0
|
|
cmp charmCloverUses,0
|
|
jne artifact_fail_5
|
|
|
|
; Reward cannot be claimed twice; reset starts a fresh run without touching bank.
|
|
mov bankroll,10000
|
|
mov towerStage,1
|
|
mov towerFloorState,TOWER_STATE_BRIEFING
|
|
call ResetTowerRelics3D
|
|
call PrepareTowerReward3D
|
|
invoke ChooseTowerRelic3D,0
|
|
invoke ChooseTowerRelic3D,0
|
|
cmp towerHeartUses3D,1
|
|
jne artifact_fail_6
|
|
mov tournamentMode,1
|
|
mov towerFloorState,TOWER_STATE_IN_GAME
|
|
invoke TowerReportRoundOutcome,2
|
|
cmp towerFloorState,TOWER_STATE_BRIEFING
|
|
jne artifact_fail_6
|
|
cmp towerHeartUses3D,0
|
|
jne artifact_fail_6
|
|
; Duplicate report cannot kill a saved attempt.
|
|
invoke TowerReportRoundOutcome,2
|
|
cmp towerFloorState,TOWER_STATE_BRIEFING
|
|
jne artifact_fail_6
|
|
mov towerFloorState,TOWER_STATE_IN_GAME
|
|
invoke TowerReportRoundOutcome,1
|
|
cmp towerRewardPending3D,1
|
|
jne artifact_fail_6
|
|
invoke ChooseTowerRelic3D,1
|
|
cmp towerSealStage3D,2
|
|
jne artifact_fail_6
|
|
cmp towerSealUses3D,2
|
|
jne artifact_fail_6
|
|
mov towerStage,2
|
|
mov towerFloorState,TOWER_STATE_IN_GAME
|
|
invoke TowerReportRoundOutcome,2
|
|
cmp towerSealUses3D,1
|
|
jne artifact_fail_6
|
|
cmp towerFloorState,TOWER_STATE_BRIEFING
|
|
jne artifact_fail_6
|
|
call ResetTowerRelics3D
|
|
cmp towerSealUses3D,0
|
|
jne artifact_fail_6
|
|
cmp bankroll,10000
|
|
jne artifact_fail_6
|
|
; Exact net loss refunded, not arbitrary prize or a second stake payout.
|
|
mov towerPurseUses3D,1
|
|
mov towerHeartUses3D,1
|
|
mov towerEntryBank3D,10000
|
|
mov bankroll,9900
|
|
mov towerFloorState,TOWER_STATE_IN_GAME
|
|
invoke TowerReportRoundOutcome,2
|
|
cmp bankroll,10000
|
|
jne artifact_fail_6
|
|
cmp towerPurseUses3D,0
|
|
jne artifact_fail_6
|
|
mov towerStage,6
|
|
mov towerFloorState,TOWER_STATE_IN_GAME
|
|
invoke TowerReportRoundOutcome,1
|
|
mov eax,bankroll
|
|
mov savedBank,eax
|
|
invoke TowerReportRoundOutcome,1
|
|
mov eax,savedBank
|
|
cmp bankroll,eax
|
|
jne artifact_fail_6
|
|
cmp towerHeartUses3D,0
|
|
jne artifact_fail_6
|
|
|
|
; Bust protection removes the real last card, without drawing a replacement.
|
|
mov towerBustUses3D,1
|
|
mov cbPlayerCount3D,3
|
|
mov cbPlayer3D,8
|
|
mov cbPlayer3D[4],7
|
|
mov cbPlayer3D[8],6
|
|
call TryTowerBustSave3D
|
|
cmp eax,1
|
|
jne artifact_fail_7
|
|
cmp cbPlayerCount3D,2
|
|
jne artifact_fail_7
|
|
cmp cbPlayerScore3D,19
|
|
jne artifact_fail_7
|
|
mov towerEye3D,1
|
|
mov bs3d_shellIndex,0
|
|
mov bs3d_shellCount,2
|
|
mov bs3d_shells,1
|
|
call UpdateTowerEye3D
|
|
cmp bs3d_known,1
|
|
jne artifact_fail_7
|
|
mov tournamentMode,0
|
|
call ResetTowerRelics3D
|
|
|
|
; Both legacy checksum and versioned extension must validate.
|
|
mov DWORD PTR [saveBuffer+280],32465241h
|
|
mov DWORD PTR [saveBuffer+284],3
|
|
mov DWORD PTR [saveBuffer+288],5
|
|
mov DWORD PTR [saveBuffer+292],1
|
|
mov DWORD PTR [saveBuffer+296],2
|
|
mov DWORD PTR [saveBuffer+300],7
|
|
mov DWORD PTR [saveBuffer+304],2
|
|
invoke ArtifactSaveSignature3D,OFFSET saveBuffer
|
|
mov DWORD PTR [saveBuffer+308],eax
|
|
invoke ValidateArtifactSave3D,OFFSET saveBuffer
|
|
cmp eax,1
|
|
jne artifact_fail_8
|
|
inc DWORD PTR [saveBuffer+284]
|
|
invoke ValidateArtifactSave3D,OFFSET saveBuffer
|
|
test eax,eax
|
|
jnz artifact_fail_8
|
|
|
|
; Real profile write/reload uses test paths, never runtime/profiles.
|
|
invoke CreateDirectoryA,OFFSET artifactTestDir3D,0
|
|
mov nativeProfilesDirPtr3D,OFFSET artifactTestDir3D
|
|
mov nativeProfileActive3D,1
|
|
invoke CopyCString,OFFSET saveFileName,OFFSET artifactTestProfileName3D
|
|
mov charmCloverUses,3
|
|
mov charmHorseshoeUses,5
|
|
mov charmSkullUses,1
|
|
mov charmDevilUses,2
|
|
mov charmEnabledMask3D,7
|
|
mov bankroll,12345
|
|
call SaveGraphicProfile3D
|
|
invoke ValidateGraphicProfile3D,OFFSET artifactTestPath3D
|
|
cmp eax,1
|
|
jne artifact_fail_9
|
|
call ResetArtifacts3D
|
|
mov bankroll,0
|
|
call LoadSave
|
|
cmp bankroll,12345
|
|
jne artifact_fail_9
|
|
cmp charmCloverUses,3
|
|
jne artifact_fail_9
|
|
cmp charmDevilUses,2
|
|
jne artifact_fail_9
|
|
cmp charmEnabledMask3D,7
|
|
jne artifact_fail_9
|
|
; A signed 280-byte legacy file imports with an empty artifact inventory.
|
|
invoke CreateFileA,OFFSET artifactTestLegacy3D,40000000h,0,0,2,80h,0
|
|
mov ebx,eax
|
|
invoke WriteFile,ebx,OFFSET saveBuffer,SAVE_LEGACY_SIZE,ADDR io,0
|
|
invoke CloseHandle,ebx
|
|
invoke ValidateGraphicProfile3D,OFFSET artifactTestLegacy3D
|
|
cmp eax,1
|
|
jne artifact_fail_9
|
|
invoke CopyCString,OFFSET saveFileName,OFFSET artifactTestLegacyName3D
|
|
call LoadSave
|
|
cmp charmCloverUses,0
|
|
jne artifact_fail_9
|
|
cmp bankroll,12345
|
|
jne artifact_fail_9
|
|
|
|
; Preview format roundtrip, disabled effects and checksum rejection.
|
|
mov nativeProfileActive3D,0
|
|
invoke CopyCString,OFFSET savePath3D,OFFSET artifactTestPreview3D
|
|
mov arrivalConfirmed3D,1
|
|
mov bankroll,54321
|
|
mov charmCloverUses,9
|
|
mov charmHorseshoeUses,8
|
|
mov charmSkullUses,7
|
|
mov charmDevilUses,6
|
|
mov charmEnabledMask3D,4
|
|
call SaveProgress3D
|
|
mov arrivalConfirmed3D,0
|
|
call ResetArtifacts3D
|
|
call LoadProgress3D
|
|
cmp bankroll,54321
|
|
jne artifact_fail_10
|
|
cmp charmCloverUses,9
|
|
jne artifact_fail_10
|
|
cmp charmDevilUses,6
|
|
jne artifact_fail_10
|
|
cmp charmEnabledMask3D,4
|
|
jne artifact_fail_10
|
|
invoke TriggerCharmWinBonus,100
|
|
test eax,eax
|
|
jnz artifact_fail_10
|
|
cmp charmHorseshoeUses,8
|
|
jne artifact_fail_10
|
|
inc saveData3D[16]
|
|
invoke CreateFileA,OFFSET artifactTestPreview3D,40000000h,0,0,2,80h,0
|
|
mov ebx,eax
|
|
invoke WriteFile,ebx,OFFSET saveData3D,40,ADDR io,0
|
|
invoke CloseHandle,ebx
|
|
call LoadProgress3D
|
|
cmp bankroll,10000
|
|
jne artifact_fail_10
|
|
cmp charmCloverUses,0
|
|
jne artifact_fail_10
|
|
call ResetProfileDefaults
|
|
cmp charmSkullUses,0
|
|
jne artifact_fail_10
|
|
|
|
; Production Buckshot settlement pays net-profit bonus and cannot repeat it.
|
|
mov bankroll,9900
|
|
mov charmEnabledMask3D,7
|
|
mov charmHorseshoeUses,2
|
|
mov bs3d_bet,100
|
|
mov bs3d_state,BS_SHOT
|
|
mov bs3d_afterAnim,BS_WIN
|
|
call FinishBuckshotAction3D
|
|
cmp bankroll,10270
|
|
jne artifact_fail_11
|
|
cmp charmHorseshoeUses,1
|
|
jne artifact_fail_11
|
|
call FinishBuckshotAction3D
|
|
cmp bankroll,10270
|
|
jne artifact_fail_11
|
|
mov bankroll,9900
|
|
mov charmCloverUses,2
|
|
mov bs3d_state,BS_SHOT
|
|
mov bs3d_afterAnim,BS_LOSE
|
|
call FinishBuckshotAction3D
|
|
cmp bankroll,9950
|
|
jne artifact_fail_11
|
|
cmp charmCloverUses,1
|
|
jne artifact_fail_11
|
|
|
|
; Keyboard purchase requires two presses; selection cancels confirmation.
|
|
mov bankroll,10000
|
|
call ResetArtifacts3D
|
|
mov merchantTab3D,1
|
|
mov merchantCharmSelected3D,0
|
|
mov artifactBuyArmed3D,0
|
|
call ClearKeys3D
|
|
mov pressedKeys3D[0Dh],1
|
|
call HandleArtifactShop3D
|
|
cmp bankroll,10000
|
|
jne artifact_fail_12
|
|
cmp artifactBuyArmed3D,1
|
|
jne artifact_fail_12
|
|
mov pressedKeys3D[0Dh],1
|
|
call HandleArtifactShop3D
|
|
cmp bankroll,9100
|
|
jne artifact_fail_12
|
|
cmp charmCloverUses,3
|
|
jne artifact_fail_12
|
|
mov artifactBuyArmed3D,1
|
|
mov pressedKeys3D[28h],1
|
|
call HandleArtifactShop3D
|
|
cmp artifactBuyArmed3D,0
|
|
jne artifact_fail_12
|
|
cmp merchantCharmSelected3D,1
|
|
jne artifact_fail_12
|
|
mov pressedKeys3D[09h],1
|
|
call HandleArtifactShop3D
|
|
cmp merchantTab3D,0
|
|
jne artifact_fail_12
|
|
|
|
; Fresh run clears every relic, stale offer and entry bankroll.
|
|
mov towerHeartUses3D,5
|
|
mov towerSealUses3D,2
|
|
mov towerSealStage3D,6
|
|
mov towerBulletUses3D,2
|
|
mov towerBustUses3D,2
|
|
mov towerEye3D,1
|
|
mov towerPurseUses3D,2
|
|
call ResetTowerRelics3D
|
|
mov eax,towerHeartUses3D
|
|
or eax,towerSealUses3D
|
|
or eax,towerSealStage3D
|
|
or eax,towerBulletUses3D
|
|
or eax,towerBustUses3D
|
|
or eax,towerEye3D
|
|
or eax,towerPurseUses3D
|
|
or eax,towerEntryBank3D
|
|
or eax,towerRewardPending3D
|
|
test eax,eax
|
|
jnz artifact_fail_12
|
|
|
|
; Leaving each native table must end the run, not leak tournament mode to hall.
|
|
mov tournamentMode,1
|
|
mov towerHeartUses3D,1
|
|
mov dice3d_state,0
|
|
call DiceEscape3D
|
|
cmp tournamentMode,0
|
|
jne artifact_fail_13
|
|
cmp towerHeartUses3D,0
|
|
jne artifact_fail_13
|
|
mov tournamentMode,1
|
|
mov towerEye3D,1
|
|
mov roulState,0
|
|
call WheelEscape3D
|
|
cmp tournamentMode,0
|
|
jne artifact_fail_13
|
|
cmp towerEye3D,0
|
|
jne artifact_fail_13
|
|
mov tournamentMode,1
|
|
mov towerBustUses3D,1
|
|
mov engineMode,MODE_BLACKJACK
|
|
mov cbState3D,CB_IDLE
|
|
call LeaveCardTable3D
|
|
cmp tournamentMode,0
|
|
jne artifact_fail_13
|
|
cmp towerBustUses3D,0
|
|
jne artifact_fail_13
|
|
mov tournamentMode,1
|
|
mov towerPurseUses3D,1
|
|
mov bankroll,9950
|
|
mov coin3d_bet,50
|
|
mov coin3d_state,COIN_TOSS
|
|
mov coin3d_choice,1
|
|
mov coin3d_result,1
|
|
call CoinEscape3D
|
|
cmp bankroll,10050
|
|
jne artifact_fail_13
|
|
cmp tournamentMode,0
|
|
jne artifact_fail_13
|
|
cmp towerPurseUses3D,0
|
|
jne artifact_fail_13
|
|
call CoinSettle3D
|
|
cmp bankroll,10050
|
|
jne artifact_fail_13
|
|
|
|
; Situational offers follow the upcoming floor, not the one just cleared.
|
|
mov towerStage,2
|
|
mov towerFloorState,TOWER_STATE_CLEAR
|
|
call PrepareTowerReward3D
|
|
cmp towerRewardOffers3D[4],3
|
|
jne artifact_fail_14
|
|
mov towerStage,3
|
|
call PrepareTowerReward3D
|
|
cmp towerRewardOffers3D[4],2
|
|
jne artifact_fail_14
|
|
mov towerStage,5
|
|
call PrepareTowerReward3D
|
|
cmp towerRewardOffers3D[4],4
|
|
jne artifact_fail_14
|
|
invoke ChooseTowerRelic3D,1
|
|
cmp towerEye3D,1
|
|
jne artifact_fail_14
|
|
call ResetTowerRelics3D
|
|
|
|
; Render shipping UI; captures inspected by the Python runner.
|
|
mov merchantTab3D,1
|
|
mov merchantCharmSelected3D,0
|
|
mov merchantMessage3D,0
|
|
call RenderMerchant3D
|
|
invoke ArtifactTestCapture3D,OFFSET artifactTestBmp3D
|
|
mov towerStage,3
|
|
mov engineMode,MODE_HELL_TOWER
|
|
mov towerFloorState,TOWER_STATE_BRIEFING
|
|
call PrepareTowerReward3D
|
|
call RenderHellTower3D
|
|
invoke ArtifactTestCapture3D,OFFSET artifactTestRewardBmp3D
|
|
xor eax,eax
|
|
ret
|
|
artifact_fail_1:
|
|
mov eax,101
|
|
ret
|
|
artifact_fail_2:
|
|
mov eax,102
|
|
ret
|
|
artifact_fail_3:
|
|
mov eax,103
|
|
ret
|
|
artifact_fail_4:
|
|
mov eax,104
|
|
ret
|
|
artifact_fail_5:
|
|
mov eax,105
|
|
ret
|
|
artifact_fail_6:
|
|
mov eax,106
|
|
ret
|
|
artifact_fail_7:
|
|
mov eax,107
|
|
ret
|
|
artifact_fail_8:
|
|
mov eax,108
|
|
ret
|
|
artifact_fail_9:
|
|
mov eax,109
|
|
ret
|
|
artifact_fail_10:
|
|
mov eax,110
|
|
ret
|
|
artifact_fail_11:
|
|
mov eax,111
|
|
ret
|
|
artifact_fail_12:
|
|
mov eax,112
|
|
ret
|
|
artifact_fail_13:
|
|
mov eax,113
|
|
ret
|
|
artifact_fail_14:
|
|
mov eax,114
|
|
ret
|
|
RunArtifactTest3D ENDP
|