User prompt
add new waves and waves get harder with each wave
User prompt
add the new column attacks to the 6th wave
User prompt
add a new column attack class
User prompt
make the particles disappear after falling from screen and not visible
User prompt
make the particles disappear aftre longer going off-screeen
User prompt
make the particle anim in slow motion
User prompt
reduce the amount of particles
User prompt
make blastparticle an asset
User prompt
Use BlastParticle class instead of Bullet for soul death blast particles
User prompt
reduces the gravcty
User prompt
add gravity physics to blast particles
User prompt
make the background black
User prompt
make the arnea outline a diferrent asset
User prompt
add 1 more hit to stoppping music
User prompt
it is stopping on fourth hit now make it on 5th
User prompt
not when 1 hit whne 5th hit
User prompt
stop the music when soul is hit not game over
User prompt
play music in the background and maki it stop if the character dies
User prompt
make the arena outlines color indiviudally from the arena itself an make the eoutline white for a better visual outline
User prompt
make the outline white
User prompt
not an ellipse either. make it an OUTLINE
User prompt
make the outline asset a outlşne not a box
User prompt
add an outline between arena and background
User prompt
give the enemy a visual hover effect by going up and down slowly ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
move the enemy lower signigfcacn kyty
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Arena border (visual only)
var ArenaBorder = Container.expand(function () {
var self = Container.call(this);
var border = self.attachAsset('arenaBorderBox', {
anchorX: 0.5,
anchorY: 0.5,
color: 0x181818 // dark color for the border fill
});
border.alpha = 0.7;
return self;
});
// Arena outline (true outline, not filled)
var ArenaOutline = Container.expand(function () {
var self = Container.call(this);
// Use 4 thin rectangles to form a rectangular outline
var thickness = 10;
var width = ARENA_W + thickness * 2;
var height = ARENA_H + thickness * 2;
// Top
var top = LK.getAsset('arenaBorderBox', {
width: width,
height: thickness,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0
});
top.x = 0;
top.y = -height / 2;
self.addChild(top);
// Bottom
var bottom = LK.getAsset('arenaBorderBox', {
width: width,
height: thickness,
color: 0xffffff,
anchorX: 0.5,
anchorY: 1
});
bottom.x = 0;
bottom.y = height / 2;
self.addChild(bottom);
// Left
var left = LK.getAsset('arenaBorderBox', {
width: thickness,
height: height,
color: 0xffffff,
anchorX: 0,
anchorY: 0.5
});
left.x = -width / 2;
left.y = 0;
self.addChild(left);
// Right
var right = LK.getAsset('arenaBorderBox', {
width: thickness,
height: height,
color: 0xffffff,
anchorX: 1,
anchorY: 0.5
});
right.x = width / 2;
right.y = 0;
self.addChild(right);
self.alpha = 1;
return self;
});
// BlastParticle class for soul death blast particles
var BlastParticle = Container.expand(function () {
var self = Container.call(this);
var particleSprite = self.attachAsset('blastParticle', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self._gravity = 0.5;
self.update = function () {
self.vy += self._gravity;
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Enemy (boss) class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemyBox', {
anchorX: 0.5,
anchorY: 0.5
});
// Health property
self.maxHP = 20;
self.hp = self.maxHP;
// For hit flash
self.flash = function () {
tween(self, {
alpha: 0.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120
});
}
});
};
return self;
});
// EnemyAsset: individual asset for enemy visual
var EnemyAsset = Container.expand(function () {
var self = Container.call(this);
// Main body
var body = LK.getAsset('enemyBox', {
width: 220,
height: 120,
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(body);
// Face/eyes (simple style)
var eyeL = LK.getAsset('bullet', {
width: 24,
height: 24,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0.5
});
eyeL.x = -40;
eyeL.y = -20;
self.addChild(eyeL);
var eyeR = LK.getAsset('bullet', {
width: 24,
height: 24,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0.5
});
eyeR.x = 40;
eyeR.y = -20;
self.addChild(eyeR);
// Angry brow (simple rectangle)
var browL = LK.getAsset('arenaBorderBox', {
width: 32,
height: 8,
color: 0x222222,
anchorX: 0.5,
anchorY: 0.5
});
browL.x = -40;
browL.y = -36;
browL.rotation = -0.3;
self.addChild(browL);
var browR = LK.getAsset('arenaBorderBox', {
width: 32,
height: 8,
color: 0x222222,
anchorX: 0.5,
anchorY: 0.5
});
browR.x = 40;
browR.y = -36;
browR.rotation = 0.3;
self.addChild(browR);
return self;
});
// Soul (player) class
var Soul = Container.expand(function () {
var self = Container.call(this);
var soulSprite = self.attachAsset('soul', {
anchorX: 0.5,
anchorY: 0.5
});
// For hit flash
self.flash = function () {
tween(self, {
alpha: 0.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Enemy healthbar bg
// Enemy healthbar border
// Player healthbar bg
// Player healthbar border
// Enemy only
// Arena border only
// Unique assets for each visual element
// Enemy warning (yellow, for telegraphing)
// Bullet (white circle)
// Arena border (white rectangle, thin)
// Heart-shaped soul (red)
// Arena dimensions (centered)
var ARENA_W = 1200;
var ARENA_H = 900;
var ARENA_X = 2048 / 2 - ARENA_W / 2;
var ARENA_Y = 2732 / 2 - ARENA_H / 2;
// Add arena outline (true outline asset, white)
var arenaOutline = new ArenaOutline();
arenaOutline.x = 2048 / 2;
arenaOutline.y = 2732 / 2;
game.addChild(arenaOutline);
// Add enemy (boss) above the arena
var enemy = new Enemy();
enemy.x = 2048 / 2;
enemy.y = ARENA_Y - 200; // Move enemy further up above arena top
game.addChild(enemy);
// Enemy hover effect: slow up and down tween loop
function enemyHoverUp() {
tween(enemy, {
y: ARENA_Y - 170
}, {
duration: 1200,
easing: tween.sineInOut,
onFinish: enemyHoverDown
});
}
function enemyHoverDown() {
tween(enemy, {
y: ARENA_Y - 230
}, {
duration: 1200,
easing: tween.sineInOut,
onFinish: enemyHoverUp
});
}
// Start at current position, tween down first
enemyHoverDown();
// Add soul (player)
var soul = new Soul();
soul.x = 2048 / 2;
soul.y = 2732 / 2;
game.addChild(soul);
// GUI: Timer (shows wave time left)
var timerTxt = new Text2('', {
size: 80,
fill: "#fff"
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
// GUI: Wave counter
var waveTxt = new Text2('', {
size: 80,
fill: "#fff"
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
waveTxt.y = 90;
// Game state
var bullets = [];
var currentWave = 0;
var waveTime = 0;
var waveTimer = 0;
var waveActive = false;
var soulAlive = true;
var dragSoul = false;
var dragOffsetX = 0;
var dragOffsetY = 0;
// Soul health
var soulMaxHP = 5;
var soulHP = soulMaxHP;
// Player healthbar (box style) under the arena
var playerHealthbarBg = LK.getAsset('playerHealthbarBg', {
anchorX: 0.5,
anchorY: 0.5
});
var playerHealthbarBox = LK.getAsset('playerHealthbarBox', {
anchorX: 0,
anchorY: 0.5
});
// Healthbar dimensions
var healthbarWidth = 900;
var healthbarHeight = 39.49;
var healthbarX = 2048 / 2 - healthbarWidth / 2;
var healthbarY = ARENA_Y + ARENA_H + 60; // 60px below arena
playerHealthbarBg.x = 2048 / 2;
playerHealthbarBg.y = healthbarY + healthbarHeight / 2;
playerHealthbarBox.x = healthbarX;
playerHealthbarBox.y = healthbarY + healthbarHeight / 2;
// Add HP label to the left of the healthbar
var hpLabelTxt = new Text2("HP", {
size: 64,
fill: "#fff"
});
hpLabelTxt.anchor.set(1, 0.5); // right align, vertically centered
// Place it 24px to the left of the healthbar
hpLabelTxt.x = healthbarX - 24;
hpLabelTxt.y = healthbarY + healthbarHeight / 2;
// Add to game
game.addChild(playerHealthbarBg);
game.addChild(playerHealthbarBox);
game.addChild(hpLabelTxt);
// Update function for healthbar
function updatePlayerHealthbar() {
// Clamp HP
var hp = Math.max(0, Math.min(soulHP, soulMaxHP));
// Set width proportional to HP
playerHealthbarBox.width = healthbarWidth * (hp / soulMaxHP);
}
updatePlayerHealthbar();
// Arena bounds (for clamping soul)
function clampSoul(x, y) {
var hw = 50,
hh = 50; // soul half size
var minX = ARENA_X + hw;
var maxX = ARENA_X + ARENA_W - hw;
var minY = ARENA_Y + hh;
var maxY = ARENA_Y + ARENA_H - hh;
return {
x: Math.max(minX, Math.min(maxX, x)),
y: Math.max(minY, Math.min(maxY, y))
};
}
// Touch/drag controls
function handleSoulMove(x, y, obj) {
if (!dragSoul || !soulAlive) return;
var pos = clampSoul(x - dragOffsetX, y - dragOffsetY);
soul.x = pos.x;
soul.y = pos.y;
}
game.move = function (x, y, obj) {
handleSoulMove(x, y, obj);
};
game.down = function (x, y, obj) {
// Only start drag if inside soul
var dx = x - soul.x,
dy = y - soul.y;
if (dx * dx + dy * dy <= 60 * 60) {
dragSoul = true;
dragOffsetX = x - soul.x;
dragOffsetY = y - soul.y;
handleSoulMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragSoul = false;
};
// Waves definition
// Each wave: {duration, spawnFunc(tick, bulletsArr)}
var waves = [
// Wave 1: Simple slow bullets from top
{
duration: 180,
// 3s
spawnFunc: function spawnFunc(tick, arr) {
// Unpredictable horizontal movement for the whole line
if (typeof spawnFunc._targetX === "undefined") {
spawnFunc._targetX = 0;
spawnFunc._currentX = 0;
spawnFunc._tween = null;
spawnFunc._lastTick = 0;
}
if (tick - spawnFunc._lastTick > 40 || Math.abs(spawnFunc._targetX - spawnFunc._currentX) < 5) {
// Pick a new random target X offset within [-180, 180]
var newTarget = (Math.random() * 2 - 1) * 180;
spawnFunc._lastTick = tick;
spawnFunc._targetX = newTarget;
// Tween smoothly to new target over 30-60 frames
if (spawnFunc._tween) spawnFunc._tween.stop();
spawnFunc._tween = tween(spawnFunc, {
_currentX: newTarget
}, {
duration: 30 + Math.floor(Math.random() * 30)
});
}
var lineXOffset = spawnFunc._currentX;
if (tick % 24 === 0) {
// Wave 1: faster and more bullets, with unpredictable horizontal movement
var speed = 8;
for (var i = 0; i < 6; ++i) {
var b = new Bullet();
b.x = ARENA_X + 120 + i * 180 + lineXOffset;
b.y = ARENA_Y + 30;
b.vx = 0;
b.vy = speed;
arr.push(b);
game.addChild(b);
}
}
}
},
// Wave 2: Bullets from left/right
{
duration: 210,
// 3.5s
spawnFunc: function spawnFunc(tick, arr) {
// Make the whole line go up and down using a vertical offset
var lineOscAmp = 120; // amplitude of up/down movement
var lineOscFreq = 0.025; // frequency of up/down movement
var lineYOffset = Math.sin(tick * lineOscFreq) * lineOscAmp;
// Center the bullet lines vertically in the arena, closer to the middle
var centerY1 = ARENA_Y + ARENA_H / 2 - 120;
var centerY2 = ARENA_Y + ARENA_H / 2 + 120;
if (tick % 24 === 0) {
// S-shape: bullets from left and right, with vy oscillating as a sine wave
var speed = 10;
var amplitude = 60; // increased vertical amplitude of S
var freq = 0.012; // lowered frequency of S even more (even slower oscillation)
// Left to right
var b1 = new Bullet();
b1.x = ARENA_X + 30;
b1.y = (tick % 2 === 0 ? centerY1 : centerY2) + lineYOffset;
b1.vx = speed;
b1.vy = amplitude * Math.sin((tick + 0) * freq);
// Store phase for S motion
b1._sPhase = (tick + 0) * freq;
b1._sDir = 1;
// Override update for S shape
b1._baseY = b1.y;
b1._baseX = b1.x;
b1._t = 0;
b1._lineOscAmp = lineOscAmp;
b1._lineOscFreq = lineOscFreq;
b1._spawnTick = tick;
b1.update = function () {
this.x += this.vx;
this._t += 1;
// S shape: y = baseY + amplitude * sin((x - baseX) * freq) + line oscillation
var lineYOffset = Math.sin((this._spawnTick + this._t) * this._lineOscFreq) * this._lineOscAmp;
this.y = this._baseY + amplitude * Math.sin((this.x - this._baseX) * freq) + (lineYOffset - Math.sin(this._spawnTick * this._lineOscFreq) * this._lineOscAmp);
};
arr.push(b1);
game.addChild(b1);
// Right to left
var b2 = new Bullet();
b2.x = ARENA_X + ARENA_W - 30;
b2.y = (tick % 2 === 0 ? centerY2 : centerY1) + lineYOffset;
b2.vx = -speed;
b2.vy = amplitude * Math.sin((tick + 60) * freq);
b2._sPhase = (tick + 60) * freq;
b2._sDir = -1;
b2._baseY = b2.y;
b2._baseX = b2.x;
b2._t = 0;
b2._lineOscAmp = lineOscAmp;
b2._lineOscFreq = lineOscFreq;
b2._spawnTick = tick;
b2.update = function () {
this.x += this.vx;
this._t += 1;
var lineYOffset = Math.sin((this._spawnTick + this._t) * this._lineOscFreq) * this._lineOscAmp;
this.y = this._baseY + amplitude * Math.sin((this.x - this._baseX) * freq) + (lineYOffset - Math.sin(this._spawnTick * this._lineOscFreq) * this._lineOscAmp);
};
arr.push(b2);
game.addChild(b2);
}
}
},
// Wave 3: Diagonal bullets from corners
{
duration: 240,
// 4s
spawnFunc: function spawnFunc(tick, arr) {
if (tick % 36 === 0) {
// Wave 3: diagonal, medium speed
var speed = 7.5;
var diag = speed / Math.sqrt(2);
// Top-left
var b1 = new Bullet();
b1.x = ARENA_X + 40;
b1.y = ARENA_Y + 40;
b1.vx = diag;
b1.vy = diag;
arr.push(b1);
game.addChild(b1);
// Top-right
var b2 = new Bullet();
b2.x = ARENA_X + ARENA_W - 40;
b2.y = ARENA_Y + 40;
b2.vx = -diag;
b2.vy = diag;
arr.push(b2);
game.addChild(b2);
// Bottom-left
var b3 = new Bullet();
b3.x = ARENA_X + 40;
b3.y = ARENA_Y + ARENA_H - 40;
b3.vx = diag;
b3.vy = -diag;
arr.push(b3);
game.addChild(b3);
// Bottom-right
var b4 = new Bullet();
b4.x = ARENA_X + ARENA_W - 40;
b4.y = ARENA_Y + ARENA_H - 40;
b4.vx = -diag;
b4.vy = -diag;
arr.push(b4);
game.addChild(b4);
}
}
},
// Wave 4: Random rain
{
duration: 210,
// 3.5s
spawnFunc: function spawnFunc(tick, arr) {
if (tick % 10 === 0) {
// Wave 4: fast random rain
var minSpeed = 12,
maxSpeed = 16;
var b = new Bullet();
b.x = ARENA_X + 80 + Math.floor(Math.random() * (ARENA_W - 160));
b.y = ARENA_Y + 30;
b.vx = 0;
b.vy = minSpeed + Math.random() * (maxSpeed - minSpeed);
arr.push(b);
game.addChild(b);
}
}
},
// Wave 5: Spiral (center out)
{
duration: 270,
// 4.5s
spawnFunc: function spawnFunc(tick, arr) {
if (tick % 12 === 0) {
// Wave 5: spiral, moderate speed
var speed = 9;
var angle = tick / 12 * 0.5;
for (var i = 0; i < 4; ++i) {
var a = angle + i * Math.PI / 2;
var b = new Bullet();
b.x = 2048 / 2;
b.y = 2732 / 2;
b.vx = Math.cos(a) * speed;
b.vy = Math.sin(a) * speed;
arr.push(b);
game.addChild(b);
}
}
}
}];
// Start first wave
function startWave(idx) {
currentWave = idx;
waveTime = 0;
waveActive = true;
waveTimer = 0;
// Remove all bullets
for (var i = bullets.length - 1; i >= 0; --i) {
bullets[i].destroy();
bullets.splice(i, 1);
}
// Update GUI
waveTxt.setText("Wave " + (currentWave + 1) + "/" + waves.length);
// Reset HP and healthbar if starting first wave (new game)
if (currentWave === 0) {
soulHP = soulMaxHP;
soulAlive = true;
updatePlayerHealthbar();
// Restore soul sprite if needed
if (soul.children && soul.children.length > 0) {
var firstChild = soul.children[0];
if (firstChild.assetId === 'warning') {
soul.removeChild(firstChild);
var newSoulSprite = LK.getAsset('soul', {
anchorX: 0.5,
anchorY: 0.5
});
soul.addChildAt(newSoulSprite, 0);
// Play sound on soul texture change (revive)
LK.getSound('shoot').play();
}
}
}
}
startWave(0);
// Play background music at game start
LK.playMusic('music');
// Main update loop
game.update = function () {
if (!soulAlive) return;
// Wave logic
if (waveActive) {
var wave = waves[currentWave];
wave.spawnFunc(waveTime, bullets);
waveTime++;
// Update timer GUI
var tleft = Math.max(0, Math.ceil((wave.duration - waveTime) / 60));
timerTxt.setText("Time: " + tleft + "s");
// End wave?
if (waveTime >= wave.duration) {
waveActive = false;
// Remove all bullets after short delay
LK.setTimeout(function () {
for (var i = bullets.length - 1; i >= 0; --i) {
bullets[i].destroy();
bullets.splice(i, 1);
}
// Next wave or win
if (currentWave + 1 < waves.length) {
startWave(currentWave + 1);
} else {
// Win!
LK.showYouWin();
}
}, 600);
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; --i) {
var b = bullets[i];
b.update();
// Remove if out of arena (immediately on leaving the border)
if (b.x < ARENA_X || b.x > ARENA_X + ARENA_W || b.y < ARENA_Y || b.y > ARENA_Y + ARENA_H) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Collision with soul
var dx = b.x - soul.x,
dy = b.y - soul.y;
if (dx * dx + dy * dy < 45 * 45) {
// Hit!
soul.flash();
soulHP -= 1;
updatePlayerHealthbar();
// Stop music only when soul dies (on 6th hit)
if (soulHP <= 0 && soulAlive) {
LK.stopMusic();
}
// Remove bullet on hit
b.destroy();
bullets.splice(i, 1);
if (soulHP <= 0 && soulAlive) {
soulAlive = false;
// Delay soul's texture asset change to 2 seconds after dying
LK.setTimeout(function () {
if (soul.children && soul.children.length > 0) {
var deadSprite = LK.getAsset('warning', {
anchorX: 0.5,
anchorY: 0.5
});
var oldSprite = soul.children[0];
deadSprite.x = oldSprite.x;
deadSprite.y = oldSprite.y;
soul.removeChild(oldSprite);
soul.addChildAt(deadSprite, 0);
// Play sound on soul texture change (death)
LK.getSound('shoot').play();
// Blast into particles 1.35 sec after texture change
LK.setTimeout(function () {
// Remove soul sprite (if still present)
if (soul.children && soul.children.length > 0) {
soul.removeChild(soul.children[0]);
}
// Create 8 particles in a circle (reduced from 18)
var numParticles = 8;
var radius = 60;
for (var i = 0; i < numParticles; ++i) {
var angle = 2 * Math.PI * i / numParticles;
var px = soul.x + Math.cos(angle) * 0;
var py = soul.y + Math.sin(angle) * 0;
var particle = new BlastParticle();
particle.x = px;
particle.y = py;
// Give each particle a random color (yellow/orange/red)
var colors = [0xffe066, 0xffb347, 0xff4444, 0xffe0a0, 0xffd700];
var color = colors[Math.floor(Math.random() * colors.length)];
if (particle.children && particle.children.length > 0) {
particle.children[0].tint = color;
}
// Set velocity outward (slow motion: reduce speed)
var speed = (13 + Math.random() * 4) * 0.35;
particle.vx = Math.cos(angle) * speed;
particle.vy = Math.sin(angle) * speed;
// Add gravity property for blast particles (slow motion: reduce gravity)
particle._gravity = (0.5 + Math.random() * 0.15) * 0.25;
// Fade out and destroy after 0.7s
tween(particle, {
alpha: 0
}, {
duration: 700,
onFinish: function () {
this.destroy();
}.bind(particle)
});
game.addChild(particle);
}
}, 1350);
}
}, 2000);
LK.effects.flashScreen(0xff0000, 800);
LK.setTimeout(function () {
LK.showGameOver();
}, 5000);
break;
} else {
// Flash screen for hit, but not game over
LK.effects.flashScreen(0xff0000, 300);
}
}
}
};
// Initial GUI text
timerTxt.setText("Time: ");
waveTxt.setText("Wave 1/" + waves.length); ===================================================================
--- original.js
+++ change.js
@@ -703,14 +703,14 @@
var color = colors[Math.floor(Math.random() * colors.length)];
if (particle.children && particle.children.length > 0) {
particle.children[0].tint = color;
}
- // Set velocity outward
- var speed = 13 + Math.random() * 4;
+ // Set velocity outward (slow motion: reduce speed)
+ var speed = (13 + Math.random() * 4) * 0.35;
particle.vx = Math.cos(angle) * speed;
particle.vy = Math.sin(angle) * speed;
- // Add gravity property for blast particles
- particle._gravity = 0.5 + Math.random() * 0.15;
+ // Add gravity property for blast particles (slow motion: reduce gravity)
+ particle._gravity = (0.5 + Math.random() * 0.15) * 0.25;
// Fade out and destroy after 0.7s
tween(particle, {
alpha: 0
}, {