Merge branch 'main' into translations-system

This commit is contained in:
Ne_Eo
2025-01-09 20:26:29 +01:00
119 changed files with 1207 additions and 416 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ import flixel.sound.FlxSound;
import lime.media.AudioBuffer;
// Thank you yosh :DDD -lunar
// (https://github.com/FNF-CNE-Devs/YoshiCrafterEngine/blob/main/source/WaveformSprite.hx)
// (https://github.com/CodenameCrew/YoshiCrafterEngine/blob/main/source/WaveformSprite.hx)
class AudioAnalyzer {
public var buffer:AudioBuffer;
var __peakByte:Float = 0;
+58 -16
View File
@@ -40,9 +40,15 @@ import openfl.geom.ColorTransform;
using StringTools;
/**
* Various utilities, that have no specific Util class.
**/
@:allow(funkin.game.PlayState)
class CoolUtil
{
/**
* Gets the last exception stack. Useful for debugging.
*/
public static function getLastExceptionStack():String {
return CallStack.toString(CallStack.exceptionStack());
}
@@ -149,14 +155,14 @@ class CoolUtil
* Gets file attributes from a file or a folder adding eventual missing folders in the path
* (WARNING: Only works on `windows` for now. On other platforms the attributes' value it's always going to be `0` -thanks to the wrapper you can also use `isNothing` for checking- but still creates eventual missing folders if the platforms allows it to).
* @param path Path to the file or folder
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
* @param useAbsolute If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
* @return The attributes through the `FileAttributeWrapper`
*/
@:noUsing public static inline function safeGetAttributes(path:String, useAbsol:Bool = true):FileAttributeWrapper {
@:noUsing public static inline function safeGetAttributes(path:String, useAbsolute:Bool = true):FileAttributeWrapper {
#if sys
addMissingFolders(Path.directory(path));
var result = NativeAPI.getFileAttributes(path, useAbsol);
var result = NativeAPI.getFileAttributes(path, useAbsolute);
if(result.isNothing) Logs.trace('The file where it has been tried to get the attributes from, might be corrupted or inexistent (code: ${result.getValue()})', WARNING);
return result;
#else
@@ -169,15 +175,15 @@ class CoolUtil
* (WARNING: Only works on `windows` for now. On other platforms the return code it's always going to be `0` but still creates eventual missing folders if the platforms allows it to).
* @param path Path to the file or folder
* @param attrib The attribute(s) to set (WARNING: There are some non settable attributes, such as the `COMPRESSED` one)
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
* @param useAbsolute If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
* @return The result code: `0` means that it failed setting
*/
@:noUsing public static inline function safeSetAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsol:Bool = true):Int {
@:noUsing public static inline function safeSetAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsolute:Bool = true):Int {
// yes, i'm aware that FileAttribute is also an Int so need to include it too, but at least like this we don't have to make cast sometimes while passing the arguments - Nex
#if sys
addMissingFolders(Path.directory(path));
var result = NativeAPI.setFileAttributes(path, attrib, useAbsol);
var result = NativeAPI.setFileAttributes(path, attrib, useAbsolute);
if(result == 0) Logs.trace('Failed to set attributes to $path with a code of: $result', WARNING);
return result;
#else
@@ -190,14 +196,14 @@ class CoolUtil
* (WARNING: Only works on `windows` for now. On other platforms the return code it's always going to be `0` but still creates eventual missing folders if the platforms allows it to).
* @param path Path to the file or folder
* @param attrib The attribute(s) to add (WARNING: There are some non settable attributes, such as the `COMPRESSED` one)
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
* @param useAbsolute If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
* @return The result code: `0` means that it failed setting
*/
@:noUsing public static inline function safeAddAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsol:Bool = true):Int {
@:noUsing public static inline function safeAddAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsolute:Bool = true):Int {
#if sys
addMissingFolders(Path.directory(path));
var result = NativeAPI.addFileAttributes(path, attrib, useAbsol);
var result = NativeAPI.addFileAttributes(path, attrib, useAbsolute);
if(result == 0) Logs.trace('Failed to add attributes to $path with a code of: $result', WARNING);
return result;
#else
@@ -210,14 +216,14 @@ class CoolUtil
* (WARNING: Only works on `windows` for now. On other platforms the return code it's always going to be `0` but still creates eventual missing folders if the platforms allows it to).
* @param path Path to the file or folder
* @param attrib The attribute(s) to remove (WARNING: There are some non settable attributes, such as the `COMPRESSED` one)
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
* @param useAbsolute If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
* @return The result code: `0` means that it failed setting
*/
@:noUsing public static inline function safeRemoveAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsol:Bool = true):Int {
@:noUsing public static inline function safeRemoveAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsolute:Bool = true):Int {
#if sys
addMissingFolders(Path.directory(path));
var result = NativeAPI.removeFileAttributes(path, attrib, useAbsol);
var result = NativeAPI.removeFileAttributes(path, attrib, useAbsolute);
if(result == 0) Logs.trace('Failed to remove attributes to $path with a code of: $result', WARNING);
return result;
#else
@@ -467,7 +473,7 @@ class CoolUtil
var beatsPerMeasure:Float = Flags.DEFAULT_BEATS_PER_MEASURE;
var stepsPerBeat:Int = Flags.DEFAULT_STEPS_PER_BEAT;
// Check later, i dont think timeSignParsed can contain null, only nan
// Check later, i don't think timeSignParsed can contain null, only nan
if (timeSignParsed.length == 2 && !timeSignParsed.contains(null)) {
beatsPerMeasure = timeSignParsed[0] == null || timeSignParsed[0] <= 0 ? Flags.DEFAULT_BEATS_PER_MEASURE : cast timeSignParsed[0];
stepsPerBeat = timeSignParsed[1] == null || timeSignParsed[1] <= 0 ? Flags.DEFAULT_STEPS_PER_BEAT : cast timeSignParsed[1];
@@ -823,6 +829,15 @@ class CoolUtil
return r;
}
/**
* Quantizes a value to a certain amount.
* Example: `quantize(2.5543, 1)` will return `2.0`
* Example: `quantize(2.5543, 10)` will return `2.5`
* Example: `quantize(2.5543, 100)` will return `2.55`
*
* @param Value Value to quantize
* @param Quant Quantization amount
*/
@:noUsing public static inline function quantize(Value:Float, Quant:Float) {
return Math.fround(Value * Quant) / Quant;
}
@@ -840,9 +855,19 @@ class CoolUtil
frontEnd.music = music;
}
/**
* Gets the FlxEase from a string.
* @param mainEase Main ease
* @param suffix Suffix (Ignored if `mainEase` is `linear`)
*/
@:noUsing public static inline function flxeaseFromString(mainEase:String, suffix:String)
return Reflect.field(FlxEase, mainEase + (mainEase == "linear" ? "" : suffix));
/**
* Sorts an array alphabetically.
* @param array Array to sort
* @param lowercase Whenever the array should be sorted in lowercase
*/
public static function sortAlphabetically(array:Array<String>, ?lowercase:Bool=false) {
array.sort(function(a1, a2):Int {
if(lowercase) {
@@ -856,6 +881,11 @@ class CoolUtil
return array;
}
/**
* Pushes an element to an array, but only if it doesn't already exist.
* @param array Array to push to
* @param element Element to push
*/
public static inline function pushOnce<T>(array:Array<T>, element:T) {
#if (haxe >= "4.0.0")
if (!array.contains(element))
@@ -867,6 +897,11 @@ class CoolUtil
}
#if !(haxe >= "4.0.0")
/**
* Checks if an array contains an element.
* @param array Array to check
* @param element Element to check
*/
public static inline function contains<T>(array:Array<T>, element:T) {
return array.indexOf(element) != -1;
}
@@ -932,9 +967,10 @@ class CoolUtil
* Converts an array of numbers into a string of ranges.
* Example: [1,2,3,5,7,8,9,8,7,6,5] -> "1..3,5,7..9,8..5"
* @param numbers Array of numbers
* @param separator Separator between ranges
* @return String representing the ranges
*/
public static function formatNumberRange(numbers:Array<Int>, seperator:String = ","):String {
public static function formatNumberRange(numbers:Array<Int>, separator:String = ","):String {
if (numbers.length == 0) return "";
var result:Array<String> = [];
@@ -971,9 +1007,15 @@ class CoolUtil
i++;
}
return result.join(seperator);
return result.join(separator);
}
/**
* Deep flattens an array.
* Example: `deepFlatten([1, [2, 3], 4])` will return `[1, 2, 3, 4]`
* @param arr Array to flatten
* @param result Result array
*/
public static function deepFlatten(arr:Array<Dynamic>, ?result:Array<Dynamic>):Array<Dynamic> {
if(arr == null) return [];
if(result == null) result = [];
@@ -989,7 +1031,7 @@ class CoolUtil
}
/**
* SFXs to play using `playMenuSFX`.
* SFXs to play using `CoolUtil.playMenuSFX`.
*/
enum abstract CoolSfx(Int) from Int {
var SCROLL = 0;
@@ -19,9 +19,11 @@ class FunkinParentDisabler extends FlxBasic {
var __timers:Array<FlxTimer>;
var __sounds:Array<FlxSound>;
var __replaceUponDestroy:Bool;
public function new(replaceUponDestroy:Bool = false) {
var __restoreUponDestroy:Bool;
public function new(replaceUponDestroy:Bool = false, restoreUponDestroy:Bool = true) {
super();
__replaceUponDestroy = replaceUponDestroy;
__restoreUponDestroy = restoreUponDestroy;
@:privateAccess {
// tweens
__tweens = FlxTween.globalManager._tweens.copy();
@@ -53,6 +55,11 @@ class FunkinParentDisabler extends FlxBasic {
public override function destroy() {
super.destroy();
@:privateAccess {
if (!__restoreUponDestroy) {
for(t in __tweens) { t.cancel(); t.destroy(); };
for(t in __timers) { t.cancel(); t.destroy(); };
return;
}
if (__replaceUponDestroy) {
FlxTween.globalManager._tweens = __tweens;
FlxTimer.globalManager._timers = __timers;
-118
View File
@@ -1,118 +0,0 @@
package funkin.backend.utils;
import flixel.util.FlxSave;
import lime.app.Application;
import openfl.Lib;
/**
* Class used for saves WITHOUT going through the struggle of type checks
* Just add your save variables the way you would do in the Options.hx file.
* The macro will automatically generate the `flush` and `load` functions.
*/
@:build(funkin.backend.system.macros.FunkinSaveMacro.build("save", "flush", "load"))
class FunkinSave {
public static var highscores:Map<HighscoreEntry, SongScore> = [];
/**
* ONLY OPEN IF YOU WANT TO EDIT FUNCTIONS RELATED TO SAVING, LOADING OR HIGHSCORES.
*/
#if REGION
@:dox(hide) @:doNotSave
private static var __eventAdded = false;
@:doNotSave
public static var save:FlxSave;
public static function init() {
//trace(Application.current.meta.get('save-path'));
//trace(Application.current.meta.get('save-name'));
save = new FlxSave();
save.bind('save-default', #if sys 'YoshiCrafter29/CodenameEngine' #else 'CodenameEngine' #end);
load();
if (!__eventAdded) {
Lib.application.onExit.add(function(i:Int) {
Logs.traceColored([
Logs.getPrefix("FunkinSave"),
Logs.logText("Saving "),
Logs.logText("savedata", GREEN),
Logs.logText("...")
], VERBOSE);
flush();
});
__eventAdded = true;
}
}
/**
* Returns the high-score for a song.
* @param name Song name
* @param diff Song difficulty
* @param changes Changes made to that song in freeplay.
*/
public static inline function getSongHighscore(name:String, diff:String, ?changes:Array<HighscoreChange>) {
if (changes == null) changes = [];
return safeGetHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), changes));
}
public static inline function setSongHighscore(name:String, diff:String, highscore:SongScore, ?changes:Array<HighscoreChange>) {
if (changes == null) changes = [];
if (safeRegisterHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), changes), highscore)) {
flush();
return true;
}
return false;
}
public static inline function getWeekHighscore(name:String, diff:String)
return safeGetHighscore(HWeekEntry(name.toLowerCase(), diff.toLowerCase()));
public static inline function setWeekHighscore(name:String, diff:String, highscore:SongScore) {
if (safeRegisterHighscore(HWeekEntry(name.toLowerCase(), diff.toLowerCase()), highscore)) {
flush();
return true;
}
return false;
}
private static function safeGetHighscore(entry:HighscoreEntry):SongScore {
if (!highscores.exists(entry)) {
return {
score: 0,
accuracy: 0,
misses: 0,
hits: [],
date: null
};
}
return highscores.get(entry);
}
private static function safeRegisterHighscore(entry:HighscoreEntry, highscore:SongScore) {
var oldHigh = safeGetHighscore(entry);
if (oldHigh.date == null || oldHigh.score < highscore.score) {
highscores.set(entry, highscore);
return true;
}
return false;
}
#end
}
enum HighscoreEntry {
HWeekEntry(weekName:String, difficulty:String);
HSongEntry(songName:String, difficulty:String, changes:Array<HighscoreChange>);
}
enum HighscoreChange {
CCoopMode;
COpponentMode;
}
typedef SongScore = {
var score:Int;
var accuracy:Float;
var misses:Int;
var hits:Map<String, Int>;
var date:String;
}
+31
View File
@@ -14,6 +14,12 @@ import openfl.system.System;
using StringTools;
/**
* Tools that are related to memory.
* Including garbage collection, and memory usage, and hardware info.
*
* DISCLAIMER: Hardware info is only available on Native platforms.
**/
class MemoryUtil {
public static var disableCount:Int = 0;
@@ -34,12 +40,18 @@ class MemoryUtil {
public static function init() {}
/**
* Does a minor garbage collection.
*/
public static function clearMinor() {
#if (cpp || java || neko)
Gc.run(false);
#end
}
/**
* Does a full garbage collection.
*/
public static function clearMajor() {
#if cpp
Gc.run(true);
@@ -51,18 +63,29 @@ class MemoryUtil {
#end
}
/**
* Enables garbage collection.
*/
public static function enable() {
#if (cpp || hl)
Gc.enable(true);
#end
}
/**
* Disables garbage collection.
* Fyi: doesn't fully disable garbage collection, but prevents it from running as much.
*/
public static function disable() {
#if (cpp || hl)
Gc.enable(false);
#end
}
/**
* Gets the total memory of the system.
* Output depends on the hardware.
*/
public static function getTotalMem():Float
{
#if windows
@@ -76,6 +99,10 @@ class MemoryUtil {
#end
}
/**
* Gets the current memory usage of the app.
* DISCLAIMER: This gets the memory usage that is taken up by Haxe, not the actual memory usage of the app.
*/
public static inline function currentMemUsage() {
#if cpp
return Gc.memInfo64(Gc.MEM_INFO_USAGE);
@@ -89,6 +116,10 @@ class MemoryUtil {
}
/**
* Gets the memory type of the system.
* Output depends on the platform, and hardware.
*/
public static function getMemType():String {
#if windows
var memoryMap:Map<Int, String> = [
+10 -10
View File
@@ -35,9 +35,9 @@ class NativeAPI {
/**
* Gets the specified file's (or folder) attributes.
*/
public static function getFileAttributesRaw(path:String, useAbsol:Bool = true):Int {
public static function getFileAttributesRaw(path:String, useAbsolute:Bool = true):Int {
#if windows
if(useAbsol) path = sys.FileSystem.absolutePath(path);
if(useAbsolute) path = sys.FileSystem.absolutePath(path);
return Windows.getFileAttributes(path);
#else
return -1;
@@ -47,16 +47,16 @@ class NativeAPI {
/**
* Gets the specified file's (or folder) attributes and passes it to `FileAttributeWrapper`.
*/
public static function getFileAttributes(path:String, useAbsol:Bool = true):FileAttributeWrapper {
return new FileAttributeWrapper(getFileAttributesRaw(path, useAbsol));
public static function getFileAttributes(path:String, useAbsolute:Bool = true):FileAttributeWrapper {
return new FileAttributeWrapper(getFileAttributesRaw(path, useAbsolute));
}
/**
* Sets the specified file's (or folder) attributes. If it fails, the return value is `0`.
*/
public static function setFileAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsol:Bool = true):Int {
public static function setFileAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsolute:Bool = true):Int {
#if windows
if(useAbsol) path = sys.FileSystem.absolutePath(path);
if(useAbsolute) path = sys.FileSystem.absolutePath(path);
return Windows.setFileAttributes(path, attrib is FileAttributeWrapper ? cast(attrib, FileAttributeWrapper).getValue() : cast(attrib, Int));
#else
return 0;
@@ -66,9 +66,9 @@ class NativeAPI {
/**
* Removes from the specified file's (or folder) one (or more) specific attribute.
*/
public static function addFileAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsol:Bool = true):Int {
public static function addFileAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsolute:Bool = true):Int {
#if windows
return setFileAttributes(path, getFileAttributesRaw(path, useAbsol) | cast(attrib, Int), useAbsol);
return setFileAttributes(path, getFileAttributesRaw(path, useAbsolute) | cast(attrib, Int), useAbsolute);
#else
return 0;
#end
@@ -77,9 +77,9 @@ class NativeAPI {
/**
* Removes from the specified file's (or folder) one (or more) specific attribute.
*/
public static function removeFileAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsol:Bool = true):Int {
public static function removeFileAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsolute:Bool = true):Int {
#if windows
return setFileAttributes(path, getFileAttributesRaw(path, useAbsol) & ~cast(attrib, Int), useAbsol);
return setFileAttributes(path, getFileAttributesRaw(path, useAbsolute) & ~cast(attrib, Int), useAbsolute);
#else
return 0;
#end
+1 -1
View File
@@ -38,7 +38,7 @@ class NdllUtil {
return Reflect.makeVarArgs(function(a:Array<Dynamic>) {
// This generates horrific code
return funkin.backend.system.macros.Utils.generateReflectionLike(25, "func", "a");
//return Reflect.callMethod(null, func, a); // wouldnt work for some reason, maybe cause like c++ functions doesnt have reflection enabled
//return Reflect.callMethod(null, func, a); // wouldn't work for some reason, maybe cause like c++ functions doesn't have reflection enabled
});
#else
Logs.warn('NDLLs are not supported on this platform.');
+11 -3
View File
@@ -1,14 +1,16 @@
package funkin.backend.utils;
/**
* All types of utils only usable in a sorted array.
* Tools to work with sorted arrays, in a more efficient way.
*
* DISCLAIMER: All types of utils only usable in a sorted array.
*/
class SortedArrayUtil {
/**
* Gets the index of a possible new element of an Array of T using an efficient algorithm.
* @param array Array of T to check in
* @param getVal Function that returns the position value of T
* @return Index
* @return The index of the element
*/
public static function binarySearch<T>(array:Array<T>, val:Float, getVal:T->Float):Int {
if (array.length <= 0) return 0; // if the array is empty, it should be equal to zero (the beginning)
@@ -47,7 +49,13 @@ class SortedArrayUtil {
array.insert(binarySearch(array, getVal(val), getVal), val);
}
public static function removeSorted<T>(array:Array<T>, val:T, getVal:T->Float) {
/**
* Removes from a sorted array, using binary search.
* @param array Array to remove from
* @param val Value to remove
* @param getVal Function that returns the value that needs to be sorted
*/
public static inline function removeSorted<T>(array:Array<T>, val:T, getVal:T->Float) {
var index = binarySearch(array, getVal(val), getVal);
if (index != -1)
array.splice(index, 1);
+23 -6
View File
@@ -3,9 +3,9 @@ package funkin.backend.utils;
import openfl.Lib;
class WindowUtils {
public static var winTitle(default, set):String;
private static function set_winTitle(value:String):String {
winTitle = value;
public static var title(default, set):String;
private static function set_title(value:String):String {
title = value;
updateTitle();
return value;
}
@@ -28,7 +28,7 @@ class WindowUtils {
static var __triedClosing:Bool = false;
public static inline function resetClosing() __triedClosing = false;
public static inline function init() {
@:dox(hide) public static inline function init() {
resetTitle();
resetClosing();
@@ -41,18 +41,27 @@ class WindowUtils {
});
}
/**
* Resets the window title to the application name and resets the prefix and suffix.
**/
public static inline function resetTitle() {
winTitle = Lib.application.meta["name"];
title = Lib.application.meta["name"];
resetAffixes();
}
/**
* Resets the prefix and suffix.
**/
public static inline function resetAffixes() {
prefix = suffix = "";
updateTitle();
}
/**
* Updates the window title to have the current title and prefix/suffix.
**/
public static inline function updateTitle()
Lib.application.window.title = '$prefix$winTitle$suffix';
Lib.application.window.title = '$prefix$title$suffix';
// backwards compat
@:noCompletion public static var endfix(get, set):String;
@@ -62,4 +71,12 @@ class WindowUtils {
@:noCompletion private static function get_endfix():String {
return suffix;
}
@:noCompletion public static var winTitle(get, set):String;
@:noCompletion private static function get_winTitle():String {
return title;
}
@:noCompletion private static function set_winTitle(value:String):String {
return title = value;
}
}
+92 -26
View File
@@ -5,7 +5,6 @@ import flixel.util.typeLimit.OneOfTwo;
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;
@@ -18,17 +17,53 @@ import haxe.xml.Access;
using StringTools;
@:dox(hide)
enum abstract ErrorCode(Int) {
var OK = 0;
var FAILED = 1;
var MISSING_PROPERTY = 2;
var TYPE_INCORRECT = 3;
var VALUE_NULL = 4;
var REFLECT_ERROR = 5;
}
@:dox(hide)
typedef TextFormat = { text:String, format:Dynamic }
/**
* Class made to make XML parsing easier.
* Used in Stage.hx, Character.hx, and more.
*/
class XMLUtil {
/**
* Applies a property XML node to an object.
* The format for the XML is as follows:
* ```xml
* <!-- float -->
* <property name="name" type="float" value="value" />
* <property name="name" type="number" value="value" />
* <property name="name" type="f" value="value" />
* <!-- string -->
* <property name="name" type="string" value="value" />
* <property name="name" type="str" value="value" />
* <property name="name" type="text" value="value" />
* <property name="name" type="s" value="value" />
* <!-- int -->
* <property name="name" type="int" value="value" />
* <property name="name" type="integer" value="value" />
* <property name="name" type="i" value="value" />
* <!-- bool -->
* <property name="name" type="bool" value="value" />
* <property name="name" type="boolean" value="value" />
* <property name="name" type="b" value="value" />
* <!-- color -->
* <property name="name" type="color" value="value" />
* <property name="name" type="c" value="value" />
* ```
*
* @param object Object to which the xml property will be applied
* @param property `property` node.
* @return Error code (0 = OK, 1 = FAILED, 2 = MISSING_PROPERTY, 3 = TYPE_INCORRECT, 4 = VALUE_NULL, 5 = REFLECT_ERROR)
*/
public static function applyXMLProperty(object:Dynamic, property:Access):ErrorCode {
if (!property.has.name || !property.has.type || !property.has.value) {
@@ -47,7 +82,8 @@ class XMLUtil {
var value:Dynamic = switch(property.att.type.toLowerCase()) {
case "f" | "float" | "number": Std.parseFloat(property.att.value);
case "i" | "int" | "integer" | "color": Std.parseInt(property.att.value);
case "i" | "int" | "integer": Std.parseInt(property.att.value);
case "c" | "color": FlxColor.fromString(property.att.value);
case "s" | "string" | "str" | "text": property.att.value;
case "b" | "bool" | "boolean": property.att.value.toLowerCase() == "true";
default: return TYPE_INCORRECT;
@@ -72,7 +108,11 @@ class XMLUtil {
}
/**
* Overrides a sprite based on a XML node.
* Sets the properties of a sprite based on a XML node.
* @param spr The sprite
* @param node The XML node
* @param parentFolder The parent folder
* @param defaultAnimType The default animation type
*/
public static function loadSpriteFromXML(spr:FunkinSprite, node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT):FunkinSprite {
if (parentFolder == null) parentFolder = "";
@@ -147,8 +187,8 @@ class XMLUtil {
var graphicSizey:Null<Int> = Std.parseInt(node.att.graphicSizey);
if (graphicSizey.isNotNull()) spr.setGraphicSize(0, graphicSizey);
}
if (node.has.flipX && node.att.flipX == "true") spr.flipX = true;
if (node.has.flipX) spr.flipX = node.att.flipX == "true";
if (node.has.flipY) spr.flipY = node.att.flipY == "true";
if (node.has.updateHitbox && node.att.updateHitbox == "true") spr.updateHitbox();
if (node.has.zoomfactor)
@@ -170,19 +210,17 @@ class XMLUtil {
if(node.hasNode.anim) {
for(anim in node.nodes.anim)
addXMLAnimation(spr, anim);
} else {
if (spr.frames != null && spr.frames.frames != null) {
addAnimToSprite(spr, {
name: "idle",
anim: null,
fps: 24,
loop: spr.spriteAnimType == LOOP,
animType: spr.spriteAnimType,
x: 0,
y: 0,
indices: [for(i in 0...spr.frames.frames.length) i]
});
}
} else if (spr.frames != null && spr.frames.frames != null) {
addAnimToSprite(spr, {
name: "idle",
anim: null,
fps: 24,
loop: spr.spriteAnimType == LOOP,
animType: spr.spriteAnimType,
x: 0,
y: 0,
indices: [for(i in 0...spr.frames.frames.length) i]
});
}
return spr;
@@ -190,6 +228,11 @@ class XMLUtil {
/**
* Creates a new sprite based on a XML node.
* @param node The XML node
* @param parentFolder The parent folder
* @param defaultAnimType The default animation type
* @param cl The class to create (advanced)
* @param args The arguments to pass to the class (advanced)
*/
public static inline function createSpriteFromXML(node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT, ?cl:Class<FunkinSprite>, ?args:Array<Dynamic>):FunkinSprite {
if(cl == null) cl = FunkinSprite;
@@ -197,6 +240,12 @@ class XMLUtil {
return loadSpriteFromXML(Type.createInstance(cl, args), node, parentFolder, defaultAnimType);
}
/**
* Extracts an animation from an XML node.
* @param anim The animation node
* @param animType The animation type
* @param loop Whether the animation should loop
**/
public static function extractAnimFromXML(anim:Access, animType:XMLAnimType = NONE, loop:Bool = false):AnimData {
var animData:AnimData = {
name: null,
@@ -235,6 +284,11 @@ class XMLUtil {
return addAnimToSprite(sprite, extractAnimFromXML(anim, animType, loop));
}
/**
* Adds an animation to a sprite.
* @param sprite The sprite
* @param animData The animation data (gotten from `extractAnimFromXML`)
**/
public static function addAnimToSprite(sprite:FlxSprite, animData:AnimData):ErrorCode {
if (animData.name != null) {
if (animData.fps <= 0 #if web || animData.fps == null #end) animData.fps = 24;
@@ -244,16 +298,17 @@ class XMLUtil {
if(animData.anim == null)
return MISSING_PROPERTY;
if (animData.indices.length > 0)
if (animData.indices != null && animData.indices.length > 0)
animateAnim.addBySymbolIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop);
else
animateAnim.addBySymbol(animData.name, animData.anim, animData.fps, animData.loop);
} else {
if (animData.anim == null && animData.indices.length > 0)
sprite.animation.add(animData.name, animData.indices, animData.fps, animData.loop);
else if (animData.indices.length > 0)
sprite.animation.addByIndices(animData.name, animData.anim, animData.indices, "", animData.fps, animData.loop);
else
if (animData.indices != null && animData.indices.length > 0) {
if (animData.anim == null)
sprite.animation.add(animData.name, animData.indices, animData.fps, animData.loop);
else
sprite.animation.addByIndices(animData.name, animData.anim, animData.indices, "", animData.fps, animData.loop);
} else
sprite.animation.addByPrefix(animData.name, animData.anim, animData.fps, animData.loop);
}
@@ -309,6 +364,17 @@ class XMLUtil {
return node;
}
/**
* Gets the text formats from an XML node.
* The format for the XML is as follows:
* ```xml
* <text>Hello <format color="#FF0000">World</format></text>
* ```
*
* @param _node The XML node
* @param currentFormat The current format
* @param parsedSegments The parsed segments
**/
public static function getTextFormats(_node:OneOfTwo<Xml, Access>, currentFormat:Dynamic = null, parsedSegments:Array<TextFormat> = null):Array<TextFormat> {
var node:Xml = cast _node;
if (currentFormat == null)
@@ -401,4 +467,4 @@ typedef BeatAnim = {
interface IXMLEvents {
public function onPropertySet(property:String, value:Dynamic):Void;
}
}
+3 -3
View File
@@ -21,13 +21,13 @@ import sys.thread.Thread;
using StringTools;
// import ZipUtils; ZipUtils.uncompressZip(ZipUtils.openZip("E:\\Desktop\\test\\termination lua.ycemod"), "E:\\Desktop\\test\\uncompressed\\");
// import ZipUtils; var e = ZipUtils.createZipFile("gjnsdghs.ycemod"); ZipUtils.writeFolderToZip(e, "./mods/Friday Night Funkin'/", "Friday Night Funkin'/"); e.flush(); e.close();
// import ZipUtils; var e = ZipUtils.createZipFile("file.ycemod"); ZipUtils.writeFolderToZip(e, "./mods/Friday Night Funkin'/", "Friday Night Funkin'/"); e.flush(); e.close();
class ZipUtil {
public static var bannedNames:Array<String> = [".git", ".gitignore", ".github", ".vscode", ".gitattributes", "readme.txt"];
/**
* [Description] Uncompresses `zip` into the `destFolder` folder
* [Description] Decompresses a `zip` into the `destFolder` folder
* @param zip
* @param destFolder
*/
@@ -218,7 +218,7 @@ class ZipProgress {
public var curFile:Int = 0;
public var fileCount:Int = 0;
public var done:Bool = false;
public var percentage(get, null):Float;
public var percentage(get, never):Float;
private function get_percentage() {
return fileCount <= 0 ? 0 : curFile / fileCount;
@@ -13,7 +13,7 @@ import funkin.backend.utils.NativeAPI.MessageBoxIcon;
</target>
')
// majority is taken from microsofts doc
// majority is taken from Microsoft's doc
@:cppFileCode('
#include "mmdeviceapi.h"
#include "combaseapi.h"
@@ -132,7 +132,7 @@ class Windows {
int darkMode = enable ? 1 : 0;
HWND window = FindWindowA(NULL, title.c_str());
// Look for child windows if top level aint found
// Look for child windows if top level is not found
if (window == NULL) window = FindWindowExA(GetActiveWindow(), NULL, NULL, title.c_str());
// If still not found, try to get the active window
if (window == NULL) window = GetActiveWindow();