From 1f913154b4f6738d44a2975204c72c0e8bcd3580 Mon Sep 17 00:00:00 2001
From: TheZoroForce240 <86524550+TheZoroForce240@users.noreply.github.com>
Date: Sat, 25 Jul 2026 07:52:11 +0100
Subject: [PATCH 1/2] hardcoded scripts wip
---
modSource/scripts/import.hx | 84 ++
project.xml | 4 +
source/funkin/backend/scripting/HScript.hx | 17 +
.../backend/scripting/HardcodedScript.hx | 99 +++
.../scripting/HardcodedScriptPrinter.hx | 749 ++++++++++++++++++
.../scripting/HardcodedScriptRegistry.hx | 48 ++
source/funkin/backend/scripting/Script.hx | 30 +
source/funkin/backend/system/Flags.hx | 5 +
.../macros/HardcodedScriptRegistryMacro.hx | 54 ++
.../system/macros/HardcodedScriptVarMacro.hx | 137 ++++
source/funkin/backend/system/macros/Macros.hx | 5 +
source/funkin/game/PlayState.hx | 6 +
12 files changed, 1238 insertions(+)
create mode 100644 modSource/scripts/import.hx
create mode 100644 source/funkin/backend/scripting/HardcodedScript.hx
create mode 100644 source/funkin/backend/scripting/HardcodedScriptPrinter.hx
create mode 100644 source/funkin/backend/scripting/HardcodedScriptRegistry.hx
create mode 100644 source/funkin/backend/system/macros/HardcodedScriptRegistryMacro.hx
create mode 100644 source/funkin/backend/system/macros/HardcodedScriptVarMacro.hx
diff --git a/modSource/scripts/import.hx b/modSource/scripts/import.hx
new file mode 100644
index 0000000000..464b67d17c
--- /dev/null
+++ b/modSource/scripts/import.hx
@@ -0,0 +1,84 @@
+#if !macro
+package scripts;
+import funkin.backend.scripting.HardcodedScript;
+import funkin.backend.scripting.events.*;
+import funkin.backend.scripting.events.character.*;
+import funkin.backend.scripting.events.dialogue.*;
+import funkin.backend.scripting.events.discord.*;
+import funkin.backend.scripting.events.gameover.*;
+import funkin.backend.scripting.events.gameplay.*;
+import funkin.backend.scripting.events.healthicon.*;
+import funkin.backend.scripting.events.menu.*;
+import funkin.backend.scripting.events.note.*;
+import funkin.backend.scripting.events.shader.*;
+import funkin.backend.scripting.events.soundtray.*;
+import funkin.backend.scripting.events.splash.*;
+import funkin.backend.scripting.events.sprite.*;
+import funkin.backend.scripting.events.stage.*;
+
+import StringTools;
+import haxe.Json;
+import Xml;
+
+import openfl.utils.Assets;
+import lime.app.Application;
+import funkin.backend.system.Main;
+
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.FlxBasic;
+import flixel.FlxCamera;
+import flixel.tweens.FlxEase;
+import flixel.tweens.FlxTween;
+import flixel.sound.FlxSound;
+import flixel.system.FlxAssets;
+import flixel.math.FlxMath;
+import flixel.group.FlxGroup;
+import flixel.group.FlxGroup.FlxTypedGroup;
+import flixel.group.FlxSpriteGroup;
+import flixel.addons.text.FlxTypeText;
+import flixel.text.FlxText;
+import flixel.util.FlxTimer;
+import flixel.math.FlxPoint;
+import flixel.util.FlxAxes;
+import flixel.util.FlxColor;
+import funkin.backend.scripting.ModState;
+import funkin.backend.scripting.ModSubState;
+import funkin.game.PlayState;
+import funkin.game.GameOverSubstate;
+import funkin.game.HealthIcon;
+import funkin.game.HudCamera;
+import funkin.game.Note;
+import funkin.game.Strum;
+import funkin.game.StrumLine;
+import funkin.game.Character;
+import funkin.menus.PauseSubState;
+import funkin.menus.FreeplayState;
+import funkin.menus.MainMenuState;
+import funkin.menus.PauseSubState;
+import funkin.menus.StoryMenuState;
+import funkin.menus.TitleState;
+import funkin.options.Options;
+import funkin.backend.assets.Paths;
+import funkin.backend.system.Conductor;
+import funkin.backend.shaders.FunkinShader;
+import funkin.backend.shaders.CustomShader;
+import funkin.backend.FunkinText;
+import animate.FlxAnimate;
+import funkin.backend.FunkinSprite;
+import funkin.menus.ui.Alphabet;
+import funkin.backend.system.Flags;
+
+import funkin.backend.utils.CoolUtil;
+import funkin.backend.utils.IniUtil;
+import funkin.backend.utils.XMLUtil;
+#if sys
+import funkin.backend.utils.ZipUtil;
+#end
+import funkin.backend.utils.MarkdownUtil;
+import funkin.backend.utils.EngineUtil;
+import funkin.backend.utils.ThreadUtil;
+import funkin.backend.utils.MemoryUtil;
+import funkin.backend.utils.BitmapUtil;
+import funkin.backend.utils.TranslationUtil;
+#end
\ No newline at end of file
diff --git a/project.xml b/project.xml
index b129dbb377..9afdad34de 100644
--- a/project.xml
+++ b/project.xml
@@ -118,6 +118,9 @@
+
+
+
@@ -150,6 +153,7 @@
+
diff --git a/source/funkin/backend/scripting/HScript.hx b/source/funkin/backend/scripting/HScript.hx
index c3656478c6..4ad2e52a27 100644
--- a/source/funkin/backend/scripting/HScript.hx
+++ b/source/funkin/backend/scripting/HScript.hx
@@ -13,6 +13,10 @@ class HScript extends Script {
//public var folderlessPath:String;
var __importedPaths:Array;
+ #if HARDCODED_SCRIPTS
+ public var trackedSets:Array = [];
+ #end
+
public static function initParser() {
var parser = new Parser();
parser.allowJSON = parser.allowMetadata = parser.allowTypes = true;
@@ -146,6 +150,10 @@ class HScript extends Script {
if (expr != null) {
interp.execute(expr);
call("new", []);
+
+ #if HARDCODED_SCRIPTS
+ if (Flags.CONVERT_HSCRIPT_TO_SOURCE) HardcodedScriptPrinter.convertHscript(this);
+ #end
}
#if GLOBAL_SCRIPT
@@ -195,6 +203,15 @@ class HScript extends Script {
public override function set(val:String, value:Dynamic) {
interp.variables.set(val, value);
+ #if HARDCODED_SCRIPTS
+ if (Flags.CONVERT_HSCRIPT_TO_SOURCE) {
+ if (!trackedSets.contains(val)) {
+ if (!(value is Class)) {
+ trackedSets.push(val);
+ }
+ }
+ }
+ #end
}
public override function trace(v:Dynamic) {
diff --git a/source/funkin/backend/scripting/HardcodedScript.hx b/source/funkin/backend/scripting/HardcodedScript.hx
new file mode 100644
index 0000000000..79404cfef2
--- /dev/null
+++ b/source/funkin/backend/scripting/HardcodedScript.hx
@@ -0,0 +1,99 @@
+package funkin.backend.scripting;
+
+#if HARDCODED_SCRIPTS
+class HardcodedScript extends Script {
+ private var parent:T = null;
+ private var handleErrors:Bool = true;
+ private var disableAfterError:Bool = false;
+ private var publicVariables:Map = [];
+ private var dynamicVariables:Map = [];
+
+ public override function get(v:String) {
+ if (Reflect.getProperty(this, v) != null) return Reflect.getProperty(this, v);
+ if (Reflect.getProperty(parent, v) != null) return Reflect.getProperty(parent, v);
+ return dynamicVariables.get(v);
+ }
+ public override function set(v:String, v2:Dynamic) {
+ if (v2 is Class) return;
+
+ if (Reflect.getProperty(this, v) != null) {
+ Reflect.setProperty(this, v, v2);
+ return;
+ }
+ if (Reflect.getProperty(parent, v) != null) {
+ Reflect.setProperty(parent, v, v2);
+ return;
+ }
+ dynamicVariables.set(v, v2);
+ }
+
+ public function getDynamic(v:String) {
+ return dynamicVariables.get(v);
+ }
+ public function setDynamic(v:String, v2:Dynamic) {
+ dynamicVariables.set(v, v2);
+ }
+ public function getPublic(v:String) {
+ return publicVariables.get(v);
+ }
+ public function setPublic(v:String, v2:Dynamic) {
+ publicVariables.set(v, v2);
+ }
+ public function getStatic(v:String) {
+ return Script.staticVariables.get(v);
+ }
+ public function setStatic(v:String, v2:Dynamic) {
+ Script.staticVariables.set(v, v2);
+ }
+ /*public function importScript(path:String) {
+ //TODO
+ }*/
+
+ public override function setParent(variable:Dynamic) { parent = variable; }
+ public override function setPublicMap(map:Map) { this.publicVariables = map; }
+
+ public override function onCall(method:String, parameters:Array):Dynamic {
+ var func = Reflect.getProperty(this, method);
+ if (Reflect.isFunction(func)) {
+ if (handleErrors) {
+ var ret = null;
+ try {
+ ret = (parameters != null && parameters.length > 0) ? Reflect.callMethod(null, func, parameters) : func();
+ } catch(e) {
+ _errorHandler(e);
+ }
+ return ret;
+ }
+ return (parameters != null && parameters.length > 0) ? Reflect.callMethod(null, func, parameters) : func();
+ }
+ return null;
+ }
+ private function _errorHandler(error:haxe.Exception) {
+ var fileName = this.fileName;
+ for (item in error.stack) {
+ switch(item) {
+ case FilePos(s, file, line, col):
+ fileName = file + ":" + line;
+ default:
+ }
+ break;
+ }
+
+ Logs.traceColored([
+ Logs.logText(fileName + ': ', GREEN),
+ Logs.logText(error.toString(), RED)
+ ], ERROR);
+
+ if (disableAfterError) {
+ active = false;
+ }
+ }
+
+ public override function load() {
+ if (!didLoad) {
+ onCall("initVars", []); //initVars generated from HardcodedScriptVarMacro.hx
+ }
+ super.load();
+ }
+}
+#end
\ No newline at end of file
diff --git a/source/funkin/backend/scripting/HardcodedScriptPrinter.hx b/source/funkin/backend/scripting/HardcodedScriptPrinter.hx
new file mode 100644
index 0000000000..7bc77eadd9
--- /dev/null
+++ b/source/funkin/backend/scripting/HardcodedScriptPrinter.hx
@@ -0,0 +1,749 @@
+/*
+ * Copyright (C)2008-2017 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+package funkin.backend.scripting;
+#if HARDCODED_SCRIPTS
+
+import hscript.Tools;
+import hscript.Expr;
+import hscript.Printer;
+
+class HardcodedScriptPrinter {
+
+ var script:HScript;
+ var parentInstanceFields:Array = [];
+ var currentFuncParams:Array> = [];
+ var currentForParams:Array> = [];
+
+ var staticFields:Array = [];
+ var publicFields:Array = [];
+
+ var usedPublic:Array = [];
+ var usedPublicTypes:Array = [];
+ var usedStatic:Array = [];
+ var usedStaticTypes:Array = [];
+ var usedDynamic:Array = [];
+ var usedDynamicTypes:Array = [];
+
+ var imports:Array = [];
+ var usings:Array = [];
+ var funcStack:Int = 0;
+
+ var funcsToForcePublicOverride = [
+ "update", "draw", "destroy"
+ ];
+
+ var defaultFuncArgTypes:Map> = [
+ "preUpdate" => ["Float"],
+ "update" => ["Float"],
+ "postUpdate" => ["Float"],
+ "stepHit" => ["Int"],
+ "beatHit" => ["Int"],
+ "measureHit" => ["Int"],
+ "draw" => ["DrawEvent"],
+ "postDraw" => ["DrawEvent"],
+ "onResize" => ["ResizeEvent"],
+ "gameResized" => ["Int", "Int"],
+ "preStateCreate" => ["FlxState"],
+ "onDance" => ["DanceEvent"],
+ "onTryDance" => ["CancellableEvent"],
+ "onPlaySingAnim" => ["DirectionAnimEvent"],
+ "playSingAnimUnsafe" => ["DirectionAnimEvent"],
+ "onPlayAnim" => ["PlayAnimEvent"],
+ "onGetCamPos" => ["PointEvent"],
+ "onPreGenerateStrums" => ["AmountEvent"],
+ "onPostGenerateStrums" => ["AmountEvent"],
+ "onRatingUpdate" => ["RatingUpdateEvent"],
+ "onStartCountdown" => ["CancellableEvent"],
+ "onCountdown" => ["CountdownEvent"],
+ "onPostCountdown" => ["CountdownEvent"],
+ "onSubstateOpen" => ["StateEvent"],
+ "onSubstateClose" => ["StateEvent"],
+ "onStateSwitch" => ["StateEvent"],
+ "onGamePause" => ["CancellableEvent"],
+ "onCameraMove" => ["CamMoveEvent"],
+ "onEvent" => ["EventGameEvent"],
+ "onPostEvent" => ["EventGameEvent"],
+ "onGameOver" => ["GameOverEvent"],
+ "onPostGameOver" => ["GameOverEvent"],
+ "onSongEnd" => ["CancellableEvent"],
+ "onPlayerMiss" => ["NoteMissEvent"],
+ "onPostPlayerMiss" => ["NoteMissEvent"],
+ "onNoteHit" => ["NoteHitEvent"],
+ "onPlayerHit" => ["NoteHitEvent"],
+ "onDadHit" => ["NoteHitEvent"],
+ "onHealthIconAnimChange" => ["HealthIconChangeEvent"],
+ "onNoteCreation" => ["NoteCreationEvent"],
+ "onPostNoteCreation" => ["NoteCreationEvent"],
+ "onSplashShown" => ["SplashShowEvent"],
+ "onStageXMLParsed" => ["StageXMLEvent"],
+ "onStageNodeParsed" => ["StageNodeEvent"],
+ "onPostStageCreation" => ["StageXMLEvent"],
+ "onStageDestroy" => ["Stage"],
+ "onInputUpdate" => ["InputSystemEvent"],
+ "onStrumCreation" => ["StrumCreationEvent"],
+ "onPostStrumCreation" => ["StrumCreationEvent"]
+ ];
+
+ var buf : StringBuf;
+ var tabs : String;
+
+ public function new(script:HScript) {
+ this.script = script;
+ if (script != null && script.interp.scriptObject != null) {
+ parentInstanceFields = Type.getInstanceFields(Type.getClass(script.interp.scriptObject));
+ }
+ }
+
+ public function exprToString( e : Expr ):String {
+ buf = new StringBuf();
+ tabs = "";
+ expr(e);
+ return buf.toString();
+ }
+
+ public function typeToString( t : CType ):String {
+ buf = new StringBuf();
+ tabs = "";
+ type(t);
+ return buf.toString();
+ }
+
+ inline function add(s:T):Void buf.add(s);
+
+ function type( t : CType ):Void {
+ switch( t ) {
+ case CTOpt(t):
+ add('?');
+ type(t);
+ case CTPath(path, params):
+ add(path.join("."));
+ if( params != null ) {
+ add("<");
+ var first = true;
+ for( p in params ) {
+ if( first ) first = false else add(", ");
+ type(p);
+ }
+ add(">");
+ }
+ case CTNamed(name, t):
+ add(name);
+ add(':');
+ type(t);
+ case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))):
+ add('(');
+ for (a in args)
+ switch a {
+ case CTNamed(_, _): type(a);
+ default: type(CTNamed('_', a));
+ }
+ add(')->');
+ type(ret);
+ case CTFun(args, ret):
+ if( args.length == 0 )
+ add("Void -> ");
+ else {
+ for( a in args ) {
+ type(a);
+ add(" -> ");
+ }
+ }
+ type(ret);
+ case CTAnon(fields):
+ add("{");
+ var first = true;
+ for( f in fields ) {
+ if( first ) { first = false; add(" "); } else add(", ");
+ add(f.name + " : ");
+ type(f.t);
+ }
+ add(first ? "}" : " }");
+ case CTParent(t):
+ add("(");
+ type(t);
+ add(")");
+ case CTExpr(e):
+ expr(e);
+ }
+ }
+
+ function addType( t : CType ):Void {
+ if( t != null ) {
+ add(" : ");
+ type(t);
+ }
+ }
+
+ function expr( e : Expr ):Void {
+ if( e == null ) {
+ add("??NULL??");
+ return;
+ }
+ switch(Tools.expr(e)) {
+ case EPackage(n):
+ /*
+ add('package');
+ if(n != null)
+ add(' $n');
+ add(';\n');
+ */
+ case EImport(c, n, u):
+ //var str = '${u ? 'using' : 'import'} $c';
+ //if(n != null)
+ //str += ' as $n';
+
+ if (u) {
+ usings.push(c + (n != null ? ' as $n' : ''));
+ } else {
+ imports.push(c);
+ }
+
+ //add('${u ? 'using' : 'import'} $c');
+ //if(n != null)
+ //add(' as $n');
+ case EClass(name, fields, extend, interfaces, fnal):
+ /*
+ var isFinal = fnal != null && fnal;
+ if(isFinal)
+ add('final ');
+ add('class $name');
+ if (extend != null)
+ add(' extends $extend');
+ for(_interface in interfaces) {
+ add(' implements $_interface');
+ }
+ tabs += "\t";
+ add(" {\n");
+ for( e in fields ) {
+ add(tabs);
+ expr(e);
+ //add(";\n");
+ }
+ //for(field in fields) {
+ // expr(field);
+ //}
+
+ tabs = tabs.substr(1);
+ add("}");
+ */
+ case EEnum(en, _):
+ /*
+ add('enum ${en.name}');
+ if(en.fields.length == 0) {
+ add(' {}');
+ return;
+ }
+ tabs += "\t";
+ add(" {\n");
+
+ for(e in en.fields) {
+ add(tabs);
+ add(e.name);
+ if(e.args.length > 0) {
+ add("(");
+ var first = true;
+ for( a in e.args ) {
+ if( first ) first = false else add(", ");
+ if( a.opt ) add("?");
+ add(a.name);
+ addType(a.t);
+ }
+ add(')');
+ }
+ add(";\n");
+ }
+
+ tabs = tabs.substr(1);
+ add("}");
+ */
+ case ECast(e, t):
+ var safe = t != null;
+ add("cast ");
+ if(safe) add("(");
+ expr(e);
+ if(safe) {
+ add(", ");
+ addType(t);
+ add(")");
+ }
+ case ERegex(e, f):
+ add('~/$e/$f');
+ add(';\n');
+ case EConst(c):
+ switch( c ) {
+ case CInt(i): add(i);
+ case CFloat(f): add(f);
+ case CString(s): add('"'); add(s.split('"').join('\\"').split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")); add('"');
+ }
+ case EIdent(v):
+
+ var isLocal:Bool = false;
+ for (f in currentFuncParams) {
+ if (f.contains(v)) {isLocal = true; break;}
+ }
+ for (f in currentForParams) {
+ if (f.contains(v)) {isLocal = true; break;}
+ }
+
+ if (v == "this") v = "parent";
+
+ if (isLocal) {
+ add(v);
+ } else if (parentInstanceFields.contains(v) || parentInstanceFields.contains("get_" + v)) {
+ add("parent.");
+ add(v);
+ } else if (script.interp.publicVariables.exists(v)) {
+ add(v);
+ if (!usedPublic.contains(v)) {
+ usedPublic.push(v);
+ usedPublicTypes.push(getVarTypeName(script.interp.publicVariables.get(v)));
+ }
+ } else if (Script.staticVariables.exists(v)) {
+ add(v);
+ if (!usedStatic.contains(v)) {
+ usedStatic.push(v);
+ usedStaticTypes.push(getVarTypeName(Script.staticVariables.get(v)));
+ }
+ } else if (script.trackedSets.contains(v)) {
+ add(v);
+ if (!usedDynamic.contains(v)) {
+ usedDynamic.push(v);
+ usedDynamicTypes.push(getVarTypeName(script.get(v)));
+ }
+ } else {
+ add(v);
+ }
+ case EVar(n, t, e, p, s, pr, isFinal, isInline, get, set, _):
+ var name = n;
+ if (funcStack == 0) {
+ if (s) {
+ add("@:staticVar ");
+ staticFields.push(n);
+ } else if (p) {
+ add("@:publicVar ");
+ publicFields.push(n);
+ }
+
+ if(p) add(""); //disable
+ else if(pr) add("private ");
+ if(s) add(""); //disable
+ if(isInline) add("inline ");
+ }
+ if(isFinal) add("final " + name);
+ else add("var " + name);
+
+ if(get != null || set != null) {
+ add("(");
+ switch(get) {
+ case ADefault: add("default, ");
+ case ANull: add("null, ");
+ case AGet: add("get, ");
+ case ADynamic: add("dynamic, ");
+ case ANever: add("never, ");
+ default:
+ }
+ switch(set) {
+ case ADefault: add("default");
+ case ANull: add("null");
+ case ASet: add("set");
+ case ADynamic: add("dynamic");
+ case ANever: add("never");
+ default:
+ }
+ add(")");
+ }
+
+ addType(t);
+ if( e != null ) {
+ add(" = ");
+ expr(e);
+ }
+ case EParent(e):
+ add("("); expr(e); add(")");
+ case EBlock(el):
+ if( el.length == 0 ) {
+ add("{}");
+ } else {
+ tabs += "\t";
+ add("{\n");
+ for( e in el ) {
+ add(tabs);
+ expr(e);
+ add(";\n");
+ }
+ tabs = tabs.substr(1);
+ add(tabs);
+ add("}");
+ }
+ case EField(e, f, s):
+ expr(e);
+ add((s == true ? "?." : ".") + f);
+ case EBinop(op, e1, e2):
+ expr(e1);
+ add(" " + op + " ");
+ expr(e2);
+ case EUnop(op, pre, e):
+ if( pre ) {
+ add(op);
+ expr(e);
+ } else {
+ expr(e);
+ add(op);
+ }
+ case ECall(e, args):
+ if( e == null )
+ expr(e);
+ else switch( Tools.expr(e)) {
+ case EField(_), EIdent(_), EConst(_):
+ expr(e);
+ default:
+ add("(");
+ expr(e);
+ add(")");
+ }
+ add("(");
+ var first = true;
+ for( a in args ) {
+ if( first ) first = false else add(", ");
+ expr(a);
+ }
+ add(")");
+ case EIf(cond,e1,e2):
+ add("if( ");
+ expr(cond);
+ add(" ) ");
+ expr(e1);
+ if( e2 != null ) {
+ add(" else ");
+ expr(e2);
+ }
+ case EWhile(cond,e):
+ add("while( ");
+ expr(cond);
+ add(" ) ");
+ expr(e);
+ case EDoWhile(cond,e):
+ add("do ");
+ expr(e);
+ add(" while ( ");
+ expr(cond);
+ add(" )");
+ case EFor(v, it, e, ithv):
+
+ if(ithv != null) {
+ add("for( "+ithv+" => "+v+" in ");
+ currentForParams.push([ithv, v]);
+ } else {
+ add("for( "+v+" in ");
+ currentForParams.push([v]);
+ }
+ expr(it);
+ add(" ) ");
+ expr(e);
+ currentForParams.pop();
+ case EBreak:
+ add("break");
+ case EContinue:
+ add("continue");
+ case EFunction(params, e, n, ret, p, s, o, pr, isFinal, isInline):
+ var name = n;
+ if (funcStack == 0) {
+ if (s) {
+ add("@:staticVar ");
+ staticFields.push(n);
+ } else if (p) {
+ add("@:publicVar ");
+ publicFields.push(n);
+ }
+
+ if (funcsToForcePublicOverride.contains(n)) {
+ o = true;
+ p = true;
+ }
+
+ if(o) add("override ");
+ if (p) add("public ");
+ else if(pr) add("private ");
+ if(s) add(""); //disable
+ if(isInline) add("inline ");
+ if(isFinal) add("final ");
+ }
+ add("function");
+ if( name != null )
+ add(" " + name);
+ add("(");
+ var first = true;
+ var funcParamNames:Array = [];
+ for( i => a in params ) {
+ if( first ) first = false else add(", ");
+ if( a.opt ) add("?");
+ add(a.name);
+ trace(name + ":" + a.name + ":" + a.t);
+ if (a.t == null) {
+ if (defaultFuncArgTypes.exists(name)) {
+ var arr = defaultFuncArgTypes.get(name);
+ trace(arr);
+ if (i < arr.length) add(" : " + arr[i]);
+ }
+ } else {
+ addType(a.t);
+ }
+
+ funcParamNames.push(a.name);
+ }
+ currentFuncParams.push(funcParamNames);
+ add(")");
+ addType(ret);
+ add(" ");
+ funcStack++;
+ expr(e);
+ funcStack--;
+ currentFuncParams.pop();
+ case EReturn(e):
+ add("return");
+ if( e != null ) {
+ add(" ");
+ expr(e);
+ }
+ case EArray(e,index):
+ expr(e);
+ add("[");
+ expr(index);
+ add("]");
+ case EArrayDecl(el, _):
+ add("[");
+ var first = true;
+ for( e in el ) {
+ if( first ) first = false else add(", ");
+ expr(e);
+ }
+ add("]");
+ case ENew(cl, args, params):
+ add("new " + cl);
+ if(params != null) {
+ add("<");
+ var first = true;
+ for( p in params ) {
+ if( first ) first = false else add(", ");
+ type(p);
+ }
+ add(">");
+ }
+ add("(");
+ var first = true;
+ for( e in args ) {
+ if( first ) first = false else add(", ");
+ expr(e);
+ }
+ add(")");
+ case EThrow(e):
+ add("throw ");
+ expr(e);
+ case ETry(e, v, t, ecatch):
+ add("try ");
+ expr(e);
+ add(" catch( " + v);
+ addType(t);
+ add(") ");
+ expr(ecatch);
+ case EObject(fl):
+ if( fl.length == 0 ) {
+ add("{}");
+ } else {
+ tabs += "\t";
+ add("{\n");
+ for( f in fl ) {
+ add(tabs);
+ add(f.name+" : ");
+ expr(f.e);
+ add(",\n");
+ }
+ tabs = tabs.substr(1);
+ add(tabs);
+ add("}");
+ }
+ case ETernary(c,e1,e2):
+ expr(c);
+ add(" ? ");
+ expr(e1);
+ add(" : ");
+ expr(e2);
+ case ESwitch(e, cases, def):
+ add("switch( ");
+ expr(e);
+ add(") {\n");
+ tabs += "\t";
+ for( c in cases ) {
+ add(tabs);
+ add("case ");
+ var first = true;
+ for( v in c.values ) {
+ if( first ) first = false else add(", ");
+ expr(v);
+ }
+ add(": ");
+ expr(c.expr);
+ add(";\n");
+ }
+ if( def != null ) {
+ add(tabs);
+ add("default: ");
+ expr(def);
+ add(";\n");
+ }
+ tabs = tabs.substr(1);
+ add(tabs);
+ add("}");
+ case EMeta(name, args, e):
+ add("@");
+ add(name);
+ if( args != null && args.length > 0 ) {
+ add("(");
+ var first = true;
+ for( a in args ) {
+ if( first ) first = false else add(", ");
+ expr(e);
+ }
+ add(")");
+ }
+ add(" ");
+ expr(e);
+ case ECheckType(e, t):
+ add("(");
+ expr(e);
+ add(" : ");
+ addType(t);
+ add(")");
+ }
+ }
+
+ public static function errorToString( e : Error ):String {
+ var message = switch( #if hscriptPos e.e #else e #end ) {
+ case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")";
+ case EUnexpected(s): "Unexpected token: \""+s+"\"";
+ case EUnterminatedString: "Unterminated string";
+ case EUnterminatedComment: "Unterminated comment";
+ case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
+ case EUnknownVariable(v): "Unknown variable: "+v;
+ case EInvalidIterator(v): "Invalid iterator: "+v;
+ case EInvalidOp(op): "Invalid operator: "+op;
+ case EInvalidAccess(f): "Invalid access to field " + f;
+ case ECustom(msg): msg;
+ case EInvalidClass(cla): "Invalid class: " + cla + " was not found.";
+ case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.';
+ };
+ #if hscriptPos
+ return e.origin + ":" + e.line + ": " + message;
+ #else
+ return message;
+ #end
+ }
+
+ private function getVarTypeName(obj:Dynamic) {
+ switch(Type.typeof(obj)) {
+ case TBool:
+ return "Bool";
+ case TInt:
+ return "Int";
+ case TFloat:
+ return "Float";
+ case TFunction:
+ return "Dynamic"; //use dynamic for now
+ case TObject:
+ return "Dynamic";
+ case TEnum(c):
+ return Type.getEnumName(c);
+ case TClass(c):
+ return Type.getClassName(c);
+ default:
+ return Type.getClassName(Type.getClass(obj));
+ }
+ return "Dynamic";
+ }
+
+ public static function convertHscript(script:HScript) {
+ var printer = new HardcodedScriptPrinter(script);
+ var savePath = "scriptOutput/" + script.path.replace("assets/", "");
+ var str = printer.exprToString(script.expr);
+
+ var scriptClassName = "Script"; // + script.path.replace("assets/", "").replace("/", "_").replace(" ", "_").replace(".hx", "").replace(".", "");
+ var parentClassName:String = Type.getClassName(Type.getClass(script.interp.scriptObject));
+ if (parentClassName != null && parentClassName != "null" ) {
+ if (!printer.imports.contains(parentClassName)) printer.imports.push(parentClassName);
+ var splitPackage = parentClassName.split(".");
+ parentClassName = splitPackage[splitPackage.length-1];
+ } else {
+ parentClassName = "Dynamic";
+ }
+
+ var scriptPackageName = script.path.replace("assets/", "").replace(".hx", "");
+ scriptPackageName = scriptPackageName.substring(0, scriptPackageName.lastIndexOf("/"));
+ scriptPackageName = scriptPackageName.replace("/", ".").replace(" ", "_");
+
+ var redirects = Script.getDefaultImportRedirects();
+ var importBlacklist:Array = ["Int", "String", "Float"];
+
+ var headerStr = "package scripts." + scriptPackageName + ";\n\n";
+ for (imp in printer.imports) {
+ var i = imp;
+ if (importBlacklist.contains(imp)) continue;
+ if (redirects.exists(imp)) i = redirects.get(imp);
+ headerStr += "import " + i + ";\n";
+ }
+ for (imp in printer.usings) {
+ headerStr += "using " + imp + ";\n";
+ }
+ headerStr += "import funkin.backend.scripting.HardcodedScript;\n";
+ headerStr += "\n";
+ headerStr += '@:registerScript("' + script.path.replace("assets/", "") + '")\n';
+ headerStr += "class " + scriptClassName + " extends HardcodedScript<" + parentClassName + ">\n";
+
+ var extraClassCode:String = "";
+
+ for (i => field in printer.usedPublic) {
+ if (!printer.publicFields.contains(field)) {
+ if (extraClassCode == "") extraClassCode = "\n";
+ extraClassCode += "\t@:publicVarRef var " + field + ":" + printer.usedPublicTypes[i] + ";\n";
+ }
+ }
+ for (i => field in printer.usedStatic) {
+ if (!printer.staticFields.contains(field)) {
+ if (extraClassCode == "") extraClassCode = "\n";
+ extraClassCode += "\t@:staticVarRef var " + field + ":" + printer.usedStaticTypes[i] + ";\n";
+ }
+ }
+ for (i => field in printer.usedDynamic) {
+ if (extraClassCode == "") extraClassCode = "\n";
+ extraClassCode += "\t@:dynamicVarRef var " + field + ":" + printer.usedDynamicTypes[i] + ";\n";
+ }
+
+ str = str.replace("\t;\n", "");
+ if (str.charAt(0) == "{") {
+ str = "{" + extraClassCode + str.substring(1);
+ } else {
+ str = "{\n\t" + extraClassCode + str + "\n}";
+ }
+
+ CoolUtil.safeSaveFile(savePath, headerStr + str);
+ }
+}
+#end
\ No newline at end of file
diff --git a/source/funkin/backend/scripting/HardcodedScriptRegistry.hx b/source/funkin/backend/scripting/HardcodedScriptRegistry.hx
new file mode 100644
index 0000000000..8afdffb18a
--- /dev/null
+++ b/source/funkin/backend/scripting/HardcodedScriptRegistry.hx
@@ -0,0 +1,48 @@
+package funkin.backend.scripting;
+
+import haxe.io.Path;
+
+#if HARDCODED_SCRIPTS
+@:rtti
+@:build(funkin.backend.system.macros.HardcodedScriptRegistryMacro.build())
+class HardcodedScriptRegistry {
+
+ private static var data:Map = null;
+
+ public static function getRegisteredScripts():Map {
+ if (data == null) {
+ data = [];
+
+ var rtti = haxe.rtti.Rtti.getRtti(HardcodedScriptRegistry);
+ var registeredScripts:Array = [];
+ var registeredClasses:Array = [];
+ for (metadata in rtti.meta) {
+ if (metadata.name == "registeredScripts") {
+ registeredScripts = metadata.params;
+ } else if (metadata.name == "registeredClasses") {
+ registeredClasses = metadata.params;
+ }
+ }
+ for (i in 0...registeredScripts.length) {
+ var scriptPath = registeredScripts[i].replace("\"", "").trim();
+ var classPath = registeredClasses[i].replace("\"", "").trim();
+ if (scriptPath.endsWith(".hx")) scriptPath = Path.withoutExtension(scriptPath);
+ data.set(scriptPath, classPath);
+ }
+ }
+ return data;
+ }
+
+ public static function getFolderScripts(path:String):Array {
+ var registeredScripts = getRegisteredScripts();
+ var scripts:Array = [];
+ path = Path.addTrailingSlash(path);
+ for (key => val in registeredScripts) {
+ if (Path.addTrailingSlash(Path.directory(key)) == path) scripts.push(key);
+ }
+ return scripts;
+ }
+}
+#end
+
+
diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx
index 4a03275f2a..efdb50be98 100644
--- a/source/funkin/backend/scripting/Script.hx
+++ b/source/funkin/backend/scripting/Script.hx
@@ -215,6 +215,12 @@ class Script extends FlxBasic implements IFlxDestroyable {
* @param path Path in assets
*/
public static function create(path:String):Script {
+ #if HARDCODED_SCRIPTS
+ if (Flags.PREFER_HARDCODED_SCRIPTS) {
+ var script = tryHardcodedScript(path);
+ if (script != null) return script;
+ }
+ #end
if (Assets.exists(path)) {
return switch(Path.extension(path).toLowerCase()) {
case "hx" | "hscript" | "hsc" | "hxs":
@@ -229,9 +235,33 @@ class Script extends FlxBasic implements IFlxDestroyable {
new DummyScript(path);
}
}
+ #if HARDCODED_SCRIPTS
+ if (!Flags.PREFER_HARDCODED_SCRIPTS) {
+ var script = tryHardcodedScript(path);
+ if (script != null) return script;
+ }
+ #end
return new DummyScript(path);
}
+ #if HARDCODED_SCRIPTS
+ private static function tryHardcodedScript(path:String):Script {
+ var hardcodedScripts = HardcodedScriptRegistry.getRegisteredScripts();
+ var hardcodedPath = Path.withoutExtension(path).replace("assets/", "");
+ for (p in [hardcodedPath, hardcodedPath.replace("/LIB_assets", "")]) {
+ if (hardcodedScripts.exists(p)) {
+ var cl = Type.resolveClass(hardcodedScripts.get(p));
+ if (cl != null) {
+ return Type.createInstance(cl, [path]);
+ } else {
+ trace("hardcoded script class is null?", p, hardcodedScripts.get(p));
+ }
+ }
+ }
+ return null;
+ }
+ #end
+
/**
* Creates a script from the string. The language is determined based on the path.
* @param code code
diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx
index 37b74b92e9..960a9805d5 100644
--- a/source/funkin/backend/system/Flags.hx
+++ b/source/funkin/backend/system/Flags.hx
@@ -282,6 +282,11 @@ class Flags {
public static var DEFAULT_GLSL_VERSION:String = "120";
@:also(funkin.backend.utils.HttpUtil.userAgent)
public static var USER_AGENT:String = 'request';
+
+ #if HARDCODED_SCRIPTS
+ public static var CONVERT_HSCRIPT_TO_SOURCE:Bool = true;
+ public static var PREFER_HARDCODED_SCRIPTS:Bool = false;
+ #end
// -- End of Codename's Default Flags --
/**
diff --git a/source/funkin/backend/system/macros/HardcodedScriptRegistryMacro.hx b/source/funkin/backend/system/macros/HardcodedScriptRegistryMacro.hx
new file mode 100644
index 0000000000..8c62809420
--- /dev/null
+++ b/source/funkin/backend/system/macros/HardcodedScriptRegistryMacro.hx
@@ -0,0 +1,54 @@
+package funkin.backend.system.macros;
+
+#if HARDCODED_SCRIPTS
+#if macro
+import haxe.macro.Context;
+import haxe.macro.Expr;
+import haxe.macro.ExprTools;
+#end
+
+class HardcodedScriptRegistryMacro {
+ #if macro
+ public static function build() {
+ var fields = Context.getBuildFields();
+ Context.onGenerate(findRegisteredScripts);
+ return fields;
+ }
+
+ static function findRegisteredScripts(allTypes:Array) {
+ var registeredScripts:Array = [];
+ var registeredClasses:Array = [];
+ for (type in allTypes) {
+ switch (type) {
+ case TInst(cl,_):
+ var c = cl.get();
+ var meta = c.meta.get();
+ for (m in meta) {
+ if (m.name == ":registerScript") {
+ var classPath:String = c.module;
+ if (classPath.indexOf(".") != -1) {
+ classPath = classPath.substring(0, classPath.lastIndexOf(".")+1) + c.name;
+ }
+ registeredClasses.push(macro $v{classPath});
+ registeredScripts.push(m.params[0]);
+ }
+ }
+ default:
+ }
+ }
+
+ //store within metadata, which we can get at runtime
+ //based on how this lib does it: https://github.com/jasononeil/compiletime/blob/master/src/CompileTime.hx#L268
+ switch (Context.getType("funkin.backend.scripting.HardcodedScriptRegistry")) {
+ case TInst(cl, _):
+ var c = cl.get();
+ if (c.meta.has('registeredScripts')) c.meta.remove('registeredScripts');
+ c.meta.add('registeredScripts', registeredScripts, Context.currentPos());
+ if (c.meta.has('registeredClasses')) c.meta.remove('registeredClasses');
+ c.meta.add('registeredClasses', registeredClasses, Context.currentPos());
+ default:
+ }
+ }
+ #end
+}
+#end
\ No newline at end of file
diff --git a/source/funkin/backend/system/macros/HardcodedScriptVarMacro.hx b/source/funkin/backend/system/macros/HardcodedScriptVarMacro.hx
new file mode 100644
index 0000000000..613e734559
--- /dev/null
+++ b/source/funkin/backend/system/macros/HardcodedScriptVarMacro.hx
@@ -0,0 +1,137 @@
+package funkin.backend.system.macros;
+
+import haxe.macro.TypeTools;
+#if HARDCODED_SCRIPTS
+#if macro
+import haxe.macro.Context;
+import haxe.macro.Expr;
+import haxe.macro.ExprTools;
+using haxe.macro.Tools;
+#end
+
+class HardcodedScriptVarMacro {
+
+ #if macro
+
+ public static function build()
+ {
+ var fields = Context.getBuildFields();
+
+ var newFields:Array = [];
+
+ var varInits:Array = [];
+
+ for (field in fields) {
+ for (meta in field.meta) {
+
+ if (meta.name == ":publicVar" || meta.name == ":publicVarRef" || meta.name == ":staticVar" || meta.name == ":staticVarRef" || meta.name == ":dynamicVar" || meta.name == ":dynamicVarRef") {
+ var name:String = field.name;
+ var ret:Null;
+ switch(field.kind) {
+ case FVar(t, e):
+ if (t != null) {
+ ret = t;
+ field.kind = FieldType.FProp("get", "set", t);
+ }
+ if (meta.name == ":publicVar" && e != null) {
+ varInits.push(macro {setPublic($v{name}, ${e});});
+ } else if (meta.name == ":staticVar" && e != null) {
+ varInits.push(macro {setStatic($v{name}, ${e});});
+ } else if (meta.name == ":dynamicVar" && e != null) {
+ varInits.push(macro {setDynamic($v{name}, ${e});});
+ }
+ case FFun(f):
+ field.name = "__" + field.name;
+ var argTypes:Array = [];
+ for (a in f.args) {
+ argTypes.push(a.type);
+ }
+ ret = macro : Dynamic; //just do dynamic for now, should still work
+ //ret = TFunction(argTypes, f.ret);
+ //ret = TypeTools.toComplexType()
+ newFields.push({
+ name: name,
+ access: [Access.APrivate],
+ kind: FieldType.FProp("get", "set", ret),
+ pos: Context.currentPos(),
+ });
+
+ if (meta.name == ":publicVar") {
+ varInits.push(macro {setPublic($v{name}, $i{field.name});});
+ } else if (meta.name == ":staticVar") {
+ varInits.push(macro {setStatic($v{name}, $i{field.name});});
+ } else if (meta.name == ":dynamicVar") {
+ varInits.push(macro {setDynamic($v{name}, $i{field.name});});
+ }
+ default:
+ }
+
+ var getFuncExpr:Expr = null;
+ var setFuncExpr:Expr = null;
+
+ if (meta.name == ":publicVar" || meta.name == ":publicVarRef") {
+ getFuncExpr = macro return getPublic($v{name});
+ setFuncExpr = macro {
+ setPublic($v{name}, value);
+ return value;
+ };
+ } else if (meta.name == ":staticVar" || meta.name == ":staticVarRef") {
+ getFuncExpr = macro return getStatic($v{name});
+ setFuncExpr = macro {
+ setStatic($v{name}, value);
+ return value;
+ };
+ } else if (meta.name == ":dynamicVar" || meta.name == ":dynamicVarRef") {
+ getFuncExpr = macro return getDynamic($v{name});
+ setFuncExpr = macro {
+ setDynamic($v{name}, value);
+ return value;
+ };
+ }
+
+ var getterField:Field = {
+ name: "get_" + name,
+ access: [Access.APrivate, Access.AInline],
+ kind: FieldType.FFun({
+ expr: getFuncExpr,
+ ret: ret,
+ args: [],
+ }),
+ pos: Context.currentPos(),
+ };
+ var setterField:Field = {
+ name: "set_" + name,
+ access: [Access.APrivate, Access.AInline],
+ kind: FieldType.FFun({
+ expr: setFuncExpr,
+ ret: ret,
+ args: [{name: "value", type: ret}]
+ }),
+ pos: Context.currentPos(),
+ };
+ newFields.push(getterField);
+ newFields.push(setterField);
+ }
+ }
+ }
+ for (field in newFields) fields.push(field);
+
+ fields.push({
+ pos: Context.currentPos(),
+ name: "initVars",
+ kind: FFun({
+ args: [],
+ expr: {
+ pos: Context.currentPos(),
+ expr: EBlock(varInits)
+ }
+ }),
+ access: [Access.APrivate]
+ });
+
+ return fields;
+ }
+
+ #end
+}
+#end
\ No newline at end of file
diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx
index 801eda0c17..823f77190c 100644
--- a/source/funkin/backend/system/macros/Macros.hx
+++ b/source/funkin/backend/system/macros/Macros.hx
@@ -22,6 +22,7 @@ class Macros {
#if THREE_D_SUPPORT "away3d", "flx3d", #end
#if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end
#if NAPE_ENABLED "nape", "flixel.addons.nape", #end
+ #if HARDCODED_SCRIPTS "scripts", #end
// BASE HAXE
"DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting", "animate"
])
@@ -63,6 +64,10 @@ class Macros {
if (Context.defined("hscript_improved") && !Context.defined("hscript")) {
Compiler.define('hscript');
}
+
+ #if HARDCODED_SCRIPTS
+ Compiler.addGlobalMetadata('scripts', '@:build(funkin.backend.system.macros.HardcodedScriptVarMacro.build())');
+ #end
}
public static function buildLimeAssetLibrary():Array {
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index b4e7a29099..fa4f8bf7b2 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -750,6 +750,12 @@ class PlayState extends MusicBeatState
addScript(file);
}
+
+ #if HARDCODED_SCRIPTS
+ for (script in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts(folder)) {
+ scripts.add(Script.create("assets/" + script));
+ }
+ #end
}
var songEvents:Array = [];
From 59015d6149e7b7e7c783c0a0bda7a22d7ddab54b Mon Sep 17 00:00:00 2001
From: TheZoroForce240 <86524550+TheZoroForce240@users.noreply.github.com>
Date: Sun, 26 Jul 2026 11:03:23 +0100
Subject: [PATCH 2/2] setup event and note type loading for hardcoded scripts
---
.../scripting/HardcodedScriptPrinter.hx | 2 --
.../scripting/HardcodedScriptRegistry.hx | 6 +++++
.../editors/charter/CharterNoteTypesList.hx | 6 +++++
source/funkin/game/PlayState.hx | 27 ++++++++++++++-----
4 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/source/funkin/backend/scripting/HardcodedScriptPrinter.hx b/source/funkin/backend/scripting/HardcodedScriptPrinter.hx
index 7bc77eadd9..6d1ec73dc0 100644
--- a/source/funkin/backend/scripting/HardcodedScriptPrinter.hx
+++ b/source/funkin/backend/scripting/HardcodedScriptPrinter.hx
@@ -497,11 +497,9 @@ class HardcodedScriptPrinter {
if( first ) first = false else add(", ");
if( a.opt ) add("?");
add(a.name);
- trace(name + ":" + a.name + ":" + a.t);
if (a.t == null) {
if (defaultFuncArgTypes.exists(name)) {
var arr = defaultFuncArgTypes.get(name);
- trace(arr);
if (i < arr.length) add(" : " + arr[i]);
}
} else {
diff --git a/source/funkin/backend/scripting/HardcodedScriptRegistry.hx b/source/funkin/backend/scripting/HardcodedScriptRegistry.hx
index 8afdffb18a..56734ddab9 100644
--- a/source/funkin/backend/scripting/HardcodedScriptRegistry.hx
+++ b/source/funkin/backend/scripting/HardcodedScriptRegistry.hx
@@ -42,6 +42,12 @@ class HardcodedScriptRegistry {
}
return scripts;
}
+
+ public static function exists(path:String) {
+ var registeredScripts = getRegisteredScripts();
+ if (path.startsWith("assets/")) path = path.replace("assets/", "");
+ return registeredScripts.exists(path);
+ }
}
#end
diff --git a/source/funkin/editors/charter/CharterNoteTypesList.hx b/source/funkin/editors/charter/CharterNoteTypesList.hx
index d9a2b66f30..75e1181ae3 100644
--- a/source/funkin/editors/charter/CharterNoteTypesList.hx
+++ b/source/funkin/editors/charter/CharterNoteTypesList.hx
@@ -55,6 +55,12 @@ class CharterNoteTypesList extends UISubstateWindow {
var file:String = Path.withoutDirectory(path);
list.pushOnce(file);
}
+ #if HARDCODED_SCRIPTS
+ for (path in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts(pathString)) {
+ var file:String = Path.withoutDirectory(path);
+ list.pushOnce(file);
+ }
+ #end
return list;
}
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index c4fb163b7c..d73225ceac 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -752,8 +752,10 @@ class PlayState extends MusicBeatState
}
#if HARDCODED_SCRIPTS
- for (script in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts(folder)) {
- scripts.add(Script.create("assets/" + script));
+ for (file in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts(folder)) {
+ if (!scripts.contains("assets/" + file)) {
+ scripts.add(Script.create("assets/" + file));
+ }
}
#end
}
@@ -767,6 +769,15 @@ class PlayState extends MusicBeatState
addScript(file);
}
}
+
+ #if HARDCODED_SCRIPTS
+ for (file in funkin.backend.scripting.HardcodedScriptRegistry.getFolderScripts('data/events/')) {
+ var fileName:String = CoolUtil.getFilename(file);
+ if (EventsData.eventsList.contains(fileName) && songEvents.contains(fileName) && !scripts.contains("assets/" + file)) {
+ scripts.add(Script.create("assets/" + file));
+ }
+ }
+ #end
}
}
@@ -792,11 +803,13 @@ class PlayState extends MusicBeatState
for(noteType in SONG.noteTypes) {
var scriptPath = Paths.script('data/notes/${noteType}');
- if (Assets.exists(scriptPath) && !scripts.contains(scriptPath)) {
- var script = Script.create(scriptPath);
- if (!(script is DummyScript)) {
- scripts.add(script);
- script.load();
+ if (!scripts.contains(scriptPath)) {
+ if (Assets.exists(scriptPath) #if HARDCODED_SCRIPTS || funkin.backend.scripting.HardcodedScriptRegistry.exists(scriptPath) #end) {
+ var script = Script.create(scriptPath);
+ if (!(script is DummyScript)) {
+ scripts.add(script);
+ script.load();
+ }
}
}
}