diff --git a/Project.xml b/Project.xml index 69400d8b1..a1772a9ef 100644 --- a/Project.xml +++ b/Project.xml @@ -165,7 +165,10 @@ + + +
diff --git a/assets b/assets index ef79a6cf1..6b6f2462a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit ef79a6cf1ae3dcbd86a5b798f8117a6c692c0156 +Subproject commit 6b6f2462afb099a7301a782ae521a0175fb7c71b diff --git a/source/funkin/MusicBeatState.hx b/source/funkin/MusicBeatState.hx index 9a986a8b5..b045fdc24 100644 --- a/source/funkin/MusicBeatState.hx +++ b/source/funkin/MusicBeatState.hx @@ -56,27 +56,45 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler Conductor.stepHit.remove(this.stepHit); } - override function update(elapsed:Float) + function handleControls():Void { - super.update(elapsed); + var isHaxeUIFocused:Bool = haxe.ui.focus.FocusManager.instance?.focus != null; - // Rebindable volume keys. - if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted(); - else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1); - else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1); + if (!isHaxeUIFocused) + { + // Rebindable volume keys. + if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted(); + else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1); + else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1); + } + } + function handleFunctionControls():Void + { // Emergency exit button. if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState()); // This can now be used in EVERY STATE YAY! if (FlxG.keys.justPressed.F5) debug_refreshModules(); + } + function handleQuickWatch():Void + { // Display Conductor info in the watch window. FlxG.watch.addQuick("songPosition", Conductor.songPosition); FlxG.watch.addQuick("bpm", Conductor.bpm); FlxG.watch.addQuick("currentMeasureTime", Conductor.currentBeatTime); FlxG.watch.addQuick("currentBeatTime", Conductor.currentBeatTime); FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime); + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + handleControls(); + handleFunctionControls(); + handleQuickWatch(); dispatchEvent(new UpdateScriptEvent(elapsed)); } diff --git a/source/funkin/data/event/SongEventData.hx b/source/funkin/data/event/SongEventData.hx index 831a53fbd..7a167b031 100644 --- a/source/funkin/data/event/SongEventData.hx +++ b/source/funkin/data/event/SongEventData.hx @@ -208,25 +208,32 @@ typedef SongEventSchemaField = type:SongEventFieldType, /** - * Used for ENUM values. + * 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. */ diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 88993e519..eac4a4cb5 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -516,12 +516,22 @@ class SongEventData public inline function getInt(key:String):Null { - return value == null ? null : cast Reflect.field(value, key); + if (value == null) return null; + var result = Reflect.field(value, key); + if (result == null) return null; + if (Std.isOfType(result, Int)) return result; + if (Std.isOfType(result, String)) return Std.parseInt(cast result); + return cast result; } public inline function getFloat(key:String):Null { - return value == null ? null : cast Reflect.field(value, key); + if (value == null) return null; + var result = Reflect.field(value, key); + if (result == null) return null; + if (Std.isOfType(result, Float)) return result; + if (Std.isOfType(result, String)) return Std.parseFloat(cast result); + return cast result; } public inline function getString(key:String):String diff --git a/source/funkin/import.hx b/source/funkin/import.hx index 1c3a0fdb4..62241c4f4 100644 --- a/source/funkin/import.hx +++ b/source/funkin/import.hx @@ -12,6 +12,7 @@ using Lambda; using StringTools; using funkin.util.tools.ArraySortTools; using funkin.util.tools.ArrayTools; +using funkin.util.tools.DynamicTools; using funkin.util.tools.Int64Tools; using funkin.util.tools.IteratorTools; using funkin.util.tools.MapTools; diff --git a/source/funkin/ui/debug/charting/ChartEditorEventSprite.hx b/source/funkin/ui/debug/charting/ChartEditorEventSprite.hx index 021abde0f..d5d81ae29 100644 --- a/source/funkin/ui/debug/charting/ChartEditorEventSprite.hx +++ b/source/funkin/ui/debug/charting/ChartEditorEventSprite.hx @@ -123,21 +123,25 @@ class ChartEditorEventSprite extends FlxSprite function set_eventData(value:Null):Null { - this.eventData = value; - - if (this.eventData == null) + if (value == null) { + this.eventData = null; // Disown parent. MAKE SURE TO REVIVE BEFORE REUSING this.kill(); + this.visible = false; + return null; + } + else + { + this.visible = true; + // Only play the animation if the event type has changed. + // if (this.eventData == null || this.eventData.event != value.event) + playAnimation(value.event); + this.eventData = value; + // Update the position to match the note data. + updateEventPosition(); return this.eventData; } - - this.visible = true; - playAnimation(this.eventData.event); - // Update the position to match the note data. - updateEventPosition(); - - return this.eventData; } public function updateEventPosition(?origin:FlxObject) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 05173726f..c421c08f2 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -71,6 +71,7 @@ import haxe.ui.core.Component; import haxe.ui.core.Screen; import haxe.ui.events.DragEvent; import haxe.ui.events.UIEvent; +import haxe.ui.focus.FocusManager; import haxe.ui.notifications.NotificationManager; import haxe.ui.notifications.NotificationType; import openfl.Assets; @@ -492,22 +493,14 @@ class ChartEditorState extends HaxeUIState var hitsoundsEnabledOpponent:Bool = true; /** - * Whether the user's mouse cursor is hovering over a SOLID component of the HaxeUI. - * If so, ignore mouse events underneath. + * Whether the user is focused on an input in the Haxe UI, and inputs are being fed into it. + * If the user clicks off the input, focus will leave. */ - var isCursorOverHaxeUI(get, never):Bool; + var isHaxeUIFocused(get, never):Bool; - function get_isCursorOverHaxeUI():Bool + function get_isHaxeUIFocused():Bool { - return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY); - } - - var isCursorOverHaxeUIButton(get, never):Bool; - - function get_isCursorOverHaxeUIButton():Bool - { - return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY, haxe.ui.components.Button) - || Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY, haxe.ui.components.Link); + return FocusManager.instance.focus != null; } /** @@ -1905,11 +1898,11 @@ class ChartEditorState extends HaxeUIState handleHelpKeybinds(); #if debug - handleQuickWatch(); + handleQuickWatches(); #end } - function handleQuickWatch():Void + function handleQuickWatches():Void { FlxG.watch.addQuick('scrollPosInPixels', scrollPositionInPixels); FlxG.watch.addQuick('playheadPosInPixels', playheadPositionInPixels); @@ -1957,8 +1950,8 @@ class ChartEditorState extends HaxeUIState **/ function handleScrollKeybinds():Void { - // Don't scroll when the cursor is over the UI, unless a playbar button (the << >> ones) is pressed. - if (isCursorOverHaxeUI && playbarButtonPressed == null) return; + // Don't scroll when the user is interacting with the UI, unless a playbar button (the << >> ones) is pressed. + if (isHaxeUIFocused && playbarButtonPressed == null) return; var scrollAmount:Float = 0; // Amount to scroll the grid. var playheadAmount:Float = 0; // Amount to scroll the playhead relative to the grid. @@ -2157,7 +2150,7 @@ class ChartEditorState extends HaxeUIState if (FlxG.mouse.justReleased) FlxG.sound.play(Paths.sound("chartingSounds/ClickUp")); // Note: If a menu is open in HaxeUI, don't handle cursor behavior. - var shouldHandleCursor:Bool = !isCursorOverHaxeUI || (selectionBoxStartPos != null); + var shouldHandleCursor:Bool = !isHaxeUIFocused || (selectionBoxStartPos != null); var eventColumn:Int = (STRUMLINE_SIZE * 2 + 1) - 1; if (shouldHandleCursor) @@ -2612,14 +2605,14 @@ class ChartEditorState extends HaxeUIState { // Create an event and place it in the chart. // TODO: Figure out configuring event data. - var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData); + var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData.clone()); performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL)); } else { // Create a note and place it in the chart. - var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind); + var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind.clone()); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); @@ -3392,7 +3385,7 @@ class ChartEditorState extends HaxeUIState */ function handleTestKeybinds():Void { - if (!isHaxeUIDialogOpen && !isCursorOverHaxeUI && FlxG.keys.justPressed.ENTER) + if (!isHaxeUIDialogOpen && !isHaxeUIFocused && FlxG.keys.justPressed.ENTER) { var minimal = FlxG.keys.pressed.SHIFT; ChartEditorToolboxHandler.hideAllToolboxes(this); @@ -3889,7 +3882,7 @@ class ChartEditorState extends HaxeUIState } } - if (FlxG.keys.justPressed.SPACE && !isHaxeUIDialogOpen) + if (FlxG.keys.justPressed.SPACE && !(isHaxeUIDialogOpen || isHaxeUIFocused)) { toggleAudioPlayback(); } diff --git a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx index 7cee1edde..fb0981bae 100644 --- a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx @@ -346,6 +346,8 @@ class ChartEditorToolboxHandler trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType'); + state.selectedEventKind = eventType; + var schema:SongEventSchema = SongEventParser.getEventSchema(eventType); if (schema == null) @@ -356,6 +358,7 @@ class ChartEditorToolboxHandler buildEventDataFormFromSchema(state, toolboxEventsDataGrid, schema); } + toolboxEventsEventKind.value = state.selectedEventKind; return toolbox; } @@ -379,6 +382,7 @@ class ChartEditorToolboxHandler // Add a label. var label:Label = new Label(); label.text = field.title; + label.verticalAlign = "center"; target.addComponent(label); var input:Component; @@ -396,8 +400,8 @@ class ChartEditorToolboxHandler var numberStepper:NumberStepper = new NumberStepper(); numberStepper.id = field.name; numberStepper.step = field.step ?? 0.1; - numberStepper.min = field.min ?? 0.0; - numberStepper.max = field.max ?? 1.0; + if (field.min != null) numberStepper.min = field.min; + if (field.max != null) numberStepper.max = field.max; if (field.defaultValue != null) numberStepper.value = field.defaultValue; input = numberStepper; case BOOL: @@ -416,7 +420,7 @@ class ChartEditorToolboxHandler for (optionName in field.keys.keys()) { - var optionValue:Null = field.keys.get(optionName); + var optionValue:Null = field.keys.get(optionName); trace('$optionName : $optionValue'); dropDown.dataSource.add({value: optionValue, text: optionName}); } @@ -438,11 +442,21 @@ class ChartEditorToolboxHandler target.addComponent(input); input.onChange = function(event:UIEvent) { - trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${event.target.value}'); + var value = event.target.value; + if (field.type == ENUM) + { + value = event.target.value.value; + } + trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${value}'); - if (event.target.value == null) state.selectedEventData.remove(event.target.id); + if (value == null) + { + state.selectedEventData.remove(event.target.id); + } else - state.selectedEventData.set(event.target.id, event.target.value); + { + state.selectedEventData.set(event.target.id, value); + } } } } diff --git a/source/funkin/util/tools/DynamicTools.hx b/source/funkin/util/tools/DynamicTools.hx new file mode 100644 index 000000000..47501ea22 --- /dev/null +++ b/source/funkin/util/tools/DynamicTools.hx @@ -0,0 +1,14 @@ +package funkin.util.tools; + +class DynamicTools +{ + /** + * Creates a full clone of the input `Dynamic`. Only guaranteed to work on anonymous structures. + * @param input The `Dynamic` to clone. + * @return A clone of the input `Dynamic`. + */ + public static function clone(input:Dynamic):Dynamic + { + return Reflect.copy(input); + } +}