U19: Spielstand-Tabelle, Formular mit INSERT/UPDATE, Begrüßung, Zähler und Reset
This commit is contained in:
parent
d0c8f18edd
commit
2fc4bf3889
2 changed files with 81 additions and 103 deletions
179
app/app.py
179
app/app.py
|
|
@ -1,9 +1,10 @@
|
|||
from flask import Flask, request
|
||||
from flask import Flask, request, session, redirect
|
||||
import html
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = "hier-eigenen-geheimen-text" # für session nötig
|
||||
DB_PATH = Path(__file__).with_name("adventure.db")
|
||||
|
||||
|
||||
|
|
@ -13,97 +14,8 @@ def get_db():
|
|||
return db
|
||||
|
||||
|
||||
BASE_CSS = """
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #07121d 0%, #102a43 45%, #1e3a5f 100%);
|
||||
color: #e2e8f0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.shell {
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
.card {
|
||||
width: min(760px, 100%);
|
||||
background: rgba(7, 18, 29, 0.88);
|
||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||
border-radius: 24px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.card h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 2rem;
|
||||
color: #f8fafc;
|
||||
}
|
||||
.card p {
|
||||
line-height: 1.7;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
.actions, .links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
.btn, .card a, .card button {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #2563eb 0%, #06b6d4 100%);
|
||||
color: #f8fafc;
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
padding: 0.8rem 1.1rem;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
box-shadow: 0 10px 25px rgba(6, 182, 212, 0.25);
|
||||
}
|
||||
.btn:hover, .card a:hover, .card button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 30px rgba(37, 99, 235, 0.35);
|
||||
}
|
||||
.btn.secondary {
|
||||
background: linear-gradient(135deg, #475569 0%, #64748b 100%);
|
||||
box-shadow: 0 10px 25px rgba(100, 116, 139, 0.25);
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
input {
|
||||
flex: 1 1 220px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||
border-radius: 999px;
|
||||
padding: 0.8rem 1rem;
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
color: #f8fafc;
|
||||
}
|
||||
input:focus {
|
||||
outline: 2px solid rgba(6, 182, 212, 0.45);
|
||||
border-color: transparent;
|
||||
}
|
||||
"""
|
||||
|
||||
BASE_JS = """
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const firstInput = document.querySelector('input');
|
||||
if (firstInput) firstInput.focus();
|
||||
});
|
||||
"""
|
||||
BASE_CSS = """... (unverändert, deine bestehende CSS bleibt) ..."""
|
||||
BASE_JS = """... (unverändert) ..."""
|
||||
|
||||
|
||||
def render_page(title, body_html):
|
||||
|
|
@ -126,16 +38,52 @@ def render_page(title, body_html):
|
|||
</html>"""
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@app.route("/app")
|
||||
@app.route("/app/")
|
||||
@app.route("/", methods=["GET", "POST"])
|
||||
@app.route("/app", methods=["GET", "POST"])
|
||||
@app.route("/app/", methods=["GET", "POST"])
|
||||
def start():
|
||||
body = """
|
||||
if request.method == "POST":
|
||||
name = request.form.get("name", "").strip()
|
||||
if name != "":
|
||||
session["spieler"] = name
|
||||
db = get_db()
|
||||
db.execute(
|
||||
"INSERT OR IGNORE INTO spielstand (spieler, raum_id) VALUES (?, 1)",
|
||||
(name,)
|
||||
)
|
||||
db.commit()
|
||||
db.close()
|
||||
return redirect("/app/raum/1")
|
||||
|
||||
db = get_db()
|
||||
anzahl = db.execute("SELECT COUNT(*) AS n FROM spielstand").fetchone()
|
||||
|
||||
weiterspielen_html = ""
|
||||
if "spieler" in session:
|
||||
s = db.execute(
|
||||
"SELECT raum_id FROM spielstand WHERE spieler=?",
|
||||
(session["spieler"],)
|
||||
).fetchone()
|
||||
if s:
|
||||
weiterspielen_html = f"""
|
||||
<p>Willkommen zurück, {html.escape(session['spieler'])}!</p>
|
||||
<div class="actions">
|
||||
<a class="btn" href="/app/raum/{s['raum_id']}">Weiterspielen</a>
|
||||
<a class="btn secondary" href="/app/reset">Neues Abenteuer</a>
|
||||
</div>
|
||||
<hr>
|
||||
"""
|
||||
db.close()
|
||||
|
||||
body = f"""
|
||||
<h1>Verlassenes Raumschiff</h1>
|
||||
<p>Du bist der einzige Überlebende an Bord. Erkunde das Schiff und aktiviere das Notsignal.</p>
|
||||
<div class="actions">
|
||||
<a class="btn" href="/app/raum/1">Mission starten</a>
|
||||
</div>
|
||||
{weiterspielen_html}
|
||||
<form method="post">
|
||||
<input name="name" placeholder="Dein Name" autocomplete="off">
|
||||
<button type="submit">Abenteuer starten</button>
|
||||
</form>
|
||||
<p><small>{anzahl['n']} Abenteurer waren schon hier.</small></p>
|
||||
"""
|
||||
return render_page("Verlassenes Raumschiff", body)
|
||||
|
||||
|
|
@ -144,32 +92,49 @@ def start():
|
|||
@app.route("/app/raum/<int:id>", methods=["GET", "POST"])
|
||||
def raum(id):
|
||||
db = get_db()
|
||||
|
||||
# Spielstand fortschreiben, sobald jemand angemeldet ist
|
||||
if "spieler" in session:
|
||||
db.execute(
|
||||
"UPDATE spielstand SET raum_id=? WHERE spieler=?",
|
||||
(id, session["spieler"])
|
||||
)
|
||||
db.commit()
|
||||
|
||||
r = db.execute("SELECT * FROM raeume WHERE id=?", (id,)).fetchone()
|
||||
if not r:
|
||||
db.close()
|
||||
return render_page(
|
||||
"Raum nicht gefunden",
|
||||
'<h1>Raum nicht gefunden</h1><p>Der angeforderte Raum existiert nicht.</p><div class="actions"><a class="btn secondary" href="/app/">Zurück zum Start</a></div>'
|
||||
'<h1>Raum nicht gefunden</h1><p>Der angeforderte Raum existiert nicht.</p>'
|
||||
'<div class="actions"><a class="btn secondary" href="/app/">Zurück zum Start</a></div>'
|
||||
)
|
||||
|
||||
unterwegs_html = ""
|
||||
if "spieler" in session:
|
||||
unterwegs_html = f'<p><small>Unterwegs als: {html.escape(session["spieler"])}</small></p>'
|
||||
|
||||
if r["raetsel_frage"]:
|
||||
if request.method == "POST":
|
||||
if request.form.get("antwort", "").strip() == r["raetsel_antwort"]:
|
||||
db.close()
|
||||
return render_page(
|
||||
"Notsignal aktiviert",
|
||||
'<h1>Notsignal erfolgreich aktiviert</h1><p>Rettung ist unterwegs.</p><div class="actions"><a class="btn" href="/app/">Zurück zum Start</a></div>'
|
||||
'<h1>Notsignal erfolgreich aktiviert</h1><p>Rettung ist unterwegs.</p>'
|
||||
'<div class="actions"><a class="btn" href="/app/">Zurück zum Start</a></div>'
|
||||
)
|
||||
db.close()
|
||||
return render_page(
|
||||
"Falscher Code",
|
||||
'<h1>Falscher Code</h1><p>Der KI-Kern reagiert nicht.</p><div class="actions"><a class="btn secondary" href="/app/raum/' + str(id) + '">nochmal versuchen</a></div>'
|
||||
'<h1>Falscher Code</h1><p>Der KI-Kern reagiert nicht.</p>'
|
||||
'<div class="actions"><a class="btn secondary" href="/app/raum/' + str(id) + '">nochmal versuchen</a></div>'
|
||||
)
|
||||
|
||||
db.close()
|
||||
body = f"""
|
||||
<h1>{html.escape(r['name'])}</h1>
|
||||
<p>{html.escape(r['beschreibung'])}</p>
|
||||
{unterwegs_html}
|
||||
<form method="post">
|
||||
<input id="antwort" name="antwort" placeholder="Antwort eingeben" autocomplete="off">
|
||||
<button type="submit">OK</button>
|
||||
|
|
@ -185,6 +150,7 @@ def raum(id):
|
|||
body = f"""
|
||||
<h1>{html.escape(r['name'])}</h1>
|
||||
<p>{html.escape(r['beschreibung'])}</p>
|
||||
{unterwegs_html}
|
||||
<div class="links">
|
||||
"""
|
||||
for a in ausgaenge:
|
||||
|
|
@ -193,5 +159,12 @@ def raum(id):
|
|||
return render_page(html.escape(r["name"]), body)
|
||||
|
||||
|
||||
@app.route("/reset")
|
||||
@app.route("/app/reset")
|
||||
def reset():
|
||||
session.pop("spieler", None) # Session vergessen (DB bleibt!)
|
||||
return redirect("/app/")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=9004)
|
||||
app.run(host="0.0.0.0", port=9004)
|
||||
|
|
@ -22,6 +22,11 @@ CREATE TABLE IF NOT EXISTS ausgaenge (
|
|||
nach_raum INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS spielstand (
|
||||
spieler TEXT PRIMARY KEY,
|
||||
raum_id INTEGER
|
||||
);
|
||||
|
||||
INSERT INTO raeume (id, name, beschreibung, raetsel_frage, raetsel_antwort) VALUES
|
||||
(1, 'Schlafkapsel', 'Du erwachst aus dem Kryoschlaf. Die Notbeleuchtung flackert und das Schiff wirkt verlassen.', NULL, NULL),
|
||||
(2, 'Brücke', 'Die Steuerzentrale ist dunkel. Mehrere Monitore zeigen Fehlermeldungen.', NULL, NULL),
|
||||
|
|
|
|||
Loading…
Reference in a new issue