diff --git a/assets b/assets index 70592c5024f..49189dde6fb 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 70592c5024f6ef0369c2ce7ac8cc123533c866f0 +Subproject commit 49189dde6fbd9b7902b8e13648023729c9255910 diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 728e749465c..42f95cbf5c2 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3345,8 +3345,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState menubarItemToggleToolboxEventData.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT, event.value); menubarItemToggleToolboxFreeplay.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_FREEPLAY_LAYOUT, event.value); menubarItemToggleToolboxPlaytestProperties.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT, event.value); - menubarItemToggleToolboxPlayerPreview.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT, event.value); - menubarItemToggleToolboxOpponentPreview.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT, event.value); + menubarItemToggleToolboxPlayerPreview.onChange = event -> { + this.setToolboxState(CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT, event.value); + playerPreviewDirty = event.value; + } + menubarItemToggleToolboxOpponentPreview.onChange = event -> { + this.setToolboxState(CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT, event.value); + opponentPreviewDirty = event.value; + } // TODO: Pass specific HaxeUI components to add context menus to them. // registerContextMenu(null, Paths.ui('chart-editor/context/test')); @@ -3669,7 +3675,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState var oldStepTime:Float = Conductor.instance.currentStepTime; var oldSongPosition:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset; updateSongTime(); - handleHitsounds(oldSongPosition, Conductor.instance.songPosition + Conductor.instance.instrumentalOffset); + handleMusicPositionUpdate(oldSongPosition, Conductor.instance.songPosition + Conductor.instance.instrumentalOffset); // Resync vocals. if (Math.abs(audioInstTrack.time - audioVocalTrackGroup.time) > 100) { @@ -3687,7 +3693,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Else, move the entire view. var oldSongPosition:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset; updateSongTime(); - handleHitsounds(oldSongPosition, Conductor.instance.songPosition + Conductor.instance.instrumentalOffset); + handleMusicPositionUpdate(oldSongPosition, Conductor.instance.songPosition + Conductor.instance.instrumentalOffset); // Resync vocals. if (Math.abs(audioInstTrack.time - audioVocalTrackGroup.time) > 100) { @@ -3904,7 +3910,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // If a new event is needed, call buildEventSprite. var eventSprite:ChartEditorEventSprite = renderedEvents.recycle(() -> new ChartEditorEventSprite(this), false, true); eventSprite.parentState = this; - trace('Creating new Event... (${renderedEvents.members.length})'); + // trace('Creating new Event... (${renderedEvents.members.length})'); if (eventData?.value != null && (eventData?.value?.ease != null && eventData?.value?.easeDir == null)) { @@ -5392,8 +5398,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function handleToolboxes():Void { handleDifficultyToolbox(); - // handlePlayerPreviewToolbox(); - // handleOpponentPreviewToolbox(); + handlePlayerPreviewToolbox(); + handleOpponentPreviewToolbox(); } function handleDifficultyToolbox():Void @@ -5416,15 +5422,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function handlePlayerPreviewToolbox():Void { // Manage the Select Difficulty tree view. - var charPreviewToolbox:Null = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT); + var charPreviewToolbox:Null = this.getToolboxUnCast(CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT); if (charPreviewToolbox == null) return; - // TODO: Re-enable the player preview once we figure out the performance issues. - var charPlayer:Null = null; // charPreviewToolbox.findComponent('charPlayer'); + var charPlayer:Null = charPreviewToolbox.findComponent('charPlayer'); if (charPlayer == null) return; - - currentPlayerCharacterPlayer = charPlayer; - if (playerPreviewDirty) { playerPreviewDirty = false; @@ -5442,28 +5444,28 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState charPlayer.targetScale = 0.5; charPreviewToolbox.title = 'Player Preview - ${charPlayer.charName}'; + charPreviewToolbox.invalidateComponentLayout(); } + } - if (charPreviewToolbox != null && !charPreviewToolbox.minimized) - { - charPreviewToolbox.width = charPlayer.width + 32; - charPreviewToolbox.height = charPlayer.height + 64; - } + if (charPreviewToolbox != null && !charPreviewToolbox.minimized) + { + charPreviewToolbox.width = charPlayer.width + 32; + charPreviewToolbox.height = charPlayer.height + 64; } + currentPlayerCharacterPlayer = charPlayer; } function handleOpponentPreviewToolbox():Void { // Manage the Select Difficulty tree view. - var charPreviewToolbox:Null = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT); + var charPreviewToolbox:Null = this.getToolboxUnCast(CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT); if (charPreviewToolbox == null) return; // TODO: Re-enable the player preview once we figure out the performance issues. - var charPlayer:Null = null; // charPreviewToolbox.findComponent('charPlayer'); + var charPlayer:Null = charPreviewToolbox.findComponent('charOpponent'); if (charPlayer == null) return; - currentOpponentCharacterPlayer = charPlayer; - if (opponentPreviewDirty) { opponentPreviewDirty = false; @@ -5481,14 +5483,16 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState charPlayer.targetScale = 0.5; charPreviewToolbox.title = 'Opponent Preview - ${charPlayer.charName}'; + charPreviewToolbox.invalidateComponentLayout(); } + } - if (charPreviewToolbox != null && !charPreviewToolbox.minimized) - { - charPreviewToolbox.width = charPlayer.width + 32; - charPreviewToolbox.height = charPlayer.height + 64; - } + if (charPreviewToolbox != null && !charPreviewToolbox.minimized) + { + charPreviewToolbox.width = charPlayer.width + 32; + charPreviewToolbox.height = charPlayer.height + 64; } + currentOpponentCharacterPlayer = charPlayer; } function handleSelectionButtons():Void @@ -5745,32 +5749,40 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState /** * Handle aligning the health icons next to the grid. */ + var _charIconData = null; + + @:access(funkin.play.character.BaseCharacter) function handleHealthIcons():Void { if (healthIconsDirty) { - var charDataBF = CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.player); - var charDataDad = CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.opponent); + _charIconData = currentPlayerCharacterPlayer?.character?._data ?? CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.player); + if (healthIconBF != null) { - healthIconBF.configure(charDataBF?.healthIcon); + healthIconBF.configure(_charIconData?.healthIcon); healthIconBF.size *= 0.5; // Make the icon smaller in Chart Editor. healthIconBF.flipX = !healthIconBF.flipX; // BF faces the other way. } + if (buttonSelectPlayer != null) { - buttonSelectPlayer.text = charDataBF?.name ?? 'Player'; + buttonSelectPlayer.text = _charIconData?.name ?? 'Player'; } + + _charIconData = currentOpponentCharacterPlayer?.character?._data ?? CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.opponent); + if (healthIconDad != null) { - healthIconDad.configure(charDataDad?.healthIcon); + healthIconDad.configure(_charIconData?.healthIcon); healthIconDad.size *= 0.5; // Make the icon smaller in Chart Editor. } if (buttonSelectOpponent != null) { - buttonSelectOpponent.text = charDataDad?.name ?? 'Opponent'; + buttonSelectOpponent.text = _charIconData?.name ?? 'Opponent'; } healthIconsDirty = false; + _charIconData = null; } // Right align, and visibly center, the BF health icon. @@ -6818,9 +6830,20 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState /** * Handle the playback of hitsounds. */ - function handleHitsounds(oldSongPosition:Float, newSongPosition:Float):Void + var _scriptNoteObj:NoteSprite = null; + + var _noteScriptEvent:NoteScriptEvent = null; + + var _currentEvents = null; + var _allowedEvents = null; + var _eventTarget:Null = null; + + public static var _allowedEventsNames:Array = ['PlayAnimation']; + + function handleMusicPositionUpdate(oldSongPosition:Float, newSongPosition:Float):Void { - if (!hitsoundsEnabled) return; + _currentEvents = SongDataUtils.getEventsInTimeRange(currentSongChartEventData, oldSongPosition, newSongPosition); + _allowedEvents = SongDataUtils.getEventsWithKind(_currentEvents, _allowedEventsNames); // Assume notes are sorted by time. for (noteData in currentSongChartNoteData) @@ -6830,25 +6853,34 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState if (noteData.time < oldSongPosition) // Note is in the past. continue; - if (noteData.time > newSongPosition) // Note is in the future. - return; // Assume all notes are also in the future. - - // Note was just hit. + if (noteData.time > newSongPosition) break; - // Character preview. + /** + * We hit a note. + * We're gonna create scripted event and dispatch it al over ChartEditor. + */ + _scriptNoteObj = new NoteSprite(NoteStyleRegistry.instance.fetchDefault()); + _scriptNoteObj.noteData = noteData; + _scriptNoteObj.kill(); + _scriptNoteObj.direction = _scriptNoteObj.noteData?.getDirection() ?? 0; + _scriptNoteObj.scrollFactor.set(); - // NoteScriptEvent takes a sprite, ehe. Need to rework that. - var tempNote:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault()); - tempNote.noteData = noteData; - tempNote.scrollFactor.set(0, 0); - var event:NoteScriptEvent = new HitNoteScriptEvent(tempNote, 0.0, 0, 'perfect', false, 0); - dispatchEvent(event); + _noteScriptEvent = new HitNoteScriptEvent(_scriptNoteObj, 0.0, 0, (noteData.getStrumlineIndex() == 0 ? 'perfect' : 'sick'), false, 0); + dispatchEvent(_noteScriptEvent); // Calling event.cancelEvent() skips all the other logic! Neat! - if (event.eventCanceled) continue; + if (_noteScriptEvent.eventCanceled) + { + _scriptNoteObj.destroy(); + _scriptNoteObj = null; + + _noteScriptEvent = null; + + continue; + } // Hitsounds. - switch (noteData.getStrumlineIndex()) + if (hitsoundsEnabled) switch (noteData.getStrumlineIndex()) { case 0: // Player if (hitsoundVolumePlayer > 0) this.playSound(Paths.sound('chartingSounds/hitNotePlayer'), hitsoundVolumePlayer); @@ -6856,6 +6888,31 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState if (hitsoundVolumeOpponent > 0) this.playSound(Paths.sound('chartingSounds/hitNoteOpponent'), hitsoundVolumeOpponent); } } + // Clearing memory before next event call. + _scriptNoteObj?.destroy(); + _scriptNoteObj = null; + + _noteScriptEvent = null; + for (data in _allowedEvents) + { + switch (data.eventKind) + { + case "PlayAnimation": + switch (data.getString('target').toLowerCase().trim()) + { + case 'boyfriend' | 'bf' | 'player': + _eventTarget = currentPlayerCharacterPlayer; + case 'dad' | 'opponent' | 'enemy': + _eventTarget = currentOpponentCharacterPlayer; + default: + } + if (_eventTarget != null) _eventTarget.playAnimManually(data.getString('anim') ?? 'idle', data.getBool('force') ?? false); + } + } + + _currentEvents = null; + _allowedEvents.resize(0); + _eventTarget = null; } function stopAudioPlayback():Void @@ -6925,6 +6982,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Many things get reset when song length changes. healthIconsDirty = true; + playerPreviewDirty = true; + opponentPreviewDirty = true; } public function loadSubtitles():Void diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorCharacterIconSelectorMenu.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorCharacterIconSelectorMenu.hx index 0a622d2835d..ba5d41bd70f 100644 --- a/source/funkin/ui/debug/charting/dialogs/ChartEditorCharacterIconSelectorMenu.hx +++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorCharacterIconSelectorMenu.hx @@ -116,14 +116,19 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu charButton.onClick = _ -> { switch (charType) { - case BF: chartEditorState.currentSongMetadata.playData.characters.player = charId; + case BF: + chartEditorState.currentSongMetadata.playData.characters.player = charId; + chartEditorState.playerPreviewDirty = true; case GF: chartEditorState.currentSongMetadata.playData.characters.girlfriend = charId; - case DAD: chartEditorState.currentSongMetadata.playData.characters.opponent = charId; + case DAD: + chartEditorState.currentSongMetadata.playData.characters.opponent = charId; + chartEditorState.opponentPreviewDirty = true; default: throw 'Invalid charType: ' + charType; }; defaultText = (charId != "") ? '${charData.name} [${charId}]' : 'None'; chartEditorState.healthIconsDirty = true; + chartEditorState.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT); }; diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx index da385223389..03b360bd51c 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx @@ -204,14 +204,14 @@ class ChartEditorToolboxHandler * @param id The asset ID of the toolbox layout. * @return The toolbox. */ - public static function getToolbox_OLD(state:ChartEditorState, id:String):Null + public static function getToolboxUnCast(state:ChartEditorState, id:String):Null { var toolbox:Null = state.activeToolboxes.get(id); // Initialize the toolbox without showing it. if (toolbox == null) toolbox = initToolbox(state, id); - if (toolbox == null) throw 'ChartEditorToolboxHandler.getToolbox_OLD() - Could not retrieve or build toolbox: $id'; + if (toolbox == null) throw 'ChartEditorToolboxHandler.getToolboxUnCast() - Could not retrieve or build toolbox: $id'; return toolbox; } @@ -346,8 +346,8 @@ class ChartEditorToolboxHandler if (toolbox == null) return null; // Starting position. - toolbox.x = 200; - toolbox.y = 350; + toolbox.x = 700; + toolbox.y = 150; toolbox.onDialogClosed = function(event:DialogEvent) { state.menubarItemToggleToolboxPlayerPreview.selected = false; @@ -376,14 +376,14 @@ class ChartEditorToolboxHandler // Starting position. toolbox.x = 200; - toolbox.y = 350; + toolbox.y = 150; toolbox.onDialogClosed = (event:DialogEvent) -> { state.menubarItemToggleToolboxOpponentPreview.selected = false; } - var charPlayer:Null = toolbox.findComponent('charPlayer'); - if (charPlayer == null) throw 'ChartEditorToolboxHandler.buildToolboxOpponentPreviewLayout() - Could not find charPlayer component.'; + var charPlayer:Null = toolbox.findComponent('charOpponent'); + if (charPlayer == null) throw 'ChartEditorToolboxHandler.buildToolboxOpponentPreviewLayout() - Could not find charOpponent component.'; // TODO: We need to implement character swapping in ChartEditorState. charPlayer.loadCharacter('dad'); charPlayer.characterType = CharacterType.DAD; diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx index f8698dc491e..1b927d6cf4c 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorDifficultyToolbox.hx @@ -142,6 +142,9 @@ class ChartEditorDifficultyToolbox extends ChartEditorBaseToolbox { chartEditorState.currentSongMetadata = songMetadata; chartEditorState.healthIconsDirty = true; + chartEditorState.playerPreviewDirty = true; + chartEditorState.opponentPreviewDirty = true; + chartEditorState.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT); chartEditorState.success('Replaced Metadata', 'Replaced metadata with file (${fileReference.name})'); } diff --git a/source/funkin/ui/haxeui/components/CharacterPlayer.hx b/source/funkin/ui/haxeui/components/CharacterPlayer.hx index dbe537fc3bc..295af4b0de6 100644 --- a/source/funkin/ui/haxeui/components/CharacterPlayer.hx +++ b/source/funkin/ui/haxeui/components/CharacterPlayer.hx @@ -31,7 +31,7 @@ typedef AnimationInfo = @:composite(Layout) class CharacterPlayer extends Box { - var character:Null; + public var character:Null; public function new(defaultToBf:Bool = true) { @@ -74,6 +74,7 @@ class CharacterPlayer extends Box * Loads a character by ID. * @param id The ID of the character to load. */ + @:access(funkin.play.character.BaseCharacter) public function loadCharacter(id:String):Void { if (id == null) return; @@ -101,12 +102,14 @@ class CharacterPlayer extends Box if (flip) character.flipX = !character.flipX; if (targetScale != 1.0) character.setScale(targetScale); - character.animation.onFrameChange.add(function(name:String = '', frameNumber:Int = -1, frameIndex:Int = -1) { - dispatch(new AnimationEvent(AnimationEvent.FRAME)); - }); - character.animation.onFinish.add(function(name:String = '') { - dispatch(new AnimationEvent(AnimationEvent.END)); - }); + if (character._data.isPixel) + { + character.scale.x *= Constants.PIXEL_ART_SCALE; + character.scale.y *= Constants.PIXEL_ART_SCALE; + } + + character.animation.onFrameChange.add(onFrame); + character.animation.onFinish.add(onFinish); add(character); invalidateComponentLayout(); @@ -170,17 +173,16 @@ class CharacterPlayer extends Box dispatch(new AnimationEvent(AnimationEvent.END)); } + public function playAnimManually(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void + { + if (character != null) character.playAnimation(name, restart, ignoreOther, reversed); + } + override function repositionChildren():Void { super.repositionChildren(); character.x = this.cachedScreenX; character.y = this.cachedScreenY; - - // Apply animation offsets, so the character is positioned correctly based on the animation. - @:privateAccess var animOffsets:Array = character.animOffsets; - - character.x -= animOffsets[0] * targetScale * (flip ? -1 : 1); - character.y -= animOffsets[1] * targetScale; } /** @@ -225,7 +227,15 @@ class CharacterPlayer extends Box */ public function onNoteHit(event:HitNoteScriptEvent):Void { - if (character != null) character.onNoteHit(event); + if (character != null) + { + character.onNoteHit(event); + + if ((event.note.noteData.getMustHitNote() && characterType == BF) + || (!event.note.noteData.getMustHitNote() + && characterType == DAD)) character.holdTimer = -event.note.noteData?.length / 1000; + // At least i tried yaknow? + } } /** @@ -260,6 +270,7 @@ class CharacterPlayer extends Box } @:access(funkin.ui.haxeui.components.CharacterPlayer) +@:access(funkin.play.character.BaseCharacter) private class Layout extends DefaultLayout { public override function resizeChildren():Void @@ -275,6 +286,7 @@ private class Layout extends DefaultLayout character.cornerPosition.set(0, 0); // character.setGraphicSize(Std.int(innerWidth), Std.int(innerHeight)); + // if (character._data.isPixel) character.setGraphicSize(Std.int(innerWidth * Constants.PIXEL_ART_SCALE), Std.int(innerHeight * Constants.PIXEL_ART_SCALE)); } public override function calcAutoSize(exclusions:Array = null):Size @@ -286,8 +298,8 @@ private class Layout extends DefaultLayout return super.calcAutoSize(exclusions); } var size:Size = new Size(); - size.width = character.width + paddingLeft + paddingRight; - size.height = character.height + paddingTop + paddingBottom; + size.width = (character.width * (character._data.isPixel ? Constants.PIXEL_ART_SCALE : 1)) + paddingLeft + paddingRight; + size.height = (character.height * (character._data.isPixel ? Constants.PIXEL_ART_SCALE : 1)) + paddingTop + paddingBottom; return size; } }