/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var EnergyBar = Container.expand(function (fighter, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.fighter = fighter;
var bgBar = self.attachAsset('energyBarBg', {
anchorX: 0,
anchorY: 0
});
// Create 3 individual energy bars
self.energyBars = [];
for (var i = 0; i < 3; i++) {
var energyBar = self.attachAsset('energyBar', {
anchorX: 0,
anchorY: 0
});
energyBar.x = i * 335; // Space bars evenly (1000/3 ≈ 333)
energyBar.width = 310; // Slightly smaller to have gaps
self.energyBars.push(energyBar);
}
self.update = function () {
// Show energy bars based on current energy level (0-3)
var energyLevel = Math.floor(self.fighter.energy / (self.fighter.maxEnergy / 3));
for (var i = 0; i < 3; i++) {
self.energyBars[i].visible = i < energyLevel;
}
};
return self;
});
var Fighter = Container.expand(function (fighterType, isPlayer) {
var self = Container.call(this);
// Fighter properties
self.fighterType = fighterType || 1;
self.isPlayer = isPlayer || false;
self.maxHealth = 100;
self.health = 100;
self.maxEnergy = 3; // Changed to 3 energy units
self.energy = 3; // Start with full energy
self.speed = 7;
self.isBlocking = false;
self.attackCooldown = 0;
self.specialCooldown = 0;
self.maxSpecialCharge = 100;
self.specialCharge = 0;
self.isCrouching = false;
self.isJumping = false;
self.jumpHeight = 0;
self.maxJumpHeight = 100;
self.jumpSpeed = 15;
self.originalY = 0;
self.gravity = 0.12;
self.velocityY = 0;
self.groundY = 1800; // Ground level for all fighters - adjusted for horizontal layout and moved higher
// Create fighter graphics
var fighterGraphics = self.attachAsset('fighter' + self.fighterType, {
anchorX: 0.5,
anchorY: 1.0
});
// Create blocking shield (initially hidden)
var blockingShield = self.attachAsset('blockAsset', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0.0
});
blockingShield.visible = false;
// Animation properties
self.currentAnimation = 'normal';
self.animationTimer = 0;
self.animationDuration = 0;
self.isWalking = false;
self.isCrouchingAnimated = false;
self.lastX = self.x;
self.walkingAnimationTimer = 0;
// Fighter stats based on type
switch (self.fighterType) {
case 1:
// Red Fighter - Balanced
self.attackDamage = 8;
self.specialDamage = 10;
self.specialCost = 1; // 1 energy bar per special attack
break;
case 2:
// Blue Fighter - Fast
self.attackDamage = 6;
self.specialDamage = 8;
self.specialCost = 1; // 1 energy bar per special attack
self.speed = 4;
break;
case 3:
// Green Fighter - Strong
self.attackDamage = 10;
self.specialDamage = 12;
self.specialCost = 1; // 1 energy bar per special attack
self.speed = 2;
break;
case 4:
// Yellow Fighter - Energy Efficient
self.attackDamage = 7;
self.specialDamage = 9;
self.specialCost = 1; // 1 energy bar per special attack
break;
case 5:
// Purple Fighter - High Health
self.attackDamage = 7;
self.specialDamage = 9;
self.specialCost = 1; // 1 energy bar per special attack
self.maxHealth = 120;
self.health = 120;
break;
}
// Apply difficulty modifiers for enemy fighters
if (!self.isPlayer && gameDifficulty) {
if (gameDifficulty === 'easy') {
self.attackDamage = Math.floor(self.attackDamage * 0.7);
self.specialDamage = Math.floor(self.specialDamage * 0.7);
self.speed = Math.floor(self.speed * 0.8);
self.maxHealth = Math.floor(self.maxHealth * 0.8);
self.health = self.maxHealth;
} else if (gameDifficulty === 'hard') {
self.attackDamage = Math.floor(self.attackDamage * 1.3);
self.specialDamage = Math.floor(self.specialDamage * 1.3);
self.speed = Math.floor(self.speed * 1.2);
self.maxHealth = Math.floor(self.maxHealth * 1.2);
self.health = self.maxHealth;
}
}
// Track if this fighter has taken damage yet
self.hasBeenDamaged = false;
self.takeDamage = function (damage, isSuperProjectile, attacker) {
// Prevent multiple damage calls when fighter is already defeated
if (self.health <= 0 || self.isBeingDefeated) {
return; // No damage taken, fighter is already defeated or being defeated
}
// In team battle mode, prevent damage to fighters that haven't been damaged yet unless they are the current active fighter
if (gameMode === 'teamBattle') {
var isCurrentPlayerFighter = self.isPlayer && self === player;
var isCurrentEnemyFighter = !self.isPlayer && self === enemy;
var isCurrentActiveFighter = isCurrentPlayerFighter || isCurrentEnemyFighter;
// If this fighter hasn't been damaged yet and isn't the current active fighter, make them invulnerable
if (!self.hasBeenDamaged && !isCurrentActiveFighter) {
return; // No damage taken, fighter is invulnerable
}
}
if (self.isPlayer && playerIsBlocking) {
// Check if within perfect block window (0.5 seconds = 30 ticks at 60fps)
var timeSinceBlockStart = LK.ticks - playerBlockStartTime;
if (timeSinceBlockStart <= 30) {
// 30 ticks = 0.5 seconds at 60fps
damage = Math.floor(damage * 0.5); // 50% damage reduction (reduced from 100%)
// Show brief white flash effect during immunity period (like blue flash when blocked)
LK.effects.flashObject(fighterGraphics, 0xffffff, 200);
} else {
if (isSuperProjectile) {
damage = Math.floor(damage * 0.85); // Only 15% damage reduction for super projectiles after perfect block window (85% damage goes through)
} else {
damage = Math.floor(damage * 0.7); // 30% damage reduction for regular attacks after perfect block window
}
}
} else if (self.isBlocking) {
if (isSuperProjectile) {
damage = Math.floor(damage * 0.85); // Only 15% damage reduction when blocking super projectile (85% damage goes through)
} else {
damage = Math.floor(damage * 0.7); // 30% damage reduction for normal blocking (reduced from 50%)
}
}
// Mark fighter as having been damaged (once they take any damage)
if (damage > 0) {
self.hasBeenDamaged = true;
}
self.health = Math.max(0, self.health - damage);
// Charge special attack bar by 2% when taking damage (only if damage > 0)
if (damage > 0) {
self.chargeSpecial(2); // 2% of maxSpecialCharge (100)
}
// Flash red when taking damage
LK.effects.flashObject(fighterGraphics, 0xFF0000, 300);
};
self.useEnergy = function (amount) {
if (self.energy >= amount) {
self.energy -= amount;
return true;
}
return false;
};
self.attack = function (attackType) {
if (self.attackCooldown <= 0) {
self.attackCooldown = 30;
self.lastAttackType = attackType || 'punch';
return true;
}
return false;
};
self.specialAttack = function (specialType) {
if (self.specialCooldown <= 0 && self.useEnergy(self.specialCost)) {
self.specialCooldown = 90;
self.lastSpecialType = specialType || 'range';
return true;
}
return false;
};
self.chargeSpecial = function (amount) {
self.specialCharge = Math.min(self.maxSpecialCharge, self.specialCharge + amount);
};
self.canUseSpecialAttack = function () {
return self.specialCharge >= self.maxSpecialCharge;
};
self.useSpecialAttack = function () {
if (self.canUseSpecialAttack()) {
self.specialCharge = 0;
return true;
}
return false;
};
self.crouch = function () {
if (!self.isJumping) {
self.isCrouching = true;
if (self.currentAnimation === 'normal') {
self.setAnimation('crouching');
self.isCrouchingAnimated = true;
}
}
};
self.stopCrouch = function () {
self.isCrouching = false;
if (self.isCrouchingAnimated && self.currentAnimation === 'crouching') {
self.setAnimation('normal');
self.isCrouchingAnimated = false;
}
};
self.jump = function () {
if (!self.isCrouching && !self.isJumping && self.y >= self.groundY) {
self.isJumping = true;
self.velocityY = -self.jumpSpeed; // Negative velocity for upward movement
}
};
self.setAnimation = function (animationType, duration) {
if (animationType === self.currentAnimation) return;
self.currentAnimation = animationType;
self.animationTimer = duration || 0;
// Store current direction before removing graphics
var currentDirection = fighterGraphics.scaleX;
// Remove current graphics
self.removeChild(fighterGraphics);
// Add new graphics based on animation
var assetName = 'fighter' + self.fighterType;
if (animationType === 'punching') {
assetName = 'punchingAnimation' + self.fighterType;
} else if (animationType === 'kicking') {
assetName = 'kickAnimation' + self.fighterType;
} else if (animationType === 'walking') {
assetName = 'walkingAnimation' + self.fighterType;
} else if (animationType === 'crouching') {
assetName = 'crouchingAnimation' + self.fighterType;
} else if (animationType === 'rangeShooting') {
assetName = 'rangeShoot' + self.fighterType;
} else if (animationType === 'potentShooting') {
assetName = 'potentShoot' + self.fighterType;
}
fighterGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0
});
// Maintain direction after asset change
fighterGraphics.scaleX = currentDirection;
};
self.update = function () {
// Direction checking - determine if asset should face left or right
if (player && enemy) {
var shouldFaceRight = false;
if (self === player) {
shouldFaceRight = enemy.x > player.x; // Player faces enemy
} else if (self === enemy) {
shouldFaceRight = player.x > enemy.x; // Enemy faces player
}
// Update asset direction only when needed to prevent flashing
var currentFacingRight = fighterGraphics.scaleX > 0;
if (currentFacingRight !== shouldFaceRight) {
if (shouldFaceRight) {
fighterGraphics.scaleX = Math.abs(fighterGraphics.scaleX); // Face right
} else {
fighterGraphics.scaleX = -Math.abs(fighterGraphics.scaleX); // Face left
}
}
}
// Walking animation detection
var currentlyWalking = Math.abs(self.x - self.lastX) > 0.5;
if (currentlyWalking && !self.isWalking && self.currentAnimation === 'normal') {
self.setAnimation('walking');
self.isWalking = true;
self.walkingAnimationTimer = 0;
} else if (!currentlyWalking && self.isWalking && self.currentAnimation === 'walking') {
self.setAnimation('normal');
self.isWalking = false;
}
// Crouching animation detection
if (self.isCrouching && !self.isCrouchingAnimated && self.currentAnimation === 'normal') {
self.setAnimation('crouching');
self.isCrouchingAnimated = true;
} else if (!self.isCrouching && self.isCrouchingAnimated && self.currentAnimation === 'crouching') {
self.setAnimation('normal');
self.isCrouchingAnimated = false;
}
self.lastX = self.x;
// Animation management
if (self.animationTimer > 0) {
self.animationTimer--;
if (self.animationTimer <= 0) {
self.setAnimation('normal');
self.isWalking = false;
}
}
// Cooldown management
if (self.attackCooldown > 0) self.attackCooldown--;
if (self.specialCooldown > 0) self.specialCooldown--;
// Energy regeneration - regenerate 1 energy bar every 10 seconds (600 ticks), only one bar at a time
// Initialize lastEnergyRegenTime if not set
if (self.lastEnergyRegenTime === undefined) {
self.lastEnergyRegenTime = 0;
}
// Check if 10 seconds (600 ticks) have passed since last regeneration
if (self.energy < self.maxEnergy && LK.ticks - self.lastEnergyRegenTime >= 600) {
// Only regenerate if we're exactly at a whole number threshold (0, 1, or 2)
var currentEnergyLevel = Math.floor(self.energy);
// Only regenerate if current energy is exactly at the integer threshold
// This ensures sequential regeneration: 0->1, then 1->2, then 2->3
if (self.energy === currentEnergyLevel && currentEnergyLevel < self.maxEnergy) {
self.energy = currentEnergyLevel + 1;
self.lastEnergyRegenTime = LK.ticks; // Update last regeneration time
}
}
// Handle jumping and gravity physics
if (self.isJumping) {
// Apply gravity to vertical velocity
self.velocityY += self.gravity;
// Update position based on velocity
self.y += self.velocityY;
// Check if landed on ground
if (self.y >= self.groundY) {
self.y = self.groundY;
self.isJumping = false;
self.velocityY = 0;
}
} else {
// Ensure fighter stays on ground when not jumping
if (self.y > self.groundY) {
self.y = self.groundY;
}
}
// Handle blocking visual effect
if (self.isBlocking) {
// Show blocking shield
if (!blockingShield.visible) {
blockingShield.visible = true;
blockingShield.alpha = 0.7;
}
// Position shield to always point toward opponent
if (player && enemy) {
var opponent = self === player ? enemy : player;
// Shield always points toward opponent regardless of fighter facing direction
if (opponent.x > self.x) {
blockingShield.x = 100; // Shield on right side (facing right toward opponent)
blockingShield.scaleX = Math.abs(blockingShield.scaleX); // Face right
} else {
blockingShield.x = -100; // Shield on left side (facing left toward opponent)
blockingShield.scaleX = -Math.abs(blockingShield.scaleX); // Face left
}
}
} else {
// Hide blocking shield
if (blockingShield.visible) {
blockingShield.visible = false;
blockingShield.alpha = 0.0;
}
}
// Handle crouching visual effect
if (self.isCrouching && !self.isJumping) {
// Scale down vertically when crouching - slightly bigger hitbox than before
if (fighterGraphics.scaleY > 0.25) {
fighterGraphics.scaleY -= 0.05;
}
} else {
// Return to normal scale
if (fighterGraphics.scaleY < 1.0) {
fighterGraphics.scaleY += 0.05;
}
}
};
return self;
});
var HealthBar = Container.expand(function (fighter, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.fighter = fighter;
var bgBar = self.attachAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
});
var healthBar = self.attachAsset('healthBar', {
anchorX: 0,
anchorY: 0
});
self.update = function () {
var healthPercent = self.fighter.health / self.fighter.maxHealth;
healthBar.width = 1000 * healthPercent;
};
return self;
});
var Projectile = Container.expand(function (x, y, direction, damage, owner) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.direction = direction; // 1 for right, -1 for left
self.damage = damage;
self.owner = owner;
self.speed = 8;
var projectileAsset = 'projectile' + owner.fighterType;
var projectileGraphics = self.attachAsset(projectileAsset, {
anchorX: 0.5,
anchorY: 0.5
});
// Set projectile direction to match shoot direction
if (direction < 0) {
projectileGraphics.scaleX = -Math.abs(projectileGraphics.scaleX); // Face left
} else {
projectileGraphics.scaleX = Math.abs(projectileGraphics.scaleX); // Face right
}
self.update = function () {
self.x += self.speed * self.direction;
};
return self;
});
var SpecialBar = Container.expand(function (fighter, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.fighter = fighter;
var bgBar = self.attachAsset('specialBarBg', {
anchorX: 0,
anchorY: 0
});
var specialBar = self.attachAsset('specialBar', {
anchorX: 0,
anchorY: 0
});
self.update = function () {
var specialPercent = self.fighter.specialCharge / self.fighter.maxSpecialCharge;
specialBar.width = 1000 * specialPercent;
};
return self;
});
var SuperProjectile = Container.expand(function (x, y, direction, damage, owner) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.direction = direction; // 1 for right, -1 for left
self.damage = damage;
self.owner = owner;
self.speed = 6;
self.isSuperProjectile = true;
var superProjectileAsset = 'superProjectile' + owner.fighterType;
var superProjectileGraphics = self.attachAsset(superProjectileAsset, {
anchorX: 0.5,
anchorY: 0.5
});
// Set super projectile direction to match shoot direction
if (direction < 0) {
superProjectileGraphics.scaleX = -Math.abs(superProjectileGraphics.scaleX); // Face left
} else {
superProjectileGraphics.scaleX = Math.abs(superProjectileGraphics.scaleX); // Face right
}
self.update = function () {
self.x += self.speed * self.direction;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2C1810
});
/****
* Game Code
****/
// Add background with random selection for fights - adjusted for horizontal gameplay
var currentBackgroundAsset = 'background'; // Default background
var gameBackground = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1.0,
scaleY: 1.0
});
game.addChild(gameBackground);
// Add menu background (initially visible)
var menuBackground = LK.getAsset('menuBackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(menuBackground);
// Sounds
// Combat elements
// UI elements
// Character assets
// Game states
// Animation assets for different actions
var gameState = 'titleScreen'; // 'titleScreen', 'characterSelect', 'playerSelect', 'teamSelect', 'combat', 'gameOver'
var selectedCharacter = 0; // Initialize to 0 to require selection
var enemyCharacter = 2;
var gameMode = 'normal'; // 'normal', 'playerVsPlayer', 'teamBattle'
var gameDifficulty = ''; // Initialize to empty to require selection
var playerTeam = [];
var enemyTeam = [];
var currentPlayerSelection = 1;
var currentEnemySelection = 1;
var teamSize = 2;
var selectedTeamIndex = 0;
var titleButtons = [];
// Character selection
var characterSelectors = [];
var selectionBorder = null;
// Combat variables
var player = null;
var enemy = null;
var playerHealthBar = null;
var playerEnergyBar = null;
var playerSpecialBar = null;
var enemyHealthBar = null;
var enemyEnergyBar = null;
var enemySpecialBar = null;
var projectiles = [];
var superProjectiles = [];
var punchButton = null;
var kickButton = null;
var rangeButton = null;
var blockButton = null;
var specialButton = null;
var playerBlockStartTime = 0;
var playerIsBlocking = false;
var punchButtonText = null;
var kickButtonText = null;
var rangeButtonText = null;
var blockButtonText = null;
var specialButtonText = null;
var leftMoveButton = null;
var rightMoveButton = null;
var leftMoveButtonText = null;
var rightMoveButtonText = null;
var crouchButton = null;
var jumpButton = null;
var crouchButtonText = null;
var jumpButtonText = null;
var isMovingLeft = false;
var isMovingRight = false;
var enemyIsMovingLeft = false;
var enemyIsMovingRight = false;
var enemyMovementTimer = 0;
var currentPlayerTeamIndex = 0;
var currentEnemyTeamIndex = 0;
var teamPlayerDisplays = [];
var teamEnemyDisplays = [];
var lastPlayerDefeatCheck = -1;
var lastEnemyDefeatCheck = -1;
// UI elements
var titleText = new Text2('AI.nimal Brawlers', {
size: 60,
fill: 0xff0000
});
titleText.anchor.set(0.5, 0);
LK.gui.top.addChild(titleText);
titleText.y = 30;
var instructionText = new Text2('Select Your Fighter', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
// Combat controls text
var controlsText = new Text2('Left: Move | Right: Move | Center Left: Punch | Center Right: Kick | Top: Block | Bottom: Special', {
size: 30,
fill: 0xFFFFFF
});
controlsText.anchor.set(0.5, 1);
function initTitleScreen() {
// Show menu background, hide game background
if (menuBackground) menuBackground.visible = true;
if (gameBackground) gameBackground.visible = false;
// Start menu music
LK.playMusic('menuMusic', {
loop: true
});
titleText.setText('AI.nimal Brawlers');
titleText.y = 200;
// Create title screen buttons
var normalButton = LK.getAsset('playNormalButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalButton.x = 1024;
normalButton.y = 800;
normalButton.buttonType = 'normal';
game.addChild(normalButton);
titleButtons.push(normalButton);
var normalButtonText = new Text2('PLAY NORMAL', {
size: 30,
fill: 0xFFFFFF
});
normalButtonText.anchor.set(0.5, 0.5);
normalButtonText.x = normalButton.x;
normalButtonText.y = normalButton.y;
game.addChild(normalButtonText);
var selectButton = LK.getAsset('selectFightersButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
selectButton.x = 1024;
selectButton.y = 1000;
selectButton.buttonType = 'select';
game.addChild(selectButton);
titleButtons.push(selectButton);
var selectButtonText = new Text2('SELECT FIGHTERS', {
size: 30,
fill: 0xFFFFFF
});
selectButtonText.anchor.set(0.5, 0.5);
selectButtonText.x = selectButton.x;
selectButtonText.y = selectButton.y;
game.addChild(selectButtonText);
var teamButton = LK.getAsset('teamBattleButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
teamButton.x = 1024;
teamButton.y = 1200;
teamButton.buttonType = 'team';
game.addChild(teamButton);
titleButtons.push(teamButton);
var teamButtonText = new Text2('TEAM BATTLE', {
size: 30,
fill: 0xFFFFFF
});
teamButtonText.anchor.set(0.5, 0.5);
teamButtonText.x = teamButton.x;
teamButtonText.y = teamButton.y;
game.addChild(teamButtonText);
var instructionsText = new Text2('Choose your game mode', {
size: 40,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 1024;
instructionsText.y = 500;
game.addChild(instructionsText);
}
function clearTitleScreen() {
for (var i = 0; i < titleButtons.length; i++) {
game.removeChild(titleButtons[i]);
}
titleButtons = [];
// Remove all text elements by checking children
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof Text2) {
game.removeChild(child);
}
}
}
function initPlayerSelection() {
instructionText.setText('Player 1: Select Fighter');
instructionText.y = 150;
game.addChild(instructionText);
// Create character selection grid
for (var i = 1; i <= 5; i++) {
var fighter = LK.getAsset('fighter' + i, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var col = (i - 1) % 5;
var row = Math.floor((i - 1) / 5);
fighter.x = 300 + col * 280;
fighter.y = 400 + row * 300;
fighter.fighterIndex = i;
characterSelectors.push(fighter);
game.addChild(fighter);
}
// Create selection border
selectionBorder = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionBorder.alpha = 0.3;
updateSelectionBorder();
game.addChild(selectionBorder);
// Add difficulty selection for player vs player mode
// Add difficulty selection text
var difficultyText = new Text2('Select Difficulty:', {
size: 35,
fill: 0xFFFFFF
});
difficultyText.anchor.set(0.5, 0.5);
difficultyText.x = 1024;
difficultyText.y = 1800;
game.addChild(difficultyText);
// Easy difficulty button
var easyButton = LK.getAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
easyButton.x = 600;
easyButton.y = 2400;
easyButton.buttonType = 'easy';
if (gameDifficulty === 'easy') {
easyButton.tint = 0xff9500; // Lighter orange when selected
}
game.addChild(easyButton);
titleButtons.push(easyButton);
var easyButtonText = new Text2('EASY', {
size: 30,
fill: 0xFFFFFF
});
easyButtonText.anchor.set(0.5, 0.5);
easyButtonText.x = easyButton.x;
easyButtonText.y = easyButton.y;
game.addChild(easyButtonText);
// Normal difficulty button
var normalDiffButton = LK.getAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalDiffButton.x = 1024;
normalDiffButton.y = 2400;
normalDiffButton.buttonType = 'normalDiff';
game.addChild(normalDiffButton);
titleButtons.push(normalDiffButton);
var normalDiffButtonText = new Text2('NORMAL', {
size: 30,
fill: 0xFFFFFF
});
normalDiffButtonText.anchor.set(0.5, 0.5);
normalDiffButtonText.x = normalDiffButton.x;
normalDiffButtonText.y = normalDiffButton.y;
game.addChild(normalDiffButtonText);
// Hard difficulty button
var hardButton = LK.getAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
hardButton.x = 1448;
hardButton.y = 2400;
hardButton.buttonType = 'hard';
game.addChild(hardButton);
titleButtons.push(hardButton);
var hardButtonText = new Text2('HARD', {
size: 30,
fill: 0xFFFFFF
});
hardButtonText.anchor.set(0.5, 0.5);
hardButtonText.x = hardButton.x;
hardButtonText.y = hardButton.y;
game.addChild(hardButtonText);
// Add start combat button
var startCombatButton = LK.getAsset('startCombatButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
startCombatButton.x = 1024;
startCombatButton.y = 2300;
startCombatButton.buttonType = 'startCombat';
game.addChild(startCombatButton);
titleButtons.push(startCombatButton);
var startCombatButtonText = new Text2('START COMBAT', {
size: 30,
fill: 0xFFFFFF
});
startCombatButtonText.anchor.set(0.5, 0.5);
startCombatButtonText.x = startCombatButton.x;
startCombatButtonText.y = startCombatButton.y;
game.addChild(startCombatButtonText);
// Add back to menu button
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
backButton.x = 1024;
backButton.y = 2600;
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
titleButtons.push(backButton);
var backButtonText = new Text2('BACK TO MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
}
function initTeamSelection() {
instructionText.setText('Select Team Size: ' + teamSize + ' vs ' + teamSize);
instructionText.y = 100;
game.addChild(instructionText);
// Team size buttons
var size2Button = LK.getAsset('team2v2Button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
size2Button.x = 400;
size2Button.y = 200;
size2Button.buttonType = 'size2';
if (teamSize === 2) {
size2Button.tint = 0x44ff44; // Green when selected
}
game.addChild(size2Button);
titleButtons.push(size2Button);
var size2Text = new Text2('2 vs 2', {
size: 30,
fill: 0xFFFFFF
});
size2Text.anchor.set(0.5, 0.5);
size2Text.x = size2Button.x;
size2Text.y = size2Button.y;
game.addChild(size2Text);
var size3Button = LK.getAsset('team3v3Button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
size3Button.x = 800;
size3Button.y = 200;
size3Button.buttonType = 'size3';
if (teamSize === 3) {
size3Button.tint = 0x44ff44; // Green when selected
}
game.addChild(size3Button);
titleButtons.push(size3Button);
var size3Text = new Text2('3 vs 3', {
size: 30,
fill: 0xFFFFFF
});
size3Text.anchor.set(0.5, 0.5);
size3Text.x = size3Button.x;
size3Text.y = size3Button.y;
game.addChild(size3Text);
// Add back to menu button
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
backButton.x = 1024;
backButton.y = 1900;
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
titleButtons.push(backButton);
var backButtonText = new Text2('BACK TO MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
var startButton = LK.getAsset('startTeamSelectionButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
startButton.x = 1024;
startButton.y = 1700;
startButton.buttonType = 'startTeam';
game.addChild(startButton);
titleButtons.push(startButton);
var startText = new Text2('START TEAM SELECTION', {
size: 30,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = startButton.x;
startText.y = startButton.y;
game.addChild(startText);
// Show current teams
updateTeamDisplay();
}
function initTeamFighterSelection() {
// Clear previous state
selectedTeamIndex = 0;
var currentTeam = selectedTeamIndex < teamSize ? 'player' : 'enemy';
var fighterNumber = selectedTeamIndex % teamSize + 1;
var teamName = currentTeam === 'player' ? 'Player' : 'Enemy';
instructionText.setText(teamName + ' Team - Select Fighter ' + fighterNumber);
instructionText.y = 150;
game.addChild(instructionText);
// Create character selection grid
for (var i = 1; i <= 5; i++) {
var fighter = LK.getAsset('fighter' + i, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var col = (i - 1) % 5;
var row = Math.floor((i - 1) / 5);
fighter.x = 300 + col * 280;
fighter.y = 600 + row * 300;
fighter.fighterIndex = i;
characterSelectors.push(fighter);
game.addChild(fighter);
}
// Create selection border
selectionBorder = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionBorder.alpha = 0.3;
selectionBorder.x = characterSelectors[0].x;
selectionBorder.y = characterSelectors[0].y;
game.addChild(selectionBorder);
// Show current team selections below the character grid
updateTeamBattleDisplay();
// Add start combat button
var startCombatButton = LK.getAsset('startCombatButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
startCombatButton.x = 1024;
startCombatButton.y = 1700;
startCombatButton.buttonType = 'startCombat';
game.addChild(startCombatButton);
titleButtons.push(startCombatButton);
var startCombatButtonText = new Text2('START COMBAT', {
size: 30,
fill: 0xFFFFFF
});
startCombatButtonText.anchor.set(0.5, 0.5);
startCombatButtonText.x = startCombatButton.x;
startCombatButtonText.y = startCombatButton.y;
game.addChild(startCombatButtonText);
// Add next selection button
var nextButton = LK.getAsset('rightArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
nextButton.x = 1300;
nextButton.y = 1400;
nextButton.buttonType = 'nextSelection';
game.addChild(nextButton);
titleButtons.push(nextButton);
var nextButtonText = new Text2('NEXT', {
size: 30,
fill: 0xFFFFFF
});
nextButtonText.anchor.set(0.5, 0.5);
nextButtonText.x = nextButton.x;
nextButtonText.y = nextButton.y;
game.addChild(nextButtonText);
// Add previous selection button
var prevButton = LK.getAsset('leftArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
prevButton.x = 700;
prevButton.y = 1400;
prevButton.buttonType = 'prevSelection';
game.addChild(prevButton);
titleButtons.push(prevButton);
var prevButtonText = new Text2('PREV', {
size: 30,
fill: 0xFFFFFF
});
prevButtonText.anchor.set(0.5, 0.5);
prevButtonText.x = prevButton.x;
prevButtonText.y = prevButton.y;
game.addChild(prevButtonText);
// Add back to menu button
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
backButton.x = 1024;
backButton.y = 2100;
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
titleButtons.push(backButton);
var backButtonText = new Text2('BACK TO MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
// Add difficulty selection for team battle mode below back button
// Easy difficulty button
var easyButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
easyButton.x = 600;
easyButton.y = 1900;
easyButton.buttonType = 'easy';
if (gameDifficulty === 'easy') {
easyButton.tint = 0xff9500; // Lighter orange when selected
}
game.addChild(easyButton);
titleButtons.push(easyButton);
var easyButtonText = new Text2('EASY', {
size: 30,
fill: 0xFFFFFF
});
easyButtonText.anchor.set(0.5, 0.5);
easyButtonText.x = easyButton.x;
easyButtonText.y = easyButton.y;
game.addChild(easyButtonText);
// Normal difficulty button
var normalDiffButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalDiffButton.x = 1024;
normalDiffButton.y = 1900;
normalDiffButton.buttonType = 'normalDiff';
if (gameDifficulty === 'normal') {
normalDiffButton.tint = 0xcc6600; // Darker orange when selected
}
game.addChild(normalDiffButton);
titleButtons.push(normalDiffButton);
var normalDiffButtonText = new Text2('NORMAL', {
size: 30,
fill: 0xFFFFFF
});
normalDiffButtonText.anchor.set(0.5, 0.5);
normalDiffButtonText.x = normalDiffButton.x;
normalDiffButtonText.y = normalDiffButton.y;
game.addChild(normalDiffButtonText);
// Hard difficulty button
var hardButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
hardButton.x = 1448;
hardButton.y = 1900;
hardButton.buttonType = 'hard';
if (gameDifficulty === 'hard') {
hardButton.tint = 0xff0000; // Red when selected
}
game.addChild(hardButton);
titleButtons.push(hardButton);
var hardButtonText = new Text2('HARD', {
size: 30,
fill: 0xFFFFFF
});
hardButtonText.anchor.set(0.5, 0.5);
hardButtonText.x = hardButton.x;
hardButtonText.y = hardButton.y;
game.addChild(hardButtonText);
}
function updateTeamBattleDisplay() {
// Remove existing team battle display
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.isTeamBattleDisplay) {
game.removeChild(child);
}
}
// Player team display
var playerTeamText = new Text2('Player Team:', {
size: 35,
fill: 0x44ff44
});
playerTeamText.anchor.set(0.5, 0);
playerTeamText.x = 500;
playerTeamText.y = 900;
playerTeamText.isTeamBattleDisplay = true;
game.addChild(playerTeamText);
for (var i = 0; i < teamSize; i++) {
var fighterType = playerTeam[i] || 1;
var teamFighter = LK.getAsset('fighter' + fighterType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.35,
scaleY: 0.35
});
teamFighter.x = 250 + i * 120;
teamFighter.y = 1100;
teamFighter.isTeamBattleDisplay = true;
// Add selection indicator for current selection
if (selectedTeamIndex === i) {
var selectionIndicator = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionIndicator.alpha = 0.6;
selectionIndicator.tint = 0x00ff00;
selectionIndicator.x = teamFighter.x;
selectionIndicator.y = teamFighter.y;
selectionIndicator.scaleX = 0.4;
selectionIndicator.scaleY = 0.4;
selectionIndicator.isTeamBattleDisplay = true;
game.addChild(selectionIndicator);
}
game.addChild(teamFighter);
}
// Enemy team display
var enemyTeamText = new Text2('Enemy Team:', {
size: 35,
fill: 0xff4444
});
enemyTeamText.anchor.set(0.5, 0);
enemyTeamText.x = 1500;
enemyTeamText.y = 900;
enemyTeamText.isTeamBattleDisplay = true;
game.addChild(enemyTeamText);
for (var i = 0; i < teamSize; i++) {
var fighterType = enemyTeam[i] || (i + 1) % 5 + 1;
var teamFighter = LK.getAsset('fighter' + fighterType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.35,
scaleY: 0.35
});
teamFighter.x = 1250 + i * 120;
teamFighter.y = 1100;
teamFighter.isTeamBattleDisplay = true;
// Add selection indicator for current selection
if (selectedTeamIndex === teamSize + i) {
var selectionIndicator = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionIndicator.alpha = 0.6;
selectionIndicator.tint = 0xff0000;
selectionIndicator.x = teamFighter.x;
selectionIndicator.y = teamFighter.y;
selectionIndicator.scaleX = 0.4;
selectionIndicator.scaleY = 0.4;
selectionIndicator.isTeamBattleDisplay = true;
game.addChild(selectionIndicator);
}
game.addChild(teamFighter);
}
}
function updateTeamDisplay() {
// Remove existing team display
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.isTeamDisplay) {
game.removeChild(child);
}
}
// Player team display
var playerTeamText = new Text2('Player Team:', {
size: 40,
fill: 0x44ff44
});
playerTeamText.anchor.set(0.5, 0);
playerTeamText.x = 500;
playerTeamText.y = 400;
playerTeamText.isTeamDisplay = true;
game.addChild(playerTeamText);
for (var i = 0; i < teamSize; i++) {
var fighterType = playerTeam[i] || i + 1;
var teamFighter = LK.getAsset('fighter' + fighterType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
teamFighter.x = 300 + i * 150;
teamFighter.y = 500;
teamFighter.isTeamDisplay = true;
game.addChild(teamFighter);
}
// Enemy team display
var enemyTeamText = new Text2('Enemy Team:', {
size: 40,
fill: 0xff4444
});
enemyTeamText.anchor.set(0.5, 0);
enemyTeamText.x = 1500;
enemyTeamText.y = 400;
enemyTeamText.isTeamDisplay = true;
game.addChild(enemyTeamText);
for (var i = 0; i < teamSize; i++) {
var fighterType = enemyTeam[i] || (i + 2) % 5 + 1;
var teamFighter = LK.getAsset('fighter' + fighterType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
teamFighter.x = 1300 + i * 150;
teamFighter.y = 500;
teamFighter.isTeamDisplay = true;
game.addChild(teamFighter);
}
}
function initCharacterSelection() {
instructionText.y = 150;
game.addChild(instructionText);
// Create character selection grid
for (var i = 1; i <= 5; i++) {
var fighter = LK.getAsset('fighter' + i, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var col = (i - 1) % 5;
var row = Math.floor((i - 1) / 5);
fighter.x = 300 + col * 280;
fighter.y = 400 + row * 300;
fighter.fighterIndex = i;
characterSelectors.push(fighter);
game.addChild(fighter);
}
// Create selection border
selectionBorder = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionBorder.alpha = 0.3;
updateSelectionBorder();
game.addChild(selectionBorder);
// Add difficulty selection if in normal mode
if (gameMode === 'normal') {
// Add difficulty selection text
var difficultyText = new Text2('Select Difficulty:', {
size: 35,
fill: 0xFFFFFF
});
difficultyText.anchor.set(0.5, 0.5);
difficultyText.x = 1024;
difficultyText.y = 700;
game.addChild(difficultyText);
// Easy difficulty button
var easyButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
easyButton.x = 600;
easyButton.y = 2000;
easyButton.buttonType = 'easy';
if (gameDifficulty === 'easy') {
easyButton.tint = 0xff9500; // Lighter orange when selected
}
game.addChild(easyButton);
titleButtons.push(easyButton);
var easyButtonText = new Text2('EASY', {
size: 30,
fill: 0xFFFFFF
});
easyButtonText.anchor.set(0.5, 0.5);
easyButtonText.x = easyButton.x;
easyButtonText.y = easyButton.y;
game.addChild(easyButtonText);
// Normal difficulty button
var normalDiffButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalDiffButton.x = 1024;
normalDiffButton.y = 2000;
normalDiffButton.buttonType = 'normalDiff';
game.addChild(normalDiffButton);
titleButtons.push(normalDiffButton);
var normalDiffButtonText = new Text2('NORMAL', {
size: 30,
fill: 0xFFFFFF
});
normalDiffButtonText.anchor.set(0.5, 0.5);
normalDiffButtonText.x = normalDiffButton.x;
normalDiffButtonText.y = normalDiffButton.y;
game.addChild(normalDiffButtonText);
// Hard difficulty button
var hardButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
hardButton.x = 1448;
hardButton.y = 2000;
hardButton.buttonType = 'hard';
game.addChild(hardButton);
titleButtons.push(hardButton);
var hardButtonText = new Text2('HARD', {
size: 30,
fill: 0xFFFFFF
});
hardButtonText.anchor.set(0.5, 0.5);
hardButtonText.x = hardButton.x;
hardButtonText.y = hardButton.y;
game.addChild(hardButtonText);
}
// Add start combat button
var startCombatButton = LK.getAsset('startCombatButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
startCombatButton.x = 1024;
startCombatButton.y = gameMode === 'normal' ? 1850 : 1800;
startCombatButton.buttonType = 'startCombat';
game.addChild(startCombatButton);
titleButtons.push(startCombatButton);
var startCombatButtonText = new Text2('START COMBAT', {
size: 30,
fill: 0xFFFFFF
});
startCombatButtonText.anchor.set(0.5, 0.5);
startCombatButtonText.x = startCombatButton.x;
startCombatButtonText.y = startCombatButton.y;
game.addChild(startCombatButtonText);
// Add back to menu button
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
backButton.x = 1024;
backButton.y = gameMode === 'normal' ? 2200 : 2000;
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
titleButtons.push(backButton);
var backButtonText = new Text2('BACK TO MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
}
function updateSelectionBorder() {
if (selectionBorder && characterSelectors[selectedCharacter - 1]) {
selectionBorder.x = characterSelectors[selectedCharacter - 1].x;
selectionBorder.y = characterSelectors[selectedCharacter - 1].y;
}
}
function startCombat() {
gameState = 'combat';
// Stop menu music when entering combat
LK.stopMusic();
// Start combat music
LK.playMusic('combatMusic', {
loop: true
});
// Randomly select background for this fight
var backgroundOptions = ['background', 'background2', 'background3'];
var randomBackgroundIndex = Math.floor(Math.random() * backgroundOptions.length);
var selectedBackground = backgroundOptions[randomBackgroundIndex];
currentBackgroundAsset = selectedBackground;
// Remove current game background and create new one with selected background
if (gameBackground) {
game.removeChild(gameBackground);
}
gameBackground = LK.getAsset(selectedBackground, {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1.33,
scaleY: 0.75
});
game.addChild(gameBackground);
// Show game background, hide menu background
if (menuBackground) menuBackground.visible = false;
if (gameBackground) gameBackground.visible = true;
// Clear character selection
game.removeChild(instructionText);
for (var i = 0; i < characterSelectors.length; i++) {
game.removeChild(characterSelectors[i]);
}
game.removeChild(selectionBorder);
// Clear title screen and team displays when starting combat
titleText.visible = false;
clearTitleScreen();
// Remove team battle displays
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.isTeamDisplay || child.isTeamBattleDisplay) {
game.removeChild(child);
}
}
// Reset defeat tracking
lastPlayerDefeatCheck = -1;
lastEnemyDefeatCheck = -1;
// Create fighters - positioned for horizontal gameplay
player = game.addChild(new Fighter(selectedCharacter, true));
player.x = 400;
player.y = 1800;
enemy = game.addChild(new Fighter(enemyCharacter, false));
enemy.x = 1648;
enemy.y = 1800;
// Create health and energy bars - repositioned for horizontal layout
playerHealthBar = game.addChild(new HealthBar(player, -25, 50));
playerEnergyBar = game.addChild(new EnergyBar(player, -25, 90));
playerSpecialBar = game.addChild(new SpecialBar(player, -25, 130));
enemyHealthBar = game.addChild(new HealthBar(enemy, 1100, 50));
enemyEnergyBar = game.addChild(new EnergyBar(enemy, 1100, 90));
enemySpecialBar = game.addChild(new SpecialBar(enemy, 1100, 130));
// Create combat control buttons arranged for horizontal mobile layout (moved to left side)
punchButton = LK.getAsset('punchButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8
});
punchButton.x = 220;
punchButton.y = 2200;
game.addChild(punchButton);
kickButton = LK.getAsset('kickButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 2.1
});
kickButton.x = 600;
kickButton.y = 2200;
game.addChild(kickButton);
blockButton = LK.getAsset('blockButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 2.1
});
blockButton.x = 980;
blockButton.y = 2200;
game.addChild(blockButton);
rangeButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 2.1
});
rangeButton.x = 220;
rangeButton.y = 2400;
game.addChild(rangeButton);
specialButton = LK.getAsset('specialButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 2.1
});
specialButton.x = 600;
specialButton.y = 2400;
game.addChild(specialButton);
// Create movement buttons arranged for horizontal layout (moved to right side)
leftMoveButton = LK.getAsset('leftArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.9,
scaleY: 1.9
});
leftMoveButton.x = 1550;
leftMoveButton.y = 2400;
game.addChild(leftMoveButton);
rightMoveButton = LK.getAsset('rightArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.9,
scaleY: 1.9
});
rightMoveButton.x = 1850;
rightMoveButton.y = 2400;
game.addChild(rightMoveButton);
// Jump button positioned above movement buttons
jumpButton = LK.getAsset('leftArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.9,
scaleY: 1.9
});
jumpButton.x = 1700;
jumpButton.y = 2150;
game.addChild(jumpButton);
// Crouch button positioned below movement buttons
crouchButton = LK.getAsset('leftArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.9,
scaleY: 1.9
});
crouchButton.x = 1700;
crouchButton.y = 2650;
game.addChild(crouchButton);
// Add button labels
punchButtonText = new Text2('PUNCH', {
size: 30,
fill: 0xFFFFFF
});
punchButtonText.anchor.set(0.5, 0.5);
punchButtonText.x = punchButton.x;
punchButtonText.y = punchButton.y;
game.addChild(punchButtonText);
kickButtonText = new Text2('KICK', {
size: 30,
fill: 0xFFFFFF
});
kickButtonText.anchor.set(0.5, 0.5);
kickButtonText.x = kickButton.x;
kickButtonText.y = kickButton.y;
game.addChild(kickButtonText);
rangeButtonText = new Text2('RANGE', {
size: 30,
fill: 0xFFFFFF
});
rangeButtonText.anchor.set(0.5, 0.5);
rangeButtonText.x = rangeButton.x;
rangeButtonText.y = rangeButton.y;
game.addChild(rangeButtonText);
blockButtonText = new Text2('BLOCK', {
size: 30,
fill: 0xFFFFFF
});
blockButtonText.anchor.set(0.5, 0.5);
blockButtonText.x = blockButton.x;
blockButtonText.y = blockButton.y;
game.addChild(blockButtonText);
specialButtonText = new Text2('SPECIAL', {
size: 30,
fill: 0xFFFFFF
});
specialButtonText.anchor.set(0.5, 0.5);
specialButtonText.x = specialButton.x;
specialButtonText.y = specialButton.y;
game.addChild(specialButtonText);
leftMoveButtonText = new Text2('←', {
size: 60,
fill: 0xFFFFFF
});
leftMoveButtonText.anchor.set(0.5, 0.5);
leftMoveButtonText.x = leftMoveButton.x;
leftMoveButtonText.y = leftMoveButton.y;
game.addChild(leftMoveButtonText);
rightMoveButtonText = new Text2('→', {
size: 60,
fill: 0xFFFFFF
});
rightMoveButtonText.anchor.set(0.5, 0.5);
rightMoveButtonText.x = rightMoveButton.x;
rightMoveButtonText.y = rightMoveButton.y;
game.addChild(rightMoveButtonText);
crouchButtonText = new Text2('↓', {
size: 60,
fill: 0xFFFFFF
});
crouchButtonText.anchor.set(0.5, 0.5);
crouchButtonText.x = crouchButton.x;
crouchButtonText.y = crouchButton.y;
game.addChild(crouchButtonText);
jumpButtonText = new Text2('↑', {
size: 60,
fill: 0xFFFFFF
});
jumpButtonText.anchor.set(0.5, 0.5);
jumpButtonText.x = jumpButton.x;
jumpButtonText.y = jumpButton.y;
game.addChild(jumpButtonText);
// Add back to menu button positioned for horizontal layout
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5
});
backButton.x = 1024; // Top center of screen
backButton.y = 50; // Top of screen
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
var backButtonText = new Text2('MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
// Add controls text positioned for horizontal layout
controlsText.setText('Left side: Combat actions | Right side: Move & Jump/Crouch');
controlsText.y = 2700;
game.addChild(controlsText);
// Add player and enemy name labels positioned for horizontal layout
var playerLabel = new Text2('Player', {
size: 40,
fill: 0xFFFFFF
});
playerLabel.anchor.set(0, 0);
playerLabel.x = 50;
playerLabel.y = 20;
game.addChild(playerLabel);
var enemyLabel = new Text2('Enemy', {
size: 40,
fill: 0xFFFFFF
});
enemyLabel.anchor.set(1, 0);
enemyLabel.x = 1998;
enemyLabel.y = 20;
game.addChild(enemyLabel);
// Add team displays if in team battle mode
if (gameMode === 'teamBattle') {
// Player team display positioned for horizontal layout
for (var i = 0; i < playerTeam.length; i++) {
var baseScale = 0.3;
var isCurrentFighter = i === currentPlayerTeamIndex;
var teamFighter = LK.getAsset('fighter' + playerTeam[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: isCurrentFighter ? baseScale * 1.2 : baseScale,
scaleY: isCurrentFighter ? baseScale * 1.2 : baseScale
});
teamFighter.x = 100 + i * 60;
teamFighter.y = 200;
teamFighter.teamIndex = i;
teamFighter.isTeamDisplay = true;
teamFighter.tint = 0xffffff; // Normal color for all fighters initially
teamPlayerDisplays.push(teamFighter);
game.addChild(teamFighter);
}
// Enemy team display positioned for horizontal layout
for (var i = 0; i < enemyTeam.length; i++) {
var baseScale = 0.3;
var isCurrentFighter = i === currentEnemyTeamIndex;
var teamFighter = LK.getAsset('fighter' + enemyTeam[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: isCurrentFighter ? baseScale * 1.2 : baseScale,
scaleY: isCurrentFighter ? baseScale * 1.2 : baseScale
});
teamFighter.x = 1600 + i * 60;
teamFighter.y = 200;
teamFighter.teamIndex = i;
teamFighter.isTeamDisplay = true;
teamFighter.tint = 0xffffff; // Normal color for all fighters initially
teamEnemyDisplays.push(teamFighter);
game.addChild(teamFighter);
}
}
}
function handleCombatInput(x, y) {
if (!player || gameState !== 'combat') return;
var gameWidth = 2048;
var gameHeight = 2732;
// Check button presses first
if (punchButton && Math.abs(x - punchButton.x) < 180 && Math.abs(y - punchButton.y) < 72) {
// Punch button pressed
if (player.attack('punch')) {
player.setAnimation('punching', 20); // 20 ticks punch animation
var hit = checkMeleeHit(player, enemy, Math.floor(player.attackDamage * 0.75), 'punch');
if (hit) player.chargeSpecial(15); // Charge special on successful hit
LK.getSound('punchSound').play();
}
return;
}
if (kickButton && Math.abs(x - kickButton.x) < 210 && Math.abs(y - kickButton.y) < 84) {
// Kick button pressed - can't kick while crouching
if (!player.isCrouching && player.attack('kick')) {
player.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
var hit = checkMeleeHit(player, enemy, Math.floor((player.attackDamage + 1) * 0.75), 'kick');
if (hit) player.chargeSpecial(15); // Charge special on successful hit
LK.getSound('kickSound').play();
}
return;
}
if (rangeButton && Math.abs(x - rangeButton.x) < 208 && Math.abs(y - rangeButton.y) < 83) {
// Range attack button pressed
if (enemy && player.specialAttack('range')) {
player.setAnimation('rangeShooting', 15); // 15 ticks range shooting animation
// Determine shoot direction based on enemy position
var shootDirection = enemy.x > player.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust projectile origin based on player stance
var projectileY = player.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (player.isCrouching && !player.isJumping) {
projectileY = player.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (player.isJumping) {
projectileY = player.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
var projectile = new Projectile(player.x + projectileXOffset, projectileY, shootDirection, player.specialDamage, player);
projectiles.push(projectile);
game.addChild(projectile);
player.chargeSpecial(7); // Charge special at 50% rate (15 * 0.5 = 7.5, rounded to 7)
LK.getSound('rangeShoot').play();
}
return;
}
if (specialButton && Math.abs(x - specialButton.x) < 210 && Math.abs(y - specialButton.y) < 84) {
// Special button pressed
if (enemy && player.useSpecialAttack()) {
player.lastSpecialType = 'potent';
// Start charging animation before firing
tween(player, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffaa00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Set potent shooting animation
player.setAnimation('potentShooting', 50); // 50 ticks potent shooting animation - extended duration
// Return to normal size and color after charging
tween(player, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
// Determine shoot direction based on enemy position
var shootDirection = enemy.x > player.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on player stance
var superProjectileY = player.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (player.isCrouching && !player.isJumping) {
superProjectileY = player.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (player.isJumping) {
superProjectileY = player.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
// Create 7 super projectiles fired with spacing
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
var projectileX = player.x + projectileXOffset + (index - 3) * 60; // Horizontal spread in a row
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(player.specialDamage * 2 / 7), player);
// Increase speed for potent projectiles
superProjectile.speed = 8; // Increased speed to match regular projectiles
// Add laser effects for fighter 3
if (player.fighterType === 3) {
// Initial bright laser effect
tween(superProjectile, {
tint: 0xff0000
}, {
duration: 50
});
// Pulsing laser effect
tween(superProjectile, {
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(superProjectile, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// No pushback when firing potent projectiles
// Sound will be played with each projectile instead
}
});
}
return;
}
if (blockButton && Math.abs(x - blockButton.x) < 420 && Math.abs(y - blockButton.y) < 525) {
// Block button pressed
if (!playerIsBlocking) {
playerBlockStartTime = LK.ticks;
playerIsBlocking = true;
}
player.isBlocking = true;
LK.getSound('block').play();
return;
}
if (leftMoveButton && Math.abs(x - leftMoveButton.x) < 142 && Math.abs(y - leftMoveButton.y) < 142) {
// Left movement button pressed
if (!playerIsBlocking && player.x > 200) {
player.stopCrouch(); // Stop crouching when moving left
player.x -= player.speed * 10;
if (player.currentAnimation === 'normal') {
player.setAnimation('walking');
}
}
return;
}
if (rightMoveButton && Math.abs(x - rightMoveButton.x) < 142 && Math.abs(y - rightMoveButton.y) < 142) {
// Right movement button pressed
if (!playerIsBlocking && player.x < 2532) {
player.stopCrouch(); // Stop crouching when moving right
player.x += player.speed * 10;
if (player.currentAnimation === 'normal') {
player.setAnimation('walking');
}
}
return;
}
if (crouchButton && Math.abs(x - crouchButton.x) < 142 && Math.abs(y - crouchButton.y) < 142) {
// Crouch button pressed
player.crouch();
return;
}
if (jumpButton && Math.abs(x - jumpButton.x) < 142 && Math.abs(y - jumpButton.y) < 142) {
// Jump button pressed
player.jump();
if (player.currentAnimation === 'normal') {
player.setAnimation('walking', 30);
}
return;
}
// Movement is now handled by dedicated buttons above
}
function checkMeleeHit(attacker, target, damage, attackType) {
var horizontalDistance = Math.abs(attacker.x - target.x);
var verticalDistance = Math.abs(attacker.y - target.y);
// Bigger horizontal range (600) but smaller vertical range (250)
if (horizontalDistance < 600 && verticalDistance < 250) {
// Punch attacks can't hit crouching targets
if (attackType === 'punch' && target.isCrouching && !target.isJumping) {
return false; // Punch misses crouching target
}
if (target.isBlocking) {
// Attack was blocked - reduced damage and pushback
target.takeDamage(Math.floor(damage * 0.7)); // 30% damage reduction for normal blocking (increased from 70% reduction)
LK.getSound('block').play();
// Apply pushback to the attacker
var pushDirection = attacker.x < target.x ? -1 : 1;
var pushDistance = 60;
var newX = attacker.x + pushDirection * pushDistance;
// Make sure attacker stays within bounds
newX = Math.max(200, Math.min(1848, newX));
// Animate the pushback using tween
tween(attacker, {
x: newX
}, {
duration: 200,
easing: tween.easeOut
});
// Flash the target to show block effect
LK.effects.flashObject(target, 0x4444ff, 300);
return false; // Hit was blocked
} else {
// Normal hit
target.takeDamage(damage);
return true; // Hit was successful
}
}
return false; // No hit
}
function updateAI() {
if (!enemy || !player) return;
var distance = Math.abs(enemy.x - player.x);
// Handle enemy movement holding
if (enemyMovementTimer > 0) {
enemyMovementTimer--;
if (enemyIsMovingLeft && enemy.x > 200) {
enemy.x -= enemy.speed * 2;
// Set walking animation when moving
if (enemy.currentAnimation === 'normal') {
enemy.setAnimation('walking');
}
}
if (enemyIsMovingRight && enemy.x < 2532) {
enemy.x += enemy.speed * 2;
// Set walking animation when moving
if (enemy.currentAnimation === 'normal') {
enemy.setAnimation('walking');
}
}
} else {
enemyIsMovingLeft = false;
enemyIsMovingRight = false;
// Reset walking animation when movement stops
if (enemy.currentAnimation === 'walking') {
enemy.setAnimation('normal');
}
}
// Enhanced AI behavior - mix of range attacks, melee attacks, movement, jumping and crouching
var aiUpdateFrequency = 30;
if (gameDifficulty === 'easy') {
aiUpdateFrequency = 45; // Slower AI reactions
} else if (gameDifficulty === 'hard') {
aiUpdateFrequency = 8; // Much faster AI reactions for hard mode - increased from 15
}
// Hard mode prediction system - track player patterns
if (gameDifficulty === 'hard') {
// Initialize prediction tracking if not set
if (enemy.playerActionHistory === undefined) {
enemy.playerActionHistory = [];
enemy.playerPositionHistory = [];
enemy.lastPlayerX = player.x;
enemy.lastPlayerY = player.y;
enemy.predictedPlayerAction = null;
enemy.predictionConfidence = 0;
}
// Track player movement patterns
var playerMovement = player.x - enemy.lastPlayerX;
if (Math.abs(playerMovement) > 5) {
enemy.playerPositionHistory.push({
x: player.x,
y: player.y,
movement: playerMovement,
tick: LK.ticks
});
// Keep only recent history (last 60 ticks)
if (enemy.playerPositionHistory.length > 10) {
enemy.playerPositionHistory.shift();
}
}
// Track player actions
if (player.currentAnimation !== 'normal' && player.currentAnimation !== 'walking') {
enemy.playerActionHistory.push({
action: player.currentAnimation,
distance: distance,
playerX: player.x,
tick: LK.ticks
});
// Keep only recent action history
if (enemy.playerActionHistory.length > 15) {
enemy.playerActionHistory.shift();
}
}
// Predict player movement direction
var predictedMovement = 0;
if (enemy.playerPositionHistory.length >= 3) {
var recentMovements = enemy.playerPositionHistory.slice(-3);
var avgMovement = 0;
for (var m = 0; m < recentMovements.length; m++) {
avgMovement += recentMovements[m].movement;
}
predictedMovement = avgMovement / recentMovements.length;
}
// Predict likely player action based on pattern analysis
var actionPatterns = {};
for (var h = 0; h < enemy.playerActionHistory.length; h++) {
var historyItem = enemy.playerActionHistory[h];
var distanceRange = Math.floor(historyItem.distance / 200) * 200; // Group by distance ranges
var pattern = historyItem.action + '_' + distanceRange;
actionPatterns[pattern] = (actionPatterns[pattern] || 0) + 1;
}
// Find most likely action at current distance
var currentDistanceRange = Math.floor(distance / 200) * 200;
var mostLikelyAction = null;
var highestCount = 0;
for (var pattern in actionPatterns) {
if (pattern.includes('_' + currentDistanceRange) && actionPatterns[pattern] > highestCount) {
highestCount = actionPatterns[pattern];
mostLikelyAction = pattern.split('_')[0];
}
}
enemy.predictedPlayerAction = mostLikelyAction;
enemy.predictionConfidence = Math.min(highestCount / 3, 1.0); // Max confidence at 3+ occurrences
// Preemptive actions based on predictions
if (enemy.predictionConfidence > 0.6) {
// High confidence predictions - react preemptively
if (enemy.predictedPlayerAction === 'punching' && distance < 600) {
// Preemptively crouch or move away from predicted punch
if (Math.random() < 0.9) {
if (Math.random() < 0.7) {
enemy.crouch();
} else {
// Move away from predicted punch
if (enemy.x > player.x) {
enemyIsMovingRight = true;
enemyMovementTimer = 20;
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 20;
}
}
reactToPlayerAction = true;
}
} else if (enemy.predictedPlayerAction === 'kicking' && distance < 600) {
// Preemptively jump or block predicted kick
if (Math.random() < 0.85) {
if (Math.random() < 0.6) {
enemy.jump();
} else {
enemy.isBlocking = true;
}
reactToPlayerAction = true;
}
} else if (enemy.predictedPlayerAction === 'rangeShooting') {
// Preemptively dodge or block predicted range attack
if (Math.random() < 0.8) {
if (Math.random() < 0.5) {
enemy.jump(); // Jump to avoid
} else if (Math.random() < 0.7) {
enemy.crouch(); // Crouch to avoid
} else {
enemy.isBlocking = true; // Block
}
reactToPlayerAction = true;
}
}
}
// Predictive positioning - move to optimal distance based on predicted movement
if (enemy.predictionConfidence > 0.4 && Math.abs(predictedMovement) > 2) {
var futurePlayerX = player.x + predictedMovement * 3; // Predict 3 frames ahead
var futureDist = Math.abs(enemy.x - futurePlayerX);
// Position for optimal attack range (400-600 pixels)
if (futureDist < 300) {
// Too close - back away
if (enemy.x > futurePlayerX) {
enemyIsMovingRight = true;
enemyMovementTimer = 15;
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 15;
}
reactToPlayerAction = true;
} else if (futureDist > 700) {
// Too far - move closer for attack opportunity
if (enemy.x > futurePlayerX) {
enemyIsMovingLeft = true;
enemyMovementTimer = 20;
} else {
enemyIsMovingRight = true;
enemyMovementTimer = 20;
}
reactToPlayerAction = true;
}
}
// Advanced counter-attack predictions
if (enemy.predictionConfidence > 0.5 && distance < 500) {
// Predict and counter player's most likely next action
if (enemy.predictedPlayerAction === 'punching') {
// Counter predicted punch with kick
if (enemy.attack('kick') && Math.random() < 0.7) {
enemy.setAnimation('kicking', 25);
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15);
LK.getSound('kickSound').play();
reactToPlayerAction = true;
}
} else if (enemy.predictedPlayerAction === 'kicking') {
// Counter predicted kick with range attack
if (enemy.energy >= 1 && enemy.specialAttack() && Math.random() < 0.8) {
enemy.setAnimation('rangeShooting', 15);
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
var projectileY = enemy.y - 280;
var projectile = new Projectile(enemy.x + projectileXOffset, projectileY, shootDirection, enemy.specialDamage, enemy);
projectiles.push(projectile);
game.addChild(projectile);
enemy.chargeSpecial(7);
LK.getSound('rangeShoot').play();
reactToPlayerAction = true;
}
}
}
// Update tracking variables
enemy.lastPlayerX = player.x;
enemy.lastPlayerY = player.y;
}
// Check for player actions to react to (normal and hard modes only)
var reactToPlayerAction = false;
if (gameDifficulty === 'normal' || gameDifficulty === 'hard') {
// Hard mode AI tries to block all attacks
if (gameDifficulty === 'hard') {
// Try to block all incoming attacks with perfect timing
if (player.currentAnimation === 'punching' || player.currentAnimation === 'kicking' || player.currentAnimation === 'rangeShooting' || player.currentAnimation === 'potentShooting') {
if (distance < 1000) {
// Even wider detection range for hard mode - increased from 800
enemy.isBlocking = true;
reactToPlayerAction = true;
}
}
// Try to maintain distance for range attacks - more aggressive
if (distance < 500 && Math.random() < 0.95) {
if (enemy.x > player.x) {
enemyIsMovingRight = true;
enemyMovementTimer = 35; // Hold movement even longer - increased from 25
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 35; // Hold movement even longer - increased from 25
}
reactToPlayerAction = true;
}
// Jump and crouch to avoid projectiles - much more frequently
if (player.currentAnimation === 'rangeShooting' || player.currentAnimation === 'potentShooting') {
if (Math.random() < 0.85) {
if (Math.random() < 0.5) {
enemy.jump(); // Jump to avoid projectiles
} else {
enemy.crouch(); // Crouch to avoid projectiles
}
reactToPlayerAction = true;
}
}
// Attack player based on their position - more aggressively
if (player.isCrouching && distance < 560 && Math.random() < 0.98) {
if (enemy.attack('kick')) {
enemy.setAnimation('kicking', 25);
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15);
LK.getSound('kickSound').play();
reactToPlayerAction = true;
}
}
// Attack jumping player with range/potent attacks - more frequently
if (player.isJumping && distance > 200 && distance < 800) {
if (enemy.energy >= 1 && enemy.specialAttack() && Math.random() < 0.9) {
enemy.setAnimation('rangeShooting', 15);
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
var projectileY = enemy.y - 300; // Aim higher for jumping player
var projectile = new Projectile(enemy.x + projectileXOffset, projectileY, shootDirection, enemy.specialDamage, enemy);
projectiles.push(projectile);
game.addChild(projectile);
enemy.chargeSpecial(7);
LK.getSound('rangeShoot').play();
reactToPlayerAction = true;
}
}
// Counter-attack immediately after blocking
if (enemy.isBlocking && distance < 560 && Math.random() < 0.7) {
// Stop blocking and counter-attack
enemy.isBlocking = false;
if (enemy.attack('punch')) {
enemy.setAnimation('punching', 20);
var hit = checkMeleeHit(enemy, player, Math.floor(enemy.attackDamage * 0.75), 'punch');
if (hit) enemy.chargeSpecial(15);
LK.getSound('punchSound').play();
reactToPlayerAction = true;
}
}
// Use special attacks more aggressively when player is vulnerable
if (player.currentAnimation === 'punching' || player.currentAnimation === 'kicking') {
if (distance > 400 && distance < 700 && enemy.canUseSpecialAttack() && Math.random() < 0.6) {
if (enemy.useSpecialAttack()) {
enemy.setAnimation('potentShooting', 30);
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
var superProjectileY = enemy.y - 280;
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
if (!enemy) return; // Safety check to prevent null access
var projectileX = enemy.x + projectileXOffset + (index - 3) * 60;
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(enemy.specialDamage * 2 / 7), enemy);
superProjectile.speed = 10; // Even faster super projectiles in hard mode
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// Sound will be played with each projectile instead
reactToPlayerAction = true;
}
}
}
} else {
// Normal mode behavior (existing)
// Check if player is punching - try to crouch
if (player.currentAnimation === 'punching' && distance < 560) {
if (Math.random() < 0.7) {
enemy.crouch();
reactToPlayerAction = true;
}
}
// Check if player is kicking - try to jump
else if (player.currentAnimation === 'kicking' && distance < 560) {
if (Math.random() < 0.7) {
enemy.jump();
reactToPlayerAction = true;
}
}
// Check if player is shooting range attacks - try to block
else if (player.currentAnimation === 'rangeShooting') {
if (Math.random() < 0.8) {
enemy.isBlocking = true;
reactToPlayerAction = true;
}
}
// Check if player is crouching - try to kick
else if (player.isCrouching && distance < 560) {
if (Math.random() < 0.6) {
if (enemy.attack('kick')) {
enemy.setAnimation('kicking', 25);
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15);
LK.getSound('kickSound').play();
reactToPlayerAction = true;
}
}
}
}
}
if (LK.ticks % aiUpdateFrequency === 0 && !reactToPlayerAction) {
var actionRange = 12;
if (gameDifficulty === 'easy') {
actionRange = 8; // Less varied AI behavior
} else if (gameDifficulty === 'hard') {
actionRange = 25; // Much more varied and aggressive AI behavior for hard mode - increased from 20
}
var action = Math.floor(Math.random() * actionRange);
// Check if enemy is in melee range (close enough to punch/kick)
if (distance < 560) {
// In melee range - try to attack or block
if (gameDifficulty === 'hard') {
// Hard mode: much more aggressive melee combat with timing prediction
var shouldAttack = action <= 8;
var attackType = 'punch';
// Predict optimal attack timing based on player's current state
if (gameDifficulty === 'hard' && enemy.predictionConfidence > 0.3) {
// If player is likely to move away, time attack for interception
if (enemy.predictedPlayerAction === 'walking' && Math.abs(player.x - enemy.lastPlayerX) > 3) {
// Lead the target - attack where player will be
var futurePlayerX = player.x + (player.x - enemy.lastPlayerX) * 2;
var interceptDistance = Math.abs(enemy.x - futurePlayerX);
if (interceptDistance < 500) {
shouldAttack = true;
attackType = 'kick'; // Kick has longer range
}
}
// If player is in animation recovery, attack with higher probability
if (player.currentAnimation === 'punching' || player.currentAnimation === 'kicking') {
shouldAttack = Math.random() < 0.9; // Very high chance to punish
attackType = 'punch'; // Faster attack for punishment
}
}
if (shouldAttack && enemy.attack()) {
if (attackType === 'kick' && !enemy.isCrouching) {
// Kick attack
enemy.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('kick').play();
} else {
// Punch attack
enemy.setAnimation('punching', 20); // 20 ticks punch animation
var hit = checkMeleeHit(enemy, player, Math.floor(enemy.attackDamage * 0.75), 'punch');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('punchSound').play();
}
} else if (action <= 15 && !enemy.isCrouching && enemy.attack()) {
// Regular kick attacks in hard mode
enemy.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('kick').play();
} else if (action <= 18) {
// Block when not attacking
enemy.isBlocking = true;
} else if (action <= 20) {
// Back away occasionally when too close
if (enemy.x > player.x) {
enemyIsMovingRight = true;
enemyMovementTimer = 15; // Shorter movement in hard mode for more aggression
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 15; // Shorter movement in hard mode for more aggression
}
} else if (action <= 22) {
// Jump more frequently in melee range in hard mode
enemy.jump();
} else {
// Crouch more frequently in melee range in hard mode
enemy.crouch();
}
} else {
// Normal and easy mode: existing behavior
if (action <= 3 && enemy.attack()) {
// Punch attack
enemy.setAnimation('punching', 20); // 20 ticks punch animation
var hit = checkMeleeHit(enemy, player, Math.floor(enemy.attackDamage * 0.75), 'punch');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('punchSound').play();
} else if (action <= 6 && !enemy.isCrouching && enemy.attack()) {
// Kick attack (slightly more damage) - can't kick while crouching
enemy.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('kickSound').play();
} else if (action <= 8) {
// Block when not attacking
enemy.isBlocking = true;
} else if (action <= 9) {
// Back away occasionally when too close
if (enemy.x > player.x) {
enemyIsMovingRight = true;
enemyMovementTimer = 20; // Hold movement for 20 ticks
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 20; // Hold movement for 20 ticks
}
} else if (action <= 10) {
// Jump occasionally in melee range
enemy.jump();
} else {
// Crouch occasionally in melee range
enemy.crouch();
}
}
} else if (distance < 400) {
// Medium range - move closer for melee or use range attack
if (action <= 4) {
// Move closer to get into melee range
if (enemy.x > player.x) {
enemyIsMovingLeft = true;
enemyMovementTimer = 15; // Hold movement for 15 ticks
} else {
enemyIsMovingRight = true;
enemyMovementTimer = 15; // Hold movement for 15 ticks
}
} else if (action <= 6 && enemy.canUseSpecialAttack() && enemy.useSpecialAttack()) {
// Use super special attack with charging animation
tween(enemy, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffaa00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Set potent shooting animation
enemy.setAnimation('potentShooting', 50); // 50 ticks potent shooting animation - extended duration
// Return to normal size and color after charging
tween(enemy, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
// Determine shoot direction based on player position
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on enemy stance
var superProjectileY = enemy.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (enemy.isCrouching && !enemy.isJumping) {
superProjectileY = enemy.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (enemy.isJumping) {
superProjectileY = enemy.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
// Create 7 super projectiles fired with spacing
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
if (!enemy) return; // Safety check to prevent null access
var projectileX = enemy.x + projectileXOffset + (index - 3) * 60; // Horizontal spread in a row
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(enemy.specialDamage * 2 / 7), enemy);
// Increase speed for AI potent projectiles
superProjectile.speed = 8; // Increased speed to match regular projectiles
// Add laser effects for fighter 3
if (enemy.fighterType === 3) {
// Initial bright laser effect
tween(superProjectile, {
tint: 0xff0000
}, {
duration: 50
});
// Pulsing laser effect
tween(superProjectile, {
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(superProjectile, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// No pushback when firing potent projectiles
// Sound will be played with each projectile instead
}
});
} else if (action <= 6 && enemy.energy >= 1 && enemy.specialAttack()) {
// Use range attack (reduced frequency)
enemy.setAnimation('rangeShooting', 15); // 15 ticks range shooting animation
// Determine shoot direction based on player position
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust projectile origin based on enemy stance
var projectileY = enemy.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (enemy.isCrouching && !enemy.isJumping) {
projectileY = enemy.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (enemy.isJumping) {
projectileY = enemy.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
var projectile = new Projectile(enemy.x + projectileXOffset, projectileY, shootDirection, enemy.specialDamage, enemy);
projectiles.push(projectile);
game.addChild(projectile);
enemy.chargeSpecial(7); // Charge special at 50% rate (15 * 0.5 = 7.5, rounded to 7)
LK.getSound('rangeShoot').play();
} else if (action <= 8) {
// Block occasionally
enemy.isBlocking = true;
} else if (action <= 9) {
// Jump occasionally at medium range
enemy.jump();
} else {
// Crouch occasionally at medium range
enemy.crouch();
}
} else {
// Long range - use range attacks or move closer
if (action <= 2 && enemy.canUseSpecialAttack() && enemy.useSpecialAttack()) {
// Use super special attack when available (reduced frequency) with charging animation
tween(enemy, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffaa00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Set potent shooting animation
enemy.setAnimation('potentShooting', 50); // 50 ticks potent shooting animation - extended duration
// Return to normal size and color after charging
tween(enemy, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
// Determine shoot direction based on player position
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on enemy stance
var superProjectileY = enemy.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (enemy.isCrouching && !enemy.isJumping) {
superProjectileY = enemy.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (enemy.isJumping) {
superProjectileY = enemy.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
// Create 7 super projectiles fired with spacing
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
if (!enemy) return; // Safety check to prevent null access
var projectileX = enemy.x + projectileXOffset + (index - 3) * 60; // Horizontal spread in a row
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(enemy.specialDamage * 2 / 7), enemy);
// Increase speed for AI long range potent projectiles
superProjectile.speed = 8; // Increased speed to match regular projectiles
// Add laser effects for fighter 3
if (enemy.fighterType === 3) {
// Initial bright laser effect
tween(superProjectile, {
tint: 0xff0000
}, {
duration: 50
});
// Pulsing laser effect
tween(superProjectile, {
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(superProjectile, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// No pushback when firing potent projectiles
// Sound will be played with each projectile instead
}
});
} else if (action <= 3 && enemy.energy >= 1 && enemy.specialAttack()) {
// Use range attack when available (reduced frequency)
enemy.setAnimation('rangeShooting', 15); // 15 ticks range shooting animation
// Determine shoot direction based on player position
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on enemy stance
var projectileY = enemy.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (enemy.isCrouching && !enemy.isJumping) {
projectileY = enemy.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (enemy.isJumping) {
projectileY = enemy.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
var projectile = new Projectile(enemy.x + projectileXOffset, projectileY, shootDirection, enemy.specialDamage, enemy);
projectiles.push(projectile);
game.addChild(projectile);
enemy.chargeSpecial(7); // Charge special at 50% rate (15 * 0.5 = 7.5, rounded to 7)
LK.getSound('rangeShoot').play();
} else if (action <= 8) {
// Move closer to player for melee combat
if (enemy.x > player.x) {
enemyIsMovingLeft = true;
enemyMovementTimer = 25; // Hold movement for 25 ticks
} else {
enemyIsMovingRight = true;
enemyMovementTimer = 25; // Hold movement for 25 ticks
}
} else if (action <= 9) {
// Occasionally block at range
enemy.isBlocking = true;
} else if (action <= 10) {
// Jump occasionally at long range
enemy.jump();
} else {
// Crouch occasionally at long range
enemy.crouch();
}
}
}
// Reset blocking and crouching periodically
var resetFrequency = 45;
if (gameDifficulty === 'hard') {
resetFrequency = 25; // Much faster reset in hard mode for more responsive AI
}
if (LK.ticks % resetFrequency === 0) {
enemy.isBlocking = false;
enemy.stopCrouch(); // Release crouch periodically
}
}
function updateCombatTeamDisplays() {
if (gameMode !== 'teamBattle') return;
var baseScale = 0.3;
var activeScale = baseScale * 1.2;
// Update player team displays
for (var i = 0; i < teamPlayerDisplays.length; i++) {
var fighter = teamPlayerDisplays[i];
if (i < currentPlayerTeamIndex) {
// Defeated fighter - grey tint
tween(fighter, {
tint: 0x666666,
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 300,
easing: tween.easeOut
});
} else if (i === currentPlayerTeamIndex) {
// Active fighter - normal color and bigger
tween(fighter, {
tint: 0xffffff,
scaleX: activeScale,
scaleY: activeScale
}, {
duration: 300,
easing: tween.easeOut
});
} else {
// Not yet active fighter - normal color and size
tween(fighter, {
tint: 0xffffff,
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Update enemy team displays
for (var i = 0; i < teamEnemyDisplays.length; i++) {
var fighter = teamEnemyDisplays[i];
if (i < currentEnemyTeamIndex) {
// Defeated fighter - grey tint
tween(fighter, {
tint: 0x666666,
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 300,
easing: tween.easeOut
});
} else if (i === currentEnemyTeamIndex) {
// Active fighter - normal color and bigger
tween(fighter, {
tint: 0xffffff,
scaleX: activeScale,
scaleY: activeScale
}, {
duration: 300,
easing: tween.easeOut
});
} else {
// Not yet active fighter - normal color and size
tween(fighter, {
tint: 0xffffff,
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
function switchToNextTeamFighter(isPlayer) {
if (gameMode !== 'teamBattle') return false;
if (isPlayer) {
// Check if player is already being defeated to prevent double switching
if (player.isBeingDefeated || lastPlayerDefeatCheck === currentPlayerTeamIndex) return false;
player.isBeingDefeated = true;
lastPlayerDefeatCheck = currentPlayerTeamIndex;
// Flash defeated fighter red before switching
LK.getSound('defeated').play();
tween(player, {
tint: 0xff0000
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Mark current fighter as defeated (grey)
if (teamPlayerDisplays[currentPlayerTeamIndex]) {
tween(teamPlayerDisplays[currentPlayerTeamIndex], {
tint: 0x333333
}, {
duration: 500,
easing: tween.easeOut
});
}
currentPlayerTeamIndex++;
if (currentPlayerTeamIndex < playerTeam.length) {
// Switch to next player team fighter
selectedCharacter = playerTeam[currentPlayerTeamIndex];
// Remove current player
player.destroy();
game.removeChild(player);
// Create new player
player = game.addChild(new Fighter(selectedCharacter, true));
player.x = 300;
player.y = 1800;
// Flash new fighter with immunity
tween(player, {
tint: 0xffffff,
alpha: 0.5
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
alpha: 1.0,
tint: 0x44ff44
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
tint: 0xffffff
}, {
duration: 400,
easing: tween.easeOut
});
}
});
}
});
// Update health bars to reference new fighter
playerHealthBar.fighter = player;
playerEnergyBar.fighter = player;
playerSpecialBar.fighter = player;
// Update team displays to show new states
updateCombatTeamDisplays();
}
}
});
return true;
} else {
// Check if enemy is already being defeated to prevent double switching
if (enemy.isBeingDefeated || lastEnemyDefeatCheck === currentEnemyTeamIndex) return false;
enemy.isBeingDefeated = true;
lastEnemyDefeatCheck = currentEnemyTeamIndex;
// Flash defeated fighter red before switching
LK.getSound('defeated').play();
tween(enemy, {
tint: 0xff0000
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Mark current fighter as defeated (grey)
if (teamEnemyDisplays[currentEnemyTeamIndex]) {
tween(teamEnemyDisplays[currentEnemyTeamIndex], {
tint: 0x333333
}, {
duration: 500,
easing: tween.easeOut
});
}
currentEnemyTeamIndex++;
if (currentEnemyTeamIndex < enemyTeam.length) {
// Switch to next enemy team fighter
enemyCharacter = enemyTeam[currentEnemyTeamIndex];
// Remove current enemy
enemy.destroy();
game.removeChild(enemy);
// Create new enemy
enemy = game.addChild(new Fighter(enemyCharacter, false));
enemy.x = 2432;
enemy.y = 1800;
// Flash new fighter with immunity
tween(enemy, {
tint: 0xffffff,
alpha: 0.5
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(enemy, {
alpha: 1.0,
tint: 0x44ff44
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(enemy, {
tint: 0xffffff
}, {
duration: 400,
easing: tween.easeOut
});
}
});
}
});
// Update health bars to reference new fighter
enemyHealthBar.fighter = enemy;
enemyEnergyBar.fighter = enemy;
enemySpecialBar.fighter = enemy;
// Update team displays to show new states
updateCombatTeamDisplays();
}
}
});
return true;
}
return false;
}
function checkGameOver() {
if (player && player.health <= 0) {
// Try to switch to next team fighter
if (gameMode === 'teamBattle') {
// Only check game over if we can't switch to another fighter
var switched = switchToNextTeamFighter(true);
if (!switched) {
// Check if all team fighters are actually defeated
var allPlayerDefeated = true;
for (var i = 0; i < playerTeam.length; i++) {
if (i > currentPlayerTeamIndex) {
allPlayerDefeated = false;
break;
}
}
if (allPlayerDefeated) {
// All team fighters defeated - game over
LK.showGameOver();
}
}
} else {
LK.getSound('defeated').play();
LK.showGameOver();
}
} else if (enemy && enemy.health <= 0) {
// Try to switch to next team fighter
if (gameMode === 'teamBattle') {
// Only check win condition if we can't switch to another fighter
var switched = switchToNextTeamFighter(false);
if (!switched) {
// Check if all enemy team fighters are actually defeated
var allEnemyDefeated = true;
for (var i = 0; i < enemyTeam.length; i++) {
if (i > currentEnemyTeamIndex) {
allEnemyDefeated = false;
break;
}
}
if (allEnemyDefeated) {
// All team fighters defeated - player wins
LK.showYouWin();
}
}
} else {
LK.getSound('defeated').play();
LK.showYouWin();
}
}
}
// Character selection input
game.down = function (x, y, obj) {
// Check back to menu button for all states
for (var i = 0; i < game.children.length; i++) {
var child = game.children[i];
if (child.buttonType === 'backToMenu' && Math.abs(x - child.x) < 200 && Math.abs(y - child.y) < 80) {
// Clear current game state and return to title screen
gameState = 'titleScreen';
// Remove all game objects
for (var k = game.children.length - 1; k >= 0; k--) {
game.removeChild(game.children[k]);
}
game.addChild(gameBackground);
game.addChild(menuBackground);
// Show menu background, hide game background
if (menuBackground) menuBackground.visible = true;
if (gameBackground) gameBackground.visible = false;
// Reset variables
player = null;
enemy = null;
projectiles = [];
superProjectiles = [];
teamPlayerDisplays = [];
teamEnemyDisplays = [];
currentPlayerTeamIndex = 0;
currentEnemyTeamIndex = 0;
selectedTeamIndex = 0;
characterSelectors = [];
titleButtons = [];
selectionBorder = null;
currentPlayerSelection = 1;
titleText.visible = true;
// Restart menu music when returning to title screen
LK.playMusic('menuMusic', {
loop: true
});
initTitleScreen();
return;
}
}
if (gameState === 'titleScreen') {
// Check title screen buttons
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'normal') {
gameMode = 'normal';
titleText.visible = false;
clearTitleScreen();
gameState = 'characterSelect';
initCharacterSelection();
} else if (button.buttonType === 'select') {
gameMode = 'teamBattle';
teamSize = 1;
titleText.visible = false;
clearTitleScreen();
gameState = 'teamFighterSelect';
// Initialize teams for 1v1
playerTeam = [];
enemyTeam = [];
selectedTeamIndex = 0;
initTeamFighterSelection();
} else if (button.buttonType === 'team') {
gameMode = 'teamBattle';
titleText.visible = false;
clearTitleScreen();
gameState = 'teamSelect';
initTeamSelection();
}
break;
}
}
} else if (gameState === 'teamSelect') {
// Check team selection buttons
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'easy') {
gameDifficulty = 'easy';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'easy') {
diffButton.tint = 0xff9500; // Lighter orange for easy
} else if (diffButton.buttonType === 'normalDiff' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'normalDiff') {
gameDifficulty = 'normal';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xcc6600; // Darker orange for normal
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'hard') {
gameDifficulty = 'hard';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'hard') {
diffButton.tint = 0xff0000; // Red for hard
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'size2') {
teamSize = 2;
playerTeam = [];
enemyTeam = [];
instructionText.setText('Select Team Size: ' + teamSize + ' vs ' + teamSize);
updateTeamDisplay();
// Update button colors to show selection
for (var k = 0; k < titleButtons.length; k++) {
var sizeButton = titleButtons[k];
if (sizeButton.buttonType === 'size2') {
sizeButton.tint = 0x44ff44; // Green when selected
} else if (sizeButton.buttonType === 'size3') {
sizeButton.tint = 0xffffff; // White when not selected
}
}
} else if (button.buttonType === 'size3') {
teamSize = 3;
playerTeam = [];
enemyTeam = [];
instructionText.setText('Select Team Size: ' + teamSize + ' vs ' + teamSize);
updateTeamDisplay();
// Update button colors to show selection
for (var k = 0; k < titleButtons.length; k++) {
var sizeButton = titleButtons[k];
if (sizeButton.buttonType === 'size3') {
sizeButton.tint = 0x44ff44; // Green when selected
} else if (sizeButton.buttonType === 'size2') {
sizeButton.tint = 0xffffff; // White when not selected
}
}
} else if (button.buttonType === 'startTeam') {
// Remove team display elements
for (var k = game.children.length - 1; k >= 0; k--) {
var child = game.children[k];
if (child.isTeamDisplay) {
game.removeChild(child);
}
}
// Initialize teams if empty
for (var j = 0; j < teamSize; j++) {
if (!playerTeam[j]) playerTeam[j] = j + 1;
if (!enemyTeam[j]) enemyTeam[j] = (j + 2) % 5 + 1;
}
// Reset team indices
currentPlayerTeamIndex = 0;
currentEnemyTeamIndex = 0;
clearTitleScreen();
gameState = 'teamFighterSelect';
initTeamFighterSelection();
}
break;
}
}
} else if (gameState === 'characterSelect') {
// Check difficulty buttons first
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'easy') {
gameDifficulty = 'easy';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'easy') {
diffButton.tint = 0xff9500; // Lighter orange for easy
} else if (diffButton.buttonType === 'normalDiff' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'normalDiff') {
gameDifficulty = 'normal';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xcc6600; // Darker orange for normal
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'hard') {
gameDifficulty = 'hard';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'hard') {
diffButton.tint = 0xff0000; // Red for hard
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
}
}
}
// Check for start combat button
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (button.buttonType === 'startCombat' && Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
// Check if character is selected and difficulty is selected before starting combat
if (!selectedCharacter || selectedCharacter === 0) {
// Flash screen to indicate character must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
if (!gameDifficulty || gameDifficulty === '') {
// Flash screen to indicate difficulty must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
enemyCharacter = selectedCharacter % 5 + 1; // Simple enemy selection
storage.lastPlayerFighter = selectedCharacter;
storage.lastEnemyFighter = enemyCharacter;
// Clear character selection
game.removeChild(instructionText);
for (var j = 0; j < characterSelectors.length; j++) {
game.removeChild(characterSelectors[j]);
}
game.removeChild(selectionBorder);
characterSelectors = [];
gameState = 'combat';
startCombat();
return;
}
}
// Check which character was selected
for (var i = 0; i < characterSelectors.length; i++) {
var fighter = characterSelectors[i];
// Use x,y coordinates directly since they're already in game coordinates
if (Math.abs(x - fighter.x) < 100 && Math.abs(y - fighter.y) < 150) {
selectedCharacter = fighter.fighterIndex;
storage.lastPlayerFighter = selectedCharacter;
updateSelectionBorder();
break;
}
}
} else if (gameState === 'playerSelect') {
// Check difficulty buttons first
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'easy') {
gameDifficulty = 'easy';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'easy') {
diffButton.tint = 0xff9500; // Lighter orange for easy
} else if (diffButton.buttonType === 'normalDiff' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'normalDiff') {
gameDifficulty = 'normal';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xcc6600; // Darker orange for normal
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'hard') {
gameDifficulty = 'hard';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'hard') {
diffButton.tint = 0xff0000; // Red for hard
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
}
}
}
// Check for start combat button
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (button.buttonType === 'startCombat' && Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
// Check if both characters are selected and difficulty is selected before starting combat
if (currentPlayerSelection !== 2 || !enemyCharacter) {
// Flash screen to indicate both characters must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
if (!gameDifficulty || gameDifficulty === '') {
// Flash screen to indicate difficulty must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
storage.lastEnemyFighter = enemyCharacter;
// Clear character selection
game.removeChild(instructionText);
for (var j = 0; j < characterSelectors.length; j++) {
game.removeChild(characterSelectors[j]);
}
game.removeChild(selectionBorder);
characterSelectors = [];
gameState = 'combat';
startCombat();
return;
}
}
// Check which character was selected for player vs player
for (var i = 0; i < characterSelectors.length; i++) {
var fighter = characterSelectors[i];
if (Math.abs(x - fighter.x) < 100 && Math.abs(y - fighter.y) < 150) {
if (currentPlayerSelection === 1) {
selectedCharacter = fighter.fighterIndex;
storage.lastPlayerFighter = selectedCharacter;
currentPlayerSelection = 2;
instructionText.setText('Player 2: Select Fighter');
} else {
enemyCharacter = fighter.fighterIndex;
}
updateSelectionBorder();
break;
}
}
} else if (gameState === 'teamFighterSelect') {
// Check difficulty buttons first
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'easy') {
gameDifficulty = 'easy';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'easy') {
diffButton.tint = 0xff9500; // Lighter orange for easy
} else if (diffButton.buttonType === 'normalDiff' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'normalDiff') {
gameDifficulty = 'normal';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xcc6600; // Darker orange for normal
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'hard') {
gameDifficulty = 'hard';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'hard') {
diffButton.tint = 0xff0000; // Red for hard
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
}
}
}
// Check for next/previous selection buttons
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 150 && Math.abs(y - button.y) < 150) {
if (button.buttonType === 'nextSelection') {
// Move to next selection if not at the end
if (selectedTeamIndex < teamSize * 2 - 1) {
selectedTeamIndex++;
var currentTeam = selectedTeamIndex < teamSize ? 'player' : 'enemy';
var fighterNumber = selectedTeamIndex % teamSize + 1;
var teamName = currentTeam === 'player' ? 'Player' : 'Enemy';
instructionText.setText(teamName + ' Team - Select Fighter ' + fighterNumber);
updateTeamBattleDisplay();
}
return;
} else if (button.buttonType === 'prevSelection') {
// Move to previous selection if not at the beginning
if (selectedTeamIndex > 0) {
selectedTeamIndex--;
var currentTeam = selectedTeamIndex < teamSize ? 'player' : 'enemy';
var fighterNumber = selectedTeamIndex % teamSize + 1;
var teamName = currentTeam === 'player' ? 'Player' : 'Enemy';
instructionText.setText(teamName + ' Team - Select Fighter ' + fighterNumber);
updateTeamBattleDisplay();
}
return;
}
}
}
// Check for start combat button
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (button.buttonType === 'startCombat' && Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
// Check if all fighters are selected and difficulty is selected before starting combat
// Need to check if both teams have at least one fighter selected
var playerTeamComplete = true;
var enemyTeamComplete = true;
for (var t = 0; t < teamSize; t++) {
if (!playerTeam[t] || playerTeam[t] === 0) playerTeamComplete = false;
if (!enemyTeam[t] || enemyTeam[t] === 0) enemyTeamComplete = false;
}
if (!playerTeamComplete || !enemyTeamComplete) {
// Flash screen to indicate all fighters must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
if (!gameDifficulty || gameDifficulty === '') {
// Flash screen to indicate difficulty must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
// All fighters selected, start combat
// Clear character selection
game.removeChild(instructionText);
for (var j = 0; j < characterSelectors.length; j++) {
game.removeChild(characterSelectors[j]);
}
game.removeChild(selectionBorder);
characterSelectors = [];
gameState = 'combat';
selectedCharacter = playerTeam[0];
enemyCharacter = enemyTeam[0];
startCombat();
return;
}
}
// Check which character was selected for team battle
for (var i = 0; i < characterSelectors.length; i++) {
var fighter = characterSelectors[i];
if (Math.abs(x - fighter.x) < 100 && Math.abs(y - fighter.y) < 150) {
// Determine which team and position we're selecting for
if (selectedTeamIndex < teamSize) {
// Selecting for player team
playerTeam[selectedTeamIndex] = fighter.fighterIndex;
} else {
// Selecting for enemy team
enemyTeam[selectedTeamIndex - teamSize] = fighter.fighterIndex;
}
// Don't auto-advance selectedTeamIndex - let user manually advance or change selection
// Update display for current selection
var currentTeam = selectedTeamIndex < teamSize ? 'player' : 'enemy';
var fighterNumber = selectedTeamIndex % teamSize + 1;
var teamName = currentTeam === 'player' ? 'Player' : 'Enemy';
instructionText.setText(teamName + ' Team - Fighter ' + fighterNumber + ' selected. Click another fighter to change or advance to next selection.');
updateTeamBattleDisplay();
break;
}
}
} else if (gameState === 'combat') {
handleCombatInput(x, y);
}
};
game.move = function (x, y, obj) {
if (gameState === 'combat' && player && !playerIsBlocking) {
// Check if moving over left button
if (leftMoveButton && Math.abs(x - leftMoveButton.x) < 142 && Math.abs(y - leftMoveButton.y) < 142) {
isMovingLeft = true;
player.stopCrouch(); // Stop crouching when moving left
} else {
isMovingLeft = false;
}
// Check if moving over right button
if (rightMoveButton && Math.abs(x - rightMoveButton.x) < 142 && Math.abs(y - rightMoveButton.y) < 142) {
isMovingRight = true;
player.stopCrouch(); // Stop crouching when moving right
} else {
isMovingRight = false;
}
// Check if holding crouch button
if (crouchButton && Math.abs(x - crouchButton.x) < 142 && Math.abs(y - crouchButton.y) < 142) {
player.crouch();
}
}
};
game.up = function (x, y, obj) {
if (player && gameState === 'combat') {
player.isBlocking = false;
playerIsBlocking = false;
isMovingLeft = false;
isMovingRight = false;
player.stopCrouch();
// Reset walking animation to normal when movement stops
if (player.currentAnimation === 'walking') {
player.setAnimation('normal');
}
}
};
game.update = function () {
if (gameState === 'combat') {
// Update fighters
if (player) player.update();
if (enemy) enemy.update();
// Update UI bars
if (playerHealthBar) playerHealthBar.update();
if (playerEnergyBar) playerEnergyBar.update();
if (playerSpecialBar) playerSpecialBar.update();
if (enemyHealthBar) enemyHealthBar.update();
if (enemyEnergyBar) enemyEnergyBar.update();
if (enemySpecialBar) enemySpecialBar.update();
// Update button availability visual feedback
if (rangeButton && rangeButtonText && player) {
if (player.energy >= player.specialCost) {
// Available - normal colors
tween.stop(rangeButton, {
tint: true
});
tween.stop(rangeButtonText, {
tint: true
});
rangeButton.tint = 0xFFFFFF; // White button when available
rangeButtonText.tint = 0xFFFFFF; // White text when available
} else {
// Not available - grey colors
tween.stop(rangeButton, {
tint: true
});
tween.stop(rangeButtonText, {
tint: true
});
rangeButton.tint = 0x666666; // Gray button when not available
rangeButtonText.tint = 0x666666; // Gray text when not available
}
}
if (specialButton && specialButtonText && player) {
if (player.canUseSpecialAttack()) {
// Available - normal colors
tween.stop(specialButton, {
tint: true
});
tween.stop(specialButtonText, {
tint: true
});
specialButton.tint = 0xFFFFFF; // White button when available
specialButtonText.tint = 0xFFFFFF; // White text when available
} else {
// Not available - grey colors
tween.stop(specialButton, {
tint: true
});
tween.stop(specialButtonText, {
tint: true
});
specialButton.tint = 0x666666; // Gray button when not available
specialButtonText.tint = 0x666666; // Gray text when not available
}
}
// Update super projectiles
for (var i = superProjectiles.length - 1; i >= 0; i--) {
var superProj = superProjectiles[i];
superProj.update();
// Check for super projectile clashes
for (var j = superProjectiles.length - 1; j >= 0; j--) {
if (i !== j) {
var otherSuperProj = superProjectiles[j];
if (Math.abs(superProj.x - otherSuperProj.x) < 80 && Math.abs(superProj.y - otherSuperProj.y) < 80) {
// Super projectiles clash - destroy both
LK.effects.flashScreen(0xffffff, 200);
LK.getSound('projectileClash').play();
superProj.destroy();
otherSuperProj.destroy();
superProjectiles.splice(Math.max(i, j), 1);
superProjectiles.splice(Math.min(i, j), 1);
i = Math.min(i, j) - 1; // Adjust index
break;
}
}
}
if (i >= 0 && i < superProjectiles.length) {
var superProj = superProjectiles[i];
// Check super projectile hits
var target = superProj.owner === player ? enemy : player;
if (target && Math.abs(superProj.x - target.x) < 200 && Math.abs(superProj.y - target.y) < 300) {
// Check if super projectile should miss crouching target
// Standing super projectiles (fired from y-280) should miss crouching targets
var superProjectileFromStanding = Math.abs(superProj.y - (superProj.owner.y - 280)) < 20;
if (target.isCrouching && !target.isJumping && superProjectileFromStanding) {
// Super projectile passes over crouching target - no hit
// Remove off-screen super projectiles
if (superProj.x < -50 || superProj.x > 2782) {
superProj.destroy();
superProjectiles.splice(i, 1);
}
continue; // Skip hit detection for crouching dodge
}
if (target.isBlocking) {
// Super projectile was blocked - apply 50% damage and pushback
target.takeDamage(superProj.damage, true, superProj.owner);
LK.getSound('block').play();
// No pushback for shooter when projectile impacts on block
// Push back the receiver (target) - much stronger pushback
var targetPushDirection = target.x < superProj.owner.x ? -1 : 1;
var targetNewX = target.x + targetPushDirection * 450;
targetNewX = Math.max(200, Math.min(1848, targetNewX));
tween(target, {
x: targetNewX
}, {
duration: 800,
easing: tween.easeOut
});
// Flash the target to show block effect
LK.effects.flashObject(target, 0x4444ff, 400);
} else {
// Normal hit
target.takeDamage(superProj.damage, true, superProj.owner);
LK.getSound('specialImpact').play();
// Push back the target when receiving super projectile hit - much stronger pushback
var targetPushDirection = target.x < superProj.owner.x ? -1 : 1;
var targetNewX = target.x + targetPushDirection * 500;
targetNewX = Math.max(200, Math.min(1848, targetNewX));
tween(target, {
x: targetNewX
}, {
duration: 900,
easing: tween.easeOut
});
// Also push back the shooter (attacker) when hitting unblocked target
var shooterPushDirection = superProj.owner.x < target.x ? -1 : 1;
var shooterNewX = superProj.owner.x + shooterPushDirection * 180;
shooterNewX = Math.max(200, Math.min(1848, shooterNewX));
tween(superProj.owner, {
x: shooterNewX
}, {
duration: 600,
easing: tween.easeOut
});
}
superProj.destroy();
superProjectiles.splice(i, 1);
continue;
}
// Remove off-screen super projectiles
if (superProj.x < -50 || superProj.x > 2782) {
superProj.destroy();
superProjectiles.splice(i, 1);
}
}
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var proj = projectiles[i];
proj.update();
// Check for projectile clashes (range attacks destroying each other)
for (var j = projectiles.length - 1; j >= 0; j--) {
if (i !== j) {
var otherProj = projectiles[j];
if (Math.abs(proj.x - otherProj.x) < 50 && Math.abs(proj.y - otherProj.y) < 50) {
// Regular projectiles clash - destroy both
LK.effects.flashScreen(0xffffff, 150);
LK.getSound('projectileClash').play();
proj.destroy();
otherProj.destroy();
projectiles.splice(Math.max(i, j), 1);
projectiles.splice(Math.min(i, j), 1);
i = Math.min(i, j) - 1; // Adjust index
break;
}
}
}
if (i >= 0 && i < projectiles.length) {
var proj = projectiles[i];
// Check if regular projectile hits super projectile
for (var j = superProjectiles.length - 1; j >= 0; j--) {
var superProj = superProjectiles[j];
if (Math.abs(proj.x - superProj.x) < 60 && Math.abs(proj.y - superProj.y) < 60) {
// Regular projectile destroyed by super projectile
proj.destroy();
projectiles.splice(i, 1);
break;
}
}
}
if (i >= 0 && i < projectiles.length) {
var proj = projectiles[i];
// Check projectile hits
var target = proj.owner === player ? enemy : player;
if (target && Math.abs(proj.x - target.x) < 200 && Math.abs(proj.y - target.y) < 300) {
// Check if projectile should miss crouching target
// Standing projectiles (fired from y-280) should miss crouching targets
var projectileFromStanding = Math.abs(proj.y - (proj.owner.y - 280)) < 20;
if (target.isCrouching && !target.isJumping && projectileFromStanding) {
// Projectile passes over crouching target - no hit
// Remove off-screen projectiles
if (proj.x < -50 || proj.x > 2782) {
proj.destroy();
projectiles.splice(i, 1);
}
continue; // Skip hit detection for crouching dodge
}
if (target.isBlocking) {
// Projectile was blocked - apply reduced damage and pushback
target.takeDamage(Math.floor(proj.damage * 0.7)); // 30% damage reduction for normal blocking (increased from 70% reduction)
LK.getSound('block').play();
// No pushback for shooter when projectile impacts on block
// Push back the receiver (target)
var targetPushDirection = target.x < proj.owner.x ? -1 : 1;
var targetNewX = target.x + targetPushDirection * 120;
targetNewX = Math.max(200, Math.min(1848, targetNewX));
tween(target, {
x: targetNewX
}, {
duration: 400,
easing: tween.easeOut
});
// Flash the target to show block effect
LK.effects.flashObject(target, 0x4444ff, 300);
} else {
// Normal hit
target.takeDamage(proj.damage);
LK.getSound('rangeImpact').play();
// Push back the target when hit by regular projectile
var targetPushDirection = target.x < proj.owner.x ? -1 : 1;
var targetNewX = target.x + targetPushDirection * 120;
targetNewX = Math.max(200, Math.min(1848, targetNewX));
tween(target, {
x: targetNewX
}, {
duration: 400,
easing: tween.easeOut
});
}
proj.owner.chargeSpecial(7); // Charge special at 50% rate for successful range hit
proj.destroy();
projectiles.splice(i, 1);
continue;
}
// Remove off-screen projectiles
if (proj.x < -50 || proj.x > 2782) {
proj.destroy();
projectiles.splice(i, 1);
}
}
}
// Handle continuous movement
if (player && !playerIsBlocking) {
if (isMovingLeft && player.x > 200) {
player.stopCrouch(); // Stop crouching when moving left
player.x -= player.speed * 3;
if (player.currentAnimation === 'normal') {
player.setAnimation('walking');
}
} else if (isMovingRight && player.x < 2532) {
player.stopCrouch(); // Stop crouching when moving right
player.x += player.speed * 3;
if (player.currentAnimation === 'normal') {
player.setAnimation('walking');
}
} else {
// Neither left nor right movement is active, reset walking animation
if (player.currentAnimation === 'walking') {
player.setAnimation('normal');
}
}
}
// Update AI
updateAI();
// Check game over with debounce protection (only check every 10 ticks to prevent rapid multiple calls)
if (LK.ticks % 10 === 0) {
checkGameOver();
}
}
};
// Keyboard event handler
LK.on('keydown', function (event) {
if (gameState !== 'combat' || !player) return;
var key = event.key.toLowerCase();
if (key === 'z') {
// Z key - Punch
if (player.attack()) {
player.setAnimation('punching', 20); // 20 ticks punch animation
checkMeleeHit(player, enemy, Math.floor(player.attackDamage * 0.75), 'punch');
LK.getSound('punchSound').play();
}
} else if (key === 'x') {
// X key - Kick - can't kick while crouching
if (!player.isCrouching && player.attack()) {
player.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
checkMeleeHit(player, enemy, Math.floor((player.attackDamage + 1) * 0.75), 'kick');
LK.getSound('kickSound').play();
}
} else if (key === 'c') {
// C key - Range attack (special)
if (enemy && player.specialAttack()) {
player.setAnimation('rangeShooting', 15); // 15 ticks range shooting animation
// Determine shoot direction based on enemy position
var shootDirection = enemy.x > player.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust projectile origin based on player stance
var projectileY = player.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (player.isCrouching && !player.isJumping) {
projectileY = player.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (player.isJumping) {
projectileY = player.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
var projectile = new Projectile(player.x + projectileXOffset, projectileY, shootDirection, player.specialDamage, player);
projectiles.push(projectile);
game.addChild(projectile);
player.chargeSpecial(7); // Charge special at 50% rate (15 * 0.5 = 7.5, rounded to 7)
LK.getSound('rangeShoot').play();
}
} else if (key === 'v') {
// V key - Super special attack
if (enemy && player.useSpecialAttack()) {
// Start charging animation before firing
tween(player, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffaa00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Set potent shooting animation
player.setAnimation('potentShooting', 50); // 50 ticks potent shooting animation - extended duration
// Return to normal size and color after charging
tween(player, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
// Determine shoot direction based on enemy position
var shootDirection = enemy.x > player.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on player stance
var superProjectileY = player.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (player.isCrouching && !player.isJumping) {
superProjectileY = player.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (player.isJumping) {
superProjectileY = player.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
// Create 7 super projectiles fired with spacing
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
var projectileX = player.x + projectileXOffset + (index - 3) * 60; // Horizontal spread in a row
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(player.specialDamage * 2 / 7), player);
// Increase speed for keyboard potent projectiles
superProjectile.speed = 8; // Increased speed to match regular projectiles
// Add laser effects for fighter 3
if (player.fighterType === 3) {
// Initial bright laser effect
tween(superProjectile, {
tint: 0xff0000
}, {
duration: 50
});
// Pulsing laser effect
tween(superProjectile, {
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(superProjectile, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// No pushback when firing potent projectiles
// Sound will be played with each projectile instead
}
});
}
}
});
// Initialize title screen
initTitleScreen();
; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var EnergyBar = Container.expand(function (fighter, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.fighter = fighter;
var bgBar = self.attachAsset('energyBarBg', {
anchorX: 0,
anchorY: 0
});
// Create 3 individual energy bars
self.energyBars = [];
for (var i = 0; i < 3; i++) {
var energyBar = self.attachAsset('energyBar', {
anchorX: 0,
anchorY: 0
});
energyBar.x = i * 335; // Space bars evenly (1000/3 ≈ 333)
energyBar.width = 310; // Slightly smaller to have gaps
self.energyBars.push(energyBar);
}
self.update = function () {
// Show energy bars based on current energy level (0-3)
var energyLevel = Math.floor(self.fighter.energy / (self.fighter.maxEnergy / 3));
for (var i = 0; i < 3; i++) {
self.energyBars[i].visible = i < energyLevel;
}
};
return self;
});
var Fighter = Container.expand(function (fighterType, isPlayer) {
var self = Container.call(this);
// Fighter properties
self.fighterType = fighterType || 1;
self.isPlayer = isPlayer || false;
self.maxHealth = 100;
self.health = 100;
self.maxEnergy = 3; // Changed to 3 energy units
self.energy = 3; // Start with full energy
self.speed = 7;
self.isBlocking = false;
self.attackCooldown = 0;
self.specialCooldown = 0;
self.maxSpecialCharge = 100;
self.specialCharge = 0;
self.isCrouching = false;
self.isJumping = false;
self.jumpHeight = 0;
self.maxJumpHeight = 100;
self.jumpSpeed = 15;
self.originalY = 0;
self.gravity = 0.12;
self.velocityY = 0;
self.groundY = 1800; // Ground level for all fighters - adjusted for horizontal layout and moved higher
// Create fighter graphics
var fighterGraphics = self.attachAsset('fighter' + self.fighterType, {
anchorX: 0.5,
anchorY: 1.0
});
// Create blocking shield (initially hidden)
var blockingShield = self.attachAsset('blockAsset', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0.0
});
blockingShield.visible = false;
// Animation properties
self.currentAnimation = 'normal';
self.animationTimer = 0;
self.animationDuration = 0;
self.isWalking = false;
self.isCrouchingAnimated = false;
self.lastX = self.x;
self.walkingAnimationTimer = 0;
// Fighter stats based on type
switch (self.fighterType) {
case 1:
// Red Fighter - Balanced
self.attackDamage = 8;
self.specialDamage = 10;
self.specialCost = 1; // 1 energy bar per special attack
break;
case 2:
// Blue Fighter - Fast
self.attackDamage = 6;
self.specialDamage = 8;
self.specialCost = 1; // 1 energy bar per special attack
self.speed = 4;
break;
case 3:
// Green Fighter - Strong
self.attackDamage = 10;
self.specialDamage = 12;
self.specialCost = 1; // 1 energy bar per special attack
self.speed = 2;
break;
case 4:
// Yellow Fighter - Energy Efficient
self.attackDamage = 7;
self.specialDamage = 9;
self.specialCost = 1; // 1 energy bar per special attack
break;
case 5:
// Purple Fighter - High Health
self.attackDamage = 7;
self.specialDamage = 9;
self.specialCost = 1; // 1 energy bar per special attack
self.maxHealth = 120;
self.health = 120;
break;
}
// Apply difficulty modifiers for enemy fighters
if (!self.isPlayer && gameDifficulty) {
if (gameDifficulty === 'easy') {
self.attackDamage = Math.floor(self.attackDamage * 0.7);
self.specialDamage = Math.floor(self.specialDamage * 0.7);
self.speed = Math.floor(self.speed * 0.8);
self.maxHealth = Math.floor(self.maxHealth * 0.8);
self.health = self.maxHealth;
} else if (gameDifficulty === 'hard') {
self.attackDamage = Math.floor(self.attackDamage * 1.3);
self.specialDamage = Math.floor(self.specialDamage * 1.3);
self.speed = Math.floor(self.speed * 1.2);
self.maxHealth = Math.floor(self.maxHealth * 1.2);
self.health = self.maxHealth;
}
}
// Track if this fighter has taken damage yet
self.hasBeenDamaged = false;
self.takeDamage = function (damage, isSuperProjectile, attacker) {
// Prevent multiple damage calls when fighter is already defeated
if (self.health <= 0 || self.isBeingDefeated) {
return; // No damage taken, fighter is already defeated or being defeated
}
// In team battle mode, prevent damage to fighters that haven't been damaged yet unless they are the current active fighter
if (gameMode === 'teamBattle') {
var isCurrentPlayerFighter = self.isPlayer && self === player;
var isCurrentEnemyFighter = !self.isPlayer && self === enemy;
var isCurrentActiveFighter = isCurrentPlayerFighter || isCurrentEnemyFighter;
// If this fighter hasn't been damaged yet and isn't the current active fighter, make them invulnerable
if (!self.hasBeenDamaged && !isCurrentActiveFighter) {
return; // No damage taken, fighter is invulnerable
}
}
if (self.isPlayer && playerIsBlocking) {
// Check if within perfect block window (0.5 seconds = 30 ticks at 60fps)
var timeSinceBlockStart = LK.ticks - playerBlockStartTime;
if (timeSinceBlockStart <= 30) {
// 30 ticks = 0.5 seconds at 60fps
damage = Math.floor(damage * 0.5); // 50% damage reduction (reduced from 100%)
// Show brief white flash effect during immunity period (like blue flash when blocked)
LK.effects.flashObject(fighterGraphics, 0xffffff, 200);
} else {
if (isSuperProjectile) {
damage = Math.floor(damage * 0.85); // Only 15% damage reduction for super projectiles after perfect block window (85% damage goes through)
} else {
damage = Math.floor(damage * 0.7); // 30% damage reduction for regular attacks after perfect block window
}
}
} else if (self.isBlocking) {
if (isSuperProjectile) {
damage = Math.floor(damage * 0.85); // Only 15% damage reduction when blocking super projectile (85% damage goes through)
} else {
damage = Math.floor(damage * 0.7); // 30% damage reduction for normal blocking (reduced from 50%)
}
}
// Mark fighter as having been damaged (once they take any damage)
if (damage > 0) {
self.hasBeenDamaged = true;
}
self.health = Math.max(0, self.health - damage);
// Charge special attack bar by 2% when taking damage (only if damage > 0)
if (damage > 0) {
self.chargeSpecial(2); // 2% of maxSpecialCharge (100)
}
// Flash red when taking damage
LK.effects.flashObject(fighterGraphics, 0xFF0000, 300);
};
self.useEnergy = function (amount) {
if (self.energy >= amount) {
self.energy -= amount;
return true;
}
return false;
};
self.attack = function (attackType) {
if (self.attackCooldown <= 0) {
self.attackCooldown = 30;
self.lastAttackType = attackType || 'punch';
return true;
}
return false;
};
self.specialAttack = function (specialType) {
if (self.specialCooldown <= 0 && self.useEnergy(self.specialCost)) {
self.specialCooldown = 90;
self.lastSpecialType = specialType || 'range';
return true;
}
return false;
};
self.chargeSpecial = function (amount) {
self.specialCharge = Math.min(self.maxSpecialCharge, self.specialCharge + amount);
};
self.canUseSpecialAttack = function () {
return self.specialCharge >= self.maxSpecialCharge;
};
self.useSpecialAttack = function () {
if (self.canUseSpecialAttack()) {
self.specialCharge = 0;
return true;
}
return false;
};
self.crouch = function () {
if (!self.isJumping) {
self.isCrouching = true;
if (self.currentAnimation === 'normal') {
self.setAnimation('crouching');
self.isCrouchingAnimated = true;
}
}
};
self.stopCrouch = function () {
self.isCrouching = false;
if (self.isCrouchingAnimated && self.currentAnimation === 'crouching') {
self.setAnimation('normal');
self.isCrouchingAnimated = false;
}
};
self.jump = function () {
if (!self.isCrouching && !self.isJumping && self.y >= self.groundY) {
self.isJumping = true;
self.velocityY = -self.jumpSpeed; // Negative velocity for upward movement
}
};
self.setAnimation = function (animationType, duration) {
if (animationType === self.currentAnimation) return;
self.currentAnimation = animationType;
self.animationTimer = duration || 0;
// Store current direction before removing graphics
var currentDirection = fighterGraphics.scaleX;
// Remove current graphics
self.removeChild(fighterGraphics);
// Add new graphics based on animation
var assetName = 'fighter' + self.fighterType;
if (animationType === 'punching') {
assetName = 'punchingAnimation' + self.fighterType;
} else if (animationType === 'kicking') {
assetName = 'kickAnimation' + self.fighterType;
} else if (animationType === 'walking') {
assetName = 'walkingAnimation' + self.fighterType;
} else if (animationType === 'crouching') {
assetName = 'crouchingAnimation' + self.fighterType;
} else if (animationType === 'rangeShooting') {
assetName = 'rangeShoot' + self.fighterType;
} else if (animationType === 'potentShooting') {
assetName = 'potentShoot' + self.fighterType;
}
fighterGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0
});
// Maintain direction after asset change
fighterGraphics.scaleX = currentDirection;
};
self.update = function () {
// Direction checking - determine if asset should face left or right
if (player && enemy) {
var shouldFaceRight = false;
if (self === player) {
shouldFaceRight = enemy.x > player.x; // Player faces enemy
} else if (self === enemy) {
shouldFaceRight = player.x > enemy.x; // Enemy faces player
}
// Update asset direction only when needed to prevent flashing
var currentFacingRight = fighterGraphics.scaleX > 0;
if (currentFacingRight !== shouldFaceRight) {
if (shouldFaceRight) {
fighterGraphics.scaleX = Math.abs(fighterGraphics.scaleX); // Face right
} else {
fighterGraphics.scaleX = -Math.abs(fighterGraphics.scaleX); // Face left
}
}
}
// Walking animation detection
var currentlyWalking = Math.abs(self.x - self.lastX) > 0.5;
if (currentlyWalking && !self.isWalking && self.currentAnimation === 'normal') {
self.setAnimation('walking');
self.isWalking = true;
self.walkingAnimationTimer = 0;
} else if (!currentlyWalking && self.isWalking && self.currentAnimation === 'walking') {
self.setAnimation('normal');
self.isWalking = false;
}
// Crouching animation detection
if (self.isCrouching && !self.isCrouchingAnimated && self.currentAnimation === 'normal') {
self.setAnimation('crouching');
self.isCrouchingAnimated = true;
} else if (!self.isCrouching && self.isCrouchingAnimated && self.currentAnimation === 'crouching') {
self.setAnimation('normal');
self.isCrouchingAnimated = false;
}
self.lastX = self.x;
// Animation management
if (self.animationTimer > 0) {
self.animationTimer--;
if (self.animationTimer <= 0) {
self.setAnimation('normal');
self.isWalking = false;
}
}
// Cooldown management
if (self.attackCooldown > 0) self.attackCooldown--;
if (self.specialCooldown > 0) self.specialCooldown--;
// Energy regeneration - regenerate 1 energy bar every 10 seconds (600 ticks), only one bar at a time
// Initialize lastEnergyRegenTime if not set
if (self.lastEnergyRegenTime === undefined) {
self.lastEnergyRegenTime = 0;
}
// Check if 10 seconds (600 ticks) have passed since last regeneration
if (self.energy < self.maxEnergy && LK.ticks - self.lastEnergyRegenTime >= 600) {
// Only regenerate if we're exactly at a whole number threshold (0, 1, or 2)
var currentEnergyLevel = Math.floor(self.energy);
// Only regenerate if current energy is exactly at the integer threshold
// This ensures sequential regeneration: 0->1, then 1->2, then 2->3
if (self.energy === currentEnergyLevel && currentEnergyLevel < self.maxEnergy) {
self.energy = currentEnergyLevel + 1;
self.lastEnergyRegenTime = LK.ticks; // Update last regeneration time
}
}
// Handle jumping and gravity physics
if (self.isJumping) {
// Apply gravity to vertical velocity
self.velocityY += self.gravity;
// Update position based on velocity
self.y += self.velocityY;
// Check if landed on ground
if (self.y >= self.groundY) {
self.y = self.groundY;
self.isJumping = false;
self.velocityY = 0;
}
} else {
// Ensure fighter stays on ground when not jumping
if (self.y > self.groundY) {
self.y = self.groundY;
}
}
// Handle blocking visual effect
if (self.isBlocking) {
// Show blocking shield
if (!blockingShield.visible) {
blockingShield.visible = true;
blockingShield.alpha = 0.7;
}
// Position shield to always point toward opponent
if (player && enemy) {
var opponent = self === player ? enemy : player;
// Shield always points toward opponent regardless of fighter facing direction
if (opponent.x > self.x) {
blockingShield.x = 100; // Shield on right side (facing right toward opponent)
blockingShield.scaleX = Math.abs(blockingShield.scaleX); // Face right
} else {
blockingShield.x = -100; // Shield on left side (facing left toward opponent)
blockingShield.scaleX = -Math.abs(blockingShield.scaleX); // Face left
}
}
} else {
// Hide blocking shield
if (blockingShield.visible) {
blockingShield.visible = false;
blockingShield.alpha = 0.0;
}
}
// Handle crouching visual effect
if (self.isCrouching && !self.isJumping) {
// Scale down vertically when crouching - slightly bigger hitbox than before
if (fighterGraphics.scaleY > 0.25) {
fighterGraphics.scaleY -= 0.05;
}
} else {
// Return to normal scale
if (fighterGraphics.scaleY < 1.0) {
fighterGraphics.scaleY += 0.05;
}
}
};
return self;
});
var HealthBar = Container.expand(function (fighter, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.fighter = fighter;
var bgBar = self.attachAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
});
var healthBar = self.attachAsset('healthBar', {
anchorX: 0,
anchorY: 0
});
self.update = function () {
var healthPercent = self.fighter.health / self.fighter.maxHealth;
healthBar.width = 1000 * healthPercent;
};
return self;
});
var Projectile = Container.expand(function (x, y, direction, damage, owner) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.direction = direction; // 1 for right, -1 for left
self.damage = damage;
self.owner = owner;
self.speed = 8;
var projectileAsset = 'projectile' + owner.fighterType;
var projectileGraphics = self.attachAsset(projectileAsset, {
anchorX: 0.5,
anchorY: 0.5
});
// Set projectile direction to match shoot direction
if (direction < 0) {
projectileGraphics.scaleX = -Math.abs(projectileGraphics.scaleX); // Face left
} else {
projectileGraphics.scaleX = Math.abs(projectileGraphics.scaleX); // Face right
}
self.update = function () {
self.x += self.speed * self.direction;
};
return self;
});
var SpecialBar = Container.expand(function (fighter, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.fighter = fighter;
var bgBar = self.attachAsset('specialBarBg', {
anchorX: 0,
anchorY: 0
});
var specialBar = self.attachAsset('specialBar', {
anchorX: 0,
anchorY: 0
});
self.update = function () {
var specialPercent = self.fighter.specialCharge / self.fighter.maxSpecialCharge;
specialBar.width = 1000 * specialPercent;
};
return self;
});
var SuperProjectile = Container.expand(function (x, y, direction, damage, owner) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.direction = direction; // 1 for right, -1 for left
self.damage = damage;
self.owner = owner;
self.speed = 6;
self.isSuperProjectile = true;
var superProjectileAsset = 'superProjectile' + owner.fighterType;
var superProjectileGraphics = self.attachAsset(superProjectileAsset, {
anchorX: 0.5,
anchorY: 0.5
});
// Set super projectile direction to match shoot direction
if (direction < 0) {
superProjectileGraphics.scaleX = -Math.abs(superProjectileGraphics.scaleX); // Face left
} else {
superProjectileGraphics.scaleX = Math.abs(superProjectileGraphics.scaleX); // Face right
}
self.update = function () {
self.x += self.speed * self.direction;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2C1810
});
/****
* Game Code
****/
// Add background with random selection for fights - adjusted for horizontal gameplay
var currentBackgroundAsset = 'background'; // Default background
var gameBackground = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1.0,
scaleY: 1.0
});
game.addChild(gameBackground);
// Add menu background (initially visible)
var menuBackground = LK.getAsset('menuBackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(menuBackground);
// Sounds
// Combat elements
// UI elements
// Character assets
// Game states
// Animation assets for different actions
var gameState = 'titleScreen'; // 'titleScreen', 'characterSelect', 'playerSelect', 'teamSelect', 'combat', 'gameOver'
var selectedCharacter = 0; // Initialize to 0 to require selection
var enemyCharacter = 2;
var gameMode = 'normal'; // 'normal', 'playerVsPlayer', 'teamBattle'
var gameDifficulty = ''; // Initialize to empty to require selection
var playerTeam = [];
var enemyTeam = [];
var currentPlayerSelection = 1;
var currentEnemySelection = 1;
var teamSize = 2;
var selectedTeamIndex = 0;
var titleButtons = [];
// Character selection
var characterSelectors = [];
var selectionBorder = null;
// Combat variables
var player = null;
var enemy = null;
var playerHealthBar = null;
var playerEnergyBar = null;
var playerSpecialBar = null;
var enemyHealthBar = null;
var enemyEnergyBar = null;
var enemySpecialBar = null;
var projectiles = [];
var superProjectiles = [];
var punchButton = null;
var kickButton = null;
var rangeButton = null;
var blockButton = null;
var specialButton = null;
var playerBlockStartTime = 0;
var playerIsBlocking = false;
var punchButtonText = null;
var kickButtonText = null;
var rangeButtonText = null;
var blockButtonText = null;
var specialButtonText = null;
var leftMoveButton = null;
var rightMoveButton = null;
var leftMoveButtonText = null;
var rightMoveButtonText = null;
var crouchButton = null;
var jumpButton = null;
var crouchButtonText = null;
var jumpButtonText = null;
var isMovingLeft = false;
var isMovingRight = false;
var enemyIsMovingLeft = false;
var enemyIsMovingRight = false;
var enemyMovementTimer = 0;
var currentPlayerTeamIndex = 0;
var currentEnemyTeamIndex = 0;
var teamPlayerDisplays = [];
var teamEnemyDisplays = [];
var lastPlayerDefeatCheck = -1;
var lastEnemyDefeatCheck = -1;
// UI elements
var titleText = new Text2('AI.nimal Brawlers', {
size: 60,
fill: 0xff0000
});
titleText.anchor.set(0.5, 0);
LK.gui.top.addChild(titleText);
titleText.y = 30;
var instructionText = new Text2('Select Your Fighter', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
// Combat controls text
var controlsText = new Text2('Left: Move | Right: Move | Center Left: Punch | Center Right: Kick | Top: Block | Bottom: Special', {
size: 30,
fill: 0xFFFFFF
});
controlsText.anchor.set(0.5, 1);
function initTitleScreen() {
// Show menu background, hide game background
if (menuBackground) menuBackground.visible = true;
if (gameBackground) gameBackground.visible = false;
// Start menu music
LK.playMusic('menuMusic', {
loop: true
});
titleText.setText('AI.nimal Brawlers');
titleText.y = 200;
// Create title screen buttons
var normalButton = LK.getAsset('playNormalButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalButton.x = 1024;
normalButton.y = 800;
normalButton.buttonType = 'normal';
game.addChild(normalButton);
titleButtons.push(normalButton);
var normalButtonText = new Text2('PLAY NORMAL', {
size: 30,
fill: 0xFFFFFF
});
normalButtonText.anchor.set(0.5, 0.5);
normalButtonText.x = normalButton.x;
normalButtonText.y = normalButton.y;
game.addChild(normalButtonText);
var selectButton = LK.getAsset('selectFightersButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
selectButton.x = 1024;
selectButton.y = 1000;
selectButton.buttonType = 'select';
game.addChild(selectButton);
titleButtons.push(selectButton);
var selectButtonText = new Text2('SELECT FIGHTERS', {
size: 30,
fill: 0xFFFFFF
});
selectButtonText.anchor.set(0.5, 0.5);
selectButtonText.x = selectButton.x;
selectButtonText.y = selectButton.y;
game.addChild(selectButtonText);
var teamButton = LK.getAsset('teamBattleButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
teamButton.x = 1024;
teamButton.y = 1200;
teamButton.buttonType = 'team';
game.addChild(teamButton);
titleButtons.push(teamButton);
var teamButtonText = new Text2('TEAM BATTLE', {
size: 30,
fill: 0xFFFFFF
});
teamButtonText.anchor.set(0.5, 0.5);
teamButtonText.x = teamButton.x;
teamButtonText.y = teamButton.y;
game.addChild(teamButtonText);
var instructionsText = new Text2('Choose your game mode', {
size: 40,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 1024;
instructionsText.y = 500;
game.addChild(instructionsText);
}
function clearTitleScreen() {
for (var i = 0; i < titleButtons.length; i++) {
game.removeChild(titleButtons[i]);
}
titleButtons = [];
// Remove all text elements by checking children
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof Text2) {
game.removeChild(child);
}
}
}
function initPlayerSelection() {
instructionText.setText('Player 1: Select Fighter');
instructionText.y = 150;
game.addChild(instructionText);
// Create character selection grid
for (var i = 1; i <= 5; i++) {
var fighter = LK.getAsset('fighter' + i, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var col = (i - 1) % 5;
var row = Math.floor((i - 1) / 5);
fighter.x = 300 + col * 280;
fighter.y = 400 + row * 300;
fighter.fighterIndex = i;
characterSelectors.push(fighter);
game.addChild(fighter);
}
// Create selection border
selectionBorder = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionBorder.alpha = 0.3;
updateSelectionBorder();
game.addChild(selectionBorder);
// Add difficulty selection for player vs player mode
// Add difficulty selection text
var difficultyText = new Text2('Select Difficulty:', {
size: 35,
fill: 0xFFFFFF
});
difficultyText.anchor.set(0.5, 0.5);
difficultyText.x = 1024;
difficultyText.y = 1800;
game.addChild(difficultyText);
// Easy difficulty button
var easyButton = LK.getAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
easyButton.x = 600;
easyButton.y = 2400;
easyButton.buttonType = 'easy';
if (gameDifficulty === 'easy') {
easyButton.tint = 0xff9500; // Lighter orange when selected
}
game.addChild(easyButton);
titleButtons.push(easyButton);
var easyButtonText = new Text2('EASY', {
size: 30,
fill: 0xFFFFFF
});
easyButtonText.anchor.set(0.5, 0.5);
easyButtonText.x = easyButton.x;
easyButtonText.y = easyButton.y;
game.addChild(easyButtonText);
// Normal difficulty button
var normalDiffButton = LK.getAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalDiffButton.x = 1024;
normalDiffButton.y = 2400;
normalDiffButton.buttonType = 'normalDiff';
game.addChild(normalDiffButton);
titleButtons.push(normalDiffButton);
var normalDiffButtonText = new Text2('NORMAL', {
size: 30,
fill: 0xFFFFFF
});
normalDiffButtonText.anchor.set(0.5, 0.5);
normalDiffButtonText.x = normalDiffButton.x;
normalDiffButtonText.y = normalDiffButton.y;
game.addChild(normalDiffButtonText);
// Hard difficulty button
var hardButton = LK.getAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
hardButton.x = 1448;
hardButton.y = 2400;
hardButton.buttonType = 'hard';
game.addChild(hardButton);
titleButtons.push(hardButton);
var hardButtonText = new Text2('HARD', {
size: 30,
fill: 0xFFFFFF
});
hardButtonText.anchor.set(0.5, 0.5);
hardButtonText.x = hardButton.x;
hardButtonText.y = hardButton.y;
game.addChild(hardButtonText);
// Add start combat button
var startCombatButton = LK.getAsset('startCombatButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
startCombatButton.x = 1024;
startCombatButton.y = 2300;
startCombatButton.buttonType = 'startCombat';
game.addChild(startCombatButton);
titleButtons.push(startCombatButton);
var startCombatButtonText = new Text2('START COMBAT', {
size: 30,
fill: 0xFFFFFF
});
startCombatButtonText.anchor.set(0.5, 0.5);
startCombatButtonText.x = startCombatButton.x;
startCombatButtonText.y = startCombatButton.y;
game.addChild(startCombatButtonText);
// Add back to menu button
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
backButton.x = 1024;
backButton.y = 2600;
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
titleButtons.push(backButton);
var backButtonText = new Text2('BACK TO MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
}
function initTeamSelection() {
instructionText.setText('Select Team Size: ' + teamSize + ' vs ' + teamSize);
instructionText.y = 100;
game.addChild(instructionText);
// Team size buttons
var size2Button = LK.getAsset('team2v2Button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
size2Button.x = 400;
size2Button.y = 200;
size2Button.buttonType = 'size2';
if (teamSize === 2) {
size2Button.tint = 0x44ff44; // Green when selected
}
game.addChild(size2Button);
titleButtons.push(size2Button);
var size2Text = new Text2('2 vs 2', {
size: 30,
fill: 0xFFFFFF
});
size2Text.anchor.set(0.5, 0.5);
size2Text.x = size2Button.x;
size2Text.y = size2Button.y;
game.addChild(size2Text);
var size3Button = LK.getAsset('team3v3Button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
size3Button.x = 800;
size3Button.y = 200;
size3Button.buttonType = 'size3';
if (teamSize === 3) {
size3Button.tint = 0x44ff44; // Green when selected
}
game.addChild(size3Button);
titleButtons.push(size3Button);
var size3Text = new Text2('3 vs 3', {
size: 30,
fill: 0xFFFFFF
});
size3Text.anchor.set(0.5, 0.5);
size3Text.x = size3Button.x;
size3Text.y = size3Button.y;
game.addChild(size3Text);
// Add back to menu button
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
backButton.x = 1024;
backButton.y = 1900;
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
titleButtons.push(backButton);
var backButtonText = new Text2('BACK TO MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
var startButton = LK.getAsset('startTeamSelectionButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
startButton.x = 1024;
startButton.y = 1700;
startButton.buttonType = 'startTeam';
game.addChild(startButton);
titleButtons.push(startButton);
var startText = new Text2('START TEAM SELECTION', {
size: 30,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = startButton.x;
startText.y = startButton.y;
game.addChild(startText);
// Show current teams
updateTeamDisplay();
}
function initTeamFighterSelection() {
// Clear previous state
selectedTeamIndex = 0;
var currentTeam = selectedTeamIndex < teamSize ? 'player' : 'enemy';
var fighterNumber = selectedTeamIndex % teamSize + 1;
var teamName = currentTeam === 'player' ? 'Player' : 'Enemy';
instructionText.setText(teamName + ' Team - Select Fighter ' + fighterNumber);
instructionText.y = 150;
game.addChild(instructionText);
// Create character selection grid
for (var i = 1; i <= 5; i++) {
var fighter = LK.getAsset('fighter' + i, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var col = (i - 1) % 5;
var row = Math.floor((i - 1) / 5);
fighter.x = 300 + col * 280;
fighter.y = 600 + row * 300;
fighter.fighterIndex = i;
characterSelectors.push(fighter);
game.addChild(fighter);
}
// Create selection border
selectionBorder = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionBorder.alpha = 0.3;
selectionBorder.x = characterSelectors[0].x;
selectionBorder.y = characterSelectors[0].y;
game.addChild(selectionBorder);
// Show current team selections below the character grid
updateTeamBattleDisplay();
// Add start combat button
var startCombatButton = LK.getAsset('startCombatButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
startCombatButton.x = 1024;
startCombatButton.y = 1700;
startCombatButton.buttonType = 'startCombat';
game.addChild(startCombatButton);
titleButtons.push(startCombatButton);
var startCombatButtonText = new Text2('START COMBAT', {
size: 30,
fill: 0xFFFFFF
});
startCombatButtonText.anchor.set(0.5, 0.5);
startCombatButtonText.x = startCombatButton.x;
startCombatButtonText.y = startCombatButton.y;
game.addChild(startCombatButtonText);
// Add next selection button
var nextButton = LK.getAsset('rightArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
nextButton.x = 1300;
nextButton.y = 1400;
nextButton.buttonType = 'nextSelection';
game.addChild(nextButton);
titleButtons.push(nextButton);
var nextButtonText = new Text2('NEXT', {
size: 30,
fill: 0xFFFFFF
});
nextButtonText.anchor.set(0.5, 0.5);
nextButtonText.x = nextButton.x;
nextButtonText.y = nextButton.y;
game.addChild(nextButtonText);
// Add previous selection button
var prevButton = LK.getAsset('leftArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
prevButton.x = 700;
prevButton.y = 1400;
prevButton.buttonType = 'prevSelection';
game.addChild(prevButton);
titleButtons.push(prevButton);
var prevButtonText = new Text2('PREV', {
size: 30,
fill: 0xFFFFFF
});
prevButtonText.anchor.set(0.5, 0.5);
prevButtonText.x = prevButton.x;
prevButtonText.y = prevButton.y;
game.addChild(prevButtonText);
// Add back to menu button
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
backButton.x = 1024;
backButton.y = 2100;
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
titleButtons.push(backButton);
var backButtonText = new Text2('BACK TO MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
// Add difficulty selection for team battle mode below back button
// Easy difficulty button
var easyButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
easyButton.x = 600;
easyButton.y = 1900;
easyButton.buttonType = 'easy';
if (gameDifficulty === 'easy') {
easyButton.tint = 0xff9500; // Lighter orange when selected
}
game.addChild(easyButton);
titleButtons.push(easyButton);
var easyButtonText = new Text2('EASY', {
size: 30,
fill: 0xFFFFFF
});
easyButtonText.anchor.set(0.5, 0.5);
easyButtonText.x = easyButton.x;
easyButtonText.y = easyButton.y;
game.addChild(easyButtonText);
// Normal difficulty button
var normalDiffButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalDiffButton.x = 1024;
normalDiffButton.y = 1900;
normalDiffButton.buttonType = 'normalDiff';
if (gameDifficulty === 'normal') {
normalDiffButton.tint = 0xcc6600; // Darker orange when selected
}
game.addChild(normalDiffButton);
titleButtons.push(normalDiffButton);
var normalDiffButtonText = new Text2('NORMAL', {
size: 30,
fill: 0xFFFFFF
});
normalDiffButtonText.anchor.set(0.5, 0.5);
normalDiffButtonText.x = normalDiffButton.x;
normalDiffButtonText.y = normalDiffButton.y;
game.addChild(normalDiffButtonText);
// Hard difficulty button
var hardButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
hardButton.x = 1448;
hardButton.y = 1900;
hardButton.buttonType = 'hard';
if (gameDifficulty === 'hard') {
hardButton.tint = 0xff0000; // Red when selected
}
game.addChild(hardButton);
titleButtons.push(hardButton);
var hardButtonText = new Text2('HARD', {
size: 30,
fill: 0xFFFFFF
});
hardButtonText.anchor.set(0.5, 0.5);
hardButtonText.x = hardButton.x;
hardButtonText.y = hardButton.y;
game.addChild(hardButtonText);
}
function updateTeamBattleDisplay() {
// Remove existing team battle display
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.isTeamBattleDisplay) {
game.removeChild(child);
}
}
// Player team display
var playerTeamText = new Text2('Player Team:', {
size: 35,
fill: 0x44ff44
});
playerTeamText.anchor.set(0.5, 0);
playerTeamText.x = 500;
playerTeamText.y = 900;
playerTeamText.isTeamBattleDisplay = true;
game.addChild(playerTeamText);
for (var i = 0; i < teamSize; i++) {
var fighterType = playerTeam[i] || 1;
var teamFighter = LK.getAsset('fighter' + fighterType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.35,
scaleY: 0.35
});
teamFighter.x = 250 + i * 120;
teamFighter.y = 1100;
teamFighter.isTeamBattleDisplay = true;
// Add selection indicator for current selection
if (selectedTeamIndex === i) {
var selectionIndicator = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionIndicator.alpha = 0.6;
selectionIndicator.tint = 0x00ff00;
selectionIndicator.x = teamFighter.x;
selectionIndicator.y = teamFighter.y;
selectionIndicator.scaleX = 0.4;
selectionIndicator.scaleY = 0.4;
selectionIndicator.isTeamBattleDisplay = true;
game.addChild(selectionIndicator);
}
game.addChild(teamFighter);
}
// Enemy team display
var enemyTeamText = new Text2('Enemy Team:', {
size: 35,
fill: 0xff4444
});
enemyTeamText.anchor.set(0.5, 0);
enemyTeamText.x = 1500;
enemyTeamText.y = 900;
enemyTeamText.isTeamBattleDisplay = true;
game.addChild(enemyTeamText);
for (var i = 0; i < teamSize; i++) {
var fighterType = enemyTeam[i] || (i + 1) % 5 + 1;
var teamFighter = LK.getAsset('fighter' + fighterType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.35,
scaleY: 0.35
});
teamFighter.x = 1250 + i * 120;
teamFighter.y = 1100;
teamFighter.isTeamBattleDisplay = true;
// Add selection indicator for current selection
if (selectedTeamIndex === teamSize + i) {
var selectionIndicator = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionIndicator.alpha = 0.6;
selectionIndicator.tint = 0xff0000;
selectionIndicator.x = teamFighter.x;
selectionIndicator.y = teamFighter.y;
selectionIndicator.scaleX = 0.4;
selectionIndicator.scaleY = 0.4;
selectionIndicator.isTeamBattleDisplay = true;
game.addChild(selectionIndicator);
}
game.addChild(teamFighter);
}
}
function updateTeamDisplay() {
// Remove existing team display
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.isTeamDisplay) {
game.removeChild(child);
}
}
// Player team display
var playerTeamText = new Text2('Player Team:', {
size: 40,
fill: 0x44ff44
});
playerTeamText.anchor.set(0.5, 0);
playerTeamText.x = 500;
playerTeamText.y = 400;
playerTeamText.isTeamDisplay = true;
game.addChild(playerTeamText);
for (var i = 0; i < teamSize; i++) {
var fighterType = playerTeam[i] || i + 1;
var teamFighter = LK.getAsset('fighter' + fighterType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
teamFighter.x = 300 + i * 150;
teamFighter.y = 500;
teamFighter.isTeamDisplay = true;
game.addChild(teamFighter);
}
// Enemy team display
var enemyTeamText = new Text2('Enemy Team:', {
size: 40,
fill: 0xff4444
});
enemyTeamText.anchor.set(0.5, 0);
enemyTeamText.x = 1500;
enemyTeamText.y = 400;
enemyTeamText.isTeamDisplay = true;
game.addChild(enemyTeamText);
for (var i = 0; i < teamSize; i++) {
var fighterType = enemyTeam[i] || (i + 2) % 5 + 1;
var teamFighter = LK.getAsset('fighter' + fighterType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
teamFighter.x = 1300 + i * 150;
teamFighter.y = 500;
teamFighter.isTeamDisplay = true;
game.addChild(teamFighter);
}
}
function initCharacterSelection() {
instructionText.y = 150;
game.addChild(instructionText);
// Create character selection grid
for (var i = 1; i <= 5; i++) {
var fighter = LK.getAsset('fighter' + i, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var col = (i - 1) % 5;
var row = Math.floor((i - 1) / 5);
fighter.x = 300 + col * 280;
fighter.y = 400 + row * 300;
fighter.fighterIndex = i;
characterSelectors.push(fighter);
game.addChild(fighter);
}
// Create selection border
selectionBorder = LK.getAsset('selectionBorder', {
anchorX: 0.5,
anchorY: 0.5
});
selectionBorder.alpha = 0.3;
updateSelectionBorder();
game.addChild(selectionBorder);
// Add difficulty selection if in normal mode
if (gameMode === 'normal') {
// Add difficulty selection text
var difficultyText = new Text2('Select Difficulty:', {
size: 35,
fill: 0xFFFFFF
});
difficultyText.anchor.set(0.5, 0.5);
difficultyText.x = 1024;
difficultyText.y = 700;
game.addChild(difficultyText);
// Easy difficulty button
var easyButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
easyButton.x = 600;
easyButton.y = 2000;
easyButton.buttonType = 'easy';
if (gameDifficulty === 'easy') {
easyButton.tint = 0xff9500; // Lighter orange when selected
}
game.addChild(easyButton);
titleButtons.push(easyButton);
var easyButtonText = new Text2('EASY', {
size: 30,
fill: 0xFFFFFF
});
easyButtonText.anchor.set(0.5, 0.5);
easyButtonText.x = easyButton.x;
easyButtonText.y = easyButton.y;
game.addChild(easyButtonText);
// Normal difficulty button
var normalDiffButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalDiffButton.x = 1024;
normalDiffButton.y = 2000;
normalDiffButton.buttonType = 'normalDiff';
game.addChild(normalDiffButton);
titleButtons.push(normalDiffButton);
var normalDiffButtonText = new Text2('NORMAL', {
size: 30,
fill: 0xFFFFFF
});
normalDiffButtonText.anchor.set(0.5, 0.5);
normalDiffButtonText.x = normalDiffButton.x;
normalDiffButtonText.y = normalDiffButton.y;
game.addChild(normalDiffButtonText);
// Hard difficulty button
var hardButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
hardButton.x = 1448;
hardButton.y = 2000;
hardButton.buttonType = 'hard';
game.addChild(hardButton);
titleButtons.push(hardButton);
var hardButtonText = new Text2('HARD', {
size: 30,
fill: 0xFFFFFF
});
hardButtonText.anchor.set(0.5, 0.5);
hardButtonText.x = hardButton.x;
hardButtonText.y = hardButton.y;
game.addChild(hardButtonText);
}
// Add start combat button
var startCombatButton = LK.getAsset('startCombatButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
startCombatButton.x = 1024;
startCombatButton.y = gameMode === 'normal' ? 1850 : 1800;
startCombatButton.buttonType = 'startCombat';
game.addChild(startCombatButton);
titleButtons.push(startCombatButton);
var startCombatButtonText = new Text2('START COMBAT', {
size: 30,
fill: 0xFFFFFF
});
startCombatButtonText.anchor.set(0.5, 0.5);
startCombatButtonText.x = startCombatButton.x;
startCombatButtonText.y = startCombatButton.y;
game.addChild(startCombatButtonText);
// Add back to menu button
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
backButton.x = 1024;
backButton.y = gameMode === 'normal' ? 2200 : 2000;
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
titleButtons.push(backButton);
var backButtonText = new Text2('BACK TO MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
}
function updateSelectionBorder() {
if (selectionBorder && characterSelectors[selectedCharacter - 1]) {
selectionBorder.x = characterSelectors[selectedCharacter - 1].x;
selectionBorder.y = characterSelectors[selectedCharacter - 1].y;
}
}
function startCombat() {
gameState = 'combat';
// Stop menu music when entering combat
LK.stopMusic();
// Start combat music
LK.playMusic('combatMusic', {
loop: true
});
// Randomly select background for this fight
var backgroundOptions = ['background', 'background2', 'background3'];
var randomBackgroundIndex = Math.floor(Math.random() * backgroundOptions.length);
var selectedBackground = backgroundOptions[randomBackgroundIndex];
currentBackgroundAsset = selectedBackground;
// Remove current game background and create new one with selected background
if (gameBackground) {
game.removeChild(gameBackground);
}
gameBackground = LK.getAsset(selectedBackground, {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1.33,
scaleY: 0.75
});
game.addChild(gameBackground);
// Show game background, hide menu background
if (menuBackground) menuBackground.visible = false;
if (gameBackground) gameBackground.visible = true;
// Clear character selection
game.removeChild(instructionText);
for (var i = 0; i < characterSelectors.length; i++) {
game.removeChild(characterSelectors[i]);
}
game.removeChild(selectionBorder);
// Clear title screen and team displays when starting combat
titleText.visible = false;
clearTitleScreen();
// Remove team battle displays
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.isTeamDisplay || child.isTeamBattleDisplay) {
game.removeChild(child);
}
}
// Reset defeat tracking
lastPlayerDefeatCheck = -1;
lastEnemyDefeatCheck = -1;
// Create fighters - positioned for horizontal gameplay
player = game.addChild(new Fighter(selectedCharacter, true));
player.x = 400;
player.y = 1800;
enemy = game.addChild(new Fighter(enemyCharacter, false));
enemy.x = 1648;
enemy.y = 1800;
// Create health and energy bars - repositioned for horizontal layout
playerHealthBar = game.addChild(new HealthBar(player, -25, 50));
playerEnergyBar = game.addChild(new EnergyBar(player, -25, 90));
playerSpecialBar = game.addChild(new SpecialBar(player, -25, 130));
enemyHealthBar = game.addChild(new HealthBar(enemy, 1100, 50));
enemyEnergyBar = game.addChild(new EnergyBar(enemy, 1100, 90));
enemySpecialBar = game.addChild(new SpecialBar(enemy, 1100, 130));
// Create combat control buttons arranged for horizontal mobile layout (moved to left side)
punchButton = LK.getAsset('punchButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8
});
punchButton.x = 220;
punchButton.y = 2200;
game.addChild(punchButton);
kickButton = LK.getAsset('kickButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 2.1
});
kickButton.x = 600;
kickButton.y = 2200;
game.addChild(kickButton);
blockButton = LK.getAsset('blockButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 2.1
});
blockButton.x = 980;
blockButton.y = 2200;
game.addChild(blockButton);
rangeButton = LK.getAsset('rangeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 2.1
});
rangeButton.x = 220;
rangeButton.y = 2400;
game.addChild(rangeButton);
specialButton = LK.getAsset('specialButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 2.1
});
specialButton.x = 600;
specialButton.y = 2400;
game.addChild(specialButton);
// Create movement buttons arranged for horizontal layout (moved to right side)
leftMoveButton = LK.getAsset('leftArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.9,
scaleY: 1.9
});
leftMoveButton.x = 1550;
leftMoveButton.y = 2400;
game.addChild(leftMoveButton);
rightMoveButton = LK.getAsset('rightArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.9,
scaleY: 1.9
});
rightMoveButton.x = 1850;
rightMoveButton.y = 2400;
game.addChild(rightMoveButton);
// Jump button positioned above movement buttons
jumpButton = LK.getAsset('leftArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.9,
scaleY: 1.9
});
jumpButton.x = 1700;
jumpButton.y = 2150;
game.addChild(jumpButton);
// Crouch button positioned below movement buttons
crouchButton = LK.getAsset('leftArrowButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.9,
scaleY: 1.9
});
crouchButton.x = 1700;
crouchButton.y = 2650;
game.addChild(crouchButton);
// Add button labels
punchButtonText = new Text2('PUNCH', {
size: 30,
fill: 0xFFFFFF
});
punchButtonText.anchor.set(0.5, 0.5);
punchButtonText.x = punchButton.x;
punchButtonText.y = punchButton.y;
game.addChild(punchButtonText);
kickButtonText = new Text2('KICK', {
size: 30,
fill: 0xFFFFFF
});
kickButtonText.anchor.set(0.5, 0.5);
kickButtonText.x = kickButton.x;
kickButtonText.y = kickButton.y;
game.addChild(kickButtonText);
rangeButtonText = new Text2('RANGE', {
size: 30,
fill: 0xFFFFFF
});
rangeButtonText.anchor.set(0.5, 0.5);
rangeButtonText.x = rangeButton.x;
rangeButtonText.y = rangeButton.y;
game.addChild(rangeButtonText);
blockButtonText = new Text2('BLOCK', {
size: 30,
fill: 0xFFFFFF
});
blockButtonText.anchor.set(0.5, 0.5);
blockButtonText.x = blockButton.x;
blockButtonText.y = blockButton.y;
game.addChild(blockButtonText);
specialButtonText = new Text2('SPECIAL', {
size: 30,
fill: 0xFFFFFF
});
specialButtonText.anchor.set(0.5, 0.5);
specialButtonText.x = specialButton.x;
specialButtonText.y = specialButton.y;
game.addChild(specialButtonText);
leftMoveButtonText = new Text2('←', {
size: 60,
fill: 0xFFFFFF
});
leftMoveButtonText.anchor.set(0.5, 0.5);
leftMoveButtonText.x = leftMoveButton.x;
leftMoveButtonText.y = leftMoveButton.y;
game.addChild(leftMoveButtonText);
rightMoveButtonText = new Text2('→', {
size: 60,
fill: 0xFFFFFF
});
rightMoveButtonText.anchor.set(0.5, 0.5);
rightMoveButtonText.x = rightMoveButton.x;
rightMoveButtonText.y = rightMoveButton.y;
game.addChild(rightMoveButtonText);
crouchButtonText = new Text2('↓', {
size: 60,
fill: 0xFFFFFF
});
crouchButtonText.anchor.set(0.5, 0.5);
crouchButtonText.x = crouchButton.x;
crouchButtonText.y = crouchButton.y;
game.addChild(crouchButtonText);
jumpButtonText = new Text2('↑', {
size: 60,
fill: 0xFFFFFF
});
jumpButtonText.anchor.set(0.5, 0.5);
jumpButtonText.x = jumpButton.x;
jumpButtonText.y = jumpButton.y;
game.addChild(jumpButtonText);
// Add back to menu button positioned for horizontal layout
var backButton = LK.getAsset('backToMenuButton', {
anchorX: 0.5,
anchorY: 0.5
});
backButton.x = 1024; // Top center of screen
backButton.y = 50; // Top of screen
backButton.buttonType = 'backToMenu';
game.addChild(backButton);
var backButtonText = new Text2('MENU', {
size: 30,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
game.addChild(backButtonText);
// Add controls text positioned for horizontal layout
controlsText.setText('Left side: Combat actions | Right side: Move & Jump/Crouch');
controlsText.y = 2700;
game.addChild(controlsText);
// Add player and enemy name labels positioned for horizontal layout
var playerLabel = new Text2('Player', {
size: 40,
fill: 0xFFFFFF
});
playerLabel.anchor.set(0, 0);
playerLabel.x = 50;
playerLabel.y = 20;
game.addChild(playerLabel);
var enemyLabel = new Text2('Enemy', {
size: 40,
fill: 0xFFFFFF
});
enemyLabel.anchor.set(1, 0);
enemyLabel.x = 1998;
enemyLabel.y = 20;
game.addChild(enemyLabel);
// Add team displays if in team battle mode
if (gameMode === 'teamBattle') {
// Player team display positioned for horizontal layout
for (var i = 0; i < playerTeam.length; i++) {
var baseScale = 0.3;
var isCurrentFighter = i === currentPlayerTeamIndex;
var teamFighter = LK.getAsset('fighter' + playerTeam[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: isCurrentFighter ? baseScale * 1.2 : baseScale,
scaleY: isCurrentFighter ? baseScale * 1.2 : baseScale
});
teamFighter.x = 100 + i * 60;
teamFighter.y = 200;
teamFighter.teamIndex = i;
teamFighter.isTeamDisplay = true;
teamFighter.tint = 0xffffff; // Normal color for all fighters initially
teamPlayerDisplays.push(teamFighter);
game.addChild(teamFighter);
}
// Enemy team display positioned for horizontal layout
for (var i = 0; i < enemyTeam.length; i++) {
var baseScale = 0.3;
var isCurrentFighter = i === currentEnemyTeamIndex;
var teamFighter = LK.getAsset('fighter' + enemyTeam[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: isCurrentFighter ? baseScale * 1.2 : baseScale,
scaleY: isCurrentFighter ? baseScale * 1.2 : baseScale
});
teamFighter.x = 1600 + i * 60;
teamFighter.y = 200;
teamFighter.teamIndex = i;
teamFighter.isTeamDisplay = true;
teamFighter.tint = 0xffffff; // Normal color for all fighters initially
teamEnemyDisplays.push(teamFighter);
game.addChild(teamFighter);
}
}
}
function handleCombatInput(x, y) {
if (!player || gameState !== 'combat') return;
var gameWidth = 2048;
var gameHeight = 2732;
// Check button presses first
if (punchButton && Math.abs(x - punchButton.x) < 180 && Math.abs(y - punchButton.y) < 72) {
// Punch button pressed
if (player.attack('punch')) {
player.setAnimation('punching', 20); // 20 ticks punch animation
var hit = checkMeleeHit(player, enemy, Math.floor(player.attackDamage * 0.75), 'punch');
if (hit) player.chargeSpecial(15); // Charge special on successful hit
LK.getSound('punchSound').play();
}
return;
}
if (kickButton && Math.abs(x - kickButton.x) < 210 && Math.abs(y - kickButton.y) < 84) {
// Kick button pressed - can't kick while crouching
if (!player.isCrouching && player.attack('kick')) {
player.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
var hit = checkMeleeHit(player, enemy, Math.floor((player.attackDamage + 1) * 0.75), 'kick');
if (hit) player.chargeSpecial(15); // Charge special on successful hit
LK.getSound('kickSound').play();
}
return;
}
if (rangeButton && Math.abs(x - rangeButton.x) < 208 && Math.abs(y - rangeButton.y) < 83) {
// Range attack button pressed
if (enemy && player.specialAttack('range')) {
player.setAnimation('rangeShooting', 15); // 15 ticks range shooting animation
// Determine shoot direction based on enemy position
var shootDirection = enemy.x > player.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust projectile origin based on player stance
var projectileY = player.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (player.isCrouching && !player.isJumping) {
projectileY = player.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (player.isJumping) {
projectileY = player.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
var projectile = new Projectile(player.x + projectileXOffset, projectileY, shootDirection, player.specialDamage, player);
projectiles.push(projectile);
game.addChild(projectile);
player.chargeSpecial(7); // Charge special at 50% rate (15 * 0.5 = 7.5, rounded to 7)
LK.getSound('rangeShoot').play();
}
return;
}
if (specialButton && Math.abs(x - specialButton.x) < 210 && Math.abs(y - specialButton.y) < 84) {
// Special button pressed
if (enemy && player.useSpecialAttack()) {
player.lastSpecialType = 'potent';
// Start charging animation before firing
tween(player, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffaa00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Set potent shooting animation
player.setAnimation('potentShooting', 50); // 50 ticks potent shooting animation - extended duration
// Return to normal size and color after charging
tween(player, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
// Determine shoot direction based on enemy position
var shootDirection = enemy.x > player.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on player stance
var superProjectileY = player.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (player.isCrouching && !player.isJumping) {
superProjectileY = player.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (player.isJumping) {
superProjectileY = player.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
// Create 7 super projectiles fired with spacing
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
var projectileX = player.x + projectileXOffset + (index - 3) * 60; // Horizontal spread in a row
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(player.specialDamage * 2 / 7), player);
// Increase speed for potent projectiles
superProjectile.speed = 8; // Increased speed to match regular projectiles
// Add laser effects for fighter 3
if (player.fighterType === 3) {
// Initial bright laser effect
tween(superProjectile, {
tint: 0xff0000
}, {
duration: 50
});
// Pulsing laser effect
tween(superProjectile, {
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(superProjectile, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// No pushback when firing potent projectiles
// Sound will be played with each projectile instead
}
});
}
return;
}
if (blockButton && Math.abs(x - blockButton.x) < 420 && Math.abs(y - blockButton.y) < 525) {
// Block button pressed
if (!playerIsBlocking) {
playerBlockStartTime = LK.ticks;
playerIsBlocking = true;
}
player.isBlocking = true;
LK.getSound('block').play();
return;
}
if (leftMoveButton && Math.abs(x - leftMoveButton.x) < 142 && Math.abs(y - leftMoveButton.y) < 142) {
// Left movement button pressed
if (!playerIsBlocking && player.x > 200) {
player.stopCrouch(); // Stop crouching when moving left
player.x -= player.speed * 10;
if (player.currentAnimation === 'normal') {
player.setAnimation('walking');
}
}
return;
}
if (rightMoveButton && Math.abs(x - rightMoveButton.x) < 142 && Math.abs(y - rightMoveButton.y) < 142) {
// Right movement button pressed
if (!playerIsBlocking && player.x < 2532) {
player.stopCrouch(); // Stop crouching when moving right
player.x += player.speed * 10;
if (player.currentAnimation === 'normal') {
player.setAnimation('walking');
}
}
return;
}
if (crouchButton && Math.abs(x - crouchButton.x) < 142 && Math.abs(y - crouchButton.y) < 142) {
// Crouch button pressed
player.crouch();
return;
}
if (jumpButton && Math.abs(x - jumpButton.x) < 142 && Math.abs(y - jumpButton.y) < 142) {
// Jump button pressed
player.jump();
if (player.currentAnimation === 'normal') {
player.setAnimation('walking', 30);
}
return;
}
// Movement is now handled by dedicated buttons above
}
function checkMeleeHit(attacker, target, damage, attackType) {
var horizontalDistance = Math.abs(attacker.x - target.x);
var verticalDistance = Math.abs(attacker.y - target.y);
// Bigger horizontal range (600) but smaller vertical range (250)
if (horizontalDistance < 600 && verticalDistance < 250) {
// Punch attacks can't hit crouching targets
if (attackType === 'punch' && target.isCrouching && !target.isJumping) {
return false; // Punch misses crouching target
}
if (target.isBlocking) {
// Attack was blocked - reduced damage and pushback
target.takeDamage(Math.floor(damage * 0.7)); // 30% damage reduction for normal blocking (increased from 70% reduction)
LK.getSound('block').play();
// Apply pushback to the attacker
var pushDirection = attacker.x < target.x ? -1 : 1;
var pushDistance = 60;
var newX = attacker.x + pushDirection * pushDistance;
// Make sure attacker stays within bounds
newX = Math.max(200, Math.min(1848, newX));
// Animate the pushback using tween
tween(attacker, {
x: newX
}, {
duration: 200,
easing: tween.easeOut
});
// Flash the target to show block effect
LK.effects.flashObject(target, 0x4444ff, 300);
return false; // Hit was blocked
} else {
// Normal hit
target.takeDamage(damage);
return true; // Hit was successful
}
}
return false; // No hit
}
function updateAI() {
if (!enemy || !player) return;
var distance = Math.abs(enemy.x - player.x);
// Handle enemy movement holding
if (enemyMovementTimer > 0) {
enemyMovementTimer--;
if (enemyIsMovingLeft && enemy.x > 200) {
enemy.x -= enemy.speed * 2;
// Set walking animation when moving
if (enemy.currentAnimation === 'normal') {
enemy.setAnimation('walking');
}
}
if (enemyIsMovingRight && enemy.x < 2532) {
enemy.x += enemy.speed * 2;
// Set walking animation when moving
if (enemy.currentAnimation === 'normal') {
enemy.setAnimation('walking');
}
}
} else {
enemyIsMovingLeft = false;
enemyIsMovingRight = false;
// Reset walking animation when movement stops
if (enemy.currentAnimation === 'walking') {
enemy.setAnimation('normal');
}
}
// Enhanced AI behavior - mix of range attacks, melee attacks, movement, jumping and crouching
var aiUpdateFrequency = 30;
if (gameDifficulty === 'easy') {
aiUpdateFrequency = 45; // Slower AI reactions
} else if (gameDifficulty === 'hard') {
aiUpdateFrequency = 8; // Much faster AI reactions for hard mode - increased from 15
}
// Hard mode prediction system - track player patterns
if (gameDifficulty === 'hard') {
// Initialize prediction tracking if not set
if (enemy.playerActionHistory === undefined) {
enemy.playerActionHistory = [];
enemy.playerPositionHistory = [];
enemy.lastPlayerX = player.x;
enemy.lastPlayerY = player.y;
enemy.predictedPlayerAction = null;
enemy.predictionConfidence = 0;
}
// Track player movement patterns
var playerMovement = player.x - enemy.lastPlayerX;
if (Math.abs(playerMovement) > 5) {
enemy.playerPositionHistory.push({
x: player.x,
y: player.y,
movement: playerMovement,
tick: LK.ticks
});
// Keep only recent history (last 60 ticks)
if (enemy.playerPositionHistory.length > 10) {
enemy.playerPositionHistory.shift();
}
}
// Track player actions
if (player.currentAnimation !== 'normal' && player.currentAnimation !== 'walking') {
enemy.playerActionHistory.push({
action: player.currentAnimation,
distance: distance,
playerX: player.x,
tick: LK.ticks
});
// Keep only recent action history
if (enemy.playerActionHistory.length > 15) {
enemy.playerActionHistory.shift();
}
}
// Predict player movement direction
var predictedMovement = 0;
if (enemy.playerPositionHistory.length >= 3) {
var recentMovements = enemy.playerPositionHistory.slice(-3);
var avgMovement = 0;
for (var m = 0; m < recentMovements.length; m++) {
avgMovement += recentMovements[m].movement;
}
predictedMovement = avgMovement / recentMovements.length;
}
// Predict likely player action based on pattern analysis
var actionPatterns = {};
for (var h = 0; h < enemy.playerActionHistory.length; h++) {
var historyItem = enemy.playerActionHistory[h];
var distanceRange = Math.floor(historyItem.distance / 200) * 200; // Group by distance ranges
var pattern = historyItem.action + '_' + distanceRange;
actionPatterns[pattern] = (actionPatterns[pattern] || 0) + 1;
}
// Find most likely action at current distance
var currentDistanceRange = Math.floor(distance / 200) * 200;
var mostLikelyAction = null;
var highestCount = 0;
for (var pattern in actionPatterns) {
if (pattern.includes('_' + currentDistanceRange) && actionPatterns[pattern] > highestCount) {
highestCount = actionPatterns[pattern];
mostLikelyAction = pattern.split('_')[0];
}
}
enemy.predictedPlayerAction = mostLikelyAction;
enemy.predictionConfidence = Math.min(highestCount / 3, 1.0); // Max confidence at 3+ occurrences
// Preemptive actions based on predictions
if (enemy.predictionConfidence > 0.6) {
// High confidence predictions - react preemptively
if (enemy.predictedPlayerAction === 'punching' && distance < 600) {
// Preemptively crouch or move away from predicted punch
if (Math.random() < 0.9) {
if (Math.random() < 0.7) {
enemy.crouch();
} else {
// Move away from predicted punch
if (enemy.x > player.x) {
enemyIsMovingRight = true;
enemyMovementTimer = 20;
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 20;
}
}
reactToPlayerAction = true;
}
} else if (enemy.predictedPlayerAction === 'kicking' && distance < 600) {
// Preemptively jump or block predicted kick
if (Math.random() < 0.85) {
if (Math.random() < 0.6) {
enemy.jump();
} else {
enemy.isBlocking = true;
}
reactToPlayerAction = true;
}
} else if (enemy.predictedPlayerAction === 'rangeShooting') {
// Preemptively dodge or block predicted range attack
if (Math.random() < 0.8) {
if (Math.random() < 0.5) {
enemy.jump(); // Jump to avoid
} else if (Math.random() < 0.7) {
enemy.crouch(); // Crouch to avoid
} else {
enemy.isBlocking = true; // Block
}
reactToPlayerAction = true;
}
}
}
// Predictive positioning - move to optimal distance based on predicted movement
if (enemy.predictionConfidence > 0.4 && Math.abs(predictedMovement) > 2) {
var futurePlayerX = player.x + predictedMovement * 3; // Predict 3 frames ahead
var futureDist = Math.abs(enemy.x - futurePlayerX);
// Position for optimal attack range (400-600 pixels)
if (futureDist < 300) {
// Too close - back away
if (enemy.x > futurePlayerX) {
enemyIsMovingRight = true;
enemyMovementTimer = 15;
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 15;
}
reactToPlayerAction = true;
} else if (futureDist > 700) {
// Too far - move closer for attack opportunity
if (enemy.x > futurePlayerX) {
enemyIsMovingLeft = true;
enemyMovementTimer = 20;
} else {
enemyIsMovingRight = true;
enemyMovementTimer = 20;
}
reactToPlayerAction = true;
}
}
// Advanced counter-attack predictions
if (enemy.predictionConfidence > 0.5 && distance < 500) {
// Predict and counter player's most likely next action
if (enemy.predictedPlayerAction === 'punching') {
// Counter predicted punch with kick
if (enemy.attack('kick') && Math.random() < 0.7) {
enemy.setAnimation('kicking', 25);
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15);
LK.getSound('kickSound').play();
reactToPlayerAction = true;
}
} else if (enemy.predictedPlayerAction === 'kicking') {
// Counter predicted kick with range attack
if (enemy.energy >= 1 && enemy.specialAttack() && Math.random() < 0.8) {
enemy.setAnimation('rangeShooting', 15);
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
var projectileY = enemy.y - 280;
var projectile = new Projectile(enemy.x + projectileXOffset, projectileY, shootDirection, enemy.specialDamage, enemy);
projectiles.push(projectile);
game.addChild(projectile);
enemy.chargeSpecial(7);
LK.getSound('rangeShoot').play();
reactToPlayerAction = true;
}
}
}
// Update tracking variables
enemy.lastPlayerX = player.x;
enemy.lastPlayerY = player.y;
}
// Check for player actions to react to (normal and hard modes only)
var reactToPlayerAction = false;
if (gameDifficulty === 'normal' || gameDifficulty === 'hard') {
// Hard mode AI tries to block all attacks
if (gameDifficulty === 'hard') {
// Try to block all incoming attacks with perfect timing
if (player.currentAnimation === 'punching' || player.currentAnimation === 'kicking' || player.currentAnimation === 'rangeShooting' || player.currentAnimation === 'potentShooting') {
if (distance < 1000) {
// Even wider detection range for hard mode - increased from 800
enemy.isBlocking = true;
reactToPlayerAction = true;
}
}
// Try to maintain distance for range attacks - more aggressive
if (distance < 500 && Math.random() < 0.95) {
if (enemy.x > player.x) {
enemyIsMovingRight = true;
enemyMovementTimer = 35; // Hold movement even longer - increased from 25
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 35; // Hold movement even longer - increased from 25
}
reactToPlayerAction = true;
}
// Jump and crouch to avoid projectiles - much more frequently
if (player.currentAnimation === 'rangeShooting' || player.currentAnimation === 'potentShooting') {
if (Math.random() < 0.85) {
if (Math.random() < 0.5) {
enemy.jump(); // Jump to avoid projectiles
} else {
enemy.crouch(); // Crouch to avoid projectiles
}
reactToPlayerAction = true;
}
}
// Attack player based on their position - more aggressively
if (player.isCrouching && distance < 560 && Math.random() < 0.98) {
if (enemy.attack('kick')) {
enemy.setAnimation('kicking', 25);
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15);
LK.getSound('kickSound').play();
reactToPlayerAction = true;
}
}
// Attack jumping player with range/potent attacks - more frequently
if (player.isJumping && distance > 200 && distance < 800) {
if (enemy.energy >= 1 && enemy.specialAttack() && Math.random() < 0.9) {
enemy.setAnimation('rangeShooting', 15);
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
var projectileY = enemy.y - 300; // Aim higher for jumping player
var projectile = new Projectile(enemy.x + projectileXOffset, projectileY, shootDirection, enemy.specialDamage, enemy);
projectiles.push(projectile);
game.addChild(projectile);
enemy.chargeSpecial(7);
LK.getSound('rangeShoot').play();
reactToPlayerAction = true;
}
}
// Counter-attack immediately after blocking
if (enemy.isBlocking && distance < 560 && Math.random() < 0.7) {
// Stop blocking and counter-attack
enemy.isBlocking = false;
if (enemy.attack('punch')) {
enemy.setAnimation('punching', 20);
var hit = checkMeleeHit(enemy, player, Math.floor(enemy.attackDamage * 0.75), 'punch');
if (hit) enemy.chargeSpecial(15);
LK.getSound('punchSound').play();
reactToPlayerAction = true;
}
}
// Use special attacks more aggressively when player is vulnerable
if (player.currentAnimation === 'punching' || player.currentAnimation === 'kicking') {
if (distance > 400 && distance < 700 && enemy.canUseSpecialAttack() && Math.random() < 0.6) {
if (enemy.useSpecialAttack()) {
enemy.setAnimation('potentShooting', 30);
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
var superProjectileY = enemy.y - 280;
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
if (!enemy) return; // Safety check to prevent null access
var projectileX = enemy.x + projectileXOffset + (index - 3) * 60;
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(enemy.specialDamage * 2 / 7), enemy);
superProjectile.speed = 10; // Even faster super projectiles in hard mode
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// Sound will be played with each projectile instead
reactToPlayerAction = true;
}
}
}
} else {
// Normal mode behavior (existing)
// Check if player is punching - try to crouch
if (player.currentAnimation === 'punching' && distance < 560) {
if (Math.random() < 0.7) {
enemy.crouch();
reactToPlayerAction = true;
}
}
// Check if player is kicking - try to jump
else if (player.currentAnimation === 'kicking' && distance < 560) {
if (Math.random() < 0.7) {
enemy.jump();
reactToPlayerAction = true;
}
}
// Check if player is shooting range attacks - try to block
else if (player.currentAnimation === 'rangeShooting') {
if (Math.random() < 0.8) {
enemy.isBlocking = true;
reactToPlayerAction = true;
}
}
// Check if player is crouching - try to kick
else if (player.isCrouching && distance < 560) {
if (Math.random() < 0.6) {
if (enemy.attack('kick')) {
enemy.setAnimation('kicking', 25);
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15);
LK.getSound('kickSound').play();
reactToPlayerAction = true;
}
}
}
}
}
if (LK.ticks % aiUpdateFrequency === 0 && !reactToPlayerAction) {
var actionRange = 12;
if (gameDifficulty === 'easy') {
actionRange = 8; // Less varied AI behavior
} else if (gameDifficulty === 'hard') {
actionRange = 25; // Much more varied and aggressive AI behavior for hard mode - increased from 20
}
var action = Math.floor(Math.random() * actionRange);
// Check if enemy is in melee range (close enough to punch/kick)
if (distance < 560) {
// In melee range - try to attack or block
if (gameDifficulty === 'hard') {
// Hard mode: much more aggressive melee combat with timing prediction
var shouldAttack = action <= 8;
var attackType = 'punch';
// Predict optimal attack timing based on player's current state
if (gameDifficulty === 'hard' && enemy.predictionConfidence > 0.3) {
// If player is likely to move away, time attack for interception
if (enemy.predictedPlayerAction === 'walking' && Math.abs(player.x - enemy.lastPlayerX) > 3) {
// Lead the target - attack where player will be
var futurePlayerX = player.x + (player.x - enemy.lastPlayerX) * 2;
var interceptDistance = Math.abs(enemy.x - futurePlayerX);
if (interceptDistance < 500) {
shouldAttack = true;
attackType = 'kick'; // Kick has longer range
}
}
// If player is in animation recovery, attack with higher probability
if (player.currentAnimation === 'punching' || player.currentAnimation === 'kicking') {
shouldAttack = Math.random() < 0.9; // Very high chance to punish
attackType = 'punch'; // Faster attack for punishment
}
}
if (shouldAttack && enemy.attack()) {
if (attackType === 'kick' && !enemy.isCrouching) {
// Kick attack
enemy.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('kick').play();
} else {
// Punch attack
enemy.setAnimation('punching', 20); // 20 ticks punch animation
var hit = checkMeleeHit(enemy, player, Math.floor(enemy.attackDamage * 0.75), 'punch');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('punchSound').play();
}
} else if (action <= 15 && !enemy.isCrouching && enemy.attack()) {
// Regular kick attacks in hard mode
enemy.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('kick').play();
} else if (action <= 18) {
// Block when not attacking
enemy.isBlocking = true;
} else if (action <= 20) {
// Back away occasionally when too close
if (enemy.x > player.x) {
enemyIsMovingRight = true;
enemyMovementTimer = 15; // Shorter movement in hard mode for more aggression
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 15; // Shorter movement in hard mode for more aggression
}
} else if (action <= 22) {
// Jump more frequently in melee range in hard mode
enemy.jump();
} else {
// Crouch more frequently in melee range in hard mode
enemy.crouch();
}
} else {
// Normal and easy mode: existing behavior
if (action <= 3 && enemy.attack()) {
// Punch attack
enemy.setAnimation('punching', 20); // 20 ticks punch animation
var hit = checkMeleeHit(enemy, player, Math.floor(enemy.attackDamage * 0.75), 'punch');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('punchSound').play();
} else if (action <= 6 && !enemy.isCrouching && enemy.attack()) {
// Kick attack (slightly more damage) - can't kick while crouching
enemy.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
var hit = checkMeleeHit(enemy, player, Math.floor((enemy.attackDamage + 1) * 0.75), 'kick');
if (hit) enemy.chargeSpecial(15); // Charge special on successful hit
LK.getSound('kickSound').play();
} else if (action <= 8) {
// Block when not attacking
enemy.isBlocking = true;
} else if (action <= 9) {
// Back away occasionally when too close
if (enemy.x > player.x) {
enemyIsMovingRight = true;
enemyMovementTimer = 20; // Hold movement for 20 ticks
} else {
enemyIsMovingLeft = true;
enemyMovementTimer = 20; // Hold movement for 20 ticks
}
} else if (action <= 10) {
// Jump occasionally in melee range
enemy.jump();
} else {
// Crouch occasionally in melee range
enemy.crouch();
}
}
} else if (distance < 400) {
// Medium range - move closer for melee or use range attack
if (action <= 4) {
// Move closer to get into melee range
if (enemy.x > player.x) {
enemyIsMovingLeft = true;
enemyMovementTimer = 15; // Hold movement for 15 ticks
} else {
enemyIsMovingRight = true;
enemyMovementTimer = 15; // Hold movement for 15 ticks
}
} else if (action <= 6 && enemy.canUseSpecialAttack() && enemy.useSpecialAttack()) {
// Use super special attack with charging animation
tween(enemy, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffaa00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Set potent shooting animation
enemy.setAnimation('potentShooting', 50); // 50 ticks potent shooting animation - extended duration
// Return to normal size and color after charging
tween(enemy, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
// Determine shoot direction based on player position
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on enemy stance
var superProjectileY = enemy.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (enemy.isCrouching && !enemy.isJumping) {
superProjectileY = enemy.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (enemy.isJumping) {
superProjectileY = enemy.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
// Create 7 super projectiles fired with spacing
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
if (!enemy) return; // Safety check to prevent null access
var projectileX = enemy.x + projectileXOffset + (index - 3) * 60; // Horizontal spread in a row
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(enemy.specialDamage * 2 / 7), enemy);
// Increase speed for AI potent projectiles
superProjectile.speed = 8; // Increased speed to match regular projectiles
// Add laser effects for fighter 3
if (enemy.fighterType === 3) {
// Initial bright laser effect
tween(superProjectile, {
tint: 0xff0000
}, {
duration: 50
});
// Pulsing laser effect
tween(superProjectile, {
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(superProjectile, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// No pushback when firing potent projectiles
// Sound will be played with each projectile instead
}
});
} else if (action <= 6 && enemy.energy >= 1 && enemy.specialAttack()) {
// Use range attack (reduced frequency)
enemy.setAnimation('rangeShooting', 15); // 15 ticks range shooting animation
// Determine shoot direction based on player position
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust projectile origin based on enemy stance
var projectileY = enemy.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (enemy.isCrouching && !enemy.isJumping) {
projectileY = enemy.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (enemy.isJumping) {
projectileY = enemy.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
var projectile = new Projectile(enemy.x + projectileXOffset, projectileY, shootDirection, enemy.specialDamage, enemy);
projectiles.push(projectile);
game.addChild(projectile);
enemy.chargeSpecial(7); // Charge special at 50% rate (15 * 0.5 = 7.5, rounded to 7)
LK.getSound('rangeShoot').play();
} else if (action <= 8) {
// Block occasionally
enemy.isBlocking = true;
} else if (action <= 9) {
// Jump occasionally at medium range
enemy.jump();
} else {
// Crouch occasionally at medium range
enemy.crouch();
}
} else {
// Long range - use range attacks or move closer
if (action <= 2 && enemy.canUseSpecialAttack() && enemy.useSpecialAttack()) {
// Use super special attack when available (reduced frequency) with charging animation
tween(enemy, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffaa00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Set potent shooting animation
enemy.setAnimation('potentShooting', 50); // 50 ticks potent shooting animation - extended duration
// Return to normal size and color after charging
tween(enemy, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
// Determine shoot direction based on player position
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on enemy stance
var superProjectileY = enemy.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (enemy.isCrouching && !enemy.isJumping) {
superProjectileY = enemy.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (enemy.isJumping) {
superProjectileY = enemy.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
// Create 7 super projectiles fired with spacing
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
if (!enemy) return; // Safety check to prevent null access
var projectileX = enemy.x + projectileXOffset + (index - 3) * 60; // Horizontal spread in a row
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(enemy.specialDamage * 2 / 7), enemy);
// Increase speed for AI long range potent projectiles
superProjectile.speed = 8; // Increased speed to match regular projectiles
// Add laser effects for fighter 3
if (enemy.fighterType === 3) {
// Initial bright laser effect
tween(superProjectile, {
tint: 0xff0000
}, {
duration: 50
});
// Pulsing laser effect
tween(superProjectile, {
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(superProjectile, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// No pushback when firing potent projectiles
// Sound will be played with each projectile instead
}
});
} else if (action <= 3 && enemy.energy >= 1 && enemy.specialAttack()) {
// Use range attack when available (reduced frequency)
enemy.setAnimation('rangeShooting', 15); // 15 ticks range shooting animation
// Determine shoot direction based on player position
var shootDirection = player.x > enemy.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on enemy stance
var projectileY = enemy.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (enemy.isCrouching && !enemy.isJumping) {
projectileY = enemy.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (enemy.isJumping) {
projectileY = enemy.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
var projectile = new Projectile(enemy.x + projectileXOffset, projectileY, shootDirection, enemy.specialDamage, enemy);
projectiles.push(projectile);
game.addChild(projectile);
enemy.chargeSpecial(7); // Charge special at 50% rate (15 * 0.5 = 7.5, rounded to 7)
LK.getSound('rangeShoot').play();
} else if (action <= 8) {
// Move closer to player for melee combat
if (enemy.x > player.x) {
enemyIsMovingLeft = true;
enemyMovementTimer = 25; // Hold movement for 25 ticks
} else {
enemyIsMovingRight = true;
enemyMovementTimer = 25; // Hold movement for 25 ticks
}
} else if (action <= 9) {
// Occasionally block at range
enemy.isBlocking = true;
} else if (action <= 10) {
// Jump occasionally at long range
enemy.jump();
} else {
// Crouch occasionally at long range
enemy.crouch();
}
}
}
// Reset blocking and crouching periodically
var resetFrequency = 45;
if (gameDifficulty === 'hard') {
resetFrequency = 25; // Much faster reset in hard mode for more responsive AI
}
if (LK.ticks % resetFrequency === 0) {
enemy.isBlocking = false;
enemy.stopCrouch(); // Release crouch periodically
}
}
function updateCombatTeamDisplays() {
if (gameMode !== 'teamBattle') return;
var baseScale = 0.3;
var activeScale = baseScale * 1.2;
// Update player team displays
for (var i = 0; i < teamPlayerDisplays.length; i++) {
var fighter = teamPlayerDisplays[i];
if (i < currentPlayerTeamIndex) {
// Defeated fighter - grey tint
tween(fighter, {
tint: 0x666666,
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 300,
easing: tween.easeOut
});
} else if (i === currentPlayerTeamIndex) {
// Active fighter - normal color and bigger
tween(fighter, {
tint: 0xffffff,
scaleX: activeScale,
scaleY: activeScale
}, {
duration: 300,
easing: tween.easeOut
});
} else {
// Not yet active fighter - normal color and size
tween(fighter, {
tint: 0xffffff,
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Update enemy team displays
for (var i = 0; i < teamEnemyDisplays.length; i++) {
var fighter = teamEnemyDisplays[i];
if (i < currentEnemyTeamIndex) {
// Defeated fighter - grey tint
tween(fighter, {
tint: 0x666666,
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 300,
easing: tween.easeOut
});
} else if (i === currentEnemyTeamIndex) {
// Active fighter - normal color and bigger
tween(fighter, {
tint: 0xffffff,
scaleX: activeScale,
scaleY: activeScale
}, {
duration: 300,
easing: tween.easeOut
});
} else {
// Not yet active fighter - normal color and size
tween(fighter, {
tint: 0xffffff,
scaleX: baseScale,
scaleY: baseScale
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
function switchToNextTeamFighter(isPlayer) {
if (gameMode !== 'teamBattle') return false;
if (isPlayer) {
// Check if player is already being defeated to prevent double switching
if (player.isBeingDefeated || lastPlayerDefeatCheck === currentPlayerTeamIndex) return false;
player.isBeingDefeated = true;
lastPlayerDefeatCheck = currentPlayerTeamIndex;
// Flash defeated fighter red before switching
LK.getSound('defeated').play();
tween(player, {
tint: 0xff0000
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Mark current fighter as defeated (grey)
if (teamPlayerDisplays[currentPlayerTeamIndex]) {
tween(teamPlayerDisplays[currentPlayerTeamIndex], {
tint: 0x333333
}, {
duration: 500,
easing: tween.easeOut
});
}
currentPlayerTeamIndex++;
if (currentPlayerTeamIndex < playerTeam.length) {
// Switch to next player team fighter
selectedCharacter = playerTeam[currentPlayerTeamIndex];
// Remove current player
player.destroy();
game.removeChild(player);
// Create new player
player = game.addChild(new Fighter(selectedCharacter, true));
player.x = 300;
player.y = 1800;
// Flash new fighter with immunity
tween(player, {
tint: 0xffffff,
alpha: 0.5
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
alpha: 1.0,
tint: 0x44ff44
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
tint: 0xffffff
}, {
duration: 400,
easing: tween.easeOut
});
}
});
}
});
// Update health bars to reference new fighter
playerHealthBar.fighter = player;
playerEnergyBar.fighter = player;
playerSpecialBar.fighter = player;
// Update team displays to show new states
updateCombatTeamDisplays();
}
}
});
return true;
} else {
// Check if enemy is already being defeated to prevent double switching
if (enemy.isBeingDefeated || lastEnemyDefeatCheck === currentEnemyTeamIndex) return false;
enemy.isBeingDefeated = true;
lastEnemyDefeatCheck = currentEnemyTeamIndex;
// Flash defeated fighter red before switching
LK.getSound('defeated').play();
tween(enemy, {
tint: 0xff0000
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Mark current fighter as defeated (grey)
if (teamEnemyDisplays[currentEnemyTeamIndex]) {
tween(teamEnemyDisplays[currentEnemyTeamIndex], {
tint: 0x333333
}, {
duration: 500,
easing: tween.easeOut
});
}
currentEnemyTeamIndex++;
if (currentEnemyTeamIndex < enemyTeam.length) {
// Switch to next enemy team fighter
enemyCharacter = enemyTeam[currentEnemyTeamIndex];
// Remove current enemy
enemy.destroy();
game.removeChild(enemy);
// Create new enemy
enemy = game.addChild(new Fighter(enemyCharacter, false));
enemy.x = 2432;
enemy.y = 1800;
// Flash new fighter with immunity
tween(enemy, {
tint: 0xffffff,
alpha: 0.5
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(enemy, {
alpha: 1.0,
tint: 0x44ff44
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(enemy, {
tint: 0xffffff
}, {
duration: 400,
easing: tween.easeOut
});
}
});
}
});
// Update health bars to reference new fighter
enemyHealthBar.fighter = enemy;
enemyEnergyBar.fighter = enemy;
enemySpecialBar.fighter = enemy;
// Update team displays to show new states
updateCombatTeamDisplays();
}
}
});
return true;
}
return false;
}
function checkGameOver() {
if (player && player.health <= 0) {
// Try to switch to next team fighter
if (gameMode === 'teamBattle') {
// Only check game over if we can't switch to another fighter
var switched = switchToNextTeamFighter(true);
if (!switched) {
// Check if all team fighters are actually defeated
var allPlayerDefeated = true;
for (var i = 0; i < playerTeam.length; i++) {
if (i > currentPlayerTeamIndex) {
allPlayerDefeated = false;
break;
}
}
if (allPlayerDefeated) {
// All team fighters defeated - game over
LK.showGameOver();
}
}
} else {
LK.getSound('defeated').play();
LK.showGameOver();
}
} else if (enemy && enemy.health <= 0) {
// Try to switch to next team fighter
if (gameMode === 'teamBattle') {
// Only check win condition if we can't switch to another fighter
var switched = switchToNextTeamFighter(false);
if (!switched) {
// Check if all enemy team fighters are actually defeated
var allEnemyDefeated = true;
for (var i = 0; i < enemyTeam.length; i++) {
if (i > currentEnemyTeamIndex) {
allEnemyDefeated = false;
break;
}
}
if (allEnemyDefeated) {
// All team fighters defeated - player wins
LK.showYouWin();
}
}
} else {
LK.getSound('defeated').play();
LK.showYouWin();
}
}
}
// Character selection input
game.down = function (x, y, obj) {
// Check back to menu button for all states
for (var i = 0; i < game.children.length; i++) {
var child = game.children[i];
if (child.buttonType === 'backToMenu' && Math.abs(x - child.x) < 200 && Math.abs(y - child.y) < 80) {
// Clear current game state and return to title screen
gameState = 'titleScreen';
// Remove all game objects
for (var k = game.children.length - 1; k >= 0; k--) {
game.removeChild(game.children[k]);
}
game.addChild(gameBackground);
game.addChild(menuBackground);
// Show menu background, hide game background
if (menuBackground) menuBackground.visible = true;
if (gameBackground) gameBackground.visible = false;
// Reset variables
player = null;
enemy = null;
projectiles = [];
superProjectiles = [];
teamPlayerDisplays = [];
teamEnemyDisplays = [];
currentPlayerTeamIndex = 0;
currentEnemyTeamIndex = 0;
selectedTeamIndex = 0;
characterSelectors = [];
titleButtons = [];
selectionBorder = null;
currentPlayerSelection = 1;
titleText.visible = true;
// Restart menu music when returning to title screen
LK.playMusic('menuMusic', {
loop: true
});
initTitleScreen();
return;
}
}
if (gameState === 'titleScreen') {
// Check title screen buttons
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'normal') {
gameMode = 'normal';
titleText.visible = false;
clearTitleScreen();
gameState = 'characterSelect';
initCharacterSelection();
} else if (button.buttonType === 'select') {
gameMode = 'teamBattle';
teamSize = 1;
titleText.visible = false;
clearTitleScreen();
gameState = 'teamFighterSelect';
// Initialize teams for 1v1
playerTeam = [];
enemyTeam = [];
selectedTeamIndex = 0;
initTeamFighterSelection();
} else if (button.buttonType === 'team') {
gameMode = 'teamBattle';
titleText.visible = false;
clearTitleScreen();
gameState = 'teamSelect';
initTeamSelection();
}
break;
}
}
} else if (gameState === 'teamSelect') {
// Check team selection buttons
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'easy') {
gameDifficulty = 'easy';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'easy') {
diffButton.tint = 0xff9500; // Lighter orange for easy
} else if (diffButton.buttonType === 'normalDiff' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'normalDiff') {
gameDifficulty = 'normal';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xcc6600; // Darker orange for normal
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'hard') {
gameDifficulty = 'hard';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'hard') {
diffButton.tint = 0xff0000; // Red for hard
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'size2') {
teamSize = 2;
playerTeam = [];
enemyTeam = [];
instructionText.setText('Select Team Size: ' + teamSize + ' vs ' + teamSize);
updateTeamDisplay();
// Update button colors to show selection
for (var k = 0; k < titleButtons.length; k++) {
var sizeButton = titleButtons[k];
if (sizeButton.buttonType === 'size2') {
sizeButton.tint = 0x44ff44; // Green when selected
} else if (sizeButton.buttonType === 'size3') {
sizeButton.tint = 0xffffff; // White when not selected
}
}
} else if (button.buttonType === 'size3') {
teamSize = 3;
playerTeam = [];
enemyTeam = [];
instructionText.setText('Select Team Size: ' + teamSize + ' vs ' + teamSize);
updateTeamDisplay();
// Update button colors to show selection
for (var k = 0; k < titleButtons.length; k++) {
var sizeButton = titleButtons[k];
if (sizeButton.buttonType === 'size3') {
sizeButton.tint = 0x44ff44; // Green when selected
} else if (sizeButton.buttonType === 'size2') {
sizeButton.tint = 0xffffff; // White when not selected
}
}
} else if (button.buttonType === 'startTeam') {
// Remove team display elements
for (var k = game.children.length - 1; k >= 0; k--) {
var child = game.children[k];
if (child.isTeamDisplay) {
game.removeChild(child);
}
}
// Initialize teams if empty
for (var j = 0; j < teamSize; j++) {
if (!playerTeam[j]) playerTeam[j] = j + 1;
if (!enemyTeam[j]) enemyTeam[j] = (j + 2) % 5 + 1;
}
// Reset team indices
currentPlayerTeamIndex = 0;
currentEnemyTeamIndex = 0;
clearTitleScreen();
gameState = 'teamFighterSelect';
initTeamFighterSelection();
}
break;
}
}
} else if (gameState === 'characterSelect') {
// Check difficulty buttons first
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'easy') {
gameDifficulty = 'easy';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'easy') {
diffButton.tint = 0xff9500; // Lighter orange for easy
} else if (diffButton.buttonType === 'normalDiff' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'normalDiff') {
gameDifficulty = 'normal';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xcc6600; // Darker orange for normal
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'hard') {
gameDifficulty = 'hard';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'hard') {
diffButton.tint = 0xff0000; // Red for hard
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
}
}
}
// Check for start combat button
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (button.buttonType === 'startCombat' && Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
// Check if character is selected and difficulty is selected before starting combat
if (!selectedCharacter || selectedCharacter === 0) {
// Flash screen to indicate character must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
if (!gameDifficulty || gameDifficulty === '') {
// Flash screen to indicate difficulty must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
enemyCharacter = selectedCharacter % 5 + 1; // Simple enemy selection
storage.lastPlayerFighter = selectedCharacter;
storage.lastEnemyFighter = enemyCharacter;
// Clear character selection
game.removeChild(instructionText);
for (var j = 0; j < characterSelectors.length; j++) {
game.removeChild(characterSelectors[j]);
}
game.removeChild(selectionBorder);
characterSelectors = [];
gameState = 'combat';
startCombat();
return;
}
}
// Check which character was selected
for (var i = 0; i < characterSelectors.length; i++) {
var fighter = characterSelectors[i];
// Use x,y coordinates directly since they're already in game coordinates
if (Math.abs(x - fighter.x) < 100 && Math.abs(y - fighter.y) < 150) {
selectedCharacter = fighter.fighterIndex;
storage.lastPlayerFighter = selectedCharacter;
updateSelectionBorder();
break;
}
}
} else if (gameState === 'playerSelect') {
// Check difficulty buttons first
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'easy') {
gameDifficulty = 'easy';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'easy') {
diffButton.tint = 0xff9500; // Lighter orange for easy
} else if (diffButton.buttonType === 'normalDiff' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'normalDiff') {
gameDifficulty = 'normal';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xcc6600; // Darker orange for normal
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'hard') {
gameDifficulty = 'hard';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'hard') {
diffButton.tint = 0xff0000; // Red for hard
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
}
}
}
// Check for start combat button
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (button.buttonType === 'startCombat' && Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
// Check if both characters are selected and difficulty is selected before starting combat
if (currentPlayerSelection !== 2 || !enemyCharacter) {
// Flash screen to indicate both characters must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
if (!gameDifficulty || gameDifficulty === '') {
// Flash screen to indicate difficulty must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
storage.lastEnemyFighter = enemyCharacter;
// Clear character selection
game.removeChild(instructionText);
for (var j = 0; j < characterSelectors.length; j++) {
game.removeChild(characterSelectors[j]);
}
game.removeChild(selectionBorder);
characterSelectors = [];
gameState = 'combat';
startCombat();
return;
}
}
// Check which character was selected for player vs player
for (var i = 0; i < characterSelectors.length; i++) {
var fighter = characterSelectors[i];
if (Math.abs(x - fighter.x) < 100 && Math.abs(y - fighter.y) < 150) {
if (currentPlayerSelection === 1) {
selectedCharacter = fighter.fighterIndex;
storage.lastPlayerFighter = selectedCharacter;
currentPlayerSelection = 2;
instructionText.setText('Player 2: Select Fighter');
} else {
enemyCharacter = fighter.fighterIndex;
}
updateSelectionBorder();
break;
}
}
} else if (gameState === 'teamFighterSelect') {
// Check difficulty buttons first
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
if (button.buttonType === 'easy') {
gameDifficulty = 'easy';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'easy') {
diffButton.tint = 0xff9500; // Lighter orange for easy
} else if (diffButton.buttonType === 'normalDiff' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'normalDiff') {
gameDifficulty = 'normal';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xcc6600; // Darker orange for normal
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'hard') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
} else if (button.buttonType === 'hard') {
gameDifficulty = 'hard';
// Update button colors
for (var k = 0; k < titleButtons.length; k++) {
var diffButton = titleButtons[k];
if (diffButton.buttonType === 'hard') {
diffButton.tint = 0xff0000; // Red for hard
} else if (diffButton.buttonType === 'easy' || diffButton.buttonType === 'normalDiff') {
diffButton.tint = 0xffffff; // White for others
}
}
return;
}
}
}
// Check for next/previous selection buttons
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (Math.abs(x - button.x) < 150 && Math.abs(y - button.y) < 150) {
if (button.buttonType === 'nextSelection') {
// Move to next selection if not at the end
if (selectedTeamIndex < teamSize * 2 - 1) {
selectedTeamIndex++;
var currentTeam = selectedTeamIndex < teamSize ? 'player' : 'enemy';
var fighterNumber = selectedTeamIndex % teamSize + 1;
var teamName = currentTeam === 'player' ? 'Player' : 'Enemy';
instructionText.setText(teamName + ' Team - Select Fighter ' + fighterNumber);
updateTeamBattleDisplay();
}
return;
} else if (button.buttonType === 'prevSelection') {
// Move to previous selection if not at the beginning
if (selectedTeamIndex > 0) {
selectedTeamIndex--;
var currentTeam = selectedTeamIndex < teamSize ? 'player' : 'enemy';
var fighterNumber = selectedTeamIndex % teamSize + 1;
var teamName = currentTeam === 'player' ? 'Player' : 'Enemy';
instructionText.setText(teamName + ' Team - Select Fighter ' + fighterNumber);
updateTeamBattleDisplay();
}
return;
}
}
}
// Check for start combat button
for (var i = 0; i < titleButtons.length; i++) {
var button = titleButtons[i];
if (button.buttonType === 'startCombat' && Math.abs(x - button.x) < 200 && Math.abs(y - button.y) < 80) {
// Check if all fighters are selected and difficulty is selected before starting combat
// Need to check if both teams have at least one fighter selected
var playerTeamComplete = true;
var enemyTeamComplete = true;
for (var t = 0; t < teamSize; t++) {
if (!playerTeam[t] || playerTeam[t] === 0) playerTeamComplete = false;
if (!enemyTeam[t] || enemyTeam[t] === 0) enemyTeamComplete = false;
}
if (!playerTeamComplete || !enemyTeamComplete) {
// Flash screen to indicate all fighters must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
if (!gameDifficulty || gameDifficulty === '') {
// Flash screen to indicate difficulty must be selected
LK.effects.flashScreen(0xff0000, 500);
return;
}
// All fighters selected, start combat
// Clear character selection
game.removeChild(instructionText);
for (var j = 0; j < characterSelectors.length; j++) {
game.removeChild(characterSelectors[j]);
}
game.removeChild(selectionBorder);
characterSelectors = [];
gameState = 'combat';
selectedCharacter = playerTeam[0];
enemyCharacter = enemyTeam[0];
startCombat();
return;
}
}
// Check which character was selected for team battle
for (var i = 0; i < characterSelectors.length; i++) {
var fighter = characterSelectors[i];
if (Math.abs(x - fighter.x) < 100 && Math.abs(y - fighter.y) < 150) {
// Determine which team and position we're selecting for
if (selectedTeamIndex < teamSize) {
// Selecting for player team
playerTeam[selectedTeamIndex] = fighter.fighterIndex;
} else {
// Selecting for enemy team
enemyTeam[selectedTeamIndex - teamSize] = fighter.fighterIndex;
}
// Don't auto-advance selectedTeamIndex - let user manually advance or change selection
// Update display for current selection
var currentTeam = selectedTeamIndex < teamSize ? 'player' : 'enemy';
var fighterNumber = selectedTeamIndex % teamSize + 1;
var teamName = currentTeam === 'player' ? 'Player' : 'Enemy';
instructionText.setText(teamName + ' Team - Fighter ' + fighterNumber + ' selected. Click another fighter to change or advance to next selection.');
updateTeamBattleDisplay();
break;
}
}
} else if (gameState === 'combat') {
handleCombatInput(x, y);
}
};
game.move = function (x, y, obj) {
if (gameState === 'combat' && player && !playerIsBlocking) {
// Check if moving over left button
if (leftMoveButton && Math.abs(x - leftMoveButton.x) < 142 && Math.abs(y - leftMoveButton.y) < 142) {
isMovingLeft = true;
player.stopCrouch(); // Stop crouching when moving left
} else {
isMovingLeft = false;
}
// Check if moving over right button
if (rightMoveButton && Math.abs(x - rightMoveButton.x) < 142 && Math.abs(y - rightMoveButton.y) < 142) {
isMovingRight = true;
player.stopCrouch(); // Stop crouching when moving right
} else {
isMovingRight = false;
}
// Check if holding crouch button
if (crouchButton && Math.abs(x - crouchButton.x) < 142 && Math.abs(y - crouchButton.y) < 142) {
player.crouch();
}
}
};
game.up = function (x, y, obj) {
if (player && gameState === 'combat') {
player.isBlocking = false;
playerIsBlocking = false;
isMovingLeft = false;
isMovingRight = false;
player.stopCrouch();
// Reset walking animation to normal when movement stops
if (player.currentAnimation === 'walking') {
player.setAnimation('normal');
}
}
};
game.update = function () {
if (gameState === 'combat') {
// Update fighters
if (player) player.update();
if (enemy) enemy.update();
// Update UI bars
if (playerHealthBar) playerHealthBar.update();
if (playerEnergyBar) playerEnergyBar.update();
if (playerSpecialBar) playerSpecialBar.update();
if (enemyHealthBar) enemyHealthBar.update();
if (enemyEnergyBar) enemyEnergyBar.update();
if (enemySpecialBar) enemySpecialBar.update();
// Update button availability visual feedback
if (rangeButton && rangeButtonText && player) {
if (player.energy >= player.specialCost) {
// Available - normal colors
tween.stop(rangeButton, {
tint: true
});
tween.stop(rangeButtonText, {
tint: true
});
rangeButton.tint = 0xFFFFFF; // White button when available
rangeButtonText.tint = 0xFFFFFF; // White text when available
} else {
// Not available - grey colors
tween.stop(rangeButton, {
tint: true
});
tween.stop(rangeButtonText, {
tint: true
});
rangeButton.tint = 0x666666; // Gray button when not available
rangeButtonText.tint = 0x666666; // Gray text when not available
}
}
if (specialButton && specialButtonText && player) {
if (player.canUseSpecialAttack()) {
// Available - normal colors
tween.stop(specialButton, {
tint: true
});
tween.stop(specialButtonText, {
tint: true
});
specialButton.tint = 0xFFFFFF; // White button when available
specialButtonText.tint = 0xFFFFFF; // White text when available
} else {
// Not available - grey colors
tween.stop(specialButton, {
tint: true
});
tween.stop(specialButtonText, {
tint: true
});
specialButton.tint = 0x666666; // Gray button when not available
specialButtonText.tint = 0x666666; // Gray text when not available
}
}
// Update super projectiles
for (var i = superProjectiles.length - 1; i >= 0; i--) {
var superProj = superProjectiles[i];
superProj.update();
// Check for super projectile clashes
for (var j = superProjectiles.length - 1; j >= 0; j--) {
if (i !== j) {
var otherSuperProj = superProjectiles[j];
if (Math.abs(superProj.x - otherSuperProj.x) < 80 && Math.abs(superProj.y - otherSuperProj.y) < 80) {
// Super projectiles clash - destroy both
LK.effects.flashScreen(0xffffff, 200);
LK.getSound('projectileClash').play();
superProj.destroy();
otherSuperProj.destroy();
superProjectiles.splice(Math.max(i, j), 1);
superProjectiles.splice(Math.min(i, j), 1);
i = Math.min(i, j) - 1; // Adjust index
break;
}
}
}
if (i >= 0 && i < superProjectiles.length) {
var superProj = superProjectiles[i];
// Check super projectile hits
var target = superProj.owner === player ? enemy : player;
if (target && Math.abs(superProj.x - target.x) < 200 && Math.abs(superProj.y - target.y) < 300) {
// Check if super projectile should miss crouching target
// Standing super projectiles (fired from y-280) should miss crouching targets
var superProjectileFromStanding = Math.abs(superProj.y - (superProj.owner.y - 280)) < 20;
if (target.isCrouching && !target.isJumping && superProjectileFromStanding) {
// Super projectile passes over crouching target - no hit
// Remove off-screen super projectiles
if (superProj.x < -50 || superProj.x > 2782) {
superProj.destroy();
superProjectiles.splice(i, 1);
}
continue; // Skip hit detection for crouching dodge
}
if (target.isBlocking) {
// Super projectile was blocked - apply 50% damage and pushback
target.takeDamage(superProj.damage, true, superProj.owner);
LK.getSound('block').play();
// No pushback for shooter when projectile impacts on block
// Push back the receiver (target) - much stronger pushback
var targetPushDirection = target.x < superProj.owner.x ? -1 : 1;
var targetNewX = target.x + targetPushDirection * 450;
targetNewX = Math.max(200, Math.min(1848, targetNewX));
tween(target, {
x: targetNewX
}, {
duration: 800,
easing: tween.easeOut
});
// Flash the target to show block effect
LK.effects.flashObject(target, 0x4444ff, 400);
} else {
// Normal hit
target.takeDamage(superProj.damage, true, superProj.owner);
LK.getSound('specialImpact').play();
// Push back the target when receiving super projectile hit - much stronger pushback
var targetPushDirection = target.x < superProj.owner.x ? -1 : 1;
var targetNewX = target.x + targetPushDirection * 500;
targetNewX = Math.max(200, Math.min(1848, targetNewX));
tween(target, {
x: targetNewX
}, {
duration: 900,
easing: tween.easeOut
});
// Also push back the shooter (attacker) when hitting unblocked target
var shooterPushDirection = superProj.owner.x < target.x ? -1 : 1;
var shooterNewX = superProj.owner.x + shooterPushDirection * 180;
shooterNewX = Math.max(200, Math.min(1848, shooterNewX));
tween(superProj.owner, {
x: shooterNewX
}, {
duration: 600,
easing: tween.easeOut
});
}
superProj.destroy();
superProjectiles.splice(i, 1);
continue;
}
// Remove off-screen super projectiles
if (superProj.x < -50 || superProj.x > 2782) {
superProj.destroy();
superProjectiles.splice(i, 1);
}
}
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var proj = projectiles[i];
proj.update();
// Check for projectile clashes (range attacks destroying each other)
for (var j = projectiles.length - 1; j >= 0; j--) {
if (i !== j) {
var otherProj = projectiles[j];
if (Math.abs(proj.x - otherProj.x) < 50 && Math.abs(proj.y - otherProj.y) < 50) {
// Regular projectiles clash - destroy both
LK.effects.flashScreen(0xffffff, 150);
LK.getSound('projectileClash').play();
proj.destroy();
otherProj.destroy();
projectiles.splice(Math.max(i, j), 1);
projectiles.splice(Math.min(i, j), 1);
i = Math.min(i, j) - 1; // Adjust index
break;
}
}
}
if (i >= 0 && i < projectiles.length) {
var proj = projectiles[i];
// Check if regular projectile hits super projectile
for (var j = superProjectiles.length - 1; j >= 0; j--) {
var superProj = superProjectiles[j];
if (Math.abs(proj.x - superProj.x) < 60 && Math.abs(proj.y - superProj.y) < 60) {
// Regular projectile destroyed by super projectile
proj.destroy();
projectiles.splice(i, 1);
break;
}
}
}
if (i >= 0 && i < projectiles.length) {
var proj = projectiles[i];
// Check projectile hits
var target = proj.owner === player ? enemy : player;
if (target && Math.abs(proj.x - target.x) < 200 && Math.abs(proj.y - target.y) < 300) {
// Check if projectile should miss crouching target
// Standing projectiles (fired from y-280) should miss crouching targets
var projectileFromStanding = Math.abs(proj.y - (proj.owner.y - 280)) < 20;
if (target.isCrouching && !target.isJumping && projectileFromStanding) {
// Projectile passes over crouching target - no hit
// Remove off-screen projectiles
if (proj.x < -50 || proj.x > 2782) {
proj.destroy();
projectiles.splice(i, 1);
}
continue; // Skip hit detection for crouching dodge
}
if (target.isBlocking) {
// Projectile was blocked - apply reduced damage and pushback
target.takeDamage(Math.floor(proj.damage * 0.7)); // 30% damage reduction for normal blocking (increased from 70% reduction)
LK.getSound('block').play();
// No pushback for shooter when projectile impacts on block
// Push back the receiver (target)
var targetPushDirection = target.x < proj.owner.x ? -1 : 1;
var targetNewX = target.x + targetPushDirection * 120;
targetNewX = Math.max(200, Math.min(1848, targetNewX));
tween(target, {
x: targetNewX
}, {
duration: 400,
easing: tween.easeOut
});
// Flash the target to show block effect
LK.effects.flashObject(target, 0x4444ff, 300);
} else {
// Normal hit
target.takeDamage(proj.damage);
LK.getSound('rangeImpact').play();
// Push back the target when hit by regular projectile
var targetPushDirection = target.x < proj.owner.x ? -1 : 1;
var targetNewX = target.x + targetPushDirection * 120;
targetNewX = Math.max(200, Math.min(1848, targetNewX));
tween(target, {
x: targetNewX
}, {
duration: 400,
easing: tween.easeOut
});
}
proj.owner.chargeSpecial(7); // Charge special at 50% rate for successful range hit
proj.destroy();
projectiles.splice(i, 1);
continue;
}
// Remove off-screen projectiles
if (proj.x < -50 || proj.x > 2782) {
proj.destroy();
projectiles.splice(i, 1);
}
}
}
// Handle continuous movement
if (player && !playerIsBlocking) {
if (isMovingLeft && player.x > 200) {
player.stopCrouch(); // Stop crouching when moving left
player.x -= player.speed * 3;
if (player.currentAnimation === 'normal') {
player.setAnimation('walking');
}
} else if (isMovingRight && player.x < 2532) {
player.stopCrouch(); // Stop crouching when moving right
player.x += player.speed * 3;
if (player.currentAnimation === 'normal') {
player.setAnimation('walking');
}
} else {
// Neither left nor right movement is active, reset walking animation
if (player.currentAnimation === 'walking') {
player.setAnimation('normal');
}
}
}
// Update AI
updateAI();
// Check game over with debounce protection (only check every 10 ticks to prevent rapid multiple calls)
if (LK.ticks % 10 === 0) {
checkGameOver();
}
}
};
// Keyboard event handler
LK.on('keydown', function (event) {
if (gameState !== 'combat' || !player) return;
var key = event.key.toLowerCase();
if (key === 'z') {
// Z key - Punch
if (player.attack()) {
player.setAnimation('punching', 20); // 20 ticks punch animation
checkMeleeHit(player, enemy, Math.floor(player.attackDamage * 0.75), 'punch');
LK.getSound('punchSound').play();
}
} else if (key === 'x') {
// X key - Kick - can't kick while crouching
if (!player.isCrouching && player.attack()) {
player.setAnimation('kicking', 25); // 25 ticks kick animation (slightly longer)
checkMeleeHit(player, enemy, Math.floor((player.attackDamage + 1) * 0.75), 'kick');
LK.getSound('kickSound').play();
}
} else if (key === 'c') {
// C key - Range attack (special)
if (enemy && player.specialAttack()) {
player.setAnimation('rangeShooting', 15); // 15 ticks range shooting animation
// Determine shoot direction based on enemy position
var shootDirection = enemy.x > player.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust projectile origin based on player stance
var projectileY = player.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (player.isCrouching && !player.isJumping) {
projectileY = player.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (player.isJumping) {
projectileY = player.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
var projectile = new Projectile(player.x + projectileXOffset, projectileY, shootDirection, player.specialDamage, player);
projectiles.push(projectile);
game.addChild(projectile);
player.chargeSpecial(7); // Charge special at 50% rate (15 * 0.5 = 7.5, rounded to 7)
LK.getSound('rangeShoot').play();
}
} else if (key === 'v') {
// V key - Super special attack
if (enemy && player.useSpecialAttack()) {
// Start charging animation before firing
tween(player, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffaa00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Set potent shooting animation
player.setAnimation('potentShooting', 50); // 50 ticks potent shooting animation - extended duration
// Return to normal size and color after charging
tween(player, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
// Determine shoot direction based on enemy position
var shootDirection = enemy.x > player.x ? 1 : -1;
var projectileXOffset = shootDirection > 0 ? 50 : -50;
// Adjust super projectile origin based on player stance
var superProjectileY = player.y - 280; // Default standing position - adjusted for 2x bigger fighters
if (player.isCrouching && !player.isJumping) {
superProjectileY = player.y - 100; // Lower origin when crouching - adjusted for 2x bigger fighters
} else if (player.isJumping) {
superProjectileY = player.y - 300; // Higher origin when jumping - adjusted for 2x bigger fighters
}
// Create 7 super projectiles fired with spacing
for (var i = 0; i < 7; i++) {
(function (index) {
LK.setTimeout(function () {
var projectileX = player.x + projectileXOffset + (index - 3) * 60; // Horizontal spread in a row
var projectileYOffset = superProjectileY + (index - 3) * 15; // Add vertical spacing between projectiles
var superProjectile = new SuperProjectile(projectileX, projectileYOffset, shootDirection, Math.floor(player.specialDamage * 2 / 7), player);
// Increase speed for keyboard potent projectiles
superProjectile.speed = 8; // Increased speed to match regular projectiles
// Add laser effects for fighter 3
if (player.fighterType === 3) {
// Initial bright laser effect
tween(superProjectile, {
tint: 0xff0000
}, {
duration: 50
});
// Pulsing laser effect
tween(superProjectile, {
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(superProjectile, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
superProjectiles.push(superProjectile);
game.addChild(superProjectile);
LK.getSound('specialShoot').play();
}, index * 80); // 80ms delay between each projectile
})(i);
}
// No pushback when firing potent projectiles
// Sound will be played with each projectile instead
}
});
}
}
});
// Initialize title screen
initTitleScreen();
;
transparent bubble. In-Game asset. 2d. High contrast. No shadows
shield
arm with a fist
band-aid
a little bit smaller image
hornless
whitte
whole gloves
fire
transparent kicking feet simbol. In-Game asset. 2d. High contrast. No shadows
red
2D
crouching with blue background
teeth close to bite
scratch attack
teeth
toothless open mouth
shark teeth. In-Game asset. 2d. High contrast. No shadows
simbolo transparente de bala. In-Game asset. 2d. High contrast. No shadows
transparent fist simbol. In-Game asset. 2d. High contrast. No shadows
transparent fire simbol. In-Game asset. 2d. High contrast. No shadows
right arrow. In-Game asset. 2d. High contrast. No shadows
add sand
building roof