136 lines
3.4 KiB
TypeScript
136 lines
3.4 KiB
TypeScript
import { makeAutoObservable, reaction, toJS } from "mobx";
|
|
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 type { GameEventContext } from "../types/runtime/gameBus";
|
|
|
|
export class GameState {
|
|
private readonly world: WorldState;
|
|
|
|
public isPaused: boolean = false;
|
|
public time: number = 0;
|
|
public isFirstTick: boolean;
|
|
public scene: RuntimeGameScene;
|
|
|
|
private eventBus: GameEventBus;
|
|
|
|
private startAutoSave() {
|
|
return reaction(
|
|
() => (this.asGame),
|
|
(data) => this.saveData(data),
|
|
{ delay: 5000 }
|
|
);
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
// @ts-ignore
|
|
private withoutAutoSave(fn: () => void) {
|
|
this._stopAutoSave();
|
|
fn();
|
|
this._stopAutoSave = this.startAutoSave();
|
|
}
|
|
|
|
private _stopAutoSave: () => void = () => { };
|
|
|
|
constructor(world: WorldState) {
|
|
this.world = world;
|
|
|
|
const rawWorld = toJS(this.world.data);
|
|
|
|
const game = GameFactory.create(rawWorld);
|
|
this.isPaused = game.paused;
|
|
this.time = game.time;
|
|
this.isFirstTick = true;
|
|
this.scene = game.scene;
|
|
|
|
const eventBus = new GameEventBus();
|
|
this.eventBus = eventBus;
|
|
|
|
this._stopAutoSave = this.startAutoSave();
|
|
makeAutoObservable(this);
|
|
|
|
GameFactory.evalGameScripts(rawWorld, this.scene, eventBus);
|
|
}
|
|
|
|
public emitEvent(event: string, context: Omit<GameEventContext, 'world' | 'scene'> = {}) {
|
|
this.eventBus.emit(
|
|
event,
|
|
{
|
|
...context,
|
|
world: this.world.data,
|
|
scene: this.scene,
|
|
},
|
|
);
|
|
}
|
|
|
|
public get asGame(): Game {
|
|
return {
|
|
paused: toJS(this.isPaused),
|
|
time: toJS(this.time),
|
|
scene: toJS(this.scene),
|
|
}
|
|
}
|
|
|
|
public setGame(value: Game) {
|
|
this.isPaused = value.paused;
|
|
this.time = value.time;
|
|
this.scene = value.scene;
|
|
}
|
|
|
|
public setIsPaused(value: boolean) {
|
|
this.isPaused = value;
|
|
}
|
|
|
|
public get camera(): Pos3 {
|
|
return this.world.data.editorCamera;
|
|
}
|
|
|
|
public get cameraAsThree(): CameraProps {
|
|
const cam = this.camera;
|
|
return {
|
|
position: cam.position,
|
|
fov: 50,
|
|
rotation: cam.look,
|
|
}
|
|
}
|
|
|
|
// public load() {
|
|
// console.log('Loading game...');
|
|
// this.withoutAutoSave(() => {
|
|
// this.data = GameFactory.load() ?? GameFactory.create(this.world.data);
|
|
// });
|
|
// }
|
|
|
|
private saveData(data: Game): void {
|
|
console.log('Saving game...');
|
|
const stack = new Error('Saving game...').stack!.split('\n').slice(1);
|
|
const { ...debug } = toJS(data);
|
|
console.dir({ stack, debug });
|
|
GameFactory.save(data);
|
|
}
|
|
|
|
public save(): void {
|
|
this.saveData(this.asGame);
|
|
}
|
|
|
|
public resume(): void {
|
|
this.isPaused = false;
|
|
}
|
|
|
|
public pause(): void {
|
|
this.isPaused = true;
|
|
}
|
|
|
|
public tick(deltaTime: number): void {
|
|
if (this.isPaused)
|
|
return;
|
|
|
|
this.time += deltaTime;
|
|
|
|
if (this.isFirstTick) {
|
|
this.isFirstTick = false;
|
|
this.emitEvent('game_start');
|
|
}
|
|
}
|
|
} |