mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-23 16:17:53 -05:00
Merge pull request #360 from FunkinCrew/feature/blazin-animation-work
Week of Feb 26: Blazin' Animation Work
This commit is contained in:
commit
f661e36fbb
31 changed files with 625 additions and 315 deletions
|
@ -1,8 +1,10 @@
|
||||||
# Ignore artifacts
|
# Ignore artifacts
|
||||||
export
|
export
|
||||||
|
|
||||||
# Ignore all asset files (including FlxAnimate JSONs)
|
# Ignore all JSONS in the images folder (including FlxAnimate JSONs)
|
||||||
assets
|
assets/preload/images
|
||||||
|
assets/shared/images
|
||||||
|
|
||||||
# Don't ignore data files
|
# Don't ignore data files
|
||||||
!assets/preload/data
|
# TODO: These don't work.
|
||||||
|
!assets/preload/data/
|
||||||
|
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -71,6 +71,7 @@
|
||||||
|
|
||||||
"haxe.displayPort": "auto",
|
"haxe.displayPort": "auto",
|
||||||
"haxe.enableCompilationServer": false,
|
"haxe.enableCompilationServer": false,
|
||||||
|
"haxe.enableServerView": true,
|
||||||
"haxe.displayServer": {
|
"haxe.displayServer": {
|
||||||
"arguments": ["-v"]
|
"arguments": ["-v"]
|
||||||
},
|
},
|
||||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 9a55f8b24bd41a8b8f03302b4f155e18b97774ff
|
Subproject commit d0fe6c22328b2bd82bd3f18fe32f09bc4b0ce57a
|
|
@ -84,9 +84,9 @@ class PlayerSettings
|
||||||
function addKeyboard():Void
|
function addKeyboard():Void
|
||||||
{
|
{
|
||||||
var useDefault = true;
|
var useDefault = true;
|
||||||
if (Save.get().hasControls(id, Keys))
|
if (Save.instance.hasControls(id, Keys))
|
||||||
{
|
{
|
||||||
var keyControlData = Save.get().getControls(id, Keys);
|
var keyControlData = Save.instance.getControls(id, Keys);
|
||||||
trace("keyControlData: " + haxe.Json.stringify(keyControlData));
|
trace("keyControlData: " + haxe.Json.stringify(keyControlData));
|
||||||
useDefault = false;
|
useDefault = false;
|
||||||
controls.fromSaveData(keyControlData, Keys);
|
controls.fromSaveData(keyControlData, Keys);
|
||||||
|
@ -112,9 +112,9 @@ class PlayerSettings
|
||||||
function addGamepad(gamepad:FlxGamepad)
|
function addGamepad(gamepad:FlxGamepad)
|
||||||
{
|
{
|
||||||
var useDefault = true;
|
var useDefault = true;
|
||||||
if (Save.get().hasControls(id, Gamepad(gamepad.id)))
|
if (Save.instance.hasControls(id, Gamepad(gamepad.id)))
|
||||||
{
|
{
|
||||||
var padControlData = Save.get().getControls(id, Gamepad(gamepad.id));
|
var padControlData = Save.instance.getControls(id, Gamepad(gamepad.id));
|
||||||
trace("padControlData: " + haxe.Json.stringify(padControlData));
|
trace("padControlData: " + haxe.Json.stringify(padControlData));
|
||||||
useDefault = false;
|
useDefault = false;
|
||||||
controls.addGamepadWithSaveData(gamepad.id, padControlData);
|
controls.addGamepadWithSaveData(gamepad.id, padControlData);
|
||||||
|
@ -141,7 +141,7 @@ class PlayerSettings
|
||||||
if (keyData != null)
|
if (keyData != null)
|
||||||
{
|
{
|
||||||
trace("saving key data: " + haxe.Json.stringify(keyData));
|
trace("saving key data: " + haxe.Json.stringify(keyData));
|
||||||
Save.get().setControls(id, Keys, keyData);
|
Save.instance.setControls(id, Keys, keyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controls.gamepadsAdded.length > 0)
|
if (controls.gamepadsAdded.length > 0)
|
||||||
|
@ -150,7 +150,7 @@ class PlayerSettings
|
||||||
if (padData != null)
|
if (padData != null)
|
||||||
{
|
{
|
||||||
trace("saving pad data: " + haxe.Json.stringify(padData));
|
trace("saving pad data: " + haxe.Json.stringify(padData));
|
||||||
Save.get().setControls(id, Gamepad(controls.gamepadsAdded[0]), padData);
|
Save.instance.setControls(id, Gamepad(controls.gamepadsAdded[0]), padData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,12 @@ class Preferences
|
||||||
|
|
||||||
static function get_naughtyness():Bool
|
static function get_naughtyness():Bool
|
||||||
{
|
{
|
||||||
return Save.get().options.naughtyness;
|
return Save.instance.options.naughtyness;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function set_naughtyness(value:Bool):Bool
|
static function set_naughtyness(value:Bool):Bool
|
||||||
{
|
{
|
||||||
var save = Save.get();
|
var save = Save.instance;
|
||||||
save.options.naughtyness = value;
|
save.options.naughtyness = value;
|
||||||
save.flush();
|
save.flush();
|
||||||
return value;
|
return value;
|
||||||
|
@ -34,12 +34,12 @@ class Preferences
|
||||||
|
|
||||||
static function get_downscroll():Bool
|
static function get_downscroll():Bool
|
||||||
{
|
{
|
||||||
return Save.get().options.downscroll;
|
return Save.instance.options.downscroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function set_downscroll(value:Bool):Bool
|
static function set_downscroll(value:Bool):Bool
|
||||||
{
|
{
|
||||||
var save = Save.get();
|
var save = Save.instance;
|
||||||
save.options.downscroll = value;
|
save.options.downscroll = value;
|
||||||
save.flush();
|
save.flush();
|
||||||
return value;
|
return value;
|
||||||
|
@ -53,12 +53,12 @@ class Preferences
|
||||||
|
|
||||||
static function get_flashingLights():Bool
|
static function get_flashingLights():Bool
|
||||||
{
|
{
|
||||||
return Save.get().options.flashingLights;
|
return Save.instance.options.flashingLights;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function set_flashingLights(value:Bool):Bool
|
static function set_flashingLights(value:Bool):Bool
|
||||||
{
|
{
|
||||||
var save = Save.get();
|
var save = Save.instance;
|
||||||
save.options.flashingLights = value;
|
save.options.flashingLights = value;
|
||||||
save.flush();
|
save.flush();
|
||||||
return value;
|
return value;
|
||||||
|
@ -72,12 +72,12 @@ class Preferences
|
||||||
|
|
||||||
static function get_zoomCamera():Bool
|
static function get_zoomCamera():Bool
|
||||||
{
|
{
|
||||||
return Save.get().options.zoomCamera;
|
return Save.instance.options.zoomCamera;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function set_zoomCamera(value:Bool):Bool
|
static function set_zoomCamera(value:Bool):Bool
|
||||||
{
|
{
|
||||||
var save = Save.get();
|
var save = Save.instance;
|
||||||
save.options.zoomCamera = value;
|
save.options.zoomCamera = value;
|
||||||
save.flush();
|
save.flush();
|
||||||
return value;
|
return value;
|
||||||
|
@ -91,17 +91,17 @@ class Preferences
|
||||||
|
|
||||||
static function get_debugDisplay():Bool
|
static function get_debugDisplay():Bool
|
||||||
{
|
{
|
||||||
return Save.get().options.debugDisplay;
|
return Save.instance.options.debugDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function set_debugDisplay(value:Bool):Bool
|
static function set_debugDisplay(value:Bool):Bool
|
||||||
{
|
{
|
||||||
if (value != Save.get().options.debugDisplay)
|
if (value != Save.instance.options.debugDisplay)
|
||||||
{
|
{
|
||||||
toggleDebugDisplay(value);
|
toggleDebugDisplay(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
var save = Save.get();
|
var save = Save.instance;
|
||||||
save.options.debugDisplay = value;
|
save.options.debugDisplay = value;
|
||||||
save.flush();
|
save.flush();
|
||||||
return value;
|
return value;
|
||||||
|
@ -115,14 +115,14 @@ class Preferences
|
||||||
|
|
||||||
static function get_autoPause():Bool
|
static function get_autoPause():Bool
|
||||||
{
|
{
|
||||||
return Save.get().options.autoPause;
|
return Save.instance.options.autoPause;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function set_autoPause(value:Bool):Bool
|
static function set_autoPause(value:Bool):Bool
|
||||||
{
|
{
|
||||||
if (value != Save.get().options.autoPause) FlxG.autoPause = value;
|
if (value != Save.instance.options.autoPause) FlxG.autoPause = value;
|
||||||
|
|
||||||
var save = Save.get();
|
var save = Save.instance;
|
||||||
save.options.autoPause = value;
|
save.options.autoPause = value;
|
||||||
save.flush();
|
save.flush();
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -86,10 +86,10 @@ class NGUtil
|
||||||
#end
|
#end
|
||||||
|
|
||||||
var onSessionFail:Error->Void = null;
|
var onSessionFail:Error->Void = null;
|
||||||
if (sessionId == null && Save.get().ngSessionId != null)
|
if (sessionId == null && Save.instance.ngSessionId != null)
|
||||||
{
|
{
|
||||||
trace("using stored session id");
|
trace("using stored session id");
|
||||||
sessionId = Save.get().ngSessionId;
|
sessionId = Save.instance.ngSessionId;
|
||||||
onSessionFail = function(error) savedSessionFailed = true;
|
onSessionFail = function(error) savedSessionFailed = true;
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
@ -159,8 +159,8 @@ class NGUtil
|
||||||
static function onNGLogin():Void
|
static function onNGLogin():Void
|
||||||
{
|
{
|
||||||
trace('logged in! user:${NG.core.user.name}');
|
trace('logged in! user:${NG.core.user.name}');
|
||||||
Save.get().ngSessionId = NG.core.sessionId;
|
Save.instance.ngSessionId = NG.core.sessionId;
|
||||||
Save.get().flush();
|
Save.instance.flush();
|
||||||
// Load medals then call onNGMedalFetch()
|
// Load medals then call onNGMedalFetch()
|
||||||
NG.core.requestMedals(onNGMedalFetch);
|
NG.core.requestMedals(onNGMedalFetch);
|
||||||
|
|
||||||
|
@ -174,8 +174,8 @@ class NGUtil
|
||||||
{
|
{
|
||||||
NG.core.logOut();
|
NG.core.logOut();
|
||||||
|
|
||||||
Save.get().ngSessionId = null;
|
Save.instance.ngSessionId = null;
|
||||||
Save.get().flush();
|
Save.instance.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- MEDALS
|
// --- MEDALS
|
||||||
|
|
|
@ -86,10 +86,10 @@ class NGio
|
||||||
#end
|
#end
|
||||||
|
|
||||||
var onSessionFail:Error->Void = null;
|
var onSessionFail:Error->Void = null;
|
||||||
if (sessionId == null && Save.get().ngSessionId != null)
|
if (sessionId == null && Save.instance.ngSessionId != null)
|
||||||
{
|
{
|
||||||
trace("using stored session id");
|
trace("using stored session id");
|
||||||
sessionId = Save.get().ngSessionId;
|
sessionId = Save.instance.ngSessionId;
|
||||||
onSessionFail = function(error) savedSessionFailed = true;
|
onSessionFail = function(error) savedSessionFailed = true;
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
@ -159,8 +159,8 @@ class NGio
|
||||||
static function onNGLogin():Void
|
static function onNGLogin():Void
|
||||||
{
|
{
|
||||||
trace('logged in! user:${NG.core.user.name}');
|
trace('logged in! user:${NG.core.user.name}');
|
||||||
Save.get().ngSessionId = NG.core.sessionId;
|
Save.instance.ngSessionId = NG.core.sessionId;
|
||||||
Save.get().flush();
|
Save.instance.flush();
|
||||||
// Load medals then call onNGMedalFetch()
|
// Load medals then call onNGMedalFetch()
|
||||||
NG.core.requestMedals(onNGMedalFetch);
|
NG.core.requestMedals(onNGMedalFetch);
|
||||||
|
|
||||||
|
@ -174,8 +174,8 @@ class NGio
|
||||||
{
|
{
|
||||||
NG.core.logOut();
|
NG.core.logOut();
|
||||||
|
|
||||||
Save.get().ngSessionId = null;
|
Save.instance.ngSessionId = null;
|
||||||
Save.get().flush();
|
Save.instance.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- MEDALS
|
// --- MEDALS
|
||||||
|
|
|
@ -240,6 +240,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
||||||
*/
|
*/
|
||||||
function createEntry(id:String):Null<T>
|
function createEntry(id:String):Null<T>
|
||||||
{
|
{
|
||||||
|
// We enforce that T is Constructible to ensure this is valid.
|
||||||
return new T(id);
|
return new T(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,18 +32,21 @@ class StageData
|
||||||
bf:
|
bf:
|
||||||
{
|
{
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
|
scale: 1,
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
cameraOffsets: [-100, -100]
|
cameraOffsets: [-100, -100]
|
||||||
},
|
},
|
||||||
dad:
|
dad:
|
||||||
{
|
{
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
|
scale: 1,
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
cameraOffsets: [100, -100]
|
cameraOffsets: [100, -100]
|
||||||
},
|
},
|
||||||
gf:
|
gf:
|
||||||
{
|
{
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
|
scale: 1,
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
cameraOffsets: [0, 0]
|
cameraOffsets: [0, 0]
|
||||||
}
|
}
|
||||||
|
@ -114,6 +117,7 @@ typedef StageDataProp =
|
||||||
@:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
|
@:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
|
||||||
@:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
|
@:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
|
||||||
@:optional
|
@:optional
|
||||||
|
@:default(Left(1.0))
|
||||||
var scale:haxe.ds.Either<Float, Array<Float>>;
|
var scale:haxe.ds.Either<Float, Array<Float>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -190,6 +194,13 @@ typedef StageDataCharacter =
|
||||||
@:default([0, 0])
|
@:default([0, 0])
|
||||||
var position:Array<Float>;
|
var position:Array<Float>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scale to render the character at.
|
||||||
|
*/
|
||||||
|
@:optional
|
||||||
|
@:default(1)
|
||||||
|
var scale:Float;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The camera offsets to apply when focusing on the character on this stage.
|
* The camera offsets to apply when focusing on the character on this stage.
|
||||||
* @default [-100, -100] for BF, [100, -100] for DAD/OPPONENT, [0, 0] for GF
|
* @default [-100, -100] for BF, [100, -100] for DAD/OPPONENT, [0, 0] for GF
|
||||||
|
|
|
@ -82,6 +82,8 @@ class FlxAtlasSprite extends FlxAnimate
|
||||||
* @param id A string ID of the animation to play.
|
* @param id A string ID of the animation to play.
|
||||||
* @param restart Whether to restart the animation if it is already playing.
|
* @param restart Whether to restart the animation if it is already playing.
|
||||||
* @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing
|
* @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing
|
||||||
|
* @param loop Whether to loop the animation
|
||||||
|
* NOTE: `loop` and `ignoreOther` are not compatible with each other!
|
||||||
*/
|
*/
|
||||||
public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, ?loop:Bool = false):Void
|
public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, ?loop:Bool = false):Void
|
||||||
{
|
{
|
||||||
|
@ -114,12 +116,19 @@ class FlxAtlasSprite extends FlxAnimate
|
||||||
}
|
}
|
||||||
|
|
||||||
anim.callback = function(_, frame:Int) {
|
anim.callback = function(_, frame:Int) {
|
||||||
if (frame == (anim.getFrameLabel(id).duration - 1) + anim.getFrameLabel(id).index)
|
var offset = loop ? 0 : -1;
|
||||||
|
|
||||||
|
if (frame == (anim.getFrameLabel(id).duration + offset) + anim.getFrameLabel(id).index)
|
||||||
{
|
{
|
||||||
if (loop) playAnimation(id, true, false, true);
|
if (loop)
|
||||||
|
{
|
||||||
|
playAnimation(id, true, false, true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
onAnimationFinish.dispatch(id);
|
onAnimationFinish.dispatch(id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prevent other animations from playing if `ignoreOther` is true.
|
// Prevent other animations from playing if `ignoreOther` is true.
|
||||||
|
@ -176,7 +185,7 @@ class FlxAtlasSprite extends FlxAnimate
|
||||||
public function cleanupAnimation(_:String):Void
|
public function cleanupAnimation(_:String):Void
|
||||||
{
|
{
|
||||||
canPlayOtherAnims = true;
|
canPlayOtherAnims = true;
|
||||||
this.currentAnimation = null;
|
// this.currentAnimation = null;
|
||||||
this.anim.stop();
|
this.anim.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ class PolymodHandler
|
||||||
createModRoot();
|
createModRoot();
|
||||||
|
|
||||||
trace("Initializing Polymod (using configured mods)...");
|
trace("Initializing Polymod (using configured mods)...");
|
||||||
loadModsById(Save.get().enabledModIds);
|
loadModsById(Save.instance.enabledModIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -236,7 +236,7 @@ class PolymodHandler
|
||||||
|
|
||||||
public static function getEnabledMods():Array<ModMetadata>
|
public static function getEnabledMods():Array<ModMetadata>
|
||||||
{
|
{
|
||||||
var modIds = Save.get().enabledModIds;
|
var modIds = Save.instance.enabledModIds;
|
||||||
var modMetadata = getAllMods();
|
var modMetadata = getAllMods();
|
||||||
var enabledMods = [];
|
var enabledMods = [];
|
||||||
for (item in modMetadata)
|
for (item in modMetadata)
|
||||||
|
|
|
@ -107,18 +107,18 @@ class NoteScriptEvent extends ScriptEvent
|
||||||
public var playSound(default, default):Bool;
|
public var playSound(default, default):Bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A multiplier to the health gained or lost from this note.
|
* The health gained or lost from this note.
|
||||||
* This affects both hits and misses. Remember that max health is 2.00.
|
* This affects both hits and misses. Remember that max health is 2.00.
|
||||||
*/
|
*/
|
||||||
public var healthMulti:Float;
|
public var healthChange:Float;
|
||||||
|
|
||||||
public function new(type:ScriptEventType, note:NoteSprite, comboCount:Int = 0, cancelable:Bool = false):Void
|
public function new(type:ScriptEventType, note:NoteSprite, healthChange:Float, comboCount:Int = 0, cancelable:Bool = false):Void
|
||||||
{
|
{
|
||||||
super(type, cancelable);
|
super(type, cancelable);
|
||||||
this.note = note;
|
this.note = note;
|
||||||
this.comboCount = comboCount;
|
this.comboCount = comboCount;
|
||||||
this.playSound = true;
|
this.playSound = true;
|
||||||
this.healthMulti = 1.0;
|
this.healthChange = healthChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function toString():String
|
public override function toString():String
|
||||||
|
@ -127,6 +127,31 @@ class NoteScriptEvent extends ScriptEvent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HitNoteScriptEvent extends NoteScriptEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The judgement the player received for hitting the note.
|
||||||
|
*/
|
||||||
|
public var judgement:String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The score the player received for hitting the note.
|
||||||
|
*/
|
||||||
|
public var score:Int;
|
||||||
|
|
||||||
|
public function new(note:NoteSprite, healthChange:Float, score:Int, judgement:String, comboCount:Int = 0):Void
|
||||||
|
{
|
||||||
|
super(NOTE_HIT, note, healthChange, comboCount, true);
|
||||||
|
this.score = score;
|
||||||
|
this.judgement = judgement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function toString():String
|
||||||
|
{
|
||||||
|
return 'HitNoteScriptEvent(note=' + note + ', comboCount=' + comboCount + ', judgement=' + judgement + ', score=' + score + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event that is fired when you press a key with no note present.
|
* An event that is fired when you press a key with no note present.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,6 +12,7 @@ import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.play.character.BaseCharacter;
|
import funkin.play.character.BaseCharacter;
|
||||||
import funkin.play.PlayState;
|
import funkin.play.PlayState;
|
||||||
|
import funkin.util.MathUtil;
|
||||||
import funkin.ui.freeplay.FreeplayState;
|
import funkin.ui.freeplay.FreeplayState;
|
||||||
import funkin.ui.MusicBeatSubState;
|
import funkin.ui.MusicBeatSubState;
|
||||||
import funkin.ui.story.StoryMenuState;
|
import funkin.ui.story.StoryMenuState;
|
||||||
|
@ -82,6 +83,9 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
|
|
||||||
var transparent:Bool;
|
var transparent:Bool;
|
||||||
|
|
||||||
|
final CAMERA_ZOOM_DURATION:Float = 0.5;
|
||||||
|
var targetCameraZoom:Float = 1.0;
|
||||||
|
|
||||||
public function new(params:GameOverParams)
|
public function new(params:GameOverParams)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
@ -142,6 +146,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
|
|
||||||
FlxG.camera.target = null;
|
FlxG.camera.target = null;
|
||||||
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.01);
|
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.01);
|
||||||
|
targetCameraZoom = PlayState?.instance?.currentStage?.camZoom * boyfriend.getDeathCameraZoom();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Set up the audio
|
// Set up the audio
|
||||||
|
@ -177,6 +182,9 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Smoothly lerp the camera
|
||||||
|
FlxG.camera.zoom = MathUtil.smoothLerp(FlxG.camera.zoom, targetCameraZoom, elapsed, CAMERA_ZOOM_DURATION);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle user inputs.
|
// Handle user inputs.
|
||||||
//
|
//
|
||||||
|
@ -286,6 +294,9 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
remove(boyfriend);
|
remove(boyfriend);
|
||||||
PlayState.instance.currentStage.addCharacter(boyfriend, BF);
|
PlayState.instance.currentStage.addCharacter(boyfriend, BF);
|
||||||
|
|
||||||
|
// Snap reset the camera which may have changed because of the player character data.
|
||||||
|
resetCameraZoom();
|
||||||
|
|
||||||
// Close the substate.
|
// Close the substate.
|
||||||
close();
|
close();
|
||||||
});
|
});
|
||||||
|
|
|
@ -386,7 +386,6 @@ class PauseSubState extends MusicBeatSubState
|
||||||
// Set the position.
|
// Set the position.
|
||||||
var targetX = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 20 + 90;
|
var targetX = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 20 + 90;
|
||||||
var targetY = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 120 + (FlxG.height * 0.48);
|
var targetY = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 120 + (FlxG.height * 0.48);
|
||||||
trace(targetY);
|
|
||||||
FlxTween.globalManager.cancelTweensOf(text);
|
FlxTween.globalManager.cancelTweensOf(text);
|
||||||
FlxTween.tween(text, {x: targetX, y: targetY}, 0.33, {ease: FlxEase.quartOut});
|
FlxTween.tween(text, {x: targetX, y: targetY}, 0.33, {ease: FlxEase.quartOut});
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ typedef PlayStateParams =
|
||||||
?targetDifficulty:String,
|
?targetDifficulty:String,
|
||||||
/**
|
/**
|
||||||
* The variation to play on.
|
* The variation to play on.
|
||||||
* @default `Constants.DEFAULT_VARIATION` .
|
* @default `Constants.DEFAULT_VARIATION`
|
||||||
*/
|
*/
|
||||||
?targetVariation:String,
|
?targetVariation:String,
|
||||||
/**
|
/**
|
||||||
|
@ -118,8 +118,14 @@ typedef PlayStateParams =
|
||||||
?minimalMode:Bool,
|
?minimalMode:Bool,
|
||||||
/**
|
/**
|
||||||
* If specified, the game will jump to the specified timestamp after the countdown ends.
|
* If specified, the game will jump to the specified timestamp after the countdown ends.
|
||||||
|
* @default `0.0`
|
||||||
*/
|
*/
|
||||||
?startTimestamp:Float,
|
?startTimestamp:Float,
|
||||||
|
/**
|
||||||
|
* If specified, the game will play the song with the given speed.
|
||||||
|
* @default `1.0` for 100% speed.
|
||||||
|
*/
|
||||||
|
?playbackRate:Float,
|
||||||
/**
|
/**
|
||||||
* If specified, the game will not load the instrumental or vocal tracks,
|
* If specified, the game will not load the instrumental or vocal tracks,
|
||||||
* and must be loaded externally.
|
* and must be loaded externally.
|
||||||
|
@ -210,6 +216,12 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
public var startTimestamp:Float = 0.0;
|
public var startTimestamp:Float = 0.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play back the song at this speed.
|
||||||
|
* @default `1.0` for normal speed.
|
||||||
|
*/
|
||||||
|
public var playbackRate:Float = 1.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An empty FlxObject contained in the scene.
|
* An empty FlxObject contained in the scene.
|
||||||
* The current gameplay camera will always follow this object. Tween its position to move the camera smoothly.
|
* The current gameplay camera will always follow this object. Tween its position to move the camera smoothly.
|
||||||
|
@ -270,6 +282,12 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
public var isPracticeMode:Bool = false;
|
public var isPracticeMode:Bool = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the player has dropped below zero health,
|
||||||
|
* and we are just waiting for an animation to play out before transitioning.
|
||||||
|
*/
|
||||||
|
public var isPlayerDying:Bool = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In Minimal Mode, the stage and characters are not loaded and a standard background is used.
|
* In Minimal Mode, the stage and characters are not loaded and a standard background is used.
|
||||||
*/
|
*/
|
||||||
|
@ -550,6 +568,7 @@ class PlayState extends MusicBeatSubState
|
||||||
isPracticeMode = params.practiceMode ?? false;
|
isPracticeMode = params.practiceMode ?? false;
|
||||||
isMinimalMode = params.minimalMode ?? false;
|
isMinimalMode = params.minimalMode ?? false;
|
||||||
startTimestamp = params.startTimestamp ?? 0.0;
|
startTimestamp = params.startTimestamp ?? 0.0;
|
||||||
|
playbackRate = params.playbackRate ?? 1.0;
|
||||||
overrideMusic = params.overrideMusic ?? false;
|
overrideMusic = params.overrideMusic ?? false;
|
||||||
previousCameraFollowPoint = params.cameraFollowPoint;
|
previousCameraFollowPoint = params.cameraFollowPoint;
|
||||||
|
|
||||||
|
@ -772,11 +791,13 @@ class PlayState extends MusicBeatSubState
|
||||||
persistentDraw = true;
|
persistentDraw = true;
|
||||||
|
|
||||||
startingSong = true;
|
startingSong = true;
|
||||||
|
isPlayerDying = false;
|
||||||
|
|
||||||
inputSpitter = [];
|
inputSpitter = [];
|
||||||
|
|
||||||
// Reset music properly.
|
// Reset music properly.
|
||||||
FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
|
FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
|
||||||
|
FlxG.sound.music.pitch = playbackRate;
|
||||||
FlxG.sound.music.pause();
|
FlxG.sound.music.pause();
|
||||||
|
|
||||||
if (!overrideMusic)
|
if (!overrideMusic)
|
||||||
|
@ -913,7 +934,7 @@ class PlayState extends MusicBeatSubState
|
||||||
camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95);
|
camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentStage != null)
|
if (currentStage != null && currentStage.getBoyfriend() != null)
|
||||||
{
|
{
|
||||||
FlxG.watch.addQuick('bfAnim', currentStage.getBoyfriend().getCurrentAnimation());
|
FlxG.watch.addQuick('bfAnim', currentStage.getBoyfriend().getCurrentAnimation());
|
||||||
}
|
}
|
||||||
|
@ -939,7 +960,7 @@ class PlayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
if (health <= Constants.HEALTH_MIN && !isPracticeMode)
|
if (health <= Constants.HEALTH_MIN && !isPracticeMode && !isPlayerDying)
|
||||||
{
|
{
|
||||||
vocals.pause();
|
vocals.pause();
|
||||||
FlxG.sound.music.pause();
|
FlxG.sound.music.pause();
|
||||||
|
@ -965,20 +986,30 @@ class PlayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
var gameOverSubState = new GameOverSubState(
|
isPlayerDying = true;
|
||||||
|
|
||||||
|
var deathPreTransitionDelay = currentStage?.getBoyfriend()?.getDeathPreTransitionDelay() ?? 0.0;
|
||||||
|
if (deathPreTransitionDelay > 0)
|
||||||
{
|
{
|
||||||
isChartingMode: isChartingMode,
|
new FlxTimer().start(deathPreTransitionDelay, function(_) {
|
||||||
transparent: persistentDraw
|
moveToGameOver();
|
||||||
});
|
});
|
||||||
FlxTransitionableSubState.skipNextTransIn = true;
|
}
|
||||||
FlxTransitionableSubState.skipNextTransOut = true;
|
else
|
||||||
openSubState(gameOverSubState);
|
{
|
||||||
|
// Transition immediately.
|
||||||
|
moveToGameOver();
|
||||||
|
}
|
||||||
|
|
||||||
#if discord_rpc
|
#if discord_rpc
|
||||||
// Game Over doesn't get his own variable because it's only used here
|
// Game Over doesn't get his own variable because it's only used here
|
||||||
DiscordClient.changePresence('Game Over - ' + detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC);
|
DiscordClient.changePresence('Game Over - ' + detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC);
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
else if (isPlayerDying)
|
||||||
|
{
|
||||||
|
// Wait up.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processSongEvents();
|
processSongEvents();
|
||||||
|
@ -994,6 +1025,18 @@ class PlayState extends MusicBeatSubState
|
||||||
justUnpaused = false;
|
justUnpaused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moveToGameOver():Void
|
||||||
|
{
|
||||||
|
var gameOverSubState = new GameOverSubState(
|
||||||
|
{
|
||||||
|
isChartingMode: isChartingMode,
|
||||||
|
transparent: persistentDraw
|
||||||
|
});
|
||||||
|
FlxTransitionableSubState.skipNextTransIn = true;
|
||||||
|
FlxTransitionableSubState.skipNextTransOut = true;
|
||||||
|
openSubState(gameOverSubState);
|
||||||
|
}
|
||||||
|
|
||||||
function processSongEvents():Void
|
function processSongEvents():Void
|
||||||
{
|
{
|
||||||
// Query and activate song events.
|
// Query and activate song events.
|
||||||
|
@ -1484,7 +1527,6 @@ class PlayState extends MusicBeatSubState
|
||||||
if (dad != null)
|
if (dad != null)
|
||||||
{
|
{
|
||||||
dad.characterType = CharacterType.DAD;
|
dad.characterType = CharacterType.DAD;
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// OPPONENT HEALTH ICON
|
// OPPONENT HEALTH ICON
|
||||||
|
@ -1495,6 +1537,7 @@ class PlayState extends MusicBeatSubState
|
||||||
iconP2.zIndex = 850;
|
iconP2.zIndex = 850;
|
||||||
add(iconP2);
|
add(iconP2);
|
||||||
iconP2.cameras = [camHUD];
|
iconP2.cameras = [camHUD];
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// BOYFRIEND
|
// BOYFRIEND
|
||||||
|
@ -1504,7 +1547,6 @@ class PlayState extends MusicBeatSubState
|
||||||
if (boyfriend != null)
|
if (boyfriend != null)
|
||||||
{
|
{
|
||||||
boyfriend.characterType = CharacterType.BF;
|
boyfriend.characterType = CharacterType.BF;
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// PLAYER HEALTH ICON
|
// PLAYER HEALTH ICON
|
||||||
|
@ -1515,6 +1557,7 @@ class PlayState extends MusicBeatSubState
|
||||||
iconP1.zIndex = 850;
|
iconP1.zIndex = 850;
|
||||||
add(iconP1);
|
add(iconP1);
|
||||||
iconP1.cameras = [camHUD];
|
iconP1.cameras = [camHUD];
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// ADD CHARACTERS TO SCENE
|
// ADD CHARACTERS TO SCENE
|
||||||
|
@ -1783,14 +1826,17 @@ class PlayState extends MusicBeatSubState
|
||||||
// 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.play(true, startTimestamp - Conductor.instance.instrumentalOffset);
|
FlxG.sound.music.play(true, startTimestamp - Conductor.instance.instrumentalOffset);
|
||||||
|
FlxG.sound.music.pitch = playbackRate;
|
||||||
|
|
||||||
// I am going insane.
|
// I am going insane.
|
||||||
FlxG.sound.music.volume = 1.0;
|
FlxG.sound.music.volume = 1.0;
|
||||||
|
|
||||||
FlxG.sound.music.fadeTween?.cancel();
|
FlxG.sound.music.fadeTween?.cancel();
|
||||||
|
|
||||||
trace('Playing vocals...');
|
trace('Playing vocals...');
|
||||||
add(vocals);
|
add(vocals);
|
||||||
vocals.play();
|
vocals.play();
|
||||||
|
vocals.pitch = playbackRate;
|
||||||
resyncVocals();
|
resyncVocals();
|
||||||
|
|
||||||
#if discord_rpc
|
#if discord_rpc
|
||||||
|
@ -2000,7 +2046,7 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
// Call an event to allow canceling the note miss.
|
// Call an event to allow canceling the note miss.
|
||||||
// NOTE: This is what handles the character animations!
|
// NOTE: This is what handles the character animations!
|
||||||
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, 0, true);
|
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, -Constants.HEALTH_MISS_PENALTY, 0, true);
|
||||||
dispatchEvent(event);
|
dispatchEvent(event);
|
||||||
|
|
||||||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||||
|
@ -2009,7 +2055,7 @@ class PlayState extends MusicBeatSubState
|
||||||
// Judge the miss.
|
// Judge the miss.
|
||||||
// NOTE: This is what handles the scoring.
|
// NOTE: This is what handles the scoring.
|
||||||
trace('Missed note! ${note.noteData}');
|
trace('Missed note! ${note.noteData}');
|
||||||
onNoteMiss(note, event.playSound, event.healthMulti);
|
onNoteMiss(note, event.playSound, event.healthChange);
|
||||||
|
|
||||||
note.handledMiss = true;
|
note.handledMiss = true;
|
||||||
}
|
}
|
||||||
|
@ -2155,13 +2201,41 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
function goodNoteHit(note:NoteSprite, input:PreciseInputEvent):Void
|
function goodNoteHit(note:NoteSprite, input:PreciseInputEvent):Void
|
||||||
{
|
{
|
||||||
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_HIT, note, Highscore.tallies.combo + 1, true);
|
// Calculate the input latency (do this as late as possible).
|
||||||
|
// trace('Compare: ${PreciseInputManager.getCurrentTimestamp()} - ${input.timestamp}');
|
||||||
|
var inputLatencyNs:Int64 = PreciseInputManager.getCurrentTimestamp() - input.timestamp;
|
||||||
|
var inputLatencyMs:Float = inputLatencyNs.toFloat() / Constants.NS_PER_MS;
|
||||||
|
// trace('Input: ${daNote.noteData.getDirectionName()} pressed ${inputLatencyMs}ms ago!');
|
||||||
|
|
||||||
|
// Get the offset and compensate for input latency.
|
||||||
|
// Round inward (trim remainder) for consistency.
|
||||||
|
var noteDiff:Int = Std.int(Conductor.instance.songPosition - note.noteData.time - inputLatencyMs);
|
||||||
|
|
||||||
|
var score = Scoring.scoreNote(noteDiff, PBOT1);
|
||||||
|
var daRating = Scoring.judgeNote(noteDiff, PBOT1);
|
||||||
|
|
||||||
|
var healthChange = 0.0;
|
||||||
|
switch (daRating)
|
||||||
|
{
|
||||||
|
case 'sick':
|
||||||
|
healthChange = Constants.HEALTH_SICK_BONUS;
|
||||||
|
case 'good':
|
||||||
|
healthChange = Constants.HEALTH_GOOD_BONUS;
|
||||||
|
case 'bad':
|
||||||
|
healthChange = Constants.HEALTH_BAD_BONUS;
|
||||||
|
case 'shit':
|
||||||
|
healthChange = Constants.HEALTH_SHIT_BONUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the note hit event.
|
||||||
|
var event:HitNoteScriptEvent = new HitNoteScriptEvent(note, healthChange, score, daRating, Highscore.tallies.combo + 1);
|
||||||
dispatchEvent(event);
|
dispatchEvent(event);
|
||||||
|
|
||||||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||||
if (event.eventCanceled) return;
|
if (event.eventCanceled) return;
|
||||||
|
|
||||||
popUpScore(note, input, event.healthMulti);
|
// Display the combo meter and add the calculation to the score.
|
||||||
|
popUpScore(note, event.score, event.judgement, event.healthChange);
|
||||||
|
|
||||||
if (note.isHoldNote && note.holdNoteSprite != null)
|
if (note.isHoldNote && note.holdNoteSprite != null)
|
||||||
{
|
{
|
||||||
|
@ -2175,11 +2249,11 @@ class PlayState extends MusicBeatSubState
|
||||||
* Called when a note leaves the screen and is considered missed by the player.
|
* Called when a note leaves the screen and is considered missed by the player.
|
||||||
* @param note
|
* @param note
|
||||||
*/
|
*/
|
||||||
function onNoteMiss(note:NoteSprite, playSound:Bool = false, healthLossMulti:Float = 1.0):Void
|
function onNoteMiss(note:NoteSprite, playSound:Bool = false, healthChange:Float):Void
|
||||||
{
|
{
|
||||||
// If we are here, we already CALLED the onNoteMiss script hook!
|
// If we are here, we already CALLED the onNoteMiss script hook!
|
||||||
|
|
||||||
health -= Constants.HEALTH_MISS_PENALTY * healthLossMulti;
|
health += healthChange;
|
||||||
songScore -= 10;
|
songScore -= 10;
|
||||||
|
|
||||||
if (!isPracticeMode)
|
if (!isPracticeMode)
|
||||||
|
@ -2351,23 +2425,10 @@ class PlayState extends MusicBeatSubState
|
||||||
/**
|
/**
|
||||||
* Handles health, score, and rating popups when a note is hit.
|
* Handles health, score, and rating popups when a note is hit.
|
||||||
*/
|
*/
|
||||||
function popUpScore(daNote:NoteSprite, input:PreciseInputEvent, healthGainMulti:Float = 1.0):Void
|
function popUpScore(daNote:NoteSprite, score:Int, daRating:String, healthChange:Float):Void
|
||||||
{
|
{
|
||||||
vocals.playerVolume = 1;
|
vocals.playerVolume = 1;
|
||||||
|
|
||||||
// Calculate the input latency (do this as late as possible).
|
|
||||||
// trace('Compare: ${PreciseInputManager.getCurrentTimestamp()} - ${input.timestamp}');
|
|
||||||
var inputLatencyNs:Int64 = PreciseInputManager.getCurrentTimestamp() - input.timestamp;
|
|
||||||
var inputLatencyMs:Float = inputLatencyNs.toFloat() / Constants.NS_PER_MS;
|
|
||||||
// trace('Input: ${daNote.noteData.getDirectionName()} pressed ${inputLatencyMs}ms ago!');
|
|
||||||
|
|
||||||
// Get the offset and compensate for input latency.
|
|
||||||
// Round inward (trim remainder) for consistency.
|
|
||||||
var noteDiff:Int = Std.int(Conductor.instance.songPosition - daNote.noteData.time - inputLatencyMs);
|
|
||||||
|
|
||||||
var score = Scoring.scoreNote(noteDiff, PBOT1);
|
|
||||||
var daRating = Scoring.judgeNote(noteDiff, PBOT1);
|
|
||||||
|
|
||||||
if (daRating == 'miss')
|
if (daRating == 'miss')
|
||||||
{
|
{
|
||||||
// If daRating is 'miss', that means we made a mistake and should not continue.
|
// If daRating is 'miss', that means we made a mistake and should not continue.
|
||||||
|
@ -2382,22 +2443,20 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
case 'sick':
|
case 'sick':
|
||||||
Highscore.tallies.sick += 1;
|
Highscore.tallies.sick += 1;
|
||||||
health += Constants.HEALTH_SICK_BONUS * healthGainMulti;
|
|
||||||
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
|
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
|
||||||
case 'good':
|
case 'good':
|
||||||
Highscore.tallies.good += 1;
|
Highscore.tallies.good += 1;
|
||||||
health += Constants.HEALTH_GOOD_BONUS * healthGainMulti;
|
|
||||||
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
|
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
|
||||||
case 'bad':
|
case 'bad':
|
||||||
Highscore.tallies.bad += 1;
|
Highscore.tallies.bad += 1;
|
||||||
health += Constants.HEALTH_BAD_BONUS * healthGainMulti;
|
|
||||||
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
|
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
|
||||||
case 'shit':
|
case 'shit':
|
||||||
Highscore.tallies.shit += 1;
|
Highscore.tallies.shit += 1;
|
||||||
health += Constants.HEALTH_SHIT_BONUS * healthGainMulti;
|
|
||||||
isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
|
isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
health += healthChange;
|
||||||
|
|
||||||
if (isComboBreak)
|
if (isComboBreak)
|
||||||
{
|
{
|
||||||
// Break the combo, but don't increment tallies.misses.
|
// Break the combo, but don't increment tallies.misses.
|
||||||
|
@ -2563,9 +2622,9 @@ class PlayState extends MusicBeatSubState
|
||||||
accuracy: Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes,
|
accuracy: Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Save.get().isSongHighScore(currentSong.id, currentDifficulty, data))
|
if (Save.instance.isSongHighScore(currentSong.id, currentDifficulty, data))
|
||||||
{
|
{
|
||||||
Save.get().setSongScore(currentSong.id, currentDifficulty, data);
|
Save.instance.setSongScore(currentSong.id, currentDifficulty, data);
|
||||||
#if newgrounds
|
#if newgrounds
|
||||||
NGio.postScore(score, currentSong.id);
|
NGio.postScore(score, currentSong.id);
|
||||||
#end
|
#end
|
||||||
|
@ -2613,9 +2672,9 @@ class PlayState extends MusicBeatSubState
|
||||||
accuracy: Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes,
|
accuracy: Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Save.get().isLevelHighScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data))
|
if (Save.instance.isLevelHighScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data))
|
||||||
{
|
{
|
||||||
Save.get().setLevelScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data);
|
Save.instance.setLevelScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data);
|
||||||
#if newgrounds
|
#if newgrounds
|
||||||
NGio.postScore(score, 'Level ${PlayStatePlaylist.campaignId}');
|
NGio.postScore(score, 'Level ${PlayStatePlaylist.campaignId}');
|
||||||
#end
|
#end
|
||||||
|
|
|
@ -46,7 +46,8 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
var _skipTransformChildren:Bool = false;
|
var _skipTransformChildren:Bool = false;
|
||||||
|
|
||||||
var animations:Map<String, AnimateAtlasAnimation> = new Map<String, AnimateAtlasAnimation>();
|
var animations:Map<String, AnimateAtlasAnimation> = new Map<String, AnimateAtlasAnimation>();
|
||||||
var currentAnimation:String;
|
var currentAnimName:Null<String> = null;
|
||||||
|
var animFinished:Bool = false;
|
||||||
|
|
||||||
public function new(id:String)
|
public function new(id:String)
|
||||||
{
|
{
|
||||||
|
@ -77,6 +78,7 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
|
|
||||||
var atlasSprite:FlxAtlasSprite = loadAtlasSprite();
|
var atlasSprite:FlxAtlasSprite = loadAtlasSprite();
|
||||||
setSprite(atlasSprite);
|
setSprite(atlasSprite);
|
||||||
|
|
||||||
loadAnimations();
|
loadAnimations();
|
||||||
|
|
||||||
super.onCreate(event);
|
super.onCreate(event);
|
||||||
|
@ -86,10 +88,36 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
{
|
{
|
||||||
if ((!canPlayOtherAnims && !ignoreOther)) return;
|
if ((!canPlayOtherAnims && !ignoreOther)) return;
|
||||||
|
|
||||||
currentAnimation = name;
|
var correctName = correctAnimationName(name);
|
||||||
var prefix:String = getAnimationData(name).prefix;
|
if (correctName == null)
|
||||||
if (prefix == null) prefix = name;
|
{
|
||||||
this.mainSprite.playAnimation(prefix, restart, ignoreOther);
|
trace('Could not find Atlas animation: ' + name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var animData = getAnimationData(correctName);
|
||||||
|
currentAnimName = correctName;
|
||||||
|
var prefix:String = animData.prefix;
|
||||||
|
if (prefix == null) prefix = correctName;
|
||||||
|
var loop:Bool = animData.looped;
|
||||||
|
|
||||||
|
this.mainSprite.playAnimation(prefix, restart, ignoreOther, loop);
|
||||||
|
|
||||||
|
animFinished = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function hasAnimation(name:String):Bool
|
||||||
|
{
|
||||||
|
return getAnimationData(name) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the animation has finished playing.
|
||||||
|
* Never true if animation is configured to loop.
|
||||||
|
*/
|
||||||
|
public override function isAnimationFinished():Bool
|
||||||
|
{
|
||||||
|
return animFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAtlasSprite():FlxAtlasSprite
|
function loadAtlasSprite():FlxAtlasSprite
|
||||||
|
@ -114,7 +142,13 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Make the game hold on the last frame.
|
||||||
this.mainSprite.cleanupAnimation(prefix);
|
this.mainSprite.cleanupAnimation(prefix);
|
||||||
|
// currentAnimName = null;
|
||||||
|
animFinished = true;
|
||||||
|
|
||||||
|
// Fallback to idle!
|
||||||
|
// playAnimation('idle', true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,19 +174,30 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
|
|
||||||
function loadAnimations():Void
|
function loadAnimations():Void
|
||||||
{
|
{
|
||||||
trace('[ATLASCHAR] Loading ${_data.animations.length} animations for ${characterId}');
|
trace('[ATLASCHAR] Attempting to load ${_data.animations.length} animations for ${characterId}');
|
||||||
|
|
||||||
var animData:Array<AnimateAtlasAnimation> = cast _data.animations;
|
var animData:Array<AnimateAtlasAnimation> = cast _data.animations;
|
||||||
|
|
||||||
for (anim in animData)
|
for (anim in animData)
|
||||||
{
|
{
|
||||||
animations.set(anim.name, anim);
|
// Validate the animation before adding.
|
||||||
|
var prefix = anim.prefix;
|
||||||
|
if (!this.mainSprite.hasAnimation(prefix))
|
||||||
|
{
|
||||||
|
FlxG.log.warn('[ATLASCHAR] Animation ${prefix} not found in Animate Atlas ${_data.assetPath}');
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
animations.set(anim.name, anim);
|
||||||
|
trace('[ATLASCHAR] - Successfully loaded animation ${anim.name} to ${characterId}');
|
||||||
|
}
|
||||||
|
|
||||||
|
trace('[ATLASCHAR] Loaded ${animations.size()} animations for ${characterId}');
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function getCurrentAnimation():String
|
public override function getCurrentAnimation():String
|
||||||
{
|
{
|
||||||
return this.mainSprite.getCurrentAnimation();
|
// return this.mainSprite.getCurrentAnimation();
|
||||||
|
return currentAnimName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAnimationData(name:String = null):AnimateAtlasAnimation
|
function getAnimationData(name:String = null):AnimateAtlasAnimation
|
||||||
|
|
|
@ -60,7 +60,7 @@ class BaseCharacter extends Bopper
|
||||||
|
|
||||||
@:allow(funkin.ui.debug.anim.DebugBoundingState)
|
@:allow(funkin.ui.debug.anim.DebugBoundingState)
|
||||||
final _data:CharacterData;
|
final _data:CharacterData;
|
||||||
final singTimeSec:Float;
|
final singTimeSteps:Float;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The offset between the corner of the sprite and the origin of the sprite (at the character's feet).
|
* The offset between the corner of the sprite and the origin of the sprite (at the character's feet).
|
||||||
|
@ -180,7 +180,7 @@ class BaseCharacter extends Bopper
|
||||||
{
|
{
|
||||||
this.characterName = _data.name;
|
this.characterName = _data.name;
|
||||||
this.name = _data.name;
|
this.name = _data.name;
|
||||||
this.singTimeSec = _data.singTime;
|
this.singTimeSteps = _data.singTime;
|
||||||
this.globalOffsets = _data.offsets;
|
this.globalOffsets = _data.offsets;
|
||||||
this.flipX = _data.flipX;
|
this.flipX = _data.flipX;
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,16 @@ class BaseCharacter extends Bopper
|
||||||
return _data.death?.cameraOffsets ?? [0.0, 0.0];
|
return _data.death?.cameraOffsets ?? [0.0, 0.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDeathCameraZoom():Float
|
||||||
|
{
|
||||||
|
return _data.death?.cameraZoom ?? 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDeathPreTransitionDelay():Float
|
||||||
|
{
|
||||||
|
return _data.death?.preTransitionDelay ?? 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of flipX from the character data.
|
* Gets the value of flipX from the character data.
|
||||||
* `!getFlipX()` is the direction Boyfriend should face.
|
* `!getFlipX()` is the direction Boyfriend should face.
|
||||||
|
@ -367,9 +377,9 @@ 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.instance.beatLengthMs * 0.001); // x beats, to ms.
|
var singTimeSec:Float = singTimeSteps * (Conductor.instance.stepLengthMs / Constants.MS_PER_SEC); // 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???
|
||||||
|
|
||||||
// Without this check here, the player character would only play the `sing` animation
|
// Without this check here, the player character would only play the `sing` animation
|
||||||
// for one beat, as opposed to holding it as long as the player is holding the button.
|
// for one beat, as opposed to holding it as long as the player is holding the button.
|
||||||
|
@ -378,7 +388,7 @@ class BaseCharacter extends Bopper
|
||||||
FlxG.watch.addQuick('singTimeSec-${characterId}', singTimeSec);
|
FlxG.watch.addQuick('singTimeSec-${characterId}', singTimeSec);
|
||||||
if (holdTimer > singTimeSec && shouldStopSinging)
|
if (holdTimer > singTimeSec && shouldStopSinging)
|
||||||
{
|
{
|
||||||
// trace('holdTimer reached ${holdTimer}sec (> ${singTimeSec}), stopping sing animation');
|
trace('holdTimer reached ${holdTimer}sec (> ${singTimeSec}), stopping sing animation');
|
||||||
holdTimer = 0;
|
holdTimer = 0;
|
||||||
dance(true);
|
dance(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -744,4 +744,17 @@ typedef DeathData =
|
||||||
* @default [0, 0]
|
* @default [0, 0]
|
||||||
*/
|
*/
|
||||||
var ?cameraOffsets:Array<Float>;
|
var ?cameraOffsets:Array<Float>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount to zoom the camera by while focusing on this character as they die.
|
||||||
|
* Value is a multiplier of the default camera zoom for the stage.
|
||||||
|
* @default 1.0
|
||||||
|
*/
|
||||||
|
var ?cameraZoom:Float;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Impose a delay between when the character reaches `0` health and when the death animation plays.
|
||||||
|
* @default 0.0
|
||||||
|
*/
|
||||||
|
var ?preTransitionDelay:Float;
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,25 +218,25 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||||
// If the animation exists, we're good.
|
// If the animation exists, we're good.
|
||||||
if (hasAnimation(name)) return name;
|
if (hasAnimation(name)) return name;
|
||||||
|
|
||||||
trace('[BOPPER] Animation "$name" does not exist!');
|
FlxG.log.notice('Speaker tried to play animation "$name" that does not exist, stripping suffixes...');
|
||||||
|
|
||||||
// Attempt to strip a `-alt` suffix, if it exists.
|
// Attempt to strip a `-alt` suffix, if it exists.
|
||||||
if (name.lastIndexOf('-') != -1)
|
if (name.lastIndexOf('-') != -1)
|
||||||
{
|
{
|
||||||
var correctName = name.substring(0, name.lastIndexOf('-'));
|
var correctName = name.substring(0, name.lastIndexOf('-'));
|
||||||
trace('[BOPPER] Attempting to fallback to "$correctName"');
|
FlxG.log.notice('Speaker tried to play animation "$name" that does not exist, stripping suffixes...');
|
||||||
return correctAnimationName(correctName);
|
return correctAnimationName(correctName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (name != 'idle')
|
if (name != 'idle')
|
||||||
{
|
{
|
||||||
trace('[BOPPER] Attempting to fallback to "idle"');
|
FlxG.log.warn('Speaker tried to play animation "$name" that does not exist, fallback to idle...');
|
||||||
return correctAnimationName('idle');
|
return correctAnimationName('idle');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('[BOPPER] Failing animation playback.');
|
FlxG.log.error('Speaker tried to play animation "idle" that does not exist! This is bad!');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,26 +16,53 @@ class NoteSprite extends FunkinSprite
|
||||||
var hsvShader:HSVShader;
|
var hsvShader:HSVShader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time at which the note should be hit, in milliseconds.
|
* The strum time at which the note should be hit, in milliseconds.
|
||||||
*/
|
*/
|
||||||
public var strumTime(default, set):Float;
|
public var strumTime(get, set):Float;
|
||||||
|
|
||||||
|
function get_strumTime():Float
|
||||||
|
{
|
||||||
|
return this.noteData?.time ?? 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
function set_strumTime(value:Float):Float
|
function set_strumTime(value:Float):Float
|
||||||
{
|
{
|
||||||
this.strumTime = value;
|
if (this.noteData == null) return value;
|
||||||
return this.strumTime;
|
return this.noteData.time = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length for which the note should be held, in milliseconds.
|
||||||
|
* Defaults to 0 for single notes.
|
||||||
|
*/
|
||||||
|
public var length(get, set):Float;
|
||||||
|
|
||||||
|
function get_length():Float
|
||||||
|
{
|
||||||
|
return this.noteData?.length ?? 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_length(value:Float):Float
|
||||||
|
{
|
||||||
|
if (this.noteData == null) return value;
|
||||||
|
return this.noteData.length = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An extra attribute for the note.
|
* An extra attribute for the note.
|
||||||
* For example, whether the note is an "alt" note, or whether it has custom behavior on hit.
|
* For example, whether the note is an "alt" note, or whether it has custom behavior on hit.
|
||||||
*/
|
*/
|
||||||
public var kind(default, set):String;
|
public var kind(get, set):Null<String>;
|
||||||
|
|
||||||
|
function get_kind():Null<String>
|
||||||
|
{
|
||||||
|
return this.noteData?.kind;
|
||||||
|
}
|
||||||
|
|
||||||
function set_kind(value:String):String
|
function set_kind(value:String):String
|
||||||
{
|
{
|
||||||
this.kind = value;
|
if (this.noteData == null) return value;
|
||||||
return this.kind;
|
return this.noteData.kind = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,16 +127,13 @@ class NoteSprite extends FunkinSprite
|
||||||
*/
|
*/
|
||||||
public var handledMiss:Bool;
|
public var handledMiss:Bool;
|
||||||
|
|
||||||
public function new(noteStyle:NoteStyle, strumTime:Float = 0, direction:Int = 0)
|
public function new(noteStyle:NoteStyle, direction:Int = 0)
|
||||||
{
|
{
|
||||||
super(0, -9999);
|
super(0, -9999);
|
||||||
this.strumTime = strumTime;
|
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
|
|
||||||
this.hsvShader = new HSVShader();
|
this.hsvShader = new HSVShader();
|
||||||
|
|
||||||
if (this.strumTime < 0) this.strumTime = 0;
|
|
||||||
|
|
||||||
setupNoteGraphic(noteStyle);
|
setupNoteGraphic(noteStyle);
|
||||||
|
|
||||||
// Disables the update() function for performance.
|
// Disables the update() function for performance.
|
||||||
|
|
|
@ -659,7 +659,6 @@ class Strumline extends FlxSpriteGroup
|
||||||
|
|
||||||
if (noteSprite != null)
|
if (noteSprite != null)
|
||||||
{
|
{
|
||||||
noteSprite.strumTime = note.time;
|
|
||||||
noteSprite.direction = note.getDirection();
|
noteSprite.direction = note.getDirection();
|
||||||
noteSprite.noteData = note;
|
noteSprite.noteData = note;
|
||||||
|
|
||||||
|
|
|
@ -236,25 +236,25 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
|
||||||
// If the animation exists, we're good.
|
// If the animation exists, we're good.
|
||||||
if (hasAnimation(name)) return name;
|
if (hasAnimation(name)) return name;
|
||||||
|
|
||||||
trace('[BOPPER] Animation "$name" does not exist!');
|
FlxG.log.notice('Bopper tried to play animation "$name" that does not exist, stripping suffixes...');
|
||||||
|
|
||||||
// Attempt to strip a `-alt` suffix, if it exists.
|
// Attempt to strip a `-alt` suffix, if it exists.
|
||||||
if (name.lastIndexOf('-') != -1)
|
if (name.lastIndexOf('-') != -1)
|
||||||
{
|
{
|
||||||
var correctName = name.substring(0, name.lastIndexOf('-'));
|
var correctName = name.substring(0, name.lastIndexOf('-'));
|
||||||
trace('[BOPPER] Attempting to fallback to "$correctName"');
|
FlxG.log.notice('Bopper tried to play animation "$name" that does not exist, stripping suffixes...');
|
||||||
return correctAnimationName(correctName);
|
return correctAnimationName(correctName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (name != 'idle')
|
if (name != 'idle')
|
||||||
{
|
{
|
||||||
trace('[BOPPER] Attempting to fallback to "idle"');
|
FlxG.log.warn('Bopper tried to play animation "$name" that does not exist, fallback to idle...');
|
||||||
return correctAnimationName('idle');
|
return correctAnimationName('idle');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('[BOPPER] Failing animation playback.');
|
FlxG.log.error('Bopper tried to play animation "idle" that does not exist! This is bad!');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
getBoyfriend().resetCharacter(true);
|
getBoyfriend().resetCharacter(true);
|
||||||
// Reapply the camera offsets.
|
// Reapply the camera offsets.
|
||||||
var charData = _data.characters.bf;
|
var charData = _data.characters.bf;
|
||||||
|
getBoyfriend().scale.set(charData.scale, charData.scale);
|
||||||
getBoyfriend().cameraFocusPoint.x += charData.cameraOffsets[0];
|
getBoyfriend().cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||||
getBoyfriend().cameraFocusPoint.y += charData.cameraOffsets[1];
|
getBoyfriend().cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||||
}
|
}
|
||||||
|
@ -122,6 +123,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
getGirlfriend().resetCharacter(true);
|
getGirlfriend().resetCharacter(true);
|
||||||
// Reapply the camera offsets.
|
// Reapply the camera offsets.
|
||||||
var charData = _data.characters.gf;
|
var charData = _data.characters.gf;
|
||||||
|
getGirlfriend().scale.set(charData.scale, charData.scale);
|
||||||
getGirlfriend().cameraFocusPoint.x += charData.cameraOffsets[0];
|
getGirlfriend().cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||||
getGirlfriend().cameraFocusPoint.y += charData.cameraOffsets[1];
|
getGirlfriend().cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||||
}
|
}
|
||||||
|
@ -130,6 +132,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
getDad().resetCharacter(true);
|
getDad().resetCharacter(true);
|
||||||
// Reapply the camera offsets.
|
// Reapply the camera offsets.
|
||||||
var charData = _data.characters.dad;
|
var charData = _data.characters.dad;
|
||||||
|
getDad().scale.set(charData.scale, charData.scale);
|
||||||
getDad().cameraFocusPoint.x += charData.cameraOffsets[0];
|
getDad().cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||||
getDad().cameraFocusPoint.y += charData.cameraOffsets[1];
|
getDad().cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||||
}
|
}
|
||||||
|
@ -226,7 +229,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
switch (dataProp.scale)
|
switch (dataProp.scale)
|
||||||
{
|
{
|
||||||
case Left(value):
|
case Left(value):
|
||||||
propSprite.scale.set(value);
|
propSprite.scale.set(value, value);
|
||||||
|
|
||||||
case Right(values):
|
case Right(values):
|
||||||
propSprite.scale.set(values[0], values[1]);
|
propSprite.scale.set(values[0], values[1]);
|
||||||
|
@ -435,6 +438,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
character.originalPosition.y = character.y + character.animOffsets[1];
|
character.originalPosition.y = character.y + character.animOffsets[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
character.scale.set(charData.scale, charData.scale);
|
||||||
character.cameraFocusPoint.x += charData.cameraOffsets[0];
|
character.cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||||
character.cameraFocusPoint.y += charData.cameraOffsets[1];
|
character.cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||||
|
|
||||||
|
@ -637,7 +641,30 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
*/
|
*/
|
||||||
public function dispatchToCharacters(event:ScriptEvent):Void
|
public function dispatchToCharacters(event:ScriptEvent):Void
|
||||||
{
|
{
|
||||||
for (characterId in characters.keys())
|
var charList = this.characters.keys().array();
|
||||||
|
|
||||||
|
// Dad, then BF, then GF, in that order.
|
||||||
|
|
||||||
|
if (charList.contains('dad'))
|
||||||
|
{
|
||||||
|
dispatchToCharacter('dad', event);
|
||||||
|
charList.remove('dad');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charList.contains('bf'))
|
||||||
|
{
|
||||||
|
dispatchToCharacter('bf', event);
|
||||||
|
charList.remove('bf');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charList.contains('gf'))
|
||||||
|
{
|
||||||
|
dispatchToCharacter('gf', event);
|
||||||
|
charList.remove('gf');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then the rest of the characters, if any.
|
||||||
|
for (characterId in charList)
|
||||||
{
|
{
|
||||||
dispatchToCharacter(characterId, event);
|
dispatchToCharacter(characterId, event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,7 @@ import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
|
||||||
import thx.semver.Version;
|
import thx.semver.Version;
|
||||||
|
|
||||||
@:nullSafety
|
@:nullSafety
|
||||||
@:forward(volume, mute)
|
class Save
|
||||||
abstract Save(RawSaveData)
|
|
||||||
{
|
{
|
||||||
// Version 2.0.2 adds attributes to `optionsChartEditor`, that should return default values if they are null.
|
// Version 2.0.2 adds attributes to `optionsChartEditor`, that should return default values if they are null.
|
||||||
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.2";
|
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.2";
|
||||||
|
@ -25,6 +24,20 @@ abstract Save(RawSaveData)
|
||||||
static final SAVE_PATH_LEGACY:String = 'ninjamuffin99';
|
static final SAVE_PATH_LEGACY:String = 'ninjamuffin99';
|
||||||
static final SAVE_NAME_LEGACY:String = 'funkin';
|
static final SAVE_NAME_LEGACY:String = 'funkin';
|
||||||
|
|
||||||
|
public static var instance(get, never):Save;
|
||||||
|
static var _instance:Null<Save> = null;
|
||||||
|
|
||||||
|
static function get_instance():Save
|
||||||
|
{
|
||||||
|
if (_instance == null)
|
||||||
|
{
|
||||||
|
_instance = new Save(FlxG.save.data);
|
||||||
|
}
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data:RawSaveData;
|
||||||
|
|
||||||
public static function load():Void
|
public static function load():Void
|
||||||
{
|
{
|
||||||
trace("[SAVE] Loading save...");
|
trace("[SAVE] Loading save...");
|
||||||
|
@ -33,18 +46,19 @@ abstract Save(RawSaveData)
|
||||||
loadFromSlot(1);
|
loadFromSlot(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get():Save
|
|
||||||
{
|
|
||||||
return FlxG.save.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructing a new Save will load the default values.
|
* Constructing a new Save will load the default values.
|
||||||
*/
|
*/
|
||||||
public function new()
|
public function new(data:RawSaveData)
|
||||||
{
|
{
|
||||||
this =
|
this.data = data;
|
||||||
|
|
||||||
|
if (this.data == null) data = Save.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDefault():RawSaveData
|
||||||
{
|
{
|
||||||
|
return {
|
||||||
version: Save.SAVE_DATA_VERSION,
|
version: Save.SAVE_DATA_VERSION,
|
||||||
|
|
||||||
volume: 1.0,
|
volume: 1.0,
|
||||||
|
@ -120,7 +134,7 @@ abstract Save(RawSaveData)
|
||||||
|
|
||||||
function get_options():SaveDataOptions
|
function get_options():SaveDataOptions
|
||||||
{
|
{
|
||||||
return this.options;
|
return data.options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,7 +144,7 @@ abstract Save(RawSaveData)
|
||||||
|
|
||||||
function get_modOptions():Map<String, Dynamic>
|
function get_modOptions():Map<String, Dynamic>
|
||||||
{
|
{
|
||||||
return this.mods.modOptions;
|
return data.mods.modOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,232 +154,232 @@ abstract Save(RawSaveData)
|
||||||
|
|
||||||
function get_ngSessionId():Null<String>
|
function get_ngSessionId():Null<String>
|
||||||
{
|
{
|
||||||
return this.api.newgrounds.sessionId;
|
return data.api.newgrounds.sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_ngSessionId(value:Null<String>):Null<String>
|
function set_ngSessionId(value:Null<String>):Null<String>
|
||||||
{
|
{
|
||||||
this.api.newgrounds.sessionId = value;
|
data.api.newgrounds.sessionId = value;
|
||||||
flush();
|
flush();
|
||||||
return this.api.newgrounds.sessionId;
|
return data.api.newgrounds.sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var enabledModIds(get, set):Array<String>;
|
public var enabledModIds(get, set):Array<String>;
|
||||||
|
|
||||||
function get_enabledModIds():Array<String>
|
function get_enabledModIds():Array<String>
|
||||||
{
|
{
|
||||||
return this.mods.enabledMods;
|
return data.mods.enabledMods;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_enabledModIds(value:Array<String>):Array<String>
|
function set_enabledModIds(value:Array<String>):Array<String>
|
||||||
{
|
{
|
||||||
this.mods.enabledMods = value;
|
data.mods.enabledMods = value;
|
||||||
flush();
|
flush();
|
||||||
return this.mods.enabledMods;
|
return data.mods.enabledMods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorPreviousFiles(get, set):Array<String>;
|
public var chartEditorPreviousFiles(get, set):Array<String>;
|
||||||
|
|
||||||
function get_chartEditorPreviousFiles():Array<String>
|
function get_chartEditorPreviousFiles():Array<String>
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.previousFiles == null) this.optionsChartEditor.previousFiles = [];
|
if (data.optionsChartEditor.previousFiles == null) data.optionsChartEditor.previousFiles = [];
|
||||||
|
|
||||||
return this.optionsChartEditor.previousFiles;
|
return data.optionsChartEditor.previousFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorPreviousFiles(value:Array<String>):Array<String>
|
function set_chartEditorPreviousFiles(value:Array<String>):Array<String>
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.previousFiles = value;
|
data.optionsChartEditor.previousFiles = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.previousFiles;
|
return data.optionsChartEditor.previousFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorHasBackup(get, set):Bool;
|
public var chartEditorHasBackup(get, set):Bool;
|
||||||
|
|
||||||
function get_chartEditorHasBackup():Bool
|
function get_chartEditorHasBackup():Bool
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.hasBackup == null) this.optionsChartEditor.hasBackup = false;
|
if (data.optionsChartEditor.hasBackup == null) data.optionsChartEditor.hasBackup = false;
|
||||||
|
|
||||||
return this.optionsChartEditor.hasBackup;
|
return data.optionsChartEditor.hasBackup;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorHasBackup(value:Bool):Bool
|
function set_chartEditorHasBackup(value:Bool):Bool
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.hasBackup = value;
|
data.optionsChartEditor.hasBackup = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.hasBackup;
|
return data.optionsChartEditor.hasBackup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorNoteQuant(get, set):Int;
|
public var chartEditorNoteQuant(get, set):Int;
|
||||||
|
|
||||||
function get_chartEditorNoteQuant():Int
|
function get_chartEditorNoteQuant():Int
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.noteQuant == null) this.optionsChartEditor.noteQuant = 3;
|
if (data.optionsChartEditor.noteQuant == null) data.optionsChartEditor.noteQuant = 3;
|
||||||
|
|
||||||
return this.optionsChartEditor.noteQuant;
|
return data.optionsChartEditor.noteQuant;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorNoteQuant(value:Int):Int
|
function set_chartEditorNoteQuant(value:Int):Int
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.noteQuant = value;
|
data.optionsChartEditor.noteQuant = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.noteQuant;
|
return data.optionsChartEditor.noteQuant;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorLiveInputStyle(get, set):ChartEditorLiveInputStyle;
|
public var chartEditorLiveInputStyle(get, set):ChartEditorLiveInputStyle;
|
||||||
|
|
||||||
function get_chartEditorLiveInputStyle():ChartEditorLiveInputStyle
|
function get_chartEditorLiveInputStyle():ChartEditorLiveInputStyle
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.chartEditorLiveInputStyle == null) this.optionsChartEditor.chartEditorLiveInputStyle = ChartEditorLiveInputStyle.None;
|
if (data.optionsChartEditor.chartEditorLiveInputStyle == null) data.optionsChartEditor.chartEditorLiveInputStyle = ChartEditorLiveInputStyle.None;
|
||||||
|
|
||||||
return this.optionsChartEditor.chartEditorLiveInputStyle;
|
return data.optionsChartEditor.chartEditorLiveInputStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorLiveInputStyle(value:ChartEditorLiveInputStyle):ChartEditorLiveInputStyle
|
function set_chartEditorLiveInputStyle(value:ChartEditorLiveInputStyle):ChartEditorLiveInputStyle
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.chartEditorLiveInputStyle = value;
|
data.optionsChartEditor.chartEditorLiveInputStyle = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.chartEditorLiveInputStyle;
|
return data.optionsChartEditor.chartEditorLiveInputStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorDownscroll(get, set):Bool;
|
public var chartEditorDownscroll(get, set):Bool;
|
||||||
|
|
||||||
function get_chartEditorDownscroll():Bool
|
function get_chartEditorDownscroll():Bool
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.downscroll == null) this.optionsChartEditor.downscroll = false;
|
if (data.optionsChartEditor.downscroll == null) data.optionsChartEditor.downscroll = false;
|
||||||
|
|
||||||
return this.optionsChartEditor.downscroll;
|
return data.optionsChartEditor.downscroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorDownscroll(value:Bool):Bool
|
function set_chartEditorDownscroll(value:Bool):Bool
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.downscroll = value;
|
data.optionsChartEditor.downscroll = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.downscroll;
|
return data.optionsChartEditor.downscroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorPlaytestStartTime(get, set):Bool;
|
public var chartEditorPlaytestStartTime(get, set):Bool;
|
||||||
|
|
||||||
function get_chartEditorPlaytestStartTime():Bool
|
function get_chartEditorPlaytestStartTime():Bool
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.playtestStartTime == null) this.optionsChartEditor.playtestStartTime = false;
|
if (data.optionsChartEditor.playtestStartTime == null) data.optionsChartEditor.playtestStartTime = false;
|
||||||
|
|
||||||
return this.optionsChartEditor.playtestStartTime;
|
return data.optionsChartEditor.playtestStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorPlaytestStartTime(value:Bool):Bool
|
function set_chartEditorPlaytestStartTime(value:Bool):Bool
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.playtestStartTime = value;
|
data.optionsChartEditor.playtestStartTime = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.playtestStartTime;
|
return data.optionsChartEditor.playtestStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorTheme(get, set):ChartEditorTheme;
|
public var chartEditorTheme(get, set):ChartEditorTheme;
|
||||||
|
|
||||||
function get_chartEditorTheme():ChartEditorTheme
|
function get_chartEditorTheme():ChartEditorTheme
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.theme == null) this.optionsChartEditor.theme = ChartEditorTheme.Light;
|
if (data.optionsChartEditor.theme == null) data.optionsChartEditor.theme = ChartEditorTheme.Light;
|
||||||
|
|
||||||
return this.optionsChartEditor.theme;
|
return data.optionsChartEditor.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorTheme(value:ChartEditorTheme):ChartEditorTheme
|
function set_chartEditorTheme(value:ChartEditorTheme):ChartEditorTheme
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.theme = value;
|
data.optionsChartEditor.theme = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.theme;
|
return data.optionsChartEditor.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorMetronomeVolume(get, set):Float;
|
public var chartEditorMetronomeVolume(get, set):Float;
|
||||||
|
|
||||||
function get_chartEditorMetronomeVolume():Float
|
function get_chartEditorMetronomeVolume():Float
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.metronomeVolume == null) this.optionsChartEditor.metronomeVolume = 1.0;
|
if (data.optionsChartEditor.metronomeVolume == null) data.optionsChartEditor.metronomeVolume = 1.0;
|
||||||
|
|
||||||
return this.optionsChartEditor.metronomeVolume;
|
return data.optionsChartEditor.metronomeVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorMetronomeVolume(value:Float):Float
|
function set_chartEditorMetronomeVolume(value:Float):Float
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.metronomeVolume = value;
|
data.optionsChartEditor.metronomeVolume = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.metronomeVolume;
|
return data.optionsChartEditor.metronomeVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorHitsoundVolumePlayer(get, set):Float;
|
public var chartEditorHitsoundVolumePlayer(get, set):Float;
|
||||||
|
|
||||||
function get_chartEditorHitsoundVolumePlayer():Float
|
function get_chartEditorHitsoundVolumePlayer():Float
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.hitsoundVolumePlayer == null) this.optionsChartEditor.hitsoundVolumePlayer = 1.0;
|
if (data.optionsChartEditor.hitsoundVolumePlayer == null) data.optionsChartEditor.hitsoundVolumePlayer = 1.0;
|
||||||
|
|
||||||
return this.optionsChartEditor.hitsoundVolumePlayer;
|
return data.optionsChartEditor.hitsoundVolumePlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorHitsoundVolumePlayer(value:Float):Float
|
function set_chartEditorHitsoundVolumePlayer(value:Float):Float
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.hitsoundVolumePlayer = value;
|
data.optionsChartEditor.hitsoundVolumePlayer = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.hitsoundVolumePlayer;
|
return data.optionsChartEditor.hitsoundVolumePlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorHitsoundVolumeOpponent(get, set):Float;
|
public var chartEditorHitsoundVolumeOpponent(get, set):Float;
|
||||||
|
|
||||||
function get_chartEditorHitsoundVolumeOpponent():Float
|
function get_chartEditorHitsoundVolumeOpponent():Float
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.hitsoundVolumeOpponent == null) this.optionsChartEditor.hitsoundVolumeOpponent = 1.0;
|
if (data.optionsChartEditor.hitsoundVolumeOpponent == null) data.optionsChartEditor.hitsoundVolumeOpponent = 1.0;
|
||||||
|
|
||||||
return this.optionsChartEditor.hitsoundVolumeOpponent;
|
return data.optionsChartEditor.hitsoundVolumeOpponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorHitsoundVolumeOpponent(value:Float):Float
|
function set_chartEditorHitsoundVolumeOpponent(value:Float):Float
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.hitsoundVolumeOpponent = value;
|
data.optionsChartEditor.hitsoundVolumeOpponent = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.hitsoundVolumeOpponent;
|
return data.optionsChartEditor.hitsoundVolumeOpponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorThemeMusic(get, set):Bool;
|
public var chartEditorThemeMusic(get, set):Bool;
|
||||||
|
|
||||||
function get_chartEditorThemeMusic():Bool
|
function get_chartEditorThemeMusic():Bool
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.themeMusic == null) this.optionsChartEditor.themeMusic = true;
|
if (data.optionsChartEditor.themeMusic == null) data.optionsChartEditor.themeMusic = true;
|
||||||
|
|
||||||
return this.optionsChartEditor.themeMusic;
|
return data.optionsChartEditor.themeMusic;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorThemeMusic(value:Bool):Bool
|
function set_chartEditorThemeMusic(value:Bool):Bool
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.themeMusic = value;
|
data.optionsChartEditor.themeMusic = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.themeMusic;
|
return data.optionsChartEditor.themeMusic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var chartEditorPlaybackSpeed(get, set):Float;
|
public var chartEditorPlaybackSpeed(get, set):Float;
|
||||||
|
|
||||||
function get_chartEditorPlaybackSpeed():Float
|
function get_chartEditorPlaybackSpeed():Float
|
||||||
{
|
{
|
||||||
if (this.optionsChartEditor.playbackSpeed == null) this.optionsChartEditor.playbackSpeed = 1.0;
|
if (data.optionsChartEditor.playbackSpeed == null) data.optionsChartEditor.playbackSpeed = 1.0;
|
||||||
|
|
||||||
return this.optionsChartEditor.playbackSpeed;
|
return data.optionsChartEditor.playbackSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_chartEditorPlaybackSpeed(value:Float):Float
|
function set_chartEditorPlaybackSpeed(value:Float):Float
|
||||||
{
|
{
|
||||||
// Set and apply.
|
// Set and apply.
|
||||||
this.optionsChartEditor.playbackSpeed = value;
|
data.optionsChartEditor.playbackSpeed = value;
|
||||||
flush();
|
flush();
|
||||||
return this.optionsChartEditor.playbackSpeed;
|
return data.optionsChartEditor.playbackSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -377,11 +391,11 @@ abstract Save(RawSaveData)
|
||||||
*/
|
*/
|
||||||
public function getLevelScore(levelId:String, difficultyId:String = 'normal'):Null<SaveScoreData>
|
public function getLevelScore(levelId:String, difficultyId:String = 'normal'):Null<SaveScoreData>
|
||||||
{
|
{
|
||||||
var level = this.scores.levels.get(levelId);
|
var level = data.scores.levels.get(levelId);
|
||||||
if (level == null)
|
if (level == null)
|
||||||
{
|
{
|
||||||
level = [];
|
level = [];
|
||||||
this.scores.levels.set(levelId, level);
|
data.scores.levels.set(levelId, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
return level.get(difficultyId);
|
return level.get(difficultyId);
|
||||||
|
@ -392,11 +406,11 @@ abstract Save(RawSaveData)
|
||||||
*/
|
*/
|
||||||
public function setLevelScore(levelId:String, difficultyId:String, score:SaveScoreData):Void
|
public function setLevelScore(levelId:String, difficultyId:String, score:SaveScoreData):Void
|
||||||
{
|
{
|
||||||
var level = this.scores.levels.get(levelId);
|
var level = data.scores.levels.get(levelId);
|
||||||
if (level == null)
|
if (level == null)
|
||||||
{
|
{
|
||||||
level = [];
|
level = [];
|
||||||
this.scores.levels.set(levelId, level);
|
data.scores.levels.set(levelId, level);
|
||||||
}
|
}
|
||||||
level.set(difficultyId, score);
|
level.set(difficultyId, score);
|
||||||
|
|
||||||
|
@ -405,11 +419,11 @@ abstract Save(RawSaveData)
|
||||||
|
|
||||||
public function isLevelHighScore(levelId:String, difficultyId:String = 'normal', score:SaveScoreData):Bool
|
public function isLevelHighScore(levelId:String, difficultyId:String = 'normal', score:SaveScoreData):Bool
|
||||||
{
|
{
|
||||||
var level = this.scores.levels.get(levelId);
|
var level = data.scores.levels.get(levelId);
|
||||||
if (level == null)
|
if (level == null)
|
||||||
{
|
{
|
||||||
level = [];
|
level = [];
|
||||||
this.scores.levels.set(levelId, level);
|
data.scores.levels.set(levelId, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentScore = level.get(difficultyId);
|
var currentScore = level.get(difficultyId);
|
||||||
|
@ -448,11 +462,11 @@ abstract Save(RawSaveData)
|
||||||
*/
|
*/
|
||||||
public function getSongScore(songId:String, difficultyId:String = 'normal'):Null<SaveScoreData>
|
public function getSongScore(songId:String, difficultyId:String = 'normal'):Null<SaveScoreData>
|
||||||
{
|
{
|
||||||
var song = this.scores.songs.get(songId);
|
var song = data.scores.songs.get(songId);
|
||||||
if (song == null)
|
if (song == null)
|
||||||
{
|
{
|
||||||
song = [];
|
song = [];
|
||||||
this.scores.songs.set(songId, song);
|
data.scores.songs.set(songId, song);
|
||||||
}
|
}
|
||||||
return song.get(difficultyId);
|
return song.get(difficultyId);
|
||||||
}
|
}
|
||||||
|
@ -462,11 +476,11 @@ abstract Save(RawSaveData)
|
||||||
*/
|
*/
|
||||||
public function setSongScore(songId:String, difficultyId:String, score:SaveScoreData):Void
|
public function setSongScore(songId:String, difficultyId:String, score:SaveScoreData):Void
|
||||||
{
|
{
|
||||||
var song = this.scores.songs.get(songId);
|
var song = data.scores.songs.get(songId);
|
||||||
if (song == null)
|
if (song == null)
|
||||||
{
|
{
|
||||||
song = [];
|
song = [];
|
||||||
this.scores.songs.set(songId, song);
|
data.scores.songs.set(songId, song);
|
||||||
}
|
}
|
||||||
song.set(difficultyId, score);
|
song.set(difficultyId, score);
|
||||||
|
|
||||||
|
@ -482,11 +496,11 @@ abstract Save(RawSaveData)
|
||||||
*/
|
*/
|
||||||
public function isSongHighScore(songId:String, difficultyId:String = 'normal', score:SaveScoreData):Bool
|
public function isSongHighScore(songId:String, difficultyId:String = 'normal', score:SaveScoreData):Bool
|
||||||
{
|
{
|
||||||
var song = this.scores.songs.get(songId);
|
var song = data.scores.songs.get(songId);
|
||||||
if (song == null)
|
if (song == null)
|
||||||
{
|
{
|
||||||
song = [];
|
song = [];
|
||||||
this.scores.songs.set(songId, song);
|
data.scores.songs.set(songId, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentScore = song.get(difficultyId);
|
var currentScore = song.get(difficultyId);
|
||||||
|
@ -527,9 +541,9 @@ abstract Save(RawSaveData)
|
||||||
switch (inputType)
|
switch (inputType)
|
||||||
{
|
{
|
||||||
case Keys:
|
case Keys:
|
||||||
return (playerId == 0) ? this.options.controls.p1.keyboard : this.options.controls.p2.keyboard;
|
return (playerId == 0) ? data.options.controls.p1.keyboard : data.options.controls.p2.keyboard;
|
||||||
case Gamepad(_):
|
case Gamepad(_):
|
||||||
return (playerId == 0) ? this.options.controls.p1.gamepad : this.options.controls.p2.gamepad;
|
return (playerId == 0) ? data.options.controls.p1.gamepad : data.options.controls.p2.gamepad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,20 +561,20 @@ abstract Save(RawSaveData)
|
||||||
case Keys:
|
case Keys:
|
||||||
if (playerId == 0)
|
if (playerId == 0)
|
||||||
{
|
{
|
||||||
this.options.controls.p1.keyboard = controls;
|
data.options.controls.p1.keyboard = controls;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.options.controls.p2.keyboard = controls;
|
data.options.controls.p2.keyboard = controls;
|
||||||
}
|
}
|
||||||
case Gamepad(_):
|
case Gamepad(_):
|
||||||
if (playerId == 0)
|
if (playerId == 0)
|
||||||
{
|
{
|
||||||
this.options.controls.p1.gamepad = controls;
|
data.options.controls.p1.gamepad = controls;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.options.controls.p2.gamepad = controls;
|
data.options.controls.p2.gamepad = controls;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,6 +595,36 @@ abstract Save(RawSaveData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user's current volume setting.
|
||||||
|
*/
|
||||||
|
public var volume(get, set):Float;
|
||||||
|
|
||||||
|
function get_volume():Float
|
||||||
|
{
|
||||||
|
return data.volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_volume(value:Float):Float
|
||||||
|
{
|
||||||
|
return data.volume = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user's volume is currently muted.
|
||||||
|
*/
|
||||||
|
public var mute(get, set):Bool;
|
||||||
|
|
||||||
|
function get_mute():Bool
|
||||||
|
{
|
||||||
|
return data.mute;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_mute(value:Bool):Bool
|
||||||
|
{
|
||||||
|
return data.mute = value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this to make sure the save data is written to disk.
|
* Call this to make sure the save data is written to disk.
|
||||||
*/
|
*/
|
||||||
|
@ -606,17 +650,22 @@ abstract Save(RawSaveData)
|
||||||
if (legacySaveData != null)
|
if (legacySaveData != null)
|
||||||
{
|
{
|
||||||
trace('[SAVE] Found legacy save data, converting...');
|
trace('[SAVE] Found legacy save data, converting...');
|
||||||
FlxG.save.mergeData(SaveDataMigrator.migrateFromLegacy(legacySaveData));
|
var gameSave = SaveDataMigrator.migrate(legacySaveData);
|
||||||
|
@:privateAccess
|
||||||
|
FlxG.save.mergeData(gameSave.data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('[SAVE] No legacy save data found.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('[SAVE] Loaded save data.');
|
trace('[SAVE] Loaded save data.');
|
||||||
FlxG.save.mergeData(SaveDataMigrator.migrate(FlxG.save.data));
|
@:privateAccess
|
||||||
|
var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
|
||||||
|
FlxG.save.mergeData(gameSave.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace('[SAVE] Done loading save data.');
|
|
||||||
trace(FlxG.save.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static function fetchLegacySaveData():Null<RawSaveData_v1_0_0>
|
static function fetchLegacySaveData():Null<RawSaveData_v1_0_0>
|
||||||
|
|
|
@ -19,21 +19,21 @@ class SaveDataMigrator
|
||||||
{
|
{
|
||||||
trace('[SAVE] No version found in save data! Returning blank data.');
|
trace('[SAVE] No version found in save data! Returning blank data.');
|
||||||
trace(inputData);
|
trace(inputData);
|
||||||
return new Save();
|
return new Save(Save.getDefault());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (VersionUtil.validateVersion(version, Save.SAVE_DATA_VERSION_RULE))
|
if (VersionUtil.validateVersion(version, Save.SAVE_DATA_VERSION_RULE))
|
||||||
{
|
{
|
||||||
// Simply cast the structured data.
|
// Simply import the structured data.
|
||||||
var save:Save = inputData;
|
var save:Save = new Save(inputData);
|
||||||
return save;
|
return save;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('[SAVE] Invalid save data version! Returning blank data.');
|
trace('[SAVE] Invalid save data version! Returning blank data.');
|
||||||
trace(inputData);
|
trace(inputData);
|
||||||
return new Save();
|
return new Save(Save.getDefault());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ class SaveDataMigrator
|
||||||
{
|
{
|
||||||
var inputSaveData:RawSaveData_v1_0_0 = cast inputData;
|
var inputSaveData:RawSaveData_v1_0_0 = cast inputData;
|
||||||
|
|
||||||
var result:Save = new Save();
|
var result:Save = new Save(Save.getDefault());
|
||||||
|
|
||||||
result.volume = inputSaveData.volume;
|
result.volume = inputSaveData.volume;
|
||||||
result.mute = inputSaveData.mute;
|
result.mute = inputSaveData.mute;
|
||||||
|
|
|
@ -920,12 +920,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
function get_shouldShowBackupAvailableDialog():Bool
|
function get_shouldShowBackupAvailableDialog():Bool
|
||||||
{
|
{
|
||||||
return Save.get().chartEditorHasBackup;
|
return Save.instance.chartEditorHasBackup;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_shouldShowBackupAvailableDialog(value:Bool):Bool
|
function set_shouldShowBackupAvailableDialog(value:Bool):Bool
|
||||||
{
|
{
|
||||||
return Save.get().chartEditorHasBackup = value;
|
return Save.instance.chartEditorHasBackup = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2163,7 +2163,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
public function loadPreferences():Void
|
public function loadPreferences():Void
|
||||||
{
|
{
|
||||||
var save:Save = Save.get();
|
var save:Save = Save.instance;
|
||||||
|
|
||||||
if (previousWorkingFilePaths[0] == null)
|
if (previousWorkingFilePaths[0] == null)
|
||||||
{
|
{
|
||||||
|
@ -2191,7 +2191,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
public function writePreferences(hasBackup:Bool):Void
|
public function writePreferences(hasBackup:Bool):Void
|
||||||
{
|
{
|
||||||
var save:Save = Save.get();
|
var save:Save = Save.instance;
|
||||||
|
|
||||||
// Can't use filter() because of null safety checking!
|
// Can't use filter() because of null safety checking!
|
||||||
var filteredWorkingFilePaths:Array<String> = [];
|
var filteredWorkingFilePaths:Array<String> = [];
|
||||||
|
@ -5308,6 +5308,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
var startTimestamp:Float = 0;
|
var startTimestamp:Float = 0;
|
||||||
if (playtestStartTime) startTimestamp = scrollPositionInMs + playheadPositionInMs;
|
if (playtestStartTime) startTimestamp = scrollPositionInMs + playheadPositionInMs;
|
||||||
|
|
||||||
|
var playbackRate:Float = ((menubarItemPlaybackSpeed.value ?? 1.0) * 2.0) / 100.0;
|
||||||
|
playbackRate = Math.floor(playbackRate / 0.05) * 0.05; // Round to nearest 5%
|
||||||
|
playbackRate = Math.max(0.05, Math.min(2.0, playbackRate)); // Clamp to 5% to 200%
|
||||||
|
|
||||||
var targetSong:Song;
|
var targetSong:Song;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -5357,6 +5361,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
practiceMode: playtestPracticeMode,
|
practiceMode: playtestPracticeMode,
|
||||||
minimalMode: minimal,
|
minimalMode: minimal,
|
||||||
startTimestamp: startTimestamp,
|
startTimestamp: startTimestamp,
|
||||||
|
playbackRate: playbackRate,
|
||||||
overrideMusic: true,
|
overrideMusic: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -117,18 +117,32 @@ class ChartEditorDropdowns
|
||||||
"ugh" => "Ugh (Week 7)",
|
"ugh" => "Ugh (Week 7)",
|
||||||
"hehPrettyGood" => "Heh, Pretty Good (Week 7)",
|
"hehPrettyGood" => "Heh, Pretty Good (Week 7)",
|
||||||
// Weekend 1
|
// Weekend 1
|
||||||
"weekend-1-lightcan" => "Light Can (2hot)",
|
"weekend-1-punchhigh" => "Punch High (Blazin')",
|
||||||
"weekend-1-kickcan" => "Kick Can (2hot)",
|
"weekend-1-punchhighdodged" => "Punch High (Dodge) (Blazin')",
|
||||||
"weekend-1-kneecan" => "Knee Can (2hot)",
|
"weekend-1-punchhighblocked" => "Punch High (Block) (Blazin')",
|
||||||
"weekend-1-cockgun" => "Cock Gun (2hot)",
|
"weekend-1-punchhighspin" => "Punch High (Spin) (Blazin')",
|
||||||
"weekend-1-firegun" => "Fire Gun (2hot)",
|
"weekend-1-punchlow" => "Punch Low (Blazin')",
|
||||||
"weekend-1-punchlow" => "Punch Low (Blazin)",
|
"weekend-1-punchlowdodged" => "Punch Low (Dodge) (Blazin')",
|
||||||
"weekend-1-punchhigh" => "Punch High (Blazin)",
|
"weekend-1-punchlowblocked" => "Punch Low (Block) (Blazin')",
|
||||||
"weekend-1-punchlowblocked" => "Punch Low Blocked (Blazin)",
|
"weekend-1-punchlowspin" => "Punch High (Spin) (Blazin')",
|
||||||
"weekend-1-punchhighblocked" => "Punch High Blocked (Blazin)",
|
"weekend-1-picouppercutprep" => "Pico Uppercut (Prep) (Blazin')",
|
||||||
"weekend-1-dodgelow" => "Dodge Low (Blazin)",
|
"weekend-1-picouppercut" => "Pico Uppercut (Blazin')",
|
||||||
"weekend-1-blockhigh" => "Block High (Blazin)",
|
"weekend-1-blockhigh" => "Block High (Blazin')",
|
||||||
"weekend-1-fakeout" => "Fakeout (Blazin)",
|
"weekend-1-blocklow" => "Dodge High (Blazin')",
|
||||||
|
"weekend-1-blockspin" => "Block High (Spin) (Blazin')",
|
||||||
|
"weekend-1-dodgehigh" => "Block Low (Blazin')",
|
||||||
|
"weekend-1-dodgelow" => "Dodge Low (Blazin')",
|
||||||
|
"weekend-1-dodgespin" => "Dodge High (Spin) (Blazin')",
|
||||||
|
"weekend-1-hithigh" => "Hit High (Blazin')",
|
||||||
|
"weekend-1-hitlow" => "Hit Low (Blazin')",
|
||||||
|
"weekend-1-hitspin" => "Hit High (Spin) (Blazin')",
|
||||||
|
"weekend-1-darnelluppercutprep" => "Darnell Uppercut (Prep) (Blazin')",
|
||||||
|
"weekend-1-darnelluppercut" => "Darnell Uppercut (Blazin')",
|
||||||
|
"weekend-1-idle" => "Idle (Blazin')",
|
||||||
|
"weekend-1-fakeout" => "Fakeout (Blazin')",
|
||||||
|
"weekend-1-taunt" => "Taunt (If Fakeout) (Blazin')",
|
||||||
|
"weekend-1-tauntforce" => "Taunt (Forced) (Blazin')",
|
||||||
|
"weekend-1-reversefakeout" => "Fakeout (Reverse) (Blazin')",
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function populateDropdownWithNoteKinds(dropDown:DropDown, startingKindId:String):DropDownEntry
|
public static function populateDropdownWithNoteKinds(dropDown:DropDown, startingKindId:String):DropDownEntry
|
||||||
|
|
|
@ -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.instance.beatLengthMs * i);
|
var note:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault());
|
||||||
noteGrp.add(note);
|
noteGrp.add(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1001,7 +1001,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
var daSong = songs[curSelected];
|
var daSong = songs[curSelected];
|
||||||
if (daSong != null)
|
if (daSong != null)
|
||||||
{
|
{
|
||||||
var songScore:SaveScoreData = Save.get().getSongScore(songs[curSelected].songId, currentDifficulty);
|
var songScore:SaveScoreData = Save.instance.getSongScore(songs[curSelected].songId, currentDifficulty);
|
||||||
intendedScore = songScore?.score ?? 0;
|
intendedScore = songScore?.score ?? 0;
|
||||||
intendedCompletion = songScore?.accuracy ?? 0.0;
|
intendedCompletion = songScore?.accuracy ?? 0.0;
|
||||||
rememberedDifficulty = currentDifficulty;
|
rememberedDifficulty = currentDifficulty;
|
||||||
|
@ -1143,6 +1143,12 @@ class FreeplayState extends MusicBeatSubState
|
||||||
targetSong: targetSong,
|
targetSong: targetSong,
|
||||||
targetDifficulty: targetDifficulty,
|
targetDifficulty: targetDifficulty,
|
||||||
targetVariation: targetVariation,
|
targetVariation: targetVariation,
|
||||||
|
// TODO: Make this an option!
|
||||||
|
// startTimestamp: 0.0,
|
||||||
|
// TODO: Make this an option!
|
||||||
|
// playbackRate: 0.5,
|
||||||
|
practiceMode: false,
|
||||||
|
minimalMode: false,
|
||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1183,7 +1189,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
var daSongCapsule = grpCapsules.members[curSelected];
|
var daSongCapsule = grpCapsules.members[curSelected];
|
||||||
if (daSongCapsule.songData != null)
|
if (daSongCapsule.songData != null)
|
||||||
{
|
{
|
||||||
var songScore:SaveScoreData = Save.get().getSongScore(daSongCapsule.songData.songId, currentDifficulty);
|
var songScore:SaveScoreData = Save.instance.getSongScore(daSongCapsule.songData.songId, currentDifficulty);
|
||||||
intendedScore = songScore?.score ?? 0;
|
intendedScore = songScore?.score ?? 0;
|
||||||
intendedCompletion = songScore?.accuracy ?? 0.0;
|
intendedCompletion = songScore?.accuracy ?? 0.0;
|
||||||
diffIdsCurrent = daSongCapsule.songData.songDifficulties;
|
diffIdsCurrent = daSongCapsule.songData.songDifficulties;
|
||||||
|
|
|
@ -23,7 +23,7 @@ class ColorsMenu extends Page
|
||||||
|
|
||||||
for (i in 0...4)
|
for (i in 0...4)
|
||||||
{
|
{
|
||||||
var note:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault(), 0, i);
|
var note:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault(), i);
|
||||||
|
|
||||||
note.x = (100 * i) + i;
|
note.x = (100 * i) + i;
|
||||||
note.screenCenter(Y);
|
note.screenCenter(Y);
|
||||||
|
|
|
@ -649,7 +649,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
tracklistText.screenCenter(X);
|
tracklistText.screenCenter(X);
|
||||||
tracklistText.x -= FlxG.width * 0.35;
|
tracklistText.x -= FlxG.width * 0.35;
|
||||||
|
|
||||||
var levelScore:Null<SaveScoreData> = Save.get().getLevelScore(currentLevelId, currentDifficultyId);
|
var levelScore:Null<SaveScoreData> = Save.instance.getLevelScore(currentLevelId, currentDifficultyId);
|
||||||
highScore = levelScore?.score ?? 0;
|
highScore = levelScore?.score ?? 0;
|
||||||
// levelScore.accuracy
|
// levelScore.accuracy
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue