User prompt
puede hacer el juego que tenga pantalla completa
User prompt
si la unidad del jugador no puede atacar a nadie, permiti que pueda usar a la siguiente unidad disponible
User prompt
ahora quiero que el boton de omir turno este disponible desde que inica la partida
User prompt
la unidad arquera no se esta moviendo
User prompt
cuando inicia el juego quiero que suene una musica, uno cuando se este leyendo el comic, y la otra cuando ya esten luchando
User prompt
me gustaria que antes que inicie la partida, salga un comic que explique la historia
User prompt
la caja no debe salir como objeto que se pueda atacar
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 863
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading '0')' in or related to this line: 'var cell = grid[x][y];' Line Number: 854
User prompt
otra vez
User prompt
cuando la unidad ataque, ya no tiene mas acciones y le toca al otra unidad
User prompt
las unidades pueden atacar y moverse
User prompt
cuando el jugador seleciona a la unidad enemiga al cual atacar, se debe tomar como confirmacion el segundo click del mouse.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Archer = Container.expand(function () {
var self = Container.call(this);
var archerGraphics = self.attachAsset('archer', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.hp = 80;
self.maxHp = 80;
self.damage = 25;
self.moveRange = 2;
self.attackRange = 4;
self.hasActed = false;
self.hasMoved = false;
self.takeDamage = function (amount) {
self.hp = Math.max(0, self.hp - amount);
LK.effects.flashObject(self, 0xFF0000, 500);
// Create damage particles
for (var i = 0; i < 8; i++) {
var particle = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0xFF0000
});
particle.x = self.x + (Math.random() - 0.5) * 40;
particle.y = self.y + (Math.random() - 0.5) * 40;
game.addChild(particle);
var targetX = particle.x + (Math.random() - 0.5) * 200;
var targetY = particle.y - Math.random() * 100 - 50;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.05,
scaleY: 0.05
}, {
duration: 800 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
if (self.hp <= 0) {
self.destroy();
archer = null;
checkGameOver();
}
};
self.attack = function (target) {
if (target && target.takeDamage) {
// Create arrow projectile
var arrow = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.1,
tint: 0x8B4513
});
arrow.x = self.x;
arrow.y = self.y;
game.addChild(arrow);
// Calculate angle to target
var dx = target.x - self.x;
var dy = target.y - self.y;
arrow.rotation = Math.atan2(dy, dx);
// Animate arrow to target
tween(arrow, {
x: target.x,
y: target.y
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
arrow.destroy();
target.takeDamage(self.damage);
}
});
LK.getSound('attack').play();
self.hasActed = true;
self.hasMoved = true;
}
};
self.down = function (x, y, obj) {
if (gameState === 'player_turn' && turnPhase === 'select') {
handleCellClick(self.gridX, self.gridY);
}
showHealthInterface(self);
};
return self;
});
var Barrel = Container.expand(function () {
var self = Container.call(this);
var barrelGraphics = self.attachAsset('barrel', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.hp = 30;
self.maxHp = 30;
self.exploded = false;
self.takeDamage = function (amount) {
if (self.exploded) return;
self.hp = Math.max(0, self.hp - amount);
LK.effects.flashObject(self, 0xFF0000, 500);
if (self.hp <= 0) {
self.explode();
}
};
self.explode = function () {
if (self.exploded) return;
self.exploded = true;
// Play explosion sound
LK.getSound('explosion').play();
// Create explosion particles
for (var i = 0; i < 15; i++) {
var particle = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.15,
tint: 0xFF4500
});
particle.x = self.x + (Math.random() - 0.5) * 60;
particle.y = self.y + (Math.random() - 0.5) * 60;
game.addChild(particle);
var targetX = particle.x + (Math.random() - 0.5) * 300;
var targetY = particle.y - Math.random() * 150 - 100;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.05,
scaleY: 0.05
}, {
duration: 1000 + Math.random() * 500,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
// Deal damage to all units within 2 cells radius
var damageAmount = 40;
var explosionRadius = 2;
// Check all grid positions within radius
for (var x = Math.max(0, self.gridX - explosionRadius); x <= Math.min(gridWidth - 1, self.gridX + explosionRadius); x++) {
for (var y = Math.max(0, self.gridY - explosionRadius); y <= Math.min(gridHeight - 1, self.gridY + explosionRadius); y++) {
var distance = getDistance(self.gridX, self.gridY, x, y);
if (distance <= explosionRadius && grid[x][y].occupied) {
var unit = grid[x][y].unit;
if (unit && unit.takeDamage && unit !== self) {
unit.takeDamage(damageAmount);
}
}
}
}
// Remove from grid and destroy
removeFromGrid(self.gridX, self.gridY);
for (var i = barrels.length - 1; i >= 0; i--) {
if (barrels[i] === self) {
barrels.splice(i, 1);
break;
}
}
self.destroy();
};
self.down = function (x, y, obj) {
showHealthInterface(self);
};
return self;
});
var EndTurnButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('endTurnButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2('End Turn', {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.visible = false;
self.down = function (x, y, obj) {
if (self.visible && gameState === 'player_turn') {
endPlayerTurn();
}
};
return self;
});
var GridCell = Container.expand(function () {
var self = Container.call(this);
var cellGraphics = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
cellGraphics.alpha = 0.3;
// Add black border effect by creating a larger black background
var borderGraphics = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.05,
scaleY: 1.05,
tint: 0x000000
});
// Move border behind main cell
self.setChildIndex(borderGraphics, 0);
self.gridX = 0;
self.gridY = 0;
self.occupied = false;
self.unit = null;
self.highlighted = false;
self.selectedBorder = null;
self.showSelectedBorder = function () {
if (!self.selectedBorder) {
self.selectedBorder = self.attachAsset('selectedBorder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.15,
scaleY: 1.15
});
self.setChildIndex(self.selectedBorder, 0);
}
self.selectedBorder.visible = true;
};
self.hideSelectedBorder = function () {
if (self.selectedBorder) {
self.selectedBorder.visible = false;
}
};
self.highlight = function () {
if (!self.highlighted) {
cellGraphics.tint = 0xFFFF00;
// Add thick black border for highlighted cells
if (!self.highlightBorder) {
self.highlightBorder = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1,
tint: 0x000000
});
self.setChildIndex(self.highlightBorder, 0);
}
self.highlightBorder.visible = true;
self.highlighted = true;
}
};
self.unhighlight = function () {
if (self.highlighted) {
cellGraphics.tint = 0xFFFFFF;
if (self.highlightBorder) {
self.highlightBorder.visible = false;
}
self.highlighted = false;
}
};
self.down = function (x, y, obj) {
if (gameState === 'player_turn' && self.highlighted) {
handleCellClick(self.gridX, self.gridY);
}
};
return self;
});
var Knight = Container.expand(function () {
var self = Container.call(this);
var knightGraphics = self.attachAsset('knight', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.hp = 100;
self.maxHp = 100;
self.damage = 30;
self.moveRange = 3;
self.attackRange = 1;
self.hasActed = false;
self.hasMoved = false;
self.takeDamage = function (amount) {
self.hp = Math.max(0, self.hp - amount);
LK.effects.flashObject(self, 0xFF0000, 500);
// Create damage particles
for (var i = 0; i < 8; i++) {
var particle = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0xFF0000
});
particle.x = self.x + (Math.random() - 0.5) * 40;
particle.y = self.y + (Math.random() - 0.5) * 40;
game.addChild(particle);
var targetX = particle.x + (Math.random() - 0.5) * 200;
var targetY = particle.y - Math.random() * 100 - 50;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.05,
scaleY: 0.05
}, {
duration: 800 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
if (self.hp <= 0) {
self.destroy();
knight = null;
checkGameOver();
}
};
self.attack = function (target) {
if (target && target.takeDamage) {
target.takeDamage(self.damage);
LK.getSound('attack').play();
self.hasActed = true;
self.hasMoved = true;
}
};
self.down = function (x, y, obj) {
if (gameState === 'player_turn' && turnPhase === 'select') {
handleCellClick(self.gridX, self.gridY);
}
showHealthInterface(self);
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
self.gridX = 0;
self.gridY = 0;
self.obstacleType = 'stone'; // 'stone', 'box', 'column'
self.isDestructible = false;
self.hp = 0;
self.maxHp = 0;
self.graphics = null;
self.init = function (type) {
self.obstacleType = type;
if (type === 'stone') {
self.graphics = self.attachAsset('stone', {
anchorX: 0.5,
anchorY: 0.5
});
self.isDestructible = false;
} else if (type === 'box') {
self.graphics = self.attachAsset('box', {
anchorX: 0.5,
anchorY: 0.5
});
self.isDestructible = true;
self.hp = 50;
self.maxHp = 50;
} else if (type === 'column') {
self.graphics = self.attachAsset('column', {
anchorX: 0.5,
anchorY: 0.5
});
self.isDestructible = false;
}
};
self.takeDamage = function (amount) {
if (!self.isDestructible) return;
self.hp = Math.max(0, self.hp - amount);
LK.effects.flashObject(self, 0xFF0000, 500);
// Create damage particles
for (var i = 0; i < 6; i++) {
var particle = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.08,
scaleY: 0.08,
tint: 0x8B4513
});
particle.x = self.x + (Math.random() - 0.5) * 30;
particle.y = self.y + (Math.random() - 0.5) * 30;
game.addChild(particle);
var targetX = particle.x + (Math.random() - 0.5) * 150;
var targetY = particle.y - Math.random() * 80 - 40;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.03,
scaleY: 0.03
}, {
duration: 600 + Math.random() * 300,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
if (self.hp <= 0) {
removeFromGrid(self.gridX, self.gridY);
for (var i = obstacles.length - 1; i >= 0; i--) {
if (obstacles[i] === self) {
obstacles.splice(i, 1);
break;
}
}
self.destroy();
}
};
self.down = function (x, y, obj) {
if (self.isDestructible) {
showHealthInterface(self);
}
};
return self;
});
var SkeletonArcher = Container.expand(function () {
var self = Container.call(this);
var archerGraphics = self.attachAsset('skeletonArcher', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.hp = 40;
self.maxHp = 40;
self.damage = 18;
self.moveRange = 3;
self.attackRange = 3;
self.attack = function (target) {
if (target && target.takeDamage) {
// Create arrow projectile
var arrow = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.1,
tint: 0x654321
});
arrow.x = self.x;
arrow.y = self.y;
game.addChild(arrow);
// Calculate angle to target
var dx = target.x - self.x;
var dy = target.y - self.y;
arrow.rotation = Math.atan2(dy, dx);
// Animate arrow to target
tween(arrow, {
x: target.x,
y: target.y
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
arrow.destroy();
target.takeDamage(self.damage);
}
});
LK.getSound('attack').play();
}
};
self.takeDamage = function (amount) {
self.hp = Math.max(0, self.hp - amount);
LK.effects.flashObject(self, 0xFF0000, 500);
// Create grey damage particles
for (var i = 0; i < 6; i++) {
var particle = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.08,
scaleY: 0.08,
tint: 0x808080
});
particle.x = self.x + (Math.random() - 0.5) * 30;
particle.y = self.y + (Math.random() - 0.5) * 30;
game.addChild(particle);
var targetX = particle.x + (Math.random() - 0.5) * 150;
var targetY = particle.y - Math.random() * 80 - 40;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.03,
scaleY: 0.03
}, {
duration: 600 + Math.random() * 300,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
if (self.hp <= 0) {
removeFromGrid(self.gridX, self.gridY);
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
checkVictory();
}
};
self.down = function (x, y, obj) {
showHealthInterface(self);
};
return self;
});
var SkeletonKnight = Container.expand(function () {
var self = Container.call(this);
var knightGraphics = self.attachAsset('skeletonKnight', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.hp = 100;
self.maxHp = 100;
self.damage = 35;
self.moveRange = 1;
self.attackRange = 1;
self.attack = function (target) {
if (target && target.takeDamage) {
target.takeDamage(self.damage);
LK.getSound('attack').play();
}
};
self.takeDamage = function (amount) {
self.hp = Math.max(0, self.hp - amount);
LK.effects.flashObject(self, 0xFF0000, 500);
// Create grey damage particles
for (var i = 0; i < 6; i++) {
var particle = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.08,
scaleY: 0.08,
tint: 0x808080
});
particle.x = self.x + (Math.random() - 0.5) * 30;
particle.y = self.y + (Math.random() - 0.5) * 30;
game.addChild(particle);
var targetX = particle.x + (Math.random() - 0.5) * 150;
var targetY = particle.y - Math.random() * 80 - 40;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.03,
scaleY: 0.03
}, {
duration: 600 + Math.random() * 300,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
if (self.hp <= 0) {
removeFromGrid(self.gridX, self.gridY);
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
checkVictory();
}
};
self.down = function (x, y, obj) {
showHealthInterface(self);
};
return self;
});
var SkeletonSoldier = Container.expand(function () {
var self = Container.call(this);
var soldierGraphics = self.attachAsset('skeletonSoldier', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.hp = 60;
self.maxHp = 60;
self.damage = 20;
self.moveRange = 2;
self.attackRange = 1;
self.attack = function (target) {
if (target && target.takeDamage) {
target.takeDamage(self.damage);
LK.getSound('attack').play();
}
};
self.takeDamage = function (amount) {
self.hp = Math.max(0, self.hp - amount);
LK.effects.flashObject(self, 0xFF0000, 500);
// Create grey damage particles
for (var i = 0; i < 6; i++) {
var particle = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.08,
scaleY: 0.08,
tint: 0x808080
});
particle.x = self.x + (Math.random() - 0.5) * 30;
particle.y = self.y + (Math.random() - 0.5) * 30;
game.addChild(particle);
var targetX = particle.x + (Math.random() - 0.5) * 150;
var targetY = particle.y - Math.random() * 80 - 40;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.03,
scaleY: 0.03
}, {
duration: 600 + Math.random() * 300,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
if (self.hp <= 0) {
removeFromGrid(self.gridX, self.gridY);
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
checkVictory();
}
};
self.down = function (x, y, obj) {
showHealthInterface(self);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var gridWidth = 8;
var gridHeight = 8;
var cellSize = 200;
var grid = [];
var units = [];
var enemies = [];
var barrels = [];
var obstacles = [];
var knight = null;
var archer = null;
var exitPortal = null;
var selectedUnit = null;
var selectedTarget = null;
var gameState = 'player_turn'; // 'player_turn', 'enemy_turn', 'game_over'
var turnPhase = 'select'; // 'select', 'move', 'attack'
var wave = 1;
// UI Elements
var turnText = new Text2('Player Turn', {
size: 60,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0);
LK.gui.top.addChild(turnText);
var waveText = new Text2('Wave 1', {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
LK.gui.topRight.addChild(waveText);
var instructionText = new Text2('Select a unit to move', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionText);
// Create End Turn button
var endTurnButton = new EndTurnButton();
endTurnButton.x = 2048 - 200;
endTurnButton.y = 2732 - 100;
game.addChild(endTurnButton);
// Initialize grid
function initializeGrid() {
var startX = (2048 - gridWidth * cellSize) / 2;
var startY = (2732 - gridHeight * cellSize) / 2;
for (var x = 0; x < gridWidth; x++) {
grid[x] = [];
for (var y = 0; y < gridHeight; y++) {
var cell = new GridCell();
cell.gridX = x;
cell.gridY = y;
cell.x = startX + x * cellSize + cellSize / 2;
cell.y = startY + y * cellSize + cellSize / 2;
grid[x][y] = cell;
game.addChild(cell);
}
}
}
function placeUnit(unit, gridX, gridY) {
if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
if (grid[gridX][gridY].occupied) {
return false;
}
// Remove from old position
if (unit.gridX !== undefined && unit.gridY !== undefined) {
grid[unit.gridX][unit.gridY].occupied = false;
grid[unit.gridX][unit.gridY].unit = null;
}
unit.gridX = gridX;
unit.gridY = gridY;
unit.x = grid[gridX][gridY].x;
unit.y = grid[gridX][gridY].y;
grid[gridX][gridY].occupied = true;
grid[gridX][gridY].unit = unit;
return true;
}
return false;
}
function removeFromGrid(gridX, gridY) {
if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
grid[gridX][gridY].occupied = false;
grid[gridX][gridY].unit = null;
}
}
function getDistance(x1, y1, x2, y2) {
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
}
function highlightValidMoves(unit) {
clearHighlights();
// Only show movement highlights if unit hasn't moved yet
if (!unit.hasMoved) {
for (var x = 0; x < gridWidth; x++) {
for (var y = 0; y < gridHeight; y++) {
var distance = getDistance(unit.gridX, unit.gridY, x, y);
if (distance <= unit.moveRange && !grid[x][y].occupied) {
grid[x][y].highlight();
}
}
}
}
}
function highlightValidAttacks(unit) {
var hasValidAttacks = false;
// Only show attack highlights if unit hasn't attacked yet
if (!unit.hasActed) {
for (var x = 0; x < gridWidth; x++) {
for (var y = 0; y < gridHeight; y++) {
var distance = getDistance(unit.gridX, unit.gridY, x, y);
if (distance <= unit.attackRange && grid[x][y].occupied && grid[x][y].unit !== unit) {
// Check if it's an enemy, barrel, or destructible obstacle
var isEnemy = false;
var isBarrel = false;
var isDestructibleObstacle = false;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === grid[x][y].unit) {
isEnemy = true;
break;
}
}
for (var i = 0; i < barrels.length; i++) {
if (barrels[i] === grid[x][y].unit) {
isBarrel = true;
break;
}
}
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i] === grid[x][y].unit && obstacles[i].isDestructible) {
isDestructibleObstacle = true;
break;
}
}
if (isEnemy || isBarrel || isDestructibleObstacle) {
hasValidAttacks = true;
var cell = grid[x][y];
cell.highlighted = true;
if (!cell.attackHighlight) {
cell.attackHighlight = cell.attachAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
cell.setChildIndex(cell.attackHighlight, cell.children.length - 1);
}
cell.attackHighlight.visible = true;
}
}
}
}
}
}
function clearHighlights() {
for (var x = 0; x < gridWidth; x++) {
for (var y = 0; y < gridHeight; y++) {
var cell = grid[x][y];
cell.unhighlight();
cell.hideSelectedBorder();
if (cell.attackHighlight) {
cell.attackHighlight.visible = false;
}
}
}
}
function handleCellClick(gridX, gridY) {
if (gameState !== 'player_turn') return;
var cell = grid[gridX][gridY];
if (turnPhase === 'select') {
// Select a unit
if (cell.occupied && (cell.unit === knight || cell.unit === archer)) {
if (!cell.unit.hasActed || !cell.unit.hasMoved) {
selectedUnit = cell.unit;
// Show golden border for selected unit
grid[selectedUnit.gridX][selectedUnit.gridY].showSelectedBorder();
turnPhase = 'action';
highlightValidMoves(selectedUnit);
highlightValidAttacks(selectedUnit);
instructionText.setText('Click to move and/or attack, then click unit again when done');
}
}
} else if (turnPhase === 'action') {
if (cell.highlighted && !cell.occupied && !selectedUnit.hasMoved) {
// Move to this cell
placeUnit(selectedUnit, gridX, gridY);
selectedUnit.hasMoved = true;
// Play random movement sound
var moveSounds = ['move', 'move1', 'move2', 'move3'];
var randomSound = moveSounds[Math.floor(Math.random() * moveSounds.length)];
LK.getSound(randomSound).play();
// Check if reached exit portal
if (exitPortal && gridX === exitPortal.gridX && gridY === exitPortal.gridY) {
LK.getSound('victory').play();
LK.playMusic('Victoria');
LK.showYouWin();
return;
}
// After moving, update highlights to show new attack options
clearHighlights();
highlightValidMoves(selectedUnit);
highlightValidAttacks(selectedUnit);
instructionText.setText('Click to attack or click unit again when done');
} else if (cell.highlighted && cell.occupied && !selectedUnit.hasActed) {
// Check if it's an enemy, barrel, or destructible obstacle
var isEnemy = false;
var isBarrel = false;
var isDestructibleObstacle = false;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === cell.unit) {
isEnemy = true;
break;
}
}
for (var i = 0; i < barrels.length; i++) {
if (barrels[i] === cell.unit) {
isBarrel = true;
break;
}
}
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i] === cell.unit && obstacles[i].isDestructible) {
isDestructibleObstacle = true;
break;
}
}
if (isEnemy || isBarrel || isDestructibleObstacle) {
// First click selects target for attack
selectedTarget = cell.unit;
turnPhase = 'confirm_attack';
clearHighlights();
// Highlight only the selected target
cell.highlight();
instructionText.setText('Click target again to confirm attack');
}
} else if (cell.occupied && cell.unit === selectedUnit) {
// Clicking on the selected unit again ends their turn
endPlayerAction();
}
} else if (turnPhase === 'confirm_attack') {
if (cell.occupied && cell.unit === selectedTarget && cell.highlighted) {
// Second click confirms attack
selectedUnit.attack(selectedTarget);
selectedUnit.hasActed = true;
selectedTarget = null;
// After attacking, unit's turn is over
clearHighlights();
endPlayerAction();
} else {
// Click elsewhere cancels attack confirmation
selectedTarget = null;
turnPhase = 'action';
highlightValidMoves(selectedUnit);
highlightValidAttacks(selectedUnit);
instructionText.setText('Click to move and/or attack, then click unit again when done');
}
}
}
function endPlayerAction() {
clearHighlights();
if (selectedUnit) {
// Mark unit as fully done (both moved and acted)
selectedUnit.hasActed = true;
selectedUnit.hasMoved = true;
grid[selectedUnit.gridX][selectedUnit.gridY].hideSelectedBorder();
}
selectedUnit = null;
turnPhase = 'select';
// Check if both units have completed their full turns
var knightDone = !knight || knight.hasActed && knight.hasMoved;
var archerDone = !archer || archer.hasActed && archer.hasMoved;
if (knightDone && archerDone) {
endTurnButton.visible = true;
instructionText.setText('Click End Turn to continue');
} else {
instructionText.setText('Select next unit to move');
}
}
function endPlayerTurn() {
endTurnButton.visible = false;
gameState = 'enemy_turn';
turnText.setText('Enemy Turn');
instructionText.setText('Enemies moving...');
// Reset player units
if (knight) {
knight.hasActed = false;
knight.hasMoved = false;
}
if (archer) {
archer.hasActed = false;
archer.hasMoved = false;
}
// Start enemy turn after delay
LK.setTimeout(function () {
executeEnemyTurn();
}, 1000);
}
function executeEnemyTurn() {
var enemyIndex = 0;
function processNextEnemy() {
if (enemyIndex >= enemies.length) {
// All enemies processed, start new player turn
gameState = 'player_turn';
turnText.setText('Player Turn');
instructionText.setText('Select a unit to move');
turnPhase = 'select';
selectedUnit = null;
clearHighlights();
LK.getSound('trumpet').play();
return;
}
var enemy = enemies[enemyIndex];
if (!enemy || enemy.hp <= 0) {
enemyIndex++;
processNextEnemy();
return;
}
// Simple AI: move towards nearest player and attack if in range
var targets = [];
if (knight && knight.hp > 0) targets.push(knight);
if (archer && archer.hp > 0) targets.push(archer);
if (targets.length === 0) {
enemyIndex++;
processNextEnemy();
return;
}
// Find closest target
var closestTarget = targets[0];
var closestDistance = getDistance(enemy.gridX, enemy.gridY, closestTarget.gridX, closestTarget.gridY);
for (var i = 1; i < targets.length; i++) {
var distance = getDistance(enemy.gridX, enemy.gridY, targets[i].gridX, targets[i].gridY);
if (distance < closestDistance) {
closestTarget = targets[i];
closestDistance = distance;
}
}
// Try to attack first
if (closestDistance <= enemy.attackRange) {
if (enemy.attack && typeof enemy.attack === 'function') {
enemy.attack(closestTarget);
}
enemyIndex++;
LK.setTimeout(processNextEnemy, 500);
} else {
// Move towards target
var bestMove = findBestMove(enemy, closestTarget);
if (bestMove) {
// Play random movement sound
var moveSounds = ['move', 'move1', 'move2', 'move3'];
var randomSound = moveSounds[Math.floor(Math.random() * moveSounds.length)];
LK.getSound(randomSound).play();
moveToPosition(enemy, bestMove.x, bestMove.y, function () {
enemyIndex++;
LK.setTimeout(processNextEnemy, 500);
});
} else {
enemyIndex++;
LK.setTimeout(processNextEnemy, 500);
}
}
}
processNextEnemy();
}
function moveToPosition(unit, targetX, targetY, callback) {
var startX = unit.gridX;
var startY = unit.gridY;
var path = [];
// Simple pathfinding - move one step at a time toward target
var currentX = startX;
var currentY = startY;
while (currentX !== targetX || currentY !== targetY) {
var nextX = currentX;
var nextY = currentY;
if (currentX < targetX) nextX++;else if (currentX > targetX) nextX--;else if (currentY < targetY) nextY++;else if (currentY > targetY) nextY--;
path.push({
x: nextX,
y: nextY
});
currentX = nextX;
currentY = nextY;
}
var stepIndex = 0;
function animateNextStep() {
if (stepIndex >= path.length) {
if (callback) callback();
return;
}
var step = path[stepIndex];
placeUnit(unit, step.x, step.y);
stepIndex++;
LK.setTimeout(animateNextStep, 300);
}
animateNextStep();
}
function findBestMove(enemy, target) {
var bestMove = null;
var bestDistance = getDistance(enemy.gridX, enemy.gridY, target.gridX, target.gridY);
for (var dx = -enemy.moveRange; dx <= enemy.moveRange; dx++) {
for (var dy = -enemy.moveRange; dy <= enemy.moveRange; dy++) {
if (Math.abs(dx) + Math.abs(dy) > enemy.moveRange) continue;
var newX = enemy.gridX + dx;
var newY = enemy.gridY + dy;
// Check bounds first before accessing grid
if (newX >= 0 && newX < gridWidth && newY >= 0 && newY < gridHeight) {
var cell = grid[newX][newY];
if (cell && (!cell.occupied || newX === enemy.gridX && newY === enemy.gridY)) {
var distance = getDistance(newX, newY, target.gridX, target.gridY);
if (distance < bestDistance) {
bestDistance = distance;
bestMove = {
x: newX,
y: newY
};
}
}
}
}
}
return bestMove;
}
function spawnEnemies() {
var enemyTypes = [SkeletonSoldier, SkeletonArcher, SkeletonKnight];
var enemyCount = Math.min(3 + wave, 8);
for (var i = 0; i < enemyCount; i++) {
var enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new enemyType();
// Find random empty position on right side of map
var placed = false;
var attempts = 0;
while (!placed && attempts < 50) {
var x = Math.floor(gridWidth * 0.6 + Math.random() * gridWidth * 0.4);
var y = Math.floor(Math.random() * gridHeight);
if (!grid[x][y].occupied) {
placeUnit(enemy, x, y);
enemies.push(enemy);
game.addChild(enemy);
placed = true;
}
attempts++;
}
}
}
function spawnBarrels() {
var barrelCount = Math.min(2 + Math.floor(wave / 2), 4);
for (var i = 0; i < barrelCount; i++) {
var barrel = new Barrel();
// Find random empty position in middle area of map
var placed = false;
var attempts = 0;
while (!placed && attempts < 50) {
var x = Math.floor(gridWidth * 0.3 + Math.random() * gridWidth * 0.4);
var y = Math.floor(Math.random() * gridHeight);
if (!grid[x][y].occupied) {
placeUnit(barrel, x, y);
barrels.push(barrel);
game.addChild(barrel);
placed = true;
}
attempts++;
}
}
}
function spawnObstacles() {
var obstacleTypes = ['stone', 'box', 'column'];
var obstacleCount = Math.min(3 + Math.floor(wave / 3), 6);
for (var i = 0; i < obstacleCount; i++) {
var obstacle = new Obstacle();
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
obstacle.init(type);
// Find random empty position across the map
var placed = false;
var attempts = 0;
while (!placed && attempts < 50) {
var x = Math.floor(Math.random() * gridWidth);
var y = Math.floor(Math.random() * gridHeight);
// Avoid placing near starting positions and exit
if (x > 2 && x < gridWidth - 3 && !grid[x][y].occupied) {
placeUnit(obstacle, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
placed = true;
}
attempts++;
}
}
}
function checkVictory() {
if (enemies.length === 0) {
LK.getSound('trompeta').play();
LK.playMusic('Victoria');
LK.showYouWin();
return;
}
}
function showHealthInterface(unit) {
// Remove existing health interface if any
if (game.healthInterface) {
game.healthInterface.destroy();
game.healthInterface = null;
}
// Create health interface
var healthInterface = new Container();
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.8,
tint: 0x000000
});
background.alpha = 0.8;
healthInterface.addChild(background);
// Health text
var healthText = new Text2('HP: ' + unit.hp + '/' + unit.maxHp, {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthInterface.addChild(healthText);
// Position interface next to end turn button
healthInterface.x = endTurnButton.x - 300;
healthInterface.y = endTurnButton.y;
game.addChild(healthInterface);
game.healthInterface = healthInterface;
// Auto-hide after 3 seconds
LK.setTimeout(function () {
if (game.healthInterface === healthInterface) {
healthInterface.destroy();
game.healthInterface = null;
}
}, 3000);
}
function checkGameOver() {
if ((!knight || knight.hp <= 0) && (!archer || archer.hp <= 0)) {
LK.showGameOver();
}
}
// Initialize game
initializeGrid();
// Create and place heroes
knight = new Knight();
archer = new Archer();
game.addChild(knight);
game.addChild(archer);
placeUnit(knight, 1, 3);
placeUnit(archer, 1, 4);
// Create exit portal
exitPortal = LK.getAsset('exitPortal', {
anchorX: 0.5,
anchorY: 0.5
});
exitPortal.gridX = gridWidth - 2;
exitPortal.gridY = 3;
exitPortal.x = grid[exitPortal.gridX][exitPortal.gridY].x;
exitPortal.y = grid[exitPortal.gridX][exitPortal.gridY].y;
exitPortal.alpha = 0.7;
game.addChild(exitPortal);
// Spawn initial enemies
spawnEnemies();
// Spawn initial barrels
spawnBarrels();
// Spawn initial obstacles
spawnObstacles();
// Play trumpet sound to signal game start
LK.getSound('trumpet').play();
// Stop victory music if playing to ensure it only plays during victories
LK.stopMusic();
// Reset all game variables to initial state
function resetGame() {
// Clear all arrays
units = [];
enemies = [];
barrels = [];
obstacles = [];
// Reset references
knight = null;
archer = null;
exitPortal = null;
selectedUnit = null;
selectedTarget = null;
// Reset game state
gameState = 'player_turn';
turnPhase = 'select';
wave = 1;
// Clear grid
for (var x = 0; x < gridWidth; x++) {
for (var y = 0; y < gridHeight; y++) {
if (grid[x][y]) {
grid[x][y].occupied = false;
grid[x][y].unit = null;
grid[x][y].destroy();
}
}
}
grid = [];
// Clear health interface
if (game.healthInterface) {
game.healthInterface.destroy();
game.healthInterface = null;
}
// Reset UI elements
turnText.setText('Player Turn');
waveText.setText('Wave 1');
instructionText.setText('Select a unit to move');
endTurnButton.visible = false;
// Clear highlights
clearHighlights();
// Reinitialize everything
initializeGrid();
// Create and place heroes
knight = new Knight();
archer = new Archer();
game.addChild(knight);
game.addChild(archer);
placeUnit(knight, 1, 3);
placeUnit(archer, 1, 4);
// Create exit portal
exitPortal = LK.getAsset('exitPortal', {
anchorX: 0.5,
anchorY: 0.5
});
exitPortal.gridX = gridWidth - 2;
exitPortal.gridY = 3;
exitPortal.x = grid[exitPortal.gridX][exitPortal.gridY].x;
exitPortal.y = grid[exitPortal.gridX][exitPortal.gridY].y;
exitPortal.alpha = 0.7;
game.addChild(exitPortal);
// Spawn initial enemies
spawnEnemies();
// Spawn initial barrels
spawnBarrels();
// Spawn initial obstacles
spawnObstacles();
// Play trumpet sound to signal game start
LK.getSound('trumpet').play();
// Stop victory music if playing to ensure it only plays during victories
LK.stopMusic();
}
// Call reset function immediately to restart the game
resetGame();
game.update = function () {
// Game logic is handled by event-driven system
}; ===================================================================
--- original.js
+++ change.js
@@ -1062,10 +1062,11 @@
if (Math.abs(dx) + Math.abs(dy) > enemy.moveRange) continue;
var newX = enemy.gridX + dx;
var newY = enemy.gridY + dy;
// Check bounds first before accessing grid
- if (newX >= 0 && newX < gridWidth && newY >= 0 && newY < gridHeight && grid[newX] && grid[newX][newY]) {
- if (!grid[newX][newY].occupied || newX === enemy.gridX && newY === enemy.gridY) {
+ if (newX >= 0 && newX < gridWidth && newY >= 0 && newY < gridHeight) {
+ var cell = grid[newX][newY];
+ if (cell && (!cell.occupied || newX === enemy.gridX && newY === enemy.gridY)) {
var distance = getDistance(newX, newY, target.gridX, target.gridY);
if (distance < bestDistance) {
bestDistance = distance;
bestMove = {
Un portal de color morado que tiene pinta de vortice. In-Game asset. 2d. High contrast. No shadows
caballero zorro, con escudo y lanza. In-Game asset. 2d. High contrast. No shadows
conejo con archo y flecha mirando hacia la derecha y capucha tiene. In-Game asset. 2d. High contrast. No shadows
arquero esqueleto. In-Game asset. High contrast. No shadows
un esqueleto con espada y escudo. In-Game asset. 2d. High contrast. No shadows
un esqueleto grande, con un hacha, un caso y escudo. In-Game asset. 2d. High contrast. No shadows
un botton que dice "Fin del Turno" con letras blancas y grandes. In-Game asset. 2d. High contrast. No shadows
cesped. In-Game asset. 2d. High contrast. No shadows
un barril rojo de era medieval. In-Game asset. 2d. High contrast. No shadows
una caja de madera de color marron. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
una columna de piedra. In-Game asset. 2d. High contrast. No shadows
rocas grande. In-Game asset. 2d. High contrast. No shadows
esqueletos guerreros que salen de la tierra por un brujo, miesntras un ballero zorro y un conejo arquero miran asombrado. In-Game asset. 2d. High contrast. No shadows
un hechizero esqueleto les mira de forma aterradora hacia adelante con ojos rojos y brillosos. In-Game asset. 2d. High contrast. No shadows
un caballero zorro y un arquero conejo caminan por el bosque tranquilamente, mientras en el fondo en la oscuridad del bosque se ven unos ojos rojos que resaltan. In-Game asset. 2d. High contrast. No shadows