User prompt
Haz que los autos no sean tan lineales en la persecución. Ej: que se alejen del auto jugador para buscar pegarles de frente, que persigan por detrás o tengan variaciones en la ruta
User prompt
Haz que el movimiento sea variado así no hacerlo tan predecible
User prompt
Haz que la persecución tenga variedades y no sea tan lineal así no hacer tan predecible a la IA
User prompt
Agrega movimiento libre a la IA y seguimiento con variaciones así no ser tan predecible
User prompt
Haz que la IA embosque a su objetivo asi predecir y atajar su futura posición
User prompt
Agrega emboscada para la IA asi predecir y atajar la futura posición de su objetivo de forma inteligente
User prompt
Agrega formato emboscada para la IA asi predecir y atajar la futura posición de su objetivo de forma inteligente
User prompt
Agrega formato emboscada para la IA asi predecir y atajar la futura posición de su objetivo de forma inteligente
User prompt
Haz que la IA prediscan la posición de su objetivo
User prompt
Haz que los enemigos sigan al jugador con la misma lógica de movimiento de este
User prompt
Agrega a los enemigos el sistema de movimiento de player
User prompt
Haz que los enemigos sigan al jugador
User prompt
Ajusta el sistema de movimiento del jugador para permitirles el uso alos enemigos
User prompt
Haz que los enemigos sigan objetivos
User prompt
Haz que los enemigos digan a cualquier auto dependiendo su velocidad y distancia
User prompt
Agrega IA a los enemigos
User prompt
Agrega IA con el mismo sistema de movimiento y choque
User prompt
Agrega IA a los enemigos
User prompt
Haz que la desaceleración de choque sea más lenta
User prompt
Mejora las colisiones, son demasiadas imprecisas
User prompt
alarga un poco la colision y disminuye en los costados
User prompt
Arregla el error que al chocar los autos se teletransportan en distancia muy cortas,
User prompt
Mejora la lógica de choque, pulelo más y arregla el error que hace que auto jugador se teletransporte al chocar
User prompt
Haz que la transferencia de velocidad sea mayor a mayor velocidad
User prompt
Haz que la perdida sea mayor a mayor velocidad
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var EnemyCar = Container.expand(function () { var self = Container.call(this); var enemyCarGraphics = self.attachAsset('Cars', { anchorX: 0.5, anchorY: 0.5 }); // Assign random color (excluding red) var enemyColors = [0x0066ff, // Blue 0x00ff66, // Green 0xffff00, // Yellow 0xff8800, // Orange 0x8800ff, // Purple 0x00ffff, // Cyan 0xff00ff, // Magenta 0x888888, // Gray 0xffffff // White ]; var randomColorIndex = Math.floor(Math.random() * enemyColors.length); enemyCarGraphics.tint = enemyColors[randomColorIndex]; // Basic properties for enemy car (no logic yet) self.velocityX = 0; self.velocityY = 0; self.rotation = 0; // Mass property for physics calculations self.mass = 800 + Math.random() * 800; // Random mass between 800-1600 kg return self; }); var Particle = Container.expand(function () { var self = Container.call(this); // Random particle size between 10.8-25.2 pixels (20% smaller than original) var particleSize = 10.8 + Math.random() * 14.4; var particleGraphics = self.attachAsset('ParticulasVel', { anchorX: 0.5, anchorY: 0.5, scaleX: particleSize / 30, scaleY: particleSize / 30 }); // Random initial properties (20% smaller velocities) self.velocityX = (Math.random() - 0.5) * 3.2; self.velocityY = Math.random() * 2.4 + 0.8; self.lifespan = 20 + Math.random() * 20; // Reduced lifespan: 0.33-0.67 seconds at 60fps self.age = 0; self.update = function () { // Update position self.x += self.velocityX; self.y += self.velocityY; // Age particle self.age++; // Fade out over time var fadeProgress = self.age / self.lifespan; particleGraphics.alpha = 1 - fadeProgress; // Scale down over time var scaleProgress = 1 - fadeProgress * 0.5; particleGraphics.scaleX = particleSize / 30 * scaleProgress; particleGraphics.scaleY = particleSize / 30 * scaleProgress; // Apply gravity and air resistance (20% reduced for smaller scale) self.velocityY += 0.08; // Reduced gravity self.velocityX *= 0.984; // Slightly less air resistance self.velocityY *= 0.984; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Create gameplay background - 4/5 of screen height (top portion) 6; var gameplayBackground = game.attachAsset('gameplayBg', { x: 0, y: 0, anchorX: 0, anchorY: 0 }); // Create carPlayer character on top of gameplayBackground var carPlayer = gameplayBackground.attachAsset('CarPlayer', { x: 1024, // Center horizontally y: 1800, // Position in lower portion of gameplay area anchorX: 0.5, anchorY: 0.5 }); // Create array to track enemy cars var enemyCars = []; // Create 3-5 enemy cars in gameplay area var numEnemies = 3 + Math.floor(Math.random() * 3); // Random number between 3-5 for (var e = 0; e < numEnemies; e++) { var enemyCar = new EnemyCar(); // Position enemies randomly across the gameplay area enemyCar.x = 200 + Math.random() * 1648; // Keep within bounds (200 to 1848) enemyCar.y = 200 + Math.random() * 1786; // Keep within bounds (200 to 1986) enemyCars.push(enemyCar); gameplayBackground.addChild(enemyCar); } // Create UI background - 1/5 of screen height (bottom portion) var uiBackground = game.attachAsset('uiBg', { x: 0, y: 2186, anchorX: 0, anchorY: 0 }); // Create speed display text var speedText = new Text2('Speed: 0', { size: 60, fill: 0x000000 }); speedText.anchor.set(0, 0.5); speedText.x = 50; speedText.y = 2459; // Center vertically in UI area game.addChild(speedText); // Create joystickBG centered in UI background var joystickBG = uiBackground.attachAsset('JoystickBG', { x: 1024, // Center horizontally in UI y: 273, // Center vertically in UI (546/2 = 273) anchorX: 0.5, anchorY: 0.5 }); // Create point object that will follow touch position var point = null; // Create JoystickPoinr that will follow point position smoothly var joystickPoinr = game.attachAsset('JoystickPoinr', { x: 1024, y: 2459, anchorX: 0.5, anchorY: 0.5 }); // Variables for smooth movement var targetX = 1024; var targetY = 2459; var smoothSpeed = 0.2; // Variables for smooth rotation var targetRotation = 0; var baseRotationSpeed = 0.052; // Variables for realistic car physics var currentVelocity = 0; var acceleration = 0.16; var deceleration = 0.44; var maxSpeed = 15.36; // Vehicle mass properties var playerMass = 1200; // Player car mass in kg // Variables for drift physics var velocityX = 0; var velocityY = 0; var driftFactor = 0.85; // How much momentum is retained (lower = more drift) var gripFactor = 0.3; // How quickly car aligns with direction (lower = more drift) // Handle touch down - create and show point game.down = function (x, y, obj) { // Create point at touch position point = game.attachAsset('Puntero', { x: x, y: y, anchorX: 0.5, anchorY: 0.5 }); }; // Handle touch move - update point position game.move = function (x, y, obj) { if (point) { point.x = x; point.y = y; // Calculate joystickBG world position var joystickWorldX = joystickBG.x + uiBackground.x; var joystickWorldY = joystickBG.y + uiBackground.y; // Calculate distance from joystick center var deltaX = x - joystickWorldX; var deltaY = y - joystickWorldY; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var maxRadius = joystickBG.width / 2; // Limit movement to joystick radius if (distance > maxRadius) { var angle = Math.atan2(deltaY, deltaX); deltaX = Math.cos(angle) * maxRadius; deltaY = Math.sin(angle) * maxRadius; } // Update target position for smooth movement (constrained) targetX = joystickWorldX + deltaX; targetY = joystickWorldY + deltaY; } }; // Handle touch up - remove point game.up = function (x, y, obj) { if (point) { point.destroy(); point = null; // Reset target position to joystick center var joystickWorldX = joystickBG.x + uiBackground.x; var joystickWorldY = joystickBG.y + uiBackground.y; targetX = joystickWorldX; targetY = joystickWorldY; } }; // Update function for smooth movement game.update = function () { // Smoothly move JoystickPoinr towards target position var deltaX = targetX - joystickPoinr.x; var deltaY = targetY - joystickPoinr.y; joystickPoinr.x += deltaX * smoothSpeed; joystickPoinr.y += deltaY * smoothSpeed; // Double-check that joystickPoinr stays within bounds var joystickWorldX = joystickBG.x + uiBackground.x; var joystickWorldY = joystickBG.y + uiBackground.y; var currentDeltaX = joystickPoinr.x - joystickWorldX; var currentDeltaY = joystickPoinr.y - joystickWorldY; var currentDistance = Math.sqrt(currentDeltaX * currentDeltaX + currentDeltaY * currentDeltaY); var maxRadius = joystickBG.width / 2; if (currentDistance > maxRadius) { var angle = Math.atan2(currentDeltaY, currentDeltaX); joystickPoinr.x = joystickWorldX + Math.cos(angle) * maxRadius; joystickPoinr.y = joystickWorldY + Math.sin(angle) * maxRadius; } // Update car rotation based on joystick position var joystickOffsetX = joystickPoinr.x - joystickWorldX; var joystickOffsetY = joystickPoinr.y - joystickWorldY; var joystickDistance = Math.sqrt(joystickOffsetX * joystickOffsetX + joystickOffsetY * joystickOffsetY); // Calculate power based on distance from center (0 to 1) var power = Math.min(joystickDistance / maxRadius, 1); // Only rotate if joystick is moved significantly from center if (joystickDistance > 10) { var joystickAngle = Math.atan2(joystickOffsetX, -joystickOffsetY); targetRotation = joystickAngle; } // Smoothly interpolate car rotation towards target var rotationDelta = targetRotation - carPlayer.rotation; // Handle angle wrapping for shortest rotation path while (rotationDelta > Math.PI) { rotationDelta -= 2 * Math.PI; } while (rotationDelta < -Math.PI) { rotationDelta += 2 * Math.PI; } // Calculate rotation speed based on current velocity (slower speed = much slower turning) var speedRatio = Math.sqrt(velocityX * velocityX + velocityY * velocityY) / maxSpeed; var dynamicRotationSpeed = baseRotationSpeed * Math.max(0.1, speedRatio); // Minimum 10% rotation speed carPlayer.rotation += rotationDelta * dynamicRotationSpeed; // Calculate target velocity based on joystick power var targetVelocity = maxSpeed * power; // Apply smooth velocity transitions with exponential interpolation if (power > 0.1) { // Accelerating - smooth exponential approach to target velocity var velocityDiff = targetVelocity - currentVelocity; var accelerationRate = 0.004; // Smooth acceleration rate (20% slower for smaller car) currentVelocity += velocityDiff * accelerationRate; } else { // Decelerating when joystick is near center - smooth exponential decay var decelerationRate = 0.048; // Smooth deceleration rate (20% slower for smaller car) currentVelocity *= 1 - decelerationRate; if (Math.abs(currentVelocity) < 0.1) { currentVelocity = 0; } } // Limit velocity to max speed currentVelocity = Math.min(currentVelocity, maxSpeed); // Calculate intended movement direction based on car rotation and current velocity var intendedMoveX = Math.sin(carPlayer.rotation) * currentVelocity; var intendedMoveY = -Math.cos(carPlayer.rotation) * currentVelocity; // Calculate turning friction based on dynamic rotation speed var rotationFriction = Math.abs(rotationDelta * dynamicRotationSpeed) * 0.8; // Reduced friction intensity for smoother feel var frictionMultiplier = Math.max(0.85, 1 - rotationFriction); // Less velocity reduction when turning (min 0.85 for more natural feel) // Apply drift physics - blend current momentum with intended direction velocityX = velocityX * driftFactor + intendedMoveX * gripFactor; velocityY = velocityY * driftFactor + intendedMoveY * gripFactor; // Apply turning friction to reduce velocity when steering velocityX *= frictionMultiplier; velocityY *= frictionMultiplier; // Apply some base deceleration to drift momentum velocityX *= 0.98; velocityY *= 0.98; // Update car position using drift momentum carPlayer.x += velocityX; carPlayer.y += velocityY; // Keep car within gameplay area bounds with realistic collision physics var halfCarWidth = 16; // CarPlayer width is 32, so half is 16 var halfCarHeight = 24; // CarPlayer height is 47.36, so half is ~24 // Calculate current speed for impact calculations var currentSpeed = Math.sqrt(velocityX * velocityX + velocityY * velocityY); var impactThreshold = 2; // Minimum speed to trigger impact effects // Realistic collision physics with energy loss and proper angles if (carPlayer.x < halfCarWidth) { carPlayer.x = halfCarWidth; // Calculate impact intensity based on perpendicular velocity component var impactVelocity = Math.abs(velocityX); var energyLoss = 0.4 + impactVelocity / maxSpeed * 0.3; // More energy loss at higher speeds // Realistic bounce with energy conservation velocityX = -velocityX * (1 - energyLoss); velocityY *= 0.8; // Friction reduces parallel velocity component // Visual feedback for significant impacts if (impactVelocity > impactThreshold) { LK.effects.flashObject(carPlayer, 0xff4444, 200); } } if (carPlayer.x > 2048 - halfCarWidth) { carPlayer.x = 2048 - halfCarWidth; // Calculate impact intensity based on perpendicular velocity component var impactVelocity = Math.abs(velocityX); var energyLoss = 0.4 + impactVelocity / maxSpeed * 0.3; // More energy loss at higher speeds // Realistic bounce with energy conservation velocityX = -velocityX * (1 - energyLoss); velocityY *= 0.8; // Friction reduces parallel velocity component // Visual feedback for significant impacts if (impactVelocity > impactThreshold) { LK.effects.flashObject(carPlayer, 0xff4444, 200); } } if (carPlayer.y < halfCarHeight) { carPlayer.y = halfCarHeight; // Calculate impact intensity based on perpendicular velocity component var impactVelocity = Math.abs(velocityY); var energyLoss = 0.4 + impactVelocity / maxSpeed * 0.3; // More energy loss at higher speeds // Realistic bounce with energy conservation velocityY = -velocityY * (1 - energyLoss); velocityX *= 0.8; // Friction reduces parallel velocity component // Visual feedback for significant impacts if (impactVelocity > impactThreshold) { LK.effects.flashObject(carPlayer, 0xff4444, 200); } } if (carPlayer.y > 2186 - halfCarHeight) { carPlayer.y = 2186 - halfCarHeight; // Calculate impact intensity based on perpendicular velocity component var impactVelocity = Math.abs(velocityY); var energyLoss = 0.4 + impactVelocity / maxSpeed * 0.3; // More energy loss at higher speeds // Realistic bounce with energy conservation velocityY = -velocityY * (1 - energyLoss); velocityX *= 0.8; // Friction reduces parallel velocity component // Visual feedback for significant impacts if (impactVelocity > impactThreshold) { LK.effects.flashObject(carPlayer, 0xff4444, 200); } } // Particle system for car exhaust if (!game.particles) { game.particles = []; } // Generate particles proportional to current velocity (from very little to much) var totalSpeed = Math.sqrt(velocityX * velocityX + velocityY * velocityY); var speedRatio = totalSpeed / maxSpeed; // 0 to 1 ratio of current speed to max speed // Calculate particle generation frequency based on speed (more speed = more frequent particles) var particleFrequency = Math.max(1, Math.floor(8 - speedRatio * 6)); // From every 8 ticks (slow) to every 2 ticks (fast) if (speedRatio > 0.05 && LK.ticks % particleFrequency === 0) { // Only generate particles when moving at least 5% of max speed // Calculate particle spawn position further behind the car (20% closer for smaller car) var particleSpawnX = carPlayer.x - Math.sin(carPlayer.rotation) * 55; var particleSpawnY = carPlayer.y + Math.cos(carPlayer.rotation) * 55; // Create particles based on speed - more speed = more particles var particleCount = Math.max(1, Math.floor(speedRatio * 3)); // 1-3 particles based on speed ratio for (var p = 0; p < particleCount; p++) { // Create new particle var newParticle = new Particle(); newParticle.x = particleSpawnX + (Math.random() - 0.5) * 19.2; // 20% smaller spawn area newParticle.y = particleSpawnY + (Math.random() - 0.5) * 19.2; // Add velocity variation based on car movement (20% reduced for smaller scale) newParticle.velocityX += -velocityX * 0.12 + (Math.random() - 0.5) * 2.4; newParticle.velocityY += -velocityY * 0.12 + (Math.random() - 0.5) * 2.4; game.particles.push(newParticle); gameplayBackground.addChild(newParticle); // Tween particle color for variety var colors = [0xffffff, 0xcccccc, 0x999999, 0x666666]; var randomColor = colors[Math.floor(Math.random() * colors.length)]; tween(newParticle.children[0], { tint: randomColor }, { duration: 100 }); } } // Update and clean up particles for (var i = game.particles.length - 1; i >= 0; i--) { var particle = game.particles[i]; if (particle.age >= particle.lifespan) { particle.destroy(); game.particles.splice(i, 1); } } // Check collision between player car and all enemy cars if (!carPlayer.lastColliding) carPlayer.lastColliding = []; var anyCollision = false; for (var ec = 0; ec < enemyCars.length; ec++) { var enemyCar = enemyCars[ec]; if (!carPlayer.lastColliding[ec]) carPlayer.lastColliding[ec] = false; // Precise collision detection using actual car dimensions var playerHalfWidth = 32; // CarPlayer actual half width var playerHalfHeight = 47; // CarPlayer actual half height var enemyHalfWidth = 32; // Enemy car actual half width var enemyHalfHeight = 47; // Enemy car actual half height // Calculate actual distance between car centers var deltaX = carPlayer.x - enemyCar.x; var deltaY = carPlayer.y - enemyCar.y; var actualDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Minimum separation distance based on actual car sizes var minDistance = Math.sqrt((playerHalfWidth + enemyHalfWidth) * (playerHalfWidth + enemyHalfWidth) + (playerHalfHeight + enemyHalfHeight) * (playerHalfHeight + enemyHalfHeight)) * 0.8; var currentColliding = actualDistance < minDistance; if (!carPlayer.lastColliding[ec] && currentColliding) { // Collision just started - calculate collision physics var carSpeed = Math.sqrt(velocityX * velocityX + velocityY * velocityY); // Calculate collision direction (from enemy car to player car) var collisionDeltaX = carPlayer.x - enemyCar.x; var collisionDeltaY = carPlayer.y - enemyCar.y; var collisionDistance = Math.sqrt(collisionDeltaX * collisionDeltaX + collisionDeltaY * collisionDeltaY); // Normalize collision direction if (collisionDistance > 0) { collisionDeltaX /= collisionDistance; collisionDeltaY /= collisionDistance; } // Calculate player car facing direction var playerFacingX = Math.sin(carPlayer.rotation); var playerFacingY = -Math.cos(carPlayer.rotation); // Calculate how much player velocity aligns with their facing direction var velocityMagnitude = Math.sqrt(velocityX * velocityX + velocityY * velocityY); var facingDotVelocity = 0; if (velocityMagnitude > 0) { // Normalize velocity vector var normalizedVelX = velocityX / velocityMagnitude; var normalizedVelY = velocityY / velocityMagnitude; // Calculate dot product to see if moving forward or backward facingDotVelocity = playerFacingX * normalizedVelX + playerFacingY * normalizedVelY; } // Calculate momentum transfer based on player's facing direction, velocity alignment, and speed var baseTransferRatio = 0.6; // Base amount of velocity to transfer var directionBonus = Math.max(0, facingDotVelocity) * 0.4; // Bonus for moving forward var speedRatio = Math.min(velocityMagnitude / maxSpeed, 1); var speedBonus = speedRatio * 0.3 + speedRatio * speedRatio * 0.7; // Quadratic scaling: up to 100% more transfer at max speed var totalTransferRatio = baseTransferRatio + directionBonus + speedBonus; // Calculate mass-based momentum transfer var massRatio = playerMass / (playerMass + enemyCar.mass); var transferMultiplier = totalTransferRatio * massRatio; // Transfer velocity from player to enemy car based on facing direction var transferVelocityX = velocityX * transferMultiplier; var transferVelocityY = velocityY * transferMultiplier; // Apply transferred velocity to enemy car enemyCar.velocityX += transferVelocityX; enemyCar.velocityY += transferVelocityY; // Calculate collision damage based on impact velocity with quadratic scaling for higher speeds var impactVelocity = Math.sqrt(velocityX * velocityX + velocityY * velocityY); var speedRatio = impactVelocity / maxSpeed; var velocityLossRatio = Math.min(0.6, speedRatio * 0.2 + speedRatio * speedRatio * 0.4); // Up to 60% velocity loss, quadratic scaling var accelerationLossRatio = Math.min(0.5, speedRatio * 0.15 + speedRatio * speedRatio * 0.35); // Up to 50% acceleration loss, quadratic scaling // Reduce player velocity by transferred amount plus collision damage var totalVelocityLoss = 0.8 + velocityLossRatio; velocityX -= transferVelocityX * totalVelocityLoss; velocityY -= transferVelocityY * totalVelocityLoss; // Reduce maximum speed and acceleration temporarily based on impact maxSpeed *= 1 - velocityLossRatio; acceleration *= 1 - accelerationLossRatio; // Add collision bounce based on collision normal var bounceStrength = carSpeed * 0.3; var playerBounceX = -collisionDeltaX * bounceStrength * (enemyCar.mass / (playerMass + enemyCar.mass)); var playerBounceY = -collisionDeltaY * bounceStrength * (enemyCar.mass / (playerMass + enemyCar.mass)); var enemyBounceX = collisionDeltaX * bounceStrength * (playerMass / (playerMass + enemyCar.mass)); var enemyBounceY = collisionDeltaY * bounceStrength * (playerMass / (playerMass + enemyCar.mass)); // Apply bounce velocities velocityX += playerBounceX; velocityY += playerBounceY; enemyCar.velocityX += enemyBounceX; enemyCar.velocityY += enemyBounceY; // Separate cars to prevent overlap with precise calculations var exactOverlap = minDistance - actualDistance; // Only separate if cars are actually overlapping if (exactOverlap > 0) { // Normalize separation direction var normalX = deltaX / actualDistance; var normalY = deltaY / actualDistance; // Calculate separation based on exact overlap with smooth correction var separationAmount = exactOverlap * 0.6; // More responsive separation var totalMass = playerMass + enemyCar.mass; var playerSeparation = separationAmount * (enemyCar.mass / totalMass); var enemySeparation = separationAmount * (playerMass / totalMass); // Apply separation in normalized direction carPlayer.x += normalX * playerSeparation; carPlayer.y += normalY * playerSeparation; enemyCar.x -= normalX * enemySeparation; enemyCar.y -= normalY * enemySeparation; } // Visual feedback for collision LK.effects.flashObject(carPlayer, 0xff0000, 500); // Play collision sound var collisionSounds = ['SonidoChoque', 'SonidoChoque2', 'SonidoChoque3']; var randomSound = collisionSounds[Math.floor(Math.random() * collisionSounds.length)]; LK.getSound(randomSound).play(); anyCollision = true; } // Continuous separation while still colliding if (currentColliding) { // Use already calculated precise values from collision detection var exactOverlap = minDistance - actualDistance; // Apply continuous separation only if still overlapping if (exactOverlap > 0) { // Normalize separation direction var normalX = deltaX / actualDistance; var normalY = deltaY / actualDistance; // Apply gentle continuous separation var separationAmount = exactOverlap * 0.4; // Move both cars apart based on mass ratio var totalMass = playerMass + enemyCar.mass; var playerSeparation = separationAmount * (enemyCar.mass / totalMass); var enemySeparation = separationAmount * (playerMass / totalMass); // Apply separation in normalized direction carPlayer.x += normalX * playerSeparation; carPlayer.y += normalY * playerSeparation; enemyCar.x -= normalX * enemySeparation; enemyCar.y -= normalY * enemySeparation; } } // Update last collision state for this enemy carPlayer.lastColliding[ec] = currentColliding; if (currentColliding) anyCollision = true; } // Check enemy-to-enemy collisions if (!game.enemyLastColliding) game.enemyLastColliding = []; for (var ec1 = 0; ec1 < enemyCars.length; ec1++) { if (!game.enemyLastColliding[ec1]) game.enemyLastColliding[ec1] = []; var enemy1 = enemyCars[ec1]; for (var ec2 = ec1 + 1; ec2 < enemyCars.length; ec2++) { if (!game.enemyLastColliding[ec1][ec2]) game.enemyLastColliding[ec1][ec2] = false; var enemy2 = enemyCars[ec2]; // Precise collision detection for enemy-enemy collisions var enemy1HalfWidth = 32; var enemy1HalfHeight = 47; var enemy2HalfWidth = 32; var enemy2HalfHeight = 47; // Calculate actual distance between enemy car centers var enemyDeltaX = enemy1.x - enemy2.x; var enemyDeltaY = enemy1.y - enemy2.y; var enemyActualDistance = Math.sqrt(enemyDeltaX * enemyDeltaX + enemyDeltaY * enemyDeltaY); // Minimum separation distance for enemy cars var enemyMinDistance = Math.sqrt((enemy1HalfWidth + enemy2HalfWidth) * (enemy1HalfWidth + enemy2HalfWidth) + (enemy1HalfHeight + enemy2HalfHeight) * (enemy1HalfHeight + enemy2HalfHeight)) * 0.8; var currentColliding = enemyActualDistance < enemyMinDistance; if (!game.enemyLastColliding[ec1][ec2] && currentColliding) { // Collision just started - calculate collision physics var enemy1Speed = Math.sqrt(enemy1.velocityX * enemy1.velocityX + enemy1.velocityY * enemy1.velocityY); var enemy2Speed = Math.sqrt(enemy2.velocityX * enemy2.velocityX + enemy2.velocityY * enemy2.velocityY); // Calculate collision direction (from enemy2 to enemy1) var collisionDeltaX = enemy1.x - enemy2.x; var collisionDeltaY = enemy1.y - enemy2.y; var collisionDistance = Math.sqrt(collisionDeltaX * collisionDeltaX + collisionDeltaY * collisionDeltaY); // Normalize collision direction if (collisionDistance > 0) { collisionDeltaX /= collisionDistance; collisionDeltaY /= collisionDistance; } // Calculate momentum transfer based on mass and velocity var totalMass = enemy1.mass + enemy2.mass; var enemy1MomentumX = enemy1.velocityX * enemy1.mass; var enemy1MomentumY = enemy1.velocityY * enemy1.mass; var enemy2MomentumX = enemy2.velocityX * enemy2.mass; var enemy2MomentumY = enemy2.velocityY * enemy2.mass; // Apply conservation of momentum with energy loss var energyLoss = 0.3; // Energy lost in collision var restitution = 1 - energyLoss; // Calculate new velocities after collision var newEnemy1VelX = ((enemy1.mass - enemy2.mass) * enemy1.velocityX + 2 * enemy2.mass * enemy2.velocityX) / totalMass * restitution; var newEnemy1VelY = ((enemy1.mass - enemy2.mass) * enemy1.velocityY + 2 * enemy2.mass * enemy2.velocityY) / totalMass * restitution; var newEnemy2VelX = ((enemy2.mass - enemy1.mass) * enemy2.velocityX + 2 * enemy1.mass * enemy1.velocityX) / totalMass * restitution; var newEnemy2VelY = ((enemy2.mass - enemy1.mass) * enemy2.velocityY + 2 * enemy1.mass * enemy1.velocityY) / totalMass * restitution; // Calculate collision damage for both enemy cars with quadratic scaling var enemy1ImpactVelocity = Math.sqrt(enemy1.velocityX * enemy1.velocityX + enemy1.velocityY * enemy1.velocityY); var enemy2ImpactVelocity = Math.sqrt(enemy2.velocityX * enemy2.velocityX + enemy2.velocityY * enemy2.velocityY); var enemy1SpeedRatio = enemy1ImpactVelocity / 15; // Assuming enemy max speed of 15 var enemy2SpeedRatio = enemy2ImpactVelocity / 15; var enemy1VelocityLoss = Math.min(0.5, enemy1SpeedRatio * 0.15 + enemy1SpeedRatio * enemy1SpeedRatio * 0.35); // Up to 50% loss with quadratic scaling var enemy2VelocityLoss = Math.min(0.5, enemy2SpeedRatio * 0.15 + enemy2SpeedRatio * enemy2SpeedRatio * 0.35); // Apply new velocities with collision damage enemy1.velocityX = newEnemy1VelX * (1 - enemy1VelocityLoss); enemy1.velocityY = newEnemy1VelY * (1 - enemy1VelocityLoss); enemy2.velocityX = newEnemy2VelX * (1 - enemy2VelocityLoss); enemy2.velocityY = newEnemy2VelY * (1 - enemy2VelocityLoss); // Separate enemy cars with precise overlap calculations var enemyExactOverlap = enemyMinDistance - enemyActualDistance; // Only separate if cars are actually overlapping if (enemyExactOverlap > 0) { // Normalize separation direction var enemyNormalX = enemyDeltaX / enemyActualDistance; var enemyNormalY = enemyDeltaY / enemyActualDistance; // Calculate precise separation var separationAmount = enemyExactOverlap * 0.6; var totalMass = enemy1.mass + enemy2.mass; var enemy1Separation = separationAmount * (enemy2.mass / totalMass); var enemy2Separation = separationAmount * (enemy1.mass / totalMass); // Apply separation in normalized direction enemy1.x += enemyNormalX * enemy1Separation; enemy1.y += enemyNormalY * enemy1Separation; enemy2.x -= enemyNormalX * enemy2Separation; enemy2.y -= enemyNormalY * enemy2Separation; } // Visual feedback for collision LK.effects.flashObject(enemy1, 0xffff00, 300); LK.effects.flashObject(enemy2, 0xffff00, 300); // Play collision sound var collisionSounds = ['SonidoChoque', 'SonidoChoque2', 'SonidoChoque3']; var randomSound = collisionSounds[Math.floor(Math.random() * collisionSounds.length)]; LK.getSound(randomSound).play(); } // Continuous separation while still colliding if (currentColliding) { // Use already calculated precise values var enemyExactOverlap = enemyMinDistance - enemyActualDistance; // Apply continuous separation only if still overlapping if (enemyExactOverlap > 0) { // Normalize separation direction var enemyNormalX = enemyDeltaX / enemyActualDistance; var enemyNormalY = enemyDeltaY / enemyActualDistance; // Apply gentle continuous separation var separationAmount = enemyExactOverlap * 0.4; // Move both cars apart based on mass ratio var totalMass = enemy1.mass + enemy2.mass; var enemy1Separation = separationAmount * (enemy2.mass / totalMass); var enemy2Separation = separationAmount * (enemy1.mass / totalMass); // Apply separation in normalized direction enemy1.x += enemyNormalX * enemy1Separation; enemy1.y += enemyNormalY * enemy1Separation; enemy2.x -= enemyNormalX * enemy2Separation; enemy2.y -= enemyNormalY * enemy2Separation; } } // Update last collision state for this enemy pair game.enemyLastColliding[ec1][ec2] = currentColliding; } } // Update all enemy cars with AI movement system for (var ec = 0; ec < enemyCars.length; ec++) { var enemyCar = enemyCars[ec]; // Initialize AI movement properties if not set if (enemyCar.aiCurrentVelocity === undefined) enemyCar.aiCurrentVelocity = 0; if (enemyCar.aiTargetRotation === undefined) enemyCar.aiTargetRotation = 0; if (enemyCar.aiMaxSpeed === undefined) enemyCar.aiMaxSpeed = 10 + Math.random() * 4; // Varied max speed 10-14 if (enemyCar.aiAcceleration === undefined) enemyCar.aiAcceleration = 0.1 + Math.random() * 0.08; // Varied acceleration 0.1-0.18 if (enemyCar.aiBaseRotationSpeed === undefined) enemyCar.aiBaseRotationSpeed = 0.03 + Math.random() * 0.03; // Varied turning 0.03-0.06 if (enemyCar.aiDriftFactor === undefined) enemyCar.aiDriftFactor = 0.85 + Math.random() * 0.06; // Varied drift 0.85-0.91 if (enemyCar.aiGripFactor === undefined) enemyCar.aiGripFactor = 0.2 + Math.random() * 0.1; // Varied grip 0.2-0.3 if (enemyCar.aiDriftVelocityX === undefined) enemyCar.aiDriftVelocityX = 0; if (enemyCar.aiDriftVelocityY === undefined) enemyCar.aiDriftVelocityY = 0; // Initialize AI behavior properties for varied movement patterns if (enemyCar.aiBehaviorType === undefined) enemyCar.aiBehaviorType = Math.floor(Math.random() * 4); // 0-3 behavior types if (enemyCar.aiAggressiveness === undefined) enemyCar.aiAggressiveness = 0.3 + Math.random() * 0.7; // 0.3-1.0 aggression level if (enemyCar.aiDecisionTimer === undefined) enemyCar.aiDecisionTimer = 0; if (enemyCar.aiDecisionInterval === undefined) enemyCar.aiDecisionInterval = 60 + Math.random() * 120; // 1-3 seconds between decisions if (enemyCar.aiCurrentStrategy === undefined) enemyCar.aiCurrentStrategy = 'intercept'; if (enemyCar.aiCirclingDirection === undefined) enemyCar.aiCirclingDirection = Math.random() > 0.5 ? 1 : -1; if (enemyCar.aiPatrolTarget === undefined) { enemyCar.aiPatrolTarget = { x: 200 + Math.random() * 1648, y: 200 + Math.random() * 1786 }; } // Calculate player's current speed and velocity direction var playerSpeed = Math.sqrt(velocityX * velocityX + velocityY * velocityY); var playerVelX = velocityX; var playerVelY = velocityY; // Calculate distance to player for behavior decisions var deltaToPlayer = { x: carPlayer.x - enemyCar.x, y: carPlayer.y - enemyCar.y }; var distanceToPlayer = Math.sqrt(deltaToPlayer.x * deltaToPlayer.x + deltaToPlayer.y * deltaToPlayer.y); // Update AI decision making timer enemyCar.aiDecisionTimer++; if (enemyCar.aiDecisionTimer >= enemyCar.aiDecisionInterval) { enemyCar.aiDecisionTimer = 0; enemyCar.aiDecisionInterval = 60 + Math.random() * 120; // Randomize next decision time // Choose new strategy based on behavior type and current situation var strategies = []; switch (enemyCar.aiBehaviorType) { case 0: // Aggressive - prefers direct attack strategies = ['intercept', 'intercept', 'flanking', 'circling']; break; case 1: // Flanker - prefers side attacks strategies = ['flanking', 'flanking', 'circling', 'intercept']; break; case 2: // Circler - prefers to circle around strategies = ['circling', 'circling', 'flanking', 'patrol']; break; case 3: // Patrol - more defensive strategies = ['patrol', 'intercept', 'flanking', 'circling']; break; } enemyCar.aiCurrentStrategy = strategies[Math.floor(Math.random() * strategies.length)]; } // Execute current AI strategy var targetX, targetY, aiPower = 0; switch (enemyCar.aiCurrentStrategy) { case 'intercept': // Predict where player will be var predictionTime = 0; if (playerSpeed > 0.5) { var enemySpeed = Math.sqrt(enemyCar.aiDriftVelocityX * enemyCar.aiDriftVelocityX + enemyCar.aiDriftVelocityY * enemyCar.aiDriftVelocityY); var averageSpeed = Math.max(1, (enemySpeed + enemyCar.aiMaxSpeed) / 2); predictionTime = distanceToPlayer / averageSpeed; predictionTime = Math.min(predictionTime, 120); } targetX = carPlayer.x + playerVelX * predictionTime; targetY = carPlayer.y + playerVelY * predictionTime; // Add some randomness based on aggression var randomOffset = (1 - enemyCar.aiAggressiveness) * 100; var randomAngle = Math.random() * Math.PI * 2; targetX += Math.cos(randomAngle) * randomOffset; targetY += Math.sin(randomAngle) * randomOffset; aiPower = enemyCar.aiAggressiveness; break; case 'flanking': // Try to approach from the side var playerFacingX = Math.sin(carPlayer.rotation); var playerFacingY = -Math.cos(carPlayer.rotation); // Choose left or right side randomly var flankDirection = Math.random() > 0.5 ? 1 : -1; var sideOffsetX = -playerFacingY * flankDirection * 200; var sideOffsetY = playerFacingX * flankDirection * 200; targetX = carPlayer.x + sideOffsetX; targetY = carPlayer.y + sideOffsetY; aiPower = 0.7 + enemyCar.aiAggressiveness * 0.3; break; case 'circling': // Circle around the player var circleRadius = 150 + Math.random() * 100; var circleAngle = Math.atan2(deltaToPlayer.y, deltaToPlayer.x) + enemyCar.aiCirclingDirection * 0.02; targetX = carPlayer.x + Math.cos(circleAngle) * circleRadius; targetY = carPlayer.y + Math.sin(circleAngle) * circleRadius; aiPower = 0.5 + enemyCar.aiAggressiveness * 0.3; // Switch circling direction occasionally if (Math.random() < 0.01) { enemyCar.aiCirclingDirection *= -1; } break; case 'patrol': // Move to patrol points and occasionally attack var deltaToPatrol = { x: enemyCar.aiPatrolTarget.x - enemyCar.x, y: enemyCar.aiPatrolTarget.y - enemyCar.y }; var distanceToPatrol = Math.sqrt(deltaToPatrol.x * deltaToPatrol.x + deltaToPatrol.y * deltaToPatrol.y); if (distanceToPatrol < 100 || Math.random() < 0.005) { // Reached patrol point or randomly choose new one enemyCar.aiPatrolTarget = { x: 200 + Math.random() * 1648, y: 200 + Math.random() * 1786 }; } targetX = enemyCar.aiPatrolTarget.x; targetY = enemyCar.aiPatrolTarget.y; // Occasionally switch to aggressive mode if player is close if (distanceToPlayer < 300 && Math.random() < 0.02) { enemyCar.aiCurrentStrategy = 'intercept'; targetX = carPlayer.x; targetY = carPlayer.y; } aiPower = 0.4 + enemyCar.aiAggressiveness * 0.4; break; } // Keep target within bounds targetX = Math.max(64, Math.min(2048 - 64, targetX)); targetY = Math.max(64, Math.min(2186 - 64, targetY)); // Calculate direction to target var deltaToTarget = { x: targetX - enemyCar.x, y: targetY - enemyCar.y }; var distanceToTarget = Math.sqrt(deltaToTarget.x * deltaToTarget.x + deltaToTarget.y * deltaToTarget.y); // Calculate desired angle to face target var desiredAngle = Math.atan2(deltaToTarget.x, -deltaToTarget.y); // Add some random steering variation to make movement less robotic var steeringNoise = (Math.random() - 0.5) * 0.1 * (1 - enemyCar.aiAggressiveness); desiredAngle += steeringNoise; // Adjust power based on distance and strategy if (distanceToTarget > 100) { var speedBonus = Math.min(0.3, playerSpeed / maxSpeed); aiPower = Math.min(1, aiPower + speedBonus); } else if (distanceToTarget < 50) { aiPower *= 0.7; // Reduce power when close } // Update AI target rotation to face intercept target enemyCar.aiTargetRotation = desiredAngle; // Apply same rotation logic as player var rotationDelta = enemyCar.aiTargetRotation - enemyCar.rotation; // Handle angle wrapping for shortest rotation path while (rotationDelta > Math.PI) { rotationDelta -= 2 * Math.PI; } while (rotationDelta < -Math.PI) { rotationDelta += 2 * Math.PI; } // Calculate rotation speed based on current velocity (same as player logic) var aiSpeedRatio = Math.sqrt(enemyCar.aiDriftVelocityX * enemyCar.aiDriftVelocityX + enemyCar.aiDriftVelocityY * enemyCar.aiDriftVelocityY) / enemyCar.aiMaxSpeed; var aiDynamicRotationSpeed = enemyCar.aiBaseRotationSpeed * Math.max(0.1, aiSpeedRatio); enemyCar.rotation += rotationDelta * aiDynamicRotationSpeed; // Calculate target velocity based on AI power var aiTargetVelocity = enemyCar.aiMaxSpeed * aiPower; // Apply smooth velocity transitions with random variations var accelerationVariation = 0.8 + Math.random() * 0.4; // 0.8-1.2x variation if (aiPower > 0.1) { // Accelerating with random variation var velocityDiff = aiTargetVelocity - enemyCar.aiCurrentVelocity; var accelerationRate = 0.004 * accelerationVariation; enemyCar.aiCurrentVelocity += velocityDiff * accelerationRate; } else { // Decelerating with random variation var decelerationRate = 0.048 * accelerationVariation; enemyCar.aiCurrentVelocity *= 1 - decelerationRate; if (Math.abs(enemyCar.aiCurrentVelocity) < 0.1) { enemyCar.aiCurrentVelocity = 0; } } // Add random speed bursts occasionally if (Math.random() < 0.005 && enemyCar.aiAggressiveness > 0.6) { enemyCar.aiCurrentVelocity = Math.min(enemyCar.aiMaxSpeed * 1.2, enemyCar.aiCurrentVelocity * 1.3); } // Limit velocity to max speed enemyCar.aiCurrentVelocity = Math.min(enemyCar.aiCurrentVelocity, enemyCar.aiMaxSpeed); // Calculate intended movement direction based on rotation and velocity var intendedMoveX = Math.sin(enemyCar.rotation) * enemyCar.aiCurrentVelocity; var intendedMoveY = -Math.cos(enemyCar.rotation) * enemyCar.aiCurrentVelocity; // Calculate turning friction (same as player logic) var rotationFriction = Math.abs(rotationDelta * aiDynamicRotationSpeed) * 0.8; var frictionMultiplier = Math.max(0.85, 1 - rotationFriction); // Apply drift physics with random variations var driftVariation = 0.9 + Math.random() * 0.2; // 0.9-1.1x variation var adjustedDriftFactor = enemyCar.aiDriftFactor * driftVariation; var adjustedGripFactor = enemyCar.aiGripFactor * driftVariation; enemyCar.aiDriftVelocityX = enemyCar.aiDriftVelocityX * adjustedDriftFactor + intendedMoveX * adjustedGripFactor; enemyCar.aiDriftVelocityY = enemyCar.aiDriftVelocityY * adjustedDriftFactor + intendedMoveY * adjustedGripFactor; // Apply turning friction with variation var frictionVariation = 0.8 + Math.random() * 0.4; // 0.8-1.2x variation enemyCar.aiDriftVelocityX *= frictionMultiplier * frictionVariation; enemyCar.aiDriftVelocityY *= frictionMultiplier * frictionVariation; // Apply base deceleration with occasional hesitation var baseDeceleration = 0.98; if (Math.random() < 0.01) { // Occasional hesitation - sudden braking baseDeceleration = 0.85; } enemyCar.aiDriftVelocityX *= baseDeceleration; enemyCar.aiDriftVelocityY *= baseDeceleration; // Add random momentum changes if (Math.random() < 0.003) { var randomPushX = (Math.random() - 0.5) * 2; var randomPushY = (Math.random() - 0.5) * 2; enemyCar.aiDriftVelocityX += randomPushX; enemyCar.aiDriftVelocityY += randomPushY; } // Update enemy car position using AI drift momentum enemyCar.x += enemyCar.aiDriftVelocityX; enemyCar.y += enemyCar.aiDriftVelocityY; // Also apply the original momentum from collisions enemyCar.x += enemyCar.velocityX; enemyCar.y += enemyCar.velocityY; // Apply friction to collision momentum enemyCar.velocityX *= 0.95; enemyCar.velocityY *= 0.95; // Keep enemy car within bounds with same physics as player var enemyHalfWidth = 32; var enemyHalfHeight = 47; var enemyCurrentSpeed = Math.sqrt(enemyCar.aiDriftVelocityX * enemyCar.aiDriftVelocityX + enemyCar.aiDriftVelocityY * enemyCar.aiDriftVelocityY); var impactThreshold = 2; if (enemyCar.x < enemyHalfWidth) { enemyCar.x = enemyHalfWidth; var impactVelocity = Math.abs(enemyCar.aiDriftVelocityX); var energyLoss = 0.4 + impactVelocity / enemyCar.aiMaxSpeed * 0.3; enemyCar.aiDriftVelocityX = -enemyCar.aiDriftVelocityX * (1 - energyLoss); enemyCar.aiDriftVelocityY *= 0.8; enemyCar.velocityX = -enemyCar.velocityX * 0.6; } if (enemyCar.x > 2048 - enemyHalfWidth) { enemyCar.x = 2048 - enemyHalfWidth; var impactVelocity = Math.abs(enemyCar.aiDriftVelocityX); var energyLoss = 0.4 + impactVelocity / enemyCar.aiMaxSpeed * 0.3; enemyCar.aiDriftVelocityX = -enemyCar.aiDriftVelocityX * (1 - energyLoss); enemyCar.aiDriftVelocityY *= 0.8; enemyCar.velocityX = -enemyCar.velocityX * 0.6; } if (enemyCar.y < enemyHalfHeight) { enemyCar.y = enemyHalfHeight; var impactVelocity = Math.abs(enemyCar.aiDriftVelocityY); var energyLoss = 0.4 + impactVelocity / enemyCar.aiMaxSpeed * 0.3; enemyCar.aiDriftVelocityY = -enemyCar.aiDriftVelocityY * (1 - energyLoss); enemyCar.aiDriftVelocityX *= 0.8; enemyCar.velocityY = -enemyCar.velocityY * 0.6; } if (enemyCar.y > 2186 - enemyHalfHeight) { enemyCar.y = 2186 - enemyHalfHeight; var impactVelocity = Math.abs(enemyCar.aiDriftVelocityY); var energyLoss = 0.4 + impactVelocity / enemyCar.aiMaxSpeed * 0.3; enemyCar.aiDriftVelocityY = -enemyCar.aiDriftVelocityY * (1 - energyLoss); enemyCar.aiDriftVelocityX *= 0.8; enemyCar.velocityY = -enemyCar.velocityY * 0.6; } } // Gradual recovery of maximum speed and acceleration (damage healing over time) var recoveryRate = 0.005; // 0.5% recovery per frame (about 3 seconds for full recovery at 60fps) if (maxSpeed < 15.36) { maxSpeed = Math.min(15.36, maxSpeed + recoveryRate * 15.36); } if (acceleration < 0.16) { acceleration = Math.min(0.16, acceleration + recoveryRate * 0.16); } // Update speed display speedText.setText('Speed: ' + Math.round(totalSpeed)); };
===================================================================
--- original.js
+++ change.js
@@ -646,112 +646,138 @@
var enemyCar = enemyCars[ec];
// Initialize AI movement properties if not set
if (enemyCar.aiCurrentVelocity === undefined) enemyCar.aiCurrentVelocity = 0;
if (enemyCar.aiTargetRotation === undefined) enemyCar.aiTargetRotation = 0;
- if (enemyCar.aiMaxSpeed === undefined) enemyCar.aiMaxSpeed = 10 + Math.random() * 6; // Variable speed: 10-16
- if (enemyCar.aiAcceleration === undefined) enemyCar.aiAcceleration = 0.10 + Math.random() * 0.08; // Variable acceleration: 0.10-0.18
- if (enemyCar.aiBaseRotationSpeed === undefined) enemyCar.aiBaseRotationSpeed = 0.035 + Math.random() * 0.025; // Variable turning: 0.035-0.06
- if (enemyCar.aiDriftFactor === undefined) enemyCar.aiDriftFactor = 0.82 + Math.random() * 0.12; // Variable drift: 0.82-0.94
- if (enemyCar.aiGripFactor === undefined) enemyCar.aiGripFactor = 0.20 + Math.random() * 0.15; // Variable grip: 0.20-0.35
+ if (enemyCar.aiMaxSpeed === undefined) enemyCar.aiMaxSpeed = 10 + Math.random() * 4; // Varied max speed 10-14
+ if (enemyCar.aiAcceleration === undefined) enemyCar.aiAcceleration = 0.1 + Math.random() * 0.08; // Varied acceleration 0.1-0.18
+ if (enemyCar.aiBaseRotationSpeed === undefined) enemyCar.aiBaseRotationSpeed = 0.03 + Math.random() * 0.03; // Varied turning 0.03-0.06
+ if (enemyCar.aiDriftFactor === undefined) enemyCar.aiDriftFactor = 0.85 + Math.random() * 0.06; // Varied drift 0.85-0.91
+ if (enemyCar.aiGripFactor === undefined) enemyCar.aiGripFactor = 0.2 + Math.random() * 0.1; // Varied grip 0.2-0.3
if (enemyCar.aiDriftVelocityX === undefined) enemyCar.aiDriftVelocityX = 0;
if (enemyCar.aiDriftVelocityY === undefined) enemyCar.aiDriftVelocityY = 0;
- // Assign AI personality if not set
- if (enemyCar.aiPersonality === undefined) {
- var personalities = ['aggressive', 'interceptor', 'circler', 'cautious', 'erratic'];
- enemyCar.aiPersonality = personalities[ec % personalities.length]; // Distribute personalities evenly
- enemyCar.aiPersonalityTimer = 0;
- enemyCar.aiPersonalityState = 0;
+ // Initialize AI behavior properties for varied movement patterns
+ if (enemyCar.aiBehaviorType === undefined) enemyCar.aiBehaviorType = Math.floor(Math.random() * 4); // 0-3 behavior types
+ if (enemyCar.aiAggressiveness === undefined) enemyCar.aiAggressiveness = 0.3 + Math.random() * 0.7; // 0.3-1.0 aggression level
+ if (enemyCar.aiDecisionTimer === undefined) enemyCar.aiDecisionTimer = 0;
+ if (enemyCar.aiDecisionInterval === undefined) enemyCar.aiDecisionInterval = 60 + Math.random() * 120; // 1-3 seconds between decisions
+ if (enemyCar.aiCurrentStrategy === undefined) enemyCar.aiCurrentStrategy = 'intercept';
+ if (enemyCar.aiCirclingDirection === undefined) enemyCar.aiCirclingDirection = Math.random() > 0.5 ? 1 : -1;
+ if (enemyCar.aiPatrolTarget === undefined) {
+ enemyCar.aiPatrolTarget = {
+ x: 200 + Math.random() * 1648,
+ y: 200 + Math.random() * 1786
+ };
}
// Calculate player's current speed and velocity direction
var playerSpeed = Math.sqrt(velocityX * velocityX + velocityY * velocityY);
var playerVelX = velocityX;
var playerVelY = velocityY;
- // Calculate distance to player
+ // Calculate distance to player for behavior decisions
var deltaToPlayer = {
x: carPlayer.x - enemyCar.x,
y: carPlayer.y - enemyCar.y
};
var distanceToPlayer = Math.sqrt(deltaToPlayer.x * deltaToPlayer.x + deltaToPlayer.y * deltaToPlayer.y);
- // Initialize target variables
- var targetX = carPlayer.x;
- var targetY = carPlayer.y;
- var aiPower = 0.8;
- // Update personality timer
- enemyCar.aiPersonalityTimer++;
- // Apply different AI behaviors based on personality
- if (enemyCar.aiPersonality === 'aggressive') {
- // Direct aggressive pursuit - always heads straight for player
- targetX = carPlayer.x;
- targetY = carPlayer.y;
- aiPower = 1.0; // Full power
- } else if (enemyCar.aiPersonality === 'interceptor') {
- // Predictive ambush - intercepts player's future position
- var predictionTime = 0;
- if (playerSpeed > 0.5) {
- var enemySpeed = Math.sqrt(enemyCar.aiDriftVelocityX * enemyCar.aiDriftVelocityX + enemyCar.aiDriftVelocityY * enemyCar.aiDriftVelocityY);
- var averageSpeed = Math.max(1, (enemySpeed + enemyCar.aiMaxSpeed) / 2);
- predictionTime = distanceToPlayer / averageSpeed;
- predictionTime = Math.min(predictionTime, 100);
+ // Update AI decision making timer
+ enemyCar.aiDecisionTimer++;
+ if (enemyCar.aiDecisionTimer >= enemyCar.aiDecisionInterval) {
+ enemyCar.aiDecisionTimer = 0;
+ enemyCar.aiDecisionInterval = 60 + Math.random() * 120; // Randomize next decision time
+ // Choose new strategy based on behavior type and current situation
+ var strategies = [];
+ switch (enemyCar.aiBehaviorType) {
+ case 0:
+ // Aggressive - prefers direct attack
+ strategies = ['intercept', 'intercept', 'flanking', 'circling'];
+ break;
+ case 1:
+ // Flanker - prefers side attacks
+ strategies = ['flanking', 'flanking', 'circling', 'intercept'];
+ break;
+ case 2:
+ // Circler - prefers to circle around
+ strategies = ['circling', 'circling', 'flanking', 'patrol'];
+ break;
+ case 3:
+ // Patrol - more defensive
+ strategies = ['patrol', 'intercept', 'flanking', 'circling'];
+ break;
}
- targetX = carPlayer.x + playerVelX * predictionTime;
- targetY = carPlayer.y + playerVelY * predictionTime;
- aiPower = 0.9;
- } else if (enemyCar.aiPersonality === 'circler') {
- // Circles around player trying to hit from sides
- var circleRadius = 200 + Math.sin(enemyCar.aiPersonalityTimer * 0.02) * 50; // Variable radius
- var circleAngle = enemyCar.aiPersonalityTimer * 0.03 + ec * 2; // Different phase for each car
- targetX = carPlayer.x + Math.cos(circleAngle) * circleRadius;
- targetY = carPlayer.y + Math.sin(circleAngle) * circleRadius;
- // More aggressive when close to player
- aiPower = distanceToPlayer < 150 ? 1.0 : 0.6;
- } else if (enemyCar.aiPersonality === 'cautious') {
- // Approaches carefully, backs off when too close
- if (distanceToPlayer > 250) {
- // Approach player when far away
- targetX = carPlayer.x;
- targetY = carPlayer.y;
- aiPower = 0.7;
- } else if (distanceToPlayer < 120) {
- // Back away when too close
- targetX = enemyCar.x - deltaToPlayer.x * 0.5;
- targetY = enemyCar.y - deltaToPlayer.y * 0.5;
- aiPower = 0.8;
- } else {
- // Maintain distance and circle
- var sideAngle = Math.atan2(deltaToPlayer.y, deltaToPlayer.x) + Math.PI * 0.5;
- targetX = carPlayer.x + Math.cos(sideAngle) * 180;
- targetY = carPlayer.y + Math.sin(sideAngle) * 180;
- aiPower = 0.5;
- }
- } else if (enemyCar.aiPersonality === 'erratic') {
- // Unpredictable behavior that changes every few seconds
- var behaviorCycle = Math.floor(enemyCar.aiPersonalityTimer / 180) % 4; // Change every 3 seconds
- if (behaviorCycle === 0) {
- // Direct attack
- targetX = carPlayer.x;
- targetY = carPlayer.y;
- aiPower = 1.0;
- } else if (behaviorCycle === 1) {
- // Circle behavior
- var angle = enemyCar.aiPersonalityTimer * 0.05;
- targetX = carPlayer.x + Math.cos(angle) * 150;
- targetY = carPlayer.y + Math.sin(angle) * 150;
- aiPower = 0.8;
- } else if (behaviorCycle === 2) {
- // Intercept behavior
- var predTime = Math.min(60, distanceToPlayer / 8);
- targetX = carPlayer.x + playerVelX * predTime;
- targetY = carPlayer.y + playerVelY * predTime;
- aiPower = 0.9;
- } else {
- // Random movement with slight player bias
- var randomAngle = enemyCar.aiPersonalityTimer * 0.02 + ec;
- targetX = carPlayer.x + Math.cos(randomAngle) * 300 + (Math.random() - 0.5) * 200;
- targetY = carPlayer.y + Math.sin(randomAngle) * 300 + (Math.random() - 0.5) * 200;
- aiPower = 0.6;
- }
+ enemyCar.aiCurrentStrategy = strategies[Math.floor(Math.random() * strategies.length)];
}
- // Keep target within gameplay bounds
+ // Execute current AI strategy
+ var targetX,
+ targetY,
+ aiPower = 0;
+ switch (enemyCar.aiCurrentStrategy) {
+ case 'intercept':
+ // Predict where player will be
+ var predictionTime = 0;
+ if (playerSpeed > 0.5) {
+ var enemySpeed = Math.sqrt(enemyCar.aiDriftVelocityX * enemyCar.aiDriftVelocityX + enemyCar.aiDriftVelocityY * enemyCar.aiDriftVelocityY);
+ var averageSpeed = Math.max(1, (enemySpeed + enemyCar.aiMaxSpeed) / 2);
+ predictionTime = distanceToPlayer / averageSpeed;
+ predictionTime = Math.min(predictionTime, 120);
+ }
+ targetX = carPlayer.x + playerVelX * predictionTime;
+ targetY = carPlayer.y + playerVelY * predictionTime;
+ // Add some randomness based on aggression
+ var randomOffset = (1 - enemyCar.aiAggressiveness) * 100;
+ var randomAngle = Math.random() * Math.PI * 2;
+ targetX += Math.cos(randomAngle) * randomOffset;
+ targetY += Math.sin(randomAngle) * randomOffset;
+ aiPower = enemyCar.aiAggressiveness;
+ break;
+ case 'flanking':
+ // Try to approach from the side
+ var playerFacingX = Math.sin(carPlayer.rotation);
+ var playerFacingY = -Math.cos(carPlayer.rotation);
+ // Choose left or right side randomly
+ var flankDirection = Math.random() > 0.5 ? 1 : -1;
+ var sideOffsetX = -playerFacingY * flankDirection * 200;
+ var sideOffsetY = playerFacingX * flankDirection * 200;
+ targetX = carPlayer.x + sideOffsetX;
+ targetY = carPlayer.y + sideOffsetY;
+ aiPower = 0.7 + enemyCar.aiAggressiveness * 0.3;
+ break;
+ case 'circling':
+ // Circle around the player
+ var circleRadius = 150 + Math.random() * 100;
+ var circleAngle = Math.atan2(deltaToPlayer.y, deltaToPlayer.x) + enemyCar.aiCirclingDirection * 0.02;
+ targetX = carPlayer.x + Math.cos(circleAngle) * circleRadius;
+ targetY = carPlayer.y + Math.sin(circleAngle) * circleRadius;
+ aiPower = 0.5 + enemyCar.aiAggressiveness * 0.3;
+ // Switch circling direction occasionally
+ if (Math.random() < 0.01) {
+ enemyCar.aiCirclingDirection *= -1;
+ }
+ break;
+ case 'patrol':
+ // Move to patrol points and occasionally attack
+ var deltaToPatrol = {
+ x: enemyCar.aiPatrolTarget.x - enemyCar.x,
+ y: enemyCar.aiPatrolTarget.y - enemyCar.y
+ };
+ var distanceToPatrol = Math.sqrt(deltaToPatrol.x * deltaToPatrol.x + deltaToPatrol.y * deltaToPatrol.y);
+ if (distanceToPatrol < 100 || Math.random() < 0.005) {
+ // Reached patrol point or randomly choose new one
+ enemyCar.aiPatrolTarget = {
+ x: 200 + Math.random() * 1648,
+ y: 200 + Math.random() * 1786
+ };
+ }
+ targetX = enemyCar.aiPatrolTarget.x;
+ targetY = enemyCar.aiPatrolTarget.y;
+ // Occasionally switch to aggressive mode if player is close
+ if (distanceToPlayer < 300 && Math.random() < 0.02) {
+ enemyCar.aiCurrentStrategy = 'intercept';
+ targetX = carPlayer.x;
+ targetY = carPlayer.y;
+ }
+ aiPower = 0.4 + enemyCar.aiAggressiveness * 0.4;
+ break;
+ }
+ // Keep target within bounds
targetX = Math.max(64, Math.min(2048 - 64, targetX));
targetY = Math.max(64, Math.min(2186 - 64, targetY));
// Calculate direction to target
var deltaToTarget = {
@@ -760,20 +786,17 @@
};
var distanceToTarget = Math.sqrt(deltaToTarget.x * deltaToTarget.x + deltaToTarget.y * deltaToTarget.y);
// Calculate desired angle to face target
var desiredAngle = Math.atan2(deltaToTarget.x, -deltaToTarget.y);
- // Adjust power based on distance to target
- if (distanceToTarget > 80) {
- // Add speed bonus for aggressive personalities
- var speedBonus = 0;
- if (enemyCar.aiPersonality === 'aggressive' || enemyCar.aiPersonality === 'interceptor') {
- speedBonus = Math.min(0.3, playerSpeed / maxSpeed);
- }
- var basePower = Math.min(1, (distanceToTarget - 80) / 250);
- aiPower = Math.min(1, aiPower * (basePower + speedBonus));
- } else if (distanceToTarget < 40 && enemyCar.aiPersonality !== 'aggressive') {
- // Non-aggressive cars reduce power when very close
- aiPower *= 0.4;
+ // Add some random steering variation to make movement less robotic
+ var steeringNoise = (Math.random() - 0.5) * 0.1 * (1 - enemyCar.aiAggressiveness);
+ desiredAngle += steeringNoise;
+ // Adjust power based on distance and strategy
+ if (distanceToTarget > 100) {
+ var speedBonus = Math.min(0.3, playerSpeed / maxSpeed);
+ aiPower = Math.min(1, aiPower + speedBonus);
+ } else if (distanceToTarget < 50) {
+ aiPower *= 0.7; // Reduce power when close
}
// Update AI target rotation to face intercept target
enemyCar.aiTargetRotation = desiredAngle;
// Apply same rotation logic as player
@@ -790,39 +813,60 @@
var aiDynamicRotationSpeed = enemyCar.aiBaseRotationSpeed * Math.max(0.1, aiSpeedRatio);
enemyCar.rotation += rotationDelta * aiDynamicRotationSpeed;
// Calculate target velocity based on AI power
var aiTargetVelocity = enemyCar.aiMaxSpeed * aiPower;
- // Apply smooth velocity transitions (same as player logic)
+ // Apply smooth velocity transitions with random variations
+ var accelerationVariation = 0.8 + Math.random() * 0.4; // 0.8-1.2x variation
if (aiPower > 0.1) {
- // Accelerating
+ // Accelerating with random variation
var velocityDiff = aiTargetVelocity - enemyCar.aiCurrentVelocity;
- var accelerationRate = 0.004;
+ var accelerationRate = 0.004 * accelerationVariation;
enemyCar.aiCurrentVelocity += velocityDiff * accelerationRate;
} else {
- // Decelerating
- var decelerationRate = 0.048;
+ // Decelerating with random variation
+ var decelerationRate = 0.048 * accelerationVariation;
enemyCar.aiCurrentVelocity *= 1 - decelerationRate;
if (Math.abs(enemyCar.aiCurrentVelocity) < 0.1) {
enemyCar.aiCurrentVelocity = 0;
}
}
+ // Add random speed bursts occasionally
+ if (Math.random() < 0.005 && enemyCar.aiAggressiveness > 0.6) {
+ enemyCar.aiCurrentVelocity = Math.min(enemyCar.aiMaxSpeed * 1.2, enemyCar.aiCurrentVelocity * 1.3);
+ }
// Limit velocity to max speed
enemyCar.aiCurrentVelocity = Math.min(enemyCar.aiCurrentVelocity, enemyCar.aiMaxSpeed);
// Calculate intended movement direction based on rotation and velocity
var intendedMoveX = Math.sin(enemyCar.rotation) * enemyCar.aiCurrentVelocity;
var intendedMoveY = -Math.cos(enemyCar.rotation) * enemyCar.aiCurrentVelocity;
// Calculate turning friction (same as player logic)
var rotationFriction = Math.abs(rotationDelta * aiDynamicRotationSpeed) * 0.8;
var frictionMultiplier = Math.max(0.85, 1 - rotationFriction);
- // Apply drift physics (same as player logic)
- enemyCar.aiDriftVelocityX = enemyCar.aiDriftVelocityX * enemyCar.aiDriftFactor + intendedMoveX * enemyCar.aiGripFactor;
- enemyCar.aiDriftVelocityY = enemyCar.aiDriftVelocityY * enemyCar.aiDriftFactor + intendedMoveY * enemyCar.aiGripFactor;
- // Apply turning friction
- enemyCar.aiDriftVelocityX *= frictionMultiplier;
- enemyCar.aiDriftVelocityY *= frictionMultiplier;
- // Apply base deceleration
- enemyCar.aiDriftVelocityX *= 0.98;
- enemyCar.aiDriftVelocityY *= 0.98;
+ // Apply drift physics with random variations
+ var driftVariation = 0.9 + Math.random() * 0.2; // 0.9-1.1x variation
+ var adjustedDriftFactor = enemyCar.aiDriftFactor * driftVariation;
+ var adjustedGripFactor = enemyCar.aiGripFactor * driftVariation;
+ enemyCar.aiDriftVelocityX = enemyCar.aiDriftVelocityX * adjustedDriftFactor + intendedMoveX * adjustedGripFactor;
+ enemyCar.aiDriftVelocityY = enemyCar.aiDriftVelocityY * adjustedDriftFactor + intendedMoveY * adjustedGripFactor;
+ // Apply turning friction with variation
+ var frictionVariation = 0.8 + Math.random() * 0.4; // 0.8-1.2x variation
+ enemyCar.aiDriftVelocityX *= frictionMultiplier * frictionVariation;
+ enemyCar.aiDriftVelocityY *= frictionMultiplier * frictionVariation;
+ // Apply base deceleration with occasional hesitation
+ var baseDeceleration = 0.98;
+ if (Math.random() < 0.01) {
+ // Occasional hesitation - sudden braking
+ baseDeceleration = 0.85;
+ }
+ enemyCar.aiDriftVelocityX *= baseDeceleration;
+ enemyCar.aiDriftVelocityY *= baseDeceleration;
+ // Add random momentum changes
+ if (Math.random() < 0.003) {
+ var randomPushX = (Math.random() - 0.5) * 2;
+ var randomPushY = (Math.random() - 0.5) * 2;
+ enemyCar.aiDriftVelocityX += randomPushX;
+ enemyCar.aiDriftVelocityY += randomPushY;
+ }
// Update enemy car position using AI drift momentum
enemyCar.x += enemyCar.aiDriftVelocityX;
enemyCar.y += enemyCar.aiDriftVelocityY;
// Also apply the original momentum from collisions