Meta Description" name="description" />
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FIFA 2D - Creado por Lionel Navarro</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial', sans-serif;
}
body {
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
color: #fff;
overflow: hidden;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
#game-container {
width: 1200px;
height: 700px;
position: relative;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.8);
border-radius: 10px;
overflow: hidden;
background: #000;
}
/* Pantallas */
.screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: linear-gradient(to bottom, #0a0a0a, #1a1a1a);
transition: transform 0.5s ease;
z-index: 10;
}
.hidden {
transform: translateX(-100%);
z-index: 1;
}
/* Pantalla de título */
#title-screen {
background: linear-gradient(rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.9)),
url('https://images.unsplash.com/photo-1575361204480-aadea25e6e68?ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&q=80');
background-size: cover;
background-position: center;
}
.game-title {
font-size: 5rem;
text-align: center;
margin-bottom: 10px;
background: linear-gradient(to right, #00a8ff, #0097e6);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 5px 20px rgba(0, 168, 255, 0.5);
font-weight: 900;
letter-spacing: 3px;
font-family: 'Impact', 'Arial Black', sans-serif;
}
.creator {
font-size: 1.2rem;
color: #999;
margin-bottom: 50px;
font-style: italic;
}
.menu-button {
width: 350px;
padding: 18px 30px;
margin: 15px;
font-size: 1.5rem;
font-weight: bold;
background: linear-gradient(to right, #00a8ff, #005a8c);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
text-align: center;
position: relative;
overflow: hidden;
}
.menu-button:hover:not(:disabled) {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 168, 255, 0.4);
background: linear-gradient(to right, #0097e6, #004d73);
}
.menu-button:disabled {
opacity: 0.5;
cursor: not-allowed;
background: #333;
}
.menu-button:active:not(:disabled) {
transform: translateY(0);
}
.coming-soon {
position: relative;
}
.coming-soon::after {
content: "PRÓXIMAMENTE";
position: absolute;
top: -10px;
right: -10px;
background: #e84118;
color: white;
font-size: 0.8rem;
padding: 3px 8px;
border-radius: 10px;
font-weight: bold;
}
/* Pantalla de selección de modo */
.screen-title {
font-size: 3.2rem;
margin-bottom: 40px;
text-align: center;
color: #00a8ff;
text-shadow: 0 3px 10px rgba(0, 168, 255, 0.5);
}
.modes-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 25px;
width: 90%;
max-width: 900px;
margin-bottom: 40px;
}
.mode-button {
padding: 25px 20px;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: white;
font-size: 1.5rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
backdrop-filter: blur(5px);
}
.mode-button:hover {
background: rgba(0, 168, 255, 0.1);
border-color: #00a8ff;
transform: translateY(-5px);
}
.mode-button.selected {
background: rgba(0, 168, 255, 0.2);
border-color: #00a8ff;
box-shadow: 0 5px 20px rgba(0, 168, 255, 0.3);
}
.mode-icon {
font-size: 3rem;
margin-bottom: 15px;
}
/* Pantalla de selección de competición */
.competitions-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
width: 90%;
max-width: 1000px;
height: 400px;
overflow-y: auto;
padding: 15px;
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
margin-bottom: 30px;
scrollbar-width: thin;
scrollbar-color: #00a8ff #222;
}
.competitions-container::-webkit-scrollbar {
width: 8px;
}
.competitions-container::-webkit-scrollbar-track {
background: #222;
border-radius: 10px;
}
.competitions-container::-webkit-scrollbar-thumb {
background: #00a8ff;
border-radius: 10px;
}
.competition-button {
padding: 20px 15px;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: white;
font-size: 1.2rem;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.competition-button:hover {
background: rgba(0, 168, 255, 0.1);
border-color: #00a8ff;
transform: scale(1.05);
}
.competition-button.selected {
background: rgba(0, 168, 255, 0.2);
border-color: #00a8ff;
}
.competition-logo {
width: 60px;
height: 60px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
}
/* Pantalla de selección de equipo - CON COLORES */
.teams-container {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 15px;
width: 95%;
max-width: 1100px;
height: 400px;
overflow-y: auto;
padding: 15px;
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
margin-bottom: 30px;
scrollbar-width: thin;
scrollbar-color: #00a8ff #222;
}
.team-button {
padding: 15px 10px;
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: white;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
position: relative;
overflow: hidden;
}
.team-button:hover {
transform: scale(1.05);
border-color: #00a8ff;
}
.team-button.selected {
border-color: #FFD700;
box-shadow: 0 0 15px rgba(255, 215, 0, 0.5);
}
.team-logo {
width: 50px;
height: 50px;
border-radius: 50%;
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
border: 2px solid rgba(255, 255, 255, 0.3);
}
/* Pantalla de selección de dificultad */
.difficulty-container {
display: flex;
flex-direction: column;
gap: 20px;
width: 80%;
max-width: 700px;
margin-bottom: 40px;
}
.difficulty-button {
padding: 20px 30px;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: white;
font-size: 1.4rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
position: relative;
overflow: hidden;
}
.difficulty-button:hover {
background: rgba(0, 168, 255, 0.1);
transform: translateY(-3px);
}
.difficulty-button.selected {
background: rgba(0, 168, 255, 0.2);
border-color: #00a8ff;
}
.difficulty-very-easy {
border-left: 8px solid #4CAF50;
}
.difficulty-easy {
border-left: 8px solid #8BC34A;
}
.difficulty-normal {
border-left: 8px solid #FFC107;
}
.difficulty-hard {
border-left: 8px solid #FF9800;
}
.difficulty-extreme {
border-left: 8px solid #F44336;
}
/* Pantalla de juego */
#game-screen {
padding: 0;
background: #2a623d;
z-index: 5;
}
#canvas-container {
width: 100%;
height: 100%;
position: relative;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
/* HUD del juego */
.game-hud {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.scoreboard {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
background: rgba(0, 0, 0, 0.85);
padding: 10px 40px;
border-radius: 15px;
font-size: 2.2rem;
font-weight: bold;
min-width: 300px;
justify-content: center;
border: 3px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.7);
}
.scoreboard.libertadores {
border-color: #FFD700;
background: linear-gradient(to right, rgba(0, 0, 0, 0.9), rgba(139, 0, 0, 0.7));
}
.scoreboard.sudamericana {
border-color: #00a8ff;
background: linear-gradient(to right, rgba(0, 0, 0, 0.9), rgba(0, 50, 160, 0.7));
}
.scoreboard.champions {
border-color: #00a8ff;
background: linear-gradient(to right, rgba(0, 0, 0, 0.9), rgba(0, 20, 100, 0.7));
}
.scoreboard.mundial {
border-color: #F44336;
background: linear-gradient(to right, rgba(0, 0, 0, 0.9), rgba(200, 0, 0, 0.7));
}
.scoreboard.copa-america {
border-color: #00a8ff;
background: linear-gradient(to right, rgba(0, 0, 0, 0.9), rgba(0, 100, 200, 0.7));
}
.scoreboard.eurocopa {
border-color: #FFD700;
background: linear-gradient(to right, rgba(0, 0, 0, 0.9), rgba(255, 215, 0, 0.2));
}
.team-score {
padding: 0 20px;
font-size: 2.8rem;
text-shadow: 0 0 10px currentColor;
}
.player-score {
color: #00a8ff;
}
.cpu-score {
color: #e84118;
}
.score-separator {
color: #fff;
font-size: 2.5rem;
margin: 0 10px;
}
.team-name-hud {
position: absolute;
top: 90px;
font-size: 1.4rem;
font-weight: bold;
background: rgba(0, 0, 0, 0.7);
padding: 8px 20px;
border-radius: 8px;
border: 2px solid rgba(255, 255, 255, 0.2);
}
.team-name-hud.player {
left: 30px;
color: #00a8ff;
border-color: #00a8ff;
}
.team-name-hud.cpu {
right: 30px;
color: #e84118;
border-color: #e84118;
}
.game-time {
position: absolute;
top: 25px;
right: 30px;
background: rgba(0, 0, 0, 0.8);
padding: 10px 20px;
border-radius: 8px;
font-size: 1.5rem;
font-weight: bold;
border: 2px solid rgba(255, 255, 255, 0.2);
}
.controls-info {
position: absolute;
bottom: 25px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
padding: 12px 25px;
border-radius: 10px;
font-size: 1.1rem;
text-align: center;
border: 2px solid rgba(255, 255, 255, 0.1);
}
.key {
display: inline-block;
background: rgba(0, 168, 255, 0.3);
padding: 5px 12px;
border-radius: 5px;
margin: 0 5px;
font-weight: bold;
border: 1px solid rgba(0, 168, 255, 0.5);
}
/* Panel de relator (esquina superior derecha) */
.commentator-panel {
position: absolute;
top: 100px;
right: 20px;
width: 300px;
background: rgba(0, 0, 0, 0.8);
padding: 12px 15px;
border-radius: 8px;
font-size: 1rem;
text-align: left;
min-height: 60px;
border-left: 4px solid #00a8ff;
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.5);
z-index: 20;
}
.commentator-icon {
position: absolute;
left: -25px;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
background: #1a5fb4;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
}
/* Tarjetas amarillas/rojas */
.cards-hud {
position: absolute;
top: 90px;
right: 330px;
display: flex;
flex-direction: column;
gap: 5px;
}
.card-indicator {
display: flex;
align-items: center;
background: rgba(0, 0, 0, 0.7);
padding: 5px 10px;
border-radius: 5px;
font-size: 0.9rem;
}
.yellow-card {
color: #FFD700;
margin-right: 5px;
}
.red-card {
color: #FF0000;
margin-right: 5px;
}
/* Modal de faltas */
.foul-modal {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
.foul-title {
font-size: 3rem;
color: #FFD700;
margin-bottom: 20px;
text-shadow: 0 0 10px #FFD700;
}
.foul-details {
font-size: 1.5rem;
margin-bottom: 30px;
text-align: center;
}
.card-display {
font-size: 4rem;
margin: 20px 0;
}
/* Modal de penales */
.penalty-modal {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
.penalty-title {
font-size: 3rem;
color: #FFD700;
margin-bottom: 30px;
text-shadow: 0 0 10px #FFD700;
}
.penalty-instructions {
font-size: 1.5rem;
margin-bottom: 40px;
text-align: center;
}
.penalty-meter {
width: 300px;
height: 30px;
background: #333;
border-radius: 15px;
margin: 20px 0;
overflow: hidden;
position: relative;
}
.penalty-fill {
height: 100%;
width: 0%;
background: linear-gradient(to right, #4CAF50, #FF9800, #F44336);
transition: width 0.1s linear;
}
.penalty-target {
position: absolute;
top: 0;
width: 40px;
height: 100%;
background: rgba(255, 255, 255, 0.3);
border: 2px solid #FFD700;
left: 50%;
transform: translateX(-50%);
}
/* Animaciones */
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes goalAnimation {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
@keyframes passAnimation {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
@keyframes shotAnimation {
0% { transform: scale(1); }
50% { transform: scale(1.3); }
100% { transform: scale(1); }
}
@keyframes tackleAnimation {
0% { transform: scale(1); }
50% { transform: scale(1.1); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
.pulse {
animation: pulse 0.5s ease;
}
.goal-animation {
animation: goalAnimation 0.8s ease;
}
.pass-animation {
animation: passAnimation 0.3s ease;
}
.shot-animation {
animation: shotAnimation 0.4s ease;
}
.tackle-animation {
animation: tackleAnimation 0.2s ease;
}
/* Botones de navegación */
.nav-buttons {
display: flex;
gap: 25px;
margin-top: 20px;
}
.nav-button {
padding: 15px 30px;
background: rgba(255, 255, 255, 0.1);
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 10px;
color: white;
font-size: 1.2rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
}
.nav-button:hover {
background: rgba(255, 255, 255, 0.2);
}
.nav-button.primary {
background: linear-gradient(to right, #00a8ff, #005a8c);
}
.back-button {
position: absolute;
top: 20px;
left: 20px;
padding: 12px 25px;
background: rgba(255, 255, 255, 0.1);
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 10px;
color: white;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
z-index: 20;
}
.back-button:hover {
background: rgba(255, 255, 255, 0.2);
}
/* Responsive */
@media (max-width: 1250px) {
#game-container {
width: 1000px;
height: 600px;
}
.teams-container {
grid-template-columns: repeat(4, 1fr);
}
.commentator-panel {
width: 250px;
right: 15px;
top: 80px;
}
}
@media (max-width: 1050px) {
#game-container {
width: 900px;
height: 550px;
}
.game-title {
font-size: 4rem;
}
.teams-container {
grid-template-columns: repeat(3, 1fr);
}
.competitions-container {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 950px) {
#game-container {
width: 100%;
height: 100%;
border-radius: 0;
}
}
</style>
</head>
<body>
<div id="game-container">
<!-- Pantalla de título -->
<div id="title-screen" class="screen">
<h1 class="game-title">FIFA 2D</h1>
<p class="creator">Creado por Lionel Navarro</p>
<button id="friendly-match" class="menu-button">Partido Amistoso</button>
<button id="cups-mode" class="menu-button">Copas</button>
<button id="career-mode" class="menu-button coming-soon" disabled>Modo Carrera</button>
<button id="exit-game" class="menu-button">Salir del Juego</button>
</div>
<!-- Pantalla de selección de modo -->
<div id="mode-screen" class="screen hidden">
<h2 class="screen-title">Selecciona un Modo</h2>
<div class="modes-container" id="modes-container">
<!-- Los modos se generarán con JavaScript -->
</div>
<div class="nav-buttons">
<button id="back-from-mode" class="nav-button">Volver</button>
<button id="select-mode" class="nav-button primary" disabled>Seleccionar</button>
</div>
</div>
<!-- Pantalla de selección de competición -->
<div id="competition-screen" class="screen hidden">
<h2 class="screen-title" id="competition-title">Selecciona una Competición</h2>
<div class="competitions-container" id="competitions-container">
<!-- Las competiciones se generarán con JavaScript -->
</div>
<div class="nav-buttons">
<button id="back-from-competition" class="nav-button">Volver</button>
<button id="select-competition" class="nav-button primary" disabled>Seleccionar</button>
</div>
</div>
<!-- Pantalla de selección de equipo -->
<div id="team-screen" class="screen hidden">
<h2 class="screen-title" id="team-selection-title">Selecciona tu Equipo</h2>
<div class="teams-container" id="teams-container">
<!-- Los equipos se generarán con JavaScript -->
</div>
<div class="nav-buttons">
<button id="back-from-team" class="nav-button">Volver</button>
<button id="select-team" class="nav-button primary" disabled>Seleccionar Equipo</button>
</div>
</div>
<!-- Pantalla de selección de equipo rival -->
<div id="opponent-screen" class="screen hidden">
<h2 class="screen-title" id="opponent-selection-title">Selecciona el Equipo Rival</h2>
<div class="teams-container" id="opponent-teams-container">
<!-- Los equipos rivales se generarán con JavaScript -->
</div>
<div class="nav-buttons">
<button id="back-from-opponent" class="nav-button">Volver</button>
<button id="select-opponent" class="nav-button primary" disabled>Seleccionar Rival</button>
</div>
</div>
<!-- Pantalla de selección de dificultad -->
<div id="difficulty-screen" class="screen hidden">
<h2 class="screen-title">Selecciona la Dificultad</h2>
<div class="difficulty-container" id="difficulty-container">
<!-- Las dificultades se generarán con JavaScript -->
</div>
<div class="nav-buttons">
<button id="back-from-difficulty" class="nav-button">Volver</button>
<button id="start-game" class="nav-button primary" disabled>Comenzar Partido</button>
</div>
</div>
<!-- Pantalla de juego -->
<div id="game-screen" class="screen hidden">
<button id="back-from-game" class="back-button">Menú Principal</button>
<div id="canvas-container">
<canvas id="game-canvas"></canvas>
<div class="game-hud">
<div class="scoreboard" id="scoreboard">
<div class="team-score player-score" id="player-score">0</div>
<div class="score-separator">-</div>
<div class="team-score cpu-score" id="cpu-score">0</div>
</div>
<div class="team-name-hud player" id="player-team-name">Jugador</div>
<div class="team-name-hud cpu" id="cpu-team-name">CPU</div>
<div class="game-time" id="game-time">00:00</div>
<div class="controls-info">
<span>PASE: <span class="key">ESPACIO</span></span> |
<span>CHUTAR: <span class="key">ESPACIO (mantener)</span></span> |
<span>ROBAR: <span class="key">X</span></span> |
<span>CAMBIAR JUGADOR: <span class="key">TAB</span></span>
</div>
<div class="commentator-panel">
<div class="commentator-icon">🎙️</div>
<div id="commentary-text">¡Bienvenidos al partido!</div>
</div>
<div class="cards-hud">
<div class="card-indicator player-cards" id="player-cards">
<span class="yellow-card">🟨</span> <span id="player-yellow">0</span>
<span class="red-card">🟥</span> <span id="player-red">0</span>
</div>
<div class="card-indicator cpu-cards" id="cpu-cards">
<span class="yellow-card">🟨</span> <span id="cpu-yellow">0</span>
<span class="red-card">🟥</span> <span id="cpu-red">0</span>
</div>
</div>
</div>
<!-- Modal de faltas -->
<div id="foul-modal" class="foul-modal hidden">
<h2 class="foul-title" id="foul-title">FALTA</h2>
<div class="foul-details" id="foul-details"></div>
<div class="card-display" id="card-display"></div>
<button id="continue-foul" class="menu-button" style="width: 200px;">Continuar</button>
</div>
<!-- Modal de penales -->
<div id="penalty-modal" class="penalty-modal hidden">
<h2 class="penalty-title">PENAL</h2>
<div class="penalty-instructions" id="penalty-instructions">
Presiona ESPACIO cuando la barra esté en la zona dorada
</div>
<div class="penalty-meter">
<div class="penalty-fill" id="penalty-fill"></div>
<div class="penalty-target"></div>
</div>
<div id="penalty-score" style="font-size: 2rem; margin: 20px 0;">
Jugador: 0 - 0 :CPU
</div>
<button id="start-penalty" class="menu-button" style="width: 200px;">Comenzar Penal</button>
</div>
</div>
</div>
</div>
<script>
// Datos del juego - CON COLORES DE EQUIPOS REALES
const gameData = {
modes: {
'amistoso': {
name: 'Partido Amistoso',
icon: '⚽',
competitions: ['amistoso']
},
'copas': {
name: 'Copas',
icon: '🏆',
competitions: ['libertadores', 'sudamericana', 'champions', 'mundial', 'copa-america', 'eurocopa']
}
},
competitions: {
'amistoso': {
name: 'Partido Amistoso',
icon: '⚽',
color: '#00a8ff',
allTeams: {},
tournamentMode: false
},
'libertadores': {
name: 'Copa Libertadores',
icon: '🏆',
color: '#FFD700',
teams: [
'River Plate', 'Boca Juniors', 'Flamengo', 'Palmeiras',
'Atlético Mineiro', 'Corinthians', 'Nacional (URU)', 'Peñarol',
'Olimpia (PAR)', 'Cerro Porteño', 'Independiente del Valle', 'LDU Quito',
'Universidad de Chile', 'Colo-Colo', 'Atlético Nacional', 'Millonarios'
],
tournamentMode: true,
currentRound: 0,
tournamentBracket: []
},
'sudamericana': {
name: 'Copa Sudamericana',
icon: '🌎',
color: '#00a8ff',
teams: [
'Racing Club', 'San Lorenzo', 'Internacional', 'São Paulo',
'Athletico Paranaense', 'Emelec', 'Blooming', 'The Strongest',
'Deportivo Cali', 'Junior', 'Always Ready', 'Guaraní',
'Universitario', 'Sport Huancayo', 'Deportivo Lara', 'Metropolitano'
],
tournamentMode: true,
currentRound: 0,
tournamentBracket: []
},
'champions': {
name: 'Champions League',
icon: '⭐',
color: '#00a8ff',
teams: [
'Real Madrid', 'Barcelona', 'Manchester City', 'Liverpool',
'Bayern Munich', 'PSG', 'Chelsea', 'AC Milan',
'Inter Milan', 'Juventus', 'Borussia Dortmund', 'Atlético Madrid',
'Benfica', 'Porto', 'Ajax', 'RB Leipzig'
],
tournamentMode: true,
currentRound: 0,
tournamentBracket: []
},
'mundial': {
name: 'Mundial de Selecciones',
icon: '🌍',
color: '#F44336',
teams: [
'Argentina', 'Brasil', 'Francia', 'Alemania',
'España', 'Inglaterra', 'Italia', 'Portugal',
'Países Bajos', 'Bélgica', 'Croacia', 'Uruguay',
'México', 'Estados Unidos', 'Japón', 'Senegal'
],
tournamentMode: true,
currentRound: 0,
tournamentBracket: []
},
'copa-america': {
name: 'Copa América',
icon: '🇦🇷',
color: '#00a8ff',
teams: [
'Argentina', 'Brasil', 'Uruguay', 'Colombia',
'Chile', 'Perú', 'Paraguay', 'Ecuador',
'Venezuela', 'Bolivia', 'Costa Rica', 'México',
'Estados Unidos', 'Canadá', 'Jamaica', 'Panamá'
],
tournamentMode: true,
currentRound: 0,
tournamentBracket: []
},
'eurocopa': {
name: 'Eurocopa',
icon: '🇪🇺',
color: '#FFD700',
teams: [
'Francia', 'Alemania', 'España', 'Inglaterra',
'Italia', 'Portugal', 'Países Bajos', 'Bélgica',
'Croacia', 'Dinamarca', 'Suiza', 'Polonia',
'Suecia', 'Gales', 'Ucrania', 'Austria'
],
tournamentMode: true,
currentRound: 0,
tournamentBracket: []
}
},
// Todas las ligas con sus equipos y colores REALES
leagues: {
'española': {
name: 'LaLiga Española',
flag: '🇪🇸',
teams: [
{name: 'Real Madrid', colors: {primary: '#FFFFFF', secondary: '#00529F'}},
{name: 'Barcelona', colors: {primary: '#A50044', secondary: '#004D98'}},
{name: 'Atlético Madrid', colors: {primary: '#CB3524', secondary: '#1F2D5A'}},
{name: 'Sevilla', colors: {primary: '#D81B26', secondary: '#FFFFFF'}},
{name: 'Real Sociedad', colors: {primary: '#0073CF', secondary: '#FFFFFF'}},
{name: 'Betis', colors: {primary: '#00954C', secondary: '#FFFFFF'}},
{name: 'Villarreal', colors: {primary: '#FFCD00', secondary: '#00529F'}},
{name: 'Athletic Club', colors: {primary: '#ED1C24', secondary: '#FFFFFF'}},
{name: 'Valencia', colors: {primary: '#F7B11B', secondary: '#FFFFFF'}},
{name: 'Osasuna', colors: {primary: '#003366', secondary: '#D0103A'}},
{name: 'Celta Vigo', colors: {primary: '#67B2E8', secondary: '#FFFFFF'}},
{name: 'Mallorca', colors: {primary: '#FF0000', secondary: '#000000'}},
{name: 'Rayo Vallecano', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Girona', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Getafe', colors: {primary: '#0047AB', secondary: '#FFFFFF'}},
{name: 'Almería', colors: {primary: '#FF0000', secondary: '#FFFFFF'}}
]
},
'premier': {
name: 'Premier League',
flag: '🏴',
teams: [
{name: 'Manchester City', colors: {primary: '#6CABDD', secondary: '#FFFFFF'}},
{name: 'Arsenal', colors: {primary: '#EF0107', secondary: '#FFFFFF'}},
{name: 'Manchester United', colors: {primary: '#DA291C', secondary: '#FBE122'}},
{name: 'Liverpool', colors: {primary: '#C8102E', secondary: '#00B2A9'}},
{name: 'Chelsea', colors: {primary: '#034694', secondary: '#DBA111'}},
{name: 'Tottenham', colors: {primary: '#132257', secondary: '#FFFFFF'}},
{name: 'Newcastle', colors: {primary: '#241F20', secondary: '#FFFFFF'}},
{name: 'Aston Villa', colors: {primary: '#95BFE5', secondary: '#7A003C'}},
{name: 'Brighton', colors: {primary: '#0057B8', secondary: '#FFFFFF'}},
{name: 'West Ham', colors: {primary: '#7A263A', secondary: '#1BB1E7'}},
{name: 'Brentford', colors: {primary: '#E30613', secondary: '#FFFFFF'}},
{name: 'Crystal Palace', colors: {primary: '#1B458F', secondary: '#C4122E'}},
{name: 'Everton', colors: {primary: '#003399', secondary: '#FFFFFF'}},
{name: 'Leicester', colors: {primary: '#003090', secondary: '#FDBE11'}},
{name: 'Leeds', colors: {primary: '#FFCD00', secondary: '#1D428A'}},
{name: 'Southampton', colors: {primary: '#D71920', secondary: '#FFFFFF'}}
]
},
'francesa': {
name: 'Ligue 1',
flag: '🇫🇷',
teams: [
{name: 'PSG', colors: {primary: '#004170', secondary: '#DA291C'}},
{name: 'Marsella', colors: {primary: '#0C2340', secondary: '#D21034'}},
{name: 'Mónaco', colors: {primary: '#D52B1E', secondary: '#FFFFFF'}},
{name: 'Lyon', colors: {primary: '#FFFFFF', secondary: '#0055A4'}},
{name: 'Lille', colors: {primary: '#ED1C24', secondary: '#0055A4'}},
{name: 'Rennes', colors: {primary: '#E3001B', secondary: '#000000'}},
{name: 'Niza', colors: {primary: '#E2001A', secondary: '#000000'}},
{name: 'Lens', colors: {primary: '#FF0000', secondary: '#FFD700'}},
{name: 'Montpellier', colors: {primary: '#F7B11B', secondary: '#0055A4'}},
{name: 'Toulouse', colors: {primary: '#69002F', secondary: '#FFFFFF'}},
{name: 'Strasbourg', colors: {primary: '#0000FF', secondary: '#FFFFFF'}},
{name: 'Nantes', colors: {primary: '#FFD700', secondary: '#0066CC'}},
{name: 'Reims', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Ajaccio', colors: {primary: '#E4001B', secondary: '#FFFFFF'}},
{name: 'Auxerre', colors: {primary: '#002395', secondary: '#FFFFFF'}},
{name: 'Troyes', colors: {primary: '#0066CC', secondary: '#FFFFFF'}}
]
},
'alemana': {
name: 'Bundesliga',
flag: '🇩🇪',
teams: [
{name: 'Bayern Munich', colors: {primary: '#DC052D', secondary: '#FFFFFF'}},
{name: 'Borussia Dortmund', colors: {primary: '#FDE100', secondary: '#000000'}},
{name: 'RB Leipzig', colors: {primary: '#FFFFFF', secondary: '#DD052C'}},
{name: 'Bayer Leverkusen', colors: {primary: '#E32221', secondary: '#000000'}},
{name: 'Union Berlin', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Freiburg', colors: {primary: '#000000', secondary: '#FFFFFF'}},
{name: 'Wolfsburg', colors: {primary: '#009933', secondary: '#FFFFFF'}},
{name: 'Eintracht Frankfurt', colors: {primary: '#E1000F', secondary: '#000000'}},
{name: 'Mainz', colors: {primary: '#ED1C24', secondary: '#FFFFFF'}},
{name: 'Borussia Mönchengladbach', colors: {primary: '#000000', secondary: '#FFFFFF'}},
{name: 'Köln', colors: {primary: '#ED1C24', secondary: '#FFFFFF'}},
{name: 'Hoffenheim', colors: {primary: '#1C5AA5', secondary: '#FFFFFF'}},
{name: 'Werder Bremen', colors: {primary: '#1D9053', secondary: '#FFFFFF'}},
{name: 'Schalke 04', colors: {primary: '#004D9F', secondary: '#FFFFFF'}},
{name: 'Augsburg', colors: {primary: '#BA3733', secondary: '#FFFFFF'}},
{name: 'Stuttgart', colors: {primary: '#FFFFFF', secondary: '#E32221'}}
]
},
'italiana': {
name: 'Serie A',
flag: '🇮🇹',
teams: [
{name: 'Juventus', colors: {primary: '#000000', secondary: '#FFFFFF'}},
{name: 'Inter Milan', colors: {primary: '#0066CC', secondary: '#000000'}},
{name: 'AC Milan', colors: {primary: '#FB090B', secondary: '#000000'}},
{name: 'Napoli', colors: {primary: '#12A0D7', secondary: '#FFFFFF'}},
{name: 'Roma', colors: {primary: '#9B1C2C', secondary: '#F9D616'}},
{name: 'Lazio', colors: {primary: '#9ACD32', secondary: '#FFFFFF'}},
{name: 'Atalanta', colors: {primary: '#0054A5', secondary: '#000000'}},
{name: 'Fiorentina', colors: {primary: '#7F3F98', secondary: '#FFFFFF'}},
{name: 'Bologna', colors: {primary: '#AB0520', secondary: '#0C2340'}},
{name: 'Torino', colors: {primary: '#8B0000', secondary: '#FFFFFF'}},
{name: 'Udinese', colors: {primary: '#FFFFFF', secondary: '#000000'}},
{name: 'Sassuolo', colors: {primary: '#0066CC', secondary: '#000000'}},
{name: 'Empoli', colors: {primary: '#0054A5', secondary: '#FFFFFF'}},
{name: 'Salernitana', colors: {primary: '#8B0000', secondary: '#FFFFFF'}},
{name: 'Lecce', colors: {primary: '#FFD700', secondary: '#FF0000'}},
{name: 'Spezia', colors: {primary: '#FFFFFF', secondary: '#000000'}}
]
},
'argentina': {
name: 'Torneo Betano 2025',
flag: '🇦🇷',
teams: [
{name: 'Argentinos Juniors', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Atlético Tucumán', colors: {primary: '#FFFFFF', secondary: '#0000FF'}},
{name: 'Banfield', colors: {primary: '#006847', secondary: '#FFFFFF'}},
{name: 'Barracas Central', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Belgrano', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Boca Juniors', colors: {primary: '#0000FF', secondary: '#FFFF00'}},
{name: 'Central Córdoba', colors: {primary: '#FF0000', secondary: '#000000'}},
{name: 'Defensa y Justicia', colors: {primary: '#FFD700', secondary: '#000000'}},
{name: 'Deportivo Riestra', colors: {primary: '#0000FF', secondary: '#FFFFFF'}},
{name: 'Estudiantes', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Gimnasia LP', colors: {primary: '#0000FF', secondary: '#FFFFFF'}},
{name: 'Godoy Cruz', colors: {primary: '#0000FF', secondary: '#FFFFFF'}},
{name: 'Huracán', colors: {primary: '#FFFFFF', secondary: '#FF0000'}},
{name: 'Independiente', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Independiente Rivadavia', colors: {primary: '#0000FF', secondary: '#FFFFFF'}},
{name: 'Instituto', colors: {primary: '#FF0000', secondary: '#000000'}},
{name: 'Lanús', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Newell\'s', colors: {primary: '#FF0000', secondary: '#000000'}},
{name: 'Platense', colors: {primary: '#7F0000', secondary: '#FFFFFF'}},
{name: 'Racing Club', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'River Plate', colors: {primary: '#FFFFFF', secondary: '#FF0000'}},
{name: 'Rosario Central', colors: {primary: '#FFD700', secondary: '#0000FF'}},
{name: 'San Lorenzo', colors: {primary: '#FF0000', secondary: '#0000FF'}},
{name: 'San Martín de San Juan', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Sarmiento', colors: {primary: '#006600', secondary: '#FFFFFF'}},
{name: 'Talleres', colors: {primary: '#0000FF', secondary: '#FFFFFF'}},
{name: 'Tigre', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Unión', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Vélez', colors: {primary: '#FFFFFF', secondary: '#0000FF'}}
]
},
'nacional': {
name: 'Primera Nacional',
flag: '🇦🇷',
teams: [
{name: 'Racing Córdoba', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Deportivo Madryn', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Atlanta', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Tristán Suárez', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Gimnasia y Tiro', colors: {primary: '#FF0000', secondary: '#000000'}},
{name: 'San Miguel', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'San Martín Tucumán', colors: {primary: '#FFFFFF', secondary: '#0066CC'}},
{name: 'Deportivo Maipú', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Patronato', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Colegiales', colors: {primary: '#0000FF', secondary: '#FFFFFF'}},
{name: 'All Boys', colors: {primary: '#FFFFFF', secondary: '#0000FF'}},
{name: 'Ferro Carril Oeste', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Los Andes', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Güemes', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Quilmes', colors: {primary: '#FFFFFF', secondary: '#0066CC'}},
{name: 'Almagro', colors: {primary: '#FF0000', secondary: '#FFFFFF'}}
]
},
'brasileña': {
name: 'Brasileirão',
flag: '🇧🇷',
teams: [
{name: 'Flamengo', colors: {primary: '#FF0000', secondary: '#000000'}},
{name: 'Palmeiras', colors: {primary: '#006600', secondary: '#FFFFFF'}},
{name: 'Corinthians', colors: {primary: '#FFFFFF', secondary: '#000000'}},
{name: 'São Paulo', colors: {primary: '#FFFFFF', secondary: '#FF0000'}},
{name: 'Santos', colors: {primary: '#FFFFFF', secondary: '#000000'}},
{name: 'Grêmio', colors: {primary: '#0066CC', secondary: '#000000'}},
{name: 'Internacional', colors: {primary: '#FF0000', secondary: '#FFFFFF'}},
{name: 'Atlético Mineiro', colors: {primary: '#000000', secondary: '#FFFFFF'}},
{name: 'Cruzeiro', colors: {primary: '#0066CC', secondary: '#FFFFFF'}},
{name: 'Fluminense', colors: {primary: '#8B0000', secondary: '#0066CC'}},
{name: 'Botafogo', colors: {primary: '#000000', secondary: '#FFFFFF'}},
{name: 'Vasco da Gama', colors: {primary: '#000000', secondary: '#FFFFFF'}},
{name: 'Bahia', colors: {primary: '#0066CC', secondary: '#FF0000'}},
{name: 'Sport Recife', colors: {primary: '#FF0000', secondary: '#000000'}},
{name: 'Fortaleza', colors: {primary: '#0066CC', secondary: '#FF0000'}},
{name: 'Ceará', colors: {primary: '#000000', secondary: '#FFFFFF'}}
]
},
'especial': {
name: 'Equipo Especial',
flag: '⭐',
teams: [
{name: 'Equipo Especial', colors: {primary: '#00a8ff', secondary: '#005a8c'}}
]
}
},
difficulties: [
{ id: 'very-easy', name: 'Muy Fácil', color: '#4CAF50', speed: 0.7, aiDelay: 1000, aiAccuracy: 0.3, aiAggression: 0.3, aiShootingRange: 150 },
{ id: 'easy', name: 'Fácil', color: '#8BC34A', speed: 0.9, aiDelay: 700, aiAccuracy: 0.5, aiAggression: 0.5, aiShootingRange: 200 },
{ id: 'normal', name: 'Normal', color: '#FFC107', speed: 1.0, aiDelay: 500, aiAccuracy: 0.7, aiAggression: 0.7, aiShootingRange: 250 },
{ id: 'hard', name: 'Difícil', color: '#FF9800', speed: 1.3, aiDelay: 300, aiAccuracy: 0.85, aiAggression: 0.8, aiShootingRange: 300 },
{ id: 'extreme', name: 'Extremo', color: '#F44336', speed: 1.8, aiDelay: 150, aiAccuracy: 0.95, aiAggression: 1.0, aiShootingRange: 400 }
],
selectedMode: null,
selectedCompetition: null,
selectedLeague: null,
selectedTeam: null,
selectedOpponent: null,
selectedDifficulty: null,
gameState: 'menu',
score: { player: 0, cpu: 0 },
matchTime: 0,
maxMatchTime: 300,
commentaryMessages: [
"¡Comienza el partido!",
"El balón está en juego",
"Qué jugada más interesante",
"Se avecina una oportunidad de gol",
"El portero está atento",
"¡Cuidado con el contraataque!",
"La defensa se mantiene firme",
"El mediocampo controla el ritmo",
"¡Qué pase tan preciso!",
"La afición está vibrante",
"Falta en una zona peligrosa",
"¡Otra oportunidad desaprovechada!",
"El entrenador da instrucciones",
"Cambio de ritmo en el juego",
"¡GOLAZO! ¡Increíble!",
"El árbitro señala falta",
"Tiempo de descuento",
"El partido está muy parejo",
"La posesión es clave en este encuentro",
"¡Qué remate! Por poco y entra",
"¡Chute desde lejos!",
"El arquero responde con una gran atajada",
"Jugada de contraataque rápida",
"La pelota se va por la línea",
"¡Corner! Oportunidad de gol"
],
specialTeamPlayers: {
goalkeeper: { name: "Fran Burela", number: 1 },
defenders: [
{ name: "Masma", number: 4 },
{ name: "Benja Perez", number: 5 }
],
forwards: [
{ name: "Lio", number: 10 },
{ name: "Chochi", number: 7 }
]
},
// Sistema de torneo
tournament: {
active: false,
currentRound: 0,
bracket: [],
playerTeam: null,
eliminated: false
},
// Sistema de penales
penalties: {
active: false,
playerScore: 0,
cpuScore: 0,
currentAttempt: 0,
maxAttempts: 5,
penaltyInProgress: false,
penaltyPower: 0,
penaltyDirection: 0,
penaltyTimer: 0,
penaltySpeed: 0.05
},
// Sistema de faltas y tarjetas
fouls: {
active: false,
foulingPlayer: null,
fouledPlayer: null,
foulType: null, // 'tackle', 'push', 'handball'
foulLocation: {x: 0, y: 0},
isPenalty: false,
cards: {
player: { yellow: 0, red: 0 },
cpu: { yellow: 0, red: 0 }
}
}
};
// Elementos del DOM
const screens = {
title: document.getElementById('title-screen'),
mode: document.getElementById('mode-screen'),
competition: document.getElementById('competition-screen'),
team: document.getElementById('team-screen'),
opponent: document.getElementById('opponent-screen'),
difficulty: document.getElementById('difficulty-screen'),
game: document.getElementById('game-screen')
};
// Variables del juego
let canvas, ctx;
let gameLoopId;
let keys = {};
let playerTeam = [];
let cpuTeam = [];
let ball = {};
let activePlayer = null;
let lastCommentaryTime = 0;
let commentaryInterval = 5000;
let lastAIAction = 0;
let aiActionDelay = 500;
let gameActive = false;
let animationTimer = 0;
let celebrationTimer = 0;
let isCelebrating = false;
let celebratingTeam = null;
let corners = 0;
let freeKicks = 0;
let lastGoalBy = null;
let goalScorer = null;
let ballTrail = [];
let ballRotation = 0;
let lastPlayerSwitch = 0;
let playerSwitchDelay = 300;
let goalkeepers = { player: null, cpu: null };
let lastTackleTime = 0;
let tackleCooldown = 500;
let foulCooldown = 2000;
let lastFoulTime = 0;
let isFoulActive = false;
let foulTimer = 0;
let commentaryLines = [];
let currentCommentary = 0;
// Inicialización del juego
document.addEventListener('DOMContentLoaded', () => {
initEventListeners();
renderModes();
renderDifficulties();
prepareAllTeamsForFriendly();
});
// Preparar todos los equipos para amistosos
function prepareAllTeamsForFriendly() {
gameData.competitions.amistoso.allTeams = {};
for (const leagueId in gameData.leagues) {
gameData.competitions.amistoso.allTeams[leagueId] = gameData.leagues[leagueId];
}
}
// Inicializar event listeners
function initEventListeners() {
// Botones de navegación
document.getElementById('friendly-match').addEventListener('click', () => {
gameData.selectedMode = 'amistoso';
showScreen('mode');
});
document.getElementById('cups-mode').addEventListener('click', () => {
gameData.selectedMode = 'copas';
showScreen('mode');
});
document.getElementById('exit-game').addEventListener('click', () => {
alert('¡Gracias por jugar FIFA 2D!');
});
// Navegación entre pantallas
document.getElementById('back-from-mode').addEventListener('click', () => showScreen('title'));
document.getElementById('select-mode').addEventListener('click', () => showScreen('competition'));
document.getElementById('back-from-competition').addEventListener('click', () => showScreen('mode'));
document.getElementById('select-competition').addEventListener('click', selectCompetition);
document.getElementById('back-from-team').addEventListener('click', () => {
if (gameData.selectedMode === 'amistoso' && gameData.selectedCompetition === 'amistoso') {
showScreen('competition');
} else {
showScreen('competition');
}
});
document.getElementById('select-team').addEventListener('click', () => showScreen('opponent'));
document.getElementById('back-from-opponent').addEventListener('click', () => showScreen('team'));
document.getElementById('select-opponent').addEventListener('click', () => showScreen('difficulty'));
document.getElementById('back-from-difficulty').addEventListener('click', () => showScreen('opponent'));
document.getElementById('start-game').addEventListener('click', startGame);
document.getElementById('back-from-game').addEventListener('click', () => {
stopGame();
showScreen('title');
resetGameData();
});
// Botón de faltas
document.getElementById('continue-foul').addEventListener('click', continueAfterFoul);
// Botón de penales
document.getElementById('start-penalty').addEventListener('click', startPenaltyShot);
// Controles de teclado
document.addEventListener('keydown', (e) => {
const key = e.key.toLowerCase();
keys[key] = true;
// Espacio para pase/chute/penal
if (key === ' ' && gameActive && !isCelebrating && !isFoulActive) {
e.preventDefault();
if (gameData.penalties.active) {
if (!gameData.penalties.penaltyInProgress) {
startPenaltyShot();
} else {
executePenaltyShot();
}
} else {
if (!ball.spacePressed) {
ball.spacePressed = true;
ball.spacePressTime = Date.now();
ball.spaceStartX = ball.x;
ball.spaceStartY = ball.y;
}
}
}
// Robar pelota con X
if (key === 'x' && gameActive && !isCelebrating && !isFoulActive) {
e.preventDefault();
attemptTackle();
}
// Cambiar jugador con TAB
if (key === 'tab' && gameActive && Date.now() - lastPlayerSwitch > playerSwitchDelay) {
e.preventDefault();
switchPlayer();
lastPlayerSwitch = Date.now();
}
// Pausa con P
if (key === 'p' && gameActive) {
togglePause();
}
});
document.addEventListener('keyup', (e) => {
const key = e.key.toLowerCase();
keys[key] = false;
// Espacio liberado
if (key === ' ' && gameActive && !isCelebrating && !gameData.penalties.active && !isFoulActive) {
if (ball.spacePressed) {
const pressDuration = Date.now() - ball.spacePressTime;
// Si fue un tap rápido (menos de 200ms) es un pase
if (pressDuration < 200) {
passBall();
} else {
// Si fue más largo, es un chute
shootBall(pressDuration);
}
ball.spacePressed = false;
ball.spacePressTime = 0;
}
}
});
}
// Renderizar modos
function renderModes() {
const container = document.getElementById('modes-container');
container.innerHTML = '';
Object.keys(gameData.modes).forEach(modeId => {
const mode = gameData.modes[modeId];
const button = document.createElement('button');
button.className = 'mode-button';
button.innerHTML = `
<div class="mode-icon">${mode.icon}</div>
<div>${mode.name}</div>
`;
button.addEventListener('click', () => {
document.querySelectorAll('.mode-button').forEach(btn => btn.classList.remove('selected'));
button.classList.add('selected');
gameData.selectedMode = modeId;
document.getElementById('select-mode').disabled = false;
renderCompetitions();
});
container.appendChild(button);
});
}
// Renderizar competiciones
function renderCompetitions() {
const container = document.getElementById('competitions-container');
container.innerHTML = '';
if (!gameData.selectedMode) return;
const mode = gameData.modes[gameData.selectedMode];
mode.competitions.forEach(compId => {
const comp = gameData.competitions[compId];
const button = document.createElement('button');
button.className = 'competition-button';
button.innerHTML = `
<div class="competition-logo">${comp.icon}</div>
<div>${comp.name}</div>
`;
button.addEventListener('click', () => {
document.querySelectorAll('.competition-button').forEach(btn => btn.classList.remove('selected'));
button.classList.add('selected');
gameData.selectedCompetition = compId;
document.getElementById('select-competition').disabled = false;
});
container.appendChild(button);
});
document.getElementById('competition-title').textContent =
`Selecciona una Competición - ${mode.name}`;
}
// Seleccionar competición
function selectCompetition() {
if (gameData.selectedCompetition === 'amistoso') {
renderLeaguesForAmistoso('player');
showScreen('team');
} else {
initTournament();
renderTeamsForCompetition();
showScreen('team');
}
}
// Inicializar torneo
function initTournament() {
if (!gameData.selectedCompetition) return;
const competition = gameData.competitions[gameData.selectedCompetition];
if (competition.tournamentMode) {
gameData.tournament.active = true;
gameData.tournament.currentRound = 0;
gameData.tournament.bracket = [];
gameData.tournament.playerTeam = null;
gameData.tournament.eliminated = false;
const teams = [...competition.teams];
shuffleArray(teams);
for (let i = 0; i < teams.length; i += 2) {
if (i + 1 < teams.length) {
gameData.tournament.bracket.push({
team1: teams[i],
team2: teams[i + 1],
winner: null,
played: false
});
}
}
competition.tournamentBracket = gameData.tournament.bracket;
competition.currentRound = 0;
}
}
// Función para mezclar array
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// Renderizar ligas para amistosos
function renderLeaguesForAmistoso(type) {
const container = type === 'player' ?
document.getElementById('teams-container') :
document.getElementById('opponent-teams-container');
container.innerHTML = '';
if (type === 'player') {
document.getElementById('team-selection-title').textContent = 'Selecciona una Liga';
} else {
document.getElementById('opponent-selection-title').textContent = 'Selecciona una Liga';
}
// Opción "Todas las Ligas"
const allButton = document.createElement('button');
allButton.className = 'team-button';
allButton.innerHTML = `
<div class="team-logo">🌍</div>
<div>Todas las Ligas</div>
`;
allButton.addEventListener('click', () => {
document.querySelectorAll(`#${container.id} .team-button`).forEach(btn => btn.classList.remove('selected'));
allButton.classList.add('selected');
if (type === 'player') {
gameData.selectedLeague = 'all';
renderAllTeamsForFriendly('player');
} else {
gameData.selectedOpponentLeague = 'all';
renderAllTeamsForFriendly('opponent');
}
});
container.appendChild(allButton);
// Agregar cada liga
for (const leagueId in gameData.leagues) {
const league = gameData.leagues[leagueId];
const button = document.createElement('button');
button.className = 'team-button';
button.innerHTML = `
<div class="team-logo">${league.flag}</div>
<div>${league.name}</div>
`;
button.addEventListener('click', () => {
document.querySelectorAll(`#${container.id} .team-button`).forEach(btn => btn.classList.remove('selected'));
button.classList.add('selected');
if (type === 'player') {
gameData.selectedLeague = leagueId;
renderTeamsFromLeague('player');
} else {
gameData.selectedOpponentLeague = leagueId;
renderTeamsFromLeague('opponent');
}
});
container.appendChild(button);
}
}
// Renderizar todos los equipos para amistosos CON COLORES
function renderAllTeamsForFriendly(type) {
const container = type === 'player' ?
document.getElementById('teams-container') :
document.getElementById('opponent-teams-container');
container.innerHTML = '';
if (type === 'player') {
document.getElementById('team-selection-title').textContent = 'Selecciona tu Equipo';
} else {
document.getElementById('opponent-selection-title').textContent = 'Selecciona el Equipo Rival';
}
// Recopilar todos los equipos
const allTeams = [];
for (const leagueId in gameData.leagues) {
const league = gameData.leagues[leagueId];
league.teams.forEach(team => {
allTeams.push(team);
});
}
// Mostrar equipos con sus colores
allTeams.forEach(team => {
const button = document.createElement('button');
button.className = 'team-button';
button.style.background = `linear-gradient(135deg, ${team.colors.primary}, ${team.colors.secondary})`;
button.style.borderColor = team.colors.secondary;
button.innerHTML = `
<div class="team-logo" style="background: ${team.colors.secondary}; color: ${team.colors.primary}">
${team.name.charAt(0)}
</div>
<div style="font-weight: bold; text-shadow: 1px 1px 2px rgba(0,0,0,0.5)">${team.name}</div>
`;
button.addEventListener('click', () => {
document.querySelectorAll(`#${container.id} .team-button`).forEach(btn => btn.classList.remove('selected'));
button.classList.add('selected');
if (type === 'player') {
gameData.selectedTeam = team.name;
document.getElementById('select-team').disabled = false;
} else {
gameData.selectedOpponent = team.name;
document.getElementById('select-opponent').disabled = false;
}
});
container.appendChild(button);
});
}
// Renderizar equipos desde liga específica CON COLORES
function renderTeamsFromLeague(type) {
const container = type === 'player' ?
document.getElementById('teams-container') :
document.getElementById('opponent-teams-container');
container.innerHTML = '';
const leagueId = type === 'player' ? gameData.selectedLeague : gameData.selectedOpponentLeague;
const league = gameData.leagues[leagueId];
if (type === 'player') {
document.getElementById('team-selection-title').textContent =
`Selecciona tu Equipo - ${league.name}`;
} else {
document.getElementById('opponent-selection-title').textContent =
`Selecciona Rival - ${league.name}`;
}
league.teams.forEach(team => {
const button = document.createElement('button');
button.className = 'team-button';
button.style.background = `linear-gradient(135deg, ${team.colors.primary}, ${team.colors.secondary})`;
button.style.borderColor = team.colors.secondary;
button.innerHTML = `
<div class="team-logo" style="background: ${team.colors.secondary}; color: ${team.colors.primary}">
${team.name.charAt(0)}
</div>
<div style="font-weight: bold; text-shadow: 1px 1px 2px rgba(0,0,0,0.5)">${team.name}</div>
`;
button.addEventListener('click', () => {
document.querySelectorAll(`#${container.id} .team-button`).forEach(btn => btn.classList.remove('selected'));
button.classList.add('selected');
if (type === 'player') {
gameData.selectedTeam = team.name;
document.getElementById('select-team').disabled = false;
} else {
gameData.selectedOpponent = team.name;
document.getElementById('select-opponent').disabled = false;
}
});
container.appendChild(button);
});
}
// Renderizar equipos para competición
function renderTeamsForCompetition() {
const container = document.getElementById('teams-container');
container.innerHTML = '';
document.getElementById('team-selection-title').textContent =
`Selecciona tu Equipo - ${gameData.competitions[gameData.selectedCompetition].name}`;
const competition = gameData.competitions[gameData.selectedCompetition];
competition.teams.forEach(team => {
// Buscar colores del equipo
let teamColors = {primary: '#00a8ff', secondary: '#005a8c'};
// Buscar en todas las ligas
for (const leagueId in gameData.leagues) {
const league = gameData.leagues[leagueId];
const foundTeam = league.teams.find(t => t.name === team);
if (foundTeam) {
teamColors = foundTeam.colors;
break;
}
}
const button = document.createElement('button');
button.className = 'team-button';
button.style.background = `linear-gradient(135deg, ${teamColors.primary}, ${teamColors.secondary})`;
button.style.borderColor = teamColors.secondary;
button.innerHTML = `
<div class="team-logo" style="background: ${teamColors.secondary}; color: ${teamColors.primary}">
${team.charAt(0)}
</div>
<div style="font-weight: bold; text-shadow: 1px 1px 2px rgba(0,0,0,0.5)">${team}</div>
`;
button.addEventListener('click', () => {
document.querySelectorAll('#teams-container .team-button').forEach(btn => btn.classList.remove('selected'));
button.classList.add('selected');
gameData.selectedTeam = team;
gameData.tournament.playerTeam = team;
document.getElementById('select-team').disabled = false;
// Encontrar oponente
const bracket = gameData.tournament.bracket[0];
let opponent = null;
if (bracket.team1 === team) {
opponent = bracket.team2;
} else if (bracket.team2 === team) {
opponent = bracket.team1;
} else {
opponent = competition.teams.find(t => t !== team);
}
gameData.selectedOpponent = opponent;
renderOpponentsForCompetition();
});
container.appendChild(button);
});
}
// Renderizar oponentes para competición CON COLORES
function renderOpponentsForCompetition() {
const container = document.getElementById('opponent-teams-container');
container.innerHTML = '';
document.getElementById('opponent-selection-title').textContent =
`Tu rival es: ${gameData.selectedOpponent}`;
// Buscar colores del equipo rival
let opponentColors = {primary: '#e84118', secondary: '#c23616'};
for (const leagueId in gameData.leagues) {
const league = gameData.leagues[leagueId];
const foundTeam = league.teams.find(t => t.name === gameData.selectedOpponent);
if (foundTeam) {
opponentColors = foundTeam.colors;
break;
}
}
const button = document.createElement('button');
button.className = 'team-button selected';
button.style.background = `linear-gradient(135deg, ${opponentColors.primary}, ${opponentColors.secondary})`;
button.style.borderColor = opponentColors.secondary;
button.innerHTML = `
<div class="team-logo" style="background: ${opponentColors.secondary}; color: ${opponentColors.primary}">
${gameData.selectedOpponent.charAt(0)}
</div>
<div style="font-weight: bold; text-shadow: 1px 1px 2px rgba(0,0,0,0.5)">${gameData.selectedOpponent}</div>
`;
container.appendChild(button);
document.getElementById('select-opponent').disabled = false;
}
// Renderizar dificultades
function renderDifficulties() {
const container = document.getElementById('difficulty-container');
container.innerHTML = '';
gameData.difficulties.forEach(diff => {
const button = document.createElement('button');
button.className = `difficulty-button difficulty-${diff.id}`;
button.textContent = diff.name;
button.addEventListener('click', () => {
document.querySelectorAll('.difficulty-button').forEach(btn => btn.classList.remove('selected'));
button.classList.add('selected');
gameData.selectedDifficulty = diff;
document.getElementById('start-game').disabled = false;
});
container.appendChild(button);
});
}
// Mostrar pantalla específica
function showScreen(screenName) {
Object.values(screens).forEach(screen => {
screen.classList.add('hidden');
});
screens[screenName].classList.remove('hidden');
if (screenName === 'game') {
document.getElementById('player-team-name').textContent = gameData.selectedTeam;
document.getElementById('cpu-team-name').textContent = gameData.selectedOpponent;
const scoreboard = document.getElementById('scoreboard');
scoreboard.className = 'scoreboard';
if (gameData.selectedCompetition && gameData.selectedCompetition !== 'amistoso') {
scoreboard.classList.add(gameData.selectedCompetition);
}
}
}
// Iniciar el juego
function startGame() {
showScreen('game');
initCanvas();
initGameObjects();
gameActive = true;
gameData.gameState = 'playing';
gameData.matchTime = 0;
gameData.score = { player: 0, cpu: 0 };
updateScoreboard();
updateCommentary("¡Comienza el partido!");
gameLoopId = requestAnimationFrame(gameLoop);
}
// Cambiar jugador activo
function switchPlayer() {
if (!playerTeam.length || isCelebrating || isFoulActive) return;
let currentIndex = playerTeam.findIndex(player => player === activePlayer);
let nextIndex = (currentIndex + 1) % playerTeam.length;
let attempts = 0;
while (playerTeam[nextIndex].isGoalkeeper && attempts < playerTeam.length) {
nextIndex = (nextIndex + 1) % playerTeam.length;
attempts++;
}
activePlayer = playerTeam[nextIndex];
updateCommentary(`Controlas a: ${activePlayer.name}`);
}
// Intentar robar la pelota
function attemptTackle() {
if (Date.now() - lastTackleTime < tackleCooldown || isCelebrating || isFoulActive) return;
lastTackleTime = Date.now();
// Si la CPU tiene la pelota
if (ball.owner === 'cpu') {
const cpuWithBall = cpuTeam.find(player => player.hasBall);
if (cpuWithBall) {
const dx = activePlayer.x - cpuWithBall.x;
const dy = activePlayer.y - cpuWithBall.y;
const distance = Math.sqrt(dx*dx + dy*dy);
// Verificar si está cerca de la pelota o del jugador
const distToBall = Math.sqrt((activePlayer.x - ball.x)**2 + (activePlayer.y - ball.y)**2);
if (distToBall < 25) {
// Robo exitoso - tocó la pelota
cpuWithBall.hasBall = false;
activePlayer.hasBall = true;
ball.owner = 'player';
updateCommentary("¡Robo limpio!");
animateTackle();
} else if (distance < 30) {
// Falta - tocó al jugador
commitFoul(activePlayer, cpuWithBall);
}
}
}
}
// Cometer falta
function commitFoul(foulingPlayer, fouledPlayer) {
if (Date.now() - lastFoulTime < foulCooldown) return;
lastFoulTime = Date.now();
isFoulActive = true;
foulTimer = 0;
// Determinar tipo de falta
const foulTypes = ['entrada fuerte', 'empujón', 'zancadilla'];
const foulType = foulTypes[Math.floor(Math.random() * foulTypes.length)];
// Determinar si es dentro del área (penal)
const isInPenaltyArea = fouledPlayer.x < 120 || fouledPlayer.x > canvas.width - 120;
// Determinar tarjeta (probabilidades)
let cardType = 'none';
const random = Math.random();
if (random < 0.3) {
cardType = 'yellow';
} else if (random < 0.5) {
// Verificar si ya tiene amarilla
const team = foulingPlayer.color === '#00a8ff' ? 'player' : 'cpu';
if (gameData.fouls.cards[team].yellow > 0) {
cardType = 'red';
} else {
cardType = 'yellow';
}
}
// Registrar falta
gameData.fouls.foulingPlayer = foulingPlayer;
gameData.fouls.fouledPlayer = fouledPlayer;
gameData.fouls.foulType = foulType;
gameData.fouls.foulLocation = {x: fouledPlayer.x, y: fouledPlayer.y};
gameData.fouls.isPenalty = isInPenaltyArea;
// Asignar tarjeta
if (cardType !== 'none') {
assignCard(foulingPlayer, cardType);
}
// Mostrar modal de falta
document.getElementById('foul-modal').classList.remove('hidden');
document.getElementById('foul-title').textContent = 'FALTA';
document.getElementById('foul-details').textContent =
`${foulType.charAt(0).toUpperCase() + foulType.slice(1)} de ${foulingPlayer.name} sobre ${fouledPlayer.name}`;
if (isInPenaltyArea) {
document.getElementById('foul-details').textContent += ' - ¡PENAL!';
} else {
document.getElementById('foul-details').textContent += ' - Tiro libre';
}
// Mostrar tarjeta
if (cardType === 'yellow') {
document.getElementById('card-display').textContent = '🟨 AMARILLA';
document.getElementById('card-display').style.color = '#FFD700';
} else if (cardType === 'red') {
document.getElementById('card-display').textContent = '🟥 ROJA - EXPULSADO';
document.getElementById('card-display').style.color = '#FF0000';
} else {
document.getElementById('card-display').textContent = 'Sin tarjeta';
document.getElementById('card-display').style.color = '#FFFFFF';
}
updateCommentary(`¡Falta! ${foulType} de ${foulingPlayer.name}`);
}
// Asignar tarjeta
function assignCard(player, cardType) {
const team = player.color === '#00a8ff' ? 'player' : 'cpu';
if (cardType === 'yellow') {
gameData.fouls.cards[team].yellow++;
if (gameData.fouls.cards[team].yellow >= 2) {
// Segunda amarilla = roja
gameData.fouls.cards[team].yellow = 0;
gameData.fouls.cards[team].red++;
updateCommentary(`¡Segunda amarilla! ${player.name} expulsado`);
// Expulsar jugador
if (team === 'player') {
playerTeam = playerTeam.filter(p => p !== player);
if (activePlayer === player) {
switchPlayer();
}
} else {
cpuTeam = cpuTeam.filter(p => p !== player);
}
} else {
updateCommentary(`Tarjeta amarilla para ${player.name}`);
}
} else if (cardType === 'red') {
gameData.fouls.cards[team].red++;
updateCommentary(`¡Tarjeta roja! ${player.name} expulsado`);
// Expulsar jugador
if (team === 'player') {
playerTeam = playerTeam.filter(p => p !== player);
if (activePlayer === player) {
switchPlayer();
}
} else {
cpuTeam = cpuTeam.filter(p => p !== player);
}
}
// Actualizar HUD de tarjetas
document.getElementById('player-yellow').textContent = gameData.fouls.cards.player.yellow;
document.getElementById('player-red').textContent = gameData.fouls.cards.player.red;
document.getElementById('cpu-yellow').textContent = gameData.fouls.cards.cpu.yellow;
document.getElementById('cpu-red').textContent = gameData.fouls.cards.cpu.red;
}
// Continuar después de falta
function continueAfterFoul() {
document.getElementById('foul-modal').classList.add('hidden');
isFoulActive = false;
// Si es penal, iniciar penales
if (gameData.fouls.isPenalty) {
startPenaltyShootout();
} else {
// Tiro libre
setupFreeKick();
}
}
// Configurar tiro libre
function setupFreeKick() {
// Reposicionar pelota en lugar de la falta
ball.x = gameData.fouls.foulLocation.x;
ball.y = gameData.fouls.foulLocation.y;
ball.speed = 0;
ball.owner = null;
// Reposicionar jugadores
const isPlayerFoul = gameData.fouls.foulingPlayer.color === '#00a8ff';
if (isPlayerFoul) {
// Tiro libre para CPU
updateCommentary("Tiro libre para la CPU");
// Posicionar a un jugador de la CPU cerca de la pelota
const nearestCPU = cpuTeam.reduce((nearest, player) => {
const dist = Math.abs(player.y - ball.y);
return dist < nearest.dist ? {player, dist} : nearest;
}, {player: cpuTeam[0], dist: Infinity});
nearestCPU.player.x = ball.x - 40;
nearestCPU.player.y = ball.y;
nearestCPU.player.hasBall = true;
ball.owner = 'cpu';
} else {
// Tiro libre para jugador
updateCommentary("Tiro libre para tu equipo");
// Posicionar al jugador activo cerca de la pelota
activePlayer.x = ball.x + 40;
activePlayer.y = ball.y;
activePlayer.hasBall = true;
ball.owner = 'player';
}
}
// Iniciar tanda de penales
function startPenaltyShootout() {
gameData.penalties.active = true;
gameData.penalties.playerScore = 0;
gameData.penalties.cpuScore = 0;
gameData.penalties.currentAttempt = 0;
gameData.penalties.penaltyInProgress = false;
document.getElementById('penalty-modal').classList.remove('hidden');
document.getElementById('penalty-score').textContent =
`Jugador: ${gameData.penalties.playerScore} - ${gameData.penalties.cpuScore} :CPU`;
updateCommentary("¡Penal! Tanda de penales");
}
// Detener el juego
function stopGame() {
gameActive = false;
if (gameLoopId) {
cancelAnimationFrame(gameLoopId);
}
}
// Pausar/reanudar juego
function togglePause() {
if (gameData.gameState === 'playing') {
gameData.gameState = 'paused';
updateCommentary("Partido en pausa");
} else if (gameData.gameState === 'paused') {
gameData.gameState = 'playing';
updateCommentary("El partido se reanuda");
gameLoopId = requestAnimationFrame(gameLoop);
}
}
// Reiniciar datos del juego
function resetGameData() {
gameData.selectedMode = null;
gameData.selectedCompetition = null;
gameData.selectedLeague = null;
gameData.selectedTeam = null;
gameData.selectedOpponent = null;
gameData.selectedDifficulty = null;
document.getElementById('select-mode').disabled = true;
document.getElementById('select-competition').disabled = true;
document.getElementById('select-team').disabled = true;
document.getElementById('select-opponent').disabled = true;
document.getElementById('start-game').disabled = true;
document.querySelectorAll('.selected').forEach(el => el.classList.remove('selected'));
}
// Inicializar canvas
function initCanvas() {
canvas = document.getElementById('game-canvas');
ctx = canvas.getContext('2d');
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
// Inicializar objetos del juego CON COLORES REALES
function initGameObjects() {
const fieldWidth = canvas.width;
const fieldHeight = canvas.height;
const centerX = fieldWidth / 2;
const centerY = fieldHeight / 2;
// Buscar colores del equipo del jugador
let playerTeamColors = {primary: '#00a8ff', secondary: '#005a8c'};
let cpuTeamColors = {primary: '#e84118', secondary: '#c23616'};
// Buscar colores reales
for (const leagueId in gameData.leagues) {
const league = gameData.leagues[leagueId];
const foundPlayerTeam = league.teams.find(t => t.name === gameData.selectedTeam);
if (foundPlayerTeam) {
playerTeamColors = foundPlayerTeam.colors;
break;
}
}
for (const leagueId in gameData.leagues) {
const league = gameData.leagues[leagueId];
const foundCpuTeam = league.teams.find(t => t.name === gameData.selectedOpponent);
if (foundCpuTeam) {
cpuTeamColors = foundCpuTeam.colors;
break;
}
}
// Limpiar equipos
playerTeam = [];
cpuTeam = [];
goalkeepers = { player: null, cpu: null };
// Crear equipo especial si está seleccionado
const isSpecialTeam = gameData.selectedTeam === 'Equipo Especial' ||
gameData.selectedOpponent === 'Equipo Especial';
// Equipo del jugador
const playerFormation = [
// Arquero
{ x: 80, y: centerY, type: 'gk', number: 1, name: isSpecialTeam && gameData.selectedTeam === 'Equipo Especial' ?
gameData.specialTeamPlayers.goalkeeper.name : 'Arquero' },
// Defensores
{ x: 250, y: centerY - 80, type: 'df', number: 4, name: isSpecialTeam && gameData.selectedTeam === 'Equipo Especial' ?
gameData.specialTeamPlayers.defenders[0].name : 'Defensor 1' },
{ x: 250, y: centerY + 80, type: 'df', number: 5, name: isSpecialTeam && gameData.selectedTeam === 'Equipo Especial' ?
gameData.specialTeamPlayers.defenders[1].name : 'Defensor 2' },
// Delanteros
{ x: 400, y: centerY - 60, type: 'fw', number: 10, name: isSpecialTeam && gameData.selectedTeam === 'Equipo Especial' ?
gameData.specialTeamPlayers.forwards[0].name : 'Delantero 1' },
{ x: 400, y: centerY + 60, type: 'fw', number: 7, name: isSpecialTeam && gameData.selectedTeam === 'Equipo Especial' ?
gameData.specialTeamPlayers.forwards[1].name : 'Delantero 2' }
];
playerFormation.forEach((pos, i) => {
const player = {
x: pos.x,
y: pos.y,
radius: 20,
color: playerTeamColors.primary,
secondaryColor: playerTeamColors.secondary,
type: pos.type,
number: pos.number,
name: pos.name,
speed: 3.5,
hasBall: i === 3,
isGoalkeeper: pos.type === 'gk',
targetX: pos.x,
targetY: pos.y,
isMoving: false,
passTarget: null,
velocityX: 0,
velocityY: 0,
preferredPosition: { x: pos.x, y: pos.y },
moveTimer: 0,
state: 'idle'
};
playerTeam.push(player);
if (player.isGoalkeeper) {
goalkeepers.player = player;
}
});
// Equipo de la CPU
const cpuFormation = [
// Arquero
{ x: fieldWidth - 80, y: centerY, type: 'gk', number: 1, name: isSpecialTeam && gameData.selectedOpponent === 'Equipo Especial' ?
gameData.specialTeamPlayers.goalkeeper.name : 'Arquero' },
// Defensores
{ x: fieldWidth - 250, y: centerY - 80, type: 'df', number: 4, name: isSpecialTeam && gameData.selectedOpponent === 'Equipo Especial' ?
gameData.specialTeamPlayers.defenders[0].name : 'Defensor 1' },
{ x: fieldWidth - 250, y: centerY + 80, type: 'df', number: 5, name: isSpecialTeam && gameData.selectedOpponent === 'Equipo Especial' ?
gameData.specialTeamPlayers.defenders[1].name : 'Defensor 2' },
// Delanteros
{ x: fieldWidth - 400, y: centerY - 60, type: 'fw', number: 10, name: isSpecialTeam && gameData.selectedOpponent === 'Equipo Especial' ?
gameData.specialTeamPlayers.forwards[0].name : 'Delantero 1' },
{ x: fieldWidth - 400, y: centerY + 60, type: 'fw', number: 7, name: isSpecialTeam && gameData.selectedOpponent === 'Equipo Especial' ?
gameData.specialTeamPlayers.forwards[1].name : 'Delantero 2' }
];
cpuFormation.forEach((pos, i) => {
const player = {
x: pos.x,
y: pos.y,
radius: 20,
color: cpuTeamColors.primary,
secondaryColor: cpuTeamColors.secondary,
type: pos.type,
number: pos.number,
name: pos.name,
speed: 3.5,
hasBall: false,
isGoalkeeper: pos.type === 'gk',
targetX: pos.x,
targetY: pos.y,
isMoving: false,
passTarget: null,
velocityX: 0,
velocityY: 0,
preferredPosition: { x: pos.x, y: pos.y },
moveTimer: 0,
state: 'idle'
};
cpuTeam.push(player);
if (player.isGoalkeeper) {
goalkeepers.cpu = player;
}
});
// Crear pelota
const playerWithBall = playerTeam.find(player => player.hasBall);
ball = {
x: playerWithBall ? playerWithBall.x + 30 : centerX,
y: playerWithBall ? playerWithBall.y : centerY,
radius: 14,
color: '#FFD700',
speed: 0,
direction: 0,
owner: playerWithBall ? 'player' : null,
spacePressed: false,
spacePressTime: 0,
spaceStartX: 0,
spaceStartY: 0,
isMoving: false,
trail: [],
rotation: 0,
rotationSpeed: 0
};
// Establecer jugador activo
activePlayer = playerWithBall || playerTeam[3];
// Configurar IA según dificultad
if (gameData.selectedDifficulty) {
aiActionDelay = gameData.selectedDifficulty.aiDelay;
cpuTeam.forEach(player => {
player.speed = 3.5 * gameData.selectedDifficulty.speed;
});
}
// Inicializar tarjetas
gameData.fouls.cards = {
player: { yellow: 0, red: 0 },
cpu: { yellow: 0, red: 0 }
};
// Inicializar otros valores
ballTrail = [];
isCelebrating = false;
celebratingTeam = null;
celebrationTimer = 0;
corners = 0;
freeKicks = 0;
lastGoalBy = null;
goalScorer = null;
ballRotation = 0;
isFoulActive = false;
foulTimer = 0;
// Actualizar HUD de tarjetas
document.getElementById('player-yellow').textContent = '0';
document.getElementById('player-red').textContent = '0';
document.getElementById('cpu-yellow').textContent = '0';
document.getElementById('cpu-red').textContent = '0';
}
// Bucle principal del juego
function gameLoop(timestamp) {
if (gameData.gameState !== 'playing') return;
// Si estamos en penales, manejar penales
if (gameData.penalties.active) {
updatePenalties();
requestAnimationFrame(gameLoop);
return;
}
// Si hay falta activa
if (isFoulActive) {
foulTimer += 1/60;
if (foulTimer > 10) { // Timeout de 10 segundos
continueAfterFoul();
}
requestAnimationFrame(gameLoop);
return;
}
// Actualizar tiempo del partido
updateMatchTime();
// Actualizar animaciones
animationTimer += 1/60;
// Actualizar celebración si está activa
if (isCelebrating) {
updateCelebration();
drawField();
drawPlayers();
drawBall();
requestAnimationFrame(gameLoop);
return;
}
// Actualizar posiciones
updatePlayers();
updateBall();
updateAI();
updateTeammateAI();
// Dibujar todo
drawField();
drawPlayers();
drawBall();
// Verificar colisiones y goles
checkCollisions();
checkGoal();
checkOutOfBounds();
// Comentarios aleatorios
if (timestamp - lastCommentaryTime > commentaryInterval) {
randomCommentary();
lastCommentaryTime = timestamp;
}
// Verificar si hay que ir a penales
if (gameData.matchTime >= gameData.maxMatchTime &&
gameData.score.player === gameData.score.cpu) {
startPenaltyShootout();
return;
}
// Verificar eliminación en copas
if (gameData.tournament.active) {
if (gameData.score.player >= 5) {
if (gameData.tournament.eliminated) return;
winTournamentMatch();
return;
} else if (gameData.score.cpu >= 5) {
if (gameData.tournament.eliminated) return;
loseTournamentMatch();
return;
}
}
// Continuar el bucle
gameLoopId = requestAnimationFrame(gameLoop);
}
// Actualizar tiempo del partido
function updateMatchTime() {
gameData.matchTime += 1/60;
const minutes = Math.floor(gameData.matchTime / 60);
const seconds = Math.floor(gameData.matchTime % 60);
document.getElementById('game-time').textContent =
`${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
if (gameData.matchTime >= gameData.maxMatchTime &&
!gameData.tournament.active) {
endMatch();
}
}
// Actualizar jugadores
function updatePlayers() {
// Mover jugador activo con teclado
if (activePlayer && !isCelebrating && !isFoulActive) {
let moveX = 0, moveY = 0;
if (keys['w'] || keys['arrowup']) moveY -= activePlayer.speed;
if (keys['s'] || keys['arrowdown']) moveY += activePlayer.speed;
if (keys['a'] || keys['arrowleft']) moveX -= activePlayer.speed;
if (keys['d'] || keys['arrowright']) moveX += activePlayer.speed;
if (moveX !== 0 && moveY !== 0) {
moveX *= 0.7071;
moveY *= 0.7071;
}
activePlayer.velocityX = moveX * 0.8 + activePlayer.velocityX * 0.2;
activePlayer.velocityY = moveY * 0.8 + activePlayer.velocityY * 0.2;
const newX = activePlayer.x + activePlayer.velocityX;
const newY = activePlayer.y + activePlayer.velocityY;
if (newX >= 50 && newX <= canvas.width - 50) {
activePlayer.x = newX;
}
if (newY >= 50 && newY <= canvas.height - 50) {
activePlayer.y = newY;
}
// Si tiene la pelota, moverla junto al jugador
if (activePlayer.hasBall && ball.owner === 'player') {
const angle = Math.atan2(activePlayer.velocityY, activePlayer.velocityX);
const distanceFromPlayer = 35;
ball.x = activePlayer.x + Math.cos(angle) * distanceFromPlayer;
ball.y = activePlayer.y + Math.sin(angle) * distanceFromPlayer;
if (Math.abs(activePlayer.velocityX) > 0.1 || Math.abs(activePlayer.velocityY) > 0.1) {
ball.rotation += 0.1;
ball.rotationSpeed = Math.sqrt(activePlayer.velocityX * activePlayer.velocityX +
activePlayer.velocityY * activePlayer.velocityY) * 0.1;
}
if (ballTrail.length < 10 || Math.random() < 0.3) {
ballTrail.push({
x: ball.x,
y: ball.y,
alpha: 1,
radius: ball.radius * 0.7
});
}
}
}
// Actualizar rastro de la pelota
for (let i = 0; i < ballTrail.length; i++) {
ballTrail[i].alpha -= 0.05;
ballTrail[i].radius -= 0.1;
}
ballTrail = ballTrail.filter(point => point.alpha > 0 && point.radius > 0);
// Actualizar porteros
updateGoalkeepers();
// Aplicar inercia
playerTeam.forEach(player => {
if (player !== activePlayer) {
player.velocityX *= 0.9;
player.velocityY *= 0.9;
player.x += player.velocityX;
player.y += player.velocityY;
}
});
cpuTeam.forEach(player => {
player.velocityX *= 0.9;
player.velocityY *= 0.9;
player.x += player.velocityX;
player.y += player.velocityY;
});
}
// Actualizar porteros
function updateGoalkeepers() {
// Portero del jugador
if (goalkeepers.player) {
if (ball.x < canvas.width / 3) {
const targetY = ball.y;
const dy = targetY - goalkeepers.player.y;
goalkeepers.player.y += dy * 0.05;
const goalY = canvas.height / 2;
const maxMove = 100;
goalkeepers.player.y = Math.max(goalY - maxMove,
Math.min(goalY + maxMove, goalkeepers.player.y));
} else {
const centerY = canvas.height / 2;
const dy = centerY - goalkeepers.player.y;
goalkeepers.player.y += dy * 0.05;
}
}
// Portero de la CPU
if (goalkeepers.cpu) {
if (ball.x > canvas.width * 2/3) {
const targetY = ball.y;
const dy = targetY - goalkeepers.cpu.y;
goalkeepers.cpu.y += dy * 0.05;
const goalY = canvas.height / 2;
const maxMove = 100;
goalkeepers.cpu.y = Math.max(goalY - maxMove,
Math.min(goalY + maxMove, goalkeepers.cpu.y));
} else {
const centerY = canvas.height / 2;
const dy = centerY - goalkeepers.cpu.y;
goalkeepers.cpu.y += dy * 0.05;
}
}
}
// Actualizar IA de compañeros
function updateTeammateAI() {
playerTeam.forEach(player => {
if (player === activePlayer || player.isGoalkeeper) return;
player.moveTimer -= 1/60;
if (player.moveTimer <= 0) {
const states = ['move_to_position', 'support_attack', 'defend'];
player.state = states[Math.floor(Math.random() * states.length)];
player.moveTimer = 2 + Math.random() * 3;
}
switch(player.state) {
case 'move_to_position':
const dx = player.preferredPosition.x - player.x;
const dy = player.preferredPosition.y - player.y;
const distance = Math.sqrt(dx*dx + dy*dy);
if (distance > 10) {
player.velocityX += (dx / distance) * 0.1;
player.velocityY += (dy / distance) * 0.1;
}
break;
case 'support_attack':
if (ball.owner === 'player' && ball.x > canvas.width / 3) {
const targetX = player.x + 50;
const targetY = player.y + (Math.random() * 40 - 20);
const dx = targetX - player.x;
const dy = targetY - player.y;
const distance = Math.sqrt(dx*dx + dy*dy);
if (distance > 5) {
player.velocityX += (dx / distance) * 0.15;
player.velocityY += (dy / distance) * 0.15;
}
}
break;
case 'defend':
if (ball.x < canvas.width / 3) {
const dx = ball.x - player.x;
const dy = ball.y - player.y;
const distance = Math.sqrt(dx*dx + dy*dy);
if (distance > 30) {
player.velocityX += (dx / distance) * 0.2;
player.velocityY += (dy / distance) * 0.2;
}
}
break;
}
const speed = Math.sqrt(player.velocityX * player.velocityX +
player.velocityY * player.velocityY);
if (speed > player.speed) {
player.velocityX = (player.velocityX / speed) * player.speed;
player.velocityY = (player.velocityY / speed) * player.speed;
}
});
}
// Actualizar pelota
function updateBall() {
ballRotation += ball.rotationSpeed;
ball.rotationSpeed *= 0.95;
if (ball.owner && !isCelebrating && !isFoulActive) {
const team = ball.owner === 'player' ? playerTeam : cpuTeam;
const playerWithBall = team.find(player => player.hasBall);
if (playerWithBall) {
const angle = Math.atan2(playerWithBall.velocityY, playerWithBall.velocityX);
const targetX = playerWithBall.x + Math.cos(angle) * 35;
const targetY = playerWithBall.y + Math.sin(angle) * 35;
const dx = targetX - ball.x;
const dy = targetY - ball.y;
ball.x += dx * 0.3;
ball.y += dy * 0.3;
ball.rotation += Math.sqrt(dx*dx + dy*dy) * 0.01;
}
} else if (ball.speed > 0.5 && !isCelebrating && !isFoulActive) {
ball.x += Math.cos(ball.direction) * ball.speed;
ball.y += Math.sin(ball.direction) * ball.speed;
ball.speed *= 0.98;
ball.rotation += ball.speed * 0.05;
if (ballTrail.length < 15 && ball.speed > 3) {
ballTrail.push({
x: ball.x,
y: ball.y,
alpha: 1,
radius: ball.radius * 0.8
});
}
if (ball.x <= ball.radius || ball.x >= canvas.width - ball.radius) {
ball.direction = Math.PI - ball.direction;
ball.speed *= 0.8;
updateCommentary("¡La pelota sale por la línea!");
}
if (ball.y <= ball.radius || ball.y >= canvas.height - ball.radius) {
ball.direction = -ball.direction;
ball.speed *= 0.8;
updateCommentary("¡La pelota sale por la línea!");
}
ball.x = Math.max(ball.radius, Math.min(canvas.width - ball.radius, ball.x));
ball.y = Math.max(ball.radius, Math.min(canvas.height - ball.radius, ball.y));
}
}
// Actualizar IA de la CPU CON MEJORAS PARA EXTREMO
function updateAI() {
const now = Date.now();
if (now - lastAIAction < aiActionDelay || isCelebrating || isFoulActive) return;
// Encontrar jugador de la CPU con la pelota
const cpuWithBall = cpuTeam.find(player => player.hasBall);
if (cpuWithBall) {
const aggression = gameData.selectedDifficulty ? gameData.selectedDifficulty.aiAggression : 0.5;
const shootingRange = gameData.selectedDifficulty ? gameData.selectedDifficulty.aiShootingRange : 250;
// La CPU ahora puede chutar desde más lejos
const distanceToGoal = Math.abs(canvas.width - 80 - cpuWithBall.x);
const isInShootingRange = distanceToGoal < shootingRange;
if (isInShootingRange && Math.random() < 0.015 * aggression) {
shootBallCPU(cpuWithBall);
} else if (Math.random() < 0.02 * aggression) {
passBallCPU(cpuWithBall);
} else {
// Moverse hacia la portería del jugador
const targetX = 80;
const targetY = canvas.height / 2 + (Math.random() * 100 - 50);
const dx = targetX - cpuWithBall.x;
const dy = targetY - cpuWithBall.y;
const distance = Math.sqrt(dx*dx + dy*dy);
if (distance > 50) {
cpuWithBall.velocityX += (dx / distance) * cpuWithBall.speed * 0.1;
cpuWithBall.velocityY += (dy / distance) * cpuWithBall.speed * 0.1;
}
const speed = Math.sqrt(cpuWithBall.velocityX * cpuWithBall.velocityX +
cpuWithBall.velocityY * cpuWithBall.velocityY);
if (speed > cpuWithBall.speed) {
cpuWithBall.velocityX = (cpuWithBall.velocityX / speed) * cpuWithBall.speed;
cpuWithBall.velocityY = (cpuWithBall.velocityY / speed) * cpuWithBall.speed;
}
}
} else if (ball.owner === null) {
let closestPlayer = null;
let closestDistance = Infinity;
cpuTeam.forEach(player => {
if (player.isGoalkeeper) return;
const dx = ball.x - player.x;
const dy = ball.y - player.y;
const distance = Math.sqrt(dx*dx + dy*dy);
if (distance < closestDistance && distance < 200) {
closestDistance = distance;
closestPlayer = player;
}
});
if (closestPlayer && closestDistance > 30) {
const dx = ball.x - closestPlayer.x;
const dy = ball.y - closestPlayer.y;
const distance = Math.sqrt(dx*dx + dy*dy);
closestPlayer.velocityX += (dx / distance) * closestPlayer.speed * 0.15;
closestPlayer.velocityY += (dy / distance) * closestPlayer.speed * 0.15;
}
} else if (ball.owner === 'player') {
const playerWithBall = playerTeam.find(player => player.hasBall);
if (playerWithBall) {
let closestDefender = null;
let closestDistance = Infinity;
cpuTeam.forEach(player => {
if (!player.isGoalkeeper) {
const dx = playerWithBall.x - player.x;
const dy = playerWithBall.y - player.y;
const distance = Math.sqrt(dx*dx + dy*dy);
if (distance < closestDistance && distance < 300) {
closestDistance = distance;
closestDefender = player;
}
}
});
if (closestDefender && closestDistance > 20) {
const dx = playerWithBall.x - closestDefender.x;
const dy = playerWithBall.y - closestDefender.y;
const distance = Math.sqrt(dx*dx + dy*dy);
closestDefender.velocityX += (dx / distance) * closestDefender.speed * 0.2;
closestDefender.velocityY += (dy / distance) * closestDefender.speed * 0.2;
}
cpuTeam.forEach(player => {
if (player !== closestDefender && !player.isGoalkeeper) {
const targetX = player.preferredPosition.x;
const targetY = player.preferredPosition.y;
const dx = targetX - player.x;
const dy = targetY - player.y;
const distance = Math.sqrt(dx*dx + dy*dy);
if (distance > 10) {
player.velocityX += (dx / distance) * 0.1;
player.velocityY += (dy / distance) * 0.1;
}
}
});
}
}
lastAIAction = now;
}
// Dibujar campo CON ARCOS VISIBLES
function drawField() {
const width = canvas.width;
const height = canvas.height;
// Césped
ctx.fillStyle = '#2a623d';
ctx.fillRect(0, 0, width, height);
// Patrón de césped
ctx.strokeStyle = '#3a7d4d';
ctx.lineWidth = 1;
for (let i = 0; i < width; i += 40) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i, height);
ctx.stroke();
}
for (let i = 0; i < height; i += 40) {
ctx.beginPath();
ctx.moveTo(0, i);
ctx.lineTo(width, i);
ctx.stroke();
}
// Líneas del campo
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = 3;
// Líneas exteriores
ctx.strokeRect(20, 20, width - 40, height - 40);
// Línea de mitad de cancha
ctx.beginPath();
ctx.moveTo(width / 2, 20);
ctx.lineTo(width / 2, height - 20);
ctx.stroke();
// Círculo central
ctx.beginPath();
ctx.arc(width / 2, height / 2, 80, 0, Math.PI * 2);
ctx.stroke();
// Punto central
ctx.beginPath();
ctx.arc(width / 2, height / 2, 5, 0, Math.PI * 2);
ctx.fillStyle = '#FFFFFF';
ctx.fill();
// Áreas
// Área izquierda (jugador)
ctx.strokeRect(20, height / 2 - 120, 100, 240);
// Área derecha (CPU)
ctx.strokeRect(width - 120, height / 2 - 120, 100, 240);
// ARCOS VISIBLES Y FUNCIONALES
ctx.lineWidth = 6;
ctx.strokeStyle = '#FFFFFF';
// Arco izquierdo (CPU)
ctx.beginPath();
ctx.moveTo(20, height / 2 - 60);
ctx.lineTo(10, height / 2 - 60);
ctx.lineTo(10, height / 2 + 60);
ctx.lineTo(20, height / 2 + 60);
ctx.stroke();
// Arco derecho (Jugador)
ctx.beginPath();
ctx.moveTo(width - 20, height / 2 - 60);
ctx.lineTo(width - 10, height / 2 - 60);
ctx.lineTo(width - 10, height / 2 + 60);
ctx.lineTo(width - 20, height / 2 + 60);
ctx.stroke();
// Redes de los arcos (diseño)
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
ctx.lineWidth = 1;
// Red izquierda
for (let i = 0; i < 6; i++) {
ctx.beginPath();
ctx.moveTo(10, height / 2 - 60 + i * 20);
ctx.lineTo(0, height / 2 - 50 + i * 20);
ctx.stroke();
}
// Red derecha
for (let i = 0; i < 6; i++) {
ctx.beginPath();
ctx.moveTo(width - 10, height / 2 - 60 + i * 20);
ctx.lineTo(width, height / 2 - 50 + i * 20);
ctx.stroke();
}
// Tribuna
ctx.fillStyle = '#333333';
ctx.fillRect(0, 0, width, 20);
ctx.fillRect(0, height - 20, width, 20);
ctx.fillRect(0, 0, 20, height);
ctx.fillRect(width - 20, 0, 20, height);
// Detalles de la tribuna
ctx.fillStyle = '#555555';
for (let i = 40; i < width - 40; i += 40) {
ctx.fillRect(i, 5, 20, 10);
ctx.fillRect(i, height - 15, 20, 10);
}
for (let i = 40; i < height - 40; i += 40) {
ctx.fillRect(5, i, 10, 20);
ctx.fillRect(width - 15, i, 10, 20);
}
// Línea de medio campo
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = 5;
ctx.setLineDash([10, 10]);
ctx.beginPath();
ctx.moveTo(width / 2, 20);
ctx.lineTo(width / 2, height - 20);
ctx.stroke();
ctx.setLineDash([]);
}
// Dibujar jugadores CON COLORES REALES
function drawPlayers() {
// Dibujar rastro de la pelota
for (let i = 0; i < ballTrail.length; i++) {
const point = ballTrail[i];
ctx.beginPath();
ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 215, 0, ${point.alpha})`;
ctx.fill();
}
// Dibujar jugadores del equipo del jugador
playerTeam.forEach(player => {
// Cuerpo del jugador
ctx.beginPath();
ctx.arc(player.x, player.y, player.radius, 0, Math.PI * 2);
ctx.fillStyle = player.color;
ctx.fill();
// Borde (color secundario)
if (player === activePlayer) {
ctx.strokeStyle = '#FFD700';
ctx.lineWidth = 4;
ctx.stroke();
} else {
ctx.strokeStyle = player.secondaryColor;
ctx.lineWidth = player.hasBall ? 3 : 2;
ctx.stroke();
}
// Número y nombre
ctx.fillStyle = '#FFFFFF';
ctx.font = player.hasBall ? 'bold 14px Arial' : '12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(player.number, player.x, player.y);
// Nombre pequeño arriba
ctx.font = '10px Arial';
ctx.fillStyle = player.secondaryColor;
ctx.fillText(player.name, player.x, player.y - player.radius - 8);
// Indicador de jugador activo
if (player === activePlayer) {
ctx.beginPath();
ctx.arc(player.x, player.y - player.radius - 15, 3, 0, Math.PI * 2);
ctx.fillStyle = '#FFD700';
ctx.fill();
}
});
// Dibujar jugadores de la CPU
cpuTeam.forEach(player => {
// Cuerpo del jugador
ctx.beginPath();
ctx.arc(player.x, player.y, player.radius, 0, Math.PI * 2);
ctx.fillStyle = player.color;
ctx.fill();
// Borde (color secundario)
ctx.strokeStyle = player.secondaryColor;
ctx.lineWidth = player.hasBall ? 3 : 2;
ctx.stroke();
// Número y nombre
ctx.fillStyle = '#FFFFFF';
ctx.font = player.hasBall ? 'bold 14px Arial' : '12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(player.number, player.x, player.y);
// Nombre pequeño arriba
ctx.font = '10px Arial';
ctx.fillStyle = player.secondaryColor;
ctx.fillText(player.name, player.x, player.y - player.radius - 8);
});
}
// Dibujar pelota
function drawBall() {
ctx.save();
ctx.translate(ball.x, ball.y);
ctx.rotate(ballRotation);
const ballScale = ball.speed > 5 ? 1.1 : 1.0;
ctx.scale(ballScale, ballScale);
// Cuerpo de la pelota (amarilla)
ctx.beginPath();
ctx.arc(0, 0, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = '#FFD700';
ctx.fill();
// Diseño de la pelota
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2;
// Círculo exterior
ctx.beginPath();
ctx.arc(0, 0, ball.2
2
421KB
421KB
197.0ms
576.0ms
378.0ms