Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Ne_Eo
2024-10-18 02:53:24 +02:00
12 changed files with 200 additions and 79 deletions
+13 -2
View File
@@ -114,17 +114,28 @@ class Paths
return scriptPath;
}
static public function chart(song:String, ?difficulty:String = "normal"):String {
static public function chart(song:String, ?difficulty:String = "normal"):String
{
difficulty = difficulty.toLowerCase();
song = song.toLowerCase();
return getPath('songs/$song/charts/$difficulty.json', null);
}
static public function character(character:String):String {
inline static public function character(character:String):String
{
return getPath('data/characters/$character.xml', null);
}
/**
* Gets the name of a registered font.
* @param font The font's path (if it's already passed as a font name, the same name will be returned)
*/
inline static public function getFontName(font:String)
{
return OpenFlAssets.exists(font, FONT) ? OpenFlAssets.getFont(font).fontName : font;
}
inline static public function font(key:String)
{
return getPath('fonts/$key');
@@ -0,0 +1,21 @@
package funkin.backend.scripting.events;
import funkin.game.Character;
import haxe.xml.Access;
final class CharacterNodeEvent extends CancellableEvent {
/**
* The character instance
*/
public var character:Character;
/**
* The node which is currently being parsed
*/
public var node:Access;
/**
* The name of the node, quicker access than e.node.name
*/
public var name:String;
}
@@ -0,0 +1,16 @@
package funkin.backend.scripting.events;
import funkin.game.Character;
import haxe.xml.Access;
final class CharacterXMLEvent extends CancellableEvent {
/**
* The character instance
*/
public var character:Character;
/**
* The xml
*/
public var xml:Access;
}
@@ -62,6 +62,7 @@ class AudioSwitchFix {
}
}
@:dox(hide)
typedef PlayingSound = {
var sound:FlxSound;
var time:Float;
+1 -1
View File
@@ -596,7 +596,7 @@ class CoolUtil
var fmt = new FlxTextFormat();
fmt.format.color = Reflect.hasField(fmtt, "color") ? FlxColor.fromString(fmtt.color) : text.color;
fmt.format.font = Reflect.hasField(fmtt, "font") ? Paths.font(fmtt.font) : text.font;
fmt.format.font = Reflect.hasField(fmtt, "font") ? Paths.getFontName(Paths.font(fmtt.font)) : text.font;
fmt.format.size = Reflect.hasField(fmtt, "size") ? Std.parseInt(fmtt.size) : text.size;
fmt.format.italic = Reflect.hasField(fmtt, "italic") ? fmtt.italic == "true" : text.italic;
fmt.format.bold = Reflect.hasField(fmtt, "bold") ? fmtt.bold == "true" : text.bold;
+52
View File
@@ -6,6 +6,13 @@ import funkin.backend.FunkinSprite.XMLAnimType;
import funkin.backend.FunkinSprite;
import funkin.game.Character;
import funkin.backend.system.ErrorCode;
import funkin.backend.FunkinSprite.XMLAnimType;
import flixel.util.FlxColor;
import haxe.xml.Access;
import funkin.backend.scripting.Script;
import funkin.backend.scripting.DummyScript;
import funkin.backend.scripting.ScriptPack;
import flixel.util.typeLimit.OneOfTwo;
import funkin.backend.system.interfaces.IOffsetCompatible;
import haxe.xml.Access;
@@ -328,6 +335,51 @@ class XMLUtil {
}
}
class XMLImportedScriptInfo {
public var path:String;
public var shortLived:Bool = false;
public var loadBefore:Bool = true;
public var importStageSprites:Bool = false; // maybe will change later?? - Nex
public var parentScriptPack:ScriptPack = null;
public function new(path:String, parentScriptPack:ScriptPack) {
this.parentScriptPack = parentScriptPack;
this.path = path;
}
public function getScript():Script
return parentScriptPack == null ? null : parentScriptPack.getByPath(path);
public static function prepareInfos(node:Access, parentScriptPack:ScriptPack, ?onScriptPreLoad:XMLImportedScriptInfo->Void):XMLImportedScriptInfo {
if (!node.has.script || parentScriptPack == null) return null;
var folder = node.getAtt("folder").getDefault("data/scripts/");
if (!folder.endsWith("/")) folder += "/";
var path = Paths.script(folder + node.getAtt("script"));
var daScript = Script.create(path);
if (daScript is DummyScript) {
Logs.trace('Script Extension at ${path} does not exist.', ERROR);
return null;
}
var infos = new XMLImportedScriptInfo(daScript.path, parentScriptPack);
infos.shortLived = node.getAtt("isShortLived") == "true" || node.getAtt("shortLived") == "true";
infos.importStageSprites = node.getAtt("importStageSprites") == "true";
@:privateAccess infos.loadBefore = shouldLoadBefore(node);
if (onScriptPreLoad != null) onScriptPreLoad(infos);
parentScriptPack.add(daScript);
daScript.set("scriptInfo", infos);
daScript.load();
return infos;
}
@:dox(hide) public static inline function shouldLoadBefore(node:Access):Bool
return node.getAtt("loadBefore") != "false";
}
typedef AnimData = {
var name:String;
var anim:String;
+67 -16
View File
@@ -8,6 +8,9 @@ import flixel.util.FlxColor;
import funkin.backend.FunkinSprite;
import funkin.backend.scripting.DummyScript;
import funkin.backend.scripting.Script;
import funkin.backend.scripting.ScriptPack;
import funkin.backend.scripting.events.CharacterNodeEvent;
import funkin.backend.scripting.events.CharacterXMLEvent;
import funkin.backend.scripting.events.DanceEvent;
import funkin.backend.scripting.events.DirectionAnimEvent;
import funkin.backend.scripting.events.PlayAnimContext;
@@ -44,8 +47,13 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public var cameraOffset:FlxPoint = FlxPoint.get(0, 0);
public var globalOffset:FlxPoint = FlxPoint.get(0, 0);
public var script:Script;
public var xml:Access;
public var scripts:ScriptPack;
public var xmlImportedScripts:Array<XMLImportedScriptInfo> = [];
public var script(default, set):Script;
public function prepareInfos(node:Access)
return XMLImportedScriptInfo.prepareInfos(node, scripts, (infos) -> xmlImportedScripts.push(infos));
public var idleSuffix:String = "";
public var stunned(default, set):Bool = false;
@@ -72,16 +80,15 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
script = Script.create(Paths.script(Path.withoutExtension(Paths.xml('characters/$curCharacter')), null, true));
else
script = new DummyScript(curCharacter);
script.setParent(this);
script.load();
buildCharacter(xml);
script.call("create");
scripts.call("create");
if (script == null)
script = new DummyScript(curCharacter);
script.call("postCreate");
scripts.call("postCreate");
}
@:noCompletion var __swappedLeftRightAnims:Bool = false;
@@ -118,7 +125,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
override function update(elapsed:Float) {
super.update(elapsed);
script.call("update", [elapsed]);
scripts.call("update", [elapsed]);
if (stunned) {
__stunnedTime += elapsed;
if (__stunnedTime > 5 / 60)
@@ -137,7 +144,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if(debugMode) return;
var event = EventManager.get(DanceEvent).recycle(danced);
script.call("onDance", [event]);
scripts.call("onDance", [event]);
if (event.cancelled) return;
if (isDanceLeftDanceRight)
@@ -167,14 +174,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
*/
public var danceOnBeat:Bool = true;
public override function beatHit(curBeat:Int) {
script.call("beatHit", [curBeat]);
scripts.call("beatHit", [curBeat]);
if (danceOnBeat && (curBeat + beatOffset) % beatInterval == 0 && !__lockAnimThisFrame)
tryDance();
}
public override function stepHit(curStep:Int)
script.call("stepHit", [curStep]);
scripts.call("stepHit", [curStep]);
@:noCompletion var __reverseDrawProcedure:Bool = false;
public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect {
@@ -237,7 +244,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public override function playAnim(AnimName:String, ?Force:Bool, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) {
var event = EventManager.get(PlayAnimEvent).recycle(AnimName, Force, Reversed, Frame, Context);
script.call("onPlayAnim", [event]);
scripts.call("onPlayAnim", [event]);
if (event.cancelled) return;
super.playAnim(event.animName, event.force, event.context, event.reverse, event.startingFrame);
@@ -252,16 +259,16 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
var event = EventManager.get(PointEvent).recycle(
midpoint.x + (isPlayer ? -100 : 150) + globalOffset.x + cameraOffset.x,
midpoint.y - 100 + globalOffset.y + cameraOffset.y);
script.call("onGetCamPos", [event]);
scripts.call("onGetCamPos", [event]);
midpoint.put();
return new FlxPoint(event.x, event.y);
}
public override function destroy() {
if(script != null) {
script.call('destroy');
script.destroy();
if(scripts != null) {
scripts.call('destroy');
scripts.destroy();
}
super.destroy();
@@ -304,9 +311,18 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
}
public inline function buildCharacter(xml:Access) {
this.xml = xml; // Modders wassup :D
for(node in xml.elements)
switch(node.name) {
case "use-extension" | "extension" | "ext":
if (!XMLImportedScriptInfo.shouldLoadBefore(node)) continue;
prepareInfos(node);
}
xml = scripts.event("onCharacterXMLParsed", EventManager.get(CharacterXMLEvent).recycle(this, xml)).xml;
sprite = curCharacter;
spriteAnimType = BEAT;
this.xml = xml; // Modders wassup :D
if (xml.x.exists("isPlayer")) playerOffsets = (xml.x.get("isPlayer") == "true");
if (xml.x.exists("x")) globalOffset.x = Std.parseFloat(xml.x.get("x"));
@@ -330,9 +346,19 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if (hasInterval) beatInterval = Std.parseInt(xml.x.get("interval"));
loadSprite(Paths.image('characters/$sprite'));
for(node in xml.elements) {
switch(node.name) {
case "anim":
XMLUtil.addXMLAnimation(this, node);
case "use-extension" | "extension" | "ext":
if (XMLImportedScriptInfo.shouldLoadBefore(node)) continue;
prepareInfos(node);
default:
// nothing
}
for (anim in xml.nodes.anim)
XMLUtil.addXMLAnimation(this, anim);
scripts.event("onCharacterNodeParsed", EventManager.get(CharacterNodeEvent).recycle(this, node, node.name));
}
for (attribute in xml.x.attributes())
if (!characterProperties.contains(attribute))
@@ -340,6 +366,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
fixChar(__switchAnims, !hasInterval);
dance();
for (info in xmlImportedScripts) if (info.shortLived) {
var script = info.getScript();
if (script == null) continue;
scripts.remove(script);
script.destroy();
}
}
public static var characterProperties:Array<String> = [
@@ -423,6 +457,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
return stunned = b;
}
// ---- Backwards compat ----
// Interval at which the character will dance (higher number = slower dance)
@:noCompletion public var danceInterval(get, set):Int;
@:noCompletion private function set_danceInterval(v:Int)
@@ -430,6 +465,22 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
@:noCompletion private function get_danceInterval()
return beatInterval;
private function set_script(script:Script):Script {
if (scripts == null) (scripts = new ScriptPack("Character")).setParent(this);
var lastIndex = scripts.scripts.indexOf(this.script);
if(lastIndex >= 0) {
if(script == null) // last != null && new == null
scripts.scripts.splice(lastIndex, 1);
else // last != null && new != null
scripts.scripts[lastIndex] = script;
} else if(script != null) // last == null
scripts.insert(0, script);
return this.script = script;
}
// ---- end of Backwards compat ----
public static var FALLBACK_CHARACTER:String = "bf";
public static var FALLBACK_DEAD_CHARACTER:String = "bf-dead";
+8 -8
View File
@@ -873,12 +873,12 @@ class PlayState extends MusicBeatState
Conductor.songPosition = 0;
Conductor.songPosition -= Conductor.crochet * introLength - Conductor.songOffset;
var swagCounter:Int = 0;
startTimer = new FlxTimer().start(Conductor.crochet / 1000, function(tmr:FlxTimer)
{
countdown(swagCounter++);
}, introLength);
if(introLength > 0) {
var swagCounter:Int = 0;
startTimer = new FlxTimer().start(Conductor.crochet / 1000, (tmr:FlxTimer) -> {
countdown(swagCounter++);
}, introLength);
}
scripts.call("onPostStartCountdown");
}
@@ -1055,7 +1055,7 @@ class PlayState extends MusicBeatState
vocals.pause();
}
if (!startTimer.finished)
if (startTimer != null && !startTimer.finished)
startTimer.active = false;
}
@@ -1075,7 +1075,7 @@ class PlayState extends MusicBeatState
resyncVocals();
}
if (!startTimer.finished)
if (startTimer != null && !startTimer.finished)
startTimer.active = true;
paused = false;
+9 -47
View File
@@ -2,6 +2,9 @@ package funkin.game;
import flixel.FlxState;
import flixel.math.FlxPoint;
import haxe.xml.Access;
import funkin.backend.utils.XMLUtil.XMLImportedScriptInfo;
import funkin.backend.system.interfaces.IBeatReceiver;
import funkin.backend.scripting.DummyScript;
import funkin.backend.scripting.Script;
import funkin.backend.scripting.events.StageNodeEvent;
@@ -30,6 +33,9 @@ class Stage extends FlxBasic implements IBeatReceiver {
public function setStagesSprites(script:Script)
for (k=>e in stageSprites) script.set(k, e);
public function prepareInfos(node:Access)
return XMLImportedScriptInfo.prepareInfos(node, PlayState.instance.scripts, (infos) -> xmlImportedScripts.push(infos));
public function new(stage:String, ?state:FlxState) {
super();
@@ -136,7 +142,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
PlayState.instance.add(PlayState.instance.comboGroup);
PlayState.instance.comboGroup;
case "use-extension" | "extension" | "ext":
if (__shouldLoadBefore(node) || XMLImportedScriptInfo.prepareInfos(node, this) == null) continue;
if (XMLImportedScriptInfo.shouldLoadBefore(node) || prepareInfos(node) == null) continue;
null;
default: null;
}
@@ -201,13 +207,10 @@ class Stage extends FlxBasic implements IBeatReceiver {
@:dox(hide) private function __pushNcheckNode(array:Array<Access>, node:Access) {
array.push(node);
if ((node.name == "use-extension" || node.name == "extension" || node.name == "ext") && __shouldLoadBefore(node))
XMLImportedScriptInfo.prepareInfos(node, this);
if ((node.name == "use-extension" || node.name == "extension" || node.name == "ext") && XMLImportedScriptInfo.shouldLoadBefore(node))
prepareInfos(node);
}
@:dox(hide) private static inline function __shouldLoadBefore(node:Access):Bool
return node.getAtt("loadBefore") != "false";
public function addCharPos(name:String, node:Access, ?nonXMLInfo:StageCharPosInfo):StageCharPos {
var charPos = new StageCharPos();
charPos.visible = charPos.active = false;
@@ -317,45 +320,4 @@ typedef StageCharPosInfo = {
var y:Float;
var flip:Bool;
var scroll:Float;
}
class XMLImportedScriptInfo {
public var path:String;
public var shortLived:Bool = false;
public var loadBefore:Bool = true;
public var importStageSprites:Bool = false;
public function new(path:String)
this.path = path;
public function getScript():Script
return PlayState.instance == null ? null : PlayState.instance.scripts.getByPath(path);
public static function prepareInfos(node:Access, ?stage:Stage):XMLImportedScriptInfo {
if (!node.has.script || PlayState.instance == null) return null;
var folder = node.getAtt("folder").getDefault("data/scripts/");
if (!folder.endsWith("/")) folder += "/";
var path = Paths.script(folder + node.getAtt("script"));
var daScript = Script.create(path);
if (daScript is DummyScript) {
var msg = 'Script Extension at ${path} does not exist';
if (stage != null) msg += ' through stage "${stage.stageName}"';
Logs.trace(msg + ".", ERROR);
return null;
}
var infos = new XMLImportedScriptInfo(daScript.path);
infos.shortLived = node.getAtt("isShortLived") == "true";
infos.importStageSprites = node.getAtt("importStageSprites") == "true";
@:privateAccess infos.loadBefore = Stage.__shouldLoadBefore(node);
if (stage != null) stage.xmlImportedScripts.push(infos);
PlayState.instance.scripts.add(daScript);
daScript.set("scriptInfo", infos);
daScript.load();
return infos;
}
}
+1 -1
View File
@@ -318,7 +318,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
babyArrow.scrollSpeed = data.scrollSpeed;
var event = EventManager.get(StrumCreationEvent).recycle(babyArrow, PlayState.instance.strumLines.members.indexOf(this), i, animPrefix);
event.__doAnimation = !MusicBeatState.skipTransIn;
event.__doAnimation = !MusicBeatState.skipTransIn && (PlayState.instance != null ? PlayState.instance.introLength > 0 : true);
event = PlayState.instance.scripts.event("onStrumCreation", event);
if (!event.cancelled) {
@@ -11,9 +11,11 @@ using StringTools;
class CreditsCodename extends funkin.options.OptionsScreen {
public var error:Bool = false;
public var author:String = "FNF-CNE-Devs";
public var totalContributions:Int = 0;
public var mainDevCol:FlxColor = 0xFF9C35D5;
public var minContrCol:FlxColor = 0xFFB4A7DA;
public var totalContributions:Int = 0;
public var contribFormats:Array<FlxTextFormatMarkerPair> = [];
public override function new()
{
@@ -58,12 +60,17 @@ class CreditsCodename extends funkin.options.OptionsScreen {
if(parent == null || parent.treeParent == null) return;
var text:String = parent.treeParent.pathDesc.text;
parent.treeParent.pathDesc.text = "";
parent.treeParent.pathDesc.applyMarkup(text, [
parent.treeParent.pathDesc.applyMarkup(text, contribFormats = [
new FlxTextFormatMarkerPair(new FlxTextFormat(mainDevCol), '*'),
new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(minContrCol, mainDevCol, Options.contributors[curSelected].contributions / totalContributions)), '~')
]);
}
override function close() {
super.close();
for (frmt in contribFormats) parent.treeParent.pathDesc.removeFormat(frmt.format);
}
public function checkUpdate():Bool {
var curTime:Float = Date.now().getTime();
if(Options.lastUpdated != null && curTime < Options.lastUpdated + 120000) return false; // Fuck you Github rate limits - Nex