← Creature Collection results

Creature Collection

Gemma 4 12b qat · composer

Initial view of Creature Collection

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

Reviews & generation details

Reviews

Human review · Jul 17, 2026 · rubric 1

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

The reviewer marked this result as broken. It remains in the archive.

Visual design
Unscored
UX / feel
Unscored
Works
Unscored
Prompt spirit
Unscored
Delight
Unscored

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 33,721 bytes · SHA-256 be993fdd0267
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Vitreous Resonance</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
/* ── css: core_styles ── */
:root {
  --primary: #a2d2ff;
  --secondary: #ffafcc;
  --accent: #ffcdb9;
  --bg: #0f172a;
  --surface: rgba(30, 41, 59, 0.8);
  --text: #f8fafc;
  --border: rgba(255, 255, 255, 0.1);
  --font-main: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-header: 'Georgia', serif;
}

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

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

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

canvas {
  display: block;
  image-rendering: pixelated;
  box-shadow: 0 0 50px rgba(0,0,0,0.5);
}

/* UI Overlays */
.overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background: radial-gradient(circle, rgba(15, 23, 42, 0.2) 0%, rgba(15, 23, 42, 0.9) 100%);
  z-index: 100;
  backdrop-filter: blur(10px);
}

.menu-container {
  background: var(--surface);
  padding: 2rem;
  border-radius: 1rem;
  border: 1px solid var(--border);
  text-align: center;
  max-width: 80%;
  max-height: 80%;
  overflow-y: auto;
}

h1 {
  font-family: var(--font-header);
  font-size: 4rem;
  margin-bottom: 2rem;
  text-shadow: 0 0 20px var(--primary);
}

button {
  background: var(--primary);
  border: none;
  padding: 0.8rem 2rem;
  margin: 0.5rem;
  border-radius: 0.5rem;
  color: var(--bg);
  font-weight: bold;
  cursor: pointer;
  transition: transform 0.2s, background 0.2s;
  font-size: 1.2rem;
}

button:hover {
  transform: scale(1.05);
  background: var(--secondary);
}

button:disabled {
  background: #64748b;
  cursor: not-allowed;
}

/* HUD */
#hud {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  padding: 20px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.hud-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

.status-bar {
  background: var(--surface);
  padding: 10px 20px;
  border-radius: 10px;
  border: 1px solid var(--border);
}

#battle-ui {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 30%;
  background: rgba(15, 23, 42, 0.9);
  border-top: 2px solid var(--primary);
  display: none;
  padding: 20px;
  box-sizing: border-box;
  pointer-events: auto;
  flex-direction: column;
  justify-content: space-between;
}

.battle-party {
  display: flex;
  justify-content: space-around;
  margin-bottom: 20px;
}

.creature-slot {
  text-align: center;
  width: 120px;
}

.hp-bar-outer {
  width: 100%;
  height: 8px;
  background: #444;
  border-radius: 4px;
  overflow: hidden;
  margin-top: 5px;
}

.hp-bar-inner {
  height: 100%;
  background: #4ade80;
  transition: width 0.3s;
}

.action-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  max-width: 600px;
  margin: 0 auto;
}

.dialogue-box {
  background: var(--surface);
  padding: 15px;
  border-radius: 10px;
  margin-bottom: 10px;
  border: 1px solid var(--border);
  min-height: 60px;
  font-size: 1.1rem;
}

/* Animations */
@keyframes shake {
  0% { transform: translate(1px, 1px) rotate(0deg); }
  20% { transform: translate(-3px, 0px) rotate(-1deg); }
  40% { transform: translate(3px, 2px) rotate(1deg); }
  60% { transform: translate(-3px, 1px) rotate(0deg); }
  80% { transform: translate(3px, -1px) rotate(1deg); }
  100% { transform: translate(1px, 2px) rotate(0deg); }
}

.shake {
  animation: shake 0.2s ease-in-out;
}

.floating-text {
  position: absolute;
  pointer-events: none;
  animation: floatUp 1s forwards;
  font-weight: bold;
}

@keyframes floatUp {
  0% { transform: translateY(0); opacity: 1; }
  100% { transform: translateY(-50px); opacity: 0; }
}
/* ── css: ui_polish ── */
.pulse {
  animation: pulse-animation 2s infinite;
}

@keyframes pulse-animation {
  0% { opacity: 0.8; }
  50% { opacity: 1; }
  100% { opacity: 0.8; }
}

.pause-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 200;
  padding: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: white;
  cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
  <canvas id="game-canvas"></canvas>
  <div id="hud" style="display: none;">
    <div class="hud-top">
      <div class="status-bar" id="hud-zone-info">Zone: ---</div>
      <div class="status-bar" id="hud-party-info">Party: 0/6</div>
    </div>
    <div id="battle-ui">
      <div id="battle-log" class="dialogue-box">Welcome to the Resonance.</div>
      <div class="battle-party" id="battle-party-display"></div>
      <div class="action-grid" id="action-grid"></div>
    </div>
  </div>
  <div id="ui-layer"></div>
</div>
<script>
'use strict';

// ── const: TYPES ──
const TYPES = {
  A: { name: "Aether", color: "#a2d2ff" },
  B: { name: "Blight", color: "#88cc88" },
  C: { name: "Cinder", color: "#ff8844" },
  D: { name: "Dusk", color: "#aa4488" },
  E: { name: "Echo", color: "#ffcc00" },
  F: { name: "Flux", color: "#ffffff" },
  G: { name: "Glass", color: "#cc99ff" }
};

// ── const: TYPE_CHART ──
const TYPE_CHART = {
  A: { beats: ["C"], weak: ["D"] }, // Type A > Type C; Type D > Type A
  B: { beats: ["A"], weak: ["E"] }, // Type B > Type A; Type E > Type B
  C: { beats: ["B"], weak: ["A"] }, // Type C > Type B; Type A > Type C
  D: { beats: ["A", "C"], weak: ["E"] }, // Type D > A & C; Type E > D
  E: { beats: ["D", "B"], weak: [] }, // Type E > D & B
  F: { beats: ["all", 1.1], weak: [] }, // Type F beats all slightly
  G: { beats: [], weak: ["F"] } // Type G resists all but weak to F
};

// ── function: getMultiplier ──
// Logic for "beats all slightly" or "resists everything"
function getMultiplier(attackerType, defenderType) {
  if (attackerType === "F") return 1.1;
  if (attackerType === "G") return 0.5; // Resists everything
  
  const chart = TYPE_CHART[attackerType];
  if (chart.beats.includes(defenderType)) return 1.5;
  if (chart.beats.includes("all") && attackerType === "F") return 1.1;
  
  // Check if defender is weak to attacker
  const defChart = TYPE_CHART[defenderType];
  if (defChart.weak.includes(attackerType)) return 1.5;
  
  return 1.0;
}

// ── const: ABILITIES ──
const ABILITIES = [
  { name: "Vitreous Strike", type: "Cinder", power: 40, acc: 0.9, effect: "none", desc: "A sharp physical blow." },
  { name: "Aetheric Veil", type: "Aether", power: 20, acc: 0.8, effect: "barrier", desc: "Creates a temporary barrier." },
  { name: "Blight Bloom", type: "Blight", power: 35, acc: 0.85, effect: "burn", desc: "Spreads decaying spores." },
  { name: "Frost Pulse", type: "Echo", power: 30, acc: 0.8, effect: "frost", desc: "Slows the target's movements." },
  { name: "Static Surge", type: "Flux", power: 45, acc: 0.7, effect: "shock", desc: "A high-voltage discharge." },
  { name: "Lumina Heal", type: "Aether", power: 50, acc: 0.9, effect: "heal", desc: "Restores HP to an ally." },
  { name: "Shatter", type: "Glass", power: 60, acc: 0.7, effect: "multi-hit", desc: "Hits three times for less damage." },
  { name: "Echoing Shout", type: "Echo", power: 25, acc: 1.0, effect: "speed", desc: "Increases party speed." },
  { name: "Void Rip", type: "Dusk", power: 55, acc: 0.75, effect: "none", desc: "Tears through the fabric of space." },
  { name: "Molten Core", type: "Cinder", power: 40, acc: 0.8, effect: "burn", desc: "Heats the target's blood." },
  { name: "Prism Ray", type: "Glass", power: 35, acc: 0.85, effect: "none", desc: "A beam of focused light." },
  { name: "Root Bind", type: "Blight", power: 30, acc: 0.8, effect: "frost", desc: "Entangles the target in vines." },
  { name: "Spark Jump", type: "Flux", power: 20, acc: 0.95, effect: "speed", desc: "Quick leap with electricity." },
  { name: "Gravity Well", type: "Dusk", power: 45, acc: 0.7, effect: "slow", desc: "Heavy weight crushes the target." },
  { name: "Crystal Shield", type: "Glass", power: 25, acc: 0.9, effect: "barrier", desc: "Hardens the skin into glass." },
  { name: "Flare", type: "Cinder", power: 50, acc: 0.8, effect: "burn", desc: "A sudden burst of heat." },
  { name: "Mist Veil", type: "Aether", power: 15, acc: 1.0, effect: "heal", desc: "Mends minor wounds with mist." },
  { name: "Corruption", type: "Blight", power: 50, acc: 0.7, effect: "burn", desc: "Injects rotting essence." },
  { name: "Shockwave", type: "Flux", power: 40, acc: 0.85, effect: "shock", desc: "A blast of kinetic energy." },
  { name: "Mirror Image", type: "Echo", power: 20, acc: 0.8, effect: "barrier", desc: "Creates a decoy." },
  { name: "Stone Skin", type: "Glass", power: 30, acc: 0.9, effect: "defense", desc: "Increases defense for 2 turns." },
  { name: "Tectonic Shift", type: "Dusk", power: 60, acc: 0.6, effect: "multi-hit", desc: "Earthquakes strike the ground." },
  { name: "Solar Flare", type: "Cinder", power: 70, acc: 0.6, effect: "burn", desc: "Blinding heat from above." },
  { name: "Nature's Wrath", type: "Blight", power: 65, acc: 0.65, effect: "slow", desc: "Overwhelming growth." },
  { name: "Luminous Dance", type: "Aether", power: 45, acc: 0.85, effect: "speed", desc: "Agile movement in light." },
  { name: "Darkness Dive", type: "Dusk", power: 55, acc: 0.8, effect: "none", desc: "Strikes from the shadows." },
  { name: "Refraction", type: "Glass", power: 30, acc: 0.9, effect: "multi-hit", desc: "Light splits into multiple rays." },
  { name: "Thunderclap", type: "Flux", power: 40, acc: 0.8, effect: "shock", desc: "A deafening crack of thunder." },
  { name: "Spore Cloud", type: "Blight", power: 35, acc: 0.9, effect: "burn", desc: "Clouds the area with dust." },
  { name: "Wisp Guide", type: "Echo", power: 25, acc: 1.0, effect: "heal", desc: "Small wisps mend wounds." },
  { name: "Quake", type: "Dusk", power: 50, acc: 0.75, effect: "slow", desc: "Rumbles the earth." },
  { name: "Ignite", type: "Cinder", power: 30, acc: 0.9, effect: "burn", desc: "Sets the target's clothes on fire." },
  { name: "Ethereal Link", type: "Aether", power: 40, acc: 0.8, effect: "regen", desc: "Connects souls to heal." },
  { name: "Shatterpoint", type: "Glass", power: 80, acc: 0.5, effect: "multi-hit", desc: "Destroys structural integrity." },
  { name: "Void Touch", type: "Dusk", power: 50, acc: 0.75, effect: "none", desc: "Absorbs life energy." },
  { name: "Storm Call", type: "Flux", power: 60, acc: 0.6, effect: "shock", desc: "Summons a miniature storm." },
  { name: "Wild Growth", type: "Blight", power: 55, acc: 0.7, effect: "slow", desc: "Overruns the enemy." },
  { name: "Sunbeam", type: "Aether", power: 45, acc: 0.9, effect: "heal", desc: "A warm ray of healing." },
  { name: "Obsidian Fang", type: "Glass", power: 55, acc: 0.8, effect: "none", desc: "Piercing bite." },
  { name: "Cinder Burst", type: "Cinder", power: 40, acc: 0.85, effect: "burn", desc: "Explosive heat." },
  { name: "Shadow Step", type: "Dusk", power: 35, acc: 0.95, effect: "speed", desc: "Teleports behind the foe." },
  { name: "Static Discharge", type: "Flux", power: 30, acc: 1.0, effect: "shock", desc: "Constant buzzing electricity." },
  { name: "Echo Strike", type: "Echo", power: 40, acc: 0.8, effect: "multi-hit", desc: "Strike repeats in time." },
  { name: "Verdant Reach", type: "Blight", power: 45, acc: 0.8, effect: "slow", desc: "Vines grab the target." },
  { name: "Prism Shield", type: "Glass", power: 30, acc: 0.9, effect: "barrier", desc: "Reflects light into armor." },
  { name: "Nova", type: "Flux", power: 100, acc: 0.4, effect: "none", desc: "Massive explosion." }
];

// ── class: Creature ──
class Creature {
  constructor(data) {
    this.id = Math.random().toString(36).substr(2, 9);
    this.name = data.name;
    this.lore = data.lore;
    this.type = data.type;
    this.level = data.level || 1;
    this.maxHp = data.hp || 100;
    this.hp = data.hp || 100;
    this.maxAtk = data.atk || 10;
    this.maxDef = data.def || 8;
    this.maxSpd = data.spd || 5;
    this.currentAtk = this.maxAtk;
    this.currentDef = this.maxDef;
    this.currentSpd = this.maxSpd;
    this.abilities = data.abilities.map(a => ({ ...a, pp: 100 }));
    this.exp = 0;
    this.isTransformed = false;
    this.statusEffects = [];
    this.barrier = false;
  }

  get stats() {
    return {
      atk: Math.floor(this.maxAtk * (1 + (this.level - 1) * 0.1)),
      def: Math.floor(this.maxDef * (1 + (this.level - 1) * 0.08)),
      spd: Math.floor(this.maxSpd * (1 + (this.level - 1) * 0.05))
    };
  }

  takeDamage(amount) {
    if (this.barrier) {
      this.barrier = false;
      return 0;
    }
    let damage = Math.max(1, Math.floor(amount * (1 - (this.stats.def / (this.stats.def + 20)))));
    this.hp = Math.max(0, this.hp - damage);
    return damage;
  }

  applyStatus(effectName) {
    // Simple status logic
    if (effectName === "burn") this.statusEffects.push({ type: "burn", duration: 3 });
    if (effectName === "frost") this.statusEffects.push({ type: "frost", duration: 2 });
    if (effectName === "shock") this.statusEffects.push({ type: "shock", duration: 1 });
    if (effectName === "barrier") this.barrier = true;
    if (effectName === "regen") this.statusEffects.push({ type: "regen", duration: 3 });
  }

  updateStatus() {
    this.statusEffects = this.statusEffects.filter(s => {
      s.duration--;
      return s.duration > 0;
    });
  }
}

// ── const: BASE_CREATURES ──
const BASE_CREATURES = [
  { name: "Lume", lore: "A flickering spirit of the first dawn.", type: "Aether", hp: 110, atk: 12, def: 10, spd: 14, abilities: [0, 1, 4, 5] },
  { name: "Mire", lore: "A sludge-born entity that feeds on decay.", type: "Blight", hp: 130, atk: 10, def: 15, spd: 8, abilities: [2, 11, 12, 18] },
  { name: "Pyre", lore: "A living ember that dances in the drafts.", type: "Cinder", hp: 90, atk: 18, def: 8, spd: 12, abilities: [0, 6, 15, 39] },
  { name: "Shade", lore: "A silhouette that moves between thoughts.", type: "Dusk", hp: 100, atk: 15, def: 12, spd: 16, abilities: [8, 13, 25, 35] },
  { name: "Vibe", lore: "A resonant pulse in the air.", type: "Echo", hp: 85, atk: 10, def: 8, spd: 22, abilities: [3, 7, 19, 40] },
  { name: "Spark", lore: "A jittery spark of pure energy.", type: "Flux", hp: 70, atk: 14, def: 6, spd: 25, abilities: [4, 12, 20, 30] },
  { name: "Shard", lore: "A jagged piece of the world's core.", type: "Glass", hp: 120, atk: 16, def: 20, spd: 9, abilities: [6, 10, 14, 36] },
  { name: "Glint", lore: "A tiny shard of reflective light.", type: "Glass", hp: 80, atk: 12, def: 15, spd: 14, abilities: [10, 28, 32, 36] },
  { name: "Drift", lore: "A cloud of wandering memories.", type: "Echo", hp: 100, atk: 11, def: 11, spd: 15, abilities: [3, 18, 21, 38] },
  { name: "Bloom", lore: "A flower that grows in whispers.", type: "Blight", hp: 115, atk: 9, def: 14, spd: 11, abilities: [2, 11, 31, 41] },
  { name: "Cinderfall", lore: "Falling ash that never hits the ground.", type: "Cinder", hp: 95, atk: 14, def: 10, spd: 13, abilities: [0, 15, 23, 26] },
  { name: "Aetheris", lore: "A majestic bird of pure light.", type: "Aether", hp: 120, atk: 15, def: 12, spd: 18, abilities: [1, 5, 22, 37] },
  { name: "Null", lore: "A void that consumes all sound.", type: "Dusk", hp: 140, atk: 18, def: 18, spd: 7, abilities: [8, 13, 35, 40] },
  { name: "Fluxian", lore: "A creature made of shifting frequencies.", type: "Flux", hp: 80, atk: 16, def: 10, spd: 24, abilities: [4, 20, 30, 39] },
  { name: "Vitrum", lore: "The foundation of the crystal gardens.", type: "Glass", hp: 150, atk: 14, def: 25, spd: 6, abilities: [6, 10, 14, 26] },
  { name: "Scorch", lore: "A serpent of living magma.", type: "Cinder", hp: 110, atk: 20, def: 12, spd: 10, abilities: [0, 6, 15, 39] },
  { name: "Mistral", lore: "The wind that carries the first secrets.", type: "Echo", hp: 80, atk: 12, def: 9, spd: 26, abilities: [3, 7, 19, 38] },
  { name: "Wilder", lore: "A beast of unbridled growth.", type: "Blight", hp: 135, atk: 19, def: 14, spd: 9, abilities: [2, 11, 18, 31] },
  { name: "Zenith", lore: "The pinnacle of resonant grace.", type: "Aether", hp: 110, atk: 18, def: 15, spd: 20, abilities: [1, 5, 22, 37] }
];

// ── const: TRANSFORMATIONS ──
const TRANSFORMATIONS = [
  { name: "Lume-Exalted", base: "Lume", hp: 160, atk: 25, def: 20, spd: 25, reqLevel: 20, catalyst: "Echo Shard" },
  { name: "Mire-Corrupted", base: "Mire", hp: 200, atk: 30, def: 35, spd: 5, reqLevel: 25, catalyst: "Void Essence" },
  { name: "Pyre-Infernal", base: "Pyre", hp: 130, atk: 40, def: 15, spd: 18, reqLevel: 20, catalyst: "Cinder Core" },
  { name: "Shade-Ethereal", base: "Shade", hp: 120, atk: 25, def: 25, spd: 30, reqLevel: 25, catalyst: "Aether Shard" },
  { name: "Vibe-Resonant", base: "Vibe", hp: 100, atk: 20, def: 20, spd: 40, reqLevel: 20, catalyst: "Resonance Key" },
  { name: "Spark-Overload", base: "Spark", hp: 90, atk: 45, def: 10, spd: 50, reqLevel: 25, catalyst: "Flux Core" },
  { name: "Shard-Prismatic", base: "Shard", hp: 180, atk: 30, def: 40, spd: 10, reqLevel: 30, catalyst: "Prism Shard" },
  { name: "Glint-Radiant", base: "Glint", hp: 140, atk: 25, def: 30, spd: 25, reqLevel: 25, catalyst: "Lumina Shard" },
  { name: "Drift-Haunting", base: "Drift", hp: 150, atk: 22, def: 22, spd: 22, reqLevel: 25, catalyst: "Void Essence" }
];

// ── class: GameEngine ──
class GameEngine {
  constructor() {
    this.state = {
      currentZone: 0,
      party: [],
      inventory: [],
      enemies: [],
      battle: null,
      mapPos: { x: 2, y: 2 },
      storyProgress: 0,
      flags: {
        bossesDefeated: 0,
        unlockedZones: [0],
        collectedCreatures: []
      },
      gameActive: false,
      selectedCreatureIdx: 0,
      captureProgress: 0,
      isCapturing: false,
      pause: false
    };
    this.zones = [
      { name: "The Whispering Glade", bg: "linear-gradient(to bottom, #1e293b, #334155)", color: "#a2d2ff", rate: 0.1, story: "The air here hums with a faint, melodic frequency." },
      { name: "The Scorched Basin", bg: "linear-gradient(to bottom, #450a0a, #7f1d1d)", color: "#ff8844", rate: 0.15, story: "Heat waves distort the horizon, smelling of ancient ash." },
      { name: "The Sunken Mire", bg: "linear-gradient(to bottom, #064e3b, #065f46)", color: "#88cc88", rate: 0.18, story: "Thick moss coats everything, damp and heavy with life." },
      { name: "The Void Rift", bg: "linear-gradient(to bottom, #2e1065, #4c1d95)", color: "#aa4488", rate: 0.2, story: "A tear in reality where shadows bleed into color." },
      { name: "The Crystal Spires", bg: "linear-gradient(to bottom, #581c87, #86198f)", color: "#cc99ff", rate: 0.22, story: "Tall structures of glass reach toward the heavens." },
      { name: "The Core Nexus", bg: "linear-gradient(to bottom, #0f172a, #1e293b)", color: "#ffffff", rate: 0.25, story: "The heartbeat of the world, pulsing with white light." }
    ];
    
    this.init();
  }

  init() {
    const canvas = document.getElementById('game-canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 600;
    PARTICLE_SYSTEM.init();
    this.renderOverworld();
    this.setupInput();
    this.startLoop();
    this.showIntro();
  }

  saveGame() {
    const saveData = {
      currentZone: this.state.currentZone,
      party: this.state.party.map(p => ({
        name: p.name,
        maxHp: p.maxHp,
        hp: p.hp,
        maxAtk: p.maxAtk,
        maxDef: p.maxDef,
        maxSpd: p.maxSpd,
        level: p.level,
        exp: p.exp,
        abilities: p.abilities.map(a => ({...a, pp: a.pp})),
        isTransformed: p.isTransformed,
        statusEffects: p.statusEffects
      })),
      inventory: this.state.inventory,
      flags: this.state.flags,
      mapPos: this.state.mapPos
    };
    localStorage.setItem(SAVE_KEY, JSON.stringify(saveData));
    this.showNotification("Game Saved");
  }

  loadGame() {
    const data = localStorage.getItem(SAVE_KEY);
    if (!data) return;
    const parsed = JSON.parse(data);
    this.state.currentZone = parsed.currentZone;
    this.state.party = parsed.party.map(p => {
      const c = new Creature(p);
      c.hp = p.hp;
      c.exp = p.exp;
      c.isTransformed = p.isTransformed;
      c.statusEffects = p.statusEffects;
      return c;
    });
    this.state.inventory = parsed.inventory;
    this.state.flags = parsed.flags;
    this.state.mapPos = parsed.mapPos;
    this.updateHUD();
    this.renderOverworld();
    this.showNotification("Game Loaded");
  }

  showNotification(text) {
    const ui = document.getElementById('ui-layer');
    ui.innerHTML = `<div class="overlay" style="background: transparent; pointer-events: none;">
      <div class="status-bar" style="margin-top: 20px; font-size: 2rem;">${text}</div>
    </div>`;
    setTimeout(() => { ui.innerHTML = ''; }, 2000);
  }

  showIntro() {
    const ui = document.getElementById('ui-layer');
    ui.innerHTML = `
      <div class="overlay">
        <div class="menu-container">
          <h1 id="game-title">VITREOUS RESONANCE</h1>
          <p>The world hums in frequencies. You are the Tuner.</p>
          <div style="display:flex; justify-content:center; margin-top:20px;">
            <button id="btn-new">New Binding</button>
            <button id="btn-load">Continue</button>
          </div>
        </div>
      </div>
    `;
    document.getElementById('btn-new').onclick = () => {
      this.state.party = [new Creature(BASE_CREATURES[0]), new Creature(BASE_CREATURES[1]), new Creature(BASE_CREATURES[2])];
      this.state.gameActive = true;
      this.updateHUD();
      ui.innerHTML = '';
    };
    document.getElementById('btn-load').onclick = () => {
      this.loadGame();
      if (this.state.party.length > 0) {
        this.state.gameActive = true;
        ui.innerHTML = '';
      }
    };
  }

  updateHUD() {
    document.getElementById('hud-zone-info').innerText = `Zone: ${this.zones[this.state.currentZone].name}`;
    document.getElementById('hud-party-info').innerText = `Party: ${this.state.party.length}/6`;
  }

  setupInput() {
    window.addEventListener('keydown', (e) => {
      if (this.state.battle || !this.state.gameActive || this.state.pause) return;
      const move = { w: [0, -1], s: [0, 1], a: [-1, 0], d: [1, 0] };
      let step = null;
      if (e.key === 'w' || e.key === 'ArrowUp') step = move.w;
      if (e.key === 's' || e.key === 'ArrowDown') step = move.s;
      if (e.key === 'a' || e.key === 'ArrowLeft') step = move.a;
      if (e.key === 'd' || e.key === 'ArrowRight') step = move.d;
      if (e.key === 'p' || e.key === 'Escape') {
        this.state.pause = !this.state.pause;
        if (this.state.pause) this.togglePauseMenu();
        return;
      }
      
      if (step) {
        this.state.mapPos.x = Math.max(0, Math.min(19, this.state.mapPos.x + step[0]));
        this.state.mapPos.y = Math.max(0, Math.min(19, this.state.mapPos.y + step[1]));
        this.handleMapEvent();
        this.renderOverworld();
      }
    });

    // Capture Button
    const captureBtn = document.createElement('button');
    captureBtn.id = "capture-btn";
    captureBtn.innerText = "CAPTURE";
    captureBtn.style.cssText = "position:absolute; bottom: 20px; right: 20px; padding: 15px; background: #ffcc00; font-weight: bold; display:none; border: 2px solid white; border-radius: 10px; z-index: 1000; box-shadow: 0 4px 15px rgba(0,0,0,0.5);";
    document.getElementById('battle-ui').appendChild(captureBtn);
    captureBtn.onclick = () => this.handleCapture();
  }

  togglePauseMenu() {
    const ui = document.getElementById('ui-layer');
    if (this.state.pause) {
      ui.innerHTML = `
        <div class="overlay">
          <div class="menu-container">
            <h1 style="font-size: 2rem;">PAUSED</h1>
            <p>Press P to resume.</p>
            <button id="btn-resume">Resume</button>
          </div>
        </div>`;
      document.getElementById('btn-resume').onclick = () => {
        this.state.pause = false;
        ui.innerHTML = '';
      };
    } else {
      ui.innerHTML = '';
    }
  }

  handleMapEvent() {
    const { x, y } = this.state.mapPos;
    if (Math.random() < this.zones[this.state.currentZone].rate) {
      this.startBattle();
    }
    if (x === 10 && y === 10) {
      this.state.party.forEach(p => p.hp = p.maxHp);
      this.updateHUD();
      this.showNotification("Restored at the Shrine");
    }
    // Zone Guardian Check (at 18, 18)
    if (x === 18 && y === 18) {
      this.startBossBattle();
    }
  }

  startBossBattle() {
    const bossData = {
      name: "Guardian of the " + this.zones[this.state.currentZone].name,
      lore: "A monumental entity of pure resonance.",
      type: "Flux",
      hp: 500,
      atk: 40,
      def: 30,
      spd: 10,
      abilities: [4, 19, 30, 40]
    };
    this.state.battle = {
      enemies: [new Creature(bossData)],
      log: "A Guardian of the Resonance appears!",
      isBoss: true
    };
    document.getElementById('hud').style.display = 'flex';
    document.getElementById('battle-ui').style.display = 'flex';
    this.renderBattleUI();
  }

  startBattle() {
    this.state.battle = {
      enemies: [new Creature({...BASE_CREATURES[Math.floor(Math.random() * 18)]})],
      turn: 0,
      log: "A wild creature appears!",
      isBoss: false
    };
    document.getElementById('hud').style.display = 'flex';
    document.getElementById('battle-ui').style.display = 'flex';
    this.renderBattleUI();
  }

  renderOverworld() {
    const canvas = document.getElementById('game-canvas');
    const ctx = canvas.getContext('2d');
    const zone = this.zones[this.state.currentZone];

    const grad = ctx.createLinearGradient(0, 0, 0, 600);
    grad.addColorStop(0, '#0f172a');
    grad.addColorStop(1, '#1e293b');
    ctx.fillStyle = grad;
    ctx.fillRect(0, 0, 800, 600);

    ctx.fillStyle = zone.color + "11";
    ctx.fillRect(0, 0, 800, 600);

    ctx.strokeStyle = 'rgba(255,255,255,0.03)';
    for (let i = 0; i < 20; i++) {
      ctx.beginPath(); ctx.moveTo(i * 40, 0); ctx.lineTo(i * 40, 600); ctx.stroke();
      ctx.beginPath(); ctx.moveTo(0, i * 40); ctx.lineTo(800, i * 40); ctx.stroke();
    }

    ctx.fillStyle = 'white';
    ctx.shadowBlur = 15;
    ctx.shadowColor = 'white';
    ctx.beginPath();
    ctx.arc(this.state.mapPos.x * 40 + 20, this.state.mapPos.y * 40 + 20, 10, 0, Math.PI * 2);
    ctx.fill();
    ctx.shadowBlur = 0;
    
    PARTICLE_SYSTEM.draw(ctx);
  }

  renderBattleUI() {
    const log = document.getElementById('battle-log');
    log.innerText = this.state.battle.log;

    const partyDisplay = document.getElementById('battle-party-display');
    partyDisplay.innerHTML = '';
    this.state.party.forEach((p, i) => {
      const slot = document.createElement('div');
      slot.className = 'creature-slot';
      const hpPct = Math.max(0, (p.hp / p.maxHp) * 100);
      slot.innerHTML = `
        <div style="font-weight:bold">${p.name}</div>
        <div class="hp-bar-outer"><div class="hp-bar-inner" style="width: ${hpPct}%"></div></div>
        <div style="font-size:0.8rem">Lv ${p.level}</div>
      `;
      partyDisplay.appendChild(slot);
    });

    const enemy = this.state.battle.enemies[0];
    const enemyDisplay = document.createElement('div');
    enemyDisplay.style.cssText = "margin: 20px auto; text-align: center; border: 1px solid var(--primary); padding: 10px; border-radius: 10px;";
    enemyDisplay.innerHTML = `
      <div style="font-weight:bold; font-size: 1.5rem;">${enemy.name}</div>
      <div class="hp-bar-outer" style="width: 300px; margin: 10px auto;"><div class="hp-bar-inner" style="width: ${(enemy.hp / enemy.maxHp) * 100}%"></div></div>
      <div style="font-size:0.9rem;">${enemy.hp} / ${enemy.maxHp} HP</div>
    `;
    partyDisplay.prepend(enemyDisplay);

    const grid = document.getElementById('action-grid');
    grid.innerHTML = '';
    const active = this.state.party[0];
    if (active && active.hp > 0) {
      active.abilities.forEach((a, i) => {
        const btn = document.createElement('button');
        btn.innerText = a.name;
        btn.onclick = () => this.resolveMove(i);
        grid.appendChild(btn);
      });
    }

    const capBtn = document.getElementById('capture-btn');
    capBtn.style.display = this.state.battle.isBoss ? 'none' : 'block';
  }

  resolveMove(abilityIdx) {
    const active = this.state.party[0];
    const ability = active.abilities[abilityIdx];
    const enemy = this.state.battle.enemies[0];

    if (!active || !enemy || active.hp <= 0) return;

    const mult = getMultiplier(active.type, enemy.type);
    const baseDmg = (0.4 * active.level * active.stats.atk / enemy.stats.def * ability.power);
    const finalDmg = Math.floor(baseDmg * mult + (Math.random() * 5));
    
    const taken = enemy.takeDamage(finalDmg);
    document.getElementById('battle-log').innerText = `${active.name} uses ${ability.name}! Deals ${taken} damage.`;
    
    if (enemy.hp <= 0) {
      document.getElementById('battle-log').innerText = `${enemy.name} was defeated!`;
      
      if (this.state.battle.isBoss) {
        this.state.flags.bossesDefeated++;
        this.state.currentZone = (this.state.currentZone + 1) % 6;
        this.state.battle = null;
        document.getElementById('battle-ui').style.display = 'none';
        document.getElementById('hud').style.display = 'none';
        this.state.gameActive = true;
        this.renderOverworld();
        this.saveGame();
      } else {
        this.state.battle.enemies.shift();
        if (this.state.battle.enemies.length === 0) {
          this.state.battle = null;
          document.getElementById('battle-ui').style.display = 'none';
          document.getElementById('hud').style.display = 'none';
          this.state.gameActive = true;
          this.renderOverworld();
          this.saveGame();
        } else {
          this.renderBattleUI();
        }
      }
    } else {
      setTimeout(() => {
        const e = this.state.battle.enemies[0];
        const eDmg = Math.floor(e.stats.atk * 0.8);
        active.hp -= eDmg;
        document.getElementById('battle-log').innerText = `${e.name} attacks back for ${eDmg}!`;
        this.renderBattleUI();
      }, 800);
    }
  }

  handleCapture() {
    if (this.state.isCapturing) return;
    this.state.isCapturing = true;
    const enemy = this.state.battle.enemies[0];
    const captureBtn = document.getElementById('capture-btn');
    
    let progress = 0;
    const interval = setInterval(() => {
      if (!this.state.isCapturing || !enemy || enemy.hp <= 0) {
        clearInterval(interval);
        this.state.isCapturing = false;
        return;
      }
      progress += 2;
      captureBtn.innerText = `CAPTURING: ${Math.floor(progress)}%`;
      
      if (progress >= 100) {
        clearInterval(interval);
        this.state.isCapturing = false;
        if (Math.random() > 0.5) {
          this.state.party.push(new Creature({...enemy}));
          document.getElementById('battle-log').innerText = `Success! ${enemy.name} joined your party!`;
          this.state.battle.enemies.shift();
          setTimeout(() => this.renderBattleUI(), 1500);
        } else {
          document.getElementById('battle-log').innerText = `Capture failed!`;
          progress = 0;
          captureBtn.innerText = "CAPTURE";
        }
      }
    }, 50);
  }

  startLoop() {
    const loop = () => {
      if (!this.state.pause) {
        PARTICLE_SYSTEM.update();
        if (this.state.battle) {
          // Handle some animations or small updates here if needed
        }
      }
      requestAnimationFrame(loop);
    };
    loop();
  }
}

// ── const: game ──
const game = new GameEngine();

// ── const: SAVE_KEY ──
const SAVE_KEY = "vitreous_resonance_save";

// ── const: PARTICLE_SYSTEM ──
const PARTICLE_SYSTEM = {
  particles: [],
  init() {
    this.particles = [];
    for (let i = 0; i < 80; i++) {
      this.particles.push({
        x: Math.random() * 800,
        y: Math.random() * 600,
        vx: (Math.random() - 0.5) * 0.6,
        vy: (Math.random() - 0.5) * 0.6,
        size: Math.random() * 2 + 1,
        alpha: Math.random(),
        life: 100 + Math.random() * 100
      });
    }
  },
  update() {
    this.particles.forEach(p => {
      p.x += p.vx;
      p.y += p.vy;
      p.life--;
      if (p.life <= 0) {
        p.life = 100 + Math.random() * 100;
        p.x = Math.random() * 800;
        p.y = Math.random() * 600;
      }
    });
  },
  draw(ctx) {
    ctx.fillStyle = "rgba(255, 255, 255, 0.2)";
    this.particles.forEach(p => {
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
      ctx.fill();
    });
  }
};

// ── main ──
// The main block handles the initial render and updates
// In this simple setup, GameEngine.init() handles it.
// However, we need to ensure the DOM is ready.
window.onload = () => {
  game.init();
};
</script>
</body>
</html>
<!-- agent-meta {"model":"google/gemma-4-12b-qat","provider":"lmstudio","persona":"composer","sessionId":"55d34ab3-f51a-4d44-97ab-a01c15773f52","tokensIn":208121,"tokensOut":28755,"tokensTotal":236876,"turns":14,"toolCalls":13,"failedToolCalls":3,"timestamp":"2026-07-13T23:52:36.317Z"} -->