User prompt
Reduces the gain even further
User prompt
That the TP recharges more slowly
User prompt
Let only one bullet count towards the TP
User prompt
Let the TP recharge 0.1 per second
User prompt
Let only one bullet count for the TP
User prompt
Let the TP recharge 1 per second
User prompt
Let only one bullet count, no matter the rest
User prompt
That the TP recharges 0.4 seconds slower
User prompt
Make the TP recharge every 0.1 seconds if there is a bullet in its hitbox, but if the player is in seconds of invulnerability, it does not happen and reduces the seconds of invulnerability to 1 seconds.
User prompt
Make the TP recharge more slowly and create a square around the player the size of the TP's hitbox.
User prompt
Make it so that when the player narrowly dodges bullets, he gains TP and can fill it up to 100.
User prompt
Make it so that to spare the enemy you have to act 15 times and the player has 2.5 seconds of invulnerability when hit ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make He2 loop when the battle starts
User prompt
Let the enemy have 5 different attack patterns
User prompt
The enemy has 8000 health, but the player deals 100 to 200 damage and the healing heals 50
User prompt
Make the table where the attacks are bigger.
User prompt
That the enemy has other attack patterns
User prompt
Remove the wave system
User prompt
Let there be no waves
User prompt
That the player can heal himself 8 times
User prompt
Make sure the bullets are a little further apart because it's almost impossible to dodge them and make sure the buttons are a little further apart.
User prompt
Make the buttons bigger, put a Sprite for the enemy and a way to defeat the enemy with violence or In a peaceful manner
Code edit (1 edits merged)
Please save this source code
User prompt
Dodge Quest: Turn-Based Defense
Initial prompt
An RPG game inspired by Undertale, which is a turn-based game where you can dodge your enemies' attacks, which has buttons to move around the attack box to dodge
/**** * 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