diff --git a/.vscode/settings.json b/.vscode/settings.json
index c28bebeab..a8a67245b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -170,6 +170,11 @@
"target": "windows",
"args": ["-debug", "-DANIMDEBUG", "-DFORCE_DEBUG_VERSION"]
},
+ {
+ "label": "Windows / Debug (Debug hxCodec)",
+ "target": "windows",
+ "args": ["-debug", "-DHXC_LIBVLC_LOGGING", "-DFORCE_DEBUG_VERSION"]
+ },
{
"label": "HashLink / Debug (Straight to Animation Editor)",
"target": "hl",
diff --git a/Project.xml b/Project.xml
index db338d32a..e232fff91 100644
--- a/Project.xml
+++ b/Project.xml
@@ -124,6 +124,8 @@
+
+
diff --git a/hmm.json b/hmm.json
index 8c07023c7..6ee95f984 100644
--- a/hmm.json
+++ b/hmm.json
@@ -4,7 +4,7 @@
"name": "discord_rpc",
"type": "git",
"dir": null,
- "ref": "2d83fa863ef0c1eace5f1cf67c3ac315d1a3a8a5",
+ "ref": "2d83fa8",
"url": "https://github.com/Aidan63/linc_discord-rpc"
},
{
@@ -18,7 +18,7 @@
"name": "flixel-addons",
"type": "git",
"dir": null,
- "ref": "a523c3b56622f0640933944171efed46929e360e",
+ "ref": "a523c3b",
"url": "https://github.com/FunkinCrew/flixel-addons"
},
{
@@ -30,14 +30,14 @@
"name": "flixel-ui",
"type": "git",
"dir": null,
- "ref": "719b4f10d94186ed55f6fef1b6618d32abec8c15",
+ "ref": "719b4f1",
"url": "https://github.com/HaxeFlixel/flixel-ui"
},
{
"name": "flxanimate",
"type": "git",
"dir": null,
- "ref": "9bacdd6ea39f5e3a33b0f5dfb7bc583fe76060d4",
+ "ref": "17e0d59",
"url": "https://github.com/FunkinCrew/flxanimate"
},
{
@@ -45,6 +45,13 @@
"type": "haxelib",
"version": "3.5.0"
},
+ {
+ "name": "funkVis",
+ "type": "git",
+ "dir": null,
+ "ref": "backend-rework",
+ "url": "https://github.com/FunkinCrew/funkVis"
+ },
{
"name": "hamcrest",
"type": "haxelib",
@@ -54,14 +61,14 @@
"name": "haxeui-core",
"type": "git",
"dir": null,
- "ref": "0212d8fdfcafeb5f0d5a41e1ddba8ff21d0e183b",
+ "ref": "0212d8fd",
"url": "https://github.com/haxeui/haxeui-core"
},
{
"name": "haxeui-flixel",
"type": "git",
"dir": null,
- "ref": "63a906a6148958dbfde8c7b48d90b0693767fd95",
+ "ref": "63a906a",
"url": "https://github.com/haxeui/haxeui-flixel"
},
{
@@ -73,7 +80,7 @@
"name": "hxCodec",
"type": "git",
"dir": null,
- "ref": "387e1665d6feb5762358134f168e6ebfe46acec8",
+ "ref": "387e166",
"url": "https://github.com/FunkinCrew/hxCodec"
},
{
@@ -85,7 +92,7 @@
"name": "hxcpp-debug-server",
"type": "git",
"dir": "hxcpp-debug-server",
- "ref": "147294123f983e35f50a966741474438069a7a8f",
+ "ref": "1472941",
"url": "https://github.com/FunkinCrew/hxcpp-debugger"
},
{
@@ -97,7 +104,7 @@
"name": "json2object",
"type": "git",
"dir": null,
- "ref": "a8c26f18463c98da32f744c214fe02273e1823fa",
+ "ref": "a8c26f1",
"url": "https://github.com/FunkinCrew/json2object"
},
{
@@ -111,28 +118,28 @@
"name": "mconsole",
"type": "git",
"dir": null,
- "ref": "master",
+ "ref": "06c0499",
"url": "https://github.com/massive-oss/mconsole"
},
{
"name": "mcover",
"type": "git",
"dir": "src",
- "ref": "master",
+ "ref": "c3c47cd",
"url": "https://github.com/massive-oss/mcover"
},
{
"name": "mockatoo",
"type": "git",
"dir": "src",
- "ref": "master",
+ "ref": "13d77a0",
"url": "https://github.com/FunkinCrew/mockatoo"
},
{
"name": "munit",
"type": "git",
"dir": "src",
- "ref": "master",
+ "ref": "f61be7f",
"url": "https://github.com/FunkinCrew/MassiveUnit"
},
{
diff --git a/source/Main.hx b/source/Main.hx
index f4c5d9eb2..35d7bf602 100644
--- a/source/Main.hx
+++ b/source/Main.hx
@@ -113,6 +113,8 @@ class Main extends Sprite
addChild(game);
+ addChild(fpsCounter);
+
#if hxcpp_debug_server
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
#else
@@ -128,6 +130,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/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();
diff --git a/source/funkin/audio/visualize/ABotVis.hx b/source/funkin/audio/visualize/ABotVis.hx
index 89b004df4..5874b8921 100644
--- a/source/funkin/audio/visualize/ABotVis.hx
+++ b/source/funkin/audio/visualize/ABotVis.hx
@@ -8,153 +8,180 @@ import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
import flixel.math.FlxMath;
import flixel.sound.FlxSound;
import funkin.util.MathUtil;
+import funkVis.dsp.SpectralAnalyzer;
+import funkVis.audioclip.frontends.LimeAudioClip;
using Lambda;
class ABotVis extends FlxTypedSpriteGroup
{
- public var vis:VisShit;
+ // public var vis:VisShit;
+ var analyzer:SpectralAnalyzer;
var volumes:Array = [];
+ public var snd:FlxSound;
+
public function new(snd:FlxSound)
{
super();
- vis = new VisShit(snd);
+ this.snd = snd;
+
+ // vis = new VisShit(snd);
// vis.snd = snd;
var visFrms:FlxAtlasFrames = Paths.getSparrowAtlas('aBotViz');
+ // these are the differences in X position, from left to right
+ var positionX:Array = [0, 59, 56, 66, 54, 52, 51];
+ var positionY:Array = [0, -8, -3.5, -0.4, 0.5, 4.7, 7];
+
for (lol in 1...8)
{
// pushes initial value
volumes.push(0.0);
+ var sum = function(num:Float, total:Float) return total += num;
+ var posX:Float = positionX.slice(0, lol).fold(sum, 0);
+ var posY:Float = positionY.slice(0, lol).fold(sum, 0);
- var viz:FlxSprite = new FlxSprite(50 * lol, 0);
+ var viz:FlxSprite = new FlxSprite(posX, posY);
viz.frames = visFrms;
add(viz);
- var visStr = 'VIZ';
- if (lol == 5) visStr = 'viz'; // lol makes it lowercase, accomodates for art that I dont wanna rename!
-
+ var visStr = 'viz';
viz.animation.addByPrefix('VIZ', visStr + lol, 0);
- viz.animation.play('VIZ', false, false, -1);
+ viz.animation.play('VIZ', false, false, 2);
}
}
+ public function initAnalyzer()
+ {
+ @:privateAccess
+ analyzer = new SpectralAnalyzer(7, new LimeAudioClip(cast snd._channel.__source), 0.01, 30);
+ analyzer.maxDb = -35;
+ // analyzer.fftN = 2048;
+ }
+
+ var visTimer:Float = -1;
+ var visTimeMax:Float = 1 / 30;
+
override function update(elapsed:Float)
{
// updateViz();
- updateFFT(elapsed);
+ // updateFFT(elapsed);
+ //
super.update(elapsed);
}
- function updateFFT(elapsed:Float)
+ static inline function min(x:Int, y:Int):Int
{
- if (vis.snd != null)
+ return x > y ? y : x;
+ }
+
+ override function draw()
+ {
+ #if web
+ if (analyzer != null) drawFFT();
+ #end
+ super.draw();
+ }
+
+ /**
+ * TJW funkVis based visualizer! updateFFT() is the old nasty shit that dont worky!
+ */
+ function drawFFT():Void
+ {
+ var levels = analyzer.getLevels(false);
+
+ for (i in 0...min(group.members.length, levels.length))
{
- vis.checkAndSetBuffer();
+ var animFrame:Int = Math.round(levels[i].value * 5);
- if (vis.setBuffer)
- {
- var remappedShit:Int = 0;
+ animFrame = Math.floor(Math.min(5, animFrame));
+ animFrame = Math.floor(Math.max(0, animFrame));
- if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, vis.numSamples));
- else
- remappedShit = Std.int(FlxMath.remapToRange(Conductor.instance.songPosition, 0, vis.snd.length, 0, vis.numSamples));
+ animFrame = Std.int(Math.abs(animFrame - 5)); // shitty dumbass flip, cuz dave got da shit backwards lol!
- var fftSamples:Array = [];
-
- var swagBucks = remappedShit;
-
- for (i in remappedShit...remappedShit + (Std.int((44100 * (1 / 144)))))
- {
- var left = vis.audioData[swagBucks] / 32767;
- var right = vis.audioData[swagBucks + 1] / 32767;
-
- var balanced = (left + right) / 2;
-
- swagBucks += 2;
-
- fftSamples.push(balanced);
- }
-
- var freqShit = vis.funnyFFT(fftSamples);
-
- for (i in 0...group.members.length)
- {
- var getSliceShit = function(s:Int) {
- var powShit = FlxMath.remapToRange(s, 0, group.members.length, 0, MathUtil.logBase(10, freqShit[0].length));
- return Math.round(Math.pow(10, powShit));
- };
-
- // var powShit:Float = getSliceShit(i);
- var hzSliced:Int = getSliceShit(i);
-
- var sliceLength:Int = Std.int(freqShit[0].length / group.members.length);
-
- var volSlice = freqShit[0].slice(hzSliced, getSliceShit(i + 1));
-
- var avgVel:Float = 0;
-
- for (slice in volSlice)
- {
- avgVel += slice;
- }
-
- avgVel /= volSlice.length;
-
- avgVel *= 10000000;
-
- volumes[i] += avgVel - (elapsed * (volumes[i] * 50));
-
- var animFrame:Int = Std.int(volumes[i]);
-
- animFrame = Math.floor(Math.min(5, animFrame));
- animFrame = Math.floor(Math.max(0, animFrame));
-
- animFrame = Std.int(Math.abs(animFrame - 5)); // shitty dumbass flip, cuz dave got da shit backwards lol!
-
- group.members[i].animation.curAnim.curFrame = animFrame;
- if (FlxG.keys.justPressed.U)
- {
- trace(avgVel);
- trace(group.members[i].animation.curAnim.curFrame);
- }
- }
-
- // group.members[0].animation.curAnim.curFrame =
- }
+ group.members[i].animation.curAnim.curFrame = animFrame;
}
}
- public function updateViz()
- {
- if (vis.snd != null)
- {
- var remappedShit:Int = 0;
- vis.checkAndSetBuffer();
-
- if (vis.setBuffer)
- {
- // var startingSample:Int = Std.int(FlxMath.remapToRange)
-
- if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, vis.numSamples));
-
- for (i in 0...group.members.length)
- {
- var sampleApprox:Int = Std.int(FlxMath.remapToRange(i, 0, group.members.length, remappedShit, remappedShit + 500));
-
- var left = vis.audioData[sampleApprox] / 32767;
-
- var animFrame:Int = Std.int(FlxMath.remapToRange(left, -1, 1, 0, 6));
-
- group.members[i].animation.curAnim.curFrame = animFrame;
- }
- }
- }
- }
+ // function updateFFT(elapsed:Float)
+ // {
+ // if (vis.snd != null)
+ // {
+ // vis.checkAndSetBuffer();
+ // if (vis.setBuffer)
+ // {
+ // var remappedShit:Int = 0;
+ // if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, vis.numSamples));
+ // else
+ // remappedShit = Std.int(FlxMath.remapToRange(Conductor.instance.songPosition, 0, vis.snd.length, 0, vis.numSamples));
+ // var fftSamples:Array = [];
+ // var swagBucks = remappedShit;
+ // for (i in remappedShit...remappedShit + (Std.int((44100 * (1 / 144)))))
+ // {
+ // var left = vis.audioData[swagBucks] / 32767;
+ // var right = vis.audioData[swagBucks + 1] / 32767;
+ // var balanced = (left + right) / 2;
+ // swagBucks += 2;
+ // fftSamples.push(balanced);
+ // }
+ // var freqShit = vis.funnyFFT(fftSamples);
+ // for (i in 0...group.members.length)
+ // {
+ // var getSliceShit = function(s:Int) {
+ // var powShit = FlxMath.remapToRange(s, 0, group.members.length, 0, MathUtil.logBase(10, freqShit[0].length));
+ // return Math.round(Math.pow(10, powShit));
+ // };
+ // // var powShit:Float = getSliceShit(i);
+ // var hzSliced:Int = getSliceShit(i);
+ // var sliceLength:Int = Std.int(freqShit[0].length / group.members.length);
+ // var volSlice = freqShit[0].slice(hzSliced, getSliceShit(i + 1));
+ // var avgVel:Float = 0;
+ // for (slice in volSlice)
+ // {
+ // avgVel += slice;
+ // }
+ // avgVel /= volSlice.length;
+ // avgVel *= 10000000;
+ // volumes[i] += avgVel - (elapsed * (volumes[i] * 50));
+ // var animFrame:Int = Std.int(volumes[i]);
+ // animFrame = Math.floor(Math.min(5, animFrame));
+ // animFrame = Math.floor(Math.max(0, animFrame));
+ // animFrame = Std.int(Math.abs(animFrame - 5)); // shitty dumbass flip, cuz dave got da shit backwards lol!
+ // group.members[i].animation.curAnim.curFrame = animFrame;
+ // if (FlxG.keys.justPressed.U)
+ // {
+ // trace(avgVel);
+ // trace(group.members[i].animation.curAnim.curFrame);
+ // }
+ // }
+ // // group.members[0].animation.curAnim.curFrame =
+ // }
+ // }
+ // }
+ // public function updateViz()
+ // {
+ // if (vis.snd != null)
+ // {
+ // var remappedShit:Int = 0;
+ // vis.checkAndSetBuffer();
+ // if (vis.setBuffer)
+ // {
+ // // var startingSample:Int = Std.int(FlxMath.remapToRange)
+ // if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, vis.numSamples));
+ // for (i in 0...group.members.length)
+ // {
+ // var sampleApprox:Int = Std.int(FlxMath.remapToRange(i, 0, group.members.length, remappedShit, remappedShit + 500));
+ // var left = vis.audioData[sampleApprox] / 32767;
+ // var animFrame:Int = Std.int(FlxMath.remapToRange(left, -1, 1, 0, 6));
+ // group.members[i].animation.curAnim.curFrame = animFrame;
+ // }
+ // }
+ // }
+ // }
}
diff --git a/source/funkin/graphics/FunkinCamera.hx b/source/funkin/graphics/FunkinCamera.hx
index 90861c263..8c8a1c9a7 100644
--- a/source/funkin/graphics/FunkinCamera.hx
+++ b/source/funkin/graphics/FunkinCamera.hx
@@ -47,9 +47,13 @@ class FunkinCamera extends FlxCamera
public var shouldDraw:Bool = true;
- public function new(x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, zoom:Float = 0)
+ // Used to identify the camera during debugging.
+ final id:String = 'unknown';
+
+ public function new(id:String = 'unknown', x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, zoom:Float = 0)
{
super(x, y, width, height, zoom);
+ this.id = id;
bgTexture = pickTexture(width, height);
bgBitmap = FixedBitmapData.fromTexture(bgTexture);
bgFrame = new FlxFrame(new FlxGraphic('', null));
diff --git a/source/funkin/input/Controls.hx b/source/funkin/input/Controls.hx
index 0857678d0..14b226d88 100644
--- a/source/funkin/input/Controls.hx
+++ b/source/funkin/input/Controls.hx
@@ -30,44 +30,44 @@ 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 _screenshot = new FlxActionDigital(Action.SCREENSHOT);
- var _cutscene_advance = new FlxActionDigital(Action.CUTSCENE_ADVANCE);
- 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);
+ var _ui_left = new FunkinAction(Action.UI_LEFT);
+ var _ui_right = new FunkinAction(Action.UI_RIGHT);
+ var _ui_down = new FunkinAction(Action.UI_DOWN);
+ var _ui_upP = new FunkinAction(Action.UI_UP_P);
+ var _ui_leftP = new FunkinAction(Action.UI_LEFT_P);
+ var _ui_rightP = new FunkinAction(Action.UI_RIGHT_P);
+ var _ui_downP = new FunkinAction(Action.UI_DOWN_P);
+ var _ui_upR = new FunkinAction(Action.UI_UP_R);
+ var _ui_leftR = new FunkinAction(Action.UI_LEFT_R);
+ var _ui_rightR = new FunkinAction(Action.UI_RIGHT_R);
+ var _ui_downR = new FunkinAction(Action.UI_DOWN_R);
+ var _note_up = new FunkinAction(Action.NOTE_UP);
+ var _note_left = new FunkinAction(Action.NOTE_LEFT);
+ var _note_right = new FunkinAction(Action.NOTE_RIGHT);
+ var _note_down = new FunkinAction(Action.NOTE_DOWN);
+ var _note_upP = new FunkinAction(Action.NOTE_UP_P);
+ var _note_leftP = new FunkinAction(Action.NOTE_LEFT_P);
+ var _note_rightP = new FunkinAction(Action.NOTE_RIGHT_P);
+ var _note_downP = new FunkinAction(Action.NOTE_DOWN_P);
+ var _note_upR = new FunkinAction(Action.NOTE_UP_R);
+ var _note_leftR = new FunkinAction(Action.NOTE_LEFT_R);
+ var _note_rightR = new FunkinAction(Action.NOTE_RIGHT_R);
+ var _note_downR = new FunkinAction(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 _screenshot = new FunkinAction(Action.SCREENSHOT);
+ var _cutscene_advance = new FunkinAction(Action.CUTSCENE_ADVANCE);
+ 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);
- var byName:Map = new Map();
+ var byName:Map = new Map();
public var gamepadsAdded:Array = [];
public var keyboardScheme = KeyboardScheme.None;
@@ -75,122 +75,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;
@@ -260,26 +280,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);
@@ -293,8 +297,16 @@ class Controls extends FlxActionSet
add(_volume_down);
add(_volume_mute);
- 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;
@@ -307,14 +319,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 {
@@ -405,36 +420,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:
@@ -1042,6 +1057,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();
@@ -1229,8 +1411,7 @@ enum Control
DEBUG_STAGE;
}
-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/play/PlayState.hx b/source/funkin/play/PlayState.hx
index 3342a6b69..572eb6135 100644
--- a/source/funkin/play/PlayState.hx
+++ b/source/funkin/play/PlayState.hx
@@ -498,7 +498,7 @@ class PlayState extends MusicBeatSubState
/**
* The combo popups. Includes the real-time combo counter and the rating.
*/
- var comboPopUps:PopUpStuff;
+ public var comboPopUps:PopUpStuff;
/**
* PROPERTIES
@@ -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;
@@ -1467,7 +1470,7 @@ class PlayState extends MusicBeatSubState
*/
function initCameras():Void
{
- camGame = new FunkinCamera();
+ camGame = new FunkinCamera('playStateCamGame');
camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage.
camHUD = new FlxCamera();
camHUD.bgColor.alpha = 0; // Show the game scene behind the camera.
@@ -1548,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);
@@ -1915,8 +1919,6 @@ class PlayState extends MusicBeatSubState
*/
function startSong():Void
{
- dispatchEvent(new ScriptEvent(SONG_START));
-
startingSong = false;
if (!overrideMusic && !isGamePaused && currentChart != null)
@@ -1938,7 +1940,7 @@ class PlayState extends MusicBeatSubState
// Prevent the volume from being wrong.
FlxG.sound.music.volume = 1.0;
- FlxG.sound.music.fadeTween?.cancel();
+ if (FlxG.sound.music.fadeTween != null) FlxG.sound.music.fadeTween.cancel();
trace('Playing vocals...');
add(vocals);
@@ -1957,6 +1959,8 @@ class PlayState extends MusicBeatSubState
// FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
handleSkippedNotes();
}
+
+ dispatchEvent(new ScriptEvent(SONG_START));
}
/**
diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx
index c05257338..acce8645a 100644
--- a/source/funkin/play/ResultState.hx
+++ b/source/funkin/play/ResultState.hx
@@ -64,6 +64,9 @@ class ResultState extends MusicBeatSubState
loop: resultsVariation != SHIT
});
+ // Reset the camera zoom on the results screen.
+ FlxG.camera.zoom = 1.0;
+
// TEMP-ish, just used to sorta "cache" the 3000x3000 image!
var cacheBullShit:FlxSprite = new FlxSprite().loadGraphic(Paths.image("resultScreen/soundSystem"));
add(cacheBullShit);
@@ -80,34 +83,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 +271,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 +295,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("");
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);
diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx
index 724bf0cb9..b062b22cf 100644
--- a/source/funkin/play/components/PopUpStuff.hx
+++ b/source/funkin/play/components/PopUpStuff.hx
@@ -10,6 +10,8 @@ import funkin.util.TimerUtil;
class PopUpStuff extends FlxTypedGroup
{
+ public var offsets:Array = [0, 0];
+
override public function new()
{
super();
@@ -29,9 +31,9 @@ class PopUpStuff extends FlxTypedGroup
rating.scrollFactor.set(0.2, 0.2);
rating.zIndex = 1000;
- rating.x = FlxG.width * 0.50;
+ rating.x = (FlxG.width * 0.474) + offsets[0];
// rating.x -= FlxG.camera.scroll.x * 0.2;
- rating.y = FlxG.camera.height * 0.4 - 60;
+ rating.y = (FlxG.camera.height * 0.45 - 60) + offsets[1];
rating.acceleration.y = 550;
rating.velocity.y -= FlxG.random.int(140, 175);
rating.velocity.x -= FlxG.random.int(0, 10);
@@ -40,16 +42,19 @@ class PopUpStuff extends FlxTypedGroup
if (PlayState.instance.currentStageId.startsWith('school'))
{
- rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7));
+ rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.65));
rating.antialiasing = false;
}
else
{
- rating.setGraphicSize(Std.int(rating.width * 0.7));
+ rating.setGraphicSize(Std.int(rating.width * 0.65));
rating.antialiasing = true;
}
rating.updateHitbox();
+ rating.x -= rating.width / 2;
+ rating.y -= rating.height / 2;
+
FlxTween.tween(rating, {alpha: 0}, 0.2,
{
onComplete: function(tween:FlxTween) {
@@ -77,8 +82,8 @@ class PopUpStuff extends FlxTypedGroup
pixelShitPart2 = '-pixel';
}
var comboSpr:FunkinSprite = FunkinSprite.create(pixelShitPart1 + 'combo' + pixelShitPart2);
- comboSpr.y = FlxG.camera.height * 0.4 + 80;
- comboSpr.x = FlxG.width * 0.50;
+ comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1];
+ comboSpr.x = (FlxG.width * 0.507) + offsets[0];
// comboSpr.x -= FlxG.camera.scroll.x * 0.2;
comboSpr.acceleration.y = 600;
@@ -133,14 +138,14 @@ class PopUpStuff extends FlxTypedGroup
}
else
{
- numScore.setGraphicSize(Std.int(numScore.width * 0.5));
+ numScore.setGraphicSize(Std.int(numScore.width * 0.45));
numScore.antialiasing = true;
}
numScore.updateHitbox();
- numScore.x = comboSpr.x - (43 * daLoop); //- 90;
- numScore.acceleration.y = FlxG.random.int(200, 300);
- numScore.velocity.y -= FlxG.random.int(140, 160);
+ numScore.x = comboSpr.x - (36 * daLoop) - 65; //- 90;
+ numScore.acceleration.y = FlxG.random.int(250, 300);
+ numScore.velocity.y -= FlxG.random.int(130, 150);
numScore.velocity.x = FlxG.random.float(-5, 5);
add(numScore);
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index dba1a7e55..93907bdda 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -8,6 +8,7 @@ import flixel.FlxSprite;
import flixel.FlxSubState;
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;
@@ -40,6 +41,8 @@ import funkin.data.stage.StageData;
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;
@@ -74,6 +77,7 @@ 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.ChartEditorMeasureTicks;
import funkin.ui.debug.charting.components.ChartEditorNotePreview;
import funkin.ui.debug.charting.components.ChartEditorNoteSprite;
import funkin.ui.debug.charting.components.ChartEditorPlaybarHead;
@@ -401,8 +405,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;
@@ -463,6 +470,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Move the playhead sprite to the correct position.
gridPlayhead.y = this.playheadPositionInPixels + GRID_INITIAL_Y_POS;
+ updatePlayheadGhostHoldNotes();
+ refreshNotePreviewPlayheadPosition();
+
return this.playheadPositionInPixels;
}
@@ -769,6 +779,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
return currentPlaceNoteData = value;
}
+ /**
+ * 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
/**
@@ -799,6 +816,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.
*/
@@ -1071,6 +1094,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
*/
@@ -1949,10 +2032,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.
*/
@@ -1970,6 +2058,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.
@@ -2091,7 +2185,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
loadPreferences();
- uiCamera = new FunkinCamera();
+ uiCamera = new FunkinCamera('chartEditorUI');
FlxG.cameras.reset(uiCamera);
buildDefaultSongData();
@@ -2349,7 +2443,7 @@ 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;
@@ -2423,6 +2517,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());
}
@@ -2519,6 +2622,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.
*/
@@ -3015,6 +3125,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
*/
function setupTurboKeyHandlers():Void
{
+ // Keyboard shortcuts
add(undoKeyHandler);
add(redoKeyHandler);
add(upKeyHandler);
@@ -3023,6 +3134,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);
}
/**
@@ -3709,32 +3834,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;
@@ -3744,20 +3893,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;
@@ -3767,26 +3933,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.
@@ -4380,9 +4566,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
}
gridGhostHoldNote.visible = true;
- gridGhostHoldNote.noteData = currentPlaceNoteData;
- gridGhostHoldNote.noteDirection = currentPlaceNoteData.getDirection();
-
+ gridGhostHoldNote.noteData = gridGhostNote.noteData;
+ gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection();
gridGhostHoldNote.setHeightDirectly(dragLengthPixels, true);
gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes);
@@ -4943,37 +5128,57 @@ 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.WASDKeys:
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.
}
+
+ updatePlayheadGhostHoldNotes();
}
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));
@@ -4984,14 +5189,136 @@ 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)
{
+ trace('Placing note. ${column}');
var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, noteKindToPlace);
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
+ currentLiveInputPlaceNoteData[column] = newNoteData;
+ }
+ else if (removeNoteInstead)
+ {
+ 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
+ {
+ // Ensure all the ghost hold notes exist.
+ while (gridPlayheadGhostHoldNotes.length < (STRUMLINE_SIZE * 2))
+ {
+ 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[column] = 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[column] != targetNoteLengthStepsInt)
+ {
+ stretchySounds = !stretchySounds;
+ this.playSound(Paths.sound('chartingSounds/stretch' + (stretchySounds ? '1' : '2') + '_UI'));
+ playheadDragLengthCurrent[column] = targetNoteLengthStepsInt;
+ }
+ ghostHold.visible = true;
+ 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[column] = 0;
+ continue;
+ }
+ }
+ }
+
+ 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. ${column}');
+ currentLiveInputPlaceNoteData[column] = null;
+ gridPlayheadGhostHoldNotes[column].noteData = null;
+ }
+ else
+ {
+ // Extend the note to the playhead position.
+ trace('Extending note. ${column}');
+ this.playSound(Paths.sound('chartingSounds/stretchSNAP_UI'));
+ performCommand(new ExtendNoteLengthCommand(currentLiveInputPlaceNoteData[column], newNoteLength));
+ currentLiveInputPlaceNoteData[column] = null;
+ gridPlayheadGhostHoldNotes[column].noteData = null;
}
}
@@ -5382,7 +5709,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Kill and replace the UI camera so it doesn't get destroyed during the state transition.
uiCamera.kill();
FlxG.cameras.remove(uiCamera, false);
- FlxG.cameras.reset(new FunkinCamera());
+ FlxG.cameras.reset(new FunkinCamera('chartEditorUI2'));
this.persistentUpdate = false;
this.persistentDraw = false;
diff --git a/source/funkin/ui/debug/charting/commands/RemoveEventsCommand.hx b/source/funkin/ui/debug/charting/commands/RemoveEventsCommand.hx
index b4d913607..30f4280d2 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 69317aff4..1cc61f233 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 4811f831d..18ad6e04d 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 c7f7747c0..aeb6dd0e4 100644
--- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx
+++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx
@@ -54,11 +54,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..70383d3fd
--- /dev/null
+++ b/source/funkin/ui/debug/charting/handlers/ChartEditorGamepadHandler.hx
@@ -0,0 +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
+
+@:access(funkin.ui.debug.charting.ChartEditorState)
+class ChartEditorGamepadHandler
+{
+ public static function handleGamepadControls(chartEditorState:ChartEditorState)
+ {
+ if (FlxG.gamepads.firstActive != null) handleGamepad(chartEditorState, FlxG.gamepads.firstActive);
+ }
+
+ /**
+ * 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)
+ {
+ ChartEditorGamepadActionInputSource.instance.handleGamepad(gamepad);
+ }
+ else
+ {
+ 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 (gamepad.justPressed.GUIDE)
+ {
+ trace('Gamepad: Guide pressed, quitting chart editor.');
+ chartEditorState.quitChartEditor();
+ }
+ }
+
+ 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))
+ {
+ 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 (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/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;
diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx
index 3ac441212..0bafa02ed 100644
--- a/source/funkin/ui/freeplay/FreeplayState.hx
+++ b/source/funkin/ui/freeplay/FreeplayState.hx
@@ -517,7 +517,7 @@ class FreeplayState extends MusicBeatSubState
// var swag:Alphabet = new Alphabet(1, 0, 'swag');
- var funnyCam:FunkinCamera = new FunkinCamera(0, 0, FlxG.width, FlxG.height);
+ var funnyCam:FunkinCamera = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height);
funnyCam.bgColor = FlxColor.TRANSPARENT;
FlxG.cameras.add(funnyCam);
diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx
index f8b3d7ac3..f6d85e56e 100644
--- a/source/funkin/ui/freeplay/SongMenuItem.hx
+++ b/source/funkin/ui/freeplay/SongMenuItem.hx
@@ -46,7 +46,7 @@ class SongMenuItem extends FlxSpriteGroup
public var hsvShader(default, set):HSVShader;
- var diffRatingSprite:FlxSprite;
+ // var diffRatingSprite:FlxSprite;
public function new(x:Float, y:Float)
{
@@ -65,13 +65,13 @@ class SongMenuItem extends FlxSpriteGroup
var rank:String = FlxG.random.getObject(ranks);
ranking = new FlxSprite(capsule.width * 0.84, 30);
- ranking.loadGraphic(Paths.image('freeplay/ranks/' + rank));
- ranking.scale.x = ranking.scale.y = realScaled;
+ // ranking.loadGraphic(Paths.image('freeplay/ranks/' + rank));
+ // ranking.scale.x = ranking.scale.y = realScaled;
// ranking.alpha = 0.75;
- ranking.visible = false;
- ranking.origin.set(capsule.origin.x - ranking.x, capsule.origin.y - ranking.y);
- add(ranking);
- grpHide.add(ranking);
+ // ranking.visible = false;
+ // ranking.origin.set(capsule.origin.x - ranking.x, capsule.origin.y - ranking.y);
+ // add(ranking);
+ // grpHide.add(ranking);
switch (rank)
{
@@ -81,9 +81,9 @@ class SongMenuItem extends FlxSpriteGroup
grayscaleShader = new Grayscale(1);
- diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image('freeplay/diffRatings/diff00'));
- diffRatingSprite.shader = grayscaleShader;
- diffRatingSprite.origin.set(capsule.origin.x - diffRatingSprite.x, capsule.origin.y - diffRatingSprite.y);
+ // diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image('freeplay/diffRatings/diff00'));
+ // diffRatingSprite.shader = grayscaleShader;
+ // diffRatingSprite.origin.set(capsule.origin.x - diffRatingSprite.x, capsule.origin.y - diffRatingSprite.y);
// TODO: Readd once ratings are fully implemented
// add(diffRatingSprite);
// grpHide.add(diffRatingSprite);
@@ -118,8 +118,8 @@ class SongMenuItem extends FlxSpriteGroup
function updateDifficultyRating(newRating:Int):Void
{
var ratingPadded:String = newRating < 10 ? '0$newRating' : '$newRating';
- diffRatingSprite.loadGraphic(Paths.image('freeplay/diffRatings/diff${ratingPadded}'));
- diffRatingSprite.visible = false;
+ // diffRatingSprite.loadGraphic(Paths.image('freeplay/diffRatings/diff${ratingPadded}'));
+ // diffRatingSprite.visible = false;
}
function set_hsvShader(value:HSVShader):HSVShader
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);
+ }
+}
diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx
index 1a4686c5b..f053d39fe 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
@@ -178,7 +172,7 @@ class MainMenuState extends MusicBeatState
function resetCamStuff()
{
- FlxG.cameras.reset(new FunkinCamera());
+ FlxG.cameras.reset(new FunkinCamera('mainMenu'));
FlxG.camera.follow(camFollow, null, 0.06);
FlxG.camera.snapToTarget();
}
@@ -334,6 +328,31 @@ class MainMenuState extends MusicBeatState
FlxG.state.openSubState(new DebugMenuSubState());
}
+ #if (debug || FORCE_DEBUG_VERSION)
+ if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W)
+ {
+ // Give the user a score of 1 point on Weekend 1 story mode.
+ // This makes the level count as cleared and displays the songs in Freeplay.
+ funkin.save.Save.instance.setLevelScore('weekend1', 'easy',
+ {
+ score: 1,
+ tallies:
+ {
+ sick: 0,
+ good: 0,
+ bad: 0,
+ shit: 0,
+ missed: 0,
+ combo: 0,
+ maxCombo: 0,
+ totalNotesHit: 0,
+ totalNotes: 0,
+ },
+ accuracy: 0,
+ });
+ }
+ #end
+
if (FlxG.sound.music.volume < 0.8)
{
FlxG.sound.music.volume += 0.5 * elapsed;
diff --git a/source/funkin/ui/options/ControlsMenu.hx b/source/funkin/ui/options/ControlsMenu.hx
index 62ae4b1a9..dd7d5ff38 100644
--- a/source/funkin/ui/options/ControlsMenu.hx
+++ b/source/funkin/ui/options/ControlsMenu.hx
@@ -48,7 +48,7 @@ class ControlsMenu extends funkin.ui.options.OptionsState.Page
{
super();
- menuCamera = new FunkinCamera();
+ menuCamera = new FunkinCamera('controlsMenu');
FlxG.cameras.add(menuCamera, false);
menuCamera.bgColor = 0x0;
camera = menuCamera;
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();
diff --git a/source/funkin/ui/options/PreferencesMenu.hx b/source/funkin/ui/options/PreferencesMenu.hx
index c23c3f165..783aef0ba 100644
--- a/source/funkin/ui/options/PreferencesMenu.hx
+++ b/source/funkin/ui/options/PreferencesMenu.hx
@@ -21,7 +21,7 @@ class PreferencesMenu extends Page
{
super();
- menuCamera = new FunkinCamera();
+ menuCamera = new FunkinCamera('prefMenu');
FlxG.cameras.add(menuCamera, false);
menuCamera.bgColor = 0x0;
camera = menuCamera;
diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx
index 5881ef624..d4ffa961d 100644
--- a/source/funkin/ui/story/StoryMenuState.hx
+++ b/source/funkin/ui/story/StoryMenuState.hx
@@ -344,6 +344,17 @@ class StoryMenuState extends MusicBeatState
changeDifficulty(0);
}
+ // TODO: Querying UI_RIGHT_P (justPressed) after UI_RIGHT always returns false. Fix it!
+ if (controls.UI_RIGHT_P)
+ {
+ changeDifficulty(1);
+ }
+
+ if (controls.UI_LEFT_P)
+ {
+ changeDifficulty(-1);
+ }
+
if (controls.UI_RIGHT)
{
rightDifficultyArrow.animation.play('press');
@@ -362,16 +373,6 @@ class StoryMenuState extends MusicBeatState
leftDifficultyArrow.animation.play('idle');
}
- if (controls.UI_RIGHT_P)
- {
- changeDifficulty(1);
- }
-
- if (controls.UI_LEFT_P)
- {
- changeDifficulty(-1);
- }
-
if (FlxG.keys.justPressed.TAB && modeText.visible)
{
switchMode(!displayingModdedLevels);
diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx
index e76e66003..d122bf7c1 100644
--- a/source/funkin/ui/title/TitleState.hx
+++ b/source/funkin/ui/title/TitleState.hx
@@ -188,6 +188,7 @@ class TitleState extends MusicBeatState
ngSpr.animation.add('idle', [0, 1], 4);
ngSpr.animation.play('idle');
ngSpr.setGraphicSize(Std.int(ngSpr.width * 0.55));
+ ngSpr.y += 25;
}
else
{
diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx
index 0b5e16f97..e5abef872 100644
--- a/source/funkin/ui/transition/StickerSubState.hx
+++ b/source/funkin/ui/transition/StickerSubState.hx
@@ -247,10 +247,6 @@ class StickerSubState extends MusicBeatSubState
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
- // TODO: Rework this asset caching stuff
- FunkinSprite.preparePurgeCache();
- FunkinSprite.purgeCache();
-
// I think this grabs the screen and puts it under the stickers?
// Leaving this commented out rather than stripping it out because it's cool...
/*
@@ -265,7 +261,15 @@ class StickerSubState extends MusicBeatSubState
// FlxG.addChildBelowMouse(dipshit);
*/
- FlxG.switchState(() -> targetState(this));
+ FlxG.switchState(() -> {
+ // TODO: Rework this asset caching stuff
+ // NOTE: This has to come AFTER the state switch,
+ // otherwise the game tries to render destroyed sprites!
+ FunkinSprite.preparePurgeCache();
+ FunkinSprite.purgeCache();
+
+ return targetState(this);
+ });
}
});
});
diff --git a/source/funkin/util/FlxTweenUtil.hx b/source/funkin/util/FlxTweenUtil.hx
new file mode 100644
index 000000000..57c8f0cfe
--- /dev/null
+++ b/source/funkin/util/FlxTweenUtil.hx
@@ -0,0 +1,36 @@
+package funkin.util;
+
+import flixel.addons.plugin.taskManager.FlxTask;
+import flixel.tweens.FlxTween;
+import flixel.tweens.FlxEase;
+
+class FlxTweenUtil
+{
+ public static function pauseTween(tween:FlxTween):Void
+ {
+ if (tween != null)
+ {
+ tween.active = false;
+ }
+ }
+
+ public static function resumeTween(tween:FlxTween):Void
+ {
+ if (tween != null)
+ {
+ tween.active = true;
+ }
+ }
+
+ public static function pauseTweensOf(Object:Dynamic, ?FieldPaths:Array):Void
+ {
+ @:privateAccess
+ FlxTween.globalManager.forEachTweensOf(Object, FieldPaths, pauseTween);
+ }
+
+ public static function resumeTweensOf(Object:Dynamic, ?FieldPaths:Array):Void
+ {
+ @:privateAccess
+ FlxTween.globalManager.forEachTweensOf(Object, FieldPaths, resumeTween);
+ }
+}