upatde: add prettier

This commit is contained in:
2026-04-14 08:59:36 +02:00
parent 25e3d503b2
commit dbb3c46e35
52 changed files with 1444 additions and 268 deletions
+7
View File
@@ -0,0 +1,7 @@
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}
+123 -116
View File
@@ -7,33 +7,35 @@ Built with React, Three.js, and Vite. Runs in the browser, no installation requi
## 📦 Tech Stack ## 📦 Tech Stack
### Build & Language ### Build & Language
| Package | Doc |
|--------|-----| | Package | Doc |
| -------------------------------------------------- | ------------------------------------ |
| [TypeScript](https://www.typescriptlang.org/docs/) | https://www.typescriptlang.org/docs/ | | [TypeScript](https://www.typescriptlang.org/docs/) | https://www.typescriptlang.org/docs/ |
| [React](https://react.dev/learn) | https://react.dev/learn | | [React](https://react.dev/learn) | https://react.dev/learn |
| [Vite](https://vite.dev/guide/) | https://vite.dev/guide/ | | [Vite](https://vite.dev/guide/) | https://vite.dev/guide/ |
| [ESLint](https://eslint.org/docs/latest/) | https://eslint.org/docs/latest/ | | [ESLint](https://eslint.org/docs/latest/) | https://eslint.org/docs/latest/ |
| [Prettier](https://prettier.io/docs/) | https://prettier.io/docs/ | | [Prettier](https://prettier.io/docs/) | https://prettier.io/docs/ |
### 3D Engine ### 3D Engine
| Package | Doc |
|--------|-----| | Package | Doc |
| [Three.js](https://threejs.org/docs/) | https://threejs.org/docs/ | | ----------------------------------------------------------------------------------------- | ---------------------------------------------- |
| [@react-three/fiber](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction) | https://docs.pmnd.rs/react-three-fiber | | [Three.js](https://threejs.org/docs/) | https://threejs.org/docs/ |
| [@react-three/drei](https://pmndrs.github.io/drei) | https://pmndrs.github.io/drei | | [@react-three/fiber](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction) | https://docs.pmnd.rs/react-three-fiber |
| [@react-three/rapier](https://rapier.rs/docs/) | https://rapier.rs/docs/user_guides/javascript/ | | [@react-three/drei](https://pmndrs.github.io/drei) | https://pmndrs.github.io/drei |
| [@react-three/postprocessing](https://github.com/pmndrs/postprocessing) | https://github.com/pmndrs/postprocessing | | [@react-three/rapier](https://rapier.rs/docs/) | https://rapier.rs/docs/user_guides/javascript/ |
| [GSAP](https://gsap.com/docs/v3/Installation/) | https://gsap.com/docs/v3/ | | [@react-three/postprocessing](https://github.com/pmndrs/postprocessing) | https://github.com/pmndrs/postprocessing |
| [GSAP](https://gsap.com/docs/v3/Installation/) | https://gsap.com/docs/v3/ |
### Performance & Effects ### Performance & Effects
| Package | Doc |
|--------|-----| | Package | Doc |
| [r3f-perf](https://github.com/utsuboco/r3f-perf) | https://github.com/utsuboco/r3f-perf | | --------------------------------------------------------------------------- | --------------------------------------------------------- |
| [r3f-perf](https://github.com/utsuboco/r3f-perf) | https://github.com/utsuboco/r3f-perf |
| [AnimationMixer](https://threejs.org/docs/#api/en/animation/AnimationMixer) | https://threejs.org/docs/#api/en/animation/AnimationMixer | | [AnimationMixer](https://threejs.org/docs/#api/en/animation/AnimationMixer) | https://threejs.org/docs/#api/en/animation/AnimationMixer |
## 🗂 Project Structure ## 🗂 Project Structure
``` ```
la-fabrik/ la-fabrik/
├── public/ ├── public/
@@ -103,7 +105,7 @@ la-fabrik/
├── App.tsx # Canvas + UI superimposed ├── App.tsx # Canvas + UI superimposed
└── main.tsx └── main.tsx
``` ```
## 🏗 Architecture Patterns ## 🏗 Architecture Patterns
The project uses **two complementary patterns**: The project uses **two complementary patterns**:
@@ -123,6 +125,7 @@ Consistency matters, but the codebase does **not** force the same lifecycle patt
Only cross-cutting services use the singleton pattern. Only cross-cutting services use the singleton pattern.
Examples: Examples:
- `GameManager` - `GameManager`
- `CinematicManager` - `CinematicManager`
- `AudioManager` - `AudioManager`
@@ -135,30 +138,30 @@ These services must exist once, be accessible from anywhere, and coordinate the
```ts ```ts
// stateManager/GameManager.ts // stateManager/GameManager.ts
export class GameManager { export class GameManager {
private static _instance: GameManager | null = null private static _instance: GameManager | null = null;
cinematic!: CinematicManager cinematic!: CinematicManager;
audio!: AudioManager audio!: AudioManager;
zone!: ZoneManager zone!: ZoneManager;
static getInstance(): GameManager { static getInstance(): GameManager {
if (!GameManager._instance) { if (!GameManager._instance) {
GameManager._instance = new GameManager() GameManager._instance = new GameManager();
} }
return GameManager._instance return GameManager._instance;
} }
private constructor() { private constructor() {
this.cinematic = CinematicManager.getInstance() this.cinematic = CinematicManager.getInstance();
this.audio = AudioManager.getInstance() this.audio = AudioManager.getInstance();
this.zone = ZoneManager.getInstance() this.zone = ZoneManager.getInstance();
} }
destroy(): void { destroy(): void {
this.cinematic.destroy() this.cinematic.destroy();
this.audio.destroy() this.audio.destroy();
this.zone.destroy() this.zone.destroy();
GameManager._instance = null GameManager._instance = null;
} }
} }
``` ```
@@ -166,8 +169,8 @@ export class GameManager {
Usage: Usage:
```ts ```ts
const game = GameManager.getInstance() const game = GameManager.getInstance();
game.startMission('workshop') game.startMission("workshop");
``` ```
**Important:** scene objects such as `Map`, `WorkshopZone`, `Lighting`, or `Environment` are **not** singletons and must remain standard React components. **Important:** scene objects such as `Map`, `WorkshopZone`, `Lighting`, or `Environment` are **not** singletons and must remain standard React components.
@@ -179,6 +182,7 @@ game.startMission('workshop')
All 3D scene objects are implemented as **declarative React components**. All 3D scene objects are implemented as **declarative React components**.
This includes: This includes:
- maps - maps
- lights - lights
- environments - environments
@@ -193,34 +197,35 @@ Example:
```tsx ```tsx
// world/zones/WorkshopZone.tsx // world/zones/WorkshopZone.tsx
import { useEffect, useRef } from 'react' import { useEffect, useRef } from "react";
import * as THREE from 'three' import * as THREE from "three";
import { useFrame } from '@react-three/fiber' import { useFrame } from "@react-three/fiber";
import { useGLTF } from '@react-three/drei' import { useGLTF } from "@react-three/drei";
export function WorkshopZone() { export function WorkshopZone() {
const root = useRef<THREE.Group>(null) const root = useRef<THREE.Group>(null);
const gltf = useGLTF('/models/workshop/ebike.glb') const gltf = useGLTF("/models/workshop/ebike.glb");
const mixer = useRef<THREE.AnimationMixer | null>(null) const mixer = useRef<THREE.AnimationMixer | null>(null);
useEffect(() => { useEffect(() => {
mixer.current = new THREE.AnimationMixer(gltf.scene) mixer.current = new THREE.AnimationMixer(gltf.scene);
return () => { return () => {
mixer.current?.stopAllAction() mixer.current?.stopAllAction();
mixer.current = null mixer.current = null;
} };
}, [gltf.scene]) }, [gltf.scene]);
useFrame((_, delta) => { useFrame((_, delta) => {
mixer.current?.update(delta) mixer.current?.update(delta);
}) });
return <primitive ref={root} object={gltf.scene.clone()} /> return <primitive ref={root} object={gltf.scene.clone()} />;
} }
``` ```
Per-frame values such as movement, interpolation, camera smoothing, and physics must stay in: Per-frame values such as movement, interpolation, camera smoothing, and physics must stay in:
- `useRef` - `useRef`
- `useFrame` - `useFrame`
- Rapier bodies - Rapier bodies
@@ -241,89 +246,90 @@ High-frequency values such as movement, camera interpolation, or physics never g
```ts ```ts
// stateManager/GameManager.ts // stateManager/GameManager.ts
type Phase = 'loading' | 'intro' | 'exploring' | 'cinematic' | 'outro' type Phase = "loading" | "intro" | "exploring" | "cinematic" | "outro";
type ZoneId = 'workshop' | 'powerGrid' | 'farm' | null type ZoneId = "workshop" | "powerGrid" | "farm" | null;
type GameSnapshot = { type GameSnapshot = {
phase: Phase phase: Phase;
activeZone: ZoneId activeZone: ZoneId;
missionId: string | null missionId: string | null;
missionStep: number missionStep: number;
inputLocked: boolean inputLocked: boolean;
dialogueId: string | null dialogueId: string | null;
} };
export class GameManager { export class GameManager {
private static _instance: GameManager | null = null private static _instance: GameManager | null = null;
private listeners = new Set<() => void>() private listeners = new Set<() => void>();
private state: GameSnapshot = { private state: GameSnapshot = {
phase: 'loading', phase: "loading",
activeZone: null, activeZone: null,
missionId: null, missionId: null,
missionStep: 0, missionStep: 0,
inputLocked: false, inputLocked: false,
dialogueId: null, dialogueId: null,
} };
static getInstance(): GameManager { static getInstance(): GameManager {
if (!GameManager._instance) { if (!GameManager._instance) {
GameManager._instance = new GameManager() GameManager._instance = new GameManager();
} }
return GameManager._instance return GameManager._instance;
} }
getState(): GameSnapshot { getState(): GameSnapshot {
return this.state return this.state;
} }
subscribe(listener: () => void): () => void { subscribe(listener: () => void): () => void {
this.listeners.add(listener) this.listeners.add(listener);
return () => this.listeners.delete(listener) return () => this.listeners.delete(listener);
} }
private emit(): void { private emit(): void {
this.listeners.forEach((cb) => cb()) this.listeners.forEach((cb) => cb());
} }
setPhase(phase: Phase): void { setPhase(phase: Phase): void {
this.state.phase = phase this.state.phase = phase;
this.emit() this.emit();
} }
setActiveZone(zone: ZoneId): void { setActiveZone(zone: ZoneId): void {
this.state.activeZone = zone this.state.activeZone = zone;
this.emit() this.emit();
} }
startMission(id: string): void { startMission(id: string): void {
this.state.missionId = id this.state.missionId = id;
this.state.missionStep = 0 this.state.missionStep = 0;
this.emit() this.emit();
} }
} }
``` ```
```ts ```ts
// hooks/useGameState.ts // hooks/useGameState.ts
import { useEffect, useState } from 'react' import { useEffect, useState } from "react";
import { GameManager } from '@/stateManager/GameManager' import { GameManager } from "@/stateManager/GameManager";
export function useGameState() { export function useGameState() {
const game = GameManager.getInstance() const game = GameManager.getInstance();
const [state, setState] = useState(game.getState()) const [state, setState] = useState(game.getState());
useEffect(() => { useEffect(() => {
return game.subscribe(() => { return game.subscribe(() => {
setState({ ...game.getState() }) setState({ ...game.getState() });
}) });
}, [game]) }, [game]);
return state return state;
} }
``` ```
This keeps the architecture simple: This keeps the architecture simple:
- **GameManager** owns durable gameplay state - **GameManager** owns durable gameplay state
- **other managers** handle side effects - **other managers** handle side effects
- **React components** render that state - **React components** render that state
@@ -336,6 +342,7 @@ This keeps the architecture simple:
Managers other than `GameManager` should not become secondary state stores. Managers other than `GameManager` should not become secondary state stores.
Their role is to manage side effects and specialized runtime logic, such as: Their role is to manage side effects and specialized runtime logic, such as:
- GSAP timelines - GSAP timelines
- audio playback - audio playback
- zone entry detection - zone entry detection
@@ -369,6 +376,7 @@ However, the project does **not** blindly deep-dispose every object on unmount.
Only resources explicitly created and owned by the current component or manager should be disposed. Only resources explicitly created and owned by the current component or manager should be disposed.
This includes things like: This includes things like:
- custom materials - custom materials
- render targets - render targets
- postprocessing passes - postprocessing passes
@@ -380,33 +388,33 @@ Shared or cached assets must **not** be blindly disposed.
```ts ```ts
// utils/Dispose.ts // utils/Dispose.ts
import * as THREE from 'three' import * as THREE from "three";
export class Dispose { export class Dispose {
static material(material: THREE.Material): void { static material(material: THREE.Material): void {
for (const value of Object.values(material)) { for (const value of Object.values(material)) {
if (value instanceof THREE.Texture) { if (value instanceof THREE.Texture) {
value.dispose() value.dispose();
} }
} }
material.dispose() material.dispose();
} }
static mesh(mesh: THREE.Mesh): void { static mesh(mesh: THREE.Mesh): void {
mesh.geometry?.dispose() mesh.geometry?.dispose();
const materials = Array.isArray(mesh.material) const materials = Array.isArray(mesh.material)
? mesh.material ? mesh.material
: [mesh.material] : [mesh.material];
for (const material of materials) { for (const material of materials) {
if (material) this.material(material) if (material) this.material(material);
} }
} }
static renderTarget(rt: THREE.WebGLRenderTarget): void { static renderTarget(rt: THREE.WebGLRenderTarget): void {
rt.texture.dispose() rt.texture.dispose();
rt.dispose() rt.dispose();
} }
} }
``` ```
@@ -418,14 +426,14 @@ useEffect(() => {
const material = new THREE.ShaderMaterial({ const material = new THREE.ShaderMaterial({
vertexShader, vertexShader,
fragmentShader, fragmentShader,
}) });
meshRef.current.material = material meshRef.current.material = material;
return () => { return () => {
Dispose.material(material) Dispose.material(material);
} };
}, []) }, []);
``` ```
**Rule:** disposal is ownership-based, not automatic and not blind. **Rule:** disposal is ownership-based, not automatic and not blind.
@@ -443,29 +451,29 @@ Do not scatter debug checks across the codebase.
```ts ```ts
// utils/Debug.ts // utils/Debug.ts
import GUI from 'lil-gui' import GUI from "lil-gui";
export class Debug { export class Debug {
private static _instance: Debug | null = null private static _instance: Debug | null = null;
readonly active: boolean readonly active: boolean;
gui: GUI | null = null gui: GUI | null = null;
static getInstance(): Debug { static getInstance(): Debug {
if (!Debug._instance) Debug._instance = new Debug() if (!Debug._instance) Debug._instance = new Debug();
return Debug._instance return Debug._instance;
} }
private constructor() { private constructor() {
this.active = new URLSearchParams(window.location.search).has('debug') this.active = new URLSearchParams(window.location.search).has("debug");
if (this.active) { if (this.active) {
this.gui = new GUI({ title: 'La-Fabrik Debug' }) this.gui = new GUI({ title: "La-Fabrik Debug" });
} }
} }
destroy(): void { destroy(): void {
this.gui?.destroy() this.gui?.destroy();
Debug._instance = null Debug._instance = null;
} }
} }
``` ```
@@ -473,26 +481,25 @@ export class Debug {
Usage: Usage:
```ts ```ts
const debug = Debug.getInstance() const debug = Debug.getInstance();
if (debug.active) { if (debug.active) {
debug.gui!.add(params, 'bloomIntensity', 0, 3).name('Bloom') debug.gui!.add(params, "bloomIntensity", 0, 3).name("Bloom");
} }
``` ```
## 🚀 Getting Started ## 🚀 Getting Started
```bash ```bash
git clone https://github.com/La-Fabrik-Durable/La-Fabrik.git git clone https://github.com/La-Fabrik-Durable/La-Fabrik.git
cd La-Fabrik cd La-Fabrik
npm install npm install
npm run dev npm run dev
``` ```
Open `http://localhost:5173` — standard experience. Open `http://localhost:5173` — standard experience.
Open `http://localhost:5173?debug` — debug panel + r3f-perf overlay. Open `http://localhost:5173?debug` — debug panel + r3f-perf overlay.
## 📜 License ## 📜 License
See [LICENSE](./LICENSE) file. See [LICENSE](./LICENSE) file.
+9 -9
View File
@@ -1,14 +1,14 @@
import js from '@eslint/js' import js from "@eslint/js";
import globals from 'globals' import globals from "globals";
import reactHooks from 'eslint-plugin-react-hooks' import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from 'eslint-plugin-react-refresh' import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from 'typescript-eslint' import tseslint from "typescript-eslint";
import { defineConfig, globalIgnores } from 'eslint/config' import { defineConfig, globalIgnores } from "eslint/config";
export default defineConfig([ export default defineConfig([
globalIgnores(['dist']), globalIgnores(["dist"]),
{ {
files: ['**/*.{ts,tsx}'], files: ["**/*.{ts,tsx}"],
extends: [ extends: [
js.configs.recommended, js.configs.recommended,
tseslint.configs.recommended, tseslint.configs.recommended,
@@ -20,4 +20,4 @@ export default defineConfig([
globals: globals.browser, globals: globals.browser,
}, },
}, },
]) ]);
+1 -1
View File
@@ -10,4 +10,4 @@
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
</body> </body>
</html> </html>
+1215 -79
View File
File diff suppressed because it is too large Load Diff
+18 -3
View File
@@ -1,17 +1,28 @@
{ {
"name": "la-fabrik", "name": "la-fabrik",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview" "lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"preview": "vite preview",
"typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.6.0",
"@react-three/postprocessing": "^3.0.4",
"@react-three/rapier": "^2.2.0",
"gsap": "^3.15.0",
"r3f-perf": "^7.2.3",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4" "react-dom": "^19.2.4",
"three": "^0.183.2"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.39.4", "@eslint/js": "^9.39.4",
@@ -20,9 +31,13 @@
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4", "eslint": "^9.39.4",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2", "eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0", "globals": "^17.4.0",
"lil-gui": "^0.21.0",
"prettier": "^3.8.2",
"typescript": "~6.0.2", "typescript": "~6.0.2",
"typescript-eslint": "^8.58.0", "typescript-eslint": "^8.58.0",
"vite": "^8.0.4" "vite": "^8.0.4"
+1 -1
View File
@@ -1 +1 @@
# public/models/farm/* # public/models/farm/\*
+1 -1
View File
@@ -1 +1 @@
# public/models/map/* # public/models/map/\*
+1 -1
View File
@@ -1 +1 @@
# public/models/powerGrid/* # public/models/powerGrid/\*
+1 -1
View File
@@ -1 +1 @@
# public/sounds/* # public/sounds/\*
+1 -1
View File
@@ -1 +1 @@
# public/textures/* # public/textures/\*
+1 -1
View File
@@ -167,7 +167,7 @@
&::before, &::before,
&::after { &::after {
content: ''; content: "";
position: absolute; position: absolute;
top: -4.5px; top: -4.5px;
border: 5px solid transparent; border: 5px solid transparent;
+8 -8
View File
@@ -1,11 +1,11 @@
import { useState } from 'react' import { useState } from "react";
import reactLogo from './assets/react.svg' import reactLogo from "./assets/react.svg";
import viteLogo from './assets/vite.svg' import viteLogo from "./assets/vite.svg";
import heroImg from './assets/hero.png' import heroImg from "./assets/hero.png";
import './App.css' import "./App.css";
function App() { function App() {
const [count, setCount] = useState(0) const [count, setCount] = useState(0);
return ( return (
<> <>
@@ -115,7 +115,7 @@ function App() {
<div className="ticks"></div> <div className="ticks"></div>
<section id="spacer"></section> <section id="spacer"></section>
</> </>
) );
} }
export default App export default App;
+1 -1
View File
@@ -1 +1 @@
// src/components/3d/InteractiveObject.tsx // src/components/3d/InteractiveObject.tsx
+1 -1
View File
@@ -1 +1 @@
// src/components/ui/CinematicBars.tsx // src/components/ui/CinematicBars.tsx
+1 -1
View File
@@ -1 +1 @@
// src/components/ui/LoadingScreen.tsx // src/components/ui/LoadingScreen.tsx
+1 -1
View File
@@ -1 +1 @@
// src/components/ui/MapHUD.tsx // src/components/ui/MapHUD.tsx
+1 -1
View File
@@ -1 +1 @@
// src/components/ui/MissionHUD.tsx // src/components/ui/MissionHUD.tsx
+1 -1
View File
@@ -1 +1 @@
// src/components/ui/NarrativeOverlay.tsx // src/components/ui/NarrativeOverlay.tsx
+1 -1
View File
@@ -1 +1 @@
// src/data/dialogues.ts // src/data/dialogues.ts
+1 -1
View File
@@ -1 +1 @@
// src/data/missions.ts // src/data/missions.ts
+1 -1
View File
@@ -1 +1 @@
// src/data/zones.ts // src/data/zones.ts
+1 -1
View File
@@ -1 +1 @@
// src/hooks/useAudio.ts // src/hooks/useAudio.ts
+1 -1
View File
@@ -1 +1 @@
// src/hooks/useCinematic.ts // src/hooks/useCinematic.ts
+1 -1
View File
@@ -1 +1 @@
// src/hooks/useGameState.ts // src/hooks/useGameState.ts
+1 -1
View File
@@ -1 +1 @@
// src/hooks/useInteraction.ts // src/hooks/useInteraction.ts
+1 -1
View File
@@ -1 +1 @@
// src/hooks/useLOD.ts // src/hooks/useLOD.ts
+1 -1
View File
@@ -1 +1 @@
// src/hooks/useZoneDetection.ts // src/hooks/useZoneDetection.ts
+2 -2
View File
@@ -11,8 +11,8 @@
--shadow: --shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif; --sans: system-ui, "Segoe UI", Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif; --heading: system-ui, "Segoe UI", Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace; --mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans); font: 18px/145% var(--sans);
+6 -6
View File
@@ -1,10 +1,10 @@
import { StrictMode } from 'react' import { StrictMode } from "react";
import { createRoot } from 'react-dom/client' import { createRoot } from "react-dom/client";
import './index.css' import "./index.css";
import App from './App.tsx' import App from "./App.tsx";
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<App /> <App />
</StrictMode>, </StrictMode>,
) );
+1 -1
View File
@@ -1 +1 @@
// src/stateManager/AudioManager.ts // src/stateManager/AudioManager.ts
+1 -1
View File
@@ -1 +1 @@
// src/stateManager/CinematicManager.ts // src/stateManager/CinematicManager.ts
+1 -1
View File
@@ -1 +1 @@
// src/stateManager/GameManager.ts // src/stateManager/GameManager.ts
+1 -1
View File
@@ -1 +1 @@
// src/stateManager/ZoneManager.ts // src/stateManager/ZoneManager.ts
+1 -1
View File
@@ -1 +1 @@
// src/utils/Debug.ts // src/utils/Debug.ts
+11
View File
@@ -0,0 +1,11 @@
import { Suspense, lazy } from 'react'
const Perf = lazy(() => import('r3f-perf').then((m) => ({ default: m.Perf })))
export function DebugPerf() {
const debug = new URLSearchParams(window.location.search).has('debug')
if (!debug) return null
return (
<Suspense fallback={null}>
<Perf position="top-left" />
</Suspense>
)
}
+1 -1
View File
@@ -1 +1 @@
// src/utils/Dispose.ts // src/utils/Dispose.ts
+1 -1
View File
@@ -1 +1 @@
// src/utils/EventEmitter.ts // src/utils/EventEmitter.ts
+1 -1
View File
@@ -1 +1 @@
// src/utils/Sizes.ts // src/utils/Sizes.ts
+1 -1
View File
@@ -1 +1 @@
// src/utils/Time.ts // src/utils/Time.ts
+1 -1
View File
@@ -1 +1 @@
// src/world/Environment.tsx // src/world/Environment.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/Lighting.tsx // src/world/Lighting.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/Map.tsx // src/world/Map.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/PostFX.tsx // src/world/PostFX.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/player/Crosshair.tsx // src/world/player/Crosshair.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/player/FPSController.tsx // src/world/player/FPSController.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/zones/FarmZone.tsx // src/world/zones/FarmZone.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/zones/PowerGridZone.tsx // src/world/zones/PowerGridZone.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/zones/ResidentialZone.tsx // src/world/zones/ResidentialZone.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/zones/SchoolZone.tsx // src/world/zones/SchoolZone.tsx
+1 -1
View File
@@ -1 +1 @@
// src/world/zones/WorkshopZone.tsx // src/world/zones/WorkshopZone.tsx
+3 -3
View File
@@ -1,7 +1,7 @@
import { defineConfig } from 'vite' import { defineConfig } from "vite";
import react from '@vitejs/plugin-react' import react from "@vitejs/plugin-react";
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
}) });