+
+
+ {
+ state.game
+ ? <>
+
+ {
+ state.game!.isPaused
+ ?
+ :
+ }
+ >
+ :
+ }
+
)
});
diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx
index 95afdac..faa61d1 100644
--- a/src/components/Toolbar.tsx
+++ b/src/components/Toolbar.tsx
@@ -14,18 +14,6 @@ export const Toolbar = observer(function () {
}
return
- {state.worldEditor.isEnabled &&
EDITOR MODE
}
- {state.world.isPlaying
- ? <>
-
- {
- (state.world.data.state as RunningGameState).paused
- ?
- :
- }
- >
- :
- }
diff --git a/src/components/WorldView.tsx b/src/components/WorldView.tsx
deleted file mode 100644
index 9c5e362..0000000
--- a/src/components/WorldView.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { useLayoutEffect, useRef } from 'react';
-import type React from 'react';
-import { useFrame, useThree } from '@react-three/fiber';
-import { OrbitControls } from '@react-three/drei';
-import { observer } from 'mobx-react-lite';
-import { state } from '../state';
-import { SceneView } from './SceneView';
-
-const CameraSync = observer(function () {
- const { camera } = useThree();
- const cam = state.world.currentCamera; // MobX tracks this; re-renders on change
-
- useLayoutEffect(() => {
- camera.position.set(cam.position[0], cam.position[1], cam.position[2]);
- camera.rotation.set(cam.look[0], cam.look[1], cam.look[2]);
- camera.updateProjectionMatrix();
- });
-
- return null;
-});
-
-export const WorldView = observer(function () {
- const world = state.world;
- const controlsRef = useRef
>(null);
-
- useFrame((_, delta) => {
- world.tick(delta);
- });
-
- const handleEnd = () => {
- const controls = controlsRef.current;
- if (!controls || world.isPlaying)
- return;
- const [x, y, z] = controls.object.rotation.toArray();
- state.worldEditor.setCamera({
- position: controls.object.position.toArray(),
- look: [x, y, z],
- });
- };
-
- return (<>
-
-
-
- >)
-});
diff --git a/src/index.css b/src/index.scss
similarity index 67%
rename from src/index.css
rename to src/index.scss
index 5fb3313..b02ea1a 100644
--- a/src/index.css
+++ b/src/index.scss
@@ -15,7 +15,8 @@
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
- font: 18px/145% var(--sans);
+ font: 16px/100% var(--sans);
+ line-height: 1;
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
@@ -24,10 +25,6 @@
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-
- @media (max-width: 1024px) {
- font-size: 16px;
- }
}
@media (prefers-color-scheme: dark) {
@@ -66,46 +63,17 @@ body {
margin: 0;
}
-h1,
-h2 {
- font-family: var(--heading);
- font-weight: 500;
- color: var(--text-h);
+svg {
+ fill: currentColor;
+ display: inline-block;
+ height: 1em;
+ top: 0.125em;
+ vertical-align: bottom;
}
-h1 {
- font-size: 56px;
- letter-spacing: -1.68px;
- margin: 32px 0;
- @media (max-width: 1024px) {
- font-size: 36px;
- margin: 20px 0;
- }
-}
-h2 {
- font-size: 24px;
- line-height: 118%;
- letter-spacing: -0.24px;
- margin: 0 0 8px;
- @media (max-width: 1024px) {
- font-size: 20px;
- }
-}
-p {
- margin: 0;
-}
-
-code,
-.counter {
- font-family: var(--mono);
- display: inline-flex;
- border-radius: 4px;
- color: var(--text-h);
-}
-
-code {
- font-size: 15px;
- line-height: 135%;
- padding: 4px 8px;
- background: var(--code-bg);
-}
+button,
+input,
+select,
+textarea {
+ font: inherit;
+}
\ No newline at end of file
diff --git a/src/main.tsx b/src/main.tsx
index ee85237..0d14946 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,7 +1,7 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { App } from './App.tsx'
-import './index.css'
+import './index.scss'
createRoot(document.getElementById('root')!).render(
diff --git a/src/state/characterState.ts b/src/state/characterState.ts
deleted file mode 100644
index ebfdb0c..0000000
--- a/src/state/characterState.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { makeAutoObservable } from "mobx";
-import type { WorldState } from "./worldState";
-import type { CameraProps } from "@react-three/fiber";
-
-export class CharacterState {
- private readonly world: WorldState;
-
- constructor(world: WorldState) {
- this.world = world;
- makeAutoObservable(
- this,
- {
- },
- );
- }
-
- public get camera(): CameraProps {
- const cam = this.world.currentCamera;
- return {
- position: cam.position,
- fov: 50,
- rotation: cam.look,
- }
- }
-}
\ No newline at end of file
diff --git a/src/state/gameState.ts b/src/state/gameState.ts
new file mode 100644
index 0000000..311511e
--- /dev/null
+++ b/src/state/gameState.ts
@@ -0,0 +1,61 @@
+import { makeAutoObservable } from "mobx";
+import type { WorldState } from "./worldState";
+import type { RunningGameState, Scene } from "../types";
+import { clone } from "../utils";
+import { state } from "./rootState";
+import type { Pos3 } from "../types/3d";
+import type { CameraProps } from "@react-three/fiber";
+
+export class GameState {
+ private readonly world: WorldState;
+
+ constructor(world: WorldState) {
+ this.world = world;
+ makeAutoObservable(this);
+ }
+
+ public get state(): RunningGameState {
+ return this.world.data.state as RunningGameState;
+ }
+
+ public get isPaused(): boolean {
+ return this.state.paused;
+ }
+
+ public get scene(): Scene {
+ return this.state.scene;
+ }
+
+ public get camera(): Pos3 {
+ return this.scene.character;
+ }
+
+ public get cameraAsThree(): CameraProps {
+ const cam = this.camera;
+ return {
+ position: cam.position,
+ fov: 50,
+ rotation: cam.look,
+ }
+ }
+
+ public resume(): void {
+ const state = clone(this.world.data.state) as RunningGameState;
+ state.paused = false;
+ this.world.data.state = state;
+ }
+
+ public pause(): void {
+ const state = clone(this.world.data.state) as RunningGameState;
+ state.paused = true;
+ this.world.data.state = state;
+ }
+
+ public stop(): void {
+ this.world.data.state = { playing: false };
+ }
+
+ public tick(deltaTime: number): void {
+ //TODO
+ }
+}
\ No newline at end of file
diff --git a/src/state/rootState.ts b/src/state/rootState.ts
index cb44001..19ccfba 100644
--- a/src/state/rootState.ts
+++ b/src/state/rootState.ts
@@ -1,6 +1,7 @@
import { makeAutoObservable } from "mobx";
import { WorldState } from "./worldState";
import { WorldEditorState } from "./worldEditorState";
+import { GameState } from "./gameState";
export class RootState {
public readonly world = new WorldState();
@@ -16,6 +17,15 @@ export class RootState {
},
);
}
+
+ public get isGamePlaying(): boolean {
+ return this.world.data.state.playing;
+ }
+
+ public get game(): GameState | undefined {
+ if (this.isGamePlaying)
+ return new GameState(this.world);
+ }
}
export const state = new RootState();
diff --git a/src/state/worldEditorState.ts b/src/state/worldEditorState.ts
index c3a5a42..089ec51 100644
--- a/src/state/worldEditorState.ts
+++ b/src/state/worldEditorState.ts
@@ -4,6 +4,7 @@ import type { ObjectInstance, Scene, World } from "../types";
import { createObjectInstance } from "../utils/object";
import { randomId } from "../utils";
import type { Pos3, R3, V3 } from "../types/3d";
+import { state } from "./rootState";
export const SelectionEditModeEnum = [
'translate',
@@ -34,7 +35,7 @@ export class WorldEditorState {
}
public get isEnabled(): boolean {
- return !this.world.isPlaying;
+ return !state.isGamePlaying;
}
public setCamera(value: Pos3): void {
diff --git a/src/state/worldState.ts b/src/state/worldState.ts
index 6bde9ec..06dd023 100644
--- a/src/state/worldState.ts
+++ b/src/state/worldState.ts
@@ -1,19 +1,15 @@
import { makeAutoObservable } from "mobx";
import { WorldFactory } from "../model/worldFactory";
-import type { ObjectType, RunningGameState, Scene, World } from "../types";
-import { CharacterState } from "./characterState";
-import type { Pos3 } from "../types/3d";
-import { clone } from "../utils";
+import type { ObjectType, World } from "../types";
import type { VoxelType } from "../types/voxel";
import { state } from "./rootState";
import { DEFAULT_VOXEL_TYPES } from "../model/defaultVoxelTypes";
import { wolf } from "../model/objectPrefabs/wolf";
+import { clone } from "../utils";
export class WorldState {
public data: World = WorldFactory.create();
- public character = new CharacterState(this);
-
constructor() {
makeAutoObservable(this);
}
@@ -75,60 +71,6 @@ export class WorldState {
WorldFactory.save(this.data);
}
- public get isPlaying(): boolean {
- return this.data.state.playing;
- }
-
- public get currentScene(): Scene {
- return this.isPlaying
- ? (this.data.state as RunningGameState).scene
- : this.data.initialScene;
- }
-
- public get currentCamera(): Pos3 {
- return this.isPlaying
- ? (this.data.state as RunningGameState).scene.character
- : this.data.editorCamera;
- }
-
- public tick(_delta: number) {
- if (!this.isPlaying)
- return;
-
- //TODO
- }
-
- public play(): void {
- if (this.isPlaying) {
- const state = clone(this.data.state) as RunningGameState;
- state.paused = false;
- this.data.state = state;
- }
- else {
- this.data.state = {
- playing: true,
- paused: false,
- time: 0,
- scene: clone(this.data.initialScene),
- }
- }
- state.worldEditor.resetSelectedObject();
- }
-
- public pause(): void {
- if (!this.isPlaying)
- return;
- const state = clone(this.data.state) as RunningGameState;
- state.paused = true;
- this.data.state = state;
- }
-
- public stop(): void {
- if (!this.isPlaying)
- return;
- this.data.state = { playing: false };
- }
-
public getObjectTypeById(id: string): ObjectType | undefined {
return this.data.objectTypes[id];
}
@@ -136,4 +78,18 @@ export class WorldState {
public getVoxelTypeById(id: string): VoxelType | undefined {
return this.data.voxelTypes[id];
}
+
+ public play(): void {
+ if (state.game)
+ state.game.resume();
+ else {
+ this.data.state = {
+ playing: true,
+ paused: false,
+ time: 0,
+ scene: clone(this.data.initialScene),
+ }
+ state.worldEditor.resetSelectedObject();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/types/character.ts b/src/types/character.ts
new file mode 100644
index 0000000..000f940
--- /dev/null
+++ b/src/types/character.ts
@@ -0,0 +1,4 @@
+import type { Pos3 } from "./3d";
+
+export type Character = Pos3 & {
+}
diff --git a/src/types/characterState.ts b/src/types/characterState.ts
deleted file mode 100644
index 92f1bb2..0000000
--- a/src/types/characterState.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import type { Pos3 } from "./3d";
-
-export type CharacterState = Pos3 & {
-}
diff --git a/src/types/index.ts b/src/types/index.ts
index afede59..005ebad 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -3,7 +3,7 @@ export * from './scene';
export * from './world';
export * from './gameRules';
export * from './gameState';
-export * from './characterState';
+export * from './character';
diff --git a/src/types/scene.ts b/src/types/scene.ts
index 88b5c1f..afe918a 100644
--- a/src/types/scene.ts
+++ b/src/types/scene.ts
@@ -1,7 +1,7 @@
-import type { CharacterState } from "./characterState";
+import type { Character } from "./character";
import type { ObjectInstance } from "./object";
export type Scene = {
- character: CharacterState;
+ character: Character;
objects: Record;
}
\ No newline at end of file