Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENHANCEMENT] Better mod support for stickers (StickerRegistry) #4003

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/funkin/InitState.hx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
9 changes: 9 additions & 0 deletions source/funkin/data/stickers/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
39 changes: 39 additions & 0 deletions source/funkin/data/stickers/StickerData.hx
Original file line number Diff line number Diff line change
@@ -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<String, Array<String>>;

/**
* The sticker packs in this set.
*/
public var stickerPacks:Map<String, Array<String>>;
}
84 changes: 84 additions & 0 deletions source/funkin/data/stickers/StickerRegistry.hx
Original file line number Diff line number Diff line change
@@ -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<StickerSet, StickerData>
{
/**
* 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<StickerData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser:json2object.JsonParser<StickerData> = new json2object.JsonParser<StickerData>();
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<StickerData>
{
var parser:json2object.JsonParser<StickerData> = new json2object.JsonParser<StickerData>();
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<String>
{
return ScriptedStickerSet.listScriptClasses();
}
}
109 changes: 109 additions & 0 deletions source/funkin/data/stickers/StickerSet.hx
Original file line number Diff line number Diff line change
@@ -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<StickerData>
{
/**
* 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<String>
{
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<String>
{
return _data.stickerPacks[packName];
}

public function toString():String
{
return 'StickerSet($id)';
}

public function destroy():Void {}

static function _fetchData(id:String):Null<StickerData>
{
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();
}
}
17 changes: 15 additions & 2 deletions source/funkin/modding/PolymodHandler.hx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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();
Expand Down
8 changes: 8 additions & 0 deletions source/funkin/ui/transition/ScriptedStickerSet.hx
Original file line number Diff line number Diff line change
@@ -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 {}
Loading
Loading