Red Mode: 10 FSK-18-Karten (Rotlicht), Host-Schalter im Raum, rotes Design
Build & Deploy / deploy (push) Successful in 19s

This commit is contained in:
Kimi Work
2026-10-10 17:42:00 +02:00
parent 4e2eda855a
commit 1094cf6948
7 changed files with 205 additions and 16 deletions
+25 -1
View File
@@ -1,6 +1,6 @@
// ── Kategorien & Statement-Karten (aus dem offiziellen Spielregeln-PDF) ── // ── Kategorien & Statement-Karten (aus dem offiziellen Spielregeln-PDF) ──
export type KategorieId = 'abenteuer' | 'persoenlichkeit' | 'alltag' | 'party' | 'erfolg' export type KategorieId = 'abenteuer' | 'persoenlichkeit' | 'alltag' | 'party' | 'erfolg' | 'rotlicht'
export interface Kategorie { export interface Kategorie {
id: KategorieId id: KategorieId
@@ -59,6 +59,15 @@ export const KATEGORIEN: Kategorie[] = [
ring: 'ring-emerald-400/40', ring: 'ring-emerald-400/40',
chip: 'bg-emerald-400/15 text-emerald-300 border-emerald-400/30', chip: 'bg-emerald-400/15 text-emerald-300 border-emerald-400/30',
}, },
{
id: 'rotlicht',
titel: 'Rotlicht & Versuchung 🔞',
emoji: '🌶️',
farbe: 'from-red-500 to-rose-700',
textfarbe: 'text-red-300',
ring: 'ring-red-400/40',
chip: 'bg-red-400/15 text-red-300 border-red-400/30',
},
] ]
export const kategorieById = (id: KategorieId): Kategorie => export const kategorieById = (id: KategorieId): Kategorie =>
@@ -96,8 +105,23 @@ export const KARTEN: Spielkarte[] = [
{ id: 18, kategorie: 'erfolg', text: 'Wer wäre der beste Kandidat für eine Reality-TV-Show?' }, { id: 18, kategorie: 'erfolg', text: 'Wer wäre der beste Kandidat für eine Reality-TV-Show?' },
{ id: 19, kategorie: 'erfolg', text: 'Wer könnte am ehesten eine 30-minütige Präsentation über ein Thema halten, von dem er eigentlich gar keine Ahnung hat?' }, { id: 19, kategorie: 'erfolg', text: 'Wer könnte am ehesten eine 30-minütige Präsentation über ein Thema halten, von dem er eigentlich gar keine Ahnung hat?' },
{ id: 20, kategorie: 'erfolg', text: 'Wer hat das Potenzial, ein berühmter Geheimagent zu sein, weil er am besten Geheimnisse bewahren kann?' }, { id: 20, kategorie: 'erfolg', text: 'Wer hat das Potenzial, ein berühmter Geheimagent zu sein, weil er am besten Geheimnisse bewahren kann?' },
// Rotlicht & Versuchung (Red Mode / FSK 18)
{ id: 21, kategorie: 'rotlicht', text: 'Wer aus der Gruppe würde am ehesten einen One-Night-Stand über eine Dating-App haben?' },
{ id: 22, kategorie: 'rotlicht', text: 'Wer würde bei „Wahrheit oder Pflicht“ drei Mal hintereinander „Pflicht“ wählen – egal, was kommt?' },
{ id: 23, kategorie: 'rotlicht', text: 'Wer würde am ehesten heimlich in der hintersten Reihe des Kinos rummachen?' },
{ id: 24, kategorie: 'rotlicht', text: 'Wer würde nach einer durchzechten Nacht als Erster den Namen seines Dates vergessen?' },
{ id: 25, kategorie: 'rotlicht', text: 'Wer würde am ehesten einen Strip-Poker-Abend anzetteln – und zwar gewinnen?' },
{ id: 26, kategorie: 'rotlicht', text: 'Wer würde am ehesten mit dem Ex eines guten Freunds anbändeln und es vertuschen?' },
{ id: 27, kategorie: 'rotlicht', text: 'Wer hat die Stimme, mit der man am Telefon am ehesten flirten könnte?' },
{ id: 28, kategorie: 'rotlicht', text: 'Wer würde in einem Club am ehesten auf der Theke tanzen – und es auch noch gut finden?' },
{ id: 29, kategorie: 'rotlicht', text: 'Wer würde am ehesten den ganzen Abend mit einem Fremden flirten, nur um einen Drink abzugreifen?' },
{ id: 30, kategorie: 'rotlicht', text: 'Wer sagt „Ich melde mich noch“ – und meint damit die nächsten drei Jahre?' },
] ]
/** FSK-18-Karten – nur im Red Mode verfügbar */
export const ROT_KARTEN: Spielkarte[] = KARTEN.filter((k) => k.kategorie === 'rotlicht')
export const KARTEN_OHNE_ROT: Spielkarte[] = KARTEN.filter((k) => k.kategorie !== 'rotlicht')
// ── Spielregeln (Inhalt aus dem offiziellen PDF) ────────── // ── Spielregeln (Inhalt aus dem offiziellen PDF) ──────────
export interface RegelSchritt { export interface RegelSchritt {
+15
View File
@@ -0,0 +1,15 @@
import { useEffect } from 'react'
/** Hängt die Design-Klasse red-mode an <body> und passt theme-color an. */
export function useRedMode(aktiv: boolean) {
useEffect(() => {
document.body.classList.toggle('red-mode', aktiv)
const meta = document.querySelector('meta[name="theme-color"]')
const vorher = meta?.getAttribute('content')
if (meta) meta.setAttribute('content', aktiv ? '#1f060b' : '#170a2e')
return () => {
document.body.classList.remove('red-mode')
if (meta && vorher) meta.setAttribute('content', vorher)
}
}, [aktiv])
}
+22
View File
@@ -136,6 +136,28 @@
animation: glanzen 3.5s linear infinite; animation: glanzen 3.5s linear infinite;
} }
/* ── Red Mode (FSK 18): rote Design-Welt ────────────────── */
body.red-mode {
background-image:
radial-gradient(ellipse 80% 50% at 50% -10%, rgba(239, 68, 68, 0.32), transparent),
radial-gradient(ellipse 60% 40% at 100% 0%, rgba(244, 63, 94, 0.22), transparent),
radial-gradient(ellipse 60% 40% at 0% 100%, rgba(190, 18, 60, 0.2), transparent),
linear-gradient(180deg, #1f060b 0%, #2a0710 55%, #1a0409 100%);
}
body.red-mode .text-gradient {
background-image: linear-gradient(to right, #f87171, #fb7185, #f472b6);
}
body.red-mode nav.fixed {
background-color: rgb(38 8 14 / 0.88);
border-color: rgba(248, 113, 113, 0.18);
}
body.red-mode .glanz {
background-image: linear-gradient(90deg, #ef4444, #e11d48, #be123c, #e11d48, #ef4444) !important;
}
body.red-mode .glass-card {
border-color: rgba(248, 113, 113, 0.14);
}
/* Zahlen-Chips der Hero-Sektion */ /* Zahlen-Chips der Hero-Sektion */
.zahlen-chip { .zahlen-chip {
@apply flex h-9 w-9 items-center justify-center rounded-xl font-display text-lg font-semibold shadow-lg; @apply flex h-9 w-9 items-center justify-center rounded-xl font-display text-lg font-semibold shadow-lg;
+4 -2
View File
@@ -32,6 +32,7 @@ export interface RaumZustand {
spieler: SpielerInfo[] spieler: SpielerInfo[]
minSpieler: number minSpieler: number
maxSpieler: number maxSpieler: number
redMode: boolean
alleReady: boolean alleReady: boolean
rankingsFertig: number rankingsFertig: number
punkte: Record<string, number> punkte: Record<string, number>
@@ -79,11 +80,11 @@ async function anfrage(methode: 'GET' | 'POST', pfad: string, body?: object, tok
} }
/** Raum eröffnen; liefert Session direkt mit */ /** Raum eröffnen; liefert Session direkt mit */
export async function raumErstellen(name: string): Promise<{ session: RaumSession; zustand: RaumZustand }> { export async function raumErstellen(name: string, redMode = false): Promise<{ session: RaumSession; zustand: RaumZustand }> {
const res = await fetch('/api/game/room', { const res = await fetch('/api/game/room', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }), body: JSON.stringify({ name, redMode }),
}) })
const daten = await res.json().catch(() => ({})) const daten = await res.json().catch(() => ({}))
if (!res.ok || !daten.token) throw new ApiFehler(daten.fehler || 'Raum konnte nicht erstellt werden') if (!res.ok || !daten.token) throw new ApiFehler(daten.fehler || 'Raum konnte nicht erstellt werden')
@@ -108,6 +109,7 @@ export async function raumBeitreten(code: string, name: string): Promise<{ sessi
export const zustandLaden = (s: RaumSession) => anfrage('GET', `/${s.code}`, undefined, s.token) export const zustandLaden = (s: RaumSession) => anfrage('GET', `/${s.code}`, undefined, s.token)
export const readySetzen = (s: RaumSession, ready: boolean) => anfrage('POST', `/${s.code}/ready`, { ready }, s.token) export const readySetzen = (s: RaumSession, ready: boolean) => anfrage('POST', `/${s.code}/ready`, { ready }, s.token)
export const redModeSetzen = (s: RaumSession, redMode: boolean) => anfrage('POST', `/${s.code}/redmode`, { redMode }, s.token)
export const frageStellen = (s: RaumSession, frage: string) => anfrage('POST', `/${s.code}/question`, { frage }, s.token) export const frageStellen = (s: RaumSession, frage: string) => anfrage('POST', `/${s.code}/question`, { frage }, s.token)
export const rankingAbgeben = (s: RaumSession, reihenfolge: string[]) => export const rankingAbgeben = (s: RaumSession, reihenfolge: string[]) =>
anfrage('POST', `/${s.code}/ranking`, { reihenfolge }, s.token) anfrage('POST', `/${s.code}/ranking`, { reihenfolge }, s.token)
+57 -7
View File
@@ -1,6 +1,9 @@
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { RefreshCw, Shuffle } from 'lucide-react' import { Flame, RefreshCw, Shuffle } from 'lucide-react'
import { KARTEN, KATEGORIEN, kategorieById, type KategorieId, type Spielkarte } from '@/data/game' import { KARTEN, KATEGORIEN, KARTEN_OHNE_ROT, kategorieById, type KategorieId, type Spielkarte } from '@/data/game'
import { useRedMode } from '@/hooks/useRedMode'
const REDMODE_KEY = 'einsbisacht-redmode'
type Filter = 'alle' | KategorieId type Filter = 'alle' | KategorieId
@@ -47,10 +50,24 @@ export default function Cards() {
const [filter, setFilter] = useState<Filter>('alle') const [filter, setFilter] = useState<Filter>('alle')
const [gezogen, setGezogen] = useState<Spielkarte | null>(null) const [gezogen, setGezogen] = useState<Spielkarte | null>(null)
const [zaehler, setZaehler] = useState(0) // triggert Re-Flip-Animation const [zaehler, setZaehler] = useState(0) // triggert Re-Flip-Animation
const [redMode, setRedMode] = useState(() => localStorage.getItem(REDMODE_KEY) === '1')
useRedMode(redMode)
const toggleRedMode = () => {
setRedMode((alt) => {
localStorage.setItem(REDMODE_KEY, alt ? '0' : '1')
return !alt
})
}
const stapel = useMemo( const stapel = useMemo(
() => (filter === 'alle' ? KARTEN : KARTEN.filter((k) => k.kategorie === filter)), () =>
[filter] filter === 'alle'
? redMode
? KARTEN
: KARTEN_OHNE_ROT
: KARTEN.filter((k) => k.kategorie === filter),
[filter, redMode]
) )
const ziehen = () => { const ziehen = () => {
@@ -71,15 +88,48 @@ export default function Cards() {
Runde haben soll! 🎨 Runde haben soll! 🎨
</p> </p>
{/* Red Mode Toggle */}
<button
onClick={toggleRedMode}
className={`mt-4 flex w-full items-center justify-between rounded-2xl border p-3.5 transition-all active:scale-[0.98] ${
redMode
? 'border-red-400/50 bg-red-500/10 shadow-[0_0_20px_rgba(239,68,68,0.25)]'
: 'border-white/10 bg-white/[0.05]'
}`}
>
<span className="flex items-center gap-2.5">
<Flame size={19} className={redMode ? 'text-red-400' : 'text-white/40'} />
<span className="text-left">
<span className={`block text-sm font-bold ${redMode ? 'text-red-300' : 'text-white/75'}`}>
🔞 Red Mode <span className="font-extrabold">(FSK 18)</span>
</span>
<span className="block text-xs text-white/45">
{redMode ? 'Aktiviert – 10 extra freche Karten im Stapel' : 'Schaltet 10 freche Karten frei'}
</span>
</span>
</span>
<span
className={`relative h-7 w-12 shrink-0 rounded-full transition-colors ${
redMode ? 'bg-gradient-to-r from-red-500 to-rose-600' : 'bg-white/15'
}`}
>
<span
className={`absolute top-1 h-5 w-5 rounded-full bg-white shadow transition-all ${
redMode ? 'left-6' : 'left-1'
}`}
/>
</span>
</button>
{/* Kategorie-Filter */} {/* Kategorie-Filter */}
<div className="scrollbar-none -mx-5 mt-5 flex gap-2 overflow-x-auto px-5 pb-1"> <div className="scrollbar-none -mx-5 mt-4 flex gap-2 overflow-x-auto px-5 pb-1">
<FilterChip aktiv={filter === 'alle'} onClick={() => setFilter('alle')} label="✨ Alle" /> <FilterChip aktiv={filter === 'alle'} onClick={() => setFilter('alle')} label="✨ Alle" />
{KATEGORIEN.map((k) => ( {KATEGORIEN.filter((k) => k.id !== 'rotlicht' || redMode).map((k) => (
<FilterChip <FilterChip
key={k.id} key={k.id}
aktiv={filter === k.id} aktiv={filter === k.id}
onClick={() => setFilter(k.id)} onClick={() => setFilter(k.id)}
label={`${k.emoji} ${k.titel.split(' ')[0]}`} label={k.id === 'rotlicht' ? `🌶️ Rotlicht` : `${k.emoji} ${k.titel.split(' ')[0]}`}
chipKlasse={k.chip} chipKlasse={k.chip}
/> />
))} ))}
+71 -6
View File
@@ -14,7 +14,8 @@ import {
Trophy, Trophy,
Users, Users,
} from 'lucide-react' } from 'lucide-react'
import { KARTEN } from '@/data/game' import { KARTEN, KARTEN_OHNE_ROT } from '@/data/game'
import { useRedMode } from '@/hooks/useRedMode'
import { import {
ladeSession, ladeSession,
speichereSession, speichereSession,
@@ -22,6 +23,7 @@ import {
raumBeitreten, raumBeitreten,
zustandLaden, zustandLaden,
readySetzen, readySetzen,
redModeSetzen,
frageStellen, frageStellen,
rankingAbgeben, rankingAbgeben,
naechstePhase, naechstePhase,
@@ -34,6 +36,8 @@ export default function Game() {
const [session, setSession] = useState<RaumSession | null>(ladeSession) const [session, setSession] = useState<RaumSession | null>(ladeSession)
const [zustand, setZustand] = useState<RaumZustand | null>(null) const [zustand, setZustand] = useState<RaumZustand | null>(null)
const [fehler, setFehler] = useState('') const [fehler, setFehler] = useState('')
// Red Mode des Raums verdunkelt das Design (nur während des Spiels)
useRedMode(!!zustand?.redMode)
const refresh = useCallback(async () => { const refresh = useCallback(async () => {
if (!session) return if (!session) return
@@ -66,8 +70,8 @@ export default function Game() {
setSession(s) setSession(s)
}, []) }, [])
const erstellen = useCallback(async (name: string) => { const erstellen = useCallback(async (name: string, redMode: boolean) => {
const { session: s } = await raumErstellen(name) const { session: s } = await raumErstellen(name, redMode)
setSession(s) setSession(s)
}, []) }, [])
@@ -142,12 +146,13 @@ function Einstieg({
onErstellen, onErstellen,
onBeitreten, onBeitreten,
}: { }: {
onErstellen: (name: string) => Promise<void> onErstellen: (name: string, redMode: boolean) => Promise<void>
onBeitreten: (code: string, name: string) => Promise<void> onBeitreten: (code: string, name: string) => Promise<void>
}) { }) {
const [modus, setModus] = useState<'start' | 'beitreten'>('start') const [modus, setModus] = useState<'start' | 'beitreten'>('start')
const [name, setName] = useState('') const [name, setName] = useState('')
const [code, setCode] = useState(() => new URLSearchParams(window.location.search).get('raum') ?? '') const [code, setCode] = useState(() => new URLSearchParams(window.location.search).get('raum') ?? '')
const [redMode, setRedMode] = useState(false)
const [lade, setLade] = useState(false) const [lade, setLade] = useState(false)
const [fehler, setFehler] = useState('') const [fehler, setFehler] = useState('')
@@ -157,7 +162,7 @@ function Einstieg({
setLade(true) setLade(true)
setFehler('') setFehler('')
try { try {
if (modus === 'start') await onErstellen(name.trim()) if (modus === 'start') await onErstellen(name.trim(), redMode)
else await onBeitreten(code.trim(), name.trim()) else await onBeitreten(code.trim(), name.trim())
} catch (e) { } catch (e) {
setFehler(e instanceof Error ? e.message : 'Verbindung fehlgeschlagen') setFehler(e instanceof Error ? e.message : 'Verbindung fehlgeschlagen')
@@ -207,6 +212,30 @@ function Einstieg({
className="w-full rounded-xl border border-white/10 bg-white/[0.06] px-3.5 py-3 text-[15px] font-bold text-white placeholder:font-normal placeholder:text-white/30 focus:border-pink-400/60 focus:outline-none focus:ring-2 focus:ring-pink-400/20" className="w-full rounded-xl border border-white/10 bg-white/[0.06] px-3.5 py-3 text-[15px] font-bold text-white placeholder:font-normal placeholder:text-white/30 focus:border-pink-400/60 focus:outline-none focus:ring-2 focus:ring-pink-400/20"
/> />
</Feld> </Feld>
{modus === 'start' && (
<button
onClick={() => setRedMode(!redMode)}
className={`flex w-full items-center justify-between rounded-xl border px-3.5 py-3 transition-all active:scale-[0.98] ${
redMode ? 'border-red-400/50 bg-red-500/10' : 'border-white/10 bg-white/[0.04]'
}`}
>
<span className="flex items-center gap-2 text-sm font-bold text-white/80">
🌶️ Red Mode <span className="text-red-300">🔞 FSK 18</span>
<span className="text-xs font-normal text-white/45">– freche Karten im Spiel</span>
</span>
<span
className={`relative h-6 w-10 shrink-0 rounded-full transition-colors ${
redMode ? 'bg-gradient-to-r from-red-500 to-rose-600' : 'bg-white/15'
}`}
>
<span
className={`absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-all ${
redMode ? 'left-5' : 'left-0.5'
}`}
/>
</span>
</button>
)}
{fehler && <p className="text-sm font-bold text-rose-300">{fehler}</p>} {fehler && <p className="text-sm font-bold text-rose-300">{fehler}</p>}
<button <button
onClick={los} onClick={los}
@@ -290,6 +319,36 @@ function Lobby({
</h3> </h3>
<span className="text-xs font-bold text-white/45">Mind. {z.minSpieler}</span> <span className="text-xs font-bold text-white/45">Mind. {z.minSpieler}</span>
</div> </div>
{/* Red Mode: Host schaltet frei, alle sehen den Status */}
<button
onClick={() => ich?.istHost && onAct((s) => redModeSetzen(s, !z.redMode))}
disabled={!ich?.istHost}
className={`mb-3 flex w-full items-center justify-between rounded-xl border px-3.5 py-2.5 text-left transition-all ${
z.redMode ? 'border-red-400/50 bg-red-500/10' : 'border-white/10 bg-white/[0.04]'
} ${ich?.istHost ? 'active:scale-[0.98]' : 'opacity-80'}`}
>
<span className="flex items-center gap-2 text-sm font-bold text-white/80">
🌶️ Red Mode <span className="text-red-300">🔞 FSK 18</span>
<span className="text-xs font-normal text-white/45">
{z.redMode ? 'AN – freche Karten sind im Spiel' : 'aus – klassische Karten'}
</span>
</span>
<span
className={`relative h-6 w-10 shrink-0 rounded-full transition-colors ${
z.redMode ? 'bg-gradient-to-r from-red-500 to-rose-600' : 'bg-white/15'
}`}
>
<span
className={`absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-all ${
z.redMode ? 'left-5' : 'left-0.5'
}`}
/>
</span>
</button>
{!ich?.istHost && (
<p className="-mt-2 mb-3 text-right text-[11px] text-white/35">Nur der Host kann den Red Mode ändern.</p>
)}
<div className="space-y-2"> <div className="space-y-2">
{z.spieler.map((s) => ( {z.spieler.map((s) => (
<div key={s.name} className="flex items-center justify-between rounded-xl bg-white/[0.05] px-3.5 py-2.5"> <div key={s.name} className="flex items-center justify-between rounded-xl bg-white/[0.05] px-3.5 py-2.5">
@@ -333,7 +392,8 @@ function FragePhase({ z, onAct }: { z: RaumZustand; onAct: (fn: (s: RaumSession)
const istFragesteller = z.ich === z.fragesteller const istFragesteller = z.ich === z.fragesteller
const ziehen = () => { const ziehen = () => {
const karte = KARTEN[Math.floor(Math.random() * KARTEN.length)] const stapel = z.redMode ? KARTEN : KARTEN_OHNE_ROT
const karte = stapel[Math.floor(Math.random() * stapel.length)]
setFrage(karte.text) setFrage(karte.text)
} }
@@ -354,6 +414,11 @@ function FragePhase({ z, onAct }: { z: RaumZustand; onAct: (fn: (s: RaumSession)
<div className="glass-card p-5 text-center"> <div className="glass-card p-5 text-center">
<p className="text-xs font-extrabold uppercase tracking-[0.25em] text-violet-300">Du bist dran!</p> <p className="text-xs font-extrabold uppercase tracking-[0.25em] text-violet-300">Du bist dran!</p>
<p className="mt-1 text-sm text-white/60">Ziehe eine Karte und lies sie laut vor – oder erfinde eigene Frage.</p> <p className="mt-1 text-sm text-white/60">Ziehe eine Karte und lies sie laut vor – oder erfinde eigene Frage.</p>
{z.redMode && (
<p className="mt-2 inline-block rounded-full border border-red-400/40 bg-red-500/10 px-3 py-1 text-xs font-bold text-red-300">
🌶️ Red Mode – freche Karten sind im Stapel
</p>
)}
</div> </div>
<div className="glass-card space-y-3 p-4"> <div className="glass-card space-y-3 p-4">
<button <button
+11
View File
@@ -154,6 +154,7 @@ function oeffentlicherZustand(raum, token) {
})), })),
minSpieler: MIN_SPIELER, minSpieler: MIN_SPIELER,
maxSpieler: MAX_SPIELER, maxSpieler: MAX_SPIELER,
redMode: !!raum.redMode,
alleReady, alleReady,
rankingsFertig: Object.keys(raum.rankings).length, rankingsFertig: Object.keys(raum.rankings).length,
punkte: raum.punkte, punkte: raum.punkte,
@@ -211,6 +212,7 @@ const server = http.createServer(async (req, res) => {
status: 'lobby', status: 'lobby',
runde: 0, runde: 0,
frage: '', frage: '',
redMode: !!body.redMode,
spieler: [{ name, token: crypto.randomBytes(18).toString('hex'), ready: false, istHost: true }], spieler: [{ name, token: crypto.randomBytes(18).toString('hex'), ready: false, istHost: true }],
rankings: {}, rankings: {},
punkte: { [name]: 0 }, punkte: { [name]: 0 },
@@ -260,6 +262,15 @@ const server = http.createServer(async (req, res) => {
return antwort(res, 200, { ok: true, zustand: oeffentlicherZustand(raum, token) }); return antwort(res, 200, { ok: true, zustand: oeffentlicherZustand(raum, token) });
} }
/* Red Mode umschalten (nur Host, nur vor Spielstart) */
if (req.method === 'POST' && aktion === 'redmode') {
if (raum.status !== 'lobby') return fehler(res, 409, 'Red Mode ist nur vor Spielstart aenderbar');
if (!spieler.istHost) return fehler(res, 403, 'Nur der Host kann den Red Mode aendern');
raum.redMode = !!body.redMode;
speichern();
return antwort(res, 200, { ok: true, zustand: oeffentlicherZustand(raum, token) });
}
/* Frage stellen */ /* Frage stellen */
if (req.method === 'POST' && aktion === 'question') { if (req.method === 'POST' && aktion === 'question') {
if (raum.status !== 'question') return fehler(res, 409, 'Keine Fragephase'); if (raum.status !== 'question') return fehler(res, 409, 'Keine Fragephase');