/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Enemy class var Enemy = Container.expand(function () { var self = Container.call(this); // Enemy body (red ellipse) var enemyBody = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); // Enemy eyes (black ellipses) var eyeLeft = self.attachAsset('enemy_eye', { anchorX: 0.5, anchorY: 0.5, x: -18, y: -10 }); var eyeRight = self.attachAsset('enemy_eye', { anchorX: 0.5, anchorY: 0.5, x: 18, y: -10 }); self.speed = 4 + Math.random() * 2; // base speed, will be set on spawn self.dir = 0; // direction in radians, will be set on spawn // For hit flash self.isHit = false; self.hitTicks = 0; self.update = function () { // Move towards direction self.x += Math.cos(self.dir) * self.speed; self.y += Math.sin(self.dir) * self.speed; // Hit flash if (self.isHit) { self.hitTicks--; if (self.hitTicks <= 0) { enemyBody.tint = 0xe74c3c; self.isHit = false; } } }; self.flashHit = function () { self.isHit = true; self.hitTicks = 6; enemyBody.tint = 0xffffff; }; return self; }); // Hero class var Hero = Container.expand(function () { var self = Container.call(this); // Hero body (blue box) var heroBody = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); // Hero face (white ellipse) var heroFace = self.attachAsset('hero_face', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -10 }); // Hero hair (brown box) var heroHair = self.attachAsset('hero_hair', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -36 }); // Sword blade (yellow) var swordBlade = self.attachAsset('sword_blade', { anchorX: 0.0, anchorY: 0.5, x: 60, y: 0 }); // Sword handle (brown) var swordHandle = self.attachAsset('sword_handle', { anchorX: 0.0, anchorY: 0.5, x: 48, y: 0 }); swordBlade.rotation = 0; swordHandle.rotation = 0; swordBlade.visible = false; swordHandle.visible = false; // Slash effect (white ellipse) var slash = self.attachAsset('slash', { anchorX: 0.0, anchorY: 0.5, x: 80, y: 0 }); slash.alpha = 0; slash.visible = false; // State self.sword = swordBlade; self.swordHandle = swordHandle; self.slash = slash; self.slashCooldown = 0; // ticks until next slash allowed self.slashActive = false; self.slashDir = 0; // radians, direction of last slash // Slash method self.doSlash = function (dir) { if (self.slashCooldown > 0) return false; self.slashCooldown = 18; // 0.3s at 60fps self.slashActive = true; self.slashDir = dir; // Sword swing animation self.sword.visible = true; self.swordHandle.visible = true; self.sword.rotation = dir - Math.PI / 2 - 0.5; self.swordHandle.rotation = dir - Math.PI / 2 - 0.5; tween(self.sword, { rotation: dir - Math.PI / 2 + 0.5 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { self.sword.visible = false; self.swordHandle.visible = false; } }); tween(self.swordHandle, { rotation: dir - Math.PI / 2 + 0.5 }, { duration: 120, easing: tween.cubicOut }); // Slash effect self.slash.visible = true; self.slash.rotation = dir - Math.PI / 2; self.slash.alpha = 0.7; tween(self.slash, { alpha: 0 }, { duration: 180, easing: tween.linear, onFinish: function onFinish() { self.slash.visible = false; } }); // Play swing sound LK.getSound('swing').play(); return true; }; // Update self.update = function () { if (self.slashCooldown > 0) { self.slashCooldown--; if (self.slashCooldown === 0) { self.slashActive = false; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Palace floor image for Turkish palace background (replace id with actual palace floor tile image asset) // Sounds // Enemy: red ellipse with black eyes (pixel art style) // Slash: white ellipse (pixel art style) // Sword: yellow blade, brown handle (pixel art style) // Hero: blue box with white face and brown hair (pixel art style) // Game constants // Hero: blue box, 120x120 // Sword: yellow box, 80x24 // Enemy: red ellipse, 100x100 // Slash effect: white ellipse, 90x40 // Sound: enemy hit // Sound: sword swing // --- MENU UI --- var menuGroup = new Container(); game.addChild(menuGroup); // Palace floor background for menu var palaceFloor = LK.getAsset('palace_floor', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); menuGroup.addChild(palaceFloor); // Palace floor background for game arena (separate from menu) var arenaFloor = LK.getAsset('palace_floor', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChildAt(arenaFloor, 0); // Always behind hero/enemies arenaFloor.visible = false; // Hero pixel art for menu (centered, larger) var menuHero = LK.getAsset('hero', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 900, width: 220, height: 220 }); menuGroup.addChild(menuHero); // Sword pixel art for menu (angled, next to hero) var menuSword = LK.getAsset('sword_blade', { anchorX: 0.1, anchorY: 0.5, x: 2048 / 2 + 120, y: 900, width: 140, height: 28, rotation: -0.3 }); menuGroup.addChild(menuSword); // Game title in Turkish palace style var menuTitle = new Text2("Palace Arena", { size: 160, fill: 0xE6C200, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); menuTitle.anchor.set(0.5, 0.5); menuTitle.x = 2048 / 2; menuTitle.y = 350; menuGroup.addChild(menuTitle); // Subtitle var menuSubtitle = new Text2("Endless Sword Survival", { size: 70, fill: "#fff" }); menuSubtitle.anchor.set(0.5, 0.5); menuSubtitle.x = 2048 / 2; menuSubtitle.y = 500; menuGroup.addChild(menuSubtitle); // Start button var startBtn = new Text2("Start", { size: 120, fill: "#0f0" }); startBtn.anchor.set(0.5, 0.5); startBtn.x = 2048 / 2; startBtn.y = 1300; startBtn.interactive = true; startBtn.buttonMode = true; menuGroup.addChild(startBtn); // Hide menu and start game function startGameFromMenu() { menuGroup.visible = false; gameStarted = true; } startBtn.down = function (x, y, obj) { startGameFromMenu(); }; // Prevent game from running until menu is dismissed var gameStarted = false; var ARENA_MARGIN = 60; // px, keep enemies from spawning too close to edge var HERO_START_X = 2048 / 2; var HERO_START_Y = 2732 / 2; var HERO_SPEED = 18; // px per move var ENEMY_SPAWN_INTERVAL = 60; // ticks (1s) var ENEMY_MIN_DIST = 220; // min distance from hero to spawn var ENEMY_MAX_BASE = 8; // base max enemies per round var ENEMY_MAX = ENEMY_MAX_BASE; // will increase per round // Game state var hero = new Hero(); game.addChild(hero); hero.x = HERO_START_X; hero.y = HERO_START_Y; var enemies = []; var score = 0; var wave = 1; var roundKills = 0; // kills in current round var roundActive = true; // is round ongoing var ticksSinceSpawn = 0; var isDragging = false; var dragOffsetX = 0; var dragOffsetY = 0; var lastTouchX = hero.x; var lastTouchY = hero.y; // Shop/Power-up system state var shopActive = false; var availablePowerups = []; var selectedPowerup = null; // Power-up definitions (add more as needed) var POWERUPS = [{ id: "longer_sword", name: "Longer Sword", desc: "Sword range +30%", apply: function apply() { hero.sword.width *= 1.3; } }, { id: "faster_slash", name: "Faster Slash", desc: "Slash cooldown -20%", apply: function apply() { hero.slashCooldown = Math.max(1, Math.floor(hero.slashCooldown * 0.8)); } }, { id: "move_speed", name: "Move Speed Up", desc: "Hero moves faster", apply: function apply() { HERO_SPEED += 3; } }]; // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); // Score text (left side) scoreTxt.anchor.set(0, 0); scoreTxt.x = 120; scoreTxt.y = 0; LK.gui.top.addChild(scoreTxt); // Round text (center) var roundTxt = new Text2('Round 1', { size: 80, fill: "#ff0" }); roundTxt.anchor.set(0.5, 0); roundTxt.x = 2048 / 2; roundTxt.y = 0; LK.gui.top.addChild(roundTxt); // Enemies left text (right side) var enemiesLeftTxt = new Text2('', { size: 70, fill: "#fff" }); enemiesLeftTxt.anchor.set(1, 0); enemiesLeftTxt.x = 2048 - 120; enemiesLeftTxt.y = 0; LK.gui.top.addChild(enemiesLeftTxt); // Shop/Power-up UI var shopTxt = null; var powerupBtns = []; // Show shop and power-up selection at end of round function showShop() { // Update round text roundTxt.setText("Round " + wave + " Complete!"); // Pick 2 random powerups availablePowerups = []; var pool = POWERUPS.slice(); for (var i = 0; i < 2; i++) { if (pool.length === 0) break; var idx = Math.floor(Math.random() * pool.length); availablePowerups.push(pool[idx]); pool.splice(idx, 1); } // Show shop text if (!shopTxt) { shopTxt = new Text2("Choose a Power-Up!", { size: 90, fill: "#fff" }); shopTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(shopTxt); } shopTxt.setText("Choose a Power-Up!"); shopTxt.visible = true; // Show powerup buttons for (var b = 0; b < powerupBtns.length; b++) { LK.gui.center.removeChild(powerupBtns[b]); } powerupBtns = []; for (var i = 0; i < availablePowerups.length; i++) { (function (i) { var p = availablePowerups[i]; var btn = new Text2(p.name + "\n" + p.desc, { size: 70, fill: "#0f0" }); btn.anchor.set(0.5, 0.5); btn.x = 0; btn.y = 180 + i * 220; btn.interactive = true; btn.buttonMode = true; btn.down = function (x, y, obj) { selectPowerup(i); }; LK.gui.center.addChild(btn); powerupBtns.push(btn); })(i); } } // Handle power-up selection function selectPowerup(idx) { var p = availablePowerups[idx]; if (!p) return; // Apply powerup effect if (typeof p.apply === "function") p.apply(); // Hide shop UI if (shopTxt) shopTxt.visible = false; for (var b = 0; b < powerupBtns.length; b++) { LK.gui.center.removeChild(powerupBtns[b]); } powerupBtns = []; // Start next round wave++; ENEMY_MAX = ENEMY_MAX_BASE + (wave - 1) * 4; roundKills = 0; roundActive = true; shopActive = false; roundTxt.setText("Round " + wave); } // Helper: spawn enemy at random edge, moving toward hero function spawnEnemy() { // Pick random edge: 0=top,1=bottom,2=left,3=right var edge = Math.floor(Math.random() * 4); var ex, ey; if (edge === 0) { // top ex = ARENA_MARGIN + Math.random() * (2048 - 2 * ARENA_MARGIN); ey = ARENA_MARGIN; } else if (edge === 1) { // bottom ex = ARENA_MARGIN + Math.random() * (2048 - 2 * ARENA_MARGIN); ey = 2732 - ARENA_MARGIN; } else if (edge === 2) { // left ex = ARENA_MARGIN; ey = ARENA_MARGIN + Math.random() * (2732 - 2 * ARENA_MARGIN); } else { // right ex = 2048 - ARENA_MARGIN; ey = ARENA_MARGIN + Math.random() * (2732 - 2 * ARENA_MARGIN); } // Don't spawn too close to hero var dx = ex - hero.x, dy = ey - hero.y; if (Math.sqrt(dx * dx + dy * dy) < ENEMY_MIN_DIST) { // Try again return; } var enemy = new Enemy(); enemy.x = ex; enemy.y = ey; // Direction toward hero var dir = Math.atan2(hero.y - ey, hero.x - ex); enemy.dir = dir; // Speed increases with wave enemy.speed = 3.5 + Math.random() * 1.5 + (wave - 1) * 0.25; enemies.push(enemy); game.addChild(enemy); } // Helper: check collision between two objects (circle approx) function isColliding(a, b, rA, 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: check if point is inside sword slash arc function isInSlashArc(enemy, hero, dir) { // Sword arc: 90deg (PI/2) in direction dir, radius 180px from hero center var dx = enemy.x - hero.x; var dy = enemy.y - hero.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 180 || dist < 60) return false; var angle = Math.atan2(dy, dx); var diff = Math.abs((angle - dir + Math.PI * 3) % (Math.PI * 2) - Math.PI); return diff < Math.PI / 4; // 45deg to either side } // Touch controls: drag to move, tap/hold to slash in direction var dragNode = null; var dragStartX = 0, dragStartY = 0; var dragHeroStartX = 0, dragHeroStartY = 0; var lastMoveDir = 0; // Move handler: drag to move hero, tap/hold to slash function handleMove(x, y, obj) { if (!gameStarted) return; // Don't allow hero to move into top left 100x100 if (x < 120 && y < 120) return; // Only move hero if dragging (left side touch) if (isDragging) { var nx = x - dragOffsetX; var ny = y - dragOffsetY; // Clamp to arena nx = Math.max(ARENA_MARGIN, Math.min(2048 - ARENA_MARGIN, nx)); ny = Math.max(ARENA_MARGIN, Math.min(2732 - ARENA_MARGIN, ny)); hero.x = nx; hero.y = ny; lastTouchX = nx; lastTouchY = ny; } } game.move = handleMove; game.down = function (x, y, obj) { // Block all input except menu start until game is started if (!gameStarted) return; // Divide screen: left half for movement, right half for attack if (x < 2048 / 2) { // Left side: start dragging hero isDragging = true; dragOffsetX = x - hero.x; dragOffsetY = y - hero.y; } else { // Right side: attack in direction from hero to touch var dir = Math.atan2(y - hero.y, x - hero.x); lastMoveDir = dir; hero.doSlash(dir); } }; game.up = function (x, y, obj) { if (!gameStarted) return; isDragging = false; }; // Main update loop game.update = function () { // Block all gameplay until menu is dismissed if (!gameStarted) { // Only show menu, do not update game menuGroup.visible = true; arenaFloor.visible = false; return; } else { menuGroup.visible = false; arenaFloor.visible = true; } // Update hero hero.update(); // Update round and enemies left display roundTxt.setText("Wave " + wave); if (roundActive) { enemiesLeftTxt.setText("Enemies left: " + Math.max(ENEMY_MAX - roundKills, 0)); enemiesLeftTxt.visible = true; } else { enemiesLeftTxt.setText(""); enemiesLeftTxt.visible = false; } // Show round/wave info (optional, can be improved with GUI) if (!shopActive && !roundActive) { // Show "Round X Complete!" and trigger shop shopActive = true; showShop(); return; } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; enemy.update(); // If enemy off screen, remove if (enemy.x < -120 || enemy.x > 2048 + 120 || enemy.y < -120 || enemy.y > 2732 + 120) { enemy.destroy(); enemies.splice(i, 1); continue; } // If enemy collides with hero, game over if (isColliding(enemy, hero, 50, 50)) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } // If hero is slashing and enemy is within slash radius, destroy enemy if (hero.slashActive) { // Hit everywhere in a circular area (e.g. 180px radius) var dx = enemy.x - hero.x; var dy = enemy.y - hero.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= 180) { // Only allow one hit per slash per enemy if (!enemy.isHit) { enemy.flashHit(); LK.getSound('hit').play(); // Remove after short delay for flash (function (e, idx) { LK.setTimeout(function () { if (enemies.indexOf(e) !== -1) { e.destroy(); enemies.splice(enemies.indexOf(e), 1); // Score score++; roundKills++; scoreTxt.setText(score); // Check if round is complete if (roundKills >= ENEMY_MAX) { roundActive = false; } } }, 80); })(enemy, i); } } } } // Only spawn enemies if round is active and shop is not open if (roundActive && !shopActive) { ticksSinceSpawn++; var spawnRate = Math.max(ENEMY_SPAWN_INTERVAL - (wave - 1) * 4, 24); // faster per wave if (ticksSinceSpawn >= spawnRate && enemies.length < ENEMY_MAX) { spawnEnemy(); ticksSinceSpawn = 0; } } // If round is not active and shop is not open, trigger shop if (!roundActive && !shopActive) { shopActive = true; showShop(); } // Show wave/round info (optional, can be improved with GUI) };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Enemy body (red ellipse)
var enemyBody = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Enemy eyes (black ellipses)
var eyeLeft = self.attachAsset('enemy_eye', {
anchorX: 0.5,
anchorY: 0.5,
x: -18,
y: -10
});
var eyeRight = self.attachAsset('enemy_eye', {
anchorX: 0.5,
anchorY: 0.5,
x: 18,
y: -10
});
self.speed = 4 + Math.random() * 2; // base speed, will be set on spawn
self.dir = 0; // direction in radians, will be set on spawn
// For hit flash
self.isHit = false;
self.hitTicks = 0;
self.update = function () {
// Move towards direction
self.x += Math.cos(self.dir) * self.speed;
self.y += Math.sin(self.dir) * self.speed;
// Hit flash
if (self.isHit) {
self.hitTicks--;
if (self.hitTicks <= 0) {
enemyBody.tint = 0xe74c3c;
self.isHit = false;
}
}
};
self.flashHit = function () {
self.isHit = true;
self.hitTicks = 6;
enemyBody.tint = 0xffffff;
};
return self;
});
// Hero class
var Hero = Container.expand(function () {
var self = Container.call(this);
// Hero body (blue box)
var heroBody = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
// Hero face (white ellipse)
var heroFace = self.attachAsset('hero_face', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -10
});
// Hero hair (brown box)
var heroHair = self.attachAsset('hero_hair', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -36
});
// Sword blade (yellow)
var swordBlade = self.attachAsset('sword_blade', {
anchorX: 0.0,
anchorY: 0.5,
x: 60,
y: 0
});
// Sword handle (brown)
var swordHandle = self.attachAsset('sword_handle', {
anchorX: 0.0,
anchorY: 0.5,
x: 48,
y: 0
});
swordBlade.rotation = 0;
swordHandle.rotation = 0;
swordBlade.visible = false;
swordHandle.visible = false;
// Slash effect (white ellipse)
var slash = self.attachAsset('slash', {
anchorX: 0.0,
anchorY: 0.5,
x: 80,
y: 0
});
slash.alpha = 0;
slash.visible = false;
// State
self.sword = swordBlade;
self.swordHandle = swordHandle;
self.slash = slash;
self.slashCooldown = 0; // ticks until next slash allowed
self.slashActive = false;
self.slashDir = 0; // radians, direction of last slash
// Slash method
self.doSlash = function (dir) {
if (self.slashCooldown > 0) return false;
self.slashCooldown = 18; // 0.3s at 60fps
self.slashActive = true;
self.slashDir = dir;
// Sword swing animation
self.sword.visible = true;
self.swordHandle.visible = true;
self.sword.rotation = dir - Math.PI / 2 - 0.5;
self.swordHandle.rotation = dir - Math.PI / 2 - 0.5;
tween(self.sword, {
rotation: dir - Math.PI / 2 + 0.5
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.sword.visible = false;
self.swordHandle.visible = false;
}
});
tween(self.swordHandle, {
rotation: dir - Math.PI / 2 + 0.5
}, {
duration: 120,
easing: tween.cubicOut
});
// Slash effect
self.slash.visible = true;
self.slash.rotation = dir - Math.PI / 2;
self.slash.alpha = 0.7;
tween(self.slash, {
alpha: 0
}, {
duration: 180,
easing: tween.linear,
onFinish: function onFinish() {
self.slash.visible = false;
}
});
// Play swing sound
LK.getSound('swing').play();
return true;
};
// Update
self.update = function () {
if (self.slashCooldown > 0) {
self.slashCooldown--;
if (self.slashCooldown === 0) {
self.slashActive = false;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Palace floor image for Turkish palace background (replace id with actual palace floor tile image asset)
// Sounds
// Enemy: red ellipse with black eyes (pixel art style)
// Slash: white ellipse (pixel art style)
// Sword: yellow blade, brown handle (pixel art style)
// Hero: blue box with white face and brown hair (pixel art style)
// Game constants
// Hero: blue box, 120x120
// Sword: yellow box, 80x24
// Enemy: red ellipse, 100x100
// Slash effect: white ellipse, 90x40
// Sound: enemy hit
// Sound: sword swing
// --- MENU UI ---
var menuGroup = new Container();
game.addChild(menuGroup);
// Palace floor background for menu
var palaceFloor = LK.getAsset('palace_floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
menuGroup.addChild(palaceFloor);
// Palace floor background for game arena (separate from menu)
var arenaFloor = LK.getAsset('palace_floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChildAt(arenaFloor, 0); // Always behind hero/enemies
arenaFloor.visible = false;
// Hero pixel art for menu (centered, larger)
var menuHero = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 900,
width: 220,
height: 220
});
menuGroup.addChild(menuHero);
// Sword pixel art for menu (angled, next to hero)
var menuSword = LK.getAsset('sword_blade', {
anchorX: 0.1,
anchorY: 0.5,
x: 2048 / 2 + 120,
y: 900,
width: 140,
height: 28,
rotation: -0.3
});
menuGroup.addChild(menuSword);
// Game title in Turkish palace style
var menuTitle = new Text2("Palace Arena", {
size: 160,
fill: 0xE6C200,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 2048 / 2;
menuTitle.y = 350;
menuGroup.addChild(menuTitle);
// Subtitle
var menuSubtitle = new Text2("Endless Sword Survival", {
size: 70,
fill: "#fff"
});
menuSubtitle.anchor.set(0.5, 0.5);
menuSubtitle.x = 2048 / 2;
menuSubtitle.y = 500;
menuGroup.addChild(menuSubtitle);
// Start button
var startBtn = new Text2("Start", {
size: 120,
fill: "#0f0"
});
startBtn.anchor.set(0.5, 0.5);
startBtn.x = 2048 / 2;
startBtn.y = 1300;
startBtn.interactive = true;
startBtn.buttonMode = true;
menuGroup.addChild(startBtn);
// Hide menu and start game
function startGameFromMenu() {
menuGroup.visible = false;
gameStarted = true;
}
startBtn.down = function (x, y, obj) {
startGameFromMenu();
};
// Prevent game from running until menu is dismissed
var gameStarted = false;
var ARENA_MARGIN = 60; // px, keep enemies from spawning too close to edge
var HERO_START_X = 2048 / 2;
var HERO_START_Y = 2732 / 2;
var HERO_SPEED = 18; // px per move
var ENEMY_SPAWN_INTERVAL = 60; // ticks (1s)
var ENEMY_MIN_DIST = 220; // min distance from hero to spawn
var ENEMY_MAX_BASE = 8; // base max enemies per round
var ENEMY_MAX = ENEMY_MAX_BASE; // will increase per round
// Game state
var hero = new Hero();
game.addChild(hero);
hero.x = HERO_START_X;
hero.y = HERO_START_Y;
var enemies = [];
var score = 0;
var wave = 1;
var roundKills = 0; // kills in current round
var roundActive = true; // is round ongoing
var ticksSinceSpawn = 0;
var isDragging = false;
var dragOffsetX = 0;
var dragOffsetY = 0;
var lastTouchX = hero.x;
var lastTouchY = hero.y;
// Shop/Power-up system state
var shopActive = false;
var availablePowerups = [];
var selectedPowerup = null;
// Power-up definitions (add more as needed)
var POWERUPS = [{
id: "longer_sword",
name: "Longer Sword",
desc: "Sword range +30%",
apply: function apply() {
hero.sword.width *= 1.3;
}
}, {
id: "faster_slash",
name: "Faster Slash",
desc: "Slash cooldown -20%",
apply: function apply() {
hero.slashCooldown = Math.max(1, Math.floor(hero.slashCooldown * 0.8));
}
}, {
id: "move_speed",
name: "Move Speed Up",
desc: "Hero moves faster",
apply: function apply() {
HERO_SPEED += 3;
}
}];
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
// Score text (left side)
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 120;
scoreTxt.y = 0;
LK.gui.top.addChild(scoreTxt);
// Round text (center)
var roundTxt = new Text2('Round 1', {
size: 80,
fill: "#ff0"
});
roundTxt.anchor.set(0.5, 0);
roundTxt.x = 2048 / 2;
roundTxt.y = 0;
LK.gui.top.addChild(roundTxt);
// Enemies left text (right side)
var enemiesLeftTxt = new Text2('', {
size: 70,
fill: "#fff"
});
enemiesLeftTxt.anchor.set(1, 0);
enemiesLeftTxt.x = 2048 - 120;
enemiesLeftTxt.y = 0;
LK.gui.top.addChild(enemiesLeftTxt);
// Shop/Power-up UI
var shopTxt = null;
var powerupBtns = [];
// Show shop and power-up selection at end of round
function showShop() {
// Update round text
roundTxt.setText("Round " + wave + " Complete!");
// Pick 2 random powerups
availablePowerups = [];
var pool = POWERUPS.slice();
for (var i = 0; i < 2; i++) {
if (pool.length === 0) break;
var idx = Math.floor(Math.random() * pool.length);
availablePowerups.push(pool[idx]);
pool.splice(idx, 1);
}
// Show shop text
if (!shopTxt) {
shopTxt = new Text2("Choose a Power-Up!", {
size: 90,
fill: "#fff"
});
shopTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(shopTxt);
}
shopTxt.setText("Choose a Power-Up!");
shopTxt.visible = true;
// Show powerup buttons
for (var b = 0; b < powerupBtns.length; b++) {
LK.gui.center.removeChild(powerupBtns[b]);
}
powerupBtns = [];
for (var i = 0; i < availablePowerups.length; i++) {
(function (i) {
var p = availablePowerups[i];
var btn = new Text2(p.name + "\n" + p.desc, {
size: 70,
fill: "#0f0"
});
btn.anchor.set(0.5, 0.5);
btn.x = 0;
btn.y = 180 + i * 220;
btn.interactive = true;
btn.buttonMode = true;
btn.down = function (x, y, obj) {
selectPowerup(i);
};
LK.gui.center.addChild(btn);
powerupBtns.push(btn);
})(i);
}
}
// Handle power-up selection
function selectPowerup(idx) {
var p = availablePowerups[idx];
if (!p) return;
// Apply powerup effect
if (typeof p.apply === "function") p.apply();
// Hide shop UI
if (shopTxt) shopTxt.visible = false;
for (var b = 0; b < powerupBtns.length; b++) {
LK.gui.center.removeChild(powerupBtns[b]);
}
powerupBtns = [];
// Start next round
wave++;
ENEMY_MAX = ENEMY_MAX_BASE + (wave - 1) * 4;
roundKills = 0;
roundActive = true;
shopActive = false;
roundTxt.setText("Round " + wave);
}
// Helper: spawn enemy at random edge, moving toward hero
function spawnEnemy() {
// Pick random edge: 0=top,1=bottom,2=left,3=right
var edge = Math.floor(Math.random() * 4);
var ex, ey;
if (edge === 0) {
// top
ex = ARENA_MARGIN + Math.random() * (2048 - 2 * ARENA_MARGIN);
ey = ARENA_MARGIN;
} else if (edge === 1) {
// bottom
ex = ARENA_MARGIN + Math.random() * (2048 - 2 * ARENA_MARGIN);
ey = 2732 - ARENA_MARGIN;
} else if (edge === 2) {
// left
ex = ARENA_MARGIN;
ey = ARENA_MARGIN + Math.random() * (2732 - 2 * ARENA_MARGIN);
} else {
// right
ex = 2048 - ARENA_MARGIN;
ey = ARENA_MARGIN + Math.random() * (2732 - 2 * ARENA_MARGIN);
}
// Don't spawn too close to hero
var dx = ex - hero.x,
dy = ey - hero.y;
if (Math.sqrt(dx * dx + dy * dy) < ENEMY_MIN_DIST) {
// Try again
return;
}
var enemy = new Enemy();
enemy.x = ex;
enemy.y = ey;
// Direction toward hero
var dir = Math.atan2(hero.y - ey, hero.x - ex);
enemy.dir = dir;
// Speed increases with wave
enemy.speed = 3.5 + Math.random() * 1.5 + (wave - 1) * 0.25;
enemies.push(enemy);
game.addChild(enemy);
}
// Helper: check collision between two objects (circle approx)
function isColliding(a, b, rA, 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: check if point is inside sword slash arc
function isInSlashArc(enemy, hero, dir) {
// Sword arc: 90deg (PI/2) in direction dir, radius 180px from hero center
var dx = enemy.x - hero.x;
var dy = enemy.y - hero.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 180 || dist < 60) return false;
var angle = Math.atan2(dy, dx);
var diff = Math.abs((angle - dir + Math.PI * 3) % (Math.PI * 2) - Math.PI);
return diff < Math.PI / 4; // 45deg to either side
}
// Touch controls: drag to move, tap/hold to slash in direction
var dragNode = null;
var dragStartX = 0,
dragStartY = 0;
var dragHeroStartX = 0,
dragHeroStartY = 0;
var lastMoveDir = 0;
// Move handler: drag to move hero, tap/hold to slash
function handleMove(x, y, obj) {
if (!gameStarted) return;
// Don't allow hero to move into top left 100x100
if (x < 120 && y < 120) return;
// Only move hero if dragging (left side touch)
if (isDragging) {
var nx = x - dragOffsetX;
var ny = y - dragOffsetY;
// Clamp to arena
nx = Math.max(ARENA_MARGIN, Math.min(2048 - ARENA_MARGIN, nx));
ny = Math.max(ARENA_MARGIN, Math.min(2732 - ARENA_MARGIN, ny));
hero.x = nx;
hero.y = ny;
lastTouchX = nx;
lastTouchY = ny;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Block all input except menu start until game is started
if (!gameStarted) return;
// Divide screen: left half for movement, right half for attack
if (x < 2048 / 2) {
// Left side: start dragging hero
isDragging = true;
dragOffsetX = x - hero.x;
dragOffsetY = y - hero.y;
} else {
// Right side: attack in direction from hero to touch
var dir = Math.atan2(y - hero.y, x - hero.x);
lastMoveDir = dir;
hero.doSlash(dir);
}
};
game.up = function (x, y, obj) {
if (!gameStarted) return;
isDragging = false;
};
// Main update loop
game.update = function () {
// Block all gameplay until menu is dismissed
if (!gameStarted) {
// Only show menu, do not update game
menuGroup.visible = true;
arenaFloor.visible = false;
return;
} else {
menuGroup.visible = false;
arenaFloor.visible = true;
}
// Update hero
hero.update();
// Update round and enemies left display
roundTxt.setText("Wave " + wave);
if (roundActive) {
enemiesLeftTxt.setText("Enemies left: " + Math.max(ENEMY_MAX - roundKills, 0));
enemiesLeftTxt.visible = true;
} else {
enemiesLeftTxt.setText("");
enemiesLeftTxt.visible = false;
}
// Show round/wave info (optional, can be improved with GUI)
if (!shopActive && !roundActive) {
// Show "Round X Complete!" and trigger shop
shopActive = true;
showShop();
return;
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// If enemy off screen, remove
if (enemy.x < -120 || enemy.x > 2048 + 120 || enemy.y < -120 || enemy.y > 2732 + 120) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// If enemy collides with hero, game over
if (isColliding(enemy, hero, 50, 50)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// If hero is slashing and enemy is within slash radius, destroy enemy
if (hero.slashActive) {
// Hit everywhere in a circular area (e.g. 180px radius)
var dx = enemy.x - hero.x;
var dy = enemy.y - hero.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= 180) {
// Only allow one hit per slash per enemy
if (!enemy.isHit) {
enemy.flashHit();
LK.getSound('hit').play();
// Remove after short delay for flash
(function (e, idx) {
LK.setTimeout(function () {
if (enemies.indexOf(e) !== -1) {
e.destroy();
enemies.splice(enemies.indexOf(e), 1);
// Score
score++;
roundKills++;
scoreTxt.setText(score);
// Check if round is complete
if (roundKills >= ENEMY_MAX) {
roundActive = false;
}
}
}, 80);
})(enemy, i);
}
}
}
}
// Only spawn enemies if round is active and shop is not open
if (roundActive && !shopActive) {
ticksSinceSpawn++;
var spawnRate = Math.max(ENEMY_SPAWN_INTERVAL - (wave - 1) * 4, 24); // faster per wave
if (ticksSinceSpawn >= spawnRate && enemies.length < ENEMY_MAX) {
spawnEnemy();
ticksSinceSpawn = 0;
}
}
// If round is not active and shop is not open, trigger shop
if (!roundActive && !shopActive) {
shopActive = true;
showShop();
}
// Show wave/round info (optional, can be improved with GUI)
};
Give me an animation of a hit.. In-Game asset. 2d. High contrast. No shadows
Create a pixel art turkish character. In-Game asset. 2d. High contrast. No shadows
draw me zombie enemie. In-Game asset. 2d. High contrast. No shadows
draw me pixel art sword. In-Game asset. 2d. High contrast. No shadows
place floor draw me. In-Game asset. 2d. High contrast. No shadows