/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Enemy = Container.expand(function (enemyType) { var self = Container.call(this); self.enemyType = enemyType; self.health = 1; self.speed = 2; self.damage = 1; self.points = 10; self.lastHitTime = 0; self.slowedTimer = 0; self.originalSpeed = 0; self.poisonedTimer = 0; self.poisonDamageTimer = 0; var enemyGraphics; if (enemyType === 'goblin') { enemyGraphics = self.attachAsset('goblin', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1; self.speed = 3; self.points = 10; } else if (enemyType === 'demon') { enemyGraphics = self.attachAsset('demon', { anchorX: 0.5, anchorY: 0.5 }); self.health = 3; self.speed = 1.5; self.points = 30; } else if (enemyType === 'flyingEye') { enemyGraphics = self.attachAsset('flyingEye', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1; self.speed = 4; self.points = 20; } else if (enemyType === 'golem') { enemyGraphics = self.attachAsset('golem', { anchorX: 0.5, anchorY: 0.5 }); self.health = 5; self.speed = 1; self.points = 50; } else if (enemyType === 'skeleton') { enemyGraphics = self.attachAsset('skeleton', { anchorX: 0.5, anchorY: 0.5 }); self.health = 2; self.speed = 2; self.points = 20; } else if (enemyType === 'mushroom') { enemyGraphics = self.attachAsset('mushroom', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1; self.speed = 1; self.points = 15; } else if (enemyType === 'hellhound') { enemyGraphics = self.attachAsset('hellhound', { anchorX: 0.5, anchorY: 0.5 }); self.health = 2; self.speed = 5; self.points = 25; } else if (enemyType === 'darkSorcerer') { enemyGraphics = self.attachAsset('darkSorcerer', { anchorX: 0.5, anchorY: 0.5 }); self.health = 3; self.speed = 1.5; self.points = 40; } self.maxHealth = self.health; // Spawn from random edge var edge = Math.floor(Math.random() * 4); if (edge === 0) { // Top self.x = Math.random() * 2048; self.y = -50; } else if (edge === 1) { // Right self.x = 2048 + 50; self.y = Math.random() * 2732; } else if (edge === 2) { // Bottom self.x = Math.random() * 2048; self.y = 2732 + 50; } else { // Left self.x = -50; self.y = Math.random() * 2732; } self.applySlowEffect = function () { if (self.slowedTimer <= 0) { self.originalSpeed = self.speed; } self.slowedTimer = 180; // 3 seconds self.speed = self.originalSpeed * 0.3; // 70% speed reduction tween(enemyGraphics, { tint: 0x87CEEB }, { duration: 200 }); }; self.stunTimer = 0; self.applyStunEffect = function () { self.stunTimer = 42; // 0.7 seconds at 60fps if (!self.originalSpeed) { self.originalSpeed = self.speed; } self.speed = 0; // Complete stop when stunned tween(enemyGraphics, { tint: 0xFFFF00 }, { duration: 200 }); }; self.applyPoisonEffect = function () { self.poisonedTimer = 120; // 2 seconds at 60fps self.poisonDamageTimer = 0; // Reset damage timer tween(enemyGraphics, { tint: 0x00FF00 // Green tint for poison }, { duration: 200 }); }; self.takeDamage = function (damage) { // Prevent taking damage if already dead if (self.health <= 0) { return true; } self.health -= damage; self.lastHitTime = LK.ticks; // Flash red when hit tween(enemyGraphics, { tint: 0xFF0000 }, { duration: 100, onFinish: function onFinish() { tween(enemyGraphics, { tint: 0xFFFFFF }, { duration: 100 }); } }); LK.getSound('enemyHit').play(); if (self.health <= 0) { self.health = 0; // Ensure health doesn't go negative return true; // Enemy is dead } return false; }; self.update = function () { // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.speed = self.originalSpeed || self.speed; tween(enemyGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } return; // Don't move while stunned } // Handle slow effect if (self.slowedTimer > 0) { self.slowedTimer--; if (self.slowedTimer <= 0) { self.speed = self.originalSpeed; tween(enemyGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } } // Handle poison effect if (self.poisonedTimer > 0) { self.poisonedTimer--; self.poisonDamageTimer++; // Deal damage every 18 frames (0.3 seconds at 60fps) if (self.poisonDamageTimer >= 18) { self.poisonDamageTimer = 0; self.takeDamage(1); // Small poison damage } // Remove poison effect when timer expires if (self.poisonedTimer <= 0) { tween(enemyGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } } // Move towards wizard var dx = wizard.x - self.x; var dy = wizard.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; // Flying eye has erratic movement if (self.enemyType === 'flyingEye') { moveX += (Math.random() - 0.5) * 2; moveY += (Math.random() - 0.5) * 2; } self.x += moveX; self.y += moveY; } }; return self; }); var Spell = Container.expand(function (spellType, startX, startY, targetX, targetY) { var self = Container.call(this); self.spellType = spellType; self.speed = 8; self.damage = 1; self.lifeTime = 180; // 3 seconds at 60fps self.age = 0; var spellGraphics; if (spellType === 'fireball') { spellGraphics = self.attachAsset('fireball', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 2; } else if (spellType === 'iceShard') { spellGraphics = self.attachAsset('iceShard', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 1; self.slowEffect = true; } else if (spellType === 'lightning') { spellGraphics = self.attachAsset('lightning', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 3; self.speed = 15; } else if (spellType === 'poisonCloud') { spellGraphics = self.attachAsset('poisonCloud', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 1; self.speed = 0; self.lifeTime = 210; // 3.5 seconds total duration (210 frames at 60fps) self.lastDamageTick = 0; self.damageInterval = 18; // Damage every 0.3 seconds (18 frames at 60fps) spellGraphics.alpha = 0.6; } else if (spellType === 'explosion') { spellGraphics = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 4; self.speed = 0; self.lifeTime = 30; spellGraphics.alpha = 0.8; } self.x = startX; self.y = startY; // Calculate direction var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; } else { self.velocityX = 0; self.velocityY = 0; } self.createExplosion = function () { var explosion = new Spell('explosion', self.x, self.y, self.x, self.y); spells.push(explosion); game.addChild(explosion); }; self.update = function () { self.age++; if (self.spellType !== 'poisonCloud' && self.spellType !== 'explosion') { self.x += self.velocityX; self.y += self.velocityY; } if (self.spellType === 'explosion') { var scale = 1 + self.age / 30 * 2; spellGraphics.scaleX = scale; spellGraphics.scaleY = scale; } }; return self; }); var SpellButton = Container.expand(function (spellType, buttonX, buttonY) { var self = Container.call(this); self.spellType = spellType; self.cooldown = 0; self.maxCooldown = 60; // 1 second self.manaCost = 10; // Default mana cost var buttonBg = self.attachAsset('spellButton', { anchorX: 0.5, anchorY: 0.5 }); var spellNames = { 'fireball': 'Fire', 'iceShard': 'Ice', 'lightning': 'Bolt', 'shield': 'Shield', 'teleport': 'Port', 'poisonCloud': 'Poison', 'mindControl': 'Mind', 'explosion': 'Boom', 'timeSlow': 'Stop', 'storm': 'Storm', 'manaRestorer': 'Restore', 'heal': 'Heal' }; var buttonText = new Text2(spellNames[spellType] || spellType, { size: 32, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.spellName = spellNames[spellType] || spellType; self.x = buttonX; self.y = buttonY; // Set different cooldowns and mana costs for different spells if (spellType === 'fireball') { self.maxCooldown = 30; self.manaCost = 15; } else if (spellType === 'iceShard') { self.maxCooldown = 25; self.manaCost = 10; } else if (spellType === 'lightning') { self.maxCooldown = 45; self.manaCost = 25; } else if (spellType === 'shield') { self.maxCooldown = 180; self.manaCost = 40; } else if (spellType === 'teleport') { self.maxCooldown = 120; self.manaCost = 30; } else if (spellType === 'poisonCloud') { self.maxCooldown = 390; // 6.5 seconds at 60fps self.manaCost = 35; } else if (spellType === 'mindControl') { self.maxCooldown = 150; self.manaCost = 50; } else if (spellType === 'explosion') { self.maxCooldown = 80; self.manaCost = 45; } else if (spellType === 'timeSlow') { self.maxCooldown = 270; // 4.5 seconds self.manaCost = 60; } else if (spellType === 'storm') { self.maxCooldown = 522; // 8.7 seconds self.manaCost = 70; } else if (spellType === 'manaRestorer') { self.maxCooldown = 1000; // 10 seconds self.manaCost = 0; // No mana cost } else if (spellType === 'heal') { self.maxCooldown = 1200; // 20 seconds at 60fps self.manaCost = 130; } self.canCast = function () { if (self.spellType === 'manaRestorer') { return self.cooldown <= 0; // Mana restorer ignores mana cost and incapacity } return self.cooldown <= 0 && wizardMana >= self.manaCost && incapacityTimer <= 0; }; self.cast = function () { if (self.canCast()) { self.cooldown = self.maxCooldown; return true; } return false; }; self.update = function () { if (self.cooldown > 0) { self.cooldown--; buttonBg.tint = 0x666666; buttonText.fill = 0x999999; // Show cooldown duration var cooldownSeconds = Math.ceil(self.cooldown / 60); buttonText.setText(cooldownSeconds + 's'); } else if (self.spellType !== 'manaRestorer' && incapacityTimer > 0) { // Incapacitated - show red tint buttonBg.tint = 0xFF4444; buttonText.fill = 0xFF8888; var incapacitySeconds = Math.ceil(incapacityTimer / 60); buttonText.setText('Inc: ' + incapacitySeconds + 's'); } else if (wizardMana < self.manaCost) { // Not enough mana - show blue tint buttonBg.tint = 0x4444FF; buttonText.fill = 0x8888FF; buttonText.setText(self.spellName); } else { buttonBg.tint = 0xFFFFFF; buttonText.fill = 0xFFFFFF; buttonText.setText(self.spellName); } }; self.down = function (x, y, obj) { if (self.canCast()) { castSpell(self.spellType); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ // Game variables // Wizard // Spells // Enemies // UI Elements // Sounds var wizard; var enemies = []; var spells = []; var spellButtons = []; var waveNumber = 1; var enemiesSpawned = 0; var enemiesPerWave = 5; var spawnTimer = 0; var spawnDelay = 120; // 2 seconds var gameTime = 0; var shieldActive = false; var shieldObject = null; var timeSlowActive = false; var timeSlowTimer = 0; var stormActive = false; var stormTimer = 0; var stormObject = null; var wizardHealth = 100; var wizardMaxHealth = 100; var wizardMana = 180; var wizardMaxMana = 180; var manaRegenRate = 0.1; // Mana regenerated per frame var incapacityTimer = 0; // Timer for power incapacity after mana restorer var poisonCloudCooldown = 0; // 6.5 second cooldown for poison cloud var stormParticles = []; // Track storm particles var healthBar = null; var healthBarFill = null; var manaBar = null; var manaBarFill = null; // Enemy types for spawning var enemyTypes = ['goblin', 'demon', 'flyingEye', 'golem', 'skeleton', 'mushroom', 'hellhound', 'darkSorcerer']; // Create wizard wizard = game.addChild(new Container()); var wizardGraphics = wizard.attachAsset('wizard', { anchorX: 0.5, anchorY: 0.5 }); wizard.x = 1024; wizard.y = 1366; // Create spell buttons var spellTypes = ['fireball', 'iceShard', 'lightning', 'shield', 'teleport', 'poisonCloud', 'mindControl', 'explosion', 'timeSlow', 'storm', 'manaRestorer', 'heal']; for (var i = 0; i < spellTypes.length; i++) { var buttonX = 150 + i % 6 * 180; // Increased horizontal spacing for larger buttons var buttonY = 2450 + Math.floor(i / 6) * 130; // Increased vertical spacing and moved up slightly var button = new SpellButton(spellTypes[i], buttonX, buttonY); spellButtons.push(button); game.addChild(button); } // Create score display var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); scoreTxt.x = 150; scoreTxt.y = 100; game.addChild(scoreTxt); // Create wave display var waveTxt = new Text2('Wave: 1', { size: 50, fill: 0xFFFF00 }); waveTxt.anchor.set(0, 0); waveTxt.x = 150; waveTxt.y = 180; game.addChild(waveTxt); // Create health bar healthBar = game.addChild(new Container()); var healthBarBg = healthBar.attachAsset('healthBarBg', { anchorX: 0, anchorY: 0 }); healthBarFill = healthBar.attachAsset('healthBarFill', { anchorX: 0, anchorY: 0 }); healthBar.x = 150; healthBar.y = 250; // Health bar text var healthTxt = new Text2('Health: 100/100', { size: 40, fill: 0xFFFFFF }); healthTxt.anchor.set(0, 0); healthTxt.x = 150; healthTxt.y = 280; game.addChild(healthTxt); // Create mana bar manaBar = game.addChild(new Container()); var manaBarBg = manaBar.attachAsset('manaBarBg', { anchorX: 0, anchorY: 0 }); manaBarFill = manaBar.attachAsset('manaBarFill', { anchorX: 0, anchorY: 0 }); manaBar.x = 150; manaBar.y = 330; // Mana bar text var manaTxt = new Text2('Mana: 180/180', { size: 40, fill: 0x0080FF }); manaTxt.anchor.set(0, 0); manaTxt.x = 150; manaTxt.y = 360; game.addChild(manaTxt); function castSpell(spellType) { // Find the button and check cooldown var button = null; for (var i = 0; i < spellButtons.length; i++) { if (spellButtons[i].spellType === spellType) { button = spellButtons[i]; break; } } if (!button || !button.cast()) { return; } // Consume mana wizardMana -= button.manaCost; if (wizardMana < 0) { wizardMana = 0; } // Update mana display var manaPercent = wizardMana / wizardMaxMana; manaBarFill.scaleX = manaPercent; manaTxt.setText('Mana: ' + Math.floor(wizardMana) + '/' + wizardMaxMana); LK.getSound('spellCast').play(); if (spellType === 'shield') { activateShield(); } else if (spellType === 'teleport') { teleportWizard(); } else if (spellType === 'timeSlow') { activateTimeSlow(); } else if (spellType === 'mindControl') { mindControlEnemies(); } else if (spellType === 'explosion') { // Explosion at wizard location var spell = new Spell(spellType, wizard.x, wizard.y, wizard.x, wizard.y); spells.push(spell); game.addChild(spell); } else if (spellType === 'storm') { activateStorm(); } else if (spellType === 'manaRestorer') { activateManaRestorer(); } else if (spellType === 'heal') { activateHeal(); } else { // Targeted spells - cast towards nearest enemy var nearestEnemy = findNearestEnemy(); if (nearestEnemy) { var spell = new Spell(spellType, wizard.x, wizard.y, nearestEnemy.x, nearestEnemy.y); spell.targetX = nearestEnemy.x; spell.targetY = nearestEnemy.y; spells.push(spell); game.addChild(spell); } } } function findNearestEnemy() { var nearest = null; var minDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var dx = enemies[i].x - wizard.x; var dy = enemies[i].y - wizard.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < minDistance) { minDistance = distance; nearest = enemies[i]; } } return nearest; } function activateShield() { if (shieldObject) { shieldObject.destroy(); } shieldObject = game.addChild(new Container()); var shieldGraphics = shieldObject.attachAsset('shield', { anchorX: 0.5, anchorY: 0.5 }); shieldGraphics.alpha = 0.5; shieldActive = true; LK.setTimeout(function () { if (shieldObject) { shieldObject.destroy(); shieldObject = null; } shieldActive = false; }, 3000); } function teleportWizard() { // Teleport to a safe random location var attempts = 0; var newX, newY; do { newX = 200 + Math.random() * (2048 - 400); newY = 200 + Math.random() * (2200 - 400); attempts++; } while (attempts < 10 && isLocationUnsafe(newX, newY)); wizard.x = newX; wizard.y = newY; // Teleport effect LK.effects.flashObject(wizard, 0x00FFFF, 500); } function isLocationUnsafe(x, y) { for (var i = 0; i < enemies.length; i++) { var dx = enemies[i].x - x; var dy = enemies[i].y - y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 150) { return true; } } return false; } function activateTimeSlow() { timeSlowActive = true; timeSlowTimer = 90; // 1.5 seconds LK.effects.flashScreen(0x0066FF, 200); // Visual effect on all enemies - freeze them completely for (var i = 0; i < enemies.length; i++) { if (!enemies[i].originalSpeed) { enemies[i].originalSpeed = enemies[i].speed; } enemies[i].speed = 0; // Complete stop tween(enemies[i], { tint: 0x6666FF }, { duration: 300 }); } } function activateStorm() { stormActive = true; stormTimer = 210; // 3.5 seconds at 60fps // Create storm object that follows wizard if (stormObject) { stormObject.destroy(); } stormObject = game.addChild(new Container()); var stormGraphics = stormObject.attachAsset('stormEffect', { anchorX: 0.5, anchorY: 0.5 }); stormGraphics.alpha = 0.8; // Clear existing storm particles for (var p = 0; p < stormParticles.length; p++) { if (stormParticles[p]) { stormParticles[p].destroy(); } } stormParticles = []; // Create storm particles around wizard that follow the storm for (var i = 0; i < 8; i++) { var particle = game.addChild(new Container()); var particleGraphics = particle.attachAsset('iceShard', { anchorX: 0.5, anchorY: 0.5 }); particleGraphics.scaleX = 0.3; particleGraphics.scaleY = 0.3; particleGraphics.tint = 0x87CEEB; particle.x = wizard.x; particle.y = wizard.y; particle.angle = i / 8 * Math.PI * 2; particle.baseRadius = 100 + i * 20; particle.rotationSpeed = 0.1; stormParticles.push(particle); } // Start wizard spinning and shrinking animation tween(wizard, { rotation: Math.PI * 14, // 7 full rotations over 3.5 seconds scaleX: 0.6, // Shrink to 60% size scaleY: 0.6 }, { duration: 3500, easing: tween.linear }); // Visual storm effect LK.effects.flashScreen(0x4169E1, 300); } function activateManaRestorer() { // Instantly restore full mana wizardMana = wizardMaxMana; // Update mana display var manaPercent = wizardMana / wizardMaxMana; manaBarFill.scaleX = manaPercent; manaTxt.setText('Mana: ' + Math.floor(wizardMana) + '/' + wizardMaxMana); // Add 6 seconds of incapacity to all powers incapacityTimer = 432; // 6 seconds at 60fps // Visual effect LK.effects.flashObject(wizard, 0x00FFFF, 1000); LK.effects.flashScreen(0x00FFFF, 300); } function activateHeal() { // Restore to exactly 100 health (full health) wizardHealth = 100; wizardMaxHealth = 100; // Ensure max health is also 100 // Update health bar var healthPercent = wizardHealth / wizardMaxHealth; healthBarFill.scaleX = healthPercent; // Change color based on health if (healthPercent > 0.6) { healthBarFill.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { healthBarFill.tint = 0xffff00; // Yellow } else { healthBarFill.tint = 0xff0000; // Red } // Update health text healthTxt.setText('Health: ' + wizardHealth + '/' + wizardMaxHealth); // Visual healing effect LK.effects.flashObject(wizard, 0x00FF00, 1000); tween(wizard, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(wizard, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeIn }); } }); } function mindControlEnemies() { var controlled = 0; for (var i = 0; i < enemies.length && controlled < 3; i++) { var dx = enemies[i].x - wizard.x; var dy = enemies[i].y - wizard.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 300) { // Mind control effect - purple flash then instant kill tween(enemies[i], { tint: 0x9932CC }, { duration: 200, onFinish: function onFinish() { tween(this, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 300, onFinish: function onFinish() { enemies[i].takeDamage(enemies[i].health); } }); } }); controlled++; } } } function damageWizard(damage) { wizardHealth -= damage; if (wizardHealth < 0) { wizardHealth = 0; } // Update health bar var healthPercent = wizardHealth / wizardMaxHealth; healthBarFill.scaleX = healthPercent; // Change color based on health if (healthPercent > 0.6) { healthBarFill.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { healthBarFill.tint = 0xffff00; // Yellow } else { healthBarFill.tint = 0xff0000; // Red } // Update health text healthTxt.setText('Health: ' + wizardHealth + '/' + wizardMaxHealth); if (wizardHealth <= 0) { LK.effects.flashScreen(0xFF0000, 500); LK.showGameOver(); } } function spawnEnemy() { if (enemiesSpawned < enemiesPerWave) { var enemyType = enemyTypes[Math.floor(Math.random() * Math.min(enemyTypes.length, 2 + Math.floor(waveNumber / 2)))]; var enemy = new Enemy(enemyType); enemies.push(enemy); game.addChild(enemy); enemiesSpawned++; } } function checkCollisions() { // Spells vs Enemies for (var s = spells.length - 1; s >= 0; s--) { var spell = spells[s]; // Area damage spells (explosion, poison cloud) if (spell.spellType === 'explosion' || spell.spellType === 'poisonCloud') { for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; var dx = enemy.x - spell.x; var dy = enemy.y - spell.y; var distance = Math.sqrt(dx * dx + dy * dy); var damageRadius = spell.spellType === 'explosion' ? 120 : 80; if (distance < damageRadius) { // For poison cloud, only damage at intervals and apply poison effect if (spell.spellType === 'poisonCloud') { if (spell.age - spell.lastDamageTick >= spell.damageInterval) { spell.lastDamageTick = spell.age; var isDead = enemy.takeDamage(spell.damage); enemy.applyPoisonEffect(); // Apply poison effect if (isDead) { LK.setScore(LK.getScore() + enemy.points); scoreTxt.setText('Score: ' + LK.getScore()); enemy.destroy(); enemies.splice(e, 1); LK.getSound('enemyDeath').play(); } } } else { // Explosion deals instant damage var isDead = enemy.takeDamage(spell.damage); if (isDead) { LK.setScore(LK.getScore() + enemy.points); scoreTxt.setText('Score: ' + LK.getScore()); enemy.destroy(); enemies.splice(e, 1); LK.getSound('enemyDeath').play(); } } } } } else { // Single target spells for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; if (spell.intersects && spell.intersects(enemy)) { // Apply ice shard slow effect with blue tint if (spell.slowEffect) { enemy.applySlowEffect(); } var isDead = enemy.takeDamage(spell.damage); if (isDead) { LK.setScore(LK.getScore() + enemy.points); scoreTxt.setText('Score: ' + LK.getScore()); enemy.destroy(); enemies.splice(e, 1); LK.getSound('enemyDeath').play(); } // Fireball creates explosion on impact if (spell.spellType === 'fireball') { spell.createExplosion(); } if (spell.spellType !== 'poisonCloud') { spell.destroy(); spells.splice(s, 1); break; } } } } } // Enemies vs Wizard for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; if (enemy.intersects && enemy.intersects(wizard)) { if (!shieldActive) { // Only damage if enough time has passed since last hit if (LK.ticks - enemy.lastHitTime > 60) { // 1 second cooldown damageWizard(enemy.damage * 10); // Scale damage for visibility enemy.lastHitTime = LK.ticks; } } else { // Shield absorbs hit enemy.destroy(); enemies.splice(e, 1); if (shieldObject) { shieldObject.destroy(); shieldObject = null; shieldActive = false; } } } } // Storm vs Enemies if (stormActive && stormObject) { for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; var dx = enemy.x - stormObject.x; var dy = enemy.y - stormObject.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 560) { // Storm radius // Push enemy back strongly var pushDistance = 145; if (distance > 0) { enemy.x += dx / distance * pushDistance; enemy.y += dy / distance * pushDistance; } // Deal small damage every 10 frames (6 times per second) if (LK.ticks % 10 === 0) { var isDead = enemy.takeDamage(1); if (isDead) { LK.setScore(LK.getScore() + enemy.points); scoreTxt.setText('Score: ' + LK.getScore()); enemy.destroy(); enemies.splice(e, 1); LK.getSound('enemyDeath').play(); } } } } } } game.move = function (x, y, obj) { if (x > 100 && x < 1948 && y > 100 && y < 2200) { wizard.x = x; wizard.y = y; } }; game.update = function () { gameTime++; // Update incapacity timer if (incapacityTimer > 0) { incapacityTimer--; } // Update poison cloud cooldown if (poisonCloudCooldown > 0) { poisonCloudCooldown--; } // Regenerate mana if (wizardMana < wizardMaxMana) { wizardMana += manaRegenRate; if (wizardMana > wizardMaxMana) { wizardMana = wizardMaxMana; } // Update mana display var manaPercent = wizardMana / wizardMaxMana; manaBarFill.scaleX = manaPercent; manaTxt.setText('Mana: ' + Math.floor(wizardMana) + '/' + wizardMaxMana); } // Update time slow if (timeSlowActive) { timeSlowTimer--; if (timeSlowTimer <= 0) { timeSlowActive = false; } } // Update storm if (stormActive) { stormTimer--; if (stormObject) { stormObject.x = wizard.x; stormObject.y = wizard.y; } // Update storm particles to follow storm for (var p = 0; p < stormParticles.length; p++) { if (stormParticles[p]) { var particle = stormParticles[p]; particle.angle += particle.rotationSpeed; particle.x = wizard.x + Math.cos(particle.angle) * particle.baseRadius; particle.y = wizard.y + Math.sin(particle.angle) * particle.baseRadius; particle.rotation += 0.2; } } if (stormTimer <= 0) { stormActive = false; if (stormObject) { stormObject.destroy(); stormObject = null; } // Destroy all storm particles for (var p = 0; p < stormParticles.length; p++) { if (stormParticles[p]) { stormParticles[p].destroy(); } } stormParticles = []; // Stop wizard spinning and restore size tween.stop(wizard, { rotation: true, scaleX: true, scaleY: true }); // Animate wizard back to normal size tween(wizard, { scaleX: 1.0, scaleY: 1.0, rotation: 0 }, { duration: 500, easing: tween.easeOut }); } } // Update shield position if (shieldObject) { shieldObject.x = wizard.x; shieldObject.y = wizard.y; } // Spawn enemies spawnTimer++; var actualSpawnDelay = timeSlowActive ? spawnDelay * 2 : spawnDelay; if (spawnTimer >= actualSpawnDelay) { spawnEnemy(); spawnTimer = 0; } // Update spells for (var s = spells.length - 1; s >= 0; s--) { var spell = spells[s]; spell.age++; if (spell.age >= spell.lifeTime || spell.x < -100 || spell.x > 2148 || spell.y < -100 || spell.y > 2832) { spell.destroy(); spells.splice(s, 1); } } // Update enemies with time stop effect for (var e = 0; e < enemies.length; e++) { if (timeSlowActive && !enemies[e].timeSlowApplied) { if (!enemies[e].originalSpeed) { enemies[e].originalSpeed = enemies[e].speed; } enemies[e].speed = 0; // Complete stop enemies[e].timeSlowApplied = true; } else if (!timeSlowActive && enemies[e].timeSlowApplied) { enemies[e].speed = enemies[e].originalSpeed || enemies[e].speed; enemies[e].timeSlowApplied = false; // Restore normal tint tween(enemies[e], { tint: 0xFFFFFF }, { duration: 200 }); } } // Check for wave completion if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) { waveNumber++; enemiesSpawned = 0; enemiesPerWave = 5 + waveNumber * 2; spawnDelay = Math.max(30, 120 - waveNumber * 5); waveTxt.setText('Wave: ' + waveNumber); // Bonus points for completing wave LK.setScore(LK.getScore() + waveNumber * 50); scoreTxt.setText('Score: ' + LK.getScore()); } checkCollisions(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
self.enemyType = enemyType;
self.health = 1;
self.speed = 2;
self.damage = 1;
self.points = 10;
self.lastHitTime = 0;
self.slowedTimer = 0;
self.originalSpeed = 0;
self.poisonedTimer = 0;
self.poisonDamageTimer = 0;
var enemyGraphics;
if (enemyType === 'goblin') {
enemyGraphics = self.attachAsset('goblin', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.speed = 3;
self.points = 10;
} else if (enemyType === 'demon') {
enemyGraphics = self.attachAsset('demon', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.speed = 1.5;
self.points = 30;
} else if (enemyType === 'flyingEye') {
enemyGraphics = self.attachAsset('flyingEye', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.speed = 4;
self.points = 20;
} else if (enemyType === 'golem') {
enemyGraphics = self.attachAsset('golem', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 5;
self.speed = 1;
self.points = 50;
} else if (enemyType === 'skeleton') {
enemyGraphics = self.attachAsset('skeleton', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 2;
self.speed = 2;
self.points = 20;
} else if (enemyType === 'mushroom') {
enemyGraphics = self.attachAsset('mushroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.speed = 1;
self.points = 15;
} else if (enemyType === 'hellhound') {
enemyGraphics = self.attachAsset('hellhound', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 2;
self.speed = 5;
self.points = 25;
} else if (enemyType === 'darkSorcerer') {
enemyGraphics = self.attachAsset('darkSorcerer', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.speed = 1.5;
self.points = 40;
}
self.maxHealth = self.health;
// Spawn from random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// Top
self.x = Math.random() * 2048;
self.y = -50;
} else if (edge === 1) {
// Right
self.x = 2048 + 50;
self.y = Math.random() * 2732;
} else if (edge === 2) {
// Bottom
self.x = Math.random() * 2048;
self.y = 2732 + 50;
} else {
// Left
self.x = -50;
self.y = Math.random() * 2732;
}
self.applySlowEffect = function () {
if (self.slowedTimer <= 0) {
self.originalSpeed = self.speed;
}
self.slowedTimer = 180; // 3 seconds
self.speed = self.originalSpeed * 0.3; // 70% speed reduction
tween(enemyGraphics, {
tint: 0x87CEEB
}, {
duration: 200
});
};
self.stunTimer = 0;
self.applyStunEffect = function () {
self.stunTimer = 42; // 0.7 seconds at 60fps
if (!self.originalSpeed) {
self.originalSpeed = self.speed;
}
self.speed = 0; // Complete stop when stunned
tween(enemyGraphics, {
tint: 0xFFFF00
}, {
duration: 200
});
};
self.applyPoisonEffect = function () {
self.poisonedTimer = 120; // 2 seconds at 60fps
self.poisonDamageTimer = 0; // Reset damage timer
tween(enemyGraphics, {
tint: 0x00FF00 // Green tint for poison
}, {
duration: 200
});
};
self.takeDamage = function (damage) {
// Prevent taking damage if already dead
if (self.health <= 0) {
return true;
}
self.health -= damage;
self.lastHitTime = LK.ticks;
// Flash red when hit
tween(enemyGraphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
LK.getSound('enemyHit').play();
if (self.health <= 0) {
self.health = 0; // Ensure health doesn't go negative
return true; // Enemy is dead
}
return false;
};
self.update = function () {
// Handle stun effect
if (self.stunTimer > 0) {
self.stunTimer--;
if (self.stunTimer <= 0) {
self.speed = self.originalSpeed || self.speed;
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
return; // Don't move while stunned
}
// Handle slow effect
if (self.slowedTimer > 0) {
self.slowedTimer--;
if (self.slowedTimer <= 0) {
self.speed = self.originalSpeed;
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
// Handle poison effect
if (self.poisonedTimer > 0) {
self.poisonedTimer--;
self.poisonDamageTimer++;
// Deal damage every 18 frames (0.3 seconds at 60fps)
if (self.poisonDamageTimer >= 18) {
self.poisonDamageTimer = 0;
self.takeDamage(1); // Small poison damage
}
// Remove poison effect when timer expires
if (self.poisonedTimer <= 0) {
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
// Move towards wizard
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
// Flying eye has erratic movement
if (self.enemyType === 'flyingEye') {
moveX += (Math.random() - 0.5) * 2;
moveY += (Math.random() - 0.5) * 2;
}
self.x += moveX;
self.y += moveY;
}
};
return self;
});
var Spell = Container.expand(function (spellType, startX, startY, targetX, targetY) {
var self = Container.call(this);
self.spellType = spellType;
self.speed = 8;
self.damage = 1;
self.lifeTime = 180; // 3 seconds at 60fps
self.age = 0;
var spellGraphics;
if (spellType === 'fireball') {
spellGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 2;
} else if (spellType === 'iceShard') {
spellGraphics = self.attachAsset('iceShard', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 1;
self.slowEffect = true;
} else if (spellType === 'lightning') {
spellGraphics = self.attachAsset('lightning', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 3;
self.speed = 15;
} else if (spellType === 'poisonCloud') {
spellGraphics = self.attachAsset('poisonCloud', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 1;
self.speed = 0;
self.lifeTime = 210; // 3.5 seconds total duration (210 frames at 60fps)
self.lastDamageTick = 0;
self.damageInterval = 18; // Damage every 0.3 seconds (18 frames at 60fps)
spellGraphics.alpha = 0.6;
} else if (spellType === 'explosion') {
spellGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 4;
self.speed = 0;
self.lifeTime = 30;
spellGraphics.alpha = 0.8;
}
self.x = startX;
self.y = startY;
// Calculate direction
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
} else {
self.velocityX = 0;
self.velocityY = 0;
}
self.createExplosion = function () {
var explosion = new Spell('explosion', self.x, self.y, self.x, self.y);
spells.push(explosion);
game.addChild(explosion);
};
self.update = function () {
self.age++;
if (self.spellType !== 'poisonCloud' && self.spellType !== 'explosion') {
self.x += self.velocityX;
self.y += self.velocityY;
}
if (self.spellType === 'explosion') {
var scale = 1 + self.age / 30 * 2;
spellGraphics.scaleX = scale;
spellGraphics.scaleY = scale;
}
};
return self;
});
var SpellButton = Container.expand(function (spellType, buttonX, buttonY) {
var self = Container.call(this);
self.spellType = spellType;
self.cooldown = 0;
self.maxCooldown = 60; // 1 second
self.manaCost = 10; // Default mana cost
var buttonBg = self.attachAsset('spellButton', {
anchorX: 0.5,
anchorY: 0.5
});
var spellNames = {
'fireball': 'Fire',
'iceShard': 'Ice',
'lightning': 'Bolt',
'shield': 'Shield',
'teleport': 'Port',
'poisonCloud': 'Poison',
'mindControl': 'Mind',
'explosion': 'Boom',
'timeSlow': 'Stop',
'storm': 'Storm',
'manaRestorer': 'Restore',
'heal': 'Heal'
};
var buttonText = new Text2(spellNames[spellType] || spellType, {
size: 32,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.spellName = spellNames[spellType] || spellType;
self.x = buttonX;
self.y = buttonY;
// Set different cooldowns and mana costs for different spells
if (spellType === 'fireball') {
self.maxCooldown = 30;
self.manaCost = 15;
} else if (spellType === 'iceShard') {
self.maxCooldown = 25;
self.manaCost = 10;
} else if (spellType === 'lightning') {
self.maxCooldown = 45;
self.manaCost = 25;
} else if (spellType === 'shield') {
self.maxCooldown = 180;
self.manaCost = 40;
} else if (spellType === 'teleport') {
self.maxCooldown = 120;
self.manaCost = 30;
} else if (spellType === 'poisonCloud') {
self.maxCooldown = 390; // 6.5 seconds at 60fps
self.manaCost = 35;
} else if (spellType === 'mindControl') {
self.maxCooldown = 150;
self.manaCost = 50;
} else if (spellType === 'explosion') {
self.maxCooldown = 80;
self.manaCost = 45;
} else if (spellType === 'timeSlow') {
self.maxCooldown = 270; // 4.5 seconds
self.manaCost = 60;
} else if (spellType === 'storm') {
self.maxCooldown = 522; // 8.7 seconds
self.manaCost = 70;
} else if (spellType === 'manaRestorer') {
self.maxCooldown = 1000; // 10 seconds
self.manaCost = 0; // No mana cost
} else if (spellType === 'heal') {
self.maxCooldown = 1200; // 20 seconds at 60fps
self.manaCost = 130;
}
self.canCast = function () {
if (self.spellType === 'manaRestorer') {
return self.cooldown <= 0; // Mana restorer ignores mana cost and incapacity
}
return self.cooldown <= 0 && wizardMana >= self.manaCost && incapacityTimer <= 0;
};
self.cast = function () {
if (self.canCast()) {
self.cooldown = self.maxCooldown;
return true;
}
return false;
};
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
buttonBg.tint = 0x666666;
buttonText.fill = 0x999999;
// Show cooldown duration
var cooldownSeconds = Math.ceil(self.cooldown / 60);
buttonText.setText(cooldownSeconds + 's');
} else if (self.spellType !== 'manaRestorer' && incapacityTimer > 0) {
// Incapacitated - show red tint
buttonBg.tint = 0xFF4444;
buttonText.fill = 0xFF8888;
var incapacitySeconds = Math.ceil(incapacityTimer / 60);
buttonText.setText('Inc: ' + incapacitySeconds + 's');
} else if (wizardMana < self.manaCost) {
// Not enough mana - show blue tint
buttonBg.tint = 0x4444FF;
buttonText.fill = 0x8888FF;
buttonText.setText(self.spellName);
} else {
buttonBg.tint = 0xFFFFFF;
buttonText.fill = 0xFFFFFF;
buttonText.setText(self.spellName);
}
};
self.down = function (x, y, obj) {
if (self.canCast()) {
castSpell(self.spellType);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game variables
// Wizard
// Spells
// Enemies
// UI Elements
// Sounds
var wizard;
var enemies = [];
var spells = [];
var spellButtons = [];
var waveNumber = 1;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var spawnTimer = 0;
var spawnDelay = 120; // 2 seconds
var gameTime = 0;
var shieldActive = false;
var shieldObject = null;
var timeSlowActive = false;
var timeSlowTimer = 0;
var stormActive = false;
var stormTimer = 0;
var stormObject = null;
var wizardHealth = 100;
var wizardMaxHealth = 100;
var wizardMana = 180;
var wizardMaxMana = 180;
var manaRegenRate = 0.1; // Mana regenerated per frame
var incapacityTimer = 0; // Timer for power incapacity after mana restorer
var poisonCloudCooldown = 0; // 6.5 second cooldown for poison cloud
var stormParticles = []; // Track storm particles
var healthBar = null;
var healthBarFill = null;
var manaBar = null;
var manaBarFill = null;
// Enemy types for spawning
var enemyTypes = ['goblin', 'demon', 'flyingEye', 'golem', 'skeleton', 'mushroom', 'hellhound', 'darkSorcerer'];
// Create wizard
wizard = game.addChild(new Container());
var wizardGraphics = wizard.attachAsset('wizard', {
anchorX: 0.5,
anchorY: 0.5
});
wizard.x = 1024;
wizard.y = 1366;
// Create spell buttons
var spellTypes = ['fireball', 'iceShard', 'lightning', 'shield', 'teleport', 'poisonCloud', 'mindControl', 'explosion', 'timeSlow', 'storm', 'manaRestorer', 'heal'];
for (var i = 0; i < spellTypes.length; i++) {
var buttonX = 150 + i % 6 * 180; // Increased horizontal spacing for larger buttons
var buttonY = 2450 + Math.floor(i / 6) * 130; // Increased vertical spacing and moved up slightly
var button = new SpellButton(spellTypes[i], buttonX, buttonY);
spellButtons.push(button);
game.addChild(button);
}
// Create score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 150;
scoreTxt.y = 100;
game.addChild(scoreTxt);
// Create wave display
var waveTxt = new Text2('Wave: 1', {
size: 50,
fill: 0xFFFF00
});
waveTxt.anchor.set(0, 0);
waveTxt.x = 150;
waveTxt.y = 180;
game.addChild(waveTxt);
// Create health bar
healthBar = game.addChild(new Container());
var healthBarBg = healthBar.attachAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
});
healthBarFill = healthBar.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0
});
healthBar.x = 150;
healthBar.y = 250;
// Health bar text
var healthTxt = new Text2('Health: 100/100', {
size: 40,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 150;
healthTxt.y = 280;
game.addChild(healthTxt);
// Create mana bar
manaBar = game.addChild(new Container());
var manaBarBg = manaBar.attachAsset('manaBarBg', {
anchorX: 0,
anchorY: 0
});
manaBarFill = manaBar.attachAsset('manaBarFill', {
anchorX: 0,
anchorY: 0
});
manaBar.x = 150;
manaBar.y = 330;
// Mana bar text
var manaTxt = new Text2('Mana: 180/180', {
size: 40,
fill: 0x0080FF
});
manaTxt.anchor.set(0, 0);
manaTxt.x = 150;
manaTxt.y = 360;
game.addChild(manaTxt);
function castSpell(spellType) {
// Find the button and check cooldown
var button = null;
for (var i = 0; i < spellButtons.length; i++) {
if (spellButtons[i].spellType === spellType) {
button = spellButtons[i];
break;
}
}
if (!button || !button.cast()) {
return;
}
// Consume mana
wizardMana -= button.manaCost;
if (wizardMana < 0) {
wizardMana = 0;
}
// Update mana display
var manaPercent = wizardMana / wizardMaxMana;
manaBarFill.scaleX = manaPercent;
manaTxt.setText('Mana: ' + Math.floor(wizardMana) + '/' + wizardMaxMana);
LK.getSound('spellCast').play();
if (spellType === 'shield') {
activateShield();
} else if (spellType === 'teleport') {
teleportWizard();
} else if (spellType === 'timeSlow') {
activateTimeSlow();
} else if (spellType === 'mindControl') {
mindControlEnemies();
} else if (spellType === 'explosion') {
// Explosion at wizard location
var spell = new Spell(spellType, wizard.x, wizard.y, wizard.x, wizard.y);
spells.push(spell);
game.addChild(spell);
} else if (spellType === 'storm') {
activateStorm();
} else if (spellType === 'manaRestorer') {
activateManaRestorer();
} else if (spellType === 'heal') {
activateHeal();
} else {
// Targeted spells - cast towards nearest enemy
var nearestEnemy = findNearestEnemy();
if (nearestEnemy) {
var spell = new Spell(spellType, wizard.x, wizard.y, nearestEnemy.x, nearestEnemy.y);
spell.targetX = nearestEnemy.x;
spell.targetY = nearestEnemy.y;
spells.push(spell);
game.addChild(spell);
}
}
}
function findNearestEnemy() {
var nearest = null;
var minDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var dx = enemies[i].x - wizard.x;
var dy = enemies[i].y - wizard.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
nearest = enemies[i];
}
}
return nearest;
}
function activateShield() {
if (shieldObject) {
shieldObject.destroy();
}
shieldObject = game.addChild(new Container());
var shieldGraphics = shieldObject.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5
});
shieldGraphics.alpha = 0.5;
shieldActive = true;
LK.setTimeout(function () {
if (shieldObject) {
shieldObject.destroy();
shieldObject = null;
}
shieldActive = false;
}, 3000);
}
function teleportWizard() {
// Teleport to a safe random location
var attempts = 0;
var newX, newY;
do {
newX = 200 + Math.random() * (2048 - 400);
newY = 200 + Math.random() * (2200 - 400);
attempts++;
} while (attempts < 10 && isLocationUnsafe(newX, newY));
wizard.x = newX;
wizard.y = newY;
// Teleport effect
LK.effects.flashObject(wizard, 0x00FFFF, 500);
}
function isLocationUnsafe(x, y) {
for (var i = 0; i < enemies.length; i++) {
var dx = enemies[i].x - x;
var dy = enemies[i].y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
return true;
}
}
return false;
}
function activateTimeSlow() {
timeSlowActive = true;
timeSlowTimer = 90; // 1.5 seconds
LK.effects.flashScreen(0x0066FF, 200);
// Visual effect on all enemies - freeze them completely
for (var i = 0; i < enemies.length; i++) {
if (!enemies[i].originalSpeed) {
enemies[i].originalSpeed = enemies[i].speed;
}
enemies[i].speed = 0; // Complete stop
tween(enemies[i], {
tint: 0x6666FF
}, {
duration: 300
});
}
}
function activateStorm() {
stormActive = true;
stormTimer = 210; // 3.5 seconds at 60fps
// Create storm object that follows wizard
if (stormObject) {
stormObject.destroy();
}
stormObject = game.addChild(new Container());
var stormGraphics = stormObject.attachAsset('stormEffect', {
anchorX: 0.5,
anchorY: 0.5
});
stormGraphics.alpha = 0.8;
// Clear existing storm particles
for (var p = 0; p < stormParticles.length; p++) {
if (stormParticles[p]) {
stormParticles[p].destroy();
}
}
stormParticles = [];
// Create storm particles around wizard that follow the storm
for (var i = 0; i < 8; i++) {
var particle = game.addChild(new Container());
var particleGraphics = particle.attachAsset('iceShard', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.scaleX = 0.3;
particleGraphics.scaleY = 0.3;
particleGraphics.tint = 0x87CEEB;
particle.x = wizard.x;
particle.y = wizard.y;
particle.angle = i / 8 * Math.PI * 2;
particle.baseRadius = 100 + i * 20;
particle.rotationSpeed = 0.1;
stormParticles.push(particle);
}
// Start wizard spinning and shrinking animation
tween(wizard, {
rotation: Math.PI * 14,
// 7 full rotations over 3.5 seconds
scaleX: 0.6,
// Shrink to 60% size
scaleY: 0.6
}, {
duration: 3500,
easing: tween.linear
});
// Visual storm effect
LK.effects.flashScreen(0x4169E1, 300);
}
function activateManaRestorer() {
// Instantly restore full mana
wizardMana = wizardMaxMana;
// Update mana display
var manaPercent = wizardMana / wizardMaxMana;
manaBarFill.scaleX = manaPercent;
manaTxt.setText('Mana: ' + Math.floor(wizardMana) + '/' + wizardMaxMana);
// Add 6 seconds of incapacity to all powers
incapacityTimer = 432; // 6 seconds at 60fps
// Visual effect
LK.effects.flashObject(wizard, 0x00FFFF, 1000);
LK.effects.flashScreen(0x00FFFF, 300);
}
function activateHeal() {
// Restore to exactly 100 health (full health)
wizardHealth = 100;
wizardMaxHealth = 100; // Ensure max health is also 100
// Update health bar
var healthPercent = wizardHealth / wizardMaxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
// Update health text
healthTxt.setText('Health: ' + wizardHealth + '/' + wizardMaxHealth);
// Visual healing effect
LK.effects.flashObject(wizard, 0x00FF00, 1000);
tween(wizard, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(wizard, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
function mindControlEnemies() {
var controlled = 0;
for (var i = 0; i < enemies.length && controlled < 3; i++) {
var dx = enemies[i].x - wizard.x;
var dy = enemies[i].y - wizard.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 300) {
// Mind control effect - purple flash then instant kill
tween(enemies[i], {
tint: 0x9932CC
}, {
duration: 200,
onFinish: function onFinish() {
tween(this, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
enemies[i].takeDamage(enemies[i].health);
}
});
}
});
controlled++;
}
}
}
function damageWizard(damage) {
wizardHealth -= damage;
if (wizardHealth < 0) {
wizardHealth = 0;
}
// Update health bar
var healthPercent = wizardHealth / wizardMaxHealth;
healthBarFill.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xffff00; // Yellow
} else {
healthBarFill.tint = 0xff0000; // Red
}
// Update health text
healthTxt.setText('Health: ' + wizardHealth + '/' + wizardMaxHealth);
if (wizardHealth <= 0) {
LK.effects.flashScreen(0xFF0000, 500);
LK.showGameOver();
}
}
function spawnEnemy() {
if (enemiesSpawned < enemiesPerWave) {
var enemyType = enemyTypes[Math.floor(Math.random() * Math.min(enemyTypes.length, 2 + Math.floor(waveNumber / 2)))];
var enemy = new Enemy(enemyType);
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
}
function checkCollisions() {
// Spells vs Enemies
for (var s = spells.length - 1; s >= 0; s--) {
var spell = spells[s];
// Area damage spells (explosion, poison cloud)
if (spell.spellType === 'explosion' || spell.spellType === 'poisonCloud') {
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
var dx = enemy.x - spell.x;
var dy = enemy.y - spell.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var damageRadius = spell.spellType === 'explosion' ? 120 : 80;
if (distance < damageRadius) {
// For poison cloud, only damage at intervals and apply poison effect
if (spell.spellType === 'poisonCloud') {
if (spell.age - spell.lastDamageTick >= spell.damageInterval) {
spell.lastDamageTick = spell.age;
var isDead = enemy.takeDamage(spell.damage);
enemy.applyPoisonEffect(); // Apply poison effect
if (isDead) {
LK.setScore(LK.getScore() + enemy.points);
scoreTxt.setText('Score: ' + LK.getScore());
enemy.destroy();
enemies.splice(e, 1);
LK.getSound('enemyDeath').play();
}
}
} else {
// Explosion deals instant damage
var isDead = enemy.takeDamage(spell.damage);
if (isDead) {
LK.setScore(LK.getScore() + enemy.points);
scoreTxt.setText('Score: ' + LK.getScore());
enemy.destroy();
enemies.splice(e, 1);
LK.getSound('enemyDeath').play();
}
}
}
}
} else {
// Single target spells
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
if (spell.intersects && spell.intersects(enemy)) {
// Apply ice shard slow effect with blue tint
if (spell.slowEffect) {
enemy.applySlowEffect();
}
var isDead = enemy.takeDamage(spell.damage);
if (isDead) {
LK.setScore(LK.getScore() + enemy.points);
scoreTxt.setText('Score: ' + LK.getScore());
enemy.destroy();
enemies.splice(e, 1);
LK.getSound('enemyDeath').play();
}
// Fireball creates explosion on impact
if (spell.spellType === 'fireball') {
spell.createExplosion();
}
if (spell.spellType !== 'poisonCloud') {
spell.destroy();
spells.splice(s, 1);
break;
}
}
}
}
}
// Enemies vs Wizard
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
if (enemy.intersects && enemy.intersects(wizard)) {
if (!shieldActive) {
// Only damage if enough time has passed since last hit
if (LK.ticks - enemy.lastHitTime > 60) {
// 1 second cooldown
damageWizard(enemy.damage * 10); // Scale damage for visibility
enemy.lastHitTime = LK.ticks;
}
} else {
// Shield absorbs hit
enemy.destroy();
enemies.splice(e, 1);
if (shieldObject) {
shieldObject.destroy();
shieldObject = null;
shieldActive = false;
}
}
}
}
// Storm vs Enemies
if (stormActive && stormObject) {
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
var dx = enemy.x - stormObject.x;
var dy = enemy.y - stormObject.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 560) {
// Storm radius
// Push enemy back strongly
var pushDistance = 145;
if (distance > 0) {
enemy.x += dx / distance * pushDistance;
enemy.y += dy / distance * pushDistance;
}
// Deal small damage every 10 frames (6 times per second)
if (LK.ticks % 10 === 0) {
var isDead = enemy.takeDamage(1);
if (isDead) {
LK.setScore(LK.getScore() + enemy.points);
scoreTxt.setText('Score: ' + LK.getScore());
enemy.destroy();
enemies.splice(e, 1);
LK.getSound('enemyDeath').play();
}
}
}
}
}
}
game.move = function (x, y, obj) {
if (x > 100 && x < 1948 && y > 100 && y < 2200) {
wizard.x = x;
wizard.y = y;
}
};
game.update = function () {
gameTime++;
// Update incapacity timer
if (incapacityTimer > 0) {
incapacityTimer--;
}
// Update poison cloud cooldown
if (poisonCloudCooldown > 0) {
poisonCloudCooldown--;
}
// Regenerate mana
if (wizardMana < wizardMaxMana) {
wizardMana += manaRegenRate;
if (wizardMana > wizardMaxMana) {
wizardMana = wizardMaxMana;
}
// Update mana display
var manaPercent = wizardMana / wizardMaxMana;
manaBarFill.scaleX = manaPercent;
manaTxt.setText('Mana: ' + Math.floor(wizardMana) + '/' + wizardMaxMana);
}
// Update time slow
if (timeSlowActive) {
timeSlowTimer--;
if (timeSlowTimer <= 0) {
timeSlowActive = false;
}
}
// Update storm
if (stormActive) {
stormTimer--;
if (stormObject) {
stormObject.x = wizard.x;
stormObject.y = wizard.y;
}
// Update storm particles to follow storm
for (var p = 0; p < stormParticles.length; p++) {
if (stormParticles[p]) {
var particle = stormParticles[p];
particle.angle += particle.rotationSpeed;
particle.x = wizard.x + Math.cos(particle.angle) * particle.baseRadius;
particle.y = wizard.y + Math.sin(particle.angle) * particle.baseRadius;
particle.rotation += 0.2;
}
}
if (stormTimer <= 0) {
stormActive = false;
if (stormObject) {
stormObject.destroy();
stormObject = null;
}
// Destroy all storm particles
for (var p = 0; p < stormParticles.length; p++) {
if (stormParticles[p]) {
stormParticles[p].destroy();
}
}
stormParticles = [];
// Stop wizard spinning and restore size
tween.stop(wizard, {
rotation: true,
scaleX: true,
scaleY: true
});
// Animate wizard back to normal size
tween(wizard, {
scaleX: 1.0,
scaleY: 1.0,
rotation: 0
}, {
duration: 500,
easing: tween.easeOut
});
}
}
// Update shield position
if (shieldObject) {
shieldObject.x = wizard.x;
shieldObject.y = wizard.y;
}
// Spawn enemies
spawnTimer++;
var actualSpawnDelay = timeSlowActive ? spawnDelay * 2 : spawnDelay;
if (spawnTimer >= actualSpawnDelay) {
spawnEnemy();
spawnTimer = 0;
}
// Update spells
for (var s = spells.length - 1; s >= 0; s--) {
var spell = spells[s];
spell.age++;
if (spell.age >= spell.lifeTime || spell.x < -100 || spell.x > 2148 || spell.y < -100 || spell.y > 2832) {
spell.destroy();
spells.splice(s, 1);
}
}
// Update enemies with time stop effect
for (var e = 0; e < enemies.length; e++) {
if (timeSlowActive && !enemies[e].timeSlowApplied) {
if (!enemies[e].originalSpeed) {
enemies[e].originalSpeed = enemies[e].speed;
}
enemies[e].speed = 0; // Complete stop
enemies[e].timeSlowApplied = true;
} else if (!timeSlowActive && enemies[e].timeSlowApplied) {
enemies[e].speed = enemies[e].originalSpeed || enemies[e].speed;
enemies[e].timeSlowApplied = false;
// Restore normal tint
tween(enemies[e], {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
// Check for wave completion
if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) {
waveNumber++;
enemiesSpawned = 0;
enemiesPerWave = 5 + waveNumber * 2;
spawnDelay = Math.max(30, 120 - waveNumber * 5);
waveTxt.setText('Wave: ' + waveNumber);
// Bonus points for completing wave
LK.setScore(LK.getScore() + waveNumber * 50);
scoreTxt.setText('Score: ' + LK.getScore());
}
checkCollisions();
};
A very mysterious and cool man with a purple hat a wizard character's head is purple and dark blue themed. In-Game asset. 2d. High contrast. No shadows
a mysterious and angry goblin. In-Game asset. 2d. High contrast. No shadows
2d mysterious and scary demon character's head. In-Game asset. 2d. High contrast. No shadows
2d mysterious and scary flying eye character's head. In-Game asset. 2d. High contrast. No shadows
2d mysterious and scary golem character's head. In-Game asset. 2d. High contrast. No shadows
2d mysterious and scary wizard character's head. In-Game asset. 2d. High contrast. No shadows
2d mysterious and scary skeleton character's head. In-Game asset. 2d. High contrast. No shadows
2d mysterious and scary hellhound character's head. In-Game asset. 2d. High contrast. No shadows
2d mysterious and scary mushroom character's head. In-Game asset. 2d. High contrast. No shadows
an old-style square long button in grey in a magical way. In-Game asset. 2d. High contrast. No shadows