-
+
+
{t.START_SCREEN.INTRO_TEXT}
-
- {" "}
+
{t.START_SCREEN.MISSION_HIGHLIGHT}
-
+
+
- •{" "}
-
- {t.START_SCREEN.FEATURE_NAV_TITLE}
- {" "}
- {t.START_SCREEN.FEATURE_NAV_DESC}
+ • {t.START_SCREEN.FEATURE_NAV_TITLE} {" "}
+
+ {t.START_SCREEN.FEATURE_NAV_DESC}
+
- •{" "}
-
- {t.START_SCREEN.FEATURE_RISK_TITLE}
- {" "}
- {t.START_SCREEN.FEATURE_RISK_DESC}
+ • {t.START_SCREEN.FEATURE_RISK_TITLE} {" "}
+
+ {t.START_SCREEN.FEATURE_RISK_DESC}
+
- •{" "}
-
- {t.START_SCREEN.FEATURE_COMBAT_TITLE}
- {" "}
- {t.START_SCREEN.FEATURE_COMBAT_DESC}
+ • {t.START_SCREEN.FEATURE_COMBAT_TITLE} {" "}
+
+ {t.START_SCREEN.FEATURE_COMBAT_DESC}
+
@@ -59,13 +86,12 @@ export default function StartScreen({ onStart }: StartScreenProps) {
- {t.UI.START_BUTTON}
-
+ {t.UI.START_BUTTON}
-
+
{t.START_SCREEN.FOOTER_ENGINE}
diff --git a/components/VictoryScreen.tsx b/components/VictoryScreen.tsx
new file mode 100644
index 0000000..2694adf
--- /dev/null
+++ b/components/VictoryScreen.tsx
@@ -0,0 +1,113 @@
+"use client";
+
+import React from "react";
+import { t } from "@/content/locales";
+
+interface VictoryScreenProps {
+ credits: number;
+ onRestart: () => void;
+}
+
+export default function VictoryScreen({
+ credits,
+ onRestart,
+}: VictoryScreenProps) {
+ const creditsBlue = "#4bd5ee";
+
+ return (
+
+
+
+
+
+
+
+
+ {t.VICTORY.TITLE}
+
+
+ {t.VICTORY.SUBTITLE}
+
+
+
+
+
+
+ {t.VICTORY.MESSAGE}
+
+
+ {credits.toLocaleString()} CR
+
+
+
+
+ The galaxy will remember your name.
+
+
+
+
+ {
+ e.currentTarget.style.backgroundColor = creditsBlue;
+ e.currentTarget.style.color = "black";
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.backgroundColor = "transparent";
+ e.currentTarget.style.color = creditsBlue;
+ }}
+ >
+ {t.VICTORY.BUTTON}
+
+
+
+
+ );
+}
diff --git a/content/locales.ts b/content/locales.ts
index ab56ff3..a6fc41a 100644
--- a/content/locales.ts
+++ b/content/locales.ts
@@ -1,24 +1,24 @@
// constants/locales.ts
export const locales = {
- de: {
+ en: {
UI: {
GAME_TITLE: "Smuggler's Run",
SUBTITLE: "Fast Ships, faster credits!",
START_BUTTON: "Initialize Systems",
- RESTART_BUTTON: "Klon-Zylinder aktivieren",
+ RESTART_BUTTON: "Activate Clone Cylinder",
JUMP_BUTTON: "Initiate Jump",
- ATTACK_BUTTON: "Feuer frei",
- ESCAPE_BUTTON: "Fluchtversuch",
+ ATTACK_BUTTON: "Open Fire",
+ ESCAPE_BUTTON: "Evasive Maneuvers",
},
SYSTEM: {
LOADING: "INITIALIZING HOLONET LINK...",
DEEP_SPACE: "Deep Space",
},
START_SCREEN: {
- SUBTITLE: "Fast Ships, faster credits!",
+ SUBTITLE: "Episode I: Fast Ships, faster credits!",
INTRO_TEXT:
- "Smuggler's Run is a minimalist space adventure game, where you, the player, are an unlicensed space trader, flying cargo from planet to planet while trying not to be blown to pieces by bounty hunters, imperial forces or rebel do-gooders. Your mission is simple:",
+ "Smuggler's Run is a minimalist space adventure game where you are an unlicensed space trader, flying cargo from planet to planet while trying not to be blown to pieces by bounty hunters, imperial forces, or rebel do-gooders. Your mission is simple:",
MISSION_HIGHLIGHT: "Survive and earn credits",
FEATURE_NAV_TITLE: "Navigation:",
FEATURE_NAV_DESC: "Jump between sectors to deliver cargo.",
@@ -59,6 +59,8 @@ export const locales = {
FIRE: "Open Fire",
JUMP: "Initiate Jump",
ESCAPE: "Evasive Maneuvers",
+ REPAIR: (cost: number) => `Repair ship for (${cost} Cr)`,
+ HULL_INTACT: "Hull integrity stable",
},
},
GAME_OVER: {
@@ -67,6 +69,13 @@ export const locales = {
SCORE_LABEL: "Final Profit",
RESTART_BUTTON: "Activate Clone Cylinder",
},
+ VICTORY: {
+ TITLE: "Victory",
+ SUBTITLE: "Wealth Threshold Exceeded",
+ MESSAGE:
+ "You have amassed enough credits to retire in the Outer Rim. The Empire's reach can no longer find you.",
+ BUTTON: "NEW ADVENTURE",
+ },
EVENT_LOG: {
TITLE: "Event Log",
EMPTY_STATE: "Waiting for signals...",
@@ -74,7 +83,7 @@ export const locales = {
HUD: {
SECTOR_LABEL: "Current Sector",
HULL_LABEL: "Hull Integrity",
- GOAL_LABEL: "Credits to Freedom", // Oder "Credits to Falcon"
+ GOAL_LABEL: "Credits to Freedom",
},
GALAXY_MAP: {
SCAN_STATUS: "Sector Scan: Active",
@@ -102,29 +111,54 @@ export const locales = {
SUCCESS: "Jump successful! We've shaken off the pursuer.",
},
COMBAT: {
- INIT: (ship: string) =>
- `Weapon systems synchronized. Opening fire on ${ship}!`,
+ INIT: (ship: string) => `Weapon systems synchronized. Target: ${ship}!`,
VICTORY: (ship: string, loot: number) =>
- `Direct hit! The ${ship} has been obliterated. +${loot} Credits salvaged.`,
+ `Target neutralized! The ${ship} has been destroyed. +${loot} Credits salvaged.`,
COUNTER: (ship: string, dmg: number) =>
- `Shields holding! The ${ship} counters. -${dmg} HP`,
+ `The ${ship} returns fire! -${dmg} HP`,
+ HIT: (ship: string, hp: number) =>
+ `Direct hit! ${ship} integrity at ${hp} HP.`,
+ EVASION: (roll: number, bonus: number) =>
+ `[Evasion: ${roll}+${bonus}] High-speed maneuver successful! Enemy fire avoided.`,
+ },
+ REPAIR: {
+ SUCCESS: (planet: string, cost: number) =>
+ `Repairs complete at ${planet}. -${cost} Credits.`,
+ INSUFFICIENT_FUNDS: "Insufficient credits for repairs!",
},
SYSTEMS: {
CRITICAL_FAILURE:
"CRITICAL STRUCTURAL DAMAGE! The ship is breaking apart...",
+ REROUTING: "Rerouting power systems...",
+ REROUTE_SUCCESS: "Power configuration updated.",
+ REROUTE_FAIL: (ship: string, dmg: number) =>
+ `Power lag! ${ship} exploits the window. -${dmg} HP`,
+ SECTOR_UPDATE: "Nav-computer recalibrated. New sector discovered.",
},
DICE: {
ROLLING_1W6: "[Rolling 1D6...]",
ROLLING_2W6: "[Rolling 2D6...]",
RESULT_1W6: (res: number) => `[Roll: ${res}]`,
- RESULT_2W6: (d1: number, d2: number, total: number) =>
- `[Roll: ${d1} + ${d2} = ${total}]`,
+ RESULT_TOTAL: (total: number, bonus: number) =>
+ `[Total: ${total} (Bonus: +${bonus})]`,
},
},
+ SYSTEMS: {
+ POWER_MANAGEMENT: "Power Management",
+ SUB_LABEL: "System Distribution Active",
+ RESET: "Reset",
+ BOOST: "Boost",
+ ENGINES: "Engines",
+ LASERS: "Lasers",
+ SHIELDS: "Shields",
+ TOTAL_POWER: "Total Power",
+ NO_ENERGY: "Energy Depleted",
+ REROUTING: "Rerouting Energy...",
+ },
},
- // Später einfach erweiterbar:
- // en: { ... }
};
-// Aktuelle Sprache festlegen (könnte später aus einem State kommen)
-export const t = locales.de;
+export type LocaleType = typeof locales.en;
+
+// Defaulting to English
+export const t = locales.en;
diff --git a/hooks/useGameEngine.ts b/hooks/useGameEngine.ts
index 21a45f7..e763043 100644
--- a/hooks/useGameEngine.ts
+++ b/hooks/useGameEngine.ts
@@ -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
([]);
const [planets, setPlanets] = useState([]);
const [starships, setStarships] = useState([]);
const [loading, setLoading] = useState(true);
-
const [gameStarted, setGameStarted] = useState(false);
const [isGameOver, setIsGameOver] = useState(false);
+ const [isVictory, setIsVictory] = useState(false);
const [credits, setCredits] = useState(100);
const [hp, setHp] = useState(10);
const [currentLocationId, setCurrentLocationId] = useState("");
const [selectedPlanet, setSelectedPlanet] = useState(null);
- const [isJumping, setIsJumping] = useState(false);
+
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [isRerouting, setIsRerouting] = useState(false);
+
const [activeEnemy, setActiveEnemy] = useState(null);
const [logs, setLogs] = useState([]);
+ 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,
};
}
diff --git a/public/fonts/Aurebesh-English.ttf b/public/fonts/Aurebesh-English.ttf
new file mode 100644
index 0000000..f03b6b9
Binary files /dev/null and b/public/fonts/Aurebesh-English.ttf differ
diff --git a/services/swapi.ts b/services/swapi.ts
index 39e641f..4f9432a 100644
--- a/services/swapi.ts
+++ b/services/swapi.ts
@@ -1,35 +1,84 @@
import { SWAPIPlanet, SWAPIStarship, Planet, Starship } from "@/types/swapi";
const BASE_URL = "https://swapi.dev/api";
+const CACHE_KEY = "smugglers_run_cache";
+const CACHE_DURATION = 1000 * 60 * 60 * 24; // Cache TTL 24 hrs
-export async function fetchPlanets(): Promise {
- const res = await fetch(`${BASE_URL}/planets/`);
- if (!res.ok) throw new Error("Galaxy data unavailable");
- const data = await res.json();
-
- return data.results.map(
- (p: SWAPIPlanet): Planet => ({
- ...p,
- id: p.url,
- // Add random x/y coordinates for galaxy map
- x: Math.floor(Math.random() * 80) + 10,
- y: Math.floor(Math.random() * 70) + 15,
- }),
- );
+interface GameCache {
+ planets: Planet[];
+ starships: Starship[];
+ timestamp: number;
}
-export async function fetchStarships(): Promise {
- const res = await fetch(`${BASE_URL}/starships/`);
- if (!res.ok) throw new Error("Imperial records unavailable");
- const data = await res.json();
+async function fetchAllPages(endpoint: string): Promise {
+ let allResults: T[] = [];
+ let nextUrl = `${BASE_URL}/${endpoint}/`;
- return data.results.map((s: SWAPIStarship): Starship => {
- // Add difficulty score based on ship cost
+ while (nextUrl) {
+ const res = await fetch(nextUrl);
+ if (!res.ok) throw new Error(`${endpoint} data unavailable`);
+ const data = await res.json();
+ allResults = [...allResults, ...data.results];
+ nextUrl = data.next;
+ }
+ return allResults;
+}
+
+export async function fetchGameData(): Promise<{
+ planets: Planet[];
+ starships: Starship[];
+}> {
+ // Try load from localstorage as simple cache
+ const cachedData =
+ typeof window !== "undefined" ? localStorage.getItem(CACHE_KEY) : null;
+
+ if (cachedData) {
+ const parsedCache: GameCache = JSON.parse(cachedData);
+ const isExpired = Date.now() - parsedCache.timestamp > CACHE_DURATION;
+
+ if (!isExpired) {
+ console.log("data loaded from cache");
+
+ return { planets: parsedCache.planets, starships: parsedCache.starships };
+ }
+ }
+
+ // If not in cache or outdated, fetch from Swapi
+ console.log("data not in cache. fetching from swapi...");
+
+ const [rawPlanets, rawStarships] = await Promise.all([
+ fetchAllPages("planets"),
+ fetchAllPages("starships"),
+ ]);
+
+ const planets: Planet[] = rawPlanets.map((p) => ({
+ ...p,
+ id: p.url,
+ x: Math.floor(Math.random() * 80) + 10,
+ y: Math.floor(Math.random() * 70) + 15,
+ }));
+
+ const starships: Starship[] = rawStarships.map((s) => {
const cost = parseInt(s.cost_in_credits);
- let ds = 8;
- if (isNaN(cost) || cost > 1000000) ds = 12;
- else if (cost > 100000) ds = 10;
-
- return { ...s, id: s.url, ds };
+ let ds = 8,
+ hp = 2;
+ if (isNaN(cost) || cost > 1000000) {
+ ds = 12;
+ hp = 6;
+ } else if (cost > 100000) {
+ ds = 10;
+ hp = 4;
+ }
+ return { ...s, id: s.url, ds, hp };
});
+
+ // Save to localstorage
+ const cacheToSave: GameCache = {
+ planets,
+ starships,
+ timestamp: Date.now(),
+ };
+ localStorage.setItem(CACHE_KEY, JSON.stringify(cacheToSave));
+
+ return { planets, starships };
}
diff --git a/types/swapi.ts b/types/swapi.ts
index 4d2a2c9..b89954d 100644
--- a/types/swapi.ts
+++ b/types/swapi.ts
@@ -1,7 +1,4 @@
-// types/swapi.ts
-
-// Die korrigierten Rohdaten von der SWAPI
-export interface SWAPIPlanet { // <-- Jetzt mit korrektem 'n'
+export interface SWAPIPlanet {
name: string;
rotation_period: string;
orbital_period: string;
@@ -28,7 +25,6 @@ export interface SWAPIStarship {
url: string;
}
-// Unsere erweiterten Typen für das Spiel
export interface Planet extends SWAPIPlanet {
id: string;
x: number;
@@ -37,5 +33,6 @@ export interface Planet extends SWAPIPlanet {
export interface Starship extends SWAPIStarship {
id: string;
- ds: number; // Difficulty Score für Tunnel Goons
-}
\ No newline at end of file
+ ds: number;
+ hp: number;
+}
diff --git a/utils/getPlanetTheme.ts b/utils/getPlanetTheme.ts
new file mode 100644
index 0000000..058f5aa
--- /dev/null
+++ b/utils/getPlanetTheme.ts
@@ -0,0 +1,63 @@
+import { LocaleType } from "@/content/locales";
+
+export const getPlanetTheme = (
+ terrain: string = "",
+ climate: string = "",
+ t: LocaleType,
+) => {
+ const terr = terrain.toLowerCase();
+ const clim = climate.toLowerCase();
+
+ if (terr.includes("ocean") || terr.includes("water"))
+ return {
+ color: "text-blue-400",
+ border: "border-blue-500",
+ glow: "shadow-[0_0_20px_#3b82f6]",
+ label: t.ACTION_PANEL.MODES.AQUATIC,
+ hex: "#60a5fa",
+ };
+ if (terr.includes("forest") || terr.includes("jungle"))
+ return {
+ color: "text-green-400",
+ border: "border-green-500",
+ glow: "shadow-[0_0_20px_#22c55e]",
+ label: t.ACTION_PANEL.MODES.VERDANT,
+ hex: "#4ade80",
+ };
+ if (terr.includes("desert") || clim.includes("arid"))
+ return {
+ color: "text-orange-400",
+ border: "border-orange-500",
+ glow: "shadow-[0_0_20px_#f97316]",
+ label: t.ACTION_PANEL.MODES.ARID,
+ hex: "#fb923c",
+ };
+ if (
+ terr.includes("ice") ||
+ terr.includes("glacier") ||
+ clim.includes("frozen")
+ )
+ return {
+ color: "text-cyan-200",
+ border: "border-cyan-300",
+ glow: "shadow-[0_0_20px_#a5f3fc]",
+ label: t.ACTION_PANEL.MODES.CRYO,
+ hex: "#22d3ee",
+ };
+ if (terr.includes("city") || terr.includes("urban"))
+ return {
+ color: "text-purple-400",
+ border: "border-purple-500",
+ glow: "shadow-[0_0_20px_#a855f7]",
+ label: t.ACTION_PANEL.MODES.ECUMENOPOLIS,
+ hex: "#c084fc",
+ };
+
+ return {
+ color: "text-yellow-500",
+ border: "border-yellow-500",
+ glow: "shadow-[0_0_20px_#eab308]",
+ label: t.ACTION_PANEL.MODES.STANDARD,
+ hex: "#eab308",
+ };
+};