← Aethelgard results

Aethelgard

Gemma 4 31b · basic

Initial view of Aethelgard

Browser check: Artifact runtime error. This is a technical check, not a quality review.

Reviews & generation details

Reviews

Human review · Jul 16, 2026 · rubric 1

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

Visual design
4 / 5
UX / feel
3 / 5
Works
3 / 5
Prompt spirit
4 / 5
Delight
3 / 5
Excellent design; original creature svg artwork; some glitchy controls and no way to use potions

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 40,557 bytes · SHA-256 62b5c0919b08
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Aethelgard: Resonance</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&family=Press+Start+2P&display=swap" rel="stylesheet">
    <style>
        :root {
            --color-pyro: #ff4d4d;
            --color-hydro: #4da6ff;
            --color-flora: #4dff88;
            --color-zephyr: #e6e6e6;
            --color-earth: #daa520;
            --color-volt: #ffff4d;
            --color-cosmos: #bc4dff;
            --glass: rgba(15, 23, 42, 0.7);
        }
        body {
            font-family: 'Inter', sans-serif;
            background: radial-gradient(circle at center, #1e1b4b 0%, #0f172a 100%);
            color: #f1f5f9;
            overflow: hidden;
            user-select: none;
        }
        .retro-font { font-family: 'Press Start 2P', cursive; }
        .glass-panel {
            background: var(--glass);
            backdrop-filter: blur(12px);
            border: 1px solid rgba(255, 255, 255, 0.1);
            box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.8);
        }
        .element-glow-pyro { border-color: var(--color-pyro); box-shadow: 0 0 15px var(--color-pyro); }
        .element-glow-hydro { border-color: var(--color-hydro); box-shadow: 0 0 15px var(--color-hydro); }
        .element-glow-flora { border-color: var(--color-flora); box-shadow: 0 0 15px var(--color-flora); }
        .element-glow-zephyr { border-color: var(--color-zephyr); box-shadow: 0 0 15px var(--color-zephyr); }
        .element-glow-earth { border-color: var(--color-earth); box-shadow: 0 0 15px var(--color-earth); }
        .element-glow-volt { border-color: var(--color-volt); box-shadow: 0 0 15px var(--color-volt); }
        .element-glow-cosmos { border-color: var(--color-cosmos); box-shadow: 0 0 15px var(--color-cosmos); }

        .hp-bar-inner { transition: width 0.5s ease-out, background-color 0.3s ease; }
        
        @keyframes bounce-gentle {
            0%, 100% { transform: translateY(0); }
            50% { transform: translateY(-10px); }
        }
        .animate-bounce-gentle { animation: bounce-gentle 3s infinite ease-in-out; }
        
        @keyframes shake {
            0%, 100% { transform: translateX(0); }
            25% { transform: translateX(-5px); }
            75% { transform: translateX(5px); }
        }
        .screen-shake { animation: shake 0.2s ease-in-out 0s 2; }

        .node-active { cursor: pointer; filter: drop-shadow(0 0 8px rgba(255,255,255,0.5)); transition: 0.3s; }
        .node-active:hover { transform: scale(1.2); filter: drop-shadow(0 0 12px white); }
        .node-locked { opacity: 0.4; cursor: not-allowed; }

        .fade-in { animation: fadeIn 0.4s ease-out; }
        @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
    </style>
</head>
<body>

    <div id="app" class="h-screen w-screen relative overflow-hidden flex items-center justify-center p-4">
        <!-- Screens will be injected here by the Game State Manager -->
    </div>

    <script>
        /** 
         * WEB AUDIO SYNTHESIZER 
         */
        class SoundEngine {
            constructor() {
                this.ctx = new (window.AudioContext || window.webkitAudioContext)();
            }
            play(type) {
                const osc = this.ctx.createOscillator();
                const gain = this.ctx.createGain();
                osc.connect(gain);
                gain.connect(this.ctx.destination);

                const now = this.ctx.currentTime;
                switch(type) {
                    case 'hover':
                        osc.type = 'sine';
                        osc.frequency.setValueAtTime(440, now);
                        osc.frequency.exponentialRampToValueAtTime(880, now + 0.1);
                        gain.gain.setValueAtTime(0.1, now);
                        gain.gain.exponentialRampToValueAtTime(0.01, now + 0.1);
                        osc.start(); osc.stop(now + 0.1);
                        break;
                    case 'select':
                        osc.type = 'square';
                        osc.frequency.setValueAtTime(523.25, now); 
                        osc.frequency.exponentialRampToValueAtTime(1046.5, now + 0.1);
                        gain.gain.setValueAtTime(0.1, now);
                        gain.gain.exponentialRampToValueAtTime(0.01, now + 0.1);
                        osc.start(); osc.stop(now + 0.1);
                        break;
                    case 'hit':
                        osc.type = 'sawtooth';
                        osc.frequency.setValueAtTime(150, now);
                        osc.frequency.linearRampToValueAtTime(50, now + 0.2);
                        gain.gain.setValueAtTime(0.2, now);
                        gain.gain.linearRampToValueAtTime(0, now + 0.2);
                        osc.start(); osc.stop(now + 0.2);
                        break;
                    case 'crit':
                        osc.type = 'triangle';
                        osc.frequency.setValueAtTime(800, now);
                        osc.frequency.linearRampToValueAtTime(1200, now + 0.1);
                        gain.gain.setValueAtTime(0.3, now);
                        gain.gain.linearRampToValueAtTime(0, now + 0.2);
                        osc.start(); osc.stop(now + 0.2);
                        break;
                    case 'levelup':
                        const now2 = this.ctx.currentTime;
                        [440, 554, 659, 880].forEach((f, i) => {
                            const o = this.ctx.createOscillator();
                            const g = this.ctx.createGain();
                            o.connect(g); g.connect(this.ctx.destination);
                            o.frequency.setValueAtTime(f, now2 + i*0.1);
                            g.gain.setValueAtTime(0.1, now2 + i*0.1);
                            g.gain.exponentialRampToValueAtTime(0.01, now2 + i*0.1 + 0.2);
                            o.start(now2 + i*0.1); o.stop(now2 + i*0.1 + 0.2);
                        });
                        break;
                    case 'capture':
                        osc.type = 'sine';
                        osc.frequency.setValueAtTime(200, now);
                        osc.frequency.exponentialRampToValueAtTime(2000, now + 0.5);
                        gain.gain.setValueAtTime(0.2, now);
                        gain.gain.exponentialRampToValueAtTime(0.01, now + 0.5);
                        osc.start(); osc.stop(now + 0.5);
                        break;
                }
            }
        }
        const FX = new SoundEngine();

        /**
         * DATABASE: RESONANTS
         */
        const TYPE_CHART = {
            Pyro: { strong: 'Flora', weak: 'Hydro' },
            Hydro: { strong: 'Pyro', weak: 'Flora' },
            Flora: { strong: 'Hydro', weak: 'Pyro' },
            Zephyr: { strong: 'Earth', weak: 'Volt' },
            Earth: { strong: 'Volt', weak: 'Zephyr' },
            Volt: { strong: 'Zephyr', weak: 'Earth' },
            Cosmos: { strong: null, weak: null }
        };

        const RESONANTS_DB = [
            // Pyro Line
            { id: 'pyro1', name: 'Emberkit', type: 'Pyro', hp: 45, atk: 12, def: 8, spd: 10, evol: 'pyro2', evolLvl: 8, color: '#ff4d4d', paths: 'M20,80 L50,20 L80,80 Z M40,60 A10,10 0 1,0 60,60 A10,10 0 1,0 40,60',
                moves: [{ name: 'Spark', pwr: 40, acc: 95, type: 'Pyro', lvl: 1 }, { name: 'Flare', pwr: 60, acc: 90, type: 'Pyro', lvl: 5 }, { name: 'Singe', pwr: 80, acc: 80, type: 'Pyro', lvl: 10 }, { name: 'Inferno', pwr: 110, acc: 75, type: 'Pyro', lvl: 15 }] },
            { id: 'pyro2', name: 'Pyrowolf', type: 'Pyro', hp: 70, atk: 18, def: 14, spd: 18, evol: 'pyro3', evolLvl: 15, color: '#ff774d', paths: 'M20,80 L30,40 L50,50 L70,40 L80,80 Z M40,40 L40,30 M60,40 L60,30',
                moves: [{ name: 'Spark', pwr: 40, acc: 95, type: 'Pyro', lvl: 1 }, { name: 'Flare', pwr: 60, acc: 90, type: 'Pyro', lvl: 5 }, { name: 'Singe', pwr: 80, acc: 80, type: 'Pyro', lvl: 10 }, { name: 'Inferno', pwr: 110, acc: 75, type: 'Pyro', lvl: 15 }] },
            { id: 'pyro3', name: 'Ignisaur', type: 'Pyro', hp: 110, atk: 25, def: 20, spd: 20, evol: null, color: '#ff0000', paths: 'M10,80 Q50,10 90,80 Z M30,60 L40,50 M60,60 L70,50',
                moves: [{ name: 'Spark', pwr: 40, acc: 95, type: 'Pyro', lvl: 1 }, { name: 'Flare', pwr: 60, acc: 90, type: 'Pyro', lvl: 5 }, { name: 'Singe', pwr: 80, acc: 80, type: 'Pyro', lvl: 10 }, { name: 'Inferno', pwr: 110, acc: 75, type: 'Pyro', lvl: 15 }] },
            // Hydro Line
            { id: 'hydro1', name: 'Droplet', type: 'Hydro', hp: 50, atk: 10, def: 10, spd: 8, evol: 'hydro2', evolLvl: 8, color: '#4da6ff', paths: 'M50,20 Q80,50 80,80 Q50,90 20,80 Q20,50 50,20 Z',
                moves: [{ name: 'Mist', pwr: 40, acc: 95, type: 'Hydro', lvl: 1 }, { name: 'Geyser', pwr: 60, acc: 90, type: 'Hydro', lvl: 5 }, { name: 'Tide', pwr: 80, acc: 80, type: 'Hydro', lvl: 10 }, { name: 'Tsunami', pwr: 110, acc: 75, type: 'Hydro', lvl: 15 }] },
            { id: 'hydro2', name: 'Geoxus', type: 'Hydro', hp: 80, atk: 16, def: 16, spd: 12, evol: 'hydro3', evolLvl: 15, color: '#4dceff', paths: 'M20,80 Q20,30 50,30 Q80,30 80,80 L60,70 L50,80 L40,70 Z',
                moves: [{ name: 'Mist', pwr: 40, acc: 95, type: 'Hydro', lvl: 1 }, { name: 'Geyser', pwr: 60, acc: 90, type: 'Hydro', lvl: 5 }, { name: 'Tide', pwr: 80, acc: 80, type: 'Hydro', lvl: 10 }, { name: 'Tsunami', pwr: 110, acc: 75, type: 'Hydro', lvl: 15 }] },
            { id: 'hydro3', name: 'Neptunian', type: 'Hydro', hp: 120, atk: 22, def: 22, spd: 15, evol: null, color: '#004dff', paths: 'M10,70 Q50,10 90,70 L80,90 L50,80 L20,90 Z',
                moves: [{ name: 'Mist', pwr: 40, acc: 95, type: 'Hydro', lvl: 1 }, { name: 'Geyser', pwr: 60, acc: 90, type: 'Hydro', lvl: 5 }, { name: 'Tide', pwr: 80, acc: 80, type: 'Hydro', lvl: 10 }, { name: 'Tsunami', pwr: 110, acc: 75, type: 'Hydro', lvl: 15 }] },
            // Flora Line
            { id: 'flora1', name: 'Seedling', type: 'Flora', hp: 40, atk: 11, def: 11, spd: 9, evol: 'flora2', evolLvl: 8, color: '#4dff88', paths: 'M50,80 L40,60 Q50,50 60,60 L50,80 M50,60 L50,40',
                moves: [{ name: 'Leaf', pwr: 40, acc: 95, type: 'Flora', lvl: 1 }, { name: 'Root', pwr: 60, acc: 90, type: 'Flora', lvl: 5 }, { name: 'Vine', pwr: 80, acc: 80, type: 'Flora', lvl: 10 }, { name: 'Overgrow', pwr: 110, acc: 75, type: 'Flora', lvl: 15 }] },
            { id: 'flora2', name: 'Thornback', type: 'Flora', hp: 75, atk: 17, def: 17, spd: 11, evol: 'flora3', evolLvl: 15, color: '#77ff88', paths: 'M20,80 L30,50 L50,60 L70,50 L80,80 Z M50,50 L50,30',
                moves: [{ name: 'Leaf', pwr: 40, acc: 95, type: 'Flora', lvl: 1 }, { name: 'Root', pwr: 60, acc: 90, type: 'Flora', lvl: 5 }, { name: 'Vine', pwr: 80, acc: 80, type: 'Flora', lvl: 10 }, { name: 'Overgrow', pwr: 110, acc: 75, type: 'Flora', lvl: 15 }] },
            { id: 'flora3', name: 'Bloomgoliath', type: 'Flora', hp: 130, atk: 24, def: 24, spd: 10, evol: null, color: '#00ff55', paths: 'M20,80 Q50,20 80,80 Z M40,40 A5,5 0 1,0 41,40 M60,40 A5,5 0 1,0 61,40',
                moves: [{ name: 'Leaf', pwr: 40, acc: 95, type: 'Flora', lvl: 1 }, { name: 'Root', pwr: 60, acc: 90, type: 'Flora', lvl: 5 }, { name: 'Vine', pwr: 80, acc: 80, type: 'Flora', lvl: 10 }, { name: 'Overgrow', pwr: 110, acc: 75, type: 'Flora', lvl: 15 }] },
            // Volt Line
            { id: 'volt1', name: 'Sparky', type: 'Volt', hp: 35, atk: 15, def: 7, spd: 16, evol: 'volt2', evolLvl: 8, color: '#ffff4d', paths: 'M50,20 L60,40 L80,40 L60,60 L70,80 L50,60 L30,80 L40,60 L20,40 L40,40 Z',
                moves: [{ name: 'Zap', pwr: 40, acc: 95, type: 'Volt', lvl: 1 }, { name: 'Bolt', pwr: 60, acc: 90, type: 'Volt', lvl: 5 }, { name: 'Shock', pwr: 80, acc: 80, type: 'Volt', lvl: 10 }, { name: 'Thunder', pwr: 110, acc: 75, type: 'Volt', lvl: 15 }] },
            { id: 'volt2', name: 'Boltsaber', type: 'Volt', hp: 65, atk: 21, def: 12, spd: 22, evol: 'volt3', evolLvl: 15, color: '#ffff88', paths: 'M30,80 L50,20 L70,80 L50,70 Z M50,20 L50,10',
                moves: [{ name: 'Zap', pwr: 40, acc: 95, type: 'Volt', lvl: 1 }, { name: 'Bolt', pwr: 60, acc: 90, type: 'Volt', lvl: 5 }, { name: 'Shock', pwr: 80, acc: 80, type: 'Volt', lvl: 10 }, { name: 'Thunder', pwr: 110, acc: 75, type: 'Volt', lvl: 15 }] },
            { id: 'volt3', name: 'Stormlord', type: 'Volt', hp: 90, atk: 28, def: 18, spd: 28, evol: null, color: '#ffff00', paths: 'M20,80 L40,40 L50,60 L60,40 L80,80 Z M50,40 L50,10',
                moves: [{ name: 'Zap', pwr: 40, acc: 95, type: 'Volt', lvl: 1 }, { name: 'Bolt', pwr: 60, acc: 90, type: 'Volt', lvl: 5 }, { name: 'Shock', pwr: 80, acc: 80, type: 'Volt', lvl: 10 }, { name: 'Thunder', pwr: 110, acc: 75, type: 'Volt', lvl: 15 }] },
            // Zephyr Line
            { id: 'zeph1', name: 'Gusty', type: 'Zephyr', hp: 40, atk: 12, def: 8, spd: 15, evol: 'zeph2', evolLvl: 8, color: '#e6e6e6', paths: 'M50,20 Q70,50 50,80 Q30,50 50,20 Z M30,50 L70,50',
                moves: [{ name: 'Breeze', pwr: 40, acc: 95, type: 'Zephyr', lvl: 1 }, { name: 'Gale', pwr: 60, acc: 90, type: 'Zephyr', lvl: 5 }, { name: 'Sirocco', pwr: 80, acc: 80, type: 'Zephyr', lvl: 10 }, { name: 'Cyclone', pwr: 110, acc: 75, type: 'Zephyr', lvl: 15 }] },
            { id: 'zeph2', name: 'Slipstream', type: 'Zephyr', hp: 70, atk: 18, def: 13, spd: 21, evol: 'zeph3', evolLvl: 15, color: '#ffffff', paths: 'M20,60 Q50,20 80,60 Q50,90 20,60 Z M40,60 L60,60',
                moves: [{ name: 'Breeze', pwr: 40, acc: 95, type: 'Zephyr', lvl: 1 }, { name: 'Gale', pwr: 60, acc: 90, type: 'Zephyr', lvl: 5 }, { name: 'Sirocco', pwr: 80, acc: 80, type: 'Zephyr', lvl: 10 }, { name: 'Cyclone', pwr: 110, acc: 75, type: 'Zephyr', lvl: 15 }] },
            { id: 'zeph3', name: 'Zephyrus', type: 'Zephyr', hp: 100, atk: 24, def: 18, spd: 28, evol: null, color: '#f0f8ff', paths: 'M10,50 Q50,10 90,50 Q50,90 10,50 Z M30,50 L70,50',
                moves: [{ name: 'Breeze', pwr: 40, acc: 95, type: 'Zephyr', lvl: 1 }, { name: 'Gale', pwr: 60, acc: 90, type: 'Zephyr', lvl: 5 }, { name: 'Sirocco', pwr: 80, acc: 80, type: 'Zephyr', lvl: 10 }, { name: 'Cyclone', pwr: 110, acc: 75, type: 'Zephyr', lvl: 15 }] },
            // Earth Line
            { id: 'earth1', name: 'Pebble', type: 'Earth', hp: 60, atk: 11, def: 15, spd: 5, evol: 'earth2', evolLvl: 8, color: '#daa520', paths: 'M30,80 L70,80 L60,50 L40,50 Z',
                moves: [{ name: 'Slam', pwr: 40, acc: 95, type: 'Earth', lvl: 1 }, { name: 'Tremor', pwr: 60, acc: 90, type: 'Earth', lvl: 5 }, { name: 'Quake', pwr: 80, acc: 80, type: 'Earth', lvl: 10 }, { name: 'Bedrock', pwr: 110, acc: 75, type: 'Earth', lvl: 15 }] },
            { id: 'earth2', name: 'Obsidian', type: 'Earth', hp: 90, atk: 17, def: 20, spd: 8, evol: 'earth3', evolLvl: 15, color: '#b8860b', paths: 'M20,80 L80,80 L70,40 L30,40 Z M40,40 L40,30 M60,40 L60,30',
                moves: [{ name: 'Slam', pwr: 40, acc: 95, type: 'Earth', lvl: 1 }, { name: 'Tremor', pwr: 60, acc: 90, type: 'Earth', lvl: 5 }, { name: 'Quake', pwr: 80, acc: 80, type: 'Earth', lvl: 10 }, { name: 'Bedrock', pwr: 110, acc: 75, type: 'Earth', lvl: 15 }] },
            { id: 'earth3', name: 'Terrasunder', type: 'Earth', hp: 150, atk: 23, def: 28, spd: 10, evol: null, color: '#8b4513', paths: 'M10,80 L90,80 L80,30 L20,30 Z M30,30 L30,20 M50,30 L50,20 M70,30 L70,20',
                moves: [{ name: 'Slam', pwr: 40, acc: 95, type: 'Earth', lvl: 1 }, { name: 'Tremor', pwr: 60, acc: 90, type: 'Earth', lvl: 5 }, { name: 'Quake', pwr: 80, acc: 80, type: 'Earth', lvl: 10 }, { name: 'Bedrock', pwr: 110, acc: 75, type: 'Earth', lvl: 15 }] },
        ];

        /**
         * GAME STATE & ENGINE
         */
        const STATE = {
            screen: 'MENU',
            player: {
                shards: 500,
                party: [],
                inventory: { prisms: 5, hyperPrisms: 0, elixirs: 2, restorers: 2 },
                unlockedZones: 1,
                currentZone: 1,
                currentNode: 0,
                defeatedBosses: []
            },
            battle: null,
            zones: [
                { name: 'The Verdant Canopy', color: '#4dff88', nodes: 5, types: ['Flora', 'Zephyr'], boss: { id: 'flora3', name: 'Bloomgoliath Prime', lvl: 5, dialogue: 'Nature shall reclaim this land!' } },
                { name: 'The Sunken Grotto', color: '#4da6ff', nodes: 5, types: ['Hydro', 'Earth'], boss: { id: 'hydro3', name: 'Neptunian Rex', lvl: 10, dialogue: 'Drown in the depths of Aethelgard!' } },
                { name: 'The Obsidian Crags', color: '#ff4d4d', nodes: 5, types: ['Pyro', 'Volt'], boss: { id: 'pyro3', name: 'Ignisaur Alpha', lvl: 15, dialogue: 'Everything turns to ash eventually!' } },
                { name: 'The Apex Pillar', color: '#bc4dff', nodes: 3, types: ['Cosmos', 'Pyro', 'Hydro', 'Flora', 'Zephyr', 'Earth', 'Volt'], boss: { id: 'pyro3', name: 'The Harmonic Core', lvl: 20, dialogue: 'I am the vibration of the universe itself!' } },
            ]
        };

        class GameEngine {
            constructor() {
                this.init();
            }

            init() {
                this.load();
                if (STATE.player.party.length === 0) {
                    this.giveStarter();
                }
                this.render();
            }

            giveStarter() {
                const starters = [RESONANTS_DB[0], RESONANTS_DB[3], RESONANTS_DB[6]];
                // Default to Pyro for first start
                const s = JSON.parse(JSON.stringify(starters[0]));
                s.currentHp = s.hp;
                s.lvl = 5;
                s.xp = 0;
                STATE.player.party.push(s);
                this.save();
            }

            save() {
                localStorage.setItem('aethelgard_save', JSON.stringify(STATE.player));
            }

            load() {
                const saved = localStorage.getItem('aethelgard_save');
                if (saved) STATE.player = JSON.parse(saved);
            }

            reset() {
                localStorage.removeItem('aethelgard_save');
                location.reload();
            }

            setScreen(screen) {
                STATE.screen = screen;
                this.render();
            }

            // --- Combat Logic ---
            startBattle(isBoss = false, bossData = null) {
                const wildResonant = this.generateWildResonant();
                const enemy = bossData ? { ...this.getDb(bossData.id), lvl: bossData.lvl, currentHp: 0, name: bossData.name } : wildResonant;
                if (!bossData) {
                    enemy.currentHp = enemy.hp;
                } else {
                    enemy.currentHp = enemy.hp * (enemy.lvl / 5);
                }

                STATE.battle = {
                    playerResonant: STATE.player.party[0],
                    enemy: enemy,
                    turn: 'player',
                    isBoss: isBoss,
                    log: 'A wild Resonant appeared!',
                    bossDialogue: bossData ? bossData.dialogue : null
                };
                this.setScreen('BATTLE');
            }

            generateWildResonant() {
                const zone = STATE.zones[STATE.player.currentZone - 1];
                const type = zone.types[Math.floor(Math.random() * zone.types.length)];
                const possible = RESONANTS_DB.filter(r => r.type === type);
                const base = possible[Math.floor(Math.random() * possible.length)];
                const lvl = Math.max(1, Math.floor(Math.random() * 5) + (STATE.player.currentZone * 4));
                
                const res = JSON.parse(JSON.stringify(base));
                res.lvl = lvl;
                res.currentHp = res.hp + (lvl * 5);
                res.maxHp = res.currentHp;
                return res;
            }

            getDb(id) { return RESONANTS_DB.find(r => r.id === id); }

            calculateDamage(attacker, defender, move) {
                const levelMod = (2 * attacker.lvl / 5 + 2);
                const ratio = attacker.atk / defender.def;
                let damage = (((levelMod * move.pwr * ratio) / 50) + 2);
                
                // Type Modifier
                const atkType = move.type;
                const defType = defender.type;
                let mod = 1;
                if (TYPE_CHART[atkType] && TYPE_CHART[atkType].strong === defType) mod = 1.5;
                if (TYPE_CHART[atkType] && TYPE_CHART[atkType].weak === defType) mod = 0.5;
                
                damage *= mod;
                const crit = Math.random() < 0.1 ? 1.5 : 1;
                damage *= crit * (0.85 + Math.random() * 0.15);
                
                return { 
                    value: Math.floor(damage), 
                    isCrit: crit > 1,
                    isSuper: mod > 1 
                };
            }

            async executeTurn(action, moveIndex = null) {
                const b = STATE.battle;
                const p = b.playerResonant;
                const e = b.enemy;

                if (action === 'attack') {
                    const move = p.moves[moveIndex];
                    if (Math.random() * 100 > move.acc) {
                        b.log = `The attack missed!`;
                    } else {
                        constdmg = this.calculateDamage(p, e, move);
                        e.currentHp -= constdmg.value;
                        b.log = `${p.name} used ${move.name}! ${constdmg.isCrit ? 'CRITICAL HIT! ' : ''}${constdmg.isSuper ? 'It\'s super effective! ' : ''}Dealt ${constdmg.value} damage.`;
                        FX.play(constdmg.isCrit ? 'crit' : 'hit');
                    }
                } else if (action === 'prism') {
                    const prismType = STATE.player.inventory.hyperPrisms > 0 ? 'hyper' : 'basic';
                    if (STATE.player.inventory.prisms <= 0 && STATE.player.inventory.hyperPrisms <= 0) {
                        b.log = "No Prisms remaining!";
                        this.render();
                        return;
                    }
                    
                    const rate = (1 - (e.currentHp / e.maxHp)) * 0.8 + (prismType === 'hyper' ? 0.4 : 0.1);
                    if (Math.random() < rate) {
                        FX.play('capture');
                        const captured = JSON.parse(JSON.stringify(e));
                        captured.currentHp = captured.hp;
                        STATE.player.party.push(captured);
                        b.log = `Success! ${e.name} was captured!`;
                        this.render();
                        setTimeout(() => {
                            this.save();
                            this.setScreen('MAP');
                        }, 2000);
                        return;
                    } else {
                        b.log = "The Resonant broke free!";
                    }
                }

                this.render();
                
                // Enemy Turn
                await new Promise(r => setTimeout(r, 1500));
                if (e.currentHp <= 0) {
                    this.winBattle();
                    return;
                }

                const eMove = e.moves[Math.floor(Math.random() * e.moves.length)];
                const dmg = this.calculateDamage(e, p, eMove);
                p.currentHp -= dmg.value;
                b.log = `${e.name} used ${eMove.name}! Dealt ${dmg.value} damage.`;
                FX.play('hit');
                
                if (p.currentHp <= 0) {
                    b.log = "Your Resonant fainted! Returning to map...";
                    this.render();
                    setTimeout(() => this.setScreen('MAP'), 2000);
                }
                
                this.render();
            }

            winBattle() {
                const e = STATE.battle.enemy;
                const xpGain = e.lvl * 20;
                const shardGain = e.lvl * 10;
                
                STATE.player.party[0].xp += xpGain;
                STATE.player.shards += shardGain;
                
                // Level Up
                let p = STATE.player.party[0];
                while (p.xp >= p.lvl * 100) {
                    p.xp -= p.lvl * 100;
                    p.lvl++;
                    p.hp += 10;
                    p.atk += 2;
                    p.def += 2;
                    FX.play('levelup');
                }

                // Evolution
                const dbEntry = this.getDb(p.id);
                if (dbEntry.evol && p.lvl >= dbEntry.evolLvl) {
                    const next = this.getDb(dbEntry.evol);
                    p.id = next.id;
                    p.name = next.name;
                    p.type = next.type;
                    p.hp = next.hp + (p.lvl * 5);
                    p.atk = next.atk + (p.lvl * 2);
                    p.def = next.def + (p.lvl * 2);
                    p.color = next.color;
                    p.paths = next.paths;
                    b.log = `What? ${p.name} is evolving!`;
                }

                STATE.battle.log = `Victory! Gained ${xpGain} XP and ${shardGain} Shards.`;
                this.render();
                
                if (STATE.battle.isBoss) {
                    const zoneIdx = STATE.player.currentZone - 1;
                    STATE.player.unlockedZones = Math.min(4, STATE.player.unlockedZones + 1);
                    STATE.player.defeatedBosses.push(STATE.zones[zoneIdx].name);
                }

                setTimeout(() => {
                    this.save();
                    this.setScreen('MAP');
                }, 3000);
            }

            // --- Rendering Engine ---
            render() {
                const app = document.getElementById('app');
                app.innerHTML = '';

                if (STATE.screen === 'MENU') {
                    app.innerHTML = `
                        <div class="fade-in flex flex-col items-center gap-8 text-center">
                            <h1 class="retro-font text-4xl md:text-6xl text-white mb-4 drop-shadow-lg">AETHELGARD<br><span class="text-2xl text-indigo-400">RESONANCE</span></h1>
                            <div class="flex flex-col gap-4 w-64">
                                <button onclick="game.setScreen('MAP')" class="glass-panel p-4 rounded-xl hover:bg-white/10 transition-all retro-font text-sm">START JOURNEY</button>
                                <button onclick="game.setScreen('DEX')" class="glass-panel p-4 rounded-xl hover:bg-white/10 transition-all retro-font text-sm">CONDUCTOR DEX</button>
                                <button onclick="game.reset()" class="glass-panel p-4 rounded-xl hover:bg-red-900/20 transition-all retro-font text-sm text-red-400">RESET DATA</button>
                            </div>
                        </div>
                    `;
                } else if (STATE.screen === 'MAP') {
                    const zone = STATE.zones[STATE.player.currentZone - 1];
                    app.innerHTML = `
                        <div class="fade-in w-full max-w-4xl h-full flex flex-col p-6">
                            <div class="flex justify-between items-center mb-8">
                                <div>
                                    <h2 class="retro-font text-xl text-white">${zone.name}</h2>
                                    <p class="text-slate-400 text-sm">Zone ${STATE.player.currentZone} / 4</p>
                                </div>
                                <div class="glass-panel px-4 py-2 rounded-full flex gap-4 text-sm font-semibold">
                                    <span>💎 ${STATE.player.shards}</span>
                                    <span>🎒 Prisms: ${STATE.player.inventory.prisms + STATE.player.inventory.hyperPrisms}</span>
                                </div>
                                <button onclick="game.setScreen('SHOP')" class="glass-panel px-4 py-2 rounded-lg hover:bg-white/10 transition-all text-sm retro-font">SHOP</button>
                            </div>

                            <div class="relative flex-1 flex items-center justify-center">
                                <svg width="800" height="400" viewBox="0 0 800 400" class="overflow-visible">
                                    <!-- Path Line -->
                                    <polyline points="100,200 200,150 300,250 400,150 500,200 600,150 700,200" fill="none" stroke="rgba(255,255,255,0.2)" stroke-width="4" stroke-dasharray="10,10" />
                                    
                                    ${Array.from({length: 7}).map((_, i) => {
                                        const points = [[100,200], [200,150], [300,250], [400,150], [500,200], [600,150], [700,200]];
                                        const [x, y] = points[i];
                                        const isLocked = i > STATE.player.currentNode + 1;
                                        const isCurrent = i === STATE.player.currentNode;
                                        const isBoss = i === 6;

                                        return `
                                            <g class="${isLocked ? 'node-locked' : 'node-active'}" onclick="game.handleNodeClick(${i})">
                                                <circle cx="${x}" cy="${y}" r="20" fill="${isBoss ? '#bc4dff' : zone.color}" class="${isCurrent ? 'ring-4 ring-white' : ''}" />
                                                <text x="${x}" y="${y+35}" text-anchor="middle" fill="white" class="text-xs font-bold">${isBoss ? 'GATEKEEPER' : 'NODE ' + (i+1)}</text>
                                            </g>
                                        `;
                                    }).join('')}
                                </svg>
                            </div>
                            <div class="mt-8 glass-panel p-4 rounded-2xl text-center retro-font text-xs text-slate-300">
                                Click a node to explore. Defeat the Gatekeeper to unlock the next zone.
                            </div>
                        </div>
                    `;
                } else if (STATE.screen === 'BATTLE') {
                    const b = STATE.battle;
                    const p = b.playerResonant;
                    const e = b.enemy;
                    
                    app.innerHTML = `
                        <div class="fade-in w-full max-w-5xl h-full flex flex-col justify-between p-4 gap-4">
                            <div class="flex justify-between items-start">
                                <!-- Player Status -->
                                <div class="glass-panel p-4 rounded-2xl w-64 element-glow-${p.type.toLowerCase()}">
                                    <div class="flex justify-between items-end mb-2">
                                        <span class="retro-font text-xs">${p.name}</span>
                                        <span class="text-xs">Lv.${p.lvl}</span>
                                    </div>
                                    <div class="w-full h-3 bg-slate-800 rounded-full overflow-hidden border border-white/10">
                                        <div class="hp-bar-inner h-full" style="width: ${(p.currentHp/p.maxHp)*100}%; background-color: ${this.getHpColor(p.currentHp/p.maxHp)}"></div>
                                    </div>
                                    <div class="text-right text-[10px] mt-1">${Math.floor(p.currentHp)} / ${p.maxHp} HP</div>
                                </div>

                                <!-- Enemy Status -->
                                <div class="glass-panel p-4 rounded-2xl w-64 element-glow-${e.type.toLowerCase()} text-right">
                                    <div class="flex justify-between items-end mb-2 flex-row-reverse">
                                        <span class="retro-font text-xs">${e.name}</span>
                                        <span class="text-xs">Lv.${e.lvl}</span>
                                    </div>
                                    <div class="w-full h-3 bg-slate-800 rounded-full overflow-hidden border border-white/10">
                                        <div class="hp-bar-inner h-full ml-auto" style="width: ${(e.currentHp/e.maxHp)*100}%; background-color: ${this.getHpColor(e.currentHp/e.maxHp)}"></div>
                                    </div>
                                    <div class="text-left text-[10px] mt-1">${Math.floor(e.currentHp)} / ${e.maxHp} HP</div>
                                </div>
                            </div>

                            <div class="flex justify-between items-center px-12">
                                <div class="w-48 h-48 flex items-center justify-center animate-bounce-gentle">
                                    ${this.renderResonant(p)}
                                </div>
                                <div class="w-48 h-48 flex items-center justify-center animate-bounce-gentle" style="animation-delay: 1.5s">
                                    ${this.renderResonant(e)}
                                </div>
                            </div>

                            <div class="glass-panel p-6 rounded-3xl flex flex-col gap-4">
                                <div class="h-12 text-center retro-font text-sm text-indigo-300" id="battle-log">${b.log}</div>
                                <div class="grid grid-cols-2 gap-4">
                                    <div class="grid grid-cols-2 gap-2">
                                        ${p.moves.map((m, i) => `
                                            <button onclick="game.executeTurn('attack', ${i})" class="bg-white/10 p-2 rounded-lg hover:bg-white/20 transition-all text-xs font-bold">${m.name}</button>
                                        `).join('')}
                                    </div>
                                    <div class="grid grid-cols-2 gap-2">
                                        <button onclick="game.executeTurn('prism')" class="bg-indigo-600/40 p-2 rounded-lg hover:bg-indigo-600/60 transition-all text-xs font-bold">PRISM</button>
                                        <button onclick="game.setScreen('MAP')" class="bg-slate-600/40 p-2 rounded-lg hover:bg-slate-600/60 transition-all text-xs font-bold">RUN</button>
                                    </div>
                                </div>
                            </div>
                        </div>
                    `;
                } else if (STATE.screen === 'SHOP') {
                    app.innerHTML = `
                        <div class="fade-in glass-panel p-8 rounded-3xl w-full max-w-2xl">
                            <h2 class="retro-font text-2xl text-center mb-8">CAMP SHOP</h2>
                            <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
                                <div class="flex justify-between items-center p-4 bg-white/5 rounded-xl border border-white/10">
                                    <span>Harmonizer Prism (Basic)</span>
                                    <button onclick="game.buy('prisms', 100)" class="bg-indigo-600 px-3 py-1 rounded-lg text-xs retro-font">100💎</button>
                                </div>
                                <div class="flex justify-between items-center p-4 bg-white/5 rounded-xl border border-white/10">
                                    <span>Hyper-Conductive Prism</span>
                                    <button onclick="game.buy('hyperPrisms', 300)" class="bg-indigo-600 px-3 py-1 rounded-lg text-xs retro-font">300💎</button>
                                </div>
                                <div class="flex justify-between items-center p-4 bg-white/5 rounded-xl border border-white/10">
                                    <span>Core Restorer (50% HP)</span>
                                    <button onclick="game.buy('restorers', 150)" class="bg-indigo-600 px-3 py-1 rounded-lg text-xs retro-font">150💎</button>
                                </div>
                                <div class="flex justify-between items-center p-4 bg-white/5 rounded-xl border border-white/10">
                                    <span>Resonance Elixir (Full Heal)</span>
                                    <button onclick="game.buy('elixirs', 250)" class="bg-indigo-600 px-3 py-1 rounded-lg text-xs retro-font">250💎</button>
                                </div>
                            </div>
                            <button onclick="game.setScreen('MAP')" class="w-full p-4 bg-slate-700 rounded-xl retro-font text-sm">BACK TO MAP</button>
                        </div>
                    `;
                } else if (STATE.screen === 'DEX') {
                    app.innerHTML = `
                        <div class="fade-in w-full max-w-6xl h-full flex flex-col p-6">
                            <h2 class="retro-font text-3xl text-center mb-8">CONDUCTOR DEX</h2>
                            <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 overflow-y-auto p-4">
                                ${RESONANTS_DB.map(r => `
                                    <div class="glass-panel p-4 rounded-2xl text-center ${STATE.player.party.some(p => p.id === r.id) ? 'opacity-100' : 'opacity-40'}">
                                        <div class="w-20 h-20 mx-auto mb-2">${this.renderResonant(r)}</div>
                                        <div class="text-xs font-bold">${r.name}</div>
                                        <div class="text-[10px] text-slate-400">${r.type}</div>
                                    </div>
                                `).join('')}
                            </div>
                            <button onclick="game.setScreen('MENU')" class="mt-8 mx-auto w-64 p-4 glass-panel rounded-xl retro-font text-sm">BACK TO MENU</button>
                        </div>
                    `;
                }
            }

            renderResonant(r) {
                return `
                    <svg width="100" height="100" viewBox="0 0 100 100">
                        <defs>
                            <radialGradient id="grad-${r.id}" cx="50%" cy="50%" r="50%">
                                <stop offset="0%" style="stop-color:${r.color};stop-opacity:0.6" />
                                <stop offset="100%" style="stop-color:${r.color};stop-opacity:0" />
                            </radialGradient>
                        </defs>
                        <circle cx="50" cy="50" r="40" fill="url(#grad-${r.id})" />
                        <path d="${r.paths}" fill="none" stroke="${r.color}" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
                    </svg>
                `;
            }

            getHpColor(pct) {
                if (pct > 0.5) return '#4dff88';
                if (pct > 0.2) return '#ffff4d';
                return '#ff4d4d';
            }

            handleNodeClick(index) {
                if (index > STATE.player.currentNode + 1) return;
                
                STATE.player.currentNode = index;
                const zone = STATE.zones[STATE.player.currentZone - 1];
                
                if (index === 6) {
                    this.startBattle(true, zone.boss);
                } else {
                    this.startBattle();
                }
            }

            buy(item, cost) {
                if (STATE.player.shards >= cost) {
                    STATE.player.shards -= cost;
                    STATE.player.inventory[item]++;
                    FX.play('select');
                    this.save();
                    this.render();
                }
            }
        }

        const game = new GameEngine();
    </script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"basic","sessionId":"4d8fd7b6-df8a-4e45-bfef-677347efa5b1","tokensIn":1837,"tokensOut":12776,"tokensTotal":14613,"turns":1,"toolCalls":0,"failedToolCalls":0,"timestamp":"2026-07-14T21:54:48.486Z"} -->