diff --git a/Archive.zip b/Archive.zip new file mode 100644 index 0000000..4f9ca4f Binary files /dev/null and b/Archive.zip differ diff --git a/sys_digger/beschreibung.md b/sys_digger/beschreibung.md new file mode 100644 index 0000000..542d656 --- /dev/null +++ b/sys_digger/beschreibung.md @@ -0,0 +1,33 @@ +# Sys Digger – Spielbeschreibung + +## Was ist Sys Digger? + +Sys Digger ist ein browserbasiertes Reaktionsspiel, das lose auf dem bekannten Arcade-Klassiker **Whac-a-Mole** basiert. Statt Maulwürfe zu treffen, geht es hier jedoch um Chemie und Moleküle: Der Spieler muss ein zentrales Atom davor schützen, zu einem gefährlichen **Cystinstein** heranzuwachsen. + +## Spielprinzip + +Im Zentrum des Bildschirms befindet sich ein Molekül mit mehreren **Ankerpunkten** um sich herum. Von den Rändern des Bildschirms nähern sich fremde Atome und versuchen, an diesen Ankerpunkten anzudocken. Gelingt eine Verbindung, wächst das Molekül ein Stück weiter in Richtung Cystinstein. + +Der Spieler muss dies verhindern – und zwar durch gezieltes Klicken auf den leuchtend roten Ankerpunkt, bevor das anfliegende Atom dort andockt. + +## Steuerung + +- **Mausklick** auf einen rot markierten Ankerpunkt, sobald sich ein Atom nähert. +- Trifft der Klick rechtzeitig, wird die Verbindung blockiert und der Spieler erhält einen **Punkt**. +- Kommt der Klick zu spät oder wird der Ankerpunkt verfehlt, dockt das Atom an. + +## Spielverlauf + +- Je länger das Spiel andauert, desto schneller erscheinen neue Atome – die Schwierigkeit steigt kontinuierlich. +- In der **oberen linken Ecke** befindet sich ein kleines Panel, das den aktuellen Zustand des Cystinsteins zeigt. Mit jeder erfolgreichen Andockung wächst dort ein neues Atom hinzu. +- Wurden **6 Andockungen** nicht verhindert, ist der Cystinstein vollständig gebildet – das Spiel ist verloren. + +## Highscore + +Nach jedem Spiel kann der Spieler seinen Namen eingeben und seinen Punktestand in der **Bestenliste** speichern. Die Top-10-Ergebnisse werden dauerhaft gespeichert und sind beim nächsten Besuch wieder abrufbar. + +Bleibt der Startbildschirm 30 Sekunden unberührt, wechselt das Spiel automatisch in eine **Highscore-Ansicht**, die die aktuelle Bestenliste anzeigt. Ein Klick oder ein Tastendruck bringt den Startbildschirm zurück. + +## Ziel + +So viele Andockversuche wie möglich abwehren, den Highscore knacken – und verhindern, dass der Cystinstein entsteht. diff --git a/thiola-pong/constants.js b/thiola-pong/constants.js new file mode 100644 index 0000000..84564d5 --- /dev/null +++ b/thiola-pong/constants.js @@ -0,0 +1,57 @@ +const PADDLE_W = 12; +const PADDLE_H = 90; +const PADDLE_MARGIN = 42; +const BALL_R = 7; +const BASE_SPEED = 5; +const WIN_SCORE = 5; +const MAX_LEVEL = 3; +const WALL_WIDTH = 32; + +const LEVEL_COVERAGE = [0, 0, 40, 70]; +const LEVEL_CPU_SPEED = [0, 1.6, 3.0, 3.8]; + +const playerGoalMessages = [ + { icon: '🔬', title: 'Steinanalyse rettet Leben', + text: 'Jeder Nierenstein sollte analysiert werden. Nur so kann ein Cystinstein sicher identifiziert werden.' }, + { icon: '🎯', title: 'Früherkennung ist der Schlüssel', + text: 'Je früher Cystinurie erkannt wird, desto besser die Prognose. Die Steinanalyse ist der erste Schritt zur Diagnose.' }, + { icon: '💊', title: 'Thiola senkt das Cystinniveau', + text: 'Thiola (Tiopronin) senkt die Cystinkonzentration im Urin und verhindert die Neubildung von Steinen.' }, + { icon: '🛡️', title: 'Schutz durch konsequente Therapie', + text: 'Unter konsequenter Thiola-Therapie kann die Steinbildungsrate signifikant reduziert werden.' }, + { icon: '✅', title: 'Evidenzbasierter Therapiestandard', + text: 'Tiopronin (Thiola) ist der Goldstandard in der medikamentösen Prävention von Cystinsteinen.' }, + { icon: '🔁', title: 'Rezidivprophylaxe mit Thiola', + text: 'Ohne spezifische Therapie liegt die Rezidivrate bei Cystinsteinen bei bis zu 60%. Thiola kann das verhindern.' }, + { icon: '📋', title: 'Leitliniengerecht handeln', + text: 'Die urologischen Leitlinien empfehlen eine konsequente Steinanalyse als Basis jeder Metaphylaxe.' }, + { icon: '🏆', title: 'Therapieerfolg messbar', + text: 'Unter Thiola-Therapie kann die Cystinausscheidung im Urin kontrolliert und der Therapieerfolg laborchemisch überwacht werden.' }, + { icon: '💎', title: 'Analyse schafft Klarheit', + text: 'Cystinsteine machen nur 1–2% aller Nierensteine aus — ohne Analyse bleiben sie unerkannt.' }, + { icon: '🔑', title: 'Der Schlüssel zur Prävention', + text: 'Steinanalyse → Diagnose → Thiola — dieser Dreiklang schützt Patienten vor neuen Steinen.' }, +]; + +const cpuGoalMessages = [ + { icon: '⚡', title: 'Kolikschmerzen — unerträglich', + text: 'Nierenkoliken gehören zu den stärksten Schmerzen, die ein Mensch erleben kann. Cystinstein-Patienten erleben das immer wieder.' }, + { icon: '🔄', title: 'Rezidive ohne Ende', + text: 'Ohne gezielte Therapie bilden sich Cystinsteine immer wieder neu. Bis zu 60% Rezidivrate — ein Teufelskreis für Betroffene.' }, + { icon: '🏥', title: 'Wiederholte Operationen', + text: 'Viele Cystinurie-Patienten müssen mehrfach operiert werden. Jeder Eingriff belastet die Niere zusätzlich.' }, + { icon: '⏳', title: 'Diagnose oft viel zu spät', + text: 'Ohne Steinanalyse dauert es oft Jahre bis zur richtigen Diagnose. Wertvolle Zeit, in der die Niere Schaden nimmt.' }, + { icon: '🫘', title: 'Nierenfunktion in Gefahr', + text: 'Wiederholte Steinereignisse können zum Verlust der Nierenfunktion führen — besonders bei jungen Patienten.' }, + { icon: '👶', title: 'Junge Patienten betroffen', + text: 'Cystinurie manifestiert sich oft schon im Kindes- und Jugendalter. Betroffene leiden ihr ganzes Leben.' }, + { icon: '🧬', title: 'Genetisch bedingt — lebenslang', + text: 'Cystinurie ist eine autosomal-rezessiv vererbte Erkrankung. Die Steinbildung hört ohne Therapie nie auf.' }, + { icon: '😔', title: 'Lebensqualität massiv eingeschränkt', + text: 'Ständige Angst vor der nächsten Kolik, Krankenhausaufenthalte, OPs — die Lebensqualität sinkt drastisch.' }, + { icon: '❓', title: 'Fehlende Steinanalyse = Blindflug', + text: 'Ohne Steinanalyse wird symptomatisch statt kausal behandelt. Die wahre Ursache bleibt im Dunkeln.' }, + { icon: '📉', title: 'Kosten für das Gesundheitssystem', + text: 'Wiederholte Notaufnahmen, OPs und Krankheitstage — unerkannte Cystinurie verursacht immense Folgekosten.' }, +]; diff --git a/thiola-pong/game.js b/thiola-pong/game.js new file mode 100644 index 0000000..03eb558 --- /dev/null +++ b/thiola-pong/game.js @@ -0,0 +1,451 @@ +(() => { + const canvas = document.getElementById('canvas'); + const ctx = canvas.getContext('2d'); + const W = canvas.width, H = canvas.height; + + // DOM + const overlay = document.getElementById('overlay'); + const startBtnCpu = document.getElementById('start-btn-cpu'); + const startBtnPvp = document.getElementById('start-btn-pvp'); + const hudLevel = document.getElementById('hud-level'); + const hudShield = document.getElementById('hud-shield'); + const hudPScore = document.getElementById('hud-pscore'); + const hudCScore = document.getElementById('hud-cscore'); + const popup = document.getElementById('popup'); + const popupTitle = document.getElementById('popup-title'); + const popupContent = document.getElementById('popup-content'); + const popupSub = document.getElementById('popup-sub'); + const popupBtn = document.getElementById('popup-btn'); + const goalFlash = document.getElementById('goal-flash'); + const flashScore = document.getElementById('flash-score'); + const flashIcon = document.getElementById('flash-icon'); + const flashTitle = document.getElementById('flash-title'); + const flashText = document.getElementById('flash-text'); + + // ─── Game state ─── + let level = 1, playerScore = 0, cpuScore = 0; + let running = false, paused = false; + let gameMode = 'cpu'; // 'cpu' or 'pvp' + + let ballX, ballY, ballVX, ballVY; + let playerY = H/2 - PADDLE_H/2, cpuY = H/2 - PADDLE_H/2; + let cpuSpeed = 1.6; + let coveragePercent = 0, gapTop, gapBottom; + let keyW = false, keyS = false; + let keyUp = false, keyDown = false, mouseY = null; + let particles = [], trails = []; + let glowPhase = 0; + + let playerMsgIndex = 0, cpuMsgIndex = 0; + + // ─── Level config ─── + function getCoverage(lvl) { const v = LEVEL_COVERAGE[lvl]; return v !== undefined ? v : 70; } + function getCpuSpeed(lvl) { const v = LEVEL_CPU_SPEED[lvl]; return v !== undefined ? v : 3.8; } + + // ─── Helpers ─── + function calcGap() { + const openH = H * ((100 - coveragePercent) / 100); + gapTop = (H - openH) / 2; + gapBottom = gapTop + openH; + } + + function resetBall(dir) { + ballX = W / 2; ballY = H / 2; + const angle = (Math.random() * 0.7 - 0.35); + const speed = BASE_SPEED + level * 0.2; + ballVX = speed * dir; + ballVY = speed * Math.sin(angle); + } + + function spawnParticles(x, y, color, count) { + for (let i = 0; i < count; i++) { + particles.push({ x, y, vx: (Math.random()-.5)*7, vy: (Math.random()-.5)*7, life: 1, decay: 0.02+Math.random()*0.03, r: 2+Math.random()*3, color }); + } + } + + function updateHUD() { + hudLevel.textContent = level; + hudShield.textContent = Math.round(coveragePercent); + hudPScore.textContent = playerScore; + hudCScore.textContent = cpuScore; + } + + function startLevel() { + playerScore = 0; cpuScore = 0; + coveragePercent = getCoverage(level); + cpuSpeed = getCpuSpeed(level); + calcGap(); resetBall(1); updateHUD(); + } + + // ─── Score a goal ─── + function scoreGoal(isPlayerGoal) { + if (isPlayerGoal) playerScore++; + else cpuScore++; + updateHUD(); + void hudPScore.offsetHeight; + showGoalFlash(isPlayerGoal); + } + + // ─── Goal flash (edu per goal) ─── + function showGoalFlash(isPlayerGoal) { + paused = true; + + let msg; + if (isPlayerGoal) { + msg = playerGoalMessages[playerMsgIndex % playerGoalMessages.length]; + playerMsgIndex++; + } else { + msg = cpuGoalMessages[cpuMsgIndex % cpuGoalMessages.length]; + cpuMsgIndex++; + } + + flashScore.textContent = `${playerScore} : ${cpuScore}`; + flashIcon.textContent = msg.icon; + flashTitle.textContent = msg.title; + flashText.innerHTML = msg.text; + void goalFlash.offsetHeight; + goalFlash.className = isPlayerGoal ? 'show player-goal' : 'show cpu-goal'; + + function cont(e) { + if (e && e.type === 'keydown') { + const k = e.key; + if (k === 'ArrowUp' || k === 'ArrowDown' || k === 'w' || k === 'W' || k === 's' || k === 'S') return; + } + goalFlash.className = ''; + document.removeEventListener('keydown', cont); + goalFlash.removeEventListener('click', cont); + if (playerScore >= WIN_SCORE) { advanceLevel(); } + else if (cpuScore >= WIN_SCORE) { showGameOver(); } + else { paused = false; resetBall(isPlayerGoal ? -1 : 1); } + } + + setTimeout(() => { + document.addEventListener('keydown', cont); + goalFlash.addEventListener('click', cont); + }, 400); + } + + // ─── Popup helper ─── + function showPopup(title, html, sub, btnText, btnClass, onContinue) { + popupTitle.textContent = title; + popupContent.innerHTML = html; + popupSub.textContent = sub || ''; + popupBtn.textContent = btnText; + popupBtn.className = btnClass || 'game-btn'; + popup.classList.add('show'); + paused = true; + popupBtn.onclick = () => { + popup.classList.remove('show'); + paused = false; + popupBtn.blur(); + if (onContinue) onContinue(); + }; + } + + // ─── Start screen ─── + function showStartScreen() { + running = false; + paused = false; + level = 1; + coveragePercent = 0; + popup.classList.remove('show'); + overlay.classList.remove('hidden'); + } + + // ─── Level transitions ─── + function advanceLevel() { + if (level === 1) { + level = 2; + showPopup('LEVEL 1 GESCHAFFT!', + `
+ 🛡️ +
Verstärkung kommt: Thiola!
+
+ Gut gespielt! Doch im nächsten Level wird der Gegner stärker.

+ Zum Glück bekommst du jetzt Unterstützung von + ✦ THIOLA ✦

+ Thiola-Wände schützen Teile deines Tors und blockieren eingehende Bälle — + genau wie Thiola (Tiopronin) die Neubildung von Cystinsteinen verhindert. +
+
`, + 'Level 2 — 40% Thiola-Schutz', + 'MIT THIOLA WEITERSPIELEN', 'game-btn thiola', + () => startLevel() + ); + } else if (level === 2) { + level = 3; + showPopup('LEVEL 2 GESCHAFFT!', + `
+ 💊 +
Thiola verstärkt den Schutz!
+
+ Der Gegner wird noch aggressiver — aber Thiola auch!

+ Im finalen Level schützt Thiola 70% deines Tors.
+ Konsequente Therapie = maximaler Schutz. +
+
`, + 'Finales Level — 70% Thiola-Schutz', + 'THIOLA-SCHUTZ MAXIMIEREN', 'game-btn thiola', + () => startLevel() + ); + } else { + showFinalScreen(); + } + } + + function showFinalScreen() { + showPopup('🏆 GEWONNEN!', + `
+
ALLE 3 LEVEL GEMEISTERT!
+
+ Du hast erlebt, wie Thiola dein Tor Schritt für Schritt geschützt hat — + genau so schützt Thiola (Tiopronin) Cystinurie-Patienten + vor der Neubildung von Cystinsteinen. +
+
+ 🔬 Steinanalyse + + 🎯 Diagnose + + 💊 Thiola +
+
+ Jeder Stein verdient eine Analyse.
+ Denn nur wer Cystinsteine erkennt, kann sie gezielt verhindern. +
+
`, + '', 'NOCHMAL SPIELEN', 'game-btn', + () => showStartScreen() + ); + } + + function showGameOver() { + showPopup(`LEVEL ${level} VERLOREN`, + `
+ +
Nicht aufgeben!
+
+ Auch bei Cystinurie gilt: konsequente Therapie ist der Schlüssel.
+ Versuche es erneut — mit Thiola an deiner Seite! +
+
`, + 'Zurück zum Start', 'NOCHMAL VERSUCHEN', 'game-btn', + () => showStartScreen() + ); + } + + // ─── Input ─── + document.addEventListener('keydown', e => { + if (e.key === 'ArrowUp') keyUp = true; + if (e.key === 'ArrowDown') keyDown = true; + if (e.key === 'w' || e.key === 'W') keyW = true; + if (e.key === 's' || e.key === 'S') keyS = true; + }); + document.addEventListener('keyup', e => { + if (e.key === 'ArrowUp') keyUp = false; + if (e.key === 'ArrowDown') keyDown = false; + if (e.key === 'w' || e.key === 'W') keyW = false; + if (e.key === 's' || e.key === 'S') keyS = false; + }); + canvas.addEventListener('mousemove', e => { + const rect = canvas.getBoundingClientRect(); + mouseY = (e.clientY - rect.top) * (H / rect.height); + }); + canvas.addEventListener('mouseleave', () => { mouseY = null; }); + + // ─── Update ─── + function update() { + if (!running || paused) return; + glowPhase += 0.03; + + // Player 1 (left) — PvP: W/S only; CPU mode: mouse OR W/S + if (gameMode === 'pvp') { + if (keyW) playerY -= 6; + if (keyS) playerY += 6; + } else { + if (mouseY !== null) { + playerY += (mouseY - PADDLE_H/2 - playerY) * 0.15; + } else { + if (keyUp || keyW) playerY -= 6; + if (keyDown || keyS) playerY += 6; + } + } + playerY = Math.max(0, Math.min(H - PADDLE_H, playerY)); + + // Player 2 / CPU (right) + if (gameMode === 'pvp') { + if (keyUp) cpuY -= 6; + if (keyDown) cpuY += 6; + } else { + const diff = ballY - (cpuY + PADDLE_H/2); + if (Math.abs(diff) > 10) cpuY += Math.sign(diff) * Math.min(cpuSpeed, Math.abs(diff) * 0.07); + } + cpuY = Math.max(0, Math.min(H - PADDLE_H, cpuY)); + + // Ball movement + ballX += ballVX; ballY += ballVY; + if (ballY - BALL_R <= 0) { ballY = BALL_R; ballVY = Math.abs(ballVY); } + if (ballY + BALL_R >= H) { ballY = H - BALL_R; ballVY = -Math.abs(ballVY); } + trails.push({ x: ballX, y: ballY, life: 1 }); + + // Right (CPU) side + if (ballX + BALL_R >= W - PADDLE_MARGIN - PADDLE_W) { + if (ballX + BALL_R <= W - PADDLE_MARGIN + PADDLE_W && ballY >= cpuY && ballY <= cpuY + PADDLE_H) { + ballVX = -Math.abs(ballVX) * 1.03; + ballVY += ((ballY - cpuY)/PADDLE_H - 0.5) * 3; + ballX = W - PADDLE_MARGIN - PADDLE_W - BALL_R; + spawnParticles(ballX, ballY, '#ff6b35', 8); + } else if (ballX > W + BALL_R) { + spawnParticles(W, ballY, '#4ecdc4', 20); + scoreGoal(true); + } + } + + // Left (Player) side + Thiola wall + if (ballX - BALL_R <= PADDLE_MARGIN + PADDLE_W) { + if (ballX - BALL_R >= PADDLE_MARGIN - PADDLE_W && ballY >= playerY && ballY <= playerY + PADDLE_H) { + ballVX = Math.abs(ballVX) * 1.03; + ballVY += ((ballY - playerY)/PADDLE_H - 0.5) * 3; + ballX = PADDLE_MARGIN + PADDLE_W + BALL_R; + spawnParticles(ballX, ballY, '#4ecdc4', 8); + } else if (ballX - BALL_R <= WALL_WIDTH && coveragePercent > 0) { + if (ballY < gapTop || ballY > gapBottom) { + ballVX = Math.abs(ballVX); + ballX = WALL_WIDTH + BALL_R; + spawnParticles(WALL_WIDTH, ballY, '#55efc4', 15); + } else if (ballX - BALL_R <= 0) { + spawnParticles(0, ballY, '#ff6b35', 20); + scoreGoal(false); + } + } else if (ballX - BALL_R <= 0 && coveragePercent === 0) { + spawnParticles(0, ballY, '#ff6b35', 20); + scoreGoal(false); + } + } + + // Clamp speed + const spd = Math.sqrt(ballVX*ballVX + ballVY*ballVY); + if (spd > 12) { ballVX = (ballVX/spd)*12; ballVY = (ballVY/spd)*12; } + + particles = particles.filter(p => { p.x+=p.vx; p.y+=p.vy; p.life-=p.decay; p.vx*=.96; p.vy*=.96; return p.life>0; }); + trails = trails.filter(t => { t.life -= 0.06; return t.life > 0; }); + } + + // ─── Draw ─── + function draw() { + ctx.clearRect(0, 0, W, H); + + // Background grid + ctx.strokeStyle = 'rgba(255,255,255,.02)'; ctx.lineWidth = 1; + for (let x = 0; x < W; x += 40) { ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,H); ctx.stroke(); } + for (let y = 0; y < H; y += 40) { ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(W,y); ctx.stroke(); } + + // Center line + ctx.setLineDash([8,12]); ctx.strokeStyle='rgba(255,255,255,.08)'; ctx.lineWidth=2; + ctx.beginPath(); ctx.moveTo(W/2,0); ctx.lineTo(W/2,H); ctx.stroke(); ctx.setLineDash([]); + + calcGap(); + + // Thiola walls + if (coveragePercent > 0) { + const pa = 0.7 + 0.3 * Math.sin(glowPhase); + if (gapTop > 0) { drawThiolaWall(0, 0, WALL_WIDTH, gapTop, pa); drawThiolaText(WALL_WIDTH/2, gapTop/2, gapTop); } + if (gapBottom < H) { drawThiolaWall(0, gapBottom, WALL_WIDTH, H-gapBottom, pa); drawThiolaText(WALL_WIDTH/2, gapBottom+(H-gapBottom)/2, H-gapBottom); } + ctx.fillStyle='#fff'; ctx.shadowColor='#55efc4'; ctx.shadowBlur=14; + ctx.fillRect(0, gapTop-3, WALL_WIDTH+6, 3); + ctx.fillRect(0, gapBottom, WALL_WIDTH+6, 3); + ctx.shadowBlur=0; + } + + // Paddles + const pG = ctx.createLinearGradient(PADDLE_MARGIN, 0, PADDLE_MARGIN+PADDLE_W, 0); + pG.addColorStop(0,'#3ab0a5'); pG.addColorStop(1,'#4ecdc4'); + ctx.fillStyle=pG; ctx.shadowColor='#4ecdc4'; ctx.shadowBlur=15; + roundRect(PADDLE_MARGIN, playerY, PADDLE_W, PADDLE_H, 4); ctx.shadowBlur=0; + + const cG = ctx.createLinearGradient(W-PADDLE_MARGIN-PADDLE_W, 0, W-PADDLE_MARGIN, 0); + cG.addColorStop(0,'#e85d2a'); cG.addColorStop(1,'#ff6b35'); + ctx.fillStyle=cG; ctx.shadowColor='#ff6b35'; ctx.shadowBlur=15; + roundRect(W-PADDLE_MARGIN-PADDLE_W, cpuY, PADDLE_W, PADDLE_H, 4); ctx.shadowBlur=0; + + // Ball trail + trails.forEach(t => { + ctx.beginPath(); ctx.arc(t.x, t.y, BALL_R*t.life*0.6, 0, Math.PI*2); + ctx.fillStyle=`rgba(255,255,255,${t.life*0.12})`; ctx.fill(); + }); + + // Ball + ctx.beginPath(); ctx.arc(ballX, ballY, BALL_R, 0, Math.PI*2); + ctx.fillStyle='#fff'; ctx.shadowColor='#fff'; ctx.shadowBlur=20; ctx.fill(); ctx.shadowBlur=0; + + // Particles + particles.forEach(p => { + ctx.beginPath(); ctx.arc(p.x, p.y, p.r*p.life, 0, Math.PI*2); + ctx.fillStyle=p.color; ctx.globalAlpha=p.life; ctx.fill(); ctx.globalAlpha=1; + }); + + // Big score overlay + ctx.font='72px "Press Start 2P", monospace'; + ctx.textAlign='center'; ctx.textBaseline='middle'; + ctx.fillStyle='rgba(78,205,196,.2)'; ctx.shadowColor='rgba(78,205,196,.08)'; ctx.shadowBlur=30; + ctx.fillText(playerScore, W*0.3, H/2); ctx.shadowBlur=0; + ctx.fillStyle='rgba(255,107,53,.2)'; ctx.shadowColor='rgba(255,107,53,.08)'; ctx.shadowBlur=30; + ctx.fillText(cpuScore, W*0.7, H/2); ctx.shadowBlur=0; + ctx.textBaseline='alphabetic'; + } + + function drawThiolaWall(x, y, w, h, pulseAlpha) { + const g = ctx.createLinearGradient(x, 0, x+w, 0); + g.addColorStop(0, `rgba(0,184,148,${0.9*pulseAlpha})`); + g.addColorStop(0.5, `rgba(85,239,196,${0.7*pulseAlpha})`); + g.addColorStop(1, `rgba(78,205,196,${0.35*pulseAlpha})`); + ctx.fillStyle=g; ctx.fillRect(x, y, w, h); + ctx.fillStyle=`rgba(255,255,255,${0.06*pulseAlpha})`; ctx.fillRect(x+w-3, y, 3, h); + } + + function drawThiolaText(cx, cy, segH) { + if (segH < 50) return; + ctx.save(); ctx.translate(cx, cy); ctx.rotate(-Math.PI/2); + const fs = Math.min(18, segH * 0.14); + ctx.font = `900 ${fs}px 'Orbitron', monospace`; + ctx.textAlign='center'; ctx.textBaseline='middle'; + ctx.fillStyle='rgba(0,0,0,.35)'; ctx.fillText('THIOLA', 1, 1); + ctx.fillStyle='rgba(255,255,255,.93)'; ctx.shadowColor='rgba(85,239,196,.8)'; ctx.shadowBlur=12; + ctx.fillText('THIOLA', 0, 0); ctx.shadowBlur=0; + if (segH > 170) { + ctx.fillStyle='rgba(255,255,255,.45)'; + const off = segH * 0.3; + ctx.fillText('THIOLA', -off, 0); ctx.fillText('THIOLA', off, 0); + } + ctx.restore(); + } + + function roundRect(x, y, w, h, r) { + ctx.beginPath(); ctx.moveTo(x+r, y); ctx.lineTo(x+w-r, y); + ctx.quadraticCurveTo(x+w, y, x+w, y+r); ctx.lineTo(x+w, y+h-r); + ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h); ctx.lineTo(x+r, y+h); + ctx.quadraticCurveTo(x, y+h, x, y+h-r); ctx.lineTo(x, y+r); + ctx.quadraticCurveTo(x, y, x+r, y); ctx.closePath(); ctx.fill(); + } + + // ─── Loop ─── + function loop() { update(); draw(); requestAnimationFrame(loop); } + + function startGame(mode) { + gameMode = mode; + document.getElementById('hud-plabel').textContent = mode === 'pvp' ? 'SP1' : 'SPIELER'; + document.getElementById('hud-clabel').textContent = mode === 'pvp' ? 'SP2' : 'CPU'; + overlay.classList.add('hidden'); + if (document.activeElement && document.activeElement.blur) { + document.activeElement.blur(); + } + running = true; level = 1; coveragePercent = 0; + playerMsgIndex = 0; cpuMsgIndex = 0; + startLevel(); + } + + startBtnCpu.addEventListener('click', () => startGame('cpu')); + startBtnPvp.addEventListener('click', () => startGame('pvp')); + + calcGap(); resetBall(1); draw(); loop(); +})(); diff --git a/thiola-pong/pong-thiola.html b/thiola-pong/pong-thiola.html new file mode 100644 index 0000000..3680522 --- /dev/null +++ b/thiola-pong/pong-thiola.html @@ -0,0 +1,217 @@ + + + + + +PONG – Thiola Edition + + + + +
+
+
LEVEL 1 / 3
+
THIOLA-SCHUTZ: 0%
+
+ SPIELER 0 + : + 0 CPU +
+
+ +
+ + +
+

PONG

+
THIOLA EDITION
+
+

+ Nierensteine treffen auf dein Tor — kannst du sie abwehren?
+ Gewinne 3 Level mit jeweils 5 Punkten Vorsprung.
+ Ab Level 2 schützt Thiola dein Tor! +

+
+
SPIELMODUS WÄHLEN
+
+ + +
+
+ SPIELER 1 (links): W / S oder Maus
+ SPIELER 2 (rechts): ↑ / ↓ Pfeiltasten +
+
+ + +
+
+
+ +
+
+
+
KLICKEN ODER TASTE DRÜCKEN
+
+ + + +
+ +
PONG · THIOLA EDITION
+
+ + + + + diff --git a/thiola-pong/thiola-pong-konzept.docx b/thiola-pong/thiola-pong-konzept.docx new file mode 100644 index 0000000..a63bb35 Binary files /dev/null and b/thiola-pong/thiola-pong-konzept.docx differ