/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Player Brawler class
var Brawler = Container.expand(function () {
var self = Container.call(this);
// Attach brawler asset (box, blue)
var brawlerAsset = self.attachAsset('brawler', {
anchorX: 0.5,
anchorY: 0.5
});
// Attach a gun image asset (anchored at left-middle)
var gunAsset = self.attachAsset('gun', {
anchorX: 0.0,
anchorY: 0.5
});
gunAsset.x = 60; // position gun to the right of brawler
gunAsset.y = 0;
self.gunAsset = gunAsset;
// Movement speed (pixels per tick)
self.speed = 16;
// Direction vector for movement (set by drag)
self.moveDir = {
x: 0,
y: 0
};
// Shooting cooldown (ticks)
self.shootCooldown = 0;
// Health
self.maxHealth = 5;
self.health = self.maxHealth;
// For hit flash
self.isHit = false;
self.hitTimer = 0;
// Update method called every tick
self.update = function () {
// Move
if (self.moveDir.x !== 0 || self.moveDir.y !== 0) {
var mag = Math.sqrt(self.moveDir.x * self.moveDir.x + self.moveDir.y * self.moveDir.y);
if (mag > 0) {
var dx = self.moveDir.x / mag * self.speed;
var dy = self.moveDir.y / mag * self.speed;
self.x += dx;
self.y += dy;
}
}
// Clamp to arena bounds (with margin)
var margin = 100;
if (self.x < margin) self.x = margin;
if (self.x > 2048 - margin) self.x = 2048 - margin;
if (self.y < margin) self.y = margin;
if (self.y > 2732 - margin) self.y = 2732 - margin;
// Rotate gun to aim at nearest enemy
if (typeof enemies !== "undefined" && enemies.length > 0 && self.gunAsset) {
var nearest = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var dx = enemies[i].x - self.x;
var dy = enemies[i].y - self.y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
nearest = enemies[i];
}
}
if (nearest) {
var dx = nearest.x - self.x;
var dy = nearest.y - self.y;
self.gunAsset.rotation = Math.atan2(dy, dx);
}
}
// Shooting cooldown
if (self.shootCooldown > 0) self.shootCooldown--;
// Hit flash
if (self.isHit) {
self.hitTimer--;
if (self.hitTimer <= 0) {
self.isHit = false;
brawlerAsset.tint = 0x3498db;
}
}
};
// Take damage
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health < 0) self.health = 0;
// Flash red
brawlerAsset.tint = 0xff4444;
self.isHit = true;
self.hitTimer = 12;
};
// Heal
self.heal = function (amount) {
self.health += amount;
if (self.health > self.maxHealth) self.health = self.maxHealth;
};
// Reset tint
brawlerAsset.tint = 0x3498db;
return self;
});
// Bullet class (player's bullet)
var Bullet = Container.expand(function () {
var self = Container.call(this);
// Attach bullet asset (box, yellow)
var bulletAsset = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
// Direction vector
self.dir = {
x: 0,
y: 0
};
// Speed
self.speed = 40;
// Lifetime (ticks)
self.lifetime = 60;
// Set initial rotation to match direction (default 0)
self.setRotationFromDir = function () {
// Only set if direction is not zero
if (self.dir.x !== 0 || self.dir.y !== 0) {
self.rotation = Math.atan2(self.dir.y, self.dir.x);
}
};
// Update method
self.update = function () {
// Ensure rotation matches direction
self.setRotationFromDir();
self.x += self.dir.x * self.speed;
self.y += self.dir.y * self.speed;
self.lifetime--;
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Attach enemy asset (ellipse, red)
var enemyAsset = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Movement speed
self.speed = 9; // Enemy speed fixed at 9
// Health
self.maxHealth = 2;
self.health = self.maxHealth;
// Target (the player)
self.target = null;
// For hit flash
self.isHit = false;
self.hitTimer = 0;
// Update method
self.update = function () {
if (self.target) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var mag = Math.sqrt(dx * dx + dy * dy);
if (mag > 0) {
self.x += dx / mag * self.speed;
self.y += dy / mag * self.speed;
}
}
// Clamp to arena bounds
var margin = 80;
if (self.x < margin) self.x = margin;
if (self.x > 2048 - margin) self.x = 2048 - margin;
if (self.y < margin) self.y = margin;
if (self.y > 2732 - margin) self.y = 2732 - margin;
// Hit flash
if (self.isHit) {
self.hitTimer--;
if (self.hitTimer <= 0) {
self.isHit = false;
enemyAsset.tint = 0xd83318;
}
}
};
// Take damage
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health < 0) self.health = 0;
// Flash yellow
enemyAsset.tint = 0xffe066;
self.isHit = true;
self.hitTimer = 8;
};
// Reset tint
enemyAsset.tint = 0xd83318;
return self;
});
// Gem class (collectible)
var Gem = Container.expand(function () {
var self = Container.call(this);
// Attach gem asset (ellipse, green)
var gemAsset = self.attachAsset('gem', {
anchorX: 0.5,
anchorY: 0.5
});
// For floating animation
self.floatDir = Math.random() > 0.5 ? 1 : -1;
self.floatTick = Math.floor(Math.random() * 60);
self.update = function () {
self.floatTick++;
self.y += Math.sin(self.floatTick / 10) * 0.8 * self.floatDir;
};
return self;
});
// PowerUp class (heal or speed)
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Type: 'heal' or 'speed'
self.type = 'heal';
// Attach asset (box, purple for speed, pink for heal)
var color = self.type === 'speed' ? 0x9b59b6 : 0xff69b4;
var powerAsset = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
powerAsset.tint = color;
// For floating animation
self.floatTick = Math.floor(Math.random() * 60);
self.update = function () {
self.floatTick++;
self.y += Math.sin(self.floatTick / 12) * 0.7;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222a36
});
/****
* Game Code
****/
// Arena bounds (for spawning)
// gun image asset
var arenaMargin = 120;
var arenaWidth = 2048 - 2 * arenaMargin;
var arenaHeight = 2732 - 2 * arenaMargin;
// Main game objects
var player;
var enemies = [];
var bullets = [];
var gems = [];
var powerups = [];
// Score and UI
var score = 0;
var gemGoal = 25;
var healthTxt, scoreTxt;
// Drag control
var dragging = false;
var dragStart = {
x: 0,
y: 0
};
var dragCurrent = {
x: 0,
y: 0
};
// Last tick for enemy spawn
var lastEnemySpawnTick = 0;
var enemySpawnInterval = 120; // ticks
// Last tick for powerup spawn
var lastPowerupSpawnTick = 0;
var powerupSpawnInterval = 600; // ticks
// --- UI Setup ---
// Score text (gems)
scoreTxt = new Text2('Gems: 0/' + gemGoal, {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Health text
healthTxt = new Text2('♥♥♥♥♥', {
size: 90,
fill: 0xFF4444
});
healthTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(healthTxt);
healthTxt.y = 100;
// --- Asset Initialization (shapes) ---
// --- Game Setup ---
// Spawn player in center
player = new Brawler();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// Initial enemies
function spawnEnemy() {
var enemy = new Enemy();
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
enemy.x = arenaMargin + Math.random() * arenaWidth;
enemy.y = arenaMargin;
} else if (edge === 1) {
// bottom
enemy.x = arenaMargin + Math.random() * arenaWidth;
enemy.y = 2732 - arenaMargin;
} else if (edge === 2) {
// left
enemy.x = arenaMargin;
enemy.y = arenaMargin + Math.random() * arenaHeight;
} else {
// right
enemy.x = 2048 - arenaMargin;
enemy.y = arenaMargin + Math.random() * arenaHeight;
}
enemy.target = player;
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn a gem at random position
function spawnGem() {
var gem = new Gem();
gem.x = arenaMargin + 100 + Math.random() * (arenaWidth - 200);
gem.y = arenaMargin + 100 + Math.random() * (arenaHeight - 200);
gems.push(gem);
game.addChild(gem);
}
// Spawn a powerup at random position
function spawnPowerup() {
var power = new PowerUp();
// Randomly choose type
power.type = Math.random() > 0.5 ? 'heal' : 'speed';
// Set color
var color = power.type === 'speed' ? 0x9b59b6 : 0xff69b4;
power.children[0].tint = color;
power.x = arenaMargin + 100 + Math.random() * (arenaWidth - 200);
power.y = arenaMargin + 100 + Math.random() * (arenaHeight - 200);
powerups.push(power);
game.addChild(power);
}
// Initial spawn
for (var i = 0; i < 5; i++) spawnEnemy();
for (var i = 0; i < 3; i++) spawnGem();
// --- UI Update ---
function updateUI() {
// Health
var hearts = '';
for (var i = 0; i < player.health; i++) hearts += '♥';
for (var i = player.health; i < player.maxHealth; i++) hearts += '♡';
healthTxt.setText(hearts);
// Score
scoreTxt.setText('Gems: ' + score + '/' + gemGoal);
}
// --- Drag Controls ---
// Player can be dragged directly to move
// Helper to track dragging state
var dragging = false;
var dragOffsetX = 0;
var dragOffsetY = 0;
// Handle drag start: if touch/click is on player, start dragging
game.down = function (x, y, obj) {
// Check if touch/click is on player
// Use bounding box for hit test
var px = player.x;
var py = player.y;
var pw = player.width || 120;
var ph = player.height || 120;
if (x >= px - pw / 2 && x <= px + pw / 2 && y >= py - ph / 2 && y <= py + ph / 2) {
dragging = true;
dragOffsetX = x - player.x;
dragOffsetY = y - player.y;
}
};
// Handle drag move: move player if dragging
game.move = function (x, y, obj) {
if (!dragging) return;
player.x = x - dragOffsetX;
player.y = y - dragOffsetY;
// Clamp to arena bounds (with margin)
var margin = 100;
if (player.x < margin) player.x = margin;
if (player.x > 2048 - margin) player.x = 2048 - margin;
if (player.y < margin) player.y = margin;
if (player.y > 2732 - margin) player.y = 2732 - margin;
// Stop any velocity-based movement
player.moveDir.x = 0;
player.moveDir.y = 0;
};
// Handle drag end: stop dragging
game.up = function (x, y, obj) {
dragging = false;
player.moveDir.x = 0;
player.moveDir.y = 0;
};
// --- Main Game Loop ---
game.update = function () {
// Update player
player.update();
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Check collision with player
if (enemy.intersects(player)) {
// Damage player
player.takeDamage(1);
// Flash screen
LK.effects.flashScreen(0xff0000, 300);
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
// Check for game over
if (player.health <= 0) {
LK.showGameOver();
return;
}
continue;
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
// Remove if out of bounds or expired
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732 || bullet.lifetime <= 0) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with enemies
var hit = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
enemy.takeDamage(1);
if (enemy.health <= 0) {
// Remove enemy
enemy.destroy();
enemies.splice(j, 1);
// 50% chance to spawn a gem at enemy position
if (Math.random() < 0.5) {
var gem = new Gem();
gem.x = enemy.x;
gem.y = enemy.y;
gems.push(gem);
game.addChild(gem);
}
}
hit = true;
break;
}
}
if (hit) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
}
// Update gems
for (var i = gems.length - 1; i >= 0; i--) {
var gem = gems[i];
gem.update();
// Collect if player touches
if (gem.intersects(player)) {
score++;
gem.destroy();
gems.splice(i, 1);
updateUI();
// Win condition
if (score >= gemGoal) {
LK.showYouWin();
return;
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var power = powerups[i];
power.update();
// Collect if player touches
if (power.intersects(player)) {
if (power.type === 'heal') {
player.heal(2);
} else if (power.type === 'speed') {
// Temporary speed boost
player.speed = 28;
// Tween back to normal speed after 2 seconds
tween(player, {
speed: 16
}, {
duration: 2000,
easing: tween.linear
});
}
power.destroy();
powerups.splice(i, 1);
updateUI();
}
}
// --- Shooting (auto-aim at nearest enemy) ---
if (player.shootCooldown <= 0 && enemies.length > 0) {
// Find nearest enemy
var nearest = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var dx = enemies[i].x - player.x;
var dy = enemies[i].y - player.y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
nearest = enemies[i];
}
}
if (nearest) {
// Fire bullet toward nearest enemy
var dx = nearest.x - player.x;
var dy = nearest.y - player.y;
var mag = Math.sqrt(dx * dx + dy * dy);
if (mag > 0) {
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.dir.x = dx / mag;
bullet.dir.y = dy / mag;
bullet.setRotationFromDir(); // Set rotation to match direction
bullets.push(bullet);
game.addChild(bullet);
player.shootCooldown = 10; // faster shooting (~6 shots per second)
}
}
}
// --- Enemy Spawning ---
if (enemies.length < 5) {
spawnEnemy();
}
if (LK.ticks - lastEnemySpawnTick > enemySpawnInterval) {
spawnEnemy();
lastEnemySpawnTick = LK.ticks;
// Gradually decrease spawn interval
if (enemySpawnInterval > 40) enemySpawnInterval -= 2;
}
// --- Gem Spawning ---
if (gems.length < 3) {
spawnGem();
}
// --- Powerup Spawning ---
if (LK.ticks - lastPowerupSpawnTick > powerupSpawnInterval) {
spawnPowerup();
lastPowerupSpawnTick = LK.ticks;
}
// --- UI ---
updateUI();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Player Brawler class
var Brawler = Container.expand(function () {
var self = Container.call(this);
// Attach brawler asset (box, blue)
var brawlerAsset = self.attachAsset('brawler', {
anchorX: 0.5,
anchorY: 0.5
});
// Attach a gun image asset (anchored at left-middle)
var gunAsset = self.attachAsset('gun', {
anchorX: 0.0,
anchorY: 0.5
});
gunAsset.x = 60; // position gun to the right of brawler
gunAsset.y = 0;
self.gunAsset = gunAsset;
// Movement speed (pixels per tick)
self.speed = 16;
// Direction vector for movement (set by drag)
self.moveDir = {
x: 0,
y: 0
};
// Shooting cooldown (ticks)
self.shootCooldown = 0;
// Health
self.maxHealth = 5;
self.health = self.maxHealth;
// For hit flash
self.isHit = false;
self.hitTimer = 0;
// Update method called every tick
self.update = function () {
// Move
if (self.moveDir.x !== 0 || self.moveDir.y !== 0) {
var mag = Math.sqrt(self.moveDir.x * self.moveDir.x + self.moveDir.y * self.moveDir.y);
if (mag > 0) {
var dx = self.moveDir.x / mag * self.speed;
var dy = self.moveDir.y / mag * self.speed;
self.x += dx;
self.y += dy;
}
}
// Clamp to arena bounds (with margin)
var margin = 100;
if (self.x < margin) self.x = margin;
if (self.x > 2048 - margin) self.x = 2048 - margin;
if (self.y < margin) self.y = margin;
if (self.y > 2732 - margin) self.y = 2732 - margin;
// Rotate gun to aim at nearest enemy
if (typeof enemies !== "undefined" && enemies.length > 0 && self.gunAsset) {
var nearest = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var dx = enemies[i].x - self.x;
var dy = enemies[i].y - self.y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
nearest = enemies[i];
}
}
if (nearest) {
var dx = nearest.x - self.x;
var dy = nearest.y - self.y;
self.gunAsset.rotation = Math.atan2(dy, dx);
}
}
// Shooting cooldown
if (self.shootCooldown > 0) self.shootCooldown--;
// Hit flash
if (self.isHit) {
self.hitTimer--;
if (self.hitTimer <= 0) {
self.isHit = false;
brawlerAsset.tint = 0x3498db;
}
}
};
// Take damage
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health < 0) self.health = 0;
// Flash red
brawlerAsset.tint = 0xff4444;
self.isHit = true;
self.hitTimer = 12;
};
// Heal
self.heal = function (amount) {
self.health += amount;
if (self.health > self.maxHealth) self.health = self.maxHealth;
};
// Reset tint
brawlerAsset.tint = 0x3498db;
return self;
});
// Bullet class (player's bullet)
var Bullet = Container.expand(function () {
var self = Container.call(this);
// Attach bullet asset (box, yellow)
var bulletAsset = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
// Direction vector
self.dir = {
x: 0,
y: 0
};
// Speed
self.speed = 40;
// Lifetime (ticks)
self.lifetime = 60;
// Set initial rotation to match direction (default 0)
self.setRotationFromDir = function () {
// Only set if direction is not zero
if (self.dir.x !== 0 || self.dir.y !== 0) {
self.rotation = Math.atan2(self.dir.y, self.dir.x);
}
};
// Update method
self.update = function () {
// Ensure rotation matches direction
self.setRotationFromDir();
self.x += self.dir.x * self.speed;
self.y += self.dir.y * self.speed;
self.lifetime--;
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Attach enemy asset (ellipse, red)
var enemyAsset = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Movement speed
self.speed = 9; // Enemy speed fixed at 9
// Health
self.maxHealth = 2;
self.health = self.maxHealth;
// Target (the player)
self.target = null;
// For hit flash
self.isHit = false;
self.hitTimer = 0;
// Update method
self.update = function () {
if (self.target) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var mag = Math.sqrt(dx * dx + dy * dy);
if (mag > 0) {
self.x += dx / mag * self.speed;
self.y += dy / mag * self.speed;
}
}
// Clamp to arena bounds
var margin = 80;
if (self.x < margin) self.x = margin;
if (self.x > 2048 - margin) self.x = 2048 - margin;
if (self.y < margin) self.y = margin;
if (self.y > 2732 - margin) self.y = 2732 - margin;
// Hit flash
if (self.isHit) {
self.hitTimer--;
if (self.hitTimer <= 0) {
self.isHit = false;
enemyAsset.tint = 0xd83318;
}
}
};
// Take damage
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health < 0) self.health = 0;
// Flash yellow
enemyAsset.tint = 0xffe066;
self.isHit = true;
self.hitTimer = 8;
};
// Reset tint
enemyAsset.tint = 0xd83318;
return self;
});
// Gem class (collectible)
var Gem = Container.expand(function () {
var self = Container.call(this);
// Attach gem asset (ellipse, green)
var gemAsset = self.attachAsset('gem', {
anchorX: 0.5,
anchorY: 0.5
});
// For floating animation
self.floatDir = Math.random() > 0.5 ? 1 : -1;
self.floatTick = Math.floor(Math.random() * 60);
self.update = function () {
self.floatTick++;
self.y += Math.sin(self.floatTick / 10) * 0.8 * self.floatDir;
};
return self;
});
// PowerUp class (heal or speed)
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Type: 'heal' or 'speed'
self.type = 'heal';
// Attach asset (box, purple for speed, pink for heal)
var color = self.type === 'speed' ? 0x9b59b6 : 0xff69b4;
var powerAsset = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
powerAsset.tint = color;
// For floating animation
self.floatTick = Math.floor(Math.random() * 60);
self.update = function () {
self.floatTick++;
self.y += Math.sin(self.floatTick / 12) * 0.7;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222a36
});
/****
* Game Code
****/
// Arena bounds (for spawning)
// gun image asset
var arenaMargin = 120;
var arenaWidth = 2048 - 2 * arenaMargin;
var arenaHeight = 2732 - 2 * arenaMargin;
// Main game objects
var player;
var enemies = [];
var bullets = [];
var gems = [];
var powerups = [];
// Score and UI
var score = 0;
var gemGoal = 25;
var healthTxt, scoreTxt;
// Drag control
var dragging = false;
var dragStart = {
x: 0,
y: 0
};
var dragCurrent = {
x: 0,
y: 0
};
// Last tick for enemy spawn
var lastEnemySpawnTick = 0;
var enemySpawnInterval = 120; // ticks
// Last tick for powerup spawn
var lastPowerupSpawnTick = 0;
var powerupSpawnInterval = 600; // ticks
// --- UI Setup ---
// Score text (gems)
scoreTxt = new Text2('Gems: 0/' + gemGoal, {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Health text
healthTxt = new Text2('♥♥♥♥♥', {
size: 90,
fill: 0xFF4444
});
healthTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(healthTxt);
healthTxt.y = 100;
// --- Asset Initialization (shapes) ---
// --- Game Setup ---
// Spawn player in center
player = new Brawler();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// Initial enemies
function spawnEnemy() {
var enemy = new Enemy();
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
enemy.x = arenaMargin + Math.random() * arenaWidth;
enemy.y = arenaMargin;
} else if (edge === 1) {
// bottom
enemy.x = arenaMargin + Math.random() * arenaWidth;
enemy.y = 2732 - arenaMargin;
} else if (edge === 2) {
// left
enemy.x = arenaMargin;
enemy.y = arenaMargin + Math.random() * arenaHeight;
} else {
// right
enemy.x = 2048 - arenaMargin;
enemy.y = arenaMargin + Math.random() * arenaHeight;
}
enemy.target = player;
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn a gem at random position
function spawnGem() {
var gem = new Gem();
gem.x = arenaMargin + 100 + Math.random() * (arenaWidth - 200);
gem.y = arenaMargin + 100 + Math.random() * (arenaHeight - 200);
gems.push(gem);
game.addChild(gem);
}
// Spawn a powerup at random position
function spawnPowerup() {
var power = new PowerUp();
// Randomly choose type
power.type = Math.random() > 0.5 ? 'heal' : 'speed';
// Set color
var color = power.type === 'speed' ? 0x9b59b6 : 0xff69b4;
power.children[0].tint = color;
power.x = arenaMargin + 100 + Math.random() * (arenaWidth - 200);
power.y = arenaMargin + 100 + Math.random() * (arenaHeight - 200);
powerups.push(power);
game.addChild(power);
}
// Initial spawn
for (var i = 0; i < 5; i++) spawnEnemy();
for (var i = 0; i < 3; i++) spawnGem();
// --- UI Update ---
function updateUI() {
// Health
var hearts = '';
for (var i = 0; i < player.health; i++) hearts += '♥';
for (var i = player.health; i < player.maxHealth; i++) hearts += '♡';
healthTxt.setText(hearts);
// Score
scoreTxt.setText('Gems: ' + score + '/' + gemGoal);
}
// --- Drag Controls ---
// Player can be dragged directly to move
// Helper to track dragging state
var dragging = false;
var dragOffsetX = 0;
var dragOffsetY = 0;
// Handle drag start: if touch/click is on player, start dragging
game.down = function (x, y, obj) {
// Check if touch/click is on player
// Use bounding box for hit test
var px = player.x;
var py = player.y;
var pw = player.width || 120;
var ph = player.height || 120;
if (x >= px - pw / 2 && x <= px + pw / 2 && y >= py - ph / 2 && y <= py + ph / 2) {
dragging = true;
dragOffsetX = x - player.x;
dragOffsetY = y - player.y;
}
};
// Handle drag move: move player if dragging
game.move = function (x, y, obj) {
if (!dragging) return;
player.x = x - dragOffsetX;
player.y = y - dragOffsetY;
// Clamp to arena bounds (with margin)
var margin = 100;
if (player.x < margin) player.x = margin;
if (player.x > 2048 - margin) player.x = 2048 - margin;
if (player.y < margin) player.y = margin;
if (player.y > 2732 - margin) player.y = 2732 - margin;
// Stop any velocity-based movement
player.moveDir.x = 0;
player.moveDir.y = 0;
};
// Handle drag end: stop dragging
game.up = function (x, y, obj) {
dragging = false;
player.moveDir.x = 0;
player.moveDir.y = 0;
};
// --- Main Game Loop ---
game.update = function () {
// Update player
player.update();
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Check collision with player
if (enemy.intersects(player)) {
// Damage player
player.takeDamage(1);
// Flash screen
LK.effects.flashScreen(0xff0000, 300);
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
// Check for game over
if (player.health <= 0) {
LK.showGameOver();
return;
}
continue;
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
// Remove if out of bounds or expired
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732 || bullet.lifetime <= 0) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with enemies
var hit = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
enemy.takeDamage(1);
if (enemy.health <= 0) {
// Remove enemy
enemy.destroy();
enemies.splice(j, 1);
// 50% chance to spawn a gem at enemy position
if (Math.random() < 0.5) {
var gem = new Gem();
gem.x = enemy.x;
gem.y = enemy.y;
gems.push(gem);
game.addChild(gem);
}
}
hit = true;
break;
}
}
if (hit) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
}
// Update gems
for (var i = gems.length - 1; i >= 0; i--) {
var gem = gems[i];
gem.update();
// Collect if player touches
if (gem.intersects(player)) {
score++;
gem.destroy();
gems.splice(i, 1);
updateUI();
// Win condition
if (score >= gemGoal) {
LK.showYouWin();
return;
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var power = powerups[i];
power.update();
// Collect if player touches
if (power.intersects(player)) {
if (power.type === 'heal') {
player.heal(2);
} else if (power.type === 'speed') {
// Temporary speed boost
player.speed = 28;
// Tween back to normal speed after 2 seconds
tween(player, {
speed: 16
}, {
duration: 2000,
easing: tween.linear
});
}
power.destroy();
powerups.splice(i, 1);
updateUI();
}
}
// --- Shooting (auto-aim at nearest enemy) ---
if (player.shootCooldown <= 0 && enemies.length > 0) {
// Find nearest enemy
var nearest = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var dx = enemies[i].x - player.x;
var dy = enemies[i].y - player.y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
nearest = enemies[i];
}
}
if (nearest) {
// Fire bullet toward nearest enemy
var dx = nearest.x - player.x;
var dy = nearest.y - player.y;
var mag = Math.sqrt(dx * dx + dy * dy);
if (mag > 0) {
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.dir.x = dx / mag;
bullet.dir.y = dy / mag;
bullet.setRotationFromDir(); // Set rotation to match direction
bullets.push(bullet);
game.addChild(bullet);
player.shootCooldown = 10; // faster shooting (~6 shots per second)
}
}
}
// --- Enemy Spawning ---
if (enemies.length < 5) {
spawnEnemy();
}
if (LK.ticks - lastEnemySpawnTick > enemySpawnInterval) {
spawnEnemy();
lastEnemySpawnTick = LK.ticks;
// Gradually decrease spawn interval
if (enemySpawnInterval > 40) enemySpawnInterval -= 2;
}
// --- Gem Spawning ---
if (gems.length < 3) {
spawnGem();
}
// --- Powerup Spawning ---
if (LK.ticks - lastPowerupSpawnTick > powerupSpawnInterval) {
spawnPowerup();
lastPowerupSpawnTick = LK.ticks;
}
// --- UI ---
updateUI();
};
gem. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
zombie pixel no text. In-Game asset. 2d. High contrast. No shadows
human head. In-Game asset. 2d. High contrast. No shadows
purple cristal. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
gun pistol . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
bullet . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat