refactor: tighten project structure and strengthen tooling
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { Crosshair } from "@/components/ui/Crosshair";
|
||||
import { DebugPerf } from "@/debug/DebugPerf";
|
||||
import { DebugPerf } from "@/utils/debug/DebugPerf";
|
||||
import { World } from "@/world/World";
|
||||
|
||||
function App(): React.JSX.Element {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// src/components/3d/InteractiveObject.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/components/ui/CinematicBars.tsx
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCameraMode } from "@/debug/useCameraMode";
|
||||
import { useCameraMode } from "@/hooks/debug/useCameraMode";
|
||||
|
||||
export function Crosshair(): React.JSX.Element | null {
|
||||
const cameraMode = useCameraMode();
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// src/components/ui/LoadingScreen.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/components/ui/MapHUD.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/components/ui/MissionHUD.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/components/ui/NarrativeOverlay.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/data/dialogues.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/data/missions.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/data/zones.ts
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useSyncExternalStore } from "react";
|
||||
import type { CameraMode } from "@/debug/Debug";
|
||||
import { Debug } from "@/debug/Debug";
|
||||
import type { CameraMode } from "@/types/debug";
|
||||
import { Debug } from "@/utils/debug/Debug";
|
||||
|
||||
export function useCameraMode(): CameraMode {
|
||||
const debug = Debug.getInstance();
|
||||
@@ -1 +0,0 @@
|
||||
// src/hooks/useAudio.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/hooks/useCinematic.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/hooks/useGameState.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/hooks/useInteraction.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/hooks/useLOD.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/hooks/useZoneDetection.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/stateManager/AudioManager.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/stateManager/CinematicManager.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/stateManager/GameManager.ts
|
||||
@@ -1 +0,0 @@
|
||||
// src/stateManager/ZoneManager.ts
|
||||
@@ -0,0 +1 @@
|
||||
export type CameraMode = "player" | "debug";
|
||||
@@ -1 +0,0 @@
|
||||
// src/utils/Dispose.ts
|
||||
@@ -1 +1,54 @@
|
||||
// src/utils/EventEmitter.ts
|
||||
type Listener<TPayload> = (payload: TPayload) => void;
|
||||
|
||||
export class EventEmitter<TEvents extends Record<string, unknown>> {
|
||||
private readonly listeners = new Map<
|
||||
keyof TEvents,
|
||||
Set<Listener<TEvents[keyof TEvents]>>
|
||||
>();
|
||||
|
||||
on<TKey extends keyof TEvents>(
|
||||
event: TKey,
|
||||
listener: Listener<TEvents[TKey]>,
|
||||
): () => void {
|
||||
const currentListeners = this.listeners.get(event) ?? new Set();
|
||||
currentListeners.add(listener as Listener<TEvents[keyof TEvents]>);
|
||||
this.listeners.set(event, currentListeners);
|
||||
|
||||
return () => {
|
||||
this.off(event, listener);
|
||||
};
|
||||
}
|
||||
|
||||
off<TKey extends keyof TEvents>(
|
||||
event: TKey,
|
||||
listener: Listener<TEvents[TKey]>,
|
||||
): void {
|
||||
const currentListeners = this.listeners.get(event);
|
||||
|
||||
if (!currentListeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentListeners.delete(listener as Listener<TEvents[keyof TEvents]>);
|
||||
|
||||
if (currentListeners.size === 0) {
|
||||
this.listeners.delete(event);
|
||||
}
|
||||
}
|
||||
|
||||
emit<TKey extends keyof TEvents>(event: TKey, payload: TEvents[TKey]): void {
|
||||
const currentListeners = this.listeners.get(event);
|
||||
|
||||
if (!currentListeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentListeners.forEach((listener) => {
|
||||
listener(payload as TEvents[keyof TEvents]);
|
||||
});
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
+50
-1
@@ -1 +1,50 @@
|
||||
// src/utils/Sizes.ts
|
||||
type SizeSnapshot = {
|
||||
width: number;
|
||||
height: number;
|
||||
pixelRatio: number;
|
||||
};
|
||||
|
||||
type SizeListener = (snapshot: SizeSnapshot) => void;
|
||||
|
||||
export class Sizes {
|
||||
private snapshot: SizeSnapshot;
|
||||
private readonly listeners = new Set<SizeListener>();
|
||||
private readonly handleResize = (): void => {
|
||||
this.snapshot = Sizes.readWindow();
|
||||
this.emit();
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.snapshot = Sizes.readWindow();
|
||||
window.addEventListener("resize", this.handleResize);
|
||||
}
|
||||
|
||||
subscribe(listener: SizeListener): () => void {
|
||||
this.listeners.add(listener);
|
||||
|
||||
return () => {
|
||||
this.listeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
getSnapshot(): SizeSnapshot {
|
||||
return this.snapshot;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
window.removeEventListener("resize", this.handleResize);
|
||||
this.listeners.clear();
|
||||
}
|
||||
|
||||
private emit(): void {
|
||||
this.listeners.forEach((listener) => listener(this.snapshot));
|
||||
}
|
||||
|
||||
private static readWindow(): SizeSnapshot {
|
||||
return {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
pixelRatio: Math.min(window.devicePixelRatio, 2),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+42
-1
@@ -1 +1,42 @@
|
||||
// src/utils/Time.ts
|
||||
type TickListener = (delta: number, elapsed: number) => void;
|
||||
|
||||
export class Time {
|
||||
private readonly listeners = new Set<TickListener>();
|
||||
private animationFrameId = 0;
|
||||
private lastTick = performance.now();
|
||||
private elapsed = 0;
|
||||
|
||||
constructor() {
|
||||
this.tick = this.tick.bind(this);
|
||||
this.animationFrameId = window.requestAnimationFrame(this.tick);
|
||||
}
|
||||
|
||||
subscribe(listener: TickListener): () => void {
|
||||
this.listeners.add(listener);
|
||||
|
||||
return () => {
|
||||
this.listeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
getElapsed(): number {
|
||||
return this.elapsed;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
window.cancelAnimationFrame(this.animationFrameId);
|
||||
this.listeners.clear();
|
||||
}
|
||||
|
||||
private tick(now: number): void {
|
||||
const delta = (now - this.lastTick) / 1000;
|
||||
this.lastTick = now;
|
||||
this.elapsed += delta;
|
||||
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(delta, this.elapsed);
|
||||
});
|
||||
|
||||
this.animationFrameId = window.requestAnimationFrame(this.tick);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import GUI from "lil-gui";
|
||||
|
||||
export type CameraMode = "player" | "debug";
|
||||
import type { CameraMode } from "@/types/debug";
|
||||
|
||||
export class Debug {
|
||||
private static instance: Debug | null = null;
|
||||
@@ -9,8 +8,9 @@ export class Debug {
|
||||
private readonly gui: GUI | null;
|
||||
private readonly folders = new Map<string, GUI>();
|
||||
private readonly listeners = new Set<() => void>();
|
||||
private readonly controls = { cameraMode: "player" as CameraMode };
|
||||
private cameraMode: CameraMode = "player";
|
||||
private readonly controls: { cameraMode: CameraMode } = {
|
||||
cameraMode: "player",
|
||||
};
|
||||
|
||||
static getInstance(): Debug {
|
||||
if (!Debug.instance) {
|
||||
@@ -27,17 +27,22 @@ export class Debug {
|
||||
if (this.gui) {
|
||||
const folder = this.createFolder("Debug");
|
||||
|
||||
if (!folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
folder
|
||||
?.add(this.controls, "cameraMode", { Player: "player", Debug: "debug" })
|
||||
.add(this.controls, "cameraMode", { Player: "player", Debug: "debug" })
|
||||
.name("Camera Mode")
|
||||
.onChange((value: CameraMode) => {
|
||||
this.controls.cameraMode = value;
|
||||
this.cameraMode = value;
|
||||
this.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
createFolder(name: string): GUI;
|
||||
createFolder(name: string): GUI | null;
|
||||
createFolder(name: string): GUI | null {
|
||||
if (!this.gui) {
|
||||
return null;
|
||||
@@ -63,19 +68,8 @@ export class Debug {
|
||||
};
|
||||
}
|
||||
|
||||
isDebugCameraEnabled(): boolean {
|
||||
return this.cameraMode === "debug";
|
||||
}
|
||||
|
||||
getCameraMode(): CameraMode {
|
||||
return this.cameraMode;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.listeners.clear();
|
||||
this.folders.clear();
|
||||
this.gui?.destroy();
|
||||
Debug.instance = null;
|
||||
return this.controls.cameraMode;
|
||||
}
|
||||
|
||||
private emit(): void {
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Suspense, lazy } from "react";
|
||||
import { Debug } from "@/debug/Debug";
|
||||
import { Debug } from "@/utils/debug/Debug";
|
||||
|
||||
const Perf = lazy(() => import("r3f-perf").then((m) => ({ default: m.Perf })));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Debug } from "@/debug/Debug";
|
||||
import { Debug } from "@/utils/debug/Debug";
|
||||
|
||||
export function DebugHelpers(): React.JSX.Element | null {
|
||||
const debug = Debug.getInstance();
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import type { AmbientLight, DirectionalLight } from "three";
|
||||
import { Debug } from "@/debug/Debug";
|
||||
import { Debug } from "@/utils/debug/Debug";
|
||||
|
||||
type LightingState = {
|
||||
ambientIntensity: number;
|
||||
@@ -27,22 +27,16 @@ export function Lighting(): React.JSX.Element {
|
||||
const debug = Debug.getInstance();
|
||||
|
||||
if (!debug.active) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const folder = debug.createFolder("Lighting");
|
||||
|
||||
if (!folder) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
folder.add(LIGHTING_STATE, "ambientIntensity", 0, 5, 0.1).name("Ambient");
|
||||
folder.add(LIGHTING_STATE, "sunIntensity", 0, 8, 0.1).name("Sun Intensity");
|
||||
folder.add(LIGHTING_STATE, "sunX", -100, 100, 1).name("Sun X");
|
||||
folder.add(LIGHTING_STATE, "sunY", 0, 150, 1).name("Sun Y");
|
||||
folder.add(LIGHTING_STATE, "sunZ", -100, 100, 1).name("Sun Z");
|
||||
|
||||
return undefined;
|
||||
}, []);
|
||||
|
||||
useFrame(() => {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// src/world/PostFX.tsx
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { Suspense } from "react";
|
||||
import { DebugCameraControls } from "@/debug/scene/DebugCameraControls";
|
||||
import { DebugHelpers } from "@/debug/scene/DebugHelpers";
|
||||
import { useCameraMode } from "@/debug/useCameraMode";
|
||||
import { useCameraMode } from "@/hooks/debug/useCameraMode";
|
||||
import { DebugCameraControls } from "@/utils/debug/scene/DebugCameraControls";
|
||||
import { DebugHelpers } from "@/utils/debug/scene/DebugHelpers";
|
||||
import { Environment } from "@/world/Environment";
|
||||
import { Lighting } from "@/world/Lighting";
|
||||
import { Map } from "@/world/Map";
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// src/world/player/Crosshair.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/world/zones/FarmZone.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/world/zones/PowerGridZone.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/world/zones/ResidentialZone.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/world/zones/SchoolZone.tsx
|
||||
@@ -1 +0,0 @@
|
||||
// src/world/zones/WorkshopZone.tsx
|
||||
Reference in New Issue
Block a user