User prompt
add ammo system
User prompt
add cheats tab
User prompt
deleate asste from classic menu
User prompt
add new asset to main menu
User prompt
add clasic menu
User prompt
remove main menu and add clasic menu
User prompt
add main menu
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'setText')' in or related to this line: 'scoreTxt.setText(score);' Line Number: 320
User prompt
remove character sellenct menu
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var local = menuOverlay.toLocal(obj.parent.toGlobal({' Line Number: 384
User prompt
add character sellect menu
User prompt
Please fix the bug: 'Uncaught ReferenceError: menuActive is not defined' in or related to this line: 'if (menuActive) {' Line Number: 406
User prompt
remove menu
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'fill')' in or related to this line: 'skinLabelNodes[i].style.fill = i === selectedHeroSkin ? "#F7E733" : "#fff";' Line Number: 415
User prompt
add sellect character menu
User prompt
add healt bar
User prompt
add healt system
User prompt
add more zombies and assets
User prompt
add healt bar
User prompt
make wanes and stronger zombies
User prompt
add new asset to gun
User prompt
give a gun to characters hand
User prompt
add a menu
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bulletSprite.width / 2;
self.vx = 0;
self.vy = 0;
self.lifetime = 60; // frames
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.lifetime--;
};
return self;
});
// Hero class
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroSprite = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.heroSprite = heroSprite;
// Add gun sprite to hero's hand
var gunSprite = self.attachAsset('gun', {
anchorX: 0.2,
// grip of gun
anchorY: 0.5,
width: heroSprite.width * 0.45,
height: heroSprite.height * 0.22
});
gunSprite.x = heroSprite.width * 0.32; // offset to right hand
gunSprite.y = 0;
self.radius = heroSprite.width / 2;
self.speed = 18; // pixels per move
self.lastShotTick = 0;
self.shootCooldown = 18; // frames between shots
// Allow skin change
self.setSkin = function (skinId) {
if (!skinId || skinId === "hero") {
// Already default
return;
}
// Remove old sprite
if (self.heroSprite && self.heroSprite.parent) {
self.removeChild(self.heroSprite);
}
// Add new sprite
var newSprite = self.attachAsset(skinId, {
anchorX: 0.5,
anchorY: 0.5,
width: heroSprite.width,
height: heroSprite.height
});
self.heroSprite = newSprite;
// Move gun to top
if (gunSprite && gunSprite.parent) {
self.removeChild(gunSprite);
self.addChild(gunSprite);
}
// Update radius
self.radius = newSprite.width / 2;
};
self.update = function () {};
return self;
});
// Powerup class
var Powerup = Container.expand(function () {
var self = Container.call(this);
var powerupSprite = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = powerupSprite.width / 2;
self.type = 'rapid'; // only one type for MVP
self.update = function () {};
return self;
});
// Zombie class
var Zombie = Container.expand(function () {
var self = Container.call(this);
// Default to normal zombie asset
var zombieSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.zombieSprite = zombieSprite;
self.radius = zombieSprite.width / 2;
self.baseSpeed = 3 + Math.random() * 2; // base speed, will be increased by wave
self.speed = self.baseSpeed;
self.target = null; // set to hero
self.maxHp = 1;
self.hp = 1;
self.wave = 1;
self.type = "normal"; // can be "normal", "fast", "tank", "crawler"
self.setWave = function (wave) {
self.wave = wave;
// Remove old sprite if present
if (self.zombieSprite && self.zombieSprite.parent) {
self.removeChild(self.zombieSprite);
}
// Decide type
var r = Math.random();
if (wave >= 7 && r < 0.18) {
// Crawler: slow, small, hard to hit
self.type = "crawler";
self.speed = 1.7 + 0.12 * wave;
self.maxHp = 2 + Math.floor(wave / 3);
self.hp = self.maxHp;
self.zombieSprite = self.attachAsset('zombie_crawler', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = self.zombieSprite.width / 2;
self.zombieSprite.tint = 0x8e8e8e; // grayish
} else if (wave >= 6 && r < 0.38) {
// Tank zombie: slow but high HP
self.type = "tank";
self.speed = 2 + 0.15 * wave;
self.maxHp = 3 + Math.floor(wave / 2);
self.hp = self.maxHp;
self.zombieSprite = self.attachAsset('zombie_tank', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = self.zombieSprite.width / 2;
self.zombieSprite.tint = 0x6e4e2e; // brownish
} else if (wave >= 3 && r < 0.63) {
// Fast zombie: high speed, low HP
self.type = "fast";
self.speed = 5 + 0.4 * wave;
self.maxHp = 1;
self.hp = 1;
self.zombieSprite = self.attachAsset('zombie_fast', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = self.zombieSprite.width / 2;
self.zombieSprite.tint = 0x7cf442; // bright green
} else {
// Normal zombie
self.type = "normal";
self.speed = self.baseSpeed + (wave - 1) * 0.5;
self.maxHp = 1 + Math.floor(wave / 4);
self.hp = self.maxHp;
self.zombieSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = self.zombieSprite.width / 2;
self.zombieSprite.tint = 0x7bbf3f; // normal green
}
};
// Health bar setup
var healthBarW = zombieSprite.width * 0.7;
var healthBarH = 16;
var healthBarBg = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: healthBarW,
height: healthBarH,
color: 0x222222
});
healthBarBg.alpha = 0.7;
var healthBarFill = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: healthBarW,
height: healthBarH,
color: 0x3FA34D
});
healthBarFill.alpha = 0.92;
self.addChild(healthBarBg);
self.addChild(healthBarFill);
self.update = function () {
if (!self.target) return;
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += self.speed * dx / dist;
self.y += self.speed * dy / dist;
}
// Update health bar position and fill
var barY = -self.radius - 30;
healthBarBg.x = -healthBarW / 2;
healthBarBg.y = barY;
healthBarFill.x = -healthBarW / 2;
healthBarFill.y = barY;
var ratio = Math.max(0, Math.min(1, self.hp / self.maxHp));
healthBarFill.width = healthBarW * ratio;
if (ratio > 0.6) {
healthBarFill.tint = 0x3FA34D;
} else if (ratio > 0.3) {
healthBarFill.tint = 0xF7E733;
} else {
healthBarFill.tint = 0xD83318;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Game area
// Hero: red box
// Zombie: green ellipse
// Bullet: yellow box
// Powerup: blue ellipse
// Sound effects
// Music
// new gun asset
var GAME_W = 2048;
var GAME_H = 2732;
// State
var hero = null;
var zombies = [];
var bullets = [];
var powerups = [];
var dragNode = null;
var lastIntersecting = false;
var score = 0;
var highScore = storage.highScore || 0;
var wave = 1;
var ticksSinceStart = 0;
var zombieSpawnTick = 0;
var zombieSpawnInterval = 90; // frames
var rapidFireTicks = 0;
// --- HEALTH SYSTEM ---
var heroMaxHp = 5;
var heroHp = heroMaxHp;
// Health bar UI
var healthBarBg = new Container();
var healthBarWidth = 520;
var healthBarHeight = 38;
var healthBarBgRect = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: healthBarWidth,
height: healthBarHeight,
color: 0x333333
});
healthBarBgRect.alpha = 0.7;
healthBarBg.addChild(healthBarBgRect);
var healthBarFill = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: healthBarWidth,
height: healthBarHeight,
color: 0x3FA34D
});
healthBarFill.alpha = 0.92;
healthBarBg.addChild(healthBarFill);
healthBarBg.x = (GAME_W - healthBarWidth) / 2;
healthBarBg.y = 110;
LK.gui.top.addChild(healthBarBg);
function updateHealthBar() {
var ratio = Math.max(0, Math.min(1, heroHp / heroMaxHp));
healthBarFill.width = healthBarWidth * ratio;
if (ratio > 0.6) {
healthBarFill.tint = 0x3FA34D;
} else if (ratio > 0.3) {
healthBarFill.tint = 0xF7E733;
} else {
healthBarFill.tint = 0xD83318;
}
}
updateHealthBar();
// --- MENU STATE ---
var menuActive = true;
var menuOverlay = new Container();
var menuBg = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5,
width: 900,
height: 1100,
color: 0x222222
});
menuBg.alpha = 0.92;
menuBg.x = GAME_W / 2;
menuBg.y = GAME_H / 2;
menuOverlay.addChild(menuBg);
var menuTitle = new Text2('Zombie Swarm Survival', {
size: 120,
fill: "#fff"
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = GAME_W / 2;
menuTitle.y = GAME_H / 2 - 350;
menuOverlay.addChild(menuTitle);
var menuDesc = new Text2('Survive as long as you can!\nDrag to move. Tap to shoot.', {
size: 60,
fill: "#fff"
});
menuDesc.anchor.set(0.5, 0.5);
menuDesc.x = GAME_W / 2;
menuDesc.y = GAME_H / 2 - 180;
menuOverlay.addChild(menuDesc);
var menuBest = new Text2('Best: ' + highScore, {
size: 70,
fill: 0xF7E733
});
menuBest.anchor.set(0.5, 0.5);
menuBest.x = GAME_W / 2;
menuBest.y = GAME_H / 2 + 80;
menuOverlay.addChild(menuBest);
// --- CHARACTER SELECTION ---
var heroSkins = [{
id: 'hero',
label: 'Classic',
color: 0xffffff
}, {
id: 'zombie_fast',
label: 'Speedster',
color: 0x7cf442
}, {
id: 'zombie_tank',
label: 'Tank',
color: 0x6e4e2e
}, {
id: 'zombie_crawler',
label: 'Crawler',
color: 0x8e8e8e
}];
var selectedHeroSkin = 0;
// Show hero skin options
var skinPreviewNodes = [];
var skinLabelNodes = [];
var skinY = GAME_H / 2 - 40;
var skinSpacing = 220;
var skinX0 = GAME_W / 2 - (heroSkins.length - 1) * skinSpacing / 2;
for (var i = 0; i < heroSkins.length; i++) {
var skin = heroSkins[i];
var preview = LK.getAsset(skin.id, {
anchorX: 0.5,
anchorY: 0.5,
width: 180,
height: 180
});
preview.x = skinX0 + i * skinSpacing;
preview.y = skinY;
preview.alpha = i === selectedHeroSkin ? 1 : 0.5;
menuOverlay.addChild(preview);
skinPreviewNodes.push(preview);
var label = new Text2(skin.label, {
size: 48,
fill: "#fff"
});
label.anchor.set(0.5, 0);
label.x = preview.x;
label.y = skinY + 110;
label.alpha = i === selectedHeroSkin ? 1 : 0.6;
menuOverlay.addChild(label);
skinLabelNodes.push(label);
}
// Left/right arrows for selection
var arrowLeft = new Text2('<', {
size: 120,
fill: "#fff"
});
arrowLeft.anchor.set(0.5, 0.5);
arrowLeft.x = skinX0 - 100;
arrowLeft.y = skinY;
menuOverlay.addChild(arrowLeft);
var arrowRight = new Text2('>', {
size: 120,
fill: "#fff"
});
arrowRight.anchor.set(0.5, 0.5);
arrowRight.x = skinX0 + (heroSkins.length - 1) * skinSpacing + 100;
arrowRight.y = skinY;
menuOverlay.addChild(arrowRight);
function updateSkinSelection() {
for (var i = 0; i < skinPreviewNodes.length; i++) {
skinPreviewNodes[i].alpha = i === selectedHeroSkin ? 1 : 0.5;
skinLabelNodes[i].alpha = i === selectedHeroSkin ? 1 : 0.6;
skinLabelNodes[i].style.fill = i === selectedHeroSkin ? "#F7E733" : "#fff";
}
}
// Start button
var menuBtn = new Text2('START', {
size: 110,
fill: 0x3FA34D
});
menuBtn.anchor.set(0.5, 0.5);
menuBtn.x = GAME_W / 2;
menuBtn.y = GAME_H / 2 + 350;
menuOverlay.addChild(menuBtn);
game.addChild(menuOverlay);
// --- MENU INTERACTION ---
menuOverlay.down = function (x, y, obj) {
// Arrows
var dxL = x - arrowLeft.x,
dyL = y - arrowLeft.y;
if (dxL * dxL + dyL * dyL < 9000) {
if (selectedHeroSkin > 0) {
selectedHeroSkin--;
updateSkinSelection();
}
return;
}
var dxR = x - arrowRight.x,
dyR = y - arrowRight.y;
if (dxR * dxR + dyR * dyR < 9000) {
if (selectedHeroSkin < heroSkins.length - 1) {
selectedHeroSkin++;
updateSkinSelection();
}
return;
}
// Skin previews (tap to select)
for (var i = 0; i < skinPreviewNodes.length; i++) {
var dx = x - skinPreviewNodes[i].x,
dy = y - skinPreviewNodes[i].y;
if (dx * dx + dy * dy < 110 * 110) {
selectedHeroSkin = i;
updateSkinSelection();
return;
}
}
// Only start if tap is on START button area
var dx = x - menuBtn.x;
var dy = y - menuBtn.y;
if (dx * dx + dy * dy < 40000) {
// radius ~200px
startGame();
}
};
updateSkinSelection();
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// High score text
var highScoreTxt = new Text2('Best: ' + highScore, {
size: 60,
fill: "#fff"
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 120;
LK.gui.top.addChild(highScoreTxt);
// Powerup text
var powerupTxt = new Text2('', {
size: 70,
fill: 0x3B7DD8
});
powerupTxt.anchor.set(0.5, 0);
powerupTxt.y = 200;
LK.gui.top.addChild(powerupTxt);
// Only spawn hero after menu
function startGame() {
menuActive = false;
menuOverlay.visible = false;
// Reset state
score = 0;
wave = 1;
ticksSinceStart = 0;
zombieSpawnTick = 0;
zombieSpawnInterval = 90;
rapidFireTicks = 0;
heroHp = heroMaxHp;
updateHealthBar();
scoreTxt.setText('0');
highScoreTxt.setText('Best: ' + highScore);
powerupTxt.setText('');
// Remove all zombies, bullets, powerups
for (var i = 0; i < zombies.length; i++) zombies[i].destroy();
for (var i = 0; i < bullets.length; i++) bullets[i].destroy();
for (var i = 0; i < powerups.length; i++) powerups[i].destroy();
zombies = [];
bullets = [];
powerups = [];
// Spawn hero in center
if (hero) hero.destroy();
hero = new Hero();
if (typeof heroSkins !== "undefined" && typeof selectedHeroSkin !== "undefined") {
hero.setSkin && hero.setSkin(heroSkins[selectedHeroSkin].id);
}
hero.x = GAME_W / 2;
hero.y = GAME_H / 2;
game.addChild(hero);
}
// Menu tap handler
menuOverlay.down = function (x, y, obj) {
// Only start if tap is on START button area
var dx = x - menuBtn.x;
var dy = y - menuBtn.y;
if (dx * dx + dy * dy < 40000) {
// radius ~200px
startGame();
}
};
// Helper: spawn zombie at random edge
function spawnZombie() {
var z = new Zombie();
// Pick edge: 0=top,1=bottom,2=left,3=right
var edge = Math.floor(Math.random() * 4);
var margin = 80;
if (edge === 0) {
// top
z.x = margin + Math.random() * (GAME_W - 2 * margin);
z.y = -z.radius;
} else if (edge === 1) {
// bottom
z.x = margin + Math.random() * (GAME_W - 2 * margin);
z.y = GAME_H + z.radius;
} else if (edge === 2) {
// left
z.x = -z.radius;
z.y = margin + Math.random() * (GAME_H - 2 * margin);
} else {
// right
z.x = GAME_W + z.radius;
z.y = margin + Math.random() * (GAME_H - 2 * margin);
}
// Set wave and type
if (typeof z.setWave === "function") {
z.setWave(wave);
}
z.target = hero;
zombies.push(z);
game.addChild(z);
}
// Helper: spawn powerup
function spawnPowerup() {
var p = new Powerup();
p.x = 200 + Math.random() * (GAME_W - 400);
p.y = 300 + Math.random() * (GAME_H - 600);
powerups.push(p);
game.addChild(p);
}
// Helper: shoot bullet towards (tx,ty)
function shootBullet(tx, ty) {
if (LK.ticks - hero.lastShotTick < (rapidFireTicks > 0 ? 6 : hero.shootCooldown)) return;
var b = new Bullet();
b.x = hero.x;
b.y = hero.y;
var dx = tx - hero.x;
var dy = ty - hero.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 38;
b.vx = speed * dx / dist;
b.vy = speed * dy / dist;
bullets.push(b);
game.addChild(b);
hero.lastShotTick = LK.ticks;
LK.getSound('shoot').play();
}
// Helper: distance between two objects
function dist2(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Dragging
function handleMove(x, y, obj) {
if (menuActive) return;
if (dragNode) {
// Clamp hero inside game area
var r = hero.radius;
dragNode.x = Math.max(r, Math.min(GAME_W - r, x));
dragNode.y = Math.max(r, Math.min(GAME_H - r, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (menuActive) return;
// Only drag if touch is on hero
if (dist2({
x: x,
y: y
}, hero) <= hero.radius * 1.2) {
dragNode = hero;
}
};
game.up = function (x, y, obj) {
if (menuActive) return;
dragNode = null;
// Shoot bullet towards release point if not dragging
if (dist2({
x: x,
y: y
}, hero) > hero.radius * 1.2) {
shootBullet(x, y);
}
};
// Main update loop
game.update = function () {
if (menuActive) {
// Block all gameplay updates while menu is active
return;
}
ticksSinceStart++;
// Increase wave every 900 ticks (~15s)
if (ticksSinceStart % 900 === 0) {
wave++;
if (zombieSpawnInterval > 30) zombieSpawnInterval -= 8;
}
// Spawn zombies
if (LK.ticks - zombieSpawnTick > zombieSpawnInterval) {
// More zombies per spawn as wave increases
var zombiesToSpawn = 1 + Math.floor(wave / 4);
for (var n = 0; n < zombiesToSpawn; n++) {
spawnZombie();
}
zombieSpawnTick = LK.ticks;
}
// Maybe spawn powerup
if (powerups.length === 0 && Math.random() < 0.002) {
spawnPowerup();
}
// Update hero (nothing for now)
hero.update();
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var z = zombies[i];
z.update();
// Check collision with hero
if (dist2(z, hero) < z.radius + hero.radius - 10) {
LK.effects.flashScreen(0xff0000, 400);
heroHp--;
updateHealthBar();
if (heroHp <= 0) {
if (score > highScore) {
storage.highScore = score;
}
LK.showGameOver();
// Show menu again after game over
menuActive = true;
menuOverlay.visible = true;
menuBest.setText('Best: ' + (score > highScore ? score : highScore));
return;
}
// Remove zombie on hit to prevent instant double hit
z.destroy();
zombies.splice(i, 1);
continue;
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
// Remove if out of bounds or expired
if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100 || b.lifetime <= 0) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with zombies
for (var j = zombies.length - 1; j >= 0; j--) {
var z = zombies[j];
if (dist2(b, z) < b.radius + z.radius - 10) {
// Damage zombie
if (typeof z.hp === "number") {
z.hp--;
LK.effects.flashObject(z, 0xffffff, 120);
if (z.hp <= 0) {
LK.getSound('zombie_die').play();
score++;
scoreTxt.setText(score);
if (score > highScore) {
highScore = score;
highScoreTxt.setText('Best: ' + highScore);
storage.highScore = highScore;
}
z.destroy();
zombies.splice(j, 1);
}
} else {
// fallback: destroy if no hp
LK.getSound('zombie_die').play();
score++;
scoreTxt.setText(score);
if (score > highScore) {
highScore = score;
highScoreTxt.setText('Best: ' + highScore);
storage.highScore = highScore;
}
z.destroy();
zombies.splice(j, 1);
}
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
// Check collision with hero
if (dist2(p, hero) < p.radius + hero.radius - 10) {
LK.getSound('powerup').play();
rapidFireTicks = 360; // 6 seconds
powerupTxt.setText('Rapid Fire!');
LK.effects.flashObject(hero, 0x3b7dd8, 400);
p.destroy();
powerups.splice(i, 1);
}
}
// Powerup timer
if (rapidFireTicks > 0) {
rapidFireTicks--;
if (rapidFireTicks === 0) {
powerupTxt.setText('');
}
}
// Update powerup text alpha for effect
if (rapidFireTicks > 0) {
powerupTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 6);
} else {
powerupTxt.alpha = 1;
}
};
// Start music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.7,
duration: 1200
}
}); ===================================================================
--- original.js
+++ change.js
@@ -31,8 +31,9 @@
var heroSprite = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
+ self.heroSprite = heroSprite;
// Add gun sprite to hero's hand
var gunSprite = self.attachAsset('gun', {
anchorX: 0.2,
// grip of gun
@@ -45,8 +46,34 @@
self.radius = heroSprite.width / 2;
self.speed = 18; // pixels per move
self.lastShotTick = 0;
self.shootCooldown = 18; // frames between shots
+ // Allow skin change
+ self.setSkin = function (skinId) {
+ if (!skinId || skinId === "hero") {
+ // Already default
+ return;
+ }
+ // Remove old sprite
+ if (self.heroSprite && self.heroSprite.parent) {
+ self.removeChild(self.heroSprite);
+ }
+ // Add new sprite
+ var newSprite = self.attachAsset(skinId, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: heroSprite.width,
+ height: heroSprite.height
+ });
+ self.heroSprite = newSprite;
+ // Move gun to top
+ if (gunSprite && gunSprite.parent) {
+ self.removeChild(gunSprite);
+ self.addChild(gunSprite);
+ }
+ // Update radius
+ self.radius = newSprite.width / 2;
+ };
self.update = function () {};
return self;
});
// Powerup class
@@ -194,16 +221,16 @@
/****
* Game Code
****/
-// new gun asset
-// Music
-// Sound effects
-// Powerup: blue ellipse
-// Bullet: yellow box
-// Zombie: green ellipse
-// Hero: red box
// Game area
+// Hero: red box
+// Zombie: green ellipse
+// Bullet: yellow box
+// Powerup: blue ellipse
+// Sound effects
+// Music
+// new gun asset
var GAME_W = 2048;
var GAME_H = 2732;
// State
var hero = null;
@@ -265,9 +292,9 @@
var menuBg = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5,
width: 900,
- height: 900,
+ height: 1100,
color: 0x222222
});
menuBg.alpha = 0.92;
menuBg.x = GAME_W / 2;
@@ -278,17 +305,17 @@
fill: "#fff"
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = GAME_W / 2;
-menuTitle.y = GAME_H / 2 - 250;
+menuTitle.y = GAME_H / 2 - 350;
menuOverlay.addChild(menuTitle);
var menuDesc = new Text2('Survive as long as you can!\nDrag to move. Tap to shoot.', {
size: 60,
fill: "#fff"
});
menuDesc.anchor.set(0.5, 0.5);
menuDesc.x = GAME_W / 2;
-menuDesc.y = GAME_H / 2 - 80;
+menuDesc.y = GAME_H / 2 - 180;
menuOverlay.addChild(menuDesc);
var menuBest = new Text2('Best: ' + highScore, {
size: 70,
fill: 0xF7E733
@@ -296,17 +323,131 @@
menuBest.anchor.set(0.5, 0.5);
menuBest.x = GAME_W / 2;
menuBest.y = GAME_H / 2 + 80;
menuOverlay.addChild(menuBest);
+// --- CHARACTER SELECTION ---
+var heroSkins = [{
+ id: 'hero',
+ label: 'Classic',
+ color: 0xffffff
+}, {
+ id: 'zombie_fast',
+ label: 'Speedster',
+ color: 0x7cf442
+}, {
+ id: 'zombie_tank',
+ label: 'Tank',
+ color: 0x6e4e2e
+}, {
+ id: 'zombie_crawler',
+ label: 'Crawler',
+ color: 0x8e8e8e
+}];
+var selectedHeroSkin = 0;
+// Show hero skin options
+var skinPreviewNodes = [];
+var skinLabelNodes = [];
+var skinY = GAME_H / 2 - 40;
+var skinSpacing = 220;
+var skinX0 = GAME_W / 2 - (heroSkins.length - 1) * skinSpacing / 2;
+for (var i = 0; i < heroSkins.length; i++) {
+ var skin = heroSkins[i];
+ var preview = LK.getAsset(skin.id, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 180,
+ height: 180
+ });
+ preview.x = skinX0 + i * skinSpacing;
+ preview.y = skinY;
+ preview.alpha = i === selectedHeroSkin ? 1 : 0.5;
+ menuOverlay.addChild(preview);
+ skinPreviewNodes.push(preview);
+ var label = new Text2(skin.label, {
+ size: 48,
+ fill: "#fff"
+ });
+ label.anchor.set(0.5, 0);
+ label.x = preview.x;
+ label.y = skinY + 110;
+ label.alpha = i === selectedHeroSkin ? 1 : 0.6;
+ menuOverlay.addChild(label);
+ skinLabelNodes.push(label);
+}
+// Left/right arrows for selection
+var arrowLeft = new Text2('<', {
+ size: 120,
+ fill: "#fff"
+});
+arrowLeft.anchor.set(0.5, 0.5);
+arrowLeft.x = skinX0 - 100;
+arrowLeft.y = skinY;
+menuOverlay.addChild(arrowLeft);
+var arrowRight = new Text2('>', {
+ size: 120,
+ fill: "#fff"
+});
+arrowRight.anchor.set(0.5, 0.5);
+arrowRight.x = skinX0 + (heroSkins.length - 1) * skinSpacing + 100;
+arrowRight.y = skinY;
+menuOverlay.addChild(arrowRight);
+function updateSkinSelection() {
+ for (var i = 0; i < skinPreviewNodes.length; i++) {
+ skinPreviewNodes[i].alpha = i === selectedHeroSkin ? 1 : 0.5;
+ skinLabelNodes[i].alpha = i === selectedHeroSkin ? 1 : 0.6;
+ skinLabelNodes[i].style.fill = i === selectedHeroSkin ? "#F7E733" : "#fff";
+ }
+}
+// Start button
var menuBtn = new Text2('START', {
size: 110,
fill: 0x3FA34D
});
menuBtn.anchor.set(0.5, 0.5);
menuBtn.x = GAME_W / 2;
-menuBtn.y = GAME_H / 2 + 250;
+menuBtn.y = GAME_H / 2 + 350;
menuOverlay.addChild(menuBtn);
game.addChild(menuOverlay);
+// --- MENU INTERACTION ---
+menuOverlay.down = function (x, y, obj) {
+ // Arrows
+ var dxL = x - arrowLeft.x,
+ dyL = y - arrowLeft.y;
+ if (dxL * dxL + dyL * dyL < 9000) {
+ if (selectedHeroSkin > 0) {
+ selectedHeroSkin--;
+ updateSkinSelection();
+ }
+ return;
+ }
+ var dxR = x - arrowRight.x,
+ dyR = y - arrowRight.y;
+ if (dxR * dxR + dyR * dyR < 9000) {
+ if (selectedHeroSkin < heroSkins.length - 1) {
+ selectedHeroSkin++;
+ updateSkinSelection();
+ }
+ return;
+ }
+ // Skin previews (tap to select)
+ for (var i = 0; i < skinPreviewNodes.length; i++) {
+ var dx = x - skinPreviewNodes[i].x,
+ dy = y - skinPreviewNodes[i].y;
+ if (dx * dx + dy * dy < 110 * 110) {
+ selectedHeroSkin = i;
+ updateSkinSelection();
+ return;
+ }
+ }
+ // Only start if tap is on START button area
+ var dx = x - menuBtn.x;
+ var dy = y - menuBtn.y;
+ if (dx * dx + dy * dy < 40000) {
+ // radius ~200px
+ startGame();
+ }
+};
+updateSkinSelection();
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
@@ -354,8 +495,11 @@
powerups = [];
// Spawn hero in center
if (hero) hero.destroy();
hero = new Hero();
+ if (typeof heroSkins !== "undefined" && typeof selectedHeroSkin !== "undefined") {
+ hero.setSkin && hero.setSkin(heroSkins[selectedHeroSkin].id);
+ }
hero.x = GAME_W / 2;
hero.y = GAME_H / 2;
game.addChild(hero);
}
zombie. In-Game asset. 2d. High contrast. No shadows
bullet. In-Game asset. 2d. High contrast. No shadows
power up. In-Game asset. 2d. High contrast. No shadows
pistol. In-Game asset. 2d. High contrast. No shadows
crawler zombie. In-Game asset. 2d. High contrast. No shadows
runner zombie. In-Game asset. 2d. High contrast. No shadows
tank zombie. In-Game asset. 2d. High contrast. No shadows
man. In-Game asset. 2d. High contrast. No shadows