U18: Lord-Business-Finale mit Polizei-Fuehrerschein-Kontrolle (Blaulicht-Intro + 3 Fahrschul-Fragen im gruenen Pruefstil)

This commit is contained in:
David Kertzscher 2026-07-10 13:21:43 +00:00
parent bd13b3c201
commit 7e297d235e
8 changed files with 212 additions and 1 deletions

213
app.py
View file

@ -201,7 +201,8 @@ def adventure_seite(inhalt, akzent="#f59e0b"):
def start():
# Fortschritt für einen frischen Durchlauf zurücksetzen
for key in [k for k in list(session.keys())
if k.startswith("raum_") or k.startswith("chal_")]:
if k.startswith("raum_") or k.startswith("chal_")
or k.startswith("fs40")]:
session.pop(key, None)
inhalt = """
<div class="adv-card">
@ -408,6 +409,212 @@ def challenge_raum(db, r):
return adventure_seite(html, akzent="#f87171")
# ─── Polizei-Führerschein-Kontrolle vor dem Lord-Business-Finale (U18) ────────
# Die drei Aufgaben stammen aus der deutschen Führerschein-Theorieprüfung.
# "richtig" = Menge der (1-basierten) anzukreuzenden Optionen. Mehrfachauswahl!
FS_FRAGEN = [
{
"frage": "Welches Verhalten ist richtig?",
"bild": "/fs_frage1.png",
"optionen": ["erst nach dem Bus abbiegen",
"vor dem Motorrad abbiegen",
"als Erster abbiegen"],
"richtig": {3},
},
{
"frage": "Wann kann es hier zu einer gefährlichen Situation kommen?",
"bild": "/fs_frage2.png",
"optionen": ["Wenn der Radfahrer am Ende des Radweges anhält",
"Wenn ich unvermindert weiterfahre",
"Wenn der Radfahrer auf meine Fahrbahn wechselt"],
"richtig": {2, 3},
},
{
"frage": "Warum ist man nach dem Konsum der Droge „Crystal Meth“ fahruntauglich?",
"bild": None,
"optionen": ["Weil plötzliche Erschöpfung eintreten kann",
"Weil Wahnvorstellungen auftreten können",
"Weil die eigene Leistungsfähigkeit überschätzt werden kann"],
"richtig": {1, 2, 3},
},
]
FS_IDX_KEY = "fs40_idx"
FS_DONE_KEY = "fs40_done"
def _blaulicht_intro():
"""Grelles deutsches Blaulicht + Polizei-Ansage vor der Kontrolle."""
inhalt = """
<style>
.blitz-overlay {
position:fixed; inset:0; z-index:5; pointer-events:none;
animation:blaulicht 0.9s steps(1,end) infinite;
}
@keyframes blaulicht {
0% { background:rgba(10,40,255,0.00); box-shadow:none; }
10% { background:rgba(30,90,255,0.55); box-shadow:inset 0 0 240px 80px #1e50ff; }
20% { background:rgba(10,40,255,0.00); box-shadow:none; }
50% { background:rgba(10,40,255,0.00); box-shadow:none; }
60% { background:rgba(30,90,255,0.55); box-shadow:inset 0 0 240px 80px #1e50ff; }
70% { background:rgba(10,40,255,0.00); box-shadow:none; }
100% { background:rgba(10,40,255,0.00); box-shadow:none; }
}
.polizei-card { position:relative; z-index:6; text-align:center;
border:2px solid #1e50ff !important;
box-shadow:0 0 40px rgba(30,80,255,0.6) !important; }
.polizei-siren { font-size:60px; margin-bottom:6px;
animation:sirene 0.9s ease-in-out infinite; }
@keyframes sirene {
0%,100% { filter:drop-shadow(0 0 4px #1e50ff); transform:scale(1); }
50% { filter:drop-shadow(0 0 22px #1e50ff); transform:scale(1.12); }
}
.polizei-badge { display:inline-block; background:#0b2a8a; color:#dbe5ff;
border:1px solid #1e50ff; border-radius:8px;
padding:6px 14px; font-weight:800; letter-spacing:2px;
margin-bottom:14px; }
</style>
<div class="blitz-overlay"></div>
<div class="adv-card polizei-card">
<div class="polizei-siren">🚨</div>
<span class="polizei-badge">🚔 POLIZEI · VERKEHRSKONTROLLE</span>
<h1>Anhalten Fahrzeugkontrolle!</h1>
<p class="adv-desc">Kurz vor dem Kragle-Tresor stoppt dich eine Streife.
Die Beamten mustern deinen Führerschein misstrauisch:
<em>Diesen Lappen haben Sie doch nie im Leben ehrlich bestanden!
Wenn er echt ist, beweisen Sie es: Lösen Sie diese drei Aufgaben
aus der Theorieprüfung dann dürfen Sie weiter.</em></p>
<p class="adv-desc">🚦 <strong>Achtung:</strong> Bei manchen Fragen ist
<strong>mehr als eine Antwort</strong> richtig. Kreuze <strong>alle</strong>
zutreffenden an ein falsches Kreuz und du fällst durch.</p>
<a href="/app/raum/40?fs=1" class="exit-link">Führerschein beweisen 📝</a>
</div>"""
return adventure_seite(inhalt, akzent="#1e50ff")
def _fs_frage_screen(idx, feedback=""):
"""Eine Führerschein-Frage im (bewusst hässlichen) grünen Prüfungs-Stil."""
f = FS_FRAGEN[idx]
bild_html = ""
if f["bild"]:
bild_html = ('<div class="fs-bild"><img src="%s" alt="Verkehrssituation"></div>'
% f["bild"])
checks = ""
for i, opt in enumerate(f["optionen"], start=1):
checks += ("""
<label class="fs-opt">
<input type="checkbox" name="opt%d" value="1">
<span class="fs-box"></span>
<span class="fs-text">%s</span>
</label>""" % (i, esc(opt)))
fb = ""
if feedback:
fb = '<div class="fs-feedback">%s</div>' % esc(feedback)
inhalt = """
<style>
.fs-shell { background:#c3d64a; border:3px solid #6b8e23;
border-radius:6px; padding:0; overflow:hidden;
box-shadow:0 6px 0 #4d6b16; }
.fs-head { background:#6b8e23; color:#f2ffd0; padding:12px 18px;
display:flex; justify-content:space-between; align-items:center;
font-weight:800; letter-spacing:.5px;
border-bottom:3px solid #3f5a10; }
.fs-head .fs-nr { background:#3f5a10; color:#eaffb0; border-radius:4px;
padding:4px 10px; font-size:14px; }
.fs-body { padding:18px; color:#1e2b05; }
.fs-frage { font-size:20px; font-weight:800; color:#243a06;
margin-bottom:14px; }
.fs-bild { margin:0 0 16px; border:3px solid #6b8e23; border-radius:4px;
overflow:hidden; background:#000; }
.fs-bild img { display:block; width:100%; height:auto; }
.fs-opts { display:flex; flex-direction:column; gap:10px; }
.fs-opt { display:flex; align-items:flex-start; gap:12px;
background:#e4f09a; border:2px solid #7c9a2e; border-radius:4px;
padding:12px 14px; cursor:pointer; transition:.12s;
font-size:16px; color:#243a06; }
.fs-opt:hover { background:#eef7b6; border-color:#4d6b16; }
.fs-opt input { position:absolute; opacity:0; width:0; height:0; }
.fs-box { width:22px; height:22px; flex-shrink:0; background:#fff;
border:2px solid #4d6b16; border-radius:3px; position:relative;
margin-top:1px; }
.fs-opt input:checked + .fs-box { background:#3f5a10; }
.fs-opt input:checked + .fs-box::after { content:""; color:#d4ff5a;
position:absolute; top:-3px; left:2px; font-size:18px;
font-weight:900; }
.fs-btn { margin-top:18px; width:100%; background:#3f5a10; color:#eaffb0;
border:none; border-bottom:4px solid #263806; border-radius:5px;
padding:14px; font-size:17px; font-weight:800; cursor:pointer;
font-family:inherit; letter-spacing:.5px; }
.fs-btn:hover { background:#4d6b16; }
.fs-feedback { background:#a11c1c; color:#ffe0e0; border:2px solid #7a0f0f;
border-radius:4px; padding:10px 14px; margin-bottom:14px;
font-weight:700; }
.fs-hint { margin-top:12px; font-size:13px; color:#3f5a10; text-align:center; }
</style>
<div class="adv-card" style="background:transparent;border:none;padding:0;box-shadow:none">
<div class="fs-shell">
<div class="fs-head">
<span>🚔 THEORIEPRÜFUNG · POLIZEIKONTROLLE</span>
<span class="fs-nr">Frage __NR__ / __GES__</span>
</div>
<div class="fs-body">
__FB__
<p class="fs-frage">__FRAGE__</p>
__BILD__
<form method="post">
<div class="fs-opts">__CHECKS__</div>
<button type="submit" class="fs-btn">Antwort abgeben </button>
</form>
<p class="fs-hint">Mehrfachauswahl möglich kreuze alle richtigen Antworten an.</p>
</div>
</div>
</div>"""
html = (inhalt
.replace("__NR__", str(idx + 1))
.replace("__GES__", str(len(FS_FRAGEN)))
.replace("__FB__", fb)
.replace("__FRAGE__", esc(f["frage"]))
.replace("__BILD__", bild_html)
.replace("__CHECKS__", checks))
return adventure_seite(html, akzent="#6b8e23")
def fuehrerschein_kontrolle():
"""Steuert die dreiteilige Polizei-Führerschein-Kontrolle vor dem Finale."""
# Noch nicht gestartet → Blaulicht-Intro (oder Start per ?fs=1)
if FS_IDX_KEY not in session:
if request.args.get("fs") == "1":
session[FS_IDX_KEY] = 0
session.modified = True
return redirect("/app/raum/40")
return _blaulicht_intro()
idx = session.get(FS_IDX_KEY, 0)
feedback = ""
if request.method == "POST" and idx < len(FS_FRAGEN):
gewaehlt = set()
for i in range(1, len(FS_FRAGEN[idx]["optionen"]) + 1):
if request.form.get("opt%d" % i):
gewaehlt.add(i)
if gewaehlt == FS_FRAGEN[idx]["richtig"]:
idx += 1
session[FS_IDX_KEY] = idx
session.modified = True
elif not gewaehlt:
feedback = ("🚨 Kein Kreuz gesetzt! „So kommen Sie hier nicht durch.“ "
"Kreuze alle richtigen Antworten an.")
else:
feedback = ("🚨 Durchgefallen bei dieser Aufgabe! Die Beamten schütteln "
"den Kopf: „Nochmal und diesmal richtig.“")
if idx >= len(FS_FRAGEN):
session[FS_DONE_KEY] = True
session.modified = True
return redirect("/app/raum/40")
return _fs_frage_screen(idx, feedback)
@app.route("/raum/<int:id>", methods=["GET", "POST"])
def raum(id):
db = get_db()
@ -428,6 +635,10 @@ def raum(id):
# ═══ Räume mit Fragen: thema (Prüf-Weg) · ziel (Finale) ═══
if typ in ("thema", "ziel"):
# ── Polizei-Führerschein-Kontrolle vor dem Lord-Business-Finale ──
if typ == "ziel" and id == 40 and not session.get(FS_DONE_KEY):
db.close()
return fuehrerschein_kontrolle()
fragen = db.execute(
"SELECT * FROM fragen WHERE raum_id=? ORDER BY id", (id,)
).fetchall()

BIN
fs_frage1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
fs_frage2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
fuhrerschein1 3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
führerschein1 frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
führerschein2 23.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
führerschein2 frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
führerschein3 123.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB