/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('hotako', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 15,
scaleY: 15
});
self.health = 100;
self.maxHealth = 100;
self.fireTimer = 0;
self.fireRate = 30; // Faster shooting than normal Hotakos
self.speed = 0.5; // Slower movement
self.directionX = 0;
self.directionY = 1;
self.rapidFireCount = 0;
self.rapidFireMax = 5;
self.rapidFireDelay = 10;
self.giantBulletTimer = 0;
self.giantBulletRate = 300; // Fire giant bullet every 5 seconds
self.setDirection = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.takeDamage = function () {
self.health--;
// Flash red when taking damage
LK.effects.flashObject(self, 0xff0000, 100);
return self.health <= 0;
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
self.fireTimer++;
self.giantBulletTimer++;
if (self.fireTimer >= self.fireRate) {
self.fireTimer = 0;
// Rapid fire burst
if (self.rapidFireCount < self.rapidFireMax) {
self.shouldFire = true;
self.rapidFireCount++;
self.fireTimer = -self.rapidFireDelay; // Short delay between rapid shots
} else {
self.rapidFireCount = 0;
}
}
// Giant bullet firing
if (self.giantBulletTimer >= self.giantBulletRate) {
self.giantBulletTimer = 0;
self.shouldFireGiant = true;
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.directionX = 0;
self.directionY = -1; // Default upward direction
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var FastHotako = Container.expand(function () {
var self = Container.call(this);
var hotakoGraphics = self.attachAsset('hotako', {
anchorX: 0.5,
anchorY: 0.5
});
hotakoGraphics.tint = 0x0080ff; // Blue color
self.fireTimer = 0;
self.fireRate = 60; // Faster firing rate
self.speed = currentFastHotakoSpeed; // Use progressive speed
self.directionX = 0;
self.directionY = 1; // Default downward direction
self.setDirection = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
self.fireTimer = 0;
// Signal to create fire projectile
self.shouldFire = true;
}
};
return self;
});
var Fire = Container.expand(function () {
var self = Container.call(this);
var fireGraphics = self.attachAsset('fire', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.directionX = 0;
self.directionY = 1;
self.setDirection = function (targetX, targetY, startX, startY) {
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var GiantBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('fire', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4
});
bulletGraphics.tint = 0xff4444; // Red color for giant bullets
self.speed = 3; // Slower speed
self.directionX = 0;
self.directionY = 1;
self.setDirection = function (targetX, targetY, startX, startY) {
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var GreenBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGraphics.tint = 0x00ff00; // Green color
self.speed = 12;
self.directionX = 0;
self.directionY = -1; // Always shoots upward
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var Hotako = Container.expand(function () {
var self = Container.call(this);
var hotakoGraphics = self.attachAsset('hotako', {
anchorX: 0.5,
anchorY: 0.5
});
self.fireTimer = 0;
self.fireRate = 90; // Frames between shots
self.speed = currentHotakoSpeed; // Use current game speed
self.directionX = 0;
self.directionY = 1; // Default downward direction
self.setDirection = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
self.fireTimer = 0;
// Signal to create fire projectile
self.shouldFire = true;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.moveDirection = 0; // -1 left, 0 stop, 1 right
self.setColor = function (color) {
playerGraphics.tint = color;
};
self.update = function () {
// Smooth movement toward target position (X and Y)
var deltaX = targetX - self.x;
var deltaY = targetY - self.y;
var moveDistanceX = Math.min(Math.abs(deltaX), self.speed);
var moveDistanceY = Math.min(Math.abs(deltaY), self.speed);
if (Math.abs(deltaX) > 5) {
self.x += deltaX > 0 ? moveDistanceX : -moveDistanceX;
}
if (Math.abs(deltaY) > 5) {
self.y += deltaY > 0 ? moveDistanceY : -moveDistanceY;
}
// Keep player within screen bounds
if (self.x < 40) self.x = 40;
if (self.x > 2008) self.x = 2008;
if (self.y < 100) self.y = 100;
if (self.y > 2632) self.y = 2632;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('fire', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
powerupGraphics.tint = 0x00ffff; // Cyan color for powerup
self.collected = false;
self.update = function () {
// Gentle floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
};
return self;
});
var YellowBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('yellowBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.directionX = 0;
self.directionY = -1; // Always shoots upward
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e,
fullscreen: true,
resizeToWindow: true
});
/****
* Game Code
****/
game.setBackgroundColor(0x1a1a2e);
// Game variables
var player;
var hotakos = [];
var fastHotakos = [];
var fires = [];
var bullets = [];
var giantBullets = [];
var greenBullets = [];
var yellowBullets = [];
var spawnTimer = 0;
var spawnRate = 180; // Frames between spawns
var fastSpawnTimer = 0;
var fastSpawnRate = 150; // Frames between fast hotako spawns (more frequent)
var gameTime = 0;
var difficultyLevel = 1;
var hotakosKilled = 0;
var bossesKilled = 0;
var currentBoss = null;
var bossSpawnTimer = 0;
var normalSpawnMultiplier = 1; // How many hotakos to spawn at once
var baseHotakoSpeed = 3; // Initial speed for first Hotako
var speedIncrement = 3; // Speed increase per kill
var currentHotakoSpeed = baseHotakoSpeed; // Current speed for new Hotakos
var baseFastHotakoSpeed = 6; // Base speed for fast Hotakos
var fastHotakosKilled = 0; // Track fast Hotako kills separately
var currentFastHotakoSpeed = baseFastHotakoSpeed; // Current speed for new fast Hotakos
var powerups = [];
var hasLaserPower = false;
var laserDuration = 118 * 60; // 118 seconds in frames for boss powerups
var hotakoLaserDuration = 10 * 60; // 10 seconds in frames for hotako powerups
var laserCooldown = 15 * 60; // 15 seconds cooldown in frames
var laserTimer = 0;
var laserCooldownTimer = 0;
var maxBulletsOnScreen = 30; // Max bullets when using hotako powerup
var isBossPowerup = false; // Track if current powerup is from boss or hotako
var pauseTimer = 0;
var pauseDuration = 3 * 60; // 3 second pause after boss kill
var isPaused = false;
// UI Elements
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var timeTxt = new Text2('Time: 0', {
size: 80,
fill: 0xFFDD44
});
timeTxt.anchor.set(0, 0);
timeTxt.x = 200;
timeTxt.y = 40;
LK.gui.topLeft.addChild(timeTxt);
var killCountTxt = new Text2('Hotakos: 0', {
size: 80,
fill: 0xFF6644
});
killCountTxt.anchor.set(0, 0);
killCountTxt.x = 200;
killCountTxt.y = 140;
LK.gui.topLeft.addChild(killCountTxt);
var laserCooldownTxt = new Text2('Laser Ready', {
size: 80,
fill: 0x00ff00
});
laserCooldownTxt.anchor.set(0, 0);
laserCooldownTxt.x = 200;
laserCooldownTxt.y = 240;
LK.gui.topLeft.addChild(laserCooldownTxt);
// Initialize player
player = game.addChild(new Player());
player.x = 1024;
player.y = 2500;
// Load player customization
var playerColor = storage.playerColor || 0x4a90e2;
player.setColor(playerColor);
// Mouse following controls
var targetX = 1024; // Initial target position
var targetY = 2500; // Initial target Y position
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
};
var isMouseDown = false;
game.down = function (x, y, obj) {
isMouseDown = true;
if (obj.event && obj.event.button === 2) {
// Right click - create yellow bullet that shoots upward
var newYellowBullet = new YellowBullet();
newYellowBullet.x = player.x;
newYellowBullet.y = player.y - 40;
yellowBullets.push(newYellowBullet);
game.addChild(newYellowBullet);
LK.getSound('playerShot').play();
} else {
// Left click - create normal bullet toward cursor
var newBullet = new Bullet();
newBullet.x = player.x;
newBullet.y = player.y - 40;
// Calculate direction toward mouse cursor
var dx = x - player.x;
var dy = y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
newBullet.directionX = dx / distance;
newBullet.directionY = dy / distance;
} else {
newBullet.directionX = 0;
newBullet.directionY = -1; // Default upward
}
bullets.push(newBullet);
game.addChild(newBullet);
LK.getSound('playerShot').play();
}
};
game.up = function (x, y, obj) {
isMouseDown = false;
};
game.update = function () {
gameTime++;
// Handle pause after boss kill
if (isPaused) {
pauseTimer++;
if (pauseTimer >= pauseDuration) {
isPaused = false;
pauseTimer = 0;
}
return; // Skip all game updates during pause
}
// Update difficulty every 10 seconds (600 frames)
if (gameTime % 600 === 0) {
difficultyLevel++;
if (spawnRate > 60) spawnRate -= 10;
}
// Update time display
var seconds = Math.floor(gameTime / 60);
timeTxt.setText('Time: ' + seconds);
// Update kill counter display
var totalKills = hotakosKilled + fastHotakosKilled + bossesKilled * 10;
killCountTxt.setText('Hotakos: ' + totalKills);
// Update laser timers and UI
if (laserTimer > 0) {
laserTimer--;
if (laserTimer <= 0) {
hasLaserPower = false;
isBossPowerup = false; // Reset powerup type
}
}
if (laserCooldownTimer > 0) {
laserCooldownTimer--;
}
// Update laser power display
if (!hasLaserPower) {
laserCooldownTxt.setText('No Laser Power');
laserCooldownTxt.tint = 0xff4444;
} else {
var remainingSeconds = Math.ceil(laserTimer / 60);
laserCooldownTxt.setText('Laser Time: ' + remainingSeconds + 's');
laserCooldownTxt.tint = 0x00ff00;
}
// Update powerups and check collection
for (var p = powerups.length - 1; p >= 0; p--) {
var powerup = powerups[p];
if (powerup.intersects(player)) {
// Powerup collected
hasLaserPower = true;
// Check if this powerup has a special property to identify its type
if (powerup.isBossPowerup) {
// This is a boss powerup
laserTimer = laserDuration; // 118 seconds
isBossPowerup = true;
} else {
// This is a hotako powerup
laserTimer = hotakoLaserDuration; // 10 seconds
isBossPowerup = false;
}
laserCooldownTimer = 0;
powerup.destroy();
powerups.splice(p, 1);
LK.effects.flashScreen(0x00ffff, 300);
}
}
// Check if boss should spawn (every 10 total hotako kills)
var totalHotakoKills = hotakosKilled + fastHotakosKilled;
if (totalHotakoKills >= 10 && currentBoss === null) {
// Spawn boss
currentBoss = new Boss();
currentBoss.x = 1024; // Center top
currentBoss.y = 200;
currentBoss.setDirection(player.x, player.y);
game.addChild(currentBoss);
// Update spawn multiplier based on bosses killed
if (bossesKilled === 0) {
normalSpawnMultiplier = 1;
} else if (bossesKilled === 1) {
normalSpawnMultiplier = 2;
} else {
normalSpawnMultiplier = 3;
}
// Reset kill counter and speed progression
hotakosKilled = 0;
fastHotakosKilled = 0;
currentHotakoSpeed = baseHotakoSpeed; // Reset speed for new cycle
currentFastHotakoSpeed = baseFastHotakoSpeed; // Reset fast speed for new cycle
}
// Spawn normal Hotakos (multiple based on difficulty)
spawnTimer++;
if (spawnTimer >= spawnRate && currentBoss === null) {
spawnTimer = 0;
// Spawn multiple Hotakos based on current multiplier
for (var spawnCount = 0; spawnCount < normalSpawnMultiplier; spawnCount++) {
var newHotako = new Hotako();
// Random spawn position from all four sides
var spawnSide = Math.floor(Math.random() * 4);
if (spawnSide === 0) {
// Top
newHotako.x = Math.random() * 1800 + 100;
newHotako.y = -50;
} else if (spawnSide === 1) {
// Bottom
newHotako.x = Math.random() * 1800 + 100;
newHotako.y = 2782;
} else if (spawnSide === 2) {
// Left
newHotako.x = -50;
newHotako.y = Math.random() * 2500 + 100;
} else {
// Right
newHotako.x = 2098;
newHotako.y = Math.random() * 2500 + 100;
}
// Set direction toward player
newHotako.setDirection(player.x, player.y);
hotakos.push(newHotako);
game.addChild(newHotako);
}
}
// Spawn fast blue Hotakos randomly
fastSpawnTimer++;
if (fastSpawnTimer >= fastSpawnRate && currentBoss === null) {
fastSpawnTimer = 0;
// 30% chance to spawn a fast hotako
if (Math.random() < 0.3) {
var newFastHotako = new FastHotako();
// Random spawn position from all four sides
var spawnSide = Math.floor(Math.random() * 4);
if (spawnSide === 0) {
// Top
newFastHotako.x = Math.random() * 1800 + 100;
newFastHotako.y = -50;
} else if (spawnSide === 1) {
// Bottom
newFastHotako.x = Math.random() * 1800 + 100;
newFastHotako.y = 2782;
} else if (spawnSide === 2) {
// Left
newFastHotako.x = -50;
newFastHotako.y = Math.random() * 2500 + 100;
} else {
// Right
newFastHotako.x = 2098;
newFastHotako.y = Math.random() * 2500 + 100;
}
// Set direction toward player
newFastHotako.setDirection(player.x, player.y);
fastHotakos.push(newFastHotako);
game.addChild(newFastHotako);
}
}
// Update Boss
if (currentBoss !== null) {
// Check if boss should fire regular bullets
if (currentBoss.shouldFire) {
currentBoss.shouldFire = false;
var newFire = new Fire();
newFire.x = currentBoss.x;
newFire.y = currentBoss.y;
newFire.setDirection(player.x, player.y, currentBoss.x, currentBoss.y);
fires.push(newFire);
game.addChild(newFire);
LK.getSound('fireShot').play();
}
// Check if boss should fire giant bullet
if (currentBoss.shouldFireGiant) {
currentBoss.shouldFireGiant = false;
var newGiantBullet = new GiantBullet();
newGiantBullet.x = currentBoss.x;
newGiantBullet.y = currentBoss.y;
newGiantBullet.setDirection(player.x, player.y, currentBoss.x, currentBoss.y);
giantBullets.push(newGiantBullet);
game.addChild(newGiantBullet);
LK.getSound('fireShot').play();
}
// Keep boss on screen and make it move around
if (currentBoss.x < 150) currentBoss.x = 150;
if (currentBoss.x > 1898) currentBoss.x = 1898;
if (currentBoss.y < 100) currentBoss.y = 100;
if (currentBoss.y > 800) currentBoss.y = 800;
}
// Update Hotakos and create fires
for (var h = hotakos.length - 1; h >= 0; h--) {
var hotako = hotakos[h];
// Check if Hotako should fire
if (hotako.shouldFire) {
hotako.shouldFire = false;
var newFire = new Fire();
newFire.x = hotako.x;
newFire.y = hotako.y;
newFire.setDirection(player.x, player.y, hotako.x, hotako.y);
fires.push(newFire);
game.addChild(newFire);
LK.getSound('fireShot').play();
}
// Remove off-screen Hotakos (expanded bounds for all directions)
if (hotako.y > 2800 || hotako.y < -100 || hotako.x < -100 || hotako.x > 2148) {
hotako.destroy();
hotakos.splice(h, 1);
}
}
// Update Fast Hotakos and create fires
for (var fh = fastHotakos.length - 1; fh >= 0; fh--) {
var fastHotako = fastHotakos[fh];
// Check if FastHotako should fire
if (fastHotako.shouldFire) {
fastHotako.shouldFire = false;
var newFire = new Fire();
newFire.x = fastHotako.x;
newFire.y = fastHotako.y;
newFire.setDirection(player.x, player.y, fastHotako.x, fastHotako.y);
fires.push(newFire);
game.addChild(newFire);
LK.getSound('fireShot').play();
}
// Remove off-screen FastHotakos (expanded bounds for all directions)
if (fastHotako.y > 2800 || fastHotako.y < -100 || fastHotako.x < -100 || fastHotako.x > 2148) {
fastHotako.destroy();
fastHotakos.splice(fh, 1);
}
}
// Update fires and check collisions
for (var f = fires.length - 1; f >= 0; f--) {
var fire = fires[f];
// Check collision with player
if (fire.intersects(player)) {
// Game Over
LK.effects.flashScreen(0xff0000, 1000);
// Save score
var finalScore = Math.floor(gameTime / 60) * 10 + Math.floor(gameTime / 10);
LK.setScore(finalScore);
LK.showGameOver();
return;
}
// Remove off-screen fires
if (fire.y > 2800 || fire.y < -50 || fire.x < -50 || fire.x > 2098) {
fire.destroy();
fires.splice(f, 1);
}
}
// Update giant bullets and check collisions
for (var gb = giantBullets.length - 1; gb >= 0; gb--) {
var giantBullet = giantBullets[gb];
// Check collision with player
if (giantBullet.intersects(player)) {
// Game Over
LK.effects.flashScreen(0xff0000, 1000);
// Save score
var finalScore = Math.floor(gameTime / 60) * 10 + Math.floor(gameTime / 10);
LK.setScore(finalScore);
LK.showGameOver();
return;
}
// Remove off-screen giant bullets
if (giantBullet.y > 2800 || giantBullet.y < -50 || giantBullet.x < -50 || giantBullet.x > 2098) {
giantBullet.destroy();
giantBullets.splice(gb, 1);
}
}
// Update green bullets and check collisions
for (var grb = greenBullets.length - 1; grb >= 0; grb--) {
var greenBullet = greenBullets[grb];
var hitTarget = false;
// Check collision with Boss
if (currentBoss !== null && greenBullet.intersects(currentBoss)) {
greenBullet.destroy();
greenBullets.splice(grb, 1);
hitTarget = true;
if (currentBoss.takeDamage()) {
// Boss defeated
var bossX = currentBoss.x;
var bossY = currentBoss.y;
currentBoss.destroy();
currentBoss = null;
bossesKilled++;
LK.setScore(LK.getScore() + 500);
LK.effects.flashScreen(0x00ff00, 500);
// Spawn powerup at boss location
var newPowerup = new PowerUp();
newPowerup.x = bossX;
newPowerup.y = bossY;
newPowerup.isBossPowerup = true; // Mark as boss powerup
powerups.push(newPowerup);
game.addChild(newPowerup);
// Start pause
isPaused = true;
pauseTimer = 0;
}
}
// Check collision with normal Hotakos
if (!hitTarget) {
for (var h = hotakos.length - 1; h >= 0; h--) {
var hotako = hotakos[h];
if (greenBullet.intersects(hotako)) {
greenBullet.destroy();
greenBullets.splice(grb, 1);
hotako.destroy();
hotakos.splice(h, 1);
LK.setScore(LK.getScore() + 50);
hotakosKilled++;
currentHotakoSpeed = baseHotakoSpeed;
hitTarget = true;
LK.effects.flashObject(hotako, 0xffffff, 100);
break;
}
}
}
// Check collision with fast Hotakos
if (!hitTarget) {
for (var fh = fastHotakos.length - 1; fh >= 0; fh--) {
var fastHotako = fastHotakos[fh];
if (greenBullet.intersects(fastHotako)) {
greenBullet.destroy();
greenBullets.splice(grb, 1);
fastHotako.destroy();
fastHotakos.splice(fh, 1);
LK.setScore(LK.getScore() + 75);
fastHotakosKilled++;
currentFastHotakoSpeed = baseFastHotakoSpeed + fastHotakosKilled * speedIncrement;
hitTarget = true;
LK.effects.flashObject(fastHotako, 0x0080ff, 100);
break;
}
}
}
// Remove off-screen green bullets
if (!hitTarget && greenBullet.y < -50) {
greenBullet.destroy();
greenBullets.splice(grb, 1);
}
}
// Update yellow bullets and check collisions
for (var yb = yellowBullets.length - 1; yb >= 0; yb--) {
var yellowBullet = yellowBullets[yb];
var hitTarget = false;
// Check collision with Boss
if (currentBoss !== null && yellowBullet.intersects(currentBoss)) {
yellowBullet.destroy();
yellowBullets.splice(yb, 1);
hitTarget = true;
if (currentBoss.takeDamage()) {
// Boss defeated
var bossX = currentBoss.x;
var bossY = currentBoss.y;
currentBoss.destroy();
currentBoss = null;
bossesKilled++;
LK.setScore(LK.getScore() + 500);
LK.effects.flashScreen(0x00ff00, 500);
// Spawn powerup at boss location
var newPowerup = new PowerUp();
newPowerup.x = bossX;
newPowerup.y = bossY;
newPowerup.isBossPowerup = true; // Mark as boss powerup
powerups.push(newPowerup);
game.addChild(newPowerup);
// Start pause
isPaused = true;
pauseTimer = 0;
}
}
// Check collision with normal Hotakos
if (!hitTarget) {
for (var h = hotakos.length - 1; h >= 0; h--) {
var hotako = hotakos[h];
if (yellowBullet.intersects(hotako)) {
yellowBullet.destroy();
yellowBullets.splice(yb, 1);
hotako.destroy();
hotakos.splice(h, 1);
LK.setScore(LK.getScore() + 50);
hotakosKilled++;
currentHotakoSpeed = baseHotakoSpeed;
hitTarget = true;
LK.effects.flashObject(hotako, 0xffffff, 100);
break;
}
}
}
// Check collision with fast Hotakos
if (!hitTarget) {
for (var fh = fastHotakos.length - 1; fh >= 0; fh--) {
var fastHotako = fastHotakos[fh];
if (yellowBullet.intersects(fastHotako)) {
yellowBullet.destroy();
yellowBullets.splice(yb, 1);
fastHotako.destroy();
fastHotakos.splice(fh, 1);
LK.setScore(LK.getScore() + 75);
fastHotakosKilled++;
currentFastHotakoSpeed = baseFastHotakoSpeed + fastHotakosKilled * speedIncrement;
hitTarget = true;
LK.effects.flashObject(fastHotako, 0x0080ff, 100);
break;
}
}
}
// Remove off-screen yellow bullets
if (!hitTarget && yellowBullet.y < -50) {
yellowBullet.destroy();
yellowBullets.splice(yb, 1);
}
}
// Rapid fire shooting when laser power is active and mouse is held
if (hasLaserPower && laserTimer > 0 && isMouseDown) {
// Check bullet limit for hotako powerups only, boss powerups have no limit
var canShoot = true;
if (!isBossPowerup && bullets.length >= maxBulletsOnScreen) {
canShoot = false;
}
if (canShoot) {
// Shoot bullets every frame for maximum rapid fire
var rapidBullet = new Bullet();
rapidBullet.x = player.x;
rapidBullet.y = player.y - 40;
// Shoot toward current mouse position
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
rapidBullet.directionX = dx / distance;
rapidBullet.directionY = dy / distance;
} else {
rapidBullet.directionX = 0;
rapidBullet.directionY = -1;
}
rapidBullet.speed = 15; // Fast bullets
bullets.push(rapidBullet);
game.addChild(rapidBullet);
}
}
// Update bullets and check collisions with Hotakos and Boss
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
var hitTarget = false;
// Check collision with Boss first
if (currentBoss !== null && bullet.intersects(currentBoss)) {
bullet.destroy();
bullets.splice(b, 1);
hitTarget = true;
if (currentBoss.takeDamage()) {
// Boss defeated
var bossX = currentBoss.x;
var bossY = currentBoss.y;
currentBoss.destroy();
currentBoss = null;
bossesKilled++;
LK.setScore(LK.getScore() + 500); // Big score for boss
LK.effects.flashScreen(0x00ff00, 500); // Green flash for victory
// Spawn powerup at boss location
var newPowerup = new PowerUp();
newPowerup.x = bossX;
newPowerup.y = bossY;
newPowerup.isBossPowerup = true; // Mark as boss powerup
powerups.push(newPowerup);
game.addChild(newPowerup);
// Start pause
isPaused = true;
pauseTimer = 0;
}
}
// Check collision with normal Hotakos
if (!hitTarget) {
for (var h = hotakos.length - 1; h >= 0; h--) {
var hotako = hotakos[h];
if (bullet.intersects(hotako)) {
// Hotako hit - destroy both bullet and Hotako
var hotakoX = hotako.x;
var hotakoY = hotako.y;
bullet.destroy();
bullets.splice(b, 1);
hotako.destroy();
hotakos.splice(h, 1);
// Add score for killing Hotako
LK.setScore(LK.getScore() + 50);
hotakosKilled++;
// Keep normal Hotakos at base speed - no speed increase
currentHotakoSpeed = baseHotakoSpeed;
// 10% chance to spawn powerup from normal hotako
if (Math.random() < 0.1) {
var newPowerup = new PowerUp();
newPowerup.x = hotakoX;
newPowerup.y = hotakoY;
newPowerup.isBossPowerup = false; // Mark as hotako powerup
powerups.push(newPowerup);
game.addChild(newPowerup);
}
hitTarget = true;
LK.effects.flashObject(hotako, 0xffffff, 100);
break;
}
}
}
// Check collision with fast Hotakos
if (!hitTarget) {
for (var fh = fastHotakos.length - 1; fh >= 0; fh--) {
var fastHotako = fastHotakos[fh];
if (bullet.intersects(fastHotako)) {
// FastHotako hit - destroy both bullet and FastHotako
bullet.destroy();
bullets.splice(b, 1);
fastHotako.destroy();
fastHotakos.splice(fh, 1);
// Add higher score for killing FastHotako
LK.setScore(LK.getScore() + 75);
fastHotakosKilled++;
// Increase speed for next fast Hotakos
currentFastHotakoSpeed = baseFastHotakoSpeed + fastHotakosKilled * speedIncrement;
hitTarget = true;
LK.effects.flashObject(fastHotako, 0x0080ff, 100);
break;
}
}
}
// Remove off-screen bullets if not already removed
if (!hitTarget && (bullet.y < -50 || bullet.x < -50 || bullet.x > 2098)) {
bullet.destroy();
bullets.splice(b, 1);
}
}
// Update score based on survival time and dodges
var currentScore = Math.floor(gameTime / 60) * 10 + Math.floor(gameTime / 10);
LK.setScore(currentScore);
scoreTxt.setText(LK.getScore().toString());
// Create particle effects occasionally
if (gameTime % 120 === 0) {
LK.effects.flashObject(player, 0xffd700, 200);
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('hotako', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 15,
scaleY: 15
});
self.health = 100;
self.maxHealth = 100;
self.fireTimer = 0;
self.fireRate = 30; // Faster shooting than normal Hotakos
self.speed = 0.5; // Slower movement
self.directionX = 0;
self.directionY = 1;
self.rapidFireCount = 0;
self.rapidFireMax = 5;
self.rapidFireDelay = 10;
self.giantBulletTimer = 0;
self.giantBulletRate = 300; // Fire giant bullet every 5 seconds
self.setDirection = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.takeDamage = function () {
self.health--;
// Flash red when taking damage
LK.effects.flashObject(self, 0xff0000, 100);
return self.health <= 0;
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
self.fireTimer++;
self.giantBulletTimer++;
if (self.fireTimer >= self.fireRate) {
self.fireTimer = 0;
// Rapid fire burst
if (self.rapidFireCount < self.rapidFireMax) {
self.shouldFire = true;
self.rapidFireCount++;
self.fireTimer = -self.rapidFireDelay; // Short delay between rapid shots
} else {
self.rapidFireCount = 0;
}
}
// Giant bullet firing
if (self.giantBulletTimer >= self.giantBulletRate) {
self.giantBulletTimer = 0;
self.shouldFireGiant = true;
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.directionX = 0;
self.directionY = -1; // Default upward direction
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var FastHotako = Container.expand(function () {
var self = Container.call(this);
var hotakoGraphics = self.attachAsset('hotako', {
anchorX: 0.5,
anchorY: 0.5
});
hotakoGraphics.tint = 0x0080ff; // Blue color
self.fireTimer = 0;
self.fireRate = 60; // Faster firing rate
self.speed = currentFastHotakoSpeed; // Use progressive speed
self.directionX = 0;
self.directionY = 1; // Default downward direction
self.setDirection = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
self.fireTimer = 0;
// Signal to create fire projectile
self.shouldFire = true;
}
};
return self;
});
var Fire = Container.expand(function () {
var self = Container.call(this);
var fireGraphics = self.attachAsset('fire', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.directionX = 0;
self.directionY = 1;
self.setDirection = function (targetX, targetY, startX, startY) {
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var GiantBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('fire', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4
});
bulletGraphics.tint = 0xff4444; // Red color for giant bullets
self.speed = 3; // Slower speed
self.directionX = 0;
self.directionY = 1;
self.setDirection = function (targetX, targetY, startX, startY) {
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var GreenBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGraphics.tint = 0x00ff00; // Green color
self.speed = 12;
self.directionX = 0;
self.directionY = -1; // Always shoots upward
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
var Hotako = Container.expand(function () {
var self = Container.call(this);
var hotakoGraphics = self.attachAsset('hotako', {
anchorX: 0.5,
anchorY: 0.5
});
self.fireTimer = 0;
self.fireRate = 90; // Frames between shots
self.speed = currentHotakoSpeed; // Use current game speed
self.directionX = 0;
self.directionY = 1; // Default downward direction
self.setDirection = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
self.fireTimer = 0;
// Signal to create fire projectile
self.shouldFire = true;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.moveDirection = 0; // -1 left, 0 stop, 1 right
self.setColor = function (color) {
playerGraphics.tint = color;
};
self.update = function () {
// Smooth movement toward target position (X and Y)
var deltaX = targetX - self.x;
var deltaY = targetY - self.y;
var moveDistanceX = Math.min(Math.abs(deltaX), self.speed);
var moveDistanceY = Math.min(Math.abs(deltaY), self.speed);
if (Math.abs(deltaX) > 5) {
self.x += deltaX > 0 ? moveDistanceX : -moveDistanceX;
}
if (Math.abs(deltaY) > 5) {
self.y += deltaY > 0 ? moveDistanceY : -moveDistanceY;
}
// Keep player within screen bounds
if (self.x < 40) self.x = 40;
if (self.x > 2008) self.x = 2008;
if (self.y < 100) self.y = 100;
if (self.y > 2632) self.y = 2632;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('fire', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
powerupGraphics.tint = 0x00ffff; // Cyan color for powerup
self.collected = false;
self.update = function () {
// Gentle floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
};
return self;
});
var YellowBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('yellowBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.directionX = 0;
self.directionY = -1; // Always shoots upward
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e,
fullscreen: true,
resizeToWindow: true
});
/****
* Game Code
****/
game.setBackgroundColor(0x1a1a2e);
// Game variables
var player;
var hotakos = [];
var fastHotakos = [];
var fires = [];
var bullets = [];
var giantBullets = [];
var greenBullets = [];
var yellowBullets = [];
var spawnTimer = 0;
var spawnRate = 180; // Frames between spawns
var fastSpawnTimer = 0;
var fastSpawnRate = 150; // Frames between fast hotako spawns (more frequent)
var gameTime = 0;
var difficultyLevel = 1;
var hotakosKilled = 0;
var bossesKilled = 0;
var currentBoss = null;
var bossSpawnTimer = 0;
var normalSpawnMultiplier = 1; // How many hotakos to spawn at once
var baseHotakoSpeed = 3; // Initial speed for first Hotako
var speedIncrement = 3; // Speed increase per kill
var currentHotakoSpeed = baseHotakoSpeed; // Current speed for new Hotakos
var baseFastHotakoSpeed = 6; // Base speed for fast Hotakos
var fastHotakosKilled = 0; // Track fast Hotako kills separately
var currentFastHotakoSpeed = baseFastHotakoSpeed; // Current speed for new fast Hotakos
var powerups = [];
var hasLaserPower = false;
var laserDuration = 118 * 60; // 118 seconds in frames for boss powerups
var hotakoLaserDuration = 10 * 60; // 10 seconds in frames for hotako powerups
var laserCooldown = 15 * 60; // 15 seconds cooldown in frames
var laserTimer = 0;
var laserCooldownTimer = 0;
var maxBulletsOnScreen = 30; // Max bullets when using hotako powerup
var isBossPowerup = false; // Track if current powerup is from boss or hotako
var pauseTimer = 0;
var pauseDuration = 3 * 60; // 3 second pause after boss kill
var isPaused = false;
// UI Elements
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var timeTxt = new Text2('Time: 0', {
size: 80,
fill: 0xFFDD44
});
timeTxt.anchor.set(0, 0);
timeTxt.x = 200;
timeTxt.y = 40;
LK.gui.topLeft.addChild(timeTxt);
var killCountTxt = new Text2('Hotakos: 0', {
size: 80,
fill: 0xFF6644
});
killCountTxt.anchor.set(0, 0);
killCountTxt.x = 200;
killCountTxt.y = 140;
LK.gui.topLeft.addChild(killCountTxt);
var laserCooldownTxt = new Text2('Laser Ready', {
size: 80,
fill: 0x00ff00
});
laserCooldownTxt.anchor.set(0, 0);
laserCooldownTxt.x = 200;
laserCooldownTxt.y = 240;
LK.gui.topLeft.addChild(laserCooldownTxt);
// Initialize player
player = game.addChild(new Player());
player.x = 1024;
player.y = 2500;
// Load player customization
var playerColor = storage.playerColor || 0x4a90e2;
player.setColor(playerColor);
// Mouse following controls
var targetX = 1024; // Initial target position
var targetY = 2500; // Initial target Y position
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
};
var isMouseDown = false;
game.down = function (x, y, obj) {
isMouseDown = true;
if (obj.event && obj.event.button === 2) {
// Right click - create yellow bullet that shoots upward
var newYellowBullet = new YellowBullet();
newYellowBullet.x = player.x;
newYellowBullet.y = player.y - 40;
yellowBullets.push(newYellowBullet);
game.addChild(newYellowBullet);
LK.getSound('playerShot').play();
} else {
// Left click - create normal bullet toward cursor
var newBullet = new Bullet();
newBullet.x = player.x;
newBullet.y = player.y - 40;
// Calculate direction toward mouse cursor
var dx = x - player.x;
var dy = y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
newBullet.directionX = dx / distance;
newBullet.directionY = dy / distance;
} else {
newBullet.directionX = 0;
newBullet.directionY = -1; // Default upward
}
bullets.push(newBullet);
game.addChild(newBullet);
LK.getSound('playerShot').play();
}
};
game.up = function (x, y, obj) {
isMouseDown = false;
};
game.update = function () {
gameTime++;
// Handle pause after boss kill
if (isPaused) {
pauseTimer++;
if (pauseTimer >= pauseDuration) {
isPaused = false;
pauseTimer = 0;
}
return; // Skip all game updates during pause
}
// Update difficulty every 10 seconds (600 frames)
if (gameTime % 600 === 0) {
difficultyLevel++;
if (spawnRate > 60) spawnRate -= 10;
}
// Update time display
var seconds = Math.floor(gameTime / 60);
timeTxt.setText('Time: ' + seconds);
// Update kill counter display
var totalKills = hotakosKilled + fastHotakosKilled + bossesKilled * 10;
killCountTxt.setText('Hotakos: ' + totalKills);
// Update laser timers and UI
if (laserTimer > 0) {
laserTimer--;
if (laserTimer <= 0) {
hasLaserPower = false;
isBossPowerup = false; // Reset powerup type
}
}
if (laserCooldownTimer > 0) {
laserCooldownTimer--;
}
// Update laser power display
if (!hasLaserPower) {
laserCooldownTxt.setText('No Laser Power');
laserCooldownTxt.tint = 0xff4444;
} else {
var remainingSeconds = Math.ceil(laserTimer / 60);
laserCooldownTxt.setText('Laser Time: ' + remainingSeconds + 's');
laserCooldownTxt.tint = 0x00ff00;
}
// Update powerups and check collection
for (var p = powerups.length - 1; p >= 0; p--) {
var powerup = powerups[p];
if (powerup.intersects(player)) {
// Powerup collected
hasLaserPower = true;
// Check if this powerup has a special property to identify its type
if (powerup.isBossPowerup) {
// This is a boss powerup
laserTimer = laserDuration; // 118 seconds
isBossPowerup = true;
} else {
// This is a hotako powerup
laserTimer = hotakoLaserDuration; // 10 seconds
isBossPowerup = false;
}
laserCooldownTimer = 0;
powerup.destroy();
powerups.splice(p, 1);
LK.effects.flashScreen(0x00ffff, 300);
}
}
// Check if boss should spawn (every 10 total hotako kills)
var totalHotakoKills = hotakosKilled + fastHotakosKilled;
if (totalHotakoKills >= 10 && currentBoss === null) {
// Spawn boss
currentBoss = new Boss();
currentBoss.x = 1024; // Center top
currentBoss.y = 200;
currentBoss.setDirection(player.x, player.y);
game.addChild(currentBoss);
// Update spawn multiplier based on bosses killed
if (bossesKilled === 0) {
normalSpawnMultiplier = 1;
} else if (bossesKilled === 1) {
normalSpawnMultiplier = 2;
} else {
normalSpawnMultiplier = 3;
}
// Reset kill counter and speed progression
hotakosKilled = 0;
fastHotakosKilled = 0;
currentHotakoSpeed = baseHotakoSpeed; // Reset speed for new cycle
currentFastHotakoSpeed = baseFastHotakoSpeed; // Reset fast speed for new cycle
}
// Spawn normal Hotakos (multiple based on difficulty)
spawnTimer++;
if (spawnTimer >= spawnRate && currentBoss === null) {
spawnTimer = 0;
// Spawn multiple Hotakos based on current multiplier
for (var spawnCount = 0; spawnCount < normalSpawnMultiplier; spawnCount++) {
var newHotako = new Hotako();
// Random spawn position from all four sides
var spawnSide = Math.floor(Math.random() * 4);
if (spawnSide === 0) {
// Top
newHotako.x = Math.random() * 1800 + 100;
newHotako.y = -50;
} else if (spawnSide === 1) {
// Bottom
newHotako.x = Math.random() * 1800 + 100;
newHotako.y = 2782;
} else if (spawnSide === 2) {
// Left
newHotako.x = -50;
newHotako.y = Math.random() * 2500 + 100;
} else {
// Right
newHotako.x = 2098;
newHotako.y = Math.random() * 2500 + 100;
}
// Set direction toward player
newHotako.setDirection(player.x, player.y);
hotakos.push(newHotako);
game.addChild(newHotako);
}
}
// Spawn fast blue Hotakos randomly
fastSpawnTimer++;
if (fastSpawnTimer >= fastSpawnRate && currentBoss === null) {
fastSpawnTimer = 0;
// 30% chance to spawn a fast hotako
if (Math.random() < 0.3) {
var newFastHotako = new FastHotako();
// Random spawn position from all four sides
var spawnSide = Math.floor(Math.random() * 4);
if (spawnSide === 0) {
// Top
newFastHotako.x = Math.random() * 1800 + 100;
newFastHotako.y = -50;
} else if (spawnSide === 1) {
// Bottom
newFastHotako.x = Math.random() * 1800 + 100;
newFastHotako.y = 2782;
} else if (spawnSide === 2) {
// Left
newFastHotako.x = -50;
newFastHotako.y = Math.random() * 2500 + 100;
} else {
// Right
newFastHotako.x = 2098;
newFastHotako.y = Math.random() * 2500 + 100;
}
// Set direction toward player
newFastHotako.setDirection(player.x, player.y);
fastHotakos.push(newFastHotako);
game.addChild(newFastHotako);
}
}
// Update Boss
if (currentBoss !== null) {
// Check if boss should fire regular bullets
if (currentBoss.shouldFire) {
currentBoss.shouldFire = false;
var newFire = new Fire();
newFire.x = currentBoss.x;
newFire.y = currentBoss.y;
newFire.setDirection(player.x, player.y, currentBoss.x, currentBoss.y);
fires.push(newFire);
game.addChild(newFire);
LK.getSound('fireShot').play();
}
// Check if boss should fire giant bullet
if (currentBoss.shouldFireGiant) {
currentBoss.shouldFireGiant = false;
var newGiantBullet = new GiantBullet();
newGiantBullet.x = currentBoss.x;
newGiantBullet.y = currentBoss.y;
newGiantBullet.setDirection(player.x, player.y, currentBoss.x, currentBoss.y);
giantBullets.push(newGiantBullet);
game.addChild(newGiantBullet);
LK.getSound('fireShot').play();
}
// Keep boss on screen and make it move around
if (currentBoss.x < 150) currentBoss.x = 150;
if (currentBoss.x > 1898) currentBoss.x = 1898;
if (currentBoss.y < 100) currentBoss.y = 100;
if (currentBoss.y > 800) currentBoss.y = 800;
}
// Update Hotakos and create fires
for (var h = hotakos.length - 1; h >= 0; h--) {
var hotako = hotakos[h];
// Check if Hotako should fire
if (hotako.shouldFire) {
hotako.shouldFire = false;
var newFire = new Fire();
newFire.x = hotako.x;
newFire.y = hotako.y;
newFire.setDirection(player.x, player.y, hotako.x, hotako.y);
fires.push(newFire);
game.addChild(newFire);
LK.getSound('fireShot').play();
}
// Remove off-screen Hotakos (expanded bounds for all directions)
if (hotako.y > 2800 || hotako.y < -100 || hotako.x < -100 || hotako.x > 2148) {
hotako.destroy();
hotakos.splice(h, 1);
}
}
// Update Fast Hotakos and create fires
for (var fh = fastHotakos.length - 1; fh >= 0; fh--) {
var fastHotako = fastHotakos[fh];
// Check if FastHotako should fire
if (fastHotako.shouldFire) {
fastHotako.shouldFire = false;
var newFire = new Fire();
newFire.x = fastHotako.x;
newFire.y = fastHotako.y;
newFire.setDirection(player.x, player.y, fastHotako.x, fastHotako.y);
fires.push(newFire);
game.addChild(newFire);
LK.getSound('fireShot').play();
}
// Remove off-screen FastHotakos (expanded bounds for all directions)
if (fastHotako.y > 2800 || fastHotako.y < -100 || fastHotako.x < -100 || fastHotako.x > 2148) {
fastHotako.destroy();
fastHotakos.splice(fh, 1);
}
}
// Update fires and check collisions
for (var f = fires.length - 1; f >= 0; f--) {
var fire = fires[f];
// Check collision with player
if (fire.intersects(player)) {
// Game Over
LK.effects.flashScreen(0xff0000, 1000);
// Save score
var finalScore = Math.floor(gameTime / 60) * 10 + Math.floor(gameTime / 10);
LK.setScore(finalScore);
LK.showGameOver();
return;
}
// Remove off-screen fires
if (fire.y > 2800 || fire.y < -50 || fire.x < -50 || fire.x > 2098) {
fire.destroy();
fires.splice(f, 1);
}
}
// Update giant bullets and check collisions
for (var gb = giantBullets.length - 1; gb >= 0; gb--) {
var giantBullet = giantBullets[gb];
// Check collision with player
if (giantBullet.intersects(player)) {
// Game Over
LK.effects.flashScreen(0xff0000, 1000);
// Save score
var finalScore = Math.floor(gameTime / 60) * 10 + Math.floor(gameTime / 10);
LK.setScore(finalScore);
LK.showGameOver();
return;
}
// Remove off-screen giant bullets
if (giantBullet.y > 2800 || giantBullet.y < -50 || giantBullet.x < -50 || giantBullet.x > 2098) {
giantBullet.destroy();
giantBullets.splice(gb, 1);
}
}
// Update green bullets and check collisions
for (var grb = greenBullets.length - 1; grb >= 0; grb--) {
var greenBullet = greenBullets[grb];
var hitTarget = false;
// Check collision with Boss
if (currentBoss !== null && greenBullet.intersects(currentBoss)) {
greenBullet.destroy();
greenBullets.splice(grb, 1);
hitTarget = true;
if (currentBoss.takeDamage()) {
// Boss defeated
var bossX = currentBoss.x;
var bossY = currentBoss.y;
currentBoss.destroy();
currentBoss = null;
bossesKilled++;
LK.setScore(LK.getScore() + 500);
LK.effects.flashScreen(0x00ff00, 500);
// Spawn powerup at boss location
var newPowerup = new PowerUp();
newPowerup.x = bossX;
newPowerup.y = bossY;
newPowerup.isBossPowerup = true; // Mark as boss powerup
powerups.push(newPowerup);
game.addChild(newPowerup);
// Start pause
isPaused = true;
pauseTimer = 0;
}
}
// Check collision with normal Hotakos
if (!hitTarget) {
for (var h = hotakos.length - 1; h >= 0; h--) {
var hotako = hotakos[h];
if (greenBullet.intersects(hotako)) {
greenBullet.destroy();
greenBullets.splice(grb, 1);
hotako.destroy();
hotakos.splice(h, 1);
LK.setScore(LK.getScore() + 50);
hotakosKilled++;
currentHotakoSpeed = baseHotakoSpeed;
hitTarget = true;
LK.effects.flashObject(hotako, 0xffffff, 100);
break;
}
}
}
// Check collision with fast Hotakos
if (!hitTarget) {
for (var fh = fastHotakos.length - 1; fh >= 0; fh--) {
var fastHotako = fastHotakos[fh];
if (greenBullet.intersects(fastHotako)) {
greenBullet.destroy();
greenBullets.splice(grb, 1);
fastHotako.destroy();
fastHotakos.splice(fh, 1);
LK.setScore(LK.getScore() + 75);
fastHotakosKilled++;
currentFastHotakoSpeed = baseFastHotakoSpeed + fastHotakosKilled * speedIncrement;
hitTarget = true;
LK.effects.flashObject(fastHotako, 0x0080ff, 100);
break;
}
}
}
// Remove off-screen green bullets
if (!hitTarget && greenBullet.y < -50) {
greenBullet.destroy();
greenBullets.splice(grb, 1);
}
}
// Update yellow bullets and check collisions
for (var yb = yellowBullets.length - 1; yb >= 0; yb--) {
var yellowBullet = yellowBullets[yb];
var hitTarget = false;
// Check collision with Boss
if (currentBoss !== null && yellowBullet.intersects(currentBoss)) {
yellowBullet.destroy();
yellowBullets.splice(yb, 1);
hitTarget = true;
if (currentBoss.takeDamage()) {
// Boss defeated
var bossX = currentBoss.x;
var bossY = currentBoss.y;
currentBoss.destroy();
currentBoss = null;
bossesKilled++;
LK.setScore(LK.getScore() + 500);
LK.effects.flashScreen(0x00ff00, 500);
// Spawn powerup at boss location
var newPowerup = new PowerUp();
newPowerup.x = bossX;
newPowerup.y = bossY;
newPowerup.isBossPowerup = true; // Mark as boss powerup
powerups.push(newPowerup);
game.addChild(newPowerup);
// Start pause
isPaused = true;
pauseTimer = 0;
}
}
// Check collision with normal Hotakos
if (!hitTarget) {
for (var h = hotakos.length - 1; h >= 0; h--) {
var hotako = hotakos[h];
if (yellowBullet.intersects(hotako)) {
yellowBullet.destroy();
yellowBullets.splice(yb, 1);
hotako.destroy();
hotakos.splice(h, 1);
LK.setScore(LK.getScore() + 50);
hotakosKilled++;
currentHotakoSpeed = baseHotakoSpeed;
hitTarget = true;
LK.effects.flashObject(hotako, 0xffffff, 100);
break;
}
}
}
// Check collision with fast Hotakos
if (!hitTarget) {
for (var fh = fastHotakos.length - 1; fh >= 0; fh--) {
var fastHotako = fastHotakos[fh];
if (yellowBullet.intersects(fastHotako)) {
yellowBullet.destroy();
yellowBullets.splice(yb, 1);
fastHotako.destroy();
fastHotakos.splice(fh, 1);
LK.setScore(LK.getScore() + 75);
fastHotakosKilled++;
currentFastHotakoSpeed = baseFastHotakoSpeed + fastHotakosKilled * speedIncrement;
hitTarget = true;
LK.effects.flashObject(fastHotako, 0x0080ff, 100);
break;
}
}
}
// Remove off-screen yellow bullets
if (!hitTarget && yellowBullet.y < -50) {
yellowBullet.destroy();
yellowBullets.splice(yb, 1);
}
}
// Rapid fire shooting when laser power is active and mouse is held
if (hasLaserPower && laserTimer > 0 && isMouseDown) {
// Check bullet limit for hotako powerups only, boss powerups have no limit
var canShoot = true;
if (!isBossPowerup && bullets.length >= maxBulletsOnScreen) {
canShoot = false;
}
if (canShoot) {
// Shoot bullets every frame for maximum rapid fire
var rapidBullet = new Bullet();
rapidBullet.x = player.x;
rapidBullet.y = player.y - 40;
// Shoot toward current mouse position
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
rapidBullet.directionX = dx / distance;
rapidBullet.directionY = dy / distance;
} else {
rapidBullet.directionX = 0;
rapidBullet.directionY = -1;
}
rapidBullet.speed = 15; // Fast bullets
bullets.push(rapidBullet);
game.addChild(rapidBullet);
}
}
// Update bullets and check collisions with Hotakos and Boss
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
var hitTarget = false;
// Check collision with Boss first
if (currentBoss !== null && bullet.intersects(currentBoss)) {
bullet.destroy();
bullets.splice(b, 1);
hitTarget = true;
if (currentBoss.takeDamage()) {
// Boss defeated
var bossX = currentBoss.x;
var bossY = currentBoss.y;
currentBoss.destroy();
currentBoss = null;
bossesKilled++;
LK.setScore(LK.getScore() + 500); // Big score for boss
LK.effects.flashScreen(0x00ff00, 500); // Green flash for victory
// Spawn powerup at boss location
var newPowerup = new PowerUp();
newPowerup.x = bossX;
newPowerup.y = bossY;
newPowerup.isBossPowerup = true; // Mark as boss powerup
powerups.push(newPowerup);
game.addChild(newPowerup);
// Start pause
isPaused = true;
pauseTimer = 0;
}
}
// Check collision with normal Hotakos
if (!hitTarget) {
for (var h = hotakos.length - 1; h >= 0; h--) {
var hotako = hotakos[h];
if (bullet.intersects(hotako)) {
// Hotako hit - destroy both bullet and Hotako
var hotakoX = hotako.x;
var hotakoY = hotako.y;
bullet.destroy();
bullets.splice(b, 1);
hotako.destroy();
hotakos.splice(h, 1);
// Add score for killing Hotako
LK.setScore(LK.getScore() + 50);
hotakosKilled++;
// Keep normal Hotakos at base speed - no speed increase
currentHotakoSpeed = baseHotakoSpeed;
// 10% chance to spawn powerup from normal hotako
if (Math.random() < 0.1) {
var newPowerup = new PowerUp();
newPowerup.x = hotakoX;
newPowerup.y = hotakoY;
newPowerup.isBossPowerup = false; // Mark as hotako powerup
powerups.push(newPowerup);
game.addChild(newPowerup);
}
hitTarget = true;
LK.effects.flashObject(hotako, 0xffffff, 100);
break;
}
}
}
// Check collision with fast Hotakos
if (!hitTarget) {
for (var fh = fastHotakos.length - 1; fh >= 0; fh--) {
var fastHotako = fastHotakos[fh];
if (bullet.intersects(fastHotako)) {
// FastHotako hit - destroy both bullet and FastHotako
bullet.destroy();
bullets.splice(b, 1);
fastHotako.destroy();
fastHotakos.splice(fh, 1);
// Add higher score for killing FastHotako
LK.setScore(LK.getScore() + 75);
fastHotakosKilled++;
// Increase speed for next fast Hotakos
currentFastHotakoSpeed = baseFastHotakoSpeed + fastHotakosKilled * speedIncrement;
hitTarget = true;
LK.effects.flashObject(fastHotako, 0x0080ff, 100);
break;
}
}
}
// Remove off-screen bullets if not already removed
if (!hitTarget && (bullet.y < -50 || bullet.x < -50 || bullet.x > 2098)) {
bullet.destroy();
bullets.splice(b, 1);
}
}
// Update score based on survival time and dodges
var currentScore = Math.floor(gameTime / 60) * 10 + Math.floor(gameTime / 10);
LK.setScore(currentScore);
scoreTxt.setText(LK.getScore().toString());
// Create particle effects occasionally
if (gameTime % 120 === 0) {
LK.effects.flashObject(player, 0xffd700, 200);
}
};