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 ComicPanel = Container.expand(function () {
var self = Container.call(this);
self.panelImage = null;
self.titleText = null;
self.storyText = null;
self.currentPanel = 0;
self.totalPanels = 3;
self.init = function (panelNumber, title, story) {
// Create background
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 2.5,
tint: 0x000000
});
background.alpha = 0.9;
self.addChild(background);
// Create panel image
self.panelImage = self.attachAsset('comicPanel' + panelNumber, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
self.panelImage.y = -100;
// Create title
self.titleText = new Text2(title, {
size: 60,
fill: 0xFFD700
});
self.titleText.anchor.set(0.5, 0.5);
self.titleText.y = 200;
self.addChild(self.titleText);
// Create story text
self.storyText = new Text2(story, {
size: 35,
fill: 0xFFFFFF
});
self.storyText.anchor.set(0.5, 0.5);
self.storyText.y = 280;
self.addChild(self.storyText);
// Create continue prompt
var continueText = new Text2('Tap to continue...', {
size: 30,
fill: 0xCCCCCC
});
continueText.anchor.set(0.5, 0.5);
continueText.y = 350;
self.addChild(continueText);
// Animate text appearance
self.titleText.alpha = 0;
self.storyText.alpha = 0;
continueText.alpha = 0;
tween(self.titleText, {
alpha: 1
}, {
duration: 500,
delay: 200
});
tween(self.storyText, {
alpha: 1
}, {
duration: 800,
delay: 600
});
tween(continueText, {
alpha: 1
}, {
duration: 500,
delay: 1200
});
};
self.down = function (x, y, obj) {
LK.getSound('pageFlip').play();
self.destroy();
game.showNextComicPanel();
};
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
****/
// Comic story system
var comicStory = [{
title: "The Ancient Kingdom",
story: "Long ago, brave knights and archers defended\nthe realm from the forces of darkness..."
}, {
title: "The Skeleton Army Rises",
story: "But evil has awakened! Skeleton warriors emerge\nfrom ancient tombs to conquer the land..."
}, {
title: "Heroes Must Rise",
story: "Only you can lead our heroes to victory!\nGuide them through enemy territory to escape!"
}];
var currentComicPanel = 0;
var showingComic = true;
var comicContainer = null;
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 - 150;
endTurnButton.y = 2732 - 150;
endTurnButton.visible = false; // Hide during comic
game.addChild(endTurnButton);
// Initialize grid
function initializeGrid() {
// Optimize grid positioning for full screen - leave more space at top for UI
var startX = (2048 - gridWidth * cellSize) / 2;
var startY = 200 + (2732 - 400 - gridHeight * cellSize) / 2; // Leave 200px at top and bottom
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 && obstacles[i].obstacleType !== 'box') {
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;
}
}
}
}
}
return hasValidAttacks;
}
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);
var hasAttacks = 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', '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);
var hasAttacks = highlightValidAttacks(selectedUnit);
// If unit has no valid attacks and has moved, auto-switch to next unit
if (!hasAttacks && selectedUnit.hasMoved) {
// Find next available unit
var nextUnit = null;
if (selectedUnit === knight && archer && (!archer.hasActed || !archer.hasMoved)) {
nextUnit = archer;
} else if (selectedUnit === archer && knight && (!knight.hasActed || !knight.hasMoved)) {
nextUnit = knight;
}
if (nextUnit) {
// Mark current unit as done and switch to next
selectedUnit.hasActed = true;
grid[selectedUnit.gridX][selectedUnit.gridY].hideSelectedBorder();
selectedUnit = nextUnit;
grid[selectedUnit.gridX][selectedUnit.gridY].showSelectedBorder();
highlightValidMoves(selectedUnit);
highlightValidAttacks(selectedUnit);
instructionText.setText('Switched to next unit - Click to move and/or attack');
} else {
instructionText.setText('Click to attack or click unit again when done');
}
} else {
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 && obstacles[i].obstacleType !== 'box') {
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';
// End Turn button is always available during player turn
endTurnButton.visible = true;
// 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) {
instructionText.setText('All units ready - Click End Turn to continue');
} else {
instructionText.setText('Select next unit to move or Click End Turn to skip');
}
}
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', '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;
if (newX >= 0 && newX < gridWidth && newY >= 0 && newY < gridHeight) {
if (!grid[newX][newY].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, optimized for full screen
healthInterface.x = endTurnButton.x - 350;
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();
}
}
function showComicStory() {
if (currentComicPanel < comicStory.length) {
// Start comic music on first panel
if (currentComicPanel === 0) {
LK.playMusic('comicMusic');
}
var panel = new ComicPanel();
var storyData = comicStory[currentComicPanel];
panel.init(currentComicPanel + 1, storyData.title, storyData.story);
panel.x = 2048 / 2;
panel.y = 2732 / 2;
game.addChild(panel);
comicContainer = panel;
currentComicPanel++;
} else {
// Comic finished, start the game
startActualGame();
}
}
game.showNextComicPanel = function () {
if (comicContainer) {
comicContainer = null;
}
showComicStory();
};
function startActualGame() {
showingComic = false;
// Stop comic music and start battle music
LK.stopMusic();
LK.playMusic('battleMusic');
// Initialize the actual game
initializeGameplay();
}
function initializeGameplay() {
// Initialize grid
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();
// Make End Turn button available from game start
endTurnButton.visible = true;
// Play trumpet sound to signal game start
LK.getSound('trumpet').play();
}
// Start with comic story
showComicStory();
game.update = function () {
// Show/hide UI based on comic state
if (showingComic) {
turnText.visible = false;
waveText.visible = false;
instructionText.visible = false;
endTurnButton.visible = false;
} else {
turnText.visible = true;
waveText.visible = true;
instructionText.visible = true;
// endTurnButton visibility handled by game logic
}
}; ===================================================================
--- original.js
+++ change.js
@@ -795,16 +795,17 @@
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;
+endTurnButton.x = 2048 - 150;
+endTurnButton.y = 2732 - 150;
endTurnButton.visible = false; // Hide during comic
game.addChild(endTurnButton);
// Initialize grid
function initializeGrid() {
+ // Optimize grid positioning for full screen - leave more space at top for UI
var startX = (2048 - gridWidth * cellSize) / 2;
- var startY = (2732 - gridHeight * cellSize) / 2;
+ var startY = 200 + (2732 - 400 - gridHeight * cellSize) / 2; // Leave 200px at top and bottom
for (var x = 0; x < gridWidth; x++) {
grid[x] = [];
for (var y = 0; y < gridHeight; y++) {
var cell = new GridCell();
@@ -1295,10 +1296,10 @@
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthInterface.addChild(healthText);
- // Position interface next to end turn button
- healthInterface.x = endTurnButton.x - 300;
+ // Position interface next to end turn button, optimized for full screen
+ healthInterface.x = endTurnButton.x - 350;
healthInterface.y = endTurnButton.y;
game.addChild(healthInterface);
game.healthInterface = healthInterface;
// Auto-hide after 3 seconds
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