From 3e19d29228bfaf4dc3275033b31a02e4c25a975f Mon Sep 17 00:00:00 2001 From: Abnormal <86753001+AbnormalPoof@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:41:17 -0600 Subject: [PATCH] feat: Sticker Registry --- source/funkin/InitState.hx | 2 + source/funkin/data/stickers/CHANGELOG.md | 9 ++ source/funkin/data/stickers/StickerData.hx | 39 +++++++ .../funkin/data/stickers/StickerRegistry.hx | 84 ++++++++++++++ source/funkin/data/stickers/StickerSet.hx | 109 ++++++++++++++++++ source/funkin/modding/PolymodHandler.hx | 17 ++- .../ui/transition/ScriptedStickerSet.hx | 8 ++ .../funkin/ui/transition/StickerSubState.hx | 93 +++------------ 8 files changed, 281 insertions(+), 80 deletions(-) create mode 100644 source/funkin/data/stickers/CHANGELOG.md create mode 100644 source/funkin/data/stickers/StickerData.hx create mode 100644 source/funkin/data/stickers/StickerRegistry.hx create mode 100644 source/funkin/data/stickers/StickerSet.hx create mode 100644 source/funkin/ui/transition/ScriptedStickerSet.hx diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index fb1a48ef8a..6da76d19d8 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -18,6 +18,7 @@ import funkin.data.freeplay.player.PlayerRegistry; import funkin.data.freeplay.style.FreeplayStyleRegistry; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongRegistry; +import funkin.data.stickers.StickerRegistry; import funkin.data.event.SongEventRegistry; import funkin.data.stage.StageRegistry; import funkin.data.story.level.LevelRegistry; @@ -176,6 +177,7 @@ class InitState extends FlxState FreeplayStyleRegistry.instance.loadEntries(); AlbumRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); + StickerRegistry.instance.loadEntries(); // TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers and more prone to syntax errors. // Move it to use a BaseRegistry. diff --git a/source/funkin/data/stickers/CHANGELOG.md b/source/funkin/data/stickers/CHANGELOG.md new file mode 100644 index 0000000000..d278a5f9cf --- /dev/null +++ b/source/funkin/data/stickers/CHANGELOG.md @@ -0,0 +1,9 @@ +# Sticker Set Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/stickers/StickerData.hx b/source/funkin/data/stickers/StickerData.hx new file mode 100644 index 0000000000..e0d82d3234 --- /dev/null +++ b/source/funkin/data/stickers/StickerData.hx @@ -0,0 +1,39 @@ +package funkin.data.stickers; + +/** + * A type definition for a sticker set. + * It includes things like its name, the artist, and the stickers. + * @see https://lib.haxe.org/p/json2object/ + */ +typedef StickerData = +{ + /** + * Semantic version of the sticker set data. + */ + public var version:String; + + /** + * Readable name of the sticker set. + */ + public var name:String; + + /** + * The asset key for this sticker set. + */ + public var assetPath:String; + + /** + * The artist of the sticker set. + */ + public var artist:String; + + /** + * The stickers in the set. + */ + public var stickers:Map>; + + /** + * The sticker packs in this set. + */ + public var stickerPacks:Map>; +} diff --git a/source/funkin/data/stickers/StickerRegistry.hx b/source/funkin/data/stickers/StickerRegistry.hx new file mode 100644 index 0000000000..08ec6194b0 --- /dev/null +++ b/source/funkin/data/stickers/StickerRegistry.hx @@ -0,0 +1,84 @@ +package funkin.data.stickers; + +import funkin.data.stickers.StickerSet; +import funkin.data.stickers.StickerData; +import funkin.ui.transition.ScriptedStickerSet; + +class StickerRegistry extends BaseRegistry +{ + /** + * The current version string for the sticker set data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migrateStickerData()` function. + */ + public static final STICKER_DATA_VERSION:thx.semver.Version = '1.0.0'; + + public static final STICKER_DATA_VERSION_RULE:thx.semver.VersionRule = '1.0.x'; + + public static final instance:StickerRegistry = new StickerRegistry(); + + public function new() + { + super('STICKER', 'stickersets', STICKER_DATA_VERSION_RULE); + } + + /** + * Read, parse, and validate the JSON data and produce the corresponding data object. + * @param id The ID of the entry to load. + * @return The parsed data object. + */ + public function parseEntryData(id:String):Null + { + // JsonParser does not take type parameters, + // otherwise this function would be in BaseRegistry. + var parser:json2object.JsonParser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + + switch (loadEntryFile(id)) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + printErrors(parser.errors, id); + return null; + } + return parser.value; + } + + /** + * Parse and validate the JSON data and produce the corresponding data object. + * + * NOTE: Must be implemented on the implementation class. + * @param contents The JSON as a string. + * @param fileName An optional file name for error reporting. + * @return The parsed data object. + */ + public function parseEntryDataRaw(contents:String, ?fileName:String):Null + { + var parser:json2object.JsonParser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + parser.fromJson(contents, fileName); + + if (parser.errors.length > 0) + { + printErrors(parser.errors, fileName); + return null; + } + return parser.value; + } + + function createScriptedEntry(clsName:String):StickerSet + { + return ScriptedStickerSet.init(clsName, 'unknown'); + } + + function getScriptedClassNames():Array + { + return ScriptedStickerSet.listScriptClasses(); + } +} diff --git a/source/funkin/data/stickers/StickerSet.hx b/source/funkin/data/stickers/StickerSet.hx new file mode 100644 index 0000000000..1cea8293a5 --- /dev/null +++ b/source/funkin/data/stickers/StickerSet.hx @@ -0,0 +1,109 @@ +package funkin.data.stickers; + +import funkin.data.stickers.StickerData; +import funkin.data.stickers.StickerRegistry; +import funkin.data.IRegistryEntry; +import funkin.graphics.FunkinSprite; + +/** + * A class representing the data for a sticker set as displayed in the Sticker SubState. + */ +class StickerSet implements IRegistryEntry +{ + /** + * The internal ID for this sticker set. + */ + public final id:String; + + /** + * The full data for this sticker set. + */ + public final _data:StickerData; + + public function new(id:String) + { + this.id = id; + this._data = _fetchData(id); + + if (_data == null) + { + throw 'Could not parse sticker set data for id: $id'; + } + } + + /** + * Return the name of the sticker set. + * @return The name of the sticker set + */ + public function getStickerSetName():String + { + return _data.name; + } + + /** + * Return the artist of the sticker set. + * @return The list of artists + */ + public function getStickerSetArtist():String + { + return _data.artist; + } + + /** + * Get the asset key for the album art. + * @return The asset key + */ + public function getStickerSetAssetKey():String + { + return _data.assetPath; + } + + /** + * Gets the stickers for a given sticker name. + * @param stickerName The name of the sticker to get. + * @return The sticker. + */ + public function getStickers(stickerName:String):Array + { + return _data.stickers[stickerName]; + } + + /** + * Gets the sticker pack for a given pack name. + * @param packName The name of the pack to get. + * @return The sticker pack. + */ + public function getPack(packName:String):Array + { + return _data.stickerPacks[packName]; + } + + public function toString():String + { + return 'StickerSet($id)'; + } + + public function destroy():Void {} + + static function _fetchData(id:String):Null + { + return StickerRegistry.instance.parseEntryDataWithMigration(id, StickerRegistry.instance.fetchEntryVersion(id)); + } +} + +class StickerSprite extends FunkinSprite +{ + public var timing:Float = 0; + + public function new(x:Float, y:Float, filePath:String):Void + { + super(x, y); + if (!Assets.exists(Paths.image(filePath))) + { + throw 'File path does not exist! ($filePath)'; + } + loadTexture(filePath); + updateHitbox(); + scrollFactor.set(); + } +} diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index e5bcae0f55..aaa7642c6d 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -11,6 +11,7 @@ import funkin.play.notes.notekind.NoteKindManager; import funkin.data.song.SongRegistry; import funkin.data.freeplay.player.PlayerRegistry; import funkin.data.stage.StageRegistry; +import funkin.data.stickers.StickerRegistry; import funkin.data.freeplay.album.AlbumRegistry; import funkin.modding.module.ModuleHandler; import funkin.play.character.CharacterData.CharacterDataParser; @@ -335,8 +336,19 @@ class PolymodHandler { return { assetLibraryPaths: [ - 'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'videos' => 'videos', 'tutorial' => 'tutorial', 'week1' => 'week1', - 'week2' => 'week2', 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1', + 'default' => 'preload', + 'shared' => 'shared', + 'songs' => 'songs', + 'videos' => 'videos', + 'tutorial' => 'tutorial', + 'week1' => 'week1', + 'week2' => 'week2', + 'week3' => 'week3', + 'week4' => 'week4', + 'week5' => 'week5', + 'week6' => 'week6', + 'week7' => 'week7', + 'weekend1' => 'weekend1', ], coreAssetRedirect: CORE_FOLDER, } @@ -427,6 +439,7 @@ class PolymodHandler SpeakerRegistry.instance.loadEntries(); AlbumRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); + StickerRegistry.instance.loadEntries(); CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry. NoteKindManager.loadScripts(); diff --git a/source/funkin/ui/transition/ScriptedStickerSet.hx b/source/funkin/ui/transition/ScriptedStickerSet.hx new file mode 100644 index 0000000000..aaada82dba --- /dev/null +++ b/source/funkin/ui/transition/ScriptedStickerSet.hx @@ -0,0 +1,8 @@ +package funkin.ui.transition; + +/** + * A script that can be tied to a StickerSet. + * Create a scripted class that extends StickerSet to use this. + */ +@:hscriptClass +class ScriptedStickerSet extends funkin.data.stickers.StickerSet implements polymod.hscript.HScriptedClass {} diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx index e8d6877d8c..9244a0a7ad 100644 --- a/source/funkin/ui/transition/StickerSubState.hx +++ b/source/funkin/ui/transition/StickerSubState.hx @@ -15,6 +15,8 @@ import flixel.util.FlxSignal; import funkin.ui.mainmenu.MainMenuState; import flixel.addons.transition.FlxTransitionableState; import openfl.display.BitmapData; +import funkin.data.stickers.StickerRegistry; +import funkin.data.stickers.StickerSet; import funkin.ui.freeplay.FreeplayState; import openfl.geom.Matrix; import funkin.audio.FunkinSound; @@ -155,11 +157,20 @@ class StickerSubState extends MusicBeatSubState grpStickers.clear(); } - var stickerInfo:StickerInfo = new StickerInfo('stickers-set-1'); + var stickerSets:Array = StickerRegistry.instance.listEntryIds(); var stickers:Map> = new Map>(); - for (stickerSets in stickerInfo.getPack("all")) + + for (stickerSetEntry in stickerSets) { - stickers.set(stickerSets, stickerInfo.getStickers(stickerSets)); + trace('Got sticker set: ${stickerSetEntry}'); + var stickerSet:StickerSet = new StickerSet(stickerSetEntry); + var assetKey:String = stickerSet.getStickerSetAssetKey(); + for (sticker in stickerSet.getPack("all")) + { + // add the asset key at the beginning of each sticker name because it's just easier lol + var stickerPack:Array = stickerSet.getStickers(sticker).map(s -> '${assetKey}/${s}'); + stickers.set(sticker, stickerPack); + } } var xPos:Float = -100; @@ -168,7 +179,7 @@ class StickerSubState extends MusicBeatSubState { var stickerSet:String = FlxG.random.getObject(stickers.keyValues()); var sticker:String = FlxG.random.getObject(stickers.get(stickerSet)); - var sticky:StickerSprite = new StickerSprite(0, 0, stickerInfo.name, sticker); + var sticky:StickerSprite = new StickerSprite(0, 0, sticker); sticky.visible = false; sticky.x = xPos; @@ -309,77 +320,3 @@ class StickerSubState extends MusicBeatSubState super.destroy(); } } - -class StickerSprite extends FunkinSprite -{ - public var timing:Float = 0; - - public function new(x:Float, y:Float, stickerSet:String, stickerName:String):Void - { - super(x, y); - loadTexture('transitionSwag/' + stickerSet + '/' + stickerName); - updateHitbox(); - scrollFactor.set(); - } -} - -class StickerInfo -{ - public var name:String; - public var artist:String; - public var stickers:Map>; - public var stickerPacks:Map>; - - public function new(stickerSet:String):Void - { - var path = Paths.file('images/transitionSwag/' + stickerSet + '/stickers.json'); - var json = Json.parse(Assets.getText(path)); - - // doin this dipshit nonsense cuz i dunno how to deal with casting a json object with - // a dash in its name (sticker-packs) - var jsonInfo:StickerShit = cast json; - - this.name = jsonInfo.name; - this.artist = jsonInfo.artist; - - stickerPacks = new Map>(); - - for (field in Reflect.fields(json.stickerPacks)) - { - var stickerFunny = json.stickerPacks; - var stickerStuff = Reflect.field(stickerFunny, field); - - stickerPacks.set(field, cast stickerStuff); - } - - // creates a similar for loop as before but for the stickers - stickers = new Map>(); - - for (field in Reflect.fields(json.stickers)) - { - var stickerFunny = json.stickers; - var stickerStuff = Reflect.field(stickerFunny, field); - - stickers.set(field, cast stickerStuff); - } - } - - public function getStickers(stickerName:String):Array - { - return this.stickers[stickerName]; - } - - public function getPack(packName:String):Array - { - return this.stickerPacks[packName]; - } -} - -// somethin damn cute just for the json to cast to! -typedef StickerShit = -{ - name:String, - artist:String, - stickers:Map>, - stickerPacks:Map> -}