oistarian / index.html
jurgenpaul82's picture
undefined - Initial Deployment
a0ac73e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tactical Legends - Enhanced</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.pulse-animation {
animation: pulse 2s infinite;
}
.character-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.character-card {
transition: all 0.3s ease;
}
.health-bar {
height: 6px;
border-radius: 3px;
transition: width 0.5s ease;
}
.mana-bar {
height: 4px;
border-radius: 2px;
transition: width 0.5s ease;
}
.battle-log {
max-height: 200px;
overflow-y: auto;
scrollbar-width: thin;
}
.battle-log::-webkit-scrollbar {
width: 6px;
}
.battle-log::-webkit-scrollbar-thumb {
background-color: rgba(255,255,255,0.2);
border-radius: 3px;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltip-text {
visibility: hidden;
width: 200px;
background-color: #1F2937;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
.skill-icon {
transition: all 0.2s ease;
}
.skill-icon:hover {
transform: scale(1.2);
}
.skill-icon:active {
transform: scale(0.95);
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
.shake {
animation: shake 0.5s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="mb-10 text-center">
<h1 class="text-4xl md:text-6xl font-bold bg-gradient-to-r from-blue-400 to-purple-600 bg-clip-text text-transparent mb-2">
TACTICAL LEGENDS
</h1>
<p class="text-lg text-gray-300 max-w-2xl mx-auto">
A strategic battle game where you assemble a team of heroes to defeat powerful enemies
</p>
</header>
<!-- Game Container -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Player Team -->
<div class="bg-gray-800 rounded-xl p-6 shadow-lg border border-gray-700">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-blue-400">Your Team</h2>
<div class="flex space-x-2">
<button id="new-game-btn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition">
<i class="fas fa-plus mr-2"></i>New Game
</button>
<button id="heal-all-btn" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition">
<i class="fas fa-heart mr-2"></i>Heal All
</button>
</div>
</div>
<div id="player-team" class="space-y-4">
<!-- Character cards will be inserted here by JavaScript -->
</div>
</div>
<!-- Battle Area -->
<div class="bg-gray-800 rounded-xl p-6 shadow-lg border border-gray-700">
<h2 class="text-2xl font-bold text-purple-400 mb-6 text-center">Battle Arena</h2>
<div class="flex flex-col items-center justify-center mb-6">
<div id="battle-animation" class="h-40 w-full flex items-center justify-center mb-4">
<div class="text-center">
<i class="fas fa-swords text-4xl text-gray-500"></i>
<p class="text-gray-400 mt-2">Select a character and action to begin</p>
</div>
</div>
<div id="battle-controls" class="grid grid-cols-2 gap-4 w-full max-w-md">
<button id="attack-btn" class="bg-red-600 hover:bg-red-700 text-white px-4 py-3 rounded-lg transition flex items-center justify-center">
<i class="fas fa-fist-raised mr-2"></i> Basic Attack
</button>
<button id="skill1-btn" class="bg-yellow-600 hover:bg-yellow-700 text-white px-4 py-3 rounded-lg transition flex items-center justify-center">
<i class="fas fa-fire mr-2"></i> Fireball
</button>
<button id="skill2-btn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-3 rounded-lg transition flex items-center justify-center">
<i class="fas fa-shield-alt mr-2"></i> Shield
</button>
<button id="skill3-btn" class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-3 rounded-lg transition flex items-center justify-center">
<i class="fas fa-bolt mr-2"></i> Lightning
</button>
</div>
</div>
<div class="bg-gray-900 rounded-lg p-4">
<h3 class="text-lg font-semibold text-gray-300 mb-2">Battle Log</h3>
<div id="battle-log" class="battle-log text-sm space-y-1">
<!-- Battle messages will appear here -->
<div class="text-gray-400 italic">Waiting for battle to begin...</div>
</div>
</div>
</div>
<!-- Enemy Team -->
<div class="bg-gray-800 rounded-xl p-6 shadow-lg border border-gray-700">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-red-400">Enemies</h2>
<div class="text-gray-400">
Wave: <span id="wave-count" class="font-bold text-white">1</span>
</div>
</div>
<div id="enemy-team" class="space-y-4">
<!-- Enemy cards will be inserted here by JavaScript -->
</div>
</div>
</div>
<!-- Game Stats -->
<div class="mt-10 bg-gray-800 rounded-xl p-6 shadow-lg border border-gray-700">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-gray-900 p-4 rounded-lg">
<h3 class="text-lg font-semibold text-blue-400 mb-2">Game Stats</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span class="text-gray-400">Battles Won:</span>
<span id="battles-won" class="font-bold">0</span>
</div>
<div class="flex justify-between">
<span class="text-gray-400">Enemies Defeated:</span>
<span id="enemies-defeated" class="font-bold">0</span>
</div>
<div class="flex justify-between">
<span class="text-gray-400">Total Damage:</span>
<span id="total-damage" class="font-bold">0</span>
</div>
</div>
</div>
<div class="bg-gray-900 p-4 rounded-lg">
<h3 class="text-lg font-semibold text-purple-400 mb-2">Team Status</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span class="text-gray-400">Alive:</span>
<span id="team-alive" class="font-bold text-green-400">0/3</span>
</div>
<div class="flex justify-between">
<span class="text-gray-400">Average Health:</span>
<span id="avg-health" class="font-bold">0%</span>
</div>
<div class="flex justify-between">
<span class="text-gray-400">Average Mana:</span>
<span id="avg-mana" class="font-bold">0%</span>
</div>
</div>
</div>
<div class="bg-gray-900 p-4 rounded-lg">
<h3 class="text-lg font-semibold text-yellow-400 mb-2">Skills Used</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span class="text-gray-400">Basic Attacks:</span>
<span id="basic-attacks" class="font-bold">0</span>
</div>
<div class="flex justify-between">
<span class="text-gray-400">Fireballs:</span>
<span id="fireballs-used" class="font-bold">0</span>
</div>
<div class="flex justify-between">
<span class="text-gray-400">Shields:</span>
<span id="shields-used" class="font-bold">0</span>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Game State
const gameState = {
playerTeam: [],
enemyTeam: [],
selectedCharacter: null,
selectedEnemy: null,
wave: 1,
stats: {
battlesWon: 0,
enemiesDefeated: 0,
totalDamage: 0,
basicAttacks: 0,
fireballsUsed: 0,
shieldsUsed: 0,
lightningsUsed: 0
},
gameActive: false
};
// Character Classes
class Character {
constructor(name, health, mana, attack, defense, speed, skills, image) {
this.name = name;
this.maxHealth = health;
this.health = health;
this.maxMana = mana;
this.mana = mana;
this.attack = attack;
this.defense = defense;
this.speed = speed;
this.skills = skills;
this.image = image;
this.alive = true;
this.shield = 0;
}
takeDamage(damage) {
const actualDamage = Math.max(0, damage - this.shield);
this.health = Math.max(0, this.health - actualDamage);
this.shield = Math.max(0, this.shield - damage);
if (this.health <= 0) {
this.alive = false;
}
return actualDamage;
}
useMana(amount) {
if (this.mana >= amount) {
this.mana -= amount;
return true;
}
return false;
}
heal(amount) {
this.health = Math.min(this.maxHealth, this.health + amount);
}
restoreMana(amount) {
this.mana = Math.min(this.maxMana, this.mana + amount);
}
addShield(amount) {
this.shield += amount;
}
basicAttack(target) {
const damage = Math.max(1, this.attack - Math.floor(target.defense / 2));
const actualDamage = target.takeDamage(damage);
gameState.stats.basicAttacks++;
gameState.stats.totalDamage += actualDamage;
return { damage: actualDamage, critical: false };
}
useSkill(skillIndex, target) {
const skill = this.skills[skillIndex];
if (!skill || !this.useMana(skill.manaCost)) return null;
let result = { damage: 0, effect: null };
switch(skillIndex) {
case 0: // Fireball
const fireDamage = Math.max(1, this.attack * 1.5 - Math.floor(target.defense / 3));
result.damage = target.takeDamage(fireDamage);
gameState.stats.fireballsUsed++;
gameState.stats.totalDamage += result.damage;
break;
case 1: // Shield
this.addShield(skill.effectAmount);
result.effect = `gained ${skill.effectAmount} shield`;
gameState.stats.shieldsUsed++;
break;
case 2: // Lightning
const lightningDamage = Math.max(1, this.attack * 2 - Math.floor(target.defense / 4));
result.damage = target.takeDamage(lightningDamage);
gameState.stats.lightningsUsed++;
gameState.stats.totalDamage += result.damage;
result.effect = "stunned the target";
break;
}
return result;
}
}
// Initialize the game
function initGame() {
gameState.gameActive = true;
gameState.playerTeam = [
new Character("Warrior", 120, 50, 15, 12, 8, [
{ name: "Fireball", manaCost: 15, effectAmount: 0 },
{ name: "Shield", manaCost: 10, effectAmount: 10 },
{ name: "Lightning", manaCost: 25, effectAmount: 0 }
], "warrior.png"),
new Character("Mage", 80, 100, 20, 6, 10, [
{ name: "Fireball", manaCost: 10, effectAmount: 0 },
{ name: "Shield", manaCost: 15, effectAmount: 5 },
{ name: "Lightning", manaCost: 20, effectAmount: 0 }
], "mage.png"),
new Character("Rogue", 90, 70, 18, 8, 15, [
{ name: "Fireball", manaCost: 12, effectAmount: 0 },
{ name: "Shield", manaCost: 8, effectAmount: 8 },
{ name: "Lightning", manaCost: 18, effectAmount: 0 }
], "rogue.png")
];
spawnEnemyWave();
renderTeams();
updateStats();
addToBattleLog("Game started! Select a character and enemy to begin.");
}
// Spawn enemies for current wave
function spawnEnemyWave() {
gameState.enemyTeam = [];
const enemyCount = Math.min(3, 1 + Math.floor(gameState.wave / 2));
for (let i = 0; i < enemyCount; i++) {
const enemyTypes = ["Goblin", "Orc", "Skeleton", "Troll"];
const type = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
let enemy;
switch(type) {
case "Goblin":
enemy = new Character("Goblin", 60 + gameState.wave * 5, 30, 10 + gameState.wave, 6 + gameState.wave, 12, [
{ name: "Bite", manaCost: 5, effectAmount: 0 },
{ name: "Dodge", manaCost: 8, effectAmount: 5 }
], "goblin.png");
break;
case "Orc":
enemy = new Character("Orc", 100 + gameState.wave * 8, 20, 15 + gameState.wave * 2, 10 + gameState.wave, 6, [
{ name: "Smash", manaCost: 10, effectAmount: 0 },
{ name: "Roar", manaCost: 5, effectAmount: 3 }
], "orc.png");
break;
case "Skeleton":
enemy = new Character("Skeleton", 70 + gameState.wave * 6, 40, 12 + gameState.wave, 8 + gameState.wave, 10, [
{ name: "Bone Throw", manaCost: 8, effectAmount: 0 },
{ name: "Dodge", manaCost: 6, effectAmount: 4 }
], "skeleton.png");
break;
case "Troll":
enemy = new Character("Troll", 150 + gameState.wave * 10, 10, 18 + gameState.wave * 2, 5 + gameState.wave, 4, [
{ name: "Regenerate", manaCost: 15, effectAmount: 15 },
{ name: "Club Swing", manaCost: 12, effectAmount: 0 }
], "troll.png");
break;
}
gameState.enemyTeam.push(enemy);
}
}
// Render player and enemy teams
function renderTeams() {
renderPlayerTeam();
renderEnemyTeam();
}
function renderPlayerTeam() {
const container = document.getElementById('player-team');
container.innerHTML = '';
gameState.playerTeam.forEach((char, index) => {
const card = document.createElement('div');
card.className = `character-card bg-gray-700 rounded-lg p-4 cursor-pointer transition ${!char.alive ? 'opacity-50' : ''} ${gameState.selectedCharacter === index ? 'ring-2 ring-blue-500' : ''}`;
card.innerHTML = `
<div class="flex items-center space-x-4">
<div class="w-16 h-16 rounded-full bg-gray-600 flex items-center justify-center overflow-hidden">
<i class="fas fa-user text-3xl text-gray-400"></i>
</div>
<div class="flex-1">
<div class="flex justify-between items-center mb-1">
<h3 class="font-bold ${!char.alive ? 'text-gray-400' : 'text-blue-300'}">${char.name}</h3>
<span class="text-sm ${char.alive ? 'text-green-400' : 'text-red-400'}">${char.alive ? 'ALIVE' : 'DEFEATED'}</span>
</div>
<div class="mb-1">
<div class="flex justify-between text-xs mb-1">
<span>HP: ${char.health}/${char.maxHealth}</span>
<span>${Math.floor((char.health / char.maxHealth) * 100)}%</span>
</div>
<div class="health-bar bg-gray-600 rounded-full">
<div class="health-bar bg-red-500 rounded-full" style="width: ${(char.health / char.maxHealth) * 100}%"></div>
</div>
</div>
<div class="mb-1">
<div class="flex justify-between text-xs mb-1">
<span>MP: ${char.mana}/${char.maxMana}</span>
<span>${Math.floor((char.mana / char.maxMana) * 100)}%</span>
</div>
<div class="mana-bar bg-gray-600 rounded-full">
<div class="mana-bar bg-blue-500 rounded-full" style="width: ${(char.mana / char.maxMana) * 100}%"></div>
</div>
</div>
<div class="flex justify-between text-xs">
<span>ATK: ${char.attack}</span>
<span>DEF: ${char.defense}</span>
<span>SPD: ${char.speed}</span>
</div>
</div>
</div>
${char.shield > 0 ? `<div class="mt-2 text-xs text-yellow-400"><i class="fas fa-shield-alt mr-1"></i> Shield: ${char.shield}</div>` : ''}
`;
card.addEventListener('click', () => selectCharacter(index));
container.appendChild(card);
});
}
function renderEnemyTeam() {
const container = document.getElementById('enemy-team');
container.innerHTML = '';
gameState.enemyTeam.forEach((enemy, index) => {
const card = document.createElement('div');
card.className = `character-card bg-gray-700 rounded-lg p-4 cursor-pointer transition ${!enemy.alive ? 'opacity-50' : ''} ${gameState.selectedEnemy === index ? 'ring-2 ring-red-500' : ''}`;
card.innerHTML = `
<div class="flex items-center space-x-4">
<div class="w-16 h-16 rounded-full bg-gray-600 flex items-center justify-center overflow-hidden">
<i class="fas fa-skull text-3xl text-gray-400"></i>
</div>
<div class="flex-1">
<div class="flex justify-between items-center mb-1">
<h3 class="font-bold ${!enemy.alive ? 'text-gray-400' : 'text-red-300'}">${enemy.name}</h3>
<span class="text-sm ${enemy.alive ? 'text-red-400' : 'text-green-400'}">${enemy.alive ? 'THREAT' : 'DEFEATED'}</span>
</div>
<div class="mb-1">
<div class="flex justify-between text-xs mb-1">
<span>HP: ${enemy.health}/${enemy.maxHealth}</span>
<span>${Math.floor((enemy.health / enemy.maxHealth) * 100)}%</span>
</div>
<div class="health-bar bg-gray-600 rounded-full">
<div class="health-bar bg-red-500 rounded-full" style="width: ${(enemy.health / enemy.maxHealth) * 100}%"></div>
</div>
</div>
<div class="mb-1">
<div class="flex justify-between text-xs mb-1">
<span>MP: ${enemy.mana}/${enemy.maxMana}</span>
<span>${Math.floor((enemy.mana / enemy.maxMana) * 100)}%</span>
</div>
<div class="mana-bar bg-gray-600 rounded-full">
<div class="mana-bar bg-blue-500 rounded-full" style="width: ${(enemy.mana / enemy.maxMana) * 100}%"></div>
</div>
</div>
<div class="flex justify-between text-xs">
<span>ATK: ${enemy.attack}</span>
<span>DEF: ${enemy.defense}</span>
<span>SPD: ${enemy.speed}</span>
</div>
</div>
</div>
${enemy.shield > 0 ? `<div class="mt-2 text-xs text-yellow-400"><i class="fas fa-shield-alt mr-1"></i> Shield: ${enemy.shield}</div>` : ''}
`;
card.addEventListener('click', () => selectEnemy(index));
container.appendChild(card);
});
}
// Select characters
function selectCharacter(index) {
const char = gameState.playerTeam[index];
if (!char.alive) return;
gameState.selectedCharacter = index;
renderTeams();
// Enable skill buttons based on mana
updateSkillButtons();
addToBattleLog(`Selected ${char.name} as your active character.`);
}
function selectEnemy(index) {
const enemy = gameState.enemyTeam[index];
if (!enemy.alive) return;
gameState.selectedEnemy = index;
renderTeams();
addToBattleLog(`Targeted ${enemy.name} for your next action.`);
}
// Update skill buttons based on selected character's mana
function updateSkillButtons() {
if (gameState.selectedCharacter === null) return;
const char = gameState.playerTeam[gameState.selectedCharacter];
document.getElementById('attack-btn').disabled = false;
document.getElementById('skill1-btn').disabled = char.mana < char.skills[0].manaCost;
document.getElementById('skill2-btn').disabled = char.mana < char.skills[1].manaCost;
document.getElementById('skill3-btn').disabled = char.mana < char.skills[2].manaCost;
// Update tooltips
document.getElementById('skill1-btn').title = `${char.skills[0].name} (${char.skills[0].manaCost} MP)`;
document.getElementById('skill2-btn').title = `${char.skills[1].name} (${char.skills[1].manaCost} MP)`;
document.getElementById('skill3-btn').title = `${char.skills[2].name} (${char.skills[2].manaCost} MP)`;
}
// Battle actions
function playerAttack() {
if (gameState.selectedCharacter === null || gameState.selectedEnemy === null) {
addToBattleLog("Please select both a character and an enemy first!", "red-400");
return;
}
const char = gameState.playerTeam[gameState.selectedCharacter];
const enemy = gameState.enemyTeam[gameState.selectedEnemy];
if (!char.alive || !enemy.alive) {
addToBattleLog("Cannot attack with a defeated character or target a defeated enemy!", "red-400");
return;
}
const result = char.basicAttack(enemy);
animateBattle(char, enemy, result.damage, "basic");
addToBattleLog(`${char.name} attacked ${enemy.name} for ${result.damage} damage!`, "blue-400");
processEnemyTurn();
}
function playerUseSkill(skillIndex) {
if (gameState.selectedCharacter === null || gameState.selectedEnemy === null) {
addToBattleLog("Please select both a character and an enemy first!", "red-400");
return;
}
const char = gameState.playerTeam[gameState.selectedCharacter];
const enemy = gameState.enemyTeam[gameState.selectedEnemy];
if (!char.alive || !enemy.alive) {
addToBattleLog("Cannot use skills with a defeated character or target a defeated enemy!", "red-400");
return;
}
const result = char.useSkill(skillIndex, enemy);
if (!result) {
addToBattleLog(`${char.name} doesn't have enough mana for ${char.skills[skillIndex].name}!`, "red-400");
return;
}
animateBattle(char, enemy, result.damage, "skill");
let message = `${char.name} used ${char.skills[skillIndex].name} on ${enemy.name}`;
if (result.damage > 0) message += ` for ${result.damage} damage!`;
if (result.effect) message += ` and ${result.effect}!`;
addToBattleLog(message, "purple-400");
processEnemyTurn();
}
function processEnemyTurn() {
// Check if all enemies are defeated
if (gameState.enemyTeam.every(e => !e.alive)) {
endWave();
return;
}
// Check if all players are defeated
if (gameState.playerTeam.every(p => !p.alive)) {
gameOver();
return;
}
// Enemy AI: Simple random actions
setTimeout(() => {
gameState.enemyTeam.forEach((enemy, enemyIndex) => {
if (!enemy.alive) return;
// Find a random alive player character
const alivePlayers = gameState.playerTeam
.map((char, idx) => ({ char, idx }))
.filter(c => c.char.alive);
if (alivePlayers.length === 0) return;
const target = alivePlayers[Math.floor(Math.random() * alivePlayers.length)];
const playerChar = target.char;
const playerIndex = target.idx;
// Randomly choose between basic attack and skills (if has mana)
const possibleActions = ['attack'];
enemy.skills.forEach((skill, idx) => {
if (enemy.mana >= skill.manaCost) {
possibleActions.push(`skill${idx}`);
}
});
const action = possibleActions[Math.floor(Math.random() * possibleActions.length)];
let result, message;
switch(action) {
case 'attack':
result = enemy.basicAttack(playerChar);
message = `${enemy.name} attacked ${playerChar.name} for ${result.damage} damage!`;
break;
case 'skill0':
result = enemy.useSkill(0, playerChar);
message = `${enemy.name} used ${enemy.skills[0].name} on ${playerChar.name} for ${result.damage} damage!`;
break;
case 'skill1':
result = enemy.useSkill(1, playerChar);
message = `${enemy.name} used ${enemy.skills[1].name} and ${result.effect}!`;
break;
}
animateBattle(enemy, playerChar, result?.damage || 0, "enemy");
addToBattleLog(message, "red-400");
// Update UI after enemy turn
renderTeams();
updateStats();
// Check again if all players are defeated after enemy turn
if (gameState.playerTeam.every(p => !p.alive)) {
gameOver();
}
});
}, 1000);
}
// Battle animations
function animateBattle(attacker, defender, damage, type) {
const animationContainer = document.getElementById('battle-animation');
animationContainer.innerHTML = '';
const attackerImg = document.createElement('div');
attackerImg.className = 'text-5xl mx-4';
attackerImg.innerHTML = type === 'enemy' ? '<i class="fas fa-skull"></i>' : '<i class="fas fa-user"></i>';
const defenderImg = document.createElement('div');
defenderImg.className = 'text-5xl mx-4 shake';
defenderImg.innerHTML = type !== 'enemy' ? '<i class="fas fa-skull"></i>' : '<i class="fas fa-user"></i>';
const effect = document.createElement('div');
effect.className = 'text-4xl mx-4 fade-in';
switch(type) {
case 'basic':
effect.innerHTML = '<i class="fas fa-fist-raised text-red-500"></i>';
break;
case 'skill':
effect.innerHTML = '<i class="fas fa-fire text-yellow-500"></i>';
break;
case 'enemy':
effect.innerHTML = '<i class="fas fa-bolt text-purple-500"></i>';
break;
}
animationContainer.appendChild(attackerImg);
animationContainer.appendChild(effect);
animationContainer.appendChild(defenderImg);
if (damage > 0) {
setTimeout(() => {
const damageText = document.createElement('div');
damageText.className = 'absolute text-xl font-bold text-red-500 fade-in';
damageText.textContent = `-${damage}`;
defenderImg.appendChild(damageText);
setTimeout(() => {
damageText.remove();
}, 1000);
}, 300);
}
// Reset animation after delay
setTimeout(() => {
animationContainer.innerHTML = '<div class="text-center"><i class="fas fa-swords text-4xl text-gray-500"></i><p class="text-gray-400 mt-2">Select a character and action to continue</p></div>';
}, 1500);
}
// End of wave
function endWave() {
gameState.stats.battlesWon++;
gameState.wave++;
document.getElementById('wave-count').textContent = gameState.wave;
// Heal team a bit between waves
gameState.playerTeam.forEach(char => {
if (char.alive) {
char.heal(Math.floor(char.maxHealth * 0.3));
char.restoreMana(Math.floor(char.maxMana * 0.2));
}
});
spawnEnemyWave();
renderTeams();
updateStats();
addToBattleLog(`Wave ${gameState.wave - 1} cleared! Starting wave ${gameState.wave}.`, "green-400");
}
// Game over
function gameOver() {
gameState.gameActive = false;
addToBattleLog("GAME OVER! Your team has been defeated.", "red-400");
document.getElementById('attack-btn').disabled = true;
document.getElementById('skill1-btn').disabled = true;
document.getElementById('skill2-btn').disabled = true;
document.getElementById('skill3-btn').disabled = true;
}
// Heal all characters
function healAll() {
if (!gameState.gameActive) {
addToBattleLog("Start a new game first!", "red-400");
return;
}
gameState.playerTeam.forEach(char => {
if (char.alive) {
char.heal(char.maxHealth);
char.restoreMana(char.maxMana);
}
});
renderTeams();
updateStats();
addToBattleLog("All alive characters have been fully healed!", "green-400");
}
// Update game stats display
function updateStats() {
document.getElementById('battles-won').textContent = gameState.stats.battlesWon;
document.getElementById('enemies-defeated').textContent = gameState.stats.enemiesDefeated;
document.getElementById('total-damage').textContent = gameState.stats.totalDamage;
const aliveCount = gameState.playerTeam.filter(c => c.alive).length;
document.getElementById('team-alive').textContent = `${aliveCount}/${gameState.playerTeam.length}`;
// Calculate average health and mana
let totalHealth = 0, totalMana = 0;
let aliveChars = 0;
gameState.playerTeam.forEach(char => {
if (char.alive) {
totalHealth += (char.health / char.maxHealth) * 100;
totalMana += (char.mana / char.maxMana) * 100;
aliveChars++;
}
});
const avgHealth = aliveChars > 0 ? Math.floor(totalHealth / aliveChars) : 0;
const avgMana = aliveChars > 0 ? Math.floor(totalMana / aliveChars) : 0;
document.getElementById('avg-health').textContent = `${avgHealth}%`;
document.getElementById('avg-mana').textContent = `${avgMana}%`;
// Skill usage
document.getElementById('basic-attacks').textContent = gameState.stats.basicAttacks;
document.getElementById('fireballs-used').textContent = gameState.stats.fireballsUsed;
document.getElementById('shields-used').textContent = gameState.stats.shieldsUsed;
}
// Battle log
function addToBattleLog(message, color = "gray-400") {
const log = document.getElementById('battle-log');
const entry = document.createElement('div');
entry.className = `fade-in text-${color}`;
entry.textContent = message;
log.insertBefore(entry, log.firstChild);
// Limit log entries
if (log.children.length > 20) {
log.removeChild(log.lastChild);
}
}
// Event listeners
document.getElementById('new-game-btn').addEventListener('click', initGame);
document.getElementById('heal-all-btn').addEventListener('click', healAll);
document.getElementById('attack-btn').addEventListener('click', playerAttack);
document.getElementById('skill1-btn').addEventListener('click', () => playerUseSkill(0));
document.getElementById('skill2-btn').addEventListener('click', () => playerUseSkill(1));
document.getElementById('skill3-btn').addEventListener('click', () => playerUseSkill(2));
// Initialize UI
document.getElementById('attack-btn').disabled = true;
document.getElementById('skill1-btn').disabled = true;
document.getElementById('skill2-btn').disabled = true;
document.getElementById('skill3-btn').disabled = true;
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=jurgenpaul82/oistarian" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>