User prompt
Please fix the bug: 'e is not defined' in or related to this line: 'if (e.constructor === SpiralEnemy && e.shootCooldown <= 0) {' Line Number: 1669
User prompt
Please fix the bug: 'freezeActive is not defined' in or related to this line: 'if (!freezeActive && !stunActive) {' Line Number: 1664
User prompt
Please fix the bug: 'enemyTypeRand is not defined' in or related to this line: 'if (enemyTypeRand < 0.20) {' Line Number: 1664
User prompt
yeni bir düşman özelliiği ekle
User prompt
geminin canı daha fazla olsun
User prompt
başlangıç arkaplanı gökyüzü olsun
User prompt
otomatik hedefleme özelliği ekle powerupdan çıksın ve çok güçlü olsun
User prompt
firerate özelliğinin gelme olasılığı artsın ve birden fazla özellik kazanma şeyleri gelme ihtimali artsın
User prompt
Add 20 powerup features, test if they work, delete the ones that don't work, keep the ones that work
User prompt
geminin canını arttır
User prompt
geminin canı biraz fazla olsun
User prompt
double score powerup çalışmıyor
User prompt
arkaplanlar gökyüzü olsun hep farklı bir gökyüzü
User prompt
başlangıçta bir menü olsun
User prompt
geri al
User prompt
arkaplanlar farklı renklerin birleşmesiyle oluşan güzel renkler olsun
User prompt
başlangıçta bir menü olsun
User prompt
geri al bozdu mermiyi
User prompt
oyunu biraz kolaylaştır ve 6 mermi ve 9 mermi ateşliyeceğimiz özellikler ekle
User prompt
Please fix the bug: 'ReferenceError: invincibilityActive is not defined' in or related to this line: 'if (invincibilityActive) {' Line Number: 860
User prompt
olmuyorsa kaldır onları
User prompt
son eklediğin özellikler çalışmıyor çalışır duruma getir
User prompt
add 20 different features and more features to come
User prompt
add different features and more features to come
User prompt
oyun çok hızlanıyor öyle olmamalı
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Enemy (Base class) var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.radius = enemyGfx.width * 0.5; self.speed = ENEMY_BASE_SPEED + Math.random() * 4; self.moveType = Math.random() < 0.5 ? 'straight' : 'sine'; self.sinePhase = Math.random() * Math.PI * 2; self.sineAmp = 120 + Math.random() * 120; self.shootCooldown = 60 + Math.floor(Math.random() * 60); // Health system for enemy self.maxHealth = 1; self.health = self.maxHealth; // Health bar var healthBarBg = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 0.18, tint: 0x222222, y: enemyGfx.height * 0.7 }); var healthBar = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.95, scaleY: 0.12, tint: 0x44ff44, y: enemyGfx.height * 0.7 }); self.update = function () { if (self.moveType === 'straight') { self.y += self.speed; } else { self.y += self.speed * 0.85; self.x += Math.sin(self.y / 120 + self.sinePhase) * 6; } // Clamp enemy inside left/right screen bounds var margin = self.radius; if (self.x < margin) self.x = margin; if (self.x > GAME_W - margin) self.x = GAME_W - margin; self.shootCooldown--; // Update health bar healthBar.scaleX = 0.95 * (self.health / self.maxHealth); if (self.health < 1) healthBar.scaleX = 0; }; self.takeDamage = function (dmg) { self.health -= dmg; LK.effects.flashObject(self, 0xffffff, 80); if (self.health <= 0) { self.health = 0; // Death handled in game loop } }; return self; }); // Enemy Bullet var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGfx = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = ENEMY_BULLET_SPEED; self.dirX = 0; self.dirY = 1; self.update = function () { self.x += self.dirX * self.speed; self.y += self.dirY * self.speed; }; return self; }); // FastEnemy: moves faster, less health var FastEnemy = Container.expand(function () { var self = Container.call(this); var enemyGfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); enemyGfx.tint = 0x00e1ff; self.radius = enemyGfx.width * 0.5; self.speed = ENEMY_FAST_SPEED + Math.random() * 4; self.moveType = 'straight'; self.shootCooldown = 40 + Math.floor(Math.random() * 40); self.maxHealth = 1; self.health = self.maxHealth; // Health bar var healthBarBg = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 0.18, tint: 0x222222, y: enemyGfx.height * 0.7 }); var healthBar = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.95, scaleY: 0.12, tint: 0x00e1ff, y: enemyGfx.height * 0.7 }); self.update = function () { self.y += self.speed; // Clamp enemy inside left/right screen bounds var margin = self.radius; if (self.x < margin) self.x = margin; if (self.x > GAME_W - margin) self.x = GAME_W - margin; self.shootCooldown--; // Update health bar healthBar.scaleX = 0.95 * (self.health / self.maxHealth); if (self.health < 1) healthBar.scaleX = 0; }; self.takeDamage = function (dmg) { self.health -= dmg; LK.effects.flashObject(self, 0x00e1ff, 60); if (self.health <= 0) self.health = 0; }; return self; }); // Player Bullet var PlayerBullet = Container.expand(function () { var self = Container.call(this); var bulletGfx = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = PLAYER_BULLET_SPEED; self.update = function () { self.y += self.speed; }; return self; }); // Powerup var Powerup = Container.expand(function () { var self = Container.call(this); // Add new powerup types: tripleShot, megaShield, invincibility, doubleScore, slowMotion var types = ['rapid', 'shield', 'heal', 'doubleScore', 'slowMotion', 'tripleShot', 'megaShield', 'invincibility']; var r = Math.random(); if (r < 0.22) self.type = 'rapid';else if (r < 0.37) self.type = 'shield';else if (r < 0.52) self.type = 'heal';else if (r < 0.65) self.type = 'doubleScore';else if (r < 0.77) self.type = 'slowMotion';else if (r < 0.87) self.type = 'tripleShot';else if (r < 0.95) self.type = 'megaShield';else self.type = 'invincibility'; var powerupGfx = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); if (self.type === 'rapid') powerupGfx.tint = 0xffe100;else if (self.type === 'shield') powerupGfx.tint = 0x00ffff;else if (self.type === 'heal') powerupGfx.tint = 0x44ff44;else if (self.type === 'doubleScore') powerupGfx.tint = 0xff00ff;else if (self.type === 'slowMotion') powerupGfx.tint = 0x8888ff;else if (self.type === 'tripleShot') powerupGfx.tint = 0xff8800;else if (self.type === 'megaShield') powerupGfx.tint = 0x00ff88;else if (self.type === 'invincibility') powerupGfx.tint = 0xffffff; self.speed = 10; self.update = function () { self.y += self.speed; }; return self; }); // Player Ship var Ship = Container.expand(function () { var self = Container.call(this); var shipGfx = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); self.radius = shipGfx.width * 0.5; self.shootCooldown = 0; self.rapidFire = false; self.rapidFireTimer = 0; self.shield = false; self.shieldTimer = 0; // Health system for player self.maxHealth = 13; // Even more survivability for easier gameplay self.health = self.maxHealth; // Health bar var healthBarBg = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.2, scaleY: 0.25, tint: 0x222222, y: shipGfx.height * 0.7 }); var healthBar = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.18, tint: 0xff4444, y: shipGfx.height * 0.7 }); // Visual shield indicator var shieldGfx = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3, tint: 0x00ffff }); shieldGfx.alpha = 0.25; shieldGfx.visible = false; self.update = function () { // Handle powerup timers if (self.rapidFire) { self.rapidFireTimer--; // Visual feedback for rapid fire: pulse ship color shipGfx.tint = LK.ticks % 10 < 5 ? 0xffe100 : 0x33c1ff; if (self.rapidFireTimer <= 0) { self.rapidFire = false; shipGfx.tint = 0x33c1ff; } } else { shipGfx.tint = 0x33c1ff; } if (self.shield) { self.shieldTimer--; shieldGfx.visible = true; if (self.shieldTimer <= 0) { self.shield = false; shieldGfx.visible = false; } } else { shieldGfx.visible = false; } // Update health bar healthBar.scaleX = 2 * (self.health / self.maxHealth); if (self.health < 1) healthBar.scaleX = 0; }; self.activateRapidFire = function (duration) { self.rapidFire = true; self.rapidFireTimer = duration; }; self.activateShield = function (duration) { self.shield = true; self.shieldTimer = duration; shieldGfx.visible = true; }; self.takeDamage = function (dmg) { if (self.shield) return; self.health -= dmg; LK.effects.flashObject(self, 0xff0000, 200); if (self.health < 0) self.health = 0; }; self.heal = function (amount) { self.health += amount; if (self.health > self.maxHealth) self.health = self.maxHealth; }; return self; }); // TankEnemy: slow, high health, shoots more var TankEnemy = Container.expand(function () { var self = Container.call(this); var enemyGfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 }); enemyGfx.tint = 0xff4444; self.radius = enemyGfx.width * 0.65; self.speed = ENEMY_TANK_SPEED + Math.random() * 1.2; self.moveType = 'sine'; self.sinePhase = Math.random() * Math.PI * 2; self.sineAmp = 180 + Math.random() * 80; self.shootCooldown = 30 + Math.floor(Math.random() * 30); self.maxHealth = 4; self.health = self.maxHealth; // Health bar var healthBarBg = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 0.18, tint: 0x222222, y: enemyGfx.height * 0.9 }); var healthBar = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.25, scaleY: 0.12, tint: 0xff4444, y: enemyGfx.height * 0.9 }); self.update = function () { self.y += self.speed * 0.7; self.x += Math.sin(self.y / 120 + self.sinePhase) * 10; // Clamp enemy inside left/right screen bounds var margin = self.radius; if (self.x < margin) self.x = margin; if (self.x > GAME_W - margin) self.x = GAME_W - margin; self.shootCooldown--; // Update health bar healthBar.scaleX = 1.25 * (self.health / self.maxHealth); if (self.health < 1) healthBar.scaleX = 0; }; self.takeDamage = function (dmg) { self.health -= dmg; LK.effects.flashObject(self, 0xff4444, 100); if (self.health <= 0) self.health = 0; }; return self; }); // ZigzagEnemy: moves in zigzag, normal health var ZigzagEnemy = Container.expand(function () { var self = Container.call(this); var enemyGfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); enemyGfx.tint = 0xffe100; self.radius = enemyGfx.width * 0.5; self.speed = ENEMY_ZIGZAG_SPEED + Math.random() * 3; self.moveType = 'zigzag'; self.zigzagDir = Math.random() < 0.5 ? -1 : 1; self.zigzagTimer = 0; self.shootCooldown = 60 + Math.floor(Math.random() * 60); self.maxHealth = 2; self.health = self.maxHealth; // Health bar var healthBarBg = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 0.18, tint: 0x222222, y: enemyGfx.height * 0.7 }); var healthBar = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.95, scaleY: 0.12, tint: 0xffe100, y: enemyGfx.height * 0.7 }); self.update = function () { self.y += self.speed; self.zigzagTimer++; if (self.zigzagTimer % 30 === 0) self.zigzagDir *= -1; self.x += self.zigzagDir * 18; // Clamp enemy inside left/right screen bounds var margin = self.radius; if (self.x < margin) self.x = margin; if (self.x > GAME_W - margin) self.x = GAME_W - margin; self.shootCooldown--; // Update health bar healthBar.scaleX = 0.95 * (self.health / self.maxHealth); if (self.health < 1) healthBar.scaleX = 0; }; self.takeDamage = function (dmg) { self.health -= dmg; LK.effects.flashObject(self, 0xffe100, 80); if (self.health <= 0) self.health = 0; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ // No title, no description // Always backgroundColor is black backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game area // Spaceship (player) // Player bullet // Enemy // Enemy bullet // Powerup // Sound effects // Music var GAME_W = 2048, GAME_H = 2732; // Score var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var multiplierTxt = new Text2('x1', { size: 70, fill: 0x00ff88 }); multiplierTxt.anchor.set(0.5, 0); multiplierTxt.x = scoreTxt.x + 220; multiplierTxt.y = scoreTxt.y + 40; LK.gui.top.addChild(multiplierTxt); // Combo system variables var comboCount = 0; var comboTimer = 0; var lastComboTime = 0; // Double score powerup var doubleScoreActive = false; var doubleScoreTimer = 0; // Slow motion powerup var slowMotionActive = false; var slowMotionTimer = 0; // Triple shot powerup var tripleShotActive = false; var tripleShotTimer = 0; // Invincibility powerup var invincibilityActive = false; var invincibilityTimer = 0; // Player ship var ship = new Ship(); game.addChild(ship); ship.x = GAME_W / 2; ship.y = GAME_H - 350; // Arrays for game objects var playerBullets = []; var enemies = []; var enemyBullets = []; var powerups = []; // Dragging var dragNode = null; // Difficulty (tuned for human reflexes) // Made easier: enemies spawn less frequently var enemySpawnTimer = 0; var enemySpawnInterval = 110; // slower spawn var minEnemyInterval = 44; // minimum interval increased var enemySpeedInc = 0; // Powerup spawn var powerupTimer = 0; var powerupInterval = 120; // Powerups much more frequent // --- Reflex tuning: adjust bullet and enemy speeds globally --- // Made easier: all speeds reduced, player shoots faster var PLAYER_BULLET_SPEED = -18; // slower bullet, easier to dodge var ENEMY_BULLET_SPEED = 9; // slower enemy bullet var ENEMY_BASE_SPEED = 4.5; // slower base enemy var ENEMY_FAST_SPEED = 8; // slower fast enemy var ENEMY_TANK_SPEED = 1.7; // slower tank var ENEMY_ZIGZAG_SPEED = 5; // slower zigzag var PLAYER_SHOOT_RATE = 16; // player shoots more often // Last intersect states var lastShipEnemyIntersect = false; var lastShipEnemyBulletIntersect = false; var lastShipPowerupIntersect = false; // Music LK.playMusic('bgmusic'); // Move handler (drag ship) function handleMove(x, y, obj) { if (dragNode === ship) { // Clamp ship inside game area (with margin) var margin = 80; var nx = Math.max(margin, Math.min(GAME_W - margin, x)); var ny = Math.max(margin, Math.min(GAME_H - margin, y)); ship.x = nx; ship.y = ny; // Touch feedback: scale ship up slightly while dragging, then back ship.scale.set(1.15, 1.15); tween(ship.scale, { x: 1, y: 1 }, { duration: 120 }); } } game.move = handleMove; var dragAnywhereHintShown = false; game.down = function (x, y, obj) { // User-friendly: allow drag to start if touch is on ship OR anywhere in lower half of screen var dx = x - ship.x, dy = y - ship.y; var onShip = dx * dx + dy * dy < ship.radius * ship.radius * 1.2; var inLowerHalf = y > GAME_H / 2; if (onShip || inLowerHalf) { dragNode = ship; handleMove(x, y, obj); // Visual feedback: flash ship blue for 120ms on drag start LK.effects.flashObject(ship, 0x33c1ff, 120); // Show floating helper text only the first time user drags from lower half (not on ship) if (!onShip && inLowerHalf && !dragAnywhereHintShown) { showFloatingText("Tip: Drag anywhere below to move!", ship.x, ship.y - 180, 0x33c1ff); dragAnywhereHintShown = true; } } }; game.up = function (x, y, obj) { dragNode = null; }; // Main update loop game.update = function () { // Update ship ship.update(); // --- Double Score timer --- if (doubleScoreActive) { doubleScoreTimer--; if (doubleScoreTimer <= 0) { doubleScoreActive = false; showFloatingText("Double Score Ended", ship.x, ship.y - 120, 0xff00ff); } } // --- Slow Motion timer --- if (slowMotionActive) { slowMotionTimer--; if (slowMotionTimer <= 0) { slowMotionActive = false; showFloatingText("Speed Restored", ship.x, ship.y - 120, 0x8888ff); } } // --- Triple Shot timer --- if (tripleShotActive) { tripleShotTimer--; if (tripleShotTimer <= 0) { tripleShotActive = false; showFloatingText("Triple Shot Ended", ship.x, ship.y - 120, 0xff8800); } } // --- Invincibility timer --- if (invincibilityActive) { invincibilityTimer--; if (invincibilityTimer <= 0) { invincibilityActive = false; showFloatingText("Invincibility Ended", ship.x, ship.y - 120, 0xffffff); } } // --- Player shooting --- ship.shootCooldown--; var shootRate = ship.rapidFire ? Math.max(6, Math.floor(PLAYER_SHOOT_RATE * 0.33)) : PLAYER_SHOOT_RATE; if (ship.shootCooldown <= 0) { // Auto-fire if (tripleShotActive) { for (var ts = -1; ts <= 1; ts++) { var pb = new PlayerBullet(); pb.x = ship.x + ts * 38; pb.y = ship.y - ship.radius - 30; if (ts !== 0) pb.rotation = ts * 0.18; playerBullets.push(pb); game.addChild(pb); } } else { var pb = new PlayerBullet(); pb.x = ship.x; pb.y = ship.y - ship.radius - 30; playerBullets.push(pb); game.addChild(pb); } ship.shootCooldown = shootRate; LK.getSound('shoot').play(); } // --- Update player bullets --- for (var i = playerBullets.length - 1; i >= 0; i--) { var b = playerBullets[i]; b.update(); // Remove if off screen if (b.y < -80) { b.destroy(); playerBullets.splice(i, 1); } } // --- Update enemies --- for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; // --- Enemy AI: dodge if player is close horizontally --- if (Math.abs(ship.x - e.x) < 180 && Math.abs(ship.y - e.y) < 600) { // Try to dodge left or right, but less distance for fairness if (ship.x < e.x) e.x += 6 + Math.random() * 3;else e.x -= 6 + Math.random() * 3; } // Slow motion: update less frequently if (!slowMotionActive || LK.ticks % 2 === 0) { e.update(); } // Destroy and remove enemies when they move off the bottom of the screen if (e.y > GAME_H + e.radius) { e.destroy(); enemies.splice(i, 1); continue; } // (Removed clamping: enemies can now leave the screen at the bottom) // Enemy shooting if (e.shootCooldown <= 0) { var eb = new EnemyBullet(); eb.x = e.x; eb.y = e.y + e.radius + 10; // Aim at ship var dx = ship.x - e.x, dy = ship.y - e.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { eb.dirX = dx / len; eb.dirY = dy / len; } enemyBullets.push(eb); game.addChild(eb); e.shootCooldown = 90 + Math.floor(Math.random() * 60); } } // --- Update enemy bullets --- for (var i = enemyBullets.length - 1; i >= 0; i--) { var eb = enemyBullets[i]; // Slow motion: update less frequently if (!slowMotionActive || LK.ticks % 2 === 0) { eb.update(); } if (eb.x < -100 || eb.x > GAME_W + 100 || eb.y < -100 || eb.y > GAME_H + 100) { eb.destroy(); enemyBullets.splice(i, 1); } } // --- Update powerups --- for (var i = powerups.length - 1; i >= 0; i--) { var p = powerups[i]; p.update(); if (p.y > GAME_H + 100) { p.destroy(); powerups.splice(i, 1); } } // --- Collision: Player bullets vs Enemies --- for (var i = playerBullets.length - 1; i >= 0; i--) { var b = playerBullets[i]; for (var j = enemies.length - 1; j >= 0; j--) { var e = enemies[j]; if (b.intersects(e)) { // Hit enemy e.takeDamage(1); // Recoil effect for enemy tween(e, { y: e.y - 30 }, { duration: 60, yoyo: true, repeat: 1 }); // Camera shake on hit (simulate with quick flash) LK.getSound('enemyDown').play(); // Combo system variables (global scope, but initialize if undefined) if (typeof comboCount === "undefined") comboCount = 0; if (typeof comboTimer === "undefined") comboTimer = 0; if (typeof lastComboTime === "undefined") lastComboTime = 0; // Score only if enemy dies if (e.health <= 0) { // Combo logic: if last kill was within 2 seconds, increase combo var now = LK.ticks; if (now - lastComboTime < 120) { comboCount++; } else { comboCount = 1; } lastComboTime = now; comboTimer = 120; // 2 seconds to continue combo // Combo bonus: +10 base, +5 per combo after first, multiplied by scoreMultiplier if (typeof scoreMultiplier === "undefined") scoreMultiplier = 1; var comboBonus = 10 + (comboCount > 1 ? (comboCount - 1) * 5 : 0); if (doubleScoreActive) comboBonus *= 2; comboBonus *= scoreMultiplier; score += comboBonus; // Animate score text for feedback scoreTxt.setText(score); if (typeof multiplierTxt !== "undefined") { multiplierTxt.setText("x" + scoreMultiplier); multiplierTxt.scale.set(1.3, 1.3); tween(multiplierTxt.scale, { x: 1, y: 1 }, { duration: 200 }); } scoreTxt.scale.set(1.25, 1.25); tween(scoreTxt.scale, { x: 1, y: 1 }, { duration: 200 }); // Show floating text for combo if (comboCount > 1) { showFloatingText("Combo x" + comboCount + "! +" + comboBonus, e.x, e.y - 80, 0xffe100); if (comboCount % 3 === 0) { showFloatingText("Multiplier: x" + scoreMultiplier, e.x, e.y - 160, 0x00ff88); } } else { showFloatingText("+10", e.x, e.y - 80, 0xffffff); } // Fun: random emoji on enemy kill! var emojis = ["🚀", "💥", "✨", "🔥", "😎", "🎉", "🛸", "👾"]; var emoji = emojis[Math.floor(Math.random() * emojis.length)]; showFloatingText(emoji, e.x + (Math.random() * 80 - 40), e.y - 120, 0xffffff); e.destroy(); enemies.splice(j, 1); } b.destroy(); playerBullets.splice(i, 1); break; } } } // --- Combo timer update --- if (typeof comboTimer !== "undefined" && comboTimer > 0) { comboTimer--; if (comboTimer === 0) { comboCount = 0; scoreMultiplier = 1; // Reset multiplier on missed combo } } if (comboCount > 1) { scoreMultiplier = 1 + Math.floor(comboCount / 3); // Every 3 combo increases multiplier by 1 } else { scoreMultiplier = 1; } // --- Collision: Ship vs Enemies --- var shipEnemyIntersect = false; for (var i = 0; i < enemies.length; i++) { var e = enemies[i]; if (ship.intersects(e)) { shipEnemyIntersect = true; break; } } if (!lastShipEnemyIntersect && shipEnemyIntersect) { if (invincibilityActive) { LK.effects.flashObject(ship, 0xffffff, 400); showFloatingText("No Damage!", ship.x, ship.y - 120, 0xffffff); } else if (ship.shield) { // Absorb hit, destroy enemy LK.effects.flashObject(ship, 0x00ffff, 400); for (var i = 0; i < enemies.length; i++) { if (ship.intersects(enemies[i])) { enemies[i].destroy(); enemies.splice(i, 1); break; } } ship.shield = false; ship.shieldTimer = 0; ship.children[1].visible = false; } else { // Take damage ship.takeDamage(2); // (Screen flash removed) LK.getSound('hit').play(); if (ship.health <= 0) { LK.showGameOver(); return; } } } lastShipEnemyIntersect = shipEnemyIntersect; // --- Collision: Ship vs Enemy Bullets --- var shipEnemyBulletIntersect = false; for (var i = 0; i < enemyBullets.length; i++) { var eb = enemyBullets[i]; if (ship.intersects(eb)) { shipEnemyBulletIntersect = true; break; } } if (!lastShipEnemyBulletIntersect && shipEnemyBulletIntersect) { if (invincibilityActive) { LK.effects.flashObject(ship, 0xffffff, 400); showFloatingText("No Damage!", ship.x, ship.y - 120, 0xffffff); } else if (ship.shield) { LK.effects.flashObject(ship, 0x00ffff, 400); // Remove bullet for (var i = 0; i < enemyBullets.length; i++) { if (ship.intersects(enemyBullets[i])) { enemyBullets[i].destroy(); enemyBullets.splice(i, 1); break; } } ship.shield = false; ship.shieldTimer = 0; ship.children[1].visible = false; } else { // Take damage ship.takeDamage(1); // (Screen flash removed) LK.getSound('hit').play(); if (ship.health <= 0) { LK.showGameOver(); return; } } } lastShipEnemyBulletIntersect = shipEnemyBulletIntersect; // --- Collision: Ship vs Powerups --- var shipPowerupIntersect = false; for (var i = powerups.length - 1; i >= 0; i--) { var p = powerups[i]; if (ship.intersects(p)) { shipPowerupIntersect = true; // Apply powerup LK.getSound('powerup').play(); // Fun: confetti burst effect on powerup collect! for (var confetti = 0; confetti < 18; confetti++) { (function () { var part = new Container(); var c = part.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4 + Math.random() * 0.5, scaleY: 0.4 + Math.random() * 0.5, tint: 0xffe100 + Math.floor(Math.random() * 0xffffff) }); part.x = ship.x; part.y = ship.y - 40; part.alpha = 0.85; game.addChild(part); var angle = Math.random() * Math.PI * 2; var dist = 120 + Math.random() * 80; var dx = Math.cos(angle) * dist; var dy = Math.sin(angle) * dist; tween(part, { x: part.x + dx, y: part.y + dy, alpha: 0 }, { duration: 600 + Math.random() * 400, onFinish: function onFinish() { part.destroy(); } }); })(); } // Combo stacking: combine effects if already active, extend duration, and show combo text! if (p.type === 'rapid') { if (ship.rapidFire) { ship.rapidFireTimer += 240; // stack duration showFloatingText("Rapid Fire Combo!", ship.x, ship.y - 120, 0xffe100); } else { ship.activateRapidFire(360); showFloatingText("Rapid Fire!", ship.x, ship.y - 120, 0xffe100); } } else if (p.type === 'shield') { if (ship.shield) { ship.shieldTimer += 320; showFloatingText("Shield Combo!", ship.x, ship.y - 120, 0x00ffff); } else { ship.activateShield(480); showFloatingText("Shield!", ship.x, ship.y - 120, 0x00ffff); } } else if (p.type === 'heal') { ship.heal(4); // Heal more showFloatingText("+4 Health", ship.x, ship.y - 120, 0x44ff44); } else if (p.type === 'doubleScore') { if (doubleScoreActive) { doubleScoreTimer += 400; showFloatingText("Double Score Combo!", ship.x, ship.y - 120, 0xff00ff); } else { doubleScoreActive = true; doubleScoreTimer = 600; showFloatingText("Double Score!", ship.x, ship.y - 120, 0xff00ff); } } else if (p.type === 'slowMotion') { if (slowMotionActive) { slowMotionTimer += 300; showFloatingText("Slow Motion Combo!", ship.x, ship.y - 120, 0x8888ff); } else { slowMotionActive = true; slowMotionTimer = 420; showFloatingText("Slow Motion!", ship.x, ship.y - 120, 0x8888ff); } } else if (p.type === 'tripleShot') { if (tripleShotActive) { tripleShotTimer += 360; showFloatingText("Triple Shot Combo!", ship.x, ship.y - 120, 0xff8800); } else { tripleShotActive = true; tripleShotTimer = 540; showFloatingText("Triple Shot!", ship.x, ship.y - 120, 0xff8800); } } else if (p.type === 'megaShield') { if (ship.shield) { ship.shieldTimer += 800; showFloatingText("Mega Shield Combo!", ship.x, ship.y - 120, 0x00ff88); } else { ship.activateShield(1200); showFloatingText("Mega Shield!", ship.x, ship.y - 120, 0x00ff88); } } else if (p.type === 'invincibility') { if (invincibilityActive) { invincibilityTimer += 240; showFloatingText("Invincible Combo!", ship.x, ship.y - 120, 0xffffff); } else { invincibilityActive = true; invincibilityTimer = 360; showFloatingText("Invincible!", ship.x, ship.y - 120, 0xffffff); } } // Combo fusion: if multiple effects are active, show a special floating text! var comboActiveCount = 0; if (ship.rapidFire) comboActiveCount++; if (doubleScoreActive) comboActiveCount++; if (tripleShotActive) comboActiveCount++; if (slowMotionActive) comboActiveCount++; if (invincibilityActive) comboActiveCount++; if (ship.shield) comboActiveCount++; if (comboActiveCount >= 3) { showFloatingText("Combo Fusion! (" + comboActiveCount + ")", ship.x, ship.y - 220, 0xffe100); } p.destroy(); powerups.splice(i, 1); } } lastShipPowerupIntersect = shipPowerupIntersect; // --- Mission system: show floating text for milestones --- if (score > 0 && score % 100 === 0 && !game['milestone_' + score]) { showFloatingText("Milestone: " + score + "!", GAME_W / 2, 320, 0xffe100); game['milestone_' + score] = true; } // --- Background color cycling every 200 points --- // Define a palette of beautiful background colors if (typeof bgColors === "undefined") { var bgColors = [0x000000, // black 0x1a237e, // indigo 0x004d40, // teal dark 0x263238, // blue grey 0x880e4f, // deep pink 0x1565c0, // blue 0x2e7d32, // green 0xff6f00, // orange 0x4a148c, // purple 0x212121, // dark grey 0x00695c, // teal 0x3949ab, // blue indigo 0xc62828, // red 0x00838f, // cyan 0x6d4c41, // brown 0x283593 // deep blue ]; var lastBgColorIndex = 0; var lastBgScoreStep = 0; } // Calculate which color to use based on score var bgScoreStep = Math.floor(score / 200); if (bgScoreStep !== lastBgScoreStep) { // Cycle through palette, skip black after first var colorIdx = bgScoreStep % bgColors.length; if (colorIdx === 0 && bgScoreStep > 0) colorIdx = 1; game.setBackgroundColor(bgColors[colorIdx]); lastBgColorIndex = colorIdx; lastBgScoreStep = bgScoreStep; } // --- Feature selection popup removed --- // --- Enemy spawn --- enemySpawnTimer--; if (enemySpawnTimer <= 0) { // Randomly pick enemy type var enemyTypeRand = Math.random(); var e; if (enemyTypeRand < 0.25) { e = new FastEnemy(); } else if (enemyTypeRand < 0.5) { e = new TankEnemy(); } else if (enemyTypeRand < 0.75) { e = new ZigzagEnemy(); } else { e = new Enemy(); } e.x = 180 + Math.random() * (GAME_W - 360); e.y = -100; // Removed enemy speed scaling with score to keep game speed consistent enemies.push(e); game.addChild(e); // Decrease interval as score increases enemySpawnInterval = Math.max(minEnemyInterval, 60 - Math.floor(score / 100) * 4); enemySpawnTimer = enemySpawnInterval; } // --- Powerup spawn --- powerupTimer--; if (powerupTimer <= 0) { var p = new Powerup(); p.x = 180 + Math.random() * (GAME_W - 360); p.y = -80; powerups.push(p); game.addChild(p); powerupInterval = 120 + Math.floor(Math.random() * 120); powerupTimer = powerupInterval; } }; // Floating text helper for feedback function showFloatingText(txt, x, y, color) { var t = new Text2(txt, { size: 90, fill: color || 0xffffff, stroke: 0x000000, strokeThickness: 8 }); t.anchor.set(0.5, 1); t.x = x; t.y = y; game.addChild(t); tween(t, { y: y - 120, alpha: 0 }, { duration: 700, onFinish: function onFinish() { t.destroy(); } }); } // Show instructions at game start var instructionTxt = new Text2("Drag the ship to move\nAuto-fire enabled\nCollect powerups!", { size: 90, fill: 0xffffff, stroke: 0x000000, strokeThickness: 8, align: "center" }); instructionTxt.anchor.set(0.5, 0.5); instructionTxt.x = GAME_W / 2; instructionTxt.y = GAME_H / 2 - 200; game.addChild(instructionTxt); tween(instructionTxt, { alpha: 0 }, { duration: 1800, onFinish: function onFinish() { instructionTxt.destroy(); } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy (Base class)
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGfx = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = enemyGfx.width * 0.5;
self.speed = ENEMY_BASE_SPEED + Math.random() * 4;
self.moveType = Math.random() < 0.5 ? 'straight' : 'sine';
self.sinePhase = Math.random() * Math.PI * 2;
self.sineAmp = 120 + Math.random() * 120;
self.shootCooldown = 60 + Math.floor(Math.random() * 60);
// Health system for enemy
self.maxHealth = 1;
self.health = self.maxHealth;
// Health bar
var healthBarBg = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 0.18,
tint: 0x222222,
y: enemyGfx.height * 0.7
});
var healthBar = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.95,
scaleY: 0.12,
tint: 0x44ff44,
y: enemyGfx.height * 0.7
});
self.update = function () {
if (self.moveType === 'straight') {
self.y += self.speed;
} else {
self.y += self.speed * 0.85;
self.x += Math.sin(self.y / 120 + self.sinePhase) * 6;
}
// Clamp enemy inside left/right screen bounds
var margin = self.radius;
if (self.x < margin) self.x = margin;
if (self.x > GAME_W - margin) self.x = GAME_W - margin;
self.shootCooldown--;
// Update health bar
healthBar.scaleX = 0.95 * (self.health / self.maxHealth);
if (self.health < 1) healthBar.scaleX = 0;
};
self.takeDamage = function (dmg) {
self.health -= dmg;
LK.effects.flashObject(self, 0xffffff, 80);
if (self.health <= 0) {
self.health = 0;
// Death handled in game loop
}
};
return self;
});
// Enemy Bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = ENEMY_BULLET_SPEED;
self.dirX = 0;
self.dirY = 1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// FastEnemy: moves faster, less health
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGfx = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
enemyGfx.tint = 0x00e1ff;
self.radius = enemyGfx.width * 0.5;
self.speed = ENEMY_FAST_SPEED + Math.random() * 4;
self.moveType = 'straight';
self.shootCooldown = 40 + Math.floor(Math.random() * 40);
self.maxHealth = 1;
self.health = self.maxHealth;
// Health bar
var healthBarBg = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 0.18,
tint: 0x222222,
y: enemyGfx.height * 0.7
});
var healthBar = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.95,
scaleY: 0.12,
tint: 0x00e1ff,
y: enemyGfx.height * 0.7
});
self.update = function () {
self.y += self.speed;
// Clamp enemy inside left/right screen bounds
var margin = self.radius;
if (self.x < margin) self.x = margin;
if (self.x > GAME_W - margin) self.x = GAME_W - margin;
self.shootCooldown--;
// Update health bar
healthBar.scaleX = 0.95 * (self.health / self.maxHealth);
if (self.health < 1) healthBar.scaleX = 0;
};
self.takeDamage = function (dmg) {
self.health -= dmg;
LK.effects.flashObject(self, 0x00e1ff, 60);
if (self.health <= 0) self.health = 0;
};
return self;
});
// Player Bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = PLAYER_BULLET_SPEED;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Powerup
var Powerup = Container.expand(function () {
var self = Container.call(this);
// Add new powerup types: tripleShot, megaShield, invincibility, doubleScore, slowMotion
var types = ['rapid', 'shield', 'heal', 'doubleScore', 'slowMotion', 'tripleShot', 'megaShield', 'invincibility'];
var r = Math.random();
if (r < 0.22) self.type = 'rapid';else if (r < 0.37) self.type = 'shield';else if (r < 0.52) self.type = 'heal';else if (r < 0.65) self.type = 'doubleScore';else if (r < 0.77) self.type = 'slowMotion';else if (r < 0.87) self.type = 'tripleShot';else if (r < 0.95) self.type = 'megaShield';else self.type = 'invincibility';
var powerupGfx = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
if (self.type === 'rapid') powerupGfx.tint = 0xffe100;else if (self.type === 'shield') powerupGfx.tint = 0x00ffff;else if (self.type === 'heal') powerupGfx.tint = 0x44ff44;else if (self.type === 'doubleScore') powerupGfx.tint = 0xff00ff;else if (self.type === 'slowMotion') powerupGfx.tint = 0x8888ff;else if (self.type === 'tripleShot') powerupGfx.tint = 0xff8800;else if (self.type === 'megaShield') powerupGfx.tint = 0x00ff88;else if (self.type === 'invincibility') powerupGfx.tint = 0xffffff;
self.speed = 10;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Ship
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGfx = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = shipGfx.width * 0.5;
self.shootCooldown = 0;
self.rapidFire = false;
self.rapidFireTimer = 0;
self.shield = false;
self.shieldTimer = 0;
// Health system for player
self.maxHealth = 13; // Even more survivability for easier gameplay
self.health = self.maxHealth;
// Health bar
var healthBarBg = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 0.25,
tint: 0x222222,
y: shipGfx.height * 0.7
});
var healthBar = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.18,
tint: 0xff4444,
y: shipGfx.height * 0.7
});
// Visual shield indicator
var shieldGfx = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00ffff
});
shieldGfx.alpha = 0.25;
shieldGfx.visible = false;
self.update = function () {
// Handle powerup timers
if (self.rapidFire) {
self.rapidFireTimer--;
// Visual feedback for rapid fire: pulse ship color
shipGfx.tint = LK.ticks % 10 < 5 ? 0xffe100 : 0x33c1ff;
if (self.rapidFireTimer <= 0) {
self.rapidFire = false;
shipGfx.tint = 0x33c1ff;
}
} else {
shipGfx.tint = 0x33c1ff;
}
if (self.shield) {
self.shieldTimer--;
shieldGfx.visible = true;
if (self.shieldTimer <= 0) {
self.shield = false;
shieldGfx.visible = false;
}
} else {
shieldGfx.visible = false;
}
// Update health bar
healthBar.scaleX = 2 * (self.health / self.maxHealth);
if (self.health < 1) healthBar.scaleX = 0;
};
self.activateRapidFire = function (duration) {
self.rapidFire = true;
self.rapidFireTimer = duration;
};
self.activateShield = function (duration) {
self.shield = true;
self.shieldTimer = duration;
shieldGfx.visible = true;
};
self.takeDamage = function (dmg) {
if (self.shield) return;
self.health -= dmg;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health < 0) self.health = 0;
};
self.heal = function (amount) {
self.health += amount;
if (self.health > self.maxHealth) self.health = self.maxHealth;
};
return self;
});
// TankEnemy: slow, high health, shoots more
var TankEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGfx = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
});
enemyGfx.tint = 0xff4444;
self.radius = enemyGfx.width * 0.65;
self.speed = ENEMY_TANK_SPEED + Math.random() * 1.2;
self.moveType = 'sine';
self.sinePhase = Math.random() * Math.PI * 2;
self.sineAmp = 180 + Math.random() * 80;
self.shootCooldown = 30 + Math.floor(Math.random() * 30);
self.maxHealth = 4;
self.health = self.maxHealth;
// Health bar
var healthBarBg = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 0.18,
tint: 0x222222,
y: enemyGfx.height * 0.9
});
var healthBar = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.25,
scaleY: 0.12,
tint: 0xff4444,
y: enemyGfx.height * 0.9
});
self.update = function () {
self.y += self.speed * 0.7;
self.x += Math.sin(self.y / 120 + self.sinePhase) * 10;
// Clamp enemy inside left/right screen bounds
var margin = self.radius;
if (self.x < margin) self.x = margin;
if (self.x > GAME_W - margin) self.x = GAME_W - margin;
self.shootCooldown--;
// Update health bar
healthBar.scaleX = 1.25 * (self.health / self.maxHealth);
if (self.health < 1) healthBar.scaleX = 0;
};
self.takeDamage = function (dmg) {
self.health -= dmg;
LK.effects.flashObject(self, 0xff4444, 100);
if (self.health <= 0) self.health = 0;
};
return self;
});
// ZigzagEnemy: moves in zigzag, normal health
var ZigzagEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGfx = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
enemyGfx.tint = 0xffe100;
self.radius = enemyGfx.width * 0.5;
self.speed = ENEMY_ZIGZAG_SPEED + Math.random() * 3;
self.moveType = 'zigzag';
self.zigzagDir = Math.random() < 0.5 ? -1 : 1;
self.zigzagTimer = 0;
self.shootCooldown = 60 + Math.floor(Math.random() * 60);
self.maxHealth = 2;
self.health = self.maxHealth;
// Health bar
var healthBarBg = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 0.18,
tint: 0x222222,
y: enemyGfx.height * 0.7
});
var healthBar = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.95,
scaleY: 0.12,
tint: 0xffe100,
y: enemyGfx.height * 0.7
});
self.update = function () {
self.y += self.speed;
self.zigzagTimer++;
if (self.zigzagTimer % 30 === 0) self.zigzagDir *= -1;
self.x += self.zigzagDir * 18;
// Clamp enemy inside left/right screen bounds
var margin = self.radius;
if (self.x < margin) self.x = margin;
if (self.x > GAME_W - margin) self.x = GAME_W - margin;
self.shootCooldown--;
// Update health bar
healthBar.scaleX = 0.95 * (self.health / self.maxHealth);
if (self.health < 1) healthBar.scaleX = 0;
};
self.takeDamage = function (dmg) {
self.health -= dmg;
LK.effects.flashObject(self, 0xffe100, 80);
if (self.health <= 0) self.health = 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game area
// Spaceship (player)
// Player bullet
// Enemy
// Enemy bullet
// Powerup
// Sound effects
// Music
var GAME_W = 2048,
GAME_H = 2732;
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var multiplierTxt = new Text2('x1', {
size: 70,
fill: 0x00ff88
});
multiplierTxt.anchor.set(0.5, 0);
multiplierTxt.x = scoreTxt.x + 220;
multiplierTxt.y = scoreTxt.y + 40;
LK.gui.top.addChild(multiplierTxt);
// Combo system variables
var comboCount = 0;
var comboTimer = 0;
var lastComboTime = 0;
// Double score powerup
var doubleScoreActive = false;
var doubleScoreTimer = 0;
// Slow motion powerup
var slowMotionActive = false;
var slowMotionTimer = 0;
// Triple shot powerup
var tripleShotActive = false;
var tripleShotTimer = 0;
// Invincibility powerup
var invincibilityActive = false;
var invincibilityTimer = 0;
// Player ship
var ship = new Ship();
game.addChild(ship);
ship.x = GAME_W / 2;
ship.y = GAME_H - 350;
// Arrays for game objects
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
var powerups = [];
// Dragging
var dragNode = null;
// Difficulty (tuned for human reflexes)
// Made easier: enemies spawn less frequently
var enemySpawnTimer = 0;
var enemySpawnInterval = 110; // slower spawn
var minEnemyInterval = 44; // minimum interval increased
var enemySpeedInc = 0;
// Powerup spawn
var powerupTimer = 0;
var powerupInterval = 120; // Powerups much more frequent
// --- Reflex tuning: adjust bullet and enemy speeds globally ---
// Made easier: all speeds reduced, player shoots faster
var PLAYER_BULLET_SPEED = -18; // slower bullet, easier to dodge
var ENEMY_BULLET_SPEED = 9; // slower enemy bullet
var ENEMY_BASE_SPEED = 4.5; // slower base enemy
var ENEMY_FAST_SPEED = 8; // slower fast enemy
var ENEMY_TANK_SPEED = 1.7; // slower tank
var ENEMY_ZIGZAG_SPEED = 5; // slower zigzag
var PLAYER_SHOOT_RATE = 16; // player shoots more often
// Last intersect states
var lastShipEnemyIntersect = false;
var lastShipEnemyBulletIntersect = false;
var lastShipPowerupIntersect = false;
// Music
LK.playMusic('bgmusic');
// Move handler (drag ship)
function handleMove(x, y, obj) {
if (dragNode === ship) {
// Clamp ship inside game area (with margin)
var margin = 80;
var nx = Math.max(margin, Math.min(GAME_W - margin, x));
var ny = Math.max(margin, Math.min(GAME_H - margin, y));
ship.x = nx;
ship.y = ny;
// Touch feedback: scale ship up slightly while dragging, then back
ship.scale.set(1.15, 1.15);
tween(ship.scale, {
x: 1,
y: 1
}, {
duration: 120
});
}
}
game.move = handleMove;
var dragAnywhereHintShown = false;
game.down = function (x, y, obj) {
// User-friendly: allow drag to start if touch is on ship OR anywhere in lower half of screen
var dx = x - ship.x,
dy = y - ship.y;
var onShip = dx * dx + dy * dy < ship.radius * ship.radius * 1.2;
var inLowerHalf = y > GAME_H / 2;
if (onShip || inLowerHalf) {
dragNode = ship;
handleMove(x, y, obj);
// Visual feedback: flash ship blue for 120ms on drag start
LK.effects.flashObject(ship, 0x33c1ff, 120);
// Show floating helper text only the first time user drags from lower half (not on ship)
if (!onShip && inLowerHalf && !dragAnywhereHintShown) {
showFloatingText("Tip: Drag anywhere below to move!", ship.x, ship.y - 180, 0x33c1ff);
dragAnywhereHintShown = true;
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main update loop
game.update = function () {
// Update ship
ship.update();
// --- Double Score timer ---
if (doubleScoreActive) {
doubleScoreTimer--;
if (doubleScoreTimer <= 0) {
doubleScoreActive = false;
showFloatingText("Double Score Ended", ship.x, ship.y - 120, 0xff00ff);
}
}
// --- Slow Motion timer ---
if (slowMotionActive) {
slowMotionTimer--;
if (slowMotionTimer <= 0) {
slowMotionActive = false;
showFloatingText("Speed Restored", ship.x, ship.y - 120, 0x8888ff);
}
}
// --- Triple Shot timer ---
if (tripleShotActive) {
tripleShotTimer--;
if (tripleShotTimer <= 0) {
tripleShotActive = false;
showFloatingText("Triple Shot Ended", ship.x, ship.y - 120, 0xff8800);
}
}
// --- Invincibility timer ---
if (invincibilityActive) {
invincibilityTimer--;
if (invincibilityTimer <= 0) {
invincibilityActive = false;
showFloatingText("Invincibility Ended", ship.x, ship.y - 120, 0xffffff);
}
}
// --- Player shooting ---
ship.shootCooldown--;
var shootRate = ship.rapidFire ? Math.max(6, Math.floor(PLAYER_SHOOT_RATE * 0.33)) : PLAYER_SHOOT_RATE;
if (ship.shootCooldown <= 0) {
// Auto-fire
if (tripleShotActive) {
for (var ts = -1; ts <= 1; ts++) {
var pb = new PlayerBullet();
pb.x = ship.x + ts * 38;
pb.y = ship.y - ship.radius - 30;
if (ts !== 0) pb.rotation = ts * 0.18;
playerBullets.push(pb);
game.addChild(pb);
}
} else {
var pb = new PlayerBullet();
pb.x = ship.x;
pb.y = ship.y - ship.radius - 30;
playerBullets.push(pb);
game.addChild(pb);
}
ship.shootCooldown = shootRate;
LK.getSound('shoot').play();
}
// --- Update player bullets ---
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.y < -80) {
b.destroy();
playerBullets.splice(i, 1);
}
}
// --- Update enemies ---
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
// --- Enemy AI: dodge if player is close horizontally ---
if (Math.abs(ship.x - e.x) < 180 && Math.abs(ship.y - e.y) < 600) {
// Try to dodge left or right, but less distance for fairness
if (ship.x < e.x) e.x += 6 + Math.random() * 3;else e.x -= 6 + Math.random() * 3;
}
// Slow motion: update less frequently
if (!slowMotionActive || LK.ticks % 2 === 0) {
e.update();
}
// Destroy and remove enemies when they move off the bottom of the screen
if (e.y > GAME_H + e.radius) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// (Removed clamping: enemies can now leave the screen at the bottom)
// Enemy shooting
if (e.shootCooldown <= 0) {
var eb = new EnemyBullet();
eb.x = e.x;
eb.y = e.y + e.radius + 10;
// Aim at ship
var dx = ship.x - e.x,
dy = ship.y - e.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
eb.dirX = dx / len;
eb.dirY = dy / len;
}
enemyBullets.push(eb);
game.addChild(eb);
e.shootCooldown = 90 + Math.floor(Math.random() * 60);
}
}
// --- Update enemy bullets ---
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var eb = enemyBullets[i];
// Slow motion: update less frequently
if (!slowMotionActive || LK.ticks % 2 === 0) {
eb.update();
}
if (eb.x < -100 || eb.x > GAME_W + 100 || eb.y < -100 || eb.y > GAME_H + 100) {
eb.destroy();
enemyBullets.splice(i, 1);
}
}
// --- Update powerups ---
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
p.update();
if (p.y > GAME_H + 100) {
p.destroy();
powerups.splice(i, 1);
}
}
// --- Collision: Player bullets vs Enemies ---
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
// Hit enemy
e.takeDamage(1);
// Recoil effect for enemy
tween(e, {
y: e.y - 30
}, {
duration: 60,
yoyo: true,
repeat: 1
});
// Camera shake on hit (simulate with quick flash)
LK.getSound('enemyDown').play();
// Combo system variables (global scope, but initialize if undefined)
if (typeof comboCount === "undefined") comboCount = 0;
if (typeof comboTimer === "undefined") comboTimer = 0;
if (typeof lastComboTime === "undefined") lastComboTime = 0;
// Score only if enemy dies
if (e.health <= 0) {
// Combo logic: if last kill was within 2 seconds, increase combo
var now = LK.ticks;
if (now - lastComboTime < 120) {
comboCount++;
} else {
comboCount = 1;
}
lastComboTime = now;
comboTimer = 120; // 2 seconds to continue combo
// Combo bonus: +10 base, +5 per combo after first, multiplied by scoreMultiplier
if (typeof scoreMultiplier === "undefined") scoreMultiplier = 1;
var comboBonus = 10 + (comboCount > 1 ? (comboCount - 1) * 5 : 0);
if (doubleScoreActive) comboBonus *= 2;
comboBonus *= scoreMultiplier;
score += comboBonus;
// Animate score text for feedback
scoreTxt.setText(score);
if (typeof multiplierTxt !== "undefined") {
multiplierTxt.setText("x" + scoreMultiplier);
multiplierTxt.scale.set(1.3, 1.3);
tween(multiplierTxt.scale, {
x: 1,
y: 1
}, {
duration: 200
});
}
scoreTxt.scale.set(1.25, 1.25);
tween(scoreTxt.scale, {
x: 1,
y: 1
}, {
duration: 200
});
// Show floating text for combo
if (comboCount > 1) {
showFloatingText("Combo x" + comboCount + "! +" + comboBonus, e.x, e.y - 80, 0xffe100);
if (comboCount % 3 === 0) {
showFloatingText("Multiplier: x" + scoreMultiplier, e.x, e.y - 160, 0x00ff88);
}
} else {
showFloatingText("+10", e.x, e.y - 80, 0xffffff);
}
// Fun: random emoji on enemy kill!
var emojis = ["🚀", "💥", "✨", "🔥", "😎", "🎉", "🛸", "👾"];
var emoji = emojis[Math.floor(Math.random() * emojis.length)];
showFloatingText(emoji, e.x + (Math.random() * 80 - 40), e.y - 120, 0xffffff);
e.destroy();
enemies.splice(j, 1);
}
b.destroy();
playerBullets.splice(i, 1);
break;
}
}
}
// --- Combo timer update ---
if (typeof comboTimer !== "undefined" && comboTimer > 0) {
comboTimer--;
if (comboTimer === 0) {
comboCount = 0;
scoreMultiplier = 1; // Reset multiplier on missed combo
}
}
if (comboCount > 1) {
scoreMultiplier = 1 + Math.floor(comboCount / 3); // Every 3 combo increases multiplier by 1
} else {
scoreMultiplier = 1;
}
// --- Collision: Ship vs Enemies ---
var shipEnemyIntersect = false;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
if (ship.intersects(e)) {
shipEnemyIntersect = true;
break;
}
}
if (!lastShipEnemyIntersect && shipEnemyIntersect) {
if (invincibilityActive) {
LK.effects.flashObject(ship, 0xffffff, 400);
showFloatingText("No Damage!", ship.x, ship.y - 120, 0xffffff);
} else if (ship.shield) {
// Absorb hit, destroy enemy
LK.effects.flashObject(ship, 0x00ffff, 400);
for (var i = 0; i < enemies.length; i++) {
if (ship.intersects(enemies[i])) {
enemies[i].destroy();
enemies.splice(i, 1);
break;
}
}
ship.shield = false;
ship.shieldTimer = 0;
ship.children[1].visible = false;
} else {
// Take damage
ship.takeDamage(2);
// (Screen flash removed)
LK.getSound('hit').play();
if (ship.health <= 0) {
LK.showGameOver();
return;
}
}
}
lastShipEnemyIntersect = shipEnemyIntersect;
// --- Collision: Ship vs Enemy Bullets ---
var shipEnemyBulletIntersect = false;
for (var i = 0; i < enemyBullets.length; i++) {
var eb = enemyBullets[i];
if (ship.intersects(eb)) {
shipEnemyBulletIntersect = true;
break;
}
}
if (!lastShipEnemyBulletIntersect && shipEnemyBulletIntersect) {
if (invincibilityActive) {
LK.effects.flashObject(ship, 0xffffff, 400);
showFloatingText("No Damage!", ship.x, ship.y - 120, 0xffffff);
} else if (ship.shield) {
LK.effects.flashObject(ship, 0x00ffff, 400);
// Remove bullet
for (var i = 0; i < enemyBullets.length; i++) {
if (ship.intersects(enemyBullets[i])) {
enemyBullets[i].destroy();
enemyBullets.splice(i, 1);
break;
}
}
ship.shield = false;
ship.shieldTimer = 0;
ship.children[1].visible = false;
} else {
// Take damage
ship.takeDamage(1);
// (Screen flash removed)
LK.getSound('hit').play();
if (ship.health <= 0) {
LK.showGameOver();
return;
}
}
}
lastShipEnemyBulletIntersect = shipEnemyBulletIntersect;
// --- Collision: Ship vs Powerups ---
var shipPowerupIntersect = false;
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
if (ship.intersects(p)) {
shipPowerupIntersect = true;
// Apply powerup
LK.getSound('powerup').play();
// Fun: confetti burst effect on powerup collect!
for (var confetti = 0; confetti < 18; confetti++) {
(function () {
var part = new Container();
var c = part.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4 + Math.random() * 0.5,
scaleY: 0.4 + Math.random() * 0.5,
tint: 0xffe100 + Math.floor(Math.random() * 0xffffff)
});
part.x = ship.x;
part.y = ship.y - 40;
part.alpha = 0.85;
game.addChild(part);
var angle = Math.random() * Math.PI * 2;
var dist = 120 + Math.random() * 80;
var dx = Math.cos(angle) * dist;
var dy = Math.sin(angle) * dist;
tween(part, {
x: part.x + dx,
y: part.y + dy,
alpha: 0
}, {
duration: 600 + Math.random() * 400,
onFinish: function onFinish() {
part.destroy();
}
});
})();
}
// Combo stacking: combine effects if already active, extend duration, and show combo text!
if (p.type === 'rapid') {
if (ship.rapidFire) {
ship.rapidFireTimer += 240; // stack duration
showFloatingText("Rapid Fire Combo!", ship.x, ship.y - 120, 0xffe100);
} else {
ship.activateRapidFire(360);
showFloatingText("Rapid Fire!", ship.x, ship.y - 120, 0xffe100);
}
} else if (p.type === 'shield') {
if (ship.shield) {
ship.shieldTimer += 320;
showFloatingText("Shield Combo!", ship.x, ship.y - 120, 0x00ffff);
} else {
ship.activateShield(480);
showFloatingText("Shield!", ship.x, ship.y - 120, 0x00ffff);
}
} else if (p.type === 'heal') {
ship.heal(4); // Heal more
showFloatingText("+4 Health", ship.x, ship.y - 120, 0x44ff44);
} else if (p.type === 'doubleScore') {
if (doubleScoreActive) {
doubleScoreTimer += 400;
showFloatingText("Double Score Combo!", ship.x, ship.y - 120, 0xff00ff);
} else {
doubleScoreActive = true;
doubleScoreTimer = 600;
showFloatingText("Double Score!", ship.x, ship.y - 120, 0xff00ff);
}
} else if (p.type === 'slowMotion') {
if (slowMotionActive) {
slowMotionTimer += 300;
showFloatingText("Slow Motion Combo!", ship.x, ship.y - 120, 0x8888ff);
} else {
slowMotionActive = true;
slowMotionTimer = 420;
showFloatingText("Slow Motion!", ship.x, ship.y - 120, 0x8888ff);
}
} else if (p.type === 'tripleShot') {
if (tripleShotActive) {
tripleShotTimer += 360;
showFloatingText("Triple Shot Combo!", ship.x, ship.y - 120, 0xff8800);
} else {
tripleShotActive = true;
tripleShotTimer = 540;
showFloatingText("Triple Shot!", ship.x, ship.y - 120, 0xff8800);
}
} else if (p.type === 'megaShield') {
if (ship.shield) {
ship.shieldTimer += 800;
showFloatingText("Mega Shield Combo!", ship.x, ship.y - 120, 0x00ff88);
} else {
ship.activateShield(1200);
showFloatingText("Mega Shield!", ship.x, ship.y - 120, 0x00ff88);
}
} else if (p.type === 'invincibility') {
if (invincibilityActive) {
invincibilityTimer += 240;
showFloatingText("Invincible Combo!", ship.x, ship.y - 120, 0xffffff);
} else {
invincibilityActive = true;
invincibilityTimer = 360;
showFloatingText("Invincible!", ship.x, ship.y - 120, 0xffffff);
}
}
// Combo fusion: if multiple effects are active, show a special floating text!
var comboActiveCount = 0;
if (ship.rapidFire) comboActiveCount++;
if (doubleScoreActive) comboActiveCount++;
if (tripleShotActive) comboActiveCount++;
if (slowMotionActive) comboActiveCount++;
if (invincibilityActive) comboActiveCount++;
if (ship.shield) comboActiveCount++;
if (comboActiveCount >= 3) {
showFloatingText("Combo Fusion! (" + comboActiveCount + ")", ship.x, ship.y - 220, 0xffe100);
}
p.destroy();
powerups.splice(i, 1);
}
}
lastShipPowerupIntersect = shipPowerupIntersect;
// --- Mission system: show floating text for milestones ---
if (score > 0 && score % 100 === 0 && !game['milestone_' + score]) {
showFloatingText("Milestone: " + score + "!", GAME_W / 2, 320, 0xffe100);
game['milestone_' + score] = true;
}
// --- Background color cycling every 200 points ---
// Define a palette of beautiful background colors
if (typeof bgColors === "undefined") {
var bgColors = [0x000000,
// black
0x1a237e,
// indigo
0x004d40,
// teal dark
0x263238,
// blue grey
0x880e4f,
// deep pink
0x1565c0,
// blue
0x2e7d32,
// green
0xff6f00,
// orange
0x4a148c,
// purple
0x212121,
// dark grey
0x00695c,
// teal
0x3949ab,
// blue indigo
0xc62828,
// red
0x00838f,
// cyan
0x6d4c41,
// brown
0x283593 // deep blue
];
var lastBgColorIndex = 0;
var lastBgScoreStep = 0;
}
// Calculate which color to use based on score
var bgScoreStep = Math.floor(score / 200);
if (bgScoreStep !== lastBgScoreStep) {
// Cycle through palette, skip black after first
var colorIdx = bgScoreStep % bgColors.length;
if (colorIdx === 0 && bgScoreStep > 0) colorIdx = 1;
game.setBackgroundColor(bgColors[colorIdx]);
lastBgColorIndex = colorIdx;
lastBgScoreStep = bgScoreStep;
}
// --- Feature selection popup removed ---
// --- Enemy spawn ---
enemySpawnTimer--;
if (enemySpawnTimer <= 0) {
// Randomly pick enemy type
var enemyTypeRand = Math.random();
var e;
if (enemyTypeRand < 0.25) {
e = new FastEnemy();
} else if (enemyTypeRand < 0.5) {
e = new TankEnemy();
} else if (enemyTypeRand < 0.75) {
e = new ZigzagEnemy();
} else {
e = new Enemy();
}
e.x = 180 + Math.random() * (GAME_W - 360);
e.y = -100;
// Removed enemy speed scaling with score to keep game speed consistent
enemies.push(e);
game.addChild(e);
// Decrease interval as score increases
enemySpawnInterval = Math.max(minEnemyInterval, 60 - Math.floor(score / 100) * 4);
enemySpawnTimer = enemySpawnInterval;
}
// --- Powerup spawn ---
powerupTimer--;
if (powerupTimer <= 0) {
var p = new Powerup();
p.x = 180 + Math.random() * (GAME_W - 360);
p.y = -80;
powerups.push(p);
game.addChild(p);
powerupInterval = 120 + Math.floor(Math.random() * 120);
powerupTimer = powerupInterval;
}
};
// Floating text helper for feedback
function showFloatingText(txt, x, y, color) {
var t = new Text2(txt, {
size: 90,
fill: color || 0xffffff,
stroke: 0x000000,
strokeThickness: 8
});
t.anchor.set(0.5, 1);
t.x = x;
t.y = y;
game.addChild(t);
tween(t, {
y: y - 120,
alpha: 0
}, {
duration: 700,
onFinish: function onFinish() {
t.destroy();
}
});
}
// Show instructions at game start
var instructionTxt = new Text2("Drag the ship to move\nAuto-fire enabled\nCollect powerups!", {
size: 90,
fill: 0xffffff,
stroke: 0x000000,
strokeThickness: 8,
align: "center"
});
instructionTxt.anchor.set(0.5, 0.5);
instructionTxt.x = GAME_W / 2;
instructionTxt.y = GAME_H / 2 - 200;
game.addChild(instructionTxt);
tween(instructionTxt, {
alpha: 0
}, {
duration: 1800,
onFinish: function onFinish() {
instructionTxt.destroy();
}
});