User prompt
as que el fondo sea verde
User prompt
cambia el fondo
User prompt
CREA UN MENU PRINCIPAL
User prompt
VUELVE A LA PROMERA VERCION
User prompt
as que sea todo como en el inisio
User prompt
cada 10 segundos
User prompt
as que sea cada 20 segundos
User prompt
as que las unidades salgan cada 30segundos
User prompt
as que las unidades no se superpongan
User prompt
regresa a antes de las modificaciones
User prompt
arregla el bug de movimiento de las unidades
User prompt
arregla el bugg que las unidades no se mueven de las ciudades
User prompt
optimiza el codijo sin dañarlo
User prompt
as que las unidades rodeen cuando no puedan llegar a su objetivo
User prompt
as que las barra de vida de las ciudades sean mas anchas y grandes
User prompt
as que las unidades tengan barrita de vida
User prompt
as que las unidades se muevan por los cuadrados
User prompt
as que las unidades sean igual de grandes que la ciudades
User prompt
as que las unidades rodeen si no pueden continuar
User prompt
as que las unidades no se superpongan
User prompt
Please fix the bug: 'Timeout.tick error: tween.to is not a function' in or related to this line: 'tween.to(territorySquare, {' Line Number: 471 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
regresa el juego a una version anterior
User prompt
arregla problemas
User prompt
corrigue errores
User prompt
corrigue errores
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (startX, startY, targetX, targetY, damage, faction) {
var self = Container.call(this);
self.x = startX;
self.y = startY;
self.damage = damage;
self.faction = faction;
self.speed = 8;
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
self.maxDistance = distance;
self.traveled = 0;
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.traveled += self.speed;
// Check if bullet reached target area or max distance
if (self.traveled >= self.maxDistance) {
self.explode();
}
};
self.explode = function () {
// Find units in explosion radius
var explosionRadius = 30;
for (var i = 0; i < allUnits.length; i++) {
var unit = allUnits[i];
if (unit.faction !== self.faction && unit.health > 0) {
var dx = unit.x - self.x;
var dy = unit.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < explosionRadius) {
var destroyed = unit.takeDamage(self.damage);
if (destroyed) {
LK.getSound('explosion').play();
}
}
}
}
// Check cities
var enemyCities = self.faction === 'red' ? blueCities : redCities;
for (var i = 0; i < enemyCities.length; i++) {
var city = enemyCities[i];
if (city.health > 0) {
var dx = city.x - self.x;
var dy = city.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < explosionRadius) {
var destroyed = city.takeDamage(self.damage);
if (destroyed) {
LK.getSound('explosion').play();
}
}
}
}
// Remove bullet
for (var i = 0; i < bullets.length; i++) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var City = Container.expand(function (faction) {
var self = Container.call(this);
self.faction = faction;
self.health = 1000;
self.maxHealth = 1000;
self.lastProduction = 0;
self.productionCooldown = 180; // 3 seconds at 60fps
var cityGraphics = self.attachAsset('city', {
anchorX: 0.5,
anchorY: 0.5
});
// Color based on faction
if (faction === 'red') {
cityGraphics.tint = 0xff4444;
} else {
cityGraphics.tint = 0x4444ff;
}
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
y: -50,
scaleX: 1.5,
scaleY: 1.8
});
// Health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
y: -50,
scaleX: 1.5,
scaleY: 1.8
});
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
return true; // City destroyed
}
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBar.width = 80 * healthPercent * 1.5;
return false;
};
self.produceUnit = function () {
if (LK.ticks - self.lastProduction < self.productionCooldown) return;
// Choose random unit type
var unitTypes = ['infantry', 'tank', 'artillery'];
var unitType = unitTypes[Math.floor(Math.random() * unitTypes.length)];
var unit = new Unit(unitType, self.faction);
// Position near city
unit.x = self.x + (Math.random() - 0.5) * 100;
unit.y = self.y + (Math.random() - 0.5) * 100;
// Keep within bounds
unit.x = Math.max(50, Math.min(1998, unit.x));
unit.y = Math.max(50, Math.min(2682, unit.y));
allUnits.push(unit);
game.addChild(unit);
self.lastProduction = LK.ticks;
};
self.update = function () {
if (self.health <= 0) return;
self.produceUnit();
};
return self;
});
var Unit = Container.expand(function (type, faction) {
var self = Container.call(this);
self.type = type;
self.faction = faction;
self.target = null;
self.lastShot = 0;
self.health = 100;
self.maxHealth = 100;
self.moveSpeed = 2;
self.range = 100;
self.damage = 20;
self.shootCooldown = 60;
self.lastMoveTime = 0;
// Set unit stats based on type
if (type === 'infantry') {
self.moveSpeed = 2;
self.health = 60;
self.maxHealth = 60;
self.damage = 15;
self.range = 80;
self.shootCooldown = 40;
} else if (type === 'tank') {
self.moveSpeed = 2.5;
self.health = 120;
self.maxHealth = 120;
self.damage = 30;
self.range = 100;
self.shootCooldown = 60;
} else if (type === 'artillery') {
self.moveSpeed = 1;
self.health = 80;
self.maxHealth = 80;
self.damage = 50;
self.range = 180;
self.shootCooldown = 90;
}
var unitGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
// Color based on faction
if (faction === 'red') {
unitGraphics.tint = 0xff4444;
} else {
unitGraphics.tint = 0x4444ff;
}
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
y: -50
});
// Health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
y: -50
});
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
return true; // Unit destroyed
}
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBar.width = 80 * healthPercent;
return false;
};
self.findTarget = function () {
var closestDistance = Infinity;
var closestTarget = null;
// Find closest enemy unit
for (var i = 0; i < allUnits.length; i++) {
var unit = allUnits[i];
if (unit.faction !== self.faction && unit.health > 0) {
var dx = unit.x - self.x;
var dy = unit.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
closestTarget = unit;
}
}
}
// Check enemy cities
var enemyCities = self.faction === 'red' ? blueCities : redCities;
for (var i = 0; i < enemyCities.length; i++) {
var city = enemyCities[i];
if (city.health > 0) {
var dx = city.x - self.x;
var dy = city.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
closestTarget = city;
}
}
}
return closestTarget;
};
self.update = function () {
if (self.health <= 0) return;
self.target = self.findTarget();
if (self.target && self.target.health > 0) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.range) {
// Grid-based movement with improved pathfinding
var gridSize = 80;
var currentGridX = Math.floor(self.x / gridSize);
var currentGridY = Math.floor(self.y / gridSize);
var targetGridX = Math.floor(self.target.x / gridSize);
var targetGridY = Math.floor(self.target.y / gridSize);
// Find path using A* pathfinding
var path = self.findPath(currentGridX, currentGridY, targetGridX, targetGridY, gridSize);
if (path && path.length > 1) {
// Move to next step in path
var nextStep = path[1]; // Skip current position (path[0])
var nextX = nextStep.x * gridSize + gridSize / 2;
var nextY = nextStep.y * gridSize + gridSize / 2;
// Keep units within bounds
nextX = Math.max(40, Math.min(2008, nextX));
nextY = Math.max(40, Math.min(2692, nextY));
// Only move if enough time has passed
if (!self.lastMoveTime || LK.ticks - self.lastMoveTime > 20) {
self.x = nextX;
self.y = nextY;
self.lastMoveTime = LK.ticks;
}
}
} else {
// In range, shoot
if (LK.ticks - self.lastShot > self.shootCooldown) {
self.shoot();
self.lastShot = LK.ticks;
}
}
}
};
self.shoot = function () {
if (!self.target || self.target.health <= 0) return;
var bullet = new Bullet(self.x, self.y, self.target.x, self.target.y, self.damage, self.faction);
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.findPath = function (startX, startY, goalX, goalY, gridSize) {
var cols = Math.floor(2048 / gridSize);
var rows = Math.floor(2732 / gridSize);
// Check if coordinates are within bounds
if (goalX < 0 || goalX >= cols || goalY < 0 || goalY >= rows) {
return null;
}
// Create grid representation
var grid = [];
for (var y = 0; y < rows; y++) {
grid[y] = [];
for (var x = 0; x < cols; x++) {
grid[y][x] = {
x: x,
y: y,
walkable: true,
g: 0,
h: 0,
f: 0,
parent: null
};
}
}
// Mark occupied cells as non-walkable
for (var i = 0; i < allUnits.length; i++) {
var unit = allUnits[i];
if (unit !== self && unit.health > 0) {
var unitGridX = Math.floor(unit.x / gridSize);
var unitGridY = Math.floor(unit.y / gridSize);
if (unitGridX >= 0 && unitGridX < cols && unitGridY >= 0 && unitGridY < rows) {
grid[unitGridY][unitGridX].walkable = false;
}
}
}
// Mark cities as non-walkable (except goal)
var allCities = redCities.concat(blueCities);
for (var i = 0; i < allCities.length; i++) {
var city = allCities[i];
if (city.health > 0) {
var cityGridX = Math.floor(city.x / gridSize);
var cityGridY = Math.floor(city.y / gridSize);
if (cityGridX >= 0 && cityGridX < cols && cityGridY >= 0 && cityGridY < rows) {
// Don't block the goal position
if (cityGridX !== goalX || cityGridY !== goalY) {
grid[cityGridY][cityGridX].walkable = false;
}
}
}
}
// A* algorithm
var openList = [];
var closedList = [];
var startNode = grid[startY][startX];
var goalNode = grid[goalY][goalX];
openList.push(startNode);
while (openList.length > 0) {
// Find node with lowest f score
var currentNode = openList[0];
var currentIndex = 0;
for (var i = 1; i < openList.length; i++) {
if (openList[i].f < currentNode.f) {
currentNode = openList[i];
currentIndex = i;
}
}
// Move current node from open to closed list
openList.splice(currentIndex, 1);
closedList.push(currentNode);
// Check if we reached the goal
if (currentNode.x === goalX && currentNode.y === goalY) {
var path = [];
var node = currentNode;
while (node) {
path.push({
x: node.x,
y: node.y
});
node = node.parent;
}
return path.reverse();
}
// Check all neighbors
var neighbors = [{
x: currentNode.x - 1,
y: currentNode.y
},
// Left
{
x: currentNode.x + 1,
y: currentNode.y
},
// Right
{
x: currentNode.x,
y: currentNode.y - 1
},
// Up
{
x: currentNode.x,
y: currentNode.y + 1
},
// Down
{
x: currentNode.x - 1,
y: currentNode.y - 1
},
// Up-left
{
x: currentNode.x + 1,
y: currentNode.y - 1
},
// Up-right
{
x: currentNode.x - 1,
y: currentNode.y + 1
},
// Down-left
{
x: currentNode.x + 1,
y: currentNode.y + 1
} // Down-right
];
for (var i = 0; i < neighbors.length; i++) {
var neighbor = neighbors[i];
// Check if neighbor is within bounds
if (neighbor.x < 0 || neighbor.x >= cols || neighbor.y < 0 || neighbor.y >= rows) {
continue;
}
var neighborNode = grid[neighbor.y][neighbor.x];
// Check if neighbor is walkable and not in closed list
if (!neighborNode.walkable || closedList.indexOf(neighborNode) !== -1) {
continue;
}
// Calculate movement cost (diagonal movement costs more)
var isDiagonal = Math.abs(neighbor.x - currentNode.x) === 1 && Math.abs(neighbor.y - currentNode.y) === 1;
var movementCost = isDiagonal ? 14 : 10; // 14 ≈ sqrt(2) * 10
var tentativeG = currentNode.g + movementCost;
// Check if this path to neighbor is better
if (openList.indexOf(neighborNode) === -1) {
openList.push(neighborNode);
} else if (tentativeG >= neighborNode.g) {
continue;
}
// This path is the best so far
neighborNode.parent = currentNode;
neighborNode.g = tentativeG;
neighborNode.h = Math.abs(neighborNode.x - goalX) + Math.abs(neighborNode.y - goalY);
neighborNode.f = neighborNode.g + neighborNode.h;
}
}
// No path found, return null
return null;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d4a2d
});
/****
* Game Code
****/
// Game state
var allUnits = [];
var bullets = [];
var redCities = [];
var blueCities = [];
var gameStarted = false;
var territorialGrid = [];
// UI
var statusText = new Text2('Europa 1941: AI War', {
size: 40,
fill: 0xFFFFFF
});
statusText.anchor.set(0.5, 0);
LK.gui.top.addChild(statusText);
var redScoreText = new Text2('Red Cities: 0', {
size: 30,
fill: 0xFF4444
});
redScoreText.anchor.set(0, 0);
redScoreText.x = 20;
redScoreText.y = 120;
LK.gui.top.addChild(redScoreText);
var blueScoreText = new Text2('Blue Cities: 0', {
size: 30,
fill: 0x4444FF
});
blueScoreText.anchor.set(1, 0);
blueScoreText.x = -20;
blueScoreText.y = 50;
LK.gui.topRight.addChild(blueScoreText);
// Initialize cities
function initializeCities() {
var minDistance = 120; // Minimum distance between cities
// Red cities (left side)
for (var i = 0; i < 5; i++) {
var city = new City('red');
var attempts = 0;
var maxAttempts = 50;
var validPosition = false;
while (!validPosition && attempts < maxAttempts) {
city.x = 150 + Math.random() * 500; // Random x between 150-650
city.y = 200 + Math.random() * 2300; // Random y between 200-2500
validPosition = true;
// Check distance from other red cities
for (var j = 0; j < redCities.length; j++) {
var dx = city.x - redCities[j].x;
var dy = city.y - redCities[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
validPosition = false;
break;
}
}
attempts++;
}
redCities.push(city);
game.addChild(city);
}
// Blue cities (right side)
for (var i = 0; i < 5; i++) {
var city = new City('blue');
var attempts = 0;
var maxAttempts = 50;
var validPosition = false;
while (!validPosition && attempts < maxAttempts) {
city.x = 1400 + Math.random() * 500; // Random x between 1400-1900
city.y = 200 + Math.random() * 2300; // Random y between 200-2500
validPosition = true;
// Check distance from other blue cities
for (var j = 0; j < blueCities.length; j++) {
var dx = city.x - blueCities[j].x;
var dy = city.y - blueCities[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
validPosition = false;
break;
}
}
attempts++;
}
blueCities.push(city);
game.addChild(city);
}
}
function updateUI() {
var redAlive = 0;
var blueAlive = 0;
var previousRedAlive = 0;
var previousBlueAlive = 0;
// Count previous alive cities
for (var i = 0; i < redCities.length; i++) {
if (redCities[i].health > 0) {
redAlive++;
} else if (redCities[i].health === 0) {
// Check if this was just destroyed
previousRedAlive++;
}
}
for (var i = 0; i < blueCities.length; i++) {
if (blueCities[i].health > 0) {
blueAlive++;
} else if (blueCities[i].health === 0) {
// Check if this was just destroyed
previousBlueAlive++;
}
}
// Update territorial grid when cities are destroyed
var totalCitiesNow = redAlive + blueAlive;
var totalCitiesBefore = redCities.length + blueCities.length;
if (totalCitiesNow < totalCitiesBefore) {
createTerritorialGrid();
}
redScoreText.setText('Red Cities: ' + redAlive);
blueScoreText.setText('Blue Cities: ' + blueAlive);
// Check win conditions
if (redAlive === 0) {
statusText.setText('Blue AI Wins!');
LK.showYouWin();
} else if (blueAlive === 0) {
statusText.setText('Red AI Wins!');
LK.showYouWin();
}
}
function createTerritorialGrid() {
// Clear existing grid
for (var i = 0; i < territorialGrid.length; i++) {
territorialGrid[i].destroy();
}
territorialGrid = [];
var gridSize = 80; // Same size as cities
var rows = Math.floor(2732 / gridSize);
var cols = Math.floor(2048 / gridSize);
for (var row = 0; row < rows; row++) {
for (var col = 0; col < cols; col++) {
var x = col * gridSize + gridSize / 2;
var y = row * gridSize + gridSize / 2;
// Determine territory based on closest surviving city
var closestRedDistance = Infinity;
var closestBlueDistance = Infinity;
// Check red cities
for (var i = 0; i < redCities.length; i++) {
if (redCities[i].health > 0) {
var dx = x - redCities[i].x;
var dy = y - redCities[i].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestRedDistance) {
closestRedDistance = distance;
}
}
}
// Check blue cities
for (var i = 0; i < blueCities.length; i++) {
if (blueCities[i].health > 0) {
var dx = x - blueCities[i].x;
var dy = y - blueCities[i].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestBlueDistance) {
closestBlueDistance = distance;
}
}
}
// Create territory square
var territorySquare = LK.getAsset('territory', {
anchorX: 0.5,
anchorY: 0.5
});
territorySquare.x = x;
territorySquare.y = y;
territorySquare.alpha = 0.6;
// Color based on closest territory
if (closestRedDistance < closestBlueDistance) {
territorySquare.tint = 0xff4444;
} else {
territorySquare.tint = 0x4444ff;
}
territorialGrid.push(territorySquare);
game.addChild(territorySquare);
// Add pulsing effect to make colors more visible
tween(territorySquare, {
alpha: 0.3
}, {
duration: 2000
});
}
}
}
function cleanupDeadUnits() {
for (var i = allUnits.length - 1; i >= 0; i--) {
if (allUnits[i].health <= 0) {
allUnits[i].destroy();
allUnits.splice(i, 1);
}
}
}
// Start game
LK.setTimeout(function () {
initializeCities();
createTerritorialGrid();
gameStarted = true;
statusText.setText('Battle in Progress...');
}, 1000);
// Game update loop
game.update = function () {
if (!gameStarted) return;
// Update all units
for (var i = 0; i < allUnits.length; i++) {
if (allUnits[i].health > 0) {
allUnits[i].update();
}
}
// Update all cities
for (var i = 0; i < redCities.length; i++) {
redCities[i].update();
}
for (var i = 0; i < blueCities.length; i++) {
blueCities[i].update();
}
// Update all bullets
for (var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
// Cleanup dead units
cleanupDeadUnits();
// Update UI
updateUI();
}; ===================================================================
--- original.js
+++ change.js
@@ -251,125 +251,31 @@
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.range) {
- // Grid-based movement
+ // Grid-based movement with improved pathfinding
var gridSize = 80;
var currentGridX = Math.floor(self.x / gridSize);
var currentGridY = Math.floor(self.y / gridSize);
var targetGridX = Math.floor(self.target.x / gridSize);
var targetGridY = Math.floor(self.target.y / gridSize);
- // Calculate next grid position
- var nextGridX = currentGridX;
- var nextGridY = currentGridY;
- if (targetGridX > currentGridX) {
- nextGridX = currentGridX + 1;
- } else if (targetGridX < currentGridX) {
- nextGridX = currentGridX - 1;
- }
- if (targetGridY > currentGridY) {
- nextGridY = currentGridY + 1;
- } else if (targetGridY < currentGridY) {
- nextGridY = currentGridY - 1;
- }
- // Convert back to world coordinates
- var nextX = nextGridX * gridSize + gridSize / 2;
- var nextY = nextGridY * gridSize + gridSize / 2;
- // Keep units within bounds
- nextX = Math.max(40, Math.min(2008, nextX));
- nextY = Math.max(40, Math.min(2692, nextY));
- // Check for collision with other units at target grid position
- var canMove = true;
- for (var i = 0; i < allUnits.length; i++) {
- var otherUnit = allUnits[i];
- if (otherUnit !== self && otherUnit.health > 0) {
- var otherGridX = Math.floor(otherUnit.x / gridSize);
- var otherGridY = Math.floor(otherUnit.y / gridSize);
- var targetOtherGridX = Math.floor(nextX / gridSize);
- var targetOtherGridY = Math.floor(nextY / gridSize);
- if (otherGridX === targetOtherGridX && otherGridY === targetOtherGridY) {
- canMove = false;
- break;
- }
+ // Find path using A* pathfinding
+ var path = self.findPath(currentGridX, currentGridY, targetGridX, targetGridY, gridSize);
+ if (path && path.length > 1) {
+ // Move to next step in path
+ var nextStep = path[1]; // Skip current position (path[0])
+ var nextX = nextStep.x * gridSize + gridSize / 2;
+ var nextY = nextStep.y * gridSize + gridSize / 2;
+ // Keep units within bounds
+ nextX = Math.max(40, Math.min(2008, nextX));
+ nextY = Math.max(40, Math.min(2692, nextY));
+ // Only move if enough time has passed
+ if (!self.lastMoveTime || LK.ticks - self.lastMoveTime > 20) {
+ self.x = nextX;
+ self.y = nextY;
+ self.lastMoveTime = LK.ticks;
}
}
- // If direct path is blocked, try alternative grid positions
- if (!canMove) {
- var alternativePositions = [{
- x: currentGridX + 1,
- y: currentGridY
- },
- // Right
- {
- x: currentGridX - 1,
- y: currentGridY
- },
- // Left
- {
- x: currentGridX,
- y: currentGridY + 1
- },
- // Down
- {
- x: currentGridX,
- y: currentGridY - 1
- },
- // Up
- {
- x: currentGridX + 1,
- y: currentGridY + 1
- },
- // Down-right
- {
- x: currentGridX - 1,
- y: currentGridY + 1
- },
- // Down-left
- {
- x: currentGridX + 1,
- y: currentGridY - 1
- },
- // Up-right
- {
- x: currentGridX - 1,
- y: currentGridY - 1
- } // Up-left
- ];
- for (var j = 0; j < alternativePositions.length; j++) {
- var altGridX = alternativePositions[j].x;
- var altGridY = alternativePositions[j].y;
- var altX = altGridX * gridSize + gridSize / 2;
- var altY = altGridY * gridSize + gridSize / 2;
- // Keep units within bounds
- altX = Math.max(40, Math.min(2008, altX));
- altY = Math.max(40, Math.min(2692, altY));
- // Check if this alternative position is clear
- var altCanMove = true;
- for (var k = 0; k < allUnits.length; k++) {
- var otherUnit = allUnits[k];
- if (otherUnit !== self && otherUnit.health > 0) {
- var otherGridX = Math.floor(otherUnit.x / gridSize);
- var otherGridY = Math.floor(otherUnit.y / gridSize);
- if (otherGridX === altGridX && otherGridY === altGridY) {
- altCanMove = false;
- break;
- }
- }
- }
- if (altCanMove) {
- nextX = altX;
- nextY = altY;
- canMove = true;
- break;
- }
- }
- }
- // Only move if we found a clear path and enough time has passed
- if (canMove && (!self.lastMoveTime || LK.ticks - self.lastMoveTime > 20)) {
- self.x = nextX;
- self.y = nextY;
- self.lastMoveTime = LK.ticks;
- }
} else {
// In range, shoot
if (LK.ticks - self.lastShot > self.shootCooldown) {
self.shoot();
@@ -384,8 +290,161 @@
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
+ self.findPath = function (startX, startY, goalX, goalY, gridSize) {
+ var cols = Math.floor(2048 / gridSize);
+ var rows = Math.floor(2732 / gridSize);
+ // Check if coordinates are within bounds
+ if (goalX < 0 || goalX >= cols || goalY < 0 || goalY >= rows) {
+ return null;
+ }
+ // Create grid representation
+ var grid = [];
+ for (var y = 0; y < rows; y++) {
+ grid[y] = [];
+ for (var x = 0; x < cols; x++) {
+ grid[y][x] = {
+ x: x,
+ y: y,
+ walkable: true,
+ g: 0,
+ h: 0,
+ f: 0,
+ parent: null
+ };
+ }
+ }
+ // Mark occupied cells as non-walkable
+ for (var i = 0; i < allUnits.length; i++) {
+ var unit = allUnits[i];
+ if (unit !== self && unit.health > 0) {
+ var unitGridX = Math.floor(unit.x / gridSize);
+ var unitGridY = Math.floor(unit.y / gridSize);
+ if (unitGridX >= 0 && unitGridX < cols && unitGridY >= 0 && unitGridY < rows) {
+ grid[unitGridY][unitGridX].walkable = false;
+ }
+ }
+ }
+ // Mark cities as non-walkable (except goal)
+ var allCities = redCities.concat(blueCities);
+ for (var i = 0; i < allCities.length; i++) {
+ var city = allCities[i];
+ if (city.health > 0) {
+ var cityGridX = Math.floor(city.x / gridSize);
+ var cityGridY = Math.floor(city.y / gridSize);
+ if (cityGridX >= 0 && cityGridX < cols && cityGridY >= 0 && cityGridY < rows) {
+ // Don't block the goal position
+ if (cityGridX !== goalX || cityGridY !== goalY) {
+ grid[cityGridY][cityGridX].walkable = false;
+ }
+ }
+ }
+ }
+ // A* algorithm
+ var openList = [];
+ var closedList = [];
+ var startNode = grid[startY][startX];
+ var goalNode = grid[goalY][goalX];
+ openList.push(startNode);
+ while (openList.length > 0) {
+ // Find node with lowest f score
+ var currentNode = openList[0];
+ var currentIndex = 0;
+ for (var i = 1; i < openList.length; i++) {
+ if (openList[i].f < currentNode.f) {
+ currentNode = openList[i];
+ currentIndex = i;
+ }
+ }
+ // Move current node from open to closed list
+ openList.splice(currentIndex, 1);
+ closedList.push(currentNode);
+ // Check if we reached the goal
+ if (currentNode.x === goalX && currentNode.y === goalY) {
+ var path = [];
+ var node = currentNode;
+ while (node) {
+ path.push({
+ x: node.x,
+ y: node.y
+ });
+ node = node.parent;
+ }
+ return path.reverse();
+ }
+ // Check all neighbors
+ var neighbors = [{
+ x: currentNode.x - 1,
+ y: currentNode.y
+ },
+ // Left
+ {
+ x: currentNode.x + 1,
+ y: currentNode.y
+ },
+ // Right
+ {
+ x: currentNode.x,
+ y: currentNode.y - 1
+ },
+ // Up
+ {
+ x: currentNode.x,
+ y: currentNode.y + 1
+ },
+ // Down
+ {
+ x: currentNode.x - 1,
+ y: currentNode.y - 1
+ },
+ // Up-left
+ {
+ x: currentNode.x + 1,
+ y: currentNode.y - 1
+ },
+ // Up-right
+ {
+ x: currentNode.x - 1,
+ y: currentNode.y + 1
+ },
+ // Down-left
+ {
+ x: currentNode.x + 1,
+ y: currentNode.y + 1
+ } // Down-right
+ ];
+ for (var i = 0; i < neighbors.length; i++) {
+ var neighbor = neighbors[i];
+ // Check if neighbor is within bounds
+ if (neighbor.x < 0 || neighbor.x >= cols || neighbor.y < 0 || neighbor.y >= rows) {
+ continue;
+ }
+ var neighborNode = grid[neighbor.y][neighbor.x];
+ // Check if neighbor is walkable and not in closed list
+ if (!neighborNode.walkable || closedList.indexOf(neighborNode) !== -1) {
+ continue;
+ }
+ // Calculate movement cost (diagonal movement costs more)
+ var isDiagonal = Math.abs(neighbor.x - currentNode.x) === 1 && Math.abs(neighbor.y - currentNode.y) === 1;
+ var movementCost = isDiagonal ? 14 : 10; // 14 ≈ sqrt(2) * 10
+ var tentativeG = currentNode.g + movementCost;
+ // Check if this path to neighbor is better
+ if (openList.indexOf(neighborNode) === -1) {
+ openList.push(neighborNode);
+ } else if (tentativeG >= neighborNode.g) {
+ continue;
+ }
+ // This path is the best so far
+ neighborNode.parent = currentNode;
+ neighborNode.g = tentativeG;
+ neighborNode.h = Math.abs(neighborNode.x - goalX) + Math.abs(neighborNode.y - goalY);
+ neighborNode.f = neighborNode.g + neighborNode.h;
+ }
+ }
+ // No path found, return null
+ return null;
+ };
return self;
});
/****
con estilo de hoi4
prollectil de un flak 8,8cm. In-Game asset. 2d. High contrast. No shadows
boton de inicio al estilo hoi4. In-Game asset. 2d. High contrast. No shadows
QUE LAS LETRAS TENGAN UN COLOR MAS LLAMATIVO
SOLDADOS SOVIETICOS. In-Game asset. 2d. High contrast. No shadows
tanque t34-85. In-Game asset. 2d. High contrast. No shadows
artilleria sobietica yag 10 g. In-Game asset. 2d. High contrast. No shadows
mas cuadros