Merge branch 'main' into new-editors
This commit is contained in:
@@ -230,6 +230,8 @@ class StringTools {
|
||||
return (cast s).startsWith(start);
|
||||
#elseif lua
|
||||
return untyped __lua__("{0}:sub(1, #{1}) == {1}", s, start);
|
||||
#elseif cpp
|
||||
return untyped s.__StartsWith(start);
|
||||
#else
|
||||
return (s.length >= start.length && s.indexOf(start, 0) == 0);
|
||||
#end
|
||||
@@ -255,6 +257,8 @@ class StringTools {
|
||||
return (cast s).endsWith(end);
|
||||
#elseif lua
|
||||
return end == "" || untyped __lua__("{0}:sub(-#{1}) == {1}", s, end);
|
||||
#elseif cpp
|
||||
return untyped s.__EndsWith(end);
|
||||
#else
|
||||
var elen = end.length;
|
||||
var slen = s.length;
|
||||
|
||||
@@ -8,7 +8,7 @@ import flixel.math.FlxRect;
|
||||
import flixel.system.FlxAssets.FlxGraphicAsset;
|
||||
import flixel.util.typeLimit.OneOfTwo;
|
||||
import flxanimate.animate.FlxAnim.FlxSymbolAnimation;
|
||||
import funkin.backend.scripting.events.PlayAnimContext;
|
||||
import funkin.backend.scripting.events.sprite.PlayAnimContext;
|
||||
import funkin.backend.system.interfaces.IBeatReceiver;
|
||||
import funkin.backend.system.interfaces.IOffsetCompatible;
|
||||
import funkin.backend.utils.XMLUtil.AnimData;
|
||||
@@ -56,6 +56,8 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
public var animEnabled:Bool = true;
|
||||
public var zoomFactorEnabled:Bool = true;
|
||||
|
||||
public var globalCurFrame(get, set):Int;
|
||||
|
||||
/**
|
||||
* ODD interval -> asynced; EVEN interval -> synced
|
||||
*/
|
||||
@@ -142,7 +144,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
public function beatHit(curBeat:Int)
|
||||
{
|
||||
if(!animEnabled) return;
|
||||
if (beatAnims.length > 0 && (curBeat + beatOffset) % beatInterval == 0)
|
||||
if (lastAnimContext != LOCK && beatAnims.length > 0 && (curBeat + beatOffset) % beatInterval == 0)
|
||||
{
|
||||
if(skipNegativeBeats && curBeat < 0) return;
|
||||
// TODO: find a solution without countedBeat
|
||||
@@ -392,4 +394,11 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
|
||||
return beatInterval = v;
|
||||
}
|
||||
|
||||
@:noCompletion private inline function get_globalCurFrame() {
|
||||
return animateAtlas != null ? (animateAtlas.anim.curFrame) : (animation.curAnim != null ? animation.curAnim.curFrame : 0);
|
||||
}
|
||||
@:noCompletion private inline function set_globalCurFrame(val:Int) {
|
||||
return animateAtlas != null ? (animateAtlas.anim.curFrame = val) : (animation.curAnim != null ? animation.curAnim.curFrame = val : val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,10 +164,36 @@ class MusicBeatState extends FlxState implements IBeatReceiver
|
||||
persistentUpdate = true;
|
||||
call("postCreate");
|
||||
if (!skipTransIn)
|
||||
openSubState(new MusicBeatTransition(null));
|
||||
startTransition(null);
|
||||
skipTransIn = false;
|
||||
skipTransOut = false;
|
||||
}
|
||||
|
||||
public function startTransition(?newState:FlxState, skipSubStates:Bool = false):Bool {
|
||||
if (!skipSubStates) {
|
||||
var curCheckingSub:FlxSubState = subState;
|
||||
var found:FlxSubState = null; // always gets the last one - Nex
|
||||
|
||||
while (curCheckingSub != null) {
|
||||
if (curCheckingSub is MusicBeatTransition) {
|
||||
found = null; // avoiding infinite loops - Nex
|
||||
break;
|
||||
}
|
||||
|
||||
if (curCheckingSub is MusicBeatSubstate && cast(curCheckingSub, MusicBeatSubstate).canOpenCustomTransition) found = curCheckingSub;
|
||||
curCheckingSub = curCheckingSub.subState;
|
||||
}
|
||||
|
||||
if (found != null) {
|
||||
found.openSubState(new MusicBeatTransition(newState));
|
||||
return true; // "Started from a substate?"
|
||||
}
|
||||
}
|
||||
|
||||
openSubState(new MusicBeatTransition(newState));
|
||||
return false;
|
||||
}
|
||||
|
||||
public function call(name:String, ?args:Array<Dynamic>, ?defaultVal:Dynamic):Dynamic {
|
||||
// calls the function on the assigned script
|
||||
if(stateScripts != null)
|
||||
@@ -232,7 +258,7 @@ class MusicBeatState extends FlxState implements IBeatReceiver
|
||||
public override function openSubState(subState:FlxSubState) {
|
||||
var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState));
|
||||
if (!e.cancelled)
|
||||
super.openSubState(subState);
|
||||
super.openSubState(e.substate is FlxSubState ? cast e.substate : subState);
|
||||
}
|
||||
|
||||
public override function onResize(w:Int, h:Int) {
|
||||
@@ -260,11 +286,9 @@ class MusicBeatState extends FlxState implements IBeatReceiver
|
||||
if (e.cancelled)
|
||||
return false;
|
||||
|
||||
if (skipTransOut)
|
||||
if (skipTransOut || (subState is MusicBeatTransition && cast(subState, MusicBeatTransition).newState != null))
|
||||
return true;
|
||||
if (subState is MusicBeatTransition && cast(subState, MusicBeatTransition).newState != null)
|
||||
return true;
|
||||
openSubState(new MusicBeatTransition(nextState));
|
||||
startTransition(e.substate);
|
||||
persistentUpdate = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
|
||||
private var lastBeat:Float = 0;
|
||||
private var lastStep:Float = 0;
|
||||
|
||||
/**
|
||||
* Whether this specifc substate can open custom transitions
|
||||
*/
|
||||
public var canOpenCustomTransition:Bool = false;
|
||||
/**
|
||||
* Current step
|
||||
*/
|
||||
@@ -221,7 +225,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
|
||||
public override function openSubState(subState:FlxSubState) {
|
||||
var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState));
|
||||
if (!e.cancelled)
|
||||
super.openSubState(subState);
|
||||
super.openSubState(e.substate is FlxSubState ? cast e.substate : subState);
|
||||
}
|
||||
|
||||
public override function onResize(w:Int, h:Int) {
|
||||
@@ -239,7 +243,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
|
||||
var e = event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState));
|
||||
if (e.cancelled)
|
||||
return false;
|
||||
return super.switchTo(nextState);
|
||||
return super.switchTo(e.substate);
|
||||
}
|
||||
|
||||
public override function onFocus() {
|
||||
@@ -254,19 +258,14 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
|
||||
|
||||
public var parent:FlxState;
|
||||
|
||||
public function onSubstateOpen() {
|
||||
|
||||
}
|
||||
public function onSubstateOpen() {}
|
||||
|
||||
public override function resetSubState() {
|
||||
super.resetSubState();
|
||||
if (subState != null && subState is MusicBeatSubstate) {
|
||||
var subState:MusicBeatSubstate = cast subState;
|
||||
subState.parent = this;
|
||||
super.resetSubState();
|
||||
if (subState != null)
|
||||
cast(subState, MusicBeatSubstate).onSubstateOpen();
|
||||
return;
|
||||
subState.onSubstateOpen();
|
||||
}
|
||||
super.resetSubState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,6 @@ using StringTools;
|
||||
|
||||
class Paths
|
||||
{
|
||||
/**
|
||||
* Preferred sound extension for the game's audio files.
|
||||
* Currently is set to `mp3` for web targets, and `ogg` for other targets.
|
||||
*/
|
||||
inline public static final SOUND_EXT = #if web "mp3" #else "ogg" #end;
|
||||
|
||||
public static var assetsTree:AssetsLibraryList;
|
||||
|
||||
public static var tempFramesCache:Map<String, FlxFramesCollection> = [];
|
||||
@@ -32,8 +26,8 @@ class Paths
|
||||
public static inline function getPath(file:String, ?library:String)
|
||||
return library != null ? '$library:assets/$library/$file' : 'assets/$file';
|
||||
|
||||
public static inline function video(key:String, ?ext:String = "mp4")
|
||||
return getPath('videos/$key.$ext');
|
||||
public static inline function video(key:String, ?ext:String)
|
||||
return getPath('videos/$key.${ext != null ? ext : Flags.VIDEO_EXT}');
|
||||
|
||||
public static inline function ndll(key:String)
|
||||
return getPath('ndlls/$key.ndll');
|
||||
@@ -65,31 +59,33 @@ class Paths
|
||||
inline static public function ps1(key:String, ?library:String)
|
||||
return getPath('data/$key.ps1', library);
|
||||
|
||||
static public function sound(key:String, ?library:String)
|
||||
return getPath('sounds/$key.$SOUND_EXT', library);
|
||||
static public function sound(key:String, ?library:String, ?ext:String)
|
||||
return getPath('sounds/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
|
||||
|
||||
inline static public function soundRandom(key:String, min:Int, max:Int, ?library:String)
|
||||
return sound(key + FlxG.random.int(min, max), library);
|
||||
|
||||
inline static public function music(key:String, ?library:String)
|
||||
return getPath('music/$key.$SOUND_EXT', library);
|
||||
inline static public function music(key:String, ?library:String, ?ext:String)
|
||||
return getPath('music/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
|
||||
|
||||
inline static public function voices(song:String, difficulty:String = "normal", ?prefix:String = "")
|
||||
{
|
||||
inline static public function voices(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
if (ext == null) ext = Flags.SOUND_EXT;
|
||||
song = song.toLowerCase();
|
||||
var diff = getPath('songs/${song}/song/Voices$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null);
|
||||
return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song}/song/Voices$prefix.$SOUND_EXT', null);
|
||||
var diff = getPath('songs/$song/song/Voices$prefix-${difficulty.toLowerCase()}.${ext}', null);
|
||||
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$prefix.${ext}', null);
|
||||
}
|
||||
|
||||
inline static public function inst(song:String, difficulty:String = "normal", ?prefix:String = "")
|
||||
{
|
||||
inline static public function inst(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
if (ext == null) ext = Flags.SOUND_EXT;
|
||||
song = song.toLowerCase();
|
||||
var diff = getPath('songs/${song}/song/Inst$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null);
|
||||
return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song}/song/Inst$prefix.$SOUND_EXT', null);
|
||||
var diff = getPath('songs/$song/song/Inst$prefix-${difficulty.toLowerCase()}.${ext}', null);
|
||||
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$prefix.${ext}', null);
|
||||
}
|
||||
|
||||
static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String = "png")
|
||||
{
|
||||
static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String) {
|
||||
if (ext == null) ext = Flags.IMAGE_EXT;
|
||||
if (checkForAtlas) {
|
||||
var atlasPath = getPath('images/$key/spritemap.$ext', library);
|
||||
var multiplePath = getPath('images/$key/1.$ext', library);
|
||||
@@ -113,9 +109,9 @@ class Paths
|
||||
return scriptPath;
|
||||
}
|
||||
|
||||
static public function chart(song:String, ?difficulty:String = "normal"):String
|
||||
static public function chart(song:String, ?difficulty:String):String
|
||||
{
|
||||
difficulty = difficulty.toLowerCase();
|
||||
difficulty = (difficulty != null ? difficulty : Flags.DEFAULT_DIFFICULTY).toLowerCase();
|
||||
song = song.toLowerCase();
|
||||
|
||||
return getPath('songs/$song/charts/$difficulty.json', null);
|
||||
@@ -160,33 +156,33 @@ class Paths
|
||||
return getPath('models/$key.awd');
|
||||
}
|
||||
|
||||
inline static public function getSparrowAtlas(key:String, ?library:String)
|
||||
return FlxAtlasFrames.fromSparrow(image(key, library), file('images/$key.xml', library));
|
||||
inline static public function getSparrowAtlas(key:String, ?library:String, ?ext:String)
|
||||
return FlxAtlasFrames.fromSparrow(image(key, library, ext), file('images/$key.xml', library));
|
||||
|
||||
inline static public function getSparrowAtlasAlt(key:String)
|
||||
return FlxAtlasFrames.fromSparrow('$key.png', '$key.xml');
|
||||
inline static public function getSparrowAtlasAlt(key:String, ?ext:String)
|
||||
return FlxAtlasFrames.fromSparrow('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.xml');
|
||||
|
||||
inline static public function getPackerAtlas(key:String, ?library:String)
|
||||
return FlxAtlasFrames.fromSpriteSheetPacker(image(key, library), file('images/$key.txt', library));
|
||||
inline static public function getPackerAtlas(key:String, ?library:String, ?ext:String)
|
||||
return FlxAtlasFrames.fromSpriteSheetPacker(image(key, library, ext), file('images/$key.txt', library));
|
||||
|
||||
inline static public function getPackerAtlasAlt(key:String)
|
||||
return FlxAtlasFrames.fromSpriteSheetPacker('$key.png', '$key.txt');
|
||||
inline static public function getPackerAtlasAlt(key:String, ?ext:String)
|
||||
return FlxAtlasFrames.fromSpriteSheetPacker('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.txt');
|
||||
|
||||
inline static public function getAsepriteAtlas(key:String, ?library:String)
|
||||
return FlxAtlasFrames.fromAseprite(image(key, library), file('images/$key.json', library));
|
||||
inline static public function getAsepriteAtlas(key:String, ?library:String, ?ext:String)
|
||||
return FlxAtlasFrames.fromAseprite(image(key, library, ext), file('images/$key.json', library));
|
||||
|
||||
inline static public function getAsepriteAtlasAlt(key:String)
|
||||
return FlxAtlasFrames.fromAseprite('$key.png', '$key.json');
|
||||
inline static public function getAsepriteAtlasAlt(key:String, ?ext:String)
|
||||
return FlxAtlasFrames.fromAseprite('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.json');
|
||||
|
||||
inline static public function getAssetsRoot():String
|
||||
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : './assets';
|
||||
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end;
|
||||
|
||||
/**
|
||||
* Gets frames at specified path.
|
||||
* @param key Path to the frames
|
||||
* @param library (Additional) library to load the frames from.
|
||||
*/
|
||||
public static function getFrames(key:String, assetsPath:Bool = false, ?library:String) {
|
||||
public static function getFrames(key:String, assetsPath:Bool = false, ?library:String, ?ext:String = null) {
|
||||
if (tempFramesCache.exists(key)) {
|
||||
var frames = tempFramesCache[key];
|
||||
if (frames != null && frames.parent != null && frames.parent.bitmap != null && frames.parent.bitmap.readable)
|
||||
@@ -194,7 +190,7 @@ class Paths
|
||||
else
|
||||
tempFramesCache.remove(key);
|
||||
}
|
||||
return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true));
|
||||
return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true, ext), false, null, false, ext);
|
||||
}
|
||||
|
||||
|
||||
@@ -206,10 +202,11 @@ class Paths
|
||||
* @param SkipAtlasCheck Whenever the atlas check should be skipped.
|
||||
* @return FlxFramesCollection Frames
|
||||
*/
|
||||
static function loadFrames(path:String, Unique:Bool = false, Key:String = null, SkipAtlasCheck:Bool = false):FlxFramesCollection {
|
||||
static function loadFrames(path:String, Unique:Bool = false, Key:String = null, SkipAtlasCheck:Bool = false, ?Ext:String = null):FlxFramesCollection {
|
||||
var noExt = Path.withoutExtension(path);
|
||||
var ext = Ext != null ? Ext : Flags.IMAGE_EXT;
|
||||
|
||||
if (Assets.exists('$noExt/1.png')) {
|
||||
if (Assets.exists('$noExt/1.${ext}')) {
|
||||
// MULTIPLE SPRITESHEETS!!
|
||||
|
||||
var graphic = FlxG.bitmap.add("flixel/images/logo/default.png", false, '$noExt/mult');
|
||||
@@ -220,18 +217,18 @@ class Paths
|
||||
trace("no frames yet for multiple atlases!!");
|
||||
var cur = 1;
|
||||
var finalFrames = new MultiFramesCollection(graphic);
|
||||
while(Assets.exists('$noExt/$cur.png')) {
|
||||
var spr = loadFrames('$noExt/$cur.png');
|
||||
while(Assets.exists('$noExt/$cur.${ext}')) {
|
||||
var spr = loadFrames('$noExt/$cur.${ext}');
|
||||
finalFrames.addFrames(spr);
|
||||
cur++;
|
||||
}
|
||||
return finalFrames;
|
||||
} else if (Assets.exists('$noExt.xml')) {
|
||||
return Paths.getSparrowAtlasAlt(noExt);
|
||||
return Paths.getSparrowAtlasAlt(noExt, ext);
|
||||
} else if (Assets.exists('$noExt.txt')) {
|
||||
return Paths.getPackerAtlasAlt(noExt);
|
||||
return Paths.getPackerAtlasAlt(noExt, ext);
|
||||
} else if (Assets.exists('$noExt.json')) {
|
||||
return Paths.getAsepriteAtlasAlt(noExt);
|
||||
return Paths.getAsepriteAtlasAlt(noExt, ext);
|
||||
}
|
||||
|
||||
var graph:FlxGraphic = FlxG.bitmap.add(path, Unique, Key);
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
package funkin.backend.chart;
|
||||
|
||||
import funkin.backend.chart.ChartData.ChartMetaData;
|
||||
|
||||
// These new structures are kinda a mess to port, i love and hate them at the same time; why the hell are every difficulty in the same file??? - Nex
|
||||
class BaseGameParser {
|
||||
public static function parseChart(data:Dynamic, metaData:Dynamic, result:ChartData) {
|
||||
// TO DO
|
||||
}
|
||||
|
||||
public static function parseMeta(data:Dynamic, result:ChartMetaData) {
|
||||
var data:SwagMetadata = data;
|
||||
result.name = data.songName;
|
||||
result.artist = data.artist;
|
||||
|
||||
result.customValues = {};
|
||||
result.customValues.timeChanges = data.timeChanges;
|
||||
var firstTimeChng:SwagTimeChange = data.timeChanges[0];
|
||||
result.bpm = firstTimeChng.bpm;
|
||||
result.beatsPerMeasure = firstTimeChng.b != null ? firstTimeChng.b : Flags.DEFAULT_BEATS_PER_MEASURE;
|
||||
|
||||
result.difficulties = data.playData.difficulties.concat(data.playData.songVariations);
|
||||
Reflect.deleteField(data.playData, "difficulties");
|
||||
|
||||
for (field in Reflect.fields(data.playData))
|
||||
Reflect.setProperty(result.customValues, field, Reflect.getProperty(data.playData, field));
|
||||
}
|
||||
|
||||
public static function encodeMeta(meta:ChartMetaData, ?chart:ChartData):SwagMetadata {
|
||||
var addVars:Dynamic = meta.customValues;
|
||||
var defStage:String = addVars.stage != null ? addVars.stage : Flags.DEFAULT_STAGE;
|
||||
var defChars:SwagCharactersList = addVars.characters != null ? addVars.characters : {player: Flags.DEFAULT_CHARACTER, girlfriend: Flags.DEFAULT_GIRLFRIEND, opponent: Flags.DEFAULT_OPPONENT};
|
||||
var defTimeCh:Array<SwagTimeChange>;
|
||||
|
||||
if (addVars.timeChanges != null && addVars.timeChanges.length > 0) {
|
||||
defTimeCh = addVars.timeChanges;
|
||||
defTimeCh[0].bpm = meta.bpm;
|
||||
}
|
||||
else defTimeCh = [{bpm: meta.bpm, t: -1}];
|
||||
|
||||
if (chart != null) {
|
||||
defStage = chart.stage;
|
||||
|
||||
var done:Array<Bool> = [false, false, false];
|
||||
for (strumLine in chart.strumLines) switch (strumLine.type) {
|
||||
case OPPONENT:
|
||||
if (!done[0]) {
|
||||
done[0] = true;
|
||||
defChars.opponent = strumLine.characters.getDefault([defChars.opponent])[0];
|
||||
}
|
||||
case PLAYER:
|
||||
if (!done[1]) {
|
||||
done[1] = true;
|
||||
defChars.player = strumLine.characters.getDefault([defChars.player])[0];
|
||||
}
|
||||
case ADDITIONAL:
|
||||
if (!done[3]) {
|
||||
done[3] = true;
|
||||
defChars.girlfriend = strumLine.characters.getDefault([defChars.girlfriend])[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result:SwagMetadata = {
|
||||
songName: meta.name,
|
||||
timeFormat: MILLISECONDS,
|
||||
artist: meta.artist,
|
||||
timeChanges: defTimeCh,
|
||||
looped: false,
|
||||
generatedBy: 'Basegame Chart Importer (Codename Engine)',
|
||||
version: Flags.BASEGAME_SONG_METADATA_VERSION,
|
||||
playData: {
|
||||
stage: defStage,
|
||||
characters: defChars,
|
||||
songVariations: addVars.songVariations != null ? [for (i in 0...addVars.songVariations.length) {meta.difficulties.remove(addVars.songVariations[i]); addVars.songVariations[i];}] : [],
|
||||
difficulties: meta.difficulties,
|
||||
noteStyle: addVars.noteStyle != null ? addVars.noteStyle : Flags.BASEGAME_DEFAULT_NOTE_STYLE,
|
||||
album: addVars.album != null ? addVars.album : Flags.BASEGAME_DEFAULT_ALBUM_ID,
|
||||
previewStart: addVars.previewStart != null ? addVars.previewStart : Flags.BASEGAME_DEFAULT_PREVIEW_START,
|
||||
previewEnd: addVars.previewEnd != null ? addVars.previewEnd : Flags.BASEGAME_DEFAULT_PREVIEW_END
|
||||
},
|
||||
};
|
||||
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function encodeChart(chart:ChartData):NewSwagSong {
|
||||
// TO DO
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// METADATA STRUCTURES
|
||||
typedef SwagMetadata =
|
||||
{
|
||||
var timeFormat:SwagTimeFormat;
|
||||
var artist:String;
|
||||
var songName:String;
|
||||
var playData:SwagPlayData;
|
||||
var timeChanges:Array<SwagTimeChange>;
|
||||
var generatedBy:String;
|
||||
var looped:Bool;
|
||||
var version:String;
|
||||
|
||||
var ?divisions:Int;
|
||||
var ?offsets:SwagSongOffsets;
|
||||
}
|
||||
|
||||
enum abstract SwagTimeFormat(String) from String to String
|
||||
{
|
||||
var TICKS = 'ticks';
|
||||
var FLOAT = 'float';
|
||||
var MILLISECONDS = 'ms';
|
||||
}
|
||||
|
||||
typedef SwagTimeChange =
|
||||
{
|
||||
var ?d:Int; // Time Signature Den
|
||||
var ?n:Int; // Time Signature Num
|
||||
var t:Int; // Time Stamp
|
||||
var ?b:Int; // Beat Time
|
||||
var ?bt:Array<Int>; // Beat Tuplets
|
||||
var bpm:Float;
|
||||
}
|
||||
|
||||
typedef SwagPlayData =
|
||||
{
|
||||
var album:String;
|
||||
var previewStart:Float;
|
||||
var previewEnd:Float;
|
||||
var stage:String;
|
||||
var characters:SwagCharactersList;
|
||||
var songVariations:Array<String>;
|
||||
var difficulties:Array<String>;
|
||||
var noteStyle:String;
|
||||
}
|
||||
|
||||
typedef SwagCharactersList =
|
||||
{
|
||||
var player:String;
|
||||
var girlfriend:String;
|
||||
var opponent:String;
|
||||
}
|
||||
|
||||
typedef SwagSongOffsets =
|
||||
{
|
||||
var ?instrumental:Float;
|
||||
var ?altInstrumentals:Dynamic;
|
||||
var ?vocals:Dynamic;
|
||||
}
|
||||
|
||||
// CHART STRUCTURE
|
||||
typedef NewSwagSong =
|
||||
{
|
||||
var version:String;
|
||||
var scrollSpeed:Dynamic; // Map<String, Float>
|
||||
var events:Array<SwagEvent>;
|
||||
var notes:Dynamic; // Map<String, Array<SwagNote>>
|
||||
var generatedBy:String;
|
||||
}
|
||||
|
||||
typedef SwagEvent =
|
||||
{
|
||||
var t:Float; // Time
|
||||
var e:String; // Event Kind
|
||||
var v:Dynamic; // Value (Map<String, Dynamic>)
|
||||
}
|
||||
|
||||
typedef SwagNote =
|
||||
{
|
||||
var t:Float; // Time
|
||||
var d:Int; // Data
|
||||
var l:Float; // Length
|
||||
var k:String; // Kind
|
||||
}
|
||||
@@ -12,11 +12,6 @@ import sys.FileSystem;
|
||||
using StringTools;
|
||||
|
||||
final class Chart {
|
||||
/**
|
||||
* Default background colors for songs without bg color
|
||||
*/
|
||||
public inline static var defaultColor:FlxColor = 0xFF9271FD;
|
||||
|
||||
public static final version:String = "1.6.0";
|
||||
|
||||
public static function loadEventsJson(songName:String) {
|
||||
@@ -32,7 +27,9 @@ final class Chart {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function loadChartMeta(songName:String, difficulty:String = "normal", fromMods:Bool = true) {
|
||||
public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true) {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
|
||||
var metaPath = Paths.file('songs/${songName.toLowerCase()}/meta.json');
|
||||
var metaDiffPath = Paths.file('songs/${songName.toLowerCase()}/meta-${difficulty.toLowerCase()}.json');
|
||||
|
||||
@@ -52,19 +49,19 @@ final class Chart {
|
||||
|
||||
if (data == null)
|
||||
data = {
|
||||
name: songName,
|
||||
bpm: 100
|
||||
name: songName
|
||||
};
|
||||
data.setFieldDefault("name", songName);
|
||||
data.setFieldDefault("beatsPerMeasure", 4);
|
||||
data.setFieldDefault("stepsPerBeat", 4);
|
||||
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
|
||||
data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE);
|
||||
data.setFieldDefault("stepsPerBeat", Flags.DEFAULT_STEPS_PER_BEAT);
|
||||
data.setFieldDefault("needsVoices", true);
|
||||
data.setFieldDefault("icon", "face");
|
||||
data.setFieldDefault("icon", Flags.DEFAULT_HEALTH_ICON);
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("coopAllowed", false);
|
||||
data.setFieldDefault("opponentModeAllowed", false);
|
||||
data.setFieldDefault("coopAllowed", Flags.DEFAULT_COOP_ALLOWED);
|
||||
data.setFieldDefault("opponentModeAllowed", Flags.DEFAULT_OPPONENT_MODE_ALLOWED);
|
||||
data.setFieldDefault("displayName", data.name);
|
||||
data.setFieldDefault("parsedColor", data.color.getColorFromDynamic().getDefault(defaultColor));
|
||||
data.setFieldDefault("parsedColor", data.color.getColorFromDynamic().getDefault(Flags.DEFAULT_COLOR));
|
||||
|
||||
if (data.difficulties.length <= 0) {
|
||||
data.difficulties = [for(f in Paths.getFolderContent('songs/${songName.toLowerCase()}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
@@ -88,7 +85,9 @@ final class Chart {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function parse(songName:String, difficulty:String = "normal"):ChartData {
|
||||
public static function parse(songName:String, ?difficulty:String):ChartData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
|
||||
var chartPath = Paths.chart(songName, difficulty);
|
||||
var base:ChartData = {
|
||||
strumLines: [],
|
||||
@@ -97,8 +96,8 @@ final class Chart {
|
||||
meta: {
|
||||
name: null
|
||||
},
|
||||
scrollSpeed: 2,
|
||||
stage: "stage",
|
||||
scrollSpeed: Flags.DEFAULT_SCROLL_SPEED,
|
||||
stage: Flags.DEFAULT_STAGE,
|
||||
codenameChart: true,
|
||||
fromMods: Paths.assetsTree.existsSpecific(chartPath, "TEXT", MODS)
|
||||
};
|
||||
@@ -202,7 +201,8 @@ final class Chart {
|
||||
* @param saveSettings
|
||||
* @return Filtered chart used for saving.
|
||||
*/
|
||||
public static function save(songFolderPath:String, chart:ChartData, difficulty:String = "normal", ?saveSettings:ChartSaveSettings):ChartData {
|
||||
public static function save(songFolderPath:String, chart:ChartData, ?difficulty:String, ?saveSettings:ChartSaveSettings):ChartData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
if (saveSettings == null) saveSettings = {};
|
||||
|
||||
if (saveSettings.saveMetaInChart == null) saveSettings.saveMetaInChart = true;
|
||||
@@ -220,11 +220,11 @@ final class Chart {
|
||||
var chartPath = '${songFolderPath}/$saveFolder/${difficulty.trim()}.json';
|
||||
var metaPath = '${songFolderPath}/meta.json';
|
||||
|
||||
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? "\t" : null));
|
||||
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null));
|
||||
|
||||
// idk how null reacts to it so better be sure
|
||||
if (saveSettings.overrideExistingMeta == true || !FileSystem.exists(metaPath))
|
||||
CoolUtil.safeSaveFile(metaPath, Json.stringify(meta, null, saveSettings.prettyPrint == true ? "\t" : null));
|
||||
CoolUtil.safeSaveFile(metaPath, Json.stringify(meta, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null));
|
||||
#end
|
||||
return filteredChart;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ typedef ChartMetaData = {
|
||||
public var ?bpm:Float;
|
||||
public var ?displayName:String;
|
||||
public var ?beatsPerMeasure:Float;
|
||||
public var ?stepsPerBeat:Float;
|
||||
public var ?stepsPerBeat:Int;
|
||||
public var ?icon:String;
|
||||
public var ?color:Dynamic;
|
||||
public var ?difficulties:Array<String>;
|
||||
@@ -28,6 +28,7 @@ typedef ChartMetaData = {
|
||||
public var ?opponentModeAllowed:Bool;
|
||||
public var ?customValues:Dynamic;
|
||||
public var ?needsVoices:Bool;
|
||||
public var ?artist:String;
|
||||
|
||||
// NOT TO BE EXPORTED
|
||||
public var ?parsedColor:FlxColor;
|
||||
|
||||
@@ -45,7 +45,7 @@ class FNFLegacyParser {
|
||||
|
||||
var camFocusedBF:Bool = false;
|
||||
var altAnims:Bool = false;
|
||||
var beatsPerMeasure:Float = data.beatsPerMeasure.getDefault(4);
|
||||
var beatsPerMeasure:Float = data.beatsPerMeasure.getDefault(Flags.DEFAULT_BEATS_PER_MEASURE);
|
||||
var curBPM:Float = data.bpm;
|
||||
var curTime:Float = 0;
|
||||
var curCrochet:Float = ((60 / curBPM) * 1000);
|
||||
@@ -132,10 +132,10 @@ class FNFLegacyParser {
|
||||
if ((swagSection.mustHitSection && strumLine.type == OPPONENT) ||
|
||||
(!swagSection.mustHitSection && strumLine.type == PLAYER))
|
||||
sectionNote[1] += 4;
|
||||
swagSection.sectionNotes.push(sectionNote);
|
||||
swagSection.sectionNotes.push(sectionNote);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {song: base};
|
||||
}
|
||||
|
||||
@@ -156,9 +156,9 @@ class FNFLegacyParser {
|
||||
for (strumLine in chart.strumLines)
|
||||
switch (strumLine.type) {
|
||||
case OPPONENT:
|
||||
if (base.player2 == null) base.player2 = strumLine.characters.getDefault(["dad"])[0];
|
||||
if (base.player2 == null) base.player2 = strumLine.characters.getDefault([Flags.DEFAULT_OPPONENT])[0];
|
||||
case PLAYER:
|
||||
if (base.player1 == null) base.player1 = strumLine.characters.getDefault(["bf"])[0];
|
||||
if (base.player1 == null) base.player1 = strumLine.characters.getDefault([Flags.DEFAULT_CHARACTER])[0];
|
||||
case ADDITIONAL: // do nothing
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ typedef SwagSong =
|
||||
// ADDITIONAL STUFF THAT MAY NOT BE PRESENT IN CHART
|
||||
var ?maxHealth:Float;
|
||||
var ?beatsPerMeasure:Float;
|
||||
var ?stepsPerBeat:Float;
|
||||
var ?stepsPerBeat:Int;
|
||||
}
|
||||
|
||||
typedef SwagSection =
|
||||
|
||||
@@ -32,7 +32,7 @@ class PsychParser {
|
||||
for (note in strumLine.notes) {
|
||||
var section:Int = Math.floor(Conductor.getStepForTime(note.time) / Conductor.getMeasureLength());
|
||||
var swagSection:SwagSection = base.notes[section];
|
||||
|
||||
|
||||
if (section >= 0 && section < base.notes.length) {
|
||||
var sectionNote:Array<Dynamic> = [
|
||||
note.time, // TIME
|
||||
@@ -47,7 +47,7 @@ class PsychParser {
|
||||
swagSection.sectionNotes.push(sectionNote);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var groupedEvents:Array<Array<ChartEvent>> = [];
|
||||
var __last:Array<ChartEvent> = null;
|
||||
var __lastTime:Float = Math.NaN;
|
||||
@@ -106,7 +106,7 @@ class PsychParser {
|
||||
|
||||
for (psychEvent in psychEvents)
|
||||
for (i in 1...3) // Turn both vals into strings
|
||||
if (!(psychEvent[i] is String))
|
||||
if (!(psychEvent[i] is String))
|
||||
psychEvent[i] = Std.string(psychEvent[i]);
|
||||
|
||||
base.events.push([events[0].time, psychEvents]);
|
||||
|
||||
@@ -19,89 +19,134 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
public static function getDefaultVariables(?script:Script):Map<String, Dynamic> {
|
||||
return [
|
||||
// Haxe related stuff
|
||||
"Std" => Std,
|
||||
"Math" => Math,
|
||||
"Reflect" => Reflect,
|
||||
"StringTools" => StringTools,
|
||||
"Json" => haxe.Json,
|
||||
"Std" => Std,
|
||||
"Math" => Math,
|
||||
"Reflect" => Reflect,
|
||||
"StringTools" => StringTools,
|
||||
"Json" => haxe.Json,
|
||||
|
||||
// OpenFL & Lime related stuff
|
||||
"Assets" => openfl.utils.Assets,
|
||||
"Application" => lime.app.Application,
|
||||
"Application" => lime.app.Application,
|
||||
"Main" => funkin.backend.system.Main,
|
||||
"window" => lime.app.Application.current.window,
|
||||
|
||||
// Flixel related stuff
|
||||
"FlxG" => flixel.FlxG,
|
||||
"FlxSprite" => flixel.FlxSprite,
|
||||
"FlxBasic" => flixel.FlxBasic,
|
||||
"FlxCamera" => flixel.FlxCamera,
|
||||
"state" => flixel.FlxG.state,
|
||||
"FlxEase" => flixel.tweens.FlxEase,
|
||||
"FlxTween" => flixel.tweens.FlxTween,
|
||||
"FlxSound" => flixel.sound.FlxSound,
|
||||
"FlxAssets" => flixel.system.FlxAssets,
|
||||
"FlxMath" => flixel.math.FlxMath,
|
||||
"FlxGroup" => flixel.group.FlxGroup,
|
||||
"FlxTypedGroup" => flixel.group.FlxGroup.FlxTypedGroup,
|
||||
"state" => flixel.FlxG.state,
|
||||
"FlxG" => flixel.FlxG,
|
||||
"FlxSprite" => flixel.FlxSprite,
|
||||
"FlxBasic" => flixel.FlxBasic,
|
||||
"FlxCamera" => flixel.FlxCamera,
|
||||
"FlxEase" => flixel.tweens.FlxEase,
|
||||
"FlxTween" => flixel.tweens.FlxTween,
|
||||
"FlxSound" => flixel.sound.FlxSound,
|
||||
"FlxAssets" => flixel.system.FlxAssets,
|
||||
"FlxMath" => flixel.math.FlxMath,
|
||||
"FlxGroup" => flixel.group.FlxGroup,
|
||||
"FlxTypedGroup" => flixel.group.FlxGroup.FlxTypedGroup,
|
||||
"FlxSpriteGroup" => flixel.group.FlxSpriteGroup,
|
||||
"FlxTypeText" => flixel.addons.text.FlxTypeText,
|
||||
"FlxText" => flixel.text.FlxText,
|
||||
"FlxTimer" => flixel.util.FlxTimer,
|
||||
"FlxPoint" => CoolUtil.getMacroAbstractClass("flixel.math.FlxPoint"),
|
||||
"FlxAxes" => CoolUtil.getMacroAbstractClass("flixel.util.FlxAxes"),
|
||||
"FlxColor" => CoolUtil.getMacroAbstractClass("flixel.util.FlxColor"),
|
||||
"FlxTypeText" => flixel.addons.text.FlxTypeText,
|
||||
"FlxText" => flixel.text.FlxText,
|
||||
"FlxTimer" => flixel.util.FlxTimer,
|
||||
"FlxPoint" => CoolUtil.getMacroAbstractClass("flixel.math.FlxPoint"),
|
||||
"FlxAxes" => CoolUtil.getMacroAbstractClass("flixel.util.FlxAxes"),
|
||||
"FlxColor" => CoolUtil.getMacroAbstractClass("flixel.util.FlxColor"),
|
||||
|
||||
// Engine related stuff
|
||||
"engine" => {
|
||||
commit: funkin.backend.system.macros.GitCommitMacro.commitNumber,
|
||||
hash: funkin.backend.system.macros.GitCommitMacro.commitHash,
|
||||
commit: Flags.COMMIT_NUMBER,
|
||||
hash: Flags.COMMIT_HASH,
|
||||
build: 2675, // 2675 being the last build num before it was removed
|
||||
name: "Codename Engine"
|
||||
},
|
||||
"ModState" => funkin.backend.scripting.ModState,
|
||||
"ModSubState" => funkin.backend.scripting.ModSubState,
|
||||
"PlayState" => funkin.game.PlayState,
|
||||
"GameOverSubstate" => funkin.game.GameOverSubstate,
|
||||
"ModState" => funkin.backend.scripting.ModState,
|
||||
"ModSubState" => funkin.backend.scripting.ModSubState,
|
||||
"PlayState" => funkin.game.PlayState,
|
||||
"GameOverSubstate" => funkin.game.GameOverSubstate,
|
||||
"HealthIcon" => funkin.game.HealthIcon,
|
||||
"HudCamera" => funkin.game.HudCamera,
|
||||
"Note" => funkin.game.Note,
|
||||
"Strum" => funkin.game.Strum,
|
||||
"StrumLine" => funkin.game.StrumLine,
|
||||
"Character" => funkin.game.Character,
|
||||
"Boyfriend" => funkin.game.Character, // for compatibility
|
||||
"PauseSubstate" => funkin.menus.PauseSubState,
|
||||
"FreeplayState" => funkin.menus.FreeplayState,
|
||||
"MainMenuState" => funkin.menus.MainMenuState,
|
||||
"PauseSubState" => funkin.menus.PauseSubState,
|
||||
"HudCamera" => funkin.game.HudCamera,
|
||||
"Note" => funkin.game.Note,
|
||||
"Strum" => funkin.game.Strum,
|
||||
"StrumLine" => funkin.game.StrumLine,
|
||||
"Character" => funkin.game.Character,
|
||||
"Boyfriend" => funkin.game.Character, // for compatibility
|
||||
"PauseSubstate" => funkin.menus.PauseSubState,
|
||||
"FreeplayState" => funkin.menus.FreeplayState,
|
||||
"MainMenuState" => funkin.menus.MainMenuState,
|
||||
"PauseSubState" => funkin.menus.PauseSubState,
|
||||
"StoryMenuState" => funkin.menus.StoryMenuState,
|
||||
"TitleState" => funkin.menus.TitleState,
|
||||
"Options" => funkin.options.Options,
|
||||
"Paths" => funkin.backend.assets.Paths,
|
||||
"Conductor" => funkin.backend.system.Conductor,
|
||||
"FunkinShader" => funkin.backend.shaders.FunkinShader,
|
||||
"CustomShader" => funkin.backend.shaders.CustomShader,
|
||||
"Options" => funkin.options.Options,
|
||||
"Paths" => funkin.backend.assets.Paths,
|
||||
"Conductor" => funkin.backend.system.Conductor,
|
||||
"FunkinShader" => funkin.backend.shaders.FunkinShader,
|
||||
"CustomShader" => funkin.backend.shaders.CustomShader,
|
||||
"FunkinText" => funkin.backend.FunkinText,
|
||||
"FlxAnimate" => funkin.backend.FlxAnimate,
|
||||
"FunkinSprite" => funkin.backend.FunkinSprite,
|
||||
"Alphabet" => funkin.menus.ui.Alphabet,
|
||||
"Alphabet" => funkin.menus.ui.Alphabet,
|
||||
|
||||
"CoolUtil" => funkin.backend.utils.CoolUtil,
|
||||
"IniUtil" => funkin.backend.utils.IniUtil,
|
||||
"XMLUtil" => funkin.backend.utils.XMLUtil,
|
||||
#if sys "ZipUtil" => funkin.backend.utils.ZipUtil, #end
|
||||
"MarkdownUtil" => funkin.backend.utils.MarkdownUtil,
|
||||
"CoolUtil" => funkin.backend.utils.CoolUtil,
|
||||
"IniUtil" => funkin.backend.utils.IniUtil,
|
||||
"XMLUtil" => funkin.backend.utils.XMLUtil,
|
||||
#if sys "ZipUtil" => funkin.backend.utils.ZipUtil, #end
|
||||
"MarkdownUtil" => funkin.backend.utils.MarkdownUtil,
|
||||
"EngineUtil" => funkin.backend.utils.EngineUtil,
|
||||
"MemoryUtil" => funkin.backend.utils.MemoryUtil,
|
||||
"BitmapUtil" => funkin.backend.utils.BitmapUtil,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally to keep backwards compatibility with old scripts.
|
||||
* This gets set on `hscript.Interp.importRedirects`,
|
||||
* if you wanna modify it, please edit `hscript.Interp.importRedirects` directly.
|
||||
**/
|
||||
public static function getDefaultImportRedirects():Map<String, String> {
|
||||
var redirects:Map<String, String> = [
|
||||
"funkin.savedata.FunkinSave" => "funkin.backend.utils.FunkinSave"
|
||||
];
|
||||
|
||||
final events = "funkin.backend.scripting.events.";
|
||||
redirects[events + "CharacterNodeEvent"] = events + "character.CharacterNodeEvent";
|
||||
redirects[events + "CharacterXMLEvent"] = events + "character.CharacterXMLEvent";
|
||||
redirects[events + "DanceEvent"] = events + "character.DanceEvent";
|
||||
redirects[events + "DirectionAnimEvent"] = events + "character.DirectionAnimEvent";
|
||||
redirects[events + "DiscordPresenceUpdateEvent"] = events + "discord.DiscordPresenceUpdateEvent";
|
||||
redirects[events + "GameOverCreationEvent"] = events + "gameover.GameOverCreationEvent";
|
||||
redirects[events + "CamMoveEvent"] = events + "gameplay.CamMoveEvent";
|
||||
redirects[events + "CountdownEvent"] = events + "gameplay.CountdownEvent";
|
||||
redirects[events + "EventGameEvent"] = events + "gameplay.EventGameEvent";
|
||||
redirects[events + "GameOverEvent"] = events + "gameplay.GameOverEvent";
|
||||
redirects[events + "RatingUpdateEvent"] = events + "gameplay.RatingUpdateEvent";
|
||||
redirects[events + "HealthIconChangeEvent"] = events + "healthicon.HealthIconChangeEvent";
|
||||
redirects[events + "FreeplayAlphaUpdateEvent"] = events + "menu.freeplay.FreeplayAlphaUpdateEvent";
|
||||
redirects[events + "FreeplaySongSelectEvent"] = events + "menu.freeplay.FreeplaySongSelectEvent";
|
||||
redirects[events + "MenuChangeEvent"] = events + "menu.MenuChangeEvent";
|
||||
redirects[events + "PauseCreationEvent"] = events + "menu.pause.PauseCreationEvent";
|
||||
redirects[events + "WeekSelectEvent"] = events + "menu.storymenu.WeekSelectEvent";
|
||||
redirects[events + "InputSystemEvent"] = events + "note.InputSystemEvent";
|
||||
redirects[events + "NoteCreationEvent"] = events + "note.NoteCreationEvent";
|
||||
redirects[events + "NoteHitEvent"] = events + "note.NoteHitEvent";
|
||||
redirects[events + "NoteMissEvent"] = events + "note.NoteMissEvent";
|
||||
redirects[events + "NoteUpdateEvent"] = events + "note.NoteUpdateEvent";
|
||||
redirects[events + "SimpleNoteEvent"] = events + "note.SimpleNoteEvent";
|
||||
redirects[events + "StrumCreationEvent"] = events + "note.StrumCreationEvent";
|
||||
redirects[events + "SplashShowEvent"] = events + "splash.SplashShowEvent";
|
||||
redirects[events + "PlayAnimContext"] = events + "sprite.PlayAnimContext";
|
||||
redirects[events + "PlayAnimEvent"] = events + "sprite.PlayAnimEvent";
|
||||
redirects[events + "StageNodeEvent"] = events + "stage.StageNodeEvent";
|
||||
redirects[events + "StageXMLEvent"] = events + "stage.StageXMLEvent";
|
||||
|
||||
return redirects;
|
||||
}
|
||||
|
||||
public static function getDefaultPreprocessors():Map<String, Dynamic> {
|
||||
var defines = funkin.backend.system.macros.DefinesMacro.defines;
|
||||
defines.set("CODENAME_ENGINE", true);
|
||||
defines.set("CODENAME_VER", Application.current.meta.get('version'));
|
||||
defines.set("CODENAME_BUILD", 2675); // 2675 being the last build num before it was removed
|
||||
defines.set("CODENAME_COMMIT", funkin.backend.system.macros.GitCommitMacro.commitNumber);
|
||||
defines.set("CODENAME_COMMIT", Flags.COMMIT_NUMBER);
|
||||
return defines;
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -5,9 +5,4 @@ final class AmountEvent extends CancellableEvent {
|
||||
* Amount
|
||||
*/
|
||||
public var amount:Null<Int>;
|
||||
|
||||
/**
|
||||
* Shows whether or not psych users complained about this class
|
||||
*/
|
||||
public var psychUsersComplained:Bool = true;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
package funkin.backend.scripting.events;
|
||||
|
||||
final class DrawEvent extends CancellableEvent {
|
||||
// temp empty for now - Nex
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
## NOTE
|
||||
Keep in mind that a certain criteria was used to place events in their corresponding folders which is simply what the events themselves are mostly related to.
|
||||
|
||||
For example:
|
||||
|
||||
- `GameOverEvent` inside of the `gameplay` folder instead of the `gameover` folder since it gets called in the `PlayState` class (that owns the `gameplay` folder) instead of the `GameOverSubstate` class itself;
|
||||
- `NoteHitEvent` gets indeed called by the `PlayState` class but it's also used in the `StrumLine` (For those who didn't really know, Strumlines are the Input Notes) class and since the event's name itself starts with the word `Note` it was more accurate to place it in the `note` folder instead of the `gameplay` folder that `PlayState` owns.
|
||||
@@ -4,7 +4,7 @@ import flixel.FlxState;
|
||||
|
||||
final class StateEvent extends CancellableEvent {
|
||||
/**
|
||||
* Substate that is about to be opened/closed
|
||||
* Substate or State that is about to be opened/closed
|
||||
*/
|
||||
public var substate:FlxState;
|
||||
public var substate:FlxState; // WHY is it named substate :sob: - Nex
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.character;
|
||||
|
||||
import funkin.game.Character;
|
||||
import haxe.xml.Access;
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.character;
|
||||
|
||||
import funkin.game.Character;
|
||||
import haxe.xml.Access;
|
||||
@@ -7,7 +7,7 @@ final class CharacterXMLEvent extends CancellableEvent {
|
||||
/**
|
||||
* The character instance
|
||||
*/
|
||||
public var character:Character;
|
||||
public var character:Character;
|
||||
|
||||
/**
|
||||
* The xml
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.character;
|
||||
|
||||
final class DanceEvent extends CancellableEvent {
|
||||
public var danced:Bool;
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.character;
|
||||
|
||||
import funkin.backend.scripting.events.sprite.PlayAnimContext;
|
||||
|
||||
final class DirectionAnimEvent extends CancellableEvent {
|
||||
/**
|
||||
@@ -0,0 +1,14 @@
|
||||
package funkin.backend.scripting.events.dialogue;
|
||||
|
||||
/**
|
||||
* CANCEL this event to prevent continuing with the next dialogue.
|
||||
*
|
||||
* **NOTE**: To access the current, past or next lines, use `curLine`, `lastLine` or also `dialogueLines` in the `DialogueCutscene` class (they're globals and can be accessed at any time as long as the Dialogue Cutscene is active)!
|
||||
*/
|
||||
final class DialogueNextLineEvent extends CancellableEvent
|
||||
{
|
||||
/**
|
||||
* If the dialogue has just been opened.
|
||||
*/
|
||||
public var playFirst:Bool = false;
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.discord;
|
||||
|
||||
final class DiscordPresenceUpdateEvent extends CancellableEvent {
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.gameover;
|
||||
|
||||
/**
|
||||
* CANCEL this event to prevent default behaviour!
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.gameplay;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.game.StrumLine;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.gameplay;
|
||||
|
||||
import flixel.sound.FlxSound;
|
||||
import flixel.tweens.FlxTween;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.gameplay;
|
||||
|
||||
import funkin.backend.chart.ChartData;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.gameplay;
|
||||
|
||||
import funkin.game.Character;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.gameplay;
|
||||
|
||||
final class RatingUpdateEvent extends CancellableEvent {
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.healthicon;
|
||||
|
||||
final class HealthIconChangeEvent extends CancellableEvent {
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.menu;
|
||||
|
||||
final class MenuChangeEvent extends CancellableEvent {
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.menu.freeplay;
|
||||
|
||||
final class FreeplayAlphaUpdateEvent extends CancellableEvent {
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.menu.freeplay;
|
||||
|
||||
final class FreeplaySongSelectEvent extends CancellableEvent {
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.menu.pause;
|
||||
|
||||
/**
|
||||
* CANCEL this event to prevent default behaviour!
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.menu.storymenu;
|
||||
|
||||
import funkin.menus.StoryMenuState.WeekData;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.note;
|
||||
|
||||
import funkin.game.StrumLine;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.note;
|
||||
|
||||
import funkin.game.Note;
|
||||
|
||||
+19
-2
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.note;
|
||||
|
||||
import funkin.game.Character;
|
||||
import funkin.game.Note;
|
||||
@@ -24,9 +24,19 @@ final class NoteHitEvent extends CancellableEvent {
|
||||
*/
|
||||
public var countScore:Bool = true;
|
||||
/**
|
||||
* Whenever ratings should be shown or not.
|
||||
* Whenever ratings (Rating sprite, Digits sprites and Combo sprite) should be shown or not.
|
||||
*
|
||||
* NOTE: Whether it's `true` use `displayRating` and `displayCombo` (plus `minDigitDisplay` in the PlayState class) to change what's going to pop up!
|
||||
*/
|
||||
public var showRating:Null<Bool> = null;
|
||||
/**
|
||||
* Whenever the Rating sprites should be shown or not.
|
||||
*/
|
||||
public var displayRating:Bool;
|
||||
/**
|
||||
* Whenever the Combo sprite should be shown or not (like old Week 7 patches).
|
||||
*/
|
||||
public var displayCombo:Bool;
|
||||
/**
|
||||
* Note that has been pressed
|
||||
*/
|
||||
@@ -123,6 +133,13 @@ final class NoteHitEvent extends CancellableEvent {
|
||||
@:dox(hide)
|
||||
public function cancelDeletion() {preventDeletion();}
|
||||
|
||||
/**
|
||||
* Forces the note to be deleted.
|
||||
**/
|
||||
public function forceDeletion() {
|
||||
deleteNote = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the vocals volume from being set to 1 after pressing the note.
|
||||
*/
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.note;
|
||||
|
||||
import funkin.game.Character;
|
||||
import funkin.game.Note;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.note;
|
||||
|
||||
import funkin.game.Note;
|
||||
import funkin.game.Strum;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.note;
|
||||
|
||||
import funkin.game.Note;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.note;
|
||||
|
||||
import funkin.game.Strum;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package funkin.backend.scripting.events.shader;
|
||||
|
||||
final class ShaderProcessEvent extends CancellableEvent
|
||||
{
|
||||
public var source:String;
|
||||
|
||||
public var storageType:String;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package funkin.backend.scripting.events.soundtray;
|
||||
|
||||
final class SoundTrayShowEvent extends CancellableEvent
|
||||
{
|
||||
public var up:Bool;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package funkin.backend.scripting.events.soundtray;
|
||||
|
||||
final class SoundTrayTextEvent extends CancellableEvent
|
||||
{
|
||||
public var checkIfNull:Bool;
|
||||
|
||||
public var reloadDefaultTextFormat:Bool;
|
||||
|
||||
public var displayTxt:String;
|
||||
|
||||
public var y:Float;
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.splash;
|
||||
|
||||
import funkin.game.SplashGroup;
|
||||
import funkin.game.Strum;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.sprite;
|
||||
|
||||
/**
|
||||
Contains all contexts possible for `PlayAnimEvent`.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.sprite;
|
||||
|
||||
final class PlayAnimEvent extends CancellableEvent {
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.stage;
|
||||
|
||||
import funkin.game.Stage;
|
||||
import haxe.xml.Access;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.backend.scripting.events;
|
||||
package funkin.backend.scripting.events.stage;
|
||||
|
||||
import funkin.game.Stage;
|
||||
import haxe.xml.Access;
|
||||
@@ -25,6 +25,10 @@ class CustomShader extends FunkinShader {
|
||||
var fragCode = Assets.exists(fragShaderPath) ? Assets.getText(fragShaderPath) : null;
|
||||
var vertCode = Assets.exists(vertShaderPath) ? Assets.getText(vertShaderPath) : null;
|
||||
|
||||
fileName = name;
|
||||
fragFileName = fragShaderPath;
|
||||
vertFileName = vertShaderPath;
|
||||
|
||||
path = fragShaderPath+vertShaderPath;
|
||||
|
||||
if (fragCode == null && vertCode == null)
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package funkin.backend.shaders;
|
||||
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import haxe.Exception;
|
||||
import hscript.IHScriptCustomBehaviour;
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
|
||||
import lime.utils.Float32Array;
|
||||
|
||||
import openfl.display.ShaderParameterType;
|
||||
import openfl.display.ShaderParameter;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.ShaderInput;
|
||||
import openfl.display.ShaderParameter;
|
||||
import openfl.display.ShaderParameterType;
|
||||
import openfl.display3D._internal.GLProgram;
|
||||
import openfl.display3D._internal.GLShader;
|
||||
import openfl.utils._internal.Log;
|
||||
|
||||
using StringTools;
|
||||
@:access(openfl.display3D.Context3D)
|
||||
@@ -18,8 +20,20 @@ using StringTools;
|
||||
@:access(openfl.display.ShaderParameter)
|
||||
class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
private static var __instanceFields = Type.getInstanceFields(FunkinShader);
|
||||
private static var FRAGMENT_SHADER = 0;
|
||||
private static var VERTEX_SHADER = 1;
|
||||
|
||||
public var onGLUpdate:FlxTypedSignal<Void->Void> = new FlxTypedSignal<Void->Void>();
|
||||
public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
|
||||
|
||||
public var glslVer:String = "120";
|
||||
public var fileName:String = "FunkinShader";
|
||||
public var fragFileName:String = "FunkinShader";
|
||||
public var vertFileName:String = "FunkinShader";
|
||||
|
||||
public var shaderPrefix:String = "";
|
||||
public var fragmentPrefix:String = "";
|
||||
public var vertexPrefix:String = "";
|
||||
|
||||
/**
|
||||
* Creates a new shader from the specified fragment and vertex source.
|
||||
@@ -38,11 +52,171 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
super();
|
||||
}
|
||||
|
||||
static var IMPORT_REGEX = ~/#import\s+<(.*)>/;
|
||||
|
||||
private function processImports(value:String, type:Int):String
|
||||
{
|
||||
while(IMPORT_REGEX.match(value))
|
||||
{
|
||||
var importPath = IMPORT_REGEX.matched(1);
|
||||
var importSource = Assets.getText("assets/shaders/" + importPath);
|
||||
if(importSource == null) {
|
||||
var fileName = type == FRAGMENT_SHADER ? fragFileName : vertFileName;
|
||||
Logs.traceColored([
|
||||
Logs.logText('[Shader] ', RED),
|
||||
Logs.logText('Failed to import shader ${importPath} in ${fileName}', RED),
|
||||
]);
|
||||
} else {
|
||||
value = value.replace(IMPORT_REGEX.matched(0), importSource);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static var ERROR_POS_REGEX = ~/(\d+):(\d+): (.*)/g;
|
||||
static var ERROR_REGEX = ~/ERROR: (\d+):(\d+): (.*)/g;
|
||||
static var ERROR_REGEX_2 = ~/(\d+)\((\d+)\) : error ([^:]+): (.*)/g;
|
||||
@:noCompletion private override function __createGLShader(source:String, type:Int):GLShader
|
||||
{
|
||||
var gl = __context.gl;
|
||||
|
||||
var shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
var shaderInfoLog = gl.getShaderInfoLog(shader);
|
||||
var hasInfoLog = shaderInfoLog != null && StringTools.trim(shaderInfoLog) != "";
|
||||
var compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
||||
|
||||
if (hasInfoLog || compileStatus == 0)
|
||||
{
|
||||
var isVertexShader = type == gl.VERTEX_SHADER;
|
||||
var messageBuf = new StringBuf();
|
||||
messageBuf.add((compileStatus == 0) ? "Error" : "Info");
|
||||
if(isVertexShader) {
|
||||
messageBuf.add(" compiling vertex shader");
|
||||
if(vertFileName != null && vertFileName.length > 0) {
|
||||
messageBuf.add(" (" + vertFileName + ")");
|
||||
}
|
||||
} else {
|
||||
messageBuf.add(" compiling fragment shader");
|
||||
if(fragFileName != null && fragFileName.length > 0) {
|
||||
messageBuf.add(" (" + fragFileName + ")");
|
||||
}
|
||||
}
|
||||
messageBuf.add("\n");
|
||||
var errorPositions = [];
|
||||
var regex = null;
|
||||
var tmp = shaderInfoLog;
|
||||
if(shaderInfoLog.contains(" : error ")) {
|
||||
regex = ERROR_REGEX_2;
|
||||
|
||||
while(regex.match(tmp)) {
|
||||
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(4)));
|
||||
tmp = regex.matchedRight();
|
||||
}
|
||||
} else if(shaderInfoLog.contains("ERROR: ")) {
|
||||
regex = ERROR_REGEX;
|
||||
|
||||
while(regex.match(tmp)) {
|
||||
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
|
||||
tmp = regex.matchedRight();
|
||||
}
|
||||
} else {
|
||||
regex = ERROR_POS_REGEX;
|
||||
|
||||
while(regex.match(tmp)) {
|
||||
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
|
||||
tmp = regex.matchedRight();
|
||||
}
|
||||
}
|
||||
var splitSource = source.split("\n");
|
||||
for(error in errorPositions) {
|
||||
messageBuf.add("ERROR: Line: " + error.line);
|
||||
if(error.column > 0) {
|
||||
messageBuf.add(", Column: " + error.column);
|
||||
}
|
||||
messageBuf.add(", " + error.message);
|
||||
if(error.line < splitSource.length) {
|
||||
messageBuf.add("\nLine: ");
|
||||
messageBuf.add(splitSource[error.line-1].trim());
|
||||
}
|
||||
messageBuf.add("\n\n");
|
||||
}
|
||||
var hasErrorPosition = errorPositions.length > 0;
|
||||
if(hasErrorPosition) {
|
||||
messageBuf.add("Raw shader info log:\n");
|
||||
}
|
||||
messageBuf.add(shaderInfoLog);
|
||||
messageBuf.add("\n");
|
||||
messageBuf.add(source);
|
||||
|
||||
var message = messageBuf.toString();
|
||||
if (compileStatus == 0) Log.error(message);
|
||||
else if (hasInfoLog) Log.debug(message);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
@:noCompletion private override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
|
||||
{
|
||||
var program:GLProgram = null;
|
||||
try
|
||||
{
|
||||
var gl = __context.gl;
|
||||
|
||||
var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER);
|
||||
var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER);
|
||||
|
||||
program = gl.createProgram();
|
||||
|
||||
// Fix support for drivers that don't draw if attribute 0 is disabled
|
||||
for (param in __paramFloat)
|
||||
{
|
||||
if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_"))
|
||||
{
|
||||
gl.bindAttribLocation(program, 0, param.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
|
||||
if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0)
|
||||
{
|
||||
var messageBuf = new StringBuf();
|
||||
messageBuf.add("Unable to initialize the shader program");
|
||||
messageBuf.add("\n");
|
||||
messageBuf.add(gl.getProgramInfoLog(program));
|
||||
var message = messageBuf.toString();
|
||||
Log.error(message);
|
||||
}
|
||||
}
|
||||
catch (error:Dynamic)
|
||||
{
|
||||
Logs.traceColored([
|
||||
Logs.logText('[Shader] ', BLUE),
|
||||
Logs.logText('Failed to compile shader ${fileName}: ', RED),
|
||||
Logs.logText(Std.string(error))
|
||||
], TRACE);
|
||||
}
|
||||
return program;
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
var glRawFragmentSource:String;
|
||||
var glRawVertexSource:String;
|
||||
|
||||
@:noCompletion override private function set_glFragmentSource(value:String):String
|
||||
{
|
||||
if(value == null)
|
||||
value = ShaderTemplates.defaultFragmentSource;
|
||||
glRawFragmentSource = value;
|
||||
value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody);
|
||||
value = processImports(value, FRAGMENT_SHADER);
|
||||
if (value != __glFragmentSource)
|
||||
{
|
||||
__glSourceDirty = true;
|
||||
@@ -55,7 +229,9 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
{
|
||||
if(value == null)
|
||||
value = ShaderTemplates.defaultVertexSource;
|
||||
glRawVertexSource = value;
|
||||
value = value.replace("#pragma header", ShaderTemplates.vertHeader).replace("#pragma body", ShaderTemplates.vertBody);
|
||||
value = processImports(value, VERTEX_SHADER);
|
||||
if (value != __glVertexSource)
|
||||
{
|
||||
__glSourceDirty = true;
|
||||
@@ -64,6 +240,11 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
return __glVertexSource = value;
|
||||
}
|
||||
|
||||
override function __updateGL():Void {
|
||||
onGLUpdate.dispatch();
|
||||
super.__updateGL();
|
||||
}
|
||||
|
||||
@:noCompletion private override function __initGL():Void
|
||||
{
|
||||
if (__glSourceDirty || __paramBool == null)
|
||||
@@ -83,23 +264,28 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
|
||||
if (__context != null && program == null)
|
||||
{
|
||||
var prefix = '#version ${glslVer}\n';
|
||||
var prefixBuf = new StringBuf();
|
||||
prefixBuf.add('#version ${glslVer}\n');
|
||||
prefixBuf.add(shaderPrefix);
|
||||
|
||||
var gl = __context.gl;
|
||||
|
||||
prefix += "#ifdef GL_ES
|
||||
"
|
||||
+ (precisionHint == FULL ? "#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif" : "precision lowp float;")
|
||||
+ "
|
||||
#endif
|
||||
";
|
||||
prefixBuf.add("#ifdef GL_ES\n");
|
||||
if (precisionHint == FULL) {
|
||||
prefixBuf.add("#ifdef GL_FRAGMENT_PRECISION_HIGH\n");
|
||||
prefixBuf.add("precision highp float;\n");
|
||||
prefixBuf.add("#else\n");
|
||||
prefixBuf.add("precision mediump float;\n");
|
||||
prefixBuf.add("#endif\n");
|
||||
} else {
|
||||
prefixBuf.add("precision lowp float;\n");
|
||||
}
|
||||
prefixBuf.add("#endif\n");
|
||||
|
||||
var vertex = prefix + glVertexSource;
|
||||
var fragment = prefix + glFragmentSource;
|
||||
var prefix = prefixBuf.toString();
|
||||
|
||||
var vertex = prefix + vertexPrefix + glVertexSource;
|
||||
var fragment = prefix + fragmentPrefix + glFragmentSource;
|
||||
|
||||
var id = vertex + fragment;
|
||||
|
||||
@@ -110,9 +296,7 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
else
|
||||
{
|
||||
program = __context.createProgram(GLSL);
|
||||
|
||||
program.__glProgram = __createGLProgram(vertex, fragment);
|
||||
|
||||
__context.__programs.set(id, program);
|
||||
}
|
||||
|
||||
@@ -120,56 +304,35 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
{
|
||||
glProgram = program.__glProgram;
|
||||
|
||||
for (input in __inputBitmapData)
|
||||
{
|
||||
for (input in __inputBitmapData) {
|
||||
|
||||
if (input.__isUniform)
|
||||
{
|
||||
if (input.__isUniform) {
|
||||
input.index = gl.getUniformLocation(glProgram, input.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
input.index = gl.getAttribLocation(glProgram, input.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in __paramBool)
|
||||
{
|
||||
|
||||
if (parameter.__isUniform)
|
||||
{
|
||||
for (parameter in __paramBool) {
|
||||
if (parameter.__isUniform) {
|
||||
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in __paramFloat)
|
||||
{
|
||||
|
||||
if (parameter.__isUniform)
|
||||
{
|
||||
for (parameter in __paramFloat) {
|
||||
if (parameter.__isUniform) {
|
||||
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in __paramInt)
|
||||
{
|
||||
|
||||
if (parameter.__isUniform)
|
||||
{
|
||||
|
||||
for (parameter in __paramInt) {
|
||||
if (parameter.__isUniform) {
|
||||
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
} else {
|
||||
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
|
||||
}
|
||||
}
|
||||
@@ -178,8 +341,12 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
}
|
||||
}
|
||||
|
||||
var __cancelNextProcessGLData:Bool = false;
|
||||
@:noCompletion private override function __processGLData(source:String, storageType:String):Void
|
||||
{
|
||||
onProcessGLData.dispatch(source, storageType);
|
||||
if (__cancelNextProcessGLData != (__cancelNextProcessGLData = false))
|
||||
return;
|
||||
var lastMatch = 0, position, regex, name, type;
|
||||
|
||||
if (storageType == "uniform")
|
||||
@@ -202,160 +369,146 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
}
|
||||
|
||||
var isUniform = (storageType == "uniform");
|
||||
|
||||
if (StringTools.startsWith(type, "sampler"))
|
||||
{
|
||||
var input = new ShaderInput<BitmapData>();
|
||||
input.name = name;
|
||||
@:privateAccess
|
||||
input.__isUniform = isUniform;
|
||||
__inputBitmapData.push(input);
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case "openfl_Texture":
|
||||
__texture = input;
|
||||
case "bitmap":
|
||||
__bitmap = input;
|
||||
default:
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, input);
|
||||
try{Reflect.setField(this, name, input);} catch(e) {}
|
||||
}
|
||||
else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null)
|
||||
{
|
||||
var parameterType:ShaderParameterType = switch (type)
|
||||
{
|
||||
case "bool": BOOL;
|
||||
case "double", "float": FLOAT;
|
||||
case "int", "uint": INT;
|
||||
case "bvec2": BOOL2;
|
||||
case "bvec3": BOOL3;
|
||||
case "bvec4": BOOL4;
|
||||
case "ivec2", "uvec2": INT2;
|
||||
case "ivec3", "uvec3": INT3;
|
||||
case "ivec4", "uvec4": INT4;
|
||||
case "vec2", "dvec2": FLOAT2;
|
||||
case "vec3", "dvec3": FLOAT3;
|
||||
case "vec4", "dvec4": FLOAT4;
|
||||
case "mat2", "mat2x2": MATRIX2X2;
|
||||
case "mat2x3": MATRIX2X3;
|
||||
case "mat2x4": MATRIX2X4;
|
||||
case "mat3x2": MATRIX3X2;
|
||||
case "mat3", "mat3x3": MATRIX3X3;
|
||||
case "mat3x4": MATRIX3X4;
|
||||
case "mat4x2": MATRIX4X2;
|
||||
case "mat4x3": MATRIX4X3;
|
||||
case "mat4", "mat4x4": MATRIX4X4;
|
||||
default: null;
|
||||
}
|
||||
|
||||
var length = switch (parameterType)
|
||||
{
|
||||
case BOOL2, INT2, FLOAT2: 2;
|
||||
case BOOL3, INT3, FLOAT3: 3;
|
||||
case BOOL4, INT4, FLOAT4, MATRIX2X2: 4;
|
||||
case MATRIX3X3: 9;
|
||||
case MATRIX4X4: 16;
|
||||
default: 1;
|
||||
}
|
||||
|
||||
var arrayLength = switch (parameterType)
|
||||
{
|
||||
case MATRIX2X2: 2;
|
||||
case MATRIX3X3: 3;
|
||||
case MATRIX4X4: 4;
|
||||
default: 1;
|
||||
}
|
||||
|
||||
switch (parameterType)
|
||||
{
|
||||
case BOOL, BOOL2, BOOL3, BOOL4:
|
||||
var parameter = new ShaderParameter<Bool>();
|
||||
parameter.name = name;
|
||||
@:privateAccess
|
||||
parameter.type = parameterType;
|
||||
@:privateAccess
|
||||
parameter.__arrayLength = arrayLength;
|
||||
@:privateAccess
|
||||
parameter.__isBool = true;
|
||||
@:privateAccess
|
||||
parameter.__isUniform = isUniform;
|
||||
@:privateAccess
|
||||
parameter.__length = length;
|
||||
__paramBool.push(parameter);
|
||||
|
||||
if (name == "openfl_HasColorTransform")
|
||||
{
|
||||
__hasColorTransform = parameter;
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
|
||||
case INT, INT2, INT3, INT4:
|
||||
var parameter = new ShaderParameter<Int>();
|
||||
parameter.name = name;
|
||||
@:privateAccess
|
||||
parameter.type = parameterType;
|
||||
@:privateAccess
|
||||
parameter.__arrayLength = arrayLength;
|
||||
@:privateAccess
|
||||
parameter.__isInt = true;
|
||||
@:privateAccess
|
||||
parameter.__isUniform = isUniform;
|
||||
@:privateAccess
|
||||
parameter.__length = length;
|
||||
@:privateAccess
|
||||
__paramInt.push(parameter);
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
|
||||
default:
|
||||
var parameter = new ShaderParameter<Float>();
|
||||
parameter.name = name;
|
||||
@:privateAccess
|
||||
parameter.type = parameterType;
|
||||
@:privateAccess
|
||||
parameter.__arrayLength = arrayLength;
|
||||
#if lime
|
||||
@:privateAccess
|
||||
if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength);
|
||||
#end
|
||||
@:privateAccess
|
||||
parameter.__isFloat = true;
|
||||
@:privateAccess
|
||||
parameter.__isUniform = isUniform;
|
||||
@:privateAccess
|
||||
parameter.__length = length;
|
||||
__paramFloat.push(parameter);
|
||||
|
||||
if (StringTools.startsWith(name, "openfl_"))
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "openfl_Alpha": __alpha = parameter;
|
||||
case "openfl_ColorMultiplier": __colorMultiplier = parameter;
|
||||
case "openfl_ColorOffset": __colorOffset = parameter;
|
||||
case "openfl_Matrix": __matrix = parameter;
|
||||
case "openfl_Position": __position = parameter;
|
||||
case "openfl_TextureCoord": __textureCoord = parameter;
|
||||
case "openfl_TextureSize": __textureSize = parameter;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
}
|
||||
}
|
||||
registerParameter(name, type, isUniform);
|
||||
|
||||
position = regex.matchedPos();
|
||||
lastMatch = position.pos + position.len;
|
||||
}
|
||||
}
|
||||
|
||||
function registerParameter(name:String, type:String, isUniform:Bool):Void
|
||||
{
|
||||
if (StringTools.startsWith(type, "sampler"))
|
||||
{
|
||||
var input = new ShaderInput<BitmapData>();
|
||||
input.name = name;
|
||||
input.__isUniform = isUniform;
|
||||
__inputBitmapData.push(input);
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case "openfl_Texture":
|
||||
__texture = input;
|
||||
case "bitmap":
|
||||
__bitmap = input;
|
||||
default:
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, input);
|
||||
try{Reflect.setField(this, name, input);} catch(e) {}
|
||||
}
|
||||
else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null)
|
||||
{
|
||||
var parameterType:ShaderParameterType = switch (type)
|
||||
{
|
||||
case "bool": BOOL;
|
||||
case "double", "float": FLOAT;
|
||||
case "int", "uint": INT;
|
||||
case "bvec2": BOOL2;
|
||||
case "bvec3": BOOL3;
|
||||
case "bvec4": BOOL4;
|
||||
case "ivec2", "uvec2": INT2;
|
||||
case "ivec3", "uvec3": INT3;
|
||||
case "ivec4", "uvec4": INT4;
|
||||
case "vec2", "dvec2": FLOAT2;
|
||||
case "vec3", "dvec3": FLOAT3;
|
||||
case "vec4", "dvec4": FLOAT4;
|
||||
case "mat2", "mat2x2": MATRIX2X2;
|
||||
case "mat2x3": MATRIX2X3;
|
||||
case "mat2x4": MATRIX2X4;
|
||||
case "mat3x2": MATRIX3X2;
|
||||
case "mat3", "mat3x3": MATRIX3X3;
|
||||
case "mat3x4": MATRIX3X4;
|
||||
case "mat4x2": MATRIX4X2;
|
||||
case "mat4x3": MATRIX4X3;
|
||||
case "mat4", "mat4x4": MATRIX4X4;
|
||||
default: null;
|
||||
}
|
||||
|
||||
var length = switch (parameterType)
|
||||
{
|
||||
case BOOL2, INT2, FLOAT2: 2;
|
||||
case BOOL3, INT3, FLOAT3: 3;
|
||||
case BOOL4, INT4, FLOAT4, MATRIX2X2: 4;
|
||||
case MATRIX3X3: 9;
|
||||
case MATRIX4X4: 16;
|
||||
default: 1;
|
||||
}
|
||||
|
||||
var arrayLength = switch (parameterType)
|
||||
{
|
||||
case MATRIX2X2: 2;
|
||||
case MATRIX3X3: 3;
|
||||
case MATRIX4X4: 4;
|
||||
default: 1;
|
||||
}
|
||||
|
||||
switch (parameterType)
|
||||
{
|
||||
case BOOL, BOOL2, BOOL3, BOOL4:
|
||||
var parameter = new ShaderParameter<Bool>();
|
||||
parameter.name = name;
|
||||
parameter.type = parameterType;
|
||||
parameter.__arrayLength = arrayLength;
|
||||
parameter.__isBool = true;
|
||||
parameter.__isUniform = isUniform;
|
||||
parameter.__length = length;
|
||||
__paramBool.push(parameter);
|
||||
|
||||
if (name == "openfl_HasColorTransform")
|
||||
{
|
||||
__hasColorTransform = parameter;
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
|
||||
case INT, INT2, INT3, INT4:
|
||||
var parameter = new ShaderParameter<Int>();
|
||||
parameter.name = name;
|
||||
parameter.type = parameterType;
|
||||
parameter.__arrayLength = arrayLength;
|
||||
parameter.__isInt = true;
|
||||
parameter.__isUniform = isUniform;
|
||||
parameter.__length = length;
|
||||
__paramInt.push(parameter);
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
|
||||
default:
|
||||
var parameter = new ShaderParameter<Float>();
|
||||
parameter.name = name;
|
||||
parameter.type = parameterType;
|
||||
parameter.__arrayLength = arrayLength;
|
||||
#if lime
|
||||
if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength);
|
||||
#end
|
||||
parameter.__isFloat = true;
|
||||
parameter.__isUniform = isUniform;
|
||||
parameter.__length = length;
|
||||
__paramFloat.push(parameter);
|
||||
|
||||
if (StringTools.startsWith(name, "openfl_"))
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "openfl_Alpha": __alpha = parameter;
|
||||
case "openfl_ColorMultiplier": __colorMultiplier = parameter;
|
||||
case "openfl_ColorOffset": __colorOffset = parameter;
|
||||
case "openfl_Matrix": __matrix = parameter;
|
||||
case "openfl_Position": __position = parameter;
|
||||
case "openfl_TextureCoord": __textureCoord = parameter;
|
||||
case "openfl_TextureSize": __textureSize = parameter;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
if (__instanceFields.contains(name) || __instanceFields.contains('get_${name}'))
|
||||
return Reflect.getProperty(this, name);
|
||||
@@ -394,7 +547,6 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
var cl:String = Type.getClassName(Type.getClass(field));
|
||||
// cant do "field is ShaderInput" for some reason
|
||||
if (cl.startsWith("openfl.display.ShaderParameter")) {
|
||||
@:privateAccess
|
||||
if (field.__length <= 1) {
|
||||
if ((val is Array))
|
||||
return field.value = val;
|
||||
@@ -546,4 +698,16 @@ class ShaderTypeException extends Exception {
|
||||
this.name = name;
|
||||
super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.');
|
||||
}
|
||||
}
|
||||
|
||||
class ShaderErrorPosition {
|
||||
public var column:Int;
|
||||
public var line:Int;
|
||||
public var message:String;
|
||||
|
||||
public function new(line:String, column:String, message:String) {
|
||||
this.line = Std.parseInt(line);
|
||||
this.column = Std.parseInt(column);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package funkin.backend.shaders;
|
||||
|
||||
import hscript.IHScriptCustomBehaviour;
|
||||
import funkin.backend.scripting.events.shader.ShaderProcessEvent;
|
||||
import funkin.backend.scripting.ScriptPack;
|
||||
import funkin.backend.scripting.Script;
|
||||
|
||||
@:structInit
|
||||
class FieldInfo {
|
||||
public var get:Void->Dynamic;
|
||||
public var set:Dynamic->Dynamic;
|
||||
}
|
||||
|
||||
@:access(funkin.backend.shaders.FunkinShader)
|
||||
class ScriptableShader extends FlxBasic implements IHScriptCustomBehaviour {
|
||||
private static var __instanceFields = Type.getInstanceFields(ScriptableShader);
|
||||
|
||||
public var shader:FunkinShader;
|
||||
public var script:Script;
|
||||
|
||||
var fields:Map<String, FieldInfo> = [];
|
||||
|
||||
public function new(shader:FunkinShader, ?scriptName:String, ?parentScriptPack:ScriptPack) {
|
||||
super();
|
||||
|
||||
if(parentScriptPack == null && PlayState.instance != null)
|
||||
parentScriptPack = PlayState.instance.scripts;
|
||||
|
||||
if(scriptName == null)
|
||||
if((shader is CustomShader)) scriptName = cast(shader, CustomShader).fileName;
|
||||
else throw "Missing name for shader script, please provide a scriptName, or use CustomShader";
|
||||
|
||||
script = Script.create(Paths.script('shaders/$scriptName'));
|
||||
script.setParent(shader);
|
||||
script.set("shader", shader);
|
||||
script.set("registerParameter", shader.registerParameter);
|
||||
script.set("registerField", (name:String, get: Void->Dynamic, set: Dynamic->Dynamic) -> {
|
||||
registerField(name, get, set);
|
||||
});
|
||||
script.set("registerShaderField", (name:String, shaderField:String) -> {
|
||||
var get = () -> shader.hget(shaderField);
|
||||
var set = (value:Dynamic) -> {
|
||||
var val = shader.hset(shaderField, value);
|
||||
script.set(name, val);
|
||||
return val;
|
||||
};
|
||||
registerField(name, get, set);
|
||||
var gg = script.get(name);
|
||||
if(gg != null) {
|
||||
shader.hset(shaderField, gg);
|
||||
}
|
||||
});
|
||||
if(parentScriptPack != null)
|
||||
script.setPublicMap(parentScriptPack.publicVariables);
|
||||
script.load();
|
||||
script.call("create");
|
||||
|
||||
shader.onGLUpdate.add(updateGL);
|
||||
shader.onProcessGLData.add(processGLData);
|
||||
}
|
||||
|
||||
function registerField(name:String, get: Void->Dynamic, set: Dynamic->Dynamic) {
|
||||
fields.set(name, {
|
||||
get: get,
|
||||
set: set
|
||||
});
|
||||
script.set("get_"+name, get);
|
||||
script.set("set_"+name, set);
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) script.call("update", [elapsed]);
|
||||
public override function draw() script.call("draw", []);
|
||||
public function updateGL() script.call("updateGL", []);
|
||||
public function processGLData(source:String, storageType:String) {
|
||||
var event = EventManager.get(ShaderProcessEvent).recycle(source, storageType);
|
||||
script.call("processGLData", [event]);
|
||||
if(event.cancelled)
|
||||
shader.__cancelNextProcessGLData = true;
|
||||
script.call("processGLDataPost", [event]);
|
||||
}
|
||||
|
||||
public override function destroy() {
|
||||
shader.onGLUpdate.remove(updateGL);
|
||||
shader.onProcessGLData.remove(processGLData);
|
||||
if(script != null) {
|
||||
script.call("destroy");
|
||||
script.destroy();
|
||||
}
|
||||
fields = null;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
if(fields.exists(name))
|
||||
return fields.get(name).get();
|
||||
if (__instanceFields.contains(name) || __instanceFields.contains('get_${name}'))
|
||||
return Reflect.getProperty(this, name);
|
||||
return shader.hget(name);
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if (fields.exists(name)) {
|
||||
return fields.get(name).set(val);
|
||||
}
|
||||
if (__instanceFields.contains(name) || __instanceFields.contains('set_${name}')) {
|
||||
Reflect.setProperty(this, name, val);
|
||||
return val;
|
||||
}
|
||||
return shader.hset(name, val);
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ class Conductor
|
||||
/**
|
||||
* Number of steps per beat (bottom number in time signature). Defaults to 4.
|
||||
*/
|
||||
public static var stepsPerBeat:Float = 4;
|
||||
public static var stepsPerBeat:Int = 4;
|
||||
|
||||
|
||||
/**
|
||||
@@ -262,7 +262,7 @@ class Conductor
|
||||
}
|
||||
}
|
||||
|
||||
public static function changeBPM(newBpm:Float, beatsPerMeasure:Float = 4, stepsPerBeat:Float = 4)
|
||||
public static function changeBPM(newBpm:Float, beatsPerMeasure:Float = 4, stepsPerBeat:Int = 4)
|
||||
{
|
||||
bpm = newBpm;
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ package funkin.backend.system;
|
||||
|
||||
import lime.utils.AssetLibrary;
|
||||
import lime.utils.AssetType;
|
||||
import flixel.util.FlxColor;
|
||||
|
||||
import lime.app.Application;
|
||||
import funkin.backend.system.macros.GitCommitMacro;
|
||||
import funkin.backend.assets.IModsAssetLibrary;
|
||||
import funkin.backend.assets.ScriptedAssetLibrary;
|
||||
|
||||
@@ -11,13 +14,129 @@ import funkin.backend.assets.ScriptedAssetLibrary;
|
||||
*/
|
||||
@:build(funkin.backend.system.macros.FlagMacro.build())
|
||||
class Flags {
|
||||
// -- Codename's Default Flags --
|
||||
public static var COMMIT_NUMBER:Int = GitCommitMacro.commitNumber;
|
||||
public static var COMMIT_HASH:String = GitCommitMacro.commitHash;
|
||||
public static var COMMIT_MESSAGE:String = 'Commit $COMMIT_NUMBER ($COMMIT_HASH)';
|
||||
@:lazy public static var VERSION_MESSAGE:String = 'Codename Engine v${Application.current.meta.get('version')}';
|
||||
|
||||
public static var REPO_NAME:String = "CodenameEngine";
|
||||
public static var REPO_OWNER:String = "FNF-CNE-Devs";
|
||||
public static var REPO_URL:String = 'https://github.com/$REPO_OWNER/$REPO_NAME';
|
||||
|
||||
// make this empty once you guys are done with the project.
|
||||
// good luck /gen <3 @crowplexus
|
||||
public static var RELEASE_CYCLE:String = "Beta";
|
||||
|
||||
/**
|
||||
* Preferred sound extension for the game's audio files.
|
||||
* Currently is set to `mp3` for web targets, and `ogg` for other targets.
|
||||
*/
|
||||
public static var SOUND_EXT:String = #if web "mp3" #else "ogg" #end; // we also support wav
|
||||
public static var VIDEO_EXT:String = "mp4";
|
||||
public static var IMAGE_EXT:String = "png"; // we also support jpg
|
||||
|
||||
public static var DEFAULT_DISCORD_LOGO_KEY:String = "icon";
|
||||
public static var DEFAULT_DISCORD_CLIENT_ID:String = "1027994136193810442";
|
||||
public static var DEFAULT_DISCORD_LOGO_TEXT:String = "Codename Engine";
|
||||
|
||||
@:also(funkin.game.Character.FALLBACK_CHARACTER)
|
||||
public static var DEFAULT_CHARACTER:String = "bf";
|
||||
public static var DEFAULT_GIRLFRIEND:String = "gf";
|
||||
public static var DEFAULT_OPPONENT:String = "dad";
|
||||
|
||||
@:also(funkin.game.PlayState.difficulty)
|
||||
public static var DEFAULT_DIFFICULTY:String = "normal";
|
||||
public static var DEFAULT_STAGE:String = "stage";
|
||||
public static var DEFAULT_SCROLL_SPEED:Float = 2.0;
|
||||
public static var DEFAULT_HEALTH_ICON:String = "face";
|
||||
|
||||
public static var SONGS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override";
|
||||
public static var WEEKS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override";
|
||||
|
||||
public static var CHARTER_ADVANCED_SETTINGS:Bool = false;
|
||||
public static var DEFAULT_BPM:Float = 100.0;
|
||||
public static var DEFAULT_BEATS_PER_MEASURE:Float = 4;
|
||||
public static var DEFAULT_STEPS_PER_BEAT:Int = 4;
|
||||
|
||||
// Internal stuff
|
||||
public static var SUPPORTED_CHART_RUNTIME_FORMATS:Array<String> = ["Legacy", "Psych Engine"];
|
||||
public static var SUPPORTED_CHART_FORMATS:Array<String> = ["BaseGame"];
|
||||
|
||||
public static var BASEGAME_SONG_METADATA_VERSION:String = "2.2.2";
|
||||
public static var BASEGAME_SONG_CHART_DATA_VERSION:String = "2.0.0";
|
||||
public static var BASEGAME_DEFAULT_NOTE_STYLE:String = 'funkin';
|
||||
public static var BASEGAME_DEFAULT_ALBUM_ID:String = 'volume1';
|
||||
public static var BASEGAME_DEFAULT_PREVIEW_START:Float = 0;
|
||||
public static var BASEGAME_DEFAULT_PREVIEW_END:Float = 15000;
|
||||
|
||||
/**
|
||||
* Default background colors for songs without bg color
|
||||
*/
|
||||
public static var DEFAULT_COLOR:FlxColor = 0xFF9271FD;
|
||||
public static var DEFAULT_COOP_ALLOWED:Bool = false;
|
||||
public static var DEFAULT_OPPONENT_MODE_ALLOWED:Bool = false;
|
||||
|
||||
@:also(funkin.game.PlayState.coopMode)
|
||||
public static var DEFAULT_COOP_MODE:Bool = false; // used in playstate if it doesnt find it
|
||||
@:also(funkin.game.PlayState.opponentMode)
|
||||
public static var DEFAULT_OPPONENT_MODE:Bool = false;
|
||||
|
||||
public static var DEFAULT_NOTE_MS_LIMIT:Float = 1500;
|
||||
public static var DEFAULT_NOTE_SCALE:Float = 0.7;
|
||||
|
||||
@:also(funkin.game.Character.FALLBACK_DEAD_CHARACTER)
|
||||
public static var DEFAULT_GAMEOVER_CHARACTER:String = "bf-dead";
|
||||
public static var DEFAULT_GAMEOVER_SONG:String = "gameOver";
|
||||
public static var DEFAULT_GAMEOVER_LOSS_SFX:String = "gameOverSFX";
|
||||
public static var DEFAULT_GAMEOVER_RETRY_SFX:String = "gameOverEnd";
|
||||
|
||||
public static var DEFAULT_CAM_ZOOM_INTERVAL:Int = 4;
|
||||
public static var DEFAULT_CAM_ZOOM_STRENGTH:Int = 1;
|
||||
public static var DEFAULT_CAM_ZOOM:Float = 1.05; // what zoom level it defaults to
|
||||
public static var DEFAULT_HUD_ZOOM:Float = 1.0;
|
||||
public static var MAX_CAMERA_ZOOM:Float = 1.35;
|
||||
|
||||
public static var DEFAULT_PAUSE_ITEMS:Array<String> = ['Resume', 'Restart Song', 'Change Controls', 'Change Options', 'Exit to menu', "Exit to charter"];
|
||||
public static var DEFAULT_CUTSCENE_PAUSE_ITEMS:Array<String> = ['Resume Cutscene', 'Skip Cutscene', 'Restart Cutscene', 'Exit to menu'];
|
||||
public static var DEFAULT_GITAROO:Bool = true;
|
||||
public static var GITAROO_CHANCE:Float = 0.1;
|
||||
public static var DEFAULT_CAN_ACCESS_DEBUG_MENUS:Bool = true;
|
||||
public static var DEFAULT_MUTE_VOCALS_ON_MISS:Bool = true;
|
||||
|
||||
public static var DEFAULT_MAX_HEALTH:Float = 2.0;
|
||||
public static var DEFAULT_HEALTH:Null<Float> = null;//DEFAULT_MAX_HEALTH / 2.0;
|
||||
public static var BOP_ICON_SCALE:Float = 1.2;
|
||||
public static var ICON_OFFSET:Float = 26;
|
||||
public static var ICON_LERP:Float = 0.33;
|
||||
|
||||
public static var CAM_BOP_STRENGTH:Float = 0.015;
|
||||
public static var HUD_BOP_STRENGTH:Float = 0.03;
|
||||
public static var DEFAULT_CAM_ZOOM_LERP:Float = 0.05;
|
||||
public static var DEFAULT_HUD_ZOOM_LERP:Float = 0.05;
|
||||
|
||||
public static var MAX_SPLASHES:Int = 8;
|
||||
public static var STUNNED_TIME:Float = 5 / 60;
|
||||
|
||||
@:also(funkin.game.PlayState.daPixelZoom)
|
||||
public static var PIXEL_ART_SCALE:Float = 6.0;
|
||||
|
||||
public static var DEFAULT_COMBO_GROUP_MAX_SIZE:Int = 25;
|
||||
|
||||
public static var DEFAULT_STRUM_AMOUNT:Int = 4;
|
||||
public static var DEFAULT_CAMERA_FOLLOW_SPEED:Float = 0.04;
|
||||
|
||||
public static var VOCAL_OFFSET_VIOLATION_THRESHOLD:Float = 25;
|
||||
|
||||
// Usage: Codename Credits
|
||||
public static var MAIN_DEVS_COLOR:FlxColor = 0xFF9C35D5;
|
||||
public static var MIN_CONTRIBUTIONS_COLOR:FlxColor = 0xFFB4A7DA;
|
||||
|
||||
public static var UNDO_PREFIX:String = "* ";
|
||||
public static var JSON_PRETTY_PRINT:String = "\t";
|
||||
// -- End of Codename's Default Flags --
|
||||
|
||||
/**
|
||||
* Flags that Codename couldn't recognize as it's own defaults (they can only be `string`!).
|
||||
*/
|
||||
@:bypass public static var customFlags:Map<String, String> = [];
|
||||
|
||||
public static function load() {
|
||||
|
||||
@@ -11,8 +11,7 @@ final class Logs {
|
||||
|
||||
public static var nativeTrace = Log.trace;
|
||||
public static function init() {
|
||||
|
||||
Log.trace = function(v : Dynamic, ?infos : Null<haxe.PosInfos>) {
|
||||
Log.trace = function(v:Dynamic, ?infos:Null<haxe.PosInfos>) {
|
||||
var data = [
|
||||
logText('${infos.fileName}:${infos.lineNumber}: ', CYAN),
|
||||
logText(Std.string(v))
|
||||
@@ -28,22 +27,48 @@ final class Logs {
|
||||
__showInConsole(prepareColoredTrace(data, TRACE));
|
||||
};
|
||||
|
||||
LogFrontEnd.onLogs = function(Data, Style, FireOnce) {
|
||||
LogFrontEnd.onLogs = function(data, style, fireOnce) {
|
||||
var prefix = "[FLIXEL]";
|
||||
var color:ConsoleColor = LIGHTGRAY;
|
||||
var level:Level = INFO;
|
||||
if (Style == LogStyle.CONSOLE) {prefix = "> "; color = WHITE; level = INFO; } else
|
||||
if (Style == LogStyle.ERROR) {prefix = "[FLIXEL]"; color = RED; level = ERROR; } else
|
||||
if (Style == LogStyle.NORMAL) {prefix = "[FLIXEL]"; color = WHITE; level = INFO; } else
|
||||
if (Style == LogStyle.NOTICE) {prefix = "[FLIXEL]"; color = GREEN; level = VERBOSE;} else
|
||||
if (Style == LogStyle.WARNING) {prefix = "[FLIXEL]"; color = YELLOW; level = WARNING;}
|
||||
if (style == LogStyle.CONSOLE) // cant place a switch here as these arent inline values - Nex
|
||||
{
|
||||
prefix = "> ";
|
||||
color = WHITE;
|
||||
level = INFO;
|
||||
}
|
||||
else if (style == LogStyle.ERROR)
|
||||
{
|
||||
prefix = "[FLIXEL]";
|
||||
color = RED;
|
||||
level = ERROR;
|
||||
}
|
||||
else if (style == LogStyle.NORMAL)
|
||||
{
|
||||
prefix = "[FLIXEL]";
|
||||
color = WHITE;
|
||||
level = INFO;
|
||||
}
|
||||
else if (style == LogStyle.NOTICE)
|
||||
{
|
||||
prefix = "[FLIXEL]";
|
||||
color = GREEN;
|
||||
level = VERBOSE;
|
||||
}
|
||||
else if (style == LogStyle.WARNING)
|
||||
{
|
||||
prefix = "[FLIXEL]";
|
||||
color = YELLOW;
|
||||
level = WARNING;
|
||||
}
|
||||
|
||||
var d:Dynamic = Data;
|
||||
var d:Dynamic = data;
|
||||
if (!(d is Array))
|
||||
d = [d];
|
||||
var a:Array<Dynamic> = d;
|
||||
var strs = [for(e in a) Std.string(e)];
|
||||
for(e in strs) {
|
||||
for(e in strs)
|
||||
{
|
||||
Logs.trace('$prefix $e', level, color);
|
||||
}
|
||||
};
|
||||
@@ -55,12 +80,18 @@ final class Logs {
|
||||
logText('[ '),
|
||||
logText('${Std.string(time.getHours()).addZeros(2)}:${Std.string(time.getMinutes()).addZeros(2)}:${Std.string(time.getSeconds()).addZeros(2)}', DARKMAGENTA),
|
||||
logText(' |'),
|
||||
switch(level) {
|
||||
case WARNING: logText(' WARNING ', DARKYELLOW);
|
||||
case ERROR: logText(' ERROR ', DARKRED);
|
||||
case TRACE: logText(' TRACE ', GRAY);
|
||||
case VERBOSE: logText(' VERBOSE ', DARKMAGENTA);
|
||||
default: logText(' INFORMATION ', CYAN);
|
||||
switch (level)
|
||||
{
|
||||
case WARNING:
|
||||
logText(' WARNING ', DARKYELLOW);
|
||||
case ERROR:
|
||||
logText(' ERROR ', DARKRED);
|
||||
case TRACE:
|
||||
logText(' TRACE ', GRAY);
|
||||
case VERBOSE:
|
||||
logText(' VERBOSE ', DARKMAGENTA);
|
||||
default:
|
||||
logText(' INFORMATION ', CYAN);
|
||||
},
|
||||
logText('] ')
|
||||
];
|
||||
@@ -77,7 +108,7 @@ final class Logs {
|
||||
}
|
||||
|
||||
public static function __showInConsole(text:Array<LogText>) {
|
||||
#if sys
|
||||
#if (sys && !mobile)
|
||||
while(__showing) {
|
||||
Sys.sleep(0.05);
|
||||
}
|
||||
@@ -89,20 +120,30 @@ final class Logs {
|
||||
NativeAPI.setConsoleColors();
|
||||
Sys.print("\r\n");
|
||||
__showing = false;
|
||||
#elseif mobile
|
||||
while(__showing) {
|
||||
Sys.sleep(0.05);
|
||||
}
|
||||
__showing = true;
|
||||
@:privateAccess
|
||||
Sys.print([for(t in text) t.text].join(""));
|
||||
__showing = false;
|
||||
#else
|
||||
@:privateAccess
|
||||
nativeTrace([for(t in text) t.text].join(""));
|
||||
#end
|
||||
}
|
||||
|
||||
public static function traceColored(text:Array<LogText>, level:Level = INFO) {
|
||||
public static function traceColored(text:Array<LogText>, level:Level = INFO)
|
||||
__showInConsole(prepareColoredTrace(text, level));
|
||||
}
|
||||
|
||||
public static function trace(text:String, level:Level = INFO, color:ConsoleColor = LIGHTGRAY) {
|
||||
traceColored([{
|
||||
text: text,
|
||||
color: color
|
||||
}], level);
|
||||
traceColored([
|
||||
{
|
||||
text: text,
|
||||
color: color
|
||||
}
|
||||
], level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +154,8 @@ enum abstract Level(Int) {
|
||||
var TRACE = 3;
|
||||
var VERBOSE = 4;
|
||||
}
|
||||
|
||||
typedef LogText = {
|
||||
var text:String;
|
||||
var color:ConsoleColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +30,6 @@ import android.os.Build;
|
||||
|
||||
class Main extends Sprite
|
||||
{
|
||||
// make this empty once you guys are done with the project.
|
||||
// good luck /gen <3 @crowplexus
|
||||
public static final releaseCycle:String = "Beta";
|
||||
|
||||
public static var instance:Main;
|
||||
|
||||
public static var modToLoad:String = null;
|
||||
@@ -123,6 +119,9 @@ class Main extends Sprite
|
||||
ShaderResizeFix.init();
|
||||
Logs.init();
|
||||
Paths.init();
|
||||
|
||||
hscript.Interp.importRedirects = funkin.backend.scripting.Script.getDefaultImportRedirects();
|
||||
|
||||
#if GLOBAL_SCRIPT
|
||||
funkin.backend.scripting.GlobalScript.init();
|
||||
#end
|
||||
@@ -145,7 +144,7 @@ class Main extends Sprite
|
||||
Assets.registerLibrary('default', lib);
|
||||
|
||||
funkin.options.PlayerSettings.init();
|
||||
funkin.savedata.FunkinSave.init();
|
||||
funkin.backend.utils.FunkinSave.init();
|
||||
Options.load();
|
||||
|
||||
FlxG.fixedTimestep = false;
|
||||
@@ -171,13 +170,22 @@ class Main extends Sprite
|
||||
initTransition();
|
||||
}
|
||||
|
||||
public static function refreshAssets() {
|
||||
FlxSoundTray.volumeChangeSFX = Paths.sound('menu/volume');
|
||||
FlxSoundTray.volumeUpChangeSFX = null;
|
||||
FlxSoundTray.volumeDownChangeSFX = null;
|
||||
public static function refreshAssets() @:privateAccess {
|
||||
WindowUtils.resetTitle();
|
||||
|
||||
if (FlxG.game.soundTray != null)
|
||||
FlxG.game.soundTray.text.setTextFormat(new TextFormat(Paths.font("vcr.ttf")));
|
||||
var game = FlxG.game;
|
||||
var daSndTray = Type.createInstance(game._customSoundTray = funkin.menus.ui.FunkinSoundTray, []);
|
||||
var index:Int = game.numChildren - 1;
|
||||
|
||||
if(game.soundTray != null)
|
||||
{
|
||||
var newIndex:Int = game.getChildIndex(game.soundTray);
|
||||
if(newIndex != -1) index = newIndex;
|
||||
game.removeChild(game.soundTray);
|
||||
game.soundTray.__cleanup();
|
||||
}
|
||||
|
||||
game.addChildAt(game.soundTray = daSndTray, index);
|
||||
}
|
||||
|
||||
public static function initTransition() {
|
||||
|
||||
@@ -18,8 +18,10 @@ class MainState extends FlxState {
|
||||
public static var betaWarningShown:Bool = false;
|
||||
public override function create() {
|
||||
super.create();
|
||||
if (!initiated)
|
||||
if (!initiated) {
|
||||
Main.loadGameSettings();
|
||||
}
|
||||
|
||||
initiated = true;
|
||||
|
||||
#if sys
|
||||
@@ -55,9 +57,9 @@ class MainState extends FlxState {
|
||||
}
|
||||
#end
|
||||
|
||||
Main.refreshAssets();
|
||||
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder);
|
||||
Flags.load();
|
||||
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder); // Loads global.hx
|
||||
Main.refreshAssets();
|
||||
DiscordUtil.init();
|
||||
EventsData.reloadEvents();
|
||||
ControlsUtil.loadCustomControls();
|
||||
@@ -73,6 +75,8 @@ class MainState extends FlxState {
|
||||
betaWarningShown = true;
|
||||
}
|
||||
|
||||
#if sys
|
||||
CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN);
|
||||
#end
|
||||
}
|
||||
}
|
||||
@@ -13,5 +13,5 @@ class CodenameBuildField extends TextField {
|
||||
}
|
||||
|
||||
public function reload()
|
||||
text = 'Codename Engine ${Main.releaseCycle}\nCommit ${GitCommitMacro.commitNumber} (${GitCommitMacro.commitHash})';
|
||||
text = 'Codename Engine ${Flags.RELEASE_CYCLE}\n${Flags.COMMIT_MESSAGE}';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package funkin.backend.system.framerate;
|
||||
|
||||
import funkin.backend.scripting.ModState;
|
||||
|
||||
class FlixelInfo extends FramerateCategory {
|
||||
public function new() {
|
||||
super("Flixel Info");
|
||||
@@ -14,7 +16,12 @@ class FlixelInfo extends FramerateCategory {
|
||||
for(_ in FlxG.bitmap._cache.keys())
|
||||
c++;
|
||||
|
||||
_text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}';
|
||||
if((FlxG.state is ModState)) {
|
||||
var state:ModState = cast FlxG.state;
|
||||
_text = "Mod State: " + state.scriptName;
|
||||
} else {
|
||||
_text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}';
|
||||
}
|
||||
_text += '\nObject Count: ${FlxG.state.members.length}';
|
||||
_text += '\nCamera Count: ${FlxG.cameras.list.length}';
|
||||
_text += '\nBitmaps Count: ${c}';
|
||||
@@ -22,9 +29,9 @@ class FlixelInfo extends FramerateCategory {
|
||||
_text += '\nFlxG.game Childs Count: ${FlxG.game.numChildren}';
|
||||
// _text += '\nCached objects count: ${cachedObjects}';
|
||||
#if FLX_POINT_POOL
|
||||
var points = flixel.math.FlxPoint.FlxBasePoint.pool;
|
||||
//var points = flixel.math.FlxPoint.FlxBasePoint.pool;
|
||||
//_text += '\nPoint Count: ${points._count} | +${points.made} | -${points.gotten} | ${points.balance} | >${points.putted}';
|
||||
_text += '\nPoint Count: ${points._count}';
|
||||
//_text += '\nPoint Count: ${points._count}';
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import haxe.Exception;
|
||||
#if GITHUB_API
|
||||
import haxe.Json;
|
||||
#end
|
||||
import haxe.Exception;
|
||||
|
||||
// TODO: Document further and perhaps make this a Haxelib.
|
||||
class GitHub {
|
||||
@@ -82,7 +83,7 @@ class GitHub {
|
||||
* @param onError Error Callback
|
||||
* @return Members List
|
||||
*/
|
||||
public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array<GitHubContributor> {
|
||||
public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array<GitHubContributor> {
|
||||
#if GITHUB_API
|
||||
try {
|
||||
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org/members'));
|
||||
@@ -105,7 +106,7 @@ class GitHub {
|
||||
* @param onError Error Callback
|
||||
* @return User/Organization
|
||||
*/
|
||||
public static function getUser(user:String, ?onError:Exception->Void):GitHubUser {
|
||||
public static function getUser(user:String, ?onError:Exception->Void):GitHubUser {
|
||||
#if GITHUB_API
|
||||
try {
|
||||
var url = 'https://api.github.com/users/$user';
|
||||
|
||||
@@ -6,13 +6,21 @@ using StringTools;
|
||||
class BuildInfo {
|
||||
public static function printBuildInfo() {
|
||||
if(haxe.macro.Context.defined("display")) return;
|
||||
var haxeVersion = haxe.macro.Context.definedValue("haxe");
|
||||
Sys.println('--- BUILD INFO ---');
|
||||
var haxeVersion = haxe.macro.Context.definedValue("haxe").trim();
|
||||
Sys.println('[ BUILD INFO ]');
|
||||
Sys.println('Haxe Version: ${haxeVersion}');
|
||||
try {
|
||||
var lastBuiltWith:Null<String> = null;
|
||||
var compiling = #if final "final" #elseif debug "debug" #else "release" #end;
|
||||
var target = #if windows "windows" #elseif mac "macos" #elseif linux "linux" #elseif android "android" #elseif ios "ios" #elseif html5 "html5" #else "" #end;
|
||||
var compiling = #if final "release" #elseif debug "debug" #else "release" #end;
|
||||
var target = #if hl "hl"
|
||||
#elseif html5 "html5"
|
||||
#elseif ios "ios"
|
||||
#elseif android "android"
|
||||
#elseif windows "windows"
|
||||
#elseif (mac || macos) "macos"
|
||||
#elseif linux "linux"
|
||||
#else ""
|
||||
#end;
|
||||
if(target == "") throw "Unknown target";
|
||||
var exportPath = Sys.getCwd() + "/export/" + compiling + "/" + target + "/";
|
||||
exportPath += "obj/Options.txt";
|
||||
@@ -20,24 +28,27 @@ class BuildInfo {
|
||||
var options = sys.io.File.getContent(exportPath);
|
||||
for(option in options.split("\n")) {
|
||||
if(option.startsWith("haxe=")) {
|
||||
lastBuiltWith = option.substr(5);
|
||||
lastBuiltWith = option.substr(5).trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(lastBuiltWith != null && lastBuiltWith != haxeVersion)
|
||||
if(lastBuiltWith != null && lastBuiltWith.length > 0 && lastBuiltWith != haxeVersion)
|
||||
Sys.println('Last Built With Haxe: ${lastBuiltWith} [!!!! MAKE SURE IF YOU SWITCHED VERSIONS YOU DELETE EXPORT FOLDERS !!!!]');
|
||||
} catch(e) {}
|
||||
Sys.println('Target Platform: ${
|
||||
#if windows "Windows"
|
||||
#elseif mac "Mac"
|
||||
#elseif linux "Linux"
|
||||
#elseif android "Android"
|
||||
var targetPlatform = #if html5 "Web (HTML5)"
|
||||
#elseif ios "iOS"
|
||||
#elseif html5 "HTML5"
|
||||
#elseif android "Android"
|
||||
#elseif windows "Windows"
|
||||
#elseif (mac || macos) "Mac"
|
||||
#elseif linux "Linux"
|
||||
#else "Unknown"
|
||||
#end
|
||||
}');
|
||||
#end;
|
||||
|
||||
if(haxe.macro.Context.defined("hl")) {
|
||||
targetPlatform = "Hashlink (" + targetPlatform + ")";
|
||||
}
|
||||
Sys.println('Target Platform: ' + targetPlatform);
|
||||
Sys.println('Build Date: ${Date.now().toString()}');
|
||||
Sys.println('');
|
||||
}
|
||||
|
||||
@@ -7,7 +7,18 @@ using StringTools;
|
||||
|
||||
using haxe.macro.Tools;
|
||||
|
||||
using funkin.backend.system.macros.FlagMacro;
|
||||
|
||||
class FlagMacro {
|
||||
private static function hasMeta(meta:Array<MetadataEntry>, name:String):Bool {
|
||||
for(m in meta) {
|
||||
if(m.name == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
|
||||
@@ -21,26 +32,50 @@ class FlagMacro {
|
||||
var parserExprs:Array<Expr> = [];
|
||||
|
||||
for (field in fields) {
|
||||
var skip = false;
|
||||
for(meta in field.meta) {
|
||||
if(meta.name == ":bypass") {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var skip = field.meta.hasMeta(":bypass");
|
||||
var hasLazy = field.meta.hasMeta(":lazy");
|
||||
|
||||
if(skip) continue;
|
||||
|
||||
switch (field.kind) {
|
||||
case FVar(type, expr):
|
||||
var isNullable = false;
|
||||
switch(type) {
|
||||
case TPath({name: "Null", pack: [], params: [TPType(p)]}):
|
||||
type = p;
|
||||
isNullable = true;
|
||||
default:
|
||||
}
|
||||
|
||||
var alsos:Array<Expr> = [];
|
||||
for(meta in field.meta) {
|
||||
if(meta.name == ":also") {
|
||||
for(param in meta.params) {
|
||||
switch(param.expr) {
|
||||
case EField(_, _):
|
||||
alsos.push(param);
|
||||
default:
|
||||
Context.error("Invalid :also parameter", param.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if(type == null) {
|
||||
switch(expr.expr) {
|
||||
case EConst(CIdent("true")) | EConst(CIdent("false")):
|
||||
type = macro: Bool;
|
||||
case EConst(CInt(_)):
|
||||
type = macro: Int;
|
||||
case EConst(CFloat(_)):
|
||||
type = macro: Float;
|
||||
case EConst(CString(_)):
|
||||
type = macro: String;
|
||||
default:
|
||||
Context.error('Flag ' + field.name + ' must have a type', field.pos);
|
||||
}
|
||||
}
|
||||
|
||||
var parser:Expr = null;
|
||||
@@ -52,7 +87,7 @@ class FlagMacro {
|
||||
parser = macro value.split(",").map((e) -> e.trim());
|
||||
case macro: Array<String>:
|
||||
parser = macro value.split(",");
|
||||
case macro: Array<Int>:
|
||||
case (macro: Array<Int>) | (macro: Array<FlxColor>):
|
||||
parser = macro value.split(",").map((e) -> Std.parseInt(e));
|
||||
case macro: Array<Float>:
|
||||
parser = macro value.split(",").map((e) -> Std.parseFloat(e));
|
||||
@@ -61,12 +96,15 @@ class FlagMacro {
|
||||
e = e.trim();
|
||||
e == "true" || e == "t" || e == "1";
|
||||
});
|
||||
case macro: Int:
|
||||
case macro: TrimmedString:
|
||||
field.kind = FVar(macro: String, expr);
|
||||
parser = macro value.trim();
|
||||
case macro: String:
|
||||
parser = macro value;
|
||||
case (macro: Int) | (macro: FlxColor):
|
||||
parser = macro Std.parseInt(value);
|
||||
case macro: Float:
|
||||
parser = macro Std.parseFloat(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}):
|
||||
@@ -128,8 +166,7 @@ class FlagMacro {
|
||||
if(chosenType == STRING) {
|
||||
parser = macro value;
|
||||
field.kind = FVar(macro: String, expr);
|
||||
}
|
||||
else if(chosenType == INT) {
|
||||
} else if(chosenType == INT) {
|
||||
parser = macro Std.parseInt(value);
|
||||
field.kind = FVar(macro: Int, expr);
|
||||
} else {
|
||||
@@ -147,7 +184,7 @@ class FlagMacro {
|
||||
}
|
||||
|
||||
case TPath({name: "Array", pack: []}):
|
||||
Context.error("Flag " + field.name + " cannot be an Array that isnt a String or Bool or TrimmedString", field.pos);
|
||||
Context.error("Flag " + field.name + " cannot be an Array that isnt a String or Bool or TrimmedString or Int or Float", field.pos);
|
||||
case TPath({name: "Map", pack: []}):
|
||||
Context.error("Flag " + field.name + " cannot be a Map<K, V>", field.pos);
|
||||
default:
|
||||
@@ -159,6 +196,14 @@ class FlagMacro {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isNullable) {
|
||||
switch(field.kind) {
|
||||
case FVar(t, e):
|
||||
field.kind = FVar(TPath({name: "Null", pack: [], params: [TPType(t)]}), e);
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
resetExprs.push(macro $i{field.name} = ${expr});
|
||||
|
||||
// parse(name: String, value: String)
|
||||
@@ -166,16 +211,39 @@ class FlagMacro {
|
||||
if(customCheck != null) {
|
||||
parserExprs.push(customCheck);
|
||||
} else {
|
||||
parserExprs.push(macro @:mergeBlock {
|
||||
if(name == $v{field.name}) {
|
||||
$i{field.name} = $parser;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
var alsoExpr = alsos.length > 0 ? macro @:mergeBlock $b{alsos.map((e) -> return macro $e = val)} : macro {};
|
||||
|
||||
if(isNullable) {
|
||||
parserExprs.push(macro @:mergeBlock {
|
||||
if(name == $v{field.name}) {
|
||||
var val = value == "NULL" ? null : $parser;
|
||||
$i{field.name} = val;
|
||||
$alsoExpr;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
parserExprs.push(macro @:mergeBlock {
|
||||
if(name == $v{field.name}) {
|
||||
var val = $parser;
|
||||
$i{field.name} = val;
|
||||
$alsoExpr;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
|
||||
if(hasLazy) {
|
||||
switch(field.kind) {
|
||||
case FVar(t, expr):
|
||||
field.kind = FVar(t, null);
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fields.push({
|
||||
@@ -209,6 +277,9 @@ class FlagMacro {
|
||||
});
|
||||
|
||||
//var printer = new haxe.macro.Printer();
|
||||
//for(field in fields) {
|
||||
// trace(printer.printField(field));
|
||||
//}
|
||||
//trace(printer.printField(fields[fields.length - 2]));
|
||||
//trace(printer.printField(fields[fields.length - 1]));
|
||||
|
||||
|
||||
@@ -42,26 +42,12 @@ class HashLinkFixer {
|
||||
var cl = clRef.get();
|
||||
|
||||
if (cl.isAbstract || cl.isExtern || cl.isInterface) return fields;
|
||||
if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith("_HSX") && !cl.name.endsWith("_HSC") && !cl.name.endsWith("_HLFHelper")) {
|
||||
var metas = cl.meta.get();
|
||||
|
||||
if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith("_HSX") && !cl.name.endsWith("_HSC")) {
|
||||
if(cl.params.length > 0)
|
||||
return fields;
|
||||
|
||||
if(cl.module == "EReg") return fields; // private typedef in same class
|
||||
if(cl.module == "hl.Format") return fields; // enum in same class
|
||||
|
||||
var definedFields = [];
|
||||
|
||||
var helperClass = macro class {
|
||||
|
||||
};
|
||||
|
||||
helperClass.pos = cl.pos;
|
||||
|
||||
var module = cl.module + "_HLFHelper";
|
||||
var hcClassName = cl.name + "_HLFHelper";
|
||||
|
||||
for(f in fields.copy()) {
|
||||
if (f == null)
|
||||
continue;
|
||||
@@ -70,10 +56,12 @@ class HashLinkFixer {
|
||||
|
||||
if(definedFields.contains(f.name)) continue; // no duplicate fields
|
||||
|
||||
var hlNativeMeta = null;
|
||||
var hasHlNative = false;
|
||||
for(m in f.meta)
|
||||
if (m.name == ":hlNative") {
|
||||
hasHlNative = true;
|
||||
hlNativeMeta = m;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -92,16 +80,39 @@ class HashLinkFixer {
|
||||
var overrideExpr:Expr;
|
||||
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
|
||||
|
||||
var printer = new haxe.macro.Printer();
|
||||
if(cl.module == "hl.Gc" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void
|
||||
if(cl.module == "hl.Format" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void
|
||||
|
||||
var name = 'hlf_${f.name}';
|
||||
|
||||
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
|
||||
|
||||
var funcExpr:Expr = returns ? {
|
||||
//macro return $i{name}($a{arguments});
|
||||
macro return @:privateAccess $i{hcClassName}.$name($a{arguments});
|
||||
} : {
|
||||
macro @:privateAccess $i{hcClassName}.$name($a{arguments});
|
||||
};
|
||||
var funcExpr:Expr = macro @:privateAccess $i{name}($a{arguments});
|
||||
if(returns) funcExpr = macro return $funcExpr;
|
||||
|
||||
var cleanMeta = f.meta.copy().filter(function(m) return m.name != ":hlNative");
|
||||
var hasBareMeta = hlNativeMeta.params.length == 0;
|
||||
|
||||
var meta = f.meta.copy();
|
||||
switch hlNativeMeta {
|
||||
case {params: []}:
|
||||
meta = [{name: ":hlNative", params: [macro "std", macro $v{f.name}], pos: Context.currentPos()}].concat(cleanMeta);
|
||||
case {params: [_.expr => EConst(CString(name))]}:
|
||||
meta = [{name: ":hlNative", params: [macro "std", macro $v{name}], pos: Context.currentPos()}].concat(cleanMeta);
|
||||
case {params: [_.expr => EConst(CFloat(version))]}:
|
||||
var curVersion = Context.definedValue("hl_ver");
|
||||
if(curVersion == null) curVersion = "";
|
||||
if(version > curVersion) {
|
||||
meta = cleanMeta;
|
||||
if(f.meta.filter((m) -> m.name == ":noExpr").length > 0)
|
||||
Context.error("Missing function body", f.pos);
|
||||
funcExpr = fun.expr; // restore to default behaviour
|
||||
} else {
|
||||
meta = [{name: ":hlNative", params: [macro "std", macro $v{f.name}], pos: Context.currentPos()}].concat(cleanMeta);
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
var fiel:Field = {
|
||||
name: name,
|
||||
@@ -109,50 +120,32 @@ class HashLinkFixer {
|
||||
kind: FFun({
|
||||
ret: fun.ret,
|
||||
params: fun.params.copy(),
|
||||
expr: fun.expr,
|
||||
expr: funcExpr,
|
||||
args: fun.args.copy()
|
||||
}),
|
||||
access: f.access.copy(),
|
||||
meta: f.meta.copy()
|
||||
access: f.access.copy().filter(function(a) return a != APublic && a != APrivate).concat([APrivate]),
|
||||
meta: meta
|
||||
};
|
||||
helperClass.fields.push(fiel);
|
||||
fields.push(fiel);
|
||||
definedFields.push(f.name);
|
||||
|
||||
// Remove meta from original function
|
||||
for(m in f.meta.copy())
|
||||
if (m.name == ":hlNative") {
|
||||
f.meta.remove(m);
|
||||
}
|
||||
|
||||
fun.expr = funcExpr;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
helperClass.pack = cl.pack.copy();
|
||||
helperClass.pos = cl.pos;
|
||||
helperClass.name = hcClassName;
|
||||
|
||||
if(definedFields.length > 0) {
|
||||
/*if(definedFields.length > 0) {
|
||||
trace(cl.module);
|
||||
|
||||
/*for(m in metas.copy()) {
|
||||
trace(" " + m.name);
|
||||
if(m.name == ":coreApi") {
|
||||
metas.remove(m);
|
||||
}
|
||||
}*/
|
||||
|
||||
var imports = Context.getLocalImports().copy();
|
||||
Context.defineModule(module, [helperClass], imports);
|
||||
|
||||
Context.getLocalImports().push({
|
||||
path: [for(m in module.split(".")) {
|
||||
name: m,
|
||||
pos: Context.currentPos()
|
||||
}],
|
||||
mode: INormal
|
||||
});
|
||||
}
|
||||
var printer = new haxe.macro.Printer();
|
||||
for(field in fields) if(field.name.startsWith("hlf_"))
|
||||
Sys.println(printer.printField(field));
|
||||
}*/
|
||||
}
|
||||
|
||||
return fields;
|
||||
|
||||
@@ -99,7 +99,7 @@ class AsyncUpdater {
|
||||
progress.files = files.length;
|
||||
progress.step = DOWNLOADING_ASSETS;
|
||||
trace('starting assets download');
|
||||
doFile([for(e in files) e], fileNames.copy(), function() {
|
||||
doFile(files.copy(), fileNames.copy(), function() {
|
||||
progress.curFile = -1;
|
||||
progress.curFileName = null;
|
||||
progress.files = 1;
|
||||
|
||||
@@ -56,7 +56,7 @@ class UpdateScreen extends MusicBeatState {
|
||||
generalProgress.alignment = CENTER;
|
||||
add(generalProgress);
|
||||
|
||||
overSound = FlxG.sound.load(Paths.sound('gameOverEnd'));
|
||||
overSound = FlxG.sound.load(Paths.sound(Flags.DEFAULT_GAMEOVER_RETRY_SFX));
|
||||
|
||||
updater.execute();
|
||||
|
||||
|
||||
@@ -122,11 +122,15 @@ final class CoolUtil
|
||||
* @return The attributes through the `FileAttributeWrapper`
|
||||
*/
|
||||
@:noUsing public static inline function safeGetAttributes(path:String, useAbsol:Bool = true):FileAttributeWrapper {
|
||||
#if sys
|
||||
addMissingFolders(Path.directory(path));
|
||||
|
||||
var result = NativeAPI.getFileAttributes(path, useAbsol);
|
||||
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
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,11 +143,15 @@ final class CoolUtil
|
||||
*/
|
||||
@:noUsing public static inline function safeSetAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsol: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);
|
||||
if(result == 0) Logs.trace('Failed to set attributes to $path with a code of: $result', WARNING);
|
||||
return result;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,11 +163,15 @@ final class CoolUtil
|
||||
* @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 {
|
||||
#if sys
|
||||
addMissingFolders(Path.directory(path));
|
||||
|
||||
var result = NativeAPI.addFileAttributes(path, attrib, useAbsol);
|
||||
if(result == 0) Logs.trace('Failed to add attributes to $path with a code of: $result', WARNING);
|
||||
return result;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,11 +183,15 @@ final class CoolUtil
|
||||
* @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 {
|
||||
#if sys
|
||||
addMissingFolders(Path.directory(path));
|
||||
|
||||
var result = NativeAPI.removeFileAttributes(path, attrib, useAbsol);
|
||||
if(result == 0) Logs.trace('Failed to remove attributes to $path with a code of: $result', WARNING);
|
||||
return result;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -393,7 +409,7 @@ final class CoolUtil
|
||||
* @param Looped Whenever the music loops (true)
|
||||
* @param Group A group that this music belongs to (default)
|
||||
*/
|
||||
@:noUsing public static function playMusic(path:String, Persist:Bool = false, Volume:Float = 1, Looped:Bool = true, DefaultBPM:Int = 102, ?Group:FlxSoundGroup) {
|
||||
@:noUsing public static function playMusic(path:String, Persist:Bool = false, Volume:Float = 1, Looped:Bool = true, DefaultBPM:Float = 102, ?Group:FlxSoundGroup) {
|
||||
Conductor.reset();
|
||||
FlxG.sound.playMusic(path, Volume, Looped, Group);
|
||||
if (FlxG.sound.music != null) {
|
||||
@@ -404,17 +420,17 @@ final class CoolUtil
|
||||
if (Assets.exists(infoPath)) {
|
||||
var musicInfo = IniUtil.parseAsset(infoPath, [
|
||||
"BPM" => null,
|
||||
"TimeSignature" => "4/4"
|
||||
"TimeSignature" => Flags.DEFAULT_BEATS_PER_MEASURE + "/" + Flags.DEFAULT_STEPS_PER_BEAT
|
||||
]);
|
||||
|
||||
var timeSignParsed:Array<Null<Float>> = musicInfo["TimeSignature"] == null ? [] : [for(s in musicInfo["TimeSignature"].split("/")) Std.parseFloat(s)];
|
||||
var beatsPerMeasure:Float = 4;
|
||||
var stepsPerBeat:Float = 4;
|
||||
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
|
||||
if (timeSignParsed.length == 2 && !timeSignParsed.contains(null)) {
|
||||
beatsPerMeasure = timeSignParsed[0] == null || timeSignParsed[0] <= 0 ? 4 : cast timeSignParsed[0];
|
||||
stepsPerBeat = timeSignParsed[1] == null || timeSignParsed[1] <= 0 ? 4 : cast timeSignParsed[1];
|
||||
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];
|
||||
}
|
||||
|
||||
var bpm:Null<Float> = Std.parseFloat(musicInfo["BPM"]).getDefault(DefaultBPM);
|
||||
@@ -762,7 +778,14 @@ final class CoolUtil
|
||||
* @param url
|
||||
*/
|
||||
@:noUsing public static inline function openURL(url:String) {
|
||||
#if linux
|
||||
// generally `xdg-open` should work in every distro
|
||||
var cmd = Sys.command("xdg-open", [url]);
|
||||
// run old command JUST IN CASE it fails, which it shouldn't
|
||||
if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [url]);
|
||||
#else
|
||||
FlxG.openURL(url);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ import flixel.sound.FlxSound;
|
||||
import flixel.util.typeLimit.OneOfTwo;
|
||||
import funkin.backend.scripting.*; // lazy
|
||||
import funkin.backend.scripting.events.CancellableEvent;
|
||||
import funkin.backend.scripting.events.DiscordPresenceUpdateEvent;
|
||||
import funkin.backend.scripting.events.discord.*;
|
||||
import funkin.backend.system.macros.Utils;
|
||||
import haxe.Json;
|
||||
import openfl.display.BitmapData;
|
||||
@@ -86,9 +86,9 @@ final class DiscordUtil
|
||||
if (config == null)
|
||||
config = {};
|
||||
|
||||
config.logoKey = config.logoKey.getDefault("icon");
|
||||
config.logoText = config.logoText.getDefault(Application.current.meta.get('title'));
|
||||
config.clientID = config.clientID.getDefault("1027994136193810442");
|
||||
config.logoKey = config.logoKey.getDefault(Flags.DEFAULT_DISCORD_LOGO_KEY);
|
||||
config.logoText = config.logoText.getDefault(Flags.DEFAULT_DISCORD_LOGO_TEXT);
|
||||
config.clientID = config.clientID.getDefault(Flags.DEFAULT_DISCORD_CLIENT_ID);
|
||||
currentID = config.clientID;
|
||||
#end
|
||||
}
|
||||
@@ -525,7 +525,7 @@ typedef DPresence =
|
||||
var ?button1Url:String; /* max 512 bytes */
|
||||
var ?button2Label:String; /* max 32 bytes */
|
||||
var ?button2Url:String; /* max 512 bytes */
|
||||
var ?activityType:ActivityType;
|
||||
var ?activityType:#if DISCORD_RPC ActivityType #else Dynamic #end;
|
||||
var ?streamUrl:String; /* max 512 bytes */
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package funkin.savedata;
|
||||
package funkin.backend.utils;
|
||||
|
||||
import flixel.util.FlxSave;
|
||||
import lime.app.Application;
|
||||
@@ -140,14 +140,16 @@ final class MemoryUtil {
|
||||
reg.match(process.stdout.readAll().toString());
|
||||
if (process.exitCode() == 0) return reg.matched(1);
|
||||
#elseif linux
|
||||
var process = new HiddenProcess("sudo", ["dmidecode", "--type", "17"]);
|
||||
/*var process = new HiddenProcess("sudo", ["dmidecode", "--type", "17"]);
|
||||
if (process.exitCode() != 0) return "Unknown";
|
||||
var lines = process.stdout.readAll().toString().split("\n");
|
||||
for (line in lines) {
|
||||
if (line.startsWith("Type:")) {
|
||||
return line.substring("Type:".length).trim();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
// TODO: sort of unsafe? also requires users to use `sudo`
|
||||
// when launching the engine through the CLI, REIMPLEMENT LATER.
|
||||
#end
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -147,6 +147,8 @@ final 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.updateHitbox && node.att.updateHitbox == "true") spr.updateHitbox();
|
||||
|
||||
if (node.has.zoomfactor)
|
||||
|
||||
@@ -362,7 +362,7 @@ class CharacterEditor extends UIState {
|
||||
}
|
||||
animationText.text = '"${character.getAnimName()}"';
|
||||
|
||||
if (Options.characterDragging)
|
||||
if (Options.characterDragging)
|
||||
handleMouseOffsets();
|
||||
|
||||
super.update(elapsed);
|
||||
|
||||
@@ -73,7 +73,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
|
||||
strumLineList.addButton.callback = function() {
|
||||
strumLineList.add(new StrumLineButton(strumLineList.buttons.length, {
|
||||
characters: ["dad"],
|
||||
characters: [Flags.DEFAULT_OPPONENT],
|
||||
type: 0,
|
||||
notes: null,
|
||||
position: "DAD",
|
||||
@@ -84,7 +84,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
|
||||
// DEFAULTS
|
||||
strumLineList.add(new StrumLineButton(0, {
|
||||
characters: ["dad"],
|
||||
characters: [Flags.DEFAULT_OPPONENT],
|
||||
type: 0,
|
||||
notes: null,
|
||||
position: "DAD",
|
||||
@@ -93,7 +93,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
scrollSpeed: 1,
|
||||
}, strumLineList));
|
||||
strumLineList.add(new StrumLineButton(1, {
|
||||
characters: ["bf"],
|
||||
characters: [Flags.DEFAULT_CHARACTER],
|
||||
type: 1,
|
||||
notes: null,
|
||||
position: "BOYFRIEND",
|
||||
@@ -102,7 +102,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
scrollSpeed: 1,
|
||||
}, strumLineList));
|
||||
strumLineList.add(new StrumLineButton(2, {
|
||||
characters: ["gf"],
|
||||
characters: [Flags.DEFAULT_GIRLFRIEND],
|
||||
type: 2,
|
||||
notes: null,
|
||||
position: "GIRLFRIEND",
|
||||
@@ -333,7 +333,7 @@ class CompactCharacterButton extends UIButton {
|
||||
char = funkin.game.Character.getIconFromCharName(char);
|
||||
var image = Paths.image("icons/" + char);
|
||||
if(!Assets.exists(image))
|
||||
image = Paths.image("icons/face");
|
||||
image = Paths.image("icons/" + Flags.DEFAULT_HEALTH_ICON);
|
||||
charIcon.loadGraphic(image, true, 150, 150);
|
||||
charIcon.updateHitbox();
|
||||
}
|
||||
|
||||
@@ -1325,7 +1325,7 @@ class Charter extends UIState {
|
||||
if (strumLines != null)
|
||||
strumlineLockButton.button.animation.play(strumLines.draggable ? "1" : "0", true);
|
||||
|
||||
WindowUtils.prefix = undos.unsaved ? "* " : "";
|
||||
WindowUtils.prefix = undos.unsaved ? Flags.UNDO_PREFIX : "";
|
||||
SaveWarning.showWarning = undos.unsaved;
|
||||
|
||||
__firstFrame = false;
|
||||
@@ -1382,7 +1382,7 @@ class Charter extends UIState {
|
||||
}
|
||||
|
||||
function _file_saveas(_) {
|
||||
openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false), null, Options.editorPrettyPrint ? "\t" : null), {
|
||||
openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), {
|
||||
defaultSaveFile: '${__diff.toLowerCase()}.json'
|
||||
}));
|
||||
undos.save();
|
||||
@@ -1398,7 +1398,7 @@ class Charter extends UIState {
|
||||
}
|
||||
|
||||
function _file_saveas_no_events(_) {
|
||||
openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false, false), null, Options.editorPrettyPrint ? "\t" : null), {
|
||||
openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false, false), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), {
|
||||
defaultSaveFile: '${__diff.toLowerCase()}.json'
|
||||
}));
|
||||
undos.save();
|
||||
@@ -1408,7 +1408,7 @@ class Charter extends UIState {
|
||||
#if sys
|
||||
CoolUtil.safeSaveFile(
|
||||
'${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json',
|
||||
Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, "\t")
|
||||
Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, Flags.JSON_PRETTY_PRINT)
|
||||
);
|
||||
#else
|
||||
_file_meta_saveas(_);
|
||||
@@ -1416,20 +1416,20 @@ class Charter extends UIState {
|
||||
}
|
||||
|
||||
function _file_meta_saveas(_) {
|
||||
openSubState(new SaveSubstate(Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, "\t"), { // always pretty print meta
|
||||
openSubState(new SaveSubstate(Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, Flags.JSON_PRETTY_PRINT), { // always pretty print meta
|
||||
defaultSaveFile: 'meta.json'
|
||||
}));
|
||||
}
|
||||
|
||||
function _file_saveas_fnflegacy(_) {
|
||||
openSubState(new SaveSubstate(Json.stringify(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? "\t" : null), {
|
||||
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == "normal" ? "" : '-${__diff.toLowerCase()}'}.json',
|
||||
openSubState(new SaveSubstate(Json.stringify(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), {
|
||||
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json',
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function _file_saveas_psych(_) {
|
||||
openSubState(new SaveSubstate(Json.stringify(PsychParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? "\t" : null), {
|
||||
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == "normal" ? "" : '-${__diff.toLowerCase()}'}.json',
|
||||
openSubState(new SaveSubstate(Json.stringify(PsychParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), {
|
||||
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json',
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1437,7 +1437,7 @@ class Charter extends UIState {
|
||||
#if sys
|
||||
CoolUtil.safeSaveFile(
|
||||
'${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json',
|
||||
Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? "\t" : null)
|
||||
Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null)
|
||||
);
|
||||
#else
|
||||
_file_events_saveas(_);
|
||||
@@ -1446,7 +1446,7 @@ class Charter extends UIState {
|
||||
|
||||
function _file_events_saveas(_) {
|
||||
#if sys
|
||||
openSubState(new SaveSubstate(Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? "\t" : null), {
|
||||
openSubState(new SaveSubstate(Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), {
|
||||
defaultSaveFile: 'events.json'
|
||||
}));
|
||||
#end
|
||||
@@ -1775,7 +1775,7 @@ class Charter extends UIState {
|
||||
changeNoteSustain(-1);
|
||||
|
||||
function _note_selectall(_) {
|
||||
selection = [for (note in notesGroup.members) note];
|
||||
selection = cast notesGroup.members.copy();
|
||||
}
|
||||
|
||||
function _note_selectmeasure(_) {
|
||||
|
||||
@@ -164,7 +164,11 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
colorWheel;
|
||||
case TDropDown(options):
|
||||
addLabel();
|
||||
var dropdown = new UIDropDown(eventName.x, y, 320, 32, options, Std.int(Math.abs(options.indexOf(cast value))));
|
||||
var optionIndex = options.indexOf(cast value);
|
||||
if(optionIndex < 0) {
|
||||
optionIndex = 0;
|
||||
}
|
||||
var dropdown = new UIDropDown(eventName.x, y, 320, 32, options, optionIndex);
|
||||
paramsPanel.add(dropdown); paramsFields.push(dropdown);
|
||||
dropdown;
|
||||
case TCharacter:
|
||||
|
||||
@@ -129,7 +129,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
@:privateAccess iconSprite.animation.clearAnimations();
|
||||
|
||||
var path:String = Paths.image('icons/$icon');
|
||||
if (!Assets.exists(path)) path = Paths.image('icons/face');
|
||||
if (!Assets.exists(path)) path = Paths.image('icons/' + Flags.DEFAULT_HEALTH_ICON);
|
||||
|
||||
iconSprite.loadGraphic(path, true, 150, 150);
|
||||
iconSprite.animation.add(icon, [0], 0, false);
|
||||
|
||||
@@ -109,9 +109,9 @@ class CharterSelection extends EditorTreeMenu {
|
||||
sys.FileSystem.createDirectory('$songFolder/charts');
|
||||
|
||||
// Save Files
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(creation.meta, "\t"));
|
||||
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Paths.SOUND_EXT}', creation.instBytes);
|
||||
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Paths.SOUND_EXT}', creation.voicesBytes);
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(creation.meta, Flags.JSON_PRETTY_PRINT));
|
||||
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Flags.SOUND_EXT}', creation.instBytes);
|
||||
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Flags.SOUND_EXT}', creation.voicesBytes);
|
||||
#end
|
||||
|
||||
var option = new EditorIconOption(creation.meta.name, "Press ACCEPT to choose a difficulty to edit.", creation.meta.icon, function() {
|
||||
@@ -147,7 +147,7 @@ class CharterSelection extends EditorTreeMenu {
|
||||
var songFolder:String = '${Paths.getAssetsRoot()}/songs/${curSong.name}';
|
||||
|
||||
// Save Files
|
||||
CoolUtil.safeSaveFile('$songFolder/charts/${name}.json', Json.stringify(data, "\t"));
|
||||
CoolUtil.safeSaveFile('$songFolder/charts/${name}.json', Json.stringify(data, Flags.JSON_PRETTY_PRINT));
|
||||
|
||||
// Add to List
|
||||
curSong.difficulties.push(name);
|
||||
|
||||
@@ -48,7 +48,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
|
||||
if (creatingStrumLine)
|
||||
strumLine = {
|
||||
characters: ["dad"],
|
||||
characters: [Flags.DEFAULT_OPPONENT],
|
||||
type: OPPONENT,
|
||||
notes: [],
|
||||
position: "dad",
|
||||
@@ -256,7 +256,7 @@ class CharacterButton extends UIButton {
|
||||
char = Character.getIconFromCharName(char);
|
||||
var image = Paths.image("icons/" + char);
|
||||
if(!Assets.exists(image))
|
||||
image = Paths.image("icons/face");
|
||||
image = Paths.image("icons/" + Flags.DEFAULT_HEALTH_ICON);
|
||||
charIcon.loadGraphic(image, true, 150, 150);
|
||||
charIcon.updateHitbox();
|
||||
}
|
||||
|
||||
@@ -80,7 +80,9 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54);
|
||||
songDataGroup.add(stepsPerBeatStepper);
|
||||
|
||||
instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Paths.SOUND_EXT, function (res) {
|
||||
var voicesUIText:UIText = null;
|
||||
|
||||
instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Flags.SOUND_EXT, function (res) {
|
||||
var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 8, instExplorer.y + 8, res);
|
||||
instExplorer.members.push(audioPlayer);
|
||||
instExplorer.uiElement = audioPlayer;
|
||||
@@ -90,7 +92,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
"Inst Audio File $* Required$",
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, null, null, Paths.SOUND_EXT, function (res) {
|
||||
voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, null, null, Flags.SOUND_EXT, function (res) {
|
||||
var audioPlayer:UIAudioPlayer = new UIAudioPlayer(voicesExplorer.x + 8, voicesExplorer.y + 8, res);
|
||||
voicesExplorer.members.push(audioPlayer);
|
||||
voicesExplorer.uiElement = audioPlayer;
|
||||
@@ -211,7 +213,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
@:privateAccess iconSprite.animation.clearAnimations();
|
||||
|
||||
var path:String = Paths.image('icons/$icon');
|
||||
if (!Assets.exists(path)) path = Paths.image('icons/face');
|
||||
if (!Assets.exists(path)) path = Paths.image('icons/' + Flags.DEFAULT_HEALTH_ICON);
|
||||
|
||||
iconSprite.loadGraphic(path, true, 150, 150);
|
||||
iconSprite.animation.add(icon, [0], 0, false);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import openfl.display.ShaderParameter;
|
||||
import openfl.display.ShaderInput;
|
||||
import openfl.filters.ShaderFilter;
|
||||
import flixel.tweens.FlxTween;
|
||||
import funkin.backend.shaders.CustomShader;
|
||||
import openfl.filters.ShaderFilter;
|
||||
@@ -7,7 +10,17 @@ import openfl.filters.ShaderFilter;
|
||||
// TODO: make UIWarningSubstate extend this
|
||||
class UISubstateWindow extends MusicBeatSubstate {
|
||||
var camShaders:Array<FlxCamera> = [];
|
||||
var blurShader:CustomShader = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast");
|
||||
var blurShader:CustomShader = {
|
||||
var _ = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast");
|
||||
if(!Options.intensiveBlur) {
|
||||
var noiseTexture:ShaderInput<openfl.display.BitmapData> = _.data.noiseTexture;
|
||||
noiseTexture.input = Assets.getBitmapData("assets/shaders/noise256.png");
|
||||
noiseTexture.wrap = REPEAT;
|
||||
var noiseTextureSize:ShaderParameter<Float> = _.data.noiseTextureSize;
|
||||
noiseTextureSize.value = [noiseTexture.input.width, noiseTexture.input.height];
|
||||
}
|
||||
_;
|
||||
};
|
||||
|
||||
var titleSpr:UIText;
|
||||
var messageSpr:UIText;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import openfl.display.ShaderParameter;
|
||||
import openfl.display.ShaderInput;
|
||||
import openfl.filters.ShaderFilter;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import funkin.backend.shaders.CustomShader;
|
||||
@@ -7,7 +10,17 @@ import openfl.filters.ShaderFilter;
|
||||
|
||||
class UIWarningSubstate extends MusicBeatSubstate {
|
||||
var camShaders:Array<FlxCamera> = [];
|
||||
var blurShader:CustomShader = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast");
|
||||
var blurShader:CustomShader = {
|
||||
var _ = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast");
|
||||
if(!Options.intensiveBlur) {
|
||||
var noiseTexture:ShaderInput<openfl.display.BitmapData> = _.data.noiseTexture;
|
||||
noiseTexture.input = Assets.getBitmapData("assets/shaders/noise256.png");
|
||||
noiseTexture.wrap = REPEAT;
|
||||
var noiseTextureSize:ShaderParameter<Float> = _.data.noiseTextureSize;
|
||||
noiseTextureSize.value = [noiseTexture.input.width, noiseTexture.input.height];
|
||||
}
|
||||
_;
|
||||
};
|
||||
|
||||
var title:String;
|
||||
var message:String;
|
||||
|
||||
@@ -12,13 +12,11 @@ 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;
|
||||
import funkin.backend.scripting.events.PlayAnimEvent;
|
||||
import funkin.backend.scripting.events.CancellableEvent;
|
||||
import funkin.backend.scripting.events.character.*;
|
||||
import funkin.backend.scripting.events.sprite.*;
|
||||
import funkin.backend.scripting.events.PointEvent;
|
||||
import funkin.backend.scripting.events.DrawEvent;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.backend.system.interfaces.IBeatReceiver;
|
||||
import funkin.backend.system.interfaces.IOffsetCompatible;
|
||||
@@ -36,8 +34,8 @@ using StringTools;
|
||||
@:allow(funkin.game.PlayState)
|
||||
class Character extends FunkinSprite implements IBeatReceiver implements IOffsetCompatible implements IPrePostDraw {
|
||||
public var isPlayer:Bool = false;
|
||||
public var curCharacter:String = 'bf';
|
||||
public var sprite:String = 'bf';
|
||||
public var curCharacter:String = Flags.DEFAULT_CHARACTER;
|
||||
public var sprite:String = Flags.DEFAULT_CHARACTER;
|
||||
|
||||
public var lastHit:Float = Math.NEGATIVE_INFINITY;
|
||||
public var holdTime:Float = 4;
|
||||
@@ -68,12 +66,12 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
|
||||
@:noCompletion var __switchAnims:Bool = true;
|
||||
|
||||
public function new(x:Float, y:Float, ?character:String = "bf", isPlayer:Bool = false, switchAnims:Bool = true, disableScripts:Bool = false)
|
||||
public function new(x:Float, y:Float, ?character:String, isPlayer:Bool = false, switchAnims:Bool = true, disableScripts:Bool = false)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
animOffsets = new Map<String, FlxPoint>();
|
||||
curCharacter = character;
|
||||
curCharacter = character != null ? character : Flags.DEFAULT_CHARACTER;
|
||||
this.isPlayer = isPlayer;
|
||||
__switchAnims = switchAnims;
|
||||
|
||||
@@ -130,7 +128,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
scripts.call("update", [elapsed]);
|
||||
if (stunned) {
|
||||
__stunnedTime += elapsed;
|
||||
if (__stunnedTime > 5 / 60)
|
||||
if (__stunnedTime > Flags.STUNNED_TIME)
|
||||
stunned = false;
|
||||
}
|
||||
|
||||
@@ -156,6 +154,11 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
}
|
||||
|
||||
public function tryDance() {
|
||||
var event = new CancellableEvent();
|
||||
script.call("onTryDance", [event]);
|
||||
if (event.cancelled)
|
||||
return;
|
||||
|
||||
switch (lastAnimContext) {
|
||||
case SING | MISS:
|
||||
if (lastHit + (Conductor.stepCrochet * holdTime) < Conductor.songPosition)
|
||||
@@ -182,6 +185,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
tryDance();
|
||||
}
|
||||
|
||||
public override function measureHit(curMeasure:Int)
|
||||
script.call("measureHit", [curMeasure]);
|
||||
|
||||
public override function stepHit(curStep:Int)
|
||||
scripts.call("stepHit", [curStep]);
|
||||
|
||||
@@ -234,6 +240,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
|
||||
public var ghostDraw:Bool = false;
|
||||
public override function draw() {
|
||||
var e = EventManager.get(DrawEvent).recycle();
|
||||
script.call("draw", [e]);
|
||||
|
||||
preDraw();
|
||||
super.draw();
|
||||
postDraw();
|
||||
@@ -242,6 +251,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
if (debugHitbox) drawHitbox();
|
||||
if (debugCamera) drawCamera();
|
||||
}
|
||||
|
||||
script.call("postDraw", [e]);
|
||||
}
|
||||
|
||||
public var debugHitbox:Bool = false;
|
||||
@@ -353,15 +364,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
|
||||
@:noCompletion var __reverseTrailProcedure:Bool = false;
|
||||
|
||||
// When using trails on characters you should do `trail.beforeCache = char.beforeTrailCache;`
|
||||
// When using trails on characters you should do `trail.beforeCache = char.beforeTrailCache;` and `trail.afterCache = char.afterTrailCache;`
|
||||
public dynamic function beforeTrailCache()
|
||||
if (isFlippedOffsets()) {
|
||||
flipX = !flipX;
|
||||
scale.x *= -1;
|
||||
__reverseTrailProcedure = true;
|
||||
}
|
||||
|
||||
// When using trails on characters you should do `trail.afterCache = char.afterTrailCache;`
|
||||
|
||||
public dynamic function afterTrailCache()
|
||||
if (__reverseTrailProcedure) {
|
||||
flipX = !flipX;
|
||||
@@ -540,6 +550,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
@:noCompletion private function get_danceInterval()
|
||||
return beatInterval;
|
||||
|
||||
public static var FALLBACK_CHARACTER:String = Flags.DEFAULT_CHARACTER;
|
||||
public static var FALLBACK_DEAD_CHARACTER:String = Flags.DEFAULT_GAMEOVER_CHARACTER;
|
||||
|
||||
private function set_script(script:Script):Script {
|
||||
if (scripts == null) (scripts = new ScriptPack("Character")).setParent(this);
|
||||
|
||||
@@ -557,8 +570,6 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
// ---- end of Backwards compat ----
|
||||
|
||||
|
||||
public static var FALLBACK_CHARACTER:String = "bf";
|
||||
public static var FALLBACK_DEAD_CHARACTER:String = "bf-dead";
|
||||
public static function getXMLFromCharName(character:OneOfTwo<String, Character>):Access {
|
||||
var char:Character = null;
|
||||
if (character is Character) {
|
||||
@@ -595,7 +606,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
}
|
||||
|
||||
public static function getIconFromCharName(?character:String, ?defaultIcon:String = null) {
|
||||
if(character == null) return "face";
|
||||
if(character == null) return Flags.DEFAULT_HEALTH_ICON;
|
||||
if(defaultIcon == null) defaultIcon = character;
|
||||
var icon:String = defaultIcon;
|
||||
|
||||
@@ -618,4 +629,4 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
list.push(CoolUtil.getFilename(path));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.backend.scripting.events.CancellableEvent;
|
||||
import funkin.backend.scripting.events.GameOverCreationEvent;
|
||||
import funkin.backend.scripting.events.gameover.*;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.editors.charter.Charter;
|
||||
import funkin.menus.FreeplayState;
|
||||
@@ -28,6 +28,7 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||
|
||||
public var gameoverScript:Script;
|
||||
public var game:PlayState = PlayState.instance; // shortcut
|
||||
public static var instance:GameOverSubstate = null;
|
||||
|
||||
private var __cancelDefault:Bool = false;
|
||||
|
||||
@@ -36,20 +37,16 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||
|
||||
public var lossSFX:FlxSound;
|
||||
|
||||
public function new(x:Float, y:Float, character:String = null, player:Bool = true, gameOverSong:String = "gameOver", lossSFX:String = "gameOverSFX", retrySFX:String = "gameOverEnd")
|
||||
public function new(x:Float, y:Float, ?character:String, player:Bool = true, ?gameOverSong:String, ?lossSFX:String, ?retrySFX:String)
|
||||
{
|
||||
super();
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
characterName = character;
|
||||
if (characterName == null)
|
||||
characterName = Character.FALLBACK_DEAD_CHARACTER;
|
||||
|
||||
this.player = player;
|
||||
this.gameOverSong = gameOverSong;
|
||||
this.lossSFXName = lossSFX;
|
||||
this.retrySFX = retrySFX;
|
||||
this.characterName = character != null ? character : Flags.DEFAULT_GAMEOVER_CHARACTER;
|
||||
this.gameOverSong = gameOverSong != null ? gameOverSong : Flags.DEFAULT_GAMEOVER_SONG;
|
||||
this.lossSFXName = lossSFX != null ? lossSFX : Flags.DEFAULT_GAMEOVER_LOSS_SFX;
|
||||
this.retrySFX = retrySFX != null ? retrySFX : Flags.DEFAULT_GAMEOVER_RETRY_SFX;
|
||||
}
|
||||
|
||||
public override function create()
|
||||
@@ -60,7 +57,7 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||
FlxG.sound.music.stop();
|
||||
|
||||
gameoverScript = Script.create(Paths.script(script));
|
||||
gameoverScript.setParent(this);
|
||||
gameoverScript.setParent(instance = this);
|
||||
gameoverScript.load();
|
||||
|
||||
var event = EventManager.get(GameOverCreationEvent).recycle(x, y, characterName, player, gameOverSong, gameOverSongBPM, lossSFXName, retrySFX);
|
||||
@@ -131,7 +128,7 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||
|
||||
if (!isEnding && ((!lossSFX.playing) || (character.getAnimName() == "firstDeath" && character.isAnimFinished())) && (FlxG.sound.music == null || !FlxG.sound.music.playing))
|
||||
{
|
||||
CoolUtil.playMusic(Paths.music(gameOverSong), false, 1, true, 100);
|
||||
CoolUtil.playMusic(Paths.music(gameOverSong), false, 1, true, Flags.DEFAULT_BPM);
|
||||
beatHit(0);
|
||||
}
|
||||
}
|
||||
@@ -202,5 +199,6 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||
gameoverScript.destroy();
|
||||
|
||||
super.destroy();
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class HealthIcon extends FlxSprite
|
||||
/**
|
||||
* current animation state
|
||||
*/
|
||||
public var curAnimState:Int = -1;
|
||||
public var curAnimState:Int = -1;
|
||||
|
||||
/**
|
||||
* Helper for HScript who can't make maps
|
||||
@@ -48,12 +48,12 @@ class HealthIcon extends FlxSprite
|
||||
];
|
||||
}
|
||||
|
||||
public function new(char:String = 'bf', isPlayer:Bool = false)
|
||||
public function new(?char:String, isPlayer:Bool = false)
|
||||
{
|
||||
super();
|
||||
health = 0.5;
|
||||
this.isPlayer = isPlayer;
|
||||
setIcon(char);
|
||||
setIcon(char != null ? char : Flags.DEFAULT_CHARACTER);
|
||||
|
||||
scrollFactor.set();
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class HealthIcon extends FlxSprite
|
||||
if(curCharacter != char || this.width != width || this.height != height) {
|
||||
curCharacter = char;
|
||||
var path = Paths.image('icons/$char');
|
||||
if (!Assets.exists(path)) path = Paths.image('icons/face');
|
||||
if (!Assets.exists(path)) path = Paths.image('icons/' + Flags.DEFAULT_HEALTH_ICON);
|
||||
|
||||
loadGraphic(path, true, width, height);
|
||||
|
||||
@@ -90,14 +90,12 @@ class HealthIcon extends FlxSprite
|
||||
if (animation.curAnim != null) {
|
||||
var i:Int = -1;
|
||||
var oldKey:Int = -1;
|
||||
for(k=>icon in healthSteps) {
|
||||
if (k > oldKey && k < health * 100) {
|
||||
oldKey = k;
|
||||
i = icon;
|
||||
}
|
||||
for (k=>icon in healthSteps) if (k > oldKey && k <= health * 100) {
|
||||
oldKey = k;
|
||||
i = icon;
|
||||
}
|
||||
if (i >= 0 && curAnimState != i) {
|
||||
var event = EventManager.get(funkin.backend.scripting.events.HealthIconChangeEvent).recycle(i, this);
|
||||
var event = EventManager.get(funkin.backend.scripting.events.healthicon.HealthIconChangeEvent).recycle(i, this);
|
||||
funkin.backend.scripting.GlobalScript.event("onHealthIconAnimChange", event);
|
||||
if (!event.cancelled)
|
||||
animation.curAnim.curFrame = event.amount;
|
||||
|
||||
@@ -3,7 +3,7 @@ package funkin.game;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.math.FlxRect;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import funkin.backend.scripting.events.*;
|
||||
import funkin.backend.scripting.events.note.NoteCreationEvent;
|
||||
import funkin.backend.system.Conductor;
|
||||
|
||||
using StringTools;
|
||||
@@ -86,7 +86,7 @@ class Note extends FlxSprite
|
||||
return PlayState.instance.getNoteType(noteTypeID);
|
||||
}
|
||||
|
||||
public static var swagWidth:Float = 160 * 0.7;
|
||||
public static var swagWidth:Float = 160 * 0.7; // TODO: remove this
|
||||
|
||||
private static var __customNoteTypeExists:Map<String, Bool> = [];
|
||||
|
||||
@@ -117,11 +117,8 @@ class Note extends FlxSprite
|
||||
this.isSustainNote = sustain;
|
||||
this.sustainLength = sustainLength;
|
||||
this.strumLine = strumLine;
|
||||
for(field in Reflect.fields(noteData)) {
|
||||
if(!DEFAULT_FIELDS.contains(field)) {
|
||||
this.extra.set(field, Reflect.field(noteData, field));
|
||||
}
|
||||
}
|
||||
for(field in Reflect.fields(noteData)) if(!DEFAULT_FIELDS.contains(field))
|
||||
this.extra.set(field, Reflect.field(noteData, field));
|
||||
|
||||
x += 50;
|
||||
// MAKE SURE ITS DEFINITELY OFF SCREEN?
|
||||
@@ -132,10 +129,10 @@ class Note extends FlxSprite
|
||||
|
||||
var customType = Paths.image('game/notes/${this.noteType}');
|
||||
var event = EventManager.get(NoteCreationEvent).recycle(this, strumID, this.noteType, noteTypeID, PlayState.instance.strumLines.members.indexOf(strumLine), mustPress,
|
||||
(this.noteType != null && customTypePathExists(customType)) ? 'game/notes/${this.noteType}' : 'game/notes/default', @:privateAccess strumLine.strumScale * 0.7, animSuffix);
|
||||
(this.noteType != null && customTypePathExists(customType)) ? 'game/notes/${this.noteType}' : 'game/notes/default', @:privateAccess strumLine.strumScale * Flags.DEFAULT_NOTE_SCALE, animSuffix);
|
||||
|
||||
if (PlayState.instance != null)
|
||||
event = PlayState.instance.scripts.event("onNoteCreation", event);
|
||||
event = PlayState.instance.gameAndCharsEvent("onNoteCreation", event);
|
||||
|
||||
this.animSuffix = event.animSuffix;
|
||||
if (!event.cancelled) {
|
||||
@@ -189,7 +186,7 @@ class Note extends FlxSprite
|
||||
|
||||
if (PlayState.instance != null) {
|
||||
PlayState.instance.splashHandler.getSplashGroup(splash);
|
||||
PlayState.instance.scripts.event("onPostNoteCreation", event);
|
||||
PlayState.instance.gameAndCharsEvent("onPostNoteCreation", event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class NoteGroup extends FlxTypedGroup<Note> {
|
||||
/**
|
||||
* How many ms it should show a note before it should be hit
|
||||
**/
|
||||
public var limit:Float = 1500;
|
||||
public var limit:Float = Flags.DEFAULT_NOTE_MS_LIMIT;
|
||||
|
||||
/**
|
||||
* Preallocates the members array with nulls, but if theres anything in the array already it clears it
|
||||
|
||||
+131
-78
@@ -18,6 +18,8 @@ import funkin.backend.chart.EventsData;
|
||||
import funkin.backend.scripting.DummyScript;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.backend.scripting.ScriptPack;
|
||||
import funkin.backend.scripting.events.gameplay.*;
|
||||
import funkin.backend.scripting.events.note.*;
|
||||
import funkin.backend.scripting.events.*;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.backend.system.RotatingSpriteGroup;
|
||||
@@ -28,7 +30,7 @@ import funkin.game.SplashHandler;
|
||||
import funkin.game.cutscenes.*;
|
||||
import funkin.menus.*;
|
||||
import funkin.menus.StoryMenuState.WeekData;
|
||||
import funkin.savedata.FunkinSave;
|
||||
import funkin.backend.utils.FunkinSave;
|
||||
import haxe.io.Path;
|
||||
|
||||
using StringTools;
|
||||
@@ -61,7 +63,7 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* The selected difficulty name
|
||||
*/
|
||||
public static var difficulty:String = "normal";
|
||||
public static var difficulty:String = Flags.DEFAULT_DIFFICULTY;
|
||||
/**
|
||||
* Whenever the week is coming from the mods folder or not.
|
||||
*/
|
||||
@@ -73,11 +75,11 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* Whenever the song has been started with opponent mode on.
|
||||
*/
|
||||
public static var opponentMode:Bool = false;
|
||||
public static var opponentMode:Bool = Flags.DEFAULT_OPPONENT_MODE;
|
||||
/**
|
||||
* Whenever the song has been started with co-op mode on.
|
||||
*/
|
||||
public static var coopMode:Bool = false;
|
||||
public static var coopMode:Bool = Flags.DEFAULT_COOP_MODE;
|
||||
|
||||
/**
|
||||
* Script Pack of all the scripts being ran.
|
||||
@@ -96,15 +98,15 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* Game Over Song. (assets/music/gameOver.ogg)
|
||||
*/
|
||||
public var gameOverSong:String = "gameOver";
|
||||
public var gameOverSong:String = Flags.DEFAULT_GAMEOVER_SONG;
|
||||
/**
|
||||
* Game Over Song. (assets/sounds/gameOverSFX.ogg)
|
||||
*/
|
||||
public var lossSFX:String = "gameOverSFX";
|
||||
public var lossSFX:String = Flags.DEFAULT_GAMEOVER_LOSS_SFX;
|
||||
/**
|
||||
* Game Over End SFX, used when retrying. (assets/sounds/gameOverEnd.ogg)
|
||||
*/
|
||||
public var retrySFX:String = "gameOverEnd";
|
||||
public var retrySFX:String = Flags.DEFAULT_GAMEOVER_RETRY_SFX;
|
||||
|
||||
/**
|
||||
* Current Stage.
|
||||
@@ -209,15 +211,15 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* Whenever the vocals should be muted when a note is missed.
|
||||
*/
|
||||
public var muteVocalsOnMiss:Bool = true;
|
||||
public var muteVocalsOnMiss:Bool = Flags.DEFAULT_MUTE_VOCALS_ON_MISS;
|
||||
/**
|
||||
* Whenever the player can press 7, 8 or 9 to access the debug menus.
|
||||
*/
|
||||
public var canAccessDebugMenus:Bool = true;
|
||||
public var canAccessDebugMenus:Bool = Flags.DEFAULT_CAN_ACCESS_DEBUG_MENUS;
|
||||
/**
|
||||
* Whether or not to show the secret gitaroo pause.
|
||||
*/
|
||||
public var allowGitaroo:Bool = true;
|
||||
public var allowGitaroo:Bool = Flags.DEFAULT_GITAROO;
|
||||
/**
|
||||
* Whether or not to bop the icons on beat.
|
||||
*/
|
||||
@@ -231,19 +233,23 @@ class PlayState extends MusicBeatState
|
||||
* Interval of cam zooming (beats).
|
||||
* For example: if set to 4, the camera will zoom every 4 beats.
|
||||
*/
|
||||
public var camZoomingInterval:Int = 4;
|
||||
public var camZoomingInterval:Int = Flags.DEFAULT_CAM_ZOOM_INTERVAL;
|
||||
/**
|
||||
* How strong the cam zooms should be (defaults to 1)
|
||||
*/
|
||||
public var camZoomingStrength:Float = 1;
|
||||
public var camZoomingStrength:Float = Flags.DEFAULT_CAM_ZOOM_STRENGTH;
|
||||
/**
|
||||
* Maximum amount of zoom for the camera.
|
||||
*/
|
||||
public var maxCamZoom:Float = 1.35;
|
||||
public var maxCamZoom:Float = Flags.MAX_CAMERA_ZOOM;
|
||||
/**
|
||||
* Current song name (lowercase)
|
||||
*/
|
||||
public var curSong:String = "";
|
||||
/**
|
||||
* Current song name (lowercase and spaces to dashes)
|
||||
*/
|
||||
public var curSongID:String = "";
|
||||
/**
|
||||
* Current stage name
|
||||
*/
|
||||
@@ -262,7 +268,7 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* Maximum health the player can have. Defaults to 2.
|
||||
*/
|
||||
@:isVar public var maxHealth(get, set):Float = 2;
|
||||
@:isVar public var maxHealth(get, set):Float = Flags.DEFAULT_MAX_HEALTH;
|
||||
/**
|
||||
* Current combo.
|
||||
*/
|
||||
@@ -362,25 +368,25 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* Camera zoom at which the game lerps to.
|
||||
*/
|
||||
public var defaultCamZoom:Float = 1.05;
|
||||
public var defaultCamZoom:Float = Flags.DEFAULT_CAM_ZOOM;
|
||||
/**
|
||||
* Speed at which the game camera zoom lerps to.
|
||||
*/
|
||||
public var camGameZoomLerp:Float = 0.05;
|
||||
public var camGameZoomLerp:Float = Flags.DEFAULT_CAM_ZOOM_LERP;
|
||||
|
||||
/**
|
||||
* Camera zoom at which the hud lerps to.
|
||||
*/
|
||||
public var defaultHudZoom:Float = 1.0;
|
||||
public var defaultHudZoom:Float = Flags.DEFAULT_HUD_ZOOM;
|
||||
/**
|
||||
* Speed at which the hud camera zoom lerps to.
|
||||
*/
|
||||
public var camHUDZoomLerp:Float = 0.05;
|
||||
public var camHUDZoomLerp:Float = Flags.DEFAULT_HUD_ZOOM_LERP;
|
||||
|
||||
/**
|
||||
* Zoom for the pixel assets.
|
||||
*/
|
||||
public static var daPixelZoom:Float = 6;
|
||||
public static var daPixelZoom:Float = Flags.PIXEL_ART_SCALE;
|
||||
|
||||
/**
|
||||
* Whenever the game is currently in a cutscene or not.
|
||||
@@ -459,6 +465,22 @@ class PlayState extends MusicBeatState
|
||||
* Group containing all of the combo sprites.
|
||||
*/
|
||||
public var comboGroup:RotatingSpriteGroup;
|
||||
/**
|
||||
* Whenever the Rating sprites should be shown or not.
|
||||
*
|
||||
* NOTE: This is just a default value for the final value, the final value can be changed through notes hit events.
|
||||
*/
|
||||
public var defaultDisplayRating:Bool = true;
|
||||
/**
|
||||
* Whenever the Combo sprite should be shown or not (like old Week 7 patches).
|
||||
*
|
||||
* NOTE: This is just a default value for the final value, the final value can be changed through notes hit events.
|
||||
*/
|
||||
public var defaultDisplayCombo:Bool = false;
|
||||
/**
|
||||
* Minimum Combo Count to display the combo digits. Anything less than 0 means it won't be shown.
|
||||
*/
|
||||
public var minDigitDisplay:Int = 10;
|
||||
/**
|
||||
* Array containing all of the note types names.
|
||||
*/
|
||||
@@ -516,7 +538,7 @@ class PlayState extends MusicBeatState
|
||||
if (e.percent <= acc && (rating == null || rating.percent < e.percent))
|
||||
rating = e;
|
||||
|
||||
var event = scripts.event("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating));
|
||||
var event = gameAndCharsEvent("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating));
|
||||
if (!event.cancelled)
|
||||
curRating = event.rating;
|
||||
}
|
||||
@@ -538,6 +560,24 @@ class PlayState extends MusicBeatState
|
||||
return name;
|
||||
}
|
||||
|
||||
public inline function callOnCharacters(func:String, ?parameters:Array<Dynamic>) {
|
||||
if(strumLines != null) strumLines.forEachAlive(function (strLine:StrumLine) {
|
||||
if (strLine.characters != null) for (character in strLine.characters)
|
||||
if (character != null) character.script.call(func, parameters);
|
||||
});
|
||||
}
|
||||
|
||||
public inline function gameAndCharsCall(func:String, ?parameters:Array<Dynamic>, ?charsFunc:String) {
|
||||
scripts.call(func, parameters);
|
||||
callOnCharacters(charsFunc != null ? charsFunc : func, parameters);
|
||||
}
|
||||
|
||||
public inline function gameAndCharsEvent<T:CancellableEvent>(func:String, ?event:T, ?charsFunc:String):T {
|
||||
scripts.event(func, event);
|
||||
callOnCharacters(charsFunc != null ? charsFunc : func, [event]);
|
||||
return event;
|
||||
}
|
||||
|
||||
@:dox(hide) override public function create()
|
||||
{
|
||||
Note.__customNoteTypeExists = [];
|
||||
@@ -581,7 +621,7 @@ class PlayState extends MusicBeatState
|
||||
// CHARACTER INITIALIZATION
|
||||
#if REGION
|
||||
comboGroup = new RotatingSpriteGroup(FlxG.width * 0.55, (FlxG.height * 0.5) - 60);
|
||||
comboGroup.maxSize = 25;
|
||||
comboGroup.maxSize = Flags.DEFAULT_COMBO_GROUP_MAX_SIZE;
|
||||
#end
|
||||
|
||||
// CAMERA FOLLOW, SCRIPTS & STAGE INITIALIZATION
|
||||
@@ -589,7 +629,7 @@ class PlayState extends MusicBeatState
|
||||
camFollow = new FlxObject(0, 0, 2, 2);
|
||||
add(camFollow);
|
||||
|
||||
if (SONG.stage == null || SONG.stage.trim() == "") SONG.stage = "stage";
|
||||
if (SONG.stage == null || SONG.stage.trim() == "") SONG.stage = Flags.DEFAULT_STAGE;
|
||||
add(stage = new Stage(SONG.stage));
|
||||
|
||||
if (!chartingMode || Options.charterEnablePlaytestScripts) {
|
||||
@@ -702,15 +742,15 @@ class PlayState extends MusicBeatState
|
||||
// HUD INITIALIZATION & CAMERA INITIALIZATION
|
||||
#if REGION
|
||||
var event = EventManager.get(AmountEvent).recycle(null);
|
||||
if (!scripts.event("onPreGenerateStrums", event).cancelled) {
|
||||
if (!gameAndCharsEvent("onPreGenerateStrums", event).cancelled) {
|
||||
generateStrums(event.amount);
|
||||
scripts.event("onPostGenerateStrums", event);
|
||||
gameAndCharsEvent("onPostGenerateStrums", event);
|
||||
}
|
||||
|
||||
for(str in strumLines)
|
||||
str.generate(str.data, (chartingMode && Charter.startHere) ? Charter.startTime : null);
|
||||
|
||||
FlxG.camera.follow(camFollow, LOCKON, 0.04);
|
||||
FlxG.camera.follow(camFollow, LOCKON, Flags.DEFAULT_CAMERA_FOLLOW_SPEED);
|
||||
FlxG.camera.zoom = defaultCamZoom;
|
||||
// camHUD.zoom = defaultHudZoom;
|
||||
|
||||
@@ -739,10 +779,11 @@ class PlayState extends MusicBeatState
|
||||
healthBar.createFilledBar(leftColor, rightColor);
|
||||
add(healthBar);
|
||||
|
||||
health = maxHealth / 2;
|
||||
if (Flags.DEFAULT_HEALTH != null) health = Flags.DEFAULT_HEALTH;
|
||||
else health = maxHealth / 2;
|
||||
|
||||
iconP1 = new HealthIcon(boyfriend != null ? boyfriend.getIcon() : "face", true);
|
||||
iconP2 = new HealthIcon(dad != null ? dad.getIcon() : "face", false);
|
||||
iconP1 = new HealthIcon(boyfriend != null ? boyfriend.getIcon() : Flags.DEFAULT_HEALTH_ICON, true);
|
||||
iconP2 = new HealthIcon(dad != null ? dad.getIcon() : Flags.DEFAULT_HEALTH_ICON, false);
|
||||
for(icon in [iconP1, iconP2]) {
|
||||
icon.y = healthBar.y - (icon.height / 2);
|
||||
add(icon);
|
||||
@@ -779,7 +820,7 @@ class PlayState extends MusicBeatState
|
||||
FlxG.sound.load(Paths.sound(s));
|
||||
|
||||
if (chartingMode) {
|
||||
WindowUtils.prefix = Charter.undos.unsaved ? "* " : "";
|
||||
WindowUtils.prefix = Charter.undos.unsaved ? Flags.UNDO_PREFIX : "";
|
||||
WindowUtils.suffix = " (Chart Playtesting)";
|
||||
|
||||
SaveWarning.showWarning = Charter.undos.unsaved;
|
||||
@@ -804,7 +845,7 @@ class PlayState extends MusicBeatState
|
||||
|
||||
__updateNote_event = EventManager.get(NoteUpdateEvent);
|
||||
|
||||
scripts.call("postCreate");
|
||||
gameAndCharsCall("postCreate", null, "gamePostCreate");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -835,7 +876,7 @@ class PlayState extends MusicBeatState
|
||||
|
||||
inCutscene = true;
|
||||
var videoCutscene = Paths.video('$songName-${prefix}cutscene');
|
||||
var videoCutsceneAlt = Paths.file('songs/$songName/${prefix}cutscene.mp4');
|
||||
var videoCutsceneAlt = Paths.file('songs/$songName/${prefix}cutscene.${Flags.VIDEO_EXT}');
|
||||
var dialogue = Paths.file('songs/$songName/${prefix}dialogue.xml');
|
||||
persistentUpdate = true;
|
||||
var toCall:Void->Void = function() {
|
||||
@@ -868,7 +909,7 @@ class PlayState extends MusicBeatState
|
||||
_startCountdownCalled = true;
|
||||
inCutscene = false;
|
||||
|
||||
if (scripts.event("onStartCountdown", new CancellableEvent()).cancelled) return;
|
||||
if (gameAndCharsEvent("onStartCountdown", new CancellableEvent()).cancelled) return;
|
||||
}
|
||||
|
||||
startedCountdown = true;
|
||||
@@ -881,14 +922,14 @@ class PlayState extends MusicBeatState
|
||||
countdown(swagCounter++);
|
||||
}, introLength);
|
||||
}
|
||||
scripts.call("onPostStartCountdown");
|
||||
gameAndCharsCall("onPostStartCountdown");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fake countdown.
|
||||
*/
|
||||
public function countdown(swagCounter:Int) {
|
||||
var event:CountdownEvent = scripts.event("onCountdown", EventManager.get(CountdownEvent).recycle(
|
||||
var event:CountdownEvent = gameAndCharsEvent("onCountdown", EventManager.get(CountdownEvent).recycle(
|
||||
swagCounter,
|
||||
1,
|
||||
introSounds[swagCounter],
|
||||
@@ -931,12 +972,12 @@ class PlayState extends MusicBeatState
|
||||
event.spriteTween = tween;
|
||||
event.cancelled = false;
|
||||
|
||||
scripts.event("onPostCountdown", event);
|
||||
gameAndCharsEvent("onPostCountdown", event);
|
||||
}
|
||||
|
||||
@:dox(hide) function startSong():Void
|
||||
{
|
||||
scripts.call("onSongStart");
|
||||
gameAndCharsCall("onSongStart");
|
||||
startingSong = false;
|
||||
|
||||
inst.onComplete = endSong;
|
||||
@@ -963,7 +1004,7 @@ class PlayState extends MusicBeatState
|
||||
|
||||
updateDiscordPresence();
|
||||
|
||||
scripts.call("onStartSong");
|
||||
gameAndCharsCall("onStartSong");
|
||||
}
|
||||
|
||||
public override function destroy() {
|
||||
@@ -1010,11 +1051,15 @@ class PlayState extends MusicBeatState
|
||||
return FlxSort.byValues(FlxSort.DESCENDING, p1.time, p2.time);
|
||||
});
|
||||
|
||||
camZoomingInterval = cast songData.meta.beatsPerMeasure.getDefault(4);
|
||||
var beatsPerMeasure:Float = songData.meta.beatsPerMeasure.getDefault(Flags.DEFAULT_BEATS_PER_MEASURE);
|
||||
var stepsPerBeat:Int = songData.meta.stepsPerBeat.getDefault(Flags.DEFAULT_STEPS_PER_BEAT);
|
||||
|
||||
Conductor.changeBPM(songData.meta.bpm, cast songData.meta.beatsPerMeasure.getDefault(4), cast songData.meta.stepsPerBeat.getDefault(4));
|
||||
camZoomingInterval = Std.int(beatsPerMeasure);
|
||||
|
||||
Conductor.changeBPM(songData.meta.bpm, beatsPerMeasure, stepsPerBeat);
|
||||
|
||||
curSong = songData.meta.name.toLowerCase();
|
||||
curSongID = curSong.replace(" ", "-");
|
||||
|
||||
inst = FlxG.sound.load(Paths.inst(SONG.meta.name, difficulty));
|
||||
if (SONG.meta.needsVoices != false && Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true
|
||||
@@ -1044,7 +1089,7 @@ class PlayState extends MusicBeatState
|
||||
@:dox(hide)
|
||||
override function openSubState(SubState:FlxSubState)
|
||||
{
|
||||
var event = scripts.event("onSubstateOpen", EventManager.get(StateEvent).recycle(SubState));
|
||||
var event = gameAndCharsEvent("onSubstateOpen", EventManager.get(StateEvent).recycle(SubState));
|
||||
|
||||
if (!postCreated)
|
||||
MusicBeatState.skipTransIn = true;
|
||||
@@ -1064,13 +1109,13 @@ class PlayState extends MusicBeatState
|
||||
startTimer.active = false;
|
||||
}
|
||||
|
||||
super.openSubState(SubState);
|
||||
super.openSubState(event.substate is FlxSubState ? cast event.substate : SubState);
|
||||
}
|
||||
|
||||
@:dox(hide)
|
||||
override function closeSubState()
|
||||
{
|
||||
var event = scripts.event("onSubstateClose", EventManager.get(StateEvent).recycle(subState));
|
||||
var event = gameAndCharsEvent("onSubstateClose", EventManager.get(StateEvent).recycle(subState));
|
||||
if (event.cancelled) return;
|
||||
|
||||
if (paused)
|
||||
@@ -1104,7 +1149,7 @@ class PlayState extends MusicBeatState
|
||||
inst.resume();
|
||||
vocals.resume();
|
||||
}
|
||||
scripts.call("onFocus");
|
||||
gameAndCharsCall("onFocus");
|
||||
updateDiscordPresence();
|
||||
super.onFocus();
|
||||
}
|
||||
@@ -1117,7 +1162,7 @@ class PlayState extends MusicBeatState
|
||||
inst.pause();
|
||||
vocals.pause();
|
||||
}
|
||||
scripts.call("onFocusLost");
|
||||
gameAndCharsCall("onFocusLost");
|
||||
updateDiscordPresence();
|
||||
super.onFocusLost();
|
||||
}
|
||||
@@ -1136,14 +1181,14 @@ class PlayState extends MusicBeatState
|
||||
strumLine.vocals.play();
|
||||
}
|
||||
vocals.play();
|
||||
scripts.call("onVocalsResync");
|
||||
gameAndCharsCall("onVocalsResync");
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the game.
|
||||
*/
|
||||
public function pauseGame() {
|
||||
var e = scripts.event("onGamePause", new CancellableEvent());
|
||||
var e = gameAndCharsEvent("onGamePause", new CancellableEvent());
|
||||
if (e.cancelled) return;
|
||||
|
||||
persistentUpdate = false;
|
||||
@@ -1151,7 +1196,7 @@ class PlayState extends MusicBeatState
|
||||
paused = true;
|
||||
|
||||
// 1 / 1000 chance for Gitaroo Man easter egg
|
||||
if (allowGitaroo && FlxG.random.bool(0.1))
|
||||
if (allowGitaroo && FlxG.random.bool(Flags.GITAROO_CHANCE))
|
||||
{
|
||||
// gitaroo man easter egg
|
||||
FlxG.switchState(new GitarooPause());
|
||||
@@ -1208,7 +1253,7 @@ class PlayState extends MusicBeatState
|
||||
}
|
||||
|
||||
function updateIconPositions() {
|
||||
var iconOffset:Int = 26;
|
||||
var iconOffset = Flags.ICON_OFFSET;
|
||||
|
||||
var center:Float = healthBar.x + healthBar.width * FlxMath.remapToRange(healthBar.percent, 0, 100, 1, 0);
|
||||
|
||||
@@ -1267,8 +1312,9 @@ class PlayState extends MusicBeatState
|
||||
}
|
||||
|
||||
if (doIconBop) {
|
||||
iconP1.scale.set(lerp(iconP1.scale.x, 1, 0.33), lerp(iconP1.scale.y, 1, 0.33));
|
||||
iconP2.scale.set(lerp(iconP2.scale.x, 1, 0.33), lerp(iconP2.scale.y, 1, 0.33));
|
||||
var iconLerp = Flags.ICON_LERP;
|
||||
iconP1.scale.set(lerp(iconP1.scale.x, 1, iconLerp), lerp(iconP1.scale.y, 1, iconLerp));
|
||||
iconP2.scale.set(lerp(iconP2.scale.x, 1, iconLerp), lerp(iconP2.scale.y, 1, iconLerp));
|
||||
|
||||
iconP1.updateHitbox();
|
||||
iconP2.updateHitbox();
|
||||
@@ -1287,7 +1333,7 @@ class PlayState extends MusicBeatState
|
||||
var instTime = FlxG.sound.music.time;
|
||||
var isOffsync = vocals.time != instTime || [for(strumLine in strumLines.members) strumLine.vocals.time != instTime].contains(true);
|
||||
__vocalOffsetViolation = Math.max(0, __vocalOffsetViolation + (isOffsync ? elapsed : -elapsed / 2));
|
||||
if (__vocalOffsetViolation > 25) {
|
||||
if (__vocalOffsetViolation > Flags.VOCAL_OFFSET_VIOLATION_THRESHOLD) {
|
||||
resyncVocals();
|
||||
__vocalOffsetViolation = 0;
|
||||
}
|
||||
@@ -1312,7 +1358,7 @@ class PlayState extends MusicBeatState
|
||||
pos.x /= r;
|
||||
pos.y /= r;
|
||||
|
||||
var event = scripts.event("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r));
|
||||
var event = gameAndCharsEvent("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r));
|
||||
if (!event.cancelled)
|
||||
camFollow.setPosition(pos.x, pos.y);
|
||||
}
|
||||
@@ -1357,10 +1403,12 @@ class PlayState extends MusicBeatState
|
||||
public var scrollSpeedTween:FlxTween = null;
|
||||
|
||||
public function executeEvent(event:ChartEvent) @:privateAccess {
|
||||
if (event == null) return;
|
||||
if (event.params == null) return;
|
||||
if (event == null || event.params == null) return;
|
||||
|
||||
if (scripts.event("onEvent", EventManager.get(EventGameEvent).recycle(event)).cancelled) return;
|
||||
var e = EventManager.get(EventGameEvent).recycle(event);
|
||||
gameAndCharsEvent("onEvent", e);
|
||||
if (e.cancelled) return;
|
||||
var event = e.event;
|
||||
|
||||
switch(event.name) {
|
||||
case "HScript Call":
|
||||
@@ -1420,11 +1468,11 @@ class PlayState extends MusicBeatState
|
||||
*/
|
||||
public function gameOver(?character:Character, ?deathCharID:String, ?gameOverSong:String, ?lossSFX:String, ?retrySFX:String) {
|
||||
var charToUse:Character = character.getDefault(opponentMode ? dad : boyfriend); // Imma still make it check null later just in case dad or bf are also null for some weird scripts - Nex
|
||||
var event:GameOverEvent = scripts.event("onGameOver", EventManager.get(GameOverEvent).recycle(
|
||||
var event:GameOverEvent = gameAndCharsEvent("onGameOver", EventManager.get(GameOverEvent).recycle(
|
||||
charToUse == null ? 0 : charToUse.x,
|
||||
charToUse == null ? 0 : charToUse.y,
|
||||
charToUse,
|
||||
deathCharID.getDefault(charToUse != null ? charToUse.gameOverCharacter : Character.FALLBACK_DEAD_CHARACTER),
|
||||
deathCharID.getDefault(charToUse != null ? charToUse.gameOverCharacter : Flags.DEFAULT_GAMEOVER_CHARACTER),
|
||||
charToUse != null ? charToUse.isPlayer : true,
|
||||
gameOverSong.getDefault(this.gameOverSong),
|
||||
lossSFX.getDefault(this.lossSFX),
|
||||
@@ -1449,7 +1497,7 @@ class PlayState extends MusicBeatState
|
||||
|
||||
openSubState(new GameOverSubstate(event.x, event.y, event.deathCharID, event.isPlayer, event.gameOverSong, event.lossSFX, event.retrySFX));
|
||||
|
||||
scripts.event("onPostGameOver", event);
|
||||
gameAndCharsEvent("onPostGameOver", event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1457,7 +1505,7 @@ class PlayState extends MusicBeatState
|
||||
*/
|
||||
public function endSong():Void
|
||||
{
|
||||
scripts.call("onSongEnd");
|
||||
gameAndCharsCall("onSongEnd");
|
||||
canPause = false;
|
||||
inst.volume = 0;
|
||||
vocals.volume = 0;
|
||||
@@ -1578,7 +1626,7 @@ class PlayState extends MusicBeatState
|
||||
var directionID:Null<Int> = note == null ? direction : note.strumID;
|
||||
if (playerID == null || directionID == null || playerID == -1) return;
|
||||
|
||||
var event:NoteMissEvent = scripts.event("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0));
|
||||
var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0));
|
||||
strumLine.onMiss.dispatch(event);
|
||||
if (event.cancelled) return;
|
||||
|
||||
@@ -1662,11 +1710,13 @@ class PlayState extends MusicBeatState
|
||||
|
||||
var event:NoteHitEvent;
|
||||
if (strumLine != null && !strumLine.cpu)
|
||||
event = scripts.event("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick"));
|
||||
event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick");
|
||||
else
|
||||
event = scripts.event("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false));
|
||||
event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false);
|
||||
event.deleteNote = !note.isSustainNote; // work around, to allow sustain notes to be deleted
|
||||
event = scripts.event(strumLine != null && !strumLine.cpu ? "onPlayerHit" : "onDadHit", event);
|
||||
strumLine.onHit.dispatch(event);
|
||||
scripts.event("onNoteHit", event);
|
||||
gameAndCharsEvent("onNoteHit", event);
|
||||
|
||||
if (!event.cancelled) {
|
||||
if (!note.isSustainNote) {
|
||||
@@ -1681,7 +1731,8 @@ class PlayState extends MusicBeatState
|
||||
if (event.showRating || (event.showRating == null && event.player))
|
||||
{
|
||||
displayCombo(event);
|
||||
displayRating(event.rating, event);
|
||||
if (event.displayRating)
|
||||
displayRating(event.rating, event);
|
||||
ratingNum += 1;
|
||||
}
|
||||
}
|
||||
@@ -1711,7 +1762,7 @@ class PlayState extends MusicBeatState
|
||||
}
|
||||
}
|
||||
|
||||
if (event.deleteNote && !note.isSustainNote) strumLine.deleteNote(note);
|
||||
if (event.deleteNote) strumLine.deleteNote(note);
|
||||
}
|
||||
|
||||
public function displayRating(myRating:String, ?evt:NoteHitEvent = null):Void {
|
||||
@@ -1740,14 +1791,12 @@ class PlayState extends MusicBeatState
|
||||
}
|
||||
|
||||
public function displayCombo(?evt:NoteHitEvent = null):Void {
|
||||
var hasEvent = evt != null;
|
||||
var pre:String = hasEvent ? evt.ratingPrefix : "";
|
||||
var suf:String = hasEvent ? evt.ratingSuffix : "";
|
||||
if (minDigitDisplay >= 0 && (combo == 0 || combo >= minDigitDisplay)) {
|
||||
var hasEvent = evt != null;
|
||||
var pre:String = hasEvent ? evt.ratingPrefix : "";
|
||||
var suf:String = hasEvent ? evt.ratingSuffix : "";
|
||||
|
||||
var separatedScore:String = Std.string(combo).addZeros(3);
|
||||
|
||||
if (combo == 0 || combo >= 10) {
|
||||
if (combo >= 10) {
|
||||
if (evt.displayCombo) {
|
||||
var comboSpr:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}combo${suf}'));
|
||||
comboSpr.resetSprite(comboGroup.x, comboGroup.y);
|
||||
comboSpr.acceleration.y = 600;
|
||||
@@ -1769,6 +1818,7 @@ class PlayState extends MusicBeatState
|
||||
});
|
||||
}
|
||||
|
||||
var separatedScore:String = Std.string(combo).addZeros(3);
|
||||
for (i in 0...separatedScore.length)
|
||||
{
|
||||
var numScore:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}num${separatedScore.charAt(i)}${suf}'));
|
||||
@@ -1820,14 +1870,15 @@ class PlayState extends MusicBeatState
|
||||
if (camZoomingInterval < 1) camZoomingInterval = 1;
|
||||
if (Options.camZoomOnBeat && camZooming && FlxG.camera.zoom < maxCamZoom && curBeat % camZoomingInterval == 0)
|
||||
{
|
||||
FlxG.camera.zoom += 0.015 * camZoomingStrength;
|
||||
camHUD.zoom += 0.03 * camZoomingStrength;
|
||||
FlxG.camera.zoom += Flags.CAM_BOP_STRENGTH * camZoomingStrength;
|
||||
camHUD.zoom += Flags.HUD_BOP_STRENGTH * camZoomingStrength;
|
||||
}
|
||||
|
||||
if (doIconBop)
|
||||
{
|
||||
iconP1.scale.set(1.2, 1.2);
|
||||
iconP2.scale.set(1.2, 1.2);
|
||||
var iconScale = Flags.BOP_ICON_SCALE;
|
||||
iconP1.scale.set(iconScale, iconScale);
|
||||
iconP2.scale.set(iconScale, iconScale);
|
||||
|
||||
iconP1.updateHitbox();
|
||||
iconP2.updateHitbox();
|
||||
@@ -1915,7 +1966,8 @@ class PlayState extends MusicBeatState
|
||||
* @param weekData Week Data
|
||||
* @param difficulty Week Difficulty
|
||||
*/
|
||||
public static function loadWeek(weekData:WeekData, difficulty:String = "normal") {
|
||||
public static function loadWeek(weekData:WeekData, ?difficulty:String) {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
storyWeek = weekData;
|
||||
storyPlaylist = [for(e in weekData.songs) e.name];
|
||||
isStoryMode = true;
|
||||
@@ -1935,7 +1987,8 @@ class PlayState extends MusicBeatState
|
||||
* @param opponentMode Whenever opponent mode is on
|
||||
* @param coopMode Whenever co-op mode is on.
|
||||
*/
|
||||
public static function loadSong(_name:String, _difficulty:String = "normal", _opponentMode:Bool = false, _coopMode:Bool = false) {
|
||||
public static function loadSong(_name:String, ?_difficulty:String, _opponentMode:Bool = false, _coopMode:Bool = false) {
|
||||
if (_difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
isStoryMode = false;
|
||||
opponentMode = _opponentMode;
|
||||
chartingMode = false;
|
||||
|
||||
@@ -43,7 +43,7 @@ class SplashGroup extends FlxTypedGroup<FunkinSprite> {
|
||||
Logs.trace('Couldn\'t parse splash data for "${path}": ${e.toString()}', ERROR);
|
||||
valid = false;
|
||||
}
|
||||
maxSize = 8;
|
||||
maxSize = Flags.MAX_SPLASHES;
|
||||
}
|
||||
|
||||
var _scale:Float = 1.0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package funkin.game;
|
||||
|
||||
import funkin.backend.scripting.events.SplashShowEvent;
|
||||
import funkin.backend.scripting.events.splash.*;
|
||||
|
||||
class SplashHandler extends FlxTypedGroup<FunkinSprite> {
|
||||
/**
|
||||
@@ -42,13 +42,15 @@ class SplashHandler extends FlxTypedGroup<FunkinSprite> {
|
||||
var __grp:SplashGroup;
|
||||
public function showSplash(name:String, strum:Strum) {
|
||||
__grp = getSplashGroup(name);
|
||||
|
||||
var event = EventManager.get(SplashShowEvent).recycle(name, __grp.showOnStrum(strum), strum, __grp);
|
||||
event = PlayState.instance.scripts.event("onSplashShown", event);
|
||||
event = PlayState.instance.gameAndCharsEvent("onSplashShown", event);
|
||||
|
||||
if (!event.cancelled)
|
||||
add(event.splash);
|
||||
|
||||
// max 8 rendered splashes
|
||||
while(members.length > 8)
|
||||
while(members.length > Flags.MAX_SPLASHES)
|
||||
remove(members[0], true);
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,7 @@ 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;
|
||||
import funkin.backend.scripting.events.StageXMLEvent;
|
||||
import funkin.backend.scripting.events.stage.*;
|
||||
import funkin.backend.system.interfaces.IBeatReceiver;
|
||||
import haxe.io.Path;
|
||||
import haxe.xml.Access;
|
||||
@@ -106,8 +105,8 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
}
|
||||
|
||||
if (PlayState.instance == state) {
|
||||
event = EventManager.get(StageXMLEvent).recycle(this, xml, elems);
|
||||
elems = PlayState.instance.scripts.event("onStageXMLParsed", event).elems;
|
||||
event = EventManager.get(StageXMLEvent).recycle(this, stageXML, elems);
|
||||
elems = PlayState.instance.gameAndCharsEvent("onStageXMLParsed", event).elems;
|
||||
}
|
||||
if(onXMLLoaded != null) {
|
||||
elems = onXMLLoaded(xml, elems);
|
||||
@@ -163,7 +162,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
}
|
||||
|
||||
if(PlayState.instance == state) {
|
||||
sprite = PlayState.instance.scripts.event("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite;
|
||||
sprite = PlayState.instance.gameAndCharsEvent("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite;
|
||||
}
|
||||
if(onNodeLoaded != null) {
|
||||
sprite = onNodeLoaded(node, sprite);
|
||||
@@ -213,7 +212,6 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
if(onXMLPostLoaded != null) {
|
||||
elems = onXMLPostLoaded(xml, elems);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function getDefaultPos(name:String):StageCharPosInfo {
|
||||
|
||||
@@ -5,7 +5,7 @@ import flixel.sound.FlxSound;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import funkin.backend.scripting.events.*;
|
||||
import funkin.backend.scripting.events.note.*;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.backend.system.Controls;
|
||||
|
||||
@@ -153,9 +153,11 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
if(scrollSpeed == null) if (PlayState.instance != null) scrollSpeed = PlayState.instance.scrollSpeed;
|
||||
if(scrollSpeed == null) scrollSpeed = 1;
|
||||
|
||||
// TODOD: Make this work by accounting zoom and scrollspeed changes - Nex
|
||||
// TODO: Make this work by accounting zoom and scrollspeed changes - Nex
|
||||
/*var limit = FlxG.height / 0.45;
|
||||
notes.limit = limit / scrollSpeed;*/
|
||||
notes.limit = limit / scrollSpeed;
|
||||
OR
|
||||
notes.limit = Flags.DEFAULT_NOTE_MS_LIMIT / scrollSpeed;*/
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
@@ -250,7 +252,8 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
__justReleased.push(s.__getJustReleased(this));
|
||||
}
|
||||
|
||||
var event = PlayState.instance.scripts.event("onInputUpdate", EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id));
|
||||
var event = EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id);
|
||||
if (PlayState.instance != null) PlayState.instance.gameAndCharsEvent("onInputUpdate", event);
|
||||
if (event.cancelled) return;
|
||||
|
||||
__pressed = CoolUtil.getDefault(event.pressed, []);
|
||||
@@ -285,13 +288,14 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
forEach(function(str:Strum) {
|
||||
str.updatePlayerInput(str.__getPressed(this), str.__getJustPressed(this), str.__getJustReleased(this));
|
||||
});
|
||||
PlayState.instance.scripts.call("onPostInputUpdate");
|
||||
PlayState.instance.gameAndCharsCall("onPostInputUpdate");
|
||||
}
|
||||
|
||||
public inline function addHealth(health:Float)
|
||||
PlayState.instance.health += health * (opponentSide ? -1 : 1);
|
||||
|
||||
public inline function generateStrums(amount:Int = 4) {
|
||||
public inline function generateStrums(amount:Int = -1) {
|
||||
if(amount == -1) amount = Flags.DEFAULT_STRUM_AMOUNT;
|
||||
for (i in 0...amount)
|
||||
add(createStrum(i));
|
||||
}
|
||||
@@ -319,7 +323,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
|
||||
var event = EventManager.get(StrumCreationEvent).recycle(babyArrow, PlayState.instance.strumLines.members.indexOf(this), i, animPrefix);
|
||||
event.__doAnimation = !MusicBeatState.skipTransIn && (PlayState.instance != null ? PlayState.instance.introLength > 0 : true);
|
||||
event = PlayState.instance.scripts.event("onStrumCreation", event);
|
||||
if (PlayState.instance != null) event = PlayState.instance.gameAndCharsEvent("onStrumCreation", event);
|
||||
|
||||
if (!event.cancelled) {
|
||||
babyArrow.frames = Paths.getFrames(event.sprite);
|
||||
@@ -329,7 +333,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
babyArrow.animation.addByPrefix('red', 'arrowRIGHT');
|
||||
|
||||
babyArrow.antialiasing = true;
|
||||
babyArrow.setGraphicSize(Std.int((babyArrow.width * 0.7) * strumScale));
|
||||
babyArrow.setGraphicSize(Std.int((babyArrow.width * Flags.DEFAULT_NOTE_SCALE) * strumScale));
|
||||
|
||||
babyArrow.animation.addByPrefix('static', 'arrow${event.animPrefix.toUpperCase()}');
|
||||
babyArrow.animation.addByPrefix('pressed', '${event.animPrefix} press', 24, false);
|
||||
@@ -350,7 +354,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
|
||||
insert(i, babyArrow);
|
||||
|
||||
PlayState.instance.scripts.event("onPostStrumCreation", event);
|
||||
if (PlayState.instance != null) PlayState.instance.gameAndCharsEvent("onPostStrumCreation", event);
|
||||
|
||||
return babyArrow;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,77 @@
|
||||
package funkin.game.cutscenes;
|
||||
|
||||
import funkin.backend.scripting.events.NameEvent;
|
||||
import funkin.menus.PauseSubState;
|
||||
|
||||
/**
|
||||
* Substate made for cutscenes.
|
||||
*/
|
||||
class Cutscene extends MusicBeatSubstate {
|
||||
var __callback:Void->Void;
|
||||
var __pausable:Bool;
|
||||
var game:PlayState = PlayState.instance;
|
||||
|
||||
public function new(callback:Void->Void) {
|
||||
super();
|
||||
__callback = callback;
|
||||
public var paused:Bool = false;
|
||||
public var pauseItems:Array<String>;
|
||||
public var skippable(default, set):Bool;
|
||||
|
||||
public function set_skippable(val:Bool):Bool {
|
||||
if(!val) pauseItems.remove('Skip Cutscene');
|
||||
else if(!pauseItems.contains('Skip Cutscene')) pauseItems.insert(1, 'Skip Cutscene');
|
||||
return skippable = val;
|
||||
}
|
||||
|
||||
public function new(callback:Void->Void, allowPause:Bool = true, canSkip:Bool = true) {
|
||||
super();
|
||||
canOpenCustomTransition = true;
|
||||
|
||||
__callback = callback;
|
||||
__pausable = allowPause;
|
||||
|
||||
pauseItems = Flags.DEFAULT_CUTSCENE_PAUSE_ITEMS;
|
||||
skippable = canSkip;
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
if (pauseCheck()) pauseCutscene();
|
||||
}
|
||||
|
||||
public function pauseCheck():Bool
|
||||
return controls.PAUSE && __pausable;
|
||||
|
||||
var _before:Array<Bool> = [false, true];
|
||||
public function pauseCutscene() {
|
||||
_before = [game.persistentUpdate, game.persistentDraw];
|
||||
game.persistentUpdate = persistentUpdate = false;
|
||||
game.persistentDraw = persistentDraw = true;
|
||||
paused = true;
|
||||
|
||||
openSubState(new PauseSubState(pauseItems, selectPauseOption));
|
||||
}
|
||||
|
||||
public override function closeSubState() {
|
||||
if (paused) {
|
||||
game.persistentUpdate = _before[0];
|
||||
game.persistentDraw = _before[1];
|
||||
paused = false;
|
||||
}
|
||||
|
||||
super.closeSubState();
|
||||
}
|
||||
|
||||
public function onSkipCutscene(event:NameEvent) close();
|
||||
public function onRestartCutscene(event:NameEvent) event.name = "Restart Song"; // Making the whole PlayState restart just for precaution - Nex
|
||||
public function onResumeCutscene(event:NameEvent) event.name = "Resume";
|
||||
|
||||
public function selectPauseOption(event:NameEvent) {
|
||||
switch(event.name) {
|
||||
case 'Skip Cutscene': onSkipCutscene(event);
|
||||
case 'Restart Cutscene': onRestartCutscene(event);
|
||||
case 'Resume Cutscene': onResumeCutscene(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override function close() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import haxe.xml.Access;
|
||||
/**
|
||||
* Substate made for dialogue cutscenes. To use it in a scripted cutscene, call `startDialogue`.
|
||||
*/
|
||||
class DialogueCutscene extends Cutscene {
|
||||
class DialogueCutscene extends ScriptedCutscene {
|
||||
public var dialoguePath:String;
|
||||
public var dialogueData:Access;
|
||||
|
||||
@@ -28,23 +28,29 @@ class DialogueCutscene extends Cutscene {
|
||||
public var curMusic:FlxSound = null;
|
||||
|
||||
public static var cutscene:DialogueCutscene;
|
||||
public var dialogueScript:Script;
|
||||
public var dialogueScript(get, set):Script;
|
||||
|
||||
public function set_curLine(val:DialogueLine) {
|
||||
lastLine = curLine;
|
||||
return curLine = val;
|
||||
}
|
||||
|
||||
// Backwards comapt funcs - Nex
|
||||
public function set_dialogueScript(val:Script) return script = val;
|
||||
public function get_dialogueScript() return script;
|
||||
|
||||
public function new(dialoguePath:String, callback:Void->Void) {
|
||||
super(callback);
|
||||
this.dialoguePath = dialoguePath;
|
||||
super(this.dialoguePath = dialoguePath, callback);
|
||||
|
||||
camera = dialogueCamera = new FlxCamera();
|
||||
dialogueCamera.bgColor = 0;
|
||||
FlxG.cameras.add(dialogueCamera, false);
|
||||
|
||||
dialogueScript = Script.create(Paths.script(Path.withoutExtension(dialoguePath), null, dialoguePath.startsWith('assets')));
|
||||
dialogueScript.setParent(cutscene = this);
|
||||
dialogueScript.load();
|
||||
cutscene = this;
|
||||
}
|
||||
|
||||
public override function onErrorScriptLoading() {
|
||||
Logs.trace('Could not find script for dialogue cutscene at "${scriptPath}"', WARNING, YELLOW);
|
||||
}
|
||||
|
||||
var parentDisabler:FunkinParentDisabler;
|
||||
@@ -89,7 +95,7 @@ class DialogueCutscene extends Cutscene {
|
||||
musicVolume: node.has.musicVolume ? (volume = Std.parseFloat(node.att.speed).getDefault(0.8)) : null,
|
||||
changeMusic: node.has.changeMusic ? FlxG.sound.load(Paths.music(node.att.changeMusic), volume, true) : null,
|
||||
playSound: node.has.playSound ? FlxG.sound.load(Paths.sound(node.att.playSound)) : null,
|
||||
nextSound: node.has.nextSound ? FlxG.sound.load(Paths.music(node.att.nextSound)) : null,
|
||||
nextSound: node.has.nextSound ? FlxG.sound.load(Paths.sound(node.att.nextSound)) : null,
|
||||
textSound: null
|
||||
};
|
||||
|
||||
@@ -110,22 +116,21 @@ class DialogueCutscene extends Cutscene {
|
||||
|
||||
next(true);
|
||||
} catch(e) {
|
||||
Logs.trace('Error while loading dialogue at ${dialoguePath}: ${e.toString()}', ERROR);
|
||||
trace(CoolUtil.getLastExceptionStack());
|
||||
var message:String = e.toString();
|
||||
Logs.trace('Error while loading dialogue at ${dialoguePath}: $message', ERROR, RED);
|
||||
dialogueScript.call("loadingError", [message]);
|
||||
close();
|
||||
}
|
||||
|
||||
dialogueScript.call("postCreate");
|
||||
}
|
||||
|
||||
public override function beatHit(curBeat:Int) {
|
||||
super.beatHit(curBeat);
|
||||
dialogueScript.call("beatHit", [curBeat]);
|
||||
public override function pauseCheck():Bool {
|
||||
return super.pauseCheck() && !controls.ACCEPT; // Avoids next() and pause() being called at the same time - Nex
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
dialogueScript.call("update", [elapsed]);
|
||||
|
||||
if(controls.ACCEPT) {
|
||||
if(dialogueBox.dialogueEnded) next();
|
||||
else dialogueBox.text.skip();
|
||||
@@ -138,8 +143,8 @@ class DialogueCutscene extends Cutscene {
|
||||
public var canProceed:Bool = true;
|
||||
|
||||
public function next(playFirst:Bool = false) {
|
||||
var event = EventManager.get(DynamicEvent).recycle(playFirst);
|
||||
dialogueScript.call("next", [playFirst]);
|
||||
var event = EventManager.get(DialogueNextLineEvent).recycle(playFirst);
|
||||
dialogueScript.call("next", [event]);
|
||||
if(event.cancelled || !canProceed) return;
|
||||
|
||||
if ((curLine = dialogueLines.shift()) == null) {
|
||||
@@ -154,7 +159,7 @@ class DialogueCutscene extends Cutscene {
|
||||
}
|
||||
|
||||
if (curLine.callback != null)
|
||||
dialogueScript.call(curLine.callback, [playFirst]);
|
||||
dialogueScript.call(curLine.callback, [event.playFirst]);
|
||||
|
||||
for (k=>c in charMap)
|
||||
if (k != curLine.char)
|
||||
@@ -167,8 +172,8 @@ class DialogueCutscene extends Cutscene {
|
||||
dialogueBox.popupChar(char, force);
|
||||
}
|
||||
|
||||
var finalSuffix:String = playFirst && dialogueBox.hasAnimation(curLine.bubble + "-firstOpen") ? "-firstOpen" : dialogueBox.hasAnimation(curLine.bubble + "-open") ? "-open" : "";
|
||||
dialogueBox.playBubbleAnim(curLine.bubble, finalSuffix, curLine.text, curLine.format, curLine.speed, curLine.nextSound, curLine.textSound != null ? [curLine.textSound] : null, finalSuffix == "-firstOpen" || finalSuffix == "-open", !playFirst);
|
||||
var finalSuffix:String = event.playFirst && dialogueBox.hasAnimation(curLine.bubble + "-firstOpen") ? "-firstOpen" : dialogueBox.hasAnimation(curLine.bubble + "-open") ? "-open" : "";
|
||||
dialogueBox.playBubbleAnim(curLine.bubble, finalSuffix, curLine.text, curLine.format, curLine.speed, curLine.nextSound, curLine.textSound != null ? [curLine.textSound] : null, finalSuffix == "-firstOpen" || finalSuffix == "-open", !event.playFirst);
|
||||
|
||||
if(curLine.playSound != null) curLine.playSound.play();
|
||||
if(curLine.changeMusic != null) {
|
||||
@@ -178,7 +183,7 @@ class DialogueCutscene extends Cutscene {
|
||||
curMusic.fadeIn(1, 0, curMusic.volume);
|
||||
} else if(curLine.musicVolume != null && curMusic != null) curMusic.volume = curLine.musicVolume;
|
||||
|
||||
dialogueScript.call("postNext", [playFirst]);
|
||||
dialogueScript.call("postNext", [event]);
|
||||
}
|
||||
|
||||
public override function close() {
|
||||
@@ -193,8 +198,6 @@ class DialogueCutscene extends Cutscene {
|
||||
|
||||
public override function destroy() {
|
||||
if(curMusic != null && !curMusic.persist) curMusic.destroy();
|
||||
dialogueScript.call("destroy");
|
||||
dialogueScript.destroy();
|
||||
|
||||
super.destroy();
|
||||
cutscene = null;
|
||||
@@ -215,4 +218,4 @@ typedef DialogueLine = {
|
||||
var nextSound:FlxSound;
|
||||
var textSound:FlxSound;
|
||||
var changeDefAnim:String;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
package funkin.game.cutscenes;
|
||||
|
||||
import haxe.io.Path;
|
||||
import flixel.FlxSubState;
|
||||
import funkin.backend.scripting.DummyScript;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.backend.scripting.events.CancellableEvent;
|
||||
import funkin.backend.scripting.events.NameEvent;
|
||||
import funkin.backend.scripting.events.StateEvent;
|
||||
|
||||
/**
|
||||
* Substate made for scripted cutscenes.
|
||||
* To add cutscenes to your songs, add a `cutscene.hx` file in your song's directory (ex: `songs/song/cutscene.hx`)
|
||||
*/
|
||||
class ScriptedCutscene extends Cutscene {
|
||||
var scriptPath:String;
|
||||
var script:Script;
|
||||
|
||||
var scriptPath:String;
|
||||
public function new(scriptPath:String, callback:Void->Void) {
|
||||
super(callback);
|
||||
|
||||
script = Script.create(this.scriptPath = scriptPath);
|
||||
script = Script.create(this.scriptPath = Paths.script(Path.withoutExtension(scriptPath), null, scriptPath.startsWith('assets')));
|
||||
script.setPublicMap(PlayState.instance.scripts.publicVariables);
|
||||
script.setParent(this);
|
||||
script.load();
|
||||
@@ -22,32 +27,91 @@ class ScriptedCutscene extends Cutscene {
|
||||
|
||||
public override function create() {
|
||||
super.create();
|
||||
trace("fuck you");
|
||||
script.call("create");
|
||||
if (Std.isOfType(script, DummyScript)) {
|
||||
Logs.trace('Could not find script for scripted cutscene at ${scriptPath}', ERROR, RED);
|
||||
close();
|
||||
}
|
||||
|
||||
if(Std.isOfType(script, DummyScript))
|
||||
onErrorScriptLoading();
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
public function onErrorScriptLoading() {
|
||||
Logs.trace('Could not find script for scripted cutscene at "${scriptPath}"', ERROR, RED);
|
||||
close();
|
||||
}
|
||||
|
||||
public override function pauseCheck():Bool
|
||||
{
|
||||
var shouldClose = super.pauseCheck();
|
||||
script.call("pauseCheck", [shouldClose]);
|
||||
return shouldClose;
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float)
|
||||
{
|
||||
script.call("update", [elapsed]);
|
||||
super.update(elapsed);
|
||||
script.call("postUpdate", [elapsed]);
|
||||
}
|
||||
|
||||
public override function beatHit(curBeat:Int) {
|
||||
public override function pauseCutscene()
|
||||
{
|
||||
var event = new CancellableEvent();
|
||||
script.call("pauseCutscene", [event]);
|
||||
if(!event.cancelled) super.pauseCutscene();
|
||||
}
|
||||
|
||||
public override function onSkipCutscene(event:NameEvent)
|
||||
{
|
||||
script.call("onSkipCutscene", [event]);
|
||||
if(!event.cancelled) super.onSkipCutscene(event);
|
||||
}
|
||||
|
||||
public override function onRestartCutscene(event:NameEvent)
|
||||
{
|
||||
script.call("onRestartCutscene", [event]);
|
||||
if(!event.cancelled) super.onRestartCutscene(event);
|
||||
}
|
||||
|
||||
public override function onResumeCutscene(event:NameEvent)
|
||||
{
|
||||
script.call("onResumeCutscene", [event]);
|
||||
if(!event.cancelled) super.onResumeCutscene(event);
|
||||
}
|
||||
|
||||
public override function measureHit(curMeasure:Int)
|
||||
{
|
||||
super.measureHit(curMeasure);
|
||||
script.call("measureHit", [curMeasure]);
|
||||
}
|
||||
|
||||
public override function beatHit(curBeat:Int)
|
||||
{
|
||||
super.beatHit(curBeat);
|
||||
script.call("beatHit", [curBeat]);
|
||||
}
|
||||
|
||||
public override function stepHit(curStep:Int) {
|
||||
public override function stepHit(curStep:Int)
|
||||
{
|
||||
super.stepHit(curStep);
|
||||
script.call("stepHit", [curStep]);
|
||||
}
|
||||
|
||||
public override function openSubState(sub:FlxSubState)
|
||||
{
|
||||
var event = EventManager.get(StateEvent).recycle(sub);
|
||||
script.call("onSubstateClose", [event]);
|
||||
if(!event.cancelled) super.openSubState(event.substate is FlxSubState ? cast event.substate : sub);
|
||||
}
|
||||
|
||||
public override function closeSubState()
|
||||
{
|
||||
var event = EventManager.get(StateEvent).recycle(subState);
|
||||
script.call("onSubstateOpen", [event]);
|
||||
if(!event.cancelled) super.closeSubState();
|
||||
}
|
||||
|
||||
public override function destroy() {
|
||||
script.call("destroy");
|
||||
script.destroy();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.game.cutscenes;
|
||||
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.addons.display.FlxBackdrop;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
@@ -12,7 +13,7 @@ import haxe.xml.Access;
|
||||
import sys.io.File;
|
||||
#end
|
||||
#if VIDEO_CUTSCENES
|
||||
import hxvlc.flixel.FlxVideo;
|
||||
import hxvlc.flixel.FlxVideoSprite;
|
||||
#end
|
||||
|
||||
/**
|
||||
@@ -24,22 +25,24 @@ class VideoCutscene extends Cutscene {
|
||||
var localPath:String;
|
||||
|
||||
#if VIDEO_CUTSCENES
|
||||
var video:FlxVideo;
|
||||
public var skippable:Bool = true;
|
||||
#end
|
||||
var video:FlxVideoSprite;
|
||||
final mutex = new sys.thread.Mutex();
|
||||
|
||||
var cutsceneCamera:FlxCamera;
|
||||
|
||||
var text:FunkinText;
|
||||
var loadingBackdrop:FlxBackdrop;
|
||||
var videoReady:Bool = false;
|
||||
private var __loaded:Bool = false;
|
||||
|
||||
var bg:FlxSprite;
|
||||
var subtitle:FunkinText;
|
||||
|
||||
public var subtitles:Array<CutsceneSubtitle> = [];
|
||||
var curSubtitle:Int = 0;
|
||||
#end
|
||||
|
||||
public function new(path:String, callback:Void->Void) {
|
||||
super(callback);
|
||||
super(callback, false);
|
||||
localPath = Assets.getPath(this.path = path);
|
||||
}
|
||||
|
||||
@@ -48,21 +51,17 @@ class VideoCutscene extends Cutscene {
|
||||
|
||||
// TODO: get vlc to stop autoloading those goddamn subtitles (use different file ext??)
|
||||
|
||||
#if VIDEO_CUTSCENES
|
||||
cutsceneCamera = new FlxCamera();
|
||||
cutsceneCamera.bgColor = 0;
|
||||
FlxG.cameras.add(cutsceneCamera, false);
|
||||
|
||||
#if VIDEO_CUTSCENES
|
||||
parseSubtitles();
|
||||
|
||||
video = new FlxVideo();
|
||||
video.onEndReached.add(function()
|
||||
{
|
||||
video.dispose();
|
||||
FlxG.removeChild(video);
|
||||
});
|
||||
video.onEndReached.add(close);
|
||||
FlxG.addChildBelowMouse(video);
|
||||
add(video = new FlxVideoSprite());
|
||||
video.antialiasing = true;
|
||||
video.autoPause = false; // Imma handle it better inside this class, mainly because of the pause menu - Nex
|
||||
video.bitmap.onEndReached.add(close);
|
||||
|
||||
//cover = new FlxSprite(0, FlxG.height * 0.85).makeSolid(FlxG.width + 50, FlxG.height + 50, 0xFF000000);
|
||||
//cover.scrollFactor.set(0, 0);
|
||||
@@ -77,41 +76,44 @@ class VideoCutscene extends Cutscene {
|
||||
subtitle.alignment = CENTER;
|
||||
subtitle.visible = false;
|
||||
|
||||
if (localPath.startsWith("[ZIP]")) {
|
||||
text = new FunkinText(10, 10, Std.int(FlxG.width / 2), "Loading video...");
|
||||
text.cameras = [cutsceneCamera];
|
||||
text.visible = false;
|
||||
@:privateAccess
|
||||
text.regenGraphic();
|
||||
add(text);
|
||||
text = new FunkinText(10, 10, Std.int(FlxG.width / 2), "Loading video...");
|
||||
text.cameras = [cutsceneCamera];
|
||||
text.visible = false;
|
||||
@:privateAccess
|
||||
text.regenGraphic();
|
||||
add(text);
|
||||
|
||||
loadingBackdrop = new FlxBackdrop(text.graphic, X);
|
||||
loadingBackdrop.y = FlxG.height - 20 - loadingBackdrop.height;
|
||||
loadingBackdrop.cameras = [cutsceneCamera];
|
||||
add(loadingBackdrop);
|
||||
loadingBackdrop = new FlxBackdrop(text.graphic, X);
|
||||
loadingBackdrop.y = FlxG.height - 20 - loadingBackdrop.height;
|
||||
loadingBackdrop.velocity.x = 70;
|
||||
loadingBackdrop.cameras = [cutsceneCamera];
|
||||
add(loadingBackdrop);
|
||||
|
||||
// ZIP PATH: EXPORT
|
||||
// TODO: this but better and more ram friendly
|
||||
localPath = './.temp/video-${curVideo++}.mp4';
|
||||
Main.execAsync(function() {
|
||||
loadingBackdrop.alpha = 0;
|
||||
FlxTween.tween(loadingBackdrop, {alpha: 1}, 0.5, {ease: FlxEase.sineInOut});
|
||||
|
||||
Main.execAsync(function() {
|
||||
if (localPath.startsWith("[ZIP]")) {
|
||||
// ZIP PATH: EXPORT
|
||||
// TODO: this but better and more ram friendly
|
||||
localPath = './.temp/video-${curVideo++}.mp4';
|
||||
File.saveBytes(localPath, Assets.getBytes(path));
|
||||
videoReady = true;
|
||||
});
|
||||
} else {
|
||||
if (video.load(localPath))
|
||||
new FlxTimer().start(0.001, function(tmr:FlxTimer) {
|
||||
video.play();
|
||||
});
|
||||
else
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
if(video.load(localPath)) new FlxTimer().start(0.001, function(_) {mutex.acquire(); onReady(); mutex.release();});
|
||||
else { mutex.acquire(); close(); mutex.release(); }
|
||||
});
|
||||
|
||||
add(bg);
|
||||
add(subtitle);
|
||||
#end
|
||||
|
||||
cameras = [cutsceneCamera];
|
||||
#else
|
||||
close();
|
||||
#end
|
||||
}
|
||||
|
||||
#if VIDEO_CUTSCENES
|
||||
public function parseSubtitles() {
|
||||
var subtitlesPath = '${Path.withoutExtension(path)}.srt';
|
||||
|
||||
@@ -141,27 +143,23 @@ class VideoCutscene extends Cutscene {
|
||||
}
|
||||
if (subtitleText.length <= 0) continue; // empty subtitle, skipping
|
||||
var lastSub = subtitles.last();
|
||||
if (lastSub != null && lastSub.subtitle == "" && lastSub.time > beginTime)
|
||||
if (lastSub != null && lastSub.subtitle == "" && lastSub.time >= beginTime)
|
||||
subtitles.pop(); // remove last subtitle auto reset to prevent bugs
|
||||
subtitles.push({
|
||||
subtitle: subtitleText.join("."),
|
||||
time: beginTime * 1000,
|
||||
color: 0xFFFFFFFF // todo
|
||||
subtitle: subtitleText.join(""),
|
||||
time: beginTime * 1000
|
||||
});
|
||||
subtitles.push({
|
||||
subtitle: "",
|
||||
time: endTime * 1000,
|
||||
color: 0xFFFFFFFF
|
||||
time: endTime * 1000
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
trace(subtitles);
|
||||
}
|
||||
|
||||
public static function splitTime(str:String):Float {
|
||||
if (str == null || str.trim() == "") return -1;
|
||||
var multipliers:Array<Float> = [1, 60, 3600, 86400]; // no way a cutscene will last longer than days
|
||||
var multipliers:Array<Float> = [1, 60, 3600, 86400, 31536000]; // no way a cutscene will last longer than years
|
||||
var timeSplit:Array<Null<Float>> = [for(e in str.split(":")) Std.parseFloat(e.replace(",", "."))];
|
||||
var time:Float = 0;
|
||||
|
||||
@@ -173,50 +171,62 @@ class VideoCutscene extends Cutscene {
|
||||
return time;
|
||||
}
|
||||
|
||||
public inline function onReady() {
|
||||
FlxTween.cancelTweensOf(loadingBackdrop);
|
||||
FlxTween.tween(loadingBackdrop, {alpha: 0}, 0.7, {ease: FlxEase.sineInOut, onComplete: function(_) {
|
||||
loadingBackdrop.destroy();
|
||||
text.destroy();
|
||||
}});
|
||||
|
||||
__pausable = __loaded = true;
|
||||
video.play();
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
#if VIDEO_CUTSCENES
|
||||
if (videoReady) {
|
||||
videoReady = false;
|
||||
|
||||
if (video.load(localPath))
|
||||
new FlxTimer().start(0.001, function(tmr:FlxTimer) {
|
||||
video.play();
|
||||
});
|
||||
else
|
||||
{
|
||||
close();
|
||||
if(loadingBackdrop != null) return;
|
||||
|
||||
if (loadingBackdrop != null)
|
||||
loadingBackdrop.visible = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadingBackdrop != null)
|
||||
loadingBackdrop.visible = false;
|
||||
}
|
||||
@:privateAccess
|
||||
var time:Int64 = video.time;
|
||||
var time:Int64 = video.bitmap.time;
|
||||
var time:Float = FPHelper.i64ToDouble(time.low, time.high);
|
||||
|
||||
while (subtitles.length > 0 && subtitles[0].time < Math.round(FPHelper.i64ToDouble(time.low, time.high)))
|
||||
setSubtitle(subtitles.shift());
|
||||
if(curSubtitle < subtitles.length && subtitles[curSubtitle].time < time) {
|
||||
setSubtitle(subtitles[curSubtitle]);
|
||||
curSubtitle++;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadingBackdrop != null) {
|
||||
loadingBackdrop.x -= elapsed * FlxG.width * 0.5;
|
||||
}
|
||||
if (skippable && video.isPlaying && controls.ACCEPT) {
|
||||
video.onEndReached.dispatch();
|
||||
}
|
||||
#else
|
||||
close();
|
||||
#end
|
||||
@:dox(hide) override public function onFocus() {
|
||||
if(FlxG.autoPause && !paused) video.resume();
|
||||
super.onFocus();
|
||||
}
|
||||
|
||||
@:dox(hide) override public function onFocusLost() {
|
||||
if(FlxG.autoPause && !paused) video.pause();
|
||||
super.onFocusLost();
|
||||
}
|
||||
|
||||
public override function pauseCutscene() {
|
||||
video.pause();
|
||||
super.pauseCutscene();
|
||||
}
|
||||
|
||||
public override function onResumeCutscene(event) {
|
||||
video.resume();
|
||||
super.onResumeCutscene(event);
|
||||
}
|
||||
|
||||
public override function onRestartCutscene(event) {
|
||||
closeSubState();
|
||||
video.bitmap.position = curSubtitle = 0;
|
||||
setSubtitle({time: -1, subtitle: ""});
|
||||
video.resume();
|
||||
}
|
||||
|
||||
public function setSubtitle(sub:CutsceneSubtitle) {
|
||||
if (bg.visible = subtitle.visible = (sub.subtitle.length > 0)) {
|
||||
subtitle.text = sub.subtitle;
|
||||
subtitle.color = sub.color;
|
||||
subtitle.screenCenter(X);
|
||||
bg.scale.set(subtitle.width + 8, subtitle.height + 8);
|
||||
bg.updateHitbox();
|
||||
@@ -228,10 +238,10 @@ class VideoCutscene extends Cutscene {
|
||||
FlxG.cameras.remove(cutsceneCamera, true);
|
||||
super.destroy();
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
typedef CutsceneSubtitle = {
|
||||
var time:Float; // time in ms
|
||||
var subtitle:String; // subtitle text
|
||||
var color:FlxColor;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ package funkin.game.cutscenes.dialogue;
|
||||
import flixel.addons.text.FlxTypeText;
|
||||
import flixel.sound.FlxSound;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.backend.scripting.events.PlayAnimContext;
|
||||
import funkin.backend.scripting.events.PlayAnimEvent;
|
||||
import funkin.backend.scripting.events.sprite.*;
|
||||
import funkin.backend.scripting.events.dialogue.*;
|
||||
import haxe.xml.Access;
|
||||
|
||||
@@ -87,8 +86,11 @@ class DialogueBox extends FunkinSprite {
|
||||
}
|
||||
} catch(e) {
|
||||
active = false;
|
||||
Logs.trace('Couldn\'t load dialogue box "$name": ${e.toString()}', ERROR);
|
||||
var message:String = e.toString();
|
||||
Logs.trace('Couldn\'t load dialogue box "$name": $message', ERROR, RED);
|
||||
dialogueBoxScript.call("loadingError", [message]);
|
||||
}
|
||||
|
||||
defaultTextTypeSFX = [FlxG.sound.load(textTypeSFX)];
|
||||
FlxG.sound.cache(nextSFX);
|
||||
dialogueBoxScript.call("postCreate");
|
||||
|
||||
@@ -2,8 +2,7 @@ package funkin.game.cutscenes.dialogue;
|
||||
|
||||
import flixel.tweens.FlxTween;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.backend.scripting.events.PlayAnimContext;
|
||||
import funkin.backend.scripting.events.PlayAnimEvent;
|
||||
import funkin.backend.scripting.events.sprite.*;
|
||||
import funkin.backend.scripting.events.dialogue.*;
|
||||
import haxe.xml.Access;
|
||||
|
||||
@@ -54,9 +53,10 @@ class DialogueCharacter extends FunkinSprite {
|
||||
x = 0; y = 0;
|
||||
} catch(e) {
|
||||
var message:String = e.toString();
|
||||
Logs.trace('Failed to load dialogue character $name: ${message}', ERROR);
|
||||
Logs.trace('Failed to load dialogue character $name: ${message}', ERROR, RED);
|
||||
dialogueCharScript.call("loadingError", [message]);
|
||||
}
|
||||
|
||||
visible = false;
|
||||
dialogueCharScript.call("postCreate");
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class BetaWarningState extends MusicBeatState {
|
||||
|
||||
disclaimer = new FunkinText(16, titleAlphabet.y + titleAlphabet.height + 10, FlxG.width - 32, "", 32);
|
||||
disclaimer.alignment = CENTER;
|
||||
disclaimer.applyMarkup('This engine is still in a *${Main.releaseCycle}* state. That means *majority of the features* are either *buggy* or *non finished*. If you find any bugs, please report them to the Codename Engine GitHub.\n\nPress ENTER to continue',
|
||||
disclaimer.applyMarkup('This engine is still in a *${Flags.RELEASE_CYCLE}* state. That means *majority of the features* are either *buggy* or *non finished*. If you find any bugs, please report them to the Codename Engine GitHub.\n\nPress ENTER to continue',
|
||||
[
|
||||
new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFFF4444), "*")
|
||||
]
|
||||
|
||||
@@ -4,9 +4,10 @@ import flixel.text.FlxText;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.backend.chart.Chart;
|
||||
import funkin.backend.chart.ChartData.ChartMetaData;
|
||||
import funkin.backend.scripting.events.*;
|
||||
import funkin.backend.scripting.events.menu.MenuChangeEvent;
|
||||
import funkin.backend.scripting.events.menu.freeplay.*;
|
||||
import funkin.game.HealthIcon;
|
||||
import funkin.savedata.FunkinSave;
|
||||
import funkin.backend.utils.FunkinSave;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -458,7 +459,7 @@ class FreeplaySonglist {
|
||||
|
||||
if (songsFound.length > 0) {
|
||||
for(s in songsFound)
|
||||
songs.push(Chart.loadChartMeta(s, "normal", source == MODS));
|
||||
songs.push(Chart.loadChartMeta(s, Flags.DEFAULT_DIFFICULTY, source == MODS));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -4,7 +4,8 @@ import flixel.FlxState;
|
||||
import flixel.effects.FlxFlicker;
|
||||
import flixel.tweens.FlxTween;
|
||||
import funkin.backend.FunkinText;
|
||||
import funkin.backend.scripting.events.*;
|
||||
import funkin.backend.scripting.events.menu.MenuChangeEvent;
|
||||
import funkin.backend.scripting.events.NameEvent;
|
||||
import funkin.menus.credits.CreditsMain;
|
||||
import funkin.options.OptionsMenu;
|
||||
import lime.app.Application;
|
||||
@@ -24,7 +25,7 @@ class MainMenuState extends MusicBeatState
|
||||
var camFollow:FlxObject;
|
||||
var versionText:FunkinText;
|
||||
|
||||
public var canAccessDebugMenus:Bool = true;
|
||||
public var canAccessDebugMenus:Bool = Flags.DEFAULT_CAN_ACCESS_DEBUG_MENUS;
|
||||
|
||||
override function create()
|
||||
{
|
||||
@@ -72,7 +73,7 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
FlxG.camera.follow(camFollow, null, 0.06);
|
||||
|
||||
versionText = new FunkinText(5, FlxG.height - 2, 0, 'Codename Engine v${Application.current.meta.get('version')}\nCommit ${funkin.backend.system.macros.GitCommitMacro.commitNumber} (${funkin.backend.system.macros.GitCommitMacro.commitHash})\n[${controls.getKeyName(SWITCHMOD)}] Open Mods menu\n');
|
||||
versionText = new FunkinText(5, FlxG.height - 2, 0, '${Flags.VERSION_MESSAGE}\n${Flags.COMMIT_MESSAGE}\n[${controls.getKeyName(SWITCHMOD)}] Open Mods menu\n');
|
||||
versionText.y -= versionText.height;
|
||||
versionText.scrollFactor.set();
|
||||
add(versionText);
|
||||
@@ -158,7 +159,7 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
var event = event("onSelectItem", EventManager.get(NameEvent).recycle(daChoice));
|
||||
if (event.cancelled) return;
|
||||
switch (daChoice)
|
||||
switch (event.name)
|
||||
{
|
||||
case 'story mode': FlxG.switchState(new StoryMenuState());
|
||||
case 'freeplay': FlxG.switchState(new FreeplayState());
|
||||
|
||||
@@ -5,15 +5,16 @@ import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.backend.scripting.events.MenuChangeEvent;
|
||||
import funkin.backend.scripting.events.menu.MenuChangeEvent;
|
||||
import funkin.backend.scripting.events.menu.pause.*;
|
||||
import funkin.backend.scripting.events.NameEvent;
|
||||
import funkin.backend.scripting.events.PauseCreationEvent;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.backend.utils.FunkinParentDisabler;
|
||||
import funkin.editors.charter.Charter;
|
||||
import funkin.menus.StoryMenuState;
|
||||
import funkin.options.OptionsMenu;
|
||||
import funkin.options.keybinds.KeybindsOptions;
|
||||
import funkin.options.TreeMenu;
|
||||
|
||||
class PauseSubState extends MusicBeatSubstate
|
||||
{
|
||||
@@ -21,19 +22,22 @@ class PauseSubState extends MusicBeatSubstate
|
||||
|
||||
var grpMenuShit:FlxTypedGroup<Alphabet>;
|
||||
|
||||
var menuItems:Array<String> = ['Resume', 'Restart Song', 'Change Controls', 'Change Options', 'Exit to menu', "Exit to charter"];
|
||||
var menuItems:Array<String>;
|
||||
var curSelected:Int = 0;
|
||||
|
||||
var pauseMusic:FlxSound;
|
||||
|
||||
public var pauseScript:Script;
|
||||
public var selectCall:NameEvent->Void; // Mainly for extern stuff that aren't scripts - Nex
|
||||
|
||||
public var game:PlayState = PlayState.instance; // shortcut
|
||||
|
||||
private var __cancelDefault:Bool = false;
|
||||
|
||||
public function new(x:Float = 0, y:Float = 0) {
|
||||
public function new(?items:Array<String>, ?selectCall:NameEvent->Void) {
|
||||
super();
|
||||
menuItems = items != null ? items : Flags.DEFAULT_PAUSE_ITEMS;
|
||||
this.selectCall = selectCall;
|
||||
}
|
||||
|
||||
var parentDisabler:FunkinParentDisabler;
|
||||
@@ -135,13 +139,11 @@ class PauseSubState extends MusicBeatSubstate
|
||||
|
||||
public function selectOption() {
|
||||
var event = EventManager.get(NameEvent).recycle(menuItems[curSelected]);
|
||||
if (selectCall != null) selectCall(event);
|
||||
pauseScript.call("onSelectOption", [event]);
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
var daSelected:String = event.name;
|
||||
|
||||
switch (daSelected)
|
||||
switch (event.name)
|
||||
{
|
||||
case "Resume":
|
||||
close();
|
||||
@@ -153,9 +155,10 @@ class PauseSubState extends MusicBeatSubstate
|
||||
persistentDraw = false;
|
||||
openSubState(new KeybindsOptions());
|
||||
case "Change Options":
|
||||
TreeMenu.lastState = PlayState;
|
||||
FlxG.switchState(new OptionsMenu());
|
||||
case "Exit to charter":
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
|
||||
FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
|
||||
case "Exit to menu":
|
||||
if (PlayState.chartingMode && Charter.undos.unsaved)
|
||||
game.saveWarn(false);
|
||||
@@ -206,4 +209,4 @@ class PauseSubState extends MusicBeatSubstate
|
||||
item.alpha = 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,20 @@ import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.backend.FunkinText;
|
||||
import funkin.backend.scripting.events.*;
|
||||
import funkin.savedata.FunkinSave;
|
||||
import funkin.backend.scripting.events.menu.MenuChangeEvent;
|
||||
import funkin.backend.scripting.events.menu.storymenu.*;
|
||||
import funkin.backend.scripting.events.CancellableEvent;
|
||||
import funkin.backend.utils.FunkinSave;
|
||||
import haxe.io.Path;
|
||||
import haxe.xml.Access;
|
||||
|
||||
class StoryMenuState extends MusicBeatState {
|
||||
public var characters:Map<String, MenuCharacter> = [];
|
||||
public var characters:Map<String, Access> = [];
|
||||
public var weeks:Array<WeekData> = [];
|
||||
|
||||
// yes it supports parameters - Nex
|
||||
public var scoreMessage:String = "WEEK SCORE:{0}";
|
||||
|
||||
public var scoreText:FlxText;
|
||||
public var tracklist:FlxText;
|
||||
public var weekTitle:FlxText;
|
||||
@@ -23,18 +28,21 @@ class StoryMenuState extends MusicBeatState {
|
||||
public var curWeek:Int = 0;
|
||||
|
||||
public var difficultySprites:Map<String, FlxSprite> = [];
|
||||
public var weekBG:FlxSprite;
|
||||
public var leftArrow:FlxSprite;
|
||||
public var rightArrow:FlxSprite;
|
||||
public var blackBar:FlxSprite;
|
||||
|
||||
public var weekBG:FlxSprite;
|
||||
public var defColor:String = "#F9CF51";
|
||||
public var interpColor:FlxInterpolateColor;
|
||||
|
||||
public var lerpScore:Float = 0;
|
||||
public var intendedScore:Int = 0;
|
||||
|
||||
public var canSelect:Bool = true;
|
||||
|
||||
public var weekSprites:FlxTypedGroup<MenuItem>;
|
||||
public var characterSprites:FlxTypedGroup<MenuCharacterSprite>;
|
||||
public var characterSprites:FlxTypedGroup<FunkinSprite>;
|
||||
|
||||
//public var charFrames:Map<String, FlxFramesCollection> = [];
|
||||
|
||||
@@ -56,7 +64,7 @@ class StoryMenuState extends MusicBeatState {
|
||||
weekTitle.alpha = 0.7;
|
||||
|
||||
weekBG = new FlxSprite(0, 56).makeSolid(FlxG.width, 400, 0xFFFFFFFF);
|
||||
weekBG.color = 0xFFF9CF51;
|
||||
weekBG.color = weeks.length > 0 ? weeks[0].bgColor : FlxColor.fromString(defColor);
|
||||
weekBG.updateHitbox();
|
||||
|
||||
weekSprites = new FlxTypedGroup<MenuItem>();
|
||||
@@ -85,15 +93,12 @@ class StoryMenuState extends MusicBeatState {
|
||||
|
||||
add(weekSprites);
|
||||
for(e in [blackBar, scoreText, weekTitle, weekBG, tracklist]) {
|
||||
e.antialiasing = true;
|
||||
e.scrollFactor.set();
|
||||
add(e);
|
||||
}
|
||||
|
||||
characterSprites = new FlxTypedGroup<MenuCharacterSprite>();
|
||||
for(i in 0...3) {
|
||||
characterSprites.add(new MenuCharacterSprite(i));
|
||||
}
|
||||
add(characterSprites);
|
||||
add(characterSprites = new FlxTypedGroup<FunkinSprite>());
|
||||
|
||||
for(i=>week in weeks) {
|
||||
var spr:MenuItem = new MenuItem(0, (i * 120) + 480, 'menus/storymenu/weeks/${week.sprite}');
|
||||
@@ -114,6 +119,14 @@ class StoryMenuState extends MusicBeatState {
|
||||
}
|
||||
}
|
||||
|
||||
interpColor = new FlxInterpolateColor(weekBG.color);
|
||||
|
||||
// default difficulty should be the middle difficulty in the array
|
||||
// to be consistent with base game and whatnot, you know the drill
|
||||
curDifficulty = Math.floor(weeks[0].difficulties.length * 0.5);
|
||||
// debug stuff lol
|
||||
Logs.trace('Middle Difficulty for Week 1 is ${weeks[0].difficulties[curDifficulty]} (ID: $curDifficulty)');
|
||||
|
||||
changeWeek(0, true);
|
||||
|
||||
DiscordUtil.call("onMenuLoaded", ["Story Menu"]);
|
||||
@@ -125,7 +138,7 @@ class StoryMenuState extends MusicBeatState {
|
||||
super.update(elapsed);
|
||||
|
||||
lerpScore = lerp(lerpScore, intendedScore, 0.5);
|
||||
scoreText.text = 'WEEK SCORE:${Math.round(lerpScore)}';
|
||||
scoreText.text = scoreMessage.replace("{0}", Std.string(Math.round(lerpScore)));
|
||||
|
||||
if (canSelect) {
|
||||
if (leftArrow != null && leftArrow.exists) leftArrow.animation.play(controls.LEFT ? 'press' : 'idle');
|
||||
@@ -145,6 +158,9 @@ class StoryMenuState extends MusicBeatState {
|
||||
if (e != null && e.exists)
|
||||
e.animation.play('idle');
|
||||
}
|
||||
|
||||
interpColor.fpsLerpTo(weeks[curWeek].bgColor, 0.0625);
|
||||
weekBG.color = interpColor.color;
|
||||
}
|
||||
|
||||
public function goBack() {
|
||||
@@ -163,18 +179,43 @@ class StoryMenuState extends MusicBeatState {
|
||||
if (!force) CoolUtil.playMenuSFX();
|
||||
for(k=>e in weekSprites.members) {
|
||||
e.targetY = k - curWeek;
|
||||
e.alpha = k == curWeek ? 1.0 : 0.6;
|
||||
}
|
||||
tracklist.text = 'TRACKS\n\n${[for(e in weeks[curWeek].songs) if (!e.hide) e.name.toUpperCase()].join('\n')}';
|
||||
tracklist.text = 'TRACKS\n\n${[for(e in weeks[curWeek].songs) if (!e.hide) e.name].join('\n')}';
|
||||
weekTitle.text = weeks[curWeek].name.getDefault("");
|
||||
|
||||
for(i in 0...3)
|
||||
characterSprites.members[i].changeCharacter(characters[weeks[curWeek].chars[i]]);
|
||||
if(characterSprites != null) for(i in 0...3) {
|
||||
var curChar:FunkinSprite; var newChar:Access = characters[weeks[curWeek].chars[i]];
|
||||
if(newChar == null) modifyCharacterAt(i);
|
||||
else if((curChar = characterSprites.members[i]) == null || newChar.getAtt("name") != curChar.name) modifyCharacterAt(i, newChar);
|
||||
}
|
||||
|
||||
changeDifficulty(0, true);
|
||||
|
||||
MemoryUtil.clearMinor();
|
||||
}
|
||||
|
||||
public function modifyCharacterAt(i:Int, ?node:Access):FunkinSprite {
|
||||
var old = characterSprites.members[i];
|
||||
if(old != null) {
|
||||
characterSprites.remove(old);
|
||||
old.destroy();
|
||||
}
|
||||
|
||||
if(node == null) return null;
|
||||
var curChar:FunkinSprite = XMLUtil.createSpriteFromXML(node, "", BEAT);
|
||||
curChar.offset.x += curChar.x; curChar.offset.y += curChar.y;
|
||||
curChar.setPosition((FlxG.width * 0.25) * (1 + i) - 150, 70);
|
||||
if(characterSprites != null) characterSprites.insert(i, curChar); // Making so many null checks abt this group just in case if mods destroy it - Nex
|
||||
curChar.playAnim("idle", true, DANCE);
|
||||
return curChar;
|
||||
}
|
||||
|
||||
public override function beatHit(curBeat:Int) {
|
||||
super.beatHit(curBeat);
|
||||
if(characterSprites != null) characterSprites.forEachAlive(function(spr) spr.beatHit(curBeat));
|
||||
}
|
||||
|
||||
var __oldDiffName = null;
|
||||
public function changeDifficulty(change:Int, force:Bool = false) {
|
||||
if (change == 0 && !force) return;
|
||||
@@ -238,7 +279,8 @@ class StoryMenuState extends MusicBeatState {
|
||||
sprite: week.getAtt('sprite').getDefault(weekName),
|
||||
chars: [null, null, null],
|
||||
songs: [],
|
||||
difficulties: ['easy', 'normal', 'hard']
|
||||
difficulties: ['easy', 'normal', 'hard'],
|
||||
bgColor: FlxColor.fromString(week.getAtt("bgColor").getDefault(defColor))
|
||||
};
|
||||
|
||||
var diffNodes = week.nodes.difficulty;
|
||||
@@ -292,19 +334,14 @@ class StoryMenuState extends MusicBeatState {
|
||||
} catch(e) {
|
||||
Logs.trace('Story Menu: Cannot parse character "$charName.xml": ${Std.string(e)}`', ERROR);
|
||||
}
|
||||
if (char == null) return;
|
||||
|
||||
if (characters[charName] != null) return;
|
||||
var charObj:MenuCharacter = {
|
||||
spritePath: Paths.image(char.getAtt('sprite').getDefault('menus/storymenu/characters/${charName}')),
|
||||
scale: Std.parseFloat(char.getAtt('scale')).getDefault(1),
|
||||
xml: char,
|
||||
offset: FlxPoint.get(
|
||||
Std.parseFloat(char.getAtt('x')).getDefault(0),
|
||||
Std.parseFloat(char.getAtt('y')).getDefault(0)
|
||||
)
|
||||
};
|
||||
characters[charName] = charObj;
|
||||
if(char != null && characters[charName] == null) {
|
||||
if(!char.x.exists("name")) char.x.set("name", charName);
|
||||
if(!char.x.exists("sprite")) char.x.set("sprite", 'menus/storymenu/characters/${charName}');
|
||||
if(!char.x.exists("beatInterval")) char.x.set("beatInterval", "1");
|
||||
if(!char.x.exists("updateHitbox")) char.x.set("updateHitbox", "true");
|
||||
characters[charName] = char;
|
||||
}
|
||||
}
|
||||
|
||||
public function getWeeksFromSource(weeks:Array<String>, source:funkin.backend.assets.AssetsLibraryList.AssetSource) {
|
||||
@@ -325,13 +362,6 @@ class StoryMenuState extends MusicBeatState {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override function destroy() {
|
||||
super.destroy();
|
||||
for(e in characters)
|
||||
if (e != null && e.offset != null)
|
||||
e.offset.put();
|
||||
}
|
||||
|
||||
public function selectWeek() {
|
||||
var event = event("onWeekSelect", EventManager.get(WeekSelectEvent).recycle(weeks[curWeek], weeks[curWeek].difficulties[curDifficulty], curWeek, curDifficulty));
|
||||
if (event.cancelled) return;
|
||||
@@ -339,17 +369,16 @@ class StoryMenuState extends MusicBeatState {
|
||||
canSelect = false;
|
||||
CoolUtil.playMenuSFX(CONFIRM);
|
||||
|
||||
for(char in characterSprites)
|
||||
if (char.animation.exists("confirm"))
|
||||
char.animation.play("confirm");
|
||||
if(characterSprites != null)
|
||||
characterSprites.forEachAlive(function(spr) spr.playAnim("confirm", true, LOCK));
|
||||
|
||||
PlayState.loadWeek(weeks[curWeek], weeks[curWeek].difficulties[curDifficulty]);
|
||||
PlayState.loadWeek(event.week, event.difficulty);
|
||||
|
||||
new FlxTimer().start(1, function(tmr:FlxTimer)
|
||||
{
|
||||
FlxG.switchState(new PlayState());
|
||||
});
|
||||
weekSprites.members[curWeek].startFlashing();
|
||||
weekSprites.members[event.weekID].startFlashing();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +389,7 @@ typedef WeekData = {
|
||||
var chars:Array<String>;
|
||||
var songs:Array<WeekSong>;
|
||||
var difficulties:Array<String>;
|
||||
var bgColor:FlxColor;
|
||||
}
|
||||
|
||||
typedef WeekSong = {
|
||||
@@ -367,52 +397,6 @@ typedef WeekSong = {
|
||||
var hide:Bool;
|
||||
}
|
||||
|
||||
typedef MenuCharacter = {
|
||||
var spritePath:String;
|
||||
var xml:Access;
|
||||
var scale:Float;
|
||||
var offset:FlxPoint;
|
||||
// var frames:FlxFramesCollection;
|
||||
}
|
||||
|
||||
class MenuCharacterSprite extends FlxSprite
|
||||
{
|
||||
public var character:String;
|
||||
|
||||
var pos:Int;
|
||||
|
||||
public function new(pos:Int) {
|
||||
super(0, 70);
|
||||
this.pos = pos;
|
||||
visible = false;
|
||||
antialiasing = true;
|
||||
}
|
||||
|
||||
public var oldChar:MenuCharacter = null;
|
||||
|
||||
public function changeCharacter(data:MenuCharacter) {
|
||||
visible = (data != null);
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
if (oldChar != (oldChar = data)) {
|
||||
CoolUtil.loadAnimatedGraphic(this, data.spritePath);
|
||||
for(e in data.xml.nodes.anim) {
|
||||
if (e.getAtt("name") == "idle")
|
||||
animation.remove("idle");
|
||||
|
||||
XMLUtil.addXMLAnimation(this, e);
|
||||
}
|
||||
animation.play("idle");
|
||||
scale.set(data.scale, data.scale);
|
||||
updateHitbox();
|
||||
offset.x += data.offset.x;
|
||||
offset.y += data.offset.y;
|
||||
|
||||
x = (FlxG.width * 0.25) * (1 + pos) - 150;
|
||||
}
|
||||
}
|
||||
}
|
||||
class MenuItem extends FlxSprite
|
||||
{
|
||||
public var targetY:Float = 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user