diff --git a/hmm.json b/hmm.json index 4086c92f4..c8b1d911e 100644 --- a/hmm.json +++ b/hmm.json @@ -54,7 +54,7 @@ "name": "haxeui-core", "type": "git", "dir": null, - "ref": "5b2d5b8e7e470cf637953e1369c80a1f42016a75", + "ref": "8a7846b", "url": "https://github.com/haxeui/haxeui-core" }, { diff --git a/source/funkin/data/event/SongEventSchema.hx b/source/funkin/data/event/SongEventSchema.hx index 7ebaa5ae1..9591e601e 100644 --- a/source/funkin/data/event/SongEventSchema.hx +++ b/source/funkin/data/event/SongEventSchema.hx @@ -6,9 +6,14 @@ 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) +@:forward(name, title, type, keys, min, max, step, units, defaultValue, iterator) abstract SongEventSchema(SongEventSchemaRaw) { + /** + * These units look better when placed immediately next to the value, rather than after a space. + */ + static final NO_SPACE_UNITS:Array = ['x', '°', '%']; + public function new(?fields:Array) { this = fields; @@ -42,7 +47,7 @@ abstract SongEventSchema(SongEventSchemaRaw) return this[k] = v; } - public function stringifyFieldValue(name:String, value:Dynamic):String + public function stringifyFieldValue(name:String, value:Dynamic, addUnits:Bool = true):String { var field:SongEventSchemaField = getByName(name); if (field == null) return 'Unknown'; @@ -52,21 +57,36 @@ abstract SongEventSchema(SongEventSchemaRaw) case SongEventFieldType.STRING: return Std.string(value); case SongEventFieldType.INTEGER: - return Std.string(value); + var returnValue:String = Std.string(value); + if (addUnits) return addUnitsToString(returnValue, field); + return returnValue; case SongEventFieldType.FLOAT: - return Std.string(value); + var returnValue:String = Std.string(value); + if (addUnits) return addUnitsToString(returnValue, field); + return returnValue; case SongEventFieldType.BOOL: return Std.string(value); case SongEventFieldType.ENUM: + var valueString:String = Std.string(value); for (key in field.keys.keys()) { - if (field.keys.get(key) == value) return key; + // Comparing these values as strings because comparing Dynamic variables is jank. + if (Std.string(field.keys.get(key)) == valueString) return key; } - return Std.string(value); + return valueString; default: return 'Unknown'; } } + + function addUnitsToString(value:String, field:SongEventSchemaField) + { + if (field.units == null || field.units == '') return value; + + var unit:String = field.units; + + return value + (NO_SPACE_UNITS.contains(unit) ? '' : ' ') + '${unit}'; + } } typedef SongEventSchemaRaw = Array; @@ -115,6 +135,12 @@ typedef SongEventSchemaField = */ ?step:Float, + /** + * Used for INTEGER and FLOAT values. + * The units that the value is expressed in (pixels, percent, etc). + */ + ?units:String, + /** * An optional default value for the field. */ diff --git a/source/funkin/data/song/SongDataUtils.hx b/source/funkin/data/song/SongDataUtils.hx index 275106f3a..01ea2da32 100644 --- a/source/funkin/data/song/SongDataUtils.hx +++ b/source/funkin/data/song/SongDataUtils.hx @@ -153,8 +153,8 @@ class SongDataUtils public static function buildNoteClipboard(notes:Array, ?timeOffset:Int = null):Array { if (notes.length == 0) return notes; - if (timeOffset == null) timeOffset = -Std.int(notes[0].time); - return offsetSongNoteData(sortNotes(notes), timeOffset); + if (timeOffset == null) timeOffset = Std.int(notes[0].time); + return offsetSongNoteData(sortNotes(notes), -timeOffset); } /** @@ -165,8 +165,8 @@ class SongDataUtils public static function buildEventClipboard(events:Array, ?timeOffset:Int = null):Array { if (events.length == 0) return events; - if (timeOffset == null) timeOffset = -Std.int(events[0].time); - return offsetSongEventData(sortEvents(events), timeOffset); + if (timeOffset == null) timeOffset = Std.int(events[0].time); + return offsetSongEventData(sortEvents(events), -timeOffset); } /** diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 83c978ba8..847df4a60 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -135,10 +135,10 @@ class FocusCameraSongEvent extends SongEvent return new SongEventSchema([ { name: "char", - title: "Character", + title: "Target", defaultValue: 0, type: SongEventFieldType.ENUM, - keys: ["Position" => -1, "Boyfriend" => 0, "Dad" => 1, "Girlfriend" => 2] + keys: ["Position" => -1, "Player" => 0, "Opponent" => 1, "Girlfriend" => 2] }, { name: "x", @@ -146,6 +146,7 @@ class FocusCameraSongEvent extends SongEvent defaultValue: 0, step: 10.0, type: SongEventFieldType.FLOAT, + units: "px" }, { name: "y", @@ -153,6 +154,7 @@ class FocusCameraSongEvent extends SongEvent defaultValue: 0, step: 10.0, type: SongEventFieldType.FLOAT, + units: "px" } ]); } diff --git a/source/funkin/play/event/SetCameraBopSongEvent.hx b/source/funkin/play/event/SetCameraBopSongEvent.hx index d0e01346f..a82577a5f 100644 --- a/source/funkin/play/event/SetCameraBopSongEvent.hx +++ b/source/funkin/play/event/SetCameraBopSongEvent.hx @@ -78,14 +78,16 @@ class SetCameraBopSongEvent extends SongEvent title: 'Intensity', defaultValue: 1.0, step: 0.1, - type: SongEventFieldType.FLOAT + type: SongEventFieldType.FLOAT, + units: 'x' }, { name: 'rate', - title: 'Rate (beats/zoom)', + title: 'Rate', defaultValue: 4, step: 1, type: SongEventFieldType.INTEGER, + units: 'beats/zoom' } ]); } diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index a35a12e1e..809130499 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -106,14 +106,16 @@ class ZoomCameraSongEvent extends SongEvent title: 'Zoom Level', defaultValue: 1.0, step: 0.1, - type: SongEventFieldType.FLOAT + type: SongEventFieldType.FLOAT, + units: 'x' }, { name: 'duration', - title: 'Duration (in steps)', + title: 'Duration', defaultValue: 4.0, step: 0.5, type: SongEventFieldType.FLOAT, + units: 'steps' }, { name: 'ease', diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 719f6aceb..d0326be30 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -688,6 +688,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var activeToolboxes:Map = new Map(); + /** + * The camera component we're using for this state. + */ + var uiCamera:FlxCamera; + // Audio /** @@ -2040,7 +2045,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState loadPreferences(); - fixCamera(); + uiCamera = new FlxCamera(); + FlxG.cameras.reset(uiCamera); buildDefaultSongData(); @@ -5297,7 +5303,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState Paths.setCurrentLevel('weekend1'); } - subStateClosed.add(fixCamera); + subStateClosed.add(reviveUICamera); subStateClosed.add(resetConductorAfterTest); FlxTransitionableState.skipNextTransIn = false; @@ -5322,6 +5328,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } targetState.vocals = audioVocalTrackGroup; + // Kill and replace the UI camera so it doesn't get destroyed during the state transition. + uiCamera.kill(); + FlxG.cameras.remove(uiCamera, false); + FlxG.cameras.reset(new FlxCamera()); + this.persistentUpdate = false; this.persistentDraw = false; stopWelcomeMusic(); @@ -5411,13 +5422,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } /** - * Fix a camera issue caused when closing the PlayState used when testing. + * Revive the UI camera and re-establish it as the main camera so UI elements depending on it don't explode. */ - function fixCamera(_:FlxSubState = null):Void + function reviveUICamera(_:FlxSubState = null):Void { - FlxG.cameras.reset(new FlxCamera()); - FlxG.camera.focusOn(new FlxPoint(FlxG.width / 2, FlxG.height / 2)); - FlxG.camera.zoom = 1.0; + uiCamera.revive(); + FlxG.cameras.reset(uiCamera); add(this.root); } diff --git a/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx b/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx index 4361f867f..6c5152a29 100644 --- a/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx +++ b/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx @@ -46,26 +46,14 @@ class CopyItemsCommand implements ChartEditorCommand function performVisuals(state:ChartEditorState):Void { + var hasNotes:Bool = false; + var hasEvents:Bool = false; + + // Wiggle copied notes. if (state.currentNoteSelection.length > 0) { - // Display the "Copied Notes" text. - if (state.txtCopyNotif != null) - { - state.txtCopyNotif.visible = true; - state.txtCopyNotif.text = "Copied " + state.currentNoteSelection.length + " notes to clipboard"; - state.txtCopyNotif.x = FlxG.mouse.x - (state.txtCopyNotif.width / 2); - state.txtCopyNotif.y = FlxG.mouse.y - 16; - FlxTween.tween(state.txtCopyNotif, {y: state.txtCopyNotif.y - 32}, 0.5, - { - type: FlxTween.ONESHOT, - ease: FlxEase.quadOut, - onComplete: function(_) { - state.txtCopyNotif.visible = false; - } - }); - } + hasNotes = true; - // Wiggle the notes. for (note in state.renderedNotes.members) { if (state.isNoteSelected(note.noteData)) @@ -91,8 +79,13 @@ class CopyItemsCommand implements ChartEditorCommand }); } } + } + + // Wiggle copied events. + if (state.currentEventSelection.length > 0) + { + hasEvents = true; - // Wiggle the events. for (event in state.renderedEvents.members) { if (state.isEventSelected(event.eventData)) @@ -119,6 +112,39 @@ class CopyItemsCommand implements ChartEditorCommand } } } + + // Display the "Copied Notes" text. + if ((hasNotes || hasEvents) && state.txtCopyNotif != null) + { + var copiedString:String = ''; + if (hasNotes) + { + var copiedNotes:Int = state.currentNoteSelection.length; + copiedString += '${copiedNotes} note'; + if (copiedNotes > 1) copiedString += 's'; + + if (hasEvents) copiedString += ' and '; + } + if (hasEvents) + { + var copiedEvents:Int = state.currentEventSelection.length; + copiedString += '${state.currentEventSelection.length} event'; + if (copiedEvents > 1) copiedString += 's'; + } + + state.txtCopyNotif.visible = true; + state.txtCopyNotif.text = 'Copied ${copiedString} to clipboard'; + state.txtCopyNotif.x = FlxG.mouse.x - (state.txtCopyNotif.width / 2); + state.txtCopyNotif.y = FlxG.mouse.y - 16; + FlxTween.tween(state.txtCopyNotif, {y: state.txtCopyNotif.y - 32}, 0.5, + { + type: FlxTween.ONESHOT, + ease: FlxEase.quadOut, + onComplete: function(_) { + state.txtCopyNotif.visible = false; + } + }); + } } public function undo(state:ChartEditorState):Void diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx index fbd1562b4..7b163ad3d 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx @@ -18,6 +18,7 @@ import haxe.ui.core.Component; import funkin.data.event.SongEventRegistry; import haxe.ui.components.TextField; import haxe.ui.containers.Box; +import haxe.ui.containers.HBox; import haxe.ui.containers.Frame; import haxe.ui.events.UIEvent; import haxe.ui.data.ArrayDataSource; @@ -214,7 +215,20 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox input.text = field.type; } - target.addComponent(input); + // Putting in a box so we can add a unit label easily if there is one. + var inputBox:HBox = new HBox(); + inputBox.addComponent(input); + + // Add a unit label if applicable. + if (field.units != null && field.units != "") + { + var units:Label = new Label(); + units.text = field.units; + units.verticalAlign = "center"; + inputBox.addComponent(units); + } + + target.addComponent(inputBox); // Update the value of the event data. input.onChange = function(event:UIEvent) {