← Creature Collection results

Creature Collection

Gemma 4 31b · composer

Initial view of Creature Collection

Reviews & generation details

Reviews

Human review · Jul 17, 2026 · rubric 1

Historical path-based review. Association with these exact bytes is unverified.

Visual design
2 / 5
UX / feel
3 / 5
Works
2 / 5
Prompt spirit
2 / 5
Delight
1 / 5

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 29,044 bytes · SHA-256 84f19463b464
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Aetheria: The Resonance Binding</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&amp;family=JetBrains+Mono:wght@500&amp;display=swap">
<style>
/* ── css: layout ── */

:root {
    --primary: #a29bfe;
    --secondary: #6c5ce7;
    --accent: #fd79a8;
    --bg-dark: #2d3436;
    --glass: rgba(255, 255, 255, 0.1);
    --glass-border: rgba(255, 255, 255, 0.2);
    --text-main: #dfe6e9;
    --text-dim: #b2bec3;
    --font-main: 'Inter', sans-serif;
    --font-mono: 'JetBrains Mono', monospace;
}

* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; }

body {
    background: #000;
    color: var(--text-main);
    font-family: var(--font-main);
    overflow: hidden;
    width: 100vw;
    height: 100vh;
}

#game-container {
    position: relative;
    width: 100vw;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}

#game-canvas {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    image-rendering: pixelated;
    width: 100%;
    height: 100%;
}

#ui-layer {
    position: relative;
    z-index: 10;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

.screen {
    position: absolute;
    inset: 0;
    display: none;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    pointer-events: all;
    background: transparent;
    transition: opacity 0.5s ease;
}

.screen.active { display: flex; }

/* Title Screen */
.title-card {
    text-align: center;
    margin-bottom: 3rem;
    animation: float 4s ease-in-out infinite;
}

.title-card h1 {
    font-size: 5rem;
    letter-spacing: 1rem;
    color: #fff;
    text-shadow: 0 0 20px var(--primary), 0 0 40px var(--secondary);
    font-weight: 700;
}

.subtitle {
    font-size: 1.2rem;
    color: var(--text-dim);
    letter-spacing: 0.3rem;
    text-transform: uppercase;
}

.menu-actions {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    width: 300px;
}

.menu-btn {
    background: var(--glass);
    border: 1px solid var(--glass-border);
    color: #fff;
    padding: 1rem;
    font-family: var(--font-main);
    font-size: 1.1rem;
    cursor: pointer;
    backdrop-filter: blur(10px);
    transition: 0.3s;
    border-radius: 4px;
}

.menu-btn:hover:not(:disabled) {
    background: var(--secondary);
    transform: scale(1.05);
}

.menu-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Dialogue */
.dialogue-box {
    position: absolute;
    bottom: 2rem;
    width: 80%;
    max-width: 800px;
    background: rgba(0,0,0,0.8);
    border: 2px solid var(--primary);
    padding: 2rem;
    border-radius: 8px;
    min-height: 150px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

.dialogue-text {
    font-size: 1.2rem;
    line-height: 1.6;
}

.dialogue-next {
    text-align: right;
    font-size: 0.9rem;
    color: var(--primary);
    animation: pulse 1.5s infinite;
}

/* HUD */
.hud {
    position: absolute;
    inset: 0;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: 2rem;
}

.hud.hidden { display: none; }

.hud-top {
    display: flex;
    justify-content: space-between;
    pointer-events: all;
}

.location-tag {
    background: var(--glass);
    padding: 0.5rem 1.5rem;
    border-radius: 20px;
    border: 1px solid var(--glass-border);
    backdrop-filter: blur(5px);
    font-weight: 700;
}

.hud-bottom {
    display: flex;
    justify-content: center;
    gap: 1rem;
    pointer-events: all;
}

.hud-icon {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: var(--glass);
    border: 1px solid var(--glass-border);
    color: #fff;
    font-size: 1.5rem;
    cursor: pointer;
    backdrop-filter: blur(10px);
    transition: 0.2s;
}

.hud-icon:hover {
    background: var(--secondary);
    transform: translateY(-5px);
}

/* Battle UI */
.battle-arena {
    position: relative;
    width: 100%;
    height: 60%;
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding: 0 10%;
}

.battle-side {
    position: relative;
    width: 300px;
    height: 300px;
}

.battle-controls {
    position: absolute;
    bottom: 0;
    width: 100%;
    height: 40%;
    background: rgba(0,0,0,0.7);
    border-top: 3px solid var(--primary);
    display: flex;
    padding: 2rem;
    gap: 2rem;
}

.battle-menu {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    width: 400px;
}

.battle-nav {
    background: var(--glass);
    border: 1px solid var(--glass-border);
    color: #fff;
    padding: 1.5rem;
    font-size: 1.2rem;
    cursor: pointer;
    border-radius: 4px;
    transition: 0.2s;
}

.battle-nav:hover {
    background: var(--primary);
}

.move-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    width: 400px;
}

.move-btn {
    background: var(--glass);
    border: 1px solid var(--glass-border);
    color: #fff;
    padding: 1.5rem;
    text-align: left;
    cursor: pointer;
    border-radius: 4px;
    transition: 0.2s;
    display: flex;
    flex-direction: column;
}

.move-btn:hover {
    background: var(--secondary);
}

.move-btn .cost {
    font-size: 0.8rem;
    opacity: 0.7;
    text-align: right;
}

.battle-log {
    flex: 1;
    background: rgba(0,0,0,0.4);
    border: 1px solid var(--glass-border);
    padding: 1rem;
    overflow-y: auto;
    font-family: var(--font-mono);
    font-size: 1rem;
    border-radius: 8px;
}

/* Unit Bars */
.unit-card {
    position: absolute;
    width: 200px;
    background: var(--glass);
    border: 1px solid var(--glass-border);
    padding: 10px;
    border-radius: 8px;
    backdrop-filter: blur(10px);
}

.enemy-card { top: 0; right: 0; }
.player-card { bottom: 0; left: 0; }

.unit-name {
    font-weight: bold;
    margin-bottom: 5px;
    display: flex;
    justify-content: space-between;
}

.hp-bar-bg {
    background: #444;
    height: 10px;
    border-radius: 5px;
    overflow: hidden;
}

.hp-bar-fill {
    background: linear-gradient(90deg, #ff7675, #fdcb6e);
    height: 100%;
    transition: width 0.4s ease;
}

/* Modals */
.modal-overlay {
    position: absolute;
    inset: 0;
    background: rgba(0,0,0,0.8);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 100;
    pointer-events: all;
}

.modal-content {
    background: rgba(45, 52, 54, 0.9);
    border: 2px solid var(--primary);
    width: 80%;
    max-width: 600px;
    max-height: 80vh;
    border-radius: 12px;
    padding: 2rem;
    position: relative;
    overflow-y: auto;
    backdrop-filter: blur(20px);
}

.modal-close {
    position: sticky;
    bottom: 0;
    width: 100%;
    padding: 1rem;
    margin-top: 1rem;
    background: var(--secondary);
    border: none;
    color: #fff;
    cursor: pointer;
    border-radius: 4px;
}

/* Capture Game */
.capture-ring-container {
    position: relative;
    width: 300px;
    height: 300px;
    display: flex;
    justify-content: center;
    align-items: center;
}

.capture-ring {
    position: absolute;
    border: 4px solid #fff;
    border-radius: 50%;
    pointer-events: none;
}

.capture-target {
    width: 50px;
    height: 50px;
    background: var(--accent);
    border-radius: 50%;
    box-shadow: 0 0 20px var(--accent);
}

.capture-hint {
    position: absolute;
    bottom: 20%;
    font-size: 1.2rem;
    color: #fff;
}

/* Audio Toggle */
.audio-toggle {
    position: absolute;
    top: 2rem;
    right: 2rem;
    z-index: 1000;
    background: var(--glass);
    border: 1px solid var(--glass-border);
    padding: 0.5rem 1rem;
    border-radius: 20px;
    pointer-events: all;
    cursor: pointer;
    backdrop-filter: blur(10px);
}

/* Animations */
@keyframes float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-20px); }
}

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

.hidden { display: none !important; }
</style>
</head>
<body>
<div id="game-container">
    <div id="ui-layer">
        <!-- Main Menu -->
        <div id="screen-menu" class="screen active">
            <div class="title-card">
                <h1>AETHERIA</h1>
                <p class="subtitle">The Resonance Binding</p>
            </div>
            <div class="menu-actions">
                <button id="btn-new-game" class="menu-btn">Begin New Binding</button>
                <button id="btn-continue" class="menu-btn" disabled="">Continue Journey</button>
                <button id="btn-options" class="menu-btn">Resonance Settings</button>
            </div>
        </div>

        <!-- Dialogue/Cutscene Overlay -->
        <div id="screen-dialogue" class="screen">
            <div class="dialogue-box">
                <p id="dialogue-text"></p>
                <span class="dialogue-next">Press SPACE or Tap to continue →</span>
            </div>
        </div>

        <!-- Overworld HUD -->
        <div id="hud-overworld" class="hud hidden">
            <div class="hud-top">
                <div class="location-tag" id="location-name">The Whispering Hollows</div>
                <div class="game-time" id="game-clock">00:00</div>
            </div>
            <div class="hud-bottom">
                <button id="btn-open-bag" class="hud-icon">🎒</button>
                <button id="btn-open-team" class="hud-icon">👥</button>
                <button id="btn-open-index" class="hud-icon">📖</button>
                <button id="btn-open-map" class="hud-icon">🗺️</button>
            </div>
        </div>

        <!-- Battle UI -->
        <div id="screen-battle" class="screen">
            <div class="battle-arena">
                <div id="enemy-side" class="battle-side"></div>
                <div id="player-side" class="battle-side"></div>
            </div>
            <div class="battle-controls">
                <div class="battle-menu">
                    <button id="battle-btn-attack" class="battle-nav">Resonate</button>
                    <button id="battle-btn-item" class="battle-nav">Essence</button>
                    <button id="battle-btn-switch" class="battle-nav">Shift</button>
                    <button id="battle-btn-run" class="battle-nav">Recede</button>
                </div>
                <div id="move-selection" class="move-grid hidden"></div>
                <div id="battle-log" class="battle-log"></div>
            </div>
        </div>

        <!-- Modal Menus (Glassmorphism) -->
        <div id="modal-overlay" class="modal-overlay hidden">
            <div class="modal-content">
                <div id="modal-body"></div>
                <button id="modal-close" class="modal-close">Close</button>
            </div>
        </div>

        <!-- Capture Mini-game -->
        <div id="screen-capture" class="screen">
            <div class="capture-ring-container">
                <div id="capture-ring-outer" class="capture-ring"></div>
                <div id="capture-ring-inner" class="capture-ring"></div>
                <div class="capture-target"></div>
            </div>
            <p class="capture-hint">Tap when the rings align!</p>
        </div>

        <!-- Audio Toggle -->
        <div id="audio-toggle" class="audio-toggle">
            <span id="audio-status">🔊</span>
        </div>
    </div>
    <canvas id="game-canvas"></canvas>
</div>
<script>
'use strict';

// ── const: GAME_CONFIG ──
const GAME_CONFIG = {
    TYPES: {
        'Solar': { weakness: 'Lunar', strength: 'Flora', resist: 'Wind', color: '#f1c40f' },
        'Lunar': { weakness: 'Flora', strength: 'Solar', resist: 'Wind', color: '#9b59b6' },
        'Flora': { weakness: 'Solar', strength: 'Lunar', resist: 'Void', color: '#2ecc71' },
        'Wind': { weakness: 'Void', strength: 'Solar', resist: 'Flora', color: '#3498db' },
        'Void': { weakness: 'Shatter', strength: 'Wind', resist: 'Solar', color: '#2c3e50' },
        'Shatter': { weakness: 'Aether', strength: 'Void', resist: 'Lunar', color: '#e74c3c' },
        'Aether': { weakness: 'Shatter', strength: 'None', resist: 'All', color: '#ecf0f1' }
    },
    ZONES: [
        { id: 0, name: 'Azure Glade', color: 'linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d)', encounterRate: 0.08, boss: 'The Gilded Talon' },
        { id: 1, name: 'Echoing Spires', color: 'linear-gradient(135deg, #2c3e50, #4ca1af)', encounterRate: 0.1, boss: 'The Silent Gale' },
        { id: 2, name: 'Crimson Basin', color: 'linear-gradient(135deg, #870000, #190a05)', encounterRate: 0.12, boss: 'The Molten Core' },
        { id: 3, name: 'Obsidian Veins', color: 'linear-gradient(135deg, #000000, #434343)', encounterRate: 0.15, boss: 'The Void Weaver' },
        { id: 4, name: 'Luminous Reach', color: 'linear-gradient(135deg, #f9d423, #ff4e50)', encounterRate: 0.18, boss: 'The Prism Sentinel' },
        { id: 5, name: 'Zenith Apex', color: 'linear-gradient(135deg, #000428, #004e92)', encounterRate: 0.2, boss: 'The Eternal Harmony' }
    ]
};

// ── class: GameState ──
class GameState {
    constructor() {
        this.player = {
            pos: { x: 5, y: 5 },
            zone: 0,
            party: [],
            inventory: [],
            index: [],
            flags: {},
            saveSlot: 0
        };
        this.currentScreen = 'menu';
        this.battle = null;
        this.dialogueQueue = [];
        this.lastShrine = { x: 5, y: 5, zone: 0 };
        this.audioEnabled = true;
    }

    save() {
        localStorage.setItem('aetheria_save', JSON.stringify(this));
    }

    load() {
        const saved = localStorage.getItem('aetheria_save');
        if (saved) {
            const data = JSON.parse(saved);
            Object.assign(this, data);
            return true;
        }
        return false;
    }
}

// ── class: GameRenderer ──
class GameRenderer {
    constructor(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.resize();
        window.addEventListener('resize', () => this.resize());
    }

    resize() {
        this.canvas.width = window.innerWidth * window.devicePixelRatio;
        this.canvas.height = window.innerHeight * window.devicePixelRatio;
        this.ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
    }

    clear() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }

    drawOverworld(state) {
        const { x, y } = state.player.pos;
        const zone = GAME_CONFIG.ZONES[state.player.zone];
        
        // Background Gradient
        const grad = this.ctx.createLinearGradient(0, 0, 0, window.innerHeight);
        // Since CSS handles the page backdrop, we might just draw a tile grid or particles
        
        const tileSize = 64;
        this.ctx.strokeStyle = 'rgba(255,255,255,0.1)';
        this.ctx.beginPath();
        for(let i=0; i<window.innerWidth; i+=tileSize) {
            this.ctx.moveTo(i, 0);
            this.ctx.lineTo(i, window.innerHeight);
        }
        for(let i=0; i<window.innerHeight; i+=tileSize) {
            this.ctx.moveTo(0, i);
            this.ctx.lineTo(window.innerWidth, i);
        }
        this.ctx.stroke();

        // Player
        this.ctx.fillStyle = '#fff';
        this.ctx.shadowBlur = 15;
        this.ctx.shadowColor = varColor('--primary');
        this.ctx.beginPath();
        this.ctx.arc(
            (window.innerWidth/2), 
            (window.innerHeight/2), 
            12, 0, Math.PI*2
        );
        this.ctx.fill();
        this.ctx.shadowBlur = 0;
    }
    
    drawBattle(state) {
        // Battle rendering is largely DOM-based for these specs
    }
}

// ── function: varColor ──
function varColor(varName) {
    return getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
}

// ── class: Creature ──
class Creature {
    constructor(id, name, types, stats, abilities, lore) {
        this.id = id;
        this.name = name;
        this.types = types;
        this.baseStats = stats;
        this.abilities = abilities;
        this.lore = lore;
    }

    createInstance(level = 1) {
        return {
            name: this.name,
            types: this.types,
            level: level,
            hp: this.calculateStat('hp', level),
            maxHp: this.calculateStat('hp', level),
            atk: this.calculateStat('atk', level),
            def: this.calculateStat('def', level),
            spd: this.calculateStat('spd', level),
            abilities: this.abilities.map(a => ({ ...a })),
            status: null,
            exp: 0,
            nextLevel: 100 * level
        };
    }

    calculateStat(stat, level) {
        const base = this.baseStats[stat];
        if (stat === 'hp') return Math.floor(base * (level / 5 + 2));
        return Math.floor(base + (level * 1.5));
    }
}

// ── const: MOVE_DB ──
const MOVE_DB = {
    'Solar Flash': { power: 40, acc: 95, type: 'Solar', cost: 10, effect: 'none' },
    'Lunar Pulse': { power: 40, acc: 95, type: 'Lunar', cost: 10, effect: 'none' },
    'Vine Whip': { power: 40, acc: 90, type: 'Flora', cost: 10, effect: 'none' },
    'Zephyr Gust': { power: 40, acc: 90, type: 'Wind', cost: 10, effect: 'none' },
    'Void Maw': { power: 60, acc: 80, type: 'Void', cost: 20, effect: 'none' },
    'Crystal Edge': { power: 60, acc: 85, type: 'Shatter', cost: 20, effect: 'none' },
    'Aether Breath': { power: 70, acc: 90, type: 'Aether', cost: 30, effect: 'none' },
    'Nova Burst': { power: 110, acc: 75, type: 'Solar', cost: 40, effect: 'none' },
    'Eclipse Veil': { power: 110, acc: 75, type: 'Lunar', cost: 40, effect: 'none' },
    'Root Bind': { power: 40, acc: 80, type: 'Flora', cost: 15, effect: 'frost' },
    'Shatter Step': { power: 50, acc: 100, type: 'Shatter', cost: 15, effect: 'priority' },
    'Cosmic Heal': { power: 0, acc: 100, type: 'Aether', cost: 20, effect: 'heal' },
    'Void Pulse': { power: 80, acc: 90, type: 'Void', cost: 30, effect: 'shock' },
    'Gale Force': { power: 90, acc: 85, type: 'Wind', cost: 30, effect: 'none' },
    'Sun Beam': { power: 120, acc: 70, type: 'Solar', cost: 50, effect: 'burn' },
    'Moon Sliver': { power: 120, acc: 70, type: 'Lunar', cost: 50, effect: 'none' },
};

// ── const: CREATURE_DB ──
const CREATURE_DB = {
    'Solaris': new Creature('Solaris', 'Solaris', ['Solar'], { hp: 50, atk: 12, def: 10, spd: 11 }, ['Solar Flash', 'Shatter Step'], 'A glimmering orb of perpetual dawn.'),
    'Lunaris': new Creature('Lunaris', 'Lunaris', ['Lunar'], { hp: 55, atk: 11, def: 11, spd: 10 }, ['Lunar Pulse', 'Shatter Step'], 'A silent mirror of the night sky.'),
    'Floris': new Creature('Floris', 'Floris', ['Flora'], { hp: 60, atk: 10, def: 12, spd: 9 }, ['Vine Whip', 'Shatter Step'], 'Rooted in the essence of the first forest.'),
    'Ventus': new Creature('Ventus', 'Ventus', ['Wind'], { hp: 45, atk: 13, def: 8, spd: 15 }, ['Zephyr Gust', 'Shatter Step'], 'A whirlwind given consciousness.'),
    'Voidon': new Creature('Voidon', 'Voidon', ['Void'], { hp: 70, atk: 14, def: 12, spd: 7 }, ['Void Maw', 'Shatter Step'], 'An entity from the spaces between stars.'),
    'Krystos': new Creature('Krystos', 'Krystos', ['Shatter'], { hp: 65, atk: 15, def: 15, spd: 6 }, ['Crystal Edge', 'Shatter Step'], 'Living geometry of hardened silence.'),
    'Aethon': new Creature('Aethon', 'Aethon', ['Aether'], { hp: 100, atk: 20, def: 20, spd: 20 }, ['Aether Breath', 'Cosmic Heal'], 'The primordial breath of the world.'),
};

// ── const: TRANSFORMATIONS ──
const TRANSFORMATIONS = {
    'Solaris' : { level: 20, item: 'Solar Catalyst', result: 'Super Solaris' },
    'Lunaris' : { level: 20, item: 'Lunar Catalyst', result: 'Super Lunaris' },
    'Floris' : { level: 20, item: 'Flora Catalyst', result: 'Super Floris' },
};

// ── class: BattleEngine ──
class BattleEngine {
    constructor(playerParty, enemyParty, onLog, onUpdate) {
        this.playerParty = playerParty;
        this.enemyParty = enemyParty;
        this.onLog = onLog;
        this.onUpdate = onUpdate;
        this.turnQueue = [];
        this.currentTurnUnit = null;
        this.phase = 'init';
    }

    init() {
        this.refreshTurnQueue();
        this.nextTurn();
    }

    refreshTurnQueue() {
        const all = [
            ...this.playerParty.map((u, i) => ({ unit: u, side: 'player', index: i })),
            ...this.enemyParty.map((u, i) => ({ unit: u, side: 'enemy', index: i }))
        ].filter(entry => entry.unit.hp > 0);

        this.turnQueue = all.sort((a, b) => b.unit.spd - a.unit.spd);
    }

    nextTurn() {
        if (this.turnQueue.length === 0) {
            this.refreshTurnQueue();
            if (this.turnQueue.length === 0) return;
        }

        this.currentTurnUnit = this.turnQueue.shift();
        
        if (this.currentTurnUnit.unit.hp <= 0) {
            this.nextTurn();
            return;
        }

        if (this.currentTurnUnit.side === 'player') {
            this.phase = 'player_turn';
            this.onUpdate();
        } else {
            this.phase = 'enemy_turn';
            this.processEnemyAI();
        }
    }

    processEnemyAI() {
        const target = this.playerParty.find(u => u.hp > 0);
        if (!target) return;
        const moves = this.currentTurnUnit.unit.abilities;
        const moveName = moves[Math.floor(Math.random() * moves.length)];
        this.executeMove(this.currentTurnUnit, target, moveName);
    }

    executeMove(attackerEntry, targetUnit, moveName) {
        const move = MOVE_DB[moveName];
        if (!move) return;

        const attacker = attackerEntry.unit;
        this.onLog(`${attacker.name} used ${moveName}!`);

        if (Math.random() * 100 > move.acc) {
            this.onLog(`It missed!`);
            this.resolveTurn();
            return;
        }

        let multiplier = 1;
        const typeChart = GAME_CONFIG.TYPES;
        const aType = attacker.types[0];
        const tType = targetUnit.types[0];
        
        if (typeChart[aType] && typeChart[aType].strength === tType) multiplier *= 2;
        if (typeChart[aType] && typeChart[aType].weakness === tType) multiplier *= 0.5;
        if (attacker.types.includes(move.type)) multiplier *= 1.25;

        const damage = Math.floor((0.4 * attacker.level * attacker.atk / targetUnit.def * multiplier) + (Math.random() * 5));
        targetUnit.hp = Math.max(0, targetUnit.hp - damage);
        this.onLog(`${targetUnit.name} took ${damage} damage!`);

        this.onUpdate();
        this.resolveTurn();
    }

    resolveTurn() {
        setTimeout(() => this.nextTurn(), 1000);
    }
}

// ── main ──

const state = new GameState();
const canvas = document.getElementById('game-canvas');
const renderer = new GameRenderer(canvas);

function showScreen(id) {
    document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
    const target = document.getElementById('screen-' + id);
    if (target) target.classList.add('active');
    state.currentScreen = id;
}

function addDialogue(text) {
    state.dialogueQueue.push(text);
    if (state.currentScreen !== 'dialogue') {
        showScreen('dialogue');
    }
    updateDialogue();
}

function updateDialogue() {
    const el = document.getElementById('dialogue-text');
    if (state.dialogueQueue.length > 0) {
        el.innerText = state.dialogueQueue[0];
    } else {
        if (state.currentScreen === 'dialogue') {
            showScreen('menu'); 
        }
    }
}

function handleDialogueNext() {
    if (state.currentScreen === 'dialogue') {
        const current = state.dialogueQueue[0];
        state.dialogueQueue.shift();
        if (current === 'END_GAME_START') {
            showScreen('overworld');
            document.getElementById('hud-overworld').classList.remove('hidden');
            document.body.style.background = GAME_CONFIG.ZONES[0].color;
        } else {
            updateDialogue();
        }
    }
}

function loop() {
    renderer.clear();
    if (state.currentScreen === 'overworld') {
        renderer.drawOverworld(state);
    }
    requestAnimationFrame(loop);
}

window.addEventListener('keydown', (e) => {
    if (state.currentScreen === 'dialogue') {
        if (e.code === 'Space' || e.code === 'Enter') handleDialogueNext();
        return;
    }
    if (state.currentScreen !== 'overworld') return;

    let dx = 0, dy = 0;
    if (e.key === 'w' || e.key === 'ArrowUp') dy = -1;
    if (e.key === 's' || e.key === 'ArrowDown') dy = 1;
    if (e.key === 'a' || e.key === 'ArrowLeft') dx = -1;
    if (e.key === 'd' || e.key === 'ArrowRight') dx = 1;

    if (dx !== 0 || dy !== 0) {
        state.player.pos.x += dx;
        state.player.pos.y += dy;
        
        const zone = GAME_CONFIG.ZONES[state.player.zone];
        if (Math.random() < zone.encounterRate) {
            startBattle();
        }
    }
});

function startBattle() {
    showScreen('battle');
    
    const keys = Object.keys(CREATURE_DB);
    const enemyC = CREATURE_DB[keys[Math.floor(Math.random() * keys.length)]].createInstance(Math.floor(Math.random() * 5) + 1);
    
    const enemySide = document.getElementById('enemy-side');
    enemySide.innerHTML = `
        <div class="unit-card enemy-card">
            <div class="unit-name"><span>${enemyC.name}</span><span>Lv.${enemyC.level}</span></div>
            <div class="hp-bar-bg"><div id="enemy-hp-fill" class="hp-bar-fill" style="width: 100%"></div></div>
        </div>
    `;

    const playerSide = document.getElementById('player-side');
    const pC = state.player.party[0] || CREATURE_DB['Solaris'].createInstance(5);
    playerSide.innerHTML = `
        <div class="unit-card player-card">
            <div class="unit-name"><span>${pC.name}</span><span>Lv.${pC.level}</span></div>
            <div class="hp-bar-bg"><div id="player-hp-fill" class="hp-bar-fill" style="width: 100%"></div></div>
        </div>
    `;

    const engine = new BattleEngine(
        [pC], 
        [enemyC], 
        (msg) => {
            const log = document.getElementById('battle-log');
            log.innerHTML += `<div>${msg}</div>`;
            log.scrollTop = log.scrollHeight;
        },
        () => {
            const eFill = document.getElementById('enemy-hp-fill');
            const pFill = document.getElementById('player-hp-fill');
            if(eFill) eFill.style.width = (enemyC.hp / enemyC.maxHp * 100) + '%';
            if(pFill) pFill.style.width = (pC.hp / pC.maxHp * 100) + '%';
            
            if (enemyC.hp <= 0) {
                setTimeout(() => {
                    alert('Victory!');
                    showScreen('overworld');
                }, 1000);
            }
            if (pC.hp <= 0) {
                setTimeout(() => {
                    alert('You were defeated!');
                    showScreen('overworld');
                    pC.hp = pC.maxHp; // Simple heal to avoid lock
                }, 1000);
            }
        }
    );
    
    engine.init();

    document.getElementById('battle-btn-attack').onclick = () => {
        const moveSelection = document.getElementById('move-selection');
        moveSelection.classList.remove('hidden');
        moveSelection.innerHTML = '';
        pC.abilities.forEach(moveName => {
            const btn = document.createElement('button');
            btn.className = 'move-btn';
            btn.innerHTML = `<span>${moveName}</span><span class="cost">${MOVE_DB[moveName]?.cost || 0} E</span>`;
            btn.onclick = () => {
                engine.executeMove({ unit: pC, side: 'player' }, enemyC, moveName);
                moveSelection.classList.add('hidden');
            };
            moveSelection.appendChild(btn);
        });
    };

    document.getElementById('battle-btn-run').onclick = () => {
        showScreen('overworld');
    };
}

document.getElementById('btn-new-game').onclick = () => {
    state.player.party = [CREATURE_DB['Solaris'].createInstance(5)];
    addDialogue("Welcome to Aetheria...");
    addDialogue("You have awakened as a Resonator, one who can bind with the spirits of this world.");
    addDialogue("Use WASD to explore the glades and find creatures to bind.");
    addDialogue("END_GAME_START");
};

document.getElementById('audio-toggle').onclick = () => {
    state.audioEnabled = !state.audioEnabled;
    document.getElementById('audio-status').innerText = state.audioEnabled ? '🔊' : '🔇';
};

loop();
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"composer","sessionId":"59031951-111a-4084-95d0-1210ba9453c0","tokensIn":58822,"tokensOut":14060,"tokensTotal":72882,"turns":4,"toolCalls":15,"failedToolCalls":2,"timestamp":"2026-07-14T16:14:07.861Z"} -->