linting
This commit is contained in:
parent
3bdde094f8
commit
88b36988ff
|
|
@ -19,13 +19,12 @@ export default defineConfig([
|
|||
globals: globals.browser,
|
||||
},
|
||||
rules: {
|
||||
"padding-line-between-statements": [
|
||||
"warn",
|
||||
{ blankLine: 'always', prev: '*', next: 'block' },
|
||||
{ blankLine: 'always', prev: 'block', next: '*' },
|
||||
{ blankLine: 'always', prev: '*', next: 'block-like' },
|
||||
{ blankLine: 'always', prev: 'block-like', next: '*' },
|
||||
]
|
||||
"semi": ["error", "always"],
|
||||
"nonblock-statement-body-position": ["error", "below"],
|
||||
"@typescript-eslint/no-unused-vars": ["error", {
|
||||
"varsIgnorePattern": "^_",
|
||||
"argsIgnorePattern": "^_",
|
||||
}],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
|
|
|||
35
src/App.tsx
35
src/App.tsx
|
|
@ -1,6 +1,6 @@
|
|||
import { BrowserRouter, Link, Outlet, Route, Routes, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { VoxelEditorPage } from './components/voxelEditor/VoxelEditorPage';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect, useLayoutEffect, useRef } from 'react';
|
||||
import { reaction } from 'mobx';
|
||||
import { ThreeView } from './components/ThreeView';
|
||||
import { state } from './state';
|
||||
|
|
@ -11,7 +11,7 @@ function StateToUrlSync() {
|
|||
const navigate = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
const pathnameRef = useRef(pathname);
|
||||
pathnameRef.current = pathname;
|
||||
useLayoutEffect(() => { pathnameRef.current = pathname; });
|
||||
|
||||
useEffect(() => reaction(
|
||||
() => ({
|
||||
|
|
@ -21,22 +21,28 @@ function StateToUrlSync() {
|
|||
}),
|
||||
({ isGame, selectedObjectId, selectedObjectTypeId }) => {
|
||||
let target: string;
|
||||
if (isGame) target = '/game';
|
||||
else if (selectedObjectId) target = `/editor/object/${selectedObjectId}`;
|
||||
else if (selectedObjectTypeId) target = `/editor/objectType/${selectedObjectTypeId}`;
|
||||
else target = '/editor';
|
||||
if (isGame)
|
||||
target = '/game';
|
||||
else if (selectedObjectId)
|
||||
target = `/editor/object/${selectedObjectId}`;
|
||||
else if (selectedObjectTypeId)
|
||||
target = `/editor/objectType/${selectedObjectTypeId}`;
|
||||
else
|
||||
target = '/editor';
|
||||
|
||||
const current = pathnameRef.current;
|
||||
if (current === target || current.startsWith(target + '/')) return;
|
||||
if (current === target || current.startsWith(target + '/'))
|
||||
return;
|
||||
navigate(target);
|
||||
},
|
||||
), []);
|
||||
), [navigate]);
|
||||
return null;
|
||||
}
|
||||
|
||||
function EditorRoute() {
|
||||
useEffect(() => {
|
||||
if (!!state.game) state.stopGame();
|
||||
if (state.game)
|
||||
state.stopGame();
|
||||
state.worldEditor.resetSelection();
|
||||
}, []);
|
||||
return null;
|
||||
|
|
@ -45,8 +51,9 @@ function EditorRoute() {
|
|||
function EditorObjectRoute() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
if (!!state.game)
|
||||
if (!id)
|
||||
return;
|
||||
if (state.game)
|
||||
state.stopGame();
|
||||
const editMode = state.worldEditor.selection?.type === 'object' && state.worldEditor.selection.id === id
|
||||
? state.worldEditor.selection.editMode ?? 'translate'
|
||||
|
|
@ -59,8 +66,10 @@ function EditorObjectRoute() {
|
|||
function EditorObjectTypeRoute() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
if (!!state.game) state.stopGame();
|
||||
if (!id)
|
||||
return;
|
||||
if (state.game)
|
||||
state.stopGame();
|
||||
const editMode = state.worldEditor.selection?.type === 'objectType' && state.worldEditor.selection.id === id
|
||||
? state.worldEditor.selection.editMode ?? 'scripts'
|
||||
: 'scripts';
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Blockly.Blocks['console_log_action'] = {
|
|||
.setAlign(Blockly.inputs.Align.RIGHT)
|
||||
// .setCheck('String')
|
||||
.appendField('print');
|
||||
this.setInputsInline(false)
|
||||
this.setInputsInline(false);
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setTooltip('Print value to console');
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Blockly.Blocks['physics_apply_impulse_action'] = {
|
|||
.appendField(new Blockly.FieldNumber(1, -10, 10, 0.01), 'FORCE');
|
||||
|
||||
|
||||
this.setInputsInline(true)
|
||||
this.setInputsInline(true);
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setTooltip('Push me in a direction');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import * as Blockly from "blockly";
|
||||
|
||||
export class ObjecTypeField extends Blockly.Field {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
constructor(value: any, validator: any) {
|
||||
super(value, validator);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Blockly.Blocks['current_object_value'] = {
|
|||
init(this: Blockly.Block) {
|
||||
this.appendEndRowInput('NAME')
|
||||
.appendField('Me');
|
||||
this.setInputsInline(false)
|
||||
this.setInputsInline(false);
|
||||
this.setOutput(true, 'Object');
|
||||
this.setTooltip('Returns current object instance');
|
||||
this.setColour(315);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Blockly.Blocks['current_object_type_value'] = {
|
|||
init(this: Blockly.Block) {
|
||||
this.appendEndRowInput('NAME')
|
||||
.appendField('My type');
|
||||
this.setInputsInline(false)
|
||||
this.setInputsInline(false);
|
||||
this.setOutput(true, 'ObjectType');
|
||||
this.setTooltip('Returns current object type');
|
||||
this.setColour(315);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Blockly.Blocks['object_by_id_value'] = {
|
|||
this.appendEndRowInput()
|
||||
.appendField('Object with id')
|
||||
.appendField(new Blockly.FieldTextInput(''), 'TARGET_ID');
|
||||
this.setInputsInline(false)
|
||||
this.setInputsInline(false);
|
||||
this.setOutput(true, 'Object');
|
||||
this.setTooltip('Returns object by id, if any');
|
||||
this.setColour(315);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Blockly.Blocks['player_object_value'] = {
|
|||
init(this: Blockly.Block) {
|
||||
this.appendEndRowInput()
|
||||
.appendField('Player');
|
||||
this.setInputsInline(false)
|
||||
this.setInputsInline(false);
|
||||
this.setOutput(true, 'Object');
|
||||
this.setTooltip('Returns object that is controlled by player');
|
||||
this.setColour(315);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export const PlayerObjectView = observer(function ({ object, objectType }: Playe
|
|||
shoulderOffset: new Vector3(W * 0.1, centerY + H * 0.3, bb.max[2] + radius),
|
||||
lookAtY: centerY,
|
||||
};
|
||||
}, [object.id]);
|
||||
}, [object.cache.boundingBox]);
|
||||
|
||||
const yawRef = useRef(0);
|
||||
const pitchRef = useRef(0);
|
||||
|
|
@ -69,9 +69,11 @@ export const PlayerObjectView = observer(function ({ object, objectType }: Playe
|
|||
|
||||
useEffect(() => {
|
||||
const canvas = gl.domElement;
|
||||
const onClick = () => { if (document.pointerLockElement !== canvas) canvas.requestPointerLock()?.catch(() => {}); };
|
||||
const onClick = () => { if (document.pointerLockElement !== canvas)
|
||||
canvas.requestPointerLock()?.catch(() => {}); };
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
if (document.pointerLockElement !== canvas) return;
|
||||
if (document.pointerLockElement !== canvas)
|
||||
return;
|
||||
mouseRef.current.x += e.movementX;
|
||||
mouseRef.current.y += e.movementY;
|
||||
};
|
||||
|
|
@ -85,12 +87,16 @@ export const PlayerObjectView = observer(function ({ object, objectType }: Playe
|
|||
|
||||
useBeforePhysicsStep((world) => {
|
||||
const rb = handleRef.current?.rb;
|
||||
if (!rb) return;
|
||||
if (!rb)
|
||||
return;
|
||||
const collider = rb.collider(0);
|
||||
if (!collider) return;
|
||||
if (!collider)
|
||||
return;
|
||||
const controller = controllerRef.current;
|
||||
if (!controller) return;
|
||||
if (state.game?.isPaused) return;
|
||||
if (!controller)
|
||||
return;
|
||||
if (state.game?.isPaused)
|
||||
return;
|
||||
|
||||
const dt = world.timestep;
|
||||
const yaw = yawRef.current;
|
||||
|
|
@ -128,7 +134,8 @@ export const PlayerObjectView = observer(function ({ object, objectType }: Playe
|
|||
|
||||
useFrame(({ camera }, delta) => {
|
||||
const rb = handleRef.current?.rb;
|
||||
if (!rb) return;
|
||||
if (!rb)
|
||||
return;
|
||||
|
||||
mouseRef.current.x += joystickValues.look.x * LOOK_RATE * delta;
|
||||
mouseRef.current.y += -joystickValues.look.y * LOOK_RATE * delta;
|
||||
|
|
|
|||
|
|
@ -24,5 +24,5 @@ export const LeftPanel = observer(function () {
|
|||
{/* <button onClick={handleLoadWorld}>Load</button> */}
|
||||
<button onClick={handleLoadMockWorld}>Load mock world</button>
|
||||
<div className="debug"><RenderInfoView /></div>
|
||||
</Panel>
|
||||
</Panel>;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,5 +26,5 @@ export const MainPanel = observer(function () {
|
|||
<div><span className="title">{objectType?.name}</span> ({objects.length} object{objects.length > 1 ? 's' : ''})</div>
|
||||
</header>
|
||||
<ScriptEditorView objectType={objectType} />
|
||||
</Panel>
|
||||
</Panel>;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const SelectionOverlay = observer(function ({ objectId, ref, onTransformEnd }: S
|
|||
[ref],
|
||||
);
|
||||
|
||||
useHelper(isSelected ? groupRef : { current: null } as any, BoxHelper, 'white');
|
||||
useHelper(isSelected ? groupRef : { current: null }, BoxHelper, 'white');
|
||||
|
||||
if (!isSelected || selectionMode === undefined || !groupRef.current)
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
|
|||
|
||||
useEffect(
|
||||
() => {
|
||||
if (props.isPlayer) return;
|
||||
if (props.isPlayer)
|
||||
return;
|
||||
return reaction(
|
||||
() => 'version' in object ? object.version : 0,
|
||||
() => {
|
||||
|
|
@ -53,19 +54,20 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
|
|||
},
|
||||
);
|
||||
},
|
||||
[object.id]
|
||||
[object, object.id, props.isPlayer]
|
||||
);
|
||||
|
||||
const gameObj = object as RuntimeGameObjectInstance;
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const gameObj = object as RuntimeGameObjectInstance;
|
||||
|
||||
return reaction(
|
||||
() => gameObj.pendingActions.impulse,
|
||||
(impulse) => {
|
||||
if (!impulse || !rbRef.current)
|
||||
return;
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { direction, amplitude } = impulse;
|
||||
amplitude *= 100;
|
||||
const v = { x: direction[0] * amplitude, y: direction[1] * amplitude, z: direction[2] * amplitude };
|
||||
|
|
@ -76,7 +78,7 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
|
|||
},
|
||||
);
|
||||
},
|
||||
[object.id]
|
||||
[gameObj.pendingActions, object.id]
|
||||
);
|
||||
|
||||
function handleClick(e: ThreeEvent<MouseEvent>) {
|
||||
|
|
@ -126,4 +128,4 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
|
|||
</group>
|
||||
</SyncRigidBody>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ export type PanelProps = {
|
|||
export const Panel = observer(function ({ children, side = 'left' }: PanelProps) {
|
||||
return <div className={`panel ${side}`}>
|
||||
{children}
|
||||
</div >
|
||||
</div >;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@ export const Panels = observer(function () {
|
|||
return <div className="overlay-panels">
|
||||
<LeftPanel />
|
||||
<MainPanel />
|
||||
</div>
|
||||
</div>;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ export const RenderInfoView = observer(function () {
|
|||
|
||||
useEffect(() => {
|
||||
const el = containerRef.current;
|
||||
if (!el) return;
|
||||
if (!el)
|
||||
return;
|
||||
el.appendChild(chartRef.current);
|
||||
return () => { el.removeChild(chartRef.current); };
|
||||
}, []);
|
||||
|
|
@ -25,5 +26,5 @@ export const RenderInfoView = observer(function () {
|
|||
<span>textures: {info.textures}</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</>;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { KeyboardControls, Stats } from '@react-three/drei';
|
||||
import { action } from 'mobx';
|
||||
import { chartRef } from './chartRef';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { state } from '../state';
|
||||
|
|
@ -9,23 +8,7 @@ import { SceneEditorView } from './SceneEditorView';
|
|||
import { JoystickView } from './JoystickView';
|
||||
import type { RefObject } from 'react';
|
||||
import { IconPlayerPlayFilled, IconPlayerPauseFilled, IconPlayerStopFilled } from '@tabler/icons-react';
|
||||
|
||||
function RenderInfoUpdater() {
|
||||
const { gl } = useThree();
|
||||
useFrame(action(() => {
|
||||
const { calls, triangles } = gl.info.render;
|
||||
const shaders = gl.info.programs?.length ?? 0;
|
||||
const { geometries, textures } = gl.info.memory;
|
||||
state.setRenderInfo({
|
||||
calls,
|
||||
triangles,
|
||||
shaders,
|
||||
geometries,
|
||||
textures,
|
||||
});
|
||||
}));
|
||||
return null;
|
||||
}
|
||||
import { RenderInfoUpdater } from './renderInfo';
|
||||
|
||||
export const ThreeView = observer(function () {
|
||||
const isGame = !!state.game;
|
||||
|
|
@ -63,5 +46,5 @@ export const ThreeView = observer(function () {
|
|||
</div>
|
||||
</div>
|
||||
</KeyboardControls>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { state } from "../state";
|
||||
import { action } from "mobx";
|
||||
|
||||
export function RenderInfoUpdater() {
|
||||
const { gl } = useThree();
|
||||
useFrame(action(() => {
|
||||
const { calls, triangles } = gl.info.render;
|
||||
const shaders = gl.info.programs?.length ?? 0;
|
||||
const { geometries, textures } = gl.info.memory;
|
||||
state.setRenderInfo({
|
||||
calls,
|
||||
triangles,
|
||||
shaders,
|
||||
geometries,
|
||||
textures,
|
||||
});
|
||||
}));
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
import { useEffect, useLayoutEffect, useRef } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { runInAction } from "mobx";
|
||||
import * as Blockly from "blockly";
|
||||
|
|
@ -68,10 +68,11 @@ export const ScriptEditorView = observer(function ({ objectType }: ScriptEditorV
|
|||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const workspaceRef = useRef<Blockly.WorkspaceSvg | null>(null);
|
||||
const objectTypeRef = useRef(objectType);
|
||||
objectTypeRef.current = objectType;
|
||||
useLayoutEffect(() => { objectTypeRef.current = objectType; });
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
if (!containerRef.current)
|
||||
return;
|
||||
|
||||
const workspace = Blockly.inject(containerRef.current, { toolbox: TOOLBOX, theme: gameTheme });
|
||||
workspaceRef.current = workspace;
|
||||
|
|
@ -116,11 +117,12 @@ export const ScriptEditorView = observer(function ({ objectType }: ScriptEditorV
|
|||
workspace.dispose();
|
||||
workspaceRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
}, [objectType.program]);
|
||||
|
||||
useEffect(() => {
|
||||
const workspace = workspaceRef.current;
|
||||
if (!workspace) return;
|
||||
if (!workspace)
|
||||
return;
|
||||
|
||||
try {
|
||||
Blockly.Events.disable();
|
||||
|
|
@ -132,7 +134,7 @@ export const ScriptEditorView = observer(function ({ objectType }: ScriptEditorV
|
|||
} finally {
|
||||
Blockly.Events.enable();
|
||||
}
|
||||
}, [objectType.id]);
|
||||
}, [objectType.id, objectType.program]);
|
||||
|
||||
return <div className="script-editor">
|
||||
<div ref={containerRef} className="blockly-container" />
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ export const VoxelEditorPage = observer(function () {
|
|||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (id) state.worldEditor.refreshObjectTypeCaches(id);
|
||||
if (id)
|
||||
state.worldEditor.refreshObjectTypeCaches(id);
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
|
|
|
|||
10
src/main.tsx
10
src/main.tsx
|
|
@ -1,10 +1,10 @@
|
|||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { App } from './App.tsx'
|
||||
import './index.scss'
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { App } from './App.tsx';
|
||||
import './index.scss';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ export class GameScheduler {
|
|||
tick(deltaTime: number): void {
|
||||
const next: Coroutine[] = [];
|
||||
for (const co of this.coroutines) {
|
||||
if (this.isReady(co)) this.step(co, next);
|
||||
else next.push(this.countdown(co, deltaTime));
|
||||
if (this.isReady(co))
|
||||
this.step(co, next);
|
||||
else
|
||||
next.push(this.countdown(co, deltaTime));
|
||||
}
|
||||
this.coroutines = next;
|
||||
}
|
||||
|
|
@ -47,7 +49,8 @@ export class GameScheduler {
|
|||
|
||||
private step(co: Coroutine, list: Coroutine[]): void {
|
||||
const result = co.gen.next();
|
||||
if (!result.done) list.push(this.make(co.gen, result.value));
|
||||
if (!result.done)
|
||||
list.push(this.make(co.gen, result.value));
|
||||
}
|
||||
|
||||
private make(gen: Generator<YieldCommand, void, void>, cmd: YieldCommand): Coroutine {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,6 @@ export const DEFAULT_VOXEL_TYPES = {
|
|||
[dirt.id]: dirt,
|
||||
[water.id]: water,
|
||||
[glass.id]: glass,
|
||||
}
|
||||
};
|
||||
|
||||
export const DEFAULT_VOXEL_TYPE = stone;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { Game, RuntimeGameScene, RuntimeScene, World } from "../types";
|
||||
import type { GameEventContext, GameEventHandler, InternalGameEventBus } from "../types/runtime/gameBus";
|
||||
import type { GameEventContext, GameEventHandler, GeneratorEventHandler, InternalGameEventBus } from "../types/runtime/gameBus";
|
||||
import { clone } from "../utils";
|
||||
import { populateRuntimeScene } from "../utils/runtime";
|
||||
import { ObjectApi } from "../utils/runtime/objectApi";
|
||||
import type { GameScheduler, YieldCommand } from "./GameScheduler";
|
||||
import type { GameScheduler, YieldCommand } from "./gameScheduler";
|
||||
|
||||
export class GameEventBus {
|
||||
private handlers = new Map<string, GameEventHandler[]>();
|
||||
|
|
@ -70,7 +70,7 @@ export class GameFactory {
|
|||
scheduler: GameScheduler,
|
||||
): void {
|
||||
|
||||
const handlerWrappers = new WeakMap<Function, (ctx: GameEventContext) => void>();
|
||||
const handlerWrappers = new WeakMap<GeneratorEventHandler, GameEventHandler>();
|
||||
|
||||
const wait = (seconds: number): YieldCommand => ({ type: 'waitSeconds', seconds });
|
||||
const waitFrames = (frames: number): YieldCommand => ({ type: 'waitFrames', frames });
|
||||
|
|
@ -102,11 +102,11 @@ export class GameFactory {
|
|||
.forEach((o) => {
|
||||
const api = new ObjectApi(o, ot, world, scene);
|
||||
gameScript({ ...internalBus, wait, waitFrames, waitUntil }, { api, object: o, objectType: ot });
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
console.log('Error running game script:\n' + err)
|
||||
console.log('Error running game script:\n' + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { WorldState } from "./worldState";
|
|||
import type { Game, Pos3, RuntimeGameScene } from "../types";
|
||||
import type { CameraProps } from "@react-three/fiber";
|
||||
import { GameEventBus, GameFactory } from "../model/gameFactory";
|
||||
import { GameScheduler } from "../model/GameScheduler";
|
||||
import { GameScheduler } from "../model/gameScheduler";
|
||||
import type { GameEventContext } from "../types/runtime/gameBus";
|
||||
|
||||
export class GameState {
|
||||
|
|
@ -25,8 +25,7 @@ export class GameState {
|
|||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// @ts-ignore
|
||||
// @ts-expect-error -- for future use
|
||||
private withoutAutoSave(fn: () => void) {
|
||||
this._stopAutoSave();
|
||||
fn();
|
||||
|
|
@ -72,7 +71,7 @@ export class GameState {
|
|||
paused: toJS(this.isPaused),
|
||||
time: toJS(this.time),
|
||||
scene: toJS(this.scene),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public setGame(value: Game) {
|
||||
|
|
@ -95,7 +94,7 @@ export class GameState {
|
|||
position: cam.position,
|
||||
fov: 50,
|
||||
rotation: cam.look,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// public load() {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export class MenuState {
|
|||
</>,
|
||||
className: isPlayer ? 'player-controlled-object' : undefined,
|
||||
actions,
|
||||
onClick: () => { state.worldEditor.setSelectedObject({ id: o.id, editMode: 'translate' }) },
|
||||
onClick: () => { state.worldEditor.setSelectedObject({ id: o.id, editMode: 'translate' }); },
|
||||
selected: () => state.worldEditor.selection?.type === 'object' && state.worldEditor.selection?.id === o.id,
|
||||
} as MenuNode;
|
||||
});
|
||||
|
|
@ -89,10 +89,10 @@ export class MenuState {
|
|||
onClick: () => { editor.deleteObjectType(ot.id); },
|
||||
},
|
||||
],
|
||||
onClick: () => { editor.setSelectedObjectType({ id: ot.id, editMode: 'scripts' }) },
|
||||
onClick: () => { editor.setSelectedObjectType({ id: ot.id, editMode: 'scripts' }); },
|
||||
selected: () => editor.selection?.type === 'objectType' && editor.selection?.id === ot.id,
|
||||
children,
|
||||
} as MenuNode
|
||||
} as MenuNode;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ export class MenuState {
|
|||
{
|
||||
id: 'editor-scene-menu',
|
||||
title: 'Scene',
|
||||
onClick: () => { state.worldEditor.resetSelection() },
|
||||
onClick: () => { state.worldEditor.resetSelection(); },
|
||||
selected: () => !state.worldEditor.selection,
|
||||
children: this.editorObjectTypesMenu,
|
||||
actions: [{
|
||||
|
|
@ -111,13 +111,13 @@ export class MenuState {
|
|||
onClick: () => { state.worldEditor.addObjectType(); },
|
||||
}],
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public get nodes(): MenuNode[] {
|
||||
return [
|
||||
...this.editorMenu,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public nodeContainsSelected(node: MenuNode): boolean {
|
||||
|
|
|
|||
|
|
@ -165,33 +165,39 @@ export class WorldEditorState {
|
|||
|
||||
public addVoxelToObjectType(typeId: string, voxel: Voxel): void {
|
||||
const objectType = this.world.getObjectTypeById(typeId);
|
||||
if (!objectType) return;
|
||||
if (!objectType)
|
||||
return;
|
||||
const occupied = objectType.voxels.some(v =>
|
||||
v.position[0] === voxel.position[0] &&
|
||||
v.position[1] === voxel.position[1] &&
|
||||
v.position[2] === voxel.position[2]
|
||||
);
|
||||
if (!occupied) objectType.voxels.push(voxel);
|
||||
if (!occupied)
|
||||
objectType.voxels.push(voxel);
|
||||
}
|
||||
|
||||
public removeVoxelFromObjectType(typeId: string, position: V3): void {
|
||||
const objectType = this.world.getObjectTypeById(typeId);
|
||||
if (!objectType) return;
|
||||
if (!objectType)
|
||||
return;
|
||||
const idx = objectType.voxels.findIndex(v =>
|
||||
v.position[0] === position[0] &&
|
||||
v.position[1] === position[1] &&
|
||||
v.position[2] === position[2]
|
||||
);
|
||||
if (idx !== -1) objectType.voxels.splice(idx, 1);
|
||||
if (idx !== -1)
|
||||
objectType.voxels.splice(idx, 1);
|
||||
}
|
||||
|
||||
public refreshObjectTypeCaches(typeId: string): void {
|
||||
const objectType = this.world.getObjectTypeById(typeId);
|
||||
if (!objectType) return;
|
||||
if (!objectType)
|
||||
return;
|
||||
const world = this.world.data;
|
||||
const now = Date.now();
|
||||
for (const obj of Object.values(this.scene.objects)) {
|
||||
if (obj.typeId !== typeId) continue;
|
||||
if (obj.typeId !== typeId)
|
||||
continue;
|
||||
obj.cache.voxelGroups = getObjectVoxelGroups(objectType, world.voxelTypes);
|
||||
obj.cache.colliderMesh = buildObjectTrimesh(objectType, world.voxelTypes);
|
||||
obj.cache.boundingBox = computeBoundingBox(objectType);
|
||||
|
|
@ -207,7 +213,7 @@ export class WorldEditorState {
|
|||
if (this.scene.objects[id].typeId === typeId)
|
||||
this.deleteObject(id);
|
||||
|
||||
delete (this.world.data.objectTypes[typeId])
|
||||
delete (this.world.data.objectTypes[typeId]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export class WorldState {
|
|||
} as ObjectInstance));
|
||||
const objectMap = Object.fromEntries(
|
||||
objects.map((obj) => [obj.id, obj]),
|
||||
) as Record<string, ObjectInstance>
|
||||
) as Record<string, ObjectInstance>;
|
||||
|
||||
this.data = populateRuntimeWorld({
|
||||
objectTypes: {
|
||||
|
|
@ -106,7 +106,7 @@ export class WorldState {
|
|||
private saveData(data: World): void {
|
||||
console.log('Saving world...');
|
||||
const stack = new Error('Saving world...').stack!.split('\n').slice(1);
|
||||
const { voxelTypes, initialScene, ...debug } = toJS(data);
|
||||
const { voxelTypes: _vts, initialScene: _is, ...debug } = toJS(data);
|
||||
console.dir({ stack, debug });
|
||||
WorldFactory.save(toJS(this.data));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ export function v3toRapier(value: V3): Vector3Object {
|
|||
x: value[0],
|
||||
y: value[1],
|
||||
z: value[2],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,6 @@ export function populateRuntimeObject(object: ObjectInstance, world: World): Run
|
|||
}
|
||||
|
||||
export function depopulateRuntimeObject(object: RuntimeObjectInstance, _world: World): ObjectInstance {
|
||||
const { cache, ...result } = object;
|
||||
const { cache: _cache, ...result } = object;
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ export class ObjectApi {
|
|||
const dy = targetPosition[1] - py;
|
||||
const dz = targetPosition[2] - pz;
|
||||
const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len < 1e-6) return;
|
||||
if (len < 1e-6)
|
||||
return;
|
||||
const direction: V3 = [dx / len, dy / len, dz / len];
|
||||
this.object.pendingActions.impulse = { direction, amplitude };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ export function populateRuntimeScene(scene: Scene, world: World): RuntimeScene {
|
|||
return {
|
||||
...scene,
|
||||
objects: Object.fromEntries(Object.entries(scene.objects).map(([id, obj]) => [id, populateRuntimeObject(obj, world)])),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function depopulateRuntimeScene(scene: RuntimeScene, world: World): Scene {
|
||||
return {
|
||||
...scene,
|
||||
objects: Object.fromEntries(Object.entries(scene.objects).map(([id, obj]) => [id, depopulateRuntimeObject(obj, world)])),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue