/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Barrier = Container.expand(function () {
var self = Container.call(this);
var barrierGraphics = self.attachAsset('barrier', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Update barrier visibility based on energy
var energyPercent = crystal.barrierEnergy / crystal.maxBarrierEnergy;
barrierGraphics.alpha = energyPercent * 0.3;
if (energyPercent > 0.5) {
barrierGraphics.tint = 0x00FFFF;
} else if (energyPercent > 0.25) {
barrierGraphics.tint = 0xFFFF00;
} else {
barrierGraphics.tint = 0xFF4500;
}
};
return self;
});
var Battery = Container.expand(function () {
var self = Container.call(this);
var batteryGraphics = self.attachAsset('battery', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Check if cyborg is close enough to collect
var distToCyborg = Math.sqrt(Math.pow(self.x - cyborg.x, 2) + Math.pow(self.y - cyborg.y, 2));
if (distToCyborg < 40) {
crystal.restoreBarrier(20);
LK.getSound('collect').play();
// Remove from batteries array
for (var i = batteries.length - 1; i >= 0; i--) {
if (batteries[i] === self) {
batteries.splice(i, 1);
break;
}
}
returnBatteryToPool(self);
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 25;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (self.intersects(enemy)) {
enemy.takeDamage(self.damage);
// Remove bullet
for (var j = bullets.length - 1; j >= 0; j--) {
if (bullets[j] === self) {
bullets.splice(j, 1);
break;
}
}
returnBulletToPool(self);
return;
}
}
// Check collision with structures
for (var s = structures.length - 1; s >= 0; s--) {
if (self.intersects(structures[s])) {
// Remove bullet
for (var p = bullets.length - 1; p >= 0; p--) {
if (bullets[p] === self) {
bullets.splice(p, 1);
break;
}
}
returnBulletToPool(self);
return;
}
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
for (var k = bullets.length - 1; k >= 0; k--) {
if (bullets[k] === self) {
bullets.splice(k, 1);
break;
}
}
returnBulletToPool(self);
}
};
return self;
});
var Crystal = Container.expand(function () {
var self = Container.call(this);
var crystalGraphics = self.attachAsset('crystal', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 500;
self.maxHealth = 500;
self.barrierEnergy = 100;
self.maxBarrierEnergy = 100;
self.takeDamage = function (damage) {
if (self.barrierEnergy > 0) {
self.barrierEnergy -= damage;
if (self.barrierEnergy < 0) {
var overflow = -self.barrierEnergy;
self.barrierEnergy = 0;
self.health -= overflow;
}
} else {
self.health -= damage;
}
crystalHealthText.setText('Crystal: ' + Math.floor(self.health));
barrierText.setText('Barrier: ' + Math.floor(self.barrierEnergy));
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
LK.showGameOver();
}
};
self.restoreBarrier = function (amount) {
self.barrierEnergy += amount;
if (self.barrierEnergy > self.maxBarrierEnergy) {
self.barrierEnergy = self.maxBarrierEnergy;
}
barrierText.setText('Barrier: ' + Math.floor(self.barrierEnergy));
};
return self;
});
var Cyborg = Container.expand(function () {
var self = Container.call(this);
var cyborgGraphics = self.attachAsset('cyborg', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 12; // Increased from 8 to 12 for even faster movement
self.attackDamage = 25;
self.shootCooldown = 0;
self.isNearCrystal = false;
self.update = function () {
// Store previous position for rotation calculation
if (self.lastX === undefined) self.lastX = self.x;
if (self.lastY === undefined) self.lastY = self.y;
// Handle movement button input with improved responsiveness
if (isMoving && (Math.abs(movementDirection.x) > 0.05 || Math.abs(movementDirection.y) > 0.05)) {
var speedMultiplier = 2.0; // Boost speed when using movement button
var newX = self.x + movementDirection.x * self.speed * speedMultiplier;
var newY = self.y + movementDirection.y * self.speed * speedMultiplier;
// Check bounds
newX = Math.max(40, Math.min(2008, newX));
newY = Math.max(40, Math.min(2692, newY));
// Check structure collision
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
}
// Calculate movement direction for smooth rotation
var dx = self.x - self.lastX;
var dy = self.y - self.lastY;
var movementDistance = Math.sqrt(dx * dx + dy * dy);
// Only rotate if actually moving
if (movementDistance > 0.5) {
var targetRotation = Math.atan2(dy, dx) + Math.PI / 2;
// Smooth rotation towards movement direction
var currentRotation = cyborgGraphics.rotation;
var rotationDiff = targetRotation - currentRotation;
// Handle rotation wrap-around
if (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
if (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
// Apply smooth rotation
cyborgGraphics.rotation += rotationDiff * 0.3; // Increased from 0.2 to 0.3 for even faster rotation
}
// Update last position
self.lastX = self.x;
self.lastY = self.y;
// Check if near crystal for healing
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
self.isNearCrystal = distToCrystal < 150;
// Heal when near crystal
if (self.isNearCrystal && self.health < self.maxHealth) {
self.health += 0.5;
if (self.health > self.maxHealth) self.health = self.maxHealth;
healthText.setText('Health: ' + Math.floor(self.health));
}
// Reduce shoot cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Visual feedback when near crystal with smooth color transitions
if (self.isNearCrystal) {
tween(cyborgGraphics, {
tint: 0x90EE90
}, {
duration: 200,
easing: tween.easeOut
});
} else {
tween(cyborgGraphics, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.shoot = function (targetX, targetY) {
if (self.shootCooldown <= 0) {
var bullet = getPooledBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(targetY - self.y, targetX - self.x);
bullet.velocityX = Math.cos(angle) * 8;
bullet.velocityY = Math.sin(angle) * 8;
bullets.push(bullet);
game.addChild(bullet);
self.shootCooldown = 10; // Reduced from 15 to 10 for even faster shooting
LK.getSound('shoot').play();
// Add subtle recoil animation that doesn't interfere with movement
var recoilDistance = 4; // Reduced from 8 to 4 for less movement interference
var recoilX = self.x - Math.cos(angle) * recoilDistance;
var recoilY = self.y - Math.sin(angle) * recoilDistance;
var originalX = self.x;
var originalY = self.y;
// Quick recoil back - only if not actively moving
if (!isMoving) {
tween(self, {
x: recoilX,
y: recoilY
}, {
duration: 50,
// Reduced from 80 to 50
easing: tween.easeOut,
onFinish: function onFinish() {
// Spring back to original position
tween(self, {
x: originalX,
y: originalY
}, {
duration: 80,
// Reduced from 120 to 80
easing: tween.easeOut
});
}
});
}
// Add shooting flash effect
tween(cyborgGraphics, {
tint: 0xFFFFAA
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(cyborgGraphics, {
tint: self.isNearCrystal ? 0x90EE90 : 0xFFFFFF
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
};
self.takeDamage = function (damage) {
self.health -= damage;
healthText.setText('Health: ' + Math.floor(self.health));
LK.effects.flashObject(self, 0xFF0000, 300);
// Add impact animation
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeIn
});
}
});
// Add screen shake effect by briefly moving the cyborg
var shakeIntensity = damage * 0.3;
var originalX = self.x;
var originalY = self.y;
tween(self, {
x: originalX + shakeIntensity,
y: originalY + shakeIntensity
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: originalX - shakeIntensity,
y: originalY - shakeIntensity
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: originalX,
y: originalY
}, {
duration: 100,
easing: tween.easeOut
});
}
});
}
});
if (self.health <= 0) {
LK.showGameOver();
}
};
return self;
});
var CyborgDog = Container.expand(function () {
var self = Container.call(this);
var dogGraphics = self.attachAsset('cyborgDog', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 80;
self.maxHealth = 80;
self.speed = 6;
self.attackDamage = 20;
self.shootCooldown = 0;
self.followDistance = 100;
self.lastX = 0;
self.lastY = 0;
self.targetEnemy = null;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Find closest enemy to attack
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
if (distance < closestDistance && distance < 400) {
closestDistance = distance;
closestEnemy = enemy;
}
}
self.targetEnemy = closestEnemy;
// Movement behavior
if (self.targetEnemy) {
// Combat mode: move towards enemy but maintain some distance
var enemyAngle = Math.atan2(self.targetEnemy.y - self.y, self.targetEnemy.x - self.x);
var targetDistance = 120; // Preferred combat distance
if (closestDistance > targetDistance + 20) {
// Move closer to enemy
var newX = self.x + Math.cos(enemyAngle) * self.speed;
var newY = self.y + Math.sin(enemyAngle) * self.speed;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
} else if (closestDistance < targetDistance - 20) {
// Move away from enemy (too close)
var newX = self.x - Math.cos(enemyAngle) * self.speed * 0.5;
var newY = self.y - Math.sin(enemyAngle) * self.speed * 0.5;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
}
// Shoot at enemy
if (self.shootCooldown <= 0 && closestDistance < 300) {
self.shootAtEnemy(self.targetEnemy);
self.shootCooldown = 25;
}
} else {
// Follow mode: stay near cyborg
var distToCyborg = Math.sqrt(Math.pow(self.x - cyborg.x, 2) + Math.pow(self.y - cyborg.y, 2));
if (distToCyborg > self.followDistance) {
var followAngle = Math.atan2(cyborg.y - self.y, cyborg.x - self.x);
var newX = self.x + Math.cos(followAngle) * self.speed;
var newY = self.y + Math.sin(followAngle) * self.speed;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
}
}
// Rotation towards movement direction
var dx = self.x - self.lastX;
var dy = self.y - self.lastY;
var movementDistance = Math.sqrt(dx * dx + dy * dy);
if (movementDistance > 0.5) {
var targetRotation = Math.atan2(dy, dx) + Math.PI / 2;
var currentRotation = dogGraphics.rotation;
var rotationDiff = targetRotation - currentRotation;
if (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
if (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
dogGraphics.rotation += rotationDiff * 0.25;
}
// Reduce shoot cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Visual feedback based on mode
if (self.targetEnemy) {
// Combat mode - red tint
tween(dogGraphics, {
tint: 0xFFAAAA
}, {
duration: 200,
easing: tween.easeOut
});
} else {
// Follow mode - green tint
tween(dogGraphics, {
tint: 0xAAFFAA
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.shootAtEnemy = function (enemy) {
var bullet = getPooledBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(enemy.y - self.y, enemy.x - self.x);
bullet.velocityX = Math.cos(angle) * 7;
bullet.velocityY = Math.sin(angle) * 7;
bullet.damage = self.attackDamage;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Muzzle flash effect
tween(dogGraphics, {
tint: 0xFFFFAA
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(dogGraphics, {
tint: self.targetEnemy ? 0xFFAAAA : 0xAAFFAA
}, {
duration: 100,
easing: tween.easeIn
});
}
});
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 300);
// Impact animation
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeIn
});
}
});
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Visual death effect
tween(dogGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
// Respawn after 10 seconds
LK.setTimeout(function () {
self.health = self.maxHealth;
self.x = cyborg.x + 50;
self.y = cyborg.y + 50;
dogGraphics.alpha = 1;
dogGraphics.scaleX = 1;
dogGraphics.scaleY = 1;
}, 10000);
}
});
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.speed = 1;
self.attackDamage = 15;
self.attackCooldown = 0;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Move towards crystal
var angle = Math.atan2(crystal.y - self.y, crystal.x - self.x);
var newX = self.x + Math.cos(angle) * self.speed;
var newY = self.y + Math.sin(angle) * self.speed;
// Check structure collision
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
} else {
// Try to move around obstacle
var sideAngle = angle + Math.PI / 2;
var sideX = self.x + Math.cos(sideAngle) * self.speed;
var sideY = self.y + Math.sin(sideAngle) * self.speed;
if (!checkStructureCollision(self, sideX, sideY)) {
self.x = sideX;
self.y = sideY;
}
}
// Attack crystal if close enough
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
if (distToCrystal < 160 && self.attackCooldown <= 0) {
crystal.takeDamage(self.attackDamage);
self.attackCooldown = 60;
LK.effects.flashObject(self, 0xFFFFFF, 200);
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop gear
var gear = getPooledGear();
gear.x = self.x;
gear.y = self.y;
gears.push(gear);
game.addChild(gear);
// Sometimes drop battery
if (Math.random() < 0.3) {
var battery = getPooledBattery();
battery.x = self.x + (Math.random() - 0.5) * 40;
battery.y = self.y + (Math.random() - 0.5) * 40;
batteries.push(battery);
game.addChild(battery);
}
LK.getSound('enemyHit').play();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGraphics.tint = 0xFF0000; // Red tint for enemy bullets
self.velocityX = 0;
self.velocityY = 0;
self.damage = 20;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with cyborg
if (self.intersects(cyborg)) {
cyborg.takeDamage(self.damage);
// Remove bullet
for (var i = enemyBullets.length - 1; i >= 0; i--) {
if (enemyBullets[i] === self) {
enemyBullets.splice(i, 1);
break;
}
}
returnEnemyBulletToPool(self);
return;
}
// Check collision with cyborg dog
if (self.intersects(cyborgDog)) {
cyborgDog.takeDamage(self.damage);
// Remove bullet
for (var i = enemyBullets.length - 1; i >= 0; i--) {
if (enemyBullets[i] === self) {
enemyBullets.splice(i, 1);
break;
}
}
returnEnemyBulletToPool(self);
return;
}
// Check collision with structures
for (var s = structures.length - 1; s >= 0; s--) {
if (self.intersects(structures[s])) {
// Remove bullet
for (var p = enemyBullets.length - 1; p >= 0; p--) {
if (enemyBullets[p] === self) {
enemyBullets.splice(p, 1);
break;
}
}
returnEnemyBulletToPool(self);
return;
}
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
for (var j = enemyBullets.length - 1; j >= 0; j--) {
if (enemyBullets[j] === self) {
enemyBullets.splice(j, 1);
break;
}
}
returnEnemyBulletToPool(self);
}
};
return self;
});
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('fastEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.speed = 2.5;
self.attackDamage = 10;
self.attackCooldown = 0;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Move towards crystal with erratic movement
var angle = Math.atan2(crystal.y - self.y, crystal.x - self.x);
var wobble = Math.sin(LK.ticks * 0.1) * 0.5;
var newX = self.x + Math.cos(angle + wobble) * self.speed;
var newY = self.y + Math.sin(angle + wobble) * self.speed;
// Check structure collision
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
} else {
// Try to move around obstacle
var sideAngle = angle + Math.PI / 2;
var sideX = self.x + Math.cos(sideAngle) * self.speed;
var sideY = self.y + Math.sin(sideAngle) * self.speed;
if (!checkStructureCollision(self, sideX, sideY)) {
self.x = sideX;
self.y = sideY;
}
}
// Attack crystal if close enough
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
if (distToCrystal < 160 && self.attackCooldown <= 0) {
crystal.takeDamage(self.attackDamage);
self.attackCooldown = 40;
LK.effects.flashObject(self, 0xFFFFFF, 200);
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop gear
var gear = new Gear();
gear.x = self.x;
gear.y = self.y;
gears.push(gear);
game.addChild(gear);
// Sometimes drop battery
if (Math.random() < 0.25) {
var battery = new Battery();
battery.x = self.x + (Math.random() - 0.5) * 40;
battery.y = self.y + (Math.random() - 0.5) * 40;
batteries.push(battery);
game.addChild(battery);
}
LK.getSound('enemyHit').play();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Gear = Container.expand(function () {
var self = Container.call(this);
var gearGraphics = self.attachAsset('gear', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
gearGraphics.rotation += 0.1;
// Check if cyborg is close enough to collect
var distToCyborg = Math.sqrt(Math.pow(self.x - cyborg.x, 2) + Math.pow(self.y - cyborg.y, 2));
if (distToCyborg < 40) {
gearCount++;
gearsText.setText('Gears: ' + gearCount);
LK.getSound('collect').play();
// Remove from gears array
for (var i = gears.length - 1; i >= 0; i--) {
if (gears[i] === self) {
gears.splice(i, 1);
break;
}
}
returnGearToPool(self);
}
};
return self;
});
var MovementButton = Container.expand(function () {
var self = Container.call(this);
// Outer circle (background)
var outerCircle = self.attachAsset('barrier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
alpha: 0.3
});
// Inner circle (knob)
var innerCircle = self.attachAsset('cyborg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
self.outerRadius = 60;
self.innerRadius = 25;
self.centerX = 0;
self.centerY = 0;
self.isDragging = false;
self.down = function (x, y, obj) {
self.isDragging = true;
isMoving = true;
self.updateKnob(x, y);
};
self.up = function (x, y, obj) {
self.isDragging = false;
isMoving = false;
movementDirection.x = 0;
movementDirection.y = 0;
// Animate knob back to center
tween(innerCircle, {
x: 0,
y: 0
}, {
duration: 200,
easing: tween.easeOut
});
};
self.move = function (x, y, obj) {
if (self.isDragging) {
self.updateKnob(x, y);
}
};
self.updateKnob = function (x, y) {
var dx = x - self.centerX;
var dy = y - self.centerY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.outerRadius) {
dx = dx / distance * self.outerRadius;
dy = dy / distance * self.outerRadius;
distance = self.outerRadius;
}
innerCircle.x = dx;
innerCircle.y = dy;
// Calculate normalized movement direction with improved sensitivity
var strength = Math.min(distance / self.outerRadius, 1.0);
var sensitivity = 1.5; // Increase sensitivity for better control
movementDirection.x = dx / self.outerRadius * strength * sensitivity;
movementDirection.y = dy / self.outerRadius * strength * sensitivity;
};
return self;
});
var RangedEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('rangedEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 40;
self.speed = 1.2;
self.attackDamage = 12;
self.attackCooldown = 0;
self.shootCooldown = 0;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Stay at medium distance from crystal
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
var angle = Math.atan2(crystal.y - self.y, crystal.x - self.x);
if (distToCrystal > 400) {
// Move closer
var newX = self.x + Math.cos(angle) * self.speed;
var newY = self.y + Math.sin(angle) * self.speed;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
} else if (distToCrystal < 300) {
// Move away
var newX = self.x - Math.cos(angle) * self.speed;
var newY = self.y - Math.sin(angle) * self.speed;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
}
// Shoot at cyborg
if (self.shootCooldown <= 0) {
var distToCyborg = Math.sqrt(Math.pow(self.x - cyborg.x, 2) + Math.pow(self.y - cyborg.y, 2));
if (distToCyborg < 500) {
self.shootAtCyborg();
self.shootCooldown = 120;
}
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Attack crystal if close enough
if (distToCrystal < 160 && self.attackCooldown <= 0) {
crystal.takeDamage(self.attackDamage);
self.attackCooldown = 60;
LK.effects.flashObject(self, 0xFFFFFF, 200);
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.shootAtCyborg = function () {
var bullet = getPooledEnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(cyborg.y - self.y, cyborg.x - self.x);
bullet.velocityX = Math.cos(angle) * 4;
bullet.velocityY = Math.sin(angle) * 4;
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop gear
var gear = new Gear();
gear.x = self.x;
gear.y = self.y;
gears.push(gear);
game.addChild(gear);
// Sometimes drop battery
if (Math.random() < 0.3) {
var battery = new Battery();
battery.x = self.x + (Math.random() - 0.5) * 40;
battery.y = self.y + (Math.random() - 0.5) * 40;
batteries.push(battery);
game.addChild(battery);
}
LK.getSound('enemyHit').play();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Structure = Container.expand(function (type) {
var self = Container.call(this);
var structureGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.structureType = type;
return self;
});
var TankEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('tankEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 120;
self.speed = 0.7;
self.attackDamage = 30;
self.attackCooldown = 0;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Move towards crystal steadily
var angle = Math.atan2(crystal.y - self.y, crystal.x - self.x);
var newX = self.x + Math.cos(angle) * self.speed;
var newY = self.y + Math.sin(angle) * self.speed;
// Check structure collision
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
} else {
// Try to move around obstacle
var sideAngle = angle + Math.PI / 2;
var sideX = self.x + Math.cos(sideAngle) * self.speed;
var sideY = self.y + Math.sin(sideAngle) * self.speed;
if (!checkStructureCollision(self, sideX, sideY)) {
self.x = sideX;
self.y = sideY;
}
}
// Attack crystal if close enough
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
if (distToCrystal < 160 && self.attackCooldown <= 0) {
crystal.takeDamage(self.attackDamage);
self.attackCooldown = 90;
LK.effects.flashObject(self, 0xFFFFFF, 300);
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= Math.floor(damage * 0.7); // Reduced damage
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop more gear
var gear = new Gear();
gear.x = self.x;
gear.y = self.y;
gears.push(gear);
game.addChild(gear);
// Drop extra gear
if (Math.random() < 0.6) {
var gear2 = new Gear();
gear2.x = self.x + (Math.random() - 0.5) * 30;
gear2.y = self.y + (Math.random() - 0.5) * 30;
gears.push(gear2);
game.addChild(gear2);
}
// Sometimes drop battery
if (Math.random() < 0.4) {
var battery = new Battery();
battery.x = self.x + (Math.random() - 0.5) * 40;
battery.y = self.y + (Math.random() - 0.5) * 40;
batteries.push(battery);
game.addChild(battery);
}
LK.getSound('enemyHit').play();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F2F2F
});
/****
* Game Code
****/
// Game variables
var cyborg;
var cyborgDog;
var crystal;
var barrier;
var enemies = [];
var bullets = [];
var enemyBullets = [];
var gears = [];
var batteries = [];
var structures = [];
var gearCount = 0;
var waveNumber = 1;
var enemySpawnTimer = 0;
var enemiesPerWave = 5;
var waveTimer = 0;
var waveDelay = 300;
// Object pools for performance
var bulletPool = [];
var enemyBulletPool = [];
var gearPool = [];
var batteryPool = [];
var maxPoolSize = 50;
// Movement control variables
var movementButton;
var isMoving = false;
var movementDirection = {
x: 0,
y: 0
};
// UI elements
var healthText = new Text2('Health: 100', {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0);
healthText.x = 120;
healthText.y = 50;
LK.gui.topLeft.addChild(healthText);
var crystalHealthText = new Text2('Crystal: 500', {
size: 40,
fill: 0x00FFFF
});
crystalHealthText.anchor.set(0.5, 0);
crystalHealthText.x = 1024;
crystalHealthText.y = 50;
LK.gui.top.addChild(crystalHealthText);
var barrierText = new Text2('Barrier: 100', {
size: 40,
fill: 0x00FFFF
});
barrierText.anchor.set(1, 0);
barrierText.x = 1928;
barrierText.y = 50;
LK.gui.topRight.addChild(barrierText);
var gearsText = new Text2('Gears: 0', {
size: 40,
fill: 0xFFD700
});
gearsText.anchor.set(0, 0);
gearsText.x = 120;
gearsText.y = 100;
LK.gui.topLeft.addChild(gearsText);
var waveText = new Text2('Wave: 1', {
size: 40,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.x = 1024;
waveText.y = 100;
LK.gui.top.addChild(waveText);
// Initialize game objects
crystal = game.addChild(new Crystal());
crystal.x = 1024;
crystal.y = 1366;
barrier = game.addChild(new Barrier());
barrier.x = crystal.x;
barrier.y = crystal.y;
cyborg = game.addChild(new Cyborg());
cyborg.x = 1024;
cyborg.y = 1200;
// Create cyborg dog ally
cyborgDog = game.addChild(new CyborgDog());
cyborgDog.x = cyborg.x + 80;
cyborgDog.y = cyborg.y + 80;
// Create movement button
movementButton = new MovementButton();
movementButton.x = 180;
movementButton.y = 2400; // Moved up slightly for better thumb reach
movementButton.centerX = movementButton.x;
movementButton.centerY = movementButton.y;
LK.gui.bottomLeft.addChild(movementButton);
// Create map structures
createMapStructures();
// Event handlers
game.down = function (x, y, obj) {
cyborg.shoot(x, y);
};
game.move = function (x, y, obj) {
// Only handle tap-to-move if not using movement button
if (!isMoving) {
var dx = x - cyborg.x;
var dy = y - cyborg.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
// Calculate target position with bounds checking
var targetX = Math.max(40, Math.min(2008, x));
var targetY = Math.max(40, Math.min(2692, y));
// Check if target position would collide with structures
var tempX = cyborg.x;
var tempY = cyborg.y;
cyborg.x = targetX;
cyborg.y = targetY;
var wouldCollide = false;
for (var i = 0; i < structures.length; i++) {
if (cyborg.intersects(structures[i])) {
wouldCollide = true;
break;
}
}
cyborg.x = tempX;
cyborg.y = tempY;
if (!wouldCollide) {
// Stop any existing movement tween
tween.stop(cyborg, {
x: true,
y: true
});
// Create smooth movement tween with faster speed
tween(cyborg, {
x: targetX,
y: targetY
}, {
duration: distance * 8,
// Reduced from 15 to 8 for even faster movement
// Dynamic duration based on distance
easing: tween.easeOut,
onFinish: function onFinish() {
// Optional: Add a subtle bounce effect when reaching destination
tween(cyborg, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(cyborg, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
});
}
}
}
};
function createMapStructures() {
// Create trees
var treePositions = [{
x: 300,
y: 400
}, {
x: 1700,
y: 1100
}];
for (var i = 0; i < treePositions.length; i++) {
var tree = new Structure('tree');
tree.x = treePositions[i].x;
tree.y = treePositions[i].y;
structures.push(tree);
game.addChild(tree);
}
// Create destroyed cars
var carPositions = [{
x: 600,
y: 500
}, {
x: 1400,
y: 700
}];
for (var j = 0; j < carPositions.length; j++) {
var car = new Structure('destroyedCar');
car.x = carPositions[j].x;
car.y = carPositions[j].y;
structures.push(car);
game.addChild(car);
}
// Create rocks
var rockPositions = [{
x: 700,
y: 900
}, {
x: 1300,
y: 1200
}];
for (var k = 0; k < rockPositions.length; k++) {
var rock = new Structure('rock');
rock.x = rockPositions[k].x;
rock.y = rockPositions[k].y;
structures.push(rock);
game.addChild(rock);
}
}
function spawnEnemy() {
var enemy;
// Randomly choose enemy type based on wave number
var enemyType = Math.random();
if (enemyType < 0.4) {
enemy = new Enemy();
} else if (enemyType < 0.7) {
enemy = new FastEnemy();
} else if (enemyType < 0.9) {
enemy = new RangedEnemy();
} else {
enemy = new TankEnemy();
}
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -30;
break;
case 1:
// Right
enemy.x = 2078;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2762;
break;
case 3:
// Left
enemy.x = -30;
enemy.y = Math.random() * 2732;
break;
}
enemies.push(enemy);
game.addChild(enemy);
}
function checkStructureCollision(obj, newX, newY) {
var tempX = obj.x;
var tempY = obj.y;
obj.x = newX;
obj.y = newY;
for (var i = 0; i < structures.length; i++) {
if (obj.intersects(structures[i])) {
obj.x = tempX;
obj.y = tempY;
return true;
}
}
return false;
}
function getPooledBullet() {
if (bulletPool.length > 0) {
return bulletPool.pop();
}
return new Bullet();
}
function returnBulletToPool(bullet) {
if (bulletPool.length < maxPoolSize) {
bulletPool.push(bullet);
game.removeChild(bullet);
} else {
bullet.destroy();
}
}
function getPooledEnemyBullet() {
if (enemyBulletPool.length > 0) {
return enemyBulletPool.pop();
}
return new EnemyBullet();
}
function returnEnemyBulletToPool(bullet) {
if (enemyBulletPool.length < maxPoolSize) {
enemyBulletPool.push(bullet);
game.removeChild(bullet);
} else {
bullet.destroy();
}
}
function getPooledGear() {
if (gearPool.length > 0) {
return gearPool.pop();
}
return new Gear();
}
function returnGearToPool(gear) {
if (gearPool.length < maxPoolSize) {
gearPool.push(gear);
game.removeChild(gear);
} else {
gear.destroy();
}
}
function getPooledBattery() {
if (batteryPool.length > 0) {
return batteryPool.pop();
}
return new Battery();
}
function returnBatteryToPool(battery) {
if (batteryPool.length < maxPoolSize) {
batteryPool.push(battery);
game.removeChild(battery);
} else {
battery.destroy();
}
}
function startNewWave() {
waveNumber++;
waveText.setText('Wave: ' + waveNumber);
enemiesPerWave = Math.floor(5 + waveNumber * 1.5);
waveTimer = 0;
// Spawn enemies for this wave
for (var i = 0; i < enemiesPerWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 1000);
}
}
// Main game loop
game.update = function () {
// Update wave timer
waveTimer++;
// Check if wave is complete (no enemies left and enough time passed)
if (enemies.length === 0 && waveTimer > waveDelay) {
startNewWave();
}
// Update all bullets (optimized loop)
var i = bullets.length;
while (i--) {
bullets[i].update();
}
// Update all enemy bullets (optimized loop)
var m = enemyBullets.length;
while (m--) {
enemyBullets[m].update();
}
// Update all enemies (optimized loop)
var j = enemies.length;
while (j--) {
enemies[j].update();
}
// Update all gears (optimized loop)
var k = gears.length;
while (k--) {
gears[k].update();
}
// Update all batteries (optimized loop)
var l = batteries.length;
while (l--) {
batteries[l].update();
}
// Update cyborg
cyborg.update();
// Update cyborg dog
cyborgDog.update();
// Update barrier
barrier.update();
};
// Start first wave
LK.setTimeout(function () {
startNewWave();
}, 2000); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Barrier = Container.expand(function () {
var self = Container.call(this);
var barrierGraphics = self.attachAsset('barrier', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Update barrier visibility based on energy
var energyPercent = crystal.barrierEnergy / crystal.maxBarrierEnergy;
barrierGraphics.alpha = energyPercent * 0.3;
if (energyPercent > 0.5) {
barrierGraphics.tint = 0x00FFFF;
} else if (energyPercent > 0.25) {
barrierGraphics.tint = 0xFFFF00;
} else {
barrierGraphics.tint = 0xFF4500;
}
};
return self;
});
var Battery = Container.expand(function () {
var self = Container.call(this);
var batteryGraphics = self.attachAsset('battery', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Check if cyborg is close enough to collect
var distToCyborg = Math.sqrt(Math.pow(self.x - cyborg.x, 2) + Math.pow(self.y - cyborg.y, 2));
if (distToCyborg < 40) {
crystal.restoreBarrier(20);
LK.getSound('collect').play();
// Remove from batteries array
for (var i = batteries.length - 1; i >= 0; i--) {
if (batteries[i] === self) {
batteries.splice(i, 1);
break;
}
}
returnBatteryToPool(self);
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 25;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (self.intersects(enemy)) {
enemy.takeDamage(self.damage);
// Remove bullet
for (var j = bullets.length - 1; j >= 0; j--) {
if (bullets[j] === self) {
bullets.splice(j, 1);
break;
}
}
returnBulletToPool(self);
return;
}
}
// Check collision with structures
for (var s = structures.length - 1; s >= 0; s--) {
if (self.intersects(structures[s])) {
// Remove bullet
for (var p = bullets.length - 1; p >= 0; p--) {
if (bullets[p] === self) {
bullets.splice(p, 1);
break;
}
}
returnBulletToPool(self);
return;
}
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
for (var k = bullets.length - 1; k >= 0; k--) {
if (bullets[k] === self) {
bullets.splice(k, 1);
break;
}
}
returnBulletToPool(self);
}
};
return self;
});
var Crystal = Container.expand(function () {
var self = Container.call(this);
var crystalGraphics = self.attachAsset('crystal', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 500;
self.maxHealth = 500;
self.barrierEnergy = 100;
self.maxBarrierEnergy = 100;
self.takeDamage = function (damage) {
if (self.barrierEnergy > 0) {
self.barrierEnergy -= damage;
if (self.barrierEnergy < 0) {
var overflow = -self.barrierEnergy;
self.barrierEnergy = 0;
self.health -= overflow;
}
} else {
self.health -= damage;
}
crystalHealthText.setText('Crystal: ' + Math.floor(self.health));
barrierText.setText('Barrier: ' + Math.floor(self.barrierEnergy));
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
LK.showGameOver();
}
};
self.restoreBarrier = function (amount) {
self.barrierEnergy += amount;
if (self.barrierEnergy > self.maxBarrierEnergy) {
self.barrierEnergy = self.maxBarrierEnergy;
}
barrierText.setText('Barrier: ' + Math.floor(self.barrierEnergy));
};
return self;
});
var Cyborg = Container.expand(function () {
var self = Container.call(this);
var cyborgGraphics = self.attachAsset('cyborg', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 12; // Increased from 8 to 12 for even faster movement
self.attackDamage = 25;
self.shootCooldown = 0;
self.isNearCrystal = false;
self.update = function () {
// Store previous position for rotation calculation
if (self.lastX === undefined) self.lastX = self.x;
if (self.lastY === undefined) self.lastY = self.y;
// Handle movement button input with improved responsiveness
if (isMoving && (Math.abs(movementDirection.x) > 0.05 || Math.abs(movementDirection.y) > 0.05)) {
var speedMultiplier = 2.0; // Boost speed when using movement button
var newX = self.x + movementDirection.x * self.speed * speedMultiplier;
var newY = self.y + movementDirection.y * self.speed * speedMultiplier;
// Check bounds
newX = Math.max(40, Math.min(2008, newX));
newY = Math.max(40, Math.min(2692, newY));
// Check structure collision
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
}
// Calculate movement direction for smooth rotation
var dx = self.x - self.lastX;
var dy = self.y - self.lastY;
var movementDistance = Math.sqrt(dx * dx + dy * dy);
// Only rotate if actually moving
if (movementDistance > 0.5) {
var targetRotation = Math.atan2(dy, dx) + Math.PI / 2;
// Smooth rotation towards movement direction
var currentRotation = cyborgGraphics.rotation;
var rotationDiff = targetRotation - currentRotation;
// Handle rotation wrap-around
if (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
if (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
// Apply smooth rotation
cyborgGraphics.rotation += rotationDiff * 0.3; // Increased from 0.2 to 0.3 for even faster rotation
}
// Update last position
self.lastX = self.x;
self.lastY = self.y;
// Check if near crystal for healing
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
self.isNearCrystal = distToCrystal < 150;
// Heal when near crystal
if (self.isNearCrystal && self.health < self.maxHealth) {
self.health += 0.5;
if (self.health > self.maxHealth) self.health = self.maxHealth;
healthText.setText('Health: ' + Math.floor(self.health));
}
// Reduce shoot cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Visual feedback when near crystal with smooth color transitions
if (self.isNearCrystal) {
tween(cyborgGraphics, {
tint: 0x90EE90
}, {
duration: 200,
easing: tween.easeOut
});
} else {
tween(cyborgGraphics, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.shoot = function (targetX, targetY) {
if (self.shootCooldown <= 0) {
var bullet = getPooledBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(targetY - self.y, targetX - self.x);
bullet.velocityX = Math.cos(angle) * 8;
bullet.velocityY = Math.sin(angle) * 8;
bullets.push(bullet);
game.addChild(bullet);
self.shootCooldown = 10; // Reduced from 15 to 10 for even faster shooting
LK.getSound('shoot').play();
// Add subtle recoil animation that doesn't interfere with movement
var recoilDistance = 4; // Reduced from 8 to 4 for less movement interference
var recoilX = self.x - Math.cos(angle) * recoilDistance;
var recoilY = self.y - Math.sin(angle) * recoilDistance;
var originalX = self.x;
var originalY = self.y;
// Quick recoil back - only if not actively moving
if (!isMoving) {
tween(self, {
x: recoilX,
y: recoilY
}, {
duration: 50,
// Reduced from 80 to 50
easing: tween.easeOut,
onFinish: function onFinish() {
// Spring back to original position
tween(self, {
x: originalX,
y: originalY
}, {
duration: 80,
// Reduced from 120 to 80
easing: tween.easeOut
});
}
});
}
// Add shooting flash effect
tween(cyborgGraphics, {
tint: 0xFFFFAA
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(cyborgGraphics, {
tint: self.isNearCrystal ? 0x90EE90 : 0xFFFFFF
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
};
self.takeDamage = function (damage) {
self.health -= damage;
healthText.setText('Health: ' + Math.floor(self.health));
LK.effects.flashObject(self, 0xFF0000, 300);
// Add impact animation
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeIn
});
}
});
// Add screen shake effect by briefly moving the cyborg
var shakeIntensity = damage * 0.3;
var originalX = self.x;
var originalY = self.y;
tween(self, {
x: originalX + shakeIntensity,
y: originalY + shakeIntensity
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: originalX - shakeIntensity,
y: originalY - shakeIntensity
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: originalX,
y: originalY
}, {
duration: 100,
easing: tween.easeOut
});
}
});
}
});
if (self.health <= 0) {
LK.showGameOver();
}
};
return self;
});
var CyborgDog = Container.expand(function () {
var self = Container.call(this);
var dogGraphics = self.attachAsset('cyborgDog', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 80;
self.maxHealth = 80;
self.speed = 6;
self.attackDamage = 20;
self.shootCooldown = 0;
self.followDistance = 100;
self.lastX = 0;
self.lastY = 0;
self.targetEnemy = null;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Find closest enemy to attack
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
if (distance < closestDistance && distance < 400) {
closestDistance = distance;
closestEnemy = enemy;
}
}
self.targetEnemy = closestEnemy;
// Movement behavior
if (self.targetEnemy) {
// Combat mode: move towards enemy but maintain some distance
var enemyAngle = Math.atan2(self.targetEnemy.y - self.y, self.targetEnemy.x - self.x);
var targetDistance = 120; // Preferred combat distance
if (closestDistance > targetDistance + 20) {
// Move closer to enemy
var newX = self.x + Math.cos(enemyAngle) * self.speed;
var newY = self.y + Math.sin(enemyAngle) * self.speed;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
} else if (closestDistance < targetDistance - 20) {
// Move away from enemy (too close)
var newX = self.x - Math.cos(enemyAngle) * self.speed * 0.5;
var newY = self.y - Math.sin(enemyAngle) * self.speed * 0.5;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
}
// Shoot at enemy
if (self.shootCooldown <= 0 && closestDistance < 300) {
self.shootAtEnemy(self.targetEnemy);
self.shootCooldown = 25;
}
} else {
// Follow mode: stay near cyborg
var distToCyborg = Math.sqrt(Math.pow(self.x - cyborg.x, 2) + Math.pow(self.y - cyborg.y, 2));
if (distToCyborg > self.followDistance) {
var followAngle = Math.atan2(cyborg.y - self.y, cyborg.x - self.x);
var newX = self.x + Math.cos(followAngle) * self.speed;
var newY = self.y + Math.sin(followAngle) * self.speed;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
}
}
// Rotation towards movement direction
var dx = self.x - self.lastX;
var dy = self.y - self.lastY;
var movementDistance = Math.sqrt(dx * dx + dy * dy);
if (movementDistance > 0.5) {
var targetRotation = Math.atan2(dy, dx) + Math.PI / 2;
var currentRotation = dogGraphics.rotation;
var rotationDiff = targetRotation - currentRotation;
if (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
if (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
dogGraphics.rotation += rotationDiff * 0.25;
}
// Reduce shoot cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Visual feedback based on mode
if (self.targetEnemy) {
// Combat mode - red tint
tween(dogGraphics, {
tint: 0xFFAAAA
}, {
duration: 200,
easing: tween.easeOut
});
} else {
// Follow mode - green tint
tween(dogGraphics, {
tint: 0xAAFFAA
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.shootAtEnemy = function (enemy) {
var bullet = getPooledBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(enemy.y - self.y, enemy.x - self.x);
bullet.velocityX = Math.cos(angle) * 7;
bullet.velocityY = Math.sin(angle) * 7;
bullet.damage = self.attackDamage;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Muzzle flash effect
tween(dogGraphics, {
tint: 0xFFFFAA
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(dogGraphics, {
tint: self.targetEnemy ? 0xFFAAAA : 0xAAFFAA
}, {
duration: 100,
easing: tween.easeIn
});
}
});
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 300);
// Impact animation
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeIn
});
}
});
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Visual death effect
tween(dogGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
// Respawn after 10 seconds
LK.setTimeout(function () {
self.health = self.maxHealth;
self.x = cyborg.x + 50;
self.y = cyborg.y + 50;
dogGraphics.alpha = 1;
dogGraphics.scaleX = 1;
dogGraphics.scaleY = 1;
}, 10000);
}
});
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.speed = 1;
self.attackDamage = 15;
self.attackCooldown = 0;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Move towards crystal
var angle = Math.atan2(crystal.y - self.y, crystal.x - self.x);
var newX = self.x + Math.cos(angle) * self.speed;
var newY = self.y + Math.sin(angle) * self.speed;
// Check structure collision
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
} else {
// Try to move around obstacle
var sideAngle = angle + Math.PI / 2;
var sideX = self.x + Math.cos(sideAngle) * self.speed;
var sideY = self.y + Math.sin(sideAngle) * self.speed;
if (!checkStructureCollision(self, sideX, sideY)) {
self.x = sideX;
self.y = sideY;
}
}
// Attack crystal if close enough
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
if (distToCrystal < 160 && self.attackCooldown <= 0) {
crystal.takeDamage(self.attackDamage);
self.attackCooldown = 60;
LK.effects.flashObject(self, 0xFFFFFF, 200);
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop gear
var gear = getPooledGear();
gear.x = self.x;
gear.y = self.y;
gears.push(gear);
game.addChild(gear);
// Sometimes drop battery
if (Math.random() < 0.3) {
var battery = getPooledBattery();
battery.x = self.x + (Math.random() - 0.5) * 40;
battery.y = self.y + (Math.random() - 0.5) * 40;
batteries.push(battery);
game.addChild(battery);
}
LK.getSound('enemyHit').play();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGraphics.tint = 0xFF0000; // Red tint for enemy bullets
self.velocityX = 0;
self.velocityY = 0;
self.damage = 20;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with cyborg
if (self.intersects(cyborg)) {
cyborg.takeDamage(self.damage);
// Remove bullet
for (var i = enemyBullets.length - 1; i >= 0; i--) {
if (enemyBullets[i] === self) {
enemyBullets.splice(i, 1);
break;
}
}
returnEnemyBulletToPool(self);
return;
}
// Check collision with cyborg dog
if (self.intersects(cyborgDog)) {
cyborgDog.takeDamage(self.damage);
// Remove bullet
for (var i = enemyBullets.length - 1; i >= 0; i--) {
if (enemyBullets[i] === self) {
enemyBullets.splice(i, 1);
break;
}
}
returnEnemyBulletToPool(self);
return;
}
// Check collision with structures
for (var s = structures.length - 1; s >= 0; s--) {
if (self.intersects(structures[s])) {
// Remove bullet
for (var p = enemyBullets.length - 1; p >= 0; p--) {
if (enemyBullets[p] === self) {
enemyBullets.splice(p, 1);
break;
}
}
returnEnemyBulletToPool(self);
return;
}
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
for (var j = enemyBullets.length - 1; j >= 0; j--) {
if (enemyBullets[j] === self) {
enemyBullets.splice(j, 1);
break;
}
}
returnEnemyBulletToPool(self);
}
};
return self;
});
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('fastEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.speed = 2.5;
self.attackDamage = 10;
self.attackCooldown = 0;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Move towards crystal with erratic movement
var angle = Math.atan2(crystal.y - self.y, crystal.x - self.x);
var wobble = Math.sin(LK.ticks * 0.1) * 0.5;
var newX = self.x + Math.cos(angle + wobble) * self.speed;
var newY = self.y + Math.sin(angle + wobble) * self.speed;
// Check structure collision
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
} else {
// Try to move around obstacle
var sideAngle = angle + Math.PI / 2;
var sideX = self.x + Math.cos(sideAngle) * self.speed;
var sideY = self.y + Math.sin(sideAngle) * self.speed;
if (!checkStructureCollision(self, sideX, sideY)) {
self.x = sideX;
self.y = sideY;
}
}
// Attack crystal if close enough
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
if (distToCrystal < 160 && self.attackCooldown <= 0) {
crystal.takeDamage(self.attackDamage);
self.attackCooldown = 40;
LK.effects.flashObject(self, 0xFFFFFF, 200);
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop gear
var gear = new Gear();
gear.x = self.x;
gear.y = self.y;
gears.push(gear);
game.addChild(gear);
// Sometimes drop battery
if (Math.random() < 0.25) {
var battery = new Battery();
battery.x = self.x + (Math.random() - 0.5) * 40;
battery.y = self.y + (Math.random() - 0.5) * 40;
batteries.push(battery);
game.addChild(battery);
}
LK.getSound('enemyHit').play();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Gear = Container.expand(function () {
var self = Container.call(this);
var gearGraphics = self.attachAsset('gear', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
gearGraphics.rotation += 0.1;
// Check if cyborg is close enough to collect
var distToCyborg = Math.sqrt(Math.pow(self.x - cyborg.x, 2) + Math.pow(self.y - cyborg.y, 2));
if (distToCyborg < 40) {
gearCount++;
gearsText.setText('Gears: ' + gearCount);
LK.getSound('collect').play();
// Remove from gears array
for (var i = gears.length - 1; i >= 0; i--) {
if (gears[i] === self) {
gears.splice(i, 1);
break;
}
}
returnGearToPool(self);
}
};
return self;
});
var MovementButton = Container.expand(function () {
var self = Container.call(this);
// Outer circle (background)
var outerCircle = self.attachAsset('barrier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
alpha: 0.3
});
// Inner circle (knob)
var innerCircle = self.attachAsset('cyborg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
self.outerRadius = 60;
self.innerRadius = 25;
self.centerX = 0;
self.centerY = 0;
self.isDragging = false;
self.down = function (x, y, obj) {
self.isDragging = true;
isMoving = true;
self.updateKnob(x, y);
};
self.up = function (x, y, obj) {
self.isDragging = false;
isMoving = false;
movementDirection.x = 0;
movementDirection.y = 0;
// Animate knob back to center
tween(innerCircle, {
x: 0,
y: 0
}, {
duration: 200,
easing: tween.easeOut
});
};
self.move = function (x, y, obj) {
if (self.isDragging) {
self.updateKnob(x, y);
}
};
self.updateKnob = function (x, y) {
var dx = x - self.centerX;
var dy = y - self.centerY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.outerRadius) {
dx = dx / distance * self.outerRadius;
dy = dy / distance * self.outerRadius;
distance = self.outerRadius;
}
innerCircle.x = dx;
innerCircle.y = dy;
// Calculate normalized movement direction with improved sensitivity
var strength = Math.min(distance / self.outerRadius, 1.0);
var sensitivity = 1.5; // Increase sensitivity for better control
movementDirection.x = dx / self.outerRadius * strength * sensitivity;
movementDirection.y = dy / self.outerRadius * strength * sensitivity;
};
return self;
});
var RangedEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('rangedEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 40;
self.speed = 1.2;
self.attackDamage = 12;
self.attackCooldown = 0;
self.shootCooldown = 0;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Stay at medium distance from crystal
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
var angle = Math.atan2(crystal.y - self.y, crystal.x - self.x);
if (distToCrystal > 400) {
// Move closer
var newX = self.x + Math.cos(angle) * self.speed;
var newY = self.y + Math.sin(angle) * self.speed;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
} else if (distToCrystal < 300) {
// Move away
var newX = self.x - Math.cos(angle) * self.speed;
var newY = self.y - Math.sin(angle) * self.speed;
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
}
}
// Shoot at cyborg
if (self.shootCooldown <= 0) {
var distToCyborg = Math.sqrt(Math.pow(self.x - cyborg.x, 2) + Math.pow(self.y - cyborg.y, 2));
if (distToCyborg < 500) {
self.shootAtCyborg();
self.shootCooldown = 120;
}
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Attack crystal if close enough
if (distToCrystal < 160 && self.attackCooldown <= 0) {
crystal.takeDamage(self.attackDamage);
self.attackCooldown = 60;
LK.effects.flashObject(self, 0xFFFFFF, 200);
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.shootAtCyborg = function () {
var bullet = getPooledEnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(cyborg.y - self.y, cyborg.x - self.x);
bullet.velocityX = Math.cos(angle) * 4;
bullet.velocityY = Math.sin(angle) * 4;
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop gear
var gear = new Gear();
gear.x = self.x;
gear.y = self.y;
gears.push(gear);
game.addChild(gear);
// Sometimes drop battery
if (Math.random() < 0.3) {
var battery = new Battery();
battery.x = self.x + (Math.random() - 0.5) * 40;
battery.y = self.y + (Math.random() - 0.5) * 40;
batteries.push(battery);
game.addChild(battery);
}
LK.getSound('enemyHit').play();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Structure = Container.expand(function (type) {
var self = Container.call(this);
var structureGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.structureType = type;
return self;
});
var TankEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('tankEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 120;
self.speed = 0.7;
self.attackDamage = 30;
self.attackCooldown = 0;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Move towards crystal steadily
var angle = Math.atan2(crystal.y - self.y, crystal.x - self.x);
var newX = self.x + Math.cos(angle) * self.speed;
var newY = self.y + Math.sin(angle) * self.speed;
// Check structure collision
if (!checkStructureCollision(self, newX, newY)) {
self.x = newX;
self.y = newY;
} else {
// Try to move around obstacle
var sideAngle = angle + Math.PI / 2;
var sideX = self.x + Math.cos(sideAngle) * self.speed;
var sideY = self.y + Math.sin(sideAngle) * self.speed;
if (!checkStructureCollision(self, sideX, sideY)) {
self.x = sideX;
self.y = sideY;
}
}
// Attack crystal if close enough
var distToCrystal = Math.sqrt(Math.pow(self.x - crystal.x, 2) + Math.pow(self.y - crystal.y, 2));
if (distToCrystal < 160 && self.attackCooldown <= 0) {
crystal.takeDamage(self.attackDamage);
self.attackCooldown = 90;
LK.effects.flashObject(self, 0xFFFFFF, 300);
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= Math.floor(damage * 0.7); // Reduced damage
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop more gear
var gear = new Gear();
gear.x = self.x;
gear.y = self.y;
gears.push(gear);
game.addChild(gear);
// Drop extra gear
if (Math.random() < 0.6) {
var gear2 = new Gear();
gear2.x = self.x + (Math.random() - 0.5) * 30;
gear2.y = self.y + (Math.random() - 0.5) * 30;
gears.push(gear2);
game.addChild(gear2);
}
// Sometimes drop battery
if (Math.random() < 0.4) {
var battery = new Battery();
battery.x = self.x + (Math.random() - 0.5) * 40;
battery.y = self.y + (Math.random() - 0.5) * 40;
batteries.push(battery);
game.addChild(battery);
}
LK.getSound('enemyHit').play();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F2F2F
});
/****
* Game Code
****/
// Game variables
var cyborg;
var cyborgDog;
var crystal;
var barrier;
var enemies = [];
var bullets = [];
var enemyBullets = [];
var gears = [];
var batteries = [];
var structures = [];
var gearCount = 0;
var waveNumber = 1;
var enemySpawnTimer = 0;
var enemiesPerWave = 5;
var waveTimer = 0;
var waveDelay = 300;
// Object pools for performance
var bulletPool = [];
var enemyBulletPool = [];
var gearPool = [];
var batteryPool = [];
var maxPoolSize = 50;
// Movement control variables
var movementButton;
var isMoving = false;
var movementDirection = {
x: 0,
y: 0
};
// UI elements
var healthText = new Text2('Health: 100', {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0);
healthText.x = 120;
healthText.y = 50;
LK.gui.topLeft.addChild(healthText);
var crystalHealthText = new Text2('Crystal: 500', {
size: 40,
fill: 0x00FFFF
});
crystalHealthText.anchor.set(0.5, 0);
crystalHealthText.x = 1024;
crystalHealthText.y = 50;
LK.gui.top.addChild(crystalHealthText);
var barrierText = new Text2('Barrier: 100', {
size: 40,
fill: 0x00FFFF
});
barrierText.anchor.set(1, 0);
barrierText.x = 1928;
barrierText.y = 50;
LK.gui.topRight.addChild(barrierText);
var gearsText = new Text2('Gears: 0', {
size: 40,
fill: 0xFFD700
});
gearsText.anchor.set(0, 0);
gearsText.x = 120;
gearsText.y = 100;
LK.gui.topLeft.addChild(gearsText);
var waveText = new Text2('Wave: 1', {
size: 40,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.x = 1024;
waveText.y = 100;
LK.gui.top.addChild(waveText);
// Initialize game objects
crystal = game.addChild(new Crystal());
crystal.x = 1024;
crystal.y = 1366;
barrier = game.addChild(new Barrier());
barrier.x = crystal.x;
barrier.y = crystal.y;
cyborg = game.addChild(new Cyborg());
cyborg.x = 1024;
cyborg.y = 1200;
// Create cyborg dog ally
cyborgDog = game.addChild(new CyborgDog());
cyborgDog.x = cyborg.x + 80;
cyborgDog.y = cyborg.y + 80;
// Create movement button
movementButton = new MovementButton();
movementButton.x = 180;
movementButton.y = 2400; // Moved up slightly for better thumb reach
movementButton.centerX = movementButton.x;
movementButton.centerY = movementButton.y;
LK.gui.bottomLeft.addChild(movementButton);
// Create map structures
createMapStructures();
// Event handlers
game.down = function (x, y, obj) {
cyborg.shoot(x, y);
};
game.move = function (x, y, obj) {
// Only handle tap-to-move if not using movement button
if (!isMoving) {
var dx = x - cyborg.x;
var dy = y - cyborg.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
// Calculate target position with bounds checking
var targetX = Math.max(40, Math.min(2008, x));
var targetY = Math.max(40, Math.min(2692, y));
// Check if target position would collide with structures
var tempX = cyborg.x;
var tempY = cyborg.y;
cyborg.x = targetX;
cyborg.y = targetY;
var wouldCollide = false;
for (var i = 0; i < structures.length; i++) {
if (cyborg.intersects(structures[i])) {
wouldCollide = true;
break;
}
}
cyborg.x = tempX;
cyborg.y = tempY;
if (!wouldCollide) {
// Stop any existing movement tween
tween.stop(cyborg, {
x: true,
y: true
});
// Create smooth movement tween with faster speed
tween(cyborg, {
x: targetX,
y: targetY
}, {
duration: distance * 8,
// Reduced from 15 to 8 for even faster movement
// Dynamic duration based on distance
easing: tween.easeOut,
onFinish: function onFinish() {
// Optional: Add a subtle bounce effect when reaching destination
tween(cyborg, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(cyborg, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
});
}
}
}
};
function createMapStructures() {
// Create trees
var treePositions = [{
x: 300,
y: 400
}, {
x: 1700,
y: 1100
}];
for (var i = 0; i < treePositions.length; i++) {
var tree = new Structure('tree');
tree.x = treePositions[i].x;
tree.y = treePositions[i].y;
structures.push(tree);
game.addChild(tree);
}
// Create destroyed cars
var carPositions = [{
x: 600,
y: 500
}, {
x: 1400,
y: 700
}];
for (var j = 0; j < carPositions.length; j++) {
var car = new Structure('destroyedCar');
car.x = carPositions[j].x;
car.y = carPositions[j].y;
structures.push(car);
game.addChild(car);
}
// Create rocks
var rockPositions = [{
x: 700,
y: 900
}, {
x: 1300,
y: 1200
}];
for (var k = 0; k < rockPositions.length; k++) {
var rock = new Structure('rock');
rock.x = rockPositions[k].x;
rock.y = rockPositions[k].y;
structures.push(rock);
game.addChild(rock);
}
}
function spawnEnemy() {
var enemy;
// Randomly choose enemy type based on wave number
var enemyType = Math.random();
if (enemyType < 0.4) {
enemy = new Enemy();
} else if (enemyType < 0.7) {
enemy = new FastEnemy();
} else if (enemyType < 0.9) {
enemy = new RangedEnemy();
} else {
enemy = new TankEnemy();
}
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -30;
break;
case 1:
// Right
enemy.x = 2078;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2762;
break;
case 3:
// Left
enemy.x = -30;
enemy.y = Math.random() * 2732;
break;
}
enemies.push(enemy);
game.addChild(enemy);
}
function checkStructureCollision(obj, newX, newY) {
var tempX = obj.x;
var tempY = obj.y;
obj.x = newX;
obj.y = newY;
for (var i = 0; i < structures.length; i++) {
if (obj.intersects(structures[i])) {
obj.x = tempX;
obj.y = tempY;
return true;
}
}
return false;
}
function getPooledBullet() {
if (bulletPool.length > 0) {
return bulletPool.pop();
}
return new Bullet();
}
function returnBulletToPool(bullet) {
if (bulletPool.length < maxPoolSize) {
bulletPool.push(bullet);
game.removeChild(bullet);
} else {
bullet.destroy();
}
}
function getPooledEnemyBullet() {
if (enemyBulletPool.length > 0) {
return enemyBulletPool.pop();
}
return new EnemyBullet();
}
function returnEnemyBulletToPool(bullet) {
if (enemyBulletPool.length < maxPoolSize) {
enemyBulletPool.push(bullet);
game.removeChild(bullet);
} else {
bullet.destroy();
}
}
function getPooledGear() {
if (gearPool.length > 0) {
return gearPool.pop();
}
return new Gear();
}
function returnGearToPool(gear) {
if (gearPool.length < maxPoolSize) {
gearPool.push(gear);
game.removeChild(gear);
} else {
gear.destroy();
}
}
function getPooledBattery() {
if (batteryPool.length > 0) {
return batteryPool.pop();
}
return new Battery();
}
function returnBatteryToPool(battery) {
if (batteryPool.length < maxPoolSize) {
batteryPool.push(battery);
game.removeChild(battery);
} else {
battery.destroy();
}
}
function startNewWave() {
waveNumber++;
waveText.setText('Wave: ' + waveNumber);
enemiesPerWave = Math.floor(5 + waveNumber * 1.5);
waveTimer = 0;
// Spawn enemies for this wave
for (var i = 0; i < enemiesPerWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 1000);
}
}
// Main game loop
game.update = function () {
// Update wave timer
waveTimer++;
// Check if wave is complete (no enemies left and enough time passed)
if (enemies.length === 0 && waveTimer > waveDelay) {
startNewWave();
}
// Update all bullets (optimized loop)
var i = bullets.length;
while (i--) {
bullets[i].update();
}
// Update all enemy bullets (optimized loop)
var m = enemyBullets.length;
while (m--) {
enemyBullets[m].update();
}
// Update all enemies (optimized loop)
var j = enemies.length;
while (j--) {
enemies[j].update();
}
// Update all gears (optimized loop)
var k = gears.length;
while (k--) {
gears[k].update();
}
// Update all batteries (optimized loop)
var l = batteries.length;
while (l--) {
batteries[l].update();
}
// Update cyborg
cyborg.update();
// Update cyborg dog
cyborgDog.update();
// Update barrier
barrier.update();
};
// Start first wave
LK.setTimeout(function () {
startNewWave();
}, 2000);
Un cibor visto desde arriba con una escopeta en las manos. In-Game asset. 2d. High contrast. No shadows
Cristal magico visto desde arriba. In-Game asset. 2d. High contrast. No shadows
Roca vista desde arriba. In-Game asset. 2d. High contrast. No shadows
Arbol visto desde arriba algo oscuro. In-Game asset. 2d. High contrast. No shadows
Coche destruido, algo retro visto desde arriba y en posición horizontal. In-Game asset. 2d. High contrast. No shadows
Perro ciborg visto desde arriba. In-Game asset. 2d. High contrast. No shadows