Football Stars Deluxe
by BlazeKoala99651 lines29.5 KB
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Football Stars Deluxe</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#010408;overflow:hidden;font-family:'Segoe UI',Arial,sans-serif;user-select:none;touch-action:none;color:#fff}
#gameCanvas{position:absolute;inset:0;width:100%;height:100%;z-index:1}
#score-board{position:absolute;top:15px;left:20px;background:linear-gradient(135deg,rgba(10,25,60,.95),rgba(5,12,30,.98));border:1px solid rgba(255,204,0,.4);padding:8px 18px;border-left:6px solid #ffcc00;border-radius:0 8px 8px 0;font-weight:900;min-width:280px;display:flex;justify-content:space-between;align-items:center;z-index:2;box-shadow:0 10px 25px rgba(0,0,0,.5)}
.score-display{background:#020612;padding:4px 16px;border-radius:4px;font-size:22px;color:#ffcc00;font-family:monospace;border:1px solid rgba(255,255,255,.1)}
#match-time{color:#8a99ad;font-size:14px;font-weight:700}
#tournament-stage-text{position:absolute;top:75px;left:20px;font-size:11px;font-weight:800;background:rgba(0,0,0,.7);border:1px solid rgba(255,255,255,.15);padding:4px 12px;border-radius:4px;letter-spacing:1px;color:#ffcc00;z-index:2}
.control-zone{position:absolute;bottom:25px;width:100%;display:flex;justify-content:space-between;padding:0 30px;z-index:2;pointer-events:none}
.joystick-container{width:130px;height:130px;background:radial-gradient(circle,rgba(10,25,50,.8),rgba(2,5,15,.9));border:2px solid rgba(255,204,0,.4);border-radius:50%;position:relative;pointer-events:auto;box-shadow:0 10px 30px rgba(0,0,0,.6)}
#joystick-knob{width:56px;height:56px;background:radial-gradient(circle,#ffffff,#7391b5);border-radius:50%;position:absolute;top:35px;left:35px;box-shadow:0 5px 15px rgba(0,0,0,.8);border:1px solid rgba(255,255,255,.5)}
.action-buttons{display:flex;gap:15px;align-items:flex-end;pointer-events:auto}
.btn-fifa{border-radius:50%;color:#fff;font-weight:800;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:11px;border:2px solid rgba(255,255,255,.3);text-transform:uppercase;cursor:pointer;box-shadow:0 8px 20px rgba(0,0,0,.6)}
.btn-shoot{background:linear-gradient(135deg,#ff9f00,#cc3300);width:76px;height:76px}
.btn-pass{background:linear-gradient(135deg,#00a3ff,#0033cc);width:66px;height:66px}
.btn-tackle{background:linear-gradient(135deg,#2ecc71,#1e8449);width:66px;height:66px}
.screen{position:absolute;inset:0;background:radial-gradient(circle at center,#0c224a,#02050c);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:10;overflow-y:auto;padding:20px}
.menu-title{font-size:38px;font-weight:900;margin-bottom:25px;letter-spacing:3px;background:linear-gradient(90deg,#fff,#ffcc00,#fff);-webkit-background-clip:text;-webkit-text-fill-color:transparent;text-transform:uppercase;text-align:center}
.selection-box{background:rgba(4,12,30,.95);border:2px solid rgba(255,204,0,.5);padding:30px;border-radius:16px;text-align:center;box-shadow:0 25px 50px rgba(0,0,0,.8)}
select{background:#061026;color:#fff;border:1px solid #ffcc00;padding:12px 30px;font-size:16px;border-radius:8px;outline:none;font-weight:bold;box-shadow:inset 0 2px 5px rgba(0,0,0,.5)}
.btn-gold{background:linear-gradient(90deg,#ffcc00,#ff9900);border:none;color:#050b14;font-weight:900;padding:16px 50px;font-size:16px;border-radius:8px;box-shadow:0 10px 30px rgba(255,204,0,.4);cursor:pointer;text-transform:uppercase;margin-top:20px;transition:transform .1s}
.btn-gold:active{transform:scale(.96)}
table{border-collapse:collapse;margin:15px 0;width:340px;text-align:left;background:rgba(255,255,255,.05);border-radius:8px;overflow:hidden}
th,td{padding:10px 12px;border-bottom:1px solid rgba(255,255,255,.1);font-size:14px}
th{background:rgba(255,204,0,.25);color:#ffcc00}
.highlight-row{background:rgba(0,229,255,.2);font-weight:bold}
#goal-overlay{position:absolute;inset:0;background:radial-gradient(circle,rgba(255,204,0,.4) 0%,rgba(0,0,0,.8) 80%);display:flex;align-items:center;justify-content:center;z-index:5;opacity:0;pointer-events:none;transition:opacity .3s}
.goal-text{font-size:75px;font-weight:1000;text-shadow:0 0 40px #ffcc00,0 0 80px #ff9900;font-style:italic;letter-spacing:4px}
#loading-screen{position:absolute;inset:0;background:radial-gradient(circle at center,#163566 0%,#02050c 80%);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:99;transition:opacity .5s}
.loading-title{font-size:44px;font-weight:1000;letter-spacing:6px;text-transform:uppercase}
.loading-title span{color:#ffcc00}
.loading-sub{margin-top:6px;color:#9fb0c5;font-size:13px;letter-spacing:4px;text-transform:uppercase}
.bar-container{width:75%;max-width:440px;height:16px;background:rgba(255,255,255,.1);border:1px solid rgba(255,204,0,.3);border-radius:99px;overflow:hidden;margin-top:30px;box-shadow:inset 0 2px 5px rgba(0,0,0,.6)}
#progress-bar{width:0%;height:100%;background:linear-gradient(90deg,#ffcc00,#ff9900);box-shadow:0 0 20px #ffcc00}
#progress-text{font-size:20px;font-weight:900;color:#ffcc00;margin-top:14px;font-family:monospace}
#loading-info{font-size:11px;color:#8a99ad;margin-top:6px;text-transform:uppercase;letter-spacing:1px}
.loading-ball{font-size:52px;margin-top:25px;animation:spin 1s linear infinite;filter:drop-shadow(0 0 10px rgba(255,204,0,.5)}}
@keyframes spin{to{transform:rotate(360deg)}}
</style>
</head>
<body>
<div id="loading-screen">
<div class="loading-title">FOOTBALL <span>STARS</span></div>
<div class="loading-sub">Deluxe Edition</div>
<div class="bar-container"><div id="progress-bar"></div></div>
<div id="progress-text">0%</div>
<div id="loading-info">Stadion wird aufgebaut...</div>
<div class="loading-ball">⚽</div>
</div>
<div id="menu-team" class="screen" style="display:none">
<div style="color:#ffcc00;font-weight:900;letter-spacing:4px;margin-bottom:5px">FOOTBALL STARS DELUXE</div>
<div class="menu-title">Wähle dein Land</div>
<div class="selection-box">
<select id="select-country"></select><br>
<button class="btn-gold" onclick="setupTournament()">Turnier starten</button>
</div>
</div>
<div id="menu-hub" class="screen" style="display:none">
<div class="menu-title" id="hub-stage-title">Gruppenphase</div>
<div class="selection-box" style="min-width:380px">
<div id="hub-content"></div>
<button class="btn-gold" id="btn-next-match" onclick="triggerLoadingScreen()">Nächstes Match spielen</button>
</div>
</div>
<div id="menu-penalty" class="screen" style="display:none">
<div class="menu-title">Elfmeterschiessen</div>
<div class="selection-box">
<h3 id="penalty-teams" style="color:#ffcc00;font-size:24px">0 : 0</h3>
<p id="penalty-info">Nutze den Joystick zum Zielen und drücke SCHUSS!</p>
<button class="btn-gold" onclick="executePenaltyRound()">Schießen / Halten</button>
</div>
</div>
<div id="goal-overlay"><div class="goal-text" id="gt">TOR!</div></div>
<div id="score-board" style="display:none">
<span id="name-p">GER</span>
<span class="score-display"><span id="p-score">0</span> : <span id="e-score">0</span></span>
<span id="name-e">BRA</span>
<span id="match-time">00:00</span>
</div>
<div id="tournament-stage-text" style="display:none">GRUPPENPHASE</div>
<canvas id="gameCanvas"></canvas>
<div class="control-zone" id="controls" style="display:none">
<div class="joystick-container" id="joystick"><div id="joystick-knob"></div></div>
<div class="action-buttons">
<div class="btn-fifa btn-tackle" id="tackleBtn">Abnehmen</div>
<div class="btn-fifa btn-pass" id="passBtn">Pass</div>
<div class="btn-fifa btn-shoot" id="shootBtn">Schuss</div>
</div>
</div>
<script>
const canvas=document.getElementById("gameCanvas"),ctx=canvas.getContext("2d");
let W=innerWidth,H=innerHeight,fieldX=0,fieldY=0,fieldW=0,fieldH=0;
let gameRunning=false,penaltyMode=false,userTeamKey="",tournamentStage="GROUP",currentOpponent="",groupStageData=[];
let scorePlayer=0,scoreEnemy=0,matchMinutes=0,matchSeconds=0,frameCounter=0;
let kickoffTeamIsPlayer = true;
const TEAMS={
GER:{short:"GER",flag:"🇩🇪",name:"Deutschland",color:"#f5f5f5",accent:"#000"},
BRA:{short:"BRA",flag:"🇧🇷",name:"Brasilien",color:"#f1c40f",accent:"#27ae60"},
FRA:{short:"FRA",flag:"🇫🇷",name:"Frankreich",color:"#155ab6",accent:"#c0392b"},
ARG:{short:"ARG",flag:"🇦🇷",name:"Argentinien",color:"#74acdf",accent:"#ffffff"},
ESP:{short:"ESP",flag:"🇪🇸",name:"Spanien",color:"#c0392b",accent:"#f1c40f"},
POR:{short:"POR",flag:"🇵🇹",name:"Portugal",color:"#b31919",accent:"#27ae60"},
ENG:{short:"ENG",flag:"🏴",name:"England",color:"#ffffff",accent:"#c0392b"},
ITA:{short:"ITA",flag:"🇮🇹",name:"Italien",color:"#006699",accent:"#ffffff"},
NED:{short:"NED",flag:"🇳🇱",name:"Niederlande",color:"#ff6600",accent:"#ffffff"},
BEL:{short:"BEL",flag:"🇧🇪",name:"Belgien",color:"#cc0000",accent:"#f1c40f"},
CRO:{short:"CRO",flag:"🇭🇷",name:"Kroatien",color:"#ffffff",accent:"#c0392b"},
JPN:{short:"JPN",flag:"🇯🇵",name:"Japan",color:"#000066",accent:"#ffffff"},
USA:{short:"USA",flag:"🇺🇸",name:"USA",color:"#002b49",accent:"#c0392b"},
MAR:{short:"MAR",flag:"🇲🇦",name:"Marokko",color:"#c1272d",accent:"#006633"}
};
function createTeamPlayers(isPlayerTeam){
const list = [];
const baseHomeX = isPlayerTeam ? -18 : 18;
list.push({x: isPlayerTeam ? -27 : 27, z: 0, homeX: isPlayerTeam ? -27 : 27, homeZ: 0, vx: 0, vz: 0, speed: .18, radius: .8, isGK: true});
const defZ = [-12, -4, 4, 12];
for(let i=0; i<4; i++){
list.push({x: baseHomeX + (isPlayerTeam ? -6 : 6), z: defZ[i], homeX: baseHomeX + (isPlayerTeam ? -6 : 6), homeZ: defZ[i], vx: 0, vz: 0, speed: .20, radius: .8});
}
const midZ = [-10, -3, 3, 10];
for(let i=0; i<4; i++){
list.push({x: baseHomeX + (isPlayerTeam ? 2 : -2), z: midZ[i], homeX: baseHomeX + (isPlayerTeam ? 2 : -2), homeZ: midZ[i], vx: 0, vz: 0, speed: .21, radius: .8});
}
const stZ = [-5, 5];
for(let i=0; i<2; i++){
list.push({x: baseHomeX + (isPlayerTeam ? 8 : -8), z: stZ[i], homeX: baseHomeX + (isPlayerTeam ? 8 : -8), homeZ: stZ[i], vx: 0, vz: 0, speed: .22, radius: .8});
}
return list;
}
let playerTeam = [];
let enemyTeam = [];
let controlledPlayerIndex = 9;
const referee={x:0,z:-6,speed:.15,radius:.7};
const ball={x:0,y:.4,z:0,vx:0,vz:0,radius:.4,owner:null,friction:.98};
const selectC=document.getElementById("select-country");
Object.keys(TEAMS).forEach(k=>selectC.innerHTML+=`<option value="${k}">${TEAMS[k].flag} ${TEAMS[k].name}</option>`);
function resizeCanvas(){
W=innerWidth;H=innerHeight;canvas.width=W*devicePixelRatio;canvas.height=H*devicePixelRatio;
canvas.style.width=W+"px";canvas.style.height=H+"px";ctx.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0);
const maxW=Math.min(W-30,960), maxH=Math.min(H-80,620);
fieldW=maxW;fieldH=maxH;fieldX=(W-fieldW)/2;fieldY=(H-fieldH)/2;
}
addEventListener("resize",resizeCanvas);resizeCanvas();
function worldToScreen(x,z){return {x:fieldX+(x+30)/60*fieldW,y:fieldY+(z+20)/40*fieldH}}
function drawField(){
ctx.clearRect(0,0,W,H);
// Stadion Außenbereich (Tribünen-Anmutung)
ctx.fillStyle="#040b16";
ctx.fillRect(0,0,W,H);
// Auslauf / Rasen-Umrandung
ctx.fillStyle="#10381d";
ctx.fillRect(fieldX-15, fieldY-15, fieldW+30, fieldH+30);
// Realistisches Rasenmuster (Mähstreifen)
const stripeWidth = fieldW / 12;
for(let i=0; i<12; i++){
ctx.fillStyle = i % 2 === 0 ? "#186b30" : "#1f7d39";
ctx.fillRect(fieldX + i * stripeWidth, fieldY, stripeWidth, fieldH);
}
// Tornetze (Hintergrund-Grafik in den Toren)
ctx.fillStyle = "rgba(255,255,255,0.15)";
ctx.strokeStyle = "rgba(255,255,255,0.3)";
ctx.lineWidth = 1;
// Linkes Tornetz
ctx.fillRect(fieldX - 18, fieldY + fieldH * 0.4, 18, fieldH * 0.2);
ctx.strokeRect(fieldX - 18, fieldY + fieldH * 0.4, 18, fieldH * 0.2);
// Rechtes Tornetz
ctx.fillRect(fieldX + fieldW, fieldY + fieldH * 0.4, 18, fieldH * 0.2);
ctx.strokeRect(fieldX + fieldW, fieldY + fieldH * 0.4, 18, fieldH * 0.2);
// Spielfeld-Linien (Weiß, leicht leuchtend)
ctx.strokeStyle="rgba(255,255,255,.9)";
ctx.lineWidth=3;
ctx.strokeRect(fieldX,fieldY,fieldW,fieldH);
// Mittellinie
ctx.beginPath();ctx.moveTo(fieldX+fieldW/2,fieldY);ctx.lineTo(fieldX+fieldW/2,fieldY+fieldH);ctx.stroke();
// Mittelkreis
ctx.beginPath();ctx.arc(fieldX+fieldW/2,fieldY+fieldH/2,fieldH*.13,0,Math.PI*2);ctx.stroke();
ctx.beginPath();ctx.fillStyle="rgba(255,255,255,.9)";ctx.arc(fieldX+fieldW/2,fieldY+fieldH/2,4,0,Math.PI*2);ctx.fill();
// Strafräume
ctx.strokeRect(fieldX,fieldY+fieldH*.32,fieldW*.14,fieldH*.36);
ctx.strokeRect(fieldX+fieldW*.86,fieldY+fieldH*.32,fieldW*.14,fieldH*.36);
// Torpfosten (Stabiler Look)
ctx.fillStyle="#fff";
ctx.fillRect(fieldX-4,fieldY+fieldH*.4,6,fieldH*.2);
ctx.fillRect(fieldX+fieldW-2,fieldY+fieldH*.4,6,fieldH*.2);
}
function drawPlayer(obj,teamObj,label,isControlled){
const p=worldToScreen(obj.x,obj.z),r=Math.max(9,fieldW/72);
// Dynamischer Bodenschatten
ctx.beginPath();
ctx.fillStyle="rgba(0,0,0,.45)";
ctx.ellipse(p.x,p.y+r*.85,r*1.1,r*.4,0,0,Math.PI*2);
ctx.fill();
// Spieler-Indikator (Fokus-Ring)
if(isControlled){
ctx.strokeStyle="#ffcc00";ctx.lineWidth=3.5;
ctx.beginPath();ctx.arc(p.x,p.y,r+5,0,Math.PI*2);ctx.stroke();
// Kleiner Leuchteffekt
ctx.strokeStyle="rgba(255,204,0,0.4)";ctx.lineWidth=6;
ctx.beginPath();ctx.arc(p.x,p.y,r+8,0,Math.PI*2);ctx.stroke();
}
// Trikot / Körper
ctx.beginPath();
ctx.fillStyle=teamObj.color;
ctx.arc(p.x,p.y,r,0,Math.PI*2);
ctx.fill();
ctx.strokeStyle=teamObj.accent;
ctx.lineWidth=2;
ctx.stroke();
// Kopf / Haut
ctx.beginPath();
ctx.fillStyle="#f3c19a";
ctx.arc(p.x,p.y-r*.7,r*.4,0,Math.PI*2);
ctx.fill();
// Spieler-Label / TW Anzeige
if(label){
ctx.fillStyle="#fff";
ctx.font="bold 10px Segoe UI";
ctx.textAlign="center";
ctx.shadowColor="rgba(0,0,0,0.8)";
ctx.shadowBlur=4;
ctx.fillText(label,p.x,p.y+r*1.9);
ctx.shadowBlur=0; // Reset
}
}
function drawGame(){
drawField();
const pTeamObj = TEAMS[userTeamKey] || {color:"#fff", accent:"#000"};
const eTeamObj = TEAMS[currentOpponent] || {color:"#c33", accent:"#fff"};
playerTeam.forEach((pl, idx)=>{
drawPlayer(pl, pTeamObj, idx===0?"TW":"", idx===controlledPlayerIndex);
});
enemyTeam.forEach((el, idx)=>{
drawPlayer(el, eTeamObj, idx===0?"TW":"", false);
});
// Schiedsrichter (auffälliges Gelb/Neon)
drawPlayer(referee,{color:"#d4ac0d",accent:"#111"},"REF",false);
// Ball mit Glanz und Schatten
const b=worldToScreen(ball.x,ball.z),br=Math.max(6,fieldW/95);
// Ballschatten
ctx.beginPath();
ctx.fillStyle="rgba(0,0,0,.5)";
ctx.ellipse(b.x,b.y+br,br*1.2,br*0.5,0,0,Math.PI*2);
ctx.fill();
// Ballkörper
ctx.beginPath();
ctx.fillStyle="#ffffff";
ctx.arc(b.x,b.y,br,0,Math.PI*2);
ctx.fill();
ctx.strokeStyle="#222";
ctx.lineWidth=1.5;
ctx.stroke();
// Kleines Fußballelement-Muster auf dem Ball
ctx.beginPath();
ctx.fillStyle="#111";
ctx.arc(b.x,b.y,br*0.35,0,Math.PI*2);
ctx.fill();
}
function triggerLoadingScreen(isInitialStart=false){
if(!isInitialStart) document.getElementById("menu-hub").style.display="none";
const screen=document.getElementById("loading-screen");
const bar=document.getElementById("progress-bar");
const txt=document.getElementById("progress-text");
screen.style.display="flex";
screen.style.opacity="1";
bar.style.width="0%";
txt.textContent="0%";
const fillDuration=1400;
const holdDuration=2000;
const total=fillDuration+holdDuration;
const start=Date.now();
function loaderStep(){
const elapsed=Date.now()-start;
let percent = elapsed<fillDuration ? Math.floor((elapsed/fillDuration)*99) : (elapsed<total ? 99 : 100);
bar.style.width=percent+"%";
txt.textContent=percent+"%";
if(percent<100){
setTimeout(loaderStep,30);
}else{
setTimeout(()=>{
screen.style.opacity="0";
setTimeout(()=>{
screen.style.display="none";
if(isInitialStart){document.getElementById("menu-team").style.display="flex";}
else{playNextMatch();}
},500);
},200);
}
}
loaderStep();
}
function setupTournament(){
userTeamKey=selectC.value;tournamentStage="GROUP";
let allKeys = Object.keys(TEAMS);
let opponents = allKeys.filter(k => k !== userTeamKey);
opponents.sort(() => Math.random() - 0.5);
let selectedGroupKeys = [userTeamKey, opponents[0], opponents[1], opponents[2]];
groupStageData = selectedGroupKeys.map(k=>({key:k,name:TEAMS[k].name,flag:TEAMS[k].flag,pts:0,gd:0,matchesPlayed:0}));
document.getElementById("menu-team").style.display="none";updateTournamentHub();drawGame();
}
function updateTournamentHub(){
document.getElementById("menu-hub").style.display="flex";
const content=document.getElementById("hub-content");
if(tournamentStage==="GROUP"){
document.getElementById("hub-stage-title").textContent="🏆 WM - Gruppenphase";
groupStageData.sort((a,b)=>b.pts-a.pts||b.gd-a.gd);
let html="<table><tr><th>Pos</th><th>Team</th><th>Sp</th><th>Diff</th><th>Pkt</th></tr>";
groupStageData.forEach((t,i)=>{let c=t.key===userTeamKey?"class='highlight-row'":"";html+=`<tr ${c}><td>${i+1}</td><td>${t.flag} ${t.key}</td><td>${t.matchesPlayed}</td><td>${t.gd}</td><td>${t.pts}</td></tr>`});
html+="</table>";
const me=groupStageData.find(t=>t.key===userTeamKey);
if(me.matchesPlayed>=3){tournamentStage="HALBFINALE";content.innerHTML="Gruppenphase beendet!";document.getElementById("btn-next-match").textContent="Zum Halbfinale";return}
const myIdx=groupStageData.findIndex(t=>t.key===userTeamKey);
currentOpponent=groupStageData[(myIdx+me.matchesPlayed+1)%groupStageData.length].key;
html+=`<p>Nächstes Spiel: <b>Dein Team</b> vs <b>${TEAMS[currentOpponent].flag} ${currentOpponent}</b></p>`;
content.innerHTML=html;
}else if(tournamentStage==="HALBFINALE"||tournamentStage==="FINALE"){
document.getElementById("hub-stage-title").textContent=`🏁 WM - ${tournamentStage}`;
content.innerHTML=`<h3>K.o.-Runde</h3><p>${TEAMS[userTeamKey].flag} ${userTeamKey} vs ${TEAMS[currentOpponent].flag} ${currentOpponent}</p>`;
document.getElementById("btn-next-match").textContent="Match starten";
}else{
document.getElementById("hub-stage-title").textContent="🎉 WELTMEISTER!";
content.innerHTML="<h2>🏆 CHAMPION! 🏆</h2><p>Pokal gewonnen!</p>";
document.getElementById("btn-next-match").textContent="Neues Turnier";
document.getElementById("btn-next-match").onclick=()=>location.reload();
}
}
function playNextMatch(){
document.getElementById("score-board").style.display="flex";document.getElementById("tournament-stage-text").style.display="block";document.getElementById("controls").style.display="flex";
document.getElementById("name-p").textContent=userTeamKey;document.getElementById("name-e").textContent=currentOpponent;
scorePlayer=0;scoreEnemy=0;matchMinutes=0;matchSeconds=0;document.getElementById("p-score").textContent="0";document.getElementById("e-score").textContent="0";
penaltyMode=false;
kickoffTeamIsPlayer = true;
playerTeam = createTeamPlayers(true);
enemyTeam = createTeamPlayers(false);
controlledPlayerIndex = 9;
resetPositions();
gameRunning=true;
}
function resetPositions(){
playerTeam.forEach(pl=>{ pl.x=pl.homeX; pl.z=pl.homeZ; pl.vx=0; pl.vz=0; });
enemyTeam.forEach(el=>{ el.x=el.homeX; el.z=el.homeZ; el.vx=0; el.vz=0; });
referee.x=0; referee.z=-6;
ball.x=0; ball.z=0; ball.vx=0; ball.vz=0;
if(kickoffTeamIsPlayer){
ball.owner = playerTeam[9];
ball.owner.isPlayer = true;
controlledPlayerIndex = 9;
}else{
ball.owner = enemyTeam[9];
ball.owner.isPlayer = false;
}
}
function finishMatch(){
gameRunning=false;document.getElementById("score-board").style.display="none";document.getElementById("tournament-stage-text").style.display="none";document.getElementById("controls").style.display="none";
if(tournamentStage==="GROUP"){
let me=groupStageData.find(t=>t.key===userTeamKey),opp=groupStageData.find(t=>t.key===currentOpponent);me.matchesPlayed++;opp.matchesPlayed++;me.gd+=scorePlayer-scoreEnemy;opp.gd+=scoreEnemy-scorePlayer;
if(scorePlayer>scoreEnemy)me.pts+=3;else if(scorePlayer<scoreEnemy)opp.pts+=3;else{me.pts++;opp.pts++}
groupStageData.forEach(t=>{if(t.key!==userTeamKey&&t.key!==currentOpponent){t.matchesPlayed++;t.pts+=Math.random()>.5?3:1}});
updateTournamentHub();
}else{
if(scorePlayer===scoreEnemy){startPenaltyShootout()}
else if(scorePlayer>scoreEnemy){
if(tournamentStage==="HALBFINALE"){
tournamentStage="FINALE";
let otherTeams = Object.keys(TEAMS).filter(k=>k!==userTeamKey&&k!==currentOpponent);
currentOpponent = otherTeams[Math.floor(Math.random()*otherTeams.length)];
}
else tournamentStage="FINISHED";updateTournamentHub();
}else{alert("Ausgeschieden!");location.reload()}
}
}
let penaltyState={pScores:0,eScores:0,rounds:0,turn:true};
function startPenaltyShootout(){penaltyMode=true;gameRunning=false;document.getElementById("menu-penalty").style.display="flex";penaltyState={pScores:0,eScores:0,rounds:0,turn:true};updatePenaltyScreen()}
function updatePenaltyScreen(){document.getElementById("penalty-teams").textContent=`${userTeamKey} ${penaltyState.pScores} : ${penaltyState.eScores} ${currentOpponent}`;document.getElementById("penalty-info").textContent=penaltyState.turn?"Joystick zielen & SCHUSS!":"Gegner schießt!"}
function executePenaltyRound(){
const target=moveZ<-.2?"left":moveZ>.2?"right":"center";
if(penaltyState.turn){const dive=Math.random()>.5?"left":"right";if(target!==dive){penaltyState.pScores++;alert("TOR!")}else alert("GEHALTEN!")}
else{const shoot=Math.random()>.5?"left":"right",dive=Math.random()>.5?"left":"right";if(shoot!==dive){penaltyState.eScores++;alert("Gegner trifft.")}else alert("PARADE!")}
penaltyState.rounds++;penaltyState.turn=!penaltyState.turn;updatePenaltyScreen();
if(penaltyState.rounds>=3&&penaltyState.pScores!==penaltyState.eScores){document.getElementById("menu-penalty").style.display="none";if(penaltyState.pScores>penaltyState.eScores){scorePlayer++;finishMatch()}else{alert("Verloren!");location.reload()}}
}
let joystickActive=false,joyStartX=0,joyStartY=0,moveX=0,moveZ=0;
const joystick=document.getElementById("joystick"),knob=document.getElementById("joystick-knob");
function handleStart(e){if(!gameRunning&&!penaltyMode)return;joystickActive=true;const r=joystick.getBoundingClientRect();joyStartX=r.left+r.width/2;joyStartY=r.top+r.height/2}
function handleMove(e){if(!joystickActive)return;const p=e.touches?e.touches[0]:e,dx=p.clientX-joyStartX,dy=p.clientY-joyStartY,dist=Math.min(Math.hypot(dx,dy),40),a=Math.atan2(dy,dx);knob.style.transform=`translate(${Math.cos(a)*dist}px,${Math.sin(a)*dist}px)`;moveX=Math.cos(a)*dist/40;moveZ=Math.sin(a)*dist/40}
function handleEnd(){joystickActive=false;knob.style.transform="translate(0,0)";moveX=0;moveZ=0}
joystick.addEventListener("touchstart",handleStart);joystick.addEventListener("mousedown",handleStart);addEventListener("touchmove",handleMove,{passive:false});addEventListener("mousemove",handleMove);addEventListener("touchend",handleEnd);addEventListener("mouseup",handleEnd);
function triggerPass(){
if(ball.owner && ball.owner.isPlayer){
let ownerObj = ball.owner;
ball.owner = null;
let bestTarget = null;
let minDist = 999;
playerTeam.forEach(pl => {
if(pl !== ownerObj){
let d = Math.hypot(pl.x - ownerObj.x, pl.z - ownerObj.z);
let forwardBonus = pl.x > ownerObj.x ? -5 : 0;
let score = d + forwardBonus;
if(score < minDist){ minDist = score; bestTarget = pl; }
}
});
if(bestTarget){
let a = Math.atan2(bestTarget.z - ownerObj.z, bestTarget.x - ownerObj.x);
ball.vx = Math.cos(a) * 1.4;
ball.vz = Math.sin(a) * 1.4;
}else{
ball.vx = 1.2; ball.vz = 0;
}
}
}
function triggerShoot(){
if(ball.owner && ball.owner.isPlayer){
ball.owner = null;
let targetX = 30;
let targetZ = (Math.random() - 0.5) * 4;
let a = Math.atan2(targetZ - ball.z, targetX - ball.x);
ball.vx = Math.cos(a) * 2.2;
ball.vz = Math.sin(a) * 2.2;
}
}
function triggerTackle(){
let activePl = playerTeam[controlledPlayerIndex];
if(ball.owner && !ball.owner.isPlayer){
let enemyWithBall = ball.owner;
let dist = Math.hypot(activePl.x - enemyWithBall.x, activePl.z - enemyWithBall.z);
if(dist < 3.0){
ball.owner = activePl;
ball.owner.isPlayer = true;
controlledPlayerIndex = playerTeam.indexOf(activePl);
}
}
}
document.getElementById("passBtn").addEventListener("click",triggerPass);
document.getElementById("shootBtn").addEventListener("click",triggerShoot);
document.getElementById("tackleBtn").addEventListener("click",triggerTackle);
function updatePhysics(){
if(!gameRunning)return;
frameCounter++;if(frameCounter%6===0){matchSeconds++;if(matchSeconds>=60){matchSeconds=0;matchMinutes++}document.getElementById("match-time").textContent=`${String(matchMinutes).padStart(2,"0")}:${String(matchSeconds).padStart(2,"0")}`}
if(matchMinutes>=3){finishMatch();return}
if(!ball.owner || ball.owner.isPlayer === false){
let closestIdx = 0;
let minD = 999;
playerTeam.forEach((pl, idx)=>{
let d = Math.hypot(ball.x - pl.x, ball.z - pl.z);
if(d < minD){ minD = d; closestIdx = idx; }
});
controlledPlayerIndex = closestIdx;
}
let activePl = playerTeam[controlledPlayerIndex];
let targetVx = moveX * activePl.speed * 1.2;
let targetVz = moveZ * activePl.speed * 1.2;
activePl.vx += (targetVx - activePl.vx) * 0.2;
activePl.vz += (targetVz - activePl.vz) * 0.2;
activePl.x = Math.max(-29, Math.min(29, activePl.x + activePl.vx));
activePl.z = Math.max(-19, Math.min(19, activePl.z + activePl.vz));
playerTeam.forEach((pl, idx)=>{
if(idx !== controlledPlayerIndex && !pl.isGK){
let targetX = pl.homeX + (ball.x * 0.05);
let targetZ = pl.homeZ + (ball.z * 0.04);
pl.x += (targetX - pl.x) * 0.05;
pl.z += (targetZ - pl.z) * 0.05;
}
});
enemyTeam.forEach((el, idx)=>{
if(el.isGK){
let targetZ = Math.max(-4, Math.min(4, ball.z));
el.x += (27 - el.x) * 0.12;
el.z += (targetZ - el.z) * 0.12;
}else{
if(ball.owner === el){
let myGoalDist = el.x;
if(Math.random() < 0.02 || myGoalDist < -10){
let bestEnemyTarget = null;
let minEd = 999;
enemyTeam.forEach(otherEl => {
if(otherEl !== el && otherEl.x < el.x + 5){
let ed = Math.hypot(otherEl.x - el.x, otherEl.z - el.z);
if(ed < minEd){ minEd = ed; bestEnemyTarget = otherEl; }
}
});
if(bestEnemyTarget){
ball.owner = null;
let ea = Math.atan2(bestEnemyTarget.z - el.z, bestEnemyTarget.x - el.x);
ball.vx = Math.cos(ea) * 1.4;
ball.vz = Math.sin(ea) * 1.4;
} else {
ball.owner = null;
ball.vx = -2.0; ball.vz = (Math.random()-0.5)*1.0;
}
}
} else {
let targetX = el.homeX + (ball.x * 0.08);
let targetZ = el.homeZ + (ball.z * 0.06);
let dx = ball.x - el.x, dz = ball.z - el.z, d = Math.hypot(dx, dz);
if(d < 12 && !ball.owner){
el.x += (dx/d) * el.speed * 0.9;
el.z += (dz/d) * el.speed * 0.9;
} else {
el.x += (targetX - el.x) * 0.05;
el.z += (targetZ - el.z) * 0.05;
}
if(ball.owner && ball.owner.isPlayer){
let playerWithBall = ball.owner;
let tackleDist = Math.hypot(el.x - playerWithBall.x, el.z - playerWithBall.z);
if(tackleDist < 2.0 && Math.random() < 0.08){
ball.owner = el;
ball.owner.isPlayer = false;
}
}
}
}
});
let rDx = ball.x - referee.x, rDz = ball.z - referee.z, rD = Math.hypot(rDx, rDz);
if(rD > 3){ referee.x += rDx/rD * referee.speed; referee.z += rDz/rD * referee.speed; }
if(ball.owner){
ball.x = ball.owner.x + (ball.owner.isPlayer ? 1.0 : -1.0);
ball.z = ball.owner.z;
}else{
ball.x += ball.vx; ball.z += ball.vz;
ball.vx *= ball.friction; ball.vz *= ball.friction;
}
if(!ball.owner){
playerTeam.forEach((pl, idx)=>{
if(Math.hypot(ball.x - pl.x, ball.z - pl.z) < 1.5){
ball.owner = pl;
ball.owner.isPlayer = true;
controlledPlayerIndex = idx;
}
});
enemyTeam.forEach(el=>{
if(Math.hypot(ball.x - el.x, ball.z - el.z) < 1.5){
ball.owner = el;
ball.owner.isPlayer = false;
}
});
}
if(ball.x<-30&&Math.abs(ball.z)<4){
scoreEnemy++;
document.getElementById("e-score").textContent=scoreEnemy;
kickoffTeamIsPlayer = true;
showGoal("TOR FÜR GEGNER!");
resetPositions();
}
else if(ball.x>30&&Math.abs(ball.z)<4){
scorePlayer++;
document.getElementById("p-score").textContent=scorePlayer;
kickoffTeamIsPlayer = false;
showGoal("TOR FÜR DICH!");
resetPositions();
}
if(Math.abs(ball.z)>20){ball.z=Math.sign(ball.z)*20;ball.vz*=-.8;ball.owner=null}
if(Math.abs(ball.x)>30){ball.x=Math.sign(ball.x)*30;ball.vx*=-.8;ball.owner=null}
}
function showGoal(text){document.getElementById("gt").textContent=text;const o=document.getElementById("goal-overlay");o.style.opacity="1";setTimeout(()=>o.style.opacity="0",1200)}
function animate(){updatePhysics();drawGame();requestAnimationFrame(animate)}
function startGame(){
triggerLoadingScreen(true);
animate();
}
if(document.readyState==="loading"){
document.addEventListener("DOMContentLoaded",startGame,{once:true});
}else{
startGame();
}
</script>
</body>
</html>Game Source: Football Stars Deluxe
Creator: BlazeKoala99
Libraries: none
Complexity: complex (651 lines, 29.5 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: football-stars-blazekoala99" to link back to the original. Then publish at arcadelab.ai/publish.