Merge branch 'main' into translations-system
This commit is contained in:
@@ -219,7 +219,7 @@ class StringTools {
|
||||
|
||||
If `start` is the empty String `""`, the result is true.
|
||||
**/
|
||||
public static #if (java || python || (js && js_es >= 6) || cpp) inline #end function startsWith(s:String, start:String):Bool {
|
||||
public static #if (java || python || (js && js_es >= 6)) inline #end function startsWith(s:String, start:String):Bool {
|
||||
#if java
|
||||
return (cast s : java.NativeString).startsWith(start);
|
||||
#elseif hl
|
||||
@@ -244,7 +244,7 @@ class StringTools {
|
||||
|
||||
If `end` is the empty String `""`, the result is true.
|
||||
**/
|
||||
public static #if (java || python || (js && js_es >= 6) || cpp) inline #end function endsWith(s:String, end:String):Bool {
|
||||
public static #if (java || python || (js && js_es >= 6)) inline #end function endsWith(s:String, end:String):Bool {
|
||||
#if java
|
||||
return (cast s : java.NativeString).endsWith(end);
|
||||
#elseif hl
|
||||
|
||||
@@ -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;
|
||||
@@ -46,6 +46,8 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
public var debugMode:Bool = false;
|
||||
public var animDatas:Map<String, AnimData> = [];
|
||||
|
||||
public var globalCurFrame(get, set):Int;
|
||||
|
||||
/**
|
||||
* ODD interval -> asynced; EVEN interval -> synced
|
||||
*/
|
||||
@@ -130,7 +132,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
private var countedBeat = 0;
|
||||
public function beatHit(curBeat:Int)
|
||||
{
|
||||
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
|
||||
@@ -391,4 +393,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,10 +162,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)
|
||||
@@ -228,7 +254,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) {
|
||||
@@ -256,11 +282,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
|
||||
*/
|
||||
@@ -217,7 +221,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) {
|
||||
@@ -235,7 +239,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() {
|
||||
@@ -250,19 +254,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.
|
||||
*/
|
||||
public static inline 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,30 +59,33 @@ class Paths
|
||||
public static inline function ps1(key:String, ?library:String)
|
||||
return getPath('data/$key.ps1', library);
|
||||
|
||||
public static 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);
|
||||
|
||||
public static inline function soundRandom(key:String, min:Int, max:Int, ?library:String)
|
||||
return sound(key + FlxG.random.int(min, max), library);
|
||||
|
||||
public static inline 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);
|
||||
}
|
||||
|
||||
public static 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,8 +110,9 @@ class Paths
|
||||
return scriptPath;
|
||||
}
|
||||
|
||||
public static function chart(song:String, ?difficulty:String = "normal"):String {
|
||||
difficulty = difficulty.toLowerCase();
|
||||
static public function chart(song:String, ?difficulty:String):String
|
||||
{
|
||||
difficulty = (difficulty != null ? difficulty : Flags.DEFAULT_DIFFICULTY).toLowerCase();
|
||||
song = song.toLowerCase();
|
||||
|
||||
return getPath('songs/$song/charts/$difficulty.json', null);
|
||||
@@ -156,33 +154,33 @@ class Paths
|
||||
return getPath('models/$key.awd');
|
||||
}
|
||||
|
||||
public static inline 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));
|
||||
|
||||
public static inline 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');
|
||||
|
||||
public static inline 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));
|
||||
|
||||
public static inline 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');
|
||||
|
||||
public static inline 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));
|
||||
|
||||
public static inline 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');
|
||||
|
||||
public static inline function getAssetsRoot():String
|
||||
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : './assets';
|
||||
inline static public function getAssetsRoot():String
|
||||
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)
|
||||
@@ -190,7 +188,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -202,10 +200,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');
|
||||
@@ -216,18 +215,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;
|
||||
|
||||
class Chart {
|
||||
/**
|
||||
* Default background colors for songs without bg color
|
||||
*/
|
||||
public inline static var defaultColor:FlxColor = 0xFF9271FD;
|
||||
|
||||
public static function loadEventsJson(songName:String) {
|
||||
var path = Paths.file('songs/${songName.toLowerCase()}/events.json');
|
||||
var data:Array<ChartEvent> = null;
|
||||
@@ -30,7 +25,9 @@ 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');
|
||||
|
||||
@@ -50,19 +47,19 @@ 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)];
|
||||
@@ -86,7 +83,9 @@ 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: [],
|
||||
@@ -95,8 +94,8 @@ 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)
|
||||
};
|
||||
@@ -192,7 +191,8 @@ 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;
|
||||
@@ -210,11 +210,11 @@ 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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,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>;
|
||||
@@ -27,6 +27,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,7 +132,7 @@ class FNFLegacyParser {
|
||||
if ((swagSection.mustHitSection && strumLine.type == OPPONENT) ||
|
||||
(!swagSection.mustHitSection && strumLine.type == PLAYER))
|
||||
sectionNote[1] += 4;
|
||||
swagSection.sectionNotes.push(sectionNote);
|
||||
swagSection.sectionNotes.push(sectionNote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -19,78 +19,78 @@ 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,
|
||||
@@ -101,12 +101,57 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
#end
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -4,10 +4,5 @@ final class AmountEvent extends CancellableEvent {
|
||||
/**
|
||||
* Amount
|
||||
*/
|
||||
public var amount:Int;
|
||||
|
||||
/**
|
||||
* Shows whether or not psych users complained about this class
|
||||
*/
|
||||
public var psychUsersComplained:Bool = true;
|
||||
public var amount:Null<Int>;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
+12
-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
|
||||
*/
|
||||
+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,6 +14,43 @@ 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";
|
||||
|
||||
@@ -29,7 +69,89 @@ class Flags {
|
||||
public static var WHITELISTED_LANGUAGES:Array<String> = [];
|
||||
|
||||
// Internal stuff
|
||||
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;
|
||||
|
||||
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() {
|
||||
|
||||
@@ -29,10 +29,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;
|
||||
@@ -128,6 +124,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
|
||||
@@ -149,7 +148,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;
|
||||
@@ -172,15 +171,22 @@ class Main extends Sprite
|
||||
initTransition();
|
||||
}
|
||||
|
||||
public static function refreshAssets() {
|
||||
public static function refreshAssets() @:privateAccess {
|
||||
WindowUtils.resetTitle();
|
||||
|
||||
FlxSoundTray.volumeChangeSFX = Paths.sound('menu/volume');
|
||||
FlxSoundTray.volumeUpChangeSFX = null;
|
||||
FlxSoundTray.volumeDownChangeSFX = null;
|
||||
var game = FlxG.game;
|
||||
var daSndTray = Type.createInstance(game._customSoundTray = funkin.menus.ui.FunkinSoundTray, []);
|
||||
var index:Int = game.numChildren - 1;
|
||||
|
||||
if (FlxG.game.soundTray != null)
|
||||
FlxG.game.soundTray.text.setTextFormat(new TextFormat(Paths.font("vcr.ttf")));
|
||||
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
|
||||
@@ -54,11 +56,11 @@ class MainState extends FlxState {
|
||||
}
|
||||
#end
|
||||
|
||||
Main.refreshAssets();
|
||||
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder);
|
||||
Flags.load();
|
||||
TranslationUtil.findAllLanguages();
|
||||
TranslationUtil.setLanguage();
|
||||
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder); // Loads global.hx
|
||||
Main.refreshAssets();
|
||||
DiscordUtil.init();
|
||||
EventsData.reloadEvents();
|
||||
TitleState.initialized = false;
|
||||
|
||||
@@ -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}';
|
||||
}
|
||||
|
||||
@@ -83,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'));
|
||||
@@ -106,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';
|
||||
|
||||
@@ -11,8 +11,16 @@ class BuildInfo {
|
||||
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";
|
||||
@@ -25,18 +33,22 @@ class BuildInfo {
|
||||
}
|
||||
}
|
||||
|
||||
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 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]));
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -153,11 +153,15 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,11 +174,15 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,11 +194,15 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,11 +214,15 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,7 +449,7 @@ 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:Int = 1, Looped:Bool = true, DefaultBPM:Int = 102, ?Group:FlxSoundGroup) {
|
||||
@:noUsing public static function playMusic(path:String, Persist:Bool = false, Volume:Int = 1, Looped:Bool = true, DefaultBPM:Float = 102, ?Group:FlxSoundGroup) {
|
||||
Conductor.reset();
|
||||
FlxG.sound.playMusic(path, Volume, Looped, Group);
|
||||
if (FlxG.sound.music != null) {
|
||||
@@ -444,17 +460,17 @@ 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"]).getDefaultFloat(DefaultBPM);
|
||||
|
||||
@@ -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 @@ 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
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package funkin.savedata;
|
||||
package funkin.backend.utils;
|
||||
|
||||
import flixel.util.FlxSave;
|
||||
import lime.app.Application;
|
||||
@@ -147,6 +147,8 @@ class XMLUtil {
|
||||
var graphicSizey:Null<Int> = Std.parseInt(node.att.graphicSizey);
|
||||
if (graphicSizey.isNotNull()) spr.setGraphicSize(0, graphicSizey);
|
||||
}
|
||||
|
||||
if (node.has.flipX && node.att.flipX == "true") spr.flipX = true;
|
||||
if (node.has.updateHitbox && node.att.updateHitbox == "true") spr.updateHitbox();
|
||||
|
||||
if (node.has.zoomfactor)
|
||||
|
||||
@@ -324,7 +324,7 @@ class CharacterEditor extends UIState {
|
||||
characterBG.scale.set(FlxG.width/characterBG.width, FlxG.height/characterBG.height);
|
||||
characterBG.scale.set(characterBG.scale.x / charCamera.zoom, characterBG.scale.y / charCamera.zoom);
|
||||
|
||||
WindowUtils.prefix = undos.unsaved ? "* " : "";
|
||||
WindowUtils.prefix = undos.unsaved ? Flags.UNDO_PREFIX : "";
|
||||
SaveWarning.showWarning = undos.unsaved;
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ class CharacterEditor extends UIState {
|
||||
]);
|
||||
|
||||
// clean
|
||||
if (charXML.exists("gameOverChar") && character.gameOverCharacter == "bf-dead") charXML.remove("gameOverChar");
|
||||
if (charXML.exists("gameOverChar") && character.gameOverCharacter == Flags.DEFAULT_GAMEOVER_CHARACTER) charXML.remove("gameOverChar");
|
||||
if (charXML.exists("camx") && character.cameraOffset.x == 0) charXML.remove("camx");
|
||||
if (charXML.exists("camy") && character.cameraOffset.y == 0) charXML.remove("camy");
|
||||
if (charXML.exists("holdTime") && character.holdTime == 4) charXML.remove("holdTime");
|
||||
|
||||
@@ -151,7 +151,7 @@ class CharacterInfoScreen 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);
|
||||
|
||||
@@ -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",
|
||||
@@ -334,7 +334,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();
|
||||
}
|
||||
|
||||
@@ -1255,7 +1255,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;
|
||||
@@ -1307,7 +1307,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();
|
||||
@@ -1323,7 +1323,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();
|
||||
@@ -1333,7 +1333,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(_);
|
||||
@@ -1341,20 +1341,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',
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1362,7 +1362,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(_);
|
||||
@@ -1371,7 +1371,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
|
||||
|
||||
@@ -132,7 +132,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);
|
||||
|
||||
@@ -111,9 +111,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
|
||||
|
||||
// duplicated code, todo: fix this
|
||||
@@ -151,7 +151,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
|
||||
// duplicated code, todo: fix this
|
||||
|
||||
@@ -39,7 +39,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
|
||||
if (creatingStrumLine)
|
||||
strumLine = {
|
||||
characters: ["dad"],
|
||||
characters: [Flags.DEFAULT_OPPONENT],
|
||||
type: OPPONENT,
|
||||
notes: [],
|
||||
position: "dad",
|
||||
@@ -205,7 +205,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();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,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;
|
||||
@@ -96,7 +98,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
translate("instAudio"),
|
||||
[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;
|
||||
@@ -224,7 +226,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);
|
||||
|
||||
@@ -9,13 +9,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;
|
||||
@@ -32,8 +30,8 @@ using StringTools;
|
||||
@:allow(funkin.game.PlayState)
|
||||
class Character extends FunkinSprite implements IBeatReceiver implements IOffsetCompatible {
|
||||
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;
|
||||
@@ -63,12 +61,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;
|
||||
|
||||
@@ -128,7 +126,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;
|
||||
}
|
||||
|
||||
@@ -154,6 +152,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)
|
||||
@@ -180,6 +183,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]);
|
||||
|
||||
@@ -203,17 +209,24 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
return (isPlayer != playerOffsets) != (flipX != __baseFlipped);
|
||||
|
||||
public override function draw() {
|
||||
if (isFlippedOffsets()) {
|
||||
__reverseDrawProcedure = true;
|
||||
flipX = !flipX;
|
||||
scale.x *= -1;
|
||||
var e = EventManager.get(DrawEvent).recycle();
|
||||
script.call("draw", [e]);
|
||||
|
||||
super.draw();
|
||||
if(!e.cancelled) {
|
||||
if (isFlippedOffsets()) {
|
||||
__reverseDrawProcedure = true;
|
||||
flipX = !flipX;
|
||||
scale.x *= -1;
|
||||
|
||||
flipX = !flipX;
|
||||
scale.x *= -1;
|
||||
__reverseDrawProcedure = false;
|
||||
} else super.draw();
|
||||
super.draw();
|
||||
|
||||
flipX = !flipX;
|
||||
scale.x *= -1;
|
||||
__reverseDrawProcedure = false;
|
||||
} else super.draw();
|
||||
}
|
||||
|
||||
script.call("postDraw", [e]);
|
||||
}
|
||||
|
||||
public var singAnims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"];
|
||||
@@ -464,6 +477,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);
|
||||
|
||||
@@ -481,8 +497,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) {
|
||||
@@ -519,7 +533,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;
|
||||
|
||||
|
||||
@@ -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,16 +37,16 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||
|
||||
public var lossSFX:FlxSound;
|
||||
|
||||
public function new(x:Float, y:Float, character:String = "bf-dead", 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;
|
||||
this.characterName = 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()
|
||||
@@ -56,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);
|
||||
@@ -127,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);
|
||||
}
|
||||
}
|
||||
@@ -198,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);
|
||||
|
||||
@@ -95,7 +95,7 @@ class HealthIcon extends FlxSprite
|
||||
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
|
||||
|
||||
+135
-81
@@ -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) {
|
||||
@@ -699,16 +739,16 @@ class PlayState extends MusicBeatState
|
||||
|
||||
// HUD INITIALIZATION & CAMERA INITIALIZATION
|
||||
#if REGION
|
||||
var event = EventManager.get(AmountEvent).recycle(4);
|
||||
if (!scripts.event("onPreGenerateStrums", event).cancelled) {
|
||||
var event = EventManager.get(AmountEvent).recycle(null);
|
||||
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;
|
||||
|
||||
@@ -737,10 +777,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);
|
||||
@@ -777,7 +818,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 = TU.translate("charter.chartPlaytesting");
|
||||
|
||||
SaveWarning.showWarning = Charter.undos.unsaved;
|
||||
@@ -802,7 +843,7 @@ class PlayState extends MusicBeatState
|
||||
|
||||
__updateNote_event = EventManager.get(NoteUpdateEvent);
|
||||
|
||||
scripts.call("postCreate");
|
||||
gameAndCharsCall("postCreate", null, "gamePostCreate");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -833,7 +874,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() {
|
||||
@@ -866,7 +907,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;
|
||||
@@ -879,14 +920,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],
|
||||
@@ -929,12 +970,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;
|
||||
@@ -961,7 +1002,7 @@ class PlayState extends MusicBeatState
|
||||
|
||||
updateDiscordPresence();
|
||||
|
||||
scripts.call("onStartSong");
|
||||
gameAndCharsCall("onStartSong");
|
||||
}
|
||||
|
||||
public override function destroy() {
|
||||
@@ -1008,11 +1049,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
|
||||
@@ -1032,14 +1077,17 @@ class PlayState extends MusicBeatState
|
||||
}
|
||||
|
||||
@:dox(hide)
|
||||
private inline function generateStrums(amount:Int = 4):Void
|
||||
for(p in strumLines)
|
||||
p.generateStrums(amount);
|
||||
private inline function generateStrums(amount:Null<Int> = null):Void {
|
||||
for(p in strumLines) {
|
||||
var kc = amount != null ? amount : Flags.DEFAULT_STRUM_AMOUNT;
|
||||
p.generateStrums(kc);
|
||||
}
|
||||
}
|
||||
|
||||
@: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;
|
||||
@@ -1059,13 +1107,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)
|
||||
@@ -1099,7 +1147,7 @@ class PlayState extends MusicBeatState
|
||||
inst.resume();
|
||||
vocals.resume();
|
||||
}
|
||||
scripts.call("onFocus");
|
||||
gameAndCharsCall("onFocus");
|
||||
updateDiscordPresence();
|
||||
super.onFocus();
|
||||
}
|
||||
@@ -1112,7 +1160,7 @@ class PlayState extends MusicBeatState
|
||||
inst.pause();
|
||||
vocals.pause();
|
||||
}
|
||||
scripts.call("onFocusLost");
|
||||
gameAndCharsCall("onFocusLost");
|
||||
updateDiscordPresence();
|
||||
super.onFocusLost();
|
||||
}
|
||||
@@ -1131,14 +1179,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;
|
||||
@@ -1146,7 +1194,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());
|
||||
@@ -1203,7 +1251,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);
|
||||
|
||||
@@ -1266,8 +1314,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();
|
||||
@@ -1286,7 +1335,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;
|
||||
}
|
||||
@@ -1311,7 +1360,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);
|
||||
}
|
||||
@@ -1356,10 +1405,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":
|
||||
@@ -1419,11 +1470,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 : "bf-dead"),
|
||||
deathCharID.getDefault(charToUse != null ? charToUse.gameOverCharacter : Flags.DEFAULT_GAMEOVER_CHARACTER),
|
||||
charToUse != null ? charToUse.isPlayer : true,
|
||||
gameOverSong.getDefault(this.gameOverSong),
|
||||
lossSFX.getDefault(this.lossSFX),
|
||||
@@ -1448,7 +1499,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1456,7 +1507,7 @@ class PlayState extends MusicBeatState
|
||||
*/
|
||||
public function endSong():Void
|
||||
{
|
||||
scripts.call("onSongEnd");
|
||||
gameAndCharsCall("onSongEnd");
|
||||
canPause = false;
|
||||
inst.volume = 0;
|
||||
vocals.volume = 0;
|
||||
@@ -1577,7 +1628,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;
|
||||
|
||||
@@ -1661,13 +1712,13 @@ class PlayState extends MusicBeatState
|
||||
|
||||
var event:NoteHitEvent;
|
||||
if (strumLine != null && !strumLine.cpu)
|
||||
event = 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 = 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) {
|
||||
@@ -1682,7 +1733,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;
|
||||
}
|
||||
}
|
||||
@@ -1741,14 +1793,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;
|
||||
@@ -1770,6 +1820,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}'));
|
||||
@@ -1821,14 +1872,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();
|
||||
@@ -1916,7 +1968,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;
|
||||
@@ -1936,7 +1989,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;
|
||||
|
||||
@@ -42,7 +42,7 @@ class SplashGroup extends FlxTypedGroup<FunkinSprite> {
|
||||
Logs.error('Couldn\'t parse splash data for "${path}": ${e.toString()}');
|
||||
valid = false;
|
||||
}
|
||||
maxSize = 8;
|
||||
maxSize = Flags.MAX_SPLASHES;
|
||||
}
|
||||
|
||||
function createSplash(imagePath:String) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,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;
|
||||
@@ -76,7 +75,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
|
||||
if (PlayState.instance != null) {
|
||||
event = EventManager.get(StageXMLEvent).recycle(this, stageXML, elems);
|
||||
elems = PlayState.instance.scripts.event("onStageXMLParsed", event).elems;
|
||||
elems = PlayState.instance.gameAndCharsEvent("onStageXMLParsed", event).elems;
|
||||
}
|
||||
|
||||
for(node in elems) {
|
||||
@@ -148,7 +147,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
}
|
||||
|
||||
if(PlayState.instance != null) {
|
||||
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 (sprite != null) {
|
||||
@@ -193,7 +192,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
}
|
||||
|
||||
// idk lemme check anyways just in case scripts did smth - Nex
|
||||
if (event != null) PlayState.instance.scripts.event("onPostStageCreation", event);
|
||||
if (event != null) PlayState.instance.gameAndCharsEvent("onPostStageCreation", event);
|
||||
|
||||
// shortlived scripts destroy when the stage finishes setting up - Nex
|
||||
for (info in xmlImportedScripts) if (info.shortLived) {
|
||||
|
||||
@@ -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;
|
||||
@@ -112,22 +118,21 @@ class DialogueCutscene extends Cutscene {
|
||||
|
||||
next(true);
|
||||
} catch(e) {
|
||||
Logs.error('Error while loading dialogue at ${dialoguePath}: ${e.toString()}');
|
||||
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();
|
||||
@@ -140,8 +145,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) {
|
||||
@@ -156,7 +161,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)
|
||||
@@ -169,8 +174,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) {
|
||||
@@ -180,7 +185,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() {
|
||||
@@ -195,8 +200,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;
|
||||
|
||||
@@ -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.error('Could not find script for scripted cutscene at ${scriptPath}');
|
||||
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.error('Couldn\'t load dialogue box "$name": ${e.toString()}');
|
||||
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.error('Failed to load dialogue character $name: ${message}');
|
||||
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(TU.translate("betaWarning.desc", [Main.releaseCycle]) + "\n\n" + TU.translate("betaWarning.proceed", ["ENTER"]),
|
||||
disclaimer.applyMarkup(TU.translate("betaWarning.desc", [Flags.RELEASE_CYCLE]) + "\n\n" + TU.translate("betaWarning.proceed", ["ENTER"]),
|
||||
[
|
||||
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;
|
||||
|
||||
@@ -463,7 +464,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()
|
||||
{
|
||||
@@ -73,8 +74,8 @@ 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')}',
|
||||
TU.translate("mainMenu.commit", [funkin.backend.system.macros.GitCommitMacro.commitNumber, funkin.backend.system.macros.GitCommitMacro.commitHash]),
|
||||
Flags.VERSION_MESSAGE,
|
||||
TU.translate("mainMenu.commit", [Flags.COMMIT_NUMBER, Flags.COMMIT_HASH]),
|
||||
TU.translate("mainMenu.openMods", [controls.getKeyName(SWITCHMOD)]),
|
||||
''
|
||||
].join('\n'));
|
||||
|
||||
@@ -5,9 +5,9 @@ import funkin.backend.FunkinText;
|
||||
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;
|
||||
@@ -29,19 +29,22 @@ class PauseSubState extends MusicBeatSubstate
|
||||
// Change Options -> pause.changeOptions
|
||||
// Exit to menu -> pause.exitToMenu
|
||||
// Exit to charter -> pause.exitToCharter
|
||||
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;
|
||||
@@ -150,13 +153,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();
|
||||
|
||||
@@ -6,15 +6,21 @@ 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
|
||||
// To be removed later, when translation branch is merged into public - Neo
|
||||
public var scoreMessage:String = "WEEK SCORE:{0}";
|
||||
|
||||
public var scoreText:FlxText;
|
||||
public var tracklist:FlxText;
|
||||
public var weekTitle:FlxText;
|
||||
@@ -23,18 +29,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 +65,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 +94,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 +120,8 @@ 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);
|
||||
@@ -151,6 +159,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() {
|
||||
@@ -185,14 +196,38 @@ class StoryMenuState extends MusicBeatState {
|
||||
tracklist.text = str.toString();
|
||||
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;
|
||||
@@ -256,7 +291,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;
|
||||
@@ -310,19 +346,14 @@ class StoryMenuState extends MusicBeatState {
|
||||
} catch(e) {
|
||||
Logs.error('Story Menu: Cannot parse character "$charName.xml": ${Std.string(e)}');
|
||||
}
|
||||
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')).getDefaultFloat(1),
|
||||
xml: char,
|
||||
offset: FlxPoint.get(
|
||||
Std.parseFloat(char.getAtt('x')).getDefaultFloat(0),
|
||||
Std.parseFloat(char.getAtt('y')).getDefaultFloat(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.AssetSource) {
|
||||
@@ -343,13 +374,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;
|
||||
@@ -357,9 +381,8 @@ 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(event.week, event.difficulty);
|
||||
|
||||
@@ -378,6 +401,7 @@ typedef WeekData = {
|
||||
var chars:Array<String>;
|
||||
var songs:Array<WeekSong>;
|
||||
var difficulties:Array<String>;
|
||||
var bgColor:FlxColor;
|
||||
}
|
||||
|
||||
typedef WeekSong = {
|
||||
@@ -385,52 +409,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;
|
||||
|
||||
@@ -10,11 +10,7 @@ using StringTools;
|
||||
|
||||
class CreditsCodename extends funkin.options.OptionsScreen {
|
||||
public var error:Bool = false;
|
||||
public var author:String = "FNF-CNE-Devs";
|
||||
public var totalContributions:Int = 0;
|
||||
|
||||
public var mainDevCol:FlxColor = 0xFF9C35D5;
|
||||
public var minContrCol:FlxColor = 0xFFB4A7DA;
|
||||
public var contribFormats:Array<FlxTextFormatMarkerPair> = [];
|
||||
|
||||
public override function new()
|
||||
@@ -61,8 +57,8 @@ class CreditsCodename extends funkin.options.OptionsScreen {
|
||||
var text:String = parent.treeParent.pathDesc.text;
|
||||
parent.treeParent.pathDesc.text = "";
|
||||
parent.treeParent.pathDesc.applyMarkup(text, contribFormats = [
|
||||
new FlxTextFormatMarkerPair(new FlxTextFormat(mainDevCol), '*'),
|
||||
new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(minContrCol, mainDevCol, Options.contributors[curSelected].contributions / totalContributions)), '~')
|
||||
new FlxTextFormatMarkerPair(new FlxTextFormat(Flags.MAIN_DEVS_COLOR), '*'),
|
||||
new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(Flags.MIN_CONTRIBUTIONS_COLOR, Flags.MAIN_DEVS_COLOR, Options.contributors[curSelected].contributions / totalContributions)), '~')
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -77,7 +73,7 @@ class CreditsCodename extends funkin.options.OptionsScreen {
|
||||
Options.lastUpdated = curTime;
|
||||
|
||||
error = false;
|
||||
var idk = GitHub.getContributors(author, "CodenameEngine", function(e) {
|
||||
var idk = GitHub.getContributors(Flags.REPO_OWNER, Flags.REPO_NAME, function(e) {
|
||||
error = true;
|
||||
var errMsg:String = 'Error while trying to download contributors list:\n${CoolUtil.removeIP(e.message)}';
|
||||
Logs.error(errMsg.replace('\n', ' '));
|
||||
@@ -88,9 +84,9 @@ class CreditsCodename extends funkin.options.OptionsScreen {
|
||||
Logs.trace('[CreditsCodename] Contributors list Updated!', VERBOSE);
|
||||
|
||||
var errorOnMain:Bool = false;
|
||||
var idk2 = GitHub.getOrganizationMembers(author, function(e) {
|
||||
var idk2 = GitHub.getOrganizationMembers(Flags.REPO_OWNER, function(e) {
|
||||
errorOnMain = true;
|
||||
var errMsg:String = 'Error while trying to download $author members list:\n${CoolUtil.removeIP(e.message)}';
|
||||
var errMsg:String = 'Error while trying to download ${Flags.REPO_OWNER} members list:\n${CoolUtil.removeIP(e.message)}';
|
||||
Logs.error(errMsg.replace('\n', ' '));
|
||||
funkin.backend.utils.NativeAPI.showMessageBox("Codename Engine Warning", errMsg, MSG_WARNING);
|
||||
});
|
||||
@@ -119,7 +115,7 @@ class CreditsCodename extends funkin.options.OptionsScreen {
|
||||
var opt:GithubIconOption = new GithubIconOption(c, text);
|
||||
if(Options.mainDevs.contains(c.id)) {
|
||||
opt.desc += TU.translate("credits.mainDev");
|
||||
@:privateAccess opt.__text.color = mainDevCol;
|
||||
@:privateAccess opt.__text.color = Flags.MAIN_DEVS_COLOR;
|
||||
}
|
||||
add(opt);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package funkin.menus.ui;
|
||||
|
||||
import funkin.backend.scripting.events.CancellableEvent;
|
||||
import funkin.backend.scripting.events.soundtray.*;
|
||||
import funkin.backend.scripting.Script;
|
||||
import flixel.system.ui.FlxSoundTray;
|
||||
import openfl.text.TextFormat;
|
||||
|
||||
class FunkinSoundTray extends FlxSoundTray
|
||||
{
|
||||
public var script:Script;
|
||||
|
||||
// Neeo wanted them since it would reduce lags for some people if activated - Nex
|
||||
public var callsUpdate:Bool = true;
|
||||
public var callsPostUpdate:Bool = true;
|
||||
|
||||
public function new()
|
||||
{
|
||||
script = Script.create(Paths.script('data/soundTray'));
|
||||
script.setParent(this);
|
||||
script.load();
|
||||
|
||||
script.call("create");
|
||||
super();
|
||||
|
||||
FlxSoundTray.volumeChangeSFX = Paths.sound('menu/volume');
|
||||
FlxSoundTray.volumeUpChangeSFX = null;
|
||||
FlxSoundTray.volumeDownChangeSFX = null;
|
||||
FlxSoundTray.volumeMaxChangeSFX = null;
|
||||
text.setTextFormat(new TextFormat(Paths.font("vcr.ttf")));
|
||||
|
||||
script.call("postCreate");
|
||||
}
|
||||
|
||||
public override function reloadText(checkIfNull:Bool = true, reloadDefaultTextFormat:Bool = true, displayTxt:String = "VOLUME", y:Float = 16)
|
||||
{
|
||||
var event = EventManager.get(SoundTrayTextEvent).recycle(checkIfNull, reloadDefaultTextFormat, displayTxt, y);
|
||||
script.call("reloadText", [event]);
|
||||
if (event.cancelled) return;
|
||||
|
||||
super.reloadText(event.checkIfNull, event.reloadDefaultTextFormat, event.displayTxt, event.y);
|
||||
script.call("postReloadText", [event]);
|
||||
}
|
||||
|
||||
public override function reloadDtf()
|
||||
{
|
||||
var event = new CancellableEvent();
|
||||
script.call("reloadDtf", [event]);
|
||||
if (event.cancelled) return;
|
||||
|
||||
super.reloadDtf();
|
||||
script.call("postReloadDtf");
|
||||
}
|
||||
|
||||
public override function regenerateBarsArray()
|
||||
{
|
||||
var event = new CancellableEvent();
|
||||
script.call("regenerateBarsArray", [event]);
|
||||
if (event.cancelled) return;
|
||||
|
||||
super.regenerateBarsArray();
|
||||
script.call("postRegenerateBarsArray");
|
||||
}
|
||||
|
||||
public override function regenerateBars()
|
||||
{
|
||||
var event = new CancellableEvent();
|
||||
script.call("regenerateBars", [event]);
|
||||
if (event.cancelled) return;
|
||||
|
||||
super.regenerateBars();
|
||||
script.call("postRegenerateBars");
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float)
|
||||
{
|
||||
if (callsUpdate) script.call("update", [elapsed]);
|
||||
super.update(elapsed);
|
||||
if (callsPostUpdate) script.call("postUpdate", [elapsed]);
|
||||
}
|
||||
|
||||
public override function saveSoundPreferences()
|
||||
{
|
||||
var event = new CancellableEvent();
|
||||
script.call("saveSoundPreferences", [event]);
|
||||
if (event.cancelled) return;
|
||||
|
||||
super.saveSoundPreferences();
|
||||
script.call("postSaveSoundPreferences");
|
||||
}
|
||||
|
||||
public override function show(up:Bool = false)
|
||||
{
|
||||
var event = EventManager.get(SoundTrayShowEvent).recycle(up);
|
||||
script.call("show", [event]);
|
||||
if (event.cancelled) return;
|
||||
|
||||
super.show(event.up);
|
||||
script.call("postShow", [event]);
|
||||
}
|
||||
|
||||
public override function screenCenter()
|
||||
{
|
||||
var event = new CancellableEvent();
|
||||
script.call("screenCenter", [event]);
|
||||
if (event.cancelled) return;
|
||||
|
||||
super.screenCenter();
|
||||
script.call("postScreenCenter");
|
||||
}
|
||||
|
||||
private override function __cleanup()
|
||||
{
|
||||
script.call("destroy");
|
||||
script.destroy();
|
||||
super.__cleanup();
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,6 @@ class Config {
|
||||
];
|
||||
|
||||
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
||||
"funkin.backend.scripting.events.PlayAnimContext", // Error: expected member name or ';' after declaration specifiers, Due to define macro from math.h
|
||||
"funkin.backend.scripting.events.sprite.PlayAnimContext", // Error: expected member name or ';' after declaration specifiers, Due to define macro from math.h
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package openfl.display;
|
||||
|
||||
import openfl.utils.ByteArray;
|
||||
|
||||
#if !openfl_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
class GraphicsShader extends Shader
|
||||
{
|
||||
@:glVertexHeader("attribute float openfl_Alpha;
|
||||
attribute vec4 openfl_ColorMultiplier;
|
||||
attribute vec4 openfl_ColorOffset;
|
||||
attribute vec4 openfl_Position;
|
||||
attribute vec2 openfl_TextureCoord;
|
||||
|
||||
varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform mat4 openfl_Matrix;
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;")
|
||||
@:glVertexBody("openfl_Alphav = openfl_Alpha;
|
||||
openfl_TextureCoordv = openfl_TextureCoord;
|
||||
|
||||
if (openfl_HasColorTransform) {
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier;
|
||||
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
|
||||
}
|
||||
|
||||
gl_Position = openfl_Matrix * openfl_Position;")
|
||||
@:glVertexSource("#pragma header
|
||||
|
||||
void main(void) {
|
||||
#pragma body
|
||||
}")
|
||||
@:glFragmentHeader("varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;
|
||||
uniform sampler2D bitmap;")
|
||||
@:glFragmentBody("vec4 color = texture2D (bitmap, openfl_TextureCoordv);
|
||||
|
||||
if (color.a == 0.0) {
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
} else if (openfl_HasColorTransform) {
|
||||
color = vec4(color.rgb / color.a, color.a);
|
||||
|
||||
mat4 colorMultiplier = mat4(0);
|
||||
colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
|
||||
colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
|
||||
colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
|
||||
colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w;
|
||||
|
||||
color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
|
||||
|
||||
if (color.a > 0.0) {
|
||||
gl_FragColor = vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
|
||||
} else {
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
} else {
|
||||
gl_FragColor = color * openfl_Alphav;
|
||||
}")
|
||||
#if emscripten
|
||||
@:glFragmentSource("#pragma header
|
||||
|
||||
void main(void) {
|
||||
#pragma body
|
||||
|
||||
gl_FragColor = gl_FragColor.bgra;
|
||||
}")
|
||||
#else
|
||||
@:glFragmentSource("#pragma header
|
||||
|
||||
void main(void) {
|
||||
#pragma body
|
||||
}")
|
||||
#end
|
||||
public function new(code:ByteArray = null)
|
||||
{
|
||||
super(code);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user