osbtools
Version:
A set of tools for working with osu! storyboards
179 lines (148 loc) • 5.57 kB
text/typescript
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;