/****
* 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