User prompt
When choosing a plugin, creatures should not cause damage, and add more plugins, add plugins that give area damage, etc., and add a background model, I will change it. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
They die without touching me. Add a bullet model and when choosing an add-on, the creatures should not cause damage.
User prompt
There is a level up tab, add it as a model, I will change it and the bullet model should go directly to the man, not as a straight png, but straight
User prompt
add a level screen to show how many levels we have or how much of the other level is left and add an assets screen compatible with the police
User prompt
The issue is that when we get a skill, it should not go to living beings, it should come only to us, and also to living beings with weapons in their hands, and it should slow down the very fast speeds of the living beings a little.
User prompt
Reduce the number of living creatures coming, but increase it by 1 click after 3 minutes, so that it constantly uses weapons, and the gun barrel points to living creatures.
User prompt
Let the gun barrel also look at the creatures, and while you buy the add-ons, the game should pause, and the creatures should be a little smarter, and the creatures should come in stages.
User prompt
Let the gun be visible in our hand and have a bullet model when firing
User prompt
Let the creatures come a little later and give us a weapon model
User prompt
Please fix the bug: 'prepareWave is not defined' in or related to this line: 'prepareWave();' Line Number: 758
User prompt
Knk, look, creatures should behave a little differently from each other because when creatures are gathered somewhere and revolve around them, they cannot do anything. Also, they should come continuously, not in waves, but continuously and without stopping. Also, the game should wait like this while purchasing add-ons.
User prompt
Knk, look, the creatures should behave a little differently from each other because when you gather the creatures somewhere, for example, they cannot do anything when they revolve around you. Also, they should come continuously, not in waves, but continuously and without stopping. Also, the game should wait like this while purchasing add-ons.
User prompt
Let it be a continuous live stream and add something like artificial intelligence to the creatures so that it is not so stupid
User prompt
Don't let the creatures come all at once, let them come continuously and expand the playing area a little.
User prompt
Don't let other creatures come so much, let them increase over time, and make sure that almost no bullets miss. Make a better weapon's mechanic to turn into creatures.
Code edit (1 edits merged)
Please save this source code
Initial prompt
Beast Survivors
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Creature projectile class with unique behaviors
var CreatureProjectile = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('bulletModel', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 32;
self.speed = 32;
self.angle = 0;
self.damage = 1;
// Assign a unique behavior type to each projectile
var behaviorTypes = ['homing', 'zigzag', 'boomerang', 'spiral', 'dash', 'orbit', 'bounce'];
self.behavior = behaviorTypes[Math.floor(Math.random() * behaviorTypes.length)];
self.behaviorSeed = Math.random() * 10000;
self.behaviorTick = 0;
self.originX = self.x;
self.originY = self.y;
self.maxDistance = 1200 + Math.random() * 400;
self.orbitAngle = Math.random() * Math.PI * 2;
self.bounceCount = 0;
self.update = function () {
self.behaviorTick++;
// --- Unique behaviors ---
if (self.behavior === 'homing') {
// Homing: as before, but with a little more erratic movement
var nearest = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
var dx = e.x - self.x;
var dy = e.y - self.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < minDist) {
minDist = d;
nearest = e;
}
}
if (nearest && minDist > 1) {
var lead = 0.18 + Math.random() * 0.08;
var ex = nearest.x,
ey = nearest.y;
if (typeof nearest.lastX === "number" && typeof nearest.lastY === "number") {
var vx = nearest.x - nearest.lastX;
var vy = nearest.y - nearest.lastY;
ex += vx * lead * (minDist / 64);
ey += vy * lead * (minDist / 64);
}
var targetAngle = Math.atan2(ey - self.y, ex - self.x);
// Add a little random wiggle
targetAngle += Math.sin(self.behaviorTick * 0.13 + self.behaviorSeed) * 0.12;
// Smoothly turn toward target
var da = targetAngle - self.angle;
while (da > Math.PI) da -= Math.PI * 2;
while (da < -Math.PI) da += Math.PI * 2;
var turnRate = 0.18 + Math.min(0.32, minDist / 400 * 0.25);
self.angle += da * turnRate;
}
// Move forward
self.x += Math.cos(self.angle) * self.speed;
self.y += Math.sin(self.angle) * self.speed;
} else if (self.behavior === 'zigzag') {
// Zigzag: moves forward, but oscillates side to side
var freq = 0.18 + self.behaviorSeed % 0.2;
var amp = 48 + self.behaviorSeed % 32;
var zig = Math.sin(self.behaviorTick * freq + self.behaviorSeed) * amp;
self.x += Math.cos(self.angle) * self.speed + Math.cos(self.angle + Math.PI / 2) * zig * 0.08;
self.y += Math.sin(self.angle) * self.speed + Math.sin(self.angle + Math.PI / 2) * zig * 0.08;
} else if (self.behavior === 'boomerang') {
// Boomerang: flies out, then returns to player
var t = self.behaviorTick;
var outTime = 32 + self.behaviorSeed % 32;
if (t < outTime) {
self.x += Math.cos(self.angle) * self.speed;
self.y += Math.sin(self.angle) * self.speed;
} else {
// Return to player
var dx = player.x - self.x;
var dy = player.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += dx / dist * self.speed * 1.2;
self.y += dy / dist * self.speed * 1.2;
}
}
} else if (self.behavior === 'spiral') {
// Spiral: moves in a spiral pattern
var spiralSpeed = self.speed * 0.7;
var spiralRadius = 80 + self.behaviorSeed % 40;
var spiralAngle = self.angle + self.behaviorTick * 0.13;
self.x += Math.cos(self.angle) * spiralSpeed + Math.cos(spiralAngle) * spiralRadius * 0.04;
self.y += Math.sin(self.angle) * spiralSpeed + Math.sin(spiralAngle) * spiralRadius * 0.04;
} else if (self.behavior === 'dash') {
// Dash: moves very fast in a straight line, then slows and homes
if (self.behaviorTick < 18) {
self.x += Math.cos(self.angle) * self.speed * 2.2;
self.y += Math.sin(self.angle) * self.speed * 2.2;
} else {
// Home in after dash
var nearest = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
var dx = e.x - self.x;
var dy = e.y - self.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < minDist) {
minDist = d;
nearest = e;
}
}
if (nearest && minDist > 1) {
var targetAngle = Math.atan2(nearest.y - self.y, nearest.x - self.x);
var da = targetAngle - self.angle;
while (da > Math.PI) da -= Math.PI * 2;
while (da < -Math.PI) da += Math.PI * 2;
self.angle += da * 0.22;
}
self.x += Math.cos(self.angle) * self.speed * 0.8;
self.y += Math.sin(self.angle) * self.speed * 0.8;
}
} else if (self.behavior === 'orbit') {
// Orbit: circles around the player for a while, then launches at nearest enemy
if (self.behaviorTick < 36 + self.behaviorSeed % 24) {
self.orbitAngle += 0.18 + self.behaviorSeed % 0.1;
var orbitRadius = 180 + self.behaviorSeed % 60;
self.x = player.x + Math.cos(self.orbitAngle) * orbitRadius;
self.y = player.y + Math.sin(self.orbitAngle) * orbitRadius;
} else {
var nearest = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
var dx = e.x - self.x;
var dy = e.y - self.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < minDist) {
minDist = d;
nearest = e;
}
}
if (nearest && minDist > 1) {
var targetAngle = Math.atan2(nearest.y - self.y, nearest.x - self.x);
self.angle = targetAngle;
}
self.x += Math.cos(self.angle) * self.speed * 1.2;
self.y += Math.sin(self.angle) * self.speed * 1.2;
}
} else if (self.behavior === 'bounce') {
// Bounce: ricochets off the play area edges a few times
self.x += Math.cos(self.angle) * self.speed;
self.y += Math.sin(self.angle) * self.speed;
var bounced = false;
if (self.x < self.radius) {
self.x = self.radius;
self.angle = Math.PI - self.angle;
bounced = true;
}
if (self.x > GAME_WIDTH - self.radius) {
self.x = GAME_WIDTH - self.radius;
self.angle = Math.PI - self.angle;
bounced = true;
}
if (self.y < self.radius) {
self.y = self.radius;
self.angle = -self.angle;
bounced = true;
}
if (self.y > GAME_HEIGHT - self.radius) {
self.y = GAME_HEIGHT - self.radius;
self.angle = -self.angle;
bounced = true;
}
if (bounced) self.bounceCount++;
if (self.bounceCount > 3) {
self.destroy();
}
}
// Save last position for AI
self.lastX = self.x;
self.lastY = self.y;
// Remove projectile if it has traveled too far from its origin
var dx0 = self.x - self.originX;
var dy0 = self.y - self.originY;
if (dx0 * dx0 + dy0 * dy0 > self.maxDistance * self.maxDistance) {
self.destroy();
}
};
return self;
});
// Creature weapon class (auto-attacks for player)
var CreatureWeapon = Container.expand(function () {
var self = Container.call(this);
// Attach asset (box, yellow)
var asset = self.attachAsset('creature', {
anchorX: 0.5,
anchorY: 0.5
});
// Attach weapon model (sword) as a visual
var weaponModel = self.attachAsset('weaponModel', {
anchorX: 0.5,
anchorY: 0.8
});
weaponModel.y = 60; // Offset so it appears in front of the player
weaponModel.rotation = Math.PI / 8 * (Math.random() > 0.5 ? 1 : -1); // Slight random angle for variety
weaponModel.alpha = 0.85;
// Weapon stats
self.damage = 1;
self.speed = 32;
self.cooldown = 60; // frames between shots
self.timer = 0;
self.projectileCount = 1;
self.spread = 0.2; // radians
// Update method: fires projectiles if timer is up
self.update = function () {
if (self.timer > 0) {
self.timer--;
} else {
self.fire();
self.timer = self.cooldown;
}
};
// Fire projectiles in a spread
self.fire = function () {
// Projectiles orbit and launch from around the player
var now = Date.now();
for (var i = 0; i < self.projectileCount; i++) {
// Orbit offset for each projectile
var orbitAngle = 2 * Math.PI / self.projectileCount * i + now % 1000 / 1000 * Math.PI * 2;
var launchDist = 120 + Math.random() * 30;
var px = player.x + Math.cos(orbitAngle) * launchDist;
var py = player.y + Math.sin(orbitAngle) * launchDist;
// Clamp projectile spawn to play area
if (px < 0) px = 0;
if (px > GAME_WIDTH) px = GAME_WIDTH;
if (py < 0) py = 0;
if (py > GAME_HEIGHT) py = GAME_HEIGHT;
// Aim at nearest enemy if possible
var target = null;
var minDist = 999999;
for (var j = 0; j < enemies.length; j++) {
var e = enemies[j];
var dx = e.x - px;
var dy = e.y - py;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < minDist) {
minDist = d;
target = e;
}
}
var angle;
if (target) {
angle = Math.atan2(target.y - py, target.x - px);
} else {
angle = orbitAngle;
}
var proj = new CreatureProjectile();
proj.x = px;
proj.y = py;
proj.angle = angle;
proj.speed = self.speed;
proj.damage = self.damage;
projectiles.push(proj);
game.addChild(proj);
}
};
// Upgrade weapon
self.upgrade = function (type) {
if (type === 'damage') self.damage++;
if (type === 'speed') self.speed += 6;
if (type === 'cooldown' && self.cooldown > 20) self.cooldown -= 8;
if (type === 'projectile') self.projectileCount++;
if (type === 'spread') self.spread += 0.1;
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 48;
// Slow down very fast speeds of living beings (enemies)
self.speed = 6 + Math.random() * 2.2; // reduced max speed from 9 to 8.2
if (self.speed > 8) self.speed = 8; // clamp to max 8
self.hp = 2 + Math.floor(wave * 0.5);
self.maxHp = self.hp;
// For movement
self.update = function () {
// Save last position for AI
self.lastX = self.x;
self.lastY = self.y;
// Move towards player
var dx = player.x - self.x;
var dy = player.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;
}
};
// Take damage
self.takeDamage = function (amount) {
self.hp -= amount;
LK.effects.flashObject(self, 0xffff00, 200);
if (self.hp <= 0) {
spawnExp(self.x, self.y, 1 + Math.floor(wave * 0.2));
self.destroy();
return true;
}
return false;
};
return self;
});
// Experience gem class
var ExpGem = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('expGem', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 28;
self.value = 1;
self.update = function () {
// Move towards player if close
var dx = player.x - self.x;
var dy = player.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var moveTarget = null;
var minDist = dist;
if (dist < 300) {
moveTarget = player;
minDist = dist;
}
// Also move towards living beings (enemies) that have weapons in their hands
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
// Assume enemies with weapons have a property hasWeapon === true
if (e.hasWeapon) {
var edx = e.x - self.x;
var edy = e.y - self.y;
var edist = Math.sqrt(edx * edx + edy * edy);
if (edist < 300 && edist < minDist) {
moveTarget = e;
minDist = edist;
}
}
}
if (moveTarget && minDist < 300) {
var mdx = moveTarget.x - self.x;
var mdy = moveTarget.y - self.y;
var mlen = Math.sqrt(mdx * mdx + mdy * mdy);
if (mlen > 0) {
self.x += mdx / mlen * 18;
self.y += mdy / mlen * 18;
}
}
};
return self;
});
// Player character class
var Player = Container.expand(function () {
var self = Container.call(this);
// Attach player asset (ellipse, green)
var playerAsset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Attach weapon model (gun) to player's hand
var gunModel = self.attachAsset('weaponModel', {
anchorX: 0.2,
// grip point
anchorY: 0.7
});
gunModel.x = 38; // offset to player's right hand
gunModel.y = 38;
gunModel.rotation = Math.PI / 8;
gunModel.alpha = 0.95;
self.gunModel = gunModel;
// Set up player stats
self.radius = 60; // for collision
self.speed = 18; // movement speed per frame
self.level = 1;
self.exp = 0;
self.expToLevel = 10;
self.hp = 3;
self.maxHp = 3;
// For movement smoothing
self.targetX = GAME_WIDTH / 2;
self.targetY = GAME_HEIGHT / 2;
// For invulnerability after hit
self.invulnTicks = 0;
// Update method
self.update = function () {
// Smoothly move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > self.speed) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
} else {
self.x = self.targetX;
self.y = self.targetY;
}
// Clamp to game area
if (self.x < self.radius) self.x = self.radius;
if (self.x > GAME_WIDTH - self.radius) self.x = GAME_WIDTH - self.radius;
if (self.y < self.radius) self.y = self.radius;
if (self.y > GAME_HEIGHT - self.radius) self.y = GAME_HEIGHT - self.radius;
// Invulnerability timer
if (self.invulnTicks > 0) {
self.invulnTicks--;
playerAsset.alpha = self.invulnTicks % 10 < 5 ? 0.5 : 1;
} else {
playerAsset.alpha = 1;
}
// Update gun model to follow hand (right side of player)
if (self.gunModel) {
// Gun follows player's facing direction (right side)
self.gunModel.x = self.radius * 0.55;
self.gunModel.y = self.radius * 0.25;
// Find nearest living creature (enemy)
var nearest = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
var dx = e.x - (self.x + self.gunModel.x);
var dy = e.y - (self.y + self.gunModel.y);
var d = Math.sqrt(dx * dx + dy * dy);
if (d < minDist) {
minDist = d;
nearest = e;
}
}
if (nearest) {
// Point gun barrel at nearest enemy
var dx = nearest.x - (self.x + self.gunModel.x);
var dy = nearest.y - (self.y + self.gunModel.y);
self.gunModel.rotation = Math.atan2(dy, dx);
} else {
// Optionally, add a little bobbing for effect if no enemy
self.gunModel.rotation = Math.PI / 8 + Math.sin(LK.ticks * 0.08) * 0.08;
}
}
};
// Level up
self.gainExp = function (amount) {
self.exp += amount;
while (self.exp >= self.expToLevel) {
self.exp -= self.expToLevel;
self.level++;
self.expToLevel = Math.floor(self.expToLevel * 1.3 + 5);
showLevelUp();
}
updateExpBar();
};
// Take damage
self.takeDamage = function () {
if (self.invulnTicks > 0) return;
self.hp--;
updateHpBar();
self.invulnTicks = 60;
LK.effects.flashObject(self, 0xff0000, 500);
if (self.hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Weapon model asset (simple sword shape)
// --- Global variables ---
var player;
var creatureWeapon;
var projectiles = [];
var enemies = [];
var expGems = [];
var wave = 1; // still used for scaling difficulty
var dragNode = null;
// --- GUI Elements ---
var scoreTxt = new Text2('0', {
size: 100,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var expBar = new Text2('', {
size: 60,
fill: 0xAAFF00
});
expBar.anchor.set(0.5, 0);
LK.gui.top.addChild(expBar);
var hpBar = new Text2('', {
size: 60,
fill: 0xFF4444
});
hpBar.anchor.set(0.5, 0);
LK.gui.top.addChild(hpBar);
function updateExpBar() {
expBar.setText('LV ' + player.level + ' EXP: ' + player.exp + '/' + player.expToLevel);
}
function updateHpBar() {
hpBar.setText('HP: ' + player.hp + '/' + player.maxHp);
}
function updateScore() {
scoreTxt.setText('Wave: ' + wave);
}
// --- Asset initialization (shapes) ---
// --- Game setup ---
player = new Player();
// Expanded play area
var GAME_WIDTH = 3072;
var GAME_HEIGHT = 4096;
player.x = GAME_WIDTH / 2;
player.y = GAME_HEIGHT / 2;
game.addChild(player);
creatureWeapon = new CreatureWeapon();
game.addChild(creatureWeapon);
updateExpBar();
updateHpBar();
updateScore();
// --- Level up popup ---
var levelUpPopup = null;
function showLevelUp() {
if (levelUpPopup) return; // Already showing
// Randomly pick 3 upgrades
var upgrades = [{
type: 'damage',
label: 'Creature Damage +'
}, {
type: 'speed',
label: 'Creature Speed +'
}, {
type: 'cooldown',
label: 'Faster Attacks'
}, {
type: 'projectile',
label: 'More Projectiles'
}, {
type: 'spread',
label: 'Wider Spread'
}];
// Shuffle and pick 3
for (var i = upgrades.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = upgrades[i];
upgrades[i] = upgrades[j];
upgrades[j] = t;
}
var options = upgrades.slice(0, 3);
// Popup container
levelUpPopup = new Container();
var bg = LK.getAsset('levelUpBg', {
width: 900,
height: 700,
color: 0x222244,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
levelUpPopup.addChild(bg);
bg.x = 0;
bg.y = 0;
var title = new Text2('LEVEL UP!', {
size: 120,
fill: "#fff"
});
title.anchor.set(0.5, 0);
title.x = 0;
title.y = -300;
levelUpPopup.addChild(title);
// Option buttons
for (var i = 0; i < 3; i++) {
(function (idx) {
var opt = options[idx];
var btn = LK.getAsset('levelUpBtn' + idx, {
width: 700,
height: 140,
color: 0x4444aa,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
btn.x = 0;
btn.y = -80 + idx * 180;
levelUpPopup.addChild(btn);
var txt = new Text2(opt.label, {
size: 70,
fill: "#fff"
});
txt.anchor.set(0.5, 0.5);
txt.x = 0;
txt.y = btn.y;
levelUpPopup.addChild(txt);
btn.down = function (x, y, obj) {
creatureWeapon.upgrade(opt.type);
// Remove popup
LK.gui.center.removeChild(levelUpPopup);
levelUpPopup = null;
};
})(i);
}
levelUpPopup.x = 0;
levelUpPopup.y = 0;
LK.gui.center.addChild(levelUpPopup);
}
// --- Spawn experience gem ---
function spawnExp(x, y, value) {
var gem = new ExpGem();
gem.x = x;
gem.y = y;
gem.value = value;
expGems.push(gem);
game.addChild(gem);
}
// --- Continuous enemy spawn system ---
var spawnDelay = 32; // frames between spawns, will decrease as wave increases
var spawnDelayTimer = 0;
// Add a delay before the first enemy spawns
var initialSpawnDelay = 120; // 2 seconds at 60fps
var initialSpawnDelayTimer = 0;
// Limit for max living enemies, starts low and increases over time
var maxLivingEnemies = 3;
var maxLivingEnemiesIncreaseInterval = 60 * 60 * 3; // 3 minutes at 60fps
var lastMaxLivingEnemiesIncreaseTick = 0;
function updateSpawnRate() {
// Increase spawn rate over time, but clamp to a minimum
spawnDelay = Math.max(4, 32 - Math.floor(wave * 1.2));
}
function spawnEnemy() {
if (enemies.length >= maxLivingEnemies) return; // Don't spawn if at max
var enemy = new Enemy();
// Give some enemies weapons in their hands (e.g. 30% chance)
enemy.hasWeapon = Math.random() < 0.3;
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
enemy.x = Math.random() * GAME_WIDTH;
enemy.y = -100;
} else if (edge === 1) {
// bottom
enemy.x = Math.random() * GAME_WIDTH;
enemy.y = GAME_HEIGHT + 100;
} else if (edge === 2) {
// left
enemy.x = -100;
enemy.y = Math.random() * GAME_HEIGHT;
} else {
// right
enemy.x = GAME_WIDTH + 100;
enemy.y = Math.random() * GAME_HEIGHT;
}
enemies.push(enemy);
game.addChild(enemy);
}
// --- Game move handler (touch/mouse drag to move player) ---
function handleMove(x, y, obj) {
// Don't allow movement if level up popup is open
if (levelUpPopup) return;
// Clamp to avoid top left menu
if (x < 100) x = 100;
if (y < 0) y = 0;
player.targetX = x;
player.targetY = y;
}
game.move = handleMove;
game.down = function (x, y, obj) {
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
// No dragNode needed, movement is always allowed
};
// --- Main game update loop ---
game.update = function () {
// Pause all game logic except GUI and popup when level up popup is open
if (levelUpPopup) {
// Only update GUI, skip all gameplay logic
return;
}
// Update player
player.update();
// Update weapon
creatureWeapon.update();
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var proj = projectiles[i];
proj.update();
// Remove if out of bounds
if (proj.x < -100 || proj.x > GAME_WIDTH + 100 || proj.y < -100 || proj.y > GAME_HEIGHT + 100) {
proj.destroy();
projectiles.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
var dx = proj.x - enemy.x;
var dy = proj.y - enemy.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < proj.radius + enemy.radius) {
var killed = enemy.takeDamage(proj.damage);
proj.destroy();
projectiles.splice(i, 1);
if (killed) {
enemies.splice(j, 1);
}
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Check collision with player
var dx = enemy.x - player.x;
var dy = enemy.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < enemy.radius + player.radius) {
player.takeDamage();
// Knockback enemy
var angle = Math.atan2(dy, dx);
enemy.x += Math.cos(angle) * 120;
enemy.y += Math.sin(angle) * 120;
}
}
// Update exp gems
for (var i = expGems.length - 1; i >= 0; i--) {
var gem = expGems[i];
gem.update();
// Collect if close to player
var dx = gem.x - player.x;
var dy = gem.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < gem.radius + player.radius) {
player.gainExp(gem.value);
gem.destroy();
expGems.splice(i, 1);
}
}
// Continuous enemy spawn (no waves)
if (!levelUpPopup) {
// Wait for initial spawn delay before starting enemy spawns
if (initialSpawnDelayTimer < initialSpawnDelay) {
initialSpawnDelayTimer++;
} else {
spawnDelayTimer++;
if (spawnDelayTimer >= spawnDelay) {
spawnEnemy();
spawnDelayTimer = 0;
}
// Increase difficulty over time, every 10 seconds
if (LK.ticks % (60 * 10) === 0) {
wave++;
updateScore();
updateSpawnRate();
}
// Increase maxLivingEnemies by 1 every 3 minutes
if (LK.ticks - lastMaxLivingEnemiesIncreaseTick >= maxLivingEnemiesIncreaseInterval) {
maxLivingEnemies++;
lastMaxLivingEnemiesIncreaseTick = LK.ticks;
}
}
}
};
// --- GUI positioning ---
scoreTxt.x = GAME_WIDTH / 2;
scoreTxt.y = 20;
expBar.x = GAME_WIDTH / 2;
expBar.y = 140;
hpBar.x = GAME_WIDTH / 2;
hpBar.y = 220;
// --- Start first wave ---
// No prepareWave needed; enemy spawning is now continuous. ===================================================================
--- original.js
+++ change.js
@@ -289,9 +289,11 @@
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 48;
- self.speed = 6 + Math.random() * 3;
+ // Slow down very fast speeds of living beings (enemies)
+ self.speed = 6 + Math.random() * 2.2; // reduced max speed from 9 to 8.2
+ if (self.speed > 8) self.speed = 8; // clamp to max 8
self.hp = 2 + Math.floor(wave * 0.5);
self.maxHp = self.hp;
// For movement
self.update = function () {
@@ -329,16 +331,41 @@
});
self.radius = 28;
self.value = 1;
self.update = function () {
- // If close to player, move towards player
+ // Move towards player if close
var dx = player.x - self.x;
var dy = player.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
+ var moveTarget = null;
+ var minDist = dist;
if (dist < 300) {
- self.x += dx / dist * 18;
- self.y += dy / dist * 18;
+ moveTarget = player;
+ minDist = dist;
}
+ // Also move towards living beings (enemies) that have weapons in their hands
+ for (var i = 0; i < enemies.length; i++) {
+ var e = enemies[i];
+ // Assume enemies with weapons have a property hasWeapon === true
+ if (e.hasWeapon) {
+ var edx = e.x - self.x;
+ var edy = e.y - self.y;
+ var edist = Math.sqrt(edx * edx + edy * edy);
+ if (edist < 300 && edist < minDist) {
+ moveTarget = e;
+ minDist = edist;
+ }
+ }
+ }
+ if (moveTarget && minDist < 300) {
+ var mdx = moveTarget.x - self.x;
+ var mdy = moveTarget.y - self.y;
+ var mlen = Math.sqrt(mdx * mdx + mdy * mdy);
+ if (mlen > 0) {
+ self.x += mdx / mlen * 18;
+ self.y += mdy / mlen * 18;
+ }
+ }
};
return self;
});
// Player character class
@@ -462,10 +489,10 @@
/****
* Game Code
****/
-// --- Global variables ---
// Weapon model asset (simple sword shape)
+// --- Global variables ---
var player;
var creatureWeapon;
var projectiles = [];
var enemies = [];
@@ -624,8 +651,10 @@
}
function spawnEnemy() {
if (enemies.length >= maxLivingEnemies) return; // Don't spawn if at max
var enemy = new Enemy();
+ // Give some enemies weapons in their hands (e.g. 30% chance)
+ enemy.hasWeapon = Math.random() < 0.3;
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
police pixel art. In-Game asset. 2d. High contrast. No shadows
black person pixel art. In-Game asset. High contrast. No shadows
glock pixel art. In-Game asset. High contrast. No shadows
pixel art bullet. In-Game asset. High contrast. No shadows
money pixel art. In-Game asset. High contrast. No shadows
a police themed level up screen pixel art. In-Game asset. High contrast. No shadows