User prompt
despues de los 15 segundos de partida agrega un nuevo enemigo el ogro que es capaz de recibir 2 proyectiles antes de morir
User prompt
aumenta un poco mas la velocidad de los enemigos ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
aumenta la velociad de los enemigos ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
aumenta la velocidad de los enemigos progresivamente ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ahora haz que tenga sentido la aparicion de enemigos teniendo en cuenta que deberia aumentar la dificultad conforme avanza la partida
User prompt
cada vez que se elimina un enemigo se produce un sonido de dolor
User prompt
ahora 1.5s
User prompt
ahora de 1s
User prompt
deben tener un tiempo de espera entre enemigos de 0.25s
User prompt
ahora pueden salir aleatoriamente a partir del 5
User prompt
ahora del 5 al 10 pueden salir de manera aleatoria del centro y las diagonales
User prompt
ahora has que los primeros 5 salgan del centro
User prompt
continua fallando
User prompt
fix it
User prompt
cuando dejo de disparar siguen apareciendo todos del mismo lado
User prompt
implementalo
User prompt
ahora no saldran mas de dos enemigos consecutivos de un solo camino
User prompt
no esta sonando
User prompt
produce un sonido cada vez que se impacte un enemigo
User prompt
ahora no pueden salir mas de 2 enemigos seguidos de un solo camino
User prompt
que los enemigos mueran de un solo proyectil
User prompt
que el proyectil sea casi instantaneo
User prompt
ahora el proyectil sea morado y casi intantaneo ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cada vez que tocas un enemigo el mago tira un proyectil ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ahora los proyectiles seran morados y mucho mas rapidos ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.bobOffset = Math.random() * Math.PI * 2;
self.initialY = 0;
self.update = function () {
if (self.initialY === 0) {
self.initialY = self.y;
}
// Only do bobbing animation and collection if not animating to coin counter
if (!self.isAnimating) {
// Bobbing animation
self.y = self.initialY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 10;
// Check collection by knight
if (knight && self.intersects(knight)) {
self.collect();
}
}
};
self.collect = function () {
LK.getSound('coinCollect').play();
LK.setScore(LK.getScore() + 5);
coinCounter++;
coinText.setText('Coins: ' + coinCounter);
// Remove from coins array
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] === self) {
coins.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
// Game arrays to track objects
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0
});
self.health = 100;
self.maxHealth = 100;
self.speed = 7;
// Note: health, maxHealth and speed will be overridden by difficulty system
self.lastX = 0;
self.update = function () {
// Progressive speed increase - increase speed by 50% over 10 seconds
if (!self.speedTweenStarted) {
self.speedTweenStarted = true;
var targetSpeed = self.speed * 2.5; // Increase speed by 150%
tween(self, {
speed: targetSpeed
}, {
duration: 10000,
// 10 seconds
easing: tween.easeOut
});
}
// Move along the assigned path toward the wizard
if (wizard && self.pathAngle !== undefined) {
// Move along the path direction toward the wizard
var moveX = -Math.cos(self.pathAngle) * self.speed;
var moveY = -Math.sin(self.pathAngle) * self.speed;
self.x += moveX;
self.y += moveY;
} else if (wizard) {
// Fallback: move directly toward the wizard if no path assigned
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Normalize direction and apply speed
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else {
// Fallback: move down if knight doesn't exist
self.y += self.speed;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
// Flash red when hit
LK.effects.flashObject(self, 0xFF0000, 200);
// Enemies die from any projectile hit
self.die();
};
self.down = function (x, y, obj) {
// Create projectile from wizard to enemy when enemy is tapped
if (wizard && projectiles.length < 10) {
// Limit projectiles to prevent spam
var projectile = game.addChild(new Projectile());
projectile.x = wizard.x;
projectile.y = wizard.y;
// Calculate direction from wizard to enemy
var dx = self.x - wizard.x;
var dy = self.y - wizard.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
projectile.direction.x = dx / distance;
projectile.direction.y = dy / distance;
}
projectiles.push(projectile);
LK.getSound('spellCast').play();
}
};
self.die = function () {
// Play pain sound when enemy dies
LK.getSound('painSound').play();
// Drop coin
var coin = game.addChild(new Coin());
coin.x = self.x;
coin.y = self.y - 50;
coin.isAnimating = true;
coins.push(coin);
// Animate coin moving toward coin counter
var coinTargetX = 120 + coinText.width / 2;
var coinTargetY = 90 + coinText.height / 2;
tween(coin, {
x: coinTargetX,
y: coinTargetY,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Increment coin counter after animation
coinCounter++;
coinText.setText('Coins: ' + coinCounter);
// Remove coin from array and destroy
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] === coin) {
coins.splice(i, 1);
break;
}
}
coin.destroy();
}
});
// Increment enemy kill counter
enemyKillCounter++;
killCountText.setText('Puntuacion: ' + enemyKillCounter);
// Add experience to wizard
wizard.gainExperience(25);
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
LK.setScore(LK.getScore() + 10);
};
return self;
});
var GameMenu = Container.expand(function () {
var self = Container.call(this);
// Semi-transparent background overlay
var menuBg = self.attachAsset('pathSelector', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 35,
scaleY: 45
});
menuBg.alpha = 0.9;
menuBg.tint = 0x87CEEB; // Celeste (sky blue) color
// Title text
var titleText = new Text2('WIZARD DEFENDER', {
size: 150,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 800;
self.addChild(titleText);
// Instructions text
var instructionsText = new Text2('TAP PATHS TO CAST SPELLS\nDEFEND YOUR CASTLE!', {
size: 80,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 2048 / 2;
instructionsText.y = 1200;
self.addChild(instructionsText);
// Start button
var startButton = self.attachAsset('wizard', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1600,
scaleX: 2,
scaleY: 2
});
var startButtonText = new Text2('START GAME', {
size: 100,
fill: 0x000000
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 2048 / 2;
startButtonText.y = 1700;
self.addChild(startButtonText);
// Button interaction
self.down = function (x, y, obj) {
// Start the game by hiding menu
self.startGame();
};
self.startGame = function () {
// Hide menu and start game
self.visible = false;
gameStarted = true;
// Show all game elements
castle.visible = true;
wizard.visible = true;
for (var i = 0; i < paths.length; i++) {
paths[i].visible = true;
}
coinText.visible = true;
killCountText.visible = true;
tapText.visible = true;
healthBarBg.visible = true;
healthBar.visible = true;
healthText.visible = true;
// Start medieval music
LK.playMusic('medievalTheme');
};
return self;
});
var Ogre = Container.expand(function () {
var self = Container.call(this);
var ogreGraphics = self.attachAsset('ogre', {
anchorX: 0.5,
anchorY: 1.0
});
self.health = 100;
self.maxHealth = 100;
self.speed = 7;
self.hitsToKill = 2;
self.hitsTaken = 0;
// Note: health, maxHealth and speed will be overridden by difficulty system
self.lastX = 0;
self.update = function () {
// Progressive speed increase - increase speed by 150% over 10 seconds
if (!self.speedTweenStarted) {
self.speedTweenStarted = true;
var targetSpeed = self.speed * 2.5; // Increase speed by 150%
tween(self, {
speed: targetSpeed
}, {
duration: 10000,
// 10 seconds
easing: tween.easeOut
});
}
// Move along the assigned path toward the wizard
if (wizard && self.pathAngle !== undefined) {
// Move along the path direction toward the wizard
var moveX = -Math.cos(self.pathAngle) * self.speed;
var moveY = -Math.sin(self.pathAngle) * self.speed;
self.x += moveX;
self.y += moveY;
} else if (wizard) {
// Fallback: move directly toward the wizard if no path assigned
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Normalize direction and apply speed
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else {
// Fallback: move down if knight doesn't exist
self.y += self.speed;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.hitsTaken++;
// Flash red when hit
LK.effects.flashObject(self, 0xFF0000, 200);
// Ogres need 2 hits to die
if (self.hitsTaken >= self.hitsToKill) {
self.die();
}
};
self.down = function (x, y, obj) {
// Create projectile from wizard to ogre when ogre is tapped
if (wizard && projectiles.length < 10) {
// Limit projectiles to prevent spam
var projectile = game.addChild(new Projectile());
projectile.x = wizard.x;
projectile.y = wizard.y;
// Calculate direction from wizard to ogre
var dx = self.x - wizard.x;
var dy = self.y - wizard.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
projectile.direction.x = dx / distance;
projectile.direction.y = dy / distance;
}
projectiles.push(projectile);
LK.getSound('spellCast').play();
}
};
self.die = function () {
// Play pain sound when ogre dies
LK.getSound('painSound').play();
// Drop coin
var coin = game.addChild(new Coin());
coin.x = self.x;
coin.y = self.y - 50;
coin.isAnimating = true;
coins.push(coin);
// Animate coin moving toward coin counter
var coinTargetX = 120 + coinText.width / 2;
var coinTargetY = 90 + coinText.height / 2;
tween(coin, {
x: coinTargetX,
y: coinTargetY,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Increment coin counter after animation
coinCounter++;
coinText.setText('Coins: ' + coinCounter);
// Remove coin from array and destroy
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] === coin) {
coins.splice(i, 1);
break;
}
}
coin.destroy();
}
});
// Increment enemy kill counter
enemyKillCounter++;
killCountText.setText('Puntuacion: ' + enemyKillCounter);
// Add experience to wizard
wizard.gainExperience(25);
// Remove from ogres array
for (var i = ogres.length - 1; i >= 0; i--) {
if (ogres[i] === self) {
ogres.splice(i, 1);
break;
}
}
self.destroy();
LK.setScore(LK.getScore() + 15);
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 50;
self.direction = {
x: 0,
y: 0
};
self.lastIntersecting = {};
self.update = function () {
// Move projectile
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.removeFromGame();
return;
}
// Check collision with enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (!self.lastIntersecting[i]) {
self.lastIntersecting[i] = false;
}
var currentIntersecting = self.intersects(enemy);
if (!self.lastIntersecting[i] && currentIntersecting) {
// Hit enemy
enemy.takeDamage(50);
self.removeFromGame();
return;
}
self.lastIntersecting[i] = currentIntersecting;
}
// Check collision with ogres
for (var i = ogres.length - 1; i >= 0; i--) {
var ogre = ogres[i];
var ogreKey = 'ogre_' + i;
if (!self.lastIntersecting[ogreKey]) {
self.lastIntersecting[ogreKey] = false;
}
var currentOgreIntersecting = self.intersects(ogre);
if (!self.lastIntersecting[ogreKey] && currentOgreIntersecting) {
// Hit ogre
ogre.takeDamage(50);
self.removeFromGame();
return;
}
self.lastIntersecting[ogreKey] = currentOgreIntersecting;
}
};
self.removeFromGame = function () {
// Remove from projectiles array
for (var i = projectiles.length - 1; i >= 0; i--) {
if (projectiles[i] === self) {
projectiles.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Wizard = Container.expand(function () {
var self = Container.call(this);
var wizardGraphics = self.attachAsset('wizard', {
anchorX: 0.5,
anchorY: 1.0
});
self.attackCooldown = 0;
self.level = 1;
self.experience = 0;
self.health = 100;
self.maxHealth = 100;
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.attack = function (direction) {
if (self.attackCooldown <= 0) {
// Default direction if none specified
if (direction === undefined) {
direction = 0; // Default to center path
}
// Get attack angle based on path direction
var attackAngle = pathAngles[direction];
var attackDistance = 100;
// Calculate spell position based on attack direction
var spellX = self.x + Math.cos(attackAngle) * attackDistance;
var spellY = self.y + Math.sin(attackAngle) * attackDistance;
// Create spell effect
var spell = game.addChild(LK.getAsset('spell', {
anchorX: 0.5,
anchorY: 0.5,
x: spellX,
y: spellY,
scaleX: 0.5,
scaleY: 0.5
}));
// Animate spell with magical effects
tween(spell, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
spell.destroy();
}
});
// Add rotation animation to spell
tween(spell, {
rotation: Math.PI * 2
}, {
duration: 500,
easing: tween.linear
});
self.attackCooldown = 30; // 0.5 seconds at 60fps
LK.getSound('spellCast').play();
// Attack enemies in the specified direction/path
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.pathIndex === direction) {
// Check if enemy is within attack range along this path
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
// Attack range
enemy.takeDamage(50);
}
}
}
// Attack ogres in the specified direction/path
for (var i = ogres.length - 1; i >= 0; i--) {
var ogre = ogres[i];
if (ogre.pathIndex === direction) {
// Check if ogre is within attack range along this path
var dx = ogre.x - self.x;
var dy = ogre.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
// Attack range
ogre.takeDamage(50);
}
}
}
return true;
}
return false;
};
self.gainExperience = function (amount) {
self.experience += amount;
var expNeeded = self.level * 100;
if (self.experience >= expNeeded) {
self.levelUp();
}
};
self.levelUp = function () {
self.level++;
self.experience = 0;
// Visual level up effect
LK.effects.flashObject(self, 0xFFD700, 500);
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
// Game over when health reaches 0
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
// Update health bar
updateHealthBar();
// Flash red when hit
LK.effects.flashObject(self, 0xFF0000, 200);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Celeste (sky blue) background
});
/****
* Game Code
****/
// Game state variables
var gameStarted = false;
var gameMenu;
// Game arrays to track objects
var enemies = [];
var ogres = [];
var coins = [];
var projectiles = [];
// Create and show game menu
gameMenu = game.addChild(new GameMenu());
// Create castle background
var castle = game.addChild(LK.getAsset('castle', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: 2732 - 200
}));
castle.visible = false;
// Create 5 dirt paths leading toward the knight position
var paths = [];
var knightX = 2048 / 2;
var knightY = 2732 - 250;
// Create paths: center, two diagonal, and two side paths
var pathAngles = [-Math.PI / 2,
// Center (straight down)
-Math.PI / 4,
// Diagonal right
-3 * Math.PI / 4,
// Diagonal left
0,
// Right side (horizontal)
Math.PI // Left side (horizontal)
]; // 5 paths: center, two diagonal, two sides
for (var p = 0; p < 5; p++) {
var angle = pathAngles[p];
// Calculate path length - make center path longer
var pathLength = p === 0 ? 3000 : 1800; // Center path is longer
// Calculate path center position to extend from screen edge to knight
var centerX = knightX + Math.cos(angle) * (pathLength / 2);
var centerY = knightY + Math.sin(angle) * (pathLength / 2);
// Create stone path with extended length
var path = game.addChild(LK.getAsset('stonePath', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
scaleY: pathLength / 800,
// Scale to desired length
rotation: angle + Math.PI / 2 // Rotate path to point toward knight
}));
// Make paths slightly transparent so they don't overwhelm the game
path.alpha = 0.7;
path.visible = false;
// Store path index for identification
path.pathIndex = p;
// Add touch handler for directional attacks (enemy elimination is now handled by attacks)
path.down = function (x, y, obj) {
// Attack in this path's direction
wizard.attack(obj.pathIndex);
// Visual feedback - flash the path briefly
LK.effects.flashObject(obj, 0xFFFFFF, 300);
};
paths.push(path);
}
// Create wizard
var wizard = game.addChild(new Wizard());
wizard.x = knightX;
wizard.y = knightY;
wizard.visible = false;
// UI Elements
// Removed scoreText and levelText to eliminate stray characters in top right
var coinCounter = 0;
var enemyKillCounter = 0;
var coinText = new Text2('Coins: 0', {
size: 60,
fill: 0xFFD700
});
coinText.anchor.set(0, 0);
LK.gui.topLeft.addChild(coinText);
coinText.x = 120;
coinText.y = 90;
coinText.visible = false;
var killCountText = new Text2('Puntuacion: 0', {
size: 60,
fill: 0xFF6B6B
});
killCountText.anchor.set(0, 0);
LK.gui.topLeft.addChild(killCountText);
killCountText.x = 120;
killCountText.y = 150;
killCountText.visible = false;
var tapText = new Text2('TAP TO CAST SPELLS!', {
size: 100,
fill: 0xFF6B6B
});
tapText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(tapText);
tapText.y = -200;
tapText.visible = false;
// Health bar UI
var healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
});
LK.gui.topLeft.addChild(healthBarBg);
healthBarBg.x = 120;
healthBarBg.y = 20;
healthBarBg.visible = false;
var healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0
});
LK.gui.topLeft.addChild(healthBar);
healthBar.x = 120;
healthBar.y = 20;
healthBar.visible = false;
var healthText = new Text2('Health: 100/100', {
size: 50,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthText);
healthText.x = 120;
healthText.y = 50;
healthText.visible = false;
function updateHealthBar() {
var healthPercent = wizard.health / wizard.maxHealth;
healthBar.scaleX = healthPercent;
healthText.setText('Health: ' + wizard.health + '/' + wizard.maxHealth);
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
}
// Enemy spawning variables
var enemySpawnTimer = 0;
var lastSpawnedPath = -1; // Track the last spawned path
var consecutiveSpawns = 0; // Track consecutive spawns from same path
// Cooldown system variables
var pathLastSpawnTime = [-1, -1, -1, -1, -1]; // Track last spawn time for each path
var pathConsecutiveSpawns = [0, 0, 0, 0, 0]; // Track consecutive spawns per path
var pathCooldownDuration = 300; // 5 seconds at 60fps
// Game input handling
game.down = function (x, y, obj) {
// Check if a path was tapped for directional attack
var pathTapped = false;
for (var p = 0; p < paths.length; p++) {
var path = paths[p];
// Convert tap position to path's local coordinates
var localPos = path.toLocal({
x: x,
y: y
});
// Check if tap is within path bounds
if (Math.abs(localPos.x) < path.width / 2 && Math.abs(localPos.y) < path.height / 2) {
// Attack in this path's direction
wizard.attack(path.pathIndex);
pathTapped = true;
break;
}
}
// No default attack - player must tap a path to attack
};
// Main game update loop
game.update = function () {
// Only update game logic if game has started
if (!gameStarted) {
return;
}
// Reset consecutive spawns for paths that have cooled down (runs every frame)
for (var pathIdx = 0; pathIdx < 5; pathIdx++) {
// Check if enough time has passed since last spawn (cooldown expired)
if (pathLastSpawnTime[pathIdx] !== -1 && LK.ticks - pathLastSpawnTime[pathIdx] > pathCooldownDuration) {
// Reset consecutive spawns for this path due to cooldown
pathConsecutiveSpawns[pathIdx] = 0;
}
}
// Progressive difficulty system based on kills
var difficultyLevel = Math.floor(enemyKillCounter / 10); // Every 10 kills increases difficulty
var currentSpawnRate = Math.max(30, 90 - difficultyLevel * 8); // Faster spawning over time
var enemyHealthMultiplier = 1 + difficultyLevel * 0.3; // 30% more health per difficulty level
var enemySpeedMultiplier = 1 + difficultyLevel * 0.35; // 35% faster per difficulty level
// Spawn enemies
enemySpawnTimer++;
if (enemySpawnTimer >= currentSpawnRate) {
enemySpawnTimer = 0;
var enemy = game.addChild(new Enemy());
// Apply difficulty scaling to enemy stats
enemy.health = Math.floor(100 * enemyHealthMultiplier);
enemy.maxHealth = enemy.health;
enemy.speed = 3 * enemySpeedMultiplier;
// Spawn enemy at a random path entrance
var randomPathIndex;
if (enemyKillCounter < 5) {
// First 5 enemies spawn from center path only
randomPathIndex = 0; // Center path
} else {
// After 5 enemies, spawn from any path but limit consecutive spawns with cooldown system
// Build available paths list
var availablePaths = [];
for (var pathIdx = 0; pathIdx < 5; pathIdx++) {
// If path has spawned less than 2 consecutive times, it's available
if (pathConsecutiveSpawns[pathIdx] < 2) {
availablePaths.push(pathIdx);
}
}
// If no paths available, reset ALL paths to ensure balanced distribution
if (availablePaths.length === 0) {
// Reset all paths' consecutive spawn counters
for (var pathIdx = 0; pathIdx < 5; pathIdx++) {
pathConsecutiveSpawns[pathIdx] = 0;
availablePaths.push(pathIdx);
}
}
randomPathIndex = availablePaths[Math.floor(Math.random() * availablePaths.length)];
}
// Update path-specific spawn tracking
// Always increment consecutive spawns for the selected path
pathConsecutiveSpawns[randomPathIndex]++;
// Update path spawn time and global tracking
pathLastSpawnTime[randomPathIndex] = LK.ticks;
lastSpawnedPath = randomPathIndex;
consecutiveSpawns = pathConsecutiveSpawns[randomPathIndex];
var pathAngle = pathAngles[randomPathIndex];
// Store the path information on the enemy
enemy.pathIndex = randomPathIndex;
enemy.pathAngle = pathAngle;
// Calculate spawn position at the far end of the path (screen edge)
var pathLength = randomPathIndex === 0 ? 3000 : 1800; // Match path lengths
var spawnDistance = pathLength * 0.8; // Spawn farther away - 80% of path length
enemy.x = knightX + Math.cos(pathAngle) * spawnDistance;
enemy.y = knightY + Math.sin(pathAngle) * spawnDistance;
// Make sure enemies spawn within screen bounds
enemy.x = Math.max(50, Math.min(1998, enemy.x));
enemy.y = Math.max(-200, Math.min(2732 + 100, enemy.y));
enemy.lastX = enemy.x;
enemies.push(enemy);
}
// Spawn ogres after 15 seconds (900 ticks at 60fps)
if (LK.ticks >= 900 && LK.ticks % 180 === 0) {
// Every 3 seconds after 15 seconds
var ogre = game.addChild(new Ogre());
// Apply difficulty scaling to ogre stats
ogre.health = Math.floor(150 * enemyHealthMultiplier); // Ogres have more base health
ogre.maxHealth = ogre.health;
ogre.speed = 2.5 * enemySpeedMultiplier; // Ogres are slightly slower
// Spawn ogre at a random path entrance
var ogrePathIndex = Math.floor(Math.random() * 5);
var ogrePathAngle = pathAngles[ogrePathIndex];
// Store the path information on the ogre
ogre.pathIndex = ogrePathIndex;
ogre.pathAngle = ogrePathAngle;
// Calculate spawn position at the far end of the path (screen edge)
var ogrePathLength = ogrePathIndex === 0 ? 3000 : 1800;
var ogreSpawnDistance = ogrePathLength * 0.8;
ogre.x = knightX + Math.cos(ogrePathAngle) * ogreSpawnDistance;
ogre.y = knightY + Math.sin(ogrePathAngle) * ogreSpawnDistance;
// Make sure ogres spawn within screen bounds
ogre.x = Math.max(50, Math.min(1998, ogre.x));
ogre.y = Math.max(-200, Math.min(2732 + 100, ogre.y));
ogre.lastX = ogre.x;
ogres.push(ogre);
}
// Check enemy-knight collisions and cleanup off-screen enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Remove enemies that went off-screen at bottom
if (enemy.y > 2732 + 100) {
enemies.splice(i, 1);
enemy.destroy();
continue;
}
// Initialize lastIntersecting if not set
if (enemy.lastIntersecting === undefined) {
enemy.lastIntersecting = false;
}
// Check for collision transition
var currentIntersecting = enemy.intersects(wizard);
if (!enemy.lastIntersecting && currentIntersecting) {
// Damage wizard when enemy touches for the first time
wizard.takeDamage(20);
// Remove enemy after dealing damage
enemies.splice(i, 1);
enemy.destroy();
continue;
}
// Update last intersecting state
enemy.lastIntersecting = currentIntersecting;
}
// Check ogre-wizard collisions and cleanup off-screen ogres
for (var i = ogres.length - 1; i >= 0; i--) {
var ogre = ogres[i];
// Remove ogres that went off-screen at bottom
if (ogre.y > 2732 + 100) {
ogres.splice(i, 1);
ogre.destroy();
continue;
}
// Initialize lastIntersecting if not set
if (ogre.lastIntersecting === undefined) {
ogre.lastIntersecting = false;
}
// Check for collision transition
var currentOgreIntersecting = ogre.intersects(wizard);
if (!ogre.lastIntersecting && currentOgreIntersecting) {
// Damage wizard when ogre touches for the first time
wizard.takeDamage(30); // Ogres deal more damage
// Remove ogre after dealing damage
ogres.splice(i, 1);
ogre.destroy();
continue;
}
// Update last intersecting state
ogre.lastIntersecting = currentOgreIntersecting;
}
// Make tap text pulse
var pulse = 1 + Math.sin(LK.ticks * 0.1) * 0.2;
tapText.scale.set(pulse, pulse);
};
// Remove tap text after 5 seconds
tween({}, {}, {
duration: 5000,
onFinish: function onFinish() {
if (tapText && tapText.parent) {
tapText.destroy();
}
}
}); ===================================================================
--- original.js
+++ change.js
@@ -241,8 +241,136 @@
LK.playMusic('medievalTheme');
};
return self;
});
+var Ogre = Container.expand(function () {
+ var self = Container.call(this);
+ var ogreGraphics = self.attachAsset('ogre', {
+ anchorX: 0.5,
+ anchorY: 1.0
+ });
+ self.health = 100;
+ self.maxHealth = 100;
+ self.speed = 7;
+ self.hitsToKill = 2;
+ self.hitsTaken = 0;
+ // Note: health, maxHealth and speed will be overridden by difficulty system
+ self.lastX = 0;
+ self.update = function () {
+ // Progressive speed increase - increase speed by 150% over 10 seconds
+ if (!self.speedTweenStarted) {
+ self.speedTweenStarted = true;
+ var targetSpeed = self.speed * 2.5; // Increase speed by 150%
+ tween(self, {
+ speed: targetSpeed
+ }, {
+ duration: 10000,
+ // 10 seconds
+ easing: tween.easeOut
+ });
+ }
+ // Move along the assigned path toward the wizard
+ if (wizard && self.pathAngle !== undefined) {
+ // Move along the path direction toward the wizard
+ var moveX = -Math.cos(self.pathAngle) * self.speed;
+ var moveY = -Math.sin(self.pathAngle) * self.speed;
+ self.x += moveX;
+ self.y += moveY;
+ } else if (wizard) {
+ // Fallback: move directly toward the wizard if no path assigned
+ var dx = wizard.x - self.x;
+ var dy = wizard.y - self.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance > 0) {
+ // Normalize direction and apply speed
+ self.x += dx / distance * self.speed;
+ self.y += dy / distance * self.speed;
+ }
+ } else {
+ // Fallback: move down if knight doesn't exist
+ self.y += self.speed;
+ }
+ };
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ self.hitsTaken++;
+ // Flash red when hit
+ LK.effects.flashObject(self, 0xFF0000, 200);
+ // Ogres need 2 hits to die
+ if (self.hitsTaken >= self.hitsToKill) {
+ self.die();
+ }
+ };
+ self.down = function (x, y, obj) {
+ // Create projectile from wizard to ogre when ogre is tapped
+ if (wizard && projectiles.length < 10) {
+ // Limit projectiles to prevent spam
+ var projectile = game.addChild(new Projectile());
+ projectile.x = wizard.x;
+ projectile.y = wizard.y;
+ // Calculate direction from wizard to ogre
+ var dx = self.x - wizard.x;
+ var dy = self.y - wizard.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance > 0) {
+ projectile.direction.x = dx / distance;
+ projectile.direction.y = dy / distance;
+ }
+ projectiles.push(projectile);
+ LK.getSound('spellCast').play();
+ }
+ };
+ self.die = function () {
+ // Play pain sound when ogre dies
+ LK.getSound('painSound').play();
+ // Drop coin
+ var coin = game.addChild(new Coin());
+ coin.x = self.x;
+ coin.y = self.y - 50;
+ coin.isAnimating = true;
+ coins.push(coin);
+ // Animate coin moving toward coin counter
+ var coinTargetX = 120 + coinText.width / 2;
+ var coinTargetY = 90 + coinText.height / 2;
+ tween(coin, {
+ x: coinTargetX,
+ y: coinTargetY,
+ scaleX: 0.5,
+ scaleY: 0.5
+ }, {
+ duration: 1000,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ // Increment coin counter after animation
+ coinCounter++;
+ coinText.setText('Coins: ' + coinCounter);
+ // Remove coin from array and destroy
+ for (var i = coins.length - 1; i >= 0; i--) {
+ if (coins[i] === coin) {
+ coins.splice(i, 1);
+ break;
+ }
+ }
+ coin.destroy();
+ }
+ });
+ // Increment enemy kill counter
+ enemyKillCounter++;
+ killCountText.setText('Puntuacion: ' + enemyKillCounter);
+ // Add experience to wizard
+ wizard.gainExperience(25);
+ // Remove from ogres array
+ for (var i = ogres.length - 1; i >= 0; i--) {
+ if (ogres[i] === self) {
+ ogres.splice(i, 1);
+ break;
+ }
+ }
+ self.destroy();
+ LK.setScore(LK.getScore() + 15);
+ };
+ return self;
+});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
@@ -277,8 +405,24 @@
return;
}
self.lastIntersecting[i] = currentIntersecting;
}
+ // Check collision with ogres
+ for (var i = ogres.length - 1; i >= 0; i--) {
+ var ogre = ogres[i];
+ var ogreKey = 'ogre_' + i;
+ if (!self.lastIntersecting[ogreKey]) {
+ self.lastIntersecting[ogreKey] = false;
+ }
+ var currentOgreIntersecting = self.intersects(ogre);
+ if (!self.lastIntersecting[ogreKey] && currentOgreIntersecting) {
+ // Hit ogre
+ ogre.takeDamage(50);
+ self.removeFromGame();
+ return;
+ }
+ self.lastIntersecting[ogreKey] = currentOgreIntersecting;
+ }
};
self.removeFromGame = function () {
// Remove from projectiles array
for (var i = projectiles.length - 1; i >= 0; i--) {
@@ -362,8 +506,22 @@
enemy.takeDamage(50);
}
}
}
+ // Attack ogres in the specified direction/path
+ for (var i = ogres.length - 1; i >= 0; i--) {
+ var ogre = ogres[i];
+ if (ogre.pathIndex === direction) {
+ // Check if ogre is within attack range along this path
+ var dx = ogre.x - self.x;
+ var dy = ogre.y - self.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 150) {
+ // Attack range
+ ogre.takeDamage(50);
+ }
+ }
+ }
return true;
}
return false;
};
@@ -410,8 +568,9 @@
var gameStarted = false;
var gameMenu;
// Game arrays to track objects
var enemies = [];
+var ogres = [];
var coins = [];
var projectiles = [];
// Create and show game menu
gameMenu = game.addChild(new GameMenu());
@@ -646,8 +805,33 @@
enemy.y = Math.max(-200, Math.min(2732 + 100, enemy.y));
enemy.lastX = enemy.x;
enemies.push(enemy);
}
+ // Spawn ogres after 15 seconds (900 ticks at 60fps)
+ if (LK.ticks >= 900 && LK.ticks % 180 === 0) {
+ // Every 3 seconds after 15 seconds
+ var ogre = game.addChild(new Ogre());
+ // Apply difficulty scaling to ogre stats
+ ogre.health = Math.floor(150 * enemyHealthMultiplier); // Ogres have more base health
+ ogre.maxHealth = ogre.health;
+ ogre.speed = 2.5 * enemySpeedMultiplier; // Ogres are slightly slower
+ // Spawn ogre at a random path entrance
+ var ogrePathIndex = Math.floor(Math.random() * 5);
+ var ogrePathAngle = pathAngles[ogrePathIndex];
+ // Store the path information on the ogre
+ ogre.pathIndex = ogrePathIndex;
+ ogre.pathAngle = ogrePathAngle;
+ // Calculate spawn position at the far end of the path (screen edge)
+ var ogrePathLength = ogrePathIndex === 0 ? 3000 : 1800;
+ var ogreSpawnDistance = ogrePathLength * 0.8;
+ ogre.x = knightX + Math.cos(ogrePathAngle) * ogreSpawnDistance;
+ ogre.y = knightY + Math.sin(ogrePathAngle) * ogreSpawnDistance;
+ // Make sure ogres spawn within screen bounds
+ ogre.x = Math.max(50, Math.min(1998, ogre.x));
+ ogre.y = Math.max(-200, Math.min(2732 + 100, ogre.y));
+ ogre.lastX = ogre.x;
+ ogres.push(ogre);
+ }
// Check enemy-knight collisions and cleanup off-screen enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Remove enemies that went off-screen at bottom
@@ -672,8 +856,34 @@
}
// Update last intersecting state
enemy.lastIntersecting = currentIntersecting;
}
+ // Check ogre-wizard collisions and cleanup off-screen ogres
+ for (var i = ogres.length - 1; i >= 0; i--) {
+ var ogre = ogres[i];
+ // Remove ogres that went off-screen at bottom
+ if (ogre.y > 2732 + 100) {
+ ogres.splice(i, 1);
+ ogre.destroy();
+ continue;
+ }
+ // Initialize lastIntersecting if not set
+ if (ogre.lastIntersecting === undefined) {
+ ogre.lastIntersecting = false;
+ }
+ // Check for collision transition
+ var currentOgreIntersecting = ogre.intersects(wizard);
+ if (!ogre.lastIntersecting && currentOgreIntersecting) {
+ // Damage wizard when ogre touches for the first time
+ wizard.takeDamage(30); // Ogres deal more damage
+ // Remove ogre after dealing damage
+ ogres.splice(i, 1);
+ ogre.destroy();
+ continue;
+ }
+ // Update last intersecting state
+ ogre.lastIntersecting = currentOgreIntersecting;
+ }
// Make tap text pulse
var pulse = 1 + Math.sin(LK.ticks * 0.1) * 0.2;
tapText.scale.set(pulse, pulse);
};