import React, { useState, useEffect, useRef } from 'react'; import { Castle, Wheat, Trees, Hammer, Coins, Shield, Sword, Crown, Users, Map, Zap, Star, X, ChevronUp, Flame, Skull, Home } from 'lucide-react'; const TotalBattlePrototype = () => { // ============ GAME STATE ============ const [resources, setResources] = useState({ food: 500, wood: 500, stone: 200, gold: 100, }); const [power, setPower] = useState(0); const [view, setView] = useState('castle'); // castle, world, troops, hero const [selectedBuilding, setSelectedBuilding] = useState(null); const [selectedMonster, setSelectedMonster] = useState(null); const [notification, setNotification] = useState(null); const [marches, setMarches] = useState([]); // active army marches const [battleResult, setBattleResult] = useState(null); // Buildings โ€” each has a level. Production buildings generate resources/hour. const [buildings, setBuildings] = useState({ castle: { level: 1, name: 'Castle', icon: 'castle', upgrading: null }, farm: { level: 1, name: 'Farm', icon: 'wheat', upgrading: null }, lumbermill: { level: 1, name: 'Lumber Mill', icon: 'trees', upgrading: null }, quarry: { level: 1, name: 'Quarry', icon: 'hammer', upgrading: null }, treasury: { level: 1, name: 'Treasury', icon: 'coins', upgrading: null }, barracks: { level: 1, name: 'Barracks', icon: 'sword', upgrading: null }, wall: { level: 1, name: 'Wall', icon: 'shield', upgrading: null }, academy: { level: 0, name: 'Academy', icon: 'star', upgrading: null }, }); // Troops in your barracks const [troops, setTroops] = useState({ swordsmen: 10, archers: 5, cavalry: 0, }); const [training, setTraining] = useState(null); // {type, count, finishesAt} // Hero const [hero, setHero] = useState({ name: 'Aydae', level: 1, xp: 0, attack: 10, defense: 8, }); // World map monsters โ€” randomly generated const [monsters, setMonsters] = useState(() => generateMonsters()); function generateMonsters() { const types = [ { name: 'Goblin Camp', level: 1, hp: 50, attack: 8, defense: 5, reward: { food: 100, wood: 50, gold: 20, xp: 20 } }, { name: 'Bandit Hideout', level: 2, hp: 120, attack: 15, defense: 10, reward: { food: 200, wood: 100, stone: 50, gold: 50, xp: 50 } }, { name: 'Orc Warband', level: 3, hp: 250, attack: 25, defense: 18, reward: { food: 400, wood: 200, stone: 100, gold: 100, xp: 100 } }, { name: 'Troll Lair', level: 4, hp: 500, attack: 40, defense: 30, reward: { food: 800, wood: 400, stone: 250, gold: 200, xp: 200 } }, { name: 'Dragon Roost', level: 5, hp: 1000, attack: 70, defense: 50, reward: { food: 1500, wood: 800, stone: 500, gold: 500, xp: 500 } }, ]; const out = []; for (let i = 0; i < 12; i++) { const t = types[Math.floor(Math.random() * types.length)]; out.push({ id: i, ...t, currentHp: t.hp, x: 10 + Math.random() * 80, y: 10 + Math.random() * 80, }); } return out; } // ============ TROOP DEFINITIONS ============ const troopTypes = { swordsmen: { name: 'Swordsmen', cost: { food: 20, gold: 5 }, time: 5, attack: 5, defense: 4, hp: 10, icon: 'โš”๏ธ' }, archers: { name: 'Archers', cost: { food: 15, wood: 15, gold: 10 }, time: 8, attack: 8, defense: 2, hp: 7, icon: '๐Ÿน' }, cavalry: { name: 'Cavalry', cost: { food: 40, wood: 10, gold: 25 }, time: 15, attack: 12, defense: 8, hp: 20, icon: '๐ŸŽ' }, }; // ============ BUILDING DEFINITIONS ============ function buildingCost(key, level) { const targetLevel = level + 1; const base = { castle: { wood: 200, stone: 150, gold: 50 }, farm: { wood: 60, stone: 20 }, lumbermill: { wood: 30, stone: 50 }, quarry: { wood: 80, stone: 30 }, treasury: { wood: 100, stone: 100, gold: 20 }, barracks: { wood: 120, stone: 80, gold: 30 }, wall: { wood: 50, stone: 200 }, academy: { wood: 150, stone: 150, gold: 100 }, }[key]; const cost = {}; Object.entries(base).forEach(([k, v]) => { cost[k] = Math.floor(v * Math.pow(1.5, targetLevel - 1)); }); return cost; } function buildingTime(level) { return 5 + level * 5; // seconds for prototype speed } function buildingProduction(key, level) { if (level === 0) return 0; const base = { farm: 60, lumbermill: 50, quarry: 30, treasury: 20 }; return Math.floor((base[key] || 0) * Math.pow(1.3, level - 1)); } function getResourceType(buildingKey) { return { farm: 'food', lumbermill: 'wood', quarry: 'stone', treasury: 'gold' }[buildingKey]; } // ============ POWER CALCULATION ============ useEffect(() => { let p = 0; Object.values(buildings).forEach(b => { p += b.level * 100; }); Object.entries(troops).forEach(([k, v]) => { const t = troopTypes[k]; p += v * (t.attack + t.defense + t.hp); }); p += hero.level * 50 + hero.attack * 5 + hero.defense * 5; setPower(p); }, [buildings, troops, hero]); // ============ RESOURCE GENERATION TICK ============ useEffect(() => { const interval = setInterval(() => { setResources(prev => { const next = { ...prev }; Object.entries(buildings).forEach(([key, b]) => { const r = getResourceType(key); if (r) { const perSecond = buildingProduction(key, b.level) / 60; next[r] = Math.floor(next[r] + perSecond); } }); return next; }); }, 1000); return () => clearInterval(interval); }, [buildings]); // ============ BUILDING UPGRADE TICK ============ useEffect(() => { const interval = setInterval(() => { setBuildings(prev => { let changed = false; const next = { ...prev }; Object.entries(prev).forEach(([key, b]) => { if (b.upgrading && Date.now() >= b.upgrading) { next[key] = { ...b, level: b.level + 1, upgrading: null }; changed = true; showNotification(`${b.name} upgraded to level ${b.level + 1}!`, 'success'); } }); return changed ? next : prev; }); }, 500); return () => clearInterval(interval); }, []); // ============ TRAINING TICK ============ useEffect(() => { if (!training) return; const interval = setInterval(() => { if (Date.now() >= training.finishesAt) { setTroops(prev => ({ ...prev, [training.type]: prev[training.type] + training.count })); showNotification(`${training.count} ${troopTypes[training.type].name} trained!`, 'success'); setTraining(null); } }, 500); return () => clearInterval(interval); }, [training]); // ============ MARCH/BATTLE TICK ============ useEffect(() => { const interval = setInterval(() => { setMarches(prev => { const remaining = []; const now = Date.now(); prev.forEach(m => { if (now >= m.returnAt) { // Battle complete applyBattleResult(m); } else { remaining.push(m); } }); return remaining; }); }, 500); return () => clearInterval(interval); }, []); function applyBattleResult(march) { const monster = monsters.find(m => m.id === march.monsterId); if (!monster) return; // Calculate army stats let totalAttack = 0, totalDefense = 0, totalHp = 0; Object.entries(march.army).forEach(([k, v]) => { const t = troopTypes[k]; totalAttack += v * t.attack; totalDefense += v * t.defense; totalHp += v * t.hp; }); totalAttack += hero.attack * 10; totalDefense += hero.defense * 10; // Simple battle: if your attack vs monster defense const yourPower = totalAttack + totalHp / 2; const monsterPower = monster.attack * 5 + monster.hp + monster.defense * 3; const won = yourPower > monsterPower; // Losses scale based on how close the battle was const ratio = monsterPower / yourPower; const lossRate = won ? Math.min(0.4, ratio * 0.3) : Math.min(0.95, ratio * 0.7); const survivingArmy = {}; let lostCount = 0; Object.entries(march.army).forEach(([k, v]) => { const survived = Math.floor(v * (1 - lossRate)); survivingArmy[k] = survived; lostCount += v - survived; }); setTroops(prev => { const next = { ...prev }; Object.entries(survivingArmy).forEach(([k, v]) => { next[k] += v; }); return next; }); if (won) { setResources(prev => ({ food: prev.food + monster.reward.food, wood: prev.wood + monster.reward.wood, stone: prev.stone + (monster.reward.stone || 0), gold: prev.gold + monster.reward.gold, })); // Hero XP setHero(prev => { let newXp = prev.xp + monster.reward.xp; let newLevel = prev.level; let newAtk = prev.attack; let newDef = prev.defense; const xpNeeded = newLevel * 100; if (newXp >= xpNeeded) { newXp -= xpNeeded; newLevel++; newAtk += 3; newDef += 2; showNotification(`${prev.name} reached level ${newLevel}!`, 'levelup'); } return { ...prev, xp: newXp, level: newLevel, attack: newAtk, defense: newDef }; }); // Remove monster, spawn a new one setMonsters(prev => { const filtered = prev.filter(m => m.id !== monster.id); const types = [ { name: 'Goblin Camp', level: 1, hp: 50, attack: 8, defense: 5, reward: { food: 100, wood: 50, gold: 20, xp: 20 } }, { name: 'Bandit Hideout', level: 2, hp: 120, attack: 15, defense: 10, reward: { food: 200, wood: 100, stone: 50, gold: 50, xp: 50 } }, { name: 'Orc Warband', level: 3, hp: 250, attack: 25, defense: 18, reward: { food: 400, wood: 200, stone: 100, gold: 100, xp: 100 } }, { name: 'Troll Lair', level: 4, hp: 500, attack: 40, defense: 30, reward: { food: 800, wood: 400, stone: 250, gold: 200, xp: 200 } }, { name: 'Dragon Roost', level: 5, hp: 1000, attack: 70, defense: 50, reward: { food: 1500, wood: 800, stone: 500, gold: 500, xp: 500 } }, ]; const t = types[Math.floor(Math.random() * types.length)]; return [...filtered, { id: Date.now(), ...t, currentHp: t.hp, x: 10 + Math.random() * 80, y: 10 + Math.random() * 80, }]; }); } setBattleResult({ won, monsterName: monster.name, reward: won ? monster.reward : null, lostCount, survivingArmy, }); } // ============ ACTIONS ============ function showNotification(text, type = 'info') { setNotification({ text, type }); setTimeout(() => setNotification(null), 3000); } function canAfford(cost) { return Object.entries(cost).every(([k, v]) => resources[k] >= v); } function payCost(cost) { setResources(prev => { const next = { ...prev }; Object.entries(cost).forEach(([k, v]) => { next[k] -= v; }); return next; }); } function upgradeBuilding(key) { const b = buildings[key]; if (b.upgrading) return; if (key !== 'castle' && b.level >= buildings.castle.level) { showNotification('Upgrade your Castle first!', 'error'); return; } const cost = buildingCost(key, b.level); if (!canAfford(cost)) { showNotification('Not enough resources!', 'error'); return; } payCost(cost); setBuildings(prev => ({ ...prev, [key]: { ...prev[key], upgrading: Date.now() + buildingTime(b.level) * 1000 }, })); showNotification(`Started upgrading ${b.name}...`, 'info'); } function trainTroops(type, count) { if (training) { showNotification('Already training troops!', 'error'); return; } const t = troopTypes[type]; const totalCost = {}; Object.entries(t.cost).forEach(([k, v]) => { totalCost[k] = v * count; }); if (!canAfford(totalCost)) { showNotification('Not enough resources!', 'error'); return; } payCost(totalCost); setTraining({ type, count, finishesAt: Date.now() + t.time * count * 1000 }); showNotification(`Training ${count} ${t.name}...`, 'info'); } function attackMonster(monster, armyComposition) { // Check totals const total = Object.values(armyComposition).reduce((a, b) => a + b, 0); if (total === 0) { showNotification('Send at least one troop!', 'error'); return; } // Verify we have the troops for (const [k, v] of Object.entries(armyComposition)) { if (troops[k] < v) { showNotification('Not enough troops!', 'error'); return; } } // Deduct troops (they're now marching) setTroops(prev => { const next = { ...prev }; Object.entries(armyComposition).forEach(([k, v]) => { next[k] -= v; }); return next; }); // Travel time based on monster level const travelTime = (5 + monster.level * 3) * 1000; setMarches(prev => [...prev, { id: Date.now(), monsterId: monster.id, monsterName: monster.name, army: armyComposition, returnAt: Date.now() + travelTime, totalTime: travelTime, startedAt: Date.now(), }]); setSelectedMonster(null); showNotification(`Army marching to ${monster.name}...`, 'info'); } // ============ UI HELPERS ============ function ResourceBar() { return (
{resources.food}
{resources.wood}
{resources.stone}
{resources.gold}
{power.toLocaleString()}
); } function getBuildingIcon(iconName, size = 28) { const map = { castle: , wheat: , trees: , hammer: , coins: , sword: , shield: , star: , }; return map[iconName] || ; } // ============ VIEW: CASTLE ============ function CastleView() { const buildingOrder = ['castle', 'farm', 'lumbermill', 'quarry', 'treasury', 'barracks', 'wall', 'academy']; return (
{/* Sky / Clouds */}
{/* Ground */}
{/* Buildings grid */}
{buildingOrder.map(key => { const b = buildings[key]; const isUpgrading = b.upgrading !== null; const timeLeft = isUpgrading ? Math.max(0, b.upgrading - Date.now()) / 1000 : 0; const isLocked = key === 'academy' && b.level === 0 && buildings.castle.level < 3; return ( ); })}
); } // ============ VIEW: WORLD MAP ============ function WorldView() { return (
{/* Map texture */}
{/* Your castle in the center */}
setView('castle')} >
Your Castle
{/* Monsters */} {monsters.map(m => ( ))} {/* Active marches */} {marches.map(m => { const monster = monsters.find(mo => mo.id === m.monsterId); if (!monster) return null; const progress = (Date.now() - m.startedAt) / m.totalTime; const half = progress < 0.5; const t = half ? progress * 2 : (1 - progress) * 2; const cx = 50 + (monster.x - 50) * t; const cy = 50 + (monster.y - 50) * t; return (
); })}
); } // ============ MODALS ============ function BuildingModal() { if (!selectedBuilding) return null; const b = buildings[selectedBuilding]; const cost = buildingCost(selectedBuilding, b.level); const time = buildingTime(b.level); const resourceType = getResourceType(selectedBuilding); const currentProd = buildingProduction(selectedBuilding, b.level); const nextProd = buildingProduction(selectedBuilding, b.level + 1); const canUpgrade = !b.upgrading && canAfford(cost) && (selectedBuilding === 'castle' || b.level < buildings.castle.level); return (
setSelectedBuilding(null)}>
e.stopPropagation()}>

{b.name}

Level {b.level}

{resourceType && (
Produces: {currentProd}/min {resourceType}
Next level: {nextProd}/min
)} {selectedBuilding === 'castle' && (
Increases the max level of all other buildings.
)} {selectedBuilding === 'barracks' && (
Higher level allows training more powerful troops faster.
)} {selectedBuilding === 'wall' && (
Defends your castle from attacks.
)} {selectedBuilding === 'academy' && (
Research technologies to boost your army and economy.
)}
Upgrade to Lvl {b.level + 1}
{Object.entries(cost).map(([k, v]) => (
= v ? 'text-green-300' : 'text-red-300'}`}> {k}: {v}
))}
Time: {time}s
); } function MonsterModal() { const [army, setArmy] = useState({ swordsmen: 0, archers: 0, cavalry: 0 }); if (!selectedMonster) return null; const m = selectedMonster; function adjust(k, delta) { setArmy(prev => ({ ...prev, [k]: Math.max(0, Math.min(troops[k], prev[k] + delta)) })); } function allIn() { setArmy({ swordsmen: troops.swordsmen, archers: troops.archers, cavalry: troops.cavalry }); } // Estimate let attack = 0, defense = 0, hp = 0; Object.entries(army).forEach(([k, v]) => { const t = troopTypes[k]; attack += v * t.attack; defense += v * t.defense; hp += v * t.hp; }); attack += hero.attack * 10; defense += hero.defense * 10; const yourPower = attack + hp / 2; const monsterPower = m.attack * 5 + m.hp + m.defense * 3; const winChance = yourPower > monsterPower ? Math.min(99, 50 + (yourPower / monsterPower - 1) * 50) : Math.max(1, 50 - (1 - yourPower / monsterPower) * 50); return (
setSelectedMonster(null)}>
e.stopPropagation()}>

{m.name}

Level {m.level}

HP: {m.hp}
ATK: {m.attack}
DEF: {m.defense}
Rewards:
๐ŸŒพ {m.reward.food}
๐ŸŒฒ {m.reward.wood}
โ›๏ธ {m.reward.stone || 0}
๐Ÿ’ฐ {m.reward.gold}
Send army:
{Object.entries(troopTypes).map(([k, t]) => (
{t.icon} {t.name} (have {troops[k]})
{army[k]}
))}
Your power: {Math.floor(yourPower)}
Enemy power: {Math.floor(monsterPower)}
50 ? 'text-green-400 font-bold' : 'text-red-400 font-bold'}> Win chance: ~{Math.floor(winChance)}%
); } function BattleResultModal() { if (!battleResult) return null; const r = battleResult; return (
setBattleResult(null)}>
e.stopPropagation()}>

{r.won ? 'โš”๏ธ Victory!' : '๐Ÿ’€ Defeat!'}

vs {r.monsterName}

{r.won && r.reward && (
Loot:
๐ŸŒพ +{r.reward.food}
๐ŸŒฒ +{r.reward.wood}
โ›๏ธ +{r.reward.stone || 0}
๐Ÿ’ฐ +{r.reward.gold}
โญ Hero +{r.reward.xp} XP
)}
Casualties: {r.lostCount}
Returning: โš”๏ธ{r.survivingArmy.swordsmen || 0} ๐Ÿน{r.survivingArmy.archers || 0} ๐ŸŽ{r.survivingArmy.cavalry || 0}
); } // ============ TROOP/HERO PANELS ============ function TroopsPanel() { const [counts, setCounts] = useState({ swordsmen: 5, archers: 5, cavalry: 1 }); return (

Barracks (Lvl {buildings.barracks.level})

{training && (
Training {training.count} {troopTypes[training.type].name}... ({Math.ceil((training.finishesAt - Date.now()) / 1000)}s)
)}
Current army:
{Object.entries(troopTypes).map(([k, t]) => (
{t.icon}
{troops[k]}
{t.name}
))}
Train new troops:
{Object.entries(troopTypes).map(([k, t]) => { const total = {}; Object.entries(t.cost).forEach(([rk, rv]) => { total[rk] = rv * counts[k]; }); return (
{t.icon} {t.name}
{counts[k]}
ATK {t.attack} ยท DEF {t.defense} ยท HP {t.hp} {t.time}s each
{Object.entries(total).map(([rk, rv]) => ( = rv ? 'text-green-700' : 'text-red-700'}> {rk}: {rv} ))}
); })}
); } function HeroPanel() { const xpNeeded = hero.level * 100; return (

Hero

๐Ÿ›ก๏ธ
{hero.name}
Level {hero.level}
{hero.xp} / {xpNeeded} XP
Attack
{hero.attack}
Defense
{hero.defense}
Your hero joins every march and adds power to your army. Defeat monsters to gain XP.
); } // ============ MAIN RENDER ============ return (
{/* Top bar */}
Kingdom of Aetheria
{/* Main game area */}
{view === 'castle' && } {view === 'world' && } {view === 'troops' && ( <> )} {view === 'hero' && ( <> )} {/* Active marches indicator */} {marches.length > 0 && view !== 'world' && (
Marching ({marches.length})
{marches.map(m => { const timeLeft = Math.max(0, (m.returnAt - Date.now()) / 1000); return
โ†’ {m.monsterName} ({Math.ceil(timeLeft)}s)
; })}
)} {/* Notification */} {notification && (
{notification.text}
)}
{/* Bottom nav */}
); }; export default TotalBattlePrototype;