top of page
Buscar

Mouse Shadow Fight: Gatos vs Perros 3D




<!DOCTYPE html>

<html lang="es">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Shadow Fight: Gatos vs Perros - Modo Infinito</title>

<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">

<style>

* {

margin: 0;

padding: 0;

box-sizing: border-box;

}


body {

font-family: 'Orbitron', monospace;

background: linear-gradient(135deg, #0a0a0a, #1a1a2e, #16213e, #0f3460);

overflow: hidden;

color: white;

cursor: crosshair;

}


position: relative;

width: 100vw;

height: 100vh;

}


display: block;

}


#ui {

position: absolute;

top: 0;

left: 0;

width: 100%;

height: 100%;

pointer-events: none;

z-index: 100;

}


.health-bar-container {

position: absolute;

top: 20px;

width: 350px;

height: 35px;

border: 3px solid #fff;

border-radius: 18px;

background: rgba(0, 0, 0, 0.8);

overflow: hidden;

box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);

}


left: 20px;

}


right: 20px;

}


.health-bar {

width: 100%;

height: 100%;

border-radius: 15px;

transition: width 0.3s ease;

position: relative;

}


.health-cat {

background: linear-gradient(90deg, #3498db, #2980b9, #1abc9c);

box-shadow: inset 0 0 10px rgba(52, 152, 219, 0.5);

}


.health-dog {

background: linear-gradient(90deg, #e74c3c, #c0392b, #d35400);

box-shadow: inset 0 0 10px rgba(231, 76, 60, 0.5);

}


.health-bar::after {

content: '';

position: absolute;

top: 0;

left: 0;

right: 0;

bottom: 0;

background: linear-gradient(90deg, transparent 30%, rgba(255,255,255,0.3) 50%, transparent 70%);

animation: shine 2s infinite;

}


@keyframes shine {

0% { transform: translateX(-100%); }

100% { transform: translateX(100%); }

}


.player-name {

position: absolute;

bottom: -30px;

width: 100%;

text-align: center;

font-size: 16px;

font-weight: bold;

text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);

}


#round-counter {

position: absolute;

top: 20px;

left: 50%;

transform: translateX(-50%);

background: rgba(0, 0, 0, 0.9);

padding: 15px 25px;

border-radius: 25px;

border: 3px solid #ffd700;

font-size: 20px;

font-weight: bold;

color: #ffd700;

text-shadow: 0 0 10px rgba(255, 215, 0, 0.8);

box-shadow: 0 0 20px rgba(255, 215, 0, 0.3);

}


#battle-counter {

position: absolute;

top: 140px;

left: 50%;

transform: translateX(-50%);

background: rgba(0, 0, 0, 0.9);

padding: 10px 20px;

border-radius: 15px;

border: 2px solid #e74c3c;

font-size: 16px;

font-weight: bold;

color: #e74c3c;

text-shadow: 0 0 10px rgba(231, 76, 60, 0.8);

box-shadow: 0 0 20px rgba(231, 76, 60, 0.3);

}


.combo-display {

position: absolute;

top: 50%;

left: 50%;

transform: translate(-50%, -50%);

font-size: 60px;

font-weight: 900;

color: #ffd700;

text-shadow:

0 0 10px rgba(255, 215, 0, 0.8),

0 0 20px rgba(255, 215, 0, 0.6),

0 0 30px rgba(255, 215, 0, 0.4);

opacity: 0;

transition: all 0.5s ease;

pointer-events: none;

z-index: 1000;

}


.combo-display.show {

opacity: 1;

transform: translate(-50%, -50%) scale(1.3);

}


.power-indicator {

position: absolute;

bottom: 80px;

width: 220px;

height: 25px;

border: 3px solid #fff;

border-radius: 12px;

background: rgba(0, 0, 0, 0.8);

overflow: hidden;

box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);

}


left: 20px;

}


right: 20px;

}


.power-bar {

height: 100%;

background: linear-gradient(90deg, #9b59b6, #8e44ad, #e74c3c);

border-radius: 9px;

transition: width 0.3s ease;

width: 0%;

box-shadow: inset 0 0 10px rgba(155, 89, 182, 0.5);

}


#ai-indicator {

position: absolute;

top: 80px;

right: 20px;

background: rgba(0, 0, 0, 0.9);

padding: 12px 18px;

border-radius: 12px;

border: 3px solid #3498db;

font-size: 13px;

color: #3498db;

box-shadow: 0 0 20px rgba(52, 152, 219, 0.3);

}


#avatar-display {

position: absolute;

top: 80px;

left: 20px;

background: rgba(0, 0, 0, 0.9);

padding: 15px 20px;

border-radius: 15px;

border: 3px solid #ffd700;

font-size: 14px;

color: #ffd700;

box-shadow: 0 0 20px rgba(255, 215, 0, 0.3);

text-align: center;

}


#next-round-timer {

position: absolute;

top: 50%;

left: 50%;

transform: translate(-50%, -50%);

background: rgba(0, 0, 0, 0.95);

padding: 30px 50px;

border-radius: 25px;

border: 4px solid #ffd700;

text-align: center;

font-size: 24px;

color: #ffd700;

text-shadow: 0 0 20px rgba(255, 215, 0, 0.8);

box-shadow: 0 0 50px rgba(255, 215, 0, 0.5);

opacity: 0;

pointer-events: none;

z-index: 1000;

transition: opacity 0.3s ease;

}


#next-round-timer.show {

opacity: 1;

}


position: absolute;

bottom: 20px;

left: 50%;

transform: translateX(-50%);

background: rgba(0, 0, 0, 0.9);

padding: 15px 25px;

border-radius: 15px;

border: 2px solid #3498db;

font-size: 14px;

text-align: center;

max-width: 500px;

}


.hidden {

display: none !important;

}


.energy-wave {

position: absolute;

pointer-events: none;

z-index: 500;

}

</style>

</head>

<body>

<div id="gameContainer">

<canvas id="gameCanvas"></canvas>

<div id="ui">

<!-- Barras de vida -->

<div id="player1Health" class="health-bar-container">

<div class="health-bar health-cat" id="p1HealthBar"></div>

<div class="player-name" id="p1Name">GATO AZUL</div>

</div>

<div id="player2Health" class="health-bar-container">

<div class="health-bar health-dog" id="p2HealthBar"></div>

<div class="player-name" id="p2Name">PERRO ROJO</div>

</div>


<!-- Contador de rondas -->

<div id="round-counter">RONDA 1</div>


<!-- Contador de batallas -->

<div id="battle-counter" class="battle-count">BATALLA #1</div>


<!-- Indicadores de poder -->

<div id="player1Power" class="power-indicator">

<div class="power-bar" id="p1PowerBar"></div>

</div>

<div id="player2Power" class="power-indicator">

<div class="power-bar" id="p2PowerBar"></div>

</div>


<!-- Display de combos -->

<div id="comboDisplay" class="combo-display"></div>


<!-- Display de avatar actual -->

<div id="avatar-display">

<div><strong>🎭 LUCHADOR ACTUAL:</strong></div>

<div id="current-avatar">🐱 GATO AZUL</div>

<div style="font-size: 11px; margin-top: 5px; color: #bdc3c7;">

Cambia automáticamente

</div>

</div>


<!-- Timer para próxima ronda -->

<div id="next-round-timer">

<div><strong>⚔️ PRÓXIMA BATALLA ⚔️</strong></div>

<div id="timer-text">Preparando nuevo luchador...</div>

<div id="countdown" style="font-size: 48px; margin-top: 15px;">3</div>

</div>


<!-- Instrucciones -->

<div id="instructions">

<strong>🖱️ CONTROLES:</strong><br>

<span style="color: #1abc9c;">1 CLICK = PATADA</span> |

<span style="color: #3498db;">2 CLICKS = PUÑO</span> |

<span style="color: #9b59b6;">3 CLICKS = ONDA VITAL</span><br>

<span style="color: #e74c3c;">CLICK MANTENIDO = RECARGAR VIDA</span>

</div>


<!-- Indicador de IA -->

<div id="ai-indicator">

<strong>🤖 IA ACTIVA</strong><br>

Dificultad: <span id="ai-difficulty">NORMAL</span>

</div>

</div>

</div>


<script>

// Variables globales del juego

let scene, camera, renderer, clock;

let player1, player2;

let gameState = 'playing'; // always playing now

let selectedAvatar = { type: 'cat', color: 'blue' };

let round = 1;

let battleCount = 1;

let wins = { player1: 0, player2: 0 };


// Lista de avatares disponibles

const catColors = ['blue', 'yellow', 'orange'];

const dogColors = ['red', 'black', 'white'];

// Contador de clicks

let clickCount = 0;

let lastClickTime = 0;

const clickThreshold = 300; // ms entre clicks para considerarlos consecutivos

let isLongClick = false;

let longClickTimer = null;

const longClickThreshold = 1000; // ms para considerar un click largo


// IA del oponente

let aiData = {

lastAction: 0,

difficulty: 'normal',

reactionTime: 1000,

aggressiveness: 0.4,

lastPlayerPosition: { x: 0, z: 0 },

comboCooldown: 0,

strategy: 'balanced',

nextAction: null,

actionDelay: 0

};


// Estados de los jugadores

let gameData = {

player1: {

health: 100,

power: 0,

position: { x: -3, y: 0, z: 0 },

isAttacking: false,

isBlocking: false,

combo: 0,

lastAttack: 0

},

player2: {

health: 100,

power: 0,

position: { x: 3, y: 0, z: 0 },

isAttacking: false,

isBlocking: false,

combo: 0,

lastAttack: 0

}

};


// Configuración de colores para gatos

const catColorSettings = {

blue: { primary: 0x3498db, secondary: 0x2980b9, accent: 0x5dade2, name: 'GATO AZUL' },

yellow: { primary: 0xf1c40f, secondary: 0xf39c12, accent: 0xffd700, name: 'GATO AMARILLO' },

orange: { primary: 0xe67e22, secondary: 0xd35400, accent: 0xe74c3c, name: 'GATO NARANJA' }

};


const dogColorSettings = {

red: { primary: 0xe74c3c, secondary: 0xc0392b, accent: 0xff6b6b, name: 'PERRO ROJO' },

black: { primary: 0x2c3e50, secondary: 0x34495e, accent: 0x7f8c8d, name: 'PERRO NEGRO' },

white: { primary: 0xecf0f1, secondary: 0xbdc3c7, accent: 0xffffff, name: 'PERRO BLANCO' }

};


function selectRandomAvatar() {

// Seleccionar color aleatorio para gato y perro

const randomCatIndex = Math.floor(Math.random() * catColors.length);

const randomDogIndex = Math.floor(Math.random() * dogColors.length);

const catColor = catColors[randomCatIndex];

const dogColor = dogColors[randomDogIndex];

selectedAvatar = { type: 'cat', color: catColor };

// Actualizar display

document.getElementById('current-avatar').textContent = `🐱 ${catColorSettings[catColor].name}`;

document.getElementById('p1Name').textContent = catColorSettings[catColor].name;

document.getElementById('p2Name').textContent = dogColorSettings[dogColor].name;

return { catColor, dogColor };

}


function resetGameState() {

// Resetear estadísticas de jugadores

gameData.player1.health = 100;

gameData.player1.power = 0;

gameData.player1.position = { x: -3, y: 0, z: 0 };

gameData.player1.isAttacking = false;

gameData.player1.isBlocking = false;

gameData.player1.combo = 0;

gameData.player2.health = 100;

gameData.player2.power = 0;

gameData.player2.position = { x: 3, y: 0, z: 0 };

gameData.player2.isAttacking = false;

gameData.player2.isBlocking = false;

gameData.player2.combo = 0;


round = 1;

wins = { player1: 0, player2: 0 };

updateUI();

}


function showNextRoundTimer(callback) {

const timerEl = document.getElementById('next-round-timer');

const countdownEl = document.getElementById('countdown');

const timerTextEl = document.getElementById('timer-text');

// Seleccionar nuevo avatar

const { catColor, dogColor } = selectRandomAvatar();

timerTextEl.textContent = `Nuevos luchadores: 🐱 ${catColorSettings[catColor].name} vs 🐶 ${dogColorSettings[dogColor].name}`;

timerEl.classList.add('show');

let countdown = 3;

countdownEl.textContent = countdown;

const countdownInterval = setInterval(() => {

countdown--;

if (countdown > 0) {

countdownEl.textContent = countdown;

} else {

countdownEl.textContent = '¡LUCHA!';

setTimeout(() => {

timerEl.classList.remove('show');

if (callback) callback();

}, 500);

clearInterval(countdownInterval);

}

}, 1000);

}


function startNewBattle() {

battleCount++;

document.getElementById('battle-counter').textContent = `BATALLA #${battleCount}`;

// Limpiar escena

if (player1) {

scene.remove(player1);

player1 = null;

}

if (player2) {

scene.remove(player2);

player2 = null;

}

// Resetear estado

resetGameState();

// Crear nuevos personajes

createCharacters();

// Volver al estado de juego

gameState = 'playing';

}


function checkGameOver() {

if (gameData.player1.health <= 0) {

wins.player2++;

setTimeout(() => {

showNextRoundTimer(() => {

startNewBattle();

});

}, 1500);

return true;

} else if (gameData.player2.health <= 0) {

wins.player1++;

setTimeout(() => {

showNextRoundTimer(() => {

startNewBattle();

});

}, 1500);

return true;

}

return false;

}


// Inicialización

function init() {

// Seleccionar avatar inicial aleatorio

selectRandomAvatar();

// Configurar Three.js

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

renderer = new THREE.WebGLRenderer({

canvas: document.getElementById('gameCanvas'),

antialias: true,

alpha: true

});

renderer.setSize(window.innerWidth, window.innerHeight);

renderer.shadowMap.enabled = true;

renderer.shadowMap.type = THREE.PCFSoftShadowMap;

renderer.setClearColor(0x0a0a0a, 1);

renderer.physicallyCorrectLights = true;

clock = new THREE.Clock();


// Configurar escena

setupScene();

setupLights();

createCharacters();

// Event listeners

document.addEventListener('mousedown', onMouseDown);

document.addEventListener('mouseup', onMouseUp);

document.addEventListener('click', onClick);

window.addEventListener('resize', onWindowResize);


// Configurar IA

setupAI();


// Actualizar UI inicial

updateUI();


// Iniciar loop de renderizado

animate();

}


function onMouseDown(e) {

// Iniciar temporizador para click largo

isLongClick = false;

longClickTimer = setTimeout(() => {

isLongClick = true;

handleLongClick();

}, longClickThreshold);

}


function onMouseUp(e) {

// Cancelar temporizador de click largo

clearTimeout(longClickTimer);

}


function onClick(e) {

if (gameState !== 'playing') return;

const currentTime = Date.now();

// Verificar si es un click consecutivo

if (currentTime - lastClickTime < clickThreshold) {

clickCount++;

} else {

clickCount = 1;

}

lastClickTime = currentTime;

// Esperar un poco para ver si hay más clicks

setTimeout(() => {

if (Date.now() - lastClickTime >= clickThreshold) {

// No hay más clicks, procesar el combo

if (clickCount === 1 && !isLongClick) {

handlePlayerAction('kick');

} else if (clickCount === 2) {

handlePlayerAction('punch');

} else if (clickCount >= 3) {

handlePlayerAction('energyWave');

}

clickCount = 0;

}

}, clickThreshold + 50);

}


function handleLongClick() {

if (gameState !== 'playing') return;

// Recargar vida

gameData.player1.health = Math.min(100, gameData.player1.health + 30);

gameData.player1.power = Math.min(100, gameData.player1.power + 20);

// Animación de recarga

animateHeal(player1);

updateUI();

showCombo('+30 VIDA');

}


function animateHeal(character) {

if (!character || character.userData.isAnimating) return;

character.userData.isAnimating = true;

// Efecto de curación

for (let i = 0; i < 15; i++) {

setTimeout(() => {

const particle = new THREE.Mesh(

new THREE.SphereGeometry(0.1, 8, 8),

new THREE.MeshBasicMaterial({ color: 0x1abc9c })

);

particle.position.copy(character.position);

particle.position.y += 0.5;

particle.position.x += (Math.random() - 0.5) * 2;

particle.position.z += (Math.random() - 0.5) * 2;

scene.add(particle);

// Animar partícula hacia arriba

const animateParticle = () => {

particle.position.y += 0.1;

particle.material.opacity -= 0.02;

if (particle.material.opacity > 0) {

requestAnimationFrame(animateParticle);

} else {

scene.remove(particle);

}

};

animateParticle();

}, i * 100);

}

setTimeout(() => {

character.userData.isAnimating = false;

}, 1500);

}


function setupScene() {

// Crear suelo

const floorGeometry = new THREE.PlaneGeometry(20, 20);

const floorMaterial = new THREE.MeshLambertMaterial({

color: 0x2c3e50,

transparent: true,

opacity: 0.8

});

const floor = new THREE.Mesh(floorGeometry, floorMaterial);

floor.rotation.x = -Math.PI / 2;

floor.receiveShadow = true;

scene.add(floor);


// Configurar cámara

camera.position.set(0, 8, 10);

camera.lookAt(0, 0, 0);

}


function setupLights() {

// Luz ambiental

const ambientLight = new THREE.AmbientLight(0x404040, 0.4);

scene.add(ambientLight);


// Luz direccional principal

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);

directionalLight.position.set(5, 10, 5);

directionalLight.castShadow = true;

directionalLight.shadow.mapSize.width = 2048;

directionalLight.shadow.mapSize.height = 2048;

scene.add(directionalLight);


// Luz de relleno

const fillLight = new THREE.DirectionalLight(0x3498db, 0.3);

fillLight.position.set(-5, 5, -5);

scene.add(fillLight);

}


function createCharacters() {

// Crear jugador 1 (gato) con avatar seleccionado

const catColor = catColorSettings[selectedAvatar.color];

player1 = createCatCharacter(catColor);

player1.position.set(gameData.player1.position.x, gameData.player1.position.y, gameData.player1.position.z);

player1.castShadow = true;

scene.add(player1);


// Crear jugador 2 (perro) - color aleatorio

const dogColor = dogColorSettings[Object.keys(dogColorSettings)[0]]; // Comienza con rojo

player2 = createDogCharacter(dogColor);

player2.position.set(gameData.player2.position.x, gameData.player2.position.y, gameData.player2.position.z);

player2.castShadow = true;

scene.add(player2);

}


function createCatCharacter(colors) {

const catGroup = new THREE.Group();


// Cuerpo principal

const bodyGeometry = new THREE.CylinderGeometry(0.6, 0.8, 1.5, 8);

const bodyMaterial = new THREE.MeshLambertMaterial({ color: colors.primary });

const body = new THREE.Mesh(bodyGeometry, bodyMaterial);

body.position.y = 1.2;

catGroup.add(body);


// Cabeza

const headGeometry = new THREE.SphereGeometry(0.6, 8, 6);

const headMaterial = new THREE.MeshLambertMaterial({ color: colors.secondary });

const head = new THREE.Mesh(headGeometry, headMaterial);

head.position.y = 2.3;

catGroup.add(head);


// Orejas

const earGeometry = new THREE.ConeGeometry(0.25, 0.5, 4);

const earMaterial = new THREE.MeshLambertMaterial({ color: colors.accent });

const leftEar = new THREE.Mesh(earGeometry, earMaterial);

leftEar.position.set(-0.35, 2.7, 0.1);

leftEar.rotation.z = -0.3;

catGroup.add(leftEar);


const rightEar = new THREE.Mesh(earGeometry, earMaterial);

rightEar.position.set(0.35, 2.7, 0.1);

rightEar.rotation.z = 0.3;

catGroup.add(rightEar);


// Cola

const tailGeometry = new THREE.CylinderGeometry(0.1, 0.2, 1.5, 6);

const tailMaterial = new THREE.MeshLambertMaterial({ color: colors.primary });

const tail = new THREE.Mesh(tailGeometry, tailMaterial);

tail.position.set(0, 1, -1);

tail.rotation.x = Math.PI / 3;

catGroup.add(tail);


return catGroup;

}


function createDogCharacter(colors) {

const dogGroup = new THREE.Group();


// Cuerpo principal

const bodyGeometry = new THREE.BoxGeometry(1.2, 1, 1.8);

const bodyMaterial = new THREE.MeshLambertMaterial({ color: colors.primary });

const body = new THREE.Mesh(bodyGeometry, bodyMaterial);

body.position.y = 1.2;

dogGroup.add(body);


// Cabeza

const headGeometry = new THREE.BoxGeometry(0.8, 0.6, 0.8);

const headMaterial = new THREE.MeshLambertMaterial({ color: colors.secondary });

const head = new THREE.Mesh(headGeometry, headMaterial);

head.position.y = 1.8;

head.position.z = 0.8;

dogGroup.add(head);


// Orejas

const earGeometry = new THREE.SphereGeometry(0.2, 8, 8);

const earMaterial = new THREE.MeshLambertMaterial({ color: colors.accent });

const leftEar = new THREE.Mesh(earGeometry, earMaterial);

leftEar.position.set(-0.3, 1.9, 0.8);

leftEar.scale.set(0.5, 1, 0.5);

dogGroup.add(leftEar);


const rightEar = new THREE.Mesh(earGeometry, earMaterial);

rightEar.position.set(0.3, 1.9, 0.8);

rightEar.scale.set(0.5, 1, 0.5);

dogGroup.add(rightEar);


// Cola

const tailGeometry = new THREE.CylinderGeometry(0.1, 0.15, 0.8);

const tailMaterial = new THREE.MeshLambertMaterial({ color: colors.primary });

const tail = new THREE.Mesh(tailGeometry, tailMaterial);

tail.position.set(0, 1, -0.8);

tail.rotation.x = Math.PI / 4;

dogGroup.add(tail);


return dogGroup;

}


function setupAI() {

switch(aiData.difficulty) {

case 'easy':

aiData.reactionTime = 1800;

aiData.aggressiveness = 0.2;

break;

case 'normal':

aiData.reactionTime = 1200;

aiData.aggressiveness = 0.4;

break;

case 'hard':

aiData.reactionTime = 800;

aiData.aggressiveness = 0.7;

break;

}

}


function handlePlayerAction(action) {

if (gameState !== 'playing' || !player1 || !player2) return;


const player = 'player1';

const character = player1;

const opponent = 'player2';

const opponentChar = player2;


switch(action) {

case 'punch':

performAttack(player, opponent, character, opponentChar, 'punch', 15);

break;

case 'kick':

performAttack(player, opponent, character, opponentChar, 'kick', 22);

break;

case 'energyWave':

if (gameData[player].power >= 60) {

performEnergyWave(player, opponent, character, opponentChar);

gameData[player].power -= 60;

}

break;

}


updateUI();

}


function performAttack(attackerKey, defenderKey, attackerChar, defenderChar, type, damage) {

if (gameData[attackerKey].isAttacking) return;


gameData[attackerKey].isAttacking = true;

setTimeout(() => gameData[attackerKey].isAttacking = false, 500);


// Animación de ataque

animateAttack(attackerChar, type);


// Verificar distancia

const distance = Math.abs(attackerChar.position.x - defenderChar.position.x);

if (distance < 2) {

// Golpe exitoso

gameData[defenderKey].health -= damage;

gameData[attackerKey].power = Math.min(100, gameData[attackerKey].power + 10);

gameData[attackerKey].combo++;


// Mostrar combo

if (gameData[attackerKey].combo > 1) {

showCombo(gameData[attackerKey].combo + ' HITS');

}


// Animación de impacto

animateHit(defenderChar);


// Partículas de impacto

createImpactEffect(defenderChar.position);

}


updateUI();

checkGameOver();

}


function performEnergyWave(attackerKey, defenderKey, attackerChar, defenderChar) {

if (gameData[attackerKey].isAttacking) return;


gameData[attackerKey].isAttacking = true;

setTimeout(() => gameData[attackerKey].isAttacking = false, 1200);


// Animación del lanzador

animateEnergyWave(attackerChar);


// Crear onda de energía visual

createEnergyWaveEffect(attackerChar.position, defenderChar.position);


// El ataque de onda vital siempre impacta

const damage = 40;

gameData[defenderKey].health -= damage;

gameData[attackerKey].combo += 3;


showCombo('🌊 ONDA VITAL! 🌊');

animateHit(defenderChar);


// Efecto especial más dramático

createWaveImpactEffect(defenderChar.position);


updateUI();

checkGameOver();

}


function animateAttack(character, type) {

if (!character || character.userData.isAnimating) return;

character.userData.isAnimating = true;

const originalX = character.position.x;

// Movimiento hacia adelante

const direction = character.position.x < 0 ? 1 : -1;

character.position.x += direction * 0.5;

// Rotación de ataque

const targetRotation = type === 'kick' ? Math.PI / 4 : Math.PI / 6;

character.rotation.y = direction * targetRotation;

setTimeout(() => {

character.position.x = originalX;

character.rotation.y = 0;

character.userData.isAnimating = false;

}, 300);

}


function animateEnergyWave(character) {

if (!character || character.userData.isAnimating) return;

character.userData.isAnimating = true;

const originalY = character.position.y;

const originalScale = { ...character.scale };

// Levitar y cargar energía

character.position.y += 0.8;

character.scale.set(1.1, 1.1, 1.1);

// Efecto de rotación

let rotation = 0;

const animateCharge = () => {

rotation += 0.1;

character.rotation.y = Math.sin(rotation) * 0.3;

if (rotation < Math.PI * 4) {

requestAnimationFrame(animateCharge);

}

};

animateCharge();

setTimeout(() => {

character.position.y = originalY;

character.scale.copy(originalScale);

character.rotation.set(0, 0, 0);

character.userData.isAnimating = false;

}, 1000);

}


function animateHit(character) {

if (!character) return;

const originalX = character.position.x;

const direction = character.position.x < 0 ? -1 : 1;

character.position.x += direction * 0.3;

setTimeout(() => {

character.position.x = originalX;

}, 200);

}


function createImpactEffect(position) {

const particleCount = 10;

for (let i = 0; i < particleCount; i++) {

const geometry = new THREE.SphereGeometry(0.05, 8, 8);

const material = new THREE.MeshBasicMaterial({

color: Math.random() > 0.5 ? 0xff6b35 : 0xffd700,

transparent: true,

opacity: 0.8

});

const particle = new THREE.Mesh(geometry, material);

particle.position.copy(position);

particle.position.x += (Math.random() - 0.5) * 2;

particle.position.y += Math.random() + 0.5;

particle.position.z += (Math.random() - 0.5) * 2;

scene.add(particle);

// Animar partícula

const velocity = {

x: (Math.random() - 0.5) * 0.2,

y: Math.random() * 0.3 + 0.1,

z: (Math.random() - 0.5) * 0.2

};

let opacity = 0.8;

const animateParticle = () => {

particle.position.x += velocity.x;

particle.position.y += velocity.y;

particle.position.z += velocity.z;

velocity.y -= 0.01; // Gravedad

opacity -= 0.02;

particle.material.opacity = opacity;

if (opacity > 0) {

requestAnimationFrame(animateParticle);

} else {

scene.remove(particle);

}

};

animateParticle();

}

}


function createEnergyWaveEffect(startPos, targetPos) {

// Crear múltiples ondas de energía

for (let wave = 0; wave < 3; wave++) {

setTimeout(() => {

const waveGeometry = new THREE.RingGeometry(0.2, 1.5 + wave * 0.5, 16);

const waveMaterial = new THREE.MeshBasicMaterial({

color: wave % 2 === 0 ? 0x1abc9c : 0x3498db,

transparent: true,

opacity: 0.8 - wave * 0.2,

side: THREE.DoubleSide

});

const waveRing = new THREE.Mesh(waveGeometry, waveMaterial);

waveRing.position.copy(startPos);

waveRing.position.y += 1;

waveRing.rotation.x = -Math.PI / 2;

scene.add(waveRing);

// Animar onda

let distance = 0;

const totalDistance = Math.sqrt(

Math.pow(targetPos.x - startPos.x, 2) +

Math.pow(targetPos.z - startPos.z, 2)

);

const animateWave = () => {

distance += 0.2;

const progress = distance / totalDistance;

if (progress < 1) {

waveRing.position.x = startPos.x + (targetPos.x - startPos.x) * progress;

waveRing.position.z = startPos.z + (targetPos.z - startPos.z) * progress;

waveRing.scale.set(1 + progress, 1 + progress, 1 + progress);

requestAnimationFrame(animateWave);

} else {

scene.remove(waveRing);

}

};

animateWave();

}, wave * 200);

}

}


function createWaveImpactEffect(position) {

// Explosión de energía al impactar

const explosionGeometry = new THREE.SphereGeometry(1, 16, 16);

const explosionMaterial = new THREE.MeshBasicMaterial({

color: 0x9b59b6,

transparent: true,

opacity: 0.8

});

const explosion = new THREE.Mesh(explosionGeometry, explosionMaterial);

explosion.position.copy(position);

explosion.position.y += 1;

scene.add(explosion);

// Animar explosión

let scale = 0.5;

const animateExplosion = () => {

scale += 0.1;

explosion.scale.set(scale, scale, scale);

explosion.material.opacity = Math.max(0, 0.8 - scale * 0.1);

if (explosion.material.opacity > 0) {

requestAnimationFrame(animateExplosion);

} else {

scene.remove(explosion);

}

};

animateExplosion();

}


function showCombo(text) {

const comboDisplay = document.getElementById('comboDisplay');

comboDisplay.textContent = text;

comboDisplay.classList.add('show');

setTimeout(() => {

comboDisplay.classList.remove('show');

}, 1000);

}


function updateAI() {

if (gameState !== 'playing' || !player2) return;


const currentTime = Date.now();

// Verificar si es momento de que la IA actúe

if (currentTime - aiData.lastAction < aiData.reactionTime) return;


const distance = Math.abs(player1.position.x - player2.position.x);

const playerHealth = gameData.player1.health;

const aiHealth = gameData.player2.health;

// Determinar estrategia de IA

let strategy = aiData.strategy;

if (aiHealth < 30) strategy = 'aggressive';

else if (playerHealth < 30) strategy = 'defensive';


// Decidir acción de la IA

const actionProbability = Math.random();


if (distance > 3) {

// Moverse hacia el jugador

if (player2.position.x > player1.position.x && player2.position.x > -8) {

player2.position.x -= 0.15;

} else if (player2.position.x < player1.position.x && player2.position.x < 8) {

player2.position.x += 0.15;

}


// Movimiento en Z ocasional

if (Math.random() < 0.3) {

if (player2.position.z > player1.position.z && player2.position.z > -8) {

player2.position.z -= 0.1;

} else if (player2.position.z < player1.position.z && player2.position.z < 8) {

player2.position.z += 0.1;

}

}

} else {

// En rango de ataque

if (strategy === 'aggressive' || actionProbability < aiData.aggressiveness) {

// Decidir tipo de ataque

if (gameData.player2.power >= 100 && Math.random() < 0.3) {

// Super ataque

performSpecialAttack('player2', 'player1', player2, player1, 'super', 50);

gameData.player2.power = 0;

} else if (gameData.player2.power >= 50 && Math.random() < 0.4) {

// Ataque de rayo

performSpecialAttack('player2', 'player1', player2, player1, 'lightning', 35);

gameData.player2.power -= 50;

} else if (Math.random() < 0.6) {

// Ataque básico

const attackType = Math.random() < 0.5 ? 'punch' : 'kick';

const damage = attackType === 'punch' ? 15 : 20;

performAttack('player2', 'player1', player2, player1, attackType, damage);

}

} else {

// Movimiento defensivo/evasivo

const evadeDirection = Math.random() < 0.5 ? -1 : 1;

if (player2.position.x + evadeDirection * 0.2 > -8 && player2.position.x + evadeDirection * 0.2 < 8) {

player2.position.x += evadeDirection * 0.2;

}

}

}


// Actualizar datos de IA

aiData.lastAction = currentTime;

aiData.lastPlayerPosition = { x: player1.position.x, z: player1.position.z };

gameData.player2.position = { ...player2.position };

}


function performSpecialAttack(attackerKey, defenderKey, attackerChar, defenderChar, type, damage) {

if (gameData[attackerKey].isAttacking) return;


gameData[attackerKey].isAttacking = true;

setTimeout(() => gameData[attackerKey].isAttacking = false, 1000);


// Animación especial

animateSpecialAttack(attackerChar, type);


// Efecto siempre golpea en ataques especiales

gameData[defenderKey].health -= damage;

gameData[attackerKey].combo += 2;


showCombo(`${type.toUpperCase()}!`);

animateHit(defenderChar);

createSpecialEffect(attackerChar.position, type);


updateUI();

checkGameOver();

}


function animateSpecialAttack(character, type) {

if (!character || character.userData.isAnimating) return;

character.userData.isAnimating = true;

const originalY = character.position.y;

if (type === 'lightning') {

// Salto con rayo

character.position.y += 1;

character.rotation.z = Math.PI * 2;

} else if (type === 'super') {

// Giro épico

character.rotation.y = Math.PI * 4;

character.scale.set(1.2, 1.2, 1.2);

}

setTimeout(() => {

character.position.y = originalY;

character.rotation.set(0, 0, 0);

character.scale.set(1, 1, 1);

character.userData.isAnimating = false;

}, 800);

}


function createSpecialEffect(position, type) {

if (type === 'lightning') {

// Efecto de rayo

for (let i = 0; i < 20; i++) {

const geometry = new THREE.CylinderGeometry(0.02, 0.02, Math.random() * 2 + 1);

const material = new THREE.MeshBasicMaterial({

color: 0x00ffff,

transparent: true,

opacity: 0.9

});

const bolt = new THREE.Mesh(geometry, material);

bolt.position.copy(position);

bolt.position.y += Math.random() * 3;

bolt.rotation.z = Math.random() * Math.PI;

scene.add(bolt);

setTimeout(() => scene.remove(bolt), 500);

}

} else if (type === 'super') {

// Efecto de super combo - explosión de energía

const ringGeometry = new THREE.RingGeometry(0.5, 2, 16);

const ringMaterial = new THREE.MeshBasicMaterial({

color: 0xff0080,

transparent: true,

opacity: 0.7,

side: THREE.DoubleSide

});

const ring = new THREE.Mesh(ringGeometry, ringMaterial);

ring.position.copy(position);

ring.rotation.x = -Math.PI / 2;

scene.add(ring);

let scale = 0.1;

const animateRing = () => {

scale += 0.1;

ring.scale.set(scale, scale, scale);

ring.material.opacity = Math.max(0, 0.7 - scale * 0.1);

if (ring.material.opacity > 0) {

requestAnimationFrame(animateRing);

} else {

scene.remove(ring);

}

};

animateRing();

}

}


function updateUI() {

// Actualizar barras de vida

const p1Health = Math.max(0, gameData.player1.health);

const p2Health = Math.max(0, gameData.player2.health);

document.getElementById('p1HealthBar').style.width = p1Health + '%';

document.getElementById('p2HealthBar').style.width = p2Health + '%';


// Actualizar barras de poder

document.getElementById('p1PowerBar').style.width = gameData.player1.power + '%';

document.getElementById('p2PowerBar').style.width = gameData.player2.power + '%';


// Actualizar contador de rondas

document.getElementById('round-counter').textContent = `RONDA ${round}`;

}


function animate() {

requestAnimationFrame(animate);

const deltaTime = clock.getDelta();

// Actualizar IA del oponente

updateAI();

// Animaciones de idle para los personajes

if (player1 && !player1.userData.isAnimating) {

player1.position.y = Math.sin(Date.now() * 0.005) * 0.1;

player1.rotation.z = Math.sin(Date.now() * 0.003) * 0.05;

}

if (player2 && !player2.userData.isAnimating) {

player2.position.y = Math.sin(Date.now() * 0.005 + Math.PI) * 0.1;

player2.rotation.z = Math.sin(Date.now() * 0.003 + Math.PI) * 0.05;

}

// Efectos de luces dinámicas

const time = Date.now() * 0.001;

if (scene.children.length > 0) {

scene.children.forEach(child => {

if (child.type === 'PointLight') {

child.intensity = 0.8 + Math.sin(time * 2) * 0.3;

}

});

}

// Renderizar

renderer.render(scene, camera);

}


function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;

camera.updateProjectionMatrix();

renderer.setSize(window.innerWidth, window.innerHeight);

}


// Inicializar el juego

init();

</script>

</body>

</html>

 
 
 

Comentarios


bottom of page