/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BerserkerShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('berserkerShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5 + Math.random() * 1; self.health = 5; self.maxHealth = 5; self.monsterType = 'berserker'; self.enraged = false; // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); // Enrage when health is low if (self.health <= self.maxHealth * 0.4 && !self.enraged) { self.enraged = true; self.speed *= 2; shadowGraphics.tint = 0xff0000; } self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Berserker effect when enraged if (self.enraged) { var shake = (Math.random() - 0.5) * 4; self.x += shake; self.y += shake; // Pulsing red effect var pulse = 1 + Math.sin(LK.ticks * 0.8) * 0.5; shadowGraphics.scaleX = pulse; shadowGraphics.scaleY = pulse; } // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var BomberShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('bomberShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2 + Math.random() * 1; self.health = 2; self.maxHealth = 2; self.monsterType = 'bomber'; self.exploding = false; self.explosionTimer = 30; // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2)); if (distanceToHero < 100 && !self.exploding) { self.exploding = true; shadowGraphics.tint = 0xff4444; } if (self.exploding) { self.explosionTimer--; var flash = Math.sin(self.explosionTimer * 0.5) > 0 ? 0xff0000 : 0xffff00; shadowGraphics.tint = flash; if (self.explosionTimer <= 0) { // Create explosion effect for (var i = 0; i < 6; i++) { var explosionAngle = i / 6 * Math.PI * 2; var fragment = new Shadow(); fragment.x = self.x + Math.cos(explosionAngle) * 20; fragment.y = self.y + Math.sin(explosionAngle) * 20; fragment.speed = 2; shadows.push(fragment); game.addChild(fragment); } } } else { self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var ExplosiveOrb = Container.expand(function () { var self = Container.call(this); var orbGraphics = self.attachAsset('lightOrb', { anchorX: 0.5, anchorY: 0.5 }); self.lifetime = 180; // 3 seconds self.speed = 3; self.explosionRadius = 150; self.damage = 4; self.velocityX = 0; self.velocityY = 0; self.update = function () { self.lifetime--; self.x += self.velocityX; self.y += self.velocityY; // Pulsing effect var scale = 1 + Math.sin(LK.ticks * 0.4) * 0.3; orbGraphics.scaleX = scale; orbGraphics.scaleY = scale; // Color shifting from blue to red as it gets closer to explosion var timeRatio = 1 - self.lifetime / 180; var red = Math.floor(timeRatio * 255); var blue = Math.floor((1 - timeRatio) * 255); orbGraphics.tint = red << 16 | blue; }; return self; }); var FastShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('fastShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3 + Math.random() * 2; self.health = 1; self.maxHealth = 1; self.monsterType = 'fast'; // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Add erratic movement self.x += Math.sin(LK.ticks * 0.2) * 2; self.y += Math.cos(LK.ticks * 0.15) * 2; // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var FireSpell = Container.expand(function () { var self = Container.call(this); var spellGraphics = self.attachAsset('fireSpell', { anchorX: 0.5, anchorY: 0.5 }); self.lifetime = 30; // Half second self.radius = 80; self.update = function () { self.lifetime--; // Grow the spell var scale = 1 + (30 - self.lifetime) / 30; spellGraphics.scaleX = scale; spellGraphics.scaleY = scale; spellGraphics.alpha = self.lifetime / 30; }; return self; }); var GuidedFire = Container.expand(function () { var self = Container.call(this); var fireGraphics = self.attachAsset('fireSpell', { anchorX: 0.5, anchorY: 0.5 }); self.lifetime = 300; // 5 seconds self.speed = 4; self.target = null; self.damage = 3; self.update = function () { self.lifetime--; // Find nearest enemy if no target or target is destroyed if (!self.target || self.target.destroyed) { var nearestDistance = Infinity; self.target = null; for (var i = 0; i < shadows.length; i++) { var distance = Math.sqrt(Math.pow(self.x - shadows[i].x, 2) + Math.pow(self.y - shadows[i].y, 2)); if (distance < nearestDistance) { nearestDistance = distance; self.target = shadows[i]; } } } // Move towards target if (self.target) { 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; // Rotate towards target fireGraphics.rotation = angle; } // Fire effects var scale = 1 + Math.sin(LK.ticks * 0.3) * 0.2; fireGraphics.scaleX = scale; fireGraphics.scaleY = scale; fireGraphics.tint = 0xff4400; }; return self; }); var HealerShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('healerShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.8 + Math.random() * 0.5; self.health = 3; self.maxHealth = 3; self.monsterType = 'healer'; self.lastHeal = 0; self.healCooldown = 120; // 2 seconds // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Healing ability if (LK.ticks - self.lastHeal > self.healCooldown) { for (var i = 0; i < shadows.length; i++) { var otherShadow = shadows[i]; if (otherShadow !== self) { var distance = Math.sqrt(Math.pow(self.x - otherShadow.x, 2) + Math.pow(self.y - otherShadow.y, 2)); if (distance < 150 && otherShadow.health < otherShadow.maxHealth) { otherShadow.health = Math.min(otherShadow.maxHealth, otherShadow.health + 1); LK.effects.flashObject(otherShadow, 0x00ff00, 300); self.lastHeal = LK.ticks; break; } } } } // Pulsing green effect var pulse = 1 + Math.sin(LK.ticks * 0.4) * 0.3; shadowGraphics.tint = 0x44ff44; shadowGraphics.scaleX = pulse; shadowGraphics.scaleY = pulse; // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var Hero = Container.expand(function () { var self = Container.call(this); var heroGraphics = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.lightPower = 1; self.fireRate = 60; // Fire every 60 ticks initially self.lastLightShot = 0; self.update = function () { // Hero update method - no automatic firing }; return self; }); var LightOrb = Container.expand(function () { var self = Container.call(this); var orbGraphics = self.attachAsset('lightOrb', { anchorX: 0.5, anchorY: 0.5 }); self.bobOffset = Math.random() * Math.PI * 2; self.baseY = self.y; self.update = function () { self.y = self.baseY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 10; }; return self; }); var LightProjectile = Container.expand(function () { var self = Container.call(this); var projectileGraphics = self.attachAsset('lightProjectile', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.lifetime = 180; // 3 seconds at 60fps self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.lifetime--; }; return self; }); var MimicShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('mimicShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.8 + Math.random() * 1.2; self.health = 2; self.maxHealth = 2; self.monsterType = 'mimic'; self.lastCopy = 0; self.copyCooldown = 240; // 4 seconds self.copyTarget = null; // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Copy nearest enemy abilities if (LK.ticks - self.lastCopy > self.copyCooldown) { var nearestEnemy = null; var nearestDistance = Infinity; for (var i = 0; i < shadows.length; i++) { var otherShadow = shadows[i]; if (otherShadow !== self && otherShadow.monsterType !== 'mimic') { var distance = Math.sqrt(Math.pow(self.x - otherShadow.x, 2) + Math.pow(self.y - otherShadow.y, 2)); if (distance < 200 && distance < nearestDistance) { nearestDistance = distance; nearestEnemy = otherShadow; } } } if (nearestEnemy) { self.copyTarget = nearestEnemy.monsterType; self.lastCopy = LK.ticks; // Visual feedback LK.effects.flashObject(self, 0xff00ff, 500); // Copy some properties if (nearestEnemy.monsterType === 'fast') { self.speed = nearestEnemy.speed; shadowGraphics.tint = 0xffff00; } else if (nearestEnemy.monsterType === 'tank') { self.health = Math.min(self.maxHealth + 2, nearestEnemy.health); shadowGraphics.tint = 0x888888; } else if (nearestEnemy.monsterType === 'teleporter') { shadowGraphics.tint = 0x8844ff; } } } // Rainbow shifting effect var hue = LK.ticks * 0.05 % 1; var r = Math.sin(hue * Math.PI * 2) * 127 + 128; var g = Math.sin((hue + 0.33) * Math.PI * 2) * 127 + 128; var b = Math.sin((hue + 0.66) * Math.PI * 2) * 127 + 128; var color = Math.floor(r) << 16 | Math.floor(g) << 8 | Math.floor(b); shadowGraphics.tint = color; // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var Shadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('shadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1 + Math.random() * 2; self.health = 2; self.maxHealth = 2; // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var ShielderShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('shielderShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.2 + Math.random() * 0.8; self.health = 4; self.maxHealth = 4; self.monsterType = 'shielder'; self.shieldActive = false; self.lastShield = 0; self.shieldCooldown = 180; // 3 seconds self.shieldDuration = 120; // 2 seconds self.shieldTimer = 0; // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Shield activation when damaged if (self.health < self.maxHealth && !self.shieldActive && LK.ticks - self.lastShield > self.shieldCooldown) { self.shieldActive = true; self.shieldTimer = self.shieldDuration; self.lastShield = LK.ticks; shadowGraphics.tint = 0x4444ff; } // Shield timer if (self.shieldActive) { self.shieldTimer--; if (self.shieldTimer <= 0) { self.shieldActive = false; shadowGraphics.tint = 0xffffff; } // Pulsing blue effect when shielded var pulse = 1 + Math.sin(LK.ticks * 0.6) * 0.4; shadowGraphics.scaleX = pulse; shadowGraphics.scaleY = pulse; } // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var SplitterShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('splitterShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5 + Math.random() * 1; self.health = 3; self.maxHealth = 3; self.monsterType = 'splitter'; // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Pulsing effect var pulse = 1 + Math.sin(LK.ticks * 0.3) * 0.2; shadowGraphics.scaleX = pulse; shadowGraphics.scaleY = pulse; // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var TankShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('tankShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.5 + Math.random() * 0.5; self.health = 8; self.maxHealth = 8; self.monsterType = 'tank'; // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Visual health indicator var healthPercent = self.health / self.maxHealth; shadowGraphics.alpha = 0.5 + healthPercent * 0.5; // Update health bar healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); var TeleporterShadow = Container.expand(function () { var self = Container.call(this); var shadowGraphics = self.attachAsset('teleporterShadow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1 + Math.random() * 1; self.health = 2; self.maxHealth = 2; self.monsterType = 'teleporter'; self.lastTeleport = 0; self.teleportCooldown = 180; // 3 seconds // Create health bar var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); healthBarBg.y = -shadowGraphics.height / 2 - 15; var healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0.5, anchorY: 0.5 }); healthBarFill.y = -shadowGraphics.height / 2 - 15; self.update = function () { var angle = Math.atan2(hero.y - self.y, hero.x - self.x); var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2)); // Teleport if far from hero and cooldown is ready if (distanceToHero > 300 && LK.ticks - self.lastTeleport > self.teleportCooldown) { var teleportDistance = 150; var teleportAngle = Math.atan2(hero.y - self.y, hero.x - self.x); self.x = hero.x - Math.cos(teleportAngle) * teleportDistance; self.y = hero.y - Math.sin(teleportAngle) * teleportDistance; self.lastTeleport = LK.ticks; LK.effects.flashObject(self, 0x8844ff, 300); } else { // Normal movement self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } // Update health bar var healthPercent = self.health / self.maxHealth; healthBarFill.scaleX = healthPercent; healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; }; return self; }); /**** * Initialize Game ****/ // Guided fire button press handler var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Create single cube background // Guided fire button press handler function handleGuidedFireButton(x, y, obj) { var timeSinceLastFire = LK.ticks - lastIceAttack; if (timeSinceLastFire >= iceAttackCooldown && shadows.length > 0) { // Create 3 guided fire projectiles for (var i = 0; i < 3; i++) { var guidedFire = new GuidedFire(); guidedFire.x = hero.x + (i - 1) * 30; guidedFire.y = hero.y; iceAttacks.push(guidedFire); game.addChild(guidedFire); } lastIceAttack = LK.ticks; LK.effects.flashObject(iceButton, 0xff4400, 300); // Cooldown visual feedback tween(iceButton, { alpha: 0.3 }, { duration: 0 }); tween(iceButton, { alpha: 0.8 }, { duration: iceAttackCooldown * 16.67 }); } } // Explosive orb button press handler function handleExplosiveOrbButton(x, y, obj) { var timeSinceLastOrb = LK.ticks - lastWindAttack; if (timeSinceLastOrb >= windAttackCooldown) { // Create explosive orb towards nearest enemy var nearestShadow = null; var nearestDistance = Infinity; for (var i = 0; i < shadows.length; i++) { var distance = Math.sqrt(Math.pow(hero.x - shadows[i].x, 2) + Math.pow(hero.y - shadows[i].y, 2)); if (distance < nearestDistance) { nearestDistance = distance; nearestShadow = shadows[i]; } } if (nearestShadow) { var explosiveOrb = new ExplosiveOrb(); explosiveOrb.x = hero.x; explosiveOrb.y = hero.y; var angle = Math.atan2(nearestShadow.y - hero.y, nearestShadow.x - hero.x); explosiveOrb.velocityX = Math.cos(angle) * explosiveOrb.speed; explosiveOrb.velocityY = Math.sin(angle) * explosiveOrb.speed; windAttacks.push(explosiveOrb); game.addChild(explosiveOrb); } lastWindAttack = LK.ticks; LK.effects.flashObject(windButton, 0xff8800, 300); // Cooldown visual feedback tween(windButton, { alpha: 0.3 }, { duration: 0 }); tween(windButton, { alpha: 0.8 }, { duration: windAttackCooldown * 16.67 }); } } var cube = LK.getAsset('cube', { anchorX: 0.5, anchorY: 0.5 }); cube.x = 1024; cube.y = 1366; game.addChild(cube); var hero = game.addChild(new Hero()); hero.x = 1024; hero.y = 1366; hero.health = 3; hero.maxHealth = 3; hero.lastDamageTime = 0; hero.invulnerabilityDuration = 120; // 2 seconds of invulnerability after taking damage var shadows = []; var lightProjectiles = []; var fireSpells = []; var lightOrbs = []; var iceAttacks = []; var windAttacks = []; var lastShadowSpawn = 0; var shadowSpawnRate = 120; // Start spawning every 2 seconds var waveLevel = 1; var score = 0; var lastFireSpell = 0; var fireSpellCooldown = 90; // 1.5 seconds cooldown at 60fps var maxActiveFireSpells = 3; var lastLightAttack = 0; var lightAttackCooldown = 420; // 7 seconds cooldown at 60fps var orbsUsedThisWave = 0; var maxOrbsPerWave = 7; var waveEnemiesRemaining = 0; var waveEnemiesTotal = 0; var waveActive = false; var waveStartDelay = 0; var waveDifficultyMultiplier = 1.0; var shelterActive = false; var shelterScore = 10000; // Score threshold to activate shelter var shelterDuration = 300; // 5 seconds at 60fps var shelterTimer = 0; var shelterGraphics = null; var damageBonusActive = false; var damageBonusTimer = 0; var damageBonusDuration = 360; // 6 seconds at 60fps var shieldActive = false; var shieldTimer = 0; var shieldDuration = 360; // 6 seconds at 60fps var lastIceAttack = 0; var iceAttackCooldown = 480; // 8 seconds cooldown var lastWindAttack = 0; var windAttackCooldown = 360; // 6 seconds cooldown var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var waveTxt = new Text2('Wave: 1', { size: 50, fill: 0xFFFF00 }); waveTxt.anchor.set(0.5, 0); waveTxt.y = 70; LK.gui.top.addChild(waveTxt); var waveProgressTxt = new Text2('Enemies: 0/0', { size: 40, fill: 0x00FFFF }); waveProgressTxt.anchor.set(0.5, 0); waveProgressTxt.y = 130; LK.gui.top.addChild(waveProgressTxt); var healthTxt = new Text2('Health: 3/3', { size: 50, fill: 0xFF0000 }); healthTxt.anchor.set(0.5, 0); healthTxt.y = 180; LK.gui.top.addChild(healthTxt); // Create fire button var fireButton = LK.getAsset('fireSpell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, alpha: 0.8 }); fireButton.x = 200; fireButton.y = 2500; game.addChild(fireButton); // Create light attack button var lightButton = LK.getAsset('lightOrb', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.8, scaleY: 1.8, alpha: 0.8 }); lightButton.x = 1800; lightButton.y = 2500; game.addChild(lightButton); // Create light projectile button var lightProjectileButton = LK.getAsset('lightProjectile', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0, alpha: 0.8 }); lightProjectileButton.x = 1024; lightProjectileButton.y = 2500; game.addChild(lightProjectileButton); // Create guided fire button var iceButton = LK.getAsset('fireSpell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, alpha: 0.8 }); iceButton.x = 512; iceButton.y = 2500; game.addChild(iceButton); iceButton.down = handleGuidedFireButton; // Create explosive orb button var windButton = LK.getAsset('lightOrb', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, alpha: 0.8 }); windButton.x = 1536; windButton.y = 2500; game.addChild(windButton); windButton.down = handleExplosiveOrbButton; // Fire button press handler fireButton.down = function (x, y, obj) { // Fire spell at nearest enemy - check cooldown and limit var timeSinceLastFire = LK.ticks - lastFireSpell; if (timeSinceLastFire >= fireSpellCooldown && fireSpells.length < maxActiveFireSpells && shadows.length > 0) { // Find nearest shadow var nearestShadow = null; var nearestDistance = Infinity; for (var i = 0; i < shadows.length; i++) { var distance = Math.sqrt(Math.pow(hero.x - shadows[i].x, 2) + Math.pow(hero.y - shadows[i].y, 2)); if (distance < nearestDistance) { nearestDistance = distance; nearestShadow = shadows[i]; } } ; // Light attack button press handler lightButton.down = function (x, y, obj) { // Check cooldown and orb usage limit var timeSinceLastLight = LK.ticks - lastLightAttack; if (timeSinceLastLight >= lightAttackCooldown && orbsUsedThisWave < maxOrbsPerWave) { // Destroy all enemies on screen for (var i = shadows.length - 1; i >= 0; i--) { var shadow = shadows[i]; // Create light orb for each destroyed shadow var newOrb = new LightOrb(); newOrb.x = shadow.x; newOrb.y = shadow.y; newOrb.baseY = newOrb.y; lightOrbs.push(newOrb); game.addChild(newOrb); // Add score based on monster type var scoreValue = shadow.monsterType === 'tank' ? 25 : shadow.monsterType === 'splitter' ? 30 : shadow.monsterType === 'teleporter' ? 20 : shadow.monsterType === 'bomber' ? 35 : shadow.monsterType === 'healer' ? 40 : shadow.monsterType === 'shielder' ? 35 : shadow.monsterType === 'berserker' ? 45 : shadow.monsterType === 'mimic' ? 50 : 10; score += scoreValue; // Flash effect on each enemy before destruction tween(shadow, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { shadow.destroy(); } }); shadows.splice(i, 1); } // Update score display scoreTxt.setText('Score: ' + score); // Play sound effect LK.getSound('shadowDestroy').play(); // Screen flash effect LK.effects.flashScreen(0xffffaa, 800); // Button feedback with cooldown visual LK.effects.flashObject(lightButton, 0xffffaa, 400); tween(lightButton, { alpha: 0.3 }, { duration: 0 }); tween(lightButton, { alpha: 0.8 }, { duration: lightAttackCooldown * 16.67 }); // Convert ticks to ms // Update cooldown and usage counter lastLightAttack = LK.ticks; orbsUsedThisWave++; } }; // Light projectile button press handler lightProjectileButton.down = function (x, y, obj) { // Fire light projectile var newProjectile = new LightProjectile(); newProjectile.x = hero.x; newProjectile.y = hero.y; // Fire in 8 directions var directions = 8; for (var i = 0; i < directions; i++) { var angle = i / directions * Math.PI * 2; var projectile = new LightProjectile(); projectile.x = hero.x; projectile.y = hero.y; projectile.velocityX = Math.cos(angle) * 8; projectile.velocityY = Math.sin(angle) * 8; lightProjectiles.push(projectile); game.addChild(projectile); } // Button feedback LK.effects.flashObject(lightProjectileButton, 0xffffaa, 200); }; if (nearestShadow) { var newFireSpell = new FireSpell(); newFireSpell.x = nearestShadow.x; newFireSpell.y = nearestShadow.y; fireSpells.push(newFireSpell); game.addChild(newFireSpell); lastFireSpell = LK.ticks; LK.getSound('fireBlast').play(); // Button feedback LK.effects.flashObject(fireButton, 0xff4400, 200); } } }; var dragNode = null; function handleMove(x, y, obj) { if (dragNode) { dragNode.x = x; dragNode.y = y; // Keep hero within bounds if (dragNode.x < 40) dragNode.x = 40; if (dragNode.x > 2008) dragNode.x = 2008; if (dragNode.y < 40) dragNode.y = 40; if (dragNode.y > 2692) dragNode.y = 2692; } } game.move = handleMove; game.down = function (x, y, obj) { // Check if touching hero var heroDistance = Math.sqrt(Math.pow(x - hero.x, 2) + Math.pow(y - hero.y, 2)); if (heroDistance < 60) { dragNode = hero; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; function startWave() { waveActive = true; waveStartDelay = 180; // 3 second delay before spawning waveEnemiesTotal = Math.min(5 + waveLevel * 2, 30); // Cap at 30 enemies waveEnemiesRemaining = waveEnemiesTotal; waveDifficultyMultiplier = 1.0 + (waveLevel - 1) * 0.15; // 15% increase per wave shadowSpawnRate = Math.max(30, 120 - waveLevel * 8); // Faster spawning each wave orbsUsedThisWave = 0; // Reset orb usage for new wave waveTxt.setText('Wave: ' + waveLevel); waveProgressTxt.setText('Enemies: ' + (waveEnemiesTotal - waveEnemiesRemaining) + '/' + waveEnemiesTotal); LK.effects.flashScreen(0x00FF00, 500); } game.update = function () { // Wave management if (!waveActive && shadows.length === 0) { // Start new wave when no enemies remain startWave(); } // Spawn shadows during active wave if (waveActive && waveEnemiesRemaining > 0 && waveStartDelay <= 0 && LK.ticks - lastShadowSpawn >= shadowSpawnRate) { var newShadow; // Choose random monster type based on wave level var monsterRoll = Math.random(); if (waveLevel < 3) { // Early waves: mostly basic shadows if (monsterRoll < 0.7) newShadow = new Shadow();else if (monsterRoll < 0.9) newShadow = new FastShadow();else newShadow = new TankShadow(); } else if (waveLevel < 6) { // Mid waves: introduce more variety if (monsterRoll < 0.4) newShadow = new Shadow();else if (monsterRoll < 0.6) newShadow = new FastShadow();else if (monsterRoll < 0.75) newShadow = new TankShadow();else if (monsterRoll < 0.9) newShadow = new SplitterShadow();else newShadow = new TeleporterShadow(); } else if (waveLevel < 10) { // Late waves: all monster types including dangerous ones if (monsterRoll < 0.2) newShadow = new Shadow();else if (monsterRoll < 0.35) newShadow = new FastShadow();else if (monsterRoll < 0.5) newShadow = new TankShadow();else if (monsterRoll < 0.65) newShadow = new SplitterShadow();else if (monsterRoll < 0.75) newShadow = new TeleporterShadow();else if (monsterRoll < 0.85) newShadow = new BomberShadow();else if (monsterRoll < 0.92) newShadow = new HealerShadow();else newShadow = new ShielderShadow(); } else { // Very late waves: all enemy types including new ones if (monsterRoll < 0.15) newShadow = new Shadow();else if (monsterRoll < 0.25) newShadow = new FastShadow();else if (monsterRoll < 0.35) newShadow = new TankShadow();else if (monsterRoll < 0.45) newShadow = new SplitterShadow();else if (monsterRoll < 0.55) newShadow = new TeleporterShadow();else if (monsterRoll < 0.65) newShadow = new BomberShadow();else if (monsterRoll < 0.75) newShadow = new HealerShadow();else if (monsterRoll < 0.85) newShadow = new ShielderShadow();else if (monsterRoll < 0.93) newShadow = new BerserkerShadow();else newShadow = new MimicShadow(); } // Spawn from random edge var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top newShadow.x = Math.random() * 2048; newShadow.y = -30; break; case 1: // Right newShadow.x = 2078; newShadow.y = Math.random() * 2732; break; case 2: // Bottom newShadow.x = Math.random() * 2048; newShadow.y = 2762; break; case 3: // Left newShadow.x = -30; newShadow.y = Math.random() * 2732; break; } // Apply wave difficulty multiplier newShadow.speed = (newShadow.speed + waveLevel * 0.3) * waveDifficultyMultiplier; newShadow.health = Math.ceil(newShadow.health * waveDifficultyMultiplier); newShadow.maxHealth = newShadow.health; shadows.push(newShadow); game.addChild(newShadow); lastShadowSpawn = LK.ticks; waveEnemiesRemaining--; waveProgressTxt.setText('Enemies: ' + (waveEnemiesTotal - waveEnemiesRemaining) + '/' + waveEnemiesTotal); } // Wave start delay countdown if (waveStartDelay > 0) { waveStartDelay--; } // Check if wave is complete if (waveActive && waveEnemiesRemaining <= 0 && shadows.length === 0) { waveActive = false; waveLevel++; // Wave completion bonus score += waveLevel * 50; scoreTxt.setText('Score: ' + score); LK.effects.flashScreen(0xFFFF00, 800); } // Check hero collision with shadows for (var i = 0; i < shadows.length; i++) { var shadow = shadows[i]; if (hero.intersects(shadow) && !shelterActive && !shieldActive) { // Check if hero is not invulnerable if (LK.ticks - hero.lastDamageTime > hero.invulnerabilityDuration) { hero.health--; hero.lastDamageTime = LK.ticks; healthTxt.setText('Health: ' + hero.health + '/' + hero.maxHealth); // Flash effect and make hero temporarily invulnerable LK.effects.flashScreen(0xff0000, 500); LK.effects.flashObject(hero, 0xff0000, hero.invulnerabilityDuration * 16.67); // Make hero semi-transparent during invulnerability tween(hero, { alpha: 0.5 }, { duration: 0 }); tween(hero, { alpha: 1.0 }, { duration: hero.invulnerabilityDuration * 16.67 }); if (hero.health <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } } } // Check bomber explosions if (shadow.monsterType === 'bomber' && shadow.exploding && shadow.explosionTimer <= 0) { var explosionDistance = Math.sqrt(Math.pow(hero.x - shadow.x, 2) + Math.pow(hero.y - shadow.y, 2)); if (explosionDistance < 120 && !shelterActive && !shieldActive) { // Check if hero is not invulnerable if (LK.ticks - hero.lastDamageTime > hero.invulnerabilityDuration) { hero.health--; hero.lastDamageTime = LK.ticks; healthTxt.setText('Health: ' + hero.health + '/' + hero.maxHealth); // Flash effect and make hero temporarily invulnerable LK.effects.flashScreen(0xff4400, 500); LK.effects.flashObject(hero, 0xff4400, hero.invulnerabilityDuration * 16.67); // Make hero semi-transparent during invulnerability tween(hero, { alpha: 0.5 }, { duration: 0 }); tween(hero, { alpha: 1.0 }, { duration: hero.invulnerabilityDuration * 16.67 }); if (hero.health <= 0) { LK.effects.flashScreen(0xff4400, 1000); LK.showGameOver(); return; } } } // Remove exploded bomber shadow.destroy(); shadows.splice(i, 1); i--; // Adjust index after removal } } // Update and check light projectiles for (var j = lightProjectiles.length - 1; j >= 0; j--) { var projectile = lightProjectiles[j]; if (projectile.lifetime <= 0 || projectile.x < -50 || projectile.x > 2098 || projectile.y < -50 || projectile.y > 2782) { projectile.destroy(); lightProjectiles.splice(j, 1); continue; } // Check collision with shadows for (var k = shadows.length - 1; k >= 0; k--) { if (projectile.intersects(shadows[k])) { var shadow = shadows[k]; // Check if enemy is shielded var damage = damageBonusActive ? 2 : 1; if (shadow.monsterType === 'shielder' && shadow.shieldActive) { damage = 0; // No damage when shielded LK.effects.flashObject(shadow, 0x4444ff, 100); } else { shadow.health -= damage; } var shadowDestroyed = false; if (shadow.health <= 0) { // Handle special monster behaviors on death if (shadow.monsterType === 'splitter' && shadow.health <= 0) { // Create two smaller fast shadows for (var split = 0; split < 2; split++) { var miniShadow = new FastShadow(); miniShadow.x = shadow.x + (split === 0 ? -30 : 30); miniShadow.y = shadow.y + (Math.random() - 0.5) * 60; miniShadow.speed *= 0.8; shadows.push(miniShadow); game.addChild(miniShadow); } } if (shadow.monsterType === 'bomber' && shadow.exploding && shadow.explosionTimer > 0) { // Bomber was destroyed while exploding, cancel explosion shadow.exploding = false; } // Create light orb var newOrb = new LightOrb(); newOrb.x = shadow.x; newOrb.y = shadow.y; newOrb.baseY = newOrb.y; lightOrbs.push(newOrb); game.addChild(newOrb); shadow.destroy(); shadows.splice(k, 1); shadowDestroyed = true; var scoreValue = shadow.monsterType === 'tank' ? 25 : shadow.monsterType === 'splitter' ? 30 : shadow.monsterType === 'teleporter' ? 20 : shadow.monsterType === 'bomber' ? 35 : shadow.monsterType === 'healer' ? 40 : shadow.monsterType === 'shielder' ? 35 : shadow.monsterType === 'berserker' ? 45 : shadow.monsterType === 'mimic' ? 50 : 10; score += scoreValue; scoreTxt.setText('Score: ' + score); LK.getSound('shadowDestroy').play(); } else { // Monster took damage but not destroyed LK.effects.flashObject(shadow, 0xffffff, 200); } projectile.destroy(); lightProjectiles.splice(j, 1); break; } } } // Update and check fire spells for (var l = fireSpells.length - 1; l >= 0; l--) { var spell = fireSpells[l]; if (spell.lifetime <= 0) { spell.destroy(); fireSpells.splice(l, 1); continue; } // Check collision with shadows for (var m = shadows.length - 1; m >= 0; m--) { var distance = Math.sqrt(Math.pow(spell.x - shadows[m].x, 2) + Math.pow(spell.y - shadows[m].y, 2)); if (distance < spell.radius) { var shadow = shadows[m]; // Check if enemy is shielded var damage = damageBonusActive ? 4 : 2; // Fire spells do more damage if (shadow.monsterType === 'shielder' && shadow.shieldActive) { damage = Math.ceil(damage / 2); // Reduced damage when shielded LK.effects.flashObject(shadow, 0x4444ff, 100); } shadow.health -= damage; if (shadow.health <= 0) { // Handle special monster behaviors on death if (shadow.monsterType === 'splitter') { // Create two smaller fast shadows for (var split = 0; split < 2; split++) { var miniShadow = new FastShadow(); miniShadow.x = shadow.x + (split === 0 ? -30 : 30); miniShadow.y = shadow.y + (Math.random() - 0.5) * 60; miniShadow.speed *= 0.8; shadows.push(miniShadow); game.addChild(miniShadow); } } if (shadow.monsterType === 'bomber' && shadow.exploding && shadow.explosionTimer > 0) { // Bomber was destroyed while exploding, cancel explosion shadow.exploding = false; } // Create light orb var newOrb = new LightOrb(); newOrb.x = shadow.x; newOrb.y = shadow.y; newOrb.baseY = newOrb.y; lightOrbs.push(newOrb); game.addChild(newOrb); shadow.destroy(); shadows.splice(m, 1); var scoreValue = shadow.monsterType === 'tank' ? 30 : shadow.monsterType === 'splitter' ? 35 : shadow.monsterType === 'teleporter' ? 25 : shadow.monsterType === 'bomber' ? 40 : shadow.monsterType === 'healer' ? 45 : shadow.monsterType === 'shielder' ? 40 : shadow.monsterType === 'berserker' ? 50 : shadow.monsterType === 'mimic' ? 55 : 15; score += scoreValue; scoreTxt.setText('Score: ' + score); LK.getSound('shadowDestroy').play(); } else { // Monster took damage but not destroyed LK.effects.flashObject(shadow, 0xff8800, 200); } } } } // Check light orb collection for (var n = lightOrbs.length - 1; n >= 0; n--) { var orb = lightOrbs[n]; if (hero.intersects(orb)) { hero.lightPower++; if (hero.fireRate > 20) { hero.fireRate -= 2; } orb.destroy(); lightOrbs.splice(n, 1); LK.getSound('collectOrb').play(); // Flash hero with light effect LK.effects.flashObject(hero, 0xffffaa, 500); } } // Shelter activation check if (score >= shelterScore && !shelterActive) { shelterActive = true; shelterTimer = shelterDuration; // Create shelter visual shelterGraphics = LK.getAsset('shelter', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); shelterGraphics.x = hero.x; shelterGraphics.y = hero.y; game.addChild(shelterGraphics); // Visual feedback for shelter activation LK.effects.flashScreen(0x00ffff, 1000); LK.effects.flashObject(hero, 0x00ffff, 1000); // Make hero semi-transparent during shelter hero.alpha = 0.5; // Pulsing effect for shelter tween(shelterGraphics, { alpha: 0.6 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(shelterGraphics, { alpha: 0.3 }, { duration: 500, easing: tween.easeInOut }); } }); } // Shelter timer countdown if (shelterActive) { shelterTimer--; // Move shelter with hero if (shelterGraphics) { shelterGraphics.x = hero.x; shelterGraphics.y = hero.y; } if (shelterTimer <= 0) { shelterActive = false; hero.alpha = 1.0; // Restore normal visibility // Remove shelter visual if (shelterGraphics) { shelterGraphics.destroy(); shelterGraphics = null; } // Activate damage bonus and shield after leaving shelter damageBonusActive = true; damageBonusTimer = damageBonusDuration; shieldActive = true; shieldTimer = shieldDuration; // Visual feedback for bonuses LK.effects.flashScreen(0xff8800, 800); LK.effects.flashObject(hero, 0xff8800, 1000); // Increase shelter score threshold for next activation shelterScore += 10000; } } // Damage bonus timer countdown if (damageBonusActive) { damageBonusTimer--; if (damageBonusTimer <= 0) { damageBonusActive = false; } } // Shield timer countdown if (shieldActive) { shieldTimer--; if (shieldTimer <= 0) { shieldActive = false; } } // Update and check guided fire attacks for (var p = iceAttacks.length - 1; p >= 0; p--) { var guidedFire = iceAttacks[p]; if (guidedFire.lifetime <= 0) { guidedFire.destroy(); iceAttacks.splice(p, 1); continue; } // Check collision with shadows for (var q = shadows.length - 1; q >= 0; q--) { if (guidedFire.intersects(shadows[q])) { var shadow = shadows[q]; // Check if enemy is shielded var damage = damageBonusActive ? guidedFire.damage * 2 : guidedFire.damage; if (shadow.monsterType === 'shielder' && shadow.shieldActive) { damage = Math.ceil(damage / 2); LK.effects.flashObject(shadow, 0x4444ff, 100); } shadow.health -= damage; if (shadow.health <= 0) { // Handle special monster behaviors on death if (shadow.monsterType === 'splitter') { for (var split = 0; split < 2; split++) { var miniShadow = new FastShadow(); miniShadow.x = shadow.x + (split === 0 ? -30 : 30); miniShadow.y = shadow.y + (Math.random() - 0.5) * 60; miniShadow.speed *= 0.8; shadows.push(miniShadow); game.addChild(miniShadow); } } // Create light orb var newOrb = new LightOrb(); newOrb.x = shadow.x; newOrb.y = shadow.y; newOrb.baseY = newOrb.y; lightOrbs.push(newOrb); game.addChild(newOrb); shadow.destroy(); shadows.splice(q, 1); var scoreValue = shadow.monsterType === 'tank' ? 25 : shadow.monsterType === 'splitter' ? 30 : shadow.monsterType === 'teleporter' ? 20 : shadow.monsterType === 'bomber' ? 35 : shadow.monsterType === 'healer' ? 40 : shadow.monsterType === 'shielder' ? 35 : shadow.monsterType === 'berserker' ? 45 : shadow.monsterType === 'mimic' ? 50 : 10; score += scoreValue; scoreTxt.setText('Score: ' + score); LK.getSound('shadowDestroy').play(); } else { LK.effects.flashObject(shadow, 0xff4400, 200); } guidedFire.destroy(); iceAttacks.splice(p, 1); break; } } } // Update and check explosive orbs for (var r = windAttacks.length - 1; r >= 0; r--) { var explosiveOrb = windAttacks[r]; var shouldExplode = false; // Check if orb should explode (lifetime ended, hit enemy, or went off screen) if (explosiveOrb.lifetime <= 0 || explosiveOrb.x < -100 || explosiveOrb.x > 2148 || explosiveOrb.y < -100 || explosiveOrb.y > 2832) { shouldExplode = true; } else { // Check collision with shadows for immediate explosion for (var s = 0; s < shadows.length; s++) { if (explosiveOrb.intersects(shadows[s])) { shouldExplode = true; break; } } } if (shouldExplode) { // Create explosion effect LK.effects.flashScreen(0xff8800, 300); // Damage all enemies within explosion radius for (var s = shadows.length - 1; s >= 0; s--) { var shadow = shadows[s]; var distance = Math.sqrt(Math.pow(explosiveOrb.x - shadow.x, 2) + Math.pow(explosiveOrb.y - shadow.y, 2)); if (distance < explosiveOrb.explosionRadius) { var damage = damageBonusActive ? explosiveOrb.damage * 2 : explosiveOrb.damage; if (shadow.monsterType === 'shielder' && shadow.shieldActive) { damage = Math.ceil(damage / 2); LK.effects.flashObject(shadow, 0x4444ff, 100); } shadow.health -= damage; if (shadow.health <= 0) { // Handle special monster behaviors on death if (shadow.monsterType === 'splitter') { for (var split = 0; split < 2; split++) { var miniShadow = new FastShadow(); miniShadow.x = shadow.x + (split === 0 ? -30 : 30); miniShadow.y = shadow.y + (Math.random() - 0.5) * 60; miniShadow.speed *= 0.8; shadows.push(miniShadow); game.addChild(miniShadow); } } // Create light orb var newOrb = new LightOrb(); newOrb.x = shadow.x; newOrb.y = shadow.y; newOrb.baseY = newOrb.y; lightOrbs.push(newOrb); game.addChild(newOrb); shadow.destroy(); shadows.splice(s, 1); var scoreValue = shadow.monsterType === 'tank' ? 25 : shadow.monsterType === 'splitter' ? 30 : shadow.monsterType === 'teleporter' ? 20 : shadow.monsterType === 'bomber' ? 35 : shadow.monsterType === 'healer' ? 40 : shadow.monsterType === 'shielder' ? 35 : shadow.monsterType === 'berserker' ? 45 : shadow.monsterType === 'mimic' ? 50 : 10; score += scoreValue; scoreTxt.setText('Score: ' + score); LK.getSound('shadowDestroy').play(); } else { LK.effects.flashObject(shadow, 0xff8800, 200); } } } explosiveOrb.destroy(); windAttacks.splice(r, 1); } } // Game is now infinite - no victory condition }; // Start first wave startWave();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BerserkerShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('berserkerShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5 + Math.random() * 1;
self.health = 5;
self.maxHealth = 5;
self.monsterType = 'berserker';
self.enraged = false;
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
// Enrage when health is low
if (self.health <= self.maxHealth * 0.4 && !self.enraged) {
self.enraged = true;
self.speed *= 2;
shadowGraphics.tint = 0xff0000;
}
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
// Berserker effect when enraged
if (self.enraged) {
var shake = (Math.random() - 0.5) * 4;
self.x += shake;
self.y += shake;
// Pulsing red effect
var pulse = 1 + Math.sin(LK.ticks * 0.8) * 0.5;
shadowGraphics.scaleX = pulse;
shadowGraphics.scaleY = pulse;
}
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var BomberShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('bomberShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2 + Math.random() * 1;
self.health = 2;
self.maxHealth = 2;
self.monsterType = 'bomber';
self.exploding = false;
self.explosionTimer = 30;
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2));
if (distanceToHero < 100 && !self.exploding) {
self.exploding = true;
shadowGraphics.tint = 0xff4444;
}
if (self.exploding) {
self.explosionTimer--;
var flash = Math.sin(self.explosionTimer * 0.5) > 0 ? 0xff0000 : 0xffff00;
shadowGraphics.tint = flash;
if (self.explosionTimer <= 0) {
// Create explosion effect
for (var i = 0; i < 6; i++) {
var explosionAngle = i / 6 * Math.PI * 2;
var fragment = new Shadow();
fragment.x = self.x + Math.cos(explosionAngle) * 20;
fragment.y = self.y + Math.sin(explosionAngle) * 20;
fragment.speed = 2;
shadows.push(fragment);
game.addChild(fragment);
}
}
} else {
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var ExplosiveOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('lightOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 180; // 3 seconds
self.speed = 3;
self.explosionRadius = 150;
self.damage = 4;
self.velocityX = 0;
self.velocityY = 0;
self.update = function () {
self.lifetime--;
self.x += self.velocityX;
self.y += self.velocityY;
// Pulsing effect
var scale = 1 + Math.sin(LK.ticks * 0.4) * 0.3;
orbGraphics.scaleX = scale;
orbGraphics.scaleY = scale;
// Color shifting from blue to red as it gets closer to explosion
var timeRatio = 1 - self.lifetime / 180;
var red = Math.floor(timeRatio * 255);
var blue = Math.floor((1 - timeRatio) * 255);
orbGraphics.tint = red << 16 | blue;
};
return self;
});
var FastShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('fastShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3 + Math.random() * 2;
self.health = 1;
self.maxHealth = 1;
self.monsterType = 'fast';
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
// Add erratic movement
self.x += Math.sin(LK.ticks * 0.2) * 2;
self.y += Math.cos(LK.ticks * 0.15) * 2;
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var FireSpell = Container.expand(function () {
var self = Container.call(this);
var spellGraphics = self.attachAsset('fireSpell', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 30; // Half second
self.radius = 80;
self.update = function () {
self.lifetime--;
// Grow the spell
var scale = 1 + (30 - self.lifetime) / 30;
spellGraphics.scaleX = scale;
spellGraphics.scaleY = scale;
spellGraphics.alpha = self.lifetime / 30;
};
return self;
});
var GuidedFire = Container.expand(function () {
var self = Container.call(this);
var fireGraphics = self.attachAsset('fireSpell', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 300; // 5 seconds
self.speed = 4;
self.target = null;
self.damage = 3;
self.update = function () {
self.lifetime--;
// Find nearest enemy if no target or target is destroyed
if (!self.target || self.target.destroyed) {
var nearestDistance = Infinity;
self.target = null;
for (var i = 0; i < shadows.length; i++) {
var distance = Math.sqrt(Math.pow(self.x - shadows[i].x, 2) + Math.pow(self.y - shadows[i].y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
self.target = shadows[i];
}
}
}
// Move towards target
if (self.target) {
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;
// Rotate towards target
fireGraphics.rotation = angle;
}
// Fire effects
var scale = 1 + Math.sin(LK.ticks * 0.3) * 0.2;
fireGraphics.scaleX = scale;
fireGraphics.scaleY = scale;
fireGraphics.tint = 0xff4400;
};
return self;
});
var HealerShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('healerShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.8 + Math.random() * 0.5;
self.health = 3;
self.maxHealth = 3;
self.monsterType = 'healer';
self.lastHeal = 0;
self.healCooldown = 120; // 2 seconds
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
// Healing ability
if (LK.ticks - self.lastHeal > self.healCooldown) {
for (var i = 0; i < shadows.length; i++) {
var otherShadow = shadows[i];
if (otherShadow !== self) {
var distance = Math.sqrt(Math.pow(self.x - otherShadow.x, 2) + Math.pow(self.y - otherShadow.y, 2));
if (distance < 150 && otherShadow.health < otherShadow.maxHealth) {
otherShadow.health = Math.min(otherShadow.maxHealth, otherShadow.health + 1);
LK.effects.flashObject(otherShadow, 0x00ff00, 300);
self.lastHeal = LK.ticks;
break;
}
}
}
}
// Pulsing green effect
var pulse = 1 + Math.sin(LK.ticks * 0.4) * 0.3;
shadowGraphics.tint = 0x44ff44;
shadowGraphics.scaleX = pulse;
shadowGraphics.scaleY = pulse;
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.lightPower = 1;
self.fireRate = 60; // Fire every 60 ticks initially
self.lastLightShot = 0;
self.update = function () {
// Hero update method - no automatic firing
};
return self;
});
var LightOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('lightOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.bobOffset = Math.random() * Math.PI * 2;
self.baseY = self.y;
self.update = function () {
self.y = self.baseY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 10;
};
return self;
});
var LightProjectile = Container.expand(function () {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('lightProjectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.lifetime = 180; // 3 seconds at 60fps
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.lifetime--;
};
return self;
});
var MimicShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('mimicShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.8 + Math.random() * 1.2;
self.health = 2;
self.maxHealth = 2;
self.monsterType = 'mimic';
self.lastCopy = 0;
self.copyCooldown = 240; // 4 seconds
self.copyTarget = null;
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
// Copy nearest enemy abilities
if (LK.ticks - self.lastCopy > self.copyCooldown) {
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var i = 0; i < shadows.length; i++) {
var otherShadow = shadows[i];
if (otherShadow !== self && otherShadow.monsterType !== 'mimic') {
var distance = Math.sqrt(Math.pow(self.x - otherShadow.x, 2) + Math.pow(self.y - otherShadow.y, 2));
if (distance < 200 && distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = otherShadow;
}
}
}
if (nearestEnemy) {
self.copyTarget = nearestEnemy.monsterType;
self.lastCopy = LK.ticks;
// Visual feedback
LK.effects.flashObject(self, 0xff00ff, 500);
// Copy some properties
if (nearestEnemy.monsterType === 'fast') {
self.speed = nearestEnemy.speed;
shadowGraphics.tint = 0xffff00;
} else if (nearestEnemy.monsterType === 'tank') {
self.health = Math.min(self.maxHealth + 2, nearestEnemy.health);
shadowGraphics.tint = 0x888888;
} else if (nearestEnemy.monsterType === 'teleporter') {
shadowGraphics.tint = 0x8844ff;
}
}
}
// Rainbow shifting effect
var hue = LK.ticks * 0.05 % 1;
var r = Math.sin(hue * Math.PI * 2) * 127 + 128;
var g = Math.sin((hue + 0.33) * Math.PI * 2) * 127 + 128;
var b = Math.sin((hue + 0.66) * Math.PI * 2) * 127 + 128;
var color = Math.floor(r) << 16 | Math.floor(g) << 8 | Math.floor(b);
shadowGraphics.tint = color;
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var Shadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 2;
self.health = 2;
self.maxHealth = 2;
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var ShielderShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('shielderShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.2 + Math.random() * 0.8;
self.health = 4;
self.maxHealth = 4;
self.monsterType = 'shielder';
self.shieldActive = false;
self.lastShield = 0;
self.shieldCooldown = 180; // 3 seconds
self.shieldDuration = 120; // 2 seconds
self.shieldTimer = 0;
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
// Shield activation when damaged
if (self.health < self.maxHealth && !self.shieldActive && LK.ticks - self.lastShield > self.shieldCooldown) {
self.shieldActive = true;
self.shieldTimer = self.shieldDuration;
self.lastShield = LK.ticks;
shadowGraphics.tint = 0x4444ff;
}
// Shield timer
if (self.shieldActive) {
self.shieldTimer--;
if (self.shieldTimer <= 0) {
self.shieldActive = false;
shadowGraphics.tint = 0xffffff;
}
// Pulsing blue effect when shielded
var pulse = 1 + Math.sin(LK.ticks * 0.6) * 0.4;
shadowGraphics.scaleX = pulse;
shadowGraphics.scaleY = pulse;
}
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var SplitterShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('splitterShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5 + Math.random() * 1;
self.health = 3;
self.maxHealth = 3;
self.monsterType = 'splitter';
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
// Pulsing effect
var pulse = 1 + Math.sin(LK.ticks * 0.3) * 0.2;
shadowGraphics.scaleX = pulse;
shadowGraphics.scaleY = pulse;
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var TankShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('tankShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.5 + Math.random() * 0.5;
self.health = 8;
self.maxHealth = 8;
self.monsterType = 'tank';
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
// Visual health indicator
var healthPercent = self.health / self.maxHealth;
shadowGraphics.alpha = 0.5 + healthPercent * 0.5;
// Update health bar
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
var TeleporterShadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphics = self.attachAsset('teleporterShadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 1;
self.health = 2;
self.maxHealth = 2;
self.monsterType = 'teleporter';
self.lastTeleport = 0;
self.teleportCooldown = 180; // 3 seconds
// Create health bar
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -shadowGraphics.height / 2 - 15;
var healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarFill.y = -shadowGraphics.height / 2 - 15;
self.update = function () {
var angle = Math.atan2(hero.y - self.y, hero.x - self.x);
var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2));
// Teleport if far from hero and cooldown is ready
if (distanceToHero > 300 && LK.ticks - self.lastTeleport > self.teleportCooldown) {
var teleportDistance = 150;
var teleportAngle = Math.atan2(hero.y - self.y, hero.x - self.x);
self.x = hero.x - Math.cos(teleportAngle) * teleportDistance;
self.y = hero.y - Math.sin(teleportAngle) * teleportDistance;
self.lastTeleport = LK.ticks;
LK.effects.flashObject(self, 0x8844ff, 300);
} else {
// Normal movement
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.scaleX = healthPercent;
healthBarFill.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000;
};
return self;
});
/****
* Initialize Game
****/
// Guided fire button press handler
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Create single cube background
// Guided fire button press handler
function handleGuidedFireButton(x, y, obj) {
var timeSinceLastFire = LK.ticks - lastIceAttack;
if (timeSinceLastFire >= iceAttackCooldown && shadows.length > 0) {
// Create 3 guided fire projectiles
for (var i = 0; i < 3; i++) {
var guidedFire = new GuidedFire();
guidedFire.x = hero.x + (i - 1) * 30;
guidedFire.y = hero.y;
iceAttacks.push(guidedFire);
game.addChild(guidedFire);
}
lastIceAttack = LK.ticks;
LK.effects.flashObject(iceButton, 0xff4400, 300);
// Cooldown visual feedback
tween(iceButton, {
alpha: 0.3
}, {
duration: 0
});
tween(iceButton, {
alpha: 0.8
}, {
duration: iceAttackCooldown * 16.67
});
}
}
// Explosive orb button press handler
function handleExplosiveOrbButton(x, y, obj) {
var timeSinceLastOrb = LK.ticks - lastWindAttack;
if (timeSinceLastOrb >= windAttackCooldown) {
// Create explosive orb towards nearest enemy
var nearestShadow = null;
var nearestDistance = Infinity;
for (var i = 0; i < shadows.length; i++) {
var distance = Math.sqrt(Math.pow(hero.x - shadows[i].x, 2) + Math.pow(hero.y - shadows[i].y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestShadow = shadows[i];
}
}
if (nearestShadow) {
var explosiveOrb = new ExplosiveOrb();
explosiveOrb.x = hero.x;
explosiveOrb.y = hero.y;
var angle = Math.atan2(nearestShadow.y - hero.y, nearestShadow.x - hero.x);
explosiveOrb.velocityX = Math.cos(angle) * explosiveOrb.speed;
explosiveOrb.velocityY = Math.sin(angle) * explosiveOrb.speed;
windAttacks.push(explosiveOrb);
game.addChild(explosiveOrb);
}
lastWindAttack = LK.ticks;
LK.effects.flashObject(windButton, 0xff8800, 300);
// Cooldown visual feedback
tween(windButton, {
alpha: 0.3
}, {
duration: 0
});
tween(windButton, {
alpha: 0.8
}, {
duration: windAttackCooldown * 16.67
});
}
}
var cube = LK.getAsset('cube', {
anchorX: 0.5,
anchorY: 0.5
});
cube.x = 1024;
cube.y = 1366;
game.addChild(cube);
var hero = game.addChild(new Hero());
hero.x = 1024;
hero.y = 1366;
hero.health = 3;
hero.maxHealth = 3;
hero.lastDamageTime = 0;
hero.invulnerabilityDuration = 120; // 2 seconds of invulnerability after taking damage
var shadows = [];
var lightProjectiles = [];
var fireSpells = [];
var lightOrbs = [];
var iceAttacks = [];
var windAttacks = [];
var lastShadowSpawn = 0;
var shadowSpawnRate = 120; // Start spawning every 2 seconds
var waveLevel = 1;
var score = 0;
var lastFireSpell = 0;
var fireSpellCooldown = 90; // 1.5 seconds cooldown at 60fps
var maxActiveFireSpells = 3;
var lastLightAttack = 0;
var lightAttackCooldown = 420; // 7 seconds cooldown at 60fps
var orbsUsedThisWave = 0;
var maxOrbsPerWave = 7;
var waveEnemiesRemaining = 0;
var waveEnemiesTotal = 0;
var waveActive = false;
var waveStartDelay = 0;
var waveDifficultyMultiplier = 1.0;
var shelterActive = false;
var shelterScore = 10000; // Score threshold to activate shelter
var shelterDuration = 300; // 5 seconds at 60fps
var shelterTimer = 0;
var shelterGraphics = null;
var damageBonusActive = false;
var damageBonusTimer = 0;
var damageBonusDuration = 360; // 6 seconds at 60fps
var shieldActive = false;
var shieldTimer = 0;
var shieldDuration = 360; // 6 seconds at 60fps
var lastIceAttack = 0;
var iceAttackCooldown = 480; // 8 seconds cooldown
var lastWindAttack = 0;
var windAttackCooldown = 360; // 6 seconds cooldown
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var waveTxt = new Text2('Wave: 1', {
size: 50,
fill: 0xFFFF00
});
waveTxt.anchor.set(0.5, 0);
waveTxt.y = 70;
LK.gui.top.addChild(waveTxt);
var waveProgressTxt = new Text2('Enemies: 0/0', {
size: 40,
fill: 0x00FFFF
});
waveProgressTxt.anchor.set(0.5, 0);
waveProgressTxt.y = 130;
LK.gui.top.addChild(waveProgressTxt);
var healthTxt = new Text2('Health: 3/3', {
size: 50,
fill: 0xFF0000
});
healthTxt.anchor.set(0.5, 0);
healthTxt.y = 180;
LK.gui.top.addChild(healthTxt);
// Create fire button
var fireButton = LK.getAsset('fireSpell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.8
});
fireButton.x = 200;
fireButton.y = 2500;
game.addChild(fireButton);
// Create light attack button
var lightButton = LK.getAsset('lightOrb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.8
});
lightButton.x = 1800;
lightButton.y = 2500;
game.addChild(lightButton);
// Create light projectile button
var lightProjectileButton = LK.getAsset('lightProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0,
alpha: 0.8
});
lightProjectileButton.x = 1024;
lightProjectileButton.y = 2500;
game.addChild(lightProjectileButton);
// Create guided fire button
var iceButton = LK.getAsset('fireSpell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.8
});
iceButton.x = 512;
iceButton.y = 2500;
game.addChild(iceButton);
iceButton.down = handleGuidedFireButton;
// Create explosive orb button
var windButton = LK.getAsset('lightOrb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.8
});
windButton.x = 1536;
windButton.y = 2500;
game.addChild(windButton);
windButton.down = handleExplosiveOrbButton;
// Fire button press handler
fireButton.down = function (x, y, obj) {
// Fire spell at nearest enemy - check cooldown and limit
var timeSinceLastFire = LK.ticks - lastFireSpell;
if (timeSinceLastFire >= fireSpellCooldown && fireSpells.length < maxActiveFireSpells && shadows.length > 0) {
// Find nearest shadow
var nearestShadow = null;
var nearestDistance = Infinity;
for (var i = 0; i < shadows.length; i++) {
var distance = Math.sqrt(Math.pow(hero.x - shadows[i].x, 2) + Math.pow(hero.y - shadows[i].y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestShadow = shadows[i];
}
}
;
// Light attack button press handler
lightButton.down = function (x, y, obj) {
// Check cooldown and orb usage limit
var timeSinceLastLight = LK.ticks - lastLightAttack;
if (timeSinceLastLight >= lightAttackCooldown && orbsUsedThisWave < maxOrbsPerWave) {
// Destroy all enemies on screen
for (var i = shadows.length - 1; i >= 0; i--) {
var shadow = shadows[i];
// Create light orb for each destroyed shadow
var newOrb = new LightOrb();
newOrb.x = shadow.x;
newOrb.y = shadow.y;
newOrb.baseY = newOrb.y;
lightOrbs.push(newOrb);
game.addChild(newOrb);
// Add score based on monster type
var scoreValue = shadow.monsterType === 'tank' ? 25 : shadow.monsterType === 'splitter' ? 30 : shadow.monsterType === 'teleporter' ? 20 : shadow.monsterType === 'bomber' ? 35 : shadow.monsterType === 'healer' ? 40 : shadow.monsterType === 'shielder' ? 35 : shadow.monsterType === 'berserker' ? 45 : shadow.monsterType === 'mimic' ? 50 : 10;
score += scoreValue;
// Flash effect on each enemy before destruction
tween(shadow, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
shadow.destroy();
}
});
shadows.splice(i, 1);
}
// Update score display
scoreTxt.setText('Score: ' + score);
// Play sound effect
LK.getSound('shadowDestroy').play();
// Screen flash effect
LK.effects.flashScreen(0xffffaa, 800);
// Button feedback with cooldown visual
LK.effects.flashObject(lightButton, 0xffffaa, 400);
tween(lightButton, {
alpha: 0.3
}, {
duration: 0
});
tween(lightButton, {
alpha: 0.8
}, {
duration: lightAttackCooldown * 16.67
}); // Convert ticks to ms
// Update cooldown and usage counter
lastLightAttack = LK.ticks;
orbsUsedThisWave++;
}
};
// Light projectile button press handler
lightProjectileButton.down = function (x, y, obj) {
// Fire light projectile
var newProjectile = new LightProjectile();
newProjectile.x = hero.x;
newProjectile.y = hero.y;
// Fire in 8 directions
var directions = 8;
for (var i = 0; i < directions; i++) {
var angle = i / directions * Math.PI * 2;
var projectile = new LightProjectile();
projectile.x = hero.x;
projectile.y = hero.y;
projectile.velocityX = Math.cos(angle) * 8;
projectile.velocityY = Math.sin(angle) * 8;
lightProjectiles.push(projectile);
game.addChild(projectile);
}
// Button feedback
LK.effects.flashObject(lightProjectileButton, 0xffffaa, 200);
};
if (nearestShadow) {
var newFireSpell = new FireSpell();
newFireSpell.x = nearestShadow.x;
newFireSpell.y = nearestShadow.y;
fireSpells.push(newFireSpell);
game.addChild(newFireSpell);
lastFireSpell = LK.ticks;
LK.getSound('fireBlast').play();
// Button feedback
LK.effects.flashObject(fireButton, 0xff4400, 200);
}
}
};
var dragNode = null;
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
// Keep hero within bounds
if (dragNode.x < 40) dragNode.x = 40;
if (dragNode.x > 2008) dragNode.x = 2008;
if (dragNode.y < 40) dragNode.y = 40;
if (dragNode.y > 2692) dragNode.y = 2692;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Check if touching hero
var heroDistance = Math.sqrt(Math.pow(x - hero.x, 2) + Math.pow(y - hero.y, 2));
if (heroDistance < 60) {
dragNode = hero;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
function startWave() {
waveActive = true;
waveStartDelay = 180; // 3 second delay before spawning
waveEnemiesTotal = Math.min(5 + waveLevel * 2, 30); // Cap at 30 enemies
waveEnemiesRemaining = waveEnemiesTotal;
waveDifficultyMultiplier = 1.0 + (waveLevel - 1) * 0.15; // 15% increase per wave
shadowSpawnRate = Math.max(30, 120 - waveLevel * 8); // Faster spawning each wave
orbsUsedThisWave = 0; // Reset orb usage for new wave
waveTxt.setText('Wave: ' + waveLevel);
waveProgressTxt.setText('Enemies: ' + (waveEnemiesTotal - waveEnemiesRemaining) + '/' + waveEnemiesTotal);
LK.effects.flashScreen(0x00FF00, 500);
}
game.update = function () {
// Wave management
if (!waveActive && shadows.length === 0) {
// Start new wave when no enemies remain
startWave();
}
// Spawn shadows during active wave
if (waveActive && waveEnemiesRemaining > 0 && waveStartDelay <= 0 && LK.ticks - lastShadowSpawn >= shadowSpawnRate) {
var newShadow;
// Choose random monster type based on wave level
var monsterRoll = Math.random();
if (waveLevel < 3) {
// Early waves: mostly basic shadows
if (monsterRoll < 0.7) newShadow = new Shadow();else if (monsterRoll < 0.9) newShadow = new FastShadow();else newShadow = new TankShadow();
} else if (waveLevel < 6) {
// Mid waves: introduce more variety
if (monsterRoll < 0.4) newShadow = new Shadow();else if (monsterRoll < 0.6) newShadow = new FastShadow();else if (monsterRoll < 0.75) newShadow = new TankShadow();else if (monsterRoll < 0.9) newShadow = new SplitterShadow();else newShadow = new TeleporterShadow();
} else if (waveLevel < 10) {
// Late waves: all monster types including dangerous ones
if (monsterRoll < 0.2) newShadow = new Shadow();else if (monsterRoll < 0.35) newShadow = new FastShadow();else if (monsterRoll < 0.5) newShadow = new TankShadow();else if (monsterRoll < 0.65) newShadow = new SplitterShadow();else if (monsterRoll < 0.75) newShadow = new TeleporterShadow();else if (monsterRoll < 0.85) newShadow = new BomberShadow();else if (monsterRoll < 0.92) newShadow = new HealerShadow();else newShadow = new ShielderShadow();
} else {
// Very late waves: all enemy types including new ones
if (monsterRoll < 0.15) newShadow = new Shadow();else if (monsterRoll < 0.25) newShadow = new FastShadow();else if (monsterRoll < 0.35) newShadow = new TankShadow();else if (monsterRoll < 0.45) newShadow = new SplitterShadow();else if (monsterRoll < 0.55) newShadow = new TeleporterShadow();else if (monsterRoll < 0.65) newShadow = new BomberShadow();else if (monsterRoll < 0.75) newShadow = new HealerShadow();else if (monsterRoll < 0.85) newShadow = new ShielderShadow();else if (monsterRoll < 0.93) newShadow = new BerserkerShadow();else newShadow = new MimicShadow();
}
// Spawn from random edge
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
newShadow.x = Math.random() * 2048;
newShadow.y = -30;
break;
case 1:
// Right
newShadow.x = 2078;
newShadow.y = Math.random() * 2732;
break;
case 2:
// Bottom
newShadow.x = Math.random() * 2048;
newShadow.y = 2762;
break;
case 3:
// Left
newShadow.x = -30;
newShadow.y = Math.random() * 2732;
break;
}
// Apply wave difficulty multiplier
newShadow.speed = (newShadow.speed + waveLevel * 0.3) * waveDifficultyMultiplier;
newShadow.health = Math.ceil(newShadow.health * waveDifficultyMultiplier);
newShadow.maxHealth = newShadow.health;
shadows.push(newShadow);
game.addChild(newShadow);
lastShadowSpawn = LK.ticks;
waveEnemiesRemaining--;
waveProgressTxt.setText('Enemies: ' + (waveEnemiesTotal - waveEnemiesRemaining) + '/' + waveEnemiesTotal);
}
// Wave start delay countdown
if (waveStartDelay > 0) {
waveStartDelay--;
}
// Check if wave is complete
if (waveActive && waveEnemiesRemaining <= 0 && shadows.length === 0) {
waveActive = false;
waveLevel++;
// Wave completion bonus
score += waveLevel * 50;
scoreTxt.setText('Score: ' + score);
LK.effects.flashScreen(0xFFFF00, 800);
}
// Check hero collision with shadows
for (var i = 0; i < shadows.length; i++) {
var shadow = shadows[i];
if (hero.intersects(shadow) && !shelterActive && !shieldActive) {
// Check if hero is not invulnerable
if (LK.ticks - hero.lastDamageTime > hero.invulnerabilityDuration) {
hero.health--;
hero.lastDamageTime = LK.ticks;
healthTxt.setText('Health: ' + hero.health + '/' + hero.maxHealth);
// Flash effect and make hero temporarily invulnerable
LK.effects.flashScreen(0xff0000, 500);
LK.effects.flashObject(hero, 0xff0000, hero.invulnerabilityDuration * 16.67);
// Make hero semi-transparent during invulnerability
tween(hero, {
alpha: 0.5
}, {
duration: 0
});
tween(hero, {
alpha: 1.0
}, {
duration: hero.invulnerabilityDuration * 16.67
});
if (hero.health <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
// Check bomber explosions
if (shadow.monsterType === 'bomber' && shadow.exploding && shadow.explosionTimer <= 0) {
var explosionDistance = Math.sqrt(Math.pow(hero.x - shadow.x, 2) + Math.pow(hero.y - shadow.y, 2));
if (explosionDistance < 120 && !shelterActive && !shieldActive) {
// Check if hero is not invulnerable
if (LK.ticks - hero.lastDamageTime > hero.invulnerabilityDuration) {
hero.health--;
hero.lastDamageTime = LK.ticks;
healthTxt.setText('Health: ' + hero.health + '/' + hero.maxHealth);
// Flash effect and make hero temporarily invulnerable
LK.effects.flashScreen(0xff4400, 500);
LK.effects.flashObject(hero, 0xff4400, hero.invulnerabilityDuration * 16.67);
// Make hero semi-transparent during invulnerability
tween(hero, {
alpha: 0.5
}, {
duration: 0
});
tween(hero, {
alpha: 1.0
}, {
duration: hero.invulnerabilityDuration * 16.67
});
if (hero.health <= 0) {
LK.effects.flashScreen(0xff4400, 1000);
LK.showGameOver();
return;
}
}
}
// Remove exploded bomber
shadow.destroy();
shadows.splice(i, 1);
i--; // Adjust index after removal
}
}
// Update and check light projectiles
for (var j = lightProjectiles.length - 1; j >= 0; j--) {
var projectile = lightProjectiles[j];
if (projectile.lifetime <= 0 || projectile.x < -50 || projectile.x > 2098 || projectile.y < -50 || projectile.y > 2782) {
projectile.destroy();
lightProjectiles.splice(j, 1);
continue;
}
// Check collision with shadows
for (var k = shadows.length - 1; k >= 0; k--) {
if (projectile.intersects(shadows[k])) {
var shadow = shadows[k];
// Check if enemy is shielded
var damage = damageBonusActive ? 2 : 1;
if (shadow.monsterType === 'shielder' && shadow.shieldActive) {
damage = 0; // No damage when shielded
LK.effects.flashObject(shadow, 0x4444ff, 100);
} else {
shadow.health -= damage;
}
var shadowDestroyed = false;
if (shadow.health <= 0) {
// Handle special monster behaviors on death
if (shadow.monsterType === 'splitter' && shadow.health <= 0) {
// Create two smaller fast shadows
for (var split = 0; split < 2; split++) {
var miniShadow = new FastShadow();
miniShadow.x = shadow.x + (split === 0 ? -30 : 30);
miniShadow.y = shadow.y + (Math.random() - 0.5) * 60;
miniShadow.speed *= 0.8;
shadows.push(miniShadow);
game.addChild(miniShadow);
}
}
if (shadow.monsterType === 'bomber' && shadow.exploding && shadow.explosionTimer > 0) {
// Bomber was destroyed while exploding, cancel explosion
shadow.exploding = false;
}
// Create light orb
var newOrb = new LightOrb();
newOrb.x = shadow.x;
newOrb.y = shadow.y;
newOrb.baseY = newOrb.y;
lightOrbs.push(newOrb);
game.addChild(newOrb);
shadow.destroy();
shadows.splice(k, 1);
shadowDestroyed = true;
var scoreValue = shadow.monsterType === 'tank' ? 25 : shadow.monsterType === 'splitter' ? 30 : shadow.monsterType === 'teleporter' ? 20 : shadow.monsterType === 'bomber' ? 35 : shadow.monsterType === 'healer' ? 40 : shadow.monsterType === 'shielder' ? 35 : shadow.monsterType === 'berserker' ? 45 : shadow.monsterType === 'mimic' ? 50 : 10;
score += scoreValue;
scoreTxt.setText('Score: ' + score);
LK.getSound('shadowDestroy').play();
} else {
// Monster took damage but not destroyed
LK.effects.flashObject(shadow, 0xffffff, 200);
}
projectile.destroy();
lightProjectiles.splice(j, 1);
break;
}
}
}
// Update and check fire spells
for (var l = fireSpells.length - 1; l >= 0; l--) {
var spell = fireSpells[l];
if (spell.lifetime <= 0) {
spell.destroy();
fireSpells.splice(l, 1);
continue;
}
// Check collision with shadows
for (var m = shadows.length - 1; m >= 0; m--) {
var distance = Math.sqrt(Math.pow(spell.x - shadows[m].x, 2) + Math.pow(spell.y - shadows[m].y, 2));
if (distance < spell.radius) {
var shadow = shadows[m];
// Check if enemy is shielded
var damage = damageBonusActive ? 4 : 2; // Fire spells do more damage
if (shadow.monsterType === 'shielder' && shadow.shieldActive) {
damage = Math.ceil(damage / 2); // Reduced damage when shielded
LK.effects.flashObject(shadow, 0x4444ff, 100);
}
shadow.health -= damage;
if (shadow.health <= 0) {
// Handle special monster behaviors on death
if (shadow.monsterType === 'splitter') {
// Create two smaller fast shadows
for (var split = 0; split < 2; split++) {
var miniShadow = new FastShadow();
miniShadow.x = shadow.x + (split === 0 ? -30 : 30);
miniShadow.y = shadow.y + (Math.random() - 0.5) * 60;
miniShadow.speed *= 0.8;
shadows.push(miniShadow);
game.addChild(miniShadow);
}
}
if (shadow.monsterType === 'bomber' && shadow.exploding && shadow.explosionTimer > 0) {
// Bomber was destroyed while exploding, cancel explosion
shadow.exploding = false;
}
// Create light orb
var newOrb = new LightOrb();
newOrb.x = shadow.x;
newOrb.y = shadow.y;
newOrb.baseY = newOrb.y;
lightOrbs.push(newOrb);
game.addChild(newOrb);
shadow.destroy();
shadows.splice(m, 1);
var scoreValue = shadow.monsterType === 'tank' ? 30 : shadow.monsterType === 'splitter' ? 35 : shadow.monsterType === 'teleporter' ? 25 : shadow.monsterType === 'bomber' ? 40 : shadow.monsterType === 'healer' ? 45 : shadow.monsterType === 'shielder' ? 40 : shadow.monsterType === 'berserker' ? 50 : shadow.monsterType === 'mimic' ? 55 : 15;
score += scoreValue;
scoreTxt.setText('Score: ' + score);
LK.getSound('shadowDestroy').play();
} else {
// Monster took damage but not destroyed
LK.effects.flashObject(shadow, 0xff8800, 200);
}
}
}
}
// Check light orb collection
for (var n = lightOrbs.length - 1; n >= 0; n--) {
var orb = lightOrbs[n];
if (hero.intersects(orb)) {
hero.lightPower++;
if (hero.fireRate > 20) {
hero.fireRate -= 2;
}
orb.destroy();
lightOrbs.splice(n, 1);
LK.getSound('collectOrb').play();
// Flash hero with light effect
LK.effects.flashObject(hero, 0xffffaa, 500);
}
}
// Shelter activation check
if (score >= shelterScore && !shelterActive) {
shelterActive = true;
shelterTimer = shelterDuration;
// Create shelter visual
shelterGraphics = LK.getAsset('shelter', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
shelterGraphics.x = hero.x;
shelterGraphics.y = hero.y;
game.addChild(shelterGraphics);
// Visual feedback for shelter activation
LK.effects.flashScreen(0x00ffff, 1000);
LK.effects.flashObject(hero, 0x00ffff, 1000);
// Make hero semi-transparent during shelter
hero.alpha = 0.5;
// Pulsing effect for shelter
tween(shelterGraphics, {
alpha: 0.6
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(shelterGraphics, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
}
// Shelter timer countdown
if (shelterActive) {
shelterTimer--;
// Move shelter with hero
if (shelterGraphics) {
shelterGraphics.x = hero.x;
shelterGraphics.y = hero.y;
}
if (shelterTimer <= 0) {
shelterActive = false;
hero.alpha = 1.0; // Restore normal visibility
// Remove shelter visual
if (shelterGraphics) {
shelterGraphics.destroy();
shelterGraphics = null;
}
// Activate damage bonus and shield after leaving shelter
damageBonusActive = true;
damageBonusTimer = damageBonusDuration;
shieldActive = true;
shieldTimer = shieldDuration;
// Visual feedback for bonuses
LK.effects.flashScreen(0xff8800, 800);
LK.effects.flashObject(hero, 0xff8800, 1000);
// Increase shelter score threshold for next activation
shelterScore += 10000;
}
}
// Damage bonus timer countdown
if (damageBonusActive) {
damageBonusTimer--;
if (damageBonusTimer <= 0) {
damageBonusActive = false;
}
}
// Shield timer countdown
if (shieldActive) {
shieldTimer--;
if (shieldTimer <= 0) {
shieldActive = false;
}
}
// Update and check guided fire attacks
for (var p = iceAttacks.length - 1; p >= 0; p--) {
var guidedFire = iceAttacks[p];
if (guidedFire.lifetime <= 0) {
guidedFire.destroy();
iceAttacks.splice(p, 1);
continue;
}
// Check collision with shadows
for (var q = shadows.length - 1; q >= 0; q--) {
if (guidedFire.intersects(shadows[q])) {
var shadow = shadows[q];
// Check if enemy is shielded
var damage = damageBonusActive ? guidedFire.damage * 2 : guidedFire.damage;
if (shadow.monsterType === 'shielder' && shadow.shieldActive) {
damage = Math.ceil(damage / 2);
LK.effects.flashObject(shadow, 0x4444ff, 100);
}
shadow.health -= damage;
if (shadow.health <= 0) {
// Handle special monster behaviors on death
if (shadow.monsterType === 'splitter') {
for (var split = 0; split < 2; split++) {
var miniShadow = new FastShadow();
miniShadow.x = shadow.x + (split === 0 ? -30 : 30);
miniShadow.y = shadow.y + (Math.random() - 0.5) * 60;
miniShadow.speed *= 0.8;
shadows.push(miniShadow);
game.addChild(miniShadow);
}
}
// Create light orb
var newOrb = new LightOrb();
newOrb.x = shadow.x;
newOrb.y = shadow.y;
newOrb.baseY = newOrb.y;
lightOrbs.push(newOrb);
game.addChild(newOrb);
shadow.destroy();
shadows.splice(q, 1);
var scoreValue = shadow.monsterType === 'tank' ? 25 : shadow.monsterType === 'splitter' ? 30 : shadow.monsterType === 'teleporter' ? 20 : shadow.monsterType === 'bomber' ? 35 : shadow.monsterType === 'healer' ? 40 : shadow.monsterType === 'shielder' ? 35 : shadow.monsterType === 'berserker' ? 45 : shadow.monsterType === 'mimic' ? 50 : 10;
score += scoreValue;
scoreTxt.setText('Score: ' + score);
LK.getSound('shadowDestroy').play();
} else {
LK.effects.flashObject(shadow, 0xff4400, 200);
}
guidedFire.destroy();
iceAttacks.splice(p, 1);
break;
}
}
}
// Update and check explosive orbs
for (var r = windAttacks.length - 1; r >= 0; r--) {
var explosiveOrb = windAttacks[r];
var shouldExplode = false;
// Check if orb should explode (lifetime ended, hit enemy, or went off screen)
if (explosiveOrb.lifetime <= 0 || explosiveOrb.x < -100 || explosiveOrb.x > 2148 || explosiveOrb.y < -100 || explosiveOrb.y > 2832) {
shouldExplode = true;
} else {
// Check collision with shadows for immediate explosion
for (var s = 0; s < shadows.length; s++) {
if (explosiveOrb.intersects(shadows[s])) {
shouldExplode = true;
break;
}
}
}
if (shouldExplode) {
// Create explosion effect
LK.effects.flashScreen(0xff8800, 300);
// Damage all enemies within explosion radius
for (var s = shadows.length - 1; s >= 0; s--) {
var shadow = shadows[s];
var distance = Math.sqrt(Math.pow(explosiveOrb.x - shadow.x, 2) + Math.pow(explosiveOrb.y - shadow.y, 2));
if (distance < explosiveOrb.explosionRadius) {
var damage = damageBonusActive ? explosiveOrb.damage * 2 : explosiveOrb.damage;
if (shadow.monsterType === 'shielder' && shadow.shieldActive) {
damage = Math.ceil(damage / 2);
LK.effects.flashObject(shadow, 0x4444ff, 100);
}
shadow.health -= damage;
if (shadow.health <= 0) {
// Handle special monster behaviors on death
if (shadow.monsterType === 'splitter') {
for (var split = 0; split < 2; split++) {
var miniShadow = new FastShadow();
miniShadow.x = shadow.x + (split === 0 ? -30 : 30);
miniShadow.y = shadow.y + (Math.random() - 0.5) * 60;
miniShadow.speed *= 0.8;
shadows.push(miniShadow);
game.addChild(miniShadow);
}
}
// Create light orb
var newOrb = new LightOrb();
newOrb.x = shadow.x;
newOrb.y = shadow.y;
newOrb.baseY = newOrb.y;
lightOrbs.push(newOrb);
game.addChild(newOrb);
shadow.destroy();
shadows.splice(s, 1);
var scoreValue = shadow.monsterType === 'tank' ? 25 : shadow.monsterType === 'splitter' ? 30 : shadow.monsterType === 'teleporter' ? 20 : shadow.monsterType === 'bomber' ? 35 : shadow.monsterType === 'healer' ? 40 : shadow.monsterType === 'shielder' ? 35 : shadow.monsterType === 'berserker' ? 45 : shadow.monsterType === 'mimic' ? 50 : 10;
score += scoreValue;
scoreTxt.setText('Score: ' + score);
LK.getSound('shadowDestroy').play();
} else {
LK.effects.flashObject(shadow, 0xff8800, 200);
}
}
}
explosiveOrb.destroy();
windAttacks.splice(r, 1);
}
}
// Game is now infinite - no victory condition
};
// Start first wave
startWave();
bola de fuego de pixeles. In-Game asset. 2d. High contrast. No shadows
monstruo de slime negro de pixeles. In-Game asset. 2d. High contrast. No shadows
suelo de sesped oscuro de pixeles. In-Game asset. 2d. High contrast. No shadows
bola de luz de pixeles. In-Game asset. 2d. High contrast. No shadows
sombra de pixeles que de miedo. In-Game asset. 2d. High contrast. No shadows
portal de pixeles de luz. In-Game asset. 2d. High contrast. No shadows
angel en forma de ojo de pixeles. In-Game asset. 2d. High contrast. No shadows
que ahora tenga una exprecion de determinacion
fuego negro de pixeles. In-Game asset. 2d. High contrast. No shadows
bola de luz azul de pixeles. In-Game asset. 2d. High contrast. No shadows
la muerte de pixeles. In-Game asset. 2d. High contrast. No shadows
orbe oscuro de pixeles con cara enojada. In-Game asset. 2d. High contrast. No shadows
berserquer de pixeles. In-Game asset. 2d. High contrast. No shadows
sombra mimica de pixeles. In-Game asset. 2d. High contrast. No shadows
casa de madera con barricadas de pixeles. In-Game asset. 2d. High contrast. No shadows
bola de hielo de pixeles. In-Game asset. 2d. High contrast. No shadows
bola de viento de pixeles. In-Game asset. 2d. High contrast. No shadows