blockly3d/src/state/worldState.ts

131 lines
4.0 KiB
TypeScript

import { makeAutoObservable, reaction, toJS } from "mobx";
import { WorldFactory } from "../model/worldFactory";
import { type ObjectInstance, type ObjectType, type World, type VoxelType, type RuntimeWorld } from "../types";
import { state } from "./rootState";
import { DEFAULT_VOXEL_TYPES } from "../model/defaultVoxelTypes";
import { wolf } from "../model/objectPrefabs/wolf";
import { terrainXZ } from "../model/objectPrefabs/terrain";
import { populateRuntimeWorld } from "../utils/runtime";
export class WorldState {
public data: RuntimeWorld = WorldFactory.create();
private startAutoSave() {
return reaction(() => toJS(this.data), (data) => this.saveData(data), { delay: 500 });
}
private withoutAutoSave(fn: () => void) {
this._stopAutoSave();
fn();
this._stopAutoSave = this.startAutoSave();
}
private _stopAutoSave: () => void = () => { };
constructor() {
makeAutoObservable(this);
this._stopAutoSave = this.startAutoSave();
}
public reset() {
console.log('Resetting world...');
this.withoutAutoSave(() => {
this.data = WorldFactory.create();
});
state.worldEditor.resetSelectedObject();
}
public loadMock() {
console.log('Mocking world...');
const objects = Array(3).fill(0)
.map((_, idx) => ({
id: `obj${idx}`,
typeId: 'wolf',
physics: true,
gravityScale: 1,
position: [idx * 10 - 10, 5, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1],
} as ObjectInstance));
const objectMap = Object.fromEntries(
objects.map((obj) => [obj.id, obj]),
) as Record<string, ObjectInstance>
this.data = populateRuntimeWorld({
objectTypes: {
wolf: {
id: 'wolf',
name: 'Wolf',
voxels: wolf,
},
terrain: {
id: 'terrain',
name: 'Terrain',
voxels: terrainXZ(50, 50),
}
},
voxelTypes: DEFAULT_VOXEL_TYPES,
editorCamera: {
position: [-9, 11, 30],
look: [-0.52, -0.35, -0.2],
},
initialScene: {
character: {
transform: {
position: [0, 5, 20],
look: [0, 0, 0],
},
},
objects: {
terrain: {
id: 'terrain',
typeId: 'terrain',
physics: false, // pinned
gravityScale: 0, // pinned
position: [0, -1, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1],
},
...objectMap,
},
},
gameRules: {
gravity: true,
},
});
console.log(objects);
state.worldEditor.resetSelectedObject();
}
public load() {
console.log('Loading world...');
this.withoutAutoSave(() => {
this.data = WorldFactory.load() ?? WorldFactory.create();
});
state.worldEditor.resetSelectedObject();
}
private saveData(data: World): void {
console.log('Saving world...');
const stack = new Error('Saving world...').stack!.split('\n').slice(1);
const { objectTypes, voxelTypes, ...debug } = toJS(data);
console.dir({ stack, debug });
WorldFactory.save(toJS(this.data));
}
public save(): void {
this.saveData(this.data);
}
public getObjectTypeById(id: string): ObjectType | undefined {
return this.data.objectTypes[id];
}
public getVoxelTypeById(id: string): VoxelType | undefined {
return this.data.voxelTypes[id];
}
}