KAZIK/tools/tests/render_hell_tower_preview.py
Игорь Федосеев eef2039c7e feat(hell_tower): integrate custom hell sprites, redesign tower menu, and fix entrance crash
- 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
2026-10-26 19:44:06 +03:00

115 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Render full simulation of Hell Tower menu at floors 1, 5, 10."""
import struct
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
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]
BOSSES = [
"БЕС АЗАРТА", "ДЕМОН КОСТЕЙ", "ЧЕРНОКНИЖНИК", "КРУПЬЕ ПРЕИСПОДНЕЙ", "ДЕМОН БЛЕФА",
"ОБРЕЧЕННЫЙ ГРЕШНИК", "КОЛЕСО СУДЬБЫ", "КАРТОЧНЫЙ ЧЕРТ", "ПАЛАЧ КРЮКА", "ВЛАДЫКА БАШНИ"
]
GAMES = [
"Орел и решка", "Кости", "Черепа и шарик", "21 / Очко", "Покер",
"Русская рулетка", "Казино-рулетка", "Дурак", "Крюк: Skill Check", "Buckshot Roulette"
]
DESCS = [
"Угадай сторону монеты.",
"Перебрось демона в сумме костей.",
"Найди душу под черепом.",
"Собери 21 очко и победи крупье.",
"Вскрой карты и победи в покер.",
"1 патрон в барабане: выживи.",
"Сделай ставку на рулетке.",
"Оставь чёрта в дураках.",
"Попади в зону и сорвись с крюка.",
"Дуэль на дробовиках за жизнь!"
]
def render_menu_frame(stage: int, callout_anim: int = 100):
# 1. Load background from ANM1 pack
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
target_y = FLOOR_Y[floor_idx]
# Calculate sliding card box
card_h = 208
card_y = 20
card_w = max(10, 206 * callout_anim // 100)
card_x = 214 - card_w
# Connector line
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
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)
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))
# Floor indicator
draw.text((card_x + 10, 48), f"ЭТАЖ {stage} / 10", fill=(255, 102, 102))
# 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))
# 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
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")
if __name__ == "__main__":
main()