Creature Collection
Gemma 4 31b · middle

Reviews & generation details
Reviews
Human review · Jul 17, 2026 · rubric 1
Historical path-based review. Association with these exact bytes is unverified.
- Visual design
- 1 / 5
- UX / feel
- 2 / 5
- Works
- 1 / 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 42,368 bytes · SHA-256 27bfe6fffd50
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Aetheria: The Shard Bound</title>
<style>
:root {
--bg-deep: #0a0b1e;
--glass: rgba(255, 255, 255, 0.1);
--glass-border: rgba(255, 255, 255, 0.2);
--accent: #00f2ff;
--text: #e0e6ed;
--gold: #ffd700;
--danger: #ff4d4d;
}
* { box-sizing: border-box; user-select: none; -webkit-tap-highlight-color: transparent; }
body, html {
margin: 0; padding: 0; width: 100%; height: 100%;
overflow: hidden; background: var(--bg-deep);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: var(--text);
}
#game-container {
position: relative; width: 100vw; height: 100vh;
display: flex; align-items: center; justify-content: center;
}
canvas {
image-rendering: pixelated;
max-width: 100%; max-height: 100%;
box-shadow: 0 0 50px rgba(0,0,0,0.5);
}
/* Glassmorphism UI */
.overlay {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
display: flex; flex-direction: column; align-items: center; justify-content: center;
background: rgba(0,0,0,0.7); backdrop-filter: blur(8px);
z-index: 100; transition: opacity 0.3s ease;
}
.hidden { display: none !important; opacity: 0; pointer-events: none; }
.panel {
background: var(--glass);
border: 1px solid var(--glass-border);
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.8);
text-align: center;
max-width: 80%;
animation: slideUp 0.4s ease-out;
}
@keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
button {
background: var(--glass);
border: 1px solid var(--accent);
color: var(--text);
padding: 12px 24px;
margin: 10px;
border-radius: 10px;
cursor: pointer;
font-size: 1rem;
transition: all 0.2s;
text-transform: uppercase;
letter-spacing: 1px;
}
button:hover { background: var(--accent); color: var(--bg-deep); box-shadow: 0 0 15px var(--accent); }
#dialogue-box {
position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
width: 90%; max-width: 700px;
background: rgba(10, 11, 30, 0.85);
border: 2px solid var(--accent);
border-radius: 15px;
padding: 20px;
z-index: 90;
cursor: pointer;
display: none;
}
#hud {
position: absolute; top: 20px; left: 20px;
pointer-events: none;
z-index: 80;
display: flex; gap: 10px;
}
.hud-item {
background: var(--glass);
padding: 8px 15px;
border-radius: 10px;
border: 1px solid var(--glass-border);
font-size: 0.9rem;
}
/* Battle UI */
#battle-ui {
position: absolute; bottom: 0; left: 0; width: 100%;
height: 40%; background: linear-gradient(transparent, var(--bg-deep));
display: none; grid-template-columns: 1fr 1fr;
padding: 20px; z-index: 110;
}
.battle-menu {
display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
}
.stat-bar-container {
width: 200px; height: 12px; background: #333;
border-radius: 6px; overflow: hidden; margin: 5px 0;
}
.stat-bar-fill { height: 100%; width: 100%; transition: width 0.5s ease; }
.hp-fill { background: #4caf50; }
.exp-fill { background: #2196f3; }
#controls {
position: absolute; bottom: 30px; right: 30px;
display: grid; grid-template-columns: repeat(3, 60px); gap: 10px;
z-index: 80;
}
.ctrl-btn {
width: 60px; height: 60px; background: var(--glass);
border: 1px solid var(--glass-border); border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-weight: bold; cursor: pointer;
}
.type-icon {
display: inline-block; width: 12px; height: 12px; border-radius: 50%; margin-right: 5px;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="mainCanvas"></canvas>
<div id="hud">
<div class="hud-item" id="hud-zone">Zone: Echo Plains</div>
<div class="hud-item" id="hud-party">Creatures: 0/6</div>
</div>
<div id="controls">
<div></div><div class="ctrl-btn" id="btn-up">W</div><div></div>
<div class="ctrl-btn" id="btn-left">A</div><div class="ctrl-btn" id="btn-down">S</div><div class="ctrl-btn" id="btn-right">D</div>
</div>
<div id="dialogue-box">
<div id="dialogue-text">Welcome to Aetheria...</div>
<div style="font-size: 0.7rem; text-align: right; margin-top: 10px; opacity: 0.6;">Click to continue...</div>
</div>
<div id="battle-ui">
<div id="battle-stats"></div>
<div class="battle-menu" id="battle-actions"></div>
</div>
<!-- Screens -->
<div id="screen-title" class="overlay">
<div class="panel">
<h1 style="color: var(--accent); font-size: 3rem; margin-bottom: 0.5rem;">AETHERIA</h1>
<p style="margin-bottom: 2rem; opacity: 0.8;">The Shard Bound</p>
<button onclick="game.startNewGame()">New Binding</button>
<button onclick="game.loadGame()">Continue</button>
<button onclick="game.toggleMute()" id="mute-btn">Audio: ON</button>
</div>
</div>
<div id="screen-pause" class="overlay hidden">
<div class="panel">
<h2>Pause</h2>
<button onclick="game.showMenu('team')">My Team</button>
<button onclick="game.showMenu('bag')">Inventory</button>
<button onclick="game.showMenu('lexicon')">Lexicon</button>
<button onclick="game.resume()">Resume</button>
</div>
</div>
<div id="screen-menu" class="overlay hidden">
<div class="panel" id="menu-content" style="max-height: 80vh; overflow-y: auto; width: 600px;">
<!-- Dynamic Content -->
</div>
<button onclick="game.resume()">Back</button>
</div>
<div id="screen-capture" class="overlay hidden">
<div class="panel">
<h2 id="capture-title">Binding Sequence</h2>
<p>Match the pulse to capture the Shard!</p>
<div id="capture-ring" style="width: 100px; height: 100px; border: 5px solid var(--accent); border-radius: 50%; margin: 20px auto; position: relative;">
<div id="capture-needle" style="position: absolute; top: 0; left: 50%; width: 2px; height: 50px; background: white; transform-origin: bottom; transform: rotate(0deg);"></div>
</div>
<button id="capture-btn">BIND</button>
</div>
</div>
</div>
<script>
/**
* AETHERIA: THE SHARD BOUND
* A fully autonomous creature collection RPG.
*/
// --- CORE CONSTANTS & SETTINGS ---
const CONFIG = {
TILE_SIZE: 48,
MAP_SIZE: 40, // 40x40 per zone
FPS: 60,
SAVE_KEY: 'aetheria_save_v1',
TYPES: {
LUMEN: { name: 'Lumen', color: '#ffffaa', beats: 'Umbra', resists: 'Umbra' },
UMBRA: { name: 'Umbra', color: '#7a52a5', beats: 'Lumen', resists: 'Lumen' },
TERRA: { name: 'Terra', color: '#aa8844', beats: 'Lumen', resists: 'Umbra' },
AETHER: { name: 'Aether', color: '#44aaff', beats: 'Terra', resists: 'Lumen' },
PYRE: { name: 'Pyre', color: '#ff4444', beats: 'Aether', resists: 'Terra' },
VOID: { name: 'Void', color: '#222222', beats: 'all', resists: 'all' }, // "F" Logic
PRISM: { name: 'Prism', color: '#ffffff', beats: 'Void', resists: 'all' } // "G" Logic
}
};
const TYPE_RELATIONS = {
'Lumen': { 'Umbra': 1.5, 'Terra': 0.5 },
'Umbra': { 'Lumen': 1.5, 'Terra': 0.5 },
'Terra': { 'Lumen': 1.5, 'Aether': 0.5 },
'Aether': { 'Terra': 1.5, 'Pyre': 0.5 },
'Pyre': { 'Aether': 1.5, 'Umbra': 0.5 },
'Void': { 'Lumen': 1.2, 'Umbra': 1.2, 'Terra': 1.2, 'Aether': 1.2, 'Pyre': 1.2, 'Prism': 0.5 },
'Prism': { 'Void': 2.0, 'Lumen': 1.0, 'Umbra': 1.0, 'Terra': 1.0, 'Aether': 1.0, 'Pyre': 1.0 }
};
// --- DATA DEFINITIONS ---
const ABILITIES = {
"Glimmer": { power: 40, acc: 95, type: 'Lumen', cost: 5, effect: 'none', desc: 'A soft flash of light.' },
"Soot-Slam": { power: 55, acc: 85, type: 'Terra', cost: 10, effect: 'slow', desc: 'Heavy impact of earth.' },
"Void-Siphon": { power: 60, acc: 90, type: 'Void', cost: 15, effect: 'drain', desc: 'Steal essence from target.' },
"Prism-Ray": { power: 75, acc: 80, type: 'Prism', cost: 20, effect: 'crit', desc: 'A beam of refracted light.' },
"Ember-Singe": { power: 50, acc: 90, type: 'Pyre', cost: 8, effect: 'burn', desc: 'Ignite the surface.' },
"Static-Pulse": { power: 45, acc: 95, type: 'Aether', cost: 10, effect: 'shock', desc: 'Disrupt neural flow.' },
"Gloom-Veil": { power: 0, acc: 100, type: 'Umbra', cost: 10, effect: 'barrier', desc: 'Shield with shadows.' },
"Shatter-Step": { power: 65, acc: 85, type: 'Terra', cost: 15, effect: 'stagger', desc: 'Break the foundation.' },
"Sky-Lash": { power: 60, acc: 90, type: 'Aether', cost: 12, effect: 'none', desc: 'A whip of lightning.' },
"Nova-Burst": { power: 110, acc: 70, type: 'Pyre', cost: 30, effect: 'burn', desc: 'Massive explosion.' },
"Zero-Point": { power: 80, acc: 85, type: 'Void', cost: 20, effect: 'frost', desc: 'Flash freeze space.' },
"Halo-Beam": { power: 90, acc: 90, type: 'Lumen', cost: 25, effect: 'heal', desc: 'Pure light auras.' },
"Quake-Core": { power: 100, acc: 75, type: 'Terra', cost: 25, effect: 'stagger', desc: 'Raze the earth.' },
"Nebula-Sway": { power: 40, acc: 100, type: 'Aether', cost: 5, effect: 'speed', desc: 'Shift perspective.' },
"Shadow-Stalk": { power: 70, acc: 80, type: 'Umbra', cost: 15, effect: 'crit', desc: 'Attack from blindspot.' },
"Crown-Flare": { power: 120, acc: 70, type: 'Prism', cost: 30, effect: 'none', desc: 'The apex of radiance.' },
"Dust-Cloud": { power: 30, acc: 100, type: 'Terra', cost: 5, effect: 'slow', desc: 'Obscure the field.' },
"Searing-Edge": { power: 65, acc: 90, type: 'Pyre', cost: 12, effect: 'burn', desc: 'A blade of plasma.' },
"Aether-Bolt": { power: 50, acc: 95, type: 'Aether', cost: 8, effect: 'shock', desc: 'Fast ionic discharge.' },
"Umbra-Claw": { power: 55, acc: 90, type: 'Umbra', cost: 10, effect: 'none', desc: 'Tearing dark energy.' },
"Solar-Wind": { power: 45, acc: 100, type: 'Lumen', cost: 10, effect: 'speed', desc: ' Push enemies back.' },
"Void-Scream": { power: 85, acc: 80, type: 'Void', cost: 18, effect: 'shock', desc: 'Tearing audio rift.' },
"Crystal-Spike": { power: 70, acc: 85, type: 'Prism', cost: 15, effect: 'none', desc: 'Sharp geometric shards.' },
"Magma-Vent": { power: 65, acc: 85, type: 'Pyre', cost: 15, effect: 'burn', desc: 'Liquid heat burst.' },
"Echo-Shock": { power: 50, acc: 95, type: 'Aether', cost: 10, effect: 'none', desc: 'Rhythmic energy pulse.' },
"Lumen-Swell": { power: 0, acc: 100, type: 'Lumen', cost: 15, effect: 'regen', desc: 'Restore vitality.' },
"Tectonic-Shift": { power: 90, acc: 80, type: 'Terra', cost: 20, effect: 'stagger', desc: 'Move the ground.' },
"Night-Terror": { power: 75, acc: 85, type: 'Umbra', cost: 15, effect: 'shock', desc: 'Invoke primal fear.' },
"Prism-Wall": { power: 0, acc: 100, type: 'Prism', cost: 20, effect: 'barrier', desc: 'Perfect reflection.' },
"Nova-Swell": { power: 100, acc: 80, type: 'Pyre', cost: 25, effect: 'burn', desc: 'Intense heat wave.' },
"Aether-Drift": { power: 40, acc: 100, type: 'Aether', cost: 10, effect: 'speed', desc: 'Float weightlessly.' },
"Void-Crush": { power: 110, acc: 70, type: 'Void', cost: 25, effect: 'none', desc: 'Compress reality.' },
"Lumina-Strike": { power: 65, acc: 90, type: 'Lumen', cost: 12, effect: 'none', desc: 'Focused light spear.' },
"Stone-Skin": { power: 0, acc: 100, type: 'Terra', cost: 15, effect: 'defup', desc: 'Harden exterior.' },
"Umbra-Siphon": { power: 60, acc: 85, type: 'Umbra', cost: 15, effect: 'drain', desc: 'Leach light.' },
"Prism-Shatter": { power: 80, acc: 80, type: 'Prism', cost: 20, effect: 'crit', desc: 'Fracture the air.' },
"Pyro-Clasm": { power: 95, acc: 80, type: 'Pyre', cost: 20, effect: 'burn', desc: 'Fiery devastation.' },
"ionic-Storm": { power: 70, acc: 90, type: 'Aether', cost: 18, effect: 'shock', desc: 'Chaotic sky-fire.' },
"Null-Void": { power: 50, acc: 100, type: 'Void', cost: 15, effect: 'slow', desc: 'Silence the area.' },
"Grand-Prism": { power: 150, acc: 60, type: 'Prism', cost: 40, effect: 'none', desc: 'The ultimate spectrum.' }
};
const CREATURE_DATA = {
// Starters
"Lumios": { name: "Lumios", type: "Lumen", hp: 45, atk: 52, def: 40, spd: 60, lore: "A floating orb of serene light.", learns: { 1: "Glimmer", 10: "Solar-Wind", 20: "Lumen-Swell" }, transform: { level: 25, item: "SunShard", to: "Solarix" } },
"Terrox": { name: "Terrox", type: "Terra", hp: 60, atk: 65, def: 50, spd: 30, lore: "A sturdy golem of etched basalt.", learns: { 1: "Soot-Slam", 10: "Dust-Cloud", 20: "Shatter-Step" }, transform: { level: 25, item: "EarthCore", to: "TerraTitan" } },
"Aeris": { name: "Aeris", type: "Aether", hp: 40, atk: 50, def: 35, spd: 75, lore: "A wispy creature of static clouds.", learns: { 1: "Static-Pulse", 10: "Aether-Bolt", 20: "Nebula-Sway" }, transform: { level: 25, item: "CloudGem", to: "AetherStorm" } },
// Wilds
"Gloomite": { name: "Gloomite", type: "Umbra", hp: 40, atk: 45, def: 40, spd: 50, lore: "Hides in the deep shadows of ruins.", learns: { 1: "Umbra-Claw", 12: "Gloom-Veil" }, transform: { level: 30, item: "DarkEssence", to: "UmbraLord" } },
"Pyrepup": { name: "Pyrepup", type: "Pyre", hp: 42, atk: 55, def: 38, spd: 55, lore: "Their paws leave singed marks.", learns: { 1: "Ember-Singe", 12: "Searing-Edge" }, transform: { level: 30, item: "FireSoot", to: "PyreDrake" } },
"Shardling": { name: "Shardling", type: "Prism", hp: 35, atk: 48, def: 42, spd: 45, lore: "Crystalline fragments of a lost world.", learns: { 1: "Crystal-Spike", 15: "Prism-Wall" }, transform: { level: 30, item: "PureCrystal", to: "PrismApex" } },
"Nilith": { name: "Nilith", type: "Void", hp: 50, atk: 50, def: 50, spd: 40, lore: "A creature from the edges of existence.", learns: { 1: "Void-Siphon", 15: "Null-Void" }, transform: { level: 30, item: "VoidSliver", to: "VoidTerror" } },
"Floris": { name: "Floris", type: "Terra", hp: 55, atk: 40, def: 60, spd: 35, lore: "Rooted deeply in the Echo Plains.", learns: { 1: "Soot-Slam", 12: "Lumen-Swell" } },
"Zappo": { name: "Zappo", type: "Aether", hp: 38, atk: 58, def: 30, spd: 70, lore: "Moves like a bolt of lightning.", learns: { 1: "Static-Pulse", 12: "Echo-Shock" } },
"Mistral": { name: "Mistral", type: "Umbra", hp: 45, atk: 42, def: 45, spd: 60, lore: "An elusive spirit of the dusk.", learns: { 1: "Umbra-Claw", 15: "Night-Terror" } },
"Blazowl": { name: "Blazowl", type: "Pyre", hp: 48, atk: 62, def: 35, spd: 65, lore: "Wings of living flame.", learns: { 1: "Ember-Singe", 15: "Nova-Swell" } },
"Gladius": { name: "Gladius", type: "Prism", hp: 52, atk: 55, def: 55, spd: 40, lore: "Armed with organic glass blades.", learns: { 1: "Crystal-Spike", 18: "Prism-Shatter" } },
"Nullon": { name: "Nullon", type: "Void", hp: 60, atk: 45, def: 60, spd: 35, lore: "Absorbs all surrounding sound.", learns: { 1: "Void-Siphon", 20: "Void-Crush" } },
"Krakos": { name: "Krakos", type: "Terra", hp: 80, atk: 70, def: 70, spd: 20, lore: "A behemoth of the deep caves.", learns: { 1: "Soot-Slam", 20: "Quake-Core" } },
"Sirocco": { name: "Sirocco", type: "Pyre", hp: 50, atk: 75, def: 40, spd: 70, lore: "A whirlwind of scorching air.", learns: { 1: "Ember-Singe", 20: "Pyro-Clasm" } },
"Voltane": { name: "Voltane", type: "Aether", hp: 45, atk: 80, def: 35, spd: 85, lore: "Pure electricity in biological form.", learns: { 1: "Static-Pulse", 20: "ionic-Storm" } },
"Noxis": { name: "Noxis", type: "Umbra", hp: 55, atk: 70, def: 50, spd: 60, lore: "The embodiment of a starless night.", learns: { 1: "Umbra-Claw", 20: "Shadow-Stalk" } },
"Lumira": { name: "Lumira", type: "Lumen", hp: 50, atk: 60, def: 45, spd: 65, lore: "Guided by the light of the Zenith.", learns: { 1: "Glimmer", 20: "Halo-Beam" } },
"Onyxian": { name: "Onyxian", type: "Prism", hp: 65, atk: 65, def: 65, spd: 45, lore: "A fortress of reflective onyx.", learns: { 1: "Crystal-Spike", 20: "Grand-Prism" } },
"Xylos": { name: "Xylos", type: "Void", hp: 70, atk: 60, def: 70, spd: 30, lore: "Ancient keeper of the Null Rift.", learns: { 1: "Void-Siphon", 20: "Zero-Point" } },
"Zenith": { name: "Zenith", type: "Prism", hp: 100, atk: 90, def: 90, spd: 80, lore: "The ultimate harmony of all colors.", learns: { 1: "Prism-Ray", 10: "Grand-Prism" } },
// Transformations
"Solarix": { name: "Solarix", type: "Lumen", hp: 80, atk: 85, def: 70, spd: 90, lore: "A living star of unmatched brilliance.", learns: { 25: "Halo-Beam", 35: "Crown-Flare" } },
"TerraTitan": { name: "TerraTitan", type: "Terra", hp: 110, atk: 100, def: 110, spd: 40, lore: "The mountain that walks.", learns: { 25: "Quake-Core", 35: "Shatter-Step" } },
"AetherStorm": { name: "AetherStorm", type: "Aether", hp: 70, atk: 95, def: 60, spd: 110, lore: "A storm that spans the horizons.", learns: { 25: "ionic-Storm", 35: "Nebula-Sway" } },
"UmbraLord": { name: "UmbraLord", type: "Umbra", hp: 85, atk: 90, def: 75, spd: 80, lore: "Master of the unseen realms.", learns: { 30: "Shadow-Stalk", 40: "Night-Terror" } },
"PyreDrake": { name: "PyreDrake", type: "Pyre", hp: 90, atk: 110, def: 70, spd: 85, lore: "Breath of incinerating plasma.", learns: { 30: "Nova-Burst", 40: "Pyro-Clasm" } },
"PrismApex": { name: "PrismApex", type: "Prism", hp: 80, atk: 95, def: 95, spd: 70, lore: "The peak of crystalline evolution.", learns: { 30: "Grand-Prism", 40: "Prism-Shatter" } },
"VoidTerror": { name: "VoidTerror", type: "Void", hp: 100, atk: 100, def: 100, spd: 60, lore: "The end of all things.", learns: { 30: "Void-Crush", 40: "Zero-Point" } },
"LumenQueen": { name: "LumenQueen", type: "Lumen", hp: 85, atk: 80, def: 80, spd: 95, lore: "Sovereign of the white light.", learns: { 30: "Halo-Beam", 40: "Solar-Wind" } },
"TerraBehemoth": { name: "TerraBehemoth", type: "Terra", hp: 130, atk: 120, def: 130, spd: 20, lore: "The world-breaker.", learns: { 30: "Quake-Core", 40: "Stone-Skin" } },
};
const ZONES = [
{ id: 0, name: "Echo Plains", color: "linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d)", enemyPool: ["Lumios", "Terrox", "Aeris", "Floris", "Zappo"], guardian: "LumenQueen", unlockAt: 0 },
{ id: 1, name: "Shatter Coast", color: "linear-gradient(135deg, #203a43, #2c5364)", enemyPool: ["Shardling", "Gloomite", "Mistral", "Gladius"], guardian: "PrismApex", unlockAt: 1 },
{ id: 2, name: "Pyre Peaks", color: "linear-gradient(135deg, #4b1212, #ff4b2b)", enemyPool: ["Pyrepup", "Blazowl", "Sirocco", "Lumina"], guardian: "PyreDrake", unlockAt: 2 },
{ id: 3, name: "Aether Void", color: "linear-gradient(135deg, #0f0c29, #302b63, #24243e)", enemyPool: ["Nilith", "Nullon", "Xylos", "Voltane"], guardian: "VoidTerror", unlockAt: 3 },
{ id: 4, name: "Obsidian Reach", color: "linear-gradient(135deg, #000000, #434343)", enemyPool: ["Noxis", "Krakos", "Onyxian", "Gloomite"], guardian: "UmbraLord", unlockAt: 4 },
{ id: 5, name: "Zenith Spire", color: "linear-gradient(135deg, #ffffff, #8e9eab)", enemyPool: ["Zenith", "VoidTerror", "PrismApex"], guardian: "Zenith", unlockAt: 5 },
];
const ITEMS = {
"Binding Orb": { type: "capture", tier: 1, desc: "Basic capture device." },
"Binding Sphere": { type: "capture", tier: 2, desc: "Enhanced stability." },
"Binding Core": { type: "capture", tier: 3, desc: "Maximum binding force." },
"Vital-Dew": { type: "heal", value: 50, desc: "Heals a creature for 50 HP." },
"Full-Restore": { type: "heal", value: 'full', desc: "Fully restores HP." },
"SunShard": { type: "catalyst", target: "Lumios", desc: "Crystallized sunlight." },
"EarthCore": { type: "catalyst", target: "Terrox", desc: "The heart of a mountain." },
"CloudGem": { type: "catalyst", target: "Aeris", desc: "A condensed storm-cloud." },
"DarkEssence": { type: "catalyst", target: "Gloomite", desc: "Pure concentrated shadow." },
"FireSoot": { type: "catalyst", target: "Pyrepup", desc: "Ash from a dying star." },
"PureCrystal": { type: "catalyst", target: "Shardling", desc: "A flawlessly clear prism." },
"VoidSliver": { type: "catalyst", target: "Nilith", desc: "A fragment of the void." },
};
// --- GAME ENGINE ---
class Game {
constructor() {
this.canvas = document.getElementById('mainCanvas');
this.ctx = this.canvas.getContext('2d');
this.muted = false;
this.state = 'TITLE'; // TITLE, OVERWORLD, BATTLE, MENU, DIALOGUE, CAPTURE
this.player = {
x: 5, y: 5,
zone: 0,
party: [],
inventory: { "Binding Orb": 5, "Vital-Dew": 3 },
unlockedZones: [0],
anchorsDefeated: [],
savePoint: { x: 5, y: 5, zone: 0 }
};
this.currentBattle = null;
this.dialogueQueue = [];
this.dialogueIndex = 0;
this.particles = [];
this.input = { w: false, a: false, s: false, d: false };
this.initCanvas();
this. setupControls();
this.loop();
}
initCanvas() {
this.canvas.width = 800;
this.canvas.height = 600;
}
setupControls() {
window.addEventListener('keydown', e => this.handleKey(e.key, true));
window.addEventListener('keyup', e => this.handleKey(e.key, false));
// Touch buttons
const bindBtn = (id, key) => {
const btn = document.getElementById(id);
btn.onmousedown = () => this.handleKey(key, true);
btn.onmouseup = () => this.handleKey(key, false);
btn.ontouchstart = (e) => { e.preventDefault(); this.handleKey(key, true); };
btn.ontouchend = (e) => { e.preventDefault(); this.handleKey(key, false); };
};
bindBtn('btn-up', 'w'); bindBtn('btn-down', 's');
bindBtn('btn-left', 'a'); bindBtn('btn-right', 'd');
document.getElementById('dialogue-box').onclick = () => this.nextDialogue();
document.getElementById('capture-btn').onclick = () => this.attemptCapture();
}
handleKey(key, isDown) {
const k = key.toLowerCase();
if (k === 'w' || k === 'arrowup') this.input.w = isDown;
if (k === 'a' || k === 'arrowleft') this.input.a = isDown;
if (k === 's' || k === 'arrowdown') this.input.s = isDown;
if (k === 'd' || k === 'arrowright') this.input.d = isDown;
if (isDown && k === 'escape') this.togglePause();
}
startNewGame() {
this.state = 'DIALOGUE';
document.getElementById('screen-title').classList.add('hidden');
this.dialogueQueue = [
"Welcome, Seeker, to the world of Aetheria...",
"The Shards of the old world have manifested as creatures of elemental energy.",
"You are a Binder, one who can unite these fragments.",
"Choose your first companion to begin your journey."
];
this.dialogueIndex = 0;
this.showDialogue();
// After intro, trigger starter selection
setTimeout(() => {
if (this.dialogueIndex >= this.dialogueQueue.length) {
this.showStarterSelection();
}
}, 100);
}
showStarterSelection() {
this.state = 'MENU';
const content = document.getElementById('menu-content');
content.innerHTML = `<h2>Choose Your Starter</h2><div style="display:flex; gap:20px; justify-content:center;">`;
const starters = ["Lumios", "Terrox", "Aeris"];
starters.forEach(s => {
const data = CREATURE_DATA[s];
content.innerHTML += `
<div class="panel" style="width:180px; cursor:pointer; background: rgba(255,255,255,0.05);" onclick="game.pickStarter('${s}')">
<div style="font-weight:bold; color:var(--accent)">${data.name}</div>
<div style="font-size:0.8rem; margin:10px 0;">${data.type}</div>
<div style="font-size:0.7rem; opacity:0.7;">${data.lore}</div>
</div>
`;
});
content.innerHTML += `</div>`;
document.getElementById('screen-menu').classList.remove('hidden');
}
pickStarter(name) {
const creature = this.createCreature(name, 5);
this.player.party.push(creature);
document.getElementById('screen-menu').classList.add('hidden');
this.state = 'OVERWORLD';
this.dialogueQueue = ["You have bound " + name + "! Now, explore the Echo Plains. Use WASD to move."];
this.dialogueIndex = 0;
this.showDialogue();
}
createCreature(species, level) {
const data = CREATURE_DATA[species];
const c = {
species,
level,
hp: data.hp + (level * 2),
maxHp: data.hp + (level * 2),
atk: data.atk + (level * 1.5),
def: data.def + (level * 1.2),
spd: data.spd + (level * 1.1),
type: data.type,
exp: 0,
nextLevel: 100,
moves: [],
status: null
};
// Initial moves
Object.keys(data.learns).forEach(lvl => {
if (level >= parseInt(lvl)) c.moves.push(data.learns[lvl]);
});
return c;
}
showDialogue() {
const box = document.getElementById('dialogue-box');
const text = document.getElementById('dialogue-text');
box.style.display = 'block';
text.innerText = this.dialogueQueue[this.dialogueIndex];
}
nextDialogue() {
this.dialogueIndex++;
if (this.dialogueIndex < this.dialogueQueue.length) {
this.showDialogue();
} else {
document.getElementById('dialogue-box').style.display = 'none';
if (this.state === 'DIALOGUE') this.state = 'OVERWORLD';
}
}
togglePause() {
if (this.state === 'BATTLE') return;
if (this.state === 'OVERWORLD') {
this.state = 'PAUSE';
document.getElementById('screen-pause').classList.remove('hidden');
} else if (this.state === 'PAUSE') {
this.resume();
}
}
resume() {
document.getElementById('screen-pause').classList.add('hidden');
document.getElementById('screen-menu').classList.add('hidden');
this.state = 'OVERWORLD';
}
showMenu(type) {
this.state = 'MENU';
document.getElementById('screen-pause').classList.add('hidden');
document.getElementById('screen-menu').classList.remove('hidden');
const content = document.getElementById('menu-content');
if (type === 'team') {
content.innerHTML = `<h2>Your Bound Shards</h2>`;
this.player.party.forEach((c, i) => {
content.innerHTML += `
<div class="panel" style="margin:10px 0; text-align:left; display:flex; justify-content:space-between; align-items:center;">
<div>
<strong>${c.species}</strong> (Lv.${c.level}) - <span style="color:${CONFIG.TYPES[c.type].color}">${c.type}</span><br>
HP: ${c.hp}/${c.maxHp} | ATK: ${Math.floor(c.atk)} | DEF: ${Math.floor(c.def)} | SPD: ${Math.floor(c.spd)}
</div>
<div>
${c.moves.map(m => `<div style="font-size:0.7rem;">${m}</div>`).join('')}
</div>
</div>
`;
});
} else if (type === 'bag') {
content.innerHTML = `<h2>Inventory</h2>`;
for (let item in this.player.inventory) {
const qty = this.player.inventory[item];
if (qty > 0) {
content.innerHTML += `<div class="panel" style="margin:10px 0; display:flex; justify-content:space-between;">
<span>${item} x${qty}</span>
<button onclick="game.useItem('${item}')">Use</button>
</div>`;
}
}
} else if (type === 'lexicon') {
content.innerHTML = `<h2>Aetherian Lexicon</h2>`;
Object.keys(CREATURE_DATA).forEach(k => {
const d = CREATURE_DATA[k];
content.innerHTML += `<div style="margin:5px 0; font-size:0.8rem;"><strong>${d.name}</strong> [${d.type}]: ${d.lore}</div>`;
});
}
}
useItem(itemName) {
const item = ITEMS[itemName];
if (!item) return;
if (item.type === 'heal') {
if (this.player.party.length === 0) return;
const target = this.player.party[0];
if (item.value === 'full') target.hp = target.maxHp;
else target.hp = Math.min(target.maxHp, target.hp + item.value);
this.player.inventory[itemName]--;
this.showMenu('bag');
} else if (item.type === 'catalyst') {
// Transformation logic: Find creature of that base species
const index = this.player.party.findIndex(c => c.species === item.target);
if (index !== -1) {
const c = this.player.party[index];
const data = CREATURE_DATA[c.species];
if (c.level >= data.transform.level) {
const nextSpecies = data.transform.to;
const nextData = CREATURE_DATA[nextSpecies];
c.species = nextSpecies;
c.type = nextData.type;
c.atk += 20; c.def += 20; c.hp += 40; c.maxHp += 40;
c.moves.push(...Object.keys(nextData.learns).filter(l => c.level >= parseInt(l)).map(l => nextData.learns[l]));
this.player.inventory[itemName]--;
alert(`${c.species} has evolved!`);
} else {
alert(`${c.species} is not yet ready to transform.`);
}
} else {
alert("You have no creature that can use this catalyst.");
}
this.showMenu('bag');
}
}
// --- OVERWORLD LOGIC ---
updateOverworld() {
let moved = false;
if (this.input.w) { this.player.y -= 0.1; moved = true; }
if (this.input.s) { this.player.y += 0.1; moved = true; }
if (this.input.a) { this.player.x -= 0.1; moved = true; }
if (this.input.d) { this.player.x += 0.1; moved = true; }
// Boundaries
this.player.x = Math.max(0, Math.min(CONFIG.MAP_SIZE, this.player.x));
this.player.y = Math.max(0, Math.min(CONFIG.MAP_SIZE, this.player.y));
if (moved) {
// Encounter check
if (Math.random() < 0.005) {
this.startBattle();
}
}
}
drawOverworld() {
const zone = ZONES[this.player.zone];
const grad = this.ctx.createLinearGradient(0,0, this.canvas.width, this.canvas.height);
grad.addColorStop(0, '#1a1a2e');
grad.addColorStop(1, '#16213e');
this.ctx.fillStyle = grad;
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
// Draw grid/terrain
this.ctx.strokeStyle = 'rgba(255,255,255,0.05)';
this.ctx.lineWidth = 1;
for (let i = 0; i <= CONFIG.MAP_SIZE; i++) {
this.ctx.beginPath();
this.ctx.moveTo(i * (this.canvas.width / CONFIG.MAP_SIZE), 0);
this.ctx.lineTo(i * (this.canvas.width / CONFIG.MAP_SIZE), this.canvas.height);
this.ctx.stroke();
this.ctx.beginPath();
this.ctx.moveTo(0, i * (this.canvas.height / CONFIG.MAP_SIZE));
this.ctx.lineTo(this.canvas.width, i * (this.canvas.height / CONFIG.MAP_SIZE));
this.ctx.stroke();
}
// Player
this.ctx.fillStyle = varColor('--accent');
this.ctx.beginPath();
this.ctx.arc(this.player.x * (this.canvas.width / CONFIG.MAP_SIZE), this.player.y * (this.canvas.height / CONFIG.MAP_SIZE), 8, 0, Math.PI*2);
this.ctx.fill();
this.ctx.shadowBlur = 15;
this.ctx.shadowColor = varColor('--accent');
this.ctx.stroke();
this.ctx.shadowBlur = 0;
// HUD update
document.getElementById('hud-zone').innerText = `Zone: ${zone.name}`;
document.getElementById('hud-party').innerText = `Creatures: ${this.player.party.length}/6`;
}
// --- BATTLE SYSTEM ---
startBattle() {
this.state = 'BATTLE';
const zone = ZONES[this.player.zone];
const enemySpecies = zone.enemyPool[Math.floor(Math.random() * zone.enemyPool.length)];
const level = 5 + (this.player.zone * 5) + Math.floor(Math.random() * 3);
const enemy = this.createCreature(enemySpecies, level);
this.currentBattle = {
player: this.player.party[0],
enemy: enemy,
turn: 'player',
turnQueue: [],
log: "A wild " + enemySpecies + " appeared!"
};
document.getElementById('battle-ui').style.display = 'grid';
this.updateBattleUI();
}
updateBattleUI() {
const b = this.currentBattle;
const stats = document.getElementById('battle-stats');
stats.innerHTML = `
<div style="margin-bottom:20px;">
<strong>${b.enemy.species}</strong> (Lv.${b.enemy.level})<br>
<div class="stat-bar-container"><div class="stat-bar-fill hp-fill" style="width:${(b.enemy.hp/b.enemy.maxHp)*100}%"></div></div>
</div>
<div>
<strong>${b.player.species}</strong> (Lv.${b.player.level})<br>
<div class="stat-bar-container"><div class="stat-bar-fill hp-fill" style="width:${(b.player.hp/b.player.maxHp)*100}%"></div></div>
<div style="font-size:0.7rem;">HP: ${Math.floor(b.player.hp)} / ${Math.floor(b.player.maxHp)}</div>
</div>
`;
const actions = document.getElementById('battle-actions');
actions.innerHTML = '';
if (b.turn === 'player') {
b.player.moves.forEach(m => {
const btn = document.createElement('button');
btn.innerText = m;
btn.onclick = () => this.executeMove(m);
actions.appendChild(btn);
});
const captureBtn = document.createElement('button');
captureBtn.innerText = "Bind Shard";
captureBtn.onclick = () => this.initCaptureSequence();
actions.appendChild(captureBtn);
} else {
actions.innerHTML = `<div style="grid-column: span 2; text-align:center; font-style:italic; padding:20px;">Enemy is calculating...</div>`;
}
}
executeMove(moveName) {
const move = ABILITIES[moveName];
const b = this.currentBattle;
const dmg = this.calculateDamage(b.player, b.enemy, move);
b.enemy.hp -= dmg;
this.logBattle(`Your ${b.player.species} used ${moveName} for ${Math.floor(dmg)} damage!`);
if (b.enemy.hp <= 0) {
this.winBattle();
} else {
b.turn = 'enemy';
this.updateBattleUI();
setTimeout(() => this.enemyTurn(), 1000);
}
}
enemyTurn() {
const b = this.currentBattle;
const moveName = b.enemy.moves[Math.floor(Math.random() * b.enemy.moves.length)];
const move = ABILITIES[moveName];
const dmg = this.calculateDamage(b.enemy, b.player, move);
b.player.hp -= dmg;
this.logBattle(`${b.enemy.species} used ${moveName} for ${Math.floor(dmg)} damage!`);
if (b.player.hp <= 0) {
alert("Your creature fainted! Returning to last shrine...");
this.player.x = this.player.savePoint.x;
this.player.y = this.player.savePoint.y;
this.player.party.forEach(c => c.hp = c.maxHp);
this.endBattle();
} else {
b.turn = 'player';
this.updateBattleUI();
}
}
calculateDamage(attacker, defender, move) {
const mult = (TYPE_RELATIONS[attacker.type] && TYPE_RELATIONS[attacker.type][defender.type]) || 1.0;
const stab = (move.type === attacker.type) ? 1.25 : 1.0;
const baseDmg = (0.4 * attacker.level * attacker.atk / defender.def * mult * stab);
return Math.floor(baseDmg + Math.random() * 5);
}
logBattle(msg) {
console.log(msg);
// In a full version, we'd have a log scroll
}
winBattle() {
const b = this.currentBattle;
const expGain = 50 + (b.enemy.level * 10);
b.player.exp += expGain;
if (b.player.exp >= b.player.nextLevel) {
b.player.level++;
b.player.exp -= b.player.nextLevel;
b.player.maxHp += 5; b.player.hp = b.player.maxHp;
b.player.atk += 2; b.player.def += 2; b.player.spd += 2;
alert(`${b.player.species} leveled up to ${b.player.level}!`);
}
alert(`Victory! Gained ${expGain} EXP.`);
this.endBattle();
}
endBattle() {
this.state = 'OVERWORLD';
document.getElementById('battle-ui').style.display = 'none';
this.currentBattle = null;
}
initCaptureSequence() {
this.state = 'CAPTURE';
document.getElementById('screen-capture').classList.remove('hidden');
this.captureAngle = 0;
this.captureDir = 1;
this.captureInterval = setInterval(() => {
this.captureAngle += 4 * this.captureDir;
if (this.captureAngle > 360 || this.captureAngle < 0) this.captureDir *= -1;
document.getElementById('capture-needle').style.transform = `rotate(${this.captureAngle}deg)`;
}, 16);
}
attemptCapture() {
const b = this.currentBattle;
clearInterval(this.captureInterval);
// Simple success check: if angle is between 340 and 20 (top)
const normAngle = this.captureAngle % 360;
const success = (normAngle > 340 || normAngle < 20) || (Math.random() < (1 - b.enemy.hp/b.enemy.maxHp));
if (success && this.player.inventory["Binding Orb"] > 0) {
this.player.inventory["Binding Orb"]--;
this.player.party.push({...b.enemy});
alert(`Successfully bound ${b.enemy.species}!`);
} else {
alert("The binding failed...");
}
document.getElementById('screen-capture').classList.add('hidden');
this.state = 'BATTLE';
this.updateBattleUI();
}
// --- MAIN LOOP ---
loop() {
this.update();
this.draw();
requestAnimationFrame(() => this.loop());
}
update() {
if (this.state === 'OVERWORLD') {
this.updateOverworld();
}
}
draw() {
if (this.state === 'OVERWORLD' || this.state === 'DIALOGUE') {
this.drawOverworld();
} else if (this.state === 'BATTLE') {
const b = this.currentBattle;
const ctx = this.ctx;
ctx.fillStyle = '#050510';
ctx.fillRect(0,0, this.canvas.width, this.canvas.height);
// Enemy
ctx.fillStyle = CONFIG.TYPES[b.enemy.type].color;
ctx.beginPath();
ctx.arc(600, 200, 40, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.fillText(b.enemy.species, 570, 150);
// Player
ctx.fillStyle = CONFIG.TYPES[b.player.type].color;
ctx.beginPath();
ctx.arc(200, 400, 50, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.fillText(b.player.species, 170, 340);
} else if (this.state === 'MENU' || this.state === 'PAUSE') {
this.ctx.fillStyle = 'black';
this.ctx.fillRect(0,0, this.canvas.width, this.canvas.height);
}
}
toggleMute() {
this.muted = !this.muted;
document.getElementById('mute-btn').innerText = `Audio: ${this.muted ? 'OFF' : 'ON'}`;
}
loadGame() {
const saved = localStorage.getItem(CONFIG.SAVE_KEY);
if (saved) {
const data = JSON.parse(saved);
this.player = data;
this.state = 'OVERWORLD';
document.getElementById('screen-title').classList.add('hidden');
} else {
alert("No saved binding found.");
}
}
saveGame() {
localStorage.setItem(CONFIG.SAVE_KEY, JSON.stringify(this.player));
}
}
function varColor(name) {
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
}
const game = new Game();
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"middle","sessionId":"8c2cb690-c0fe-4123-b698-e216eddd2275","tokensIn":18544,"tokensOut":14733,"tokensTotal":33277,"turns":2,"toolCalls":1,"failedToolCalls":0,"timestamp":"2026-07-14T16:16:08.082Z"} -->