diff --git a/.gitignore b/.gitignore
index ad720ba2..2931e2d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,5 @@ latest/windows/haxe/
.haxelib/
pages/
docs/doc.xml
-mods/
\ No newline at end of file
+mods/*
+!mods/readme.txt
diff --git a/PATCHNOTES.md b/PATCHNOTES.md
index 5a1512fe..cda299b5 100644
--- a/PATCHNOTES.md
+++ b/PATCHNOTES.md
@@ -41,9 +41,9 @@
- Reworked Lagless Credits Codename Engine's Contributors Menu
- New Credit Method for GitHub accounts (also lagless) in Mod Credits (First gets the data normally, if fails uses the ratelimited api)
- MANY OG GAME WEEK STUFF!! (still wip and softcoded and also many reworks by nex!)
- - Senpai Cutscene got reworked! (https://github.com/FNF-CNE-Devs/CodenameEngine/assets/87421482/3b3f32fc-78d1-40fa-8398-776554cab1d6)
- - Thorns got reworked with a cool chromatic aberration effect! (https://github.com/FNF-CNE-Devs/CodenameEngine/assets/87421482/73d9f7c5-1c9c-407e-baf0-e8e2c381ecbb)
- - Winter Horrorland got reworked with a bloody vignette effect! (https://github.com/FNF-CNE-Devs/CodenameEngine/assets/87421482/1e10c3e2-32dc-4de2-b31a-d9d577d516da - https://github.com/FNF-CNE-Devs/CodenameEngine/assets/87421482/cad852c3-1ee2-409e-a70e-b3571ab5cf7b)
+ - Senpai Cutscene got reworked! (https://github.com/CodenameCrew/CodenameEngine/assets/87421482/3b3f32fc-78d1-40fa-8398-776554cab1d6)
+ - Thorns got reworked with a cool chromatic aberration effect! (https://github.com/CodenameCrew/CodenameEngine/assets/87421482/73d9f7c5-1c9c-407e-baf0-e8e2c381ecbb)
+ - Winter Horrorland got reworked with a bloody vignette effect! (https://github.com/CodenameCrew/CodenameEngine/assets/87421482/1e10c3e2-32dc-4de2-b31a-d9d577d516da - https://github.com/CodenameCrew/CodenameEngine/assets/87421482/cad852c3-1ee2-409e-a70e-b3571ab5cf7b)
- SMALLER BUT STILL COOL
- Ability to set Controls.hx variables (Being able to block inputs or even trigger them (needs to be in an if statement))
- You can now easily check if a cutscene was played (by default is disabled and makes it auto play or not; Check startCutscene() code for more details in PlayState)
diff --git a/README.md b/README.md
index 6600a2a9..2d95c17b 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Also, the command `.\cne test` uses the source assets folder instead of the expo
## Codename Engine
Codename Engine is a new Friday Night Funkin' Engine aimed at simplifying modding, along with extensiblity and ease of use.
-### Before making issues or need help with something, check our website [HERE](https://fnf-cne-devs.github.io/) (it contains a wiki of how to mod with EXAMPLES, an api, lists of mods made with Codename Engine and more)!!!
+### Before making issues or need help with something, check our website [HERE](https://codename-engine.com/) (it contains a wiki of how to mod with EXAMPLES, an api, lists of mods made with Codename Engine and more)!!!
#### The Engine includes many new features, as seen [here](FEATURES.md)
#### Wanna see the new features added in the most recent update? Click [here](PATCHNOTES.md)
@@ -67,7 +67,7 @@ In the future (when the engine won't be a WIP anymore) we're gonna also publish
- Download and play the engine with its mods and modpacks
- Mod and fork the engine (without using it for illicit purposes)
- Contribute to the engine (for example through *Pull Requests*, *Issues*, etc)
- - Create a sub engine with Codename Engine as **TEMPLATE** with **CREDITS** (for example leaving the *credits menu submenu with the GitHub contributors* and putting the *[main devs](https://github.com/FNF-CNE-Devs)* in a *README* specifying that it's a *sub engine from Codename Engine*)
+ - Create a sub engine with Codename Engine as **TEMPLATE** with **CREDITS** (for example leaving the *credits menu submenu with the GitHub contributors* and putting the *[main devs](https://github.com/CodenameCrew)* in a *README* specifying that it's a *sub engine from Codename Engine*)
- Release excutable mods that use Codename Engine as source (Specifing that uses Codename Engine by for example the same way written above this)
- Release modpacks
diff --git a/art/generateDoc.bat b/art/generateDoc.bat
index e2d35e4f..595314b1 100644
--- a/art/generateDoc.bat
+++ b/art/generateDoc.bat
@@ -1,8 +1,8 @@
@ECHO OFF
cd ..
echo Building Game...
-lime build windows --haxeflag="--macro include('scripting')" --haxeflag="-xml docs/doc.xml" -D DOCUMENTATION --no-output
+lime build windows --haxeflag="-xml docs/doc.xml" -D doc-gen -D DOCUMENTATION --no-output
echo art
echo Generated the api xml file at docs/doc.xml
-echo Please put this in FNF-CNE-Devs.github.io/api-generator/api/doc.xml
\ No newline at end of file
+echo Please put this in codename-website/api-generator/api/doc.xml
\ No newline at end of file
diff --git a/assets/data/scripts/week6-pause.hx b/assets/data/scripts/week6-pause.hx
index 6a4455cd..a80598ce 100644
--- a/assets/data/scripts/week6-pause.hx
+++ b/assets/data/scripts/week6-pause.hx
@@ -92,15 +92,11 @@ function update(elapsed) {
if (!canDoShit) return;
var oldSec = curSelected;
- if (controls.DOWN_P)
- changeSelection(1, false);
- if (controls.UP_P)
- changeSelection(-1);
- if (oldSec != curSelected) {
+ changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
+
+ if (oldSec != curSelected)
FlxG.sound.play(Paths.sound(isThorns ? 'pixel/type' : 'pixel/pixelText'));
- }
-
if (controls.ACCEPT) {
FlxG.sound.play(Paths.sound(isThorns ? 'pixel/ANGRY' : 'pixel/clickText'));
@@ -118,7 +114,7 @@ function update(elapsed) {
}
}
-function changeSelection(change) {
+function changeSelection(change) { // this overrides the function inside of the normal pause btw, so no event gets called - Nex
curSelected += change;
if (curSelected < 0)
diff --git a/commandline/commands/Update.hx b/commandline/commands/Update.hx
index 102cf0cd..77600497 100644
--- a/commandline/commands/Update.hx
+++ b/commandline/commands/Update.hx
@@ -77,17 +77,17 @@ class Update {
if (curHaxeVer[i] < requiredHaxeVer[i]) {
prettyPrint("!! WARNING !!");
Sys.println("Your current Haxe version is outdated.");
- Sys.println('You\'re using ${haxeVer}, while the required version is 4.2.5.');
+ Sys.println('You\'re using ${haxeVer}, while the minimum required version is 4.2.5.');
Sys.println('The engine may not compile with your current version of Haxe.');
- Sys.println('We recommend upgrading to 4.2.5');
+ Sys.println('We recommend upgrading to 4.2.5 or newer');
break;
- } else if (curHaxeVer[i] > requiredHaxeVer[i]) {
+ }/* else if (curHaxeVer[i] > requiredHaxeVer[i]) {
prettyPrint("!! WARNING !!"
+ "\nUsing Haxe 4.3.0 and above is currently not recommended due to lack of testing.");
Sys.println('');
Sys.println('We recommend downgrading back to 4.2.5.');
break;
- }
+ }*/
}
}
diff --git a/libs.xml b/libs.xml
index 4218feeb..f932da09 100644
--- a/libs.xml
+++ b/libs.xml
@@ -5,17 +5,17 @@
-
-
+
+
-
-
-
-
+
+
+
+
-
+
diff --git a/source/funkin/backend/FlxAnimate.hx b/source/funkin/backend/FlxAnimate.hx
index 5f92e9a4..cd06b514 100644
--- a/source/funkin/backend/FlxAnimate.hx
+++ b/source/funkin/backend/FlxAnimate.hx
@@ -7,14 +7,19 @@ import flixel.math.FlxAngle;
import flixel.math.FlxRect;
import flixel.graphics.frames.FlxFrame;
import flixel.math.FlxPoint;
+import openfl.display.BlendMode;
class FlxAnimate extends flxanimate.FlxAnimate {
static var rMatrix = new FlxMatrix();
- override function drawLimb(limb:FlxFrame, _rMatrix:FlxMatrix, ?colorTransform:ColorTransform)
+ override function drawLimb(limb:FlxFrame, _rMatrix:FlxMatrix, ?colorTransform:ColorTransform, ?blendMode:BlendMode)
{
if (alpha == 0 || colorTransform != null && (colorTransform.alphaMultiplier == 0 || colorTransform.alphaOffset == -255) || limb == null || limb.type == EMPTY)
return;
+
+ if (blendMode == null)
+ blendMode = BlendMode.NORMAL;
+
for (camera in cameras)
{
rMatrix.identity();
@@ -69,7 +74,7 @@ class FlxAnimate extends flxanimate.FlxAnimate {
}
rMatrix.translate(_point.x, _point.y);
- camera.drawPixels(limb, null, rMatrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null);
+ camera.drawPixels(limb, null, rMatrix, colorTransform, blendMode, antialiasing, shaderEnabled ? shader : null);
#if FLX_DEBUG
FlxBasic.visibleCount++;
#end
@@ -87,4 +92,4 @@ class FlxAnimate extends flxanimate.FlxAnimate {
// TODO: ACTUAL OPTIMISATION
return true;
}
-}
\ No newline at end of file
+}
diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx
index fa79a95b..2e4df83d 100644
--- a/source/funkin/backend/FunkinSprite.hx
+++ b/source/funkin/backend/FunkinSprite.hx
@@ -22,9 +22,9 @@ enum abstract XMLAnimType(Int)
var BEAT = 1;
var LOOP = 2;
- public static function fromString(str:String, def:XMLAnimType = NONE)
+ public static function fromString(str:String, def:XMLAnimType = XMLAnimType.NONE)
{
- return switch (str.trim().toLowerCase())
+ return switch (StringTools.trim(str).toLowerCase())
{
case "none": NONE;
case "beat" | "onbeat": BEAT;
@@ -328,7 +328,31 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
lastAnimContext = Context;
}
- public function getAnim(name:String):OneOfTwo {
+ public inline function addAnim(name:String, prefix:String, frameRate:Float = 24, ?looped:Bool, ?forced:Bool, ?indices:Array, x:Float = 0, y:Float = 0, animType:XMLAnimType = NONE)
+ {
+ return XMLUtil.addAnimToSprite(this, {
+ name: name,
+ anim: prefix,
+ fps: frameRate,
+ loop: looped == null ? animType == LOOP : looped,
+ animType: animType,
+ x: x,
+ y: y,
+ indices: indices,
+ forced: forced
+ });
+ }
+
+ public inline function removeAnim(name:String)
+ {
+ if (animateAtlas != null)
+ @:privateAccess animateAtlas.anim.animsMap.remove(name);
+ else
+ animation.remove(name);
+ }
+
+ public function getAnim(name:String):OneOfTwo
+ {
if(animateAtlas != null)
return animateAtlas.anim.getByName(name);
return animation.getByName(name);
@@ -341,7 +365,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
return FlxPoint.weak(0, 0);
}
- public inline function hasAnimation(AnimName:String):Bool @:privateAccess
+ public inline function hasAnim(AnimName:String):Bool @:privateAccess
return animateAtlas != null ? (animateAtlas.anim.animsMap.exists(AnimName)
|| animateAtlas.anim.symbolDictionary.exists(AnimName)) : animation.exists(AnimName);
@@ -364,13 +388,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
return animateAtlas != null ? animateAtlas.anim.reversed : animation.curAnim != null ? animation.curAnim.reversed : false;
}
- public inline function removeAnimation(name:String) {
- if (animateAtlas != null)
- @:privateAccess animateAtlas.anim.animsMap.remove(name);
- else
- animation.remove(name);
- }
-
public inline function getNameList():Array {
if (animateAtlas != null)
return [for (name in @:privateAccess animateAtlas.anim.animsMap.keys()) name];
@@ -378,7 +395,8 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
return animation.getNameList();
}
- public inline function stopAnimation() {
+ public inline function stopAnim()
+ {
if (animateAtlas != null)
animateAtlas.anim.pause();
else
@@ -393,6 +411,11 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public inline function isAnimAtEnd() {
return animateAtlas != null ? animateAtlas.anim.isAtEnd : (animation.curAnim != null ? animation.curAnim.isAtEnd : false);
}
+
+ // Backwards compat (the names used to be all different and it sucked, please lets use the same format in the future) - Nex
+ public inline function hasAnimation(AnimName:String) return hasAnim(AnimName);
+ public inline function removeAnimation(name:String) return removeAnim(name);
+ public inline function stopAnimation() return stopAnim();
#end
// Getter / Setters
diff --git a/source/funkin/backend/MusicBeatState.hx b/source/funkin/backend/MusicBeatState.hx
index 35b684e5..6ef650bc 100644
--- a/source/funkin/backend/MusicBeatState.hx
+++ b/source/funkin/backend/MusicBeatState.hx
@@ -93,6 +93,9 @@ class MusicBeatState extends FlxState implements IBeatReceiver
public var scriptsAllowed:Bool = true;
+ public static var lastScriptName:String = null;
+ public static var lastStateName:String = null;
+
public var scriptName:String = null;
public static var skipTransOut:Bool = false;
@@ -108,7 +111,12 @@ class MusicBeatState extends FlxState implements IBeatReceiver
public function new(scriptsAllowed:Bool = true, ?scriptName:String) {
super();
this.scriptsAllowed = #if SOFTCODED_STATES scriptsAllowed #else false #end;
- this.scriptName = scriptName;
+
+ if(lastStateName != (lastStateName = Type.getClassName(Type.getClass(this)))) {
+ lastScriptName = null;
+ }
+ this.scriptName = scriptName != null ? scriptName : lastScriptName;
+ lastScriptName = this.scriptName;
}
function loadScript() {
diff --git a/source/funkin/backend/MusicBeatTransition.hx b/source/funkin/backend/MusicBeatTransition.hx
index 34b02388..58ddc2c5 100644
--- a/source/funkin/backend/MusicBeatTransition.hx
+++ b/source/funkin/backend/MusicBeatTransition.hx
@@ -1,15 +1,24 @@
package funkin.backend;
+import funkin.backend.scripting.events.CancellableEvent;
+import funkin.backend.scripting.events.TransitionCreationEvent;
+import funkin.backend.scripting.Script;
import flixel.tweens.FlxTween;
import flixel.FlxState;
import funkin.backend.utils.FunkinParentDisabler;
class MusicBeatTransition extends MusicBeatSubstate {
+ public static var script:String = "";
+ public var transitionScript:Script;
+
var nextFrameSkip:Bool = false;
public var transitionTween:FlxTween = null;
public var transitionCamera:FlxCamera;
public var newState:FlxState;
+ public var transOut:Bool = false;
+
+ public var allowSkip:Bool = true;
public var blackSpr:FlxSprite;
public var transitionSprite:FunkinSprite;
@@ -20,16 +29,30 @@ class MusicBeatTransition extends MusicBeatSubstate {
public override function create() {
if (newState != null)
- add(new FunkinParentDisabler(true));
+ add(new FunkinParentDisabler(true, false));
transitionCamera = new FlxCamera();
transitionCamera.bgColor = 0;
FlxG.cameras.add(transitionCamera, false);
cameras = [transitionCamera];
- var out = newState != null;
- blackSpr = new FlxSprite(0, out ? -transitionCamera.height : transitionCamera.height).makeGraphic(1, 1, -1);
+ transitionScript = Script.create(Paths.script(script));
+ transitionScript.setParent(this);
+ transitionScript.load();
+
+ var event = EventManager.get(TransitionCreationEvent).recycle(newState != null, newState);
+ transitionScript.call('create', [event]);
+
+ transOut = event.transOut;
+ newState = event.newState;
+
+ if (event.cancelled) {
+ super.create();
+ return;
+ }
+
+ blackSpr = new FlxSprite(0, transOut ? -transitionCamera.height : transitionCamera.height).makeGraphic(1, 1, -1);
blackSpr.scale.set(transitionCamera.width, transitionCamera.height);
blackSpr.color = 0xFF000000;
blackSpr.updateHitbox();
@@ -43,7 +66,7 @@ class MusicBeatTransition extends MusicBeatSubstate {
} else {
transitionSprite.screenCenter();
}
- transitionCamera.flipY = !out;
+ transitionCamera.flipY = !transOut;
add(transitionSprite);
transitionCamera.scroll.y = transitionCamera.height;
@@ -55,41 +78,59 @@ class MusicBeatTransition extends MusicBeatSubstate {
});
super.create();
+ transitionScript.call('postCreate', [event]);
}
public override function update(elapsed:Float) {
+ transitionScript.call('update', [elapsed]);
super.update(elapsed);
if (nextFrameSkip) {
- finish();
- return;
+ var event = new CancellableEvent();
+ transitionScript.call('onSkip', [event]);
+ if (!event.cancelled) {
+ finish();
+ return;
+ }
}
- if (!parent.persistentUpdate && FlxG.keys.pressed.SHIFT) {
+ if (allowSkip && !parent.persistentUpdate && FlxG.keys.pressed.SHIFT) {
// skip
if (newState != null) {
nextFrameSkip = true;
parent.persistentDraw = false;
} else {
- finish();
+ var event = new CancellableEvent();
+ transitionScript.call('onSkip', [event]);
+ if (!event.cancelled) {
+ finish();
+ }
}
}
+ transitionScript.call('postUpdate', [elapsed]);
}
public function finish() {
+ var event = new CancellableEvent();
+ transitionScript.call('onFinish', [event]);
+ if (event.cancelled) return;
+
if (newState != null)
FlxG.switchState(newState);
close();
+
+ transitionScript.call('onPostFinish');
}
public override function destroy() {
- if (transitionTween != null)
- transitionTween.cancel();
+ transitionScript.call('destroy');
+
+ if (transitionTween != null) transitionTween.cancel();
transitionTween = FlxDestroyUtil.destroy(transitionTween);
- if (newState == null && FlxG.cameras.list.contains(transitionCamera))
- FlxG.cameras.remove(transitionCamera);
- else
- transitionCamera.bgColor = 0xFF000000;
+ if (newState == null && FlxG.cameras.list.contains(transitionCamera)) FlxG.cameras.remove(transitionCamera);
+ else transitionCamera.bgColor = 0xFF000000;
+
+ transitionScript.destroy();
super.destroy();
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/assets/ModsFolder.hx b/source/funkin/backend/assets/ModsFolder.hx
index cd1704ed..9d575a1e 100644
--- a/source/funkin/backend/assets/ModsFolder.hx
+++ b/source/funkin/backend/assets/ModsFolder.hx
@@ -93,7 +93,12 @@ class ModsFolder {
public static function getModsList():Array {
var mods:Array = [];
#if MOD_SUPPORT
- for(modFolder in FileSystem.readDirectory(modsPath)) {
+ final modsList:Array = FileSystem.readDirectory(modsPath);
+
+ if (modsList == null || modsList.length <= 0)
+ return mods;
+
+ for (modFolder in modsList) {
if (FileSystem.isDirectory('${modsPath}${modFolder}')) {
mods.push(modFolder);
} else {
@@ -159,4 +164,4 @@ class ModsFolder {
return prepareModLibrary(libName, new ZipFolderLibrary(zipPath, libName, modName), force);
}
#end
-}
\ No newline at end of file
+}
diff --git a/source/funkin/backend/chart/Chart.hx b/source/funkin/backend/chart/Chart.hx
index bd13fd50..a65f45b7 100644
--- a/source/funkin/backend/chart/Chart.hx
+++ b/source/funkin/backend/chart/Chart.hx
@@ -1,6 +1,5 @@
package funkin.backend.chart;
-import funkin.backend.system.Conductor;
import funkin.backend.chart.ChartData;
import flixel.util.FlxColor;
import haxe.io.Path;
@@ -13,12 +12,68 @@ import sys.FileSystem;
using StringTools;
+enum abstract ChartFormat(Int) {
+ var CODENAME = 0;
+ var LEGACY = 1; // also used by many other engines (old Psych, Kade and more) - Nex
+ var VSLICE = 2;
+ var PSYCH_NEW = 3;
+
+ @:to public function toString():String {
+ return switch(cast (this, ChartFormat)) {
+ case CODENAME: "CODENAME";
+ case LEGACY: "LEGACY";
+ case VSLICE: "VSLICE";
+ case PSYCH_NEW: "PSYCH_NEW";
+ }
+ }
+
+ public static function fromString(str:String, def:ChartFormat = ChartFormat.LEGACY) {
+ str = str.toLowerCase();
+ str = StringTools.replace(str, " ", "");
+ str = StringTools.replace(str, "_", "");
+ str = StringTools.replace(str, ".", "");
+
+ if(StringTools.startsWith(str, "psychv1") || StringTools.startsWith(str, "psych1"))
+ return PSYCH_NEW;
+
+ return switch(str) {
+ case "codename" | "codenameengine": CODENAME;
+ case "newpsych" | "psychnew": PSYCH_NEW;
+ default: def;
+ }
+ }
+}
+
class Chart {
/**
* Default background colors for songs without bg color
*/
public inline static var defaultColor:FlxColor = 0xFF9271FD;
+ public static function cleanSongData(data:Dynamic):Dynamic {
+ if (Reflect.hasField(data, "song")) {
+ var field:Dynamic = Reflect.field(data, "song");
+ if (field != null && Type.typeof(field) == TObject) // Cant use Reflect.isObject, because it detects strings for some reason
+ return field;
+ }
+ return data;
+ }
+
+ public static function detectChartFormat(data:Dynamic):ChartFormat {
+ var __temp:Dynamic; // imma reuse this var so the program doesnt have to get values multiple times - Nex
+
+ if ((__temp = data.codenameChart) == true || __temp == "true")
+ return CODENAME;
+
+ if (Reflect.hasField(data, "version") && Reflect.hasField(data, "scrollSpeed"))
+ return VSLICE;
+
+ if ((__temp = cleanSongData(data).format) != null && __temp is String && StringTools.startsWith(__temp, "psych_v1"))
+ return PSYCH_NEW;
+
+ return LEGACY;
+ }
+
public static function loadEventsJson(songName:String) {
var path = Paths.file('songs/${songName.toLowerCase()}/events.json');
var data:Array = null;
@@ -33,8 +88,9 @@ class Chart {
}
public static function loadChartMeta(songName:String, difficulty:String = "normal", fromMods:Bool = true) {
- var metaPath = Paths.file('songs/${songName.toLowerCase()}/meta.json');
- var metaDiffPath = Paths.file('songs/${songName.toLowerCase()}/meta-${difficulty.toLowerCase()}.json');
+ var songNameLower = songName.toLowerCase();
+ var metaPath = Paths.file('songs/${songNameLower}/meta.json');
+ var metaDiffPath = Paths.file('songs/${songNameLower}/meta-${difficulty.toLowerCase()}.json');
var data:ChartMetaData = null;
var fromMods:Bool = fromMods;
@@ -67,7 +123,7 @@ class Chart {
data.setFieldDefault("parsedColor", data.color.getColorFromDynamic().getDefault(defaultColor));
if (data.difficulties.length <= 0) {
- data.difficulties = [for(f in Paths.getFolderContent('songs/${songName.toLowerCase()}/charts/', false, !fromMods)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
+ data.difficulties = [for(f in Paths.getFolderContent('songs/${songNameLower}/charts/', false, !fromMods)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
if (data.difficulties.length == 3) {
var hasHard = false, hasNormal = false, hasEasy = false;
for(d in data.difficulties) {
@@ -116,12 +172,12 @@ class Chart {
Logs.trace('Could not parse chart for song ${songName} ($difficulty): ${Std.string(e)}', ERROR, RED);
}
- if (data != null) {
- /**
- * CHART CONVERSION
- */
- #if REGION
- if (Reflect.hasField(data, "codenameChart") && Reflect.field(data, "codenameChart") == true) {
+ /**
+ * CHART CONVERSION
+ */
+ #if REGION
+ if (data != null) switch (detectChartFormat(data)) {
+ case CODENAME:
// backward compat on events since its caused problems
var eventTypesToString:Map = [
-1 => "HScript Call",
@@ -132,28 +188,23 @@ class Chart {
];
if (data.events == null) data.events = [];
- for (event in cast(data.events, Array)) {
- if (Reflect.hasField(event, "type")) {
- if(event.type != null)
- event.name = eventTypesToString[event.type];
- Reflect.deleteField(event, "type");
- }
+ for (event in cast(data.events, Array)) if (Reflect.hasField(event, "type")) {
+ if (event.type != null)
+ event.name = eventTypesToString[event.type];
+ Reflect.deleteField(event, "type");
}
- // codename chart
base = data;
- } else {
- // base game chart
- FNFLegacyParser.parse(data, base);
- }
- #end
+ case PSYCH_NEW: PsychParser.parse(data, base);
+ case VSLICE: // TODO
+ case LEGACY: FNFLegacyParser.parse(data, base);
}
+ #end
- if (base.meta == null)
- base.meta = loadChartMeta(songName, difficulty, base.fromMods);
+ var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
+ if (base.meta == null) base.meta = loadedMeta;
else {
- var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
- for(field in Reflect.fields(base.meta)) {
+ for (field in Reflect.fields(base.meta)) {
var f = Reflect.field(base.meta, field);
if (f != null)
Reflect.setField(loadedMeta, field, f);
@@ -249,4 +300,4 @@ typedef ChartSaveSettings = {
var ?saveEventsInChart:Bool;
var ?prettyPrint:Bool;
var ?folder:String;
-}
+}
\ No newline at end of file
diff --git a/source/funkin/backend/chart/EventsData.hx b/source/funkin/backend/chart/EventsData.hx
index 9380723d..d1cac615 100644
--- a/source/funkin/backend/chart/EventsData.hx
+++ b/source/funkin/backend/chart/EventsData.hx
@@ -31,7 +31,7 @@ class EventsData {
{name: "Time (Steps)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4},
{name: "Camera", type: TDropDown(['camGame', 'camHUD']), defValue: "camHUD"}
],
- "BPM Change" => [{name: "Target BPM", type: TFloat(1), defValue: 100}],
+ "BPM Change" => [{name: "Target BPM", type: TFloat(1.00, null, 0.001, 3), defValue: 100}],
"Scroll Speed Change" => [
{name: "Tween Speed?", type: TBool, defValue: true},
{name: "New Speed", type: TFloat(0.01, 99, 0.01, 2), defValue: 1.},
diff --git a/source/funkin/backend/chart/FNFLegacyParser.hx b/source/funkin/backend/chart/FNFLegacyParser.hx
index 1a16068f..94fa14eb 100644
--- a/source/funkin/backend/chart/FNFLegacyParser.hx
+++ b/source/funkin/backend/chart/FNFLegacyParser.hx
@@ -6,12 +6,7 @@ import funkin.backend.system.Conductor;
class FNFLegacyParser {
public static function parse(data:Dynamic, result:ChartData) {
// base fnf chart parsing
- var data:SwagSong = data;
- if (Reflect.hasField(data, "song")) {
- var field:Dynamic = Reflect.field(data, "song");
- if (!(field is String))
- data = field;
- }
+ var data:SwagSong = Chart.cleanSongData(data);
result.scrollSpeed = data.speed;
result.stage = data.stage;
@@ -29,7 +24,7 @@ class FNFLegacyParser {
position: "boyfriend",
notes: []
});
- var gfName = data.gf != null ? data.gf : (data.gfVersion != null ? data.gfVersion : "gf");
+ var gfName = data.gf != null ? data.gf : (data.gfVersion != null ? data.gfVersion : (data.player3 != null ? data.player3 : "gf"));
if (!p2isGF && gfName != "none") {
result.strumLines.push({
characters: [gfName],
@@ -142,10 +137,10 @@ class FNFLegacyParser {
if ((swagSection.mustHitSection && strumLine.type == OPPONENT) ||
(!swagSection.mustHitSection && strumLine.type == PLAYER))
sectionNote[1] += 4;
- swagSection.sectionNotes.push(sectionNote);
+ swagSection.sectionNotes.push(sectionNote);
}
}
-
+
return {song: base};
}
@@ -227,6 +222,7 @@ typedef SwagSong =
var player1:String;
var player2:String;
+ var ?player3:String; // alt for gf but idr if it was just used in psych or what but eh whatever maybe its better here anyways - Nex
var ?gf:String;
var ?gfVersion:String;
var validScore:Bool;
diff --git a/source/funkin/backend/chart/PsychParser.hx b/source/funkin/backend/chart/PsychParser.hx
index 1af9962a..a600b141 100644
--- a/source/funkin/backend/chart/PsychParser.hx
+++ b/source/funkin/backend/chart/PsychParser.hx
@@ -6,15 +6,33 @@ import funkin.backend.chart.FNFLegacyParser.SwagSong;
import funkin.backend.system.Conductor;
class PsychParser {
- // Alreadly parsed in sections
+ // Already parsed in sections
public static var ignoreEvents:Array = [
"Camera Movement",
"Alt Animation Toggle",
"BPM Change"
];
- public static function parse(data:Dynamic, result:ChartData)
+ /**
+ * Converts 1.0 Psych charts to older ones
+ */
+ public static function standardize(data:Dynamic, result:ChartData) {
+ var sectionsData:Array = Chart.cleanSongData(data).notes;
+ if (sectionsData == null) return;
+
+ for (section in sectionsData) for (note in cast(section.sectionNotes, Array)) {
+ var gottaHitNote:Bool = section.mustHitSection == (note[1] >= 4);
+ note[1] = (note[1] % 4) + (gottaHitNote ? 4 : 0);
+
+ if (note[3] != null && Std.isOfType(note[3], String))
+ note[3] = Chart.addNoteType(result, note[3]);
+ }
+ }
+
+ public static function parse(data:Dynamic, result:ChartData) {
+ if (Chart.detectChartFormat(data) == PSYCH_NEW) standardize(data, result);
FNFLegacyParser.parse(data, result);
+ }
public static function encode(chart:ChartData):Dynamic {
var base:SwagSong = FNFLegacyParser.__convertToSwagSong(chart);
@@ -32,7 +50,7 @@ class PsychParser {
for (note in strumLine.notes) {
var section:Int = Math.floor(Conductor.getStepForTime(note.time) / Conductor.getMeasureLength());
var swagSection:SwagSection = base.notes[section];
-
+
if (section >= 0 && section < base.notes.length) {
var sectionNote:Array = [
note.time, // TIME
@@ -44,10 +62,10 @@ class PsychParser {
if ((swagSection.mustHitSection && strumLine.type == OPPONENT) ||
(!swagSection.mustHitSection && strumLine.type == PLAYER))
sectionNote[1] += 4;
- swagSection.sectionNotes.push(sectionNote);
+ swagSection.sectionNotes.push(sectionNote);
}
}
-
+
var groupedEvents:Array> = [];
var __last:Array = null;
var __lastTime:Float = Math.NaN;
@@ -91,7 +109,7 @@ class PsychParser {
FlxMath.roundDecimal(event.params[1]/chart.scrollSpeed, 2), // SCROLL SPEED MULTIPLER
FlxMath.roundDecimal( // TIME
event.params[0] ? // IS TWEENED?
- (Conductor.getTimeForStep(eventStep+event.params[2]) - Conductor.getTimeForStep(eventStep))/1000
+ (Conductor.getTimeForStep(eventStep+event.params[2]) - Conductor.getTimeForStep(eventStep))/1000
: 0, 2)
]);
default:
@@ -106,7 +124,7 @@ class PsychParser {
for (psychEvent in psychEvents)
for (i in 1...3) // Turn both vals into strings
- if (!(psychEvent[i] is String))
+ if (!(psychEvent[i] is String))
psychEvent[i] = Std.string(psychEvent[i]);
base.events.push([events[0].time, psychEvents]);
diff --git a/source/funkin/backend/scripting/HScript.hx b/source/funkin/backend/scripting/HScript.hx
index 37aaaa95..0597a32f 100644
--- a/source/funkin/backend/scripting/HScript.hx
+++ b/source/funkin/backend/scripting/HScript.hx
@@ -74,8 +74,10 @@ class HScript extends Script {
var code = Assets.getText(p);
var expr:Expr = null;
try {
- if (code != null && code.trim() != "")
+ if (code != null && code.trim() != "") {
+ parser.line = 1; // fun fact: this is all you need to reuse a parser without issues. all the other vars get reset on parse.
expr = parser.parseString(code, cl.join("/") + "." + hxExt);
+ }
} catch(e:Error) {
_errorHandler(e);
} catch(e) {
diff --git a/source/funkin/backend/scripting/events/TransitionCreationEvent.hx b/source/funkin/backend/scripting/events/TransitionCreationEvent.hx
new file mode 100644
index 00000000..e72a56c2
--- /dev/null
+++ b/source/funkin/backend/scripting/events/TransitionCreationEvent.hx
@@ -0,0 +1,18 @@
+package funkin.backend.scripting.events;
+
+import flixel.FlxState;
+
+/**
+ * CANCEL this event to prevent default behaviour!
+ */
+final class TransitionCreationEvent extends CancellableEvent {
+ /**
+ * If the transition is going out into another state
+ */
+ public var transOut:Bool;
+
+ /**
+ * The state that is about to be loaded (only on trans out)
+ */
+ public var newState:FlxState;
+}
\ No newline at end of file
diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx
index cbfe8846..152ca6c3 100644
--- a/source/funkin/backend/shaders/FunkinShader.hx
+++ b/source/funkin/backend/shaders/FunkinShader.hx
@@ -192,7 +192,11 @@ 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);
+
+ var useBackCompat:Bool = true;
+ for (regex in ShaderTemplates.vertBackCompatVarList) if (!regex.match(value)) useBackCompat = false;
+
+ value = value.replace("#pragma header", useBackCompat ? ShaderTemplates.vertHeaderBackCompat : ShaderTemplates.vertHeader).replace("#pragma body", useBackCompat ? ShaderTemplates.vertBodyBackCompat : ShaderTemplates.vertBody);
if (value != __glVertexSource)
{
__glSourceDirty = true;
@@ -625,6 +629,38 @@ if(hasColorTransform) {
gl_Position = openfl_Matrix * openfl_Position;";
+// TODO: make this ignore comments
+public static final vertBackCompatVarList:Array = [
+ ~/attribute float alpha/,
+ ~/attribute vec4 colorMultiplier/,
+ ~/attribute vec4 colorOffset/,
+ ~/uniform bool hasColorTransform/
+];
+
+public static final vertHeaderBackCompat:String = "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;";
+
+public static final vertBodyBackCompat:String = "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;";
public static final defaultVertexSource:String = "#pragma header
diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx
index 6705b93c..5795c4a1 100644
--- a/source/funkin/backend/system/Main.hx
+++ b/source/funkin/backend/system/Main.hx
@@ -49,6 +49,10 @@ class Main extends Sprite
public static var game:FunkinGame;
+ /**
+ * The time since the game was focused last time in seconds.
+ */
+ public static var timeSinceFocus(get, never):Float;
public static var time:Int = 0;
// You can pretty much ignore everything from here on - your code should go in your states.
@@ -152,10 +156,14 @@ class Main extends Sprite
Conductor.init();
AudioSwitchFix.init();
EventManager.init();
+ FlxG.signals.focusGained.add(onFocus);
FlxG.signals.preStateSwitch.add(onStateSwitch);
FlxG.signals.postStateSwitch.add(onStateSwitchPost);
FlxG.mouse.useSystemCursor = true;
+ #if DARK_MODE_WINDOW
+ if(funkin.backend.utils.NativeAPI.hasVersion("Windows 10")) funkin.backend.utils.NativeAPI.redrawWindowHeader();
+ #end
ModsFolder.init();
#if MOD_SUPPORT
@@ -187,6 +195,10 @@ class Main extends Sprite
{asset: diamond, width: 32, height: 32}, new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
}
+ public static function onFocus() {
+ _tickFocused = FlxG.game.ticks;
+ }
+
private static function onStateSwitch() {
scaleMode.resetSize();
}
@@ -206,4 +218,9 @@ class Main extends Sprite
MemoryUtil.clearMajor();
}
+
+ private static var _tickFocused:Float = 0;
+ public static function get_timeSinceFocus():Float {
+ return (FlxG.game.ticks - _tickFocused) / 1000;
+ }
}
diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx
index 14c40aff..a5011d6d 100644
--- a/source/funkin/backend/system/MainState.hx
+++ b/source/funkin/backend/system/MainState.hx
@@ -53,6 +53,7 @@ class MainState extends FlxState {
}
#end
+ MusicBeatTransition.script = "";
Main.refreshAssets();
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder);
DiscordUtil.init();
diff --git a/source/funkin/backend/system/macros/NewHaxeWarning.hx b/source/funkin/backend/system/macros/NewHaxeWarning.hx
index ee467955..aa3da3d9 100644
--- a/source/funkin/backend/system/macros/NewHaxeWarning.hx
+++ b/source/funkin/backend/system/macros/NewHaxeWarning.hx
@@ -1,16 +1,17 @@
package funkin.backend.system.macros;
#if macro
+@:dox(hide)
class NewHaxeWarning {
public static function warn() {
- #if (haxe >= "4.3.0")
+ /*#if (haxe >= "4.3.0")
Sys.println("====================");
Sys.println("[ WARNING ]");
Sys.println("Compiling with Haxe 4.3.0 isnt fully recommended yet, but it does work.");
Sys.println("We recommend building the project using Haxe 4.2.5.");
Sys.println("You can download it here -> https://haxe.org/download/version/4.2.5/");
Sys.println("====================");
- #end
+ #end*/
}
}
#end
\ No newline at end of file
diff --git a/source/funkin/backend/utils/AudioAnalyzer.hx b/source/funkin/backend/utils/AudioAnalyzer.hx
index b5208fa3..03fc3967 100644
--- a/source/funkin/backend/utils/AudioAnalyzer.hx
+++ b/source/funkin/backend/utils/AudioAnalyzer.hx
@@ -4,7 +4,7 @@ import flixel.sound.FlxSound;
import lime.media.AudioBuffer;
// Thank you yosh :DDD -lunar
-// (https://github.com/FNF-CNE-Devs/YoshiCrafterEngine/blob/main/source/WaveformSprite.hx)
+// (https://github.com/CodenameCrew/YoshiCrafterEngine/blob/main/source/WaveformSprite.hx)
class AudioAnalyzer {
public var buffer:AudioBuffer;
var __peakByte:Float = 0;
diff --git a/source/funkin/backend/utils/FunkinParentDisabler.hx b/source/funkin/backend/utils/FunkinParentDisabler.hx
index 29e076be..bd8b1684 100644
--- a/source/funkin/backend/utils/FunkinParentDisabler.hx
+++ b/source/funkin/backend/utils/FunkinParentDisabler.hx
@@ -19,9 +19,11 @@ class FunkinParentDisabler extends FlxBasic {
var __timers:Array;
var __sounds:Array;
var __replaceUponDestroy:Bool;
- public function new(replaceUponDestroy:Bool = false) {
+ var __restoreUponDestroy:Bool;
+ public function new(replaceUponDestroy:Bool = false, restoreUponDestroy:Bool = true) {
super();
__replaceUponDestroy = replaceUponDestroy;
+ __restoreUponDestroy = restoreUponDestroy;
@:privateAccess {
// tweens
__tweens = FlxTween.globalManager._tweens.copy();
@@ -53,6 +55,11 @@ class FunkinParentDisabler extends FlxBasic {
public override function destroy() {
super.destroy();
@:privateAccess {
+ if (!__restoreUponDestroy) {
+ for(t in __tweens) { t.cancel(); t.destroy(); };
+ for(t in __timers) { t.cancel(); t.destroy(); };
+ return;
+ }
if (__replaceUponDestroy) {
FlxTween.globalManager._tweens = __tweens;
FlxTimer.globalManager._timers = __timers;
diff --git a/source/funkin/backend/utils/NativeAPI.hx b/source/funkin/backend/utils/NativeAPI.hx
index 78f0b38d..a8753a8a 100644
--- a/source/funkin/backend/utils/NativeAPI.hx
+++ b/source/funkin/backend/utils/NativeAPI.hx
@@ -3,6 +3,7 @@ package funkin.backend.utils;
import funkin.backend.utils.native.*;
import flixel.util.typeLimit.OneOfTwo;
import flixel.util.typeLimit.OneOfThree;
+import flixel.util.FlxColor;
/**
* Class for functions that talk to a lower level than haxe, such as message boxes, and more.
@@ -84,12 +85,86 @@ class NativeAPI {
#end
}
+ /**
+ * WINDOW COLOR MODE FUNCTIONS.
+ */
+
+ /**
+ * Switch the window's color mode to dark or light mode.
+ */
public static function setDarkMode(title:String, enable:Bool) {
#if windows
+ if(title == null) title = lime.app.Application.current.window.title;
Windows.setDarkMode(title, enable);
#end
}
+ /**
+ * Switch the window's color to any color.
+ *
+ * WARNING: This is exclusive to windows 11 users, unfortunately.
+ *
+ * NOTE: Setting the color to 0x00000000 (FlxColor.TRANSPARENT) will set the border (must have setBorder on) invisible.
+ */
+ public static function setWindowBorderColor(title:String, color:FlxColor, setHeader:Bool = true, setBorder:Bool = true) {
+ #if windows
+ if(title == null) title = lime.app.Application.current.window.title;
+ Windows.setWindowBorderColor(title, [color.red, color.green, color.blue, color.alpha], setHeader, setBorder);
+ #end
+ }
+
+ /**
+ * Resets the window's border color to the default one.
+ *
+ * WARNING: This is exclusive to windows 11 users, unfortunately.
+ **/
+ public static function resetWindowBorderColor(title:String, setHeader:Bool = true, setBorder:Bool = true) {
+ #if windows
+ if(title == null) title = lime.app.Application.current.window.title;
+ Windows.setWindowBorderColor(title, [-1, -1, -1, -1], setHeader, setBorder);
+ #end
+ }
+
+ /**
+ * Switch the window's title text to any color.
+ *
+ * WARNING: This is exclusive to windows 11 users, unfortunately.
+ */
+ public static function setWindowTitleColor(title:String, color:FlxColor) {
+ #if windows
+ if(title == null) title = lime.app.Application.current.window.title;
+ Windows.setWindowTitleColor(title, [color.red, color.green, color.blue, color.alpha]);
+ #end
+ }
+
+ /**
+ * Resets the window's title color to the default one.
+ *
+ * WARNING: This is exclusive to windows 11 users, unfortunately.
+ **/
+ public static function resetWindowTitleColor(title:String) {
+ #if windows
+ if(title == null) title = lime.app.Application.current.window.title;
+ Windows.setWindowTitleColor(title, [-1, -1, -1, -1]);
+ #end
+ }
+
+ /**
+ * Forces the window header to redraw, causes a small visual jitter so use it sparingly.
+ */
+ public static function redrawWindowHeader() {
+ #if windows
+ flixel.FlxG.stage.window.borderless = true;
+ flixel.FlxG.stage.window.borderless = false;
+ #end
+ }
+
+ /**
+ * Can be used to check if your using a specific version of an OS (or if your using a certain OS).
+ */
+ public static function hasVersion(vers:String)
+ return lime.system.System.platformLabel.toLowerCase().indexOf(vers.toLowerCase()) != -1;
+
/**
* Shows a message box
*/
diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx
index afb8ae04..a074f97d 100644
--- a/source/funkin/backend/utils/XMLUtil.hx
+++ b/source/funkin/backend/utils/XMLUtil.hx
@@ -143,6 +143,8 @@ class XMLUtil {
var graphicSizey:Null = Std.parseInt(node.att.graphicSizey);
if (graphicSizey.isNotNull()) spr.setGraphicSize(0, graphicSizey);
}
+ if (node.has.flipX) spr.flipX = node.att.flipX == "true";
+ if (node.has.flipY) spr.flipY = node.att.flipY == "true";
if (node.has.updateHitbox && node.att.updateHitbox == "true") spr.updateHitbox();
if (node.has.zoomfactor)
@@ -164,19 +166,17 @@ class XMLUtil {
if(node.hasNode.anim) {
for(anim in node.nodes.anim)
addXMLAnimation(spr, anim);
- } else {
- if (spr.frames != null && spr.frames.frames != null) {
- addAnimToSprite(spr, {
- name: "idle",
- anim: null,
- fps: 24,
- loop: spr.spriteAnimType == LOOP,
- animType: spr.spriteAnimType,
- x: 0,
- y: 0,
- indices: [for(i in 0...spr.frames.frames.length) i]
- });
- }
+ } else if (spr.frames != null && spr.frames.frames != null) {
+ addAnimToSprite(spr, {
+ name: "idle",
+ anim: null,
+ fps: 24,
+ loop: spr.spriteAnimType == LOOP,
+ animType: spr.spriteAnimType,
+ x: 0,
+ y: 0,
+ indices: [for(i in 0...spr.frames.frames.length) i]
+ });
}
return spr;
@@ -238,16 +238,17 @@ class XMLUtil {
if(animData.anim == null)
return MISSING_PROPERTY;
- if (animData.indices.length > 0)
+ if (animData.indices != null && animData.indices.length > 0)
animateAnim.addBySymbolIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop);
else
animateAnim.addBySymbol(animData.name, animData.anim, animData.fps, animData.loop);
} else {
- if (animData.anim == null && animData.indices.length > 0)
- sprite.animation.add(animData.name, animData.indices, animData.fps, animData.loop);
- else if (animData.indices.length > 0)
- sprite.animation.addByIndices(animData.name, animData.anim, animData.indices, "", animData.fps, animData.loop);
- else
+ if (animData.indices != null && animData.indices.length > 0) {
+ if (animData.anim == null)
+ sprite.animation.add(animData.name, animData.indices, animData.fps, animData.loop);
+ else
+ sprite.animation.addByIndices(animData.name, animData.anim, animData.indices, "", animData.fps, animData.loop);
+ } else
sprite.animation.addByPrefix(animData.name, animData.anim, animData.fps, animData.loop);
}
@@ -393,4 +394,4 @@ typedef BeatAnim = {
interface IXMLEvents {
public function onPropertySet(property:String, value:Dynamic):Void;
-}
\ No newline at end of file
+}
diff --git a/source/funkin/backend/utils/native/Windows.hx b/source/funkin/backend/utils/native/Windows.hx
index a536c75f..8653d2e8 100644
--- a/source/funkin/backend/utils/native/Windows.hx
+++ b/source/funkin/backend/utils/native/Windows.hx
@@ -134,13 +134,57 @@ class Windows {
HWND window = FindWindowA(NULL, title.c_str());
// Look for child windows if top level aint found
if (window == NULL) window = FindWindowExA(GetActiveWindow(), NULL, NULL, title.c_str());
+ // If still not found, try to get the active window
+ if (window == NULL) window = GetActiveWindow();
+ if (window == NULL) return;
- if (window != NULL && S_OK != DwmSetWindowAttribute(window, 19, &darkMode, sizeof(darkMode))) {
+ if (S_OK != DwmSetWindowAttribute(window, 19, &darkMode, sizeof(darkMode))) {
DwmSetWindowAttribute(window, 20, &darkMode, sizeof(darkMode));
}
+ UpdateWindow(window);
')
public static function setDarkMode(title:String, enable:Bool) {}
+ @:functionCode('
+ HWND window = FindWindowA(NULL, title.c_str());
+ if (window == NULL) window = FindWindowExA(GetActiveWindow(), NULL, NULL, title.c_str());
+ if (window == NULL) window = GetActiveWindow();
+ if (window == NULL) return;
+
+ COLORREF finalColor;
+ if(color[0] == -1 && color[1] == -1 && color[2] == -1 && color[3] == -1) { // bad fix, I know :sob:
+ finalColor = 0xFFFFFFFF; // Default border
+ } else if(color[3] == 0) {
+ finalColor = 0xFFFFFFFE; // No border (must have setBorder as true)
+ } else {
+ finalColor = RGB(color[0], color[1], color[2]); // Use your custom color
+ }
+
+ if(setHeader) DwmSetWindowAttribute(window, 35, &finalColor, sizeof(COLORREF));
+ if(setBorder) DwmSetWindowAttribute(window, 34, &finalColor, sizeof(COLORREF));
+
+ UpdateWindow(window);
+ ')
+ public static function setWindowBorderColor(title:String, color:Array, setHeader:Bool = true, setBorder:Bool = true) {}
+
+ @:functionCode('
+ HWND window = FindWindowA(NULL, title.c_str());
+ if (window == NULL) window = FindWindowExA(GetActiveWindow(), NULL, NULL, title.c_str());
+ if (window == NULL) window = GetActiveWindow();
+ if (window == NULL) return;
+
+ COLORREF finalColor;
+ if(color[0] == -1 && color[1] == -1 && color[2] == -1 && color[3] == -1) { // bad fix, I know :sob:
+ finalColor = 0xFFFFFFFF; // Default border
+ } else {
+ finalColor = RGB(color[0], color[1], color[2]); // Use your custom color
+ }
+
+ DwmSetWindowAttribute(window, 36, &finalColor, sizeof(COLORREF));
+ UpdateWindow(window);
+ ')
+ public static function setWindowTitleColor(title:String, color:Array) {}
+
@:functionCode('
// https://stackoverflow.com/questions/15543571/allocconsole-not-displaying-cout
diff --git a/source/funkin/editors/EditorPicker.hx b/source/funkin/editors/EditorPicker.hx
index 7056305f..f210ba92 100644
--- a/source/funkin/editors/EditorPicker.hx
+++ b/source/funkin/editors/EditorPicker.hx
@@ -22,11 +22,13 @@ class EditorPicker extends MusicBeatSubstate {
iconID: 2,
state: null
},
+ #if debug
{
name: "UI Debug State",
iconID: 3,
state: UIDebugState
},
+ #end
{
name: "Debug Options",
iconID: 4,
@@ -214,4 +216,4 @@ class EditorPickerOption extends FlxTypedSpriteGroup {
callback();
});
}
-}
\ No newline at end of file
+}
diff --git a/source/funkin/editors/charter/ChartDataScreen.hx b/source/funkin/editors/charter/ChartDataScreen.hx
index b4592d05..c629c7de 100644
--- a/source/funkin/editors/charter/ChartDataScreen.hx
+++ b/source/funkin/editors/charter/ChartDataScreen.hx
@@ -23,6 +23,7 @@ class ChartDataScreen extends UISubstateWindow {
public override function create() {
FlxG.sound.music.pause();
Charter.instance.vocals.pause();
+ for (strumLine in Charter.instance.strumLines.members) strumLine.vocals.pause();
winTitle = 'Chart Properties';
winWidth = 420; winHeight = 230; // guys look, the funny numbers!
diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx
index 99f6f690..4ec2f299 100644
--- a/source/funkin/editors/charter/Charter.hx
+++ b/source/funkin/editors/charter/Charter.hx
@@ -341,6 +341,7 @@ class Charter extends UIState {
null,
{
label: "↑ Speed 25%",
+ keybind: [PERIOD],
onSelect: _playback_speed_raise
},
{
@@ -349,6 +350,7 @@ class Charter extends UIState {
},
{
label: "↓ Speed 25%",
+ keybind: [COMMA],
onSelect: _playback_speed_lower
},
null,
@@ -362,6 +364,22 @@ class Charter extends UIState {
keybind: [D],
onSelect: _playback_forward
},
+ {
+ label: "Go to start of section",
+ keybind: [SHIFT, S],
+ onSelect: _playback_section_start
+ },
+ null,
+ {
+ label: "Go back a step",
+ keybind: [W],
+ onSelect: _playback_back_step
+ },
+ {
+ label: "Go forward a step",
+ keybind: [S],
+ onSelect: _playback_forward_step
+ },
null,
{
label: "Metronome",
@@ -1338,7 +1356,7 @@ class Charter extends UIState {
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == "normal" ? "" : '-${__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',
@@ -1579,6 +1597,18 @@ class Charter extends UIState {
if (FlxG.sound.music.playing) return;
Conductor.songPosition += (Conductor.beatsPerMeasure * __crochet);
}
+ function _playback_section_start(_) {
+ if(FlxG.sound.music.playing) return;
+ Conductor.songPosition = (Conductor.beatsPerMeasure * (60000 / Conductor.bpm)) * curMeasure;
+ }
+ function _playback_back_step(_) {
+ if (FlxG.sound.music.playing) return;
+ Conductor.songPosition -= Conductor.stepCrochet;
+ }
+ function _playback_forward_step(_) {
+ if (FlxG.sound.music.playing) return;
+ Conductor.songPosition += Conductor.stepCrochet;
+ }
function _song_start(_) {
if (FlxG.sound.music.playing) return;
Conductor.songPosition = 0;
diff --git a/source/funkin/editors/charter/CharterEventScreen.hx b/source/funkin/editors/charter/CharterEventScreen.hx
index c1633303..307aa907 100644
--- a/source/funkin/editors/charter/CharterEventScreen.hx
+++ b/source/funkin/editors/charter/CharterEventScreen.hx
@@ -44,6 +44,7 @@ class CharterEventScreen extends UISubstateWindow {
FlxG.sound.music.pause(); // prevent the song from continuing
Charter.instance.vocals.pause();
+ for (strumLine in Charter.instance.strumLines.members) strumLine.vocals.pause();
events = chartEvent.events.copy();
diff --git a/source/funkin/editors/charter/CharterMetaDataScreen.hx b/source/funkin/editors/charter/CharterMetaDataScreen.hx
index 900cc721..4179dfc7 100644
--- a/source/funkin/editors/charter/CharterMetaDataScreen.hx
+++ b/source/funkin/editors/charter/CharterMetaDataScreen.hx
@@ -40,6 +40,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
FlxG.sound.music.pause();
Charter.instance.vocals.pause();
+ for (strumLine in Charter.instance.strumLines.members) strumLine.vocals.pause();
function addLabelOn(ui:UISprite, text:String)
add(new UIText(ui.x, ui.y - 24, 0, text));
diff --git a/source/funkin/editors/charter/CharterNote.hx b/source/funkin/editors/charter/CharterNote.hx
index 1d130842..d039962e 100644
--- a/source/funkin/editors/charter/CharterNote.hx
+++ b/source/funkin/editors/charter/CharterNote.hx
@@ -76,7 +76,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
public function updatePos(step:Float, id:Int, susLength:Float = 0, ?type:Int = 0, ?strumLine:CharterStrumline = null) {
this.step = step;
this.id = id;
- this.susLength = susLength;
+ this.susLength = Math.max(susLength, 0);
this.type = type;
if (strumLine != null) this.strumLine = strumLine;
diff --git a/source/funkin/editors/charter/CharterNoteTypesList.hx b/source/funkin/editors/charter/CharterNoteTypesList.hx
index b1cc28b6..827cfa7e 100644
--- a/source/funkin/editors/charter/CharterNoteTypesList.hx
+++ b/source/funkin/editors/charter/CharterNoteTypesList.hx
@@ -13,6 +13,7 @@ class CharterNoteTypesList extends UISubstateWindow {
public override function create() {
FlxG.sound.music.pause();
Charter.instance.vocals.pause();
+ for (strumLine in Charter.instance.strumLines.members) strumLine.vocals.pause();
winTitle = 'Note Types List Editor';
winWidth = 380; winHeight = 390;
diff --git a/source/funkin/editors/charter/CharterStrumlineScreen.hx b/source/funkin/editors/charter/CharterStrumlineScreen.hx
index 26696251..d9298430 100644
--- a/source/funkin/editors/charter/CharterStrumlineScreen.hx
+++ b/source/funkin/editors/charter/CharterStrumlineScreen.hx
@@ -52,6 +52,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
FlxG.sound.music.pause();
Charter.instance.vocals.pause();
+ for (strumLine in Charter.instance.strumLines.members) strumLine.vocals.pause();
super.create();
diff --git a/source/funkin/editors/ui/UISlider.hx b/source/funkin/editors/ui/UISlider.hx
index 277c7b95..091886c1 100644
--- a/source/funkin/editors/ui/UISlider.hx
+++ b/source/funkin/editors/ui/UISlider.hx
@@ -26,6 +26,7 @@ class UISlider extends UISprite {
public var value(default, set):Float = 0;
public function set_value(newVal:Float):Float {
+ newVal = FlxMath.bound(newVal, segments[0].start, segments[segments.length-1].end);
__barProgress = __calcProgress(newVal); if (onChange != null) onChange(newVal);
if (valueStepper != null) valueStepper.value = newVal;
return value = newVal;
diff --git a/source/funkin/editors/ui/UITextBox.hx b/source/funkin/editors/ui/UITextBox.hx
index 3aa97fd2..b12c1770 100644
--- a/source/funkin/editors/ui/UITextBox.hx
+++ b/source/funkin/editors/ui/UITextBox.hx
@@ -119,12 +119,21 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
case END:
position = label.text.length;
case V:
- if (modifier == KeyModifier.LEFT_CTRL || modifier == KeyModifier.RIGHT_CTRL) {
- // paste
- var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
- if (data != null)
- onTextInput(data);
- }
+ // Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
+ // but somehow it gave a int outside of the KeyModifier's range :sob:
+ // apparently there is a boolean that just checks for you. yw :D
+
+ // if we are not holding ctrl, ignore
+ if (!modifier.ctrlKey) return;
+ // we pasting
+ var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
+ if (data != null) onTextInput(data);
+ case C:
+ // if we are not holding ctrl, ignore
+ if (!modifier.ctrlKey) return;
+
+ // copying
+ Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
default:
// nothing
}
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index 1e1f3819..bb458994 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -686,7 +686,7 @@ class PlayState extends MusicBeatState
startingPos,
strumLine.strumScale == null ? 1 : strumLine.strumScale,
strumLine.type == 2 || (!coopMode && !((strumLine.type == 1 && !opponentMode) || (strumLine.type == 0 && opponentMode))),
- strumLine.type != 1, coopMode ? (strumLine.type == 1 ? controlsP1 : controlsP2) : controls,
+ strumLine.type != 1, coopMode ? ((strumLine.type == 1) != opponentMode ? controlsP1 : controlsP2) : controls,
strumLine.vocalsSuffix
);
strLine.cameras = [camHUD];
diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx
index 3e88bc7c..b5880acc 100644
--- a/source/funkin/game/StrumLine.hx
+++ b/source/funkin/game/StrumLine.hx
@@ -292,6 +292,7 @@ class StrumLine extends FlxTypedGroup {
super.destroy();
if(startingPos != null)
startingPos.put();
+ notes = FlxDestroyUtil.destroy(notes);
}
/**
diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx
index 7ca66b02..ec9cbeb9 100644
--- a/source/funkin/menus/FreeplayState.hx
+++ b/source/funkin/menus/FreeplayState.hx
@@ -2,6 +2,7 @@ package funkin.menus;
import funkin.backend.chart.Chart;
import funkin.backend.chart.ChartData.ChartMetaData;
+import funkin.backend.system.Conductor;
import haxe.io.Path;
import openfl.text.TextField;
import flixel.text.FlxText;
@@ -216,7 +217,7 @@ class FreeplayState extends MusicBeatState
lerpScore = intendedScore;
if (canSelect) {
- changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0));
+ changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
changeDiff((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0));
changeCoopMode((FlxG.keys.justPressed.TAB ? 1 : 0));
// putting it before so that its actually smooth
@@ -239,7 +240,11 @@ class FreeplayState extends MusicBeatState
autoplayElapsed += elapsed;
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay || FlxG.keys.justPressed.SPACE)) {
if (curPlayingInst != (curPlayingInst = Paths.inst(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]))) {
- var huh:Void->Void = function() FlxG.sound.playMusic(curPlayingInst, 0);
+ var huh:Void->Void = function()
+ {
+ FlxG.sound.playMusic(curPlayingInst, 0);
+ Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat);
+ }
if(!disableAsyncLoading) Main.execAsync(huh);
else huh();
}
diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx
index 448552ac..968494f1 100644
--- a/source/funkin/menus/MainMenuState.hx
+++ b/source/funkin/menus/MainMenuState.hx
@@ -109,11 +109,12 @@ class MainMenuState extends MusicBeatState
*/
}
- if (controls.UP_P)
- changeItem(-1);
+ var upP = controls.UP_P;
+ var downP = controls.DOWN_P;
+ var scroll = FlxG.mouse.wheel;
- if (controls.DOWN_P)
- changeItem(1);
+ if (upP || downP || scroll != 0) // like this we wont break mods that expect a 0 change event when calling sometimes - Nex
+ changeItem((upP ? -1 : 0) + (downP ? 1 : 0) - scroll);
if (controls.BACK)
FlxG.switchState(new TitleState());
@@ -127,9 +128,7 @@ class MainMenuState extends MusicBeatState
#end
if (controls.ACCEPT)
- {
selectItem();
- }
}
super.update(elapsed);
diff --git a/source/funkin/menus/ModSwitchMenu.hx b/source/funkin/menus/ModSwitchMenu.hx
index 1b23b9d2..eac992dc 100644
--- a/source/funkin/menus/ModSwitchMenu.hx
+++ b/source/funkin/menus/ModSwitchMenu.hx
@@ -39,7 +39,7 @@ class ModSwitchMenu extends MusicBeatSubstate {
public override function update(elapsed:Float) {
super.update(elapsed);
- changeSelection((controls.DOWN_P ? 1 : 0) + (controls.UP_P ? -1 : 0));
+ changeSelection((controls.DOWN_P ? 1 : 0) + (controls.UP_P ? -1 : 0) - FlxG.mouse.wheel);
if (controls.ACCEPT) {
ModsFolder.switchMod(mods[curSelected]);
@@ -64,4 +64,4 @@ class ModSwitchMenu extends MusicBeatSubstate {
alphabets.members[curSelected].alpha = 1;
}
}
-#end
\ No newline at end of file
+#end
diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx
index bff3cd4d..ecf2fa7d 100644
--- a/source/funkin/menus/PauseSubState.hx
+++ b/source/funkin/menus/PauseSubState.hx
@@ -124,13 +124,12 @@ class PauseSubState extends MusicBeatSubstate
var upP = controls.UP_P;
var downP = controls.DOWN_P;
- var accepted = controls.ACCEPT;
+ var scroll = FlxG.mouse.wheel;
- if (upP)
- changeSelection(-1);
- if (downP)
- changeSelection(1);
- if (accepted)
+ if (upP || downP || scroll != 0) // like this we wont break mods that expect a 0 change event when calling sometimes - Nex
+ changeSelection((upP ? -1 : 0) + (downP ? 1 : 0) - scroll);
+
+ if (controls.ACCEPT)
selectOption();
}
diff --git a/source/funkin/menus/StoryMenuState.hx b/source/funkin/menus/StoryMenuState.hx
index d69d0c5b..847d921e 100644
--- a/source/funkin/menus/StoryMenuState.hx
+++ b/source/funkin/menus/StoryMenuState.hx
@@ -142,7 +142,7 @@ class StoryMenuState extends MusicBeatState {
}
changeDifficulty((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0));
- changeWeek((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0));
+ changeWeek((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
if (controls.ACCEPT)
selectWeek();
diff --git a/source/funkin/menus/credits/CreditsCodename.hx b/source/funkin/menus/credits/CreditsCodename.hx
index e6c717c0..f5929f18 100644
--- a/source/funkin/menus/credits/CreditsCodename.hx
+++ b/source/funkin/menus/credits/CreditsCodename.hx
@@ -10,7 +10,7 @@ using StringTools;
class CreditsCodename extends funkin.options.OptionsScreen {
public var error:Bool = false;
- public var author:String = "FNF-CNE-Devs";
+ public var author:String = "CodenameCrew";
public var totalContributions:Int = 0;
public var mainDevCol:FlxColor = 0xFF9C35D5;
diff --git a/source/funkin/menus/credits/CreditsMain.hx b/source/funkin/menus/credits/CreditsMain.hx
index 847d2b64..6da46187 100644
--- a/source/funkin/menus/credits/CreditsMain.hx
+++ b/source/funkin/menus/credits/CreditsMain.hx
@@ -11,7 +11,7 @@ class CreditsMain extends TreeMenu {
var items:Array = [];
public override function create() {
- bg = new FlxSprite(-80).loadGraphic(Paths.image('menus/menuBGBlue'));
+ bg = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
// bg.scrollFactor.set();
bg.scale.set(1.15, 1.15);
bg.updateHitbox();
diff --git a/source/funkin/options/OptionsMenu.hx b/source/funkin/options/OptionsMenu.hx
index 497bfd9b..30662136 100644
--- a/source/funkin/options/OptionsMenu.hx
+++ b/source/funkin/options/OptionsMenu.hx
@@ -31,6 +31,8 @@ class OptionsMenu extends TreeMenu {
}
];
+ var bg:FlxSprite;
+
public override function create() {
super.create();
@@ -38,7 +40,7 @@ class OptionsMenu extends TreeMenu {
DiscordUtil.call("onMenuLoaded", ["Options Menu"]);
- var bg:FlxSprite = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
+ bg = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
// bg.scrollFactor.set();
bg.scale.set(1.15, 1.15);
bg.updateHitbox();
diff --git a/source/funkin/options/OptionsScreen.hx b/source/funkin/options/OptionsScreen.hx
index 45580821..b703d994 100644
--- a/source/funkin/options/OptionsScreen.hx
+++ b/source/funkin/options/OptionsScreen.hx
@@ -45,7 +45,7 @@ class OptionsScreen extends FlxTypedSpriteGroup {
if (members.length > 0) {
members[curSelected].selected = true;
- if (controls.ACCEPT || FlxG.mouse.justReleased)
+ if (controls.ACCEPT || (FlxG.mouse.justReleased && Main.timeSinceFocus > 0.25))
members[curSelected].onSelect();
if (controls.LEFT_P)
members[curSelected].onChangeSelection(-1);
diff --git a/source/hscript/Config.hx b/source/hscript/Config.hx
index d0667ab9..4eec6ab1 100644
--- a/source/hscript/Config.hx
+++ b/source/hscript/Config.hx
@@ -29,7 +29,6 @@ class Config {
];
public static final DISALLOW_ABSTRACT_AND_ENUM = [
- "funkin.backend.FunkinSprite", // Error: String has no field trim, Due to Func
"funkin.backend.scripting.events.PlayAnimEvent", // Error: expected member name or ';' after declaration specifiers, Due to Func
];
}
\ No newline at end of file