User prompt
Sonido golpear al dar a un obstáculo o Boss
User prompt
Sonido acierto disparo a Boss o obstáculos
User prompt
Audio música de fondo
User prompt
Y sonido para disparo para el Boss
User prompt
Power-up de balas disparan 3 balas hacia delante abiertas. Crea un activo para este power-up ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Mis balas en ver de ir al centro de la pantalla van al exterior de la pantalla
User prompt
El campo estelar gira al lado que gire el jugador si el jugador va a la derecha ellas hacia la derecha si ve el jugador hacia la izquierda ellas a la izquierda y a la misma velocidad que el jugador ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Hacer que el campo estelar sea más amplio ,gire y orbite alrededor del centro a la misma velocidad y dirección que el jugador ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El fondo de estrellas se mueve gira en la misma orbita y a la par que el jugador ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
La imagen de la luna y el fondo de estrellas mover a la par del desplazamiento del jugador
User prompt
El nivel debajo de la puntuación
User prompt
El nivel al lado de la puntuación. Y un récord de nivel debajo de el récord de puntos ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
El nivel debajo del récord
User prompt
Mi bala tiene la cabeza que mirar al centro de la pantalla y el culo de la bala a los bordes de la pantalla
User prompt
El assets para mis balas
User prompt
Los Boss con colores originales sin efectos, mis balas más grandes x3 su tamaño
User prompt
La cabeza del Boss tiene que estar mirando al borde de la pantalla y los pies al centro. La bala tiene que estar la punta al centro de la pantalla y el culo de la bala al borde de la pantalla ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El Boss gira en su órbita según el nivel de la partida y dispara según el nivel de la partida. Agusta la dificultad del Boss y añade 3 assets más para poner distintos Boss y que salgan aleatorio ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El Boss no gira sobre si mismo ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Cuando matas al Boss con tus balas sumas 500 puntos
User prompt
El Boss no es inmune a tus balas
User prompt
El Boss dispara con las mismas reglas que tú y es inmune a los obstáculos pero no a tus balas
User prompt
El Boss está en una órbita más superior y los disparos son comos los del player pero en color naranja ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Crea boss que disparan en un órbita mayor que la tuya y crea su assets ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Dificultad creciente y marcador de nivel
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
lives: 3,
maxLives: 3,
highestLevel: 1
});
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
// Randomly select boss asset
var bossAssets = ['boss', 'boss2', 'boss3', 'boss4'];
var selectedAsset = bossAssets[Math.floor(Math.random() * bossAssets.length)];
var bossGraphics = self.attachAsset(selectedAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
self.health = 50;
self.maxHealth = 50;
self.shootTimer = 0;
// Shooting rate based on current level - faster at higher levels
self.shootRate = Math.max(30, 90 - (currentLevel - 1) * 5); // Gets faster each level
self.angle = 0;
self.orbitDistance = 800; // Boss orbits at much larger radius than player (was 600)
self.centerX = 2048 / 2;
self.centerY = 2732 / 2;
// Rotation speed based on current level - faster at higher levels
self.rotationSpeed = 0.02 + (currentLevel - 1) * 0.005;
self.bullets = [];
self.glowPhase = 0;
self.update = function () {
// Update level-based properties dynamically
self.shootRate = Math.max(30, 90 - (currentLevel - 1) * 5); // Gets faster each level
self.rotationSpeed = 0.02 + (currentLevel - 1) * 0.005; // Gets faster each level
// Orbital movement around center
self.angle += self.rotationSpeed;
self.x = self.centerX + Math.cos(self.angle) * self.orbitDistance;
self.y = self.centerY + Math.sin(self.angle) * self.orbitDistance;
// Visual effects - glow and rotation to face outward
self.glowPhase += 0.1;
var glowIntensity = Math.sin(self.glowPhase) * 0.3 + 0.7;
bossGraphics.alpha = glowIntensity;
// Rotate boss to face outward from center (head toward edge, feet toward center)
var angleToCenter = Math.atan2(self.centerY - self.y, self.centerX - self.x);
bossGraphics.rotation = angleToCenter + Math.PI; // Add PI to face away from center
// Remove color tinting to show original boss colors
// Shooting mechanics
self.shootTimer++;
if (self.shootTimer >= self.shootRate) {
self.shoot();
self.shootTimer = 0;
}
// Clean up destroyed bullets
for (var i = self.bullets.length - 1; i >= 0; i--) {
var bullet = self.bullets[i];
if (bullet.age >= bullet.lifetime) {
self.bullets.splice(i, 1);
}
}
};
self.shoot = function () {
// Create bullet that shoots toward center like player
var bullet = new BossBullet();
bullet.x = self.x;
bullet.y = self.y;
// Always shoot toward center (moon) like player does
var dx = self.centerX - self.x;
var dy = self.centerY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = dx / distance * bullet.speed;
bullet.velocityY = dy / distance * bullet.speed;
self.bullets.push(bullet);
game.addChild(bullet);
LK.getSound('bossShoot').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
// Flash effect when taking damage (scale only, no color change)
tween(bossGraphics, {
scaleX: 2.8,
scaleY: 2.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(bossGraphics, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 100,
easing: tween.easeIn
});
}
});
if (self.health <= 0) {
self.destroy();
return true; // Boss defeated
}
return false;
};
return self;
});
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bossBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = 8; // Same speed as player bullets
self.velocityX = 0;
self.velocityY = 0;
self.centerX = 2048 / 2;
self.centerY = 2732 / 2;
self.update = function () {
// Move in straight line toward center like player bullets
self.x += self.velocityX;
self.y += self.velocityY;
// Rotate bullet to point toward center (tip toward center, tail toward edge)
bulletGraphics.rotation = Math.atan2(self.velocityY, self.velocityX);
// Remove bullet if it goes off screen or reaches center
var distanceToCenter = Math.sqrt((self.x - self.centerX) * (self.x - self.centerX) + (self.y - self.centerY) * (self.y - self.centerY));
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782 || distanceToCenter < 80) {
self.destroy();
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
self.speed = 12;
self.velocityX = 0;
self.velocityY = 0;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Rotate bullet to point away from center (tip toward edge, tail toward center)
bulletGraphics.rotation = Math.atan2(self.velocityY, self.velocityX);
// Remove bullet if it goes off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
}
};
return self;
});
var FireParticle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
tint: 0xff4500
});
self.life = 1.0;
self.velocityX = 0;
self.velocityY = 0;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.life -= 0.05;
particleGraphics.alpha = self.life;
particleGraphics.scaleX = self.life * 0.6;
particleGraphics.scaleY = self.life * 0.6;
if (self.life <= 0) {
self.destroy();
}
};
return self;
});
var ImmunityPowerUp = Container.expand(function () {
var self = Container.call(this);
var shieldGraphics = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x00ffff
});
// Rotation and glow effect
self.rotationSpeed = 0.08;
self.glowPhase = 0;
self.glowSpeed = 0.12;
self.baseScale = 1.2;
self.lifetime = 600; // 10 seconds at 60fps
self.age = 0;
self.update = function () {
// Rotate the shield
shieldGraphics.rotation += self.rotationSpeed;
// Glowing effect
self.glowPhase += self.glowSpeed;
var glowFactor = 1 + Math.sin(self.glowPhase) * 0.3;
shieldGraphics.scaleX = self.baseScale * glowFactor;
shieldGraphics.scaleY = self.baseScale * glowFactor;
// Cycle through cyan and blue tints
var tintPhase = Math.sin(self.glowPhase * 0.5) * 0.5 + 0.5;
var r = 0;
var g = Math.floor(255 * (0.6 + tintPhase * 0.4));
var b = 255;
shieldGraphics.tint = r << 16 | g << 8 | b;
// Age and fade out near end of lifetime
self.age++;
if (self.age > self.lifetime * 0.7) {
var fadeProgress = (self.age - self.lifetime * 0.7) / (self.lifetime * 0.3);
shieldGraphics.alpha = 1 - fadeProgress;
}
// Remove when lifetime expires
if (self.age >= self.lifetime) {
self.destroy();
}
};
return self;
});
var LifePowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0xff1493
});
// Rotation and pulsing effect
self.rotationSpeed = 0.05;
self.pulsePhase = 0;
self.pulseSpeed = 0.1;
self.baseScale = 0.8;
self.lifetime = 600; // 10 seconds at 60fps
self.age = 0;
self.update = function () {
// Rotate the power-up
powerUpGraphics.rotation += self.rotationSpeed;
// Pulsing scale effect
self.pulsePhase += self.pulseSpeed;
var pulseFactor = 1 + Math.sin(self.pulsePhase) * 0.2;
powerUpGraphics.scaleX = self.baseScale * pulseFactor;
powerUpGraphics.scaleY = self.baseScale * pulseFactor;
// Age and fade out near end of lifetime
self.age++;
if (self.age > self.lifetime * 0.7) {
var fadeProgress = (self.age - self.lifetime * 0.7) / (self.lifetime * 0.3);
powerUpGraphics.alpha = 1 - fadeProgress;
}
// Remove when lifetime expires
if (self.age >= self.lifetime) {
self.destroy();
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
self.speed = 2;
self.targetX = 2048 / 2;
self.targetY = 2732 / 2;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Calculate scale based on distance from center, but only start scaling after passing player orbit
var playerOrbitDistance = 400; // Player orbit distance from center
var maxDistance = Math.sqrt(2048 / 2 * (2048 / 2) + 2732 / 2 * (2732 / 2)); // Max distance from center to corner
var minDistance = 80; // Stop before touching the moon (moon radius ~60px)
var scale;
if (distance > playerOrbitDistance) {
// Maintain original size until reaching player orbit
scale = 2.0; // Original obstacle scale
} else {
// Start scaling only after passing player orbit
var normalizedDistance = Math.max(0, Math.min(1, (distance - minDistance) / (playerOrbitDistance - minDistance)));
scale = 0.15 + normalizedDistance * 1.85; // Scale from 0.15 to 2.0 within player orbit
}
// Apply scale with tween for smooth transition
tween(obstacleGraphics, {
scaleX: scale,
scaleY: scale
}, {
duration: 100,
easing: tween.easeOut
});
// Always move toward center - obstacles will disappear before reaching moon
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Rotate obstacle continuously as it moves
obstacleGraphics.rotation += 0.1;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Make it look more like a spaceship by rotating it to point upward
playerGraphics.rotation = -Math.PI * 1.5; // Rotated an additional 90 degrees
self.speed = 8;
self.targetX = self.x;
self.targetY = self.y;
self.lastX = self.x;
self.lastY = self.y;
self.fireParticles = [];
self.bullets = [];
self.shootCooldown = 0;
self.tripleShotTimer = 0;
self.tripleShotDuration = 900; // 15 seconds at 60fps
self.hasTripleShot = false;
self.engineSoundPlaying = false;
self.update = function () {
// Store last position for movement detection
self.lastX = self.x;
self.lastY = self.y;
// Smooth movement toward target position
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var isMoving = Math.abs(dx) > 1 || Math.abs(dy) > 1;
if (isMoving) {
self.x += dx * 0.15;
self.y += dy * 0.15;
// Play engine sound when moving
if (!self.engineSoundPlaying) {
self.engineSound = LK.getSound('engine');
self.engineSound.play();
self.engineSoundPlaying = true;
}
} else {
// Stop engine sound when not moving
if (self.engineSoundPlaying) {
if (self.engineSound) {
self.engineSound.stop();
}
self.engineSoundPlaying = false;
}
}
// Calculate angle to center and rotate spaceship to point toward it
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var angleToCenter = Math.atan2(centerY - self.y, centerX - self.x);
var targetRotation = angleToCenter - Math.PI / 2; // Point toward center
// Smooth rotation toward target angle using tween
tween(playerGraphics, {
rotation: targetRotation
}, {
duration: 100,
easing: tween.easeOut
});
// Create fire particles when moving
if (isMoving) {
var particle = new FireParticle();
// Always position particle from the left side of player (consistent side)
var leftSideOffsetX = -35; // Fixed offset to the left
var leftSideOffsetY = 0;
particle.x = self.x + leftSideOffsetX;
particle.y = self.y + leftSideOffsetY;
// Add velocity toward the left side (consistent direction)
particle.velocityX = -2 - Math.random() * 2;
particle.velocityY = 0;
// Add some random spread
particle.velocityX += (Math.random() - 0.5) * 2;
particle.velocityY += (Math.random() - 0.5) * 2;
self.fireParticles.push(particle);
game.addChild(particle);
}
// Clean up dead particles
for (var i = self.fireParticles.length - 1; i >= 0; i--) {
if (self.fireParticles[i].life <= 0) {
self.fireParticles.splice(i, 1);
}
}
// Update shooting cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Update triple shot timer
if (self.tripleShotTimer > 0) {
self.tripleShotTimer--;
if (self.tripleShotTimer <= 0) {
self.hasTripleShot = false;
}
}
// Clean up bullets that are destroyed
for (var i = self.bullets.length - 1; i >= 0; i--) {
var bullet = self.bullets[i];
if (bullet.destroyed || bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
self.bullets.splice(i, 1);
}
}
};
self.moveTo = function (x, y) {
// Keep player within central area bounds
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var maxDistance = 400;
// Always constrain player to move only on the circular path at maxDistance
var angle = Math.atan2(y - centerY, x - centerX);
self.targetX = centerX + Math.cos(angle) * maxDistance;
self.targetY = centerY + Math.sin(angle) * maxDistance;
};
self.shoot = function () {
if (self.shootCooldown <= 0) {
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var dx = self.x - centerX; // Reversed direction
var dy = self.y - centerY; // Reversed direction
var distance = Math.sqrt(dx * dx + dy * dy);
if (self.hasTripleShot) {
// Fire three bullets with spread
var angles = [-0.3, 0, 0.3]; // 30 degree spread
var baseAngle = Math.atan2(dy, dx);
for (var i = 0; i < angles.length; i++) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = baseAngle + angles[i];
bullet.velocityX = Math.cos(angle) * bullet.speed;
bullet.velocityY = Math.sin(angle) * bullet.speed;
self.bullets.push(bullet);
game.addChild(bullet);
}
} else {
// Normal single bullet
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.velocityX = dx / distance * bullet.speed;
bullet.velocityY = dy / distance * bullet.speed;
self.bullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
self.shootCooldown = 15; // Cooldown between shots
}
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
// Random star properties
self.twinkleSpeed = 0.02 + Math.random() * 0.03;
self.twinklePhase = Math.random() * Math.PI * 2;
self.baseAlpha = 0.5 + Math.random() * 0.5;
self.isFlashing = false;
self.flashTimer = 0;
self.nextFlashTime = Math.random() * 300 + 120; // Random time between 2-7 seconds
self.updateScale = function () {
// Calculate distance from center of screen
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var dx = self.x - centerX;
var dy = self.y - centerY;
var distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
// Maximum possible distance from center to corner
var maxDistance = Math.sqrt(centerX * centerX + centerY * centerY);
// Scale factor: 3.0 at center, 8.0 at edges (much larger stars)
var normalizedDistance = distanceFromCenter / maxDistance;
var scale = 3.0 + normalizedDistance * 5.0;
starGraphics.scaleX = scale;
starGraphics.scaleY = scale;
};
self.update = function () {
// Orbit around center matching player's movement direction and speed
var centerX = 2048 / 2;
var centerY = 2732 / 2;
// Calculate player's movement direction
var playerDx = player.x - player.lastX;
var playerDy = player.y - player.lastY;
var playerMovement = Math.sqrt(playerDx * playerDx + playerDy * playerDy);
// Determine rotation direction based on player's orbital movement
var playerAngle = Math.atan2(player.y - centerY, player.x - centerX);
var playerLastAngle = Math.atan2(player.lastY - centerY, player.lastX - centerX);
var angleDiff = playerAngle - playerLastAngle;
// Handle angle wrapping around π/-π boundary
if (angleDiff > Math.PI) angleDiff -= 2 * Math.PI;
if (angleDiff < -Math.PI) angleDiff += 2 * Math.PI;
// Only rotate stars when player is actually moving
var rotationSpeed = 0;
if (playerMovement > 0.5) {
// Player is moving
rotationSpeed = angleDiff; // Match player's angular velocity
}
// Calculate current angle and distance from center
var dx = self.x - centerX;
var dy = self.y - centerY;
var currentDistance = Math.sqrt(dx * dx + dy * dy);
var currentAngle = Math.atan2(dy, dx);
// Rotate around center matching player's rotation
currentAngle += rotationSpeed;
// Update position maintaining same distance from center
self.x = centerX + Math.cos(currentAngle) * currentDistance;
self.y = centerY + Math.sin(currentAngle) * currentDistance;
// Normal twinkling effect
if (!self.isFlashing) {
self.twinklePhase += self.twinkleSpeed;
starGraphics.alpha = self.baseAlpha + Math.sin(self.twinklePhase) * 0.3;
starGraphics.tint = 0xffffff;
// Check if it's time for a colored flash
self.flashTimer++;
if (self.flashTimer >= self.nextFlashTime) {
self.isFlashing = true;
self.flashTimer = 0;
// Randomly choose between yellow and blue flash
var flashColor = Math.random() < 0.5 ? 0xffff00 : 0x00aaff;
// Start colored flash tween
tween(starGraphics, {
tint: flashColor
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fade back to white
tween(starGraphics, {
tint: 0xffffff
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isFlashing = false;
self.nextFlashTime = Math.random() * 300 + 120; // Next flash in 2-7 seconds
}
});
}
});
}
} else {
// During flash, maintain bright alpha
starGraphics.alpha = 1.0;
}
// Update scale based on distance from center
self.updateScale();
};
return self;
});
var TripleShotPowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('tripleShotPowerUp', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0xffa500
});
// Rotation and pulsing effect
self.rotationSpeed = 0.06;
self.pulsePhase = 0;
self.pulseSpeed = 0.08;
self.baseScale = 1.0;
self.lifetime = 600; // 10 seconds at 60fps
self.age = 0;
self.update = function () {
// Rotate the power-up
powerUpGraphics.rotation += self.rotationSpeed;
// Pulsing scale effect
self.pulsePhase += self.pulseSpeed;
var pulseFactor = 1 + Math.sin(self.pulsePhase) * 0.3;
powerUpGraphics.scaleX = self.baseScale * pulseFactor;
powerUpGraphics.scaleY = self.baseScale * pulseFactor;
// Cycle through orange and yellow tints
var tintPhase = Math.sin(self.pulsePhase * 0.7) * 0.5 + 0.5;
var r = 255;
var g = Math.floor(160 + tintPhase * 95);
var b = 0;
powerUpGraphics.tint = r << 16 | g << 8 | b;
// Age and fade out near end of lifetime
self.age++;
if (self.age > self.lifetime * 0.7) {
var fadeProgress = (self.age - self.lifetime * 0.7) / (self.lifetime * 0.3);
powerUpGraphics.alpha = 1 - fadeProgress;
}
// Remove when lifetime expires
if (self.age >= self.lifetime) {
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var player;
var obstacles = [];
var bullets = [];
var stars = [];
var powerUps = [];
var gameStarted = false;
var spawnTimer = 0;
var spawnRate = 120; // Initial spawn rate (ticks between spawns)
var difficultyTimer = 0;
var baseObstacleSpeed = 2;
var currentLives = storage.lives || 3;
var maxLives = storage.maxLives || 3;
var invulnerabilityTimer = 0;
var invulnerabilityDuration = 120; // 2 seconds at 60fps
var powerUpSpawnTimer = 0;
var powerUpSpawnRate = 1800; // Spawn power-up every 30 seconds at 60fps
var timeScoreTimer = 0;
var timeScoreRate = 60; // Award points every second (60 ticks)
var immunityTimer = 0;
var immunityDuration = 600; // 10 seconds at 60fps
var isImmune = false;
var currentLevel = 1;
var levelTimer = 0;
var levelDuration = 1800; // 30 seconds per level at 60fps
var baseSpawnRate = 120;
var minSpawnRate = 20;
var bosses = [];
var bossBullets = [];
var bossSpawnTimer = 0;
var bossSpawnRate = 3600; // Spawn boss every 60 seconds
var bossActive = false;
// Create starfield background
function createStarfield() {
for (var i = 0; i < 300; i++) {
// Double the number of stars for more expansive field
var star = new Star();
// Expand star generation area beyond screen bounds for wider coverage
var expandedWidth = 2048 * 1.8; // 80% larger width
var expandedHeight = 2732 * 1.8; // 80% larger height
var offsetX = (expandedWidth - 2048) / 2; // Center the expanded area
var offsetY = (expandedHeight - 2732) / 2;
star.x = -offsetX + Math.random() * expandedWidth;
star.y = -offsetY + Math.random() * expandedHeight;
// Apply initial scaling based on position
star.updateScale();
stars.push(star);
game.addChild(star);
}
}
// Initialize starfield
createStarfield();
// Create center moon indicator
var centerIndicator = LK.getAsset('moon', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: 0.8
});
centerIndicator.x = 2048 / 2;
centerIndicator.y = 2732 / 2;
game.addChild(centerIndicator);
// Start continuous moon rotation
tween(centerIndicator, {
rotation: Math.PI * 2
}, {
duration: 8000,
easing: tween.linear,
onFinish: function onFinish() {
// Reset rotation and start again for infinite loop
centerIndicator.rotation = 0;
tween(centerIndicator, {
rotation: Math.PI * 2
}, {
duration: 8000,
easing: tween.linear,
onFinish: arguments.callee // Reference to this same function for infinite loop
});
}
});
// Create sparkle effects container for moon impact glimmers
var moonSparkles = [];
function createMoonSparkle(impactX, impactY) {
// Random initial size for sparkles - vary between small, medium and large
var initialSize = 4 + Math.random() * 12; // Range from 4 to 16
// Random sparkle color - white, yellow, orange, or red
var sparkleColors = [0xffffff, 0xffff00, 0xff8800, 0xff0000];
var randomColor = sparkleColors[Math.floor(Math.random() * sparkleColors.length)];
var sparkle = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: initialSize,
scaleY: initialSize,
alpha: 0,
tint: randomColor
});
// Position sparkle at the impact location on moon's surface
sparkle.x = impactX;
sparkle.y = impactY;
game.addChild(sparkle);
moonSparkles.push(sparkle);
// Calculate target sizes - make them grow to 1.5-2x their initial size
var maxSize = initialSize * (1.5 + Math.random() * 0.5);
var endSize = initialSize * 0.3;
// Sparkle animation - fade in, scale up, then fade out (much faster)
tween(sparkle, {
alpha: 1,
scaleX: maxSize,
scaleY: maxSize
}, {
duration: 120,
// Much faster - was 300ms, now 120ms
easing: tween.easeOut,
onFinish: function onFinish() {
tween(sparkle, {
alpha: 0,
scaleX: endSize,
scaleY: endSize
}, {
duration: 180,
// Much faster - was 400ms, now 180ms
easing: tween.easeIn,
onFinish: function onFinish() {
sparkle.destroy();
// Remove from array
for (var i = moonSparkles.length - 1; i >= 0; i--) {
if (moonSparkles[i] === sparkle) {
moonSparkles.splice(i, 1);
break;
}
}
}
});
}
});
}
// Create player
player = new Player();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// Score display
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// High score display
var highScore = storage.highScore || 0;
var highScoreTxt = new Text2('Best: ' + highScore, {
size: 60,
fill: 0xFFD700
});
highScoreTxt.anchor.set(1, 0);
highScoreTxt.y = 0;
LK.gui.topRight.addChild(highScoreTxt);
// Level display (below score)
var levelTxt = new Text2('Level 1', {
size: 60,
fill: 0x00FF88
});
levelTxt.anchor.set(0.5, 0);
levelTxt.x = 0;
levelTxt.y = 100;
LK.gui.top.addChild(levelTxt);
// Level record display (below high score)
var highestLevel = storage.highestLevel || 1;
var levelRecordTxt = new Text2('Best Level: ' + highestLevel, {
size: 60,
fill: 0x00FF88
});
levelRecordTxt.anchor.set(1, 0);
levelRecordTxt.x = 0;
levelRecordTxt.y = 80;
LK.gui.topRight.addChild(levelRecordTxt);
// Lives display with player icons
var livesContainer = new Container();
var lifeIcons = [];
function updateLivesDisplay() {
// Clear existing icons and text
for (var i = 0; i < lifeIcons.length; i++) {
lifeIcons[i].destroy();
}
lifeIcons = [];
if (livesContainer.livesText) {
livesContainer.livesText.destroy();
}
// Create single life icon using heart asset
var lifeIcon = LK.getAsset('heartIcon', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0xff4444
});
lifeIcon.x = 40; // Position icon from left edge
lifeIcon.y = 0;
lifeIcons.push(lifeIcon);
livesContainer.addChild(lifeIcon);
// Create lives number text
var livesText = new Text2('x' + currentLives, {
size: 60,
fill: 0xFFFFFF
});
livesText.anchor.set(0, 0.5);
livesText.x = 80; // Position text next to icon
livesText.y = 0;
livesContainer.livesText = livesText;
livesContainer.addChild(livesText);
}
livesContainer.x = 0;
livesContainer.y = 0;
LK.gui.bottomLeft.addChild(livesContainer);
updateLivesDisplay();
// Create shoot button in bottom right
var shootButtonContainer = new Container();
var shootButton = LK.getAsset('shootButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4,
alpha: 0.8
});
shootButtonContainer.addChild(shootButton);
shootButtonContainer.x = -120;
shootButtonContainer.y = -120;
LK.gui.bottomRight.addChild(shootButtonContainer);
// Add shoot button touch handler
shootButtonContainer.down = function (x, y, obj) {
if (gameStarted) {
player.shoot();
// Visual feedback - briefly scale and flash the button
tween(shootButton, {
scaleX: 5,
scaleY: 5,
alpha: 1.0
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(shootButton, {
scaleX: 4,
scaleY: 4,
alpha: 0.8
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
};
// Game instructions
var instructionTxt = new Text2('Touch to move around the center', {
size: 60,
fill: 0xCCCCCC
});
instructionTxt.anchor.set(0.5, 0.5);
instructionTxt.x = 2048 / 2;
instructionTxt.y = 2732 / 2 + 80;
game.addChild(instructionTxt);
function updateLives(newLives) {
currentLives = Math.max(0, newLives); // Remove max lives constraint
storage.lives = currentLives;
updateLivesDisplay();
if (currentLives <= 0) {
LK.showGameOver();
}
}
function spawnObstacle() {
var obstacle = new Obstacle();
// Random spawn position along screen edges
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top edge
obstacle.x = Math.random() * 2048;
obstacle.y = -50;
break;
case 1:
// Right edge
obstacle.x = 2048 + 50;
obstacle.y = Math.random() * 2732;
break;
case 2:
// Bottom edge
obstacle.x = Math.random() * 2048;
obstacle.y = 2732 + 50;
break;
case 3:
// Left edge
obstacle.x = -50;
obstacle.y = Math.random() * 2732;
break;
}
// Add random size variation to obstacles
var randomScale = 0.5 + Math.random() * 1.5; // Random scale between 0.5x and 2x
obstacle.scaleX = randomScale;
obstacle.scaleY = randomScale;
// Set speed based on current level difficulty
obstacle.speed = baseObstacleSpeed + Math.random() * 1; // Add slight random variation
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnPowerUp() {
// Always spawn power-ups (no maximum lives check)
// Randomly choose between life, immunity, and triple shot power-ups (33% each)
var randomValue = Math.random();
var powerUp;
if (randomValue < 0.33) {
powerUp = new LifePowerUp();
} else if (randomValue < 0.66) {
powerUp = new ImmunityPowerUp();
} else {
powerUp = new TripleShotPowerUp();
}
// Spawn in a safe area around the player's orbit
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var angle = Math.random() * Math.PI * 2;
var distance = 300 + Math.random() * 200; // Between player orbit and center
powerUp.x = centerX + Math.cos(angle) * distance;
powerUp.y = centerY + Math.sin(angle) * distance;
powerUps.push(powerUp);
game.addChild(powerUp);
}
function spawnBoss() {
if (!bossActive) {
var boss = new Boss();
// Start boss at a random position in its orbit
boss.angle = Math.random() * Math.PI * 2;
boss.x = boss.centerX + Math.cos(boss.angle) * boss.orbitDistance;
boss.y = boss.centerY + Math.sin(boss.angle) * boss.orbitDistance;
bosses.push(boss);
game.addChild(boss);
bossActive = true;
// Flash screen orange to indicate boss spawn
LK.effects.flashScreen(0xff4400, 1000);
}
}
function checkCollisions() {
// Check power-up collisions
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
// Initialize collision tracking if not present
if (powerUp.lastIntersecting === undefined) {
powerUp.lastIntersecting = false;
}
var currentIntersecting = player.intersects(powerUp);
// Check if collection just started (transition from false to true)
if (!powerUp.lastIntersecting && currentIntersecting) {
// Check if it's a life power-up or immunity power-up
if (powerUp instanceof LifePowerUp) {
// Always add life (no maximum limit)
// Play power-up sound when gaining a life
LK.getSound('lifeup').play();
updateLives(currentLives + 1);
LK.effects.flashScreen(0x00ff00, 300); // Green flash for power-up
} else if (powerUp instanceof ImmunityPowerUp) {
// Activate immunity
isImmune = true;
immunityTimer = immunityDuration;
// Play power-up sound
LK.getSound('powerup').play();
LK.effects.flashScreen(0x00ffff, 500); // Cyan flash for immunity
// Add glowing effect to player during immunity
tween(player, {
tint: 0x00ffff
}, {
duration: 200,
easing: tween.easeOut
});
} else if (powerUp instanceof TripleShotPowerUp) {
// Activate triple shot
player.hasTripleShot = true;
player.tripleShotTimer = player.tripleShotDuration;
// Play power-up sound
LK.getSound('powerup').play();
LK.effects.flashScreen(0xffa500, 500); // Orange flash for triple shot
// Add orange glow effect to player during triple shot
tween(player, {
tint: 0xffa500
}, {
duration: 200,
easing: tween.easeOut
});
}
// Create collection effect with tween
tween(powerUp, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
powerUp.destroy();
}
});
powerUps.splice(i, 1);
continue; // Skip further checks for this power-up
}
// Update last intersecting state
powerUp.lastIntersecting = currentIntersecting;
}
// Clean up expired power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (powerUp.age >= powerUp.lifetime) {
powerUps.splice(i, 1);
}
}
// Check boss-obstacle collisions (bosses are immune to obstacles)
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
for (var j = bosses.length - 1; j >= 0; j--) {
var boss = bosses[j];
if (obstacle.intersects(boss)) {
// Boss destroys obstacle without taking damage
obstacle.destroy();
obstacles.splice(i, 1);
break;
}
}
}
// Only check collisions if not invulnerable and not immune
if (invulnerabilityTimer <= 0 && !isImmune) {
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Initialize collision tracking if not present
if (obstacle.lastIntersecting === undefined) {
obstacle.lastIntersecting = false;
}
// Create smaller collision area (75% of obstacle size) for more forgiving gameplay
var collisionScale = 0.75;
var originalScaleX = obstacle.scaleX;
var originalScaleY = obstacle.scaleY;
// Temporarily reduce obstacle scale for collision detection
obstacle.scaleX = originalScaleX * collisionScale;
obstacle.scaleY = originalScaleY * collisionScale;
var currentIntersecting = player.intersects(obstacle);
// Restore original scale
obstacle.scaleX = originalScaleX;
obstacle.scaleY = originalScaleY;
// Check if collision just started (transition from false to true)
if (!obstacle.lastIntersecting && currentIntersecting) {
if (isImmune) {
// When immune, destroy obstacle and gain points
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Update high score if current score is higher
if (LK.getScore() > highScore) {
highScore = LK.getScore();
storage.highScore = highScore;
highScoreTxt.setText('Best: ' + highScore);
}
// Create destruction effect
tween(obstacle, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
obstacle.destroy();
}
});
obstacles.splice(i, 1);
} else {
// Normal collision behavior when not immune
// Play collision sound immediately when collision starts
LK.getSound('collision').play();
LK.effects.flashScreen(0xff0000, 500);
// Reduce life instead of immediate game over
updateLives(currentLives - 1);
// Set invulnerability period
invulnerabilityTimer = invulnerabilityDuration;
// Remove the obstacle that hit the player
obstacle.destroy();
obstacles.splice(i, 1);
}
break; // Exit loop after first collision
}
// Update last intersecting state
obstacle.lastIntersecting = currentIntersecting;
}
}
// Check player bullet-boss collisions
for (var i = bosses.length - 1; i >= 0; i--) {
var boss = bosses[i];
for (var j = player.bullets.length - 1; j >= 0; j--) {
var bullet = player.bullets[j];
if (bullet.intersects(boss)) {
// Boss takes damage from player bullet
var bossDefeated = boss.takeDamage(10); // 10 damage per bullet
// Remove bullet
bullet.destroy();
player.bullets.splice(j, 1);
if (bossDefeated) {
// Award 500 points for defeating boss
LK.setScore(LK.getScore() + 500);
scoreTxt.setText(LK.getScore());
// Update high score if current score is higher
if (LK.getScore() > highScore) {
highScore = LK.getScore();
storage.highScore = highScore;
highScoreTxt.setText('Best: ' + highScore);
}
// Remove boss from array
bosses.splice(i, 1);
bossActive = false;
// Create victory effect
LK.effects.flashScreen(0x00ff00, 1000); // Green flash for boss defeat
}
break;
}
}
}
// Check boss bullet-player collisions (only if not immune and not invulnerable)
if (invulnerabilityTimer <= 0 && !isImmune) {
for (var i = bosses.length - 1; i >= 0; i--) {
var boss = bosses[i];
for (var j = boss.bullets.length - 1; j >= 0; j--) {
var bossBullet = boss.bullets[j];
if (bossBullet.lastIntersecting === undefined) {
bossBullet.lastIntersecting = false;
}
var currentIntersecting = player.intersects(bossBullet);
if (!bossBullet.lastIntersecting && currentIntersecting) {
// Play collision sound
LK.getSound('collision').play();
LK.effects.flashScreen(0xff0000, 500);
// Reduce life
updateLives(currentLives - 1);
// Set invulnerability period
invulnerabilityTimer = invulnerabilityDuration;
// Remove boss bullet
bossBullet.destroy();
boss.bullets.splice(j, 1);
break;
}
bossBullet.lastIntersecting = currentIntersecting;
}
}
}
// Check bullet-obstacle collisions
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
var hitByBullet = false;
for (var j = player.bullets.length - 1; j >= 0; j--) {
var bullet = player.bullets[j];
// Create smaller collision area (75% of obstacle size) for bullet collisions
var collisionScale = 0.75;
var originalScaleX = obstacle.scaleX;
var originalScaleY = obstacle.scaleY;
// Temporarily reduce obstacle scale for collision detection
obstacle.scaleX = originalScaleX * collisionScale;
obstacle.scaleY = originalScaleY * collisionScale;
var bulletHit = bullet.intersects(obstacle);
// Restore original scale
obstacle.scaleX = originalScaleX;
obstacle.scaleY = originalScaleY;
if (bulletHit) {
// Add 10 points for destroying obstacle
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Update high score if current score is higher
if (LK.getScore() > highScore) {
highScore = LK.getScore();
storage.highScore = highScore;
highScoreTxt.setText('Best: ' + highScore);
}
// Create destruction effect
tween(obstacle, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
obstacle.destroy();
}
});
// Remove bullet
bullet.destroy();
player.bullets.splice(j, 1);
// Remove obstacle
obstacles.splice(i, 1);
hitByBullet = true;
break;
}
}
if (hitByBullet) continue;
}
// Bullets now pass through the moon without collision detection
// Clean up obstacles that reach close to the moon - make them disappear with effect
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
var distanceToCenter = Math.sqrt((obstacle.x - 2048 / 2) * (obstacle.x - 2048 / 2) + (obstacle.y - 2732 / 2) * (obstacle.y - 2732 / 2));
if (distanceToCenter < 80) {
// Moon collision radius
// Create impact sparkle at obstacle's current position
createMoonSparkle(obstacle.x, obstacle.y);
// Make obstacle disappear with fade effect when touching the moon
tween(obstacle, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
obstacle.destroy();
}
});
obstacles.splice(i, 1);
}
}
}
function updateDifficulty() {
difficultyTimer++;
levelTimer++;
// Level progression - advance level every 30 seconds
if (levelTimer >= levelDuration) {
currentLevel++;
levelTimer = 0;
levelTxt.setText('Level ' + currentLevel);
// Update level record if current level is higher
if (currentLevel > highestLevel) {
highestLevel = currentLevel;
storage.highestLevel = highestLevel;
levelRecordTxt.setText('Best Level: ' + highestLevel);
}
// Flash level up effect
LK.effects.flashScreen(0x00FF88, 800); // Green flash for level up
tween(levelTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(levelTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
// Progressive difficulty based on level
var levelMultiplier = 1 + (currentLevel - 1) * 0.3; // 30% increase per level
baseObstacleSpeed = 2 * levelMultiplier;
// Decrease spawn rate based on level (more frequent spawning)
spawnRate = Math.max(minSpawnRate, baseSpawnRate - (currentLevel - 1) * 8);
// Slightly increase power-up spawn rate at higher levels
powerUpSpawnRate = Math.max(900, 1800 - (currentLevel - 1) * 60); // Faster power-ups at higher levels
}
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
instructionTxt.destroy();
// Start background music
LK.playMusic('backgroundMusic');
}
player.moveTo(x, y);
};
game.move = function (x, y, obj) {
if (gameStarted) {
player.moveTo(x, y);
}
};
game.update = function () {
if (!gameStarted) return;
// Update immunity timer
if (immunityTimer > 0) {
immunityTimer--;
// Make player glow cyan during immunity
var glowIntensity = Math.sin(LK.ticks * 0.3) * 0.5 + 0.5;
player.alpha = 0.7 + glowIntensity * 0.3;
// End immunity when timer expires
if (immunityTimer <= 0) {
isImmune = false;
// Only reset color if no other power-ups are active
if (!player.hasTripleShot) {
// Fade player back to normal color
tween(player, {
tint: 0xffffff,
alpha: 1.0
}, {
duration: 500,
easing: tween.easeOut
});
}
}
}
// Update triple shot visual effect
if (player.hasTripleShot && !isImmune) {
// Make player glow orange during triple shot
var glowIntensity = Math.sin(LK.ticks * 0.2) * 0.3 + 0.7;
player.alpha = glowIntensity;
// End triple shot when timer expires
if (player.tripleShotTimer <= 0) {
// Fade player back to normal color
tween(player, {
tint: 0xffffff,
alpha: 1.0
}, {
duration: 500,
easing: tween.easeOut
});
}
}
// Update invulnerability timer
if (invulnerabilityTimer > 0) {
invulnerabilityTimer--;
// Make player flash during invulnerability (only if not immune)
if (!isImmune) {
player.alpha = invulnerabilityTimer % 10 < 5 ? 0.5 : 1.0;
}
} else if (!isImmune) {
player.alpha = 1.0;
}
// Update score display (score is now updated in collision detection)
scoreTxt.setText(LK.getScore());
// Update high score if current score is higher
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
highScoreTxt.setText('Best: ' + highScore);
}
// Spawn obstacles
spawnTimer++;
if (spawnTimer >= spawnRate) {
spawnObstacle();
spawnTimer = 0;
}
// Spawn power-ups
powerUpSpawnTimer++;
if (powerUpSpawnTimer >= powerUpSpawnRate) {
spawnPowerUp();
powerUpSpawnTimer = 0;
}
// Spawn bosses
bossSpawnTimer++;
if (bossSpawnTimer >= bossSpawnRate) {
spawnBoss();
bossSpawnTimer = 0;
}
// Update difficulty
updateDifficulty();
// Time-based scoring - award points for staying alive
timeScoreTimer++;
if (timeScoreTimer >= timeScoreRate) {
LK.setScore(LK.getScore() + 1);
timeScoreTimer = 0;
}
// Manual shooting only - removed auto-shoot
// Check for collisions
checkCollisions();
}; ===================================================================
--- original.js
+++ change.js
@@ -1276,8 +1276,10 @@
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
instructionTxt.destroy();
+ // Start background music
+ LK.playMusic('backgroundMusic');
}
player.moveTo(x, y);
};
game.move = function (x, y, obj) {
Botón rojo con dibujo de bala en el interior. In-Game asset. 2d. High contrast. No shadows
Planeta tierra. In-Game asset. 2d. High contrast. No shadows
Rodeado de un corazón rosa
Escudo azul. In-Game asset. 2d. High contrast. No shadows
Boss espacial. In-Game asset. 2d. High contrast. No shadows
Bala azul. In-Game asset. 2d. High contrast. No shadows
Amarillo