mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-08-18 14:05:06 +00:00
Merge pull request #109 from FunkinCrew/feature/week-7-cutscenes
Week 7 Cutscenes
This commit is contained in:
commit
2ce3228bd5
|
@ -5,14 +5,14 @@ import flixel.group.FlxGroup.FlxTypedGroup;
|
|||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.util.FlxTimer;
|
||||
|
||||
class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
||||
class ComboMilestone extends FlxTypedSpriteGroup<FlxSprite>
|
||||
{
|
||||
var effectStuff:FlxSprite;
|
||||
|
||||
var wasComboSetup:Bool = false;
|
||||
var daCombo:Int = 0;
|
||||
|
||||
var grpNumbers:FlxTypedGroup<ComboNumber>;
|
||||
var grpNumbers:FlxTypedGroup<ComboMilestoneNumber>;
|
||||
|
||||
var onScreenTime:Float = 0;
|
||||
|
||||
|
@ -23,7 +23,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
|||
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<FlxSprite>
|
|||
effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7));
|
||||
add(effectStuff);
|
||||
|
||||
grpNumbers = new FlxTypedGroup<ComboNumber>();
|
||||
grpNumbers = new FlxTypedGroup<ComboMilestoneNumber>();
|
||||
// add(grpNumbers);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
|||
{
|
||||
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<FlxSprite>
|
|||
|
||||
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<FlxSprite>
|
|||
|
||||
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<FlxSprite>
|
|||
|
||||
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;
|
|
@ -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<SongTimeChange> = [];
|
||||
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<Float> = null;
|
||||
|
||||
// OLD, replaced with timeChanges.
|
||||
public static var bpmChangeMap:Array<BPMChangeEvent> = [];
|
||||
/**
|
||||
* 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<SongTimeChange> = [];
|
||||
|
||||
/**
|
||||
* 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<SongTimeChange>)
|
||||
public static function mapTimeChanges(songTimeChanges:Array<SongTimeChange>):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;
|
||||
}
|
||||
|
|
|
@ -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<FlxKey>, state:FlxInputState)
|
||||
static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, 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<FlxKey>)
|
||||
|
@ -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<FlxKey> {
|
||||
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<FlxGamepadInputID> {
|
||||
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<FlxGamepadInputID>, 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<FlxGamepadInputID>)
|
||||
|
@ -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<Int> = 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -148,8 +148,7 @@ class HealthIcon extends FlxSprite
|
|||
{
|
||||
if (characterId == 'beta')
|
||||
{
|
||||
// characterId = PlayState.instance.currentPlayerId;
|
||||
characterId = 'bf';
|
||||
characterId = PlayState.instance.currentPlayerId;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -56,10 +56,12 @@ class MultiSparrowCharacter extends BaseCharacter
|
|||
|
||||
if (_data.isPixel)
|
||||
{
|
||||
this.isPixel = true;
|
||||
this.antialiasing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isPixel = false;
|
||||
this.antialiasing = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,10 +41,12 @@ class PackerCharacter extends BaseCharacter
|
|||
|
||||
if (_data.isPixel)
|
||||
{
|
||||
this.isPixel = true;
|
||||
this.antialiasing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isPixel = false;
|
||||
this.antialiasing = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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<Array<Control>> = [
|
||||
[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<Array<InputItem>> = [for (i in 0...controlGroups.length) []];
|
||||
|
@ -36,7 +38,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
|||
var labels:FlxTypedGroup<AtlasText>;
|
||||
|
||||
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<AtlasText>();
|
||||
var headers = new FlxTypedGroup<AtlasText>();
|
||||
var headers:FlxTypedGroup<AtlasText> = new FlxTypedGroup<AtlasText>();
|
||||
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<Int> = 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
|||
remove(rating, true);
|
||||
rating.destroy();
|
||||
},
|
||||
startDelay: Conductor.crochet * 0.001
|
||||
startDelay: Conductor.beatLengthMs * 0.001
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
|||
remove(comboSpr, true);
|
||||
comboSpr.destroy();
|
||||
},
|
||||
startDelay: Conductor.crochet * 0.001
|
||||
startDelay: Conductor.beatLengthMs * 0.001
|
||||
});
|
||||
|
||||
var seperatedScore:Array<Int> = [];
|
||||
|
@ -155,7 +155,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
|||
remove(numScore, true);
|
||||
numScore.destroy();
|
||||
},
|
||||
startDelay: Conductor.crochet * 0.002
|
||||
startDelay: Conductor.beatLengthMs * 0.002
|
||||
});
|
||||
|
||||
daLoop++;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue