diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 08e9b4d2..f7e7ef90 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -25,6 +25,9 @@ jobs:
export/release/linux/obj/
restore-keys: |
cache-build-linux
+ - name: Installing LibVLC
+ run: |
+ sudo apt-get install libvlc-dev libvlccore-dev
- name: Installing/Updating libraries
run: |
haxe -cp commandline -D analyzer-optimize --run Main setup
diff --git a/assets/images/editors/charter/add-strumline.png b/assets/images/editors/charter/add-strumline.png
index d205c17e..f6355d05 100644
Binary files a/assets/images/editors/charter/add-strumline.png and b/assets/images/editors/charter/add-strumline.png differ
diff --git a/assets/images/editors/charter/event-icons/Alt Animation Toggle.png b/assets/images/editors/charter/event-icons/Alt Animation Toggle.png
index f2180276..39e1afd1 100644
Binary files a/assets/images/editors/charter/event-icons/Alt Animation Toggle.png and b/assets/images/editors/charter/event-icons/Alt Animation Toggle.png differ
diff --git a/assets/images/editors/charter/lock-strumline.png b/assets/images/editors/charter/lock-strumline.png
new file mode 100644
index 00000000..cf283f19
Binary files /dev/null and b/assets/images/editors/charter/lock-strumline.png differ
diff --git a/assets/images/editors/charter/strumline-drag.png b/assets/images/editors/charter/strumline-drag.png
new file mode 100644
index 00000000..9fb8fa5e
Binary files /dev/null and b/assets/images/editors/charter/strumline-drag.png differ
diff --git a/assets/images/editors/new.png b/assets/images/editors/new.png
index d205c17e..945c49e0 100644
Binary files a/assets/images/editors/new.png and b/assets/images/editors/new.png differ
diff --git a/assets/images/editors/ui/audio-time-empty.png b/assets/images/editors/ui/audio-time-empty.png
index d5a36361..0f380282 100644
Binary files a/assets/images/editors/ui/audio-time-empty.png and b/assets/images/editors/ui/audio-time-empty.png differ
diff --git a/assets/images/editors/ui/audio-time-full.png b/assets/images/editors/ui/audio-time-full.png
index 22073536..c52c765a 100644
Binary files a/assets/images/editors/ui/audio-time-full.png and b/assets/images/editors/ui/audio-time-full.png differ
diff --git a/assets/images/editors/ui/audio-volume-empty.png b/assets/images/editors/ui/audio-volume-empty.png
index 7255b513..5ad6dc52 100644
Binary files a/assets/images/editors/ui/audio-volume-empty.png and b/assets/images/editors/ui/audio-volume-empty.png differ
diff --git a/assets/images/editors/ui/audio-volume-full.png b/assets/images/editors/ui/audio-volume-full.png
index 76ffc958..bac31729 100644
Binary files a/assets/images/editors/ui/audio-volume-full.png and b/assets/images/editors/ui/audio-volume-full.png differ
diff --git a/assets/images/editors/ui/context-icons.png b/assets/images/editors/ui/context-icons.png
index 73b8ff97..7bf46448 100644
Binary files a/assets/images/editors/ui/context-icons.png and b/assets/images/editors/ui/context-icons.png differ
diff --git a/assets/images/editors/ui/grayscale-button.png b/assets/images/editors/ui/grayscale-button.png
new file mode 100644
index 00000000..1467fece
Binary files /dev/null and b/assets/images/editors/ui/grayscale-button.png differ
diff --git a/assets/images/editors/ui/grayscale-button.xml b/assets/images/editors/ui/grayscale-button.xml
new file mode 100644
index 00000000..0c0811f4
--- /dev/null
+++ b/assets/images/editors/ui/grayscale-button.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/images/editors/ui/grayscale-popup.png b/assets/images/editors/ui/grayscale-popup.png
new file mode 100644
index 00000000..b0113603
Binary files /dev/null and b/assets/images/editors/ui/grayscale-popup.png differ
diff --git a/assets/images/editors/ui/grayscale-popup.xml b/assets/images/editors/ui/grayscale-popup.xml
new file mode 100644
index 00000000..021dd81a
--- /dev/null
+++ b/assets/images/editors/ui/grayscale-popup.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/images/editors/ui/warning-popup.png b/assets/images/editors/ui/warning-popup.png
deleted file mode 100644
index c190bffa..00000000
Binary files a/assets/images/editors/ui/warning-popup.png and /dev/null differ
diff --git a/assets/images/editors/warnings/error.png b/assets/images/editors/warnings/error.png
new file mode 100644
index 00000000..9c65f94b
Binary files /dev/null and b/assets/images/editors/warnings/error.png differ
diff --git a/assets/images/editors/warnings/warning.png b/assets/images/editors/warnings/warning.png
new file mode 100644
index 00000000..0d4f1999
Binary files /dev/null and b/assets/images/editors/warnings/warning.png differ
diff --git a/libs.xml b/libs.xml
index aad494a6..70e72592 100644
--- a/libs.xml
+++ b/libs.xml
@@ -1,24 +1,22 @@
-
+
-
-
-
+
+
+
-
+
+
+
+
+
-
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
diff --git a/project.xml b/project.xml
index 1a008240..5e4aaa8a 100644
--- a/project.xml
+++ b/project.xml
@@ -21,7 +21,6 @@
-
@@ -53,12 +52,11 @@
-
+
-
@@ -74,17 +72,11 @@
-
-
-
-
-
-
@@ -100,6 +92,7 @@
+
@@ -113,10 +106,12 @@
-
+
-
+
@@ -157,7 +152,9 @@
-
+
@@ -172,23 +169,20 @@
+
+
-
-
+
+
-
-
-
@@ -208,5 +202,6 @@
+
diff --git a/source/funkin/backend/FlxAnimate.hx b/source/funkin/backend/FlxAnimate.hx
index ea1644bc..c6559dfd 100644
--- a/source/funkin/backend/FlxAnimate.hx
+++ b/source/funkin/backend/FlxAnimate.hx
@@ -25,7 +25,7 @@ class FlxAnimate extends flxanimate.FlxAnimate {
getScreenPosition(_point, camera).subtractPoint(offset);
rMatrix.translate(-origin.x, -origin.y);
- if (!showPivot && limb.name != "pivot") {
+ if (limb != _pivot) {
if (frameOffsetAngle != null && frameOffsetAngle != angle)
{
var angleOff = (-angle + frameOffsetAngle) * FlxAngle.TO_RAD;
diff --git a/source/funkin/backend/MusicBeatState.hx b/source/funkin/backend/MusicBeatState.hx
index eca0c466..1b5499de 100644
--- a/source/funkin/backend/MusicBeatState.hx
+++ b/source/funkin/backend/MusicBeatState.hx
@@ -9,6 +9,7 @@ import flixel.FlxState;
import flixel.FlxSubState;
import funkin.backend.scripting.events.*;
import funkin.backend.scripting.Script;
+import funkin.backend.scripting.ScriptPack;
import funkin.backend.system.interfaces.IBeatReceiver;
import funkin.backend.system.Conductor;
import funkin.options.PlayerSettings;
@@ -89,7 +90,7 @@ class MusicBeatState extends FlxState implements IBeatReceiver
/**
* Current injected script attached to the state. To add one, create a file at path "data/states/stateName" (ex: data/states/FreeplayState)
*/
- public var stateScript:Script;
+ public var stateScripts:ScriptPack;
public var scriptsAllowed:Bool = true;
@@ -112,15 +113,22 @@ class MusicBeatState extends FlxState implements IBeatReceiver
}
function loadScript() {
+ var className = Type.getClassName(Type.getClass(this));
+ if (stateScripts == null)
+ (stateScripts = new ScriptPack(className)).setParent(this);
if (scriptsAllowed) {
- if (stateScript == null || stateScript is DummyScript) {
- var className = Type.getClassName(Type.getClass(this));
+ if (stateScripts.scripts.length == 0) {
var scriptName = this.scriptName != null ? this.scriptName : className.substr(className.lastIndexOf(".")+1);
- stateScript = Script.create(Paths.script('data/states/${scriptName}'));
- stateScript.setParent(this);
- stateScript.load();
- } else
- stateScript.reload();
+ for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
+ var path = Paths.script('data/states/${scriptName}/LIB_$i');
+ var script = Script.create(path);
+ if (script is DummyScript) continue;
+ script.fileName = '$i:$scriptName';
+ stateScripts.add(script);
+ script.load();
+ }
+ }
+ else stateScripts.reload();
}
}
@@ -161,13 +169,14 @@ class MusicBeatState extends FlxState implements IBeatReceiver
}
public function call(name:String, ?args:Array, ?defaultVal:Dynamic):Dynamic {
// calls the function on the assigned script
- if (stateScript == null) return defaultVal;
- return stateScript.call(name, args);
+ if(stateScripts != null)
+ return stateScripts.call(name, args);
+ return defaultVal;
}
public function event(name:String, event:T):T {
- if (stateScript == null) return event;
- stateScript.call(name, [event]);
+ if(stateScripts != null)
+ stateScripts.call(name, [event]);
return event;
}
@@ -176,8 +185,6 @@ class MusicBeatState extends FlxState implements IBeatReceiver
// TODO: DEBUG MODE!!
if (FlxG.keys.justPressed.F5) {
loadScript();
- if (stateScript != null && !(stateScript is DummyScript))
- Logs.trace('State script successfully reloaded', WARNING, GREEN);
}
call("update", [elapsed]);
@@ -234,8 +241,7 @@ class MusicBeatState extends FlxState implements IBeatReceiver
super.destroy();
graphicCache.destroy();
call("onDestroy");
- if (stateScript != null)
- stateScript.destroy();
+ stateScripts = FlxDestroyUtil.destroy(stateScripts);
}
public override function draw() {
diff --git a/source/funkin/backend/MusicBeatSubstate.hx b/source/funkin/backend/MusicBeatSubstate.hx
index d1d96f43..4b4315ca 100644
--- a/source/funkin/backend/MusicBeatSubstate.hx
+++ b/source/funkin/backend/MusicBeatSubstate.hx
@@ -3,6 +3,7 @@ package funkin.backend;
import flixel.FlxState;
import funkin.backend.scripting.events.*;
import funkin.backend.scripting.Script;
+import funkin.backend.scripting.ScriptPack;
import funkin.backend.scripting.DummyScript;
import funkin.backend.system.interfaces.IBeatReceiver;
import funkin.backend.system.Conductor.BPMChangeEvent;
@@ -63,7 +64,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
/**
* Current injected script attached to the state. To add one, create a file at path "data/states/stateName" (ex: "data/states/PauseMenuSubstate.hx")
*/
- public var stateScript:Script;
+ public var stateScripts:ScriptPack;
public var scriptsAllowed:Bool = true;
@@ -99,15 +100,22 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
}
function loadScript() {
+ var className = Type.getClassName(Type.getClass(this));
+ if (stateScripts == null)
+ (stateScripts = new ScriptPack(className)).setParent(this);
if (scriptsAllowed) {
- if (stateScript == null || stateScript is DummyScript) {
- var className = Type.getClassName(Type.getClass(this));
+ if (stateScripts.scripts.length == 0) {
var scriptName = this.scriptName != null ? this.scriptName : className.substr(className.lastIndexOf(".")+1);
- stateScript = Script.create(Paths.script('data/states/${scriptName}'));
- stateScript.setParent(this);
- stateScript.load();
- } else
- stateScript.reload();
+ for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
+ var path = Paths.script('data/states/${scriptName}/LIB_$i');
+ var script = Script.create(path);
+ if (script is DummyScript) continue;
+ script.fileName = '$i:$scriptName';
+ stateScripts.add(script);
+ script.load();
+ }
+ }
+ else stateScripts.reload();
}
}
@@ -151,13 +159,14 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
}
public function call(name:String, ?args:Array, ?defaultVal:Dynamic):Dynamic {
// calls the function on the assigned script
- if (stateScript == null) return defaultVal;
- return stateScript.call(name, args);
+ if(stateScripts != null)
+ return stateScripts.call(name, args);
+ return defaultVal;
}
public function event(name:String, event:T):T {
- if (stateScript == null) return event;
- stateScript.call(name, [event]);
+ if(stateScripts != null)
+ stateScripts.call(name, [event]);
return event;
}
@@ -166,8 +175,6 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
// TODO: DEBUG MODE!!
if (FlxG.keys.justPressed.F5) {
loadScript();
- if (stateScript != null && !(stateScript is DummyScript))
- Logs.trace('State script successfully reloaded', WARNING, GREEN);
}
call("update", [elapsed]);
super.update(elapsed);
@@ -222,8 +229,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
public override function destroy() {
super.destroy();
call("onDestroy");
- if(stateScript != null)
- stateScript.destroy();
+ stateScripts = FlxDestroyUtil.destroy(stateScripts);
}
public override function switchTo(nextState:FlxState) {
diff --git a/source/funkin/backend/MusicBeatTransition.hx b/source/funkin/backend/MusicBeatTransition.hx
index c49f88a8..6313c10b 100644
--- a/source/funkin/backend/MusicBeatTransition.hx
+++ b/source/funkin/backend/MusicBeatTransition.hx
@@ -11,14 +11,14 @@ class MusicBeatTransition extends MusicBeatSubstate {
public var transitionCamera:FlxCamera;
public var newState:FlxState;
+ public var blackSpr:FlxSprite;
+ public var transitionSprite:FunkinSprite;
public function new(?newState:FlxState) {
super();
this.newState = newState;
}
public override function create() {
- super.create();
-
if (newState != null)
add(new FunkinParentDisabler(true));
@@ -29,13 +29,13 @@ class MusicBeatTransition extends MusicBeatSubstate {
cameras = [transitionCamera];
var out = newState != null;
- var blackSpr = new FlxSprite(0, out ? -transitionCamera.height : transitionCamera.height).makeGraphic(1, 1, -1);
+ blackSpr = new FlxSprite(0, out ? -transitionCamera.height : transitionCamera.height).makeGraphic(1, 1, -1);
blackSpr.scale.set(transitionCamera.width, transitionCamera.height);
blackSpr.color = 0xFF000000;
blackSpr.updateHitbox();
add(blackSpr);
- var transitionSprite = new FunkinSprite();
+ transitionSprite = new FunkinSprite();
transitionSprite.loadSprite(Paths.image('menus/transitionSpr'));
if (transitionSprite.animateAtlas == null) {
transitionSprite.setGraphicSize(transitionCamera.width, transitionCamera.height);
@@ -53,6 +53,8 @@ class MusicBeatTransition extends MusicBeatSubstate {
finish();
}
});
+
+ super.create();
}
public override function update(elapsed:Float) {
diff --git a/source/funkin/backend/assets/IModsAssetLibrary.hx b/source/funkin/backend/assets/IModsAssetLibrary.hx
index 5c90b185..a080189b 100644
--- a/source/funkin/backend/assets/IModsAssetLibrary.hx
+++ b/source/funkin/backend/assets/IModsAssetLibrary.hx
@@ -23,6 +23,7 @@ using StringTools;
interface IModsAssetLibrary {
public var prefix:String;
+ public var modName:String;
public var libName:String;
#if MOD_SUPPORT
diff --git a/source/funkin/backend/assets/ModsFolder.hx b/source/funkin/backend/assets/ModsFolder.hx
index 33458fe0..a88377ad 100644
--- a/source/funkin/backend/assets/ModsFolder.hx
+++ b/source/funkin/backend/assets/ModsFolder.hx
@@ -41,6 +41,11 @@ class ModsFolder {
*/
public static var addonsPath:String = "./addons/";
+ /**
+ * If accessing a file as assets/data/global/LIB_mymod.hx should redirect to mymod:assets/data/global.hx
+ */
+ public static var useLibFile:Bool = true;
+
/**
* Whenever its the first time mods has been reloaded.
*/
@@ -72,18 +77,34 @@ class ModsFolder {
* @param modName Name of the mod
* @param force Whenever the mod should be reloaded if it has already been loaded
*/
- public static function loadModLib(path:String, force:Bool = false) {
+ public static function loadModLib(path:String, force:Bool = false, ?modName:String) {
#if MOD_SUPPORT
if (FileSystem.exists('$path.zip'))
- return loadLibraryFromZip('$path'.toLowerCase(), '$path.zip', force);
+ return loadLibraryFromZip('$path'.toLowerCase(), '$path.zip', force, modName);
else
- return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force);
+ return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName);
#else
return null;
#end
}
+ public static function getLoadedMods():Array {
+ var libs = [];
+ for (i in Paths.assetsTree.libraries) {
+ var l = i;
+ if (l is openfl.utils.AssetLibrary) {
+ var al = cast(l, openfl.utils.AssetLibrary);
+ @:privateAccess
+ if (al.__proxy != null) l = al.__proxy;
+ }
+ var libString:String;
+ if (l is ScriptedAssetLibrary || l is IModsAssetLibrary) libString = cast(l, IModsAssetLibrary).modName;
+ else continue;
+ libs.push(libString);
+ }
+ return libs;
+ }
public static function prepareLibrary(libName:String, force:Bool = false) {
var assets:AssetManifest = new AssetManifest();
assets.name = libName;
@@ -111,12 +132,12 @@ class ModsFolder {
}
#if MOD_SUPPORT
- public static function loadLibraryFromFolder(libName:String, folder:String, force:Bool = false) {
- return prepareModLibrary(libName, new ModsFolderLibrary(folder, libName), force);
+ public static function loadLibraryFromFolder(libName:String, folder:String, force:Bool = false, ?modName:String) {
+ return prepareModLibrary(libName, new ModsFolderLibrary(folder, libName, modName), force);
}
- public static function loadLibraryFromZip(libName:String, zipPath:String, force:Bool = false) {
- return prepareModLibrary(libName, new ZipFolderLibrary(zipPath, libName), force);
+ public static function loadLibraryFromZip(libName:String, zipPath:String, force:Bool = false, ?modName:String) {
+ return prepareModLibrary(libName, new ZipFolderLibrary(zipPath, libName, modName), force);
}
#end
}
\ No newline at end of file
diff --git a/source/funkin/backend/assets/ModsFolderLibrary.hx b/source/funkin/backend/assets/ModsFolderLibrary.hx
index 69f87693..ed608bda 100644
--- a/source/funkin/backend/assets/ModsFolderLibrary.hx
+++ b/source/funkin/backend/assets/ModsFolderLibrary.hx
@@ -17,14 +17,19 @@ using StringTools;
class ModsFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
public var folderPath:String;
+ public var modName:String;
public var libName:String;
public var useImageCache:Bool = true;
public var prefix = 'assets/';
- public function new(folderPath:String, libName:String) {
+ public function new(folderPath:String, libName:String, ?modName:String) {
this.folderPath = folderPath;
this.libName = libName;
this.prefix = 'assets/$libName/';
+ if(modName == null)
+ this.modName = libName;
+ else
+ this.modName = modName;
super();
}
@@ -133,6 +138,15 @@ class ModsFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
private function __parseAsset(asset:String):Bool {
if (!asset.startsWith(prefix)) return false;
_parsedAsset = asset.substr(prefix.length);
+ if(ModsFolder.useLibFile) {
+ var file = new haxe.io.Path(_parsedAsset);
+ if(file.file.startsWith("LIB_")) {
+ var library = file.file.substr(4);
+ if(library != modName) return false;
+
+ _parsedAsset = file.dir + "." + file.ext;
+ }
+ }
return true;
}
#end
diff --git a/source/funkin/backend/assets/MultiFramesCollection.hx b/source/funkin/backend/assets/MultiFramesCollection.hx
new file mode 100644
index 00000000..a3d9c733
--- /dev/null
+++ b/source/funkin/backend/assets/MultiFramesCollection.hx
@@ -0,0 +1,72 @@
+package funkin.backend.assets;
+
+import flixel.graphics.frames.FlxFramesCollection;
+import flixel.FlxG;
+import flixel.graphics.FlxGraphic;
+import flixel.graphics.frames.FlxFrame.FlxFrameAngle;
+import flixel.graphics.frames.FlxFrame.FlxFrameType;
+import flixel.math.FlxMath;
+import flixel.math.FlxPoint;
+import flixel.math.FlxRect;
+import flixel.util.FlxDestroyUtil;
+import flixel.util.FlxStringUtil;
+
+/**
+ * Base class for all frame collections.
+ */
+class MultiFramesCollection extends FlxFramesCollection
+{
+ public var parentedFrames:Array = [];
+
+ public function new(parent:FlxGraphic, ?border:FlxPoint)
+ {
+ super(parent, USER("MULTI"), border);
+ }
+
+ /**
+ * Returns the `FlxAtlasFrame` of the specified `FlxGraphic` object.
+ *
+ * @param graphic `FlxGraphic` object to find the `FlxAtlasFrames` collection for.
+ * @return `FlxAtlasFrames` collection for the specified `FlxGraphic` object
+ * Could be `null` if `FlxGraphic` doesn't have it yet.
+ */
+ public static function findFrame(graphic:FlxGraphic, ?border:FlxPoint):MultiFramesCollection
+ {
+ if (border == null)
+ border = FlxPoint.weak();
+
+ var atlasFrames:Array = cast graphic.getFramesCollections(USER("MULTI"));
+
+ for (atlas in atlasFrames)
+ if (atlas.border.equals(border))
+ return atlas;
+
+ return null;
+ }
+
+ public function addFrames(collection:FlxFramesCollection) {
+ if (collection == null || collection.frames == null) return;
+
+ collection.parent.useCount++;
+ parentedFrames.push(collection);
+
+ for(f in collection.frames) {
+ if (f != null) {
+ pushFrame(f);
+ f.parent = collection.parent;
+ }
+ }
+ }
+
+ public override function destroy():Void
+ {
+ if(parentedFrames != null) {
+ for(collection in parentedFrames) {
+ if(collection != null)
+ collection.parent.useCount--;
+ }
+ parentedFrames = null;
+ }
+ super.destroy();
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx
index 2d7c0c75..495d5577 100644
--- a/source/funkin/backend/assets/Paths.hx
+++ b/source/funkin/backend/assets/Paths.hx
@@ -48,6 +48,9 @@ class Paths
inline static public function txt(key:String, ?library:String)
return getPath('data/$key.txt', library);
+ inline static public function pack(key:String, ?library:String)
+ return getPath('data/$key.pack', library);
+
inline static public function ini(key:String, ?library:String)
return getPath('data/$key.ini', library);
@@ -161,7 +164,7 @@ class Paths
inline static public function getPackerAtlasAlt(key:String)
return FlxAtlasFrames.fromSpriteSheetPacker('$key.png', '$key.txt');
- inline static public function getAssetsRoot():String
+ inline static public function getAssetsRoot():String
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : './assets';
/**
@@ -196,25 +199,18 @@ class Paths
// MULTIPLE SPRITESHEETS!!
var graphic = FlxG.bitmap.add("flixel/images/logo/default.png", false, '$noExt/mult');
- var frames = FlxAtlasFrames.findFrame(graphic);
+ var frames = MultiFramesCollection.findFrame(graphic);
if (frames != null)
return frames;
trace("no frames yet for multiple atlases!!");
- var spritesheets = [];
var cur = 1;
- var finalFrames = new FlxFramesCollection(graphic, ATLAS);
+ var finalFrames = new MultiFramesCollection(graphic);
while(Assets.exists('$noExt/$cur.png')) {
- spritesheets.push(loadFrames('$noExt/$cur.png'));
+ var spr = loadFrames('$noExt/$cur.png');
+ finalFrames.addFrames(spr);
cur++;
}
- for(frames in spritesheets)
- if (frames != null && frames.frames != null)
- for(f in frames.frames)
- if (f != null) {
- finalFrames.frames.push(f);
- f.parent = frames.parent;
- }
return finalFrames;
} else if (Assets.exists('$noExt.xml')) {
return Paths.getSparrowAtlasAlt(noExt);
diff --git a/source/funkin/backend/assets/ScriptedAssetLibrary.hx b/source/funkin/backend/assets/ScriptedAssetLibrary.hx
index 972f74e2..0fd8d380 100644
--- a/source/funkin/backend/assets/ScriptedAssetLibrary.hx
+++ b/source/funkin/backend/assets/ScriptedAssetLibrary.hx
@@ -21,9 +21,9 @@ class ScriptedAssetLibrary extends ModsFolderLibrary {
public var scriptName:String;
private static var nullValue:Dynamic = {};
- public function new(scriptName:String, args:Array = null, folderPath:String="./assets/", libName:String="assets") {
- super(folderPath, libName);
-
+ public function new(scriptName:String, args:Array = null, folderPath:String="./assets/", libName:String="assets", ?modName:String) {
+ if(modName == null) modName = scriptName;
+ super(folderPath, libName, modName);
this.scriptName = scriptName;
script = Script.create(Paths.script("data/library/" + scriptName));
script.setParent(this);
@@ -107,6 +107,14 @@ class ScriptedAssetLibrary extends ModsFolderLibrary {
return super.getAssetPath();
}
+ public override function list(type:String) {
+ var result:Dynamic = script.call("list", [type]);
+ if(result != null) {
+ return result == nullValue ? null : result;
+ }
+ return super.list(type);
+ }
+
private override function __isCacheValid(cache:Map, asset:String, isLocalCache:Bool = false) {
var result:Dynamic = script.call("__isCacheValid", [cache, asset, isLocalCache]);
if(result != null) {
diff --git a/source/funkin/backend/assets/ZipFolderLibrary.hx b/source/funkin/backend/assets/ZipFolderLibrary.hx
index 7465482e..21e425d8 100644
--- a/source/funkin/backend/assets/ZipFolderLibrary.hx
+++ b/source/funkin/backend/assets/ZipFolderLibrary.hx
@@ -27,6 +27,7 @@ import funkin.backend.utils.SysZip.SysZipEntry;
class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
public var zipPath:String;
+ public var modName:String;
public var libName:String;
public var useImageCache:Bool = false;
public var prefix = 'assets/';
@@ -34,10 +35,15 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
public var zip:SysZip;
public var assets:Map = [];
- public function new(zipPath:String, libName:String) {
+ public function new(zipPath:String, libName:String, ?modName:String) {
this.zipPath = zipPath;
this.libName = libName;
+ if(modName == null)
+ this.modName = libName;
+ else
+ this.modName = modName;
+
zip = SysZip.openFromFile(zipPath);
zip.read();
for(entry in zip.entries)
@@ -77,7 +83,18 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
public function __parseAsset(asset:String):Bool {
if (!asset.startsWith(prefix)) return false;
- _parsedAsset = asset.substr(prefix.length).toLowerCase();
+ _parsedAsset = asset.substr(prefix.length);
+ if(ModsFolder.useLibFile) {
+ var file = new haxe.io.Path(_parsedAsset);
+ if(file.file.startsWith("LIB_")) {
+ var library = file.file.substr(4);
+ if(library != modName) return false;
+
+ _parsedAsset = file.dir + "." + file.ext;
+ }
+ }
+
+ _parsedAsset = _parsedAsset.toLowerCase();
return true;
}
diff --git a/source/funkin/backend/chart/BaseGameParser.hx b/source/funkin/backend/chart/BaseGameParser.hx
index b8d2d6b9..71e707cc 100644
--- a/source/funkin/backend/chart/BaseGameParser.hx
+++ b/source/funkin/backend/chart/BaseGameParser.hx
@@ -69,12 +69,13 @@ class BaseGameParser {
var gottaHitNote:Bool = daNoteData >= 4 ? !section.mustHitSection : section.mustHitSection;
if (note.length > 2) {
- if (note[3] is Int)
+ if (note[3] is Int && data.noteTypes != null)
daNoteType = Chart.addNoteType(result, data.noteTypes[Std.int(note[3])-1]);
else if (note[3] is String)
daNoteType = Chart.addNoteType(result, note[3]);
} else {
- daNoteType = Chart.addNoteType(result, data.noteTypes[daNoteType-1]);
+ if(data.noteTypes != null)
+ daNoteType = Chart.addNoteType(result, data.noteTypes[daNoteType-1]);
}
result.strumLines[gottaHitNote ? 1 : 0].notes.push({
diff --git a/source/funkin/backend/chart/EventsData.hx b/source/funkin/backend/chart/EventsData.hx
index 0c8f0a97..2cb50557 100644
--- a/source/funkin/backend/chart/EventsData.hx
+++ b/source/funkin/backend/chart/EventsData.hx
@@ -42,15 +42,21 @@ class EventsData {
hscriptParser.allowJSON = hscriptParser.allowMetadata = false;
for (file in Paths.getFolderContent('data/events/', true, BOTH)) {
- if (Path.extension(file) != "json") continue;
+ if (Path.extension(file) != "json" && Path.extension(file) != "pack") continue;
var eventName:String = Path.withoutExtension(Path.withoutDirectory(file));
+ var fileTxt:String = Assets.getText(file);
+
+ if (Path.extension(file) == "pack") {
+ var arr = fileTxt.split("________PACKSEP________");
+ eventName = Path.withoutExtension(arr[0]);
+ fileTxt = arr[2];
+ }
+
+ if (fileTxt.trim() == "") continue;
eventsList.push(eventName);
eventsParams.set(eventName, []);
- var fileTxt:String = Assets.getText(file);
- if (fileTxt.trim() == "") continue;
-
try {
var data:Dynamic = Json.parse(fileTxt);
if (data == null || data.params == null) continue;
diff --git a/source/funkin/backend/scripting/GlobalScript.hx b/source/funkin/backend/scripting/GlobalScript.hx
index 14803c8b..dbca645c 100644
--- a/source/funkin/backend/scripting/GlobalScript.hx
+++ b/source/funkin/backend/scripting/GlobalScript.hx
@@ -9,7 +9,7 @@ import funkin.backend.assets.ModsFolder;
* Class for THE Global Script, aka script that runs in the background at all times.
*/
class GlobalScript {
- public static var script:Script;
+ public static var scripts:ScriptPack;
public static function init() {
#if MOD_SUPPORT
@@ -43,19 +43,17 @@ class GlobalScript {
FlxG.signals.postUpdate.add(function() {
call("postUpdate", [FlxG.elapsed]);
if (FlxG.keys.justPressed.F5) {
- if (script != null && !(script is DummyScript)) {
+ if (scripts.scripts.length > 0) {
Logs.trace('Reloading global script...', WARNING, YELLOW);
- script.reload();
+ scripts.reload();
Logs.trace('Global script successfully reloaded.', WARNING, GREEN);
} else {
Logs.trace('Loading global script...', WARNING, YELLOW);
onModSwitch(#if MOD_SUPPORT ModsFolder.currentModFolder #else null #end);
- if (script is DummyScript)
- Logs.trace('Global script not found. Are you sure "data/global.hx" exists?', ERROR, RED);
- else
- Logs.trace('Global script successfully loaded.', WARNING, GREEN);
}
}
+ if (FlxG.keys.justPressed.F2)
+ NativeAPI.allocConsole();
});
FlxG.signals.preDraw.add(function() {
call("preDraw");
@@ -81,23 +79,25 @@ class GlobalScript {
}
public static function event(name:String, event:T):T {
- if (script == null) return event;
- script.call(name, [event]);
+ if (scripts != null) scripts.call(name, [event]);
return event;
}
public static function call(name:String, ?args:Array) {
- if (script == null) return;
- script.call(name, args);
+ if (scripts != null) scripts.call(name, args);
}
public static function onModSwitch(newMod:String) {
call("onDestroy");
- if (script != null) {
- script.destroy();
- script = null;
+ scripts = FlxDestroyUtil.destroy(scripts);
+ scripts = new ScriptPack("GlobalScript");
+ for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
+ var path = Paths.script('data/global/LIB_$i');
+ var script = Script.create(path);
+ if (script is DummyScript) continue;
+ script.fileName = '$i:global.hx';
+ scripts.add(script);
+ script.load();
}
- script = Script.create(Paths.script('data/global'));
- script.load();
}
public static function beatHit(curBeat:Int) {
diff --git a/source/funkin/backend/scripting/HScript.hx b/source/funkin/backend/scripting/HScript.hx
index 158012a6..adb829a6 100644
--- a/source/funkin/backend/scripting/HScript.hx
+++ b/source/funkin/backend/scripting/HScript.hx
@@ -29,7 +29,7 @@ class HScript extends Script {
interp = new Interp();
- code = Assets.getText(path);
+ code = Assets.exists(path) ? Assets.getText(path) : null;
parser = initParser();
folderlessPath = Path.directory(path);
__importedPaths = [path];
@@ -45,6 +45,13 @@ class HScript extends Script {
this.trace(v);
}));
+ funkin.backend.scripting.GlobalScript.call("onScriptCreated", [this, "hscript"]);
+
+ if (code != null && code.trim() != "")
+ loadFromString(code);
+ }
+
+ public override function loadFromString(code:String) {
try {
if (code != null && code.trim() != "")
expr = parser.parseString(code, fileName);
@@ -53,6 +60,8 @@ class HScript extends Script {
} catch(e) {
_errorHandler(new Error(ECustom(e.toString()), 0, 0, fileName, 0));
}
+
+ return this;
}
private function importFailedCallback(cl:Array):Bool {
diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx
index 927e1189..bdfe59bf 100644
--- a/source/funkin/backend/scripting/Script.hx
+++ b/source/funkin/backend/scripting/Script.hx
@@ -109,6 +109,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
*/
public static var scriptExtensions:Array = [
"hx", "hscript", "hsc", "hxs",
+ "pack", // combined file
"lua" /** ACTUALLY NOT SUPPORTED, ONLY FOR THE MESSAGE **/
];
@@ -138,6 +139,9 @@ class Script extends FlxBasic implements IFlxDestroyable {
return switch(Path.extension(path).toLowerCase()) {
case "hx" | "hscript" | "hsc" | "hxs":
new HScript(path);
+ case "pack":
+ var arr = Assets.getText(path).split("________PACKSEP________");
+ fromString(arr[1], arr[0]);
case "lua":
Logs.trace("Lua is not supported in this engine. Use HScript instead.", ERROR);
new DummyScript(path);
@@ -148,6 +152,23 @@ class Script extends FlxBasic implements IFlxDestroyable {
return new DummyScript(path);
}
+ /**
+ * Creates a script from the string. The language is determined based on the path.
+ * @param code code
+ * @param path filename
+ */
+ public static function fromString(code:String, path:String):Script {
+ return switch(Path.extension(path).toLowerCase()) {
+ case "hx" | "hscript" | "hsc" | "hxs":
+ new HScript(path).loadFromString(code);
+ case "lua":
+ Logs.trace("Lua is not supported in this engine. Use HScript instead.", ERROR);
+ new DummyScript(path).loadFromString(code);
+ default:
+ new DummyScript(path).loadFromString(code);
+ }
+ }
+
/**
* Creates a new instance of the script class.
* @param path
@@ -224,6 +245,14 @@ class Script extends FlxBasic implements IFlxDestroyable {
return result;
}
+ /**
+ * Loads the code from a string, doesnt really work after the script has been loaded
+ * @param code The code.
+ */
+ public function loadFromString(code:String) {
+ return this;
+ }
+
/**
* Sets a script's parent object so that its properties can be accessed easily. Ex: Passing `PlayState.instance` will allow `boyfriend` to be typed instead of `PlayState.instance.boyfriend`.
* @param variable Parent variable.
diff --git a/source/funkin/backend/system/CommandLineHandler.hx b/source/funkin/backend/system/CommandLineHandler.hx
index d00af9a5..4050b049 100644
--- a/source/funkin/backend/system/CommandLineHandler.hx
+++ b/source/funkin/backend/system/CommandLineHandler.hx
@@ -11,13 +11,14 @@ class CommandLineHandler {
break;
case "-h" | "-help" | "help":
Sys.println("-- Codename Engine Command Line help --");
- Sys.println("-help | Show this help");
+ Sys.println("-help | Show this help");
#if MOD_SUPPORT
- Sys.println("-mod [mod name] | Load a specific mod");
- Sys.println("-modfolder [path] | Sets the mod folder path");
+ Sys.println("-mod [mod name] | Load a specific mod");
+ Sys.println("-modfolder [path] | Sets the mod folder path");
+ Sys.println("-addonsfolder [path] | Sets the addons folder path");
#end
- Sys.println("-nocolor | Disables colors in the terminal");
- Sys.println("-nogpubitmap | Forces GPU only bitmaps off");
+ Sys.println("-nocolor | Disables colors in the terminal");
+ Sys.println("-nogpubitmap | Forces GPU only bitmaps off");
Sys.exit(0);
#if MOD_SUPPORT
case "-m" | "-mod" | "-currentmod":
@@ -25,7 +26,7 @@ class CommandLineHandler {
var arg = cmd[i];
if (arg == null) {
Sys.println("[ERROR] You need to specify the mod name");
- Sys.exit(0);
+ Sys.exit(1);
} else {
Main.modToLoad = arg.trim();
}
@@ -34,12 +35,24 @@ class CommandLineHandler {
var arg = cmd[i];
if (arg == null) {
Sys.println("[ERROR] You need to specify the mod folder path");
- Sys.exit(0);
+ Sys.exit(1);
} else if (FileSystem.exists(arg)) {
funkin.backend.assets.ModsFolder.modsPath = arg;
} else {
Sys.println('[ERROR] Mod folder at "${arg}" does not exist.');
- Sys.exit(0);
+ Sys.exit(1);
+ }
+ case "-addonsfolder":
+ i++;
+ var arg = cmd[i];
+ if (arg == null) {
+ Sys.println("[ERROR] You need to specify the addon folder path");
+ Sys.exit(1);
+ } else if (FileSystem.exists(arg)) {
+ funkin.backend.assets.ModsFolder.addonsPath = arg;
+ } else {
+ Sys.println('[ERROR] Addons folder at "${arg}" does not exist.');
+ Sys.exit(1);
}
#end
case "-nocolor":
diff --git a/source/funkin/backend/system/GraphicCacheSprite.hx b/source/funkin/backend/system/GraphicCacheSprite.hx
index 4f0fe98f..fd03bdc4 100644
--- a/source/funkin/backend/system/GraphicCacheSprite.hx
+++ b/source/funkin/backend/system/GraphicCacheSprite.hx
@@ -25,14 +25,21 @@ class GraphicCacheSprite extends FlxSprite {
* @param path Path to the graphic.
*/
public function cache(path:String) {
- var graphic = FlxG.bitmap.add(path);
- if (graphic != null) {
- // make their useCount one time higher to prevent them from auto being cleared from cache
- graphic.useCount++;
- graphic.destroyOnNoUse = false;
- cachedGraphics.push(graphic);
- nonRenderedCachedGraphics.push(graphic);
- }
+ cacheGraphic(FlxG.bitmap.add(path));
+ }
+
+ /**
+ * Caches a graphic.
+ * @param graphic The FlxGraphic
+ */
+ public function cacheGraphic(graphic:FlxGraphic) {
+ if (graphic == null) return;
+
+ // make their useCount one time higher to prevent them from auto being cleared from cache
+ graphic.useCount++;
+ graphic.destroyOnNoUse = false;
+ cachedGraphics.push(graphic);
+ nonRenderedCachedGraphics.push(graphic);
}
public override function destroy() {
diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx
index fbe59ac1..79ef9adc 100644
--- a/source/funkin/backend/system/Main.hx
+++ b/source/funkin/backend/system/Main.hx
@@ -1,5 +1,6 @@
package funkin.backend.system;
+import funkin.editors.SaveWarning;
import funkin.backend.assets.AssetsLibraryList;
import funkin.backend.system.framerate.SystemInfo;
import openfl.utils.AssetLibrary;
@@ -107,7 +108,8 @@ class Main extends Sprite
}
public static function loadGameSettings() {
- WindowTitle.init();
+ WindowUtils.init();
+ SaveWarning.init();
MemoryUtil.init();
@:privateAccess
FlxG.game.getTimer = getTimer;
@@ -134,6 +136,7 @@ class Main extends Sprite
trace("Used cne test / cne build. Switching into source assets.");
#if MOD_SUPPORT
ModsFolder.modsPath = './${pathBack}mods/';
+ ModsFolder.addonsPath = './${pathBack}addons/';
#end
Paths.assetsTree.__defaultLibraries.push(ModsFolder.loadLibraryFromFolder('assets', './${pathBack}assets/', true));
#elseif USE_ADAPTED_ASSETS
diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx
index ba67513d..f2284350 100644
--- a/source/funkin/backend/system/MainState.hx
+++ b/source/funkin/backend/system/MainState.hx
@@ -33,11 +33,11 @@ class MainState extends FlxState {
#if MOD_SUPPORT
if (ModsFolder.currentModFolder != null)
- Paths.assetsTree.addLibrary(ModsFolder.loadModLib('${ModsFolder.modsPath}${ModsFolder.currentModFolder}'));
+ Paths.assetsTree.addLibrary(ModsFolder.loadModLib('${ModsFolder.modsPath}${ModsFolder.currentModFolder}', ModsFolder.currentModFolder));
if (FileSystem.exists(ModsFolder.addonsPath) && FileSystem.isDirectory(ModsFolder.addonsPath))
for(addon in [for(dir in FileSystem.readDirectory(ModsFolder.addonsPath)) if (FileSystem.isDirectory('${ModsFolder.addonsPath}$dir')) dir])
- Paths.assetsTree.addLibrary(ModsFolder.loadModLib('${ModsFolder.addonsPath}$addon'));
+ Paths.assetsTree.addLibrary(ModsFolder.loadModLib('${ModsFolder.addonsPath}$addon', addon));
#end
Main.refreshAssets();
diff --git a/source/funkin/backend/system/framerate/AssetTreeInfo.hx b/source/funkin/backend/system/framerate/AssetTreeInfo.hx
index 3f8c0388..b886abb4 100644
--- a/source/funkin/backend/system/framerate/AssetTreeInfo.hx
+++ b/source/funkin/backend/system/framerate/AssetTreeInfo.hx
@@ -22,9 +22,9 @@ class AssetTreeInfo extends FramerateCategory {
}
if (l is ScriptedAssetLibrary)
- text += '${Type.getClassName(Type.getClass(l))} - ${cast(l, ScriptedAssetLibrary).scriptName} (${cast(l, ScriptedAssetLibrary).libName} | ${cast(l, ScriptedAssetLibrary).prefix})\n';
+ text += '${Type.getClassName(Type.getClass(l))} - ${cast(l, ScriptedAssetLibrary).scriptName} (${cast(l, ScriptedAssetLibrary).modName} | ${cast(l, ScriptedAssetLibrary).libName} | ${cast(l, ScriptedAssetLibrary).prefix})\n';
else if (l is IModsAssetLibrary)
- text += '${Type.getClassName(Type.getClass(l))} - ${cast(l, IModsAssetLibrary).libName} (${cast(l, IModsAssetLibrary).prefix})\n';
+ text += '${Type.getClassName(Type.getClass(l))} - ${cast(l, IModsAssetLibrary).modName} - ${cast(l, IModsAssetLibrary).libName} (${cast(l, IModsAssetLibrary).prefix})\n';
else
text += Std.string(e) + "\n";
}
diff --git a/source/funkin/backend/system/macros/ScriptsMacro.hx b/source/funkin/backend/system/macros/ScriptsMacro.hx
index db0f9f55..013d58b6 100644
--- a/source/funkin/backend/system/macros/ScriptsMacro.hx
+++ b/source/funkin/backend/system/macros/ScriptsMacro.hx
@@ -21,7 +21,7 @@ class ScriptsMacro {
"flixel.addons.util",
// OTHER LIBRARIES & STUFF
"away3d", "flx3d",
- #if VIDEO_CUTSCENES "hxcodec.flixel", "hxcodec.openfl", "hxcodec.lime", #end
+ #if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end
// BASE HAXE
"DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting"
])
@@ -38,10 +38,8 @@ class ScriptsMacro {
Compiler.include("funkin", #if !UPDATE_CHECKING ['funkin.backend.system.updating'] #end);
- // FOR ABSTRACTS
- Compiler.addGlobalMetadata('haxe.xml', '@:build(hscript.UsingHandler.build())');
- Compiler.addGlobalMetadata('haxe.CallStack', '@:build(hscript.UsingHandler.build())');
#if HL HashLinkFixer.init(); #end
+ // Todo rewrite this to use if(Context.defined(""))
}
}
#end
\ No newline at end of file
diff --git a/source/funkin/backend/utils/WindowTitle.hx b/source/funkin/backend/utils/WindowUtils.hx
similarity index 58%
rename from source/funkin/backend/utils/WindowTitle.hx
rename to source/funkin/backend/utils/WindowUtils.hx
index 239efa4f..609e3977 100644
--- a/source/funkin/backend/utils/WindowTitle.hx
+++ b/source/funkin/backend/utils/WindowUtils.hx
@@ -2,7 +2,7 @@ package funkin.backend.utils;
import openfl.Lib;
-class WindowTitle {
+class WindowUtils {
public static var winTitle(default, set):String;
public static function set_winTitle(newWinTitle:String):String {
winTitle = newWinTitle;
@@ -22,10 +22,26 @@ class WindowTitle {
return endPrefix;
}
- public static inline function init()
- reset();
+ public static var preventClosing:Bool = true;
+ public static var onClosing:Void->Void;
- public static inline function reset() {
+ static var __triedClosing:Bool = false;
+ public static inline function resetClosing() __triedClosing = false;
+
+ public static inline function init() {
+ resetTitle();
+ resetClosing();
+
+ Lib.application.window.onClose.add(function () {
+ if (preventClosing && !__triedClosing) {
+ Lib.application.window.onClose.cancel();
+ __triedClosing = true;
+ }
+ if (onClosing != null) onClosing();
+ });
+ }
+
+ public static inline function resetTitle() {
winTitle = Lib.application.meta["name"];
prefix = endfix = "";
updateTitle();
diff --git a/source/funkin/editors/SaveWarning.hx b/source/funkin/editors/SaveWarning.hx
new file mode 100644
index 00000000..73b465ac
--- /dev/null
+++ b/source/funkin/editors/SaveWarning.hx
@@ -0,0 +1,46 @@
+package funkin.editors;
+
+class SaveWarning {
+ public static var showWarning(default, set):Bool;
+ public static function set_showWarning(warning:Bool):Bool
+ return WindowUtils.preventClosing = showWarning = warning;
+
+ public static var selectionClass:Class = null;
+
+ public static function init() {
+ showWarning = false;
+
+ WindowUtils.onClosing = function () {
+ if (SaveWarning.showWarning) triggerWarning(true);
+ }
+ }
+
+ public static function triggerWarning(?closingWindow:Bool = false) {
+ if (FlxG.state != null && FlxG.state is UIState) {
+ FlxG.state.openSubState(new UIWarningSubstate("Unsaved Changes!", "Your changes will be lost if you don't save them. (Can't be recovered)\n\n\nWould you like to Cancel?",
+ [{
+ label: closingWindow ? "Exit Window" : "Exit To Menu",
+ color: 0x74732D,
+ onClick: function(_) {
+ if (!closingWindow) {
+ if (selectionClass != null) FlxG.switchState(Type.createInstance(SaveWarning.selectionClass, []));
+ if (closingWindow) WindowUtils.resetClosing();
+ } else {
+ WindowUtils.preventClosing = false; WindowUtils.resetClosing();
+ Sys.exit(0);
+ }
+ }
+ },
+ {
+ label: "Cancel",
+ color: 0x969533,
+ onClick: function (_) {if (closingWindow) WindowUtils.resetClosing();}
+ }], false));
+ }
+ }
+
+ public static inline function reset() {
+ SaveWarning.showWarning = false;
+ SaveWarning.selectionClass = null;
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/editors/character/CharacterAnimButtons.hx b/source/funkin/editors/character/CharacterAnimButtons.hx
index e0ebd3b9..bccc51a4 100644
--- a/source/funkin/editors/character/CharacterAnimButtons.hx
+++ b/source/funkin/editors/character/CharacterAnimButtons.hx
@@ -39,6 +39,7 @@ class CharacterAnimButtons extends UIButton {
editButton = new UIButton(ghostButton.x+32+17, y, "", function () {
CharacterEditor.instance.editAnimWithUI(this.anim);
}, 32);
+ editButton.frames = Paths.getFrames("editors/ui/grayscale-button");
editButton.color = FlxColor.YELLOW;
editButton.autoAlpha = false;
members.push(editButton);
diff --git a/source/funkin/editors/character/CharacterEditor.hx b/source/funkin/editors/character/CharacterEditor.hx
index 970288ff..287b4a44 100644
--- a/source/funkin/editors/character/CharacterEditor.hx
+++ b/source/funkin/editors/character/CharacterEditor.hx
@@ -11,7 +11,7 @@ import flixel.input.keyboard.FlxKey;
import funkin.game.Character;
class CharacterEditor extends UIState {
- var __character:String;
+ static var __character:String;
public var character:Character;
public var ghosts:CharacterGhostsHandler;
@@ -45,12 +45,14 @@ class CharacterEditor extends UIState {
public function new(character:String) {
super();
- __character = character;
+ if (character != null) __character = character;
}
public override function create() {
super.create();
+ WindowUtils.endfix = " (Character Editor)";
+ SaveWarning.selectionClass = CharacterSelection;
topMenu = [
{
label: "File",
@@ -309,12 +311,16 @@ 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 ? "* " : "";
+ SaveWarning.showWarning = undos.unsaved;
}
// TOP MENU OPTIONS
#if REGION
function _file_exit(_) {
- FlxG.switchState(new CharacterSelection());
+ if (undos.unsaved) SaveWarning.triggerWarning();
+ else FlxG.switchState(new CharacterSelection());
}
function _file_new(_) {
@@ -323,9 +329,10 @@ class CharacterEditor extends UIState {
function _file_save(_) {
#if sys
sys.io.File.saveContent(
- Assets.getPath(Paths.xml('characters/${character.curCharacter}')),
+ '${Paths.getAssetsRoot()}/data/characters/${character.curCharacter}.xml',
buildCharacter()
);
+ undos.save();
return;
#end
_file_saveas(_);
@@ -335,6 +342,7 @@ class CharacterEditor extends UIState {
openSubState(new SaveSubstate(buildCharacter(), {
defaultSaveFile: '${character.curCharacter}.xml'
}));
+ undos.save();
}
function buildCharacter():String {
diff --git a/source/funkin/editors/character/CharacterSelection.hx b/source/funkin/editors/character/CharacterSelection.hx
index ae5e6635..9d1a60ed 100644
--- a/source/funkin/editors/character/CharacterSelection.hx
+++ b/source/funkin/editors/character/CharacterSelection.hx
@@ -27,13 +27,8 @@ class CharacterSelection extends EditorTreeMenu
];
list.insert(0, new NewOption("New Character", "New Character", function() {
- openSubState(new UIWarningSubstate("New Character", "This feature isnt implemented yet", [
- {
- label: "OK",
- onClick: function(t) {
-
- }
- }
+ openSubState(new UIWarningSubstate("New Character: Feature Not Implemented!", "This feature isnt implemented yet. Please wait for more cne updates to have this functional.\n\n\n- Codename Devs", [
+ {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
]));
}));
diff --git a/source/funkin/editors/charter/ChartDataScreen.hx b/source/funkin/editors/charter/ChartDataScreen.hx
index 175a51c4..d05f5d0f 100644
--- a/source/funkin/editors/charter/ChartDataScreen.hx
+++ b/source/funkin/editors/charter/ChartDataScreen.hx
@@ -1,5 +1,6 @@
package funkin.editors.charter;
+import funkin.game.Stage;
import funkin.backend.chart.ChartData;
import flixel.math.FlxPoint;
import funkin.backend.chart.ChartData.ChartMetaData;
@@ -12,7 +13,7 @@ class ChartDataScreen extends UISubstateWindow {
public var closeButton:UIButton;
public var scrollSpeedStepper:UINumericStepper;
- public var stageTextBox:UITextBox;
+ public var stageTextBox:UIAutoCompleteTextBox;
public function new(data:ChartData) {
super();
@@ -34,7 +35,11 @@ class ChartDataScreen extends UISubstateWindow {
var title:UIText;
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Edit Chart Data", 28));
- stageTextBox = new UITextBox(title.x, title.y + title.height + 38, PlayState.SONG.stage, 200);
+ var stageFileList = Stage.getList(true);
+ if (stageFileList.length == 0) stageFileList = Stage.getList(false);
+
+ stageTextBox = new UIAutoCompleteTextBox(title.x, title.y + title.height + 38, PlayState.SONG.stage, 200);
+ stageTextBox.suggestItems = stageFileList;
add(stageTextBox);
addLabelOn(stageTextBox, "Stage");
diff --git a/source/funkin/editors/charter/ChartHoverNote.hx b/source/funkin/editors/charter/ChartHoverNote.hx
deleted file mode 100644
index d3eee237..00000000
--- a/source/funkin/editors/charter/ChartHoverNote.hx
+++ /dev/null
@@ -1,66 +0,0 @@
-package funkin.editors.charter;
-
-import flixel.tweens.misc.VarTween;
-import flixel.tweens.FlxTween;
-
-class ChartHoverNote extends UISprite {
- public var angleTween:FlxTween;
-
- public var onGrid:Bool = false;
- public var id:Int = 0;
- public var nextID:Int = 0;
-
- public function new() {
- super();
- antialiasing = true;
- loadGraphic(Paths.image('editors/charter/note'), true, 157, 154);
- animation.add("note", [for(i in 0...frames.frames.length) i], 0, true);
- animation.play("note");
- this.setUnstretchedGraphicSize(40, 40, false);
-
- moves = selectable = false;
- colorTransform.color = 0xFFFFFF;
- }
-
- public override function update(elapsed:Float) {
- super.update(elapsed);
-
- var mousePos = FlxG.mouse.getWorldPosition(__lastDrawCameras[0]);
- alpha = FlxMath.lerp(alpha, onGrid ? 0.2 : 0, 1/8);
- id = Math.floor(x / 40);
-
- if (onGrid) {
- Charter.instance.gridBackdropDummy.cursor = BUTTON;
- x = (nextID = Math.floor(mousePos.x / 40)) * 40;
- y = (FlxG.keys.pressed.SHIFT ? (mousePos.y / 40) : Math.floor(mousePos.y / 40)) * 40;
-
- if (id != nextID) {
- if (angleTween != null) angleTween.cancel();
-
- angle = switch(id % 4) {
- case 0: -90;
- case 1: 180;
- case 2: 0;
- case 3: 90;
- default: 0; // how is that even possible
- };
-
- var destAngle = switch((nextID % 4)) {
- case 0: -90;
- case 1: 180;
- case 2: 0;
- case 3: 90;
- default: 0; // how is that even possible
- };
-
- angleTween = FlxTween.tween(this, {angle: destAngle}, 2/3, {ease: function(t) {
- return ((Math.sin(t * Math.PI) * 0.35) * 3 * t * Math.sqrt(1 - t)) + t;
- }, onComplete: function (t) {angleTween = null;}});
- }
- } else if (alpha > 0) {
- Charter.instance.gridBackdropDummy.cursor = ARROW;
- x = mousePos.x - 20; y = mousePos.y -20;
- }
- mousePos.put();
- }
-}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx
index 57dd195c..e04119e8 100644
--- a/source/funkin/editors/charter/Charter.hx
+++ b/source/funkin/editors/charter/Charter.hx
@@ -1,12 +1,15 @@
package funkin.editors.charter;
+import funkin.editors.ui.UITopMenu.UITopMenuButton;
+import funkin.editors.charter.CharterStrumline;
+import funkin.editors.charter.CharterBackdropGroup.EventBackdrop;
import funkin.backend.system.framerate.Framerate;
import haxe.Json;
import flixel.input.keyboard.FlxKey;
import flixel.sound.FlxSound;
import flixel.util.FlxSort;
import flixel.math.FlxPoint;
-import funkin.editors.charter.CharterBackdrop.CharterBackdropDummy;
+import funkin.editors.charter.CharterBackdropGroup.CharterBackdropDummy;
import funkin.backend.system.Conductor;
import funkin.backend.chart.*;
import funkin.backend.chart.ChartData;
@@ -18,9 +21,9 @@ import funkin.editors.ui.UIState;
import openfl.net.FileReference;
class Charter extends UIState {
- var __song:String;
- var __diff:String;
- var __reload:Bool;
+ static var __song:String;
+ static var __diff:String;
+ static var __reload:Bool;
var chart(get, null):ChartData;
private function get_chart()
@@ -43,22 +46,17 @@ class Charter extends UIState {
public var scrollBar:UIScrollBar;
public var songPosInfo:UIText;
+ public var playBackSlider:UISlider;
public var topMenuSpr:UITopMenu;
- public var gridBackdrop:CharterBackdrop;
- public var eventsBackdrop:FlxBackdrop;
+ public var gridBackdrops:CharterBackdropGroup;
+ public var eventsBackdrop:EventBackdrop;
public var addEventSpr:CharterEventAdd;
- public var beatSeparator:FlxBackdrop;
- public var sectionSeparator:FlxBackdrop;
public var gridBackdropDummy:CharterBackdropDummy;
- public var conductorFollowerSpr:FlxSprite;
- public var topLimit:FlxSprite;
- public var bottomLimit:FlxSprite;
- public var bottomSeparator:FlxSprite;
- public var hoverNote:ChartHoverNote;
public var strumlineInfoBG:FlxSprite;
- public var strumlineAddButton:CharterStrumlineExtraButton;
+ public var strumlineAddButton:CharterStrumlineButton;
+ public var strumlineLockButton:CharterStrumlineButton;
public var hitsound:FlxSound;
public var metronome:FlxSound;
@@ -68,7 +66,7 @@ class Charter extends UIState {
/**
* ACTUAL CHART DATA
*/
- public var strumLines:FlxTypedGroup = new FlxTypedGroup();
+ public var strumLines:CharterStrumLineGroup = new CharterStrumLineGroup();
public var notesGroup:CharterNoteGroup = new CharterNoteGroup();
public var eventsGroup:CharterEventGroup = new CharterEventGroup();
@@ -89,15 +87,18 @@ class Charter extends UIState {
public function new(song:String, diff:String, reload:Bool = true) {
super();
- __song = song;
- __diff = diff;
- __reload = reload;
+ if (song != null) {
+ __song = song;
+ __diff = diff;
+ __reload = reload;
+ }
}
public override function create() {
super.create();
- WindowTitle.endfix = " (Chart Editor)";
+ WindowUtils.endfix = " (Chart Editor)";
+ SaveWarning.selectionClass = CharterSelection;
topMenu = [
{
label: "File",
@@ -232,7 +233,7 @@ class Charter extends UIState {
onSelect: _note_selectall
},
{
- label: "Select maeasure",
+ label: "Select measure",
keybind: [CONTROL, SHIFT, A],
onSelect: _note_selectmeasure
},
@@ -268,12 +269,12 @@ class Charter extends UIState {
null,
{
label: 'Show Sections Separator',
- onSelect: _view_showsectionseparator,
+ onSelect: _view_showeventSecSeparator,
icon: Options.charterShowSections ? 1 : 0
},
{
label: 'Show Beats Separator',
- onSelect: _view_showbeatseparator,
+ onSelect: _view_showeventBeatSeparator,
icon: Options.charterShowBeats ? 1 : 0
}
]
@@ -345,43 +346,21 @@ class Charter extends UIState {
charterBG.scrollFactor.set();
add(charterBG);
- gridBackdrop = new CharterBackdrop();
+ gridBackdrops = new CharterBackdropGroup(strumLines);
+ gridBackdrops.notesGroup = this.notesGroup;
- eventsBackdrop = new FlxBackdrop(Paths.image('editors/charter/events-grid'), Y, 0, 0);
+ eventsBackdrop = new EventBackdrop();
eventsBackdrop.x = -eventsBackdrop.width;
- eventsBackdrop.alpha = 0.9;
eventsBackdrop.cameras = [charterCamera];
- add(eventsBackdrop);
-
- hoverNote = new ChartHoverNote();
- hoverNote.cameras = [charterCamera];
-
- add(gridBackdropDummy = new CharterBackdropDummy(gridBackdrop));
- sectionSeparator = new FlxBackdrop(null, Y, 0, 0);
- sectionSeparator.x = -20;
- sectionSeparator.y = -2;
- sectionSeparator.visible = Options.charterShowSections;
- beatSeparator = new FlxBackdrop(null, Y, 0, 0);
- beatSeparator.x = -10;
- beatSeparator.y = -1;
- sectionSeparator.visible = Options.charterShowBeats;
- for(sep in [sectionSeparator, beatSeparator]) {
- sep.makeGraphic(1, 1, -1);
- sep.alpha = 0.5;
- sep.scrollFactor.set(1, 1);
- sep.cameras = [charterCamera];
- }
+ eventsGroup.eventsBackdrop = eventsBackdrop;
+ add(gridBackdropDummy = new CharterBackdropDummy(gridBackdrops));
selectionBox = new UISliceSprite(0, 0, 2, 2, 'editors/ui/selection');
selectionBox.visible = false;
selectionBox.scrollFactor.set(1, 1);
selectionBox.incorporeal = true;
- conductorFollowerSpr = new FlxSprite(0, 0).makeGraphic(1, 1, -1);
- conductorFollowerSpr.scale.set(gridBackdrop.strumlinesAmount * 4 * 40, 4);
- conductorFollowerSpr.updateHitbox();
-
- conductorFollowerSpr.cameras = selectionBox.cameras = notesGroup.cameras = gridBackdrop.cameras = [charterCamera];
+ selectionBox.cameras = notesGroup.cameras = gridBackdrops.cameras = [charterCamera];
topMenuSpr = new UITopMenu(topMenu);
topMenuSpr.cameras = uiGroup.cameras = [uiCamera];
@@ -390,42 +369,34 @@ class Charter extends UIState {
scrollBar.cameras = [uiCamera];
scrollBar.onChange = function(v) {
if (!FlxG.sound.music.playing)
- Conductor.songPosition = Conductor.getTimeForStep(v);
+ Conductor.songPosition = Conductor.getTimeForStep(v) + Conductor.songOffset;
};
uiGroup.add(scrollBar);
songPosInfo = new UIText(FlxG.width - 30 - 400, scrollBar.y + 10, 400, "00:00\nBeat: 0\nStep: 0\nMeasure: 0\nBPM: 0\nTime Signature: 4/4");
- songPosInfo.alignment = RIGHT;
+ songPosInfo.alignment = RIGHT; songPosInfo.optimized = true;
uiGroup.add(songPosInfo);
- topLimit = new FlxSprite();
- topLimit.makeGraphic(1, 1, -1);
- topLimit.color = 0xFF888888;
- topLimit.blend = MULTIPLY;
-
- bottomLimit = new FlxSprite();
- bottomLimit.makeGraphic(1, 1, -1);
- bottomLimit.color = 0xFF888888;
- bottomLimit.blend = MULTIPLY;
-
- bottomSeparator = new FlxSprite();
- bottomSeparator.makeGraphic(1, 1, -1);
- bottomSeparator.alpha = 0.5;
- bottomSeparator.scrollFactor.set(1, 1);
- bottomSeparator.cameras = [charterCamera];
-
- bottomSeparator.x = -20;
- bottomSeparator.y = -2;
+ playBackSlider = new UISlider(FlxG.width - 160 - 26 - 20, (23/2) - (12/2), 160, 1, [{start: 0.25, end: 1, size: 0.5}, {start: 1, end: 2, size: 0.5}], true);
+ playBackSlider.onChange = function (v) {
+ FlxG.sound.music.pitch = vocals.pitch = v;
+ };
+ uiGroup.add(playBackSlider);
strumlineInfoBG = new UISprite();
strumlineInfoBG.loadGraphic(Paths.image('editors/charter/strumline-info-bg'));
strumlineInfoBG.y = 23;
strumlineInfoBG.scrollFactor.set();
- strumlineAddButton = new CharterStrumlineExtraButton("editors/charter/add-strumline", "Add New");
+ strumlineAddButton = new CharterStrumlineButton("editors/charter/add-strumline", "Create New");
strumlineAddButton.onClick = createStrumWithUI;
- strumlineAddButton.cameras = [charterCamera];
+ strumlineLockButton = new CharterStrumlineButton("editors/charter/lock-strumline", "Lock/Unlock");
+ strumlineLockButton.onClick = function () {
+ if (strumLines != null) strumLines.draggable = !strumLines.draggable;
+ };
+
+ strumlineAddButton.cameras = strumlineLockButton.cameras = [charterCamera];
strumlineInfoBG.cameras = [charterCamera];
strumLines.cameras = [charterCamera];
@@ -434,19 +405,14 @@ class Charter extends UIState {
addEventSpr.cameras = [charterCamera];
// adds grid and notes so that they're ALWAYS behind the UI
- add(gridBackdrop);
- add(hoverNote);
- add(sectionSeparator);
- add(beatSeparator);
+ add(gridBackdrops);
+ add(eventsBackdrop);
add(addEventSpr);
add(eventsGroup);
add(notesGroup);
- add(topLimit);
- add(bottomLimit);
- add(bottomSeparator);
- add(conductorFollowerSpr);
add(selectionBox);
add(strumlineInfoBG);
+ add(strumlineLockButton);
add(strumlineAddButton);
add(strumLines);
// add the top menu last OUT of the ui group so that it stays on top
@@ -461,6 +427,7 @@ class Charter extends UIState {
Framerate.memoryCounter.alpha = 0.4;
Framerate.codenameBuildField.alpha = 0.4;
}
+ updateDisplaySprites();
}
override function destroy() {
@@ -469,7 +436,6 @@ class Charter extends UIState {
Framerate.memoryCounter.alpha = 1;
Framerate.codenameBuildField.alpha = 1;
}
- WindowTitle.reset();
super.destroy();
}
@@ -485,11 +451,30 @@ class Charter extends UIState {
vocals = FlxG.sound.load(Paths.voices(__song, __diff));
vocals.group = FlxG.sound.defaultMusicGroup;
- trace("generating notes...");
- for(strL in PlayState.SONG.strumLines)
- createStrumline(strumLines.members.length, strL, false);
+ gridBackdrops.createGrids(PlayState.SONG.strumLines.length);
- trace("generating events...");
+ for(strL in PlayState.SONG.strumLines)
+ createStrumline(strumLines.members.length, strL, false, false);
+
+ // create notes
+ notesGroup.autoSort = false;
+ var noteCount:Int = 0;
+ for (strL in PlayState.SONG.strumLines)
+ for (note in strL.notes) noteCount++;
+ notesGroup.preallocate(noteCount);
+
+ var notesCreated:Int = 0;
+ for (i => strL in PlayState.SONG.strumLines)
+ for (note in strL.notes) {
+ var n = new CharterNote();
+ var t = Conductor.getStepForTime(note.time);
+ n.updatePos(t, note.id, Conductor.getStepForTime(note.time + note.sLen) - t, note.type, strumLines.members[i]);
+ notesGroup.members[notesCreated++] = n;
+ }
+ notesGroup.sortNotes();
+ notesGroup.autoSort = true;
+
+ // create events
var __last:CharterEvent = null;
var __lastTime:Float = Math.NaN;
for(e in PlayState.SONG.events) {
@@ -509,13 +494,14 @@ class Charter extends UIState {
refreshBPMSensitive();
}
+ public var __endStep:Float = 0;
public function refreshBPMSensitive() {
// refreshes everything dependant on BPM, and BPM changes
var length = FlxG.sound.music.getDefault(vocals).length;
- scrollBar.length = Conductor.getStepForTime(length);
+ scrollBar.length = __endStep = Conductor.getStepForTime(length);
- bottomLimit.y = Conductor.getStepForTime(length) * 40;
- bottomSeparator.y = bottomLimit.y-2;
+ gridBackdrops.bottomLimitY = Conductor.getStepForTime(length) * 40;
+ eventsBackdrop.bottomSeparator.y = gridBackdrops.bottomLimitY-2;
}
public override function beatHit(curBeat:Int) {
@@ -532,6 +518,7 @@ class Charter extends UIState {
#if REGION
var gridActionType:CharterGridActionType = NONE;
var dragStartPos:FlxPoint = new FlxPoint();
+ var selectionDragging:Bool = false;
public function updateNoteLogic(elapsed:Float) {
for (group in [notesGroup, eventsGroup]) {
@@ -564,7 +551,6 @@ class Charter extends UIState {
if (!gridBackdropDummy.hoveredByChild && !FlxG.mouse.pressed)
gridActionType = NONE;
selectionBox.visible = false;
- hoverNote.onGrid = false;
switch(gridActionType) {
case BOX:
if (gridBackdropDummy.hoveredByChild) {
@@ -600,12 +586,18 @@ class Charter extends UIState {
if (!FlxG.mouse.pressed)
gridActionType = NONE;
case DRAG:
- if (FlxG.mouse.pressed) {
+ selectionDragging = FlxG.mouse.pressed;
+ if (selectionDragging) {
+ gridBackdrops.draggingObj = null;
selection.loop(function (n:CharterNote) {
- n.setPosition(n.id * 40 + (mousePos.x - dragStartPos.x), n.step * 40 + (mousePos.y - dragStartPos.y));
+ n.snappedToStrumline = false;
+ n.setPosition(n.fullID * 40 + (mousePos.x - dragStartPos.x), n.step * 40 + (mousePos.y - dragStartPos.y));
+ n.y = FlxMath.bound(n.y, 0, (__endStep*40) - n.height);
+ n.x = FlxMath.bound(n.x, 0, ((strumLines.members.length * 4)-1) * 40);
n.cursor = HAND;
}, function (e:CharterEvent) {
e.y = e.step * 40 + (mousePos.y - dragStartPos.y) - 17;
+ e.y = FlxMath.bound(e.y, -17, (__endStep*40)-17);
e.cursor = HAND;
});
currentCursor = HAND;
@@ -623,14 +615,30 @@ class Charter extends UIState {
: CoolUtil.floorInt((mousePos.y - dragStartPos.y) / 40);
var horizontalChange:Int = CoolUtil.floorInt((mousePos.x - dragStartPos.x) / 40);
var changePoint:FlxPoint = FlxPoint.get(verticalChange, horizontalChange);
+ var undoDrags:Array = [];
for (s in selection) {
- if (!s.draggable) continue;
- s.handleDrag(changePoint);
+ if (s.draggable) {
+ var boundedChange:FlxPoint = changePoint.clone();
+
+ // Some maths, so cool bro -lunar (i dont know why i quopte my self here)
+ if (s.step + changePoint.x < 0) boundedChange.x += Math.abs(s.step + changePoint.x);
+ if (s.step + changePoint.x > __endStep-1) boundedChange.x -= (s.step + changePoint.x) - (__endStep-1);
+
+ if (s is CharterNote) {
+ var note:CharterNote = cast (s, CharterNote);
+ if (note.fullID + changePoint.y < 0) boundedChange.y += Math.abs(note.fullID + changePoint.y);
+ if (note.fullID + changePoint.y > (strumLines.members.length*4)-1) boundedChange.y -= (note.fullID + changePoint.y) - ((strumLines.members.length*4)-1);
+ }
+
+ s.handleDrag(changePoint);
+ undoDrags.push({selectable:s, change: boundedChange});
+ }
+
+ if (s is CharterNote) cast(s, CharterNote).snappedToStrumline = true;
if (s is UISprite) cast(s, UISprite).cursor = BUTTON;
}
- sortNotes();
- undos.addToUndo(CSelectionDrag(selection, changePoint.clone()));
+ if (!(changePoint.x == 0 && changePoint.y == 0)) undos.addToUndo(CSelectionDrag(undoDrags));
changePoint.put();
hoverOffset.put();
@@ -643,34 +651,27 @@ class Charter extends UIState {
if (FlxG.mouse.justPressed)
FlxG.mouse.getWorldPosition(charterCamera, dragStartPos);
- hoverNote.visible = hoveredSprite != null ? hoveredSprite is CharterNote : true;
-
if (gridBackdropDummy.hovered) {
// AUTO DETECT
if (FlxG.mouse.pressed && (Math.abs(mousePos.x - dragStartPos.x) > 20 || Math.abs(mousePos.y - dragStartPos.y) > 20))
gridActionType = BOX;
var id = Math.floor(mousePos.x / 40);
- var mouseOnGrid = id >= 0 && id < 4 * gridBackdrop.strumlinesAmount && mousePos.y >= 0;
+ var mouseOnGrid = id >= 0 && id < 4 * gridBackdrops.strumlinesAmount && mousePos.y >= 0;
- hoverNote.onGrid = mouseOnGrid;
-
if (FlxG.mouse.justReleased) {
- if (selection.length > 1) {
- selection = []; // clear selection
- } else {
- if (mouseOnGrid) {
+ for (n in selection) n.selected = false;
+ selection = [];
+
+ if (mouseOnGrid && mousePos.y > 0 && mousePos.y < (__endStep)*40) {
var note = new CharterNote();
- note.updatePos(FlxG.keys.pressed.SHIFT ? (mousePos.y / 40) : Math.floor(mousePos.y / 40), id, 0, 0);
+ note.updatePos(FlxMath.bound(FlxG.keys.pressed.SHIFT ? ((mousePos.y-20) / 40) : Math.floor(mousePos.y / 40), 0, __endStep-1), id % 4, 0, 0, strumLines.members[Std.int(id/4)]);
notesGroup.add(note);
selection = [note];
- sortNotes();
undos.addToUndo(CCreateSelection([note]));
}
- }
}
} else if (gridBackdropDummy.hoveredByChild) {
- // TODO: NOTE DRAGGING
if (FlxG.mouse.pressed && (Math.abs(mousePos.x - dragStartPos.x) > 5 || Math.abs(mousePos.y - dragStartPos.y) > 5)) {
var noteHovered:Bool = false;
for(n in selection)
@@ -678,7 +679,6 @@ class Charter extends UIState {
noteHovered = true;
break;
}
-
gridActionType = noteHovered ? DRAG : INVALID_DRAG;
}
}
@@ -689,6 +689,7 @@ class Charter extends UIState {
}
}
addEventSpr.selectable = !selectionBox.visible;
+
if (gridActionType == NONE && mousePos.x < 0 && mousePos.x > -addEventSpr.bWidth) {
addEventSpr.incorporeal = false;
addEventSpr.sprAlpha = lerp(addEventSpr.sprAlpha, 0.75, 0.25);
@@ -728,7 +729,6 @@ class Charter extends UIState {
eventsGroup.remove(event, true);
event.kill();
}
- sortNotes();
if (addToUndo)
undos.addToUndo(CDeleteSelection([selected]));
@@ -739,6 +739,7 @@ class Charter extends UIState {
public function createSelection(selection:Selection, addToUndo:Bool = true) {
if (selection.length <= 0) return [];
+ notesGroup.autoSort = false;
selection.loop(function (n:CharterNote) {
notesGroup.add(n);
n.revive();
@@ -747,7 +748,8 @@ class Charter extends UIState {
e.revive();
e.refreshEventIcons();
}, false);
- sortNotes();
+ notesGroup.sortNotes();
+ notesGroup.autoSort = true;
for (s in selection)
if (s is CharterEvent) {
@@ -763,6 +765,7 @@ class Charter extends UIState {
public function deleteSelection(selection:Selection, addToUndo:Bool = true) {
if (selection.length <= 0) return [];
+ notesGroup.autoSort = false;
for (objects in [notesGroup, eventsGroup]) {
var group = cast(objects, FlxTypedGroup);
var member = 0;
@@ -773,7 +776,8 @@ class Charter extends UIState {
else member++;
}
}
- sortNotes();
+ notesGroup.sortNotes();
+ notesGroup.autoSort = true;
for (s in selection)
if (s is CharterEvent) {
@@ -787,70 +791,50 @@ class Charter extends UIState {
}
// STRUMLINE DELETION/CREATION
- public function createStrumline(strumLineID:Int, strL:ChartStrumLine, addToUndo:Bool = true) {
+ public function createStrumline(strumLineID:Int, strL:ChartStrumLine, addToUndo:Bool = true, ?__createNotes:Bool = true) {
var cStr = new CharterStrumline(strL);
strumLines.insert(strumLineID, cStr);
+ strumLines.snapStrums();
- // Push forward notes that are infront of the strumLine
- for (note in notesGroup.members) {
- if (Std.int(note.id / 4) >= strumLineID)
- note.updatePos(note.step, note.id + 4, note.susLength, note.type);
+ if (__createNotes) {
+ var toBeCreated:Selection = [];
+ for(note in strL.notes) {
+ var n = new CharterNote();
+ var t = Conductor.getStepForTime(note.time);
+ n.updatePos(t, note.id, Conductor.getStepForTime(note.time + note.sLen) - t, note.type, cStr);
+ notesGroup.add(n);
+ }
+ createSelection(toBeCreated, false);
}
- var noteOffset = (strumLines.members.indexOf(cStr)) * 4;
-
- for(note in strL.notes) {
- var n = new CharterNote();
- var t = Conductor.getStepForTime(note.time);
- n.updatePos(t, noteOffset + note.id, Conductor.getStepForTime(note.time + note.sLen) - t, note.type);
- notesGroup.add(n);
- }
- sortNotes();
-
if (addToUndo)
undos.addToUndo(CCreateStrumLine(strumLineID, strL));
}
public function deleteStrumline(strumLineID:Int, addToUndo:Bool = true) {
+ var undoNotes:Array = [];
removeStrumlineFromSelection(strumLineID);
+ var i = 0;
+ var toBeDeleted:Selection = [];
+ while(i < notesGroup.members.length) {
+ var note = notesGroup.members[i];
+ if (note.strumLineID == strumLineID) {
+ undoNotes.push(buildNote(note));
+ toBeDeleted.push(note);
+ } else i++;
+ }
+ deleteSelection(toBeDeleted, false);
+
var strL = strumLines.members[strumLineID].strumLine;
strumLines.members[strumLineID].destroy();
strumLines.members.remove(strumLines.members[strumLineID]);
+ strumLines.snapStrums();
- var deletedstrumNotes:Array = [];
- // Delete this strums notes
- var i = 0; // thanks yosh!!!!! (deleteNote removes a index)
- while(i < notesGroup.members.length) {
- var note = notesGroup.members[i];
- if (Std.int(note.id / 4) == strumLineID) { // thanks neo lol!!!!!
- deletedstrumNotes.push(note);
- deleteSingleSelection(note, false);
- } else
- i++;
- }
-
- // Push back strumline notes that are infront
- for (note in notesGroup.members) {
- if (Std.int(note.id / 4) > strumLineID)
- note.updatePos(note.step, note.id - 4, note.susLength, note.type);
- }
- sortNotes();
-
- // Undo shit
if (addToUndo) {
var newStrL = Reflect.copy(strL);
- newStrL.notes.clear();
+ newStrL.notes = undoNotes;
- for (note in deletedstrumNotes) {
- var time = Conductor.getTimeForStep(note.step);
- newStrL.notes.push({
- type: note.type,
- time: time,
- sLen: Conductor.getTimeForStep(note.step + note.susLength) - time,
- id: note.id % 4
- });
- }
undos.addToUndo(CDeleteStrumLine(strumLineID, newStrL));
}
}
@@ -889,42 +873,47 @@ class Charter extends UIState {
while(i < selection.length) {
if (selection[i] is CharterNote) {
var note = cast (selection[i], CharterNote);
- if (Std.int(note.id / 4) == strumLineID)
+ if (note.strumLineID == strumLineID)
selection.remove(note);
else i++;
}
}
}
-
- public inline function sortNotes() {
- notesGroup.sort(function(i, n1, n2) {
- if (n1.step == n2.step)
- return FlxSort.byValues(FlxSort.ASCENDING, n1.id, n2.id);
- return FlxSort.byValues(FlxSort.ASCENDING, n1.step, n2.step);
- });
- }
#end
var __crochet:Float;
public override function update(elapsed:Float) {
updateNoteLogic(elapsed);
+ if (FlxG.sound.music.playing) {
+ gridBackdrops.conductorSprY = curStepFloat * 40;
+ } else {
+ gridBackdrops.conductorSprY = lerp(gridBackdrops.conductorSprY, curStepFloat * 40, 1/3);
+ }
+ charterCamera.scroll.set(
+ ((((40*4) * gridBackdrops.strumlinesAmount) - FlxG.width) / 2),
+ gridBackdrops.conductorSprY - (FlxG.height * 0.5)
+ );
+
+ if (topMenuSpr.members[5] != null) {
+ var playBackMenu:UITopMenuButton = cast topMenuSpr.members[5];
+ playBackMenu.x = playBackSlider.x-playBackSlider.startText.width-10-playBackSlider.valueStepper.bWidth-playBackMenu.bWidth-10;
+ playBackMenu.label.offset.x = -2;
+ }
+
super.update(elapsed);
scrollBar.size = (FlxG.height / 40 / charterCamera.zoom);
scrollBar.start = Conductor.curStepFloat - (scrollBar.size / 2);
- if (gridBackdrop.strumlinesAmount != strumLines.members.length)
+ if (gridBackdrops.strumlinesAmount != strumLines.members.length)
updateDisplaySprites();
- sectionSeparator.spacing.y = (10 * Conductor.beatsPerMesure * Conductor.stepsPerBeat) - 1;
- beatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1;
-
// TODO: canTypeText in case an ui input element is focused
if (true) {
__crochet = ((60 / Conductor.bpm) * 1000);
- if(FlxG.keys.justPressed.ANY)
+ if(FlxG.keys.justPressed.ANY && !strumLines.isDragging && this.currentFocus == null)
UIUtil.processShortcuts(topMenu);
if (FlxG.keys.pressed.CONTROL) {
@@ -934,7 +923,7 @@ class Charter extends UIState {
}
} else {
if (!FlxG.sound.music.playing) {
- Conductor.songPosition -= __crochet * FlxG.mouse.wheel;
+ Conductor.songPosition -= (__crochet * FlxG.mouse.wheel) - Conductor.songOffset;
}
}
}
@@ -954,58 +943,31 @@ class Charter extends UIState {
+ '\nBPM: ${Conductor.bpm}'
+ '\nTime Signature: ${Conductor.beatsPerMesure}/${Conductor.stepsPerBeat}';
- if (FlxG.sound.music.playing) {
- conductorFollowerSpr.y = curStepFloat * 40;
- } else {
- conductorFollowerSpr.y = lerp(conductorFollowerSpr.y, curStepFloat * 40, 1/3);
- }
- charterCamera.scroll.set(conductorFollowerSpr.x + ((conductorFollowerSpr.scale.x - FlxG.width) / 2), conductorFollowerSpr.y - (FlxG.height * 0.5));
- if (charterCamera.zoom != (charterCamera.zoom = lerp(charterCamera.zoom, __camZoom, 0.125))) {
+ if (charterCamera.zoom != (charterCamera.zoom = lerp(charterCamera.zoom, __camZoom, 0.125)))
updateDisplaySprites();
- }
- WindowTitle.prefix = undos.unsaved ? "UNSAVED | " : "";
+ WindowUtils.prefix = undos.unsaved ? "* " : "";
+ SaveWarning.showWarning = undos.unsaved;
}
public static var startTime:Float = 0;
public static var startHere:Bool = false;
function updateDisplaySprites() {
- gridBackdrop.strumlinesAmount = strumLines.members.length;
-
- conductorFollowerSpr.scale.set(gridBackdrop.strumlinesAmount * 4 * 40, 4);
- conductorFollowerSpr.updateHitbox();
-
- sectionSeparator.scale.set((gridBackdrop.strumlinesAmount * 4 * 40) + 20, 4);
- sectionSeparator.updateHitbox();
-
- beatSeparator.scale.set((gridBackdrop.strumlinesAmount * 4 * 40) + 10, 2);
- beatSeparator.updateHitbox();
+ gridBackdrops.strumlinesAmount = strumLines.members.length;
charterBG.scale.set(1 / charterCamera.zoom, 1 / charterCamera.zoom);
- topLimit.scale.set(gridBackdrop.strumlinesAmount * 4 * 40, Math.ceil(FlxG.height / charterCamera.zoom));
- topLimit.updateHitbox();
- topLimit.y = -topLimit.height;
-
- bottomLimit.scale.set(gridBackdrop.strumlinesAmount * 4 * 40, Math.ceil(FlxG.height / charterCamera.zoom));
- bottomLimit.updateHitbox();
-
- bottomSeparator.scale.set((gridBackdrop.strumlinesAmount * 4 * 40) + 20, 4);
- bottomSeparator.updateHitbox();
strumlineInfoBG.scale.set(FlxG.width / charterCamera.zoom, 1);
strumlineInfoBG.updateHitbox();
strumlineInfoBG.screenCenter(X);
strumlineInfoBG.y = -(((FlxG.height - (2 * topMenuSpr.bHeight)) / charterCamera.zoom) - FlxG.height) / 2;
- for(id=>str in strumLines.members) {
- if (str == null) continue;
- str.x = id * 40 * 4;
- str.y = strumlineInfoBG.y;
- }
+ for(id=>str in strumLines.members)
+ if (str != null) str.y = strumlineInfoBG.y;
- strumlineAddButton.x = strumLines.members.length * (40 * 4);
strumlineAddButton.y = strumlineInfoBG.y;
+ strumlineLockButton.y = strumlineInfoBG.y;
}
var zoom(default, set):Float = 0;
@@ -1020,24 +982,13 @@ class Charter extends UIState {
// TOP MENU OPTIONS
#if REGION
function _file_exit(_) {
- if (undos.unsaved) {
- FlxG.state.openSubState(new UIWarningSubstate("Unsaved Changes", "Your changes will be lost if you don't save them.", [
- {
- label: "Exit",
- onClick: function(_) FlxG.switchState(new CharterSelection())
- },
- {
- label: "Cancel",
- onClick: function (_) {}
- }
- ]));
- } else
- FlxG.switchState(new CharterSelection());
+ if (undos.unsaved) SaveWarning.triggerWarning();
+ else FlxG.switchState(new CharterSelection());
}
function _file_save(_) {
#if sys
- saveTo('${Paths.getAssetsRoot()}/songs/${__song.toLowerCase().replace("-", " ")}');
+ saveTo('${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}');
undos.save();
return;
#end
@@ -1054,7 +1005,7 @@ class Charter extends UIState {
function _file_meta_save(_) {
#if sys
sys.io.File.saveContent(
- '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase().replace("-", " ")}/meta.json',
+ '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json',
Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, "\t")
);
return;
@@ -1086,7 +1037,7 @@ class Charter extends UIState {
for (s in selection)
if (s is CharterNote) {
var note:CharterNote = cast(s, CharterNote);
- CNote(note.step - minStep, note.id, note.susLength, note.type);
+ CNote(note.step - minStep, note.id, note.strumLineID, note.susLength, note.type);
} else if (s is CharterEvent) {
var event = cast(s,CharterEvent);
CEvent(event.step - minStep, [for (event in event.events) Reflect.copy(event)]);
@@ -1100,19 +1051,21 @@ class Charter extends UIState {
var sObjects:Array = [];
for(c in clipboard) {
switch(c) {
- case CNote(step, id, sLen, type):
+ case CNote(step, id, strumLineID, susLength, type):
var note = new CharterNote();
- note.updatePos(minStep + step + 1, id, sLen, type);
+ note.updatePos(minStep + step, id, susLength, type, strumLines.members[Std.int(FlxMath.bound(strumLineID, 0, strumLines.length-1))]);
notesGroup.add(note);
sObjects.push(note);
case CEvent(step, events):
- var event = new CharterEvent(minStep + step + 1, events);
+ var event = new CharterEvent(minStep + step, events);
event.refreshEventIcons();
eventsGroup.add(event);
sObjects.push(event);
}
}
selection = sObjects;
+ _edit_copy(_); // to fix stupid bugs
+
undos.addToUndo(CCreateSelection(sObjects.copy()));
}
@@ -1129,6 +1082,8 @@ class Charter extends UIState {
}
function _edit_undo(_) {
+ if (strumLines.isDragging || selectionDragging) return;
+
selection = [];
var undo = undos.undo();
switch(undo) {
@@ -1137,6 +1092,10 @@ class Charter extends UIState {
createStrumline(strumLineID, strumLine, false);
case CCreateStrumLine(strumLineID, strumLine):
deleteStrumline(strumLineID, false);
+ case COrderStrumLine(strumLineID, oldID, newID):
+ var strumLine:CharterStrumline = strumLines.members[strumLineID];
+ strumLines.orderStrumline(strumLine, oldID);
+ undos.redoList[0] = COrderStrumLine(strumLines.members.indexOf(strumLine), oldID, newID);
case CEditStrumLine(strumLineID, oldStrumLine, newStrumLine):
strumLines.members[strumLineID].strumLine = oldStrumLine;
strumLines.members[strumLineID].updateInfo();
@@ -1144,10 +1103,11 @@ class Charter extends UIState {
deleteSelection(selection, false);
case CDeleteSelection(selection):
createSelection(selection, false);
- case CSelectionDrag(selection, change):
- for (s in selection)
- if (s.draggable) s.handleDrag(change * -1);
- this.selection = selection;
+ case CSelectionDrag(selectionDrags):
+ for (s in selectionDrags)
+ if (s.selectable.draggable) s.selectable.handleDrag(s.change * -1);
+
+ this.selection = [for (s in selectionDrags) s.selectable];
case CEditSustains(changes):
for(n in changes)
n.note.updatePos(n.note.step, n.note.id, n.before, n.note.type);
@@ -1163,6 +1123,8 @@ class Charter extends UIState {
}
function _edit_redo(_) {
+ if (strumLines.isDragging || selectionDragging) return;
+
selection = [];
var redo = undos.redo();
switch(redo) {
@@ -1171,6 +1133,10 @@ class Charter extends UIState {
deleteStrumline(strumLineID, false);
case CCreateStrumLine(strumLineID, strumLine):
createStrumline(strumLineID, strumLine, false);
+ case COrderStrumLine(strumLineID, oldID, newID):
+ var strumLine:CharterStrumline = strumLines.members[strumLineID];
+ strumLines.orderStrumline(strumLine, newID);
+ undos.undoList[0] = COrderStrumLine(strumLines.members.indexOf(strumLine), oldID, newID);
case CEditStrumLine(strumLineID, oldStrumLine, newStrumLine):
strumLines.members[strumLineID].strumLine = newStrumLine;
strumLines.members[strumLineID].updateInfo();
@@ -1178,10 +1144,10 @@ class Charter extends UIState {
createSelection(selection, false);
case CDeleteSelection(selection):
deleteSelection(selection, false);
- case CSelectionDrag(selection, change):
- for (s in selection)
- if (s.draggable) s.handleDrag(change);
- this.selection = selection;
+ case CSelectionDrag(selectionDrags):
+ for (s in selectionDrags)
+ if (s.selectable.draggable) s.selectable.handleDrag(s.change);
+ //this.selection = selection;
case CEditSustains(changes):
for(n in changes)
n.note.updatePos(n.note.step, n.note.id, n.after, n.note.type);
@@ -1214,7 +1180,7 @@ class Charter extends UIState {
FlxG.state.openSubState(new MetaDataScreen(PlayState.SONG.meta));
function _playback_play(_) {
- if (Conductor.songPosition >= FlxG.sound.music.getDefault(vocals).length) return;
+ if (Conductor.songPosition >= FlxG.sound.music.getDefault(vocals).length - Conductor.songOffset) return;
if (FlxG.sound.music.playing) {
FlxG.sound.music.pause();
@@ -1222,7 +1188,7 @@ class Charter extends UIState {
} else {
FlxG.sound.music.play();
vocals.play();
- vocals.time = FlxG.sound.music.time = Conductor.songPosition;
+ vocals.time = FlxG.sound.music.time = Conductor.songPosition + Conductor.songOffset * 2;
}
}
@@ -1239,11 +1205,11 @@ class Charter extends UIState {
}
function _playback_back(_) {
if (FlxG.sound.music.playing) return;
- Conductor.songPosition -= Conductor.beatsPerMesure * __crochet;
+ Conductor.songPosition -= (Conductor.beatsPerMesure * __crochet);
}
function _playback_forward(_) {
if (FlxG.sound.music.playing) return;
- Conductor.songPosition += Conductor.beatsPerMesure * __crochet;
+ Conductor.songPosition += (Conductor.beatsPerMesure * __crochet);
}
function _playback_start(_) {
if (FlxG.sound.music.playing) return;
@@ -1265,13 +1231,13 @@ class Charter extends UIState {
zoom = 0;
__camZoom = Math.pow(2, zoom);
}
- function _view_showsectionseparator(t) {
+ function _view_showeventSecSeparator(t) {
t.icon = (Options.charterShowSections = !Options.charterShowSections) ? 1 : 0;
- sectionSeparator.visible = Options.charterShowSections;
+ eventsBackdrop.eventSecSeparator.visible = gridBackdrops.sectionsVisible = Options.charterShowSections;
}
- function _view_showbeatseparator(t) {
+ function _view_showeventBeatSeparator(t) {
t.icon = (Options.charterShowBeats = !Options.charterShowBeats) ? 1 : 0;
- beatSeparator.visible = Options.charterShowBeats;
+ eventsBackdrop.eventBeatSeparator.visible = gridBackdrops.beatsVisible = Options.charterShowBeats;
}
inline function _note_addsustain(t)
@@ -1295,21 +1261,17 @@ class Charter extends UIState {
function changeNoteSustain(change:Float) {
if (selection.length <= 0 || change == 0) return;
- undos.addToUndo(CEditSustains([
- for(s in selection) {
- if (s is CharterNote) {
- var n:CharterNote = cast(s, CharterNote);
- var old:Float = n.susLength;
- n.updatePos(n.step, n.id, Math.max(n.susLength + change, 0));
-
- {
- before: old,
- after: n.susLength,
- note: n
- };
- }
+ var undoChanges:Array = [];
+ for(s in selection) {
+ if (s is CharterNote) {
+ var n:CharterNote = cast(s, CharterNote);
+ var old:Float = n.susLength;
+ n.updatePos(n.step, n.id, Math.max(n.susLength + change, 0));
+ undoChanges.push({before: old, after: n.susLength, note: n});
}
- ]));
+ }
+
+ undos.addToUndo(CEditSustains(undoChanges));
}
public function playtestChart(time:Float = 0, opponentMode = false, here = false) {
@@ -1321,6 +1283,16 @@ class Charter extends UIState {
FlxG.switchState(new PlayState());
}
+ public inline function buildNote(note:CharterNote):ChartNote {
+ var time = Conductor.getTimeForStep(note.step);
+ return {
+ type: note.type,
+ time: time,
+ sLen: Conductor.getTimeForStep(note.step + note.susLength) - time,
+ id: note.id
+ };
+ }
+
public function buildChart() {
PlayState.SONG.strumLines = [];
for(s in strumLines) {
@@ -1328,22 +1300,15 @@ class Charter extends UIState {
PlayState.SONG.strumLines.push(s.strumLine);
}
for(n in notesGroup.members) {
- var strLineID = Std.int(n.id / 4);
- if (PlayState.SONG.strumLines[strLineID] != null) {
- var time = Conductor.getTimeForStep(n.step);
- PlayState.SONG.strumLines[strLineID].notes.push({
- type: n.type,
- time: time,
- sLen: Conductor.getTimeForStep(n.step + n.susLength) - time,
- id: n.id % 4
- });
- }
+ if (PlayState.SONG.strumLines[n.strumLineID] != null)
+ PlayState.SONG.strumLines[n.strumLineID].notes.push(buildNote(n));
}
buildEvents();
}
public function buildEvents() {
PlayState.SONG.events = [];
+ eventsGroup.sortEvents();
for(e in eventsGroup.members) {
for(event in e.events) {
event.time = Conductor.getTimeForStep(e.step);
@@ -1362,23 +1327,24 @@ class Charter extends UIState {
}
public inline function hitsoundsEnabled(id:Int)
- return strumLines.members[Std.int(id / 4)] != null && strumLines.members[Std.int(id / 4)].hitsounds;
+ return strumLines.members[id] != null && strumLines.members[id].hitsounds;
}
enum CharterChange {
CCreateStrumLine(strumLineID:Int, strumLine:ChartStrumLine);
+ CEditStrumLine(strumLineID:Int, oldStrumLine:ChartStrumLine, newStrumLine:ChartStrumLine);
+ COrderStrumLine(strumLineID:Int, oldID:Int, newID:Int);
CDeleteStrumLine(strumLineID:Int, strumLine:ChartStrumLine);
CCreateSelection(selection:Selection);
CDeleteSelection(selection:Selection);
- CSelectionDrag(selection:Selection, change:FlxPoint);
- CEditStrumLine(strumLineID:Int, oldStrumLine:ChartStrumLine, newStrumLine:ChartStrumLine);
+ CSelectionDrag(selectionDrags:Array);
CEditSustains(notes:Array);
CEditEvent(event:CharterEvent, oldEvents:Array, newEvents:Array);
CEditChartData(oldData:{stage:String, speed:Float}, newData:{stage:String, speed:Float});
}
enum CharterCopyboardObject {
- CNote(step:Float, id:Int, susLength:Float, type:Int);
+ CNote(step:Float, id:Int, strumLineID:Int, susLength:Float, type:Int);
CEvent(step:Float, events:Array);
}
@@ -1388,6 +1354,11 @@ typedef NoteSustainChange = {
var after:Float;
}
+typedef SelectionDragChange = {
+ var selectable:ICharterSelectable;
+ var change:FlxPoint;
+}
+
@:forward abstract Selection(Array) from Array to Array {
public inline function new(?array:Array)
this = array == null ? [] : array;
diff --git a/source/funkin/editors/charter/CharterBackdrop.hx b/source/funkin/editors/charter/CharterBackdrop.hx
deleted file mode 100644
index 5768e461..00000000
--- a/source/funkin/editors/charter/CharterBackdrop.hx
+++ /dev/null
@@ -1,58 +0,0 @@
-package funkin.editors.charter;
-
-import openfl.geom.Rectangle;
-import flixel.addons.display.FlxBackdrop;
-
-class CharterBackdrop extends FlxBackdrop {
- public var strumlinesAmount:Int = 1;
-
- public function new() {
- super(null, Y, 0, 0);
-
- makeGraphic(160, 160, 0xFF272727, true);
- pixels.lock();
-
- // Checkerboard
- for(y in 0...4)
- for(x in 0...2)
- pixels.fillRect(new Rectangle(40*((x*2)+(y%2)), 40*y, 40, 40), 0xFF545454);
-
- // Edges
- pixels.fillRect(new Rectangle(0, 0, 1, 160), 0xFFDDDDDD);
- pixels.fillRect(new Rectangle(159, 0, 1, 160), 0xFFDDDDDD);
- pixels.unlock();
- // loadFrame(frame);
- }
-
- public override function draw() {
- var ogX:Float = x;
- for(_ in 0...strumlinesAmount) {
- if (Charter.instance.strumLines.members[_] != null)
- alpha = Charter.instance.strumLines.members[_].strumLine.visible ? 1 : 0.4;
- else alpha = 0.9;
- super.draw();
- x += width;
- }
- x = ogX;
- }
-}
-
-class CharterBackdropDummy extends UISprite {
- var parent:CharterBackdrop;
- public function new(parent:CharterBackdrop) {
- super();
- this.parent = parent;
- cameras = parent.cameras;
- scrollFactor.set(1, 0);
- }
-
- public override function updateButton() {
- camera.getViewRect(__rect);
- UIState.state.updateRectButtonHandler(this, __rect, onHovered);
- }
-
- public override function draw() {
- @:privateAccess
- __lastDrawCameras = [for(c in cameras) c];
- }
-}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/CharterBackdropGroup.hx b/source/funkin/editors/charter/CharterBackdropGroup.hx
new file mode 100644
index 00000000..62324276
--- /dev/null
+++ b/source/funkin/editors/charter/CharterBackdropGroup.hx
@@ -0,0 +1,303 @@
+package funkin.editors.charter;
+
+import flixel.util.FlxPool;
+import flixel.graphics.FlxGraphic;
+import funkin.backend.system.Conductor;
+import openfl.geom.Rectangle;
+import flixel.addons.display.FlxBackdrop;
+
+class CharterBackdropGroup extends FlxTypedGroup {
+ public var strumLineGroup:CharterStrumLineGroup;
+ public var notesGroup:CharterNoteGroup;
+ var __gridGraphic:FlxGraphic;
+
+ public var conductorSprY:Float = 0;
+ public var bottomLimitY:Float = 0;
+ public var sectionsVisible:Bool = true;
+ public var beatsVisible:Bool = true;
+
+ // Just here so you can update display sprites all dat and above
+ public var strumlinesAmount:Int = 0;
+
+ public function new(strumLineGroup:CharterStrumLineGroup) {
+ super();
+ this.strumLineGroup = strumLineGroup;
+
+ __gridGraphic = FlxG.bitmap.create(160, 160, 0xFF272727, true);
+ __gridGraphic.bitmap.lock();
+
+ // Checkerboard
+ for(y in 0...4)
+ for(x in 0...2) __gridGraphic.bitmap.fillRect(new Rectangle(40*((x*2)+(y%2)), 40*y, 40, 40), 0xFF545454);
+
+ // Edges
+ __gridGraphic.bitmap.fillRect(new Rectangle(0, 0, 1, 160), 0xFFDDDDDD);
+ __gridGraphic.bitmap.fillRect(new Rectangle(159, 0, 1, 160), 0xFFDDDDDD);
+ __gridGraphic.bitmap.unlock();
+ }
+
+ public function createGrids(amount:Int = 0) {
+ for (i in 0...amount) {
+ var grid = new CharterBackdrop(__gridGraphic);
+ grid.active = grid.visible = false;
+ add(grid);
+ }
+ }
+
+ public override function update(elapsed:Float) {
+ for (grid in members)
+ grid.active = grid.visible = false;
+
+ super.update(elapsed);
+
+ for (i => strumLine in strumLineGroup.members) {
+ if (strumLine == null) continue;
+
+ if (members[i] == null)
+ members[i] = recycle(CharterBackdrop, () -> {return new CharterBackdrop(__gridGraphic);});
+
+ var grid = members[i];
+ grid.cameras = this.cameras;
+ grid.strumLine = strumLine;
+
+ grid.conductorFollowerSpr.y = conductorSprY;
+ grid.bottomSeparator.y = (grid.bottomLimit.y = bottomLimitY)-2;
+ grid.sectionSeparator.visible = sectionsVisible;
+ grid.beatSeparator.visible = beatsVisible;
+
+ grid.notesGroup.clear();
+ notesGroup.forEach((n) -> {
+ var onStr:Bool = (n.snappedToStrumline ? n.strumLineID : Std.int(FlxMath.bound((n.x+n.width)/(40*4), 0, strumLineGroup.members.length-1))) == i;
+ if(n.exists && n.visible && onStr)
+ grid.notesGroup.add(n);
+ });
+
+ grid.active = grid.visible = true;
+ grid.updateSprites();
+ }
+
+ }
+
+ public var draggingObj:CharterBackdrop = null;
+ override function draw() @:privateAccess {
+ var i:Int = 0;
+ var basic:FlxBasic = null;
+
+ for (grid in members) {
+ if (strumLineGroup.draggingObj == null) break;
+ if (grid.strumLine == null) continue;
+
+ if (strumLineGroup.draggingObj.strumLine == grid.strumLine.strumLine) {
+ draggingObj = grid;
+ break;
+ }
+ }
+
+ var oldDefaultCameras = FlxCamera._defaultCameras;
+ if (cameras != null)
+ FlxCamera._defaultCameras = cameras;
+
+ while (i < length)
+ {
+ basic = members[i++];
+ if (basic != null && basic != draggingObj && basic.exists && basic.visible)
+ basic.draw();
+ }
+ if (draggingObj != null) draggingObj.draw();
+
+ FlxCamera._defaultCameras = oldDefaultCameras;
+ }
+}
+
+class CharterBackdrop extends FlxTypedGroup {
+ public var gridBackDrop:FlxBackdrop;
+ public var topLimit:FlxSprite;
+ public var topSeparator:FlxSprite;
+ public var bottomLimit:FlxSprite;
+ public var bottomSeparator:FlxSprite;
+
+ public var conductorFollowerSpr:FlxSprite;
+ public var beatSeparator:FlxBackdrop;
+ public var sectionSeparator:FlxBackdrop;
+
+ public var notesGroup:FlxTypedGroup = new FlxTypedGroup();
+ public var strumLine:CharterStrumline;
+
+ public function new(gridGraphic:FlxGraphic) {
+ super();
+
+ gridBackDrop = new FlxBackdrop(gridGraphic, Y, 0, 0);
+ add(gridBackDrop);
+
+ sectionSeparator = new FlxBackdrop(null, Y, 0, 0);
+ sectionSeparator.y = -2;
+ sectionSeparator.visible = Options.charterShowSections;
+
+ beatSeparator = new FlxBackdrop(null, Y, 0, 0);
+ beatSeparator.y = -1;
+ beatSeparator.visible = Options.charterShowBeats;
+
+ for(sep in [sectionSeparator, beatSeparator]) {
+ sep.makeSolid(1, 1, -1);
+ sep.alpha = 0.5;
+ sep.scrollFactor.set(1, 1);
+ sep.scale.set((4 * 40), sep == sectionSeparator ? 4 : 2);
+ sep.updateHitbox();
+ }
+ add(beatSeparator);
+ add(sectionSeparator);
+ add(notesGroup);
+
+ bottomSeparator = new FlxSprite(0,-2);
+ bottomSeparator.makeSolid(1, 1, -1);
+ bottomSeparator.alpha = 0.5;
+ bottomSeparator.scrollFactor.set(1, 1);
+ bottomSeparator.scale.set(4 * 40, 4);
+ bottomSeparator.updateHitbox();
+ add(bottomSeparator);
+
+ topSeparator = new FlxSprite(0, -2);
+ topSeparator.makeSolid(1, 1, -1);
+ topSeparator.alpha = 0.5;
+ topSeparator.scrollFactor.set(1, 1);
+ topSeparator.scale.set(4 * 40, 4);
+ topSeparator.updateHitbox();
+ add(topSeparator);
+
+ // Limits
+ topLimit = new FlxSprite();
+ topLimit.makeSolid(1, 1, -1);
+ topLimit.color = 0xFF888888;
+ topLimit.blend = MULTIPLY;
+ add(topLimit);
+
+ bottomLimit = new FlxSprite();
+ bottomLimit.makeSolid(1, 1, -1);
+ bottomLimit.color = 0xFF888888;
+ bottomLimit.blend = MULTIPLY;
+ add(bottomLimit);
+
+ // Follower
+ conductorFollowerSpr = new FlxSprite(0, 0).makeSolid(1, 1, -1);
+ conductorFollowerSpr.scale.set(4 * 40, 4);
+ conductorFollowerSpr.updateHitbox();
+ add(conductorFollowerSpr);
+ }
+
+ public function updateSprites() {
+ var x:Float = 0; // fuck you
+ var alpha:Float = 0.9;
+
+ if (strumLine != null) {
+ x = strumLine.x;
+ alpha = strumLine.strumLine.visible ? 0.9 : 0.4;
+ } else alpha = 0.9;
+
+ for (spr in [gridBackDrop, sectionSeparator, beatSeparator, topLimit, bottomLimit, topSeparator, bottomSeparator, conductorFollowerSpr]) {
+ spr.x = x; spr.alpha = alpha;
+ spr.cameras = this.cameras;
+ }
+
+ sectionSeparator.spacing.y = (10 * Conductor.beatsPerMesure * Conductor.stepsPerBeat) - 1;
+ beatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1;
+
+ topLimit.scale.set(4 * 40, Math.ceil(FlxG.height / cameras[0].zoom));
+ topLimit.updateHitbox();
+ topLimit.y = -topLimit.height;
+
+ bottomLimit.scale.set(4 * 40, Math.ceil(FlxG.height / cameras[0].zoom));
+ bottomLimit.updateHitbox();
+ }
+}
+
+class CharterBackdropDummy extends UISprite {
+ var parent:CharterBackdropGroup;
+ public function new(parent:CharterBackdropGroup) {
+ super();
+ this.parent = parent;
+ cameras = parent.cameras;
+ scrollFactor.set(1, 0);
+ }
+
+ public override function updateButton() {
+ camera.getViewRect(__rect);
+ UIState.state.updateRectButtonHandler(this, __rect, onHovered);
+ }
+
+ public override function draw() {
+ @:privateAccess
+ __lastDrawCameras = [for(c in cameras) c];
+ }
+}
+
+class EventBackdrop extends FlxBackdrop {
+ public var eventBeatSeparator:FlxBackdrop;
+ public var eventSecSeparator:FlxBackdrop;
+
+ public var topSeparator:FlxSprite;
+ public var bottomSeparator:FlxSprite;
+
+ public function new() {
+ super(Paths.image('editors/charter/events-grid'), Y, 0, 0);
+ alpha = 0.9;
+
+ // Separators
+ eventSecSeparator = new FlxBackdrop(null, Y, 0, 0);
+ eventSecSeparator.y = -2;
+ eventSecSeparator.visible = Options.charterShowSections;
+
+ eventBeatSeparator = new FlxBackdrop(null, Y, 0, 0);
+ eventBeatSeparator.y = -1;
+ eventBeatSeparator.visible = Options.charterShowBeats;
+
+ for(sep in [eventSecSeparator, eventBeatSeparator]) {
+ sep.makeSolid(1, 1, -1);
+ sep.alpha = 0.5;
+ sep.scrollFactor.set(1, 1);
+ }
+
+ eventSecSeparator.scale.set(20, 4);
+ eventSecSeparator.updateHitbox();
+
+ eventBeatSeparator.scale.set(10, 2);
+ eventBeatSeparator.updateHitbox();
+
+ bottomSeparator = new FlxSprite(0,-2);
+ bottomSeparator.makeSolid(1, 1, -1);
+ bottomSeparator.alpha = 0.5;
+ bottomSeparator.scrollFactor.set(1, 1);
+ bottomSeparator.scale.set(20, 4);
+ bottomSeparator.updateHitbox();
+
+ topSeparator = new FlxSprite(0, -2);
+ topSeparator.makeSolid(1, 1, -1);
+ topSeparator.alpha = 0.5;
+ topSeparator.scrollFactor.set(1, 1);
+ topSeparator.scale.set(20, 4);
+ topSeparator.updateHitbox();
+
+ }
+
+ public override function draw() {
+ super.draw();
+
+ eventSecSeparator.spacing.y = (10 * Conductor.beatsPerMesure * Conductor.stepsPerBeat) - 1;
+ eventBeatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1;
+
+ eventSecSeparator.cameras = cameras;
+ eventSecSeparator.x = (x+width) - 20;
+ if (eventSecSeparator.visible) eventSecSeparator.draw();
+
+ eventBeatSeparator.cameras = cameras;
+ eventBeatSeparator.x = (x+width) - 10;
+ if (eventBeatSeparator.visible) eventBeatSeparator.draw();
+
+ topSeparator.x = (x+width) - 20;
+ topSeparator.cameras = this.cameras;
+ if (!eventSecSeparator.visible) topSeparator.draw();
+
+ bottomSeparator.x = (x+width) - 20;
+ bottomSeparator.cameras = this.cameras;
+ bottomSeparator.draw();
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/CharterEvent.hx b/source/funkin/editors/charter/CharterEvent.hx
index aa593a50..ff62f465 100644
--- a/source/funkin/editors/charter/CharterEvent.hx
+++ b/source/funkin/editors/charter/CharterEvent.hx
@@ -3,8 +3,8 @@ package funkin.editors.charter;
import funkin.editors.charter.Charter.ICharterSelectable;
import flixel.math.FlxPoint;
import funkin.game.Character;
-import funkin.backend.system.Conductor;
import funkin.game.HealthIcon;
+import funkin.editors.charter.CharterBackdropGroup.EventBackdrop;
import funkin.backend.chart.ChartData.ChartEvent;
class CharterEvent extends UISliceSprite implements ICharterSelectable {
@@ -15,6 +15,9 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
public var selected:Bool = false;
public var draggable:Bool = true;
+ public var eventsBackdrop:EventBackdrop;
+ public var snappedToGrid:Bool = true;
+
public function new(step:Float, ?events:Array) {
super(-100, (step * 40) - 17, 100, 34, 'editors/charter/event-spr');
this.step = step;
@@ -25,6 +28,10 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
public override function update(elapsed:Float) {
super.update(elapsed);
+
+ if (snappedToGrid && eventsBackdrop != null)
+ x = eventsBackdrop.x + eventsBackdrop.width - (bWidth = 37 + (icons.length * 22));
+
for(k=>i in icons) {
i.follow(this, (k * 22) + 30 - (i.width / 2), (bHeight - i.height) / 2);
}
@@ -38,10 +45,15 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
}
private static function generateDefaultIcon(name:String) {
+ var isBase64:Bool = false;
var path:String = Paths.image('editors/charter/event-icons/$name');
if (!Assets.exists(path)) path = Paths.image('editors/charter/event-icons/Unknown');
-
- var spr = new FlxSprite().loadGraphic(path);
+ if (Assets.exists(Paths.pack('events/$name'))) {
+ var packimg = Assets.getText(Paths.pack('events/$name')).split('________PACKSEP________')[3];
+ if (isBase64 = (packimg != null))
+ path = Assets.getText(Paths.pack('events/$name')).split('________PACKSEP________')[3];
+ }
+ var spr = new FlxSprite().loadGraphic(isBase64 ? openfl.display.BitmapData.fromBase64(path.trim(), 'UTF8') : path);
return spr;
}
@@ -77,8 +89,10 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
return (selectionBox.x + selectionBox.bWidth > x) && (selectionBox.x < x + bWidth) && (selectionBox.y + selectionBox.bHeight > y) && (selectionBox.y < y + bHeight);
}
- public function handleDrag(change:FlxPoint)
- y = ((step += change.x) * 40) - 17;
+ public function handleDrag(change:FlxPoint) {
+ var newStep:Float = step = FlxMath.bound(step + change.x, 0, Charter.instance.__endStep-1);
+ y = ((newStep) * 40) - 17;
+ }
public function refreshEventIcons() {
while(icons.length > 0) {
@@ -100,6 +114,6 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
break;
}
- x = -(bWidth = 37 + (icons.length * 22));
+ x = (snappedToGrid && eventsBackdrop != null ? eventsBackdrop.x : 0) - (bWidth = 37 + (icons.length * 22));
}
}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/CharterEventGroup.hx b/source/funkin/editors/charter/CharterEventGroup.hx
index 90719fec..5022b3a4 100644
--- a/source/funkin/editors/charter/CharterEventGroup.hx
+++ b/source/funkin/editors/charter/CharterEventGroup.hx
@@ -1,3 +1,40 @@
package funkin.editors.charter;
-class CharterEventGroup extends FlxTypedGroup {}
\ No newline at end of file
+import funkin.editors.charter.CharterBackdropGroup.EventBackdrop;
+import flixel.util.FlxSort;
+
+class CharterEventGroup extends FlxTypedGroup {
+ public var eventsBackdrop:EventBackdrop;
+ var __lastSort:Int = 0;
+
+ public override function update(elapsed:Float) {
+ filterEvents();
+ if (length != __lastSort)
+ sortEvents();
+
+ super.update(elapsed);
+ }
+
+ public override function draw() {
+ for (event in members) {
+ event.eventsBackdrop = eventsBackdrop;
+ event.snappedToGrid = true;
+ }
+ super.draw();
+ }
+
+ public inline function sortEvents() {
+ __lastSort = length;
+ this.sort(function(i, e1, e2) {
+ return FlxSort.byValues(FlxSort.ASCENDING, e1.step, e2.step);
+ });
+ }
+
+ public inline function filterEvents() {
+ for (event in members)
+ if (event.events.length == 0) {
+ remove(event, true);
+ event.kill();
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/CharterEventScreen.hx b/source/funkin/editors/charter/CharterEventScreen.hx
index ee8107c4..257922c2 100644
--- a/source/funkin/editors/charter/CharterEventScreen.hx
+++ b/source/funkin/editors/charter/CharterEventScreen.hx
@@ -62,7 +62,7 @@ class CharterEventScreen extends UISubstateWindow {
paramsPanel = new FlxGroup();
add(paramsPanel);
- saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 10, windowSpr.y + windowSpr.bHeight - 10, "Save & Close", function() {
+ saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
saveCurTab();
chartEvent.refreshEventIcons();
@@ -88,7 +88,6 @@ class CharterEventScreen extends UISubstateWindow {
Charter.instance.updateBPMEvents();
});
saveButton.x -= saveButton.bWidth;
- saveButton.y -= saveButton.bHeight;
add(saveButton);
closeButton = new UIButton(saveButton.x - 10, saveButton.y, "Close", ()->close());
diff --git a/source/funkin/editors/charter/CharterNote.hx b/source/funkin/editors/charter/CharterNote.hx
index 9a3359f4..5156ebdd 100644
--- a/source/funkin/editors/charter/CharterNote.hx
+++ b/source/funkin/editors/charter/CharterNote.hx
@@ -33,8 +33,6 @@ class CharterNote extends UISprite implements ICharterSelectable {
cursor = BUTTON;
- //canBeHovered = false;
-
moves = false;
sustainSpr = new FlxSprite(10, 40);
@@ -50,19 +48,28 @@ class CharterNote extends UISprite implements ICharterSelectable {
UIState.state.updateRectButtonHandler(this, __rect, onHovered);
}
-
public var step:Float;
public var id:Int;
public var susLength:Float;
public var type:Int;
- public function updatePos(step:Float, id:Int, susLength:Float = 0, type:Int = 0) {
+ public var strumLine:CharterStrumline;
+ public var strumLineID(get, never):Int;
+ public function get_strumLineID():Int
+ return strumLine == null ? -1 : Charter.instance.strumLines.members.indexOf(strumLine);
+
+ public var snappedToStrumline:Bool = true;
+
+ public var fullID(get, never):Int; // instead of %4 get fullID (for mousepos stuff)
+ public function get_fullID():Int
+ return (strumLineID * 4) + id;
+
+ 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.type = type;
-
- x = id * 40;
+ if (strumLine != null) this.strumLine = strumLine;
y = step * 40;
sustainSpr.scale.set(10, (40 * susLength));
@@ -121,12 +128,12 @@ class CharterNote extends UISprite implements ICharterSelectable {
sustainSpr.follow(this, 15, 20);
if (__passed != (__passed = step < Conductor.curStepFloat)) {
- alpha = __passed ? 0.6 : 1;
- if (__passed && FlxG.sound.music.playing && Charter.instance.hitsoundsEnabled(id))
+ if (__passed && FlxG.sound.music.playing && Charter.instance.hitsoundsEnabled(strumLineID))
Charter.instance.hitsound.replay();
}
- if(sustainSpr.exists)
- sustainSpr.alpha = alpha;
+
+ alpha = strumLine != null && !strumLine.strumLine.visible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1);
+ if(sustainSpr.exists) sustainSpr.alpha = alpha;
colorTransform.redMultiplier = colorTransform.greenMultiplier = colorTransform.blueMultiplier = selected ? 0.75 : 1;
colorTransform.redOffset = colorTransform.greenOffset = selected ? 96 : 0;
@@ -141,21 +148,20 @@ class CharterNote extends UISprite implements ICharterSelectable {
var maxX = Std.int(Math.ceil((selectionBox.x + selectionBox.bWidth) / 40));
var maxY = ((selectionBox.y + selectionBox.bHeight) / 40);
- return this.id >= minX && this.id < maxX && this.step >= minY && this.step < maxY;
+ return this.fullID >= minX && this.fullID < maxX && this.step >= minY && this.step < maxY;
}
public function handleDrag(change:FlxPoint) {
- var newID:Int = id + Std.int(change.y);
- if (newID > ((Charter.instance.strumLines.members.length*4)-1)) newID %= 4;
- else if (newID < 0) newID = (Charter.instance.strumLines.members.length*4) + newID % 4;
+ var newStep = FlxMath.bound(step + change.x, 0, Charter.instance.__endStep-1);
+ var newID:Int = Std.int(FlxMath.bound(fullID + Std.int(change.y), 0, (Charter.instance.strumLines.members.length*4)-1));
- updatePos(step + change.x, newID, susLength, type);
-
- Charter.instance.notesGroup.remove(this);
- Charter.instance.notesGroup.add(this);
+ updatePos(newStep, newID % 4, susLength, type, Charter.instance.strumLines.members[Std.int(newID/4)]);
}
public override function draw() {
+ if (snappedToStrumline)
+ x = (strumLine != null ? strumLine.x : 0) + (id % 4) * 40;
+
drawMembers();
drawSuper();
}
diff --git a/source/funkin/editors/charter/CharterNoteGroup.hx b/source/funkin/editors/charter/CharterNoteGroup.hx
index 775601d0..0cfebeb9 100644
--- a/source/funkin/editors/charter/CharterNoteGroup.hx
+++ b/source/funkin/editors/charter/CharterNoteGroup.hx
@@ -1,5 +1,6 @@
package funkin.editors.charter;
+import flixel.util.FlxSort;
import funkin.backend.system.Conductor;
class CharterNoteGroup extends FlxTypedGroup {
@@ -8,6 +9,9 @@ class CharterNoteGroup extends FlxTypedGroup {
var max:Float = 0;
var __currentlyLooping:Bool = false;
+ public var autoSort:Bool = true;
+ var __lastSort:Int = 0;
+
public override function forEach(noteFunc:CharterNote->Void, recursive:Bool = false) {
__loopSprite = null;
@@ -17,18 +21,18 @@ class CharterNoteGroup extends FlxTypedGroup {
__currentlyLooping = true;
var curStep = Conductor.curStepFloat;
- if (FlxG.state is Charter && !FlxG.sound.music.playing)
- curStep = cast(FlxG.state, Charter).conductorFollowerSpr.y / 40;
+ if (!FlxG.sound.music.playing)
+ curStep = Charter.instance.gridBackdrops.conductorSprY / 40;
var begin = SortedArrayUtil.binarySearch(members, curStep - max, getVarForEachAdd);
var end = SortedArrayUtil.binarySearch(members, curStep + max, getVarForEachRemove);
for(i in begin...end) {
__loopSprite = members[i];
- if (!cast(FlxG.state, Charter).selection.contains(__loopSprite))
+ if (!Charter.instance.selection.contains(__loopSprite))
noteFunc(__loopSprite);
}
- for(c in cast(FlxG.state, Charter).selection.copy())
+ for(c in Charter.instance.selection.copy())
if (c is CharterNote) noteFunc(cast (c, CharterNote));
__currentlyLooping = oldCur;
@@ -50,22 +54,15 @@ class CharterNoteGroup extends FlxTypedGroup {
private static function getVarForEachRemove(n:CharterNote)
return n.step - n.susLength;
- public override function draw() {
- @:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras;
- @:privateAccess if (cameras != null) FlxCamera._defaultCameras = cameras;
-
- forEach((n) -> {
- if(n.exists && n.visible)
- n.draw();
- });
-
- @:privateAccess FlxCamera._defaultCameras = oldDefaultCameras;
- }
+ public override function draw() {}
public override function update(elapsed:Float) {
@:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras;
@:privateAccess if (cameras != null) FlxCamera._defaultCameras = cameras;
+ if (length != __lastSort && autoSort)
+ sortNotes();
+
forEach((n) -> {
if(n.exists && n.active)
n.update(elapsed);
@@ -73,4 +70,18 @@ class CharterNoteGroup extends FlxTypedGroup {
@:privateAccess FlxCamera._defaultCameras = oldDefaultCameras;
}
+
+ public function sortNotes() {
+ __lastSort = length;
+ this.sort(function(i, n1, n2) {
+ if (n1.step == n2.step)
+ return FlxSort.byValues(FlxSort.ASCENDING, n1.fullID, n2.fullID);
+ return FlxSort.byValues(FlxSort.ASCENDING, n1.step, n2.step);
+ });
+ }
+
+ public inline function preallocate(len:Int) {
+ members = cast new haxe.ds.Vector(len);
+ length = len;
+ }
}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/CharterSelection.hx b/source/funkin/editors/charter/CharterSelection.hx
index e7d2277f..5e7ffff8 100644
--- a/source/funkin/editors/charter/CharterSelection.hx
+++ b/source/funkin/editors/charter/CharterSelection.hx
@@ -32,13 +32,8 @@ class CharterSelection extends EditorTreeMenu {
})
];
list.push(new NewOption("New Difficulty", "New Difficulty", function() {
- openSubState(new UIWarningSubstate("New Difficulty", "This feature isnt implemented yet", [
- {
- label: "OK",
- onClick: function(t) {
-
- }
- }
+ openSubState(new UIWarningSubstate("New Difficulty: Feature Not Implemented!", "This feature isnt implemented yet. Please wait for more cne updates to have this functional.\n\n\n- Codename Devs", [
+ {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
]));
}));
optionsTree.add(new OptionsScreen(s.name, "Select a difficulty to continue.", list));
@@ -91,9 +86,18 @@ class CharterSelection extends EditorTreeMenu {
}
public function saveSong(creation:SongCreationData) {
+ var songAlreadlyExsits:Bool = [for (s in freeplayList.songs) s.name.toLowerCase()].contains(creation.meta.name.toLowerCase());
+
+ if (songAlreadlyExsits) {
+ openSubState(new UIWarningSubstate("Creating Song: Error!", "The song you are trying to create alreadly exists, if you would like to override it delete the song first!", [
+ {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
+ ]));
+ return;
+ }
+
// Paths
var songsDir:String = '${Paths.getAssetsRoot()}/songs/';
- var songFolder:String = '$songsDir${creation.meta.name.replace("-", " ")}';
+ var songFolder:String = '$songsDir${creation.meta.name}';
#if sys
// Make Directories
@@ -115,13 +119,8 @@ class CharterSelection extends EditorTreeMenu {
})
];
list.push(new NewOption("New Difficulty", "New Difficulty", function() {
- openSubState(new UIWarningSubstate("New Difficulty", "This feature isnt implemented yet", [
- {
- label: "OK",
- onClick: function(t) {
-
- }
- }
+ openSubState(new UIWarningSubstate("New Difficulty: Feature Not Implemented!", "This feature isnt implemented yet. Please wait for more cne updates to have this functional.\n\n\n- Codename Devs", [
+ {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
]));
}));
optionsTree.add(new OptionsScreen(creation.meta.name, "Select a difficulty to continue.", list));
diff --git a/source/funkin/editors/charter/CharterStrumLineGroup.hx b/source/funkin/editors/charter/CharterStrumLineGroup.hx
new file mode 100644
index 00000000..c4c3c17c
--- /dev/null
+++ b/source/funkin/editors/charter/CharterStrumLineGroup.hx
@@ -0,0 +1,114 @@
+package funkin.editors.charter;
+
+import funkin.backend.chart.ChartData.ChartStrumLine;
+import funkin.backend.chart.EventsData;
+import flixel.util.FlxSort;
+
+class CharterStrumLineGroup extends FlxTypedGroup {
+ var __pastStrumlines:Array;
+ var draggingObj:CharterStrumline = null;
+ var draggingOffset:Float = 0;
+
+ public var draggable:Bool = false;
+ public var isDragging(get, never):Bool;
+ public function get_isDragging():Bool
+ return draggingObj != null;
+
+ public override function update(elapsed:Float) {
+ var mousePos = FlxG.mouse.getWorldPosition(cameras[0], FlxPoint.get());
+ for (strumLine in members) {
+ if (strumLine == null) continue;
+ strumLine.draggable = draggable;
+ if (draggable && UIState.state.isOverlapping(strumLine.draggingSprite, @:privateAccess strumLine.draggingSprite.__rect) && FlxG.mouse.justPressed) {
+ draggingObj = strumLine;
+ strumLine.dragging = true;
+
+ draggingOffset = mousePos.x - strumLine.button.x;
+ __pastStrumlines = members.copy();
+ break;
+ }
+ }
+
+ if (isDragging) {
+ draggingObj.x = mousePos.x - draggingOffset;
+ this.sort(function(o, a, b) return FlxSort.byValues(o, a.x, b.x), -1);
+ }
+
+ for (i=>strum in members)
+ if (strum != null && !strum.dragging) strum.x = CoolUtil.fpsLerp(strum.x, 160 * i, 0.225);
+
+ if (Charter.instance.eventsBackdrop != null && members[0] != null)
+ Charter.instance.eventsBackdrop.x = members[0].button.x - Charter.instance.eventsBackdrop.width;
+ if (Charter.instance.strumlineLockButton != null && members[0] != null)
+ Charter.instance.strumlineLockButton.x = members[0].x - (40*4);
+ if (Charter.instance.strumlineAddButton != null && members[Std.int(Math.max(0, members.length-1))] != null)
+ Charter.instance.strumlineAddButton.x = members[members.length-1].x + (40*4);
+
+ if ((FlxG.mouse.justReleased || !draggable) && isDragging)
+ finishDrag();
+
+ mousePos.put();
+ super.update(elapsed);
+ }
+
+ public function snapStrums() {
+ for (i=>strum in members)
+ if (strum != null && !strum.dragging) strum.x = 160 * i;
+ }
+
+ public function orderStrumline(strumLine:CharterStrumline, newID:Int) {
+ __pastStrumlines = members.copy();
+
+ members.remove(strumLine);
+ members.insert(newID, strumLine);
+
+ finishDrag(false);
+ }
+
+ public function finishDrag(?addToUndo:Bool = true) {
+ if (isDragging)
+ draggingObj.dragging = false;
+
+ // Undo
+ if (addToUndo) {
+ var oldID = __pastStrumlines.indexOf(draggingObj);
+ var newID = members.indexOf(draggingObj);
+ if (newID != oldID) Charter.instance.undos.addToUndo(COrderStrumLine(newID, oldID, newID));
+ }
+
+ draggingObj = null;
+ fixEvents();
+ }
+
+ public inline function fixEvents() {
+ for (i in Charter.instance.eventsGroup.members) {
+ for (j in i.events) {
+ var paramTypes:Array = EventsData.getEventParams(j.name);
+ for (i => param in paramTypes) {
+ if (param.type != TStrumLine) continue;
+ j.params[i] = members.indexOf(__pastStrumlines[j.params[i]]);
+ }
+ }
+ }
+ __pastStrumlines = null;
+ }
+
+ override function draw() @:privateAccess {
+ var i:Int = 0;
+ var basic:FlxBasic = null;
+
+ var oldDefaultCameras = FlxCamera._defaultCameras;
+ if (cameras != null)
+ FlxCamera._defaultCameras = cameras;
+
+ while (i < length)
+ {
+ basic = members[i++];
+ if (basic != null && basic != draggingObj && basic.exists && basic.visible)
+ basic.draw();
+ }
+ if (draggingObj != null) draggingObj.draw();
+
+ FlxCamera._defaultCameras = oldDefaultCameras;
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/CharterStrumline.hx b/source/funkin/editors/charter/CharterStrumline.hx
index 14f4d671..78fb0275 100644
--- a/source/funkin/editors/charter/CharterStrumline.hx
+++ b/source/funkin/editors/charter/CharterStrumline.hx
@@ -1,5 +1,6 @@
package funkin.editors.charter;
+import funkin.backend.chart.EventsData;
import funkin.game.Character;
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
import funkin.editors.ui.UITopMenu.UITopMenuButton;
@@ -8,11 +9,14 @@ import funkin.backend.chart.ChartData.ChartStrumLine;
class CharterStrumline extends UISprite {
public var strumLine:ChartStrumLine;
-
public var hitsounds:Bool = true;
+ public var draggingSprite:UISprite;
public var healthIcon:HealthIcon;
- public var button:CharterStrumlineButton;
+ public var button:CharterStrumlineOptions;
+
+ public var draggable:Bool = false;
+ public var dragging:Bool = false;
public var curMenu:UIContextMenu = null;
@@ -26,22 +30,52 @@ class CharterStrumline extends UISprite {
var icon = Character.getIconFromCharName(strumLine.characters != null ? strumLine.characters[0] : null);
healthIcon = new HealthIcon(icon);
- healthIcon.scale.set(80 / 150, 80 / 150);
+ healthIcon.scale.set((80 - (draggable ? 21 : 0)) / 150, (80 - (draggable ? 21 : 0)) / 150);
healthIcon.updateHitbox();
- if(strumLine.visible == null)
- strumLine.visible = true;
+ healthIcon.y = draggable ? 29 : 7;
+
+ if(strumLine.visible == null) strumLine.visible = true;
healthIcon.alpha = strumLine.visible ? 1 : 0.4;
members.push(healthIcon);
- button = new CharterStrumlineButton(this);
+ draggingSprite = new UISprite();
+ draggingSprite.loadGraphic(Paths.image("editors/charter/strumline-drag"));
+ draggingSprite.alpha = 0.4;
+ draggingSprite.y = 9;
+ draggingSprite.antialiasing = true;
+ draggingSprite.cursor = BUTTON;
+ members.push(draggingSprite);
+
+ button = new CharterStrumlineOptions(this);
members.push(button);
}
+ private var __healthYOffset:Float = 0;
+ private var __draggingYOffset:Float = 0;
+
public override function update(elapsed:Float) {
- if (healthIcon != null)
- healthIcon.follow(this, ((40 * 4) - healthIcon.width) / 2, 0);
+ if (FlxG.keys.justPressed.K) draggable = !draggable;
+ if (healthIcon != null) {
+ var healthScale:Float = FlxMath.lerp(healthIcon.scale.x, (80 - (draggable ? 21 : 0)) / 150, 1/20);
+ healthIcon.scale.set(healthScale, healthScale);
+ healthIcon.updateHitbox();
+
+ healthIcon.follow(this, ((40 * 4) - healthIcon.width) / 2, 7 + (__healthYOffset = FlxMath.lerp(__healthYOffset, draggable ? 22 : 0, 1/20)));
+ }
+
+ draggingSprite.selectable = draggable;
+ UIState.state.updateSpriteRect(draggingSprite);
+
+ var dragScale:Float = FlxMath.lerp(draggingSprite.scale.x, draggable ? 1 : 0.8, 1/16);
+ draggingSprite.scale.set(dragScale, dragScale);
+ draggingSprite.updateHitbox();
+
+ draggingSprite.follow(this, (160/2) - (draggingSprite.width/2), 6 + (__draggingYOffset = FlxMath.lerp(__draggingYOffset, draggable ? 3 : 0, 1/12)));
+ var fullAlpha:Float = UIState.state.isOverlapping(draggingSprite, @:privateAccess draggingSprite.__rect) || dragging ? 0.9 : 0.35;
+ draggingSprite.alpha = FlxMath.lerp(draggingSprite.alpha, draggable ? fullAlpha : 0, 1/12);
button.follow(this, 0, 95);
+
super.update(elapsed);
}
@@ -52,17 +86,19 @@ class CharterStrumline extends UISprite {
var icon = Character.getIconFromCharName(strumLine.characters != null ? strumLine.characters[0] : null);
healthIcon = new HealthIcon(icon);
- healthIcon.scale.set(80 / 150, 80 / 150);
+ healthIcon.scale.set((80 - (draggable ? 21 : 0)) / 150, (80 - (draggable ? 21 : 0)) / 150);
healthIcon.updateHitbox();
+ healthIcon.y = draggable ? 29 : 7;
+
healthIcon.alpha = strumLine.visible ? 1 : 0.4;
members.push(healthIcon);
}
}
-class CharterStrumlineButton extends UITopMenuButton {
+class CharterStrumlineOptions extends UITopMenuButton {
var strLine:CharterStrumline;
public function new(parent:CharterStrumline) {
- super(0, 95, null, "Options", []);
+ super(0, 95, null, "Options ↓", []);
strLine = parent;
bWidth = 40 * 4;
this.label.fieldWidth = bWidth;
@@ -70,7 +106,7 @@ class CharterStrumlineButton extends UITopMenuButton {
public override function update(elapsed:Float) {
super.update(elapsed);
- alpha = FlxMath.lerp(1/3, 1, alpha); // so that instead of 0% it is 33% visible
+ alpha = FlxMath.lerp(1/20, 1, alpha); // so that instead of 0% it is 33% visible
}
public override function openContextMenu() {
@@ -96,13 +132,17 @@ class CharterStrumlineButton extends UITopMenuButton {
label: "Edit",
onSelect: function (_) {
Charter.instance.editStrumline(strLine.strumLine);
- }
+ },
+ color: 0xFFFFFF00,
+ icon: 2
},
{
label: "Delete",
onSelect: function (_) {
Charter.instance.deleteStrumlineFromData(strLine.strumLine);
- }
+ },
+ color: 0xFFFF0000,
+ icon: 3
}
];
super.openContextMenu();
diff --git a/source/funkin/editors/charter/CharterStrumlineExtraButton.hx b/source/funkin/editors/charter/CharterStrumlineButton.hx
similarity index 82%
rename from source/funkin/editors/charter/CharterStrumlineExtraButton.hx
rename to source/funkin/editors/charter/CharterStrumlineButton.hx
index f19e4c30..2cd156cf 100644
--- a/source/funkin/editors/charter/CharterStrumlineExtraButton.hx
+++ b/source/funkin/editors/charter/CharterStrumlineButton.hx
@@ -2,7 +2,7 @@ package funkin.editors.charter;
import flixel.math.FlxPoint;
-class CharterStrumlineExtraButton extends UISprite {
+class CharterStrumlineButton extends UISprite {
public var button:UISprite;
public var text:UIText;
@@ -30,15 +30,12 @@ class CharterStrumlineExtraButton extends UISprite {
var buttonScale:FlxPoint = FlxPoint.get(0,0);
public override function update(elapsed:Float) {
- button.follow(this, ((40 * 4) - button.width) / 2, 18);
+ button.follow(this, ((40 * 4) - button.width) / 2, 24);
text.follow(this, 0, 84);
super.update(elapsed);
- button.__rect.x = button.x;
- button.__rect.y = button.y;
- button.__rect.width = button.width;
- button.__rect.height = button.height;
+ UIState.state.updateSpriteRect(button);
if(UIState.state.isOverlapping(button, button.__rect)) {
buttonScale.set(0.85, 0.85);
if (FlxG.mouse.justReleased && onClick != null) onClick();
diff --git a/source/funkin/editors/charter/CharterStrumlineScreen.hx b/source/funkin/editors/charter/CharterStrumlineScreen.hx
index da7e369c..e6a04399 100644
--- a/source/funkin/editors/charter/CharterStrumlineScreen.hx
+++ b/source/funkin/editors/charter/CharterStrumlineScreen.hx
@@ -59,11 +59,14 @@ class CharterStrumlineScreen extends UISubstateWindow {
var title:UIText;
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, creatingStrumLine ? "Create New Strumline" : "Edit Strumline Properties", 28));
+ var charFileList = Character.getList(true);
+ if (charFileList.length == 0) charFileList = Character.getList(false);
+
charactersList = new UIButtonList(15, title.y + title.height + 36, 250, 259, "", FlxPoint.get(250, 54), null, 0);
- charactersList.addButton.callback = () -> charactersList.add(new CharacterButton(0, 0, "New Char", charactersList));
+ charactersList.addButton.callback = () -> charactersList.add(new CharacterButton(0, 0, "New Char", charFileList, charactersList));
charactersList.cameraSpacing = 0;
for (i in strumLine.characters)
- charactersList.add(new CharacterButton(0, 0, i, charactersList));
+ charactersList.add(new CharacterButton(0, 0, i, charFileList, charactersList));
add(charactersList);
addLabelOn(charactersList, "Characters");
@@ -126,13 +129,12 @@ class CharterStrumlineScreen extends UISubstateWindow {
}, 125);
add(saveButton);
- closeButton = new UIButton(saveButton.x - 20, saveButton.y, creatingStrumLine ? "Cancel" : "Close", function() {
+ closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, creatingStrumLine ? "Cancel" : "Close", function() {
if (creatingStrumLine && onSave != null) onSave(null);
close();
}, 125);
add(closeButton);
closeButton.color = 0xFFFF0000;
- closeButton.x -= closeButton.bWidth;
}
function saveStrumline() {
@@ -158,11 +160,11 @@ class CharterStrumlineScreen extends UISubstateWindow {
class CharacterButton extends UIButton {
public var charIcon:HealthIcon;
- public var textBox:UITextBox;
+ public var textBox:UIAutoCompleteTextBox;
public var deleteButton:UIButton;
public var deleteIcon:FlxSprite;
- public function new(x:Float, y:Float, char:String, parent:UIButtonList) {
+ public function new(x:Float, y:Float, char:String, charsList:Array, parent:UIButtonList) {
super(x, y, "", null, 250, 54);
autoAlpha = false;
@@ -174,7 +176,8 @@ class CharacterButton extends UIButton {
members.push(charIcon);
- members.push(textBox = new UITextBox(charIcon.x + charIcon.width + 16, bHeight/2 - (32/2), char, 115));
+ members.push(textBox = new UIAutoCompleteTextBox(charIcon.x + charIcon.width + 16, bHeight/2 - (32/2), char, 115));
+ textBox.suggestItems = charsList;
textBox.antialiasing = true;
textBox.onChange = function(char:String) {
char = Character.getIconFromCharName(char);
diff --git a/source/funkin/editors/ui/UIAutoCompleteTextBox.hx b/source/funkin/editors/ui/UIAutoCompleteTextBox.hx
new file mode 100644
index 00000000..6b5d31b3
--- /dev/null
+++ b/source/funkin/editors/ui/UIAutoCompleteTextBox.hx
@@ -0,0 +1,99 @@
+package funkin.editors.ui;
+
+import flixel.math.FlxPoint;
+import lime.ui.KeyModifier;
+import lime.ui.KeyCode;
+import openfl.desktop.Clipboard;
+import openfl.geom.Rectangle;
+
+class UIAutoCompleteTextBox extends UITextBox {
+ public var suggestionText:UIText;
+
+ public var suggestItems(default, set):Array = [];
+ var suggestIndex = 0;
+
+ public function new(x:Float, y:Float, text:String = "", width:Int = 320, height:Int = 32, multiline:Bool = false) {
+ super(x, y, text, width, height, multiline);
+
+ suggestionText = new UIText(0, 0, width, "");
+ suggestionText.color = 0xFF888888;
+ suggestionText.visible = false;
+ members.insert(members.indexOf(label), suggestionText);
+ }
+
+ public override function update(elapsed:Float) {
+ super.update(elapsed);
+
+ var selected = selectable && focused;
+
+ suggestionText.exists = selected;
+
+ suggestionText.follow(label, 0, 0);
+ }
+
+ public override function onKeyDown(e:KeyCode, modifier:KeyModifier) {
+ super.onKeyDown(e, modifier);
+ switch(e) {
+ //case RETURN:
+ // focused = false;
+ case TAB:
+ if(_suggestions.length > 0) {
+ label.text = suggestionText.text;
+ position = label.text.length;
+ updateSuggestion(true);
+ }
+ case BACKSPACE:
+ updateSuggestion(true);
+ case UP:
+ suggestIndex--;
+ updateSuggestion(false);
+ case DOWN:
+ suggestIndex++;
+ updateSuggestion(false);
+ default:
+ // nothing
+ }
+ }
+
+ var _suggestions:Array = [];
+
+ public function updateSuggestion(refreshSuggestions:Bool = true) {
+ if(refreshSuggestions) {
+ var text = label.text;
+ _suggestions = [];
+ if(text.length > 0) {
+ for(i in suggestItems) if(!_suggestions.contains(i) && i.startsWith(text)) _suggestions.push(i);
+
+ // Clean up suggestions
+ if(suggestItems.contains(text)) {_suggestions = [];}
+ if (_suggestions.length > 0) _suggestions.sort(function(a, b) return a.length - b.length);
+ }
+ }
+
+ if(suggestionText.visible = (_suggestions.length != 0)) {
+ if(suggestIndex >= _suggestions.length)
+ suggestIndex = 0;
+ suggestIndex = FlxMath.wrap(suggestIndex, 0, _suggestions.length - 1);
+
+ suggestionText.text = _suggestions[suggestIndex];
+ }
+ }
+
+ public override function onTextInput(text:String):Void {
+ super.onTextInput(text);
+
+ updateSuggestion();
+ }
+
+ public override function onTextEdit(text:String, start:Int, end:Int):Void {
+ super.onTextEdit(text, start, end);
+
+ updateSuggestion();
+ }
+
+ function set_suggestItems(v:Array) {
+ suggestItems = v;
+ updateSuggestion();
+ return v;
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/editors/ui/UIButton.hx b/source/funkin/editors/ui/UIButton.hx
index c5a7fe80..f3e67821 100644
--- a/source/funkin/editors/ui/UIButton.hx
+++ b/source/funkin/editors/ui/UIButton.hx
@@ -12,9 +12,6 @@ class UIButton extends UISliceSprite {
super(x, y, w, h, 'editors/ui/button');
this.callback = callback;
members.push(field = new UIText(x, y, w, text));
- field.borderStyle = OUTLINE;
- field.borderColor = 0x88000000;
- field.borderSize = 1;
field.alignment = CENTER;
field.fieldWidth = w;
diff --git a/source/funkin/editors/ui/UIContextMenu.hx b/source/funkin/editors/ui/UIContextMenu.hx
index bf4d08fc..8a60a0ff 100644
--- a/source/funkin/editors/ui/UIContextMenu.hx
+++ b/source/funkin/editors/ui/UIContextMenu.hx
@@ -115,6 +115,7 @@ typedef UIContextMenuOption = {
var ?keybind:Array;
var ?keybinds:Array>;
var ?keybindText:String;
+ var ?color:Int;
var ?icon:Int;
var ?onSelect:UIContextMenuOption->Void;
var ?childs:Array;
@@ -132,6 +133,7 @@ class UIContextMenuOptionSpr extends UISliceSprite {
label = new UIText(20, 2, 0, option.label);
this.option = option;
this.parent = parent;
+ this.color = option.color;
if (option.icon != null && option.icon > 0) {
icon = new FlxSprite(0, 0).loadGraphic(Paths.image('editors/ui/context-icons'), true, 20, 20);
diff --git a/source/funkin/editors/ui/UISlider.hx b/source/funkin/editors/ui/UISlider.hx
new file mode 100644
index 00000000..fca67325
--- /dev/null
+++ b/source/funkin/editors/ui/UISlider.hx
@@ -0,0 +1,153 @@
+package funkin.editors.ui;
+
+import flixel.util.FlxColor;
+using flixel.util.FlxSpriteUtil;
+using StringTools;
+
+typedef SliderSegement = {
+ var start:Float;
+ var end:Float;
+ var size:Float;
+}
+
+class UISlider extends UISprite {
+ public var segments:Array = [];
+ public var barWidth:Int = 120;
+
+ public var progressbar:UISprite;
+ public var progressCentered:Bool;
+
+ public var startText:UIText;
+ public var endText:UIText;
+
+ public var selectableBar:UISprite;
+ public var selectableBarHighlight:UISprite;
+ public var selectableHitbox:UISprite;
+
+ public var value(default, set):Float = 0;
+ public function set_value(newVal:Float):Float {
+ __barProgress = __calcProgress(newVal); onChange(newVal);
+ return value = newVal;
+ }
+
+ public var valueStepper:UINumericStepper;
+ public var onChange:Float->Void;
+
+ public function new(x:Float, y:Float, width:Int = 120, value:Float, segments:Array, centered:Bool) {
+ this.segments = segments;
+ this.barWidth = width;
+ this.progressCentered = centered;
+
+ super(x, y);
+
+ makeGraphic(barWidth, 12, 0x00000000, true);
+ this.drawRoundRect(0, 0, barWidth, 12, 8, 6, 0xFFC0C0C0);
+ this.drawRoundRect(1, 1, barWidth-2, 12-2, 8, 5, 0xFF140013);
+ cursor = BUTTON;
+
+ progressbar = new UISprite(centered ? barWidth/2 : 0);
+ progressbar.makeGraphic(barWidth, 8, 0x00000000, true);
+ progressbar.drawRoundRect(2, 0, barWidth-4, 8, 8, 2, 0xFFFFFFFF);
+ progressbar.origin.x = 0; progressbar.colorTransform.color = 0xFF67009B;
+ members.push(progressbar);
+
+ __barProgress = visualProgress = __calcProgress(value);
+
+ members.push(startText = new UIText(x, y, 0, Std.string(segments[0].start).replace("0.", ".")));
+ members.push(endText = new UIText(x + (barWidth) + 8, y, 0, Std.string(segments[segments.length-1].end).replace("0.", ".")));
+
+ for (i in 0...2) {
+ var selectableBar:UISprite = new UISprite(x,y);
+ selectableBar.loadGraphic(Paths.image("editors/ui/slider"));
+ selectableBar.antialiasing = true;
+ selectableBar.cursor = BUTTON;
+ members.push(selectableBar);
+
+ switch (i) {
+ case 0: this.selectableBar = selectableBar;
+ case 1: this.selectableBarHighlight = selectableBar; selectableBarHighlight.colorTransform.color = 0xFFFFFFFF; selectableBarHighlight.alpha = 0;
+ }
+ }
+
+ selectableHitbox = new UISprite(x,y);
+ selectableHitbox.makeSolid(barWidth, 18, -1);
+ selectableHitbox.cursor = BUTTON;
+ selectableHitbox.alpha = 0;
+ members.push(selectableHitbox);
+
+ valueStepper = new UINumericStepper(x - 64 - 64, y, 1, 0.01, 2, segments[0].start, segments[segments.length-1].end, 0, 16);
+ valueStepper.antialiasing = valueStepper.label.antialiasing = true;
+ valueStepper.onChange = function (text:String) {
+ @:privateAccess valueStepper.__onChange(text);
+ visualProgress = __barProgress = __calcProgress(value = valueStepper.value);
+ if (onChange != null) onChange(value);
+ }
+ members.push(valueStepper);
+ }
+
+ var __barProgress:Float = 0;
+ var visualProgress:Float = 0;
+
+ var __stepperWidth = 25;
+
+ public override function update(elapsed:Float) {
+ selectableHitbox.follow(this, 0, (height-selectableHitbox.height)/2);
+
+ __stepperWidth = valueStepper.label.text.length <= 0 ? 25 : Std.int(valueStepper.label.textField.width) + 12;
+ valueStepper.bWidth = Std.int(FlxMath.lerp(valueStepper.bWidth, __stepperWidth, 1/2.25));
+ valueStepper.follow(this, -startText.width-10 - valueStepper.bWidth - 4, (height-valueStepper.bHeight)/2);
+
+ var lastBarProgress:Float = __barProgress;
+
+ if (selectableHitbox.hovered && FlxG.mouse.pressed) {
+ var mousePos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get());
+ __barProgress = FlxMath.bound(mousePos.x-x, 0, barWidth)/barWidth;
+ mousePos.put();
+ }
+
+ if (__barProgress != lastBarProgress) {
+ if (onChange != null) onChange(@:bypassAccessor value = __calcValue(__barProgress));
+ valueStepper.value = value;
+ }
+
+ visualProgress = FlxMath.lerp(visualProgress, __barProgress, 1/2.25);
+ progressbar.follow(this, progressCentered ? barWidth/2 : 0, (height-progressbar.height)/2);
+ progressbar.scale.x = FlxMath.bound(visualProgress-(progressCentered?0.5:0),-1,1);
+ progressbar.colorTransform.color = FlxColor.interpolate(progressbar.colorTransform.color, selectableHitbox.hovered ? 0xFF7F00BF : 0xFF67009B, 1/14);
+
+ selectableBar.follow(this, (visualProgress * barWidth) - (selectableBar.width/2), (height-selectableBar.height)/2); selectableBarHighlight.follow(selectableBar);
+ selectableBarHighlight.alpha = FlxMath.lerp(selectableBarHighlight.alpha, selectableHitbox.hovered ? 0.1: 0, 1/14);
+
+ startText.follow(this, -startText.width-10, (height/2) - (startText.height/2));
+ endText.follow(this, barWidth + 10, (height/2) - (endText.height/2));
+
+ super.update(elapsed);
+ }
+
+ private function __calcValue(progress:Float):Float {
+ var totalProgress:Float = 0;
+ for (segment in segments) {
+ if (progress < totalProgress + segment.size) {
+ var relativeProgress = (progress - totalProgress) / segment.size;
+ return FlxMath.remapToRange(relativeProgress, 0, 1, segment.start, segment.end);
+ }
+ totalProgress += segment.size;
+ }
+ return segments[segments.length-1].end;
+ }
+
+ private function __calcProgress(value:Float):Float {
+ if (value >= segments[segments.length-1].end) return 1;
+ if (value <= segments[0].start) return 0;
+
+ var totalProgress:Float = 0;
+ for (segment in segments) {
+ if (value >= segment.start && value <= segment.end) {
+ var relativeProgress = FlxMath.remapToRange(value, segment.start, segment.end, 0, 1);
+ return totalProgress + relativeProgress * segment.size;
+ }
+ totalProgress += segment.size;
+ }
+ return -1;
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/editors/ui/UIState.hx b/source/funkin/editors/ui/UIState.hx
index 716e277d..b43e605e 100644
--- a/source/funkin/editors/ui/UIState.hx
+++ b/source/funkin/editors/ui/UIState.hx
@@ -33,6 +33,7 @@ class UIState extends MusicBeatState {
__mousePos = FlxPoint.get();
super.create();
Framerate.offset.y = 30;
+ FlxG.mouse.visible = true;
FlxG.stage.window.onKeyDown.add(onKeyDown);
FlxG.stage.window.onKeyUp.add(onKeyUp);
@@ -59,6 +60,13 @@ class UIState extends MusicBeatState {
currentFocus.onTextEdit(str, start, end);
}
+ public inline function updateSpriteRect(spr:UISprite) {
+ spr.__rect.x = spr.x;
+ spr.__rect.y = spr.y;
+ spr.__rect.width = spr.width;
+ spr.__rect.height = spr.height;
+ }
+
public function updateButtonHandler(spr:UISprite, buttonHandler:Void->Void) {
spr.__rect.x = spr.x;
spr.__rect.y = spr.y;
@@ -121,6 +129,10 @@ class UIState extends MusicBeatState {
public override function destroy() {
super.destroy();
__mousePos.put();
+
+ WindowUtils.resetTitle();
+ SaveWarning.reset();
+
FlxG.stage.window.onKeyDown.remove(onKeyDown);
FlxG.stage.window.onKeyUp.remove(onKeyUp);
FlxG.stage.window.onTextInput.remove(onTextInput);
diff --git a/source/funkin/editors/ui/UIText.hx b/source/funkin/editors/ui/UIText.hx
index 413630b2..83daab60 100644
--- a/source/funkin/editors/ui/UIText.hx
+++ b/source/funkin/editors/ui/UIText.hx
@@ -4,11 +4,42 @@ import flixel.util.FlxColor;
import funkin.backend.FunkinText;
class UIText extends FunkinText {
+ public var optimized:Bool = false;
// PUTTING THIS HERE IN CASE IMMA MAKE FUTURE CHANGES
- public function new(x, y, w, text, size:Int = 15, color:FlxColor = 0xFFFFFFFF) {
+ public function new(x, y, w, text, size:Int = 15, color:FlxColor = 0xFFFFFFFF, ?outline:Bool = true) {
super(x, y, w, text, size, false);
this.color = color;
+ if (outline) {
+ this.borderStyle = OUTLINE_FAST;
+ this.borderColor = 0x88000000;
+ this.borderSize = 1;
+ }
this.active = false;
}
+
+ public override function applyBorderStyle() {
+ if (borderStyle == OUTLINE_FAST && optimized) {
+ var iterations:Int = Std.int(borderSize * borderQuality);
+ if (iterations <= 0)
+ {
+ iterations = 1;
+ }
+ var delta:Float = borderSize / iterations;
+
+ applyFormats(_formatAdjusted, true);
+
+ var curDelta:Float = delta;
+ for (i in 0...iterations)
+ {
+ copyTextWithOffset(-curDelta, -curDelta); // upper-left
+ copyTextWithOffset(curDelta * 2, curDelta * 2); // lower-right
+
+ _matrix.translate(-curDelta, -curDelta); // return to center
+ curDelta += delta;
+ }
+ return;
+ }
+ super.applyBorderStyle();
+ }
}
\ No newline at end of file
diff --git a/source/funkin/editors/ui/UITextBox.hx b/source/funkin/editors/ui/UITextBox.hx
index 17f7222e..3aa97fd2 100644
--- a/source/funkin/editors/ui/UITextBox.hx
+++ b/source/funkin/editors/ui/UITextBox.hx
@@ -68,7 +68,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
}
var off = multiline ? 4 : ((bHeight - label.height) / 2);
- label.follow(this, 4, off);
+ label.follow(this, label.autoSize ? (bWidth-label.textField.width)/2 : 4, off);
framesOffset = (selected ? 18 : (hovered ? 9 : 0));
@:privateAccess {
if (selected) {
@@ -104,6 +104,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
switch(e) {
case RETURN:
focused = false;
+ if (onChange != null) onChange(label.text);
case LEFT:
changeSelection(-1);
case RIGHT:
@@ -140,7 +141,6 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
}
// untested, but this should be a fix for if the text wont type
public function onTextEdit(text:String, start:Int, end:Int):Void {
- trace(text, start, end);
label.text = label.text.substr(0, position) + text + label.text.substr(position);
position += text.length;
}
diff --git a/source/funkin/editors/ui/UIWarningSubstate.hx b/source/funkin/editors/ui/UIWarningSubstate.hx
index b20e78f0..cd1a70de 100644
--- a/source/funkin/editors/ui/UIWarningSubstate.hx
+++ b/source/funkin/editors/ui/UIWarningSubstate.hx
@@ -12,6 +12,7 @@ class UIWarningSubstate extends MusicBeatSubstate {
var title:String;
var message:String;
var buttons:Array;
+ var isError:Bool = true;
var titleSpr:UIText;
var messageSpr:UIText;
@@ -55,22 +56,30 @@ class UIWarningSubstate extends MusicBeatSubstate {
warnCam.zoom = 0.1;
FlxG.cameras.add(warnCam, false);
- var spr = new UISliceSprite(0, 0, CoolUtil.maxInt(560, 30 + (170 * buttons.length)), 280, "editors/ui/warning-popup");
+ var spr = new UISliceSprite(0, 0, CoolUtil.maxInt(560, 30 + (170 * buttons.length)), 232, 'editors/ui/${isError ? "normal" : "grayscale"}-popup');
spr.x = (FlxG.width - spr.bWidth) / 2;
spr.y = (FlxG.height - spr.bHeight) / 2;
+ spr.color = isError ? 0xFFFF0000 : 0xFFFFFF00;
add(spr);
add(titleSpr = new UIText(spr.x + 25, spr.y, spr.bWidth - 50, title, 15, -1));
titleSpr.y = spr.y + ((30 - titleSpr.height) / 2);
- add(messageSpr = new UIText(spr.x + 10, spr.y + 40, spr.bWidth - 20, message, 15, -1));
+ var sprIcon:FlxSprite = new FlxSprite(spr.x + 26, spr.y + 28 + 26).loadGraphic(Paths.image('editors/warnings/${isError ? "error" : "warning"}'));
+ add(sprIcon);
+
+ add(messageSpr = new UIText(sprIcon.x + 100 + 16, sprIcon.y + 12, spr.bWidth - 100 - (26 * 2), message));
var xPos = (FlxG.width - (30 + (170 * buttons.length))) / 2;
for(k=>b in buttons) {
- var button = new UIButton(xPos + 20 + (170 * k), spr.y + spr.bHeight - 40, b.label, function() {
+ var button = new UIButton(xPos + 20 + (170 * k), spr.y + spr.bHeight - (36 + 16), b.label, function() {
b.onClick(this);
close();
}, 160, 30);
+ if (b.color != null) {
+ button.frames = Paths.getFrames("editors/ui/grayscale-button");
+ button.color = b.color;
+ }
add(button);
}
@@ -89,15 +98,17 @@ class UIWarningSubstate extends MusicBeatSubstate {
FlxG.cameras.remove(warnCam);
}
- public function new(title:String, message:String, buttons:Array) {
+ public function new(title:String, message:String, buttons:Array, ?isError:Bool = true) {
super();
this.title = title;
this.message = message;
this.buttons = buttons;
+ this.isError = isError;
}
}
typedef WarningButton = {
var label:String;
+ var ?color:Int;
var onClick:UIWarningSubstate->Void;
}
\ No newline at end of file
diff --git a/source/funkin/game/NoteGroup.hx b/source/funkin/game/NoteGroup.hx
index dc066ea4..ca617eec 100644
--- a/source/funkin/game/NoteGroup.hx
+++ b/source/funkin/game/NoteGroup.hx
@@ -8,6 +8,11 @@ class NoteGroup extends FlxTypedGroup {
var i:Int = 0;
var __currentlyLooping:Bool = false;
+ /**
+ * How many ms it should show a note before it should be hit
+ **/
+ public var limit:Float = 1500;
+
/**
* Preallocates the members array with nulls, but if theres anything in the array already it clears it
**/
@@ -36,7 +41,7 @@ class NoteGroup extends FlxTypedGroup {
if (__loopSprite == null || !__loopSprite.exists || !__loopSprite.active) {
continue;
}
- if (__loopSprite.strumTime - Conductor.songPosition > 1500)
+ if (__loopSprite.strumTime - Conductor.songPosition > limit)
break;
__loopSprite.update(elapsed);
}
@@ -55,7 +60,7 @@ class NoteGroup extends FlxTypedGroup {
__loopSprite = members[i--];
if (__loopSprite == null || !__loopSprite.exists || !__loopSprite.visible)
continue;
- if (__loopSprite.strumTime - Conductor.songPosition > 1500) break;
+ if (__loopSprite.strumTime - Conductor.songPosition > limit) break;
__loopSprite.draw();
}
__currentlyLooping = oldCur;
@@ -74,7 +79,7 @@ class NoteGroup extends FlxTypedGroup {
__loopSprite = members[i--];
if (__loopSprite == null || !__loopSprite.exists)
continue;
- if (__loopSprite.strumTime - Conductor.songPosition > 1500) break;
+ if (__loopSprite.strumTime - Conductor.songPosition > limit) break;
noteFunc(__loopSprite);
}
__currentlyLooping = oldCur;
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index 46d53041..9ad808ac 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -565,8 +565,8 @@ class PlayState extends MusicBeatState
for(folder in scriptsFolders) {
for(file in Paths.getFolderContent(folder, true, fromMods ? MODS : BOTH)) {
if (folder == 'data/charts/')
- Logs.trace('data/charts/ is deprecrated and will be removed in the future. Please move script $file.hx to songs/', WARNING, DARKYELLOW);
-
+ Logs.trace('data/charts/ is deprecrated and will be removed in the future. Please move script $file to songs/', WARNING, DARKYELLOW);
+
addScript(file);
}
}
@@ -576,7 +576,9 @@ class PlayState extends MusicBeatState
for (file in Paths.getFolderContent('data/events/', true, fromMods ? MODS : BOTH)) {
var fileName:String = Path.withoutExtension(Path.withoutDirectory(file));
- if (EventsData.eventsList.contains(fileName) && songEvents.contains(fileName)) addScript(file);
+ if (EventsData.eventsList.contains(fileName) && songEvents.contains(fileName)) {
+ addScript(file);
+ }
}
}
}
@@ -1490,7 +1492,7 @@ class PlayState extends MusicBeatState
if (event.showRating || (event.showRating == null && event.player))
{
displayCombo(event);
- displayRating(daRating, event);
+ displayRating(event.rating, event);
ratingNum += 1;
}
}
@@ -1531,7 +1533,7 @@ class PlayState extends MusicBeatState
rating.velocity.y -= FlxG.random.int(140, 175);
rating.velocity.x -= FlxG.random.int(0, 10);
if (evt != null) {
- rating.scale.set(evt.ratingScale,evt.ratingScale);
+ rating.scale.set(evt.ratingScale, evt.ratingScale);
rating.antialiasing = evt.ratingAntialiasing;
}
rating.updateHitbox();
@@ -1547,7 +1549,7 @@ class PlayState extends MusicBeatState
public function displayCombo(?evt:NoteHitEvent = null):Void {
var pre:String = evt != null ? evt.ratingPrefix : "";
var suf:String = evt != null ? evt.ratingSuffix : "";
-
+
var separatedScore:String = Std.string(combo).addZeros(3);
if (combo == 0 || combo >= 10) {
diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx
index 70fd260f..f8c5f0ce 100644
--- a/source/funkin/game/Stage.hx
+++ b/source/funkin/game/Stage.hx
@@ -238,6 +238,17 @@ class Stage extends FlxBasic implements IBeatReceiver {
public function stepHit(curStep:Int) {}
public function measureHit(curMeasure:Int) {}
+
+ public static function getList(?mods:Bool = false):Array {
+ var list:Array = [];
+ for (path in Paths.getFolderContent('data/stages/', true, mods ? MODS : BOTH))
+ if (Path.extension(path) == "xml" || Path.extension(path) == "hx") {
+ var file:String = Path.withoutDirectory(Path.withoutExtension(path));
+ if (!list.contains(file)) list.push(file);
+ }
+
+ return list;
+ }
}
class StageCharPos extends FlxObject {
diff --git a/source/funkin/game/cutscenes/ScriptedCutscene.hx b/source/funkin/game/cutscenes/ScriptedCutscene.hx
index 65d48958..b17639eb 100644
--- a/source/funkin/game/cutscenes/ScriptedCutscene.hx
+++ b/source/funkin/game/cutscenes/ScriptedCutscene.hx
@@ -72,7 +72,7 @@ class ScriptedCutscene extends Cutscene {
#if REGION
public function startDialogue(path:String, ?callback:Void->Void) {
persistentDraw = true;
- openSubState(new VideoCutscene(path, function() {
+ openSubState(new DialogueCutscene(path, function() {
if (callback != null)
callback();
}));
@@ -83,4 +83,4 @@ class ScriptedCutscene extends Cutscene {
private inline function get_isDialoguePlaying()
return subState is DialogueCutscene;
#end
-}
\ No newline at end of file
+}
diff --git a/source/funkin/game/cutscenes/VideoCutscene.hx b/source/funkin/game/cutscenes/VideoCutscene.hx
index 301cd9d3..68a86b92 100644
--- a/source/funkin/game/cutscenes/VideoCutscene.hx
+++ b/source/funkin/game/cutscenes/VideoCutscene.hx
@@ -9,7 +9,7 @@ import sys.io.File;
import funkin.backend.FunkinText;
import haxe.xml.Access;
#if VIDEO_CUTSCENES
-import hxcodec.flixel.FlxVideo;
+import hxvlc.flixel.FlxVideo;
#end
/**
diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx
index 7568c27a..3464f3ce 100644
--- a/source/funkin/menus/PauseSubState.hx
+++ b/source/funkin/menus/PauseSubState.hx
@@ -22,7 +22,7 @@ class PauseSubState extends MusicBeatSubstate
var grpMenuShit:FlxTypedGroup;
- var menuItems:Array = ['Resume', 'Restart Song', 'Change Controls', 'Options','Exit to menu'];
+ var menuItems:Array = ['Resume', 'Restart Song', 'Change Controls', 'Change Options', 'Exit to menu', "Exit to charter"];
var curSelected:Int = 0;
var pauseMusic:FlxSound;
@@ -42,6 +42,9 @@ class PauseSubState extends MusicBeatSubstate
{
super.create();
+ if (menuItems.contains("Exit to charter") && !PlayState.chartingMode)
+ menuItems.remove("Exit to charter");
+
add(parentDisabler = new FunkinParentDisabler());
pauseScript = Script.create(Paths.script(script));
@@ -147,8 +150,10 @@ class PauseSubState extends MusicBeatSubstate
case "Change Controls":
persistentDraw = false;
openSubState(new KeybindsOptions());
- case "Options":
+ case "Change Options":
FlxG.switchState(new OptionsMenu());
+ case "Exit to charter":
+ FlxG.switchState(new funkin.editors.charter.Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
case "Exit to menu":
CoolUtil.playMenuSong();
FlxG.switchState(PlayState.isStoryMode ? new StoryMenuState() : new FreeplayState());
diff --git a/source/funkin/menus/TitleState.hx b/source/funkin/menus/TitleState.hx
index d46e04b9..da7e25f8 100644
--- a/source/funkin/menus/TitleState.hx
+++ b/source/funkin/menus/TitleState.hx
@@ -48,9 +48,9 @@ class TitleState extends MusicBeatState
MusicBeatState.skipTransIn = true;
- super.create();
-
startIntro();
+
+ super.create();
}
var logoBl:FlxSprite;
diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx
index 397b8069..8adaabce 100644
--- a/source/funkin/options/categories/GameplayOptions.hx
+++ b/source/funkin/options/categories/GameplayOptions.hx
@@ -43,9 +43,9 @@ class GameplayOptions extends OptionsScreen {
FlxG.camera.zoom += 0.03;
__lastBeat = Conductor.curBeat;
}
- if (__lastSongBeat != Math.floor(Conductor.getStepForTime(FlxG.sound.music.time / 2) / Conductor.stepsPerBeat)) {
+ if (__lastSongBeat != Math.floor(Conductor.getStepForTime(FlxG.sound.music.time) / Conductor.stepsPerBeat)) {
__metronome.replay();
- __lastSongBeat = Math.floor(Conductor.getStepForTime(FlxG.sound.music.time / 2) / Conductor.stepsPerBeat);
+ __lastSongBeat = Math.floor(Conductor.getStepForTime(FlxG.sound.music.time) / Conductor.stepsPerBeat);
}
}
else FlxG.sound.music.volume = 1;
diff --git a/source/funkin/options/type/NewOption.hx b/source/funkin/options/type/NewOption.hx
index bd7d1a14..e5c0f9eb 100644
--- a/source/funkin/options/type/NewOption.hx
+++ b/source/funkin/options/type/NewOption.hx
@@ -13,6 +13,7 @@ class NewOption extends TextOption {
iconSpr = new FlxSprite().loadGraphic(Paths.image("editors/new"));
iconSpr.setPosition(90 - iconSpr.width, (__text.height - iconSpr.height) / 2);
+ iconSpr.offset.set(20, -15);
add(iconSpr);
}
}
\ No newline at end of file
diff --git a/source/hscript/Config.hx b/source/hscript/Config.hx
new file mode 100644
index 00000000..bba205c5
--- /dev/null
+++ b/source/hscript/Config.hx
@@ -0,0 +1,31 @@
+package hscript;
+
+class Config {
+ // Runs support for custom classes in these
+ public static final ALLOWED_CUSTOM_CLASSES = [
+ "flixel",
+
+ "funkin",
+ ];
+
+ // Runs support for abstract support in these
+ public static final ALLOWED_ABSTRACT_AND_ENUM = [
+ "flixel",
+ "openfl.display.BlendMode",
+
+ "haxe.xml",
+ "haxe.CallStack",
+ "funkin",
+ ];
+
+ // Incase any of your files fail
+ // These are the module names
+ public static final DISALLOW_CUSTOM_CLASSES = [
+
+ ];
+
+ 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