User prompt
Сделай так чтоб некоторые звезды в случайный момент исчезали и появлялись ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Замени все белые точки на ресурс star разных цветов ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Добавь сверху в правом углу черную дыру
User prompt
Убирай ценник если скин уже куплин ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Опусти выбор цветов следа пониже
User prompt
Добавь вкладку в настройках для изменения цвета следа ↪💡 Consider importing and using the following plugins: @upit/storage.v1, @upit/tween.v1
User prompt
Добавь новый скин используя ресурс skinrocet ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Сделай так что. После смерти скины и деньги сохранялись ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Добавь скин используя рнсурс skinnlood ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Добавь возможность в настройках отключить след у звездолёта ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Добавь стоимость скину 5 $ ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Переименуй его в gosth
User prompt
Добавь новый скин используй ресурс skinfire ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Сделай так чтоб при открытии меню игра становиться на паузу как при выборе карты
User prompt
Убери скин файтера и добавь обычный скин ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Сделай так чтоб при нажатии на неё открывалось меню выбора скина корабля ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Добавь врехнию час правого бока наку настройки
User prompt
Добавь такой же след врагам ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Сделай след более яркой ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Сделай её более светлой и сделай врагам также только синего цвета ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Сделай появление полосы ниже
User prompt
Сделай так чтоб зади игрока появлялся красный путь который в конце становится прозрачным ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Повикси баг при котором основная музыка с музыкой Boss накладываются друг на друга после убийства босса
User prompt
Удали финального босса
User prompt
Убери механику при которой при выборе карты2 врыги возрастают в трое
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // New armored enemy ship: slower, more health, different sprite var ArmoredEnemyShip = Container.expand(function () { var self = Container.call(this); self.fireCooldown = 120; // Fires more often than normal enemy self.fireTimer = Math.random() * self.fireCooldown; self.enemyProjectileSpawnPoint = { x: 0, y: 0 }; self.hit = function () { if (self.isDestroyed) return; self.health -= 1; if (self.health <= 0) { self.isDestroyed = true; } else { // Flash magenta on hit if (self.shipSprite) LK.effects.flashObject(self.shipSprite, 0xe011a9, 80); } }; self.update = function () { if (self.isOffScreen || self.isDestroyed) return; if (typeof freezeEnemies !== 'undefined' && freezeEnemies) return; // Target the player ship if (typeof playerShip !== 'undefined' && playerShip && !playerShip.isDestroyed) { var dx = playerShip.x - self.x; var dy = playerShip.y - self.y; var targetAngle = Math.atan2(dy, dx); self.angle = targetAngle; if (self.shipSprite) self.shipSprite.rotation = self.angle + Math.PI / 2; } self.lastX = self.x; self.lastY = self.y; // Armored enemy moves slower var moveX = Math.cos(self.angle) * self.speed; var moveY = Math.sin(self.angle) * self.speed; if (typeof playerShip !== 'undefined' && playerShip && !playerShip.isDestroyed) { var dxToPlayer = self.x + moveX - playerShip.x; var dyToPlayer = self.y + moveY - playerShip.y; var distToPlayer = Math.sqrt(dxToPlayer * dxToPlayer + dyToPlayer * dyToPlayer); if (distToPlayer < 120) { moveX = 0; moveY = 0; } } self.x += moveX; self.y += moveY; var shipAsset = self.shipSprite; if (shipAsset) { var noseDistance = shipAsset.height / 2; self.enemyProjectileSpawnPoint.x = self.x + Math.cos(self.angle) * noseDistance; self.enemyProjectileSpawnPoint.y = self.y + Math.sin(self.angle) * noseDistance; } self.fireTimer--; if (self.fireTimer <= 0) { if (typeof EnemyProjectile !== 'undefined' && typeof enemyProjectiles !== 'undefined' && game && typeof game.addChild === 'function') { var newProjectile = new EnemyProjectile(self.angle); newProjectile.x = self.enemyProjectileSpawnPoint.x; newProjectile.y = self.enemyProjectileSpawnPoint.y; enemyProjectiles.push(newProjectile); game.addChild(newProjectile); self.fireTimer = self.fireCooldown; } } var gameWidth = 2048; var gameHeight = 2732; var marginWidth = self.shipSprite && self.shipSprite.width ? self.shipSprite.width / 2 + 50 : 100; var marginHeight = self.shipSprite && self.shipSprite.height ? self.shipSprite.height / 2 + 50 : 100; if (self.x < -marginWidth || self.x > gameWidth + marginWidth || self.y < -marginHeight || self.y > gameHeight + marginHeight) { self.isOffScreen = true; } }; // Use a magenta box for armored enemy self.shipSprite = self.attachAsset('Armoredenyme', { anchorX: 0.5, anchorY: 0.5 }); self.angle = Math.PI / 2; if (self.shipSprite) self.shipSprite.rotation = self.angle + Math.PI / 2; self.speed = 2.5; // Slower than normal enemy self.isOffScreen = false; self.lastX = self.x; self.lastY = self.y; self.health = 3; // Armored enemy starts with 3 HP self.isDestroyed = false; return self; }); var BossCannon = Container.expand(function (parentBoss) { var self = Container.call(this); self.parentBoss = parentBoss; self.health = 15; // Cannons are a bit tough self.isDestroyed = false; self.fireCooldown = 90; // Fires every 1.5 seconds self.fireTimer = Math.random() * self.fireCooldown; self.cannonSprite = self.attachAsset('Bossgun', { anchorX: 0.5, anchorY: 0.5 }); self.hit = function (damage) { if (self.isDestroyed) return; self.health -= damage; LK.effects.flashObject(self.cannonSprite, 0xff8888, 100); if (self.health <= 0) { self.isDestroyed = true; self.visible = false; // Play a smaller explosion for cannon destruction var explosionX = self.parentBoss.x + self.x; var explosionY = self.parentBoss.y + self.y; var boomImg = LK.getAsset('Boom3', { anchorX: 0.5, anchorY: 0.5, x: explosionX, y: explosionY }); game.addChild(boomImg); LK.setTimeout(function () { if (boomImg && typeof boomImg.destroy === 'function') boomImg.destroy(); }, 500); LK.getSound('Boom').play(); } }; self.update = function () { if (self.isDestroyed || typeof freezeEnemies !== 'undefined' && freezeEnemies) return; // Aim at player if (playerShip && !playerShip.isDestroyed) { var globalCannonX = self.parentBoss.x + self.x; var globalCannonY = self.parentBoss.y + self.y; var dx = playerShip.x - globalCannonX; var dy = playerShip.y - globalCannonY; var angleToPlayer = Math.atan2(dy, dx); self.cannonSprite.rotation = angleToPlayer + Math.PI / 2; // Assuming sprite points "up" self.fireTimer--; if (self.fireTimer <= 0) { var noseOffset = self.cannonSprite.height / 2; var projSpawnX = globalCannonX + Math.cos(angleToPlayer) * noseOffset; var projSpawnY = globalCannonY + Math.sin(angleToPlayer) * noseOffset; var newProjectile = new EnemyProjectile(angleToPlayer); newProjectile.x = projSpawnX; newProjectile.y = projSpawnY; enemyProjectiles.push(newProjectile); game.addChild(newProjectile); self.fireTimer = self.fireCooldown; } } }; return self; }); var BossEnemy = Container.expand(function () { var self = Container.call(this); self.isDestroyed = false; self.mainBody = new BossMainBody(self); self.addChild(self.mainBody); self.mainBody.x = 0; self.mainBody.y = 0; self.leftCannon = new BossCannon(self); self.addChild(self.leftCannon); self.rightCannon = new BossCannon(self); self.addChild(self.rightCannon); // Position cannons relative to the main body // Ensure sprites are attached to get dimensions var mainBodyWidth = self.mainBody.bodySprite.width; var cannonWidth = self.leftCannon.cannonSprite.width; // Both cannons use same asset self.leftCannon.x = -(mainBodyWidth / 2) - cannonWidth / 2 - 20; // 20px spacing self.leftCannon.y = 0; self.rightCannon.x = mainBodyWidth / 2 + cannonWidth / 2 + 20; // 20px spacing self.rightCannon.y = 0; self.update = function () { if (self.isDestroyed || typeof freezeEnemies !== 'undefined' && freezeEnemies) return; if (self.mainBody && !self.mainBody.isDestroyed) { self.mainBody.update(); } if (self.leftCannon && !self.leftCannon.isDestroyed) { self.leftCannon.update(); } if (self.rightCannon && !self.rightCannon.isDestroyed) { self.rightCannon.update(); } }; return self; }); var BossMainBody = Container.expand(function (parentBoss) { var self = Container.call(this); self.parentBoss = parentBoss; self.health = 50; // Main body is very tough self.isDestroyed = false; self.bodySprite = self.attachAsset('BossBodyAsset', { anchorX: 0.5, anchorY: 0.5 }); self.hit = function (damage) { if (self.isDestroyed) return; self.health -= damage; LK.effects.flashObject(self.bodySprite, 0xff6666, 150); if (self.health <= 0) { self.isDestroyed = true; self.visible = false; self.parentBoss.isDestroyed = true; // Main body destroyed means boss is defeated // Trigger larger explosion sequence for boss defeat in BossEnemy or game.update } }; self.update = function () { if (self.isDestroyed || typeof freezeEnemies !== 'undefined' && freezeEnemies) return; // Potential movement or special attacks later }; return self; }); var EnemyProjectile = Container.expand(function (fireAngle) { var self = Container.call(this); self.bulletSprite = self.attachAsset('enemyBulletSprite', { anchorX: 0.5, anchorY: 0.5 }); self.angle = fireAngle; // Angle of movement self.speed = 10; // Enemy projectiles are a bit slower if (self.bulletSprite) { // Assuming the bullet shape is wider than tall, rotating by angle aligns its length with movement. self.bulletSprite.rotation = self.angle; } self.isOffScreen = false; self.update = function () { if (self.isOffScreen) return; if (typeof freezeEnemies !== 'undefined' && freezeEnemies) return; // Freeze enemy projectiles during card choice self.x += Math.cos(self.angle) * self.speed; self.y += Math.sin(self.angle) * self.speed; var gameWidth = 2048; var gameHeight = 2732; // Margin based on projectile size to ensure it's fully off-screen var margin = 50; // Default margin if (self.bulletSprite && (self.bulletSprite.width || self.bulletSprite.height)) { margin = Math.max(self.bulletSprite.width || 0, self.bulletSprite.height || 0) / 2 + 50; } if (self.x < -margin || self.x > gameWidth + margin || self.y < -margin || self.y > gameHeight + margin) { self.isOffScreen = true; } }; return self; }); // Orange rectangular bullet var EnemyShip = Container.expand(function () { var self = Container.call(this); self.fireCooldown = 180; // Fire every 3 seconds (180 ticks at 60FPS) self.fireTimer = Math.random() * self.fireCooldown; // Stagger initial firing self.enemyProjectileSpawnPoint = { x: 0, y: 0 }; self.hit = function () { if (self.isDestroyed) return; // Already destroyed self.health--; if (self.health <= 0) { self.isDestroyed = true; // Optionally, trigger a small visual effect here like a flash // LK.effects.flashObject(self, 0xffffff, 100); } else { // Optionally, visual effect for taking damage but not destroyed // LK.effects.flashObject(self, 0xffaaaa, 50); } }; self.update = function () { if (self.isOffScreen || self.isDestroyed) return; // Don't update if off-screen or destroyed if (typeof freezeEnemies !== 'undefined' && freezeEnemies) return; // Freeze all enemy movement and firing during card choice // Target the player ship if (typeof playerShip !== 'undefined' && playerShip && !playerShip.isDestroyed) { var dx = playerShip.x - self.x; var dy = playerShip.y - self.y; // Calculate angle towards player var targetAngle = Math.atan2(dy, dx); // Update the enemy's angle to face the player self.angle = targetAngle; // Update sprite rotation to match the new angle // Assuming the ship sprite is designed "pointing up" (nose along its local -Y axis or top) // visual rotation = world angle + PI/2. if (self.shipSprite) { self.shipSprite.rotation = self.angle + Math.PI / 2; } } // If playerShip is not available or is destroyed, the enemy will continue in its current self.angle. self.lastX = self.x; self.lastY = self.y; // Prevent enemy from moving too close to the player var safeDistance = 120; // Minimum allowed distance between enemy and player var moveX = Math.cos(self.angle) * self.speed; var moveY = Math.sin(self.angle) * self.speed; if (typeof playerShip !== 'undefined' && playerShip && !playerShip.isDestroyed) { var dxToPlayer = self.x + moveX - playerShip.x; var dyToPlayer = self.y + moveY - playerShip.y; var distToPlayer = Math.sqrt(dxToPlayer * dxToPlayer + dyToPlayer * dyToPlayer); if (distToPlayer < safeDistance) { // If moving would bring us too close, do not move this frame moveX = 0; moveY = 0; } } self.x += moveX; self.y += moveY; // Update enemy projectile spawn point var shipAsset = self.shipSprite; if (shipAsset) { var noseDistance = shipAsset.height / 2; // Assuming front is along height axis from center self.enemyProjectileSpawnPoint.x = self.x + Math.cos(self.angle) * noseDistance; self.enemyProjectileSpawnPoint.y = self.y + Math.sin(self.angle) * noseDistance; } // Firing logic self.fireTimer--; if (self.fireTimer <= 0) { if (typeof EnemyProjectile !== 'undefined' && typeof enemyProjectiles !== 'undefined' && game && typeof game.addChild === 'function') { var newProjectile = new EnemyProjectile(self.angle); // Fire in the direction the ship is moving newProjectile.x = self.enemyProjectileSpawnPoint.x; newProjectile.y = self.enemyProjectileSpawnPoint.y; enemyProjectiles.push(newProjectile); game.addChild(newProjectile); self.fireTimer = self.fireCooldown; // Reset cooldown } } // Generalized off-screen check var gameWidth = 2048; var gameHeight = 2732; var marginWidth = self.shipSprite && self.shipSprite.width ? self.shipSprite.width / 2 + 50 : 100; var marginHeight = self.shipSprite && self.shipSprite.height ? self.shipSprite.height / 2 + 50 : 100; if (self.x < -marginWidth || self.x > gameWidth + marginWidth || self.y < -marginHeight || self.y > gameHeight + marginHeight) { self.isOffScreen = true; } }; self.shipSprite = self.attachAsset('enemyShipSprite', { anchorX: 0.5, anchorY: 0.5 }); self.angle = Math.PI / 2; // Default angle: moving downwards (positive Y direction) if (self.shipSprite) { self.shipSprite.rotation = self.angle + Math.PI / 2; } self.speed = 4; self.isOffScreen = false; self.lastX = self.x; self.lastY = self.y; self.health = 1; self.isDestroyed = false; return self; }); var FireButton = Container.expand(function () { var self = Container.call(this); self.buttonSprite = self.attachAsset('fireButtonSprite', { anchorX: 0.5, anchorY: 0.5 }); self.lastFireTick = -1000; // Track last tick when fired self.down = function (x, y, obj) { // Prevent holding down for auto-fire: only allow firing if enough time has passed since last fire if (typeof LK !== 'undefined' && typeof LK.ticks !== 'undefined') { if (typeof self.lastFireTick === 'undefined') self.lastFireTick = -1000; // Only allow firing if at least fireButtonCooldown ticks have passed since last fire if (typeof fireButtonCooldown === 'undefined') fireButtonCooldown = 30; if (LK.ticks - self.lastFireTick < fireButtonCooldown) return; } // Only fire if player has ammo and not holding down for auto-fire if (typeof playerAmmo !== 'undefined' && playerAmmo > 0) { if (playerShip && typeof playerShip.currentAngle !== 'undefined' && typeof projectileSpawnPoint !== 'undefined' && playerProjectiles && PlayerProjectile) { if (typeof playerSplitShotActive !== 'undefined' && playerSplitShotActive) { var spreadAngle = Math.PI / 10; // 18 degrees spread for each projectile from center // Projectile 1 (left/upward component of spread) var proj1 = new PlayerProjectile(playerShip.currentAngle - spreadAngle); proj1.x = projectileSpawnPoint.x; proj1.y = projectileSpawnPoint.y; playerProjectiles.push(proj1); game.addChild(proj1); // Projectile 2 (right/downward component of spread) var proj2 = new PlayerProjectile(playerShip.currentAngle + spreadAngle); proj2.x = projectileSpawnPoint.x; proj2.y = projectileSpawnPoint.y; playerProjectiles.push(proj2); game.addChild(proj2); } else { // Single projectile (default behavior) var newProjectile = new PlayerProjectile(playerShip.currentAngle); newProjectile.x = projectileSpawnPoint.x; newProjectile.y = projectileSpawnPoint.y; playerProjectiles.push(newProjectile); game.addChild(newProjectile); } playerAmmo = Math.max(0, playerAmmo - 1); // Decrease ammo by 1, never below 0 // Removed ammo counter update as per requirements if (typeof LK !== 'undefined' && typeof LK.ticks !== 'undefined') { self.lastFireTick = LK.ticks; } // Play shot sound when firing if (typeof LK !== 'undefined' && typeof LK.getSound === 'function') { var shotSound = LK.getSound('Shot'); if (shotSound && typeof shotSound.play === 'function') { shotSound.play(); } } } } }; return self; }); var HealthRestore = Container.expand(function () { var self = Container.call(this); self.restoreSprite = self.attachAsset('healthRestoreSprite', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // Check for collision with player ship if (playerShip && !playerShip.isDestroyed && self.intersects(playerShip)) { if (playerShip.health < 3) { playerShip.health = Math.min(playerShip.health + 1, 3); // Restore health, max 3 if (playerShip.shipSprite) { LK.effects.flashObject(playerShip.shipSprite, 0x00ff00, 150); // Flash green on health restore } // Update hearts display for (var i = 0; i < hearts.length; i++) { hearts[i].visible = i < playerShip.health; } } self.destroy(); } }; return self; }); var Heart = Container.expand(function () { var self = Container.call(this); self.heartSprite = self.attachAsset('heartSprite', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); return self; }); // PlayerShip class to handle player ship logic and projectile spawn point calculation var HomingMissile = Container.expand(function (initialAngle) { var self = Container.call(this); self.missileSprite = self.attachAsset('HomingMissileSprite', { anchorX: 0.5, anchorY: 0.5 }); self.angle = initialAngle; self.speed = 7; self.turnRate = 0.05; // Radians per tick self.isOffScreen = false; self.lastX = self.x; self.lastY = self.y; if (self.missileSprite) { // Ensure sprite exists before setting rotation self.missileSprite.rotation = self.angle; // Assuming sprite points right (0 rad) initially } self.update = function () { if (self.isOffScreen || typeof freezeEnemies !== 'undefined' && freezeEnemies) return; if (playerShip && !playerShip.isDestroyed) { var targetAngle = Math.atan2(playerShip.y - self.y, playerShip.x - self.x); var angleDiff = targetAngle - self.angle; while (angleDiff > Math.PI) angleDiff -= 2 * Math.PI; while (angleDiff < -Math.PI) angleDiff += 2 * Math.PI; var turnAmount = Math.sign(angleDiff) * Math.min(self.turnRate, Math.abs(angleDiff)); self.angle += turnAmount; } if (self.missileSprite) self.missileSprite.rotation = self.angle; self.lastX = self.x; self.lastY = self.y; self.x += Math.cos(self.angle) * self.speed; self.y += Math.sin(self.angle) * self.speed; var gameWidth = 2048; var gameHeight = 2732; var margin = (self.missileSprite && self.missileSprite.width ? self.missileSprite.width / 2 : 25) + 50; if (self.x < -margin || self.x > gameWidth + margin || self.y < -margin || self.y > gameHeight + margin) { self.isOffScreen = true; } }; return self; }); // Red circular fire button var Joystick = Container.expand(function () { var self = Container.call(this); self.baseSprite = self.attachAsset('joystickBaseSprite', { anchorX: 0.5, anchorY: 0.5 }); self.knobSprite = self.attachAsset('joystickKnobSprite', { anchorX: 0.5, anchorY: 0.5 }); // Maximum distance the knob's center can move from the base's center self.radius = self.baseSprite.width / 2 - self.knobSprite.width / 2; if (self.radius <= 0) { // Ensure a sensible minimum radius self.radius = self.baseSprite.width > 0 ? self.baseSprite.width / 4 : 50; // Use 1/4 of base width or 50 if base has no width } self.isDragging = false; self.inputVector = { x: 0, y: 0 }; // Normalized output (-1 to 1) self.handleDown = function (localX, localY) { // localX, localY are relative to the joystick's center (its origin) // Check if the touch is within the larger base area to start dragging var distSqFromCenter = localX * localX + localY * localY; if (distSqFromCenter <= self.baseSprite.width / 2 * (self.baseSprite.width / 2)) { self.isDragging = true; self.handleMove(localX, localY); // Snap knob to initial touch position return true; // Indicates joystick took control } return false; // Joystick not activated }; self.handleMove = function (localX, localY) { if (!self.isDragging) return; var dist = Math.sqrt(localX * localX + localY * localY); if (dist > self.radius) { // Normalize and scale to radius if touch is outside draggable area self.knobSprite.x = localX / dist * self.radius; self.knobSprite.y = localY / dist * self.radius; } else { self.knobSprite.x = localX; self.knobSprite.y = localY; } // Calculate normalized input vector if (self.radius > 0) { self.inputVector.x = self.knobSprite.x / self.radius; self.inputVector.y = self.knobSprite.y / self.radius; } else { // Avoid division by zero if radius is zero self.inputVector.x = 0; self.inputVector.y = 0; } }; self.handleUp = function () { if (self.isDragging) { self.isDragging = false; // Reset knob to center and clear input vector self.knobSprite.x = 0; self.knobSprite.y = 0; self.inputVector.x = 0; self.inputVector.y = 0; } }; self.getInput = function () { return self.inputVector; }; self.isActive = function () { return self.isDragging; }; return self; }); // Projectile class for player's bullets var PlayerProjectile = Container.expand(function (fireAngle) { var self = Container.call(this); // Attach bullet sprite self.bulletSprite = self.attachAsset('playerBulletSprite', { anchorX: 0.5, anchorY: 0.5 }); self.angle = fireAngle; // Store the firing angle self.speed = 20; // Projectile speed magnitude // The playerBulletSprite has orientation:1, meaning it's rotated 90deg clockwise. // If original was thin vertical, it's now thin horizontal, "pointing" along its X-axis. // So, self.angle directly applies. self.bulletSprite.rotation = self.angle; self.isOffScreen = false; // Flag to indicate if projectile is off-screen // Update method to move projectile self.update = function () { if (self.isOffScreen) return; // Don't update if already marked off-screen self.x += Math.cos(self.angle) * self.speed; self.y += Math.sin(self.angle) * self.speed; // Check if off-screen var gameWidth = 2048; var gameHeight = 2732; // Margin based on projectile size to ensure it's fully off-screen var margin = Math.max(self.bulletSprite.width, self.bulletSprite.height) / 2 + 50; if (self.x < -margin || self.x > gameWidth + margin || self.y < -margin || self.y > gameHeight + margin) { self.isOffScreen = true; } }; //{M} // Note: Original L was self.destroy related, removed. return self; }); var PlayerShip = Container.expand(function () { var self = Container.call(this); self.health = 3; // Player can take a few hits self._fractionalHealth = self.health; // For fractional damage (e.g., 0.5 from armored enemy) self.isDestroyed = false; self.hit = function () { if (self.isDestroyed) return; if (typeof self._fractionalHealth === 'undefined') self._fractionalHealth = self.health; self._fractionalHealth -= 1; self.health = Math.floor(self._fractionalHealth); LK.effects.flashObject(self.shipSprite || self, 0xff0000, 150); // Flash red on hit (flash sprite if available) // Update hearts display for (var i = 0; i < hearts.length; i++) { hearts[i].visible = i < self.health; } if (self._fractionalHealth <= 0) { self.isDestroyed = true; // Game over will be triggered in game.update } }; // Attach player ship sprite and set reference for spawn point calculation self.shipSprite = self.attachAsset('playerShipSprite', { anchorX: 0.5, anchorY: 0.5 }); self.moveSpeed = 10; // Pixels per tick for movement speed // currentAngle is the direction the ship moves and fires projectiles. // 0 radians = right, -PI/2 = up (screen coordinates). self.currentAngle = -Math.PI / 2; // Initial angle: pointing up. // playerShipSprite is assumed to be designed pointing "up" (its nose along its local -Y axis). // To make the sprite's nose align with currentAngle, its visual rotation needs adjustment. // Visual rotation = currentAngle + Math.PI / 2. // E.g., currentAngle = -PI/2 (up) => visual rotation = 0. // E.g., currentAngle = 0 (right) => visual rotation = PI/2. self.shipSprite.rotation = self.currentAngle + Math.PI / 2; // Initialize projectile spawn point (remains important) // This will be updated relative to the ship's current position in self.update() // Initial dummy values, will be set correctly on first update. projectileSpawnPoint.x = 0; projectileSpawnPoint.y = 0; self.applyJoystickInput = function (inputX, inputY) { // Update angle only if joystick provides directional input if (inputX !== 0 || inputY !== 0) { self.currentAngle = Math.atan2(inputY, inputX); self.shipSprite.rotation = self.currentAngle + Math.PI / 2; } self.x += inputX * self.moveSpeed; self.y += inputY * self.moveSpeed; // Boundary checks to keep ship on screen var halfWidth = self.shipSprite.width / 2; var halfHeight = self.shipSprite.height / 2; var gameWidth = 2048; var gameHeight = 2732; var topSafeMargin = 100; // Top 100px area is reserved if (self.x - halfWidth < 0) self.x = halfWidth; if (self.x + halfWidth > gameWidth) self.x = gameWidth - halfWidth; if (self.y - halfHeight < topSafeMargin) self.y = topSafeMargin + halfHeight; if (self.y + halfHeight > gameHeight) self.y = gameHeight - halfHeight; }; // Update projectile spawn point every frame based on player ship position and rotation self.update = function () { var shipAsset = self.shipSprite; if (!shipAsset) return; // Calculate spawn point at the tip of the ship, considering its currentAngle. // The "nose" is shipAsset.height / 2 distance from the center. var noseDistance = shipAsset.height / 2; projectileSpawnPoint.x = self.x + Math.cos(self.currentAngle) * noseDistance; projectileSpawnPoint.y = self.y + Math.sin(self.currentAngle) * noseDistance; }; return self; }); var PlayerTrail = Container.expand(function () { var self = Container.call(this); self.trailSprite = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, tint: 0xff3333 }); self.lifeTime = 60; // Trail segment lasts 1 second at 60fps self.currentLife = self.lifeTime; self.update = function () { self.currentLife--; if (self.currentLife <= 0) { self.isExpired = true; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ //Minimalistic tween library which should be used for animations over time, including tinting / colouring an object, scaling, rotating, or changing any game object property. //Only include the plugins you need to create the game. // Placeholder ID // Placeholder ID // Placeholder ID // Placeholder ID // Placeholder ID // Projectile spawn point (relative to player ship center) // Will be updated in PlayerShip class update var projectileSpawnPoint = { x: 0, y: 0 }; var joystick; // Declare joystick instance // Array to keep track of all player projectiles var playerProjectiles = []; // Array to keep track of player trail segments var playerTrailSegments = []; var trailSpawnTimer = 0; // Array to keep track of enemy trail segments var enemyTrailSegments = []; var enemyTrailSpawnTimer = 0; // Player projectile damage (can be doubled by Cardsttsck) var playerProjectileDamage = 1; // Player ammo count (start with 10). Player cannot fire forever: must wait for ammo to restore and cannot hold fire for auto-fire. var playerAmmo = 10; // Create and add the game background // You can change the background by updating the ID in the Assets section for 'game_background_image' var gameBackground = LK.getAsset('game_background_image', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(gameBackground); // Add colorful stars to the background var starColors = [0xFFFFFF, 0xFF3333, 0x33FF33, 0x3333FF, 0xFFFF33, 0xFF33FF, 0x33FFFF, 0xFFAA33, 0xAA33FF, 0x33AAFF]; for (var i = 0; i < 50; i++) { var star = LK.getAsset('Star', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: Math.random() * 2732, tint: starColors[Math.floor(Math.random() * starColors.length)] }); // 30% chance for a star to have disappearing/reappearing effect instead of twinkling if (Math.random() < 0.3) { // Create disappearing and reappearing effect for some stars var disappearDelay = 2000 + Math.random() * 8000; // Random delay 2-10 seconds before first disappear LK.setTimeout(function () { function startDisappearCycle() { // Disappear quickly tween(star, { alpha: 0 }, { duration: 200 + Math.random() * 300, easing: tween.easeInOut, onFinish: function onFinish() { // Stay invisible for a random time var invisibleTime = 500 + Math.random() * 2000; // 0.5-2.5 seconds invisible LK.setTimeout(function () { // Reappear tween(star, { alpha: 1.0 }, { duration: 300 + Math.random() * 500, easing: tween.easeInOut, onFinish: function onFinish() { // Wait before next disappear cycle var nextCycleDelay = 3000 + Math.random() * 7000; // 3-10 seconds before next cycle LK.setTimeout(function () { startDisappearCycle(); // Start the cycle again }, nextCycleDelay); } }); }, invisibleTime); } }); } startDisappearCycle(); }, disappearDelay); } else { // Add normal twinkling animation to other stars tween(star, { alpha: 0.3 }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(star, { alpha: 1.0 }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Create infinite loop by calling the twinkling again tween(star, { alpha: 0.3 }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut }); } }); } }); } game.addChild(star); } // Adding it here as one of the first children to 'game' ensures it's in the background. var maxPlayerAmmo = 10; // Maximum player ammo capacity // Fire button cooldown in ticks (default 30, can be halved by Card1) var fireButtonCooldown = 30; // Array to keep track of all enemy ships var enemyShips = []; // Array to keep track of all enemy projectiles var enemyProjectiles = []; // Global flag: freeze all enemy movement and firing during card choice var freezeEnemies = false; var playerSplitShotActive = false; // Flag for Asplitmind card: player fires two projectiles // Create player ship and add to game var playerShip = new PlayerShip(); // Apply saved skin selection if (selectedSkin && selectedSkin !== 'playerShipSprite') { var oldRotation = playerShip.shipSprite.rotation; playerShip.shipSprite.destroy(); playerShip.shipSprite = playerShip.attachAsset(selectedSkin, { anchorX: 0.5, anchorY: 0.5 }); playerShip.shipSprite.rotation = oldRotation; } game.addChild(playerShip); // Display hearts for player health var hearts = []; for (var i = 0; i < playerShip.health; i++) { var heart = new Heart(); heart.x = i * 120 + 120; // Position hearts with increased spacing heart.y = 60; // Top-left corner LK.gui.topLeft.addChild(heart); hearts.push(heart); } // Load saved money from storage var money = storage.money || 0; var moneyTxt = new Text2(money + " $", { size: 120, fill: 0xFFE066, font: "monospace, 'Press Start 2P', 'VT323', 'Courier New', Courier, monospace", // pixel/retro style align: "right" }); moneyTxt.anchor.set(1, 0); // Right-top anchor moneyTxt.x = -60; // Padding from right edge moneyTxt.y = 200; // Move money text down to make room for settings LK.gui.topRight.addChild(moneyTxt); // Settings icon in top-right corner var settingsIcon = new Text2("⚙", { size: 80, fill: 0xFFFFFF, align: "right" }); settingsIcon.anchor.set(1, 0); // Right-top anchor settingsIcon.x = -60; // Padding from right edge settingsIcon.y = 60; // Top padding settingsIcon.interactive = true; settingsIcon.down = function (x, y, obj) { // Open ship skin selection menu if (game._skinMenuActive) return; // Prevent multiple menus openSkinSelectionMenu(); }; // Ship skin selection menu functionality var selectedSkin = storage.selectedSkin || 'shipSkin1'; // Initialize owned skins if not exists (default and heavy are free) if (!storage.ownedSkins) { storage.ownedSkins = ['shipSkin1', 'shipSkin3']; } // Initialize trail setting if not exists (enabled by default) var trailEnabled = storage.trailEnabled !== undefined ? storage.trailEnabled : true; // Initialize trail color if not exists (default to red) var trailColor = storage.trailColor || 0xff3333; // Ensure selected skin is valid (not Fighter skin) if (selectedSkin === 'shipSkin2') { selectedSkin = 'shipSkin1'; storage.selectedSkin = selectedSkin; } function openSkinSelectionMenu() { game._skinMenuActive = true; // Freeze all enemy movement and firing like card selection freezeEnemies = true; // Create overlay for skin selection var skinOverlay = new Container(); skinOverlay.name = "skinSelectionOverlay"; // Semi-transparent background var skinBg = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 20, scaleY: 20, x: 2048 / 2, y: 2732 / 2, tint: 0x000000, alpha: 0.8 }); skinOverlay.addChild(skinBg); // Title text var titleText = new Text2("SELECT SHIP SKIN", { size: 100, fill: 0xFFFFFF, align: "center" }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 2 - 400; skinOverlay.addChild(titleText); // Available ship skins var availableSkins = [{ id: 'shipSkin1', name: 'Default', cost: 0 }, { id: 'shipSkin3', name: 'Heavy', cost: 0 }, { id: 'skinfire', name: 'Ghost', cost: 5 }, { id: 'skinnlood', name: 'Blood', cost: 8 }, { id: 'Skinrocet', name: 'Rocket', cost: 12 }]; // Position skins horizontally var skinPositions = [{ x: 2048 / 2 - 540, y: 2732 / 2 }, { x: 2048 / 2 - 270, y: 2732 / 2 }, { x: 2048 / 2, y: 2732 / 2 }, { x: 2048 / 2 + 270, y: 2732 / 2 }, { x: 2048 / 2 + 540, y: 2732 / 2 }]; for (var skinIdx = 0; skinIdx < availableSkins.length; skinIdx++) { var skinData = availableSkins[skinIdx]; var skinContainer = new Container(); // Ship preview var shipPreview = LK.getAsset(skinData.id, { anchorX: 0.5, anchorY: 0.5, x: skinPositions[skinIdx].x, y: skinPositions[skinIdx].y, scaleX: 1.5, scaleY: 1.5 }); skinContainer.addChild(shipPreview); // Selection border for currently selected skin if (skinData.id === selectedSkin) { var selectionBorder = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: skinPositions[skinIdx].x, y: skinPositions[skinIdx].y, scaleX: 3, scaleY: 3, tint: 0x00FF00, alpha: 0.3 }); skinContainer.addChild(selectionBorder); } // Skin name text var skinNameText = new Text2(skinData.name, { size: 60, fill: 0xFFFFFF, align: "center" }); skinNameText.anchor.set(0.5, 0.5); skinNameText.x = skinPositions[skinIdx].x; skinNameText.y = skinPositions[skinIdx].y + 200; skinContainer.addChild(skinNameText); // Cost text - only show if skin is not owned var ownedSkins = storage.ownedSkins || ['shipSkin1', 'shipSkin3']; var isOwned = ownedSkins.indexOf(skinData.id) !== -1 || skinData.cost === 0; if (!isOwned) { var costText = new Text2(skinData.cost > 0 ? skinData.cost + " $" : "FREE", { size: 40, fill: skinData.cost > 0 ? 0xFFE066 : 0x00FF00, align: "center" }); costText.anchor.set(0.5, 0.5); costText.x = skinPositions[skinIdx].x; costText.y = skinPositions[skinIdx].y + 270; skinContainer.addChild(costText); } // If player doesn't have enough money, make skin appear unavailable if (skinData.cost > money) { shipPreview.tint = 0x666666; skinNameText.tint = 0x666666; costText.fill = 0xFF4444; } skinContainer.interactive = true; // Use closure to capture skin data (function (currentSkinData) { skinContainer.down = function (x_down, y_down, obj_down) { if (!game._skinMenuActive) return; // Check if skin costs money and player has enough if (currentSkinData.cost > 0 && money < currentSkinData.cost) { // Flash red to indicate insufficient funds LK.effects.flashScreen(0xFF0000, 300); return; } // Check if skin is already owned (either free or already purchased) var ownedSkins = storage.ownedSkins || ['shipSkin1', 'shipSkin3']; // Default and Heavy are free if (currentSkinData.cost > 0 && ownedSkins.indexOf(currentSkinData.id) === -1) { // Purchase the skin money -= currentSkinData.cost; storage.money = money; // Save money to storage moneyTxt.setText(money + " $"); ownedSkins.push(currentSkinData.id); storage.ownedSkins = ownedSkins; } // Update selected skin selectedSkin = currentSkinData.id; storage.selectedSkin = selectedSkin; // Update player ship sprite if (playerShip && playerShip.shipSprite) { var oldRotation = playerShip.shipSprite.rotation; playerShip.shipSprite.destroy(); playerShip.shipSprite = playerShip.attachAsset(selectedSkin, { anchorX: 0.5, anchorY: 0.5 }); playerShip.shipSprite.rotation = oldRotation; } closeSkinSelectionMenu(skinOverlay); }; })(skinData); skinOverlay.addChild(skinContainer); } // Trail toggle button var trailToggleText = trailEnabled ? "TRAIL: ON" : "TRAIL: OFF"; var trailToggleButton = new Text2(trailToggleText, { size: 80, fill: trailEnabled ? 0x00FF00 : 0xFF4444, align: "center" }); trailToggleButton.anchor.set(0.5, 0.5); trailToggleButton.x = 2048 / 2 - 300; trailToggleButton.y = 2732 / 2 + 400; trailToggleButton.interactive = true; trailToggleButton.down = function () { if (!game._skinMenuActive) return; // Toggle trail setting trailEnabled = !trailEnabled; storage.trailEnabled = trailEnabled; // Update button text and color trailToggleButton.setText(trailEnabled ? "TRAIL: ON" : "TRAIL: OFF"); trailToggleButton.fill = trailEnabled ? 0x00FF00 : 0xFF4444; }; skinOverlay.addChild(trailToggleButton); // Trail color selection section var trailColorTitle = new Text2("TRAIL COLOR", { size: 60, fill: 0xFFFFFF, align: "center" }); trailColorTitle.anchor.set(0.5, 0.5); trailColorTitle.x = 2048 / 2 + 300; trailColorTitle.y = 2732 / 2 + 350; skinOverlay.addChild(trailColorTitle); // Available trail colors var trailColors = [{ name: "RED", color: 0xff3333 }, { name: "BLUE", color: 0x3333ff }, { name: "GREEN", color: 0x33ff33 }, { name: "YELLOW", color: 0xffff33 }, { name: "PURPLE", color: 0xff33ff }, { name: "ORANGE", color: 0xff6600 }]; // Get current trail color from storage or default to red var currentTrailColor = storage.trailColor || 0xff3333; // Display trail color options in a 3x2 grid for (var colorIdx = 0; colorIdx < trailColors.length; colorIdx++) { var colorData = trailColors[colorIdx]; var colorContainer = new Container(); // Calculate position in 3x2 grid var col = colorIdx % 3; var row = Math.floor(colorIdx / 3); var colorX = 2048 / 2 + 150 + col * 100; var colorY = 2732 / 2 + 420 + row * 80; // Color preview circle var colorPreview = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: colorX, y: colorY, scaleX: 0.8, scaleY: 0.8, tint: colorData.color }); colorContainer.addChild(colorPreview); // Selection border for currently selected color if (colorData.color === currentTrailColor) { var colorBorder = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: colorX, y: colorY, scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF, alpha: 0.5 }); colorContainer.addChild(colorBorder); } colorContainer.interactive = true; // Use closure to capture color data (function (currentColorData) { colorContainer.down = function () { if (!game._skinMenuActive) return; // Update trail color currentTrailColor = currentColorData.color; storage.trailColor = currentTrailColor; // Refresh the menu to show new selection if (game._skinMenuActive) { closeSkinSelectionMenu(skinOverlay); openSkinSelectionMenu(); } }; })(colorData); skinOverlay.addChild(colorContainer); } // Close button var closeButton = new Text2("✕", { size: 120, fill: 0xFF4444, align: "center" }); closeButton.anchor.set(0.5, 0.5); closeButton.x = 2048 / 2 + 600; closeButton.y = 2732 / 2 - 400; closeButton.interactive = true; closeButton.down = function () { if (!game._skinMenuActive) return; closeSkinSelectionMenu(skinOverlay); }; skinOverlay.addChild(closeButton); game.addChild(skinOverlay); } function closeSkinSelectionMenu(overlay) { game._skinMenuActive = false; // Resume game like card selection freezeEnemies = false; if (overlay && typeof overlay.destroy === 'function') { overlay.destroy(); } } LK.gui.topRight.addChild(settingsIcon); // Create black hole in top right corner var blackHole = LK.getAsset('blackHole', { anchorX: 0.5, anchorY: 0.5 }); blackHole.x = -120; // Position from right edge blackHole.y = 120; // Position from top edge LK.gui.topRight.addChild(blackHole); // Add swirling animation to black hole tween(blackHole, { rotation: Math.PI * 2 }, { duration: 2000, easing: tween.easeLinear, loop: true }); var enemiesKilled = 0; // Counter for killed enemies, to trigger boss var bossEnemyInstance = null; // Holds the boss instance var bossSpawned = false; // Flag to ensure boss spawns only once per game var homingMissiles = []; // Array for new boss's homing missiles var giantBossEnemyInstance = null; // Instance for the new giant boss var giantBossSpawned = false; // Flag for the new giant boss // Ammo counter removed as per requirements // Center player ship horizontally, place near bottom playerShip.x = 2048 / 2; playerShip.y = 2732 - 350; // Create Joystick joystick = new Joystick(); game.addChild(joystick); // Set initial position (e.g., 0,0), though it will jump to touch. joystick.x = 0; joystick.y = 0; // Enemy spawn multiplier (default 1, can be set to 3 for 5 seconds after Card2) game._enemySpawnMultiplier = 1; game._enemySpawnMultiplierTimeout = null; // Create and add Enemy Ships var enemyShip1 = new EnemyShip(); var enemyShipAssetHeight1 = enemyShip1.shipSprite && enemyShip1.shipSprite.height ? enemyShip1.shipSprite.height : 146; // Default to asset height enemyShip1.x = 2048 / 3; enemyShip1.y = -(enemyShipAssetHeight1 / 2) - 50; // Start off-screen at the top enemyShip1.lastX = enemyShip1.x; // Initialize lastX to the actual starting x enemyShip1.lastY = enemyShip1.y; // Initialize lastY to the actual starting y game.addChild(enemyShip1); enemyShips.push(enemyShip1); var enemyShip2 = new EnemyShip(); var enemyShipAssetHeight2 = enemyShip2.shipSprite && enemyShip2.shipSprite.height ? enemyShip2.shipSprite.height : 146; // Default to asset height enemyShip2.x = 2048 * 2 / 3; enemyShip2.y = -(enemyShipAssetHeight2 / 2) - 150; // Start off-screen, staggered further up enemyShip2.lastX = enemyShip2.x; // Initialize lastX to the actual starting x enemyShip2.lastY = enemyShip2.y; // Initialize lastY to the actual starting y game.addChild(enemyShip2); enemyShips.push(enemyShip2); // Game event handlers // Create and position Fire Button var fireButton = new FireButton(); LK.gui.bottomLeft.addChild(fireButton); // Position the fire button visually in the bottom-left corner with some margin // The buttonSprite is 200x200 and anchored at its center (0.5, 0.5) // fireButton (Container) is added to LK.gui.bottomLeft, positioning its top-left (0,0) at screen bottom-left. // Adjust x and y to make the button appear correctly. var buttonMargin = 50; // 50px margin from screen edges fireButton.x = fireButton.buttonSprite.width / 2 + buttonMargin; fireButton.y = -fireButton.buttonSprite.height / 2 - buttonMargin; // Negative Y moves up from the bottom edge game.down = function (x, y, obj) { if (game._cardChoiceActive || game._skinMenuActive) { // Block joystick and ship movement while card choice or skin menu is active return; } // x, y are global game coordinates // Move the joystick to the touch position joystick.x = x; joystick.y = y; joystick.visible = true; // Activate the joystick. Since the joystick's origin is now at the touch point (x,y), // the local coordinates for the touch within the joystick are (0,0). // This centers the knob under the finger and initiates dragging. joystick.handleDown(0, 0); // Pass (0,0) as local coordinates // Note: With this "floating joystick" implementation, the joystick activates on any screen press. // The previous mechanic, where tapping away from a fixed joystick fired projectiles, // is superseded. Further design would be needed for a separate firing mechanism. }; game.move = function (x, y, obj) { if (game._cardChoiceActive || game._skinMenuActive) { // Block joystick movement while card choice or skin menu is active return; } if (joystick.isActive()) { var joystickLocalPos = joystick.toLocal({ x: x, y: y }); joystick.handleMove(joystickLocalPos.x, joystickLocalPos.y); } }; game.up = function (x, y, obj) { if (game._cardChoiceActive || game._skinMenuActive) { // Block joystick release while card choice or skin menu is active return; } if (joystick.isActive()) { joystick.handleUp(); // Resets knob, inputVector, and isDragging flag joystick.visible = false; // Hide the joystick when the touch is released } }; // Update game state var ammoRestoreCooldown = 0; // Ticks until next ammo restore // Start with space music LK.playMusic('Space'); game.update = function () { // Prevent all game logic and object updates while card choice or skin menu overlay is active if (game._cardChoiceActive || game._skinMenuActive) { // Only allow overlay input, skip all updates and movement return; } // Restore ammo if not full, with a delay (e.g., 30 ticks = 0.5s) if (typeof playerAmmo !== 'undefined') { if (typeof maxPlayerAmmo === 'undefined') maxPlayerAmmo = 10; // Defensive: ensure maxPlayerAmmo is defined if (playerAmmo < maxPlayerAmmo) { // Use maxPlayerAmmo instead of hardcoded 10 if (typeof ammoRestoreCooldown === 'undefined') ammoRestoreCooldown = 0; if (ammoRestoreCooldown > 0) { ammoRestoreCooldown--; } else { playerAmmo += 1; // Removed ammo counter update as per requirements ammoRestoreCooldown = 30; // 0.5s at 60FPS } } else { // Player is at or above max ammo. // Ensure current ammo doesn't exceed new max if it somehow did, though unlikely with current regen. playerAmmo = Math.min(playerAmmo, maxPlayerAmmo); ammoRestoreCooldown = 0; } } // Apply joystick input to player ship movement if (joystick && playerShip && playerShip.applyJoystickInput) { var input = joystick.getInput(); playerShip.applyJoystickInput(input.x, input.y); } // Update player ship (e.g., to recalculate projectileSpawnPoint after moving) if (playerShip && playerShip.update) { playerShip.update(); } // Create trail segments behind player trailSpawnTimer++; if (trailSpawnTimer >= 3 && playerShip && !playerShip.isDestroyed && trailEnabled) { var trailSegment = new PlayerTrail(); trailSegment.x = playerShip.x; trailSegment.y = playerShip.y; // Get current trail color from storage var currentTrailColor = storage.trailColor || 0xff3333; // Add bright pulsing effect to make trail more vibrant with selected color if (trailSegment.trailSprite) { // Calculate a lighter version of the selected color for pulsing effect var lighterColor = currentTrailColor; // Add brightness by increasing RGB values proportionally var r = Math.min(255, (currentTrailColor >> 16 & 0xFF) + 50); var g = Math.min(255, (currentTrailColor >> 8 & 0xFF) + 50); var b = Math.min(255, (currentTrailColor & 0xFF) + 50); lighterColor = r << 16 | g << 8 | b; trailSegment.trailSprite.tint = currentTrailColor; tween(trailSegment.trailSprite, { tint: lighterColor }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(trailSegment.trailSprite, { tint: currentTrailColor }, { duration: 200, easing: tween.easeInOut }); } }); } game.addChild(trailSegment); playerTrailSegments.push(trailSegment); trailSpawnTimer = 0; } // Create trail segments behind enemies enemyTrailSpawnTimer++; if (enemyTrailSpawnTimer >= 4) { // Create trails for all active enemies for (var e = 0; e < enemyShips.length; e++) { var enemy = enemyShips[e]; if (!enemy.isDestroyed && !enemy.isOffScreen) { var enemyTrailSegment = new PlayerTrail(); enemyTrailSegment.x = enemy.x; enemyTrailSegment.y = enemy.y; // Make enemy trails orange/yellow color if (enemyTrailSegment.trailSprite) { enemyTrailSegment.trailSprite.tint = 0xff6600; tween(enemyTrailSegment.trailSprite, { tint: 0xffaa33 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(enemyTrailSegment.trailSprite, { tint: 0xff6600 }, { duration: 200, easing: tween.easeInOut }); } }); } game.addChild(enemyTrailSegment); enemyTrailSegments.push(enemyTrailSegment); } } enemyTrailSpawnTimer = 0; } // Update and clean up trail segments for (var t = playerTrailSegments.length - 1; t >= 0; t--) { var trailSeg = playerTrailSegments[t]; if (trailSeg.update) { trailSeg.update(); } // Calculate alpha based on remaining life (1.0 to 0.0) var alpha = trailSeg.currentLife / trailSeg.lifeTime; if (trailSeg.trailSprite) { trailSeg.trailSprite.alpha = Math.max(0.3, alpha); // Keep minimum visibility of 0.3 } if (trailSeg.isExpired) { trailSeg.destroy(); playerTrailSegments.splice(t, 1); } } // Update and clean up enemy trail segments for (var et = enemyTrailSegments.length - 1; et >= 0; et--) { var enemyTrailSeg = enemyTrailSegments[et]; if (enemyTrailSeg.update) { enemyTrailSeg.update(); } // Calculate alpha based on remaining life (1.0 to 0.0) var enemyAlpha = enemyTrailSeg.currentLife / enemyTrailSeg.lifeTime; if (enemyTrailSeg.trailSprite) { enemyTrailSeg.trailSprite.alpha = Math.max(0.2, enemyAlpha); // Slightly more transparent than player trails } if (enemyTrailSeg.isExpired) { enemyTrailSeg.destroy(); enemyTrailSegments.splice(et, 1); } } // Update and clean up projectiles, and check for collisions with enemies for (var i = playerProjectiles.length - 1; i >= 0; i--) { var proj = playerProjectiles[i]; if (proj.update) { proj.update(); // Call projectile's own update logic (movement) } // Check if the projectile flagged itself as off-screen AFTER moving if (proj.isOffScreen) { proj.destroy(); // Destroy the projectile playerProjectiles.splice(i, 1); // Remove from the tracking array continue; // Move to the next projectile } // Collision detection with enemy ships for (var k = enemyShips.length - 1; k >= 0; k--) { var enemy = enemyShips[k]; // Skip already destroyed or off-screen enemies for collision checks if (enemy.isDestroyed || enemy.isOffScreen) { continue; } if (proj.intersects(enemy)) { // Use playerProjectileDamage if defined, else default to 1 var dmg = typeof playerProjectileDamage !== 'undefined' ? playerProjectileDamage : 1; if (typeof enemy.health === 'number') { enemy.health -= dmg; if (enemy.health <= 0) { enemy.isDestroyed = true; } else { if (typeof enemy.hit === 'function') enemy.hit(); } } else { if (typeof enemy.hit === 'function') enemy.hit(); } proj.destroy(); // Projectile is consumed on hit playerProjectiles.splice(i, 1); // Remove projectile // If enemy.isDestroyed became true, it will be handled in the enemyShips loop below. // Score will be awarded there too. break; // Projectile is gone, no need to check against other enemies } } // If projectile still exists, check collision with boss parts if (!proj.isOffScreen && bossEnemyInstance && !bossEnemyInstance.isDestroyed) { var bossPartsToCheck = []; if (bossEnemyInstance.mainBody && !bossEnemyInstance.mainBody.isDestroyed) { bossPartsToCheck.push(bossEnemyInstance.mainBody); } if (bossEnemyInstance.leftCannon && !bossEnemyInstance.leftCannon.isDestroyed) { bossPartsToCheck.push(bossEnemyInstance.leftCannon); } if (bossEnemyInstance.rightCannon && !bossEnemyInstance.rightCannon.isDestroyed) { bossPartsToCheck.push(bossEnemyInstance.rightCannon); } for (var bp_idx = 0; bp_idx < bossPartsToCheck.length; bp_idx++) { var part = bossPartsToCheck[bp_idx]; if (proj.intersects(part)) { var dmg = typeof playerProjectileDamage !== 'undefined' ? playerProjectileDamage : 1; part.hit(dmg); // Part handles its own health and destruction visuals if (part.isDestroyed) { // Award points/money for destroying a part if (part instanceof BossCannon) { LK.setScore(LK.getScore() + 50); // More points for a cannon money += 25; storage.money = money; // Save money to storage if (typeof moneyTxt !== 'undefined' && moneyTxt.setText) { moneyTxt.setText(money + " $"); } } // Main body destruction score is handled when boss is fully defeated } proj.destroy(); // Projectile is consumed playerProjectiles.splice(i, 1); break; // Projectile is gone, stop checking this projectile against other boss parts } } } // If proj was spliced (either by hitting enemy or boss part), loop continues correctly. } // Update and clean up enemy projectiles, and check for collisions with player for (var l = enemyProjectiles.length - 1; l >= 0; l--) { var eProj = enemyProjectiles[l]; if (eProj.update) { eProj.update(); // Call projectile's own update logic (movement) } // Check if the projectile flagged itself as off-screen AFTER moving if (eProj.isOffScreen) { if (typeof eProj.destroy === 'function') eProj.destroy(); enemyProjectiles.splice(l, 1); // Remove from the tracking array continue; // Move to the next projectile } // Collision detection with player ship // Ensure playerShip exists and is not already destroyed before checking intersection if (playerShip && !playerShip.isDestroyed && typeof eProj.intersects === 'function' && eProj.intersects(playerShip)) { // Check if the projectile's parent is an ArmoredEnemyShip (for 0.5 damage) var isArmored = false; if (typeof enemyShips !== 'undefined') { for (var ai = 0; ai < enemyShips.length; ai++) { var es = enemyShips[ai]; if (es instanceof ArmoredEnemyShip && es.enemyProjectileSpawnPoint && Math.abs(eProj.x - es.enemyProjectileSpawnPoint.x) < 2 && Math.abs(eProj.y - es.enemyProjectileSpawnPoint.y) < 2) { isArmored = true; break; } } } if (isArmored) { // Deal 0.5 damage: use a fractional health system if (typeof playerShip.health === 'number') { if (typeof playerShip._fractionalHealth === 'undefined') playerShip._fractionalHealth = playerShip.health; playerShip._fractionalHealth -= 0.5; if (playerShip._fractionalHealth <= 0) { playerShip.health = 0; playerShip.isDestroyed = true; if (playerShip.shipSprite) playerShip.shipSprite.visible = false;else if (typeof playerShip.visible !== 'undefined') playerShip.visible = false; LK.showGameOver(); } else { playerShip.health = Math.floor(playerShip._fractionalHealth); LK.effects.flashObject(playerShip.shipSprite || playerShip, 0xff0000, 150); // Update hearts display for (var i = 0; i < hearts.length; i++) { hearts[i].visible = i < playerShip.health; } } } } else { if (typeof playerShip.hit === 'function') playerShip.hit(); // Player takes damage if (playerShip.isDestroyed) { if (playerShip.shipSprite) playerShip.shipSprite.visible = false;else if (typeof playerShip.visible !== 'undefined') playerShip.visible = false; LK.showGameOver(); } } if (typeof eProj.destroy === 'function') eProj.destroy(); // Projectile is consumed on hit enemyProjectiles.splice(l, 1); // Remove projectile // If projectile hit player, it's gone. No need to check further for this projectile. } } // Update and clean up enemy ships // This loop should still run even if player is destroyed, to clean up existing enemies, // though new game state might prevent further updates if LK.showGameOver() is effective immediately. var _loop = function _loop() { enemy = enemyShips[j]; // First, check if the enemy ship is marked as destroyed (e.g., by a projectile hit) if (enemy.isDestroyed) { // Capture enemy's current position for the explosion sequence var explosionX = enemy.x; var explosionY = enemy.y; // Show Boom image at the captured position var localBoomImg1 = LK.getAsset('Boom', { // Use a new local variable for the first boom image anchorX: 0.5, anchorY: 0.5, x: explosionX, // Use captured X coordinate y: explosionY // Use captured Y coordinate }); game.addChild(localBoomImg1); // Remove Boom image after a short delay (e.g., 125ms), then show Boom2 at the same position LK.setTimeout(function () { if (localBoomImg1 && typeof localBoomImg1.destroy === 'function') localBoomImg1.destroy(); // Destroy the correct local image // Show Boom2 image at the same captured position var boom2Img = LK.getAsset('Boom2', { // This was already var, so local, good. anchorX: 0.5, anchorY: 0.5, x: explosionX, // Use captured X y: explosionY // Use captured Y }); game.addChild(boom2Img); // Remove Boom2 image after a short delay (e.g., 125ms), then show Boom3 at the same position LK.setTimeout(function () { if (boom2Img && typeof boom2Img.destroy === 'function') boom2Img.destroy(); // Show Boom3 image at the same captured position var boom3Img = LK.getAsset('Boom3', { anchorX: 0.5, anchorY: 0.5, x: explosionX, // Use captured X y: explosionY // Use captured Y }); game.addChild(boom3Img); // Remove Boom3 image after a short delay (e.g., 125ms), then show Boom4 at the same position LK.setTimeout(function () { if (boom3Img && typeof boom3Img.destroy === 'function') boom3Img.destroy(); // Show Boom4 image at the same captured position var boom4Img = LK.getAsset('Boom4', { anchorX: 0.5, anchorY: 0.5, x: explosionX, // Use captured X y: explosionY // Use captured Y }); game.addChild(boom4Img); // Remove Boom4 image after a short delay (e.g., 125ms), then show Boom5 at the same position LK.setTimeout(function () { if (boom4Img && typeof boom4Img.destroy === 'function') boom4Img.destroy(); // Show Boom5 image at the same captured position var boom5Img = LK.getAsset('Boom5', { anchorX: 0.5, anchorY: 0.5, x: explosionX, // Use captured X y: explosionY // Use captured Y }); game.addChild(boom5Img); // Remove Boom5 image after a short delay (e.g., 125ms), then show Boom6 at the same position LK.setTimeout(function () { if (boom5Img && typeof boom5Img.destroy === 'function') boom5Img.destroy(); // Show Boom6 image at the same captured position var boom6Img = LK.getAsset('Boom6', { anchorX: 0.5, anchorY: 0.5, x: explosionX, // Use captured X y: explosionY // Use captured Y }); game.addChild(boom6Img); // Remove Boom6 image after a short delay (e.g., 125ms) LK.setTimeout(function () { if (boom6Img && typeof boom6Img.destroy === 'function') boom6Img.destroy(); }, 125); }, 125); }, 125); }, 125); }, 125); }, 125); enemy.destroy(); // Destroy the enemy ship enemyShips.splice(j, 1); // Remove from the tracking array LK.setScore(LK.getScore() + 10); // Award score for destroying an enemy money += 5; // Add 5 money per enemy destroyed storage.money = money; // Save money to storage if (typeof moneyTxt !== 'undefined' && moneyTxt.setText) { moneyTxt.setText(money + " $"); } LK.getSound('Boom').play(); // Play 'Boom' sound effect // 25% chance to drop a health restoration if (Math.random() < 0.25) { healthRestore = new HealthRestore(); healthRestore.x = enemy.x; healthRestore.y = enemy.y; game.addChild(healthRestore); } // --- Card choice after 3 kills --- enemiesKilled++; // --- Boss Spawn Logic --- // Spawn original boss after 10 kills if (!bossSpawned && bossEnemyInstance === null && enemiesKilled >= 10) { bossEnemyInstance = new BossEnemy(); bossEnemyInstance.x = 2048 / 2; bossEnemyInstance.y = 450; game.addChild(bossEnemyInstance); bossSpawned = true; LK.playMusic('Boss'); } // --- End Boss Spawn Logic --- if (enemiesKilled > 0 && enemiesKilled % 3 === 0 && !game._cardChoiceActive) { // Helper to finish card choice and resume game var finishCardChoice = function finishCardChoice() { if (!game._cardChoiceActive) return; game._cardChoiceActive = false; freezeEnemies = false; if (cardOverlay && typeof cardOverlay.destroy === 'function') cardOverlay.destroy(); // Restore input game.down = oldGameDown; game.move = oldGameMove; game.up = oldGameUp; // Resume game logic // No need to call LK.resumeGame(); LK engine will resume game after overlay is destroyed }; game._cardChoiceActive = true; // Freeze all enemy movement and firing freezeEnemies = true; // --- Block card selection for 2 seconds --- game._cardChoiceBlock = true; LK.setTimeout(function () { game._cardChoiceBlock = false; }, 2000); // Pause game logic is handled by LK.showGameOver() and LK.resumeGame(), no need to call LK.pause() here // Create overlay for card choice cardOverlay = new Container(); cardOverlay.name = "cardChoiceOverlay"; // Semi-transparent background bg = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 20, scaleY: 20, x: 2048 / 2, y: 2732 / 2, tint: 0x000000, alpha: 0.7 }); cardOverlay.addChild(bg); // --- Define all available cards and their effects --- var allAvailableCards = [{ key: "card1_ammo_firerate", // Card1: More Ammo, Faster Fire Rate asset: "Card1", applyEffect: function applyEffect() { if (typeof maxPlayerAmmo === 'undefined') maxPlayerAmmo = 10; // Defensive initialization maxPlayerAmmo += 2; // Increase maximum ammo capacity playerAmmo = Math.min(playerAmmo + 2, maxPlayerAmmo); // Add 2 to current ammo, capped by new maxPlayerAmmo fireButtonCooldown = Math.max(1, Math.floor(fireButtonCooldown / 2)); // Halve cooldown, min 1 } }, { key: "card2_speed_spawn_heal", // Card2: Speed Boost, Enemy Spawn Boost, Heal 1 HP asset: "Csrd2", applyEffect: function applyEffect() { if (playerShip) { if (typeof playerShip._originalMoveSpeed === 'undefined') { playerShip._originalMoveSpeed = playerShip.moveSpeed; } playerShip.moveSpeed = playerShip._originalMoveSpeed * 2; // Speed boost is now permanent, timeout removed. // Heal 1 HP if not at max health var currentMaxHealth = playerShip.maxHealth || 3; if (playerShip.health < currentMaxHealth) { playerShip.health = Math.min(playerShip.health + 1, currentMaxHealth); if (typeof playerShip._fractionalHealth !== 'undefined') { // Ensure fractional health is updated playerShip._fractionalHealth = playerShip.health; } for (var i = 0; i < hearts.length; i++) { hearts[i].visible = i < playerShip.health; } } } // Removed enemy spawn multiplier effect } }, { key: "cardhp_maxhp_heal", // Cardhp: Increase Max HP, Heal 1 HP asset: "Cardhp", applyEffect: function applyEffect() { if (playerShip) { if (typeof playerShip.maxHealth === 'undefined') { playerShip.maxHealth = 3; } playerShip.maxHealth += 1; playerShip.health = Math.min(playerShip.health + 1, playerShip.maxHealth); // Heal 1 HP up to new max if (typeof playerShip._fractionalHealth !== 'undefined') { // Ensure fractional health is updated playerShip._fractionalHealth = playerShip.health; } // Add a new heart icon if needed (maxHealth might exceed initial hearts array length) // Ensure heart icons match current maxHealth and health while (hearts.length < playerShip.maxHealth) { var newHeart = new Heart(); newHeart.x = hearts.length * 120 + 120; newHeart.y = 60; LK.gui.topLeft.addChild(newHeart); hearts.push(newHeart); } for (var i = 0; i < hearts.length; i++) { hearts[i].visible = i < playerShip.health; } } } }, { key: "cardsttsck_doubledamage", // Cardsttsck: Double Projectile Damage asset: "Cardsttsck", applyEffect: function applyEffect() { if (typeof playerProjectileDamage === 'undefined') { playerProjectileDamage = 1; } playerProjectileDamage *= 2; } }, { // Adding new card Asplitmind key: "asplitmind_split_shot", asset: "Asplitmind", applyEffect: function applyEffect() { // playerSplitShotActive is globally defined and initialized to false. // This effect makes the split shot permanently true. playerSplitShotActive = true; } }]; // --- Shuffle and select 3 cards to display --- // Fisher-Yates shuffle for (var k = allAvailableCards.length - 1; k > 0; k--) { var j_shuffle = Math.floor(Math.random() * (k + 1)); var tempCard = allAvailableCards[k]; allAvailableCards[k] = allAvailableCards[j_shuffle]; allAvailableCards[j_shuffle] = tempCard; } var cardsToDisplay = allAvailableCards.slice(0, 3); // Get the first 3 cards // --- Position and display selected cards --- var cardPositions = [{ x: 2048 / 2 - 450, y: 2732 / 2 }, // Left card { x: 2048 / 2, y: 2732 / 2 }, // Middle card { x: 2048 / 2 + 450, y: 2732 / 2 } // Right card ]; for (var cardIdx = 0; cardIdx < cardsToDisplay.length; cardIdx++) { var cardData = cardsToDisplay[cardIdx]; var cardContainer = new Container(); // Create a new container for each card // The image itself is positioned globally based on cardPositions var cardImg = LK.getAsset(cardData.asset, { anchorX: 0.5, anchorY: 0.5, x: cardPositions[cardIdx].x, y: cardPositions[cardIdx].y }); cardContainer.addChild(cardImg); // The container itself can be at 0,0 as its child (the image) is globally positioned. // However, for consistency with how interactives are often handled, // we can also set the container's position if we wanted the image local to it. // For this setup, global positioning of the image is fine. cardContainer.x = 0; cardContainer.y = 0; cardContainer.interactive = true; // Use a closure to correctly capture cardData for each event handler (function (currentCardData) { cardContainer.down = function (x_down, y_down, obj_down) { if (!game._cardChoiceActive || game._cardChoiceBlock) return; currentCardData.applyEffect(); finishCardChoice(); }; })(cardData); cardOverlay.addChild(cardContainer); } // Add overlay to game game.addChild(cardOverlay); // Block all input except card choice oldGameDown = game.down; oldGameMove = game.move; oldGameUp = game.up; game.down = function (x, y, obj) { if (game._cardChoiceBlock) return; // Defensive: Only forward to cards that exist in overlay if (cardOverlay && cardOverlay.children) { for (var i = 0; i < cardOverlay.children.length; i++) { var child = cardOverlay.children[i]; if (child && child.interactive && typeof child.down === "function" && child.children && child.children.length > 0) { var img = child.children[0]; if (img && typeof child.toLocal === "function") { var local = child.toLocal({ x: x, y: y }); if (img.width && img.height && Math.abs(local.x) < img.width / 2 && Math.abs(local.y) < img.height / 2) { child.down(x, y, obj); return; } } } } } }; game.move = function () {}; game.up = function () {}; } // } // if (typeof scoreTxt !== 'undefined' && scoreTxt && scoreTxt.setText) { // scoreTxt.setText(LK.getScore()); // } return 1; // continue // Move to the next enemy ship in the list } if (enemy.update) { enemy.update(); // Call enemy ship's own update logic (movement, etc.) } // Then, check if the enemy ship flagged itself as off-screen (if not already destroyed) if (enemy.isOffScreen) { enemy.destroy(); // Destroy the enemy ship enemyShips.splice(j, 1); // Remove from the tracking array // No score for merely going off-screen, unless desired. } }, enemy, boomImg, healthRestore, cardOverlay, bg, card1, card1Bg, card1Text, card2, card2Bg, card2Text, oldGameDown, oldGameMove, oldGameUp; for (var j = enemyShips.length - 1; j >= 0; j--) { if (_loop()) continue; } // Increase the number of enemies over time // Spawn regular enemies only if NEITHER boss is currently active var originalBossActive = bossEnemyInstance && !bossEnemyInstance.isDestroyed; var giantBossActive = giantBossEnemyInstance && !giantBossEnemyInstance.isDestroyed; if (!originalBossActive && !giantBossActive && LK.ticks % Math.max(30, 150 - Math.floor(LK.ticks / 300)) === 0) { // Determine multiplier (default 1, or 3x if card2 effect active) var multiplier = typeof game._enemySpawnMultiplier !== 'undefined' && game._enemySpawnMultiplier > 1 ? game._enemySpawnMultiplier : 1; for (var spawnIdx = 0; spawnIdx < multiplier; spawnIdx++) { // 25% chance to spawn armored enemy, else normal var newEnemyShip; if (Math.random() < 0.25 && typeof ArmoredEnemyShip !== 'undefined') { newEnemyShip = new ArmoredEnemyShip(); } else { newEnemyShip = new EnemyShip(); } var enemyShipAssetHeight = newEnemyShip.shipSprite && newEnemyShip.shipSprite.height ? newEnemyShip.shipSprite.height : 146; newEnemyShip.x = Math.random() * 2048; newEnemyShip.y = -(enemyShipAssetHeight / 2) - 50; newEnemyShip.lastX = newEnemyShip.x; newEnemyShip.lastY = newEnemyShip.y; game.addChild(newEnemyShip); enemyShips.push(newEnemyShip); } } // --- Boss Update and Defeat Logic --- if (bossEnemyInstance) { if (bossEnemyInstance.isDestroyed) { // Duration each explosion frame is visible (ms) // Helper function to manage the chained explosion sequence var _showNextExplosion = function showNextExplosion(index, previousExplosionAsset) { // Clean up the previous explosion asset from the screen if (previousExplosionAsset && typeof previousExplosionAsset.destroy === 'function') { previousExplosionAsset.destroy(); } // Check if there are more explosion frames to show if (index < explosionAssets.length) { var assetId = explosionAssets[index]; var currentExplosion = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.5, x: explosionX, y: explosionY, scaleX: 1.5, scaleY: 1.5 // Make boss explosions bigger }); game.addChild(currentExplosion); LK.getSound('Boom').play(); // Play sound for each frame LK.setTimeout(function () { _showNextExplosion(index + 1, currentExplosion); }, explosionAssetDuration); } else { // All explosion frames for the first boss are done bossEnemyInstance = null; // Officially nullify the global reference after explosion // Check if giant boss is NOT active (or not spawned / already destroyed) if (!giantBossEnemyInstance || giantBossEnemyInstance.isDestroyed) { LK.stopMusic(); // Stop current music first LK.playMusic('Space'); // Resume normal gameplay music } // If giant boss IS active, its spawn logic would have (or will) set 'Boss' music. } }; // End of _showNextExplosion definition // Boss has been defeated (main body destroyed) LK.setScore(LK.getScore() + 250); // Big score for defeating the boss money += 100; storage.money = money; // Save money to storage if (typeof moneyTxt !== 'undefined' && moneyTxt.setText) { moneyTxt.setText(money + " $"); } // Grand explosion for boss defeat var explosionX = bossEnemyInstance.x + (bossEnemyInstance.mainBody ? bossEnemyInstance.mainBody.x : 0); var explosionY = bossEnemyInstance.y + (bossEnemyInstance.mainBody ? bossEnemyInstance.mainBody.y : 0); // Grand explosion for boss defeat is handled by a chained sequence // Define explosion parameters (position is already defined above this block) var explosionAssets = ['Boom', 'Boom2', 'Boom3', 'Boom4', 'Boom5', 'Boom6']; var explosionAssetDuration = 150; if (explosionAssets.length > 0) { // Initial call to start the sequence with the first asset (index 0) // There's no previousExplosionAsset for the first frame. _showNextExplosion(0, null); } bossEnemyInstance.destroy(); // Remove boss from game (destroys the actual game object) // Note: bossEnemyInstance = null; and LK.playMusic('Space'); have been moved into _showNextExplosion // Optionally, trigger win condition or next phase // LK.showYouWin(); } else { bossEnemyInstance.update(); // Update active boss } } }; // Music will be managed by game state - boss music during boss fights, space music otherwise ; ;
===================================================================
--- original.js
+++ change.js
@@ -725,32 +725,70 @@
x: Math.random() * 2048,
y: Math.random() * 2732,
tint: starColors[Math.floor(Math.random() * starColors.length)]
});
- // Add twinkling animation to stars
- tween(star, {
- alpha: 0.3
- }, {
- duration: 1000 + Math.random() * 1000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- tween(star, {
- alpha: 1.0
- }, {
- duration: 1000 + Math.random() * 1000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- // Create infinite loop by calling the twinkling again
- tween(star, {
- alpha: 0.3
- }, {
- duration: 1000 + Math.random() * 1000,
- easing: tween.easeInOut
- });
- }
- });
- }
- });
+ // 30% chance for a star to have disappearing/reappearing effect instead of twinkling
+ if (Math.random() < 0.3) {
+ // Create disappearing and reappearing effect for some stars
+ var disappearDelay = 2000 + Math.random() * 8000; // Random delay 2-10 seconds before first disappear
+ LK.setTimeout(function () {
+ function startDisappearCycle() {
+ // Disappear quickly
+ tween(star, {
+ alpha: 0
+ }, {
+ duration: 200 + Math.random() * 300,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ // Stay invisible for a random time
+ var invisibleTime = 500 + Math.random() * 2000; // 0.5-2.5 seconds invisible
+ LK.setTimeout(function () {
+ // Reappear
+ tween(star, {
+ alpha: 1.0
+ }, {
+ duration: 300 + Math.random() * 500,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ // Wait before next disappear cycle
+ var nextCycleDelay = 3000 + Math.random() * 7000; // 3-10 seconds before next cycle
+ LK.setTimeout(function () {
+ startDisappearCycle(); // Start the cycle again
+ }, nextCycleDelay);
+ }
+ });
+ }, invisibleTime);
+ }
+ });
+ }
+ startDisappearCycle();
+ }, disappearDelay);
+ } else {
+ // Add normal twinkling animation to other stars
+ tween(star, {
+ alpha: 0.3
+ }, {
+ duration: 1000 + Math.random() * 1000,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(star, {
+ alpha: 1.0
+ }, {
+ duration: 1000 + Math.random() * 1000,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ // Create infinite loop by calling the twinkling again
+ tween(star, {
+ alpha: 0.3
+ }, {
+ duration: 1000 + Math.random() * 1000,
+ easing: tween.easeInOut
+ });
+ }
+ });
+ }
+ });
+ }
game.addChild(star);
}
// Adding it here as one of the first children to 'game' ensures it's in the background.
var maxPlayerAmmo = 10; // Maximum player ammo capacity
Звездолет вид сверху два д для 2d игры пиксельный. In-Game asset
Красный лазерный луч пиксельный вид сверху. In-Game asset. 2d. High contrast. No shadows
Пиксельная шестерёнка с гаечным ключом. In-Game asset. 2d. High contrast. No shadows
Карточка с изоброжение скорости атака пиксельная карточка улучшения пиксельная космическая. In-Game asset. 2d. High contrast. No shadows
Карта усиления пиксельная усиливает скорость игрока космическая 2д пиксели. In-Game asset. 2d. High contrast. No shadows
Бронированный летающий корабль звездолет пиксельный вид сверху 2д. In-Game asset. 2d. High contrast. No shadows
Start в космической пмксельном стиле. In-Game asset. 2d. High contrast. No shadows
pixel inscription battle of starships in the style of space pixel art. In-Game asset. 2d. High contrast. No shadows
Карта усиления дающие + хп пиксельная космическая. In-Game asset. 2d. High contrast. No shadows
Пиксельная карта усиления атаки космос битва. In-Game asset. 2d. High contrast. No shadows
Карточка улучшения раздватвает атаку пиксельная в стиле космоса. In-Game asset. 2d. High contrast. No shadows
Пиксельная круглая кнопка атаки. In-Game asset. 2d. High contrast. No shadows
Пиксельный корабль сверху с нарисованным огнем спереди вид сверху. In-Game asset. 2d. High contrast. No shadows
Звездолет оформление в стиле призрака пиксельный вид сверху. In-Game asset. 2d. High contrast. No shadows
Пиксельная черная дыра желто черного цвета. In-Game asset. 2d. High contrast. No shadows