From edc6f85e214b88427f789c867f573cb970a36dde Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 3 Jan 2024 21:10:14 -0500 Subject: [PATCH] Finish up event editing (selecting an existing event now shows its data in the event data toolbox) --- source/funkin/InitState.hx | 4 +- ...{SongEventData.hx => SongEventRegistry.hx} | 109 +-------------- source/funkin/data/event/SongEventSchema.hx | 125 ++++++++++++++++++ source/funkin/data/song/SongData.hx | 20 ++- source/funkin/modding/PolymodHandler.hx | 4 +- source/funkin/play/PlayState.hx | 8 +- .../funkin/play/event/FocusCameraSongEvent.hx | 8 +- .../play/event/PlayAnimationSongEvent.hx | 8 +- .../play/event/SetCameraBopSongEvent.hx | 8 +- source/funkin/play/event/SongEvent.hx | 2 +- .../funkin/play/event/ZoomCameraSongEvent.hx | 8 +- .../charting/commands/SelectItemsCommand.hx | 18 ++- .../commands/SetItemSelectionCommand.hx | 18 ++- .../components/ChartEditorEventSprite.hx | 6 +- .../handlers/ChartEditorToolboxHandler.hx | 2 +- .../toolboxes/ChartEditorEventDataToolbox.hx | 8 +- 16 files changed, 210 insertions(+), 146 deletions(-) rename source/funkin/data/event/{SongEventData.hx => SongEventRegistry.hx} (66%) create mode 100644 source/funkin/data/event/SongEventSchema.hx diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 6c00b6f68..02b46c88c 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -19,7 +19,7 @@ import funkin.play.PlayStatePlaylist; import openfl.display.BitmapData; import funkin.data.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; -import funkin.data.event.SongEventData.SongEventParser; +import funkin.data.event.SongEventRegistry; import funkin.play.cutscene.dialogue.ConversationDataParser; import funkin.play.cutscene.dialogue.DialogueBoxDataParser; import funkin.play.cutscene.dialogue.SpeakerDataParser; @@ -213,7 +213,7 @@ class InitState extends FlxState SongRegistry.instance.loadEntries(); LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); - SongEventParser.loadEventCache(); + SongEventRegistry.loadEventCache(); ConversationDataParser.loadConversationCache(); DialogueBoxDataParser.loadDialogueBoxCache(); SpeakerDataParser.loadSpeakerCache(); diff --git a/source/funkin/data/event/SongEventData.hx b/source/funkin/data/event/SongEventRegistry.hx similarity index 66% rename from source/funkin/data/event/SongEventData.hx rename to source/funkin/data/event/SongEventRegistry.hx index 522b9314e..dc5589813 100644 --- a/source/funkin/data/event/SongEventData.hx +++ b/source/funkin/data/event/SongEventRegistry.hx @@ -1,7 +1,7 @@ package funkin.data.event; import funkin.play.event.SongEvent; -import funkin.data.event.SongEventData.SongEventSchema; +import funkin.data.event.SongEventSchema; import funkin.data.song.SongData.SongEventData; import funkin.util.macro.ClassMacro; import funkin.play.event.ScriptedSongEvent; @@ -9,7 +9,7 @@ import funkin.play.event.ScriptedSongEvent; /** * This class statically handles the parsing of internal and scripted song event handlers. */ -class SongEventParser +class SongEventRegistry { /** * Every built-in event class must be added to this list. @@ -160,108 +160,3 @@ class SongEventParser } } } - -enum abstract SongEventFieldType(String) from String to String -{ - /** - * The STRING type will display as a text field. - */ - var STRING = "string"; - - /** - * The INTEGER type will display as a text field that only accepts numbers. - */ - var INTEGER = "integer"; - - /** - * The FLOAT type will display as a text field that only accepts numbers. - */ - var FLOAT = "float"; - - /** - * The BOOL type will display as a checkbox. - */ - var BOOL = "bool"; - - /** - * The ENUM type will display as a dropdown. - * Make sure to specify the `keys` field in the schema. - */ - var ENUM = "enum"; -} - -typedef SongEventSchemaField = -{ - /** - * The name of the property as it should be saved in the event data. - */ - name:String, - - /** - * The title of the field to display in the UI. - */ - title:String, - - /** - * The type of the field. - */ - type:SongEventFieldType, - - /** - * Used only for ENUM values. - * The key is the display name and the value is the actual value. - */ - ?keys:Map, - - /** - * Used for INTEGER and FLOAT values. - * The minimum value that can be entered. - * @default No minimum - */ - ?min:Float, - - /** - * Used for INTEGER and FLOAT values. - * The maximum value that can be entered. - * @default No maximum - */ - ?max:Float, - - /** - * Used for INTEGER and FLOAT values. - * The step value that will be used when incrementing/decrementing the value. - * @default `0.1` - */ - ?step:Float, - - /** - * An optional default value for the field. - */ - ?defaultValue:Dynamic, -} - -@:forward -abstract SongEventSchema(SongEventSchemaRaw) -{ - public function new(?fields:Array) - { - this = fields; - } - - public function getByName(name:String):SongEventSchemaField - { - for (field in this) - { - if (field.name == name) return field; - } - - return null; - } - - public function getFirstField():SongEventSchemaField - { - return this[0]; - } -} - -typedef SongEventSchemaRaw = Array; diff --git a/source/funkin/data/event/SongEventSchema.hx b/source/funkin/data/event/SongEventSchema.hx new file mode 100644 index 000000000..b5b2978d7 --- /dev/null +++ b/source/funkin/data/event/SongEventSchema.hx @@ -0,0 +1,125 @@ +package funkin.data.event; + +import funkin.play.event.SongEvent; +import funkin.data.event.SongEventSchema; +import funkin.data.song.SongData.SongEventData; +import funkin.util.macro.ClassMacro; +import funkin.play.event.ScriptedSongEvent; + +@:forward(name, tittlte, type, keys, min, max, step, defaultValue, iterator) +abstract SongEventSchema(SongEventSchemaRaw) +{ + public function new(?fields:Array) + { + this = fields; + } + + @:arrayAccess + public inline function getByName(name:String):SongEventSchemaField + { + for (field in this) + { + if (field.name == name) return field; + } + + return null; + } + + public function getFirstField():SongEventSchemaField + { + return this[0]; + } + + @:arrayAccess + public inline function get(key:Int) + { + return this[key]; + } + + @:arrayAccess + public inline function arrayWrite(k:Int, v:SongEventSchemaField):SongEventSchemaField + { + return this[k] = v; + } +} + +typedef SongEventSchemaRaw = Array; + +typedef SongEventSchemaField = +{ + /** + * The name of the property as it should be saved in the event data. + */ + name:String, + + /** + * The title of the field to display in the UI. + */ + title:String, + + /** + * The type of the field. + */ + type:SongEventFieldType, + + /** + * Used only for ENUM values. + * The key is the display name and the value is the actual value. + */ + ?keys:Map, + + /** + * Used for INTEGER and FLOAT values. + * The minimum value that can be entered. + * @default No minimum + */ + ?min:Float, + + /** + * Used for INTEGER and FLOAT values. + * The maximum value that can be entered. + * @default No maximum + */ + ?max:Float, + + /** + * Used for INTEGER and FLOAT values. + * The step value that will be used when incrementing/decrementing the value. + * @default `0.1` + */ + ?step:Float, + + /** + * An optional default value for the field. + */ + ?defaultValue:Dynamic, +} + +enum abstract SongEventFieldType(String) from String to String +{ + /** + * The STRING type will display as a text field. + */ + var STRING = "string"; + + /** + * The INTEGER type will display as a text field that only accepts numbers. + */ + var INTEGER = "integer"; + + /** + * The FLOAT type will display as a text field that only accepts numbers. + */ + var FLOAT = "float"; + + /** + * The BOOL type will display as a checkbox. + */ + var BOOL = "bool"; + + /** + * The ENUM type will display as a dropdown. + * Make sure to specify the `keys` field in the schema. + */ + var ENUM = "enum"; +} diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 90a5f47c3..de25f7f3e 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -1,5 +1,7 @@ package funkin.data.song; +import funkin.data.event.SongEventRegistry; +import funkin.data.event.SongEventSchema; import funkin.data.song.SongRegistry; import thx.semver.Version; @@ -620,18 +622,28 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR public inline function valueAsStruct(?defaultKey:String = "key"):Dynamic { if (this.value == null) return {}; - // TODO: How to check if it's a dynamic struct? - if (Std.isOfType(this.value, Int) || Std.isOfType(this.value, String) || Std.isOfType(this.value, Float) || Std.isOfType(this.value, Bool) - || Std.isOfType(this.value, Array)) + if (Std.isOfType(this.value, Array)) { var result:haxe.DynamicAccess = {}; result.set(defaultKey, this.value); return cast result; } - else + else if (Reflect.isObject(this.value)) { + // We enter this case if the value is a struct. return cast this.value; } + else + { + var result:haxe.DynamicAccess = {}; + result.set(defaultKey, this.value); + return cast result; + } + } + + public inline function getSchema():Null + { + return SongEventRegistry.getEventSchema(this.event); } public inline function getDynamic(key:String):Null diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 7716f0f02..b7ef07be5 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -8,7 +8,7 @@ import funkin.play.stage.StageData; import polymod.Polymod; import polymod.backends.PolymodAssets.PolymodAssetType; import polymod.format.ParseRules.TextFileFormat; -import funkin.data.event.SongEventData.SongEventParser; +import funkin.data.event.SongEventRegistry; import funkin.util.FileUtil; import funkin.data.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; @@ -271,7 +271,7 @@ class PolymodHandler SongRegistry.instance.loadEntries(); LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); - SongEventParser.loadEventCache(); + SongEventRegistry.loadEventCache(); ConversationDataParser.loadConversationCache(); DialogueBoxDataParser.loadDialogueBoxCache(); SpeakerDataParser.loadSpeakerCache(); diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 3dcabf953..dbf1b97e1 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -42,7 +42,7 @@ import funkin.play.cutscene.dialogue.Conversation; import funkin.play.cutscene.dialogue.ConversationDataParser; import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VideoCutscene; -import funkin.data.event.SongEventData.SongEventParser; +import funkin.data.event.SongEventRegistry; import funkin.play.notes.NoteSprite; import funkin.play.notes.NoteDirection; import funkin.play.notes.Strumline; @@ -942,7 +942,7 @@ class PlayState extends MusicBeatSubState // TODO: Check that these work even when songPosition is less than 0. if (songEvents != null && songEvents.length > 0) { - var songEventsToActivate:Array = SongEventParser.queryEvents(songEvents, Conductor.songPosition); + var songEventsToActivate:Array = SongEventRegistry.queryEvents(songEvents, Conductor.songPosition); if (songEventsToActivate.length > 0) { @@ -961,7 +961,7 @@ class PlayState extends MusicBeatSubState // Calling event.cancelEvent() skips the event. Neat! if (!eventEvent.eventCanceled) { - SongEventParser.handleEvent(event); + SongEventRegistry.handleEvent(event); } } } @@ -1607,7 +1607,7 @@ class PlayState extends MusicBeatSubState // Reset song events. songEvents = currentChart.getEvents(); - SongEventParser.resetEvents(songEvents); + SongEventRegistry.resetEvents(songEvents); // Reset the notes on each strumline. var playerNoteData:Array = []; diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 5f63254b0..83c978ba8 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -5,8 +5,8 @@ import funkin.data.song.SongData; import funkin.data.song.SongData.SongEventData; // Data from the event schema import funkin.play.event.SongEvent; -import funkin.data.event.SongEventData.SongEventSchema; -import funkin.data.event.SongEventData.SongEventFieldType; +import funkin.data.event.SongEventSchema; +import funkin.data.event.SongEventSchema.SongEventFieldType; /** * This class represents a handler for a type of song event. @@ -132,7 +132,7 @@ class FocusCameraSongEvent extends SongEvent */ public override function getEventSchema():SongEventSchema { - return [ + return new SongEventSchema([ { name: "char", title: "Character", @@ -154,6 +154,6 @@ class FocusCameraSongEvent extends SongEvent step: 10.0, type: SongEventFieldType.FLOAT, } - ]; + ]); } } diff --git a/source/funkin/play/event/PlayAnimationSongEvent.hx b/source/funkin/play/event/PlayAnimationSongEvent.hx index 6bc625517..4e6669479 100644 --- a/source/funkin/play/event/PlayAnimationSongEvent.hx +++ b/source/funkin/play/event/PlayAnimationSongEvent.hx @@ -7,8 +7,8 @@ import funkin.data.song.SongData; import funkin.data.song.SongData.SongEventData; // Data from the event schema import funkin.play.event.SongEvent; -import funkin.data.event.SongEventData.SongEventSchema; -import funkin.data.event.SongEventData.SongEventFieldType; +import funkin.data.event.SongEventSchema; +import funkin.data.event.SongEventSchema.SongEventFieldType; class PlayAnimationSongEvent extends SongEvent { @@ -89,7 +89,7 @@ class PlayAnimationSongEvent extends SongEvent */ public override function getEventSchema():SongEventSchema { - return [ + return new SongEventSchema([ { name: 'target', title: 'Target', @@ -108,6 +108,6 @@ class PlayAnimationSongEvent extends SongEvent type: SongEventFieldType.BOOL, defaultValue: false } - ]; + ]); } } diff --git a/source/funkin/play/event/SetCameraBopSongEvent.hx b/source/funkin/play/event/SetCameraBopSongEvent.hx index 3cdeb9a67..d0e01346f 100644 --- a/source/funkin/play/event/SetCameraBopSongEvent.hx +++ b/source/funkin/play/event/SetCameraBopSongEvent.hx @@ -8,8 +8,8 @@ import funkin.data.song.SongData; import funkin.data.song.SongData.SongEventData; // Data from the event schema import funkin.play.event.SongEvent; -import funkin.data.event.SongEventData.SongEventSchema; -import funkin.data.event.SongEventData.SongEventFieldType; +import funkin.data.event.SongEventSchema; +import funkin.data.event.SongEventSchema.SongEventFieldType; /** * This class represents a handler for configuring camera bop intensity and rate. @@ -72,7 +72,7 @@ class SetCameraBopSongEvent extends SongEvent */ public override function getEventSchema():SongEventSchema { - return [ + return new SongEventSchema([ { name: 'intensity', title: 'Intensity', @@ -87,6 +87,6 @@ class SetCameraBopSongEvent extends SongEvent step: 1, type: SongEventFieldType.INTEGER, } - ]; + ]); } } diff --git a/source/funkin/play/event/SongEvent.hx b/source/funkin/play/event/SongEvent.hx index 36a886673..29b394c0e 100644 --- a/source/funkin/play/event/SongEvent.hx +++ b/source/funkin/play/event/SongEvent.hx @@ -1,7 +1,7 @@ package funkin.play.event; import funkin.data.song.SongData.SongEventData; -import funkin.data.event.SongEventData.SongEventSchema; +import funkin.data.event.SongEventSchema; /** * This class represents a handler for a type of song event. diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index 1ae76039e..182cac108 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -8,8 +8,8 @@ import funkin.data.song.SongData; import funkin.data.song.SongData.SongEventData; // Data from the event schema import funkin.play.event.SongEvent; -import funkin.data.event.SongEventData.SongEventFieldType; -import funkin.data.event.SongEventData.SongEventSchema; +import funkin.data.event.SongEventSchema; +import funkin.data.event.SongEventSchema.SongEventFieldType; /** * This class represents a handler for camera zoom events. @@ -99,7 +99,7 @@ class ZoomCameraSongEvent extends SongEvent */ public override function getEventSchema():SongEventSchema { - return [ + return new SongEventSchema([ { name: 'zoom', title: 'Zoom Level', @@ -145,6 +145,6 @@ class ZoomCameraSongEvent extends SongEvent 'Elastic In/Out' => 'elasticInOut', ] } - ]; + ]); } } diff --git a/source/funkin/ui/debug/charting/commands/SelectItemsCommand.hx b/source/funkin/ui/debug/charting/commands/SelectItemsCommand.hx index f59672646..49b2ba585 100644 --- a/source/funkin/ui/debug/charting/commands/SelectItemsCommand.hx +++ b/source/funkin/ui/debug/charting/commands/SelectItemsCommand.hx @@ -37,9 +37,25 @@ class SelectItemsCommand implements ChartEditorCommand if (this.notes.length == 0 && this.events.length >= 1) { var eventSelected = this.events[0]; + state.eventKindToPlace = eventSelected.event; - var eventData = eventSelected.valueAsStruct(); + + // This code is here to parse event data that's not built as a struct for some reason. + // TODO: Clean this up or get rid of it. + var eventSchema = eventSelected.getSchema(); + var defaultKey = null; + if (eventSchema == null) + { + trace('[WARNING] Event schema not found for event ${eventSelected.event}.'); + } + else + { + defaultKey = eventSchema.getFirstField()?.name; + } + var eventData = eventSelected.valueAsStruct(defaultKey); + state.eventDataToPlace = eventData; + state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT); } diff --git a/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx b/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx index 7dc344835..4725fd275 100644 --- a/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx +++ b/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx @@ -34,9 +34,25 @@ class SetItemSelectionCommand implements ChartEditorCommand if (this.notes.length == 0 && this.events.length >= 1) { var eventSelected = this.events[0]; + state.eventKindToPlace = eventSelected.event; - var eventData = eventSelected.valueAsStruct(); + + // This code is here to parse event data that's not built as a struct for some reason. + // TODO: Clean this up or get rid of it. + var eventSchema = eventSelected.getSchema(); + var defaultKey = null; + if (eventSchema == null) + { + trace('[WARNING] Event schema not found for event ${eventSelected.event}.'); + } + else + { + defaultKey = eventSchema.getFirstField()?.name; + } + var eventData = eventSelected.valueAsStruct(defaultKey); + state.eventDataToPlace = eventData; + state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT); } diff --git a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx index e5bbf0807..79bcd59af 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx @@ -1,6 +1,6 @@ package funkin.ui.debug.charting.components; -import funkin.data.event.SongEventData.SongEventParser; +import funkin.data.event.SongEventRegistry; import flixel.graphics.frames.FlxAtlasFrames; import openfl.display.BitmapData; import openfl.utils.Assets; @@ -79,7 +79,7 @@ class ChartEditorEventSprite extends FlxSprite } // Push all the other events as frames. - for (eventName in SongEventParser.listEventIds()) + for (eventName in SongEventRegistry.listEventIds()) { var exists:Bool = Assets.exists(Paths.image('ui/chart-editor/events/$eventName')); if (!exists) continue; // No graphic for this event. @@ -105,7 +105,7 @@ class ChartEditorEventSprite extends FlxSprite function buildAnimations():Void { - var eventNames:Array = [DEFAULT_EVENT].concat(SongEventParser.listEventIds()); + var eventNames:Array = [DEFAULT_EVENT].concat(SongEventRegistry.listEventIds()); for (eventName in eventNames) { this.animation.addByPrefix(eventName, '${eventName}0', 24, false); diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx index 08298eb66..ff18a6966 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx @@ -9,7 +9,7 @@ import haxe.ui.containers.TreeView; import haxe.ui.containers.TreeViewNode; import funkin.play.character.BaseCharacter.CharacterType; import funkin.play.event.SongEvent; -import funkin.data.event.SongEventData; +import funkin.data.event.SongEventSchema; import funkin.data.song.SongData.SongTimeChange; import funkin.play.character.BaseCharacter.CharacterType; import funkin.play.character.CharacterData; diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx index 7066fc913..480873bc5 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx @@ -4,7 +4,7 @@ import funkin.play.character.BaseCharacter.CharacterType; import funkin.play.character.CharacterData; import funkin.play.stage.StageData; import funkin.play.event.SongEvent; -import funkin.data.event.SongEventData.SongEventSchema; +import funkin.data.event.SongEventSchema; import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import haxe.ui.components.Button; @@ -15,7 +15,7 @@ import haxe.ui.components.Label; import haxe.ui.components.NumberStepper; import haxe.ui.components.Slider; import haxe.ui.core.Component; -import funkin.data.event.SongEventData.SongEventParser; +import funkin.data.event.SongEventRegistry; import haxe.ui.components.TextField; import haxe.ui.containers.Box; import haxe.ui.containers.Frame; @@ -59,7 +59,7 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox { toolboxEventsEventKind.dataSource = new ArrayDataSource(); - var songEvents:Array = SongEventParser.listEvents(); + var songEvents:Array = SongEventRegistry.listEvents(); for (event in songEvents) { @@ -74,7 +74,7 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox // Edit the event data to place. chartEditorState.eventKindToPlace = eventType; - var schema:SongEventSchema = SongEventParser.getEventSchema(eventType); + var schema:SongEventSchema = SongEventRegistry.getEventSchema(eventType); if (schema == null) {