🎮ArcadeLab

Football Stars Deluxe

by BlazeKoala99
651 lines29.5 KB
▶ Play
<!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.