Merge branch 'main' into new-editors

This commit is contained in:
Ne_Eo
2024-09-03 01:23:08 +02:00
15 changed files with 215 additions and 94 deletions
+4 -2
View File
@@ -162,16 +162,18 @@ function postUpdate(elapsed) {
if (enableCameraHacks) {
for(p in strumLines)
p.notes.forEach(function(n) {
if(n.isSustainNote) return; // hacky fix for hold
n.y -= n.y % daPixelZoom;
n.x -= n.x % daPixelZoom;
});
}
var zoom = 1 / daPixelZoom / Math.min(FlxG.scaleMode.scale.x, FlxG.scaleMode.scale.y);
for(e in pixellyCameras) {
if (!e.exists) continue;
e.zoom = 1 / daPixelZoom / Math.min(FlxG.scaleMode.scale.x, FlxG.scaleMode.scale.y);
e.zoom = zoom;
}
for(e in pixellyShaders) {
e.pixelZoom = 1 / daPixelZoom / Math.min(FlxG.scaleMode.scale.x, FlxG.scaleMode.scale.y);
e.pixelZoom = zoom;
}
}
+2 -1
View File
@@ -73,7 +73,8 @@ function confText(text) {
}
function destroy() {
FlxG.cameras.remove(pauseCam);
if(FlxG.cameras.list.contains(pauseCam))
FlxG.cameras.remove(pauseCam);
}
var canDoShit = true;
+3 -4
View File
@@ -105,16 +105,15 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public override function update(elapsed:Float)
{
super.update(elapsed);
if (animateAtlas != null)
animateAtlas.update(elapsed);
// hate how it looks like but hey at least its optimized and fast - Nex
if (!debugMode && isAnimFinished()) {
var name = getAnimName() + '-loop';
if (animation.exists(name))
if (hasAnimation(name))
playAnim(name, false, lastAnimContext);
}
if (animateAtlas != null)
animateAtlas.update(elapsed);
}
public function loadSprite(path:String, Unique:Bool = false, Key:String = null)
+31 -3
View File
@@ -1,10 +1,38 @@
package funkin.backend.scripting;
class ModState extends MusicBeatState {
/**
* Name of HScript file in assets/data/states.
*/
public static var lastName:String = null;
public function new(stateName:String) {
if (stateName != null)
lastName = stateName;
/**
* Last Optional extra data.
*/
public static var lastData:Dynamic = null;
/**
* Optional extra data.
*/
public var data:Dynamic = null;
/**
* ModState Constructor.
* Inherits from MusicBeatState and allows the execution of an HScript from assets/data/states passed via parameters.
*
* @param _stateName Name or path to a HScript file from assets/data/states.
* @param _data Optional extra Dynamic data passed from a previous state (JSON suggested).
*/
public function new(_stateName:String, ?_data:Dynamic) {
if(_stateName != null && _stateName != lastName) {
lastName = _stateName;
lastData = null;
}
if(_data != null)
lastData = _data;
data = lastData;
super(true, lastName);
}
}
+31 -3
View File
@@ -1,10 +1,38 @@
package funkin.backend.scripting;
class ModSubState extends MusicBeatSubstate {
/**
* Name of HScript file in assets/data/states.
*/
public static var lastName:String = null;
public function new(stateName:String) {
if (stateName != null)
lastName = stateName;
/**
* Last Optional extra data.
*/
public static var lastData:Dynamic = null;
/**
* Optional extra data.
*/
public var data:Dynamic = null;
/**
* ModSubState Constructor.
* Inherits from MusicBeatSubstate and allows the execution of an HScript from assets/data/states passed via parameters.
*
* @param _stateName Name or path to a HScript file from assets/data/states.
* @param _data Optional extra Dynamic data passed from a previous state (JSON suggested).
*/
public function new(_stateName:String, ?_data:Dynamic) {
if(_stateName != null && _stateName != lastName) {
lastName = _stateName;
lastData = null;
}
if(_data != null)
lastData = _data;
data = lastData;
super(true, lastName);
}
}
+7 -17
View File
@@ -6,9 +6,15 @@ import lime.utils.AssetType;
import funkin.backend.assets.IModsAssetLibrary;
import funkin.backend.assets.ScriptedAssetLibrary;
/**
* A class that reads the `flags.ini` file, allowing to read settable Flags (customs too).
*/
@:build(funkin.backend.system.macros.FlagMacro.build())
class Flags {
public static var FPS_BUILD_TEXT:String = "Commit ${build} (${commit})";
public static var SONGS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override";
public static var WEEKS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override";
// Internal stuff
@:bypass public static var customFlags:Map<String, String> = [];
@@ -54,20 +60,4 @@ class Flags {
customFlags.set(name, value);
}
}
public static function getCleanLibraryName(e:AssetLibrary) {
var l = e;
if (l is openfl.utils.AssetLibrary) {
var al = cast(l, openfl.utils.AssetLibrary);
@:privateAccess
if (al.__proxy != null) l = al.__proxy;
}
if (l is ScriptedAssetLibrary)
return '${cast(l, ScriptedAssetLibrary).scriptName} (${cast(l, ScriptedAssetLibrary).modName})';
else if (l is IModsAssetLibrary)
return '${cast(l, IModsAssetLibrary).modName}';
else
return Std.string(e);
}
}
@@ -12,8 +12,6 @@ class CodenameBuildField extends TextField {
reload();
}
public function reload() {
var buildText = Flags.FPS_BUILD_TEXT.replace("${build}", Std.string(GitCommitMacro.commitNumber)).replace("${commit}", GitCommitMacro.commitHash);
text = 'Codename Engine ${Main.releaseCycle}\n' + buildText;
}
public function reload()
text = 'Codename Engine ${Main.releaseCycle}\nCommit ${GitCommitMacro.commitNumber} (${GitCommitMacro.commitHash})';
}
@@ -1,28 +1,17 @@
package funkin.backend.system.framerate;
import haxe.ds.Vector;
import openfl.display.Sprite;
import openfl.text.TextField;
import openfl.text.TextFormat;
inline final FRAME_TIME_HISTORY = 20;
class FramerateCounter extends Sprite {
public var fpsNum:TextField;
public var fpsLabel:TextField;
public var fpsHistoryIndex:Int = 0;
public var fpsHistory:Vector<Float>;
public var lastFPS:Float = 0;
public function new() {
super();
fpsHistory = new Vector(FRAME_TIME_HISTORY);
// Initialize to 60 FPS, so that the initial estimation until we get enough data is always reasonable.
for(i in 0...FRAME_TIME_HISTORY) {
fpsHistory[i] = 1000.0 / 60.0;
}
fpsNum = new TextField();
fpsLabel = new TextField();
@@ -43,20 +32,8 @@ class FramerateCounter extends Sprite {
if (alpha <= 0.05) return;
super.__enterFrame(t);
// todo use gpu time
fpsHistory[fpsHistoryIndex] = FlxG.elapsed * 1000;
fpsHistoryIndex = (fpsHistoryIndex + 1) % FRAME_TIME_HISTORY;
// Calculate average CPU time.
// Code based on Godot's FPS counter.
var cpuTime = 0.0;
for(i in 0...FRAME_TIME_HISTORY) {
cpuTime += fpsHistory[i];
}
cpuTime /= FRAME_TIME_HISTORY;
cpuTime = Math.max(0.01, cpuTime); // Prevent unrealistically low values.
fpsNum.text = Std.string(Math.floor(1000.0 / cpuTime));
lastFPS = CoolUtil.fpsLerp(lastFPS, FlxG.elapsed == 0 ? 0 : (1 / FlxG.elapsed), 0.25);
fpsNum.text = Std.string(Math.floor(lastFPS));
fpsLabel.x = fpsNum.x + fpsNum.width;
fpsLabel.y = (fpsNum.y + fpsNum.height) - fpsLabel.height;
}
@@ -34,20 +34,21 @@ class FlagMacro {
switch (field.kind) {
case FVar(type, expr):
if(expr == null)
Context.error('Flags must be initialized', field.pos);
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)
Context.error('Flags must have a type', field.pos);
Context.error('Flag ' + field.name + ' must have a type', field.pos);
}
var parser:Expr = null;
var customCheck:Expr = null;
switch(type) {
case macro: Array<TrimmedString>:
field.kind = FVar(macro: Array<String>, macro expr);
field.kind = FVar(macro: Array<String>, expr);
parser = macro value.split(",").map((e) -> e.trim());
case macro: Array<String>:
parser = macro value.split(",");
@@ -62,29 +63,110 @@ class FlagMacro {
parser = macro value;
case macro: Bool:
parser = macro value == "true" || value == "t" || value == "1";
case TPath({name: "Allow", pack: [], params: params}):
final NONE = 0;
final STRING = 1;
final INT = 2;
var chosenType = NONE;
var values:Array<String> = [];
for(param in params) {
switch(param) {
case TPExpr(e):
switch(e.expr) {
case EConst(CString(s, kind)):
if(chosenType != NONE && chosenType != STRING)
Context.error("Flag " + field.name + " Allow<> can only have one type", field.pos);
chosenType = STRING;
values.push(s);
case EConst(CInt(num)):
if(chosenType != NONE && chosenType != INT)
Context.error("Flag " + field.name + " Allow<> can only have one type", field.pos);
chosenType = INT;
values.push(num);
default:
Context.error("Flag " + field.name + " Allow<> unknown type", field.pos);
}
default:
Context.error("Flag " + field.name + " Unknown type when using Allow<>", field.pos);
}
}
var didFind = false;
for(v in values) {
switch(expr.expr) {
case EConst(CString(s, kind)):
if(v == s)
didFind = true;
case EConst(CInt(num)):
if(v == num)
didFind = true;
default:
}
}
if(!didFind)
Context.error("Flag " + field.name + "'s Allow<> must have a default value that is allowed, " + expr.toString() + " is not allowed", field.pos);
if(chosenType == NONE)
Context.error("Flag " + field.name + "'s Allow<> must have atleast one value", field.pos);
var errorMessage = 'Flag ${field.name} must be one of ${values.join(", ")}';
var checkExpr = macro value == $v{values.shift()};
for(v in values)
checkExpr = macro $checkExpr || value == $v{v};
if(chosenType == STRING) {
parser = macro value;
field.kind = FVar(macro: String, expr);
}
else if(chosenType == INT) {
parser = macro Std.parseInt(value);
field.kind = FVar(macro: Int, expr);
} else {
field.kind = FVar(macro: Any, expr);
}
customCheck = macro @:mergeBlock {
if(name == $v{field.name}) {
if($checkExpr)
$i{field.name} = $parser;
else
throw $v{errorMessage};
return true;
}
}
case TPath({name: "Array", pack: []}):
Context.error('Flags 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", field.pos);
case TPath({name: "Map", pack: []}):
Context.error('Flags cannot be a Map<K, V>', field.pos);
Context.error("Flag " + field.name + " cannot be a Map<K, V>", field.pos);
default:
Context.error("Flags must be either a Bool, Int, String, Array<String>, Array<Bool> or Array<TrimmedString>", field.pos);
Context.error("Flag " + field.name + " must be either a Bool, Int, String, Array<String>, Array<Bool> or Array<TrimmedString>", field.pos);
}
if(parser == null) {
Context.error("Flags must be either a Bool, Int, String, Array<String>, Array<Bool> or Array<TrimmedString>", field.pos);
Context.error("Flag " + field.name + " must be either a Bool, Int, String, Array<String>, Array<Bool> or Array<TrimmedString>", field.pos);
continue;
}
resetExprs.push(macro $i{field.name} = ${expr});
// parse(name: String, value: String)
parserExprs.push(macro @:mergeBlock {
if(name == $v{field.name}) {
$i{field.name} = $parser;
return true;
}
});
if(customCheck != null) {
parserExprs.push(customCheck);
} else {
parserExprs.push(macro @:mergeBlock {
if(name == $v{field.name}) {
$i{field.name} = $parser;
return true;
}
});
}
default:
// nothing
}
@@ -120,6 +202,10 @@ class FlagMacro {
meta: []
});
//var printer = new haxe.macro.Printer();
//trace(printer.printField(fields[fields.length - 2]));
//trace(printer.printField(fields[fields.length - 1]));
return fields;
}
}
@@ -32,8 +32,8 @@ class FunkinParentDisabler extends FlxBasic {
FlxTimer.globalManager._timers = [];
// cameras
__cameras = [for(c in FlxG.cameras.list) if (c.active) c];
for(c in __cameras) c.active = false;
__cameras = [for(c in FlxG.cameras.list) if (!c.paused) c];
for(c in __cameras) c.paused = true;
// sounds
__sounds = [for(s in FlxG.sound.list) if (s.playing) s];
@@ -41,9 +41,6 @@ class FunkinParentDisabler extends FlxBasic {
}
}
public override function update(elapsed:Float) {
@:privateAccess for(c in __cameras) c.updateFlashSpritePosition();
}
public override function draw() {}
public function reset() {
@@ -63,7 +60,7 @@ class FunkinParentDisabler extends FlxBasic {
for(t in __tweens) FlxTween.globalManager._tweens.push(t);
for(t in __timers) FlxTimer.globalManager._timers.push(t);
}
for(c in __cameras) c.active = true;
for(c in __cameras) c.paused = false;
for(s in __sounds) s.play();
}
}
@@ -80,8 +80,6 @@ class SongCreationScreen extends UISubstateWindow {
stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54);
songDataGroup.add(stepsPerBeatStepper);
var voicesUIText:UIText = null;
instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Paths.SOUND_EXT, function (res) {
var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 8, instExplorer.y + 8, res);
instExplorer.members.push(audioPlayer);
@@ -98,11 +96,7 @@ class SongCreationScreen extends UISubstateWindow {
voicesExplorer.uiElement = audioPlayer;
});
songDataGroup.add(voicesExplorer);
voicesUIText = addLabelOn(voicesExplorer, "Vocal Audio File");
voicesUIText.applyMarkup(
"Vocal Audio File $* Required$",
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
addLabelOn(voicesExplorer, "Vocal Audio File");
var menuTitle:UIText;
menuDataGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Menus Data (Freeplay/Story)", 28));
+1 -1
View File
@@ -362,7 +362,7 @@ class PlayState extends MusicBeatState
* Speed at which the game camera zoom lerps to.
*/
public var camGameZoomLerp:Float = 0.05;
/**
* Camera zoom at which the hud lerps to.
*/
+2
View File
@@ -22,6 +22,8 @@ import flixel.math.FlxMath;
import flixel.tweens.FlxEase;
import flixel.util.FlxDestroyUtil;
import funkin.backend.system.Flags;
import funkin.menus.ui.Alphabet;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
+11 -2
View File
@@ -465,8 +465,17 @@ class FreeplaySonglist {
public static function get(useTxt:Bool = true) {
var songList = new FreeplaySonglist();
if (songList.getSongsFromSource(MODS, useTxt))
songList.getSongsFromSource(SOURCE, useTxt);
switch(Flags.SONGS_LIST_MOD_MODE) {
case 'prepend':
songList.getSongsFromSource(MODS, useTxt);
songList.getSongsFromSource(SOURCE, useTxt);
case 'append':
songList.getSongsFromSource(SOURCE, useTxt);
songList.getSongsFromSource(MODS, useTxt);
default /*case 'override'*/:
if (songList.getSongsFromSource(MODS, useTxt))
songList.getSongsFromSource(SOURCE, useTxt);
}
return songList;
}
+15 -5
View File
@@ -199,14 +199,24 @@ class StoryMenuState extends MusicBeatState {
}
}
intendedScore = FunkinSave.getWeekHighscore(weeks[curWeek].name, weeks[curWeek].difficulties[curDifficulty]).score;
intendedScore = FunkinSave.getWeekHighscore(weeks[curWeek].id, weeks[curWeek].difficulties[curDifficulty]).score;
}
public function loadXMLs() {
// CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
var weeks:Array<String> = [];
if (getWeeksFromSource(weeks, MODS))
getWeeksFromSource(weeks, SOURCE);
switch(Flags.WEEKS_LIST_MOD_MODE) {
case 'prepend':
getWeeksFromSource(weeks, MODS);
getWeeksFromSource(weeks, SOURCE);
case 'append':
getWeeksFromSource(weeks, SOURCE);
getWeeksFromSource(weeks, MODS);
default /*case 'override'*/:
if (getWeeksFromSource(weeks, MODS))
getWeeksFromSource(weeks, SOURCE);
}
for(k=>weekName in weeks) {
var week:Access = null;
@@ -344,8 +354,8 @@ class StoryMenuState extends MusicBeatState {
}
typedef WeekData = {
var name:String;
var id:String;
var name:String; // name SHOULD NOT be used for loading week highscores, its just the name on the right side of the week, remember that next time!! - Nex
var id:String; // id IS instead for saving and loading!! - Nex
var sprite:String;
var chars:Array<String>;
var songs:Array<WeekSong>;