From 4804b9ae22eec299d84569402cb25ca21baf6a86 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Jan 2024 01:06:10 -0500 Subject: [PATCH 01/33] Work in progress for Chart Editor gamepad support (navigation implemented, note placement WIP) --- source/funkin/input/Controls.hx | 367 +++++++++++----- source/funkin/input/TurboActionHandler.hx | 111 +++++ source/funkin/input/TurboButtonHandler.hx | 127 ++++++ .../ui/debug/charting/ChartEditorState.hx | 397 ++++++++++++++++-- .../charting/commands/RemoveEventsCommand.hx | 4 + .../charting/commands/RemoveItemsCommand.hx | 4 + .../charting/commands/RemoveNotesCommand.hx | 4 + .../components/ChartEditorHoldNoteSprite.hx | 9 +- .../handlers/ChartEditorGamepadHandler.hx | 133 ++++++ source/funkin/ui/debug/charting/import.hx | 1 + 10 files changed, 1009 insertions(+), 148 deletions(-) create mode 100644 source/funkin/input/TurboActionHandler.hx create mode 100644 source/funkin/input/TurboButtonHandler.hx create mode 100644 source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx diff --git a/source/funkin/input/Controls.hx b/source/funkin/input/Controls.hx index 3fa4e0f75..5c761cd19 100644 --- a/source/funkin/input/Controls.hx +++ b/source/funkin/input/Controls.hx @@ -30,47 +30,31 @@ class Controls extends FlxActionSet * A list of actions that a player would invoke via some input device. * Uses FlxActions to funnel various inputs to a single action. */ - var _ui_up = new FlxActionDigital(Action.UI_UP); - var _ui_left = new FlxActionDigital(Action.UI_LEFT); - var _ui_right = new FlxActionDigital(Action.UI_RIGHT); - var _ui_down = new FlxActionDigital(Action.UI_DOWN); - var _ui_upP = new FlxActionDigital(Action.UI_UP_P); - var _ui_leftP = new FlxActionDigital(Action.UI_LEFT_P); - var _ui_rightP = new FlxActionDigital(Action.UI_RIGHT_P); - var _ui_downP = new FlxActionDigital(Action.UI_DOWN_P); - var _ui_upR = new FlxActionDigital(Action.UI_UP_R); - var _ui_leftR = new FlxActionDigital(Action.UI_LEFT_R); - var _ui_rightR = new FlxActionDigital(Action.UI_RIGHT_R); - var _ui_downR = new FlxActionDigital(Action.UI_DOWN_R); - var _note_up = new FlxActionDigital(Action.NOTE_UP); - var _note_left = new FlxActionDigital(Action.NOTE_LEFT); - var _note_right = new FlxActionDigital(Action.NOTE_RIGHT); - var _note_down = new FlxActionDigital(Action.NOTE_DOWN); - var _note_upP = new FlxActionDigital(Action.NOTE_UP_P); - var _note_leftP = new FlxActionDigital(Action.NOTE_LEFT_P); - var _note_rightP = new FlxActionDigital(Action.NOTE_RIGHT_P); - var _note_downP = new FlxActionDigital(Action.NOTE_DOWN_P); - var _note_upR = new FlxActionDigital(Action.NOTE_UP_R); - var _note_leftR = new FlxActionDigital(Action.NOTE_LEFT_R); - var _note_rightR = new FlxActionDigital(Action.NOTE_RIGHT_R); - var _note_downR = new FlxActionDigital(Action.NOTE_DOWN_R); - var _accept = new FlxActionDigital(Action.ACCEPT); - var _back = new FlxActionDigital(Action.BACK); - var _pause = new FlxActionDigital(Action.PAUSE); - var _reset = new FlxActionDigital(Action.RESET); - var _cutscene_advance = new FlxActionDigital(Action.CUTSCENE_ADVANCE); - var _cutscene_skip = new FlxActionDigital(Action.CUTSCENE_SKIP); - var _debug_menu = new FlxActionDigital(Action.DEBUG_MENU); - var _debug_chart = new FlxActionDigital(Action.DEBUG_CHART); - var _debug_stage = new FlxActionDigital(Action.DEBUG_STAGE); - var _volume_up = new FlxActionDigital(Action.VOLUME_UP); - var _volume_down = new FlxActionDigital(Action.VOLUME_DOWN); - var _volume_mute = new FlxActionDigital(Action.VOLUME_MUTE); + var _ui_up = new FunkinAction(Action.UI_UP, Action.UI_UP_P, Action.UI_UP_R); + var _ui_left = new FunkinAction(Action.UI_LEFT, Action.UI_LEFT_P, Action.UI_LEFT_R); + var _ui_right = new FunkinAction(Action.UI_RIGHT, Action.UI_RIGHT_P, Action.UI_RIGHT_R); + var _ui_down = new FunkinAction(Action.UI_DOWN, Action.UI_DOWN_P, Action.UI_DOWN_R); + var _note_up = new FunkinAction(Action.NOTE_UP, Action.NOTE_UP_P, Action.NOTE_UP_R); + var _note_left = new FunkinAction(Action.NOTE_LEFT, Action.NOTE_LEFT_P, Action.NOTE_LEFT_R); + var _note_right = new FunkinAction(Action.NOTE_RIGHT, Action.NOTE_RIGHT_P, Action.NOTE_RIGHT_R); + var _note_down = new FunkinAction(Action.NOTE_DOWN, Action.NOTE_DOWN_P, Action.NOTE_DOWN_R); + var _accept = new FunkinAction(Action.ACCEPT); + var _back = new FunkinAction(Action.BACK); + var _pause = new FunkinAction(Action.PAUSE); + var _reset = new FunkinAction(Action.RESET); + var _cutscene_advance = new FunkinAction(Action.CUTSCENE_ADVANCE); + var _cutscene_skip = new FunkinAction(Action.CUTSCENE_SKIP); + var _debug_menu = new FunkinAction(Action.DEBUG_MENU); + var _debug_chart = new FunkinAction(Action.DEBUG_CHART); + var _debug_stage = new FunkinAction(Action.DEBUG_STAGE); + var _volume_up = new FunkinAction(Action.VOLUME_UP); + var _volume_down = new FunkinAction(Action.VOLUME_DOWN); + var _volume_mute = new FunkinAction(Action.VOLUME_MUTE); #if CAN_CHEAT - var _cheat = new FlxActionDigital(Action.CHEAT); + var _cheat = new FunkinAction(Action.CHEAT); #end - var byName:Map = new Map(); + var byName:Map = new Map(); public var gamepadsAdded:Array = []; public var keyboardScheme = KeyboardScheme.None; @@ -78,122 +62,142 @@ class Controls extends FlxActionSet public var UI_UP(get, never):Bool; inline function get_UI_UP() - return _ui_up.check(); + return _ui_up.checkPressed(); public var UI_LEFT(get, never):Bool; inline function get_UI_LEFT() - return _ui_left.check(); + return _ui_left.checkPressed(); public var UI_RIGHT(get, never):Bool; inline function get_UI_RIGHT() - return _ui_right.check(); + return _ui_right.checkPressed(); public var UI_DOWN(get, never):Bool; inline function get_UI_DOWN() - return _ui_down.check(); + return _ui_down.checkPressed(); public var UI_UP_P(get, never):Bool; inline function get_UI_UP_P() - return _ui_upP.check(); + return _ui_up.checkJustPressed(); public var UI_LEFT_P(get, never):Bool; inline function get_UI_LEFT_P() - return _ui_leftP.check(); + return _ui_left.checkJustPressed(); public var UI_RIGHT_P(get, never):Bool; inline function get_UI_RIGHT_P() - return _ui_rightP.check(); + return _ui_right.checkJustPressed(); public var UI_DOWN_P(get, never):Bool; inline function get_UI_DOWN_P() - return _ui_downP.check(); + return _ui_down.checkJustPressed(); public var UI_UP_R(get, never):Bool; inline function get_UI_UP_R() - return _ui_upR.check(); + return _ui_up.checkJustReleased(); public var UI_LEFT_R(get, never):Bool; inline function get_UI_LEFT_R() - return _ui_leftR.check(); + return _ui_left.checkJustReleased(); public var UI_RIGHT_R(get, never):Bool; inline function get_UI_RIGHT_R() - return _ui_rightR.check(); + return _ui_right.checkJustReleased(); public var UI_DOWN_R(get, never):Bool; inline function get_UI_DOWN_R() - return _ui_downR.check(); + return _ui_down.checkJustReleased(); + + public var UI_UP_GAMEPAD(get, never):Bool; + + inline function get_UI_UP_GAMEPAD() + return _ui_up.checkPressedGamepad(); + + public var UI_LEFT_GAMEPAD(get, never):Bool; + + inline function get_UI_LEFT_GAMEPAD() + return _ui_left.checkPressedGamepad(); + + public var UI_RIGHT_GAMEPAD(get, never):Bool; + + inline function get_UI_RIGHT_GAMEPAD() + return _ui_right.checkPressedGamepad(); + + public var UI_DOWN_GAMEPAD(get, never):Bool; + + inline function get_UI_DOWN_GAMEPAD() + return _ui_down.checkPressedGamepad(); public var NOTE_UP(get, never):Bool; inline function get_NOTE_UP() - return _note_up.check(); + return _note_up.checkPressed(); public var NOTE_LEFT(get, never):Bool; inline function get_NOTE_LEFT() - return _note_left.check(); + return _note_left.checkPressed(); public var NOTE_RIGHT(get, never):Bool; inline function get_NOTE_RIGHT() - return _note_right.check(); + return _note_right.checkPressed(); public var NOTE_DOWN(get, never):Bool; inline function get_NOTE_DOWN() - return _note_down.check(); + return _note_down.checkPressed(); public var NOTE_UP_P(get, never):Bool; inline function get_NOTE_UP_P() - return _note_upP.check(); + return _note_up.checkJustPressed(); public var NOTE_LEFT_P(get, never):Bool; inline function get_NOTE_LEFT_P() - return _note_leftP.check(); + return _note_left.checkJustPressed(); public var NOTE_RIGHT_P(get, never):Bool; inline function get_NOTE_RIGHT_P() - return _note_rightP.check(); + return _note_right.checkJustPressed(); public var NOTE_DOWN_P(get, never):Bool; inline function get_NOTE_DOWN_P() - return _note_downP.check(); + return _note_down.checkJustPressed(); public var NOTE_UP_R(get, never):Bool; inline function get_NOTE_UP_R() - return _note_upR.check(); + return _note_up.checkJustReleased(); public var NOTE_LEFT_R(get, never):Bool; inline function get_NOTE_LEFT_R() - return _note_leftR.check(); + return _note_left.checkJustReleased(); public var NOTE_RIGHT_R(get, never):Bool; inline function get_NOTE_RIGHT_R() - return _note_rightR.check(); + return _note_right.checkJustReleased(); public var NOTE_DOWN_R(get, never):Bool; inline function get_NOTE_DOWN_R() - return _note_downR.check(); + return _note_down.checkJustReleased(); public var ACCEPT(get, never):Bool; @@ -270,26 +274,10 @@ class Controls extends FlxActionSet add(_ui_left); add(_ui_right); add(_ui_down); - add(_ui_upP); - add(_ui_leftP); - add(_ui_rightP); - add(_ui_downP); - add(_ui_upR); - add(_ui_leftR); - add(_ui_rightR); - add(_ui_downR); add(_note_up); add(_note_left); add(_note_right); add(_note_down); - add(_note_upP); - add(_note_leftP); - add(_note_rightP); - add(_note_downP); - add(_note_upR); - add(_note_leftR); - add(_note_rightR); - add(_note_downR); add(_accept); add(_back); add(_pause); @@ -303,8 +291,16 @@ class Controls extends FlxActionSet add(_cheat); #end - for (action in digitalActions) - byName[action.name] = action; + for (action in digitalActions) { + if (Std.isOfType(action, FunkinAction)) { + var funkinAction:FunkinAction = cast action; + byName[funkinAction.name] = funkinAction; + if (funkinAction.namePressed != null) + byName[funkinAction.namePressed] = funkinAction; + if (funkinAction.nameReleased != null) + byName[funkinAction.nameReleased] = funkinAction; + } + } if (scheme == null) scheme = None; @@ -317,14 +313,17 @@ class Controls extends FlxActionSet super.update(); } - // inline - public function checkByName(name:Action):Bool + public function check(name:Action, trigger:FlxInputState = JUST_PRESSED, gamepadOnly:Bool = false):Bool { #if debug if (!byName.exists(name)) throw 'Invalid name: $name'; #end - return byName[name].check(); + var action = byName[name]; + if (gamepadOnly) + return action.checkFiltered(trigger, GAMEPAD); + else + return action.checkFiltered(trigger); } public function getKeysForAction(name:Action):Array { @@ -411,36 +410,36 @@ class Controls extends FlxActionSet { case UI_UP: func(_ui_up, PRESSED); - func(_ui_upP, JUST_PRESSED); - func(_ui_upR, JUST_RELEASED); + func(_ui_up, JUST_PRESSED); + func(_ui_up, JUST_RELEASED); case UI_LEFT: func(_ui_left, PRESSED); - func(_ui_leftP, JUST_PRESSED); - func(_ui_leftR, JUST_RELEASED); + func(_ui_left, JUST_PRESSED); + func(_ui_left, JUST_RELEASED); case UI_RIGHT: func(_ui_right, PRESSED); - func(_ui_rightP, JUST_PRESSED); - func(_ui_rightR, JUST_RELEASED); + func(_ui_right, JUST_PRESSED); + func(_ui_right, JUST_RELEASED); case UI_DOWN: func(_ui_down, PRESSED); - func(_ui_downP, JUST_PRESSED); - func(_ui_downR, JUST_RELEASED); + func(_ui_down, JUST_PRESSED); + func(_ui_down, JUST_RELEASED); case NOTE_UP: func(_note_up, PRESSED); - func(_note_upP, JUST_PRESSED); - func(_note_upR, JUST_RELEASED); + func(_note_up, JUST_PRESSED); + func(_note_up, JUST_RELEASED); case NOTE_LEFT: func(_note_left, PRESSED); - func(_note_leftP, JUST_PRESSED); - func(_note_leftR, JUST_RELEASED); + func(_note_left, JUST_PRESSED); + func(_note_left, JUST_RELEASED); case NOTE_RIGHT: func(_note_right, PRESSED); - func(_note_rightP, JUST_PRESSED); - func(_note_rightR, JUST_RELEASED); + func(_note_right, JUST_PRESSED); + func(_note_right, JUST_RELEASED); case NOTE_DOWN: func(_note_down, PRESSED); - func(_note_downP, JUST_PRESSED); - func(_note_downR, JUST_RELEASED); + func(_note_down, JUST_PRESSED); + func(_note_down, JUST_RELEASED); case ACCEPT: func(_accept, JUST_PRESSED); case BACK: @@ -1053,6 +1052,173 @@ typedef Swipes = ?curTouchPos:FlxPoint }; +/** + * An FlxActionDigital with additional functionality, including: + * - Combining `pressed` and `released` inputs into one action. + * - Filtering by input method (`KEYBOARD`, `MOUSE`, `GAMEPAD`, etc). + */ +class FunkinAction extends FlxActionDigital { + public var namePressed(default, null):Null; + public var nameReleased(default, null):Null; + + var cache:Map = []; + + public function new(?name:String = "", ?namePressed:String, ?nameReleased:String) + { + super(name); + + this.namePressed = namePressed; + this.nameReleased = nameReleased; + } + + /** + * Input checks default to whether the input was just pressed, on any input device. + */ + public override function check():Bool { + return checkFiltered(JUST_PRESSED); + } + + /** + * Check whether the input is currently being held. + */ + public function checkPressed():Bool { + return checkFiltered(PRESSED); + } + + /** + * Check whether the input is currently being held, and was not held last frame. + */ + public function checkJustPressed():Bool { + return checkFiltered(JUST_PRESSED); + } + + /** + * Check whether the input is not currently being held. + */ + public function checkReleased():Bool { + return checkFiltered(RELEASED); + } + + /** + * Check whether the input is not currently being held, and was held last frame. + */ + public function checkJustReleased():Bool { + return checkFiltered(JUST_RELEASED); + } + + /** + * Check whether the input is currently being held by a gamepad device. + */ + public function checkPressedGamepad():Bool { + return checkFiltered(PRESSED, GAMEPAD); + } + + /** + * Check whether the input is currently being held by a gamepad device, and was not held last frame. + */ + public function checkJustPressedGamepad():Bool { + return checkFiltered(JUST_PRESSED, GAMEPAD); + } + + /** + * Check whether the input is not currently being held by a gamepad device. + */ + public function checkReleasedGamepad():Bool { + return checkFiltered(RELEASED, GAMEPAD); + } + + /** + * Check whether the input is not currently being held by a gamepad device, and was held last frame. + */ + public function checkJustReleasedGamepad():Bool { + return checkFiltered(JUST_RELEASED, GAMEPAD); + } + + public function checkMultiFiltered(?filterTriggers:Array, ?filterDevices:Array):Bool { + if (filterTriggers == null) { + filterTriggers = [PRESSED, JUST_PRESSED]; + } + if (filterDevices == null) { + filterDevices = []; + } + + // Perform checkFiltered for each combination. + for (i in filterTriggers) { + if (filterDevices.length == 0) { + if (checkFiltered(i)) { + return true; + } + } else { + for (j in filterDevices) { + if (checkFiltered(i, j)) { + return true; + } + } + } + } + return false; + } + + /** + * Performs the functionality of `FlxActionDigital.check()`, but with optional filters. + * @param action The action to check for. + * @param filterTrigger Optionally filter by trigger condition (`JUST_PRESSED`, `PRESSED`, `JUST_RELEASED`, `RELEASED`). + * @param filterDevice Optionally filter by device (`KEYBOARD`, `MOUSE`, `GAMEPAD`, `OTHER`). + */ + public function checkFiltered(?filterTrigger:FlxInputState, ?filterDevice:FlxInputDevice):Bool { + // The normal + + // Make sure we only update the inputs once per frame. + var key = '${filterTrigger}:${filterDevice}'; + var cacheEntry = cache.get(key); + + if (cacheEntry != null && cacheEntry.timestamp == FlxG.game.ticks) { + return cacheEntry.value; + } + // Use a for loop instead so we can remove inputs while iterating. + + // We don't return early because we need to call check() on ALL inputs. + var result = false; + var len = inputs != null ? inputs.length : 0; + for (i in 0...len) + { + var j = len - i - 1; + var input = inputs[j]; + + // Filter out dead inputs. + if (input.destroyed) + { + inputs.splice(j, 1); + continue; + } + + // Update the input. + input.update(); + + // Check whether the input is the right trigger. + if (filterTrigger != null && input.trigger != filterTrigger) { + continue; + } + + // Check whether the input is the right device. + if (filterDevice != null && input.device != filterDevice) { + continue; + } + + // Check whether the input has triggered. + if (input.check(this)) + { + result = true; + } + } + + // We need to cache this result. + cache.set(key, {timestamp: FlxG.game.ticks, value: result}); + + return result; + } +} + class FlxActionInputDigitalMobileSwipeGameplay extends FlxActionInputDigital { var touchMap:Map = new Map(); @@ -1242,8 +1408,7 @@ enum Control #end } -enum -abstract Action(String) to String from String +enum abstract Action(String) to String from String { // NOTE var NOTE_UP = "note_up"; diff --git a/source/funkin/input/TurboActionHandler.hx b/source/funkin/input/TurboActionHandler.hx new file mode 100644 index 000000000..9425db8cd --- /dev/null +++ b/source/funkin/input/TurboActionHandler.hx @@ -0,0 +1,111 @@ +package funkin.input; + +import flixel.input.keyboard.FlxKey; +import flixel.FlxBasic; +import funkin.input.Controls; +import funkin.input.Controls.Action; + +/** + * Handles repeating behavior when holding down a control action. + * + * When the `action` is pressed, `activated` will be true for the first frame, + * then wait `delay` seconds before becoming true for one frame every `interval` seconds. + * + * Example: Pressing Ctrl+Z will undo, while holding Ctrl+Z will start to undo repeatedly. + */ +class TurboActionHandler extends FlxBasic +{ + /** + * Default delay before repeating. + */ + static inline final DEFAULT_DELAY:Float = 0.4; + + /** + * Default interval between repeats. + */ + static inline final DEFAULT_INTERVAL:Float = 0.1; + + /** + * Whether the action for this handler is pressed. + */ + public var pressed(get, never):Bool; + + /** + * Whether the action for this handler is pressed, + * and the handler is ready to repeat. + */ + public var activated(default, null):Bool = false; + + /** + * The Funkin Controls handler. + */ + var controls(get, never):Controls; + + function get_controls():Controls + { + return PlayerSettings.player1.controls; + } + + var action:Action; + + var delay:Float; + var interval:Float; + var gamepadOnly:Bool; + + var pressedTime:Float = 0; + + function new(action:Action, delay:Float = DEFAULT_DELAY, interval:Float = DEFAULT_INTERVAL, gamepadOnly:Bool = false) + { + super(); + this.action = action; + this.delay = delay; + this.interval = interval; + this.gamepadOnly = gamepadOnly; + } + + function get_pressed():Bool + { + return controls.check(action, PRESSED, gamepadOnly); + } + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + if (pressed) + { + if (pressedTime == 0) + { + activated = true; + } + else if (pressedTime >= (delay + interval)) + { + activated = true; + pressedTime -= interval; + } + else + { + activated = false; + } + pressedTime += elapsed; + } + else + { + pressedTime = 0; + activated = false; + } + } + + /** + * Builds a TurboActionHandler that monitors from a single key. + * @param inputKey The key to monitor. + * @param delay How long to wait before repeating. + * @param repeatDelay How long to wait between repeats. + * @return A TurboActionHandler + */ + public static overload inline extern function build(action:Action, ?delay:Float = DEFAULT_DELAY, ?interval:Float = DEFAULT_INTERVAL, + ?gamepadOnly:Bool = false):TurboActionHandler + { + return new TurboActionHandler(action, delay, interval); + } +} diff --git a/source/funkin/input/TurboButtonHandler.hx b/source/funkin/input/TurboButtonHandler.hx new file mode 100644 index 000000000..63c2a294b --- /dev/null +++ b/source/funkin/input/TurboButtonHandler.hx @@ -0,0 +1,127 @@ +package funkin.input; + +import flixel.input.gamepad.FlxGamepadInputID; +import flixel.input.gamepad.FlxGamepad; +import flixel.FlxBasic; + +/** + * Handles repeating behavior when holding down a gamepad button or button combination. + * + * When the `inputs` are pressed, `activated` will be true for the first frame, + * then wait `delay` seconds before becoming true for one frame every `interval` seconds. + * + * Example: Pressing Ctrl+Z will undo, while holding Ctrl+Z will start to undo repeatedly. + */ +class TurboButtonHandler extends FlxBasic +{ + /** + * Default delay before repeating. + */ + static inline final DEFAULT_DELAY:Float = 0.4; + + /** + * Default interval between repeats. + */ + static inline final DEFAULT_INTERVAL:Float = 0.1; + + /** + * Whether all of the keys for this handler are pressed. + */ + public var allPressed(get, never):Bool; + + /** + * Whether all of the keys for this handler are activated, + * and the handler is ready to repeat. + */ + public var activated(default, null):Bool = false; + + var inputs:Array; + var delay:Float; + var interval:Float; + var targetGamepad:FlxGamepad; + + var allPressedTime:Float = 0; + + function new(inputs:Array, delay:Float = DEFAULT_DELAY, interval:Float = DEFAULT_INTERVAL, ?targetGamepad:FlxGamepad) + { + super(); + this.inputs = inputs; + this.delay = delay; + this.interval = interval; + this.targetGamepad = targetGamepad ?? FlxG.gamepads.firstActive; + } + + function get_allPressed():Bool + { + if (targetGamepad == null) return false; + if (inputs == null || inputs.length == 0) return false; + if (inputs.length == 1) return targetGamepad.anyPressed(inputs); + + // Check if ANY keys are unpressed + for (input in inputs) + { + if (!targetGamepad.anyPressed([input])) return false; + } + return true; + } + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + // Try to find a gamepad if we don't have one + if (targetGamepad == null) + { + targetGamepad = FlxG.gamepads.firstActive; + } + + if (allPressed) + { + if (allPressedTime == 0) + { + activated = true; + } + else if (allPressedTime >= (delay + interval)) + { + activated = true; + allPressedTime -= interval; + } + else + { + activated = false; + } + allPressedTime += elapsed; + } + else + { + allPressedTime = 0; + activated = false; + } + } + + /** + * Builds a TurboButtonHandler that monitors from a single input. + * @param input The input to monitor. + * @param delay How long to wait before repeating. + * @param repeatDelay How long to wait between repeats. + * @return A TurboKeyHandler + */ + public static overload inline extern function build(input:FlxGamepadInputID, ?delay:Float = DEFAULT_DELAY, + ?interval:Float = DEFAULT_INTERVAL):TurboButtonHandler + { + return new TurboButtonHandler([input], delay, interval); + } + + /** + * Builds a TurboKeyHandler that monitors a key combination. + * @param inputs The combination of inputs to monitor. + * @param delay How long to wait before repeating. + * @param repeatDelay How long to wait between repeats. + * @return A TurboKeyHandler + */ + public static overload inline extern function build(inputs:Array, ?delay:Float = DEFAULT_DELAY, + ?interval:Float = DEFAULT_INTERVAL):TurboButtonHandler + { + return new TurboButtonHandler(inputs, delay, interval); + } +} diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 1773a84fe..f3236578a 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1,24 +1,25 @@ package funkin.ui.debug.charting; -import funkin.util.logging.CrashHandler; -import haxe.ui.containers.HBox; -import haxe.ui.containers.Grid; -import haxe.ui.containers.ScrollView; -import haxe.ui.containers.menus.MenuBar; import flixel.addons.display.FlxSliceSprite; import flixel.addons.display.FlxTiledSprite; import flixel.addons.transition.FlxTransitionableState; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.FlxSubState; +import flixel.graphics.FlxGraphic; +import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup; +import flixel.input.gamepad.FlxGamepadInputID; import flixel.input.keyboard.FlxKey; +import flixel.input.mouse.FlxMouseEvent; import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.graphics.FlxGraphic; import flixel.math.FlxRect; import flixel.sound.FlxSound; +import flixel.system.debug.log.LogStyle; import flixel.system.FlxAssets.FlxSoundAsset; +import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.tweens.misc.VarTween; @@ -26,19 +27,31 @@ import haxe.ui.Toolkit; import flixel.util.FlxColor; import flixel.util.FlxSort; import flixel.util.FlxTimer; -import funkin.audio.visualize.PolygonSpectogram; -import funkin.audio.VoicesGroup; import funkin.audio.FunkinSound; +import funkin.audio.visualize.PolygonSpectogram; +import funkin.audio.visualize.PolygonSpectogram; +import funkin.audio.visualize.PolygonVisGroup; +import funkin.audio.VoicesGroup; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongData.SongCharacterData; +import funkin.data.song.SongData.SongCharacterData; +import funkin.data.song.SongData.SongChartData; import funkin.data.song.SongData.SongChartData; import funkin.data.song.SongData.SongEventData; +import funkin.data.song.SongData.SongEventData; import funkin.data.song.SongData.SongMetadata; +import funkin.data.song.SongData.SongMetadata; +import funkin.data.song.SongData.SongNoteData; import funkin.data.song.SongData.SongNoteData; import funkin.data.song.SongData.SongOffsets; import funkin.data.song.SongDataUtils; +import funkin.data.song.SongDataUtils; import funkin.data.song.SongRegistry; +import funkin.data.song.SongRegistry; +import funkin.input.Controls.Action; import funkin.input.Cursor; +import funkin.input.TurboActionHandler; +import funkin.input.TurboButtonHandler; import funkin.input.TurboKeyHandler; import funkin.modding.events.ScriptEvent; import funkin.play.character.BaseCharacter.CharacterType; @@ -48,20 +61,13 @@ import funkin.play.components.HealthIcon; import funkin.play.notes.NoteSprite; import funkin.play.PlayState; import funkin.play.song.Song; -import funkin.data.song.SongData.SongChartData; -import funkin.data.song.SongRegistry; -import funkin.data.song.SongData.SongEventData; -import funkin.data.song.SongData.SongMetadata; -import funkin.data.song.SongData.SongNoteData; -import funkin.data.song.SongData.SongCharacterData; -import funkin.data.song.SongDataUtils; -import funkin.ui.debug.charting.commands.ChartEditorCommand; import funkin.play.stage.StageData; import funkin.save.Save; import funkin.ui.debug.charting.commands.AddEventsCommand; import funkin.ui.debug.charting.commands.AddNotesCommand; import funkin.ui.debug.charting.commands.ChartEditorCommand; import funkin.ui.debug.charting.commands.ChartEditorCommand; +import funkin.ui.debug.charting.commands.ChartEditorCommand; import funkin.ui.debug.charting.commands.CutItemsCommand; import funkin.ui.debug.charting.commands.DeselectAllItemsCommand; import funkin.ui.debug.charting.commands.DeselectItemsCommand; @@ -80,17 +86,20 @@ import funkin.ui.debug.charting.commands.SelectItemsCommand; import funkin.ui.debug.charting.commands.SetItemSelectionCommand; import funkin.ui.debug.charting.components.ChartEditorEventSprite; import funkin.ui.debug.charting.components.ChartEditorHoldNoteSprite; +import funkin.ui.debug.charting.components.ChartEditorMeasureTicks; import funkin.ui.debug.charting.components.ChartEditorNotePreview; import funkin.ui.debug.charting.components.ChartEditorNoteSprite; import funkin.ui.debug.charting.components.ChartEditorMeasureTicks; import funkin.ui.debug.charting.components.ChartEditorPlaybarHead; import funkin.ui.debug.charting.components.ChartEditorSelectionSquareSprite; import funkin.ui.debug.charting.handlers.ChartEditorShortcutHandler; +import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox; import funkin.ui.haxeui.components.CharacterPlayer; import funkin.ui.haxeui.HaxeUIState; import funkin.ui.mainmenu.MainMenuState; import funkin.util.Constants; import funkin.util.FileUtil; +import funkin.util.logging.CrashHandler; import funkin.util.SortUtil; import funkin.util.WindowUtil; import haxe.DynamicAccess; @@ -98,22 +107,25 @@ import haxe.io.Bytes; import haxe.io.Path; import haxe.ui.backend.flixel.UIRuntimeState; import haxe.ui.backend.flixel.UIState; -import haxe.ui.components.DropDown; -import haxe.ui.components.Label; import haxe.ui.components.Button; +import haxe.ui.components.DropDown; +import haxe.ui.components.Image; +import haxe.ui.components.Label; import haxe.ui.components.NumberStepper; import haxe.ui.components.Slider; import haxe.ui.components.TextField; import haxe.ui.containers.dialogs.CollapsibleDialog; import haxe.ui.containers.Frame; +import haxe.ui.containers.Grid; +import haxe.ui.containers.HBox; import haxe.ui.containers.menus.Menu; import haxe.ui.containers.menus.MenuBar; -import haxe.ui.containers.menus.MenuItem; +import haxe.ui.containers.menus.MenuBar; import haxe.ui.containers.menus.MenuCheckBox; +import haxe.ui.containers.menus.MenuItem; +import haxe.ui.containers.ScrollView; import haxe.ui.containers.TreeView; import haxe.ui.containers.TreeViewNode; -import haxe.ui.components.Image; -import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox; import haxe.ui.core.Component; import haxe.ui.core.Screen; import haxe.ui.events.DragEvent; @@ -122,12 +134,6 @@ import haxe.ui.events.UIEvent; import haxe.ui.events.UIEvent; import haxe.ui.focus.FocusManager; import openfl.display.BitmapData; -import funkin.audio.visualize.PolygonSpectogram; -import flixel.group.FlxGroup.FlxTypedGroup; -import funkin.audio.visualize.PolygonVisGroup; -import flixel.input.mouse.FlxMouseEvent; -import flixel.text.FlxText; -import flixel.system.debug.log.LogStyle; using Lambda; @@ -360,6 +366,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } } + updatePlayheadGhostHoldNotes(); + // Move the rendered notes to the correct position. renderedNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); renderedHoldNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); @@ -429,6 +437,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Move the playhead sprite to the correct position. gridPlayhead.y = this.playheadPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); + updatePlayheadGhostHoldNotes(); + return this.playheadPositionInPixels; } @@ -720,6 +730,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var currentPlaceNoteData:Null = null; + /** + * The SongNoteData which is currently being placed, for each column. + * `null` if the user isn't currently placing a note. + * As the user moves down, we will update this note's sustain length, and finalize the note when they release. + */ + var currentLiveInputPlaceNoteData:Array = []; + // Note Movement /** @@ -750,6 +767,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var dragLengthCurrent:Float = 0; + /** + * The current length of the hold note we are placing with the playhead, in steps. + * Play a sound when this value changes. + */ + var playheadDragLengthCurrent:Array = []; + /** * Flip-flop to alternate between two stretching sounds. */ @@ -1020,6 +1043,66 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var pageDownKeyHandler:TurboKeyHandler = TurboKeyHandler.build(FlxKey.PAGEDOWN); + /** + * Variable used to track how long the user has been holding up on the dpad. + */ + var dpadUpGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.DPAD_UP); + + /** + * Variable used to track how long the user has been holding down on the dpad. + */ + var dpadDownGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.DPAD_DOWN); + + /** + * Variable used to track how long the user has been holding left on the dpad. + */ + var dpadLeftGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.DPAD_LEFT); + + /** + * Variable used to track how long the user has been holding right on the dpad. + */ + var dpadRightGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.DPAD_RIGHT); + + /** + * Variable used to track how long the user has been holding up on the left stick. + */ + var leftStickUpGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.LEFT_STICK_DIGITAL_UP); + + /** + * Variable used to track how long the user has been holding down on the left stick. + */ + var leftStickDownGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.LEFT_STICK_DIGITAL_DOWN); + + /** + * Variable used to track how long the user has been holding left on the left stick. + */ + var leftStickLeftGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.LEFT_STICK_DIGITAL_LEFT); + + /** + * Variable used to track how long the user has been holding right on the left stick. + */ + var leftStickRightGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.LEFT_STICK_DIGITAL_RIGHT); + + /** + * Variable used to track how long the user has been holding up on the right stick. + */ + var rightStickUpGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.RIGHT_STICK_DIGITAL_UP); + + /** + * Variable used to track how long the user has been holding down on the right stick. + */ + var rightStickDownGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.RIGHT_STICK_DIGITAL_DOWN); + + /** + * Variable used to track how long the user has been holding left on the right stick. + */ + var rightStickLeftGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.RIGHT_STICK_DIGITAL_LEFT); + + /** + * Variable used to track how long the user has been holding right on the right stick. + */ + var rightStickRightGamepadHandler:TurboButtonHandler = TurboButtonHandler.build(FlxGamepadInputID.RIGHT_STICK_DIGITAL_RIGHT); + /** * AUDIO AND SOUND DATA */ @@ -1733,10 +1816,15 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState var gridGhostNote:Null = null; /** - * A sprite used to indicate the note that will be placed on click. + * A sprite used to indicate the hold note that will be placed on click. */ var gridGhostHoldNote:Null = null; + /** + * A sprite used to indicate the hold note that will be placed on button release. + */ + var gridPlayheadGhostHoldNotes:Array = []; + /** * A sprite used to indicate the event that will be placed on click. */ @@ -2126,11 +2214,23 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState gridGhostHoldNote = new ChartEditorHoldNoteSprite(this); gridGhostHoldNote.alpha = 0.6; - gridGhostHoldNote.noteData = new SongNoteData(0, 0, 0, ""); + gridGhostHoldNote.noteData = null; gridGhostHoldNote.visible = false; add(gridGhostHoldNote); gridGhostHoldNote.zIndex = 11; + while (gridPlayheadGhostHoldNotes.length < (STRUMLINE_SIZE * 2)) + { + var ghost = new ChartEditorHoldNoteSprite(this); + ghost.alpha = 0.6; + ghost.noteData = null; + ghost.visible = false; + add(ghost); + ghost.zIndex = 11; + + gridPlayheadGhostHoldNotes.push(ghost); + } + gridGhostEvent = new ChartEditorEventSprite(this); gridGhostEvent.alpha = 0.6; gridGhostEvent.eventData = new SongEventData(-1, '', {}); @@ -2702,6 +2802,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ function setupTurboKeyHandlers():Void { + // Keyboard shortcuts add(undoKeyHandler); add(redoKeyHandler); add(upKeyHandler); @@ -2710,6 +2811,20 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState add(sKeyHandler); add(pageUpKeyHandler); add(pageDownKeyHandler); + + // Gamepad inputs + add(dpadUpGamepadHandler); + add(dpadDownGamepadHandler); + add(dpadLeftGamepadHandler); + add(dpadRightGamepadHandler); + add(leftStickUpGamepadHandler); + add(leftStickDownGamepadHandler); + add(leftStickLeftGamepadHandler); + add(leftStickRightGamepadHandler); + add(rightStickUpGamepadHandler); + add(rightStickDownGamepadHandler); + add(rightStickLeftGamepadHandler); + add(rightStickRightGamepadHandler); } /** @@ -2865,6 +2980,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState handleTestKeybinds(); handleHelpKeybinds(); + this.handleGamepadControls(); + #if debug handleQuickWatch(); #end @@ -3370,32 +3487,56 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Up Arrow = Scroll Up if (upKeyHandler.activated && currentLiveInputStyle == None) { - scrollAmount = -GRID_SIZE * 0.25 * 25.0; + scrollAmount = -GRID_SIZE * 4; shouldPause = true; } // Down Arrow = Scroll Down if (downKeyHandler.activated && currentLiveInputStyle == None) { - scrollAmount = GRID_SIZE * 0.25 * 25.0; + scrollAmount = GRID_SIZE * 4; shouldPause = true; } // W = Scroll Up (doesn't work with Ctrl+Scroll) if (wKeyHandler.activated && currentLiveInputStyle == None && !FlxG.keys.pressed.CONTROL) { - scrollAmount = -GRID_SIZE * 0.25 * 25.0; + scrollAmount = -GRID_SIZE * 4; shouldPause = true; } // S = Scroll Down (doesn't work with Ctrl+Scroll) if (sKeyHandler.activated && currentLiveInputStyle == None && !FlxG.keys.pressed.CONTROL) { - scrollAmount = GRID_SIZE * 0.25 * 25.0; + scrollAmount = GRID_SIZE * 4; shouldPause = true; } - // PAGE UP = Jump up to nearest measure - if (pageUpKeyHandler.activated) + // GAMEPAD LEFT STICK UP = Scroll Up by 1 note snap + if (leftStickUpGamepadHandler.activated) { + scrollAmount = -GRID_SIZE * noteSnapRatio; + shouldPause = true; + } + // GAMEPAD LEFT STICK DOWN = Scroll Down by 1 note snap + if (leftStickDownGamepadHandler.activated) + { + scrollAmount = GRID_SIZE * noteSnapRatio; + shouldPause = true; + } + + // GAMEPAD RIGHT STICK UP = Scroll Up by 1 note snap (playhead only) + if (rightStickUpGamepadHandler.activated) + { + playheadAmount = -GRID_SIZE * noteSnapRatio; + shouldPause = true; + } + // GAMEPAD RIGHT STICK DOWN = Scroll Down by 1 note snap (playhead only) + if (rightStickDownGamepadHandler.activated) + { + playheadAmount = GRID_SIZE * noteSnapRatio; + shouldPause = true; + } + + var funcJumpUp = (playheadOnly:Bool) -> { var measureHeight:Float = GRID_SIZE * 4 * Conductor.instance.beatsPerMeasure; var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; var targetScrollPosition:Float = Math.floor(playheadPos / measureHeight) * measureHeight; @@ -3405,20 +3546,37 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { targetScrollPosition -= GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.instance.beatsPerMeasure; } - scrollAmount = targetScrollPosition - playheadPos; + if (playheadOnly) + { + playheadAmount = targetScrollPosition - playheadPos; + } + else + { + scrollAmount = targetScrollPosition - playheadPos; + } + } + + // PAGE UP = Jump up to nearest measure + // GAMEPAD LEFT STICK LEFT = Jump up to nearest measure + if (pageUpKeyHandler.activated || leftStickLeftGamepadHandler.activated) + { + funcJumpUp(false); + shouldPause = true; + } + if (rightStickLeftGamepadHandler.activated) + { + funcJumpUp(true); shouldPause = true; } if (playbarButtonPressed == 'playbarBack') { playbarButtonPressed = ''; - scrollAmount = -GRID_SIZE * 4 * Conductor.instance.beatsPerMeasure; + funcJumpUp(false); shouldPause = true; } - // PAGE DOWN = Jump down to nearest measure - if (pageDownKeyHandler.activated) - { + var funcJumpDown = (playheadOnly:Bool) -> { var measureHeight:Float = GRID_SIZE * 4 * Conductor.instance.beatsPerMeasure; var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; var targetScrollPosition:Float = Math.ceil(playheadPos / measureHeight) * measureHeight; @@ -3428,26 +3586,46 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { targetScrollPosition += GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.instance.beatsPerMeasure; } - scrollAmount = targetScrollPosition - playheadPos; + if (playheadOnly) + { + playheadAmount = targetScrollPosition - playheadPos; + } + else + { + scrollAmount = targetScrollPosition - playheadPos; + } + } + + // PAGE DOWN = Jump down to nearest measure + // GAMEPAD LEFT STICK RIGHT = Jump down to nearest measure + if (pageDownKeyHandler.activated || leftStickRightGamepadHandler.activated) + { + funcJumpDown(false); + shouldPause = true; + } + if (rightStickRightGamepadHandler.activated) + { + funcJumpDown(true); shouldPause = true; } if (playbarButtonPressed == 'playbarForward') { playbarButtonPressed = ''; - scrollAmount = GRID_SIZE * 4 * Conductor.instance.beatsPerMeasure; + funcJumpDown(false); shouldPause = true; } // SHIFT + Scroll = Scroll Fast - if (FlxG.keys.pressed.SHIFT) + // GAMEPAD LEFT STICK CLICK + Scroll = Scroll Fast + if (FlxG.keys.pressed.SHIFT || (FlxG.gamepads.firstActive?.pressed?.LEFT_STICK_CLICK ?? false)) { scrollAmount *= 2; } // CONTROL + Scroll = Scroll Precise if (FlxG.keys.pressed.CONTROL) { - scrollAmount /= 10; + scrollAmount /= 4; } // Alt + Drag = Scroll but move the playhead the same amount. @@ -4520,37 +4698,77 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function handlePlayhead():Void { - // Place notes at the playhead. + // Place notes at the playhead with the keyboard. switch (currentLiveInputStyle) { case ChartEditorLiveInputStyle.WASD: if (FlxG.keys.justPressed.A) placeNoteAtPlayhead(4); + if (FlxG.keys.justReleased.A) finishPlaceNoteAtPlayhead(4); if (FlxG.keys.justPressed.S) placeNoteAtPlayhead(5); + if (FlxG.keys.justReleased.S) finishPlaceNoteAtPlayhead(5); if (FlxG.keys.justPressed.W) placeNoteAtPlayhead(6); + if (FlxG.keys.justReleased.W) finishPlaceNoteAtPlayhead(6); if (FlxG.keys.justPressed.D) placeNoteAtPlayhead(7); + if (FlxG.keys.justReleased.D) finishPlaceNoteAtPlayhead(7); if (FlxG.keys.justPressed.LEFT) placeNoteAtPlayhead(0); + if (FlxG.keys.justReleased.LEFT) finishPlaceNoteAtPlayhead(0); if (FlxG.keys.justPressed.DOWN) placeNoteAtPlayhead(1); + if (FlxG.keys.justReleased.DOWN) finishPlaceNoteAtPlayhead(1); if (FlxG.keys.justPressed.UP) placeNoteAtPlayhead(2); + if (FlxG.keys.justReleased.UP) finishPlaceNoteAtPlayhead(2); if (FlxG.keys.justPressed.RIGHT) placeNoteAtPlayhead(3); + if (FlxG.keys.justReleased.RIGHT) finishPlaceNoteAtPlayhead(3); case ChartEditorLiveInputStyle.NumberKeys: // Flipped because Dad is on the left but represents data 0-3. if (FlxG.keys.justPressed.ONE) placeNoteAtPlayhead(4); + if (FlxG.keys.justReleased.ONE) finishPlaceNoteAtPlayhead(4); if (FlxG.keys.justPressed.TWO) placeNoteAtPlayhead(5); + if (FlxG.keys.justReleased.TWO) finishPlaceNoteAtPlayhead(5); if (FlxG.keys.justPressed.THREE) placeNoteAtPlayhead(6); + if (FlxG.keys.justReleased.THREE) finishPlaceNoteAtPlayhead(6); if (FlxG.keys.justPressed.FOUR) placeNoteAtPlayhead(7); + if (FlxG.keys.justReleased.FOUR) finishPlaceNoteAtPlayhead(7); if (FlxG.keys.justPressed.FIVE) placeNoteAtPlayhead(0); + if (FlxG.keys.justReleased.FIVE) finishPlaceNoteAtPlayhead(0); if (FlxG.keys.justPressed.SIX) placeNoteAtPlayhead(1); if (FlxG.keys.justPressed.SEVEN) placeNoteAtPlayhead(2); + if (FlxG.keys.justReleased.SEVEN) finishPlaceNoteAtPlayhead(2); if (FlxG.keys.justPressed.EIGHT) placeNoteAtPlayhead(3); + if (FlxG.keys.justReleased.EIGHT) finishPlaceNoteAtPlayhead(3); case ChartEditorLiveInputStyle.None: // Do nothing. } + + // Place notes at the playhead with the gamepad. + if (FlxG.gamepads.firstActive != null) + { + if (FlxG.gamepads.firstActive.justPressed.DPAD_LEFT) placeNoteAtPlayhead(4); + if (FlxG.gamepads.firstActive.justReleased.DPAD_LEFT) finishPlaceNoteAtPlayhead(4); + if (FlxG.gamepads.firstActive.justPressed.DPAD_DOWN) placeNoteAtPlayhead(5); + if (FlxG.gamepads.firstActive.justReleased.DPAD_DOWN) finishPlaceNoteAtPlayhead(5); + if (FlxG.gamepads.firstActive.justPressed.DPAD_UP) placeNoteAtPlayhead(6); + if (FlxG.gamepads.firstActive.justReleased.DPAD_UP) finishPlaceNoteAtPlayhead(6); + if (FlxG.gamepads.firstActive.justPressed.DPAD_RIGHT) placeNoteAtPlayhead(7); + if (FlxG.gamepads.firstActive.justReleased.DPAD_RIGHT) finishPlaceNoteAtPlayhead(7); + + if (FlxG.gamepads.firstActive.justPressed.X) placeNoteAtPlayhead(0); + if (FlxG.gamepads.firstActive.justReleased.X) finishPlaceNoteAtPlayhead(0); + if (FlxG.gamepads.firstActive.justPressed.A) placeNoteAtPlayhead(1); + if (FlxG.gamepads.firstActive.justReleased.A) finishPlaceNoteAtPlayhead(1); + if (FlxG.gamepads.firstActive.justPressed.Y) placeNoteAtPlayhead(2); + if (FlxG.gamepads.firstActive.justReleased.Y) finishPlaceNoteAtPlayhead(2); + if (FlxG.gamepads.firstActive.justPressed.B) placeNoteAtPlayhead(3); + if (FlxG.gamepads.firstActive.justReleased.B) finishPlaceNoteAtPlayhead(3); + } } function placeNoteAtPlayhead(column:Int):Void { + // SHIFT + press or LEFT_SHOULDER + press to remove notes instead of placing them. + var removeNoteInstead:Bool = FlxG.keys.pressed.SHIFT || (FlxG.gamepads.firstActive?.pressed?.LEFT_SHOULDER ?? false); + var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / noteSnapRatio; var playheadPosStep:Int = Std.int(Math.floor(playheadPosFractionalStep)); @@ -4561,10 +4779,18 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState playheadPosSnappedMs + Conductor.instance.stepLengthMs * noteSnapRatio); notesAtPos = SongDataUtils.getNotesWithData(notesAtPos, [column]); - if (notesAtPos.length == 0) + if (notesAtPos.length == 0 && !removeNoteInstead) { var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, noteKindToPlace); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); + currentLiveInputPlaceNoteData[column] = newNoteData; + gridPlayheadGhostHoldNotes[column].noteData = newNoteData.clone(); + gridPlayheadGhostHoldNotes[column].noteDirection = newNoteData.getDirection(); + } + else if (removeNoteInstead) + { + trace('Removing existing note at position.'); + performCommand(new RemoveNotesCommand(notesAtPos)); } else { @@ -4572,6 +4798,87 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } } + function updatePlayheadGhostHoldNotes():Void + { + // Update playhead ghost hold notes. + for (index in 0...gridPlayheadGhostHoldNotes.length) + { + var ghostHold = gridPlayheadGhostHoldNotes[index]; + if (ghostHold == null) continue; + + if (ghostHold.noteData == null) + { + ghostHold.visible = false; + ghostHold.setHeightDirectly(0); + playheadDragLengthCurrent[index] = 0; + continue; + }; + + var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; + var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / noteSnapRatio; + var playheadPosStep:Int = Std.int(Math.floor(playheadPosFractionalStep)); + var playheadPosSnappedMs:Float = playheadPosStep * Conductor.instance.stepLengthMs * noteSnapRatio; + + var newNoteLength:Float = playheadPosSnappedMs - ghostHold.noteData.time; + trace('newNoteLength: ${newNoteLength}'); + + if (newNoteLength > 0) + { + ghostHold.noteData.length = newNoteLength; + var targetNoteLengthSteps:Float = ghostHold.noteData.getStepLength(true); + var targetNoteLengthStepsInt:Int = Std.int(Math.floor(targetNoteLengthSteps)); + var targetNoteLengthPixels:Float = targetNoteLengthSteps * GRID_SIZE; + + if (playheadDragLengthCurrent[index] != targetNoteLengthStepsInt) + { + stretchySounds = !stretchySounds; + this.playSound(Paths.sound('chartingSounds/stretch' + (stretchySounds ? '1' : '2') + '_UI')); + playheadDragLengthCurrent[index] = targetNoteLengthStepsInt; + } + ghostHold.visible = true; + trace('newHeight: ${targetNoteLengthPixels}'); + ghostHold.setHeightDirectly(targetNoteLengthPixels, true); + ghostHold.updateHoldNotePosition(renderedHoldNotes); + } + else + { + ghostHold.visible = false; + ghostHold.setHeightDirectly(0); + playheadDragLengthCurrent[index] = 0; + } + } + } + + function finishPlaceNoteAtPlayhead(column:Int):Void + { + if (currentLiveInputPlaceNoteData[column] == null) return; + + var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; + var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / noteSnapRatio; + var playheadPosStep:Int = Std.int(Math.floor(playheadPosFractionalStep)); + var playheadPosSnappedMs:Float = playheadPosStep * Conductor.instance.stepLengthMs * noteSnapRatio; + + var newNoteLength:Float = playheadPosSnappedMs - currentLiveInputPlaceNoteData[column].time; + trace('finishPlace newNoteLength: ${newNoteLength}'); + + if (newNoteLength < Conductor.instance.stepLengthMs) + { + // Don't extend the note if it's too short. + trace('Not extending note.'); + currentLiveInputPlaceNoteData[column] = null; + gridPlayheadGhostHoldNotes[column].noteData = null; + } + else + { + // Extend the note to the playhead position. + trace('Extending note.'); + this.playSound(Paths.sound('chartingSounds/stretchSNAP_UI')); + performCommand(new ExtendNoteLengthCommand(currentLiveInputPlaceNoteData[column], newNoteLength)); + currentLiveInputPlaceNoteData[column] = null; + gridPlayheadGhostHoldNotes[column].noteData = null; + } + } + /** * Handle aligning the health icons next to the grid. */ diff --git a/source/funkin/ui/debug/charting/commands/RemoveEventsCommand.hx b/source/funkin/ui/debug/charting/commands/RemoveEventsCommand.hx index 7e620c210..102257fc8 100644 --- a/source/funkin/ui/debug/charting/commands/RemoveEventsCommand.hx +++ b/source/funkin/ui/debug/charting/commands/RemoveEventsCommand.hx @@ -20,6 +20,8 @@ class RemoveEventsCommand implements ChartEditorCommand public function execute(state:ChartEditorState):Void { + if (events.length == 0) return; + state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events); state.currentEventSelection = []; @@ -34,6 +36,8 @@ class RemoveEventsCommand implements ChartEditorCommand public function undo(state:ChartEditorState):Void { + if (events.length == 0) return; + for (event in events) { state.currentSongChartEventData.push(event); diff --git a/source/funkin/ui/debug/charting/commands/RemoveItemsCommand.hx b/source/funkin/ui/debug/charting/commands/RemoveItemsCommand.hx index 77184209e..376908726 100644 --- a/source/funkin/ui/debug/charting/commands/RemoveItemsCommand.hx +++ b/source/funkin/ui/debug/charting/commands/RemoveItemsCommand.hx @@ -23,6 +23,8 @@ class RemoveItemsCommand implements ChartEditorCommand public function execute(state:ChartEditorState):Void { + if ((notes.length + events.length) == 0) return; + state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes); state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events); @@ -40,6 +42,8 @@ class RemoveItemsCommand implements ChartEditorCommand public function undo(state:ChartEditorState):Void { + if ((notes.length + events.length) == 0) return; + for (note in notes) { state.currentSongChartNoteData.push(note); diff --git a/source/funkin/ui/debug/charting/commands/RemoveNotesCommand.hx b/source/funkin/ui/debug/charting/commands/RemoveNotesCommand.hx index e189be83e..4e7b480dd 100644 --- a/source/funkin/ui/debug/charting/commands/RemoveNotesCommand.hx +++ b/source/funkin/ui/debug/charting/commands/RemoveNotesCommand.hx @@ -20,6 +20,8 @@ class RemoveNotesCommand implements ChartEditorCommand public function execute(state:ChartEditorState):Void { + if (notes.length == 0) return; + state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes); state.currentNoteSelection = []; state.currentEventSelection = []; @@ -35,6 +37,8 @@ class RemoveNotesCommand implements ChartEditorCommand public function undo(state:ChartEditorState):Void { + if (notes.length == 0) return; + for (note in notes) { state.currentSongChartNoteData.push(note); diff --git a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx index e5971db08..193390341 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx @@ -43,11 +43,16 @@ class ChartEditorHoldNoteSprite extends SustainTrail * Set the height directly, to a value in pixels. * @param h The desired height in pixels. */ - public function setHeightDirectly(h:Float, ?lerp:Bool = false) + public function setHeightDirectly(h:Float, lerp:Bool = false) { - if (lerp != null && lerp) sustainLength = FlxMath.lerp(sustainLength, h / (getScrollSpeed() * Constants.PIXELS_PER_MS), 0.25); + if (lerp) + { + sustainLength = FlxMath.lerp(sustainLength, h / (getScrollSpeed() * Constants.PIXELS_PER_MS), 0.25); + } else + { sustainLength = h / (getScrollSpeed() * Constants.PIXELS_PER_MS); + } fullSustainLength = sustainLength; } diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx new file mode 100644 index 000000000..896e2df68 --- /dev/null +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx @@ -0,0 +1,133 @@ +package funkin.ui.debug.charting.handlers; + +/** + * Yes, we're that crazy. Gamepad support for the chart editor. + */ +@:nullSafety +@:access(funkin.ui.debug.charting.ChartEditorState) +class ChartEditorGamepadHandler +{ + public static function handleGamepadControls(chartEditorState:ChartEditorState) + { + if (FlxG.gamepads.firstActive == null) return; + + if (FlxG.gamepads.firstActive.justPressed.A) + { + // trace('Gamepad: A pressed'); + } + if (FlxG.gamepads.firstActive.justPressed.B) + { + // trace('Gamepad: B pressed'); + } + if (FlxG.gamepads.firstActive.justPressed.X) + { + // trace('Gamepad: X pressed'); + } + if (FlxG.gamepads.firstActive.justPressed.Y) + { + // trace('Gamepad: Y pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.LEFT_SHOULDER) + { + // trace('Gamepad: LEFT_SHOULDER pressed'); + } + if (FlxG.gamepads.firstActive.justPressed.RIGHT_SHOULDER) + { + // trace('Gamepad: RIGHT_SHOULDER pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_CLICK) + { + // trace('Gamepad: LEFT_STICK_CLICK pressed'); + } + if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_CLICK) + { + // trace('Gamepad: RIGHT_STICK_CLICK pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.LEFT_TRIGGER) + { + // trace('Gamepad: LEFT_TRIGGER pressed'); + } + if (FlxG.gamepads.firstActive.justPressed.RIGHT_TRIGGER) + { + // trace('Gamepad: RIGHT_TRIGGER pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.START) + { + // trace('Gamepad: START pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.BACK) + { + // trace('Gamepad: BACK pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.GUIDE) + { + // trace('Gamepad: GUIDE pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.DPAD_UP) + { + // trace('Gamepad: DPAD_UP pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.DPAD_DOWN) + { + // trace('Gamepad: DPAD_DOWN pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.DPAD_LEFT) + { + // trace('Gamepad: DPAD_LEFT pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.DPAD_RIGHT) + { + // trace('Gamepad: DPAD_RIGHT pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_DIGITAL_UP) + { + // trace('Gamepad: LEFT_STICK_DIGITAL_UP pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_DIGITAL_DOWN) + { + // trace('Gamepad: LEFT_STICK_DIGITAL_DOWN pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_DIGITAL_LEFT) + { + // trace('Gamepad: LEFT_STICK_DIGITAL_LEFT pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_DIGITAL_RIGHT) + { + // trace('Gamepad: LEFT_STICK_DIGITAL_RIGHT pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_DIGITAL_UP) + { + // trace('Gamepad: RIGHT_STICK_DIGITAL_UP pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_DIGITAL_DOWN) + { + // trace('Gamepad: RIGHT_STICK_DIGITAL_DOWN pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_DIGITAL_LEFT) + { + // trace('Gamepad: RIGHT_STICK_DIGITAL_LEFT pressed'); + } + + if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_DIGITAL_RIGHT) + { + // trace('Gamepad: RIGHT_STICK_DIGITAL_RIGHT pressed'); + } + } +} diff --git a/source/funkin/ui/debug/charting/import.hx b/source/funkin/ui/debug/charting/import.hx index b0569e3bb..2c3d59ef7 100644 --- a/source/funkin/ui/debug/charting/import.hx +++ b/source/funkin/ui/debug/charting/import.hx @@ -5,6 +5,7 @@ package funkin.ui.debug.charting; using funkin.ui.debug.charting.handlers.ChartEditorAudioHandler; using funkin.ui.debug.charting.handlers.ChartEditorContextMenuHandler; using funkin.ui.debug.charting.handlers.ChartEditorDialogHandler; +using funkin.ui.debug.charting.handlers.ChartEditorGamepadHandler; using funkin.ui.debug.charting.handlers.ChartEditorImportExportHandler; using funkin.ui.debug.charting.handlers.ChartEditorNotificationHandler; using funkin.ui.debug.charting.handlers.ChartEditorShortcutHandler; From c6b3499897fd81090f2e7f564dc8d4bf7fe84b0d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 8 Jan 2024 21:26:24 -0500 Subject: [PATCH 02/33] Finished up hold note placement on gamepad, implemented note preview playhead --- source/Main.hx | 2 + .../ui/debug/charting/ChartEditorState.hx | 130 ++++---- .../handlers/ChartEditorGamepadHandler.hx | 278 +++++++++++------- .../ui/haxeui/FlxGamepadActionInputSource.hx | 53 ++++ 4 files changed, 301 insertions(+), 162 deletions(-) create mode 100644 source/funkin/ui/haxeui/FlxGamepadActionInputSource.hx diff --git a/source/Main.hx b/source/Main.hx index 86e520e69..a7482c8d6 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -111,6 +111,8 @@ class Main extends Sprite Toolkit.init(); Toolkit.theme = 'dark'; // don't be cringe Toolkit.autoScale = false; + // Don't focus on UI elements when they first appear. + haxe.ui.focus.FocusManager.instance.autoFocus = false; funkin.input.Cursor.registerHaxeUICursors(); haxe.ui.tooltips.ToolTipManager.defaultDelay = 200; } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f3236578a..1b9176174 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -366,8 +366,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } } - updatePlayheadGhostHoldNotes(); - // Move the rendered notes to the correct position. renderedNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); renderedHoldNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); @@ -375,8 +373,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState renderedSelectionSquares.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); // Offset the selection box start position, if we are dragging. if (selectionBoxStartPos != null) selectionBoxStartPos.y -= diff; - // Update the note preview viewport box. + + // Update the note preview. setNotePreviewViewportBounds(calculateNotePreviewViewportBounds()); + refreshNotePreviewPlayheadPosition(); + // Update the measure tick display. if (measureTicks != null) measureTicks.y = gridTiledSprite?.y ?? 0.0; return this.scrollPositionInPixels; @@ -438,6 +439,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState gridPlayhead.y = this.playheadPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); updatePlayheadGhostHoldNotes(); + refreshNotePreviewPlayheadPosition(); return this.playheadPositionInPixels; } @@ -1842,6 +1844,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var notePreviewViewport:Null = null; + /** + * The thin sprite used for representing the playhead on the note preview. + * We move this up and down to represent the current position. + */ + var notePreviewPlayhead:Null = null; + /** * The rectangular sprite used for rendering the selection box. * Uses a 9-slice to stretch the selection box to the correct size without warping. @@ -2219,18 +2227,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState add(gridGhostHoldNote); gridGhostHoldNote.zIndex = 11; - while (gridPlayheadGhostHoldNotes.length < (STRUMLINE_SIZE * 2)) - { - var ghost = new ChartEditorHoldNoteSprite(this); - ghost.alpha = 0.6; - ghost.noteData = null; - ghost.visible = false; - add(ghost); - ghost.zIndex = 11; - - gridPlayheadGhostHoldNotes.push(ghost); - } - gridGhostEvent = new ChartEditorEventSprite(this); gridGhostEvent.alpha = 0.6; gridGhostEvent.eventData = new SongEventData(-1, '', {}); @@ -2300,6 +2296,15 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState add(notePreviewViewport); notePreviewViewport.zIndex = 30; + notePreviewPlayhead = new FlxSprite().makeGraphic(2, 2, 0xFFFF0000); + notePreviewPlayhead.scrollFactor.set(0, 0); + notePreviewPlayhead.scale.set(notePreview.width / 2, 0.5); // Setting width does nothing. + notePreviewPlayhead.updateHitbox(); + notePreviewPlayhead.x = notePreview.x; + notePreviewPlayhead.y = notePreview.y; + add(notePreviewPlayhead); + notePreviewPlayhead.zIndex = 31; + setNotePreviewViewportBounds(calculateNotePreviewViewportBounds()); } @@ -2399,6 +2404,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } } + function refreshNotePreviewPlayheadPosition():Void + { + if (notePreviewPlayhead == null) return; + + notePreviewPlayhead.y = notePreview.y + (notePreview.height * ((scrollPositionInPixels + playheadPositionInPixels) / songLengthInPixels)); + } + /** * Builds the group that will hold all the notes. */ @@ -4194,7 +4206,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState gridGhostHoldNote.visible = true; gridGhostHoldNote.noteData = gridGhostNote.noteData; gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection(); - gridGhostHoldNote.setHeightDirectly(dragLengthPixels, true); gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes); @@ -4741,27 +4752,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Do nothing. } - // Place notes at the playhead with the gamepad. - if (FlxG.gamepads.firstActive != null) - { - if (FlxG.gamepads.firstActive.justPressed.DPAD_LEFT) placeNoteAtPlayhead(4); - if (FlxG.gamepads.firstActive.justReleased.DPAD_LEFT) finishPlaceNoteAtPlayhead(4); - if (FlxG.gamepads.firstActive.justPressed.DPAD_DOWN) placeNoteAtPlayhead(5); - if (FlxG.gamepads.firstActive.justReleased.DPAD_DOWN) finishPlaceNoteAtPlayhead(5); - if (FlxG.gamepads.firstActive.justPressed.DPAD_UP) placeNoteAtPlayhead(6); - if (FlxG.gamepads.firstActive.justReleased.DPAD_UP) finishPlaceNoteAtPlayhead(6); - if (FlxG.gamepads.firstActive.justPressed.DPAD_RIGHT) placeNoteAtPlayhead(7); - if (FlxG.gamepads.firstActive.justReleased.DPAD_RIGHT) finishPlaceNoteAtPlayhead(7); - - if (FlxG.gamepads.firstActive.justPressed.X) placeNoteAtPlayhead(0); - if (FlxG.gamepads.firstActive.justReleased.X) finishPlaceNoteAtPlayhead(0); - if (FlxG.gamepads.firstActive.justPressed.A) placeNoteAtPlayhead(1); - if (FlxG.gamepads.firstActive.justReleased.A) finishPlaceNoteAtPlayhead(1); - if (FlxG.gamepads.firstActive.justPressed.Y) placeNoteAtPlayhead(2); - if (FlxG.gamepads.firstActive.justReleased.Y) finishPlaceNoteAtPlayhead(2); - if (FlxG.gamepads.firstActive.justPressed.B) placeNoteAtPlayhead(3); - if (FlxG.gamepads.firstActive.justReleased.B) finishPlaceNoteAtPlayhead(3); - } + updatePlayheadGhostHoldNotes(); } function placeNoteAtPlayhead(column:Int):Void @@ -4781,38 +4772,68 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState if (notesAtPos.length == 0 && !removeNoteInstead) { + trace('Placing note. ${column}'); var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, noteKindToPlace); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); currentLiveInputPlaceNoteData[column] = newNoteData; - gridPlayheadGhostHoldNotes[column].noteData = newNoteData.clone(); - gridPlayheadGhostHoldNotes[column].noteDirection = newNoteData.getDirection(); } else if (removeNoteInstead) { - trace('Removing existing note at position.'); + trace('Removing existing note at position. ${column}'); performCommand(new RemoveNotesCommand(notesAtPos)); } else { - trace('Already a note there.'); + trace('Already a note there. ${column}'); } } function updatePlayheadGhostHoldNotes():Void { - // Update playhead ghost hold notes. - for (index in 0...gridPlayheadGhostHoldNotes.length) + // Ensure all the ghost hold notes exist. + while (gridPlayheadGhostHoldNotes.length < (STRUMLINE_SIZE * 2)) { - var ghostHold = gridPlayheadGhostHoldNotes[index]; - if (ghostHold == null) continue; + var ghost = new ChartEditorHoldNoteSprite(this); + ghost.alpha = 0.6; + ghost.noteData = null; + ghost.visible = false; + ghost.zIndex = 11; + add(ghost); // Don't add to `renderedHoldNotes` because then it will get killed every frame. + + gridPlayheadGhostHoldNotes.push(ghost); + refresh(); + } + + // Update playhead ghost hold notes. + for (column in 0...gridPlayheadGhostHoldNotes.length) + { + var targetNoteData = currentLiveInputPlaceNoteData[column]; + var ghostHold = gridPlayheadGhostHoldNotes[column]; + + if (targetNoteData == null && ghostHold.noteData != null) + { + // Remove the ghost hold note. + ghostHold.noteData = null; + } + + if (targetNoteData != null && ghostHold.noteData == null) + { + // Readd the new ghost hold note. + ghostHold.noteData = targetNoteData.clone(); + ghostHold.noteDirection = ghostHold.noteData.getDirection(); + ghostHold.visible = true; + ghostHold.alpha = 0.6; + ghostHold.setHeightDirectly(0); + ghostHold.updateHoldNotePosition(renderedHoldNotes); + } if (ghostHold.noteData == null) { ghostHold.visible = false; ghostHold.setHeightDirectly(0); - playheadDragLengthCurrent[index] = 0; + playheadDragLengthCurrent[column] = 0; continue; - }; + } var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / noteSnapRatio; @@ -4829,22 +4850,25 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState var targetNoteLengthStepsInt:Int = Std.int(Math.floor(targetNoteLengthSteps)); var targetNoteLengthPixels:Float = targetNoteLengthSteps * GRID_SIZE; - if (playheadDragLengthCurrent[index] != targetNoteLengthStepsInt) + if (playheadDragLengthCurrent[column] != targetNoteLengthStepsInt) { stretchySounds = !stretchySounds; this.playSound(Paths.sound('chartingSounds/stretch' + (stretchySounds ? '1' : '2') + '_UI')); - playheadDragLengthCurrent[index] = targetNoteLengthStepsInt; + playheadDragLengthCurrent[column] = targetNoteLengthStepsInt; } ghostHold.visible = true; - trace('newHeight: ${targetNoteLengthPixels}'); + ghostHold.alpha = 0.6; ghostHold.setHeightDirectly(targetNoteLengthPixels, true); ghostHold.updateHoldNotePosition(renderedHoldNotes); + trace('lerpLength: ${ghostHold.fullSustainLength}'); + trace('position: ${ghostHold.x}, ${ghostHold.y}'); } else { ghostHold.visible = false; ghostHold.setHeightDirectly(0); - playheadDragLengthCurrent[index] = 0; + playheadDragLengthCurrent[column] = 0; + continue; } } } @@ -4864,14 +4888,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState if (newNoteLength < Conductor.instance.stepLengthMs) { // Don't extend the note if it's too short. - trace('Not extending note.'); + trace('Not extending note. ${column}'); currentLiveInputPlaceNoteData[column] = null; gridPlayheadGhostHoldNotes[column].noteData = null; } else { // Extend the note to the playhead position. - trace('Extending note.'); + trace('Extending note. ${column}'); this.playSound(Paths.sound('chartingSounds/stretchSNAP_UI')); performCommand(new ExtendNoteLengthCommand(currentLiveInputPlaceNoteData[column], newNoteLength)); currentLiveInputPlaceNoteData[column] = null; diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx index 896e2df68..70383d3fd 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx @@ -1,133 +1,193 @@ package funkin.ui.debug.charting.handlers; +import haxe.ui.focus.FocusManager; +import flixel.input.gamepad.FlxGamepad; +import haxe.ui.actions.ActionManager; +import haxe.ui.actions.IActionInputSource; +import haxe.ui.actions.ActionType; + /** * Yes, we're that crazy. Gamepad support for the chart editor. */ -@:nullSafety +// @:nullSafety + @:access(funkin.ui.debug.charting.ChartEditorState) class ChartEditorGamepadHandler { public static function handleGamepadControls(chartEditorState:ChartEditorState) { - if (FlxG.gamepads.firstActive == null) return; + if (FlxG.gamepads.firstActive != null) handleGamepad(chartEditorState, FlxG.gamepads.firstActive); + } - if (FlxG.gamepads.firstActive.justPressed.A) + /** + * Handle context-generic binds for the gamepad. + * @param chartEditorState The chart editor state. + * @param gamepad The gamepad to handle. + */ + static function handleGamepad(chartEditorState:ChartEditorState, gamepad:FlxGamepad):Void + { + if (chartEditorState.isHaxeUIFocused) { - // trace('Gamepad: A pressed'); + ChartEditorGamepadActionInputSource.instance.handleGamepad(gamepad); } - if (FlxG.gamepads.firstActive.justPressed.B) + else { - // trace('Gamepad: B pressed'); - } - if (FlxG.gamepads.firstActive.justPressed.X) - { - // trace('Gamepad: X pressed'); - } - if (FlxG.gamepads.firstActive.justPressed.Y) - { - // trace('Gamepad: Y pressed'); + handleGamepadLiveInputs(chartEditorState, gamepad); + + if (gamepad.justPressed.RIGHT_SHOULDER) + { + trace('Gamepad: Right shoulder pressed, toggling audio playback.'); + chartEditorState.toggleAudioPlayback(); + } + + if (gamepad.justPressed.START) + { + var minimal = gamepad.pressed.LEFT_SHOULDER; + chartEditorState.hideAllToolboxes(); + trace('Gamepad: Start pressed, opening playtest (minimal: ${minimal})'); + chartEditorState.testSongInPlayState(minimal); + } + + if (gamepad.justPressed.BACK && !gamepad.pressed.LEFT_SHOULDER) + { + trace('Gamepad: Back pressed, focusing on HaxeUI menu.'); + // FocusManager.instance.focus = chartEditorState.menubarMenuFile; + } + else if (gamepad.justPressed.BACK && gamepad.pressed.LEFT_SHOULDER) + { + trace('Gamepad: Back pressed, unfocusing on HaxeUI menu.'); + FocusManager.instance.focus = null; + } } - if (FlxG.gamepads.firstActive.justPressed.LEFT_SHOULDER) + if (gamepad.justPressed.GUIDE) { - // trace('Gamepad: LEFT_SHOULDER pressed'); - } - if (FlxG.gamepads.firstActive.justPressed.RIGHT_SHOULDER) - { - // trace('Gamepad: RIGHT_SHOULDER pressed'); + trace('Gamepad: Guide pressed, quitting chart editor.'); + chartEditorState.quitChartEditor(); } + } - if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_CLICK) + static function handleGamepadLiveInputs(chartEditorState:ChartEditorState, gamepad:FlxGamepad):Void + { + // Place notes at the playhead with the gamepad. + // Disable when we are interacting with HaxeUI. + if (!(chartEditorState.isHaxeUIFocused || chartEditorState.isHaxeUIDialogOpen)) { - // trace('Gamepad: LEFT_STICK_CLICK pressed'); - } - if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_CLICK) - { - // trace('Gamepad: RIGHT_STICK_CLICK pressed'); - } + if (gamepad.justPressed.DPAD_LEFT) chartEditorState.placeNoteAtPlayhead(4); + if (gamepad.justReleased.DPAD_LEFT) chartEditorState.finishPlaceNoteAtPlayhead(4); + if (gamepad.justPressed.DPAD_DOWN) chartEditorState.placeNoteAtPlayhead(5); + if (gamepad.justReleased.DPAD_DOWN) chartEditorState.finishPlaceNoteAtPlayhead(5); + if (gamepad.justPressed.DPAD_UP) chartEditorState.placeNoteAtPlayhead(6); + if (gamepad.justReleased.DPAD_UP) chartEditorState.finishPlaceNoteAtPlayhead(6); + if (gamepad.justPressed.DPAD_RIGHT) chartEditorState.placeNoteAtPlayhead(7); + if (gamepad.justReleased.DPAD_RIGHT) chartEditorState.finishPlaceNoteAtPlayhead(7); - if (FlxG.gamepads.firstActive.justPressed.LEFT_TRIGGER) - { - // trace('Gamepad: LEFT_TRIGGER pressed'); - } - if (FlxG.gamepads.firstActive.justPressed.RIGHT_TRIGGER) - { - // trace('Gamepad: RIGHT_TRIGGER pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.START) - { - // trace('Gamepad: START pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.BACK) - { - // trace('Gamepad: BACK pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.GUIDE) - { - // trace('Gamepad: GUIDE pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.DPAD_UP) - { - // trace('Gamepad: DPAD_UP pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.DPAD_DOWN) - { - // trace('Gamepad: DPAD_DOWN pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.DPAD_LEFT) - { - // trace('Gamepad: DPAD_LEFT pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.DPAD_RIGHT) - { - // trace('Gamepad: DPAD_RIGHT pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_DIGITAL_UP) - { - // trace('Gamepad: LEFT_STICK_DIGITAL_UP pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_DIGITAL_DOWN) - { - // trace('Gamepad: LEFT_STICK_DIGITAL_DOWN pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_DIGITAL_LEFT) - { - // trace('Gamepad: LEFT_STICK_DIGITAL_LEFT pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.LEFT_STICK_DIGITAL_RIGHT) - { - // trace('Gamepad: LEFT_STICK_DIGITAL_RIGHT pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_DIGITAL_UP) - { - // trace('Gamepad: RIGHT_STICK_DIGITAL_UP pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_DIGITAL_DOWN) - { - // trace('Gamepad: RIGHT_STICK_DIGITAL_DOWN pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_DIGITAL_LEFT) - { - // trace('Gamepad: RIGHT_STICK_DIGITAL_LEFT pressed'); - } - - if (FlxG.gamepads.firstActive.justPressed.RIGHT_STICK_DIGITAL_RIGHT) - { - // trace('Gamepad: RIGHT_STICK_DIGITAL_RIGHT pressed'); + if (gamepad.justPressed.X) chartEditorState.placeNoteAtPlayhead(0); + if (gamepad.justReleased.X) chartEditorState.finishPlaceNoteAtPlayhead(0); + if (gamepad.justPressed.A) chartEditorState.placeNoteAtPlayhead(1); + if (gamepad.justReleased.A) chartEditorState.finishPlaceNoteAtPlayhead(1); + if (gamepad.justPressed.Y) chartEditorState.placeNoteAtPlayhead(2); + if (gamepad.justReleased.Y) chartEditorState.finishPlaceNoteAtPlayhead(2); + if (gamepad.justPressed.B) chartEditorState.placeNoteAtPlayhead(3); + if (gamepad.justReleased.B) chartEditorState.finishPlaceNoteAtPlayhead(3); + } + } +} + +class ChartEditorGamepadActionInputSource implements IActionInputSource +{ + public static var instance:ChartEditorGamepadActionInputSource = new ChartEditorGamepadActionInputSource(); + + public function new() {} + + public function start():Void {} + + /** + * Handle HaxeUI-specific binds for the gamepad. + * Only called when the HaxeUI menu is focused. + * @param chartEditorState The chart editor state. + * @param gamepad The gamepad to handle. + */ + public function handleGamepad(gamepad:FlxGamepad):Void + { + if (gamepad.justPressed.DPAD_LEFT) + { + trace('Gamepad: DPAD_LEFT pressed, moving left.'); + ActionManager.instance.actionStart(ActionType.LEFT, this); + } + else if (gamepad.justReleased.DPAD_LEFT) + { + ActionManager.instance.actionEnd(ActionType.LEFT, this); + } + + if (gamepad.justPressed.DPAD_RIGHT) + { + trace('Gamepad: DPAD_RIGHT pressed, moving right.'); + ActionManager.instance.actionStart(ActionType.RIGHT, this); + } + else if (gamepad.justReleased.DPAD_RIGHT) + { + ActionManager.instance.actionEnd(ActionType.RIGHT, this); + } + + if (gamepad.justPressed.DPAD_UP) + { + trace('Gamepad: DPAD_UP pressed, moving up.'); + ActionManager.instance.actionStart(ActionType.UP, this); + } + else if (gamepad.justReleased.DPAD_UP) + { + ActionManager.instance.actionEnd(ActionType.UP, this); + } + + if (gamepad.justPressed.DPAD_DOWN) + { + trace('Gamepad: DPAD_DOWN pressed, moving down.'); + ActionManager.instance.actionStart(ActionType.DOWN, this); + } + else if (gamepad.justReleased.DPAD_DOWN) + { + ActionManager.instance.actionEnd(ActionType.DOWN, this); + } + + if (gamepad.justPressed.A) + { + trace('Gamepad: A pressed, confirmingg.'); + ActionManager.instance.actionStart(ActionType.CONFIRM, this); + } + else if (gamepad.justReleased.A) + { + ActionManager.instance.actionEnd(ActionType.CONFIRM, this); + } + + if (gamepad.justPressed.B) + { + trace('Gamepad: B pressed, cancelling.'); + ActionManager.instance.actionStart(ActionType.CANCEL, this); + } + else if (gamepad.justReleased.B) + { + ActionManager.instance.actionEnd(ActionType.CANCEL, this); + } + + if (gamepad.justPressed.LEFT_TRIGGER) + { + trace('Gamepad: LEFT_TRIGGER pressed, moving to previous item.'); + ActionManager.instance.actionStart(ActionType.PREVIOUS, this); + } + else if (gamepad.justReleased.LEFT_TRIGGER) + { + ActionManager.instance.actionEnd(ActionType.PREVIOUS, this); + } + + if (gamepad.justPressed.RIGHT_TRIGGER) + { + trace('Gamepad: RIGHT_TRIGGER pressed, moving to next item.'); + ActionManager.instance.actionStart(ActionType.NEXT, this); + } + else if (gamepad.justReleased.RIGHT_TRIGGER) + { + ActionManager.instance.actionEnd(ActionType.NEXT, this); } } } diff --git a/source/funkin/ui/haxeui/FlxGamepadActionInputSource.hx b/source/funkin/ui/haxeui/FlxGamepadActionInputSource.hx new file mode 100644 index 000000000..9c2901d16 --- /dev/null +++ b/source/funkin/ui/haxeui/FlxGamepadActionInputSource.hx @@ -0,0 +1,53 @@ +package funkin.ui.haxeui; + +import flixel.FlxBasic; +import flixel.input.gamepad.FlxGamepad; +import haxe.ui.actions.IActionInputSource; + +/** + * Receives button presses from the Flixel gamepad and emits HaxeUI events. + */ +class FlxGamepadActionInputSource extends FlxBasic +{ + public static var instance(get, null):FlxGamepadActionInputSource; + + static function get_instance():FlxGamepadActionInputSource + { + if (instance == null) instance = new FlxGamepadActionInputSource(); + return instance; + } + + public function new() + { + super(); + } + + public function start():Void + { + FlxG.plugins.addPlugin(this); + } + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + if (FlxG.gamepads.firstActive != null) + { + updateGamepad(elapsed, FlxG.gamepads.firstActive); + } + } + + function updateGamepad(elapsed:Float, gamepad:FlxGamepad):Void + { + if (gamepad.justPressed.BACK) + { + // + } + } + + public override function destroy():Void + { + super.destroy(); + FlxG.plugins.remove(this); + } +} From 2dbc0cda6e63a56eccb81f8ad8eb63407dfbc3d7 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 13 Feb 2024 03:18:51 -0500 Subject: [PATCH 03/33] Fix a missing import. --- source/funkin/ui/debug/charting/ChartEditorState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 6d2b47ef4..d0ed095cc 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -45,6 +45,7 @@ import funkin.graphics.FunkinCamera; import funkin.graphics.FunkinSprite; import funkin.input.Cursor; import funkin.input.TurboActionHandler; +import funkin.input.TurboButtonHandler; import funkin.input.TurboKeyHandler; import funkin.modding.events.ScriptEvent; import funkin.play.character.BaseCharacter.CharacterType; From a764112bd40b9458f4925f5b6a4998f760a9b3e1 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 5 Apr 2024 01:24:03 -0400 Subject: [PATCH 04/33] Removed many unused assets to optimize bandwidth usage. --- Project.xml | 4 + assets | 2 +- source/funkin/Paths.hx | 2 +- source/funkin/modding/PolymodHandler.hx | 4 +- source/funkin/play/cutscene/VideoCutscene.hx | 9 +- .../funkin/ui/credits/CreditsDataHandler.hx | 7 ++ .../components/ChartEditorNoteSprite.hx | 6 - source/funkin/ui/freeplay/AlbumRoll.hx | 35 ------ source/funkin/ui/freeplay/DifficultyStars.hx | 106 ------------------ source/funkin/ui/freeplay/FreeplayState.hx | 11 -- source/funkin/ui/mainmenu/MainMenuState.hx | 4 +- 11 files changed, 25 insertions(+), 165 deletions(-) delete mode 100644 source/funkin/ui/freeplay/DifficultyStars.hx diff --git a/Project.xml b/Project.xml index 8ba14e7dc..db338d32a 100644 --- a/Project.xml +++ b/Project.xml @@ -45,6 +45,7 @@ +
@@ -58,10 +59,13 @@ +
+ + diff --git a/assets b/assets index 5027bc656..a11c558e4 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5027bc656c9df5ec208ab256f9494bd7da425111 +Subproject commit a11c558e4c0ed796c34246b43abc9c2d024d0a42 diff --git a/source/funkin/Paths.hx b/source/funkin/Paths.hx index fd4ef76fa..54a4b7acf 100644 --- a/source/funkin/Paths.hx +++ b/source/funkin/Paths.hx @@ -113,7 +113,7 @@ class Paths public static function videos(key:String, ?library:String):String { - return getPath('videos/$key.${Constants.EXT_VIDEO}', BINARY, library); + return getPath('videos/$key.${Constants.EXT_VIDEO}', BINARY, library ?? 'videos'); } public static function voices(song:String, ?suffix:String = ''):String diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 78f660d3f..62860ee0f 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -240,8 +240,8 @@ class PolymodHandler { return { assetLibraryPaths: [ - 'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'tutorial' => 'tutorial', 'week1' => 'week1', 'week2' => 'week2', - 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1', + 'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'videos' => 'videos', 'tutorial' => 'tutorial', 'week1' => 'week1', + 'week2' => 'week2', 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1', ], coreAssetRedirect: CORE_FOLDER, } diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 3da51185f..6983fbcad 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -67,8 +67,13 @@ class VideoCutscene if (!openfl.Assets.exists(filePath)) { // Display a popup. - lime.app.Application.current.window.alert('Video file does not exist: ${filePath}', 'Error playing video'); - return; + // lime.app.Application.current.window.alert('Video file does not exist: ${filePath}', 'Error playing video'); + // return; + + // TODO: After moving videos to their own library, + // this function ALWAYS FAILS on web, but the video still plays. + // I think that's due to a weird quirk with how OpenFL libraries work. + trace('Video file does not exist: ${filePath}'); } var rawFilePath = Paths.stripLibrary(filePath); diff --git a/source/funkin/ui/credits/CreditsDataHandler.hx b/source/funkin/ui/credits/CreditsDataHandler.hx index 86afdafd1..628a9f893 100644 --- a/source/funkin/ui/credits/CreditsDataHandler.hx +++ b/source/funkin/ui/credits/CreditsDataHandler.hx @@ -99,12 +99,19 @@ class CreditsDataHandler static function fetchCreditsData():funkin.data.JsonFile { + #if !macro var rawJson:String = openfl.Assets.getText(CREDITS_DATA_PATH).trim(); return { fileName: CREDITS_DATA_PATH, contents: rawJson }; + #else + return { + fileName: CREDITS_DATA_PATH, + contents: null + }; + #end } static function parseCreditsData(file:JsonFile):Null diff --git a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx index cd403c6f8..98f5a47aa 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx @@ -117,12 +117,6 @@ class ChartEditorNoteSprite extends FlxSprite { noteFrameCollection.pushFrame(frame); } - var frameCollectionNormal2:FlxAtlasFrames = Paths.getSparrowAtlas('NoteHoldNormal'); - - for (frame in frameCollectionNormal2.frames) - { - noteFrameCollection.pushFrame(frame); - } // Pixel notes var graphicPixel = FlxG.bitmap.add(Paths.image('weeb/pixelUI/arrows-pixels', 'week6'), false, null); diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index bde946e79..c1263fed6 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -37,7 +37,6 @@ class AlbumRoll extends FlxSpriteGroup var albumArt:FunkinSprite; var albumTitle:FunkinSprite; - var difficultyStars:DifficultyStars; var _exitMovers:Null; @@ -52,9 +51,6 @@ class AlbumRoll extends FlxSpriteGroup albumTitle.zIndex = 200; add(albumTitle); - difficultyStars = new DifficultyStars(140, 39); - - difficultyStars.stars.visible = true; albumTitle.visible = false; // albumArtist.visible = false; @@ -158,21 +154,6 @@ class AlbumRoll extends FlxSpriteGroup speed: 0.2, wait: 0.1 }); - - /* - exitMovers.set([albumArtist], - { - x: FlxG.width * 1.1, - speed: 0.2, - wait: 0.2 - }); - */ - exitMovers.set([difficultyStars], - { - x: FlxG.width * 1.2, - speed: 0.2, - wait: 0.3 - }); } var titleTimer:Null = null; @@ -198,26 +179,10 @@ class AlbumRoll extends FlxSpriteGroup }); } - public function setDifficultyStars(?difficulty:Int):Void - { - if (difficulty == null) return; - - difficultyStars.difficulty = difficulty; - } - public function showTitle():Void { albumTitle.visible = true; albumTitle.animation.play('active'); albumTitle.animation.finishCallback = (_) -> albumTitle.animation.play('idle'); } - - /** - * Make the album stars visible. - */ - public function showStars():Void - { - // albumArtist.visible = false; - difficultyStars.stars.visible = false; - } } diff --git a/source/funkin/ui/freeplay/DifficultyStars.hx b/source/funkin/ui/freeplay/DifficultyStars.hx deleted file mode 100644 index 51526bcbe..000000000 --- a/source/funkin/ui/freeplay/DifficultyStars.hx +++ /dev/null @@ -1,106 +0,0 @@ -package funkin.ui.freeplay; - -import flixel.group.FlxSpriteGroup; -import funkin.graphics.adobeanimate.FlxAtlasSprite; -import funkin.graphics.shaders.HSVShader; - -class DifficultyStars extends FlxSpriteGroup -{ - /** - * Internal handler var for difficulty... ranges from 0... to 15 - * 0 is 1 star... 15 is 0 stars! - */ - var curDifficulty(default, set):Int = 0; - - /** - * Range between 0 and 15 - */ - public var difficulty(default, set):Int = 1; - - public var stars:FlxAtlasSprite; - - var flames:FreeplayFlames; - - var hsvShader:HSVShader; - - public function new(x:Float, y:Float) - { - super(x, y); - - hsvShader = new HSVShader(); - - flames = new FreeplayFlames(0, 0); - add(flames); - - stars = new FlxAtlasSprite(0, 0, Paths.animateAtlas("freeplay/freeplayStars")); - stars.anim.play("diff stars"); - add(stars); - - stars.shader = hsvShader; - - for (memb in flames.members) - memb.shader = hsvShader; - } - - override function update(elapsed:Float):Void - { - super.update(elapsed); - - // "loops" the current animation - // for clarity, the animation file looks like - // frame : stars - // 0-99: 1 star - // 100-199: 2 stars - // ...... - // 1300-1499: 15 stars - // 1500 : 0 stars - if (curDifficulty < 15 && stars.anim.curFrame >= (curDifficulty + 1) * 100) - { - stars.anim.play("diff stars", true, false, curDifficulty * 100); - } - } - - function set_difficulty(value:Int):Int - { - difficulty = value; - - if (difficulty <= 0) - { - difficulty = 0; - curDifficulty = 15; - } - else if (difficulty <= 15) - { - difficulty = value; - curDifficulty = difficulty - 1; - } - else - { - difficulty = 15; - curDifficulty = difficulty - 1; - } - - if (difficulty > 10) flames.flameCount = difficulty - 10; - else - flames.flameCount = 0; - - return difficulty; - } - - function set_curDifficulty(value:Int):Int - { - curDifficulty = value; - if (curDifficulty == 15) - { - stars.anim.play("diff stars", true, false, 1500); - stars.anim.pause(); - } - else - { - stars.anim.curFrame = Std.int(curDifficulty * 100); - stars.anim.play("diff stars", true, false, curDifficulty * 100); - } - - return curDifficulty; - } -} diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 0724ad022..af76a42ce 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -473,10 +473,6 @@ class FreeplayState extends MusicBeatSubState albumRoll.showTitle(); }); - new FlxTimer().start(35 / 24, function(_) { - albumRoll.showStars(); - }); - FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut}); var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls); @@ -1047,9 +1043,6 @@ class FreeplayState extends MusicBeatSubState } } - // Set the difficulty star count on the right. - albumRoll.setDifficultyStars(daSong?.songRating); - // Set the album graphic and play the animation if relevant. var newAlbumId:String = daSong?.albumId; if (albumRoll.albumId != newAlbumId) @@ -1169,10 +1162,6 @@ class FreeplayState extends MusicBeatSubState { currentDifficulty = rememberedDifficulty; } - - // Set the difficulty star count on the right. - var daSong:Null = grpCapsules.members[curSelected]?.songData; - albumRoll.setDifficultyStars(daSong?.songRating ?? 0); } function changeSelection(change:Int = 0):Void diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index f38db1ccd..c1dc54ffe 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -1,5 +1,6 @@ package funkin.ui.mainmenu; +import funkin.graphics.FunkinSprite; import flixel.addons.transition.FlxTransitionableState; import funkin.ui.debug.DebugMenuSubState; import flixel.FlxObject; @@ -56,7 +57,8 @@ class MainMenuState extends MusicBeatState persistentUpdate = false; persistentDraw = true; - var bg:FlxSprite = new FlxSprite(Paths.image('menuBG')); + var bg = FunkinSprite.create('menuDesat'); + bg.color = 0xFFFDE871; bg.scrollFactor.x = 0; bg.scrollFactor.y = 0.17; bg.setGraphicSize(Std.int(bg.width * 1.2)); From af8a37d299aa6ea015dd7476ec4dfa8fbf4c9264 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 5 Apr 2024 19:33:24 -0400 Subject: [PATCH 05/33] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index a11c558e4..2eb07acf1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a11c558e4c0ed796c34246b43abc9c2d024d0a42 +Subproject commit 2eb07acf1fbee70229b913f87cfc1bfddf22ab88 From a6d44b1f1552e747f45028659bf5887fc7da9f08 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 5 Apr 2024 01:24:03 -0400 Subject: [PATCH 06/33] Removed many unused assets to optimize bandwidth usage. --- Project.xml | 4 + assets | 2 +- source/funkin/Paths.hx | 2 +- source/funkin/modding/PolymodHandler.hx | 4 +- source/funkin/play/cutscene/VideoCutscene.hx | 9 +- .../funkin/ui/credits/CreditsDataHandler.hx | 7 ++ .../components/ChartEditorNoteSprite.hx | 6 - source/funkin/ui/freeplay/DifficultyStars.hx | 106 ------------------ source/funkin/ui/freeplay/FreeplayState.hx | 11 +- source/funkin/ui/mainmenu/MainMenuState.hx | 4 +- 10 files changed, 29 insertions(+), 126 deletions(-) delete mode 100644 source/funkin/ui/freeplay/DifficultyStars.hx diff --git a/Project.xml b/Project.xml index 8ba14e7dc..db338d32a 100644 --- a/Project.xml +++ b/Project.xml @@ -45,6 +45,7 @@ +
@@ -58,10 +59,13 @@ +
+ + diff --git a/assets b/assets index e8f4d2d91..2eb07acf1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e8f4d2d91c7bd5a922465e7d67a0efb7d7574bd6 +Subproject commit 2eb07acf1fbee70229b913f87cfc1bfddf22ab88 diff --git a/source/funkin/Paths.hx b/source/funkin/Paths.hx index fd4ef76fa..54a4b7acf 100644 --- a/source/funkin/Paths.hx +++ b/source/funkin/Paths.hx @@ -113,7 +113,7 @@ class Paths public static function videos(key:String, ?library:String):String { - return getPath('videos/$key.${Constants.EXT_VIDEO}', BINARY, library); + return getPath('videos/$key.${Constants.EXT_VIDEO}', BINARY, library ?? 'videos'); } public static function voices(song:String, ?suffix:String = ''):String diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 78f660d3f..62860ee0f 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -240,8 +240,8 @@ class PolymodHandler { return { assetLibraryPaths: [ - 'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'tutorial' => 'tutorial', 'week1' => 'week1', 'week2' => 'week2', - 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1', + 'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'videos' => 'videos', 'tutorial' => 'tutorial', 'week1' => 'week1', + 'week2' => 'week2', 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1', ], coreAssetRedirect: CORE_FOLDER, } diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 0c05bc876..0939dae38 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -67,8 +67,13 @@ class VideoCutscene if (!openfl.Assets.exists(filePath)) { // Display a popup. - lime.app.Application.current.window.alert('Video file does not exist: ${filePath}', 'Error playing video'); - return; + // lime.app.Application.current.window.alert('Video file does not exist: ${filePath}', 'Error playing video'); + // return; + + // TODO: After moving videos to their own library, + // this function ALWAYS FAILS on web, but the video still plays. + // I think that's due to a weird quirk with how OpenFL libraries work. + trace('Video file does not exist: ${filePath}'); } var rawFilePath = Paths.stripLibrary(filePath); diff --git a/source/funkin/ui/credits/CreditsDataHandler.hx b/source/funkin/ui/credits/CreditsDataHandler.hx index 86afdafd1..628a9f893 100644 --- a/source/funkin/ui/credits/CreditsDataHandler.hx +++ b/source/funkin/ui/credits/CreditsDataHandler.hx @@ -99,12 +99,19 @@ class CreditsDataHandler static function fetchCreditsData():funkin.data.JsonFile { + #if !macro var rawJson:String = openfl.Assets.getText(CREDITS_DATA_PATH).trim(); return { fileName: CREDITS_DATA_PATH, contents: rawJson }; + #else + return { + fileName: CREDITS_DATA_PATH, + contents: null + }; + #end } static function parseCreditsData(file:JsonFile):Null diff --git a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx index cd403c6f8..98f5a47aa 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx @@ -117,12 +117,6 @@ class ChartEditorNoteSprite extends FlxSprite { noteFrameCollection.pushFrame(frame); } - var frameCollectionNormal2:FlxAtlasFrames = Paths.getSparrowAtlas('NoteHoldNormal'); - - for (frame in frameCollectionNormal2.frames) - { - noteFrameCollection.pushFrame(frame); - } // Pixel notes var graphicPixel = FlxG.bitmap.add(Paths.image('weeb/pixelUI/arrows-pixels', 'week6'), false, null); diff --git a/source/funkin/ui/freeplay/DifficultyStars.hx b/source/funkin/ui/freeplay/DifficultyStars.hx deleted file mode 100644 index 51526bcbe..000000000 --- a/source/funkin/ui/freeplay/DifficultyStars.hx +++ /dev/null @@ -1,106 +0,0 @@ -package funkin.ui.freeplay; - -import flixel.group.FlxSpriteGroup; -import funkin.graphics.adobeanimate.FlxAtlasSprite; -import funkin.graphics.shaders.HSVShader; - -class DifficultyStars extends FlxSpriteGroup -{ - /** - * Internal handler var for difficulty... ranges from 0... to 15 - * 0 is 1 star... 15 is 0 stars! - */ - var curDifficulty(default, set):Int = 0; - - /** - * Range between 0 and 15 - */ - public var difficulty(default, set):Int = 1; - - public var stars:FlxAtlasSprite; - - var flames:FreeplayFlames; - - var hsvShader:HSVShader; - - public function new(x:Float, y:Float) - { - super(x, y); - - hsvShader = new HSVShader(); - - flames = new FreeplayFlames(0, 0); - add(flames); - - stars = new FlxAtlasSprite(0, 0, Paths.animateAtlas("freeplay/freeplayStars")); - stars.anim.play("diff stars"); - add(stars); - - stars.shader = hsvShader; - - for (memb in flames.members) - memb.shader = hsvShader; - } - - override function update(elapsed:Float):Void - { - super.update(elapsed); - - // "loops" the current animation - // for clarity, the animation file looks like - // frame : stars - // 0-99: 1 star - // 100-199: 2 stars - // ...... - // 1300-1499: 15 stars - // 1500 : 0 stars - if (curDifficulty < 15 && stars.anim.curFrame >= (curDifficulty + 1) * 100) - { - stars.anim.play("diff stars", true, false, curDifficulty * 100); - } - } - - function set_difficulty(value:Int):Int - { - difficulty = value; - - if (difficulty <= 0) - { - difficulty = 0; - curDifficulty = 15; - } - else if (difficulty <= 15) - { - difficulty = value; - curDifficulty = difficulty - 1; - } - else - { - difficulty = 15; - curDifficulty = difficulty - 1; - } - - if (difficulty > 10) flames.flameCount = difficulty - 10; - else - flames.flameCount = 0; - - return difficulty; - } - - function set_curDifficulty(value:Int):Int - { - curDifficulty = value; - if (curDifficulty == 15) - { - stars.anim.play("diff stars", true, false, 1500); - stars.anim.pause(); - } - else - { - stars.anim.curFrame = Std.int(curDifficulty * 100); - stars.anim.play("diff stars", true, false, curDifficulty * 100); - } - - return curDifficulty; - } -} diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index dc1f164ea..150f5a0b1 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -469,6 +469,10 @@ class FreeplayState extends MusicBeatSubState albumRoll.playIntro(); + new FlxTimer().start(0.75, function(_) { + albumRoll.showTitle(); + }); + FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut}); var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls); @@ -1039,9 +1043,6 @@ class FreeplayState extends MusicBeatSubState } } - // Set the difficulty star count on the right. - albumRoll.setDifficultyStars(daSong?.songRating); - // Set the album graphic and play the animation if relevant. var newAlbumId:String = daSong?.albumId; if (albumRoll.albumId != newAlbumId) @@ -1161,10 +1162,6 @@ class FreeplayState extends MusicBeatSubState { currentDifficulty = rememberedDifficulty; } - - // Set the difficulty star count on the right. - var daSong:Null = grpCapsules.members[curSelected]?.songData; - albumRoll.setDifficultyStars(daSong?.songRating ?? 0); } function changeSelection(change:Int = 0):Void diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index f38db1ccd..c1dc54ffe 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -1,5 +1,6 @@ package funkin.ui.mainmenu; +import funkin.graphics.FunkinSprite; import flixel.addons.transition.FlxTransitionableState; import funkin.ui.debug.DebugMenuSubState; import flixel.FlxObject; @@ -56,7 +57,8 @@ class MainMenuState extends MusicBeatState persistentUpdate = false; persistentDraw = true; - var bg:FlxSprite = new FlxSprite(Paths.image('menuBG')); + var bg = FunkinSprite.create('menuDesat'); + bg.color = 0xFFFDE871; bg.scrollFactor.x = 0; bg.scrollFactor.y = 0.17; bg.setGraphicSize(Std.int(bg.width * 1.2)); From da4f1fb424564b1be658fd0d32b7b53e743299a0 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 5 Apr 2024 19:47:33 -0400 Subject: [PATCH 07/33] remove difficulty stars --- source/funkin/ui/freeplay/AlbumRoll.hx | 52 +++++++++++----------- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index 189e04973..89c6c73a6 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -37,8 +37,8 @@ class AlbumRoll extends FlxSpriteGroup } var newAlbumArt:FlxAtlasSprite; - var difficultyStars:DifficultyStars; + // var difficultyStars:DifficultyStars; var _exitMovers:Null; var albumData:Album; @@ -65,9 +65,9 @@ class AlbumRoll extends FlxSpriteGroup add(newAlbumArt); - difficultyStars = new DifficultyStars(140, 39); - difficultyStars.stars.visible = false; - add(difficultyStars); + // difficultyStars = new DifficultyStars(140, 39); + // difficultyStars.stars.visible = false; + // add(difficultyStars); } function onAlbumFinish(animName:String):Void @@ -86,7 +86,7 @@ class AlbumRoll extends FlxSpriteGroup { if (albumId == null) { - difficultyStars.stars.visible = false; + // difficultyStars.stars.visible = false; return; } @@ -133,12 +133,12 @@ class AlbumRoll extends FlxSpriteGroup wait: 0 }); - exitMovers.set([difficultyStars], - { - x: FlxG.width * 1.2, - speed: 0.2, - wait: 0.3 - }); + // exitMovers.set([difficultyStars], + // { + // x: FlxG.width * 1.2, + // speed: 0.2, + // wait: 0.3 + // }); } var titleTimer:Null = null; @@ -151,10 +151,10 @@ class AlbumRoll extends FlxSpriteGroup newAlbumArt.visible = true; newAlbumArt.playAnimation(animNames.get('$albumId-active'), false, false, false); - difficultyStars.stars.visible = false; + // difficultyStars.stars.visible = false; new FlxTimer().start(0.75, function(_) { // showTitle(); - showStars(); + // showStars(); }); } @@ -163,18 +163,16 @@ class AlbumRoll extends FlxSpriteGroup newAlbumArt.playAnimation(animNames.get('$albumId-trans'), false, false, false); } - public function setDifficultyStars(?difficulty:Int):Void - { - if (difficulty == null) return; - - difficultyStars.difficulty = difficulty; - } - - /** - * Make the album stars visible. - */ - public function showStars():Void - { - difficultyStars.stars.visible = false; // true; - } + // public function setDifficultyStars(?difficulty:Int):Void + // { + // if (difficulty == null) return; + // difficultyStars.difficulty = difficulty; + // } + // /** + // * Make the album stars visible. + // */ + // public function showStars():Void + // { + // difficultyStars.stars.visible = false; // true; + // } } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 150f5a0b1..ae51ba82a 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -470,7 +470,7 @@ class FreeplayState extends MusicBeatSubState albumRoll.playIntro(); new FlxTimer().start(0.75, function(_) { - albumRoll.showTitle(); + // albumRoll.showTitle(); }); FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut}); From 2462bd2d9943fe7839335d080db74c0b3f3e5059 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 5 Apr 2024 22:05:34 -0400 Subject: [PATCH 08/33] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 2eb07acf1..84c3c7fef 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2eb07acf1fbee70229b913f87cfc1bfddf22ab88 +Subproject commit 84c3c7fef411c616e558b5b497f70c106adc11d1 From 9a7a385af1e051a1affd19725eb14b731da5f921 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 01:11:27 -0400 Subject: [PATCH 09/33] Fix segmentation fault on Linux by rebuilding Lime. --- .vscode/settings.json | 5 +++++ hmm.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 13a1862d2..96481461d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -96,6 +96,11 @@ "target": "windows", "args": ["-debug", "-DFORCE_DEBUG_VERSION"] }, + { + "label": "Linux / Debug", + "target": "linux", + "args": ["-debug", "-DFORCE_DEBUG_VERSION"] + }, { "label": "HashLink / Debug", "target": "hl", diff --git a/hmm.json b/hmm.json index 641ef1bbd..8c07023c7 100644 --- a/hmm.json +++ b/hmm.json @@ -104,7 +104,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "1359fe6ad52e91175dc636a516d460bd54ea22ed", + "ref": "43ebebdd8119936b99f23407057025c7849c5f5b", "url": "https://github.com/FunkinCrew/lime" }, { From 958ebe92a3b5f8e834706e306793426b4b98b5f1 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 01:59:39 -0400 Subject: [PATCH 10/33] Fix tankman's offsets to not suck. --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index e8f4d2d91..da3c17f2b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e8f4d2d91c7bd5a922465e7d67a0efb7d7574bd6 +Subproject commit da3c17f2b0473af1b95167e563c4b3799d006438 From 29f105a44d89487a69df84604719c5fd84df88f6 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 01:59:46 -0400 Subject: [PATCH 11/33] Practice mode = no highscore! --- source/funkin/play/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 795f493e8..215979fdd 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2786,7 +2786,7 @@ class PlayState extends MusicBeatSubState // adds current song data into the tallies for the level (story levels) Highscore.talliesLevel = Highscore.combineTallies(Highscore.tallies, Highscore.talliesLevel); - if (Save.instance.isSongHighScore(currentSong.id, currentDifficulty, data)) + if (!isPracticeMode && Save.instance.isSongHighScore(currentSong.id, currentDifficulty, data)) { Save.instance.setSongScore(currentSong.id, currentDifficulty, data); #if newgrounds From 99ee2b07ca1737430afc1bf8edcc1da58c12297f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 02:06:06 -0400 Subject: [PATCH 12/33] Make results screen use full campaign score in Story Mode --- source/funkin/play/PlayState.hx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 215979fdd..2c8d72293 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -3072,18 +3072,18 @@ class PlayState extends MusicBeatSubState title: PlayStatePlaylist.isStoryMode ? ('${PlayStatePlaylist.campaignTitle}') : ('${currentChart.songName} by ${currentChart.songArtist}'), scoreData: { - score: songScore, + score: PlayStatePlaylist.isStoryMode ? PlayStatePlaylist.campaignScore : songScore, tallies: { - sick: Highscore.tallies.sick, - good: Highscore.tallies.good, - bad: Highscore.tallies.bad, - shit: Highscore.tallies.shit, - missed: Highscore.tallies.missed, - combo: Highscore.tallies.combo, - maxCombo: Highscore.tallies.maxCombo, - totalNotesHit: Highscore.tallies.totalNotesHit, - totalNotes: Highscore.tallies.totalNotes, + sick: talliesToUse.sick, + good: talliesToUse.good, + bad: talliesToUse.bad, + shit: talliesToUse.shit, + missed: talliesToUse.missed, + combo: talliesToUse.combo, + maxCombo: talliesToUse.maxCombo, + totalNotesHit: talliesToUse.totalNotesHit, + totalNotes: talliesToUse.totalNotes, }, accuracy: Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes, }, From 14869b937329e455049595d905b95ce0b830333f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 02:08:10 -0400 Subject: [PATCH 13/33] Also disable highscores in botplay --- source/funkin/play/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 2c8d72293..474caf031 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2786,7 +2786,7 @@ class PlayState extends MusicBeatSubState // adds current song data into the tallies for the level (story levels) Highscore.talliesLevel = Highscore.combineTallies(Highscore.tallies, Highscore.talliesLevel); - if (!isPracticeMode && Save.instance.isSongHighScore(currentSong.id, currentDifficulty, data)) + if (!isPracticeMode && !isBotPlayMode && Save.instance.isSongHighScore(currentSong.id, currentDifficulty, data)) { Save.instance.setSongScore(currentSong.id, currentDifficulty, data); #if newgrounds From 09d5370c2ead699969b135c61fd692075d7f72ee Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 02:35:27 -0400 Subject: [PATCH 14/33] Fix an issue where Pico gets stuck blocking after a failed uppercut. --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index e8f4d2d91..c73fd0b0a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e8f4d2d91c7bd5a922465e7d67a0efb7d7574bd6 +Subproject commit c73fd0b0ab8f904ac22b594afc847be2d10587f4 From 5beb20ec7cb876e6b571c9da7bf53c19ae14647d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 21:42:18 -0400 Subject: [PATCH 15/33] Fix bug where restarting the song would sometimes crash. --- source/funkin/play/PlayState.hx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 795f493e8..dbae9ed2a 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -834,9 +834,12 @@ class PlayState extends MusicBeatSubState inputSpitter = []; // Reset music properly. - FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset; - FlxG.sound.music.pitch = playbackRate; - FlxG.sound.music.pause(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset; + FlxG.sound.music.pitch = playbackRate; + FlxG.sound.music.pause(); + } if (!overrideMusic) { @@ -852,7 +855,7 @@ class PlayState extends MusicBeatSubState vocals.pause(); vocals.time = 0; - FlxG.sound.music.volume = 1; + if (FlxG.sound.music != null) FlxG.sound.music.volume = 1; vocals.volume = 1; vocals.playerVolume = 1; vocals.opponentVolume = 1; From ea1d123c49531d2f94063b0d4ed39a94b4654849 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 21:42:31 -0400 Subject: [PATCH 16/33] Fixes to Pico Blazin' animations. --- assets | 2 +- source/funkin/play/character/AnimateAtlasCharacter.hx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assets b/assets index e8f4d2d91..287f8e222 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e8f4d2d91c7bd5a922465e7d67a0efb7d7574bd6 +Subproject commit 287f8e222d00cfd0d24eb79465b7a256bb80318f diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index f1dadf3e2..ed58b92b5 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -192,6 +192,7 @@ class AnimateAtlasCharacter extends BaseCharacter if (!this.mainSprite.hasAnimation(prefix)) { FlxG.log.warn('[ATLASCHAR] Animation ${prefix} not found in Animate Atlas ${_data.assetPath}'); + trace('[ATLASCHAR] Animation ${prefix} not found in Animate Atlas ${_data.assetPath}'); continue; } animations.set(anim.name, anim); From b464f51ba2572ce57d01caedde7cb0fa03789c77 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 22:38:32 -0400 Subject: [PATCH 17/33] Fix an issue where a missing stage would cause a crash (rather than an error popup) --- source/funkin/play/PlayState.hx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index dbae9ed2a..a5fbb7624 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1551,10 +1551,11 @@ 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) { + currentStage.revive(); // Stages are killed and props destroyed when the PlayState is destroyed to save memory. + // Actually create and position the sprites. var event:ScriptEvent = new ScriptEvent(CREATE, false); ScriptEventDispatcher.callEvent(currentStage, event); From 207eedf7b5dc4f9c92fc3c216576fff3858ea262 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 6 Apr 2024 22:38:39 -0400 Subject: [PATCH 18/33] Update assets submodule. --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 287f8e222..6e098afdc 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 287f8e222d00cfd0d24eb79465b7a256bb80318f +Subproject commit 6e098afdcc30c8508afc09bc3e57b502821029ab From 6d374c7af9e0e784bedd1c949b59c5c468b04c3d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 8 Apr 2024 18:39:51 -0400 Subject: [PATCH 19/33] Fix loading issue tied to -DSONG compile define. --- .vscode/settings.json | 5 ++++ source/funkin/InitState.hx | 33 +++++++++++++++++++++ source/funkin/ui/transition/LoadingState.hx | 1 - 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 13a1862d2..0cca68aab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -130,6 +130,11 @@ "-DFORCE_DEBUG_VERSION" ] }, + { + "label": "Windows / Debug (Straight to Play - 2hot)", + "target": "windows", + "args": ["-debug", "-DSONG=2hot", "-DFORCE_DEBUG_VERSION"] + }, { "label": "HashLink / Debug (Straight to Play - Bopeebo Normal)", "target": "hl", diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 6ea77ec18..9b842bc13 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -261,6 +261,35 @@ class InitState extends FlxState return; } + // TODO: Rework loading behavior so we don't have to do this. + switch (songId) + { + case 'tutorial' | 'bopeebo' | 'fresh' | 'dadbattle': + Paths.setCurrentLevel('week1'); + PlayStatePlaylist.campaignId = 'week1'; + case 'spookeez' | 'south' | 'monster': + Paths.setCurrentLevel('week2'); + PlayStatePlaylist.campaignId = 'week2'; + case 'pico' | 'philly-nice' | 'blammed': + Paths.setCurrentLevel('week3'); + PlayStatePlaylist.campaignId = 'week3'; + case 'high' | 'satin-panties' | 'milf': + Paths.setCurrentLevel('week4'); + PlayStatePlaylist.campaignId = 'week4'; + case 'cocoa' | 'eggnog' | 'winter-horrorland': + Paths.setCurrentLevel('week5'); + PlayStatePlaylist.campaignId = 'week5'; + case 'senpai' | 'roses' | 'thorns': + Paths.setCurrentLevel('week6'); + PlayStatePlaylist.campaignId = 'week6'; + case 'ugh' | 'guns' | 'stress': + Paths.setCurrentLevel('week7'); + PlayStatePlaylist.campaignId = 'week7'; + case 'darnell' | 'lit-up' | '2hot' | 'blazin': + Paths.setCurrentLevel('weekend1'); + PlayStatePlaylist.campaignId = 'weekend1'; + } + LoadingState.loadPlayState( { targetSong: songData, @@ -283,6 +312,10 @@ class InitState extends FlxState return; } + // TODO: Rework loading behavior so we don't have to do this. + Paths.setCurrentLevel(levelId); + PlayStatePlaylist.campaignId = levelId; + PlayStatePlaylist.playlistSongIds = currentLevel.getSongs(); PlayStatePlaylist.isStoryMode = true; PlayStatePlaylist.campaignScore = 0; diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index af8798ae2..347190993 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -281,7 +281,6 @@ class LoadingState extends MusicBeatSubState { // TODO: This section is a hack! Redo this later when we have a proper asset caching system. FunkinSprite.preparePurgeCache(); - FunkinSprite.cacheTexture(Paths.image('combo')); FunkinSprite.cacheTexture(Paths.image('healthBar')); FunkinSprite.cacheTexture(Paths.image('menuDesat')); FunkinSprite.cacheTexture(Paths.image('combo')); From 97884021c0ad7fab887611d7971cd189e63df597 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Mon, 8 Apr 2024 22:08:24 -0700 Subject: [PATCH 20/33] Fix bad initial save data when no save data present If no save data was present, the game would check for legacy save data in the ninjamuffin99 path to migrate over. This code was incorrectly checking `FlxSave.data == null` to determine if legacy data was present. This caused an empty legacy save being migrated over, resulting in a `volume` of 0 for any new player of the game. --- source/funkin/save/Save.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index af2730ddd..bfbda2a02 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -693,7 +693,7 @@ class Save trace("[SAVE] Checking for legacy save data..."); var legacySave:FlxSave = new FlxSave(); legacySave.bind(SAVE_NAME_LEGACY, SAVE_PATH_LEGACY); - if (legacySave?.data == null) + if (legacySave.isEmpty()) { trace("[SAVE] No legacy save data found."); return null; From eb752c242f3c59d275640bacc875e99b80c5d5e5 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 9 Apr 2024 20:13:59 -0400 Subject: [PATCH 21/33] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 1a7a0b6cc..bef67ec4b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1a7a0b6cc60dc8131f1651caa7abef0c1944a10c +Subproject commit bef67ec4b3c834416e73ce4e7c3e7128c5d7de63 From 1058181cc93a609363de5faf8ae5bcc69c5478cf Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 9 Apr 2024 20:25:33 -0400 Subject: [PATCH 22/33] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 84c3c7fef..0782d868b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 84c3c7fef411c616e558b5b497f70c106adc11d1 +Subproject commit 0782d868ba8379d699ef9ab9547c3507580628e2 From 5e7b417d6f8dfcdea303b0d4e4e58b328027e9d6 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 12 Apr 2024 14:44:01 -0400 Subject: [PATCH 23/33] Remove Kickstarter link from credits. --- assets | 2 +- source/funkin/ui/credits/CreditsDataHandler.hx | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/assets b/assets index 1a7a0b6cc..40d946207 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1a7a0b6cc60dc8131f1651caa7abef0c1944a10c +Subproject commit 40d946207aa14b996b152800f285f94b7a679ba4 diff --git a/source/funkin/ui/credits/CreditsDataHandler.hx b/source/funkin/ui/credits/CreditsDataHandler.hx index 86afdafd1..1294dd73a 100644 --- a/source/funkin/ui/credits/CreditsDataHandler.hx +++ b/source/funkin/ui/credits/CreditsDataHandler.hx @@ -57,10 +57,6 @@ class CreditsDataHandler {line: 'KawaiSprite'}, {line: 'evilsk8r'}, ] - }, - { - header: 'Kickstarter Backers', - appendBackers: true } ] }; @@ -68,11 +64,11 @@ class CreditsDataHandler public static function fetchBackerEntries():Array { - // TODO: Replace this with a web request. + // TODO: Implement a web request. // We can't just grab the current Kickstarter data and include it in builds, // because we don't want to deadname people who haven't logged into the portal yet. // It can be async and paginated for performance! - return ['See the list of backers at $BACKER_PUBLIC_URL.']; + return []; } #if HARDCODED_CREDITS From 7644ce1019e75fb1d14ed7b8d1fc6686b282af60 Mon Sep 17 00:00:00 2001 From: Hazel Date: Sun, 14 Apr 2024 23:49:41 +0200 Subject: [PATCH 24/33] feat: ci parallelization, linux builds, ci refactoring (#484) * first linux build attempt * linux deps! * hxcodec dependencies * build timeouts * reup hxcpp cache every time by default gh actions will not update caches on cache hit. since the hxcpp cache grows with compiles, that's not what we want here. since we *do* need the files newly compiled. * ci speed test * group runners by purpose * REFACTOR CI. IT WAS NEEDED * smol changies * second attempt at libc * fix any format issues * it's 1:50am * migrate away from gacts/run-and-post-run@v1 * apt does not have a stable cli interface. use with caution in scripts * first attempt at libffi6 * second attempt at libffi6 * fuck that * sigh * html5 also needs new libc * make sure rerunning ln -s doesn't fail the build * desperate attempt * arc attempt * arc, ii * ci * apt-GET * who needs safeguards anyway * clean ci build * debug time * lots of connectivity debugging * :pleading_face: * natesales/q * i'm not very smart * debug hard? * whose traceroute? * pls * ... * we go even newer * merge moment * haxelib maybe * debug info * :pleading_face: * lower mtu runner? * libffi my beloved * no multiline env? * smol buggy * non docker-aware vars * i love bash * builds hopefully go nyoom * forgor native dep * [skip ci] meow * convenient typo * sigh * [skip ci] waow * [skip ci] docker is hard * i don't understand docker tbh * debuggering * docking * small amount of dumb but still doesn't explain why curl doesn't curl * just vsc things * ca certs issue? * please this has to be the one * find -type d fail, laugh at this user * too eepy * im not that smart am i * attempt to run containerized from docker base image * [skip-ci] some more docking * might as well try * :pleaading_face: * ? * ! * idea * sigh * i give in * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * :pleading_face: * hopefully fix hxcpp cache * [skip ci] a little dx never hurt anyone * try removing things until it breaks * welp that was easy * not-docker-friendly paths in non-docker env, etc. you get the point * more sane default cache size * finishing touches * welp * mounted volume * no systemd, got it * more failproof dockerfile * does this not have ossh * haxelib master * hopefully final docker build * bob the builder * docking :3 * image cleanup * github moment * okay mayb * gotta set it up first * i have an idea * hope we ready * :pleading_face: * fuck * sigh * trigger build on new image * no comment * global hxcpp, first idea * yikes * hxcpp oopsy * code dupe * more code dupe * lint * increase hxcpp cache size on native builds as well * buttons :3 * oops * forgor to export env variable * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --- .github/actions/setup-haxe/action.yml | 135 ++++++++++++++ .github/actions/setup-haxeshit/action.yml | 55 ------ .github/actions/upload-itch/action.yml | 142 ++++++++++---- .github/workflows/build-docker-image.yml | 53 ++++++ .github/workflows/build-game.yml | 125 +++++++++++++ .github/workflows/build-shit.yml | 136 -------------- .github/workflows/cancel-merged-branches.yml | 45 ++--- build/Dockerfile | 185 +++++++++++++++++++ 8 files changed, 633 insertions(+), 243 deletions(-) create mode 100644 .github/actions/setup-haxe/action.yml delete mode 100644 .github/actions/setup-haxeshit/action.yml create mode 100644 .github/workflows/build-docker-image.yml create mode 100644 .github/workflows/build-game.yml delete mode 100644 .github/workflows/build-shit.yml create mode 100644 build/Dockerfile diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml new file mode 100644 index 000000000..54db9bf79 --- /dev/null +++ b/.github/actions/setup-haxe/action.yml @@ -0,0 +1,135 @@ +name: setup-haxeshit +description: "sets up haxe shit, using HMM!" + +inputs: + haxe: + description: 'Version of haxe to install' + required: true + default: '4.3.4' + hxcpp-cache: + description: 'Whether to use a shared hxcpp compile cache' + required: true + default: 'true' + hxcpp-cache-path: + description: 'Path to create hxcpp cache in' + required: true + default: ${{ runner.temp }}/hxcpp_cache + targets: + description: 'Targets we plan to compile to. Installs native dependencies needed.' + required: true + +runs: + using: "composite" + steps: + + - name: Setup timers + shell: bash + run: | + echo "TIMER_HAXE=$(date +%s)" >> "$GITHUB_ENV" + + - name: Install Haxe + uses: funkincrew/ci-haxe@v3.1.0 + with: + haxe-version: ${{ inputs.haxe }} + + - name: Install native dependencies + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + ls -lah /usr/lib/x86_64-linux-gnu/ + apt-get update + apt-get install -y \ + g++ \ + libx11-dev libxi-dev libxext-dev libxinerama-dev libxrandr-dev \ + libgl-dev libgl1-mesa-dev \ + libasound2-dev + ln -s /usr/lib/x86_64-linux-gnu/libffi.so.8 /usr/lib/x86_64-linux-gnu/libffi.so.6 || true + - name: Install linux-specific dependencies + if: ${{ runner.os == 'Linux' && contains(inputs.targets, 'linux') }} + shell: bash + run: | + apt-get install -y libvlc-dev libvlccore-dev + + - name: Config haxelib + shell: bash + run: | + echo "TIMER_HAXELIB=$(date +%s)" >> "$GITHUB_ENV" + haxelib --debug --never install haxelib 4.1.0 --global + haxelib --debug --never deleterepo || true + haxelib --debug --never newrepo + echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV" + haxelib --debug --never git haxelib https://github.com/HaxeFoundation/haxelib.git master + haxelib --debug --global install hmm + echo "TIMER_DEPS=$(date +%s)" >> "$GITHUB_ENV" + + - name: Restore cached dependencies + id: cache-hmm + uses: actions/cache@v4 + with: + path: .haxelib + key: haxe-hmm-${{ runner.os }}-${{ hashFiles('**/hmm.json') }} + + - if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }} + name: Install dependencies + shell: bash + run: | + haxelib --debug --global run hmm install + echo "TIMER_DONE=$(date +%s)" >> "$GITHUB_ENV" + + # by default use a shared hxcpp cache + - if: ${{ inputs.hxcpp-cache == 'true' }} + name: Restore hxcpp cache + uses: actions/cache@v4 + with: + path: ${{ inputs.hxcpp-cache-path }} + key: haxe-hxcpp-${{ runner.os }}-${{ github.ref_name }}-${{ github.sha }} + restore-keys: haxe-hxcpp-${{ runner.os }}-${{ github.ref_name }} + # export env for it to reuse in builds + - if: ${{ inputs.hxcpp-cache == 'true' }} + name: Persist env for hxcpp cache + shell: bash + run: | + echo "HXCPP_COMPILE_CACHE=${{ inputs.hxcpp-cache-path }}" >> "$GITHUB_ENV" + echo 'HXCPP_CACHE_MB="4096"' >> "$GITHUB_ENV" + + # if it's explicitly disabled, still cache export/ since that then contains the builds + - if: ${{ inputs.hxcpp-cache != 'true' }} + name: Restore export cache + uses: actions/cache@v4 + with: + path: ${{ inputs.hxcpp-cache-path }} + key: haxe-export-${{ runner.os }}-${{ github.ref_name }}-${{ github.sha }} + restore-keys: haxe-export-${{ runner.os }}-${{ github.ref_name }} + + - name: Print debug info + shell: bash + run: | + cat << EOF + runner: + kernel: $(uname -a) + haxe: + version: $(haxe -version) + which: $(which haxe) + haxepath: $HAXEPATH + took: $((TIMER_HAXELIB - TIMER_HAXE))s + haxelib: + version: $(haxelib version) + which: $(which haxelib) + local: + config: $(haxelib config) + path: $(haxelib path haxelib || true) + global + config: $(haxelib config --global) + path: $(haxelib path haxelib --global || true) + system + version: $(haxelib --system version) + local: + config: $(haxelib --system config) + global: + config: $(haxelib --system config --global) + took: $((TIMER_DEPS - TIMER_HAXELIB))s + deps: + took: $((TIMER_DONE - TIMER_DEPS))s + hxcpp_cache: | + $(haxelib run hxcpp cache list || true) + EOF diff --git a/.github/actions/setup-haxeshit/action.yml b/.github/actions/setup-haxeshit/action.yml deleted file mode 100644 index 236d29944..000000000 --- a/.github/actions/setup-haxeshit/action.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: setup-haxeshit -description: "sets up haxe shit, using HMM!" -runs: - using: "composite" - steps: - - name: Install Haxe lol - uses: funkincrew/ci-haxe@v3.1.0 - with: - haxe-version: 4.3.3 - - name: Config haxelib - run: | - haxelib --never install haxelib 4.1.0 --global - haxelib --never deleterepo || true - haxelib --never newrepo - echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV" - haxelib --never git haxelib https://github.com/HaxeFoundation/haxelib.git master - shell: bash - - name: Gather debug info - run: | - cat << EOF >> "$GITHUB_STEP_SUMMARY" - ## haxe - - version: \`$(haxe -version)\` - - exe: \`$(which haxe)\` - ## haxelib - - version: \`$(haxelib version)\` - - exe: \`$(which haxelib)\` - - path: \`$HAXEPATH\` - ### local - - config: \`$(haxelib config)\` - - path: \`$(haxelib path haxelib || true)\` - ### global - - config: \`$(haxelib config --global)\` - - path: \`$(haxelib path haxelib --global || true)\` - ### system - - version: \`$(haxelib --system version)\` - - local: \`$(haxelib --system config)\` - - global: \`$(haxelib --system config --global)\` - EOF - shell: bash - - name: Install hmm - # hmm only supports global installs - run: | - haxelib --global install hmm - shell: bash - - name: Restore cached dependencies - id: cache-hmm - uses: actions/cache@v4 - with: - path: .haxelib - key: ${{ runner.os }}-hmm-${{ hashFiles('**/hmm.json') }} - - if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }} - name: hmm install - run: | - haxelib --global run hmm install - shell: bash diff --git a/.github/actions/upload-itch/action.yml b/.github/actions/upload-itch/action.yml index 2f7d3027d..fb049efc9 100644 --- a/.github/actions/upload-itch/action.yml +++ b/.github/actions/upload-itch/action.yml @@ -1,44 +1,124 @@ name: upload-itch description: "installs Butler, and uploads to itch.io!" + inputs: butler-key: description: "Butler API secret key" required: true + itch-repo: + description: "Where to upload the game to" + required: true + default: "ninja-muffin24/funkin-secret" build-dir: description: "Directory of the game build" - required: true + required: false target: - description: "Target (html5, win, linux, mac)" + description: "Target (html5, windows, linux, macos)" required: true + runs: using: "composite" steps: - - name: Install butler Windows - if: runner.os == 'Windows' - run: | - curl -L -o butler.zip https://broth.itch.ovh/butler/windows-amd64/LATEST/archive/default - 7z x butler.zip - ./butler -v - shell: bash - - name: Install butler Mac - if: runner.os == 'macOS' - run: | - curl -L -o butler.zip https://broth.itch.ovh/butler/darwin-amd64/LATEST/archive/default - unzip butler.zip - ./butler -V - shell: bash - - name: Install butler Linux - if: runner.os == 'Linux' - run: | - curl -L -o butler.zip https://broth.itch.ovh/butler/linux-amd64/LATEST/archive/default - unzip butler.zip - chmod +x butler - ./butler -V - shell: bash - - name: Upload game to itch.io - env: - BUTLER_API_KEY: ${{inputs.butler-key}} - run: | - ./butler login - ./butler push ${{inputs.build-dir}} ninja-muffin24/funkin-secret:${{inputs.target}}-${GITHUB_REF_NAME} - shell: bash + + # RUNNER_OS = Windows | macOS | Linux + # TARGET_BUILD = windows | macos | linux + # TARGET_ITCH = win | macos | linux + # TARGET_BUTLER_DOWNLOAD = windows-amd64 | darwin-amd64 | linux-amd64 + - name: Setup variables + shell: bash + run: | + TARGET_OS=${{ inputs.target }} + RUNNER=${RUNNER_OS@L} + TARGET=${TARGET_OS@L} + case "$TARGET" in + "windows") + TARGET_ITCH=win + ;; + *) + TARGET_ITCH=$TARGET + ;; + esac + case "$RUNNER" in + "macos") + OS_NODE=darwin + ;; + *) + OS_NODE=$RUNNER + ;; + esac + BUTLER_PATH=$RUNNER_TEMP/butler + + echo BUILD_DIR="export/release/$TARGET/bin" >> "$GITHUB_ENV" + echo ITCH_TAG=${{ inputs.itch-repo }}:$TARGET_ITCH-$GITHUB_REF_NAME >> "$GITHUB_ENV" + echo OS_AND_ARCH=$OS_NODE-amd64 >> "$GITHUB_ENV" + echo BUTLER_API_KEY=${{ inputs.butler-key }} >> "$GITHUB_ENV" + echo BUTLER_INSTALL_PATH=$BUTLER_PATH >> "$GITHUB_ENV" + echo TIMER_BUTLER=$(date +%s) >> "$GITHUB_ENV" + echo TARGET_ITCH=$TARGET_ITCH >> "$GITHUB_ENV" + + echo "$BUTLER_PATH" >> "$GITHUB_PATH" + + - name: Get latest butler version + shell: bash + run: | + LATEST=$(curl -sfL https://broth.itch.ovh/butler/$OS_AND_ARCH/LATEST) + echo BUTLER_LATEST=$LATEST >> "$GITHUB_ENV" + + command -v butler \ + && echo BUTLER_CURRENT=$(butler -V 2>&1 | cut -d ',' -f 1) >> "$GITHUB_ENV" \ + || echo BUTLER_CURRENT=none >> "$GITHUB_ENV" + + - name: Try to get butler from cache + id: cache-butler + uses: actions/cache@v4 + with: + path: ${{ env.BUTLER_INSTALL_PATH }} + key: butler-${{ runner.os }}-${{ env.BUTLER_LATEST }} + + - if: steps.cache-butler.outputs.cache-hit == 'true' + name: Make sure butler is executable + shell: bash + run: | + chmod +x $BUTLER_INSTALL_PATH/butler + + - if: steps.cache-butler.outputs.cache-hit != 'true' + name: Install butler + shell: bash + run: | + mkdir -p $BUTLER_INSTALL_PATH + cd $BUTLER_INSTALL_PATH + + curl -L -o butler.zip https://broth.itch.ovh/butler/$OS_AND_ARCH/LATEST/archive/default + unzip butler.zip + chmod +x butler + + - name: Upload game to itch.io + shell: bash + run: | + echo "TIMER_UPLOAD=$(date +%s)" >> "$GITHUB_ENV" + butler login + butler push $BUILD_DIR $ITCH_TAG + echo "TIMER_DONE=$(date +%s)" >> "$GITHUB_ENV" + + - name: Print debug info + shell: bash + run: | + cat << EOF + butler: + version: $( + if [[ "$BUTLER_CURRENT" == "$BUTLER_LATEST" ]] + then + echo $BUTLER_CURRENT + else + echo $BUTLER_CURRENT -> $BUTLER_LATEST + fi + ) + install: + took: $(($TIMER_UPLOAD-$TIMER_BUTLER))s + upload: + tag: $TARGET_ITCH/$GITHUB_REF_NAME + took: $(($TIMER_DONE-$TIMER_UPLOAD))s + EOF + cat << EOF >> "$GITHUB_STEP_SUMMARY" + ### open in launcher: [$TARGET_ITCH/$GITHUB_REF_NAME](https://run.funkin.me/$TARGET_ITCH/$GITHUB_REF_NAME) + EOF diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml new file mode 100644 index 000000000..15c9e5582 --- /dev/null +++ b/.github/workflows/build-docker-image.yml @@ -0,0 +1,53 @@ +name: Create and publish Docker image + +on: + workflow_dispatch: + push: + paths: + - '**/Dockerfile' + - '.github/workflows/build-docker-image.yml' + +jobs: + build-and-push-image: + runs-on: build-set + permissions: + contents: read + packages: write + + steps: + - name: Get checkout token + uses: actions/create-github-app-token@v1 + id: app_token + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PEM }} + owner: ${{ github.repository_owner }} + + - name: Checkout repo + uses: funkincrew/ci-checkout@v6 + with: + submodules: false + token: ${{ steps.app_token.outputs.token }} + + - name: Log into GitHub Container Registry + uses: docker/login-action@v3.1.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5.3.0 + with: + context: ./build + push: true + tags: | + ghcr.io/funkincrew/build-dependencies:latest + ghcr.io/funkincrew/build-dependencies:${{ github.sha }} + labels: | + org.opencontainers.image.description=precooked haxe build-dependencies + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.source=https://github.com/${{ github.repository }} + org.opencontainers.image.title=${{ github.repository_owner }}/build-dependencies + org.opencontainers.image.url=https://github.com/${{ github.repository }} + org.opencontainers.image.version=${{ github.sha }} diff --git a/.github/workflows/build-game.yml b/.github/workflows/build-game.yml new file mode 100644 index 000000000..3bfea20f2 --- /dev/null +++ b/.github/workflows/build-game.yml @@ -0,0 +1,125 @@ +name: Build and Upload nightly game builds + +on: + workflow_dispatch: + push: + paths-ignore: + - '**/Dockerfile' + - '.github/workflows/build-docker-image.yml' + +jobs: + + build-game-on-host: + strategy: + matrix: + include: + - target: windows + - target: macos + runs-on: + - ${{ matrix.target }} + defaults: + run: + shell: bash + + steps: + - name: Make git happy + if: ${{ matrix.target == 'macos' }} + run: | + git config --global --add safe.directory $GITHUB_WORKSPACE + + - name: Get checkout token + uses: actions/create-github-app-token@v1 + id: app_token + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PEM }} + owner: ${{ github.repository_owner }} + + - name: Checkout repo + uses: funkincrew/ci-checkout@v6 + with: + submodules: 'recursive' + token: ${{ steps.app_token.outputs.token }} + + - name: Setup build environment + uses: ./.github/actions/setup-haxe + + - name: Build game + if: ${{ matrix.target == 'windows' }} + run: | + haxelib run lime build windows -v -release -DGITHUB_BUILD + timeout-minutes: 120 + - name: Build game + if: ${{ matrix.target != 'windows' }} + run: | + haxelib run lime build ${{ matrix.target }} -v -release --times -DGITHUB_BUILD + timeout-minutes: 120 + + - name: Upload build artifacts + uses: ./.github/actions/upload-itch + with: + butler-key: ${{ secrets.BUTLER_API_KEY}} + target: ${{ matrix.target }} + + build-game-in-container: + runs-on: build-set + container: ghcr.io/funkincrew/build-dependencies:latest + strategy: + matrix: + include: + - target: linux + - target: html5 + defaults: + run: + shell: bash + + steps: + - name: Get checkout token + uses: actions/create-github-app-token@v1 + id: app_token + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PEM }} + owner: ${{ github.repository_owner }} + + - name: Checkout repo + uses: funkincrew/ci-checkout@v6 + with: + submodules: 'recursive' + token: ${{ steps.app_token.outputs.token }} + + - name: Config haxelib + run: | + haxelib --never newrepo + echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV" + + - name: Restore cached dependencies + id: cache-hmm + uses: actions/cache@v4 + with: + path: .haxelib + key: haxe-hmm-${{ runner.os }}-${{ hashFiles('**/hmm.json') }} + + - if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }} + name: Install dependencies + run: | + haxelib --global run hmm install + + - if: ${{ matrix.target != 'html5' }} + name: Restore hxcpp cache + uses: actions/cache@v4 + with: + path: /usr/share/hxcpp + key: haxe-hxcpp-${{ runner.os }}-${{ github.ref_name }}-${{ github.sha }} + restore-keys: haxe-hxcpp-${{ runner.os }}-${{ github.ref_name }} + + - name: Build game + run: | + haxelib run lime build ${{ matrix.target }} -v -release --times -DGITHUB_BUILD + timeout-minutes: 120 + + - name: Upload build artifacts + uses: ./.github/actions/upload-itch + with: + butler-key: ${{ secrets.BUTLER_API_KEY}} + target: ${{ matrix.target }} diff --git a/.github/workflows/build-shit.yml b/.github/workflows/build-shit.yml deleted file mode 100644 index 4d674f025..000000000 --- a/.github/workflows/build-shit.yml +++ /dev/null @@ -1,136 +0,0 @@ -name: build-upload -on: - workflow_dispatch: - push: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - create-nightly-html5: - runs-on: [self-hosted, linux] - container: ubuntu:23.10 - steps: - - name: Install tools missing in container - run: | - apt update - apt install -y sudo git curl unzip - - name: Fix git config on posix runner - # this can't be {{ github.workspace }} because that's not docker-aware - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - - name: Get checkout token - uses: actions/create-github-app-token@v1 - id: app_token - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PEM }} - owner: ${{ github.repository_owner }} - - name: Checkout repo - uses: funkincrew/ci-checkout@v6 - with: - submodules: 'recursive' - token: ${{ steps.app_token.outputs.token }} - - name: Install Haxe, dependencies - uses: ./.github/actions/setup-haxeshit - - name: Install native dependencies - run: | - apt install -y \ - libx11-dev libxi-dev libxext-dev libxinerama-dev libxrandr-dev \ - libgl-dev libgl1-mesa-dev \ - libasound2-dev - - name: Build game - run: | - haxelib run lime build html5 -release --times -DGITHUB_BUILD - - name: Upload build artifacts - uses: ./.github/actions/upload-itch - with: - butler-key: ${{ secrets.BUTLER_API_KEY}} - build-dir: export/release/html5/bin - target: html5 - create-nightly-win: - runs-on: [self-hosted, windows] - defaults: - run: - shell: bash - steps: - - name: Get checkout token - uses: actions/create-github-app-token@v1 - id: app_token - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PEM }} - owner: ${{ github.repository_owner }} - - name: Checkout repo - uses: funkincrew/ci-checkout@v6 - with: - submodules: 'recursive' - token: ${{ steps.app_token.outputs.token }} - - name: Install Haxe, dependencies - uses: ./.github/actions/setup-haxeshit - - name: Setup build cache - run: | - mkdir -p ${{ runner.temp }}/hxcpp_cache - - name: Restore build cache - id: cache-build-win - uses: actions/cache@v4 - with: - path: | - export - ${{ runner.temp }}/hxcpp_cache - key: ${{ runner.os }}-build-win-${{ github.ref_name }} - - name: Build game - run: | - haxelib run lime build windows -v -release -DGITHUB_BUILD - env: - HXCPP_COMPILE_CACHE: "${{ runner.temp }}\\hxcpp_cache" - - name: Upload build artifacts - uses: ./.github/actions/upload-itch - with: - butler-key: ${{ secrets.BUTLER_API_KEY }} - build-dir: export/release/windows/bin - target: win - create-nightly-mac: - runs-on: [self-hosted, macos] - steps: - - name: Fix git config on posix runner - # this can't be {{ github.workspace }} because that's not docker-aware - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - - name: Get checkout token - uses: actions/create-github-app-token@v1 - id: app_token - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PEM }} - owner: ${{ github.repository_owner }} - - name: Checkout repo - uses: funkincrew/ci-checkout@v6 - with: - submodules: 'recursive' - token: ${{ steps.app_token.outputs.token }} - - name: Install Haxe, dependencies - uses: ./.github/actions/setup-haxeshit - - name: Setup build cache - run: | - mkdir -p ${{ runner.temp }}/hxcpp_cache - - name: Restore build cache - id: cache-build-win - uses: actions/cache@v4 - with: - path: | - export - ${{ runner.temp }}/hxcpp_cache - key: ${{ runner.os }}-build-mac-${{ github.ref_name }} - - name: Build game - run: | - haxelib run lime build macos -release --times -DGITHUB_BUILD - env: - HXCPP_COMPILE_CACHE: "${{ runner.temp }}/hxcpp_cache" - - name: Upload build artifacts - uses: ./.github/actions/upload-itch - with: - butler-key: ${{ secrets.BUTLER_API_KEY}} - build-dir: export/release/macos/bin - target: macos diff --git a/.github/workflows/cancel-merged-branches.yml b/.github/workflows/cancel-merged-branches.yml index 84e3bedc9..f66f9647b 100644 --- a/.github/workflows/cancel-merged-branches.yml +++ b/.github/workflows/cancel-merged-branches.yml @@ -1,35 +1,38 @@ -name: cancel-merged-branches +name: Cancel queued workflows on PR merge + on: pull_request: types: - closed jobs: + cancel_stuff: if: github.event.pull_request.merged == true - runs-on: ubuntu-latest + runs-on: build-set permissions: actions: write + steps: - - uses: actions/github-script@v7 - id: cancel-runs - with: - result-encoding: string - retries: 3 - script: | - let branch_workflows = await github.rest.actions.listWorkflowRuns({ + - name: Cancel queued workflows for ${{ github.event.pull_request.head.ref }} + uses: actions/github-script@v7 + with: + result-encoding: string + retries: 3 + script: | + let branch_workflows = await github.rest.actions.listWorkflowRuns({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: "build-shit.yml", + status: "queued", + branch: "${{ github.event.pull_request.head.ref }}" + }); + let runs = branch_workflows.data.workflow_runs; + runs.forEach((run) => { + github.rest.actions.cancelWorkflowRun({ owner: context.repo.owner, repo: context.repo.repo, - workflow_id: "build-shit.yml", - status: "queued", - branch: "${{ github.event.pull_request.head.ref }}" + run_id: run.id }); - let runs = branch_workflows.data.workflow_runs; - runs.forEach((run) => { - github.rest.actions.cancelWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: run.id - }); - }); - console.log(runs); + }); + console.log(runs); diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 000000000..88b44f7a6 --- /dev/null +++ b/build/Dockerfile @@ -0,0 +1,185 @@ +FROM ubuntu:mantic + +ARG haxe_version=4.3.4 +ARG haxelib_version=4.1.0 +ARG neko_version=2.3.0 + +# prepare runner +ENV GITHUB_HOME="/github/home" + +RUN <> /etc/apt/apt.conf.d/10apt-autoremove +APT::Get::AutomaticRemove "0"; +APT::Get::HideAutoRemove "1"; +EOC + +echo <> /etc/apt/apt.conf.d/80retries +"APT::Acquire::Retries \"10\";" +EOC + +echo <> /etc/apt/apt.conf.d/90assumeyes +"APT::Get::Assume-Yes \"true\";" +EOC +EOF + +# Prepare apt-fast +RUN <> /etc/gitconfig +[safe] + directory = * +EOC + +ssh-keyscan -t rsa,ecdsa,ed25519 github.com >> /etc/ssh/ssh_known_hosts +ssh-keyscan -t rsa,ecdsa,ed25519 ravy.dev >> /etc/ssh/ssh_known_hosts +EOF + +# Haxe native dependencies +RUN < Date: Mon, 15 Apr 2024 19:21:55 -0400 Subject: [PATCH 25/33] Fix the saturation on the main menu background. --- source/funkin/ui/mainmenu/MainMenuState.hx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 3bdc2dfe6..1a4686c5b 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -58,7 +58,11 @@ class MainMenuState extends MusicBeatState persistentDraw = true; var bg = FunkinSprite.create('menuDesat'); + bg.color = 0xFFFDE871; + // This line accounts for the fact that the base color of menuDesat is #EFEFEF. + flixel.util.FlxColorTransformUtil.setOffsets(bg.colorTransform, 30, 27, 13, 0.0); + bg.scrollFactor.x = 0; bg.scrollFactor.y = 0.17; bg.setGraphicSize(Std.int(bg.width * 1.2)); From 43ec72fb9d96743388b05ea0db16dd4f3d62a2e1 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 15 Apr 2024 20:36:38 -0400 Subject: [PATCH 26/33] Ensure playNoteHoldCover doesn't call if note is judged as 'miss' --- source/funkin/play/PlayState.hx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 474caf031..3342a6b69 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2388,13 +2388,6 @@ class PlayState extends MusicBeatSubState // Display the combo meter and add the calculation to the score. popUpScore(note, event.score, event.judgement, event.healthChange); - - if (note.isHoldNote && note.holdNoteSprite != null) - { - playerStrumline.playNoteHoldCover(note.holdNoteSprite); - } - - vocals.playerVolume = 1; } /** @@ -2676,6 +2669,13 @@ class PlayState extends MusicBeatSubState } comboPopUps.displayRating(daRating); if (Highscore.tallies.combo >= 10 || Highscore.tallies.combo == 0) comboPopUps.displayCombo(Highscore.tallies.combo); + + if (daNote.isHoldNote && daNote.holdNoteSprite != null) + { + playerStrumline.playNoteHoldCover(daNote.holdNoteSprite); + } + + vocals.playerVolume = 1; } /** From b553dbd517efcac4c53a6595e0d8b69f22312b7b Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 Apr 2024 13:24:41 -0400 Subject: [PATCH 27/33] Remove janky animation on Freeplay menu. (#488) --- source/funkin/ui/freeplay/FreeplayState.hx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index ae51ba82a..3ac441212 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -638,14 +638,7 @@ class FreeplayState extends MusicBeatSubState funnyMenu.favIcon.visible = tempSongs[i].isFav; funnyMenu.hsvShader = hsvShader; - if (i < 8) - { - funnyMenu.initJumpIn(Math.min(i, 4), force); - } - else - { - funnyMenu.forcePosition(); - } + funnyMenu.forcePosition(); grpCapsules.add(funnyMenu); } From d346c9387cdb1001977a9dcf6b7d691069cafbcf Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 16 Apr 2024 19:42:52 -0400 Subject: [PATCH 28/33] Fix a crash in the Results state. also nudge GF over to the spot she's in in the FLA --- source/funkin/play/ResultState.hx | 66 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index c05257338..591020955 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -80,34 +80,34 @@ class ResultState extends MusicBeatSubState bgFlash.visible = false; add(bgFlash); - var bfGfExcellent:FlxAtlasSprite = new FlxAtlasSprite(380, -170, Paths.animateAtlas("resultScreen/resultsBoyfriendExcellent", "shared")); - bfGfExcellent.visible = false; - add(bfGfExcellent); + // var bfGfExcellent:FlxAtlasSprite = new FlxAtlasSprite(380, -170, Paths.animateAtlas("resultScreen/resultsBoyfriendExcellent", "shared")); + // bfGfExcellent.visible = false; + // add(bfGfExcellent); + // + // var bfPerfect:FlxAtlasSprite = new FlxAtlasSprite(370, -180, Paths.animateAtlas("resultScreen/resultsBoyfriendPerfect", "shared")); + // bfPerfect.visible = false; + // add(bfPerfect); + // + // var bfSHIT:FlxAtlasSprite = new FlxAtlasSprite(0, 20, Paths.animateAtlas("resultScreen/resultsBoyfriendSHIT", "shared")); + // bfSHIT.visible = false; + // add(bfSHIT); + // + // bfGfExcellent.anim.onComplete = () -> { + // bfGfExcellent.anim.curFrame = 28; + // bfGfExcellent.anim.play(); // unpauses this anim, since it's on PlayOnce! + // }; + // + // bfPerfect.anim.onComplete = () -> { + // bfPerfect.anim.curFrame = 136; + // bfPerfect.anim.play(); // unpauses this anim, since it's on PlayOnce! + // }; + // + // bfSHIT.anim.onComplete = () -> { + // bfSHIT.anim.curFrame = 150; + // bfSHIT.anim.play(); // unpauses this anim, since it's on PlayOnce! + // }; - var bfPerfect:FlxAtlasSprite = new FlxAtlasSprite(370, -180, Paths.animateAtlas("resultScreen/resultsBoyfriendPerfect", "shared")); - bfPerfect.visible = false; - add(bfPerfect); - - var bfSHIT:FlxAtlasSprite = new FlxAtlasSprite(0, 20, Paths.animateAtlas("resultScreen/resultsBoyfriendSHIT", "shared")); - bfSHIT.visible = false; - add(bfSHIT); - - bfGfExcellent.anim.onComplete = () -> { - bfGfExcellent.anim.curFrame = 28; - bfGfExcellent.anim.play(); // unpauses this anim, since it's on PlayOnce! - }; - - bfPerfect.anim.onComplete = () -> { - bfPerfect.anim.curFrame = 136; - bfPerfect.anim.play(); // unpauses this anim, since it's on PlayOnce! - }; - - bfSHIT.anim.onComplete = () -> { - bfSHIT.anim.curFrame = 150; - bfSHIT.anim.play(); // unpauses this anim, since it's on PlayOnce! - }; - - var gf:FlxSprite = FunkinSprite.createSparrow(500, 300, 'resultScreen/resultGirlfriendGOOD'); + var gf:FlxSprite = FunkinSprite.createSparrow(625, 325, 'resultScreen/resultGirlfriendGOOD'); gf.animation.addByPrefix("clap", "Girlfriend Good Anim", 24, false); gf.visible = false; gf.animation.finishCallback = _ -> { @@ -268,9 +268,9 @@ class ResultState extends MusicBeatSubState switch (resultsVariation) { - case SHIT: - bfSHIT.visible = true; - bfSHIT.playAnimation(""); + // case SHIT: + // bfSHIT.visible = true; + // bfSHIT.playAnimation(""); case NORMAL: boyfriend.animation.play('fall'); @@ -292,9 +292,9 @@ class ResultState extends MusicBeatSubState gf.animation.play('clap', true); gf.visible = true; }); - case PERFECT: - bfPerfect.visible = true; - bfPerfect.playAnimation(""); + // case PERFECT: + // bfPerfect.visible = true; + // bfPerfect.playAnimation(""); // bfGfExcellent.visible = true; // bfGfExcellent.playAnimation(""); From 80a558ac08119b3cf427f6714bb1bc3596ad8c6e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 16 Apr 2024 19:57:19 -0400 Subject: [PATCH 29/33] Fix an issue where changing focus would pause some sounds but not others. --- source/funkin/audio/FunkinSound.hx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 8c1bf3b41..df05cc3ef 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -223,11 +223,12 @@ class FunkinSound extends FlxSound implements ICloneable // already paused before we lost focus. if (_lostFocus && !_alreadyPaused) { + trace('Resuming audio (${this._label}) on focus!'); resume(); } else { - trace('Not resuming audio on focus!'); + trace('Not resuming audio (${this._label}) on focus!'); } _lostFocus = false; } @@ -402,6 +403,12 @@ class FunkinSound extends FlxSound implements ICloneable sound.group = FlxG.sound.defaultSoundGroup; sound.persist = true; + // Make sure to add the sound to the list. + // If it's already in, it won't get re-added. + // If it's not in the list (it gets removed by FunkinSound.playMusic()), + // it will get re-added (then if this was called by playMusic(), removed again) + FlxG.sound.list.add(sound); + // Call onLoad() because the sound already loaded if (onLoad != null && sound._sound != null) onLoad(); From b9824848560d426247d4bf33403786e7898ab0fe Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 16 Apr 2024 20:19:20 -0400 Subject: [PATCH 30/33] Properly fix the menu background to use the correct line colors. --- assets | 2 +- source/funkin/ui/mainmenu/MainMenuState.hx | 10 ++-------- source/funkin/ui/options/OptionsState.hx | 3 +-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/assets b/assets index 1b12b6c66..f3d6cf30d 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1b12b6c6666af6c84addf5aa0795546aa0d04185 +Subproject commit f3d6cf30dee60a237a6773d8140bd1661f62bd4e diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 1a4686c5b..d2d8adeca 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -57,12 +57,7 @@ class MainMenuState extends MusicBeatState persistentUpdate = false; persistentDraw = true; - var bg = FunkinSprite.create('menuDesat'); - - bg.color = 0xFFFDE871; - // This line accounts for the fact that the base color of menuDesat is #EFEFEF. - flixel.util.FlxColorTransformUtil.setOffsets(bg.colorTransform, 30, 27, 13, 0.0); - + var bg:FlxSprite = new FlxSprite(Paths.image('menuBG')); bg.scrollFactor.x = 0; bg.scrollFactor.y = 0.17; bg.setGraphicSize(Std.int(bg.width * 1.2)); @@ -73,7 +68,7 @@ class MainMenuState extends MusicBeatState camFollow = new FlxObject(0, 0, 1, 1); add(camFollow); - magenta = new FlxSprite(Paths.image('menuDesat')); + magenta = new FlxSprite(Paths.image('menuBGMagenta')); magenta.scrollFactor.x = bg.scrollFactor.x; magenta.scrollFactor.y = bg.scrollFactor.y; magenta.setGraphicSize(Std.int(bg.width)); @@ -81,7 +76,6 @@ class MainMenuState extends MusicBeatState magenta.x = bg.x; magenta.y = bg.y; magenta.visible = false; - magenta.color = 0xFFfd719b; // TODO: Why doesn't this line compile I'm going fucking feral diff --git a/source/funkin/ui/options/OptionsState.hx b/source/funkin/ui/options/OptionsState.hx index 0f33a0780..a00b28dbb 100644 --- a/source/funkin/ui/options/OptionsState.hx +++ b/source/funkin/ui/options/OptionsState.hx @@ -23,8 +23,7 @@ class OptionsState extends MusicBeatState override function create() { - var menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat')); - menuBG.color = 0xFFea71fd; + var menuBG = new FlxSprite().loadGraphic(Paths.image('menuBGBlue')); menuBG.setGraphicSize(Std.int(menuBG.width * 1.1)); menuBG.updateHitbox(); menuBG.screenCenter(); From 9a284c34e9fafa6ebb920e038551d25e34cede9b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 19 Apr 2024 13:45:36 -0400 Subject: [PATCH 31/33] assets update for submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 6e098afdc..39c221849 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6e098afdcc30c8508afc09bc3e57b502821029ab +Subproject commit 39c22184988e1b22bd63b07d83c30ece588e85df From 366bebc94cca62f36dadee505bf390cf0eb26446 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 19 Apr 2024 13:58:35 -0400 Subject: [PATCH 32/33] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 837a8639b..1266cb1c0 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 837a8639bd7abe4aa8786dc3790e8d4576f04f28 +Subproject commit 1266cb1c0c5078158df52b2b36205b332ccde019 From f385379935b64706b10c6847f9628d8f4b032947 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 19 Apr 2024 14:01:19 -0400 Subject: [PATCH 33/33] assets update for submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 837a8639b..069c9bf45 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 837a8639bd7abe4aa8786dc3790e8d4576f04f28 +Subproject commit 069c9bf45f197ebe0b38483d11bb30c15bbb5eca