Files
2026-02-08 17:06:59 +01:00

293 lines
8.4 KiB
TypeScript

"use client";
import { useState, useEffect, useCallback, useMemo } from "react";
import { Planet, Starship } from "@/types/swapi";
import { fetchGameData } from "@/services/swapi";
import { LogEntry } from "@/components/EventLog";
import { t } from "@/content/locales";
export function useGameEngine() {
const [allPlanets, setAllPlanets] = useState<Planet[]>([]);
const [planets, setPlanets] = useState<Planet[]>([]);
const [starships, setStarships] = useState<Starship[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [gameStarted, setGameStarted] = useState(false);
const [isGameOver, setIsGameOver] = useState(false);
const [isVictory, setIsVictory] = useState(false);
const [credits, setCredits] = useState<number>(100);
const [hp, setHp] = useState<number>(10);
const [currentLocationId, setCurrentLocationId] = useState<string>("");
const [selectedPlanet, setSelectedPlanet] = useState<Planet | null>(null);
const [isProcessing, setIsProcessing] = useState<boolean>(false);
const [isRerouting, setIsRerouting] = useState<boolean>(false);
const [activeEnemy, setActiveEnemy] = useState<Starship | null>(null);
const [logs, setLogs] = useState<LogEntry[]>([]);
const [power, setPower] = useState({
engines: 3,
lasers: 3,
shields: 3,
});
const MAX_POWER = 9;
const WIN_THRESHOLD = 100000;
const selectRandomSector = useCallback(
(all: Planet[], count: number = 15) => {
const shuffled = [...all].sort(() => 0.5 - Math.random());
return shuffled.slice(0, count);
},
[],
);
const currentPlanet = useMemo(
() => planets.find((p) => p.id === currentLocationId) || null,
[planets, currentLocationId],
);
// --- Log Helper ---
const addLog = useCallback(
(message: string, type: LogEntry["type"] = "info") => {
const newLog: LogEntry = {
id: Math.random().toString(36).substring(2, 9),
message,
type,
timestamp: new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
}),
};
setLogs((prev) => [...prev, newLog]);
},
[],
);
// --- Actions ---
const updatePower = (newSettings: typeof power) => {
if (!activeEnemy) {
setPower(newSettings);
return;
}
setIsRerouting(true);
addLog(t.ENGINE.SYSTEMS.REROUTING, "info");
setTimeout(() => {
setPower(newSettings);
if (Math.random() > 0.7) {
const damage = 1;
setHp((prev) => Math.max(0, prev - damage));
addLog(
t.ENGINE.SYSTEMS.REROUTE_FAIL(activeEnemy.name, damage),
"danger",
);
} else {
addLog(t.ENGINE.SYSTEMS.REROUTE_SUCCESS, "success");
}
setIsRerouting(false);
}, 600);
};
const startGame = () => {
setGameStarted(true);
addLog(t.ENGINE.BOOT.WELCOME, "info");
};
const executeJump = async () => {
if (!selectedPlanet || selectedPlanet.id === currentLocationId) return;
setIsProcessing(true);
addLog(
`${t.ENGINE.JUMP.INIT(selectedPlanet.name)} ${t.ENGINE.DICE.ROLLING_1W6}`,
"info",
);
setTimeout(() => {
const roll = Math.floor(Math.random() * 6) + 1;
const rollInfo = t.ENGINE.DICE.RESULT_1W6(roll);
if (roll >= 5) {
const randomShip =
starships[Math.floor(Math.random() * starships.length)];
setActiveEnemy(randomShip);
addLog(
`${rollInfo} ${t.ENGINE.JUMP.INTERCEPTION(randomShip.name)}`,
"danger",
);
} else {
const reward = 150;
setCredits((prev) => prev + reward);
setCurrentLocationId(selectedPlanet.id);
addLog(
`${rollInfo} ${t.ENGINE.JUMP.SUCCESS(selectedPlanet.name, reward)}`,
"success",
);
}
setIsProcessing(false);
setSelectedPlanet(null);
}, 1200);
};
const executeEscape = () => {
if (!activeEnemy) return;
setIsProcessing(true);
addLog(t.ENGINE.ESCAPE.INIT, "info");
setTimeout(() => {
const roll = Math.floor(Math.random() * 6) + 1;
const engineBonus = Math.floor(power.engines / 2);
const total = roll + engineBonus;
if (total >= 5) {
setActiveEnemy(null);
addLog(
`${t.ENGINE.DICE.RESULT_TOTAL(total, engineBonus)} ${t.ENGINE.ESCAPE.SUCCESS}`,
"success",
);
} else {
const damage = 2;
setHp((prev) => Math.max(0, prev - damage));
addLog(
`${t.ENGINE.DICE.RESULT_TOTAL(total, engineBonus)} ${t.ENGINE.ESCAPE.FAIL(activeEnemy.name, damage)}`,
"danger",
);
}
setIsProcessing(false);
}, 800);
};
const handleAttack = () => {
if (!activeEnemy) return;
setIsProcessing(true);
addLog(t.ENGINE.COMBAT.INIT(activeEnemy.name), "info");
setTimeout(() => {
const d1 = Math.floor(Math.random() * 6) + 1;
const d2 = Math.floor(Math.random() * 6) + 1;
const laserBonus = Math.floor(power.lasers / 2);
const total = d1 + d2 + laserBonus;
if (total >= activeEnemy.ds) {
const newHp = (activeEnemy.hp || 1) - 1;
if (newHp <= 0) {
const loot = 300;
setCredits((prev) => prev + loot);
setActiveEnemy(null);
addLog(t.ENGINE.COMBAT.VICTORY(activeEnemy.name, loot), "success");
} else {
setActiveEnemy({ ...activeEnemy, hp: newHp });
addLog(t.ENGINE.COMBAT.HIT(activeEnemy.name, newHp), "success");
}
} else {
const engineBonus = Math.floor(power.engines / 2);
const evasionRoll = Math.floor(Math.random() * 6) + 1;
if (evasionRoll + engineBonus >= 5) {
addLog(t.ENGINE.COMBAT.EVASION(evasionRoll, engineBonus), "success");
} else {
const rawDiff = activeEnemy.ds - total;
const shieldBonus = Math.floor(power.shields / 2);
const dmg = Math.max(0, rawDiff - shieldBonus);
setHp((prev) => Math.max(0, prev - dmg));
addLog(t.ENGINE.COMBAT.COUNTER(activeEnemy.name, dmg), "danger");
}
}
setIsProcessing(false);
}, 1000);
};
const handleRepair = () => {
const missingHp = 10 - hp;
const cost = missingHp * 20;
if (missingHp <= 0) return;
if (credits >= cost) {
setCredits((prev) => prev - cost);
setHp(10);
addLog(
t.ENGINE.REPAIR.SUCCESS(currentPlanet?.name || "Unknown", cost),
"success",
);
} else {
addLog(t.ENGINE.REPAIR.INSUFFICIENT_FUNDS, "danger");
}
};
// --- Effects ---
useEffect(() => {
async function initGame() {
try {
setLoading(true);
const { planets: p, starships: s } = await fetchGameData();
setAllPlanets(p);
setStarships(s);
const initialSector = selectRandomSector(p, 15);
setPlanets(initialSector);
if (initialSector.length > 0) setCurrentLocationId(initialSector[0].id);
addLog(t.ENGINE.BOOT.SUCCESS, "success");
} catch (error) {
console.error(error);
addLog(t.ENGINE.BOOT.ERROR, "danger");
} finally {
setLoading(false);
}
}
initGame();
}, [addLog, selectRandomSector]);
useEffect(() => {
if (hp <= 0 && !isGameOver) {
setIsGameOver(true);
addLog(t.ENGINE.SYSTEMS.CRITICAL_FAILURE, "danger");
}
}, [hp, isGameOver, addLog]);
useEffect(() => {
if (credits >= WIN_THRESHOLD && !isVictory) {
setIsVictory(true);
addLog(t.VICTORY.MESSAGE, "success");
}
}, [credits, isVictory, addLog]);
const restartGame = () => {
setHp(10);
setCredits(100);
setPower({ engines: 3, lasers: 3, shields: 3 });
setIsGameOver(false);
setIsVictory(false);
setActiveEnemy(null);
setSelectedPlanet(null);
setLogs([]);
const newSector = selectRandomSector(allPlanets, 15);
setPlanets(newSector);
if (newSector.length > 0) setCurrentLocationId(newSector[0].id);
addLog(t.ENGINE.BOOT.RESTART, "success");
addLog(t.ENGINE.SYSTEMS.SECTOR_UPDATE, "info");
};
return {
planets,
starships,
loading,
gameStarted,
startGame,
credits,
hp,
currentLocationId,
currentPlanet,
selectedPlanet,
setSelectedPlanet,
executeEscape,
executeJump,
handleAttack,
handleRepair,
isJumping: isProcessing,
isRerouting,
activeEnemy,
power,
updatePower,
maxPower: MAX_POWER,
logs,
isGameOver,
restartGame,
isVictory,
WIN_THRESHOLD,
};
}