/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Drop = Container.expand(function (dropType) {
var self = Container.call(this);
self.dropType = dropType;
var dropGraphics = self.attachAsset(dropType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.shootTimer = 0;
var waveReduction = (currentWave - 1) * 60; // Reduce by 1 second (60 frames) per wave
var minInterval = Math.max(60, 360 - waveReduction); // Minimum 1 second interval
var maxInterval = Math.max(120, 600 - waveReduction); // Minimum 2 second max interval
self.shootInterval = minInterval + Math.random() * (maxInterval - minInterval); // Dynamic interval based on wave level
self.moveSpeed = 2;
self.note = 0; // Musical note value
self.update = function () {
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
var waveReduction = (currentWave - 1) * 60; // Reduce by 1 second (60 frames) per wave
var minInterval = Math.max(60, 360 - waveReduction); // Minimum 1 second interval
var maxInterval = Math.max(120, 600 - waveReduction); // Minimum 2 second max interval
self.shootInterval = minInterval + Math.random() * (maxInterval - minInterval); // Dynamic interval based on wave level
}
// Add subtle horizontal sway movement for more fluid feel
if (LK.ticks % 180 === 0) {
var sideMovement = (Math.random() - 0.5) * 60;
var targetX = Math.max(50, Math.min(1998, self.x + sideMovement));
tween(self, {
x: targetX
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeInOut
});
}
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 20;
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.takeDamage = function (damage) {
self.health -= damage * rhythmMultiplier;
if (self.health <= 0) {
// Drop system - chance to drop items
var dropChance = Math.random() * 100;
if (dropChance < 2) {
// 2% chance for powerup1
var drop = new Drop('powerup1');
drop.x = self.x;
drop.y = self.y;
drops.push(drop);
game.addChild(drop);
} else if (dropChance < 3) {
// 1% chance for powerup2
var drop = new Drop('powerup2');
drop.x = self.x;
drop.y = self.y;
drops.push(drop);
game.addChild(drop);
} else if (dropChance < 4) {
// 1% chance for lifeup
var drop = new Drop('lifeup');
drop.x = self.x;
drop.y = self.y;
drops.push(drop);
game.addChild(drop);
}
self.destroy();
return true;
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.speed = 8;
self.lives = 3;
self.canShoot = true;
self.shootCooldown = 0;
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
if (self.shootCooldown <= 0) {
self.canShoot = true;
}
}
// Keep player within screen bounds
if (self.x < 60) self.x = 60;
if (self.x > 2048 - 60) self.x = 2048 - 60;
};
self.shoot = function () {
if (!self.canShoot) return false;
self.canShoot = false;
self.shootCooldown = 15;
// Determine number of bullets based on powerup
var bulletCount = 1;
if (currentPowerup === 'powerup1') {
bulletCount = 3;
}
// Create bullets
for (var i = 0; i < bulletCount; i++) {
var bullet = new PlayerBullet(currentPowerup);
if (bulletCount === 1) {
bullet.x = self.x;
} else {
bullet.x = self.x + (i - 1) * 30; // Spread bullets
}
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
// 1 in 20 chance to play rhythmBonus sound
if (Math.random() < 0.05) {
LK.getSound('rhythmBonus').play();
}
// Check if shooting in rhythm
var timeSinceLastBeat = LK.ticks * 16.67 % beatInterval;
var rhythmWindow = beatInterval * 0.15;
if (timeSinceLastBeat < rhythmWindow || timeSinceLastBeat > beatInterval - rhythmWindow) {
rhythmMultiplier = Math.min(rhythmMultiplier + 0.1, 2.0);
} else {
rhythmMultiplier = Math.max(rhythmMultiplier - 0.05, 1.0);
}
return true;
};
return self;
});
var PlayerBullet = Container.expand(function (bulletType) {
var self = Container.call(this);
self.bulletType = bulletType || 'normal';
var assetName = 'playerBullet';
if (self.bulletType === 'powerup1') {
assetName = 'powebullet1';
} else if (self.bulletType === 'powerup2') {
assetName = 'powerbullet2';
}
var bulletGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.damage = 1;
self.piercing = self.bulletType === 'powerup2';
self.update = function () {
self.y += self.speed;
};
return self;
});
var SuperBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('superboss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: 0xff0000
});
self.health = 100;
self.maxHealth = 100;
self.shootTimer = 0;
var waveReduction = (currentWave - 1) * 60; // Reduce by 1 second (60 frames) per wave
var minInterval = Math.max(60, 180 - waveReduction); // Minimum 1 second interval
var maxInterval = Math.max(120, 480 - waveReduction); // Minimum 2 second max interval
self.shootInterval = minInterval + Math.random() * (maxInterval - minInterval); // Dynamic interval based on wave level
self.moveSpeed = 1;
self.moveDirection = 1;
self.update = function () {
// Boss movement - side to side
self.x += self.moveSpeed * self.moveDirection;
if (self.x <= 200 || self.x >= 1848) {
self.moveDirection *= -1;
}
// Boss shooting
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
var waveReduction = (currentWave - 1) * 60; // Reduce by 1 second (60 frames) per wave
var minInterval = Math.max(60, 180 - waveReduction); // Minimum 1 second interval
var maxInterval = Math.max(120, 480 - waveReduction); // Minimum 2 second max interval
self.shootInterval = minInterval + Math.random() * (maxInterval - minInterval); // Dynamic interval based on wave level
}
// Update boss color based on health
var healthPercent = self.health / self.maxHealth;
if (healthPercent > 0.6) {
bossGraphics.tint = 0xff0000; // Red
} else if (healthPercent > 0.3) {
bossGraphics.tint = 0xff8800; // Orange
} else {
bossGraphics.tint = 0xffff00; // Yellow
}
};
self.shoot = function () {
// Shoot multiple bullets in different directions
for (var i = -2; i <= 2; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x + i * 50;
bullet.y = self.y + 50;
bullet.speed = 3 + Math.abs(i) * 0.5;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage * rhythmMultiplier;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000011
});
/****
* Game Code
****/
var gameStarted = false;
var startButton = game.addChild(LK.getAsset('start', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4
}));
startButton.x = 1024; // Center horizontally
startButton.y = 1366; // Center vertically
var player = game.addChild(new Player());
player.x = 1024;
player.y = 2600;
player.visible = false; // Hide player until game starts
var playerBullets = [];
var enemyBullets = [];
var enemies = [];
var currentWave = 1;
var enemiesKilled = 0;
var totalEnemiesInWave = 0;
var waveStarted = false;
var waveDelay = 0;
var rhythmMultiplier = 1.0;
var beatInterval = 500; // milliseconds
var lastBeatTime = 0;
var enemyFormationX = 0;
var enemyFormationDirection = 1;
var enemyDropDistance = 60;
var shouldDropEnemies = false;
var superBoss = null;
var bossActive = false;
var bossDefeated = false;
var drops = [];
var currentPowerup = null;
// Sound management for bonus sounds
var currentBonusSoundName = null;
var currentBonusSoundTimeout = null;
var bonusSounds = ['bonusmusic', 'bonusmusic2', 'bonusmusic3', 'bonusmusic4', 'bonusmusic5'];
function playRandomBonusSound() {
// Only play if no bonus sound is currently playing
if (currentBonusSoundName === null) {
var randomSoundName = bonusSounds[Math.floor(Math.random() * bonusSounds.length)];
var randomSound = LK.getSound(randomSoundName);
currentBonusSoundName = randomSoundName;
randomSound.play();
// Clear any existing timeout
if (currentBonusSoundTimeout !== null) {
LK.clearTimeout(currentBonusSoundTimeout);
}
// Set a longer timeout to ensure sound finishes before allowing new ones
// Using 2500ms to be safe that the sound completes
currentBonusSoundTimeout = LK.setTimeout(function () {
currentBonusSoundName = null;
currentBonusSoundTimeout = null;
}, 2500);
}
}
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 20;
scoreTxt.y = 20;
LK.gui.topLeft.addChild(scoreTxt);
var livesTxt = new Text2('Lives: 3', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(1, 0);
livesTxt.x = -20;
livesTxt.y = 20;
LK.gui.topRight.addChild(livesTxt);
var waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0.5, 0);
waveTxt.x = 0;
waveTxt.y = 20;
LK.gui.top.addChild(waveTxt);
var rhythmTxt = new Text2('Rhythm: 1.0x', {
size: 50,
fill: 0xFFFF00
});
rhythmTxt.anchor.set(0.5, 0);
rhythmTxt.x = 0;
rhythmTxt.y = 100;
LK.gui.top.addChild(rhythmTxt);
function createWave(waveNumber) {
enemies = [];
enemiesKilled = 0;
var rows = 3 + Math.floor(waveNumber / 3);
var cols = 8 + Math.floor(waveNumber / 2);
totalEnemiesInWave = rows * cols;
var startX = (2048 - cols * 100) / 2;
var startY = 200;
for (var row = 0; row < rows; row++) {
for (var col = 0; col < cols; col++) {
var enemy = new Enemy();
enemy.x = startX + col * 100;
enemy.y = startY + row * 80;
enemy.note = (row * cols + col) % 12; // Assign musical notes
enemies.push(enemy);
game.addChild(enemy);
}
}
enemyFormationX = 0;
enemyFormationDirection = 1;
waveStarted = true;
// Increase tempo with each wave
beatInterval = Math.max(300, 500 - waveNumber * 20);
}
function updateEnemyFormation() {
if (enemies.length === 0) return;
var moveDistance = 4 + Math.floor(currentWave / 2);
var shouldMove = LK.ticks % Math.max(1, 20 - currentWave * 1.5) === 0;
if (shouldMove) {
if (shouldDropEnemies) {
// Drop enemies down
for (var i = 0; i < enemies.length; i++) {
enemies[i].y += enemyDropDistance;
}
shouldDropEnemies = false;
} else {
// Move all enemies horizontally in synchronized formation using tween
var hitEdge = false;
var targetDirection = moveDistance * enemyFormationDirection;
// Check if any enemy would hit the edge
for (var i = 0; i < enemies.length; i++) {
var newX = enemies[i].x + targetDirection;
if (newX <= 50 || newX >= 1998) {
hitEdge = true;
break;
}
}
// Move all enemies together with synchronized tween animation
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
tween(enemy, {
x: enemy.x + targetDirection
}, {
duration: 300,
easing: tween.easeInOut
});
}
if (hitEdge) {
enemyFormationDirection *= -1;
shouldDropEnemies = true;
}
}
}
}
function checkCollisions() {
// Player bullets vs enemies
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
var bulletHit = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
enemies.splice(j, 1);
enemiesKilled++;
var baseScore = 100 * Math.floor(rhythmMultiplier);
LK.setScore(LK.getScore() + baseScore);
LK.getSound('enemyHit').play();
// Play random bonus sound effect with 15% chance
if (Math.random() < 0.15) {
playRandomBonusSound();
}
LK.effects.flashObject(enemy, 0xffffff, 200);
}
if (!bullet.piercing) {
bullet.destroy();
playerBullets.splice(i, 1);
bulletHit = true;
break;
} else {
bulletHit = false; // Allow piercing bullets to continue
}
}
}
// Player bullets vs super boss
if (!bulletHit && superBoss && bullet.intersects(superBoss)) {
if (superBoss.takeDamage(bullet.damage)) {
superBoss = null;
bossActive = false;
bossDefeated = true;
var bossScore = 5000 * Math.floor(rhythmMultiplier);
LK.setScore(LK.getScore() + bossScore);
LK.getSound('enemyHit').play();
// Play random bonus sound effect with 15% chance
if (Math.random() < 0.15) {
playRandomBonusSound();
}
}
if (!bullet.piercing) {
bullet.destroy();
playerBullets.splice(i, 1);
bulletHit = true;
}
}
if (!bulletHit && bullet.y < -20) {
bullet.destroy();
playerBullets.splice(i, 1);
}
}
// Player vs drops
for (var i = drops.length - 1; i >= 0; i--) {
var drop = drops[i];
if (drop.intersects(player)) {
if (drop.dropType === 'lifeup') {
player.lives = Math.min(player.lives + 1, 5);
} else {
currentPowerup = drop.dropType;
}
drop.destroy();
drops.splice(i, 1);
} else if (drop.y > 2732) {
drop.destroy();
drops.splice(i, 1);
}
}
// Enemy bullets vs player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.intersects(player)) {
player.lives--;
currentPowerup = null; // Reset powerup when player loses a life
bullet.destroy();
enemyBullets.splice(i, 1);
LK.getSound('playerHit').play();
LK.effects.flashObject(player, 0xff0000, 500);
if (player.lives <= 0) {
LK.showGameOver();
return;
}
} else if (bullet.y > 2732) {
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Check if enemies reached player
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].y > 2500) {
LK.showGameOver();
return;
}
}
// Check if super boss reached player
if (superBoss && superBoss.y > 2500) {
LK.showGameOver();
return;
}
}
function updateUI() {
scoreTxt.setText('Score: ' + LK.getScore());
livesTxt.setText('Lives: ' + player.lives);
waveTxt.setText('Wave: ' + currentWave);
rhythmTxt.setText('Rhythm: ' + rhythmMultiplier.toFixed(1) + 'x');
// Color rhythm multiplier text based on value
if (rhythmMultiplier > 1.5) {
rhythmTxt.tint = 0x00ff00;
} else if (rhythmMultiplier > 1.2) {
rhythmTxt.tint = 0xffff00;
} else {
rhythmTxt.tint = 0xffffff;
}
}
// Touch controls
var dragStartX = 0;
var isDragging = false;
var lastTouchTime = 0;
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
startButton.visible = false; // Hide start button
player.visible = true; // Show player when game starts
// Start background music
LK.playMusic('bgmusic');
// Initialize first wave
createWave(currentWave);
return;
}
isDragging = true;
// Stop any existing tween for immediate response
tween.stop(player, {
x: true
});
// Quick tween to clicked position for responsive feel
tween(player, {
x: x
}, {
duration: 100,
easing: tween.easeOut
});
var currentTime = Date.now();
if (currentTime - lastTouchTime < 300) {
// Double tap to shoot
player.shoot();
}
lastTouchTime = currentTime;
};
game.move = function (x, y, obj) {
if (!gameStarted) return;
// Stop any existing tween to prevent conflicts
tween.stop(player, {
x: true
});
// Calculate distance for dynamic duration
var distance = Math.abs(x - player.x);
// Dynamic duration based on distance - closer targets move faster
var duration = Math.min(Math.max(distance * 0.8, 50), 200);
// Use smooth easing for natural movement
tween(player, {
x: x
}, {
duration: duration,
easing: tween.easeOut
});
};
game.up = function (x, y, obj) {
if (!gameStarted) return;
isDragging = false;
// Shoot on mouse up for better control
player.shoot();
};
game.update = function () {
if (!gameStarted) return;
updateEnemyFormation();
checkCollisions();
updateUI();
// Gradually decay rhythm multiplier
rhythmMultiplier = Math.max(rhythmMultiplier - 0.001, 1.0);
// Check if wave is complete
if (enemies.length === 0 && waveStarted && !bossActive) {
waveStarted = false;
bossActive = true;
bossDefeated = false;
// Spawn super boss
superBoss = new SuperBoss();
superBoss.x = 1024;
superBoss.y = 300;
game.addChild(superBoss);
}
// Check if boss is defeated
if (bossDefeated && !waveStarted) {
waveDelay = 120; // 2 second delay
currentWave++;
bossDefeated = false;
if (currentWave > 10) {
LK.showYouWin();
return;
}
}
// Start next wave after delay
if (!waveStarted && waveDelay > 0) {
waveDelay--;
if (waveDelay <= 0) {
createWave(currentWave);
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Drop = Container.expand(function (dropType) {
var self = Container.call(this);
self.dropType = dropType;
var dropGraphics = self.attachAsset(dropType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.shootTimer = 0;
var waveReduction = (currentWave - 1) * 60; // Reduce by 1 second (60 frames) per wave
var minInterval = Math.max(60, 360 - waveReduction); // Minimum 1 second interval
var maxInterval = Math.max(120, 600 - waveReduction); // Minimum 2 second max interval
self.shootInterval = minInterval + Math.random() * (maxInterval - minInterval); // Dynamic interval based on wave level
self.moveSpeed = 2;
self.note = 0; // Musical note value
self.update = function () {
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
var waveReduction = (currentWave - 1) * 60; // Reduce by 1 second (60 frames) per wave
var minInterval = Math.max(60, 360 - waveReduction); // Minimum 1 second interval
var maxInterval = Math.max(120, 600 - waveReduction); // Minimum 2 second max interval
self.shootInterval = minInterval + Math.random() * (maxInterval - minInterval); // Dynamic interval based on wave level
}
// Add subtle horizontal sway movement for more fluid feel
if (LK.ticks % 180 === 0) {
var sideMovement = (Math.random() - 0.5) * 60;
var targetX = Math.max(50, Math.min(1998, self.x + sideMovement));
tween(self, {
x: targetX
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeInOut
});
}
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 20;
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.takeDamage = function (damage) {
self.health -= damage * rhythmMultiplier;
if (self.health <= 0) {
// Drop system - chance to drop items
var dropChance = Math.random() * 100;
if (dropChance < 2) {
// 2% chance for powerup1
var drop = new Drop('powerup1');
drop.x = self.x;
drop.y = self.y;
drops.push(drop);
game.addChild(drop);
} else if (dropChance < 3) {
// 1% chance for powerup2
var drop = new Drop('powerup2');
drop.x = self.x;
drop.y = self.y;
drops.push(drop);
game.addChild(drop);
} else if (dropChance < 4) {
// 1% chance for lifeup
var drop = new Drop('lifeup');
drop.x = self.x;
drop.y = self.y;
drops.push(drop);
game.addChild(drop);
}
self.destroy();
return true;
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.speed = 8;
self.lives = 3;
self.canShoot = true;
self.shootCooldown = 0;
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
if (self.shootCooldown <= 0) {
self.canShoot = true;
}
}
// Keep player within screen bounds
if (self.x < 60) self.x = 60;
if (self.x > 2048 - 60) self.x = 2048 - 60;
};
self.shoot = function () {
if (!self.canShoot) return false;
self.canShoot = false;
self.shootCooldown = 15;
// Determine number of bullets based on powerup
var bulletCount = 1;
if (currentPowerup === 'powerup1') {
bulletCount = 3;
}
// Create bullets
for (var i = 0; i < bulletCount; i++) {
var bullet = new PlayerBullet(currentPowerup);
if (bulletCount === 1) {
bullet.x = self.x;
} else {
bullet.x = self.x + (i - 1) * 30; // Spread bullets
}
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
// 1 in 20 chance to play rhythmBonus sound
if (Math.random() < 0.05) {
LK.getSound('rhythmBonus').play();
}
// Check if shooting in rhythm
var timeSinceLastBeat = LK.ticks * 16.67 % beatInterval;
var rhythmWindow = beatInterval * 0.15;
if (timeSinceLastBeat < rhythmWindow || timeSinceLastBeat > beatInterval - rhythmWindow) {
rhythmMultiplier = Math.min(rhythmMultiplier + 0.1, 2.0);
} else {
rhythmMultiplier = Math.max(rhythmMultiplier - 0.05, 1.0);
}
return true;
};
return self;
});
var PlayerBullet = Container.expand(function (bulletType) {
var self = Container.call(this);
self.bulletType = bulletType || 'normal';
var assetName = 'playerBullet';
if (self.bulletType === 'powerup1') {
assetName = 'powebullet1';
} else if (self.bulletType === 'powerup2') {
assetName = 'powerbullet2';
}
var bulletGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.damage = 1;
self.piercing = self.bulletType === 'powerup2';
self.update = function () {
self.y += self.speed;
};
return self;
});
var SuperBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('superboss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: 0xff0000
});
self.health = 100;
self.maxHealth = 100;
self.shootTimer = 0;
var waveReduction = (currentWave - 1) * 60; // Reduce by 1 second (60 frames) per wave
var minInterval = Math.max(60, 180 - waveReduction); // Minimum 1 second interval
var maxInterval = Math.max(120, 480 - waveReduction); // Minimum 2 second max interval
self.shootInterval = minInterval + Math.random() * (maxInterval - minInterval); // Dynamic interval based on wave level
self.moveSpeed = 1;
self.moveDirection = 1;
self.update = function () {
// Boss movement - side to side
self.x += self.moveSpeed * self.moveDirection;
if (self.x <= 200 || self.x >= 1848) {
self.moveDirection *= -1;
}
// Boss shooting
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
var waveReduction = (currentWave - 1) * 60; // Reduce by 1 second (60 frames) per wave
var minInterval = Math.max(60, 180 - waveReduction); // Minimum 1 second interval
var maxInterval = Math.max(120, 480 - waveReduction); // Minimum 2 second max interval
self.shootInterval = minInterval + Math.random() * (maxInterval - minInterval); // Dynamic interval based on wave level
}
// Update boss color based on health
var healthPercent = self.health / self.maxHealth;
if (healthPercent > 0.6) {
bossGraphics.tint = 0xff0000; // Red
} else if (healthPercent > 0.3) {
bossGraphics.tint = 0xff8800; // Orange
} else {
bossGraphics.tint = 0xffff00; // Yellow
}
};
self.shoot = function () {
// Shoot multiple bullets in different directions
for (var i = -2; i <= 2; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x + i * 50;
bullet.y = self.y + 50;
bullet.speed = 3 + Math.abs(i) * 0.5;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage * rhythmMultiplier;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000011
});
/****
* Game Code
****/
var gameStarted = false;
var startButton = game.addChild(LK.getAsset('start', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4
}));
startButton.x = 1024; // Center horizontally
startButton.y = 1366; // Center vertically
var player = game.addChild(new Player());
player.x = 1024;
player.y = 2600;
player.visible = false; // Hide player until game starts
var playerBullets = [];
var enemyBullets = [];
var enemies = [];
var currentWave = 1;
var enemiesKilled = 0;
var totalEnemiesInWave = 0;
var waveStarted = false;
var waveDelay = 0;
var rhythmMultiplier = 1.0;
var beatInterval = 500; // milliseconds
var lastBeatTime = 0;
var enemyFormationX = 0;
var enemyFormationDirection = 1;
var enemyDropDistance = 60;
var shouldDropEnemies = false;
var superBoss = null;
var bossActive = false;
var bossDefeated = false;
var drops = [];
var currentPowerup = null;
// Sound management for bonus sounds
var currentBonusSoundName = null;
var currentBonusSoundTimeout = null;
var bonusSounds = ['bonusmusic', 'bonusmusic2', 'bonusmusic3', 'bonusmusic4', 'bonusmusic5'];
function playRandomBonusSound() {
// Only play if no bonus sound is currently playing
if (currentBonusSoundName === null) {
var randomSoundName = bonusSounds[Math.floor(Math.random() * bonusSounds.length)];
var randomSound = LK.getSound(randomSoundName);
currentBonusSoundName = randomSoundName;
randomSound.play();
// Clear any existing timeout
if (currentBonusSoundTimeout !== null) {
LK.clearTimeout(currentBonusSoundTimeout);
}
// Set a longer timeout to ensure sound finishes before allowing new ones
// Using 2500ms to be safe that the sound completes
currentBonusSoundTimeout = LK.setTimeout(function () {
currentBonusSoundName = null;
currentBonusSoundTimeout = null;
}, 2500);
}
}
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 20;
scoreTxt.y = 20;
LK.gui.topLeft.addChild(scoreTxt);
var livesTxt = new Text2('Lives: 3', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(1, 0);
livesTxt.x = -20;
livesTxt.y = 20;
LK.gui.topRight.addChild(livesTxt);
var waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0.5, 0);
waveTxt.x = 0;
waveTxt.y = 20;
LK.gui.top.addChild(waveTxt);
var rhythmTxt = new Text2('Rhythm: 1.0x', {
size: 50,
fill: 0xFFFF00
});
rhythmTxt.anchor.set(0.5, 0);
rhythmTxt.x = 0;
rhythmTxt.y = 100;
LK.gui.top.addChild(rhythmTxt);
function createWave(waveNumber) {
enemies = [];
enemiesKilled = 0;
var rows = 3 + Math.floor(waveNumber / 3);
var cols = 8 + Math.floor(waveNumber / 2);
totalEnemiesInWave = rows * cols;
var startX = (2048 - cols * 100) / 2;
var startY = 200;
for (var row = 0; row < rows; row++) {
for (var col = 0; col < cols; col++) {
var enemy = new Enemy();
enemy.x = startX + col * 100;
enemy.y = startY + row * 80;
enemy.note = (row * cols + col) % 12; // Assign musical notes
enemies.push(enemy);
game.addChild(enemy);
}
}
enemyFormationX = 0;
enemyFormationDirection = 1;
waveStarted = true;
// Increase tempo with each wave
beatInterval = Math.max(300, 500 - waveNumber * 20);
}
function updateEnemyFormation() {
if (enemies.length === 0) return;
var moveDistance = 4 + Math.floor(currentWave / 2);
var shouldMove = LK.ticks % Math.max(1, 20 - currentWave * 1.5) === 0;
if (shouldMove) {
if (shouldDropEnemies) {
// Drop enemies down
for (var i = 0; i < enemies.length; i++) {
enemies[i].y += enemyDropDistance;
}
shouldDropEnemies = false;
} else {
// Move all enemies horizontally in synchronized formation using tween
var hitEdge = false;
var targetDirection = moveDistance * enemyFormationDirection;
// Check if any enemy would hit the edge
for (var i = 0; i < enemies.length; i++) {
var newX = enemies[i].x + targetDirection;
if (newX <= 50 || newX >= 1998) {
hitEdge = true;
break;
}
}
// Move all enemies together with synchronized tween animation
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
tween(enemy, {
x: enemy.x + targetDirection
}, {
duration: 300,
easing: tween.easeInOut
});
}
if (hitEdge) {
enemyFormationDirection *= -1;
shouldDropEnemies = true;
}
}
}
}
function checkCollisions() {
// Player bullets vs enemies
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
var bulletHit = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
enemies.splice(j, 1);
enemiesKilled++;
var baseScore = 100 * Math.floor(rhythmMultiplier);
LK.setScore(LK.getScore() + baseScore);
LK.getSound('enemyHit').play();
// Play random bonus sound effect with 15% chance
if (Math.random() < 0.15) {
playRandomBonusSound();
}
LK.effects.flashObject(enemy, 0xffffff, 200);
}
if (!bullet.piercing) {
bullet.destroy();
playerBullets.splice(i, 1);
bulletHit = true;
break;
} else {
bulletHit = false; // Allow piercing bullets to continue
}
}
}
// Player bullets vs super boss
if (!bulletHit && superBoss && bullet.intersects(superBoss)) {
if (superBoss.takeDamage(bullet.damage)) {
superBoss = null;
bossActive = false;
bossDefeated = true;
var bossScore = 5000 * Math.floor(rhythmMultiplier);
LK.setScore(LK.getScore() + bossScore);
LK.getSound('enemyHit').play();
// Play random bonus sound effect with 15% chance
if (Math.random() < 0.15) {
playRandomBonusSound();
}
}
if (!bullet.piercing) {
bullet.destroy();
playerBullets.splice(i, 1);
bulletHit = true;
}
}
if (!bulletHit && bullet.y < -20) {
bullet.destroy();
playerBullets.splice(i, 1);
}
}
// Player vs drops
for (var i = drops.length - 1; i >= 0; i--) {
var drop = drops[i];
if (drop.intersects(player)) {
if (drop.dropType === 'lifeup') {
player.lives = Math.min(player.lives + 1, 5);
} else {
currentPowerup = drop.dropType;
}
drop.destroy();
drops.splice(i, 1);
} else if (drop.y > 2732) {
drop.destroy();
drops.splice(i, 1);
}
}
// Enemy bullets vs player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.intersects(player)) {
player.lives--;
currentPowerup = null; // Reset powerup when player loses a life
bullet.destroy();
enemyBullets.splice(i, 1);
LK.getSound('playerHit').play();
LK.effects.flashObject(player, 0xff0000, 500);
if (player.lives <= 0) {
LK.showGameOver();
return;
}
} else if (bullet.y > 2732) {
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Check if enemies reached player
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].y > 2500) {
LK.showGameOver();
return;
}
}
// Check if super boss reached player
if (superBoss && superBoss.y > 2500) {
LK.showGameOver();
return;
}
}
function updateUI() {
scoreTxt.setText('Score: ' + LK.getScore());
livesTxt.setText('Lives: ' + player.lives);
waveTxt.setText('Wave: ' + currentWave);
rhythmTxt.setText('Rhythm: ' + rhythmMultiplier.toFixed(1) + 'x');
// Color rhythm multiplier text based on value
if (rhythmMultiplier > 1.5) {
rhythmTxt.tint = 0x00ff00;
} else if (rhythmMultiplier > 1.2) {
rhythmTxt.tint = 0xffff00;
} else {
rhythmTxt.tint = 0xffffff;
}
}
// Touch controls
var dragStartX = 0;
var isDragging = false;
var lastTouchTime = 0;
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
startButton.visible = false; // Hide start button
player.visible = true; // Show player when game starts
// Start background music
LK.playMusic('bgmusic');
// Initialize first wave
createWave(currentWave);
return;
}
isDragging = true;
// Stop any existing tween for immediate response
tween.stop(player, {
x: true
});
// Quick tween to clicked position for responsive feel
tween(player, {
x: x
}, {
duration: 100,
easing: tween.easeOut
});
var currentTime = Date.now();
if (currentTime - lastTouchTime < 300) {
// Double tap to shoot
player.shoot();
}
lastTouchTime = currentTime;
};
game.move = function (x, y, obj) {
if (!gameStarted) return;
// Stop any existing tween to prevent conflicts
tween.stop(player, {
x: true
});
// Calculate distance for dynamic duration
var distance = Math.abs(x - player.x);
// Dynamic duration based on distance - closer targets move faster
var duration = Math.min(Math.max(distance * 0.8, 50), 200);
// Use smooth easing for natural movement
tween(player, {
x: x
}, {
duration: duration,
easing: tween.easeOut
});
};
game.up = function (x, y, obj) {
if (!gameStarted) return;
isDragging = false;
// Shoot on mouse up for better control
player.shoot();
};
game.update = function () {
if (!gameStarted) return;
updateEnemyFormation();
checkCollisions();
updateUI();
// Gradually decay rhythm multiplier
rhythmMultiplier = Math.max(rhythmMultiplier - 0.001, 1.0);
// Check if wave is complete
if (enemies.length === 0 && waveStarted && !bossActive) {
waveStarted = false;
bossActive = true;
bossDefeated = false;
// Spawn super boss
superBoss = new SuperBoss();
superBoss.x = 1024;
superBoss.y = 300;
game.addChild(superBoss);
}
// Check if boss is defeated
if (bossDefeated && !waveStarted) {
waveDelay = 120; // 2 second delay
currentWave++;
bossDefeated = false;
if (currentWave > 10) {
LK.showYouWin();
return;
}
}
// Start next wave after delay
if (!waveStarted && waveDelay > 0) {
waveDelay--;
if (waveDelay <= 0) {
createWave(currentWave);
}
}
};
nave espacial extraterrestre. In-Game asset. 2d. High contrast. No shadows
un bala lazer de nergia. In-Game asset. 2d. High contrast. No shadows
monster space ship. In-Game asset. 2d. High contrast. No shadows
una espada roja con destellos. In-Game asset. 2d. High contrast. No shadows
espada azul con destellos. In-Game asset. 2d. High contrast. No shadows
un rayo lazer con destellos de fuego alrededor. In-Game asset. 2d. High contrast. No shadows
rayo láser azul con fuego azul alrededor. In-Game asset. 2d. High contrast. No shadows
una esfera blanca con destellos con alas de angel a los lados. In-Game asset. 2d. High contrast. No shadows
una imagen que diga "start" en letras grandes con una fuente alusiva a lso que se usan en video juegos de color metal brillante. In-Game asset. 2d. High contrast. No shadows