mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-27 01:55:52 -05:00
Merge branch 'rewrite/master' into feature/chart-editor-measure-numbers
This commit is contained in:
commit
8f7c449194
58 changed files with 1798 additions and 955 deletions
2
art
2
art
|
@ -1 +1 @@
|
||||||
Subproject commit 1656bea5370c65879aaeb323e329f403c78071c5
|
Subproject commit 03e7c2a2353b184e45955c96d763b7cdf1acbc34
|
4
hmm.json
4
hmm.json
|
@ -54,14 +54,14 @@
|
||||||
"name": "haxeui-core",
|
"name": "haxeui-core",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "e765a3e0b7a653823e8dec765e04623f27f573f8",
|
"ref": "5086e59e7551d775ed4d1fb0188e31de22d1312b",
|
||||||
"url": "https://github.com/haxeui/haxeui-core"
|
"url": "https://github.com/haxeui/haxeui-core"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "haxeui-flixel",
|
"name": "haxeui-flixel",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "7a517d561eff49d8123c128bf9f5c1123b84d014",
|
"ref": "2b9cff727999b53ed292b1675ac1c9089ac77600",
|
||||||
"url": "https://github.com/haxeui/haxeui-flixel"
|
"url": "https://github.com/haxeui/haxeui-flixel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,7 @@ import funkin.data.song.SongDataUtils;
|
||||||
* A core class which handles musical timing throughout the game,
|
* A core class which handles musical timing throughout the game,
|
||||||
* both in gameplay and in menus.
|
* both in gameplay and in menus.
|
||||||
*/
|
*/
|
||||||
|
@:nullSafety
|
||||||
class Conductor
|
class Conductor
|
||||||
{
|
{
|
||||||
// onBeatHit is called every quarter note
|
// onBeatHit is called every quarter note
|
||||||
|
@ -28,29 +29,53 @@ class Conductor
|
||||||
// 60 BPM = 240 sixteenth notes per minute = 4 onStepHit per second
|
// 60 BPM = 240 sixteenth notes per minute = 4 onStepHit per second
|
||||||
// 7/8 = 3.5 beats per measure = 14 steps per measure
|
// 7/8 = 3.5 beats per measure = 14 steps per measure
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current instance of the Conductor.
|
||||||
|
* If one doesn't currently exist, a new one will be created.
|
||||||
|
*
|
||||||
|
* You can also do stuff like store a reference to the Conductor and pass it around or temporarily replace it,
|
||||||
|
* or have a second Conductor running at the same time, or other weird stuff like that if you need to.
|
||||||
|
*/
|
||||||
|
public static var instance:Conductor = new Conductor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal fired when the current Conductor instance advances to a new measure.
|
||||||
|
*/
|
||||||
|
public static var measureHit(default, null):FlxSignal = new FlxSignal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal fired when the current Conductor instance advances to a new beat.
|
||||||
|
*/
|
||||||
|
public static var beatHit(default, null):FlxSignal = new FlxSignal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal fired when the current Conductor instance advances to a new step.
|
||||||
|
*/
|
||||||
|
public static var stepHit(default, null):FlxSignal = new FlxSignal();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of time changes in the song.
|
* 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.
|
* There should be at least one time change (at the beginning of the song) to define the BPM.
|
||||||
*/
|
*/
|
||||||
static var timeChanges:Array<SongTimeChange> = [];
|
var timeChanges:Array<SongTimeChange> = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent time change for the current song position.
|
* The most recent time change for the current song position.
|
||||||
*/
|
*/
|
||||||
public static var currentTimeChange(default, null):Null<SongTimeChange>;
|
public var currentTimeChange(default, null):Null<SongTimeChange>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current position in the song in milliseconds.
|
* The current position in the song in milliseconds.
|
||||||
* Update this every frame based on the audio position using `Conductor.update()`.
|
* Update this every frame based on the audio position using `Conductor.instance.update()`.
|
||||||
*/
|
*/
|
||||||
public static var songPosition(default, null):Float = 0;
|
public var songPosition(default, null):Float = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Beats per minute of the current song at the current time.
|
* Beats per minute of the current song at the current time.
|
||||||
*/
|
*/
|
||||||
public static var bpm(get, never):Float;
|
public var bpm(get, never):Float;
|
||||||
|
|
||||||
static function get_bpm():Float
|
function get_bpm():Float
|
||||||
{
|
{
|
||||||
if (bpmOverride != null) return bpmOverride;
|
if (bpmOverride != null) return bpmOverride;
|
||||||
|
|
||||||
|
@ -62,9 +87,9 @@ class Conductor
|
||||||
/**
|
/**
|
||||||
* Beats per minute of the current song at the start time.
|
* Beats per minute of the current song at the start time.
|
||||||
*/
|
*/
|
||||||
public static var startingBPM(get, never):Float;
|
public var startingBPM(get, never):Float;
|
||||||
|
|
||||||
static function get_startingBPM():Float
|
function get_startingBPM():Float
|
||||||
{
|
{
|
||||||
if (bpmOverride != null) return bpmOverride;
|
if (bpmOverride != null) return bpmOverride;
|
||||||
|
|
||||||
|
@ -78,14 +103,14 @@ class Conductor
|
||||||
* The current value set by `forceBPM`.
|
* The current value set by `forceBPM`.
|
||||||
* If false, BPM is determined by time changes.
|
* If false, BPM is determined by time changes.
|
||||||
*/
|
*/
|
||||||
static var bpmOverride:Null<Float> = null;
|
var bpmOverride:Null<Float> = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duration of a measure in milliseconds. Calculated based on bpm.
|
* Duration of a measure in milliseconds. Calculated based on bpm.
|
||||||
*/
|
*/
|
||||||
public static var measureLengthMs(get, never):Float;
|
public var measureLengthMs(get, never):Float;
|
||||||
|
|
||||||
static function get_measureLengthMs():Float
|
function get_measureLengthMs():Float
|
||||||
{
|
{
|
||||||
return beatLengthMs * timeSignatureNumerator;
|
return beatLengthMs * timeSignatureNumerator;
|
||||||
}
|
}
|
||||||
|
@ -93,9 +118,9 @@ class Conductor
|
||||||
/**
|
/**
|
||||||
* Duration of a beat (quarter note) in milliseconds. Calculated based on bpm.
|
* Duration of a beat (quarter note) in milliseconds. Calculated based on bpm.
|
||||||
*/
|
*/
|
||||||
public static var beatLengthMs(get, never):Float;
|
public var beatLengthMs(get, never):Float;
|
||||||
|
|
||||||
static function get_beatLengthMs():Float
|
function get_beatLengthMs():Float
|
||||||
{
|
{
|
||||||
// Tied directly to BPM.
|
// Tied directly to BPM.
|
||||||
return ((Constants.SECS_PER_MIN / bpm) * Constants.MS_PER_SEC);
|
return ((Constants.SECS_PER_MIN / bpm) * Constants.MS_PER_SEC);
|
||||||
|
@ -104,25 +129,25 @@ class Conductor
|
||||||
/**
|
/**
|
||||||
* Duration of a step (sixtennth note) in milliseconds. Calculated based on bpm.
|
* Duration of a step (sixtennth note) in milliseconds. Calculated based on bpm.
|
||||||
*/
|
*/
|
||||||
public static var stepLengthMs(get, never):Float;
|
public var stepLengthMs(get, never):Float;
|
||||||
|
|
||||||
static function get_stepLengthMs():Float
|
function get_stepLengthMs():Float
|
||||||
{
|
{
|
||||||
return beatLengthMs / timeSignatureNumerator;
|
return beatLengthMs / timeSignatureNumerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var timeSignatureNumerator(get, never):Int;
|
public var timeSignatureNumerator(get, never):Int;
|
||||||
|
|
||||||
static function get_timeSignatureNumerator():Int
|
function get_timeSignatureNumerator():Int
|
||||||
{
|
{
|
||||||
if (currentTimeChange == null) return Constants.DEFAULT_TIME_SIGNATURE_NUM;
|
if (currentTimeChange == null) return Constants.DEFAULT_TIME_SIGNATURE_NUM;
|
||||||
|
|
||||||
return currentTimeChange.timeSignatureNum;
|
return currentTimeChange.timeSignatureNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var timeSignatureDenominator(get, never):Int;
|
public var timeSignatureDenominator(get, never):Int;
|
||||||
|
|
||||||
static function get_timeSignatureDenominator():Int
|
function get_timeSignatureDenominator():Int
|
||||||
{
|
{
|
||||||
if (currentTimeChange == null) return Constants.DEFAULT_TIME_SIGNATURE_DEN;
|
if (currentTimeChange == null) return Constants.DEFAULT_TIME_SIGNATURE_DEN;
|
||||||
|
|
||||||
|
@ -132,44 +157,44 @@ class Conductor
|
||||||
/**
|
/**
|
||||||
* Current position in the song, in measures.
|
* Current position in the song, in measures.
|
||||||
*/
|
*/
|
||||||
public static var currentMeasure(default, null):Int = 0;
|
public var currentMeasure(default, null):Int = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current position in the song, in beats.
|
* Current position in the song, in beats.
|
||||||
*/
|
*/
|
||||||
public static var currentBeat(default, null):Int = 0;
|
public var currentBeat(default, null):Int = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current position in the song, in steps.
|
* Current position in the song, in steps.
|
||||||
*/
|
*/
|
||||||
public static var currentStep(default, null):Int = 0;
|
public var currentStep(default, null):Int = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current position in the song, in measures and fractions of a measure.
|
* Current position in the song, in measures and fractions of a measure.
|
||||||
*/
|
*/
|
||||||
public static var currentMeasureTime(default, null):Float = 0;
|
public var currentMeasureTime(default, null):Float = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current position in the song, in beats and fractions of a measure.
|
* Current position in the song, in beats and fractions of a measure.
|
||||||
*/
|
*/
|
||||||
public static var currentBeatTime(default, null):Float = 0;
|
public var currentBeatTime(default, null):Float = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current position in the song, in steps and fractions of a step.
|
* Current position in the song, in steps and fractions of a step.
|
||||||
*/
|
*/
|
||||||
public static var currentStepTime(default, null):Float = 0;
|
public var currentStepTime(default, null):Float = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An offset tied to the current chart file to compensate for a delay in the instrumental.
|
* An offset tied to the current chart file to compensate for a delay in the instrumental.
|
||||||
*/
|
*/
|
||||||
public static var instrumentalOffset:Float = 0;
|
public var instrumentalOffset:Float = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The instrumental offset, in terms of steps.
|
* The instrumental offset, in terms of steps.
|
||||||
*/
|
*/
|
||||||
public static var instrumentalOffsetSteps(get, never):Float;
|
public var instrumentalOffsetSteps(get, never):Float;
|
||||||
|
|
||||||
static function get_instrumentalOffsetSteps():Float
|
function get_instrumentalOffsetSteps():Float
|
||||||
{
|
{
|
||||||
var startingStepLengthMs:Float = ((Constants.SECS_PER_MIN / startingBPM) * Constants.MS_PER_SEC) / timeSignatureNumerator;
|
var startingStepLengthMs:Float = ((Constants.SECS_PER_MIN / startingBPM) * Constants.MS_PER_SEC) / timeSignatureNumerator;
|
||||||
|
|
||||||
|
@ -179,19 +204,19 @@ class Conductor
|
||||||
/**
|
/**
|
||||||
* An offset tied to the file format of the audio file being played.
|
* An offset tied to the file format of the audio file being played.
|
||||||
*/
|
*/
|
||||||
public static var formatOffset:Float = 0;
|
public var formatOffset:Float = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An offset set by the user to compensate for input lag.
|
* An offset set by the user to compensate for input lag.
|
||||||
*/
|
*/
|
||||||
public static var inputOffset:Float = 0;
|
public var inputOffset:Float = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of beats in a measure. May be fractional depending on the time signature.
|
* The number of beats in a measure. May be fractional depending on the time signature.
|
||||||
*/
|
*/
|
||||||
public static var beatsPerMeasure(get, never):Float;
|
public var beatsPerMeasure(get, never):Float;
|
||||||
|
|
||||||
static function get_beatsPerMeasure():Float
|
function get_beatsPerMeasure():Float
|
||||||
{
|
{
|
||||||
// NOTE: Not always an integer, for example 7/8 is 3.5 beats per measure
|
// NOTE: Not always an integer, for example 7/8 is 3.5 beats per measure
|
||||||
return stepsPerMeasure / Constants.STEPS_PER_BEAT;
|
return stepsPerMeasure / Constants.STEPS_PER_BEAT;
|
||||||
|
@ -201,30 +226,15 @@ class Conductor
|
||||||
* The number of steps in a measure.
|
* The number of steps in a measure.
|
||||||
* TODO: I don't think this can be fractional?
|
* TODO: I don't think this can be fractional?
|
||||||
*/
|
*/
|
||||||
public static var stepsPerMeasure(get, never):Int;
|
public var stepsPerMeasure(get, never):Int;
|
||||||
|
|
||||||
static function get_stepsPerMeasure():Int
|
function get_stepsPerMeasure():Int
|
||||||
{
|
{
|
||||||
// TODO: Is this always an integer?
|
// TODO: Is this always an integer?
|
||||||
return Std.int(timeSignatureNumerator / timeSignatureDenominator * Constants.STEPS_PER_BEAT * Constants.STEPS_PER_BEAT);
|
return Std.int(timeSignatureNumerator / timeSignatureDenominator * Constants.STEPS_PER_BEAT * Constants.STEPS_PER_BEAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function new() {}
|
||||||
* Signal fired when the Conductor advances to a new measure.
|
|
||||||
*/
|
|
||||||
public static var measureHit(default, null):FlxSignal = new FlxSignal();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal fired when the Conductor advances to a new beat.
|
|
||||||
*/
|
|
||||||
public static var beatHit(default, null):FlxSignal = new FlxSignal();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal fired when the Conductor advances to a new step.
|
|
||||||
*/
|
|
||||||
public static var stepHit(default, null):FlxSignal = new FlxSignal();
|
|
||||||
|
|
||||||
function new() {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forcibly defines the current BPM of the song.
|
* Forcibly defines the current BPM of the song.
|
||||||
|
@ -235,7 +245,7 @@ class Conductor
|
||||||
* WARNING: Avoid this for things like setting the BPM of the title screen music,
|
* WARNING: Avoid this for things like setting the BPM of the title screen music,
|
||||||
* you should have a metadata file for it instead.
|
* you should have a metadata file for it instead.
|
||||||
*/
|
*/
|
||||||
public static function forceBPM(?bpm:Float = null)
|
public function forceBPM(?bpm:Float = null)
|
||||||
{
|
{
|
||||||
if (bpm != null)
|
if (bpm != null)
|
||||||
{
|
{
|
||||||
|
@ -246,7 +256,7 @@ class Conductor
|
||||||
// trace('[CONDUCTOR] Resetting BPM to default');
|
// trace('[CONDUCTOR] Resetting BPM to default');
|
||||||
}
|
}
|
||||||
|
|
||||||
Conductor.bpmOverride = bpm;
|
this.bpmOverride = bpm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -256,29 +266,29 @@ class Conductor
|
||||||
* @param songPosition The current position in the song in milliseconds.
|
* @param songPosition The current position in the song in milliseconds.
|
||||||
* Leave blank to use the FlxG.sound.music position.
|
* Leave blank to use the FlxG.sound.music position.
|
||||||
*/
|
*/
|
||||||
public static function update(?songPosition:Float)
|
public function update(?songPos:Float)
|
||||||
{
|
{
|
||||||
if (songPosition == null)
|
if (songPos == null)
|
||||||
{
|
{
|
||||||
// Take into account instrumental and file format song offsets.
|
// Take into account instrumental and file format song offsets.
|
||||||
songPosition = (FlxG.sound.music != null) ? (FlxG.sound.music.time + instrumentalOffset + formatOffset) : 0.0;
|
songPos = (FlxG.sound.music != null) ? (FlxG.sound.music.time + instrumentalOffset + formatOffset) : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldMeasure = currentMeasure;
|
var oldMeasure = this.currentMeasure;
|
||||||
var oldBeat = currentBeat;
|
var oldBeat = this.currentBeat;
|
||||||
var oldStep = currentStep;
|
var oldStep = this.currentStep;
|
||||||
|
|
||||||
// Set the song position we are at (for purposes of calculating note positions, etc).
|
// Set the song position we are at (for purposes of calculating note positions, etc).
|
||||||
Conductor.songPosition = songPosition;
|
this.songPosition = songPos;
|
||||||
|
|
||||||
currentTimeChange = timeChanges[0];
|
currentTimeChange = timeChanges[0];
|
||||||
if (Conductor.songPosition > 0.0)
|
if (this.songPosition > 0.0)
|
||||||
{
|
{
|
||||||
for (i in 0...timeChanges.length)
|
for (i in 0...timeChanges.length)
|
||||||
{
|
{
|
||||||
if (songPosition >= timeChanges[i].timeStamp) currentTimeChange = timeChanges[i];
|
if (this.songPosition >= timeChanges[i].timeStamp) currentTimeChange = timeChanges[i];
|
||||||
|
|
||||||
if (songPosition < timeChanges[i].timeStamp) break;
|
if (this.songPosition < timeChanges[i].timeStamp) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,45 +296,49 @@ class Conductor
|
||||||
{
|
{
|
||||||
trace('WARNING: Conductor is broken, timeChanges is empty.');
|
trace('WARNING: Conductor is broken, timeChanges is empty.');
|
||||||
}
|
}
|
||||||
else if (currentTimeChange != null && Conductor.songPosition > 0.0)
|
else if (currentTimeChange != null && this.songPosition > 0.0)
|
||||||
{
|
{
|
||||||
// roundDecimal prevents representing 8 as 7.9999999
|
// roundDecimal prevents representing 8 as 7.9999999
|
||||||
currentStepTime = FlxMath.roundDecimal((currentTimeChange.beatTime * 4) + (songPosition - currentTimeChange.timeStamp) / stepLengthMs, 6);
|
this.currentStepTime = FlxMath.roundDecimal((currentTimeChange.beatTime * 4) + (this.songPosition - currentTimeChange.timeStamp) / stepLengthMs, 6);
|
||||||
currentBeatTime = currentStepTime / Constants.STEPS_PER_BEAT;
|
this.currentBeatTime = currentStepTime / Constants.STEPS_PER_BEAT;
|
||||||
currentMeasureTime = currentStepTime / stepsPerMeasure;
|
this.currentMeasureTime = currentStepTime / stepsPerMeasure;
|
||||||
currentStep = Math.floor(currentStepTime);
|
this.currentStep = Math.floor(currentStepTime);
|
||||||
currentBeat = Math.floor(currentBeatTime);
|
this.currentBeat = Math.floor(currentBeatTime);
|
||||||
currentMeasure = Math.floor(currentMeasureTime);
|
this.currentMeasure = Math.floor(currentMeasureTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Assume a constant BPM equal to the forced value.
|
// Assume a constant BPM equal to the forced value.
|
||||||
currentStepTime = FlxMath.roundDecimal((songPosition / stepLengthMs), 4);
|
this.currentStepTime = FlxMath.roundDecimal((songPosition / stepLengthMs), 4);
|
||||||
currentBeatTime = currentStepTime / Constants.STEPS_PER_BEAT;
|
this.currentBeatTime = currentStepTime / Constants.STEPS_PER_BEAT;
|
||||||
currentMeasureTime = currentStepTime / stepsPerMeasure;
|
this.currentMeasureTime = currentStepTime / stepsPerMeasure;
|
||||||
currentStep = Math.floor(currentStepTime);
|
this.currentStep = Math.floor(currentStepTime);
|
||||||
currentBeat = Math.floor(currentBeatTime);
|
this.currentBeat = Math.floor(currentBeatTime);
|
||||||
currentMeasure = Math.floor(currentMeasureTime);
|
this.currentMeasure = Math.floor(currentMeasureTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlxSignals are really cool.
|
// Only fire the signal if we are THE Conductor.
|
||||||
if (currentStep != oldStep)
|
if (this == Conductor.instance)
|
||||||
{
|
{
|
||||||
stepHit.dispatch();
|
// FlxSignals are really cool.
|
||||||
}
|
if (currentStep != oldStep)
|
||||||
|
{
|
||||||
|
Conductor.stepHit.dispatch();
|
||||||
|
}
|
||||||
|
|
||||||
if (currentBeat != oldBeat)
|
if (currentBeat != oldBeat)
|
||||||
{
|
{
|
||||||
beatHit.dispatch();
|
Conductor.beatHit.dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentMeasure != oldMeasure)
|
if (currentMeasure != oldMeasure)
|
||||||
{
|
{
|
||||||
measureHit.dispatch();
|
Conductor.measureHit.dispatch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function mapTimeChanges(songTimeChanges:Array<SongTimeChange>)
|
public function mapTimeChanges(songTimeChanges:Array<SongTimeChange>)
|
||||||
{
|
{
|
||||||
timeChanges = [];
|
timeChanges = [];
|
||||||
|
|
||||||
|
@ -338,24 +352,21 @@ class Conductor
|
||||||
// Without any custom handling, `currentStepTime` becomes non-zero at `songPosition = 0`.
|
// Without any custom handling, `currentStepTime` becomes non-zero at `songPosition = 0`.
|
||||||
if (currentTimeChange.timeStamp < 0.0) currentTimeChange.timeStamp = 0.0;
|
if (currentTimeChange.timeStamp < 0.0) currentTimeChange.timeStamp = 0.0;
|
||||||
|
|
||||||
if (currentTimeChange.beatTime == null)
|
if (currentTimeChange.timeStamp <= 0.0)
|
||||||
{
|
{
|
||||||
if (currentTimeChange.timeStamp <= 0.0)
|
currentTimeChange.beatTime = 0.0;
|
||||||
{
|
}
|
||||||
currentTimeChange.beatTime = 0.0;
|
else
|
||||||
}
|
{
|
||||||
else
|
// Calculate the beat time of this timestamp.
|
||||||
{
|
currentTimeChange.beatTime = 0.0;
|
||||||
// Calculate the beat time of this timestamp.
|
|
||||||
currentTimeChange.beatTime = 0.0;
|
|
||||||
|
|
||||||
if (currentTimeChange.timeStamp > 0.0 && timeChanges.length > 0)
|
if (currentTimeChange.timeStamp > 0.0 && timeChanges.length > 0)
|
||||||
{
|
{
|
||||||
var prevTimeChange:SongTimeChange = timeChanges[timeChanges.length - 1];
|
var prevTimeChange:SongTimeChange = timeChanges[timeChanges.length - 1];
|
||||||
currentTimeChange.beatTime = FlxMath.roundDecimal(prevTimeChange.beatTime
|
currentTimeChange.beatTime = FlxMath.roundDecimal(prevTimeChange.beatTime
|
||||||
+ ((currentTimeChange.timeStamp - prevTimeChange.timeStamp) * prevTimeChange.bpm / Constants.SECS_PER_MIN / Constants.MS_PER_SEC),
|
+ ((currentTimeChange.timeStamp - prevTimeChange.timeStamp) * prevTimeChange.bpm / Constants.SECS_PER_MIN / Constants.MS_PER_SEC),
|
||||||
4);
|
4);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,13 +379,13 @@ class Conductor
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update currentStepTime
|
// Update currentStepTime
|
||||||
Conductor.update(Conductor.songPosition);
|
this.update(Conductor.instance.songPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a time in milliseconds, return a time in steps.
|
* Given a time in milliseconds, return a time in steps.
|
||||||
*/
|
*/
|
||||||
public static function getTimeInSteps(ms:Float):Float
|
public function getTimeInSteps(ms:Float):Float
|
||||||
{
|
{
|
||||||
if (timeChanges.length == 0)
|
if (timeChanges.length == 0)
|
||||||
{
|
{
|
||||||
|
@ -411,7 +422,7 @@ class Conductor
|
||||||
/**
|
/**
|
||||||
* Given a time in steps and fractional steps, return a time in milliseconds.
|
* Given a time in steps and fractional steps, return a time in milliseconds.
|
||||||
*/
|
*/
|
||||||
public static function getStepTimeInMs(stepTime:Float):Float
|
public function getStepTimeInMs(stepTime:Float):Float
|
||||||
{
|
{
|
||||||
if (timeChanges.length == 0)
|
if (timeChanges.length == 0)
|
||||||
{
|
{
|
||||||
|
@ -447,7 +458,7 @@ class Conductor
|
||||||
/**
|
/**
|
||||||
* Given a time in beats and fractional beats, return a time in milliseconds.
|
* Given a time in beats and fractional beats, return a time in milliseconds.
|
||||||
*/
|
*/
|
||||||
public static function getBeatTimeInMs(beatTime:Float):Float
|
public function getBeatTimeInMs(beatTime:Float):Float
|
||||||
{
|
{
|
||||||
if (timeChanges.length == 0)
|
if (timeChanges.length == 0)
|
||||||
{
|
{
|
||||||
|
@ -480,13 +491,20 @@ class Conductor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function watchQuick():Void
|
||||||
|
{
|
||||||
|
FlxG.watch.addQuick("songPosition", Conductor.instance.songPosition);
|
||||||
|
FlxG.watch.addQuick("bpm", Conductor.instance.bpm);
|
||||||
|
FlxG.watch.addQuick("currentMeasureTime", Conductor.instance.currentMeasureTime);
|
||||||
|
FlxG.watch.addQuick("currentBeatTime", Conductor.instance.currentBeatTime);
|
||||||
|
FlxG.watch.addQuick("currentStepTime", Conductor.instance.currentStepTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the Conductor, replacing the current instance with a fresh one.
|
||||||
|
*/
|
||||||
public static function reset():Void
|
public static function reset():Void
|
||||||
{
|
{
|
||||||
beatHit.removeAll();
|
Conductor.instance = new Conductor();
|
||||||
stepHit.removeAll();
|
|
||||||
|
|
||||||
mapTimeChanges([]);
|
|
||||||
forceBPM(null);
|
|
||||||
update(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import funkin.play.PlayStatePlaylist;
|
||||||
import openfl.display.BitmapData;
|
import openfl.display.BitmapData;
|
||||||
import funkin.data.level.LevelRegistry;
|
import funkin.data.level.LevelRegistry;
|
||||||
import funkin.data.notestyle.NoteStyleRegistry;
|
import funkin.data.notestyle.NoteStyleRegistry;
|
||||||
import funkin.data.event.SongEventData.SongEventParser;
|
import funkin.data.event.SongEventRegistry;
|
||||||
import funkin.play.cutscene.dialogue.ConversationDataParser;
|
import funkin.play.cutscene.dialogue.ConversationDataParser;
|
||||||
import funkin.play.cutscene.dialogue.DialogueBoxDataParser;
|
import funkin.play.cutscene.dialogue.DialogueBoxDataParser;
|
||||||
import funkin.play.cutscene.dialogue.SpeakerDataParser;
|
import funkin.play.cutscene.dialogue.SpeakerDataParser;
|
||||||
|
@ -197,6 +197,13 @@ class InitState extends FlxState
|
||||||
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
|
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
//
|
||||||
|
// FLIXEL PLUGINS
|
||||||
|
//
|
||||||
|
funkin.util.plugins.EvacuateDebugPlugin.initialize();
|
||||||
|
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
|
||||||
|
funkin.util.plugins.WatchPlugin.initialize();
|
||||||
|
|
||||||
//
|
//
|
||||||
// GAME DATA PARSING
|
// GAME DATA PARSING
|
||||||
//
|
//
|
||||||
|
@ -206,7 +213,7 @@ class InitState extends FlxState
|
||||||
SongRegistry.instance.loadEntries();
|
SongRegistry.instance.loadEntries();
|
||||||
LevelRegistry.instance.loadEntries();
|
LevelRegistry.instance.loadEntries();
|
||||||
NoteStyleRegistry.instance.loadEntries();
|
NoteStyleRegistry.instance.loadEntries();
|
||||||
SongEventParser.loadEventCache();
|
SongEventRegistry.loadEventCache();
|
||||||
ConversationDataParser.loadConversationCache();
|
ConversationDataParser.loadConversationCache();
|
||||||
DialogueBoxDataParser.loadDialogueBoxCache();
|
DialogueBoxDataParser.loadDialogueBoxCache();
|
||||||
SpeakerDataParser.loadSpeakerCache();
|
SpeakerDataParser.loadSpeakerCache();
|
||||||
|
|
|
@ -64,7 +64,7 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
|
|
||||||
if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, vis.numSamples));
|
if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, vis.numSamples));
|
||||||
else
|
else
|
||||||
remappedShit = Std.int(FlxMath.remapToRange(Conductor.songPosition, 0, vis.snd.length, 0, vis.numSamples));
|
remappedShit = Std.int(FlxMath.remapToRange(Conductor.instance.songPosition, 0, vis.snd.length, 0, vis.numSamples));
|
||||||
|
|
||||||
var fftSamples:Array<Float> = [];
|
var fftSamples:Array<Float> = [];
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ class SpectogramSprite extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
|
|
||||||
if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, numSamples));
|
if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, numSamples));
|
||||||
else
|
else
|
||||||
remappedShit = Std.int(FlxMath.remapToRange(Conductor.songPosition, 0, vis.snd.length, 0, numSamples));
|
remappedShit = Std.int(FlxMath.remapToRange(Conductor.instance.songPosition, 0, vis.snd.length, 0, numSamples));
|
||||||
|
|
||||||
var fftSamples:Array<Float> = [];
|
var fftSamples:Array<Float> = [];
|
||||||
var i = remappedShit;
|
var i = remappedShit;
|
||||||
|
@ -235,15 +235,15 @@ class SpectogramSprite extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, numSamples));
|
if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, numSamples));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (curTime == Conductor.songPosition)
|
if (curTime == Conductor.instance.songPosition)
|
||||||
{
|
{
|
||||||
wavOptimiz = 3;
|
wavOptimiz = 3;
|
||||||
return; // already did shit, so finishes function early
|
return; // already did shit, so finishes function early
|
||||||
}
|
}
|
||||||
|
|
||||||
curTime = Conductor.songPosition;
|
curTime = Conductor.instance.songPosition;
|
||||||
|
|
||||||
remappedShit = Std.int(FlxMath.remapToRange(Conductor.songPosition, 0, vis.snd.length, 0, numSamples));
|
remappedShit = Std.int(FlxMath.remapToRange(Conductor.instance.songPosition, 0, vis.snd.length, 0, numSamples));
|
||||||
}
|
}
|
||||||
|
|
||||||
wavOptimiz = 8;
|
wavOptimiz = 8;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package funkin.data.event;
|
package funkin.data.event;
|
||||||
|
|
||||||
import funkin.play.event.SongEvent;
|
import funkin.play.event.SongEvent;
|
||||||
import funkin.data.event.SongEventData.SongEventSchema;
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
import funkin.util.macro.ClassMacro;
|
import funkin.util.macro.ClassMacro;
|
||||||
import funkin.play.event.ScriptedSongEvent;
|
import funkin.play.event.ScriptedSongEvent;
|
||||||
|
@ -9,7 +9,7 @@ import funkin.play.event.ScriptedSongEvent;
|
||||||
/**
|
/**
|
||||||
* This class statically handles the parsing of internal and scripted song event handlers.
|
* This class statically handles the parsing of internal and scripted song event handlers.
|
||||||
*/
|
*/
|
||||||
class SongEventParser
|
class SongEventRegistry
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Every built-in event class must be added to this list.
|
* Every built-in event class must be added to this list.
|
||||||
|
@ -160,84 +160,3 @@ class SongEventParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum abstract SongEventFieldType(String) from String to String
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The STRING type will display as a text field.
|
|
||||||
*/
|
|
||||||
var STRING = "string";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The INTEGER type will display as a text field that only accepts numbers.
|
|
||||||
*/
|
|
||||||
var INTEGER = "integer";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The FLOAT type will display as a text field that only accepts numbers.
|
|
||||||
*/
|
|
||||||
var FLOAT = "float";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The BOOL type will display as a checkbox.
|
|
||||||
*/
|
|
||||||
var BOOL = "bool";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ENUM type will display as a dropdown.
|
|
||||||
* Make sure to specify the `keys` field in the schema.
|
|
||||||
*/
|
|
||||||
var ENUM = "enum";
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef SongEventSchemaField =
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name of the property as it should be saved in the event data.
|
|
||||||
*/
|
|
||||||
name:String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The title of the field to display in the UI.
|
|
||||||
*/
|
|
||||||
title:String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the field.
|
|
||||||
*/
|
|
||||||
type:SongEventFieldType,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used only for ENUM values.
|
|
||||||
* The key is the display name and the value is the actual value.
|
|
||||||
*/
|
|
||||||
?keys:Map<String, Dynamic>,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for INTEGER and FLOAT values.
|
|
||||||
* The minimum value that can be entered.
|
|
||||||
* @default No minimum
|
|
||||||
*/
|
|
||||||
?min:Float,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for INTEGER and FLOAT values.
|
|
||||||
* The maximum value that can be entered.
|
|
||||||
* @default No maximum
|
|
||||||
*/
|
|
||||||
?max:Float,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for INTEGER and FLOAT values.
|
|
||||||
* The step value that will be used when incrementing/decrementing the value.
|
|
||||||
* @default `0.1`
|
|
||||||
*/
|
|
||||||
?step:Float,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An optional default value for the field.
|
|
||||||
*/
|
|
||||||
?defaultValue:Dynamic,
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef SongEventSchema = Array<SongEventSchemaField>;
|
|
125
source/funkin/data/event/SongEventSchema.hx
Normal file
125
source/funkin/data/event/SongEventSchema.hx
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
package funkin.data.event;
|
||||||
|
|
||||||
|
import funkin.play.event.SongEvent;
|
||||||
|
import funkin.data.event.SongEventSchema;
|
||||||
|
import funkin.data.song.SongData.SongEventData;
|
||||||
|
import funkin.util.macro.ClassMacro;
|
||||||
|
import funkin.play.event.ScriptedSongEvent;
|
||||||
|
|
||||||
|
@:forward(name, tittlte, type, keys, min, max, step, defaultValue, iterator)
|
||||||
|
abstract SongEventSchema(SongEventSchemaRaw)
|
||||||
|
{
|
||||||
|
public function new(?fields:Array<SongEventSchemaField>)
|
||||||
|
{
|
||||||
|
this = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:arrayAccess
|
||||||
|
public inline function getByName(name:String):SongEventSchemaField
|
||||||
|
{
|
||||||
|
for (field in this)
|
||||||
|
{
|
||||||
|
if (field.name == name) return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFirstField():SongEventSchemaField
|
||||||
|
{
|
||||||
|
return this[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@:arrayAccess
|
||||||
|
public inline function get(key:Int)
|
||||||
|
{
|
||||||
|
return this[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
@:arrayAccess
|
||||||
|
public inline function arrayWrite(k:Int, v:SongEventSchemaField):SongEventSchemaField
|
||||||
|
{
|
||||||
|
return this[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef SongEventSchemaRaw = Array<SongEventSchemaField>;
|
||||||
|
|
||||||
|
typedef SongEventSchemaField =
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name of the property as it should be saved in the event data.
|
||||||
|
*/
|
||||||
|
name:String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The title of the field to display in the UI.
|
||||||
|
*/
|
||||||
|
title:String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the field.
|
||||||
|
*/
|
||||||
|
type:SongEventFieldType,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used only for ENUM values.
|
||||||
|
* The key is the display name and the value is the actual value.
|
||||||
|
*/
|
||||||
|
?keys:Map<String, Dynamic>,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for INTEGER and FLOAT values.
|
||||||
|
* The minimum value that can be entered.
|
||||||
|
* @default No minimum
|
||||||
|
*/
|
||||||
|
?min:Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for INTEGER and FLOAT values.
|
||||||
|
* The maximum value that can be entered.
|
||||||
|
* @default No maximum
|
||||||
|
*/
|
||||||
|
?max:Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for INTEGER and FLOAT values.
|
||||||
|
* The step value that will be used when incrementing/decrementing the value.
|
||||||
|
* @default `0.1`
|
||||||
|
*/
|
||||||
|
?step:Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional default value for the field.
|
||||||
|
*/
|
||||||
|
?defaultValue:Dynamic,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum abstract SongEventFieldType(String) from String to String
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The STRING type will display as a text field.
|
||||||
|
*/
|
||||||
|
var STRING = "string";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The INTEGER type will display as a text field that only accepts numbers.
|
||||||
|
*/
|
||||||
|
var INTEGER = "integer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FLOAT type will display as a text field that only accepts numbers.
|
||||||
|
*/
|
||||||
|
var FLOAT = "float";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BOOL type will display as a checkbox.
|
||||||
|
*/
|
||||||
|
var BOOL = "bool";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ENUM type will display as a dropdown.
|
||||||
|
* Make sure to specify the `keys` field in the schema.
|
||||||
|
*/
|
||||||
|
var ENUM = "enum";
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
package funkin.data.song;
|
package funkin.data.song;
|
||||||
|
|
||||||
|
import funkin.data.event.SongEventRegistry;
|
||||||
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.song.SongRegistry;
|
import funkin.data.song.SongRegistry;
|
||||||
import thx.semver.Version;
|
import thx.semver.Version;
|
||||||
|
import funkin.util.tools.ICloneable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data containing information about a song.
|
* Data containing information about a song.
|
||||||
|
@ -9,7 +12,7 @@ import thx.semver.Version;
|
||||||
* Data which is only necessary in-game should be stored in the SongChartData.
|
* Data which is only necessary in-game should be stored in the SongChartData.
|
||||||
*/
|
*/
|
||||||
@:nullSafety
|
@:nullSafety
|
||||||
class SongMetadata
|
class SongMetadata implements ICloneable<SongMetadata>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* A semantic versioning string for the song data format.
|
* A semantic versioning string for the song data format.
|
||||||
|
@ -84,16 +87,16 @@ class SongMetadata
|
||||||
* @param newVariation Set to a new variation ID to change the new metadata.
|
* @param newVariation Set to a new variation ID to change the new metadata.
|
||||||
* @return The cloned SongMetadata
|
* @return The cloned SongMetadata
|
||||||
*/
|
*/
|
||||||
public function clone(?newVariation:String = null):SongMetadata
|
public function clone():SongMetadata
|
||||||
{
|
{
|
||||||
var result:SongMetadata = new SongMetadata(this.songName, this.artist, newVariation == null ? this.variation : newVariation);
|
var result:SongMetadata = new SongMetadata(this.songName, this.artist, this.variation);
|
||||||
result.version = this.version;
|
result.version = this.version;
|
||||||
result.timeFormat = this.timeFormat;
|
result.timeFormat = this.timeFormat;
|
||||||
result.divisions = this.divisions;
|
result.divisions = this.divisions;
|
||||||
result.offsets = this.offsets;
|
result.offsets = this.offsets.clone();
|
||||||
result.timeChanges = this.timeChanges;
|
result.timeChanges = this.timeChanges.deepClone();
|
||||||
result.looped = this.looped;
|
result.looped = this.looped;
|
||||||
result.playData = this.playData;
|
result.playData = this.playData.clone();
|
||||||
result.generatedBy = this.generatedBy;
|
result.generatedBy = this.generatedBy;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -128,7 +131,7 @@ enum abstract SongTimeFormat(String) from String to String
|
||||||
var MILLISECONDS = 'ms';
|
var MILLISECONDS = 'ms';
|
||||||
}
|
}
|
||||||
|
|
||||||
class SongTimeChange
|
class SongTimeChange implements ICloneable<SongTimeChange>
|
||||||
{
|
{
|
||||||
public static final DEFAULT_SONGTIMECHANGE:SongTimeChange = new SongTimeChange(0, 100);
|
public static final DEFAULT_SONGTIMECHANGE:SongTimeChange = new SongTimeChange(0, 100);
|
||||||
|
|
||||||
|
@ -149,7 +152,7 @@ class SongTimeChange
|
||||||
*/
|
*/
|
||||||
@:optional
|
@:optional
|
||||||
@:alias("b")
|
@:alias("b")
|
||||||
public var beatTime:Null<Float>;
|
public var beatTime:Float;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quarter notes per minute (float). Cannot be empty in the first element of the list,
|
* Quarter notes per minute (float). Cannot be empty in the first element of the list,
|
||||||
|
@ -195,6 +198,11 @@ class SongTimeChange
|
||||||
this.beatTuplets = beatTuplets == null ? DEFAULT_BEAT_TUPLETS : beatTuplets;
|
this.beatTuplets = beatTuplets == null ? DEFAULT_BEAT_TUPLETS : beatTuplets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clone():SongTimeChange
|
||||||
|
{
|
||||||
|
return new SongTimeChange(this.timeStamp, this.bpm, this.timeSignatureNum, this.timeSignatureDen, this.beatTime, this.beatTuplets);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a string representation suitable for debugging.
|
* Produces a string representation suitable for debugging.
|
||||||
*/
|
*/
|
||||||
|
@ -209,7 +217,7 @@ class SongTimeChange
|
||||||
* These are intended to correct for issues with the chart, or with the song's audio (for example a 10ms delay before the song starts).
|
* These are intended to correct for issues with the chart, or with the song's audio (for example a 10ms delay before the song starts).
|
||||||
* This is independent of the offsets applied in the user's settings, which are applied after these offsets and intended to correct for the user's hardware.
|
* This is independent of the offsets applied in the user's settings, which are applied after these offsets and intended to correct for the user's hardware.
|
||||||
*/
|
*/
|
||||||
class SongOffsets
|
class SongOffsets implements ICloneable<SongOffsets>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The offset, in milliseconds, to apply to the song's instrumental relative to the chart.
|
* The offset, in milliseconds, to apply to the song's instrumental relative to the chart.
|
||||||
|
@ -279,6 +287,15 @@ class SongOffsets
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clone():SongOffsets
|
||||||
|
{
|
||||||
|
var result:SongOffsets = new SongOffsets(this.instrumental);
|
||||||
|
result.altInstrumentals = this.altInstrumentals.clone();
|
||||||
|
result.vocals = this.vocals.clone();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a string representation suitable for debugging.
|
* Produces a string representation suitable for debugging.
|
||||||
*/
|
*/
|
||||||
|
@ -292,7 +309,7 @@ class SongOffsets
|
||||||
* Metadata for a song only used for the music.
|
* Metadata for a song only used for the music.
|
||||||
* For example, the menu music.
|
* For example, the menu music.
|
||||||
*/
|
*/
|
||||||
class SongMusicData
|
class SongMusicData implements ICloneable<SongMusicData>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* A semantic versioning string for the song data format.
|
* A semantic versioning string for the song data format.
|
||||||
|
@ -346,13 +363,13 @@ class SongMusicData
|
||||||
this.variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
this.variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clone(?newVariation:String = null):SongMusicData
|
public function clone():SongMusicData
|
||||||
{
|
{
|
||||||
var result:SongMusicData = new SongMusicData(this.songName, this.artist, newVariation == null ? this.variation : newVariation);
|
var result:SongMusicData = new SongMusicData(this.songName, this.artist, this.variation);
|
||||||
result.version = this.version;
|
result.version = this.version;
|
||||||
result.timeFormat = this.timeFormat;
|
result.timeFormat = this.timeFormat;
|
||||||
result.divisions = this.divisions;
|
result.divisions = this.divisions;
|
||||||
result.timeChanges = this.timeChanges;
|
result.timeChanges = this.timeChanges.clone();
|
||||||
result.looped = this.looped;
|
result.looped = this.looped;
|
||||||
result.generatedBy = this.generatedBy;
|
result.generatedBy = this.generatedBy;
|
||||||
|
|
||||||
|
@ -368,7 +385,7 @@ class SongMusicData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SongPlayData
|
class SongPlayData implements ICloneable<SongPlayData>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The variations this song has. The associated metadata files should exist.
|
* The variations this song has. The associated metadata files should exist.
|
||||||
|
@ -417,6 +434,20 @@ class SongPlayData
|
||||||
ratings = new Map<String, Int>();
|
ratings = new Map<String, Int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clone():SongPlayData
|
||||||
|
{
|
||||||
|
var result:SongPlayData = new SongPlayData();
|
||||||
|
result.songVariations = this.songVariations.clone();
|
||||||
|
result.difficulties = this.difficulties.clone();
|
||||||
|
result.characters = this.characters.clone();
|
||||||
|
result.stage = this.stage;
|
||||||
|
result.noteStyle = this.noteStyle;
|
||||||
|
result.ratings = this.ratings.clone();
|
||||||
|
result.album = this.album;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a string representation suitable for debugging.
|
* Produces a string representation suitable for debugging.
|
||||||
*/
|
*/
|
||||||
|
@ -430,7 +461,7 @@ class SongPlayData
|
||||||
* Information about the characters used in this variation of the song.
|
* Information about the characters used in this variation of the song.
|
||||||
* Create a new variation if you want to change the characters.
|
* Create a new variation if you want to change the characters.
|
||||||
*/
|
*/
|
||||||
class SongCharacterData
|
class SongCharacterData implements ICloneable<SongCharacterData>
|
||||||
{
|
{
|
||||||
@:optional
|
@:optional
|
||||||
@:default('')
|
@:default('')
|
||||||
|
@ -460,6 +491,14 @@ class SongCharacterData
|
||||||
this.instrumental = instrumental;
|
this.instrumental = instrumental;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clone():SongCharacterData
|
||||||
|
{
|
||||||
|
var result:SongCharacterData = new SongCharacterData(this.player, this.girlfriend, this.opponent, this.instrumental);
|
||||||
|
result.altInstrumentals = this.altInstrumentals.clone();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a string representation suitable for debugging.
|
* Produces a string representation suitable for debugging.
|
||||||
*/
|
*/
|
||||||
|
@ -469,7 +508,7 @@ class SongCharacterData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SongChartData
|
class SongChartData implements ICloneable<SongChartData>
|
||||||
{
|
{
|
||||||
@:default(funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION)
|
@:default(funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION)
|
||||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||||
|
@ -539,6 +578,24 @@ class SongChartData
|
||||||
return writer.write(this, pretty ? ' ' : null);
|
return writer.write(this, pretty ? ' ' : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clone():SongChartData
|
||||||
|
{
|
||||||
|
// We have to manually perform the deep clone here because Map.deepClone() doesn't work.
|
||||||
|
var noteDataClone:Map<String, Array<SongNoteData>> = new Map<String, Array<SongNoteData>>();
|
||||||
|
for (key in this.notes.keys())
|
||||||
|
{
|
||||||
|
noteDataClone.set(key, this.notes.get(key).deepClone());
|
||||||
|
}
|
||||||
|
var eventDataClone:Array<SongEventData> = this.events.deepClone();
|
||||||
|
|
||||||
|
var result:SongChartData = new SongChartData(this.scrollSpeed.clone(), eventDataClone, noteDataClone);
|
||||||
|
result.version = this.version;
|
||||||
|
result.generatedBy = this.generatedBy;
|
||||||
|
result.variation = this.variation;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a string representation suitable for debugging.
|
* Produces a string representation suitable for debugging.
|
||||||
*/
|
*/
|
||||||
|
@ -548,7 +605,7 @@ class SongChartData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SongEventDataRaw
|
class SongEventDataRaw implements ICloneable<SongEventDataRaw>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The timestamp of the event. The timestamp is in the format of the song's time format.
|
* The timestamp of the event. The timestamp is in the format of the song's time format.
|
||||||
|
@ -602,14 +659,19 @@ class SongEventDataRaw
|
||||||
{
|
{
|
||||||
if (_stepTime != null && !force) return _stepTime;
|
if (_stepTime != null && !force) return _stepTime;
|
||||||
|
|
||||||
return _stepTime = Conductor.getTimeInSteps(this.time);
|
return _stepTime = Conductor.instance.getTimeInSteps(this.time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clone():SongEventDataRaw
|
||||||
|
{
|
||||||
|
return new SongEventDataRaw(this.time, this.event, this.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap SongEventData in an abstract so we can overload operators.
|
* Wrap SongEventData in an abstract so we can overload operators.
|
||||||
*/
|
*/
|
||||||
@:forward(time, event, value, activated, getStepTime)
|
@:forward(time, event, value, activated, getStepTime, clone)
|
||||||
abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataRaw
|
abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataRaw
|
||||||
{
|
{
|
||||||
public function new(time:Float, event:String, value:Dynamic = null)
|
public function new(time:Float, event:String, value:Dynamic = null)
|
||||||
|
@ -617,6 +679,33 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
||||||
this = new SongEventDataRaw(time, event, value);
|
this = new SongEventDataRaw(time, event, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public inline function valueAsStruct(?defaultKey:String = "key"):Dynamic
|
||||||
|
{
|
||||||
|
if (this.value == null) return {};
|
||||||
|
if (Std.isOfType(this.value, Array))
|
||||||
|
{
|
||||||
|
var result:haxe.DynamicAccess<Dynamic> = {};
|
||||||
|
result.set(defaultKey, this.value);
|
||||||
|
return cast result;
|
||||||
|
}
|
||||||
|
else if (Reflect.isObject(this.value))
|
||||||
|
{
|
||||||
|
// We enter this case if the value is a struct.
|
||||||
|
return cast this.value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result:haxe.DynamicAccess<Dynamic> = {};
|
||||||
|
result.set(defaultKey, this.value);
|
||||||
|
return cast result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getSchema():Null<SongEventSchema>
|
||||||
|
{
|
||||||
|
return SongEventRegistry.getEventSchema(this.event);
|
||||||
|
}
|
||||||
|
|
||||||
public inline function getDynamic(key:String):Null<Dynamic>
|
public inline function getDynamic(key:String):Null<Dynamic>
|
||||||
{
|
{
|
||||||
return this.value == null ? null : Reflect.field(this.value, key);
|
return this.value == null ? null : Reflect.field(this.value, key);
|
||||||
|
@ -662,11 +751,6 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
||||||
return this.value == null ? null : cast Reflect.field(this.value, key);
|
return this.value == null ? null : cast Reflect.field(this.value, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clone():SongEventData
|
|
||||||
{
|
|
||||||
return new SongEventData(this.time, this.event, this.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@:op(A == B)
|
@:op(A == B)
|
||||||
public function op_equals(other:SongEventData):Bool
|
public function op_equals(other:SongEventData):Bool
|
||||||
{
|
{
|
||||||
|
@ -712,7 +796,7 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SongNoteDataRaw
|
class SongNoteDataRaw implements ICloneable<SongNoteDataRaw>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The timestamp of the note. The timestamp is in the format of the song's time format.
|
* The timestamp of the note. The timestamp is in the format of the song's time format.
|
||||||
|
@ -796,7 +880,7 @@ class SongNoteDataRaw
|
||||||
{
|
{
|
||||||
if (_stepTime != null && !force) return _stepTime;
|
if (_stepTime != null && !force) return _stepTime;
|
||||||
|
|
||||||
return _stepTime = Conductor.getTimeInSteps(this.time);
|
return _stepTime = Conductor.instance.getTimeInSteps(this.time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@:jignored
|
@:jignored
|
||||||
|
@ -812,7 +896,7 @@ class SongNoteDataRaw
|
||||||
|
|
||||||
if (_stepLength != null && !force) return _stepLength;
|
if (_stepLength != null && !force) return _stepLength;
|
||||||
|
|
||||||
return _stepLength = Conductor.getTimeInSteps(this.time + this.length) - getStepTime();
|
return _stepLength = Conductor.instance.getTimeInSteps(this.time + this.length) - getStepTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setStepLength(value:Float):Void
|
public function setStepLength(value:Float):Void
|
||||||
|
@ -823,11 +907,16 @@ class SongNoteDataRaw
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var lengthMs:Float = Conductor.getStepTimeInMs(value) - this.time;
|
var lengthMs:Float = Conductor.instance.getStepTimeInMs(value) - this.time;
|
||||||
this.length = lengthMs;
|
this.length = lengthMs;
|
||||||
}
|
}
|
||||||
_stepLength = null;
|
_stepLength = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clone():SongNoteDataRaw
|
||||||
|
{
|
||||||
|
return new SongNoteDataRaw(this.time, this.data, this.length, this.kind);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,7 +8,7 @@ import funkin.play.stage.StageData;
|
||||||
import polymod.Polymod;
|
import polymod.Polymod;
|
||||||
import polymod.backends.PolymodAssets.PolymodAssetType;
|
import polymod.backends.PolymodAssets.PolymodAssetType;
|
||||||
import polymod.format.ParseRules.TextFileFormat;
|
import polymod.format.ParseRules.TextFileFormat;
|
||||||
import funkin.data.event.SongEventData.SongEventParser;
|
import funkin.data.event.SongEventRegistry;
|
||||||
import funkin.util.FileUtil;
|
import funkin.util.FileUtil;
|
||||||
import funkin.data.level.LevelRegistry;
|
import funkin.data.level.LevelRegistry;
|
||||||
import funkin.data.notestyle.NoteStyleRegistry;
|
import funkin.data.notestyle.NoteStyleRegistry;
|
||||||
|
@ -271,7 +271,7 @@ class PolymodHandler
|
||||||
SongRegistry.instance.loadEntries();
|
SongRegistry.instance.loadEntries();
|
||||||
LevelRegistry.instance.loadEntries();
|
LevelRegistry.instance.loadEntries();
|
||||||
NoteStyleRegistry.instance.loadEntries();
|
NoteStyleRegistry.instance.loadEntries();
|
||||||
SongEventParser.loadEventCache();
|
SongEventRegistry.loadEventCache();
|
||||||
ConversationDataParser.loadConversationCache();
|
ConversationDataParser.loadConversationCache();
|
||||||
DialogueBoxDataParser.loadDialogueBoxCache();
|
DialogueBoxDataParser.loadDialogueBoxCache();
|
||||||
SpeakerDataParser.loadSpeakerCache();
|
SpeakerDataParser.loadSpeakerCache();
|
||||||
|
|
|
@ -40,7 +40,7 @@ class Countdown
|
||||||
stopCountdown();
|
stopCountdown();
|
||||||
|
|
||||||
PlayState.instance.isInCountdown = true;
|
PlayState.instance.isInCountdown = true;
|
||||||
Conductor.update(PlayState.instance.startTimestamp + Conductor.beatLengthMs * -5);
|
Conductor.instance.update(PlayState.instance.startTimestamp + Conductor.instance.beatLengthMs * -5);
|
||||||
// Handle onBeatHit events manually
|
// Handle onBeatHit events manually
|
||||||
// @:privateAccess
|
// @:privateAccess
|
||||||
// PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0));
|
// PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0));
|
||||||
|
@ -48,7 +48,7 @@ class Countdown
|
||||||
// The timer function gets called based on the beat of the song.
|
// The timer function gets called based on the beat of the song.
|
||||||
countdownTimer = new FlxTimer();
|
countdownTimer = new FlxTimer();
|
||||||
|
|
||||||
countdownTimer.start(Conductor.beatLengthMs / 1000, function(tmr:FlxTimer) {
|
countdownTimer.start(Conductor.instance.beatLengthMs / 1000, function(tmr:FlxTimer) {
|
||||||
if (PlayState.instance == null)
|
if (PlayState.instance == null)
|
||||||
{
|
{
|
||||||
tmr.cancel();
|
tmr.cancel();
|
||||||
|
@ -158,7 +158,7 @@ class Countdown
|
||||||
{
|
{
|
||||||
stopCountdown();
|
stopCountdown();
|
||||||
// This will trigger PlayState.startSong()
|
// This will trigger PlayState.startSong()
|
||||||
Conductor.update(0);
|
Conductor.instance.update(0);
|
||||||
// PlayState.isInCountdown = false;
|
// PlayState.isInCountdown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ class Countdown
|
||||||
countdownSprite.screenCenter();
|
countdownSprite.screenCenter();
|
||||||
|
|
||||||
// Fade sprite in, then out, then destroy it.
|
// Fade sprite in, then out, then destroy it.
|
||||||
FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.beatLengthMs / 1000,
|
FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.instance.beatLengthMs / 1000,
|
||||||
{
|
{
|
||||||
ease: FlxEase.cubeInOut,
|
ease: FlxEase.cubeInOut,
|
||||||
onComplete: function(twn:FlxTween) {
|
onComplete: function(twn:FlxTween) {
|
||||||
|
|
|
@ -129,7 +129,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
gameOverMusic.stop();
|
gameOverMusic.stop();
|
||||||
|
|
||||||
// The conductor now represents the BPM of the game over music.
|
// The conductor now represents the BPM of the game over music.
|
||||||
Conductor.update(0);
|
Conductor.instance.update(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasStartedAnimation:Bool = false;
|
var hasStartedAnimation:Bool = false;
|
||||||
|
@ -204,7 +204,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
// Match the conductor to the music.
|
// Match the conductor to the music.
|
||||||
// This enables the stepHit and beatHit events.
|
// This enables the stepHit and beatHit events.
|
||||||
Conductor.update(gameOverMusic.time);
|
Conductor.instance.update(gameOverMusic.time);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,7 +42,7 @@ import funkin.play.cutscene.dialogue.Conversation;
|
||||||
import funkin.play.cutscene.dialogue.ConversationDataParser;
|
import funkin.play.cutscene.dialogue.ConversationDataParser;
|
||||||
import funkin.play.cutscene.VanillaCutscenes;
|
import funkin.play.cutscene.VanillaCutscenes;
|
||||||
import funkin.play.cutscene.VideoCutscene;
|
import funkin.play.cutscene.VideoCutscene;
|
||||||
import funkin.data.event.SongEventData.SongEventParser;
|
import funkin.data.event.SongEventRegistry;
|
||||||
import funkin.play.notes.NoteSprite;
|
import funkin.play.notes.NoteSprite;
|
||||||
import funkin.play.notes.NoteDirection;
|
import funkin.play.notes.NoteDirection;
|
||||||
import funkin.play.notes.Strumline;
|
import funkin.play.notes.Strumline;
|
||||||
|
@ -561,15 +561,15 @@ class PlayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the Conductor.
|
// Prepare the Conductor.
|
||||||
Conductor.forceBPM(null);
|
Conductor.instance.forceBPM(null);
|
||||||
|
|
||||||
if (currentChart.offsets != null)
|
if (currentChart.offsets != null)
|
||||||
{
|
{
|
||||||
Conductor.instrumentalOffset = currentChart.offsets.getInstrumentalOffset();
|
Conductor.instance.instrumentalOffset = currentChart.offsets.getInstrumentalOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Conductor.mapTimeChanges(currentChart.timeChanges);
|
Conductor.instance.mapTimeChanges(currentChart.timeChanges);
|
||||||
Conductor.update((Conductor.beatLengthMs * -5) + startTimestamp);
|
Conductor.instance.update((Conductor.instance.beatLengthMs * -5) + startTimestamp);
|
||||||
|
|
||||||
// The song is now loaded. We can continue to initialize the play state.
|
// The song is now loaded. We can continue to initialize the play state.
|
||||||
initCameras();
|
initCameras();
|
||||||
|
@ -734,7 +734,7 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
// Reset music properly.
|
// Reset music properly.
|
||||||
|
|
||||||
FlxG.sound.music.time = Math.max(0, startTimestamp - Conductor.instrumentalOffset);
|
FlxG.sound.music.time = Math.max(0, startTimestamp - Conductor.instance.instrumentalOffset);
|
||||||
FlxG.sound.music.pause();
|
FlxG.sound.music.pause();
|
||||||
|
|
||||||
if (!overrideMusic)
|
if (!overrideMusic)
|
||||||
|
@ -785,22 +785,22 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
if (isInCountdown)
|
if (isInCountdown)
|
||||||
{
|
{
|
||||||
Conductor.update(Conductor.songPosition + elapsed * 1000);
|
Conductor.instance.update(Conductor.instance.songPosition + elapsed * 1000);
|
||||||
if (Conductor.songPosition >= (startTimestamp)) startSong();
|
if (Conductor.instance.songPosition >= (startTimestamp)) startSong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Constants.EXT_SOUND == 'mp3')
|
if (Constants.EXT_SOUND == 'mp3')
|
||||||
{
|
{
|
||||||
Conductor.formatOffset = Constants.MP3_DELAY_MS;
|
Conductor.instance.formatOffset = Constants.MP3_DELAY_MS;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Conductor.formatOffset = 0.0;
|
Conductor.instance.formatOffset = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Conductor.update(); // Normal conductor update.
|
Conductor.instance.update(); // Normal conductor update.
|
||||||
}
|
}
|
||||||
|
|
||||||
var androidPause:Bool = false;
|
var androidPause:Bool = false;
|
||||||
|
@ -942,7 +942,7 @@ class PlayState extends MusicBeatSubState
|
||||||
// TODO: Check that these work even when songPosition is less than 0.
|
// TODO: Check that these work even when songPosition is less than 0.
|
||||||
if (songEvents != null && songEvents.length > 0)
|
if (songEvents != null && songEvents.length > 0)
|
||||||
{
|
{
|
||||||
var songEventsToActivate:Array<SongEventData> = SongEventParser.queryEvents(songEvents, Conductor.songPosition);
|
var songEventsToActivate:Array<SongEventData> = SongEventRegistry.queryEvents(songEvents, Conductor.instance.songPosition);
|
||||||
|
|
||||||
if (songEventsToActivate.length > 0)
|
if (songEventsToActivate.length > 0)
|
||||||
{
|
{
|
||||||
|
@ -950,7 +950,7 @@ class PlayState extends MusicBeatSubState
|
||||||
for (event in songEventsToActivate)
|
for (event in songEventsToActivate)
|
||||||
{
|
{
|
||||||
// If an event is trying to play, but it's over 5 seconds old, skip it.
|
// If an event is trying to play, but it's over 5 seconds old, skip it.
|
||||||
if (event.time - Conductor.songPosition < -5000)
|
if (event.time - Conductor.instance.songPosition < -5000)
|
||||||
{
|
{
|
||||||
event.activated = true;
|
event.activated = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -961,7 +961,7 @@ class PlayState extends MusicBeatSubState
|
||||||
// Calling event.cancelEvent() skips the event. Neat!
|
// Calling event.cancelEvent() skips the event. Neat!
|
||||||
if (!eventEvent.eventCanceled)
|
if (!eventEvent.eventCanceled)
|
||||||
{
|
{
|
||||||
SongEventParser.handleEvent(event);
|
SongEventRegistry.handleEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1052,7 +1052,7 @@ class PlayState extends MusicBeatSubState
|
||||||
if (startTimer.finished)
|
if (startTimer.finished)
|
||||||
{
|
{
|
||||||
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true,
|
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true,
|
||||||
currentSongLengthMs - Conductor.songPosition);
|
currentSongLengthMs - Conductor.instance.songPosition);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1076,12 +1076,12 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause)
|
if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause)
|
||||||
{
|
{
|
||||||
if (Conductor.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song
|
if (Conductor.instance.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song
|
||||||
+ ' ('
|
+ ' ('
|
||||||
+ storyDifficultyText
|
+ storyDifficultyText
|
||||||
+ ')', iconRPC, true,
|
+ ')', iconRPC, true,
|
||||||
currentSongLengthMs
|
currentSongLengthMs
|
||||||
- Conductor.songPosition);
|
- Conductor.instance.songPosition);
|
||||||
else
|
else
|
||||||
DiscordClient.changePresence(detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC);
|
DiscordClient.changePresence(detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC);
|
||||||
}
|
}
|
||||||
|
@ -1154,17 +1154,17 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
if (!startingSong
|
if (!startingSong
|
||||||
&& FlxG.sound.music != null
|
&& FlxG.sound.music != null
|
||||||
&& (Math.abs(FlxG.sound.music.time - (Conductor.songPosition + Conductor.instrumentalOffset)) > 200
|
&& (Math.abs(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 200
|
||||||
|| Math.abs(vocals.checkSyncError(Conductor.songPosition + Conductor.instrumentalOffset)) > 200))
|
|| Math.abs(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 200))
|
||||||
{
|
{
|
||||||
trace("VOCALS NEED RESYNC");
|
trace("VOCALS NEED RESYNC");
|
||||||
if (vocals != null) trace(vocals.checkSyncError(Conductor.songPosition + Conductor.instrumentalOffset));
|
if (vocals != null) trace(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset));
|
||||||
trace(FlxG.sound.music.time - (Conductor.songPosition + Conductor.instrumentalOffset));
|
trace(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset));
|
||||||
resyncVocals();
|
resyncVocals();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iconP1 != null) iconP1.onStepHit(Std.int(Conductor.currentStep));
|
if (iconP1 != null) iconP1.onStepHit(Std.int(Conductor.instance.currentStep));
|
||||||
if (iconP2 != null) iconP2.onStepHit(Std.int(Conductor.currentStep));
|
if (iconP2 != null) iconP2.onStepHit(Std.int(Conductor.instance.currentStep));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1185,14 +1185,14 @@ class PlayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only zoom camera if we are zoomed by less than 35%.
|
// Only zoom camera if we are zoomed by less than 35%.
|
||||||
if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.currentBeat % cameraZoomRate == 0)
|
if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.instance.currentBeat % cameraZoomRate == 0)
|
||||||
{
|
{
|
||||||
// Zoom camera in (1.5%)
|
// Zoom camera in (1.5%)
|
||||||
FlxG.camera.zoom += cameraZoomIntensity * defaultCameraZoom;
|
FlxG.camera.zoom += cameraZoomIntensity * defaultCameraZoom;
|
||||||
// Hud zooms double (3%)
|
// Hud zooms double (3%)
|
||||||
camHUD.zoom += hudCameraZoomIntensity * defaultHUDCameraZoom;
|
camHUD.zoom += hudCameraZoomIntensity * defaultHUDCameraZoom;
|
||||||
}
|
}
|
||||||
// trace('Not bopping camera: ${FlxG.camera.zoom} < ${(1.35 * defaultCameraZoom)} && ${cameraZoomRate} > 0 && ${Conductor.currentBeat} % ${cameraZoomRate} == ${Conductor.currentBeat % cameraZoomRate}}');
|
// trace('Not bopping camera: ${FlxG.camera.zoom} < ${(1.35 * defaultCameraZoom)} && ${cameraZoomRate} > 0 && ${Conductor.instance.currentBeat} % ${cameraZoomRate} == ${Conductor.instance.currentBeat % cameraZoomRate}}');
|
||||||
|
|
||||||
// That combo milestones that got spoiled that one time.
|
// That combo milestones that got spoiled that one time.
|
||||||
// Comes with NEAT visual and audio effects.
|
// Comes with NEAT visual and audio effects.
|
||||||
|
@ -1205,13 +1205,13 @@ class PlayState extends MusicBeatSubState
|
||||||
// TODO: Re-enable combo text (how to do this without sections?).
|
// TODO: Re-enable combo text (how to do this without sections?).
|
||||||
// if (currentSong != null)
|
// if (currentSong != null)
|
||||||
// {
|
// {
|
||||||
// shouldShowComboText = (Conductor.currentBeat % 8 == 7);
|
// shouldShowComboText = (Conductor.instance.currentBeat % 8 == 7);
|
||||||
// var daSection = .getSong()[Std.int(Conductor.currentBeat / 16)];
|
// var daSection = .getSong()[Std.int(Conductor.instance.currentBeat / 16)];
|
||||||
// shouldShowComboText = shouldShowComboText && (daSection != null && daSection.mustHitSection);
|
// shouldShowComboText = shouldShowComboText && (daSection != null && daSection.mustHitSection);
|
||||||
// shouldShowComboText = shouldShowComboText && (Highscore.tallies.combo > 5);
|
// shouldShowComboText = shouldShowComboText && (Highscore.tallies.combo > 5);
|
||||||
//
|
//
|
||||||
// var daNextSection = .getSong()[Std.int(Conductor.currentBeat / 16) + 1];
|
// var daNextSection = .getSong()[Std.int(Conductor.instance.currentBeat / 16) + 1];
|
||||||
// var isEndOfSong = .getSong().length < Std.int(Conductor.currentBeat / 16);
|
// var isEndOfSong = .getSong().length < Std.int(Conductor.instance.currentBeat / 16);
|
||||||
// shouldShowComboText = shouldShowComboText && (isEndOfSong || (daNextSection != null && !daNextSection.mustHitSection));
|
// shouldShowComboText = shouldShowComboText && (isEndOfSong || (daNextSection != null && !daNextSection.mustHitSection));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -1224,7 +1224,7 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
var frameShit:Float = (1 / 24) * 2; // equals 2 frames in the animation
|
var frameShit:Float = (1 / 24) * 2; // equals 2 frames in the animation
|
||||||
|
|
||||||
new FlxTimer().start(((Conductor.beatLengthMs / 1000) * 1.25) - frameShit, function(tmr) {
|
new FlxTimer().start(((Conductor.instance.beatLengthMs / 1000) * 1.25) - frameShit, function(tmr) {
|
||||||
animShit.forceFinish();
|
animShit.forceFinish();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1261,10 +1261,10 @@ class PlayState extends MusicBeatSubState
|
||||||
if (currentStage == null) return;
|
if (currentStage == null) return;
|
||||||
|
|
||||||
// TODO: Add HEY! song events to Tutorial.
|
// TODO: Add HEY! song events to Tutorial.
|
||||||
if (Conductor.currentBeat % 16 == 15
|
if (Conductor.instance.currentBeat % 16 == 15
|
||||||
&& currentStage.getDad().characterId == 'gf'
|
&& currentStage.getDad().characterId == 'gf'
|
||||||
&& Conductor.currentBeat > 16
|
&& Conductor.instance.currentBeat > 16
|
||||||
&& Conductor.currentBeat < 48)
|
&& Conductor.instance.currentBeat < 48)
|
||||||
{
|
{
|
||||||
currentStage.getBoyfriend().playAnimation('hey', true);
|
currentStage.getBoyfriend().playAnimation('hey', true);
|
||||||
currentStage.getDad().playAnimation('cheer', true);
|
currentStage.getDad().playAnimation('cheer', true);
|
||||||
|
@ -1575,7 +1575,7 @@ class PlayState extends MusicBeatSubState
|
||||||
trace('Song difficulty could not be loaded.');
|
trace('Song difficulty could not be loaded.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conductor.forceBPM(currentChart.getStartingBPM());
|
// Conductor.instance.forceBPM(currentChart.getStartingBPM());
|
||||||
|
|
||||||
if (!overrideMusic)
|
if (!overrideMusic)
|
||||||
{
|
{
|
||||||
|
@ -1607,7 +1607,7 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
// Reset song events.
|
// Reset song events.
|
||||||
songEvents = currentChart.getEvents();
|
songEvents = currentChart.getEvents();
|
||||||
SongEventParser.resetEvents(songEvents);
|
SongEventRegistry.resetEvents(songEvents);
|
||||||
|
|
||||||
// Reset the notes on each strumline.
|
// Reset the notes on each strumline.
|
||||||
var playerNoteData:Array<SongNoteData> = [];
|
var playerNoteData:Array<SongNoteData> = [];
|
||||||
|
@ -1706,7 +1706,7 @@ class PlayState extends MusicBeatSubState
|
||||||
FlxG.sound.music.onComplete = endSong;
|
FlxG.sound.music.onComplete = endSong;
|
||||||
// A negative instrumental offset means the song skips the first few milliseconds of the track.
|
// A negative instrumental offset means the song skips the first few milliseconds of the track.
|
||||||
// This just gets added into the startTimestamp behavior so we don't need to do anything extra.
|
// This just gets added into the startTimestamp behavior so we don't need to do anything extra.
|
||||||
FlxG.sound.music.time = startTimestamp - Conductor.instrumentalOffset;
|
FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
|
||||||
|
|
||||||
trace('Playing vocals...');
|
trace('Playing vocals...');
|
||||||
add(vocals);
|
add(vocals);
|
||||||
|
@ -1722,7 +1722,7 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
if (startTimestamp > 0)
|
if (startTimestamp > 0)
|
||||||
{
|
{
|
||||||
// FlxG.sound.music.time = startTimestamp - Conductor.instrumentalOffset;
|
// FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
|
||||||
handleSkippedNotes();
|
handleSkippedNotes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1800,7 +1800,7 @@ class PlayState extends MusicBeatSubState
|
||||||
var hitWindowCenter = note.strumTime;
|
var hitWindowCenter = note.strumTime;
|
||||||
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
|
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
|
||||||
|
|
||||||
if (Conductor.songPosition > hitWindowEnd)
|
if (Conductor.instance.songPosition > hitWindowEnd)
|
||||||
{
|
{
|
||||||
if (note.hasMissed) continue;
|
if (note.hasMissed) continue;
|
||||||
|
|
||||||
|
@ -1810,7 +1810,7 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true;
|
if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true;
|
||||||
}
|
}
|
||||||
else if (Conductor.songPosition > hitWindowCenter)
|
else if (Conductor.instance.songPosition > hitWindowCenter)
|
||||||
{
|
{
|
||||||
if (note.hasBeenHit) continue;
|
if (note.hasBeenHit) continue;
|
||||||
|
|
||||||
|
@ -1831,7 +1831,7 @@ class PlayState extends MusicBeatSubState
|
||||||
opponentStrumline.playNoteHoldCover(note.holdNoteSprite);
|
opponentStrumline.playNoteHoldCover(note.holdNoteSprite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Conductor.songPosition > hitWindowStart)
|
else if (Conductor.instance.songPosition > hitWindowStart)
|
||||||
{
|
{
|
||||||
if (note.hasBeenHit || note.hasMissed) continue;
|
if (note.hasBeenHit || note.hasMissed) continue;
|
||||||
|
|
||||||
|
@ -1877,14 +1877,14 @@ class PlayState extends MusicBeatSubState
|
||||||
var hitWindowCenter = note.strumTime;
|
var hitWindowCenter = note.strumTime;
|
||||||
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
|
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
|
||||||
|
|
||||||
if (Conductor.songPosition > hitWindowEnd)
|
if (Conductor.instance.songPosition > hitWindowEnd)
|
||||||
{
|
{
|
||||||
note.tooEarly = false;
|
note.tooEarly = false;
|
||||||
note.mayHit = false;
|
note.mayHit = false;
|
||||||
note.hasMissed = true;
|
note.hasMissed = true;
|
||||||
if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true;
|
if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true;
|
||||||
}
|
}
|
||||||
else if (Conductor.songPosition > hitWindowStart)
|
else if (Conductor.instance.songPosition > hitWindowStart)
|
||||||
{
|
{
|
||||||
note.tooEarly = false;
|
note.tooEarly = false;
|
||||||
note.mayHit = true;
|
note.mayHit = true;
|
||||||
|
@ -1951,7 +1951,7 @@ class PlayState extends MusicBeatSubState
|
||||||
if (note == null || note.hasBeenHit) continue;
|
if (note == null || note.hasBeenHit) continue;
|
||||||
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
|
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
|
||||||
|
|
||||||
if (Conductor.songPosition > hitWindowEnd)
|
if (Conductor.instance.songPosition > hitWindowEnd)
|
||||||
{
|
{
|
||||||
// We have passed this note.
|
// We have passed this note.
|
||||||
// Flag the note for deletion without actually penalizing the player.
|
// Flag the note for deletion without actually penalizing the player.
|
||||||
|
@ -2115,7 +2115,7 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
inputSpitter.push(
|
inputSpitter.push(
|
||||||
{
|
{
|
||||||
t: Std.int(Conductor.songPosition),
|
t: Std.int(Conductor.instance.songPosition),
|
||||||
d: indices[i],
|
d: indices[i],
|
||||||
l: 20
|
l: 20
|
||||||
});
|
});
|
||||||
|
@ -2125,7 +2125,7 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
inputSpitter.push(
|
inputSpitter.push(
|
||||||
{
|
{
|
||||||
t: Std.int(Conductor.songPosition),
|
t: Std.int(Conductor.instance.songPosition),
|
||||||
d: -1,
|
d: -1,
|
||||||
l: 20
|
l: 20
|
||||||
});
|
});
|
||||||
|
@ -2186,7 +2186,7 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
inputSpitter.push(
|
inputSpitter.push(
|
||||||
{
|
{
|
||||||
t: Std.int(Conductor.songPosition),
|
t: Std.int(Conductor.instance.songPosition),
|
||||||
d: indices[i],
|
d: indices[i],
|
||||||
l: 20
|
l: 20
|
||||||
});
|
});
|
||||||
|
@ -2275,7 +2275,7 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
// Get the offset and compensate for input latency.
|
// Get the offset and compensate for input latency.
|
||||||
// Round inward (trim remainder) for consistency.
|
// Round inward (trim remainder) for consistency.
|
||||||
var noteDiff:Int = Std.int(Conductor.songPosition - daNote.noteData.time - inputLatencyMs);
|
var noteDiff:Int = Std.int(Conductor.instance.songPosition - daNote.noteData.time - inputLatencyMs);
|
||||||
|
|
||||||
var score = Scoring.scoreNote(noteDiff, PBOT1);
|
var score = Scoring.scoreNote(noteDiff, PBOT1);
|
||||||
var daRating = Scoring.judgeNote(noteDiff, PBOT1);
|
var daRating = Scoring.judgeNote(noteDiff, PBOT1);
|
||||||
|
@ -2330,7 +2330,7 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
inputSpitter.push(
|
inputSpitter.push(
|
||||||
{
|
{
|
||||||
t: Std.int(Conductor.songPosition),
|
t: Std.int(Conductor.instance.songPosition),
|
||||||
d: indices[i],
|
d: indices[i],
|
||||||
l: 20
|
l: 20
|
||||||
});
|
});
|
||||||
|
@ -2340,7 +2340,7 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
inputSpitter.push(
|
inputSpitter.push(
|
||||||
{
|
{
|
||||||
t: Std.int(Conductor.songPosition),
|
t: Std.int(Conductor.instance.songPosition),
|
||||||
d: -1,
|
d: -1,
|
||||||
l: 20
|
l: 20
|
||||||
});
|
});
|
||||||
|
@ -2739,15 +2739,15 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
FlxG.sound.music.pause();
|
FlxG.sound.music.pause();
|
||||||
|
|
||||||
var targetTimeSteps:Float = Conductor.currentStepTime + (Conductor.timeSignatureNumerator * Constants.STEPS_PER_BEAT * sections);
|
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.timeSignatureNumerator * Constants.STEPS_PER_BEAT * sections);
|
||||||
var targetTimeMs:Float = Conductor.getStepTimeInMs(targetTimeSteps);
|
var targetTimeMs:Float = Conductor.instance.getStepTimeInMs(targetTimeSteps);
|
||||||
|
|
||||||
FlxG.sound.music.time = targetTimeMs;
|
FlxG.sound.music.time = targetTimeMs;
|
||||||
|
|
||||||
handleSkippedNotes();
|
handleSkippedNotes();
|
||||||
// regenNoteData(FlxG.sound.music.time);
|
// regenNoteData(FlxG.sound.music.time);
|
||||||
|
|
||||||
Conductor.update(FlxG.sound.music.time);
|
Conductor.instance.update(FlxG.sound.music.time);
|
||||||
|
|
||||||
resyncVocals();
|
resyncVocals();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!
|
// This lets you add frames to the end of the sing animation to ease back into the idle!
|
||||||
|
|
||||||
holdTimer += event.elapsed;
|
holdTimer += event.elapsed;
|
||||||
var singTimeSec:Float = singTimeSec * (Conductor.beatLengthMs * 0.001); // x beats, to ms.
|
var singTimeSec:Float = singTimeSec * (Conductor.instance.beatLengthMs * 0.001); // x beats, to ms.
|
||||||
|
|
||||||
if (getCurrentAnimation().endsWith('miss')) singTimeSec *= 2; // makes it feel more awkward when you miss
|
if (getCurrentAnimation().endsWith('miss')) singTimeSec *= 2; // makes it feel more awkward when you miss
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ class ComboMilestone extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
{
|
{
|
||||||
if (onScreenTime < 0.9)
|
if (onScreenTime < 0.9)
|
||||||
{
|
{
|
||||||
new FlxTimer().start((Conductor.beatLengthMs / 1000) * 0.25, function(tmr) {
|
new FlxTimer().start((Conductor.instance.beatLengthMs / 1000) * 0.25, function(tmr) {
|
||||||
forceFinish();
|
forceFinish();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
||||||
remove(rating, true);
|
remove(rating, true);
|
||||||
rating.destroy();
|
rating.destroy();
|
||||||
},
|
},
|
||||||
startDelay: Conductor.beatLengthMs * 0.001
|
startDelay: Conductor.instance.beatLengthMs * 0.001
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
||||||
remove(comboSpr, true);
|
remove(comboSpr, true);
|
||||||
comboSpr.destroy();
|
comboSpr.destroy();
|
||||||
},
|
},
|
||||||
startDelay: Conductor.beatLengthMs * 0.001
|
startDelay: Conductor.instance.beatLengthMs * 0.001
|
||||||
});
|
});
|
||||||
|
|
||||||
var seperatedScore:Array<Int> = [];
|
var seperatedScore:Array<Int> = [];
|
||||||
|
@ -157,7 +157,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
||||||
remove(numScore, true);
|
remove(numScore, true);
|
||||||
numScore.destroy();
|
numScore.destroy();
|
||||||
},
|
},
|
||||||
startDelay: Conductor.beatLengthMs * 0.002
|
startDelay: Conductor.instance.beatLengthMs * 0.002
|
||||||
});
|
});
|
||||||
|
|
||||||
daLoop++;
|
daLoop++;
|
||||||
|
|
|
@ -5,8 +5,8 @@ import funkin.data.song.SongData;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
// Data from the event schema
|
// Data from the event schema
|
||||||
import funkin.play.event.SongEvent;
|
import funkin.play.event.SongEvent;
|
||||||
import funkin.data.event.SongEventData.SongEventSchema;
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.event.SongEventData.SongEventFieldType;
|
import funkin.data.event.SongEventSchema.SongEventFieldType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a handler for a type of song event.
|
* This class represents a handler for a type of song event.
|
||||||
|
@ -132,7 +132,7 @@ class FocusCameraSongEvent extends SongEvent
|
||||||
*/
|
*/
|
||||||
public override function getEventSchema():SongEventSchema
|
public override function getEventSchema():SongEventSchema
|
||||||
{
|
{
|
||||||
return [
|
return new SongEventSchema([
|
||||||
{
|
{
|
||||||
name: "char",
|
name: "char",
|
||||||
title: "Character",
|
title: "Character",
|
||||||
|
@ -154,6 +154,6 @@ class FocusCameraSongEvent extends SongEvent
|
||||||
step: 10.0,
|
step: 10.0,
|
||||||
type: SongEventFieldType.FLOAT,
|
type: SongEventFieldType.FLOAT,
|
||||||
}
|
}
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import funkin.data.song.SongData;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
// Data from the event schema
|
// Data from the event schema
|
||||||
import funkin.play.event.SongEvent;
|
import funkin.play.event.SongEvent;
|
||||||
import funkin.data.event.SongEventData.SongEventSchema;
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.event.SongEventData.SongEventFieldType;
|
import funkin.data.event.SongEventSchema.SongEventFieldType;
|
||||||
|
|
||||||
class PlayAnimationSongEvent extends SongEvent
|
class PlayAnimationSongEvent extends SongEvent
|
||||||
{
|
{
|
||||||
|
@ -89,7 +89,7 @@ class PlayAnimationSongEvent extends SongEvent
|
||||||
*/
|
*/
|
||||||
public override function getEventSchema():SongEventSchema
|
public override function getEventSchema():SongEventSchema
|
||||||
{
|
{
|
||||||
return [
|
return new SongEventSchema([
|
||||||
{
|
{
|
||||||
name: 'target',
|
name: 'target',
|
||||||
title: 'Target',
|
title: 'Target',
|
||||||
|
@ -108,6 +108,6 @@ class PlayAnimationSongEvent extends SongEvent
|
||||||
type: SongEventFieldType.BOOL,
|
type: SongEventFieldType.BOOL,
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
}
|
}
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import funkin.data.song.SongData;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
// Data from the event schema
|
// Data from the event schema
|
||||||
import funkin.play.event.SongEvent;
|
import funkin.play.event.SongEvent;
|
||||||
import funkin.data.event.SongEventData.SongEventSchema;
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.event.SongEventData.SongEventFieldType;
|
import funkin.data.event.SongEventSchema.SongEventFieldType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a handler for configuring camera bop intensity and rate.
|
* This class represents a handler for configuring camera bop intensity and rate.
|
||||||
|
@ -72,7 +72,7 @@ class SetCameraBopSongEvent extends SongEvent
|
||||||
*/
|
*/
|
||||||
public override function getEventSchema():SongEventSchema
|
public override function getEventSchema():SongEventSchema
|
||||||
{
|
{
|
||||||
return [
|
return new SongEventSchema([
|
||||||
{
|
{
|
||||||
name: 'intensity',
|
name: 'intensity',
|
||||||
title: 'Intensity',
|
title: 'Intensity',
|
||||||
|
@ -87,6 +87,6 @@ class SetCameraBopSongEvent extends SongEvent
|
||||||
step: 1,
|
step: 1,
|
||||||
type: SongEventFieldType.INTEGER,
|
type: SongEventFieldType.INTEGER,
|
||||||
}
|
}
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package funkin.play.event;
|
package funkin.play.event;
|
||||||
|
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
import funkin.data.event.SongEventData.SongEventSchema;
|
import funkin.data.event.SongEventSchema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a handler for a type of song event.
|
* This class represents a handler for a type of song event.
|
||||||
|
|
|
@ -8,8 +8,8 @@ import funkin.data.song.SongData;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
// Data from the event schema
|
// Data from the event schema
|
||||||
import funkin.play.event.SongEvent;
|
import funkin.play.event.SongEvent;
|
||||||
import funkin.data.event.SongEventData.SongEventFieldType;
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.event.SongEventData.SongEventSchema;
|
import funkin.data.event.SongEventSchema.SongEventFieldType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a handler for camera zoom events.
|
* This class represents a handler for camera zoom events.
|
||||||
|
@ -79,7 +79,8 @@ class ZoomCameraSongEvent extends SongEvent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.stepLengthMs * duration / 1000), {ease: easeFunction});
|
FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.instance.stepLengthMs * duration / 1000),
|
||||||
|
{ease: easeFunction});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ class ZoomCameraSongEvent extends SongEvent
|
||||||
*/
|
*/
|
||||||
public override function getEventSchema():SongEventSchema
|
public override function getEventSchema():SongEventSchema
|
||||||
{
|
{
|
||||||
return [
|
return new SongEventSchema([
|
||||||
{
|
{
|
||||||
name: 'zoom',
|
name: 'zoom',
|
||||||
title: 'Zoom Level',
|
title: 'Zoom Level',
|
||||||
|
@ -145,6 +146,6 @@ class ZoomCameraSongEvent extends SongEvent
|
||||||
'Elastic In/Out' => 'elasticInOut',
|
'Elastic In/Out' => 'elasticInOut',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,7 +279,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
var vwoosh:Float = 1.0;
|
var vwoosh:Float = 1.0;
|
||||||
var scrollSpeed:Float = PlayState.instance?.currentChart?.scrollSpeed ?? 1.0;
|
var scrollSpeed:Float = PlayState.instance?.currentChart?.scrollSpeed ?? 1.0;
|
||||||
|
|
||||||
return Constants.PIXELS_PER_MS * (Conductor.songPosition - strumTime) * scrollSpeed * vwoosh * (Preferences.downscroll ? 1 : -1);
|
return Constants.PIXELS_PER_MS * (Conductor.instance.songPosition - strumTime) * scrollSpeed * vwoosh * (Preferences.downscroll ? 1 : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNotes():Void
|
function updateNotes():Void
|
||||||
|
@ -287,8 +287,8 @@ class Strumline extends FlxSpriteGroup
|
||||||
if (noteData.length == 0) return;
|
if (noteData.length == 0) return;
|
||||||
|
|
||||||
var songStart:Float = PlayState.instance?.startTimestamp ?? 0.0;
|
var songStart:Float = PlayState.instance?.startTimestamp ?? 0.0;
|
||||||
var hitWindowStart:Float = Conductor.songPosition - Constants.HIT_WINDOW_MS;
|
var hitWindowStart:Float = Conductor.instance.songPosition - Constants.HIT_WINDOW_MS;
|
||||||
var renderWindowStart:Float = Conductor.songPosition + RENDER_DISTANCE_MS;
|
var renderWindowStart:Float = Conductor.instance.songPosition + RENDER_DISTANCE_MS;
|
||||||
|
|
||||||
for (noteIndex in nextNoteIndex...noteData.length)
|
for (noteIndex in nextNoteIndex...noteData.length)
|
||||||
{
|
{
|
||||||
|
@ -335,7 +335,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
{
|
{
|
||||||
if (holdNote == null || !holdNote.alive) continue;
|
if (holdNote == null || !holdNote.alive) continue;
|
||||||
|
|
||||||
if (Conductor.songPosition > holdNote.strumTime && holdNote.hitNote && !holdNote.missedNote)
|
if (Conductor.instance.songPosition > holdNote.strumTime && holdNote.hitNote && !holdNote.missedNote)
|
||||||
{
|
{
|
||||||
if (isPlayer && !isKeyHeld(holdNote.noteDirection))
|
if (isPlayer && !isKeyHeld(holdNote.noteDirection))
|
||||||
{
|
{
|
||||||
|
@ -349,7 +349,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
|
|
||||||
var renderWindowEnd = holdNote.strumTime + holdNote.fullSustainLength + Constants.HIT_WINDOW_MS + RENDER_DISTANCE_MS / 8;
|
var renderWindowEnd = holdNote.strumTime + holdNote.fullSustainLength + Constants.HIT_WINDOW_MS + RENDER_DISTANCE_MS / 8;
|
||||||
|
|
||||||
if (holdNote.missedNote && Conductor.songPosition >= renderWindowEnd)
|
if (holdNote.missedNote && Conductor.instance.songPosition >= renderWindowEnd)
|
||||||
{
|
{
|
||||||
// Hold note is offscreen, kill it.
|
// Hold note is offscreen, kill it.
|
||||||
holdNote.visible = false;
|
holdNote.visible = false;
|
||||||
|
@ -399,13 +399,13 @@ class Strumline extends FlxSpriteGroup
|
||||||
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime, vwoosh) + yOffset + STRUMLINE_SIZE / 2;
|
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime, vwoosh) + yOffset + STRUMLINE_SIZE / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Conductor.songPosition > holdNote.strumTime && holdNote.hitNote)
|
else if (Conductor.instance.songPosition > holdNote.strumTime && holdNote.hitNote)
|
||||||
{
|
{
|
||||||
// Hold note is currently being hit, clip it off.
|
// Hold note is currently being hit, clip it off.
|
||||||
holdConfirm(holdNote.noteDirection);
|
holdConfirm(holdNote.noteDirection);
|
||||||
holdNote.visible = true;
|
holdNote.visible = true;
|
||||||
|
|
||||||
holdNote.sustainLength = (holdNote.strumTime + holdNote.fullSustainLength) - Conductor.songPosition;
|
holdNote.sustainLength = (holdNote.strumTime + holdNote.fullSustainLength) - Conductor.instance.songPosition;
|
||||||
|
|
||||||
if (holdNote.sustainLength <= 10)
|
if (holdNote.sustainLength <= 10)
|
||||||
{
|
{
|
||||||
|
|
|
@ -80,25 +80,11 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
|
||||||
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleQuickWatch():Void
|
|
||||||
{
|
|
||||||
// Display Conductor info in the watch window.
|
|
||||||
FlxG.watch.addQuick("songPosition", Conductor.songPosition);
|
|
||||||
FlxG.watch.addQuick("songPositionNoOffset", Conductor.songPosition + Conductor.instrumentalOffset);
|
|
||||||
FlxG.watch.addQuick("musicTime", FlxG.sound.music?.time ?? 0.0);
|
|
||||||
FlxG.watch.addQuick("bpm", Conductor.bpm);
|
|
||||||
FlxG.watch.addQuick("currentMeasureTime", Conductor.currentBeatTime);
|
|
||||||
FlxG.watch.addQuick("currentBeatTime", Conductor.currentBeatTime);
|
|
||||||
FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float)
|
||||||
{
|
{
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
handleControls();
|
handleControls();
|
||||||
handleFunctionControls();
|
|
||||||
handleQuickWatch();
|
|
||||||
|
|
||||||
dispatchEvent(new UpdateScriptEvent(elapsed));
|
dispatchEvent(new UpdateScriptEvent(elapsed));
|
||||||
}
|
}
|
||||||
|
@ -139,7 +125,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
|
||||||
|
|
||||||
public function stepHit():Bool
|
public function stepHit():Bool
|
||||||
{
|
{
|
||||||
var event = new SongTimeScriptEvent(SONG_STEP_HIT, Conductor.currentBeat, Conductor.currentStep);
|
var event = new SongTimeScriptEvent(SONG_STEP_HIT, Conductor.instance.currentBeat, Conductor.instance.currentStep);
|
||||||
|
|
||||||
dispatchEvent(event);
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
@ -150,7 +136,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
|
||||||
|
|
||||||
public function beatHit():Bool
|
public function beatHit():Bool
|
||||||
{
|
{
|
||||||
var event = new SongTimeScriptEvent(SONG_BEAT_HIT, Conductor.currentBeat, Conductor.currentStep);
|
var event = new SongTimeScriptEvent(SONG_BEAT_HIT, Conductor.instance.currentBeat, Conductor.instance.currentStep);
|
||||||
|
|
||||||
dispatchEvent(event);
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
|
|
@ -65,12 +65,8 @@ class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandl
|
||||||
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
||||||
|
|
||||||
// Display Conductor info in the watch window.
|
// Display Conductor info in the watch window.
|
||||||
FlxG.watch.addQuick("songPosition", Conductor.songPosition);
|
|
||||||
FlxG.watch.addQuick("musicTime", FlxG.sound.music?.time ?? 0.0);
|
FlxG.watch.addQuick("musicTime", FlxG.sound.music?.time ?? 0.0);
|
||||||
FlxG.watch.addQuick("bpm", Conductor.bpm);
|
Conductor.watchQuick();
|
||||||
FlxG.watch.addQuick("currentMeasureTime", Conductor.currentBeatTime);
|
|
||||||
FlxG.watch.addQuick("currentBeatTime", Conductor.currentBeatTime);
|
|
||||||
FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime);
|
|
||||||
|
|
||||||
dispatchEvent(new UpdateScriptEvent(elapsed));
|
dispatchEvent(new UpdateScriptEvent(elapsed));
|
||||||
}
|
}
|
||||||
|
@ -99,7 +95,7 @@ class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandl
|
||||||
*/
|
*/
|
||||||
public function stepHit():Bool
|
public function stepHit():Bool
|
||||||
{
|
{
|
||||||
var event:ScriptEvent = new SongTimeScriptEvent(SONG_STEP_HIT, Conductor.currentBeat, Conductor.currentStep);
|
var event:ScriptEvent = new SongTimeScriptEvent(SONG_STEP_HIT, Conductor.instance.currentBeat, Conductor.instance.currentStep);
|
||||||
|
|
||||||
dispatchEvent(event);
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
@ -115,7 +111,7 @@ class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandl
|
||||||
*/
|
*/
|
||||||
public function beatHit():Bool
|
public function beatHit():Bool
|
||||||
{
|
{
|
||||||
var event:ScriptEvent = new SongTimeScriptEvent(SONG_BEAT_HIT, Conductor.currentBeat, Conductor.currentStep);
|
var event:ScriptEvent = new SongTimeScriptEvent(SONG_BEAT_HIT, Conductor.instance.currentBeat, Conductor.instance.currentStep);
|
||||||
|
|
||||||
dispatchEvent(event);
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import flixel.system.FlxAssets.FlxSoundAsset;
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import flixel.tweens.misc.VarTween;
|
import flixel.tweens.misc.VarTween;
|
||||||
|
import haxe.ui.Toolkit;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.util.FlxSort;
|
import flixel.util.FlxSort;
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
|
@ -150,7 +151,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// Layouts
|
// Layouts
|
||||||
public static final CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/notedata');
|
public static final CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/notedata');
|
||||||
|
|
||||||
public static final CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/eventdata');
|
public static final CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/eventdata');
|
||||||
public static final CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:String = Paths.ui('chart-editor/toolbox/playtest-properties');
|
public static final CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:String = Paths.ui('chart-editor/toolbox/playtest-properties');
|
||||||
public static final CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/metadata');
|
public static final CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/metadata');
|
||||||
public static final CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:String = Paths.ui('chart-editor/toolbox/difficulty');
|
public static final CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:String = Paths.ui('chart-editor/toolbox/difficulty');
|
||||||
|
@ -276,13 +277,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
function get_songLengthInSteps():Float
|
function get_songLengthInSteps():Float
|
||||||
{
|
{
|
||||||
return Conductor.getTimeInSteps(songLengthInMs);
|
return Conductor.instance.getTimeInSteps(songLengthInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_songLengthInSteps(value:Float):Float
|
function set_songLengthInSteps(value:Float):Float
|
||||||
{
|
{
|
||||||
// Getting a reasonable result from setting songLengthInSteps requires that Conductor.mapBPMChanges be called first.
|
// Getting a reasonable result from setting songLengthInSteps requires that Conductor.instance.mapBPMChanges be called first.
|
||||||
songLengthInMs = Conductor.getStepTimeInMs(value);
|
songLengthInMs = Conductor.instance.getStepTimeInMs(value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,12 +399,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
function get_scrollPositionInMs():Float
|
function get_scrollPositionInMs():Float
|
||||||
{
|
{
|
||||||
return Conductor.getStepTimeInMs(scrollPositionInSteps);
|
return Conductor.instance.getStepTimeInMs(scrollPositionInSteps);
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_scrollPositionInMs(value:Float):Float
|
function set_scrollPositionInMs(value:Float):Float
|
||||||
{
|
{
|
||||||
scrollPositionInSteps = Conductor.getTimeInSteps(value);
|
scrollPositionInSteps = Conductor.instance.getTimeInSteps(value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,13 +458,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
function get_playheadPositionInMs():Float
|
function get_playheadPositionInMs():Float
|
||||||
{
|
{
|
||||||
if (audioVisGroup != null && audioVisGroup.playerVis != null)
|
if (audioVisGroup != null && audioVisGroup.playerVis != null)
|
||||||
audioVisGroup.playerVis.realtimeStartOffset = -Conductor.getStepTimeInMs(playheadPositionInSteps);
|
audioVisGroup.playerVis.realtimeStartOffset = -Conductor.instance.getStepTimeInMs(playheadPositionInSteps);
|
||||||
return Conductor.getStepTimeInMs(playheadPositionInSteps);
|
return Conductor.instance.getStepTimeInMs(playheadPositionInSteps);
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_playheadPositionInMs(value:Float):Float
|
function set_playheadPositionInMs(value:Float):Float
|
||||||
{
|
{
|
||||||
playheadPositionInSteps = Conductor.getTimeInSteps(value);
|
playheadPositionInSteps = Conductor.instance.getTimeInSteps(value);
|
||||||
|
|
||||||
if (audioVisGroup != null && audioVisGroup.playerVis != null) audioVisGroup.playerVis.realtimeStartOffset = -value;
|
if (audioVisGroup != null && audioVisGroup.playerVis != null) audioVisGroup.playerVis.realtimeStartOffset = -value;
|
||||||
return value;
|
return value;
|
||||||
|
@ -494,17 +495,17 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
/**
|
/**
|
||||||
* The note kind to use for notes being placed in the chart. Defaults to `''`.
|
* The note kind to use for notes being placed in the chart. Defaults to `''`.
|
||||||
*/
|
*/
|
||||||
var selectedNoteKind:String = '';
|
var noteKindToPlace:String = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event type to use for events being placed in the chart. Defaults to `''`.
|
* The event type to use for events being placed in the chart. Defaults to `''`.
|
||||||
*/
|
*/
|
||||||
var selectedEventKind:String = 'FocusCamera';
|
var eventKindToPlace:String = 'FocusCamera';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event data to use for events being placed in the chart.
|
* The event data to use for events being placed in the chart.
|
||||||
*/
|
*/
|
||||||
var selectedEventData:DynamicAccess<Dynamic> = {};
|
var eventDataToPlace:DynamicAccess<Dynamic> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The internal index of what note snapping value is in use.
|
* The internal index of what note snapping value is in use.
|
||||||
|
@ -878,6 +879,70 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
return Save.get().chartEditorHasBackup = value;
|
return Save.get().chartEditorHasBackup = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of previous working file paths.
|
||||||
|
* Also known as the "recent files" list.
|
||||||
|
* The first element is [null] if the current working file has not been saved anywhere yet.
|
||||||
|
*/
|
||||||
|
public var previousWorkingFilePaths(default, set):Array<Null<String>> = [null];
|
||||||
|
|
||||||
|
function set_previousWorkingFilePaths(value:Array<Null<String>>):Array<Null<String>>
|
||||||
|
{
|
||||||
|
// Called only when the WHOLE LIST is overridden.
|
||||||
|
previousWorkingFilePaths = value;
|
||||||
|
applyWindowTitle();
|
||||||
|
populateOpenRecentMenu();
|
||||||
|
applyCanQuickSave();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current file path which the chart editor is working with.
|
||||||
|
* If `null`, the current chart has not been saved yet.
|
||||||
|
*/
|
||||||
|
public var currentWorkingFilePath(get, set):Null<String>;
|
||||||
|
|
||||||
|
function get_currentWorkingFilePath():Null<String>
|
||||||
|
{
|
||||||
|
return previousWorkingFilePaths[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_currentWorkingFilePath(value:Null<String>):Null<String>
|
||||||
|
{
|
||||||
|
if (value == previousWorkingFilePaths[0]) return value;
|
||||||
|
|
||||||
|
if (previousWorkingFilePaths.contains(null))
|
||||||
|
{
|
||||||
|
// Filter all instances of `null` from the array.
|
||||||
|
previousWorkingFilePaths = previousWorkingFilePaths.filter(function(x:Null<String>):Bool {
|
||||||
|
return x != null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousWorkingFilePaths.contains(value))
|
||||||
|
{
|
||||||
|
// Move the path to the front of the list.
|
||||||
|
previousWorkingFilePaths.remove(value);
|
||||||
|
previousWorkingFilePaths.unshift(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add the path to the front of the list.
|
||||||
|
previousWorkingFilePaths.unshift(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (previousWorkingFilePaths.length > Constants.MAX_PREVIOUS_WORKING_FILES)
|
||||||
|
{
|
||||||
|
// Remove the last path in the list.
|
||||||
|
previousWorkingFilePaths.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
populateOpenRecentMenu();
|
||||||
|
applyWindowTitle();
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the difficulty tree view in the toolbox has been modified and needs to be updated.
|
* Whether the difficulty tree view in the toolbox has been modified and needs to be updated.
|
||||||
* This happens when we add/remove difficulties.
|
* This happens when we add/remove difficulties.
|
||||||
|
@ -907,6 +972,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
*/
|
*/
|
||||||
var commandHistoryDirty:Bool = true;
|
var commandHistoryDirty:Bool = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, we are currently in the process of quitting the chart editor.
|
||||||
|
* Skip any update functions as most of them will call a crash.
|
||||||
|
*/
|
||||||
|
var criticalFailure:Bool = false;
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1742,70 +1813,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
*/
|
*/
|
||||||
var params:Null<ChartEditorParams>;
|
var params:Null<ChartEditorParams>;
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of previous working file paths.
|
|
||||||
* Also known as the "recent files" list.
|
|
||||||
* The first element is [null] if the current working file has not been saved anywhere yet.
|
|
||||||
*/
|
|
||||||
public var previousWorkingFilePaths(default, set):Array<Null<String>> = [null];
|
|
||||||
|
|
||||||
function set_previousWorkingFilePaths(value:Array<Null<String>>):Array<Null<String>>
|
|
||||||
{
|
|
||||||
// Called only when the WHOLE LIST is overridden.
|
|
||||||
previousWorkingFilePaths = value;
|
|
||||||
applyWindowTitle();
|
|
||||||
populateOpenRecentMenu();
|
|
||||||
applyCanQuickSave();
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current file path which the chart editor is working with.
|
|
||||||
* If `null`, the current chart has not been saved yet.
|
|
||||||
*/
|
|
||||||
public var currentWorkingFilePath(get, set):Null<String>;
|
|
||||||
|
|
||||||
function get_currentWorkingFilePath():Null<String>
|
|
||||||
{
|
|
||||||
return previousWorkingFilePaths[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_currentWorkingFilePath(value:Null<String>):Null<String>
|
|
||||||
{
|
|
||||||
if (value == previousWorkingFilePaths[0]) return value;
|
|
||||||
|
|
||||||
if (previousWorkingFilePaths.contains(null))
|
|
||||||
{
|
|
||||||
// Filter all instances of `null` from the array.
|
|
||||||
previousWorkingFilePaths = previousWorkingFilePaths.filter(function(x:Null<String>):Bool {
|
|
||||||
return x != null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previousWorkingFilePaths.contains(value))
|
|
||||||
{
|
|
||||||
// Move the path to the front of the list.
|
|
||||||
previousWorkingFilePaths.remove(value);
|
|
||||||
previousWorkingFilePaths.unshift(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Add the path to the front of the list.
|
|
||||||
previousWorkingFilePaths.unshift(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (previousWorkingFilePaths.length > Constants.MAX_PREVIOUS_WORKING_FILES)
|
|
||||||
{
|
|
||||||
// Remove the last path in the list.
|
|
||||||
previousWorkingFilePaths.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
populateOpenRecentMenu();
|
|
||||||
applyWindowTitle();
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function new(?params:ChartEditorParams)
|
public function new(?params:ChartEditorParams)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
@ -1887,6 +1894,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
// Setup the onClick listeners for the UI after it's been created.
|
// Setup the onClick listeners for the UI after it's been created.
|
||||||
setupUIListeners();
|
setupUIListeners();
|
||||||
|
setupContextMenu();
|
||||||
setupTurboKeyHandlers();
|
setupTurboKeyHandlers();
|
||||||
|
|
||||||
setupAutoSave();
|
setupAutoSave();
|
||||||
|
@ -2432,13 +2440,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Conductor.currentTimeChange.bpm += 1;
|
Conductor.instance.currentTimeChange.bpm += 1;
|
||||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbarBPM.onRightClick = _ -> {
|
playbarBPM.onRightClick = _ -> {
|
||||||
Conductor.currentTimeChange.bpm -= 1;
|
Conductor.instance.currentTimeChange.bpm -= 1;
|
||||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2481,31 +2489,15 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
menubarItemUndo.onClick = _ -> undoLastCommand();
|
menubarItemUndo.onClick = _ -> undoLastCommand();
|
||||||
menubarItemRedo.onClick = _ -> redoLastCommand();
|
menubarItemRedo.onClick = _ -> redoLastCommand();
|
||||||
menubarItemCopy.onClick = function(_) {
|
menubarItemCopy.onClick = function(_) {
|
||||||
// Doesn't use a command because it's not undoable.
|
copySelection();
|
||||||
|
|
||||||
// Calculate a single time offset for all the notes and events.
|
|
||||||
var timeOffset:Null<Int> = currentNoteSelection.length > 0 ? Std.int(currentNoteSelection[0].time) : null;
|
|
||||||
if (currentEventSelection.length > 0)
|
|
||||||
{
|
|
||||||
if (timeOffset == null || currentEventSelection[0].time < timeOffset)
|
|
||||||
{
|
|
||||||
timeOffset = Std.int(currentEventSelection[0].time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SongDataUtils.writeItemsToClipboard(
|
|
||||||
{
|
|
||||||
notes: SongDataUtils.buildNoteClipboard(currentNoteSelection, timeOffset),
|
|
||||||
events: SongDataUtils.buildEventClipboard(currentEventSelection, timeOffset),
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
menubarItemCut.onClick = _ -> performCommand(new CutItemsCommand(currentNoteSelection, currentEventSelection));
|
menubarItemCut.onClick = _ -> performCommand(new CutItemsCommand(currentNoteSelection, currentEventSelection));
|
||||||
|
|
||||||
menubarItemPaste.onClick = _ -> {
|
menubarItemPaste.onClick = _ -> {
|
||||||
var targetMs:Float = scrollPositionInMs + playheadPositionInMs;
|
var targetMs:Float = scrollPositionInMs + playheadPositionInMs;
|
||||||
var targetStep:Float = Conductor.getTimeInSteps(targetMs);
|
var targetStep:Float = Conductor.instance.getTimeInSteps(targetMs);
|
||||||
var targetSnappedStep:Float = Math.floor(targetStep / noteSnapRatio) * noteSnapRatio;
|
var targetSnappedStep:Float = Math.floor(targetStep / noteSnapRatio) * noteSnapRatio;
|
||||||
var targetSnappedMs:Float = Conductor.getStepTimeInMs(targetSnappedStep);
|
var targetSnappedMs:Float = Conductor.instance.getStepTimeInMs(targetSnappedStep);
|
||||||
performCommand(new PasteItemsCommand(targetSnappedMs));
|
performCommand(new PasteItemsCommand(targetSnappedMs));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2659,7 +2651,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
menubarItemToggleToolboxDifficulty.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT, event.value);
|
menubarItemToggleToolboxDifficulty.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT, event.value);
|
||||||
menubarItemToggleToolboxMetadata.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT, event.value);
|
menubarItemToggleToolboxMetadata.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT, event.value);
|
||||||
menubarItemToggleToolboxNotes.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT, event.value);
|
menubarItemToggleToolboxNotes.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT, event.value);
|
||||||
menubarItemToggleToolboxEvents.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT, event.value);
|
menubarItemToggleToolboxEventData.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT, event.value);
|
||||||
menubarItemToggleToolboxPlaytestProperties.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT, event.value);
|
menubarItemToggleToolboxPlaytestProperties.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT, event.value);
|
||||||
menubarItemToggleToolboxPlayerPreview.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT, event.value);
|
menubarItemToggleToolboxPlayerPreview.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT, event.value);
|
||||||
menubarItemToggleToolboxOpponentPreview.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT, event.value);
|
menubarItemToggleToolboxOpponentPreview.onChange = event -> this.setToolboxState(CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT, event.value);
|
||||||
|
@ -2668,6 +2660,42 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// registerContextMenu(null, Paths.ui('chart-editor/context/test'));
|
// registerContextMenu(null, Paths.ui('chart-editor/context/test'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupContextMenu():Void
|
||||||
|
{
|
||||||
|
Screen.instance.registerEvent(MouseEvent.RIGHT_MOUSE_UP, function(e:MouseEvent) {
|
||||||
|
var xPos = e.screenX;
|
||||||
|
var yPos = e.screenY;
|
||||||
|
onContextMenu(xPos, yPos);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onContextMenu(xPos:Float, yPos:Float)
|
||||||
|
{
|
||||||
|
trace('User right clicked to open menu at (${xPos}, ${yPos})');
|
||||||
|
// this.openDefaultContextMenu(xPos, yPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copySelection():Void
|
||||||
|
{
|
||||||
|
// Doesn't use a command because it's not undoable.
|
||||||
|
|
||||||
|
// Calculate a single time offset for all the notes and events.
|
||||||
|
var timeOffset:Null<Int> = currentNoteSelection.length > 0 ? Std.int(currentNoteSelection[0].time) : null;
|
||||||
|
if (currentEventSelection.length > 0)
|
||||||
|
{
|
||||||
|
if (timeOffset == null || currentEventSelection[0].time < timeOffset)
|
||||||
|
{
|
||||||
|
timeOffset = Std.int(currentEventSelection[0].time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SongDataUtils.writeItemsToClipboard(
|
||||||
|
{
|
||||||
|
notes: SongDataUtils.buildNoteClipboard(currentNoteSelection, timeOffset),
|
||||||
|
events: SongDataUtils.buildEventClipboard(currentEventSelection, timeOffset),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize TurboKeyHandlers and add them to the state (so `update()` is called)
|
* Initialize TurboKeyHandlers and add them to the state (so `update()` is called)
|
||||||
* We can then probe `keyHandler.activated` to see if the key combo's action should be taken.
|
* We can then probe `keyHandler.activated` to see if the key combo's action should be taken.
|
||||||
|
@ -2699,10 +2727,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
saveDataDirty = false;
|
saveDataDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var displayAutosavePopup:Bool = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UPDATE FUNCTIONS
|
* UPDATE FUNCTIONS
|
||||||
*/
|
*/
|
||||||
function autoSave():Void
|
function autoSave(?beforePlaytest:Bool = false):Void
|
||||||
{
|
{
|
||||||
var needsAutoSave:Bool = saveDataDirty;
|
var needsAutoSave:Bool = saveDataDirty;
|
||||||
|
|
||||||
|
@ -2720,13 +2750,21 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
if (needsAutoSave)
|
if (needsAutoSave)
|
||||||
{
|
{
|
||||||
this.exportAllSongData(true, null);
|
this.exportAllSongData(true, null);
|
||||||
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), ChartEditorImportExportHandler.BACKUPS_PATH]);
|
if (beforePlaytest)
|
||||||
this.infoWithActions('Auto-Save', 'Chart auto-saved to ${absoluteBackupsPath}.', [
|
{
|
||||||
{
|
displayAutosavePopup = true;
|
||||||
text: "Take Me There",
|
}
|
||||||
callback: openBackupsFolder,
|
else
|
||||||
}
|
{
|
||||||
]);
|
displayAutosavePopup = false;
|
||||||
|
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), ChartEditorImportExportHandler.BACKUPS_PATH]);
|
||||||
|
this.infoWithActions('Auto-Save', 'Chart auto-saved to ${absoluteBackupsPath}.', [
|
||||||
|
{
|
||||||
|
text: "Take Me There",
|
||||||
|
callback: openBackupsFolder,
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
@ -2794,7 +2832,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
public override function update(elapsed:Float):Void
|
public override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
// Override F4 behavior to include the autosave.
|
// Override F4 behavior to include the autosave.
|
||||||
if (FlxG.keys.justPressed.F4)
|
if (FlxG.keys.justPressed.F4 && !criticalFailure)
|
||||||
{
|
{
|
||||||
quitChartEditor();
|
quitChartEditor();
|
||||||
return;
|
return;
|
||||||
|
@ -2803,6 +2841,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// dispatchEvent gets called here.
|
// dispatchEvent gets called here.
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
|
if (criticalFailure) return;
|
||||||
|
|
||||||
// These ones happen even if the modal dialog is open.
|
// These ones happen even if the modal dialog is open.
|
||||||
handleMusicPlayback(elapsed);
|
handleMusicPlayback(elapsed);
|
||||||
handleNoteDisplay();
|
handleNoteDisplay();
|
||||||
|
@ -2842,9 +2882,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
if (metronomeVolume > 0.0 && this.subState == null && (audioInstTrack != null && audioInstTrack.isPlaying))
|
if (metronomeVolume > 0.0 && this.subState == null && (audioInstTrack != null && audioInstTrack.isPlaying))
|
||||||
{
|
{
|
||||||
playMetronomeTick(Conductor.currentBeat % Conductor.beatsPerMeasure == 0);
|
playMetronomeTick(Conductor.instance.currentBeat % Conductor.beatsPerMeasure == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show the mouse cursor.
|
||||||
|
// Just throwing this somewhere convenient and infrequently called because sometimes Flixel's debug thing hides the cursor.
|
||||||
|
Cursor.show();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2858,8 +2902,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
if (audioInstTrack != null && audioInstTrack.isPlaying)
|
if (audioInstTrack != null && audioInstTrack.isPlaying)
|
||||||
{
|
{
|
||||||
if (healthIconDad != null) healthIconDad.onStepHit(Conductor.currentStep);
|
if (healthIconDad != null) healthIconDad.onStepHit(Conductor.instance.currentStep);
|
||||||
if (healthIconBF != null) healthIconBF.onStepHit(Conductor.currentStep);
|
if (healthIconBF != null) healthIconBF.onStepHit(Conductor.instance.currentStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updating these every step keeps it more accurate.
|
// Updating these every step keeps it more accurate.
|
||||||
|
@ -2887,12 +2931,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
audioInstTrack.update(elapsed);
|
audioInstTrack.update(elapsed);
|
||||||
|
|
||||||
// If the song starts 50ms in, make sure we start the song there.
|
// If the song starts 50ms in, make sure we start the song there.
|
||||||
if (Conductor.instrumentalOffset < 0)
|
if (Conductor.instance.instrumentalOffset < 0)
|
||||||
{
|
{
|
||||||
if (audioInstTrack.time < -Conductor.instrumentalOffset)
|
if (audioInstTrack.time < -Conductor.instance.instrumentalOffset)
|
||||||
{
|
{
|
||||||
trace('Resetting instrumental time to ${- Conductor.instrumentalOffset}ms');
|
trace('Resetting instrumental time to ${- Conductor.instance.instrumentalOffset}ms');
|
||||||
audioInstTrack.time = -Conductor.instrumentalOffset;
|
audioInstTrack.time = -Conductor.instance.instrumentalOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2903,16 +2947,16 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
// If middle mouse panning during song playback, we move ONLY the playhead, without scrolling. Neat!
|
// If middle mouse panning during song playback, we move ONLY the playhead, without scrolling. Neat!
|
||||||
|
|
||||||
var oldStepTime:Float = Conductor.currentStepTime;
|
var oldStepTime:Float = Conductor.instance.currentStepTime;
|
||||||
var oldSongPosition:Float = Conductor.songPosition + Conductor.instrumentalOffset;
|
var oldSongPosition:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset;
|
||||||
Conductor.update(audioInstTrack.time);
|
Conductor.instance.update(audioInstTrack.time);
|
||||||
handleHitsounds(oldSongPosition, Conductor.songPosition + Conductor.instrumentalOffset);
|
handleHitsounds(oldSongPosition, Conductor.instance.songPosition + Conductor.instance.instrumentalOffset);
|
||||||
// Resync vocals.
|
// Resync vocals.
|
||||||
if (audioVocalTrackGroup != null && Math.abs(audioInstTrack.time - audioVocalTrackGroup.time) > 100)
|
if (audioVocalTrackGroup != null && Math.abs(audioInstTrack.time - audioVocalTrackGroup.time) > 100)
|
||||||
{
|
{
|
||||||
audioVocalTrackGroup.time = audioInstTrack.time;
|
audioVocalTrackGroup.time = audioInstTrack.time;
|
||||||
}
|
}
|
||||||
var diffStepTime:Float = Conductor.currentStepTime - oldStepTime;
|
var diffStepTime:Float = Conductor.instance.currentStepTime - oldStepTime;
|
||||||
|
|
||||||
// Move the playhead.
|
// Move the playhead.
|
||||||
playheadPositionInPixels += diffStepTime * GRID_SIZE;
|
playheadPositionInPixels += diffStepTime * GRID_SIZE;
|
||||||
|
@ -2922,9 +2966,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Else, move the entire view.
|
// Else, move the entire view.
|
||||||
var oldSongPosition:Float = Conductor.songPosition + Conductor.instrumentalOffset;
|
var oldSongPosition:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset;
|
||||||
Conductor.update(audioInstTrack.time);
|
Conductor.instance.update(audioInstTrack.time);
|
||||||
handleHitsounds(oldSongPosition, Conductor.songPosition + Conductor.instrumentalOffset);
|
handleHitsounds(oldSongPosition, Conductor.instance.songPosition + Conductor.instance.instrumentalOffset);
|
||||||
// Resync vocals.
|
// Resync vocals.
|
||||||
if (audioVocalTrackGroup != null && Math.abs(audioInstTrack.time - audioVocalTrackGroup.time) > 100)
|
if (audioVocalTrackGroup != null && Math.abs(audioInstTrack.time - audioVocalTrackGroup.time) > 100)
|
||||||
{
|
{
|
||||||
|
@ -2933,7 +2977,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
// We need time in fractional steps here to allow the song to actually play.
|
// We need time in fractional steps here to allow the song to actually play.
|
||||||
// Also account for a potentially offset playhead.
|
// Also account for a potentially offset playhead.
|
||||||
scrollPositionInPixels = (Conductor.currentStepTime + Conductor.instrumentalOffsetSteps) * GRID_SIZE - playheadPositionInPixels;
|
scrollPositionInPixels = (Conductor.instance.currentStepTime + Conductor.instance.instrumentalOffsetSteps) * GRID_SIZE - playheadPositionInPixels;
|
||||||
|
|
||||||
// DO NOT move song to scroll position here specifically.
|
// DO NOT move song to scroll position here specifically.
|
||||||
|
|
||||||
|
@ -3048,6 +3092,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
// Update the event sprite's position.
|
// Update the event sprite's position.
|
||||||
eventSprite.updateEventPosition(renderedEvents);
|
eventSprite.updateEventPosition(renderedEvents);
|
||||||
|
// Update the sprite's graphic. TODO: Is this inefficient?
|
||||||
|
eventSprite.playAnimation(eventSprite.eventData.event);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3062,8 +3108,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
// Let's try testing only notes within a certain range of the view area.
|
// Let's try testing only notes within a certain range of the view area.
|
||||||
// TODO: I don't think this messes up really long sustains, does it?
|
// TODO: I don't think this messes up really long sustains, does it?
|
||||||
var viewAreaTopMs:Float = scrollPositionInMs - (Conductor.measureLengthMs * 2); // Is 2 measures enough?
|
var viewAreaTopMs:Float = scrollPositionInMs - (Conductor.instance.measureLengthMs * 2); // Is 2 measures enough?
|
||||||
var viewAreaBottomMs:Float = scrollPositionInMs + (Conductor.measureLengthMs * 2); // Is 2 measures enough?
|
var viewAreaBottomMs:Float = scrollPositionInMs + (Conductor.instance.measureLengthMs * 2); // Is 2 measures enough?
|
||||||
|
|
||||||
// Add notes that are now visible.
|
// Add notes that are now visible.
|
||||||
for (noteData in currentSongChartNoteData)
|
for (noteData in currentSongChartNoteData)
|
||||||
|
@ -3350,14 +3396,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// PAGE UP = Jump up to nearest measure
|
// PAGE UP = Jump up to nearest measure
|
||||||
if (pageUpKeyHandler.activated)
|
if (pageUpKeyHandler.activated)
|
||||||
{
|
{
|
||||||
var measureHeight:Float = GRID_SIZE * 4 * Conductor.beatsPerMeasure;
|
var measureHeight:Float = GRID_SIZE * 4 * Conductor.instance.beatsPerMeasure;
|
||||||
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
|
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
|
||||||
var targetScrollPosition:Float = Math.floor(playheadPos / measureHeight) * measureHeight;
|
var targetScrollPosition:Float = Math.floor(playheadPos / measureHeight) * measureHeight;
|
||||||
// If we would move less than one grid, instead move to the top of the previous measure.
|
// If we would move less than one grid, instead move to the top of the previous measure.
|
||||||
var targetScrollAmount = Math.abs(targetScrollPosition - playheadPos);
|
var targetScrollAmount = Math.abs(targetScrollPosition - playheadPos);
|
||||||
if (targetScrollAmount < GRID_SIZE)
|
if (targetScrollAmount < GRID_SIZE)
|
||||||
{
|
{
|
||||||
targetScrollPosition -= GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.beatsPerMeasure;
|
targetScrollPosition -= GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.instance.beatsPerMeasure;
|
||||||
}
|
}
|
||||||
scrollAmount = targetScrollPosition - playheadPos;
|
scrollAmount = targetScrollPosition - playheadPos;
|
||||||
|
|
||||||
|
@ -3366,21 +3412,21 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
if (playbarButtonPressed == 'playbarBack')
|
if (playbarButtonPressed == 'playbarBack')
|
||||||
{
|
{
|
||||||
playbarButtonPressed = '';
|
playbarButtonPressed = '';
|
||||||
scrollAmount = -GRID_SIZE * 4 * Conductor.beatsPerMeasure;
|
scrollAmount = -GRID_SIZE * 4 * Conductor.instance.beatsPerMeasure;
|
||||||
shouldPause = true;
|
shouldPause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PAGE DOWN = Jump down to nearest measure
|
// PAGE DOWN = Jump down to nearest measure
|
||||||
if (pageDownKeyHandler.activated)
|
if (pageDownKeyHandler.activated)
|
||||||
{
|
{
|
||||||
var measureHeight:Float = GRID_SIZE * 4 * Conductor.beatsPerMeasure;
|
var measureHeight:Float = GRID_SIZE * 4 * Conductor.instance.beatsPerMeasure;
|
||||||
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
|
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
|
||||||
var targetScrollPosition:Float = Math.ceil(playheadPos / measureHeight) * measureHeight;
|
var targetScrollPosition:Float = Math.ceil(playheadPos / measureHeight) * measureHeight;
|
||||||
// If we would move less than one grid, instead move to the top of the next measure.
|
// If we would move less than one grid, instead move to the top of the next measure.
|
||||||
var targetScrollAmount = Math.abs(targetScrollPosition - playheadPos);
|
var targetScrollAmount = Math.abs(targetScrollPosition - playheadPos);
|
||||||
if (targetScrollAmount < GRID_SIZE)
|
if (targetScrollAmount < GRID_SIZE)
|
||||||
{
|
{
|
||||||
targetScrollPosition += GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.beatsPerMeasure;
|
targetScrollPosition += GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.instance.beatsPerMeasure;
|
||||||
}
|
}
|
||||||
scrollAmount = targetScrollPosition - playheadPos;
|
scrollAmount = targetScrollPosition - playheadPos;
|
||||||
|
|
||||||
|
@ -3389,7 +3435,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
if (playbarButtonPressed == 'playbarForward')
|
if (playbarButtonPressed == 'playbarForward')
|
||||||
{
|
{
|
||||||
playbarButtonPressed = '';
|
playbarButtonPressed = '';
|
||||||
scrollAmount = GRID_SIZE * 4 * Conductor.beatsPerMeasure;
|
scrollAmount = GRID_SIZE * 4 * Conductor.instance.beatsPerMeasure;
|
||||||
shouldPause = true;
|
shouldPause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3504,6 +3550,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
// trace('shouldHandleCursor: $shouldHandleCursor');
|
// trace('shouldHandleCursor: $shouldHandleCursor');
|
||||||
|
|
||||||
|
// TODO: TBH some of this should be using FlxMouseEventManager...
|
||||||
|
|
||||||
if (shouldHandleCursor)
|
if (shouldHandleCursor)
|
||||||
{
|
{
|
||||||
// Over the course of this big conditional block,
|
// Over the course of this big conditional block,
|
||||||
|
@ -3592,10 +3640,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
// The song position of the cursor, in steps.
|
// The song position of the cursor, in steps.
|
||||||
var cursorFractionalStep:Float = cursorY / GRID_SIZE;
|
var cursorFractionalStep:Float = cursorY / GRID_SIZE;
|
||||||
var cursorMs:Float = Conductor.getStepTimeInMs(cursorFractionalStep);
|
var cursorMs:Float = Conductor.instance.getStepTimeInMs(cursorFractionalStep);
|
||||||
// Round the cursor step to the nearest snap quant.
|
// Round the cursor step to the nearest snap quant.
|
||||||
var cursorSnappedStep:Float = Math.floor(cursorFractionalStep / noteSnapRatio) * noteSnapRatio;
|
var cursorSnappedStep:Float = Math.floor(cursorFractionalStep / noteSnapRatio) * noteSnapRatio;
|
||||||
var cursorSnappedMs:Float = Conductor.getStepTimeInMs(cursorSnappedStep);
|
var cursorSnappedMs:Float = Conductor.instance.getStepTimeInMs(cursorSnappedStep);
|
||||||
|
|
||||||
// The direction value for the column at the cursor.
|
// The direction value for the column at the cursor.
|
||||||
var cursorGridPos:Int = Math.floor(cursorX / GRID_SIZE);
|
var cursorGridPos:Int = Math.floor(cursorX / GRID_SIZE);
|
||||||
|
@ -3617,7 +3665,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// We released the mouse. Select the notes in the box.
|
// We released the mouse. Select the notes in the box.
|
||||||
var cursorFractionalStepStart:Float = cursorYStart / GRID_SIZE;
|
var cursorFractionalStepStart:Float = cursorYStart / GRID_SIZE;
|
||||||
var cursorStepStart:Int = Math.floor(cursorFractionalStepStart);
|
var cursorStepStart:Int = Math.floor(cursorFractionalStepStart);
|
||||||
var cursorMsStart:Float = Conductor.getStepTimeInMs(cursorStepStart);
|
var cursorMsStart:Float = Conductor.instance.getStepTimeInMs(cursorStepStart);
|
||||||
var cursorColumnBase:Int = Math.floor(cursorX / GRID_SIZE);
|
var cursorColumnBase:Int = Math.floor(cursorX / GRID_SIZE);
|
||||||
var cursorColumnBaseStart:Int = Math.floor(cursorXStart / GRID_SIZE);
|
var cursorColumnBaseStart:Int = Math.floor(cursorXStart / GRID_SIZE);
|
||||||
|
|
||||||
|
@ -3853,11 +3901,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
var dragDistanceMs:Float = 0;
|
var dragDistanceMs:Float = 0;
|
||||||
if (dragTargetNote != null && dragTargetNote.noteData != null)
|
if (dragTargetNote != null && dragTargetNote.noteData != null)
|
||||||
{
|
{
|
||||||
dragDistanceMs = Conductor.getStepTimeInMs(dragTargetNote.noteData.getStepTime() + dragDistanceSteps) - dragTargetNote.noteData.time;
|
dragDistanceMs = Conductor.instance.getStepTimeInMs(dragTargetNote.noteData.getStepTime() + dragDistanceSteps) - dragTargetNote.noteData.time;
|
||||||
}
|
}
|
||||||
else if (dragTargetEvent != null && dragTargetEvent.eventData != null)
|
else if (dragTargetEvent != null && dragTargetEvent.eventData != null)
|
||||||
{
|
{
|
||||||
dragDistanceMs = Conductor.getStepTimeInMs(dragTargetEvent.eventData.getStepTime() + dragDistanceSteps) - dragTargetEvent.eventData.time;
|
dragDistanceMs = Conductor.instance.getStepTimeInMs(dragTargetEvent.eventData.getStepTime() + dragDistanceSteps) - dragTargetEvent.eventData.time;
|
||||||
}
|
}
|
||||||
var dragDistanceColumns:Int = dragTargetCurrentColumn;
|
var dragDistanceColumns:Int = dragTargetCurrentColumn;
|
||||||
|
|
||||||
|
@ -3917,7 +3965,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
stepTime = dragTargetEvent.eventData.getStepTime();
|
stepTime = dragTargetEvent.eventData.getStepTime();
|
||||||
}
|
}
|
||||||
var dragDistanceSteps:Float = Conductor.getTimeInSteps(cursorSnappedMs).clamp(0, songLengthInSteps - (1 * noteSnapRatio)) - stepTime;
|
var dragDistanceSteps:Float = Conductor.instance.getTimeInSteps(cursorSnappedMs).clamp(0, songLengthInSteps - (1 * noteSnapRatio)) - stepTime;
|
||||||
var data:Int = 0;
|
var data:Int = 0;
|
||||||
var noteGridPos:Int = 0;
|
var noteGridPos:Int = 0;
|
||||||
if (dragTargetNote != null && dragTargetNote.noteData != null)
|
if (dragTargetNote != null && dragTargetNote.noteData != null)
|
||||||
|
@ -3949,8 +3997,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// Handle extending the note as you drag.
|
// Handle extending the note as you drag.
|
||||||
|
|
||||||
var stepTime:Float = inline currentPlaceNoteData.getStepTime();
|
var stepTime:Float = inline currentPlaceNoteData.getStepTime();
|
||||||
var dragLengthSteps:Float = Conductor.getTimeInSteps(cursorSnappedMs) - stepTime;
|
var dragLengthSteps:Float = Conductor.instance.getTimeInSteps(cursorSnappedMs) - stepTime;
|
||||||
var dragLengthMs:Float = dragLengthSteps * Conductor.stepLengthMs;
|
var dragLengthMs:Float = dragLengthSteps * Conductor.instance.stepLengthMs;
|
||||||
var dragLengthPixels:Float = dragLengthSteps * GRID_SIZE;
|
var dragLengthPixels:Float = dragLengthSteps * GRID_SIZE;
|
||||||
|
|
||||||
if (gridGhostNote != null && gridGhostNote.noteData != null && gridGhostHoldNote != null)
|
if (gridGhostNote != null && gridGhostNote.noteData != null && gridGhostHoldNote != null)
|
||||||
|
@ -4087,14 +4135,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
// Create an event and place it in the chart.
|
// Create an event and place it in the chart.
|
||||||
// TODO: Figure out configuring event data.
|
// TODO: Figure out configuring event data.
|
||||||
var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData.clone());
|
var newEventData:SongEventData = new SongEventData(cursorSnappedMs, eventKindToPlace, eventDataToPlace.clone());
|
||||||
|
|
||||||
performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL));
|
performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Create a note and place it in the chart.
|
// Create a note and place it in the chart.
|
||||||
var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind.clone());
|
var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, noteKindToPlace.clone());
|
||||||
|
|
||||||
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
|
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
|
||||||
|
|
||||||
|
@ -4132,13 +4180,52 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
if (highlightedNote != null && highlightedNote.noteData != null)
|
if (highlightedNote != null && highlightedNote.noteData != null)
|
||||||
{
|
{
|
||||||
// TODO: Handle the case of clicking on a sustain piece.
|
// TODO: Handle the case of clicking on a sustain piece.
|
||||||
// Remove the note.
|
if (FlxG.keys.pressed.SHIFT)
|
||||||
performCommand(new RemoveNotesCommand([highlightedNote.noteData]));
|
{
|
||||||
|
// Shift + Right click opens the context menu.
|
||||||
|
// If we are clicking a large selection, open the Selection context menu, otherwise open the Note context menu.
|
||||||
|
var isHighlightedNoteSelected:Bool = isNoteSelected(highlightedNote.noteData);
|
||||||
|
var useSingleNoteContextMenu:Bool = (!isHighlightedNoteSelected)
|
||||||
|
|| (isHighlightedNoteSelected && currentNoteSelection.length == 1);
|
||||||
|
// Show the context menu connected to the note.
|
||||||
|
if (useSingleNoteContextMenu)
|
||||||
|
{
|
||||||
|
this.openNoteContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedNote.noteData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Right click removes the note.
|
||||||
|
performCommand(new RemoveNotesCommand([highlightedNote.noteData]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (highlightedEvent != null && highlightedEvent.eventData != null)
|
else if (highlightedEvent != null && highlightedEvent.eventData != null)
|
||||||
{
|
{
|
||||||
// Remove the event.
|
if (FlxG.keys.pressed.SHIFT)
|
||||||
performCommand(new RemoveEventsCommand([highlightedEvent.eventData]));
|
{
|
||||||
|
// Shift + Right click opens the context menu.
|
||||||
|
// If we are clicking a large selection, open the Selection context menu, otherwise open the Event context menu.
|
||||||
|
var isHighlightedEventSelected:Bool = isEventSelected(highlightedEvent.eventData);
|
||||||
|
var useSingleEventContextMenu:Bool = (!isHighlightedEventSelected)
|
||||||
|
|| (isHighlightedEventSelected && currentEventSelection.length == 1);
|
||||||
|
if (useSingleEventContextMenu)
|
||||||
|
{
|
||||||
|
this.openEventContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedEvent.eventData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Right click removes the event.
|
||||||
|
performCommand(new RemoveEventsCommand([highlightedEvent.eventData]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4159,11 +4246,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
if (gridGhostEvent == null) throw "ERROR: Tried to handle cursor, but gridGhostEvent is null! Check ChartEditorState.buildGrid()";
|
if (gridGhostEvent == null) throw "ERROR: Tried to handle cursor, but gridGhostEvent is null! Check ChartEditorState.buildGrid()";
|
||||||
|
|
||||||
var eventData:SongEventData = gridGhostEvent.eventData != null ? gridGhostEvent.eventData : new SongEventData(cursorMs, selectedEventKind, null);
|
var eventData:SongEventData = gridGhostEvent.eventData != null ? gridGhostEvent.eventData : new SongEventData(cursorMs, eventKindToPlace, null);
|
||||||
|
|
||||||
if (selectedEventKind != eventData.event)
|
if (eventKindToPlace != eventData.event)
|
||||||
{
|
{
|
||||||
eventData.event = selectedEventKind;
|
eventData.event = eventKindToPlace;
|
||||||
}
|
}
|
||||||
eventData.time = cursorSnappedMs;
|
eventData.time = cursorSnappedMs;
|
||||||
|
|
||||||
|
@ -4179,11 +4266,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
if (gridGhostNote == null) throw "ERROR: Tried to handle cursor, but gridGhostNote is null! Check ChartEditorState.buildGrid()";
|
if (gridGhostNote == null) throw "ERROR: Tried to handle cursor, but gridGhostNote is null! Check ChartEditorState.buildGrid()";
|
||||||
|
|
||||||
var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, selectedNoteKind);
|
var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, noteKindToPlace);
|
||||||
|
|
||||||
if (cursorColumn != noteData.data || selectedNoteKind != noteData.kind)
|
if (cursorColumn != noteData.data || noteKindToPlace != noteData.kind)
|
||||||
{
|
{
|
||||||
noteData.kind = selectedNoteKind;
|
noteData.kind = noteKindToPlace;
|
||||||
noteData.data = cursorColumn;
|
noteData.data = cursorColumn;
|
||||||
gridGhostNote.playNoteAnimation();
|
gridGhostNote.playNoteAnimation();
|
||||||
}
|
}
|
||||||
|
@ -4411,7 +4498,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
if (playbarHeadLayout.playbarHead.value != songPosPercent) playbarHeadLayout.playbarHead.value = songPosPercent;
|
if (playbarHeadLayout.playbarHead.value != songPosPercent) playbarHeadLayout.playbarHead.value = songPosPercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
var songPos:Float = Conductor.songPosition + Conductor.instrumentalOffset;
|
var songPos:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset;
|
||||||
var songPosSeconds:String = Std.string(Math.floor((Math.abs(songPos) / 1000) % 60)).lpad('0', 2);
|
var songPosSeconds:String = Std.string(Math.floor((Math.abs(songPos) / 1000) % 60)).lpad('0', 2);
|
||||||
var songPosMinutes:String = Std.string(Math.floor((Math.abs(songPos) / 1000) / 60)).lpad('0', 2);
|
var songPosMinutes:String = Std.string(Math.floor((Math.abs(songPos) / 1000) / 60)).lpad('0', 2);
|
||||||
if (songPos < 0) songPosMinutes = '-' + songPosMinutes;
|
if (songPos < 0) songPosMinutes = '-' + songPosMinutes;
|
||||||
|
@ -4467,16 +4554,16 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
|
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
|
||||||
var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / noteSnapRatio;
|
var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / noteSnapRatio;
|
||||||
var playheadPosStep:Int = Std.int(Math.floor(playheadPosFractionalStep));
|
var playheadPosStep:Int = Std.int(Math.floor(playheadPosFractionalStep));
|
||||||
var playheadPosSnappedMs:Float = playheadPosStep * Conductor.stepLengthMs * noteSnapRatio;
|
var playheadPosSnappedMs:Float = playheadPosStep * Conductor.instance.stepLengthMs * noteSnapRatio;
|
||||||
|
|
||||||
// Look for notes within 1 step of the playhead.
|
// Look for notes within 1 step of the playhead.
|
||||||
var notesAtPos:Array<SongNoteData> = SongDataUtils.getNotesInTimeRange(currentSongChartNoteData, playheadPosSnappedMs,
|
var notesAtPos:Array<SongNoteData> = SongDataUtils.getNotesInTimeRange(currentSongChartNoteData, playheadPosSnappedMs,
|
||||||
playheadPosSnappedMs + Conductor.stepLengthMs * noteSnapRatio);
|
playheadPosSnappedMs + Conductor.instance.stepLengthMs * noteSnapRatio);
|
||||||
notesAtPos = SongDataUtils.getNotesWithData(notesAtPos, [column]);
|
notesAtPos = SongDataUtils.getNotesWithData(notesAtPos, [column]);
|
||||||
|
|
||||||
if (notesAtPos.length == 0)
|
if (notesAtPos.length == 0)
|
||||||
{
|
{
|
||||||
var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, selectedNoteKind);
|
var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, noteKindToPlace);
|
||||||
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
|
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -4578,6 +4665,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
FlxG.switchState(new MainMenuState());
|
FlxG.switchState(new MainMenuState());
|
||||||
|
|
||||||
resetWindowTitle();
|
resetWindowTitle();
|
||||||
|
|
||||||
|
criticalFailure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4668,9 +4757,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var targetMs:Float = scrollPositionInMs + playheadPositionInMs;
|
var targetMs:Float = scrollPositionInMs + playheadPositionInMs;
|
||||||
var targetStep:Float = Conductor.getTimeInSteps(targetMs);
|
var targetStep:Float = Conductor.instance.getTimeInSteps(targetMs);
|
||||||
var targetSnappedStep:Float = Math.floor(targetStep / noteSnapRatio) * noteSnapRatio;
|
var targetSnappedStep:Float = Math.floor(targetStep / noteSnapRatio) * noteSnapRatio;
|
||||||
var targetSnappedMs:Float = Conductor.getStepTimeInMs(targetSnappedStep);
|
var targetSnappedMs:Float = Conductor.instance.getStepTimeInMs(targetSnappedStep);
|
||||||
targetSnappedMs;
|
targetSnappedMs;
|
||||||
}
|
}
|
||||||
performCommand(new PasteItemsCommand(targetMs));
|
performCommand(new PasteItemsCommand(targetMs));
|
||||||
|
@ -4779,11 +4868,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
override function handleQuickWatch():Void
|
function handleQuickWatch():Void
|
||||||
{
|
{
|
||||||
super.handleQuickWatch();
|
FlxG.watch.addQuick('musicTime', audioInstTrack?.time ?? 0.0);
|
||||||
|
|
||||||
FlxG.watch.addQuick('musicTime', audioInstTrack?.time);
|
|
||||||
|
|
||||||
FlxG.watch.addQuick('scrollPosInPixels', scrollPositionInPixels);
|
FlxG.watch.addQuick('scrollPosInPixels', scrollPositionInPixels);
|
||||||
FlxG.watch.addQuick('playheadPosInPixels', playheadPositionInPixels);
|
FlxG.watch.addQuick('playheadPosInPixels', playheadPositionInPixels);
|
||||||
|
@ -4810,7 +4897,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
*/
|
*/
|
||||||
function testSongInPlayState(minimal:Bool = false):Void
|
function testSongInPlayState(minimal:Bool = false):Void
|
||||||
{
|
{
|
||||||
autoSave();
|
autoSave(true);
|
||||||
|
|
||||||
stopWelcomeMusic();
|
stopWelcomeMusic();
|
||||||
|
|
||||||
|
@ -5016,7 +5103,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
// Remove any notes past the end of the song.
|
// Remove any notes past the end of the song.
|
||||||
var songCutoffPointSteps:Float = songLengthInSteps - 0.1;
|
var songCutoffPointSteps:Float = songLengthInSteps - 0.1;
|
||||||
var songCutoffPointMs:Float = Conductor.getStepTimeInMs(songCutoffPointSteps);
|
var songCutoffPointMs:Float = Conductor.instance.getStepTimeInMs(songCutoffPointSteps);
|
||||||
currentSongChartNoteData = SongDataUtils.clampSongNoteData(currentSongChartNoteData, 0.0, songCutoffPointMs);
|
currentSongChartNoteData = SongDataUtils.clampSongNoteData(currentSongChartNoteData, 0.0, songCutoffPointMs);
|
||||||
currentSongChartEventData = SongDataUtils.clampSongEventData(currentSongChartEventData, 0.0, songCutoffPointMs);
|
currentSongChartEventData = SongDataUtils.clampSongEventData(currentSongChartEventData, 0.0, songCutoffPointMs);
|
||||||
|
|
||||||
|
@ -5118,7 +5205,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
var prevDifficulty = availableDifficulties[availableDifficulties.length - 1];
|
var prevDifficulty = availableDifficulties[availableDifficulties.length - 1];
|
||||||
selectedDifficulty = prevDifficulty;
|
selectedDifficulty = prevDifficulty;
|
||||||
|
|
||||||
Conductor.mapTimeChanges(this.currentSongMetadata.timeChanges);
|
Conductor.instance.mapTimeChanges(this.currentSongMetadata.timeChanges);
|
||||||
updateTimeSignature();
|
updateTimeSignature();
|
||||||
|
|
||||||
refreshDifficultyTreeSelection();
|
refreshDifficultyTreeSelection();
|
||||||
|
@ -5181,9 +5268,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// Update the songPosition in the audio tracks.
|
// Update the songPosition in the audio tracks.
|
||||||
if (audioInstTrack != null)
|
if (audioInstTrack != null)
|
||||||
{
|
{
|
||||||
audioInstTrack.time = scrollPositionInMs + playheadPositionInMs - Conductor.instrumentalOffset;
|
audioInstTrack.time = scrollPositionInMs + playheadPositionInMs - Conductor.instance.instrumentalOffset;
|
||||||
// Update the songPosition in the Conductor.
|
// Update the songPosition in the Conductor.
|
||||||
Conductor.update(audioInstTrack.time);
|
Conductor.instance.update(audioInstTrack.time);
|
||||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.time = audioInstTrack.time;
|
if (audioVocalTrackGroup != null) audioVocalTrackGroup.time = audioInstTrack.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5243,6 +5330,20 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
this.persistentUpdate = true;
|
this.persistentUpdate = true;
|
||||||
this.persistentDraw = true;
|
this.persistentDraw = true;
|
||||||
|
|
||||||
|
if (displayAutosavePopup)
|
||||||
|
{
|
||||||
|
displayAutosavePopup = false;
|
||||||
|
Toolkit.callLater(() -> {
|
||||||
|
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), ChartEditorImportExportHandler.BACKUPS_PATH]);
|
||||||
|
this.infoWithActions('Auto-Save', 'Chart auto-saved to ${absoluteBackupsPath}.', [
|
||||||
|
{
|
||||||
|
text: "Take Me There",
|
||||||
|
callback: openBackupsFolder,
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
moveSongToScrollPosition();
|
moveSongToScrollPosition();
|
||||||
|
|
||||||
fadeInWelcomeMusic(7, 10);
|
fadeInWelcomeMusic(7, 10);
|
||||||
|
@ -5511,7 +5612,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
trace('ERROR: Instrumental track is null!');
|
trace('ERROR: Instrumental track is null!');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.songLengthInMs = (audioInstTrack?.length ?? 1000.0) + Conductor.instrumentalOffset;
|
this.songLengthInMs = (audioInstTrack?.length ?? 1000.0) + Conductor.instance.instrumentalOffset;
|
||||||
|
|
||||||
// Many things get reset when song length changes.
|
// Many things get reset when song length changes.
|
||||||
healthIconsDirty = true;
|
healthIconsDirty = true;
|
||||||
|
@ -5536,6 +5637,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
cleanupAutoSave();
|
cleanupAutoSave();
|
||||||
|
|
||||||
|
this.closeAllMenus();
|
||||||
|
|
||||||
// Hide the mouse cursor on other states.
|
// Hide the mouse cursor on other states.
|
||||||
Cursor.hide();
|
Cursor.hide();
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,12 @@ class ChangeStartingBPMCommand implements ChartEditorCommand
|
||||||
|
|
||||||
state.currentSongMetadata.timeChanges = timeChanges;
|
state.currentSongMetadata.timeChanges = timeChanges;
|
||||||
|
|
||||||
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
state.notePreviewViewportBoundsDirty = true;
|
||||||
|
state.scrollPositionInPixels = 0;
|
||||||
|
|
||||||
|
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function undo(state:ChartEditorState):Void
|
public function undo(state:ChartEditorState):Void
|
||||||
|
@ -51,7 +56,12 @@ class ChangeStartingBPMCommand implements ChartEditorCommand
|
||||||
|
|
||||||
state.currentSongMetadata.timeChanges = timeChanges;
|
state.currentSongMetadata.timeChanges = timeChanges;
|
||||||
|
|
||||||
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
state.notePreviewViewportBoundsDirty = true;
|
||||||
|
state.scrollPositionInPixels = 0;
|
||||||
|
|
||||||
|
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toString():String
|
public function toString():String
|
||||||
|
|
|
@ -33,7 +33,7 @@ class MoveEventsCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
// Clone the notes to prevent editing from affecting the history.
|
// Clone the notes to prevent editing from affecting the history.
|
||||||
var resultEvent = event.clone();
|
var resultEvent = event.clone();
|
||||||
resultEvent.time = (resultEvent.time + offset).clamp(0, Conductor.getStepTimeInMs(state.songLengthInSteps - (1 * state.noteSnapRatio)));
|
resultEvent.time = (resultEvent.time + offset).clamp(0, Conductor.instance.getStepTimeInMs(state.songLengthInSteps - (1 * state.noteSnapRatio)));
|
||||||
|
|
||||||
movedEvents.push(resultEvent);
|
movedEvents.push(resultEvent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@ class MoveItemsCommand implements ChartEditorCommand
|
||||||
public function new(notes:Array<SongNoteData>, events:Array<SongEventData>, offset:Float, columns:Int)
|
public function new(notes:Array<SongNoteData>, events:Array<SongEventData>, offset:Float, columns:Int)
|
||||||
{
|
{
|
||||||
// Clone the notes to prevent editing from affecting the history.
|
// Clone the notes to prevent editing from affecting the history.
|
||||||
this.notes = [for (note in notes) note.clone()];
|
this.notes = notes.clone();
|
||||||
this.events = [for (event in events) event.clone()];
|
this.events = events.clone();
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.columns = columns;
|
this.columns = columns;
|
||||||
this.movedNotes = [];
|
this.movedNotes = [];
|
||||||
|
@ -41,7 +41,7 @@ class MoveItemsCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
// Clone the notes to prevent editing from affecting the history.
|
// Clone the notes to prevent editing from affecting the history.
|
||||||
var resultNote = note.clone();
|
var resultNote = note.clone();
|
||||||
resultNote.time = (resultNote.time + offset).clamp(0, Conductor.getStepTimeInMs(state.songLengthInSteps - (1 * state.noteSnapRatio)));
|
resultNote.time = (resultNote.time + offset).clamp(0, Conductor.instance.getStepTimeInMs(state.songLengthInSteps - (1 * state.noteSnapRatio)));
|
||||||
resultNote.data = ChartEditorState.gridColumnToNoteData((ChartEditorState.noteDataToGridColumn(resultNote.data) + columns).clamp(0,
|
resultNote.data = ChartEditorState.gridColumnToNoteData((ChartEditorState.noteDataToGridColumn(resultNote.data) + columns).clamp(0,
|
||||||
ChartEditorState.STRUMLINE_SIZE * 2 - 1));
|
ChartEditorState.STRUMLINE_SIZE * 2 - 1));
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class MoveItemsCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
// Clone the notes to prevent editing from affecting the history.
|
// Clone the notes to prevent editing from affecting the history.
|
||||||
var resultEvent = event.clone();
|
var resultEvent = event.clone();
|
||||||
resultEvent.time = (resultEvent.time + offset).clamp(0, Conductor.getStepTimeInMs(state.songLengthInSteps - (1 * state.noteSnapRatio)));
|
resultEvent.time = (resultEvent.time + offset).clamp(0, Conductor.instance.getStepTimeInMs(state.songLengthInSteps - (1 * state.noteSnapRatio)));
|
||||||
|
|
||||||
movedEvents.push(resultEvent);
|
movedEvents.push(resultEvent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ class MoveNotesCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
// Clone the notes to prevent editing from affecting the history.
|
// Clone the notes to prevent editing from affecting the history.
|
||||||
var resultNote = note.clone();
|
var resultNote = note.clone();
|
||||||
resultNote.time = (resultNote.time + offset).clamp(0, Conductor.getStepTimeInMs(state.songLengthInSteps - (1 * state.noteSnapRatio)));
|
resultNote.time = (resultNote.time + offset).clamp(0, Conductor.instance.getStepTimeInMs(state.songLengthInSteps - (1 * state.noteSnapRatio)));
|
||||||
resultNote.data = ChartEditorState.gridColumnToNoteData((ChartEditorState.noteDataToGridColumn(resultNote.data) + columns).clamp(0,
|
resultNote.data = ChartEditorState.gridColumnToNoteData((ChartEditorState.noteDataToGridColumn(resultNote.data) + columns).clamp(0,
|
||||||
ChartEditorState.STRUMLINE_SIZE * 2 - 1));
|
ChartEditorState.STRUMLINE_SIZE * 2 - 1));
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ class PasteItemsCommand implements ChartEditorCommand
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stepEndOfSong:Float = Conductor.getTimeInSteps(state.songLengthInMs);
|
var stepEndOfSong:Float = Conductor.instance.getTimeInSteps(state.songLengthInMs);
|
||||||
var stepCutoff:Float = stepEndOfSong - 1.0;
|
var stepCutoff:Float = stepEndOfSong - 1.0;
|
||||||
var msCutoff:Float = Conductor.getStepTimeInMs(stepCutoff);
|
var msCutoff:Float = Conductor.instance.getStepTimeInMs(stepCutoff);
|
||||||
|
|
||||||
addedNotes = SongDataUtils.offsetSongNoteData(currentClipboard.notes, Std.int(targetTimestamp));
|
addedNotes = SongDataUtils.offsetSongNoteData(currentClipboard.notes, Std.int(targetTimestamp));
|
||||||
addedNotes = SongDataUtils.clampSongNoteData(addedNotes, 0.0, msCutoff);
|
addedNotes = SongDataUtils.clampSongNoteData(addedNotes, 0.0, msCutoff);
|
||||||
|
|
|
@ -33,6 +33,32 @@ class SelectItemsCommand implements ChartEditorCommand
|
||||||
state.currentEventSelection.push(event);
|
state.currentEventSelection.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we just selected one or more events (and no notes), then we should make the event data toolbox display the event data for the selected event.
|
||||||
|
if (this.notes.length == 0 && this.events.length >= 1)
|
||||||
|
{
|
||||||
|
var eventSelected = this.events[0];
|
||||||
|
|
||||||
|
state.eventKindToPlace = eventSelected.event;
|
||||||
|
|
||||||
|
// This code is here to parse event data that's not built as a struct for some reason.
|
||||||
|
// TODO: Clean this up or get rid of it.
|
||||||
|
var eventSchema = eventSelected.getSchema();
|
||||||
|
var defaultKey = null;
|
||||||
|
if (eventSchema == null)
|
||||||
|
{
|
||||||
|
trace('[WARNING] Event schema not found for event ${eventSelected.event}.');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
defaultKey = eventSchema.getFirstField()?.name;
|
||||||
|
}
|
||||||
|
var eventData = eventSelected.valueAsStruct(defaultKey);
|
||||||
|
|
||||||
|
state.eventDataToPlace = eventData;
|
||||||
|
|
||||||
|
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT);
|
||||||
|
}
|
||||||
|
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
state.notePreviewDirty = true;
|
state.notePreviewDirty = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,32 @@ class SetItemSelectionCommand implements ChartEditorCommand
|
||||||
state.currentNoteSelection = notes;
|
state.currentNoteSelection = notes;
|
||||||
state.currentEventSelection = events;
|
state.currentEventSelection = events;
|
||||||
|
|
||||||
|
// If we just selected one or more events (and no notes), then we should make the event data toolbox display the event data for the selected event.
|
||||||
|
if (this.notes.length == 0 && this.events.length >= 1)
|
||||||
|
{
|
||||||
|
var eventSelected = this.events[0];
|
||||||
|
|
||||||
|
state.eventKindToPlace = eventSelected.event;
|
||||||
|
|
||||||
|
// This code is here to parse event data that's not built as a struct for some reason.
|
||||||
|
// TODO: Clean this up or get rid of it.
|
||||||
|
var eventSchema = eventSelected.getSchema();
|
||||||
|
var defaultKey = null;
|
||||||
|
if (eventSchema == null)
|
||||||
|
{
|
||||||
|
trace('[WARNING] Event schema not found for event ${eventSelected.event}.');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
defaultKey = eventSchema.getFirstField()?.name;
|
||||||
|
}
|
||||||
|
var eventData = eventSelected.valueAsStruct(defaultKey);
|
||||||
|
|
||||||
|
state.eventDataToPlace = eventData;
|
||||||
|
|
||||||
|
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT);
|
||||||
|
}
|
||||||
|
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package funkin.ui.debug.charting.components;
|
package funkin.ui.debug.charting.components;
|
||||||
|
|
||||||
import funkin.data.event.SongEventData.SongEventParser;
|
import funkin.data.event.SongEventRegistry;
|
||||||
import flixel.graphics.frames.FlxAtlasFrames;
|
import flixel.graphics.frames.FlxAtlasFrames;
|
||||||
import openfl.display.BitmapData;
|
import openfl.display.BitmapData;
|
||||||
import openfl.utils.Assets;
|
import openfl.utils.Assets;
|
||||||
|
@ -79,7 +79,7 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push all the other events as frames.
|
// Push all the other events as frames.
|
||||||
for (eventName in SongEventParser.listEventIds())
|
for (eventName in SongEventRegistry.listEventIds())
|
||||||
{
|
{
|
||||||
var exists:Bool = Assets.exists(Paths.image('ui/chart-editor/events/$eventName'));
|
var exists:Bool = Assets.exists(Paths.image('ui/chart-editor/events/$eventName'));
|
||||||
if (!exists) continue; // No graphic for this event.
|
if (!exists) continue; // No graphic for this event.
|
||||||
|
@ -105,7 +105,7 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
|
|
||||||
function buildAnimations():Void
|
function buildAnimations():Void
|
||||||
{
|
{
|
||||||
var eventNames:Array<String> = [DEFAULT_EVENT].concat(SongEventParser.listEventIds());
|
var eventNames:Array<String> = [DEFAULT_EVENT].concat(SongEventRegistry.listEventIds());
|
||||||
for (eventName in eventNames)
|
for (eventName in eventNames)
|
||||||
{
|
{
|
||||||
this.animation.addByPrefix(eventName, '${eventName}0', 24, false);
|
this.animation.addByPrefix(eventName, '${eventName}0', 24, false);
|
||||||
|
@ -145,8 +145,6 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
// Only play the animation if the event type has changed.
|
|
||||||
// if (this.eventData == null || this.eventData.event != value.event)
|
|
||||||
playAnimation(value.event);
|
playAnimation(value.event);
|
||||||
this.eventData = value;
|
this.eventData = value;
|
||||||
// Update the position to match the note data.
|
// Update the position to match the note data.
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package funkin.ui.debug.charting.contextmenus;
|
||||||
|
|
||||||
|
import haxe.ui.containers.menus.Menu;
|
||||||
|
|
||||||
|
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||||
|
class ChartEditorBaseContextMenu extends Menu
|
||||||
|
{
|
||||||
|
var chartEditorState:ChartEditorState;
|
||||||
|
|
||||||
|
public function new(chartEditorState:ChartEditorState, xPos:Float = 0, yPos:Float = 0)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.chartEditorState = chartEditorState;
|
||||||
|
|
||||||
|
this.left = xPos;
|
||||||
|
this.top = yPos;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package funkin.ui.debug.charting.contextmenus;
|
||||||
|
|
||||||
|
import haxe.ui.containers.menus.Menu;
|
||||||
|
import haxe.ui.core.Screen;
|
||||||
|
|
||||||
|
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||||
|
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/context-menus/default.xml"))
|
||||||
|
class ChartEditorDefaultContextMenu extends ChartEditorBaseContextMenu
|
||||||
|
{
|
||||||
|
public function new(chartEditorState2:ChartEditorState, xPos2:Float = 0, yPos2:Float = 0)
|
||||||
|
{
|
||||||
|
super(chartEditorState2, xPos2, yPos2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package funkin.ui.debug.charting.contextmenus;
|
||||||
|
|
||||||
|
import haxe.ui.containers.menus.Menu;
|
||||||
|
import haxe.ui.containers.menus.MenuItem;
|
||||||
|
import haxe.ui.core.Screen;
|
||||||
|
import funkin.data.song.SongData.SongEventData;
|
||||||
|
import funkin.ui.debug.charting.commands.RemoveEventsCommand;
|
||||||
|
|
||||||
|
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||||
|
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/context-menus/event.xml"))
|
||||||
|
class ChartEditorEventContextMenu extends ChartEditorBaseContextMenu
|
||||||
|
{
|
||||||
|
var contextmenuEdit:MenuItem;
|
||||||
|
var contextmenuDelete:MenuItem;
|
||||||
|
|
||||||
|
var data:SongEventData;
|
||||||
|
|
||||||
|
public function new(chartEditorState2:ChartEditorState, xPos2:Float = 0, yPos2:Float = 0, data:SongEventData)
|
||||||
|
{
|
||||||
|
super(chartEditorState2, xPos2, yPos2);
|
||||||
|
this.data = data;
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize()
|
||||||
|
{
|
||||||
|
contextmenuDelete.onClick = function(_) {
|
||||||
|
chartEditorState.performCommand(new RemoveEventsCommand([data]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package funkin.ui.debug.charting.contextmenus;
|
||||||
|
|
||||||
|
import haxe.ui.containers.menus.Menu;
|
||||||
|
import haxe.ui.containers.menus.MenuItem;
|
||||||
|
import haxe.ui.core.Screen;
|
||||||
|
import funkin.data.song.SongData.SongNoteData;
|
||||||
|
import funkin.ui.debug.charting.commands.FlipNotesCommand;
|
||||||
|
import funkin.ui.debug.charting.commands.RemoveNotesCommand;
|
||||||
|
|
||||||
|
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||||
|
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/context-menus/note.xml"))
|
||||||
|
class ChartEditorNoteContextMenu extends ChartEditorBaseContextMenu
|
||||||
|
{
|
||||||
|
var contextmenuFlip:MenuItem;
|
||||||
|
var contextmenuDelete:MenuItem;
|
||||||
|
|
||||||
|
var data:SongNoteData;
|
||||||
|
|
||||||
|
public function new(chartEditorState2:ChartEditorState, xPos2:Float = 0, yPos2:Float = 0, data:SongNoteData)
|
||||||
|
{
|
||||||
|
super(chartEditorState2, xPos2, yPos2);
|
||||||
|
this.data = data;
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize():Void
|
||||||
|
{
|
||||||
|
// NOTE: Remember to use commands here to ensure undo/redo works properly
|
||||||
|
contextmenuFlip.onClick = function(_) {
|
||||||
|
chartEditorState.performCommand(new FlipNotesCommand([data]));
|
||||||
|
}
|
||||||
|
|
||||||
|
contextmenuDelete.onClick = function(_) {
|
||||||
|
chartEditorState.performCommand(new RemoveNotesCommand([data]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package funkin.ui.debug.charting.contextmenus;
|
||||||
|
|
||||||
|
import haxe.ui.containers.menus.Menu;
|
||||||
|
import haxe.ui.containers.menus.MenuItem;
|
||||||
|
import haxe.ui.core.Screen;
|
||||||
|
import funkin.ui.debug.charting.commands.CutItemsCommand;
|
||||||
|
import funkin.ui.debug.charting.commands.RemoveEventsCommand;
|
||||||
|
import funkin.ui.debug.charting.commands.RemoveItemsCommand;
|
||||||
|
import funkin.ui.debug.charting.commands.RemoveNotesCommand;
|
||||||
|
|
||||||
|
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||||
|
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/context-menus/selection.xml"))
|
||||||
|
class ChartEditorSelectionContextMenu extends ChartEditorBaseContextMenu
|
||||||
|
{
|
||||||
|
var contextmenuCut:MenuItem;
|
||||||
|
var contextmenuCopy:MenuItem;
|
||||||
|
var contextmenuPaste:MenuItem;
|
||||||
|
var contextmenuDelete:MenuItem;
|
||||||
|
var contextmenuFlip:MenuItem;
|
||||||
|
var contextmenuSelectAll:MenuItem;
|
||||||
|
var contextmenuSelectInverse:MenuItem;
|
||||||
|
var contextmenuSelectNone:MenuItem;
|
||||||
|
|
||||||
|
public function new(chartEditorState2:ChartEditorState, xPos2:Float = 0, yPos2:Float = 0)
|
||||||
|
{
|
||||||
|
super(chartEditorState2, xPos2, yPos2);
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize():Void
|
||||||
|
{
|
||||||
|
contextmenuCut.onClick = (_) -> {
|
||||||
|
chartEditorState.performCommand(new CutItemsCommand(chartEditorState.currentNoteSelection, chartEditorState.currentEventSelection));
|
||||||
|
};
|
||||||
|
contextmenuCopy.onClick = (_) -> {
|
||||||
|
chartEditorState.copySelection();
|
||||||
|
};
|
||||||
|
contextmenuFlip.onClick = (_) -> {
|
||||||
|
if (chartEditorState.currentNoteSelection.length > 0 && chartEditorState.currentEventSelection.length > 0)
|
||||||
|
{
|
||||||
|
chartEditorState.performCommand(new RemoveItemsCommand(chartEditorState.currentNoteSelection, chartEditorState.currentEventSelection));
|
||||||
|
}
|
||||||
|
else if (chartEditorState.currentNoteSelection.length > 0)
|
||||||
|
{
|
||||||
|
chartEditorState.performCommand(new RemoveNotesCommand(chartEditorState.currentNoteSelection));
|
||||||
|
}
|
||||||
|
else if (chartEditorState.currentEventSelection.length > 0)
|
||||||
|
{
|
||||||
|
chartEditorState.performCommand(new RemoveEventsCommand(chartEditorState.currentEventSelection));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Do nothing???
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -185,7 +185,7 @@ class ChartEditorAudioHandler
|
||||||
state.audioVocalTrackGroup.addPlayerVoice(vocalTrack);
|
state.audioVocalTrackGroup.addPlayerVoice(vocalTrack);
|
||||||
state.audioVisGroup.addPlayerVis(vocalTrack);
|
state.audioVisGroup.addPlayerVis(vocalTrack);
|
||||||
state.audioVisGroup.playerVis.x = 885;
|
state.audioVisGroup.playerVis.x = 885;
|
||||||
state.audioVisGroup.playerVis.realtimeVisLenght = Conductor.getStepTimeInMs(16) * 0.00195;
|
state.audioVisGroup.playerVis.realtimeVisLenght = Conductor.instance.getStepTimeInMs(16) * 0.00195;
|
||||||
state.audioVisGroup.playerVis.daHeight = (ChartEditorState.GRID_SIZE) * 16;
|
state.audioVisGroup.playerVis.daHeight = (ChartEditorState.GRID_SIZE) * 16;
|
||||||
state.audioVisGroup.playerVis.detail = 1;
|
state.audioVisGroup.playerVis.detail = 1;
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ class ChartEditorAudioHandler
|
||||||
state.audioVisGroup.addOpponentVis(vocalTrack);
|
state.audioVisGroup.addOpponentVis(vocalTrack);
|
||||||
state.audioVisGroup.opponentVis.x = 405;
|
state.audioVisGroup.opponentVis.x = 405;
|
||||||
|
|
||||||
state.audioVisGroup.opponentVis.realtimeVisLenght = Conductor.getStepTimeInMs(16) * 0.00195;
|
state.audioVisGroup.opponentVis.realtimeVisLenght = Conductor.instance.getStepTimeInMs(16) * 0.00195;
|
||||||
state.audioVisGroup.opponentVis.daHeight = (ChartEditorState.GRID_SIZE) * 16;
|
state.audioVisGroup.opponentVis.daHeight = (ChartEditorState.GRID_SIZE) * 16;
|
||||||
state.audioVisGroup.opponentVis.detail = 1;
|
state.audioVisGroup.opponentVis.detail = 1;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package funkin.ui.debug.charting.handlers;
|
||||||
|
|
||||||
|
import funkin.ui.debug.charting.contextmenus.ChartEditorDefaultContextMenu;
|
||||||
|
import funkin.ui.debug.charting.contextmenus.ChartEditorEventContextMenu;
|
||||||
|
import funkin.ui.debug.charting.contextmenus.ChartEditorNoteContextMenu;
|
||||||
|
import funkin.ui.debug.charting.contextmenus.ChartEditorSelectionContextMenu;
|
||||||
|
import haxe.ui.containers.menus.Menu;
|
||||||
|
import haxe.ui.core.Screen;
|
||||||
|
import funkin.data.song.SongData.SongNoteData;
|
||||||
|
import funkin.data.song.SongData.SongEventData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles context menus (the little menus that appear when you right click on stuff) for the new Chart Editor.
|
||||||
|
*/
|
||||||
|
@:nullSafety
|
||||||
|
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||||
|
class ChartEditorContextMenuHandler
|
||||||
|
{
|
||||||
|
static var existingMenus:Array<Menu> = [];
|
||||||
|
|
||||||
|
public static function openDefaultContextMenu(state:ChartEditorState, xPos:Float, yPos:Float)
|
||||||
|
{
|
||||||
|
displayMenu(state, new ChartEditorDefaultContextMenu(state, xPos, yPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function openSelectionContextMenu(state:ChartEditorState, xPos:Float, yPos:Float)
|
||||||
|
{
|
||||||
|
displayMenu(state, new ChartEditorSelectionContextMenu(state, xPos, yPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function openNoteContextMenu(state:ChartEditorState, xPos:Float, yPos:Float, data:SongNoteData)
|
||||||
|
{
|
||||||
|
displayMenu(state, new ChartEditorNoteContextMenu(state, xPos, yPos, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function openEventContextMenu(state:ChartEditorState, xPos:Float, yPos:Float, data:SongEventData)
|
||||||
|
{
|
||||||
|
displayMenu(state, new ChartEditorEventContextMenu(state, xPos, yPos, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
static function displayMenu(state:ChartEditorState, targetMenu:Menu)
|
||||||
|
{
|
||||||
|
// Close any existing menus
|
||||||
|
closeAllMenus(state);
|
||||||
|
|
||||||
|
// Show the new menu
|
||||||
|
Screen.instance.addComponent(targetMenu);
|
||||||
|
existingMenus.push(targetMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function closeMenu(state:ChartEditorState, targetMenu:Menu)
|
||||||
|
{
|
||||||
|
// targetMenu.close();
|
||||||
|
existingMenus.remove(targetMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function closeAllMenus(state:ChartEditorState)
|
||||||
|
{
|
||||||
|
for (existingMenu in existingMenus)
|
||||||
|
{
|
||||||
|
closeMenu(state, existingMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -684,8 +684,8 @@ class ChartEditorDialogHandler
|
||||||
|
|
||||||
state.songMetadata.set(targetVariation, newSongMetadata);
|
state.songMetadata.set(targetVariation, newSongMetadata);
|
||||||
|
|
||||||
Conductor.instrumentalOffset = state.currentInstrumentalOffset; // Loads from the metadata.
|
Conductor.instance.instrumentalOffset = state.currentInstrumentalOffset; // Loads from the metadata.
|
||||||
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||||
state.updateTimeSignature();
|
state.updateTimeSignature();
|
||||||
|
|
||||||
state.selectedVariation = Constants.DEFAULT_VARIATION;
|
state.selectedVariation = Constants.DEFAULT_VARIATION;
|
||||||
|
|
|
@ -43,7 +43,8 @@ class ChartEditorImportExportHandler
|
||||||
var variation = (metadata.variation == null || metadata.variation == '') ? Constants.DEFAULT_VARIATION : metadata.variation;
|
var variation = (metadata.variation == null || metadata.variation == '') ? Constants.DEFAULT_VARIATION : metadata.variation;
|
||||||
|
|
||||||
// Clone to prevent modifying the original.
|
// Clone to prevent modifying the original.
|
||||||
var metadataClone:SongMetadata = metadata.clone(variation);
|
var metadataClone:SongMetadata = metadata.clone();
|
||||||
|
metadataClone.variation = variation;
|
||||||
if (metadataClone != null) songMetadata.set(variation, metadataClone);
|
if (metadataClone != null) songMetadata.set(variation, metadataClone);
|
||||||
|
|
||||||
var chartData:Null<SongChartData> = SongRegistry.instance.parseEntryChartData(songId, metadata.variation);
|
var chartData:Null<SongChartData> = SongRegistry.instance.parseEntryChartData(songId, metadata.variation);
|
||||||
|
@ -114,9 +115,9 @@ class ChartEditorImportExportHandler
|
||||||
state.songMetadata = newSongMetadata;
|
state.songMetadata = newSongMetadata;
|
||||||
state.songChartData = newSongChartData;
|
state.songChartData = newSongChartData;
|
||||||
|
|
||||||
Conductor.forceBPM(null); // Disable the forced BPM.
|
Conductor.instance.forceBPM(null); // Disable the forced BPM.
|
||||||
Conductor.instrumentalOffset = state.currentInstrumentalOffset; // Loads from the metadata.
|
Conductor.instance.instrumentalOffset = state.currentInstrumentalOffset; // Loads from the metadata.
|
||||||
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||||
state.updateTimeSignature();
|
state.updateTimeSignature();
|
||||||
|
|
||||||
state.notePreviewDirty = true;
|
state.notePreviewDirty = true;
|
||||||
|
@ -416,16 +417,34 @@ class ChartEditorImportExportHandler
|
||||||
]);
|
]);
|
||||||
// We have to force write because the program will die before the save dialog is closed.
|
// We have to force write because the program will die before the save dialog is closed.
|
||||||
trace('Force exporting to $targetPath...');
|
trace('Force exporting to $targetPath...');
|
||||||
FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode);
|
try
|
||||||
if (onSaveCb != null) onSaveCb(targetPath);
|
{
|
||||||
|
FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode);
|
||||||
|
// On success.
|
||||||
|
if (onSaveCb != null) onSaveCb(targetPath);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
// On failure.
|
||||||
|
if (onCancelCb != null) onCancelCb();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Force write since we know what file the user wants to overwrite.
|
// Force write since we know what file the user wants to overwrite.
|
||||||
trace('Force exporting to $targetPath...');
|
trace('Force exporting to $targetPath...');
|
||||||
FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode);
|
try
|
||||||
state.saveDataDirty = false;
|
{
|
||||||
if (onSaveCb != null) onSaveCb(targetPath);
|
// On success.
|
||||||
|
FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode);
|
||||||
|
state.saveDataDirty = false;
|
||||||
|
if (onSaveCb != null) onSaveCb(targetPath);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
// On failure.
|
||||||
|
if (onCancelCb != null) onCancelCb();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -126,7 +126,7 @@ class ChartEditorThemeHandler
|
||||||
// 2 * (Strumline Size) + 1 grid squares wide, by (4 * quarter notes per measure) grid squares tall.
|
// 2 * (Strumline Size) + 1 grid squares wide, by (4 * quarter notes per measure) grid squares tall.
|
||||||
// This gets reused to fill the screen.
|
// This gets reused to fill the screen.
|
||||||
var gridWidth:Int = Std.int(ChartEditorState.GRID_SIZE * TOTAL_COLUMN_COUNT);
|
var gridWidth:Int = Std.int(ChartEditorState.GRID_SIZE * TOTAL_COLUMN_COUNT);
|
||||||
var gridHeight:Int = Std.int(ChartEditorState.GRID_SIZE * Conductor.stepsPerMeasure);
|
var gridHeight:Int = Std.int(ChartEditorState.GRID_SIZE * Conductor.instance.stepsPerMeasure);
|
||||||
state.gridBitmap = FlxGridOverlay.createGrid(ChartEditorState.GRID_SIZE, ChartEditorState.GRID_SIZE, gridWidth, gridHeight, true, gridColor1, gridColor2);
|
state.gridBitmap = FlxGridOverlay.createGrid(ChartEditorState.GRID_SIZE, ChartEditorState.GRID_SIZE, gridWidth, gridHeight, true, gridColor1, gridColor2);
|
||||||
|
|
||||||
// Selection borders
|
// Selection borders
|
||||||
|
@ -143,7 +143,7 @@ class ChartEditorThemeHandler
|
||||||
selectionBorderColor);
|
selectionBorderColor);
|
||||||
|
|
||||||
// Selection borders horizontally along the middle.
|
// Selection borders horizontally along the middle.
|
||||||
for (i in 1...(Conductor.stepsPerMeasure))
|
for (i in 1...(Conductor.instance.stepsPerMeasure))
|
||||||
{
|
{
|
||||||
state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2),
|
state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2),
|
||||||
state.gridBitmap.width, ChartEditorState.GRID_SELECTION_BORDER_WIDTH),
|
state.gridBitmap.width, ChartEditorState.GRID_SELECTION_BORDER_WIDTH),
|
||||||
|
@ -198,9 +198,9 @@ class ChartEditorThemeHandler
|
||||||
};
|
};
|
||||||
|
|
||||||
// Selection borders horizontally in the middle.
|
// Selection borders horizontally in the middle.
|
||||||
for (i in 1...(Conductor.stepsPerMeasure))
|
for (i in 1...(Conductor.instance.stepsPerMeasure))
|
||||||
{
|
{
|
||||||
if ((i % Conductor.beatsPerMeasure) == 0)
|
if ((i % Conductor.instance.beatsPerMeasure) == 0)
|
||||||
{
|
{
|
||||||
state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (GRID_BEAT_DIVIDER_WIDTH / 2), state.gridBitmap.width,
|
state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (GRID_BEAT_DIVIDER_WIDTH / 2), state.gridBitmap.width,
|
||||||
GRID_BEAT_DIVIDER_WIDTH),
|
GRID_BEAT_DIVIDER_WIDTH),
|
||||||
|
|
|
@ -9,7 +9,7 @@ import haxe.ui.containers.TreeView;
|
||||||
import haxe.ui.containers.TreeViewNode;
|
import haxe.ui.containers.TreeViewNode;
|
||||||
import funkin.play.character.BaseCharacter.CharacterType;
|
import funkin.play.character.BaseCharacter.CharacterType;
|
||||||
import funkin.play.event.SongEvent;
|
import funkin.play.event.SongEvent;
|
||||||
import funkin.data.event.SongEventData;
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.song.SongData.SongTimeChange;
|
import funkin.data.song.SongData.SongTimeChange;
|
||||||
import funkin.play.character.BaseCharacter.CharacterType;
|
import funkin.play.character.BaseCharacter.CharacterType;
|
||||||
import funkin.play.character.CharacterData;
|
import funkin.play.character.CharacterData;
|
||||||
|
@ -23,6 +23,7 @@ import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||||
import funkin.ui.haxeui.components.CharacterPlayer;
|
import funkin.ui.haxeui.components.CharacterPlayer;
|
||||||
import funkin.util.FileUtil;
|
import funkin.util.FileUtil;
|
||||||
import haxe.ui.components.Button;
|
import haxe.ui.components.Button;
|
||||||
|
import haxe.ui.data.ArrayDataSource;
|
||||||
import haxe.ui.components.CheckBox;
|
import haxe.ui.components.CheckBox;
|
||||||
import haxe.ui.components.DropDown;
|
import haxe.ui.components.DropDown;
|
||||||
import haxe.ui.components.HorizontalSlider;
|
import haxe.ui.components.HorizontalSlider;
|
||||||
|
@ -36,12 +37,12 @@ import haxe.ui.containers.dialogs.Dialog.DialogButton;
|
||||||
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
|
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
|
||||||
import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox;
|
import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox;
|
||||||
import funkin.ui.debug.charting.toolboxes.ChartEditorMetadataToolbox;
|
import funkin.ui.debug.charting.toolboxes.ChartEditorMetadataToolbox;
|
||||||
|
import funkin.ui.debug.charting.toolboxes.ChartEditorEventDataToolbox;
|
||||||
import haxe.ui.containers.Frame;
|
import haxe.ui.containers.Frame;
|
||||||
import haxe.ui.containers.Grid;
|
import haxe.ui.containers.Grid;
|
||||||
import haxe.ui.containers.TreeView;
|
import haxe.ui.containers.TreeView;
|
||||||
import haxe.ui.containers.TreeViewNode;
|
import haxe.ui.containers.TreeViewNode;
|
||||||
import haxe.ui.core.Component;
|
import haxe.ui.core.Component;
|
||||||
import haxe.ui.data.ArrayDataSource;
|
|
||||||
import haxe.ui.events.UIEvent;
|
import haxe.ui.events.UIEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,8 +80,9 @@ class ChartEditorToolboxHandler
|
||||||
{
|
{
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
|
||||||
onShowToolboxNoteData(state, toolbox);
|
onShowToolboxNoteData(state, toolbox);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT:
|
||||||
onShowToolboxEventData(state, toolbox);
|
// TODO: Fix this.
|
||||||
|
cast(toolbox, ChartEditorBaseToolbox).refresh();
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
||||||
onShowToolboxPlaytestProperties(state, toolbox);
|
onShowToolboxPlaytestProperties(state, toolbox);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
|
||||||
|
@ -119,7 +121,7 @@ class ChartEditorToolboxHandler
|
||||||
{
|
{
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
|
||||||
onHideToolboxNoteData(state, toolbox);
|
onHideToolboxNoteData(state, toolbox);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT:
|
||||||
onHideToolboxEventData(state, toolbox);
|
onHideToolboxEventData(state, toolbox);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
||||||
onHideToolboxPlaytestProperties(state, toolbox);
|
onHideToolboxPlaytestProperties(state, toolbox);
|
||||||
|
@ -195,7 +197,7 @@ class ChartEditorToolboxHandler
|
||||||
{
|
{
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
|
||||||
toolbox = buildToolboxNoteDataLayout(state);
|
toolbox = buildToolboxNoteDataLayout(state);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT:
|
||||||
toolbox = buildToolboxEventDataLayout(state);
|
toolbox = buildToolboxEventDataLayout(state);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
||||||
toolbox = buildToolboxPlaytestPropertiesLayout(state);
|
toolbox = buildToolboxPlaytestPropertiesLayout(state);
|
||||||
|
@ -283,19 +285,19 @@ class ChartEditorToolboxHandler
|
||||||
toolboxNotesCustomKindLabel.hidden = false;
|
toolboxNotesCustomKindLabel.hidden = false;
|
||||||
toolboxNotesCustomKind.hidden = false;
|
toolboxNotesCustomKind.hidden = false;
|
||||||
|
|
||||||
state.selectedNoteKind = toolboxNotesCustomKind.text;
|
state.noteKindToPlace = toolboxNotesCustomKind.text;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
toolboxNotesCustomKindLabel.hidden = true;
|
toolboxNotesCustomKindLabel.hidden = true;
|
||||||
toolboxNotesCustomKind.hidden = true;
|
toolboxNotesCustomKind.hidden = true;
|
||||||
|
|
||||||
state.selectedNoteKind = event.data.id;
|
state.noteKindToPlace = event.data.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toolboxNotesCustomKind.onChange = function(event:UIEvent) {
|
toolboxNotesCustomKind.onChange = function(event:UIEvent) {
|
||||||
state.selectedNoteKind = toolboxNotesCustomKind.text;
|
state.noteKindToPlace = toolboxNotesCustomKind.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
return toolbox;
|
return toolbox;
|
||||||
|
@ -305,159 +307,16 @@ class ChartEditorToolboxHandler
|
||||||
|
|
||||||
static function onHideToolboxNoteData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
static function onHideToolboxNoteData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
|
|
||||||
static function buildToolboxEventDataLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
static function onHideToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
{
|
|
||||||
var toolbox:CollapsibleDialog = cast RuntimeComponentBuilder.fromAsset(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT);
|
|
||||||
|
|
||||||
if (toolbox == null) return null;
|
|
||||||
|
|
||||||
// Starting position.
|
|
||||||
toolbox.x = 100;
|
|
||||||
toolbox.y = 150;
|
|
||||||
|
|
||||||
toolbox.onDialogClosed = function(event:DialogEvent) {
|
|
||||||
state.menubarItemToggleToolboxEvents.selected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var toolboxEventsEventKind:Null<DropDown> = toolbox.findComponent('toolboxEventsEventKind', DropDown);
|
|
||||||
if (toolboxEventsEventKind == null) throw 'ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Could not find toolboxEventsEventKind component.';
|
|
||||||
var toolboxEventsDataGrid:Null<Grid> = toolbox.findComponent('toolboxEventsDataGrid', Grid);
|
|
||||||
if (toolboxEventsDataGrid == null) throw 'ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Could not find toolboxEventsDataGrid component.';
|
|
||||||
|
|
||||||
toolboxEventsEventKind.dataSource = new ArrayDataSource();
|
|
||||||
|
|
||||||
var songEvents:Array<SongEvent> = SongEventParser.listEvents();
|
|
||||||
|
|
||||||
for (event in songEvents)
|
|
||||||
{
|
|
||||||
toolboxEventsEventKind.dataSource.add({text: event.getTitle(), value: event.id});
|
|
||||||
}
|
|
||||||
|
|
||||||
toolboxEventsEventKind.onChange = function(event:UIEvent) {
|
|
||||||
var eventType:String = event.data.value;
|
|
||||||
|
|
||||||
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType');
|
|
||||||
|
|
||||||
state.selectedEventKind = eventType;
|
|
||||||
|
|
||||||
var schema:SongEventSchema = SongEventParser.getEventSchema(eventType);
|
|
||||||
|
|
||||||
if (schema == null)
|
|
||||||
{
|
|
||||||
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Unknown event kind: $eventType');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildEventDataFormFromSchema(state, toolboxEventsDataGrid, schema);
|
|
||||||
}
|
|
||||||
toolboxEventsEventKind.value = state.selectedEventKind;
|
|
||||||
|
|
||||||
return toolbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
static function onShowToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
|
||||||
|
|
||||||
static function onShowToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
|
||||||
|
|
||||||
static function onHideToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
static function onHideToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
|
|
||||||
|
static function onHideToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
|
|
||||||
|
static function onShowToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
|
|
||||||
static function onHideToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
static function onHideToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
|
|
||||||
static function buildEventDataFormFromSchema(state:ChartEditorState, target:Box, schema:SongEventSchema):Void
|
|
||||||
{
|
|
||||||
trace(schema);
|
|
||||||
// Clear the frame.
|
|
||||||
target.removeAllComponents();
|
|
||||||
|
|
||||||
state.selectedEventData = {};
|
|
||||||
|
|
||||||
for (field in schema)
|
|
||||||
{
|
|
||||||
if (field == null) continue;
|
|
||||||
|
|
||||||
// Add a label.
|
|
||||||
var label:Label = new Label();
|
|
||||||
label.text = field.title;
|
|
||||||
label.verticalAlign = "center";
|
|
||||||
target.addComponent(label);
|
|
||||||
|
|
||||||
var input:Component;
|
|
||||||
switch (field.type)
|
|
||||||
{
|
|
||||||
case INTEGER:
|
|
||||||
var numberStepper:NumberStepper = new NumberStepper();
|
|
||||||
numberStepper.id = field.name;
|
|
||||||
numberStepper.step = field.step ?? 1.0;
|
|
||||||
numberStepper.min = field.min ?? 0.0;
|
|
||||||
numberStepper.max = field.max ?? 10.0;
|
|
||||||
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
|
|
||||||
input = numberStepper;
|
|
||||||
case FLOAT:
|
|
||||||
var numberStepper:NumberStepper = new NumberStepper();
|
|
||||||
numberStepper.id = field.name;
|
|
||||||
numberStepper.step = field.step ?? 0.1;
|
|
||||||
if (field.min != null) numberStepper.min = field.min;
|
|
||||||
if (field.max != null) numberStepper.max = field.max;
|
|
||||||
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
|
|
||||||
input = numberStepper;
|
|
||||||
case BOOL:
|
|
||||||
var checkBox:CheckBox = new CheckBox();
|
|
||||||
checkBox.id = field.name;
|
|
||||||
if (field.defaultValue != null) checkBox.selected = field.defaultValue;
|
|
||||||
input = checkBox;
|
|
||||||
case ENUM:
|
|
||||||
var dropDown:DropDown = new DropDown();
|
|
||||||
dropDown.id = field.name;
|
|
||||||
dropDown.width = 200.0;
|
|
||||||
dropDown.dataSource = new ArrayDataSource();
|
|
||||||
|
|
||||||
if (field.keys == null) throw 'Field "${field.name}" is of Enum type but has no keys.';
|
|
||||||
|
|
||||||
// Add entries to the dropdown.
|
|
||||||
|
|
||||||
for (optionName in field.keys.keys())
|
|
||||||
{
|
|
||||||
var optionValue:Null<Dynamic> = field.keys.get(optionName);
|
|
||||||
trace('$optionName : $optionValue');
|
|
||||||
dropDown.dataSource.add({value: optionValue, text: optionName});
|
|
||||||
}
|
|
||||||
|
|
||||||
dropDown.value = field.defaultValue;
|
|
||||||
|
|
||||||
input = dropDown;
|
|
||||||
case STRING:
|
|
||||||
input = new TextField();
|
|
||||||
input.id = field.name;
|
|
||||||
if (field.defaultValue != null) input.text = field.defaultValue;
|
|
||||||
default:
|
|
||||||
// Unknown type. Display a label so we know what it is.
|
|
||||||
input = new Label();
|
|
||||||
input.id = field.name;
|
|
||||||
input.text = field.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
target.addComponent(input);
|
|
||||||
|
|
||||||
input.onChange = function(event:UIEvent) {
|
|
||||||
var value = event.target.value;
|
|
||||||
if (field.type == ENUM)
|
|
||||||
{
|
|
||||||
value = event.target.value.value;
|
|
||||||
}
|
|
||||||
trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${value}');
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
state.selectedEventData.remove(event.target.id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state.selectedEventData.set(event.target.id, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static function buildToolboxPlaytestPropertiesLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
static function buildToolboxPlaytestPropertiesLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||||
{
|
{
|
||||||
// fill with playtest properties
|
// fill with playtest properties
|
||||||
|
@ -586,8 +445,6 @@ class ChartEditorToolboxHandler
|
||||||
trace('selected node: ${treeView.selectedNode}');
|
trace('selected node: ${treeView.selectedNode}');
|
||||||
}
|
}
|
||||||
|
|
||||||
static function onHideToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
|
||||||
|
|
||||||
static function buildToolboxMetadataLayout(state:ChartEditorState):Null<ChartEditorBaseToolbox>
|
static function buildToolboxMetadataLayout(state:ChartEditorState):Null<ChartEditorBaseToolbox>
|
||||||
{
|
{
|
||||||
var toolbox:ChartEditorBaseToolbox = ChartEditorMetadataToolbox.build(state);
|
var toolbox:ChartEditorBaseToolbox = ChartEditorMetadataToolbox.build(state);
|
||||||
|
@ -597,7 +454,14 @@ class ChartEditorToolboxHandler
|
||||||
return toolbox;
|
return toolbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function onHideToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
static function buildToolboxEventDataLayout(state:ChartEditorState):Null<ChartEditorBaseToolbox>
|
||||||
|
{
|
||||||
|
var toolbox:ChartEditorBaseToolbox = ChartEditorEventDataToolbox.build(state);
|
||||||
|
|
||||||
|
if (toolbox == null) return null;
|
||||||
|
|
||||||
|
return toolbox;
|
||||||
|
}
|
||||||
|
|
||||||
static function buildToolboxPlayerPreviewLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
static function buildToolboxPlayerPreviewLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,6 +3,7 @@ package funkin.ui.debug.charting;
|
||||||
#if !macro
|
#if !macro
|
||||||
// Apply handlers so they can be called as though they were functions in ChartEditorState
|
// Apply handlers so they can be called as though they were functions in ChartEditorState
|
||||||
using funkin.ui.debug.charting.handlers.ChartEditorAudioHandler;
|
using funkin.ui.debug.charting.handlers.ChartEditorAudioHandler;
|
||||||
|
using funkin.ui.debug.charting.handlers.ChartEditorContextMenuHandler;
|
||||||
using funkin.ui.debug.charting.handlers.ChartEditorDialogHandler;
|
using funkin.ui.debug.charting.handlers.ChartEditorDialogHandler;
|
||||||
using funkin.ui.debug.charting.handlers.ChartEditorImportExportHandler;
|
using funkin.ui.debug.charting.handlers.ChartEditorImportExportHandler;
|
||||||
using funkin.ui.debug.charting.handlers.ChartEditorNotificationHandler;
|
using funkin.ui.debug.charting.handlers.ChartEditorNotificationHandler;
|
||||||
|
|
|
@ -0,0 +1,259 @@
|
||||||
|
package funkin.ui.debug.charting.toolboxes;
|
||||||
|
|
||||||
|
import funkin.play.character.BaseCharacter.CharacterType;
|
||||||
|
import funkin.play.character.CharacterData;
|
||||||
|
import funkin.play.stage.StageData;
|
||||||
|
import funkin.play.event.SongEvent;
|
||||||
|
import funkin.data.event.SongEventSchema;
|
||||||
|
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||||
|
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||||
|
import haxe.ui.components.Button;
|
||||||
|
import haxe.ui.components.CheckBox;
|
||||||
|
import haxe.ui.components.DropDown;
|
||||||
|
import haxe.ui.components.HorizontalSlider;
|
||||||
|
import haxe.ui.components.Label;
|
||||||
|
import haxe.ui.components.NumberStepper;
|
||||||
|
import haxe.ui.components.Slider;
|
||||||
|
import haxe.ui.core.Component;
|
||||||
|
import funkin.data.event.SongEventRegistry;
|
||||||
|
import haxe.ui.components.TextField;
|
||||||
|
import haxe.ui.containers.Box;
|
||||||
|
import haxe.ui.containers.Frame;
|
||||||
|
import haxe.ui.events.UIEvent;
|
||||||
|
import haxe.ui.data.ArrayDataSource;
|
||||||
|
import haxe.ui.containers.Grid;
|
||||||
|
import haxe.ui.components.DropDown;
|
||||||
|
import haxe.ui.containers.Frame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The toolbox which allows modifying information like Song Title, Scroll Speed, Characters/Stages, and starting BPM.
|
||||||
|
*/
|
||||||
|
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||||
|
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||||
|
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/toolboxes/event-data.xml"))
|
||||||
|
class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
||||||
|
{
|
||||||
|
var toolboxEventsEventKind:DropDown;
|
||||||
|
var toolboxEventsDataFrame:Frame;
|
||||||
|
var toolboxEventsDataGrid:Grid;
|
||||||
|
|
||||||
|
var _initializing:Bool = true;
|
||||||
|
|
||||||
|
public function new(chartEditorState2:ChartEditorState)
|
||||||
|
{
|
||||||
|
super(chartEditorState2);
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
this.onDialogClosed = onClose;
|
||||||
|
|
||||||
|
this._initializing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClose(event:UIEvent)
|
||||||
|
{
|
||||||
|
chartEditorState.menubarItemToggleToolboxEventData.selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize():Void
|
||||||
|
{
|
||||||
|
toolboxEventsEventKind.dataSource = new ArrayDataSource();
|
||||||
|
|
||||||
|
var songEvents:Array<SongEvent> = SongEventRegistry.listEvents();
|
||||||
|
|
||||||
|
for (event in songEvents)
|
||||||
|
{
|
||||||
|
toolboxEventsEventKind.dataSource.add({text: event.getTitle(), value: event.id});
|
||||||
|
}
|
||||||
|
|
||||||
|
toolboxEventsEventKind.onChange = function(event:UIEvent) {
|
||||||
|
var eventType:String = event.data.value;
|
||||||
|
|
||||||
|
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType');
|
||||||
|
|
||||||
|
// Edit the event data to place.
|
||||||
|
chartEditorState.eventKindToPlace = eventType;
|
||||||
|
|
||||||
|
var schema:SongEventSchema = SongEventRegistry.getEventSchema(eventType);
|
||||||
|
|
||||||
|
if (schema == null)
|
||||||
|
{
|
||||||
|
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Unknown event kind: $eventType');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildEventDataFormFromSchema(toolboxEventsDataGrid, schema);
|
||||||
|
|
||||||
|
if (!_initializing && chartEditorState.currentEventSelection.length > 0)
|
||||||
|
{
|
||||||
|
// Edit the event data of any selected events.
|
||||||
|
for (event in chartEditorState.currentEventSelection)
|
||||||
|
{
|
||||||
|
event.event = chartEditorState.eventKindToPlace;
|
||||||
|
event.value = chartEditorState.eventDataToPlace;
|
||||||
|
}
|
||||||
|
chartEditorState.saveDataDirty = true;
|
||||||
|
chartEditorState.noteDisplayDirty = true;
|
||||||
|
chartEditorState.notePreviewDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toolboxEventsEventKind.value = chartEditorState.eventKindToPlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function refresh():Void
|
||||||
|
{
|
||||||
|
super.refresh();
|
||||||
|
|
||||||
|
toolboxEventsEventKind.value = chartEditorState.eventKindToPlace;
|
||||||
|
|
||||||
|
for (pair in chartEditorState.eventDataToPlace.keyValueIterator())
|
||||||
|
{
|
||||||
|
var fieldId:String = pair.key;
|
||||||
|
var value:Null<Dynamic> = pair.value;
|
||||||
|
|
||||||
|
var field:Component = toolboxEventsDataGrid.findComponent(fieldId);
|
||||||
|
|
||||||
|
if (field == null)
|
||||||
|
{
|
||||||
|
throw 'ChartEditorToolboxHandler.refresh() - Field "${fieldId}" does not exist in the event data form.';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (field)
|
||||||
|
{
|
||||||
|
case Std.isOfType(_, NumberStepper) => true:
|
||||||
|
var numberStepper:NumberStepper = cast field;
|
||||||
|
numberStepper.value = value;
|
||||||
|
case Std.isOfType(_, CheckBox) => true:
|
||||||
|
var checkBox:CheckBox = cast field;
|
||||||
|
checkBox.selected = value;
|
||||||
|
case Std.isOfType(_, DropDown) => true:
|
||||||
|
var dropDown:DropDown = cast field;
|
||||||
|
dropDown.value = value;
|
||||||
|
case Std.isOfType(_, TextField) => true:
|
||||||
|
var textField:TextField = cast field;
|
||||||
|
textField.text = value;
|
||||||
|
default:
|
||||||
|
throw 'ChartEditorToolboxHandler.refresh() - Field "${fieldId}" is of unknown type "${Type.getClassName(Type.getClass(field))}".';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildEventDataFormFromSchema(target:Box, schema:SongEventSchema):Void
|
||||||
|
{
|
||||||
|
trace(schema);
|
||||||
|
// Clear the frame.
|
||||||
|
target.removeAllComponents();
|
||||||
|
|
||||||
|
chartEditorState.eventDataToPlace = {};
|
||||||
|
|
||||||
|
for (field in schema)
|
||||||
|
{
|
||||||
|
if (field == null) continue;
|
||||||
|
|
||||||
|
// Add a label for the data field.
|
||||||
|
var label:Label = new Label();
|
||||||
|
label.text = field.title;
|
||||||
|
label.verticalAlign = "center";
|
||||||
|
target.addComponent(label);
|
||||||
|
|
||||||
|
// Add an input field for the data field.
|
||||||
|
var input:Component;
|
||||||
|
switch (field.type)
|
||||||
|
{
|
||||||
|
case INTEGER:
|
||||||
|
var numberStepper:NumberStepper = new NumberStepper();
|
||||||
|
numberStepper.id = field.name;
|
||||||
|
numberStepper.step = field.step ?? 1.0;
|
||||||
|
numberStepper.min = field.min ?? 0.0;
|
||||||
|
numberStepper.max = field.max ?? 10.0;
|
||||||
|
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
|
||||||
|
input = numberStepper;
|
||||||
|
case FLOAT:
|
||||||
|
var numberStepper:NumberStepper = new NumberStepper();
|
||||||
|
numberStepper.id = field.name;
|
||||||
|
numberStepper.step = field.step ?? 0.1;
|
||||||
|
if (field.min != null) numberStepper.min = field.min;
|
||||||
|
if (field.max != null) numberStepper.max = field.max;
|
||||||
|
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
|
||||||
|
input = numberStepper;
|
||||||
|
case BOOL:
|
||||||
|
var checkBox:CheckBox = new CheckBox();
|
||||||
|
checkBox.id = field.name;
|
||||||
|
if (field.defaultValue != null) checkBox.selected = field.defaultValue;
|
||||||
|
input = checkBox;
|
||||||
|
case ENUM:
|
||||||
|
var dropDown:DropDown = new DropDown();
|
||||||
|
dropDown.id = field.name;
|
||||||
|
dropDown.width = 200.0;
|
||||||
|
dropDown.dataSource = new ArrayDataSource();
|
||||||
|
|
||||||
|
if (field.keys == null) throw 'Field "${field.name}" is of Enum type but has no keys.';
|
||||||
|
|
||||||
|
// Add entries to the dropdown.
|
||||||
|
|
||||||
|
for (optionName in field.keys.keys())
|
||||||
|
{
|
||||||
|
var optionValue:Null<Dynamic> = field.keys.get(optionName);
|
||||||
|
trace('$optionName : $optionValue');
|
||||||
|
dropDown.dataSource.add({value: optionValue, text: optionName});
|
||||||
|
}
|
||||||
|
|
||||||
|
dropDown.value = field.defaultValue;
|
||||||
|
|
||||||
|
input = dropDown;
|
||||||
|
case STRING:
|
||||||
|
input = new TextField();
|
||||||
|
input.id = field.name;
|
||||||
|
if (field.defaultValue != null) input.text = field.defaultValue;
|
||||||
|
default:
|
||||||
|
// Unknown type. Display a label that proclaims the type so we can debug it.
|
||||||
|
input = new Label();
|
||||||
|
input.id = field.name;
|
||||||
|
input.text = field.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
target.addComponent(input);
|
||||||
|
|
||||||
|
// Update the value of the event data.
|
||||||
|
input.onChange = function(event:UIEvent) {
|
||||||
|
var value = event.target.value;
|
||||||
|
if (field.type == ENUM)
|
||||||
|
{
|
||||||
|
value = event.target.value.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${value}');
|
||||||
|
|
||||||
|
// Edit the event data to place.
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
chartEditorState.eventDataToPlace.remove(event.target.id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chartEditorState.eventDataToPlace.set(event.target.id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit the event data of any existing events.
|
||||||
|
if (!_initializing && chartEditorState.currentEventSelection.length > 0)
|
||||||
|
{
|
||||||
|
for (event in chartEditorState.currentEventSelection)
|
||||||
|
{
|
||||||
|
event.event = chartEditorState.eventKindToPlace;
|
||||||
|
event.value = chartEditorState.eventDataToPlace;
|
||||||
|
}
|
||||||
|
chartEditorState.saveDataDirty = true;
|
||||||
|
chartEditorState.noteDisplayDirty = true;
|
||||||
|
chartEditorState.notePreviewDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function build(chartEditorState:ChartEditorState):ChartEditorEventDataToolbox
|
||||||
|
{
|
||||||
|
return new ChartEditorEventDataToolbox(chartEditorState);
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,9 +140,9 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
||||||
if (event.value == null) return;
|
if (event.value == null) return;
|
||||||
|
|
||||||
chartEditorState.currentInstrumentalOffset = event.value;
|
chartEditorState.currentInstrumentalOffset = event.value;
|
||||||
Conductor.instrumentalOffset = event.value;
|
Conductor.instance.instrumentalOffset = event.value;
|
||||||
// Update song length.
|
// Update song length.
|
||||||
chartEditorState.songLengthInMs = (chartEditorState.audioInstTrack?.length ?? 1000.0) + Conductor.instrumentalOffset;
|
chartEditorState.songLengthInMs = (chartEditorState.audioInstTrack?.length ?? 1000.0) + Conductor.instance.instrumentalOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
inputOffsetVocal.onChange = function(event:UIEvent) {
|
inputOffsetVocal.onChange = function(event:UIEvent) {
|
||||||
|
@ -182,6 +182,8 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
||||||
|
|
||||||
public override function refresh():Void
|
public override function refresh():Void
|
||||||
{
|
{
|
||||||
|
super.refresh();
|
||||||
|
|
||||||
inputSongName.value = chartEditorState.currentSongMetadata.songName;
|
inputSongName.value = chartEditorState.currentSongMetadata.songName;
|
||||||
inputSongArtist.value = chartEditorState.currentSongMetadata.artist;
|
inputSongArtist.value = chartEditorState.currentSongMetadata.artist;
|
||||||
inputStage.value = chartEditorState.currentSongMetadata.playData.stage;
|
inputStage.value = chartEditorState.currentSongMetadata.playData.stage;
|
||||||
|
|
|
@ -75,7 +75,7 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
// funnyStatsGraph.hi
|
// funnyStatsGraph.hi
|
||||||
|
|
||||||
Conductor.forceBPM(60);
|
Conductor.instance.forceBPM(60);
|
||||||
|
|
||||||
noteGrp = new FlxTypedGroup<NoteSprite>();
|
noteGrp = new FlxTypedGroup<NoteSprite>();
|
||||||
add(noteGrp);
|
add(noteGrp);
|
||||||
|
@ -91,14 +91,14 @@ class LatencyState extends MusicBeatSubState
|
||||||
// // musSpec.visType = FREQUENCIES;
|
// // musSpec.visType = FREQUENCIES;
|
||||||
// add(musSpec);
|
// add(musSpec);
|
||||||
|
|
||||||
for (beat in 0...Math.floor(FlxG.sound.music.length / Conductor.beatLengthMs))
|
for (beat in 0...Math.floor(FlxG.sound.music.length / Conductor.instance.beatLengthMs))
|
||||||
{
|
{
|
||||||
var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * Conductor.beatLengthMs), FlxG.height - 15);
|
var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * Conductor.instance.beatLengthMs), FlxG.height - 15);
|
||||||
beatTick.makeGraphic(2, 15);
|
beatTick.makeGraphic(2, 15);
|
||||||
beatTick.alpha = 0.3;
|
beatTick.alpha = 0.3;
|
||||||
add(beatTick);
|
add(beatTick);
|
||||||
|
|
||||||
var offsetTxt:FlxText = new FlxText(songPosToX(beat * Conductor.beatLengthMs), FlxG.height - 26, 0, "swag");
|
var offsetTxt:FlxText = new FlxText(songPosToX(beat * Conductor.instance.beatLengthMs), FlxG.height - 26, 0, "swag");
|
||||||
offsetTxt.alpha = 0.5;
|
offsetTxt.alpha = 0.5;
|
||||||
diffGrp.add(offsetTxt);
|
diffGrp.add(offsetTxt);
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
for (i in 0...32)
|
for (i in 0...32)
|
||||||
{
|
{
|
||||||
var note:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault(), Conductor.beatLengthMs * i);
|
var note:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault(), Conductor.instance.beatLengthMs * i);
|
||||||
noteGrp.add(note);
|
noteGrp.add(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,9 +146,9 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
override function stepHit():Bool
|
override function stepHit():Bool
|
||||||
{
|
{
|
||||||
if (Conductor.currentStep % 4 == 2)
|
if (Conductor.instance.currentStep % 4 == 2)
|
||||||
{
|
{
|
||||||
blocks.members[((Conductor.currentBeat % 8) + 1) % 8].alpha = 0.5;
|
blocks.members[((Conductor.instance.currentBeat % 8) + 1) % 8].alpha = 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.stepHit();
|
return super.stepHit();
|
||||||
|
@ -156,11 +156,11 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
override function beatHit():Bool
|
override function beatHit():Bool
|
||||||
{
|
{
|
||||||
if (Conductor.currentBeat % 8 == 0) blocks.forEach(blok -> {
|
if (Conductor.instance.currentBeat % 8 == 0) blocks.forEach(blok -> {
|
||||||
blok.alpha = 0;
|
blok.alpha = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
blocks.members[Conductor.currentBeat % 8].alpha = 1;
|
blocks.members[Conductor.instance.currentBeat % 8].alpha = 1;
|
||||||
// block.visible = !block.visible;
|
// block.visible = !block.visible;
|
||||||
|
|
||||||
return super.beatHit();
|
return super.beatHit();
|
||||||
|
@ -192,17 +192,17 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
if (FlxG.keys.pressed.D) FlxG.sound.music.time += 1000 * FlxG.elapsed;
|
if (FlxG.keys.pressed.D) FlxG.sound.music.time += 1000 * FlxG.elapsed;
|
||||||
|
|
||||||
Conductor.update(swagSong.getTimeWithDiff() - Conductor.inputOffset);
|
Conductor.instance.update(swagSong.getTimeWithDiff() - Conductor.instance.inputOffset);
|
||||||
// Conductor.songPosition += (Timer.stamp() * 1000) - FlxG.sound.music.prevTimestamp;
|
// Conductor.instance.songPosition += (Timer.stamp() * 1000) - FlxG.sound.music.prevTimestamp;
|
||||||
|
|
||||||
songPosVis.x = songPosToX(Conductor.songPosition);
|
songPosVis.x = songPosToX(Conductor.instance.songPosition);
|
||||||
songVisFollowAudio.x = songPosToX(Conductor.songPosition - Conductor.instrumentalOffset);
|
songVisFollowAudio.x = songPosToX(Conductor.instance.songPosition - Conductor.instance.instrumentalOffset);
|
||||||
songVisFollowVideo.x = songPosToX(Conductor.songPosition - Conductor.inputOffset);
|
songVisFollowVideo.x = songPosToX(Conductor.instance.songPosition - Conductor.instance.inputOffset);
|
||||||
|
|
||||||
offsetText.text = "INST Offset: " + Conductor.instrumentalOffset + "ms";
|
offsetText.text = "INST Offset: " + Conductor.instance.instrumentalOffset + "ms";
|
||||||
offsetText.text += "\nINPUT Offset: " + Conductor.inputOffset + "ms";
|
offsetText.text += "\nINPUT Offset: " + Conductor.instance.inputOffset + "ms";
|
||||||
offsetText.text += "\ncurrentStep: " + Conductor.currentStep;
|
offsetText.text += "\ncurrentStep: " + Conductor.instance.currentStep;
|
||||||
offsetText.text += "\ncurrentBeat: " + Conductor.currentBeat;
|
offsetText.text += "\ncurrentBeat: " + Conductor.instance.currentBeat;
|
||||||
|
|
||||||
var avgOffsetInput:Float = 0;
|
var avgOffsetInput:Float = 0;
|
||||||
|
|
||||||
|
@ -221,24 +221,24 @@ class LatencyState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
if (FlxG.keys.justPressed.RIGHT)
|
if (FlxG.keys.justPressed.RIGHT)
|
||||||
{
|
{
|
||||||
Conductor.instrumentalOffset += 1.0 * multiply;
|
Conductor.instance.instrumentalOffset += 1.0 * multiply;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlxG.keys.justPressed.LEFT)
|
if (FlxG.keys.justPressed.LEFT)
|
||||||
{
|
{
|
||||||
Conductor.instrumentalOffset -= 1.0 * multiply;
|
Conductor.instance.instrumentalOffset -= 1.0 * multiply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FlxG.keys.justPressed.RIGHT)
|
if (FlxG.keys.justPressed.RIGHT)
|
||||||
{
|
{
|
||||||
Conductor.inputOffset += 1.0 * multiply;
|
Conductor.instance.inputOffset += 1.0 * multiply;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlxG.keys.justPressed.LEFT)
|
if (FlxG.keys.justPressed.LEFT)
|
||||||
{
|
{
|
||||||
Conductor.inputOffset -= 1.0 * multiply;
|
Conductor.instance.inputOffset -= 1.0 * multiply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ class LatencyState extends MusicBeatSubState
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
noteGrp.forEach(function(daNote:NoteSprite) {
|
noteGrp.forEach(function(daNote:NoteSprite) {
|
||||||
daNote.y = (strumLine.y - ((Conductor.songPosition - Conductor.instrumentalOffset) - daNote.noteData.time) * 0.45);
|
daNote.y = (strumLine.y - ((Conductor.instance.songPosition - Conductor.instance.instrumentalOffset) - daNote.noteData.time) * 0.45);
|
||||||
daNote.x = strumLine.x + 30;
|
daNote.x = strumLine.x + 30;
|
||||||
|
|
||||||
if (daNote.y < strumLine.y) daNote.alpha = 0.5;
|
if (daNote.y < strumLine.y) daNote.alpha = 0.5;
|
||||||
|
@ -258,7 +258,7 @@ class LatencyState extends MusicBeatSubState
|
||||||
if (daNote.y < 0 - daNote.height)
|
if (daNote.y < 0 - daNote.height)
|
||||||
{
|
{
|
||||||
daNote.alpha = 1;
|
daNote.alpha = 1;
|
||||||
// daNote.data.strumTime += Conductor.beatLengthMs * 8;
|
// daNote.data.strumTime += Conductor.instance.beatLengthMs * 8;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -267,14 +267,14 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
function generateBeatStuff()
|
function generateBeatStuff()
|
||||||
{
|
{
|
||||||
Conductor.update(swagSong.getTimeWithDiff());
|
Conductor.instance.update(swagSong.getTimeWithDiff());
|
||||||
|
|
||||||
var closestBeat:Int = Math.round(Conductor.songPosition / Conductor.beatLengthMs) % diffGrp.members.length;
|
var closestBeat:Int = Math.round(Conductor.instance.songPosition / Conductor.instance.beatLengthMs) % diffGrp.members.length;
|
||||||
var getDiff:Float = Conductor.songPosition - (closestBeat * Conductor.beatLengthMs);
|
var getDiff:Float = Conductor.instance.songPosition - (closestBeat * Conductor.instance.beatLengthMs);
|
||||||
getDiff -= Conductor.inputOffset;
|
getDiff -= Conductor.instance.inputOffset;
|
||||||
|
|
||||||
// lil fix for end of song
|
// lil fix for end of song
|
||||||
if (closestBeat == 0 && getDiff >= Conductor.beatLengthMs * 2) getDiff -= FlxG.sound.music.length;
|
if (closestBeat == 0 && getDiff >= Conductor.instance.beatLengthMs * 2) getDiff -= FlxG.sound.music.length;
|
||||||
|
|
||||||
trace("\tDISTANCE TO CLOSEST BEAT: " + getDiff + "ms");
|
trace("\tDISTANCE TO CLOSEST BEAT: " + getDiff + "ms");
|
||||||
trace("\tCLOSEST BEAT: " + closestBeat);
|
trace("\tCLOSEST BEAT: " + closestBeat);
|
||||||
|
|
|
@ -238,7 +238,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
var freakyMenuMetadata:Null<SongMusicData> = SongRegistry.instance.parseMusicData('freakyMenu');
|
var freakyMenuMetadata:Null<SongMusicData> = SongRegistry.instance.parseMusicData('freakyMenu');
|
||||||
if (freakyMenuMetadata != null)
|
if (freakyMenuMetadata != null)
|
||||||
{
|
{
|
||||||
Conductor.mapTimeChanges(freakyMenuMetadata.timeChanges);
|
Conductor.instance.mapTimeChanges(freakyMenuMetadata.timeChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'), 0);
|
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'), 0);
|
||||||
|
@ -317,7 +317,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float)
|
||||||
{
|
{
|
||||||
Conductor.update();
|
Conductor.instance.update();
|
||||||
|
|
||||||
highScoreLerp = Std.int(MathUtil.coolLerp(highScoreLerp, highScore, 0.5));
|
highScoreLerp = Std.int(MathUtil.coolLerp(highScoreLerp, highScore, 0.5));
|
||||||
|
|
||||||
|
|
|
@ -221,7 +221,7 @@ class TitleState extends MusicBeatState
|
||||||
var freakyMenuMetadata:Null<SongMusicData> = SongRegistry.instance.parseMusicData('freakyMenu');
|
var freakyMenuMetadata:Null<SongMusicData> = SongRegistry.instance.parseMusicData('freakyMenu');
|
||||||
if (freakyMenuMetadata != null)
|
if (freakyMenuMetadata != null)
|
||||||
{
|
{
|
||||||
Conductor.mapTimeChanges(freakyMenuMetadata.timeChanges);
|
Conductor.instance.mapTimeChanges(freakyMenuMetadata.timeChanges);
|
||||||
}
|
}
|
||||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'), 0);
|
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'), 0);
|
||||||
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
||||||
|
@ -256,7 +256,7 @@ class TitleState extends MusicBeatState
|
||||||
if (FlxG.keys.pressed.DOWN) FlxG.sound.music.pitch -= 0.5 * elapsed;
|
if (FlxG.keys.pressed.DOWN) FlxG.sound.music.pitch -= 0.5 * elapsed;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
Conductor.update();
|
Conductor.instance.update();
|
||||||
|
|
||||||
/* if (FlxG.onMobile)
|
/* if (FlxG.onMobile)
|
||||||
{
|
{
|
||||||
|
@ -280,7 +280,7 @@ class TitleState extends MusicBeatState
|
||||||
FlxTween.tween(FlxG.stage.window, {y: FlxG.stage.window.y + 100}, 0.7, {ease: FlxEase.quadInOut, type: PINGPONG});
|
FlxTween.tween(FlxG.stage.window, {y: FlxG.stage.window.y + 100}, 0.7, {ease: FlxEase.quadInOut, type: PINGPONG});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlxG.sound.music != null) Conductor.update(FlxG.sound.music.time);
|
if (FlxG.sound.music != null) Conductor.instance.update(FlxG.sound.music.time);
|
||||||
if (FlxG.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen;
|
if (FlxG.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen;
|
||||||
|
|
||||||
// do controls.PAUSE | controls.ACCEPT instead?
|
// do controls.PAUSE | controls.ACCEPT instead?
|
||||||
|
@ -390,7 +390,7 @@ class TitleState extends MusicBeatState
|
||||||
var spec:SpectogramSprite = new SpectogramSprite(FlxG.sound.music);
|
var spec:SpectogramSprite = new SpectogramSprite(FlxG.sound.music);
|
||||||
add(spec);
|
add(spec);
|
||||||
|
|
||||||
Conductor.forceBPM(190);
|
Conductor.instance.forceBPM(190);
|
||||||
FlxG.camera.flash(FlxColor.WHITE, 1);
|
FlxG.camera.flash(FlxColor.WHITE, 1);
|
||||||
FlxG.sound.play(Paths.sound('confirmMenu'), 0.7);
|
FlxG.sound.play(Paths.sound('confirmMenu'), 0.7);
|
||||||
}
|
}
|
||||||
|
@ -442,13 +442,13 @@ class TitleState extends MusicBeatState
|
||||||
|
|
||||||
if (!skippedIntro)
|
if (!skippedIntro)
|
||||||
{
|
{
|
||||||
// FlxG.log.add(Conductor.currentBeat);
|
// FlxG.log.add(Conductor.instance.currentBeat);
|
||||||
// if the user is draggin the window some beats will
|
// if the user is draggin the window some beats will
|
||||||
// be missed so this is just to compensate
|
// be missed so this is just to compensate
|
||||||
if (Conductor.currentBeat > lastBeat)
|
if (Conductor.instance.currentBeat > lastBeat)
|
||||||
{
|
{
|
||||||
// TODO: Why does it perform ALL the previous steps each beat?
|
// TODO: Why does it perform ALL the previous steps each beat?
|
||||||
for (i in lastBeat...Conductor.currentBeat)
|
for (i in lastBeat...Conductor.instance.currentBeat)
|
||||||
{
|
{
|
||||||
switch (i + 1)
|
switch (i + 1)
|
||||||
{
|
{
|
||||||
|
@ -483,11 +483,11 @@ class TitleState extends MusicBeatState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastBeat = Conductor.currentBeat;
|
lastBeat = Conductor.instance.currentBeat;
|
||||||
}
|
}
|
||||||
if (skippedIntro)
|
if (skippedIntro)
|
||||||
{
|
{
|
||||||
if (cheatActive && Conductor.currentBeat % 2 == 0) swagShader.update(0.125);
|
if (cheatActive && Conductor.instance.currentBeat % 2 == 0) swagShader.update(0.125);
|
||||||
|
|
||||||
if (logoBl != null && logoBl.animation != null) logoBl.animation.play('bump', true);
|
if (logoBl != null && logoBl.animation != null) logoBl.animation.play('bump', true);
|
||||||
|
|
||||||
|
|
35
source/funkin/util/plugins/EvacuateDebugPlugin.hx
Normal file
35
source/funkin/util/plugins/EvacuateDebugPlugin.hx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package funkin.util.plugins;
|
||||||
|
|
||||||
|
import flixel.FlxBasic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plugin which adds functionality to press `F4` to immediately transition to the main menu.
|
||||||
|
* This is useful for debugging or if you get softlocked or something.
|
||||||
|
*/
|
||||||
|
class EvacuateDebugPlugin extends FlxBasic
|
||||||
|
{
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function initialize():Void
|
||||||
|
{
|
||||||
|
FlxG.plugins.addPlugin(new EvacuateDebugPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function update(elapsed:Float):Void
|
||||||
|
{
|
||||||
|
super.update(elapsed);
|
||||||
|
|
||||||
|
if (FlxG.keys.justPressed.F4)
|
||||||
|
{
|
||||||
|
FlxG.switchState(new funkin.ui.mainmenu.MainMenuState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function destroy():Void
|
||||||
|
{
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
}
|
5
source/funkin/util/plugins/README.md
Normal file
5
source/funkin/util/plugins/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# funkin.util.plugins
|
||||||
|
|
||||||
|
Flixel plugins are objects with `update()` functions that are called from every state.
|
||||||
|
|
||||||
|
See: https://github.com/HaxeFlixel/flixel/blob/dev/flixel/system/frontEnds/PluginFrontEnd.hx
|
38
source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx
Normal file
38
source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package funkin.util.plugins;
|
||||||
|
|
||||||
|
import flixel.FlxBasic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plugin which adds functionality to press `F5` to reload all game assets, then reload the current state.
|
||||||
|
* This is useful for hot reloading assets during development.
|
||||||
|
*/
|
||||||
|
class ReloadAssetsDebugPlugin extends FlxBasic
|
||||||
|
{
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function initialize():Void
|
||||||
|
{
|
||||||
|
FlxG.plugins.addPlugin(new ReloadAssetsDebugPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function update(elapsed:Float):Void
|
||||||
|
{
|
||||||
|
super.update(elapsed);
|
||||||
|
|
||||||
|
if (FlxG.keys.justPressed.F5)
|
||||||
|
{
|
||||||
|
funkin.modding.PolymodHandler.forceReloadAssets();
|
||||||
|
|
||||||
|
// Create a new instance of the current state, so old data is cleared.
|
||||||
|
FlxG.resetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function destroy():Void
|
||||||
|
{
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
}
|
38
source/funkin/util/plugins/WatchPlugin.hx
Normal file
38
source/funkin/util/plugins/WatchPlugin.hx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package funkin.util.plugins;
|
||||||
|
|
||||||
|
import flixel.FlxBasic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plugin which adds functionality to display several universally important values
|
||||||
|
* in the Flixel variable watch window.
|
||||||
|
*/
|
||||||
|
class WatchPlugin extends FlxBasic
|
||||||
|
{
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function initialize():Void
|
||||||
|
{
|
||||||
|
FlxG.plugins.addPlugin(new WatchPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function update(elapsed:Float):Void
|
||||||
|
{
|
||||||
|
super.update(elapsed);
|
||||||
|
|
||||||
|
FlxG.watch.addQuick("songPosition", Conductor.instance.songPosition);
|
||||||
|
FlxG.watch.addQuick("songPositionNoOffset", Conductor.instance.songPosition + Conductor.instance.instrumentalOffset);
|
||||||
|
FlxG.watch.addQuick("musicTime", FlxG.sound?.music?.time ?? 0.0);
|
||||||
|
FlxG.watch.addQuick("bpm", Conductor.instance.bpm);
|
||||||
|
FlxG.watch.addQuick("currentMeasureTime", Conductor.instance.currentMeasureTime);
|
||||||
|
FlxG.watch.addQuick("currentBeatTime", Conductor.instance.currentBeatTime);
|
||||||
|
FlxG.watch.addQuick("currentStepTime", Conductor.instance.currentStepTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function destroy():Void
|
||||||
|
{
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
}
|
|
@ -77,6 +77,22 @@ class ArrayTools
|
||||||
array.pop();
|
array.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new array with all elements of the given array, to prevent modifying the original.
|
||||||
|
*/
|
||||||
|
public static function clone<T>(array:Array<T>):Array<T>
|
||||||
|
{
|
||||||
|
return [for (element in array) element];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new array with clones of all elements of the given array, to prevent modifying the original.
|
||||||
|
*/
|
||||||
|
public static function deepClone<T, U:ICloneable<T>>(array:Array<U>):Array<T>
|
||||||
|
{
|
||||||
|
return [for (element in array) element.clone()];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true only if both arrays contain the same elements (possibly in a different order).
|
* Return true only if both arrays contain the same elements (possibly in a different order).
|
||||||
* @param a The first array to compare.
|
* @param a The first array to compare.
|
||||||
|
|
10
source/funkin/util/tools/ICloneable.hx
Normal file
10
source/funkin/util/tools/ICloneable.hx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package funkin.util.tools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this on a class to enable `Array<T>.deepClone()` to work on it.
|
||||||
|
* NOTE: T should be the type of the class that implements this interface.
|
||||||
|
*/
|
||||||
|
interface ICloneable<T>
|
||||||
|
{
|
||||||
|
public function clone():T;
|
||||||
|
}
|
|
@ -25,6 +25,33 @@ class MapTools
|
||||||
return [for (i in map.iterator()) i];
|
return [for (i in map.iterator()) i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new array with all elements of the given array, to prevent modifying the original.
|
||||||
|
*/
|
||||||
|
public static function clone<K, T>(map:Map<K, T>):Map<K, T>
|
||||||
|
{
|
||||||
|
return map.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new array with clones of all elements of the given array, to prevent modifying the original.
|
||||||
|
*/
|
||||||
|
public static function deepClone<K, T, U:ICloneable<T>>(map:Map<K, U>):Map<K, T>
|
||||||
|
{
|
||||||
|
// TODO: This function does NOT work.
|
||||||
|
throw "Not implemented";
|
||||||
|
|
||||||
|
/*
|
||||||
|
var newMap:Map<K, T> = [];
|
||||||
|
// Replace each value with a clone of itself.
|
||||||
|
for (key in newMap.keys())
|
||||||
|
{
|
||||||
|
newMap.set(key, newMap.get(key).clone());
|
||||||
|
}
|
||||||
|
return newMap;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of keys from the map (as an array, rather than an iterator).
|
* Return a list of keys from the map (as an array, rather than an iterator).
|
||||||
* TODO: Rename this?
|
* TODO: Rename this?
|
||||||
|
|
|
@ -31,23 +31,23 @@ class ConductorTest extends FunkinTest
|
||||||
{
|
{
|
||||||
// NOTE: Expected value comes first.
|
// NOTE: Expected value comes first.
|
||||||
|
|
||||||
Assert.areEqual([], Conductor.timeChanges);
|
Assert.areEqual([], Conductor.instance.timeChanges);
|
||||||
Assert.areEqual(null, Conductor.currentTimeChange);
|
Assert.areEqual(null, Conductor.instance.currentTimeChange);
|
||||||
|
|
||||||
Assert.areEqual(0, Conductor.songPosition);
|
Assert.areEqual(0, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(Constants.DEFAULT_BPM, Conductor.bpm);
|
Assert.areEqual(Constants.DEFAULT_BPM, Conductor.instance.bpm);
|
||||||
Assert.areEqual(null, Conductor.bpmOverride);
|
Assert.areEqual(null, Conductor.instance.bpmOverride);
|
||||||
|
|
||||||
Assert.areEqual(600, Conductor.beatLengthMs);
|
Assert.areEqual(600, Conductor.instance.beatLengthMs);
|
||||||
|
|
||||||
Assert.areEqual(4, Conductor.timeSignatureNumerator);
|
Assert.areEqual(4, Conductor.instance.timeSignatureNumerator);
|
||||||
Assert.areEqual(4, Conductor.timeSignatureDenominator);
|
Assert.areEqual(4, Conductor.instance.timeSignatureDenominator);
|
||||||
|
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
Assert.areEqual(0.0, Conductor.currentStepTime);
|
Assert.areEqual(0.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
Assert.areEqual(150, Conductor.stepLengthMs);
|
Assert.areEqual(150, Conductor.instance.stepLengthMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,23 +60,23 @@ class ConductorTest extends FunkinTest
|
||||||
var currentConductorState:Null<ConductorState> = conductorState;
|
var currentConductorState:Null<ConductorState> = conductorState;
|
||||||
Assert.isNotNull(currentConductorState);
|
Assert.isNotNull(currentConductorState);
|
||||||
|
|
||||||
Assert.areEqual(0, Conductor.songPosition);
|
Assert.areEqual(0, Conductor.instance.songPosition);
|
||||||
|
|
||||||
step(); // 1
|
step(); // 1
|
||||||
|
|
||||||
var BPM_100_STEP_TIME = 1 / 9;
|
var BPM_100_STEP_TIME = 1 / 9;
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(1 / 9, Conductor.currentStepTime);
|
FunkinAssert.areNear(1 / 9, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(7); // 8
|
step(7); // 8
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 8, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 8, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(8 / 9, Conductor.currentStepTime);
|
FunkinAssert.areNear(8 / 9, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
Assert.areEqual(0, currentConductorState.beatsHit);
|
Assert.areEqual(0, currentConductorState.beatsHit);
|
||||||
Assert.areEqual(0, currentConductorState.stepsHit);
|
Assert.areEqual(0, currentConductorState.stepsHit);
|
||||||
|
@ -88,10 +88,10 @@ class ConductorTest extends FunkinTest
|
||||||
currentConductorState.beatsHit = 0;
|
currentConductorState.beatsHit = 0;
|
||||||
currentConductorState.stepsHit = 0;
|
currentConductorState.stepsHit = 0;
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 9, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 9, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(1, Conductor.currentStep);
|
Assert.areEqual(1, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(1.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(1.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(35 - 9); // 35
|
step(35 - 9); // 35
|
||||||
|
|
||||||
|
@ -100,10 +100,10 @@ class ConductorTest extends FunkinTest
|
||||||
currentConductorState.beatsHit = 0;
|
currentConductorState.beatsHit = 0;
|
||||||
currentConductorState.stepsHit = 0;
|
currentConductorState.stepsHit = 0;
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 35, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 35, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(3, Conductor.currentStep);
|
Assert.areEqual(3, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(3.0 + 8 / 9, Conductor.currentStepTime);
|
FunkinAssert.areNear(3.0 + 8 / 9, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 36
|
step(); // 36
|
||||||
|
|
||||||
|
@ -112,83 +112,83 @@ class ConductorTest extends FunkinTest
|
||||||
currentConductorState.beatsHit = 0;
|
currentConductorState.beatsHit = 0;
|
||||||
currentConductorState.stepsHit = 0;
|
currentConductorState.stepsHit = 0;
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 36, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 36, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(1, Conductor.currentBeat);
|
Assert.areEqual(1, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(4, Conductor.currentStep);
|
Assert.areEqual(4, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(4.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(4.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(50 - 36); // 50
|
step(50 - 36); // 50
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 50, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 50, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(1, Conductor.currentBeat);
|
Assert.areEqual(1, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(5, Conductor.currentStep);
|
Assert.areEqual(5, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(5.555555, Conductor.currentStepTime);
|
FunkinAssert.areNear(5.555555, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(49); // 99
|
step(49); // 99
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 99, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 99, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(2, Conductor.currentBeat);
|
Assert.areEqual(2, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(11, Conductor.currentStep);
|
Assert.areEqual(11, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(11.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(11.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(1); // 100
|
step(1); // 100
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 100, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 100, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(2, Conductor.currentBeat);
|
Assert.areEqual(2, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(11, Conductor.currentStep);
|
Assert.areEqual(11, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(11.111111, Conductor.currentStepTime);
|
FunkinAssert.areNear(11.111111, Conductor.instance.currentStepTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
function testUpdateForcedBPM():Void
|
function testUpdateForcedBPM():Void
|
||||||
{
|
{
|
||||||
Conductor.forceBPM(60);
|
Conductor.instance.forceBPM(60);
|
||||||
|
|
||||||
Assert.areEqual(0, Conductor.songPosition);
|
Assert.areEqual(0, Conductor.instance.songPosition);
|
||||||
|
|
||||||
// 60 beats per minute = 1 beat per second
|
// 60 beats per minute = 1 beat per second
|
||||||
// 1 beat per second = 1/60 beats per frame = 4/60 steps per frame
|
// 1 beat per second = 1/60 beats per frame = 4/60 steps per frame
|
||||||
step(); // Advances time 1/60 of 1 second.
|
step(); // Advances time 1/60 of 1 second.
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(4 / 60, Conductor.currentStepTime); // 1/60 of 1 beat = 4/60 of 1 step
|
FunkinAssert.areNear(4 / 60, Conductor.instance.currentStepTime); // 1/60 of 1 beat = 4/60 of 1 step
|
||||||
|
|
||||||
step(14 - 1); // 14
|
step(14 - 1); // 14
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 14, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 14, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(1.0 - 4 / 60, Conductor.currentStepTime); // 1/60 of 1 beat = 4/60 of 1 step
|
FunkinAssert.areNear(1.0 - 4 / 60, Conductor.instance.currentStepTime); // 1/60 of 1 beat = 4/60 of 1 step
|
||||||
|
|
||||||
step(); // 15
|
step(); // 15
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 15, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 15, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(1, Conductor.currentStep);
|
Assert.areEqual(1, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(1.0, Conductor.currentStepTime); // 1/60 of 1 beat = 4/60 of 1 step
|
FunkinAssert.areNear(1.0, Conductor.instance.currentStepTime); // 1/60 of 1 beat = 4/60 of 1 step
|
||||||
|
|
||||||
step(45 - 1); // 59
|
step(45 - 1); // 59
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 59, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 59, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(3, Conductor.currentStep);
|
Assert.areEqual(3, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(4.0 - 4 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(4.0 - 4 / 60, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 60
|
step(); // 60
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 60, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 60, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(1, Conductor.currentBeat);
|
Assert.areEqual(1, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(4, Conductor.currentStep);
|
Assert.areEqual(4, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(4.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(4.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 61
|
step(); // 61
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 61, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 61, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(1, Conductor.currentBeat);
|
Assert.areEqual(1, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(4, Conductor.currentStep);
|
Assert.areEqual(4, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(4.0 + 4 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(4.0 + 4 / 60, Conductor.instance.currentStepTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -196,50 +196,50 @@ class ConductorTest extends FunkinTest
|
||||||
{
|
{
|
||||||
// Start the song with a BPM of 120.
|
// Start the song with a BPM of 120.
|
||||||
var songTimeChanges:Array<SongTimeChange> = [new SongTimeChange(0, 120)];
|
var songTimeChanges:Array<SongTimeChange> = [new SongTimeChange(0, 120)];
|
||||||
Conductor.mapTimeChanges(songTimeChanges);
|
Conductor.instance.mapTimeChanges(songTimeChanges);
|
||||||
|
|
||||||
// All should be at 0.
|
// All should be at 0.
|
||||||
FunkinAssert.areNear(0, Conductor.songPosition);
|
FunkinAssert.areNear(0, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(0.0, Conductor.currentStepTime); // 2/120 of 1 beat = 8/120 of 1 step
|
FunkinAssert.areNear(0.0, Conductor.instance.currentStepTime); // 2/120 of 1 beat = 8/120 of 1 step
|
||||||
|
|
||||||
// 120 beats per minute = 2 beat per second
|
// 120 beats per minute = 2 beat per second
|
||||||
// 2 beat per second = 2/60 beats per frame = 16/120 steps per frame
|
// 2 beat per second = 2/60 beats per frame = 16/120 steps per frame
|
||||||
step(); // Advances time 1/60 of 1 second.
|
step(); // Advances time 1/60 of 1 second.
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(16 / 120, Conductor.currentStepTime); // 2/120 of 1 beat = 8/120 of 1 step
|
FunkinAssert.areNear(16 / 120, Conductor.instance.currentStepTime); // 2/120 of 1 beat = 8/120 of 1 step
|
||||||
|
|
||||||
step(15 - 1); // 15
|
step(15 - 1); // 15
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 15, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 15, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(2, Conductor.currentStep);
|
Assert.areEqual(2, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(2.0, Conductor.currentStepTime); // 2/60 of 1 beat = 8/60 of 1 step
|
FunkinAssert.areNear(2.0, Conductor.instance.currentStepTime); // 2/60 of 1 beat = 8/60 of 1 step
|
||||||
|
|
||||||
step(45 - 1); // 59
|
step(45 - 1); // 59
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 59, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 59, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(1, Conductor.currentBeat);
|
Assert.areEqual(1, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(7, Conductor.currentStep);
|
Assert.areEqual(7, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(7.0 + 104 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(7.0 + 104 / 120, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 60
|
step(); // 60
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 60, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 60, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(2, Conductor.currentBeat);
|
Assert.areEqual(2, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(8, Conductor.currentStep);
|
Assert.areEqual(8, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(8.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(8.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 61
|
step(); // 61
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 61, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 61, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(2, Conductor.currentBeat);
|
Assert.areEqual(2, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(8, Conductor.currentStep);
|
Assert.areEqual(8, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(8.0 + 8 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(8.0 + 8 / 60, Conductor.instance.currentStepTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -247,57 +247,57 @@ class ConductorTest extends FunkinTest
|
||||||
{
|
{
|
||||||
// Start the song with a BPM of 120.
|
// Start the song with a BPM of 120.
|
||||||
var songTimeChanges:Array<SongTimeChange> = [new SongTimeChange(0, 120), new SongTimeChange(3000, 90)];
|
var songTimeChanges:Array<SongTimeChange> = [new SongTimeChange(0, 120), new SongTimeChange(3000, 90)];
|
||||||
Conductor.mapTimeChanges(songTimeChanges);
|
Conductor.instance.mapTimeChanges(songTimeChanges);
|
||||||
|
|
||||||
// All should be at 0.
|
// All should be at 0.
|
||||||
FunkinAssert.areNear(0, Conductor.songPosition);
|
FunkinAssert.areNear(0, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(0.0, Conductor.currentStepTime); // 2/120 of 1 beat = 8/120 of 1 step
|
FunkinAssert.areNear(0.0, Conductor.instance.currentStepTime); // 2/120 of 1 beat = 8/120 of 1 step
|
||||||
|
|
||||||
// 120 beats per minute = 2 beat per second
|
// 120 beats per minute = 2 beat per second
|
||||||
// 2 beat per second = 2/60 beats per frame = 16/120 steps per frame
|
// 2 beat per second = 2/60 beats per frame = 16/120 steps per frame
|
||||||
step(); // Advances time 1/60 of 1 second.
|
step(); // Advances time 1/60 of 1 second.
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(16 / 120, Conductor.currentStepTime); // 4/120 of 1 beat = 16/120 of 1 step
|
FunkinAssert.areNear(16 / 120, Conductor.instance.currentStepTime); // 4/120 of 1 beat = 16/120 of 1 step
|
||||||
|
|
||||||
step(60 - 1 - 1); // 59
|
step(60 - 1 - 1); // 59
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 59, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 59, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(1, Conductor.currentBeat);
|
Assert.areEqual(1, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(7, Conductor.currentStep);
|
Assert.areEqual(7, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(7.0 + 104 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(7.0 + 104 / 120, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 60
|
step(); // 60
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 60, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 60, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(2, Conductor.currentBeat);
|
Assert.areEqual(2, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(8, Conductor.currentStep);
|
Assert.areEqual(8, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(8.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(8.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 61
|
step(); // 61
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 61, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 61, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(2, Conductor.currentBeat);
|
Assert.areEqual(2, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(8, Conductor.currentStep);
|
Assert.areEqual(8, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(8.0 + 8 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(8.0 + 8 / 60, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(179 - 61); // 179
|
step(179 - 61); // 179
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 179, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 179, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(5, Conductor.currentBeat);
|
Assert.areEqual(5, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(23, Conductor.currentStep);
|
Assert.areEqual(23, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(23.0 + 52 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(23.0 + 52 / 60, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 180 (3 seconds)
|
step(); // 180 (3 seconds)
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 180, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 180, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(6, Conductor.currentBeat);
|
Assert.areEqual(6, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(24, Conductor.currentStep);
|
Assert.areEqual(24, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(24.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(24.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 181 (3 + 1/60 seconds)
|
step(); // 181 (3 + 1/60 seconds)
|
||||||
// BPM has switched to 90!
|
// BPM has switched to 90!
|
||||||
|
@ -305,24 +305,24 @@ class ConductorTest extends FunkinTest
|
||||||
// 1.5 beat per second = 1.5/60 beats per frame = 3/120 beats per frame
|
// 1.5 beat per second = 1.5/60 beats per frame = 3/120 beats per frame
|
||||||
// = 12/120 steps per frame
|
// = 12/120 steps per frame
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 181, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 181, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(6, Conductor.currentBeat);
|
Assert.areEqual(6, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(24, Conductor.currentStep);
|
Assert.areEqual(24, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(24.0 + 12 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(24.0 + 12 / 120, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(59); // 240 (4 seconds)
|
step(59); // 240 (4 seconds)
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 240, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 240, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(7, Conductor.currentBeat);
|
Assert.areEqual(7, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(30, Conductor.currentStep);
|
Assert.areEqual(30, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(30.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(30.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 241 (4 + 1/60 seconds)
|
step(); // 241 (4 + 1/60 seconds)
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 241, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 241, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(7, Conductor.currentBeat);
|
Assert.areEqual(7, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(30, Conductor.currentStep);
|
Assert.areEqual(30, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(30.0 + 12 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(30.0 + 12 / 120, Conductor.instance.currentStepTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -334,63 +334,63 @@ class ConductorTest extends FunkinTest
|
||||||
new SongTimeChange(3000, 90),
|
new SongTimeChange(3000, 90),
|
||||||
new SongTimeChange(6000, 180)
|
new SongTimeChange(6000, 180)
|
||||||
];
|
];
|
||||||
Conductor.mapTimeChanges(songTimeChanges);
|
Conductor.instance.mapTimeChanges(songTimeChanges);
|
||||||
|
|
||||||
// Verify time changes.
|
// Verify time changes.
|
||||||
Assert.areEqual(3, Conductor.timeChanges.length);
|
Assert.areEqual(3, Conductor.instance.timeChanges.length);
|
||||||
FunkinAssert.areNear(0, Conductor.timeChanges[0].beatTime);
|
FunkinAssert.areNear(0, Conductor.instance.timeChanges[0].beatTime);
|
||||||
FunkinAssert.areNear(6, Conductor.timeChanges[1].beatTime);
|
FunkinAssert.areNear(6, Conductor.instance.timeChanges[1].beatTime);
|
||||||
FunkinAssert.areNear(10.5, Conductor.timeChanges[2].beatTime);
|
FunkinAssert.areNear(10.5, Conductor.instance.timeChanges[2].beatTime);
|
||||||
|
|
||||||
// All should be at 0.
|
// All should be at 0.
|
||||||
FunkinAssert.areNear(0, Conductor.songPosition);
|
FunkinAssert.areNear(0, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(0.0, Conductor.currentStepTime); // 2/120 of 1 beat = 8/120 of 1 step
|
FunkinAssert.areNear(0.0, Conductor.instance.currentStepTime); // 2/120 of 1 beat = 8/120 of 1 step
|
||||||
|
|
||||||
// 120 beats per minute = 2 beat per second
|
// 120 beats per minute = 2 beat per second
|
||||||
// 2 beat per second = 2/60 beats per frame = 16/120 steps per frame
|
// 2 beat per second = 2/60 beats per frame = 16/120 steps per frame
|
||||||
step(); // Advances time 1/60 of 1 second.
|
step(); // Advances time 1/60 of 1 second.
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 1, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(0, Conductor.currentBeat);
|
Assert.areEqual(0, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(0, Conductor.currentStep);
|
Assert.areEqual(0, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(16 / 120, Conductor.currentStepTime); // 4/120 of 1 beat = 16/120 of 1 step
|
FunkinAssert.areNear(16 / 120, Conductor.instance.currentStepTime); // 4/120 of 1 beat = 16/120 of 1 step
|
||||||
|
|
||||||
step(60 - 1 - 1); // 59
|
step(60 - 1 - 1); // 59
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 59, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 59, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(1, Conductor.currentBeat);
|
Assert.areEqual(1, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(7, Conductor.currentStep);
|
Assert.areEqual(7, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(7 + 104 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(7 + 104 / 120, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 60
|
step(); // 60
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 60, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 60, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(2, Conductor.currentBeat);
|
Assert.areEqual(2, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(8, Conductor.currentStep);
|
Assert.areEqual(8, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(8.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(8.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 61
|
step(); // 61
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 61, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 61, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(2, Conductor.currentBeat);
|
Assert.areEqual(2, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(8, Conductor.currentStep);
|
Assert.areEqual(8, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(8.0 + 8 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(8.0 + 8 / 60, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(179 - 61); // 179
|
step(179 - 61); // 179
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 179, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 179, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(5, Conductor.currentBeat);
|
Assert.areEqual(5, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(23, Conductor.currentStep);
|
Assert.areEqual(23, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(23.0 + 52 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(23.0 + 52 / 60, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 180 (3 seconds)
|
step(); // 180 (3 seconds)
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 180, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 180, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(6, Conductor.currentBeat);
|
Assert.areEqual(6, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(24, Conductor.currentStep); // 23.999 => 24
|
Assert.areEqual(24, Conductor.instance.currentStep); // 23.999 => 24
|
||||||
FunkinAssert.areNear(24.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(24.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 181 (3 + 1/60 seconds)
|
step(); // 181 (3 + 1/60 seconds)
|
||||||
// BPM has switched to 90!
|
// BPM has switched to 90!
|
||||||
|
@ -398,45 +398,45 @@ class ConductorTest extends FunkinTest
|
||||||
// 1.5 beat per second = 1.5/60 beats per frame = 3/120 beats per frame
|
// 1.5 beat per second = 1.5/60 beats per frame = 3/120 beats per frame
|
||||||
// = 12/120 steps per frame
|
// = 12/120 steps per frame
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 181, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 181, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(6, Conductor.currentBeat);
|
Assert.areEqual(6, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(24, Conductor.currentStep);
|
Assert.areEqual(24, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(24.0 + 12 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(24.0 + 12 / 120, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(60 - 1 - 1); // 240 (4 seconds)
|
step(60 - 1 - 1); // 240 (4 seconds)
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 239, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 239, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(7, Conductor.currentBeat);
|
Assert.areEqual(7, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(29, Conductor.currentStep);
|
Assert.areEqual(29, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(29.0 + 108 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(29.0 + 108 / 120, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 240 (4 seconds)
|
step(); // 240 (4 seconds)
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 240, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 240, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(7, Conductor.currentBeat);
|
Assert.areEqual(7, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(30, Conductor.currentStep);
|
Assert.areEqual(30, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(30.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(30.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 241 (4 + 1/60 seconds)
|
step(); // 241 (4 + 1/60 seconds)
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 241, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 241, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(7, Conductor.currentBeat);
|
Assert.areEqual(7, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(30, Conductor.currentStep);
|
Assert.areEqual(30, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(30.0 + 12 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(30.0 + 12 / 120, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(359 - 241); // 359 (5 + 59/60 seconds)
|
step(359 - 241); // 359 (5 + 59/60 seconds)
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 359, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 359, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(10, Conductor.currentBeat);
|
Assert.areEqual(10, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(41, Conductor.currentStep);
|
Assert.areEqual(41, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(41 + 108 / 120, Conductor.currentStepTime);
|
FunkinAssert.areNear(41 + 108 / 120, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 360
|
step(); // 360
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 360, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 360, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(10, Conductor.currentBeat);
|
Assert.areEqual(10, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(42, Conductor.currentStep); // 41.999
|
Assert.areEqual(42, Conductor.instance.currentStep); // 41.999
|
||||||
FunkinAssert.areNear(42.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(42.0, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 361
|
step(); // 361
|
||||||
// BPM has switched to 180!
|
// BPM has switched to 180!
|
||||||
|
@ -444,24 +444,24 @@ class ConductorTest extends FunkinTest
|
||||||
// 3 beat per second = 3/60 beats per frame
|
// 3 beat per second = 3/60 beats per frame
|
||||||
// = 12/60 steps per frame
|
// = 12/60 steps per frame
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 361, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 361, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(10, Conductor.currentBeat);
|
Assert.areEqual(10, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(42, Conductor.currentStep);
|
Assert.areEqual(42, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(42.0 + 12 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(42.0 + 12 / 60, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(); // 362
|
step(); // 362
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 362, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 362, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(10, Conductor.currentBeat);
|
Assert.areEqual(10, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(42, Conductor.currentStep);
|
Assert.areEqual(42, Conductor.instance.currentStep);
|
||||||
FunkinAssert.areNear(42.0 + 24 / 60, Conductor.currentStepTime);
|
FunkinAssert.areNear(42.0 + 24 / 60, Conductor.instance.currentStepTime);
|
||||||
|
|
||||||
step(3); // 365
|
step(3); // 365
|
||||||
|
|
||||||
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 365, Conductor.songPosition);
|
FunkinAssert.areNear(FunkinTest.MS_PER_STEP * 365, Conductor.instance.songPosition);
|
||||||
Assert.areEqual(10, Conductor.currentBeat);
|
Assert.areEqual(10, Conductor.instance.currentBeat);
|
||||||
Assert.areEqual(43, Conductor.currentStep); // 42.999 => 42
|
Assert.areEqual(43, Conductor.instance.currentStep); // 42.999 => 42
|
||||||
FunkinAssert.areNear(43.0, Conductor.currentStepTime);
|
FunkinAssert.areNear(43.0, Conductor.instance.currentStepTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,6 +504,6 @@ class ConductorState extends FlxState
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
// On each step, increment the Conductor as though the song was playing.
|
// On each step, increment the Conductor as though the song was playing.
|
||||||
Conductor.update(Conductor.songPosition + elapsed * Constants.MS_PER_SEC);
|
Conductor.instance.update(Conductor.instance.songPosition + elapsed * Constants.MS_PER_SEC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue