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; // Remove particle if it is fully faded out and not visible in the game area if (self.alpha <= 0.01 && (self.x < ARENA_X || self.x > ARENA_X + ARENA_W || self.y < ARENA_Y || self.y > ARENA_Y + ARENA_H)) { self.destroy(); } }; 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 ****/ // Arena dimensions (centered) // Heart-shaped soul (red) // Arena border (white rectangle, thin) // Bullet (white circle) // Enemy warning (yellow, for telegraphing) // Unique assets for each visual element // Arena border only // Enemy only // Player healthbar border // Player healthbar bg // Enemy healthbar border // Enemy healthbar bg 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)} // Difficulty scaling: each time all waves are completed, the next set is harder var baseWaves = [ // Wave 1: Simple slow bullets from top { duration: 180, spawnFunc: function spawnFunc(tick, arr, difficulty) { // 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; var fireRate = Math.max(8, 24 - 2 * difficulty); // faster at higher difficulty if (tick % fireRate === 0) { var speed = 8 + 1.5 * difficulty; var count = 6 + Math.floor(difficulty / 2); for (var i = 0; i < count; ++i) { var b = new Bullet(); b.x = ARENA_X + 120 + i * ((ARENA_W - 240) / (count - 1)) + 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, spawnFunc: function spawnFunc(tick, arr, difficulty) { var lineOscAmp = 120 + 10 * difficulty; var lineOscFreq = 0.025 + 0.002 * difficulty; var lineYOffset = Math.sin(tick * lineOscFreq) * lineOscAmp; var centerY1 = ARENA_Y + ARENA_H / 2 - 120; var centerY2 = ARENA_Y + ARENA_H / 2 + 120; var fireRate = Math.max(8, 24 - 2 * difficulty); if (tick % fireRate === 0) { var speed = 10 + 1.5 * difficulty; var amplitude = 60 + 6 * difficulty; var freq = 0.012 + 0.001 * difficulty; // 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); b1._sPhase = (tick + 0) * freq; b1._sDir = 1; 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; 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, spawnFunc: function spawnFunc(tick, arr, difficulty) { var fireRate = Math.max(8, 36 - 2 * difficulty); if (tick % fireRate === 0) { var speed = 7.5 + 1.2 * difficulty; 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); // At higher difficulty, add a random diagonal bullet if (difficulty >= 2) { var b5 = new Bullet(); var side = Math.floor(Math.random() * 4); if (side === 0) { b5.x = ARENA_X + 40; b5.y = ARENA_Y + 40; b5.vx = diag * 1.2; b5.vy = diag * 1.2; } if (side === 1) { b5.x = ARENA_X + ARENA_W - 40; b5.y = ARENA_Y + 40; b5.vx = -diag * 1.2; b5.vy = diag * 1.2; } if (side === 2) { b5.x = ARENA_X + 40; b5.y = ARENA_Y + ARENA_H - 40; b5.vx = diag * 1.2; b5.vy = -diag * 1.2; } if (side === 3) { b5.x = ARENA_X + ARENA_W - 40; b5.y = ARENA_Y + ARENA_H - 40; b5.vx = -diag * 1.2; b5.vy = -diag * 1.2; } arr.push(b5); game.addChild(b5); } } } }, // Wave 4: Random rain { duration: 210, spawnFunc: function spawnFunc(tick, arr, difficulty) { var fireRate = Math.max(2, 10 - Math.floor(difficulty / 2)); if (tick % fireRate === 0) { var minSpeed = 12 + 1.2 * difficulty, maxSpeed = 16 + 1.5 * difficulty; var count = 1 + Math.floor(difficulty / 2); for (var i = 0; i < count; ++i) { 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, spawnFunc: function spawnFunc(tick, arr, difficulty) { var fireRate = Math.max(3, 12 - Math.floor(difficulty / 2)); if (tick % fireRate === 0) { var speed = 9 + 1.2 * difficulty; var angle = tick / fireRate * (0.5 + 0.1 * difficulty); var count = 4 + Math.floor(difficulty / 2); for (var i = 0; i < count; ++i) { var a = angle + i * (2 * Math.PI / count); 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); } } } }, // Wave 6: New - Cross barrages (hard) { duration: 240, spawnFunc: function spawnFunc(tick, arr, difficulty) { var fireRate = Math.max(6, 24 - 2 * difficulty); if (tick % fireRate === 0) { var speed = 10 + 1.5 * difficulty; // Horizontal for (var i = 0; i < 3 + Math.floor(difficulty / 2); ++i) { var b = new Bullet(); b.x = ARENA_X + 80 + i * ((ARENA_W - 160) / (2 + Math.floor(difficulty / 2))); b.y = ARENA_Y + 30; b.vx = 0; b.vy = speed; arr.push(b); game.addChild(b); } // Vertical for (var i = 0; i < 3 + Math.floor(difficulty / 2); ++i) { var b = new Bullet(); b.x = ARENA_X + 30; b.y = ARENA_Y + 80 + i * ((ARENA_H - 160) / (2 + Math.floor(difficulty / 2))); b.vx = speed; b.vy = 0; arr.push(b); game.addChild(b); } for (var i = 0; i < 3 + Math.floor(difficulty / 2); ++i) { var b = new Bullet(); b.x = ARENA_X + ARENA_W - 30; b.y = ARENA_Y + 80 + i * ((ARENA_H - 160) / (2 + Math.floor(difficulty / 2))); b.vx = -speed; b.vy = 0; arr.push(b); game.addChild(b); } } } }, // Wave 7: New - Spiral rain (very hard) { duration: 300, spawnFunc: function spawnFunc(tick, arr, difficulty) { var fireRate = Math.max(2, 8 - Math.floor(difficulty / 2)); if (tick % fireRate === 0) { var speed = 10 + 1.5 * difficulty; var count = 8 + Math.floor(difficulty / 2); var baseAngle = tick / fireRate * 0.25; for (var i = 0; i < count; ++i) { var a = baseAngle + i * (2 * Math.PI / count); var b = new Bullet(); b.x = 2048 / 2 + Math.cos(a) * 400; b.y = 2732 / 2 + Math.sin(a) * 400; b.vx = Math.cos(a) * -speed; b.vy = Math.sin(a) * -speed; arr.push(b); game.addChild(b); } } } }]; // The actual waves array is generated for each "cycle" of all waves, increasing difficulty each time var waves = []; var waveSet = 0; // how many times all waves have been completed function buildWavesForSet(set) { waves = []; for (var i = 0; i < baseWaves.length; ++i) { // Each wave gets harder with each set (function (baseWave, idx) { waves.push({ duration: Math.max(120, Math.floor(baseWave.duration * Math.max(0.7, 1 - set * 0.08))), // slightly shorter at higher sets spawnFunc: function spawnFunc(tick, arr) { baseWave.spawnFunc(tick, arr, set); } }); })(baseWaves[i], i); } } buildWavesForSet(waveSet); // 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 + " (Set " + (waveSet + 1) + ")"); // 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 { // All waves in this set completed, increase difficulty and repeat with harder waves! waveSet += 1; buildWavesForSet(waveSet); startWave(0); } }, 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 () { // Only destroy if already off-screen, otherwise wait for off-screen if (this.x < ARENA_X - 100 || this.x > ARENA_X + ARENA_W + 100 || this.y < ARENA_Y - 100 || this.y > ARENA_Y + ARENA_H + 100) { this.destroy(); } else { // Wait until particle is far off-screen, then destroy var p = this; p._offscreenCheck = function () { if (p.x < ARENA_X - 100 || p.x > ARENA_X + ARENA_W + 100 || p.y < ARENA_Y - 100 || p.y > ARENA_Y + ARENA_H + 100) { p.destroy(); // Remove from update loop p.update = function () {}; } }; // Wrap the original update to check for offscreen var origUpdate = p.update; p.update = function () { origUpdate && origUpdate.call(p); p._offscreenCheck(); }; } }.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
@@ -225,20 +225,20 @@
/****
* 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)
+// Heart-shaped soul (red)
+// Arena border (white rectangle, thin)
+// Bullet (white circle)
+// Enemy warning (yellow, for telegraphing)
+// Unique assets for each visual element
+// Arena border only
+// Enemy only
+// Player healthbar border
+// Player healthbar bg
+// Enemy healthbar border
+// Enemy healthbar bg
var ARENA_W = 1200;
var ARENA_H = 900;
var ARENA_X = 2048 / 2 - ARENA_W / 2;
var ARENA_Y = 2732 / 2 - ARENA_H / 2;
@@ -383,14 +383,14 @@
dragSoul = false;
};
// Waves definition
// Each wave: {duration, spawnFunc(tick, bulletsArr)}
-var waves = [
+// Difficulty scaling: each time all waves are completed, the next set is harder
+var baseWaves = [
// Wave 1: Simple slow bullets from top
{
duration: 180,
- // 3s
- spawnFunc: function spawnFunc(tick, arr) {
+ spawnFunc: function spawnFunc(tick, arr, difficulty) {
// Unpredictable horizontal movement for the whole line
if (typeof spawnFunc._targetX === "undefined") {
spawnFunc._targetX = 0;
spawnFunc._currentX = 0;
@@ -410,14 +410,15 @@
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 fireRate = Math.max(8, 24 - 2 * difficulty); // faster at higher difficulty
+ if (tick % fireRate === 0) {
+ var speed = 8 + 1.5 * difficulty;
+ var count = 6 + Math.floor(difficulty / 2);
+ for (var i = 0; i < count; ++i) {
var b = new Bullet();
- b.x = ARENA_X + 120 + i * 180 + lineXOffset;
+ b.x = ARENA_X + 120 + i * ((ARENA_W - 240) / (count - 1)) + lineXOffset;
b.y = ARENA_Y + 30;
b.vx = 0;
b.vy = speed;
arr.push(b);
@@ -428,32 +429,27 @@
},
// 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
+ spawnFunc: function spawnFunc(tick, arr, difficulty) {
+ var lineOscAmp = 120 + 10 * difficulty;
+ var lineOscFreq = 0.025 + 0.002 * difficulty;
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)
+ var fireRate = Math.max(8, 24 - 2 * difficulty);
+ if (tick % fireRate === 0) {
+ var speed = 10 + 1.5 * difficulty;
+ var amplitude = 60 + 6 * difficulty;
+ var freq = 0.012 + 0.001 * difficulty;
// 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;
@@ -461,9 +457,8 @@
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);
@@ -495,13 +490,12 @@
},
// 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;
+ spawnFunc: function spawnFunc(tick, arr, difficulty) {
+ var fireRate = Math.max(8, 36 - 2 * difficulty);
+ if (tick % fireRate === 0) {
+ var speed = 7.5 + 1.2 * difficulty;
var diag = speed / Math.sqrt(2);
// Top-left
var b1 = new Bullet();
b1.x = ARENA_X + 40;
@@ -533,41 +527,74 @@
b4.vx = -diag;
b4.vy = -diag;
arr.push(b4);
game.addChild(b4);
+ // At higher difficulty, add a random diagonal bullet
+ if (difficulty >= 2) {
+ var b5 = new Bullet();
+ var side = Math.floor(Math.random() * 4);
+ if (side === 0) {
+ b5.x = ARENA_X + 40;
+ b5.y = ARENA_Y + 40;
+ b5.vx = diag * 1.2;
+ b5.vy = diag * 1.2;
+ }
+ if (side === 1) {
+ b5.x = ARENA_X + ARENA_W - 40;
+ b5.y = ARENA_Y + 40;
+ b5.vx = -diag * 1.2;
+ b5.vy = diag * 1.2;
+ }
+ if (side === 2) {
+ b5.x = ARENA_X + 40;
+ b5.y = ARENA_Y + ARENA_H - 40;
+ b5.vx = diag * 1.2;
+ b5.vy = -diag * 1.2;
+ }
+ if (side === 3) {
+ b5.x = ARENA_X + ARENA_W - 40;
+ b5.y = ARENA_Y + ARENA_H - 40;
+ b5.vx = -diag * 1.2;
+ b5.vy = -diag * 1.2;
+ }
+ arr.push(b5);
+ game.addChild(b5);
+ }
}
}
},
// 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);
+ spawnFunc: function spawnFunc(tick, arr, difficulty) {
+ var fireRate = Math.max(2, 10 - Math.floor(difficulty / 2));
+ if (tick % fireRate === 0) {
+ var minSpeed = 12 + 1.2 * difficulty,
+ maxSpeed = 16 + 1.5 * difficulty;
+ var count = 1 + Math.floor(difficulty / 2);
+ for (var i = 0; i < count; ++i) {
+ 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;
+ spawnFunc: function spawnFunc(tick, arr, difficulty) {
+ var fireRate = Math.max(3, 12 - Math.floor(difficulty / 2));
+ if (tick % fireRate === 0) {
+ var speed = 9 + 1.2 * difficulty;
+ var angle = tick / fireRate * (0.5 + 0.1 * difficulty);
+ var count = 4 + Math.floor(difficulty / 2);
+ for (var i = 0; i < count; ++i) {
+ var a = angle + i * (2 * Math.PI / count);
var b = new Bullet();
b.x = 2048 / 2;
b.y = 2732 / 2;
b.vx = Math.cos(a) * speed;
@@ -577,50 +604,88 @@
}
}
}
},
-// Wave 6: Column attack (new)
+// Wave 6: New - Cross barrages (hard)
{
duration: 240,
- // 4s
- spawnFunc: function spawnFunc(tick, arr) {
- // Every 30 ticks, fire a column of bullets from the top at a random X
- if (tick % 30 === 0) {
- // Pick a random column (out of 7 possible columns)
- var numCols = 7;
- var colWidth = ARENA_W / numCols;
- var colIdx = Math.floor(Math.random() * numCols);
- var colX = ARENA_X + colWidth * (colIdx + 0.5);
- // Fire 5 bullets in a vertical column, spaced out vertically
- for (var i = 0; i < 5; ++i) {
+ spawnFunc: function spawnFunc(tick, arr, difficulty) {
+ var fireRate = Math.max(6, 24 - 2 * difficulty);
+ if (tick % fireRate === 0) {
+ var speed = 10 + 1.5 * difficulty;
+ // Horizontal
+ for (var i = 0; i < 3 + Math.floor(difficulty / 2); ++i) {
var b = new Bullet();
- b.x = colX;
- b.y = ARENA_Y - 40 - i * 60; // start above arena, staggered
+ b.x = ARENA_X + 80 + i * ((ARENA_W - 160) / (2 + Math.floor(difficulty / 2)));
+ b.y = ARENA_Y + 30;
b.vx = 0;
- b.vy = 10;
+ b.vy = speed;
arr.push(b);
game.addChild(b);
}
+ // Vertical
+ for (var i = 0; i < 3 + Math.floor(difficulty / 2); ++i) {
+ var b = new Bullet();
+ b.x = ARENA_X + 30;
+ b.y = ARENA_Y + 80 + i * ((ARENA_H - 160) / (2 + Math.floor(difficulty / 2)));
+ b.vx = speed;
+ b.vy = 0;
+ arr.push(b);
+ game.addChild(b);
+ }
+ for (var i = 0; i < 3 + Math.floor(difficulty / 2); ++i) {
+ var b = new Bullet();
+ b.x = ARENA_X + ARENA_W - 30;
+ b.y = ARENA_Y + 80 + i * ((ARENA_H - 160) / (2 + Math.floor(difficulty / 2)));
+ b.vx = -speed;
+ b.vy = 0;
+ arr.push(b);
+ game.addChild(b);
+ }
}
- // Add new column attacks: every 60 ticks, fire columns from both left and right edges
- if (tick % 60 === 0) {
- var numCols = 2;
- var edgeCols = [ARENA_X + 80, ARENA_X + ARENA_W - 80];
- for (var c = 0; c < numCols; ++c) {
- var colX = edgeCols[c];
- for (var i = 0; i < 4; ++i) {
- var b = new Bullet();
- b.x = colX;
- b.y = ARENA_Y - 60 - i * 70;
- b.vx = 0;
- b.vy = 12;
- arr.push(b);
- game.addChild(b);
- }
+ }
+},
+// Wave 7: New - Spiral rain (very hard)
+{
+ duration: 300,
+ spawnFunc: function spawnFunc(tick, arr, difficulty) {
+ var fireRate = Math.max(2, 8 - Math.floor(difficulty / 2));
+ if (tick % fireRate === 0) {
+ var speed = 10 + 1.5 * difficulty;
+ var count = 8 + Math.floor(difficulty / 2);
+ var baseAngle = tick / fireRate * 0.25;
+ for (var i = 0; i < count; ++i) {
+ var a = baseAngle + i * (2 * Math.PI / count);
+ var b = new Bullet();
+ b.x = 2048 / 2 + Math.cos(a) * 400;
+ b.y = 2732 / 2 + Math.sin(a) * 400;
+ b.vx = Math.cos(a) * -speed;
+ b.vy = Math.sin(a) * -speed;
+ arr.push(b);
+ game.addChild(b);
}
}
}
}];
+// The actual waves array is generated for each "cycle" of all waves, increasing difficulty each time
+var waves = [];
+var waveSet = 0; // how many times all waves have been completed
+function buildWavesForSet(set) {
+ waves = [];
+ for (var i = 0; i < baseWaves.length; ++i) {
+ // Each wave gets harder with each set
+ (function (baseWave, idx) {
+ waves.push({
+ duration: Math.max(120, Math.floor(baseWave.duration * Math.max(0.7, 1 - set * 0.08))),
+ // slightly shorter at higher sets
+ spawnFunc: function spawnFunc(tick, arr) {
+ baseWave.spawnFunc(tick, arr, set);
+ }
+ });
+ })(baseWaves[i], i);
+ }
+}
+buildWavesForSet(waveSet);
// Start first wave
function startWave(idx) {
currentWave = idx;
waveTime = 0;
@@ -631,9 +696,9 @@
bullets[i].destroy();
bullets.splice(i, 1);
}
// Update GUI
- waveTxt.setText("Wave " + (currentWave + 1) + "/" + waves.length);
+ waveTxt.setText("Wave " + (currentWave + 1) + "/" + waves.length + " (Set " + (waveSet + 1) + ")");
// Reset HP and healthbar if starting first wave (new game)
if (currentWave === 0) {
soulHP = soulMaxHP;
soulAlive = true;
@@ -680,10 +745,12 @@
// Next wave or win
if (currentWave + 1 < waves.length) {
startWave(currentWave + 1);
} else {
- // Win!
- LK.showYouWin();
+ // All waves in this set completed, increase difficulty and repeat with harder waves!
+ waveSet += 1;
+ buildWavesForSet(waveSet);
+ startWave(0);
}
}, 600);
}
}