dev.html: spilmenu, intro og skift af spil

?game=menu viser menuen, introen for hvert spil og menuen/introen ved skift
af spil (rum med 11, så noten om publikum ses). Harnessen får registret fra
games-fixture.js, som test_games holder i takt med party/games.py (og kan
skrive igen med WPP_WRITE_FIXTURE=1). Fup og Fakta-fixtures har game: 'fof',
så brandet er rigtigt igen, og baren har links mellem spillene.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
agw
2026-10-20 21:47:54 +02:00
co-authored by Claude Opus 5.5
parent d6a4bb0b52
commit 4cdc497bd0
4 changed files with 122 additions and 4 deletions
+1 -1
View File
@@ -88,7 +88,7 @@
opts = opts || {};
var now = Date.now();
var base = {
code: 'KLMT', phase: REAL[phase] || phase, server_time: now, deadline: null, phase_key: '0.1.' + (REAL[phase] || phase), narration: [],
game: 'fof', code: 'KLMT', phase: REAL[phase] || phase, server_time: now, deadline: null, phase_key: '0.1.' + (REAL[phase] || phase), narration: [],
join_url: 'https://party.weircon.dk/?code=KLMT', min_players: 2, max_players: 8,
round: { number: 1, name: 'Runde 1', multiplier: 1, question_number: 2, questions_in_round: 3 },
players: players(6), audience_count: 3
+22 -3
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fup og Fakta – dev-harness</title>
<title>Weirsøe Party Protocol: dev-harness</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600;700&display=swap">
<link rel="stylesheet" href="party.css">
<style>
@@ -23,6 +23,8 @@
body.only-host .stage { padding: 0; }
body.only-host .hostbox { width: 100vw; height: 100vh; border-radius: 0; box-shadow: none; }
body.only-phones .host-panel { display: none; }
body.only-host-panel .panel:not(.host-panel) { display: none; }
.devbar a { color: #2de2e6; margin-right: 8px; }
</style>
</head>
<body class="harness">
@@ -34,8 +36,10 @@
<div class="panel"><div class="lbl" id="l2"></div><div class="phonebox" id="ph2"></div></div>
</div>
<script>window.WPP_HARNESS = true;</script>
<script src="games-fixture.js"></script>
<script src="common.js"></script>
<script src="tegn.js"></script>
<script src="menu.js"></script>
<script src="host.js"></script>
<script src="play.js"></script>
<script src="dev-fixtures.js"></script>
@@ -46,8 +50,17 @@
var phase = qs.get('phase') || 'lobby';
var t = qs.get('t');
var only = qs.get('only');
/* ?game=tegn: Tegn og Gæts skærme (samme storskærm/telefon-kode, egne fixtures) */
var FX = qs.get('game') === 'tegn' ? WPPTegnFixtures : WPPFixtures;
/* ?game=tegn: Tegn og Gæts skærme (samme storskærm/telefon-kode, egne fixtures); ?game=menu: spilmenuen */
var MENU = {
phases: ['menu', 'intro-fof', 'intro-tegn', 'skift-menu', 'skift-intro'],
build: function (p) {
var sw = p.indexOf('skift') === 0 ? { code: 'KLMT', players: 11 } : null;
var key = p === 'intro-fof' ? 'fof' : p === 'menu' || p === 'skift-menu' ? null : 'tegn';
return { menu: key ? { screen: 'intro', key: key, values: {}, sw: sw } : { screen: 'menu', sw: sw }, phones: [] };
}
};
var FX = { tegn: WPPTegnFixtures, menu: MENU }[qs.get('game')] || WPPFixtures;
if (FX === MENU) { phase = MENU.phases.indexOf(phase) >= 0 ? phase : 'menu'; document.body.classList.add('only-host-panel'); }
if (only) document.body.classList.add('only-' + only);
if (qs.get('pw')) Array.prototype.forEach.call(document.querySelectorAll('.phonebox'), function (e) { e.style.width = qs.get('pw') + 'px'; });
var bar = document.getElementById('bar');
@@ -58,6 +71,7 @@
function draw() {
var fx = cur = FX.build(phase, { t: t, fest: qs.get("fest") === "1" });
var hostRoot = document.getElementById('hostroot');
if (fx.menu) { WPPMenu.render(hostRoot, fx.menu); return; }
hostCtx = { harness: true, sound: qs.get('sound') === '1', send: log, qrSrc: WPPFixtures.qr };
if (fx.drawings) WPPTegn.setDrawings(hostRoot, fx.drawings);
WPPHost.renderHost(hostRoot, fx.host, hostCtx);
@@ -76,6 +90,11 @@
});
}
['fof', 'tegn', 'menu'].forEach(function (g) {
var a = document.createElement('a');
a.href = '?game=' + g; a.textContent = { fof: 'Fup og Fakta', tegn: 'Tegn og Gæt', menu: 'Menu' }[g];
bar.appendChild(a);
});
FX.phases.forEach(function (p) {
var b = document.createElement('button');
b.textContent = p; if (p === phase) b.className = 'on';
+82
View File
@@ -0,0 +1,82 @@
/* Spilregistret (party/games.py) til dev.html, som ikke har en server. test_games tjekker, at det passer; skriv det
igen med: WPP_WRITE_FIXTURE=1 DJANGO_DEBUG=true .venv/bin/python manage.py test party.tests.test_games */
window.WPP_GAMES = [
{
"key": "fof",
"name": "Fup og Fakta",
"tagline": "Find på løgne, der lyder sande, og find sandheden blandt dem.",
"min_players": 2,
"max_players": 8,
"minutes": "20-30",
"steps": [
"Et spørgsmål med et hul står på skærmen.",
"Alle skriver en løgn, der lyder som det rigtige svar.",
"Find sandheden blandt løgnene på din telefon.",
"Point for at finde sandheden og for hver, der hopper på din løgn."
],
"icon": "games/fof.svg",
"settings": [
{
"name": "pack",
"label": "Spørgsmål",
"default": "alle",
"options": [
{
"value": "alle",
"label": "Alle"
},
{
"value": "familie",
"label": "Familie"
},
{
"value": "nørd",
"label": "Nørd"
},
{
"value": "arbejde",
"label": "Arbejde",
"hint": "Arbejdsspørgsmål: e-faktura, IT-sikkerhed og kategorier med pakken \"arbejde\"."
},
{
"value": "fest",
"label": "Fest-Z 🥃 18+",
"hint": "Slang, memes og dansk pop med zoomer-speaker. Narret = tag en tår, finalen er en shot."
}
]
}
]
},
{
"key": "tegn",
"name": "Tegn og Gæt",
"tagline": "Tegn, gæt og se ordene forvandle sig hele vejen rundt.",
"min_players": 3,
"max_players": 10,
"minutes": "10-20",
"steps": [
"Alle skriver et ord inden for temaet.",
"Telefonen sender ordet videre, og den næste tegner det.",
"Den næste ser kun tegningen og gætter, hvad den forestiller.",
"Til sidst ser vi, hvad ordene blev til. Giv hjerter til de bedste."
],
"icon": "games/tegn.svg",
"settings": [
{
"name": "rules",
"label": "Reglerne",
"default": "1",
"options": [
{
"value": "1",
"label": "Vis reglerne først"
},
{
"value": "0",
"label": "Spring dem over"
}
]
}
]
}
];
+17
View File
@@ -1,5 +1,7 @@
"""Registret over spil (party/games.py) og det, der læser fra det."""
import json
import os
import re
from pathlib import Path
from unittest import mock
@@ -54,6 +56,21 @@ class RegisterTests(TestCase):
self.assertNotRegex(f.read_text(encoding="utf-8"), r"""['"](tegn|fof)['"]|WPPTegn""")
class HarnessFixtureTests(TestCase):
@override_settings(WORK_PACK_LABEL="Arbejde")
def test_dev_html_has_the_register(self):
path = STATIC / "games-fixture.js"
want = [dict(g, icon=g["icon"].split("/static/party/", 1)[-1]) for g in games.menu_data()]
text = path.read_text(encoding="utf-8")
head, _, body = text.partition("window.WPP_GAMES = ")
if os.environ.get("WPP_WRITE_FIXTURE"):
path.write_text(head + "window.WPP_GAMES = " + json.dumps(want, ensure_ascii=False, indent=2) + ";\n",
encoding="utf-8")
return
self.assertEqual(json.loads(body.rstrip().rstrip(";")), want,
"games-fixture.js passer ikke med registret; se filens første linjer")
@override_settings(TRUSTED_PROXIES=["127.0.0.1"], DEV_HOST_USER=None)
class RegisterViewTests(TestCase):
def setUp(self):