- Add ResolveHellAssetPath to automatically load *-hell variants during tournamentMode with fallback to vanilla assets - Rebuild Hell Tower briefing menu using HellTower_menu background with smooth floor cursor and sliding callout info card - Fix buffer overrun in RenderHellTower3D by packing helltower-menu at native 640x480 resolution with bounds-checked blit - Temporarily disable tower background music per user request - Add asset packing pipeline, verification tools, and automated 10-floor run-through smoke test
599 lines
15 KiB
PHP
599 lines
15 KiB
PHP
; --- hell_tower.inc: Implementation of Hell Tower (10-floor Boss Rush) ---
|
||
.code
|
||
|
||
FreeTowerMinigameAssets PROC
|
||
call FreeBuckshotAssets3D
|
||
call FreeRussianAssets3D
|
||
call FreeCardTableAssets3D
|
||
call FreeNativeCoinArt3D
|
||
call FreeNativeWheelArt3D
|
||
ret
|
||
FreeTowerMinigameAssets ENDP
|
||
|
||
InitHellTower3D PROC
|
||
call FreeTowerMinigameAssets
|
||
mov tournamentMode, 1
|
||
mov towerStage, 1
|
||
mov towerFloorState, TOWER_STATE_BRIEFING
|
||
mov towerAnimClock, 0
|
||
mov towerCalloutAnim, 0
|
||
|
||
mov eax, towerFloorY[0]
|
||
mov towerCursorY, eax
|
||
mov towerCursorTargetY, eax
|
||
|
||
invoke LoadAnimPack3D, OFFSET hellTowerMenuArt3D, OFFSET hellTowerMenuPath3D
|
||
|
||
call StopTowerMusic
|
||
mov engineMode, MODE_HELL_TOWER
|
||
call ClearKeys3D
|
||
ret
|
||
InitHellTower3D ENDP
|
||
|
||
TowerExitToHall PROC
|
||
call StopTowerMusic
|
||
call FreeTowerMinigameAssets
|
||
mov tournamentMode, 0
|
||
mov engineMode, MODE_WALK
|
||
call ClearKeys3D
|
||
ret
|
||
TowerExitToHall ENDP
|
||
|
||
TowerLaunchCurrentFloor PROC uses ebx esi edi
|
||
mov eax, towerStage
|
||
dec eax
|
||
cmp eax, 10
|
||
jae @F
|
||
jmp launch_valid
|
||
@@:
|
||
xor eax, eax
|
||
launch_valid:
|
||
mov edx, towerFloorModes[eax*4]
|
||
mov towerCurrentGame, edx
|
||
mov towerFloorState, TOWER_STATE_IN_GAME
|
||
|
||
; Launch corresponding game
|
||
cmp edx, MODE_COIN
|
||
jne @F
|
||
call InitCoinGame
|
||
mov engineMode, MODE_COIN
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_DICE
|
||
jne @F
|
||
call InitDiceGame
|
||
mov engineMode, MODE_DICE
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_SKULLS
|
||
jne @F
|
||
call InitSkullsGame
|
||
mov engineMode, MODE_SKULLS
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_BLACKJACK
|
||
jne @F
|
||
call InitNativeBlackjack3D
|
||
mov engineMode, MODE_BLACKJACK
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_POKER
|
||
jne @F
|
||
call InitNativePoker3D
|
||
mov engineMode, MODE_POKER
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_RUSSIAN
|
||
jne @F
|
||
call InitRussianGame3D
|
||
mov engineMode, MODE_RUSSIAN
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_ROULETTE
|
||
jne @F
|
||
call InitNativeWheel3D
|
||
mov engineMode, MODE_ROULETTE
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_DURAK
|
||
jne @F
|
||
call InitNativeDurak3D
|
||
mov engineMode, MODE_DURAK
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_SKILL
|
||
jne @F
|
||
call InitSkillGame
|
||
mov engineMode, MODE_SKILL
|
||
jmp launch_done
|
||
@@:
|
||
cmp edx, MODE_BUCKSHOT
|
||
jne launch_done
|
||
call InitBuckshot3D
|
||
mov engineMode, MODE_BUCKSHOT
|
||
|
||
launch_done:
|
||
call ClearKeys3D
|
||
ret
|
||
TowerLaunchCurrentFloor ENDP
|
||
|
||
TowerReportRoundOutcome PROC uses eax outcome:DWORD
|
||
cmp tournamentMode, 1
|
||
jne report_exit
|
||
mov eax, outcome
|
||
cmp eax, 1
|
||
jne report_loss
|
||
|
||
; --- Victory ---
|
||
cmp towerStage, 10
|
||
jae report_champion
|
||
|
||
mov towerFloorState, TOWER_STATE_CLEAR
|
||
mov towerCalloutAnim, 0
|
||
mov eax, towerStage
|
||
dec eax
|
||
cmp eax, 10
|
||
jae @F
|
||
mov edx, towerFloorY[eax*4]
|
||
mov towerCursorTargetY, edx
|
||
@@:
|
||
invoke PlayCasinoSound3D, OFFSET soundRoundWin, 0, 1
|
||
mov engineMode, MODE_HELL_TOWER
|
||
call ClearKeys3D
|
||
ret
|
||
|
||
report_champion:
|
||
mov towerFloorState, TOWER_STATE_CHAMPION
|
||
mov eax, towerPrizeAmount
|
||
add bankroll, eax
|
||
invoke PlayCasinoSound3D, OFFSET soundRoundWin, 0, 1
|
||
call SaveProgress3D
|
||
mov engineMode, MODE_HELL_TOWER
|
||
call ClearKeys3D
|
||
ret
|
||
|
||
report_loss:
|
||
invoke TriggerCharmLossProtection, 0
|
||
test eax, eax
|
||
jz @F
|
||
mov towerFloorState, TOWER_STATE_BRIEFING
|
||
invoke CopyString3D, OFFSET msgCharmTowerSave, OFFSET flashMessage
|
||
mov flashTimer, 140
|
||
mov engineMode, MODE_HELL_TOWER
|
||
call ClearKeys3D
|
||
ret
|
||
@@:
|
||
mov towerFloorState, TOWER_STATE_DEFEAT
|
||
call StopTowerMusic
|
||
invoke PlayCasinoSound3D, OFFSET soundRoundLose, 0, 1
|
||
mov engineMode, MODE_HELL_TOWER
|
||
call ClearKeys3D
|
||
ret
|
||
|
||
report_exit:
|
||
ret
|
||
TowerReportRoundOutcome ENDP
|
||
|
||
HandleHellTowerInput PROC
|
||
mov eax, towerFloorState
|
||
cmp eax, TOWER_STATE_BRIEFING
|
||
je input_briefing
|
||
cmp eax, TOWER_STATE_CLEAR
|
||
je input_clear
|
||
cmp eax, TOWER_STATE_DEFEAT
|
||
je input_defeat
|
||
cmp eax, TOWER_STATE_CHAMPION
|
||
je input_champion
|
||
ret
|
||
|
||
input_briefing:
|
||
invoke Pressed3D, 1Bh ; ESC
|
||
test eax, eax
|
||
jz @F
|
||
call TowerExitToHall
|
||
ret
|
||
@@:
|
||
invoke Pressed3D, 20h ; SPACE
|
||
test eax, eax
|
||
jnz start_stage
|
||
invoke Pressed3D, 0Dh ; ENTER
|
||
test eax, eax
|
||
jnz start_stage
|
||
ret
|
||
start_stage:
|
||
call TowerLaunchCurrentFloor
|
||
ret
|
||
|
||
input_clear:
|
||
invoke Pressed3D, 1Bh ; ESC
|
||
test eax, eax
|
||
jz @F
|
||
call TowerExitToHall
|
||
ret
|
||
@@:
|
||
invoke Pressed3D, 20h ; SPACE
|
||
test eax, eax
|
||
jnz advance_stage
|
||
invoke Pressed3D, 0Dh ; ENTER
|
||
test eax, eax
|
||
jnz advance_stage
|
||
ret
|
||
advance_stage:
|
||
inc towerStage
|
||
mov towerFloorState, TOWER_STATE_BRIEFING
|
||
mov towerCalloutAnim, 0
|
||
mov eax, towerStage
|
||
dec eax
|
||
cmp eax, 10
|
||
jae @F
|
||
mov edx, towerFloorY[eax*4]
|
||
mov towerCursorTargetY, edx
|
||
@@:
|
||
call ClearKeys3D
|
||
ret
|
||
|
||
input_defeat:
|
||
invoke Pressed3D, 1Bh ; ESC
|
||
test eax, eax
|
||
jnz exit_to_hall
|
||
invoke Pressed3D, 20h ; SPACE
|
||
test eax, eax
|
||
jnz exit_to_hall
|
||
invoke Pressed3D, 0Dh ; ENTER
|
||
test eax, eax
|
||
jnz exit_to_hall
|
||
ret
|
||
|
||
input_champion:
|
||
invoke Pressed3D, 1Bh ; ESC
|
||
test eax, eax
|
||
jnz exit_to_hall
|
||
invoke Pressed3D, 20h ; SPACE
|
||
test eax, eax
|
||
jnz exit_to_hall
|
||
invoke Pressed3D, 0Dh ; ENTER
|
||
test eax, eax
|
||
jnz exit_to_hall
|
||
ret
|
||
|
||
exit_to_hall:
|
||
call TowerExitToHall
|
||
ret
|
||
HandleHellTowerInput ENDP
|
||
|
||
RenderHellTower3D PROC uses ebx esi edi
|
||
local cardX:DWORD, cardY:DWORD, cardW:DWORD, cardH:DWORD
|
||
local currY:DWORD, tgtY:DWORD
|
||
|
||
; 1. Background: draw HellTower menu art if loaded, else fallback to maroon
|
||
cmp hellTowerMenuArt3D.pixels, 0
|
||
je fallback_clear
|
||
|
||
cmp hellTowerMenuArt3D.imageWidth, SCREEN_W
|
||
jne fallback_sprite
|
||
cmp hellTowerMenuArt3D.height, SCREEN_H
|
||
jne fallback_sprite
|
||
|
||
mov esi, hellTowerMenuArt3D.pixels
|
||
lea edi, screenBuffer
|
||
mov ecx, SCREEN_SIZE
|
||
rep movsd
|
||
jmp bg_done
|
||
|
||
fallback_sprite:
|
||
invoke DrawAnimSprite3D, OFFSET hellTowerMenuArt3D, 0, 0, 0, SCREEN_W, SCREEN_H
|
||
jmp bg_done
|
||
|
||
fallback_clear:
|
||
lea edi, screenBuffer
|
||
mov ecx, SCREEN_SIZE
|
||
mov eax, 00100202h
|
||
rep stosd
|
||
|
||
bg_done:
|
||
; 2. Update towerAnimClock
|
||
inc towerAnimClock
|
||
|
||
; 3. Smooth cursor Y movement towards towerCursorTargetY (exponential damping)
|
||
mov eax, towerCursorTargetY
|
||
cmp eax, towerCursorY
|
||
je cursor_update_done
|
||
jg cursor_move_down
|
||
|
||
; Moving UP towards tower peak (target < current)
|
||
mov ecx, towerCursorY
|
||
sub ecx, eax
|
||
shr ecx, 2
|
||
inc ecx
|
||
sub towerCursorY, ecx
|
||
mov eax, towerCursorTargetY
|
||
cmp towerCursorY, eax
|
||
jge cursor_update_done
|
||
mov towerCursorY, eax
|
||
jmp cursor_update_done
|
||
|
||
cursor_move_down:
|
||
; Moving DOWN towards tower base (target > current)
|
||
mov ecx, eax
|
||
sub ecx, towerCursorY
|
||
shr ecx, 2
|
||
inc ecx
|
||
add towerCursorY, ecx
|
||
mov eax, towerCursorTargetY
|
||
cmp towerCursorY, eax
|
||
jle cursor_update_done
|
||
mov towerCursorY, eax
|
||
|
||
cursor_update_done:
|
||
; 4. Update sliding callout animation (0 to 100%)
|
||
cmp towerCalloutAnim, 100
|
||
jge callout_step_done
|
||
add towerCalloutAnim, 10
|
||
cmp towerCalloutAnim, 100
|
||
jle callout_step_done
|
||
mov towerCalloutAnim, 100
|
||
callout_step_done:
|
||
|
||
; 5. Calculate sliding callout card dimensions occupying free left space:
|
||
; Max width = 206, fixed right bound = 214, top = 20, height = 208
|
||
mov cardH, 208
|
||
mov cardY, 20
|
||
|
||
; cardW = 206 * towerCalloutAnim / 100
|
||
mov eax, 206
|
||
imul eax, towerCalloutAnim
|
||
xor edx, edx
|
||
mov ecx, 100
|
||
div ecx
|
||
cmp eax, 10
|
||
jae @F
|
||
mov eax, 10
|
||
@@:
|
||
mov cardW, eax
|
||
|
||
; cardX = 214 - cardW
|
||
mov edx, 214
|
||
sub edx, cardW
|
||
mov cardX, edx
|
||
|
||
; 6. Draw glowing cursor & indicator on the tower (Right side)
|
||
; Indicator line from card right edge (214) to cursor on tower (X=224)
|
||
mov eax, towerCursorY
|
||
invoke DrawBox, 214, eax, 10, 2, 00FF4444h
|
||
|
||
; Cursor chevron/diamond at X=224
|
||
mov eax, towerCursorY
|
||
sub eax, 3
|
||
invoke DrawBoxBorder, 224, eax, 7, 7, 00FF3300h, 00FFDD44h, 1
|
||
|
||
; Pulsing flame pip on the tower floor at X=234
|
||
mov eax, towerAnimClock
|
||
and eax, 8
|
||
jz pip_bright
|
||
mov edx, 00EE3300h
|
||
jmp pip_draw
|
||
pip_bright:
|
||
mov edx, 00FFAA22h
|
||
pip_draw:
|
||
mov eax, towerCursorY
|
||
sub eax, 2
|
||
invoke DrawBox, 234, eax, 5, 5, edx
|
||
|
||
; 7. Draw Callout Box (outer and inner border with deep infernal tint)
|
||
invoke DrawBoxBorder, cardX, cardY, cardW, cardH, 00120202h, 00CC2222h, 2
|
||
mov eax, cardX
|
||
add eax, 2
|
||
mov edx, cardY
|
||
add edx, 2
|
||
mov ecx, cardW
|
||
sub ecx, 4
|
||
mov ebx, cardH
|
||
sub ebx, 4
|
||
invoke DrawBoxBorder, eax, edx, ecx, ebx, 000C0202h, 00551111h, 1
|
||
|
||
; Only render full content once card has slid out sufficiently (cardW >= 120)
|
||
cmp cardW, 120
|
||
jb render_tower_done
|
||
|
||
; Header Banner: "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 3D - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>"
|
||
mov eax, cardX
|
||
add eax, 6
|
||
mov ecx, cardW
|
||
sub ecx, 12
|
||
invoke DrawBoxBorder, eax, 26, ecx, 18, 00220404h, 00FF4444h, 1
|
||
mov eax, cardX
|
||
add eax, 12
|
||
invoke DrawString8x8, eax, 31, OFFSET towerHeader, 00FFDD66h
|
||
|
||
; Floor indicator: "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> N / 10"
|
||
mov eax, cardX
|
||
add eax, 10
|
||
invoke DrawString8x8, eax, 50, OFFSET towerFloorPrefix, 00FF6666h
|
||
invoke FormatNumber, towerStage, OFFSET hudNumBuffer
|
||
mov eax, cardX
|
||
add eax, 50
|
||
invoke DrawString8x8, eax, 50, OFFSET hudNumBuffer, 00FFFFFFh
|
||
mov eax, cardX
|
||
add eax, 58
|
||
invoke DrawString8x8, eax, 50, OFFSET towerOfTen, 00AAAAAAh
|
||
|
||
; Specific state content inside the card
|
||
mov eax, towerFloorState
|
||
cmp eax, TOWER_STATE_BRIEFING
|
||
je render_card_briefing
|
||
cmp eax, TOWER_STATE_CLEAR
|
||
je render_card_clear
|
||
cmp eax, TOWER_STATE_DEFEAT
|
||
je render_card_defeat
|
||
cmp eax, TOWER_STATE_CHAMPION
|
||
je render_card_champion
|
||
jmp render_tower_done
|
||
|
||
render_card_briefing:
|
||
; Boss & Game sub-card
|
||
mov eax, cardX
|
||
add eax, 6
|
||
mov ecx, cardW
|
||
sub ecx, 12
|
||
invoke DrawBoxBorder, eax, 64, ecx, 62, 001E0404h, 00881818h, 1
|
||
|
||
mov eax, cardX
|
||
add eax, 10
|
||
invoke DrawString8x8, eax, 70, OFFSET towerEnemyLabel, 00FFAA55h
|
||
mov eax, towerStage
|
||
dec eax
|
||
mov edx, towerBossNames[eax*4]
|
||
mov eax, cardX
|
||
add eax, 56
|
||
invoke DrawString8x8, eax, 70, edx, 00FFFFFFh
|
||
|
||
mov eax, cardX
|
||
add eax, 10
|
||
invoke DrawString8x8, eax, 84, OFFSET towerGameLabel, 00FFAA55h
|
||
mov eax, towerStage
|
||
dec eax
|
||
mov edx, towerGameNames[eax*4]
|
||
mov eax, cardX
|
||
add eax, 56
|
||
invoke DrawString8x8, eax, 84, edx, 00FFDD66h
|
||
|
||
mov eax, towerStage
|
||
dec eax
|
||
mov edx, towerDescriptions[eax*4]
|
||
mov eax, cardX
|
||
add eax, 10
|
||
invoke DrawString8x8, eax, 100, edx, 00CCCCCCh
|
||
|
||
; Rules & warning
|
||
mov eax, cardX
|
||
add eax, 10
|
||
invoke DrawString8x8, eax, 134, OFFSET towerRuleText, 00FFAA33h
|
||
mov eax, cardX
|
||
add eax, 10
|
||
invoke DrawString8x8, eax, 148, OFFSET towerLossWarning, 00EE4444h
|
||
|
||
; Bankroll & Prize
|
||
mov eax, cardX
|
||
add eax, 10
|
||
invoke DrawString8x8, eax, 164, OFFSET textBank3D, 00AAAAAAh
|
||
invoke FormatNumber, bankroll, OFFSET hudNumBuffer
|
||
mov eax, cardX
|
||
add eax, 56
|
||
invoke DrawString8x8, eax, 164, OFFSET hudNumBuffer, 0066FF66h
|
||
|
||
mov eax, cardX
|
||
add eax, 110
|
||
invoke DrawString8x8, eax, 164, OFFSET towerPrizePrompt, 00FFAA55h
|
||
invoke FormatNumber, towerPrizeAmount, OFFSET hudNumBuffer
|
||
mov eax, cardX
|
||
add eax, 154
|
||
invoke DrawString8x8, eax, 164, OFFSET hudNumBuffer, 00FFDD44h
|
||
|
||
; Action Buttons (Start & Escape)
|
||
mov eax, cardX
|
||
add eax, 8
|
||
invoke DrawBoxBorder, eax, 186, 92, 22, 003D0C0Ch, 00FF4444h, 1
|
||
mov eax, cardX
|
||
add eax, 14
|
||
invoke DrawString8x8, eax, 193, OFFSET towerStartBtn, 00FFFFFFh
|
||
|
||
mov eax, cardX
|
||
add eax, 106
|
||
invoke DrawBoxBorder, eax, 186, 92, 22, 00220606h, 00882222h, 1
|
||
mov eax, cardX
|
||
add eax, 114
|
||
invoke DrawString8x8, eax, 193, OFFSET towerEscBtn, 00AAAAAAh
|
||
jmp render_tower_done
|
||
|
||
render_card_clear:
|
||
mov eax, cardX
|
||
add eax, 8
|
||
mov ecx, cardW
|
||
sub ecx, 16
|
||
invoke DrawBoxBorder, eax, 74, ecx, 60, 00082208h, 0033CC33h, 2
|
||
mov eax, cardX
|
||
add eax, 14
|
||
invoke DrawString8x8, eax, 86, OFFSET towerClearTitle, 0066FF66h
|
||
mov eax, cardX
|
||
add eax, 14
|
||
invoke DrawString8x8, eax, 108, OFFSET towerClearDesc, 00FFFFFFh
|
||
|
||
mov eax, cardX
|
||
add eax, 18
|
||
mov ecx, cardW
|
||
sub ecx, 36
|
||
invoke DrawBoxBorder, eax, 186, ecx, 22, 00114411h, 0044FF44h, 1
|
||
mov eax, cardX
|
||
add eax, 42
|
||
invoke DrawString8x8, eax, 193, OFFSET towerNextBtn, 00FFFFFFh
|
||
jmp render_tower_done
|
||
|
||
render_card_defeat:
|
||
mov eax, cardX
|
||
add eax, 8
|
||
mov ecx, cardW
|
||
sub ecx, 16
|
||
invoke DrawBoxBorder, eax, 74, ecx, 60, 002A0404h, 00FF2222h, 2
|
||
mov eax, cardX
|
||
add eax, 14
|
||
invoke DrawString8x8, eax, 86, OFFSET towerDefeatTitle, 00FF4444h
|
||
mov eax, cardX
|
||
add eax, 14
|
||
invoke DrawString8x8, eax, 108, OFFSET towerDefeatDesc, 00FFFFFFh
|
||
|
||
mov eax, cardX
|
||
add eax, 16
|
||
mov ecx, cardW
|
||
sub ecx, 32
|
||
invoke DrawBoxBorder, eax, 186, ecx, 22, 00441010h, 00FF4444h, 1
|
||
mov eax, cardX
|
||
add eax, 22
|
||
invoke DrawString8x8, eax, 193, OFFSET towerReturnBtn, 00FFFFFFh
|
||
jmp render_tower_done
|
||
|
||
render_card_champion:
|
||
mov eax, cardX
|
||
add eax, 8
|
||
mov ecx, cardW
|
||
sub ecx, 16
|
||
invoke DrawBoxBorder, eax, 68, ecx, 72, 00332200h, 00FFD700h, 2
|
||
mov eax, cardX
|
||
add eax, 12
|
||
invoke DrawString8x8, eax, 78, OFFSET towerChampTitle, 00FFE066h
|
||
mov eax, cardX
|
||
add eax, 16
|
||
invoke DrawString8x8, eax, 96, OFFSET towerChampSubtitle, 00FFFFFFh
|
||
mov eax, cardX
|
||
add eax, 14
|
||
invoke DrawString8x8, eax, 116, OFFSET towerPrizeText, 0066FF66h
|
||
|
||
mov eax, cardX
|
||
add eax, 16
|
||
mov ecx, cardW
|
||
sub ecx, 32
|
||
invoke DrawBoxBorder, eax, 186, ecx, 22, 00443300h, 00FFCC00h, 1
|
||
mov eax, cardX
|
||
add eax, 22
|
||
invoke DrawString8x8, eax, 193, OFFSET towerReturnBtn, 00FFFFFFh
|
||
|
||
render_tower_done:
|
||
ret
|
||
RenderHellTower3D ENDP
|
||
|
||
RenderTowerInGameBadge PROC
|
||
cmp tournamentMode, 1
|
||
jne badge_done
|
||
cmp engineMode, MODE_HELL_TOWER
|
||
je badge_done
|
||
cmp engineMode, MODE_WALK
|
||
je badge_done
|
||
|
||
invoke DrawBoxBorder, 4, 4, 96, 16, 00220404h, 00FF3333h, 1
|
||
invoke DrawString8x8, 8, 8, OFFSET towerHudBadge, 00FFAA55h
|
||
invoke FormatNumber, towerStage, OFFSET hudNumBuffer
|
||
invoke DrawString8x8, 64, 8, OFFSET hudNumBuffer, 00FFFFFFh
|
||
invoke DrawString8x8, 72, 8, OFFSET towerHudOfTen, 00AAAAAAh
|
||
|
||
badge_done:
|
||
ret
|
||
RenderTowerInGameBadge ENDP
|