UNPKG

osbtools

Version:

A set of tools for working with osu! storyboards

179 lines (148 loc) 5.57 kB
import { ESbLayer, ESbElementOrigin, ESbElementProperty, ESbElementEasing, ESbElementType } from "../types/enums"; import { TStoryboardElementColor, TStoryboardElementData, TStoryboardElementDefaultProps, TStoryboardElementFade, TStoryboardElementLoop, TStoryboardElementMove, TStoryboardElementMoveX, TStoryboardElementMoveY, TStoryboardElementProperties, TStoryboardElementPropertiesByLayer, TStoryboardElementPropertyItem, TStoryboardElementPropertyMap, TStoryboardElementRotate, TStoryboardElementScale, TStoryboardElementScaleVec, TStoryboardElementTrigger, TUnstrictStoryboardElementData } from "../types/types"; import { UnionToIntersection } from "../types/utils"; import { convertPropertyToString } from "../utils/converters"; import SbVectorValue from "./values/sbVectorValue"; abstract class StoryboardElement { abstract type: ESbElementType; #data: TStoryboardElementData; #properties: TStoryboardElementProperties = [] as unknown as TStoryboardElementProperties; #propertiesByLayer: TStoryboardElementPropertiesByLayer = Object.values(ESbElementProperty).reduce((acc, key) => { acc[key as ESbElementProperty] = [] as unknown as TStoryboardElementProperties; return acc; }, {} as TStoryboardElementPropertiesByLayer); constructor({ path = "", layer = ESbLayer.Background, origin = ESbElementOrigin.Centre, defaultPosition = new SbVectorValue({ x: 320, y: 240 }) }: TUnstrictStoryboardElementData) { this.#data = { path, layer, origin, defaultPosition, existStartTime: Infinity, existEndTime: -Infinity }; this.#properties.getProperty = function <T extends ESbElementProperty>(index: number) { return this[index] as TStoryboardElementPropertyItem<T>; }; } getType(): ESbElementType { return this.type; } getData(): TStoryboardElementData { return this.#data; } getProperties(): TStoryboardElementProperties | undefined { return this.#properties; } getPropertiesByLayer(): TStoryboardElementPropertiesByLayer | undefined { return this.#propertiesByLayer; } getProperty<T extends ESbElementProperty>( index: number, cb?: (property: TStoryboardElementPropertyItem<T>) => TStoryboardElementPropertyItem<T> ): TStoryboardElementPropertyItem<T> { if (cb) this.#properties[index] = cb(this.#properties[index] as TStoryboardElementPropertyItem<T>); return this.#properties[index] as TStoryboardElementPropertyItem<T>; } #setExistTimes(data: TStoryboardElementDefaultProps) { this.#data.existStartTime = Math.min(data.startTime, this.#data.existStartTime ?? data.startTime); const endTime = data.endTime === undefined ? data.startTime : data.endTime; this.#data.existEndTime = Math.max(endTime ?? -Infinity, this.#data.existEndTime ?? -Infinity); } #addProperty<T extends ESbElementProperty>( type: T, data: TStoryboardElementPropertyMap[T], convertType: keyof typeof convertPropertyToString ) { const updatedData: TStoryboardElementPropertyMap[T] = { easing: ESbElementEasing.Linear, ...data }; type TStoryboardElementPropertiesIntersection = UnionToIntersection< TStoryboardElementPropertyMap[keyof TStoryboardElementPropertyMap] >; this.#setExistTimes(data); const completedData = { type, data: updatedData, toString: () => convertPropertyToString[convertType](updatedData as TStoryboardElementPropertiesIntersection) }; this.#properties.push(completedData); this.#propertiesByLayer[type].push(completedData); return this; } loop(data: TStoryboardElementLoop) { const loopData = { ...data, properties: data.loopedProperties() }; return this.#addProperty(ESbElementProperty.L, loopData, "loop"); } trigger(data: TStoryboardElementTrigger) { const triggerData = { ...data, properties: data.triggeredProperties() }; return this.#addProperty(ESbElementProperty.T, triggerData, "trigger"); } move(data: TStoryboardElementMove): StoryboardElement { return this.#addProperty(ESbElementProperty.M, data, "move"); } moveX(data: TStoryboardElementMoveX): StoryboardElement { return this.#addProperty(ESbElementProperty.MX, data, "moveX"); } moveY(data: TStoryboardElementMoveY): StoryboardElement { return this.#addProperty(ESbElementProperty.MY, data, "moveY"); } rotate(data: TStoryboardElementRotate): StoryboardElement { return this.#addProperty(ESbElementProperty.R, data, "rotate"); } fade(data: TStoryboardElementFade): StoryboardElement { return this.#addProperty(ESbElementProperty.F, data, "fade"); } scale(data: TStoryboardElementScale): StoryboardElement { return this.#addProperty(ESbElementProperty.S, data, "scale"); } scaleVec(data: TStoryboardElementScaleVec): StoryboardElement { return this.#addProperty(ESbElementProperty.V, data, "scaleVec"); } color(data: TStoryboardElementColor): StoryboardElement { return this.#addProperty(ESbElementProperty.C, data, "color"); } flipH(data: TStoryboardElementDefaultProps): StoryboardElement { const startParameter = "H"; return this.#addProperty(ESbElementProperty.P, { ...data, startParameter }, "parameters"); } flipV(data: TStoryboardElementDefaultProps): StoryboardElement { const startParameter = "V"; return this.#addProperty(ESbElementProperty.P, { ...data, startParameter }, "parameters"); } additive(data: TStoryboardElementDefaultProps): StoryboardElement { const startParameter = "A"; return this.#addProperty(ESbElementProperty.P, { ...data, startParameter }, "parameters"); } abstract toString(): string; } export default StoryboardElement;