From 336810b628631b0e1dcb67b25774bac1ce815395 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 4 Jan 2024 10:00:39 -0500 Subject: [PATCH 01/11] Tooltips when hovering over chart events --- hmm.json | 4 +- source/funkin/data/event/SongEventData.hx | 109 ++++++++++++++---- source/funkin/data/song/SongData.hx | 61 ++++++++++ .../funkin/play/event/FocusCameraSongEvent.hx | 4 +- .../play/event/PlayAnimationSongEvent.hx | 4 +- .../play/event/SetCameraBopSongEvent.hx | 4 +- .../funkin/play/event/ZoomCameraSongEvent.hx | 4 +- .../ui/debug/charting/ChartEditorState.hx | 3 +- .../components/ChartEditorEventSprite.hx | 42 ++++++- source/funkin/util/HaxeUIUtil.hx | 17 +++ 10 files changed, 217 insertions(+), 35 deletions(-) create mode 100644 source/funkin/util/HaxeUIUtil.hx diff --git a/hmm.json b/hmm.json index 57fbbb555..be9e2dd26 100644 --- a/hmm.json +++ b/hmm.json @@ -54,14 +54,14 @@ "name": "haxeui-core", "type": "git", "dir": null, - "ref": "e765a3e0b7a653823e8dec765e04623f27f573f8", + "ref": "67c5700e253ff8892589a95945a7799f34ae4df0", "url": "https://github.com/haxeui/haxeui-core" }, { "name": "haxeui-flixel", "type": "git", "dir": null, - "ref": "7a517d561eff49d8123c128bf9f5c1123b84d014", + "ref": "2b9cff727999b53ed292b1675ac1c9089ac77600", "url": "https://github.com/haxeui/haxeui-flixel" }, { diff --git a/source/funkin/data/event/SongEventData.hx b/source/funkin/data/event/SongEventData.hx index 7a167b031..a4a41e3a0 100644 --- a/source/funkin/data/event/SongEventData.hx +++ b/source/funkin/data/event/SongEventData.hx @@ -161,35 +161,71 @@ class SongEventParser } } -enum abstract SongEventFieldType(String) from String to String +@:forward(name, title, type, keys, min, max, step, defaultValue, iterator) +abstract SongEventSchema(SongEventSchemaRaw) { - /** - * The STRING type will display as a text field. - */ - var STRING = "string"; + public function new(?fields:Array) + { + this = fields; + } - /** - * The INTEGER type will display as a text field that only accepts numbers. - */ - var INTEGER = "integer"; + @:arrayAccess + public function getByName(name:String):SongEventSchemaField + { + for (field in this) + { + if (field.name == name) return field; + } - /** - * The FLOAT type will display as a text field that only accepts numbers. - */ - var FLOAT = "float"; + return null; + } - /** - * The BOOL type will display as a checkbox. - */ - var BOOL = "bool"; + public function getFirstField():SongEventSchemaField + { + return this[0]; + } - /** - * The ENUM type will display as a dropdown. - * Make sure to specify the `keys` field in the schema. - */ - var ENUM = "enum"; + public function stringifyFieldValue(name:String, value:Dynamic):String + { + var field:SongEventSchemaField = getByName(name); + if (field == null) return 'Unknown'; + + switch (field.type) + { + case SongEventFieldType.STRING: + return Std.string(value); + case SongEventFieldType.INTEGER: + return Std.string(value); + case SongEventFieldType.FLOAT: + return Std.string(value); + case SongEventFieldType.BOOL: + return Std.string(value); + case SongEventFieldType.ENUM: + for (key in field.keys.keys()) + { + if (field.keys.get(key) == value) return key; + } + return Std.string(value); + default: + return 'Unknown'; + } + } + + @: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 = { /** @@ -240,4 +276,31 @@ typedef SongEventSchemaField = ?defaultValue:Dynamic, } -typedef SongEventSchema = Array; +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 600871e2f..de73cd957 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -1,5 +1,8 @@ package funkin.data.song; +import funkin.play.event.SongEvent; +import funkin.data.event.SongEventData.SongEventParser; +import funkin.data.event.SongEventData.SongEventSchema; import funkin.data.song.SongRegistry; import thx.semver.Version; @@ -617,6 +620,38 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR this = new SongEventDataRaw(time, event, value); } + public inline function valueAsStruct(?defaultKey:String = "key"):Dynamic + { + if (this.value == null) return {}; + if (Std.isOfType(this.value, Array)) + { + var result:haxe.DynamicAccess = {}; + result.set(defaultKey, this.value); + return cast result; + } + 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 getHandler():Null + { + return SongEventParser.getEvent(this.event); + } + + public inline function getSchema():Null + { + return SongEventParser.getEventSchema(this.event); + } + public inline function getDynamic(key:String):Null { return this.value == null ? null : Reflect.field(this.value, key); @@ -662,6 +697,32 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR return this.value == null ? null : cast Reflect.field(this.value, key); } + public function buildTooltip():String + { + var eventHandler = getHandler(); + var eventSchema = getSchema(); + + if (eventSchema == null) return 'Unknown Event: ${this.event}'; + + var result = '${eventHandler.getTitle()}'; + + var defaultKey = eventSchema.getFirstField()?.name; + var valueStruct:haxe.DynamicAccess = valueAsStruct(defaultKey); + + for (pair in valueStruct.keyValueIterator()) + { + var key = pair.key; + var value = pair.value; + + var title = eventSchema.getByName(key)?.title ?? 'UnknownField'; + var valueStr = eventSchema.stringifyFieldValue(key, value); + + result += '\n- ${title}: ${valueStr}'; + } + + return result; + } + public function clone():SongEventData { return new SongEventData(this.time, this.event, this.value); diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 5f63254b0..c91769eb5 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -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..0f611874b 100644 --- a/source/funkin/play/event/PlayAnimationSongEvent.hx +++ b/source/funkin/play/event/PlayAnimationSongEvent.hx @@ -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..7d5fd4699 100644 --- a/source/funkin/play/event/SetCameraBopSongEvent.hx +++ b/source/funkin/play/event/SetCameraBopSongEvent.hx @@ -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/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index 1ae76039e..9a361f71b 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -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/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 4f96fad69..5c12e3408 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2113,7 +2113,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState add(gridGhostHoldNote); gridGhostHoldNote.zIndex = 11; - gridGhostEvent = new ChartEditorEventSprite(this); + gridGhostEvent = new ChartEditorEventSprite(this, true); gridGhostEvent.alpha = 0.6; gridGhostEvent.eventData = new SongEventData(-1, '', {}); gridGhostEvent.visible = false; @@ -3127,6 +3127,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Setting event data resets position relative to the grid so we fix that. eventSprite.x += renderedEvents.x; eventSprite.y += renderedEvents.y; + eventSprite.updateTooltipPosition(); } // Add hold notes that have been made visible (but not their parents) diff --git a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx index 4c9d91407..cc9acf344 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx @@ -11,6 +11,9 @@ import flixel.graphics.frames.FlxFramesCollection; import flixel.graphics.frames.FlxTileFrames; import flixel.math.FlxPoint; import funkin.data.song.SongData.SongEventData; +import haxe.ui.tooltips.ToolTipRegionOptions; +import funkin.util.HaxeUIUtil; +import haxe.ui.tooltips.ToolTipManager; /** * A sprite that can be used to display a song event in a chart. @@ -36,6 +39,13 @@ class ChartEditorEventSprite extends FlxSprite public var overrideStepTime(default, set):Null = null; + public var tooltip:ToolTipRegionOptions; + + /** + * Whether this sprite is a "ghost" sprite used when hovering to place a new event. + */ + public var isGhost:Bool = false; + function set_overrideStepTime(value:Null):Null { if (overrideStepTime == value) return overrideStepTime; @@ -45,12 +55,14 @@ class ChartEditorEventSprite extends FlxSprite return overrideStepTime; } - public function new(parent:ChartEditorState) + public function new(parent:ChartEditorState, isGhost:Bool = false) { super(); this.parentState = parent; + this.isGhost = isGhost; + this.tooltip = HaxeUIUtil.buildTooltip('N/A'); this.frames = buildFrames(); buildAnimations(); @@ -140,6 +152,7 @@ class ChartEditorEventSprite extends FlxSprite // Disown parent. MAKE SURE TO REVIVE BEFORE REUSING this.kill(); this.visible = false; + updateTooltipPosition(); return null; } else @@ -151,6 +164,8 @@ class ChartEditorEventSprite extends FlxSprite this.eventData = value; // Update the position to match the note data. updateEventPosition(); + // Update the tooltip text. + this.tooltip.tipData = {text: this.eventData.buildTooltip()}; return this.eventData; } } @@ -169,6 +184,31 @@ class ChartEditorEventSprite extends FlxSprite this.x += origin.x; this.y += origin.y; } + + this.updateTooltipPosition(); + } + + public function updateTooltipPosition():Void + { + // No tooltip for ghost sprites. + if (this.isGhost) return; + + if (this.eventData == null) + { + // Disable the tooltip. + ToolTipManager.instance.unregisterTooltipRegion(this.tooltip); + } + else + { + // Update the position. + this.tooltip.left = this.x; + this.tooltip.top = this.y; + this.tooltip.width = this.width; + this.tooltip.height = this.height; + + // Enable the tooltip. + ToolTipManager.instance.registerTooltipRegion(this.tooltip); + } } /** diff --git a/source/funkin/util/HaxeUIUtil.hx b/source/funkin/util/HaxeUIUtil.hx new file mode 100644 index 000000000..1ffd9cd40 --- /dev/null +++ b/source/funkin/util/HaxeUIUtil.hx @@ -0,0 +1,17 @@ +package funkin.util; + +import haxe.ui.tooltips.ToolTipRegionOptions; + +class HaxeUIUtil +{ + public static function buildTooltip(text:String, ?left:Float, ?top:Float, ?width:Float, ?height:Float):ToolTipRegionOptions + { + return { + tipData: {text: text}, + left: left ?? 0.0, + top: top ?? 0.0, + width: width ?? 0.0, + height: height ?? 0.0 + } + } +} From e44b028946e5d98c5798ec5aee0502c54fbac968 Mon Sep 17 00:00:00 2001 From: Jenny Crowe Date: Wed, 17 Jan 2024 16:24:03 -0700 Subject: [PATCH 02/11] Added side sliders that alter the volume of vocals and hitsounds on player/opponent sides. --- assets | 2 +- .../ui/debug/charting/ChartEditorState.hx | 88 ++++++++++++++++++- 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/assets b/assets index d094640f7..0e9019f0f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d094640f727a670a348b3579d11af5ff6a2ada3a +Subproject commit 0e9019f0fcb53f3e554604ea9a4e62d381873d1f diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index e98809ce8..5fa5308e2 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -104,6 +104,7 @@ import haxe.ui.components.Label; import haxe.ui.components.Button; import haxe.ui.components.NumberStepper; import haxe.ui.components.Slider; +import haxe.ui.components.VerticalSlider; import haxe.ui.components.TextField; import haxe.ui.containers.dialogs.CollapsibleDialog; import haxe.ui.containers.Frame; @@ -720,6 +721,34 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState return hitsoundsEnabledPlayer || hitsoundsEnabledOpponent; } + /** + * Sound multiplier for vocals and hitsounds on the player's side. + */ + var soundMultiplierPlayer(default, set):Float = 1.0; + + function set_soundMultiplierPlayer(value:Float):Float + { + soundMultiplierPlayer = value; + var vocalTargetVolume:Float = (menubarItemVolumeVocals.value ?? 100.0) / 100.0; + if (audioVocalTrackGroup != null) audioVocalTrackGroup.playerVolume = vocalTargetVolume * soundMultiplierPlayer; + + return soundMultiplierPlayer; + } + + /** + * Sound multiplier for vocals and hitsounds on the opponent's side. + */ + var soundMultiplierOpponent(default, set):Float = 1.0; + + function set_soundMultiplierOpponent(value:Float):Float + { + soundMultiplierOpponent = value; + var vocalTargetVolume:Float = (menubarItemVolumeVocals.value ?? 100.0) / 100.0; + if (audioVocalTrackGroup != null) audioVocalTrackGroup.opponentVolume = vocalTargetVolume * soundMultiplierOpponent; + + return soundMultiplierOpponent; + } + // Auto-save /** @@ -1749,6 +1778,18 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var buttonSelectEvent:Button; + /** + * The slider above the grid that sets the volume of the player's sounds. + * Constructed manually and added to the layout so we can control its position. + */ + var sliderVolumePlayer:Slider; + + /** + * The slider above the grid that sets the volume of the opponent's sounds. + * Constructed manually and added to the layout so we can control its position. + */ + var sliderVolumeOpponent:Slider; + /** * RENDER OBJECTS */ @@ -2557,6 +2598,37 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState performCommand(new SetItemSelectionCommand([], currentSongChartEventData)); } } + + function setupSideSlider(x, y):VerticalSlider + { + var slider = new VerticalSlider(); + slider.allowFocus = false; + slider.x = x; + slider.y = y; + slider.width = NOTE_SELECT_BUTTON_HEIGHT; + slider.height = GRID_SIZE * 4; + slider.pos = slider.max; + slider.tooltip = "Slide to set the volume of sounds on this side."; + slider.zIndex = 110; + slider.styleNames = "sideSlider"; + add(slider); + + return slider; + } + + var sliderY = GRID_INITIAL_Y_POS + 34; + sliderVolumeOpponent = setupSideSlider(GRID_X_POS - 64, sliderY); + sliderVolumePlayer = setupSideSlider(buttonSelectEvent.x + buttonSelectEvent.width, sliderY); + + sliderVolumePlayer.onChange = event -> { + var volume:Float = event.value.toFloat() / 100.0; + soundMultiplierPlayer = volume; + } + + sliderVolumeOpponent.onChange = event -> { + var volume:Float = event.value.toFloat() / 100.0; + soundMultiplierOpponent = volume; + } } /** @@ -2797,7 +2869,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState menubarItemVolumeVocals.onChange = event -> { var volume:Float = event.value.toFloat() / 100.0; - if (audioVocalTrackGroup != null) audioVocalTrackGroup.volume = volume; + if (audioVocalTrackGroup != null) + { + audioVocalTrackGroup.playerVolume = volume * soundMultiplierPlayer; + audioVocalTrackGroup.opponentVolume = volume * soundMultiplierOpponent; + } menubarLabelVolumeVocals.text = 'Voices - ${Std.int(event.value)}%'; } @@ -5662,7 +5738,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState audioInstTrack.volume = instTargetVolume; audioInstTrack.onComplete = null; } - if (audioVocalTrackGroup != null) audioVocalTrackGroup.volume = vocalTargetVolume; + if (audioVocalTrackGroup != null) + { + audioVocalTrackGroup.playerVolume = vocalTargetVolume * soundMultiplierPlayer; + audioVocalTrackGroup.opponentVolume = vocalTargetVolume * soundMultiplierOpponent; + } } function updateTimeSignature():Void @@ -5864,9 +5944,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState switch (noteData.getStrumlineIndex()) { case 0: // Player - if (hitsoundsEnabledPlayer) this.playSound(Paths.sound('chartingSounds/hitNotePlayer'), hitsoundVolume); + if (hitsoundsEnabledPlayer) this.playSound(Paths.sound('chartingSounds/hitNotePlayer'), hitsoundVolume * soundMultiplierPlayer); case 1: // Opponent - if (hitsoundsEnabledOpponent) this.playSound(Paths.sound('chartingSounds/hitNoteOpponent'), hitsoundVolume); + if (hitsoundsEnabledOpponent) this.playSound(Paths.sound('chartingSounds/hitNoteOpponent'), hitsoundVolume * soundMultiplierOpponent); } } } From 027c2843f4958158e5ae74deca7499c2160ac91f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 17 Jan 2024 22:19:40 -0500 Subject: [PATCH 03/11] This bug took me like 4-5 hours of staring at code to fix i am going crazy graaaa --- .../ui/debug/charting/ChartEditorState.hx | 25 ++++++++++--------- .../handlers/ChartEditorThemeHandler.hx | 6 +++++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index e98809ce8..5e8f112dd 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1958,7 +1958,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState buildGrid(); buildMeasureTicks(); buildNotePreview(); - buildSelectionBox(); buildAdditionalUI(); populateOpenRecentMenu(); @@ -2287,17 +2286,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState setNotePreviewViewportBounds(calculateNotePreviewViewportBounds()); } - function buildSelectionBox():Void - { - if (selectionBoxSprite == null) throw 'ERROR: Tried to build selection box, but selectionBoxSprite is null! Check ChartEditorThemeHandler.updateTheme().'; - - selectionBoxSprite.scrollFactor.set(0, 0); - add(selectionBoxSprite); - selectionBoxSprite.zIndex = 30; - - setSelectionBoxBounds(); - } - function setSelectionBoxBounds(bounds:FlxRect = null):Void { if (selectionBoxSprite == null) @@ -2319,6 +2307,19 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } } + /** + * Automatically goes through and calls render on everything you added. + */ + override public function draw():Void + { + if (selectionBoxStartPos != null) + { + trace('selectionBoxSprite: ${selectionBoxSprite.visible} ${selectionBoxSprite.exists} ${this.members.contains(selectionBoxSprite)}'); + } + + super.draw(); + } + function calculateNotePreviewViewportBounds():FlxRect { var bounds:FlxRect = new FlxRect(); diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorThemeHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorThemeHandler.hx index 98bb5c2c8..89fd4d5d3 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorThemeHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorThemeHandler.hx @@ -317,6 +317,12 @@ class ChartEditorThemeHandler ChartEditorState.GRID_SIZE - (2 * SELECTION_SQUARE_BORDER_WIDTH + 8)), 32, 32); + + state.selectionBoxSprite.scrollFactor.set(0, 0); + state.selectionBoxSprite.zIndex = 30; + state.add(state.selectionBoxSprite); + + state.setSelectionBoxBounds(); } static function updateNotePreview(state:ChartEditorState):Void From a0c4499b03bb7c14fc2bf9bb0d78313617a54c28 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 17 Jan 2024 23:12:59 -0500 Subject: [PATCH 04/11] Fix a bug where replaying a level makes a pink screen --- source/funkin/play/PlayState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 20f19f714..cc9debf13 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1354,6 +1354,7 @@ class PlayState extends MusicBeatSubState function loadStage(id:String):Void { currentStage = StageRegistry.instance.fetchEntry(id); + currentStage.revive(); // Stages are killed and props destroyed when the PlayState is destroyed to save memory. if (currentStage != null) { From 26b761066303aa0fb1ab2e71689235ce9456baaa Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 17 Jan 2024 23:14:28 -0500 Subject: [PATCH 05/11] Fix an error with playable Pico death --- assets | 2 +- source/funkin/play/GameOverSubState.hx | 24 +++++++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/assets b/assets index 9e385784b..d6be0e084 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 9e385784b1d2f4332de0d696b1df655cfa269da0 +Subproject commit d6be0e084e4fda0416eca9ec7fe406af9b626e5c diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index 88d9be7d4..36f72237e 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -23,6 +23,12 @@ import funkin.play.character.BaseCharacter; */ class GameOverSubState extends MusicBeatSubState { + /** + * The currently active GameOverSubState. + * There should be only one GameOverSubState in existance at a time, we can use a singleton. + */ + public static var instance:GameOverSubState = null; + /** * Which alternate animation on the character to use. * You can set this via script. @@ -88,6 +94,13 @@ class GameOverSubState extends MusicBeatSubState override public function create() { + if (instance != null) + { + // TODO: Do something in this case? IDK. + trace('WARNING: GameOverSubState instance already exists. This should not happen.'); + } + instance = this; + super.create(); // @@ -283,10 +296,10 @@ class GameOverSubState extends MusicBeatSubState */ function startDeathMusic(?startingVolume:Float = 1, force:Bool = false):Void { - var musicPath = Paths.music('gameOver' + musicSuffix); + var musicPath = Paths.music('gameplay/gameover/gameOver' + musicSuffix); if (isEnding) { - musicPath = Paths.music('gameOverEnd' + musicSuffix); + musicPath = Paths.music('gameplay/gameover/gameOverEnd' + musicSuffix); } if (!gameOverMusic.playing || force) { @@ -306,7 +319,7 @@ class GameOverSubState extends MusicBeatSubState public static function playBlueBalledSFX() { blueballed = true; - FlxG.sound.play(Paths.sound('fnf_loss_sfx' + blueBallSuffix)); + FlxG.sound.play(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix)); } var playingJeffQuote:Bool = false; @@ -329,6 +342,11 @@ class GameOverSubState extends MusicBeatSubState } }); } + + public override function toString():String + { + return "GameOverSubState"; + } } typedef GameOverParams = From 3be0a0c4061edf97322936a44eba357ae2bc7055 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 18 Jan 2024 00:18:32 -0500 Subject: [PATCH 06/11] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 0e9019f0f..3d0c0aca0 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0e9019f0fcb53f3e554604ea9a4e62d381873d1f +Subproject commit 3d0c0aca06f0032aeca88a750b0ed9422e4afb07 From ad7c16238e331aa8b352b5cb4f9c9abab2b2fbfb Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 18 Jan 2024 00:24:25 -0500 Subject: [PATCH 07/11] assets --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 9e385784b..3d0c0aca0 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 9e385784b1d2f4332de0d696b1df655cfa269da0 +Subproject commit 3d0c0aca06f0032aeca88a750b0ed9422e4afb07 From 4919d893c80a377faf67ff9a7b620c8d30af8533 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 18 Jan 2024 00:53:06 -0500 Subject: [PATCH 08/11] submod merg --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index d6be0e084..5d28d3631 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d6be0e084e4fda0416eca9ec7fe406af9b626e5c +Subproject commit 5d28d36311d95d17ec9bddaca479b0f8db088a7c From 3ef7a030944fbcb245db12b817f16dd957a33f53 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 18 Jan 2024 01:06:15 -0500 Subject: [PATCH 09/11] Add "Remove Difficulty" button to the Difficulty toolbox. --- assets | 2 +- .../ui/debug/charting/ChartEditorState.hx | 175 ++++--------- .../handlers/ChartEditorToolboxHandler.hx | 87 +------ .../toolboxes/ChartEditorDifficultyToolbox.hx | 239 ++++++++++++++++++ 4 files changed, 288 insertions(+), 215 deletions(-) create mode 100644 source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx diff --git a/assets b/assets index 5d28d3631..e0a1446ae 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5d28d36311d95d17ec9bddaca479b0f8db088a7c +Subproject commit e0a1446ae956b15a20bf039de0c46a59b8e242bb diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 71354ef87..e49c9c3a8 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -35,6 +35,7 @@ import funkin.data.song.SongData.SongCharacterData; import funkin.data.song.SongData.SongChartData; import funkin.data.song.SongData.SongEventData; import funkin.data.song.SongData.SongMetadata; +import funkin.ui.debug.charting.toolboxes.ChartEditorDifficultyToolbox; import funkin.data.song.SongData.SongNoteData; import funkin.data.song.SongData.SongOffsets; import funkin.data.song.SongDataUtils; @@ -4731,48 +4732,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { difficultySelectDirty = false; - // Manage the Select Difficulty tree view. - var difficultyToolbox:Null = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); + var difficultyToolbox:ChartEditorDifficultyToolbox = cast this.getToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); if (difficultyToolbox == null) return; - var treeView:Null = difficultyToolbox.findComponent('difficultyToolboxTree'); - if (treeView == null) return; - - // Clear the tree view so we can rebuild it. - treeView.clearNodes(); - - // , icon: 'haxeui-core/styles/default/haxeui_tiny.png' - var treeSong:TreeViewNode = treeView.addNode({id: 'stv_song', text: 'S: $currentSongName'}); - treeSong.expanded = true; - - for (curVariation in availableVariations) - { - trace('DIFFICULTY TOOLBOX: Variation ${curVariation}'); - var variationMetadata:Null = songMetadata.get(curVariation); - if (variationMetadata == null) continue; - - var treeVariation:TreeViewNode = treeSong.addNode( - { - id: 'stv_variation_$curVariation', - text: 'V: ${curVariation.toTitleCase()}' - }); - treeVariation.expanded = true; - - var difficultyList:Array = variationMetadata.playData.difficulties; - - for (difficulty in difficultyList) - { - trace('DIFFICULTY TOOLBOX: Difficulty ${curVariation}_$difficulty'); - var _treeDifficulty:TreeViewNode = treeVariation.addNode( - { - id: 'stv_difficulty_${curVariation}_$difficulty', - text: 'D: ${difficulty.toTitleCase()}' - }); - } - } - - treeView.onChange = onChangeTreeDifficulty; - refreshDifficultyTreeSelection(treeView); + difficultyToolbox.updateTree(); } } @@ -5517,7 +5480,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState return event != null && currentEventSelection.indexOf(event) != -1; } - function createDifficulty(variation:String, difficulty:String, scrollSpeed:Float = 1.0) + function createDifficulty(variation:String, difficulty:String, scrollSpeed:Float = 1.0):Void { var variationMetadata:Null = songMetadata.get(variation); if (variationMetadata == null) return; @@ -5539,6 +5502,42 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState difficultySelectDirty = true; // Force the Difficulty toolbox to update. } + function removeDifficulty(variation:String, difficulty:String):Void + { + var variationMetadata:Null = songMetadata.get(variation); + if (variationMetadata == null) return; + + variationMetadata.playData.difficulties.remove(difficulty); + + var resultChartData = songChartData.get(variation); + if (resultChartData != null) + { + resultChartData.scrollSpeed.remove(difficulty); + resultChartData.notes.remove(difficulty); + } + + if (songMetadata.size() > 1) + { + if (variationMetadata.playData.difficulties.length == 0) + { + songMetadata.remove(variation); + songChartData.remove(variation); + } + + if (variation == selectedVariation) + { + var firstVariation = songMetadata.keyValues()[0]; + if (firstVariation != null) selectedVariation = firstVariation; + variationMetadata = songMetadata.get(selectedVariation); + } + } + + if (selectedDifficulty == difficulty + || !variationMetadata.playData.difficulties.contains(selectedDifficulty)) selectedDifficulty = variationMetadata.playData.difficulties[0]; + + difficultySelectDirty = true; // Force the Difficulty toolbox to update. + } + function incrementDifficulty(change:Int):Void { var currentDifficultyIndex:Int = availableDifficulties.indexOf(selectedDifficulty); @@ -5587,8 +5586,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState Conductor.instance.mapTimeChanges(this.currentSongMetadata.timeChanges); updateTimeSignature(); - refreshDifficultyTreeSelection(); this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT); + this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); } else { @@ -5596,8 +5595,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState var prevDifficulty = availableDifficulties[currentDifficultyIndex - 1]; selectedDifficulty = prevDifficulty; - refreshDifficultyTreeSelection(); this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT); + this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); } } else @@ -5615,8 +5614,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState var nextDifficulty = availableDifficulties[0]; selectedDifficulty = nextDifficulty; - refreshDifficultyTreeSelection(); this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT); + this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); } else { @@ -5624,7 +5623,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState var nextDifficulty = availableDifficulties[currentDifficultyIndex + 1]; selectedDifficulty = nextDifficulty; - refreshDifficultyTreeSelection(); + this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT); } } @@ -5760,92 +5759,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ // ================== - /** - * Set the currently selected item in the Difficulty tree view to the node representing the current difficulty. - * @param treeView The tree view to update. If `null`, the tree view will be found. - */ - function refreshDifficultyTreeSelection(?treeView:TreeView):Void - { - if (treeView == null) - { - // Manage the Select Difficulty tree view. - var difficultyToolbox:Null = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); - if (difficultyToolbox == null) return; - - treeView = difficultyToolbox.findComponent('difficultyToolboxTree'); - if (treeView == null) return; - } - - var currentTreeDifficultyNode = getCurrentTreeDifficultyNode(treeView); - if (currentTreeDifficultyNode != null) treeView.selectedNode = currentTreeDifficultyNode; - } - - /** - * Retrieve the node representing the current difficulty in the Difficulty tree view. - * @param treeView The tree view to search. If `null`, the tree view will be found. - * @return The node representing the current difficulty, or `null` if not found. - */ - function getCurrentTreeDifficultyNode(?treeView:TreeView = null):Null - { - if (treeView == null) - { - var difficultyToolbox:Null = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); - if (difficultyToolbox == null) return null; - - treeView = difficultyToolbox.findComponent('difficultyToolboxTree'); - if (treeView == null) return null; - } - - var result:TreeViewNode = treeView.findNodeByPath('stv_song/stv_variation_$selectedVariation/stv_difficulty_${selectedVariation}_$selectedDifficulty', - 'id'); - if (result == null) return null; - - return result; - } - - /** - * Called when selecting a tree element in the Difficulty toolbox. - * @param event The click event. - */ - function onChangeTreeDifficulty(event:UIEvent):Void - { - // Get the newly selected node. - var treeView:TreeView = cast event.target; - var targetNode:TreeViewNode = treeView.selectedNode; - - if (targetNode == null) - { - trace('No target node!'); - // Reset the user's selection. - var currentTreeDifficultyNode = getCurrentTreeDifficultyNode(treeView); - if (currentTreeDifficultyNode != null) treeView.selectedNode = currentTreeDifficultyNode; - return; - } - - switch (targetNode.data.id.split('_')[1]) - { - case 'difficulty': - var variation:String = targetNode.data.id.split('_')[2]; - var difficulty:String = targetNode.data.id.split('_')[3]; - - if (variation != null && difficulty != null) - { - trace('Changing difficulty to "$variation:$difficulty"'); - selectedVariation = variation; - selectedDifficulty = difficulty; - this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT); - } - // case 'song': - // case 'variation': - default: - // Reset the user's selection. - trace('Selected wrong node type, resetting selection.'); - var currentTreeDifficultyNode = getCurrentTreeDifficultyNode(treeView); - if (currentTreeDifficultyNode != null) treeView.selectedNode = currentTreeDifficultyNode; - this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT); - } - } - /** * STATIC FUNCTIONS */ diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx index 1916f92c2..f97b26933 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx @@ -36,6 +36,7 @@ import haxe.ui.containers.dialogs.Dialog.DialogEvent; import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox; import funkin.ui.debug.charting.toolboxes.ChartEditorMetadataToolbox; import funkin.ui.debug.charting.toolboxes.ChartEditorEventDataToolbox; +import funkin.ui.debug.charting.toolboxes.ChartEditorDifficultyToolbox; import haxe.ui.containers.Frame; import haxe.ui.containers.Grid; import haxe.ui.containers.TreeView; @@ -84,7 +85,7 @@ class ChartEditorToolboxHandler case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT: onShowToolboxPlaytestProperties(state, toolbox); case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT: - onShowToolboxDifficulty(state, toolbox); + cast(toolbox, ChartEditorBaseToolbox).refresh(); case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT: // TODO: Fix this. cast(toolbox, ChartEditorBaseToolbox).refresh(); @@ -123,8 +124,6 @@ class ChartEditorToolboxHandler onHideToolboxEventData(state, toolbox); case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT: onHideToolboxPlaytestProperties(state, toolbox); - case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT: - onHideToolboxDifficulty(state, toolbox); case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT: onHideToolboxMetadata(state, toolbox); case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT: @@ -309,8 +308,6 @@ class ChartEditorToolboxHandler static function onHideToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {} - static function onHideToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void {} - static function onShowToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {} static function onHideToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {} @@ -358,91 +355,15 @@ class ChartEditorToolboxHandler return toolbox; } - static function buildToolboxDifficultyLayout(state:ChartEditorState):Null + static function buildToolboxDifficultyLayout(state:ChartEditorState):Null { - var toolbox:CollapsibleDialog = cast RuntimeComponentBuilder.fromAsset(ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); + var toolbox:ChartEditorBaseToolbox = ChartEditorDifficultyToolbox.build(state); if (toolbox == null) return null; - // Starting position. - toolbox.x = 125; - toolbox.y = 200; - - toolbox.onDialogClosed = function(event:UIEvent) { - state.menubarItemToggleToolboxDifficulty.selected = false; - } - - var difficultyToolboxAddVariation:Null