/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AttackButton = Container.expand(function (text) {
var self = Container.call(this);
var buttonBg = self.attachAsset('attackButton', {
anchorX: 0.5,
anchorY: 0.5
});
buttonBg.tint = 0xff4444;
var buttonText = new Text2(text, {
size: 70,
fill: '#ffffff'
});
buttonText.anchor.set(0.5, 0.5);
buttonText.x = 0;
buttonText.y = 0;
self.addChild(buttonText);
self.setText = function (newText) {
buttonText.setText(newText);
};
self.down = function (x, y, obj) {
if (gameState === 'ready') {
// Add press effect
tween(buttonBg, {
scaleX: 0.95,
scaleY: 0.95,
tint: 0xcc3333
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(buttonBg, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xff4444
}, {
duration: 150,
easing: tween.bounceOut
});
}
});
performBattle();
}
};
return self;
});
var ClassButton = Container.expand(function (className, description, skills) {
var self = Container.call(this);
var buttonBg = self.attachAsset('classButton', {
anchorX: 0.5,
anchorY: 0.5
});
var classText = new Text2(className, {
size: 50,
fill: '#000000'
});
classText.anchor.set(0.5, 0.5);
classText.x = 0;
classText.y = -30;
self.addChild(classText);
var descText = new Text2(description, {
size: 30,
fill: '#000000'
});
descText.anchor.set(0.5, 0.5);
descText.x = 0;
descText.y = 30;
self.addChild(descText);
self.className = className;
self.skills = skills;
self.down = function (x, y, obj) {
if (gameState === 'selectClass') {
selectedClass = self;
startGame();
}
};
return self;
});
var CoinButton = Container.expand(function (coinCount) {
var self = Container.call(this);
var buttonBg = self.attachAsset('coinButton', {
anchorX: 0.5,
anchorY: 0.5
});
var coinText = new Text2(coinCount.toString(), {
size: 80,
fill: '#000000'
});
coinText.anchor.set(0.5, 0.5);
coinText.x = 0;
coinText.y = 0;
self.addChild(coinText);
self.coinCount = coinCount;
self.down = function (x, y, obj) {
if (gameState === 'selectCoins') {
selectedCoins = self.coinCount;
gameState = 'ready';
readyButton.alpha = 1;
}
};
return self;
});
var EnemyCard = Container.expand(function () {
var self = Container.call(this);
var cardBg = self.attachAsset('enemyCard', {
anchorX: 0.5,
anchorY: 0.5
});
var nameText = new Text2('ENEMY', {
size: 60,
fill: '#000000'
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 0;
nameText.y = -140;
self.addChild(nameText);
self.setEnemyName = function (name) {
nameText.setText(name);
};
var healthBorder = self.attachAsset('healthBarBorder', {
anchorX: 0.5,
anchorY: 0.5
});
healthBorder.x = 0;
healthBorder.y = -60;
var healthBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBg.x = 0;
healthBg.y = -60;
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -60;
var healthText = new Text2('100/100', {
size: 48,
fill: '#000000'
});
healthText.anchor.set(0.5, 0.5);
healthText.x = 0;
healthText.y = -60;
self.addChild(healthText);
self.updateHealth = function (current, max) {
healthText.setText(current + '/' + max);
var healthPercent = current / max;
healthBar.scaleX = healthPercent;
healthBar.x = -250 + 250 * healthPercent;
};
return self;
});
var FlippingCoin = Container.expand(function () {
var self = Container.call(this);
var coinGraphic = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.isFlipping = false;
self.result = null;
self.flip = function (callback, isPlayer) {
self.isFlipping = true;
self.result = Math.random() < 0.5 ? 'heads' : 'tails';
// Play different sound based on player/enemy
if (isPlayer === true) {
LK.getSound('playerCoinFlip').play();
} else if (isPlayer === false) {
LK.getSound('enemyCoinFlip').play();
} else {
LK.getSound('coinFlip').play();
}
// Add initial scale and brightness effect
tween(coinGraphic, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffffff
}, {
duration: 100,
easing: tween.easeOut
});
tween(coinGraphic, {
rotation: Math.PI * 6,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1800,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isFlipping = false;
if (self.result === 'heads') {
coinGraphic.tint = 0x00ff66;
// Success sparkle effect
tween(coinGraphic, {
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(coinGraphic, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
} else {
coinGraphic.tint = 0xff3366;
// Failure shake effect
tween(coinGraphic, {
x: coinGraphic.x + 10
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coinGraphic, {
x: coinGraphic.x - 10
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
}
if (callback) callback(self.result);
}
});
};
return self;
});
var PlayerCard = Container.expand(function () {
var self = Container.call(this);
var cardBg = self.attachAsset('playerCard', {
anchorX: 0.5,
anchorY: 0.5
});
var nameText = new Text2('PLAYER', {
size: 60,
fill: '#000000'
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 0;
nameText.y = -140;
self.addChild(nameText);
var healthBorder = self.attachAsset('healthBarBorder', {
anchorX: 0.5,
anchorY: 0.5
});
healthBorder.x = 0;
healthBorder.y = -60;
var healthBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBg.x = 0;
healthBg.y = -60;
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -60;
var healthText = new Text2('100/100', {
size: 48,
fill: '#000000'
});
healthText.anchor.set(0.5, 0.5);
healthText.x = 0;
healthText.y = -60;
self.addChild(healthText);
self.updateHealth = function (current, max) {
healthText.setText(current + '/' + max);
var healthPercent = current / max;
healthBar.scaleX = healthPercent;
healthBar.x = -250 + 250 * healthPercent;
};
return self;
});
var SkillButton = Container.expand(function (skillName, basePower, coinCount) {
var self = Container.call(this);
var buttonBorder = self.attachAsset('skillButtonBorder', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonBg = self.attachAsset('skillButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Calculate power range based on new balanced formula
var minPower = Math.max(1, Math.floor(basePower * 0.3));
var bonusPerSuccess = Math.max(2, Math.floor(basePower * 0.8));
var maxPower = minPower + coinCount * bonusPerSuccess;
var skillText = new Text2(skillName + ' (Power: ' + minPower + '-' + maxPower + ', Coins: ' + coinCount + ')', {
size: 50,
fill: '#000000'
});
skillText.anchor.set(0.5, 0.5);
skillText.x = 0;
skillText.y = 0;
self.addChild(skillText);
self.skillName = skillName;
self.basePower = basePower;
self.coinCount = coinCount;
self.minPower = minPower;
self.maxPower = maxPower;
self.selected = false;
self.setSelected = function (selected) {
self.selected = selected;
if (selected) {
buttonBg.tint = 0x00ff88;
buttonBorder.tint = 0xff8800;
tween(buttonBorder, {
scaleX: 1.08,
scaleY: 1.08
}, {
duration: 200,
easing: tween.easeOut
});
// Add pulsing glow effect
tween(buttonBg, {
alpha: 0.7
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(buttonBg, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.selected) {
self.setSelected(true); // Continue pulsing
}
}
});
}
});
} else {
buttonBg.tint = 0x27ae60;
buttonBorder.tint = 0x1e8449;
buttonBg.alpha = 1;
tween(buttonBorder, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.down = function (x, y, obj) {
if (gameState === 'selectSkill') {
selectedSkill = self;
skill1Button.setSelected(false);
skill2Button.setSelected(false);
self.setSelected(true);
gameState = 'ready';
readyButton.alpha = 1;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x34495e
});
/****
* Game Code
****/
// Game state variables
var gameState = 'selectClass';
var playerMaxHealth = 80 + Math.floor(Math.random() * 21); // 80-100 HP
var enemyMaxHealth = 80 + Math.floor(Math.random() * 21); // 80-100 HP
var playerHealth = playerMaxHealth;
var enemyHealth = enemyMaxHealth;
// Class definitions with their specific skills
var playerClasses = [{
name: 'Spearman',
description: 'Balanced fighter with reliable attacks',
skills: [{
name: 'Piercing Strike',
power: 6,
coins: 2
}, {
name: 'Whirlwind Assault',
power: 3,
coins: 5
}]
}, {
name: 'Samurai',
description: 'High-risk, high-reward warrior',
skills: [{
name: 'Perfect Cut',
power: 18,
coins: 1
}, {
name: 'Flurry Combo',
power: 2,
coins: 6
}]
}, {
name: 'Berserker',
description: 'Chaotic fighter with extreme variance',
skills: [{
name: 'Reckless Charge',
power: 5,
coins: 3
}, {
name: 'Fury Unleashed',
power: 1,
coins: 8
}]
}];
// Player skills will be assigned after class selection
var playerSkills = [];
var selectedClass = null;
// Enemy types with specialized skill sets
var enemyTypes = [{
name: 'Shadow Assassin',
description: 'Fast and precise attacks',
skills: [{
name: 'Shadow Strike',
power: 14,
coins: 1
}, {
name: 'Poison Dart Volley',
power: 3,
coins: 4
}]
}, {
name: 'Bone Golem',
description: 'Slow but devastating attacks',
skills: [{
name: 'Bone Crush',
power: 8,
coins: 2
}, {
name: 'Skeletal Rampage',
power: 2,
coins: 7
}]
}, {
name: 'Fire Demon',
description: 'Chaotic flame attacks',
skills: [{
name: 'Hellfire Blast',
power: 11,
coins: 1
}, {
name: 'Inferno Storm',
power: 4,
coins: 5
}]
}, {
name: 'Void Wraith',
description: 'Energy-based attacks',
skills: [{
name: 'Void Pulse',
power: 6,
coins: 3
}, {
name: 'Soul Drain',
power: 16,
coins: 1
}]
}, {
name: 'Frost Giant',
description: 'Ice-based powerful attacks',
skills: [{
name: 'Glacial Slam',
power: 9,
coins: 2
}, {
name: 'Blizzard Fury',
power: 3,
coins: 6
}]
}];
// Current enemy (will be randomly selected)
var currentEnemy = null;
var enemySkills = [];
// Current selection
var selectedSkill = null;
// Create class selection UI
var classSelectionTitle = new Text2('Choose Your Class', {
size: 80,
fill: '#000000'
});
classSelectionTitle.anchor.set(0.5, 0.5);
classSelectionTitle.x = 1024;
classSelectionTitle.y = 400;
game.addChild(classSelectionTitle);
var classButtons = [];
for (var i = 0; i < playerClasses.length; i++) {
var classButton = new ClassButton(playerClasses[i].name, playerClasses[i].description, playerClasses[i].skills);
classButton.x = 1024;
classButton.y = 700 + i * 300;
classButtons.push(classButton);
game.addChild(classButton);
}
// Game UI elements (will be created after class selection)
var playerCard = null;
var enemyCard = null;
var skill1Button = null;
var skill2Button = null;
var readyButton = null;
var statusText = null;
var enemySkillText = null;
// Battle result area
var battleResultText = new Text2('', {
size: 60,
fill: '#000000'
});
battleResultText.anchor.set(0.5, 0.5);
battleResultText.x = 1024;
battleResultText.y = 1750;
game.addChild(battleResultText);
// Coin flip display area
var coinFlipContainer = new Container();
coinFlipContainer.x = 1024;
coinFlipContainer.y = 1550;
game.addChild(coinFlipContainer);
// Live damage numbers container
var liveNumbersContainer = new Container();
liveNumbersContainer.x = 1024;
liveNumbersContainer.y = 1800;
game.addChild(liveNumbersContainer);
function showLiveDamageNumber(damage, x, y, isPlayer) {
var damageText = new Text2('-' + damage, {
size: 100,
fill: isPlayer ? '#ff0066' : '#ff6600'
});
damageText.anchor.set(0.5, 0.5);
damageText.x = x;
damageText.y = y;
damageText.alpha = 0;
damageText.scaleX = 0.5;
damageText.scaleY = 0.5;
liveNumbersContainer.addChild(damageText);
// Pop in effect
tween(damageText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Scale back and float up
tween(damageText, {
scaleX: 1.0,
scaleY: 1.0,
y: y - 120,
alpha: 0
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
liveNumbersContainer.removeChild(damageText);
}
});
}
});
}
function startGame() {
// Assign skills based on selected class
playerSkills = selectedClass.skills;
// Adjust health based on class - balanced fighters get more health
if (selectedClass.name === 'Spearman') {
playerMaxHealth += 20; // Balanced fighter gets extra durability
} else if (selectedClass.name === 'Samurai') {
playerMaxHealth -= 10; // High-risk fighter gets less health
} else if (selectedClass.name === 'Berserker') {
playerMaxHealth += 10; // Chaotic fighter gets moderate health boost
}
playerHealth = playerMaxHealth;
// Select random enemy type
var enemyIndex = Math.floor(Math.random() * enemyTypes.length);
currentEnemy = enemyTypes[enemyIndex];
enemySkills = currentEnemy.skills;
// Hide class selection UI
classSelectionTitle.alpha = 0;
for (var i = 0; i < classButtons.length; i++) {
classButtons[i].alpha = 0;
}
// Create game UI elements
playerCard = game.addChild(new PlayerCard());
playerCard.x = 1024;
playerCard.y = 600;
enemyCard = game.addChild(new EnemyCard());
enemyCard.x = 1024;
enemyCard.y = 300;
// Skills selection - positioned below player card with more spacing
skill1Button = game.addChild(new SkillButton(playerSkills[0].name, playerSkills[0].power, playerSkills[0].coins));
skill1Button.x = 1024;
skill1Button.y = 950;
skill2Button = game.addChild(new SkillButton(playerSkills[1].name, playerSkills[1].power, playerSkills[1].coins));
skill2Button.x = 1024;
skill2Button.y = 1130;
// Attack button
readyButton = game.addChild(new AttackButton('BATTLE!'));
readyButton.x = 1024;
readyButton.y = 1350;
readyButton.alpha = 0.5;
// Status text - positioned between player card and skill buttons
statusText = new Text2('Choose your skill', {
size: 70,
fill: '#000000'
});
statusText.anchor.set(0.5, 0.5);
statusText.x = 1024;
statusText.y = 850;
game.addChild(statusText);
// Enemy description text
var enemyDescText = new Text2(currentEnemy.description, {
size: 40,
fill: '#666666'
});
enemyDescText.anchor.set(0.5, 0.5);
enemyDescText.x = 1024;
enemyDescText.y = 120;
game.addChild(enemyDescText);
// Enemy skill display text - positioned higher to avoid blocking
enemySkillText = new Text2('', {
size: 50,
fill: '#000000'
});
enemySkillText.anchor.set(0.5, 0.5);
enemySkillText.x = 1024;
enemySkillText.y = 150;
game.addChild(enemySkillText);
// Initialize health displays
playerCard.updateHealth(playerHealth, playerMaxHealth);
enemyCard.updateHealth(enemyHealth, enemyMaxHealth);
enemyCard.setEnemyName(currentEnemy.name);
// Change game state
gameState = 'selectSkill';
}
// Battle logic
function performBattle() {
if (!selectedSkill) return;
gameState = 'battle';
statusText.setText('Battle in progress...');
readyButton.alpha = 0.5;
// Player's turn
var playerBasePower = selectedSkill.basePower;
var playerCoins = selectedSkill.coinCount;
// Enemy's turn (AI)
var enemySkillIndex = Math.floor(Math.random() * enemySkills.length);
var enemySkill = enemySkills[enemySkillIndex];
var enemyBasePower = enemySkill.power;
var enemyCoins = enemySkill.coins;
var enemyMinPower = Math.max(1, Math.floor(enemyBasePower * 0.3));
var enemyBonusPerSuccess = Math.max(2, Math.floor(enemyBasePower * 0.8));
var enemyMaxPower = enemyMinPower + enemyCoins * enemyBonusPerSuccess;
// Display enemy's planned skill
enemySkillText.setText('Enemy plans: ' + enemySkill.name + ' (Power: ' + enemyMinPower + '-' + enemyMaxPower + ', ' + enemyCoins + ' coins)');
// Clear previous coins
coinFlipContainer.removeChildren();
// Flip player coins
var playerSuccessfulFlips = 0;
var playerCoinsFlipped = 0;
for (var i = 0; i < playerCoins; i++) {
var coin = new FlippingCoin();
coin.x = (i - (playerCoins - 1) / 2) * 100;
coin.y = -50;
coinFlipContainer.addChild(coin);
// Set player coin color
var coinGraphic = coin.getChildAt(0);
coinGraphic.tint = 0x4CAF50; // Green for player coins
// Add P text label for player coins
var playerLabel = new Text2('P', {
size: 40,
fill: '#ffffff'
});
playerLabel.anchor.set(0.5, 0.5);
playerLabel.x = 0;
playerLabel.y = 0;
coin.addChild(playerLabel);
LK.setTimeout(function (coinRef) {
return function () {
coinRef.flip(function (result) {
if (result === 'heads') {
playerSuccessfulFlips++;
}
playerCoinsFlipped++;
if (playerCoinsFlipped === playerCoins) {
// All player coins flipped, now flip enemy coins
flipEnemyCoins();
}
}, true);
};
}(coin), i * 800);
}
function flipEnemyCoins() {
var enemySuccessfulFlips = 0;
var enemyCoinsFlipped = 0;
for (var i = 0; i < enemyCoins; i++) {
var coin = new FlippingCoin();
coin.x = (i - (enemyCoins - 1) / 2) * 100;
coin.y = 50;
coinFlipContainer.addChild(coin);
// Set enemy coin color
var coinGraphic = coin.getChildAt(0);
coinGraphic.tint = 0xF44336; // Red for enemy coins
// Add E text label for enemy coins
var enemyLabel = new Text2('E', {
size: 40,
fill: '#ffffff'
});
enemyLabel.anchor.set(0.5, 0.5);
enemyLabel.x = 0;
enemyLabel.y = 0;
coin.addChild(enemyLabel);
LK.setTimeout(function (coinRef) {
return function () {
coinRef.flip(function (result) {
if (result === 'heads') {
enemySuccessfulFlips++;
}
enemyCoinsFlipped++;
if (enemyCoinsFlipped === enemyCoins) {
// All coins flipped, resolve battle
resolveBattle(playerBasePower, playerSuccessfulFlips, playerCoins, enemyBasePower, enemySuccessfulFlips, enemyCoins, enemySkill.name);
}
}, false);
};
}(coin), i * 800 + 3000);
}
}
}
function resolveBattle(playerBasePower, playerSuccessfulFlips, playerCoins, enemyBasePower, enemySuccessfulFlips, enemyCoins, enemySkillName) {
var playerTotalPower = playerBasePower + playerSuccessfulFlips * 2;
var enemyTotalPower = enemyBasePower + enemySuccessfulFlips * 2;
var resultText = 'CLASH RESULTS:\n';
resultText += 'Player: ' + playerBasePower + ' base + ' + playerSuccessfulFlips + '/' + playerCoins + ' heads = ' + playerTotalPower + '\n';
resultText += 'Enemy: ' + enemyBasePower + ' base + ' + enemySuccessfulFlips + '/' + enemyCoins + ' heads = ' + enemyTotalPower + '\n';
if (playerTotalPower > enemyTotalPower) {
// Player wins - roll attack coins
resultText += 'Player wins! Rolling ' + playerCoins + ' attack coins...\n';
rollAttackCoins(playerCoins, true, function (successfulAttacks) {
var playerMinPower = Math.max(1, Math.floor(playerBasePower * 0.3));
var bonusPerSuccess = Math.max(2, Math.floor(playerBasePower * 0.8));
var totalDamage = playerMinPower + successfulAttacks * bonusPerSuccess;
if (successfulAttacks === playerCoins) {
totalDamage = Math.floor(totalDamage * 1.5); // 50% damage bonus for perfect flips
resultText += 'CRITICAL HIT! Perfect flip grants 50% bonus damage!\n';
}
enemyHealth -= totalDamage;
resultText += 'Enemy takes ' + totalDamage + ' damage (' + successfulAttacks + '/' + playerCoins + ' successful attacks)!';
// Show live damage number
showLiveDamageNumber(totalDamage, 0, -100, false);
LK.getSound('attack').play();
LK.getSound('victory').play();
// Flash enemy red
tween(enemyCard, {
tint: 0xff0000
}, {
duration: 800,
onFinish: function onFinish() {
tween(enemyCard, {
tint: 0xffffff
}, {
duration: 800
});
}
});
battleResultText.setText(resultText);
finalizeBattle();
});
} else if (enemyTotalPower > playerTotalPower) {
// Enemy wins - roll attack coins
resultText += 'Enemy wins with ' + enemySkillName + '! Rolling ' + enemyCoins + ' attack coins...\n';
rollAttackCoins(enemyCoins, false, function (successfulAttacks) {
var enemyMinPower = Math.max(1, Math.floor(enemyBasePower * 0.3));
var bonusPerSuccess = Math.max(2, Math.floor(enemyBasePower * 0.8));
var totalDamage = enemyMinPower + successfulAttacks * bonusPerSuccess;
if (successfulAttacks === enemyCoins) {
totalDamage = Math.floor(totalDamage * 1.5); // 50% damage bonus for perfect flips
resultText += 'ENEMY CRITICAL HIT! Perfect flip grants 50% bonus damage!\n';
}
playerHealth -= totalDamage;
resultText += 'Player takes ' + totalDamage + ' damage (' + successfulAttacks + '/' + enemyCoins + ' successful attacks)!';
// Show live damage number
showLiveDamageNumber(totalDamage, 0, 100, true);
LK.getSound('damage').play();
LK.getSound('defeat').play();
// Flash player red
tween(playerCard, {
tint: 0xff0000
}, {
duration: 800,
onFinish: function onFinish() {
tween(playerCard, {
tint: 0xffffff
}, {
duration: 800
});
}
});
battleResultText.setText(resultText);
finalizeBattle();
});
} else {
// Tie
resultText += 'It\'s a tie! No damage dealt.';
battleResultText.setText(resultText);
finalizeBattle();
}
function rollAttackCoins(coinCount, isPlayer, callback) {
// Clear previous attack coins
var attackCoinsContainer = new Container();
attackCoinsContainer.x = 0;
attackCoinsContainer.y = isPlayer ? 150 : -150;
coinFlipContainer.addChild(attackCoinsContainer);
var successfulAttacks = 0;
var coinsFlipped = 0;
for (var i = 0; i < coinCount; i++) {
var coin = new FlippingCoin();
coin.x = (i - (coinCount - 1) / 2) * 80;
coin.y = 0;
attackCoinsContainer.addChild(coin);
// Set attack coin color based on player/enemy
var coinGraphic = coin.getChildAt(0);
coinGraphic.tint = isPlayer ? 0x2196F3 : 0xFF5722; // Blue for player attack, orange for enemy attack
// Add P/E text label for attack coins
var attackLabel = new Text2(isPlayer ? 'P' : 'E', {
size: 40,
fill: '#ffffff'
});
attackLabel.anchor.set(0.5, 0.5);
attackLabel.x = 0;
attackLabel.y = 0;
coin.addChild(attackLabel);
LK.setTimeout(function (coinRef) {
return function () {
coinRef.flip(function (result) {
if (result === 'heads') {
successfulAttacks++;
}
coinsFlipped++;
if (coinsFlipped === coinCount) {
callback(successfulAttacks);
}
}, isPlayer);
};
}(coin), i * 400);
}
}
function finalizeBattle() {
// Update health displays
playerCard.updateHealth(Math.max(0, playerHealth), playerMaxHealth);
enemyCard.updateHealth(Math.max(0, enemyHealth), enemyMaxHealth);
// Check for game over
LK.setTimeout(function () {
if (playerHealth <= 0) {
LK.showGameOver();
} else if (enemyHealth <= 0) {
LK.showYouWin();
} else {
// Reset for next turn
resetTurn();
}
}, 4000);
}
}
function resetTurn() {
gameState = 'selectSkill';
selectedSkill = null;
skill1Button.setSelected(false);
skill2Button.setSelected(false);
readyButton.alpha = 0.5;
statusText.setText('Choose your skill');
enemySkillText.setText('');
// Clear coin flips
LK.setTimeout(function () {
coinFlipContainer.removeChildren();
}, 2000);
}
// Game update loop
game.update = function () {
// Update status text based on game state
if (gameState === 'selectClass') {
// Class selection phase - no status updates needed
} else if (gameState === 'selectSkill') {
if (statusText) statusText.setText('Choose your skill');
if (enemySkillText) enemySkillText.setText('');
} else if (gameState === 'ready') {
if (statusText) statusText.setText('Ready to battle with ' + selectedSkill.skillName + ' (Power: ' + selectedSkill.minPower + '-' + selectedSkill.maxPower + ', ' + selectedSkill.coinCount + ' coins)!');
// Show enemy's planned skill when player is ready
var enemySkillIndex = Math.floor(Math.random() * enemySkills.length);
var enemySkill = enemySkills[enemySkillIndex];
var enemyMinPower = Math.max(1, Math.floor(enemySkill.power * 0.3));
var enemyBonusPerSuccess = Math.max(2, Math.floor(enemySkill.power * 0.8));
var enemyMaxPower = enemyMinPower + enemySkill.coins * enemyBonusPerSuccess;
if (enemySkillText) enemySkillText.setText('Enemy plans: ' + enemySkill.name + ' (Power: ' + enemyMinPower + '-' + enemyMaxPower + ', ' + enemySkill.coins + ' coins)');
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AttackButton = Container.expand(function (text) {
var self = Container.call(this);
var buttonBg = self.attachAsset('attackButton', {
anchorX: 0.5,
anchorY: 0.5
});
buttonBg.tint = 0xff4444;
var buttonText = new Text2(text, {
size: 70,
fill: '#ffffff'
});
buttonText.anchor.set(0.5, 0.5);
buttonText.x = 0;
buttonText.y = 0;
self.addChild(buttonText);
self.setText = function (newText) {
buttonText.setText(newText);
};
self.down = function (x, y, obj) {
if (gameState === 'ready') {
// Add press effect
tween(buttonBg, {
scaleX: 0.95,
scaleY: 0.95,
tint: 0xcc3333
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(buttonBg, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xff4444
}, {
duration: 150,
easing: tween.bounceOut
});
}
});
performBattle();
}
};
return self;
});
var ClassButton = Container.expand(function (className, description, skills) {
var self = Container.call(this);
var buttonBg = self.attachAsset('classButton', {
anchorX: 0.5,
anchorY: 0.5
});
var classText = new Text2(className, {
size: 50,
fill: '#000000'
});
classText.anchor.set(0.5, 0.5);
classText.x = 0;
classText.y = -30;
self.addChild(classText);
var descText = new Text2(description, {
size: 30,
fill: '#000000'
});
descText.anchor.set(0.5, 0.5);
descText.x = 0;
descText.y = 30;
self.addChild(descText);
self.className = className;
self.skills = skills;
self.down = function (x, y, obj) {
if (gameState === 'selectClass') {
selectedClass = self;
startGame();
}
};
return self;
});
var CoinButton = Container.expand(function (coinCount) {
var self = Container.call(this);
var buttonBg = self.attachAsset('coinButton', {
anchorX: 0.5,
anchorY: 0.5
});
var coinText = new Text2(coinCount.toString(), {
size: 80,
fill: '#000000'
});
coinText.anchor.set(0.5, 0.5);
coinText.x = 0;
coinText.y = 0;
self.addChild(coinText);
self.coinCount = coinCount;
self.down = function (x, y, obj) {
if (gameState === 'selectCoins') {
selectedCoins = self.coinCount;
gameState = 'ready';
readyButton.alpha = 1;
}
};
return self;
});
var EnemyCard = Container.expand(function () {
var self = Container.call(this);
var cardBg = self.attachAsset('enemyCard', {
anchorX: 0.5,
anchorY: 0.5
});
var nameText = new Text2('ENEMY', {
size: 60,
fill: '#000000'
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 0;
nameText.y = -140;
self.addChild(nameText);
self.setEnemyName = function (name) {
nameText.setText(name);
};
var healthBorder = self.attachAsset('healthBarBorder', {
anchorX: 0.5,
anchorY: 0.5
});
healthBorder.x = 0;
healthBorder.y = -60;
var healthBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBg.x = 0;
healthBg.y = -60;
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -60;
var healthText = new Text2('100/100', {
size: 48,
fill: '#000000'
});
healthText.anchor.set(0.5, 0.5);
healthText.x = 0;
healthText.y = -60;
self.addChild(healthText);
self.updateHealth = function (current, max) {
healthText.setText(current + '/' + max);
var healthPercent = current / max;
healthBar.scaleX = healthPercent;
healthBar.x = -250 + 250 * healthPercent;
};
return self;
});
var FlippingCoin = Container.expand(function () {
var self = Container.call(this);
var coinGraphic = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.isFlipping = false;
self.result = null;
self.flip = function (callback, isPlayer) {
self.isFlipping = true;
self.result = Math.random() < 0.5 ? 'heads' : 'tails';
// Play different sound based on player/enemy
if (isPlayer === true) {
LK.getSound('playerCoinFlip').play();
} else if (isPlayer === false) {
LK.getSound('enemyCoinFlip').play();
} else {
LK.getSound('coinFlip').play();
}
// Add initial scale and brightness effect
tween(coinGraphic, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffffff
}, {
duration: 100,
easing: tween.easeOut
});
tween(coinGraphic, {
rotation: Math.PI * 6,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1800,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isFlipping = false;
if (self.result === 'heads') {
coinGraphic.tint = 0x00ff66;
// Success sparkle effect
tween(coinGraphic, {
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(coinGraphic, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
} else {
coinGraphic.tint = 0xff3366;
// Failure shake effect
tween(coinGraphic, {
x: coinGraphic.x + 10
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coinGraphic, {
x: coinGraphic.x - 10
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
}
if (callback) callback(self.result);
}
});
};
return self;
});
var PlayerCard = Container.expand(function () {
var self = Container.call(this);
var cardBg = self.attachAsset('playerCard', {
anchorX: 0.5,
anchorY: 0.5
});
var nameText = new Text2('PLAYER', {
size: 60,
fill: '#000000'
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 0;
nameText.y = -140;
self.addChild(nameText);
var healthBorder = self.attachAsset('healthBarBorder', {
anchorX: 0.5,
anchorY: 0.5
});
healthBorder.x = 0;
healthBorder.y = -60;
var healthBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBg.x = 0;
healthBg.y = -60;
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -60;
var healthText = new Text2('100/100', {
size: 48,
fill: '#000000'
});
healthText.anchor.set(0.5, 0.5);
healthText.x = 0;
healthText.y = -60;
self.addChild(healthText);
self.updateHealth = function (current, max) {
healthText.setText(current + '/' + max);
var healthPercent = current / max;
healthBar.scaleX = healthPercent;
healthBar.x = -250 + 250 * healthPercent;
};
return self;
});
var SkillButton = Container.expand(function (skillName, basePower, coinCount) {
var self = Container.call(this);
var buttonBorder = self.attachAsset('skillButtonBorder', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonBg = self.attachAsset('skillButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Calculate power range based on new balanced formula
var minPower = Math.max(1, Math.floor(basePower * 0.3));
var bonusPerSuccess = Math.max(2, Math.floor(basePower * 0.8));
var maxPower = minPower + coinCount * bonusPerSuccess;
var skillText = new Text2(skillName + ' (Power: ' + minPower + '-' + maxPower + ', Coins: ' + coinCount + ')', {
size: 50,
fill: '#000000'
});
skillText.anchor.set(0.5, 0.5);
skillText.x = 0;
skillText.y = 0;
self.addChild(skillText);
self.skillName = skillName;
self.basePower = basePower;
self.coinCount = coinCount;
self.minPower = minPower;
self.maxPower = maxPower;
self.selected = false;
self.setSelected = function (selected) {
self.selected = selected;
if (selected) {
buttonBg.tint = 0x00ff88;
buttonBorder.tint = 0xff8800;
tween(buttonBorder, {
scaleX: 1.08,
scaleY: 1.08
}, {
duration: 200,
easing: tween.easeOut
});
// Add pulsing glow effect
tween(buttonBg, {
alpha: 0.7
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(buttonBg, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.selected) {
self.setSelected(true); // Continue pulsing
}
}
});
}
});
} else {
buttonBg.tint = 0x27ae60;
buttonBorder.tint = 0x1e8449;
buttonBg.alpha = 1;
tween(buttonBorder, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.down = function (x, y, obj) {
if (gameState === 'selectSkill') {
selectedSkill = self;
skill1Button.setSelected(false);
skill2Button.setSelected(false);
self.setSelected(true);
gameState = 'ready';
readyButton.alpha = 1;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x34495e
});
/****
* Game Code
****/
// Game state variables
var gameState = 'selectClass';
var playerMaxHealth = 80 + Math.floor(Math.random() * 21); // 80-100 HP
var enemyMaxHealth = 80 + Math.floor(Math.random() * 21); // 80-100 HP
var playerHealth = playerMaxHealth;
var enemyHealth = enemyMaxHealth;
// Class definitions with their specific skills
var playerClasses = [{
name: 'Spearman',
description: 'Balanced fighter with reliable attacks',
skills: [{
name: 'Piercing Strike',
power: 6,
coins: 2
}, {
name: 'Whirlwind Assault',
power: 3,
coins: 5
}]
}, {
name: 'Samurai',
description: 'High-risk, high-reward warrior',
skills: [{
name: 'Perfect Cut',
power: 18,
coins: 1
}, {
name: 'Flurry Combo',
power: 2,
coins: 6
}]
}, {
name: 'Berserker',
description: 'Chaotic fighter with extreme variance',
skills: [{
name: 'Reckless Charge',
power: 5,
coins: 3
}, {
name: 'Fury Unleashed',
power: 1,
coins: 8
}]
}];
// Player skills will be assigned after class selection
var playerSkills = [];
var selectedClass = null;
// Enemy types with specialized skill sets
var enemyTypes = [{
name: 'Shadow Assassin',
description: 'Fast and precise attacks',
skills: [{
name: 'Shadow Strike',
power: 14,
coins: 1
}, {
name: 'Poison Dart Volley',
power: 3,
coins: 4
}]
}, {
name: 'Bone Golem',
description: 'Slow but devastating attacks',
skills: [{
name: 'Bone Crush',
power: 8,
coins: 2
}, {
name: 'Skeletal Rampage',
power: 2,
coins: 7
}]
}, {
name: 'Fire Demon',
description: 'Chaotic flame attacks',
skills: [{
name: 'Hellfire Blast',
power: 11,
coins: 1
}, {
name: 'Inferno Storm',
power: 4,
coins: 5
}]
}, {
name: 'Void Wraith',
description: 'Energy-based attacks',
skills: [{
name: 'Void Pulse',
power: 6,
coins: 3
}, {
name: 'Soul Drain',
power: 16,
coins: 1
}]
}, {
name: 'Frost Giant',
description: 'Ice-based powerful attacks',
skills: [{
name: 'Glacial Slam',
power: 9,
coins: 2
}, {
name: 'Blizzard Fury',
power: 3,
coins: 6
}]
}];
// Current enemy (will be randomly selected)
var currentEnemy = null;
var enemySkills = [];
// Current selection
var selectedSkill = null;
// Create class selection UI
var classSelectionTitle = new Text2('Choose Your Class', {
size: 80,
fill: '#000000'
});
classSelectionTitle.anchor.set(0.5, 0.5);
classSelectionTitle.x = 1024;
classSelectionTitle.y = 400;
game.addChild(classSelectionTitle);
var classButtons = [];
for (var i = 0; i < playerClasses.length; i++) {
var classButton = new ClassButton(playerClasses[i].name, playerClasses[i].description, playerClasses[i].skills);
classButton.x = 1024;
classButton.y = 700 + i * 300;
classButtons.push(classButton);
game.addChild(classButton);
}
// Game UI elements (will be created after class selection)
var playerCard = null;
var enemyCard = null;
var skill1Button = null;
var skill2Button = null;
var readyButton = null;
var statusText = null;
var enemySkillText = null;
// Battle result area
var battleResultText = new Text2('', {
size: 60,
fill: '#000000'
});
battleResultText.anchor.set(0.5, 0.5);
battleResultText.x = 1024;
battleResultText.y = 1750;
game.addChild(battleResultText);
// Coin flip display area
var coinFlipContainer = new Container();
coinFlipContainer.x = 1024;
coinFlipContainer.y = 1550;
game.addChild(coinFlipContainer);
// Live damage numbers container
var liveNumbersContainer = new Container();
liveNumbersContainer.x = 1024;
liveNumbersContainer.y = 1800;
game.addChild(liveNumbersContainer);
function showLiveDamageNumber(damage, x, y, isPlayer) {
var damageText = new Text2('-' + damage, {
size: 100,
fill: isPlayer ? '#ff0066' : '#ff6600'
});
damageText.anchor.set(0.5, 0.5);
damageText.x = x;
damageText.y = y;
damageText.alpha = 0;
damageText.scaleX = 0.5;
damageText.scaleY = 0.5;
liveNumbersContainer.addChild(damageText);
// Pop in effect
tween(damageText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Scale back and float up
tween(damageText, {
scaleX: 1.0,
scaleY: 1.0,
y: y - 120,
alpha: 0
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
liveNumbersContainer.removeChild(damageText);
}
});
}
});
}
function startGame() {
// Assign skills based on selected class
playerSkills = selectedClass.skills;
// Adjust health based on class - balanced fighters get more health
if (selectedClass.name === 'Spearman') {
playerMaxHealth += 20; // Balanced fighter gets extra durability
} else if (selectedClass.name === 'Samurai') {
playerMaxHealth -= 10; // High-risk fighter gets less health
} else if (selectedClass.name === 'Berserker') {
playerMaxHealth += 10; // Chaotic fighter gets moderate health boost
}
playerHealth = playerMaxHealth;
// Select random enemy type
var enemyIndex = Math.floor(Math.random() * enemyTypes.length);
currentEnemy = enemyTypes[enemyIndex];
enemySkills = currentEnemy.skills;
// Hide class selection UI
classSelectionTitle.alpha = 0;
for (var i = 0; i < classButtons.length; i++) {
classButtons[i].alpha = 0;
}
// Create game UI elements
playerCard = game.addChild(new PlayerCard());
playerCard.x = 1024;
playerCard.y = 600;
enemyCard = game.addChild(new EnemyCard());
enemyCard.x = 1024;
enemyCard.y = 300;
// Skills selection - positioned below player card with more spacing
skill1Button = game.addChild(new SkillButton(playerSkills[0].name, playerSkills[0].power, playerSkills[0].coins));
skill1Button.x = 1024;
skill1Button.y = 950;
skill2Button = game.addChild(new SkillButton(playerSkills[1].name, playerSkills[1].power, playerSkills[1].coins));
skill2Button.x = 1024;
skill2Button.y = 1130;
// Attack button
readyButton = game.addChild(new AttackButton('BATTLE!'));
readyButton.x = 1024;
readyButton.y = 1350;
readyButton.alpha = 0.5;
// Status text - positioned between player card and skill buttons
statusText = new Text2('Choose your skill', {
size: 70,
fill: '#000000'
});
statusText.anchor.set(0.5, 0.5);
statusText.x = 1024;
statusText.y = 850;
game.addChild(statusText);
// Enemy description text
var enemyDescText = new Text2(currentEnemy.description, {
size: 40,
fill: '#666666'
});
enemyDescText.anchor.set(0.5, 0.5);
enemyDescText.x = 1024;
enemyDescText.y = 120;
game.addChild(enemyDescText);
// Enemy skill display text - positioned higher to avoid blocking
enemySkillText = new Text2('', {
size: 50,
fill: '#000000'
});
enemySkillText.anchor.set(0.5, 0.5);
enemySkillText.x = 1024;
enemySkillText.y = 150;
game.addChild(enemySkillText);
// Initialize health displays
playerCard.updateHealth(playerHealth, playerMaxHealth);
enemyCard.updateHealth(enemyHealth, enemyMaxHealth);
enemyCard.setEnemyName(currentEnemy.name);
// Change game state
gameState = 'selectSkill';
}
// Battle logic
function performBattle() {
if (!selectedSkill) return;
gameState = 'battle';
statusText.setText('Battle in progress...');
readyButton.alpha = 0.5;
// Player's turn
var playerBasePower = selectedSkill.basePower;
var playerCoins = selectedSkill.coinCount;
// Enemy's turn (AI)
var enemySkillIndex = Math.floor(Math.random() * enemySkills.length);
var enemySkill = enemySkills[enemySkillIndex];
var enemyBasePower = enemySkill.power;
var enemyCoins = enemySkill.coins;
var enemyMinPower = Math.max(1, Math.floor(enemyBasePower * 0.3));
var enemyBonusPerSuccess = Math.max(2, Math.floor(enemyBasePower * 0.8));
var enemyMaxPower = enemyMinPower + enemyCoins * enemyBonusPerSuccess;
// Display enemy's planned skill
enemySkillText.setText('Enemy plans: ' + enemySkill.name + ' (Power: ' + enemyMinPower + '-' + enemyMaxPower + ', ' + enemyCoins + ' coins)');
// Clear previous coins
coinFlipContainer.removeChildren();
// Flip player coins
var playerSuccessfulFlips = 0;
var playerCoinsFlipped = 0;
for (var i = 0; i < playerCoins; i++) {
var coin = new FlippingCoin();
coin.x = (i - (playerCoins - 1) / 2) * 100;
coin.y = -50;
coinFlipContainer.addChild(coin);
// Set player coin color
var coinGraphic = coin.getChildAt(0);
coinGraphic.tint = 0x4CAF50; // Green for player coins
// Add P text label for player coins
var playerLabel = new Text2('P', {
size: 40,
fill: '#ffffff'
});
playerLabel.anchor.set(0.5, 0.5);
playerLabel.x = 0;
playerLabel.y = 0;
coin.addChild(playerLabel);
LK.setTimeout(function (coinRef) {
return function () {
coinRef.flip(function (result) {
if (result === 'heads') {
playerSuccessfulFlips++;
}
playerCoinsFlipped++;
if (playerCoinsFlipped === playerCoins) {
// All player coins flipped, now flip enemy coins
flipEnemyCoins();
}
}, true);
};
}(coin), i * 800);
}
function flipEnemyCoins() {
var enemySuccessfulFlips = 0;
var enemyCoinsFlipped = 0;
for (var i = 0; i < enemyCoins; i++) {
var coin = new FlippingCoin();
coin.x = (i - (enemyCoins - 1) / 2) * 100;
coin.y = 50;
coinFlipContainer.addChild(coin);
// Set enemy coin color
var coinGraphic = coin.getChildAt(0);
coinGraphic.tint = 0xF44336; // Red for enemy coins
// Add E text label for enemy coins
var enemyLabel = new Text2('E', {
size: 40,
fill: '#ffffff'
});
enemyLabel.anchor.set(0.5, 0.5);
enemyLabel.x = 0;
enemyLabel.y = 0;
coin.addChild(enemyLabel);
LK.setTimeout(function (coinRef) {
return function () {
coinRef.flip(function (result) {
if (result === 'heads') {
enemySuccessfulFlips++;
}
enemyCoinsFlipped++;
if (enemyCoinsFlipped === enemyCoins) {
// All coins flipped, resolve battle
resolveBattle(playerBasePower, playerSuccessfulFlips, playerCoins, enemyBasePower, enemySuccessfulFlips, enemyCoins, enemySkill.name);
}
}, false);
};
}(coin), i * 800 + 3000);
}
}
}
function resolveBattle(playerBasePower, playerSuccessfulFlips, playerCoins, enemyBasePower, enemySuccessfulFlips, enemyCoins, enemySkillName) {
var playerTotalPower = playerBasePower + playerSuccessfulFlips * 2;
var enemyTotalPower = enemyBasePower + enemySuccessfulFlips * 2;
var resultText = 'CLASH RESULTS:\n';
resultText += 'Player: ' + playerBasePower + ' base + ' + playerSuccessfulFlips + '/' + playerCoins + ' heads = ' + playerTotalPower + '\n';
resultText += 'Enemy: ' + enemyBasePower + ' base + ' + enemySuccessfulFlips + '/' + enemyCoins + ' heads = ' + enemyTotalPower + '\n';
if (playerTotalPower > enemyTotalPower) {
// Player wins - roll attack coins
resultText += 'Player wins! Rolling ' + playerCoins + ' attack coins...\n';
rollAttackCoins(playerCoins, true, function (successfulAttacks) {
var playerMinPower = Math.max(1, Math.floor(playerBasePower * 0.3));
var bonusPerSuccess = Math.max(2, Math.floor(playerBasePower * 0.8));
var totalDamage = playerMinPower + successfulAttacks * bonusPerSuccess;
if (successfulAttacks === playerCoins) {
totalDamage = Math.floor(totalDamage * 1.5); // 50% damage bonus for perfect flips
resultText += 'CRITICAL HIT! Perfect flip grants 50% bonus damage!\n';
}
enemyHealth -= totalDamage;
resultText += 'Enemy takes ' + totalDamage + ' damage (' + successfulAttacks + '/' + playerCoins + ' successful attacks)!';
// Show live damage number
showLiveDamageNumber(totalDamage, 0, -100, false);
LK.getSound('attack').play();
LK.getSound('victory').play();
// Flash enemy red
tween(enemyCard, {
tint: 0xff0000
}, {
duration: 800,
onFinish: function onFinish() {
tween(enemyCard, {
tint: 0xffffff
}, {
duration: 800
});
}
});
battleResultText.setText(resultText);
finalizeBattle();
});
} else if (enemyTotalPower > playerTotalPower) {
// Enemy wins - roll attack coins
resultText += 'Enemy wins with ' + enemySkillName + '! Rolling ' + enemyCoins + ' attack coins...\n';
rollAttackCoins(enemyCoins, false, function (successfulAttacks) {
var enemyMinPower = Math.max(1, Math.floor(enemyBasePower * 0.3));
var bonusPerSuccess = Math.max(2, Math.floor(enemyBasePower * 0.8));
var totalDamage = enemyMinPower + successfulAttacks * bonusPerSuccess;
if (successfulAttacks === enemyCoins) {
totalDamage = Math.floor(totalDamage * 1.5); // 50% damage bonus for perfect flips
resultText += 'ENEMY CRITICAL HIT! Perfect flip grants 50% bonus damage!\n';
}
playerHealth -= totalDamage;
resultText += 'Player takes ' + totalDamage + ' damage (' + successfulAttacks + '/' + enemyCoins + ' successful attacks)!';
// Show live damage number
showLiveDamageNumber(totalDamage, 0, 100, true);
LK.getSound('damage').play();
LK.getSound('defeat').play();
// Flash player red
tween(playerCard, {
tint: 0xff0000
}, {
duration: 800,
onFinish: function onFinish() {
tween(playerCard, {
tint: 0xffffff
}, {
duration: 800
});
}
});
battleResultText.setText(resultText);
finalizeBattle();
});
} else {
// Tie
resultText += 'It\'s a tie! No damage dealt.';
battleResultText.setText(resultText);
finalizeBattle();
}
function rollAttackCoins(coinCount, isPlayer, callback) {
// Clear previous attack coins
var attackCoinsContainer = new Container();
attackCoinsContainer.x = 0;
attackCoinsContainer.y = isPlayer ? 150 : -150;
coinFlipContainer.addChild(attackCoinsContainer);
var successfulAttacks = 0;
var coinsFlipped = 0;
for (var i = 0; i < coinCount; i++) {
var coin = new FlippingCoin();
coin.x = (i - (coinCount - 1) / 2) * 80;
coin.y = 0;
attackCoinsContainer.addChild(coin);
// Set attack coin color based on player/enemy
var coinGraphic = coin.getChildAt(0);
coinGraphic.tint = isPlayer ? 0x2196F3 : 0xFF5722; // Blue for player attack, orange for enemy attack
// Add P/E text label for attack coins
var attackLabel = new Text2(isPlayer ? 'P' : 'E', {
size: 40,
fill: '#ffffff'
});
attackLabel.anchor.set(0.5, 0.5);
attackLabel.x = 0;
attackLabel.y = 0;
coin.addChild(attackLabel);
LK.setTimeout(function (coinRef) {
return function () {
coinRef.flip(function (result) {
if (result === 'heads') {
successfulAttacks++;
}
coinsFlipped++;
if (coinsFlipped === coinCount) {
callback(successfulAttacks);
}
}, isPlayer);
};
}(coin), i * 400);
}
}
function finalizeBattle() {
// Update health displays
playerCard.updateHealth(Math.max(0, playerHealth), playerMaxHealth);
enemyCard.updateHealth(Math.max(0, enemyHealth), enemyMaxHealth);
// Check for game over
LK.setTimeout(function () {
if (playerHealth <= 0) {
LK.showGameOver();
} else if (enemyHealth <= 0) {
LK.showYouWin();
} else {
// Reset for next turn
resetTurn();
}
}, 4000);
}
}
function resetTurn() {
gameState = 'selectSkill';
selectedSkill = null;
skill1Button.setSelected(false);
skill2Button.setSelected(false);
readyButton.alpha = 0.5;
statusText.setText('Choose your skill');
enemySkillText.setText('');
// Clear coin flips
LK.setTimeout(function () {
coinFlipContainer.removeChildren();
}, 2000);
}
// Game update loop
game.update = function () {
// Update status text based on game state
if (gameState === 'selectClass') {
// Class selection phase - no status updates needed
} else if (gameState === 'selectSkill') {
if (statusText) statusText.setText('Choose your skill');
if (enemySkillText) enemySkillText.setText('');
} else if (gameState === 'ready') {
if (statusText) statusText.setText('Ready to battle with ' + selectedSkill.skillName + ' (Power: ' + selectedSkill.minPower + '-' + selectedSkill.maxPower + ', ' + selectedSkill.coinCount + ' coins)!');
// Show enemy's planned skill when player is ready
var enemySkillIndex = Math.floor(Math.random() * enemySkills.length);
var enemySkill = enemySkills[enemySkillIndex];
var enemyMinPower = Math.max(1, Math.floor(enemySkill.power * 0.3));
var enemyBonusPerSuccess = Math.max(2, Math.floor(enemySkill.power * 0.8));
var enemyMaxPower = enemyMinPower + enemySkill.coins * enemyBonusPerSuccess;
if (enemySkillText) enemySkillText.setText('Enemy plans: ' + enemySkill.name + ' (Power: ' + enemyMinPower + '-' + enemyMaxPower + ', ' + enemySkill.coins + ' coins)');
}
};