/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Player = Container.expand(function () { var self = Container.call(this); self.moveSpeed = 8; self.isMoving = false; self.targetX = 0; self.targetY = 0; var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.moveTo = function (x, y) { if (x < battleBoxLeft + 30) x = battleBoxLeft + 30; if (x > battleBoxRight - 30) x = battleBoxRight - 30; if (y < battleBoxTop + 30) y = battleBoxTop + 30; if (y > battleBoxBottom - 30) y = battleBoxBottom - 30; self.targetX = x; self.targetY = y; self.isMoving = true; }; self.update = function () { if (self.isMoving) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.moveSpeed) { self.x = self.targetX; self.y = self.targetY; self.isMoving = false; } else { self.x += dx / distance * self.moveSpeed; self.y += dy / distance * self.moveSpeed; } } }; return self; }); var Projectile = Container.expand(function () { var self = Container.call(this); self.velocityX = 0; self.velocityY = 0; self.isActive = true; var projectileGraphics = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.setVelocity = function (vx, vy) { self.velocityX = vx; self.velocityY = vy; }; self.update = function () { if (self.isActive) { self.x += self.velocityX; self.y += self.velocityY; // Remove if out of battle box bounds if (self.x < battleBoxLeft - 50 || self.x > battleBoxRight + 50 || self.y < battleBoxTop - 50 || self.y > battleBoxBottom + 50) { self.isActive = false; } } }; return self; }); var PurpleParticle = Container.expand(function () { var self = Container.call(this); self.isActive = true; self.velocityX = 0; self.velocityY = 0; self.lifeTime = 0; self.maxLifeTime = 300; // 5 seconds at 60fps var particleGraphics = self.attachAsset('purpleParticle', { anchorX: 0.5, anchorY: 0.5 }); self.setVelocity = function (vx, vy) { self.velocityX = vx; self.velocityY = vy; }; self.update = function () { if (self.isActive) { self.x += self.velocityX; self.y += self.velocityY; self.lifeTime++; // Fade out over time var fadeRatio = 1 - self.lifeTime / self.maxLifeTime; particleGraphics.alpha = fadeRatio; // Remove if lifetime exceeded or out of bounds if (self.lifeTime >= self.maxLifeTime || self.y < 0) { self.isActive = false; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 }); /**** * Game Code ****/ // Game state variables var gameState = 'attack'; // 'attack' or 'defense' var playerHealth = 200; var maxHealth = 200; var enemyHealth = 5000; var maxEnemyHealth = 5000; var projectiles = []; var defenseTimer = 0; var defenseDuration = 300; // 5 seconds at 60fps var spareCount = 0; var maxSpareCount = 15; var healCount = 0; var maxHealCount = 8; var isInvulnerable = false; var invulnerabilityDuration = 90; // 1.5 seconds at 60fps var playerTP = 0; var maxTP = 100; var narrowDodgeDistance = 80; // Distance threshold for narrow dodge var usedPatterns = []; // Track used patterns var maxPatterns = 3; // Maximum number of patterns to mix var forceMovementPatterns = [0, 1, 2]; // Patterns that force movement // Phase system variables var currentPhase = 1; var maxPhases = 3; var phase1Threshold = 0.66; // 66% health remaining var phase2Threshold = 0.33; // 33% health remaining // Purple particle system variables var purpleParticles = []; var particleSpawnTimer = 0; var maxParticles = 20; // Purple gradient variables var purpleGradientLayers = []; var gradientLayerCount = 8; // Battle box boundaries var battleBoxLeft = 1024 - 400; var battleBoxRight = 1024 + 400; var battleBoxTop = 1366 - 300; var battleBoxBottom = 1366 + 300; // Create battle box var battleBox = game.addChild(LK.getAsset('battleBox', { anchorX: 0.5, anchorY: 0.5 })); battleBox.x = 1024; battleBox.y = 1366; // Create player var player = game.addChild(new Player()); player.x = 1024; player.y = 1366; // Create TP hitbox visual var tpHitbox = game.addChild(LK.getAsset('tpHitbox', { anchorX: 0.5, anchorY: 0.5 })); tpHitbox.x = player.x; tpHitbox.y = player.y; tpHitbox.alpha = 0.3; // Create health bar background var healthBarBg = game.addChild(LK.getAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 })); healthBarBg.x = 1024; healthBarBg.y = 1700; // Create health bar var healthBar = game.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5 })); healthBar.x = 1024; healthBar.y = 1700; // Create enemy sprite var enemy = game.addChild(LK.getAsset('enemy', { anchorX: 0.5, anchorY: 0.5 })); enemy.x = 1024; enemy.y = 800; // Create attack button var attackButton = game.addChild(LK.getAsset('attackButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 })); attackButton.x = 650; attackButton.y = 1800; // Create spare button var spareButton = game.addChild(LK.getAsset('spareButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 })); spareButton.x = 1398; spareButton.y = 1800; // Create heal button var healButton = game.addChild(LK.getAsset('healButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 })); healButton.x = 1149; healButton.y = 1800; // Create special attack button var specialAttackButton = game.addChild(LK.getAsset('specialAttackButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 })); specialAttackButton.x = 899; specialAttackButton.y = 1800; // Create movement buttons var upButton = game.addChild(LK.getAsset('upButton', { anchorX: 0.5, anchorY: 0.5 })); upButton.x = 1024; upButton.y = 1800; var downButton = game.addChild(LK.getAsset('downButton', { anchorX: 0.5, anchorY: 0.5 })); downButton.x = 1024; downButton.y = 2200; var leftButton = game.addChild(LK.getAsset('leftButton', { anchorX: 0.5, anchorY: 0.5 })); leftButton.x = 750; leftButton.y = 2000; var rightButton = game.addChild(LK.getAsset('rightButton', { anchorX: 0.5, anchorY: 0.5 })); rightButton.x = 1298; rightButton.y = 2000; // Create UI text var phaseText = new Text2('ATTACK PHASE', { size: 80, fill: 0xFFFF00 }); phaseText.anchor.set(0.5, 0); phaseText.x = 1024; phaseText.y = 200; game.addChild(phaseText); var healthText = new Text2('HP: 200/200', { size: 50, fill: 0xffffff }); healthText.anchor.set(0.5, 0.5); healthText.x = 1024; healthText.y = 1700; game.addChild(healthText); // Create enemy health bar background var enemyHealthBarBg = game.addChild(LK.getAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5 })); enemyHealthBarBg.x = 400; enemyHealthBarBg.y = 1950; // Create enemy health bar var enemyHealthBar = game.addChild(LK.getAsset('Enemybar', { anchorX: 0.5, anchorY: 0.5 })); enemyHealthBar.x = 400; enemyHealthBar.y = 1950; enemyHealthBar.tint = 0xff0000; var enemyHealthText = new Text2('Enemy HP: 5000/5000', { size: 50, fill: 0x0066ff }); enemyHealthText.anchor.set(0.5, 0.5); enemyHealthText.x = 400; enemyHealthText.y = 1950; game.addChild(enemyHealthText); // Create spare bar background var spareBarBg = game.addChild(LK.getAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 })); spareBarBg.x = 1648; spareBarBg.y = 1950; // Create spare bar var spareBar = game.addChild(LK.getAsset('spareProgressBar', { anchorX: 0.5, anchorY: 0.5 })); spareBar.x = 1648; spareBar.y = 1950; var spareText = new Text2('Spare Progress: 0/15', { size: 50, fill: 0x00ff66 }); spareText.anchor.set(0.5, 0.5); spareText.x = 1648; spareText.y = 1950; game.addChild(spareText); var healText = new Text2('Heals: 8/8', { size: 50, fill: 0x00ff00 }); healText.anchor.set(0.5, 0); healText.x = 1024; healText.y = 2250; game.addChild(healText); // Create TP bar background var tpBarBg = game.addChild(LK.getAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 })); tpBarBg.x = 1024; tpBarBg.y = 400; // Create TP bar var tpBar = game.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5 })); tpBar.x = 1024; tpBar.y = 400; tpBar.tint = 0xffff00; var tpText = new Text2('TP: 0/100', { size: 50, fill: 0xffff00 }); tpText.anchor.set(0.5, 0); tpText.x = 1024; tpText.y = 450; game.addChild(tpText); // Button event handlers attackButton.down = function () { if (gameState === 'attack') { var damage = Math.floor(Math.random() * 101) + 100; // Random damage between 100-200 enemyHealth -= damage; // Add 6 TP when attacking playerTP = Math.min(playerTP + 6, maxTP); playerTP = Math.round(playerTP * 10) / 10; updateTPBar(); updateEnemyHealthBar(); LK.getSound('attack').play(); if (enemyHealth <= 0) { LK.showYouWin(); return; } startDefensePhase(); } }; spareButton.down = function () { if (gameState === 'attack') { spareCount++; updateSpareProgress(); LK.getSound('spare').play(); if (spareCount >= maxSpareCount) { LK.showYouWin(); return; } startDefensePhase(); } }; healButton.down = function () { if (gameState === 'attack' && healCount < maxHealCount && playerHealth < maxHealth) { healCount++; playerHealth = Math.min(playerHealth + 100, maxHealth); updateHealthBar(); updateHealCount(); LK.getSound('heal').play(); LK.effects.flashObject(player, 0x00ff00, 300); startDefensePhase(); } }; upButton.down = function () { if (gameState === 'defense') { player.moveTo(player.x, player.y - 60); LK.getSound('dodge').play(); } }; downButton.down = function () { if (gameState === 'defense') { player.moveTo(player.x, player.y + 60); LK.getSound('dodge').play(); } }; leftButton.down = function () { if (gameState === 'defense') { player.moveTo(player.x - 60, player.y); LK.getSound('dodge').play(); } }; rightButton.down = function () { if (gameState === 'defense') { player.moveTo(player.x + 60, player.y); LK.getSound('dodge').play(); } }; specialAttackButton.down = function () { if (gameState === 'attack' && playerTP >= 50) { var damage = Math.floor(Math.random() * 201) + 500; // Random damage between 500-700 enemyHealth -= damage; playerTP -= 50; playerTP = Math.round(playerTP * 10) / 10; updateEnemyHealthBar(); updateTPBar(); LK.getSound('skill').play(); LK.effects.flashObject(enemy, 0xff6600, 500); if (enemyHealth <= 0) { LK.showYouWin(); return; } startDefensePhase(); } }; function startDefensePhase() { gameState = 'defense'; defenseTimer = 0; currentPhase = getCurrentPhase(); // Adjust defense phase based on current phase if (currentPhase === 1) { defenseDuration = 180; // 3 seconds - easy maxPatterns = 2; // Use only 2 patterns phaseText.setText('DEFENSE PHASE - PHASE 1'); } else if (currentPhase === 2) { defenseDuration = 210; // 3.5 seconds - medium (reduced from 240) maxPatterns = 2; // Use only 2 patterns (reduced from 3) phaseText.setText('DEFENSE PHASE - PHASE 2'); } else { defenseDuration = 300; // 5 seconds - hard maxPatterns = 3; // Use all 3 patterns phaseText.setText('DEFENSE PHASE - PHASE 3'); } phaseText.tint = 0xff0000; // Reset used patterns for new defense phase usedPatterns = []; // Hide attack buttons, show movement buttons attackButton.visible = false; spareButton.visible = false; healButton.visible = false; specialAttackButton.visible = false; upButton.visible = true; downButton.visible = true; leftButton.visible = true; rightButton.visible = true; } function startAttackPhase() { gameState = 'attack'; phaseText.setText('ATTACK PHASE'); phaseText.tint = 0xffff00; // Clear all projectiles for (var i = projectiles.length - 1; i >= 0; i--) { projectiles[i].destroy(); projectiles.splice(i, 1); } // Hide movement buttons immediately upButton.visible = false; downButton.visible = false; leftButton.visible = false; rightButton.visible = false; // Hide attack buttons initially attackButton.visible = false; spareButton.visible = false; healButton.visible = false; specialAttackButton.visible = false; // Show attack buttons after half a second delay LK.setTimeout(function () { attackButton.visible = true; spareButton.visible = true; healButton.visible = true; specialAttackButton.visible = true; }, 500); } function spawnProjectile(x, y, vx, vy) { // Limit the number of projectiles to 50 if (projectiles.length >= 50) { return; // Don't spawn new projectile if limit reached } var projectile = new Projectile(); projectile.x = x; projectile.y = y; projectile.setVelocity(vx, vy); projectiles.push(projectile); game.addChild(projectile); LK.getSound('projectile').play(); } function updateProjectilePattern() { // Only spawn projectiles during defense phase if (gameState !== 'defense') { return; } // Adjust spawn frequency based on phase var spawnFrequency; if (currentPhase === 1) { spawnFrequency = 40; // Spawn every 0.67 seconds - easy } else if (currentPhase === 2) { spawnFrequency = 30; // Spawn every 0.5 seconds - medium } else { spawnFrequency = 18; // Spawn every 0.3 seconds - hard } if (defenseTimer % spawnFrequency === 0) { var patternType; // Reset used patterns if we've used all allowed patterns if (usedPatterns.length >= maxPatterns) { usedPatterns = []; } // Select a pattern that forces movement and hasn't been used yet var availablePatterns = []; for (var p = 0; p < forceMovementPatterns.length; p++) { var pattern = forceMovementPatterns[p]; var isUsed = false; for (var u = 0; u < usedPatterns.length; u++) { if (usedPatterns[u] === pattern) { isUsed = true; break; } } if (!isUsed) { availablePatterns.push(pattern); } } // If no available patterns, reset and use any force movement pattern if (availablePatterns.length === 0) { usedPatterns = []; availablePatterns = forceMovementPatterns.slice(); // Copy array } // Select random pattern from available ones patternType = availablePatterns[Math.floor(Math.random() * availablePatterns.length)]; usedPatterns.push(patternType); if (patternType === 0) { // Horizontal sweep - aligned with player's vertical position var projectileCount = currentPhase === 1 ? 5 : currentPhase === 2 ? 7 : 9; var speed = currentPhase === 1 ? 4 : currentPhase === 2 ? 6 : 8; var playerY = player.y; var spreadRange = 200; // How much to spread around player position for (var i = 0; i < projectileCount; i++) { var yOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount); var targetY = Math.max(battleBoxTop + 30, Math.min(battleBoxBottom - 30, playerY + yOffset)); spawnProjectile(battleBoxLeft - 30, targetY, speed, 0); } // Add delayed second wave only in phase 2 and 3 if (currentPhase >= 2) { LK.setTimeout(function () { var currentPlayerY = player.y; for (var i = 0; i < projectileCount; i++) { var yOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount) + 40; var targetY = Math.max(battleBoxTop + 30, Math.min(battleBoxBottom - 30, currentPlayerY + yOffset)); spawnProjectile(battleBoxLeft - 30, targetY, speed, 0); } }, currentPhase === 2 ? 500 : 300); } // Add third wave for phase 3 if (currentPhase === 3) { LK.setTimeout(function () { var currentPlayerY = player.y; for (var i = 0; i < projectileCount; i++) { var yOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount) - 40; var targetY = Math.max(battleBoxTop + 30, Math.min(battleBoxBottom - 30, currentPlayerY + yOffset)); spawnProjectile(battleBoxLeft - 30, targetY, speed, 0); } }, 600); } } else if (patternType === 1) { // Vertical sweep - aligned with player's horizontal position var projectileCount = currentPhase === 1 ? 6 : currentPhase === 2 ? 8 : 10; var speed = currentPhase === 1 ? 4 : currentPhase === 2 ? 5 : 7; var playerX = player.x; var spreadRange = 300; // How much to spread around player position for (var i = 0; i < projectileCount; i++) { var xOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount); var targetX = Math.max(battleBoxLeft + 30, Math.min(battleBoxRight - 30, playerX + xOffset)); spawnProjectile(targetX, battleBoxTop - 30, 0, speed); } // Add delayed second wave only in phase 2 and 3 if (currentPhase >= 2) { LK.setTimeout(function () { var currentPlayerX = player.x; for (var i = 0; i < projectileCount; i++) { var xOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount) + 40; var targetX = Math.max(battleBoxLeft + 30, Math.min(battleBoxRight - 30, currentPlayerX + xOffset)); spawnProjectile(targetX, battleBoxTop - 30, 0, speed); } }, currentPhase === 2 ? 500 : 300); } // Add third wave for phase 3 if (currentPhase === 3) { LK.setTimeout(function () { var currentPlayerX = player.x; for (var i = 0; i < projectileCount; i++) { var xOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount) - 40; var targetX = Math.max(battleBoxLeft + 30, Math.min(battleBoxRight - 30, currentPlayerX + xOffset)); spawnProjectile(targetX, battleBoxTop - 30, 0, speed); } }, 600); } } else if (patternType === 2) { // Closing walls pattern - converges on player position var playerX = player.x; var playerY = player.y; var wallCount = currentPhase === 1 ? 8 : currentPhase === 2 ? 10 : 12; var speed = currentPhase === 1 ? 2 : currentPhase === 2 ? 3 : 4; // Create walls from all sides that converge on player for (var i = 0; i < wallCount; i++) { // Calculate direction vectors towards player var leftY = battleBoxTop + i * (600 / wallCount); var rightY = battleBoxTop + i * (600 / wallCount); var topX = battleBoxLeft + i * (800 / wallCount); var bottomX = battleBoxLeft + i * (800 / wallCount); // Left wall - aim towards player var leftDx = playerX - battleBoxLeft; var leftDy = playerY - leftY; var leftDist = Math.sqrt(leftDx * leftDx + leftDy * leftDy); spawnProjectile(battleBoxLeft - 30, leftY, speed * leftDx / leftDist, speed * leftDy / leftDist); // Right wall - aim towards player var rightDx = playerX - battleBoxRight; var rightDy = playerY - rightY; var rightDist = Math.sqrt(rightDx * rightDx + rightDy * rightDy); spawnProjectile(battleBoxRight + 30, rightY, speed * rightDx / rightDist, speed * rightDy / rightDist); // Top wall - aim towards player var topDx = playerX - topX; var topDy = playerY - battleBoxTop; var topDist = Math.sqrt(topDx * topDx + topDy * topDy); spawnProjectile(topX, battleBoxTop - 30, speed * topDx / topDist, speed * topDy / topDist); // Bottom wall - aim towards player var bottomDx = playerX - bottomX; var bottomDy = playerY - battleBoxBottom; var bottomDist = Math.sqrt(bottomDx * bottomDx + bottomDy * bottomDy); spawnProjectile(bottomX, battleBoxBottom + 30, speed * bottomDx / bottomDist, speed * bottomDy / bottomDist); } // Add diagonal projectiles aimed at player position LK.setTimeout(function () { var currentPlayerX = player.x; var currentPlayerY = player.y; var diagSpeed = speed * 0.7; // Calculate diagonal directions towards player var corners = [{ x: battleBoxLeft - 30, y: battleBoxTop - 30 }, { x: battleBoxRight + 30, y: battleBoxTop - 30 }, { x: battleBoxLeft - 30, y: battleBoxBottom + 30 }, { x: battleBoxRight + 30, y: battleBoxBottom + 30 }]; for (var i = 0; i < corners.length; i++) { var corner = corners[i]; var dx = currentPlayerX - corner.x; var dy = currentPlayerY - corner.y; var dist = Math.sqrt(dx * dx + dy * dy); spawnProjectile(corner.x, corner.y, diagSpeed * dx / dist, diagSpeed * dy / dist); } }, 400); // Add tracking projectiles in phase 3 if (currentPhase === 3) { LK.setTimeout(function () { var currentPlayerX = player.x; var currentPlayerY = player.y; // Create tracking projectiles that aim at player's current position for (var i = 0; i < 4; i++) { var angle = i * Math.PI / 2; var startX = battleBoxLeft + 400 + Math.cos(angle) * 350; var startY = battleBoxTop + 300 + Math.sin(angle) * 250; var dx = currentPlayerX - startX; var dy = currentPlayerY - startY; var dist = Math.sqrt(dx * dx + dy * dy); spawnProjectile(startX, startY, 4 * dx / dist, 4 * dy / dist); } }, 600); } } } } function updateHealthBar() { var healthPercent = playerHealth / maxHealth; healthBar.scaleX = healthPercent; healthBar.x = 1024 - 200 * (1 - healthPercent); healthText.setText('HP: ' + playerHealth + '/' + maxHealth); } function updateEnemyHealthBar() { var enemyHealthPercent = enemyHealth / maxEnemyHealth; enemyHealthBar.scaleX = enemyHealthPercent; enemyHealthBar.x = 400 - 300 * (1 - enemyHealthPercent); enemyHealthText.setText('Enemy HP: ' + enemyHealth + '/' + maxEnemyHealth); } function updateSpareProgress() { var sparePercent = spareCount / maxSpareCount; spareBar.scaleX = sparePercent; spareBar.x = 1648 - 200 * (1 - sparePercent); spareText.setText('Spare Progress: ' + spareCount + '/' + maxSpareCount); } function updateHealCount() { var remaining = maxHealCount - healCount; healText.setText('Heals: ' + remaining + '/' + maxHealCount); if (remaining === 0) { healButton.alpha = 0.5; } } function updateTPBar() { var tpPercent = playerTP / maxTP; tpBar.scaleX = tpPercent; tpBar.x = 1024 - 200 * (1 - tpPercent); var roundedTP = Math.round(playerTP * 10) / 10; tpText.setText('TP: ' + roundedTP + '/' + maxTP); } function getCurrentPhase() { var healthPercent = enemyHealth / maxEnemyHealth; var sparePercent = spareCount / maxSpareCount; var combinedPercent = Math.max(healthPercent, sparePercent); if (combinedPercent > phase1Threshold) { return 1; // Phase 1: Easy (above 66%) } else if (combinedPercent > phase2Threshold) { return 2; // Phase 2: Medium (33-66%) } else { return 3; // Phase 3: Hard (below 33%) } } function checkNarrowDodge() { var hasNarrowDodge = false; for (var i = 0; i < projectiles.length; i++) { var projectile = projectiles[i]; if (projectile.isActive) { var dx = player.x - projectile.x; var dy = player.y - projectile.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= narrowDodgeDistance && distance > 30) { hasNarrowDodge = true; break; // Exit loop once we find one narrow dodge } } } if (hasNarrowDodge) { playerTP = Math.min(playerTP + 0.1, maxTP); playerTP = Math.round(playerTP * 10) / 10; updateTPBar(); LK.effects.flashObject(player, 0xffff00, 200); } } function checkCollisions() { for (var i = projectiles.length - 1; i >= 0; i--) { var projectile = projectiles[i]; if (projectile.isActive && player.intersects(projectile) && !isInvulnerable) { // Player hit - damage doubles in phase 3 var damage = currentPhase === 3 ? 20 : 10; playerHealth -= damage; LK.getSound('hit').play(); LK.effects.flashObject(player, 0xff0000, 200); // Start invulnerability isInvulnerable = true; player.alpha = 0.5; // Use tween to make player flash during invulnerability tween(player, { alpha: 1 }, { duration: 250, easing: tween.easeInOut, onFinish: function onFinish() { tween(player, { alpha: 0.5 }, { duration: 250, easing: tween.easeInOut }); } }); // Set timeout to end invulnerability LK.setTimeout(function () { isInvulnerable = false; tween.stop(player, { alpha: true }); player.alpha = 1; }, invulnerabilityDuration * 1000 / 60); // Convert frames to milliseconds // Remove projectile projectile.destroy(); projectiles.splice(i, 1); updateHealthBar(); if (playerHealth <= 0) { LK.showGameOver(); return; } } } } // Initialize UI state updateHealthBar(); updateEnemyHealthBar(); updateSpareProgress(); updateHealCount(); updateTPBar(); upButton.visible = false; downButton.visible = false; leftButton.visible = false; rightButton.visible = false; specialAttackButton.visible = true; // Create purple gradient at bottom for (var g = 0; g < gradientLayerCount; g++) { var gradientLayer = game.addChild(LK.getAsset('purpleGradient', { anchorX: 0.5, anchorY: 0.0 })); gradientLayer.x = 1024; gradientLayer.y = 2732 - 400 + g * 40; // Start from bottom and stack layers upward with tighter spacing gradientLayer.alpha = 0.15 - g * 0.018; // Much more transparent - start at 0.15 and fade out more gradually gradientLayer.height = 450 - g * 25; // Slightly larger layers with more gradual size reduction purpleGradientLayers.push(gradientLayer); } // Start battle music LK.playMusic('He2'); game.update = function () { // Update TP hitbox position to follow player tpHitbox.x = player.x; tpHitbox.y = player.y; // Show/hide TP hitbox based on game state tpHitbox.visible = gameState === 'defense'; // Purple particle system particleSpawnTimer++; if (particleSpawnTimer >= 30) { // Spawn every 0.5 seconds particleSpawnTimer = 0; if (purpleParticles.length < maxParticles) { var particle = new PurpleParticle(); // Random position at bottom of screen particle.x = Math.random() * 2048; particle.y = 2732; // Bottom of screen // Random upward velocity with slight horizontal drift var vx = (Math.random() - 0.5) * 2; // -1 to 1 horizontal drift var vy = -(Math.random() * 3 + 1); // -1 to -4 upward velocity particle.setVelocity(vx, vy); purpleParticles.push(particle); game.addChild(particle); // Animate particle with tween for scaling effect tween(particle, { scaleX: 1.5, scaleY: 1.5 }, { duration: 2000, easing: tween.easeOut }); } } // Update and clean up purple particles for (var i = purpleParticles.length - 1; i >= 0; i--) { if (!purpleParticles[i].isActive) { purpleParticles[i].destroy(); purpleParticles.splice(i, 1); } } if (gameState === 'defense') { defenseTimer++; // Update projectile patterns updateProjectilePattern(); // Check for narrow dodges checkNarrowDodge(); // Check for collisions checkCollisions(); // Remove inactive projectiles for (var i = projectiles.length - 1; i >= 0; i--) { if (!projectiles[i].isActive) { projectiles[i].destroy(); projectiles.splice(i, 1); } } // End defense phase if (defenseTimer >= defenseDuration) { startAttackPhase(); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Player = Container.expand(function () {
var self = Container.call(this);
self.moveSpeed = 8;
self.isMoving = false;
self.targetX = 0;
self.targetY = 0;
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.moveTo = function (x, y) {
if (x < battleBoxLeft + 30) x = battleBoxLeft + 30;
if (x > battleBoxRight - 30) x = battleBoxRight - 30;
if (y < battleBoxTop + 30) y = battleBoxTop + 30;
if (y > battleBoxBottom - 30) y = battleBoxBottom - 30;
self.targetX = x;
self.targetY = y;
self.isMoving = true;
};
self.update = function () {
if (self.isMoving) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.moveSpeed) {
self.x = self.targetX;
self.y = self.targetY;
self.isMoving = false;
} else {
self.x += dx / distance * self.moveSpeed;
self.y += dy / distance * self.moveSpeed;
}
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
self.velocityX = 0;
self.velocityY = 0;
self.isActive = true;
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.setVelocity = function (vx, vy) {
self.velocityX = vx;
self.velocityY = vy;
};
self.update = function () {
if (self.isActive) {
self.x += self.velocityX;
self.y += self.velocityY;
// Remove if out of battle box bounds
if (self.x < battleBoxLeft - 50 || self.x > battleBoxRight + 50 || self.y < battleBoxTop - 50 || self.y > battleBoxBottom + 50) {
self.isActive = false;
}
}
};
return self;
});
var PurpleParticle = Container.expand(function () {
var self = Container.call(this);
self.isActive = true;
self.velocityX = 0;
self.velocityY = 0;
self.lifeTime = 0;
self.maxLifeTime = 300; // 5 seconds at 60fps
var particleGraphics = self.attachAsset('purpleParticle', {
anchorX: 0.5,
anchorY: 0.5
});
self.setVelocity = function (vx, vy) {
self.velocityX = vx;
self.velocityY = vy;
};
self.update = function () {
if (self.isActive) {
self.x += self.velocityX;
self.y += self.velocityY;
self.lifeTime++;
// Fade out over time
var fadeRatio = 1 - self.lifeTime / self.maxLifeTime;
particleGraphics.alpha = fadeRatio;
// Remove if lifetime exceeded or out of bounds
if (self.lifeTime >= self.maxLifeTime || self.y < 0) {
self.isActive = false;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111
});
/****
* Game Code
****/
// Game state variables
var gameState = 'attack'; // 'attack' or 'defense'
var playerHealth = 200;
var maxHealth = 200;
var enemyHealth = 5000;
var maxEnemyHealth = 5000;
var projectiles = [];
var defenseTimer = 0;
var defenseDuration = 300; // 5 seconds at 60fps
var spareCount = 0;
var maxSpareCount = 15;
var healCount = 0;
var maxHealCount = 8;
var isInvulnerable = false;
var invulnerabilityDuration = 90; // 1.5 seconds at 60fps
var playerTP = 0;
var maxTP = 100;
var narrowDodgeDistance = 80; // Distance threshold for narrow dodge
var usedPatterns = []; // Track used patterns
var maxPatterns = 3; // Maximum number of patterns to mix
var forceMovementPatterns = [0, 1, 2]; // Patterns that force movement
// Phase system variables
var currentPhase = 1;
var maxPhases = 3;
var phase1Threshold = 0.66; // 66% health remaining
var phase2Threshold = 0.33; // 33% health remaining
// Purple particle system variables
var purpleParticles = [];
var particleSpawnTimer = 0;
var maxParticles = 20;
// Purple gradient variables
var purpleGradientLayers = [];
var gradientLayerCount = 8;
// Battle box boundaries
var battleBoxLeft = 1024 - 400;
var battleBoxRight = 1024 + 400;
var battleBoxTop = 1366 - 300;
var battleBoxBottom = 1366 + 300;
// Create battle box
var battleBox = game.addChild(LK.getAsset('battleBox', {
anchorX: 0.5,
anchorY: 0.5
}));
battleBox.x = 1024;
battleBox.y = 1366;
// Create player
var player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Create TP hitbox visual
var tpHitbox = game.addChild(LK.getAsset('tpHitbox', {
anchorX: 0.5,
anchorY: 0.5
}));
tpHitbox.x = player.x;
tpHitbox.y = player.y;
tpHitbox.alpha = 0.3;
// Create health bar background
var healthBarBg = game.addChild(LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
}));
healthBarBg.x = 1024;
healthBarBg.y = 1700;
// Create health bar
var healthBar = game.addChild(LK.getAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
}));
healthBar.x = 1024;
healthBar.y = 1700;
// Create enemy sprite
var enemy = game.addChild(LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
}));
enemy.x = 1024;
enemy.y = 800;
// Create attack button
var attackButton = game.addChild(LK.getAsset('attackButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
}));
attackButton.x = 650;
attackButton.y = 1800;
// Create spare button
var spareButton = game.addChild(LK.getAsset('spareButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
}));
spareButton.x = 1398;
spareButton.y = 1800;
// Create heal button
var healButton = game.addChild(LK.getAsset('healButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
}));
healButton.x = 1149;
healButton.y = 1800;
// Create special attack button
var specialAttackButton = game.addChild(LK.getAsset('specialAttackButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
}));
specialAttackButton.x = 899;
specialAttackButton.y = 1800;
// Create movement buttons
var upButton = game.addChild(LK.getAsset('upButton', {
anchorX: 0.5,
anchorY: 0.5
}));
upButton.x = 1024;
upButton.y = 1800;
var downButton = game.addChild(LK.getAsset('downButton', {
anchorX: 0.5,
anchorY: 0.5
}));
downButton.x = 1024;
downButton.y = 2200;
var leftButton = game.addChild(LK.getAsset('leftButton', {
anchorX: 0.5,
anchorY: 0.5
}));
leftButton.x = 750;
leftButton.y = 2000;
var rightButton = game.addChild(LK.getAsset('rightButton', {
anchorX: 0.5,
anchorY: 0.5
}));
rightButton.x = 1298;
rightButton.y = 2000;
// Create UI text
var phaseText = new Text2('ATTACK PHASE', {
size: 80,
fill: 0xFFFF00
});
phaseText.anchor.set(0.5, 0);
phaseText.x = 1024;
phaseText.y = 200;
game.addChild(phaseText);
var healthText = new Text2('HP: 200/200', {
size: 50,
fill: 0xffffff
});
healthText.anchor.set(0.5, 0.5);
healthText.x = 1024;
healthText.y = 1700;
game.addChild(healthText);
// Create enemy health bar background
var enemyHealthBarBg = game.addChild(LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5
}));
enemyHealthBarBg.x = 400;
enemyHealthBarBg.y = 1950;
// Create enemy health bar
var enemyHealthBar = game.addChild(LK.getAsset('Enemybar', {
anchorX: 0.5,
anchorY: 0.5
}));
enemyHealthBar.x = 400;
enemyHealthBar.y = 1950;
enemyHealthBar.tint = 0xff0000;
var enemyHealthText = new Text2('Enemy HP: 5000/5000', {
size: 50,
fill: 0x0066ff
});
enemyHealthText.anchor.set(0.5, 0.5);
enemyHealthText.x = 400;
enemyHealthText.y = 1950;
game.addChild(enemyHealthText);
// Create spare bar background
var spareBarBg = game.addChild(LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
}));
spareBarBg.x = 1648;
spareBarBg.y = 1950;
// Create spare bar
var spareBar = game.addChild(LK.getAsset('spareProgressBar', {
anchorX: 0.5,
anchorY: 0.5
}));
spareBar.x = 1648;
spareBar.y = 1950;
var spareText = new Text2('Spare Progress: 0/15', {
size: 50,
fill: 0x00ff66
});
spareText.anchor.set(0.5, 0.5);
spareText.x = 1648;
spareText.y = 1950;
game.addChild(spareText);
var healText = new Text2('Heals: 8/8', {
size: 50,
fill: 0x00ff00
});
healText.anchor.set(0.5, 0);
healText.x = 1024;
healText.y = 2250;
game.addChild(healText);
// Create TP bar background
var tpBarBg = game.addChild(LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
}));
tpBarBg.x = 1024;
tpBarBg.y = 400;
// Create TP bar
var tpBar = game.addChild(LK.getAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
}));
tpBar.x = 1024;
tpBar.y = 400;
tpBar.tint = 0xffff00;
var tpText = new Text2('TP: 0/100', {
size: 50,
fill: 0xffff00
});
tpText.anchor.set(0.5, 0);
tpText.x = 1024;
tpText.y = 450;
game.addChild(tpText);
// Button event handlers
attackButton.down = function () {
if (gameState === 'attack') {
var damage = Math.floor(Math.random() * 101) + 100; // Random damage between 100-200
enemyHealth -= damage;
// Add 6 TP when attacking
playerTP = Math.min(playerTP + 6, maxTP);
playerTP = Math.round(playerTP * 10) / 10;
updateTPBar();
updateEnemyHealthBar();
LK.getSound('attack').play();
if (enemyHealth <= 0) {
LK.showYouWin();
return;
}
startDefensePhase();
}
};
spareButton.down = function () {
if (gameState === 'attack') {
spareCount++;
updateSpareProgress();
LK.getSound('spare').play();
if (spareCount >= maxSpareCount) {
LK.showYouWin();
return;
}
startDefensePhase();
}
};
healButton.down = function () {
if (gameState === 'attack' && healCount < maxHealCount && playerHealth < maxHealth) {
healCount++;
playerHealth = Math.min(playerHealth + 100, maxHealth);
updateHealthBar();
updateHealCount();
LK.getSound('heal').play();
LK.effects.flashObject(player, 0x00ff00, 300);
startDefensePhase();
}
};
upButton.down = function () {
if (gameState === 'defense') {
player.moveTo(player.x, player.y - 60);
LK.getSound('dodge').play();
}
};
downButton.down = function () {
if (gameState === 'defense') {
player.moveTo(player.x, player.y + 60);
LK.getSound('dodge').play();
}
};
leftButton.down = function () {
if (gameState === 'defense') {
player.moveTo(player.x - 60, player.y);
LK.getSound('dodge').play();
}
};
rightButton.down = function () {
if (gameState === 'defense') {
player.moveTo(player.x + 60, player.y);
LK.getSound('dodge').play();
}
};
specialAttackButton.down = function () {
if (gameState === 'attack' && playerTP >= 50) {
var damage = Math.floor(Math.random() * 201) + 500; // Random damage between 500-700
enemyHealth -= damage;
playerTP -= 50;
playerTP = Math.round(playerTP * 10) / 10;
updateEnemyHealthBar();
updateTPBar();
LK.getSound('skill').play();
LK.effects.flashObject(enemy, 0xff6600, 500);
if (enemyHealth <= 0) {
LK.showYouWin();
return;
}
startDefensePhase();
}
};
function startDefensePhase() {
gameState = 'defense';
defenseTimer = 0;
currentPhase = getCurrentPhase();
// Adjust defense phase based on current phase
if (currentPhase === 1) {
defenseDuration = 180; // 3 seconds - easy
maxPatterns = 2; // Use only 2 patterns
phaseText.setText('DEFENSE PHASE - PHASE 1');
} else if (currentPhase === 2) {
defenseDuration = 210; // 3.5 seconds - medium (reduced from 240)
maxPatterns = 2; // Use only 2 patterns (reduced from 3)
phaseText.setText('DEFENSE PHASE - PHASE 2');
} else {
defenseDuration = 300; // 5 seconds - hard
maxPatterns = 3; // Use all 3 patterns
phaseText.setText('DEFENSE PHASE - PHASE 3');
}
phaseText.tint = 0xff0000;
// Reset used patterns for new defense phase
usedPatterns = [];
// Hide attack buttons, show movement buttons
attackButton.visible = false;
spareButton.visible = false;
healButton.visible = false;
specialAttackButton.visible = false;
upButton.visible = true;
downButton.visible = true;
leftButton.visible = true;
rightButton.visible = true;
}
function startAttackPhase() {
gameState = 'attack';
phaseText.setText('ATTACK PHASE');
phaseText.tint = 0xffff00;
// Clear all projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
projectiles[i].destroy();
projectiles.splice(i, 1);
}
// Hide movement buttons immediately
upButton.visible = false;
downButton.visible = false;
leftButton.visible = false;
rightButton.visible = false;
// Hide attack buttons initially
attackButton.visible = false;
spareButton.visible = false;
healButton.visible = false;
specialAttackButton.visible = false;
// Show attack buttons after half a second delay
LK.setTimeout(function () {
attackButton.visible = true;
spareButton.visible = true;
healButton.visible = true;
specialAttackButton.visible = true;
}, 500);
}
function spawnProjectile(x, y, vx, vy) {
// Limit the number of projectiles to 50
if (projectiles.length >= 50) {
return; // Don't spawn new projectile if limit reached
}
var projectile = new Projectile();
projectile.x = x;
projectile.y = y;
projectile.setVelocity(vx, vy);
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('projectile').play();
}
function updateProjectilePattern() {
// Only spawn projectiles during defense phase
if (gameState !== 'defense') {
return;
}
// Adjust spawn frequency based on phase
var spawnFrequency;
if (currentPhase === 1) {
spawnFrequency = 40; // Spawn every 0.67 seconds - easy
} else if (currentPhase === 2) {
spawnFrequency = 30; // Spawn every 0.5 seconds - medium
} else {
spawnFrequency = 18; // Spawn every 0.3 seconds - hard
}
if (defenseTimer % spawnFrequency === 0) {
var patternType;
// Reset used patterns if we've used all allowed patterns
if (usedPatterns.length >= maxPatterns) {
usedPatterns = [];
}
// Select a pattern that forces movement and hasn't been used yet
var availablePatterns = [];
for (var p = 0; p < forceMovementPatterns.length; p++) {
var pattern = forceMovementPatterns[p];
var isUsed = false;
for (var u = 0; u < usedPatterns.length; u++) {
if (usedPatterns[u] === pattern) {
isUsed = true;
break;
}
}
if (!isUsed) {
availablePatterns.push(pattern);
}
}
// If no available patterns, reset and use any force movement pattern
if (availablePatterns.length === 0) {
usedPatterns = [];
availablePatterns = forceMovementPatterns.slice(); // Copy array
}
// Select random pattern from available ones
patternType = availablePatterns[Math.floor(Math.random() * availablePatterns.length)];
usedPatterns.push(patternType);
if (patternType === 0) {
// Horizontal sweep - aligned with player's vertical position
var projectileCount = currentPhase === 1 ? 5 : currentPhase === 2 ? 7 : 9;
var speed = currentPhase === 1 ? 4 : currentPhase === 2 ? 6 : 8;
var playerY = player.y;
var spreadRange = 200; // How much to spread around player position
for (var i = 0; i < projectileCount; i++) {
var yOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount);
var targetY = Math.max(battleBoxTop + 30, Math.min(battleBoxBottom - 30, playerY + yOffset));
spawnProjectile(battleBoxLeft - 30, targetY, speed, 0);
}
// Add delayed second wave only in phase 2 and 3
if (currentPhase >= 2) {
LK.setTimeout(function () {
var currentPlayerY = player.y;
for (var i = 0; i < projectileCount; i++) {
var yOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount) + 40;
var targetY = Math.max(battleBoxTop + 30, Math.min(battleBoxBottom - 30, currentPlayerY + yOffset));
spawnProjectile(battleBoxLeft - 30, targetY, speed, 0);
}
}, currentPhase === 2 ? 500 : 300);
}
// Add third wave for phase 3
if (currentPhase === 3) {
LK.setTimeout(function () {
var currentPlayerY = player.y;
for (var i = 0; i < projectileCount; i++) {
var yOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount) - 40;
var targetY = Math.max(battleBoxTop + 30, Math.min(battleBoxBottom - 30, currentPlayerY + yOffset));
spawnProjectile(battleBoxLeft - 30, targetY, speed, 0);
}
}, 600);
}
} else if (patternType === 1) {
// Vertical sweep - aligned with player's horizontal position
var projectileCount = currentPhase === 1 ? 6 : currentPhase === 2 ? 8 : 10;
var speed = currentPhase === 1 ? 4 : currentPhase === 2 ? 5 : 7;
var playerX = player.x;
var spreadRange = 300; // How much to spread around player position
for (var i = 0; i < projectileCount; i++) {
var xOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount);
var targetX = Math.max(battleBoxLeft + 30, Math.min(battleBoxRight - 30, playerX + xOffset));
spawnProjectile(targetX, battleBoxTop - 30, 0, speed);
}
// Add delayed second wave only in phase 2 and 3
if (currentPhase >= 2) {
LK.setTimeout(function () {
var currentPlayerX = player.x;
for (var i = 0; i < projectileCount; i++) {
var xOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount) + 40;
var targetX = Math.max(battleBoxLeft + 30, Math.min(battleBoxRight - 30, currentPlayerX + xOffset));
spawnProjectile(targetX, battleBoxTop - 30, 0, speed);
}
}, currentPhase === 2 ? 500 : 300);
}
// Add third wave for phase 3
if (currentPhase === 3) {
LK.setTimeout(function () {
var currentPlayerX = player.x;
for (var i = 0; i < projectileCount; i++) {
var xOffset = (i - Math.floor(projectileCount / 2)) * (spreadRange / projectileCount) - 40;
var targetX = Math.max(battleBoxLeft + 30, Math.min(battleBoxRight - 30, currentPlayerX + xOffset));
spawnProjectile(targetX, battleBoxTop - 30, 0, speed);
}
}, 600);
}
} else if (patternType === 2) {
// Closing walls pattern - converges on player position
var playerX = player.x;
var playerY = player.y;
var wallCount = currentPhase === 1 ? 8 : currentPhase === 2 ? 10 : 12;
var speed = currentPhase === 1 ? 2 : currentPhase === 2 ? 3 : 4;
// Create walls from all sides that converge on player
for (var i = 0; i < wallCount; i++) {
// Calculate direction vectors towards player
var leftY = battleBoxTop + i * (600 / wallCount);
var rightY = battleBoxTop + i * (600 / wallCount);
var topX = battleBoxLeft + i * (800 / wallCount);
var bottomX = battleBoxLeft + i * (800 / wallCount);
// Left wall - aim towards player
var leftDx = playerX - battleBoxLeft;
var leftDy = playerY - leftY;
var leftDist = Math.sqrt(leftDx * leftDx + leftDy * leftDy);
spawnProjectile(battleBoxLeft - 30, leftY, speed * leftDx / leftDist, speed * leftDy / leftDist);
// Right wall - aim towards player
var rightDx = playerX - battleBoxRight;
var rightDy = playerY - rightY;
var rightDist = Math.sqrt(rightDx * rightDx + rightDy * rightDy);
spawnProjectile(battleBoxRight + 30, rightY, speed * rightDx / rightDist, speed * rightDy / rightDist);
// Top wall - aim towards player
var topDx = playerX - topX;
var topDy = playerY - battleBoxTop;
var topDist = Math.sqrt(topDx * topDx + topDy * topDy);
spawnProjectile(topX, battleBoxTop - 30, speed * topDx / topDist, speed * topDy / topDist);
// Bottom wall - aim towards player
var bottomDx = playerX - bottomX;
var bottomDy = playerY - battleBoxBottom;
var bottomDist = Math.sqrt(bottomDx * bottomDx + bottomDy * bottomDy);
spawnProjectile(bottomX, battleBoxBottom + 30, speed * bottomDx / bottomDist, speed * bottomDy / bottomDist);
}
// Add diagonal projectiles aimed at player position
LK.setTimeout(function () {
var currentPlayerX = player.x;
var currentPlayerY = player.y;
var diagSpeed = speed * 0.7;
// Calculate diagonal directions towards player
var corners = [{
x: battleBoxLeft - 30,
y: battleBoxTop - 30
}, {
x: battleBoxRight + 30,
y: battleBoxTop - 30
}, {
x: battleBoxLeft - 30,
y: battleBoxBottom + 30
}, {
x: battleBoxRight + 30,
y: battleBoxBottom + 30
}];
for (var i = 0; i < corners.length; i++) {
var corner = corners[i];
var dx = currentPlayerX - corner.x;
var dy = currentPlayerY - corner.y;
var dist = Math.sqrt(dx * dx + dy * dy);
spawnProjectile(corner.x, corner.y, diagSpeed * dx / dist, diagSpeed * dy / dist);
}
}, 400);
// Add tracking projectiles in phase 3
if (currentPhase === 3) {
LK.setTimeout(function () {
var currentPlayerX = player.x;
var currentPlayerY = player.y;
// Create tracking projectiles that aim at player's current position
for (var i = 0; i < 4; i++) {
var angle = i * Math.PI / 2;
var startX = battleBoxLeft + 400 + Math.cos(angle) * 350;
var startY = battleBoxTop + 300 + Math.sin(angle) * 250;
var dx = currentPlayerX - startX;
var dy = currentPlayerY - startY;
var dist = Math.sqrt(dx * dx + dy * dy);
spawnProjectile(startX, startY, 4 * dx / dist, 4 * dy / dist);
}
}, 600);
}
}
}
}
function updateHealthBar() {
var healthPercent = playerHealth / maxHealth;
healthBar.scaleX = healthPercent;
healthBar.x = 1024 - 200 * (1 - healthPercent);
healthText.setText('HP: ' + playerHealth + '/' + maxHealth);
}
function updateEnemyHealthBar() {
var enemyHealthPercent = enemyHealth / maxEnemyHealth;
enemyHealthBar.scaleX = enemyHealthPercent;
enemyHealthBar.x = 400 - 300 * (1 - enemyHealthPercent);
enemyHealthText.setText('Enemy HP: ' + enemyHealth + '/' + maxEnemyHealth);
}
function updateSpareProgress() {
var sparePercent = spareCount / maxSpareCount;
spareBar.scaleX = sparePercent;
spareBar.x = 1648 - 200 * (1 - sparePercent);
spareText.setText('Spare Progress: ' + spareCount + '/' + maxSpareCount);
}
function updateHealCount() {
var remaining = maxHealCount - healCount;
healText.setText('Heals: ' + remaining + '/' + maxHealCount);
if (remaining === 0) {
healButton.alpha = 0.5;
}
}
function updateTPBar() {
var tpPercent = playerTP / maxTP;
tpBar.scaleX = tpPercent;
tpBar.x = 1024 - 200 * (1 - tpPercent);
var roundedTP = Math.round(playerTP * 10) / 10;
tpText.setText('TP: ' + roundedTP + '/' + maxTP);
}
function getCurrentPhase() {
var healthPercent = enemyHealth / maxEnemyHealth;
var sparePercent = spareCount / maxSpareCount;
var combinedPercent = Math.max(healthPercent, sparePercent);
if (combinedPercent > phase1Threshold) {
return 1; // Phase 1: Easy (above 66%)
} else if (combinedPercent > phase2Threshold) {
return 2; // Phase 2: Medium (33-66%)
} else {
return 3; // Phase 3: Hard (below 33%)
}
}
function checkNarrowDodge() {
var hasNarrowDodge = false;
for (var i = 0; i < projectiles.length; i++) {
var projectile = projectiles[i];
if (projectile.isActive) {
var dx = player.x - projectile.x;
var dy = player.y - projectile.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= narrowDodgeDistance && distance > 30) {
hasNarrowDodge = true;
break; // Exit loop once we find one narrow dodge
}
}
}
if (hasNarrowDodge) {
playerTP = Math.min(playerTP + 0.1, maxTP);
playerTP = Math.round(playerTP * 10) / 10;
updateTPBar();
LK.effects.flashObject(player, 0xffff00, 200);
}
}
function checkCollisions() {
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
if (projectile.isActive && player.intersects(projectile) && !isInvulnerable) {
// Player hit - damage doubles in phase 3
var damage = currentPhase === 3 ? 20 : 10;
playerHealth -= damage;
LK.getSound('hit').play();
LK.effects.flashObject(player, 0xff0000, 200);
// Start invulnerability
isInvulnerable = true;
player.alpha = 0.5;
// Use tween to make player flash during invulnerability
tween(player, {
alpha: 1
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
alpha: 0.5
}, {
duration: 250,
easing: tween.easeInOut
});
}
});
// Set timeout to end invulnerability
LK.setTimeout(function () {
isInvulnerable = false;
tween.stop(player, {
alpha: true
});
player.alpha = 1;
}, invulnerabilityDuration * 1000 / 60); // Convert frames to milliseconds
// Remove projectile
projectile.destroy();
projectiles.splice(i, 1);
updateHealthBar();
if (playerHealth <= 0) {
LK.showGameOver();
return;
}
}
}
}
// Initialize UI state
updateHealthBar();
updateEnemyHealthBar();
updateSpareProgress();
updateHealCount();
updateTPBar();
upButton.visible = false;
downButton.visible = false;
leftButton.visible = false;
rightButton.visible = false;
specialAttackButton.visible = true;
// Create purple gradient at bottom
for (var g = 0; g < gradientLayerCount; g++) {
var gradientLayer = game.addChild(LK.getAsset('purpleGradient', {
anchorX: 0.5,
anchorY: 0.0
}));
gradientLayer.x = 1024;
gradientLayer.y = 2732 - 400 + g * 40; // Start from bottom and stack layers upward with tighter spacing
gradientLayer.alpha = 0.15 - g * 0.018; // Much more transparent - start at 0.15 and fade out more gradually
gradientLayer.height = 450 - g * 25; // Slightly larger layers with more gradual size reduction
purpleGradientLayers.push(gradientLayer);
}
// Start battle music
LK.playMusic('He2');
game.update = function () {
// Update TP hitbox position to follow player
tpHitbox.x = player.x;
tpHitbox.y = player.y;
// Show/hide TP hitbox based on game state
tpHitbox.visible = gameState === 'defense';
// Purple particle system
particleSpawnTimer++;
if (particleSpawnTimer >= 30) {
// Spawn every 0.5 seconds
particleSpawnTimer = 0;
if (purpleParticles.length < maxParticles) {
var particle = new PurpleParticle();
// Random position at bottom of screen
particle.x = Math.random() * 2048;
particle.y = 2732; // Bottom of screen
// Random upward velocity with slight horizontal drift
var vx = (Math.random() - 0.5) * 2; // -1 to 1 horizontal drift
var vy = -(Math.random() * 3 + 1); // -1 to -4 upward velocity
particle.setVelocity(vx, vy);
purpleParticles.push(particle);
game.addChild(particle);
// Animate particle with tween for scaling effect
tween(particle, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 2000,
easing: tween.easeOut
});
}
}
// Update and clean up purple particles
for (var i = purpleParticles.length - 1; i >= 0; i--) {
if (!purpleParticles[i].isActive) {
purpleParticles[i].destroy();
purpleParticles.splice(i, 1);
}
}
if (gameState === 'defense') {
defenseTimer++;
// Update projectile patterns
updateProjectilePattern();
// Check for narrow dodges
checkNarrowDodge();
// Check for collisions
checkCollisions();
// Remove inactive projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
if (!projectiles[i].isActive) {
projectiles[i].destroy();
projectiles.splice(i, 1);
}
}
// End defense phase
if (defenseTimer >= defenseDuration) {
startAttackPhase();
}
}
};
Appearance: An advanced and dramatically redesigned robotic version of Mettaton. It has futuristic armor, energy wings, and a stylized design reminiscent of an anime mecha. He have a heels, 2d pixel art
Red heart pixel art. In-Game asset. 2d. High contrast. No shadows
Black button with an orange outline that says "attack" and has a sword icon, pixel art. In-Game asset. 2d. High contrast. No shadows
Just a square border white no details. In-Game asset. 2d. High contrast. No shadows
Black button with an orange outline that says "Item" and has a Bag icon, pixel art. In-Game asset. 2d. High contrast. No shadows
Black button with an orange outline that says "Spare" and has a X icon, pixel art. In-Game asset. 2d. High contrast. No shadows
Black button with an orange outline that says "Spell" and has a fire icon, pixel art. In-Game asset. 2d. High contrast. No shadows
Energy ball. In-Game asset. 2d. High contrast. No shadows