fix(hell_tower): calibrate menu to 6 floors, fix UI text overflow, and remove casino prefix

- Align tower progression and cursor coordinates strictly to the 6 floors: Coin, Dice, Cards, Russian Roulette, Roulette, Buckshot
- Recalibrate tower cursor Y positions for each tier (205, 174, 143, 112, 81, 50)
- Reformat UI text, labels, and action buttons to strictly fit within card and sub-card boundaries without clipping
- Remove redundant 'КАЗИНО 3D' prefix from header banner, keeping bold centered 'АДСКАЯ БАШНЯ'
- Rebuild shipping binaries and verify complete 6-floor test suite
This commit is contained in:
Игорь Федосеев 2026-10-26 20:43:19 +03:00
parent eef2039c7e
commit 3c8465bdad
8 changed files with 791 additions and 768 deletions

Binary file not shown.

View file

@ -1935,7 +1935,7 @@ stage_test_loop:
invoke TowerReportRoundOutcome, 1 invoke TowerReportRoundOutcome, 1
; 4. Check outcome state ; 4. Check outcome state
cmp stageLoop, 10 cmp stageLoop, 6
jae stage_check_champ jae stage_check_champ
cmp towerFloorState, TOWER_STATE_CLEAR cmp towerFloorState, TOWER_STATE_CLEAR

BIN
poker.exe

Binary file not shown.

BIN
poker.obj

Binary file not shown.

View file

@ -1,4 +1,4 @@
; --- hell_tower.inc: Implementation of Hell Tower (10-floor Boss Rush) --- ; --- hell_tower.inc: Implementation of Hell Tower (6-floor Boss Rush) ---
.code .code
FreeTowerMinigameAssets PROC FreeTowerMinigameAssets PROC
@ -42,7 +42,7 @@ TowerExitToHall ENDP
TowerLaunchCurrentFloor PROC uses ebx esi edi TowerLaunchCurrentFloor PROC uses ebx esi edi
mov eax, towerStage mov eax, towerStage
dec eax dec eax
cmp eax, 10 cmp eax, 6
jae @F jae @F
jmp launch_valid jmp launch_valid
@@: @@:
@ -64,24 +64,12 @@ launch_valid:
call InitDiceGame call InitDiceGame
mov engineMode, MODE_DICE mov engineMode, MODE_DICE
jmp launch_done jmp launch_done
@@:
cmp edx, MODE_SKULLS
jne @F
call InitSkullsGame
mov engineMode, MODE_SKULLS
jmp launch_done
@@: @@:
cmp edx, MODE_BLACKJACK cmp edx, MODE_BLACKJACK
jne @F jne @F
call InitNativeBlackjack3D call InitNativeBlackjack3D
mov engineMode, MODE_BLACKJACK mov engineMode, MODE_BLACKJACK
jmp launch_done jmp launch_done
@@:
cmp edx, MODE_POKER
jne @F
call InitNativePoker3D
mov engineMode, MODE_POKER
jmp launch_done
@@: @@:
cmp edx, MODE_RUSSIAN cmp edx, MODE_RUSSIAN
jne @F jne @F
@ -94,18 +82,6 @@ launch_valid:
call InitNativeWheel3D call InitNativeWheel3D
mov engineMode, MODE_ROULETTE mov engineMode, MODE_ROULETTE
jmp launch_done 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 cmp edx, MODE_BUCKSHOT
jne launch_done jne launch_done
@ -125,14 +101,14 @@ TowerReportRoundOutcome PROC uses eax outcome:DWORD
jne report_loss jne report_loss
; --- Victory --- ; --- Victory ---
cmp towerStage, 10 cmp towerStage, 6
jae report_champion jae report_champion
mov towerFloorState, TOWER_STATE_CLEAR mov towerFloorState, TOWER_STATE_CLEAR
mov towerCalloutAnim, 0 mov towerCalloutAnim, 0
mov eax, towerStage mov eax, towerStage
dec eax dec eax
cmp eax, 10 cmp eax, 6
jae @F jae @F
mov edx, towerFloorY[eax*4] mov edx, towerFloorY[eax*4]
mov towerCursorTargetY, edx mov towerCursorTargetY, edx
@ -224,7 +200,7 @@ advance_stage:
mov towerCalloutAnim, 0 mov towerCalloutAnim, 0
mov eax, towerStage mov eax, towerStage
dec eax dec eax
cmp eax, 10 cmp eax, 6
jae @F jae @F
mov edx, towerFloorY[eax*4] mov edx, towerFloorY[eax*4]
mov towerCursorTargetY, edx mov towerCursorTargetY, edx
@ -335,9 +311,9 @@ cursor_update_done:
callout_step_done: callout_step_done:
; 5. Calculate sliding callout card dimensions occupying free left space: ; 5. Calculate sliding callout card dimensions occupying free left space:
; Max width = 206, fixed right bound = 214, top = 20, height = 208 ; Max width = 206, fixed right bound = 214, top = 16, height = 208
mov cardH, 208 mov cardH, 208
mov cardY, 20 mov cardY, 16
; cardW = 206 * towerCalloutAnim / 100 ; cardW = 206 * towerCalloutAnim / 100
mov eax, 206 mov eax, 206
@ -395,27 +371,35 @@ pip_draw:
cmp cardW, 120 cmp cardW, 120
jb render_tower_done jb render_tower_done
; Header Banner: "ÊÀÇÈÍÎ 3D - ÀÄÑÊÀß ÁÀØÍß" ; Header Banner: "ÀÄÑÊÀß ÁÀØÍß" (Centered, NO "ÊÀÇÈÍÎ 3D")
mov eax, cardX mov eax, cardX
add eax, 6 add eax, 6
mov ecx, cardW mov ecx, cardW
sub ecx, 12 sub ecx, 12
invoke DrawBoxBorder, eax, 26, ecx, 18, 00220404h, 00FF4444h, 1 invoke DrawBoxBorder, eax, 22, ecx, 18, 00220404h, 00FF4444h, 1
mov eax, cardX
add eax, 55
invoke DrawString8x8, eax, 27, OFFSET towerHeader, 00FFDD66h
; Floor indicator: "ÝÒÀÆ N / 6" and Prize prompt
mov eax, cardX mov eax, cardX
add eax, 12 add eax, 12
invoke DrawString8x8, eax, 31, OFFSET towerHeader, 00FFDD66h invoke DrawString8x8, eax, 46, OFFSET towerFloorPrefix, 00FF6666h
; Floor indicator: "ÝÒÀÆ N / 10"
mov eax, cardX
add eax, 10
invoke DrawString8x8, eax, 50, OFFSET towerFloorPrefix, 00FF6666h
invoke FormatNumber, towerStage, OFFSET hudNumBuffer invoke FormatNumber, towerStage, OFFSET hudNumBuffer
mov eax, cardX mov eax, cardX
add eax, 50 add eax, 52
invoke DrawString8x8, eax, 50, OFFSET hudNumBuffer, 00FFFFFFh invoke DrawString8x8, eax, 46, OFFSET hudNumBuffer, 00FFFFFFh
mov eax, cardX mov eax, cardX
add eax, 58 add eax, 60
invoke DrawString8x8, eax, 50, OFFSET towerOfTen, 00AAAAAAh invoke DrawString8x8, eax, 46, OFFSET towerOfTen, 00AAAAAAh
mov eax, cardX
add eax, 105
invoke DrawString8x8, eax, 46, OFFSET towerPrizePrompt, 00FFAA55h
invoke FormatNumber, towerPrizeAmount, OFFSET hudNumBuffer
mov eax, cardX
add eax, 149
invoke DrawString8x8, eax, 46, OFFSET hudNumBuffer, 00FFDD44h
; Specific state content inside the card ; Specific state content inside the card
mov eax, towerFloorState mov eax, towerFloorState
@ -430,151 +414,142 @@ pip_draw:
jmp render_tower_done jmp render_tower_done
render_card_briefing: render_card_briefing:
; Boss & Game sub-card ; Boss & Game sub-card (Y = 60 to 130)
mov eax, cardX mov eax, cardX
add eax, 6 add eax, 6
mov ecx, cardW mov ecx, cardW
sub ecx, 12 sub ecx, 12
invoke DrawBoxBorder, eax, 64, ecx, 62, 001E0404h, 00881818h, 1 invoke DrawBoxBorder, eax, 60, ecx, 70, 001E0404h, 00881818h, 1
mov eax, cardX mov eax, cardX
add eax, 10 add eax, 12
invoke DrawString8x8, eax, 70, OFFSET towerEnemyLabel, 00FFAA55h invoke DrawString8x8, eax, 66, OFFSET towerEnemyLabel, 00FFAA55h
mov eax, towerStage mov eax, towerStage
dec eax dec eax
mov edx, towerBossNames[eax*4] mov edx, towerBossNames[eax*4]
mov eax, cardX mov eax, cardX
add eax, 56 add eax, 60
invoke DrawString8x8, eax, 70, edx, 00FFFFFFh invoke DrawString8x8, eax, 66, edx, 00FFFFFFh
mov eax, cardX mov eax, cardX
add eax, 10 add eax, 12
invoke DrawString8x8, eax, 84, OFFSET towerGameLabel, 00FFAA55h invoke DrawString8x8, eax, 80, OFFSET towerGameLabel, 00FFAA55h
mov eax, towerStage mov eax, towerStage
dec eax dec eax
mov edx, towerGameNames[eax*4] mov edx, towerGameNames[eax*4]
mov eax, cardX mov eax, cardX
add eax, 56 add eax, 60
invoke DrawString8x8, eax, 84, edx, 00FFDD66h invoke DrawString8x8, eax, 80, edx, 00FFDD66h
mov eax, towerStage mov eax, towerStage
dec eax dec eax
mov edx, towerDescriptions[eax*4] mov edx, towerDescriptions[eax*4]
mov eax, cardX mov eax, cardX
add eax, 10 add eax, 12
invoke DrawString8x8, eax, 100, edx, 00CCCCCCh invoke DrawString8x8, eax, 98, edx, 00CCCCCCh
; Rules & warning ; Rules & warning box (Y = 134 to 166)
mov eax, cardX mov eax, cardX
add eax, 10 add eax, 6
invoke DrawString8x8, eax, 134, OFFSET towerRuleText, 00FFAA33h mov ecx, cardW
sub ecx, 12
invoke DrawBoxBorder, eax, 134, ecx, 32, 00180303h, 00661111h, 1
mov eax, cardX mov eax, cardX
add eax, 10 add eax, 12
invoke DrawString8x8, eax, 148, OFFSET towerLossWarning, 00EE4444h invoke DrawString8x8, eax, 139, OFFSET towerRuleText, 00FFAA33h
mov eax, cardX
add eax, 12
invoke DrawString8x8, eax, 151, OFFSET towerLossWarning, 00FF5555h
; Bankroll & Prize ; Bankroll & Prize (Y = 172)
mov eax, cardX mov eax, cardX
add eax, 10 add eax, 12
invoke DrawString8x8, eax, 164, OFFSET textBank3D, 00AAAAAAh invoke DrawString8x8, eax, 172, OFFSET textBank3D, 00AAAAAAh
invoke FormatNumber, bankroll, OFFSET hudNumBuffer invoke FormatNumber, bankroll, OFFSET hudNumBuffer
mov eax, cardX mov eax, cardX
add eax, 56 add eax, 60
invoke DrawString8x8, eax, 164, OFFSET hudNumBuffer, 0066FF66h invoke DrawString8x8, eax, 172, OFFSET hudNumBuffer, 0066FF66h
; Action Buttons (Y = 188 to 212)
mov eax, cardX
add eax, 6
invoke DrawBoxBorder, eax, 188, 93, 24, 003D0C0Ch, 00FF4444h, 1
mov eax, cardX
add eax, 12
invoke DrawString8x8, eax, 196, OFFSET towerStartBtn, 00FFFFFFh
mov eax, cardX mov eax, cardX
add eax, 110 add eax, 107
invoke DrawString8x8, eax, 164, OFFSET towerPrizePrompt, 00FFAA55h invoke DrawBoxBorder, eax, 188, 93, 24, 00220606h, 00882222h, 1
invoke FormatNumber, towerPrizeAmount, OFFSET hudNumBuffer
mov eax, cardX mov eax, cardX
add eax, 154 add eax, 113
invoke DrawString8x8, eax, 164, OFFSET hudNumBuffer, 00FFDD44h invoke DrawString8x8, eax, 196, OFFSET towerEscBtn, 00AAAAAAh
; 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 jmp render_tower_done
render_card_clear: render_card_clear:
mov eax, cardX mov eax, cardX
add eax, 8 add eax, 6
mov ecx, cardW mov ecx, cardW
sub ecx, 16 sub ecx, 12
invoke DrawBoxBorder, eax, 74, ecx, 60, 00082208h, 0033CC33h, 2 invoke DrawBoxBorder, eax, 62, ecx, 80, 00082208h, 0033CC33h, 2
mov eax, cardX mov eax, cardX
add eax, 14 add eax, 51
invoke DrawString8x8, eax, 86, OFFSET towerClearTitle, 0066FF66h invoke DrawString8x8, eax, 76, OFFSET towerClearTitle, 0066FF66h
mov eax, cardX mov eax, cardX
add eax, 14 add eax, 47
invoke DrawString8x8, eax, 108, OFFSET towerClearDesc, 00FFFFFFh invoke DrawString8x8, eax, 96, OFFSET towerClearDesc, 00FFFFFFh
mov eax, cardX mov eax, cardX
add eax, 18 add eax, 18
mov ecx, cardW invoke DrawBoxBorder, eax, 176, 170, 26, 00114411h, 0044FF44h, 1
sub ecx, 36
invoke DrawBoxBorder, eax, 186, ecx, 22, 00114411h, 0044FF44h, 1
mov eax, cardX mov eax, cardX
add eax, 42 add eax, 47
invoke DrawString8x8, eax, 193, OFFSET towerNextBtn, 00FFFFFFh invoke DrawString8x8, eax, 184, OFFSET towerNextBtn, 00FFFFFFh
jmp render_tower_done jmp render_tower_done
render_card_defeat: render_card_defeat:
mov eax, cardX mov eax, cardX
add eax, 8 add eax, 6
mov ecx, cardW mov ecx, cardW
sub ecx, 16 sub ecx, 12
invoke DrawBoxBorder, eax, 74, ecx, 60, 002A0404h, 00FF2222h, 2 invoke DrawBoxBorder, eax, 62, ecx, 80, 002A0404h, 00FF2222h, 2
mov eax, cardX mov eax, cardX
add eax, 14 add eax, 31
invoke DrawString8x8, eax, 86, OFFSET towerDefeatTitle, 00FF4444h invoke DrawString8x8, eax, 76, OFFSET towerDefeatTitle, 00FF4444h
mov eax, cardX mov eax, cardX
add eax, 14 add eax, 27
invoke DrawString8x8, eax, 108, OFFSET towerDefeatDesc, 00FFFFFFh invoke DrawString8x8, eax, 98, OFFSET towerDefeatDesc, 00FFFFFFh
mov eax, cardX mov eax, cardX
add eax, 16 add eax, 18
mov ecx, cardW invoke DrawBoxBorder, eax, 176, 170, 26, 00441010h, 00FF4444h, 1
sub ecx, 32
invoke DrawBoxBorder, eax, 186, ecx, 22, 00441010h, 00FF4444h, 1
mov eax, cardX mov eax, cardX
add eax, 22 add eax, 31
invoke DrawString8x8, eax, 193, OFFSET towerReturnBtn, 00FFFFFFh invoke DrawString8x8, eax, 184, OFFSET towerReturnBtn, 00FFFFFFh
jmp render_tower_done jmp render_tower_done
render_card_champion: render_card_champion:
mov eax, cardX mov eax, cardX
add eax, 8 add eax, 6
mov ecx, cardW mov ecx, cardW
sub ecx, 16 sub ecx, 12
invoke DrawBoxBorder, eax, 68, ecx, 72, 00332200h, 00FFD700h, 2 invoke DrawBoxBorder, eax, 58, ecx, 92, 00332200h, 00FFD700h, 2
mov eax, cardX mov eax, cardX
add eax, 12 add eax, 43
invoke DrawString8x8, eax, 78, OFFSET towerChampTitle, 00FFE066h invoke DrawString8x8, eax, 70, OFFSET towerChampTitle, 00FFE066h
mov eax, cardX mov eax, cardX
add eax, 16 add eax, 27
invoke DrawString8x8, eax, 96, OFFSET towerChampSubtitle, 00FFFFFFh invoke DrawString8x8, eax, 90, OFFSET towerChampSubtitle, 00FFFFFFh
mov eax, cardX mov eax, cardX
add eax, 14 add eax, 19
invoke DrawString8x8, eax, 116, OFFSET towerPrizeText, 0066FF66h invoke DrawString8x8, eax, 114, OFFSET towerPrizeText, 0066FF66h
mov eax, cardX mov eax, cardX
add eax, 16 add eax, 18
mov ecx, cardW invoke DrawBoxBorder, eax, 176, 170, 26, 00443300h, 00FFCC00h, 1
sub ecx, 32
invoke DrawBoxBorder, eax, 186, ecx, 22, 00443300h, 00FFCC00h, 1
mov eax, cardX mov eax, cardX
add eax, 22 add eax, 31
invoke DrawString8x8, eax, 193, OFFSET towerReturnBtn, 00FFFFFFh invoke DrawString8x8, eax, 184, OFFSET towerReturnBtn, 00FFFFFFh
render_tower_done: render_tower_done:
ret ret
@ -588,7 +563,7 @@ RenderTowerInGameBadge PROC
cmp engineMode, MODE_WALK cmp engineMode, MODE_WALK
je badge_done je badge_done
invoke DrawBoxBorder, 4, 4, 96, 16, 00220404h, 00FF3333h, 1 invoke DrawBoxBorder, 4, 4, 88, 16, 00220404h, 00FF3333h, 1
invoke DrawString8x8, 8, 8, OFFSET towerHudBadge, 00FFAA55h invoke DrawString8x8, 8, 8, OFFSET towerHudBadge, 00FFAA55h
invoke FormatNumber, towerStage, OFFSET hudNumBuffer invoke FormatNumber, towerStage, OFFSET hudNumBuffer
invoke DrawString8x8, 64, 8, OFFSET hudNumBuffer, 00FFFFFFh invoke DrawString8x8, 64, 8, OFFSET hudNumBuffer, 00FFFFFFh

View file

@ -1,4 +1,4 @@
; --- hell_tower_data.inc: Data for Hell Tower 3D --- ; --- hell_tower_data.inc: Data for Hell Tower 3D (6 Floors) ---
MODE_HELL_TOWER equ 23 MODE_HELL_TOWER equ 23
TOWER_STATE_BRIEFING equ 0 TOWER_STATE_BRIEFING equ 0
TOWER_STATE_IN_GAME equ 1 TOWER_STATE_IN_GAME equ 1
@ -24,71 +24,61 @@ towerHudText db 91,32,0
hellTowerMenuArt3D AnimPack3D <0> hellTowerMenuArt3D AnimPack3D <0>
hellTowerMenuPath3D db "assets\animations\hall\helltower-menu.bin",0 hellTowerMenuPath3D db "assets\animations\hall\helltower-menu.bin",0
towerFloorY dd 205, 186, 167, 148, 129, 110, 91, 72, 53, 34 ; 6 floors: 1=205, 2=174, 3=143, 4=112, 5=81, 6=50
towerFloorY dd 205, 174, 143, 112, 81, 50
towerCursorY dd 205 towerCursorY dd 205
towerCursorTargetY dd 205 towerCursorTargetY dd 205
towerCalloutAnim dd 0 towerCalloutAnim dd 0
towerPrizePrompt db 202,211,216,58,32,36,0 ; "КУШ: $" towerPrizePrompt db 202,211,216,58,32,36,0
towerFloorModes dd MODE_COIN, MODE_DICE, MODE_SKULLS, MODE_BLACKJACK, MODE_POKER, MODE_RUSSIAN, MODE_ROULETTE, MODE_DURAK, MODE_SKILL, MODE_BUCKSHOT towerFloorModes dd MODE_COIN, MODE_DICE, MODE_BLACKJACK, MODE_RUSSIAN, MODE_ROULETTE, MODE_BUCKSHOT
towerBoss1 db 193,197,209,32,192,199,192,208,210,192,0 towerBoss1 db 193,229,241,32,192,231,224,240,242,224,0
towerBoss2 db 196,197,204,206,205,32,202,206,209,210,197,201,0 towerBoss2 db 196,229,236,238,237,32,202,238,241,242,229,233,0
towerBoss3 db 215,197,208,205,206,202,205,200,198,205,200,202,0 towerBoss3 db 202,240,243,239,252,229,32,210,252,236,251,0
towerBoss4 db 202,208,211,207,220,197,32,207,208,197,200,209,207,206,196,205,197,201,0 towerBoss4 db 209,243,228,252,255,32,192,228,224,0
towerBoss5 db 196,197,204,206,205,32,193,203,197,212,192,0 towerBoss5 db 213,238,231,255,232,237,32,208,243,235,229,242,234,232,0
towerBoss6 db 206,193,208,197,215,197,205,205,219,201,32,195,208,197,216,205,200,202,0 towerBoss6 db 194,235,224,228,251,234,224,32,193,224,248,237,232,0
towerBoss7 db 202,206,203,197,209,206,32,209,211,196,220,193,219,0
towerBoss8 db 202,192,208,210,206,215,205,219,201,32,215,197,208,210,0
towerBoss9 db 207,192,203,192,215,32,202,208,222,202,192,0
towerBoss10 db 194,203,192,196,219,202,192,32,193,192,216,205,200,0
towerBossNames dd OFFSET towerBoss1, OFFSET towerBoss2, OFFSET towerBoss3, OFFSET towerBoss4, OFFSET towerBoss5, OFFSET towerBoss6, OFFSET towerBoss7, OFFSET towerBoss8, OFFSET towerBoss9, OFFSET towerBoss10 towerBossNames dd OFFSET towerBoss1, OFFSET towerBoss2, OFFSET towerBoss3, OFFSET towerBoss4, OFFSET towerBoss5, OFFSET towerBoss6
towerGame1 db 206,240,229,235,32,232,32,240,229,248,234,224,0 towerGame1 db 206,240,229,235,32,232,32,240,229,248,234,224,0
towerGame2 db 202,238,241,242,232,0 towerGame2 db 202,238,241,242,232,0
towerGame3 db 215,229,240,229,239,224,32,232,32,248,224,240,232,234,0 towerGame3 db 50,49,32,47,32,206,247,234,238,0
towerGame4 db 50,49,32,47,32,206,247,234,238,0 towerGame4 db 208,243,241,241,234,224,255,32,240,243,235,229,242,234,224,0
towerGame5 db 207,238,234,229,240,0 towerGame5 db 202,224,231,232,237,238,45,240,243,235,229,242,234,224,0
towerGame6 db 208,243,241,241,234,224,255,32,240,243,235,229,242,234,224,0 towerGame6 db 66,117,99,107,115,104,111,116,32,82,111,117,108,101,116,116,101,0
towerGame7 db 202,224,231,232,237,238,45,240,243,235,229,242,234,224,0
towerGame8 db 196,243,240,224,234,0
towerGame9 db 202,240,254,234,58,32,83,107,105,108,108,32,67,104,101,99,107,0
towerGame10 db 66,117,99,107,115,104,111,116,32,82,111,117,108,101,116,116,101,0
towerGameNames dd OFFSET towerGame1, OFFSET towerGame2, OFFSET towerGame3, OFFSET towerGame4, OFFSET towerGame5, OFFSET towerGame6, OFFSET towerGame7, OFFSET towerGame8, OFFSET towerGame9, OFFSET towerGame10 towerGameNames dd OFFSET towerGame1, OFFSET towerGame2, OFFSET towerGame3, OFFSET towerGame4, OFFSET towerGame5, OFFSET towerGame6
towerDesc1 db 211,227,224,228,224,233,32,241,242,238,240,238,237,243,32,236,238,237,229,242,251,46,0 towerDesc1 db 211,227,224,228,224,233,32,241,242,238,240,238,237,243,32,236,238,237,229,242,251,46,0
towerDesc2 db 207,229,240,229,225,240,238,241,252,32,228,229,236,238,237,224,32,226,32,241,243,236,236,229,32,234,238,241,242,229,233,46,0 towerDesc2 db 205,224,225,229,240,232,32,225,238,235,252,248,229,32,238,247,234,238,226,46,0
towerDesc3 db 205,224,233,228,232,32,228,243,248,243,32,239,238,228,32,247,229,240,229,239,238,236,46,0 towerDesc3 db 206,225,251,227,240,224,233,32,234,240,243,239,252,229,32,226,32,50,49,46,0
towerDesc4 db 209,238,225,229,240,232,32,50,49,32,238,247,234,238,32,232,32,239,238,225,229,228,232,32,234,240,243,239,252,229,46,0 towerDesc4 db 49,32,239,224,242,240,238,237,46,32,194,251,230,232,226,232,33,0
towerDesc5 db 194,241,234,240,238,233,32,234,224,240,242,251,32,232,32,239,238,225,229,228,232,32,226,32,239,238,234,229,240,46,0 towerDesc5 db 211,227,224,228,224,233,32,247,232,241,235,238,32,240,243,235,229,242,234,232,46,0
towerDesc6 db 49,32,239,224,242,240,238,237,32,226,32,225,224,240,224,225,224,237,229,58,32,226,251,230,232,226,232,46,0 towerDesc6 db 196,243,253,235,252,32,237,224,32,228,240,238,225,238,226,232,234,229,33,0
towerDesc7 db 209,228,229,235,224,233,32,241,242,224,226,234,243,32,237,224,32,240,243,235,229,242,234,229,46,0
towerDesc8 db 206,241,242,224,226,252,32,247,184,240,242,224,32,226,32,228,243,240,224,234,224,245,46,0
towerDesc9 db 207,238,239,224,228,232,32,226,32,231,238,237,243,32,232,32,241,238,240,226,232,241,252,32,241,32,234,240,254,234,224,46,0
towerDesc10 db 196,243,253,235,252,32,237,224,32,228,240,238,225,238,226,232,234,224,245,32,231,224,32,230,232,231,237,252,33,0
towerDescriptions dd OFFSET towerDesc1, OFFSET towerDesc2, OFFSET towerDesc3, OFFSET towerDesc4, OFFSET towerDesc5, OFFSET towerDesc6, OFFSET towerDesc7, OFFSET towerDesc8, OFFSET towerDesc9, OFFSET towerDesc10 towerDescriptions dd OFFSET towerDesc1, OFFSET towerDesc2, OFFSET towerDesc3, OFFSET towerDesc4, OFFSET towerDesc5, OFFSET towerDesc6
towerHeader db 202,192,199,200,205,206,32,51,68,32,45,32,192,196,209,202,192,223,32,193,192,216,205,223,0 towerHeader db 192,196,209,202,192,223,32,193,192,216,205,223,0
towerFloorPrefix db 221,210,192,198,32,0 towerFloorPrefix db 221,210,192,198,32,0
towerOfTen db 32,47,32,49,48,0 towerOfTen db 32,47,32,54,0
towerEnemyLabel db 194,208,192,195,58,32,0 towerEnemyLabel db 194,208,192,195,58,32,0
towerGameLabel db 200,195,208,192,58,32,0 towerGameLabel db 200,195,208,192,58,32,0
towerRuleText db 49,48,32,225,232,242,226,32,239,238,228,240,255,228,32,225,229,231,32,239,240,224,226,224,32,237,224,32,238,248,232,225,234,243,0 towerRuleText db 54,32,253,242,224,230,229,233,32,225,229,231,32,239,238,240,224,230,229,237,232,233,0
towerLossWarning db 207,238,240,224,230,229,237,232,229,32,45,32,226,238,231,226,240,224,242,32,226,32,245,238,235,235,32,234,224,231,232,237,238,0 towerLossWarning db 207,238,240,224,230,229,237,232,229,32,45,32,226,238,231,226,240,224,242,33,0
towerStartBtn db 91,207,208,206,193,197,203,93,32,194,32,193,206,201,0 towerStartBtn db 91,69,78,84,69,82,93,32,193,206,201,0
towerEscBtn db 91,69,83,67,93,32,209,193,197,198,192,210,220,0 towerEscBtn db 91,69,83,67,93,32,205,192,199,192,196,0
towerClearTitle db 221,210,192,198,32,199,192,215,200,217,197,205,33,32,194,208,192,195,32,207,206,194,197,208,198,197,205,33,0 towerClearTitle db 221,210,192,198,32,199,192,215,200,217,197,205,33,0
towerClearDesc db 194,240,224,227,32,239,238,226,229,240,230,229,237,33,32,194,239,229,240,229,228,232,32,237,238,226,251,233,32,253,242,224,230,46,0 towerClearDesc db 194,240,224,227,32,239,238,226,229,240,230,229,237,33,0
towerNextBtn db 91,207,208,206,193,197,203,93,32,196,192,203,220,216,197,0 towerNextBtn db 91,69,78,84,69,82,93,32,196,192,203,220,216,197,0
towerDefeatTitle db 194,219,32,207,192,203,200,32,194,32,192,196,209,202,206,201,32,193,192,216,205,197,46,46,46,0 towerDefeatTitle db 194,219,32,207,206,195,200,193,203,200,32,194,32,193,192,216,205,197,33,0
towerDefeatDesc db 194,224,248,224,32,228,243,248,224,32,237,224,226,229,234,232,32,241,227,238,240,229,235,224,32,226,32,192,228,243,46,0 towerDefeatDesc db 196,243,248,224,32,241,227,238,240,229,235,224,32,226,32,192,228,243,46,0
towerReturnBtn db 91,207,208,206,193,197,203,32,47,32,69,83,67,93,32,194,32,213,206,203,203,32,202,192,199,200,205,206,0 towerReturnBtn db 91,69,83,67,93,32,194,32,213,206,203,203,32,202,192,199,200,205,206,0
towerChampTitle db 210,208,200,211,204,212,33,32,192,196,209,202,192,223,32,193,192,216,205,223,32,207,206,202,206,208,197,205,192,33,0 towerChampTitle db 193,192,216,205,223,32,207,206,202,206,208,197,205,192,33,0
towerChampSubtitle db 194,219,32,45,32,215,197,204,207,200,206,205,32,192,196,209,202,206,201,32,193,192,216,205,200,33,0 towerChampSubtitle db 194,251,32,45,32,194,235,224,228,251,234,224,32,193,224,248,237,232,33,0
towerPrizeText db 209,211,207,197,208,207,208,200,199,58,32,43,36,54,54,44,54,48,48,32,194,32,193,192,205,202,208,206,203,203,33,0 towerPrizeText db 202,211,216,58,32,43,36,54,54,44,54,48,48,32,194,32,193,192,205,202,33,0
towerHudBadge db 193,192,216,205,223,58,32,0 towerHudBadge db 193,192,216,205,223,58,32,0
towerHudOfTen db 47,49,48,0 towerHudOfTen db 47,54,0

View file

@ -1,4 +1,4 @@
"""Render full simulation of Hell Tower menu at floors 1, 5, 10.""" """Render simulation of Hell Tower with 6 floors and strict boundary checks."""
import struct import struct
from pathlib import Path from pathlib import Path
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
@ -7,109 +7,167 @@ ROOT = Path(__file__).resolve().parents[2]
MENU_BIN = ROOT / "assets/animations/hall/helltower-menu.bin" MENU_BIN = ROOT / "assets/animations/hall/helltower-menu.bin"
ART_DIR = Path(r"C:\Users\IKER\.gemini\antigravity-cli\brain\c2d69e9d-8822-4f06-923a-36a9194468d6") ART_DIR = Path(r"C:\Users\IKER\.gemini\antigravity-cli\brain\c2d69e9d-8822-4f06-923a-36a9194468d6")
FLOOR_Y = [205, 186, 167, 148, 129, 110, 91, 72, 53, 34] # 6 Floors Y in logical 320x240 coordinates
FLOOR_Y = [205, 174, 143, 112, 81, 50]
BOSSES = [ BOSSES = [
"БЕС АЗАРТА", "ДЕМОН КОСТЕЙ", "ЧЕРНОКНИЖНИК", "КРУПЬЕ ПРЕИСПОДНЕЙ", "ДЕМОН БЛЕФА", "Бес Азарта",
"ОБРЕЧЕННЫЙ ГРЕШНИК", "КОЛЕСО СУДЬБЫ", "КАРТОЧНЫЙ ЧЕРТ", "ПАЛАЧ КРЮКА", "ВЛАДЫКА БАШНИ" "Демон Костей",
"Крупье Тьмы",
"Судья Ада",
"Хозяин Рулетки",
"Владыка Башни"
] ]
GAMES = [ GAMES = [
"Орел и решка", "Кости", "Черепа и шарик", "21 / Очко", "Покер", "Орел и решка",
"Русская рулетка", "Казино-рулетка", "Дурак", "Крюк: Skill Check", "Buckshot Roulette" "Кости",
"21 / Очко",
"Русская рулетка",
"Казино-рулетка",
"Buckshot Roulette"
] ]
DESCS = [ DESCS = [
"Угадай сторону монеты.", "Угадай сторону монеты.",
"Перебрось демона в сумме костей.", "Набери больше очков.",
"Найди душу под черепом.", "Обыграй крупье в 21.",
"Собери 21 очко и победи крупье.", "1 патрон. Выживи!",
"Вскрой карты и победи в покер.", "Угадай число рулетки.",
"1 патрон в барабане: выживи.", "Дуэль на дробовике!"
"Сделай ставку на рулетке.",
"Оставь чёрта в дураках.",
"Попади в зону и сорвись с крюка.",
"Дуэль на дробовиках за жизнь!"
] ]
SUB_NOTES = [
"Один бросок решит все.",
"Бросай кости против беса.",
"Собери больше, но не перебор.",
"Крути барабан револьвера.",
"Ставь на черное или красное.",
"Заряжай картечь и стреляй!"
]
def render_menu_frame(stage: int, callout_anim: int = 100): def render_menu_frame(stage: int, state: int = 0, callout_anim: int = 100):
# 1. Load background from ANM1 pack # 1. Load background
data = MENU_BIN.read_bytes() data = MENU_BIN.read_bytes()
magic, w, h, frames, b_len = struct.unpack("<4sIIII", data[:20]) magic, w, h, frames, b_len = struct.unpack("<4sIIII", data[:20])
raw_bgra = data[20:20 + b_len] raw_bgra = data[20:20 + b_len]
bg = Image.frombytes("RGBA", (w, h), raw_bgra, "raw", "BGRA") bg = Image.frombytes("RGBA", (w, h), raw_bgra, "raw", "BGRA")
draw = ImageDraw.Draw(bg) # Scale down to 320x240 logical to replicate exact ASM blit logic
canvas = bg.resize((320, 240), Image.Resampling.LANCZOS)
draw = ImageDraw.Draw(canvas)
floor_idx = stage - 1 floor_idx = max(0, min(5, stage - 1))
target_y = FLOOR_Y[floor_idx] target_y = FLOOR_Y[floor_idx]
# Calculate sliding card box # Sliding card dimensions
card_h = 208 card_h = 208
card_y = 20 card_y = 16
card_w = max(10, 206 * callout_anim // 100) card_w = max(10, 206 * callout_anim // 100)
card_x = 214 - card_w card_x = 214 - card_w
# Connector line # 1. Indicator line and cursor on tower
draw.line([(214, target_y), (224, target_y)], fill=(255, 68, 68), width=2) draw.line([(214, target_y), (224, target_y)], fill=(255, 68, 68), width=2)
# Cursor chevron/diamond
draw.rectangle([224, target_y - 3, 230, target_y + 3], fill=(255, 51, 0), outline=(255, 221, 68)) draw.rectangle([224, target_y - 3, 230, target_y + 3], fill=(255, 51, 0), outline=(255, 221, 68))
# Pulsing flame pip
draw.rectangle([234, target_y - 2, 238, target_y + 2], fill=(255, 170, 34)) draw.rectangle([234, target_y - 2, 238, target_y + 2], fill=(255, 170, 34))
# Outer and inner border of card # 2. Card outer/inner borders
draw.rectangle([card_x, card_y, card_x + card_w, card_y + card_h], outline=(204, 34, 34), width=2) draw.rectangle([card_x, card_y, card_x + card_w, card_y + card_h], outline=(204, 34, 34), width=2)
draw.rectangle([card_x + 2, card_y + 2, card_x + card_w - 2, card_y + card_h - 2], outline=(85, 17, 17), width=1) draw.rectangle([card_x + 2, card_y + 2, card_x + card_w - 2, card_y + card_h - 2], outline=(85, 17, 17), width=1)
inner_w = card_w - 12
inner_x = card_x + 6
if card_w >= 120: if card_w >= 120:
# Header banner # Header banner: "АДСКАЯ БАШНЯ" (Centered, NO "КАЗИНО 3D")
draw.rectangle([card_x + 6, 26, card_x + card_w - 6, 44], fill=(34, 4, 4), outline=(255, 68, 68)) hdr_w = len("АДСКАЯ БАШНЯ") * 8
draw.text((card_x + 12, 29), "КАЗИНО 3D - АДСКАЯ БАШНЯ", fill=(255, 221, 102)) hdr_x = inner_x + (inner_w - hdr_w) // 2
draw.rectangle([inner_x, 22, inner_x + inner_w, 40], fill=(34, 4, 4), outline=(255, 68, 68))
draw.text((hdr_x, 26), "АДСКАЯ БАШНЯ", fill=(255, 221, 102))
# Floor indicator # Floor & Bankroll / Prize indicator
draw.text((card_x + 10, 48), f"ЭТАЖ {stage} / 10", fill=(255, 102, 102)) draw.text((card_x + 12, 46), f"ЭТАЖ {stage} / 6", fill=(255, 102, 102))
draw.text((card_x + 105, 46), "КУШ: $66,600", fill=(255, 221, 68))
# Boss & Game sub-card if state == 0: # BRIEFING
draw.rectangle([card_x + 6, 64, card_x + card_w - 6, 126], fill=(30, 4, 4), outline=(136, 24, 24)) # Subcard
draw.text((card_x + 10, 68), f"ВРАГ: {BOSSES[floor_idx]}", fill=(255, 255, 255)) draw.rectangle([inner_x, 60, inner_x + inner_w, 130], fill=(30, 4, 4), outline=(136, 24, 24))
draw.text((card_x + 10, 82), f"ИГРА: {GAMES[floor_idx]}", fill=(255, 221, 102)) draw.text((card_x + 12, 66), f"ВРАГ: {BOSSES[floor_idx]}", fill=(255, 255, 255))
draw.text((card_x + 10, 98), DESCS[floor_idx], fill=(204, 204, 204)) draw.text((card_x + 12, 80), f"ИГРА: {GAMES[floor_idx]}", fill=(255, 221, 102))
draw.text((card_x + 12, 96), DESCS[floor_idx], fill=(204, 204, 204))
draw.text((card_x + 12, 112), SUB_NOTES[floor_idx], fill=(170, 170, 170))
# Rules & warning # Rules & warning box
draw.text((card_x + 10, 132), "10 битв подряд без права на ошибку", fill=(255, 170, 51)) draw.rectangle([inner_x, 134, inner_x + inner_w, 166], fill=(24, 3, 3), outline=(102, 17, 17))
draw.text((card_x + 10, 146), "Поражение - возврат в холл казино", fill=(238, 68, 68)) draw.text((card_x + 12, 139), "6 этажей без поражений", fill=(255, 170, 51))
draw.text((card_x + 12, 151), "Поражение - возврат!", fill=(255, 85, 85))
# Bankroll & Prize # Bank
draw.text((card_x + 10, 162), "БАНК: $10,000", fill=(102, 255, 102)) draw.text((card_x + 12, 172), "БАНК: $10,000", fill=(102, 255, 102))
draw.text((card_x + 110, 162), "КУШ: $66,600", fill=(255, 221, 68)) draw.text((card_x + 115, 172), "НАГРАДА: +100%", fill=(255, 200, 50))
# Buttons # Action Buttons
draw.rectangle([card_x + 8, 184, card_x + 100, 206], fill=(61, 12, 12), outline=(255, 68, 68)) btn_w = 93
draw.text((card_x + 14, 191), "[ПРОБЕЛ] В БОЙ", fill=(255, 255, 255)) draw.rectangle([inner_x, 188, inner_x + btn_w, 212], fill=(61, 12, 12), outline=(255, 68, 68))
draw.text((inner_x + 12, 196), "[ENTER] БОЙ", fill=(255, 255, 255))
draw.rectangle([card_x + 106, 184, card_x + 198, 206], fill=(34, 6, 6), outline=(136, 34, 34)) draw.rectangle([inner_x + 101, 188, inner_x + 101 + btn_w, 212], fill=(34, 6, 6), outline=(136, 34, 34))
draw.text((card_x + 114, 191), "[ESC] СБЕЖАТЬ", fill=(170, 170, 170)) draw.text((inner_x + 113, 196), "[ESC] НАЗАД", fill=(170, 170, 170))
# Scale 2x for high quality artifact preview (640x480) elif state == 2: # CLEAR
bg_hd = bg.resize((640, 480), Image.Resampling.NEAREST) draw.rectangle([inner_x, 62, inner_x + inner_w, 142], fill=(8, 34, 8), outline=(51, 204, 51), width=2)
return bg_hd clr_w = len("ЭТАЖ ЗАЧИЩЕН!") * 8
draw.text((inner_x + (inner_w - clr_w) // 2, 76), "ЭТАЖ ЗАЧИЩЕН!", fill=(102, 255, 102))
sub_w = len("Враг повержен!") * 8
draw.text((inner_x + (inner_w - sub_w) // 2, 96), "Враг повержен!", fill=(255, 255, 255))
rwd_w = len("Путь наверх открыт!") * 8
draw.text((inner_x + (inner_w - rwd_w) // 2, 116), "Путь наверх открыт!", fill=(255, 221, 102))
draw.rectangle([card_x + 18, 176, card_x + 188, 202], fill=(17, 68, 17), outline=(68, 255, 68))
btn_w2 = len("[ENTER] ДАЛЬШЕ") * 8
draw.text((card_x + 18 + (170 - btn_w2) // 2, 184), "[ENTER] ДАЛЬШЕ", fill=(255, 255, 255))
elif state == 3: # DEFEAT
draw.rectangle([inner_x, 62, inner_x + inner_w, 142], fill=(42, 4, 4), outline=(255, 34, 34), width=2)
dft_w = len("ВЫ ПОГИБЛИ В БАШНЕ!") * 8
draw.text((inner_x + (inner_w - dft_w) // 2, 76), "ВЫ ПОГИБЛИ В БАШНЕ!", fill=(255, 68, 68))
sub_w = len("Душа сгорела в Аду.") * 8
draw.text((inner_x + (inner_w - sub_w) // 2, 98), "Душа сгорела в Аду.", fill=(255, 255, 255))
draw.rectangle([card_x + 18, 176, card_x + 188, 202], fill=(68, 16, 16), outline=(255, 68, 68))
btn_w2 = len("[ESC] В ХОЛЛ КАЗИНО") * 8
draw.text((card_x + 18 + (170 - btn_w2) // 2, 184), "[ESC] В ХОЛЛ КАЗИНО", fill=(255, 255, 255))
elif state == 4: # CHAMPION
draw.rectangle([inner_x, 58, inner_x + inner_w, 150], fill=(51, 34, 0), outline=(255, 215, 0), width=2)
chm_w = len("БАШНЯ ПОКОРЕНА!") * 8
draw.text((inner_x + (inner_w - chm_w) // 2, 70), "БАШНЯ ПОКОРЕНА!", fill=(255, 224, 102))
sub_w = len("Вы - Владыка Башни!") * 8
draw.text((inner_x + (inner_w - sub_w) // 2, 90), "Вы - Владыка Башни!", fill=(255, 255, 255))
prz_w = len("КУШ: +$66,600 В БАНК!") * 8
draw.text((inner_x + (inner_w - prz_w) // 2, 114), "КУШ: +$66,600 В БАНК!", fill=(102, 255, 102))
draw.rectangle([card_x + 18, 176, card_x + 188, 202], fill=(68, 51, 0), outline=(255, 204, 0))
btn_w2 = len("[ESC] В ХОЛЛ КАЗИНО") * 8
draw.text((card_x + 18 + (170 - btn_w2) // 2, 184), "[ESC] В ХОЛЛ КАЗИНО", fill=(255, 255, 255))
# Scale 2x for full HD 640x480 preview
return canvas.resize((640, 480), Image.Resampling.NEAREST)
def main(): def main():
ART_DIR.mkdir(parents=True, exist_ok=True) ART_DIR.mkdir(parents=True, exist_ok=True)
f1 = render_menu_frame(1, 100) for st in range(1, 7):
f1.save(ART_DIR / "helltower_rendered_floor1.png") im = render_menu_frame(st, state=0)
print("[OK] Saved helltower_rendered_floor1.png") im.save(ART_DIR / f"helltower_6fl_floor{st}.png")
print(f"[OK] Saved helltower_6fl_floor{st}.png")
f5 = render_menu_frame(5, 100)
f5.save(ART_DIR / "helltower_rendered_floor5.png")
print("[OK] Saved helltower_rendered_floor5.png")
f10 = render_menu_frame(10, 100)
f10.save(ART_DIR / "helltower_rendered_floor10.png")
print("[OK] Saved helltower_rendered_floor10.png")
clr = render_menu_frame(1, state=2)
clr.save(ART_DIR / "helltower_6fl_clear.png")
dft = render_menu_frame(1, state=3)
dft.save(ART_DIR / "helltower_6fl_defeat.png")
chp = render_menu_frame(6, state=4)
chp.save(ART_DIR / "helltower_6fl_champion.png")
print("All previews generated successfully!")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -1,4 +1,4 @@
"""Automated smoke test for Hell Tower 10-floor run-through.""" """Automated smoke test for Hell Tower 6-floor run-through."""
import subprocess import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
@ -22,13 +22,13 @@ def main():
print("[FAIL] Build failed:", build_res.stdout, build_res.stderr) print("[FAIL] Build failed:", build_res.stdout, build_res.stderr)
return 1 return 1
print("[3/4] Running Hell Tower 10-floor run-through...") print("[3/4] Running Hell Tower 6-floor run-through...")
run_res = subprocess.run([str(ROOT / "poker.exe")], cwd=ROOT, capture_output=True, timeout=15) run_res = subprocess.run([str(ROOT / "poker.exe")], cwd=ROOT, capture_output=True, timeout=15)
print(f"Hell Tower test completed with exit code: {run_res.returncode}") print(f"Hell Tower test completed with exit code: {run_res.returncode}")
if run_res.returncode != 0: if run_res.returncode != 0:
print(f"[FAIL] Hell Tower run-through failed with code {run_res.returncode}") print(f"[FAIL] Hell Tower run-through failed with code {run_res.returncode}")
return run_res.returncode return run_res.returncode
print("[SUCCESS] All 10 floors of Hell Tower completed without errors or crashes!") print("[SUCCESS] All 6 floors of Hell Tower completed without errors or crashes!")
finally: finally:
print("[4/4] Restoring normal poker.asm and rebuilding...") print("[4/4] Restoring normal poker.asm and rebuilding...")
restored = POKER_ASM.read_text(encoding="cp1251") restored = POKER_ASM.read_text(encoding="cp1251")