Merge branch 'main' into base-fnf-port-remake

This commit is contained in:
⍚~Nex
2024-09-01 03:51:58 +02:00
16 changed files with 408 additions and 18 deletions
+3 -4
View File
@@ -96,16 +96,15 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public override function update(elapsed:Float)
{
super.update(elapsed);
if (animateAtlas != null)
animateAtlas.update(elapsed);
// hate how it looks like but hey at least its optimized and fast - Nex
if (!debugMode && isAnimFinished()) {
var name = getAnimName() + '-loop';
if (animation.exists(name))
if (hasAnimation(name))
playAnim(name, false, lastAnimContext);
}
if (animateAtlas != null)
animateAtlas.update(elapsed);
}
public function loadSprite(path:String, Unique:Bool = false, Key:String = null)
@@ -14,8 +14,9 @@ class EventManager {
var index = eventKeys.indexOf(c);
if (index < 0) {
eventKeys.push(c);
eventValues.push(Type.createInstance(c, []));
return cast eventValues.last();
var ret;
eventValues.push(ret = Type.createInstance(c, []));
return cast ret;
}
return cast eventValues[index];
+31 -3
View File
@@ -1,10 +1,38 @@
package funkin.backend.scripting;
class ModState extends MusicBeatState {
/**
* Name of HScript file in assets/data/states.
*/
public static var lastName:String = null;
public function new(stateName:String) {
if (stateName != null)
lastName = stateName;
/**
* Last Optional extra data.
*/
public static var lastData:Dynamic = null;
/**
* Optional extra data.
*/
public var data:Dynamic = null;
/**
* ModState Constructor.
* Inherits from MusicBeatState and allows the execution of an HScript from assets/data/states passed via parameters.
*
* @param _stateName Name or path to a HScript file from assets/data/states.
* @param _data Optional extra Dynamic data passed from a previous state (JSON suggested).
*/
public function new(_stateName:String, ?_data:Dynamic) {
if(_stateName != null && _stateName != lastName) {
lastName = _stateName;
lastData = null;
}
if(_data != null)
lastData = _data;
data = lastData;
super(true, lastName);
}
}
+31 -3
View File
@@ -1,10 +1,38 @@
package funkin.backend.scripting;
class ModSubState extends MusicBeatSubstate {
/**
* Name of HScript file in assets/data/states.
*/
public static var lastName:String = null;
public function new(stateName:String) {
if (stateName != null)
lastName = stateName;
/**
* Last Optional extra data.
*/
public static var lastData:Dynamic = null;
/**
* Optional extra data.
*/
public var data:Dynamic = null;
/**
* ModSubState Constructor.
* Inherits from MusicBeatSubstate and allows the execution of an HScript from assets/data/states passed via parameters.
*
* @param _stateName Name or path to a HScript file from assets/data/states.
* @param _data Optional extra Dynamic data passed from a previous state (JSON suggested).
*/
public function new(_stateName:String, ?_data:Dynamic) {
if(_stateName != null && _stateName != lastName) {
lastName = _stateName;
lastData = null;
}
if(_data != null)
lastData = _data;
data = lastData;
super(true, lastName);
}
}
+79
View File
@@ -0,0 +1,79 @@
package funkin.backend.system;
import lime.utils.AssetLibrary;
import lime.utils.AssetType;
import funkin.backend.assets.IModsAssetLibrary;
import funkin.backend.assets.ScriptedAssetLibrary;
/**
* A class that reads the `flags.ini` file, allowing to read settable Flags (customs too).
*/
@:build(funkin.backend.system.macros.FlagMacro.build())
class Flags {
public static var SONGS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override";
public static var WEEKS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override";
// Internal stuff
@:bypass public static var customFlags:Map<String, String> = [];
public static function load() {
var flags:Map<String, String> = [];
final flagsPath = Paths.getPath("flags.ini");
for(lib in Paths.assetsTree.libraries) {
if(lib.exists(flagsPath, AssetType.TEXT)) {
var data:String = lib.getAsset(flagsPath, AssetType.TEXT);
var trimmed:String;
var splitContent = [for(e in data.split("\n")) if ((trimmed = e.trim()) != "") trimmed];
for(line in splitContent) {
if (line.startsWith(";")) continue;
if (line.startsWith("#")) continue;
if (line.startsWith("//")) continue;
if(line.length == 0) continue;
if(line.charAt(0) == "[" && line.charAt(line.length-1) == "]") continue;
var index = line.indexOf("=");
if(index == -1) continue;
var name = line.substr(0, index).trim();
var value = line.substr(index+1).trim();
if (value.length > 1 && value.charCodeAt(0) == '"'.code && value.charCodeAt(value.length-1) == '"'.code)
value = value.substr(1, value.length - 2);
if (value.length == 0 || name.length == 0)
continue;
if(!flags.exists(name))
flags[name] = value;
}
}
}
customFlags = [];
reset();
for(name=>value in flags)
if(!parse(name, value)) {
customFlags.set(name, value);
}
}
public static function getCleanLibraryName(e:AssetLibrary) {
var l = e;
if (l is openfl.utils.AssetLibrary) {
var al = cast(l, openfl.utils.AssetLibrary);
@:privateAccess
if (al.__proxy != null) l = al.__proxy;
}
if (l is ScriptedAssetLibrary)
return '${cast(l, ScriptedAssetLibrary).scriptName} (${cast(l, ScriptedAssetLibrary).modName})';
else if (l is IModsAssetLibrary)
return '${cast(l, IModsAssetLibrary).modName}';
else
return Std.string(e);
}
}
@@ -8,6 +8,7 @@ import funkin.backend.assets.ModsFolder;
import funkin.backend.chart.EventsData;
import funkin.menus.BetaWarningState;
import funkin.menus.TitleState;
import funkin.backend.system.framerate.Framerate;
/**
* Simple state used for loading the game
@@ -59,10 +60,14 @@ class MainState extends FlxState {
Main.refreshAssets();
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder);
Flags.load();
DiscordUtil.init();
EventsData.reloadEvents();
TitleState.initialized = false;
if(Framerate.isLoaded)
Framerate.instance.reload();
if (betaWarningShown)
FlxG.switchState(new TitleState());
else {
@@ -9,6 +9,9 @@ class CodenameBuildField extends TextField {
defaultTextFormat = Framerate.textFormat;
autoSize = LEFT;
multiline = wordWrap = false;
text = 'Codename Engine ${Constants.RELEASE_CYCLE}\n${Constants.COMMIT_MESSAGE}';
reload();
}
public function reload()
text = 'Codename Engine ${Constants.RELEASE_CYCLE}\n${Constants.COMMIT_MESSAGE}';
}
@@ -82,6 +82,16 @@ class Framerate extends Sprite {
#end
}
public function reload() {
for(c in categories)
c.reload();
#if SHOW_BUILD_ON_FPS
codenameBuildField.reload();
#end
memoryCounter.reload();
fpsCounter.reload();
}
private function __addCategory(category:FramerateCategory) {
categories.push(category);
__addToList(category);
@@ -39,6 +39,8 @@ class FramerateCategory extends Sprite {
this.text.y = this.title.y + this.title.height + 2;
}
public function reload() {}
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
@@ -37,6 +37,8 @@ class FramerateCounter extends Sprite {
}
}
public function reload() {}
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
@@ -29,6 +29,8 @@ class MemoryCounter extends Sprite {
memoryPeakText.alpha = 0.5;
}
public function reload() {}
public override function __enterFrame(t:Int) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
@@ -0,0 +1,211 @@
package funkin.backend.system.macros;
import haxe.macro.Context;
import haxe.macro.Expr;
using StringTools;
using haxe.macro.Tools;
class FlagMacro {
public static function build():Array<Field> {
var fields = Context.getBuildFields();
var clRef = Context.getLocalClass();
if (clRef == null)
return fields;
var cl = clRef.get();
var resetExprs:Array<Expr> = [];
var parserExprs:Array<Expr> = [];
for (field in fields) {
var skip = false;
for(meta in field.meta) {
if(meta.name == ":bypass") {
skip = true;
break;
}
}
if(skip) continue;
switch (field.kind) {
case FVar(type, expr):
if(expr == null)
Context.error('Flag ' + field.name + ' must have a default value', field.pos);
switch(expr.expr) {
case EConst(CIdent("true")) | EConst(CIdent("false")):
type = macro: Bool;
default:
if(type == null)
Context.error('Flag ' + field.name + ' must have a type', field.pos);
}
var parser:Expr = null;
var customCheck:Expr = null;
switch(type) {
case macro: Array<TrimmedString>:
field.kind = FVar(macro: Array<String>, expr);
parser = macro value.split(",").map((e) -> e.trim());
case macro: Array<String>:
parser = macro value.split(",");
case macro: Array<Bool>:
parser = macro value.split(",").map((e) -> {
e = e.trim();
e == "true" || e == "t" || e == "1";
});
case macro: Int:
parser = macro Std.parseInt(value);
case macro: String:
parser = macro value;
case macro: Bool:
parser = macro value == "true" || value == "t" || value == "1";
case TPath({name: "Allow", pack: [], params: params}):
final NONE = 0;
final STRING = 1;
final INT = 2;
var chosenType = NONE;
var values:Array<String> = [];
for(param in params) {
switch(param) {
case TPExpr(e):
switch(e.expr) {
case EConst(CString(s, kind)):
if(chosenType != NONE && chosenType != STRING)
Context.error("Flag " + field.name + " Allow<> can only have one type", field.pos);
chosenType = STRING;
values.push(s);
case EConst(CInt(num)):
if(chosenType != NONE && chosenType != INT)
Context.error("Flag " + field.name + " Allow<> can only have one type", field.pos);
chosenType = INT;
values.push(num);
default:
Context.error("Flag " + field.name + " Allow<> unknown type", field.pos);
}
default:
Context.error("Flag " + field.name + " Unknown type when using Allow<>", field.pos);
}
}
var didFind = false;
for(v in values) {
switch(expr.expr) {
case EConst(CString(s, kind)):
if(v == s)
didFind = true;
case EConst(CInt(num)):
if(v == num)
didFind = true;
default:
}
}
if(!didFind)
Context.error("Flag " + field.name + "'s Allow<> must have a default value that is allowed, " + expr.toString() + " is not allowed", field.pos);
if(chosenType == NONE)
Context.error("Flag " + field.name + "'s Allow<> must have atleast one value", field.pos);
var errorMessage = 'Flag ${field.name} must be one of ${values.join(", ")}';
var checkExpr = macro value == $v{values.shift()};
for(v in values)
checkExpr = macro $checkExpr || value == $v{v};
if(chosenType == STRING) {
parser = macro value;
field.kind = FVar(macro: String, expr);
}
else if(chosenType == INT) {
parser = macro Std.parseInt(value);
field.kind = FVar(macro: Int, expr);
} else {
field.kind = FVar(macro: Any, expr);
}
customCheck = macro @:mergeBlock {
if(name == $v{field.name}) {
if($checkExpr)
$i{field.name} = $parser;
else
throw $v{errorMessage};
return true;
}
}
case TPath({name: "Array", pack: []}):
Context.error("Flag " + field.name + " cannot be an Array that isnt a String or Bool or TrimmedString", field.pos);
case TPath({name: "Map", pack: []}):
Context.error("Flag " + field.name + " cannot be a Map<K, V>", field.pos);
default:
Context.error("Flag " + field.name + " must be either a Bool, Int, String, Array<String>, Array<Bool> or Array<TrimmedString>", field.pos);
}
if(parser == null) {
Context.error("Flag " + field.name + " must be either a Bool, Int, String, Array<String>, Array<Bool> or Array<TrimmedString>", field.pos);
continue;
}
resetExprs.push(macro $i{field.name} = ${expr});
// parse(name: String, value: String)
if(customCheck != null) {
parserExprs.push(customCheck);
} else {
parserExprs.push(macro @:mergeBlock {
if(name == $v{field.name}) {
$i{field.name} = $parser;
return true;
}
});
}
default:
// nothing
}
}
fields.push({
name: "reset",
access: [APublic, AStatic],
kind: FFun({
args: [],
expr: macro $b{resetExprs},
ret: macro: Void
}),
pos: Context.currentPos(),
doc: null,
meta: []
});
fields.push({
name: "parse",
access: [APublic, AStatic],
kind: FFun({
args: [{name: "name", type: macro: String}, {name: "value", type: macro: String}],
expr: macro {
@:mergeBlock $b{parserExprs};
return false;
},
ret: macro: Bool
}),
pos: Context.currentPos(),
doc: null,
meta: []
});
//var printer = new haxe.macro.Printer();
//trace(printer.printField(fields[fields.length - 2]));
//trace(printer.printField(fields[fields.length - 1]));
return fields;
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ class IniUtil {
var name = line.substr(0, index).trim();
var value = line.substr(index+1).trim();
if (value.startsWith("\"") && value.endsWith("\""))
if (value.length > 1 && value.charCodeAt(0) == '"'.code && value.charCodeAt(value.length-1) == '"'.code)
value = value.substr(1, value.length - 2);
if (value.length == 0 || name.length == 0)
+1
View File
@@ -22,6 +22,7 @@ import flixel.math.FlxMath;
import flixel.tweens.FlxEase;
import flixel.util.FlxDestroyUtil;
import funkin.backend.system.Flags;
import funkin.Constants;
import funkin.menus.ui.Alphabet;
+11 -2
View File
@@ -465,8 +465,17 @@ class FreeplaySonglist {
public static function get(useTxt:Bool = true) {
var songList = new FreeplaySonglist();
if (songList.getSongsFromSource(MODS, useTxt))
songList.getSongsFromSource(SOURCE, useTxt);
switch(Flags.SONGS_LIST_MOD_MODE) {
case 'prepend':
songList.getSongsFromSource(MODS, useTxt);
songList.getSongsFromSource(SOURCE, useTxt);
case 'append':
songList.getSongsFromSource(SOURCE, useTxt);
songList.getSongsFromSource(MODS, useTxt);
default /*case 'override'*/:
if (songList.getSongsFromSource(MODS, useTxt))
songList.getSongsFromSource(SOURCE, useTxt);
}
return songList;
}
+12 -2
View File
@@ -233,8 +233,18 @@ class StoryMenuState extends MusicBeatState {
public function loadXMLs() {
// CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
var weeks:Array<String> = [];
if (getWeeksFromSource(weeks, MODS))
getWeeksFromSource(weeks, SOURCE);
switch(Flags.WEEKS_LIST_MOD_MODE) {
case 'prepend':
getWeeksFromSource(weeks, MODS);
getWeeksFromSource(weeks, SOURCE);
case 'append':
getWeeksFromSource(weeks, SOURCE);
getWeeksFromSource(weeks, MODS);
default /*case 'override'*/:
if (getWeeksFromSource(weeks, MODS))
getWeeksFromSource(weeks, SOURCE);
}
for(k=>weekName in weeks) {
var week:Access = null;