/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // AreaAttack class (visual only, handles hit in game code) var AreaAttack = Container.expand(function () { var self = Container.call(this); var areaGfx = self.attachAsset('areaAttack', { anchorX: 0.5, anchorY: 0.5 }); areaGfx.alpha = 0.4; self.update = function () {}; return self; }); // Boss class (player) var Boss = Container.expand(function () { var self = Container.call(this); var bossGfx = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bossGfx.width * 0.5; self.cooldowns = { area: 0, shield: 0, trap: 0 }; self.shieldActive = false; self.shieldTimer = 0; self.update = function () { // Cooldown timers if (self.cooldowns.area > 0) self.cooldowns.area--; if (self.cooldowns.shield > 0) self.cooldowns.shield--; if (self.cooldowns.trap > 0) self.cooldowns.trap--; if (self.shieldActive) { self.shieldTimer--; if (self.shieldTimer <= 0) { self.shieldActive = false; if (self.shieldObj) { self.shieldObj.destroy(); self.shieldObj = null; } } } }; return self; }); // Hero class (basic, blue, straight movement) var Hero = Container.expand(function () { var self = Container.call(this); var heroGfx = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6 + Math.random() * 2; self.hp = 1; self.type = 1; self.update = function () { // Move toward core var dx = core.x - self.x; var dy = core.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } }; return self; }); // Hero2 class (green, zigzag movement) var Hero2 = Container.expand(function () { var self = Container.call(this); var heroGfx = self.attachAsset('hero2', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5 + Math.random() * 2; self.hp = 2; self.type = 2; self.zigzagAngle = Math.random() * Math.PI * 2; self.update = function () { // Zigzag toward core var dx = core.x - self.x; var dy = core.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { var angle = Math.atan2(dy, dx); // Add zigzag self.zigzagAngle += 0.15; var zigzag = Math.sin(self.zigzagAngle) * 0.5; angle += zigzag; self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } }; return self; }); // Hero3 class (red, fast, pauses randomly) var Hero3 = Container.expand(function () { var self = Container.call(this); var heroGfx = self.attachAsset('hero3', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8 + Math.random() * 2; self.hp = 1; self.type = 3; self.pauseTimer = 0; self.update = function () { if (self.pauseTimer > 0) { self.pauseTimer--; return; } // Randomly pause if (Math.random() < 0.01) { self.pauseTimer = 30 + Math.floor(Math.random() * 30); return; } // Move toward core var dx = core.x - self.x; var dy = core.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } }; return self; }); // Shield class (visual only) var Shield = Container.expand(function () { var self = Container.call(this); var shieldGfx = self.attachAsset('shield', { anchorX: 0.5, anchorY: 0.5 }); shieldGfx.alpha = 0.25; self.update = function () {}; return self; }); // Trap class (placed by boss, destroys hero on contact) var Trap = Container.expand(function () { var self = Container.call(this); var trapGfx = self.attachAsset('trap', { anchorX: 0.5, anchorY: 0.5 }); self.timer = 240; // 4 seconds self.update = function () { self.timer--; if (self.timer <= 0) { self.destroy(); for (var i = 0; i < traps.length; ++i) { if (traps[i] === self) { traps.splice(i, 1); break; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Add dungeon background image as a changeable background var dungeonBg = LK.getAsset('dungeon_bg_001', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(dungeonBg); // Center positions // Boss (player) asset: large ellipse, purple // Core asset: smaller ellipse, gold // Hero asset: rectangle, blue // Hero2 asset: rectangle, green (for variety) // Hero3 asset: rectangle, red (for variety) // Area attack asset: large semi-transparent ellipse, orange // Shield asset: large semi-transparent ellipse, cyan // Trap asset: small ellipse, dark gray // Sound for area attack var centerX = 2048 / 2; var centerY = 2732 / 2; // Core removed: no core asset or object in the middle // Instead, define a virtual core position for heroes to target var core = { x: centerX, y: centerY + 500 }; // Boss (player) var boss = new Boss(); boss.x = centerX; boss.y = centerY + 900; game.addChild(boss); // Boss health var BOSS_MAX_HP = 10; var bossHp = BOSS_MAX_HP; // Boss life bar (simple rectangle) var bossHpBarBg = LK.getAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); bossHpBarBg.width = 400; bossHpBarBg.height = 40; bossHpBarBg.tint = 0x222222; bossHpBarBg.alpha = 0.7; bossHpBarBg.x = boss.x; bossHpBarBg.y = boss.y - boss.radius - 60; game.addChild(bossHpBarBg); var bossHpBar = LK.getAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); bossHpBar.width = 390; bossHpBar.height = 28; bossHpBar.tint = 0x43e97b; bossHpBar.x = boss.x; bossHpBar.y = boss.y - boss.radius - 60; game.addChild(bossHpBar); // Score display var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Arrays for game objects var heroes = []; var traps = []; var areaAttacks = []; // Dragging boss var dragNode = null; // Ability state var abilityMode = null; // null, 'area', 'shield', 'trap' var areaAttackObj = null; // Ability cooldowns (frames) var AREA_COOLDOWN = 360; // 6s var SHIELD_COOLDOWN = 540; // 9s var TRAP_COOLDOWN = 180; // 3s // Ability durations var SHIELD_DURATION = 180; // 3s // Area attack radius var AREA_RADIUS = 300; // Shield radius var SHIELD_RADIUS = 200; // Trap radius var TRAP_RADIUS = 60; // --- Ability Icon Buttons (replace text buttons) --- // Area Attack Icon Button var areaBtnIcon = LK.getAsset('areaAttack', { anchorX: 0.5, anchorY: 0.5 }); areaBtnIcon.width = 160; areaBtnIcon.height = 160; LK.gui.bottom.addChild(areaBtnIcon); areaBtnIcon.x = -400; areaBtnIcon.y = -100; // Shield Icon Button var shieldBtnIcon = LK.getAsset('shield', { anchorX: 0.5, anchorY: 0.5 }); shieldBtnIcon.width = 160; shieldBtnIcon.height = 160; LK.gui.bottom.addChild(shieldBtnIcon); shieldBtnIcon.x = 0; shieldBtnIcon.y = -100; // Trap Icon Button var trapBtnIcon = LK.getAsset('trap', { anchorX: 0.5, anchorY: 0.5 }); trapBtnIcon.width = 160; trapBtnIcon.height = 160; LK.gui.bottom.addChild(trapBtnIcon); trapBtnIcon.x = 400; trapBtnIcon.y = -100; // Add a semi-transparent overlay for cooldown effect for each button function makeCooldownOverlay(width, height) { var overlay = LK.getAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); overlay.width = width; overlay.height = height; overlay.tint = 0x222222; overlay.alpha = 0.6; return overlay; } var areaBtnOverlay = makeCooldownOverlay(160, 160); var shieldBtnOverlay = makeCooldownOverlay(160, 160); var trapBtnOverlay = makeCooldownOverlay(160, 160); LK.gui.bottom.addChild(areaBtnOverlay); LK.gui.bottom.addChild(shieldBtnOverlay); LK.gui.bottom.addChild(trapBtnOverlay); areaBtnOverlay.x = areaBtnIcon.x; areaBtnOverlay.y = areaBtnIcon.y; shieldBtnOverlay.x = shieldBtnIcon.x; shieldBtnOverlay.y = shieldBtnIcon.y; trapBtnOverlay.x = trapBtnIcon.x; trapBtnOverlay.y = trapBtnIcon.y; // Add a small text label under each icon var areaBtnLabel = new Text2('Area', { size: 48, fill: "#fff" }); areaBtnLabel.anchor.set(0.5, 0); LK.gui.bottom.addChild(areaBtnLabel); areaBtnLabel.x = areaBtnIcon.x; areaBtnLabel.y = areaBtnIcon.y + 90; var shieldBtnLabel = new Text2('Shield', { size: 48, fill: "#fff" }); shieldBtnLabel.anchor.set(0.5, 0); LK.gui.bottom.addChild(shieldBtnLabel); shieldBtnLabel.x = shieldBtnIcon.x; shieldBtnLabel.y = shieldBtnIcon.y + 90; var trapBtnLabel = new Text2('Trap', { size: 48, fill: "#fff" }); trapBtnLabel.anchor.set(0.5, 0); LK.gui.bottom.addChild(trapBtnLabel); trapBtnLabel.x = trapBtnIcon.x; trapBtnLabel.y = trapBtnIcon.y + 90; // Ability button handlers (icon down events) areaBtnIcon.down = function (x, y, obj) { useAreaAttack(); }; shieldBtnIcon.down = function (x, y, obj) { useShield(); }; trapBtnIcon.down = function (x, y, obj) { useTrap(); }; // Helper: check if two objects intersect (circle-based) function circlesIntersect(a, rA, b, rB) { var dx = a.x - b.x; var dy = a.y - b.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < rA + rB; } // Helper: spawn a hero at random edge, and make them stronger as more spawn var heroSpawnCount = 0; function spawnHero() { var edge = Math.floor(Math.random() * 4); var x, y; if (edge === 0) { // Top x = 200 + Math.random() * (2048 - 400); y = -100; } else if (edge === 1) { // Bottom x = 200 + Math.random() * (2048 - 400); y = 2732 + 100; } else if (edge === 2) { // Left x = -100; y = 400 + Math.random() * (2732 - 800); } else { // Right x = 2048 + 100; y = 400 + Math.random() * (2732 - 800); } // Only spawn one hero at a time (no batch spawn) // Make each new hero a little stronger heroSpawnCount++; var t = Math.random(); var hero; if (t < 0.5) { hero = new Hero(); } else if (t < 0.8) { hero = new Hero2(); } else { hero = new Hero3(); } // Increase hero stats as spawn count increases var hpBoost = Math.floor(heroSpawnCount / 10); var speedBoost = Math.min(heroSpawnCount * 0.05, 3); hero.hp += hpBoost; hero.speed += speedBoost; hero.x = x; hero.y = y; hero.lastIntersectingCore = false; hero.lastIntersectingBoss = false; hero.lastIntersectingTrap = false; heroes.push(hero); game.addChild(hero); } // Ability: Area attack function useAreaAttack() { if (boss.cooldowns.area > 0) return; boss.cooldowns.area = AREA_COOLDOWN; // Visual areaAttackObj = new AreaAttack(); areaAttackObj.x = boss.x; areaAttackObj.y = boss.y; areaAttackObj.scaleX = 0.1; areaAttackObj.scaleY = 0.1; areaAttackObj.alpha = 0.5; game.addChild(areaAttackObj); areaAttacks.push(areaAttackObj); // Animate tween(areaAttackObj, { scaleX: 1, scaleY: 1, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { if (areaAttackObj) { areaAttackObj.destroy(); var idx = areaAttacks.indexOf(areaAttackObj); if (idx !== -1) areaAttacks.splice(idx, 1); areaAttackObj = null; } } }); // Hit all heroes in radius for (var i = heroes.length - 1; i >= 0; --i) { var h = heroes[i]; if (circlesIntersect(boss, AREA_RADIUS, h, 60)) { h.hp -= 1; LK.getSound('area').play(); LK.effects.flashObject(h, 0xff9800, 300); if (h.hp <= 0) { LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); LK.getSound('heroHit').play(); h.destroy(); heroes.splice(i, 1); } } } } // Ability: Shield function useShield() { if (boss.cooldowns.shield > 0) return; boss.cooldowns.shield = SHIELD_COOLDOWN; boss.shieldActive = true; boss.shieldTimer = SHIELD_DURATION; if (boss.shieldObj) boss.shieldObj.destroy(); boss.shieldObj = new Shield(); boss.shieldObj.x = boss.x; boss.shieldObj.y = boss.y; game.addChild(boss.shieldObj); LK.getSound('shield').play(); } // Ability: Trap function useTrap() { if (boss.cooldowns.trap > 0) return; boss.cooldowns.trap = TRAP_COOLDOWN; // Place trap at boss position var trap = new Trap(); trap.x = boss.x; trap.y = boss.y; traps.push(trap); game.addChild(trap); LK.getSound('trap').play(); } // Ability button handlers (handled by icon event handlers above) // Dragging boss function handleMove(x, y, obj) { if (dragNode) { // Clamp boss inside game area var minX = boss.radius + 40; var maxX = 2048 - boss.radius - 40; var minY = boss.radius + 200; var maxY = 2732 - boss.radius - 40; boss.x = Math.max(minX, Math.min(maxX, x)); boss.y = Math.max(minY, Math.min(maxY, y)); if (boss.shieldObj) { boss.shieldObj.x = boss.x; boss.shieldObj.y = boss.y; } } } game.move = handleMove; game.down = function (x, y, obj) { // Only drag if touch or mouse is on boss var dx = x - boss.x; var dy = y - boss.y; if (dx * dx + dy * dy < boss.radius * boss.radius) { dragNode = boss; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // Main game update game.update = function () { // Update boss boss.update(); // Update boss life bar position bossHpBarBg.x = boss.x; bossHpBarBg.y = boss.y - boss.radius - 60; bossHpBar.x = boss.x; bossHpBar.y = boss.y - boss.radius - 60; // Update ability icon overlays to show cooldown visually function updateAbilityOverlay(overlay, cooldown, maxCooldown) { if (cooldown <= 0) { overlay.alpha = 0; } else { // Show overlay, alpha increases as cooldown is higher overlay.alpha = 0.6; // Visually show cooldown as a darken effect (simulate by scaling Y) var frac = Math.max(0, Math.min(1, cooldown / maxCooldown)); overlay.scaleY = frac; overlay.y = overlay._baseY !== undefined ? overlay._baseY : overlay.y; overlay.y = (overlay._baseY = overlay.y) - (1 - frac) * (overlay.height / 2); } } updateAbilityOverlay(areaBtnOverlay, boss.cooldowns.area, AREA_COOLDOWN); updateAbilityOverlay(shieldBtnOverlay, boss.cooldowns.shield, SHIELD_COOLDOWN); updateAbilityOverlay(trapBtnOverlay, boss.cooldowns.trap, TRAP_COOLDOWN); // Update heroes for (var i = heroes.length - 1; i >= 0; --i) { var h = heroes[i]; h.update(); // Check collision with shield if (boss.shieldActive && boss.shieldObj && circlesIntersect(boss, SHIELD_RADIUS, h, 60)) { // Repel hero var dx = h.x - boss.x; var dy = h.y - boss.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { h.x += dx / dist * 40; h.y += dy / dist * 40; } LK.effects.flashObject(h, 0x00e5ff, 200); } // Check collision with traps for (var t = traps.length - 1; t >= 0; --t) { var trap = traps[t]; if (circlesIntersect(h, 60, trap, TRAP_RADIUS)) { LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); LK.getSound('heroHit').play(); h.destroy(); heroes.splice(i, 1); trap.destroy(); traps.splice(t, 1); break; } } // Check collision with boss (boss takes damage) var nowIntersectingBoss = circlesIntersect(h, 60, boss, boss.radius); if (!h.lastIntersectingBoss && nowIntersectingBoss) { bossHp--; // Flash boss LK.effects.flashObject(boss, 0xff0000, 400); // Remove hero h.destroy(); heroes.splice(i, 1); // Update boss life bar var hpFrac = Math.max(0, bossHp) / BOSS_MAX_HP; bossHpBar.width = 390 * hpFrac; if (bossHp <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } } h.lastIntersectingBoss = nowIntersectingBoss; } // Update traps for (var i = traps.length - 1; i >= 0; --i) { traps[i].update(); } // Remove area attack visuals if needed for (var i = areaAttacks.length - 1; i >= 0; --i) { // No update needed, handled by tween } // Spawn heroes // Only spawn a new hero if there are no heroes currently on screen if (heroes.length === 0 && LK.ticks % 15 === 0) { spawnHero(); } // Win condition: survive 60 heroes if (LK.getScore() >= 60) { LK.showYouWin(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// AreaAttack class (visual only, handles hit in game code)
var AreaAttack = Container.expand(function () {
var self = Container.call(this);
var areaGfx = self.attachAsset('areaAttack', {
anchorX: 0.5,
anchorY: 0.5
});
areaGfx.alpha = 0.4;
self.update = function () {};
return self;
});
// Boss class (player)
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGfx = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bossGfx.width * 0.5;
self.cooldowns = {
area: 0,
shield: 0,
trap: 0
};
self.shieldActive = false;
self.shieldTimer = 0;
self.update = function () {
// Cooldown timers
if (self.cooldowns.area > 0) self.cooldowns.area--;
if (self.cooldowns.shield > 0) self.cooldowns.shield--;
if (self.cooldowns.trap > 0) self.cooldowns.trap--;
if (self.shieldActive) {
self.shieldTimer--;
if (self.shieldTimer <= 0) {
self.shieldActive = false;
if (self.shieldObj) {
self.shieldObj.destroy();
self.shieldObj = null;
}
}
}
};
return self;
});
// Hero class (basic, blue, straight movement)
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGfx = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6 + Math.random() * 2;
self.hp = 1;
self.type = 1;
self.update = function () {
// Move toward core
var dx = core.x - self.x;
var dy = core.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
};
return self;
});
// Hero2 class (green, zigzag movement)
var Hero2 = Container.expand(function () {
var self = Container.call(this);
var heroGfx = self.attachAsset('hero2', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5 + Math.random() * 2;
self.hp = 2;
self.type = 2;
self.zigzagAngle = Math.random() * Math.PI * 2;
self.update = function () {
// Zigzag toward core
var dx = core.x - self.x;
var dy = core.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
var angle = Math.atan2(dy, dx);
// Add zigzag
self.zigzagAngle += 0.15;
var zigzag = Math.sin(self.zigzagAngle) * 0.5;
angle += zigzag;
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
};
return self;
});
// Hero3 class (red, fast, pauses randomly)
var Hero3 = Container.expand(function () {
var self = Container.call(this);
var heroGfx = self.attachAsset('hero3', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8 + Math.random() * 2;
self.hp = 1;
self.type = 3;
self.pauseTimer = 0;
self.update = function () {
if (self.pauseTimer > 0) {
self.pauseTimer--;
return;
}
// Randomly pause
if (Math.random() < 0.01) {
self.pauseTimer = 30 + Math.floor(Math.random() * 30);
return;
}
// Move toward core
var dx = core.x - self.x;
var dy = core.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
};
return self;
});
// Shield class (visual only)
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldGfx = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5
});
shieldGfx.alpha = 0.25;
self.update = function () {};
return self;
});
// Trap class (placed by boss, destroys hero on contact)
var Trap = Container.expand(function () {
var self = Container.call(this);
var trapGfx = self.attachAsset('trap', {
anchorX: 0.5,
anchorY: 0.5
});
self.timer = 240; // 4 seconds
self.update = function () {
self.timer--;
if (self.timer <= 0) {
self.destroy();
for (var i = 0; i < traps.length; ++i) {
if (traps[i] === self) {
traps.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Add dungeon background image as a changeable background
var dungeonBg = LK.getAsset('dungeon_bg_001', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(dungeonBg);
// Center positions
// Boss (player) asset: large ellipse, purple
// Core asset: smaller ellipse, gold
// Hero asset: rectangle, blue
// Hero2 asset: rectangle, green (for variety)
// Hero3 asset: rectangle, red (for variety)
// Area attack asset: large semi-transparent ellipse, orange
// Shield asset: large semi-transparent ellipse, cyan
// Trap asset: small ellipse, dark gray
// Sound for area attack
var centerX = 2048 / 2;
var centerY = 2732 / 2;
// Core removed: no core asset or object in the middle
// Instead, define a virtual core position for heroes to target
var core = {
x: centerX,
y: centerY + 500
};
// Boss (player)
var boss = new Boss();
boss.x = centerX;
boss.y = centerY + 900;
game.addChild(boss);
// Boss health
var BOSS_MAX_HP = 10;
var bossHp = BOSS_MAX_HP;
// Boss life bar (simple rectangle)
var bossHpBarBg = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
bossHpBarBg.width = 400;
bossHpBarBg.height = 40;
bossHpBarBg.tint = 0x222222;
bossHpBarBg.alpha = 0.7;
bossHpBarBg.x = boss.x;
bossHpBarBg.y = boss.y - boss.radius - 60;
game.addChild(bossHpBarBg);
var bossHpBar = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
bossHpBar.width = 390;
bossHpBar.height = 28;
bossHpBar.tint = 0x43e97b;
bossHpBar.x = boss.x;
bossHpBar.y = boss.y - boss.radius - 60;
game.addChild(bossHpBar);
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Arrays for game objects
var heroes = [];
var traps = [];
var areaAttacks = [];
// Dragging boss
var dragNode = null;
// Ability state
var abilityMode = null; // null, 'area', 'shield', 'trap'
var areaAttackObj = null;
// Ability cooldowns (frames)
var AREA_COOLDOWN = 360; // 6s
var SHIELD_COOLDOWN = 540; // 9s
var TRAP_COOLDOWN = 180; // 3s
// Ability durations
var SHIELD_DURATION = 180; // 3s
// Area attack radius
var AREA_RADIUS = 300;
// Shield radius
var SHIELD_RADIUS = 200;
// Trap radius
var TRAP_RADIUS = 60;
// --- Ability Icon Buttons (replace text buttons) ---
// Area Attack Icon Button
var areaBtnIcon = LK.getAsset('areaAttack', {
anchorX: 0.5,
anchorY: 0.5
});
areaBtnIcon.width = 160;
areaBtnIcon.height = 160;
LK.gui.bottom.addChild(areaBtnIcon);
areaBtnIcon.x = -400;
areaBtnIcon.y = -100;
// Shield Icon Button
var shieldBtnIcon = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5
});
shieldBtnIcon.width = 160;
shieldBtnIcon.height = 160;
LK.gui.bottom.addChild(shieldBtnIcon);
shieldBtnIcon.x = 0;
shieldBtnIcon.y = -100;
// Trap Icon Button
var trapBtnIcon = LK.getAsset('trap', {
anchorX: 0.5,
anchorY: 0.5
});
trapBtnIcon.width = 160;
trapBtnIcon.height = 160;
LK.gui.bottom.addChild(trapBtnIcon);
trapBtnIcon.x = 400;
trapBtnIcon.y = -100;
// Add a semi-transparent overlay for cooldown effect for each button
function makeCooldownOverlay(width, height) {
var overlay = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
overlay.width = width;
overlay.height = height;
overlay.tint = 0x222222;
overlay.alpha = 0.6;
return overlay;
}
var areaBtnOverlay = makeCooldownOverlay(160, 160);
var shieldBtnOverlay = makeCooldownOverlay(160, 160);
var trapBtnOverlay = makeCooldownOverlay(160, 160);
LK.gui.bottom.addChild(areaBtnOverlay);
LK.gui.bottom.addChild(shieldBtnOverlay);
LK.gui.bottom.addChild(trapBtnOverlay);
areaBtnOverlay.x = areaBtnIcon.x;
areaBtnOverlay.y = areaBtnIcon.y;
shieldBtnOverlay.x = shieldBtnIcon.x;
shieldBtnOverlay.y = shieldBtnIcon.y;
trapBtnOverlay.x = trapBtnIcon.x;
trapBtnOverlay.y = trapBtnIcon.y;
// Add a small text label under each icon
var areaBtnLabel = new Text2('Area', {
size: 48,
fill: "#fff"
});
areaBtnLabel.anchor.set(0.5, 0);
LK.gui.bottom.addChild(areaBtnLabel);
areaBtnLabel.x = areaBtnIcon.x;
areaBtnLabel.y = areaBtnIcon.y + 90;
var shieldBtnLabel = new Text2('Shield', {
size: 48,
fill: "#fff"
});
shieldBtnLabel.anchor.set(0.5, 0);
LK.gui.bottom.addChild(shieldBtnLabel);
shieldBtnLabel.x = shieldBtnIcon.x;
shieldBtnLabel.y = shieldBtnIcon.y + 90;
var trapBtnLabel = new Text2('Trap', {
size: 48,
fill: "#fff"
});
trapBtnLabel.anchor.set(0.5, 0);
LK.gui.bottom.addChild(trapBtnLabel);
trapBtnLabel.x = trapBtnIcon.x;
trapBtnLabel.y = trapBtnIcon.y + 90;
// Ability button handlers (icon down events)
areaBtnIcon.down = function (x, y, obj) {
useAreaAttack();
};
shieldBtnIcon.down = function (x, y, obj) {
useShield();
};
trapBtnIcon.down = function (x, y, obj) {
useTrap();
};
// Helper: check if two objects intersect (circle-based)
function circlesIntersect(a, rA, b, rB) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < rA + rB;
}
// Helper: spawn a hero at random edge, and make them stronger as more spawn
var heroSpawnCount = 0;
function spawnHero() {
var edge = Math.floor(Math.random() * 4);
var x, y;
if (edge === 0) {
// Top
x = 200 + Math.random() * (2048 - 400);
y = -100;
} else if (edge === 1) {
// Bottom
x = 200 + Math.random() * (2048 - 400);
y = 2732 + 100;
} else if (edge === 2) {
// Left
x = -100;
y = 400 + Math.random() * (2732 - 800);
} else {
// Right
x = 2048 + 100;
y = 400 + Math.random() * (2732 - 800);
}
// Only spawn one hero at a time (no batch spawn)
// Make each new hero a little stronger
heroSpawnCount++;
var t = Math.random();
var hero;
if (t < 0.5) {
hero = new Hero();
} else if (t < 0.8) {
hero = new Hero2();
} else {
hero = new Hero3();
}
// Increase hero stats as spawn count increases
var hpBoost = Math.floor(heroSpawnCount / 10);
var speedBoost = Math.min(heroSpawnCount * 0.05, 3);
hero.hp += hpBoost;
hero.speed += speedBoost;
hero.x = x;
hero.y = y;
hero.lastIntersectingCore = false;
hero.lastIntersectingBoss = false;
hero.lastIntersectingTrap = false;
heroes.push(hero);
game.addChild(hero);
}
// Ability: Area attack
function useAreaAttack() {
if (boss.cooldowns.area > 0) return;
boss.cooldowns.area = AREA_COOLDOWN;
// Visual
areaAttackObj = new AreaAttack();
areaAttackObj.x = boss.x;
areaAttackObj.y = boss.y;
areaAttackObj.scaleX = 0.1;
areaAttackObj.scaleY = 0.1;
areaAttackObj.alpha = 0.5;
game.addChild(areaAttackObj);
areaAttacks.push(areaAttackObj);
// Animate
tween(areaAttackObj, {
scaleX: 1,
scaleY: 1,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (areaAttackObj) {
areaAttackObj.destroy();
var idx = areaAttacks.indexOf(areaAttackObj);
if (idx !== -1) areaAttacks.splice(idx, 1);
areaAttackObj = null;
}
}
});
// Hit all heroes in radius
for (var i = heroes.length - 1; i >= 0; --i) {
var h = heroes[i];
if (circlesIntersect(boss, AREA_RADIUS, h, 60)) {
h.hp -= 1;
LK.getSound('area').play();
LK.effects.flashObject(h, 0xff9800, 300);
if (h.hp <= 0) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('heroHit').play();
h.destroy();
heroes.splice(i, 1);
}
}
}
}
// Ability: Shield
function useShield() {
if (boss.cooldowns.shield > 0) return;
boss.cooldowns.shield = SHIELD_COOLDOWN;
boss.shieldActive = true;
boss.shieldTimer = SHIELD_DURATION;
if (boss.shieldObj) boss.shieldObj.destroy();
boss.shieldObj = new Shield();
boss.shieldObj.x = boss.x;
boss.shieldObj.y = boss.y;
game.addChild(boss.shieldObj);
LK.getSound('shield').play();
}
// Ability: Trap
function useTrap() {
if (boss.cooldowns.trap > 0) return;
boss.cooldowns.trap = TRAP_COOLDOWN;
// Place trap at boss position
var trap = new Trap();
trap.x = boss.x;
trap.y = boss.y;
traps.push(trap);
game.addChild(trap);
LK.getSound('trap').play();
}
// Ability button handlers (handled by icon event handlers above)
// Dragging boss
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp boss inside game area
var minX = boss.radius + 40;
var maxX = 2048 - boss.radius - 40;
var minY = boss.radius + 200;
var maxY = 2732 - boss.radius - 40;
boss.x = Math.max(minX, Math.min(maxX, x));
boss.y = Math.max(minY, Math.min(maxY, y));
if (boss.shieldObj) {
boss.shieldObj.x = boss.x;
boss.shieldObj.y = boss.y;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only drag if touch or mouse is on boss
var dx = x - boss.x;
var dy = y - boss.y;
if (dx * dx + dy * dy < boss.radius * boss.radius) {
dragNode = boss;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game update
game.update = function () {
// Update boss
boss.update();
// Update boss life bar position
bossHpBarBg.x = boss.x;
bossHpBarBg.y = boss.y - boss.radius - 60;
bossHpBar.x = boss.x;
bossHpBar.y = boss.y - boss.radius - 60;
// Update ability icon overlays to show cooldown visually
function updateAbilityOverlay(overlay, cooldown, maxCooldown) {
if (cooldown <= 0) {
overlay.alpha = 0;
} else {
// Show overlay, alpha increases as cooldown is higher
overlay.alpha = 0.6;
// Visually show cooldown as a darken effect (simulate by scaling Y)
var frac = Math.max(0, Math.min(1, cooldown / maxCooldown));
overlay.scaleY = frac;
overlay.y = overlay._baseY !== undefined ? overlay._baseY : overlay.y;
overlay.y = (overlay._baseY = overlay.y) - (1 - frac) * (overlay.height / 2);
}
}
updateAbilityOverlay(areaBtnOverlay, boss.cooldowns.area, AREA_COOLDOWN);
updateAbilityOverlay(shieldBtnOverlay, boss.cooldowns.shield, SHIELD_COOLDOWN);
updateAbilityOverlay(trapBtnOverlay, boss.cooldowns.trap, TRAP_COOLDOWN);
// Update heroes
for (var i = heroes.length - 1; i >= 0; --i) {
var h = heroes[i];
h.update();
// Check collision with shield
if (boss.shieldActive && boss.shieldObj && circlesIntersect(boss, SHIELD_RADIUS, h, 60)) {
// Repel hero
var dx = h.x - boss.x;
var dy = h.y - boss.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
h.x += dx / dist * 40;
h.y += dy / dist * 40;
}
LK.effects.flashObject(h, 0x00e5ff, 200);
}
// Check collision with traps
for (var t = traps.length - 1; t >= 0; --t) {
var trap = traps[t];
if (circlesIntersect(h, 60, trap, TRAP_RADIUS)) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('heroHit').play();
h.destroy();
heroes.splice(i, 1);
trap.destroy();
traps.splice(t, 1);
break;
}
}
// Check collision with boss (boss takes damage)
var nowIntersectingBoss = circlesIntersect(h, 60, boss, boss.radius);
if (!h.lastIntersectingBoss && nowIntersectingBoss) {
bossHp--;
// Flash boss
LK.effects.flashObject(boss, 0xff0000, 400);
// Remove hero
h.destroy();
heroes.splice(i, 1);
// Update boss life bar
var hpFrac = Math.max(0, bossHp) / BOSS_MAX_HP;
bossHpBar.width = 390 * hpFrac;
if (bossHp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
h.lastIntersectingBoss = nowIntersectingBoss;
}
// Update traps
for (var i = traps.length - 1; i >= 0; --i) {
traps[i].update();
}
// Remove area attack visuals if needed
for (var i = areaAttacks.length - 1; i >= 0; --i) {
// No update needed, handled by tween
}
// Spawn heroes
// Only spawn a new hero if there are no heroes currently on screen
if (heroes.length === 0 && LK.ticks % 15 === 0) {
spawnHero();
}
// Win condition: survive 60 heroes
if (LK.getScore() >= 60) {
LK.showYouWin();
}
};
earthquake image. In-Game asset. 2d. High contrast. No shadows
It's a scary monster, it has a 50-meter sword in its hand, it looks like the full moon sword in the game Metin2.. In-Game asset. 2d. High contrast. No shadows
game Charakter. In-Game asset. 2d. High contrast. No shadows
shield. In-Game asset. 2d. High contrast. No shadows
trap. In-Game asset. 2d. High contrast. No shadows
2d dungeon. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat