/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Crosshair class
var Crosshair = Container.expand(function () {
var self = Container.call(this);
var cross = self.attachAsset('crosshair', {
anchorX: 0.5,
anchorY: 0.5
});
cross.alpha = 0.5;
// Muzzle flash
var muzzle = self.attachAsset('muzzle', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
muzzle.visible = false;
// Show muzzle flash
self.flash = function () {
muzzle.visible = true;
muzzle.alpha = 1;
tween(muzzle, {
alpha: 0
}, {
duration: 120,
easing: tween.linear,
onFinish: function onFinish() {
muzzle.visible = false;
}
});
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Speed and direction
self.speed = 6;
self.dx = 0;
self.dy = 0;
self.radius = enemySprite.width * 0.5;
// For hit detection
self.isAlive = true;
// For wave/difficulty
self.wave = 1;
// Set movement vector towards center
self.setTarget = function (tx, ty) {
var dx = tx - self.x;
var dy = ty - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist === 0) {
self.dx = 0;
self.dy = self.speed;
} else {
self.dx = dx / dist * self.speed;
self.dy = dy / dist * self.speed;
}
};
// Called every tick
self.update = function () {
if (!self.isAlive) return;
self.x += self.dx;
self.y += self.dy;
};
// Flash when hit
self.flash = function () {
tween(self, {
alpha: 0.2
}, {
duration: 60,
easing: tween.linear,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120,
easing: tween.linear
});
}
});
};
// Destroy enemy
self.kill = function () {
self.isAlive = false;
self.visible = false;
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c1f
});
/****
* Game Code
****/
// Background music
// Miss sound
// Enemy destroyed sound
// Shot sound
// Muzzle flash: Ellipse, orange
// Crosshair: Box, yellow
// Enemy: Ellipse, red
// Game area
var GAME_W = 2048;
var GAME_H = 2732;
// Center point
var CENTER_X = GAME_W / 2;
var CENTER_Y = GAME_H / 2;
// Main character (village) at center
var village = LK.getAsset('character', {
anchorX: 0.5,
anchorY: 0.5,
x: CENTER_X,
y: CENTER_Y
});
game.addChild(village);
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Wave display
var waveTxt = new Text2('1. Dalga', {
size: 70,
fill: 0xFFE066
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
waveTxt.y = 120;
// Crosshair
var crosshair = new Crosshair();
game.addChild(crosshair);
crosshair.x = CENTER_X;
crosshair.y = CENTER_Y;
// Enemy array
var enemies = [];
// For drag/aim
var isAiming = false;
// Game state
var wave = 1;
var enemiesPerWave = 5;
var enemiesSpawned = 0;
var enemiesKilled = 0;
var waveInProgress = false;
var spawnTimer = null;
var nextWaveTimeout = null;
var gameOver = false;
// Difficulty scaling
function getEnemySpeed(wave) {
// Early waves are slow, then speed up more after wave 5
if (wave <= 3) {
return 5 + (wave - 1) * 0.8;
} else if (wave <= 7) {
return 7 + (wave - 4) * 1.5;
} else {
return 13 + (wave - 8) * 2.2;
}
}
function getEnemiesPerWave(wave) {
// Early waves: fewer enemies, then increase more rapidly
if (wave <= 2) {
return 4 + (wave - 1) * 1; // 4, 5
} else if (wave <= 5) {
return 6 + (wave - 3) * 2; // 6, 8, 10
} else if (wave <= 8) {
return 12 + (wave - 6) * 3; // 12, 15, 18
} else {
return 22 + (wave - 9) * 4; // 22, 26, 30, ...
}
}
// Spawn enemy at random edge, moving toward center
function spawnEnemy() {
var edge = Math.floor(Math.random() * 4); // 0:top, 1:right, 2:bottom, 3:left
var ex, ey;
var margin = 120;
if (edge === 0) {
// top
ex = margin + Math.random() * (GAME_W - 2 * margin);
ey = -100;
} else if (edge === 1) {
// right
ex = GAME_W + 100;
ey = margin + Math.random() * (GAME_H - 2 * margin);
} else if (edge === 2) {
// bottom
ex = margin + Math.random() * (GAME_W - 2 * margin);
ey = GAME_H + 100;
} else {
// left
ex = -100;
ey = margin + Math.random() * (GAME_H - 2 * margin);
}
var enemy = new Enemy();
enemy.x = ex;
enemy.y = ey;
enemy.speed = getEnemySpeed(wave);
enemy.wave = wave;
enemy.setTarget(village.x, village.y);
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
// Start a wave
function startWave() {
waveInProgress = true;
enemiesPerWave = getEnemiesPerWave(wave);
enemiesSpawned = 0;
enemiesKilled = 0;
waveTxt.setText(wave + ". Dalga");
// Calculate spawn interval: slow at first, then faster
var spawnInterval;
if (wave <= 3) {
spawnInterval = 700 - (wave - 1) * 100;
} else if (wave <= 7) {
spawnInterval = 400 - (wave - 4) * 40;
} else {
spawnInterval = 240 - (wave - 8) * 20;
}
spawnInterval = Math.max(100, spawnInterval);
spawnTimer = LK.setInterval(function () {
if (enemiesSpawned < enemiesPerWave) {
spawnEnemy();
}
if (enemiesSpawned >= enemiesPerWave) {
LK.clearInterval(spawnTimer);
}
}, spawnInterval);
}
// End wave, start next after delay
function endWave() {
waveInProgress = false;
nextWaveTimeout = LK.setTimeout(function () {
wave++;
startWave();
}, 1200);
}
// Reset game state
function resetGame() {
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].kill();
enemies.splice(i, 1);
}
wave = 1;
LK.setScore(0);
scoreTxt.setText('0');
waveTxt.setText('1. Dalga');
gameOver = false;
if (spawnTimer) LK.clearInterval(spawnTimer);
if (nextWaveTimeout) LK.clearTimeout(nextWaveTimeout);
startWave();
}
// Check if enemy reached center (game over)
function checkEnemyReachedCenter(enemy) {
// Use intersects to check if enemy reached the village
if (enemy.intersects(village)) {
return true;
}
return false;
}
// Check if point (x, y) hits enemy (circle hitbox)
function isHit(enemy, x, y) {
var dx = enemy.x - x;
var dy = enemy.y - y;
var r = enemy.radius * 0.95;
return dx * dx + dy * dy <= r * r;
}
// Handle shooting
function shootAt(x, y) {
if (gameOver) return;
crosshair.flash();
LK.getSound('shot').play();
// Find closest enemy under crosshair
var hitEnemy = null;
var minDist = 99999;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.isAlive) continue;
if (isHit(enemy, x, y)) {
var dx = enemy.x - x;
var dy = enemy.y - y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
hitEnemy = enemy;
}
}
}
if (hitEnemy) {
hitEnemy.flash();
hitEnemy.kill();
LK.getSound('enemyDown').play();
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
enemiesKilled++;
// Remove from array
for (var j = 0; j < enemies.length; j++) {
if (enemies[j] === hitEnemy) {
enemies.splice(j, 1);
break;
}
}
// If all enemies killed, end wave
if (enemiesKilled >= enemiesPerWave && enemiesSpawned >= enemiesPerWave) {
endWave();
}
} else {
LK.getSound('miss').play();
}
}
// Move crosshair with drag/tap
function handleMove(x, y, obj) {
if (gameOver) return;
// Clamp inside game area
var nx = Math.max(60, Math.min(GAME_W - 60, x));
var ny = Math.max(60, Math.min(GAME_H - 60, y));
crosshair.x = nx;
crosshair.y = ny;
}
game.move = handleMove;
// Touch down: aim and shoot
game.down = function (x, y, obj) {
if (gameOver) return;
isAiming = true;
handleMove(x, y, obj);
shootAt(crosshair.x, crosshair.y);
};
game.up = function (x, y, obj) {
isAiming = false;
};
// Main update loop
game.update = function () {
if (gameOver) return;
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
if (!enemy.isAlive) continue;
// Check if reached center
if (checkEnemyReachedCenter(enemy)) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 900);
LK.showGameOver();
return;
}
}
};
// Start music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 800
}
});
// Start game
resetGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Crosshair class
var Crosshair = Container.expand(function () {
var self = Container.call(this);
var cross = self.attachAsset('crosshair', {
anchorX: 0.5,
anchorY: 0.5
});
cross.alpha = 0.5;
// Muzzle flash
var muzzle = self.attachAsset('muzzle', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
muzzle.visible = false;
// Show muzzle flash
self.flash = function () {
muzzle.visible = true;
muzzle.alpha = 1;
tween(muzzle, {
alpha: 0
}, {
duration: 120,
easing: tween.linear,
onFinish: function onFinish() {
muzzle.visible = false;
}
});
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Speed and direction
self.speed = 6;
self.dx = 0;
self.dy = 0;
self.radius = enemySprite.width * 0.5;
// For hit detection
self.isAlive = true;
// For wave/difficulty
self.wave = 1;
// Set movement vector towards center
self.setTarget = function (tx, ty) {
var dx = tx - self.x;
var dy = ty - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist === 0) {
self.dx = 0;
self.dy = self.speed;
} else {
self.dx = dx / dist * self.speed;
self.dy = dy / dist * self.speed;
}
};
// Called every tick
self.update = function () {
if (!self.isAlive) return;
self.x += self.dx;
self.y += self.dy;
};
// Flash when hit
self.flash = function () {
tween(self, {
alpha: 0.2
}, {
duration: 60,
easing: tween.linear,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120,
easing: tween.linear
});
}
});
};
// Destroy enemy
self.kill = function () {
self.isAlive = false;
self.visible = false;
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c1f
});
/****
* Game Code
****/
// Background music
// Miss sound
// Enemy destroyed sound
// Shot sound
// Muzzle flash: Ellipse, orange
// Crosshair: Box, yellow
// Enemy: Ellipse, red
// Game area
var GAME_W = 2048;
var GAME_H = 2732;
// Center point
var CENTER_X = GAME_W / 2;
var CENTER_Y = GAME_H / 2;
// Main character (village) at center
var village = LK.getAsset('character', {
anchorX: 0.5,
anchorY: 0.5,
x: CENTER_X,
y: CENTER_Y
});
game.addChild(village);
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Wave display
var waveTxt = new Text2('1. Dalga', {
size: 70,
fill: 0xFFE066
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
waveTxt.y = 120;
// Crosshair
var crosshair = new Crosshair();
game.addChild(crosshair);
crosshair.x = CENTER_X;
crosshair.y = CENTER_Y;
// Enemy array
var enemies = [];
// For drag/aim
var isAiming = false;
// Game state
var wave = 1;
var enemiesPerWave = 5;
var enemiesSpawned = 0;
var enemiesKilled = 0;
var waveInProgress = false;
var spawnTimer = null;
var nextWaveTimeout = null;
var gameOver = false;
// Difficulty scaling
function getEnemySpeed(wave) {
// Early waves are slow, then speed up more after wave 5
if (wave <= 3) {
return 5 + (wave - 1) * 0.8;
} else if (wave <= 7) {
return 7 + (wave - 4) * 1.5;
} else {
return 13 + (wave - 8) * 2.2;
}
}
function getEnemiesPerWave(wave) {
// Early waves: fewer enemies, then increase more rapidly
if (wave <= 2) {
return 4 + (wave - 1) * 1; // 4, 5
} else if (wave <= 5) {
return 6 + (wave - 3) * 2; // 6, 8, 10
} else if (wave <= 8) {
return 12 + (wave - 6) * 3; // 12, 15, 18
} else {
return 22 + (wave - 9) * 4; // 22, 26, 30, ...
}
}
// Spawn enemy at random edge, moving toward center
function spawnEnemy() {
var edge = Math.floor(Math.random() * 4); // 0:top, 1:right, 2:bottom, 3:left
var ex, ey;
var margin = 120;
if (edge === 0) {
// top
ex = margin + Math.random() * (GAME_W - 2 * margin);
ey = -100;
} else if (edge === 1) {
// right
ex = GAME_W + 100;
ey = margin + Math.random() * (GAME_H - 2 * margin);
} else if (edge === 2) {
// bottom
ex = margin + Math.random() * (GAME_W - 2 * margin);
ey = GAME_H + 100;
} else {
// left
ex = -100;
ey = margin + Math.random() * (GAME_H - 2 * margin);
}
var enemy = new Enemy();
enemy.x = ex;
enemy.y = ey;
enemy.speed = getEnemySpeed(wave);
enemy.wave = wave;
enemy.setTarget(village.x, village.y);
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
// Start a wave
function startWave() {
waveInProgress = true;
enemiesPerWave = getEnemiesPerWave(wave);
enemiesSpawned = 0;
enemiesKilled = 0;
waveTxt.setText(wave + ". Dalga");
// Calculate spawn interval: slow at first, then faster
var spawnInterval;
if (wave <= 3) {
spawnInterval = 700 - (wave - 1) * 100;
} else if (wave <= 7) {
spawnInterval = 400 - (wave - 4) * 40;
} else {
spawnInterval = 240 - (wave - 8) * 20;
}
spawnInterval = Math.max(100, spawnInterval);
spawnTimer = LK.setInterval(function () {
if (enemiesSpawned < enemiesPerWave) {
spawnEnemy();
}
if (enemiesSpawned >= enemiesPerWave) {
LK.clearInterval(spawnTimer);
}
}, spawnInterval);
}
// End wave, start next after delay
function endWave() {
waveInProgress = false;
nextWaveTimeout = LK.setTimeout(function () {
wave++;
startWave();
}, 1200);
}
// Reset game state
function resetGame() {
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].kill();
enemies.splice(i, 1);
}
wave = 1;
LK.setScore(0);
scoreTxt.setText('0');
waveTxt.setText('1. Dalga');
gameOver = false;
if (spawnTimer) LK.clearInterval(spawnTimer);
if (nextWaveTimeout) LK.clearTimeout(nextWaveTimeout);
startWave();
}
// Check if enemy reached center (game over)
function checkEnemyReachedCenter(enemy) {
// Use intersects to check if enemy reached the village
if (enemy.intersects(village)) {
return true;
}
return false;
}
// Check if point (x, y) hits enemy (circle hitbox)
function isHit(enemy, x, y) {
var dx = enemy.x - x;
var dy = enemy.y - y;
var r = enemy.radius * 0.95;
return dx * dx + dy * dy <= r * r;
}
// Handle shooting
function shootAt(x, y) {
if (gameOver) return;
crosshair.flash();
LK.getSound('shot').play();
// Find closest enemy under crosshair
var hitEnemy = null;
var minDist = 99999;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.isAlive) continue;
if (isHit(enemy, x, y)) {
var dx = enemy.x - x;
var dy = enemy.y - y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
hitEnemy = enemy;
}
}
}
if (hitEnemy) {
hitEnemy.flash();
hitEnemy.kill();
LK.getSound('enemyDown').play();
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
enemiesKilled++;
// Remove from array
for (var j = 0; j < enemies.length; j++) {
if (enemies[j] === hitEnemy) {
enemies.splice(j, 1);
break;
}
}
// If all enemies killed, end wave
if (enemiesKilled >= enemiesPerWave && enemiesSpawned >= enemiesPerWave) {
endWave();
}
} else {
LK.getSound('miss').play();
}
}
// Move crosshair with drag/tap
function handleMove(x, y, obj) {
if (gameOver) return;
// Clamp inside game area
var nx = Math.max(60, Math.min(GAME_W - 60, x));
var ny = Math.max(60, Math.min(GAME_H - 60, y));
crosshair.x = nx;
crosshair.y = ny;
}
game.move = handleMove;
// Touch down: aim and shoot
game.down = function (x, y, obj) {
if (gameOver) return;
isAiming = true;
handleMove(x, y, obj);
shootAt(crosshair.x, crosshair.y);
};
game.up = function (x, y, obj) {
isAiming = false;
};
// Main update loop
game.update = function () {
if (gameOver) return;
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
if (!enemy.isAlive) continue;
// Check if reached center
if (checkEnemyReachedCenter(enemy)) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 900);
LK.showGameOver();
return;
}
}
};
// Start music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 800
}
});
// Start game
resetGame();