From 9ff4885173e42dacf35097a6df52891a72aaeefa Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 11 Oct 2023 19:39:52 -0400 Subject: [PATCH 01/11] charting sound effects in progress! --- assets | 2 +- .../ui/debug/charting/ChartEditorCommand.hx | 16 ++++++++++------ .../ui/debug/charting/ChartEditorState.hx | 18 +++++++++++++++++- .../charting/ChartEditorToolboxHandler.hx | 4 ++++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/assets b/assets index 6e5ed4602..b767f2d43 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6e5ed46026a2eb1e575c5accf9192b90c13ff857 +Subproject commit b767f2d43bf357e0d092fdf255a28963fe42cdff diff --git a/source/funkin/ui/debug/charting/ChartEditorCommand.hx b/source/funkin/ui/debug/charting/ChartEditorCommand.hx index c358c1d3d..ccefea67d 100644 --- a/source/funkin/ui/debug/charting/ChartEditorCommand.hx +++ b/source/funkin/ui/debug/charting/ChartEditorCommand.hx @@ -64,7 +64,7 @@ class AddNotesCommand implements ChartEditorCommand state.currentEventSelection = []; } - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08')); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteLay')); state.saveDataDirty = true; state.noteDisplayDirty = true; @@ -78,7 +78,7 @@ class AddNotesCommand implements ChartEditorCommand state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes); state.currentNoteSelection = []; state.currentEventSelection = []; - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01')); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo')); state.saveDataDirty = true; state.noteDisplayDirty = true; @@ -131,7 +131,7 @@ class RemoveNotesCommand implements ChartEditorCommand } state.currentNoteSelection = notes; state.currentEventSelection = []; - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08')); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo')); state.saveDataDirty = true; state.noteDisplayDirty = true; @@ -252,7 +252,7 @@ class AddEventsCommand implements ChartEditorCommand state.currentEventSelection = events; } - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08')); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteLay')); state.saveDataDirty = true; state.noteDisplayDirty = true; @@ -312,7 +312,7 @@ class RemoveEventsCommand implements ChartEditorCommand state.currentSongChartEventData.push(event); } state.currentEventSelection = events; - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08')); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo')); state.saveDataDirty = true; state.noteDisplayDirty = true; @@ -376,7 +376,7 @@ class RemoveItemsCommand implements ChartEditorCommand state.currentNoteSelection = notes; state.currentEventSelection = events; - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08')); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo')); state.saveDataDirty = true; state.noteDisplayDirty = true; @@ -777,6 +777,8 @@ class PasteItemsCommand implements ChartEditorCommand public function undo(state:ChartEditorState):Void { + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo')); + state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, addedNotes); state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, addedEvents); state.currentNoteSelection = []; @@ -829,6 +831,8 @@ class ExtendNoteLengthCommand implements ChartEditorCommand public function undo(state:ChartEditorState):Void { + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo')); + note.length = oldLength; state.saveDataDirty = true; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 67e3c1dc8..277079e31 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2096,11 +2096,18 @@ class ChartEditorState extends HaxeUIState } } + var dragLengthCurrent:Float = 0; + var stretchySounds:Bool = false; + /** * Handle display of the mouse cursor. */ function handleCursor():Void { + // Mouse sounds + if (FlxG.mouse.justPressed) FlxG.sound.play(Paths.sound("chartingSounds/ClickDown")); + 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 eventColumn:Int = (STRUMLINE_SIZE * 2 + 1) - 1; @@ -2449,6 +2456,14 @@ class ChartEditorState extends HaxeUIState if (dragLengthSteps > 0) { + if (dragLengthCurrent != dragLengthSteps) + { + stretchySounds = !stretchySounds; + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/stretch' + (stretchySounds ? '1' : '2') + '_UI')); + + dragLengthCurrent = dragLengthSteps; + } + gridGhostHoldNote.visible = true; gridGhostHoldNote.noteData = gridGhostNote.noteData; gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection(); @@ -2466,6 +2481,7 @@ class ChartEditorState extends HaxeUIState { if (dragLengthSteps > 0) { + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/stretchSNAP_UI')); // Apply the new length. performCommand(new ExtendNoteLengthCommand(currentPlaceNoteData, dragLengthMs)); } @@ -4206,7 +4222,7 @@ class ChartEditorState extends HaxeUIState function playMetronomeTick(high:Bool = false):Void { - ChartEditorAudioHandler.playSound(Paths.sound('pianoStuff/piano-${high ? '001' : '008'}')); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/metronome${high ? '1' : '2'}')); } function isNoteSelected(note:Null):Bool diff --git a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx index 6f89b6b63..25418a74e 100644 --- a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx @@ -72,6 +72,8 @@ class ChartEditorToolboxHandler { toolbox.showDialog(false); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/openWindow')); + switch (id) { case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT: @@ -109,6 +111,8 @@ class ChartEditorToolboxHandler { toolbox.hideDialog(DialogButton.CANCEL); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/exitWindow')); + switch (id) { case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT: From 0f08b1133e67c1f7bcc7d5a905e218b7ba595dad Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 14 Oct 2023 22:49:24 -0400 Subject: [PATCH 02/11] Rename some sounds used by the chart editor --- assets | 2 +- source/funkin/ui/debug/charting/ChartEditorCommand.hx | 8 +++++--- source/funkin/ui/debug/charting/ChartEditorState.hx | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/assets b/assets index b767f2d43..05973b6bb 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit b767f2d43bf357e0d092fdf255a28963fe42cdff +Subproject commit 05973b6bb816464b5cb46631285f17477d05cf08 diff --git a/source/funkin/ui/debug/charting/ChartEditorCommand.hx b/source/funkin/ui/debug/charting/ChartEditorCommand.hx index ccefea67d..e6caf61e7 100644 --- a/source/funkin/ui/debug/charting/ChartEditorCommand.hx +++ b/source/funkin/ui/debug/charting/ChartEditorCommand.hx @@ -114,7 +114,8 @@ class RemoveNotesCommand implements ChartEditorCommand state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes); state.currentNoteSelection = []; state.currentEventSelection = []; - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01')); + + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase')); state.saveDataDirty = true; state.noteDisplayDirty = true; @@ -296,7 +297,8 @@ class RemoveEventsCommand implements ChartEditorCommand { state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events); state.currentEventSelection = []; - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01')); + + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase')); state.saveDataDirty = true; state.noteDisplayDirty = true; @@ -352,7 +354,7 @@ class RemoveItemsCommand implements ChartEditorCommand state.currentNoteSelection = []; state.currentEventSelection = []; - ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01')); + ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase')); state.saveDataDirty = true; state.noteDisplayDirty = true; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 277079e31..b23df1d3a 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3849,9 +3849,9 @@ class ChartEditorState extends HaxeUIState switch (noteData.getStrumlineIndex()) { case 0: // Player - if (hitsoundsEnabledPlayer) ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-09')); + if (hitsoundsEnabledPlayer) ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/hitNotePlayer')); case 1: // Opponent - if (hitsoundsEnabledOpponent) ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-010')); + if (hitsoundsEnabledOpponent) ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/hitNoteOpponent')); } } } From f51592963e96808c4959ead04ba6acb6670f22dc Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 17 Oct 2023 02:42:52 -0400 Subject: [PATCH 03/11] Fixes for a few bugs in the chart editor. --- assets | 2 +- source/funkin/play/HealthIcon.hx | 51 ++++++-- source/funkin/play/character/BaseCharacter.hx | 15 +-- .../ui/debug/charting/ChartEditorState.hx | 117 ++++++++++-------- .../ui/haxeui/components/FunkinClickLabel.hx | 30 +++++ source/funkin/util/Constants.hx | 5 + 6 files changed, 146 insertions(+), 74 deletions(-) create mode 100644 source/funkin/ui/haxeui/components/FunkinClickLabel.hx diff --git a/assets b/assets index 8104d43e5..b9338b972 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8104d43e584a1f25e574438d7b21a7e671358969 +Subproject commit b9338b97214f71b192f5cec760c5442ec2e8cbed diff --git a/source/funkin/play/HealthIcon.hx b/source/funkin/play/HealthIcon.hx index 7785fb4b1..5b9c8ec75 100644 --- a/source/funkin/play/HealthIcon.hx +++ b/source/funkin/play/HealthIcon.hx @@ -24,13 +24,14 @@ import openfl.utils.Assets; * - i.e. `PlayState.instance.iconP1.animation.addByPrefix("jumpscare", "jumpscare", 24, false);` * @author MasterEric */ +@:nullSafety class HealthIcon extends FlxSprite { /** * The character this icon is representing. * Setting this variable will automatically update the graphic. */ - public var characterId(default, set):String; + public var characterId(default, set):Null; /** * Whether this health icon should automatically update its state based on the character's health. @@ -123,13 +124,13 @@ class HealthIcon extends FlxSprite initTargetSize(); } - function set_characterId(value:String):String + function set_characterId(value:Null):Null { if (value == characterId) return value; - characterId = value; + characterId = value ?? Constants.DEFAULT_HEALTH_ICON; loadCharacter(characterId); - return value; + return characterId; } function set_isPixel(value:Bool):Bool @@ -138,7 +139,7 @@ class HealthIcon extends FlxSprite isPixel = value; loadCharacter(characterId); - return value; + return isPixel; } /** @@ -156,6 +157,32 @@ class HealthIcon extends FlxSprite } } + /** + * Use the provided CharacterHealthIconData to configure this health icon's appearance. + * @param data The data to use to configure this health icon. + */ + public function configure(data:Null):Void + { + if (data == null) + { + this.isPixel = false; + this.characterId = Constants.DEFAULT_HEALTH_ICON; + this.size.set(1.0, 1.0); + this.offset.x = 0.0; + this.offset.y = 0.0; + this.flipX = false; + } + else + { + this.isPixel = data.isPixel ?? false; + this.characterId = data.id; + this.size.set(data.scale ?? 1.0, data.scale ?? 1.0); + this.offset.x = (data.offsets != null) ? data.offsets[0] : 0.0; + this.offset.y = (data.offsets != null) ? data.offsets[1] : 0.0; + this.flipX = data.flipX ?? false; // Face the OTHER way by default, since that is more common. + } + } + /** * Called by Flixel every frame. Includes logic to manage the currently playing animation. */ @@ -341,12 +368,17 @@ class HealthIcon extends FlxSprite this.animation.add(Losing, [1], 0, false, false); } - function correctCharacterId(charId:String):String + function correctCharacterId(charId:Null):String { + if (charId == null) + { + return Constants.DEFAULT_HEALTH_ICON; + } + if (!Assets.exists(Paths.image('icons/icon-$charId'))) { FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!'); - return 'face'; + return Constants.DEFAULT_HEALTH_ICON; } return charId; @@ -357,10 +389,11 @@ class HealthIcon extends FlxSprite return Assets.exists(Paths.file('images/icons/icon-$characterId.xml')); } - function loadCharacter(charId:String):Void + function loadCharacter(charId:Null):Void { - if (correctCharacterId(charId) != charId) + if (charId == null || correctCharacterId(charId) != charId) { + // This will recursively trigger loadCharacter to be called again. characterId = correctCharacterId(charId); return; } diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 30b549fd3..5362df61c 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -312,12 +312,8 @@ class BaseCharacter extends Bopper trace('[WARN] Player 1 health icon not found!'); return; } - PlayState.instance.iconP1.isPixel = _data.healthIcon?.isPixel ?? false; - PlayState.instance.iconP1.characterId = _data.healthIcon.id; - PlayState.instance.iconP1.size.set(_data.healthIcon.scale, _data.healthIcon.scale); - PlayState.instance.iconP1.offset.x = _data.healthIcon.offsets[0]; - PlayState.instance.iconP1.offset.y = _data.healthIcon.offsets[1]; - PlayState.instance.iconP1.flipX = !_data.healthIcon.flipX; + PlayState.instance.iconP1.configure(_data.healthIcon); + PlayState.instance.iconP1.flipX = !PlayState.instance.iconP1.flipX; // BF is looking the other way. } else { @@ -326,12 +322,7 @@ class BaseCharacter extends Bopper trace('[WARN] Player 2 health icon not found!'); return; } - PlayState.instance.iconP2.isPixel = _data.healthIcon?.isPixel ?? false; - PlayState.instance.iconP2.characterId = _data.healthIcon.id; - PlayState.instance.iconP2.size.set(_data.healthIcon.scale, _data.healthIcon.scale); - PlayState.instance.iconP2.offset.x = _data.healthIcon.offsets[0]; - PlayState.instance.iconP2.offset.y = _data.healthIcon.offsets[1]; - PlayState.instance.iconP2.flipX = _data.healthIcon.flipX; + PlayState.instance.iconP2.configure(_data.healthIcon); } } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index b4c8c3483..43ab61d88 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -87,9 +87,8 @@ using Lambda; * * @author MasterEric */ +@:nullSafety // Give other classes access to private instance fields -// @:nullSafety(Loose) // Enable this while developing, then disable to keep unit tests functional! - @:allow(funkin.ui.debug.charting.ChartEditorCommand) @:allow(funkin.ui.debug.charting.ChartEditorDropdowns) @:allow(funkin.ui.debug.charting.ChartEditorDialogHandler) @@ -965,7 +964,7 @@ class ChartEditorState extends HaxeUIState function get_currentSongChartNoteData():Array { - var result:Array = currentSongChartData.notes.get(selectedDifficulty); + var result:Null> = currentSongChartData.notes.get(selectedDifficulty); if (result == null) { // Initialize to the default value if not set. @@ -1391,16 +1390,12 @@ class ChartEditorState extends HaxeUIState healthIconDad = new HealthIcon(currentSongMetadata.playData.characters.opponent); healthIconDad.autoUpdate = false; healthIconDad.size.set(0.5, 0.5); - healthIconDad.x = gridTiledSprite.x - 15 - (HealthIcon.HEALTH_ICON_SIZE * 0.5); - healthIconDad.y = gridTiledSprite.y + 5; add(healthIconDad); healthIconDad.zIndex = 30; healthIconBF = new HealthIcon(currentSongMetadata.playData.characters.player); healthIconBF.autoUpdate = false; healthIconBF.size.set(0.5, 0.5); - healthIconBF.x = gridTiledSprite.x + gridTiledSprite.width + 15; - healthIconBF.y = gridTiledSprite.y + 5; healthIconBF.flipX = true; add(healthIconBF); healthIconBF.zIndex = 30; @@ -1627,6 +1622,12 @@ class ChartEditorState extends HaxeUIState addUIClickListener('playbarForward', _ -> playbarButtonPressed = 'playbarForward'); addUIClickListener('playbarEnd', _ -> playbarButtonPressed = 'playbarEnd'); + // Cycle note snap quant. + addUIClickListener('playbarNoteSnap', function(_) { + noteSnapQuantIndex++; + if (noteSnapQuantIndex >= SNAP_QUANTS.length) noteSnapQuantIndex = 0; + }); + // Add functionality to the menu items. addUIClickListener('menubarItemNewChart', _ -> ChartEditorDialogHandler.openWelcomeDialog(this, true)); @@ -2477,19 +2478,22 @@ class ChartEditorState extends HaxeUIState var dragLengthMs:Float = dragLengthSteps * Conductor.stepLengthMs; var dragLengthPixels:Float = dragLengthSteps * GRID_SIZE; - if (dragLengthSteps > 0) + if (gridGhostNote != null && gridGhostNote.noteData != null && gridGhostHoldNote != null) { - gridGhostHoldNote.visible = true; - gridGhostHoldNote.noteData = gridGhostNote.noteData; - gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection(); + if (dragLengthSteps > 0) + { + gridGhostHoldNote.visible = true; + gridGhostHoldNote.noteData = gridGhostNote.noteData; + gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection(); - gridGhostHoldNote.setHeightDirectly(dragLengthPixels); + gridGhostHoldNote.setHeightDirectly(dragLengthPixels); - gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes); - } - else - { - gridGhostHoldNote.visible = false; + gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes); + } + else + { + gridGhostHoldNote.visible = false; + } } if (FlxG.mouse.justReleased) @@ -2644,7 +2648,7 @@ class ChartEditorState extends HaxeUIState if (cursorColumn == eventColumn) { if (gridGhostNote != null) gridGhostNote.visible = false; - gridGhostHoldNote.visible = false; + if (gridGhostHoldNote != null) gridGhostHoldNote.visible = false; if (gridGhostEvent == null) throw "ERROR: Tried to handle cursor, but gridGhostEvent is null! Check ChartEditorState.buildGrid()"; @@ -2704,11 +2708,11 @@ class ChartEditorState extends HaxeUIState } else { - if (FlxG.mouse.overlaps(notePreview)) + if (notePreview != null && FlxG.mouse.overlaps(notePreview)) { targetCursorMode = Pointer; } - else if (FlxG.mouse.overlaps(gridPlayheadScrollArea)) + else if (gridPlayheadScrollArea != null && FlxG.mouse.overlaps(gridPlayheadScrollArea)) { targetCursorMode = Pointer; } @@ -3020,18 +3024,35 @@ class ChartEditorState extends HaxeUIState { if (healthIconsDirty) { - if (healthIconBF != null) healthIconBF.characterId = currentSongMetadata.playData.characters.player; - if (healthIconDad != null) healthIconDad.characterId = currentSongMetadata.playData.characters.opponent; + var charDataBF = CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.player); + var charDataDad = CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.opponent); + if (healthIconBF != null) + { + healthIconBF.configure(charDataBF?.healthIcon); + healthIconBF.size *= 0.5; // Make the icon smaller in Chart Editor. + healthIconBF.flipX = !healthIconBF.flipX; // BF faces the other way. + } + if (healthIconDad != null) + { + healthIconDad.configure(charDataDad?.healthIcon); + healthIconDad.size *= 0.5; // Make the icon smaller in Chart Editor. + } + healthIconsDirty = false; } - // Right align the BF health icon. + // Right align, and visibly center, the BF health icon. if (healthIconBF != null) { // Base X position to the right of the grid. - var baseHealthIconXPos:Float = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x + gridTiledSprite.width + 15); - // Will be 0 when not bopping. When bopping, will increase to push the icon left. - var healthIconOffset:Float = healthIconBF.width - (HealthIcon.HEALTH_ICON_SIZE * 0.5); - healthIconBF.x = baseHealthIconXPos - healthIconOffset; + healthIconBF.x = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x + gridTiledSprite.width + 45 - (healthIconBF.width / 2)); + healthIconBF.y = (gridTiledSprite == null) ? (0) : (MENU_BAR_HEIGHT + GRID_TOP_PAD + 30 - (healthIconBF.height / 2)); + } + + // Visibly center the Dad health icon. + if (healthIconDad != null) + { + healthIconDad.x = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x - 45 - (healthIconDad.width / 2)); + healthIconDad.y = (gridTiledSprite == null) ? (0) : (MENU_BAR_HEIGHT + GRID_TOP_PAD + 30 - (healthIconDad.height / 2)); } } @@ -3656,49 +3677,41 @@ class ChartEditorState extends HaxeUIState var inputStage:Null = toolbox.findComponent('inputStage', DropDown); var stageId:String = currentSongMetadata.playData.stage; var stageData:Null = StageDataParser.parseStageData(stageId); - if (stageData != null) + if (inputStage != null) { - inputStage.value = {id: stageId, text: stageData.name}; - } - else - { - inputStage.value = {id: "mainStage", text: "Main Stage"}; + inputStage.value = (stageData != null) ? + {id: stageId, text: stageData.name} : + {id: "mainStage", text: "Main Stage"}; } var inputCharacterPlayer:Null = toolbox.findComponent('inputCharacterPlayer', DropDown); var charIdPlayer:String = currentSongMetadata.playData.characters.player; var charDataPlayer:Null = CharacterDataParser.fetchCharacterData(charIdPlayer); - if (charDataPlayer != null) + if (inputCharacterPlayer != null) { - inputCharacterPlayer.value = {id: charIdPlayer, text: charDataPlayer.name}; - } - else - { - inputCharacterPlayer.value = {id: "bf", text: "Boyfriend"}; + inputCharacterPlayer.value = (charDataPlayer != null) ? + {id: charIdPlayer, text: charDataPlayer.name} : + {id: "bf", text: "Boyfriend"}; } var inputCharacterOpponent:Null = toolbox.findComponent('inputCharacterOpponent', DropDown); var charIdOpponent:String = currentSongMetadata.playData.characters.opponent; var charDataOpponent:Null = CharacterDataParser.fetchCharacterData(charIdOpponent); - if (charDataOpponent != null) + if (inputCharacterOpponent != null) { - inputCharacterOpponent.value = {id: charIdOpponent, text: charDataOpponent.name}; - } - else - { - inputCharacterOpponent.value = {id: "dad", text: "Dad"}; + inputCharacterOpponent.value = (charDataOpponent != null) ? + {id: charIdOpponent, text: charDataOpponent.name} : + {id: "dad", text: "Dad"}; } var inputCharacterGirlfriend:Null = toolbox.findComponent('inputCharacterGirlfriend', DropDown); var charIdGirlfriend:String = currentSongMetadata.playData.characters.girlfriend; var charDataGirlfriend:Null = CharacterDataParser.fetchCharacterData(charIdGirlfriend); - if (charDataGirlfriend != null) + if (inputCharacterGirlfriend != null) { - inputCharacterGirlfriend.value = {id: charIdGirlfriend, text: charDataGirlfriend.name}; - } - else - { - inputCharacterGirlfriend.value = {id: "none", text: "None"}; + inputCharacterGirlfriend.value = (charDataGirlfriend != null) ? + {id: charIdGirlfriend, text: charDataGirlfriend.name} : + {id: "none", text: "None"}; } } @@ -4004,7 +4017,7 @@ class ChartEditorState extends HaxeUIState this.scrollPositionInPixels = value; // Move the grid sprite to the correct position. - if (gridTiledSprite != null) + if (gridTiledSprite != null && gridPlayheadScrollArea != null) { if (isViewDownscroll) { diff --git a/source/funkin/ui/haxeui/components/FunkinClickLabel.hx b/source/funkin/ui/haxeui/components/FunkinClickLabel.hx new file mode 100644 index 000000000..77c9dbc0f --- /dev/null +++ b/source/funkin/ui/haxeui/components/FunkinClickLabel.hx @@ -0,0 +1,30 @@ +package funkin.ui.haxeui.components; + +import haxe.ui.components.Label; +import funkin.input.Cursor; +import haxe.ui.events.MouseEvent; + +/** + * A HaxeUI label which: + * - Changes the current cursor when hovered over (assume an onClick handler will be added!). + */ +class FunkinClickLabel extends Label +{ + public function new() + { + super(); + + this.onMouseOver = handleMouseOver; + this.onMouseOut = handleMouseOut; + } + + private function handleMouseOver(event:MouseEvent) + { + Cursor.cursorMode = Pointer; + } + + private function handleMouseOut(event:MouseEvent) + { + Cursor.cursorMode = Default; + } +} diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index c606e469f..11529774f 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -126,6 +126,11 @@ class Constants */ public static final DEFAULT_CHARACTER:String = 'bf'; + /** + * Default player character for health icons. + */ + public static final DEFAULT_HEALTH_ICON:String = 'face'; + /** * Default stage for charts. */ From a8a8cf96fd22102fbf0c502833b282eb63721f7b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 17 Oct 2023 23:36:25 -0400 Subject: [PATCH 04/11] submodule merge fix --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index b9338b972..a66eb8353 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit b9338b97214f71b192f5cec760c5442ec2e8cbed +Subproject commit a66eb835318b7b4cf639d5add1de98a1a155d209 From cdab8753ea20ec30a4cbe6c6b5684232ab67d5ac Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 18 Oct 2023 01:02:10 -0400 Subject: [PATCH 05/11] Lots of reported bugs fixed. --- .vscode/launch.json | 6 +++ source/funkin/PauseSubState.hx | 2 +- source/funkin/data/DataParse.hx | 51 ++++++++++++++++--- source/funkin/data/DataWrite.hx | 23 +++++++++ source/funkin/data/song/SongData.hx | 6 +++ source/funkin/data/song/SongDataUtils.hx | 7 +-- .../data/song/migrator/SongData_v2_0_0.hx | 2 + source/funkin/play/HealthIcon.hx | 12 +++-- source/funkin/play/PlayState.hx | 10 +++- source/funkin/play/song/Song.hx | 6 +++ source/funkin/save/Save.hx | 2 + .../charting/ChartEditorDialogHandler.hx | 11 ++-- .../ui/debug/charting/ChartEditorState.hx | 22 +++++--- source/funkin/util/tools/ArrayTools.hx | 13 +++++ 14 files changed, 147 insertions(+), 26 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d16f1ca4f..74f72b826 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -23,6 +23,12 @@ "name": "Haxe Eval", "type": "haxe-eval", "request": "launch" + }, + { + // Attaches the debugger to an already running game + "name": "HXCPP - Attach", + "type": "hxcpp", + "request": "attach" } ] } diff --git a/source/funkin/PauseSubState.hx b/source/funkin/PauseSubState.hx index a074410ea..2ce9abf65 100644 --- a/source/funkin/PauseSubState.hx +++ b/source/funkin/PauseSubState.hx @@ -240,7 +240,7 @@ class PauseSubState extends MusicBeatSubState case 'Exit to Chart Editor': this.close(); - if (FlxG.sound.music != null) FlxG.sound.music.stop(); + if (FlxG.sound.music != null) FlxG.sound.music.pause(); // Don't reset song position! PlayState.instance.close(); // This only works because PlayState is a substate! case 'BACK': diff --git a/source/funkin/data/DataParse.hx b/source/funkin/data/DataParse.hx index 4a422b368..64a53d2a4 100644 --- a/source/funkin/data/DataParse.hx +++ b/source/funkin/data/DataParse.hx @@ -1,13 +1,15 @@ package funkin.data; import funkin.data.song.importer.FNFLegacyData.LegacyNote; -import hxjsonast.Json; -import hxjsonast.Tools; -import hxjsonast.Json.JObjectField; -import haxe.ds.Either; -import funkin.data.song.importer.FNFLegacyData.LegacyNoteSection; import funkin.data.song.importer.FNFLegacyData.LegacyNoteData; +import funkin.data.song.importer.FNFLegacyData.LegacyNoteSection; import funkin.data.song.importer.FNFLegacyData.LegacyScrollSpeeds; +import haxe.ds.Either; +import hxjsonast.Json; +import hxjsonast.Json.JObjectField; +import hxjsonast.Tools; +import thx.semver.Version; +import thx.semver.VersionRule; /** * `json2object` has an annotation `@:jcustomparse` which allows for mutation of parsed values. @@ -23,7 +25,8 @@ class DataParse * `@:jcustomparse(funkin.data.DataParse.stringNotEmpty)` * @param json Contains the `pos` and `value` of the property. * @param name The name of the property. - * @throws If the property is not a string or is empty. + * @throws Error If the property is not a string or is empty. + * @return The string value. */ public static function stringNotEmpty(json:Json, name:String):String { @@ -37,6 +40,42 @@ class DataParse } } + /** + * `@:jcustomparse(funkin.data.DataParse.semverVersion)` + * @param json Contains the `pos` and `value` of the property. + * @param name The name of the property. + * @return The value of the property as a `thx.semver.Version`. + */ + public static function semverVersion(json:Json, name:String):Version + { + switch (json.value) + { + case JString(s): + if (s == "") throw 'Expected version property $name to be non-empty.'; + return s; + default: + throw 'Expected version property $name to be a string, but it was ${json.value}.'; + } + } + + /** + * `@:jcustomparse(funkin.data.DataParse.semverVersionRule)` + * @param json Contains the `pos` and `value` of the property. + * @param name The name of the property. + * @return The value of the property as a `thx.semver.VersionRule`. + */ + public static function semverVersionRule(json:Json, name:String):VersionRule + { + switch (json.value) + { + case JString(s): + if (s == "") throw 'Expected version rule property $name to be non-empty.'; + return s; + default: + throw 'Expected version rule property $name to be a string, but it was ${json.value}.'; + } + } + /** * Parser which outputs a Dynamic value, either a object or something else. * @param json diff --git a/source/funkin/data/DataWrite.hx b/source/funkin/data/DataWrite.hx index 41993107f..2f3a7632f 100644 --- a/source/funkin/data/DataWrite.hx +++ b/source/funkin/data/DataWrite.hx @@ -1,6 +1,8 @@ package funkin.data; import funkin.util.SerializerUtil; +import thx.semver.Version; +import thx.semver.VersionRule; /** * `json2object` has an annotation `@:jcustomwrite` which allows for custom serialization of values to be written to JSON. @@ -9,9 +11,30 @@ import funkin.util.SerializerUtil; */ class DataWrite { + /** + * `@:jcustomwrite(funkin.data.DataWrite.dynamicValue)` + * @param value + * @return String + */ public static function dynamicValue(value:Dynamic):String { // Is this cheating? Yes. Do I care? No. return SerializerUtil.toJSON(value); } + + /** + * `@:jcustomwrite(funkin.data.DataWrite.semverVersion)` + */ + public static function semverVersion(value:Version):String + { + return value.toString(); + } + + /** + * `@:jcustomwrite(funkin.data.DataWrite.semverVersionRule)` + */ + public static function semverVersionRule(value:VersionRule):String + { + return value.toString(); + } } diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 9340e46c9..88993e519 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -12,6 +12,8 @@ class SongMetadata * */ // @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION) + @:jcustomparse(funkin.data.DataParse.semverVersion) + @:jcustomwrite(funkin.data.DataWrite.semverVersion) public var version:Version; @:default("Unknown") @@ -203,6 +205,8 @@ class SongMusicData * */ // @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION) + @:jcustomparse(funkin.data.DataParse.semverVersion) + @:jcustomwrite(funkin.data.DataWrite.semverVersion) public var version:Version; @:default("Unknown") @@ -367,6 +371,8 @@ class SongCharacterData class SongChartData { @:default(funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION) + @:jcustomparse(funkin.data.DataParse.semverVersion) + @:jcustomwrite(funkin.data.DataWrite.semverVersion) public var version:Version; public var scrollSpeed:Map; diff --git a/source/funkin/data/song/SongDataUtils.hx b/source/funkin/data/song/SongDataUtils.hx index ee3dfe98c..3ff3943c6 100644 --- a/source/funkin/data/song/SongDataUtils.hx +++ b/source/funkin/data/song/SongDataUtils.hx @@ -246,7 +246,8 @@ class SongDataUtils typedef SongClipboardItems = { - ?valid:Bool, - notes:Array, - events:Array + @:optional + var valid:Bool; + var notes:Array; + var events:Array; } diff --git a/source/funkin/data/song/migrator/SongData_v2_0_0.hx b/source/funkin/data/song/migrator/SongData_v2_0_0.hx index 935e7349c..eeeed2f2b 100644 --- a/source/funkin/data/song/migrator/SongData_v2_0_0.hx +++ b/source/funkin/data/song/migrator/SongData_v2_0_0.hx @@ -24,6 +24,8 @@ class SongMetadata_v2_0_0 // ========== // UNMODIFIED VALUES // ========== + @:jcustomparse(funkin.data.DataParse.semverVersion) + @:jcustomwrite(funkin.data.DataWrite.semverVersion) public var version:Version; @:default("Unknown") diff --git a/source/funkin/play/HealthIcon.hx b/source/funkin/play/HealthIcon.hx index 5b9c8ec75..958933df8 100644 --- a/source/funkin/play/HealthIcon.hx +++ b/source/funkin/play/HealthIcon.hx @@ -129,7 +129,6 @@ class HealthIcon extends FlxSprite if (value == characterId) return value; characterId = value ?? Constants.DEFAULT_HEALTH_ICON; - loadCharacter(characterId); return characterId; } @@ -138,7 +137,6 @@ class HealthIcon extends FlxSprite if (value == isPixel) return value; isPixel = value; - loadCharacter(characterId); return isPixel; } @@ -165,8 +163,11 @@ class HealthIcon extends FlxSprite { if (data == null) { - this.isPixel = false; this.characterId = Constants.DEFAULT_HEALTH_ICON; + this.isPixel = false; + + loadCharacter(characterId); + this.size.set(1.0, 1.0); this.offset.x = 0.0; this.offset.y = 0.0; @@ -174,8 +175,11 @@ class HealthIcon extends FlxSprite } else { - this.isPixel = data.isPixel ?? false; this.characterId = data.id; + this.isPixel = data.isPixel ?? false; + + loadCharacter(characterId); + this.size.set(data.scale ?? 1.0, data.scale ?? 1.0); this.offset.x = (data.offsets != null) ? data.offsets[0] : 0.0; this.offset.y = (data.offsets != null) ? data.offsets[1] : 0.0; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 8b47e6ebd..9ccc66f69 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -698,7 +698,15 @@ class PlayState extends MusicBeatSubState FlxG.sound.music.pause(); FlxG.sound.music.time = (startTimestamp); - vocals = currentChart.buildVocals(); + if (!overrideMusic) + { + vocals = currentChart.buildVocals(); + + if (vocals.members.length == 0) + { + trace('WARNING: No vocals found for this song.'); + } + } vocals.pause(); vocals.time = 0; diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index f996d75ef..60b8b9864 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -154,6 +154,12 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = dialog.findComponent('inputSongName', TextField); if (inputSongName == null) throw 'Could not locate inputSongName TextField in Song Metadata dialog'; inputSongName.onChange = function(event:UIEvent) { diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 30d8fde7e..40f841fd8 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -554,6 +554,9 @@ class ChartEditorState extends HaxeUIState notePreviewDirty = true; notePreviewViewportBoundsDirty = true; + // Make sure the difficulty we selected is in the list of difficulties. + currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty); + return selectedDifficulty; } @@ -971,6 +974,7 @@ class ChartEditorState extends HaxeUIState result = []; trace('Initializing blank note data for difficulty ' + selectedDifficulty); currentSongChartData.notes.set(selectedDifficulty, result); + currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty); return result; } return result; @@ -979,6 +983,7 @@ class ChartEditorState extends HaxeUIState function set_currentSongChartNoteData(value:Array):Array { currentSongChartData.notes.set(selectedDifficulty, value); + currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty); return value; } @@ -4089,7 +4094,7 @@ class ChartEditorState extends HaxeUIState } subStateClosed.add(fixCamera); - subStateClosed.add(updateConductor); + subStateClosed.add(resetConductorAfterTest); FlxTransitionableState.skipNextTransIn = false; FlxTransitionableState.skipNextTransOut = false; @@ -4122,10 +4127,9 @@ class ChartEditorState extends HaxeUIState add(this.component); } - function updateConductor(_:FlxSubState = null):Void + function resetConductorAfterTest(_:FlxSubState = null):Void { - var targetPos = scrollPositionInMs; - Conductor.update(targetPos); + moveSongToScrollPosition(); } public function postLoadInstrumental():Void @@ -4179,12 +4183,14 @@ class ChartEditorState extends HaxeUIState function moveSongToScrollPosition():Void { // Update the songPosition in the audio tracks. - if (audioInstTrack != null) audioInstTrack.time = scrollPositionInMs + playheadPositionInMs; + if (audioInstTrack != null) + { + audioInstTrack.time = scrollPositionInMs + playheadPositionInMs; + // Update the songPosition in the Conductor. + Conductor.update(audioInstTrack.time); + } if (audioVocalTrackGroup != null) audioVocalTrackGroup.time = scrollPositionInMs + playheadPositionInMs; - // Update the songPosition in the Conductor. - Conductor.update(audioInstTrack.time); - // We need to update the note sprites because we changed the scroll position. noteDisplayDirty = true; } diff --git a/source/funkin/util/tools/ArrayTools.hx b/source/funkin/util/tools/ArrayTools.hx index 67cc1c041..1c9f38de5 100644 --- a/source/funkin/util/tools/ArrayTools.hx +++ b/source/funkin/util/tools/ArrayTools.hx @@ -38,6 +38,19 @@ class ArrayTools return null; } + /** + * Push an element to the array if it is not already present. + * @param input The array to push to + * @param element The element to push + * @return Whether the element was pushed + */ + public static function pushUnique(input:Array, element:T):Bool + { + if (input.contains(element)) return false; + input.push(element); + return true; + } + /** * Remove all elements from the array, without creating a new array. * @param array The array to clear. From 73022207fa6bc1bc063db5d82f1d0639dd32c140 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 18 Oct 2023 15:52:44 -0400 Subject: [PATCH 06/11] Merge hitsound audios --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index a66eb8353..f66c8559d 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a66eb835318b7b4cf639d5add1de98a1a155d209 +Subproject commit f66c8559db05df42a8503abc7b59166c35ba0b2e From f016a9ab10b41b9639ff251fea5d82aa3e3d3b63 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 18 Oct 2023 15:55:43 -0400 Subject: [PATCH 07/11] Merge assets folder --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 07ba943c8..33fa28ac3 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 07ba943c810882e2e70ea172375195b1592dbdb5 +Subproject commit 33fa28ac3e86cf471a52932a1a3ff15f2945ebe7 From 26f83b5a81cdb49056e5c385ac12c8676d983cf5 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 18 Oct 2023 16:02:56 -0400 Subject: [PATCH 08/11] Fix _time of nullable compile error. --- source/funkin/ui/debug/charting/ChartEditorState.hx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 30d8fde7e..037388a73 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -4179,12 +4179,14 @@ class ChartEditorState extends HaxeUIState function moveSongToScrollPosition():Void { // Update the songPosition in the audio tracks. - if (audioInstTrack != null) audioInstTrack.time = scrollPositionInMs + playheadPositionInMs; + if (audioInstTrack != null) + { + audioInstTrack.time = scrollPositionInMs + playheadPositionInMs; + // Update the songPosition in the Conductor. + Conductor.update(audioInstTrack.time); + } if (audioVocalTrackGroup != null) audioVocalTrackGroup.time = scrollPositionInMs + playheadPositionInMs; - // Update the songPosition in the Conductor. - Conductor.update(audioInstTrack.time); - // We need to update the note sprites because we changed the scroll position. noteDisplayDirty = true; } From 8d87f51f722543b3178950042cb48df8f917b21b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 18 Oct 2023 16:05:16 -0400 Subject: [PATCH 09/11] Update assets folder --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 33fa28ac3..777410e4a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 33fa28ac3e86cf471a52932a1a3ff15f2945ebe7 +Subproject commit 777410e4ad628f274065b0245f73a4985ffbf28a From 8f23537bace34b63f884291ceb0bd031e5c6555e Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 18 Oct 2023 18:37:00 -0400 Subject: [PATCH 10/11] assets submod merge --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index a66eb8353..777410e4a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a66eb835318b7b4cf639d5add1de98a1a155d209 +Subproject commit 777410e4ad628f274065b0245f73a4985ffbf28a From 256a9e7a23e8a57cd9a7cdace69f5a60275b5ec2 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 18 Oct 2023 19:30:50 -0400 Subject: [PATCH 11/11] remote submodule stuff --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index e0839baaf..8968471e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "assets"] path = assets url = https://github.com/FunkinCrew/Funkin-history-rewrite-assets + branch = master [submodule "art"] path = art url = https://github.com/FunkinCrew/Funkin-history-rewrite-art