object type creation and deletion
This commit is contained in:
parent
dee28ba5b2
commit
ff5528008b
|
|
@ -11,8 +11,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 8px 8px;
|
|
||||||
gap: 8px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
@ -31,6 +29,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&>.title {
|
&>.title {
|
||||||
|
padding: 8px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
@ -38,12 +37,18 @@
|
||||||
|
|
||||||
&>.actions {
|
&>.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
|
||||||
|
&>* {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&>*:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& svg {
|
& svg {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin: 2px;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,3 +38,5 @@ export const DEFAULT_VOXEL_TYPES = {
|
||||||
[water.id]: water,
|
[water.id]: water,
|
||||||
[glass.id]: glass,
|
[glass.id]: glass,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_VOXEL_TYPE = stone;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { makeAutoObservable } from "mobx";
|
import { makeAutoObservable } from "mobx";
|
||||||
import { state } from "./rootState";
|
import { state } from "./rootState";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { IconRun, IconTrash, IconCubePlus, IconPlus } from '@tabler/icons-react';
|
import { IconRun, IconTrash, IconPlus, IconCubePlus, IconCubeOff } from '@tabler/icons-react';
|
||||||
|
|
||||||
export type MenuNodeAction = {
|
export type MenuNodeAction = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -30,34 +30,22 @@ export class MenuState {
|
||||||
const scene = editor.scene;
|
const scene = editor.scene;
|
||||||
|
|
||||||
return Object.values(state.world.data.objectTypes)
|
return Object.values(state.world.data.objectTypes)
|
||||||
.map((ot) => ({
|
.map((ot) => {
|
||||||
id: `ot-${ot.id}`,
|
const children = Object.values(scene.objects)
|
||||||
title: ot.name,
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
id: 'create-instance',
|
|
||||||
content: <IconPlus size="1em" />,
|
|
||||||
tooltip: 'Create new object instance',
|
|
||||||
onClick: () => { editor.addObjectInstanceAtRandomPosition(ot.id); },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
onClick: () => { editor.setSelectedObjectType({ id: ot.id, editMode: 'scripts' }) },
|
|
||||||
selected: () => editor.selection?.type === 'objectType' && editor.selection?.id === ot.id,
|
|
||||||
children: Object.values(scene.objects)
|
|
||||||
.filter((o) => o.typeId === ot.id)
|
.filter((o) => o.typeId === ot.id)
|
||||||
.map((o) => {
|
.map((o, idx) => {
|
||||||
const isPlayer = scene.playerObjectId === o.id;
|
const isPlayer = scene.playerObjectId === o.id;
|
||||||
|
|
||||||
const actions: MenuNodeAction[] = [];
|
const actions: MenuNodeAction[] = [];
|
||||||
if (!isPlayer) {
|
if (!isPlayer) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: 'delete',
|
id: 'delete-object-instance',
|
||||||
content: <IconTrash size="1em" />,
|
content: <IconTrash size="1em" />,
|
||||||
tooltip: 'Delete the object',
|
tooltip: 'Delete the object',
|
||||||
onClick: () => { editor.deleteObject(o.id); },
|
onClick: () => { editor.deleteObject(o.id); },
|
||||||
});
|
});
|
||||||
actions.push({
|
actions.push({
|
||||||
id: 'control-by-player',
|
id: 'control-object-instance-by-player',
|
||||||
content: <IconRun size="1em" />,
|
content: <IconRun size="1em" />,
|
||||||
tooltip: 'Mark as player',
|
tooltip: 'Mark as player',
|
||||||
onClick: () => { scene.playerObjectId = o.id; },
|
onClick: () => { scene.playerObjectId = o.id; },
|
||||||
|
|
@ -68,25 +56,53 @@ export class MenuState {
|
||||||
id: `o-${o.id}`,
|
id: `o-${o.id}`,
|
||||||
title: <>
|
title: <>
|
||||||
{isPlayer && <IconRun size="1em" />}
|
{isPlayer && <IconRun size="1em" />}
|
||||||
{o.id}
|
{`${ot.name} ${idx + 1}`}
|
||||||
</>,
|
</>,
|
||||||
className: isPlayer ? 'player-controlled-object' : undefined,
|
className: isPlayer ? 'player-controlled-object' : undefined,
|
||||||
actions,
|
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,
|
selected: () => state.worldEditor.selection?.type === 'object' && state.worldEditor.selection?.id === o.id,
|
||||||
} as MenuNode;
|
} as MenuNode;
|
||||||
})
|
});
|
||||||
} as MenuNode));
|
|
||||||
|
return {
|
||||||
|
id: `ot-${ot.id}`,
|
||||||
|
title: `${ot.name} (${children.length ? children.length : 'no'} instance${children.length != 1 ? 's' : ''})`,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
id: 'create-object-instance',
|
||||||
|
content: <IconPlus size="1em" />,
|
||||||
|
tooltip: 'Create new object instance',
|
||||||
|
onClick: () => { editor.addObjectInstanceAtRandomPosition(ot.id); },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'delete-object-type',
|
||||||
|
content: <IconCubeOff size="1em" />,
|
||||||
|
tooltip: 'Delete object type',
|
||||||
|
onClick: () => { editor.deleteObjectType(ot.id); },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onClick: () => { editor.setSelectedObjectType({ id: ot.id, editMode: 'scripts' }) },
|
||||||
|
selected: () => editor.selection?.type === 'objectType' && editor.selection?.id === ot.id,
|
||||||
|
children,
|
||||||
|
} as MenuNode
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private get editorMenu(): MenuNode[] {
|
private get editorMenu(): MenuNode[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: 'editor-objects-menu',
|
id: 'editor-scene-menu',
|
||||||
title: 'Objects',
|
title: 'Scene',
|
||||||
onClick: () => { state.worldEditor.resetSelection() },
|
onClick: () => { state.worldEditor.resetSelection() },
|
||||||
selected: () => !state.worldEditor.selection,
|
selected: () => !state.worldEditor.selection,
|
||||||
children: this.editorObjectTypesMenu,
|
children: this.editorObjectTypesMenu,
|
||||||
|
actions: [{
|
||||||
|
id: 'create-object-type',
|
||||||
|
content: <IconCubePlus size="1em" />,
|
||||||
|
tooltip: 'Create new object type',
|
||||||
|
onClick: () => { state.worldEditor.addObjectType(); },
|
||||||
|
}],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { makeAutoObservable, runInAction } from "mobx";
|
import { makeAutoObservable, runInAction } from "mobx";
|
||||||
import type { WorldState } from "./worldState";
|
import type { WorldState } from "./worldState";
|
||||||
import { type ObjectInstance, type Pos3, type R3, type V3, type RuntimeScene } from "../types";
|
import { type ObjectInstance, type Pos3, type R3, type V3, type RuntimeScene, type ObjectType } from "../types";
|
||||||
import { createObjectInstance } from "../utils/object";
|
import { createObjectInstance } from "../utils/object";
|
||||||
import { randomId } from "../utils";
|
import { randomId } from "../utils";
|
||||||
import { state } from "./rootState";
|
import { state } from "./rootState";
|
||||||
import { populateRuntimeObject } from "../utils/runtime";
|
import { populateRuntimeObject } from "../utils/runtime";
|
||||||
|
import { DEFAULT_VOXEL_TYPE } from "../model/defaultVoxelTypes";
|
||||||
|
|
||||||
export const ObjectEditModeEnum = [
|
export const ObjectEditModeEnum = [
|
||||||
'translate',
|
'translate',
|
||||||
|
|
@ -139,9 +140,36 @@ export class WorldEditorState {
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteObject(objectId: string) {
|
public deleteObject(objectId: string) {
|
||||||
if (this.selection?.type ==='object' && this.selection.id === objectId)
|
if (this.selection?.type === 'object' && this.selection.id === objectId)
|
||||||
this.resetSelection();
|
this.resetSelection();
|
||||||
delete (this.scene.objects[objectId]);
|
delete (this.scene.objects[objectId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addObjectType(): ObjectType {
|
||||||
|
const objectType: ObjectType = {
|
||||||
|
id: randomId(),
|
||||||
|
name: 'Unnamed',
|
||||||
|
voxels: [
|
||||||
|
{
|
||||||
|
typeId: DEFAULT_VOXEL_TYPE.id,
|
||||||
|
position: [0, 0, 0],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
this.world.data.objectTypes[objectType.id] = objectType;
|
||||||
|
this.setSelectedObjectType({ id: objectType.id, editMode: 'scripts' });
|
||||||
|
return objectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteObjectType(typeId: string) {
|
||||||
|
if (this.selection?.type === 'objectType' && this.selection.id === typeId)
|
||||||
|
this.resetSelection();
|
||||||
|
|
||||||
|
for (const id in this.scene.objects)
|
||||||
|
if (this.scene.objects[id].typeId === typeId)
|
||||||
|
this.deleteObject(id);
|
||||||
|
|
||||||
|
delete (this.world.data.objectTypes[typeId])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue