← Deckbuilder results

Deckbuilder

GPT 5.6 terra · typescript

Initial view of Deckbuilder

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

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

Generation prompt · exact text unavailable

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

Inspect original source 29,177 bytes · SHA-256 111fffa66648
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Shadows of Aethelgard</title>
<style>
/* ── css: base ── */
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@500;600;700;800&family=Crimson+Pro:wght@400;600;700&display=swap');*{box-sizing:border-box}body{margin:0;background:#080a12;color:#eee7d1;font-family:'Crimson Pro',Georgia,serif;overflow-x:hidden}#game{min-height:100vh;background:radial-gradient(ellipse at 50% 38%,#25243a 0%,#111220 42%,#07080e 100%);position:relative;overflow:hidden}#game:before{content:'';position:absolute;inset:0;pointer-events:none;opacity:.38;background-image:linear-gradient(135deg,transparent 48%,#c29a4a0d 49%,transparent 50%),radial-gradient(circle at 15% 80%,#4c286b33,transparent 25%)}button{font-family:Cinzel,serif;color:#eadfbf;cursor:pointer}.metal{border:1px solid #8e7645;background:linear-gradient(135deg,#3c3b40,#161820 55%,#4b4131);box-shadow:inset 0 0 0 1px #08090c,0 4px 12px #0008;letter-spacing:.06em;padding:10px 14px;transition:.18s}.metal:hover{filter:brightness(1.25);transform:translateY(-1px)}#topbar{height:74px;padding:10px 28px;border-bottom:1px solid #6d5b36;background:#0c0d14e8;display:flex;align-items:center;justify-content:space-between;position:relative;z-index:3}.brand{display:flex;align-items:center;gap:10px;font-family:Cinzel,serif;color:#ddbd69;line-height:.9;letter-spacing:.12em}.brand b{font-size:19px}.brand small{font-size:8px;letter-spacing:.3em;color:#9b8760}.sigil{font-size:32px;text-shadow:0 0 15px #d8a94b}.brand+ #runstats{}#runstats{font-family:Cinzel,serif;color:#beb39c;font-size:12px;letter-spacing:.12em;display:flex;gap:28px}.stat-gold{color:#e3bc54}.stat-floor{color:#aa99d8}#stage{position:relative;z-index:1;display:grid;grid-template-columns:215px 1fr 205px;min-height:434px;max-width:1150px;margin:auto}.side-panel{margin:28px 12px;padding:16px 13px;background:linear-gradient(145deg,#151621df,#0c0d14e8);border:1px solid #4d4653;box-shadow:0 14px 30px #0007}.portrait-wrap{text-align:center;border-bottom:1px solid #51475a;padding-bottom:10px}.sprite{margin:auto;position:relative}.sprite:after,.sprite:before{content:'';position:absolute}.hero-idle{width:124px;height:155px;background:linear-gradient(135deg,transparent 24%,#8c9ab3 25% 37%,transparent 38%),linear-gradient(45deg,transparent 30%,#4c5365 31% 47%,transparent 48%),radial-gradient(ellipse at 50% 27%,#cbc3b3 0 13%,transparent 14%),linear-gradient(90deg,transparent 30%,#293041 31% 68%,transparent 69%);clip-path:polygon(50% 0,64% 13%,70% 33%,91% 93%,70% 100%,50% 80%,30% 100%,8% 93%,30% 33%,36% 13%);filter:drop-shadow(0 9px 10px #000)}.hero-idle:after{inset:17px 43px 105px;background:#151720;border-radius:50%;box-shadow:0 0 10px #b5d6ff}.hero-idle:before{width:75px;height:55px;left:25px;top:82px;border:3px solid #8b97ab;border-top:0;transform:skew(-12deg)}.portrait-label{font:700 11px Cinzel;color:#d6bf82;letter-spacing:.12em;margin-top:3px}#heroStats{padding:11px 2px;font:600 15px Cinzel;line-height:1.8}.life{color:#dd7373}.ward{color:#86b8de}.deck-note{font:10px Cinzel;color:#817b83;border-top:1px solid #373541;padding-top:9px;letter-spacing:.08em}#battlefield{text-align:center;position:relative;padding-top:18px}#roomLabel{font:700 12px Cinzel;letter-spacing:.23em;color:#b5a27b}.runes{color:#6d4e91;font-size:19px;letter-spacing:15px;margin:7px 0 -3px;text-shadow:0 0 12px #66429b}.enemy-idle{width:185px;height:205px;margin-top:0;background:radial-gradient(ellipse at 50% 24%,#b9a9a1 0 10%,transparent 11%),radial-gradient(ellipse at 50% 38%,#4a3b4b 0 25%,transparent 26%),linear-gradient(90deg,transparent 10%,#392b3c 11% 29%,transparent 30% 70%,#392b3c 71% 89%,transparent 90%),linear-gradient(55deg,transparent 35%,#33273a 36% 60%,transparent 61%);clip-path:polygon(50% 2%,64% 14%,70% 39%,95% 88%,72% 96%,50% 79%,28% 96%,5% 88%,30% 39%,36% 14%);filter:drop-shadow(0 14px 13px #000)}.enemy-idle:after{width:9px;height:6px;background:#f2b654;box-shadow:21px 0 #f2b654,0 0 12px 3px #f79031;left:77px;top:50px}.enemy-idle:before{width:100px;height:60px;border:2px solid #72606d;border-top:0;left:42px;top:103px;border-radius:0 0 45% 45%}.bar{height:15px;max-width:295px;margin:4px auto;position:relative;background:#090a0e;border:1px solid #67595c;box-shadow:inset 0 1px 3px #000}.bar i{position:absolute;left:0;top:0;bottom:0;background:linear-gradient(90deg,#672b36,#d65851);transition:width .4s}.bar span{position:relative;font:10px Cinzel;line-height:13px;text-shadow:1px 1px #000}#enemyName{font:700 13px Cinzel;letter-spacing:.16em;color:#e1ced0}#enemyIntent{margin:10px auto;color:#dd8c74;font:600 13px Cinzel;letter-spacing:.07em}.statuses{min-height:20px;font:11px Cinzel;color:#e0b04b}.statuses span{padding:3px 6px;margin:2px;border:1px solid #855d2c;background:#2b2014}.statuses .poison{color:#afcf71;border-color:#6f8a49}.statuses .stun{color:#bd9de6;border-color:#735494}#combatLog{position:absolute;left:50%;transform:translateX(-50%);bottom:12px;width:85%;height:32px;color:#b8b2c1;font-style:italic;font-size:17px;text-shadow:0 1px #000}.meter-title{font:10px Cinzel;letter-spacing:.16em;color:#ada080;margin:5px 0 8px}#energyPips{display:flex;gap:5px;margin-bottom:18px}.pip{height:22px;flex:1;clip-path:polygon(50% 0,100% 32%,82% 100%,18% 100%,0 32%);background:#292938;border:1px solid #6d5fa5}.pip.active{background:linear-gradient(#c3a9ff,#6043b5);box-shadow:0 0 10px #8766e5}#defenseValue{font:700 30px Cinzel;color:#91bee5;border:1px solid #416075;background:#101b27;padding:5px;margin-bottom:20px}.end{width:100%;font-size:11px}.end span{color:#cfb069}.shop{width:100%;margin-top:10px;font-size:9px;color:#e3bc54}
/* ── css: cards-modal ── */
#handArea{position:relative;z-index:2;border-top:1px solid #4b424c;background:linear-gradient(#10111adb,#08090f);padding:10px 25px 18px;min-height:225px}#handLabel{max-width:1100px;margin:auto;font:700 11px Cinzel;letter-spacing:.17em;color:#c8b68a}#handLabel span{font:italic 14px 'Crimson Pro';letter-spacing:0;color:#827b83;margin-left:10px}#hand{display:flex;justify-content:center;gap:12px;padding-top:9px;min-height:177px}.card{width:142px;height:184px;flex:0 0 auto;position:relative;border:1px solid #ba9b56;border-radius:8px;background:linear-gradient(135deg,#27202a,#100f15 58%,#382620);box-shadow:0 8px 14px #0009,inset 0 0 0 3px #16131b,inset 0 0 0 5px #a98a48;overflow:hidden;padding:12px 11px;cursor:pointer;transition:.18s;transform-origin:bottom}.card:hover:not(.disabled){transform:translateY(-17px) scale(1.04);filter:brightness(1.2)}.card.disabled{filter:grayscale(.65) brightness(.55);cursor:not-allowed}.card.attack{box-shadow:0 8px 14px #0009,inset 0 0 0 3px #271212,inset 0 0 0 5px #bd5d43}.card.defense{box-shadow:0 8px 14px #0009,inset 0 0 0 3px #101d2c,inset 0 0 0 5px #4c95bd}.card.other{box-shadow:0 8px 14px #0009,inset 0 0 0 3px #241629,inset 0 0 0 5px #9561ad}.card:before{content:'';position:absolute;inset:8px 8px auto;height:50px;opacity:.6;background:radial-gradient(circle,#9d8144 2px,transparent 3px),linear-gradient(135deg,transparent 45%,#997b4444 46% 54%,transparent 55%);background-size:14px 14px,auto;border:1px solid #806533}.card-type{position:relative;font:8px Cinzel;letter-spacing:.15em;color:#b8a983}.card-name{position:relative;margin-top:45px;font:700 14px Cinzel;color:#f0e2bd;line-height:1.05}.card-ability{position:relative;margin-top:8px;font-size:14px;line-height:1.05;color:#c4bbc0}.card-stats{position:absolute;left:11px;right:11px;bottom:10px;border-top:1px solid #6d5b42;padding-top:5px;display:flex;justify-content:space-between;font:700 12px Cinzel}.cost{position:absolute;right:9px;top:8px;z-index:1;width:25px;height:25px;text-align:center;padding-top:5px;background:#4e3c86;border:1px solid #c3a2e9;border-radius:50%;font:700 12px Cinzel;box-shadow:0 0 8px #8064d7}.atk{color:#e77358}.def{color:#7db9e7}.card-back{background:radial-gradient(ellipse,#4c2d64,#15101d 63%);display:flex;align-items:center;justify-content:center;font:30px Cinzel;color:#c29b52}.card-back:after{content:'✦';border:1px solid #bd9651;border-radius:50%;padding:20px}.damage-float{position:absolute;z-index:10;color:#f2a15f;font:bold 28px Cinzel;animation:float .8s forwards;text-shadow:0 2px #000}@keyframes float{to{transform:translateY(-55px);opacity:0}}#overlay{position:fixed;z-index:20;inset:0;background:#05060bdc;display:flex;align-items:center;justify-content:center;padding:18px}.hidden{display:none!important}#modal{width:min(900px,96vw);max-height:90vh;overflow:auto;padding:28px;background:linear-gradient(145deg,#201d29,#0c0d14);border:1px solid #aa8a4b;box-shadow:0 0 0 5px #08090d,0 25px 60px #000;font-size:18px}.modal-title{text-align:center;font:700 25px Cinzel;color:#e4c374;letter-spacing:.1em;margin-bottom:7px}.modal-sub{text-align:center;color:#aaa0a5;margin-bottom:22px}.class-grid,.shop-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.class-choice{border:1px solid #5d5365;padding:16px;background:#12131c;cursor:pointer;transition:.2s;text-align:center}.class-choice:hover{border-color:#d1ad61;transform:translateY(-3px)}.class-icon{font:37px Cinzel;color:#cfad65}.class-choice b{font:15px Cinzel;color:#e1d0aa;display:block;margin:7px}.class-choice small{color:#9e939e}.modal-close{float:right}.shop-grid{grid-template-columns:repeat(4,1fr)}.shop-card{min-width:0}.shop-card.card{width:100%;height:194px}.buy-label{position:absolute;bottom:7px;left:8px;right:8px;text-align:center;font:9px Cinzel;color:#e8c96d}.modal-actions{text-align:center;margin-top:20px}.modal-actions .metal{min-width:175px}@media(max-width:720px){#topbar{padding:9px 12px}.brand b{font-size:14px}#runstats{gap:8px;font-size:9px}#menuBtn{display:none}#stage{grid-template-columns:110px 1fr 105px;min-height:360px}.side-panel{margin:14px 5px;padding:9px 6px}.hero-idle{transform:scale(.72);margin:-20px auto}.portrait-label{font-size:8px}#heroStats{font-size:11px}.enemy-idle{transform:scale(.8);margin:-15px auto}.deck-note{font-size:7px}.end,.shop{font-size:7px;padding:8px 3px}.meter-title{font-size:8px}.pip{height:16px}#defenseValue{font-size:22px}.card{width:116px;height:164px;padding:10px 9px}.card-name{font-size:11px}.card-ability{font-size:12px}.card-stats{font-size:10px}.card:nth-child(n+5){display:none}#hand{gap:5px}.class-grid,.shop-grid{grid-template-columns:repeat(2,1fr)}#modal{padding:18px}.shop-grid .card{width:100%}}
/* ── css: svg-card-assets ── */
.card-art{position:absolute;inset:9px 9px auto;width:calc(100% - 18px);height:48px;z-index:0;fill:#16121a;stroke:#c7694e;stroke-width:1.1;opacity:.95}.card-art circle{fill:#3c1820}.card-art.wither{stroke:#a56acc}.card-art.wither circle{fill:#352044}.card-art.shroud{stroke:#67a8d0}.card-art.shroud circle{fill:#132b3c}.card .cost,.card .card-type,.card .card-name,.card .card-ability,.card .card-stats{z-index:1}.card.attack .card-art{stroke:#c75e45}.card.defense .card-art{stroke:#63a6d0}.card.other .card-art{stroke:#ad70bb}.card-back.bastion{background:radial-gradient(ellipse,#2b455b,#11141c 63%)}.card-back.wither{background:radial-gradient(ellipse,#52265e,#17101c 63%)}.card-back.shroud{background:radial-gradient(ellipse,#193a52,#0d111b 63%)}
</style>
</head>
<body>
<div id="game"><header id="topbar"><div class="brand"><span class="sigil">✦</span><div><b>SHADOWS</b><small>OF AETHELGARD</small></div></div><div id="runstats"></div><button id="menuBtn" class="metal">☰ Codex</button></header><main id="stage"><section id="heroPanel" class="side-panel"><div class="portrait-wrap"><div id="heroSprite" class="sprite hero-idle"></div><div class="portrait-label" id="heroName">THE BASTION</div></div><div id="heroStats"></div><div class="deck-note">DECK <span id="deckCount">12</span> · DISCARD <span id="discardCount">0</span></div></section><section id="battlefield"><div id="roomLabel">FLOOR I · ROOM 1</div><div id="enemyArea"><div class="runes">✧ &nbsp; ⟡ &nbsp; ✧</div><div id="enemySprite" class="sprite enemy-idle"></div><div id="enemyName">GOBLIN WRAITH</div><div class="bar enemybar"><i id="enemyHpFill"></i><span id="enemyHpText"></span></div><div id="enemyIntent"></div><div id="enemyStatuses" class="statuses"></div></div><div id="combatLog"></div></section><aside id="actionPanel" class="side-panel"><div class="meter-title">AETHER</div><div id="energyPips"></div><div class="meter-title">WARD</div><div id="defenseValue">0</div><button id="endTurn" class="metal end">END TURN <span>↵</span></button><button id="shopBtn" class="metal shop">✦ VISIT MERCHANT</button></aside></main><section id="handArea"><div id="handLabel">YOUR HAND <span>click a card to invoke it</span></div><div id="hand"></div></section><div id="overlay" class="hidden"><div id="modal"></div></div></div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {};
function __require(id) {
  if (__cache[id]) return __cache[id].exports;
  var module = __cache[id] = { exports: {} };
  var factory = __mods[id];
  if (!factory) throw new Error("Module not found: " + id);
  factory(module.exports, function (spec) {
    var target = (__map[id] && __map[id][spec]) || spec;
    return __require(target);
  }, module);
  return module.exports;
}

// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const $ = (s) => document.querySelector(s);
const roster = {
    bastion: [['Slam', 'attack', 2, 3, 0, 'Deals 3. At full health, draw 1', undefined, false, 0, 0, 1], ['Earthquake', 'attack', 3, 4, 0, 'Deals 4 to all enemies', undefined, true], ['Stone Fist', 'attack', 1, 2, 0, 'Deals 2'], ['Boulder', 'attack', 4, 6, 0, 'Deals 6'], ['Shield Wall', 'defense', 0, 0, 4, 'Gain 4 Ward'], ['Iron Skin', 'defense', 2, 0, 5, 'Gain 5 permanent Ward. Discard 1', undefined, false, 0, 0, 0, 0, true], ['Stand Firm', 'defense', 1, 0, 2, 'Gain 2 Ward'], ['Taunt', 'defense', 2, 0, 3, 'Gain 3 Ward. Stun target', 'stun'], ['Search', 'other', 2, 0, 0, 'Draw 1 card', undefined, false, 0, 0, 1], ['Repair', 'other', 3, 0, 0, 'Heal 6', undefined, false, 6], ['Rally', 'other', 1, 0, 0, 'Draw 1. Gain 1 Aether', undefined, false, 0, 1, 1], ['Loot', 'other', 2, 0, 0, 'Gain 5 Gold', undefined, false, 0, 0, 0, 5]],
    wither: [['Fireball', 'attack', 2, 3, 0, 'Deals 3. Apply Burn', 'burn'], ['Inferno', 'attack', 3, 4, 0, 'Deals 4. Apply 3 Burn', 'burn'], ['Ember', 'attack', 1, 2, 0, 'Deals 2. Apply Burn', 'burn'], ['Pyre', 'attack', 4, 5, 0, 'Deals 5. All foes Burn', 'burn', true], ['Scorch Shield', 'defense', 2, 0, 3, 'Gain 3 Ward'], ['Heat Resist', 'defense', 1, 0, 1, 'Gain 1 Ward, 1 Aether', undefined, false, 0, 1], ['Smoke', 'defense', 2, 0, 4, 'Gain 4 Ward'], ['Endure', 'defense', 3, 0, 2, 'Gain 2 Ward. Heal 2', undefined, false, 2], ['Poison', 'other', 2, 0, 0, 'Apply 1 Poison', 'poison'], ['Burn', 'other', 3, 0, 0, '3 damage + 2 Burn to all', 'burn', true], ['Slow', 'other', 1, 0, 0, 'Draw 1. Stun target', 'stun', false, 0, 0, 1], ['Wither', 'other', 2, 0, 0, 'Heal 5. Weaken enemy', undefined, false, 5]],
    shroud: [['Backstab', 'attack', 2, 4, 0, 'Deals 4. +1 vs no Ward'], ['Shadow Bolt', 'attack', 3, 5, 0, 'Deals 5. Cannot block', undefined, false, 0, 0, 0, 0, false, true], ['Silence', 'attack', 1, 3, 0, 'Deals 3. Silence target'], ['Vengeance', 'attack', 3, 6, 0, 'Deals 6. Gain 2 Aether', undefined, false, 0, 2], ['Blur', 'defense', 3, 0, 2, 'Gain 2 Ward. Ignore next attack', undefined, false, 0, 0, 0, 0, false, false, true], ['Step', 'defense', 2, 0, 3, 'Gain 3 Ward. Gain 1 Aether', undefined, false, 0, 1], ['Evasion', 'defense', 1, 0, 1, 'Gain 1 Ward. Skip enemy action', undefined, false, 0, 0, 0, 0, false, false, true], ['Phantom', 'defense', 2, 0, 4, 'Gain 4 Ward'], ['Dash', 'other', 1, 0, 0, 'Gain 1 Aether. Skip attack', undefined, false, 0, 1, 0, 0, false, false, true], ['Teleport', 'other', 2, 2, 0, 'Deal 2 damage'], ['Hide', 'other', 2, 0, 1, 'Gain 1 Ward. Draw 2', undefined, false, 0, 0, 2], ['Assassinate', 'other', 3, 8, 0, 'Deal 8 unblockable', undefined, false, 0, 0, 0, 0, false, true]]
};
// convert compact entries to objects
const raw = { bastion: [['Slam', 'attack', 2, 3, 0, 'Deals 3. At full health, draw 1', null, 0, 0, 0, 1], ['Earthquake', 'attack', 3, 4, 0, 'Deals 4 to all enemies', null, 1], ['Stone Fist', 'attack', 1, 2, 0, 'Deals 2'], ['Boulder', 'attack', 4, 6, 0, 'Deals 6'], ['Shield Wall', 'defense', 0, 0, 4, 'Gain 4 Ward'], ['Iron Skin', 'defense', 2, 0, 5, 'Gain 5 permanent Ward', null, 0, 0, 0, 0, 1], ['Stand Firm', 'defense', 1, 0, 2, 'Gain 2 Ward'], ['Taunt', 'defense', 2, 0, 3, 'Gain 3 Ward. Stun target', 'stun'], ['Search', 'other', 2, 0, 0, 'Draw 1', null, 0, 0, 0, 1], ['Repair', 'other', 3, 0, 0, 'Heal 6', null, 0, 6], ['Rally', 'other', 1, 0, 0, 'Draw 1. Gain 1 Aether', null, 0, 0, 1, 1], ['Loot', 'other', 2, 0, 0, 'Gain 5 Gold', null, 0, 0, 0, 0, 0, 0, 5]], wither: [['Fireball', 'attack', 2, 3, 0, 'Deals 3. Apply 1 Burn', 'burn'], ['Inferno', 'attack', 3, 4, 0, 'Deals 4. Apply 3 Burn', 'burn'], ['Ember', 'attack', 1, 2, 0, 'Deals 2. Apply 1 Burn', 'burn'], ['Pyre', 'attack', 4, 5, 0, 'Deals 5. All foes Burn', 'burn', 1], ['Scorch Shield', 'defense', 2, 0, 3, 'Gain 3 Ward'], ['Heat Resist', 'defense', 1, 0, 1, 'Gain 1 Ward, 1 Aether', null, 0, 0, 1], ['Smoke', 'defense', 2, 0, 4, 'Gain 4 Ward'], ['Endure', 'defense', 3, 0, 2, 'Gain 2 Ward. Heal 2', null, 0, 2], ['Poison', 'other', 2, 0, 0, 'Apply 1 Poison', 'poison'], ['Burn', 'other', 3, 3, 0, '3 damage + 2 Burn to all', 'burn', 1], ['Slow', 'other', 1, 0, 0, 'Draw 1. Stun target', 'stun', 0, 0, 0, 1], ['Wither', 'other', 2, 0, 0, 'Heal 5. Weaken enemy', null, 0, 5]], shroud: [['Backstab', 'attack', 2, 4, 0, 'Deals 4. +1 vs no Ward'], ['Shadow Bolt', 'attack', 3, 5, 0, 'Deals 5. Cannot block', null, 0, 0, 0, 0, 0, 1], ['Silence', 'attack', 1, 3, 0, 'Deals 3. Silence target'], ['Vengeance', 'attack', 3, 6, 0, 'Deals 6. Gain 2 Aether', null, 0, 0, 2], ['Blur', 'defense', 3, 0, 2, 'Gain 2 Ward. Ignore next attack', null, 0, 0, 0, 0, 0, 0, 0, 1], ['Step', 'defense', 2, 0, 3, 'Gain 3 Ward. Gain 1 Aether', null, 0, 0, 1], ['Evasion', 'defense', 1, 0, 1, 'Gain 1 Ward. Skip enemy action', null, 0, 0, 0, 0, 0, 0, 0, 1], ['Phantom', 'defense', 2, 0, 4, 'Gain 4 Ward'], ['Dash', 'other', 1, 0, 0, 'Gain 1 Aether. Skip attack', null, 0, 0, 1, 0, 0, 0, 0, 1], ['Teleport', 'other', 2, 2, 0, 'Deal 2 damage'], ['Hide', 'other', 2, 0, 1, 'Gain 1 Ward. Draw 2', null, 0, 0, 0, 2], ['Assassinate', 'other', 3, 8, 0, 'Deal 8 unblockable', null, 0, 0, 0, 0, 0, 1]] };
function cards(id) { return raw[id].map((a, i) => ({ id: id + '-' + i, name: a[0], kind: a[1], cost: a[2], attack: a[3] || 0, def: a[4] || 0, ability: a[5], status: a[6] || undefined, aoe: !!a[7], heal: a[8] || 0, energy: a[9] || 0, draw: a[10] || 0, gold: a[13] || 0, permanent: !!a[11], unblockable: !!a[12], skip: !!a[14] })); }
let hero, enemy, floor = 1, room = 1, gameOver = false;
function shuffle(a) { return [...a].sort(() => Math.random() - .5); }
function log(t) { $('#combatLog').textContent = t; }
function makeEnemy() { let boss = room === 4; let hp = boss ? 80 : floor === 1 ? 20 : floor === 2 ? 30 : 40; let dmg = boss ? floor === 1 ? 15 : floor === 2 ? 15 : 12 : floor === 1 ? 3 : floor === 2 ? 5 : 8; let names = boss ? ['THE GATEKEEPER', 'THE CORRUPTED', 'THE VOID LORD'] : floor === 1 ? ['GOBLIN WRAITH', 'SEWER RAT'] : floor === 2 ? ['VAMPIRE KNIGHT', 'ASH OGRE'] : ['VOID WARRIOR', 'CULTIST OF THE DEEP']; enemy = { name: names[Math.floor(Math.random() * names.length)], hp, max: hp, damage: dmg, boss, def: boss && floor === 1 ? 5 : 0, burn: 0, poison: 0, stun: 0, turns: 0 }; }
function render() { if (!hero)
    return; $('#heroName').textContent = hero.name.toUpperCase(); $('#heroStats').innerHTML = `<div class="life">✦ ${hero.hp} / ${hero.max} HP</div><div class="ward">◈ ${hero.def} WARD</div>`; $('#deckCount').textContent = String(hero.draw.length); $('#discardCount').textContent = String(hero.discard.length); $('#runstats').innerHTML = `<span class="stat-gold">✦ ${hero.gold} GOLD</span><span class="stat-floor">FLOOR ${floor} · ${room}/4</span>`; $('#roomLabel').textContent = `FLOOR ${['I', 'II', 'III'][floor - 1]} · ${enemy.boss ? 'VOID LORD' : 'ROOM ' + room}`; $('#enemyName').textContent = enemy.name; $('#enemyHpFill').style.width = (enemy.hp / enemy.max * 100) + '%'; $('#enemyHpText').textContent = `${enemy.hp} / ${enemy.max} HP${enemy.def ? ' · ' + enemy.def + ' ARMOR' : ''}`; $('#enemyIntent').textContent = enemy.stun ? '✧ STUNNED' : `⚔ INTENT: ${enemy.damage} DAMAGE`; $('#enemyStatuses').innerHTML = (enemy.burn ? `<span>🔥 BURN ${enemy.burn}</span>` : '') + (enemy.poison ? `<span class="poison">☣ POISON ${enemy.poison}</span>` : '') + (enemy.stun ? '<span class="stun">✧ STUN</span>' : ''); $('#energyPips').innerHTML = Array.from({ length: hero.maxEnergy }, (_, i) => `<i class="pip ${i < hero.energy ? 'active' : ''}"></i>`).join(''); $('#defenseValue').textContent = String(hero.def); $('#hand').innerHTML = ''; hero.hand.forEach((c, i) => { let el = document.createElement('article'); el.className = `card ${c.kind} ${hero.energy < c.cost ? 'disabled' : ''}`; el.innerHTML = `<svg class="card-art ${hero.classId}" viewBox="0 0 120 48" aria-label="${hero.classId} card art placeholder"><path d="M4 44 L15 5 L60 1 L105 5 L116 44 L96 39 L60 46 L24 39 Z"/><circle cx="60" cy="22" r="11"/><path d="M46 22h28M60 8v28"/></svg><div class="cost">${c.cost}</div><div class="card-type">${c.kind.toUpperCase()}</div><div class="card-name">${c.name}</div><div class="card-ability">${c.ability}</div><div class="card-stats"><span class="atk">${c.attack ? '⚔ ' + c.attack : ''}</span><span class="def">${c.def ? '◈ ' + c.def : ''}</span></div>`; el.onclick = () => play(i); $('#hand').append(el); }); }
function draw(n = 1) { for (let i = 0; i < n; i++) {
    if (!hero.draw.length) {
        hero.draw = shuffle(hero.discard);
        hero.discard = [];
    }
    let c = hero.draw.pop();
    if (c)
        hero.hand.push(c);
} }
function hit(amount, unblockable = false) { let dmg = amount; if (!unblockable && enemy.def) {
    let b = Math.min(enemy.def, dmg);
    enemy.def -= b;
    dmg -= b;
} enemy.hp -= dmg; log(dmg ? `${enemy.name} suffers ${dmg} damage.` : `${enemy.name}'s armor holds.`); }
function play(i) { if (gameOver)
    return; let c = hero.hand[i]; if (hero.energy < c.cost) {
    log('Not enough Aether.');
    return;
} hero.energy -= c.cost; hero.hand.splice(i, 1); hero.discard.push(c); if (c.def) {
    hero.def += c.def;
    if (c.permanent)
        hero.permDef += c.def;
} if (c.heal)
    hero.hp = Math.min(hero.max, hero.hp + c.heal); if (c.energy)
    hero.energy = Math.min(hero.maxEnergy, hero.energy + c.energy); if (c.draw)
    draw(c.draw); if (c.gold)
    hero.gold += c.gold; if (c.skip)
    hero.skip = true; if (c.attack)
    hit(c.attack, c.unblockable); if (c.status) {
    if (c.status === 'burn')
        enemy.burn += c.name === 'Inferno' ? 3 : c.name === 'Burn' ? 2 : 1;
    if (c.status === 'poison')
        enemy.poison += 1;
    if (c.status === 'stun')
        enemy.stun = 1;
} if (c.name === 'Backstab' && enemy.def === 0)
    hit(1); if (c.name === 'Wither')
    enemy.damage = Math.max(1, enemy.damage - 1); if (enemy.hp <= 0) {
    winRoom();
    return;
} render(); }
function endTurn() { if (gameOver)
    return; let dots = enemy.burn + enemy.poison; if (dots) {
    enemy.hp -= dots;
    log(`${enemy.name} takes ${dots} from afflictions.`);
} if (enemy.hp <= 0) {
    winRoom();
    return;
} if (enemy.stun) {
    enemy.stun = 0;
    log(`${enemy.name} is stunned — its blow never comes.`);
}
else if (hero.skip) {
    hero.skip = false;
    log('You vanish before the enemy can strike.');
}
else {
    let dmg = enemy.damage;
    if (enemy.boss && floor === 2 && hero.energy < 3)
        dmg = 20;
    let blocked = Math.min(hero.def, dmg);
    hero.def = Math.max(hero.permDef, hero.def - dmg);
    dmg -= blocked;
    hero.hp -= dmg;
    log(`${enemy.name} attacks: ${dmg} damage after Ward.`);
} enemy.turns++; if (enemy.boss && floor === 3) {
    enemy.hp = Math.min(enemy.max, enemy.hp + 10);
    log('The Void Lord regenerates 10 HP.');
} if (hero.hp <= 0) {
    lose();
    return;
} hero.energy = Math.min(hero.maxEnergy, hero.energy + 1); hero.def = hero.permDef; hero.discard.push(...hero.hand); hero.hand = []; draw(hero.handSize); render(); }
function winRoom() { hero.gold += enemy.boss ? 15 : 5; log(`${enemy.name} falls. +${enemy.boss ? 15 : 5} Gold.`); if (enemy.boss && floor === 3) {
    victory();
    return;
} setTimeout(() => { if (enemy.boss) {
    floor++;
    room = 1;
}
else
    room++; makeEnemy(); hero.def = hero.permDef; draw(hero.handSize); render(); showRoom(); }, 400); }
function showRoom() { log(enemy.boss ? `A Void Lord bars the descent.` : `The darkness gathers its strength.`); }
function start(id) { let base = cards(id); let opts = { bastion: ['The Bastion', 35, 3], wither: ['The Wither', 30, 5], shroud: ['The Shroud', 28, 5] }[id]; hero = { classId: id, name: opts[0], hp: opts[1], max: opts[1], energy: opts[2], maxEnergy: opts[2], def: 0, permDef: 0, gold: 0, handSize: 4, deck: base, draw: shuffle(base), discard: [], hand: [], owned: {}, skip: false }; base.forEach(c => hero.owned[c.id] = 1); floor = 1; room = 1; gameOver = false; makeEnemy(); draw(4); $('#overlay').classList.add('hidden'); render(); showRoom(); }
function cardHtml(c, shop = false) { return `<article class="card ${c.kind} shop-card"><div class="cost">${shop ? cost(c) : c.cost}</div><div class="card-type">${c.kind.toUpperCase()}</div><div class="card-name">${c.name}</div><div class="card-ability">${c.ability}</div><div class="card-stats"><span class="atk">${c.attack ? '⚔ ' + c.attack : ''}</span><span class="def">${c.def ? '◈ ' + c.def : ''}</span></div>${shop ? '<div class="buy-label">CLICK TO ACQUIRE</div>' : ''}</article>`; }
function cost(c) { let n = hero.owned[c.id] || 0; return n >= 3 ? 10 : Math.min(50, c.cost + n); }
function shop() { let list = shuffle(cards(hero.classId)).filter(c => (hero.owned[c.id] || 0) < 5).slice(0, 4); $('#modal').innerHTML = `<button id="close" class="metal modal-close">×</button><div class="modal-title">THE GILDED MERCHANT</div><div class="modal-sub">Gold: <b class="stat-gold">${hero.gold}</b> · Each relic strengthens your deck.</div><div class="shop-grid">${list.map(c => cardHtml(c, true)).join('')}</div>`; $('#overlay').classList.remove('hidden'); document.querySelectorAll('.shop-card').forEach((e, i) => e.addEventListener('click', () => { let c = list[i], p = cost(c); if (hero.gold < p) {
    log('The merchant demands more gold.');
    return;
} hero.gold -= p; hero.owned[c.id] = (hero.owned[c.id] || 0) + 1; hero.deck.push(c); hero.discard.push(c); shop(); render(); })); $('#close').onclick = () => $('#overlay').classList.add('hidden'); }
function menu() { $('#modal').innerHTML = `<div class="modal-title">SHADOWS OF AETHELGARD</div><div class="modal-sub">Choose a covenant. Descend three cursed floors. Defeat every Void Lord.</div><div class="class-grid"><div class="class-choice" data-c="bastion"><div class="class-icon">♜</div><b>THE BASTION</b><small>35 HP · 3 Aether<br>Ward and endurance</small></div><div class="class-choice" data-c="wither"><div class="class-icon">♨</div><b>THE WITHER</b><small>30 HP · 5 Aether<br>Burn and affliction</small></div><div class="class-choice" data-c="shroud"><div class="class-icon">☾</div><b>THE SHROUD</b><small>28 HP · 5 Aether<br>Speed and evasion</small></div></div>`; $('#overlay').classList.remove('hidden'); document.querySelectorAll('.class-choice').forEach(el => el.addEventListener('click', () => start(el.dataset.c))); }
function lose() { gameOver = true; $('#modal').innerHTML = `<div class="modal-title">THE VOID CONSUMES YOU</div><div class="modal-sub">Your light fades on Floor ${floor}. The descent awaits another oath.</div><div class="modal-actions"><button id="again" class="metal">BEGIN ANEW</button></div>`; $('#overlay').classList.remove('hidden'); $('#again').onclick = menu; }
function victory() { gameOver = true; $('#modal').innerHTML = `<div class="modal-title">AETHELGARD RECLAIMED</div><div class="modal-sub">The Void Lord is undone. Your covenant survives the long night.</div><div class="modal-actions"><button id="again" class="metal">NEW DESCENT</button></div>`; $('#overlay').classList.remove('hidden'); $('#again').onclick = menu; }
$('#endTurn').onclick = endTurn;
$('#shopBtn').onclick = shop;
$('#menuBtn').onclick = menu;
document.addEventListener('keydown', e => { if (e.key === 'Enter')
    endTurn(); });
menu();
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-terra","provider":"openrouter","persona":"typescript","sessionId":"4706d4d1-f41e-4604-bb72-b00e6ed93eb3","tokensIn":445734,"tokensOut":12374,"tokensTotal":458108,"turns":23,"toolCalls":24,"failedToolCalls":2,"timestamp":"2026-07-31T18:04:22.250Z"} -->