/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Boss = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.bossLevel = 1; self.health = 20; self.maxHealth = 20; self.speed = 3; self.direction = 1; self.shootCooldown = 0; self.shootInterval = 45; self.phase = 1; self.points = 1000; self.setBossLevel = function (level) { self.bossLevel = Math.floor(level / 10); self.health = 30 + self.bossLevel * 20; self.maxHealth = self.health; self.points = 1000 * (self.bossLevel + 1); bossGraphics.scale.x = 1 + self.bossLevel * 0.2; bossGraphics.scale.y = 1 + self.bossLevel * 0.2; }; self.update = function () { // Boss movement pattern self.y += self.speed * self.direction; if (self.y > 600 || self.y < 300) { self.direction *= -1; } // More complex movement for higher level bosses if (self.bossLevel >= 2) { self.x += Math.sin(LK.ticks * 0.02) * 2; } self.shootCooldown++; // Different attack patterns based on phase if (self.health < self.maxHealth * 0.5 && self.phase === 1) { self.phase = 2; self.shootInterval = 30; } if (self.health < self.maxHealth * 0.25 && self.phase === 2 && self.bossLevel >= 2) { self.phase = 3; self.shootInterval = 20; } if (self.health < self.maxHealth * 0.1 && self.phase === 3 && self.bossLevel >= 4) { self.phase = 4; self.shootInterval = 15; } if (self.shootCooldown >= self.shootInterval) { self.shootCooldown = 0; return self.phase; // Return phase for different bullet patterns } return 0; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { return true; // Boss destroyed } tween(bossGraphics, { tint: 0xffffff }, { duration: 100, onFinish: function onFinish() { bossGraphics.tint = 0xffffff; } }); bossGraphics.tint = 0xff0000; return false; }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.enemyType = 'basic'; self.health = 2; self.speed = -2; self.shootCooldown = 0; self.shootInterval = 120; self.points = 100; self.pattern = 0; self.setType = function (type) { self.enemyType = type; var stats = enemyTypesPerLevel[type]; self.health = stats.health; self.speed = stats.speed; self.shootInterval = stats.shootInterval; enemyGraphics.tint = stats.color; self.points = stats.health * 50; }; self.update = function () { self.x += self.speed; // Different movement patterns for different enemy types if (self.enemyType === 'fast') { self.y += Math.sin(self.x * 0.01) * 2; } else if (self.enemyType === 'sniper' && self.x < 1500) { self.speed = 0; } self.shootCooldown++; if (self.shootCooldown >= self.shootInterval) { self.shootCooldown = 0; return true; // Signal to shoot } return false; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { return true; // Enemy destroyed } tween(enemyGraphics, { tint: 0xffffff }, { duration: 100, onFinish: function onFinish() { enemyGraphics.tint = enemyTypesPerLevel[self.enemyType].color; } }); enemyGraphics.tint = 0xff0000; return false; }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = -8; self.speedY = 0; self.canBeParried = false; self.parried = false; self.update = function () { self.x += self.speedX; self.y += self.speedY; // Make bullets parryable when they're pink if (!self.canBeParried && Math.random() < 0.3) { self.canBeParried = true; bulletGraphics.tint = 0xff00ff; } }; return self; }); var Hero = Container.expand(function () { var self = Container.call(this); var heroGraphics = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.hand = self.attachAsset('hand', { anchorX: 0.5, anchorY: 0.5, x: 270, y: -5 }); self.handBaseY = -15; self.handAnimating = false; self.health = 3; self.maxHealth = 3; self.shootCooldown = 0; self.shootInterval = 20; self.invulnerable = 0; self.powerupTime = 0; self.powerupType = null; self.weaponType = 0; self.chargeTime = 0; self.isParrying = false; self.parryIndicator = null; self.originalTint = 0xffffff; self.isPowerupFlashing = false; self.perryCharges = 3; // Start with 3 perrys self.perryEffect = null; self.perryActive = false; self.perryCooldown = 0; self.perryMaxCooldown = 300; // 5 seconds at 60fps self.tapIndicator = null; self.tapBg = null; self.shieldActive = false; self.shieldTime = 0; self.update = function () { if (self.invulnerable > 0) { self.invulnerable--; self.alpha = Math.sin(self.invulnerable * 0.3) > 0 ? 1 : 0.5; } else { self.alpha = 1; } if (self.powerupTime > 0) { self.powerupTime--; // Flash between powerup color and original color in the last 2 seconds if (self.powerupTime <= 120 && !self.isPowerupFlashing) { self.isPowerupFlashing = true; self.startPowerupFlash(); } if (self.powerupTime === 0) { self.powerupType = null; self.shootInterval = 20; heroGraphics.tint = self.originalTint; self.isPowerupFlashing = false; } } self.shootCooldown++; // Update charge for charge weapon if (self.weaponType === 2 && self.chargeTime > 0) { self.chargeTime--; } // Update perry cooldown (weapon-specific for level 10 weapon) if (self.perryCooldown > 0) { self.perryCooldown--; } // Update shield timer if (self.shieldTime > 0) { self.shieldTime--; if (self.shieldTime === 0) { self.shieldActive = false; } } }; self.takeDamage = function () { if (self.invulnerable > 0) { return false; } // Check if shield is active and blocks damage if (self.shieldActive) { self.shieldActive = false; self.shieldTime = 0; LK.effects.flashObject(self, 0x00ff00, 300); return false; // Damage blocked by shield } self.health--; self.invulnerable = 120; // 2 seconds of invulnerability if (self.health <= 0) { return true; // Hero destroyed } LK.effects.flashObject(self, 0xff0000, 500); return false; }; self.canShoot = function () { var weapon = weaponTypes[self.weaponType]; var interval = self.shootInterval; if (weapon.fireRate) { interval = weapon.fireRate; } if (self.shootCooldown >= interval) { self.shootCooldown = 0; return true; } return false; }; self.applyPowerup = function (type) { self.powerupType = type; self.powerupTime = 600; // 10 seconds self.isPowerupFlashing = false; // Change hero color based on powerup type var powerupColor = type === 'rapid' ? 0xff00ff : 0x00ffff; heroGraphics.tint = powerupColor; if (type === 'rapid') { self.shootInterval = 10; } }; self.startPowerupFlash = function () { var flashCount = 0; var flashInterval = LK.setInterval(function () { flashCount++; if (flashCount % 2 === 0) { heroGraphics.tint = self.originalTint; } else { var powerupColor = self.powerupType === 'rapid' ? 0xff00ff : 0x00ffff; heroGraphics.tint = powerupColor; } if (self.powerupTime === 0) { LK.clearInterval(flashInterval); heroGraphics.tint = self.originalTint; } }, 200); // Flash every 200ms }; self.setWeapon = function (weaponIndex) { self.weaponType = weaponIndex; }; self.startParry = function () { if (!self.isParrying && !self.parryIndicator) { self.isParrying = true; self.parryIndicator = self.attachAsset('parryIndicator', { anchorX: 0.5, anchorY: 0.5 }); self.parryIndicator.alpha = 0.5; self.parryIndicator.tint = weaponTypes[self.weaponType].parryColor; } }; self.endParry = function () { self.isParrying = false; if (self.parryIndicator) { self.parryIndicator.destroy(); self.parryIndicator = null; } }; self.animateHandShoot = function () { if (!self.handAnimating) { self.handAnimating = true; tween(self.hand, { y: self.handBaseY - 20 }, { duration: 100, onFinish: function onFinish() { tween(self.hand, { y: self.handBaseY }, { duration: 100, onFinish: function onFinish() { self.handAnimating = false; } }); } }); } }; self.activatePerry = function () { if (self.perryCharges > 0 && !self.perryActive && self.perryCooldown === 0) { self.perryCharges--; self.perryActive = true; // Set weapon-specific cooldown (4.5 seconds for Homing weapon at 60fps) if (self.weaponType === 3) { // Homing weapon self.perryCooldown = 270; // 4.5 seconds at 60fps } else { self.perryCooldown = self.perryMaxCooldown; // Default cooldown } var weapon = weaponTypes[self.weaponType]; // Create perry effect visual self.perryEffect = self.attachAsset('perryEffect', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); self.perryEffect.tint = weapon.parryColor; // Weapon-specific animations and effects if (weapon.name === 'Spread') { // Freeze all enemies briefly self.performSpreadPerry(); } else if (weapon.name === 'Rapid') { // Multi-hit explosion self.performRapidPerry(); } else if (weapon.name === 'Charge') { // Piercing beam attack self.performChargePerry(); } else if (weapon.name === 'Homing') { // Healing wave self.performHomingPerry(); } else if (weapon.name === 'Bounce') { // Multi-directional attack self.performBouncePerry(); } // Animate perry effect tween(self.perryEffect, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 1000, onFinish: function onFinish() { if (self.perryEffect) { self.perryEffect.destroy(); self.perryEffect = null; } self.perryActive = false; } }); // Hand animation for perry tween(self.hand, { scaleX: 1.5, scaleY: 1.5, rotation: Math.PI / 4 }, { duration: 200, onFinish: function onFinish() { tween(self.hand, { scaleX: 1, scaleY: 1, rotation: 0 }, { duration: 200 }); } }); } }; self.performSpreadPerry = function () { // Freeze all enemies for 2 seconds for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var originalSpeed = enemy.speed; enemy.speed = 0; tween(enemy, { tint: 0x00ffff }, { duration: 2000, onFinish: function onFinish() { enemy.speed = originalSpeed; enemy.tint = enemyTypesPerLevel[enemy.enemyType].color; } }); } if (boss) { var originalSpeed = boss.speed; boss.speed = 0; tween(boss, { tint: 0x00ffff }, { duration: 2000, onFinish: function onFinish() { boss.speed = originalSpeed; boss.tint = 0xffffff; } }); } }; self.performRapidPerry = function () { // Damage all enemies with explosion effect LK.effects.flashScreen(0xff00ff, 500); for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.takeDamage(3)) { LK.setScore(LK.getScore() + enemy.points * 2); spawnPowerup(enemy.x, enemy.y); enemy.destroy(); enemies.splice(i, 1); } } if (boss) { boss.takeDamage(5); } }; self.performChargePerry = function () { // Create piercing beam that hits all enemies in line var beamY = self.y; for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (Math.abs(enemy.y - beamY) < 50) { if (enemy.takeDamage(4)) { LK.setScore(LK.getScore() + enemy.points * 2); spawnPowerup(enemy.x, enemy.y); enemy.destroy(); enemies.splice(i, 1); } } } if (boss && Math.abs(boss.y - beamY) < 75) { boss.takeDamage(8); } // Visual beam effect LK.effects.flashScreen(0xffff00, 300); }; self.performHomingPerry = function () { // Activate shield for 1.5 seconds self.shieldActive = true; self.shieldTime = 90; // 1.5 seconds at 60fps // Visual shield effect LK.effects.flashObject(self, 0x00ff00, 1500); // Damage nearby enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2)); if (distance < 300) { if (enemy.takeDamage(2)) { LK.setScore(LK.getScore() + enemy.points); spawnPowerup(enemy.x, enemy.y); enemy.destroy(); enemies.splice(i, 1); } } } if (boss) { var distance = Math.sqrt(Math.pow(boss.x - self.x, 2) + Math.pow(boss.y - self.y, 2)); if (distance < 400) { boss.takeDamage(3); } } }; self.performBouncePerry = function () { // Create multiple bouncing attacks for (var angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) { var bullet = new HeroBullet(); bullet.x = self.x; bullet.y = self.y; bullet.speed = 12; bullet.damage = 3; bullet.bounces = 3; bullet.speedX = Math.cos(angle) * bullet.speed; bullet.speedY = Math.sin(angle) * bullet.speed; bullet.setWeaponType(4, weaponTypes[4]); heroBullets.push(bullet); game.addChild(bullet); } }; self.collectPerry = function () { self.perryCharges++; }; self.down = function (x, y, obj) { // Create tap indicator text with background if (!self.tapIndicator) { // Create background for tap text self.tapBg = self.attachAsset('textBg', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -120, scaleX: 0.6, scaleY: 0.6, alpha: 0.9 }); // Create tap text self.tapIndicator = new Text2('Tap!', { size: 45, fill: 0xffff00 }); self.tapIndicator.anchor.set(0.5, 0.5); self.tapIndicator.x = 0; self.tapIndicator.y = -120; self.addChild(self.tapIndicator); // Animate text with tween tween(self.tapIndicator, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(self.tapIndicator, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeInOut }); } }); // Activate parry and remove tap indicator self.activatePerry(); perryTxt.setText('Perry: ' + self.perryCharges); // Remove tap indicator and background after animation LK.setTimeout(function () { if (self.tapIndicator) { tween(self.tapIndicator, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { self.tapIndicator.destroy(); self.tapIndicator = null; } }); } if (self.tapBg) { tween(self.tapBg, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { self.tapBg.destroy(); self.tapBg = null; } }); } }, 500); } }; return self; }); var HeroBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('heroBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 15; self.damage = 1; self.weaponType = 0; self.target = null; self.bounces = 0; self.pierce = false; self.setWeaponType = function (type, weapon) { self.weaponType = type; bulletGraphics.tint = weapon.parryColor; if (type === 2) { // Charge weapon self.damage = 2; bulletGraphics.scale.x = 1.5; bulletGraphics.scale.y = 1.5; } else if (type === 4) { // Bounce weapon self.bounces = weapon.bounceCount; } }; self.update = function () { if (self.weaponType === 3 && self.target && !self.target.destroyed) { // Homing var angle = Math.atan2(self.target.y - self.y, self.target.x - self.x); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } else if (self.speedX !== undefined && self.speedY !== undefined) { // Perry bouncing bullets self.x += self.speedX; self.y += self.speedY; // Bounce off screen edges if (self.bounces > 0 && (self.y < 100 || self.y > 2632)) { self.speedY *= -1; self.bounces--; } } else { self.x += self.speed; } }; return self; }); var PerryCollectible = Container.expand(function () { var self = Container.call(this); var perryGraphics = self.attachAsset('perryEffect', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); self.speed = -3; self.update = function () { self.x += self.speed; self.rotation += 0.03; // Floating animation self.y += Math.sin(LK.ticks * 0.05) * 0.5; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.type = Math.random() > 0.5 ? 'rapid' : 'triple'; self.speed = -3; self.update = function () { self.x += self.speed; self.rotation += 0.05; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ game.setBackgroundColor(0x1a1a2e); // Background layers var backgroundLayer1 = LK.getAsset('background1', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 1.0 }); game.addChild(backgroundLayer1); var backgroundLayer2 = LK.getAsset('background2', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 1.0 }); game.addChild(backgroundLayer2); // Clouds array for parallax effect var clouds = []; for (var i = 0; i < 8; i++) { var cloud = LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: 200 + Math.random() * 1000, alpha: 0.1 + Math.random() * 0.1 }); clouds.push(cloud); game.addChild(cloud); } // Game variables var hero; var heroBullets = []; var enemies = []; var enemyBullets = []; var powerups = []; var perryCollectibles = []; var boss = null; var currentLevel = 9; var enemiesSpawned = 0; var enemiesPerLevel = 10; var spawnCooldown = 0; var gameStarted = false; var isDragging = false; var dragOffsetY = 0; var parryWindow = false; var parryWindowTimer = 0; // Weapon types with different parry mechanics var weaponTypes = [{ name: 'Spread', bulletCount: 3, fireRate: 25, parryColor: 0x00ffff, parryEffect: 'freeze' }, { name: 'Rapid', fireRate: 15, parryColor: 0xff00ff, parryEffect: 'explode' }, { name: 'Charge', chargeTime: 45, fireRate: 60, parryColor: 0xffff00, parryEffect: 'pierce' }, { name: 'Homing', homingStrength: 0.1, fireRate: 30, parryColor: 0x00ff00, parryEffect: 'heal' }, { name: 'Bounce', bounceCount: 2, fireRate: 35, parryColor: 0xff0000, parryEffect: 'multi' }]; var currentWeapon = 0; // Enemy types per level range (base values) var enemyTypesBase = { basic: { health: 2, speed: -2, shootInterval: 120, color: 0xff4444 }, fast: { health: 1, speed: -4, shootInterval: 80, color: 0xff8844 }, heavy: { health: 4, speed: -1, shootInterval: 150, color: 0x884444 }, sniper: { health: 2, speed: -1.5, shootInterval: 180, color: 0x4444ff }, burst: { health: 3, speed: -2.5, shootInterval: 100, color: 0xff44ff } }; // Current enemy stats (will be modified based on weapon changes) var enemyTypesPerLevel = { basic: { health: 2, speed: -2, shootInterval: 120, color: 0xff4444 }, fast: { health: 1, speed: -4, shootInterval: 80, color: 0xff8844 }, heavy: { health: 4, speed: -1, shootInterval: 150, color: 0x884444 }, sniper: { health: 2, speed: -1.5, shootInterval: 180, color: 0x4444ff }, burst: { health: 3, speed: -2.5, shootInterval: 100, color: 0xff44ff } }; // Ground var ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - 100 }); game.addChild(ground); // Hero hero = new Hero(); hero.x = 200; hero.y = 2732 - 200; game.addChild(hero); // UI Elements var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 20; LK.gui.top.addChild(scoreTxt); // Health display var healthContainer = new Container(); LK.gui.topRight.addChild(healthContainer); healthContainer.x = -120; healthContainer.y = 40; var healthBarBg = LK.getAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthContainer.addChild(healthBarBg); var healthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0.5, x: -100 }); healthContainer.addChild(healthBar); // Level text background var levelBg = LK.getAsset('levelBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); LK.gui.center.addChild(levelBg); // Level text var levelTxt = new Text2('Level 1', { size: 80, fill: 0xFFFF00 }); levelTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(levelTxt); levelTxt.alpha = 0; // Weapon text background var weaponBg = LK.getAsset('textBg', { anchorX: 0, anchorY: 0, x: 20, y: 120, alpha: 1.0, scaleX: 1.3 }); LK.gui.topLeft.addChild(weaponBg); // Weapon text var weaponTxt = new Text2('Weapon: Spread', { size: 50, fill: 0xFFFFFF }); weaponTxt.anchor.set(0, 0); weaponTxt.x = 35; weaponTxt.y = 145; LK.gui.topLeft.addChild(weaponTxt); // Perry charges text background var perryBg = LK.getAsset('textBg', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0 }); LK.gui.topLeft.addChild(perryBg); // Perry charges text var perryTxt = new Text2('Perry: 3', { size: 45, fill: 0xff6600 }); perryTxt.anchor.set(0, 0); perryTxt.x = 35; perryTxt.y = 205; LK.gui.topLeft.addChild(perryTxt); // Perry cooldown text var perryCooldownTxt = new Text2('', { size: 35, fill: 0xffaa00 }); perryCooldownTxt.anchor.set(0, 0); perryCooldownTxt.x = 20; perryCooldownTxt.y = 270; LK.gui.topLeft.addChild(perryCooldownTxt); // Touch controls game.down = function (x, y, obj) { var heroGlobalPos = hero.toGlobal({ x: 0, y: 0 }); var localPos = game.toLocal(heroGlobalPos); if (Math.abs(x - localPos.x) < 100 && Math.abs(y - localPos.y) < 100) { isDragging = true; dragOffsetY = y - hero.y; gameStarted = true; } else if (x > 1024) { // Right side tap for parry parryWindow = true; parryWindowTimer = 20; // 1/3 second parry window hero.startParry(); } }; game.up = function (x, y, obj) { isDragging = false; }; game.move = function (x, y, obj) { if (isDragging) { hero.y = Math.max(300, Math.min(2732 - 200, y - dragOffsetY)); } }; // Helper functions function spawnEnemy() { var enemy = new Enemy(); enemy.x = 2048 + 50; enemy.y = 300 + Math.random() * (2732 - 600); // Choose enemy type based on level var types = ['basic']; if (currentLevel >= 5) { types.push('fast'); } if (currentLevel >= 10) { types.push('heavy'); } if (currentLevel >= 15) { types.push('sniper'); } if (currentLevel >= 20) { types.push('burst'); } var randomType = types[Math.floor(Math.random() * types.length)]; enemy.setType(randomType); enemies.push(enemy); game.addChild(enemy); } function spawnBoss() { boss = new Boss(); boss.x = 2048 - 200; boss.y = 450; boss.setBossLevel(currentLevel); game.addChild(boss); // Show boss warning levelTxt.setText('BOSS LEVEL ' + currentLevel + '!'); levelTxt.alpha = 1; levelBg.alpha = 0.9; tween(levelTxt, { alpha: 0 }, { duration: 2000 }); tween(levelBg, { alpha: 0 }, { duration: 2000 }); } function spawnPowerup(x, y) { if (Math.random() < 0.08) { // 8% chance for perry collectible var perryCollectible = new PerryCollectible(); perryCollectible.x = x; perryCollectible.y = y; perryCollectibles.push(perryCollectible); game.addChild(perryCollectible); } else if (Math.random() < 0.1) { // 10% chance for regular powerup var powerup = new PowerUp(); powerup.x = x; powerup.y = y; powerups.push(powerup); game.addChild(powerup); } } function shootHeroBullet() { var weapon = weaponTypes[hero.weaponType]; hero.animateHandShoot(); var bulletStartX = hero.x + hero.hand.x; var bulletStartY = hero.y + hero.hand.y; if (hero.weaponType === 0 || hero.powerupType === 'triple') { // Spread weapon // Triple shot for (var i = -1; i <= 1; i++) { var bullet = new HeroBullet(); bullet.x = bulletStartX; bullet.y = bulletStartY + i * 30; bullet.setWeaponType(hero.weaponType, weapon); if (hero.weaponType === 3) { // Homing bullet.target = enemies.length > 0 ? enemies[0] : boss; } heroBullets.push(bullet); game.addChild(bullet); } } else if (hero.weaponType === 2) { // Charge weapon if (hero.chargeTime <= 0) { var bullet = new HeroBullet(); bullet.x = bulletStartX; bullet.y = bulletStartY; bullet.setWeaponType(hero.weaponType, weapon); heroBullets.push(bullet); game.addChild(bullet); hero.chargeTime = weapon.chargeTime; } } else { // Normal shot var bullet = new HeroBullet(); bullet.x = bulletStartX; bullet.y = bulletStartY; bullet.setWeaponType(hero.weaponType, weapon); if (hero.weaponType === 3) { // Homing bullet.target = enemies.length > 0 ? enemies[0] : boss; } heroBullets.push(bullet); game.addChild(bullet); } LK.getSound('shoot').play(); } function shootEnemyBullet(enemy) { var bullet = new EnemyBullet(); bullet.x = enemy.x - 30; bullet.y = enemy.y; enemyBullets.push(bullet); game.addChild(bullet); LK.getSound('enemyShoot').play(); } function shootBossBullet(phase) { if (phase === 1) { // Single aimed shot var bullet = new EnemyBullet(); bullet.x = boss.x - 75; bullet.y = boss.y; var angle = Math.atan2(hero.y - boss.y, hero.x - boss.x); bullet.speedX = Math.cos(angle) * 10; bullet.speedY = Math.sin(angle) * 10; enemyBullets.push(bullet); game.addChild(bullet); } else if (phase === 2) { // Spread shot for (var i = -2; i <= 2; i++) { var bullet = new EnemyBullet(); bullet.x = boss.x - 75; bullet.y = boss.y; bullet.speedX = -8; bullet.speedY = i * 2; enemyBullets.push(bullet); game.addChild(bullet); } } else if (phase === 3) { // Circular burst for (var angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) { var bullet = new EnemyBullet(); bullet.x = boss.x - 75; bullet.y = boss.y; bullet.speedX = Math.cos(angle) * 6; bullet.speedY = Math.sin(angle) * 6; enemyBullets.push(bullet); game.addChild(bullet); } } else if (phase === 4) { // Rapid multi-directional chaos for (var i = 0; i < 8; i++) { var bullet = new EnemyBullet(); bullet.x = boss.x - 75; bullet.y = boss.y; var angle = Math.random() * Math.PI * 2; bullet.speedX = Math.cos(angle) * (8 + Math.random() * 4); bullet.speedY = Math.sin(angle) * (8 + Math.random() * 4); enemyBullets.push(bullet); game.addChild(bullet); } } LK.getSound('enemyShoot').play(); } // Game update game.update = function () { if (!gameStarted) { return; } // Update health bar healthBar.scale.x = hero.health / hero.maxHealth; // Update perry cooldown display if (hero.perryCooldown > 0) { var cooldownSeconds = Math.ceil(hero.perryCooldown / 60); perryCooldownTxt.setText('Cooldown: ' + cooldownSeconds + 's'); } else { perryCooldownTxt.setText(''); } // Update background clouds for (var i = 0; i < clouds.length; i++) { clouds[i].x -= 0.5; if (clouds[i].x < -100) { clouds[i].x = 2048 + 100; clouds[i].y = 200 + Math.random() * 1000; } } // Spawn enemies if (!boss && enemiesSpawned < enemiesPerLevel) { spawnCooldown++; var spawnRate = Math.max(60, 120 - currentLevel * 2); // Faster spawns at higher levels if (spawnCooldown >= spawnRate) { spawnCooldown = 0; spawnEnemy(); enemiesSpawned++; } } // Update parry window if (parryWindow && parryWindowTimer > 0) { parryWindowTimer--; if (parryWindowTimer === 0) { parryWindow = false; hero.endParry(); } } // Check for next level if (!boss && enemiesSpawned >= enemiesPerLevel && enemies.length === 0) { currentLevel++; enemiesSpawned = 0; enemiesPerLevel = Math.min(20, 10 + Math.floor(currentLevel / 5)); // Change weapon every 3 levels if ((currentLevel - 1) % 3 === 0 && currentLevel > 1) { var newWeaponIndex = Math.floor((currentLevel - 1) / 3) % weaponTypes.length; hero.setWeapon(newWeaponIndex); weaponTxt.setText('Weapon: ' + weaponTypes[hero.weaponType].name); // Make enemies stronger when weapon changes var strengthMultiplier = 1 + Math.floor((currentLevel - 1) / 3) * 0.3; for (var enemyType in enemyTypesPerLevel) { enemyTypesPerLevel[enemyType].health = Math.floor(enemyTypesPerLevel[enemyType].health * strengthMultiplier); enemyTypesPerLevel[enemyType].speed = enemyTypesPerLevel[enemyType].speed * (1 + strengthMultiplier * 0.1); } } if (currentLevel > 50) { // Win condition LK.showYouWin(); } else if (currentLevel % 10 === 0) { // Boss level spawnBoss(); } else { // Normal level levelTxt.setText('Level ' + currentLevel); levelTxt.alpha = 1; levelBg.alpha = 0.9; tween(levelTxt, { alpha: 0 }, { duration: 2000 }); tween(levelBg, { alpha: 0 }, { duration: 2000 }); } } // Hero shooting if (hero.canShoot()) { shootHeroBullet(); } // Update hero bullets for (var i = heroBullets.length - 1; i >= 0; i--) { var bullet = heroBullets[i]; if (bullet.x > 2048 + 50) { bullet.destroy(); heroBullets.splice(i, 1); continue; } // Check enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { if (enemy.takeDamage(bullet.damage)) { // Enemy destroyed LK.setScore(LK.getScore() + enemy.points); scoreTxt.setText('Score: ' + LK.getScore()); spawnPowerup(enemy.x, enemy.y); enemy.destroy(); enemies.splice(j, 1); LK.getSound('enemyDestroy').play(); } bullet.destroy(); heroBullets.splice(i, 1); break; } } // Check boss collision if (boss && bullet.intersects(boss)) { if (boss.takeDamage(bullet.damage)) { // Boss destroyed LK.setScore(LK.getScore() + boss.points); scoreTxt.setText('Score: ' + LK.getScore()); spawnPowerup(boss.x, boss.y); boss.destroy(); boss = null; LK.getSound('enemyDestroy').play(); } bullet.destroy(); heroBullets.splice(i, 1); } } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.x < -50) { enemy.destroy(); enemies.splice(i, 1); continue; } if (enemy.update()) { shootEnemyBullet(enemy); } // Check collision with hero if (enemy.intersects(hero)) { if (hero.takeDamage()) { LK.showGameOver(); } enemy.destroy(); enemies.splice(i, 1); LK.getSound('hit').play(); } } // Update boss if (boss) { var phase = boss.update(); if (phase > 0) { shootBossBullet(phase); } // Check collision with hero if (boss.intersects(hero)) { if (hero.takeDamage()) { LK.showGameOver(); } LK.getSound('hit').play(); } } // Update enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (bullet.x < -50 || bullet.x > 2048 + 50 || bullet.y < -50 || bullet.y > 2732 + 50) { bullet.destroy(); enemyBullets.splice(i, 1); continue; } // Check collision with hero if (bullet.intersects(hero)) { if (parryWindow && bullet.canBeParried && !bullet.parried) { // Successful parry! bullet.parried = true; bullet.speedX *= -2; bullet.speedY *= -0.5; bullet.tint = weaponTypes[hero.weaponType].parryColor; LK.setScore(LK.getScore() + 50); scoreTxt.setText('Score: ' + LK.getScore()); // Apply parry effect var effect = weaponTypes[hero.weaponType].parryEffect; if (effect === 'heal' && hero.health < hero.maxHealth) { hero.health++; } else if (effect === 'explode') { LK.effects.flashScreen(weaponTypes[hero.weaponType].parryColor, 200); // Damage all enemies for (var j = 0; j < enemies.length; j++) { enemies[j].takeDamage(1); } } else if (effect === 'freeze') { // Slow all enemies temporarily for (var j = 0; j < enemies.length; j++) { enemies[j].speed *= 0.5; } } } else if (!bullet.parried) { if (hero.takeDamage()) { LK.showGameOver(); } bullet.destroy(); enemyBullets.splice(i, 1); LK.getSound('hit').play(); } } // Check parried bullets hitting enemies if (bullet.parried) { for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { if (enemy.takeDamage(2)) { LK.setScore(LK.getScore() + enemy.points * 2); scoreTxt.setText('Score: ' + LK.getScore()); spawnPowerup(enemy.x, enemy.y); enemy.destroy(); enemies.splice(j, 1); LK.getSound('enemyDestroy').play(); } bullet.destroy(); enemyBullets.splice(i, 1); break; } } // Check boss collision if (boss && bullet.intersects(boss)) { if (boss.takeDamage(2)) { LK.setScore(LK.getScore() + boss.points); scoreTxt.setText('Score: ' + LK.getScore()); spawnPowerup(boss.x, boss.y); boss.destroy(); boss = null; LK.getSound('enemyDestroy').play(); } bullet.destroy(); enemyBullets.splice(i, 1); } } } // Update powerups for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; if (powerup.x < -50) { powerup.destroy(); powerups.splice(i, 1); continue; } // Check collision with hero if (powerup.intersects(hero)) { hero.applyPowerup(powerup.type); powerup.destroy(); powerups.splice(i, 1); LK.getSound('powerupCollect').play(); } } // Update perry collectibles for (var i = perryCollectibles.length - 1; i >= 0; i--) { var perryCollectible = perryCollectibles[i]; if (perryCollectible.x < -50) { perryCollectible.destroy(); perryCollectibles.splice(i, 1); continue; } // Check collision with hero if (perryCollectible.intersects(hero)) { hero.collectPerry(); perryTxt.setText('Perry: ' + hero.perryCharges); perryCollectible.destroy(); perryCollectibles.splice(i, 1); LK.getSound('powerupCollect').play(); } } }; // Start music LK.playMusic('bgMusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.bossLevel = 1;
self.health = 20;
self.maxHealth = 20;
self.speed = 3;
self.direction = 1;
self.shootCooldown = 0;
self.shootInterval = 45;
self.phase = 1;
self.points = 1000;
self.setBossLevel = function (level) {
self.bossLevel = Math.floor(level / 10);
self.health = 30 + self.bossLevel * 20;
self.maxHealth = self.health;
self.points = 1000 * (self.bossLevel + 1);
bossGraphics.scale.x = 1 + self.bossLevel * 0.2;
bossGraphics.scale.y = 1 + self.bossLevel * 0.2;
};
self.update = function () {
// Boss movement pattern
self.y += self.speed * self.direction;
if (self.y > 600 || self.y < 300) {
self.direction *= -1;
}
// More complex movement for higher level bosses
if (self.bossLevel >= 2) {
self.x += Math.sin(LK.ticks * 0.02) * 2;
}
self.shootCooldown++;
// Different attack patterns based on phase
if (self.health < self.maxHealth * 0.5 && self.phase === 1) {
self.phase = 2;
self.shootInterval = 30;
}
if (self.health < self.maxHealth * 0.25 && self.phase === 2 && self.bossLevel >= 2) {
self.phase = 3;
self.shootInterval = 20;
}
if (self.health < self.maxHealth * 0.1 && self.phase === 3 && self.bossLevel >= 4) {
self.phase = 4;
self.shootInterval = 15;
}
if (self.shootCooldown >= self.shootInterval) {
self.shootCooldown = 0;
return self.phase; // Return phase for different bullet patterns
}
return 0;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
return true; // Boss destroyed
}
tween(bossGraphics, {
tint: 0xffffff
}, {
duration: 100,
onFinish: function onFinish() {
bossGraphics.tint = 0xffffff;
}
});
bossGraphics.tint = 0xff0000;
return false;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.enemyType = 'basic';
self.health = 2;
self.speed = -2;
self.shootCooldown = 0;
self.shootInterval = 120;
self.points = 100;
self.pattern = 0;
self.setType = function (type) {
self.enemyType = type;
var stats = enemyTypesPerLevel[type];
self.health = stats.health;
self.speed = stats.speed;
self.shootInterval = stats.shootInterval;
enemyGraphics.tint = stats.color;
self.points = stats.health * 50;
};
self.update = function () {
self.x += self.speed;
// Different movement patterns for different enemy types
if (self.enemyType === 'fast') {
self.y += Math.sin(self.x * 0.01) * 2;
} else if (self.enemyType === 'sniper' && self.x < 1500) {
self.speed = 0;
}
self.shootCooldown++;
if (self.shootCooldown >= self.shootInterval) {
self.shootCooldown = 0;
return true; // Signal to shoot
}
return false;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
return true; // Enemy destroyed
}
tween(enemyGraphics, {
tint: 0xffffff
}, {
duration: 100,
onFinish: function onFinish() {
enemyGraphics.tint = enemyTypesPerLevel[self.enemyType].color;
}
});
enemyGraphics.tint = 0xff0000;
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = -8;
self.speedY = 0;
self.canBeParried = false;
self.parried = false;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
// Make bullets parryable when they're pink
if (!self.canBeParried && Math.random() < 0.3) {
self.canBeParried = true;
bulletGraphics.tint = 0xff00ff;
}
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.hand = self.attachAsset('hand', {
anchorX: 0.5,
anchorY: 0.5,
x: 270,
y: -5
});
self.handBaseY = -15;
self.handAnimating = false;
self.health = 3;
self.maxHealth = 3;
self.shootCooldown = 0;
self.shootInterval = 20;
self.invulnerable = 0;
self.powerupTime = 0;
self.powerupType = null;
self.weaponType = 0;
self.chargeTime = 0;
self.isParrying = false;
self.parryIndicator = null;
self.originalTint = 0xffffff;
self.isPowerupFlashing = false;
self.perryCharges = 3; // Start with 3 perrys
self.perryEffect = null;
self.perryActive = false;
self.perryCooldown = 0;
self.perryMaxCooldown = 300; // 5 seconds at 60fps
self.tapIndicator = null;
self.tapBg = null;
self.shieldActive = false;
self.shieldTime = 0;
self.update = function () {
if (self.invulnerable > 0) {
self.invulnerable--;
self.alpha = Math.sin(self.invulnerable * 0.3) > 0 ? 1 : 0.5;
} else {
self.alpha = 1;
}
if (self.powerupTime > 0) {
self.powerupTime--;
// Flash between powerup color and original color in the last 2 seconds
if (self.powerupTime <= 120 && !self.isPowerupFlashing) {
self.isPowerupFlashing = true;
self.startPowerupFlash();
}
if (self.powerupTime === 0) {
self.powerupType = null;
self.shootInterval = 20;
heroGraphics.tint = self.originalTint;
self.isPowerupFlashing = false;
}
}
self.shootCooldown++;
// Update charge for charge weapon
if (self.weaponType === 2 && self.chargeTime > 0) {
self.chargeTime--;
}
// Update perry cooldown (weapon-specific for level 10 weapon)
if (self.perryCooldown > 0) {
self.perryCooldown--;
}
// Update shield timer
if (self.shieldTime > 0) {
self.shieldTime--;
if (self.shieldTime === 0) {
self.shieldActive = false;
}
}
};
self.takeDamage = function () {
if (self.invulnerable > 0) {
return false;
}
// Check if shield is active and blocks damage
if (self.shieldActive) {
self.shieldActive = false;
self.shieldTime = 0;
LK.effects.flashObject(self, 0x00ff00, 300);
return false; // Damage blocked by shield
}
self.health--;
self.invulnerable = 120; // 2 seconds of invulnerability
if (self.health <= 0) {
return true; // Hero destroyed
}
LK.effects.flashObject(self, 0xff0000, 500);
return false;
};
self.canShoot = function () {
var weapon = weaponTypes[self.weaponType];
var interval = self.shootInterval;
if (weapon.fireRate) {
interval = weapon.fireRate;
}
if (self.shootCooldown >= interval) {
self.shootCooldown = 0;
return true;
}
return false;
};
self.applyPowerup = function (type) {
self.powerupType = type;
self.powerupTime = 600; // 10 seconds
self.isPowerupFlashing = false;
// Change hero color based on powerup type
var powerupColor = type === 'rapid' ? 0xff00ff : 0x00ffff;
heroGraphics.tint = powerupColor;
if (type === 'rapid') {
self.shootInterval = 10;
}
};
self.startPowerupFlash = function () {
var flashCount = 0;
var flashInterval = LK.setInterval(function () {
flashCount++;
if (flashCount % 2 === 0) {
heroGraphics.tint = self.originalTint;
} else {
var powerupColor = self.powerupType === 'rapid' ? 0xff00ff : 0x00ffff;
heroGraphics.tint = powerupColor;
}
if (self.powerupTime === 0) {
LK.clearInterval(flashInterval);
heroGraphics.tint = self.originalTint;
}
}, 200); // Flash every 200ms
};
self.setWeapon = function (weaponIndex) {
self.weaponType = weaponIndex;
};
self.startParry = function () {
if (!self.isParrying && !self.parryIndicator) {
self.isParrying = true;
self.parryIndicator = self.attachAsset('parryIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
self.parryIndicator.alpha = 0.5;
self.parryIndicator.tint = weaponTypes[self.weaponType].parryColor;
}
};
self.endParry = function () {
self.isParrying = false;
if (self.parryIndicator) {
self.parryIndicator.destroy();
self.parryIndicator = null;
}
};
self.animateHandShoot = function () {
if (!self.handAnimating) {
self.handAnimating = true;
tween(self.hand, {
y: self.handBaseY - 20
}, {
duration: 100,
onFinish: function onFinish() {
tween(self.hand, {
y: self.handBaseY
}, {
duration: 100,
onFinish: function onFinish() {
self.handAnimating = false;
}
});
}
});
}
};
self.activatePerry = function () {
if (self.perryCharges > 0 && !self.perryActive && self.perryCooldown === 0) {
self.perryCharges--;
self.perryActive = true;
// Set weapon-specific cooldown (4.5 seconds for Homing weapon at 60fps)
if (self.weaponType === 3) {
// Homing weapon
self.perryCooldown = 270; // 4.5 seconds at 60fps
} else {
self.perryCooldown = self.perryMaxCooldown; // Default cooldown
}
var weapon = weaponTypes[self.weaponType];
// Create perry effect visual
self.perryEffect = self.attachAsset('perryEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.perryEffect.tint = weapon.parryColor;
// Weapon-specific animations and effects
if (weapon.name === 'Spread') {
// Freeze all enemies briefly
self.performSpreadPerry();
} else if (weapon.name === 'Rapid') {
// Multi-hit explosion
self.performRapidPerry();
} else if (weapon.name === 'Charge') {
// Piercing beam attack
self.performChargePerry();
} else if (weapon.name === 'Homing') {
// Healing wave
self.performHomingPerry();
} else if (weapon.name === 'Bounce') {
// Multi-directional attack
self.performBouncePerry();
}
// Animate perry effect
tween(self.perryEffect, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
if (self.perryEffect) {
self.perryEffect.destroy();
self.perryEffect = null;
}
self.perryActive = false;
}
});
// Hand animation for perry
tween(self.hand, {
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI / 4
}, {
duration: 200,
onFinish: function onFinish() {
tween(self.hand, {
scaleX: 1,
scaleY: 1,
rotation: 0
}, {
duration: 200
});
}
});
}
};
self.performSpreadPerry = function () {
// Freeze all enemies for 2 seconds
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var originalSpeed = enemy.speed;
enemy.speed = 0;
tween(enemy, {
tint: 0x00ffff
}, {
duration: 2000,
onFinish: function onFinish() {
enemy.speed = originalSpeed;
enemy.tint = enemyTypesPerLevel[enemy.enemyType].color;
}
});
}
if (boss) {
var originalSpeed = boss.speed;
boss.speed = 0;
tween(boss, {
tint: 0x00ffff
}, {
duration: 2000,
onFinish: function onFinish() {
boss.speed = originalSpeed;
boss.tint = 0xffffff;
}
});
}
};
self.performRapidPerry = function () {
// Damage all enemies with explosion effect
LK.effects.flashScreen(0xff00ff, 500);
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.takeDamage(3)) {
LK.setScore(LK.getScore() + enemy.points * 2);
spawnPowerup(enemy.x, enemy.y);
enemy.destroy();
enemies.splice(i, 1);
}
}
if (boss) {
boss.takeDamage(5);
}
};
self.performChargePerry = function () {
// Create piercing beam that hits all enemies in line
var beamY = self.y;
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (Math.abs(enemy.y - beamY) < 50) {
if (enemy.takeDamage(4)) {
LK.setScore(LK.getScore() + enemy.points * 2);
spawnPowerup(enemy.x, enemy.y);
enemy.destroy();
enemies.splice(i, 1);
}
}
}
if (boss && Math.abs(boss.y - beamY) < 75) {
boss.takeDamage(8);
}
// Visual beam effect
LK.effects.flashScreen(0xffff00, 300);
};
self.performHomingPerry = function () {
// Activate shield for 1.5 seconds
self.shieldActive = true;
self.shieldTime = 90; // 1.5 seconds at 60fps
// Visual shield effect
LK.effects.flashObject(self, 0x00ff00, 1500);
// Damage nearby enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance < 300) {
if (enemy.takeDamage(2)) {
LK.setScore(LK.getScore() + enemy.points);
spawnPowerup(enemy.x, enemy.y);
enemy.destroy();
enemies.splice(i, 1);
}
}
}
if (boss) {
var distance = Math.sqrt(Math.pow(boss.x - self.x, 2) + Math.pow(boss.y - self.y, 2));
if (distance < 400) {
boss.takeDamage(3);
}
}
};
self.performBouncePerry = function () {
// Create multiple bouncing attacks
for (var angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) {
var bullet = new HeroBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.speed = 12;
bullet.damage = 3;
bullet.bounces = 3;
bullet.speedX = Math.cos(angle) * bullet.speed;
bullet.speedY = Math.sin(angle) * bullet.speed;
bullet.setWeaponType(4, weaponTypes[4]);
heroBullets.push(bullet);
game.addChild(bullet);
}
};
self.collectPerry = function () {
self.perryCharges++;
};
self.down = function (x, y, obj) {
// Create tap indicator text with background
if (!self.tapIndicator) {
// Create background for tap text
self.tapBg = self.attachAsset('textBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -120,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0.9
});
// Create tap text
self.tapIndicator = new Text2('Tap!', {
size: 45,
fill: 0xffff00
});
self.tapIndicator.anchor.set(0.5, 0.5);
self.tapIndicator.x = 0;
self.tapIndicator.y = -120;
self.addChild(self.tapIndicator);
// Animate text with tween
tween(self.tapIndicator, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self.tapIndicator, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
// Activate parry and remove tap indicator
self.activatePerry();
perryTxt.setText('Perry: ' + self.perryCharges);
// Remove tap indicator and background after animation
LK.setTimeout(function () {
if (self.tapIndicator) {
tween(self.tapIndicator, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.tapIndicator.destroy();
self.tapIndicator = null;
}
});
}
if (self.tapBg) {
tween(self.tapBg, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.tapBg.destroy();
self.tapBg = null;
}
});
}
}, 500);
}
};
return self;
});
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = 1;
self.weaponType = 0;
self.target = null;
self.bounces = 0;
self.pierce = false;
self.setWeaponType = function (type, weapon) {
self.weaponType = type;
bulletGraphics.tint = weapon.parryColor;
if (type === 2) {
// Charge weapon
self.damage = 2;
bulletGraphics.scale.x = 1.5;
bulletGraphics.scale.y = 1.5;
} else if (type === 4) {
// Bounce weapon
self.bounces = weapon.bounceCount;
}
};
self.update = function () {
if (self.weaponType === 3 && self.target && !self.target.destroyed) {
// Homing
var angle = Math.atan2(self.target.y - self.y, self.target.x - self.x);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
} else if (self.speedX !== undefined && self.speedY !== undefined) {
// Perry bouncing bullets
self.x += self.speedX;
self.y += self.speedY;
// Bounce off screen edges
if (self.bounces > 0 && (self.y < 100 || self.y > 2632)) {
self.speedY *= -1;
self.bounces--;
}
} else {
self.x += self.speed;
}
};
return self;
});
var PerryCollectible = Container.expand(function () {
var self = Container.call(this);
var perryGraphics = self.attachAsset('perryEffect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
self.speed = -3;
self.update = function () {
self.x += self.speed;
self.rotation += 0.03;
// Floating animation
self.y += Math.sin(LK.ticks * 0.05) * 0.5;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = Math.random() > 0.5 ? 'rapid' : 'triple';
self.speed = -3;
self.update = function () {
self.x += self.speed;
self.rotation += 0.05;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
game.setBackgroundColor(0x1a1a2e);
// Background layers
var backgroundLayer1 = LK.getAsset('background1', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 1.0
});
game.addChild(backgroundLayer1);
var backgroundLayer2 = LK.getAsset('background2', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 1.0
});
game.addChild(backgroundLayer2);
// Clouds array for parallax effect
var clouds = [];
for (var i = 0; i < 8; i++) {
var cloud = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: 200 + Math.random() * 1000,
alpha: 0.1 + Math.random() * 0.1
});
clouds.push(cloud);
game.addChild(cloud);
}
// Game variables
var hero;
var heroBullets = [];
var enemies = [];
var enemyBullets = [];
var powerups = [];
var perryCollectibles = [];
var boss = null;
var currentLevel = 9;
var enemiesSpawned = 0;
var enemiesPerLevel = 10;
var spawnCooldown = 0;
var gameStarted = false;
var isDragging = false;
var dragOffsetY = 0;
var parryWindow = false;
var parryWindowTimer = 0;
// Weapon types with different parry mechanics
var weaponTypes = [{
name: 'Spread',
bulletCount: 3,
fireRate: 25,
parryColor: 0x00ffff,
parryEffect: 'freeze'
}, {
name: 'Rapid',
fireRate: 15,
parryColor: 0xff00ff,
parryEffect: 'explode'
}, {
name: 'Charge',
chargeTime: 45,
fireRate: 60,
parryColor: 0xffff00,
parryEffect: 'pierce'
}, {
name: 'Homing',
homingStrength: 0.1,
fireRate: 30,
parryColor: 0x00ff00,
parryEffect: 'heal'
}, {
name: 'Bounce',
bounceCount: 2,
fireRate: 35,
parryColor: 0xff0000,
parryEffect: 'multi'
}];
var currentWeapon = 0;
// Enemy types per level range (base values)
var enemyTypesBase = {
basic: {
health: 2,
speed: -2,
shootInterval: 120,
color: 0xff4444
},
fast: {
health: 1,
speed: -4,
shootInterval: 80,
color: 0xff8844
},
heavy: {
health: 4,
speed: -1,
shootInterval: 150,
color: 0x884444
},
sniper: {
health: 2,
speed: -1.5,
shootInterval: 180,
color: 0x4444ff
},
burst: {
health: 3,
speed: -2.5,
shootInterval: 100,
color: 0xff44ff
}
};
// Current enemy stats (will be modified based on weapon changes)
var enemyTypesPerLevel = {
basic: {
health: 2,
speed: -2,
shootInterval: 120,
color: 0xff4444
},
fast: {
health: 1,
speed: -4,
shootInterval: 80,
color: 0xff8844
},
heavy: {
health: 4,
speed: -1,
shootInterval: 150,
color: 0x884444
},
sniper: {
health: 2,
speed: -1.5,
shootInterval: 180,
color: 0x4444ff
},
burst: {
health: 3,
speed: -2.5,
shootInterval: 100,
color: 0xff44ff
}
};
// Ground
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - 100
});
game.addChild(ground);
// Hero
hero = new Hero();
hero.x = 200;
hero.y = 2732 - 200;
game.addChild(hero);
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 20;
LK.gui.top.addChild(scoreTxt);
// Health display
var healthContainer = new Container();
LK.gui.topRight.addChild(healthContainer);
healthContainer.x = -120;
healthContainer.y = 40;
var healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthContainer.addChild(healthBarBg);
var healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0.5,
x: -100
});
healthContainer.addChild(healthBar);
// Level text background
var levelBg = LK.getAsset('levelBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
LK.gui.center.addChild(levelBg);
// Level text
var levelTxt = new Text2('Level 1', {
size: 80,
fill: 0xFFFF00
});
levelTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelTxt);
levelTxt.alpha = 0;
// Weapon text background
var weaponBg = LK.getAsset('textBg', {
anchorX: 0,
anchorY: 0,
x: 20,
y: 120,
alpha: 1.0,
scaleX: 1.3
});
LK.gui.topLeft.addChild(weaponBg);
// Weapon text
var weaponTxt = new Text2('Weapon: Spread', {
size: 50,
fill: 0xFFFFFF
});
weaponTxt.anchor.set(0, 0);
weaponTxt.x = 35;
weaponTxt.y = 145;
LK.gui.topLeft.addChild(weaponTxt);
// Perry charges text background
var perryBg = LK.getAsset('textBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0
});
LK.gui.topLeft.addChild(perryBg);
// Perry charges text
var perryTxt = new Text2('Perry: 3', {
size: 45,
fill: 0xff6600
});
perryTxt.anchor.set(0, 0);
perryTxt.x = 35;
perryTxt.y = 205;
LK.gui.topLeft.addChild(perryTxt);
// Perry cooldown text
var perryCooldownTxt = new Text2('', {
size: 35,
fill: 0xffaa00
});
perryCooldownTxt.anchor.set(0, 0);
perryCooldownTxt.x = 20;
perryCooldownTxt.y = 270;
LK.gui.topLeft.addChild(perryCooldownTxt);
// Touch controls
game.down = function (x, y, obj) {
var heroGlobalPos = hero.toGlobal({
x: 0,
y: 0
});
var localPos = game.toLocal(heroGlobalPos);
if (Math.abs(x - localPos.x) < 100 && Math.abs(y - localPos.y) < 100) {
isDragging = true;
dragOffsetY = y - hero.y;
gameStarted = true;
} else if (x > 1024) {
// Right side tap for parry
parryWindow = true;
parryWindowTimer = 20; // 1/3 second parry window
hero.startParry();
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
game.move = function (x, y, obj) {
if (isDragging) {
hero.y = Math.max(300, Math.min(2732 - 200, y - dragOffsetY));
}
};
// Helper functions
function spawnEnemy() {
var enemy = new Enemy();
enemy.x = 2048 + 50;
enemy.y = 300 + Math.random() * (2732 - 600);
// Choose enemy type based on level
var types = ['basic'];
if (currentLevel >= 5) {
types.push('fast');
}
if (currentLevel >= 10) {
types.push('heavy');
}
if (currentLevel >= 15) {
types.push('sniper');
}
if (currentLevel >= 20) {
types.push('burst');
}
var randomType = types[Math.floor(Math.random() * types.length)];
enemy.setType(randomType);
enemies.push(enemy);
game.addChild(enemy);
}
function spawnBoss() {
boss = new Boss();
boss.x = 2048 - 200;
boss.y = 450;
boss.setBossLevel(currentLevel);
game.addChild(boss);
// Show boss warning
levelTxt.setText('BOSS LEVEL ' + currentLevel + '!');
levelTxt.alpha = 1;
levelBg.alpha = 0.9;
tween(levelTxt, {
alpha: 0
}, {
duration: 2000
});
tween(levelBg, {
alpha: 0
}, {
duration: 2000
});
}
function spawnPowerup(x, y) {
if (Math.random() < 0.08) {
// 8% chance for perry collectible
var perryCollectible = new PerryCollectible();
perryCollectible.x = x;
perryCollectible.y = y;
perryCollectibles.push(perryCollectible);
game.addChild(perryCollectible);
} else if (Math.random() < 0.1) {
// 10% chance for regular powerup
var powerup = new PowerUp();
powerup.x = x;
powerup.y = y;
powerups.push(powerup);
game.addChild(powerup);
}
}
function shootHeroBullet() {
var weapon = weaponTypes[hero.weaponType];
hero.animateHandShoot();
var bulletStartX = hero.x + hero.hand.x;
var bulletStartY = hero.y + hero.hand.y;
if (hero.weaponType === 0 || hero.powerupType === 'triple') {
// Spread weapon
// Triple shot
for (var i = -1; i <= 1; i++) {
var bullet = new HeroBullet();
bullet.x = bulletStartX;
bullet.y = bulletStartY + i * 30;
bullet.setWeaponType(hero.weaponType, weapon);
if (hero.weaponType === 3) {
// Homing
bullet.target = enemies.length > 0 ? enemies[0] : boss;
}
heroBullets.push(bullet);
game.addChild(bullet);
}
} else if (hero.weaponType === 2) {
// Charge weapon
if (hero.chargeTime <= 0) {
var bullet = new HeroBullet();
bullet.x = bulletStartX;
bullet.y = bulletStartY;
bullet.setWeaponType(hero.weaponType, weapon);
heroBullets.push(bullet);
game.addChild(bullet);
hero.chargeTime = weapon.chargeTime;
}
} else {
// Normal shot
var bullet = new HeroBullet();
bullet.x = bulletStartX;
bullet.y = bulletStartY;
bullet.setWeaponType(hero.weaponType, weapon);
if (hero.weaponType === 3) {
// Homing
bullet.target = enemies.length > 0 ? enemies[0] : boss;
}
heroBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
}
function shootEnemyBullet(enemy) {
var bullet = new EnemyBullet();
bullet.x = enemy.x - 30;
bullet.y = enemy.y;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('enemyShoot').play();
}
function shootBossBullet(phase) {
if (phase === 1) {
// Single aimed shot
var bullet = new EnemyBullet();
bullet.x = boss.x - 75;
bullet.y = boss.y;
var angle = Math.atan2(hero.y - boss.y, hero.x - boss.x);
bullet.speedX = Math.cos(angle) * 10;
bullet.speedY = Math.sin(angle) * 10;
enemyBullets.push(bullet);
game.addChild(bullet);
} else if (phase === 2) {
// Spread shot
for (var i = -2; i <= 2; i++) {
var bullet = new EnemyBullet();
bullet.x = boss.x - 75;
bullet.y = boss.y;
bullet.speedX = -8;
bullet.speedY = i * 2;
enemyBullets.push(bullet);
game.addChild(bullet);
}
} else if (phase === 3) {
// Circular burst
for (var angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) {
var bullet = new EnemyBullet();
bullet.x = boss.x - 75;
bullet.y = boss.y;
bullet.speedX = Math.cos(angle) * 6;
bullet.speedY = Math.sin(angle) * 6;
enemyBullets.push(bullet);
game.addChild(bullet);
}
} else if (phase === 4) {
// Rapid multi-directional chaos
for (var i = 0; i < 8; i++) {
var bullet = new EnemyBullet();
bullet.x = boss.x - 75;
bullet.y = boss.y;
var angle = Math.random() * Math.PI * 2;
bullet.speedX = Math.cos(angle) * (8 + Math.random() * 4);
bullet.speedY = Math.sin(angle) * (8 + Math.random() * 4);
enemyBullets.push(bullet);
game.addChild(bullet);
}
}
LK.getSound('enemyShoot').play();
}
// Game update
game.update = function () {
if (!gameStarted) {
return;
}
// Update health bar
healthBar.scale.x = hero.health / hero.maxHealth;
// Update perry cooldown display
if (hero.perryCooldown > 0) {
var cooldownSeconds = Math.ceil(hero.perryCooldown / 60);
perryCooldownTxt.setText('Cooldown: ' + cooldownSeconds + 's');
} else {
perryCooldownTxt.setText('');
}
// Update background clouds
for (var i = 0; i < clouds.length; i++) {
clouds[i].x -= 0.5;
if (clouds[i].x < -100) {
clouds[i].x = 2048 + 100;
clouds[i].y = 200 + Math.random() * 1000;
}
}
// Spawn enemies
if (!boss && enemiesSpawned < enemiesPerLevel) {
spawnCooldown++;
var spawnRate = Math.max(60, 120 - currentLevel * 2); // Faster spawns at higher levels
if (spawnCooldown >= spawnRate) {
spawnCooldown = 0;
spawnEnemy();
enemiesSpawned++;
}
}
// Update parry window
if (parryWindow && parryWindowTimer > 0) {
parryWindowTimer--;
if (parryWindowTimer === 0) {
parryWindow = false;
hero.endParry();
}
}
// Check for next level
if (!boss && enemiesSpawned >= enemiesPerLevel && enemies.length === 0) {
currentLevel++;
enemiesSpawned = 0;
enemiesPerLevel = Math.min(20, 10 + Math.floor(currentLevel / 5));
// Change weapon every 3 levels
if ((currentLevel - 1) % 3 === 0 && currentLevel > 1) {
var newWeaponIndex = Math.floor((currentLevel - 1) / 3) % weaponTypes.length;
hero.setWeapon(newWeaponIndex);
weaponTxt.setText('Weapon: ' + weaponTypes[hero.weaponType].name);
// Make enemies stronger when weapon changes
var strengthMultiplier = 1 + Math.floor((currentLevel - 1) / 3) * 0.3;
for (var enemyType in enemyTypesPerLevel) {
enemyTypesPerLevel[enemyType].health = Math.floor(enemyTypesPerLevel[enemyType].health * strengthMultiplier);
enemyTypesPerLevel[enemyType].speed = enemyTypesPerLevel[enemyType].speed * (1 + strengthMultiplier * 0.1);
}
}
if (currentLevel > 50) {
// Win condition
LK.showYouWin();
} else if (currentLevel % 10 === 0) {
// Boss level
spawnBoss();
} else {
// Normal level
levelTxt.setText('Level ' + currentLevel);
levelTxt.alpha = 1;
levelBg.alpha = 0.9;
tween(levelTxt, {
alpha: 0
}, {
duration: 2000
});
tween(levelBg, {
alpha: 0
}, {
duration: 2000
});
}
}
// Hero shooting
if (hero.canShoot()) {
shootHeroBullet();
}
// Update hero bullets
for (var i = heroBullets.length - 1; i >= 0; i--) {
var bullet = heroBullets[i];
if (bullet.x > 2048 + 50) {
bullet.destroy();
heroBullets.splice(i, 1);
continue;
}
// Check enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
// Enemy destroyed
LK.setScore(LK.getScore() + enemy.points);
scoreTxt.setText('Score: ' + LK.getScore());
spawnPowerup(enemy.x, enemy.y);
enemy.destroy();
enemies.splice(j, 1);
LK.getSound('enemyDestroy').play();
}
bullet.destroy();
heroBullets.splice(i, 1);
break;
}
}
// Check boss collision
if (boss && bullet.intersects(boss)) {
if (boss.takeDamage(bullet.damage)) {
// Boss destroyed
LK.setScore(LK.getScore() + boss.points);
scoreTxt.setText('Score: ' + LK.getScore());
spawnPowerup(boss.x, boss.y);
boss.destroy();
boss = null;
LK.getSound('enemyDestroy').play();
}
bullet.destroy();
heroBullets.splice(i, 1);
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.x < -50) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
if (enemy.update()) {
shootEnemyBullet(enemy);
}
// Check collision with hero
if (enemy.intersects(hero)) {
if (hero.takeDamage()) {
LK.showGameOver();
}
enemy.destroy();
enemies.splice(i, 1);
LK.getSound('hit').play();
}
}
// Update boss
if (boss) {
var phase = boss.update();
if (phase > 0) {
shootBossBullet(phase);
}
// Check collision with hero
if (boss.intersects(hero)) {
if (hero.takeDamage()) {
LK.showGameOver();
}
LK.getSound('hit').play();
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.x < -50 || bullet.x > 2048 + 50 || bullet.y < -50 || bullet.y > 2732 + 50) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with hero
if (bullet.intersects(hero)) {
if (parryWindow && bullet.canBeParried && !bullet.parried) {
// Successful parry!
bullet.parried = true;
bullet.speedX *= -2;
bullet.speedY *= -0.5;
bullet.tint = weaponTypes[hero.weaponType].parryColor;
LK.setScore(LK.getScore() + 50);
scoreTxt.setText('Score: ' + LK.getScore());
// Apply parry effect
var effect = weaponTypes[hero.weaponType].parryEffect;
if (effect === 'heal' && hero.health < hero.maxHealth) {
hero.health++;
} else if (effect === 'explode') {
LK.effects.flashScreen(weaponTypes[hero.weaponType].parryColor, 200);
// Damage all enemies
for (var j = 0; j < enemies.length; j++) {
enemies[j].takeDamage(1);
}
} else if (effect === 'freeze') {
// Slow all enemies temporarily
for (var j = 0; j < enemies.length; j++) {
enemies[j].speed *= 0.5;
}
}
} else if (!bullet.parried) {
if (hero.takeDamage()) {
LK.showGameOver();
}
bullet.destroy();
enemyBullets.splice(i, 1);
LK.getSound('hit').play();
}
}
// Check parried bullets hitting enemies
if (bullet.parried) {
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(2)) {
LK.setScore(LK.getScore() + enemy.points * 2);
scoreTxt.setText('Score: ' + LK.getScore());
spawnPowerup(enemy.x, enemy.y);
enemy.destroy();
enemies.splice(j, 1);
LK.getSound('enemyDestroy').play();
}
bullet.destroy();
enemyBullets.splice(i, 1);
break;
}
}
// Check boss collision
if (boss && bullet.intersects(boss)) {
if (boss.takeDamage(2)) {
LK.setScore(LK.getScore() + boss.points);
scoreTxt.setText('Score: ' + LK.getScore());
spawnPowerup(boss.x, boss.y);
boss.destroy();
boss = null;
LK.getSound('enemyDestroy').play();
}
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (powerup.x < -50) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check collision with hero
if (powerup.intersects(hero)) {
hero.applyPowerup(powerup.type);
powerup.destroy();
powerups.splice(i, 1);
LK.getSound('powerupCollect').play();
}
}
// Update perry collectibles
for (var i = perryCollectibles.length - 1; i >= 0; i--) {
var perryCollectible = perryCollectibles[i];
if (perryCollectible.x < -50) {
perryCollectible.destroy();
perryCollectibles.splice(i, 1);
continue;
}
// Check collision with hero
if (perryCollectible.intersects(hero)) {
hero.collectPerry();
perryTxt.setText('Perry: ' + hero.perryCharges);
perryCollectible.destroy();
perryCollectibles.splice(i, 1);
LK.getSound('powerupCollect').play();
}
}
};
// Start music
LK.playMusic('bgMusic');