Compare commits
19 Commits
123-reakti
...
128-testbr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e84c13c40a | ||
|
|
3bfca331f2 | ||
|
|
dcee37f295 | ||
|
|
78bf2d296f | ||
|
|
a78cbcc10e | ||
|
|
c245f42821 | ||
|
|
0c8223ec83 | ||
|
|
3056a8b389 | ||
|
|
f30c984342 | ||
|
|
8f6bfd2aad | ||
|
|
2268035e58 | ||
|
|
59c3e5d90c | ||
|
|
8efd22034c | ||
|
|
d02109e474 | ||
|
|
49f5e0bad9 | ||
|
|
321487e7c8 | ||
|
|
b318237f50 | ||
|
|
198905232c | ||
|
|
43f81a5ae7 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
__pycache__
|
||||
.venv
|
||||
.env
|
||||
db.sqlite3
|
||||
tailwindcss.exe
|
||||
t-style.css
|
||||
|
||||
@@ -41,7 +41,7 @@ Um das Projekt erfolgreich zu starten, folge diesen Schritten:
|
||||
```
|
||||
7. Die Datenbank migrieren:
|
||||
```sh
|
||||
python3 core/manage.py migrate
|
||||
python django/manage.py migrate
|
||||
```
|
||||
|
||||
## Starten des Servers
|
||||
|
||||
@@ -10,8 +10,9 @@ For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||
"""
|
||||
|
||||
import json
|
||||
import json, os
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
@@ -21,11 +22,17 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' #TODO: NUR FÜR
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||
|
||||
# Support env variables from .env file if defined
|
||||
env_path = load_dotenv(os.path.join(BASE_DIR, '.env'))
|
||||
load_dotenv(env_path)
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-#+s307$rxkts=8@+_^i)8)n1b4*y4za1xz!mvv(h@!+n9!mp9)'
|
||||
#SECRET_KEY = 'django-insecure-#+s307$rxkts=8@+_^i)8)n1b4*y4za1xz!mvv(h@!+n9!mp9)'
|
||||
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'django-insecure-&psk#na5l=p3q8_a+-$4w1f^lt3lx1c@d*p4x$ymm_rn7pwb87')
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
#DEBUG = True
|
||||
DEBUG = os.environ.get('DJANGO_DEBUG', '') != 'False'
|
||||
|
||||
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*']
|
||||
|
||||
@@ -152,7 +159,7 @@ STATIC_ROOT = BASE_DIR / "staticfiles"
|
||||
STATICFILES_DIRS = [BASE_DIR / 'static']
|
||||
|
||||
# WhiteNoise configuration
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||
WHITENOISE_SKIP_COMPRESS_EXTENSIONS = ['css', 'js']
|
||||
# Return 404 instead of 500 for missing files
|
||||
WHITENOISE_MISSING_FILE_ERRNO = None
|
||||
|
||||
@@ -21,11 +21,21 @@ def quiz_names_json(request):
|
||||
form = QuizFilterForm(request.GET)
|
||||
if form.is_valid():
|
||||
search = form.cleaned_data.get('search')
|
||||
names = list(
|
||||
QivipQuiz.objects.all().annotate(max_amout_questions=Count('questions')).filter(max_amout_questions__gte=1, status='öffentlich', name__icontains=search)
|
||||
.values_list('name', flat=True)
|
||||
.distinct()
|
||||
|
||||
|
||||
names = QivipQuiz.objects.all().annotate(max_amout_questions=Count('questions')).filter(max_amout_questions__gte=1, status='öffentlich', name__icontains=search)
|
||||
|
||||
|
||||
if request.user.is_authenticated:
|
||||
names = names | QivipQuiz.objects.filter(
|
||||
user_id=request.user.id,
|
||||
status='privat',
|
||||
name__icontains=search
|
||||
)
|
||||
|
||||
names=list(names.values_list('name', flat=True).distinct())
|
||||
|
||||
|
||||
return JsonResponse(names, safe=False)
|
||||
|
||||
def overview_quiz(request):
|
||||
@@ -260,7 +270,6 @@ def delete_quiz(request, pk):
|
||||
|
||||
# Quiz anzeigen
|
||||
def detail_quiz(request, pk):
|
||||
#quiz = get_object_or_404(QivipQuiz, pk=pk, user_id=request.user)
|
||||
quiz = get_object_or_404(QivipQuiz, pk=pk)
|
||||
show_answers = request.GET.get('show_answers', 'false').lower() == 'true'
|
||||
questions = QivipQuestion.objects.filter(quiz_id=quiz)
|
||||
@@ -274,7 +283,11 @@ def detail_quiz(request, pk):
|
||||
'questions': questions,
|
||||
'detail':show_answers,
|
||||
}
|
||||
if quiz.status!="privat" or quiz.user_id==request.user:
|
||||
return render(request, 'library/detail_quiz.html', context)
|
||||
else:
|
||||
return redirect('library:overview_quiz')
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
|
||||
@@ -129,6 +129,7 @@ class GameConsumer(AsyncWebsocketConsumer):
|
||||
time_factor = time_remaining / 30000 # 30 seconds max
|
||||
score = int(base_score * (0.5 + 0.5 * time_factor))
|
||||
|
||||
participant.last_score=score
|
||||
participant.score += score
|
||||
participant.last_answer_correct = is_correct
|
||||
participant.save()
|
||||
@@ -460,7 +461,7 @@ class LobbyConsumer(AsyncWebsocketConsumer):
|
||||
if answer == correct_answer:
|
||||
# Base score for correct answer + bonus for speed
|
||||
score = 1000 + int(time_remaining * 10) # 10 points per remaining second
|
||||
|
||||
participant.last_score= score
|
||||
participant.score += score
|
||||
participant.save()
|
||||
return score
|
||||
|
||||
@@ -233,6 +233,7 @@ class GameConsumer(AsyncWebsocketConsumer):
|
||||
answer_obj.time_remaining = time_remaining
|
||||
answer_obj.save()
|
||||
|
||||
participant.last_score = score
|
||||
participant.score += score
|
||||
participant.last_answer_correct = is_correct
|
||||
participant.save()
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.7 on 2025-05-22 15:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('play', '0011_quizgame_updated_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='quizgameparticipant',
|
||||
name='last_score',
|
||||
field=models.IntegerField(default=0, verbose_name='Letzte_Punkte'),
|
||||
),
|
||||
]
|
||||
@@ -43,6 +43,7 @@ class QuizGameParticipant(models.Model):
|
||||
display_name = models.CharField(verbose_name="Anzeigename", max_length=15)
|
||||
quiz_game = models.ForeignKey(QuizGame, on_delete=models.CASCADE, related_name="participants")
|
||||
score = models.IntegerField(verbose_name="Punkte", default=0)
|
||||
last_score = models.IntegerField(verbose_name="Letzte_Punkte", default=0)
|
||||
avatar = models.CharField(max_length=200, blank=True, default="")
|
||||
last_heartbeat = models.DateTimeField(auto_now_add=True)
|
||||
last_answer = models.IntegerField(null=True, blank=True)
|
||||
|
||||
@@ -8,10 +8,20 @@ from django.contrib import messages
|
||||
from django.conf import settings
|
||||
|
||||
import json
|
||||
|
||||
import qrcode
|
||||
import base64
|
||||
from io import BytesIO
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
def lobby(request, join_code):
|
||||
relative_url = reverse("play:create_participant", kwargs={"join_code": join_code})
|
||||
full_url = request.build_absolute_uri(relative_url)
|
||||
|
||||
qr = qrcode.make(full_url)
|
||||
buffer = BytesIO()
|
||||
qr.save(buffer, format="PNG")
|
||||
img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +30,7 @@ def lobby(request, join_code):
|
||||
context = {
|
||||
'quiz': quiz,
|
||||
'quiz_game': quiz_game,
|
||||
"qr_code_base64": img_base64,
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +40,7 @@ def lobby(request, join_code):
|
||||
host_id = request.COOKIES['host_id']
|
||||
if host_id == quiz_game.host_id:
|
||||
context['host_id'] = host_id
|
||||
context["qr_code_base64"] = img_base64
|
||||
return render(request, 'play/lobby.html', context=context)
|
||||
elif mode=="learn":
|
||||
return redirect('play:create_participant', join_code=join_code)
|
||||
@@ -45,11 +57,13 @@ def lobby(request, join_code):
|
||||
if not participant.quiz_game.join_code == join_code: # Teilnehmer dem richtigen Spiel hinzufügen
|
||||
participant.quiz_game = quiz_game
|
||||
participant.score = 0 # Reset score when joining new game
|
||||
participant.last_score = 0
|
||||
participant.save()
|
||||
except QuizGameParticipant.DoesNotExist:
|
||||
return redirect('play:create_participant', join_code=join_code)
|
||||
|
||||
context['participant'] = participant
|
||||
context["qr_code_base64"] = img_base64
|
||||
return render(request, 'play/lobby.html', context=context)
|
||||
|
||||
def select_mode(request,join_code):
|
||||
@@ -70,6 +84,7 @@ def create_participant(request, join_code):
|
||||
if participant.quiz_game.join_code != join_code:
|
||||
participant.quiz_game = quiz_game
|
||||
participant.score = 0 # Reset score when joining new game
|
||||
participant.last_score = 0
|
||||
participant.save()
|
||||
return redirect('play:lobby', join_code=join_code)
|
||||
except QuizGameParticipant.DoesNotExist:
|
||||
@@ -90,6 +105,7 @@ def create_participant(request, join_code):
|
||||
participant.display_name = display_name
|
||||
participant.quiz_game = quiz_game
|
||||
participant.score = 0 # Initialize score
|
||||
participant.last_score = 0
|
||||
participant.save()
|
||||
|
||||
response = HttpResponseRedirect(reverse('play:lobby', kwargs={'join_code': join_code}))
|
||||
@@ -327,6 +343,7 @@ def selected_mode(request, join_code):
|
||||
participant.participant_id=host_id
|
||||
participant.quiz_game = quiz_game
|
||||
participant.score = 0 # Initialize score
|
||||
participant.last_score = 0
|
||||
participant.save()
|
||||
quiz_game.current_state = "question"
|
||||
quiz_game.question_start_time = timezone.now()
|
||||
@@ -334,5 +351,5 @@ def selected_mode(request, join_code):
|
||||
|
||||
return redirect('play:question',join_code)
|
||||
|
||||
return redirect('play:lobby', join_code=join_code)
|
||||
|
||||
return render(request, 'play/lobby.html', context=context)
|
||||
|
||||
0
django/static/css/tailwindcss
Normal file
0
django/static/css/tailwindcss
Normal file
BIN
django/static/mp3/Hintergrundmusik.mp3
Normal file
BIN
django/static/mp3/Hintergrundmusik.mp3
Normal file
Binary file not shown.
@@ -68,7 +68,7 @@
|
||||
<br>
|
||||
- alle öffentlichen Quizze können auch ohne Account gespielt werden
|
||||
<br>
|
||||
- qivip ist 100% kostenlos
|
||||
- qivip ist zu 100% kostenlos
|
||||
<br>
|
||||
- es gibt keine Werbung
|
||||
<br>
|
||||
@@ -133,7 +133,7 @@
|
||||
<br>
|
||||
<br>
|
||||
<b>Moderator: </b>Der Moderator selbst spielt nicht mit. Gehen Sie entweder auf die Startseite und klicken Sie auf "Moderieren" oder klicken einfach auf "spielen" (eines von Ihnen ausgewählten Quizzes) und dann auf Moderiermodus.
|
||||
Der Moderator kann das Spiel, wenn die Teilnehmer da sind starten.
|
||||
Der Moderator kann das Spiel, wenn die Teilnehmer da sind, starten.
|
||||
<br>
|
||||
<br>
|
||||
<b>Teilnehmer: </b>Gehen Sie auf die Startseite und klicken Sie auf "Teilnehmen" und geben Sie dann den beim Moderator angegebenen Zahlencode ein. Anschließlich können Sie noch einen Anzeigenamen eingeben.
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
|
||||
{% if image %}
|
||||
<img src="{{ image.image.url }}" style="max-width: 500px;" alt="Bild der Frage">
|
||||
<img src="{{ image.image.url }}" style="max-width: 100px;" alt="Bild der Frage">
|
||||
<label for="reset_image">
|
||||
<input type="checkbox" name="reset_ques_image" id="reset_ques_image">
|
||||
Bild zurücksetzen
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
{% if image %}
|
||||
<img src="{{ image.image.url }}" style="max-width: 500px;" alt="Bild der Frage">
|
||||
<img src="{{ image.image.url }}" style="max-width: 100px;" alt="Bild der Frage">
|
||||
<label for="reset_image">
|
||||
<input type="checkbox" name="reset_ques_image" id="reset_ques_image">
|
||||
Bild zurücksetzen
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
|
||||
{% if image %}
|
||||
<img src="{{ image.image.url }}" style="max-width: 500px;" alt="Bild der Frage">
|
||||
<img src="{{ image.image.url }}" style="max-width: 100px;" alt="Bild der Frage">
|
||||
<label for="reset_image">
|
||||
<input type="checkbox" name="reset_ques_image" id="reset_ques_image">
|
||||
Bild zurücksetzen
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
{% if question_image %}
|
||||
<div class="flex justify-center">
|
||||
<img class="m-4" src="{{ question_image.image.url }}" style="max-width: 500px;" alt="Bild der Frage">
|
||||
<img class="m-4 w-full max-w-[500px] " src="{{ question_image.image.url }}" alt="Bild der Frage">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
|
||||
{% block extra_js %}
|
||||
{% include 'play/game/common_scripts.html' %}
|
||||
{% include 'play/game/sound.html' %}
|
||||
<script>
|
||||
const joinCode = '{{ quiz_game.join_code }}';
|
||||
const hostId = '{{ host_id }}';
|
||||
|
||||
@@ -24,32 +24,120 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-6">
|
||||
<h2 class="text-xl font-bold mb-4">Punktestand</h2>
|
||||
<div id="scoreboard" class="space-y-2">
|
||||
|
||||
<div id="scoreboard" class="leading-relaxed">
|
||||
{% for participant in participants %}
|
||||
{% if participant == my_participant or is_host %}
|
||||
<div class="p-4 bg-gray-100 rounded-lg flex justify-between items-center border {% if forloop.counter <= 3 %}border-yellow-500{% else %}border-transparent{% endif %}">
|
||||
<div>
|
||||
<p class="text-lg font-bold">
|
||||
<div class="mt-2 shadow-sm p-4 bg-gray-100 rounded-lg border-2 border {% if forloop.counter <= 3 %}border-yellow-500{% else %}border-transparent{% endif %}">
|
||||
<div class="flex flex-col items-start gap-1">
|
||||
<div class="text-lg font-bold flex items-center gap-2">
|
||||
{% if forloop.counter == 1 %}
|
||||
🥇
|
||||
{% elif forloop.counter == 2 %}
|
||||
🥈
|
||||
{% elif forloop.counter == 3 %}
|
||||
🥉
|
||||
{% else %}
|
||||
{{ forloop.counter }}.
|
||||
{% endif %}
|
||||
<span class="display_name text-gray-600 font-black text-2xl " id="display_name-{{ forloop.counter0 }}">{{ participant.display_name }}</span>
|
||||
</div class="flex flex-col items-start">
|
||||
|
||||
<p data-score="{{ participant.score }}" data-last-score="{{ participant.last_score }}" class="text-gray-600 font-bold show-score" id="score-{{ forloop.counter0 }}">{{ participant.score }} Punkte</p>
|
||||
{% if participant == my_participant or request.session.mode == "learn"%}
|
||||
<p class="text-blue-600 font-bold text-sm">+{{ participant.last_score }} Punkte</p>
|
||||
{% endif %}
|
||||
{{ participant.display_name }}
|
||||
</p>
|
||||
<p data-last-score="{{ participant.last_score }}" class="text-gray-600" id="score-{{ forloop.counter0 }}">{{ participant.score }} Punkte</p>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
async function start() {
|
||||
|
||||
elements=document.getElementsByClassName("show-score");
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const el = elements[i];
|
||||
|
||||
let counter=Number(el.getAttribute('data-score'))-Number(el.getAttribute('data-last-score'));
|
||||
let goal=Number(el.getAttribute('data-score'));
|
||||
let interval= setInterval(function(){
|
||||
el.innerHTML = counter + " Punkte";
|
||||
|
||||
|
||||
if(counter<=goal){
|
||||
el.innerHTML = counter + " Punkte";
|
||||
counter+=8;
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
el.innerHTML = goal + " Punkte";
|
||||
clearInterval(interval);
|
||||
}
|
||||
|
||||
}, 20);}}
|
||||
|
||||
function bounce(){
|
||||
|
||||
const names = document.getElementsByClassName("display_name");
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
names[i].classList.add("animate-bounce");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{% if is_last_question %}
|
||||
bounce();
|
||||
|
||||
{% else %}
|
||||
start();
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
{% if participant == my_participant or request.session.mode == "learn" %}
|
||||
{% if participant.last_answer_correct %}
|
||||
<span class="text-green-500">✓</span>
|
||||
|
||||
<div class="mt-1 flex justify-center border-t-2 border-gray-300 gap-1">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="pt-2 text-center text-green-500 font-black size-10">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
|
||||
|
||||
<div class=" pt-3 text-center text-gray-600 font-bold" id="antwort-richtig">
|
||||
Hier erscheint gleich dein Feedback...
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% elif participant.last_answer_correct == False %}
|
||||
<span class="text-red-500">✗</span>
|
||||
|
||||
|
||||
<div class="mt-1 flex justify-center border-t-2 border-gray-300 gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="pt-2 text-center text-red-500 font-black size-10">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
|
||||
|
||||
<div class=" pt-3 text-center text-gray-600 font-bold" id="antwort-falsch">
|
||||
Hier erscheint gleich dein Feedback...
|
||||
</div>
|
||||
<div class="flex justify-center border-t-2 border-gray-500 gap-1">
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -57,11 +145,11 @@
|
||||
|
||||
{% if is_host %}
|
||||
{% if is_last_question %}
|
||||
<button id="finish-button" class="w-full p-3 rounded-full bg-blue-600 text-white font-bold hover:bg-blue-700 transition-colors duration-200">
|
||||
<button id="finish-button" class="mt-4 w-full p-3 rounded-full bg-blue-600 text-white font-bold hover:bg-blue-700 transition-colors duration-200">
|
||||
Quiz beenden
|
||||
</button>
|
||||
{% else %}
|
||||
<button id="next-button" class="w-full p-3 rounded-full bg-blue-600 text-white font-bold hover:bg-blue-700 transition-colors duration-200">
|
||||
<button id="next-button" class="mt-4 w-full p-3 rounded-full bg-blue-600 text-white font-bold hover:bg-blue-700 transition-colors duration-200">
|
||||
Nächste Frage
|
||||
</button>
|
||||
{% endif %}
|
||||
@@ -95,7 +183,8 @@
|
||||
});
|
||||
|
||||
// Warten bevor Animation startet
|
||||
await sleep(1500); // <<< hier die 5 Sekunden Pause
|
||||
await sleep(1500); // <<< hier die Pause
|
||||
|
||||
|
||||
// Transition-Eigenschaft setzen nach Render-Zyklus
|
||||
requestAnimationFrame(() => {
|
||||
@@ -111,6 +200,7 @@
|
||||
|
||||
if (index === top3.length - 1) {
|
||||
triggerConfetti();
|
||||
|
||||
}
|
||||
}, index * 1500); // Zeitversetzt aufdecken
|
||||
});
|
||||
@@ -128,7 +218,78 @@
|
||||
</script>
|
||||
{% endif %}
|
||||
{% include 'play/game/common_scripts.html' %}
|
||||
{% if is_host %}
|
||||
{% include 'play/game/sound.html' %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
const richtigeAntworten = [
|
||||
"Deine Antwort ist nicht falsch. Sie ist richtig!",
|
||||
"Ja, du kannst es!",
|
||||
"So kann es weiter gehen.",
|
||||
"Ja, deine Antwort ist korrekt. Was soll ich dazu noch sagen? SUPER!",
|
||||
"SUPER GUT.",
|
||||
"Volltreffer!",
|
||||
"Bravo! Das sitzt!",
|
||||
"Na bitte, geht doch!",
|
||||
"Schön. Einfach schön.",
|
||||
"Exzellent kombiniert, Sherlock!",
|
||||
"Punkt für dich!",
|
||||
"Das war meisterhaft.",
|
||||
"Korrekt - du scheinst das zu können!",
|
||||
"So sieht Erfolg aus!",
|
||||
"Glänzend gelöst!",
|
||||
"Das war keine Glückssache - das war Können!",
|
||||
"Treffer, versenkt!",
|
||||
"Du hast es drauf!",
|
||||
"Da merkt man: Profi am Werk.",
|
||||
"Richtige Antwort - dafür gibt es viele Punkte!",
|
||||
"Weiter so!",
|
||||
"Wow - das war richtig gut!"
|
||||
];
|
||||
|
||||
const falscheAntworten = [
|
||||
"Oops! War wohl nicht dein Tag.",
|
||||
"Knapp daneben ist auch vorbei!",
|
||||
"Das war... kreativ!",
|
||||
"Wenn man schon alles wüsste, wäre das Quiz doch sinnlos.",
|
||||
"Deine Antwort war einzigartig - leider falsch!",
|
||||
"Na gut, wenigstens hast du es probiert.",
|
||||
"Das war eine interessante Theorie...",
|
||||
"Ein Genie irrt sich auch mal.",
|
||||
"Eine gute Antwort - aber nicht auf diese Frage.",
|
||||
"Das ist leider falsch. Mehr gibt es nicht zu sagen.",
|
||||
"Gute Idee! Aber leider nicht richtig.",
|
||||
"Oh weh! Ein Fehler. Naja, einer ist keiner.",
|
||||
"Die Antwort klang richtig, sie ist es aber nicht.",
|
||||
"Interessanter Ansatz. Nicht hilfreich, aber interessant.",
|
||||
"Nenn das lieber „alternative Fakten“.",
|
||||
"Du hast die richtige Antwort doch gewusst: 'Die Seite hat nur nicht geladen.'",
|
||||
"Jeder fängt mal klein an!",
|
||||
"Kopf hoch - weiter geht's!",
|
||||
"Nobody is perfect!",
|
||||
"Du schaffst das - vielleicht ab jetzt.",
|
||||
"Falsche Antwort, aber guter Wille!",
|
||||
"Übung macht den Quiz-Meister.",
|
||||
"Beim nächsten Mal triffst du!",
|
||||
"Das war nur zum Aufwärmen, oder?",
|
||||
"Kein Problem - weiter geht's.",
|
||||
"Gute Frage - aber falsche Antwort."
|
||||
];
|
||||
|
||||
|
||||
const antwortRichtig = document.getElementById("antwort-richtig");
|
||||
const antwortFalsch = document.getElementById("antwort-falsch");
|
||||
|
||||
if (antwortRichtig) {
|
||||
antwortRichtig.textContent = richtigeAntworten[Math.floor(Math.random() * richtigeAntworten.length)];
|
||||
} else if (antwortFalsch) {
|
||||
antwortFalsch.textContent = falscheAntworten[Math.floor(Math.random() * falscheAntworten.length)];
|
||||
}
|
||||
const joinCode = '{{ quiz_game.join_code }}';
|
||||
const hostId = '{{ host_id }}';
|
||||
const wsScheme = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
|
||||
29
django/templates/play/game/sound.html
Normal file
29
django/templates/play/game/sound.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% load static %}
|
||||
|
||||
|
||||
<audio id="bg-music" src="{% static 'mp3/Hintergrundmusik.mp3' %}" autoplay loop ></audio>
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const audio = document.getElementById("bg-music");
|
||||
const savedTime = localStorage.getItem("bg-music-time");
|
||||
|
||||
if (savedTime) {
|
||||
audio.addEventListener("loadedmetadata", () => {
|
||||
audio.currentTime = parseFloat(savedTime);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("click", () => {
|
||||
audio.play().catch(e => console.warn("Autoplay blockiert:", e));
|
||||
}, { once: true });
|
||||
|
||||
setInterval(() => {
|
||||
if (!audio.paused) {
|
||||
localStorage.setItem("bg-music-time", audio.currentTime);
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -8,12 +8,17 @@
|
||||
<div class="text-center mb-8">
|
||||
<h3 class="text-xl text-gray-600 mb-2">{{ quiz.name }}</h3>
|
||||
{% if request.session.mode != "learn" %}
|
||||
{% if host_id %}
|
||||
<div class="bg-blue-100 rounded-lg p-4 mb-4">
|
||||
<h1 class="text-4xl font-bold text-blue-800">{{ quiz_game.join_code }}</h1>
|
||||
<div class="w-full flex justify-center my-2">
|
||||
<img src="data:image/png;base64,{{ qr_code_base64 }}" class="h-1/2 w-1/2" alt="QR-Code">
|
||||
</div>
|
||||
<p class="text-sm text-blue-600 mt-1">Spiel-Code</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Participant Info -->
|
||||
{% if participant %}
|
||||
|
||||
@@ -7,3 +7,5 @@ whitenoise~=6.6.0 # Static file serving
|
||||
django-cleanup
|
||||
redis
|
||||
channels_redis
|
||||
python-dotenv # Production server safety
|
||||
qrcode~=7.4.2
|
||||
Reference in New Issue
Block a user