forked from clawdia/der_letzte_auftrag
de213153c8
Charakterbögen, Erpresserbriefe, Hinweiskarten und Wendl-Bogen als Druckvorlagen ergänzt; CLAUDE.md, Briefing, HISTORY und Workspace-Datei aufgenommen sowie Masterdokument, TODO, Tabletop-Kampf und Werwolf-Finale aktualisiert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2017 lines
82 KiB
HTML
2017 lines
82 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light only">
|
||
<meta name="supported-color-schemes" content="light">
|
||
<title>Der letzte Auftrag – JGA Wendl · Im Jahre Schnee</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=MedievalSharp&family=Cinzel+Decorative:wght@700&family=Cinzel:wght@400;600&family=Crimson+Pro:ital,wght@0,400;0,600;1,400&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root {
|
||
--parchment: #f4e8c1;
|
||
--parchment-dark: #e8d5a0;
|
||
--ink: #1a0f00;
|
||
--ink-light: #3d2200;
|
||
--red: #8b1a1a;
|
||
--gold: #c9a227;
|
||
--gold-light: #f0c94a;
|
||
--green: #1a4a1a;
|
||
--blue: #1a2a5a;
|
||
--purple: #4a1a6a;
|
||
--shadow: rgba(0,0,0,0.4);
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
/* Force light mode – verhindert iOS/Koder Dark Mode Override */
|
||
:root { color-scheme: light only; }
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
body { background: #0d0800 !important; }
|
||
.parchment { background: #f4e8c1 !important; }
|
||
.parchment p, .parchment li, .parchment div { color: #3d2200 !important; }
|
||
.quest-card { background: #e8d5a0 !important; }
|
||
.rules-box { background: rgba(138,101,32,0.15) !important; }
|
||
input, textarea { background: #f4e8c1 !important; color: #1a0f00 !important; }
|
||
}
|
||
|
||
body {
|
||
background: #0d0800;
|
||
font-family: 'Crimson Pro', Georgia, serif;
|
||
color: #1a0f00;
|
||
min-height: 100vh;
|
||
background-image:
|
||
radial-gradient(ellipse at 50% 0%, #2a1500 0%, #0d0800 60%);
|
||
}
|
||
|
||
/* ── COVER ─────────────────────────────────── */
|
||
#cover {
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
padding: 2rem;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
#cover::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background:
|
||
radial-gradient(ellipse at 50% 40%, rgba(201,162,39,0.15) 0%, transparent 70%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.stars {
|
||
position: absolute;
|
||
inset: 0;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
}
|
||
.star {
|
||
position: absolute;
|
||
width: 2px; height: 2px;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
animation: twinkle var(--d, 3s) ease-in-out infinite var(--delay, 0s);
|
||
opacity: 0;
|
||
}
|
||
@keyframes twinkle {
|
||
0%,100% { opacity: 0; }
|
||
50% { opacity: var(--bright, 0.8); }
|
||
}
|
||
|
||
.cover-emblem {
|
||
font-size: 5rem;
|
||
margin-bottom: 1rem;
|
||
filter: drop-shadow(0 0 20px rgba(201,162,39,0.6));
|
||
animation: float 4s ease-in-out infinite;
|
||
}
|
||
@keyframes float {
|
||
0%,100% { transform: translateY(0); }
|
||
50% { transform: translateY(-10px); }
|
||
}
|
||
|
||
.cover-subtitle {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.9rem;
|
||
letter-spacing: 0.4em;
|
||
color: var(--gold);
|
||
text-transform: uppercase;
|
||
margin-bottom: 0.8rem;
|
||
}
|
||
|
||
.cover-title {
|
||
font-family: 'Cinzel Decorative', serif;
|
||
font-size: clamp(2rem, 6vw, 4.5rem);
|
||
color: var(--gold-light);
|
||
text-shadow:
|
||
0 0 30px rgba(201,162,39,0.8),
|
||
2px 2px 0 #5a3a00,
|
||
4px 4px 0 #3a2000;
|
||
line-height: 1.15;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.cover-divider {
|
||
width: 300px;
|
||
height: 2px;
|
||
background: linear-gradient(90deg, transparent, var(--gold), transparent);
|
||
margin: 1.5rem auto;
|
||
position: relative;
|
||
}
|
||
.cover-divider::before, .cover-divider::after {
|
||
content: '◆';
|
||
position: absolute;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
color: var(--gold);
|
||
font-size: 1rem;
|
||
}
|
||
.cover-divider::before { left: -10px; }
|
||
.cover-divider::after { right: -10px; }
|
||
|
||
.cover-desc {
|
||
color: #c8a86a;
|
||
font-size: 1.15rem;
|
||
font-style: italic;
|
||
max-width: 500px;
|
||
line-height: 1.7;
|
||
margin-bottom: 2.5rem;
|
||
}
|
||
|
||
.btn-primary {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 1rem;
|
||
letter-spacing: 0.15em;
|
||
color: var(--ink);
|
||
background: linear-gradient(135deg, var(--gold-light), var(--gold));
|
||
border: none;
|
||
padding: 0.9rem 2.5rem;
|
||
cursor: pointer;
|
||
clip-path: polygon(8px 0%, 100% 0%, calc(100% - 8px) 100%, 0% 100%);
|
||
transition: all 0.2s;
|
||
font-weight: 600;
|
||
box-shadow: 0 4px 15px rgba(201,162,39,0.4);
|
||
}
|
||
.btn-primary:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 25px rgba(201,162,39,0.6);
|
||
background: linear-gradient(135deg, #ffe070, var(--gold-light));
|
||
}
|
||
|
||
/* ── MAIN APP ───────────────────────────────── */
|
||
#app { display: none; }
|
||
|
||
.page-wrap {
|
||
max-width: 900px;
|
||
margin: 0 auto;
|
||
padding: 2rem 1rem 4rem;
|
||
background: #f0e4b8;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ── NAV ───────────────────────────────────── */
|
||
.top-nav {
|
||
background: linear-gradient(90deg, #1a0800, #2d1500, #1a0800);
|
||
border-bottom: 2px solid var(--gold);
|
||
padding: 0.8rem 1.5rem;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem;
|
||
}
|
||
.nav-title {
|
||
font-family: 'Cinzel', serif;
|
||
color: var(--gold);
|
||
font-size: 1rem;
|
||
letter-spacing: 0.1em;
|
||
}
|
||
.nav-tabs {
|
||
display: flex;
|
||
gap: 0.3rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
.nav-tab {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.75rem;
|
||
letter-spacing: 0.08em;
|
||
color: #a07840;
|
||
background: transparent;
|
||
border: 1px solid #4a2800;
|
||
padding: 0.35rem 0.8rem;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
.nav-tab:hover, .nav-tab.active {
|
||
color: var(--gold-light);
|
||
border-color: var(--gold);
|
||
background: rgba(201,162,39,0.1);
|
||
}
|
||
|
||
/* ── PARCHMENT SECTIONS ────────────────────── */
|
||
.parchment {
|
||
background: #f4e8c1 !important;
|
||
border: 3px solid #8a6520;
|
||
border-radius: 4px;
|
||
padding: 2rem;
|
||
margin-bottom: 2rem;
|
||
box-shadow:
|
||
0 0 0 1px #c9a227,
|
||
0 8px 30px rgba(0,0,0,0.5),
|
||
inset 0 1px 0 rgba(255,255,255,0.3);
|
||
position: relative;
|
||
}
|
||
|
||
.parchment::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 6px;
|
||
border: 1px solid rgba(138,101,32,0.3);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.section-title {
|
||
font-family: 'Cinzel Decorative', serif;
|
||
font-size: 1.4rem;
|
||
color: var(--red);
|
||
text-align: center;
|
||
margin-bottom: 0.5rem;
|
||
text-shadow: 1px 1px 0 rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.section-subtitle {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.8rem;
|
||
letter-spacing: 0.2em;
|
||
color: #8a6520;
|
||
text-align: center;
|
||
text-transform: uppercase;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.ornament {
|
||
text-align: center;
|
||
color: var(--gold);
|
||
font-size: 1.2rem;
|
||
letter-spacing: 0.5em;
|
||
margin: 1rem 0;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
p {
|
||
font-size: 1.05rem;
|
||
line-height: 1.8;
|
||
margin-bottom: 0.8rem;
|
||
color: var(--ink-light);
|
||
}
|
||
|
||
.flavor-text {
|
||
font-style: italic;
|
||
background: rgba(138,101,32,0.08);
|
||
border-left: 3px solid var(--gold);
|
||
padding: 0.8rem 1rem;
|
||
margin: 1rem 0;
|
||
border-radius: 0 4px 4px 0;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
/* ── QUEST CARDS ───────────────────────────── */
|
||
.quest-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||
gap: 1.2rem;
|
||
margin-top: 1.5rem;
|
||
}
|
||
|
||
.quest-card {
|
||
background: #e8d5a0;
|
||
border: 2px solid #8a6520;
|
||
border-radius: 4px;
|
||
padding: 1.2rem;
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: transform 0.2s, box-shadow 0.2s;
|
||
cursor: pointer;
|
||
}
|
||
.quest-card:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 8px 20px rgba(0,0,0,0.3);
|
||
}
|
||
.quest-card.done {
|
||
opacity: 0.6;
|
||
filter: saturate(0.4);
|
||
}
|
||
.quest-card.done::after {
|
||
content: '✓ ERLEDIGT';
|
||
position: absolute;
|
||
top: 50%; left: 50%;
|
||
transform: translate(-50%,-50%) rotate(-15deg);
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 1.5rem;
|
||
color: var(--green);
|
||
border: 3px solid var(--green);
|
||
padding: 0.3rem 0.8rem;
|
||
opacity: 0.8;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.quest-badge {
|
||
display: inline-block;
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.65rem;
|
||
letter-spacing: 0.1em;
|
||
padding: 0.2rem 0.6rem;
|
||
border-radius: 2px;
|
||
margin-bottom: 0.5rem;
|
||
font-weight: 600;
|
||
}
|
||
.badge-easy { background: #2a5a1a; color: #a8ff80; }
|
||
.badge-medium { background: #5a4a00; color: #ffd060; }
|
||
.badge-hard { background: #5a1a00; color: #ff8060; }
|
||
.badge-tabletop { background: #2a1a5a; color: #a080ff; }
|
||
|
||
.quest-name {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 1rem;
|
||
color: var(--red);
|
||
margin-bottom: 0.4rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.quest-holder {
|
||
font-size: 0.85rem;
|
||
color: #5a3a10;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
.quest-holder strong { color: var(--ink); }
|
||
|
||
.quest-desc {
|
||
font-size: 0.9rem;
|
||
line-height: 1.5;
|
||
color: var(--ink-light);
|
||
margin-bottom: 0.8rem;
|
||
}
|
||
|
||
.quest-rewards {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 0.8rem;
|
||
border-top: 1px solid #c9a22760;
|
||
padding-top: 0.6rem;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.reward-success {
|
||
color: var(--green);
|
||
font-weight: 600;
|
||
}
|
||
.reward-fail {
|
||
color: var(--red);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.quest-expand {
|
||
display: none;
|
||
margin-top: 0.8rem;
|
||
padding-top: 0.8rem;
|
||
border-top: 1px dashed #c9a22780;
|
||
font-size: 0.9rem;
|
||
line-height: 1.6;
|
||
color: var(--ink);
|
||
}
|
||
.quest-card.expanded .quest-expand { display: block; }
|
||
|
||
.expand-hint {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.7rem;
|
||
color: #8a6520;
|
||
text-align: right;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
/* ── INVENTORY TRACKER ─────────────────────── */
|
||
.inventory-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||
gap: 1rem;
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
.item-slot {
|
||
border: 2px dashed #8a6520;
|
||
border-radius: 4px;
|
||
padding: 1rem;
|
||
text-align: center;
|
||
min-height: 100px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 0.4rem;
|
||
transition: all 0.3s;
|
||
cursor: pointer;
|
||
position: relative;
|
||
background: rgba(138,101,32,0.05);
|
||
}
|
||
.item-slot.collected {
|
||
border: 2px solid var(--gold);
|
||
background: rgba(201,162,39,0.1);
|
||
box-shadow: 0 0 10px rgba(201,162,39,0.3);
|
||
}
|
||
|
||
.item-emoji { font-size: 2.5rem; }
|
||
.item-name {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.75rem;
|
||
color: var(--ink-light);
|
||
line-height: 1.3;
|
||
}
|
||
.item-slot.collected .item-name { color: var(--gold); }
|
||
.item-check {
|
||
position: absolute;
|
||
top: 5px; right: 8px;
|
||
font-size: 1rem;
|
||
color: var(--gold-light);
|
||
opacity: 0;
|
||
transition: opacity 0.3s;
|
||
}
|
||
.item-slot.collected .item-check { opacity: 1; }
|
||
|
||
/* ── DRINKS TRACKER ────────────────────────── */
|
||
.drink-tracker {
|
||
text-align: center;
|
||
padding: 1rem;
|
||
}
|
||
.drink-count {
|
||
font-family: 'Cinzel Decorative', serif;
|
||
font-size: 4rem;
|
||
color: var(--red);
|
||
text-shadow: 2px 2px 0 rgba(0,0,0,0.2);
|
||
line-height: 1;
|
||
}
|
||
.drink-label {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.8rem;
|
||
letter-spacing: 0.3em;
|
||
color: #8a6520;
|
||
margin-top: 0.3rem;
|
||
text-transform: uppercase;
|
||
}
|
||
.drink-btns {
|
||
display: flex;
|
||
gap: 1rem;
|
||
justify-content: center;
|
||
margin-top: 1.2rem;
|
||
}
|
||
.btn-drink {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 1.1rem;
|
||
border: 2px solid;
|
||
padding: 0.5rem 1.5rem;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
background: transparent;
|
||
border-radius: 3px;
|
||
}
|
||
.btn-drink.add { border-color: var(--red); color: var(--red); }
|
||
.btn-drink.add:hover { background: var(--red); color: #fff; }
|
||
.btn-drink.sub { border-color: #8a6520; color: #8a6520; }
|
||
.btn-drink.sub:hover { background: #8a6520; color: #fff; }
|
||
|
||
/* ── TABLETOP SECTION ──────────────────────── */
|
||
.encounter-box {
|
||
background: #0d0500;
|
||
border: 2px solid var(--gold);
|
||
border-radius: 4px;
|
||
padding: 1.5rem;
|
||
margin: 1rem 0;
|
||
color: #e8d5a0;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.encounter-box::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background: radial-gradient(ellipse at 50% 0%, rgba(201,162,39,0.08), transparent 60%);
|
||
pointer-events: none;
|
||
}
|
||
.encounter-title {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 1.1rem;
|
||
color: var(--gold-light);
|
||
margin-bottom: 0.8rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
.encounter-box p { color: #c8a86a; }
|
||
.encounter-box .flavor-text {
|
||
border-left-color: var(--gold);
|
||
background: rgba(201,162,39,0.05);
|
||
color: #e8d5a0;
|
||
}
|
||
/* Listen in der dunklen Encounter-Box hell einfärben (sonst dunkel-auf-dunkel) */
|
||
.encounter-box .rules-box { background: rgba(201,162,39,0.05); border-color: #4a2800; }
|
||
.encounter-box .rules-list li { color: #e8d5a0; }
|
||
.encounter-box .rules-list li strong { color: var(--gold-light); }
|
||
.encounter-box .rules-list li::before { color: var(--gold-light); }
|
||
|
||
.stat-block {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 0.5rem;
|
||
margin: 1rem 0;
|
||
background: #1a0800;
|
||
border: 1px solid #4a2800;
|
||
padding: 0.8rem;
|
||
border-radius: 4px;
|
||
}
|
||
.stat-item {
|
||
text-align: center;
|
||
font-family: 'Cinzel', serif;
|
||
}
|
||
.stat-val {
|
||
font-size: 1.4rem;
|
||
color: var(--gold-light);
|
||
}
|
||
.stat-name {
|
||
font-size: 0.6rem;
|
||
letter-spacing: 0.15em;
|
||
color: #8a6520;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
/* ── DICE ROLLER ───────────────────────────── */
|
||
.dice-roller {
|
||
background: #1a0800;
|
||
border: 1px solid #4a2800;
|
||
border-radius: 4px;
|
||
padding: 1rem;
|
||
margin-top: 1rem;
|
||
}
|
||
.dice-roller-title {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.8rem;
|
||
letter-spacing: 0.2em;
|
||
color: var(--gold);
|
||
text-transform: uppercase;
|
||
margin-bottom: 0.8rem;
|
||
}
|
||
.dice-btns {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem;
|
||
margin-bottom: 0.8rem;
|
||
}
|
||
.dice-btn {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.85rem;
|
||
color: #c8a86a;
|
||
background: #2a1000;
|
||
border: 1px solid #4a2800;
|
||
padding: 0.4rem 0.8rem;
|
||
cursor: pointer;
|
||
border-radius: 3px;
|
||
transition: all 0.2s;
|
||
}
|
||
.dice-btn:hover {
|
||
border-color: var(--gold);
|
||
color: var(--gold-light);
|
||
background: #3a1800;
|
||
}
|
||
.dice-result {
|
||
font-family: 'Cinzel Decorative', serif;
|
||
font-size: 2.5rem;
|
||
color: var(--gold-light);
|
||
text-align: center;
|
||
min-height: 3rem;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-shadow: 0 0 15px rgba(201,162,39,0.5);
|
||
transition: all 0.3s;
|
||
}
|
||
.dice-result.rolling {
|
||
animation: diceRoll 0.4s ease-out;
|
||
}
|
||
@keyframes diceRoll {
|
||
0%,20%,40%,60%,80% { transform: rotate(-5deg) scale(1.1); }
|
||
10%,30%,50%,70%,90% { transform: rotate(5deg) scale(1.1); }
|
||
100% { transform: none; }
|
||
}
|
||
.dice-log {
|
||
font-size: 0.8rem;
|
||
color: #5a3a10;
|
||
min-height: 1.2rem;
|
||
text-align: center;
|
||
font-style: italic;
|
||
}
|
||
|
||
/* ── TIMELINE ──────────────────────────────── */
|
||
.timeline {
|
||
position: relative;
|
||
padding-left: 2rem;
|
||
margin-top: 1rem;
|
||
}
|
||
.timeline::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0.5rem;
|
||
top: 0; bottom: 0;
|
||
width: 2px;
|
||
background: linear-gradient(180deg, var(--gold), var(--gold) 80%, transparent);
|
||
}
|
||
.timeline-item {
|
||
position: relative;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
.timeline-item::before {
|
||
content: '◆';
|
||
position: absolute;
|
||
left: -1.7rem;
|
||
color: var(--gold);
|
||
font-size: 0.8rem;
|
||
top: 0.1rem;
|
||
}
|
||
.timeline-time {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.7rem;
|
||
letter-spacing: 0.15em;
|
||
color: var(--gold);
|
||
text-transform: uppercase;
|
||
margin-bottom: 0.2rem;
|
||
}
|
||
.timeline-event {
|
||
font-size: 1rem;
|
||
color: var(--ink-light);
|
||
font-weight: 600;
|
||
}
|
||
.timeline-note {
|
||
font-size: 0.9rem;
|
||
color: #5a3a10;
|
||
font-style: italic;
|
||
}
|
||
|
||
/* ── RULES BOX ─────────────────────────────── */
|
||
.rules-box {
|
||
background: rgba(138,101,32,0.08);
|
||
border: 1px solid #c9a22740;
|
||
border-radius: 4px;
|
||
padding: 1rem 1.2rem;
|
||
margin: 0.8rem 0;
|
||
}
|
||
.rules-title {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.8rem;
|
||
letter-spacing: 0.15em;
|
||
color: var(--red);
|
||
text-transform: uppercase;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
.rules-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
}
|
||
.rules-list li {
|
||
font-size: 0.95rem;
|
||
padding: 0.25rem 0 0.25rem 1.2rem;
|
||
position: relative;
|
||
color: var(--ink-light);
|
||
}
|
||
.rules-list li::before {
|
||
content: '⚔';
|
||
position: absolute;
|
||
left: 0;
|
||
font-size: 0.75rem;
|
||
color: var(--gold);
|
||
top: 0.35rem;
|
||
}
|
||
|
||
/* ── TABS ──────────────────────────────────── */
|
||
.tab-content { display: none; }
|
||
.tab-content.active { display: block; }
|
||
|
||
/* ── SPIELER ───────────────────────────────── */
|
||
.spieler-add-row {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
flex-wrap: wrap;
|
||
margin-bottom: 1.2rem;
|
||
}
|
||
.spieler-input {
|
||
font-family: 'Crimson Pro', serif;
|
||
font-size: 1rem;
|
||
background: #fdf5dc;
|
||
border: 1px solid #8a6520;
|
||
border-radius: 3px;
|
||
padding: 0.5rem 0.8rem;
|
||
color: #1a0f00;
|
||
outline: none;
|
||
flex: 1;
|
||
min-width: 120px;
|
||
}
|
||
.spieler-input:focus { border-color: #c9a227; box-shadow: 0 0 0 2px rgba(201,162,39,0.2); }
|
||
.spieler-select {
|
||
font-family: 'Crimson Pro', serif;
|
||
font-size: 0.95rem;
|
||
background: #fdf5dc;
|
||
border: 1px solid #8a6520;
|
||
border-radius: 3px;
|
||
padding: 0.5rem 0.6rem;
|
||
color: #1a0f00;
|
||
cursor: pointer;
|
||
outline: none;
|
||
}
|
||
.btn-add {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.8rem;
|
||
letter-spacing: 0.1em;
|
||
background: #2a5a1a;
|
||
color: #a8ff80;
|
||
border: none;
|
||
padding: 0.5rem 1rem;
|
||
border-radius: 3px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
white-space: nowrap;
|
||
}
|
||
.btn-add:hover { background: #1a4a0a; }
|
||
|
||
.spieler-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.6rem;
|
||
}
|
||
.spieler-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.7rem;
|
||
background: #fdf5dc;
|
||
border: 1px solid #c9a22760;
|
||
border-radius: 4px;
|
||
padding: 0.7rem 1rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
.spieler-avatar {
|
||
width: 38px; height: 38px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #8a6520, #c9a227);
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 1.1rem;
|
||
flex-shrink: 0;
|
||
color: #fff;
|
||
font-weight: bold;
|
||
font-family: 'Cinzel', serif;
|
||
}
|
||
.spieler-info { flex: 1; min-width: 100px; }
|
||
.spieler-name-display {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.95rem;
|
||
color: #1a0f00;
|
||
font-weight: 600;
|
||
}
|
||
.spieler-rolle-display {
|
||
font-size: 0.8rem;
|
||
color: #5a3a10;
|
||
margin-top: 0.1rem;
|
||
font-style: italic;
|
||
}
|
||
.spieler-rolle-badge {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.65rem;
|
||
letter-spacing: 0.08em;
|
||
padding: 0.15rem 0.5rem;
|
||
border-radius: 2px;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
}
|
||
.rolle-held { background: #5a3a00; color: #ffd060; }
|
||
.rolle-hüter { background: #2a1a5a; color: #a080ff; }
|
||
.rolle-ratsmitglied { background: #4a1a1a; color: #ff9060; }
|
||
.rolle-einsteiger { background: #1a4a1a; color: #a8ff80; }
|
||
.rolle-spielleiter { background: #1a1a4a; color: #80c0ff; }
|
||
.rolle-gast { background: #3a3a1a; color: #e0e080; }
|
||
|
||
.spieler-quest-sel {
|
||
font-family: 'Crimson Pro', serif;
|
||
font-size: 0.8rem;
|
||
background: #f4e8c1;
|
||
border: 1px solid #8a652060;
|
||
border-radius: 3px;
|
||
padding: 0.25rem 0.4rem;
|
||
color: #3d2200;
|
||
cursor: pointer;
|
||
max-width: 160px;
|
||
}
|
||
.btn-del {
|
||
background: transparent;
|
||
border: 1px solid #8b1a1a60;
|
||
color: #8b1a1a;
|
||
border-radius: 3px;
|
||
padding: 0.25rem 0.6rem;
|
||
cursor: pointer;
|
||
font-size: 0.85rem;
|
||
transition: all 0.2s;
|
||
flex-shrink: 0;
|
||
}
|
||
.btn-del:hover { background: #8b1a1a; color: #fff; }
|
||
|
||
.spieler-empty {
|
||
text-align: center;
|
||
font-style: italic;
|
||
color: #8a6520;
|
||
padding: 2rem;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.spieler-summary {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
flex-wrap: wrap;
|
||
margin-top: 1rem;
|
||
padding-top: 1rem;
|
||
border-top: 1px solid #c9a22740;
|
||
}
|
||
.summary-chip {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.7rem;
|
||
letter-spacing: 0.08em;
|
||
padding: 0.2rem 0.6rem;
|
||
border-radius: 20px;
|
||
border: 1px solid #c9a22760;
|
||
color: #5a3a10;
|
||
}
|
||
|
||
/* ── PRINT HINT ────────────────────────────── */
|
||
.print-hint {
|
||
text-align: center;
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.75rem;
|
||
letter-spacing: 0.15em;
|
||
color: #5a3a10;
|
||
padding: 1rem;
|
||
}
|
||
|
||
/* ── MODE-BUTTON & MODUS-UMSCHALTUNG ───────── */
|
||
.nav-mode-btn {
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.08em;
|
||
color: #c8a86a;
|
||
background: rgba(201,162,39,0.08);
|
||
border: 1px solid var(--gold);
|
||
padding: 0.35rem 0.9rem;
|
||
cursor: pointer;
|
||
border-radius: 3px;
|
||
transition: all 0.2s;
|
||
white-space: nowrap;
|
||
}
|
||
.nav-mode-btn:hover {
|
||
background: rgba(201,162,39,0.2);
|
||
color: var(--gold-light);
|
||
}
|
||
body.gm-mode .nav-mode-btn {
|
||
border-color: #80c0ff;
|
||
color: #80c0ff;
|
||
background: rgba(128,192,255,0.08);
|
||
}
|
||
body.gm-mode .nav-mode-btn:hover {
|
||
background: rgba(128,192,255,0.2);
|
||
color: #b0d8ff;
|
||
}
|
||
|
||
/* Tab-Sichtbarkeit je nach Modus */
|
||
body:not(.gm-mode) .nav-tab.gm-tab { display: none; }
|
||
body.gm-mode .nav-tab.player-tab { display: none; }
|
||
|
||
/* Spoiler-Inhalte nur im Spielleiter-Modus zeigen */
|
||
body:not(.gm-mode) .gm-only { display: none !important; }
|
||
|
||
/* Dezenter GM-Hinweis-Streifen */
|
||
.gm-banner {
|
||
display: none;
|
||
background: linear-gradient(90deg, #0a1428, #16243f, #0a1428);
|
||
border-bottom: 1px solid #80c0ff;
|
||
color: #80c0ff;
|
||
font-family: 'Cinzel', serif;
|
||
font-size: 0.7rem;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
text-align: center;
|
||
padding: 0.35rem;
|
||
}
|
||
body.gm-mode .gm-banner { display: block; }
|
||
|
||
/* ── RESPONSIVE ────────────────────────────── */
|
||
@media (max-width: 600px) {
|
||
.cover-title { font-size: 1.8rem; }
|
||
.parchment { padding: 1.2rem; }
|
||
.quest-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ══════════════════ COVER ══════════════════ -->
|
||
<div id="cover">
|
||
<div class="stars" id="stars"></div>
|
||
<div class="cover-emblem">⚔️</div>
|
||
<div class="cover-subtitle">Im Jahre Schnee · Ein JGA für Wendl</div>
|
||
<div class="cover-title">Der letzte<br>Auftrag</div>
|
||
<div class="cover-divider"></div>
|
||
<div class="cover-desc">
|
||
Iris wurde vom Erpresser entführt. Wendl muss Quests bestehen,<br>
|
||
Stärke sammeln, im Tabletop-Kampf siegen und im Werwolf-Finale<br>
|
||
den Komplizen entlarven – um seine Braut zu befreien.
|
||
</div>
|
||
<button class="btn-primary" onclick="startApp()">📜 Abenteuer beginnen</button>
|
||
</div>
|
||
|
||
<!-- ══════════════════ APP ══════════════════ -->
|
||
<div id="app">
|
||
<nav class="top-nav">
|
||
<span class="nav-title">⚔ Der letzte Auftrag</span>
|
||
<div class="nav-tabs">
|
||
<!-- Spieler-Tabs (für alle) -->
|
||
<button class="nav-tab player-tab active" data-tab="willkommen" onclick="showTab('willkommen', this)">Willkommen</button>
|
||
<button class="nav-tab player-tab" data-tab="ablauf-spieler" onclick="showTab('ablauf-spieler', this)">Ablauf</button>
|
||
<!-- Spielleiter-Tabs (passwortgeschützt) -->
|
||
<button class="nav-tab gm-tab" data-tab="intro" onclick="showTab('intro', this)">Intro</button>
|
||
<button class="nav-tab" data-tab="quests" onclick="showTab('quests', this)">Quests</button>
|
||
<button class="nav-tab gm-tab" data-tab="tabletop" onclick="showTab('tabletop', this)">Tabletop</button>
|
||
<button class="nav-tab gm-tab" data-tab="werwolf" onclick="showTab('werwolf', this)">Werwolf</button>
|
||
<button class="nav-tab" data-tab="tracker" onclick="showTab('tracker', this)">Tracker</button>
|
||
<button class="nav-tab gm-tab" data-tab="spieler" onclick="showTab('spieler', this)">Spieler</button>
|
||
<button class="nav-tab gm-tab" data-tab="ablauf" onclick="showTab('ablauf', this)">Ablauf (SL)</button>
|
||
</div>
|
||
<button class="nav-mode-btn" id="modeBtn" onclick="toggleMode()">🔒 Spielleiter</button>
|
||
</nav>
|
||
<div class="gm-banner">🔮 Spielleiter-Modus · Spoiler sichtbar</div>
|
||
|
||
<div class="page-wrap">
|
||
|
||
<!-- ═══ WILLKOMMEN (Spieler) ═══ -->
|
||
<div id="tab-willkommen" class="tab-content active">
|
||
<div class="parchment">
|
||
<div class="section-title">Willkommen in der Taverne</div>
|
||
<div class="section-subtitle">„Im Jahre Schnee" · Ein JGA für Wendl</div>
|
||
<div class="ornament">✦ ✦ ✦</div>
|
||
|
||
<div class="flavor-text">
|
||
„Tretet ein, Freunde! Heute feiern wir Wendl – doch dunkle Kunde erreicht uns: Seine Iris ist verschwunden. Nur gemeinsam, mit Mut, List und manchem Krug, holen wir sie zurück."
|
||
</div>
|
||
|
||
<p>Heute verwandelt sich alles in eine mittelalterliche Taverne. <strong>Wendl</strong> ist der Held des Tages – und ihr alle seid das Tavernenvolk, das ihm zur Seite steht.</p>
|
||
|
||
<p><strong>Worum geht's?</strong> Iris wurde entführt. Über den Tag verteilt warten Prüfungen, Spiele und Aufgaben auf Wendl. Helft mit, feuert an, spielt mit – und gemeinsam bringen wir den Tag zu einem würdigen Finale.</p>
|
||
|
||
<div class="rules-box">
|
||
<div class="rules-title">Grundinfos für alle</div>
|
||
<ul class="rules-list">
|
||
<li><strong>Rolle:</strong> Wir bleiben den ganzen Tag „in der Taverne" – theatralisch, albern, mit vollem Einsatz.</li>
|
||
<li><strong>Mitmachen:</strong> Jeder hat seinen Moment. Wer eine Aufgabe hütet, wird vorher kurz gebrieft.</li>
|
||
<li><strong>Spaßwährung:</strong> Gold (Dukaten) gibt's für gute Stimmung – auszugeben beim Wirt.</li>
|
||
<li><strong>Wichtigste Regel:</strong> Stimmung vor Regeln. Alles für Wendl!</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="ornament">✦ ✦ ✦</div>
|
||
<p style="text-align:center; font-style:italic; color:#5a3a10;">Den groben Tagesablauf findest du im Reiter „Ablauf".</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ ABLAUF (Spieler) ═══ -->
|
||
<div id="tab-ablauf-spieler" class="tab-content">
|
||
<div class="parchment">
|
||
<div class="section-title">Ablauf des Tages</div>
|
||
<div class="section-subtitle">Grober Fahrplan – der Takt bleibt locker</div>
|
||
|
||
<div class="timeline">
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">~12:00</div>
|
||
<div class="timeline-event">Aufbau & Ankommen</div>
|
||
<div class="timeline-note">Taverne herrichten, einrichten, erstes Anstoßen.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">~14:00</div>
|
||
<div class="timeline-event">Ankunft Wendl & Auftakt 🕊️</div>
|
||
<div class="timeline-note">Der Wirt begrüßt – dann erreicht die böse Nachricht die Taverne. Das Abenteuer beginnt.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">Nachmittag</div>
|
||
<div class="timeline-event">Prüfungen & Spiele</div>
|
||
<div class="timeline-note">Wendl stellt sich Aufgaben rund um die Taverne. Alle helfen mit und feuern an.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">~21:00</div>
|
||
<div class="timeline-event">Grillpause 🔥</div>
|
||
<div class="timeline-note">Essen, durchatmen, Kräfte sammeln.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">Abend</div>
|
||
<div class="timeline-event">Die große Herausforderung ⚔</div>
|
||
<div class="timeline-note">Es wird ernst – Wendl braucht die ganze Taverne hinter sich.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">Mitternacht</div>
|
||
<div class="timeline-event">Das große Finale 🏆</div>
|
||
<div class="timeline-note">Wie der Tag endet? Lasst euch überraschen.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ornament">✦ ✦ ✦</div>
|
||
<p style="text-align:center; font-style:italic; color:#5a3a10;">Zeiten sind Richtwerte – die Stimmung gibt den Takt vor.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ INTRO ═══ -->
|
||
<div id="tab-intro" class="tab-content">
|
||
<div class="parchment">
|
||
<div class="section-title">Das Abenteuer</div>
|
||
<div class="section-subtitle">GM-Überblick · „Im Jahre Schnee"</div>
|
||
<div class="ornament">✦ ✦ ✦</div>
|
||
|
||
<div class="flavor-text">
|
||
„Höre mich an, Wendl. Deine Iris wurde vom Erpresser entführt. Beweise deine Stärke in den Prüfungen der Taverne, schlage den Schurken im Kampf – und entlarve den Komplizen, der unter uns weilt. Erst dann kehrt Iris zu dir zurück."
|
||
</div>
|
||
|
||
<p><strong>Der Bogen des Tages:</strong> Iris ist entführt → Wendl besteht Quests und sammelt <strong>6 Würfel</strong> → ab 6 Würfeln öffnet sich der <strong>Tabletop-Kampf</strong> gegen den Erpresser → sein Sterbemonolog verrät, dass ein Verräter unter ihnen ist (<em>ohne Namen!</em>) → das <strong>Werwolf-Finale</strong> entlarvt den <strong>Höschenhändler</strong> → Iris wird befreit.</p>
|
||
|
||
<div class="rules-box">
|
||
<div class="rules-title">Die vier Ressourcen</div>
|
||
<ul class="rules-list">
|
||
<li><strong>🎲 Würfel</strong> (Mut/Stärke) – 1 pro abgeschlossenem Quest. Ab <strong>6 Würfeln</strong> ist der Kampf offen. Einziger Pflichtpfad.</li>
|
||
<li><strong>🃏 Hinweis-Karten</strong> – Futter fürs Werwolf-Finale, führen schrittweise zum Höschenhändler.</li>
|
||
<li><strong>👕 Kleidungsstücke</strong> (Iris' Sachen) – roter Faden & Finale-Reveal.</li>
|
||
<li><strong>🪙 Gold</strong> (Dukaten) – Spaßwährung, gatet nichts Kritisches. Sink: Höschenhändler (10 Gold → „Iris-Bluse"), Barkeeper, Lukis Esel.</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="rules-box">
|
||
<div class="rules-title">Der Taktgeber – damit Wendl nie ratlos rumsteht</div>
|
||
<ul class="rules-list">
|
||
<li><strong>Wahrsager (Sebastian):</strong> Zur vollen Stunde genau ein Tipp, was als Nächstes dran ist. Im Finale = Spielleiter.</li>
|
||
<li><strong>Barkeeper (Alex):</strong> Dauer-Anlaufstelle für Gerüchte, Getränke und immer einen Faden.</li>
|
||
<li><strong>Failsafe:</strong> Steht Wendl orientierungslos rum, schiebt Wahrsager oder Barkeeper aktiv an.</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="ornament">✦ ✦ ✦</div>
|
||
<p style="text-align:center; font-style:italic; color: #5a3a10;">Die „Iris-Bluse" vom Höschenhändler ist eine <strong>Fälschung</strong> –<br>genau das überführt den Komplizen am Ende.</p>
|
||
</div>
|
||
|
||
<div class="parchment">
|
||
<div class="section-title">Die 6 Würfel der Stärke</div>
|
||
<div class="section-subtitle">Fortschritt zur Kampf-Freischaltung</div>
|
||
<p>Jeder abgeschlossene Quest gibt Wendl <strong>einen Würfel</strong>. Sind alle sechs gesammelt, öffnet sich der Tabletop-Kampf gegen den Erpresser. Tippe an, um den Fortschritt zu markieren:</p>
|
||
<div class="inventory-grid">
|
||
<div class="item-slot" id="inv-0" onclick="toggleItem(0)">
|
||
<span class="item-emoji">🎲</span>
|
||
<span class="item-name">1. Würfel</span>
|
||
<span class="item-check">✦</span>
|
||
</div>
|
||
<div class="item-slot" id="inv-1" onclick="toggleItem(1)">
|
||
<span class="item-emoji">🎲</span>
|
||
<span class="item-name">2. Würfel</span>
|
||
<span class="item-check">✦</span>
|
||
</div>
|
||
<div class="item-slot" id="inv-2" onclick="toggleItem(2)">
|
||
<span class="item-emoji">🎲</span>
|
||
<span class="item-name">3. Würfel</span>
|
||
<span class="item-check">✦</span>
|
||
</div>
|
||
<div class="item-slot" id="inv-3" onclick="toggleItem(3)">
|
||
<span class="item-emoji">🎲</span>
|
||
<span class="item-name">4. Würfel</span>
|
||
<span class="item-check">✦</span>
|
||
</div>
|
||
<div class="item-slot" id="inv-4" onclick="toggleItem(4)">
|
||
<span class="item-emoji">🎲</span>
|
||
<span class="item-name">5. Würfel</span>
|
||
<span class="item-check">✦</span>
|
||
</div>
|
||
<div class="item-slot" id="inv-5" onclick="toggleItem(5)">
|
||
<span class="item-emoji">🎲</span>
|
||
<span class="item-name">6. Würfel ⚔ Freischaltung</span>
|
||
<span class="item-check">✦</span>
|
||
</div>
|
||
</div>
|
||
<p style="text-align:center; font-style:italic; color:#5a3a10; margin-top:1rem;">Hinweis-Karten, Kleidungsstücke & Gold trägst du am besten auf den Notizen im Tracker mit.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ QUESTS ═══ -->
|
||
<div id="tab-quests" class="tab-content">
|
||
<div class="parchment">
|
||
<div class="section-title">Die Quests</div>
|
||
<div class="section-subtitle">Die Prüfungen für Wendl</div>
|
||
|
||
<p class="gm-only" style="text-align:center;font-style:italic;color:#5a3a10;margin-bottom:0.5rem;">Im Spielleiter-Modus mit allen Details — Spieler sehen nur die Quest-Namen & den Fortschritt.</p>
|
||
|
||
<div class="rules-box gm-only">
|
||
<div class="rules-title">Farbcode</div>
|
||
<ul class="rules-list" style="columns:2; gap:1rem;">
|
||
<li><span class="quest-badge badge-easy">AKT 1</span> Leichte Prüfungen</li>
|
||
<li><span class="quest-badge badge-medium">AKT 2</span> Fleischigere Quests</li>
|
||
<li><span class="quest-badge badge-hard">KERN</span> Pflicht für die Story</li>
|
||
<li><span class="quest-badge badge-tabletop">HINWEIS</span> Futter fürs Finale</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="quest-grid" id="questGrid"></div>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ TABLETOP ═══ -->
|
||
<div id="tab-tabletop" class="tab-content">
|
||
<div class="parchment">
|
||
<div class="section-title">Tabletop-Kampf: Wendl vs. Der Erpresser</div>
|
||
<div class="section-subtitle">Freischaltung ab 6 Würfeln · W6-System · ~35 Min</div>
|
||
|
||
<div class="flavor-text">
|
||
Wendl hat seine 6 Würfel gesammelt – der Erpresser kann sich nicht länger verstecken. Kurz, laut, theatralisch: ein punchiger Kampf, kein zähes Ringen. Der GM spricht für den Erpresser und hamt es richtig auf.
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">👹 Der Erpresser – „Der Namenlose"</div>
|
||
<div class="flavor-text">
|
||
Kein Spieler. Der GM spricht für ihn: monologisiert fies, reagiert auf Treffer, stirbt theatralisch.
|
||
</div>
|
||
<div class="stat-block">
|
||
<div class="stat-item">
|
||
<div class="stat-val">20</div>
|
||
<div class="stat-name">Lebenspunkte</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-val">W6</div>
|
||
<div class="stat-name">4+ = 1 Treffer</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-val">2</div>
|
||
<div class="stat-name">Phasen</div>
|
||
</div>
|
||
</div>
|
||
<p style="font-size:0.9rem;"><strong>Widerstand:</strong> Treffer unter 4 prallen ab. <strong>Phasen-Schwelle:</strong> Sinkt er auf ≤10 LP, dreht er durch – Phase-2-Fähigkeiten C & D werden frei: <em>„Er streckt sich zu voller Größe auf – jetzt kämpft er wie ein verwundetes Tier!"</em></p>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">📜 Boss-Fähigkeiten</div>
|
||
<div class="rules-box" style="background: rgba(201,162,39,0.05); border-color: #4a2800;">
|
||
<ul class="rules-list">
|
||
<li><strong>A · Erpresserschreiben</strong> (1× pro Phase): GM liest einen Brief vor; ein genannter Spieler setzt aus – oder trinkt und bleibt dabei.</li>
|
||
<li><strong>B · Geiseldrohung</strong> (1×, Phase 1): zeigt Iris' Kleidungsstück. Wendl würfelt W6, 4+; misslingt → ganze Gruppe trifft diese Runde ins Leere.</li>
|
||
<li><strong>C · Blendgranate</strong> (1×, Phase 2): Mehl-Tüte auf den Tisch. Alle Augen zu, bis 3 zählen – GM würfelt verdeckt, bei 5+ heilt der Boss 3 LP.</li>
|
||
<li><strong>D · Zweikampf-Forderung</strong> (1×, nur Phase 2): Duell best of 3 (W6). Wendl gewinnt 2/3 → 5 Sofortschaden; Boss gewinnt 2/3 → Wendl geht zu Boden.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">⚔ Helfer-Aktionen <span style="font-size:0.75rem;opacity:0.7;">(je 1× pro Kampf)</span></div>
|
||
<div class="rules-box" style="background: rgba(201,162,39,0.05); border-color: #4a2800;">
|
||
<ul class="rules-list">
|
||
<li><strong>Jaron – Schwerer Hieb:</strong> würfelt 2 W6 statt 1 (je 4+ = 1 Treffer).</li>
|
||
<li><strong>Heindl – Arkaner Eingriff:</strong> erzwingt eine 6 bei Wendl ODER hebt eine Boss-Fähigkeit auf.</li>
|
||
<li><strong>Dom – Notsalbe:</strong> Wendl steht nach „zu Boden" sofort auf und greift noch an. Alt.: +1 Extra-Würfel.</li>
|
||
<li><strong>Markus – Kampflied:</strong> spielt 30 Sek. live → diese Runde trifft die Gruppe schon ab 3+.</li>
|
||
<li><strong>Christoph – Schauder-Blick:</strong> starrt den Boss an → er überspringt seine nächste Fähigkeit.</li>
|
||
<li><strong>Stammtisch (Basti, Joe, Paul) – Schlachtruf:</strong> gemeinsamer Wurf, 4+ = 3 Schaden auf einmal.</li>
|
||
</ul>
|
||
</div>
|
||
<p style="font-size:0.9rem;"><strong>Tavernenvolk:</strong> feuert an; geht Wendl zu Boden und ALLE rufen seinen Namen → +1 Freiwürfel nächste Runde (einmalig).</p>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🎲 Wendls 6 Sonderangriffe <span style="font-size:0.75rem;opacity:0.7;">(je 1 Würfel)</span></div>
|
||
<div class="rules-box" style="background: rgba(201,162,39,0.05); border-color: #4a2800;">
|
||
<ul class="rules-list">
|
||
<li><strong>1 · „Für Iris!"</strong> – 3 W6 gleichzeitig, alle Treffer zählen (bis 3 Schaden).</li>
|
||
<li><strong>2 · „Der Bräutigam schlägt zurück"</strong> – wiederholt einen missratenen Würfel (≤3).</li>
|
||
<li><strong>3 · „Freunde!" – Massenangriff</strong> – alle werfen Münze/Daumen, jeder 3. Treffer = 1 Schaden.</li>
|
||
<li><strong>4 · „Iris wartet" – Fokusmoment</strong> – ein Satz über Iris, kein Wurf: automatisch 3 Schaden.</li>
|
||
<li><strong>5 · „Finaler Schlag"</strong> – nur bei ≤3 LP: Riesen-W6, egal was kommt – der Erpresser ist tot.</li>
|
||
<li><strong>6 · Freier Würfel</strong> – zusätzlicher freier Angriff: 1 W6 (4+ = 1 Schaden), jederzeit.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🏁 Sieg & Übergang</div>
|
||
<p>Erpresser auf 0 LP → <strong>Sterbemonolog</strong> (GM):</p>
|
||
<div class="flavor-text">
|
||
„Ihr… habt gewonnen. Aber wisst ihr, was das kostet? Iris ist frei. Doch ich war nicht allein… Jemand hier… hat euch verraten. Und schmutzige Hände… färben ab. Sucht den Fleck… er steht… mitten unter euch…" <em>(stirbt)</em>
|
||
</div>
|
||
<p style="font-size:0.9rem;"><strong>Kein Name!</strong> Der Erpresser verrät nur die Spur (<em>schmutzige Hände färben ab</em>) – wer der Verräter ist, wird erst im Werwolf enttarnt. Christoph bestattet ihn (Tuch drüber). 30 Sek. später: <em>„Jemand in dieser Taverne weiß, wo Iris ist. Das Werwolf-Spiel beginnt."</em> → Karten werden <strong>aktiv ausgeteilt</strong> (SL steuert nur Drahtzieher→Marcel, Ritter→Jaron, Heiler→Dom). <strong>Notfall:</strong> Niederlage gibt es nicht – der GM riggt still (LP anpassen, Bonus-Würfel).</p>
|
||
</div>
|
||
|
||
<div class="dice-roller">
|
||
<div class="dice-roller-title">🎲 Würfel-Assistent</div>
|
||
<div class="dice-btns" id="diceBtns"></div>
|
||
<div class="dice-result" id="diceResult">–</div>
|
||
<div class="dice-log" id="diceLog"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ WERWOLF ═══ -->
|
||
<div id="tab-werwolf" class="tab-content">
|
||
<div class="parchment">
|
||
<div class="section-title">Werwolf-Finale: Die Jagd auf den Verräter</div>
|
||
<div class="section-subtitle">Mitternacht, zum Gulasch · gerigged · 2–3 Nächte · ~20–30 Min</div>
|
||
|
||
<div class="flavor-text">
|
||
Klassisches Werwolf – am Ende <strong>gerigged</strong>, unterwegs ein echtes Rätsel. Fix ist nur: <strong>Marcel (der Höschenhändler) wird als Drahtzieher enttarnt.</strong> Der laute <strong>Scherge</strong> lenkt zuerst ab – wer ihn lyncht, hat Iris noch lange nicht.
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🧩 Warum es nicht zu leicht ist</div>
|
||
<div class="rules-box">
|
||
<ul class="rules-list">
|
||
<li><strong>Zwei Schuldige, nur einer führt zu Iris:</strong> Der grobe Scherge wird zuerst gelyncht – aber Iris bleibt weg. Erst der <em>stille</em> Drahtzieher zählt.</li>
|
||
<li><strong>Hände-Clue kommt spät:</strong> Bis zur letzten Nacht sind die Visionen metaphorisch & mehrdeutig – vorher echte Diskussion.</li>
|
||
<li><strong>Falsche Fährten:</strong> 1–2 Unschuldige mit „schuldig wirkendem" Geheimnis bringen die Runde auf die falsche Spur.</li>
|
||
<li><strong>Mehr Tote = mehr Druck:</strong> Zwei Wölfe töten, der Ritter kann an Stelle eines anderen fallen – die Reihen lichten sich, Urteile werden hastig.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">✋ Der zentrale Trick: Das Mal an den Händen</div>
|
||
<p>Der Erpresser nennt im Sterben <strong>keinen Namen</strong> – nur die Spur: <em>„schmutzige Hände färben ab."</em></p>
|
||
<div class="rules-box">
|
||
<ul class="rules-list">
|
||
<li><strong>Vorbereitung:</strong> <strong>Nur Marcel</strong> bekommt heimlich ein abfärbendes Mal auf die Handinnenfläche (Rote-Bete-Saft, abwaschbarer Stempel, Kohle oder rotbrauner Filzstift).</li>
|
||
<li><strong>Tarnung:</strong> Er hat tagsüber die „Iris-Bluse" verkauft – die Farbe der Fälschung „hat abgefärbt". Narrativ wasserdicht.</li>
|
||
<li><strong>Der Scherge hat saubere Hände</strong> – der Beweis, dass er nur Handlanger war und der Drahtzieher noch lebt.</li>
|
||
<li><strong>Reveal (erst letzte Nacht!):</strong> alle zeigen die Handflächen – nur Marcels sind gezeichnet → Überführung.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🃏 Rollen & Karten</div>
|
||
<div class="rules-box">
|
||
<ul class="rules-list">
|
||
<li><strong>Spielleiter / Wahrsager – Sebastian:</strong> moderiert, narriert die Nächte, lenkt mit „Visionen".</li>
|
||
<li><strong>Drahtzieher – Marcel</strong> (fix): trägt das Mal, tötet nachts mit, hält sich raus. Finale Enthüllung.</li>
|
||
<li><strong>Scherge – 2. Werwolf</strong> (<strong>zufällig ausgeteilt</strong> – selbst der SL weiß erst nachts, wer es ist): tötet mit; die Karte weist ihn an, laut & verdächtig zu spielen. Saubere Hände.</li>
|
||
<li><strong>Ritter / Leibwächter – Jaron:</strong> schützt jede Nacht einen; greifen die Wölfe seinen Schützling an, <strong>stirbt Jaron an dessen Stelle</strong>. Nicht sich selbst.</li>
|
||
<li><strong>Heiler – Dom:</strong> darf 1× ein Nachtopfer zurückholen.</li>
|
||
<li><strong>Falsche Fährte (1–2):</strong> unschuldige Gäste mit verdächtigem Geheimnis (Gold geklaut / nachts beim Händler) – lenken ab.</li>
|
||
<li><strong>Wendl & Dorfbewohner:</strong> diskutieren, verdächtigen, johlen, stimmen ab – Wendl hebt am Ende den Zeigefinger.</li>
|
||
</ul>
|
||
</div>
|
||
<p style="font-size:0.9rem;"><strong>Optional:</strong> Seher-Rolle an einen gebrieften Spieler abgeben, der nachts „prüft". Sebastian bleibt dann reiner Moderator.</p>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🎴 Kartenvergabe – aktiv ausgeteilt</div>
|
||
<p>Die Karten werden <strong>vor aller Augen aktiv ausgeteilt</strong> – so sitzt jeder mit verdeckter Karte da und <strong>rätselt mit</strong>.</p>
|
||
<div class="rules-box">
|
||
<ul class="rules-list">
|
||
<li>Der <strong>Spielleiter</strong> steuert unauffällig nur <strong>drei</strong> Karten: <strong>Drahtzieher → Marcel</strong>, <strong>Ritter → Jaron</strong>, <strong>Heiler → Dom</strong>.</li>
|
||
<li>Alle übrigen Karten (<strong>Scherge, 1–2 falsche Fährten, Rest Dorf</strong>) gehen <strong>zufällig</strong> an den Tisch.</li>
|
||
<li><strong>Folge:</strong> Selbst der SL weiß nicht, wer der <strong>Scherge</strong> wird – das zeigt sich erst nachts. Echtes Mitraten für alle.</li>
|
||
<li><strong>Methode:</strong> identische Rückseiten; mit dem verdeckten Stapel die Runde abgehen, den drei Schlüsselpersonen die vorbereitete Karte zustecken, Rest der Reihe nach.</li>
|
||
<li><strong>Tipp:</strong> Wendl unauffällig eine Dorfbewohner-Karte zuspielen – der Bräutigam bleibt der Jäger.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🔍 Die Beweislage (über den Tag gesammelt)</div>
|
||
<p>Alle echten Hinweise zeigen auf einen <em>Händler-Typ</em> – und der Scherge ist <strong>kein</strong> Händler. Lege sie als Wendls „Akte" in die Tischmitte:</p>
|
||
<div class="rules-box">
|
||
<ul class="rules-list">
|
||
<li><strong>Die falsche Bluse</strong> (Quest ⑥) – Fälschung mit Flecken, die Farbe färbt ab.</li>
|
||
<li><strong>Der Pub-Quiz-Hinweis</strong> (⑤) – die Hinweis-Karte vom Stammtisch deutet auf den Höschenhändler.</li>
|
||
<li><strong>Kevs Erinnerung</strong> (③) – „ein Mann mit Korb voll Wäsche".</li>
|
||
<li><strong>Der Morse-Zettel</strong> (⑨) – nennt das falsche Kleidungsstück.</li>
|
||
<li><strong>Der stumme Zeuge</strong> (⑧) – deutete „Mann mit Korb voll Wäsche".</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🎬 Ablauf – geskriptet (2–3 Nächte)</div>
|
||
<div class="rules-box">
|
||
<ul class="rules-list">
|
||
<li><strong>Auftakt (2 Min):</strong> GM-Frame „…Jemand lügt. Das Werwolf-Spiel beginnt." → Karten <strong>aktiv austeilen</strong> (3 gesteuert, Rest zufällig), Regelansage (45 Sek): <em>wer verbannt wird, deckt sofort auf</em>.</li>
|
||
<li><strong>🌙 Nacht 1:</strong> Beide Wölfe wählen ein Opfer (<em>nie Wendl/Schlüsselrolle</em>). Ritter wirft sich ggf. dazwischen, Heiler darf retten. Vision noch vage.</li>
|
||
<li><strong>Morgen 1:</strong> Opfer „zum Schweigen gebracht" (Tortilla-Gag). Vage Vision: <em>„Zwei sind es – eine Hand tat Drecksarbeit, eine lenkte im Schatten."</em> Diskussion + Abstimmung → Richtung <strong>Scherge</strong> lenken.</li>
|
||
<li><strong>Scherge fällt:</strong> <strong>Karte sofort aufdecken</strong> – als Werwolf entlarvt. Geht laut unter (<em>„Wo das Mädchen ist, weiß nur EINER von euch noch!"</em>). GM: <em>„Iris ist immer noch weg – das war nicht der Kopf."</em> Stakes hoch.</li>
|
||
<li><strong>🌙 Nacht 2:</strong> Marcel tötet (allein, falls Scherge raus) → weiteres Opfer. Vision wird wärmer: <em>„Der Drahtzieher trägt ein Mal an den Händen…"</em></li>
|
||
<li><strong>🌙 Nacht 3 / Reveal:</strong> Klare Vision: <em>„Zeigt alle eure Hände!"</em> → nur Marcels sind gezeichnet → <strong>Wendl</strong> zeigt → einstimmige Verbannung.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🏆 Auflösung & Übergang</div>
|
||
<p>Marcel wird unter Gejohle <strong>verbannt</strong> (raus aus der Tür) → schlüpft draußen in Iris' Kleidung → kommt als <strong>gerettete Iris</strong> zurück. Wendl bekommt <strong>Pokal + Schatzsack</strong>, die Kleidungsstücke gehen an „Iris", der Barde spielt das Finale-Lied. Foto. Prost.</p>
|
||
</div>
|
||
|
||
<div class="encounter-box">
|
||
<div class="encounter-title">🎛 Rigging-Knöpfe & Failsafes</div>
|
||
<div class="rules-box">
|
||
<ul class="rules-list">
|
||
<li><strong>Runde verdächtigt Wendl</strong> → Wahrsager: „Nicht der, den ihr feiert."</li>
|
||
<li><strong>Marcel zu früh im Visier</strong> (Nacht 1) → „Erst die laute Hand – der Kopf wartet." Verdacht auf den Schergen lenken.</li>
|
||
<li><strong>Scherge gelyncht, Runde glaubt fertig</strong> → „Iris ist immer noch weg – das war nur der Handlanger."</li>
|
||
<li><strong>Diskussion stockt</strong> → Stammtisch/Barkeeper liest einen Hinweis laut vor.</li>
|
||
<li><strong>Zu viele Tote zu schnell</strong> → Heiler/Ritter „retten" lassen, eine Nacht fällt niemand.</li>
|
||
<li><strong>Runde müde / will Schluss</strong> → Nacht 3 streichen, direkt zum Hände-Reveal.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rules-box">
|
||
<div class="rules-title">Requisiten (Werwolf-Teil)</div>
|
||
<ul class="rules-list">
|
||
<li>Werwolf-Kärtchen für alle (Drahtzieher, Scherge, Ritter, Heiler, 1–2 falsche Fährten, Dorf) – Marcels Karte schon bei ihm</li>
|
||
<li>Das Mal: abfärbendes Mittel + heimliche Gelegenheit, es <strong>nur Marcel</strong> aufzutragen</li>
|
||
<li>Geheimnis-Zettel für die falschen Fährten</li>
|
||
<li>Die „Akte": alle Tages-Hinweise gesammelt</li>
|
||
<li>Kalte Tortilla (Knebel-Gag), Iris-Kostüm draußen, Pokal + Schatzsack</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ TRACKER ═══ -->
|
||
<div id="tab-tracker" class="tab-content">
|
||
<div class="parchment">
|
||
<div class="section-title">Helden-Tracker</div>
|
||
<div class="section-subtitle">Live-Übersicht während des Abends</div>
|
||
|
||
<div style="display:grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom:1.5rem;">
|
||
<div class="drink-tracker">
|
||
<div class="drink-count" id="drinkCount">0</div>
|
||
<div class="drink-label">Getrunken 🍺</div>
|
||
<div class="drink-btns">
|
||
<button class="btn-drink add" onclick="changeDrinks(1)">+1</button>
|
||
<button class="btn-drink sub" onclick="changeDrinks(-1)">–1</button>
|
||
</div>
|
||
</div>
|
||
<div class="drink-tracker">
|
||
<div class="drink-count" id="questCount" style="color: var(--green);">0/6</div>
|
||
<div class="drink-label">Würfel 🎲</div>
|
||
<div style="margin-top:1rem; font-family: 'Cinzel', serif; font-size:0.75rem; color:#8a6520; letter-spacing:0.1em;">
|
||
Ab 6 → Kampf frei<span class="gm-only"> · im Intro-Tab verwalten</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rules-box">
|
||
<div class="rules-title">Quest-Status</div>
|
||
<div id="questStatusList" style="display:flex; flex-direction:column; gap:0.4rem; margin-top:0.5rem;"></div>
|
||
</div>
|
||
|
||
<div class="rules-box gm-only" style="margin-top:1rem;">
|
||
<div class="rules-title">Notizen</div>
|
||
<textarea id="notes" placeholder="Hier Notizen eingeben..."
|
||
style="width:100%; background:rgba(138,101,32,0.05); border:1px solid #c9a22740; border-radius:4px; padding:0.6rem; font-family:'Crimson Pro',serif; font-size:0.95rem; color:var(--ink-light); resize:vertical; min-height:80px; outline:none;"
|
||
oninput="saveNotes()"></textarea>
|
||
</div>
|
||
|
||
<div class="gm-only" style="text-align:center; margin-top:1.5rem;">
|
||
<button class="btn-primary" onclick="resetAll()">↺ Neustart</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SPIELER ═══ -->
|
||
<div id="tab-spieler" class="tab-content">
|
||
<div class="parchment">
|
||
<div class="section-title">Die Gefährten</div>
|
||
<div class="section-subtitle">Spieler, Rollen & Quest-Zuteilung</div>
|
||
|
||
<div class="flavor-text">
|
||
Trage hier alle Teilnehmer des Abends ein und weise ihnen ihre Rolle zu. So behältst du den Überblick, wer welche Quest hütet.
|
||
</div>
|
||
|
||
<!-- Hinzufügen -->
|
||
<div class="rules-box">
|
||
<div class="rules-title">Spieler hinzufügen</div>
|
||
<div class="spieler-add-row" style="margin-top:0.8rem;">
|
||
<input class="spieler-input" id="sName" placeholder="Name..." maxlength="30" onkeydown="if(event.key==='Enter')addSpieler()">
|
||
<select class="spieler-select" id="sRolle">
|
||
<option value="held">⚔ Held (Wendl)</option>
|
||
<option value="spielleiter">🔮 Wahrsager / Spielleiter</option>
|
||
<option value="hüter" selected>🎭 NPC / Questgeber</option>
|
||
<option value="ratsmitglied">⚔ Kampf-Helfer</option>
|
||
<option value="einsteiger">🃏 Schlüsselrolle</option>
|
||
<option value="gast">🍺 Tavernenvolk</option>
|
||
</select>
|
||
<select class="spieler-select" id="sQuest">
|
||
<option value="">– Quest zuweisen –</option>
|
||
</select>
|
||
<button class="btn-add" onclick="addSpieler()">+ Hinzufügen</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Liste -->
|
||
<div id="spielerList" class="spieler-list">
|
||
<div class="spieler-empty">Keine Teilnehmer eingetragen.</div>
|
||
</div>
|
||
|
||
<!-- Zusammenfassung -->
|
||
<div class="spieler-summary" id="spielerSummary" style="display:none;">
|
||
<span class="summary-chip" id="sumTotal">0 Teilnehmer</span>
|
||
<span class="summary-chip" id="sumHüter">0 NPCs</span>
|
||
<span class="summary-chip" id="sumRat">0 Kampf-Helfer</span>
|
||
<span class="summary-chip" id="sumEinsteiger">0 Schlüsselrollen</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Rollen-Legende -->
|
||
<div class="parchment">
|
||
<div class="section-title" style="font-size:1.1rem;">Rollen-Übersicht</div>
|
||
<div class="ornament">✦ ✦ ✦</div>
|
||
<div class="rules-box">
|
||
<ul class="rules-list">
|
||
<li><span class="spieler-rolle-badge rolle-held">⚔ HELD</span> Wendl – besteht alle Quests und führt den Kampf.</li>
|
||
<li><span class="spieler-rolle-badge rolle-spielleiter">🔮 WAHRSAGER</span> Sebastian – Spielleiter/GM, gibt zur vollen Stunde den nächsten Tipp.</li>
|
||
<li><span class="spieler-rolle-badge rolle-hüter">🎭 NPC</span> Questgeber wie Barkeeper Alex, Heindl, Janosch & Kajetan, Luki …</li>
|
||
<li><span class="spieler-rolle-badge rolle-ratsmitglied">⚔ KAMPF-HELFER</span> Jaron, Heindl, Dom, Markus, Christoph, Stammtisch – Sonder-Aktionen im Tabletop-Kampf.</li>
|
||
<li><span class="spieler-rolle-badge rolle-einsteiger">🃏 SCHLÜSSELROLLE</span> Höschenhändler (Marcel = Komplize) bzw. Erpresser (GM-Stimme).</li>
|
||
<li><span class="spieler-rolle-badge rolle-gast">🍺 TAVERNENVOLK</span> Stimmung, Gerüchte, Anfeuern – wer ohne Quest ist, gibt Wendl eine Goldmünze.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ ABLAUF ═══ -->
|
||
<div id="tab-ablauf" class="tab-content">
|
||
<div class="parchment">
|
||
<div class="section-title">Ablaufplan</div>
|
||
<div class="section-subtitle">Empfohlener Zeitplan für den Abend</div>
|
||
|
||
<div class="timeline">
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">~12:00 · Auto 1</div>
|
||
<div class="timeline-event">Aufbau & Deko</div>
|
||
<div class="timeline-note">Taverne herrichten, Hinweise + Morse-Zettel verstecken, Playlist an, Schlafplätze klären.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">~14:00 · Wendl kommt an</div>
|
||
<div class="timeline-event">Ankunft & Schreckensnachricht 🕊️</div>
|
||
<div class="timeline-note">Wirt/GM begrüßt in der Rolle, erstes Anstoßen + Grillhendl. Die Brieftaube (= Backhendl) bringt die Nachricht: Iris ist entführt!</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">Nachmittag · Akt 1</div>
|
||
<div class="timeline-event">Leichte Quests</div>
|
||
<div class="timeline-note">Bargames, Heindls Prüfungen, erste Kleidungsstücke + Würfel. Wahrsager-Heartbeat läuft zur vollen Stunde.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">21:00 · Pause</div>
|
||
<div class="timeline-event">Grillen 🔥</div>
|
||
<div class="timeline-note">Bewusster Energie-Reset. Essen, durchatmen, neu sammeln.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">Nach dem Essen · Akt 2 (Prime Time)</div>
|
||
<div class="timeline-event">Fleischige Quests → ⚔ Tabletop-Kampf</div>
|
||
<div class="timeline-note">Sobald 6 Würfel voll → Kampf gegen den Erpresser. Sieg → Kleidungsstücke „befreit", aber der Komplize ist noch frei.</div>
|
||
</div>
|
||
<div class="timeline-item">
|
||
<div class="timeline-time">Mitternacht · Akt 3</div>
|
||
<div class="timeline-event">🐺 Werwolf-Finale & Iris-Reveal 🏆</div>
|
||
<div class="timeline-note">Zum Gulasch: Höschenhändler enttarnt → rausgeschleppt → kommt als „gerettete Iris" zurück. Pokal + Schatzsack, Kleidungsstücke an Iris, Barden-Lied, Foto, Prost.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rules-box" style="margin-top:1.5rem;">
|
||
<div class="rules-title">Hinweise für den Spielleiter</div>
|
||
<ul class="rules-list">
|
||
<li>Wahrsager-Heartbeat: zur vollen Stunde genau ein Tipp für Wendl.</li>
|
||
<li>Failsafe: steht Wendl ratlos rum, schiebt Wahrsager oder Barkeeper aktiv an.</li>
|
||
<li>NPCs vorab briefen! Aufgabe kennen, Belohnung (Würfel/Hinweis/Kleidung/Gold) bereit haben.</li>
|
||
<li>Werwolf ist fürs Drama gerigged – die Anklage muss auf dem Höschenhändler landen.</li>
|
||
<li>Atmosphäre hat Vorrang vor strikter Regelauslegung.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /page-wrap -->
|
||
</div><!-- /app -->
|
||
|
||
<script>
|
||
// ═══════════════════════════════════════════
|
||
// PART 1: API-HELPERS & STATE-SYNC
|
||
// ═══════════════════════════════════════════
|
||
const API = '/api';
|
||
|
||
async function apiGet(url) {
|
||
try {
|
||
const r = await fetch(API + url);
|
||
return r.ok ? r.json() : null;
|
||
} catch(e) { console.error('GET failed:', e); return null; }
|
||
}
|
||
|
||
async function apiPost(url, body) {
|
||
try {
|
||
const r = await fetch(API + url, {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(body || {})
|
||
});
|
||
return r.ok ? r.json() : null;
|
||
} catch(e) { console.error('POST failed:', e); return null; }
|
||
}
|
||
|
||
async function apiPut(url, body) {
|
||
try {
|
||
const r = await fetch(API + url, {
|
||
method: 'PUT',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(body || {})
|
||
});
|
||
return r.ok ? r.json() : null;
|
||
} catch(e) { console.error('PUT failed:', e); return null; }
|
||
}
|
||
|
||
async function apiDelete(url) {
|
||
try {
|
||
const r = await fetch(API + url, {method: 'DELETE'});
|
||
return r.ok ? r.json() : null;
|
||
} catch(e) { console.error('DELETE failed:', e); return null; }
|
||
}
|
||
|
||
// Lokale State-Variablen
|
||
const itemCollected = new Array(6).fill(false); // 6 Würfel = Kampf-Freischaltung
|
||
let drinks = 0;
|
||
const questDone = new Array(11).fill(false); // 11 Quests
|
||
|
||
// State vom Server laden
|
||
async function syncState() {
|
||
const data = await apiGet('/game');
|
||
if (!data) return;
|
||
const s = data.state;
|
||
if (s.itemCollected) s.itemCollected.forEach((v, i) => { if (i < itemCollected.length) itemCollected[i] = v; });
|
||
if (s.drinks !== undefined) drinks = s.drinks;
|
||
if (s.questDone) s.questDone.forEach((v, i) => { if (i < questDone.length) questDone[i] = v; });
|
||
document.getElementById('drinkCount').textContent = drinks;
|
||
const ta = document.getElementById('notes');
|
||
// Notizen nicht überschreiben, während jemand tippt
|
||
if (ta && document.activeElement !== ta && typeof s.notesText === 'string') ta.value = s.notesText;
|
||
}
|
||
|
||
// Live-Sync: zieht regelmäßig den Server-State und gleicht offene Instanzen ab
|
||
// (Schlucke, Würfel, Quests synchron über mehrere Geräte hinweg).
|
||
let pollTimer = null;
|
||
async function pollState() {
|
||
const data = await apiGet('/game');
|
||
if (!data || !data.state) return;
|
||
const s = data.state;
|
||
let itemsChanged = false, questsChanged = false;
|
||
if (s.drinks !== undefined && s.drinks !== drinks) {
|
||
drinks = s.drinks;
|
||
const dc = document.getElementById('drinkCount');
|
||
if (dc) dc.textContent = drinks;
|
||
}
|
||
if (s.itemCollected) s.itemCollected.forEach((v, i) => {
|
||
if (i < itemCollected.length && itemCollected[i] !== v) { itemCollected[i] = v; itemsChanged = true; }
|
||
});
|
||
if (s.questDone) s.questDone.forEach((v, i) => {
|
||
if (i < questDone.length && questDone[i] !== v) { questDone[i] = v; questsChanged = true; }
|
||
});
|
||
if (itemsChanged) {
|
||
for (let i = 0; i < itemCollected.length; i++) {
|
||
const slot = document.getElementById('inv-' + i);
|
||
if (slot) slot.classList.toggle('collected', itemCollected[i]);
|
||
}
|
||
}
|
||
if (questsChanged) buildQuests();
|
||
if (itemsChanged || questsChanged) updateQuestStatus();
|
||
const ta = document.getElementById('notes');
|
||
if (ta && document.activeElement !== ta && typeof s.notesText === 'string' && ta.value !== s.notesText) ta.value = s.notesText;
|
||
}
|
||
|
||
// State zum Server speichern
|
||
async function saveStateToServer(state) {
|
||
await apiPost('/game/save-state', { ...state, timestamp: Date.now() });
|
||
}
|
||
|
||
// Lokale UI aktualisieren
|
||
function refreshUI() {
|
||
for (let i = 0; i < itemCollected.length; i++) {
|
||
const slot = document.getElementById('inv-' + i);
|
||
if (slot) slot.classList.toggle('collected', itemCollected[i]);
|
||
}
|
||
const dc = document.getElementById('drinkCount');
|
||
if (dc) dc.textContent = drinks;
|
||
updateQuestStatus();
|
||
renderSpieler();
|
||
}
|
||
|
||
// Sync + Refresh
|
||
async function updateAndSync(state) {
|
||
await saveStateToServer(state);
|
||
refreshUI();
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════
|
||
// PART 2: QUEST-DATEN
|
||
// ═══════════════════════════════════════════
|
||
const quests = [
|
||
{ id:0, badge:'easy', badgeLabel:'AKT 1', name:'① Bargames',
|
||
holder:'<strong>Janosch & Kajetan</strong> · Die Spieler', item:'🎲 Würfel + 👕 Kleidung',
|
||
desc:'Wendl spielt 4-Gewinnt + Schnapsen gegen die beiden.',
|
||
success:'✓ Würfelset (in Iris\' Hose) + 1 Würfel', fail:'⌁ Best 2 von 3, notfalls gewinnt Wendl',
|
||
details:'Sitzen sichtbar am Spieltisch. Das Würfelset ist in Iris\' Hose versteckt – also gleich ein Kleidungsstück mit dabei.' },
|
||
{ id:1, badge:'easy', badgeLabel:'AKT 1', name:'② Heindls Prüfungen',
|
||
holder:'<strong>Heindl</strong> · Magier', item:'🎲 Würfel',
|
||
desc:'Heindl spricht Wendl spontan an und stellt einen Random Skill-Check (z. B. „Balanciere das Bier über den Hof").',
|
||
success:'✓ Kleinigkeit + 1 Würfel', fail:'⌁ Beliebig wiederholbar (Reservequest)',
|
||
details:'Heindl kann jederzeit weitere Mini-Checks nachziehen, falls Zeit/Energie da ist.' },
|
||
{ id:2, badge:'medium', badgeLabel:'AKT 2', name:'③ Der genesene Säufer',
|
||
holder:'<strong>Kev</strong> (Säufer) + <strong>Dom</strong> (Heiler)', item:'🃏 Hinweis + 🎲 Würfel',
|
||
desc:'Kev liegt sturzbetrunken da → ins Krankenzimmer bringen, ein paar „Heilkräuter" ums Haus verteilt sammeln, „Trank" (Jägermeister) verabreichen.',
|
||
success:'✓ Kev erinnert sich → Hinweis-Karte + 1 Würfel', fail:'⌁ Einziger sequenzieller Quest – Dom lotst',
|
||
details:'Dom weiß, welche Kräuter Kev kurieren, und begleitet den Quest.' },
|
||
{ id:3, badge:'medium', badgeLabel:'AKT 2', name:'④ Das Ritterduell',
|
||
holder:'<strong>Jaron</strong> · Ritter', item:'👕 Kleidung + 🎲 Würfel',
|
||
desc:'Fecht-Duell mit Schaumstoffwaffen (bei Bedarf Gregors Schaumstoffteile), best of 3 Treffer.',
|
||
success:'✓ Kleidungsstück + 1 Würfel', fail:'⌁ Best of 3 – notfalls gewinnt Wendl',
|
||
details:'Schaumstoffwaffen statt echter Klingen – falls nötig die Schaumstoffteile von Gregor.' },
|
||
{ id:4, badge:'medium', badgeLabel:'AKT 2', name:'⑤ Pub-Quiz',
|
||
holder:'<strong>Alex</strong> (Barkeeper) + Stammtisch', item:'🃏 Hinweis + 🎲 Würfel',
|
||
desc:'Allgemeine Fragenrunde – Alex stellt die Fragen zusammen (kein Insider-Lösungssatz mehr).',
|
||
success:'✓ Hinweis-Karte (vom Stammtisch) + 1 Würfel', fail:'⌁ Locker halten, Alex hat die Fragen',
|
||
details:'Stammtisch ruft Wendl heran. Fragen sind allgemein gehalten; die Hinweis-Karte gibt es als Belohnung.' },
|
||
{ id:5, badge:'easy', badgeLabel:'AKT 1', name:'⑦ Hypnose-Stammtisch',
|
||
holder:'<strong>Joe, Paul</strong> · Stammtisch', item:'👕 Kleidung + 🃏 Hinweis',
|
||
desc:'Stammtisch lotst Wendl heran und fragt ihn über Iris aus (Wie kennengelernt? Lieblingsessen?). Herzlich + lustig.',
|
||
success:'✓ Kleidungsstück + Hinweis', fail:'⌁ Reiner Stimmungs-Quest',
|
||
details:'„Hypnotisch" inszeniert, aber eigentlich ein warmes Iris-Interview.' },
|
||
{ id:6, badge:'hard', badgeLabel:'KERN', name:'⑥ Die gekaufte Bluse',
|
||
holder:'<strong>Marcel</strong> · Höschenhändler (Komplize)', item:'👕 falsches Kleidungsstück',
|
||
desc:'Wendl spart 10 Gold und kauft die „Iris-Bluse" (weißes T-Shirt mit Grillhendl-Flecken).',
|
||
success:'✓ Das FALSCHE Kleidungsstück (späterer Beweis)', fail:'⌁ Trigger: Wendl hat 10 Gold',
|
||
details:'Scheinbar Fortschritt – in Wahrheit die Falle des Komplizen. Die Fälschung überführt Marcel im Finale.' },
|
||
{ id:7, badge:'tabletop', badgeLabel:'HINWEIS', name:'⑧ Der stumme Zeuge',
|
||
holder:'Ein <strong>Tavernengast</strong> (Pantomime)', item:'🃏 Hinweis',
|
||
desc:'Ein Gast deutet wild herum – der Erpresser hat ihm „die Zunge ausgeschnitten". Wendl errät per Pantomime einen Hinweis.',
|
||
success:'✓ Hinweis-Karte (z. B. „Mann mit Korb voll Wäsche")', fail:'⌁ GM hilft mit Gesten nach',
|
||
details:'Einer aus dem Tavernenvolk ist der stumme Zeuge.' },
|
||
{ id:8, badge:'tabletop', badgeLabel:'HINWEIS', name:'⑨ Der verschlüsselte Brief',
|
||
holder:'Spontan für jemanden · Morsecode', item:'🃏 Hinweis',
|
||
desc:'Ein versteckter Zettel wird gefunden und mit der Morse-Tabelle (vom Barden/Wahrsager) übersetzt.',
|
||
success:'✓ Hinweis-Karte (nennt das falsche Kleidungsstück)', fail:'⌁ Morse-Text vorher festlegen & altern!',
|
||
details:'Zettel + Morse-Tabelle müssen vorbereitet und versteckt werden.' },
|
||
{ id:9, badge:'easy', badgeLabel:'AKT 1', name:'⑩ Lukis Lutschstein',
|
||
holder:'<strong>Luki</strong> · Eseltreiber', item:'🃏 Hinweis (🪙 Gold-Sink)',
|
||
desc:'Luki jammert um seinen Esel. Wendl muss einen Lutschstein beim Barkeeper kaufen (Gold).',
|
||
success:'✓ Hinweis-Karte + Dank', fail:'⌁ Reiner Gold-Sink, kein Risiko',
|
||
details:'Verbrennt etwas Gold und gibt dafür einen Finale-Hinweis.' },
|
||
{ id:10, badge:'easy', badgeLabel:'GAG', name:'⑪ Saugnapf-Wurf',
|
||
holder:'Gag-Minispiel · jederzeit', item:'🎲 Würfel oder 🪙 Gold',
|
||
desc:'Zielwurf mit Saugnapf-Pfeilen.',
|
||
success:'✓ 1 Würfel oder Gold', fail:'⌁ Lockerer Lückenfüller',
|
||
details:'Jederzeit spielbar, wenn eine Pause zu füllen ist.' },
|
||
];
|
||
// ═══════════════════════════════════════════
|
||
// PART 3: STARS, TABS & START
|
||
// ═══════════════════════════════════════════
|
||
(function createStars() {
|
||
const c = document.getElementById('stars');
|
||
if (!c) return;
|
||
for (let i = 0; i < 120; i++) {
|
||
const s = document.createElement('div');
|
||
s.className = 'star';
|
||
s.style.cssText = `left:${Math.random()*100}%;top:${Math.random()*100}%;
|
||
--d:${2+Math.random()*4}s;--delay:${Math.random()*5}s;
|
||
--bright:${0.4+Math.random()*0.6};width:${Math.random()<0.3?3:2}px;height:${Math.random()<0.3?3:2}px;`;
|
||
c.appendChild(s);
|
||
}
|
||
})();
|
||
|
||
function showTab(id, el) {
|
||
document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
|
||
document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
|
||
const content = document.getElementById('tab-' + id);
|
||
if (content) content.classList.add('active');
|
||
const btn = el || document.querySelector('.nav-tab[data-tab="' + id + '"]');
|
||
if (btn) btn.classList.add('active');
|
||
}
|
||
|
||
// ═══════════════════════════════════════════
|
||
// MODUS: Spieler (Standard) ⇄ Spielleiter (Passwort)
|
||
// ═══════════════════════════════════════════
|
||
const GM_PASSWORD = 'Wendl2026'; // Hier bei Bedarf das Passwort ändern
|
||
|
||
function setMode(mode) {
|
||
const btn = document.getElementById('modeBtn');
|
||
if (mode === 'gm') {
|
||
document.body.classList.add('gm-mode');
|
||
if (btn) btn.textContent = '👁 Spieler-Modus';
|
||
showTab('intro'); // erster SL-Tab
|
||
} else {
|
||
document.body.classList.remove('gm-mode');
|
||
if (btn) btn.textContent = '🔒 Spielleiter';
|
||
showTab('willkommen'); // erster Spieler-Tab
|
||
}
|
||
}
|
||
|
||
function toggleMode() {
|
||
if (document.body.classList.contains('gm-mode')) {
|
||
setMode('spieler');
|
||
} else {
|
||
const pw = prompt('Passwort für den Spielleiter-Modus:');
|
||
if (pw === null) return; // Abbruch
|
||
if (pw === GM_PASSWORD) {
|
||
setMode('gm');
|
||
} else {
|
||
alert('Falsches Passwort.');
|
||
}
|
||
}
|
||
}
|
||
|
||
async function startApp() {
|
||
document.getElementById('cover').style.display = 'none';
|
||
document.getElementById('app').style.display = 'block';
|
||
buildDice();
|
||
await syncState(); // erst Server-State holen …
|
||
await syncSpieler();
|
||
buildQuests(); // … dann Quests mit korrektem Erledigt-Status bauen
|
||
refreshUI();
|
||
// Live-Sync starten (nur einmal, auch wenn startApp doppelt läuft)
|
||
if (!pollTimer) pollTimer = setInterval(pollState, 3000);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════
|
||
// PART 4: QUEST-FUNKTIONEN
|
||
// ═══════════════════════════════════════════
|
||
function buildQuests() {
|
||
const grid = document.getElementById('questGrid');
|
||
if (!grid) return;
|
||
grid.innerHTML = '';
|
||
quests.forEach((q, i) => {
|
||
const card = document.createElement('div');
|
||
card.className = 'quest-card' + (questDone[i] ? ' done' : '');
|
||
card.id = 'qcard-' + i;
|
||
card.innerHTML = `
|
||
<span class="quest-badge badge-${q.badge}">${q.badgeLabel}</span>
|
||
<div class="quest-name">${q.name}</div>
|
||
<div class="quest-holder gm-only">NPC: ${q.holder}</div>
|
||
<div class="quest-desc gm-only">${q.desc}</div>
|
||
<div class="quest-rewards gm-only">
|
||
<span class="reward-success">${q.success}</span>
|
||
<span class="reward-fail">${q.fail}</span>
|
||
</div>
|
||
<div class="quest-expand gm-only">${q.details}<br><br>
|
||
<button onclick="event.stopPropagation(); toggleDone(${i})"
|
||
style="font-family:'Cinzel',serif;font-size:0.75rem;background:transparent;border:1px solid #8a6520;
|
||
padding:0.3rem 0.8rem;cursor:pointer;color:#8a6520;border-radius:3px;">
|
||
${questDone[i] ? '↩ Als offen markieren' : '✓ Als erledigt markieren'}
|
||
</button>
|
||
</div>
|
||
<div class="expand-hint gm-only">▼ Aufklappen für Details</div>`;
|
||
card.addEventListener('click', () => toggleExpand(i));
|
||
grid.appendChild(card);
|
||
});
|
||
}
|
||
|
||
function toggleExpand(i) {
|
||
const card = document.getElementById('qcard-' + i);
|
||
if (card) card.classList.toggle('expanded');
|
||
}
|
||
|
||
async function toggleDone(i) {
|
||
questDone[i] = !questDone[i];
|
||
const sv = await apiGet('/game');
|
||
if (!sv) return;
|
||
const s = {...sv.state, questDone: [...questDone]};
|
||
await updateAndSync(s);
|
||
buildQuests();
|
||
updateQuestStatus();
|
||
}
|
||
|
||
// ═══════════════════════════════════════════
|
||
// PART 5: INVENTORY & DRINKS
|
||
// ═══════════════════════════════════════════
|
||
async function toggleItem(i) {
|
||
itemCollected[i] = !itemCollected[i];
|
||
const slot = document.getElementById('inv-' + i);
|
||
if (slot) slot.classList.toggle('collected', itemCollected[i]);
|
||
const sv = await apiGet('/game');
|
||
if (!sv) return;
|
||
const s = {...sv.state, itemCollected: [...itemCollected]};
|
||
await updateAndSync(s);
|
||
updateQuestStatus();
|
||
}
|
||
|
||
async function changeDrinks(n) {
|
||
const res = await apiPost('/game/drink', {amount: n});
|
||
if (res && res.drinks !== undefined) {
|
||
drinks = res.drinks;
|
||
document.getElementById('drinkCount').textContent = drinks;
|
||
}
|
||
}
|
||
|
||
// ═══════════════════════════════════════════
|
||
// PART 6: STATUS
|
||
// ═══════════════════════════════════════════
|
||
function updateQuestStatus() {
|
||
const count = itemCollected.filter(Boolean).length;
|
||
const qc = document.getElementById('questCount');
|
||
if (qc) qc.textContent = count + '/6';
|
||
const list = document.getElementById('questStatusList');
|
||
if (!list) return;
|
||
list.innerHTML = quests.map((q, i) => `
|
||
<div style="display:flex;align-items:center;gap:0.5rem;font-size:0.9rem;
|
||
color:${questDone[i]?'#2a5a1a':'#5a3a10'};">
|
||
<span>${questDone[i] ? '✓' : '○'}</span>
|
||
<span>${q.name}</span>
|
||
<span class="gm-only" style="margin-left:auto;font-size:0.8rem;opacity:0.7">${q.item}</span>
|
||
</div>`).join('');
|
||
}
|
||
// ═══════════════════════════════════════════
|
||
// PART 7: WÜRFEL
|
||
// ═══════════════════════════════════════════
|
||
function buildDice() {
|
||
const btn = document.getElementById('diceBtns');
|
||
if (!btn) return;
|
||
[4,6,8,10,12,20,100].forEach(d => {
|
||
const b = document.createElement('button');
|
||
b.className = 'dice-btn';
|
||
b.textContent = 'W' + d;
|
||
b.onclick = () => rollDice(d);
|
||
btn.appendChild(b);
|
||
});
|
||
}
|
||
|
||
function rollDice(sides) {
|
||
const result = Math.floor(Math.random() * sides) + 1;
|
||
const el = document.getElementById('diceResult');
|
||
const log = document.getElementById('diceLog');
|
||
if (!el || !log) return;
|
||
el.classList.remove('rolling');
|
||
void el.offsetWidth;
|
||
el.classList.add('rolling');
|
||
el.textContent = result;
|
||
let reactions;
|
||
if (sides === 6) {
|
||
// Kampf-Würfel: 4+ = 1 Treffer
|
||
reactions = result === 6 ? '⭐ Sechs! Heldenmoment!' :
|
||
result >= 4 ? '⚔ Treffer! (4+)' :
|
||
result === 1 ? '💀 Daneben – prallt ab!' : '✗ Prallt ab (unter 4)';
|
||
} else {
|
||
reactions = result === sides ? '⭐ KRITISCH! Legendär!' :
|
||
result === 1 ? '💀 Kritischer Fehlschlag! Trinken!' :
|
||
result >= Math.ceil(sides * 0.7) ? '✓ Starkes Ergebnis!' :
|
||
result >= Math.ceil(sides * 0.4) ? '~ Mittelmaß...' : '✗ Schwach. Trinken?';
|
||
}
|
||
log.textContent = `W${sides} → ${result} · ${reactions}`;
|
||
}
|
||
// ═══════════════════════════════════════════
|
||
// PART 8: SPIELER (API-Version)
|
||
// ═══════════════════════════════════════════
|
||
let spieler = [];
|
||
|
||
const rolleLabels = {
|
||
held: '⚔ Held (Wendl)',
|
||
spielleiter: '🔮 Wahrsager / SL',
|
||
hüter: '🎭 NPC / Questgeber',
|
||
ratsmitglied: '⚔ Kampf-Helfer',
|
||
einsteiger: '🃏 Schlüsselrolle',
|
||
gast: '🍺 Tavernenvolk',
|
||
};
|
||
|
||
async function syncSpieler() {
|
||
const data = await apiGet('/spieler');
|
||
if (data) spieler = data;
|
||
populateQuestSelect();
|
||
renderSpieler();
|
||
}
|
||
|
||
function populateQuestSelect() {
|
||
const sel = document.getElementById('sQuest');
|
||
if (!sel) return;
|
||
const current = sel.value;
|
||
sel.innerHTML = '<option value="">– Quest zuweisen –</option>';
|
||
quests.forEach((q, i) => {
|
||
const opt = document.createElement('option');
|
||
opt.value = i;
|
||
opt.textContent = q.name;
|
||
sel.appendChild(opt);
|
||
});
|
||
sel.value = current;
|
||
}
|
||
|
||
async function addSpieler() {
|
||
const nameEl = document.getElementById('sName');
|
||
const rolleEl = document.getElementById('sRolle');
|
||
const questEl = document.getElementById('sQuest');
|
||
const name = nameEl.value.trim();
|
||
if (!name) { nameEl.focus(); return; }
|
||
const rolle = rolleEl.value;
|
||
const questIdx = questEl.value !== '' ? parseInt(questEl.value) : null;
|
||
const res = await apiPost('/spieler', {name, rolle, quest_id: questIdx});
|
||
if (res && res.spieler) {
|
||
spieler.push(res.spieler);
|
||
nameEl.value = '';
|
||
questEl.value = '';
|
||
renderSpieler();
|
||
}
|
||
}
|
||
|
||
async function deleteSpieler(id) {
|
||
await apiDelete('/spieler/' + id);
|
||
spieler = spieler.filter(s => s.id !== id);
|
||
renderSpieler();
|
||
}
|
||
|
||
async function changeRolle(id, rolle) {
|
||
const s = spieler.find(s => s.id === id);
|
||
if (s) { s.rolle = rolle; await apiPut('/spieler/' + id, {rolle}); renderSpieler(); }
|
||
}
|
||
|
||
async function changeQuest(id, val) {
|
||
const s = spieler.find(s => s.id === id);
|
||
if (s) { s.questIdx = val !== '' ? parseInt(val) : null; await apiPut('/spieler/' + id, {quest_id: s.questIdx}); renderSpieler(); }
|
||
}
|
||
|
||
function getInitial(name) { return name.charAt(0).toUpperCase(); }
|
||
|
||
function renderSpieler() {
|
||
const list = document.getElementById('spielerList');
|
||
if (!list) return;
|
||
if (spieler.length === 0) {
|
||
list.innerHTML = '<div class="spieler-empty">Keine Teilnehmer eingetragen.</div>';
|
||
} else {
|
||
list.innerHTML = spieler.map(s => {
|
||
const questName = s.questIdx !== null ? quests[s.questIdx]?.name : null;
|
||
return `<div class="spieler-item">
|
||
<div class="spieler-avatar">${getInitial(s.name)}</div>
|
||
<div class="spieler-info">
|
||
<div class="spieler-name-display">${s.name}</div>
|
||
<div class="spieler-rolle-display">${questName ? '🔒 ' + questName : rolleLabels[s.rolle]}</div>
|
||
</div>
|
||
<span class="spieler-rolle-badge rolle-${s.rolle}">${rolleLabels[s.rolle]}</span>
|
||
<select class="spieler-quest-sel" onchange="changeQuest(${s.id}, this.value)">
|
||
<option value="">– Keine Quest –</option>
|
||
${quests.map((q,i) => `<option value="${i}" ${s.questIdx===i?'selected':''}>${q.name}</option>`).join('')}
|
||
</select>
|
||
<select class="spieler-select" style="font-size:0.8rem;padding:0.25rem 0.4rem;" onchange="changeRolle(${s.id}, this.value)">
|
||
${Object.entries(rolleLabels).map(([k,v]) => `<option value="${k}" ${s.rolle===k?'selected':''}>${v}</option>`).join('')}
|
||
</select>
|
||
<button class="btn-del" onclick="deleteSpieler(${s.id})">✕</button>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
const summary = document.getElementById('spielerSummary');
|
||
if (summary) {
|
||
summary.style.display = spieler.length ? 'flex' : 'none';
|
||
document.getElementById('sumTotal').textContent = spieler.length + ' Teilnehmer';
|
||
document.getElementById('sumHüter').textContent = spieler.filter(s=>s.rolle==='hüter'||s.questIdx!==null).length + ' NPCs';
|
||
document.getElementById('sumRat').textContent = spieler.filter(s=>s.rolle==='ratsmitglied').length + ' Kampf-Helfer';
|
||
document.getElementById('sumEinsteiger').textContent = spieler.filter(s=>s.rolle==='einsteiger').length + ' Schlüsselrollen';
|
||
}
|
||
}
|
||
|
||
// ═══════════════════════════════════════════
|
||
// PART 9: NOTIZEN & NEUSTART
|
||
// ═══════════════════════════════════════════
|
||
let notesTimer = null;
|
||
function saveNotes() {
|
||
clearTimeout(notesTimer);
|
||
notesTimer = setTimeout(async () => {
|
||
const ta = document.getElementById('notes');
|
||
if (!ta) return;
|
||
const sv = await apiGet('/game');
|
||
if (!sv) return;
|
||
await saveStateToServer({ ...sv.state, notesText: ta.value });
|
||
}, 600);
|
||
}
|
||
|
||
async function resetAll() {
|
||
if (!confirm('Wirklich alles zurücksetzen? Würfel, Quests, Schlucke und Notizen werden gelöscht.')) return;
|
||
await apiPost('/game/reset');
|
||
itemCollected.fill(false);
|
||
questDone.fill(false);
|
||
drinks = 0;
|
||
const ta = document.getElementById('notes');
|
||
if (ta) ta.value = '';
|
||
buildQuests();
|
||
refreshUI();
|
||
}
|
||
|
||
// Start bei Page Load
|
||
document.addEventListener('DOMContentLoaded', startApp);
|
||
</script>
|