238 lines
7.7 KiB
TypeScript
238 lines
7.7 KiB
TypeScript
import type { Graph } from '../graph/Graph';
|
||
import type { PropertyDef } from '../properties/PropertySchema';
|
||
import type { AssetType } from './AssetTypes';
|
||
import type { PropertyBag, PropertyValue } from './Properties';
|
||
import type { Scope } from './Scope';
|
||
import type { SceneObject } from './SceneObject';
|
||
|
||
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';
|
||
}
|
||
|
||
export interface TextAsset extends AssetBase {
|
||
type: 'text';
|
||
}
|
||
|
||
export interface SceneAsset extends AssetBase {
|
||
type: 'scene';
|
||
objects: SceneObject[];
|
||
}
|
||
|
||
/** `variables` is this asset's own property schema; `props` holds their default values. */
|
||
export interface BehaviorAsset extends AssetBase {
|
||
type: 'behavior';
|
||
variables: PropertyDef[];
|
||
graph: Graph;
|
||
}
|
||
|
||
/**
|
||
* Declarations shared by the whole run: a `Variables` node naming this asset reads and writes one
|
||
* value per key, however many graphs point at it. Nothing is placed, so nothing overrides it –
|
||
* `props` holds the starting values, as a behavior's does for its own variables.
|
||
*/
|
||
export interface VariablesAsset extends AssetBase {
|
||
type: 'variables';
|
||
variables: PropertyDef[];
|
||
}
|
||
|
||
/** Placed as a child of the object it belongs to; its position is the offset from that body. */
|
||
export interface ColliderAsset extends AssetBase {
|
||
type: 'collider';
|
||
}
|
||
|
||
/**
|
||
* `objects` is a template, not content: a placed container shows it without copying it, so an
|
||
* edit here reaches every instance (see `model/instance.ts`). Positions are local to the
|
||
* container's origin.
|
||
*/
|
||
export interface ContainerAsset extends AssetBase {
|
||
type: 'container';
|
||
objects: SceneObject[];
|
||
}
|
||
|
||
/** How the parts between the corners of a nine-slice frame fill the space they are given (#41). */
|
||
export type FrameFill = 'stretch' | 'repeat';
|
||
|
||
/** Distances from the frame's own edges, in image pixels; all zero means the frame is not sliced. */
|
||
export interface FrameBorders {
|
||
left: number;
|
||
top: number;
|
||
right: number;
|
||
bottom: number;
|
||
}
|
||
|
||
/**
|
||
* `id` shares the generator with asset ids and is unique across the document (see `model/texture.ts`).
|
||
* `border` and `fill` are absent on a frame nobody sliced, which is what every document written
|
||
* before #41 says – read them through `frameBorders`/`frameFill`, never off the object.
|
||
*/
|
||
export interface TextureFrame {
|
||
id: string;
|
||
name: string;
|
||
x: number;
|
||
y: number;
|
||
w: number;
|
||
h: number;
|
||
border?: FrameBorders;
|
||
fill?: FrameFill;
|
||
}
|
||
|
||
/**
|
||
* `file` names the image in `data/textures/` and is content-addressed (`{sha256}.{ext}`), so it
|
||
* can be cached forever; empty means the upload failed or has not happened yet. `width`/`height`
|
||
* are the size as uploaded, so frames can be sliced and validated before the image loads.
|
||
*/
|
||
export interface TextureAsset extends AssetBase {
|
||
type: 'texture';
|
||
file: string;
|
||
width: number;
|
||
height: number;
|
||
frames: TextureFrame[];
|
||
}
|
||
|
||
/** How a key blends into the next one (#101); absent is `linear`. `step` holds until the next key. */
|
||
export type KeyInterpolation = 'linear' | 'step' | 'ease-in' | 'ease-out' | 'ease-in-out';
|
||
|
||
/**
|
||
* A value on a whole frame. Its interpolation rules the way to the next key. A value that cannot
|
||
* blend (a texture, a switch) holds until the next key whatever it says.
|
||
*/
|
||
export interface AnimationKey {
|
||
frame: number;
|
||
value: PropertyValue;
|
||
interpolation?: KeyInterpolation;
|
||
}
|
||
|
||
/**
|
||
* One row of the timeline: a property of the root (`target` empty) or of an object inside it
|
||
* (`target` = the template object's id). `keys` are sorted by frame, at most one per frame.
|
||
* `relative` adds a number to the value the target had when the animation started.
|
||
*/
|
||
export interface AnimationTrack {
|
||
id: string;
|
||
target: string;
|
||
property: string;
|
||
relative?: boolean;
|
||
keys: AnimationKey[];
|
||
}
|
||
|
||
/**
|
||
* A timeline over one root asset – a sprite, text, container or scene. It is played on an object,
|
||
* and its rows reach that object and the objects inside it, so every instance plays on its own.
|
||
* The last key decides how long it runs (see `model/animation.ts`).
|
||
*/
|
||
export interface AnimationAsset extends AssetBase {
|
||
type: 'animation';
|
||
root: string;
|
||
tracks: AnimationTrack[];
|
||
}
|
||
|
||
/**
|
||
* The graph holds one `State` node per state and an edge per transition; a behavior reads the
|
||
* machine's current state through `State branch` and moves it through `Set state` or `State transition`.
|
||
* A `global` machine has one current state for the whole run instead of one per behavior instance.
|
||
*
|
||
* `start` is the id of the state every instance begins in, empty when the machine has no start –
|
||
* then it is in no state until `Set state` runs. It lives on the asset rather than on the node
|
||
* because only one state may be the start, and that is structural here instead of a rule someone
|
||
* has to keep. A deleted state leaves a dead id behind, which reads as no start.
|
||
*/
|
||
export interface StateMachineAsset extends AssetBase {
|
||
type: 'statemachine';
|
||
graph: Graph;
|
||
scope?: Scope;
|
||
start?: string;
|
||
}
|
||
|
||
/**
|
||
* The graph is GLSL in nodes: `Property` nodes declare the sprite rows, the `Output` node ends
|
||
* the fragment (see `core/shader/compile.ts`). A sprite wears it through its `shader` property.
|
||
*/
|
||
export interface ShaderAsset extends AssetBase {
|
||
type: 'shader';
|
||
graph: Graph;
|
||
}
|
||
|
||
/**
|
||
* `file` names the typeface in `data/fonts/` the way a texture names its image: content-addressed
|
||
* `{sha256}.{ext}`, empty when the upload failed. A text object wears it through its `font`
|
||
* property; the family the browser knows it under is derived from the file name, see `model/font`.
|
||
*/
|
||
export interface FontAsset extends AssetBase {
|
||
type: 'font';
|
||
file: string;
|
||
}
|
||
|
||
/**
|
||
* `file` names the sound in `data/sounds/` the way a font names its typeface: content-addressed
|
||
* `{sha256}.{ext}`, empty when the upload failed. Nothing wears a sound – a graph plays it with
|
||
* `Play sound`, and `props` says how loud and whether it repeats (see `properties/sounds.ts`).
|
||
*/
|
||
export interface SoundAsset extends AssetBase {
|
||
type: 'sound';
|
||
file: string;
|
||
}
|
||
|
||
/**
|
||
* A tile that draws its own edges: `pictures` names one block per animation step (a texture id for
|
||
* a sheet that holds nothing else, a frame id for one block cut out of a sheet), and `props` say
|
||
* which kind of block it is, how fast it runs and whether it loops or bounces. What a block is cut
|
||
* into and which quarter goes where is `model/autotile.ts`; placing one is the tilemap of #128.
|
||
*/
|
||
export interface AutotileAsset extends AssetBase {
|
||
type: 'autotile';
|
||
pictures: string[];
|
||
}
|
||
|
||
/**
|
||
* The graph is the motion of one particle: every output of its `Output` node is a function of the
|
||
* particle's age and its own random number (see `core/particles/compile.ts`). A placed object is
|
||
* the source that emits them; how many and for how long are its properties.
|
||
*/
|
||
export interface ParticlesAsset extends AssetBase {
|
||
type: 'particles';
|
||
graph: Graph;
|
||
}
|
||
|
||
export type Asset =
|
||
| SpriteAsset
|
||
| CameraAsset
|
||
| TextAsset
|
||
| SceneAsset
|
||
| BehaviorAsset
|
||
| VariablesAsset
|
||
| ColliderAsset
|
||
| ContainerAsset
|
||
| TextureAsset
|
||
| AnimationAsset
|
||
| AutotileAsset
|
||
| StateMachineAsset
|
||
| ShaderAsset
|
||
| FontAsset
|
||
| SoundAsset
|
||
| ParticlesAsset;
|
||
|
||
/** The assets edited as a node graph – what the graph canvas and the graph commands work on. */
|
||
export type GraphAsset = BehaviorAsset | StateMachineAsset | ShaderAsset | ParticlesAsset;
|
||
|
||
const GRAPH_TYPES: ReadonlySet<AssetType> = new Set(['behavior', 'statemachine', 'shader', 'particles']);
|
||
|
||
export function isGraphAsset(asset: Asset): asset is GraphAsset {
|
||
return GRAPH_TYPES.has(asset.type);
|
||
}
|
||
|
||
export function isSceneAsset(asset: Asset): asset is SceneAsset {
|
||
return asset.type === 'scene';
|
||
}
|