"""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()