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
; 4. Check outcome state
cmp stageLoop, 10
cmp stageLoop, 6
jae stage_check_champ
cmp towerFloorState, TOWER_STATE_CLEAR

BIN
poker.exe

Binary file not shown.

BIN
poker.obj

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -1,94 +1,84 @@
; --- hell_tower_data.inc: Data for Hell Tower 3D ---
MODE_HELL_TOWER equ 23
TOWER_STATE_BRIEFING equ 0
TOWER_STATE_IN_GAME equ 1
TOWER_STATE_CLEAR equ 2
TOWER_STATE_DEFEAT equ 3
TOWER_STATE_CHAMPION equ 4
InitHellTower3D PROTO
TowerExitToHall PROTO
TowerReportRoundOutcome PROTO :DWORD
RenderTowerInGameBadge PROTO
ResolveHellAssetPath PROTO :DWORD, :DWORD
FreeTowerMinigameAssets PROTO
.data
towerStage dd 1
towerFloorState dd 0
towerAnimClock dd 0
towerCurrentGame dd 0
towerPrizeAmount dd 66600
towerHudText db 91,32,0
hellTowerMenuArt3D AnimPack3D <0>
hellTowerMenuPath3D db "assets\animations\hall\helltower-menu.bin",0
towerFloorY dd 205, 186, 167, 148, 129, 110, 91, 72, 53, 34
towerCursorY dd 205
towerCursorTargetY dd 205
towerCalloutAnim dd 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
towerBoss1 db 193,197,209,32,192,199,192,208,210,192,0
towerBoss2 db 196,197,204,206,205,32,202,206,209,210,197,201,0
towerBoss3 db 215,197,208,205,206,202,205,200,198,205,200,202,0
towerBoss4 db 202,208,211,207,220,197,32,207,208,197,200,209,207,206,196,205,197,201,0
towerBoss5 db 196,197,204,206,205,32,193,203,197,212,192,0
towerBoss6 db 206,193,208,197,215,197,205,205,219,201,32,195,208,197,216,205,200,202,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
towerGame1 db 206,240,229,235,32,232,32,240,229,248,234,224,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
towerGame4 db 50,49,32,47,32,206,247,234,238,0
towerGame5 db 207,238,234,229,240,0
towerGame6 db 208,243,241,241,234,224,255,32,240,243,235,229,242,234,224,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
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
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
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
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
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
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
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
towerFloorPrefix db 221,210,192,198,32,0
towerOfTen db 32,47,32,49,48,0
towerEnemyLabel db 194,208,192,195,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
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
towerStartBtn db 91,207,208,206,193,197,203,93,32,194,32,193,206,201,0
towerEscBtn db 91,69,83,67,93,32,209,193,197,198,192,210,220,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
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
towerNextBtn db 91,207,208,206,193,197,203,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
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
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
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
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
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
towerHudBadge db 193,192,216,205,223,58,32,0
towerHudOfTen db 47,49,48,0
; --- hell_tower_data.inc: Data for Hell Tower 3D (6 Floors) ---
MODE_HELL_TOWER equ 23
TOWER_STATE_BRIEFING equ 0
TOWER_STATE_IN_GAME equ 1
TOWER_STATE_CLEAR equ 2
TOWER_STATE_DEFEAT equ 3
TOWER_STATE_CHAMPION equ 4
InitHellTower3D PROTO
TowerExitToHall PROTO
TowerReportRoundOutcome PROTO :DWORD
RenderTowerInGameBadge PROTO
ResolveHellAssetPath PROTO :DWORD, :DWORD
FreeTowerMinigameAssets PROTO
.data
towerStage dd 1
towerFloorState dd 0
towerAnimClock dd 0
towerCurrentGame dd 0
towerPrizeAmount dd 66600
towerHudText db 91,32,0
hellTowerMenuArt3D AnimPack3D <0>
hellTowerMenuPath3D db "assets\animations\hall\helltower-menu.bin",0
; 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
towerCursorTargetY dd 205
towerCalloutAnim dd 0
towerPrizePrompt db 202,211,216,58,32,36,0
towerFloorModes dd MODE_COIN, MODE_DICE, MODE_BLACKJACK, MODE_RUSSIAN, MODE_ROULETTE, MODE_BUCKSHOT
towerBoss1 db 193,229,241,32,192,231,224,240,242,224,0
towerBoss2 db 196,229,236,238,237,32,202,238,241,242,229,233,0
towerBoss3 db 202,240,243,239,252,229,32,210,252,236,251,0
towerBoss4 db 209,243,228,252,255,32,192,228,224,0
towerBoss5 db 213,238,231,255,232,237,32,208,243,235,229,242,234,232,0
towerBoss6 db 194,235,224,228,251,234,224,32,193,224,248,237,232,0
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
towerGame2 db 202,238,241,242,232,0
towerGame3 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 202,224,231,232,237,238,45,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
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
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 206,225,251,227,240,224,233,32,234,240,243,239,252,229,32,226,32,50,49,46,0
towerDesc4 db 49,32,239,224,242,240,238,237,46,32,194,251,230,232,226,232,33,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 196,243,253,235,252,32,237,224,32,228,240,238,225,238,226,232,234,229,33,0
towerDescriptions dd OFFSET towerDesc1, OFFSET towerDesc2, OFFSET towerDesc3, OFFSET towerDesc4, OFFSET towerDesc5, OFFSET towerDesc6
towerHeader db 192,196,209,202,192,223,32,193,192,216,205,223,0
towerFloorPrefix db 221,210,192,198,32,0
towerOfTen db 32,47,32,54,0
towerEnemyLabel db 194,208,192,195,58,32,0
towerGameLabel db 200,195,208,192,58,32,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,33,0
towerStartBtn db 91,69,78,84,69,82,93,32,193,206,201,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,0
towerClearDesc db 194,240,224,227,32,239,238,226,229,240,230,229,237,33,0
towerNextBtn db 91,69,78,84,69,82,93,32,196,192,203,220,216,197,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 196,243,248,224,32,241,227,238,240,229,235,224,32,226,32,192,228,243,46,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 193,192,216,205,223,32,207,206,202,206,208,197,205,192,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 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
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
from pathlib import Path
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"
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 = [
"БЕС АЗАРТА", "ДЕМОН КОСТЕЙ", "ЧЕРНОКНИЖНИК", "КРУПЬЕ ПРЕИСПОДНЕЙ", "ДЕМОН БЛЕФА",
"ОБРЕЧЕННЫЙ ГРЕШНИК", "КОЛЕСО СУДЬБЫ", "КАРТОЧНЫЙ ЧЕРТ", "ПАЛАЧ КРЮКА", "ВЛАДЫКА БАШНИ"
"Бес Азарта",
"Демон Костей",
"Крупье Тьмы",
"Судья Ада",
"Хозяин Рулетки",
"Владыка Башни"
]
GAMES = [
"Орел и решка", "Кости", "Черепа и шарик", "21 / Очко", "Покер",
"Русская рулетка", "Казино-рулетка", "Дурак", "Крюк: Skill Check", "Buckshot Roulette"
"Орел и решка",
"Кости",
"21 / Очко",
"Русская рулетка",
"Казино-рулетка",
"Buckshot Roulette"
]
DESCS = [
"Угадай сторону монеты.",
"Перебрось демона в сумме костей.",
"Найди душу под черепом.",
"Собери 21 очко и победи крупье.",
"Вскрой карты и победи в покер.",
"1 патрон в барабане: выживи.",
"Сделай ставку на рулетке.",
"Оставь чёрта в дураках.",
"Попади в зону и сорвись с крюка.",
"Дуэль на дробовиках за жизнь!"
"Набери больше очков.",
"Обыграй крупье в 21.",
"1 патрон. Выживи!",
"Угадай число рулетки.",
"Дуэль на дробовике!"
]
SUB_NOTES = [
"Один бросок решит все.",
"Бросай кости против беса.",
"Собери больше, но не перебор.",
"Крути барабан револьвера.",
"Ставь на черное или красное.",
"Заряжай картечь и стреляй!"
]
def render_menu_frame(stage: int, callout_anim: int = 100):
# 1. Load background from ANM1 pack
def render_menu_frame(stage: int, state: int = 0, callout_anim: int = 100):
# 1. Load background
data = MENU_BIN.read_bytes()
magic, w, h, frames, b_len = struct.unpack("<4sIIII", data[:20])
raw_bgra = data[20:20 + b_len]
bg = Image.frombytes("RGBA", (w, h), raw_bgra, "raw", "BGRA")
draw = ImageDraw.Draw(bg)
floor_idx = stage - 1
# 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 = max(0, min(5, stage - 1))
target_y = FLOOR_Y[floor_idx]
# Calculate sliding card box
# Sliding card dimensions
card_h = 208
card_y = 20
card_y = 16
card_w = max(10, 206 * callout_anim // 100)
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)
# Cursor chevron/diamond
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))
# 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 + 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:
# Header banner
draw.rectangle([card_x + 6, 26, card_x + card_w - 6, 44], fill=(34, 4, 4), outline=(255, 68, 68))
draw.text((card_x + 12, 29), "КАЗИНО 3D - АДСКАЯ БАШНЯ", fill=(255, 221, 102))
# Header banner: "АДСКАЯ БАШНЯ" (Centered, NO "КАЗИНО 3D")
hdr_w = len("АДСКАЯ БАШНЯ") * 8
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 & Bankroll / Prize indicator
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))
if state == 0: # BRIEFING
# Subcard
draw.rectangle([inner_x, 60, inner_x + inner_w, 130], fill=(30, 4, 4), outline=(136, 24, 24))
draw.text((card_x + 12, 66), f"ВРАГ: {BOSSES[floor_idx]}", fill=(255, 255, 255))
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 box
draw.rectangle([inner_x, 134, inner_x + inner_w, 166], fill=(24, 3, 3), outline=(102, 17, 17))
draw.text((card_x + 12, 139), "6 этажей без поражений", fill=(255, 170, 51))
draw.text((card_x + 12, 151), "Поражение - возврат!", fill=(255, 85, 85))
# Bank
draw.text((card_x + 12, 172), "БАНК: $10,000", fill=(102, 255, 102))
draw.text((card_x + 115, 172), "НАГРАДА: +100%", fill=(255, 200, 50))
# Action Buttons
btn_w = 93
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([inner_x + 101, 188, inner_x + 101 + btn_w, 212], fill=(34, 6, 6), outline=(136, 34, 34))
draw.text((inner_x + 113, 196), "[ESC] НАЗАД", fill=(170, 170, 170))
# Floor indicator
draw.text((card_x + 10, 48), f"ЭТАЖ {stage} / 10", fill=(255, 102, 102))
elif state == 2: # CLEAR
draw.rectangle([inner_x, 62, inner_x + inner_w, 142], fill=(8, 34, 8), outline=(51, 204, 51), width=2)
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))
# Boss & Game sub-card
draw.rectangle([card_x + 6, 64, card_x + card_w - 6, 126], fill=(30, 4, 4), outline=(136, 24, 24))
draw.text((card_x + 10, 68), f"ВРАГ: {BOSSES[floor_idx]}", fill=(255, 255, 255))
draw.text((card_x + 10, 82), f"ИГРА: {GAMES[floor_idx]}", fill=(255, 221, 102))
draw.text((card_x + 10, 98), DESCS[floor_idx], fill=(204, 204, 204))
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))
# Rules & warning
draw.text((card_x + 10, 132), "10 битв подряд без права на ошибку", fill=(255, 170, 51))
draw.text((card_x + 10, 146), "Поражение - возврат в холл казино", fill=(238, 68, 68))
# Bankroll & Prize
draw.text((card_x + 10, 162), "БАНК: $10,000", fill=(102, 255, 102))
draw.text((card_x + 110, 162), "КУШ: $66,600", fill=(255, 221, 68))
# Buttons
draw.rectangle([card_x + 8, 184, card_x + 100, 206], fill=(61, 12, 12), outline=(255, 68, 68))
draw.text((card_x + 14, 191), "[ПРОБЕЛ] В БОЙ", fill=(255, 255, 255))
draw.rectangle([card_x + 106, 184, card_x + 198, 206], fill=(34, 6, 6), outline=(136, 34, 34))
draw.text((card_x + 114, 191), "[ESC] СБЕЖАТЬ", fill=(170, 170, 170))
# Scale 2x for high quality artifact preview (640x480)
bg_hd = bg.resize((640, 480), Image.Resampling.NEAREST)
return bg_hd
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():
ART_DIR.mkdir(parents=True, exist_ok=True)
f1 = render_menu_frame(1, 100)
f1.save(ART_DIR / "helltower_rendered_floor1.png")
print("[OK] Saved helltower_rendered_floor1.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")
for st in range(1, 7):
im = render_menu_frame(st, state=0)
im.save(ART_DIR / f"helltower_6fl_floor{st}.png")
print(f"[OK] Saved helltower_6fl_floor{st}.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__":
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 sys
from pathlib import Path
@ -22,13 +22,13 @@ def main():
print("[FAIL] Build failed:", build_res.stdout, build_res.stderr)
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)
print(f"Hell Tower test completed with exit code: {run_res.returncode}")
if run_res.returncode != 0:
print(f"[FAIL] Hell Tower run-through failed with code {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:
print("[4/4] Restoring normal poker.asm and rebuilding...")
restored = POKER_ASM.read_text(encoding="cp1251")