192 lines
5.2 KiB
TypeScript
192 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useCallback } from "react";
|
|
import { Planet, Starship } from "@/types/swapi";
|
|
import { fetchPlanets, fetchStarships } from "@/services/swapi";
|
|
import { LogEntry } from "@/components/EventLog";
|
|
import { t } from "@/content/locales";
|
|
|
|
export function useGameEngine() {
|
|
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 [credits, setCredits] = useState<number>(100);
|
|
const [hp, setHp] = useState<number>(10);
|
|
const [currentLocationId, setCurrentLocationId] = useState<string>("");
|
|
const [selectedPlanet, setSelectedPlanet] = useState<Planet | null>(null);
|
|
const [isJumping, setIsJumping] = useState<boolean>(false);
|
|
const [activeEnemy, setActiveEnemy] = useState<Starship | null>(null);
|
|
const [logs, setLogs] = useState<LogEntry[]>([]);
|
|
|
|
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]);
|
|
},
|
|
[],
|
|
);
|
|
|
|
useEffect(() => {
|
|
async function initGame() {
|
|
try {
|
|
const [planets, ships] = await Promise.all([
|
|
fetchPlanets(),
|
|
fetchStarships(),
|
|
]);
|
|
setPlanets(planets);
|
|
setStarships(ships);
|
|
if (planets.length > 0) setCurrentLocationId(planets[0].id);
|
|
addLog(t.ENGINE.BOOT.SUCCESS, "success");
|
|
} catch (error) {
|
|
addLog(t.ENGINE.BOOT.ERROR, "danger");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
initGame();
|
|
}, [addLog]);
|
|
|
|
const startGame = () => {
|
|
setGameStarted(true);
|
|
addLog(t.ENGINE.BOOT.WELCOME, "info");
|
|
};
|
|
|
|
const executeJump = async () => {
|
|
if (!selectedPlanet || selectedPlanet.id === currentLocationId) return;
|
|
|
|
setIsJumping(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",
|
|
);
|
|
}
|
|
setIsJumping(false);
|
|
setSelectedPlanet(null);
|
|
}, 1200);
|
|
};
|
|
|
|
const executeEscape = () => {
|
|
if (!activeEnemy) return;
|
|
addLog(`${t.ENGINE.ESCAPE.INIT} ${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 <= 2) {
|
|
const damage = 1;
|
|
setHp((prev) => Math.max(0, prev - damage));
|
|
addLog(
|
|
`${rollInfo} ${t.ENGINE.ESCAPE.FAIL(activeEnemy.name, damage)}`,
|
|
"danger",
|
|
);
|
|
} else {
|
|
setActiveEnemy(null);
|
|
addLog(`${rollInfo} ${t.ENGINE.ESCAPE.SUCCESS}`, "success");
|
|
}
|
|
}, 800);
|
|
};
|
|
|
|
const handleAttack = () => {
|
|
if (!activeEnemy) return;
|
|
addLog(
|
|
`${t.ENGINE.COMBAT.INIT(activeEnemy.name)} ${t.ENGINE.DICE.ROLLING_2W6}`,
|
|
"info",
|
|
);
|
|
|
|
setTimeout(() => {
|
|
const d1 = Math.floor(Math.random() * 6) + 1;
|
|
const d2 = Math.floor(Math.random() * 6) + 1;
|
|
const total = d1 + d2;
|
|
const rollInfo = t.ENGINE.DICE.RESULT_2W6(d1, d2, total);
|
|
|
|
if (total >= activeEnemy.ds) {
|
|
const loot = 300;
|
|
setCredits((prev) => prev + loot);
|
|
setActiveEnemy(null);
|
|
addLog(
|
|
`${rollInfo} ${t.ENGINE.COMBAT.VICTORY(activeEnemy.name, loot)}`,
|
|
"success",
|
|
);
|
|
} else {
|
|
const diff = activeEnemy.ds - total;
|
|
setHp((prev) => Math.max(0, prev - diff));
|
|
addLog(
|
|
`${rollInfo} ${t.ENGINE.COMBAT.COUNTER(activeEnemy.name, diff)}`,
|
|
"danger",
|
|
);
|
|
}
|
|
}, 1000);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (hp <= 0 && !isGameOver) {
|
|
setIsGameOver(true);
|
|
addLog(t.ENGINE.SYSTEMS.CRITICAL_FAILURE, "danger");
|
|
}
|
|
}, [hp, isGameOver, addLog]);
|
|
|
|
const restartGame = () => {
|
|
setHp(10);
|
|
setCredits(100);
|
|
setIsGameOver(false);
|
|
setActiveEnemy(null);
|
|
setSelectedPlanet(null);
|
|
setLogs([]);
|
|
addLog(t.ENGINE.BOOT.RESTART, "success");
|
|
};
|
|
|
|
return {
|
|
planets,
|
|
starships,
|
|
loading,
|
|
gameStarted,
|
|
startGame,
|
|
credits,
|
|
hp,
|
|
currentLocationId,
|
|
selectedPlanet,
|
|
setSelectedPlanet,
|
|
executeEscape,
|
|
executeJump,
|
|
handleAttack,
|
|
isJumping,
|
|
activeEnemy,
|
|
logs,
|
|
isGameOver,
|
|
restartGame,
|
|
};
|
|
}
|