UNPKG

osbtools

Version:

A set of tools for working with osu! storyboards

131 lines (106 loc) 4.03 kB
import { ESbElementProperty, ESbElementType, ESbLayer } from "../types/enums"; import { TSbLayerData, TSbLayersBuilder } from "../types/types"; import { parseElementGroupProperty, parseElementProperty, parseElementTitle } from "../utils/parsers"; import SbEmptyElement from "./sbEmptyElement"; import StoryboardElement from "./storyboardElement"; const layerData = (title: string): TSbLayerData<StoryboardElement> => ({ title, elements: [] }); type TLayers = TSbLayersBuilder<ESbLayer, StoryboardElement>; class Storyboard { readonly #elements: StoryboardElement[] = []; readonly #layers: TLayers = { [ESbLayer.Background]: layerData("//Storyboard Layer 0 (Background)"), [ESbLayer.Fail]: layerData("//Storyboard Layer 1 (Fail)"), [ESbLayer.Pass]: layerData("//Storyboard Layer 2 (Pass)"), [ESbLayer.Foreground]: layerData("//Storyboard Layer 3 (Foreground)"), [ESbLayer.Overlay]: layerData("//Storyboard Layer 4 (Overlay)"), [ESbLayer.Sound]: layerData("//Storyboard Sound Samples") } as const; constructor(osb?: string) { if (!osb) return; this.parse(osb, false); } addElement(element: StoryboardElement): Storyboard { this.#elements.push(element); this.#layers[element.getData().layer].elements.push(element); return this; } parse(osb: string, createNew: boolean = true): Storyboard | undefined { const sb = new Storyboard(); let currentPropertyType: ESbElementProperty | null = null; let currentPropertyTrimmedLine: string | null = null; let currentGroupElement = new SbEmptyElement(); let currentElement: StoryboardElement | null = null; const lines = osb.split("\n"); lines.forEach((line, index) => { if (line.startsWith("//") || line.startsWith("[")) return; const isElementTitle = line.startsWith(ESbElementType.Sprite) || line.startsWith(ESbElementType.Animation) || line.startsWith(ESbElementType.Sample); if (isElementTitle) { currentElement = null; } const isElementProperty = line.startsWith(" "); const isElementPropertyGroup = line.startsWith(" "); if (currentElement) { const trimmedLine = line.trim(); const type = trimmedLine.substring(0, trimmedLine[1] !== "," ? 2 : 1) as ESbElementProperty; if (isElementProperty && !isElementPropertyGroup) { currentPropertyType = type; currentPropertyTrimmedLine = trimmedLine; parseElementProperty(trimmedLine, type, currentElement); } else if (isElementPropertyGroup) { const isLastElementPropertyGroup = (typeof lines[index + 1] === "string" && !lines[index + 1].startsWith(" ")) || !lines[index + 1]; parseElementProperty(trimmedLine, type, currentGroupElement); if (isLastElementPropertyGroup && currentPropertyType && currentPropertyTrimmedLine) { parseElementGroupProperty( currentPropertyTrimmedLine, currentPropertyType, currentElement, currentGroupElement ); currentGroupElement = new SbEmptyElement(); } } } parseElementTitle(line, ESbElementType.Sprite, (element) => { currentElement = element; }); parseElementTitle(line, ESbElementType.Animation, (element) => { currentElement = element; }); parseElementTitle(line, ESbElementType.Sample, (element) => { currentElement = element; }); if (currentElement && isElementTitle) { if (!createNew) this.addElement(currentElement); else sb.addElement(currentElement); } }); if (createNew) return sb; } getElements(): StoryboardElement[] { return this.#elements; } getElement(index: number): StoryboardElement | null { return this.#elements[index] || null; } getLayers(): TLayers { return this.#layers; } toString(): string { return [ "[Events]", "//Background and Video events", ...Object.entries(this.getLayers()).map(([, layerData]) => { const { title, elements } = layerData ?? {}; return [title, ...elements.map((element) => element.toString())].join("\n"); }) ].join("\n"); } } export default Storyboard;