This commit is contained in:
WizardMantis441
2023-10-14 15:19:52 -05:00
80 changed files with 1719 additions and 667 deletions
+3
View File
@@ -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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 B

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 B

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 B

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 737 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<TextureAtlas imagePath="button.png">
<!-- Created without animate lol i wrote this-->
<SubTexture name="normal-topleft" x="0" y="0" width="4" height="4"/>
<SubTexture name="normal-top" x="4" y="0" width="12" height="4"/>
<SubTexture name="normal-topright" x="16" y="0" width="4" height="4"/>
<SubTexture name="normal-middleleft" x="0" y="4" width="4" height="15"/>
<SubTexture name="normal-middle" x="4" y="4" width="12" height="15"/>
<SubTexture name="normal-middleright" x="16" y="4" width="4" height="15"/>
<SubTexture name="normal-bottomleft" x="0" y="19" width="4" height="4"/>
<SubTexture name="normal-bottom" x="4" y="19" width="12" height="4"/>
<SubTexture name="normal-bottomright" x="16" y="19" width="4" height="4"/>
<SubTexture name="hover-topleft" x="20" y="0" width="4" height="4"/>
<SubTexture name="hover-top" x="24" y="0" width="12" height="4"/>
<SubTexture name="hover-topright" x="36" y="0" width="4" height="4"/>
<SubTexture name="hover-middleleft" x="20" y="4" width="4" height="15"/>
<SubTexture name="hover-middle" x="24" y="4" width="12" height="15"/>
<SubTexture name="hover-middleright" x="36" y="4" width="4" height="15"/>
<SubTexture name="hover-bottomleft" x="20" y="19" width="4" height="4"/>
<SubTexture name="hover-bottom" x="24" y="19" width="12" height="4"/>
<SubTexture name="hover-bottomright" x="36" y="19" width="4" height="4"/>
<SubTexture name="pressed-topleft" x="40" y="0" width="4" height="4"/>
<SubTexture name="pressed-top" x="44" y="0" width="12" height="4"/>
<SubTexture name="pressed-topright" x="56" y="0" width="4" height="4"/>
<SubTexture name="pressed-middleleft" x="40" y="4" width="4" height="15"/>
<SubTexture name="pressed-middle" x="44" y="4" width="12" height="15"/>
<SubTexture name="pressed-middleright" x="56" y="4" width="4" height="15"/>
<SubTexture name="pressed-bottomleft" x="40" y="19" width="4" height="4"/>
<SubTexture name="pressed-bottom" x="44" y="19" width="12" height="4"/>
<SubTexture name="pressed-bottomright" x="56" y="19" width="4" height="4"/>
</TextureAtlas>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<TextureAtlas imagePath="button.png">
<!-- Created without animate lol i wrote this-->
<SubTexture name="topleft" x="0" y="0" width="30" height="30"/>
<SubTexture name="top" x="30" y="0" width="99" height="30"/>
<SubTexture name="topright" x="129" y="0" width="30" height="30"/>
<SubTexture name="middleleft" x="0" y="30" width="30" height="33"/>
<SubTexture name="middle" x="30" y="30" width="99" height="33"/>
<SubTexture name="middleright" x="129" y="30" width="30" height="33"/>
<SubTexture name="bottomleft" x="0" y="63" width="30" height="30"/>
<SubTexture name="bottom" x="30" y="63" width="99" height="30"/>
<SubTexture name="bottomright" x="129" y="63" width="30" height="30"/>
</TextureAtlas>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

+14 -16
View File
@@ -1,24 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<libraries>
<!-- Flixel -->
<git name="flixel" url="https://github.com/FNF-CNE-Devs/flixel"/>
<git name="flixel-addons" url="https://github.com/FNF-CNE-Devs/flixel-addons"/>
<!-- OpenFL & Lime (Required for Flixel) -->
<lib name="openfl" />
<lib name="lime" />
<!-- Other Libraries -->
<!-- Flixel -->
<git name="flixel" url="https://github.com/FNF-CNE-Devs/flixel" />
<git name="flixel-addons" url="https://github.com/FNF-CNE-Devs/flixel-addons" />
<!-- Other Libraries -->
<git name="hscript-improved" url="https://www.github.com/FNF-CNE-Devs/hscript-improved" ref="custom-classes" />
<git name="discord-rpc" url="https://github.com/Aidan63/linc_discord-rpc"/>
<git name="flxanimate" url="https://github.com/FNF-CNE-Devs/flxanimate/" />
<git name="hxCodec-cne" url="https://github.com/FNF-CNE-Devs/hxCodec-cne"/>
<git name="discord-rpc" url="https://github.com/Aidan63/linc_discord-rpc" />
<git name="flxanimate" url="https://github.com/FNF-CNE-Devs/flxanimate" />
<git name="hxvlc" url="https://github.com/MAJigsaw77/hxvlc" />
<!-- Documentation and other features -->
<lib name="dox"/>
<lib name="away3d" />
<lib name="away3d" />
<!-- <lib name="dox" /> -->
<lib name="format" />
<lib name="markdown" />
<lib name="hxcpp-debug-server" />
<!-- OpenFL & Lime (Required for Flixel) -->
<lib name="openfl" />
<lib name="lime" />
</libraries>
+14 -19
View File
@@ -21,7 +21,6 @@
<!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file
or you can set the actual size of your file manually at "FlxPreloaderBase-onUpdate-bytesTotal"-->
<!-- <app preloader="Preloader" resizable="true" /> -->
<app preloader="flixel.system.FlxPreloader" />
<!--Minimum without FLX_NO_GAMEPAD: 11.8, without FLX_NO_NATIVE_CURSOR: 11.2-->
@@ -53,12 +52,11 @@
<set name="BUILD_DIR" value="export/release-32bit" if="32bit" unless="debug" />
<set name="BUILD_DIR" value="latest/" if="beta" />
<classpath name="source" />
<source name="source" />
<define name="PRELOAD_ALL" unless="web" />
<define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL"/>
<library name="assets" preload="true" if="PRELOAD_ALL"/>
<library name="assets" preload="false" if="NO_PRELOAD_ALL" />
@@ -74,17 +72,11 @@
<haxeflag name="--macro" value="funkin.backend.system.macros.NewHaxeWarning.warn()" />
<!--Enable the Flixel core recording system-->
<!--<haxedef name="FLX_RECORD" />-->
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
<!--Disable the Flixel core debugger. Automatically gets set whenever you compile in release mode!-->
<haxedef name="FLX_NO_DEBUG" unless="debug" />
<!--Enable this for Nape release builds for a serious performance improvement-->
<haxedef name="NAPE_RELEASE_BUILD" unless="debug" />
<!-- _________________________________ Custom _______________________________ -->
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
@@ -100,6 +92,7 @@
<icon path="art/iconOG.png" size='256' />
<icon path="art/iconOG.png" size='512' />
<icon path="art/iconOG.png" size='768' />
<icon path="art/iconOG.png" size='1024' />
<!-- _________________________________ Engine Settings _______________________________ -->
@@ -113,10 +106,12 @@
<!-- Fixes thing with macros -->
<define name="HL" if="hl"/>
<define name="SYS" if="desktop"/>
<define name="SYS" if="cpp || hl || neko"/>
<!-- Comment this out to disable Discord RPC !-->
<define name="DISCORD_RPC" unless="web || hl || neko"/>
<section if="cpp">
<define name="DISCORD_RPC" if="desktop"/>
</section>
<!-- Comment this out to disable GitHub API integration !-->
<define name="GITHUB_API" unless="web || hl"/>
@@ -157,7 +152,9 @@
<define name="THREE_D_SUPPORT" />
<!-- Comment this out to disable video cutscene support, in case of compilation errors. !-->
<define name="VIDEO_CUTSCENES" unless="web || linux || hl" />
<section if="cpp">
<define name="VIDEO_CUTSCENES" if="desktop || android" />
</section>
<!-- Comment this out to disable commit number on FPS -->
<define name="SHOW_BUILD_ON_FPS" />
@@ -172,23 +169,20 @@
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES" />
<haxedef name="openfl_dpi_aware" if="openfl_dpi_aware" />
<haxedef name="EXPERIMENTAL_FLXGRAPHIC_DESTROY_FIX" />
<!-- _______________________________ Libraries ______________________________ -->
<haxelib name="flixel" />
<haxelib name="flixel-addons" />
<haxelib name="hxCodec-cne" if="VIDEO_CUTSCENES" />
<section if="THREE_D_SUPPORT">
<haxelib name="away3d" />
</section>
<haxelib name="hxvlc" if="VIDEO_CUTSCENES" />
<haxelib name="away3d" if="THREE_D_SUPPORT" />
<haxelib name="format" />
<haxelib name="flxanimate" />
<haxedev set='webgl' />
<haxelib name="hscript-improved" />
<haxelib name="discord-rpc" if="DISCORD_RPC"/>
<haxelib name="markdown" />
<haxedef name="no-deprecation-warnings"/>
@@ -208,5 +202,6 @@
<haxedef name="HXCPP_CHECK_POINTER" if="release" />
<haxedef name="HXCPP_STACK_LINE" if="release" />
<haxedef name="HXCPP_DEBUG_LINK" if="release" />
<haxedef name="hscriptPos" /> <!-- for logging -->
</project>
+1 -1
View File
@@ -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;
+22 -16
View File
@@ -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<Dynamic>, ?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<T:CancellableEvent>(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() {
+22 -16
View File
@@ -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<Dynamic>, ?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<T:CancellableEvent>(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) {
+6 -4
View File
@@ -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) {
@@ -23,6 +23,7 @@ using StringTools;
interface IModsAssetLibrary {
public var prefix:String;
public var modName:String;
public var libName:String;
#if MOD_SUPPORT
+28 -7
View File
@@ -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<String> {
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
}
@@ -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
@@ -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<FlxFramesCollection> = [];
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<MultiFramesCollection> = 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();
}
}
+8 -12
View File
@@ -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);
@@ -21,9 +21,9 @@ class ScriptedAssetLibrary extends ModsFolderLibrary {
public var scriptName:String;
private static var nullValue:Dynamic = {};
public function new(scriptName:String, args:Array<Dynamic> = null, folderPath:String="./assets/", libName:String="assets") {
super(folderPath, libName);
public function new(scriptName:String, args:Array<Dynamic> = 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<String, Dynamic>, asset:String, isLocalCache:Bool = false) {
var result:Dynamic = script.call("__isCacheValid", [cache, asset, isLocalCache]);
if(result != null) {
@@ -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<String, SysZipEntry> = [];
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;
}
@@ -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({
+10 -4
View File
@@ -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;
+16 -16
View File
@@ -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<T:CancellableEvent>(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<Dynamic>) {
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) {
+10 -1
View File
@@ -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<String>):Bool {
+29
View File
@@ -109,6 +109,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
*/
public static var scriptExtensions:Array<String> = [
"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.
@@ -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":
@@ -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() {
+4 -1
View File
@@ -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
+2 -2
View File
@@ -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();
@@ -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";
}
@@ -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
@@ -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();
+46
View File
@@ -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<EditorTreeMenu> = 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;
}
}
@@ -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);
@@ -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 {
@@ -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) {}}
]));
}));
@@ -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");
@@ -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();
}
}
+240 -269
View File
@@ -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<CharterStrumline> = new FlxTypedGroup<CharterStrumline>();
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<SelectionDragChange> = [];
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<Dynamic>);
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<ChartNote> = [];
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<CharterNote> = [];
// 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<ICharterSelectable> = [];
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<NoteSustainChange> = [];
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<SelectionDragChange>);
CEditSustains(notes:Array<NoteSustainChange>);
CEditEvent(event:CharterEvent, oldEvents:Array<ChartEvent>, newEvents:Array<ChartEvent>);
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<ChartEvent>);
}
@@ -1388,6 +1354,11 @@ typedef NoteSustainChange = {
var after:Float;
}
typedef SelectionDragChange = {
var selectable:ICharterSelectable;
var change:FlxPoint;
}
@:forward abstract Selection(Array<ICharterSelectable>) from Array<ICharterSelectable> to Array<ICharterSelectable> {
public inline function new(?array:Array<ICharterSelectable>)
this = array == null ? [] : array;
@@ -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];
}
}
@@ -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<CharterBackdrop> {
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<Dynamic> {
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<CharterNote> = new FlxTypedGroup<CharterNote>();
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();
}
}
+20 -6
View File
@@ -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<ChartEvent>) {
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));
}
}
@@ -1,3 +1,40 @@
package funkin.editors.charter;
class CharterEventGroup extends FlxTypedGroup<CharterEvent> {}
import funkin.editors.charter.CharterBackdropGroup.EventBackdrop;
import flixel.util.FlxSort;
class CharterEventGroup extends FlxTypedGroup<CharterEvent> {
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();
}
}
}
@@ -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());
+24 -18
View File
@@ -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();
}
@@ -1,5 +1,6 @@
package funkin.editors.charter;
import flixel.util.FlxSort;
import funkin.backend.system.Conductor;
class CharterNoteGroup extends FlxTypedGroup<CharterNote> {
@@ -8,6 +9,9 @@ class CharterNoteGroup extends FlxTypedGroup<CharterNote> {
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<CharterNote> {
__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<CharterNote> {
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<CharterNote> {
@: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<CharterNote>(len);
length = len;
}
}
@@ -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));
@@ -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<CharterStrumline> {
var __pastStrumlines:Array<CharterStrumline>;
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<EventParamInfo> = 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;
}
}
@@ -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();
@@ -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();
@@ -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<CharacterButton>(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<CharacterButton>) {
public function new(x:Float, y:Float, char:String, charsList:Array<String>, parent:UIButtonList<CharacterButton>) {
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);
@@ -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<String> = [];
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<String> = [];
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<String>) {
suggestItems = v;
updateSuggestion();
return v;
}
}
-3
View File
@@ -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;
@@ -115,6 +115,7 @@ typedef UIContextMenuOption = {
var ?keybind:Array<FlxKey>;
var ?keybinds:Array<Array<FlxKey>>;
var ?keybindText:String;
var ?color:Int;
var ?icon:Int;
var ?onSelect:UIContextMenuOption->Void;
var ?childs:Array<UIContextMenuOption>;
@@ -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);
+153
View File
@@ -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<SliderSegement> = [];
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<SliderSegement>, 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;
}
}
+12
View File
@@ -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);
+32 -1
View File
@@ -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();
}
}
+2 -2
View File
@@ -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;
}
+15 -4
View File
@@ -12,6 +12,7 @@ class UIWarningSubstate extends MusicBeatSubstate {
var title:String;
var message:String;
var buttons:Array<WarningButton>;
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<WarningButton>) {
public function new(title:String, message:String, buttons:Array<WarningButton>, ?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;
}
+8 -3
View File
@@ -8,6 +8,11 @@ class NoteGroup extends FlxTypedGroup<Note> {
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<Note> {
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<Note> {
__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<Note> {
__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;
+8 -6
View File
@@ -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) {
+11
View File
@@ -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<String> {
var list:Array<String> = [];
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 {
@@ -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
}
}
@@ -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
/**
+7 -2
View File
@@ -22,7 +22,7 @@ class PauseSubState extends MusicBeatSubstate
var grpMenuShit:FlxTypedGroup<Alphabet>;
var menuItems:Array<String> = ['Resume', 'Restart Song', 'Change Controls', 'Options','Exit to menu'];
var menuItems:Array<String> = ['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());
+2 -2
View File
@@ -48,9 +48,9 @@ class TitleState extends MusicBeatState
MusicBeatState.skipTransIn = true;
super.create();
startIntro();
super.create();
}
var logoBl:FlxSprite;
@@ -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;
+1
View File
@@ -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);
}
}
+31
View File
@@ -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
];
}