Files
CodenameEngine/source/funkin/system/MusicBeatState.hx
T
2023-04-08 02:50:26 +02:00

263 lines
6.8 KiB
Haxe

package funkin.system;
import flixel.addons.transition.FlxTransitionableState;
import funkin.system.framerate.Framerate;
import funkin.scripting.DummyScript;
import flixel.FlxState;
import flixel.FlxSubState;
import funkin.scripting.events.*;
import funkin.scripting.Script;
import funkin.interfaces.IBeatReceiver;
import funkin.system.Conductor;
import funkin.options.PlayerSettings;
class MusicBeatState extends FlxTransitionableState implements IBeatReceiver
{
private var lastBeat:Float = 0;
private var lastStep:Float = 0;
/**
* Dummy sprite used to cache graphics to GPU.
*/
public var graphicCache:GraphicCacheSprite = new GraphicCacheSprite();
/**
* Whenever the Conductor auto update should be enabled or not.
*/
public var cancelConductorUpdate:Bool = false;
/**
* Current step
*/
public var curStep(get, never):Int;
/**
* Current beat
*/
public var curBeat(get, never):Int;
/**
* Current beat
*/
public var curMeasure(get, never):Int;
/**
* Current step, as a `Float` (ex: 4.94, instead of 4)
*/
public var curStepFloat(get, never):Float;
/**
* Current beat, as a `Float` (ex: 1.24, instead of 1)
*/
public var curBeatFloat(get, never):Float;
/**
* Current beat, as a `Float` (ex: 1.24, instead of 1)
*/
public var curMeasureFloat(get, never):Float;
/**
* Current song position (in milliseconds).
*/
public var songPos(get, never):Float;
inline function get_curStep():Int
return Conductor.curStep;
inline function get_curBeat():Int
return Conductor.curBeat;
inline function get_curMeasure():Int
return Conductor.curMeasure;
inline function get_curStepFloat():Float
return Conductor.curStepFloat;
inline function get_curBeatFloat():Float
return Conductor.curBeatFloat;
inline function get_curMeasureFloat():Float
return Conductor.curMeasureFloat;
inline function get_songPos():Float
return Conductor.songPosition;
/**
* Game Controls. (All players / Solo)
*/
public var controls(get, never):Controls;
/**
* Game Controls (Player 1 only)
*/
public var controlsP1(get, never):Controls;
/**
* Game Controls (Player 2 only)
*/
public var controlsP2(get, never):Controls;
/**
* Current injected script attached to the state. To add one, create a file at path "data/states/stateName" (ex: data/states/FreeplayState)
*/
public var stateScript:Script;
public var scriptsAllowed:Bool = true;
public var scriptName:String = null;
inline function get_controls():Controls
return PlayerSettings.solo.controls;
inline function get_controlsP1():Controls
return PlayerSettings.player1.controls;
inline function get_controlsP2():Controls
return PlayerSettings.player2.controls;
public function new(scriptsAllowed:Bool = true, ?scriptName:String) {
super();
this.scriptsAllowed = #if SOFTCODED_STATES scriptsAllowed #else false #end;
this.scriptName = scriptName;
}
function loadScript() {
if (scriptsAllowed) {
if (stateScript == null || stateScript is DummyScript) {
var className = Type.getClassName(Type.getClass(this));
var scriptName = this.scriptName != null ? this.scriptName : className.substr(className.lastIndexOf(".")+1);
stateScript = Script.create(Paths.script('data/states/${scriptName}'));
stateScript.setParent(this);
stateScript.load();
} else
stateScript.reload();
}
}
public override function tryUpdate(elapsed:Float):Void
{
if (persistentUpdate || subState == null) {
call("preUpdate", [elapsed]);
update(elapsed);
call("postUpdate", [elapsed]);
}
if (_requestSubStateReset)
{
_requestSubStateReset = false;
resetSubState();
}
if (subState != null)
{
subState.tryUpdate(elapsed);
}
}
override function create()
{
loadScript();
Framerate.offset.y = 0;
super.create();
call("create");
}
public override function createPost() {
super.createPost();
persistentUpdate = true;
call("postCreate");
}
public function call(name:String, ?args:Array<Dynamic>, ?defaultVal:Dynamic):Dynamic {
// calls the function on the assigned script
if (stateScript == null) return defaultVal;
return stateScript.call(name, args);
}
public function event<T:CancellableEvent>(name:String, event:T):T {
if (stateScript == null) return event;
stateScript.call(name, [event]);
return event;
}
override function update(elapsed:Float)
{
// TODO: DEBUG MODE!!
if (FlxG.keys.justPressed.F5) {
loadScript();
if (stateScript != null && !(stateScript is DummyScript))
Logs.trace('State script successfully reloaded', WARNING, GREEN);
}
call("update", [elapsed]);
super.update(elapsed);
}
@:dox(hide) public function stepHit(curStep:Int):Void
{
for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).stepHit(curStep);
call("stepHit", [curStep]);
}
@:dox(hide) public function beatHit(curBeat:Int):Void
{
for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).beatHit(curBeat);
call("beatHit", [curBeat]);
}
@:dox(hide) public function measureHit(curMeasure:Int):Void
{
for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).measureHit(curMeasure);
call("measureHit", [curMeasure]);
}
/**
* Shortcut to `FlxMath.lerp` or `CoolUtil.lerp`, depending on `fpsSensitive`
* @param v1 Value 1
* @param v2 Value 2
* @param ratio Ratio
* @param fpsSensitive Whenever the ratio should not be adjusted to run at the same speed independent of framerate.
*/
public function lerp(v1:Float, v2:Float, ratio:Float, fpsSensitive:Bool = false) {
if (fpsSensitive)
return FlxMath.lerp(v1, v2, ratio);
else
return CoolUtil.fpsLerp(v1, v2, ratio);
}
/**
* SCRIPTING STUFF
*/
public override function openSubState(subState:FlxSubState) {
var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState));
if (!e.cancelled)
super.openSubState(subState);
}
public override function onResize(w:Int, h:Int) {
super.onResize(w, h);
event("onResize", EventManager.get(ResizeEvent).recycle(w, h, null, null));
}
public override function destroy() {
super.destroy();
graphicCache.destroy();
call("onDestroy");
if (stateScript != null)
stateScript.destroy();
}
public override function draw() {
graphicCache.draw();
super.draw();
}
public override function switchTo(nextState:FlxState) {
var e = event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState));
if (e.cancelled)
return false;
return super.switchTo(nextState);
}
public override function onFocus() {
super.onFocus();
call("onFocus");
}
public override function onFocusLost() {
super.onFocusLost();
call("onFocusLost");
}
public override function resetSubState() {
super.resetSubState();
if (subState != null && subState is MusicBeatSubstate) {
cast(subState, MusicBeatSubstate).parent = this;
cast(subState, MusicBeatSubstate).onSubstateOpen();
}
}
}