/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy2 = Container.expand(function () {
var self = Container.call(this);
// Random size between 80 and 175
var randomSize = 80 + Math.random() * (175 - 80);
var enemyGraphics = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
width: randomSize,
height: randomSize
});
self.lifeTime = 0;
self.maxLifeTime = 3000; // 3 seconds in milliseconds
self.isAlive = true;
self.update = function () {
if (!self.isAlive) return;
self.lifeTime += 16.67; // Approximately 60 FPS
// Flash purple as target gets closer to expiring
var timeLeft = self.maxLifeTime - self.lifeTime;
if (timeLeft < 1000) {
var alpha = 0.5 + 0.5 * Math.sin(self.lifeTime * 0.01);
enemyGraphics.alpha = alpha;
}
// Target expires
if (self.lifeTime >= self.maxLifeTime) {
self.expire();
}
};
self.expire = function () {
if (!self.isAlive) return;
self.isAlive = false;
playerHealth -= 25;
updateHealthDisplay();
LK.getSound('miss').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
// Check game over
if (playerHealth <= 0) {
LK.showGameOver();
}
self.destroy();
};
self.hit = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Create blood splash effect
createBloodSplash(self.x, self.y);
// Calculate score based on reaction time
var points = 15; // Default 15 points for 3 seconds
if (self.lifeTime <= 1000) {
points = 25; // 25 points for hitting within 1 second
} else if (self.lifeTime <= 2000) {
points = 20; // 20 points for hitting within 2 seconds
}
// Increase score
LK.setScore(LK.getScore() + points);
updateScoreDisplay();
// Play hit sound
LK.getSound('hit').play();
// Hit effect
tween(enemyGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
};
self.down = function (x, y, obj) {
self.hit();
};
return self;
});
var Enemy3 = Container.expand(function () {
var self = Container.call(this);
// Random size between 80 and 175
var randomSize = 80 + Math.random() * (175 - 80);
var enemyGraphics = self.attachAsset('enemy3', {
anchorX: 0.5,
anchorY: 0.5,
width: randomSize,
height: randomSize
});
self.lifeTime = 0;
self.maxLifeTime = 3000; // 3 seconds in milliseconds
self.isAlive = true;
self.update = function () {
if (!self.isAlive) return;
self.lifeTime += 16.67; // Approximately 60 FPS
// Flash green as target gets closer to expiring
var timeLeft = self.maxLifeTime - self.lifeTime;
if (timeLeft < 1000) {
var alpha = 0.5 + 0.5 * Math.sin(self.lifeTime * 0.01);
enemyGraphics.alpha = alpha;
}
// Target expires
if (self.lifeTime >= self.maxLifeTime) {
self.expire();
}
};
self.expire = function () {
if (!self.isAlive) return;
self.isAlive = false;
playerHealth -= 25;
updateHealthDisplay();
LK.getSound('miss').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
// Check game over
if (playerHealth <= 0) {
LK.showGameOver();
}
self.destroy();
};
self.hit = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Create blood splash effect
createBloodSplash(self.x, self.y);
// Calculate score based on reaction time
var points = 15; // Default 15 points for 3 seconds
if (self.lifeTime <= 1000) {
points = 25; // 25 points for hitting within 1 second
} else if (self.lifeTime <= 2000) {
points = 20; // 20 points for hitting within 2 seconds
}
// Increase score
LK.setScore(LK.getScore() + points);
updateScoreDisplay();
// Play hit sound
LK.getSound('hit').play();
// Hit effect
tween(enemyGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
};
self.down = function (x, y, obj) {
self.hit();
};
return self;
});
var Hostage = Container.expand(function () {
var self = Container.call(this);
// Random size between 80 and 175
var randomSize = 80 + Math.random() * (175 - 80);
var hostageGraphics = self.attachAsset('hostage', {
anchorX: 0.5,
anchorY: 0.5,
width: randomSize,
height: randomSize
});
self.lifeTime = 0;
self.maxLifeTime = 3000; // 3 seconds in milliseconds
self.isAlive = true;
self.update = function () {
if (!self.isAlive) return;
self.lifeTime += 16.67; // Approximately 60 FPS
// Flash blue as hostage gets closer to expiring
var timeLeft = self.maxLifeTime - self.lifeTime;
if (timeLeft < 1000) {
var alpha = 0.5 + 0.5 * Math.sin(self.lifeTime * 0.01);
hostageGraphics.alpha = alpha;
}
// Hostage expires
if (self.lifeTime >= self.maxLifeTime) {
self.expire();
}
};
self.expire = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Hostage survived - reward player
LK.setScore(LK.getScore() + 100);
updateScoreDisplay();
playerHealth += 25;
if (playerHealth > 100) playerHealth = 100; // Cap at 100
updateHealthDisplay();
// Flash screen green to show rewards
LK.effects.flashScreen(0x00ff00, 500);
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
self.destroy();
};
self.hit = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Hostage hit - lose 25 health
playerHealth -= 25;
updateHealthDisplay();
// Play miss sound
LK.getSound('miss').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Hit effect
tween(hostageGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
// Check game over
if (playerHealth <= 0) {
LK.showGameOver();
}
};
self.down = function (x, y, obj) {
self.hit();
};
return self;
});
var Target = Container.expand(function () {
var self = Container.call(this);
// Random size between 80 and 175
var randomSize = 80 + Math.random() * (175 - 80);
var targetGraphics = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
width: randomSize,
height: randomSize
});
self.lifeTime = 0;
self.maxLifeTime = 3000; // 3 seconds in milliseconds
self.isAlive = true;
self.update = function () {
if (!self.isAlive) return;
self.lifeTime += 16.67; // Approximately 60 FPS
// Flash red as target gets closer to expiring
var timeLeft = self.maxLifeTime - self.lifeTime;
if (timeLeft < 1000) {
var alpha = 0.5 + 0.5 * Math.sin(self.lifeTime * 0.01);
targetGraphics.alpha = alpha;
}
// Target expires
if (self.lifeTime >= self.maxLifeTime) {
self.expire();
}
};
self.expire = function () {
if (!self.isAlive) return;
self.isAlive = false;
playerHealth -= 25;
updateHealthDisplay();
LK.getSound('miss').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
// Check game over
if (playerHealth <= 0) {
LK.showGameOver();
}
self.destroy();
};
self.hit = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Create blood splash effect
createBloodSplash(self.x, self.y);
// Calculate score based on reaction time
var points = 15; // Default 15 points for 3 seconds
if (self.lifeTime <= 1000) {
points = 25; // 25 points for hitting within 1 second
} else if (self.lifeTime <= 2000) {
points = 20; // 20 points for hitting within 2 seconds
}
// Increase score
LK.setScore(LK.getScore() + points);
updateScoreDisplay();
// Play hit sound
LK.getSound('hit').play();
// Hit effect
tween(targetGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
};
self.down = function (x, y, obj) {
self.hit();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFFFFF
});
/****
* Game Code
****/
var playerHealth = 100;
var targets = [];
var spawnTimer = 0;
var baseSpawnRate = 120; // Base spawn rate in ticks (2 seconds at 60fps)
var currentSpawnRate = baseSpawnRate;
var maxTargetCount = 3; // Start with maximum 3 targets, increases every 1000 points
var hostageCount = 0; // Track number of hostages to spawn based on score
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFF0000
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var healthText = new Text2('Health: 100', {
size: 60,
fill: 0x00FF00
});
healthText.anchor.set(0.5, 0);
healthText.y = 80;
LK.gui.top.addChild(healthText);
// Background toggle button
var isBackgroundWhite = true;
var toggleButton = new Text2('◐', {
size: 80,
fill: 0x000000
});
toggleButton.anchor.set(1, 0);
toggleButton.x = -20;
toggleButton.y = 20;
LK.gui.topRight.addChild(toggleButton);
toggleButton.down = function (x, y, obj) {
if (isBackgroundWhite) {
game.setBackgroundColor(0x000000);
toggleButton.fill = 0xFFFFFF;
isBackgroundWhite = false;
} else {
game.setBackgroundColor(0xFFFFFF);
toggleButton.fill = 0x000000;
isBackgroundWhite = true;
}
};
// Crosshair
var crosshair = game.addChild(LK.getAsset('crosshair', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
crosshair.x = 1024;
crosshair.y = 1366;
function updateScoreDisplay() {
scoreText.setText('Score: ' + LK.getScore());
// Increase max target count every 1000 points
maxTargetCount = Math.min(5, Math.floor(LK.getScore() / 1000) + 3);
}
function updateHealthDisplay() {
healthText.setText('Health: ' + playerHealth);
// Change color based on health
if (playerHealth > 50) {
healthText.fill = 0x00ff00;
} else if (playerHealth > 25) {
healthText.fill = 0xffff00;
} else {
healthText.fill = 0xff0000;
}
}
function createBloodSplash(x, y) {
for (var i = 0; i < 5; i++) {
var splash = game.addChild(LK.getAsset('bloodSplash', {
anchorX: 0.5,
anchorY: 0.5
}));
splash.x = x + (Math.random() - 0.5) * 50;
splash.y = y + (Math.random() - 0.5) * 50;
splash.alpha = 0.8;
// Animate blood splash
tween(splash, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
splash.destroy();
}
});
}
}
function spawnTarget() {
// Only spawn if no targets are currently on screen
if (targets.length > 0) return;
// Calculate hostage count based on score (1 hostage per 1000 points)
hostageCount = Math.floor(LK.getScore() / 1000);
// Calculate minimum enemy count based on score
var minEnemyCount = 3; // Default minimum 3 enemies
if (LK.getScore() >= 1000) {
minEnemyCount = 3 + Math.floor((LK.getScore() - 1000) / 1000); // +1 enemy per 1000 points after 1000
}
// Spawn minEnemyCount to maxTargetCount random targets
var targetCount = Math.max(minEnemyCount, Math.floor(Math.random() * maxTargetCount) + 1); // At least minEnemyCount targets
var hostagesToSpawn = Math.min(1, Math.min(hostageCount, 1)); // Maximum 1 hostage at a time, independent of target count
var hostagesSpawned = 0;
for (var i = 0; i < targetCount; i++) {
var target;
// After 1000 points, include hostages and new enemy types
if (LK.getScore() >= 1000) {
// Force spawn hostages first if we need them
if (hostagesSpawned < hostagesToSpawn) {
target = new Hostage();
hostagesSpawned++;
} else {
// Spawn other enemy types
var targetType = Math.random();
if (targetType < 0.5) {
// 50% chance for Enemy2
target = new Enemy2();
} else if (targetType < 1.0) {
// 50% chance for Enemy3
target = new Enemy3();
}
}
} else {
target = new Target();
}
// Random position with margin from edges
var margin = 100;
var attempts = 0;
var validPosition = false;
// Try to find a position that doesn't overlap with existing targets
while (!validPosition && attempts < 20) {
target.x = margin + Math.random() * (2048 - 2 * margin);
target.y = margin + Math.random() * (2732 - 2 * margin);
validPosition = true;
attempts++;
// Check distance from other targets
for (var j = 0; j < targets.length; j++) {
var distance = Math.sqrt(Math.pow(target.x - targets[j].x, 2) + Math.pow(target.y - targets[j].y, 2));
// Calculate minimum distance based on both target sizes
var currentTargetSize = target.children[0].width || 80;
var existingTargetSize = targets[j].children[0].width || 80;
var minDistance = (currentTargetSize + existingTargetSize) / 2 + 20; // Add 20px padding
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
targets.push(target);
game.addChild(target);
// Spawn animation
target.scaleX = 0;
target.scaleY = 0;
tween(target, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
}
function calculateSpawnRate() {
// Increase spawn rate based on score
var score = LK.getScore();
var speedIncrease = Math.floor(score / 5) * 10; // Every 5 points, reduce spawn time by 10 ticks
currentSpawnRate = Math.max(30, baseSpawnRate - speedIncrease); // Minimum 0.5 seconds
}
game.move = function (x, y, obj) {
// Update crosshair position
crosshair.x = x;
crosshair.y = y;
};
game.update = function () {
spawnTimer++;
// Calculate current spawn rate
calculateSpawnRate();
// Spawn new targets
if (spawnTimer >= currentSpawnRate) {
spawnTarget();
spawnTimer = 0;
// Add some randomness to spawn timing
spawnTimer -= Math.random() * 20;
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy2 = Container.expand(function () {
var self = Container.call(this);
// Random size between 80 and 175
var randomSize = 80 + Math.random() * (175 - 80);
var enemyGraphics = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
width: randomSize,
height: randomSize
});
self.lifeTime = 0;
self.maxLifeTime = 3000; // 3 seconds in milliseconds
self.isAlive = true;
self.update = function () {
if (!self.isAlive) return;
self.lifeTime += 16.67; // Approximately 60 FPS
// Flash purple as target gets closer to expiring
var timeLeft = self.maxLifeTime - self.lifeTime;
if (timeLeft < 1000) {
var alpha = 0.5 + 0.5 * Math.sin(self.lifeTime * 0.01);
enemyGraphics.alpha = alpha;
}
// Target expires
if (self.lifeTime >= self.maxLifeTime) {
self.expire();
}
};
self.expire = function () {
if (!self.isAlive) return;
self.isAlive = false;
playerHealth -= 25;
updateHealthDisplay();
LK.getSound('miss').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
// Check game over
if (playerHealth <= 0) {
LK.showGameOver();
}
self.destroy();
};
self.hit = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Create blood splash effect
createBloodSplash(self.x, self.y);
// Calculate score based on reaction time
var points = 15; // Default 15 points for 3 seconds
if (self.lifeTime <= 1000) {
points = 25; // 25 points for hitting within 1 second
} else if (self.lifeTime <= 2000) {
points = 20; // 20 points for hitting within 2 seconds
}
// Increase score
LK.setScore(LK.getScore() + points);
updateScoreDisplay();
// Play hit sound
LK.getSound('hit').play();
// Hit effect
tween(enemyGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
};
self.down = function (x, y, obj) {
self.hit();
};
return self;
});
var Enemy3 = Container.expand(function () {
var self = Container.call(this);
// Random size between 80 and 175
var randomSize = 80 + Math.random() * (175 - 80);
var enemyGraphics = self.attachAsset('enemy3', {
anchorX: 0.5,
anchorY: 0.5,
width: randomSize,
height: randomSize
});
self.lifeTime = 0;
self.maxLifeTime = 3000; // 3 seconds in milliseconds
self.isAlive = true;
self.update = function () {
if (!self.isAlive) return;
self.lifeTime += 16.67; // Approximately 60 FPS
// Flash green as target gets closer to expiring
var timeLeft = self.maxLifeTime - self.lifeTime;
if (timeLeft < 1000) {
var alpha = 0.5 + 0.5 * Math.sin(self.lifeTime * 0.01);
enemyGraphics.alpha = alpha;
}
// Target expires
if (self.lifeTime >= self.maxLifeTime) {
self.expire();
}
};
self.expire = function () {
if (!self.isAlive) return;
self.isAlive = false;
playerHealth -= 25;
updateHealthDisplay();
LK.getSound('miss').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
// Check game over
if (playerHealth <= 0) {
LK.showGameOver();
}
self.destroy();
};
self.hit = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Create blood splash effect
createBloodSplash(self.x, self.y);
// Calculate score based on reaction time
var points = 15; // Default 15 points for 3 seconds
if (self.lifeTime <= 1000) {
points = 25; // 25 points for hitting within 1 second
} else if (self.lifeTime <= 2000) {
points = 20; // 20 points for hitting within 2 seconds
}
// Increase score
LK.setScore(LK.getScore() + points);
updateScoreDisplay();
// Play hit sound
LK.getSound('hit').play();
// Hit effect
tween(enemyGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
};
self.down = function (x, y, obj) {
self.hit();
};
return self;
});
var Hostage = Container.expand(function () {
var self = Container.call(this);
// Random size between 80 and 175
var randomSize = 80 + Math.random() * (175 - 80);
var hostageGraphics = self.attachAsset('hostage', {
anchorX: 0.5,
anchorY: 0.5,
width: randomSize,
height: randomSize
});
self.lifeTime = 0;
self.maxLifeTime = 3000; // 3 seconds in milliseconds
self.isAlive = true;
self.update = function () {
if (!self.isAlive) return;
self.lifeTime += 16.67; // Approximately 60 FPS
// Flash blue as hostage gets closer to expiring
var timeLeft = self.maxLifeTime - self.lifeTime;
if (timeLeft < 1000) {
var alpha = 0.5 + 0.5 * Math.sin(self.lifeTime * 0.01);
hostageGraphics.alpha = alpha;
}
// Hostage expires
if (self.lifeTime >= self.maxLifeTime) {
self.expire();
}
};
self.expire = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Hostage survived - reward player
LK.setScore(LK.getScore() + 100);
updateScoreDisplay();
playerHealth += 25;
if (playerHealth > 100) playerHealth = 100; // Cap at 100
updateHealthDisplay();
// Flash screen green to show rewards
LK.effects.flashScreen(0x00ff00, 500);
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
self.destroy();
};
self.hit = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Hostage hit - lose 25 health
playerHealth -= 25;
updateHealthDisplay();
// Play miss sound
LK.getSound('miss').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Hit effect
tween(hostageGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
// Check game over
if (playerHealth <= 0) {
LK.showGameOver();
}
};
self.down = function (x, y, obj) {
self.hit();
};
return self;
});
var Target = Container.expand(function () {
var self = Container.call(this);
// Random size between 80 and 175
var randomSize = 80 + Math.random() * (175 - 80);
var targetGraphics = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
width: randomSize,
height: randomSize
});
self.lifeTime = 0;
self.maxLifeTime = 3000; // 3 seconds in milliseconds
self.isAlive = true;
self.update = function () {
if (!self.isAlive) return;
self.lifeTime += 16.67; // Approximately 60 FPS
// Flash red as target gets closer to expiring
var timeLeft = self.maxLifeTime - self.lifeTime;
if (timeLeft < 1000) {
var alpha = 0.5 + 0.5 * Math.sin(self.lifeTime * 0.01);
targetGraphics.alpha = alpha;
}
// Target expires
if (self.lifeTime >= self.maxLifeTime) {
self.expire();
}
};
self.expire = function () {
if (!self.isAlive) return;
self.isAlive = false;
playerHealth -= 25;
updateHealthDisplay();
LK.getSound('miss').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
// Check game over
if (playerHealth <= 0) {
LK.showGameOver();
}
self.destroy();
};
self.hit = function () {
if (!self.isAlive) return;
self.isAlive = false;
// Create blood splash effect
createBloodSplash(self.x, self.y);
// Calculate score based on reaction time
var points = 15; // Default 15 points for 3 seconds
if (self.lifeTime <= 1000) {
points = 25; // 25 points for hitting within 1 second
} else if (self.lifeTime <= 2000) {
points = 20; // 20 points for hitting within 2 seconds
}
// Increase score
LK.setScore(LK.getScore() + points);
updateScoreDisplay();
// Play hit sound
LK.getSound('hit').play();
// Hit effect
tween(targetGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from targets array
for (var i = targets.length - 1; i >= 0; i--) {
if (targets[i] === self) {
targets.splice(i, 1);
break;
}
}
// Check if all targets are cleared and spawn new ones
if (targets.length === 0) {
spawnTimer = 0; // Reset spawn timer to spawn immediately
}
};
self.down = function (x, y, obj) {
self.hit();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFFFFF
});
/****
* Game Code
****/
var playerHealth = 100;
var targets = [];
var spawnTimer = 0;
var baseSpawnRate = 120; // Base spawn rate in ticks (2 seconds at 60fps)
var currentSpawnRate = baseSpawnRate;
var maxTargetCount = 3; // Start with maximum 3 targets, increases every 1000 points
var hostageCount = 0; // Track number of hostages to spawn based on score
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFF0000
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var healthText = new Text2('Health: 100', {
size: 60,
fill: 0x00FF00
});
healthText.anchor.set(0.5, 0);
healthText.y = 80;
LK.gui.top.addChild(healthText);
// Background toggle button
var isBackgroundWhite = true;
var toggleButton = new Text2('◐', {
size: 80,
fill: 0x000000
});
toggleButton.anchor.set(1, 0);
toggleButton.x = -20;
toggleButton.y = 20;
LK.gui.topRight.addChild(toggleButton);
toggleButton.down = function (x, y, obj) {
if (isBackgroundWhite) {
game.setBackgroundColor(0x000000);
toggleButton.fill = 0xFFFFFF;
isBackgroundWhite = false;
} else {
game.setBackgroundColor(0xFFFFFF);
toggleButton.fill = 0x000000;
isBackgroundWhite = true;
}
};
// Crosshair
var crosshair = game.addChild(LK.getAsset('crosshair', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
crosshair.x = 1024;
crosshair.y = 1366;
function updateScoreDisplay() {
scoreText.setText('Score: ' + LK.getScore());
// Increase max target count every 1000 points
maxTargetCount = Math.min(5, Math.floor(LK.getScore() / 1000) + 3);
}
function updateHealthDisplay() {
healthText.setText('Health: ' + playerHealth);
// Change color based on health
if (playerHealth > 50) {
healthText.fill = 0x00ff00;
} else if (playerHealth > 25) {
healthText.fill = 0xffff00;
} else {
healthText.fill = 0xff0000;
}
}
function createBloodSplash(x, y) {
for (var i = 0; i < 5; i++) {
var splash = game.addChild(LK.getAsset('bloodSplash', {
anchorX: 0.5,
anchorY: 0.5
}));
splash.x = x + (Math.random() - 0.5) * 50;
splash.y = y + (Math.random() - 0.5) * 50;
splash.alpha = 0.8;
// Animate blood splash
tween(splash, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
splash.destroy();
}
});
}
}
function spawnTarget() {
// Only spawn if no targets are currently on screen
if (targets.length > 0) return;
// Calculate hostage count based on score (1 hostage per 1000 points)
hostageCount = Math.floor(LK.getScore() / 1000);
// Calculate minimum enemy count based on score
var minEnemyCount = 3; // Default minimum 3 enemies
if (LK.getScore() >= 1000) {
minEnemyCount = 3 + Math.floor((LK.getScore() - 1000) / 1000); // +1 enemy per 1000 points after 1000
}
// Spawn minEnemyCount to maxTargetCount random targets
var targetCount = Math.max(minEnemyCount, Math.floor(Math.random() * maxTargetCount) + 1); // At least minEnemyCount targets
var hostagesToSpawn = Math.min(1, Math.min(hostageCount, 1)); // Maximum 1 hostage at a time, independent of target count
var hostagesSpawned = 0;
for (var i = 0; i < targetCount; i++) {
var target;
// After 1000 points, include hostages and new enemy types
if (LK.getScore() >= 1000) {
// Force spawn hostages first if we need them
if (hostagesSpawned < hostagesToSpawn) {
target = new Hostage();
hostagesSpawned++;
} else {
// Spawn other enemy types
var targetType = Math.random();
if (targetType < 0.5) {
// 50% chance for Enemy2
target = new Enemy2();
} else if (targetType < 1.0) {
// 50% chance for Enemy3
target = new Enemy3();
}
}
} else {
target = new Target();
}
// Random position with margin from edges
var margin = 100;
var attempts = 0;
var validPosition = false;
// Try to find a position that doesn't overlap with existing targets
while (!validPosition && attempts < 20) {
target.x = margin + Math.random() * (2048 - 2 * margin);
target.y = margin + Math.random() * (2732 - 2 * margin);
validPosition = true;
attempts++;
// Check distance from other targets
for (var j = 0; j < targets.length; j++) {
var distance = Math.sqrt(Math.pow(target.x - targets[j].x, 2) + Math.pow(target.y - targets[j].y, 2));
// Calculate minimum distance based on both target sizes
var currentTargetSize = target.children[0].width || 80;
var existingTargetSize = targets[j].children[0].width || 80;
var minDistance = (currentTargetSize + existingTargetSize) / 2 + 20; // Add 20px padding
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
targets.push(target);
game.addChild(target);
// Spawn animation
target.scaleX = 0;
target.scaleY = 0;
tween(target, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
}
function calculateSpawnRate() {
// Increase spawn rate based on score
var score = LK.getScore();
var speedIncrease = Math.floor(score / 5) * 10; // Every 5 points, reduce spawn time by 10 ticks
currentSpawnRate = Math.max(30, baseSpawnRate - speedIncrease); // Minimum 0.5 seconds
}
game.move = function (x, y, obj) {
// Update crosshair position
crosshair.x = x;
crosshair.y = y;
};
game.update = function () {
spawnTimer++;
// Calculate current spawn rate
calculateSpawnRate();
// Spawn new targets
if (spawnTimer >= currentSpawnRate) {
spawnTarget();
spawnTimer = 0;
// Add some randomness to spawn timing
spawnTimer -= Math.random() * 20;
}
};