/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function (classLevel) {
var self = Container.call(this);
self.isPlayer = false;
self.isBoss = true;
self.classLevel = classLevel || 1;
self.maxHealth = 200 + classLevel * 50; // Much more health than regular fighters
self.health = self.maxHealth;
self.moveSpeed = 2; // Slightly slower but more powerful
self.aiTimer = 0;
self.lastAttackTime = 0;
self.isHealing = false;
self.healTimer = 0;
self.hasSurrendered = false;
self.healingPosition = null;
self.isBlocking = false;
self.blockTimer = 0;
self.lastBlockTime = 0;
self.specialAttackTimer = 0;
// Create boss graphics with unique asset
var assetName = 'boss_class' + self.classLevel;
var bossGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Boss has better weapons than regular class
var weaponTypes = ['fist', 'sword', 'axe', 'mace', 'halberd'];
var damages = [20, 30, 40, 50, 60]; // Double damage of regular fighters
var ranges = [70, 90, 80, 75, 110]; // Better range too
self.weapon = self.addChild(new Weapon(weaponTypes[self.classLevel - 1], damages[self.classLevel - 1], ranges[self.classLevel - 1]));
self.weapon.x = 50;
// Create health bar (larger for boss)
self.healthBarBg = self.attachAsset('health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5,
y: -100,
scaleX: 1.5,
scaleY: 1.5
});
self.healthBarFill = self.attachAsset('health_bar_fill', {
anchorX: 0,
anchorY: 0.5,
x: -150,
y: -100,
scaleX: 1.5,
scaleY: 1.5
});
// Boss name text
self.nameText = new Text2('BOSS - Class ' + self.classLevel, {
size: 30,
fill: 0xFF0000
});
self.nameText.anchor.set(0.5, 0.5);
self.nameText.x = 0;
self.nameText.y = -140;
self.addChild(self.nameText);
// Inherit all methods from Fighter but override some
Fighter.call(self, false, classLevel);
// Override AI with more aggressive behavior
self.aiUpdate = function (targets) {
if (self.isDead()) return;
self.aiTimer++;
self.specialAttackTimer++;
// Boss heals less frequently and only when critically low
if (self.health <= 30 && !self.isHealing && !self.hasSurrendered && Math.random() < 0.001) {
self.startHealing();
}
// If healing, move to healing position
if (self.isHealing && self.healingPosition) {
var distanceToHealPos = Math.sqrt(Math.pow(self.healingPosition.x - self.x, 2) + Math.pow(self.healingPosition.y - self.y, 2));
if (distanceToHealPos > 10) {
self.moveTowards(self.healingPosition.x, self.healingPosition.y);
} else {
self.healTimer++;
if (self.healTimer >= 30) {
// Heals faster than regular fighters
self.health = Math.min(self.maxHealth, self.health + 10);
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 300 * healthPercent; // Adjusted for larger health bar
self.healTimer = 0;
if (self.health >= self.maxHealth * 0.8) {
self.stopHealing();
}
}
}
return;
}
// Find player target specifically
var playerTarget = null;
for (var i = 0; i < targets.length; i++) {
if (targets[i].isPlayer && !targets[i].isDead()) {
playerTarget = targets[i];
break;
}
}
if (playerTarget) {
var distance = Math.sqrt(Math.pow(playerTarget.x - self.x, 2) + Math.pow(playerTarget.y - self.y, 2));
// Move towards player aggressively
if (distance > self.weapon.range * 0.7) {
self.moveTowards(playerTarget.x, playerTarget.y);
}
// Attack more frequently than regular AI
if (distance <= self.weapon.range && self.aiTimer % 25 === 0) {
self.attackTarget(playerTarget);
}
// Special attack every 5 seconds
if (self.specialAttackTimer >= 300 && distance <= self.weapon.range * 1.5) {
self.specialAttack(playerTarget);
self.specialAttackTimer = 0;
}
}
};
// Special boss attack
self.specialAttack = function (target) {
if (!target || target.isDead()) return false;
// Visual effect for special attack
tween(bossGraphics, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFF0000
}, {
duration: 200
});
tween(bossGraphics, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
// Deal double damage
var isDead = target.takeDamage(self.weapon.damage * 2);
LK.getSound('sword_hit').play();
return isDead;
};
// Override takeDamage to update larger health bar
self.takeDamage = function (damage) {
var actualDamage = damage;
if (self.isBlocking && self.blockTimer < 30) {
actualDamage = Math.floor(damage * 0.2); // Bosses block better
LK.effects.flashObject(bossGraphics, 0x00ff00, 200);
self.stopBlocking();
} else {
LK.effects.flashObject(bossGraphics, 0xff0000, 300);
}
self.health -= actualDamage;
if (self.health < 0) self.health = 0;
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 300 * healthPercent; // Adjusted for larger health bar
if (self.isHealing) {
self.stopHealing();
}
return self.health <= 0;
};
return self;
});
var Cannon = Container.expand(function (x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.fireTimer = 0;
self.fireRate = 180; // Fire every 3 seconds
var cannonGraphics = self.attachAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5
});
self.fire = function () {
// Create cannonball
var cannonball = new Cannonball(self.x, self.y);
// Target random position between player and boss
var targetX = 512 + Math.random() * 1024; // Random X between 512-1536
var targetY = 1000 + Math.random() * 800; // Random Y between 1000-1800
cannonball.setTarget(targetX, targetY);
game.addChild(cannonball);
cannonballs.push(cannonball);
// Visual effect for firing
tween(cannonGraphics, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 100
});
tween(cannonGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
LK.getSound('cannon_fire').play();
};
self.update = function () {
if (gameState === 'bossBattle') {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
self.fire();
self.fireTimer = 0;
}
}
};
return self;
});
var Cannonball = Container.expand(function (startX, startY) {
var self = Container.call(this);
self.x = startX;
self.y = startY;
self.speed = 8;
self.targetX = 0;
self.targetY = 0;
self.velocityX = 0;
self.velocityY = 0;
self.exploded = false;
var ballGraphics = self.attachAsset('cannonball', {
anchorX: 0.5,
anchorY: 0.5
});
self.setTarget = function (targetX, targetY) {
self.targetX = targetX;
self.targetY = targetY;
// Calculate velocity towards target
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
};
self.explode = function () {
if (self.exploded) return;
self.exploded = true;
// Visual explosion effect
tween(ballGraphics, {
scaleX: 3,
scaleY: 3,
alpha: 0,
tint: 0xFF4444
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
// Check damage to all fighters in explosion radius
var explosionRadius = 100;
for (var i = 0; i < fighters.length; i++) {
var fighter = fighters[i];
if (!fighter.isDead()) {
var distance = Math.sqrt(Math.pow(fighter.x - self.x, 2) + Math.pow(fighter.y - self.y, 2));
if (distance <= explosionRadius) {
if (fighter.isPlayer) {
fighter.takeDamage(10); // 10 damage to player
} else if (fighter.isBoss) {
fighter.takeDamage(100); // 100 damage to boss
}
}
}
}
};
self.update = function () {
if (self.exploded) return;
// Move towards target
self.x += self.velocityX;
self.y += self.velocityY;
// Check if reached target or close enough
var distanceToTarget = Math.sqrt(Math.pow(self.targetX - self.x, 2) + Math.pow(self.targetY - self.y, 2));
if (distanceToTarget <= 30) {
self.explode();
}
// Remove if out of bounds
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
}
};
return self;
});
var Fighter = Container.expand(function (isPlayer, classLevel) {
var self = Container.call(this);
self.isPlayer = isPlayer || false;
self.classLevel = classLevel || 1;
self.maxHealth = 100;
self.health = self.maxHealth;
self.moveSpeed = 3;
self.aiTimer = 0;
self.lastAttackTime = 0;
self.isHealing = false;
self.healTimer = 0;
self.hasSurrendered = false;
self.healingPosition = null;
self.isBlocking = false;
self.blockTimer = 0;
self.lastBlockTime = 0;
// Create fighter graphics
var assetName = self.isPlayer ? 'player_class' + self.classLevel : 'ai_class' + self.classLevel;
var fighterGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Create weapon based on class
var weaponTypes = ['fist', 'sword', 'axe', 'mace', 'halberd'];
var damages = [10, 15, 20, 25, 30];
var ranges = [50, 70, 60, 55, 90];
self.weapon = self.addChild(new Weapon(weaponTypes[self.classLevel - 1], damages[self.classLevel - 1], ranges[self.classLevel - 1]));
self.weapon.x = 40;
// Create health bar
self.healthBarBg = self.attachAsset('health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5,
y: -80
});
self.healthBarFill = self.attachAsset('health_bar_fill', {
anchorX: 0,
anchorY: 0.5,
x: -100,
y: -80
});
self.takeDamage = function (damage) {
var actualDamage = damage;
// Check if blocking
if (self.isBlocking && self.blockTimer < 30) {
// Block only works for first 30 frames (0.5 seconds)
actualDamage = Math.floor(damage * 0.3); // Block reduces damage by 70%
// Visual feedback for successful block
LK.effects.flashObject(fighterGraphics, 0x00ff00, 200);
// Stop blocking after successful block
self.stopBlocking();
} else {
// Flash red when taking full damage
LK.effects.flashObject(fighterGraphics, 0xff0000, 300);
}
self.health -= actualDamage;
if (self.health < 0) self.health = 0;
// Update health bar
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 200 * healthPercent;
// Stop healing if taking damage
if (self.isHealing) {
self.stopHealing();
}
return self.health <= 0;
};
self.startHealing = function () {
if (self.isHealing || self.isDead() || self.hasSurrendered) return false;
// Find nearest corner for healing
var corners = [{
x: 150,
y: 250
},
// top-left
{
x: 1898,
y: 250
},
// top-right
{
x: 150,
y: 2482
},
// bottom-left
{
x: 1898,
y: 2482
} // bottom-right
];
var nearestCorner = corners[0];
var minDistance = Infinity;
for (var i = 0; i < corners.length; i++) {
var distance = Math.sqrt(Math.pow(corners[i].x - self.x, 2) + Math.pow(corners[i].y - self.y, 2));
if (distance < minDistance) {
minDistance = distance;
nearestCorner = corners[i];
}
}
self.healingPosition = nearestCorner;
self.isHealing = true;
self.healTimer = 0;
// Visual feedback - green tint while healing
tween(fighterGraphics, {
tint: 0x00ff00
}, {
duration: 300
});
return true;
};
self.stopHealing = function () {
if (!self.isHealing) return;
self.isHealing = false;
self.healTimer = 0;
self.healingPosition = null;
// Remove green tint
tween(fighterGraphics, {
tint: 0xffffff
}, {
duration: 300
});
};
self.startBlocking = function () {
if (self.isDead() || self.hasSurrendered || self.isHealing) return false;
if (LK.ticks - self.lastBlockTime < 120) return false; // 2 second cooldown between blocks
self.isBlocking = true;
self.blockTimer = 0;
self.lastBlockTime = LK.ticks;
// Visual feedback - blue tint while blocking
tween(fighterGraphics, {
tint: 0x0099ff,
scaleX: 0.9,
scaleY: 1.1
}, {
duration: 200
});
return true;
};
self.stopBlocking = function () {
if (!self.isBlocking) return;
self.isBlocking = false;
self.blockTimer = 0;
// Remove blue tint and restore normal scale
tween(fighterGraphics, {
tint: 0xffffff,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
};
self.surrender = function () {
if (self.hasSurrendered || self.isDead()) return false;
self.hasSurrendered = true;
self.stopHealing();
// Visual feedback - gray tint when surrendered
tween(fighterGraphics, {
tint: 0x666666,
alpha: 0.7
}, {
duration: 500
});
return true;
};
self.isDead = function () {
return self.health <= 0 || self.hasSurrendered;
};
self.attackTarget = function (target) {
if (!target || target.isDead()) return false;
var distance = Math.sqrt(Math.pow(target.x - self.x, 2) + Math.pow(target.y - self.y, 2));
if (distance <= self.weapon.range && self.weapon.attack()) {
var isDead = target.takeDamage(self.weapon.damage);
// Play sound
if (self.weapon.weaponType === 'fist') {
LK.getSound('punch').play();
} else {
LK.getSound('sword_hit').play();
}
return isDead;
}
return false;
};
self.moveTowards = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.moveSpeed;
self.y += dy / distance * self.moveSpeed;
}
};
self.aiUpdate = function (targets) {
if (self.isDead()) return;
self.aiTimer++;
// Check for surrender (20% chance when health is low)
if (self.health <= 30 && !self.hasSurrendered && Math.random() < 0.002) {
// 0.002 per frame ≈ 20% chance over time
self.surrender();
return;
}
// Check if should start healing (when health is below 40% and not currently healing)
if (self.health <= 40 && !self.isHealing && !self.hasSurrendered) {
// Find if any enemies are nearby before healing
var hasNearbyEnemies = false;
for (var i = 0; i < targets.length; i++) {
if (targets[i] !== self && !targets[i].isDead()) {
var distance = Math.sqrt(Math.pow(targets[i].x - self.x, 2) + Math.pow(targets[i].y - self.y, 2));
if (distance < 200) {
// Enemy within 200 pixels
hasNearbyEnemies = true;
break;
}
}
}
// Only start healing if no enemies nearby or health is critically low
if (!hasNearbyEnemies || self.health <= 20) {
self.startHealing();
}
}
// If healing, move to healing position
if (self.isHealing && self.healingPosition) {
var distanceToHealPos = Math.sqrt(Math.pow(self.healingPosition.x - self.x, 2) + Math.pow(self.healingPosition.y - self.y, 2));
if (distanceToHealPos > 10) {
self.moveTowards(self.healingPosition.x, self.healingPosition.y);
} else {
// At healing position, start healing
self.healTimer++;
if (self.healTimer >= 60) {
// Heal every 60 frames (1 second)
self.health = Math.min(self.maxHealth, self.health + 5);
// Update health bar
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 200 * healthPercent;
self.healTimer = 0;
// Stop healing when at full health or health above 70%
if (self.health >= self.maxHealth * 0.7) {
self.stopHealing();
}
}
}
return; // Don't do combat actions while healing
}
// Find nearest living target
var nearestTarget = null;
var nearestDistance = Infinity;
for (var i = 0; i < targets.length; i++) {
if (targets[i] !== self && !targets[i].isDead()) {
var distance = Math.sqrt(Math.pow(targets[i].x - self.x, 2) + Math.pow(targets[i].y - self.y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestTarget = targets[i];
}
}
}
if (nearestTarget) {
// Move towards target if too far
if (nearestDistance > self.weapon.range * 0.8) {
self.moveTowards(nearestTarget.x, nearestTarget.y);
}
// Attack if in range and cooldown is ready
if (nearestDistance <= self.weapon.range && self.aiTimer % 45 === 0) {
self.attackTarget(nearestTarget);
}
}
};
self.update = function () {
// Keep within game bounds
if (self.x < 100) self.x = 100;
if (self.x > 1948) self.x = 1948;
if (self.y < 200) self.y = 200;
if (self.y > 2532) self.y = 2532;
// Update blocking timer
if (self.isBlocking) {
self.blockTimer++;
// Auto-stop blocking after 30 frames (0.5 seconds)
if (self.blockTimer >= 30) {
self.stopBlocking();
}
}
};
return self;
});
var MenuButton = Container.expand(function (text, onClick) {
var self = Container.call(this);
var buttonBg = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
tween(buttonBg, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
if (onClick) onClick();
};
self.up = function (x, y, obj) {
tween(buttonBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
return self;
});
var MovementButton = Container.expand(function (direction, moveX, moveY) {
var self = Container.call(this);
self.direction = direction;
self.moveX = moveX;
self.moveY = moveY;
self.isPressed = false;
var buttonBg = self.attachAsset('move_button', {
anchorX: 0.5,
anchorY: 0.5
});
var arrow = self.attachAsset('move_button_arrow', {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate arrow based on direction
if (direction === 'up') arrow.rotation = -Math.PI / 2;else if (direction === 'down') arrow.rotation = Math.PI / 2;else if (direction === 'left') arrow.rotation = Math.PI;else if (direction === 'right') arrow.rotation = 0;
self.down = function (x, y, obj) {
self.isPressed = true;
tween(buttonBg, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x3498db
}, {
duration: 100
});
};
self.up = function (x, y, obj) {
self.isPressed = false;
tween(buttonBg, {
scaleX: 1,
scaleY: 1,
tint: 0xffffff
}, {
duration: 100
});
};
return self;
});
var Weapon = Container.expand(function (weaponType, damage, range) {
var self = Container.call(this);
self.weaponType = weaponType || 'fist';
self.damage = damage || 10;
self.range = range || 50;
self.cooldown = 0;
var weaponGraphics = self.attachAsset(self.weaponType, {
anchorX: 0.5,
anchorY: 0.5
});
self.canAttack = function () {
return self.cooldown <= 0;
};
self.attack = function () {
if (self.canAttack()) {
self.cooldown = 30; // 30 frames cooldown
tween(weaponGraphics, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 100
});
tween(weaponGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
return true;
}
return false;
};
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Sounds
// UI elements
// Weapon assets
// Player and AI character assets for different classes
// Game state
var gameState = 'menu'; // 'menu', 'battle', 'victory', 'defeat', 'bossBattle'
var battleMode = 'duel'; // 'duel' or 'freeforall'
var playerClass = 1;
var playerWins = 0;
var isBossBattle = false;
var currentBoss = null;
var cannons = [];
var cannonballs = [];
// UI elements
var titleText = new Text2('Medieval Beatdown', {
size: 80,
fill: 0xECF0F1
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
game.addChild(titleText);
var classText = new Text2('Class ' + playerClass + ' Fighter', {
size: 50,
fill: 0xF39C12
});
classText.anchor.set(0.5, 0.5);
classText.x = 1024;
classText.y = 500;
game.addChild(classText);
var winsText = new Text2('Wins: ' + playerWins, {
size: 40,
fill: 0x95A5A6
});
winsText.anchor.set(0.5, 0.5);
winsText.x = 1024;
winsText.y = 550;
game.addChild(winsText);
var duelButton = game.addChild(new MenuButton('1v1 Duel', function () {
startBattle('duel');
}));
duelButton.x = 1024;
duelButton.y = 700;
var freeForAllButton = game.addChild(new MenuButton('Free For All', function () {
startBattle('freeforall');
}));
freeForAllButton.x = 1024;
freeForAllButton.y = 820;
var bossButton = game.addChild(new MenuButton('Fight Boss', function () {
startBossBattle();
}));
bossButton.x = 1024;
bossButton.y = 940;
bossButton.visible = false; // Only show when player is ready
// Battle variables
var fighters = [];
var player = null;
var dragTarget = null;
var battleTimer = 0;
// Movement buttons
var moveButtons = [];
var upButton = null;
var downButton = null;
var leftButton = null;
var rightButton = null;
// Battle result text
var resultText = new Text2('', {
size: 60,
fill: 0xFFFFFF
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 1024;
resultText.y = 1366;
resultText.visible = false;
game.addChild(resultText);
var continueButton = game.addChild(new MenuButton('Continue', function () {
returnToMenu();
}));
continueButton.x = 1024;
continueButton.y = 1500;
continueButton.visible = false;
// Create movement buttons
upButton = game.addChild(new MovementButton('up', 0, -1));
upButton.x = 180;
upButton.y = 2400;
upButton.visible = false;
downButton = game.addChild(new MovementButton('down', 0, 1));
downButton.x = 180;
downButton.y = 2560;
downButton.visible = false;
leftButton = game.addChild(new MovementButton('left', -1, 0));
leftButton.x = 100;
leftButton.y = 2480;
leftButton.visible = false;
rightButton = game.addChild(new MovementButton('right', 1, 0));
rightButton.x = 260;
rightButton.y = 2480;
rightButton.visible = false;
moveButtons = [upButton, downButton, leftButton, rightButton];
var controlsText = new Text2('Tap corners to heal • Tap center top to surrender • Tap center bottom to block', {
size: 28,
fill: 0xBDC3C7
});
controlsText.anchor.set(0.5, 0.5);
controlsText.x = 1024;
controlsText.y = 150;
controlsText.visible = false;
// Hide movement buttons
for (var i = 0; i < moveButtons.length; i++) {
moveButtons[i].visible = false;
}
game.addChild(controlsText);
function startBattle(mode) {
gameState = 'battle';
battleMode = mode;
battleTimer = 0;
// Hide menu elements
titleText.visible = false;
classText.visible = false;
winsText.visible = false;
duelButton.visible = false;
freeForAllButton.visible = false;
resultText.visible = false;
continueButton.visible = false;
controlsText.visible = true;
// Show movement buttons
for (var i = 0; i < moveButtons.length; i++) {
moveButtons[i].visible = true;
}
// Show movement buttons
for (var i = 0; i < moveButtons.length; i++) {
moveButtons[i].visible = true;
}
// Clear previous fighters
for (var i = 0; i < fighters.length; i++) {
fighters[i].destroy();
}
fighters = [];
// Create player
player = game.addChild(new Fighter(true, playerClass));
player.x = 300;
player.y = 1366;
fighters.push(player);
// Create AI opponents
if (mode === 'duel') {
var enemy = game.addChild(new Fighter(false, playerClass));
enemy.x = 1700;
enemy.y = 1366;
fighters.push(enemy);
} else {
// Free for all - 3 AI opponents
for (var i = 0; i < 3; i++) {
var enemy = game.addChild(new Fighter(false, playerClass));
enemy.x = 1200 + i * 200;
enemy.y = 800 + i * 200;
fighters.push(enemy);
}
}
}
function startBossBattle() {
gameState = 'bossBattle';
isBossBattle = true;
battleTimer = 0;
// Hide menu elements
titleText.visible = false;
classText.visible = false;
winsText.visible = false;
duelButton.visible = false;
freeForAllButton.visible = false;
bossButton.visible = false;
resultText.visible = false;
continueButton.visible = false;
controlsText.visible = true;
// Clear previous fighters
for (var i = 0; i < fighters.length; i++) {
fighters[i].destroy();
}
fighters = [];
// Create player
player = game.addChild(new Fighter(true, playerClass));
player.x = 300;
player.y = 1366;
fighters.push(player);
// Create boss enemy
currentBoss = game.addChild(new Boss(playerClass));
currentBoss.x = 1700;
currentBoss.y = 1366;
fighters.push(currentBoss);
// Create cannons around the battlefield
var cannon1 = game.addChild(new Cannon(200, 600));
var cannon2 = game.addChild(new Cannon(1848, 600));
var cannon3 = game.addChild(new Cannon(1024, 300));
cannons.push(cannon1, cannon2, cannon3);
// Add dramatic entrance effect for boss
tween(currentBoss, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF0000
}, {
duration: 1000,
easing: tween.bounceOut
});
tween(currentBoss, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 500
});
}
function checkBattleEnd() {
var aliveFighters = [];
var playerAlive = false;
for (var i = 0; i < fighters.length; i++) {
if (!fighters[i].isDead()) {
aliveFighters.push(fighters[i]);
if (fighters[i].isPlayer) {
playerAlive = true;
}
}
}
if (aliveFighters.length <= 1) {
if (playerAlive) {
// Player wins
if (battleMode === 'freeforall') {
playerWins += 2; // Free for all gives 2 points
} else {
playerWins++; // Duel gives 1 point
}
if (isBossBattle) {
// Player defeated boss - advance to next class
if (playerClass < 5) {
playerClass++;
resultText.setText('BOSS DEFEATED! Advanced to Class ' + playerClass + '!');
} else {
resultText.setText('FINAL BOSS DEFEATED! You are the Champion!');
}
isBossBattle = false;
currentBoss = null;
} else {
// Check if player has enough wins to fight boss
var winsNeeded = playerClass * 3; // Need 3, 6, 9, 12, 15 wins for each class
if (playerWins >= winsNeeded && playerClass < 5) {
// Time for boss battle
resultText.setText('Victory! Ready for Boss Battle!');
gameState = 'readyForBoss';
} else {
resultText.setText('Victory! Wins: ' + playerWins + '/' + winsNeeded);
}
}
LK.getSound('victory').play();
} else {
// Player loses
if (isBossBattle) {
resultText.setText('Boss Defeated You! Try again!');
isBossBattle = false;
currentBoss = null;
} else {
resultText.setText('Defeat! Try again!');
}
LK.getSound('defeat').play();
}
gameState = 'battleEnd';
resultText.visible = true;
continueButton.visible = true;
LK.setTimeout(function () {
// Auto return to menu after 5 seconds
if (gameState === 'battleEnd') {
returnToMenu();
}
}, 5000);
}
}
function returnToMenu() {
gameState = 'menu';
// Update UI text
classText.setText('Class ' + playerClass + ' Fighter');
winsText.setText('Wins: ' + playerWins);
// Show menu elements
titleText.visible = true;
classText.visible = true;
winsText.visible = true;
resultText.visible = false;
continueButton.visible = false;
controlsText.visible = false;
// Show boss button if player has enough wins and not at max class
var winsNeeded = playerClass * 3;
if (playerWins >= winsNeeded && playerClass < 5) {
bossButton.visible = true;
// Hide other battle options when boss battle is required
duelButton.visible = false;
freeForAllButton.visible = false;
} else {
bossButton.visible = false;
// Show other battle options when boss battle is not available
duelButton.visible = true;
freeForAllButton.visible = true;
}
// Clear fighters
for (var i = 0; i < fighters.length; i++) {
fighters[i].destroy();
}
fighters = [];
// Clear cannons
for (var i = 0; i < cannons.length; i++) {
cannons[i].destroy();
}
cannons = [];
// Clear cannonballs
for (var i = 0; i < cannonballs.length; i++) {
cannonballs[i].destroy();
}
cannonballs = [];
player = null;
dragTarget = null;
isBossBattle = false;
currentBoss = null;
}
// Touch controls
game.down = function (x, y, obj) {
if (gameState === 'battle' && player && !player.isDead()) {
// Check for healing - double tap in corners
if (x < 200 && y < 400 || x > 1848 && y < 400 || x < 200 && y > 2332 || x > 1848 && y > 2332) {
if (!player.isHealing) {
player.startHealing();
} else {
player.stopHealing();
}
return;
}
// Check for surrender - double tap in center top area
if (x > 924 && x < 1124 && y > 100 && y < 300) {
if (!player.hasSurrendered) {
player.surrender();
}
return;
}
// Check for blocking - tap in center bottom area
if (x > 924 && x < 1124 && y > 2432 && y < 2632) {
if (!player.isBlocking) {
player.startBlocking();
}
return;
}
dragTarget = player;
// Check for attack - if touching near an enemy, attack instead of move
var attackRange = player.weapon.range + 50;
for (var i = 0; i < fighters.length; i++) {
var fighter = fighters[i];
if (fighter !== player && !fighter.isDead()) {
var distance = Math.sqrt(Math.pow(fighter.x - x, 2) + Math.pow(fighter.y - y, 2));
if (distance <= attackRange) {
player.attackTarget(fighter);
dragTarget = null;
return;
}
}
}
}
};
game.move = function (x, y, obj) {
if (gameState === 'battle' && dragTarget && !dragTarget.isDead()) {
dragTarget.moveTowards(x, y);
}
};
game.up = function (x, y, obj) {
dragTarget = null;
};
game.update = function () {
if (gameState === 'battle' || gameState === 'bossBattle') {
battleTimer++;
// Update all fighters
for (var i = 0; i < fighters.length; i++) {
var fighter = fighters[i];
if (!fighter.isDead()) {
if (!fighter.isPlayer) {
fighter.aiUpdate(fighters);
}
}
}
// Update cannons
for (var i = 0; i < cannons.length; i++) {
cannons[i].update();
}
// Update cannonballs and remove destroyed ones
for (var i = cannonballs.length - 1; i >= 0; i--) {
var cannonball = cannonballs[i];
if (cannonball.parent) {
cannonball.update();
} else {
// Cannonball was destroyed, remove from array
cannonballs.splice(i, 1);
}
}
// Handle movement button input
if (player && !player.isDead()) {
var moveSpeed = player.moveSpeed * 2; // Faster movement with buttons
if (upButton.isPressed) {
player.y -= moveSpeed;
}
if (downButton.isPressed) {
player.y += moveSpeed;
}
if (leftButton.isPressed) {
player.x -= moveSpeed;
}
if (rightButton.isPressed) {
player.x += moveSpeed;
}
}
// Check for battle end
checkBattleEnd();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function (classLevel) {
var self = Container.call(this);
self.isPlayer = false;
self.isBoss = true;
self.classLevel = classLevel || 1;
self.maxHealth = 200 + classLevel * 50; // Much more health than regular fighters
self.health = self.maxHealth;
self.moveSpeed = 2; // Slightly slower but more powerful
self.aiTimer = 0;
self.lastAttackTime = 0;
self.isHealing = false;
self.healTimer = 0;
self.hasSurrendered = false;
self.healingPosition = null;
self.isBlocking = false;
self.blockTimer = 0;
self.lastBlockTime = 0;
self.specialAttackTimer = 0;
// Create boss graphics with unique asset
var assetName = 'boss_class' + self.classLevel;
var bossGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Boss has better weapons than regular class
var weaponTypes = ['fist', 'sword', 'axe', 'mace', 'halberd'];
var damages = [20, 30, 40, 50, 60]; // Double damage of regular fighters
var ranges = [70, 90, 80, 75, 110]; // Better range too
self.weapon = self.addChild(new Weapon(weaponTypes[self.classLevel - 1], damages[self.classLevel - 1], ranges[self.classLevel - 1]));
self.weapon.x = 50;
// Create health bar (larger for boss)
self.healthBarBg = self.attachAsset('health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5,
y: -100,
scaleX: 1.5,
scaleY: 1.5
});
self.healthBarFill = self.attachAsset('health_bar_fill', {
anchorX: 0,
anchorY: 0.5,
x: -150,
y: -100,
scaleX: 1.5,
scaleY: 1.5
});
// Boss name text
self.nameText = new Text2('BOSS - Class ' + self.classLevel, {
size: 30,
fill: 0xFF0000
});
self.nameText.anchor.set(0.5, 0.5);
self.nameText.x = 0;
self.nameText.y = -140;
self.addChild(self.nameText);
// Inherit all methods from Fighter but override some
Fighter.call(self, false, classLevel);
// Override AI with more aggressive behavior
self.aiUpdate = function (targets) {
if (self.isDead()) return;
self.aiTimer++;
self.specialAttackTimer++;
// Boss heals less frequently and only when critically low
if (self.health <= 30 && !self.isHealing && !self.hasSurrendered && Math.random() < 0.001) {
self.startHealing();
}
// If healing, move to healing position
if (self.isHealing && self.healingPosition) {
var distanceToHealPos = Math.sqrt(Math.pow(self.healingPosition.x - self.x, 2) + Math.pow(self.healingPosition.y - self.y, 2));
if (distanceToHealPos > 10) {
self.moveTowards(self.healingPosition.x, self.healingPosition.y);
} else {
self.healTimer++;
if (self.healTimer >= 30) {
// Heals faster than regular fighters
self.health = Math.min(self.maxHealth, self.health + 10);
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 300 * healthPercent; // Adjusted for larger health bar
self.healTimer = 0;
if (self.health >= self.maxHealth * 0.8) {
self.stopHealing();
}
}
}
return;
}
// Find player target specifically
var playerTarget = null;
for (var i = 0; i < targets.length; i++) {
if (targets[i].isPlayer && !targets[i].isDead()) {
playerTarget = targets[i];
break;
}
}
if (playerTarget) {
var distance = Math.sqrt(Math.pow(playerTarget.x - self.x, 2) + Math.pow(playerTarget.y - self.y, 2));
// Move towards player aggressively
if (distance > self.weapon.range * 0.7) {
self.moveTowards(playerTarget.x, playerTarget.y);
}
// Attack more frequently than regular AI
if (distance <= self.weapon.range && self.aiTimer % 25 === 0) {
self.attackTarget(playerTarget);
}
// Special attack every 5 seconds
if (self.specialAttackTimer >= 300 && distance <= self.weapon.range * 1.5) {
self.specialAttack(playerTarget);
self.specialAttackTimer = 0;
}
}
};
// Special boss attack
self.specialAttack = function (target) {
if (!target || target.isDead()) return false;
// Visual effect for special attack
tween(bossGraphics, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFF0000
}, {
duration: 200
});
tween(bossGraphics, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
// Deal double damage
var isDead = target.takeDamage(self.weapon.damage * 2);
LK.getSound('sword_hit').play();
return isDead;
};
// Override takeDamage to update larger health bar
self.takeDamage = function (damage) {
var actualDamage = damage;
if (self.isBlocking && self.blockTimer < 30) {
actualDamage = Math.floor(damage * 0.2); // Bosses block better
LK.effects.flashObject(bossGraphics, 0x00ff00, 200);
self.stopBlocking();
} else {
LK.effects.flashObject(bossGraphics, 0xff0000, 300);
}
self.health -= actualDamage;
if (self.health < 0) self.health = 0;
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 300 * healthPercent; // Adjusted for larger health bar
if (self.isHealing) {
self.stopHealing();
}
return self.health <= 0;
};
return self;
});
var Cannon = Container.expand(function (x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.fireTimer = 0;
self.fireRate = 180; // Fire every 3 seconds
var cannonGraphics = self.attachAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5
});
self.fire = function () {
// Create cannonball
var cannonball = new Cannonball(self.x, self.y);
// Target random position between player and boss
var targetX = 512 + Math.random() * 1024; // Random X between 512-1536
var targetY = 1000 + Math.random() * 800; // Random Y between 1000-1800
cannonball.setTarget(targetX, targetY);
game.addChild(cannonball);
cannonballs.push(cannonball);
// Visual effect for firing
tween(cannonGraphics, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 100
});
tween(cannonGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
LK.getSound('cannon_fire').play();
};
self.update = function () {
if (gameState === 'bossBattle') {
self.fireTimer++;
if (self.fireTimer >= self.fireRate) {
self.fire();
self.fireTimer = 0;
}
}
};
return self;
});
var Cannonball = Container.expand(function (startX, startY) {
var self = Container.call(this);
self.x = startX;
self.y = startY;
self.speed = 8;
self.targetX = 0;
self.targetY = 0;
self.velocityX = 0;
self.velocityY = 0;
self.exploded = false;
var ballGraphics = self.attachAsset('cannonball', {
anchorX: 0.5,
anchorY: 0.5
});
self.setTarget = function (targetX, targetY) {
self.targetX = targetX;
self.targetY = targetY;
// Calculate velocity towards target
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
};
self.explode = function () {
if (self.exploded) return;
self.exploded = true;
// Visual explosion effect
tween(ballGraphics, {
scaleX: 3,
scaleY: 3,
alpha: 0,
tint: 0xFF4444
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
// Check damage to all fighters in explosion radius
var explosionRadius = 100;
for (var i = 0; i < fighters.length; i++) {
var fighter = fighters[i];
if (!fighter.isDead()) {
var distance = Math.sqrt(Math.pow(fighter.x - self.x, 2) + Math.pow(fighter.y - self.y, 2));
if (distance <= explosionRadius) {
if (fighter.isPlayer) {
fighter.takeDamage(10); // 10 damage to player
} else if (fighter.isBoss) {
fighter.takeDamage(100); // 100 damage to boss
}
}
}
}
};
self.update = function () {
if (self.exploded) return;
// Move towards target
self.x += self.velocityX;
self.y += self.velocityY;
// Check if reached target or close enough
var distanceToTarget = Math.sqrt(Math.pow(self.targetX - self.x, 2) + Math.pow(self.targetY - self.y, 2));
if (distanceToTarget <= 30) {
self.explode();
}
// Remove if out of bounds
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
}
};
return self;
});
var Fighter = Container.expand(function (isPlayer, classLevel) {
var self = Container.call(this);
self.isPlayer = isPlayer || false;
self.classLevel = classLevel || 1;
self.maxHealth = 100;
self.health = self.maxHealth;
self.moveSpeed = 3;
self.aiTimer = 0;
self.lastAttackTime = 0;
self.isHealing = false;
self.healTimer = 0;
self.hasSurrendered = false;
self.healingPosition = null;
self.isBlocking = false;
self.blockTimer = 0;
self.lastBlockTime = 0;
// Create fighter graphics
var assetName = self.isPlayer ? 'player_class' + self.classLevel : 'ai_class' + self.classLevel;
var fighterGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Create weapon based on class
var weaponTypes = ['fist', 'sword', 'axe', 'mace', 'halberd'];
var damages = [10, 15, 20, 25, 30];
var ranges = [50, 70, 60, 55, 90];
self.weapon = self.addChild(new Weapon(weaponTypes[self.classLevel - 1], damages[self.classLevel - 1], ranges[self.classLevel - 1]));
self.weapon.x = 40;
// Create health bar
self.healthBarBg = self.attachAsset('health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5,
y: -80
});
self.healthBarFill = self.attachAsset('health_bar_fill', {
anchorX: 0,
anchorY: 0.5,
x: -100,
y: -80
});
self.takeDamage = function (damage) {
var actualDamage = damage;
// Check if blocking
if (self.isBlocking && self.blockTimer < 30) {
// Block only works for first 30 frames (0.5 seconds)
actualDamage = Math.floor(damage * 0.3); // Block reduces damage by 70%
// Visual feedback for successful block
LK.effects.flashObject(fighterGraphics, 0x00ff00, 200);
// Stop blocking after successful block
self.stopBlocking();
} else {
// Flash red when taking full damage
LK.effects.flashObject(fighterGraphics, 0xff0000, 300);
}
self.health -= actualDamage;
if (self.health < 0) self.health = 0;
// Update health bar
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 200 * healthPercent;
// Stop healing if taking damage
if (self.isHealing) {
self.stopHealing();
}
return self.health <= 0;
};
self.startHealing = function () {
if (self.isHealing || self.isDead() || self.hasSurrendered) return false;
// Find nearest corner for healing
var corners = [{
x: 150,
y: 250
},
// top-left
{
x: 1898,
y: 250
},
// top-right
{
x: 150,
y: 2482
},
// bottom-left
{
x: 1898,
y: 2482
} // bottom-right
];
var nearestCorner = corners[0];
var minDistance = Infinity;
for (var i = 0; i < corners.length; i++) {
var distance = Math.sqrt(Math.pow(corners[i].x - self.x, 2) + Math.pow(corners[i].y - self.y, 2));
if (distance < minDistance) {
minDistance = distance;
nearestCorner = corners[i];
}
}
self.healingPosition = nearestCorner;
self.isHealing = true;
self.healTimer = 0;
// Visual feedback - green tint while healing
tween(fighterGraphics, {
tint: 0x00ff00
}, {
duration: 300
});
return true;
};
self.stopHealing = function () {
if (!self.isHealing) return;
self.isHealing = false;
self.healTimer = 0;
self.healingPosition = null;
// Remove green tint
tween(fighterGraphics, {
tint: 0xffffff
}, {
duration: 300
});
};
self.startBlocking = function () {
if (self.isDead() || self.hasSurrendered || self.isHealing) return false;
if (LK.ticks - self.lastBlockTime < 120) return false; // 2 second cooldown between blocks
self.isBlocking = true;
self.blockTimer = 0;
self.lastBlockTime = LK.ticks;
// Visual feedback - blue tint while blocking
tween(fighterGraphics, {
tint: 0x0099ff,
scaleX: 0.9,
scaleY: 1.1
}, {
duration: 200
});
return true;
};
self.stopBlocking = function () {
if (!self.isBlocking) return;
self.isBlocking = false;
self.blockTimer = 0;
// Remove blue tint and restore normal scale
tween(fighterGraphics, {
tint: 0xffffff,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
};
self.surrender = function () {
if (self.hasSurrendered || self.isDead()) return false;
self.hasSurrendered = true;
self.stopHealing();
// Visual feedback - gray tint when surrendered
tween(fighterGraphics, {
tint: 0x666666,
alpha: 0.7
}, {
duration: 500
});
return true;
};
self.isDead = function () {
return self.health <= 0 || self.hasSurrendered;
};
self.attackTarget = function (target) {
if (!target || target.isDead()) return false;
var distance = Math.sqrt(Math.pow(target.x - self.x, 2) + Math.pow(target.y - self.y, 2));
if (distance <= self.weapon.range && self.weapon.attack()) {
var isDead = target.takeDamage(self.weapon.damage);
// Play sound
if (self.weapon.weaponType === 'fist') {
LK.getSound('punch').play();
} else {
LK.getSound('sword_hit').play();
}
return isDead;
}
return false;
};
self.moveTowards = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.moveSpeed;
self.y += dy / distance * self.moveSpeed;
}
};
self.aiUpdate = function (targets) {
if (self.isDead()) return;
self.aiTimer++;
// Check for surrender (20% chance when health is low)
if (self.health <= 30 && !self.hasSurrendered && Math.random() < 0.002) {
// 0.002 per frame ≈ 20% chance over time
self.surrender();
return;
}
// Check if should start healing (when health is below 40% and not currently healing)
if (self.health <= 40 && !self.isHealing && !self.hasSurrendered) {
// Find if any enemies are nearby before healing
var hasNearbyEnemies = false;
for (var i = 0; i < targets.length; i++) {
if (targets[i] !== self && !targets[i].isDead()) {
var distance = Math.sqrt(Math.pow(targets[i].x - self.x, 2) + Math.pow(targets[i].y - self.y, 2));
if (distance < 200) {
// Enemy within 200 pixels
hasNearbyEnemies = true;
break;
}
}
}
// Only start healing if no enemies nearby or health is critically low
if (!hasNearbyEnemies || self.health <= 20) {
self.startHealing();
}
}
// If healing, move to healing position
if (self.isHealing && self.healingPosition) {
var distanceToHealPos = Math.sqrt(Math.pow(self.healingPosition.x - self.x, 2) + Math.pow(self.healingPosition.y - self.y, 2));
if (distanceToHealPos > 10) {
self.moveTowards(self.healingPosition.x, self.healingPosition.y);
} else {
// At healing position, start healing
self.healTimer++;
if (self.healTimer >= 60) {
// Heal every 60 frames (1 second)
self.health = Math.min(self.maxHealth, self.health + 5);
// Update health bar
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 200 * healthPercent;
self.healTimer = 0;
// Stop healing when at full health or health above 70%
if (self.health >= self.maxHealth * 0.7) {
self.stopHealing();
}
}
}
return; // Don't do combat actions while healing
}
// Find nearest living target
var nearestTarget = null;
var nearestDistance = Infinity;
for (var i = 0; i < targets.length; i++) {
if (targets[i] !== self && !targets[i].isDead()) {
var distance = Math.sqrt(Math.pow(targets[i].x - self.x, 2) + Math.pow(targets[i].y - self.y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestTarget = targets[i];
}
}
}
if (nearestTarget) {
// Move towards target if too far
if (nearestDistance > self.weapon.range * 0.8) {
self.moveTowards(nearestTarget.x, nearestTarget.y);
}
// Attack if in range and cooldown is ready
if (nearestDistance <= self.weapon.range && self.aiTimer % 45 === 0) {
self.attackTarget(nearestTarget);
}
}
};
self.update = function () {
// Keep within game bounds
if (self.x < 100) self.x = 100;
if (self.x > 1948) self.x = 1948;
if (self.y < 200) self.y = 200;
if (self.y > 2532) self.y = 2532;
// Update blocking timer
if (self.isBlocking) {
self.blockTimer++;
// Auto-stop blocking after 30 frames (0.5 seconds)
if (self.blockTimer >= 30) {
self.stopBlocking();
}
}
};
return self;
});
var MenuButton = Container.expand(function (text, onClick) {
var self = Container.call(this);
var buttonBg = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
tween(buttonBg, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
if (onClick) onClick();
};
self.up = function (x, y, obj) {
tween(buttonBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
return self;
});
var MovementButton = Container.expand(function (direction, moveX, moveY) {
var self = Container.call(this);
self.direction = direction;
self.moveX = moveX;
self.moveY = moveY;
self.isPressed = false;
var buttonBg = self.attachAsset('move_button', {
anchorX: 0.5,
anchorY: 0.5
});
var arrow = self.attachAsset('move_button_arrow', {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate arrow based on direction
if (direction === 'up') arrow.rotation = -Math.PI / 2;else if (direction === 'down') arrow.rotation = Math.PI / 2;else if (direction === 'left') arrow.rotation = Math.PI;else if (direction === 'right') arrow.rotation = 0;
self.down = function (x, y, obj) {
self.isPressed = true;
tween(buttonBg, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x3498db
}, {
duration: 100
});
};
self.up = function (x, y, obj) {
self.isPressed = false;
tween(buttonBg, {
scaleX: 1,
scaleY: 1,
tint: 0xffffff
}, {
duration: 100
});
};
return self;
});
var Weapon = Container.expand(function (weaponType, damage, range) {
var self = Container.call(this);
self.weaponType = weaponType || 'fist';
self.damage = damage || 10;
self.range = range || 50;
self.cooldown = 0;
var weaponGraphics = self.attachAsset(self.weaponType, {
anchorX: 0.5,
anchorY: 0.5
});
self.canAttack = function () {
return self.cooldown <= 0;
};
self.attack = function () {
if (self.canAttack()) {
self.cooldown = 30; // 30 frames cooldown
tween(weaponGraphics, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 100
});
tween(weaponGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
return true;
}
return false;
};
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Sounds
// UI elements
// Weapon assets
// Player and AI character assets for different classes
// Game state
var gameState = 'menu'; // 'menu', 'battle', 'victory', 'defeat', 'bossBattle'
var battleMode = 'duel'; // 'duel' or 'freeforall'
var playerClass = 1;
var playerWins = 0;
var isBossBattle = false;
var currentBoss = null;
var cannons = [];
var cannonballs = [];
// UI elements
var titleText = new Text2('Medieval Beatdown', {
size: 80,
fill: 0xECF0F1
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
game.addChild(titleText);
var classText = new Text2('Class ' + playerClass + ' Fighter', {
size: 50,
fill: 0xF39C12
});
classText.anchor.set(0.5, 0.5);
classText.x = 1024;
classText.y = 500;
game.addChild(classText);
var winsText = new Text2('Wins: ' + playerWins, {
size: 40,
fill: 0x95A5A6
});
winsText.anchor.set(0.5, 0.5);
winsText.x = 1024;
winsText.y = 550;
game.addChild(winsText);
var duelButton = game.addChild(new MenuButton('1v1 Duel', function () {
startBattle('duel');
}));
duelButton.x = 1024;
duelButton.y = 700;
var freeForAllButton = game.addChild(new MenuButton('Free For All', function () {
startBattle('freeforall');
}));
freeForAllButton.x = 1024;
freeForAllButton.y = 820;
var bossButton = game.addChild(new MenuButton('Fight Boss', function () {
startBossBattle();
}));
bossButton.x = 1024;
bossButton.y = 940;
bossButton.visible = false; // Only show when player is ready
// Battle variables
var fighters = [];
var player = null;
var dragTarget = null;
var battleTimer = 0;
// Movement buttons
var moveButtons = [];
var upButton = null;
var downButton = null;
var leftButton = null;
var rightButton = null;
// Battle result text
var resultText = new Text2('', {
size: 60,
fill: 0xFFFFFF
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 1024;
resultText.y = 1366;
resultText.visible = false;
game.addChild(resultText);
var continueButton = game.addChild(new MenuButton('Continue', function () {
returnToMenu();
}));
continueButton.x = 1024;
continueButton.y = 1500;
continueButton.visible = false;
// Create movement buttons
upButton = game.addChild(new MovementButton('up', 0, -1));
upButton.x = 180;
upButton.y = 2400;
upButton.visible = false;
downButton = game.addChild(new MovementButton('down', 0, 1));
downButton.x = 180;
downButton.y = 2560;
downButton.visible = false;
leftButton = game.addChild(new MovementButton('left', -1, 0));
leftButton.x = 100;
leftButton.y = 2480;
leftButton.visible = false;
rightButton = game.addChild(new MovementButton('right', 1, 0));
rightButton.x = 260;
rightButton.y = 2480;
rightButton.visible = false;
moveButtons = [upButton, downButton, leftButton, rightButton];
var controlsText = new Text2('Tap corners to heal • Tap center top to surrender • Tap center bottom to block', {
size: 28,
fill: 0xBDC3C7
});
controlsText.anchor.set(0.5, 0.5);
controlsText.x = 1024;
controlsText.y = 150;
controlsText.visible = false;
// Hide movement buttons
for (var i = 0; i < moveButtons.length; i++) {
moveButtons[i].visible = false;
}
game.addChild(controlsText);
function startBattle(mode) {
gameState = 'battle';
battleMode = mode;
battleTimer = 0;
// Hide menu elements
titleText.visible = false;
classText.visible = false;
winsText.visible = false;
duelButton.visible = false;
freeForAllButton.visible = false;
resultText.visible = false;
continueButton.visible = false;
controlsText.visible = true;
// Show movement buttons
for (var i = 0; i < moveButtons.length; i++) {
moveButtons[i].visible = true;
}
// Show movement buttons
for (var i = 0; i < moveButtons.length; i++) {
moveButtons[i].visible = true;
}
// Clear previous fighters
for (var i = 0; i < fighters.length; i++) {
fighters[i].destroy();
}
fighters = [];
// Create player
player = game.addChild(new Fighter(true, playerClass));
player.x = 300;
player.y = 1366;
fighters.push(player);
// Create AI opponents
if (mode === 'duel') {
var enemy = game.addChild(new Fighter(false, playerClass));
enemy.x = 1700;
enemy.y = 1366;
fighters.push(enemy);
} else {
// Free for all - 3 AI opponents
for (var i = 0; i < 3; i++) {
var enemy = game.addChild(new Fighter(false, playerClass));
enemy.x = 1200 + i * 200;
enemy.y = 800 + i * 200;
fighters.push(enemy);
}
}
}
function startBossBattle() {
gameState = 'bossBattle';
isBossBattle = true;
battleTimer = 0;
// Hide menu elements
titleText.visible = false;
classText.visible = false;
winsText.visible = false;
duelButton.visible = false;
freeForAllButton.visible = false;
bossButton.visible = false;
resultText.visible = false;
continueButton.visible = false;
controlsText.visible = true;
// Clear previous fighters
for (var i = 0; i < fighters.length; i++) {
fighters[i].destroy();
}
fighters = [];
// Create player
player = game.addChild(new Fighter(true, playerClass));
player.x = 300;
player.y = 1366;
fighters.push(player);
// Create boss enemy
currentBoss = game.addChild(new Boss(playerClass));
currentBoss.x = 1700;
currentBoss.y = 1366;
fighters.push(currentBoss);
// Create cannons around the battlefield
var cannon1 = game.addChild(new Cannon(200, 600));
var cannon2 = game.addChild(new Cannon(1848, 600));
var cannon3 = game.addChild(new Cannon(1024, 300));
cannons.push(cannon1, cannon2, cannon3);
// Add dramatic entrance effect for boss
tween(currentBoss, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF0000
}, {
duration: 1000,
easing: tween.bounceOut
});
tween(currentBoss, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 500
});
}
function checkBattleEnd() {
var aliveFighters = [];
var playerAlive = false;
for (var i = 0; i < fighters.length; i++) {
if (!fighters[i].isDead()) {
aliveFighters.push(fighters[i]);
if (fighters[i].isPlayer) {
playerAlive = true;
}
}
}
if (aliveFighters.length <= 1) {
if (playerAlive) {
// Player wins
if (battleMode === 'freeforall') {
playerWins += 2; // Free for all gives 2 points
} else {
playerWins++; // Duel gives 1 point
}
if (isBossBattle) {
// Player defeated boss - advance to next class
if (playerClass < 5) {
playerClass++;
resultText.setText('BOSS DEFEATED! Advanced to Class ' + playerClass + '!');
} else {
resultText.setText('FINAL BOSS DEFEATED! You are the Champion!');
}
isBossBattle = false;
currentBoss = null;
} else {
// Check if player has enough wins to fight boss
var winsNeeded = playerClass * 3; // Need 3, 6, 9, 12, 15 wins for each class
if (playerWins >= winsNeeded && playerClass < 5) {
// Time for boss battle
resultText.setText('Victory! Ready for Boss Battle!');
gameState = 'readyForBoss';
} else {
resultText.setText('Victory! Wins: ' + playerWins + '/' + winsNeeded);
}
}
LK.getSound('victory').play();
} else {
// Player loses
if (isBossBattle) {
resultText.setText('Boss Defeated You! Try again!');
isBossBattle = false;
currentBoss = null;
} else {
resultText.setText('Defeat! Try again!');
}
LK.getSound('defeat').play();
}
gameState = 'battleEnd';
resultText.visible = true;
continueButton.visible = true;
LK.setTimeout(function () {
// Auto return to menu after 5 seconds
if (gameState === 'battleEnd') {
returnToMenu();
}
}, 5000);
}
}
function returnToMenu() {
gameState = 'menu';
// Update UI text
classText.setText('Class ' + playerClass + ' Fighter');
winsText.setText('Wins: ' + playerWins);
// Show menu elements
titleText.visible = true;
classText.visible = true;
winsText.visible = true;
resultText.visible = false;
continueButton.visible = false;
controlsText.visible = false;
// Show boss button if player has enough wins and not at max class
var winsNeeded = playerClass * 3;
if (playerWins >= winsNeeded && playerClass < 5) {
bossButton.visible = true;
// Hide other battle options when boss battle is required
duelButton.visible = false;
freeForAllButton.visible = false;
} else {
bossButton.visible = false;
// Show other battle options when boss battle is not available
duelButton.visible = true;
freeForAllButton.visible = true;
}
// Clear fighters
for (var i = 0; i < fighters.length; i++) {
fighters[i].destroy();
}
fighters = [];
// Clear cannons
for (var i = 0; i < cannons.length; i++) {
cannons[i].destroy();
}
cannons = [];
// Clear cannonballs
for (var i = 0; i < cannonballs.length; i++) {
cannonballs[i].destroy();
}
cannonballs = [];
player = null;
dragTarget = null;
isBossBattle = false;
currentBoss = null;
}
// Touch controls
game.down = function (x, y, obj) {
if (gameState === 'battle' && player && !player.isDead()) {
// Check for healing - double tap in corners
if (x < 200 && y < 400 || x > 1848 && y < 400 || x < 200 && y > 2332 || x > 1848 && y > 2332) {
if (!player.isHealing) {
player.startHealing();
} else {
player.stopHealing();
}
return;
}
// Check for surrender - double tap in center top area
if (x > 924 && x < 1124 && y > 100 && y < 300) {
if (!player.hasSurrendered) {
player.surrender();
}
return;
}
// Check for blocking - tap in center bottom area
if (x > 924 && x < 1124 && y > 2432 && y < 2632) {
if (!player.isBlocking) {
player.startBlocking();
}
return;
}
dragTarget = player;
// Check for attack - if touching near an enemy, attack instead of move
var attackRange = player.weapon.range + 50;
for (var i = 0; i < fighters.length; i++) {
var fighter = fighters[i];
if (fighter !== player && !fighter.isDead()) {
var distance = Math.sqrt(Math.pow(fighter.x - x, 2) + Math.pow(fighter.y - y, 2));
if (distance <= attackRange) {
player.attackTarget(fighter);
dragTarget = null;
return;
}
}
}
}
};
game.move = function (x, y, obj) {
if (gameState === 'battle' && dragTarget && !dragTarget.isDead()) {
dragTarget.moveTowards(x, y);
}
};
game.up = function (x, y, obj) {
dragTarget = null;
};
game.update = function () {
if (gameState === 'battle' || gameState === 'bossBattle') {
battleTimer++;
// Update all fighters
for (var i = 0; i < fighters.length; i++) {
var fighter = fighters[i];
if (!fighter.isDead()) {
if (!fighter.isPlayer) {
fighter.aiUpdate(fighters);
}
}
}
// Update cannons
for (var i = 0; i < cannons.length; i++) {
cannons[i].update();
}
// Update cannonballs and remove destroyed ones
for (var i = cannonballs.length - 1; i >= 0; i--) {
var cannonball = cannonballs[i];
if (cannonball.parent) {
cannonball.update();
} else {
// Cannonball was destroyed, remove from array
cannonballs.splice(i, 1);
}
}
// Handle movement button input
if (player && !player.isDead()) {
var moveSpeed = player.moveSpeed * 2; // Faster movement with buttons
if (upButton.isPressed) {
player.y -= moveSpeed;
}
if (downButton.isPressed) {
player.y += moveSpeed;
}
if (leftButton.isPressed) {
player.x -= moveSpeed;
}
if (rightButton.isPressed) {
player.x += moveSpeed;
}
}
// Check for battle end
checkBattleEnd();
}
};