/**** * Classes ****/ // Alien class: 30% chance to spawn, acts as a meteor var Alien = Container.expand(function () { var self = Container.call(this); // Use the 'Alien' asset for alien var alienAsset = self.attachAsset('Alien', { anchorX: 0.5, anchorY: 0.5 }); // Hitbox (ellipse, more accurate for Alien shape) self.hitbox = { x: 0, y: 0, // Use a slightly smaller ellipse hitbox for fairness radiusX: alienAsset.width * 0.28, radiusY: alienAsset.height * 0.32 }; // Movement: falls straight down, similar to meteor self.speed = 12 + Math.random() * 8; self.rotationSpeed = (Math.random() - 0.5) * 0.08; self.lastIntersecting = false; self.update = function () { self.y += self.speed * 0.4; self.rotation += self.rotationSpeed; self.hitbox.x = self.x; self.hitbox.y = self.y; // No change needed here, just keep hitbox position updated }; return self; }); // Developer class: Rare, shootable, grants fire rate boost var Developer = Container.expand(function () { var self = Container.call(this); // Use the 'x2' asset for developer (powerup) var dev = self.attachAsset('x2', { anchorX: 0.5, anchorY: 0.5 }); // Hitbox (circle) self.hitbox = { x: 0, y: 0, radius: dev.width * 0.45 }; // Movement: falls straight down, similar to meteor self.speed = 32 + Math.random() * 8; self.rotationSpeed = (Math.random() - 0.5) * 0.08; self.lastIntersecting = false; self.update = function () { self.y += self.speed * 0.4; self.rotation += self.rotationSpeed; self.hitbox.x = self.x; self.hitbox.y = self.y; }; return self; }); // HardMeteor class: Tough meteor with unique asset and behavior var HardMeteor = Container.expand(function () { var self = Container.call(this); // Use the 'Hardmeteor' asset for hard meteor (with capital H) var hardMeteor = self.attachAsset('HardMeteor', { anchorX: 0.5, anchorY: 0.5 }); // Hitbox (circle) self.hitbox = { x: 0, y: 0, radius: hardMeteor.width * 0.38 }; // Movement: falls straight down, very slow self.speed = 4 + Math.random() * 2; // MUCH slower than other meteors self.rotationSpeed = (Math.random() - 0.5) * 0.08; self.lastIntersecting = false; // Track hits required to destroy self.hitsLeft = 7; self.update = function () { self.y += self.speed * 0.4; self.rotation += self.rotationSpeed; self.hitbox.x = self.x; self.hitbox.y = self.y; }; return self; }); // Meteor class: Falling enemy var Meteor = Container.expand(function () { var self = Container.call(this); var meteor = self.attachAsset('meteor', { anchorX: 0.5, anchorY: 0.5 }); // Add a hitbox property (circle for simplicity) self.hitbox = { x: 0, y: 0, radius: meteor.width * 0.38 // slightly smaller than graphic for fairness }; // Randomize speed and rotation self.speed = 12 + Math.random() * meteorSpeedBoost; self.rotationSpeed = (Math.random() - 0.5) * 0.08; // For intersection tracking self.lastIntersecting = false; self.update = function () { self.y += self.speed * 0.4; // slowed down to 40% of original speed self.rotation += self.rotationSpeed; // Update hitbox position self.hitbox.x = self.x; self.hitbox.y = self.y; }; return self; }); // Projectile class: Fired by satellite var Projectile = Container.expand(function () { var self = Container.call(this); var proj = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -38; // Fast upward self.lastY = undefined; self.update = function () { if (self.lastY === undefined) self.lastY = self.y; self.y += self.speed; }; return self; }); // Satellite class: Orbits above earth, destroys meteors on tap var Satellite = Container.expand(function () { var self = Container.call(this); // Use the 'satellite' asset for the satellite var sat = self.attachAsset('satellite', { anchorX: 0.5, anchorY: 0.5 }); // Satellite hitbox (circle) self.hitbox = { x: 0, y: 0, radius: sat.width * 0.35 }; // Orbit parameters self.orbitRadius = 600; self.orbitCenterX = 2048 / 2; self.orbitCenterY = 2732 - 200; self.orbitAngle = Math.random() * Math.PI * 2; self.orbitSpeed = 0.012 + Math.random() * 0.01; self.update = function () { self.x = self.orbitCenterX; self.y = self.orbitCenterY; self.hitbox.x = self.x; self.hitbox.y = self.y; }; return self; }); // SpaceDebris class: Space junk that acts as a meteor var SpaceDebris = Container.expand(function () { var self = Container.call(this); // Use the 'lives' asset for space debris (was 'debris') var debris = self.attachAsset('lives', { anchorX: 0.5, anchorY: 0.5 }); // Hitbox (circle) self.hitbox = { x: 0, y: 0, radius: debris.width * 0.32 }; // Movement: falls straight down, similar to meteor self.speed = 10 + Math.random() * 10; self.rotationSpeed = (Math.random() - 0.5) * 0.1; self.lastIntersecting = false; self.update = function () { self.y += self.speed * 0.4; self.rotation += self.rotationSpeed; self.hitbox.x = self.x; self.hitbox.y = self.y; }; return self; }); // SuperMeteorite class: Rare, fast meteor var SuperMeteorite = Container.expand(function () { var self = Container.call(this); var superMeteor = self.attachAsset('SuperMeteor', { anchorX: 0.5, anchorY: 0.5 }); // Hitbox similar to fast meteor self.hitbox = { x: 0, y: 0, radius: superMeteor.width * 0.38 }; // Same speed as fast meteor self.speed = 48 + Math.random() * 8; self.rotationSpeed = (Math.random() - 0.5) * 0.08; self.lastIntersecting = false; self.update = function () { self.y += self.speed * 0.4; self.rotation += self.rotationSpeed; self.hitbox.x = self.x; self.hitbox.y = self.y; }; return self; }); /**** * Initialize Game ****/ // Turret class: Player's cannon at the bottom var game = new LK.Game({ backgroundColor: 0x0a2233 }); /**** * Game Code ****/ // Sound effects // Explosion effect (for future use) // Earth: Ground at the bottom // Meteor: Falling enemy // Projectile: Player's bullet // Turret: Defense cannon at the bottom // Game variables var meteors = []; var projectiles = []; var satelliteFireTimer = 0; var satelliteFireInterval = 30; // frames between shots var doubleProjectilePowerup = false; // Powerup: double projectile is off by default var earth; var satellite; var scoreTxt; var livesTxt; var lives = 5; var score = 0; var meteorSpawnInterval = 60; // frames var meteorSpeedBoost = 0; // increases as game progresses var meteorTimer = 0; // Track last score milestone for spawn rate increase var lastScoreMilestone = 0; // Developer spawn scheduling var nextDevScoreSegment = 2; // first eligible segment is 100-199 (segment 2) var devSpawnScheduledScore = null; // the score at which to spawn the next developer var devSpawnedThisSegment = false; // Add 25 background tiles (5 vertical x 5 horizontal) to fill the game area and behind all other elements var backgroundTiles = []; var bgTileRows = 5; var bgTileCols = 5; var bgTileWidth = Math.ceil(2048 / bgTileCols); var bgTileHeight = Math.ceil(2732 / bgTileRows); for (var row = 0; row < bgTileRows; row++) { for (var col = 0; col < bgTileCols; col++) { var bgTile = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: col * bgTileWidth, y: row * bgTileHeight, scaleX: bgTileWidth / 100, scaleY: bgTileHeight / 100 }); game.addChild(bgTile); backgroundTiles.push(bgTile); } } earth = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 1, // anchor at bottom center x: 2048 / 2, y: 2732 + 200, // move earth further down scaleX: 7, scaleY: 2.5 }); // Move earth down so more of it is below the screen, only a smaller top part is visible earth.y = 2732 + 700; // move earth even further down, only a thin top slice is visible game.addChild(earth); // Add satellite satellite = new Satellite(); satellite.orbitCenterX = 2048 / 2; satellite.orbitCenterY = 2732 - 350; // moved satellite higher above earth satellite.orbitRadius = 600; game.addChild(satellite); // Score text (top center) scoreTxt = new Text2('Score: 0', { size: 100, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Lives text (top right, avoid top left 100x100) livesTxt = new Text2('Lives: 5', { size: 80, fill: 0xFFB300 }); livesTxt.anchor.set(1, 0); LK.gui.topRight.addChild(livesTxt); // Start playing background music LK.playMusic('gamemusic'); // Dragging logic for satellite var draggingSatellite = false; var dragOffsetX = 0; game.move = function (x, y, obj) { // If currently dragging satellite, update its X position only if (draggingSatellite) { // Clamp satellite X to stay within game bounds (avoid going off screen) var minX = 120; var maxX = 2048 - 120; var newX = x - dragOffsetX; if (newX < minX) newX = minX; if (newX > maxX) newX = maxX; satellite.x = newX; satellite.orbitCenterX = newX; // keep its logical center in sync return; } // First, check if satellite is tapped to start dragging var dxSat = x - satellite.x; var dySat = y - satellite.y; var distSat = Math.sqrt(dxSat * dxSat + dySat * dySat); if (distSat <= satellite.hitbox.radius) { // Start dragging draggingSatellite = true; dragOffsetX = x - satellite.x; return; } // Check if a meteor is tapped for (var i = meteors.length - 1; i >= 0; i--) { var mtr = meteors[i]; // Calculate distance from tap to meteor center var dx = x - mtr.x; var dy = y - mtr.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= mtr.hitbox.radius) { // Destroy meteor mtr.destroy(); meteors.splice(i, 1); score += 1; scoreTxt.setText('Score: ' + score); // Optional: add explosion effect here if desired break; // Only destroy one meteor per tap } ; // Update projectiles and remove if off screen for (var p = projectiles.length - 1; p >= 0; p--) { var proj = projectiles[p]; proj.update(); if (proj.y < -100) { proj.destroy(); projectiles.splice(p, 1); } } } ; }; // Release drag on up game.up = function (x, y, obj) { draggingSatellite = false; }; game.up = function (x, y, obj) {}; // Main update loop game.update = function () { // Every 50 score, increase spawn rate by 10% (decrease interval by 10%) if (score >= lastScoreMilestone + 50) { lastScoreMilestone += 50; meteorSpawnInterval = Math.max(8, Math.floor(meteorSpawnInterval * 0.9)); } // Developer spawn scheduling logic // Only after first 100 score, for each 100-score segment, schedule one developer at a random score in that segment if (score >= 100) { // Determine current segment (segment 2: 100-199, segment 3: 200-299, etc.) var currentSegment = Math.floor(score / 100) + 1; // If we have entered a new segment, schedule a new developer spawn if (currentSegment >= nextDevScoreSegment) { // Schedule developer for this segment at a random score in [segmentStart, segmentEnd] var segmentStart = (currentSegment - 1) * 100; var segmentEnd = segmentStart + 99; // Pick a random score in this segment, but not the very first score (to avoid instant spawn) devSpawnScheduledScore = segmentStart + Math.floor(Math.random() * 100); devSpawnedThisSegment = false; nextDevScoreSegment = currentSegment + 1; } } // Spawn meteors meteorTimer++; var spawnRate = meteorSpawnInterval; if (meteorTimer >= spawnRate) { meteorTimer = 0; // Super meteor, fast meteor, and developer spawn logic with new probabilities after score > 100 var superMeteorChance = 0; var fastMeteorChance = 0; // Remove random developerChance, we now use scheduled spawn var developerShouldSpawn = false; if (devSpawnScheduledScore !== null && !devSpawnedThisSegment && score >= devSpawnScheduledScore) { developerShouldSpawn = true; } if (score >= 1250) { // 100% super meteor, 0% fast meteor, 0% alien superMeteorChance = 1.0; // 100% fastMeteorChance = 0.0; alienChance = 0.0; } else if (score >= 1000) { // 95% fast meteor, 5% super meteor, 0% alien superMeteorChance = 0.05; // 5% fastMeteorChance = 0.95; // 95% alienChance = 0.0; // 0% } else if (score >= 700) { // 75% fast meteor, 20% super meteor, 5% alien superMeteorChance = 0.20; // 20% fastMeteorChance = 0.75; // 75% alienChance = 0.05; // 5% } else if (score >= 400) { superMeteorChance = 0.15; // 15% fastMeteorChance = 0.5; // 50% alienChance = 0.3; // 30% (default for Alien below 700) } else if (score > 200) { superMeteorChance = 0.075; // 7.5% fastMeteorChance = 0.15; // 15% alienChance = 0.3; } else if (score > 100) { superMeteorChance = 0.05; // 5% fastMeteorChance = 0.10; // 10% alienChance = 0.3; } else if (score > 50) { superMeteorChance = 0.03; // 3% fastMeteorChance = 0.05; // 5% alienChance = 0.3; } else if (score >= 50) { superMeteorChance = 0.01; // 1% fastMeteorChance = 0.05; // 5% alienChance = 0.3; } else { superMeteorChance = 0; fastMeteorChance = 0; alienChance = 0.3; } // Scheduled Developer spawn (only after 100 score, one per segment) if (developerShouldSpawn) { var dev = new Developer(); dev.x = 120 + Math.random() * (2048 - 240); dev.y = -80; meteors.push(dev); game.addChild(dev); devSpawnedThisSegment = true; } else if (superMeteorChance > 0 && Math.random() < superMeteorChance) { var m = new SuperMeteorite(); // Süper meteorları %25 yavaşlat m.speed = m.speed * 0.75; m.x = 120 + Math.random() * (2048 - 240); m.y = -80; meteors.push(m); game.addChild(m); } else { // Track if debris has already spawned if (typeof debrisHasSpawned === "undefined") { var debrisHasSpawned = false; } // 1% chance to spawn space debris instead of meteor, but only if 100 <= score <= 400 and debris hasn't spawned yet if (!debrisHasSpawned && score >= 100 && score <= 400 && Math.random() < 0.01) { var debris = new SpaceDebris(); debris.x = 120 + Math.random() * (2048 - 240); debris.y = -80; meteors.push(debris); game.addChild(debris); debrisHasSpawned = true; } else if (Math.random() < alienChance) { // Alien spawn chance (5% if score >= 700, else 30%) var alien = new Alien(); alien.x = 120 + Math.random() * (2048 - 240); alien.y = -80; meteors.push(alien); game.addChild(alien); } else { // HardMeteor spawn: Only if score > 100. 100% if score > 1500, 1% if 100 < score <= 1500 if (score > 100 && (score > 1500 || score <= 1500 && Math.random() < 0.01)) { var hard = new HardMeteor(); hard.x = 120 + Math.random() * (2048 - 240); hard.y = -80; meteors.push(hard); game.addChild(hard); } else { var m = new Meteor(); // Fast meteor spawn if (fastMeteorChance > 0 && Math.random() < fastMeteorChance) { m.speed = 48 + Math.random() * 8; // Hızlı meteorları %50 yavaşlat m.speed = m.speed * 0.5; // Hızlı meteorları %5 daha hızlı yap m.speed = m.speed * 1.05; // Replace meteor asset with FastMeteorite if (m.children && m.children.length > 0) { var oldMeteor = m.children[0]; m.removeChild(oldMeteor); } var fastMeteor = m.attachAsset('FastMeteorite', { anchorX: 0.5, anchorY: 0.5 }); // Update hitbox to match new asset size m.hitbox.radius = fastMeteor.width * 0.38; } m.x = 120 + Math.random() * (2048 - 240); m.y = -80; meteors.push(m); game.addChild(m); } } } } // Remove difficulty scaling meteorSpeedBoost = 0; // Speed up all falling objects by 25% at score 500 (only once) if (!game.speedBoosted && score >= 500) { game.speedBoosted = true; // Boost all current meteors for (var i = 0; i < meteors.length; i++) { if (meteors[i].speed !== undefined) { meteors[i].speed *= 1.25; } } // Boost future meteors by increasing meteorSpeedBoost // (affects Meteor class randomization) meteorSpeedBoost = meteorSpeedBoost * 1.25 + 2.5; // ensure new meteors are faster } // Update meteors for (var k = meteors.length - 1; k >= 0; k--) { var mtr = meteors[k]; mtr.update(); // If meteor hits earth if (mtr.y + 60 >= earth.y - earth.height / 2) { // If it's a Developer, take 2 lives and remove it if (mtr instanceof Developer) { LK.getSound('lose').play(); mtr.destroy(); meteors.splice(k, 1); lives -= 2; livesTxt.setText('Lives: ' + lives); LK.effects.flashScreen(0xff0000, 400); if (lives <= 0) { debrisHasSpawned = false; LK.showGameOver(); return; } continue; } // If it's SpaceDebris, just remove it (do not change lives) if (mtr instanceof SpaceDebris) { mtr.destroy(); meteors.splice(k, 1); continue; } LK.getSound('lose').play(); mtr.destroy(); meteors.splice(k, 1); lives -= 1; livesTxt.setText('Lives: ' + lives); // Flash screen LK.effects.flashScreen(0xff0000, 400); if (lives <= 0) { debrisHasSpawned = false; LK.showGameOver(); return; } continue; } // Check collision with projectiles for (var j = projectiles.length - 1; j >= 0; j--) { var proj = projectiles[j]; // Collision: use ellipse hitbox for Alien, circle for others var dx = proj.x - mtr.x; var dy = proj.y - mtr.y; var hit = false; if (mtr instanceof Alien) { // Ellipse hitbox: (dx/rx)^2 + (dy/ry)^2 <= 1 var rx = mtr.hitbox.radiusX + 40; var ry = mtr.hitbox.radiusY + 40; if (dx * dx / (rx * rx) + dy * dy / (ry * ry) <= 1) { hit = true; } } else { var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= (mtr.hitbox.radius !== undefined ? mtr.hitbox.radius : 0) + 40) { hit = true; } } if (hit) { // 40 is half projectile height, rough // If hit is on Developer, boost fire rate if (mtr instanceof Developer) { // Fire rate boost: halve the interval, but not less than 6 frames (10 shots/sec) satelliteFireInterval = Math.max(6, Math.floor(satelliteFireInterval / 2)); // Enable double projectile powerup when Developer is shot doubleProjectilePowerup = true; // Destroy Developer (powerup) when hit by projectile mtr.destroy(); meteors.splice(k, 1); } else if (mtr instanceof SpaceDebris) { // If hit is on SpaceDebris, increase projectile size slightly // Only increase if not already too large if (proj.children && proj.children.length > 0) { var projAsset = proj.children[0]; // Limit max scale to avoid infinite growth if (projAsset.scaleX < 2.0 && projAsset.scaleY < 2.0) { projAsset.scaleX += 0.15; projAsset.scaleY += 0.15; } } // Grant +1 life when debris is shot lives += 1; livesTxt.setText('Lives: ' + lives); score += 1; scoreTxt.setText('Score: ' + score); // Destroy SpaceDebris (lives) when hit by a projectile mtr.destroy(); meteors.splice(k, 1); proj.destroy(); projectiles.splice(j, 1); break; // Only one projectile can hit a meteor at a time } else if (mtr.children && mtr.children.length > 0 && mtr.children[0].assetId === 'FastMeteorite') { score += 2; scoreTxt.setText('Score: ' + score); } else if (mtr instanceof SuperMeteorite) { score += 5; scoreTxt.setText('Score: ' + score); mtr.destroy(); meteors.splice(k, 1); proj.destroy(); projectiles.splice(j, 1); break; // Only one projectile can hit a meteor at a time } else if (mtr instanceof HardMeteor) { // Reduce hitsLeft, only destroy if 0 mtr.hitsLeft -= 1; if (mtr.hitsLeft <= 0) { score += 3; scoreTxt.setText('Score: ' + score); mtr.destroy(); meteors.splice(k, 1); } proj.destroy(); projectiles.splice(j, 1); break; // Only one projectile can hit a meteor at a time } else { score += 1; scoreTxt.setText('Score: ' + score); // Destroy both mtr.destroy(); meteors.splice(k, 1); proj.destroy(); projectiles.splice(j, 1); break; // Only one projectile can hit a meteor at a time } } } } // Update satellite if (satellite && satellite.update) { satellite.update(); // Satellite fires projectiles at interval satelliteFireTimer++; if (satelliteFireTimer >= satelliteFireInterval) { satelliteFireTimer = 0; if (doubleProjectilePowerup) { // Fire two projectiles, slightly offset var proj1 = new Projectile(); proj1.x = satellite.x - 40; proj1.y = satellite.y - 60; projectiles.push(proj1); game.addChild(proj1); var proj2 = new Projectile(); proj2.x = satellite.x + 40; proj2.y = satellite.y - 60; projectiles.push(proj2); game.addChild(proj2); } else { var proj = new Projectile(); proj.x = satellite.x; proj.y = satellite.y - 60; // fire from above satellite projectiles.push(proj); game.addChild(proj); } } } };
/****
* Classes
****/
// Alien class: 30% chance to spawn, acts as a meteor
var Alien = Container.expand(function () {
var self = Container.call(this);
// Use the 'Alien' asset for alien
var alienAsset = self.attachAsset('Alien', {
anchorX: 0.5,
anchorY: 0.5
});
// Hitbox (ellipse, more accurate for Alien shape)
self.hitbox = {
x: 0,
y: 0,
// Use a slightly smaller ellipse hitbox for fairness
radiusX: alienAsset.width * 0.28,
radiusY: alienAsset.height * 0.32
};
// Movement: falls straight down, similar to meteor
self.speed = 12 + Math.random() * 8;
self.rotationSpeed = (Math.random() - 0.5) * 0.08;
self.lastIntersecting = false;
self.update = function () {
self.y += self.speed * 0.4;
self.rotation += self.rotationSpeed;
self.hitbox.x = self.x;
self.hitbox.y = self.y;
// No change needed here, just keep hitbox position updated
};
return self;
});
// Developer class: Rare, shootable, grants fire rate boost
var Developer = Container.expand(function () {
var self = Container.call(this);
// Use the 'x2' asset for developer (powerup)
var dev = self.attachAsset('x2', {
anchorX: 0.5,
anchorY: 0.5
});
// Hitbox (circle)
self.hitbox = {
x: 0,
y: 0,
radius: dev.width * 0.45
};
// Movement: falls straight down, similar to meteor
self.speed = 32 + Math.random() * 8;
self.rotationSpeed = (Math.random() - 0.5) * 0.08;
self.lastIntersecting = false;
self.update = function () {
self.y += self.speed * 0.4;
self.rotation += self.rotationSpeed;
self.hitbox.x = self.x;
self.hitbox.y = self.y;
};
return self;
});
// HardMeteor class: Tough meteor with unique asset and behavior
var HardMeteor = Container.expand(function () {
var self = Container.call(this);
// Use the 'Hardmeteor' asset for hard meteor (with capital H)
var hardMeteor = self.attachAsset('HardMeteor', {
anchorX: 0.5,
anchorY: 0.5
});
// Hitbox (circle)
self.hitbox = {
x: 0,
y: 0,
radius: hardMeteor.width * 0.38
};
// Movement: falls straight down, very slow
self.speed = 4 + Math.random() * 2; // MUCH slower than other meteors
self.rotationSpeed = (Math.random() - 0.5) * 0.08;
self.lastIntersecting = false;
// Track hits required to destroy
self.hitsLeft = 7;
self.update = function () {
self.y += self.speed * 0.4;
self.rotation += self.rotationSpeed;
self.hitbox.x = self.x;
self.hitbox.y = self.y;
};
return self;
});
// Meteor class: Falling enemy
var Meteor = Container.expand(function () {
var self = Container.call(this);
var meteor = self.attachAsset('meteor', {
anchorX: 0.5,
anchorY: 0.5
});
// Add a hitbox property (circle for simplicity)
self.hitbox = {
x: 0,
y: 0,
radius: meteor.width * 0.38 // slightly smaller than graphic for fairness
};
// Randomize speed and rotation
self.speed = 12 + Math.random() * meteorSpeedBoost;
self.rotationSpeed = (Math.random() - 0.5) * 0.08;
// For intersection tracking
self.lastIntersecting = false;
self.update = function () {
self.y += self.speed * 0.4; // slowed down to 40% of original speed
self.rotation += self.rotationSpeed;
// Update hitbox position
self.hitbox.x = self.x;
self.hitbox.y = self.y;
};
return self;
});
// Projectile class: Fired by satellite
var Projectile = Container.expand(function () {
var self = Container.call(this);
var proj = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -38; // Fast upward
self.lastY = undefined;
self.update = function () {
if (self.lastY === undefined) self.lastY = self.y;
self.y += self.speed;
};
return self;
});
// Satellite class: Orbits above earth, destroys meteors on tap
var Satellite = Container.expand(function () {
var self = Container.call(this);
// Use the 'satellite' asset for the satellite
var sat = self.attachAsset('satellite', {
anchorX: 0.5,
anchorY: 0.5
});
// Satellite hitbox (circle)
self.hitbox = {
x: 0,
y: 0,
radius: sat.width * 0.35
};
// Orbit parameters
self.orbitRadius = 600;
self.orbitCenterX = 2048 / 2;
self.orbitCenterY = 2732 - 200;
self.orbitAngle = Math.random() * Math.PI * 2;
self.orbitSpeed = 0.012 + Math.random() * 0.01;
self.update = function () {
self.x = self.orbitCenterX;
self.y = self.orbitCenterY;
self.hitbox.x = self.x;
self.hitbox.y = self.y;
};
return self;
});
// SpaceDebris class: Space junk that acts as a meteor
var SpaceDebris = Container.expand(function () {
var self = Container.call(this);
// Use the 'lives' asset for space debris (was 'debris')
var debris = self.attachAsset('lives', {
anchorX: 0.5,
anchorY: 0.5
});
// Hitbox (circle)
self.hitbox = {
x: 0,
y: 0,
radius: debris.width * 0.32
};
// Movement: falls straight down, similar to meteor
self.speed = 10 + Math.random() * 10;
self.rotationSpeed = (Math.random() - 0.5) * 0.1;
self.lastIntersecting = false;
self.update = function () {
self.y += self.speed * 0.4;
self.rotation += self.rotationSpeed;
self.hitbox.x = self.x;
self.hitbox.y = self.y;
};
return self;
});
// SuperMeteorite class: Rare, fast meteor
var SuperMeteorite = Container.expand(function () {
var self = Container.call(this);
var superMeteor = self.attachAsset('SuperMeteor', {
anchorX: 0.5,
anchorY: 0.5
});
// Hitbox similar to fast meteor
self.hitbox = {
x: 0,
y: 0,
radius: superMeteor.width * 0.38
};
// Same speed as fast meteor
self.speed = 48 + Math.random() * 8;
self.rotationSpeed = (Math.random() - 0.5) * 0.08;
self.lastIntersecting = false;
self.update = function () {
self.y += self.speed * 0.4;
self.rotation += self.rotationSpeed;
self.hitbox.x = self.x;
self.hitbox.y = self.y;
};
return self;
});
/****
* Initialize Game
****/
// Turret class: Player's cannon at the bottom
var game = new LK.Game({
backgroundColor: 0x0a2233
});
/****
* Game Code
****/
// Sound effects
// Explosion effect (for future use)
// Earth: Ground at the bottom
// Meteor: Falling enemy
// Projectile: Player's bullet
// Turret: Defense cannon at the bottom
// Game variables
var meteors = [];
var projectiles = [];
var satelliteFireTimer = 0;
var satelliteFireInterval = 30; // frames between shots
var doubleProjectilePowerup = false; // Powerup: double projectile is off by default
var earth;
var satellite;
var scoreTxt;
var livesTxt;
var lives = 5;
var score = 0;
var meteorSpawnInterval = 60; // frames
var meteorSpeedBoost = 0; // increases as game progresses
var meteorTimer = 0;
// Track last score milestone for spawn rate increase
var lastScoreMilestone = 0;
// Developer spawn scheduling
var nextDevScoreSegment = 2; // first eligible segment is 100-199 (segment 2)
var devSpawnScheduledScore = null; // the score at which to spawn the next developer
var devSpawnedThisSegment = false;
// Add 25 background tiles (5 vertical x 5 horizontal) to fill the game area and behind all other elements
var backgroundTiles = [];
var bgTileRows = 5;
var bgTileCols = 5;
var bgTileWidth = Math.ceil(2048 / bgTileCols);
var bgTileHeight = Math.ceil(2732 / bgTileRows);
for (var row = 0; row < bgTileRows; row++) {
for (var col = 0; col < bgTileCols; col++) {
var bgTile = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: col * bgTileWidth,
y: row * bgTileHeight,
scaleX: bgTileWidth / 100,
scaleY: bgTileHeight / 100
});
game.addChild(bgTile);
backgroundTiles.push(bgTile);
}
}
earth = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 1,
// anchor at bottom center
x: 2048 / 2,
y: 2732 + 200,
// move earth further down
scaleX: 7,
scaleY: 2.5
});
// Move earth down so more of it is below the screen, only a smaller top part is visible
earth.y = 2732 + 700; // move earth even further down, only a thin top slice is visible
game.addChild(earth);
// Add satellite
satellite = new Satellite();
satellite.orbitCenterX = 2048 / 2;
satellite.orbitCenterY = 2732 - 350; // moved satellite higher above earth
satellite.orbitRadius = 600;
game.addChild(satellite);
// Score text (top center)
scoreTxt = new Text2('Score: 0', {
size: 100,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Lives text (top right, avoid top left 100x100)
livesTxt = new Text2('Lives: 5', {
size: 80,
fill: 0xFFB300
});
livesTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(livesTxt);
// Start playing background music
LK.playMusic('gamemusic');
// Dragging logic for satellite
var draggingSatellite = false;
var dragOffsetX = 0;
game.move = function (x, y, obj) {
// If currently dragging satellite, update its X position only
if (draggingSatellite) {
// Clamp satellite X to stay within game bounds (avoid going off screen)
var minX = 120;
var maxX = 2048 - 120;
var newX = x - dragOffsetX;
if (newX < minX) newX = minX;
if (newX > maxX) newX = maxX;
satellite.x = newX;
satellite.orbitCenterX = newX; // keep its logical center in sync
return;
}
// First, check if satellite is tapped to start dragging
var dxSat = x - satellite.x;
var dySat = y - satellite.y;
var distSat = Math.sqrt(dxSat * dxSat + dySat * dySat);
if (distSat <= satellite.hitbox.radius) {
// Start dragging
draggingSatellite = true;
dragOffsetX = x - satellite.x;
return;
}
// Check if a meteor is tapped
for (var i = meteors.length - 1; i >= 0; i--) {
var mtr = meteors[i];
// Calculate distance from tap to meteor center
var dx = x - mtr.x;
var dy = y - mtr.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= mtr.hitbox.radius) {
// Destroy meteor
mtr.destroy();
meteors.splice(i, 1);
score += 1;
scoreTxt.setText('Score: ' + score);
// Optional: add explosion effect here if desired
break; // Only destroy one meteor per tap
}
;
// Update projectiles and remove if off screen
for (var p = projectiles.length - 1; p >= 0; p--) {
var proj = projectiles[p];
proj.update();
if (proj.y < -100) {
proj.destroy();
projectiles.splice(p, 1);
}
}
}
;
};
// Release drag on up
game.up = function (x, y, obj) {
draggingSatellite = false;
};
game.up = function (x, y, obj) {};
// Main update loop
game.update = function () {
// Every 50 score, increase spawn rate by 10% (decrease interval by 10%)
if (score >= lastScoreMilestone + 50) {
lastScoreMilestone += 50;
meteorSpawnInterval = Math.max(8, Math.floor(meteorSpawnInterval * 0.9));
}
// Developer spawn scheduling logic
// Only after first 100 score, for each 100-score segment, schedule one developer at a random score in that segment
if (score >= 100) {
// Determine current segment (segment 2: 100-199, segment 3: 200-299, etc.)
var currentSegment = Math.floor(score / 100) + 1;
// If we have entered a new segment, schedule a new developer spawn
if (currentSegment >= nextDevScoreSegment) {
// Schedule developer for this segment at a random score in [segmentStart, segmentEnd]
var segmentStart = (currentSegment - 1) * 100;
var segmentEnd = segmentStart + 99;
// Pick a random score in this segment, but not the very first score (to avoid instant spawn)
devSpawnScheduledScore = segmentStart + Math.floor(Math.random() * 100);
devSpawnedThisSegment = false;
nextDevScoreSegment = currentSegment + 1;
}
}
// Spawn meteors
meteorTimer++;
var spawnRate = meteorSpawnInterval;
if (meteorTimer >= spawnRate) {
meteorTimer = 0;
// Super meteor, fast meteor, and developer spawn logic with new probabilities after score > 100
var superMeteorChance = 0;
var fastMeteorChance = 0;
// Remove random developerChance, we now use scheduled spawn
var developerShouldSpawn = false;
if (devSpawnScheduledScore !== null && !devSpawnedThisSegment && score >= devSpawnScheduledScore) {
developerShouldSpawn = true;
}
if (score >= 1250) {
// 100% super meteor, 0% fast meteor, 0% alien
superMeteorChance = 1.0; // 100%
fastMeteorChance = 0.0;
alienChance = 0.0;
} else if (score >= 1000) {
// 95% fast meteor, 5% super meteor, 0% alien
superMeteorChance = 0.05; // 5%
fastMeteorChance = 0.95; // 95%
alienChance = 0.0; // 0%
} else if (score >= 700) {
// 75% fast meteor, 20% super meteor, 5% alien
superMeteorChance = 0.20; // 20%
fastMeteorChance = 0.75; // 75%
alienChance = 0.05; // 5%
} else if (score >= 400) {
superMeteorChance = 0.15; // 15%
fastMeteorChance = 0.5; // 50%
alienChance = 0.3; // 30% (default for Alien below 700)
} else if (score > 200) {
superMeteorChance = 0.075; // 7.5%
fastMeteorChance = 0.15; // 15%
alienChance = 0.3;
} else if (score > 100) {
superMeteorChance = 0.05; // 5%
fastMeteorChance = 0.10; // 10%
alienChance = 0.3;
} else if (score > 50) {
superMeteorChance = 0.03; // 3%
fastMeteorChance = 0.05; // 5%
alienChance = 0.3;
} else if (score >= 50) {
superMeteorChance = 0.01; // 1%
fastMeteorChance = 0.05; // 5%
alienChance = 0.3;
} else {
superMeteorChance = 0;
fastMeteorChance = 0;
alienChance = 0.3;
}
// Scheduled Developer spawn (only after 100 score, one per segment)
if (developerShouldSpawn) {
var dev = new Developer();
dev.x = 120 + Math.random() * (2048 - 240);
dev.y = -80;
meteors.push(dev);
game.addChild(dev);
devSpawnedThisSegment = true;
} else if (superMeteorChance > 0 && Math.random() < superMeteorChance) {
var m = new SuperMeteorite();
// Süper meteorları %25 yavaşlat
m.speed = m.speed * 0.75;
m.x = 120 + Math.random() * (2048 - 240);
m.y = -80;
meteors.push(m);
game.addChild(m);
} else {
// Track if debris has already spawned
if (typeof debrisHasSpawned === "undefined") {
var debrisHasSpawned = false;
}
// 1% chance to spawn space debris instead of meteor, but only if 100 <= score <= 400 and debris hasn't spawned yet
if (!debrisHasSpawned && score >= 100 && score <= 400 && Math.random() < 0.01) {
var debris = new SpaceDebris();
debris.x = 120 + Math.random() * (2048 - 240);
debris.y = -80;
meteors.push(debris);
game.addChild(debris);
debrisHasSpawned = true;
} else if (Math.random() < alienChance) {
// Alien spawn chance (5% if score >= 700, else 30%)
var alien = new Alien();
alien.x = 120 + Math.random() * (2048 - 240);
alien.y = -80;
meteors.push(alien);
game.addChild(alien);
} else {
// HardMeteor spawn: Only if score > 100. 100% if score > 1500, 1% if 100 < score <= 1500
if (score > 100 && (score > 1500 || score <= 1500 && Math.random() < 0.01)) {
var hard = new HardMeteor();
hard.x = 120 + Math.random() * (2048 - 240);
hard.y = -80;
meteors.push(hard);
game.addChild(hard);
} else {
var m = new Meteor();
// Fast meteor spawn
if (fastMeteorChance > 0 && Math.random() < fastMeteorChance) {
m.speed = 48 + Math.random() * 8;
// Hızlı meteorları %50 yavaşlat
m.speed = m.speed * 0.5;
// Hızlı meteorları %5 daha hızlı yap
m.speed = m.speed * 1.05;
// Replace meteor asset with FastMeteorite
if (m.children && m.children.length > 0) {
var oldMeteor = m.children[0];
m.removeChild(oldMeteor);
}
var fastMeteor = m.attachAsset('FastMeteorite', {
anchorX: 0.5,
anchorY: 0.5
});
// Update hitbox to match new asset size
m.hitbox.radius = fastMeteor.width * 0.38;
}
m.x = 120 + Math.random() * (2048 - 240);
m.y = -80;
meteors.push(m);
game.addChild(m);
}
}
}
}
// Remove difficulty scaling
meteorSpeedBoost = 0;
// Speed up all falling objects by 25% at score 500 (only once)
if (!game.speedBoosted && score >= 500) {
game.speedBoosted = true;
// Boost all current meteors
for (var i = 0; i < meteors.length; i++) {
if (meteors[i].speed !== undefined) {
meteors[i].speed *= 1.25;
}
}
// Boost future meteors by increasing meteorSpeedBoost
// (affects Meteor class randomization)
meteorSpeedBoost = meteorSpeedBoost * 1.25 + 2.5; // ensure new meteors are faster
}
// Update meteors
for (var k = meteors.length - 1; k >= 0; k--) {
var mtr = meteors[k];
mtr.update();
// If meteor hits earth
if (mtr.y + 60 >= earth.y - earth.height / 2) {
// If it's a Developer, take 2 lives and remove it
if (mtr instanceof Developer) {
LK.getSound('lose').play();
mtr.destroy();
meteors.splice(k, 1);
lives -= 2;
livesTxt.setText('Lives: ' + lives);
LK.effects.flashScreen(0xff0000, 400);
if (lives <= 0) {
debrisHasSpawned = false;
LK.showGameOver();
return;
}
continue;
}
// If it's SpaceDebris, just remove it (do not change lives)
if (mtr instanceof SpaceDebris) {
mtr.destroy();
meteors.splice(k, 1);
continue;
}
LK.getSound('lose').play();
mtr.destroy();
meteors.splice(k, 1);
lives -= 1;
livesTxt.setText('Lives: ' + lives);
// Flash screen
LK.effects.flashScreen(0xff0000, 400);
if (lives <= 0) {
debrisHasSpawned = false;
LK.showGameOver();
return;
}
continue;
}
// Check collision with projectiles
for (var j = projectiles.length - 1; j >= 0; j--) {
var proj = projectiles[j];
// Collision: use ellipse hitbox for Alien, circle for others
var dx = proj.x - mtr.x;
var dy = proj.y - mtr.y;
var hit = false;
if (mtr instanceof Alien) {
// Ellipse hitbox: (dx/rx)^2 + (dy/ry)^2 <= 1
var rx = mtr.hitbox.radiusX + 40;
var ry = mtr.hitbox.radiusY + 40;
if (dx * dx / (rx * rx) + dy * dy / (ry * ry) <= 1) {
hit = true;
}
} else {
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= (mtr.hitbox.radius !== undefined ? mtr.hitbox.radius : 0) + 40) {
hit = true;
}
}
if (hit) {
// 40 is half projectile height, rough
// If hit is on Developer, boost fire rate
if (mtr instanceof Developer) {
// Fire rate boost: halve the interval, but not less than 6 frames (10 shots/sec)
satelliteFireInterval = Math.max(6, Math.floor(satelliteFireInterval / 2));
// Enable double projectile powerup when Developer is shot
doubleProjectilePowerup = true;
// Destroy Developer (powerup) when hit by projectile
mtr.destroy();
meteors.splice(k, 1);
} else if (mtr instanceof SpaceDebris) {
// If hit is on SpaceDebris, increase projectile size slightly
// Only increase if not already too large
if (proj.children && proj.children.length > 0) {
var projAsset = proj.children[0];
// Limit max scale to avoid infinite growth
if (projAsset.scaleX < 2.0 && projAsset.scaleY < 2.0) {
projAsset.scaleX += 0.15;
projAsset.scaleY += 0.15;
}
}
// Grant +1 life when debris is shot
lives += 1;
livesTxt.setText('Lives: ' + lives);
score += 1;
scoreTxt.setText('Score: ' + score);
// Destroy SpaceDebris (lives) when hit by a projectile
mtr.destroy();
meteors.splice(k, 1);
proj.destroy();
projectiles.splice(j, 1);
break; // Only one projectile can hit a meteor at a time
} else if (mtr.children && mtr.children.length > 0 && mtr.children[0].assetId === 'FastMeteorite') {
score += 2;
scoreTxt.setText('Score: ' + score);
} else if (mtr instanceof SuperMeteorite) {
score += 5;
scoreTxt.setText('Score: ' + score);
mtr.destroy();
meteors.splice(k, 1);
proj.destroy();
projectiles.splice(j, 1);
break; // Only one projectile can hit a meteor at a time
} else if (mtr instanceof HardMeteor) {
// Reduce hitsLeft, only destroy if 0
mtr.hitsLeft -= 1;
if (mtr.hitsLeft <= 0) {
score += 3;
scoreTxt.setText('Score: ' + score);
mtr.destroy();
meteors.splice(k, 1);
}
proj.destroy();
projectiles.splice(j, 1);
break; // Only one projectile can hit a meteor at a time
} else {
score += 1;
scoreTxt.setText('Score: ' + score);
// Destroy both
mtr.destroy();
meteors.splice(k, 1);
proj.destroy();
projectiles.splice(j, 1);
break; // Only one projectile can hit a meteor at a time
}
}
}
}
// Update satellite
if (satellite && satellite.update) {
satellite.update();
// Satellite fires projectiles at interval
satelliteFireTimer++;
if (satelliteFireTimer >= satelliteFireInterval) {
satelliteFireTimer = 0;
if (doubleProjectilePowerup) {
// Fire two projectiles, slightly offset
var proj1 = new Projectile();
proj1.x = satellite.x - 40;
proj1.y = satellite.y - 60;
projectiles.push(proj1);
game.addChild(proj1);
var proj2 = new Projectile();
proj2.x = satellite.x + 40;
proj2.y = satellite.y - 60;
projectiles.push(proj2);
game.addChild(proj2);
} else {
var proj = new Projectile();
proj.x = satellite.x;
proj.y = satellite.y - 60; // fire from above satellite
projectiles.push(proj);
game.addChild(proj);
}
}
}
};
meteor . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
dünya yüzeyi. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
blue meteor. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
green meteorite. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
spaceship. In-Game asset. 2d. High contrast. No shadows
spaceship debris. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
laser projectile. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
space with only stars. In-Game asset. real. High contrast. with shadows. high resolution
dünyanın bir bölümünün yüzeyi. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
alien. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
golden dolar symbol. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
yellow meteor. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
game heart. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
shield. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
red meteor with x2 write. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat