Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package funkin.backend;
|
||||
|
||||
import funkin.backend.utils.XMLUtil.BeatAnim;
|
||||
import funkin.backend.utils.XMLUtil.AnimData;
|
||||
import funkin.backend.utils.XMLUtil.IXMLEvents;
|
||||
import flixel.system.FlxAssets.FlxGraphicAsset;
|
||||
@@ -34,13 +35,17 @@ abstract XMLAnimType(Int)
|
||||
class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements IOffsetCompatible implements IXMLEvents
|
||||
{
|
||||
public var spriteAnimType:XMLAnimType = NONE;
|
||||
public var beatAnims:Array<String> = [];
|
||||
public var beatAnims:Array<BeatAnim> = [];
|
||||
public var name:String;
|
||||
public var zoomFactor:Float = 1;
|
||||
public var initialZoom:Float = 1;
|
||||
public var debugMode:Bool = false;
|
||||
public var animDatas:Map<String, AnimData> = [];
|
||||
|
||||
public var beatInterval:Int = 1;
|
||||
public var beatOffset:Int = 0;
|
||||
public var skipNegativeBeats:Bool = false;
|
||||
|
||||
public var animateAtlas:FlxAnimate;
|
||||
@:noCompletion public var atlasPlayingAnim:String;
|
||||
@:noCompletion public var atlasPath:String;
|
||||
@@ -108,13 +113,16 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
moves = true;
|
||||
}
|
||||
|
||||
private var countedBeat = 0;
|
||||
public function beatHit(curBeat:Int)
|
||||
{
|
||||
if (beatAnims.length > 0)
|
||||
if (beatAnims.length > 0 && (curBeat + beatOffset) % beatInterval == 0)
|
||||
{
|
||||
var anim = beatAnims[FlxMath.wrap(curBeat, 0, beatAnims.length - 1)];
|
||||
if (anim != null && anim != "null" && anim != "none")
|
||||
playAnim(anim);
|
||||
if(skipNegativeBeats && curBeat < 0) return;
|
||||
// TODO: find a solution without countedBeat
|
||||
var anim = beatAnims[FlxMath.wrap(countedBeat++, 0, beatAnims.length - 1)];
|
||||
if (anim.name != null && anim.name != "null" && anim.name != "none")
|
||||
playAnim(anim.name, anim.forced);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,15 @@ class BaseGameParser {
|
||||
});
|
||||
}
|
||||
|
||||
if (section.altAnim == null) section.altAnim = false;
|
||||
if (altAnims != (altAnims = section.altAnim)) {
|
||||
result.events.push({
|
||||
time: curTime,
|
||||
name: "Alt Animation Toggle",
|
||||
params: [0, altAnims]
|
||||
});
|
||||
}
|
||||
|
||||
if (section.sectionNotes != null) for(note in section.sectionNotes) {
|
||||
if (note[1] < 0) continue;
|
||||
|
||||
@@ -129,6 +138,6 @@ typedef SwagSection =
|
||||
var mustHitSection:Bool;
|
||||
var bpm:Float;
|
||||
var changeBPM:Bool;
|
||||
var altAnim:Bool;
|
||||
var altAnim:Null<Bool>;
|
||||
@:optional var camTarget:Null<Float>;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import haxe.Http;
|
||||
class GitHub {
|
||||
/**
|
||||
* Gets all the releases from a specific GitHub repository using the GitHub API.
|
||||
* @param user
|
||||
* @param repository
|
||||
* @param user The user/group that owns the repository
|
||||
* @param repository The repository name
|
||||
* @return Releases
|
||||
*/
|
||||
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array<GitHubRelease> {
|
||||
@@ -29,6 +29,12 @@ class GitHub {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the contributors list from a specific GitHub repository using the GitHub API.
|
||||
* @param user The user/group that owns the repository
|
||||
* @param repository The repository name
|
||||
* @return Contributors List
|
||||
*/
|
||||
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array<GitHubContributor> {
|
||||
try {
|
||||
var url = 'https://api.github.com/repos/${user}/${repository}/contributors';
|
||||
@@ -45,6 +51,27 @@ class GitHub {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a specific GitHub user/group using the GitHub API.
|
||||
* @param user The user/group to get
|
||||
* @return User/Group
|
||||
*/
|
||||
public static function getUser(user:String, ?onError:Exception->Void):GitHubUser {
|
||||
try {
|
||||
var url = 'https://api.github.com/users/$user';
|
||||
|
||||
var data = Json.parse(__requestOnGitHubServers(url));
|
||||
if (Reflect.hasField(data, "documentation_url"))
|
||||
throw __parseGitHubException(data);
|
||||
|
||||
return data;
|
||||
} catch(e) {
|
||||
if (onError != null)
|
||||
onError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters all releases gotten by `getReleases`
|
||||
* @param releases Releases
|
||||
@@ -58,29 +85,52 @@ class GitHub {
|
||||
public static function __requestOnGitHubServers(url:String) {
|
||||
var h = new Http(url);
|
||||
h.setHeader("User-Agent", "request");
|
||||
|
||||
var r = null;
|
||||
h.onStatus = function(s) {
|
||||
if(isRedirect(s))
|
||||
r = __requestOnGitHubServers(h.responseHeaders.get("Location"));
|
||||
};
|
||||
|
||||
h.onData = function(d) {
|
||||
r = d;
|
||||
if(r == null) r = d;
|
||||
}
|
||||
h.onError = function(e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
h.request(false);
|
||||
return r;
|
||||
}
|
||||
public static function __requestBytesOnGitHubServers(url:String) {
|
||||
var h = new Http(url);
|
||||
h.setHeader("User-Agent", "request");
|
||||
|
||||
var r = null;
|
||||
h.onStatus = function(s) {
|
||||
if(isRedirect(s))
|
||||
r = __requestBytesOnGitHubServers(h.responseHeaders.get("Location"));
|
||||
};
|
||||
|
||||
h.onBytes = function(d) {
|
||||
r = d;
|
||||
if(r == null) r = d;
|
||||
}
|
||||
h.onError = function(e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
h.request(false);
|
||||
return r;
|
||||
}
|
||||
private static function isRedirect(status:Int):Bool {
|
||||
switch (status) {
|
||||
// 301: Moved Permanently, 302: Found (Moved Temporarily), 307: Temporary Redirect, 308: Permanent Redirect - Nex_isDumb
|
||||
case 301 | 302 | 307 | 308 :
|
||||
trace("Redirected with status code: " + status);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private static function __parseGitHubException(obj:Dynamic):GitHubException {
|
||||
var msg:String = "(No message)";
|
||||
var url:String = "(No API url)";
|
||||
@@ -99,18 +149,33 @@ import haxe.Exception;
|
||||
class GitHub {
|
||||
/**
|
||||
* Gets all the releases from a specific GitHub repository using the GitHub API.
|
||||
* @param user
|
||||
* @param repository
|
||||
* @param user The user/group that owns the repository
|
||||
* @param repository The repository name
|
||||
* @return Releases
|
||||
*/
|
||||
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array<GitHubRelease> {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the contributors list from a specific GitHub repository using the GitHub API.
|
||||
* @param user The user/group that owns the repository
|
||||
* @param repository The repository name
|
||||
* @return Contributors List
|
||||
*/
|
||||
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array<GitHubContributor> {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a specific GitHub user/group using the GitHub API.
|
||||
* @param user The user/group to get
|
||||
* @return User/Group
|
||||
*/
|
||||
public static function getUser(user:String, ?onError:Exception->Void):GitHubUser {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters all releases gotten by `getReleases`
|
||||
* @param releases Releases
|
||||
@@ -130,5 +195,14 @@ class GitHub {
|
||||
private static function __parseGitHubException(obj:Dynamic):GitHubException {
|
||||
return null;
|
||||
}
|
||||
private static function isRedirect(status:Int):Bool {
|
||||
switch (status) {
|
||||
// 301: Moved Permanently, 302: Found (Moved Temporarily), 307: Temporary Redirect, 308: Permanent Redirect - Nex_isDumb
|
||||
case 301 | 302 | 307 | 308 :
|
||||
trace("Redirected with status code: " + status);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -20,7 +20,7 @@ class ScriptsMacro {
|
||||
"flixel.addons.plugin", "flixel.addons.text", "flixel.addons.tile", "flixel.addons.transition",
|
||||
"flixel.addons.util",
|
||||
// OTHER LIBRARIES & STUFF
|
||||
"away3d", "flx3d",
|
||||
#if THREE_D_SUPPORT "away3d", "flx3d", #end
|
||||
#if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end
|
||||
// BASE HAXE
|
||||
"DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting"
|
||||
|
||||
@@ -28,6 +28,16 @@ class CoolUtil
|
||||
public static function getLastExceptionStack():String {
|
||||
return CallStack.toString(CallStack.exceptionStack());
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns `v` if not null
|
||||
* @param v The value
|
||||
* @return A bool value
|
||||
*/
|
||||
public static inline function isNotNull(v:Null<Dynamic>):Bool {
|
||||
return v != null && !isNaN(v);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns `v` if not null, `defaultValue` otherwise.
|
||||
* @param v The value
|
||||
@@ -135,7 +145,7 @@ class CoolUtil
|
||||
|
||||
/**
|
||||
* Returns a string representation of a size, following this format: `1.02 GB`, `134.00 MB`
|
||||
* @param size Size to convert ot string
|
||||
* @param size Size to convert to string
|
||||
* @return String Result string representation
|
||||
*/
|
||||
public static function getSizeString(size:Float):String {
|
||||
@@ -149,6 +159,15 @@ class CoolUtil
|
||||
return '${Std.int(rSize) + "." + addZeros(Std.string(Std.int((rSize % 1) * 100)), 2)}${labels[label]}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces in a string any kind of IP with `[Your IP]` making the string safer to trace.
|
||||
* @param msg String to check and edit
|
||||
* @return String Result without any kind of IP
|
||||
*/
|
||||
public static inline function removeIP(msg:String):String {
|
||||
return ~/\d+.\d+.\d+.\d+/.replace(msg, "[Your IP]"); // For now its just IPs but who knows in the future.. - Nex_isDumb
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternative linear interpolation function for each frame use, without worrying about framerate changes.
|
||||
* @param v1 Begin value
|
||||
|
||||
@@ -21,7 +21,7 @@ class XMLUtil {
|
||||
*/
|
||||
public static function applyXMLProperty(object:Dynamic, property:Access):ErrorCode {
|
||||
if (!property.has.name || !property.has.type || !property.has.value) {
|
||||
Logs.trace('Failed to apply XML property: XML Element misses name, type, or value attributes.', WARNING);
|
||||
Logs.trace('Failed to apply XML property: XML Element is missing name, type, or value attributes.', WARNING);
|
||||
return MISSING_PROPERTY;
|
||||
}
|
||||
|
||||
@@ -72,54 +72,86 @@ class XMLUtil {
|
||||
|
||||
spr.loadSprite(Paths.image('$parentFolder${node.getAtt("sprite")}', null, true));
|
||||
|
||||
if (spr.frames != null && spr.frames.frames != null) {
|
||||
spr.animation.add("idle", [for(i in 0...spr.frames.frames.length) i], 24, true);
|
||||
spr.animation.play("idle");
|
||||
spr.spriteAnimType = defaultAnimType;
|
||||
if (node.has.type) {
|
||||
spr.spriteAnimType = XMLAnimType.fromString(node.att.type, spr.spriteAnimType);
|
||||
}
|
||||
|
||||
spr.spriteAnimType = defaultAnimType;
|
||||
if (node.has.type)
|
||||
spr.spriteAnimType = XMLAnimType.fromString(node.att.type, spr.spriteAnimType);
|
||||
|
||||
var x:Null<Float> = node.has.x ? Std.parseFloat(node.att.x) : null;
|
||||
var y:Null<Float> = node.has.y ? Std.parseFloat(node.att.y) : null;
|
||||
if (x != null) spr.x = x;
|
||||
if (y != null) spr.y = y;
|
||||
if(node.has.x) {
|
||||
var x:Null<Float> = Std.parseFloat(node.att.x);
|
||||
if (x.isNotNull()) spr.x = x;
|
||||
}
|
||||
if(node.has.y) {
|
||||
var y:Null<Float> = Std.parseFloat(node.att.y);
|
||||
if (y.isNotNull()) spr.y = y;
|
||||
}
|
||||
if (node.has.scroll) {
|
||||
var scroll:Null<Float> = Std.parseFloat(node.att.scroll);
|
||||
if (scroll != null) spr.scrollFactor.set(scroll, scroll);
|
||||
if (scroll.isNotNull()) spr.scrollFactor.set(scroll, scroll);
|
||||
} else {
|
||||
if (node.has.scrollx) {
|
||||
var scroll:Null<Float> = Std.parseFloat(node.att.scrollx);
|
||||
if (scroll != null) spr.scrollFactor.x = scroll;
|
||||
if (scroll.isNotNull()) spr.scrollFactor.x = scroll;
|
||||
}
|
||||
if (node.has.scrolly) {
|
||||
var scroll:Null<Float> = Std.parseFloat(node.att.scrolly);
|
||||
if (scroll != null) spr.scrollFactor.y = scroll;
|
||||
if (scroll.isNotNull()) spr.scrollFactor.y = scroll;
|
||||
}
|
||||
}
|
||||
if (node.has.skewx) {
|
||||
var skew:Null<Float> = Std.parseFloat(node.att.skewx);
|
||||
if (skew != null) spr.skew.x = skew;
|
||||
if (skew.isNotNull()) spr.skew.x = skew;
|
||||
}
|
||||
if (node.has.skewy) {
|
||||
var skew:Null<Float> = Std.parseFloat(node.att.skewy);
|
||||
if (skew != null) spr.skew.y = skew;
|
||||
if (skew.isNotNull()) spr.skew.y = skew;
|
||||
}
|
||||
if (node.has.antialiasing) spr.antialiasing = node.att.antialiasing == "true";
|
||||
if (node.has.scale) {
|
||||
var scale:Null<Float> = Std.parseFloat(node.att.scale);
|
||||
if (scale != null) spr.scale.set(scale, scale);
|
||||
if (scale.isNotNull()) spr.scale.set(scale, scale);
|
||||
}
|
||||
if (node.has.scalex) {
|
||||
var scale:Null<Float> = Std.parseFloat(node.att.scalex);
|
||||
if (scale.isNotNull()) spr.scale.x = scale;
|
||||
}
|
||||
if (node.has.scaley) {
|
||||
var scale:Null<Float> = Std.parseFloat(node.att.scaley);
|
||||
if (scale.isNotNull()) spr.scale.y = scale;
|
||||
}
|
||||
if (node.has.updateHitbox && node.att.updateHitbox == "true") spr.updateHitbox();
|
||||
|
||||
spr.zoomFactor = Std.parseFloat(node.getAtt("zoomfactor")).getDefault(spr.zoomFactor);
|
||||
if(node.has.zoomfactor)
|
||||
spr.zoomFactor = Std.parseFloat(node.getAtt("zoomfactor")).getDefault(spr.zoomFactor);
|
||||
|
||||
if (node.has.alpha)
|
||||
spr.alpha = Std.parseFloat(node.getAtt("alpha")).getDefault(spr.alpha);
|
||||
|
||||
for(anim in node.nodes.anim)
|
||||
addXMLAnimation(spr, anim);
|
||||
if (node.has.playOnCountdown)
|
||||
spr.skipNegativeBeats = node.att.playOnCountdown == "true";
|
||||
if (node.has.beatInterval)
|
||||
spr.beatInterval = Std.parseInt(node.att.beatInterval);
|
||||
if (node.has.beatOffset)
|
||||
spr.beatOffset = Std.parseInt(node.att.beatOffset);
|
||||
|
||||
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],
|
||||
forced: (node.has.forced && node.att.forced == "true") || (!node.has.forced && spr.spriteAnimType == BEAT)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return spr;
|
||||
}
|
||||
@@ -133,16 +165,18 @@ class XMLUtil {
|
||||
animType: animType,
|
||||
x: 0,
|
||||
y: 0,
|
||||
indices: []
|
||||
indices: [],
|
||||
forced: false,
|
||||
};
|
||||
|
||||
if (anim.has.name) animData.name = anim.att.name;
|
||||
if (anim.has.type) animData.animType = XMLAnimType.fromString(anim.att.type, animData.animType);
|
||||
if (anim.has.anim) animData.anim = anim.att.anim;
|
||||
if (anim.has.fps) animData.fps = Std.parseFloat(anim.att.fps);
|
||||
if (anim.has.x) animData.x = Std.parseFloat(anim.att.x);
|
||||
if (anim.has.y) animData.y = Std.parseFloat(anim.att.y);
|
||||
if (anim.has.fps) animData.fps = Std.parseFloat(anim.att.fps).getDefault(animData.fps);
|
||||
if (anim.has.x) animData.x = Std.parseFloat(anim.att.x).getDefault(animData.x);
|
||||
if (anim.has.y) animData.y = Std.parseFloat(anim.att.y).getDefault(animData.y);
|
||||
if (anim.has.loop) animData.loop = anim.att.loop == "true";
|
||||
if (anim.has.forced) animData.forced = anim.att.forced == "true";
|
||||
if (anim.has.indices) {
|
||||
var indicesSplit = anim.att.indices.split(",");
|
||||
for(indice in indicesSplit) {
|
||||
@@ -161,24 +195,30 @@ class XMLUtil {
|
||||
*/
|
||||
public static function addXMLAnimation(sprite:FlxSprite, anim:Access, loop:Bool = false):ErrorCode {
|
||||
var animType:XMLAnimType = NONE;
|
||||
if (sprite is FunkinSprite)
|
||||
if (sprite is FunkinSprite) {
|
||||
animType = cast(sprite, FunkinSprite).spriteAnimType;
|
||||
}
|
||||
|
||||
return addAnimToSprite(sprite, extractAnimFromXML(anim, animType, loop));
|
||||
}
|
||||
|
||||
public static function addAnimToSprite(sprite:FlxSprite, animData:AnimData):ErrorCode {
|
||||
if (animData.name != null && animData.anim != null) {
|
||||
if (animData.name != null) {
|
||||
if (animData.fps <= 0 #if web || animData.fps == null #end) animData.fps = 24;
|
||||
|
||||
if (sprite is FunkinSprite && cast(sprite, FunkinSprite).animateAtlas != null) {
|
||||
var animateAnim = cast(sprite, FunkinSprite).animateAtlas.anim;
|
||||
if(animData.anim == null)
|
||||
return MISSING_PROPERTY;
|
||||
|
||||
if (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.indices.length > 0)
|
||||
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
|
||||
sprite.animation.addByPrefix(animData.name, animData.anim, animData.fps, animData.loop);
|
||||
@@ -191,9 +231,12 @@ class XMLUtil {
|
||||
var xmlSpr = cast(sprite, FunkinSprite);
|
||||
switch(animData.animType) {
|
||||
case BEAT:
|
||||
xmlSpr.beatAnims.push(animData.name);
|
||||
xmlSpr.beatAnims.push({
|
||||
name: animData.name,
|
||||
forced: animData.forced.getDefault(false)
|
||||
});
|
||||
case LOOP:
|
||||
xmlSpr.animation.play(animData.name);
|
||||
xmlSpr.animation.play(animData.name, animData.forced.getDefault(false));
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
@@ -219,6 +262,12 @@ typedef AnimData = {
|
||||
var y:Float;
|
||||
var indices:Array<Int>;
|
||||
var animType:XMLAnimType;
|
||||
var ?forced:Bool;
|
||||
}
|
||||
|
||||
typedef BeatAnim = {
|
||||
var name:String;
|
||||
var forced:Bool;
|
||||
}
|
||||
|
||||
interface IXMLEvents {
|
||||
|
||||
Reference in New Issue
Block a user