update and polish
This commit is contained in:
+160
-59
@@ -1,26 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useState, useEffect, useCallback, useMemo } from "react";
|
||||
import { Planet, Starship } from "@/types/swapi";
|
||||
import { fetchPlanets, fetchStarships } from "@/services/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 [isJumping, setIsJumping] = useState<boolean>(false);
|
||||
|
||||
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 = {
|
||||
@@ -37,25 +64,32 @@ export function useGameEngine() {
|
||||
[],
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
// --- Actions ---
|
||||
|
||||
const updatePower = (newSettings: typeof power) => {
|
||||
if (!activeEnemy) {
|
||||
setPower(newSettings);
|
||||
return;
|
||||
}
|
||||
initGame();
|
||||
}, [addLog]);
|
||||
|
||||
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);
|
||||
@@ -64,8 +98,7 @@ export function useGameEngine() {
|
||||
|
||||
const executeJump = async () => {
|
||||
if (!selectedPlanet || selectedPlanet.id === currentLocationId) return;
|
||||
|
||||
setIsJumping(true);
|
||||
setIsProcessing(true);
|
||||
addLog(
|
||||
`${t.ENGINE.JUMP.INIT(selectedPlanet.name)} ${t.ENGINE.DICE.ROLLING_1W6}`,
|
||||
"info",
|
||||
@@ -74,7 +107,6 @@ export function useGameEngine() {
|
||||
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)];
|
||||
@@ -92,65 +124,113 @@ export function useGameEngine() {
|
||||
"success",
|
||||
);
|
||||
}
|
||||
setIsJumping(false);
|
||||
setIsProcessing(false);
|
||||
setSelectedPlanet(null);
|
||||
}, 1200);
|
||||
};
|
||||
|
||||
const executeEscape = () => {
|
||||
if (!activeEnemy) return;
|
||||
addLog(`${t.ENGINE.ESCAPE.INIT} ${t.ENGINE.DICE.ROLLING_1W6}`, "info");
|
||||
|
||||
setIsProcessing(true);
|
||||
addLog(t.ENGINE.ESCAPE.INIT, "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));
|
||||
const engineBonus = Math.floor(power.engines / 2);
|
||||
const total = roll + engineBonus;
|
||||
if (total >= 5) {
|
||||
setActiveEnemy(null);
|
||||
addLog(
|
||||
`${rollInfo} ${t.ENGINE.ESCAPE.FAIL(activeEnemy.name, damage)}`,
|
||||
"danger",
|
||||
`${t.ENGINE.DICE.RESULT_TOTAL(total, engineBonus)} ${t.ENGINE.ESCAPE.SUCCESS}`,
|
||||
"success",
|
||||
);
|
||||
} else {
|
||||
setActiveEnemy(null);
|
||||
addLog(`${rollInfo} ${t.ENGINE.ESCAPE.SUCCESS}`, "success");
|
||||
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;
|
||||
addLog(
|
||||
`${t.ENGINE.COMBAT.INIT(activeEnemy.name)} ${t.ENGINE.DICE.ROLLING_2W6}`,
|
||||
"info",
|
||||
);
|
||||
|
||||
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 total = d1 + d2;
|
||||
const rollInfo = t.ENGINE.DICE.RESULT_2W6(d1, d2, total);
|
||||
|
||||
const laserBonus = Math.floor(power.lasers / 2);
|
||||
const total = d1 + d2 + laserBonus;
|
||||
if (total >= activeEnemy.ds) {
|
||||
const loot = 300;
|
||||
setCredits((prev) => prev + loot);
|
||||
setActiveEnemy(null);
|
||||
addLog(
|
||||
`${rollInfo} ${t.ENGINE.COMBAT.VICTORY(activeEnemy.name, loot)}`,
|
||||
"success",
|
||||
);
|
||||
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 diff = activeEnemy.ds - total;
|
||||
setHp((prev) => Math.max(0, prev - diff));
|
||||
addLog(
|
||||
`${rollInfo} ${t.ENGINE.COMBAT.COUNTER(activeEnemy.name, diff)}`,
|
||||
"danger",
|
||||
);
|
||||
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);
|
||||
@@ -158,14 +238,27 @@ export function useGameEngine() {
|
||||
}
|
||||
}, [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 {
|
||||
@@ -177,15 +270,23 @@ export function useGameEngine() {
|
||||
credits,
|
||||
hp,
|
||||
currentLocationId,
|
||||
currentPlanet,
|
||||
selectedPlanet,
|
||||
setSelectedPlanet,
|
||||
executeEscape,
|
||||
executeJump,
|
||||
handleAttack,
|
||||
isJumping,
|
||||
handleRepair,
|
||||
isJumping: isProcessing,
|
||||
isRerouting,
|
||||
activeEnemy,
|
||||
power,
|
||||
updatePower,
|
||||
maxPower: MAX_POWER,
|
||||
logs,
|
||||
isGameOver,
|
||||
restartGame,
|
||||
isVictory,
|
||||
WIN_THRESHOLD,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user