import { makeAutoObservable, runInAction } from "mobx"; import type { WorldState } from "./worldState"; import { type ObjectInstance, type Pos3, type R3, type V3, type RuntimeScene } from "../types"; import { createObjectInstance } from "../utils/object"; import { randomId } from "../utils"; import { state } from "./rootState"; import { populateRuntimeObject } from "../utils/runtime"; export const ObjectEditModeEnum = [ 'translate', 'rotate', 'scale', ] as const; type ObjectEditModeTuple = typeof ObjectEditModeEnum; export type ObjectEditMode = ObjectEditModeTuple[number]; export function nextObjectEditMode(mode: ObjectEditMode | undefined): ObjectEditMode { if (mode === undefined) return ObjectEditModeEnum[0]; const idx = ObjectEditModeEnum.indexOf(mode); return ObjectEditModeEnum[(idx + 1) % ObjectEditModeEnum.length]; } export const ObjectTypeEditModeEnum = [ 'scripts', ] as const; type ObjectTypeEditModeTuple = typeof ObjectTypeEditModeEnum; export type ObjectTypeEditMode = ObjectTypeEditModeTuple[number]; export function nextObjectTypeEditMode(mode: ObjectTypeEditMode | undefined): ObjectTypeEditMode { if (mode === undefined) return ObjectTypeEditModeEnum[0]; const idx = ObjectTypeEditModeEnum.indexOf(mode); return ObjectTypeEditModeEnum[(idx + 1) % ObjectTypeEditModeEnum.length]; } export type SelectedObject = { type: 'object', id: string; editMode: ObjectEditMode; } export type SelectedObjectType = { type: 'objectType', id: string; editMode: ObjectTypeEditMode; } export type Selection = SelectedObject | SelectedObjectType; export class WorldEditorState { private readonly world: WorldState; public selection: Selection | undefined; constructor(world: WorldState) { this.world = world; makeAutoObservable(this); } public get isEnabled(): boolean { return !state.game; } public setCamera(value: Pos3): void { this.world.data.editorCamera = value; } public setSelection(value: Selection | undefined): void { this.selection = value; } public setSelectedObject(value: Omit | undefined): void { this.setSelection(value ? { type: 'object', ...value, } : undefined, ); } public setSelectedObjectType(value: Omit | undefined): void { this.setSelection(value ? { type: 'objectType', ...value, } : undefined, ); } public resetSelection() { this.setSelection(undefined); } public get scene(): RuntimeScene { return this.world.data.initialScene; } public get camera(): Pos3 { return this.world.data.editorCamera; } public setObjectTransform(id: string, position: V3, rotation: R3, scale: V3): void { const obj = this.scene.objects[id]; if (!obj) return; runInAction(() => { // all in one go obj.position = position; obj.rotation = rotation; obj.scale = scale; }); } public addObjectInstanceAtRandomPosition(typeId: string): ObjectInstance { return this.addObjectInstance( typeId, { x: Math.random() * 3, y: Math.random() * 3, z: Math.random() * 1 }, ); } public addObjectInstance( typeId: string, pos: { x: number; y: number; z: number; }, ): ObjectInstance { const obj = populateRuntimeObject(createObjectInstance(randomId(), typeId), this.world.data); obj.position = [pos.x, pos.y, pos.z]; obj.rotation = [0, 0, 0]; this.scene.objects[obj.id] = obj; this.setSelectedObject({ id: obj.id, editMode: 'translate' }); return obj; } public deleteObject(objectId: string) { if (this.selection?.type ==='object' && this.selection.id === objectId) this.resetSelection(); delete (this.scene.objects[objectId]); } }