Trainingsplan
/* Alle Styles sind strikt auf .tp-download-card-wrap und Kinder beschränkt - kein Einfluss auf die restliche Seite */
.tp-download-card-wrap { display: block; }
.tp-download-card-wrap *,
.tp-download-card-wrap *::before,
.tp-download-card-wrap *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Barlow', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
}
.tp-download-card-wrap .tp-card {
--tp-navy: rgb(21,33,69);
--tp-gold: rgb(246,195,70);
--tp-border: #e8ecf2;
--tp-text-body: #555;
--tp-text-muted: #888;
color: var(--tp-navy);
line-height: 1.6;
max-width: 820px;
margin: 0 auto;
background: #ffffff;
border: 1px solid var(--tp-border);
padding: 44px 48px;
position: relative;
box-shadow: 0 4px 20px rgba(21,33,69,0.05);
transition: border-color 0.3s, transform 0.3s, box-shadow 0.3s;
display: block;
}
.tp-download-card-wrap .tp-card:hover {
border-color: var(--tp-gold);
transform: translateY(-4px);
box-shadow: 0 12px 36px rgba(21,33,69,0.10);
}
.tp-download-card-wrap .tp-card::before,
.tp-download-card-wrap .tp-card::after {
content: '';
position: absolute;
width: 22px;
height: 22px;
pointer-events: none;
}
.tp-download-card-wrap .tp-card::before {
top: -1px; left: -1px;
border-top: 2px solid var(--tp-gold);
border-left: 2px solid var(--tp-gold);
}
.tp-download-card-wrap .tp-card::after {
bottom: -1px; right: -1px;
border-bottom: 2px solid var(--tp-gold);
border-right: 2px solid var(--tp-gold);
}
.tp-download-card-wrap .tp-content {
display: grid;
grid-template-columns: auto 1fr auto;
gap: 32px;
align-items: center;
}
.tp-download-card-wrap .tp-icon {
width: 88px;
height: 108px;
background: var(--tp-gold);
position: relative;
display: flex;
align-items: flex-end;
justify-content: center;
padding-bottom: 14px;
font-weight: 900;
color: var(--tp-navy);
font-size: 20px;
letter-spacing: 1.5px;
}
.tp-download-card-wrap .tp-icon::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 22px;
height: 22px;
background: #ffffff;
clip-path: polygon(0 0, 100% 100%, 100% 0);
}
.tp-download-card-wrap .tp-text { display: block; }
.tp-download-card-wrap .tp-label {
font-size: 12px;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--tp-text-muted);
font-weight: 700;
margin-bottom: 8px;
display: block;
}
.tp-download-card-wrap .tp-title {
font-size: 28px;
font-weight: 900;
color: var(--tp-navy);
line-height: 1.2;
letter-spacing: 0.3px;
margin-bottom: 8px;
display: block;
}
.tp-download-card-wrap .tp-subtitle {
font-size: 15px;
color: var(--tp-text-body);
line-height: 1.55;
display: block;
}
.tp-download-card-wrap .tp-btn {
background: var(--tp-navy);
color: var(--tp-gold);
text-decoration: none;
padding: 16px 30px;
font-weight: 900;
font-size: 14px;
letter-spacing: 2px;
text-transform: uppercase;
display: inline-flex;
align-items: center;
gap: 10px;
border: 2px solid var(--tp-navy);
transition: gap 0.3s, transform 0.3s, background 0.3s;
white-space: nowrap;
cursor: pointer;
}
.tp-download-card-wrap .tp-btn:hover {
gap: 14px;
transform: translateY(-3px);
background: #1a2a5a;
color: var(--tp-gold);
}
.tp-download-card-wrap .tp-arrow {
font-size: 18px;
transition: transform 0.3s;
display: inline-block;
}
.tp-download-card-wrap .tp-btn:hover .tp-arrow {
transform: translateY(3px);
}
@media (max-width: 720px) {
.tp-download-card-wrap .tp-card { padding: 36px 28px; }
.tp-download-card-wrap .tp-content {
grid-template-columns: 1fr;
text-align: center;
gap: 22px;
}
.tp-download-card-wrap .tp-icon { margin: 0 auto; }
.tp-download-card-wrap .tp-title { font-size: 22px; }
.tp-download-card-wrap .tp-btn { justify-content: center; }
}
PDF
Camp 2026
Download ↓
Aktueller Trainingsplan
Vollständige Übersicht aller Trainingseinheiten und Zeiten.
Typischer Tagesablauf
| Uhrzeit | Aktivität | Ort |
|---|---|---|
| 08:00-09:00 | Aufwärmen / Trockentraining | Kurbads, Rasen |
| 09:30-11:30 | Eis-Training (Skating, Technik) | Kurbads Eishalle |
| 11:30-13:00 | Mittagspause | Kurbads, Café |
| 13:00-14:30 | Theorie / Videoanalyse | Seminarraum |
| 15:00-17:00 | Eis-Training (Spiel, Taktik) | Kurbads Eishalle |
.hk-game-wrap { display: block; width: 100%; }
.hk-game-wrap *,
.hk-game-wrap *::before,
.hk-game-wrap *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Barlow', -apple-system, BlinkMacSystemFont, sans-serif;
}
.hk-game-wrap .hk-container {
max-width: 900px;
margin: 0 auto;
padding: 24px 20px;
user-select: none;
}
/* Score bar */
.hk-game-wrap .hk-scorebar {
display: flex;
align-items: stretch;
justify-content: space-between;
background: rgb(21,33,69);
color: #fff;
border-radius: 8px 8px 0 0;
padding: 14px 22px;
box-shadow: 0 4px 14px rgba(21,33,69,0.2);
}
.hk-game-wrap .hk-score-block {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.hk-game-wrap .hk-score-label {
font-size: 10px;
letter-spacing: 2.5px;
text-transform: uppercase;
font-weight: 700;
opacity: 0.7;
}
.hk-game-wrap .hk-score-block.you .hk-score-label { color: rgb(246,195,70); opacity: 1; }
.hk-game-wrap .hk-score-block.cpu .hk-score-label { color: #ff6b6b; opacity: 1; }
.hk-game-wrap .hk-score-num {
font-family: 'Barlow Condensed', sans-serif;
font-size: 40px;
font-weight: 900;
line-height: 1;
}
.hk-game-wrap .hk-score-divider {
color: rgb(246,195,70);
font-family: 'Barlow Condensed', sans-serif;
font-size: 36px;
font-weight: 900;
align-self: center;
opacity: 0.6;
}
.hk-game-wrap .hk-score-stat {
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
min-width: 70px;
}
.hk-game-wrap .hk-stat-label {
font-size: 9px;
letter-spacing: 1.5px;
text-transform: uppercase;
opacity: 0.6;
margin-bottom: 2px;
}
.hk-game-wrap .hk-stat-val {
font-family: 'Barlow Condensed', sans-serif;
font-size: 22px;
font-weight: 800;
color: rgb(246,195,70);
}
/* Rink */
.hk-game-wrap .hk-rink {
position: relative;
width: 100%;
aspect-ratio: 2 / 1;
background: linear-gradient(180deg, #ffffff 0%, #e8f0ff 100%);
border: 4px solid rgb(21,33,69);
border-top: none;
border-radius: 0 0 80px 80px / 0 0 60px 60px;
overflow: hidden;
cursor: none;
box-shadow: 0 12px 40px rgba(21,33,69,0.18), inset 0 0 60px rgba(180,210,255,0.4);
touch-action: none;
}
.hk-game-wrap .hk-rink::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(90deg, transparent, transparent 8px, rgba(180,210,255,0.15) 8px, rgba(180,210,255,0.15) 9px);
pointer-events: none;
}
/* lines */
.hk-game-wrap .hk-line-center {
position: absolute;
top: 0; bottom: 0; left: 50%;
width: 4px;
background: #d93636;
transform: translateX(-50%);
}
.hk-game-wrap .hk-line-blue {
position: absolute;
top: 0; bottom: 0;
width: 3px;
background: #2962d6;
}
.hk-game-wrap .hk-line-blue.l { left: 33%; }
.hk-game-wrap .hk-line-blue.r { left: 67%; }
/* center circle */
.hk-game-wrap .hk-circle-center {
position: absolute;
top: 50%; left: 50%;
width: 22%;
aspect-ratio: 1;
border: 2px solid #2962d6;
border-radius: 50%;
transform: translate(-50%, -50%);
}
.hk-game-wrap .hk-circle-center::after {
content: '';
position: absolute;
top: 50%; left: 50%;
width: 12px; height: 12px;
background: #2962d6;
border-radius: 50%;
transform: translate(-50%, -50%);
}
/* faceoff */
.hk-game-wrap .hk-faceoff {
position: absolute;
width: 13%;
aspect-ratio: 1;
border: 2px solid #d93636;
border-radius: 50%;
}
.hk-game-wrap .hk-faceoff::after {
content: '';
position: absolute;
top: 50%; left: 50%;
width: 8px; height: 8px;
background: #d93636;
border-radius: 50%;
transform: translate(-50%, -50%);
}
.hk-game-wrap .hk-faceoff.tl { top: 18%; left: 12%; }
.hk-game-wrap .hk-faceoff.bl { bottom: 18%; left: 12%; }
.hk-game-wrap .hk-faceoff.tr { top: 18%; right: 12%; }
.hk-game-wrap .hk-faceoff.br { bottom: 18%; right: 12%; }
/* Goals */
.hk-game-wrap .hk-goal {
position: absolute;
top: 50%;
width: 14px;
height: 28%;
border: 3px solid #d93636;
background: rgba(217,54,54,0.12);
transform: translateY(-50%);
}
.hk-game-wrap .hk-goal.l { left: 6px; border-radius: 4px 0 0 4px; }
.hk-game-wrap .hk-goal.r { right: 6px; border-radius: 0 4px 4px 0; }
/* crease */
.hk-game-wrap .hk-crease {
position: absolute;
top: 50%;
width: 11%;
aspect-ratio: 1;
border: 2px solid #2962d6;
background: rgba(41,98,214,0.1);
transform: translateY(-50%);
}
.hk-game-wrap .hk-crease.l {
left: 20px;
border-radius: 0 100% 100% 0 / 0 50% 50% 0;
border-left: none;
}
.hk-game-wrap .hk-crease.r {
right: 20px;
border-radius: 100% 0 0 100% / 50% 0 0 50%;
border-right: none;
}
/* PUCK */
.hk-game-wrap .hk-puck {
position: absolute;
width: 26px;
height: 26px;
background: radial-gradient(circle at 35% 30%, #555 0%, #1a1a1a 50%, #000 100%);
border-radius: 50%;
box-shadow: 0 6px 14px rgba(0,0,0,0.5), inset 0 -3px 5px rgba(0,0,0,0.6);
z-index: 5;
transform: translate(-50%, -50%);
transition: opacity 0.3s;
}
.hk-game-wrap .hk-puck::before {
content: '';
position: absolute;
top: 18%; left: 22%;
width: 35%; height: 25%;
background: rgba(255,255,255,0.25);
border-radius: 50%;
filter: blur(2px);
}
/* STICK / paddle (player controlled) */
.hk-game-wrap .hk-stick {
position: absolute;
width: 18px;
height: 130px;
z-index: 10;
transform: translate(-50%, -50%);
pointer-events: none;
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.35));
}
/* shaft - longer wood + grip tape */
.hk-game-wrap .hk-stick-shaft {
position: absolute;
left: 50%;
bottom: 4px;
width: 7px;
height: 102px;
background:
linear-gradient(to right,
#f5d088 0%,
#d9a35a 35%,
#a87a3c 65%,
#6e4d22 100%);
border-radius: 4px;
transform: translateX(-50%);
box-shadow:
inset 1px 0 0 rgba(255,255,255,0.4),
inset -1px 0 0 rgba(0,0,0,0.3);
}
/* grip tape on shaft (right end) */
.hk-game-wrap .hk-stick-shaft::before {
content: '';
position: absolute;
left: 0; right: 0;
bottom: 0;
height: 26px;
background:
repeating-linear-gradient(
45deg,
#1a1a1a,
#1a1a1a 3px,
#2c2c2c 3px,
#2c2c2c 6px);
border-radius: 0 0 4px 4px;
}
/* shaft polish stripe */
.hk-game-wrap .hk-stick-shaft::after {
content: '';
position: absolute;
left: 1px;
top: 4px;
bottom: 30px;
width: 1.5px;
background: rgba(255,255,255,0.55);
border-radius: 2px;
}
/* blade — proper hockey blade curve */
.hk-game-wrap .hk-stick-blade {
position: absolute;
top: 0;
left: 50%;
width: 18px;
height: 36px;
background:
linear-gradient(to right,
#3a3a3a 0%,
#1a1a1a 50%,
#000 100%);
border-radius: 6px 8px 2px 2px / 8px 10px 2px 2px;
transform: translateX(-50%) skewX(-3deg);
box-shadow:
inset 1px 0 0 rgba(255,255,255,0.15),
inset -2px 0 4px rgba(0,0,0,0.6);
}
/* white blade tape stripe */
.hk-game-wrap .hk-stick-blade::before {
content: '';
position: absolute;
left: 3px;
top: 4px;
bottom: 4px;
width: 3px;
background: linear-gradient(to right, #ffffff 0%, #d8d8d8 100%);
border-radius: 2px;
box-shadow: 1px 0 0 rgba(0,0,0,0.3);
}
/* blade heel highlight */
.hk-game-wrap .hk-stick-blade::after {
content: '';
position: absolute;
right: 2px;
top: 4px;
width: 2px;
height: 8px;
background: rgba(255,255,255,0.2);
border-radius: 2px;
filter: blur(0.5px);
}
/* CPU stick (mirrored - blade on right) */
.hk-game-wrap .hk-stick-cpu {
width: 18px;
height: 130px;
}
.hk-game-wrap .hk-stick-shaft-cpu {
position: absolute;
left: 50%;
top: 4px;
width: 7px;
height: 102px;
background:
linear-gradient(to right,
#ff7777 0%,
#d93636 35%,
#a82828 65%,
#6e1818 100%);
border-radius: 4px;
transform: translateX(-50%);
box-shadow:
inset 1px 0 0 rgba(255,255,255,0.4),
inset -1px 0 0 rgba(0,0,0,0.3);
}
.hk-game-wrap .hk-stick-shaft-cpu::before {
content: '';
position: absolute;
left: 0; right: 0;
top: 0;
height: 26px;
background:
repeating-linear-gradient(
-45deg,
#1a1a1a,
#1a1a1a 3px,
#2c2c2c 3px,
#2c2c2c 6px);
border-radius: 4px 4px 0 0;
}
.hk-game-wrap .hk-stick-shaft-cpu::after {
content: '';
position: absolute;
left: 1px;
bottom: 4px;
top: 30px;
width: 1.5px;
background: rgba(255,255,255,0.55);
border-radius: 2px;
}
.hk-game-wrap .hk-stick-blade-cpu {
position: absolute;
bottom: 0;
left: 50%;
width: 18px;
height: 36px;
background:
linear-gradient(to right,
#3a3a3a 0%,
#1a1a1a 50%,
#000 100%);
border-radius: 2px 2px 8px 6px / 2px 2px 10px 8px;
transform: translateX(-50%) skewX(3deg);
box-shadow:
inset 1px 0 0 rgba(255,255,255,0.15),
inset -2px 0 4px rgba(0,0,0,0.6);
}
.hk-game-wrap .hk-stick-blade-cpu::before {
content: '';
position: absolute;
left: 3px;
top: 4px;
bottom: 4px;
width: 3px;
background: linear-gradient(to right, #ffffff 0%, #d8d8d8 100%);
border-radius: 2px;
box-shadow: 1px 0 0 rgba(0,0,0,0.3);
}
.hk-game-wrap .hk-stick-blade-cpu::after {
content: '';
position: absolute;
right: 2px;
bottom: 4px;
width: 2px;
height: 8px;
background: rgba(255,255,255,0.2);
border-radius: 2px;
filter: blur(0.5px);
}
/* goalie (right side - opponent) - LEGACY (unused, kept harmless) */
.hk-game-wrap .hk-goalie {
position: absolute;
right: 30px;
top: 50%;
width: 36px;
height: 60px;
transform: translateY(-50%);
z-index: 8;
transition: top 0.2s ease-out;
}
.hk-game-wrap .hk-goalie-body {
position: absolute;
inset: 0;
background: linear-gradient(180deg, #d93636 0%, #a82828 100%);
border-radius: 8px;
border: 2px solid #fff;
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
}
.hk-game-wrap .hk-goalie-helmet {
position: absolute;
top: -10px;
left: 50%;
width: 22px;
height: 22px;
background: #fff;
border-radius: 50%;
border: 2px solid #d93636;
transform: translateX(-50%);
}
/* Goal flash overlay */
.hk-game-wrap .hk-flash {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0;
z-index: 20;
}
.hk-game-wrap .hk-flash.goal-cpu {
background: radial-gradient(circle at center, rgba(217,54,54,0.6), transparent 70%);
animation: hk-flash-anim 0.6s ease-out;
}
.hk-game-wrap .hk-flash.goal-you {
background: radial-gradient(circle at center, rgba(246,195,70,0.6), transparent 70%);
animation: hk-flash-anim 0.6s ease-out;
}
@keyframes hk-flash-anim {
0% { opacity: 0; }
30% { opacity: 1; }
100% { opacity: 0; }
}
.hk-game-wrap .hk-confetti {
position: absolute;
inset: 0;
pointer-events: none;
overflow: hidden;
z-index: 22;
}
.hk-game-wrap .hk-confetti span {
position: absolute;
width: 10px;
height: 14px;
top: -20px;
border-radius: 2px;
animation: hk-confetti-fall 1.8s linear forwards;
opacity: 0.95;
}
@keyframes hk-confetti-fall {
0% { transform: translateY(0) rotate(0deg); opacity: 1; }
100% { transform: translateY(120%) rotate(720deg); opacity: 0; }
}
/* Goal banner */
.hk-game-wrap .hk-banner {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%) scale(0);
background: rgb(21,33,69);
color: rgb(246,195,70);
font-family: 'Barlow Condensed', sans-serif;
font-size: 56px;
font-weight: 900;
letter-spacing: 6px;
padding: 12px 40px;
border: 3px solid rgb(246,195,70);
z-index: 25;
pointer-events: none;
opacity: 0;
}
.hk-game-wrap .hk-banner.show {
animation: hk-banner-anim 1.3s ease-out;
}
@keyframes hk-banner-anim {
0% { transform: translate(-50%, -50%) scale(0); opacity: 0; }
25% { transform: translate(-50%, -50%) scale(1.15); opacity: 1; }
40% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
80% { opacity: 1; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 0; }
}
/* Pause overlay */
.hk-game-wrap .hk-pause-overlay {
position: absolute;
inset: 0;
background: rgba(21,33,69,0.78);
display: none;
align-items: center;
justify-content: center;
font-family: 'Barlow Condensed', sans-serif;
font-size: 90px;
font-weight: 900;
letter-spacing: 12px;
color: rgb(246,195,70);
z-index: 30;
pointer-events: none;
text-shadow: 0 4px 20px rgba(0,0,0,0.5);
}
.hk-game-wrap .hk-pause-overlay.show {
display: flex;
}
/* Controls */
.hk-game-wrap .hk-controls {
margin-top: 16px;
display: flex;
justify-content: center;
gap: 10px;
flex-wrap: wrap;
}
.hk-game-wrap .hk-btn {
background: rgb(21,33,69);
color: rgb(246,195,70);
border: none;
padding: 11px 24px;
font-family: 'Barlow Condensed', sans-serif;
font-weight: 800;
font-size: 13px;
letter-spacing: 2px;
text-transform: uppercase;
cursor: pointer;
transition: transform 0.2s, background 0.2s;
}
.hk-game-wrap .hk-btn:hover {
transform: translateY(-2px);
background: #1a2a5a;
}
.hk-game-wrap .hk-btn.gold {
background: rgb(246,195,70);
color: rgb(21,33,69);
}
.hk-game-wrap .hk-btn.gold:hover { background: #f0b840; }
.hk-game-wrap .hk-btn.active {
background: rgb(246,195,70);
color: rgb(21,33,69);
}
.hk-game-wrap .hk-hint {
text-align: center;
margin-top: 14px;
font-size: 12px;
color: #888;
letter-spacing: 1.5px;
text-transform: uppercase;
}
@media (max-width: 600px) {
.hk-game-wrap .hk-scorebar { padding: 10px 14px; gap: 8px; }
.hk-game-wrap .hk-score-num { font-size: 30px; }
.hk-game-wrap .hk-score-stat { min-width: 50px; }
.hk-game-wrap .hk-stat-val { font-size: 18px; }
}
(function(){
const rink = document.getElementById('hkRink');
const puck = document.getElementById('hkPuck');
const stick = document.getElementById('hkStick');
const goalie = document.getElementById('hkGoalie');
const flash = document.getElementById('hkFlash');
const banner = document.getElementById('hkBanner');
const scoreYouEl = document.getElementById('hkScoreYou');
const scoreCpuEl = document.getElementById('hkScoreCpu');
const savesEl = document.getElementById('hkSaves');
const shotsEl = document.getElementById('hkShots');
const diffBtns = document.querySelectorAll('.hk-game-wrap .hk-btn[data-diff]');
const resetBtn = document.getElementById('hkReset');
// game state in % coordinates of the rink
let puckX = 50, puckY = 50; // %
let puckVX = 0, puckVY = 0; // % per frame
let stuckCounter = 0; // anti-stuck
let stickX = 12, stickY = 50; // %
let goalieX = 82, goalieY = 50; // %
let scoreYou = 0, scoreCpu = 0;
let saves = 0, shots = 0;
let running = true;
let paused = false;
// difficulty config
const difficulties = {
easy: { puckSpeed: 0.55, goalieSpeed: 0.05, accuracy: 0.55 },
normal: { puckSpeed: 0.85, goalieSpeed: 0.075, accuracy: 0.72 },
hard: { puckSpeed: 1.2, goalieSpeed: 0.11, accuracy: 0.88 }
};
let diff = difficulties.normal;
diffBtns.forEach(btn => {
btn.addEventListener('click', () => {
diffBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
diff = difficulties[btn.dataset.diff];
});
});
const pauseBtn = document.getElementById('hkPause');
const pauseOverlay = document.getElementById('hkPauseOverlay');
pauseBtn.addEventListener('click', () => {
paused = !paused;
pauseBtn.innerHTML = paused ? '▶ Weiter' : '⏸ Pause';
pauseBtn.classList.toggle('active', paused);
pauseOverlay.classList.toggle('show', paused);
});
resetBtn.addEventListener('click', () => {
scoreYou = 0; scoreCpu = 0; saves = 0; shots = 0;
updateUI();
resetPuck('center');
});
// input: stick follows mouse / touch
function moveStick(e) {
const rect = rink.getBoundingClientRect();
const point = e.touches ? e.touches[0] : e;
const x = ((point.clientX - rect.left) / rect.width) * 100;
const y = ((point.clientY - rect.top) / rect.height) * 100;
// stick can move across the whole field
stickX = Math.max(6, Math.min(94, x));
stickY = Math.max(8, Math.min(92, y));
}
rink.addEventListener('mousemove', moveStick);
rink.addEventListener('touchmove', e => { e.preventDefault(); moveStick(e); }, {passive: false});
function resetPuck(side) {
puckX = 50; puckY = 50;
// shoot toward player by default
if (side === 'toYou') {
const angle = (Math.random() - 0.5) * 0.4;
puckVX = -diff.puckSpeed * Math.cos(angle);
puckVY = diff.puckSpeed * Math.sin(angle);
} else if (side === 'toCpu') {
const angle = (Math.random() - 0.5) * 0.4;
puckVX = diff.puckSpeed * Math.cos(angle);
puckVY = diff.puckSpeed * Math.sin(angle);
} else {
// center face-off — random direction
const dir = Math.random() < 0.5 ? -1 : 1;
puckVX = dir * diff.puckSpeed * 0.9;
puckVY = (Math.random() - 0.5) * diff.puckSpeed * 0.6;
}
}
function updateUI() {
scoreYouEl.textContent = scoreYou;
scoreCpuEl.textContent = scoreCpu;
savesEl.textContent = saves;
shotsEl.textContent = shots;
}
function rainConfetti() {
const cont = document.getElementById('hkConfetti');
if (!cont) return;
cont.innerHTML = '';
const colors = ['rgb(246,195,70)','rgb(21,33,69)','#ff6b6b','#52c4f5','#ffffff','#7bdc8e'];
const count = 70;
for (let i = 0; i { cont.innerHTML = ''; }, 2400);
}
function showBanner(text, type) {
banner.textContent = text;
banner.classList.remove('show');
flash.className = 'hk-flash';
// force reflow to restart animation
void banner.offsetWidth;
banner.classList.add('show');
flash.classList.add(type === 'cpu' ? 'goal-cpu' : 'goal-you');
setTimeout(() => {
flash.className = 'hk-flash';
}, 700);
}
function tick() {
if (!running) return;
if (paused) {
// when paused: just keep player stick moving with mouse, skip everything else
stick.style.left = stickX + '%';
stick.style.top = stickY + '%';
return scheduleNext();
}
// move puck
puckX += puckVX;
puckY += puckVY;
// bounce off top/bottom — ensure decent rebound speed
const minBounceY = diff.puckSpeed * 0.4;
if (puckY = 95) {
puckY = 95;
puckVY = -Math.max(Math.abs(puckVY), minBounceY);
}
// anti-stuck: if puck barely moves OR is in a corner with low speed, push it back to play
const speedNow = Math.hypot(puckVX, puckVY);
const inCorner = (puckX 92) && (puckY 88);
if (speedNow < diff.puckSpeed * 0.25 || inCorner && speedNow 25) {
// push toward center
const angToCenter = Math.atan2(50 - puckY, 50 - puckX);
puckVX = Math.cos(angToCenter) * diff.puckSpeed;
puckVY = Math.sin(angToCenter) * diff.puckSpeed;
stuckCounter = 0;
}
} else {
stuckCounter = 0;
}
// CPU STICK AI — moves around the right side, chases puck when in CPU half
if (puckX > 50) {
// chase puck
const targetX = puckX + 4 + (Math.random() - 0.5) * (1 - diff.accuracy) * 8;
const targetY = puckY + (Math.random() - 0.5) * (1 - diff.accuracy) * 25;
goalieX += (targetX - goalieX) * diff.goalieSpeed * 1.6;
goalieY += (targetY - goalieY) * diff.goalieSpeed * 1.6;
} else {
// drift back near right goal area
goalieX += (82 - goalieX) * 0.04;
goalieY += (50 - goalieY) * 0.03;
}
// clamp CPU stick into right side of rink
goalieX = Math.max(52, Math.min(94, goalieX));
goalieY = Math.max(10, Math.min(90, goalieY));
// collision with player stick — large hit zone, works any direction
const sdx = puckX - stickX;
const sdy = puckY - stickY;
const sdist = Math.hypot(sdx, sdy);
if (sdist < 12) {
// hit it — bounce away from stick
const ang = Math.atan2(sdy, sdx) || 0;
const speed = Math.max(Math.hypot(puckVX, puckVY), diff.puckSpeed * 0.95) * 1.1;
puckVX = Math.cos(ang) * speed;
puckVY = Math.sin(ang) * speed;
// push puck out of stick
puckX = stickX + Math.cos(ang) * 12.5;
puckY = stickY + Math.sin(ang) * 12.5;
saves++;
updateUI();
}
// collision with CPU stick — push puck out cleanly to avoid getting pinned
const cdx = puckX - goalieX;
const cdy = puckY - goalieY;
const cdist = Math.hypot(cdx, cdy);
if (cdist < 12) {
// direction from CPU stick to puck (away)
let ang = Math.atan2(cdy, cdx);
if (cdist < 0.5) ang = Math.atan2(50 - goalieY, 50 - goalieX); // fallback if exactly overlapping
// bias bounce toward player goal (left) so puck doesn't get parked at right wall
const biasAng = Math.atan2(Math.sin(ang) * 0.7, Math.cos(ang) - 0.3);
const speed = Math.max(Math.hypot(puckVX, puckVY), diff.puckSpeed) * 1.1;
puckVX = Math.cos(biasAng) * speed;
puckVY = Math.sin(biasAng) * speed;
puckX = goalieX + Math.cos(biasAng) * 13;
puckY = goalieY + Math.sin(biasAng) * 13;
}
// GOAL detection
// left goal (player's): x < 3 within goal height (vertical center ±14%)
if (puckX 36 && puckY resetPuck('toYou'), 800);
puckX = 50; puckY = 50; puckVX = 0; puckVY = 0;
return scheduleNext();
} else {
// wide / miss — bounce
puckX = 3;
puckVX = Math.abs(puckVX) * 0.8;
shots++;
updateUI();
}
}
// right goal (CPU's): x > 97 within goal height
if (puckX > 97) {
if (puckY > 36 && puckY resetPuck('toCpu'), 800);
puckX = 50; puckY = 50; puckVX = 0; puckVY = 0;
return scheduleNext();
} else {
puckX = 97;
puckVX = -Math.abs(puckVX) * 0.8;
}
}
// friction & speed cap
const speed = Math.hypot(puckVX, puckVY);
const maxSpeed = diff.puckSpeed * 1.6;
if (speed > maxSpeed) {
puckVX *= maxSpeed / speed;
puckVY *= maxSpeed / speed;
}
// render
puck.style.left = puckX + '%';
puck.style.top = puckY + '%';
stick.style.left = stickX + '%';
stick.style.top = stickY + '%';
goalie.style.left = goalieX + '%';
goalie.style.top = goalieY + '%';
scheduleNext();
}
function scheduleNext() {
requestAnimationFrame(tick);
}
// init
resetPuck('center');
scheduleNext();
})();
Du
0
Saves
0
:
Schüsse
0
Gegner
0
TOR!
PAUSE
Bewege die Maus oder den Finger im Feld – treffe den Puck mit dem Schläger!