import { COLORS } from '../../config.js'; // Flavor/ability text for unit flags — data/civilization-rules.json has no // description field on units, so this is authored here. A few flags // (aegis, paradrop, amphibious, fighter) aren't mechanically wired in // CivilizationLogic.js/CivilizationAI.js yet; their text is standard // Civilization-series flavor phrasing, harmless either way. const FLAG_TEXT = { settler: 'Can found new cities and build terrain improvements (irrigation, roads, mines).', coastal: 'Coast-hugging vessel — cannot cross open ocean away from land.', mounted: 'Fast cavalry — takes double damage from anti-mounted defenders.', paradrop: 'Can drop directly onto a target tile within range, bypassing the terrain in between.', ignoreterrain: 'Ignores terrain movement penalties — always costs 1 movement point per tile.', antimounted: 'Doubles its defense strength against mounted attackers.', aegis: 'Advanced systems grant bonus defense against air and missile attacks.', spaceship: 'A spaceship component — built toward the space-race victory instead of combat.', amphibious: 'No attack penalty when assaulting land units directly from a ship.', engineer: 'Works twice as fast on terrain improvements and can perform advanced terraforming.', missile: 'One-shot weapon — consumed whether or not the attack succeeds.', nuke: 'Devastating strike that levels the target; can be intercepted by SDI Defense.', fighter: 'Limited range — must return to a city or carrier to refuel.', submarine: 'Stealthed — invisible to enemies unless one of their units or cities is adjacent.', noncombat: 'Cannot attack or be used to initiate combat.', ignorewalls: 'Attack ignores the defensive bonus from City Walls.', caravan: 'Can be sent to another city to establish a trade route or help rush a Wonder.', }; // Benefit text for building effects — again authored here since the rules // JSON has no description field. Each entry is a function of the building // so magnitude (building.value) can be interpolated where the game engine // actually uses that value (see CivilizationLogic.js); a couple of effects // (sizecap, palace, granary, veterans, airport, sdi) are described by what // the engine actually does rather than by the value field, since that field // isn't consumed literally for them. const EFFECT_TEXT = { roadtrade: (b) => `Boosts trade from roaded tiles by ${Math.round(b.value * 100)}%.`, science: (b) => `+${Math.round(b.value * 100)}% science output from this city.`, airport: () => 'Air units based here can rebase instantly and heal fully each turn.', granary: () => 'Keeps half the food box on hand when the city grows, speeding regrowth.', walls: (b) => `Multiplies this city's defense against land attacks by ${b.value} (unless the attacker ignores walls).`, sdi: () => 'Automatically intercepts incoming nuclear missiles aimed at this city.', farmfood: (b) => `+${Math.round(b.value * 100)}% food from farmland-improved tiles worked by this city.`, defenseair: (b) => `Multiplies this city's defense against air attacks by ${b.value}.`, power: (b) => `+${Math.round(b.value * 100)}% shield output from the city's Factory (requires a Factory).`, corruption: (b) => `Cuts this city's corruption by ${Math.round(b.value * 100)}%.`, gold: (b) => `+${Math.round(b.value * 100)}% gold income from this city's trade.`, veterans: () => 'Units built here start as veterans, with a combat bonus.', sizecap: (b) => (b.id === 'sewersystem' ? 'Removes this city\'s population growth cap entirely.' : 'Raises this city\'s population cap so it can keep growing.'), oceanfood: (b) => `+${b.value} food from every ocean tile this city works.`, shields: (b) => `+${Math.round(b.value * 100)}% shield (production) output from this city.`, oceanshield: (b) => `+${b.value} shield from every ocean tile this city works.`, palace: () => 'Makes this your capital — sharply reduces corruption and stores the seat of government.', defensesea: (b) => `Multiplies this city's defense against naval attacks by ${b.value}.`, }; export function describeUnitTooltip(rules, unit) { const lines = [ { text: `Attack ${unit.attack} · Defense ${unit.defense} · Move ${unit.move}`, color: COLORS.goldHex }, ]; for (const flag of unit.flags ?? []) { const text = FLAG_TEXT[flag]; if (text) lines.push({ text: `• ${text}` }); } return { title: unit.name, lines }; } export function describeBuildingTooltip(rules, building) { const lines = [ { text: `Cost ${building.cost} shields · Upkeep ${building.upkeep}/turn`, color: COLORS.goldHex }, ]; const describe = EFFECT_TEXT[building.effect]; if (describe) lines.push({ text: `• ${describe(building)}` }); return { title: building.name, lines }; } // Hover tooltip for a map unit/stack: which leader controls it plus a // per-unit-type breakdown. `units` are live game-state unit instances (all // sharing `civ`, since only one civ's units normally occupy a tile), not // rule defs — look each one's def up via rules.units[u.type]. export function describeUnitStackTooltip(rules, civ, opponentsData, units) { const opData = opponentsData?.find((o) => o.id === civ.leaderId); const lines = []; if (units.length === 1) { const [u] = units; const def = rules.units[u.type]; lines.push({ text: def.name, color: COLORS.goldHex }); lines.push({ text: `Attack ${def.attack} · Defense ${def.defense} · Move ${def.move}` }); if (u.vet) lines.push({ text: '• Veteran' }); if (u.fortified) lines.push({ text: '• Fortified' }); for (const flag of def.flags ?? []) { const text = FLAG_TEXT[flag]; if (text) lines.push({ text: `• ${text}` }); } } else { const byName = new Map(); for (const u of units) { const def = rules.units[u.type]; const entry = byName.get(def.name) ?? { count: 0, def }; entry.count += 1; byName.set(def.name, entry); } lines.push({ text: `${units.length} units`, color: COLORS.goldHex }); for (const [name, { count, def }] of byName) { lines.push({ text: `${count}× ${name} (A${def.attack} D${def.defense} M${def.move})` }); } } return { title: civ.name, titleColor: civ.color, lines, icon: { texture: 'opponents', frame: opData?.spriteIndex ?? 0, color: civ.color, label: civ.name }, }; }