import type { Graph } from '../graph/Graph'; import type { PropertyDef } from '../properties/PropertySchema'; import type { AssetType } from './AssetTypes'; import type { PropertyBag } from './Properties'; import type { SceneObject } from './SceneObject'; /** Every asset has an id, a name, a type and a bag of typed properties (see PropertySchema). */ export interface AssetBase { id: string; type: AssetType; name: string; props: PropertyBag; } export interface SpriteAsset extends AssetBase { type: 'sprite'; } export interface CameraAsset extends AssetBase { type: 'camera'; } /** * A line of text drawn in the scene (titles, HUD readouts, buttons). Its content is the * ordinary property `text`, so behaviors change it with `Set text` (or `Set property`). */ export interface TextAsset extends AssetBase { type: 'text'; } /** A scene is an asset that additionally owns the objects placed into it. */ export interface SceneAsset extends AssetBase { type: 'scene'; objects: SceneObject[]; } /** * A behavior is logic: a node graph plus the variables it declares. Variables are ordinary * properties – `variables` is the asset's own property schema, `props` holds their default * values, and placed instances override them like any other property. */ export interface BehaviorAsset extends AssetBase { type: 'behavior'; variables: PropertyDef[]; graph: Graph; } /** * A collider is a shape (box or circle) used for collision detection. Placed as a child of * the object it belongs to (its "body"); its position is the offset from that body. */ export interface ColliderAsset extends AssetBase { type: 'collider'; } /** * A container is a reusable group of objects (a "prefab"): its `objects` are a template that * is instantiated below the container whenever it is placed. Template positions are local * to the container's origin – the centre of the group when it was created from a selection. * Template ids are private to the asset; every placed copy gets fresh ids and remembers the * template object it came from (`SceneObject.templateId`). */ export interface ContainerAsset extends AssetBase { type: 'container'; objects: SceneObject[]; } /** * One named rectangle of a texture, in image pixels – a frame of a sprite sheet. * * Its `id` is drawn from the same generator as asset ids and is unique across the document, * which is what lets a sprite's `texture` property name either a whole texture or one frame * of it (see `model/texture.ts`). */ export interface TextureFrame { id: string; name: string; x: number; y: number; w: number; h: number; } /** * An uploaded image. `file` names it in `data/textures/` and is content-addressed * (`{sha256}.{ext}`), so the same picture uploaded twice is one file and a stored file can be * cached forever – its name cannot ever come to mean different bytes. * * Like a scene's objects and a behavior's graph, this is intrinsic data rather than a * property: nothing about it is editable or overridable per instance. What *is* a property * is the reference to it – sprites carry `texture` (see PropertySchema), which is how * instance overrides, "Apply to container" and `Set property` work on it for free. * * `frames` cuts the picture into named rectangles (the Texture tab draws them); a texture * with no frames is simply used whole, which is what every single-image texture wants and * what every document written before frames existed already says. * * `width`/`height` are the pixel size as uploaded, kept so a sprite can be sized to its * picture without waiting for the image to load – and so frames can be validated and sliced * before it arrives. An empty `file` means the upload failed or has not happened yet; such a * texture draws as nothing. */ export interface TextureAsset extends AssetBase { type: 'texture'; file: string; width: number; height: number; frames: TextureFrame[]; } export type Asset = | SpriteAsset | CameraAsset | TextAsset | SceneAsset | BehaviorAsset | ColliderAsset | ContainerAsset | TextureAsset; export function isSceneAsset(asset: Asset): asset is SceneAsset { return asset.type === 'scene'; } export function isTextureAsset(asset: Asset): asset is TextureAsset { return asset.type === 'texture'; }