126 lines
3.9 KiB
TypeScript
126 lines
3.9 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useState } from "react";
|
||
import { CardView } from "./CardView";
|
||
import { COMMANDER, DECK, type Card } from "./cards";
|
||
|
||
const shuffle = <T,>(a: T[]) => [...a].sort(() => Math.random() - 0.5);
|
||
|
||
const LOG_LINES = [
|
||
"Die Umfragewerte sinken. Merz macht eine Pressekonferenz über Bierdeckel.",
|
||
"Merz tappt Sauerland. Niemand ist überrascht.",
|
||
"Das Volk wirkt „Leck Eier“. Es ist sehr effektiv.",
|
||
"Merz versucht zu mulliganen, aber die Koalition sagt nein.",
|
||
"BlackRock ruft an. Merz geht natürlich ran.",
|
||
"Merz zieht eine Karte. Es ist wieder ein Wahlversprechen. Er wirft es ab.",
|
||
];
|
||
|
||
export function Game() {
|
||
const [library, setLibrary] = useState<Card[]>([]);
|
||
const [hand, setHand] = useState<Card[]>([]);
|
||
const [grave, setGrave] = useState<Card[]>([]);
|
||
const [polls, setPolls] = useState(25);
|
||
const [eggs, setEggs] = useState(0);
|
||
const [log, setLog] = useState<string[]>(["Merz, Leck Eier betritt das Schlachtfeld. Aus einem Privatjet."]);
|
||
|
||
useEffect(() => {
|
||
setLibrary(shuffle(DECK));
|
||
try {
|
||
setEggs(Number(localStorage.getItem("eggs") ?? 0));
|
||
} catch {}
|
||
}, []);
|
||
|
||
const say = (line: string) => setLog((l) => [line, ...l].slice(0, 6));
|
||
|
||
function draw() {
|
||
if (library.length === 0) {
|
||
say("Deine Bibliothek ist leer. Wie das Rentenkonzept.");
|
||
return;
|
||
}
|
||
const [top, ...rest] = library;
|
||
setLibrary(rest);
|
||
setHand((h) => [...h, top]);
|
||
say(`Du ziehst „${top.name}“.`);
|
||
}
|
||
|
||
function cast(i: number) {
|
||
const card = hand[i];
|
||
setHand((h) => h.filter((_, j) => j !== i));
|
||
setGrave((g) => [card, ...g]);
|
||
const dmg = card.name === "Leck Eier" ? 10 : Math.ceil(Math.random() * 4);
|
||
setPolls((p) => Math.max(0, p - dmg));
|
||
say(`Du wirkst „${card.name}“. Merz verliert ${dmg}% Zustimmung.`);
|
||
}
|
||
|
||
function mulligan() {
|
||
setLibrary(shuffle(DECK));
|
||
setHand([]);
|
||
setGrave([]);
|
||
setPolls(25);
|
||
say("Mulligan! Neue Hand, gleicher Kanzler.");
|
||
}
|
||
|
||
function lick() {
|
||
const n = eggs + 1;
|
||
setEggs(n);
|
||
try {
|
||
localStorage.setItem("eggs", String(n));
|
||
} catch {}
|
||
say(LOG_LINES[Math.floor(Math.random() * LOG_LINES.length)]);
|
||
}
|
||
|
||
return (
|
||
<>
|
||
<section className="board">
|
||
<div className="commander">
|
||
<div className="label">Commander</div>
|
||
<CardView card={COMMANDER} big />
|
||
<div className="life">
|
||
<span>Umfragewerte</span>
|
||
<strong className={polls === 0 ? "dead" : ""}>{polls}%</strong>
|
||
<div className="meter">
|
||
<div style={{ width: `${(polls / 25) * 100}%` }} />
|
||
</div>
|
||
{polls === 0 && <p className="win">🏆 Du hast gewonnen. Neuwahlen!</p>}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="side">
|
||
<div className="buttons">
|
||
<button onClick={draw}>🃏 Karte ziehen ({library.length})</button>
|
||
<button onClick={mulligan} className="ghost">
|
||
🔄 Mulligan
|
||
</button>
|
||
<button onClick={lick} className="egg">
|
||
🥚 Leck Eier ({eggs})
|
||
</button>
|
||
</div>
|
||
<ol className="log">
|
||
{log.map((l, i) => (
|
||
<li key={i + l}>{l}</li>
|
||
))}
|
||
</ol>
|
||
<div className="grave">
|
||
⚰️ Friedhof: {grave.length ? grave.map((c) => c.name).join(", ") : "leer (wie die Staatskasse)"}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>Deine Hand {hand.length > 0 && <small>— klick eine Karte zum Wirken</small>}</h2>
|
||
{hand.length === 0 ? (
|
||
<p className="empty">Noch keine Karten. Zieh eine, bevor Merz es privatisiert.</p>
|
||
) : (
|
||
<div className="hand">
|
||
{hand.map((c, i) => (
|
||
<button key={i + c.name} className="card-btn" onClick={() => cast(i)}>
|
||
<CardView card={c} />
|
||
</button>
|
||
))}
|
||
</div>
|
||
)}
|
||
</section>
|
||
</>
|
||
);
|
||
}
|