From ba0660c2c0797afa1d146deb21e1b4c7f9acf51d Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 16 Jan 2024 08:58:31 -0500 Subject: [PATCH 01/18] ansi --- source/Main.hx | 2 + source/funkin/util/logging/AnsiTrace.hx | 120 ++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 source/funkin/util/logging/AnsiTrace.hx diff --git a/source/Main.hx b/source/Main.hx index 86e520e69..754d0732f 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -33,6 +33,8 @@ class Main extends Sprite public static function main():Void { + haxe.Log.trace = funkin.util.logging.AnsiTrace.trace; + funkin.util.logging.AnsiTrace.traceBF(); Lib.current.addChild(new Main()); } diff --git a/source/funkin/util/logging/AnsiTrace.hx b/source/funkin/util/logging/AnsiTrace.hx new file mode 100644 index 000000000..993fcd4b9 --- /dev/null +++ b/source/funkin/util/logging/AnsiTrace.hx @@ -0,0 +1,120 @@ +package funkin.util.logging; + +class AnsiTrace +{ + // mostly a copy of haxe.Log.trace() + // but adds nice cute ANSI things + public static function trace(v:Dynamic, ?info:haxe.PosInfos) + { + var str = formatOutput(v, info); + #if js + if (js.Syntax.typeof(untyped console) != "undefined" && (untyped console).log != null) (untyped console).log(str); + #elseif lua + untyped __define_feature__("use._hx_print", _hx_print(str)); + #elseif sys + Sys.println(str); + #else + throw new haxe.exceptions.NotImplementedException() + #end + } + + public static var colorSupported:Bool = (Sys.getEnv("TERM") == "xterm" || Sys.getEnv("ANSICON") != null); + + // ansi stuff + public static inline var RED = "\x1b[31m"; + public static inline var YELLOW = "\x1b[33m"; + public static inline var WHITE = "\x1b[37m"; + public static inline var NORMAL = "\x1b[0m"; + public static inline var BOLD = "\x1b[1m"; + public static inline var ITALIC = "\x1b[3m"; + + // where the real mf magic happens with ansi stuff! + public static function formatOutput(v:Dynamic, infos:haxe.PosInfos):String + { + var str = Std.string(v); + if (infos == null) return str; + + if (colorSupported) + { + var dirs:Array = infos.fileName.split("/"); + dirs[dirs.length - 1] = ansiWrap(dirs[dirs.length - 1], BOLD); + + // rejoin the dirs + infos.fileName = dirs.join("/"); + } + + var pstr = infos.fileName + ":" + ansiWrap(infos.lineNumber, BOLD); + if (infos.customParams != null) for (v in infos.customParams) + str += ", " + Std.string(v); + return pstr + ": " + str; + } + + public static function traceBF() + { + #if sys + if (colorSupported) Sys.println(ansiBF.join("\n")); + #end + } + + public static function ansiWrap(str:Dynamic, ansiCol:String) + { + return ansify(ansiCol) + str + ansify(NORMAL); + } + + public static function ansify(ansiCol:String) + { + return (colorSupported ? ansiCol : ""); + } + + // generated using https://dom111.github.io/image-to-ansi/ + public static var ansiBF:Array = [ + "\x1b[39m\x1b[49m \x1b[48;2;154;23;70m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;154;23;70m \x1b[48;2;184;46;83m \x1b[48;2;246;87;102m \x1b[48;2;239;83;100m \x1b[48;2;154;23;70m \x1b[48;2;154;23;69m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;154;23;70m \x1b[48;2;191;52;87m \x1b[48;2;246;87;102m \x1b[48;2;241;84;100m \x1b[48;2;191;52;87m \x1b[48;2;153;23;69m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;154;23;70m \x1b[48;2;246;87;102m \x1b[48;2;154;23;70m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;154;23;70m \x1b[48;2;246;87;102m \x1b[48;2;234;94;114m \x1b[48;2;160;97;151m \x1b[48;2;246;87;102m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;36;35;46m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;47;49;144m \x1b[48;2;246;87;102m \x1b[48;2;80;121;206m \x1b[48;2;193;167;177m \x1b[48;2;246;87;102m \x1b[48;2;184;46;83m \x1b[48;2;205;63;93m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;47;49;144m \x1b[48;2;110;187;236m \x1b[48;2;109;66;125m \x1b[48;2;246;87;102m \x1b[48;2;74;107;200m \x1b[48;2;141;248;252m \x1b[48;2;107;177;226m \x1b[48;2;234;94;114m \x1b[48;2;246;87;102m \x1b[48;2;237;81;99m \x1b[48;2;205;63;93m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;74;106;196m \x1b[48;2;87;133;210m \x1b[48;2;64;105;174m \x1b[48;2;141;248;252m \x1b[48;2;126;219;244m \x1b[48;2;57;65;148m \x1b[48;2;47;49;144m \x1b[48;2;141;248;252m \x1b[48;2;129;225;245m \x1b[48;2;157;94;147m \x1b[48;2;246;87;102m \x1b[48;2;159;27;72m \x1b[48;2;205;63;93m \x1b[48;2;55;27;43m \x1b[48;2;21;21;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;74;107;200m \x1b[48;2;125;216;244m \x1b[48;2;141;248;252m \x1b[48;2;126;219;244m \x1b[48;2;60;97;187m \x1b[48;2;141;248;252m \x1b[48;2;126;219;244m \x1b[48;2;104;173;229m \x1b[48;2;146;68;123m \x1b[48;2;246;87;102m \x1b[48;2;180;44;82m \x1b[48;2;205;63;93m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;74;107;200m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;104;173;229m \x1b[48;2;146;68;123m \x1b[48;2;246;87;102m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;73;106;199m \x1b[48;2;132;230;247m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;78;118;190m \x1b[48;2;239;83;100m \x1b[48;2;246;87;102m \x1b[48;2;205;63;93m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;73;106;199m \x1b[48;2;132;230;247m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;20;19;31m \x1b[48;2;110;187;236m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;78;118;190m \x1b[48;2;239;83;100m \x1b[48;2;246;87;102m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;73;106;199m \x1b[48;2;132;230;247m \x1b[48;2;110;187;236m \x1b[48;2;20;19;31m \x1b[48;2;110;187;236m \x1b[48;2;51;72;160m \x1b[48;2;246;87;102m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;74;107;200m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;117;138;166m \x1b[48;2;20;19;31m \x1b[48;2;110;187;236m \x1b[48;2;55;134;228m \x1b[48;2;110;187;236m \x1b[48;2;139;244;251m \x1b[48;2;205;63;93m \x1b[48;2;123;4;53m \x1b[48;2;125;6;54m \x1b[48;2;146;23;68m \x1b[48;2;205;63;93m \x1b[48;2;123;4;53m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;74;107;200m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;20;19;31m \x1b[48;2;103;130;185m \x1b[48;2;240;174;162m \x1b[48;2;74;107;200m \x1b[48;2;110;187;236m \x1b[48;2;141;248;252m \x1b[48;2;107;177;226m \x1b[48;2;74;107;200m \x1b[48;2;20;19;31m \x1b[48;2;205;63;93m \x1b[48;2;20;19;31m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;74;107;200m \x1b[48;2;110;187;236m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;58;123;219m \x1b[48;2;74;107;200m \x1b[48;2;20;19;31m \x1b[48;2;240;174;162m \x1b[48;2;141;248;252m \x1b[48;2;135;237;249m \x1b[48;2;157;140;181m \x1b[48;2;44;30;46m \x1b[48;2;20;19;31m \x1b[48;2;205;63;93m \x1b[48;2;36;35;46m \x1b[48;2;153;78;112m \x1b[48;2;249;225;202m \x1b[48;2;240;174;162m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;74;107;200m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;74;107;200m \x1b[48;2;20;19;31m \x1b[48;2;240;174;162m \x1b[48;2;93;37;66m \x1b[48;2;240;174;162m \x1b[48;2;74;107;200m \x1b[48;2;240;174;162m \x1b[48;2;130;96;96m \x1b[48;2;20;19;31m \x1b[48;2;240;174;162m \x1b[48;2;74;107;200m \x1b[48;2;141;248;252m \x1b[48;2;110;187;236m \x1b[48;2;20;19;31m \x1b[48;2;205;63;93m \x1b[48;2;170;35;77m \x1b[48;2;196;126;137m \x1b[48;2;249;225;202m \x1b[48;2;132;70;100m \x1b[48;2;20;19;31m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;73;106;199m \x1b[48;2;132;230;247m \x1b[48;2;138;242;250m \x1b[48;2;74;107;200m \x1b[49m \x1b[48;2;20;19;31m \x1b[48;2;240;174;162m \x1b[48;2;20;19;31m \x1b[48;2;175;111;124m \x1b[48;2;235;169;159m \x1b[48;2;240;174;162m \x1b[48;2;227;160;155m \x1b[48;2;20;19;31m \x1b[48;2;232;165;158m \x1b[48;2;240;174;162m \x1b[48;2;85;109;196m \x1b[48;2;138;242;250m \x1b[48;2;112;191;237m \x1b[48;2;104;181;235m \x1b[48;2;110;187;236m \x1b[48;2;23;22;43m \x1b[48;2;26;23;37m \x1b[48;2;249;225;202m \x1b[48;2;248;220;198m \x1b[48;2;249;225;202m \x1b[48;2;137;90;124m \x1b[48;2;51;112;205m \x1b[48;2;53;128;224m \x1b[48;2;23;25;44m \x1b[48;2;18;18;28m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;73;106;199m \x1b[48;2;109;182;229m \x1b[48;2;74;107;200m \x1b[49m \x1b[48;2;151;78;111m \x1b[48;2;194;126;136m \x1b[48;2;110;100;98m \x1b[48;2;244;194;178m \x1b[48;2;72;42;63m \x1b[48;2;103;76;81m \x1b[48;2;191;136;147m \x1b[48;2;240;174;162m \x1b[48;2;206;136;142m \x1b[48;2;20;19;31m \x1b[48;2;232;165;158m \x1b[48;2;240;174;162m \x1b[48;2;65;128;218m \x1b[48;2;141;248;252m \x1b[48;2;74;107;200m \x1b[48;2;85;133;200m \x1b[48;2;88;139;214m \x1b[48;2;84;69;150m \x1b[48;2;211;167;166m \x1b[48;2;187;116;132m \x1b[48;2;213;145;147m \x1b[48;2;245;205;186m \x1b[48;2;135;83;115m \x1b[48;2;43;66;124m \x1b[48;2;47;87;174m \x1b[48;2;51;130;227m \x1b[48;2;28;40;76m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;71;103;199m \x1b[48;2;73;105;197m \x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;196;126;137m \x1b[48;2;246;209;189m \x1b[48;2;249;225;202m \x1b[48;2;226;159;154m \x1b[48;2;244;215;196m \x1b[48;2;249;225;202m \x1b[48;2;240;174;162m \x1b[48;2;226;159;154m \x1b[48;2;142;54;93m \x1b[48;2;245;213;193m \x1b[48;2;249;225;202m \x1b[48;2;213;185;192m \x1b[48;2;85;132;211m \x1b[48;2;222;158;164m \x1b[48;2;183;111;129m \x1b[48;2;110;187;236m \x1b[48;2;171;158;211m \x1b[48;2;153;78;112m \x1b[48;2;196;126;137m \x1b[48;2;240;174;162m \x1b[48;2;166;93;120m \x1b[48;2;130;70;98m \x1b[48;2;19;19;31m \x1b[48;2;29;34;56m \x1b[48;2;55;93;183m \x1b[48;2;68;101;193m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;249;225;202m \x1b[48;2;196;126;137m \x1b[48;2;218;150;149m \x1b[48;2;249;225;202m \x1b[48;2;240;174;162m \x1b[48;2;196;126;137m \x1b[48;2;47;49;144m \x1b[48;2;196;126;137m \x1b[48;2;153;78;112m \x1b[49m \x1b[48;2;20;19;31m \x1b[48;2;41;43;121m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;249;225;202m \x1b[48;2;244;215;196m \x1b[48;2;249;225;202m \x1b[48;2;145;49;90m \x1b[48;2;249;225;202m \x1b[48;2;240;174;162m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;196;126;137m \x1b[48;2;249;225;202m \x1b[48;2;200;131;139m \x1b[48;2;249;225;202m \x1b[48;2;154;63;98m \x1b[48;2;145;49;90m \x1b[48;2;240;174;162m \x1b[48;2;249;225;202m \x1b[48;2;240;174;162m \x1b[48;2;153;78;112m \x1b[48;2;255;224;255m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;210;141;145m \x1b[48;2;241;181;168m \x1b[48;2;249;225;202m \x1b[48;2;196;126;137m \x1b[48;2;195;125;136m \x1b[48;2;170;94;119m \x1b[48;2;237;73;115m \x1b[48;2;244;75;120m \x1b[48;2;145;49;90m \x1b[48;2;249;225;202m \x1b[48;2;241;181;167m \x1b[48;2;181;121;161m \x1b[48;2;255;224;255m \x1b[48;2;178;117;159m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;136;72;102m \x1b[48;2;190;119;133m \x1b[48;2;171;99;123m \x1b[48;2;152;74;109m \x1b[48;2;244;75;120m \x1b[48;2;145;49;90m \x1b[48;2;190;119;133m \x1b[48;2;185;128;172m \x1b[48;2;180;121;164m \x1b[48;2;255;224;255m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;147;80;107m \x1b[48;2;153;78;112m \x1b[49m \x1b[48;2;36;35;46m \x1b[48;2;98;121;155m \x1b[48;2;50;68;111m \x1b[48;2;55;73;115m \x1b[48;2;36;35;46m \x1b[48;2;251;117;129m \x1b[48;2;205;63;93m \x1b[48;2;230;143;174m \x1b[48;2;255;224;255m \x1b[48;2;145;49;90m \x1b[48;2;153;78;112m \x1b[48;2;255;224;255m \x1b[48;2;251;219;252m \x1b[48;2;105;60;85m \x1b[48;2;36;35;46m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;156;82;114m \x1b[48;2;240;174;162m \x1b[48;2;153;78;112m \x1b[49m \x1b[48;2;65;84;125m \x1b[48;2;98;121;155m \x1b[48;2;124;146;175m \x1b[48;2;194;215;238m \x1b[48;2;50;68;111m \x1b[48;2;124;146;175m \x1b[48;2;98;121;155m \x1b[48;2;36;35;46m \x1b[48;2;254;224;245m \x1b[48;2;231;143;176m \x1b[48;2;255;224;255m \x1b[48;2;145;49;90m \x1b[48;2;255;224;255m \x1b[48;2;72;85;110m \x1b[48;2;240;174;162m \x1b[48;2;196;126;137m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;196;126;137m \x1b[48;2;250;227;206m \x1b[48;2;50;68;111m \x1b[48;2;65;84;125m \x1b[48;2;50;68;111m \x1b[48;2;124;146;175m \x1b[48;2;78;99;137m \x1b[48;2;194;215;238m \x1b[48;2;124;146;175m \x1b[48;2;36;35;46m \x1b[48;2;254;224;245m \x1b[48;2;253;170;192m \x1b[48;2;255;224;255m \x1b[48;2;251;117;129m \x1b[48;2;255;224;255m \x1b[48;2;170;105;144m \x1b[48;2;240;174;162m \x1b[48;2;196;126;137m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;76;111m \x1b[48;2;165;91;118m \x1b[48;2;180;108;128m \x1b[48;2;250;227;206m \x1b[48;2;116;138;169m \x1b[48;2;124;146;175m \x1b[48;2;36;35;46m \x1b[48;2;116;138;169m \x1b[48;2;172;193;218m \x1b[48;2;168;206;237m \x1b[48;2;49;62;121m \x1b[48;2;73;92;131m \x1b[48;2;115;137;168m \x1b[48;2;116;138;169m \x1b[48;2;124;146;175m \x1b[48;2;50;40;54m \x1b[48;2;57;43;58m \x1b[48;2;251;170;183m \x1b[48;2;255;206;227m \x1b[48;2;251;117;129m \x1b[48;2;252;132;140m \x1b[48;2;255;224;255m \x1b[48;2;153;78;112m \x1b[48;2;243;190;174m \x1b[48;2;249;226;203m \x1b[48;2;196;126;137m \x1b[48;2;44;70;156m \x1b[48;2;47;86;175m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;239;198;185m \x1b[48;2;250;227;206m \x1b[48;2;244;195;179m \x1b[48;2;250;227;206m \x1b[48;2;50;68;111m \x1b[48;2;166;188;213m \x1b[48;2;36;35;46m \x1b[48;2;46;58;91m \x1b[48;2;79;100;138m \x1b[48;2;71;79;97m \x1b[48;2;60;69;89m \x1b[48;2;136;158;188m \x1b[48;2;117;140;170m \x1b[48;2;36;35;46m \x1b[48;2;71;79;97m \x1b[48;2;79;100;138m \x1b[48;2;62;51;68m \x1b[48;2;246;192;224m \x1b[48;2;253;182;205m \x1b[48;2;255;224;255m \x1b[48;2;144;140;167m \x1b[48;2;82;52;72m \x1b[48;2;120;110;108m \x1b[48;2;250;227;206m \x1b[48;2;229;187;179m \x1b[48;2;169;166;186m \x1b[48;2;47;64;156m \x1b[48;2;46;87;175m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;250;227;206m \x1b[48;2;240;174;162m \x1b[48;2;220;168;164m \x1b[48;2;250;227;206m \x1b[48;2;50;68;111m \x1b[48;2;194;215;238m \x1b[48;2;36;35;46m \x1b[48;2;50;68;111m \x1b[48;2;98;121;155m \x1b[48;2;36;35;46m \x1b[48;2;50;68;111m \x1b[48;2;98;121;155m \x1b[48;2;95;115;147m \x1b[48;2;93;115;150m \x1b[48;2;50;68;111m \x1b[48;2;37;37;48m \x1b[48;2;51;44;59m \x1b[48;2;66;116;206m \x1b[48;2;47;83;172m \x1b[48;2;68;134;227m \x1b[48;2;250;227;206m \x1b[48;2;61;91;174m \x1b[48;2;47;85;173m \x1b[48;2;47;87;175m \x1b[48;2;47;86;175m \x1b[49m \x1b[m", + "\x1b[48;2;153;78;112m \x1b[48;2;250;227;206m \x1b[48;2;240;174;162m \x1b[48;2;217;165;163m \x1b[48;2;250;227;206m \x1b[48;2;240;174;162m \x1b[48;2;82;52;72m \x1b[48;2;194;215;238m \x1b[48;2;36;35;46m \x1b[48;2;50;68;111m \x1b[48;2;98;121;155m \x1b[48;2;36;35;46m \x1b[48;2;98;121;155m \x1b[48;2;50;68;111m \x1b[48;2;38;41;58m \x1b[48;2;47;87;175m \x1b[48;2;51;130;227m \x1b[48;2;47;87;175m \x1b[48;2;51;130;227m \x1b[48;2;47;49;144m \x1b[48;2;47;87;175m \x1b[48;2;45;85;174m \x1b[49m \x1b[m", + "\x1b[48;2;153;78;112m \x1b[48;2;250;227;206m \x1b[48;2;242;185;171m \x1b[48;2;250;227;206m \x1b[48;2;240;174;162m \x1b[48;2;217;165;163m \x1b[48;2;250;227;206m \x1b[48;2;240;174;162m \x1b[48;2;36;35;46m \x1b[48;2;124;146;175m \x1b[48;2;36;35;46m \x1b[48;2;50;68;111m \x1b[48;2;98;121;155m \x1b[48;2;50;68;111m \x1b[48;2;36;35;46m \x1b[48;2;98;121;155m \x1b[48;2;36;35;46m \x1b[48;2;38;41;58m \x1b[48;2;47;68;159m \x1b[48;2;47;87;175m \x1b[48;2;51;130;227m \x1b[48;2;47;87;175m \x1b[48;2;51;130;227m \x1b[48;2;47;87;175m \x1b[48;2;45;85;174m \x1b[49m \x1b[m", + "\x1b[48;2;153;78;112m \x1b[48;2;250;227;206m \x1b[48;2;242;185;171m \x1b[48;2;196;126;137m \x1b[48;2;250;227;206m \x1b[48;2;240;174;162m \x1b[48;2;206;136;142m \x1b[48;2;227;160;155m \x1b[48;2;240;174;162m \x1b[48;2;82;52;72m \x1b[48;2;50;68;111m \x1b[48;2;36;35;46m \x1b[48;2;50;68;111m \x1b[48;2;36;35;46m \x1b[48;2;47;87;175m \x1b[48;2;51;130;227m \x1b[48;2;153;55;95m \x1b[48;2;47;87;175m \x1b[48;2;51;130;227m \x1b[48;2;47;87;175m \x1b[48;2;45;85;174m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;152;77;112m \x1b[48;2;228;161;155m \x1b[48;2;237;171;160m \x1b[48;2;196;126;137m \x1b[48;2;193;123;135m \x1b[48;2;177;105;126m \x1b[48;2;193;123;135m \x1b[48;2;37;37;50m \x1b[48;2;155;69;105m \x1b[48;2;36;35;46m \x1b[48;2;49;66;107m \x1b[48;2;36;35;46m \x1b[48;2;159;83;115m \x1b[48;2;155;67;103m \x1b[48;2;47;69;142m \x1b[48;2;47;87;175m \x1b[48;2;46;73;157m \x1b[48;2;47;85;173m \x1b[48;2;63;77;159m \x1b[48;2;247;97;126m \x1b[48;2;254;165;165m \x1b[48;2;253;160;161m \x1b[48;2;159;79;111m \x1b[48;2;72;106;198m \x1b[48;2;50;67;149m \x1b[48;2;53;69;151m \x1b[48;2;157;77;111m \x1b[48;2;151;22;68m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;147;80;107m \x1b[48;2;176;104;125m \x1b[48;2;240;174;162m \x1b[48;2;66;46;63m \x1b[48;2;36;35;46m \x1b[48;2;170;35;77m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;201;153;202m \x1b[48;2;246;87;102m \x1b[48;2;245;171;163m \x1b[48;2;253;156;159m \x1b[48;2;205;63;93m \x1b[48;2;154;23;70m \x1b[48;2;47;87;175m \x1b[48;2;154;23;70m \x1b[48;2;246;170;163m \x1b[48;2;254;165;165m \x1b[48;2;246;87;102m \x1b[48;2;245;79;114m \x1b[48;2;118;0;50m \x1b[48;2;246;87;102m \x1b[48;2;219;68;93m \x1b[48;2;118;0;50m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;147;80;107m \x1b[48;2;187;116;132m \x1b[48;2;240;174;162m \x1b[48;2;153;78;112m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;182;124;167m \x1b[48;2;255;224;255m \x1b[48;2;251;127;141m \x1b[48;2;246;87;102m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;246;87;102m \x1b[48;2;225;75;97m \x1b[48;2;154;23;70m \x1b[48;2;159;27;72m \x1b[48;2;246;87;102m \x1b[48;2;154;23;70m \x1b[48;2;246;87;102m \x1b[48;2;118;0;50m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;151;78;111m \x1b[48;2;153;78;112m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;200;151;200m \x1b[48;2;216;175;226m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;246;85;105m \x1b[48;2;205;63;93m \x1b[48;2;118;0;50m \x1b[48;2;246;87;102m \x1b[48;2;159;27;72m \x1b[48;2;205;63;93m \x1b[48;2;246;87;102m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;144;16;64m \x1b[48;2;118;0;50m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;154;23;70m \x1b[48;2;246;87;102m \x1b[48;2;182;124;167m \x1b[48;2;255;224;255m \x1b[48;2;153;78;112m \x1b[48;2;182;124;167m \x1b[48;2;255;224;255m \x1b[48;2;205;63;93m \x1b[48;2;159;27;72m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;118;0;50m \x1b[48;2;246;87;102m \x1b[48;2;205;63;93m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;182;124;167m \x1b[48;2;168;74;106m \x1b[48;2;174;39;79m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;154;23;70m \x1b[48;2;246;87;102m \x1b[48;2;211;70;106m \x1b[48;2;233;196;238m \x1b[48;2;255;224;255m \x1b[48;2;178;42;81m \x1b[48;2;205;157;200m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;178;42;81m \x1b[48;2;154;23;70m \x1b[48;2;153;52;92m \x1b[48;2;136;40;82m \x1b[48;2;144;17;63m \x1b[48;2;225;75;97m \x1b[48;2;234;198;240m \x1b[48;2;255;224;255m \x1b[48;2;234;198;240m \x1b[48;2;233;196;238m \x1b[48;2;239;204;243m \x1b[48;2;212;165;204m \x1b[48;2;245;87;103m \x1b[48;2;180;44;82m \x1b[48;2;180;70;102m \x1b[48;2;152;77;112m \x1b[48;2;151;76;110m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;68;104m \x1b[48;2;213;67;95m \x1b[48;2;246;87;102m \x1b[48;2;213;67;95m \x1b[48;2;195;66;97m \x1b[48;2;173;61;105m \x1b[48;2;154;23;70m \x1b[48;2;195;55;89m \x1b[48;2;205;63;93m \x1b[48;2;125;4;54m \x1b[48;2;170;101;145m \x1b[48;2;171;102;146m \x1b[48;2;201;153;202m \x1b[48;2;182;124;167m \x1b[48;2;158;87;122m \x1b[48;2;138;36;79m \x1b[48;2;205;63;93m \x1b[48;2;196;143;183m \x1b[48;2;255;224;255m \x1b[48;2;234;94;114m \x1b[48;2;229;85;104m \x1b[48;2;236;96;117m \x1b[48;2;248;113;131m \x1b[48;2;246;88;103m \x1b[48;2;246;87;102m \x1b[48;2;195;66;97m \x1b[48;2;172;109;148m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;205;63;93m \x1b[48;2;246;87;102m \x1b[48;2;205;63;93m \x1b[48;2;154;23;70m \x1b[48;2;182;124;167m \x1b[48;2;197;147;195m \x1b[48;2;212;170;223m \x1b[48;2;182;124;167m \x1b[48;2;212;170;223m \x1b[48;2;153;78;112m \x1b[48;2;142;44;86m \x1b[48;2;154;23;70m \x1b[48;2;205;63;93m \x1b[48;2;255;224;255m \x1b[48;2;225;187;233m \x1b[48;2;246;87;102m \x1b[48;2;205;63;93m \x1b[48;2;246;87;102m \x1b[48;2;205;63;93m \x1b[48;2;154;23;70m \x1b[48;2;200;151;200m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;205;63;93m \x1b[48;2;154;23;70m \x1b[48;2;182;124;167m \x1b[48;2;147;64;101m \x1b[48;2;36;35;46m \x1b[48;2;153;78;112m \x1b[49m \x1b[48;2;66;78;122m \x1b[48;2;200;100;119m \x1b[48;2;205;63;93m \x1b[48;2;246;87;102m \x1b[48;2;205;63;93m \x1b[48;2;154;23;70m \x1b[48;2;200;151;200m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;182;124;167m \x1b[48;2;36;35;46m \x1b[49m \x1b[48;2;66;78;122m \x1b[48;2;153;55;95m \x1b[48;2;205;63;93m \x1b[48;2;246;87;102m \x1b[48;2;205;63;93m \x1b[48;2;154;23;70m \x1b[48;2;255;224;255m \x1b[48;2;176;115;156m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;185;128;172m \x1b[48;2;153;78;112m \x1b[48;2;36;35;46m \x1b[49m \x1b[48;2;66;78;122m \x1b[48;2;153;55;95m \x1b[48;2;154;23;70m \x1b[48;2;246;87;102m \x1b[48;2;205;63;93m \x1b[48;2;154;23;70m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;176;115;156m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[49m \x1b[48;2;87;54;75m \x1b[48;2;207;163;214m \x1b[48;2;190;136;182m \x1b[48;2;202;151;191m \x1b[48;2;244;187;187m \x1b[48;2;241;206;245m \x1b[48;2;243;209;246m \x1b[48;2;212;170;223m \x1b[48;2;153;78;112m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;56;42;57m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;255;224;255m \x1b[48;2;243;208;246m \x1b[48;2;238;203;242m \x1b[48;2;255;224;255m \x1b[48;2;212;170;223m \x1b[48;2;153;78;112m \x1b[48;2;153;77;111m \x1b[49m \x1b[m", + "\x1b[39m\x1b[49m \x1b[48;2;153;78;112m \x1b[49m \x1b[m" + ]; +} From 8be9c4f8edcb892c9670d77579b560e46a8d2fa2 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 2 Feb 2024 23:26:02 -0500 Subject: [PATCH 02/18] Fix web builds. --- source/funkin/util/logging/AnsiTrace.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/util/logging/AnsiTrace.hx b/source/funkin/util/logging/AnsiTrace.hx index 993fcd4b9..c8d27b86f 100644 --- a/source/funkin/util/logging/AnsiTrace.hx +++ b/source/funkin/util/logging/AnsiTrace.hx @@ -18,7 +18,7 @@ class AnsiTrace #end } - public static var colorSupported:Bool = (Sys.getEnv("TERM") == "xterm" || Sys.getEnv("ANSICON") != null); + public static var colorSupported:Bool = #if sys (Sys.getEnv("TERM") == "xterm" || Sys.getEnv("ANSICON") != null) #else false #end; // ansi stuff public static inline var RED = "\x1b[31m"; From fa556dc1f24a955be93c60bec985433e06fc8200 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 7 Feb 2024 09:21:44 -0500 Subject: [PATCH 03/18] Rewrite conversation JSON parsing code. --- .vscode/settings.json | 5 + Project.xml | 1 - hmm.json | 5 - source/funkin/InitState.hx | 24 +- source/funkin/data/DataParse.hx | 62 +++++ source/funkin/modding/PolymodHandler.hx | 8 +- source/funkin/play/PlayState.hx | 4 +- .../play/cutscene/dialogue/Conversation.hx | 2 +- .../cutscene/dialogue/ConversationData.hx | 240 ------------------ .../dialogue/ConversationDataParser.hx | 163 ------------ .../dialogue/ConversationDebugState.hx | 61 ----- .../play/cutscene/dialogue/DialogueBox.hx | 2 +- .../play/cutscene/dialogue/DialogueBoxData.hx | 124 --------- .../dialogue/DialogueBoxDataParser.hx | 159 ------------ .../cutscene/dialogue/ScriptedConversation.hx | 6 + .../cutscene/dialogue/ScriptedDialogueBox.hx | 5 + .../play/cutscene/dialogue/SpeakerData.hx | 78 ------ .../cutscene/dialogue/SpeakerDataParser.hx | 159 ------------ tests/unit/project.xml | 1 - 19 files changed, 103 insertions(+), 1006 deletions(-) delete mode 100644 source/funkin/play/cutscene/dialogue/ConversationData.hx delete mode 100644 source/funkin/play/cutscene/dialogue/ConversationDataParser.hx delete mode 100644 source/funkin/play/cutscene/dialogue/ConversationDebugState.hx delete mode 100644 source/funkin/play/cutscene/dialogue/DialogueBoxData.hx delete mode 100644 source/funkin/play/cutscene/dialogue/DialogueBoxDataParser.hx delete mode 100644 source/funkin/play/cutscene/dialogue/SpeakerData.hx delete mode 100644 source/funkin/play/cutscene/dialogue/SpeakerDataParser.hx diff --git a/.vscode/settings.json b/.vscode/settings.json index 8979e4de6..3d1f488f7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -110,6 +110,11 @@ "target": "windows", "args": ["-debug", "-DSONG=bopeebo -DDIFFICULTY=normal"] }, + { + "label": "Windows / Debug (Conversation Test)", + "target": "windows", + "args": ["-debug", "-DDIALOGUE"] + }, { "label": "Windows / Debug (Straight to Chart Editor)", "target": "windows", diff --git a/Project.xml b/Project.xml index f5d506688..9755a5e37 100644 --- a/Project.xml +++ b/Project.xml @@ -108,7 +108,6 @@ - diff --git a/hmm.json b/hmm.json index c8b1d911e..fa9a67057 100644 --- a/hmm.json +++ b/hmm.json @@ -156,11 +156,6 @@ "name": "thx.semver", "type": "haxelib", "version": "0.2.2" - }, - { - "name": "tink_json", - "type": "haxelib", - "version": "0.11.0" } ] } diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 23bc255f1..12a937b2a 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -21,9 +21,9 @@ import funkin.data.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.event.SongEventRegistry; import funkin.data.stage.StageRegistry; -import funkin.play.cutscene.dialogue.ConversationDataParser; -import funkin.play.cutscene.dialogue.DialogueBoxDataParser; -import funkin.play.cutscene.dialogue.SpeakerDataParser; +import funkin.data.dialogue.ConversationRegistry; +import funkin.data.dialogue.DialogueBoxRegistry; +import funkin.data.dialogue.SpeakerRegistry; import funkin.data.song.SongRegistry; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.modding.module.ModuleHandler; @@ -208,22 +208,30 @@ class InitState extends FlxState // GAME DATA PARSING // + trace('Parsing game data...'); + + var perf_gameDataParse_start = haxe.Timer.stamp(); + // NOTE: Registries and data parsers must be imported and not referenced with fully qualified names, // to ensure build macros work properly. SongRegistry.instance.loadEntries(); LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); SongEventRegistry.loadEventCache(); - ConversationDataParser.loadConversationCache(); - DialogueBoxDataParser.loadDialogueBoxCache(); - SpeakerDataParser.loadSpeakerCache(); + ConversationRegistry.instance.loadEntries(); + DialogueBoxRegistry.instance.loadEntries(); + SpeakerRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); - CharacterDataParser.loadCharacterCache(); + CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry. ModuleHandler.buildModuleCallbacks(); ModuleHandler.loadModuleCache(); ModuleHandler.callOnCreate(); + + var perf_gameDataParse_end = haxe.Timer.stamp(); + + trace('Done parsing game data. Duration: ${perf_gameDataParse_end - perf_gameDataParse_start} seconds'); } /** @@ -241,6 +249,8 @@ class InitState extends FlxState startLevel(defineLevel(), defineDifficulty()); #elseif FREEPLAY // -DFREEPLAY FlxG.switchState(new FreeplayState()); + #elseif DIALOGUE // -DDIALOGUE + FlxG.switchState(new funkin.ui.debug.dialogue.ConversationDebugState()); #elseif ANIMATE // -DANIMATE FlxG.switchState(new funkin.ui.debug.anim.FlxAnimateTest()); #elseif WAVEFORM // -DWAVEFORM diff --git a/source/funkin/data/DataParse.hx b/source/funkin/data/DataParse.hx index 49dde0198..dbded3fba 100644 --- a/source/funkin/data/DataParse.hx +++ b/source/funkin/data/DataParse.hx @@ -120,6 +120,68 @@ class DataParse } } + public static function backdropData(json:Json, name:String):BackdropData + { + switch (json.value) + { + case JObject(fields): + var result:BackdropData = {}; + var backdropType:String = ''; + + for (field in fields) + { + switch (field.name) + { + case 'backdropType': + backdropType = Tools.getValue(field.value); + } + Reflect.setField(result, field.name, Tools.getValue(field.value)); + } + + switch (backdropType) + { + case 'solid': + return SOLID(result); + default: + throw 'Expected Backdrop property $name to be specify a valid "type", but it was "${backdropType}".'; + } + + return null; + default: + throw 'Expected property $name to be an object, but it was ${json.value}.'; + } + } + + public static function outroData(json:Json, name:String):OutroData + { + switch (json.value) + { + case JObject(fields): + var result:OutroData = {}; + var outroType:String = ''; + + for (field in fields) + { + switch (field.name) + { + case 'outroType': + outroType = Tools.getValue(field.value); + } + Reflect.setField(result, field.name, Tools.getValue(field.value)); + } + + switch (outroType) + { + case 'none': + return NONE(result); + case 'fade': + return FADE(result); + default: + throw 'Expected Outro property $name to be specify a valid "type", but it was "${outroType}".'; + } + } + } + /** * Parser which outputs a `Either`. * Used by the FNF legacy JSON importer. diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 151e658b4..5488cbbbe 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -273,11 +273,11 @@ class PolymodHandler LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); SongEventRegistry.loadEventCache(); - ConversationDataParser.loadConversationCache(); - DialogueBoxDataParser.loadDialogueBoxCache(); - SpeakerDataParser.loadSpeakerCache(); + ConversationRegistry.instance.loadEntries(); + DialogueBoxRegistry.instance.loadEntries(); + SpeakerRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); - CharacterDataParser.loadCharacterCache(); + CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry. ModuleHandler.loadModuleCache(); } } diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index cc9debf13..aee9f2210 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -39,7 +39,7 @@ import funkin.modding.events.ScriptEventDispatcher; import funkin.play.character.BaseCharacter; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.cutscene.dialogue.Conversation; -import funkin.play.cutscene.dialogue.ConversationDataParser; +import funkin.data.dialogue.ConversationRegistry; import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VideoCutscene; import funkin.data.event.SongEventRegistry; @@ -1662,7 +1662,7 @@ class PlayState extends MusicBeatSubState { isInCutscene = true; - currentConversation = ConversationDataParser.fetchConversation(conversationId); + currentConversation = ConversationRegistry.instance.fetchEntry(conversationId); if (currentConversation == null) return; currentConversation.completeCallback = onConversationComplete; diff --git a/source/funkin/play/cutscene/dialogue/Conversation.hx b/source/funkin/play/cutscene/dialogue/Conversation.hx index b2361c795..817c8caf3 100644 --- a/source/funkin/play/cutscene/dialogue/Conversation.hx +++ b/source/funkin/play/cutscene/dialogue/Conversation.hx @@ -13,7 +13,7 @@ import funkin.modding.IScriptedClass.IEventHandler; import funkin.play.cutscene.dialogue.DialogueBox; import funkin.modding.IScriptedClass.IDialogueScriptedClass; import funkin.modding.events.ScriptEventDispatcher; -import funkin.play.cutscene.dialogue.ConversationData.DialogueEntryData; +import funkin.data.dialogue.ConversationData.DialogueEntryData; import flixel.addons.display.FlxPieDial; /** diff --git a/source/funkin/play/cutscene/dialogue/ConversationData.hx b/source/funkin/play/cutscene/dialogue/ConversationData.hx deleted file mode 100644 index 8c4aa9684..000000000 --- a/source/funkin/play/cutscene/dialogue/ConversationData.hx +++ /dev/null @@ -1,240 +0,0 @@ -package funkin.play.cutscene.dialogue; - -import funkin.util.SerializerUtil; - -/** - * Data about a conversation. - * Includes what speakers are in the conversation, and what phrases they say. - */ -@:jsonParse(j -> funkin.play.cutscene.dialogue.ConversationData.fromJson(j)) -@:jsonStringify(v -> v.toJson()) -class ConversationData -{ - public var version:String; - public var backdrop:BackdropData; - public var outro:OutroData; - public var music:MusicData; - public var dialogue:Array; - - public function new(version:String, backdrop:BackdropData, outro:OutroData, music:MusicData, dialogue:Array) - { - this.version = version; - this.backdrop = backdrop; - this.outro = outro; - this.music = music; - this.dialogue = dialogue; - } - - public static function fromString(i:String):ConversationData - { - if (i == null || i == '') return null; - var data: - { - version:String, - backdrop:Dynamic, // TODO: tink.Json doesn't like when these are typed - ?outro:Dynamic, // TODO: tink.Json doesn't like when these are typed - ?music:Dynamic, // TODO: tink.Json doesn't like when these are typed - dialogue:Array // TODO: tink.Json doesn't like when these are typed - } = tink.Json.parse(i); - return fromJson(data); - } - - public static function fromJson(j:Dynamic):ConversationData - { - // TODO: Check version and perform migrations if necessary. - if (j == null) return null; - return new ConversationData(j.version, BackdropData.fromJson(j.backdrop), OutroData.fromJson(j.outro), MusicData.fromJson(j.music), - j.dialogue.map(d -> DialogueEntryData.fromJson(d))); - } - - public function toJson():Dynamic - { - return { - version: this.version, - backdrop: this.backdrop.toJson(), - dialogue: this.dialogue.map(d -> d.toJson()) - }; - } -} - -/** - * Data about a single dialogue entry. - */ -@:jsonParse(j -> funkin.play.cutscene.dialogue.ConversationData.DialogueEntryData.fromJson(j)) -@:jsonStringify(v -> v.toJson()) -class DialogueEntryData -{ - /** - * The speaker who says this phrase. - */ - public var speaker:String; - - /** - * The animation the speaker will play. - */ - public var speakerAnimation:String; - - /** - * The text box that will appear. - */ - public var box:String; - - /** - * The animation the dialogue box will play. - */ - public var boxAnimation:String; - - /** - * The lines of text that will appear in the text box. - */ - public var text:Array; - - /** - * The relative speed at which the text will scroll. - * @default 1.0 - */ - public var speed:Float = 1.0; - - public function new(speaker:String, speakerAnimation:String, box:String, boxAnimation:String, text:Array, speed:Float = null) - { - this.speaker = speaker; - this.speakerAnimation = speakerAnimation; - this.box = box; - this.boxAnimation = boxAnimation; - this.text = text; - if (speed != null) this.speed = speed; - } - - public static function fromJson(j:Dynamic):DialogueEntryData - { - if (j == null) return null; - return new DialogueEntryData(j.speaker, j.speakerAnimation, j.box, j.boxAnimation, j.text, j.speed); - } - - public function toJson():Dynamic - { - var result:Dynamic = - { - speaker: this.speaker, - speakerAnimation: this.speakerAnimation, - box: this.box, - boxAnimation: this.boxAnimation, - text: this.text, - }; - - if (this.speed != 1.0) result.speed = this.speed; - - return result; - } -} - -/** - * Data about a backdrop. - */ -@:jsonParse(j -> funkin.play.cutscene.dialogue.ConversationData.BackdropData.fromJson(j)) -@:jsonStringify(v -> v.toJson()) -class BackdropData -{ - public var type:BackdropType; - public var data:Dynamic; - - public function new(typeStr:String, data:Dynamic) - { - this.type = typeStr; - this.data = data; - } - - public static function fromJson(j:Dynamic):BackdropData - { - if (j == null) return null; - return new BackdropData(j.type, j.data); - } - - public function toJson():Dynamic - { - return { - type: this.type, - data: this.data - }; - } -} - -enum abstract BackdropType(String) from String to String -{ - public var SOLID:BackdropType = 'solid'; -} - -/** - * Data about a music track. - */ -@:jsonParse(j -> funkin.play.cutscene.dialogue.ConversationData.MusicData.fromJson(j)) -@:jsonStringify(v -> v.toJson()) -class MusicData -{ - public var asset:String; - - public var fadeTime:Float; - - @:optional - @:default(false) - public var looped:Bool; - - public function new(asset:String, looped:Bool, fadeTime:Float = 0.0) - { - this.asset = asset; - this.looped = looped; - this.fadeTime = fadeTime; - } - - public static function fromJson(j:Dynamic):MusicData - { - if (j == null) return null; - return new MusicData(j.asset, j.looped, j.fadeTime); - } - - public function toJson():Dynamic - { - return { - asset: this.asset, - looped: this.looped, - fadeTime: this.fadeTime - }; - } -} - -/** - * Data about an outro. - */ -@:jsonParse(j -> funkin.play.cutscene.dialogue.ConversationData.OutroData.fromJson(j)) -@:jsonStringify(v -> v.toJson()) -class OutroData -{ - public var type:OutroType; - public var data:Dynamic; - - public function new(?typeStr:String, data:Dynamic) - { - this.type = typeStr ?? OutroType.NONE; - this.data = data; - } - - public static function fromJson(j:Dynamic):OutroData - { - if (j == null) return null; - return new OutroData(j.type, j.data); - } - - public function toJson():Dynamic - { - return { - type: this.type, - data: this.data - }; - } -} - -enum abstract OutroType(String) from String to String -{ - public var NONE:OutroType = 'none'; - public var FADE:OutroType = 'fade'; -} diff --git a/source/funkin/play/cutscene/dialogue/ConversationDataParser.hx b/source/funkin/play/cutscene/dialogue/ConversationDataParser.hx deleted file mode 100644 index 9f80f8f9b..000000000 --- a/source/funkin/play/cutscene/dialogue/ConversationDataParser.hx +++ /dev/null @@ -1,163 +0,0 @@ -package funkin.play.cutscene.dialogue; - -import openfl.Assets; -import funkin.util.assets.DataAssets; -import funkin.play.cutscene.dialogue.ScriptedConversation; - -/** - * Contains utilities for loading and parsing conversation data. - * TODO: Refactor to use the json2object + BaseRegistry system that actually validates things for you. - */ -class ConversationDataParser -{ - public static final CONVERSATION_DATA_VERSION:String = '1.0.0'; - public static final CONVERSATION_DATA_VERSION_RULE:String = '1.0.x'; - - static final conversationCache:Map = new Map(); - static final conversationScriptedClass:Map = new Map(); - - static final DEFAULT_CONVERSATION_ID:String = 'UNKNOWN'; - - /** - * Parses and preloads the game's conversation data and scripts when the game starts. - * - * If you want to force conversations to be reloaded, you can just call this function again. - */ - public static function loadConversationCache():Void - { - clearConversationCache(); - trace('Loading dialogue conversation cache...'); - - // - // SCRIPTED CONVERSATIONS - // - var scriptedConversationClassNames:Array = ScriptedConversation.listScriptClasses(); - trace(' Instantiating ${scriptedConversationClassNames.length} scripted conversations...'); - for (conversationCls in scriptedConversationClassNames) - { - var conversation:Conversation = ScriptedConversation.init(conversationCls, DEFAULT_CONVERSATION_ID); - if (conversation != null) - { - trace(' Loaded scripted conversation: ${conversationCls}'); - // Disable the rendering logic for conversation until it's loaded. - // Note that kill() =/= destroy() - conversation.kill(); - - // Then store it. - conversationCache.set(conversation.conversationId, conversation); - } - else - { - trace(' Failed to instantiate scripted conversation class: ${conversationCls}'); - } - } - - // - // UNSCRIPTED CONVERSATIONS - // - // Scripts refers to code here, not the actual dialogue. - var conversationIdList:Array = DataAssets.listDataFilesInPath('dialogue/conversations/'); - // Filter out conversations that are scripted. - var unscriptedConversationIds:Array = conversationIdList.filter(function(conversationId:String):Bool { - return !conversationCache.exists(conversationId); - }); - trace(' Fetching data for ${unscriptedConversationIds.length} conversations...'); - for (conversationId in unscriptedConversationIds) - { - try - { - var conversation:Conversation = new Conversation(conversationId); - // Say something offensive to kill the conversation. - // We will revive it later. - conversation.kill(); - if (conversation != null) - { - trace(' Loaded conversation data: ${conversation.conversationId}'); - conversationCache.set(conversation.conversationId, conversation); - } - } - catch (e) - { - trace(e); - continue; - } - } - } - - /** - * Fetches data for a conversation and returns a Conversation instance, - * ready to be displayed. - * @param conversationId The ID of the conversation to fetch. - * @return The conversation instance, or null if the conversation was not found. - */ - public static function fetchConversation(conversationId:String):Null - { - if (conversationId != null && conversationId != '' && conversationCache.exists(conversationId)) - { - trace('Successfully fetched conversation: ${conversationId}'); - var conversation:Conversation = conversationCache.get(conversationId); - // ...ANYway... - conversation.revive(); - return conversation; - } - else - { - trace('Failed to fetch conversation, not found in cache: ${conversationId}'); - return null; - } - } - - static function clearConversationCache():Void - { - if (conversationCache != null) - { - for (conversation in conversationCache) - { - conversation.destroy(); - } - conversationCache.clear(); - } - } - - public static function listConversationIds():Array - { - return conversationCache.keys().array(); - } - - /** - * Load a conversation's JSON file, parse its data, and return it. - * - * @param conversationId The conversation to load. - * @return The conversation data, or null if validation failed. - */ - public static function parseConversationData(conversationId:String):Null - { - trace('Parsing conversation data: ${conversationId}'); - var rawJson:String = loadConversationFile(conversationId); - - try - { - var conversationData:ConversationData = ConversationData.fromString(rawJson); - return conversationData; - } - catch (e) - { - trace('Failed to parse conversation ($conversationId).'); - trace(e); - return null; - } - } - - static function loadConversationFile(conversationPath:String):String - { - var conversationFilePath:String = Paths.json('dialogue/conversations/${conversationPath}'); - var rawJson:String = Assets.getText(conversationFilePath).trim(); - - while (!rawJson.endsWith('}') && rawJson.length > 0) - { - rawJson = rawJson.substr(0, rawJson.length - 1); - } - - return rawJson; - } -} diff --git a/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx b/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx deleted file mode 100644 index 13697b9f4..000000000 --- a/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx +++ /dev/null @@ -1,61 +0,0 @@ -package funkin.play.cutscene.dialogue; - -import flixel.FlxState; -import funkin.modding.events.ScriptEventDispatcher; -import funkin.modding.events.ScriptEvent; -import flixel.util.FlxColor; -import funkin.ui.MusicBeatState; - -/** - * A state with displays a conversation with no background. - * Used for testing. - * @param conversationId The conversation to display. - */ -class ConversationDebugState extends MusicBeatState -{ - final conversationId:String = 'senpai'; - - var conversation:Conversation; - - public function new() - { - super(); - - // TODO: Fix this BS - Paths.setCurrentLevel('week6'); - } - - public override function create():Void - { - conversation = ConversationDataParser.fetchConversation(conversationId); - conversation.completeCallback = onConversationComplete; - add(conversation); - - ScriptEventDispatcher.callEvent(conversation, new ScriptEvent(CREATE, false)); - } - - function onConversationComplete():Void - { - remove(conversation); - conversation = null; - } - - public override function update(elapsed:Float):Void - { - super.update(elapsed); - - if (conversation != null) - { - if (controls.CUTSCENE_ADVANCE) conversation.advanceConversation(); - - if (controls.CUTSCENE_SKIP) - { - conversation.trySkipConversation(elapsed); - } - else - { - conversation.trySkipConversation(-1); - } - } - } -} diff --git a/source/funkin/play/cutscene/dialogue/DialogueBox.hx b/source/funkin/play/cutscene/dialogue/DialogueBox.hx index cdac3c233..4df34badc 100644 --- a/source/funkin/play/cutscene/dialogue/DialogueBox.hx +++ b/source/funkin/play/cutscene/dialogue/DialogueBox.hx @@ -235,7 +235,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass { textDisplay = new FlxTypeText(0, 0, 300, '', 32); textDisplay.fieldWidth = boxData.text.width; - textDisplay.setFormat('Pixel Arial 11 Bold', boxData.text.size, FlxColor.fromString(boxData.text.color), LEFT, SHADOW, + textDisplay.setFormat(boxData.text.fontFamily, boxData.text.size, FlxColor.fromString(boxData.text.color), LEFT, SHADOW, FlxColor.fromString(boxData.text.shadowColor ?? '#00000000'), false); textDisplay.borderSize = boxData.text.shadowWidth ?? 2; textDisplay.sounds = [FlxG.sound.load(Paths.sound('pixelText'), 0.6)]; diff --git a/source/funkin/play/cutscene/dialogue/DialogueBoxData.hx b/source/funkin/play/cutscene/dialogue/DialogueBoxData.hx deleted file mode 100644 index 801a01dd7..000000000 --- a/source/funkin/play/cutscene/dialogue/DialogueBoxData.hx +++ /dev/null @@ -1,124 +0,0 @@ -package funkin.play.cutscene.dialogue; - -import funkin.data.animation.AnimationData; -import funkin.util.SerializerUtil; - -/** - * Data about a text box. - */ -@:jsonParse(j -> funkin.play.cutscene.dialogue.DialogueBoxData.fromJson(j)) -@:jsonStringify(v -> v.toJson()) -class DialogueBoxData -{ - public var version:String; - public var name:String; - public var assetPath:String; - public var flipX:Bool; - public var flipY:Bool; - public var isPixel:Bool; - public var offsets:Array; - public var text:DialogueBoxTextData; - public var scale:Float; - public var animations:Array; - - public function new(version:String, name:String, assetPath:String, flipX:Bool = false, flipY:Bool = false, isPixel:Bool = false, offsets:Null>, - text:DialogueBoxTextData, scale:Float = 1.0, animations:Array) - { - this.version = version; - this.name = name; - this.assetPath = assetPath; - this.flipX = flipX; - this.flipY = flipY; - this.isPixel = isPixel; - this.offsets = offsets ?? [0, 0]; - this.text = text; - this.scale = scale; - this.animations = animations; - } - - public static function fromString(i:String):DialogueBoxData - { - if (i == null || i == '') return null; - var data: - { - version:String, - name:String, - assetPath:String, - flipX:Bool, - flipY:Bool, - isPixel:Bool, - ?offsets:Array, - text:Dynamic, - scale:Float, - animations:Array - } = tink.Json.parse(i); - return fromJson(data); - } - - public static function fromJson(j:Dynamic):DialogueBoxData - { - // TODO: Check version and perform migrations if necessary. - if (j == null) return null; - return new DialogueBoxData(j.version, j.name, j.assetPath, j.flipX, j.flipY, j.isPixel, j.offsets, DialogueBoxTextData.fromJson(j.text), j.scale, - j.animations); - } - - public function toJson():Dynamic - { - return { - version: this.version, - name: this.name, - assetPath: this.assetPath, - flipX: this.flipX, - flipY: this.flipY, - isPixel: this.isPixel, - offsets: this.offsets, - scale: this.scale, - animations: this.animations - }; - } -} - -/** - * Data about text in a text box. - */ -@:jsonParse(j -> funkin.play.cutscene.dialogue.DialogueBoxTextData.fromJson(j)) -@:jsonStringify(v -> v.toJson()) -class DialogueBoxTextData -{ - public var offsets:Array; - public var width:Int; - public var size:Int; - public var color:String; - public var shadowColor:Null; - public var shadowWidth:Null; - - public function new(offsets:Null>, ?width:Int, ?size:Int, color:String, ?shadowColor:String, shadowWidth:Null) - { - this.offsets = offsets ?? [0, 0]; - this.width = width ?? 300; - this.size = size ?? 32; - this.color = color; - this.shadowColor = shadowColor; - this.shadowWidth = shadowWidth; - } - - public static function fromJson(j:Dynamic):DialogueBoxTextData - { - // TODO: Check version and perform migrations if necessary. - if (j == null) return null; - return new DialogueBoxTextData(j.offsets, j.width, j.size, j.color, j.shadowColor, j.shadowWidth); - } - - public function toJson():Dynamic - { - return { - offsets: this.offsets, - width: this.width, - size: this.size, - color: this.color, - shadowColor: this.shadowColor, - shadowWidth: this.shadowWidth, - }; - } -} diff --git a/source/funkin/play/cutscene/dialogue/DialogueBoxDataParser.hx b/source/funkin/play/cutscene/dialogue/DialogueBoxDataParser.hx deleted file mode 100644 index cb00dd80d..000000000 --- a/source/funkin/play/cutscene/dialogue/DialogueBoxDataParser.hx +++ /dev/null @@ -1,159 +0,0 @@ -package funkin.play.cutscene.dialogue; - -import openfl.Assets; -import funkin.util.assets.DataAssets; -import funkin.play.cutscene.dialogue.DialogueBox; -import funkin.play.cutscene.dialogue.ScriptedDialogueBox; - -/** - * Contains utilities for loading and parsing dialogueBox data. - */ -class DialogueBoxDataParser -{ - public static final DIALOGUE_BOX_DATA_VERSION:String = '1.0.0'; - public static final DIALOGUE_BOX_DATA_VERSION_RULE:String = '1.0.x'; - - static final dialogueBoxCache:Map = new Map(); - - static final dialogueBoxScriptedClass:Map = new Map(); - - static final DEFAULT_DIALOGUE_BOX_ID:String = 'UNKNOWN'; - - /** - * Parses and preloads the game's dialogueBox data and scripts when the game starts. - * - * If you want to force dialogue boxes to be reloaded, you can just call this function again. - */ - public static function loadDialogueBoxCache():Void - { - clearDialogueBoxCache(); - trace('Loading dialogue box cache...'); - - // - // SCRIPTED CONVERSATIONS - // - var scriptedDialogueBoxClassNames:Array = ScriptedDialogueBox.listScriptClasses(); - trace(' Instantiating ${scriptedDialogueBoxClassNames.length} scripted dialogue boxes...'); - for (dialogueBoxCls in scriptedDialogueBoxClassNames) - { - var dialogueBox:DialogueBox = ScriptedDialogueBox.init(dialogueBoxCls, DEFAULT_DIALOGUE_BOX_ID); - if (dialogueBox != null) - { - trace(' Loaded scripted dialogue box: ${dialogueBox.dialogueBoxName}'); - // Disable the rendering logic for dialogueBox until it's loaded. - // Note that kill() =/= destroy() - dialogueBox.kill(); - - // Then store it. - dialogueBoxCache.set(dialogueBox.dialogueBoxId, dialogueBox); - } - else - { - trace(' Failed to instantiate scripted dialogueBox class: ${dialogueBoxCls}'); - } - } - - // - // UNSCRIPTED CONVERSATIONS - // - // Scripts refers to code here, not the actual dialogue. - var dialogueBoxIdList:Array = DataAssets.listDataFilesInPath('dialogue/boxes/'); - // Filter out dialogue boxes that are scripted. - var unscriptedDialogueBoxIds:Array = dialogueBoxIdList.filter(function(dialogueBoxId:String):Bool { - return !dialogueBoxCache.exists(dialogueBoxId); - }); - trace(' Fetching data for ${unscriptedDialogueBoxIds.length} dialogue boxes...'); - for (dialogueBoxId in unscriptedDialogueBoxIds) - { - try - { - var dialogueBox:DialogueBox = new DialogueBox(dialogueBoxId); - if (dialogueBox != null) - { - trace(' Loaded dialogueBox data: ${dialogueBox.dialogueBoxName}'); - dialogueBoxCache.set(dialogueBox.dialogueBoxId, dialogueBox); - } - } - catch (e) - { - trace(e); - continue; - } - } - } - - /** - * Fetches data for a dialogueBox and returns a DialogueBox instance, - * ready to be displayed. - * @param dialogueBoxId The ID of the dialogueBox to fetch. - * @return The dialogueBox instance, or null if the dialogueBox was not found. - */ - public static function fetchDialogueBox(dialogueBoxId:String):Null - { - if (dialogueBoxId != null && dialogueBoxId != '' && dialogueBoxCache.exists(dialogueBoxId)) - { - trace('Successfully fetched dialogueBox: ${dialogueBoxId}'); - var dialogueBox:DialogueBox = dialogueBoxCache.get(dialogueBoxId); - dialogueBox.revive(); - return dialogueBox; - } - else - { - trace('Failed to fetch dialogueBox, not found in cache: ${dialogueBoxId}'); - return null; - } - } - - static function clearDialogueBoxCache():Void - { - if (dialogueBoxCache != null) - { - for (dialogueBox in dialogueBoxCache) - { - dialogueBox.destroy(); - } - dialogueBoxCache.clear(); - } - } - - public static function listDialogueBoxIds():Array - { - return dialogueBoxCache.keys().array(); - } - - /** - * Load a dialogueBox's JSON file, parse its data, and return it. - * - * @param dialogueBoxId The dialogueBox to load. - * @return The dialogueBox data, or null if validation failed. - */ - public static function parseDialogueBoxData(dialogueBoxId:String):Null - { - var rawJson:String = loadDialogueBoxFile(dialogueBoxId); - - try - { - var dialogueBoxData:DialogueBoxData = DialogueBoxData.fromString(rawJson); - return dialogueBoxData; - } - catch (e) - { - trace('Failed to parse dialogueBox ($dialogueBoxId).'); - trace(e); - return null; - } - } - - static function loadDialogueBoxFile(dialogueBoxPath:String):String - { - var dialogueBoxFilePath:String = Paths.json('dialogue/boxes/${dialogueBoxPath}'); - var rawJson:String = Assets.getText(dialogueBoxFilePath).trim(); - - while (!rawJson.endsWith('}') && rawJson.length > 0) - { - rawJson = rawJson.substr(0, rawJson.length - 1); - } - - return rawJson; - } -} diff --git a/source/funkin/play/cutscene/dialogue/ScriptedConversation.hx b/source/funkin/play/cutscene/dialogue/ScriptedConversation.hx index 4fe383a5e..cb7344273 100644 --- a/source/funkin/play/cutscene/dialogue/ScriptedConversation.hx +++ b/source/funkin/play/cutscene/dialogue/ScriptedConversation.hx @@ -1,4 +1,10 @@ package funkin.play.cutscene.dialogue; +/** + * A script that can be tied to a Conversation. + * Create a scripted class that extends Conversation to use this. + * This allows you to customize how a specific conversation appears and behaves. + * Someone clever could use this to add branching dialogue I think. + */ @:hscriptClass class ScriptedConversation extends Conversation implements polymod.hscript.HScriptedClass {} diff --git a/source/funkin/play/cutscene/dialogue/ScriptedDialogueBox.hx b/source/funkin/play/cutscene/dialogue/ScriptedDialogueBox.hx index a1b36c7c2..7689fc0d9 100644 --- a/source/funkin/play/cutscene/dialogue/ScriptedDialogueBox.hx +++ b/source/funkin/play/cutscene/dialogue/ScriptedDialogueBox.hx @@ -1,4 +1,9 @@ package funkin.play.cutscene.dialogue; +/** + * A script that can be tied to a DialogueBox. + * Create a scripted class that extends DialogueBox to use this. + * This allows you to customize how a specific dialogue box appears. + */ @:hscriptClass class ScriptedDialogueBox extends DialogueBox implements polymod.hscript.HScriptedClass {} diff --git a/source/funkin/play/cutscene/dialogue/SpeakerData.hx b/source/funkin/play/cutscene/dialogue/SpeakerData.hx deleted file mode 100644 index 88883ead8..000000000 --- a/source/funkin/play/cutscene/dialogue/SpeakerData.hx +++ /dev/null @@ -1,78 +0,0 @@ -package funkin.play.cutscene.dialogue; - -import funkin.data.animation.AnimationData; - -/** - * Data about a conversation. - * Includes what speakers are in the conversation, and what phrases they say. - */ -@:jsonParse(j -> funkin.play.cutscene.dialogue.SpeakerData.fromJson(j)) -@:jsonStringify(v -> v.toJson()) -class SpeakerData -{ - public var version:String; - public var name:String; - public var assetPath:String; - public var flipX:Bool; - public var isPixel:Bool; - public var offsets:Array; - public var scale:Float; - public var animations:Array; - - public function new(version:String, name:String, assetPath:String, animations:Array, ?offsets:Array, flipX:Bool = false, - isPixel:Bool = false, ?scale:Float = 1.0) - { - this.version = version; - this.name = name; - this.assetPath = assetPath; - this.animations = animations; - - this.offsets = offsets; - if (this.offsets == null || this.offsets == []) this.offsets = [0, 0]; - - this.flipX = flipX; - this.isPixel = isPixel; - this.scale = scale; - } - - public static function fromString(i:String):SpeakerData - { - if (i == null || i == '') return null; - var data: - { - version:String, - name:String, - assetPath:String, - animations:Array, - ?offsets:Array, - ?flipX:Bool, - ?isPixel:Bool, - ?scale:Float - } = tink.Json.parse(i); - return fromJson(data); - } - - public static function fromJson(j:Dynamic):SpeakerData - { - // TODO: Check version and perform migrations if necessary. - if (j == null) return null; - return new SpeakerData(j.version, j.name, j.assetPath, j.animations, j.offsets, j.flipX, j.isPixel, j.scale); - } - - public function toJson():Dynamic - { - var result:Dynamic = - { - version: this.version, - name: this.name, - assetPath: this.assetPath, - animations: this.animations, - flipX: this.flipX, - isPixel: this.isPixel - }; - - if (this.scale != 1.0) result.scale = this.scale; - - return result; - } -} diff --git a/source/funkin/play/cutscene/dialogue/SpeakerDataParser.hx b/source/funkin/play/cutscene/dialogue/SpeakerDataParser.hx deleted file mode 100644 index f7ddb099f..000000000 --- a/source/funkin/play/cutscene/dialogue/SpeakerDataParser.hx +++ /dev/null @@ -1,159 +0,0 @@ -package funkin.play.cutscene.dialogue; - -import openfl.Assets; -import funkin.util.assets.DataAssets; -import funkin.play.cutscene.dialogue.Speaker; -import funkin.play.cutscene.dialogue.ScriptedSpeaker; - -/** - * Contains utilities for loading and parsing speaker data. - */ -class SpeakerDataParser -{ - public static final SPEAKER_DATA_VERSION:String = '1.0.0'; - public static final SPEAKER_DATA_VERSION_RULE:String = '1.0.x'; - - static final speakerCache:Map = new Map(); - - static final speakerScriptedClass:Map = new Map(); - - static final DEFAULT_SPEAKER_ID:String = 'UNKNOWN'; - - /** - * Parses and preloads the game's speaker data and scripts when the game starts. - * - * If you want to force speakers to be reloaded, you can just call this function again. - */ - public static function loadSpeakerCache():Void - { - clearSpeakerCache(); - trace('Loading dialogue speaker cache...'); - - // - // SCRIPTED CONVERSATIONS - // - var scriptedSpeakerClassNames:Array = ScriptedSpeaker.listScriptClasses(); - trace(' Instantiating ${scriptedSpeakerClassNames.length} scripted speakers...'); - for (speakerCls in scriptedSpeakerClassNames) - { - var speaker:Speaker = ScriptedSpeaker.init(speakerCls, DEFAULT_SPEAKER_ID); - if (speaker != null) - { - trace(' Loaded scripted speaker: ${speaker.speakerName}'); - // Disable the rendering logic for speaker until it's loaded. - // Note that kill() =/= destroy() - speaker.kill(); - - // Then store it. - speakerCache.set(speaker.speakerId, speaker); - } - else - { - trace(' Failed to instantiate scripted speaker class: ${speakerCls}'); - } - } - - // - // UNSCRIPTED CONVERSATIONS - // - // Scripts refers to code here, not the actual dialogue. - var speakerIdList:Array = DataAssets.listDataFilesInPath('dialogue/speakers/'); - // Filter out speakers that are scripted. - var unscriptedSpeakerIds:Array = speakerIdList.filter(function(speakerId:String):Bool { - return !speakerCache.exists(speakerId); - }); - trace(' Fetching data for ${unscriptedSpeakerIds.length} speakers...'); - for (speakerId in unscriptedSpeakerIds) - { - try - { - var speaker:Speaker = new Speaker(speakerId); - if (speaker != null) - { - trace(' Loaded speaker data: ${speaker.speakerName}'); - speakerCache.set(speaker.speakerId, speaker); - } - } - catch (e) - { - trace(e); - continue; - } - } - } - - /** - * Fetches data for a speaker and returns a Speaker instance, - * ready to be displayed. - * @param speakerId The ID of the speaker to fetch. - * @return The speaker instance, or null if the speaker was not found. - */ - public static function fetchSpeaker(speakerId:String):Null - { - if (speakerId != null && speakerId != '' && speakerCache.exists(speakerId)) - { - trace('Successfully fetched speaker: ${speakerId}'); - var speaker:Speaker = speakerCache.get(speakerId); - speaker.revive(); - return speaker; - } - else - { - trace('Failed to fetch speaker, not found in cache: ${speakerId}'); - return null; - } - } - - static function clearSpeakerCache():Void - { - if (speakerCache != null) - { - for (speaker in speakerCache) - { - speaker.destroy(); - } - speakerCache.clear(); - } - } - - public static function listSpeakerIds():Array - { - return speakerCache.keys().array(); - } - - /** - * Load a speaker's JSON file, parse its data, and return it. - * - * @param speakerId The speaker to load. - * @return The speaker data, or null if validation failed. - */ - public static function parseSpeakerData(speakerId:String):Null - { - var rawJson:String = loadSpeakerFile(speakerId); - - try - { - var speakerData:SpeakerData = SpeakerData.fromString(rawJson); - return speakerData; - } - catch (e) - { - trace('Failed to parse speaker ($speakerId).'); - trace(e); - return null; - } - } - - static function loadSpeakerFile(speakerPath:String):String - { - var speakerFilePath:String = Paths.json('dialogue/speakers/${speakerPath}'); - var rawJson:String = Assets.getText(speakerFilePath).trim(); - - while (!rawJson.endsWith('}') && rawJson.length > 0) - { - rawJson = rawJson.substr(0, rawJson.length - 1); - } - - return rawJson; - } -} diff --git a/tests/unit/project.xml b/tests/unit/project.xml index 2e505e015..dfbf06502 100644 --- a/tests/unit/project.xml +++ b/tests/unit/project.xml @@ -27,7 +27,6 @@ - From 31cd5b34146f67d58f440826f17d48f2961f9ebc Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 7 Feb 2024 18:45:13 -0500 Subject: [PATCH 04/18] Rework Conversation data parsing --- source/funkin/InitState.hx | 17 +- source/funkin/data/BaseRegistry.hx | 7 +- source/funkin/data/DataParse.hx | 15 +- .../funkin/data/dialogue/ConversationData.hx | 168 ++++++++++++++++++ .../data/dialogue/ConversationRegistry.hx | 81 +++++++++ .../funkin/data/dialogue/DialogueBoxData.hx | 128 +++++++++++++ .../data/dialogue/DialogueBoxRegistry.hx | 81 +++++++++ source/funkin/data/dialogue/SpeakerData.hx | 68 +++++++ .../funkin/data/dialogue/SpeakerRegistry.hx | 81 +++++++++ source/funkin/data/song/SongRegistry.hx | 4 +- source/funkin/modding/PolymodHandler.hx | 12 +- source/funkin/play/character/CharacterData.hx | 2 +- .../play/cutscene/dialogue/Conversation.hx | 96 +++++----- .../play/cutscene/dialogue/DialogueBox.hx | 68 ++++--- .../funkin/play/cutscene/dialogue/Speaker.hx | 55 ++++-- .../debug/dialogue/ConversationDebugState.hx | 76 ++++++++ source/funkin/util/assets/DataAssets.hx | 3 +- 17 files changed, 849 insertions(+), 113 deletions(-) create mode 100644 source/funkin/data/dialogue/ConversationData.hx create mode 100644 source/funkin/data/dialogue/ConversationRegistry.hx create mode 100644 source/funkin/data/dialogue/DialogueBoxData.hx create mode 100644 source/funkin/data/dialogue/DialogueBoxRegistry.hx create mode 100644 source/funkin/data/dialogue/SpeakerData.hx create mode 100644 source/funkin/data/dialogue/SpeakerRegistry.hx create mode 100644 source/funkin/ui/debug/dialogue/ConversationDebugState.hx diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 12a937b2a..625a33ad7 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -208,30 +208,29 @@ class InitState extends FlxState // GAME DATA PARSING // - trace('Parsing game data...'); - - var perf_gameDataParse_start = haxe.Timer.stamp(); - - // NOTE: Registries and data parsers must be imported and not referenced with fully qualified names, + // NOTE: Registries must be imported and not referenced with fully qualified names, // to ensure build macros work properly. + trace('Parsing game data...'); + var perfStart = haxe.Timer.stamp(); + SongEventRegistry.loadEventCache(); // SongEventRegistry is structured differently so it's not a BaseRegistry. SongRegistry.instance.loadEntries(); LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); - SongEventRegistry.loadEventCache(); ConversationRegistry.instance.loadEntries(); DialogueBoxRegistry.instance.loadEntries(); SpeakerRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); + + // TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers. CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry. ModuleHandler.buildModuleCallbacks(); ModuleHandler.loadModuleCache(); - ModuleHandler.callOnCreate(); - var perf_gameDataParse_end = haxe.Timer.stamp(); + var perfEnd = haxe.Timer.stamp(); - trace('Done parsing game data. Duration: ${perf_gameDataParse_end - perf_gameDataParse_start} seconds'); + trace('Parsing game data took ${Math.floor((perfEnd - perfStart) * 1000)}ms.'); } /** diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx index 70615069b..0ccbe2f18 100644 --- a/source/funkin/data/BaseRegistry.hx +++ b/source/funkin/data/BaseRegistry.hx @@ -46,6 +46,9 @@ abstract class BaseRegistry & Constructible(); } + /** + * TODO: Create a `loadEntriesAsync()` function. + */ public function loadEntries():Void { clearEntries(); @@ -54,7 +57,7 @@ abstract class BaseRegistry & Constructible = getScriptedClassNames(); - log('Registering ${scriptedEntryClassNames.length} scripted entries...'); + log('Parsing ${scriptedEntryClassNames.length} scripted entries...'); for (entryCls in scriptedEntryClassNames) { @@ -78,7 +81,7 @@ abstract class BaseRegistry & Constructible = entryIdList.filter(function(entryId:String):Bool { return !entries.exists(entryId); }); - log('Fetching data for ${unscriptedEntryIds.length} unscripted entries...'); + log('Parsing ${unscriptedEntryIds.length} unscripted entries...'); for (entryId in unscriptedEntryIds) { try diff --git a/source/funkin/data/DataParse.hx b/source/funkin/data/DataParse.hx index dbded3fba..244d41132 100644 --- a/source/funkin/data/DataParse.hx +++ b/source/funkin/data/DataParse.hx @@ -120,19 +120,19 @@ class DataParse } } - public static function backdropData(json:Json, name:String):BackdropData + public static function backdropData(json:Json, name:String):funkin.data.dialogue.ConversationData.BackdropData { switch (json.value) { case JObject(fields): - var result:BackdropData = {}; + var result:Dynamic = {}; var backdropType:String = ''; for (field in fields) { switch (field.name) { - case 'backdropType': + case 'type': backdropType = Tools.getValue(field.value); } Reflect.setField(result, field.name, Tools.getValue(field.value)); @@ -152,19 +152,19 @@ class DataParse } } - public static function outroData(json:Json, name:String):OutroData + public static function outroData(json:Json, name:String):Null { switch (json.value) { case JObject(fields): - var result:OutroData = {}; + var result:Dynamic = {}; var outroType:String = ''; for (field in fields) { switch (field.name) { - case 'outroType': + case 'type': outroType = Tools.getValue(field.value); } Reflect.setField(result, field.name, Tools.getValue(field.value)); @@ -179,6 +179,9 @@ class DataParse default: throw 'Expected Outro property $name to be specify a valid "type", but it was "${outroType}".'; } + return null; + default: + throw 'Expected property $name to be an object, but it was ${json.value}.'; } } diff --git a/source/funkin/data/dialogue/ConversationData.hx b/source/funkin/data/dialogue/ConversationData.hx new file mode 100644 index 000000000..795ddae9a --- /dev/null +++ b/source/funkin/data/dialogue/ConversationData.hx @@ -0,0 +1,168 @@ +package funkin.data.dialogue; + +import funkin.data.animation.AnimationData; + +/** + * A type definition for the data for a specific conversation. + * It includes things like what dialogue boxes to use, what text to display, and what animations to play. + * @see https://lib.haxe.org/p/json2object/ + */ +typedef ConversationData = +{ + /** + * Semantic version for conversation data. + */ + public var version:String; + + /** + * Data on the backdrop for the conversation. + */ + @:jcustomparse(funkin.data.DataParse.backdropData) + public var backdrop:BackdropData; + + /** + * Data on the outro for the conversation. + */ + @:jcustomparse(funkin.data.DataParse.outroData) + @:optional + public var outro:Null; + + /** + * Data on the music for the conversation. + */ + @:optional + public var music:Null; + + /** + * Data for each line of dialogue in the conversation. + */ + public var dialogue:Array; +} + +/** + * Data on the backdrop for the conversation, behind the dialogue box. + * A custom parser distinguishes between backdrop types based on the `type` field. + */ +enum BackdropData +{ + SOLID(data:BackdropData_Solid); // 'solid' +} + +/** + * Data for a Solid color backdrop. + */ +typedef BackdropData_Solid = +{ + /** + * Used to distinguish between backdrop types. Should always be `solid` for this type. + */ + var type:String; + + /** + * The color of the backdrop. + */ + var color:String; + + /** + * Fade-in time for the backdrop. + * @default No fade-in + */ + @:optional + @:default(0.0) + var fadeTime:Float; +}; + +enum OutroData +{ + NONE(data:OutroData_None); // 'none' + FADE(data:OutroData_Fade); // 'fade' +} + +typedef OutroData_None = +{ + /** + * Used to distinguish between outro types. Should always be `none` for this type. + */ + var type:String; +} + +typedef OutroData_Fade = +{ + /** + * Used to distinguish between outro types. Should always be `fade` for this type. + */ + var type:String; + + /** + * The time to fade out the conversation. + * @default 1 second + */ + @:optional + @:default(1.0) + var fadeTime:Float; +} + +typedef MusicData = +{ + /** + * The asset to play for the music. + */ + var asset:String; + + /** + * The time to fade in the music. + */ + @:optional + @:default(0.0) + var fadeTime:Float; + + @:optional + @:default(false) + var looped:Bool; +}; + +/** + * Data on a single line of dialogue in a conversation. + */ +typedef DialogueEntryData = +{ + /** + * Which speaker is speaking. + * @see `SpeakerData.hx` + */ + public var speaker:String; + + /** + * The animation the speaker should play for this line of dialogue. + */ + public var speakerAnimation:String; + + /** + * Which dialogue box to use for this line of dialogue. + * @see `DialogueBoxData.hx` + */ + public var box:String; + + /** + * Which animation to play for the dialogue box. + */ + public var boxAnimation:String; + + /** + * The text that will display for this line of dialogue. + * Text will automatically wrap. + * When the user advances the dialogue, the next entry in the array will concatenate on. + * Advancing when the last entry is displayed will move to the next `DialogueEntryData`, + * or end the conversation if there are no more. + */ + public var text:Array; + + /** + * The relative speed at which text gets "typed out". + * Setting `speed` to `1.5` would make it look like the character is speaking quickly, + * and setting `speed` to `0.5` would make it look like the character is emphasizing each word. + */ + @:optional + @:default(1.0) + public var speed:Float; +}; diff --git a/source/funkin/data/dialogue/ConversationRegistry.hx b/source/funkin/data/dialogue/ConversationRegistry.hx new file mode 100644 index 000000000..9186ef786 --- /dev/null +++ b/source/funkin/data/dialogue/ConversationRegistry.hx @@ -0,0 +1,81 @@ +package funkin.data.dialogue; + +import funkin.play.cutscene.dialogue.Conversation; +import funkin.data.dialogue.ConversationData; +import funkin.play.cutscene.dialogue.ScriptedConversation; + +class ConversationRegistry extends BaseRegistry +{ + /** + * The current version string for the dialogue box data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migrateConversationData()` function. + */ + public static final CONVERSATION_DATA_VERSION:thx.semver.Version = "1.0.0"; + + public static final CONVERSATION_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; + + public static final instance:ConversationRegistry = new ConversationRegistry(); + + public function new() + { + super('CONVERSATION', 'dialogue/conversations', CONVERSATION_DATA_VERSION_RULE); + } + + /** + * Read, parse, and validate the JSON data and produce the corresponding data object. + */ + public function parseEntryData(id:String):Null + { + // JsonParser does not take type parameters, + // otherwise this function would be in BaseRegistry. + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + + switch (loadEntryFile(id)) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + printErrors(parser.errors, id); + return null; + } + return parser.value; + } + + /** + * Parse and validate the JSON data and produce the corresponding data object. + * + * NOTE: Must be implemented on the implementation class. + * @param contents The JSON as a string. + * @param fileName An optional file name for error reporting. + */ + public function parseEntryDataRaw(contents:String, ?fileName:String):Null + { + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + parser.fromJson(contents, fileName); + + if (parser.errors.length > 0) + { + printErrors(parser.errors, fileName); + return null; + } + return parser.value; + } + + function createScriptedEntry(clsName:String):Conversation + { + return ScriptedConversation.init(clsName, "unknown"); + } + + function getScriptedClassNames():Array + { + return ScriptedConversation.listScriptClasses(); + } +} diff --git a/source/funkin/data/dialogue/DialogueBoxData.hx b/source/funkin/data/dialogue/DialogueBoxData.hx new file mode 100644 index 000000000..a75a5595a --- /dev/null +++ b/source/funkin/data/dialogue/DialogueBoxData.hx @@ -0,0 +1,128 @@ +package funkin.data.dialogue; + +import funkin.data.animation.AnimationData; + +/** + * A type definition for the data for a conversation text box. + * It includes things like the sprite to use, and the font and color for the text. + * The actual text is included in the ConversationData. + * @see https://lib.haxe.org/p/json2object/ + */ +typedef DialogueBoxData = +{ + /** + * Semantic version for dialogue box data. + */ + public var version:String; + + /** + * A human readable name for the dialogue box type. + */ + public var name:String; + + /** + * The asset path for the sprite to use for the dialogue box. + * Takes a static sprite or a sprite sheet. + */ + public var assetPath:String; + + /** + * Whether to horizontally flip the dialogue box sprite. + */ + @:optional + @:default(false) + public var flipX:Bool; + + /** + * Whether to vertically flip the dialogue box sprite. + */ + @:optional + @:default(false) + public var flipY:Bool; + + /** + * Whether to disable anti-aliasing for the dialogue box sprite. + */ + @:optional + @:default(false) + public var isPixel:Bool; + + /** + * The relative horizontal and vertical offsets for the dialogue box sprite. + */ + @:optional + @:default([0, 0]) + public var offsets:Array; + + /** + * Info about how to display text in the dialogue box. + */ + public var text:DialogueBoxTextData; + + /** + * Multiply the size of the dialogue box sprite. + */ + @:optional + @:default(1) + public var scale:Float; + + /** + * If using a spritesheet for the dialogue box, the animations to use. + */ + @:optional + @:default([]) + public var animations:Array; +} + +typedef DialogueBoxTextData = +{ + /** + * The position of the text in teh box. + */ + @:optional + @:default([0, 0]) + var offsets:Array; + + /** + * The width of the + */ + @:optional + @:default(300) + var width:Int; + + /** + * The font size to use for the text. + */ + @:optional + @:default(32) + var size:Int; + + /** + * The color to use for the text. + * Use a string that can be translated to a color, like `#FF0000` for red. + */ + @:optional + @:default("#000000") + var color:String; + + /** + * The font to use for the text. + * @since v1.1.0 + * @default `Arial`, make sure to switch this! + */ + @:optional + @:default("Arial") + var fontFamily:String; + + /** + * The color to use for the shadow of the text. Use transparent to disable. + */ + var shadowColor:String; + + /** + * The width of the shadow of the text. + */ + @:optional + @:default(0) + var shadowWidth:Int; +}; diff --git a/source/funkin/data/dialogue/DialogueBoxRegistry.hx b/source/funkin/data/dialogue/DialogueBoxRegistry.hx new file mode 100644 index 000000000..87205d96c --- /dev/null +++ b/source/funkin/data/dialogue/DialogueBoxRegistry.hx @@ -0,0 +1,81 @@ +package funkin.data.dialogue; + +import funkin.play.cutscene.dialogue.DialogueBox; +import funkin.data.dialogue.DialogueBoxData; +import funkin.play.cutscene.dialogue.ScriptedDialogueBox; + +class DialogueBoxRegistry extends BaseRegistry +{ + /** + * The current version string for the dialogue box data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migrateDialogueBoxData()` function. + */ + public static final DIALOGUEBOX_DATA_VERSION:thx.semver.Version = "1.1.0"; + + public static final DIALOGUEBOX_DATA_VERSION_RULE:thx.semver.VersionRule = "1.1.x"; + + public static final instance:DialogueBoxRegistry = new DialogueBoxRegistry(); + + public function new() + { + super('DIALOGUEBOX', 'dialogue/boxes', DIALOGUEBOX_DATA_VERSION_RULE); + } + + /** + * Read, parse, and validate the JSON data and produce the corresponding data object. + */ + public function parseEntryData(id:String):Null + { + // JsonParser does not take type parameters, + // otherwise this function would be in BaseRegistry. + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + + switch (loadEntryFile(id)) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + printErrors(parser.errors, id); + return null; + } + return parser.value; + } + + /** + * Parse and validate the JSON data and produce the corresponding data object. + * + * NOTE: Must be implemented on the implementation class. + * @param contents The JSON as a string. + * @param fileName An optional file name for error reporting. + */ + public function parseEntryDataRaw(contents:String, ?fileName:String):Null + { + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + parser.fromJson(contents, fileName); + + if (parser.errors.length > 0) + { + printErrors(parser.errors, fileName); + return null; + } + return parser.value; + } + + function createScriptedEntry(clsName:String):DialogueBox + { + return ScriptedDialogueBox.init(clsName, "unknown"); + } + + function getScriptedClassNames():Array + { + return ScriptedDialogueBox.listScriptClasses(); + } +} diff --git a/source/funkin/data/dialogue/SpeakerData.hx b/source/funkin/data/dialogue/SpeakerData.hx new file mode 100644 index 000000000..e8a2eacf0 --- /dev/null +++ b/source/funkin/data/dialogue/SpeakerData.hx @@ -0,0 +1,68 @@ +package funkin.data.dialogue; + +import funkin.data.animation.AnimationData; + +/** + * A type definition for a specific speaker in a conversation. + * It includes things like what sprite to use and its available animations. + * @see https://lib.haxe.org/p/json2object/ + */ +typedef SpeakerData = +{ + /** + * Semantic version of the speaker data. + */ + public var version:String; + + /** + * A human-readable name for the speaker. + */ + public var name:String; + + /** + * The path to the asset to use for the speaker's sprite. + */ + public var assetPath:String; + + /** + * Whether the sprite should be flipped horizontally. + */ + @:optional + @:default(false) + public var flipX:Bool; + + /** + * Whether the sprite should be flipped vertically. + */ + @:optional + @:default(false) + public var flipY:Bool; + + /** + * Whether to disable anti-aliasing for the dialogue box sprite. + */ + @:optional + @:default(false) + public var isPixel:Bool; + + /** + * The offsets to apply to the sprite's position. + */ + @:optional + @:default([0, 0]) + public var offsets:Array; + + /** + * The scale to apply to the sprite. + */ + @:optional + @:default(1.0) + public var scale:Float; + + /** + * The available animations for the speaker. + */ + @:optional + @:default([]) + public var animations:Array; +} diff --git a/source/funkin/data/dialogue/SpeakerRegistry.hx b/source/funkin/data/dialogue/SpeakerRegistry.hx new file mode 100644 index 000000000..6bd301dd7 --- /dev/null +++ b/source/funkin/data/dialogue/SpeakerRegistry.hx @@ -0,0 +1,81 @@ +package funkin.data.dialogue; + +import funkin.play.cutscene.dialogue.Speaker; +import funkin.data.dialogue.SpeakerData; +import funkin.play.cutscene.dialogue.ScriptedSpeaker; + +class SpeakerRegistry extends BaseRegistry +{ + /** + * The current version string for the speaker data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migrateSpeakerData()` function. + */ + public static final SPEAKER_DATA_VERSION:thx.semver.Version = "1.0.0"; + + public static final SPEAKER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; + + public static final instance:SpeakerRegistry = new SpeakerRegistry(); + + public function new() + { + super('SPEAKER', 'dialogue/speakers', SPEAKER_DATA_VERSION_RULE); + } + + /** + * Read, parse, and validate the JSON data and produce the corresponding data object. + */ + public function parseEntryData(id:String):Null + { + // JsonParser does not take type parameters, + // otherwise this function would be in BaseRegistry. + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + + switch (loadEntryFile(id)) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + printErrors(parser.errors, id); + return null; + } + return parser.value; + } + + /** + * Parse and validate the JSON data and produce the corresponding data object. + * + * NOTE: Must be implemented on the implementation class. + * @param contents The JSON as a string. + * @param fileName An optional file name for error reporting. + */ + public function parseEntryDataRaw(contents:String, ?fileName:String):Null + { + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + parser.fromJson(contents, fileName); + + if (parser.errors.length > 0) + { + printErrors(parser.errors, fileName); + return null; + } + return parser.value; + } + + function createScriptedEntry(clsName:String):Speaker + { + return ScriptedSpeaker.init(clsName, "unknown"); + } + + function getScriptedClassNames():Array + { + return ScriptedSpeaker.listScriptClasses(); + } +} diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx index b772349bc..0e3f380f6 100644 --- a/source/funkin/data/song/SongRegistry.hx +++ b/source/funkin/data/song/SongRegistry.hx @@ -58,7 +58,7 @@ class SongRegistry extends BaseRegistry // SCRIPTED ENTRIES // var scriptedEntryClassNames:Array = getScriptedClassNames(); - log('Registering ${scriptedEntryClassNames.length} scripted entries...'); + log('Parsing ${scriptedEntryClassNames.length} scripted entries...'); for (entryCls in scriptedEntryClassNames) { @@ -84,7 +84,7 @@ class SongRegistry extends BaseRegistry var unscriptedEntryIds:Array = entryIdList.filter(function(entryId:String):Bool { return !entries.exists(entryId); }); - log('Fetching data for ${unscriptedEntryIds.length} unscripted entries...'); + log('Parsing ${unscriptedEntryIds.length} unscripted entries...'); for (entryId in unscriptedEntryIds) { try diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 5488cbbbe..f1e82aee9 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -2,7 +2,6 @@ package funkin.modding; import funkin.util.macro.ClassMacro; import funkin.modding.module.ModuleHandler; -import funkin.play.character.CharacterData.CharacterDataParser; import funkin.data.song.SongData; import funkin.data.stage.StageData; import polymod.Polymod; @@ -13,10 +12,11 @@ import funkin.data.stage.StageRegistry; import funkin.util.FileUtil; import funkin.data.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; -import funkin.play.cutscene.dialogue.ConversationDataParser; -import funkin.play.cutscene.dialogue.DialogueBoxDataParser; +import funkin.data.dialogue.ConversationRegistry; +import funkin.data.dialogue.DialogueBoxRegistry; +import funkin.data.dialogue.SpeakerRegistry; +import funkin.play.character.CharacterData.CharacterDataParser; import funkin.save.Save; -import funkin.play.cutscene.dialogue.SpeakerDataParser; import funkin.data.song.SongRegistry; class PolymodHandler @@ -208,8 +208,8 @@ class PolymodHandler { return { assetLibraryPaths: [ - "default" => "preload", "shared" => "", "songs" => "songs", "tutorial" => "tutorial", "week1" => "week1", "week2" => "week2", "week3" => "week3", - "week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "weekend1" => "weekend1", + "default" => "preload", "shared" => "shared", "songs" => "songs", "tutorial" => "tutorial", "week1" => "week1", "week2" => "week2", + "week3" => "week3", "week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "weekend1" => "weekend1", ], coreAssetRedirect: CORE_FOLDER, } diff --git a/source/funkin/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx index 16cc8b299..69e3ca48e 100644 --- a/source/funkin/play/character/CharacterData.hx +++ b/source/funkin/play/character/CharacterData.hx @@ -43,7 +43,7 @@ class CharacterDataParser { // Clear any stages that are cached if there were any. clearCharacterCache(); - trace('Loading character cache...'); + trace('[CHARACTER] Parsing all entries...'); // // UNSCRIPTED CHARACTERS diff --git a/source/funkin/play/cutscene/dialogue/Conversation.hx b/source/funkin/play/cutscene/dialogue/Conversation.hx index 817c8caf3..dc3fd8c8a 100644 --- a/source/funkin/play/cutscene/dialogue/Conversation.hx +++ b/source/funkin/play/cutscene/dialogue/Conversation.hx @@ -1,8 +1,10 @@ package funkin.play.cutscene.dialogue; +import funkin.data.IRegistryEntry; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; import flixel.util.FlxColor; +import funkin.graphics.FunkinSprite; import flixel.tweens.FlxTween; import flixel.tweens.FlxEase; import flixel.sound.FlxSound; @@ -13,27 +15,30 @@ import funkin.modding.IScriptedClass.IEventHandler; import funkin.play.cutscene.dialogue.DialogueBox; import funkin.modding.IScriptedClass.IDialogueScriptedClass; import funkin.modding.events.ScriptEventDispatcher; -import funkin.data.dialogue.ConversationData.DialogueEntryData; import flixel.addons.display.FlxPieDial; +import funkin.data.dialogue.ConversationData; +import funkin.data.dialogue.ConversationData.DialogueEntryData; +import funkin.data.dialogue.ConversationRegistry; +import funkin.data.dialogue.SpeakerData; +import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.dialogue.DialogueBoxData; +import funkin.data.dialogue.DialogueBoxRegistry; /** * A high-level handler for dialogue. * * This shit is great for modders but it's pretty elaborate for how much it'll actually be used, lolol. -Eric */ -class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass +class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry { static final CONVERSATION_SKIP_TIMER:Float = 1.5; var skipHeldTimer:Float = 0.0; /** - * DATA + * The ID of the conversation. */ - /** - * The ID of the associated dialogue. - */ - public final conversationId:String; + public final id:String; /** * The current state of the conversation. @@ -41,9 +46,9 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass var state:ConversationState = ConversationState.Start; /** - * The data for the associated dialogue. + * Conversation data as parsed from the JSON file. */ - var conversationData:ConversationData; + public final _data:ConversationData; /** * The current entry in the dialogue. @@ -54,7 +59,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass function get_currentDialogueEntryCount():Int { - return conversationData.dialogue.length; + return _data.dialogue.length; } /** @@ -73,10 +78,10 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass function get_currentDialogueEntryData():DialogueEntryData { - if (conversationData == null || conversationData.dialogue == null) return null; - if (currentDialogueEntry < 0 || currentDialogueEntry >= conversationData.dialogue.length) return null; + if (_data == null || _data.dialogue == null) return null; + if (currentDialogueEntry < 0 || currentDialogueEntry >= _data.dialogue.length) return null; - return conversationData.dialogue[currentDialogueEntry]; + return _data.dialogue[currentDialogueEntry]; } var currentDialogueLineString(get, never):String; @@ -94,7 +99,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass /** * GRAPHICS */ - var backdrop:FlxSprite; + var backdrop:FunkinSprite; var currentSpeaker:Speaker; @@ -102,14 +107,17 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass var skipTimer:FlxPieDial; - public function new(conversationId:String) + public function new(id:String) { super(); - this.conversationId = conversationId; - this.conversationData = ConversationDataParser.parseConversationData(this.conversationId); + this.id = id; + this._data = _fetchData(id); - if (conversationData == null) throw 'Could not load conversation data for conversation ID "$conversationId"'; + if (_data == null) + { + throw 'Could not parse conversation data for id: $id'; + } } public function onCreate(event:ScriptEvent):Void @@ -125,14 +133,14 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass function setupMusic():Void { - if (conversationData.music == null) return; + if (_data.music == null) return; - music = new FlxSound().loadEmbedded(Paths.music(conversationData.music.asset), true, true); + music = new FlxSound().loadEmbedded(Paths.music(_data.music.asset), true, true); music.volume = 0; - if (conversationData.music.fadeTime > 0.0) + if (_data.music.fadeTime > 0.0) { - FlxTween.tween(music, {volume: 1.0}, conversationData.music.fadeTime, {ease: FlxEase.linear}); + FlxTween.tween(music, {volume: 1.0}, _data.music.fadeTime, {ease: FlxEase.linear}); } else { @@ -145,19 +153,20 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass function setupBackdrop():Void { - backdrop = new FlxSprite(0, 0); + backdrop = new FunkinSprite(0, 0); - if (conversationData.backdrop == null) return; + if (_data.backdrop == null) return; // Play intro - switch (conversationData?.backdrop.type) + switch (_data.backdrop) { - case SOLID: - backdrop.makeGraphic(Std.int(FlxG.width), Std.int(FlxG.height), FlxColor.fromString(conversationData.backdrop.data.color)); - if (conversationData.backdrop.data.fadeTime > 0.0) + case SOLID(backdropData): + var targetColor:FlxColor = FlxColor.fromString(backdropData.color); + backdrop.makeSolidColor(Std.int(FlxG.width), Std.int(FlxG.height), targetColor); + if (backdropData.fadeTime > 0.0) { backdrop.alpha = 0.0; - FlxTween.tween(backdrop, {alpha: 1.0}, conversationData.backdrop.data.fadeTime, {ease: FlxEase.linear}); + FlxTween.tween(backdrop, {alpha: 1.0}, backdropData.fadeTime, {ease: FlxEase.linear}); } else { @@ -190,9 +199,9 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass var nextSpeakerId:String = currentDialogueEntryData.speaker; // Skip the next steps if the current speaker is already displayed. - if (currentSpeaker != null && nextSpeakerId == currentSpeaker.speakerId) return; + if (currentSpeaker != null && nextSpeakerId == currentSpeaker.id) return; - var nextSpeaker:Speaker = SpeakerDataParser.fetchSpeaker(nextSpeakerId); + var nextSpeaker:Speaker = SpeakerRegistry.instance.fetchEntry(nextSpeakerId); if (currentSpeaker != null) { @@ -241,7 +250,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass var nextDialogueBoxId:String = currentDialogueEntryData?.box; // Skip the next steps if the current speaker is already displayed. - if (currentDialogueBox != null && nextDialogueBoxId == currentDialogueBox.dialogueBoxId) return; + if (currentDialogueBox != null && nextDialogueBoxId == currentDialogueBox.id) return; if (currentDialogueBox != null) { @@ -250,7 +259,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass currentDialogueBox = null; } - var nextDialogueBox:DialogueBox = DialogueBoxDataParser.fetchDialogueBox(nextDialogueBoxId); + var nextDialogueBox:DialogueBox = DialogueBoxRegistry.instance.fetchEntry(nextDialogueBoxId); if (nextDialogueBox == null) { @@ -378,20 +387,18 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass public function startOutro():Void { - switch (conversationData?.outro?.type) + switch (_data?.outro) { - case FADE: - var fadeTime:Float = conversationData?.outro.data.fadeTime ?? 1.0; - - outroTween = FlxTween.tween(this, {alpha: 0.0}, fadeTime, + case FADE(outroData): + outroTween = FlxTween.tween(this, {alpha: 0.0}, outroData.fadeTime, { type: ONESHOT, // holy shit like the game no way startDelay: 0, onComplete: (_) -> endOutro(), }); - FlxTween.tween(this.music, {volume: 0.0}, fadeTime); - case NONE: + FlxTween.tween(this.music, {volume: 0.0}, outroData.fadeTime); + case NONE(_): // Immediately clean up. endOutro(); default: @@ -400,7 +407,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass } } - public var completeCallback:Void->Void; + public var completeCallback:() -> Void; public function endOutro():Void { @@ -596,7 +603,12 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass public override function toString():String { - return 'Conversation($conversationId)'; + return 'Conversation($id)'; + } + + static function _fetchData(id:String):Null + { + return ConversationRegistry.instance.parseEntryDataWithMigration(id, ConversationRegistry.instance.fetchEntryVersion(id)); } } diff --git a/source/funkin/play/cutscene/dialogue/DialogueBox.hx b/source/funkin/play/cutscene/dialogue/DialogueBox.hx index 4df34badc..6f8a0086a 100644 --- a/source/funkin/play/cutscene/dialogue/DialogueBox.hx +++ b/source/funkin/play/cutscene/dialogue/DialogueBox.hx @@ -1,6 +1,7 @@ package funkin.play.cutscene.dialogue; import flixel.FlxSprite; +import funkin.data.IRegistryEntry; import flixel.group.FlxSpriteGroup; import flixel.graphics.frames.FlxFramesCollection; import flixel.text.FlxText; @@ -9,18 +10,21 @@ import funkin.util.assets.FlxAnimationUtil; import funkin.modding.events.ScriptEvent; import funkin.modding.IScriptedClass.IDialogueScriptedClass; import flixel.util.FlxColor; +import funkin.data.dialogue.DialogueBoxData; +import funkin.data.dialogue.DialogueBoxRegistry; -class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass +class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry { - public final dialogueBoxId:String; + public final id:String; + public var dialogueBoxName(get, never):String; function get_dialogueBoxName():String { - return boxData?.name ?? 'UNKNOWN'; + return _data.name ?? 'UNKNOWN'; } - var boxData:DialogueBoxData; + public final _data:DialogueBoxData; /** * Offset the speaker's sprite by this much when playing each animation. @@ -88,13 +92,16 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass return this.speed; } - public function new(dialogueBoxId:String) + public function new(id:String) { super(); - this.dialogueBoxId = dialogueBoxId; - this.boxData = DialogueBoxDataParser.parseDialogueBoxData(this.dialogueBoxId); + this.id = id; + this._data = _fetchData(id); - if (boxData == null) throw 'Could not load dialogue box data for box ID "$dialogueBoxId"'; + if (_data == null) + { + throw 'Could not parse dialogue box data for id: $id'; + } } public function onCreate(event:ScriptEvent):Void @@ -115,18 +122,18 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass function loadSpritesheet():Void { - trace('[DIALOGUE BOX] Loading spritesheet ${boxData.assetPath} for ${dialogueBoxId}'); + trace('[DIALOGUE BOX] Loading spritesheet ${_data.assetPath} for ${id}'); - var tex:FlxFramesCollection = Paths.getSparrowAtlas(boxData.assetPath); + var tex:FlxFramesCollection = Paths.getSparrowAtlas(_data.assetPath); if (tex == null) { - trace('Could not load Sparrow sprite: ${boxData.assetPath}'); + trace('Could not load Sparrow sprite: ${_data.assetPath}'); return; } this.boxSprite.frames = tex; - if (boxData.isPixel) + if (_data.isPixel) { this.boxSprite.antialiasing = false; } @@ -135,9 +142,10 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass this.boxSprite.antialiasing = true; } - this.flipX = boxData.flipX; - this.globalOffsets = boxData.offsets; - this.setScale(boxData.scale); + this.flipX = _data.flipX; + this.flipY = _data.flipY; + this.globalOffsets = _data.offsets; + this.setScale(_data.scale); } public function setText(newText:String):Void @@ -184,11 +192,11 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass function loadAnimations():Void { - trace('[DIALOGUE BOX] Loading ${boxData.animations.length} animations for ${dialogueBoxId}'); + trace('[DIALOGUE BOX] Loading ${_data.animations.length} animations for ${id}'); - FlxAnimationUtil.addAtlasAnimations(this.boxSprite, boxData.animations); + FlxAnimationUtil.addAtlasAnimations(this.boxSprite, _data.animations); - for (anim in boxData.animations) + for (anim in _data.animations) { if (anim.offsets == null) { @@ -201,7 +209,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass } var animNames:Array = this.boxSprite?.animation?.getNameList() ?? []; - trace('[DIALOGUE BOX] Successfully loaded ${animNames.length} animations for ${dialogueBoxId}'); + trace('[DIALOGUE BOX] Successfully loaded ${animNames.length} animations for ${id}'); boxSprite.animation.callback = this.onAnimationFrame; boxSprite.animation.finishCallback = this.onAnimationFinished; @@ -234,16 +242,16 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass function loadText():Void { textDisplay = new FlxTypeText(0, 0, 300, '', 32); - textDisplay.fieldWidth = boxData.text.width; - textDisplay.setFormat(boxData.text.fontFamily, boxData.text.size, FlxColor.fromString(boxData.text.color), LEFT, SHADOW, - FlxColor.fromString(boxData.text.shadowColor ?? '#00000000'), false); - textDisplay.borderSize = boxData.text.shadowWidth ?? 2; + textDisplay.fieldWidth = _data.text.width; + textDisplay.setFormat(_data.text.fontFamily, _data.text.size, FlxColor.fromString(_data.text.color), LEFT, SHADOW, + FlxColor.fromString(_data.text.shadowColor ?? '#00000000'), false); + textDisplay.borderSize = _data.text.shadowWidth ?? 2; textDisplay.sounds = [FlxG.sound.load(Paths.sound('pixelText'), 0.6)]; textDisplay.completeCallback = onTypingComplete; - textDisplay.x += boxData.text.offsets[0]; - textDisplay.y += boxData.text.offsets[1]; + textDisplay.x += _data.text.offsets[0]; + textDisplay.y += _data.text.offsets[1]; add(textDisplay); } @@ -374,4 +382,14 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass } public function onScriptEvent(event:ScriptEvent):Void {} + + public override function toString():String + { + return 'DialogueBox($id)'; + } + + static function _fetchData(id:String):Null + { + return DialogueBoxRegistry.instance.parseEntryDataWithMigration(id, DialogueBoxRegistry.instance.fetchEntryVersion(id)); + } } diff --git a/source/funkin/play/cutscene/dialogue/Speaker.hx b/source/funkin/play/cutscene/dialogue/Speaker.hx index d7ed004f1..d5bffd7b0 100644 --- a/source/funkin/play/cutscene/dialogue/Speaker.hx +++ b/source/funkin/play/cutscene/dialogue/Speaker.hx @@ -1,27 +1,30 @@ package funkin.play.cutscene.dialogue; import flixel.FlxSprite; +import funkin.data.IRegistryEntry; import funkin.modding.events.ScriptEvent; import flixel.graphics.frames.FlxFramesCollection; import funkin.util.assets.FlxAnimationUtil; import funkin.modding.IScriptedClass.IDialogueScriptedClass; +import funkin.data.dialogue.SpeakerData; +import funkin.data.dialogue.SpeakerRegistry; /** * The character sprite which displays during dialogue. * * Most conversations have two speakers, with one being flipped. */ -class Speaker extends FlxSprite implements IDialogueScriptedClass +class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRegistryEntry { /** * The internal ID for this speaker. */ - public final speakerId:String; + public final id:String; /** * The full data for a speaker. */ - var speakerData:SpeakerData; + public final _data:SpeakerData; /** * A readable name for this speaker. @@ -30,7 +33,7 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass function get_speakerName():String { - return speakerData.name; + return _data.name; } /** @@ -75,14 +78,17 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass return globalOffsets = value; } - public function new(speakerId:String) + public function new(id:String) { super(); - this.speakerId = speakerId; - this.speakerData = SpeakerDataParser.parseSpeakerData(this.speakerId); + this.id = id; + this._data = _fetchData(id); - if (speakerData == null) throw 'Could not load speaker data for speaker ID "$speakerId"'; + if (_data == null) + { + throw 'Could not parse speaker data for id: $id'; + } } /** @@ -102,18 +108,18 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass function loadSpritesheet():Void { - trace('[SPEAKER] Loading spritesheet ${speakerData.assetPath} for ${speakerId}'); + trace('[SPEAKER] Loading spritesheet ${_data.assetPath} for ${id}'); - var tex:FlxFramesCollection = Paths.getSparrowAtlas(speakerData.assetPath); + var tex:FlxFramesCollection = Paths.getSparrowAtlas(_data.assetPath); if (tex == null) { - trace('Could not load Sparrow sprite: ${speakerData.assetPath}'); + trace('Could not load Sparrow sprite: ${_data.assetPath}'); return; } this.frames = tex; - if (speakerData.isPixel) + if (_data.isPixel) { this.antialiasing = false; } @@ -122,9 +128,10 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass this.antialiasing = true; } - this.flipX = speakerData.flipX; - this.globalOffsets = speakerData.offsets; - this.setScale(speakerData.scale); + this.flipX = _data.flipX; + this.flipY = _data.flipY; + this.globalOffsets = _data.offsets; + this.setScale(_data.scale); } /** @@ -141,11 +148,11 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass function loadAnimations():Void { - trace('[SPEAKER] Loading ${speakerData.animations.length} animations for ${speakerId}'); + trace('[SPEAKER] Loading ${_data.animations.length} animations for ${id}'); - FlxAnimationUtil.addAtlasAnimations(this, speakerData.animations); + FlxAnimationUtil.addAtlasAnimations(this, _data.animations); - for (anim in speakerData.animations) + for (anim in _data.animations) { if (anim.offsets == null) { @@ -158,7 +165,7 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass } var animNames:Array = this.animation.getNameList(); - trace('[SPEAKER] Successfully loaded ${animNames.length} animations for ${speakerId}'); + trace('[SPEAKER] Successfully loaded ${animNames.length} animations for ${id}'); } /** @@ -271,4 +278,14 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass } public function onScriptEvent(event:ScriptEvent):Void {} + + public override function toString():String + { + return 'Speaker($id)'; + } + + static function _fetchData(id:String):Null + { + return SpeakerRegistry.instance.parseEntryDataWithMigration(id, SpeakerRegistry.instance.fetchEntryVersion(id)); + } } diff --git a/source/funkin/ui/debug/dialogue/ConversationDebugState.hx b/source/funkin/ui/debug/dialogue/ConversationDebugState.hx new file mode 100644 index 000000000..33a6f365a --- /dev/null +++ b/source/funkin/ui/debug/dialogue/ConversationDebugState.hx @@ -0,0 +1,76 @@ +package funkin.ui.debug.dialogue; + +import flixel.FlxState; +import funkin.modding.events.ScriptEventDispatcher; +import funkin.modding.events.ScriptEvent; +import flixel.util.FlxColor; +import funkin.ui.MusicBeatState; +import funkin.data.dialogue.ConversationData; +import funkin.data.dialogue.ConversationRegistry; +import funkin.data.dialogue.DialogueBoxData; +import funkin.data.dialogue.DialogueBoxRegistry; +import funkin.data.dialogue.SpeakerData; +import funkin.data.dialogue.SpeakerRegistry; +import funkin.play.cutscene.dialogue.Conversation; +import funkin.play.cutscene.dialogue.DialogueBox; +import funkin.play.cutscene.dialogue.Speaker; + +/** + * A state with displays a conversation with no background. + * Used for testing. + * @param conversationId The conversation to display. + */ +class ConversationDebugState extends MusicBeatState +{ + final conversationId:String = 'senpai'; + + var conversation:Conversation; + + public function new() + { + super(); + + // TODO: Fix this BS + Paths.setCurrentLevel('week6'); + } + + public override function create():Void + { + conversation = ConversationRegistry.instance.fetchEntry(conversationId); + conversation.completeCallback = onConversationComplete; + add(conversation); + + ScriptEventDispatcher.callEvent(conversation, new ScriptEvent(CREATE, false)); + } + + function onConversationComplete():Void + { + remove(conversation); + conversation = null; + } + + public override function dispatchEvent(event:ScriptEvent):Void + { + // Dispatch event to conversation script. + ScriptEventDispatcher.callEvent(conversation, event); + } + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + if (conversation != null) + { + if (controls.CUTSCENE_ADVANCE) conversation.advanceConversation(); + + if (controls.CUTSCENE_SKIP) + { + conversation.trySkipConversation(elapsed); + } + else + { + conversation.trySkipConversation(-1); + } + } + } +} diff --git a/source/funkin/util/assets/DataAssets.hx b/source/funkin/util/assets/DataAssets.hx index ee75dd207..b38c993fe 100644 --- a/source/funkin/util/assets/DataAssets.hx +++ b/source/funkin/util/assets/DataAssets.hx @@ -9,7 +9,8 @@ class DataAssets public static function listDataFilesInPath(path:String, ?suffix:String = '.json'):Array { - var textAssets = openfl.utils.Assets.list(); + var textAssets = openfl.utils.Assets.list(TEXT); + var queryPath = buildDataPath(path); var results:Array = []; From 2d4ca436ecc58c930954cdff32a9c005f7584582 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 7 Feb 2024 18:46:09 -0500 Subject: [PATCH 05/18] Update Polymod to drastically improve efficiency of openfl.utils.Assets.list() --- Project.xml | 6 ++++-- assets | 2 +- hmm.json | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Project.xml b/Project.xml index 9755a5e37..40f309e1f 100644 --- a/Project.xml +++ b/Project.xml @@ -52,6 +52,7 @@ + @@ -82,14 +83,15 @@ If we can exclude the `mods` folder from the manifest, we can re-enable this line. --> - - + + + diff --git a/assets b/assets index 251d4640b..e364e0ea6 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 251d4640bd77ee0f0b6122a13f123274c43dd3f5 +Subproject commit e364e0ea65fe943675afb7171ba748d9bda28269 diff --git a/hmm.json b/hmm.json index fa9a67057..4a0895034 100644 --- a/hmm.json +++ b/hmm.json @@ -149,7 +149,7 @@ "name": "polymod", "type": "git", "dir": null, - "ref": "cb11a95d0159271eb3587428cf4b9602e46dc469", + "ref": "6cec79e4f322fbb262170594ed67ab72b4714810", "url": "https://github.com/larsiusprime/polymod" }, { From 4491465bf9c6b047f6970b6d32c45861036fae3b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 8 Feb 2024 19:22:28 -0500 Subject: [PATCH 06/18] Implement difficulty rank into Metadata toolbox. --- assets | 2 +- source/funkin/data/song/SongData.hx | 4 ++-- .../ui/debug/charting/ChartEditorState.hx | 23 +++++++++++++++++++ .../toolboxes/ChartEditorMetadataToolbox.hx | 5 ++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/assets b/assets index 251d4640b..02da1c72a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 251d4640bd77ee0f0b6122a13f123274c43dd3f5 +Subproject commit 02da1c72a22ddf8cc167f014888a94ebf468d5b1 diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 52b9c19d6..5959041a7 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -418,10 +418,10 @@ class SongPlayData implements ICloneable /** * The difficulty ratings for this song as displayed in Freeplay. - * Key is a difficulty ID or `default`. + * Key is a difficulty ID. */ @:optional - @:default(['default' => 1]) + @:default(['normal' => 0]) public var ratings:Map; /** diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index d0326be30..14d3c606f 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1294,6 +1294,29 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState return currentSongChartData.events = value; } + /** + * Convenience property to get the rating for this difficulty in the Freeplay menu. + */ + var currentSongChartDifficultyRating(get, set):Int; + + function get_currentSongChartDifficultyRating():Int + { + var result:Null = currentSongMetadata.playData.ratings.get(selectedDifficulty); + if (result == null) + { + // Initialize to the default value if not set. + currentSongMetadata.playData.ratings.set(selectedDifficulty, 0); + return 0; + } + return result; + } + + function set_currentSongChartDifficultyRating(value:Int):Int + { + currentSongMetadata.playData.ratings.set(selectedDifficulty, value); + return value; + } + var currentSongNoteStyle(get, set):String; function get_currentSongNoteStyle():String diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx index 5d8c25bae..f85307c64 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx @@ -151,6 +151,10 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox labelScrollSpeed.text = 'Scroll Speed: ${chartEditorState.currentSongChartScrollSpeed}x'; }; + inputDifficultyRating.onChange = function(event:UIEvent) { + chartEditorState.currentSongChartDifficultyRating = event.target.value; + }; + buttonCharacterOpponent.onClick = function(_) { chartEditorState.openCharacterDropdown(CharacterType.DAD, false); }; @@ -175,6 +179,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox inputStage.value = chartEditorState.currentSongMetadata.playData.stage; inputNoteStyle.value = chartEditorState.currentSongMetadata.playData.noteStyle; inputBPM.value = chartEditorState.currentSongMetadata.timeChanges[0].bpm; + inputDifficultyRating.value = chartEditorState.currentSongChartDifficultyRating; inputScrollSpeed.value = chartEditorState.currentSongChartScrollSpeed; labelScrollSpeed.text = 'Scroll Speed: ${chartEditorState.currentSongChartScrollSpeed}x'; frameVariation.text = 'Variation: ${chartEditorState.selectedVariation.toTitleCase()}'; From 6b8c0637e34e263f763b5d2a528acbcaac875e26 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 9 Feb 2024 23:36:01 -0500 Subject: [PATCH 07/18] pico death retry position fix --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 1941ec605..8378b838b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1941ec605d2da5a27e41588515d13d5bfc882582 +Subproject commit 8378b838bae80739eacd9a0316f9b49f888185f6 From 3ede16be16def08cf108bcfc8c0aa36ce191b199 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 10 Feb 2024 00:50:07 -0500 Subject: [PATCH 08/18] Fix issue where Freeplay could be silent sometimes --- source/funkin/ui/freeplay/FreeplayState.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index b23ca6e54..3c6b52c6f 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -164,9 +164,9 @@ class FreeplayState extends MusicBeatSubState isDebug = true; #end - if (FlxG.sound.music != null) + if (FlxG.sound.music == null || (FlxG.sound.music != null && !FlxG.sound.music.playing)) { - if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu')); + FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu')); } // Add a null entry that represents the RANDOM option From bb0b1764698407902f8fce32c8967dcd335b8094 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 10 Feb 2024 00:51:02 -0500 Subject: [PATCH 09/18] Fix bug with building specifically for freeplay. --- source/funkin/InitState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 23bc255f1..4e6f12eed 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -240,7 +240,7 @@ class InitState extends FlxState #elseif LEVEL // -DLEVEL=week1 -DDIFFICULTY=hard startLevel(defineLevel(), defineDifficulty()); #elseif FREEPLAY // -DFREEPLAY - FlxG.switchState(new FreeplayState()); + FlxG.switchState(new funkin.ui.freeplay.FreeplayState()); #elseif ANIMATE // -DANIMATE FlxG.switchState(new funkin.ui.debug.anim.FlxAnimateTest()); #elseif WAVEFORM // -DWAVEFORM From aaeea320e5ef188a71eaa69ba7b901be1d9c94e1 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 10 Feb 2024 00:51:53 -0500 Subject: [PATCH 10/18] Fix darnell's animations being flipped --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 8378b838b..ec284b57d 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8378b838bae80739eacd9a0316f9b49f888185f6 +Subproject commit ec284b57dec8903ad79c5d90bac063cbbc327705 From f3630dfff97d74400774d86dcb63fdcd079e8f0b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 10 Feb 2024 02:17:07 -0500 Subject: [PATCH 11/18] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index ec284b57d..590db1448 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit ec284b57dec8903ad79c5d90bac063cbbc327705 +Subproject commit 590db1448fb132a29160540b4656b2474f22f727 From f12847dc5965f9d57f490dfc7884f0a508086f52 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 10 Feb 2024 02:23:58 -0500 Subject: [PATCH 12/18] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index e364e0ea6..ba25cf692 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e364e0ea65fe943675afb7171ba748d9bda28269 +Subproject commit ba25cf692f7a09174860d50f011fef5643f68b0c From 31ef89e9e0285c36dd7074270c08385d43304cdb Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 10 Feb 2024 02:31:32 -0500 Subject: [PATCH 13/18] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 02da1c72a..594853037 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 02da1c72a22ddf8cc167f014888a94ebf468d5b1 +Subproject commit 594853037cbea06caa5c141b0d9ed3736818e592 From 86488a0964891aa2a411807e316c2b5777eda95c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 18:09:36 -0500 Subject: [PATCH 14/18] Ensure the CrashHandler ALWAYS initializes first so we have stack traces. --- source/Main.hx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/source/Main.hx b/source/Main.hx index 754d0732f..a40fda29d 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -33,8 +33,10 @@ class Main extends Sprite public static function main():Void { - haxe.Log.trace = funkin.util.logging.AnsiTrace.trace; - funkin.util.logging.AnsiTrace.traceBF(); + // We need to make the crash handler LITERALLY FIRST so nothing EVER gets past it. + CrashHandler.initialize(); + CrashHandler.queryStatus(); + Lib.current.addChild(new Main()); } @@ -42,7 +44,12 @@ class Main extends Sprite { super(); - // TODO: Replace this with loadEnabledMods(). + // Initialize custom logging. + haxe.Log.trace = funkin.util.logging.AnsiTrace.trace; + funkin.util.logging.AnsiTrace.traceBF(); + + // Load mods to override assets. + // TODO: Replace with loadEnabledMods() once the user can configure the mod list. funkin.modding.PolymodHandler.loadAllMods(); if (stage != null) @@ -82,10 +89,6 @@ class Main extends Sprite * -Eric */ - CrashHandler.initialize(); - - CrashHandler.queryStatus(); - initHaxeUI(); fpsCounter = new FPS(10, 3, 0xFFFFFF); From 4346214dc8f099265a9ccbef53ee79b4e5254665 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 18:18:50 -0500 Subject: [PATCH 15/18] Update Polymod to resolve a crash thrown when no mods are installed. --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index 4a0895034..43cbbb08a 100644 --- a/hmm.json +++ b/hmm.json @@ -149,7 +149,7 @@ "name": "polymod", "type": "git", "dir": null, - "ref": "6cec79e4f322fbb262170594ed67ab72b4714810", + "ref": "0b53e478bc375ec51b760b650201ac7a965d2ef4", "url": "https://github.com/larsiusprime/polymod" }, { From 92f52b2c3f62c3402d95562de45849ded60b02df Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 12 Feb 2024 23:15:18 -0500 Subject: [PATCH 16/18] flixel debugging on test builds --- .github/workflows/build-shit.yml | 8 ++++---- Project.xml | 12 +++--------- source/funkin/InitState.hx | 13 ++++++------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build-shit.yml b/.github/workflows/build-shit.yml index 76126d106..8ea3b16f3 100644 --- a/.github/workflows/build-shit.yml +++ b/.github/workflows/build-shit.yml @@ -31,7 +31,7 @@ jobs: sudo apt-get install -y libx11-dev xorg-dev libgl-dev libxi-dev libxext-dev libasound2-dev libxinerama-dev libxrandr-dev libgl1-mesa-dev - name: build game run: | - haxelib run lime build html5 -release --times + haxelib run lime build html5 -release --times -DGITHUB_BUILD ls - uses: ./.github/actions/upload-itch with: @@ -68,7 +68,7 @@ jobs: key: ${{ runner.os }}-build-win-${{ github.ref_name }}-${{ hashFiles('**/hmm.json') }} - name: build game run: | - haxelib run lime build windows -release -DNO_REDIRECT_ASSETS_FOLDER + haxelib run lime build windows -release -DNO_REDIRECT_ASSETS_FOLDER -DGITHUB_BUILD dir env: HXCPP_COMPILE_CACHE: "${{ runner.temp }}\\hxcpp_cache" @@ -110,7 +110,7 @@ jobs: key: ${{ runner.os }}-build-mac-${{ github.ref_name }}-${{ hashFiles('**/hmm.json') }} - name: Build game run: | - haxelib run lime build macos -release --times + haxelib run lime build macos -release --times -DGITHUB_BUILD ls env: HXCPP_COMPILE_CACHE: "${{ runner.temp }}/hxcpp_cache" @@ -119,7 +119,7 @@ jobs: butler-key: ${{ secrets.BUTLER_API_KEY}} build-dir: export/release/macos/bin target: macos - + # test-unit-win: # needs: create-nightly-win # runs-on: windows-latest diff --git a/Project.xml b/Project.xml index 40f309e1f..560baeadf 100644 --- a/Project.xml +++ b/Project.xml @@ -91,7 +91,8 @@ NOT USING A DIRECT THING TO THE ASSET!!! --> - + + @@ -118,7 +119,7 @@ - + @@ -211,13 +212,6 @@ -
- - - - -
- --> --> diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 625a33ad7..21946819f 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -144,13 +144,12 @@ class InitState extends FlxState // Make errors and warnings less annoying. // TODO: Disable this so we know to fix warnings. - if (false) - { - LogStyle.ERROR.openConsole = false; - LogStyle.ERROR.errorSound = null; - LogStyle.WARNING.openConsole = false; - LogStyle.WARNING.errorSound = null; - } + #if FORCE_DEBUG_VERSION + LogStyle.ERROR.openConsole = false; + LogStyle.ERROR.errorSound = null; + LogStyle.WARNING.openConsole = false; + LogStyle.WARNING.errorSound = null; + #end #end // From 6e0f577d54f48b77c6e1e7e58cbeddc5311d8c57 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 13 Feb 2024 00:29:46 -0500 Subject: [PATCH 17/18] debugger fixes --- source/funkin/InitState.hx | 5 ++--- source/funkin/play/GameOverSubState.hx | 1 + source/funkin/play/PauseSubState.hx | 2 +- source/funkin/play/PlayState.hx | 20 +++++++++---------- .../ui/debug/charting/ChartEditorState.hx | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 21946819f..09afe6cf4 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -89,7 +89,7 @@ class InitState extends FlxState // // FLIXEL DEBUG SETUP // - #if debug + #if (debug || FORCE_DEBUG_VERSION) // Disable using ~ to open the console (we use that for the Editor menu) FlxG.debugger.toggleKeys = [F2]; @@ -141,16 +141,15 @@ class InitState extends FlxState FlxG.sound.music.pause(); FlxG.sound.music.time += FlxG.elapsed * 1000; }); + #end // Make errors and warnings less annoying. - // TODO: Disable this so we know to fix warnings. #if FORCE_DEBUG_VERSION LogStyle.ERROR.openConsole = false; LogStyle.ERROR.errorSound = null; LogStyle.WARNING.openConsole = false; LogStyle.WARNING.errorSound = null; #end - #end // // FLIXEL TRANSITIONS diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index 36f72237e..f6d75c61b 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -90,6 +90,7 @@ class GameOverSubState extends MusicBeatSubState { animationSuffix = ""; musicSuffix = ""; + blueBallSuffix = ""; } override public function create() diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index c9039ce40..77999b60a 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -168,7 +168,7 @@ class PauseSubState extends MusicBeatSubState var downP = controls.UI_DOWN_P; var accepted = controls.ACCEPT; - #if debug + #if (debug || FORCE_DEBUG_VERSION) // to pause the game and get screenshots easy, press H on pause menu! if (FlxG.keys.justPressed.H) { diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index aee9f2210..c9d41f254 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -638,7 +638,7 @@ class PlayState extends MusicBeatSubState rightWatermarkText.cameras = [camHUD]; // Initialize some debug stuff. - #if debug + #if (debug || FORCE_DEBUG_VERSION) // Display the version number (and git commit hash) in the bottom right corner. this.rightWatermarkText.text = Constants.VERSION; @@ -907,7 +907,7 @@ class PlayState extends MusicBeatSubState // Disable updates, preventing animations in the background from playing. persistentUpdate = false; - #if debug + #if (debug || FORCE_DEBUG_VERSION) if (FlxG.keys.pressed.THREE) { // TODO: Change the key or delete this? @@ -918,7 +918,7 @@ class PlayState extends MusicBeatSubState { #end persistentDraw = false; - #if debug + #if (debug || FORCE_DEBUG_VERSION) } #end @@ -1368,7 +1368,7 @@ class PlayState extends MusicBeatSubState // Add the stage to the scene. this.add(currentStage); - #if debug + #if (debug || FORCE_DEBUG_VERSION) FlxG.console.registerObject('stage', currentStage); #end } @@ -1458,7 +1458,7 @@ class PlayState extends MusicBeatSubState { currentStage.addCharacter(girlfriend, GF); - #if debug + #if (debug || FORCE_DEBUG_VERSION) FlxG.console.registerObject('gf', girlfriend); #end } @@ -1467,7 +1467,7 @@ class PlayState extends MusicBeatSubState { currentStage.addCharacter(boyfriend, BF); - #if debug + #if (debug || FORCE_DEBUG_VERSION) FlxG.console.registerObject('bf', boyfriend); #end } @@ -1478,7 +1478,7 @@ class PlayState extends MusicBeatSubState // Camera starts at dad. cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y); - #if debug + #if (debug || FORCE_DEBUG_VERSION) FlxG.console.registerObject('dad', dad); #end } @@ -2253,7 +2253,7 @@ class PlayState extends MusicBeatSubState })); } - #if debug + #if (debug || FORCE_DEBUG_VERSION) // 1: End the song immediately. if (FlxG.keys.justPressed.ONE) endSong(); @@ -2267,7 +2267,7 @@ class PlayState extends MusicBeatSubState // 9: Toggle the old icon. if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon(); - #if debug + #if (debug || FORCE_DEBUG_VERSION) // PAGEUP: Skip forward two sections. // SHIFT+PAGEUP: Skip forward twenty sections. if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 20 : 2); @@ -2768,7 +2768,7 @@ class PlayState extends MusicBeatSubState FlxG.camera.focusOn(cameraFollowPoint.getPosition()); } - #if debug + #if (debug || FORCE_DEBUG_VERSION) /** * Jumps forward or backward a number of sections in the song. * Accounts for BPM changes, does not prevent death from skipped notes. diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index dab79a21c..759a54ec7 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3183,7 +3183,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState handleTestKeybinds(); handleHelpKeybinds(); - #if debug + #if (debug || FORCE_DEBUG_VERSION) handleQuickWatch(); #end From 1ad17aa904153333b6ec26936a8c81c6c5055f2a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 13 Feb 2024 00:45:29 -0500 Subject: [PATCH 18/18] flixel hmm update --- assets | 2 +- hmm.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets b/assets index 594853037..160acbd8a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 594853037cbea06caa5c141b0d9ed3736818e592 +Subproject commit 160acbd8a854a9f677ef7587396340e79a5ea6ca diff --git a/hmm.json b/hmm.json index b42a67928..7321e731f 100644 --- a/hmm.json +++ b/hmm.json @@ -11,7 +11,7 @@ "name": "flixel", "type": "git", "dir": null, - "ref": "07c6018008801972d12275690fc144fcc22e3de6", + "ref": "25c84b29665329f7c6366342542a3978f29300ee", "url": "https://github.com/FunkinCrew/flixel" }, {