Merge branch 'main' of https://github.com/FNF-CNE-Devs/CodenameEngine-Dev
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 652 B After Width: | Height: | Size: 611 B |
|
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 209 B |
|
After Width: | Height: | Size: 949 B |
|
After Width: | Height: | Size: 129 B |
|
Before Width: | Height: | Size: 652 B After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 685 B |
|
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 882 B |
|
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 407 B |
|
Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 560 B |
|
Before Width: | Height: | Size: 737 B After Width: | Height: | Size: 1.1 KiB |
|
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>
|
||||
|
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>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
];
|
||||
}
|
||||