Merge branch 'main' into translations-system
This commit is contained in:
@@ -56,6 +56,7 @@ class Chart {
|
||||
data.setFieldDefault("name", songName);
|
||||
data.setFieldDefault("beatsPerMeasure", 4);
|
||||
data.setFieldDefault("stepsPerBeat", 4);
|
||||
data.setFieldDefault("needsVoices", true);
|
||||
data.setFieldDefault("icon", "face");
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("coopAllowed", false);
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef ChartMetaData = {
|
||||
public var ?coopAllowed:Bool;
|
||||
public var ?opponentModeAllowed:Bool;
|
||||
public var ?customValues:Dynamic;
|
||||
public var ?needsVoices:Bool;
|
||||
|
||||
// NOT TO BE EXPORTED
|
||||
public var ?parsedColor:FlxColor;
|
||||
|
||||
@@ -70,6 +70,8 @@ class EventsData {
|
||||
hscriptInterp.variables.set("StrumLine", TStrumLine);
|
||||
hscriptInterp.variables.set("ColorWheel", TColorWheel);
|
||||
hscriptInterp.variables.set("DropDown", Reflect.makeVarArgs((args) -> {return args.length > 0 ? TDropDown([for (arg in args) Std.string(arg)]) : TDropDown(["null"]);}));
|
||||
hscriptInterp.variables.set("Character", TCharacter);
|
||||
hscriptInterp.variables.set("Stage", TStage);
|
||||
|
||||
var hscriptParser:Parser = new Parser();
|
||||
hscriptParser.allowJSON = hscriptParser.allowMetadata = false;
|
||||
@@ -127,4 +129,6 @@ enum EventParamType {
|
||||
TStrumLine;
|
||||
TColorWheel;
|
||||
TDropDown(?options:Array<String>);
|
||||
TCharacter;
|
||||
TStage;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ class FNFLegacyParser {
|
||||
}
|
||||
|
||||
result.meta.bpm = data.bpm;
|
||||
result.meta.needsVoices = data.needsVoices.getDefault(true);
|
||||
|
||||
var camFocusedBF:Bool = false;
|
||||
var altAnims:Bool = false;
|
||||
@@ -145,6 +146,7 @@ class FNFLegacyParser {
|
||||
notes: null,
|
||||
bpm: chart.meta.bpm,
|
||||
speed: chart.scrollSpeed,
|
||||
needsVoices: chart.meta.needsVoices,
|
||||
|
||||
player1: null,
|
||||
player2: null,
|
||||
@@ -208,6 +210,7 @@ typedef SwagSong =
|
||||
var ?stage:String;
|
||||
var ?noteTypes:Array<String>;
|
||||
var ?events:Array<Dynamic>;
|
||||
var ?needsVoices:Bool;
|
||||
|
||||
var player1:String;
|
||||
var player2:String;
|
||||
|
||||
@@ -11,7 +11,11 @@ import funkin.backend.system.Conductor;
|
||||
class GlobalScript {
|
||||
public static var scripts:ScriptPack;
|
||||
|
||||
private static var initalized:Bool = false;
|
||||
|
||||
public static function init() {
|
||||
if(initalized) return;
|
||||
initalized = true;
|
||||
#if MOD_SUPPORT
|
||||
ModsFolder.onModSwitch.add(onModSwitch);
|
||||
#end
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package funkin.backend.scripting.events;
|
||||
|
||||
final class HealthIconChangeEvent extends CancellableEvent {
|
||||
/**
|
||||
* Amount
|
||||
*/
|
||||
public var amount:Int;
|
||||
|
||||
/**
|
||||
* The health icon
|
||||
*/
|
||||
public var healthIcon:funkin.game.HealthIcon;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using StringTools;
|
||||
#if macro
|
||||
class BuildInfo {
|
||||
public static function printBuildInfo() {
|
||||
if(haxe.macro.Context.defined("display")) return;
|
||||
var haxeVersion = haxe.macro.Context.definedValue("haxe");
|
||||
Sys.println('[ BUILD INFO ]');
|
||||
Sys.println('Haxe Version: ${haxeVersion}');
|
||||
|
||||
@@ -28,13 +28,21 @@ class Macros {
|
||||
|
||||
var isHl = Context.defined("hl");
|
||||
|
||||
var compathx4 = [
|
||||
"sys.db.Sqlite",
|
||||
"sys.db.Mysql",
|
||||
"sys.db.Connection",
|
||||
"sys.db.ResultSet",
|
||||
"haxe.remoting.Proxy",
|
||||
];
|
||||
|
||||
if(Context.defined("sys")) {
|
||||
for(inc in ["sys", "openfl.net", "funkin.backend.system.net"]) {
|
||||
if(!isHl)
|
||||
Compiler.include(inc);
|
||||
Compiler.include(inc, compathx4);
|
||||
else {
|
||||
// TODO: Hashlink
|
||||
//Compiler.include(inc, ["sys.net.UdpSocket", "openfl.net.DatagramSocket"]); // fixes FATAL ERROR : Failed to load function std@socket_set_broadcast
|
||||
//Compiler.include(inc, compathx4.concat(["sys.net.UdpSocket", "openfl.net.DatagramSocket"]); // fixes FATAL ERROR : Failed to load function std@socket_set_broadcast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.backend.utils;
|
||||
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import flixel.sound.FlxSound;
|
||||
import flixel.util.typeLimit.OneOfTwo;
|
||||
import funkin.backend.scripting.*; // lazy
|
||||
@@ -107,14 +108,28 @@ class DiscordUtil
|
||||
|
||||
public static function loadScript()
|
||||
{
|
||||
#if DISCORD_RPC
|
||||
if (script != null)
|
||||
{
|
||||
call("destroy");
|
||||
script = FlxDestroyUtil.destroy(script);
|
||||
}
|
||||
script = Script.create(Paths.script('data/discord'));
|
||||
script.set("ActivityType", {
|
||||
Playing: ActivityType.Playing,
|
||||
PLAYING: ActivityType.Playing,
|
||||
Streaming: ActivityType.Streaming,
|
||||
STREAMING: ActivityType.Streaming,
|
||||
Watching: ActivityType.Watching,
|
||||
WATCHING: ActivityType.Watching,
|
||||
Listening: ActivityType.Listening,
|
||||
LISTENING: ActivityType.Listening,
|
||||
Competing: ActivityType.Competing,
|
||||
COMPETING: ActivityType.Competing
|
||||
});
|
||||
// script.setParent(DiscordUtil);
|
||||
script.load();
|
||||
#end
|
||||
}
|
||||
|
||||
public static function changePresence(details:String, state:String, ?smallImageKey:String)
|
||||
@@ -218,6 +233,8 @@ class DiscordUtil
|
||||
Utils.safeSetWrapper(dp.joinSecret, data.joinSecret, fixString);
|
||||
Utils.safeSetWrapper(dp.spectateSecret, data.spectateSecret, fixString);
|
||||
Utils.safeSet(dp.instance, data.instance);
|
||||
Utils.safeSet(dp.activityType, data.activityType);
|
||||
Utils.safeSetWrapper(dp.streamUrl, data.streamUrl, fixString);
|
||||
if (data.matchSecret == null && data.joinSecret == null && data.spectateSecret == null)
|
||||
{
|
||||
Utils.safeSetWrapper(dp.button1Label, data.button1Label, fixString);
|
||||
@@ -252,6 +269,7 @@ class DiscordUtil
|
||||
handlers.joinGame = cpp.Function.fromStaticFunction(onJoin);
|
||||
handlers.joinRequest = cpp.Function.fromStaticFunction(onJoinReq);
|
||||
handlers.spectateGame = cpp.Function.fromStaticFunction(onSpectate);
|
||||
handlers.anyResponse = cpp.Function.fromStaticFunction(onAnyResponse);
|
||||
Discord.Initialize(id, cpp.RawPointer.addressOf(handlers), 1, null);
|
||||
stopThread = false;
|
||||
|
||||
@@ -309,7 +327,7 @@ class DiscordUtil
|
||||
Logs.logText(")")
|
||||
], INFO);
|
||||
|
||||
call("onReady", [errorCode, cast(finalMsg, String)]);
|
||||
call("onDisconnected", [errorCode, cast(finalMsg, String)]);
|
||||
}
|
||||
|
||||
private static function onError(errorCode:Int, message:cpp.ConstCharStar):Void
|
||||
@@ -351,7 +369,38 @@ class DiscordUtil
|
||||
var req:DUser = DUser.initRaw(request);
|
||||
call("onJoinRequest", [req]);
|
||||
}
|
||||
|
||||
public static var anyResponse:FlxTypedSignal<String->Void> = new FlxTypedSignal<String->Void>();
|
||||
|
||||
private static function onAnyResponse(data:cpp.ConstCharStar):Void
|
||||
{
|
||||
call("onAnyResponse", [data]);
|
||||
anyResponse.dispatch(data);
|
||||
}
|
||||
#end
|
||||
|
||||
private static function getUUID():String {
|
||||
var uuid = new StringBuf();
|
||||
for (i in 0...16) {
|
||||
uuid.add(StringTools.hex(Math.floor(Math.random() * 16), 1));
|
||||
}
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
public static function sendCustomCommand(data:Dynamic) {
|
||||
#if DISCORD_RPC
|
||||
if(data == null) return;
|
||||
if(data.nonce == null) data.nonce = getUUID();
|
||||
var json = Json.stringify(data);
|
||||
Discord.SendCustomCommand(json);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function setDebugMode(mode:Bool) {
|
||||
#if DISCORD_RPC
|
||||
Discord.SetDebugMode(mode);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
typedef DiscordJson =
|
||||
@@ -476,6 +525,8 @@ typedef DPresence =
|
||||
var ?button1Url:String; /* max 512 bytes */
|
||||
var ?button2Label:String; /* max 32 bytes */
|
||||
var ?button2Url:String; /* max 512 bytes */
|
||||
var ?activityType:ActivityType;
|
||||
var ?streamUrl:String; /* max 512 bytes */
|
||||
}
|
||||
|
||||
typedef DEvents =
|
||||
|
||||
@@ -526,7 +526,7 @@ class Charter extends UIState {
|
||||
noteTypes = PlayState.SONG.noteTypes;
|
||||
|
||||
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff)));
|
||||
if (Assets.exists(Paths.voices(__song, __diff))) // null or true
|
||||
if (PlayState.SONG.meta.needsVoices != false && Assets.exists(Paths.voices(__song, __diff))) // null or true
|
||||
vocals = FlxG.sound.load(Paths.voices(__song, __diff));
|
||||
else
|
||||
vocals = new FlxSound();
|
||||
@@ -598,15 +598,22 @@ class Charter extends UIState {
|
||||
updateWaveforms();
|
||||
}
|
||||
|
||||
inline function isSoundLoaded(sound:FlxSound) {
|
||||
@:privateAccess
|
||||
return sound != null && sound._sound != null && sound._sound.length > 0;
|
||||
}
|
||||
|
||||
public function updateWaveforms() {
|
||||
var wavesToGenerate:Array<{name:String, sound:FlxSound}> = [
|
||||
{name: "Inst.ogg", sound: FlxG.sound.music},
|
||||
];
|
||||
if (@:privateAccess vocals._sound != null) //for some reason Assets.exists returns true regardless if the file actually exists
|
||||
var wavesToGenerate:Array<{name:String, sound:FlxSound}> = [];
|
||||
|
||||
if(isSoundLoaded(FlxG.sound.music))
|
||||
wavesToGenerate.push({name: "Inst.ogg", sound: FlxG.sound.music});
|
||||
|
||||
if (PlayState.SONG.meta.needsVoices != false && isSoundLoaded(vocals))
|
||||
wavesToGenerate.push({name: "Voices.ogg", sound: vocals});
|
||||
|
||||
for (strumLine in strumLines)
|
||||
if (strumLine.vocals != null && strumLine.strumLine.vocalsSuffix != null && strumLine.strumLine.vocalsSuffix != "")
|
||||
if (strumLine.vocals != null && strumLine.strumLine.vocalsSuffix != null && strumLine.strumLine.vocalsSuffix != "" && isSoundLoaded(strumLine.vocals))
|
||||
wavesToGenerate.push({
|
||||
name: 'Voices${strumLine.strumLine.vocalsSuffix}.ogg',
|
||||
sound: strumLine.vocals
|
||||
|
||||
@@ -5,6 +5,8 @@ import flixel.util.FlxColor;
|
||||
import funkin.backend.chart.ChartData.ChartEvent;
|
||||
import funkin.backend.chart.EventsData;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.game.Character;
|
||||
import funkin.game.Stage;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -163,6 +165,20 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
var dropdown = new UIDropDown(eventName.x, y, 320, 32, options, Std.int(Math.abs(options.indexOf(cast value))));
|
||||
paramsPanel.add(dropdown); paramsFields.push(dropdown);
|
||||
dropdown;
|
||||
case TCharacter:
|
||||
addLabel();
|
||||
var charFileList = Character.getList(false);
|
||||
var textBox:UIAutoCompleteTextBox = new UIAutoCompleteTextBox(eventName.x, y, cast value);
|
||||
textBox.suggestItems = charFileList;
|
||||
paramsPanel.add(textBox); paramsFields.push(textBox);
|
||||
textBox;
|
||||
case TStage:
|
||||
addLabel();
|
||||
var stageFileList = Stage.getList(false);
|
||||
var textBox:UIAutoCompleteTextBox = new UIAutoCompleteTextBox(eventName.x, y, cast value);
|
||||
textBox.suggestItems = stageFileList;
|
||||
paramsPanel.add(textBox); paramsFields.push(textBox);
|
||||
textBox;
|
||||
default:
|
||||
paramsFields.push(null);
|
||||
null;
|
||||
|
||||
@@ -24,6 +24,11 @@ class HealthIcon extends FlxSprite
|
||||
*/
|
||||
public var healthSteps:Map<Int, Int> = null;
|
||||
|
||||
/**
|
||||
* current animation state
|
||||
*/
|
||||
public var curAnimState:Int = -1;
|
||||
|
||||
/**
|
||||
* Helper for HScript who can't make maps
|
||||
* @param steps Something like this: `[[0, 1], [20, 0]]`
|
||||
@@ -91,7 +96,14 @@ class HealthIcon extends FlxSprite
|
||||
i = icon;
|
||||
}
|
||||
}
|
||||
if (i >= 0) animation.curAnim.curFrame = i;
|
||||
if (i >= 0 && curAnimState != i) {
|
||||
var event = EventManager.get(funkin.backend.scripting.events.HealthIconChangeEvent).recycle(i, this);
|
||||
funkin.backend.scripting.GlobalScript.event("onHealthIconAnimChange", event);
|
||||
if (!event.cancelled)
|
||||
animation.curAnim.curFrame = event.amount;
|
||||
|
||||
curAnimState = event.amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1000,7 +1000,7 @@ class PlayState extends MusicBeatState
|
||||
curSong = songData.meta.name.toLowerCase();
|
||||
|
||||
inst = FlxG.sound.load(Paths.inst(SONG.meta.name, difficulty));
|
||||
if (Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true
|
||||
if (SONG.meta.needsVoices != false && Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true
|
||||
vocals = FlxG.sound.load(Paths.voices(SONG.meta.name, difficulty));
|
||||
else
|
||||
vocals = new FlxSound();
|
||||
@@ -1424,6 +1424,7 @@ class PlayState extends MusicBeatState
|
||||
for (strumLine in strumLines.members) strumLine.vocals.stop();
|
||||
|
||||
deathCounter++;
|
||||
|
||||
openSubState(new GameOverSubstate(event.x, event.y, event.deathCharID, event.isPlayer, event.gameOverSong, event.lossSFX, event.retrySFX));
|
||||
|
||||
scripts.event("onPostGameOver", event);
|
||||
|
||||
@@ -40,7 +40,10 @@ class SplashHandler extends FlxTypedGroup<FunkinSprite> {
|
||||
var __grp:SplashGroup;
|
||||
public function showSplash(name:String, strum:Strum) {
|
||||
__grp = getSplashGroup(name);
|
||||
add(__grp.showOnStrum(strum));
|
||||
var event = funkin.backend.scripting.EventManager.get(funkin.backend.scripting.events.DynamicEvent).recycle(__grp.showOnStrum(strum), strum);
|
||||
event = PlayState.instance.scripts.event("onSplashShown", event);
|
||||
if (!event.cancelled)
|
||||
add(event.value1);
|
||||
|
||||
// max 8 rendered splashes
|
||||
while(members.length > 8)
|
||||
|
||||
@@ -14,6 +14,8 @@ class Strum extends FlxSprite {
|
||||
public var cpu = false; // Unused
|
||||
public var lastHit:Float = -5000;
|
||||
|
||||
public var strumLine = null;
|
||||
|
||||
public var scrollSpeed:Null<Float> = null; // custom scroll speed per strum
|
||||
public var noteAngle:Null<Float> = null;
|
||||
|
||||
|
||||
@@ -303,6 +303,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
animPrefix = strumAnimPrefix[i % strumAnimPrefix.length];
|
||||
var babyArrow:Strum = new Strum(startingPos.x + ((Note.swagWidth * strumScale) * i), startingPos.y);
|
||||
babyArrow.ID = i;
|
||||
babyArrow.strumLine = this;
|
||||
|
||||
if(data.scrollSpeed != null)
|
||||
babyArrow.scrollSpeed = data.scrollSpeed;
|
||||
|
||||
@@ -196,6 +196,10 @@ class FreeplayState extends MusicBeatState
|
||||
* Path to the currently playing song instrumental.
|
||||
*/
|
||||
public var curPlayingInst:String = null;
|
||||
/**
|
||||
* If it should play the song automatically.
|
||||
*/
|
||||
public var autoplayShouldPlay:Bool = true;
|
||||
#end
|
||||
|
||||
override function update(elapsed:Float)
|
||||
@@ -236,7 +240,17 @@ class FreeplayState extends MusicBeatState
|
||||
autoplayElapsed += elapsed;
|
||||
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay || FlxG.keys.justPressed.SPACE)) {
|
||||
if (curPlayingInst != (curPlayingInst = Paths.inst(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]))) {
|
||||
var huh:Void->Void = function() FlxG.sound.playMusic(curPlayingInst, 0);
|
||||
var huh:Void->Void = function() {
|
||||
var soundPath = curPlayingInst;
|
||||
var sound = null;
|
||||
if (Assets.exists(soundPath, SOUND) || Assets.exists(soundPath, MUSIC))
|
||||
sound = Assets.getSound(soundPath);
|
||||
else
|
||||
FlxG.log.error('Could not find a Sound asset with an ID of \'$soundPath\'.');
|
||||
|
||||
if (sound != null && autoplayShouldPlay)
|
||||
FlxG.sound.playMusic(sound, 0);
|
||||
}
|
||||
if(!disableAsyncLoading) Main.execAsync(huh);
|
||||
else huh();
|
||||
}
|
||||
@@ -289,6 +303,10 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
#if PRELOAD_ALL
|
||||
autoplayShouldPlay = false;
|
||||
#end
|
||||
|
||||
Options.freeplayLastSong = songs[curSelected].name;
|
||||
Options.freeplayLastDifficulty = songs[curSelected].difficulties[curDifficulty];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user