User prompt
Please fix the bug: 'Timeout.tick error: enemy.attack is not a function' in or related to this line: 'enemy.attack(closestTarget);' Line Number: 1015
User prompt
me gustaria que hubiera tres sonidos de cuando las unidades se mueven
User prompt
tambien debe haber estrucutras como piedras, cajas o columna que impida el paso de las unidades, cuando el arceqero esqueleto ataca tambien se le debe ver la flecha de a quien se le ataco ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cuando el jugador selecione a la unidad al que va a atacar en el 2 click tomalo como confirmacion. ademas la musica solo debe durar hasta que inicie la partida
User prompt
cuando el jugador gana, debe sonar un cancion de victoria
User prompt
al barril se le puede atacar tambien.
User prompt
tambien debe haber un barril rojo que este se pueda destruir y al hacerlo dañara a todas las unidades alrededor de este, este tendra poca vida y reproducira un sonido al ser destruido ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cuando el jugador selecione el ataque a la unidad enemiga, solo debe hacer un click para que ataque
User prompt
cuando el jugador este selecionado a la unidad al cual va a atacar lo debe de hacer al un solo click, ademas que el arquero esqueleto cuando este tambien ataque se le debe ver la flecha ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cuando el jugador toque a sus unidades, este debe salir cuanta vida tiene pero que se vea afuera de la lucha
User prompt
lo de la interfaz debe salir al lado del boton de fin del turno, solo debe aparecer ahi, ademas de que cuando el jugador selecione a sus unidades no debe estorbar la interzacc
User prompt
me gustaria ahora que cuando el jugador selecione a sus tropas salga un pequeño interzac para indicar cuanto le queda de vida, esto tambien para cuando seleciona a las unidades enemigas. Cunado el jugador gane debe sonar una trompeta de victoria ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cuando las unidades enemigas se mueven se deben ver por cada casilla que hace hasta llegar el punto donde quieren. Cunado los arqueros atacan se tiene que ver la flecha que se lanzo. cuando las unidades enemigas reciben daño se tiene que ver que lanzan unas particulas grises ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cuando la unidad del jugador entra en el area de ataque hacia las unidades enemigas, tiene que ser una opcion de si atacar o no, que tambien se pueda mover como atacar las dos opciones deben estar, ademas de que todas las unidades enemigas son vencidas esto lo vuelve una victoria para el jugador
User prompt
te faltaria que tambien hubiera una opcion donde si ya entra en el area de atque de la unidad del jugador, este pudiera atacar desde antes, tambien que las unidades del jugador reciba daño salga unas particulas de la unidad del jugador quien fue atacado ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
bien pero falta que cuando la unidad del jugador no puede atacar a ninguna unidad enemiga, pasara de forma automatica a la siguiente unidad del jugador
User prompt
cuando es el turno del jugador este puede selecionar a uno de sus unidades(caballero o arquero) para mover
User prompt
cuando es el turno del jugador, este podra selecionar a unos de sus unidades, para que se pueda mover, cuando este sea selecionado este tendra un borde dorado para señalar que lo esta controlando, ademas que el tendra un limite de 3 casillas para mover y para atacar sera cuando el enemigo este alrededor de la casilla de las unidades del jugador y este se pondra en rojo para señalar que se le puede atacar, en la casilla donde esta el enemigo, esto para el caballero en el arquero se puede mover dos casillas pero tiene un rango de 4 para atacar
User prompt
que se vea todo remarcado con grosor negro para diferenciar lo lugares que se puede mover el jugador, ademas de que presiono al jugador ý puede ver el area que puede moverse y despues atacar, cuando haya hecho el movimiento seguira el arquero, cuando ya nadie se pueda mover debe salir un boton que diga pasar turno y cuando se le presiona el enemigo se movera para atacarnos, finalizada sus movimientos volveria ser el turno del jugador
User prompt
podes hacer primero empezie el jugador y que de una señal como un rudio de una trompeta, ademas que el nivels sea lateral sera todo el juego en perspectiva 2D.
Code edit (1 edits merged)
Please save this source code
User prompt
Dark Lord's Domain: Knight & Archer Quest
Initial prompt
un juego de un caballero y arquero que deben pasar por las tierras del señor oscuro, sera por turno las peleas, se puede ganar llegando a la salida o matando a todos los enemigos, seran 3 tipos de enemigos(son todos esqueletos) soldado, mediano en daño,velocidad y vida, archero mediano pero menos vida y mas velocidad, y caballero pesado mas vida, mas daño pero es mas lento.
/****
* 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.down = function (x, y, obj) {
if (gameState === 'player_turn' && turnPhase === 'select') {
handleCellClick(self.gridX, self.gridY);
}
};
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.down = function (x, y, obj) {
if (gameState === 'player_turn' && turnPhase === 'select') {
handleCellClick(self.gridX, self.gridY);
}
};
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.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();
}
};
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.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();
}
};
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.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();
}
};
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 knight = null;
var archer = null;
var exitPortal = null;
var selectedUnit = 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();
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;
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
var isEnemy = false;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === grid[x][y].unit) {
isEnemy = true;
break;
}
}
if (isEnemy) {
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;
}
}
}
}
// If no valid attacks and in attack-only phase, automatically move to next unit
if (!hasValidAttacks && turnPhase === 'attack') {
unit.hasActed = true;
endPlayerAction();
}
}
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();
// Check if there are enemies in attack range
var hasEnemiesInRange = false;
for (var x = 0; x < gridWidth; x++) {
for (var y = 0; y < gridHeight; y++) {
var distance = getDistance(selectedUnit.gridX, selectedUnit.gridY, x, y);
if (distance <= selectedUnit.attackRange && grid[x][y].occupied && grid[x][y].unit !== selectedUnit) {
// Check if it's an enemy
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === grid[x][y].unit) {
hasEnemiesInRange = true;
break;
}
}
if (hasEnemiesInRange) break;
}
}
if (hasEnemiesInRange) break;
}
if (hasEnemiesInRange) {
turnPhase = 'move_or_attack';
highlightValidMoves(selectedUnit);
highlightValidAttacks(selectedUnit);
instructionText.setText('Click to move or click enemy to attack');
} else {
turnPhase = 'move';
highlightValidMoves(selectedUnit);
instructionText.setText('Click to move, or click unit again to attack');
}
}
}
} else if (turnPhase === 'move') {
if (cell === grid[selectedUnit.gridX][selectedUnit.gridY]) {
// Clicked on selected unit again, switch to attack
turnPhase = 'attack';
highlightValidAttacks(selectedUnit);
instructionText.setText('Click enemy to attack');
} else if (cell.highlighted && !cell.occupied) {
// Move to this cell
placeUnit(selectedUnit, gridX, gridY);
selectedUnit.hasMoved = true;
LK.getSound('move').play();
// Check if reached exit portal
if (exitPortal && gridX === exitPortal.gridX && gridY === exitPortal.gridY) {
LK.getSound('victory').play();
LK.showYouWin();
return;
}
turnPhase = 'attack';
highlightValidAttacks(selectedUnit);
instructionText.setText('Click enemy to attack, or end turn');
}
} else if (turnPhase === 'move_or_attack') {
if (cell.highlighted && !cell.occupied) {
// Move to this cell
placeUnit(selectedUnit, gridX, gridY);
selectedUnit.hasMoved = true;
LK.getSound('move').play();
// Check if reached exit portal
if (exitPortal && gridX === exitPortal.gridX && gridY === exitPortal.gridY) {
LK.getSound('victory').play();
LK.showYouWin();
return;
}
turnPhase = 'attack';
highlightValidAttacks(selectedUnit);
instructionText.setText('Click enemy to attack, or end turn');
} else if (cell.highlighted && cell.occupied) {
// Attack this enemy
selectedUnit.attack(cell.unit);
selectedUnit.hasActed = true;
endPlayerAction();
}
} else if (turnPhase === 'attack') {
if (cell === grid[selectedUnit.gridX][selectedUnit.gridY] && !selectedUnit.hasMoved) {
// Clicked on selected unit again and hasn't moved yet, switch to move
turnPhase = 'move';
highlightValidMoves(selectedUnit);
instructionText.setText('Click to move, or click unit again to attack');
} else if (cell.highlighted && cell.occupied) {
// Attack this enemy
selectedUnit.attack(cell.unit);
selectedUnit.hasActed = true;
endPlayerAction();
}
}
}
function endPlayerAction() {
clearHighlights();
if (selectedUnit) {
grid[selectedUnit.gridX][selectedUnit.gridY].hideSelectedBorder();
}
selectedUnit = null;
turnPhase = 'select';
// Check if both units have acted
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) {
closestTarget.takeDamage(enemy.damage);
LK.getSound('attack').play();
enemyIndex++;
LK.setTimeout(processNextEnemy, 500);
} else {
// Move towards target
var bestMove = findBestMove(enemy, closestTarget);
if (bestMove) {
LK.getSound('move').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 checkVictory() {
if (enemies.length === 0) {
LK.getSound('victory').play();
LK.showYouWin();
return;
}
}
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();
// Play trumpet sound to signal game start
LK.getSound('trumpet').play();
game.update = function () {
// Game logic is handled by event-driven system
}; ===================================================================
--- original.js
+++ change.js
@@ -59,9 +59,35 @@
}
};
self.attack = function (target) {
if (target && target.takeDamage) {
- target.takeDamage(self.damage);
+ // 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;
}
};
@@ -247,8 +273,36 @@
self.attackRange = 3;
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) {
@@ -277,8 +331,36 @@
self.attackRange = 1;
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) {
@@ -307,8 +389,36 @@
self.attackRange = 1;
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) {
@@ -650,21 +760,58 @@
// Try to attack first
if (closestDistance <= enemy.attackRange) {
closestTarget.takeDamage(enemy.damage);
LK.getSound('attack').play();
+ enemyIndex++;
+ LK.setTimeout(processNextEnemy, 500);
} else {
// Move towards target
var bestMove = findBestMove(enemy, closestTarget);
if (bestMove) {
- placeUnit(enemy, bestMove.x, bestMove.y);
LK.getSound('move').play();
+ moveToPosition(enemy, bestMove.x, bestMove.y, function () {
+ enemyIndex++;
+ LK.setTimeout(processNextEnemy, 500);
+ });
+ } else {
+ enemyIndex++;
+ LK.setTimeout(processNextEnemy, 500);
}
}
- 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++) {
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