diff --git a/source/funkin/ComboCounter.hx b/source/funkin/ComboMilestone.hx similarity index 71% rename from source/funkin/ComboCounter.hx rename to source/funkin/ComboMilestone.hx index 28aed4560..b72eda2fa 100644 --- a/source/funkin/ComboCounter.hx +++ b/source/funkin/ComboMilestone.hx @@ -5,14 +5,14 @@ import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import flixel.util.FlxTimer; -class ComboCounter extends FlxTypedSpriteGroup +class ComboMilestone extends FlxTypedSpriteGroup { var effectStuff:FlxSprite; var wasComboSetup:Bool = false; var daCombo:Int = 0; - var grpNumbers:FlxTypedGroup; + var grpNumbers:FlxTypedGroup; var onScreenTime:Float = 0; @@ -23,7 +23,7 @@ class ComboCounter extends FlxTypedSpriteGroup this.daCombo = daCombo; effectStuff = new FlxSprite(0, 0); - effectStuff.frames = Paths.getSparrowAtlas('noteCombo'); + effectStuff.frames = Paths.getSparrowAtlas('comboMilestone'); effectStuff.animation.addByPrefix('funny', 'NOTE COMBO animation', 24, false); effectStuff.animation.play('funny'); effectStuff.antialiasing = true; @@ -33,7 +33,7 @@ class ComboCounter extends FlxTypedSpriteGroup effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7)); add(effectStuff); - grpNumbers = new FlxTypedGroup(); + grpNumbers = new FlxTypedGroup(); // add(grpNumbers); } @@ -41,7 +41,7 @@ class ComboCounter extends FlxTypedSpriteGroup { if (onScreenTime < 0.9) { - new FlxTimer().start((Conductor.crochet / 1000) * 0.25, function(tmr) { + new FlxTimer().start((Conductor.beatLengthMs / 1000) * 0.25, function(tmr) { forceFinish(); }); } @@ -62,14 +62,14 @@ class ComboCounter extends FlxTypedSpriteGroup if (effectStuff.animation.curAnim.curFrame == 18) { - grpNumbers.forEach(function(spr:ComboNumber) { + grpNumbers.forEach(function(spr:ComboMilestoneNumber) { spr.animation.reset(); }); } if (effectStuff.animation.curAnim.curFrame == 20) { - grpNumbers.forEach(function(spr:ComboNumber) { + grpNumbers.forEach(function(spr:ComboMilestoneNumber) { spr.kill(); }); } @@ -86,7 +86,7 @@ class ComboCounter extends FlxTypedSpriteGroup while (daCombo > 0) { - var comboNumber:ComboNumber = new ComboNumber(450 - (100 * loopNum), 20 + 14 * loopNum, daCombo % 10); + var comboNumber:ComboMilestoneNumber = new ComboMilestoneNumber(450 - (100 * loopNum), 20 + 14 * loopNum, daCombo % 10); comboNumber.setGraphicSize(Std.int(comboNumber.width * 0.7)); grpNumbers.add(comboNumber); add(comboNumber); @@ -95,27 +95,17 @@ class ComboCounter extends FlxTypedSpriteGroup daCombo = Math.floor(daCombo / 10); } - - // var comboNumber:ComboNumber = new ComboNumber(420, 0, 0); - - // add to both, in the group just for ez organize/accessing - // grpNumbers.add(comboNumber); - // add(comboNumber); - - // var comboNumber2:ComboNumber = new ComboNumber(420 - 134, 44, 0); - // grpNumbers.add(comboNumber2); - // add(comboNumber2); } } -class ComboNumber extends FlxSprite +class ComboMilestoneNumber extends FlxSprite { public function new(x:Float, y:Float, digit:Int) { super(x - 20, y); var stringNum:String = Std.string(digit); - frames = Paths.getSparrowAtlas('noteComboNumbers'); + frames = Paths.getSparrowAtlas('comboMilestoneNumbers'); animation.addByPrefix(stringNum, stringNum, 24, false); animation.play(stringNum); antialiasing = true; diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx index e31b6d501..7f7e2b356 100644 --- a/source/funkin/Conductor.hx +++ b/source/funkin/Conductor.hx @@ -1,9 +1,8 @@ package funkin; -import flixel.util.FlxSignal; -import funkin.SongLoad.SwagSong; -import funkin.play.song.Song.SongDifficulty; import funkin.play.song.SongData.SongTimeChange; +import flixel.util.FlxSignal; +import funkin.play.song.Song.SongDifficulty; typedef BPMChangeEvent = { @@ -12,18 +11,27 @@ typedef BPMChangeEvent = var bpm:Float; } +/** + * A global source of truth for timing information. + */ class Conductor { - /** - * The list of time changes in the song. - * There should be at least one time change (at the beginning of the song) to define the BPM. - */ - static var timeChanges:Array = []; + static final STEPS_PER_BEAT:Int = 4; - /** - * The current time change. - */ - static var currentTimeChange:SongTimeChange; + // onBeatHit is called every quarter note + // onStepHit is called every sixteenth note + // 4/4 = 4 beats per measure = 16 steps per measure + // 120 BPM = 120 quarter notes per minute = 2 onBeatHit per second + // 120 BPM = 480 sixteenth notes per minute = 8 onStepHit per second + // 60 BPM = 60 quarter notes per minute = 1 onBeatHit per second + // 60 BPM = 240 sixteenth notes per minute = 4 onStepHit per second + // 3/4 = 3 beats per measure = 12 steps per measure + // (IDENTICAL TO 4/4 but shorter measure length) + // 120 BPM = 120 quarter notes per minute = 2 onBeatHit per second + // 120 BPM = 480 sixteenth notes per minute = 8 onStepHit per second + // 60 BPM = 60 quarter notes per minute = 1 onBeatHit per second + // 60 BPM = 240 sixteenth notes per minute = 4 onStepHit per second + // 7/8 = 3.5 beats per measure = 14 steps per measure /** * The current position in the song in milliseconds. @@ -47,8 +55,25 @@ class Conductor static var bpmOverride:Null = null; - // OLD, replaced with timeChanges. - public static var bpmChangeMap:Array = []; + /** + * Current position in the song, in whole measures. + */ + public static var currentMeasure(default, null):Int; + + /** + * Current position in the song, in whole beats. + **/ + public static var currentBeat(default, null):Int; + + /** + * Current position in the song, in whole steps. + */ + public static var currentStep(default, null):Int; + + /** + * Current position in the song, in steps and fractions of a step. + */ + public static var currentStepTime(default, null):Float; /** * Duration of a measure in milliseconds. Calculated based on bpm. @@ -57,29 +82,33 @@ class Conductor static function get_measureLengthMs():Float { - return crochet * timeSignatureNumerator; + return beatLengthMs * timeSignatureNumerator; } /** - * Duration of a beat in millisecond. Calculated based on bpm. + * Duration of a beat (quarter note) in milliseconds. Calculated based on bpm. */ - public static var crochet(get, null):Float; + public static var beatLengthMs(get, null):Float; - static function get_crochet():Float + static function get_beatLengthMs():Float { + // Tied directly to BPM. return ((60 / bpm) * 1000); } /** - * Duration of a step (quarter) in milliseconds. Calculated based on bpm. + * Duration of a step (sixteenth) in milliseconds. Calculated based on bpm. */ - public static var stepCrochet(get, null):Float; + public static var stepLengthMs(get, null):Float; - static function get_stepCrochet():Float + static function get_stepLengthMs():Float { - return crochet / timeSignatureNumerator; + return beatLengthMs / STEPS_PER_BEAT; } + /** + * The numerator of the current time signature (number of notes in a measure) + */ public static var timeSignatureNumerator(get, null):Int; static function get_timeSignatureNumerator():Int @@ -89,6 +118,9 @@ class Conductor return currentTimeChange.timeSignatureNum; } + /** + * The numerator of the current time signature (length of notes in a measure) + */ public static var timeSignatureDenominator(get, null):Int; static function get_timeSignatureDenominator():Int @@ -98,30 +130,57 @@ class Conductor return currentTimeChange.timeSignatureDen; } - /** - * Current position in the song, in beats. - **/ - public static var currentBeat(default, null):Int; - - /** - * Current position in the song, in steps. - */ - public static var currentStep(default, null):Int; - - /** - * Current position in the song, in steps and fractions of a step. - */ - public static var currentStepTime(default, null):Float; - - public static var beatHit(default, null):FlxSignal = new FlxSignal(); - public static var stepHit(default, null):FlxSignal = new FlxSignal(); - - public static var lastSongPos:Float; - public static var visualOffset:Float = 0; - public static var audioOffset:Float = 0; public static var offset:Float = 0; - // TODO: Add code to update this. + // TODO: What's the difference between visualOffset and audioOffset? + public static var visualOffset:Float = 0; + public static var audioOffset:Float = 0; + + // + // Signals + // + + /** + * Signal that is dispatched every measure. + * At 120 BPM 4/4, this is dispatched every 2 seconds. + * At 120 BPM 3/4, this is dispatched every 1.5 seconds. + */ + public static var measureHit(default, null):FlxSignal = new FlxSignal(); + + /** + * Signal that is dispatched every beat. + * At 120 BPM 4/4, this is dispatched every 0.5 seconds. + * At 120 BPM 3/4, this is dispatched every 0.5 seconds. + */ + public static var beatHit(default, null):FlxSignal = new FlxSignal(); + + /** + * Signal that is dispatched when a step is hit. + * At 120 BPM 4/4, this is dispatched every 0.125 seconds. + * At 120 BPM 3/4, this is dispatched every 0.125 seconds. + */ + public static var stepHit(default, null):FlxSignal = new FlxSignal(); + + // + // Internal Variables + // + + /** + * The list of time changes in the song. + * There should be at least one time change (at the beginning of the song) to define the BPM. + */ + static var timeChanges:Array = []; + + /** + * The current time change. + */ + static var currentTimeChange:SongTimeChange; + + public static var lastSongPos:Float; + + /** + * The number of beats (whole notes) in a measure. + */ public static var beatsPerMeasure(get, null):Int; static function get_beatsPerMeasure():Int @@ -129,33 +188,17 @@ class Conductor return timeSignatureNumerator; } + /** + * The number of steps (quarter-notes) in a measure. + */ public static var stepsPerMeasure(get, null):Int; static function get_stepsPerMeasure():Int { - // Is this always x4? + // This is always 4, b return timeSignatureNumerator * 4; } - function new() {} - - public static function getLastBPMChange() - { - var lastChange:BPMChangeEvent = - { - stepTime: 0, - songTime: 0, - bpm: 0 - } - for (i in 0...Conductor.bpmChangeMap.length) - { - if (Conductor.songPosition >= Conductor.bpmChangeMap[i].songTime) lastChange = Conductor.bpmChangeMap[i]; - - if (Conductor.songPosition < Conductor.bpmChangeMap[i].songTime) break; - } - return lastChange; - } - /** * Forcibly defines the current BPM of the song. * Useful for things like the chart editor that need to manipulate BPM in real time. @@ -165,11 +208,16 @@ class Conductor * WARNING: Avoid this for things like setting the BPM of the title screen music, * you should have a metadata file for it instead. */ - public static function forceBPM(?bpm:Float = null) + public static function forceBPM(?bpm:Float = null):Void { - if (bpm != null) trace('[CONDUCTOR] Forcing BPM to ' + bpm); + if (bpm != null) + { + trace('[CONDUCTOR] Forcing BPM to ' + bpm); + } else + { trace('[CONDUCTOR] Resetting BPM to default'); + } Conductor.bpmOverride = bpm; } @@ -180,15 +228,15 @@ class Conductor * @param songPosition The current position in the song in milliseconds. * Leave blank to use the FlxG.sound.music position. */ - public static function update(songPosition:Float = null) + public static function update(songPosition:Float = null):Void { if (songPosition == null) songPosition = (FlxG.sound.music != null) ? FlxG.sound.music.time + Conductor.offset : 0.0; - var oldBeat = currentBeat; - var oldStep = currentStep; + var oldMeasure:Int = currentMeasure; + var oldBeat:Int = currentBeat; + var oldStep:Int = currentStep; Conductor.songPosition = songPosition; - // Conductor.bpm = Conductor.getLastBPMChange().bpm; currentTimeChange = timeChanges[0]; for (i in 0...timeChanges.length) @@ -204,14 +252,14 @@ class Conductor } else if (currentTimeChange != null) { - currentStepTime = (currentTimeChange.beatTime * 4) + (songPosition - currentTimeChange.timeStamp) / stepCrochet; + currentStepTime = (currentTimeChange.beatTime * 4) + (songPosition - currentTimeChange.timeStamp) / stepLengthMs; currentStep = Math.floor(currentStepTime); currentBeat = Math.floor(currentStep / 4); } else { // Assume a constant BPM equal to the forced value. - currentStepTime = (songPosition / stepCrochet); + currentStepTime = (songPosition / stepLengthMs); currentStep = Math.floor(currentStepTime); currentBeat = Math.floor(currentStep / 4); } @@ -226,37 +274,14 @@ class Conductor { beatHit.dispatch(); } - } - @:deprecated // Switch to TimeChanges instead. - public static function mapBPMChanges(song:SwagSong) - { - bpmChangeMap = []; - - var curBPM:Float = song.bpm; - var totalSteps:Int = 0; - var totalPos:Float = 0; - for (i in 0...SongLoad.getSong().length) + if (currentMeasure != oldMeasure) { - if (SongLoad.getSong()[i].changeBPM && SongLoad.getSong()[i].bpm != curBPM) - { - curBPM = SongLoad.getSong()[i].bpm; - var event:BPMChangeEvent = - { - stepTime: totalSteps, - songTime: totalPos, - bpm: curBPM - }; - bpmChangeMap.push(event); - } - - var deltaSteps:Int = SongLoad.getSong()[i].lengthInSteps; - totalSteps += deltaSteps; - totalPos += ((60 / curBPM) * 1000 / 4) * deltaSteps; + measureHit.dispatch(); } } - public static function mapTimeChanges(songTimeChanges:Array) + public static function mapTimeChanges(songTimeChanges:Array):Void { timeChanges = []; @@ -278,7 +303,7 @@ class Conductor if (timeChanges.length == 0) { // Assume a constant BPM equal to the forced value. - return Math.floor(ms / stepCrochet); + return Math.floor(ms / stepLengthMs); } else { @@ -299,7 +324,7 @@ class Conductor } } - resultStep += Math.floor((ms - lastTimeChange.timeStamp) / stepCrochet); + resultStep += Math.floor((ms - lastTimeChange.timeStamp) / stepLengthMs); return resultStep; } diff --git a/source/funkin/Controls.hx b/source/funkin/Controls.hx index 243570c27..46681adbd 100644 --- a/source/funkin/Controls.hx +++ b/source/funkin/Controls.hx @@ -41,12 +41,18 @@ enum Control ACCEPT; BACK; PAUSE; + CUTSCENE_ADVANCE; + CUTSCENE_SKIP; + VOLUME_UP; + VOLUME_DOWN; + VOLUME_MUTE; #if CAN_CHEAT CHEAT; #end } -enum abstract Action(String) to String from String +@:enum +abstract Action(String) to String from String { var UI_UP = "ui_up"; var UI_LEFT = "ui_left"; @@ -75,6 +81,11 @@ enum abstract Action(String) to String from String var ACCEPT = "accept"; var BACK = "back"; var PAUSE = "pause"; + var CUTSCENE_ADVANCE = "cutscene_advance"; + var CUTSCENE_SKIP = "cutscene_skip"; + var VOLUME_UP = "volume_up"; + var VOLUME_DOWN = "volume_down"; + var VOLUME_MUTE = "volume_mute"; var RESET = "reset"; #if CAN_CHEAT var CHEAT = "cheat"; @@ -129,6 +140,11 @@ class Controls extends FlxActionSet 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 _volume_up = new FlxActionDigital(Action.VOLUME_UP); + var _volume_down = new FlxActionDigital(Action.VOLUME_DOWN); + var _volume_mute = new FlxActionDigital(Action.VOLUME_MUTE); #if CAN_CHEAT var _cheat = new FlxActionDigital(Action.CHEAT); #end @@ -273,6 +289,31 @@ class Controls extends FlxActionSet inline function get_PAUSE() return _pause.check(); + public var CUTSCENE_ADVANCE(get, never):Bool; + + inline function get_CUTSCENE_ADVANCE() + return _cutscene_advance.check(); + + public var CUTSCENE_SKIP(get, never):Bool; + + inline function get_CUTSCENE_SKIP() + return _cutscene_skip.check(); + + public var VOLUME_UP(get, never):Bool; + + inline function get_VOLUME_UP() + return _volume_up.check(); + + public var VOLUME_DOWN(get, never):Bool; + + inline function get_VOLUME_DOWN() + return _volume_down.check(); + + public var VOLUME_MUTE(get, never):Bool; + + inline function get_VOLUME_MUTE() + return _volume_mute.check(); + public var RESET(get, never):Bool; inline function get_RESET() @@ -316,6 +357,11 @@ class Controls extends FlxActionSet add(_accept); add(_back); add(_pause); + add(_cutscene_advance); + add(_cutscene_skip); + add(_volume_up); + add(_volume_down); + add(_volume_mute); add(_reset); #if CAN_CHEAT add(_cheat); @@ -377,6 +423,11 @@ class Controls extends FlxActionSet case BACK: _back; case PAUSE: _pause; case RESET: _reset; + case CUTSCENE_ADVANCE: _cutscene_advance; + case CUTSCENE_SKIP: _cutscene_skip; + case VOLUME_UP: _volume_up; + case VOLUME_DOWN: _volume_down; + case VOLUME_MUTE: _volume_mute; #if CAN_CHEAT case CHEAT: _cheat; #end @@ -437,6 +488,16 @@ class Controls extends FlxActionSet func(_back, JUST_PRESSED); case PAUSE: func(_pause, JUST_PRESSED); + case CUTSCENE_ADVANCE: + func(_cutscene_advance, JUST_PRESSED); + case CUTSCENE_SKIP: + func(_cutscene_skip, PRESSED); + case VOLUME_UP: + func(_volume_up, JUST_PRESSED); + case VOLUME_DOWN: + func(_volume_down, JUST_PRESSED); + case VOLUME_MUTE: + func(_volume_mute, JUST_PRESSED); case RESET: func(_reset, JUST_PRESSED); #if CAN_CHEAT @@ -454,37 +515,70 @@ class Controls extends FlxActionSet switch(device) { case Keys: - forEachBound(control, function(action, _) replaceKey(action, toAdd, toRemove)); + forEachBound(control, function(action, state) replaceKey(action, toAdd, toRemove, state)); case Gamepad(id): - forEachBound(control, function(action, _) replaceButton(action, id, toAdd, toRemove)); + forEachBound(control, function(action, state) replaceButton(action, id, toAdd, toRemove, state)); } } - function replaceKey(action:FlxActionDigital, toAdd:Int, toRemove:Int) + function replaceKey(action:FlxActionDigital, toAdd:FlxKey, toRemove:FlxKey, state:FlxInputState) { + if (action.inputs.length == 0) { + // Add the keybind, don't replace. + addKeys(action, [toAdd], state); + return; + } + + var hasReplaced:Bool = false; for (i in 0...action.inputs.length) { var input = action.inputs[i]; + if (input == null) continue; + if (input.device == KEYBOARD && input.inputID == toRemove) { - @:privateAccess - action.inputs[i].inputID = toAdd; + if (toAdd == FlxKey.NONE) { + // Remove the keybind, don't replace. + action.inputs.remove(input); + } else { + // Replace the keybind. + @:privateAccess + action.inputs[i].inputID = toAdd; + } + hasReplaced = true; } } + + if (!hasReplaced) { + addKeys(action, [toAdd], state); + } } - function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:Int, toRemove:Int) + function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:FlxGamepadInputID, toRemove:FlxGamepadInputID, state:FlxInputState) { + if (action.inputs.length == 0) { + addButtons(action, [toAdd], state, deviceID); + return; + } + + var hasReplaced:Bool = false; for (i in 0...action.inputs.length) { var input = action.inputs[i]; + if (input == null) continue; + if (isGamepad(input, deviceID) && input.inputID == toRemove) { @:privateAccess action.inputs[i].inputID = toAdd; + hasReplaced = true; } } + + if (!hasReplaced) { + addButtons(action, [toAdd], state, deviceID); + } } public function copyFrom(controls:Controls, ?device:Device) @@ -558,10 +652,12 @@ class Controls extends FlxActionSet forEachBound(control, function(action, _) removeKeys(action, keys)); } - inline static function addKeys(action:FlxActionDigital, keys:Array, state:FlxInputState) + static function addKeys(action:FlxActionDigital, keys:Array, state:FlxInputState) { - for (key in keys) + for (key in keys) { + if (key == FlxKey.NONE) continue; // Ignore unbound keys. action.addKey(key, state); + } } static function removeKeys(action:FlxActionDigital, keys:Array) @@ -582,54 +678,95 @@ class Controls extends FlxActionSet keyboardScheme = scheme; - switch(scheme) - { - case Solo: - bindKeys(Control.UI_UP, [W, FlxKey.UP]); - bindKeys(Control.UI_DOWN, [S, FlxKey.DOWN]); - bindKeys(Control.UI_LEFT, [A, FlxKey.LEFT]); - bindKeys(Control.UI_RIGHT, [D, FlxKey.RIGHT]); - bindKeys(Control.NOTE_UP, [W, FlxKey.UP]); - bindKeys(Control.NOTE_DOWN, [S, FlxKey.DOWN]); - bindKeys(Control.NOTE_LEFT, [A, FlxKey.LEFT]); - bindKeys(Control.NOTE_RIGHT, [D, FlxKey.RIGHT]); - bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]); - bindKeys(Control.BACK, [X, BACKSPACE, ESCAPE]); - bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]); - bindKeys(Control.RESET, [R]); - case Duo(true): - bindKeys(Control.UI_UP, [W]); - bindKeys(Control.UI_DOWN, [S]); - bindKeys(Control.UI_LEFT, [A]); - bindKeys(Control.UI_RIGHT, [D]); - bindKeys(Control.NOTE_UP, [W]); - bindKeys(Control.NOTE_DOWN, [S]); - bindKeys(Control.NOTE_LEFT, [A]); - bindKeys(Control.NOTE_RIGHT, [D]); - bindKeys(Control.ACCEPT, [G, Z]); - bindKeys(Control.BACK, [H, X]); - bindKeys(Control.PAUSE, [ONE]); - bindKeys(Control.RESET, [R]); - case Duo(false): - bindKeys(Control.UI_UP, [FlxKey.UP]); - bindKeys(Control.UI_DOWN, [FlxKey.DOWN]); - bindKeys(Control.UI_LEFT, [FlxKey.LEFT]); - bindKeys(Control.UI_RIGHT, [FlxKey.RIGHT]); - bindKeys(Control.NOTE_UP, [FlxKey.UP]); - bindKeys(Control.NOTE_DOWN, [FlxKey.DOWN]); - bindKeys(Control.NOTE_LEFT, [FlxKey.LEFT]); - bindKeys(Control.NOTE_RIGHT, [FlxKey.RIGHT]); - bindKeys(Control.ACCEPT, [O]); - bindKeys(Control.BACK, [P]); - bindKeys(Control.PAUSE, [ENTER]); - bindKeys(Control.RESET, [BACKSPACE]); - case None: // nothing - case Custom: // nothing - } + bindKeys(Control.UI_UP, getDefaultKeybinds(scheme, Control.UI_UP)); + bindKeys(Control.UI_DOWN, getDefaultKeybinds(scheme, Control.UI_DOWN)); + bindKeys(Control.UI_LEFT, getDefaultKeybinds(scheme, Control.UI_LEFT)); + bindKeys(Control.UI_RIGHT, getDefaultKeybinds(scheme, Control.UI_RIGHT)); + bindKeys(Control.NOTE_UP, getDefaultKeybinds(scheme, Control.NOTE_UP)); + bindKeys(Control.NOTE_DOWN, getDefaultKeybinds(scheme, Control.NOTE_DOWN)); + bindKeys(Control.NOTE_LEFT, getDefaultKeybinds(scheme, Control.NOTE_LEFT)); + bindKeys(Control.NOTE_RIGHT, getDefaultKeybinds(scheme, Control.NOTE_RIGHT)); + bindKeys(Control.ACCEPT, getDefaultKeybinds(scheme, Control.ACCEPT)); + bindKeys(Control.BACK, getDefaultKeybinds(scheme, Control.BACK)); + bindKeys(Control.PAUSE, getDefaultKeybinds(scheme, Control.PAUSE)); + bindKeys(Control.CUTSCENE_ADVANCE, getDefaultKeybinds(scheme, Control.CUTSCENE_ADVANCE)); + bindKeys(Control.CUTSCENE_SKIP, getDefaultKeybinds(scheme, Control.CUTSCENE_SKIP)); + bindKeys(Control.VOLUME_UP, getDefaultKeybinds(scheme, Control.VOLUME_UP)); + bindKeys(Control.VOLUME_DOWN, getDefaultKeybinds(scheme, Control.VOLUME_DOWN)); + bindKeys(Control.VOLUME_MUTE, getDefaultKeybinds(scheme, Control.VOLUME_MUTE)); bindMobileLol(); } + function getDefaultKeybinds(scheme:KeyboardScheme, control:Control):Array { + switch (scheme) { + case Solo: + switch (control) { + case Control.UI_UP: return [W, FlxKey.UP]; + case Control.UI_DOWN: return [S, FlxKey.DOWN]; + case Control.UI_LEFT: return [A, FlxKey.LEFT]; + case Control.UI_RIGHT: return [D, FlxKey.RIGHT]; + case Control.NOTE_UP: return [W, FlxKey.UP]; + case Control.NOTE_DOWN: return [S, FlxKey.DOWN]; + case Control.NOTE_LEFT: return [A, FlxKey.LEFT]; + case Control.NOTE_RIGHT: return [D, FlxKey.RIGHT]; + case Control.ACCEPT: return [Z, SPACE, ENTER]; + case Control.BACK: return [X, BACKSPACE, ESCAPE]; + case Control.PAUSE: return [P, ENTER, ESCAPE]; + case Control.CUTSCENE_ADVANCE: return [Z, ENTER]; + case Control.CUTSCENE_SKIP: return [P, ESCAPE]; + case Control.VOLUME_UP: return [PLUS, NUMPADPLUS]; + case Control.VOLUME_DOWN: return [MINUS, NUMPADMINUS]; + case Control.VOLUME_MUTE: return [ZERO, NUMPADZERO]; + case Control.RESET: return [R]; + } + case Duo(true): + switch (control) { + case Control.UI_UP: return [W]; + case Control.UI_DOWN: return [S]; + case Control.UI_LEFT: return [A]; + case Control.UI_RIGHT: return [D]; + case Control.NOTE_UP: return [W]; + case Control.NOTE_DOWN: return [S]; + case Control.NOTE_LEFT: return [A]; + case Control.NOTE_RIGHT: return [D]; + case Control.ACCEPT: return [G, Z]; + case Control.BACK: return [H, X]; + case Control.PAUSE: return [ONE]; + case Control.CUTSCENE_ADVANCE: return [G, Z]; + case Control.CUTSCENE_SKIP: return [ONE]; + case Control.VOLUME_UP: return [PLUS]; + case Control.VOLUME_DOWN: return [MINUS]; + case Control.VOLUME_MUTE: return [ZERO]; + case Control.RESET: return [R]; + } + case Duo(false): + switch (control) { + case Control.UI_UP: return [FlxKey.UP]; + case Control.UI_DOWN: return [FlxKey.DOWN]; + case Control.UI_LEFT: return [FlxKey.LEFT]; + case Control.UI_RIGHT: return [FlxKey.RIGHT]; + case Control.NOTE_UP: return [FlxKey.UP]; + case Control.NOTE_DOWN: return [FlxKey.DOWN]; + case Control.NOTE_LEFT: return [FlxKey.LEFT]; + case Control.NOTE_RIGHT: return [FlxKey.RIGHT]; + case Control.ACCEPT: return [ENTER]; + case Control.BACK: return [ESCAPE]; + case Control.PAUSE: return [ONE]; + case Control.CUTSCENE_ADVANCE: return [ENTER]; + case Control.CUTSCENE_SKIP: return [ONE]; + case Control.VOLUME_UP: return [NUMPADPLUS]; + case Control.VOLUME_DOWN: return [NUMPADMINUS]; + case Control.VOLUME_MUTE: return [NUMPADZERO]; + case Control.RESET: return [R]; + } + default: + // Fallthrough. + } + + return []; + } + function bindMobileLol() { #if FLX_TOUCH @@ -704,23 +841,51 @@ class Controls extends FlxActionSet { addGamepadLiteral(id, [ - Control.ACCEPT => [#if switch B #else A #end], - Control.BACK => [#if switch A #else B #end, FlxGamepadInputID.BACK], - Control.UI_UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP], - Control.UI_DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN], - Control.UI_LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT], - Control.UI_RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT], + Control.ACCEPT => getDefaultGamepadBinds(Control.ACCEPT), + Control.BACK => getDefaultGamepadBinds(Control.BACK), + Control.UI_UP => getDefaultGamepadBinds(Control.UI_UP), + Control.UI_DOWN => getDefaultGamepadBinds(Control.UI_DOWN), + Control.UI_LEFT => getDefaultGamepadBinds(Control.UI_LEFT), + Control.UI_RIGHT => getDefaultGamepadBinds(Control.UI_RIGHT), // don't swap A/B or X/Y for switch on these. A is always the bottom face button - Control.NOTE_UP => [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP], - Control.NOTE_DOWN => [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN], - Control.NOTE_LEFT => [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT], - Control.NOTE_RIGHT => [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT], - Control.PAUSE => [START], - Control.RESET => [RIGHT_SHOULDER] - #if CAN_CHEAT, Control.CHEAT => [X] #end + Control.NOTE_UP => getDefaultGamepadBinds(Control.NOTE_UP), + Control.NOTE_DOWN => getDefaultGamepadBinds(Control.NOTE_DOWN), + Control.NOTE_LEFT => getDefaultGamepadBinds(Control.NOTE_LEFT), + Control.NOTE_RIGHT => getDefaultGamepadBinds(Control.NOTE_RIGHT), + Control.PAUSE => getDefaultGamepadBinds(Control.PAUSE), + // Control.VOLUME_UP => [RIGHT_SHOULDER], + // Control.VOLUME_DOWN => [LEFT_SHOULDER], + // Control.VOLUME_MUTE => [RIGHT_TRIGGER], + Control.CUTSCENE_ADVANCE => getDefaultGamepadBinds(Control.CUTSCENE_ADVANCE), + Control.CUTSCENE_SKIP => getDefaultGamepadBinds(Control.CUTSCENE_SKIP), + Control.RESET => getDefaultGamepadBinds(Control.RESET), + #if CAN_CHEAT, Control.CHEAT => getDefaultGamepadBinds(Control.CHEAT) #end ]); } + function getDefaultGamepadBinds(control:Control):Array { + switch(control) { + case Control.ACCEPT: return [#if switch B #else A #end]; + case Control.BACK: return [#if switch A #else B #end, FlxGamepadInputID.BACK]; + case Control.UI_UP: return [DPAD_UP, LEFT_STICK_DIGITAL_UP]; + case Control.UI_DOWN: return [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN]; + case Control.UI_LEFT: return [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT]; + case Control.UI_RIGHT: return [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT]; + case Control.NOTE_UP: return [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP]; + case Control.NOTE_DOWN: return [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN]; + case Control.NOTE_LEFT: return [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT]; + case Control.NOTE_RIGHT: return [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT]; + case Control.PAUSE: return [START]; + case Control.CUTSCENE_ADVANCE: return [A]; + case Control.CUTSCENE_SKIP: return [START]; + case Control.RESET: return [RIGHT_SHOULDER]; + #if CAN_CHEAT, Control.CHEAT: return [X]; #end + default: + // Fallthrough. + } + return []; + } + /** * Sets all actions that pertain to the binder to trigger when the supplied keys are used. * If binder is a literal you can inline this @@ -749,8 +914,10 @@ class Controls extends FlxActionSet inline static function addButtons(action:FlxActionDigital, buttons:Array, state, id) { - for (button in buttons) + for (button in buttons) { + if (button == FlxGamepadInputID.NONE) continue; // Ignore unbound keys. action.addGamepad(button, state, id); + } } static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array) @@ -798,6 +965,11 @@ class Controls extends FlxActionSet } } + /** + * NOTE: When loading controls: + * An EMPTY array means the control is uninitialized and needs to be reset to default. + * An array with a single FlxKey.NONE means the control was intentionally unbound by the user. + */ public function fromSaveData(data:Dynamic, device:Device) { for (control in Control.createAll()) @@ -805,17 +977,44 @@ class Controls extends FlxActionSet var inputs:Array = Reflect.field(data, control.getName()); if (inputs != null) { + if (inputs.length == 0) { + trace('Control ${control} is missing bindings, resetting to default.'); + switch(device) + { + case Keys: + bindKeys(control, getDefaultKeybinds(Solo, control)); + case Gamepad(id): + bindButtons(control, id, getDefaultGamepadBinds(control)); + } + } else if (inputs == [FlxKey.NONE]) { + trace('Control ${control} is unbound, leaving it be.'); + } else { + switch(device) + { + case Keys: + bindKeys(control, inputs.copy()); + case Gamepad(id): + bindButtons(control, id, inputs.copy()); + } + } + } else { + trace('Control ${control} is missing bindings, resetting to default.'); switch(device) { case Keys: - bindKeys(control, inputs.copy()); + bindKeys(control, getDefaultKeybinds(Solo, control)); case Gamepad(id): - bindButtons(control, id, inputs.copy()); + bindButtons(control, id, getDefaultGamepadBinds(control)); } } } } + /** + * NOTE: When saving controls: + * An EMPTY array means the control is uninitialized and needs to be reset to default. + * An array with a single FlxKey.NONE means the control was intentionally unbound by the user. + */ public function createSaveData(device:Device):Dynamic { var isEmpty = true; @@ -825,6 +1024,8 @@ class Controls extends FlxActionSet var inputs = getInputsFor(control, device); isEmpty = isEmpty && inputs.length == 0; + if (inputs.length == 0) inputs = [FlxKey.NONE]; + Reflect.setField(data, control.getName(), inputs); } diff --git a/source/funkin/LatencyState.hx b/source/funkin/LatencyState.hx index 694e9c3e5..347454253 100644 --- a/source/funkin/LatencyState.hx +++ b/source/funkin/LatencyState.hx @@ -88,14 +88,14 @@ class LatencyState extends MusicBeatSubState // // musSpec.visType = FREQUENCIES; // add(musSpec); - for (beat in 0...Math.floor(FlxG.sound.music.length / Conductor.crochet)) + for (beat in 0...Math.floor(FlxG.sound.music.length / Conductor.beatLengthMs)) { - var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * Conductor.crochet), FlxG.height - 15); + var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * Conductor.beatLengthMs), FlxG.height - 15); beatTick.makeGraphic(2, 15); beatTick.alpha = 0.3; add(beatTick); - var offsetTxt:FlxText = new FlxText(songPosToX(beat * Conductor.crochet), FlxG.height - 26, 0, "swag"); + var offsetTxt:FlxText = new FlxText(songPosToX(beat * Conductor.beatLengthMs), FlxG.height - 26, 0, "swag"); offsetTxt.alpha = 0.5; diffGrp.add(offsetTxt); @@ -127,7 +127,7 @@ class LatencyState extends MusicBeatSubState for (i in 0...32) { - var note:Note = new Note(Conductor.crochet * i, 1); + var note:Note = new Note(Conductor.beatLengthMs * i, 1); noteGrp.add(note); } @@ -143,9 +143,9 @@ class LatencyState extends MusicBeatSubState override function stepHit():Bool { - if (curStep % 4 == 2) + if (Conductor.currentStep % 4 == 2) { - blocks.members[((curBeat % 8) + 1) % 8].alpha = 0.5; + blocks.members[((Conductor.currentBeat % 8) + 1) % 8].alpha = 0.5; } return super.stepHit(); @@ -153,11 +153,11 @@ class LatencyState extends MusicBeatSubState override function beatHit():Bool { - if (curBeat % 8 == 0) blocks.forEach(blok -> { + if (Conductor.currentBeat % 8 == 0) blocks.forEach(blok -> { blok.alpha = 0; }); - blocks.members[curBeat % 8].alpha = 1; + blocks.members[Conductor.currentBeat % 8].alpha = 1; // block.visible = !block.visible; return super.beatHit(); @@ -198,8 +198,8 @@ class LatencyState extends MusicBeatSubState offsetText.text = "AUDIO Offset: " + Conductor.audioOffset + "ms"; offsetText.text += "\nVIDOE Offset: " + Conductor.visualOffset + "ms"; - offsetText.text += "\ncurStep: " + curStep; - offsetText.text += "\ncurBeat: " + curBeat; + offsetText.text += "\ncurrentStep: " + Conductor.currentStep; + offsetText.text += "\ncurrentBeat: " + Conductor.currentBeat; var avgOffsetInput:Float = 0; @@ -255,7 +255,7 @@ class LatencyState extends MusicBeatSubState if (daNote.y < 0 - daNote.height) { daNote.alpha = 1; - // daNote.data.strumTime += Conductor.crochet * 8; + // daNote.data.strumTime += Conductor.beatLengthMs * 8; } }); @@ -266,12 +266,12 @@ class LatencyState extends MusicBeatSubState { Conductor.songPosition = swagSong.getTimeWithDiff(); - var closestBeat:Int = Math.round(Conductor.songPosition / Conductor.crochet) % diffGrp.members.length; - var getDiff:Float = Conductor.songPosition - (closestBeat * Conductor.crochet); + var closestBeat:Int = Math.round(Conductor.songPosition / Conductor.beatLengthMs) % diffGrp.members.length; + var getDiff:Float = Conductor.songPosition - (closestBeat * Conductor.beatLengthMs); getDiff -= Conductor.visualOffset; // lil fix for end of song - if (closestBeat == 0 && getDiff >= Conductor.crochet * 2) getDiff -= FlxG.sound.music.length; + if (closestBeat == 0 && getDiff >= Conductor.beatLengthMs * 2) getDiff -= FlxG.sound.music.length; trace("\tDISTANCE TO CLOSEST BEAT: " + getDiff + "ms"); trace("\tCLOSEST BEAT: " + closestBeat); diff --git a/source/funkin/MainMenuState.hx b/source/funkin/MainMenuState.hx index 57640bffa..82fcac77d 100644 --- a/source/funkin/MainMenuState.hx +++ b/source/funkin/MainMenuState.hx @@ -1,5 +1,6 @@ package funkin; +import funkin.ui.debug.DebugMenuSubState; import flixel.FlxObject; import flixel.FlxSprite; import flixel.FlxState; @@ -299,7 +300,14 @@ class MainMenuState extends MusicBeatState } } - // FlxG.camera.followLerp = CoolUtil.camLerpShit(0.06); + // ` / ~ to open the debug menu. + if (FlxG.keys.justPressed.GRAVEACCENT) + { + // TODO: Does this break anything? + this.persistentUpdate = false; + this.persistentDraw = false; + FlxG.state.openSubState(new DebugMenuSubState()); + } if (FlxG.sound.music.volume < 0.8) { diff --git a/source/funkin/MusicBeatState.hx b/source/funkin/MusicBeatState.hx index 6c6591c62..2b97951f9 100644 --- a/source/funkin/MusicBeatState.hx +++ b/source/funkin/MusicBeatState.hx @@ -9,7 +9,6 @@ import flixel.util.FlxSort; import funkin.modding.PolymodHandler; import funkin.modding.events.ScriptEvent; import funkin.modding.module.ModuleHandler; -import funkin.ui.debug.DebugMenuSubState; import funkin.util.SortUtil; /** @@ -66,15 +65,6 @@ class MusicBeatState extends FlxUIState // This can now be used in EVERY STATE YAY! if (FlxG.keys.justPressed.F5) debug_refreshModules(); - // ` / ~ to open the debug menu. - if (FlxG.keys.justPressed.GRAVEACCENT) - { - // TODO: Does this break anything? - this.persistentUpdate = false; - this.persistentDraw = false; - FlxG.state.openSubState(new DebugMenuSubState()); - } - // Display Conductor info in the watch window. FlxG.watch.addQuick("songPos", Conductor.songPosition); FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime); diff --git a/source/funkin/MusicBeatSubState.hx b/source/funkin/MusicBeatSubState.hx index 440e25c96..5c6635a02 100644 --- a/source/funkin/MusicBeatSubState.hx +++ b/source/funkin/MusicBeatSubState.hx @@ -5,62 +5,96 @@ import flixel.util.FlxColor; import funkin.Conductor.BPMChangeEvent; import funkin.modding.events.ScriptEvent; import funkin.modding.module.ModuleHandler; +import flixel.text.FlxText; +import funkin.modding.PolymodHandler; /** * MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState. */ class MusicBeatSubState extends FlxSubState { + public var leftWatermarkText:FlxText = null; + public var rightWatermarkText:FlxText = null; + public function new(bgColor:FlxColor = FlxColor.TRANSPARENT) { super(bgColor); } - var curStep:Int = 0; - var curBeat:Int = 0; var controls(get, never):Controls; inline function get_controls():Controls return PlayerSettings.player1.controls; - override function update(elapsed:Float) + override function create():Void { - // everyStep(); - var oldStep:Int = curStep; + super.create(); - updateCurStep(); - curBeat = Math.floor(curStep / 4); + createWatermarkText(); - if (oldStep != curStep && curStep >= 0) stepHit(); + Conductor.beatHit.add(this.beatHit); + Conductor.stepHit.add(this.stepHit); + } + public override function destroy():Void + { + super.destroy(); + Conductor.beatHit.remove(this.beatHit); + Conductor.stepHit.remove(this.stepHit); + } + + override function update(elapsed:Float):Void + { super.update(elapsed); + + // Rebindable volume keys. + if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted(); + else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1); + else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1); + + // Emergency exit button. + if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState()); + + // This can now be used in EVERY STATE YAY! + if (FlxG.keys.justPressed.F5) debug_refreshModules(); } - function updateCurStep():Void + function debug_refreshModules() { - var lastChange:BPMChangeEvent = - { - stepTime: 0, - songTime: 0, - bpm: 0 - } - for (i in 0...Conductor.bpmChangeMap.length) - { - if (Conductor.songPosition > Conductor.bpmChangeMap[i].songTime) lastChange = Conductor.bpmChangeMap[i]; - } + PolymodHandler.forceReloadAssets(); - curStep = lastChange.stepTime + Math.floor(((Conductor.songPosition - Conductor.audioOffset) - lastChange.songTime) / Conductor.stepCrochet); + // Restart the current state, so old data is cleared. + FlxG.resetState(); } + /** + * Called when a step is hit in the current song. + * Continues outside of PlayState, for things like animations in menus. + * @return Whether the event should continue (not canceled). + */ public function stepHit():Bool { - var event = new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, curBeat, curStep); + var event:ScriptEvent = new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, Conductor.currentBeat, Conductor.currentStep); dispatchEvent(event); if (event.eventCanceled) return false; - if (curStep % 4 == 0) beatHit(); + return true; + } + + /** + * Called when a beat is hit in the current song. + * Continues outside of PlayState, for things like animations in menus. + * @return Whether the event should continue (not canceled). + */ + public function beatHit():Bool + { + var event:ScriptEvent = new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, Conductor.currentBeat, Conductor.currentStep); + + dispatchEvent(event); + + if (event.eventCanceled) return false; return true; } @@ -70,6 +104,25 @@ class MusicBeatSubState extends FlxSubState ModuleHandler.callEvent(event); } + function createWatermarkText():Void + { + // Both have an xPos of 0, but a width equal to the full screen. + // The rightWatermarkText is right aligned, which puts the text in the correct spot. + leftWatermarkText = new FlxText(0, FlxG.height - 18, FlxG.width, '', 12); + rightWatermarkText = new FlxText(0, FlxG.height - 18, FlxG.width, '', 12); + + // 100,000 should be good enough. + leftWatermarkText.zIndex = 100000; + rightWatermarkText.zIndex = 100000; + leftWatermarkText.scrollFactor.set(0, 0); + rightWatermarkText.scrollFactor.set(0, 0); + leftWatermarkText.setFormat('VCR OSD Mono', 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + rightWatermarkText.setFormat('VCR OSD Mono', 16, FlxColor.WHITE, RIGHT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + + add(leftWatermarkText); + add(rightWatermarkText); + } + /** * Close this substate and replace it with a different one. */ @@ -78,15 +131,4 @@ class MusicBeatSubState extends FlxSubState this.close(); this._parentState.openSubState(substate); } - - public function beatHit():Bool - { - var event = new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, curBeat, curStep); - - dispatchEvent(event); - - if (event.eventCanceled) return false; - - return true; - } } diff --git a/source/funkin/Note.hx b/source/funkin/Note.hx index 71c63a94e..ea99449b1 100644 --- a/source/funkin/Note.hx +++ b/source/funkin/Note.hx @@ -207,7 +207,7 @@ class Note extends FlxSprite prevNote.animation.play(prevNote.colorName + 'hold'); prevNote.updateHitbox(); - var scaleThing:Float = Math.round((Conductor.stepCrochet) * (0.45 * FlxMath.roundDecimal(SongLoad.getSpeed(), 2))); + var scaleThing:Float = Math.round((Conductor.stepLengthMs) * (0.45 * FlxMath.roundDecimal(PlayState.instance.currentChart.scrollSpeed, 2))); // get them a LIL closer together cuz the antialiasing blurs the edges if (antialiasing) scaleThing *= 1.0 + (1.0 / prevNote.frameHeight); prevNote.scale.y = scaleThing / prevNote.frameHeight; diff --git a/source/funkin/graphics/video/FlxVideo.hx b/source/funkin/graphics/video/FlxVideo.hx index 34b806f7e..393e2d49c 100644 --- a/source/funkin/graphics/video/FlxVideo.hx +++ b/source/funkin/graphics/video/FlxVideo.hx @@ -20,7 +20,7 @@ class FlxVideo extends FlxBasic /** * Doesn't actually interact with Flixel shit, only just a pleasant to use class */ - public function new(vidSrc:String) + public function new(videoPath:String) { super(); @@ -36,7 +36,7 @@ class FlxVideo extends FlxBasic netStream = new NetStream(netConnection); netStream.client = {onMetaData: client_onMetaData}; netConnection.addEventListener(NetStatusEvent.NET_STATUS, netConnection_onNetStatus); - netStream.play(Paths.file(vidSrc)); + netStream.play(videoPath); } public function finishVideo():Void diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 98f60ca86..d90c1386d 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -8,6 +8,7 @@ import funkin.play.stage.StageData; import polymod.Polymod; import polymod.backends.PolymodAssets.PolymodAssetType; import polymod.format.ParseRules.TextFileFormat; +import funkin.play.event.SongEventData.SongEventParser; import funkin.util.FileUtil; class PolymodHandler @@ -279,6 +280,11 @@ class PolymodHandler // TODO: Reload event callbacks funkin.data.level.LevelRegistry.instance.loadEntries(); + SongEventParser.loadEventCache(); + // TODO: Uncomment this once conversation data is implemented. + // ConversationDataParser.loadConversationCache(); + // DialogueBoxDataParser.loadDialogueBoxCache(); + // SpeakerDataParser.loadSpeakerCache(); SongDataParser.loadSongCache(); StageDataParser.loadStageCache(); CharacterDataParser.loadCharacterCache(); diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 016b21c6c..169bda24b 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -38,7 +38,7 @@ class Countdown stopCountdown(); PlayState.instance.isInCountdown = true; - Conductor.songPosition = Conductor.crochet * -5; + Conductor.songPosition = Conductor.beatLengthMs * -5; // Handle onBeatHit events manually @:privateAccess PlayState.instance.dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, 0, 0)); @@ -46,7 +46,7 @@ class Countdown // The timer function gets called based on the beat of the song. countdownTimer = new FlxTimer(); - countdownTimer.start(Conductor.crochet / 1000, function(tmr:FlxTimer) { + countdownTimer.start(Conductor.beatLengthMs / 1000, function(tmr:FlxTimer) { countdownStep = decrement(countdownStep); // Handle onBeatHit events manually @@ -212,7 +212,7 @@ class Countdown countdownSprite.screenCenter(); // Fade sprite in, then out, then destroy it. - FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.crochet / 1000, + FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.beatLengthMs / 1000, { ease: FlxEase.cubeInOut, onComplete: function(twn:FlxTween) { diff --git a/source/funkin/play/HealthIcon.hx b/source/funkin/play/HealthIcon.hx index a15c63060..e4802b99c 100644 --- a/source/funkin/play/HealthIcon.hx +++ b/source/funkin/play/HealthIcon.hx @@ -148,8 +148,7 @@ class HealthIcon extends FlxSprite { if (characterId == 'beta') { - // characterId = PlayState.instance.currentPlayerId; - characterId = 'bf'; + characterId = PlayState.instance.currentPlayerId; } else { diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index e8cfb1402..f39000633 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -972,10 +972,10 @@ class PlayState extends MusicBeatState oldNote = newNote; // Generate X sustain notes. - var sustainSections = Math.round(songNote.length / Conductor.stepCrochet); + var sustainSections = Math.round(songNote.length / Conductor.stepLengthMs); for (noteIndex in 0...sustainSections) { - var noteTimeOffset:Float = Conductor.stepCrochet + (Conductor.stepCrochet * noteIndex); + var noteTimeOffset:Float = Conductor.stepLengthMs + (Conductor.stepLengthMs * noteIndex); var sustainNote:Note = new Note(songNote.time + noteTimeOffset, songNote.data, oldNote, true, strumlineStyle); sustainNote.mustPress = mustHitNote; sustainNote.data.noteKind = songNote.kind; @@ -2209,14 +2209,14 @@ class PlayState extends MusicBeatState if (shouldShowComboText) { - var animShit:ComboCounter = new ComboCounter(-100, 300, Highscore.tallies.combo); + var animShit:ComboMilestone = new ComboMilestone(-100, 300, Highscore.tallies.combo); animShit.scrollFactor.set(0.6, 0.6); animShit.cameras = [camHUD]; add(animShit); var frameShit:Float = (1 / 24) * 2; // equals 2 frames in the animation - new FlxTimer().start(((Conductor.crochet / 1000) * 1.25) - frameShit, function(tmr) { + new FlxTimer().start(((Conductor.beatLengthMs / 1000) * 1.25) - frameShit, function(tmr) { animShit.forceFinish(); }); } diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 12be46fc8..302858f2f 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -1,5 +1,6 @@ package funkin.play; +import funkin.ui.story.StoryMenuState; import funkin.graphics.adobeanimate.FlxAtlasSprite; import flixel.FlxBasic; import flixel.FlxSprite; @@ -355,7 +356,17 @@ class ResultState extends MusicBeatSubState if (FlxG.keys.justPressed.COMMA) songName.angle -= 0.1; - if (controls.PAUSE) FlxG.switchState(new FreeplayState()); + if (controls.PAUSE) + { + if (PlayStatePlaylist.isStoryMode) + { + FlxG.switchState(new StoryMenuState()); + } + else + { + FlxG.switchState(new FreeplayState()); + } + } super.update(elapsed); } diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index f4db7f66f..bdf7ef591 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -367,7 +367,7 @@ class BaseCharacter extends Bopper // This lets you add frames to the end of the sing animation to ease back into the idle! holdTimer += event.elapsed; - var singTimeSec:Float = singTimeSec * (Conductor.crochet * 0.001); // x beats, to ms. + var singTimeSec:Float = singTimeSec * (Conductor.beatLengthMs * 0.001); // x beats, to ms. if (getCurrentAnimation().endsWith('miss')) singTimeSec *= 2; // makes it feel more awkward when you miss diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index 45934c99b..34d89362f 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -56,10 +56,12 @@ class MultiSparrowCharacter extends BaseCharacter if (_data.isPixel) { + this.isPixel = true; this.antialiasing = false; } else { + this.isPixel = false; this.antialiasing = true; } } diff --git a/source/funkin/play/character/PackerCharacter.hx b/source/funkin/play/character/PackerCharacter.hx index 3ee276eb1..2bfac800a 100644 --- a/source/funkin/play/character/PackerCharacter.hx +++ b/source/funkin/play/character/PackerCharacter.hx @@ -41,10 +41,12 @@ class PackerCharacter extends BaseCharacter if (_data.isPixel) { + this.isPixel = true; this.antialiasing = false; } else { + this.isPixel = false; this.antialiasing = true; } diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index e6e9c843d..79425d564 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -10,7 +10,7 @@ import funkin.play.event.SongEventData.SongEventFieldType; /** * This class represents a handler for camera zoom events. - * + * * Example: Zoom to 1.3x: * ``` * { @@ -18,8 +18,8 @@ import funkin.play.event.SongEventData.SongEventFieldType; * 'v': 1.3 * } * ``` - * - * Example: Zoom to 1.3x + * + * Example: Zoom to 1.3x * ``` * { * 'e': 'FocusCamera', @@ -29,7 +29,7 @@ import funkin.play.event.SongEventData.SongEventFieldType; * } * } * ``` - * + * * Example: Focus on (100, 100): * ``` * { @@ -76,7 +76,8 @@ class ZoomCameraSongEvent extends SongEvent return; } - FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.stepCrochet * duration / 1000), {ease: easeFunction}); + FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.stepLengthMs * duration / 1000), + {ease: easeFunction}); } } diff --git a/source/funkin/play/song/SongData.hx b/source/funkin/play/song/SongData.hx index 78593c6c0..282734d10 100644 --- a/source/funkin/play/song/SongData.hx +++ b/source/funkin/play/song/SongData.hx @@ -365,7 +365,7 @@ abstract SongNoteData(RawSongNoteData) public function get_stepTime():Float { // TODO: Account for changes in BPM. - return this.t / Conductor.stepCrochet; + return this.t / Conductor.stepLengthMs; } /** @@ -551,7 +551,7 @@ abstract SongEventData(RawSongEventData) public function get_stepTime():Float { // TODO: Account for changes in BPM. - return this.t / Conductor.stepCrochet; + return this.t / Conductor.stepLengthMs; } public var event(get, set):String; diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 36d1600a6..ef2d28430 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -658,7 +658,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass public function onBeatHit(event:SongTimeScriptEvent):Void { // Override me in your scripted stage to perform custom behavior! - // Make sure to call super.onBeatHit(curBeat) if you want to keep the boppers dancing. + // Make sure to call super.onBeatHit(event) if you want to keep the boppers dancing. for (bopper in boppers) { diff --git a/source/funkin/ui/ControlsMenu.hx b/source/funkin/ui/ControlsMenu.hx index bd81cae5c..0d9db5b34 100644 --- a/source/funkin/ui/ControlsMenu.hx +++ b/source/funkin/ui/ControlsMenu.hx @@ -14,7 +14,7 @@ import funkin.ui.TextMenuList; class ControlsMenu extends funkin.ui.OptionsState.Page { - inline static public var COLUMNS = 2; + public static inline final COLUMNS = 2; static var controlList = Control.createAll(); /* * Defines groups of controls that cannot share inputs, like left and right. Say, if ACCEPT is Z, Back is X, @@ -23,7 +23,9 @@ class ControlsMenu extends funkin.ui.OptionsState.Page */ static var controlGroups:Array> = [ [NOTE_UP, NOTE_DOWN, NOTE_LEFT, NOTE_RIGHT], - [UI_UP, UI_DOWN, UI_LEFT, UI_RIGHT, ACCEPT, BACK] + [UI_UP, UI_DOWN, UI_LEFT, UI_RIGHT, ACCEPT, BACK], + [CUTSCENE_ADVANCE, CUTSCENE_SKIP], + [VOLUME_UP, VOLUME_DOWN, VOLUME_MUTE] ]; var itemGroups:Array> = [for (i in 0...controlGroups.length) []]; @@ -36,7 +38,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page var labels:FlxTypedGroup; var currentDevice:Device = Keys; - var deviceListSelected = false; + var deviceListSelected:Bool = false; public function new() { @@ -48,7 +50,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page camera = menuCamera; labels = new FlxTypedGroup(); - var headers = new FlxTypedGroup(); + var headers:FlxTypedGroup = new FlxTypedGroup(); controlGrid = new MenuTypedList(Columns(COLUMNS), Vertical); add(labels); @@ -57,20 +59,20 @@ class ControlsMenu extends funkin.ui.OptionsState.Page if (FlxG.gamepads.numActiveGamepads > 0) { - var devicesBg = new FlxSprite(); - devicesBg.makeGraphic(FlxG.width, 100, 0xFFfafd6d); + var devicesBg:FlxSprite = new FlxSprite(); + devicesBg.makeGraphic(FlxG.width, 100, 0xFFFAFD6D); add(devicesBg); deviceList = new TextMenuList(Horizontal, None); add(deviceList); deviceListSelected = true; - var item; + var item:TextMenuItem; - item = deviceList.createItem("Keyboard", AtlasFont.BOLD, selectDevice.bind(Keys)); + item = deviceList.createItem('Keyboard', AtlasFont.BOLD, selectDevice.bind(Keys)); item.x = FlxG.width / 2 - item.width - 30; item.y = (devicesBg.height - item.height) / 2; - item = deviceList.createItem("Gamepad", AtlasFont.BOLD, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id))); + item = deviceList.createItem('Gamepad', AtlasFont.BOLD, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id))); item.x = FlxG.width / 2 + 30; item.y = (devicesBg.height - item.height) / 2; } @@ -96,6 +98,18 @@ class ControlsMenu extends funkin.ui.OptionsState.Page headers.add(new AtlasText(0, y, "NOTES", AtlasFont.BOLD)).screenCenter(X); y += spacer; } + else if (currentHeader != "CUTSCENE_" && name.indexOf("CUTSCENE_") == 0) + { + currentHeader = "CUTSCENE_"; + headers.add(new AtlasText(0, y, "CUTSCENE", AtlasFont.BOLD)).screenCenter(X); + y += spacer; + } + else if (currentHeader != "VOLUME_" && name.indexOf("VOLUME_") == 0) + { + currentHeader = "VOLUME_"; + headers.add(new AtlasText(0, y, "VOLUME", AtlasFont.BOLD)).screenCenter(X); + y += spacer; + } if (currentHeader != null && name.indexOf(currentHeader) == 0) name = name.substr(currentHeader.length); @@ -128,7 +142,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0; }); - prompt = new Prompt("\nPress any key to rebind\n\n\n\n Escape to cancel", None); + prompt = new Prompt("\nPress any key to rebind\n\n\nBackspace to unbind\n Escape to cancel", None); prompt.create(); prompt.createBgFromMargin(100, 0xFFfafd6d); prompt.back.scrollFactor.set(0, 0); @@ -149,6 +163,8 @@ class ControlsMenu extends funkin.ui.OptionsState.Page function onSelect():Void { + keyUsedToEnterPrompt = FlxG.keys.firstJustPressed(); + controlGrid.enabled = false; canExit = false; prompt.exists = true; @@ -187,7 +203,9 @@ class ControlsMenu extends funkin.ui.OptionsState.Page canExit = false; } - override function update(elapsed:Float) + var keyUsedToEnterPrompt:Null = null; + + override function update(elapsed:Float):Void { super.update(elapsed); @@ -200,18 +218,35 @@ class ControlsMenu extends funkin.ui.OptionsState.Page { case Keys: { - // check released otherwise bugs can happen when you change the BACK key + // Um? + // Checking pressed causes problems when you change the BACK key, + // but checking released causes problems when the prompt is instant. + + // keyUsedToEnterPrompt is my weird workaround. + var key = FlxG.keys.firstJustReleased(); - if (key != NONE) + if (key != NONE && key != keyUsedToEnterPrompt) { - if (key != ESCAPE) onInputSelect(key); - closePrompt(); + if (key == ESCAPE) + { + closePrompt(); + } + else if (key == BACKSPACE) + { + onInputSelect(NONE); + closePrompt(); + } + else + { + onInputSelect(key); + closePrompt(); + } } } case Gamepad(id): { var button = FlxG.gamepads.getByID(id).firstJustReleasedID(); - if (button != NONE) + if (button != NONE && button != keyUsedToEnterPrompt) { if (button != BACK) onInputSelect(button); closePrompt(); @@ -219,23 +254,32 @@ class ControlsMenu extends funkin.ui.OptionsState.Page } } } + + var keyJustReleased:Int = FlxG.keys.firstJustReleased(); + if (keyJustReleased != NONE && keyJustReleased == keyUsedToEnterPrompt) + { + keyUsedToEnterPrompt = null; + } } - function onInputSelect(input:Int) + function onInputSelect(input:Int):Void { var item = controlGrid.selectedItem; // check if that key is already set for this - var column0 = Math.floor(controlGrid.selectedIndex / 2) * 2; - for (i in 0...COLUMNS) + if (input != FlxKey.NONE) { - if (controlGrid.members[column0 + i].input == input) return; + var column0 = Math.floor(controlGrid.selectedIndex / 2) * 2; + for (i in 0...COLUMNS) + { + if (controlGrid.members[column0 + i].input == input) return; + } } // Check if items in the same group already have the new input for (group in itemGroups) { - if (group.contains(item)) + if (input != FlxKey.NONE && group.contains(item)) { for (otherItem in group) { @@ -252,10 +296,45 @@ class ControlsMenu extends funkin.ui.OptionsState.Page } PlayerSettings.player1.controls.replaceBinding(item.control, currentDevice, input, item.input); + // Don't use resetItem() since items share names/labels item.input = input; item.label.text = item.getLabel(input); + // Shift left on the grid if the item on the right is bound and the item on the left is unbound. + if (controlGrid.selectedIndex % 2 == 1) + { + trace('Modified item on right side of grid'); + var leftItem = controlGrid.members[controlGrid.selectedIndex - 1]; + if (leftItem != null && input != FlxKey.NONE && leftItem.input == FlxKey.NONE) + { + trace('Left item is unbound and right item is not!'); + // Swap them. + var temp = leftItem.input; + leftItem.input = item.input; + item.input = temp; + + leftItem.label.text = leftItem.getLabel(leftItem.input); + item.label.text = item.getLabel(item.input); + } + } + else + { + trace('Modified item on left side of grid'); + var rightItem = controlGrid.members[controlGrid.selectedIndex + 1]; + if (rightItem != null && input == FlxKey.NONE && rightItem.input != FlxKey.NONE) + { + trace('Left item is unbound and right item is not!'); + // Swap them. + var temp = item.input; + item.input = rightItem.input; + rightItem.input = temp; + + item.label.text = item.getLabel(item.input); + rightItem.label.text = rightItem.getLabel(rightItem.input); + } + } + PlayerSettings.player1.saveControls(); } @@ -306,6 +385,8 @@ class InputItem extends TextMenuItem this.input = getInput(); super(x, y, getLabel(input), DEFAULT, callback); + + this.fireInstantly = true; } public function updateDevice(device:Device) @@ -334,6 +415,6 @@ class InputItem extends TextMenuItem public function getLabel(input:Int) { - return input == -1 ? "---" : InputFormatter.format(input, device); + return input == FlxKey.NONE ? "---" : InputFormatter.format(input, device); } } diff --git a/source/funkin/ui/PopUpStuff.hx b/source/funkin/ui/PopUpStuff.hx index c5828dd0a..20380f50a 100644 --- a/source/funkin/ui/PopUpStuff.hx +++ b/source/funkin/ui/PopUpStuff.hx @@ -59,7 +59,7 @@ class PopUpStuff extends FlxTypedGroup remove(rating, true); rating.destroy(); }, - startDelay: Conductor.crochet * 0.001 + startDelay: Conductor.beatLengthMs * 0.001 }); } @@ -109,7 +109,7 @@ class PopUpStuff extends FlxTypedGroup remove(comboSpr, true); comboSpr.destroy(); }, - startDelay: Conductor.crochet * 0.001 + startDelay: Conductor.beatLengthMs * 0.001 }); var seperatedScore:Array = []; @@ -155,7 +155,7 @@ class PopUpStuff extends FlxTypedGroup remove(numScore, true); numScore.destroy(); }, - startDelay: Conductor.crochet * 0.002 + startDelay: Conductor.beatLengthMs * 0.002 }); daLoop++; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index c61637536..18e34c72b 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -189,12 +189,12 @@ class ChartEditorState extends HaxeUIState function get_scrollPositionInMs():Float { - return scrollPositionInSteps * Conductor.stepCrochet; + return scrollPositionInSteps * Conductor.stepLengthMs; } function set_scrollPositionInMs(value:Float):Float { - scrollPositionInPixels = value / Conductor.stepCrochet; + scrollPositionInPixels = value / Conductor.stepLengthMs; return value; } @@ -223,7 +223,7 @@ class ChartEditorState extends HaxeUIState function get_playheadPositionInMs():Float { - return playheadPositionInSteps * Conductor.stepCrochet; + return playheadPositionInSteps * Conductor.stepLengthMs; } /** @@ -263,7 +263,7 @@ class ChartEditorState extends HaxeUIState function get_songLengthInMs():Float { - return songLengthInSteps * Conductor.stepCrochet; + return songLengthInSteps * Conductor.stepLengthMs; } function set_songLengthInMs(value:Float):Float @@ -1667,7 +1667,7 @@ class ChartEditorState extends HaxeUIState // The song position of the cursor, in steps. var cursorFractionalStep:Float = cursorY / GRID_SIZE / (16 / noteSnapQuant); var cursorStep:Int = Std.int(Math.floor(cursorFractionalStep)); - var cursorMs:Float = cursorStep * Conductor.stepCrochet * (16 / noteSnapQuant); + var cursorMs:Float = cursorStep * Conductor.stepLengthMs * (16 / noteSnapQuant); // The direction value for the column at the cursor. var cursorColumn:Int = Math.floor(cursorX / GRID_SIZE); if (cursorColumn < 0) cursorColumn = 0; @@ -1705,7 +1705,7 @@ class ChartEditorState extends HaxeUIState // We released the mouse. Select the notes in the box. var cursorFractionalStepStart:Float = cursorYStart / GRID_SIZE; var cursorStepStart:Int = Math.floor(cursorFractionalStepStart); - var cursorMsStart:Float = cursorStepStart * Conductor.stepCrochet; + var cursorMsStart:Float = cursorStepStart * Conductor.stepLengthMs; var cursorColumnBase:Int = Math.floor(cursorX / GRID_SIZE); var cursorColumnBaseStart:Int = Math.floor(cursorXStart / GRID_SIZE); @@ -1878,11 +1878,11 @@ class ChartEditorState extends HaxeUIState // Handle extending the note as you drag. // Since use Math.floor and stepCrochet here, the hold notes will be beat snapped. - var dragLengthSteps:Float = Math.floor((cursorMs - currentPlaceNoteData.time) / Conductor.stepCrochet); + var dragLengthSteps:Float = Math.floor((cursorMs - currentPlaceNoteData.time) / Conductor.stepLengthMs); // Without this, the newly placed note feels too short compared to the user's input. var INCREMENT:Float = 1.0; - var dragLengthMs:Float = (dragLengthSteps + INCREMENT) * Conductor.stepCrochet; + var dragLengthMs:Float = (dragLengthSteps + INCREMENT) * Conductor.stepLengthMs; // TODO: Add and update some sort of preview? @@ -2187,7 +2187,7 @@ class ChartEditorState extends HaxeUIState } // Get the position the note should be at. - var noteTimePixels:Float = noteData.time / Conductor.stepCrochet * GRID_SIZE; + var noteTimePixels:Float = noteData.time / Conductor.stepLengthMs * GRID_SIZE; // Make sure the note appears when scrolling up. var modifiedViewAreaTop = viewAreaTop - GRID_SIZE; @@ -2213,7 +2213,7 @@ class ChartEditorState extends HaxeUIState { // If the note is a hold, we need to make sure it's long enough. var noteLengthMs:Float = noteSprite.noteData.length; - var noteLengthSteps:Float = (noteLengthMs / Conductor.stepCrochet); + var noteLengthSteps:Float = (noteLengthMs / Conductor.stepLengthMs); var lastNoteSprite:ChartEditorNoteSprite = noteSprite; while (noteLengthSteps > 0) @@ -2237,7 +2237,7 @@ class ChartEditorState extends HaxeUIState // Make sure the last note sprite shows the end cap properly. lastNoteSprite.childNoteSprite = null; - // var noteLengthPixels:Float = (noteLengthMs / Conductor.stepCrochet + 1) * GRID_SIZE; + // var noteLengthPixels:Float = (noteLengthMs / Conductor.stepLengthMs + 1) * GRID_SIZE; // add(new FlxSprite(noteSprite.x, noteSprite.y - renderedNotes.y + noteLengthPixels).makeGraphic(40, 2, 0xFFFF0000)); } } @@ -2252,7 +2252,7 @@ class ChartEditorState extends HaxeUIState } // Get the position the event should be at. - var eventTimePixels:Float = eventData.time / Conductor.stepCrochet * GRID_SIZE; + var eventTimePixels:Float = eventData.time / Conductor.stepLengthMs * GRID_SIZE; // Make sure the event appears when scrolling up. var modifiedViewAreaTop = viewAreaTop - GRID_SIZE;