diff --git a/buildnumber.txt b/buildnumber.txt index a78b2873..a570a3f2 100644 --- a/buildnumber.txt +++ b/buildnumber.txt @@ -1 +1 @@ -1360 \ No newline at end of file +1374 \ No newline at end of file diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 8bef4b92..ce4354a3 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -265,8 +265,10 @@ class PlayState extends MusicBeatState } else { var gfVersion = SONG.gf; if (gfVersion == null) gfVersion = "gf"; - gf = new Character(400, 130, gfVersion); - gf.scrollFactor.set(0.95, 0.95); + if (gfVersion != "none") { + gf = new Character(400, 130, gfVersion); + gf.scrollFactor.set(0.95, 0.95); + } } var camPos:FlxPoint = new FlxPoint(dad.getGraphicMidpoint().x, dad.getGraphicMidpoint().y); @@ -454,13 +456,10 @@ class PlayState extends MusicBeatState startTimer = new FlxTimer().start(Conductor.crochet / 1000, function(tmr:FlxTimer) { - dad.dance(); - gf.dance(); - boyfriend.dance(); - - countdown(swagCounter); - - swagCounter += 1; + for(char in [dad, gf, boyfriend]) + if (char != null) + char.dance(); + countdown(swagCounter++); }, introLength); } @@ -1223,7 +1222,7 @@ class PlayState extends MusicBeatState if (event.cancelled) return; health += event.healthGain; - if (combo > 5 && gf.animOffsets.exists('sad')) + if (gf != null && combo > 5 && gf.animOffsets.exists('sad')) { gf.playAnim('sad'); } @@ -1441,7 +1440,7 @@ class PlayState extends MusicBeatState iconP1.updateHitbox(); iconP2.updateHitbox(); - if (curBeat % gfSpeed == 0) + if (gf != null && curBeat % gfSpeed == 0) { gf.dance(); } diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx index 0d883612..12d74953 100644 --- a/source/funkin/game/Stage.hx +++ b/source/funkin/game/Stage.hx @@ -102,12 +102,12 @@ class Stage extends FlxBasic implements IBeatReceiver { state.add(spr); spr; case "boyfriend" | "bf": - if (PlayState.instance == null) continue; + if (PlayState.instance == null || PlayState.instance.boyfriend == null) continue; doCharNodeShit(PlayState.instance.boyfriend, node); PlayState.instance.add(PlayState.instance.boyfriend); PlayState.instance.boyfriend; case "girlfriend" | "gf": - if (PlayState.instance == null) continue; + if (PlayState.instance == null || PlayState.instance.gf == null) continue; doCharNodeShit(PlayState.instance.gf, node); PlayState.instance.add(PlayState.instance.gf); PlayState.instance.gf; diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx index f35cc22a..622f9c82 100644 --- a/source/funkin/menus/MainMenuState.hx +++ b/source/funkin/menus/MainMenuState.hx @@ -227,6 +227,7 @@ class MainMenuState extends MusicBeatState } spr.updateHitbox(); + spr.centerOffsets(); }); } } diff --git a/source/funkin/mods/ModsAssetLibrary.hx b/source/funkin/mods/ModsAssetLibrary.hx index 72dae58f..7bdedc06 100644 --- a/source/funkin/mods/ModsAssetLibrary.hx +++ b/source/funkin/mods/ModsAssetLibrary.hx @@ -24,6 +24,7 @@ using StringTools; interface ModsAssetLibrary { public var prefix:String; + public var libName:String; #if MOD_SUPPORT public var _parsedAsset:String; diff --git a/source/funkin/mods/ModsFolderLibrary.hx b/source/funkin/mods/ModsFolderLibrary.hx index 790008b4..81bdc2ca 100644 --- a/source/funkin/mods/ModsFolderLibrary.hx +++ b/source/funkin/mods/ModsFolderLibrary.hx @@ -116,7 +116,6 @@ class ModsFolderLibrary extends AssetLibrary implements ModsAssetLibrary { public override function exists(asset:String, type:String):Bool { if(!__parseAsset(asset)) return false; - trace('$folderPath: $_parsedAsset: ${FileSystem.exists(getAssetPath())}'); return FileSystem.exists(getAssetPath()); } diff --git a/source/funkin/scripting/HScript.hx b/source/funkin/scripting/HScript.hx index 35fa206e..6b78a565 100644 --- a/source/funkin/scripting/HScript.hx +++ b/source/funkin/scripting/HScript.hx @@ -16,6 +16,7 @@ class HScript extends Script { var code:String = Assets.getText(path); var parser = new hscript.Parser(); parser.allowJSON = parser.allowMetadata = parser.allowTypes = true; + parser.preprocesorValues = Script.getDefaultPreprocessors(); interp.errorHandler = _errorHandler; interp.staticVariables = Script.staticVariables; interp.allowStaticVariables = interp.allowPublicVariables = true; diff --git a/source/funkin/scripting/ModSubState.hx b/source/funkin/scripting/ModSubState.hx new file mode 100644 index 00000000..60e498a9 --- /dev/null +++ b/source/funkin/scripting/ModSubState.hx @@ -0,0 +1,7 @@ +package funkin.scripting; + +class ModSubState extends MusicBeatSubstate { + public function new(scriptName:String) { + super(true, scriptName); + } +} \ No newline at end of file diff --git a/source/funkin/scripting/Script.hx b/source/funkin/scripting/Script.hx index fdb4e97f..2e9fef9e 100644 --- a/source/funkin/scripting/Script.hx +++ b/source/funkin/scripting/Script.hx @@ -1,5 +1,6 @@ package funkin.scripting; +import lime.app.Application; import flixel.util.FlxDestroyUtil.IFlxDestroyable; import flixel.FlxBasic; import haxe.io.Path; @@ -55,6 +56,7 @@ class Script extends FlxBasic implements IFlxDestroyable { name: "Codename Engine" }, "ModState" => funkin.scripting.ModState, + "ModSubState" => funkin.scripting.ModSubState, "PlayState" => funkin.game.PlayState, "GameOverSubstate" => funkin.game.GameOverSubstate, "Note" => funkin.game.Note, @@ -77,6 +79,25 @@ class Script extends FlxBasic implements IFlxDestroyable { "Alphabet" => funkin.ui.Alphabet, ]; } + public static function getDefaultPreprocessors():Map { + return [ + "CODENAME_ENGINE" => true, + "CODENAME_VER" => Application.current.meta.get('version'), + "CODENAME_BUILD" => funkin.macros.BuildCounterMacro.getBuildNumber(), + "desktop" => #if desktop true #else false #end, + "windows" => #if windows true #else false #end, + "linux" => #if linux true #else false #end, + "macos" => #if macos true #else false #end, + "android" => #if android true #else false #end, + "web" => #if web true #else false #end, + "debug" => #if debug true #else false #end, + "MOD_SUPPORT" => #if MOD_SUPPORT true #else false #end, + "GLOBAL_SCRIPT" => #if GLOBAL_SCRIPT true #else false #end, + "SOFTCODED_STATES" => #if SOFTCODED_STATES true #else false #end, + "USE_SOURCE_ASSETS" => #if USE_SOURCE_ASSETS true #else false #end, + "USE_ADAPTED_ASSETS" => #if USE_ADAPTED_ASSETS true #else false #end, + ]; + } /** * All available script extensions */ diff --git a/source/funkin/system/Conductor.hx b/source/funkin/system/Conductor.hx index fbdb141e..32379449 100644 --- a/source/funkin/system/Conductor.hx +++ b/source/funkin/system/Conductor.hx @@ -169,8 +169,8 @@ class Conductor if (FlxG.state is IBeatReceiver) { var state = FlxG.state; while(state != null) { - if (FlxG.state is IBeatReceiver) { - var st = cast(FlxG.state, IBeatReceiver); + if (state is IBeatReceiver && (state.subState == null || state.subState.persistentUpdate)) { + var st = cast(state, IBeatReceiver); st.stepHit(curStep); if (updateBeat) st.beatHit(curBeat); diff --git a/source/funkin/system/FramerateField.hx b/source/funkin/system/FramerateField.hx index a731c333..1ccc5195 100644 --- a/source/funkin/system/FramerateField.hx +++ b/source/funkin/system/FramerateField.hx @@ -1,5 +1,6 @@ package funkin.system; +import funkin.mods.ModsAssetLibrary; import lime.ui.KeyCode; import openfl.ui.Keyboard; import openfl.events.KeyboardEvent; @@ -71,14 +72,32 @@ class FramerateField extends TextField { text.push('CODENAME ENGINE ALPHA - BUILD ${funkin.macros.BuildCounterMacro.getBuildNumber()}'); #end if (debugMode) { - text.push('======'); + text.push('=== CONDUCTOR INFO ==='); text.push('Current Song Position: ${Conductor.songPosition} (${Conductor.curBeat} beats - ${Conductor.curStep} steps)'); text.push('Current BPM: ${Conductor.bpm}'); - text.push('======'); + text.push('=== SYSTEM INFO ==='); text.push('System: ${lime.system.System.platformLabel} ${lime.system.System.platformVersion}'); text.push('Objs in state: ${FlxG.state.members.length}'); text.push('Nb cameras: ${FlxG.cameras.list.length}'); text.push('Current state: ${Type.getClassName(Type.getClass(FlxG.state))}'); + text.push('=== ASSET LIBRARIES TREE ==='); + if (Paths.assetsTree == null) + text.push('Not initialized yet'); + else { + for(e in Paths.assetsTree.libraries) { + var l = e; + if (l is openfl.utils.AssetLibrary) { + var al = cast(l, openfl.utils.AssetLibrary); + @:privateAccess + if (al.__proxy != null) l = al.__proxy; + } + + if (l is ModsAssetLibrary) + text.push('${Type.getClassName(Type.getClass(l))} - ${cast(l, ModsAssetLibrary).libName} (${cast(l, ModsAssetLibrary).prefix})'); + else + text.push(Std.string(e)); + } + } } this.text = text.join("\n"); } diff --git a/source/funkin/system/MusicBeatSubstate.hx b/source/funkin/system/MusicBeatSubstate.hx index cb21884f..4a5622c9 100644 --- a/source/funkin/system/MusicBeatSubstate.hx +++ b/source/funkin/system/MusicBeatSubstate.hx @@ -1,5 +1,9 @@ package funkin.system; +import flixel.FlxState; +import funkin.scripting.events.*; +import funkin.scripting.Script; +import funkin.scripting.DummyScript; import funkin.interfaces.IBeatReceiver; import funkin.system.Conductor.BPMChangeEvent; import funkin.system.Conductor; @@ -10,11 +14,6 @@ import flixel.math.FlxMath; class MusicBeatSubstate extends FlxSubState implements IBeatReceiver { - public function new() - { - super(); - } - private var lastBeat:Float = 0; private var lastStep:Float = 0; @@ -51,6 +50,16 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver inline function get_songPos():Float return Conductor.songPosition; + + /** + * Current injected script attached to the state. To add one, create a file at path "data/states/stateName" (ex: "data/states/PauseMenuSubstate.hx") + */ + public var stateScript:Script; + + public var scriptsAllowed:Bool = true; + + public var scriptName:String = null; + /** * Game Controls. */ @@ -59,23 +68,102 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver inline function get_controls():Controls return PlayerSettings.player1.controls; + + public function new(scriptsAllowed:Bool = true, ?scriptName:String) { + super(); + this.scriptsAllowed = #if SOFTCODED_STATES scriptsAllowed #else false #end; + this.scriptName = scriptName; + loadScript(); + } + + 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 close() { + var event = event("onClose", new CancellableEvent()); + if (!event.cancelled) { + super.close(); + call("onClosePost"); + } + } + + override function create() + { + super.create(); + call("create"); + } + + public override function createPost() { + super.createPost(); + call("postCreate"); + } + public function call(name:String, ?args:Array, ?defaultVal:Dynamic):Dynamic { + // calls the function on the assigned script + if (stateScript == null) return defaultVal; + return stateScript.call(name, args); + } + + public function event(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 { - //do literally nothing neither dumbass + for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).stepHit(curStep); + call("stepHit", [curStep]); } @:dox(hide) public function beatHit(curBeat:Int):Void { - //do literally nothing dumbass + for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).beatHit(curBeat); + call("beatHit", [curBeat]); } - - /** + + /** * Shortcut to `FlxMath.lerp` or `CoolUtil.lerp`, depending on `fpsSensitive` * @param v1 Value 1 * @param v2 Value 2 @@ -88,4 +176,41 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver else return CoolUtil.fpsLerp(v1, v2, ratio); } + + /** + * SCRIPTING STUFF + */ + public override function openSubState(subState:FlxSubState) { + var e = event("onOpenSubState", new StateEvent(subState)); + if (!e.cancelled) + super.openSubState(subState); + } + + public override function onResize(w:Int, h:Int) { + super.onResize(w, h); + event("onResize", new ResizeEvent(w, h)); + } + + public override function destroy() { + super.destroy(); + call("onDestroy"); + stateScript.destroy(); + } + + public override function switchTo(nextState:FlxState) { + var e = event("onStateSwitch", new StateEvent(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"); + } }