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.

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 --- ; --- 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
TOWER_STATE_CLEAR equ 2 TOWER_STATE_CLEAR equ 2
TOWER_STATE_DEFEAT equ 3 TOWER_STATE_DEFEAT equ 3
TOWER_STATE_CHAMPION equ 4 TOWER_STATE_CHAMPION equ 4
InitHellTower3D PROTO InitHellTower3D PROTO
TowerExitToHall PROTO TowerExitToHall PROTO
TowerReportRoundOutcome PROTO :DWORD TowerReportRoundOutcome PROTO :DWORD
RenderTowerInGameBadge PROTO RenderTowerInGameBadge PROTO
ResolveHellAssetPath PROTO :DWORD, :DWORD ResolveHellAssetPath PROTO :DWORD, :DWORD
FreeTowerMinigameAssets PROTO FreeTowerMinigameAssets PROTO
.data .data
towerStage dd 1 towerStage dd 1
towerFloorState dd 0 towerFloorState dd 0
towerAnimClock dd 0 towerAnimClock dd 0
towerCurrentGame dd 0 towerCurrentGame dd 0
towerPrizeAmount dd 66600 towerPrizeAmount dd 66600
towerHudText db 91,32,0 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
towerCursorY dd 205 towerFloorY dd 205, 174, 143, 112, 81, 50
towerCursorTargetY dd 205 towerCursorY dd 205
towerCalloutAnim dd 0 towerCursorTargetY dd 205
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
towerBoss2 db 196,197,204,206,205,32,202,206,209,210,197,201,0 towerBoss1 db 193,229,241,32,192,231,224,240,242,224,0
towerBoss3 db 215,197,208,205,206,202,205,200,198,205,200,202,0 towerBoss2 db 196,229,236,238,237,32,202,238,241,242,229,233,0
towerBoss4 db 202,208,211,207,220,197,32,207,208,197,200,209,207,206,196,205,197,201,0 towerBoss3 db 202,240,243,239,252,229,32,210,252,236,251,0
towerBoss5 db 196,197,204,206,205,32,193,203,197,212,192,0 towerBoss4 db 209,243,228,252,255,32,192,228,224,0
towerBoss6 db 206,193,208,197,215,197,205,205,219,201,32,195,208,197,216,205,200,202,0 towerBoss5 db 213,238,231,255,232,237,32,208,243,235,229,242,234,232,0
towerBoss7 db 202,206,203,197,209,206,32,209,211,196,220,193,219,0 towerBoss6 db 194,235,224,228,251,234,224,32,193,224,248,237,232,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 towerBossNames dd OFFSET towerBoss1, OFFSET towerBoss2, OFFSET towerBoss3, OFFSET towerBoss4, OFFSET towerBoss5, OFFSET towerBoss6
towerBoss10 db 194,203,192,196,219,202,192,32,193,192,216,205,200,0
towerGame1 db 206,240,229,235,32,232,32,240,229,248,234,224,0
towerBossNames dd OFFSET towerBoss1, OFFSET towerBoss2, OFFSET towerBoss3, OFFSET towerBoss4, OFFSET towerBoss5, OFFSET towerBoss6, OFFSET towerBoss7, OFFSET towerBoss8, OFFSET towerBoss9, OFFSET towerBoss10 towerGame2 db 202,238,241,242,232,0
towerGame3 db 50,49,32,47,32,206,247,234,238,0
towerGame1 db 206,240,229,235,32,232,32,240,229,248,234,224,0 towerGame4 db 208,243,241,241,234,224,255,32,240,243,235,229,242,234,224,0
towerGame2 db 202,238,241,242,232,0 towerGame5 db 202,224,231,232,237,238,45,240,243,235,229,242,234,224,0
towerGame3 db 215,229,240,229,239,224,32,232,32,248,224,240,232,234,0 towerGame6 db 66,117,99,107,115,104,111,116,32,82,111,117,108,101,116,116,101,0
towerGame4 db 50,49,32,47,32,206,247,234,238,0
towerGame5 db 207,238,234,229,240,0 towerGameNames dd OFFSET towerGame1, OFFSET towerGame2, OFFSET towerGame3, OFFSET towerGame4, OFFSET towerGame5, OFFSET towerGame6
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 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
towerGame8 db 196,243,240,224,234,0 towerDesc2 db 205,224,225,229,240,232,32,225,238,235,252,248,229,32,238,247,234,238,226,46,0
towerGame9 db 202,240,254,234,58,32,83,107,105,108,108,32,67,104,101,99,107,0 towerDesc3 db 206,225,251,227,240,224,233,32,234,240,243,239,252,229,32,226,32,50,49,46,0
towerGame10 db 66,117,99,107,115,104,111,116,32,82,111,117,108,101,116,116,101,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
towerGameNames dd OFFSET towerGame1, OFFSET towerGame2, OFFSET towerGame3, OFFSET towerGame4, OFFSET towerGame5, OFFSET towerGame6, OFFSET towerGame7, OFFSET towerGame8, OFFSET towerGame9, OFFSET towerGame10 towerDesc6 db 196,243,253,235,252,32,237,224,32,228,240,238,225,238,226,232,234,229,33,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 towerDescriptions dd OFFSET towerDesc1, OFFSET towerDesc2, OFFSET towerDesc3, OFFSET towerDesc4, OFFSET towerDesc5, OFFSET towerDesc6
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 towerHeader db 192,196,209,202,192,223,32,193,192,216,205,223,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 towerFloorPrefix db 221,210,192,198,32,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 towerOfTen db 32,47,32,54,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 towerEnemyLabel db 194,208,192,195,58,32,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 towerGameLabel db 200,195,208,192,58,32,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 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
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 towerLossWarning db 207,238,240,224,230,229,237,232,229,32,45,32,226,238,231,226,240,224,242,33,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 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
towerDescriptions dd OFFSET towerDesc1, OFFSET towerDesc2, OFFSET towerDesc3, OFFSET towerDesc4, OFFSET towerDesc5, OFFSET towerDesc6, OFFSET towerDesc7, OFFSET towerDesc8, OFFSET towerDesc9, OFFSET towerDesc10 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
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 towerNextBtn db 91,69,78,84,69,82,93,32,196,192,203,220,216,197,0
towerFloorPrefix db 221,210,192,198,32,0 towerDefeatTitle db 194,219,32,207,206,195,200,193,203,200,32,194,32,193,192,216,205,197,33,0
towerOfTen db 32,47,32,49,48,0 towerDefeatDesc db 196,243,248,224,32,241,227,238,240,229,235,224,32,226,32,192,228,243,46,0
towerEnemyLabel db 194,208,192,195,58,32,0 towerReturnBtn db 91,69,83,67,93,32,194,32,213,206,203,203,32,202,192,199,200,205,206,0
towerGameLabel db 200,195,208,192,58,32,0 towerChampTitle db 193,192,216,205,223,32,207,206,202,206,208,197,205,192,33,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 towerChampSubtitle db 194,251,32,45,32,194,235,224,228,251,234,224,32,193,224,248,237,232,33,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 towerPrizeText db 202,211,216,58,32,43,36,54,54,44,54,48,48,32,194,32,193,192,205,202,33,0
towerStartBtn db 91,207,208,206,193,197,203,93,32,194,32,193,206,201,0 towerHudBadge db 193,192,216,205,223,58,32,0
towerEscBtn db 91,69,83,67,93,32,209,193,197,198,192,210,220,0 towerHudOfTen db 47,54,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

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)
floor_idx = stage - 1 draw = ImageDraw.Draw(canvas)
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 & 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 elif state == 2: # CLEAR
draw.text((card_x + 10, 48), f"ЭТАЖ {stage} / 10", fill=(255, 102, 102)) 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 elif state == 3: # DEFEAT
draw.rectangle([card_x + 6, 64, card_x + card_w - 6, 126], fill=(30, 4, 4), outline=(136, 24, 24)) draw.rectangle([inner_x, 62, inner_x + inner_w, 142], fill=(42, 4, 4), outline=(255, 34, 34), width=2)
draw.text((card_x + 10, 68), f"ВРАГ: {BOSSES[floor_idx]}", fill=(255, 255, 255)) dft_w = len("ВЫ ПОГИБЛИ В БАШНЕ!") * 8
draw.text((card_x + 10, 82), f"ИГРА: {GAMES[floor_idx]}", fill=(255, 221, 102)) draw.text((inner_x + (inner_w - dft_w) // 2, 76), "ВЫ ПОГИБЛИ В БАШНЕ!", fill=(255, 68, 68))
draw.text((card_x + 10, 98), DESCS[floor_idx], fill=(204, 204, 204)) 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 elif state == 4: # CHAMPION
draw.text((card_x + 10, 132), "10 битв подряд без права на ошибку", fill=(255, 170, 51)) draw.rectangle([inner_x, 58, inner_x + inner_w, 150], fill=(51, 34, 0), outline=(255, 215, 0), width=2)
draw.text((card_x + 10, 146), "Поражение - возврат в холл казино", fill=(238, 68, 68)) chm_w = len("БАШНЯ ПОКОРЕНА!") * 8
draw.text((inner_x + (inner_w - chm_w) // 2, 70), "БАШНЯ ПОКОРЕНА!", fill=(255, 224, 102))
# Bankroll & Prize sub_w = len("Вы - Владыка Башни!") * 8
draw.text((card_x + 10, 162), "БАНК: $10,000", fill=(102, 255, 102)) draw.text((inner_x + (inner_w - sub_w) // 2, 90), "Вы - Владыка Башни!", fill=(255, 255, 255))
draw.text((card_x + 110, 162), "КУШ: $66,600", fill=(255, 221, 68)) prz_w = len("КУШ: +$66,600 В БАНК!") * 8
draw.text((inner_x + (inner_w - prz_w) // 2, 114), "КУШ: +$66,600 В БАНК!", fill=(102, 255, 102))
# Buttons
draw.rectangle([card_x + 8, 184, card_x + 100, 206], fill=(61, 12, 12), outline=(255, 68, 68)) draw.rectangle([card_x + 18, 176, card_x + 188, 202], fill=(68, 51, 0), outline=(255, 204, 0))
draw.text((card_x + 14, 191), "[ПРОБЕЛ] В БОЙ", fill=(255, 255, 255)) btn_w2 = len("[ESC] В ХОЛЛ КАЗИНО") * 8
draw.text((card_x + 18 + (170 - btn_w2) // 2, 184), "[ESC] В ХОЛЛ КАЗИНО", 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
# 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") clr = render_menu_frame(1, state=2)
print("[OK] Saved helltower_rendered_floor5.png") clr.save(ART_DIR / "helltower_6fl_clear.png")
dft = render_menu_frame(1, state=3)
f10 = render_menu_frame(10, 100) dft.save(ART_DIR / "helltower_6fl_defeat.png")
f10.save(ART_DIR / "helltower_rendered_floor10.png") chp = render_menu_frame(6, state=4)
print("[OK] Saved helltower_rendered_floor10.png") 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")