The Container tab reuses SceneViewport against any object holder, so a container template is edited exactly like a scene. Right-click no longer changes the selection, which previously made nesting impossible: opening the menu on a container loaded it, and "Place in container" then targeted itself. Template edits (move, rename, override) now reach already-placed instances per slot, through a shared templateSync used by both the apply path and the plain object commands. Copies the user has touched keep their own value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
import { newId } from '../ids';
|
||
import type { Project } from '../model/Project';
|
||
import { createSceneObject } from '../model/SceneObject';
|
||
import { resolveSceneObjectProps } from '../properties/resolve';
|
||
import { removeById } from '../util/list';
|
||
import { buildAsset } from './assetCommands';
|
||
import { type Command, CommandError } from './Command';
|
||
import { nextAssetName } from './naming';
|
||
import { requireAsset, requireObject, requireHolder } from './require';
|
||
|
||
/**
|
||
* Creates a collider asset sized like the object (sprites: width × height; anything else: schema
|
||
* defaults) and places it as a child at the object's centre.
|
||
*/
|
||
export class AddColliderCommand implements Command {
|
||
readonly label = 'Add collider';
|
||
readonly assetId = newId();
|
||
readonly objectId = newId();
|
||
|
||
constructor(
|
||
private readonly holderId: string,
|
||
private readonly targetId: string,
|
||
) {}
|
||
|
||
execute(project: Project): void {
|
||
const holder = requireHolder(project, this.holderId);
|
||
const target = requireObject(project, this.holderId, this.targetId);
|
||
const targetAsset = requireAsset(project, target.assetId);
|
||
if (targetAsset.type === 'collider') throw new CommandError('A collider cannot have a collider');
|
||
|
||
const asset = buildAsset(
|
||
'collider',
|
||
nextAssetName(project, `${target.name || targetAsset.name} collider`),
|
||
this.assetId,
|
||
);
|
||
if (targetAsset.type === 'sprite') {
|
||
const props = resolveSceneObjectProps(targetAsset, target);
|
||
asset.props.width = Number(props.width);
|
||
asset.props.height = Number(props.height);
|
||
}
|
||
project.assets.push(asset);
|
||
holder.objects.push(createSceneObject(this.objectId, this.assetId, { x: 0, y: 0, z: 0 }, this.targetId));
|
||
}
|
||
|
||
undo(project: Project): void {
|
||
const holder = requireHolder(project, this.holderId);
|
||
holder.objects = removeById(holder.objects, this.objectId);
|
||
project.assets = removeById(project.assets, this.assetId);
|
||
}
|
||
}
|