diff --git a/README.md b/README.md
index b4d4bcc9..93b36034 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,8 @@ Also, the command `.\cne test` uses the source assets folder instead of the expo
## Codename Engine
Codename Engine is a new Friday Night Funkin' Engine aimed at simplifying modding, along with extensiblity and ease of use.
-Make sure to read the wiki and join the Discord server linked there!
-#### It includes many new features, as seen [here](FEATURES.md)
+### Before making issues or need help with something, check our website [HERE](https://fnf-cne-devs.github.io/) (it contains a wiki of how to mod with EXAMPLES, an api, lists of mods made with Codename Engine and more)!!!
+#### The Engine includes many new features, as seen [here](FEATURES.md)
#### Wanna see the new features added in the most recent update? Click [here](PATCHNOTES.md)
## How to download
diff --git a/art/flashFiles/TitleEnter.fla b/art/flashFiles/TitleEnter.fla
new file mode 100644
index 00000000..2605cfd3
Binary files /dev/null and b/art/flashFiles/TitleEnter.fla differ
diff --git a/assets/data/alphabet.xml b/assets/data/alphabet.xml
index 8ae172cd..252af638 100644
--- a/assets/data/alphabet.xml
+++ b/assets/data/alphabet.xml
@@ -2,33 +2,76 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/data/alphabet/bold.xml b/assets/data/alphabet/bold.xml
index b39cc25f..5be5fa4b 100644
--- a/assets/data/alphabet/bold.xml
+++ b/assets/data/alphabet/bold.xml
@@ -1,6 +1,49 @@
-
+
menus/alphabet-bold
- LETTER bold
+ character-LETTER0
+ character-ampersand
+ character-anglebracket-left
+ character-anglebracket-right
+ character-asterisk
+ character-at
+ character-backslash
+ character-backtick
+ character-caret
+ character-colon
+ character-comma
+ character-curlybracket-left
+ character-curlybracket-right
+ character-dollar
+ character-doublequote0
+ character-equal
+ character-exclamationmark
+ character-hyphen
+ character-percent
+ character-period
+ character-plus
+ character-pound
+ character-questionmark0
+ character-roundbracket-left
+ character-roundbracket-right
+ character-semicolon
+ character-singlequote
+ character-slash
+ character-squarebracket-left
+ character-squarebracket-right
+ character-tilde0
+ character-underscore0
+ character-verticalbar0
+
+ character-zero
+ character-one
+ character-two
+ character-three
+ character-four
+ character-five
+ character-six
+ character-seven
+ character-eight
+ character-nine
\ No newline at end of file
diff --git a/assets/data/alphabet/normal.xml b/assets/data/alphabet/normal.xml
index a02526db..a82323fe 100644
--- a/assets/data/alphabet/normal.xml
+++ b/assets/data/alphabet/normal.xml
@@ -1,13 +1,17 @@
-
+
menus/alphabet
LETTER capital
LETTER lowercase
LETTER
+ g lowercase
+ j lowercase
+ p lowercase
+ q lowercase
+ y lowercase
dollarsign
- hashtag
amp
right arrow
left arrow
diff --git a/assets/data/config/credits.xml b/assets/data/config/credits.xml
deleted file mode 100644
index 4f300fcb..00000000
--- a/assets/data/config/credits.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/assets/data/config/options.xml b/assets/data/config/options.xml
deleted file mode 100644
index 888288d7..00000000
--- a/assets/data/config/options.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/assets/data/dialogue/characters/senpai.hx b/assets/data/dialogue/characters/senpai.hx
index cd17b115..1ea774d7 100644
--- a/assets/data/dialogue/characters/senpai.hx
+++ b/assets/data/dialogue/characters/senpai.hx
@@ -1,6 +1,6 @@
function postHide() {
if(curTween != null) {
- if(animation.curAnim?.name == 'angry-show') curTween.cancel();
+ if(getAnimName() == 'angry-show') curTween.cancel();
else curTween.percent = 1;
}
-}
\ No newline at end of file
+}
diff --git a/assets/data/titlescreen/titlescreen.xml b/assets/data/titlescreen/titlescreen.xml
index f16c8b62..323e9ef1 100644
--- a/assets/data/titlescreen/titlescreen.xml
+++ b/assets/data/titlescreen/titlescreen.xml
@@ -7,7 +7,7 @@
-
+
diff --git a/assets/fonts/StatusPlz.ttf b/assets/fonts/StatusPlz.ttf
new file mode 100644
index 00000000..0f31536f
Binary files /dev/null and b/assets/fonts/StatusPlz.ttf differ
diff --git a/assets/fonts/vcr.ttf b/assets/fonts/vcr.ttf
index dcca687a..24a56b5d 100644
Binary files a/assets/fonts/vcr.ttf and b/assets/fonts/vcr.ttf differ
diff --git a/assets/images/menus/alphabet-bold.png b/assets/images/menus/alphabet-bold.png
index 012721ce..004a99f5 100644
Binary files a/assets/images/menus/alphabet-bold.png and b/assets/images/menus/alphabet-bold.png differ
diff --git a/assets/images/menus/alphabet-bold.xml b/assets/images/menus/alphabet-bold.xml
index 26ce48db..7ffb4267 100644
--- a/assets/images/menus/alphabet-bold.xml
+++ b/assets/images/menus/alphabet-bold.xml
@@ -1,153 +1,514 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/languages/en/Editors.xml b/assets/languages/en/Editors.xml
index 71182b3a..0f35b716 100644
--- a/assets/languages/en/Editors.xml
+++ b/assets/languages/en/Editors.xml
@@ -4,10 +4,11 @@
Autosaving in {0} seconds{1}
(Canceled)!
- Save & Close
- Save
- Close
Ok
+ Cancel
+ Save
+ Save & Close
+ Close
@@ -260,9 +261,11 @@
+ Press ACCEPT to edit this folder.
Press ACCEPT to edit this character.
New Character
Select a character to edit
+ Select a character to edit in {0}
New Character: Feature Not Implemented!
diff --git a/assets/languages/en/Main.xml b/assets/languages/en/Main.xml
index d8eff461..dec03273 100644
--- a/assets/languages/en/Main.xml
+++ b/assets/languages/en/Main.xml
@@ -72,7 +72,8 @@
WARNING
- This engine is still in a *{0}* state. That means *majority of the features* are either *buggy* or *non finished*. If you find any bugs, please report them to the Codename Engine GitHub.\n\nPress {1} to continue
+ This engine is still in a *{0}* state. That means *majority of the features* are either *buggy* or *non finished*. If you find any bugs, please report them to the Codename Engine GitHub.
+ Press {0} to continue
@@ -100,5 +101,7 @@
Easy
Normal
Hard
+ Erect
+ Nightmare
\ No newline at end of file
diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml
index f7debfaf..51e317eb 100644
--- a/assets/languages/en/Options.xml
+++ b/assets/languages/en/Options.xml
@@ -42,8 +42,7 @@
Engine
-
- Change Mod or Mode
+ Mod/Mode
diff --git a/assets/shaders/engine/editorBlur.frag b/assets/shaders/engine/editorBlur.frag
index 59b91c72..85754b99 100644
--- a/assets/shaders/engine/editorBlur.frag
+++ b/assets/shaders/engine/editorBlur.frag
@@ -3,19 +3,17 @@
// Used for editors
-int steps = 16;
-int stepsInside = 2;
-float strength = 0.0075;
+const int steps = 16;
+const int stepsInside = 2;
+const float strength = 0.0075;
+
vec4 getColor(vec2 pos) {
- vec2 ps = (pos);
- ps = clamp(ps, vec2(0.0), 1.0 - (1.0 / openfl_TextureSize.xy));
- return flixel_texture2D(bitmap, (ps));
+ pos = clamp(pos, vec2(0.0), vec2(1.0));//1.0 - (1.0 / openfl_TextureSize.xy));
+ return flixel_texture2D(bitmap, pos);
}
+
void main() {
- // vec2 camPos = getCamPos(openfl_TextureCoordv);
vec2 camPos = openfl_TextureCoordv;
- if (camPos.x < 0.0 || camPos.x > 1.0 || camPos.y < 0.0 || camPos.y > 1.0)
- return;
vec4 color = getColor(camPos);
float fsteps = float(steps);
diff --git a/assets/shaders/engine/editorBlurFast.frag b/assets/shaders/engine/editorBlurFast.frag
index 75ef96af..db476ae8 100644
--- a/assets/shaders/engine/editorBlurFast.frag
+++ b/assets/shaders/engine/editorBlurFast.frag
@@ -2,58 +2,29 @@
// Used for editors (faster)
-//#define iResolution _camSize
-#define getTexture textureCam
-
-#define iResolution openfl_TextureSize
-//#define getTexture flixel_texture2D
-
-float uBlur = 0.015;
-float uBrightness = 0.6;
+const float uBlur = 0.015;
+const float uBrightness = 0.6;
vec4 getColor(vec2 pos) {
- vec2 ps = (pos);
- if (ps.x < 0.0) ps.x = 0.0;
- //else if (ps.x > 1.0 - (1.0 / iResolution.x)) ps.x = 1.0 - (1.0 / iResolution.x);
- else if (ps.x > 1.0) return vec4(0.0);
- if (ps.y < 0.0) ps.y = 0.0;
- //else if (ps.y > 1.0 - (1.0 / iResolution.y)) return ps.y = 1.0 - (1.0 / iResolution.y);
- else if (ps.y > 1.0) return vec4(0.0);
- return flixel_texture2D(bitmap, (ps));
+ pos = clamp(pos, vec2(0.0), vec2(1.0));//1.0 - (1.0 / openfl_TextureSize.xy));
+ return flixel_texture2D(bitmap, pos);
}
-
-vec2 fixvec2(float x, float y) { // makes an uv the same across sizes
- vec2 val = vec2(x, y);
- val.xy *= vec2(1280.0, 720.0);
- val.xy /= iResolution.xy;
- return val;
-}
-vec2 fixvec2(vec2 uv) { // makes an uv the same across sizes
- vec2 val = uv;
- val.xy *= vec2(1280.0, 720.0);
- val.xy /= iResolution.xy;
- return val;
-}
+uniform sampler2D noiseTexture;
+uniform vec2 noiseTextureSize;
vec2 random(vec2 p) {
- p = vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3)));
- return fract(sin(p)*4375.5);
+ p *= openfl_TextureSize;
+ return texture2D(noiseTexture, p / noiseTextureSize).rg - vec2(0.5);
}
void main() {
- vec2 camPos = (openfl_TextureCoordv);
- //vec2 camPos = openfl_TextureCoordv;
- if (camPos.x < 0.0 || camPos.x > 1.0 || camPos.y < 0.0 || camPos.y > 1.0)
- return;
+ vec2 camPos = openfl_TextureCoordv;
+ vec2 blur = vec2(uBlur) * vec2(1.0, openfl_TextureSize.x / openfl_TextureSize.y);
- //vec4 color = getColor(camPos);
-
- vec2 blur = vec2(uBlur) * vec2(1.0, iResolution.x / iResolution.y);
-
- vec4 a = getColor(camPos+(random(camPos)*blur - blur / 2.0)) * uBrightness;
- a += getColor(camPos+(random(camPos+0.1)*blur - blur / 2.0)) * uBrightness;
- a += getColor(camPos+(random(camPos+0.2)*blur - blur / 2.0)) * uBrightness;
- //a += getColor(camPos+(random(camPos+0.3)*blur - blur / 2.0)) * uBrightness;
- gl_FragColor = a / 3.0;
+ vec4 a = getColor(camPos+random(camPos)*blur);
+ a += getColor(camPos+random(camPos+0.1)*blur);
+ //a += getColor(camPos+random(camPos+0.2)*blur);
+ //a += getColor(camPos+random(camPos+0.3)*blur);
+ gl_FragColor = (a / 2.0) * uBrightness;
}
\ No newline at end of file
diff --git a/assets/shaders/noise128.png b/assets/shaders/noise128.png
new file mode 100644
index 00000000..682cb32a
Binary files /dev/null and b/assets/shaders/noise128.png differ
diff --git a/assets/shaders/noise256.png b/assets/shaders/noise256.png
new file mode 100644
index 00000000..fd2a1410
Binary files /dev/null and b/assets/shaders/noise256.png differ
diff --git a/assets/shaders/noise512.png b/assets/shaders/noise512.png
new file mode 100644
index 00000000..d50270af
Binary files /dev/null and b/assets/shaders/noise512.png differ
diff --git a/project.xml b/project.xml
index fc5f5474..c8bd462f 100644
--- a/project.xml
+++ b/project.xml
@@ -118,9 +118,6 @@
-
-
-
diff --git a/source/StringTools.hx b/source/StringTools.hx
index 0b495af1..40397636 100644
--- a/source/StringTools.hx
+++ b/source/StringTools.hx
@@ -219,7 +219,7 @@ class StringTools {
If `start` is the empty String `""`, the result is true.
**/
- public static #if (java || python || (js && js_es >= 6)) inline #end function startsWith(s:String, start:String):Bool {
+ public static #if (java || python || (js && js_es >= 6) || cpp) inline #end function startsWith(s:String, start:String):Bool {
#if java
return (cast s : java.NativeString).startsWith(start);
#elseif hl
@@ -230,6 +230,8 @@ class StringTools {
return (cast s).startsWith(start);
#elseif lua
return untyped __lua__("{0}:sub(1, #{1}) == {1}", s, start);
+ #elseif cpp
+ return untyped s.__StartsWith(start);
#else
return (s.length >= start.length && s.indexOf(start, 0) == 0);
#end
@@ -242,7 +244,7 @@ class StringTools {
If `end` is the empty String `""`, the result is true.
**/
- public static #if (java || python || (js && js_es >= 6)) inline #end function endsWith(s:String, end:String):Bool {
+ public static #if (java || python || (js && js_es >= 6) || cpp) inline #end function endsWith(s:String, end:String):Bool {
#if java
return (cast s : java.NativeString).endsWith(end);
#elseif hl
@@ -255,6 +257,8 @@ class StringTools {
return (cast s).endsWith(end);
#elseif lua
return end == "" || untyped __lua__("{0}:sub(-#{1}) == {1}", s, end);
+ #elseif cpp
+ return untyped s.__EndsWith(end);
#else
var elen = end.length;
var slen = s.length;
diff --git a/source/flx3d/Flx3DCamera.hx b/source/flx3d/Flx3DCamera.hx
index 4e1da93d..129ace5d 100644
--- a/source/flx3d/Flx3DCamera.hx
+++ b/source/flx3d/Flx3DCamera.hx
@@ -87,7 +87,7 @@ class Flx3DCamera extends FlxCamera {
default: new OBJParser();
}, (event:Asset3DEvent) -> {
if (event.asset != null && event.asset.assetType == Asset3DType.MESH) {
- var mesh:Mesh = cast(event.asset, Mesh);
+ var mesh:Mesh = cast event.asset;
if (material != null)
mesh.material = material;
meshes.push(mesh);
diff --git a/source/flx3d/Flx3DView.hx b/source/flx3d/Flx3DView.hx
index e76500c3..5413fe94 100644
--- a/source/flx3d/Flx3DView.hx
+++ b/source/flx3d/Flx3DView.hx
@@ -60,7 +60,7 @@ class Flx3DView extends FlxView3D {
default: new OBJParser();
}, (event:Asset3DEvent) -> {
if (event.asset != null && event.asset.assetType == Asset3DType.MESH) {
- var mesh:Mesh = cast(event.asset, Mesh);
+ var mesh:Mesh = cast event.asset;
if (material != null)
mesh.material = material;
meshes.push(mesh);
diff --git a/source/funkin/backend/MusicBeatGroup.hx b/source/funkin/backend/MusicBeatGroup.hx
index ec6f2693..5cccf0f6 100644
--- a/source/funkin/backend/MusicBeatGroup.hx
+++ b/source/funkin/backend/MusicBeatGroup.hx
@@ -4,12 +4,12 @@ import funkin.backend.system.interfaces.IBeatReceiver;
class MusicBeatGroup extends FlxTypedSpriteGroup implements IBeatReceiver {
public function beatHit(curBeat:Int) {
- for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).beatHit(curBeat);
+ for(e in members) if (e is IBeatReceiver) ({var _:IBeatReceiver = cast e;_;}).beatHit(curBeat);
}
public function stepHit(curStep:Int) {
- for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).stepHit(curStep);
+ for(e in members) if (e is IBeatReceiver) ({var _:IBeatReceiver = cast e;_;}).stepHit(curStep);
}
public function measureHit(curMeasure:Int) {
- for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).measureHit(curMeasure);
+ for(e in members) if (e is IBeatReceiver) ({var _:IBeatReceiver = cast e;_;}).measureHit(curMeasure);
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/MusicBeatState.hx b/source/funkin/backend/MusicBeatState.hx
index e519ed0e..56dc1874 100644
--- a/source/funkin/backend/MusicBeatState.hx
+++ b/source/funkin/backend/MusicBeatState.hx
@@ -192,19 +192,19 @@ class MusicBeatState extends FlxState implements IBeatReceiver
@:dox(hide) public function stepHit(curStep:Int):Void
{
- for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).stepHit(curStep);
+ for(e in members) if (e != null && e is IBeatReceiver) ({var _:IBeatReceiver=cast e;_;}).stepHit(curStep);
call("stepHit", [curStep]);
}
@:dox(hide) public function beatHit(curBeat:Int):Void
{
- for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).beatHit(curBeat);
+ for(e in members) if (e != null && e is IBeatReceiver) ({var _:IBeatReceiver=cast e;_;}).beatHit(curBeat);
call("beatHit", [curBeat]);
}
@:dox(hide) public function measureHit(curMeasure:Int):Void
{
- for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).measureHit(curMeasure);
+ for(e in members) if (e != null && e is IBeatReceiver) ({var _:IBeatReceiver=cast e;_;}).measureHit(curMeasure);
call("measureHit", [curMeasure]);
}
@@ -278,8 +278,9 @@ class MusicBeatState extends FlxState implements IBeatReceiver
public override function resetSubState() {
super.resetSubState();
if (subState != null && subState is MusicBeatSubstate) {
- cast(subState, MusicBeatSubstate).parent = this;
- cast(subState, MusicBeatSubstate).onSubstateOpen();
+ var subState:MusicBeatSubstate = cast subState;
+ subState.parent = this;
+ subState.onSubstateOpen();
}
}
}
diff --git a/source/funkin/backend/MusicBeatSubstate.hx b/source/funkin/backend/MusicBeatSubstate.hx
index d8ee8076..b8d26038 100644
--- a/source/funkin/backend/MusicBeatSubstate.hx
+++ b/source/funkin/backend/MusicBeatSubstate.hx
@@ -181,19 +181,19 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
@:dox(hide) public function stepHit(curStep:Int):Void
{
- for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).stepHit(curStep);
+ for(e in members) if (e is IBeatReceiver) ({var _:IBeatReceiver=cast e;_;}).stepHit(curStep);
call("stepHit", [curStep]);
}
@:dox(hide) public function beatHit(curBeat:Int):Void
{
- for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).beatHit(curBeat);
+ for(e in members) if (e is IBeatReceiver) ({var _:IBeatReceiver=cast e;_;}).beatHit(curBeat);
call("beatHit", [curBeat]);
}
@:dox(hide) public function measureHit(curMeasure:Int):Void
{
- for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).measureHit(curMeasure);
+ for(e in members) if (e is IBeatReceiver) ({var _:IBeatReceiver=cast e;_;}).measureHit(curMeasure);
call("measureHit", [curMeasure]);
}
@@ -256,7 +256,8 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
public override function resetSubState() {
if (subState != null && subState is MusicBeatSubstate) {
- cast(subState, MusicBeatSubstate).parent = this;
+ var subState:MusicBeatSubstate = cast subState;
+ subState.parent = this;
super.resetSubState();
if (subState != null)
cast(subState, MusicBeatSubstate).onSubstateOpen();
diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx
index de52abe5..9ccec02a 100644
--- a/source/funkin/backend/assets/Paths.hx
+++ b/source/funkin/backend/assets/Paths.hx
@@ -124,6 +124,14 @@ class Paths
return getPath('data/characters/$character.xml', null);
}
+ /**
+ * Gets the name of a registered font.
+ * @param font The font's path (if it's already passed as a font name, the same name will be returned)
+ */
+ inline static public function getFontName(font:String) {
+ return OpenFlAssets.exists(font, FONT) ? OpenFlAssets.getFont(font).fontName : font;
+ }
+
public static inline function font(key:String) {
return getPath('fonts/$key');
}
diff --git a/source/funkin/backend/scripting/MultiThreadedScript.hx b/source/funkin/backend/scripting/MultiThreadedScript.hx
index 3ba51f52..24754c7c 100644
--- a/source/funkin/backend/scripting/MultiThreadedScript.hx
+++ b/source/funkin/backend/scripting/MultiThreadedScript.hx
@@ -33,8 +33,8 @@ class MultiThreadedScript implements IFlxDestroyable implements IHScriptCustomBe
if (parentScript != null) {
if (script is HScript && parentScript is HScript) {
- var hscript = cast(script, HScript);
- var parentHScript = cast(parentScript, HScript);
+ var hscript:HScript = cast script;
+ var parentHScript:HScript = cast parentScript;
hscript.interp.variables = parentHScript.interp.variables;
hscript.interp.publicVariables = parentHScript.interp.publicVariables;
diff --git a/source/funkin/backend/scripting/events/CharacterNodeEvent.hx b/source/funkin/backend/scripting/events/CharacterNodeEvent.hx
new file mode 100644
index 00000000..9577a64f
--- /dev/null
+++ b/source/funkin/backend/scripting/events/CharacterNodeEvent.hx
@@ -0,0 +1,21 @@
+package funkin.backend.scripting.events;
+
+import funkin.game.Character;
+import haxe.xml.Access;
+
+final class CharacterNodeEvent extends CancellableEvent {
+ /**
+ * The character instance
+ */
+ public var character:Character;
+
+ /**
+ * The node which is currently being parsed
+ */
+ public var node:Access;
+
+ /**
+ * The name of the node, quicker access than e.node.name
+ */
+ public var name:String;
+}
\ No newline at end of file
diff --git a/source/funkin/backend/scripting/events/CharacterXMLEvent.hx b/source/funkin/backend/scripting/events/CharacterXMLEvent.hx
new file mode 100644
index 00000000..e4f107fd
--- /dev/null
+++ b/source/funkin/backend/scripting/events/CharacterXMLEvent.hx
@@ -0,0 +1,16 @@
+package funkin.backend.scripting.events;
+
+import funkin.game.Character;
+import haxe.xml.Access;
+
+final class CharacterXMLEvent extends CancellableEvent {
+ /**
+ * The character instance
+ */
+ public var character:Character;
+
+ /**
+ * The xml
+ */
+ public var xml:Access;
+}
\ No newline at end of file
diff --git a/source/funkin/backend/system/Conductor.hx b/source/funkin/backend/system/Conductor.hx
index 8d4ef85a..f9758b94 100644
--- a/source/funkin/backend/system/Conductor.hx
+++ b/source/funkin/backend/system/Conductor.hx
@@ -181,7 +181,11 @@ class Conductor
private static var __updateMeasure:Bool;
private static function update() {
- if (FlxG.state != null && FlxG.state is MusicBeatState && cast(FlxG.state, MusicBeatState).cancelConductorUpdate) return;
+ if (FlxG.state != null && FlxG.state is MusicBeatState) {
+ var state:MusicBeatState = cast FlxG.state;
+ if(state.cancelConductorUpdate)
+ return;
+ }
__updateSongPos(FlxG.elapsed);
@@ -233,7 +237,7 @@ class Conductor
var state = FlxG.state;
while(state != null) {
if (state is IBeatReceiver && (state.subState == null || state.persistentUpdate)) {
- var st = cast(state, IBeatReceiver);
+ var st:IBeatReceiver = cast state;
if (curStep > oldStep) {
for(i in oldStep...curStep) {
st.stepHit(i+1);
diff --git a/source/funkin/backend/system/Logs.hx b/source/funkin/backend/system/Logs.hx
index 4f47c150..336f9443 100644
--- a/source/funkin/backend/system/Logs.hx
+++ b/source/funkin/backend/system/Logs.hx
@@ -11,8 +11,7 @@ class Logs {
public static var nativeTrace = Log.trace;
public static function init() {
-
- Log.trace = function(v : Dynamic, ?infos : Null) {
+ Log.trace = function(v:Dynamic, ?infos:Null) {
var data = [
logText('${infos.fileName}:${infos.lineNumber}: ', CYAN),
logText(Std.string(v))
@@ -28,22 +27,48 @@ class Logs {
__showInConsole(prepareColoredTrace(data, TRACE));
};
- LogFrontEnd.onLogs = function(Data, Style, FireOnce) {
+ LogFrontEnd.onLogs = function(data, style, fireOnce) {
var prefix = "[FLIXEL]";
var color:ConsoleColor = LIGHTGRAY;
var level:Level = INFO;
- if (Style == LogStyle.CONSOLE) {prefix = "> "; color = WHITE; level = INFO; } else
- if (Style == LogStyle.ERROR) {prefix = "[FLIXEL]"; color = RED; level = ERROR; } else
- if (Style == LogStyle.NORMAL) {prefix = "[FLIXEL]"; color = WHITE; level = INFO; } else
- if (Style == LogStyle.NOTICE) {prefix = "[FLIXEL]"; color = GREEN; level = VERBOSE;} else
- if (Style == LogStyle.WARNING) {prefix = "[FLIXEL]"; color = YELLOW; level = WARNING;}
+ if (style == LogStyle.CONSOLE) // cant place a switch here as these arent inline values - Nex
+ {
+ prefix = "> ";
+ color = WHITE;
+ level = INFO;
+ }
+ else if (style == LogStyle.ERROR)
+ {
+ prefix = "[FLIXEL]";
+ color = RED;
+ level = ERROR;
+ }
+ else if (style == LogStyle.NORMAL)
+ {
+ prefix = "[FLIXEL]";
+ color = WHITE;
+ level = INFO;
+ }
+ else if (style == LogStyle.NOTICE)
+ {
+ prefix = "[FLIXEL]";
+ color = GREEN;
+ level = VERBOSE;
+ }
+ else if (style == LogStyle.WARNING)
+ {
+ prefix = "[FLIXEL]";
+ color = YELLOW;
+ level = WARNING;
+ }
- var d:Dynamic = Data;
+ var d:Dynamic = data;
if (!(d is Array))
d = [d];
var a:Array = d;
var strs = [for(e in a) Std.string(e)];
- for(e in strs) {
+ for(e in strs)
+ {
Logs.trace('$prefix $e', level, color);
}
};
@@ -55,7 +80,8 @@ class Logs {
logText('[ '),
logText('${Std.string(time.getHours()).addZeros(2)}:${Std.string(time.getMinutes()).addZeros(2)}:${Std.string(time.getSeconds()).addZeros(2)}', DARKMAGENTA),
logText(' |'),
- switch(level) {
+ switch (level)
+ {
case WARNING: logText(' WARNING ', DARKYELLOW);
case ERROR: logText(' ERROR ', DARKRED);
case TRACE: logText(' TRACE ', GRAY);
@@ -79,7 +105,7 @@ class Logs {
}
public static function __showInConsole(text:Array) {
- #if sys
+ #if (sys && !mobile)
while(__showing) {
Sys.sleep(0.05);
}
@@ -91,15 +117,23 @@ class Logs {
NativeAPI.setConsoleColors();
Sys.print("\r\n");
__showing = false;
+ #elseif mobile
+ while(__showing) {
+ Sys.sleep(0.05);
+ }
+ __showing = true;
+ @:privateAccess
+ Sys.print([for(t in text) t.text].join(""));
+ __showing = false;
#else
@:privateAccess
nativeTrace([for(t in text) t.text].join(""));
#end
}
- public static function traceColored(text:Array, level:Level = INFO) {
+ public static function traceColored(text:Array, level:Level = INFO)
__showInConsole(prepareColoredTrace(text, level));
- }
+
public static function trace(text:String, level:Level = INFO, color:ConsoleColor = LIGHTGRAY, ?prefix:String) {
var text = [logText(text, color)];
if(prefix != null) text.insert(0, getPrefix(prefix));
@@ -144,7 +178,8 @@ enum abstract Level(Int) {
var SUCCESS = 5;
var FAILURE = 6;
}
+
typedef LogText = {
var text:String;
var color:ConsoleColor;
-}
\ No newline at end of file
+}
diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx
index 9f13e826..7744cc90 100644
--- a/source/funkin/backend/system/Main.hx
+++ b/source/funkin/backend/system/Main.hx
@@ -1,5 +1,6 @@
package funkin.backend.system;
+import funkin.backend.utils.translations.FormatUtil;
import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond;
import flixel.addons.transition.FlxTransitionableState;
import flixel.addons.transition.TransitionData;
@@ -58,6 +59,12 @@ class Main extends Sprite
public static var gameThreads:Array = [];
#end
+ public static function preInit() {
+ funkin.backend.utils.NativeAPI.registerAsDPICompatible();
+ funkin.backend.system.CommandLineHandler.parseCommandLine(Sys.args());
+ funkin.backend.system.Main.fixWorkingDirectory();
+ }
+
public function new()
{
super();
@@ -166,6 +173,8 @@ class Main extends Sprite
}
public static function refreshAssets() {
+ WindowUtils.resetTitle();
+
FlxSoundTray.volumeChangeSFX = Paths.sound('menu/volume');
FlxSoundTray.volumeUpChangeSFX = null;
FlxSoundTray.volumeDownChangeSFX = null;
diff --git a/source/funkin/backend/system/framerate/SystemInfo.hx b/source/funkin/backend/system/framerate/SystemInfo.hx
index 79580a21..192ad1df 100644
--- a/source/funkin/backend/system/framerate/SystemInfo.hx
+++ b/source/funkin/backend/system/framerate/SystemInfo.hx
@@ -91,7 +91,7 @@ class SystemInfo extends FramerateCategory {
#end
if(openfl.display3D.Context3D.__glMemoryTotalAvailable != -1) {
- var vRAMBytes:UInt = cast(flixel.FlxG.stage.context3D.gl.getParameter(openfl.display3D.Context3D.__glMemoryTotalAvailable), UInt);
+ var vRAMBytes:UInt = cast flixel.FlxG.stage.context3D.gl.getParameter(openfl.display3D.Context3D.__glMemoryTotalAvailable);
if (vRAMBytes == 1000 || vRAMBytes == 1 || vRAMBytes <= 0)
Logs.error('Unable to grab GPU VRAM');
else
diff --git a/source/funkin/backend/system/modules/AudioSwitchFix.hx b/source/funkin/backend/system/modules/AudioSwitchFix.hx
index 232c1eb8..86ebcb0a 100644
--- a/source/funkin/backend/system/modules/AudioSwitchFix.hx
+++ b/source/funkin/backend/system/modules/AudioSwitchFix.hx
@@ -62,6 +62,7 @@ class AudioSwitchFix {
}
}
+@:dox(hide)
typedef PlayingSound = {
var sound:FlxSound;
var time:Float;
diff --git a/source/funkin/backend/system/modules/CrashHandler.hx b/source/funkin/backend/system/modules/CrashHandler.hx
index 124b915c..1debede9 100644
--- a/source/funkin/backend/system/modules/CrashHandler.hx
+++ b/source/funkin/backend/system/modules/CrashHandler.hx
@@ -20,10 +20,10 @@ class CrashHandler {
public static function onUncaughtError(e:UncaughtErrorEvent) {
var m:String = e.error;
if (Std.isOfType(e.error, Error)) {
- var err = cast(e.error, Error);
+ var err:Error = cast e.error;
m = '${err.message}';
} else if (Std.isOfType(e.error, ErrorEvent)) {
- var err = cast(e.error, ErrorEvent);
+ var err:ErrorEvent = cast e.error;
m = '${err.text}';
}
var stack = CallStack.exceptionStack();
diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx
index 52a47dc2..df8bf94d 100644
--- a/source/funkin/backend/utils/CoolUtil.hx
+++ b/source/funkin/backend/utils/CoolUtil.hx
@@ -626,7 +626,7 @@ class CoolUtil
var fmt = new FlxTextFormat();
fmt.format.color = Reflect.hasField(fmtt, "color") ? FlxColor.fromString(fmtt.color) : text.color;
- fmt.format.font = Reflect.hasField(fmtt, "font") ? Paths.font(fmtt.font) : text.font;
+ fmt.format.font = Reflect.hasField(fmtt, "font") ? Paths.getFontName(Paths.font(fmtt.font)) : text.font;
fmt.format.size = Reflect.hasField(fmtt, "size") ? Std.parseInt(fmtt.size) : text.size;
fmt.format.italic = Reflect.hasField(fmtt, "italic") ? fmtt.italic == "true" : text.italic;
fmt.format.bold = Reflect.hasField(fmtt, "bold") ? fmtt.bold == "true" : text.bold;
@@ -751,7 +751,10 @@ class CoolUtil
*/
@:noUsing public static inline function openURL(url:String) {
#if linux
- Sys.command('/usr/bin/xdg-open', [url]);
+ // generally `xdg-open` should work in every distro
+ var cmd = Sys.command("xdg-open", [url]);
+ // run old command JUST IN CASE it fails, which it shouldn't
+ if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [url]);
#else
FlxG.openURL(url);
#end
diff --git a/source/funkin/backend/utils/MemoryUtil.hx b/source/funkin/backend/utils/MemoryUtil.hx
index 6c27629e..cd27cb0e 100644
--- a/source/funkin/backend/utils/MemoryUtil.hx
+++ b/source/funkin/backend/utils/MemoryUtil.hx
@@ -140,14 +140,16 @@ class MemoryUtil {
reg.match(process.stdout.readAll().toString());
if (process.exitCode() == 0) return reg.matched(1);
#elseif linux
- var process = new HiddenProcess("sudo", ["dmidecode", "--type", "17"]);
+ /*var process = new HiddenProcess("sudo", ["dmidecode", "--type", "17"]);
if (process.exitCode() != 0) return "Unknown";
var lines = process.stdout.readAll().toString().split("\n");
for (line in lines) {
if (line.startsWith("Type:")) {
return line.substring("Type:".length).trim();
}
- }
+ }*/
+ // TODO: sort of unsafe? also requires users to use `sudo`
+ // when launching the engine through the CLI, REIMPLEMENT LATER.
#end
return "Unknown";
}
diff --git a/source/funkin/backend/utils/NativeAPI.hx b/source/funkin/backend/utils/NativeAPI.hx
index 78f0b38d..45759231 100644
--- a/source/funkin/backend/utils/NativeAPI.hx
+++ b/source/funkin/backend/utils/NativeAPI.hx
@@ -113,8 +113,8 @@ class NativeAPI {
if(backgroundColor == NONE)
backgroundColor = BLACK;
- var fg = cast(foregroundColor, Int);
- var bg = cast(backgroundColor, Int);
+ var fg:Int = cast foregroundColor;
+ var bg:Int = cast backgroundColor;
Windows.setConsoleColors((bg * 16) + fg);
#elseif sys
Sys.print("\x1b[0m");
diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx
index 1a272757..5448ef11 100644
--- a/source/funkin/backend/utils/XMLUtil.hx
+++ b/source/funkin/backend/utils/XMLUtil.hx
@@ -6,6 +6,13 @@ import funkin.backend.FunkinSprite.XMLAnimType;
import funkin.backend.FunkinSprite;
import funkin.game.Character;
import funkin.backend.system.ErrorCode;
+import funkin.backend.FunkinSprite.XMLAnimType;
+import flixel.util.FlxColor;
+import haxe.xml.Access;
+import funkin.backend.scripting.Script;
+import funkin.backend.scripting.DummyScript;
+import funkin.backend.scripting.ScriptPack;
+import flixel.util.typeLimit.OneOfTwo;
import funkin.backend.system.interfaces.IOffsetCompatible;
import haxe.xml.Access;
@@ -252,7 +259,7 @@ class XMLUtil {
cast(sprite, IOffsetCompatible).addOffset(animData.name, animData.x, animData.y);
if (sprite is FunkinSprite) {
- var xmlSpr = cast(sprite, FunkinSprite);
+ var xmlSpr:FunkinSprite = cast sprite;
var name = animData.name;
switch(animData.animType) {
case BEAT:
@@ -328,6 +335,51 @@ class XMLUtil {
}
}
+class XMLImportedScriptInfo {
+ public var path:String;
+ public var shortLived:Bool = false;
+ public var loadBefore:Bool = true;
+ public var importStageSprites:Bool = false; // maybe will change later?? - Nex
+ public var parentScriptPack:ScriptPack = null;
+
+ public function new(path:String, parentScriptPack:ScriptPack) {
+ this.parentScriptPack = parentScriptPack;
+ this.path = path;
+ }
+
+ public function getScript():Script
+ return parentScriptPack == null ? null : parentScriptPack.getByPath(path);
+
+ public static function prepareInfos(node:Access, parentScriptPack:ScriptPack, ?onScriptPreLoad:XMLImportedScriptInfo->Void):XMLImportedScriptInfo {
+ if (!node.has.script || parentScriptPack == null) return null;
+
+ var folder = node.getAtt("folder").getDefault("data/scripts/");
+ if (!folder.endsWith("/")) folder += "/";
+
+ var path = Paths.script(folder + node.getAtt("script"));
+ var daScript = Script.create(path);
+ if (daScript is DummyScript) {
+ Logs.trace('Script Extension at ${path} does not exist.', ERROR);
+ return null;
+ }
+
+ var infos = new XMLImportedScriptInfo(daScript.path, parentScriptPack);
+ infos.shortLived = node.getAtt("isShortLived") == "true" || node.getAtt("shortLived") == "true";
+ infos.importStageSprites = node.getAtt("importStageSprites") == "true";
+ @:privateAccess infos.loadBefore = shouldLoadBefore(node);
+
+ if (onScriptPreLoad != null) onScriptPreLoad(infos);
+ parentScriptPack.add(daScript);
+ daScript.set("scriptInfo", infos);
+ daScript.load();
+
+ return infos;
+ }
+
+ @:dox(hide) public static inline function shouldLoadBefore(node:Access):Bool
+ return node.getAtt("loadBefore") != "false";
+}
+
typedef AnimData = {
var name:String;
var anim:String;
diff --git a/source/funkin/backend/utils/translations/FormatUtil.hx b/source/funkin/backend/utils/translations/FormatUtil.hx
index cb601fc7..300eef1e 100644
--- a/source/funkin/backend/utils/translations/FormatUtil.hx
+++ b/source/funkin/backend/utils/translations/FormatUtil.hx
@@ -5,7 +5,7 @@ package funkin.backend.utils.translations;
*
* For example if the parameter list is just an `Int` which is `9`, `You have been blue balled {0} times` becomes `You have been blue balled 9 times`.
*/
-class FormatUtil {
+final class FormatUtil {
private static var cache:Map = new Map();
private static var cacheStr:Map = new Map();
@@ -87,14 +87,17 @@ class ParamFormatInfo implements IFormatInfo {
public function format(params:Array):String {
if (params == null) params = [];
- var str:String = "";
- for (i=>s in strings) {
- str += s;
- if (i < indexes.length)
- str += params[indexes[i]];
- }
+ var lenStr = strings.length;
+ var lenInd = indexes.length;
- return str;
+ var arr = new haxe.ds.Vector(lenStr + ((lenStr < lenInd) ? lenStr : lenInd));
+ var i = 0;
+ for (idx=>s in strings) {
+ arr[i++] = s;
+ if (idx < lenInd)
+ arr[i++] = Std.string(params[indexes[idx]]);
+ }
+ return arr.join("");
}
public function toString():String {
diff --git a/source/funkin/editors/EditorPicker.hx b/source/funkin/editors/EditorPicker.hx
index c6db792c..5cdf4146 100644
--- a/source/funkin/editors/EditorPicker.hx
+++ b/source/funkin/editors/EditorPicker.hx
@@ -206,7 +206,7 @@ class EditorPickerOption extends FlxTypedSpriteGroup {
iconSpr.x = 25 + ((height - iconSpr.width) / 2);
iconSpr.y = (height - iconSpr.height) / 2;
- label = new Alphabet(25 + iconSpr.width + 25, 0, name, true);
+ label = new Alphabet(25 + iconSpr.width + 25, 0, name, "bold");
label.y = (height - label.height) / 2;
selectionBG = new FlxSprite().makeGraphic(1, 1, -1);
diff --git a/source/funkin/editors/character/CharacterSelection.hx b/source/funkin/editors/character/CharacterSelection.hx
index 4416a1ae..b7a83adb 100644
--- a/source/funkin/editors/character/CharacterSelection.hx
+++ b/source/funkin/editors/character/CharacterSelection.hx
@@ -4,6 +4,7 @@ import funkin.game.Character;
import funkin.options.OptionsScreen;
import funkin.options.type.IconOption;
import funkin.options.type.NewOption;
+import funkin.options.type.TextOption;
import funkin.options.type.OptionType;
class CharacterSelection extends EditorTreeMenu
@@ -16,22 +17,46 @@ class CharacterSelection extends EditorTreeMenu
bgType = "charter";
super.create();
- var modsList:Array = Character.getList(true);
+ var isMods:Bool = true;
+ var modsList:Array = Character.getList(true, true);
- var list:Array = [
- for (char in (modsList.length == 0 ? Character.getList(false) : modsList))
- new IconOption(char, translate("acceptCharacter"), Character.getIconFromCharName(char),
- function() {
- FlxG.switchState(new CharacterEditor(char));
- })
- ];
+ if(modsList.length == 0) {
+ modsList = Character.getList(false, true);
+ isMods = false;
+ }
- var newChar = translate("newCharacter");
- list.insert(0, new NewOption(newChar, newChar, function() {
- openSubState(new UIWarningSubstate(translate("warnings.notImplemented-title"), translate("warnings.notImplemented-body"), [
- {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}}
- ]));
- }));
+ function generateList(modsList:Array, isMods:Bool, folderPath:String = ""):Array {
+ var list:Array = [];
+
+ for(char in modsList) {
+ if(char.endsWith("/")) {
+ var folderName = CoolUtil.getFilename(char.substr(0, char.length-1));
+
+ list.push(new TextOption(folderName + " >", translate("acceptFolder"), function() {
+ var newModsList = Character.getList(isMods, true, char);
+ var newList:Array = generateList(newModsList, isMods, folderPath + folderName + "/");
+ optionsTree.add(new OptionsScreen(folderPath + folderName, translate("desc-folder", [folderPath + folderName + "/"]), newList));
+ }));
+ } else {
+ list.push(new IconOption(char, translate("acceptCharacter"), Character.getIconFromCharName(folderPath + char, char),
+ function() {
+ FlxG.switchState(new CharacterEditor(folderPath + char));
+ })
+ );
+ }
+ }
+
+ var newChar = translate("newCharacter");
+ list.insert(0, new NewOption(newChar, newChar, function() {
+ openSubState(new UIWarningSubstate(translate("warnings.notImplemented-title"), translate("warnings.notImplemented-body"), [
+ {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}}
+ ]));
+ }));
+
+ return list;
+ }
+
+ var list = generateList(modsList, isMods);
main = new OptionsScreen(TU.translate("characterEditor.name"), translate("desc"), list);
diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx
index e34818f1..be6d13a7 100644
--- a/source/funkin/editors/charter/Charter.hx
+++ b/source/funkin/editors/charter/Charter.hx
@@ -681,7 +681,8 @@ class Charter extends UIState {
}
for (group in [notesGroup, eventsGroup]) {
- cast(group, FlxTypedGroup).forEach(function(s) {
+ var group:FlxTypedGroup = cast group;
+ group.forEach(function(s) {
s.selected = false;
if (gridActionType == NONE) {
if (s is CharterNote) {
@@ -746,21 +747,27 @@ class Charter extends UIState {
selectionBox.bHeight = Std.int(Math.abs(mousePos.y - dragStartPos.y));
} else {
if (FlxG.keys.pressed.SHIFT) {
- for (group in [notesGroup, eventsGroup])
- for(n in cast(group, FlxTypedGroup))
+ for (group in [notesGroup, eventsGroup]) {
+ var group:FlxTypedGroup = cast group;
+ for(n in group)
if (n.handleSelection(selectionBox) && selection.contains(n))
selection.remove(n);
+ }
} else if (FlxG.keys.pressed.CONTROL) {
- for (group in [notesGroup, eventsGroup])
- for(n in cast(group, FlxTypedGroup))
+ for (group in [notesGroup, eventsGroup]) {
+ var group:FlxTypedGroup = cast group;
+ for(n in group)
if (n.handleSelection(selectionBox) && !selection.contains(n))
selection.push(n);
+ }
} else {
selection = [];
- for (group in [notesGroup, eventsGroup])
- for(n in cast(group, FlxTypedGroup))
+ for (group in [notesGroup, eventsGroup]) {
+ var group:FlxTypedGroup = cast group;
+ for(n in group)
if (n.handleSelection(selectionBox))
selection.push(n);
+ }
}
selection = __fixSelection(selection);
@@ -816,8 +823,14 @@ class Charter extends UIState {
changePoint.put();
}
- if (s is CharterNote) cast(s, CharterNote).snappedToStrumline = true;
- if (s is UISprite) cast(s, UISprite).cursor = BUTTON;
+ if (s is CharterNote) {
+ var s:CharterNote = cast s;
+ s.snappedToStrumline = true;
+ }
+ if (s is UISprite) {
+ var s:UISprite = cast s;
+ s.cursor = BUTTON;
+ }
}
if (!(verticalChange == 0 && horizontalChange == 0)) {
notesGroup.sortNotes(); eventsGroup.sortEvents();
@@ -1022,9 +1035,9 @@ class Charter extends UIState {
if (selection.length <= 0) return [];
notesGroup.autoSort = false;
- for (objects in [notesGroup, eventsGroup]) {
- var group = cast(objects, FlxTypedGroup);
- var member = 0;
+ for (group in [notesGroup, eventsGroup]) {
+ var group:FlxTypedGroup = cast group;
+ var member:Int = 0;
while(member < group.members.length) {
var s = group.members[member];
if (selection.contains(s))
@@ -1188,7 +1201,7 @@ class Charter extends UIState {
noteTypeText.x = noteTopButton.x + noteTopButton.bWidth + 6;
noteTypeText.y = Std.int((noteTopButton.bHeight - noteTypeText.height) / 2);
}
- noteTypeText.text = '($noteType) ${noteTypes[noteType-1] == null ? TU.translate("noteTypes.default") : noteTypes[noteType-1]}';
+ noteTypeText.text = '($noteType) ' + (noteTypes[noteType-1] == null ? TU.translate("charter.noteTypes.default") : noteTypes[noteType-1]);
super.update(elapsed);
@@ -1384,7 +1397,7 @@ class Charter extends UIState {
var note:CharterNote = cast s;
CNote(note.step - minStep, note.id, note.strumLineID, note.susLength, note.type);
} else if (s is CharterEvent) {
- var event = cast(s,CharterEvent);
+ var event:CharterEvent = cast s;
CEvent(event.step - minStep, [for (event in event.events) Reflect.copy(event)]);
}
];
@@ -1969,9 +1982,9 @@ typedef SelectionDragChange = {
public inline function loop(onNote:CharterNote->Void, ?onEvent:CharterEvent->Void, ?draggableOnly:Bool = true) {
for (s in this) {
if (s is CharterNote && onNote != null && (draggableOnly ? s.draggable: true))
- onNote(cast(s, CharterNote));
+ onNote(cast s);
else if (s is CharterEvent && onEvent != null && (draggableOnly ? s.draggable: true))
- onEvent(cast(s, CharterEvent));
+ onEvent(cast s);
}
}
}
diff --git a/source/funkin/editors/charter/CharterEvent.hx b/source/funkin/editors/charter/CharterEvent.hx
index af8996a4..1a0abe81 100644
--- a/source/funkin/editors/charter/CharterEvent.hx
+++ b/source/funkin/editors/charter/CharterEvent.hx
@@ -63,7 +63,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
generateDefaultIcon(event.name);
case "Camera Movement":
// custom icon for camera movement
- var state = cast(FlxG.state, Charter);
+ var state:Charter = cast FlxG.state;
if (event.params != null && event.params[0] != null && event.params[0] >= 0 && event.params[0] < state.strumLines.length) {
// camera movement, use health icon
var icon = Character.getIconFromCharName(state.strumLines.members[event.params[0]].strumLine.characters[0]);
diff --git a/source/funkin/editors/charter/CharterEventAdd.hx b/source/funkin/editors/charter/CharterEventAdd.hx
index cc4ce0bc..7fc74226 100644
--- a/source/funkin/editors/charter/CharterEventAdd.hx
+++ b/source/funkin/editors/charter/CharterEventAdd.hx
@@ -38,7 +38,7 @@ class CharterEventAdd extends UISliceSprite {
curCharterEvent = null;
this.step = step;
this.y = (step * 40) - (bHeight / 2);
- text.text = TU.translate("editors.addEvent");
+ text.text = TU.translate("charter.addEvent");
framesOffset = 0;
x = -(bWidth = 37 + Math.ceil(text.width));
}
@@ -46,7 +46,7 @@ class CharterEventAdd extends UISliceSprite {
public function updateEdit(event:CharterEvent) {
curCharterEvent = event;
this.y = event.y;
- text.text = TU.translate("editors.editEvent");
+ text.text = TU.translate("charter.editEvent");
framesOffset = 9;
x = -(bWidth = 27 + Math.ceil(text.width) + event.bWidth);
}
diff --git a/source/funkin/editors/charter/CharterEventScreen.hx b/source/funkin/editors/charter/CharterEventScreen.hx
index 2189e5af..522bab6a 100644
--- a/source/funkin/editors/charter/CharterEventScreen.hx
+++ b/source/funkin/editors/charter/CharterEventScreen.hx
@@ -174,7 +174,11 @@ class CharterEventScreen extends UISubstateWindow {
colorWheel;
case TDropDown(options):
addLabel();
- var dropdown = new UIDropDown(eventName.x, y, 320, 32, options, Std.int(Math.abs(options.indexOf(cast value))));
+ var optionIndex = options.indexOf(cast value);
+ if(optionIndex < 0) {
+ optionIndex = 0;
+ }
+ var dropdown = new UIDropDown(eventName.x, y, 320, 32, options, optionIndex);
paramsPanel.add(dropdown); paramsFields.push(dropdown);
dropdown;
case TCharacter:
diff --git a/source/funkin/editors/charter/CharterNoteGroup.hx b/source/funkin/editors/charter/CharterNoteGroup.hx
index 4c9a1d87..c2c2e388 100644
--- a/source/funkin/editors/charter/CharterNoteGroup.hx
+++ b/source/funkin/editors/charter/CharterNoteGroup.hx
@@ -33,7 +33,7 @@ class CharterNoteGroup extends FlxTypedGroup {
noteFunc(__loopSprite);
}
for(c in Charter.selection.copy())
- if (c != null && c is CharterNote) noteFunc(cast (c, CharterNote));
+ if (c != null && c is CharterNote) noteFunc(cast c);
__currentlyLooping = oldCur;
}
diff --git a/source/funkin/editors/charter/CharterNoteHoverer.hx b/source/funkin/editors/charter/CharterNoteHoverer.hx
index e20000a2..3ef2e543 100644
--- a/source/funkin/editors/charter/CharterNoteHoverer.hx
+++ b/source/funkin/editors/charter/CharterNoteHoverer.hx
@@ -47,7 +47,7 @@ class CharterNoteHoverer extends CharterNote {
for (s in Charter.selection) {
if (s != null && s.draggable && s is CharterNote) {
- var draggingNote:CharterNote = cast(s, CharterNote);
+ var draggingNote:CharterNote = cast s;
y = (draggingNote.step + verticalChange);
if (!FlxG.keys.pressed.SHIFT)
y -= ((draggingNote.step + verticalChange)
diff --git a/source/funkin/editors/ui/UIFileExplorer.hx b/source/funkin/editors/ui/UIFileExplorer.hx
index 7cc16320..a122dcec 100644
--- a/source/funkin/editors/ui/UIFileExplorer.hx
+++ b/source/funkin/editors/ui/UIFileExplorer.hx
@@ -55,8 +55,10 @@ class UIFileExplorer extends UISliceSprite {
if (uiElement != null) {
uiElement.alpha = alpha;
- if (uiElement is UIButton)
- cast(uiElement, UIButton).selectable = selectable;
+ if (uiElement is UIButton) {
+ var uiElement:UIButton = cast uiElement;
+ uiElement.selectable = selectable;
+ }
}
}
diff --git a/source/funkin/editors/ui/UISubstateWindow.hx b/source/funkin/editors/ui/UISubstateWindow.hx
index f99e184b..71550ef0 100644
--- a/source/funkin/editors/ui/UISubstateWindow.hx
+++ b/source/funkin/editors/ui/UISubstateWindow.hx
@@ -1,5 +1,8 @@
package funkin.editors.ui;
+import openfl.display.ShaderParameter;
+import openfl.display.ShaderInput;
+import openfl.filters.ShaderFilter;
import flixel.tweens.FlxTween;
import funkin.backend.shaders.CustomShader;
import openfl.filters.ShaderFilter;
@@ -7,7 +10,17 @@ import openfl.filters.ShaderFilter;
// TODO: make UIWarningSubstate extend this
class UISubstateWindow extends MusicBeatSubstate {
var camShaders:Array = [];
- var blurShader:CustomShader = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast");
+ var blurShader:CustomShader = {
+ var _ = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast");
+ if(!Options.intensiveBlur) {
+ var noiseTexture:ShaderInput = _.data.noiseTexture;
+ noiseTexture.input = Assets.getBitmapData("assets/shaders/noise256.png");
+ noiseTexture.wrap = REPEAT;
+ var noiseTextureSize:ShaderParameter = _.data.noiseTextureSize;
+ noiseTextureSize.value = [noiseTexture.input.width, noiseTexture.input.height];
+ }
+ _;
+ };
var titleSpr:UIText;
var messageSpr:UIText;
diff --git a/source/funkin/editors/ui/UITopMenu.hx b/source/funkin/editors/ui/UITopMenu.hx
index ae78469d..c4b0471d 100644
--- a/source/funkin/editors/ui/UITopMenu.hx
+++ b/source/funkin/editors/ui/UITopMenu.hx
@@ -21,9 +21,12 @@ class UITopMenu extends UISliceSprite {
public override function update(elapsed:Float) {
anyMenuOpened = false;
- for(c in members) if (cast(c, UITopMenuButton).curMenu.contextMenuOpened()) {
- anyMenuOpened = true;
- break;
+ for(c in members) {
+ var c:UITopMenuButton = cast c;
+ if (c.curMenu.contextMenuOpened()) {
+ anyMenuOpened = true;
+ break;
+ }
}
super.update(elapsed);
diff --git a/source/funkin/editors/ui/UIUtil.hx b/source/funkin/editors/ui/UIUtil.hx
index 7f9de548..ac445a05 100644
--- a/source/funkin/editors/ui/UIUtil.hx
+++ b/source/funkin/editors/ui/UIUtil.hx
@@ -5,7 +5,7 @@ import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
class UIUtil {
public static function follow(spr:FlxSprite, target:FlxSprite, x:Float = 0, y:Float = 0) {
- spr.cameras = target is UISprite ? cast(target, UISprite).__lastDrawCameras : target.cameras;
+ spr.cameras = target is UISprite ? ({var _:UISprite = cast target;_;}).__lastDrawCameras : target.cameras;
spr.setPosition(target.x + x, target.y + y);
spr.scrollFactor.set(target.scrollFactor.x, target.scrollFactor.y);
}
diff --git a/source/funkin/editors/ui/UIWarningSubstate.hx b/source/funkin/editors/ui/UIWarningSubstate.hx
index 84edf922..4f4a0380 100644
--- a/source/funkin/editors/ui/UIWarningSubstate.hx
+++ b/source/funkin/editors/ui/UIWarningSubstate.hx
@@ -1,5 +1,8 @@
package funkin.editors.ui;
+import openfl.display.ShaderParameter;
+import openfl.display.ShaderInput;
+import openfl.filters.ShaderFilter;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import funkin.backend.shaders.CustomShader;
@@ -7,7 +10,17 @@ import openfl.filters.ShaderFilter;
class UIWarningSubstate extends MusicBeatSubstate {
var camShaders:Array = [];
- var blurShader:CustomShader = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast");
+ var blurShader:CustomShader = {
+ var _ = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast");
+ if(!Options.intensiveBlur) {
+ var noiseTexture:ShaderInput = _.data.noiseTexture;
+ noiseTexture.input = Assets.getBitmapData("assets/shaders/noise256.png");
+ noiseTexture.wrap = REPEAT;
+ var noiseTextureSize:ShaderParameter = _.data.noiseTextureSize;
+ noiseTextureSize.value = [noiseTexture.input.width, noiseTexture.input.height];
+ }
+ _;
+ };
var title:String;
var message:String;
diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx
index 51fb7724..a7e93933 100644
--- a/source/funkin/game/Character.hx
+++ b/source/funkin/game/Character.hx
@@ -8,6 +8,9 @@ import flixel.util.FlxColor;
import funkin.backend.FunkinSprite;
import funkin.backend.scripting.DummyScript;
import funkin.backend.scripting.Script;
+import funkin.backend.scripting.ScriptPack;
+import funkin.backend.scripting.events.CharacterNodeEvent;
+import funkin.backend.scripting.events.CharacterXMLEvent;
import funkin.backend.scripting.events.DanceEvent;
import funkin.backend.scripting.events.DirectionAnimEvent;
import funkin.backend.scripting.events.PlayAnimContext;
@@ -44,8 +47,13 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public var cameraOffset:FlxPoint = FlxPoint.get(0, 0);
public var globalOffset:FlxPoint = FlxPoint.get(0, 0);
- public var script:Script;
public var xml:Access;
+ public var scripts:ScriptPack;
+ public var xmlImportedScripts:Array = [];
+ public var script(default, set):Script;
+
+ public function prepareInfos(node:Access)
+ return XMLImportedScriptInfo.prepareInfos(node, scripts, (infos) -> xmlImportedScripts.push(infos));
public var idleSuffix:String = "";
public var stunned(default, set):Bool = false;
@@ -72,16 +80,15 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
script = Script.create(Paths.script(Path.withoutExtension(Paths.xml('characters/$curCharacter')), null, true));
else
script = new DummyScript(curCharacter);
- script.setParent(this);
script.load();
buildCharacter(xml);
- script.call("create");
+ scripts.call("create");
if (script == null)
script = new DummyScript(curCharacter);
- script.call("postCreate");
+ scripts.call("postCreate");
}
@:noCompletion var __swappedLeftRightAnims:Bool = false;
@@ -118,7 +125,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
override function update(elapsed:Float) {
super.update(elapsed);
- script.call("update", [elapsed]);
+ scripts.call("update", [elapsed]);
if (stunned) {
__stunnedTime += elapsed;
if (__stunnedTime > 5 / 60)
@@ -137,7 +144,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if(debugMode) return;
var event = EventManager.get(DanceEvent).recycle(danced);
- script.call("onDance", [event]);
+ scripts.call("onDance", [event]);
if (event.cancelled) return;
if (isDanceLeftDanceRight)
@@ -167,14 +174,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
*/
public var danceOnBeat:Bool = true;
public override function beatHit(curBeat:Int) {
- script.call("beatHit", [curBeat]);
+ scripts.call("beatHit", [curBeat]);
if (danceOnBeat && (curBeat + beatOffset) % beatInterval == 0 && !__lockAnimThisFrame)
tryDance();
}
public override function stepHit(curStep:Int)
- script.call("stepHit", [curStep]);
+ scripts.call("stepHit", [curStep]);
@:noCompletion var __reverseDrawProcedure:Bool = false;
public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect {
@@ -237,7 +244,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public override function playAnim(AnimName:String, ?Force:Bool, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) {
var event = EventManager.get(PlayAnimEvent).recycle(AnimName, Force, Reversed, Frame, Context);
- script.call("onPlayAnim", [event]);
+ scripts.call("onPlayAnim", [event]);
if (event.cancelled) return;
super.playAnim(event.animName, event.force, event.context, event.reverse, event.startingFrame);
@@ -252,16 +259,16 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
var event = EventManager.get(PointEvent).recycle(
midpoint.x + (isPlayer ? -100 : 150) + globalOffset.x + cameraOffset.x,
midpoint.y - 100 + globalOffset.y + cameraOffset.y);
- script.call("onGetCamPos", [event]);
+ scripts.call("onGetCamPos", [event]);
midpoint.put();
return new FlxPoint(event.x, event.y);
}
public override function destroy() {
- if(script != null) {
- script.call('destroy');
- script.destroy();
+ if(scripts != null) {
+ scripts.call('destroy');
+ scripts.destroy();
}
super.destroy();
@@ -304,9 +311,18 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
}
public inline function buildCharacter(xml:Access) {
- this.xml = xml; // Modders wassup :D
+ for(node in xml.elements)
+ switch(node.name) {
+ case "use-extension" | "extension" | "ext":
+ if (!XMLImportedScriptInfo.shouldLoadBefore(node)) continue;
+ prepareInfos(node);
+ }
+
+ xml = scripts.event("onCharacterXMLParsed", EventManager.get(CharacterXMLEvent).recycle(this, xml)).xml;
+
sprite = curCharacter;
spriteAnimType = BEAT;
+ this.xml = xml; // Modders wassup :D
if (xml.x.exists("isPlayer")) playerOffsets = (xml.x.get("isPlayer") == "true");
if (xml.x.exists("x")) globalOffset.x = Std.parseFloat(xml.x.get("x"));
@@ -314,7 +330,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if (xml.x.exists("gameOverChar")) gameOverCharacter = xml.x.get("gameOverChar");
if (xml.x.exists("camx")) cameraOffset.x = Std.parseFloat(xml.x.get("camx"));
if (xml.x.exists("camy")) cameraOffset.y = Std.parseFloat(xml.x.get("camy"));
- if (xml.x.exists("holdTime")) holdTime = CoolUtil.getDefault(Std.parseFloat(xml.x.get("holdTime")), 4);
+ if (xml.x.exists("holdTime")) holdTime = Std.parseFloat(xml.x.get("holdTime")).getDefaultFloat(4);
if (xml.x.exists("flipX")) flipX = (xml.x.get("flipX") == "true");
if (xml.x.exists("icon")) icon = xml.x.get("icon");
if (xml.x.exists("color")) iconColor = FlxColor.fromString(xml.x.get("color"));
@@ -330,9 +346,19 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if (hasInterval) beatInterval = Std.parseInt(xml.x.get("interval"));
loadSprite(Paths.image('characters/$sprite'));
+ for(node in xml.elements) {
+ switch(node.name) {
+ case "anim":
+ XMLUtil.addXMLAnimation(this, node);
+ case "use-extension" | "extension" | "ext":
+ if (XMLImportedScriptInfo.shouldLoadBefore(node)) continue;
+ prepareInfos(node);
+ default:
+ // nothing
+ }
- for (anim in xml.nodes.anim)
- XMLUtil.addXMLAnimation(this, anim);
+ scripts.event("onCharacterNodeParsed", EventManager.get(CharacterNodeEvent).recycle(this, node, node.name));
+ }
for (attribute in xml.x.attributes())
if (!characterProperties.contains(attribute))
@@ -340,6 +366,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
fixChar(__switchAnims, !hasInterval);
dance();
+
+ for (info in xmlImportedScripts) if (info.shortLived) {
+ var script = info.getScript();
+ if (script == null) continue;
+
+ scripts.remove(script);
+ script.destroy();
+ }
}
public static var characterProperties:Array = [
@@ -423,6 +457,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
return stunned = b;
}
+ // ---- Backwards compat ----
// Interval at which the character will dance (higher number = slower dance)
@:noCompletion public var danceInterval(get, set):Int;
@:noCompletion private function set_danceInterval(v:Int)
@@ -430,13 +465,29 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
@:noCompletion private function get_danceInterval()
return beatInterval;
+ private function set_script(script:Script):Script {
+ if (scripts == null) (scripts = new ScriptPack("Character")).setParent(this);
+
+ var lastIndex = scripts.scripts.indexOf(this.script);
+ if(lastIndex >= 0) {
+ if(script == null) // last != null && new == null
+ scripts.scripts.splice(lastIndex, 1);
+ else // last != null && new != null
+ scripts.scripts[lastIndex] = script;
+ } else if(script != null) // last == null
+ scripts.insert(0, script);
+
+ return this.script = script;
+ }
+ // ---- end of Backwards compat ----
+
public static var FALLBACK_CHARACTER:String = "bf";
public static var FALLBACK_DEAD_CHARACTER:String = "bf-dead";
public static function getXMLFromCharName(character:OneOfTwo):Access {
var char:Character = null;
if (character is Character) {
- char = cast(character, Character);
+ char = cast character;
character = char.curCharacter;
}
@@ -468,9 +519,10 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
return xml;
}
- public static function getIconFromCharName(?character:String) {
+ public static function getIconFromCharName(?character:String, ?defaultIcon:String = null) {
if(character == null) return "face";
- var icon:String = character;
+ if(defaultIcon == null) defaultIcon = character;
+ var icon:String = defaultIcon;
var xml:Access = getXMLFromCharName(character);
if (xml != null && xml.x.exists("icon")) icon = xml.x.get("icon");
@@ -478,9 +530,17 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
return icon;
}
- public static function getList(?mods:Bool = false):Array
- return [
- for (path in Paths.getFolderContent('data/characters/', true, mods ? MODS : BOTH))
- if (Path.extension(path) == "xml") CoolUtil.getFilename(path)
- ];
+ public static function getList(?mods:Bool = false, includeFolders:Bool = false, folder:String = 'data/characters/'):Array {
+ var list:Array = [];
+ if(includeFolders) {
+ for (path in Paths.getFolderDirectories(folder, true, mods ? MODS : BOTH)) {
+ if(!path.endsWith("/")) path += "/";
+ list.push(path);
+ }
+ }
+ for (path in Paths.getFolderContent(folder, true, mods ? MODS : BOTH))
+ if (Path.extension(path) == "xml")
+ list.push(CoolUtil.getFilename(path));
+ return list;
+ }
}
\ No newline at end of file
diff --git a/source/funkin/game/HealthIcon.hx b/source/funkin/game/HealthIcon.hx
index 1c7bb59d..07feba62 100644
--- a/source/funkin/game/HealthIcon.hx
+++ b/source/funkin/game/HealthIcon.hx
@@ -90,11 +90,9 @@ class HealthIcon extends FlxSprite
if (animation.curAnim != null) {
var i:Int = -1;
var oldKey:Int = -1;
- for(k=>icon in healthSteps) {
- if (k > oldKey && k < health * 100) {
- oldKey = k;
- i = icon;
- }
+ for (k=>icon in healthSteps) if (k > oldKey && k <= health * 100) {
+ oldKey = k;
+ i = icon;
}
if (i >= 0 && curAnimState != i) {
var event = EventManager.get(funkin.backend.scripting.events.HealthIconChangeEvent).recycle(i, this);
diff --git a/source/funkin/game/Note.hx b/source/funkin/game/Note.hx
index 21e70a6e..60aab276 100644
--- a/source/funkin/game/Note.hx
+++ b/source/funkin/game/Note.hx
@@ -213,7 +213,7 @@ class Note extends FlxSprite
public var strumRelativePos:Bool = true;
override function drawComplex(camera:FlxCamera) {
- var downscrollCam = (camera is HudCamera ? cast(camera, HudCamera).downscroll : false);
+ var downscrollCam = (camera is HudCamera ? ({var _:HudCamera=cast camera;_;}).downscroll : false);
flipY = (isSustainNote && flipSustain) && (downscrollCam != (__strum != null && __strum.getScrollSpeed(this) < 0));
if (downscrollCam) {
frameOffset.y += __notePosFrameOffset.y * 2;
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index 5599267d..555fba89 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -160,6 +160,11 @@ class PlayState extends MusicBeatState
* Boyfriend character
*/
public var boyfriend(get, set):Character;
+ /**
+ * Boyfriend character
+ * Same as boyfriend, just shorter
+ **/
+ public var bf(get, set):Character;
/**
* Strum line position
@@ -868,12 +873,12 @@ class PlayState extends MusicBeatState
Conductor.songPosition = 0;
Conductor.songPosition -= Conductor.crochet * introLength - Conductor.songOffset;
- var swagCounter:Int = 0;
-
- startTimer = new FlxTimer().start(Conductor.crochet / 1000, function(tmr:FlxTimer)
- {
- countdown(swagCounter++);
- }, introLength);
+ if(introLength > 0) {
+ var swagCounter:Int = 0;
+ startTimer = new FlxTimer().start(Conductor.crochet / 1000, (tmr:FlxTimer) -> {
+ countdown(swagCounter++);
+ }, introLength);
+ }
scripts.call("onPostStartCountdown");
}
@@ -1050,7 +1055,7 @@ class PlayState extends MusicBeatState
vocals.pause();
}
- if (!startTimer.finished)
+ if (startTimer != null && !startTimer.finished)
startTimer.active = false;
}
@@ -1070,7 +1075,7 @@ class PlayState extends MusicBeatState
resyncVocals();
}
- if (!startTimer.finished)
+ if (startTimer != null && !startTimer.finished)
startTimer.active = true;
paused = false;
@@ -1858,6 +1863,16 @@ class PlayState extends MusicBeatState
strumLines.members[1].characters = [bf];
return bf;
}
+ private function set_bf(bf:Character):Character {
+ if (strumLines != null && strumLines.members[1] != null)
+ strumLines.members[1].characters = [bf];
+ return bf;
+ }
+ private function get_bf():Character {
+ if (strumLines != null && strumLines.members[1] != null)
+ return strumLines.members[1].characters[0];
+ return null;
+ }
private function get_dad():Character {
if (strumLines != null && strumLines.members[0] != null)
return strumLines.members[0].characters[0];
diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx
index 54b741f3..4ab56a26 100644
--- a/source/funkin/game/Stage.hx
+++ b/source/funkin/game/Stage.hx
@@ -2,6 +2,9 @@ package funkin.game;
import flixel.FlxState;
import flixel.math.FlxPoint;
+import haxe.xml.Access;
+import funkin.backend.utils.XMLUtil.XMLImportedScriptInfo;
+import funkin.backend.system.interfaces.IBeatReceiver;
import funkin.backend.scripting.DummyScript;
import funkin.backend.scripting.Script;
import funkin.backend.scripting.events.StageNodeEvent;
@@ -30,6 +33,9 @@ class Stage extends FlxBasic implements IBeatReceiver {
public function setStagesSprites(script:Script)
for (k=>e in stageSprites) script.set(k, e);
+ public function prepareInfos(node:Access)
+ return XMLImportedScriptInfo.prepareInfos(node, PlayState.instance.scripts, (infos) -> xmlImportedScripts.push(infos));
+
public function new(stage:String, ?state:FlxState) {
super();
@@ -136,7 +142,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
PlayState.instance.add(PlayState.instance.comboGroup);
PlayState.instance.comboGroup;
case "use-extension" | "extension" | "ext":
- if (__shouldLoadBefore(node) || XMLImportedScriptInfo.prepareInfos(node, this) == null) continue;
+ if (XMLImportedScriptInfo.shouldLoadBefore(node) || prepareInfos(node) == null) continue;
null;
default: null;
}
@@ -201,13 +207,10 @@ class Stage extends FlxBasic implements IBeatReceiver {
@:dox(hide) private function __pushNcheckNode(array:Array, node:Access) {
array.push(node);
- if ((node.name == "use-extension" || node.name == "extension" || node.name == "ext") && __shouldLoadBefore(node))
- XMLImportedScriptInfo.prepareInfos(node, this);
+ if ((node.name == "use-extension" || node.name == "extension" || node.name == "ext") && XMLImportedScriptInfo.shouldLoadBefore(node))
+ prepareInfos(node);
}
- @:dox(hide) private static inline function __shouldLoadBefore(node:Access):Bool
- return node.getAtt("loadBefore") != "false";
-
public function addCharPos(name:String, node:Access, ?nonXMLInfo:StageCharPosInfo):StageCharPos {
var charPos = new StageCharPos();
charPos.visible = charPos.active = false;
@@ -317,45 +320,4 @@ typedef StageCharPosInfo = {
var y:Float;
var flip:Bool;
var scroll:Float;
-}
-
-class XMLImportedScriptInfo {
- public var path:String;
- public var shortLived:Bool = false;
- public var loadBefore:Bool = true;
- public var importStageSprites:Bool = false;
-
- public function new(path:String)
- this.path = path;
-
- public function getScript():Script
- return PlayState.instance == null ? null : PlayState.instance.scripts.getByPath(path);
-
- public static function prepareInfos(node:Access, ?stage:Stage):XMLImportedScriptInfo {
- if (!node.has.script || PlayState.instance == null) return null;
-
- var folder = node.getAtt("folder").getDefault("data/scripts/");
- if (!folder.endsWith("/")) folder += "/";
-
- var path = Paths.script(folder + node.getAtt("script"));
- var daScript = Script.create(path);
- if (daScript is DummyScript) {
- var msg = 'Script Extension at ${path} does not exist';
- if (stage != null) msg += ' through stage "${stage.stageName}"';
- Logs.trace(msg + ".", ERROR);
- return null;
- }
-
- var infos = new XMLImportedScriptInfo(daScript.path);
- infos.shortLived = node.getAtt("isShortLived") == "true";
- infos.importStageSprites = node.getAtt("importStageSprites") == "true";
- @:privateAccess infos.loadBefore = Stage.__shouldLoadBefore(node);
-
- if (stage != null) stage.xmlImportedScripts.push(infos);
- PlayState.instance.scripts.add(daScript);
- daScript.set("scriptInfo", infos);
- daScript.load();
-
- return infos;
- }
}
\ No newline at end of file
diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx
index cca45b51..83d9c855 100644
--- a/source/funkin/game/StrumLine.hx
+++ b/source/funkin/game/StrumLine.hx
@@ -318,7 +318,7 @@ class StrumLine extends FlxTypedGroup {
babyArrow.scrollSpeed = data.scrollSpeed;
var event = EventManager.get(StrumCreationEvent).recycle(babyArrow, PlayState.instance.strumLines.members.indexOf(this), i, animPrefix);
- event.__doAnimation = !MusicBeatState.skipTransIn;
+ event.__doAnimation = !MusicBeatState.skipTransIn && (PlayState.instance != null ? PlayState.instance.introLength > 0 : true);
event = PlayState.instance.scripts.event("onStrumCreation", event);
if (!event.cancelled) {
diff --git a/source/funkin/game/cutscenes/DialogueCutscene.hx b/source/funkin/game/cutscenes/DialogueCutscene.hx
index 7d43e951..6f850007 100644
--- a/source/funkin/game/cutscenes/DialogueCutscene.hx
+++ b/source/funkin/game/cutscenes/DialogueCutscene.hx
@@ -91,7 +91,7 @@ class DialogueCutscene extends Cutscene {
musicVolume: node.has.musicVolume ? (volume = Std.parseFloat(node.att.speed).getDefaultFloat(0.8)) : null,
changeMusic: node.has.changeMusic ? FlxG.sound.load(Paths.music(node.att.changeMusic), volume, true) : null,
playSound: node.has.playSound ? FlxG.sound.load(Paths.sound(node.att.playSound)) : null,
- nextSound: node.has.nextSound ? FlxG.sound.load(Paths.music(node.att.nextSound)) : null,
+ nextSound: node.has.nextSound ? FlxG.sound.load(Paths.sound(node.att.nextSound)) : null,
textSound: null
};
@@ -217,4 +217,4 @@ typedef DialogueLine = {
var nextSound:FlxSound;
var textSound:FlxSound;
var changeDefAnim:String;
-}
\ No newline at end of file
+}
diff --git a/source/funkin/menus/BetaWarningState.hx b/source/funkin/menus/BetaWarningState.hx
index fcfae487..68147a00 100644
--- a/source/funkin/menus/BetaWarningState.hx
+++ b/source/funkin/menus/BetaWarningState.hx
@@ -13,13 +13,13 @@ class BetaWarningState extends MusicBeatState {
public override function create() {
super.create();
- titleAlphabet = new Alphabet(0, 0, TU.translate("betaWarning.title"), true);
+ titleAlphabet = new Alphabet(0, 0, TU.translate("betaWarning.title"), "bold");
titleAlphabet.screenCenter(X);
add(titleAlphabet);
disclaimer = new FunkinText(16, titleAlphabet.y + titleAlphabet.height + 10, FlxG.width - 32, "", 32);
disclaimer.alignment = CENTER;
- disclaimer.applyMarkup(TU.translate("betaWarning.desc", [Main.releaseCycle, "ENTER"]),
+ disclaimer.applyMarkup(TU.translate("betaWarning.desc", [Main.releaseCycle]) + "\n\n" + TU.translate("betaWarning.proceed", ["ENTER"]),
[
new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFFF4444), "*")
]
diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx
index c055a405..bee0eef7 100644
--- a/source/funkin/menus/FreeplayState.hx
+++ b/source/funkin/menus/FreeplayState.hx
@@ -130,7 +130,7 @@ class FreeplayState extends MusicBeatState
for (i in 0...songs.length)
{
- var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i].displayName, true, false);
+ var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i].displayName, "bold");
songText.isMenuItem = true;
songText.targetY = i;
grpSongs.add(songText);
diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx
index 509a9d82..802b8e46 100644
--- a/source/funkin/menus/MainMenuState.hx
+++ b/source/funkin/menus/MainMenuState.hx
@@ -163,7 +163,7 @@ class MainMenuState extends MusicBeatState
var event = event("onSelectItem", EventManager.get(NameEvent).recycle(daChoice));
if (event.cancelled) return;
- switch (daChoice)
+ switch (event.name)
{
case 'story mode': FlxG.switchState(new StoryMenuState());
case 'freeplay': FlxG.switchState(new FreeplayState());
diff --git a/source/funkin/menus/ModSwitchMenu.hx b/source/funkin/menus/ModSwitchMenu.hx
index 79277ff3..309afdad 100644
--- a/source/funkin/menus/ModSwitchMenu.hx
+++ b/source/funkin/menus/ModSwitchMenu.hx
@@ -28,7 +28,7 @@ class ModSwitchMenu extends MusicBeatSubstate {
alphabets = new FlxTypedGroup();
for(mod in mods) {
- var a = new Alphabet(0, 0, mod == null ? TU.translate("mods.disableMods") : mod, true);
+ var a = new Alphabet(0, 0, mod == null ? TU.translate("mods.disableMods") : mod, "bold");
if(mod == ModsFolder.currentModFolder)
a.color = FlxColor.LIME;
a.isMenuItem = true;
diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx
index fad1a15b..1f4008a0 100644
--- a/source/funkin/menus/PauseSubState.hx
+++ b/source/funkin/menus/PauseSubState.hx
@@ -14,6 +14,7 @@ import funkin.editors.charter.Charter;
import funkin.menus.StoryMenuState;
import funkin.options.OptionsMenu;
import funkin.options.keybinds.KeybindsOptions;
+import funkin.options.TreeMenu;
class PauseSubState extends MusicBeatSubstate
{
@@ -107,7 +108,7 @@ class PauseSubState extends MusicBeatSubstate
for (i in 0...menuItems.length)
{
var pauseId = "pause." + TU.raw2Id(menuItems[i]);
- var songText:Alphabet = new Alphabet(0, (70 * i) + 30, TU.translate(pauseId), true, false);
+ var songText:Alphabet = new Alphabet(0, (70 * i) + 30, TU.translate(pauseId), "bold");
songText.isMenuItem = true;
songText.targetY = i;
grpMenuShit.add(songText);
@@ -167,9 +168,10 @@ class PauseSubState extends MusicBeatSubstate
persistentDraw = false;
openSubState(new KeybindsOptions());
case "Change Options":
+ TreeMenu.lastState = PlayState;
FlxG.switchState(new OptionsMenu());
case "Exit to charter":
- FlxG.switchState(new funkin.editors.charter.Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
+ FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
case "Exit to menu":
if (PlayState.chartingMode && Charter.undos.unsaved)
game.saveWarn(false);
@@ -217,4 +219,4 @@ class PauseSubState extends MusicBeatSubstate
item.alpha = (item.targetY == 0) ? 1 : 0.6;
}
}
-}
\ No newline at end of file
+}
diff --git a/source/funkin/menus/PlaytestingWarningSubstate.hx b/source/funkin/menus/PlaytestingWarningSubstate.hx
index 1bd5a00f..de455d83 100644
--- a/source/funkin/menus/PlaytestingWarningSubstate.hx
+++ b/source/funkin/menus/PlaytestingWarningSubstate.hx
@@ -40,7 +40,7 @@ class PlaytestingWarningSubstate extends MusicBeatSubstate
bg.scrollFactor.set();
add(bg);
- titleAlphabet = new Alphabet(0, 140, TU.translate("playtesting.warningTitle"), true);
+ titleAlphabet = new Alphabet(0, 140, TU.translate("playtesting.warningTitle"), "bold");
titleAlphabet.screenCenter(X);
add(titleAlphabet);
diff --git a/source/funkin/menus/StoryMenuState.hx b/source/funkin/menus/StoryMenuState.hx
index b67c3cfc..56a708f5 100644
--- a/source/funkin/menus/StoryMenuState.hx
+++ b/source/funkin/menus/StoryMenuState.hx
@@ -114,6 +114,12 @@ class StoryMenuState extends MusicBeatState {
}
}
+ // default difficulty should be the middle difficulty in the array
+ // to be consistent with base game and whatnot, you know the drill
+ curDifficulty = Math.floor(weeks[0].difficulties.length * 0.5);
+ // debug stuff lol
+ Logs.trace('Middle Difficulty for Week 1 is ${weeks[0].difficulties[curDifficulty]} (ID: $curDifficulty)');
+
changeWeek(0, true);
DiscordUtil.call("onMenuLoaded", ["Story Menu"]);
@@ -163,6 +169,7 @@ class StoryMenuState extends MusicBeatState {
if (!force) CoolUtil.playMenuSFX();
for(k=>e in weekSprites.members) {
e.targetY = k - curWeek;
+ e.alpha = k == curWeek ? 1.0 : 0.6;
}
var str = new StringBuf();
str.add(TU.translate("story.tracks") + "\n\n");
@@ -354,13 +361,13 @@ class StoryMenuState extends MusicBeatState {
if (char.animation.exists("confirm"))
char.animation.play("confirm");
- PlayState.loadWeek(weeks[curWeek], weeks[curWeek].difficulties[curDifficulty]);
+ PlayState.loadWeek(event.week, event.difficulty);
new FlxTimer().start(1, function(tmr:FlxTimer)
{
FlxG.switchState(new PlayState());
});
- weekSprites.members[curWeek].startFlashing();
+ weekSprites.members[event.weekID].startFlashing();
}
}
diff --git a/source/funkin/menus/TitleState.hx b/source/funkin/menus/TitleState.hx
index b74a8db4..ab190b62 100644
--- a/source/funkin/menus/TitleState.hx
+++ b/source/funkin/menus/TitleState.hx
@@ -25,8 +25,6 @@ class TitleState extends MusicBeatState
public var textGroup:FlxGroup;
public var ngSpr:FlxSprite;
- public var wackyImage:FlxSprite;
-
override public function create():Void
{
curWacky = FlxG.random.getObject(getIntroTextShit());
@@ -40,9 +38,6 @@ class TitleState extends MusicBeatState
DiscordUtil.call("onMenuLoaded", ["Title Screen"]);
}
- var logoBl:FlxSprite;
- var gfDance:FlxSprite;
- var danceLeft:Bool = false;
var titleText:FlxSprite;
var titleScreenSprites:MusicBeatGroup;
@@ -56,11 +51,9 @@ class TitleState extends MusicBeatState
var bg:FlxSprite = new FlxSprite().makeSolid(FlxG.width, FlxG.height, FlxColor.BLACK);
add(bg);
- #if TITLESCREEN_XML
titleScreenSprites = new MusicBeatGroup();
add(titleScreenSprites);
loadXML();
- #end
if (titleText == null) {
titleText = new FlxSprite(100, FlxG.height * 0.8);
@@ -78,16 +71,6 @@ class TitleState extends MusicBeatState
blackScreen = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
add(blackScreen);
- #if !TITLESCREEN_XML
- ngSpr = new FlxSprite(0, FlxG.height * 0.52).loadAnimatedGraphic(Paths.image('newgrounds_logo'));
- add(ngSpr);
- ngSpr.visible = false;
- ngSpr.setGraphicSize(Std.int(ngSpr.width * 0.8));
- ngSpr.updateHitbox();
- ngSpr.screenCenter(X);
- ngSpr.antialiasing = true;
- #end
-
FlxG.mouse.visible = false;
if (initialized)
@@ -192,7 +175,7 @@ class TitleState extends MusicBeatState
for (i=>text in textArray)
{
if (text == "" || text == null) continue;
- var money:Alphabet = new Alphabet(0, (i * 60) + 200, text, true, false);
+ var money:Alphabet = new Alphabet(0, (i * 60) + 200, text, "bold");
money.screenCenter(X);
textGroup.add(money);
}
@@ -200,7 +183,7 @@ class TitleState extends MusicBeatState
public function addMoreText(text:String)
{
- var coolText:Alphabet = new Alphabet(0, (textGroup.length * 60) + 200, text, true, false);
+ var coolText:Alphabet = new Alphabet(0, (textGroup.length * 60) + 200, text, "bold");
coolText.screenCenter(X);
textGroup.add(coolText);
}
@@ -217,7 +200,6 @@ class TitleState extends MusicBeatState
{
super.beatHit(curBeat);
- #if TITLESCREEN_XML
if (curBeat >= titleLength || skippedIntro) {
if (!skippedIntro) skipIntro();
return;
@@ -225,27 +207,8 @@ class TitleState extends MusicBeatState
var introText = titleLines[curBeat];
if (introText != null)
introText.show();
- #else
- switch (curBeat)
- {
- case 1: createCoolText(['ninjamuffin99', 'phantomArcade', 'kawaisprite', 'evilsk8er']);
- case 3: addMoreText('present');
- case 4: deleteCoolText();
- case 5: createCoolText(['In association', 'with']);
- case 7: addMoreText('newgrounds'); ngSpr.visible = true;
- case 8: deleteCoolText(); ngSpr.visible = false;
- case 9: createCoolText([curWacky[0]]);
- case 11: addMoreText(curWacky[1]);
- case 12: deleteCoolText();
- case 13: addMoreText('Friday');
- case 14: addMoreText('Night');
- case 15: addMoreText('Funkin');
- case 16: skipIntro();
- }
- #end
}
- #if TITLESCREEN_XML
public var xml:Access;
public var titleLength:Int = 16;
public var titleLines:Map = [
@@ -267,12 +230,14 @@ class TitleState extends MusicBeatState
15 => new IntroText(['Friday', 'Night', "Funkin'"]),
];
+ public var titleSprites:Map = [];
+
public function loadXML() {
try {
xml = new Access(Xml.parse(Assets.getText(Paths.xml('titlescreen/titlescreen'))).firstElement());
if (xml.hasNode.intro) {
titleLines = [];
- if (xml.node.intro.has.length) titleLength = CoolUtil.getDefault(Std.parseInt(xml.node.intro.att.length), 16);
+ if (xml.node.intro.has.length) titleLength = Std.parseInt(xml.node.intro.att.length).getDefault(16);
for(node in xml.nodes.sprites) {
var parentFolder:String = node.getAtt("folder").getDefault("");
if (parentFolder != "" && !parentFolder.endsWith("/")) parentFolder += "/";
@@ -284,26 +249,28 @@ class TitleState extends MusicBeatState
default:
titleScreenSprites.add(spr);
}
+ if(node.has.name) titleSprites[node.att.name] = spr;
}
}
for(text in xml.node.intro.nodes.text) {
- var beat:Int = CoolUtil.getDefault(text.has.beat ? Std.parseInt(text.att.beat) : null, 0);
+ var beat:Int = text.has.beat ? Std.parseInt(text.att.beat).getDefault(0) : 0;
var texts:Array> = [];
- for(e in text.elements) {
- switch(e.name) {
+ for(node in text.elements) {
+ switch(node.name) {
case "line":
- if (!e.has.text) continue;
- texts.push(e.att.text);
+ if (!node.has.text) continue;
+ texts.push(node.att.text);
case "introtext":
- if (!e.has.line) continue;
- texts.push('{introText${e.att.line}}');
+ if (!node.has.line) continue;
+ texts.push('{introText${node.att.line}}');
case "sprite":
- if (!e.has.path) continue;
- var name:String = e.has.name ? e.att.name : null;
- var path:String = e.att.path;
- var flipX:Bool = e.has.flipX ? e.att.flipX == "true" : false;
- var flipY:Bool = e.has.flipY ? e.att.flipY == "true" : false;
- var scale:Float = e.has.scale ? CoolUtil.getDefault(Std.parseFloat(e.att.scale), 1) : 1;
+ if (!node.has.path) continue;
+ var name:String = node.has.name ? node.att.name : null;
+ var path:String = node.att.path;
+ var flipX:Bool = node.has.flipX ? node.att.flipX == "true" : false;
+ var flipY:Bool = node.has.flipY ? node.att.flipY == "true" : false;
+ var scale:Float = node.has.scale ? Std.parseFloat(node.att.scale).getDefault(1) : 1;
+
texts.push({
name: name,
path: path,
@@ -320,7 +287,6 @@ class TitleState extends MusicBeatState
Logs.error('Failed to load titlescreen XML: $e');
}
}
- #end
var skippedIntro:Bool = false;
@@ -328,10 +294,6 @@ class TitleState extends MusicBeatState
{
if (!skippedIntro)
{
- #if !TITLESCREEN_XML
- remove(ngSpr);
- #end
-
FlxG.camera.flash(FlxColor.WHITE, 4);
remove(blackScreen);
blackScreen.destroy();
@@ -361,7 +323,7 @@ class IntroText {
var image:TitleStateImage = e;
if (image.path == null) continue;
- var scale:Float = CoolUtil.getDefault(image.scale, 1);
+ var scale:Float = image.scale.getDefault(1);
var yPos:Float = 200;
if(state.textGroup.members.length > 0) {
@@ -370,8 +332,8 @@ class IntroText {
}
var sprite = new FlxSprite(0, yPos).loadAnimatedGraphic(Paths.image(image.path));
- sprite.flipX = CoolUtil.getDefault(image.flipX, false);
- sprite.flipY = CoolUtil.getDefault(image.flipY, false);
+ sprite.flipX = image.flipX.getDefault(false);
+ sprite.flipY = image.flipY.getDefault(false);
sprite.scale.set(scale, scale);
sprite.updateHitbox();
sprite.screenCenter(X);
diff --git a/source/funkin/menus/credits/CreditsCodename.hx b/source/funkin/menus/credits/CreditsCodename.hx
index 3deb2310..e3a730ab 100644
--- a/source/funkin/menus/credits/CreditsCodename.hx
+++ b/source/funkin/menus/credits/CreditsCodename.hx
@@ -11,9 +11,11 @@ using StringTools;
class CreditsCodename extends funkin.options.OptionsScreen {
public var error:Bool = false;
public var author:String = "FNF-CNE-Devs";
+ public var totalContributions:Int = 0;
+
public var mainDevCol:FlxColor = 0xFF9C35D5;
public var minContrCol:FlxColor = 0xFFB4A7DA;
- public var totalContributions:Int = 0;
+ public var contribFormats:Array = [];
public override function new()
{
@@ -58,12 +60,17 @@ class CreditsCodename extends funkin.options.OptionsScreen {
if(parent == null || parent.treeParent == null) return;
var text:String = parent.treeParent.pathDesc.text;
parent.treeParent.pathDesc.text = "";
- parent.treeParent.pathDesc.applyMarkup(text, [
+ parent.treeParent.pathDesc.applyMarkup(text, contribFormats = [
new FlxTextFormatMarkerPair(new FlxTextFormat(mainDevCol), '*'),
new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(minContrCol, mainDevCol, Options.contributors[curSelected].contributions / totalContributions)), '~')
]);
}
+ override function close() {
+ super.close();
+ for (frmt in contribFormats) parent.treeParent.pathDesc.removeFormat(frmt.format);
+ }
+
public function checkUpdate():Bool {
var curTime:Float = Date.now().getTime();
if(Options.lastUpdated != null && curTime < Options.lastUpdated + 120000) return false; // Fuck you Github rate limits - Nex
diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx
index 10779521..4298be57 100644
--- a/source/funkin/menus/ui/Alphabet.hx
+++ b/source/funkin/menus/ui/Alphabet.hx
@@ -1,284 +1,648 @@
package funkin.menus.ui;
-import flixel.group.FlxSpriteGroup;
-import flixel.util.FlxColor;
-import flixel.util.FlxTimer;
-import funkin.backend.assets.AssetsLibraryList;
-import funkin.backend.assets.IModsAssetLibrary;
-import funkin.backend.assets.LimeLibrarySymbol;
+import flixel.util.typeLimit.OneOfTwo;
+import haxe.ds.Vector;
+import funkin.menus.ui.effects.RegionEffect;
+
+import flixel.animation.FlxAnimation;
+import flixel.util.FlxDirectionFlags;
+import flixel.math.FlxPoint;
+import flixel.math.FlxAngle;
+import flixel.FlxTypes;
+
import haxe.xml.Access;
-import openfl.utils.AssetLibrary;
using StringTools;
+using flixel.util.FlxColorTransformUtil;
-/**
- * Loosely based on FlxTypeText lolol
- */
-class Alphabet extends FlxSpriteGroup
-{
- public var delay:Float = 0.05;
- public var paused:Bool = false;
+@:structInit
+final class AlphabetComponent {
+ public var anim:String;
+ public var x:Float;
+ public var y:Float;
+
+ // Precalculated values for angle
+ public var shouldRotate:Bool;
+ public var sin:Float;
+ public var cos:Float;
+ public var scaleX:Float;
+ public var scaleY:Float;
+
+ public var hasColorMode:Bool;
+ public var colorMode:ColorMode;
+}
+
+@:structInit
+final class AlphabetLetterData {
+ @:optional public var isDefault:Bool = false;
+ public var advance:Float;
+ public var advanceEmpty:Bool;
+ public var components:Array;
+}
+
+enum abstract AlphabetAlignment(ByteUInt) from ByteUInt to ByteUInt {
+ var LEFT;
+ var CENTER;
+ var RIGHT;
+
+ public function getMultiplier():Float {
+ return switch(this) {
+ case CENTER: 0.5;
+ case RIGHT: 1.0;
+ default: 0.0;
+ }
+ }
+
+ @:from
+ public static function fromString(value:String):AlphabetAlignment
+ return switch(value) {
+ case "left": LEFT;
+ case "center": CENTER;
+ case "right": RIGHT;
+ default: LEFT;
+ }
+}
+
+enum abstract CaseMode(ByteUInt) from ByteUInt to ByteUInt {
+ var NONE;
+ var UPPER;
+ var LOWER;
+
+ @:from
+ public static function fromString(value:String):CaseMode
+ return switch(value) {
+ case "none": NONE;
+ case "upper": UPPER;
+ case "lower": LOWER;
+ default: NONE;
+ }
+}
+
+enum abstract ColorMode(ByteInt) from ByteInt to ByteInt {
+ var TINT = 0;
+ var OFFSET = 1;
+ var NONE = 2;
+
+ public function setColorTransform(color:openfl.geom.ColorTransform, red:Float, green:Float, blue:Float, alpha:Float) {
+ var moduloWorkaround:Int = this;
+ var tintMult = Math.max(-moduloWorkaround + 1, 0);
+ var tintOffset = Math.min(moduloWorkaround, 1);
+ var offsetMult = (moduloWorkaround % 2) * 255;
+
+ color.redMultiplier = red * tintMult + tintOffset;
+ color.greenMultiplier = green * tintMult + tintOffset;
+ color.blueMultiplier = blue * tintMult + tintOffset;
+ color.redOffset = red * offsetMult;
+ color.greenOffset = green * offsetMult;
+ color.blueOffset = blue * offsetMult;
+ color.alphaMultiplier = alpha;
+ }
+}
+
+class Alphabet extends FlxSprite {
+ public var effects:Array = [];
+ var __renderData:AlphabetRenderData;
+ var __component:AlphabetComponent;
+
+ public var text(default, set):String = "";
+ @:isVar public var textWidth(get, set):Float;
+ public var textHeight(get, null):Float;
+
+ public var alignment:AlphabetAlignment = LEFT;
+ public var forceCase:CaseMode = NONE;
+
+ var __laneWidths:Array = []; // TODO: add alignment.
+ var __forceWidth:Float = 0.0; // TODO: implement functionality for this.
+ var __queueResize:Bool = false;
+ var __animTime:Float = 0.0;
+ var __ogForceScreen:Bool = false;
+
+ public var originOffset:FlxPoint = FlxPoint.get();
+
+ public var font(default, set):String;
+ var colorMode:ColorMode = TINT;
+ var defaultAdvance:Float = 40.0;
+ var lineGap:Float = 75.0;
+ var fps:Float = 24.0;
+ var letterData:Map = [];
+ var defaults:Vector = {
+ var v = new Vector(3);
+ v[0] = null;
+ v[1] = null;
+ v[2] = null;
+ v;
+ };
+ var loaded:Vector> = {
+ var v = new Vector(3);
+ v[0] = [];
+ v[1] = [];
+ v[2] = [];
+ v;
+ };
+ var failedLetters:Array = [];
// for menu shit
public var targetY:Float = 0;
public var isMenuItem:Bool = false;
- public var text:String = "";
-
- var _finalText:String = "";
- var _curText:String = "";
-
- var yMulti:Float = 1;
-
- // custom shit
- // amp, backslash, question mark, apostrophy, comma, angry faic, period
- var lastSprite:AlphaCharacter;
- var xPosResetted:Bool = false;
-
- var splitWords:Array = [];
-
- var isBold:Bool = false;
-
- private override function set_color(c:Int):Int {
- for(e in group.members) {
- if (e is AlphaCharacter) {
- var char = cast(e, AlphaCharacter);
- char.setColor(c, isBold);
- }
- }
- return super.set_color(c);
- }
-
- // TODO: fix this shit refreshing
- public function refreshAlphabetXML(path:String) {
- AlphaCharacter.__alphaPath = Paths.getAssetsRoot() + path;
- try {
- var xml = new Access(Xml.parse(Assets.getText(path)).firstElement());
- AlphaCharacter.boldAnims = [];
- AlphaCharacter.letterAnims = [];
- AlphaCharacter.boldAlphabetPath = AlphaCharacter.letterAlphabetPath = 'ui/alphabet';
-
- for(e in xml.elements) {
- var bold = e.name == "bold";
- var list = bold ? AlphaCharacter.boldAnims : AlphaCharacter.letterAnims;
- if (e.has.spritesheet) {
- if (bold)
- AlphaCharacter.boldAlphabetPath = e.att.spritesheet;
- else
- AlphaCharacter.letterAlphabetPath = e.att.spritesheet;
- }
- for(e in e.nodes.letter) {
- if (!e.has.char || !e.has.anim) continue;
- var name = e.att.char;
- var anim = e.att.anim;
- list[name] = anim;
- }
- }
- } catch(e) {
- trace(e.details());
- }
- }
- public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false)
- {
+ public function new(?x:Float, ?y:Float, ?text:String = "", ?font:OneOfTwo = "normal") {
super(x, y);
+ this.text = text;
+ this.font = (font is String) ? font : ((font == true) ? "bold" : "normal"); // == true just in case
+ this.__renderData = new AlphabetRenderData(this);
+ }
- _finalText = this.text = text;
- isBold = bold;
-
- var alphabetPath = Paths.xml("alphabet");
- if (Paths.getAssetsRoot() + alphabetPath != AlphaCharacter.__alphaPath) {
- refreshAlphabetXML(alphabetPath);
- }
- #if MOD_SUPPORT else {
- var libThing = new LimeLibrarySymbol(alphabetPath);
- if (libThing.library is AssetLibrary) {
- var library = AssetsLibraryList.getCleanLibrary(libThing.library);
- if (library is IModsAssetLibrary) {
- var modLib = cast(library, IModsAssetLibrary);
- @:privateAccess
- if (!modLib.__isCacheValid(library.cachedBytes, libThing.symbolName)) {
- refreshAlphabetXML(alphabetPath);
- }
- }
- }
- }
+ override function update(elapsed:Float):Void {
+ //super.update(elapsed);
+ // FLXOBJECT UPDATE
+ #if FLX_DEBUG
+ @:privateAccess FlxBasic.activeCount += 1;
#end
- if (text != "")
- {
- if (typed)
- {
- startTypedText();
- }
- else
- {
- addText();
- }
- }
- }
+ last.set(x, y);
- public function addText()
- {
- doSplitWords();
+ if (path != null && path.active)
+ path.update(elapsed);
- var xPos:Float = 0;
- var curRow:Int = 0;
- for (i=>character in splitWords)
- {
- if (lastSprite != null)
- xPos = lastSprite.x + lastSprite.width - x;
+ if (moves)
+ updateMotion(elapsed);
- if (xPosResetted && !(xPosResetted = false)) xPos = 0;
+ wasTouching = touching;
+ touching = FlxDirectionFlags.NONE;
+ // FLXOBJECT UPDATE END
- var letter:AlphaCharacter = new AlphaCharacter(xPos, 0);
- letter.row = curRow;
- if (isBold)
- letter.createBold(character);
- else
- letter.createLetter(character);
+ __animTime += elapsed;
+ for (effect in effects)
+ effect.effectTime += elapsed * effect.speed;
- // anim not found
- if (!letter.visible)
- xPos += 40;
-
- letter.setColor(color, isBold);
- add(letter);
-
- lastSprite = letter;
- if (_finalText.fastCodeAt(i) == "\n".code)
- {
- xPosResetted = true;
- curRow += 1;
- }
- }
- }
-
- function doSplitWords():Void
- {
- splitWords = _finalText.split("");
- }
-
- //public var personTalking:String = 'gf';
-
- public function startTypedText():Void
- {
- _finalText = text;
- doSplitWords();
-
- // trace(arrayShit);
-
- var loopNum:Int = 0;
-
- var xPos:Float = 0;
- var curRow:Int = 0;
-
- new FlxTimer().start(0.05, function(tmr:FlxTimer)
- {
- // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum));
- if (_finalText.fastCodeAt(loopNum) == "\n".code)
- {
- yMulti += 1;
- xPosResetted = true;
- xPos = 0;
- curRow += 1;
- }
- loopNum += 1;
-
- tmr.time = FlxG.random.float(0.04, 0.09);
- }, splitWords.length);
- }
-
- override function update(elapsed:Float)
- {
- super.update(elapsed);
-
- if (isMenuItem)
- {
- var scaledY = FlxMath.remapToRange(targetY, 0, 1, 0, 1.3);
+ if (isMenuItem) {
+ var scaledY = targetY * 1.3;
y = CoolUtil.fpsLerp(y, (scaledY * 120) + (FlxG.height * 0.48), 0.16);
x = CoolUtil.fpsLerp(x, (targetY * 20) + 90, 0.16);
}
-
- if (text != _finalText) {
- _finalText = text;
- for(e in members)
- e.destroy();
- members.clear();
- lastSprite = null;
- addText();
- }
}
- override function draw() {
+ override function draw():Void {
+ __ogForceScreen = forceIsOnScreen;
+ forceIsOnScreen = true;
super.draw();
}
-}
-class AlphaCharacter extends FlxSprite
-{
- @:dox(hide) @:noCompletion public static var __alphaPath:String = null;
+ override function isSimpleRender(?camera:FlxCamera):Bool {
+ return false; // maybe ill get simple render working another time??? not right now tho.
+ }
- public static var letterAlphabetPath:String;
- public static var boldAlphabetPath:String;
+ override function drawComplex(camera:FlxCamera):Void {
+ forceIsOnScreen = __ogForceScreen;
- public static var boldAnims:Map = [];
- public static var letterAnims:Map = [];
+ if (__queueResize)
+ recalcSizes();
+ var curLine = 0;
+ var lastLine = 0;
+ var daText = switch (forceCase) {
+ case UPPER: text.toUpperCase();
+ case LOWER: text.toLowerCase();
+ case NONE: text;
+ }
- public var row:Int = 0;
+ var alignmentMultiplier = alignment.getMultiplier();
- public function setColor(c:FlxColor, isBold:Bool) {
- if (isBold) {
- colorTransform.redMultiplier = c.redFloat;
- colorTransform.greenMultiplier = c.greenFloat;
- colorTransform.blueMultiplier = c.blueFloat;
- } else {
- colorTransform.redOffset = c.red;
- colorTransform.greenOffset = c.green;
- colorTransform.blueOffset = c.blue;
+ var ogOffX = frameOffset.x;
+ var ogOffY = frameOffset.y;
+ frameOffset.x -= (textWidth - __laneWidths[0]) * alignmentMultiplier;
+
+ var isGraphicsShader = shader != null && shader is flixel.graphics.tile.FlxGraphicsShader;
+ var ogRed:Float = colorTransform.redMultiplier;
+ var ogGreen:Float = colorTransform.greenMultiplier;
+ var ogBlue:Float = colorTransform.blueMultiplier;
+ var ogAlpha:Float = colorTransform.alphaMultiplier;
+
+ var frameTime = Math.floor(__animTime * fps);
+
+ for (i in 0...daText.length) {
+ __renderData.reset(this, ogRed, ogGreen, ogBlue, ogAlpha, daText.charAt(i));
+ for (effect in effects) {
+ if (effect.willModify(i, i - lastLine, __renderData))
+ effect.modify(i, i - lastLine, __renderData);
+ }
+
+ var letter = __renderData.letter;
+ if (letter == "\n") {
+ curLine++;
+ lastLine = i + 1;
+ frameOffset.x = ogOffX - (textWidth - __laneWidths[curLine]) * alignmentMultiplier;
+ frameOffset.y -= lineGap;
+ continue;
+ }
+
+ var data = getData(letter);
+
+ if (data == null) {
+ frameOffset.x -= defaultAdvance;
+ continue;
+ }
+
+ var advance:Float = Math.NaN;
+
+ for (i in 0...data.components.length) {
+ __component = data.components[i];
+ var anim = getLetterAnim(letter, data, __component, i);
+ advance = (Math.isNaN(advance)) ? getAdvance(letter, anim, data) : advance;
+
+ if (anim == null || __renderData.alpha <= 0.0)
+ break; // shouldnt this be like continue?
+
+ var frameToGet = frameTime % anim.numFrames;
+ frame = frames.frames[anim.frames[frameToGet]];
+
+ var offsetX = __component.x + __renderData.offsetX;
+ var offsetY = frame.sourceSize.y - lineGap + __component.y + __renderData.offsetY;
+ frameOffset.x += offsetX;
+ frameOffset.y += offsetY;
+ if (!isOnScreen(camera)) {
+ frameOffset.y -= offsetY;
+ frameOffset.x -= offsetX;
+ break;
+ }
+ if (isGraphicsShader)
+ shader.setCamSize(_frame.frame.x, _frame.frame.y, _frame.frame.width, _frame.frame.height);
+
+ if (__component.hasColorMode)
+ __component.colorMode.setColorTransform(colorTransform, __renderData.red, __renderData.green, __renderData.blue, __renderData.alpha);
+ else
+ colorMode.setColorTransform(colorTransform, __renderData.red, __renderData.green, __renderData.blue, __renderData.alpha);
+
+ drawLetter(camera);
+ frameOffset.y -= offsetY;
+ frameOffset.x -= offsetX;
+ }
+ frameOffset.x -= advance;
+ }
+ __component = null;
+
+ setColorTransform(ogRed, ogGreen, ogBlue, ogAlpha, 0, 0, 0, 0);
+ frameOffset.set(ogOffX, ogOffY);
+ }
+
+ function drawLetter(camera) {
+ _frame.prepareMatrix(_matrix, ANGLE_0, checkFlipX() != camera.flipX, checkFlipY() != camera.flipY);
+
+ _matrix.translate(_frame.frame.width * -0.5, _frame.frame.height * -0.5);
+ _matrix.scale(__component.scaleX, __component.scaleY);
+ if(__component.shouldRotate) _matrix.rotateWithTrig(__component.cos, __component.sin);
+ _matrix.translate(_frame.frame.width * 0.5, _frame.frame.height * 0.5);
+
+ _matrix.translate(-origin.x, -origin.y);
+
+ if (frameOffsetAngle != null && frameOffsetAngle != angle)
+ {
+ var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD;
+ var cos = Math.cos(angleOff);
+ var sin = Math.sin(angleOff);
+ // cos doesnt need to be negated
+ _matrix.rotateWithTrig(cos, -sin);
+ _matrix.translate(-frameOffset.x, -frameOffset.y);
+ _matrix.rotateWithTrig(cos, sin);
+ }
+ else
+ _matrix.translate(-frameOffset.x, -frameOffset.y);
+
+ _matrix.scale(scale.x, scale.y);
+
+ if (bakedRotationAngle <= 0)
+ {
+ updateTrig();
+
+ if (angle != 0)
+ _matrix.rotateWithTrig(_cosAngle, _sinAngle);
+ }
+
+ getScreenPosition(_point, camera).subtractPoint(offset);
+ _point.add(origin.x, origin.y);
+ _matrix.translate(_point.x, _point.y);
+
+ if (isPixelPerfectRender(camera))
+ {
+ _matrix.tx = Math.floor(_matrix.tx);
+ _matrix.ty = Math.floor(_matrix.ty);
+ }
+
+ doAdditionalMatrixStuff(_matrix, camera);
+
+ if (layer != null)
+ layer.drawPixels(this, camera, _frame, framePixels, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null);
+ else
+ camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null);
+ }
+
+ override function updateHitbox():Void {
+ offset.set(-0.5 * (width - textWidth), -0.5 * (height - textHeight));
+ origin.set(textWidth * 0.5 + originOffset.x, textHeight * 0.5 + originOffset.y);
+ }
+
+ function recalcSizes():Void {
+ __queueResize = false;
+ var curLine = 0;
+ var daText = switch (forceCase) {
+ case UPPER: text.toUpperCase();
+ case LOWER: text.toLowerCase();
+ case NONE: text;
+ }
+ @:bypassAccessor textWidth = 0;
+ textHeight = lineGap;
+ __laneWidths = [0];
+ for (i in 0...daText.length) {
+ var letter = daText.charAt(i);
+ if (letter == "\n") {
+ textHeight += lineGap;
+ __laneWidths.push(0);
+ curLine++;
+ continue;
+ }
+
+ var data = getData(letter);
+ __laneWidths[curLine] += (data != null) ? getAdvance(letter, getLetterAnim(letter, data, data.components[0], 0), data) : defaultAdvance;
+ @:bypassAccessor textWidth = Math.max(textWidth, __laneWidths[curLine]);
+ }
+
+ origin.set(textWidth * 0.5 + originOffset.x, textHeight * 0.5 + originOffset.y);
+ }
+
+ function getAdvance(letter:String, anim:FlxAnimation, data:AlphabetLetterData):Float {
+ if (anim == null)
+ return defaultAdvance;
+
+ if (data.advanceEmpty && !data.isDefault) {
+ data.advanceEmpty = false;
+ data.advance = frames.frames[anim.frames[0]].sourceSize.x;
+ }
+ return (data.isDefault) ? frames.frames[anim.frames[0]].sourceSize.x : data.advance;
+ }
+
+ function getData(char:String):AlphabetLetterData {
+ if (failedLetters.contains(char)) return null;
+ for (i in 0...3) { // this feels wrong
+ if (loaded[i].contains(char))
+ return defaults[i];
+ }
+
+ var data:AlphabetLetterData = null;
+ if (letterData.exists(char))
+ data = letterData.get(char);
+
+ if (data == null) {
+ var charCode:Int = char.charCodeAt(0);
+ if (charCode >= 'A'.code && charCode <= 'Z'.code && defaults[CaseMode.UPPER] != null)
+ data = defaults[CaseMode.UPPER];
+ else if (charCode >= 'a'.code && charCode <= 'z'.code && defaults[CaseMode.LOWER] != null)
+ data = defaults[CaseMode.LOWER];
+ else
+ data = defaults[CaseMode.NONE];
+ }
+
+ if(data == null) {
+ failedLetters.push(char);
+ return null;
+ } else if (data.isDefault)
+ loaded[defaultsIndexOf(data)].push(char);
+ return data;
+ }
+
+ final function defaultsIndexOf(data:AlphabetLetterData):Int {
+ //while(i < defaults.length) {
+ for(i in 0...3) {
+ if(defaults[i] == data)
+ return i;
+ }
+ return -1;
+ }
+
+ function getLetterAnim(char:String, data:AlphabetLetterData, component:AlphabetComponent, index:Int):FlxAnimation {
+ if (data == null) return null;
+ var name = char + Std.string(index);
+ if (animation.exists(name)) return animation.getByName(name);
+
+ var anim = (data.isDefault) ?
+ component.anim.replace("LETTER", char) :
+ component.anim;
+ animation.addByPrefix(name, anim, fps);
+ if (!animation.exists(name)) {
+ failedLetters.push(char);
+ return null;
+ }
+ return animation.getByName(name);
+ }
+
+ function checkNode(node:Xml):Void {
+ switch (node.nodeName) {
+ case "spritesheet":
+ if (frames == null)
+ frames = Paths.getFrames(node.firstChild().nodeValue);
+ else {
+ for (frame in Paths.getFrames(node.firstChild().nodeValue).frames)
+ frames.pushFrame(frame);
+ }
+ case "defaultAnim":
+ var idx = ["UPPER", "LOWER"].indexOf(node.get("casing").toUpperCase()) + 1;
+
+ var rad:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD;
+ var angleCos = Math.cos(rad);
+ var angleSin = Math.sin(rad);
+
+ var res:AlphabetLetterData = {
+ isDefault: true,
+ advance: 0.0,
+ advanceEmpty: true,
+ components: [{
+ anim: node.firstChild().nodeValue,
+
+ x: 0.0,
+ y: 0.0,
+ scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0),
+ scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0),
+
+ shouldRotate: rad != 0,
+ cos: angleCos,
+ sin: angleSin,
+
+ hasColorMode: false,
+ colorMode: 0
+ }]
+ }
+
+ defaults[idx] = res;
+ case "composite":
+ if(!node.exists("char")) {
+ Logs.error(" must have a char attribute", "Alphabet");
+ return;
+ }
+ var char = node.get("char");
+ var advance:Float = Std.parseFloat(node.get("advance"));
+ var components:Array = [];
+ for (component in node.elementsNamed("component")) {
+ if(!component.exists("anim")) {
+ Logs.error(" must have a anim attribute", "Alphabet");
+ return;
+ }
+
+ var rad:Float = Std.parseFloat(component.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD;
+ var angleCos = Math.cos(rad);
+ var angleSin = Math.sin(rad);
+
+ var xOff:Float = -Std.parseFloat(component.get("x")).getDefaultFloat(0.0);
+ var yOff:Float = Std.parseFloat(component.get("y")).getDefaultFloat(0.0);
+
+ var colorMode = ["tint", "offsets", "none"].indexOf(component.get("colorMode"));
+
+ components.push({
+ anim: component.get("anim"),
+
+ x: xOff,
+ y: yOff,
+ scaleX: Std.parseFloat(component.get("scaleX")).getDefaultFloat(1.0),
+ scaleY: Std.parseFloat(component.get("scaleY")).getDefaultFloat(1.0),
+
+ shouldRotate: rad != 0,
+ cos: angleCos,
+ sin: angleSin,
+
+ hasColorMode: colorMode >= 0,
+ colorMode: colorMode
+ });
+ }
+ letterData.set(char, {
+ isDefault: false,
+ advance: advance,
+ advanceEmpty: Math.isNaN(advance),
+ components: components
+ });
+ case "anim":
+ if(!node.exists("char")) {
+ Logs.error(" must have a char attribute", "Alphabet");
+ return;
+ }
+ var char = node.get("char");
+
+ var rad:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD;
+ var angleCos = Math.cos(rad);
+ var angleSin = Math.sin(rad);
+
+ var xOff:Float = -Std.parseFloat(node.get("x")).getDefaultFloat(0.0);
+ var yOff:Float = Std.parseFloat(node.get("y")).getDefaultFloat(0.0);
+ var advance:Float = Std.parseFloat(node.get("advance"));
+
+ var colorMode = ["tint", "offsets", "none"].indexOf(node.get("colorMode"));
+
+ letterData.set(char, {
+ isDefault: false,
+ advance: advance,
+ advanceEmpty: Math.isNaN(advance),
+ components: [{
+ anim: node.firstChild().nodeValue,
+
+ x: xOff,
+ y: yOff,
+ scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0),
+ scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0),
+
+ shouldRotate: rad != 0,
+ cos: angleCos,
+ sin: angleSin,
+
+ hasColorMode: colorMode >= 0,
+ colorMode: colorMode
+ }]
+ });
+ case "languageSection": // used to only parse characters if a specific language is selected
+ if(!node.exists("langs")) {
+ Logs.error(" must have a langs attribute", "Alphabet");
+ return;
+ }
+ var langs = [for (lang in node.get("langs").split(",")) lang.trim()];
+ // maybe add a way to toggle this off? like force it to add all
+ if (langs.contains(Options.language)) {
+ for (langNode in node.elements())
+ checkNode(langNode);
+ }
}
}
- public function new(x:Float, y:Float)
- {
- super(x, y);
- antialiasing = true;
- }
+ function loadFont(value:String):Void {
+ __queueResize = true;
- public function createBold(letter:String)
- {
- if(!boldAnims.exists(letter))
- letter = letter.toUpperCase();
- if (!boldAnims.exists(letter)) {
- visible = false;
- active = false;
- scale.set();
- width = 40;
- return;
+ var xml:Xml = Xml.parse(Assets.getText(Paths.xml("alphabet/" + value))).firstElement();
+
+ // reset old values
+ letterData = new Map();
+ defaults = {
+ var v = new Vector(3);
+ v[0] = null;
+ v[1] = null;
+ v[2] = null;
+ v;
}
- frames = Paths.getFrames(boldAlphabetPath);
- animation.addByPrefix(letter, boldAnims[letter], 24);
- animation.play(letter);
- updateHitbox();
-
- y += row * height;
- }
-
- public function createLetter(letter:String):Void
- {
- if (!letterAnims.exists(letter)) {
- visible = false;
- active = false;
- scale.set();
- width = 40;
- return;
+ loaded = {
+ var v = new Vector(3);
+ v[0] = [];
+ v[1] = [];
+ v[2] = [];
+ v;
}
- frames = Paths.getFrames(letterAlphabetPath);
- animation.addByPrefix(letter, letterAnims[letter], 24);
- animation.play(letter);
- updateHitbox();
+ failedLetters = [];
- y = (110 - height);
- y += row * 60;
+ defaultAdvance = Std.parseFloat(xml.get("advance")).getDefaultFloat(40.0);
+ lineGap = Std.parseFloat(xml.get("lineGap")).getDefaultFloat(75.0);
+ fps = Std.parseFloat(xml.get("fps")).getDefaultFloat(24.0);
+ forceCase = xml.get("forceCasing").getDefault("none").toLowerCase();
+ colorMode = (["offsets", "none"].indexOf(xml.get("colorMode")) + 1);
+
+ frames = null;
+
+ for (node in xml.elements())
+ checkNode(node);
}
-}
+
+ override function destroy():Void {
+ originOffset = FlxDestroyUtil.destroy(originOffset);
+ failedLetters = [];
+ super.destroy();
+ }
+
+ function get_textWidth():Float {
+ if (__queueResize)
+ recalcSizes();
+ return textWidth;
+ }
+ function set_textWidth(value:Float):Float {
+ if(!__queueResize)
+ __queueResize = __forceWidth != value;
+ return __forceWidth = value;
+ }
+
+ override function get_width():Float {
+ return textWidth * scale.x;
+ }
+ override function get_height():Float {
+ return textHeight * scale.y;
+ }
+
+ function get_textHeight():Float {
+ if (__queueResize)
+ recalcSizes();
+ return textHeight;
+ }
+
+ function set_text(value:String):String {
+ if(!__queueResize)
+ __queueResize = text != value;
+ return text = value;
+ }
+
+ function set_font(value:String):String {
+ if (font != value) {
+ loadFont(font = value);
+ }
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/menus/ui/ClassicAlphabet.hx b/source/funkin/menus/ui/ClassicAlphabet.hx
new file mode 100644
index 00000000..4ece8633
--- /dev/null
+++ b/source/funkin/menus/ui/ClassicAlphabet.hx
@@ -0,0 +1,284 @@
+package funkin.menus.ui;
+
+import flixel.group.FlxSpriteGroup;
+import flixel.util.FlxColor;
+import flixel.util.FlxTimer;
+import funkin.backend.assets.AssetsLibraryList;
+import funkin.backend.assets.IModsAssetLibrary;
+import funkin.backend.assets.LimeLibrarySymbol;
+import haxe.xml.Access;
+import openfl.utils.AssetLibrary;
+
+using StringTools;
+
+/**
+ * Loosely based on FlxTypeText lolol
+ */
+class ClassicAlphabet extends FlxSpriteGroup
+{
+ public var delay:Float = 0.05;
+ public var paused:Bool = false;
+
+ // for menu shit
+ public var targetY:Float = 0;
+ public var isMenuItem:Bool = false;
+
+ public var text:String = "";
+
+ var _finalText:String = "";
+ var _curText:String = "";
+
+ var yMulti:Float = 1;
+
+ // custom shit
+ // amp, backslash, question mark, apostrophy, comma, angry faic, period
+ var lastSprite:AlphaCharacter;
+ var xPosResetted:Bool = false;
+
+ var splitWords:Array = [];
+
+ var isBold:Bool = false;
+
+ private override function set_color(c:Int):Int {
+ for(e in group.members) {
+ if (e is AlphaCharacter) {
+ var char:AlphaCharacter = cast e;
+ char.setColor(c, isBold);
+ }
+ }
+ return super.set_color(c);
+ }
+
+ // TODO: fix this shit refreshing
+ public function refreshAlphabetXML(path:String) {
+ AlphaCharacter.__alphaPath = Paths.getAssetsRoot() + path;
+ try {
+ var xml = new Access(Xml.parse(Assets.getText(path)).firstElement());
+ AlphaCharacter.boldAnims = [];
+ AlphaCharacter.letterAnims = [];
+ AlphaCharacter.boldAlphabetPath = AlphaCharacter.letterAlphabetPath = 'ui/alphabet';
+
+ for(e in xml.elements) {
+ var bold = e.name == "bold";
+ var list = bold ? AlphaCharacter.boldAnims : AlphaCharacter.letterAnims;
+ if (e.has.spritesheet) {
+ if (bold)
+ AlphaCharacter.boldAlphabetPath = e.att.spritesheet;
+ else
+ AlphaCharacter.letterAlphabetPath = e.att.spritesheet;
+ }
+ for(e in e.nodes.letter) {
+ if (!e.has.char || !e.has.anim) continue;
+ var name = e.att.char;
+ var anim = e.att.anim;
+ list[name] = anim;
+ }
+ }
+ } catch(e) {
+ trace(e.details());
+ }
+ }
+ public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false)
+ {
+ super(x, y);
+
+ _finalText = this.text = text;
+ isBold = bold;
+
+ var alphabetPath = Paths.xml("alphabet");
+ if (Paths.getAssetsRoot() + alphabetPath != AlphaCharacter.__alphaPath) {
+ refreshAlphabetXML(alphabetPath);
+ }
+ #if MOD_SUPPORT else {
+ var libThing = new LimeLibrarySymbol(alphabetPath);
+ if (libThing.library is AssetLibrary) {
+ var library = AssetsLibraryList.getCleanLibrary(libThing.library);
+ if (library is IModsAssetLibrary) {
+ var modLib = cast(library, IModsAssetLibrary);
+ @:privateAccess
+ if (!modLib.__isCacheValid(library.cachedBytes, libThing.symbolName)) {
+ refreshAlphabetXML(alphabetPath);
+ }
+ }
+ }
+ }
+ #end
+
+ if (text != "")
+ {
+ if (typed)
+ {
+ startTypedText();
+ }
+ else
+ {
+ addText();
+ }
+ }
+ }
+
+ public function addText()
+ {
+ doSplitWords();
+
+ var xPos:Float = 0;
+ var curRow:Int = 0;
+ for (i=>character in splitWords)
+ {
+ if (lastSprite != null)
+ xPos = lastSprite.x + lastSprite.width - x;
+
+ if (xPosResetted && !(xPosResetted = false)) xPos = 0;
+
+ var letter:AlphaCharacter = new AlphaCharacter(xPos, 0);
+ letter.row = curRow;
+ if (isBold)
+ letter.createBold(character);
+ else
+ letter.createLetter(character);
+
+ // anim not found
+ if (!letter.visible)
+ xPos += 40;
+
+ letter.setColor(color, isBold);
+ add(letter);
+
+ lastSprite = letter;
+ if (_finalText.fastCodeAt(i) == "\n".code)
+ {
+ xPosResetted = true;
+ curRow += 1;
+ }
+ }
+ }
+
+ function doSplitWords():Void
+ {
+ splitWords = _finalText.split("");
+ }
+
+ //public var personTalking:String = 'gf';
+
+ public function startTypedText():Void
+ {
+ _finalText = text;
+ doSplitWords();
+
+ // trace(arrayShit);
+
+ var loopNum:Int = 0;
+
+ var xPos:Float = 0;
+ var curRow:Int = 0;
+
+ new FlxTimer().start(0.05, function(tmr:FlxTimer)
+ {
+ // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum));
+ if (_finalText.fastCodeAt(loopNum) == "\n".code)
+ {
+ yMulti += 1;
+ xPosResetted = true;
+ xPos = 0;
+ curRow += 1;
+ }
+ loopNum += 1;
+
+ tmr.time = FlxG.random.float(0.04, 0.09);
+ }, splitWords.length);
+ }
+
+ override function update(elapsed:Float)
+ {
+ super.update(elapsed);
+
+ if (isMenuItem)
+ {
+ var scaledY = FlxMath.remapToRange(targetY, 0, 1, 0, 1.3);
+
+ y = CoolUtil.fpsLerp(y, (scaledY * 120) + (FlxG.height * 0.48), 0.16);
+ x = CoolUtil.fpsLerp(x, (targetY * 20) + 90, 0.16);
+ }
+
+ if (text != _finalText) {
+ _finalText = text;
+ for(e in members)
+ e.destroy();
+ members.clear();
+ lastSprite = null;
+ addText();
+ }
+ }
+
+ override function draw() {
+ super.draw();
+ }
+}
+
+class AlphaCharacter extends FlxSprite
+{
+ @:dox(hide) @:noCompletion public static var __alphaPath:String = null;
+
+ public static var letterAlphabetPath:String;
+ public static var boldAlphabetPath:String;
+
+ public static var boldAnims:Map = [];
+ public static var letterAnims:Map = [];
+
+ public var row:Int = 0;
+
+ public function setColor(c:FlxColor, isBold:Bool) {
+ if (isBold) {
+ colorTransform.redMultiplier = c.redFloat;
+ colorTransform.greenMultiplier = c.greenFloat;
+ colorTransform.blueMultiplier = c.blueFloat;
+ } else {
+ colorTransform.redOffset = c.red;
+ colorTransform.greenOffset = c.green;
+ colorTransform.blueOffset = c.blue;
+ }
+ }
+ public function new(x:Float, y:Float)
+ {
+ super(x, y);
+
+ antialiasing = true;
+ }
+
+ public function createBold(letter:String)
+ {
+ if(!boldAnims.exists(letter))
+ letter = letter.toUpperCase();
+ if (!boldAnims.exists(letter)) {
+ visible = false;
+ active = false;
+ scale.set();
+ width = 40;
+ return;
+ }
+ frames = Paths.getFrames(boldAlphabetPath);
+ animation.addByPrefix(letter, boldAnims[letter], 24);
+ animation.play(letter);
+ updateHitbox();
+
+ y += row * height;
+ }
+
+ public function createLetter(letter:String):Void
+ {
+ if (!letterAnims.exists(letter)) {
+ visible = false;
+ active = false;
+ scale.set();
+ width = 40;
+ return;
+ }
+ frames = Paths.getFrames(letterAlphabetPath);
+ animation.addByPrefix(letter, letterAnims[letter], 24);
+ animation.play(letter);
+ updateHitbox();
+
+ y = (110 - height);
+ y += row * 60;
+ }
+}
diff --git a/source/funkin/menus/ui/NewAlphabet.hx b/source/funkin/menus/ui/NewAlphabet.hx
deleted file mode 100644
index 7a0c76d0..00000000
--- a/source/funkin/menus/ui/NewAlphabet.hx
+++ /dev/null
@@ -1,593 +0,0 @@
-package funkin.menus.ui;
-
-import haxe.ds.Vector;
-import funkin.menus.ui.effects.RegionEffect;
-
-import flixel.animation.FlxAnimation;
-import flixel.util.FlxDirectionFlags;
-import flixel.math.FlxPoint;
-import flixel.FlxTypes;
-
-import haxe.xml.Access;
-
-using StringTools;
-using flixel.util.FlxColorTransformUtil;
-
-@:structInit
-final class AlphabetOutline {
- public var anim:String;
- public var x:Float;
- public var y:Float;
-}
-
-@:structInit
-final class AlphabetComponentData {
- public var anim:String;
-
- @:optional public var outline:AlphabetOutline;
-}
-
-@:structInit
-final class AlphabetComponent {
- public var data:AlphabetComponentData;
- public var x:Float;
- public var y:Float;
-
- // Precalculated values for angle
- public var sin:Float;
- public var cos:Float;
- public var scaleX:Float;
- public var scaleY:Float;
-}
-
-@:structInit
-final class AlphabetLetterData {
- @:optional public var isDefault:Bool = false;
- public var advance:Float;
- public var advanceEmpty:Bool;
- public var components:Array;
-}
-
-enum abstract AlphabetAlignment(ByteUInt) from ByteUInt to ByteUInt {
- var LEFT;
- var CENTER;
- var RIGHT;
-
- public function getMultiplier():Float {
- return switch(this) {
- case CENTER: 0.5;
- case RIGHT: 1.0;
- default: 0.0;
- }
- }
-
- @:from
- public static function fromString(value:String):AlphabetAlignment
- return switch(value) {
- case "left": LEFT;
- case "center": CENTER;
- case "right": RIGHT;
- default: LEFT;
- }
-}
-
-enum abstract CaseMode(ByteUInt) from ByteUInt to ByteUInt {
- var NONE;
- var UPPER;
- var LOWER;
-
- @:from
- public static function fromString(value:String):CaseMode
- return switch(value) {
- case "none": NONE;
- case "upper": UPPER;
- case "lower": LOWER;
- default: NONE;
- }
-}
-
-class NewAlphabet extends FlxSprite {
- public var effects:Array = [];
- var __renderData:AlphabetRenderData;
-
- public var text(default, set):String = "";
- @:isVar public var textWidth(get, set):Float;
- public var textHeight(get, null):Float;
-
- public var alignment:AlphabetAlignment = LEFT;
- public var forceCase:CaseMode = NONE;
-
- var __laneWidths:Array = []; // TODO: add alignment.
- var __forceWidth:Float = 0.0; // TODO: implement functionality for this.
- var __queueResize:Bool = false;
- var __animTime:Float = 0.0;
- var __ogForceScreen:Bool = false;
-
- public var originOffset:FlxPoint = FlxPoint.get();
-
- public var font(default, set):String;
- var transformMult:ByteUInt = 1; // 1 if multipliers (tint), 0 if offset.
- var defaultAdvance:Float = 40.0;
- var lineGap:Float = 75.0;
- var fps:Float = 24.0;
- var letterData:Map = [];
- var allComponents:Map = [];
- var defaults:Vector = {
- var v = new Vector(3);
- v[0] = null;
- v[1] = null;
- v[2] = null;
- v;
- };
- var loaded:Vector> = {
- var v = new Vector(3);
- v[0] = [];
- v[1] = [];
- v[2] = [];
- v;
- };
- var failedLetters:Array = [];
-
- public function new(?x:Float, ?y:Float, ?font:String = "bold") {
- super(x, y);
- this.font = font;
- this.__renderData = new AlphabetRenderData(this);
- }
-
- override function update(elapsed:Float):Void {
- //super.update(elapsed);
- // FLXOBJECT UPDATE
- #if FLX_DEBUG
- @:privateAccess FlxBasic.activeCount += 1;
- #end
-
- last.set(x, y);
-
- if (path != null && path.active)
- path.update(elapsed);
-
- if (moves)
- updateMotion(elapsed);
-
- wasTouching = touching;
- touching = FlxDirectionFlags.NONE;
- // FLXOBJECT UPDATE END
-
- __animTime += elapsed;
- for (effect in effects)
- effect.effectTime += elapsed * effect.speed;
- }
-
- override function draw():Void {
- __ogForceScreen = forceIsOnScreen;
- forceIsOnScreen = true;
- super.draw();
- }
-
- override function isSimpleRender(?camera:FlxCamera):Bool {
- return false; // maybe ill get simple render working another time??? not right now tho.
- }
-
- override function drawComplex(camera:FlxCamera):Void {
- forceIsOnScreen = __ogForceScreen;
-
- if (__queueResize)
- recalcSizes();
- var curLine = 0;
- var lastLine = 0;
- var daText = switch (forceCase) {
- case UPPER: text.toUpperCase();
- case LOWER: text.toLowerCase();
- case NONE: text;
- }
-
- var alignmentMultiplier = alignment.getMultiplier();
-
- var ogOffX = frameOffset.x;
- var ogOffY = frameOffset.y;
- frameOffset.x -= (textWidth - __laneWidths[0]) * alignmentMultiplier;
-
- var isGraphicsShader = shader != null && shader is flixel.graphics.tile.FlxGraphicsShader;
- var offsetMult = (1 - transformMult) * 255;
- var ogRed:Float = colorTransform.redMultiplier;
- var ogGreen:Float = colorTransform.greenMultiplier;
- var ogBlue:Float = colorTransform.blueMultiplier;
- var ogAlpha:Float = colorTransform.alphaMultiplier;
-
- var frameTime = Math.floor(__animTime * fps);
-
- for (i in 0...daText.length) {
- __renderData.reset(this, ogRed, ogGreen, ogBlue, ogAlpha, daText.charAt(i));
- for (effect in effects) {
- if (effect.willModify(i, i - lastLine, __renderData))
- effect.modify(i, i - lastLine, __renderData);
- }
-
- var letter = __renderData.letter;
- if (letter == "\n") {
- curLine++;
- lastLine = i + 1;
- frameOffset.x = ogOffX - (textWidth - __laneWidths[curLine]) * alignmentMultiplier;
- frameOffset.y -= lineGap;
- continue;
- }
-
- var data = getData(letter);
-
- if (data == null) {
- frameOffset.x -= defaultAdvance;
- continue;
- }
-
- var advance:Float = Math.NaN;
-
- for (i => com in data.components) {
- var anim = getLetterAnim(letter, data, com, i);
- advance = (Math.isNaN(advance)) ? getAdvance(letter, anim, data) : advance;
-
- if (anim == null || __renderData.alpha <= 0.0) {
- frameOffset.x -= advance;
- break; // shouldnt this be like continue?
- }
-
- var frameToGet = frameTime % anim.numFrames;
- frame = frames.frames[anim.frames[frameToGet]];
-
- var offsetX = com.x + __renderData.offsetX;
- var offsetY = frame.sourceSize.y - lineGap + com.y + __renderData.offsetY;
- frameOffset.x += offsetX;
- frameOffset.y += offsetY;
- if (!isOnScreen(camera)) {
- frameOffset.y -= offsetY;
- frameOffset.x -= offsetX + advance;
- break;
- }
- if (isGraphicsShader)
- shader.setCamSize(_frame.frame.x, _frame.frame.y, _frame.frame.width, _frame.frame.height);
-
- setColorTransform(
- __renderData.red * transformMult, __renderData.green * transformMult, __renderData.blue * transformMult, __renderData.alpha,
- Math.round(__renderData.red * offsetMult), Math.round(__renderData.green * offsetMult), Math.round(__renderData.blue * offsetMult), 0
- );
- super.drawComplex(camera);
- frameOffset.y -= offsetY;
- frameOffset.x -= offsetX + advance;
- }
- }
-
- setColorTransform(ogRed, ogGreen, ogBlue, ogAlpha, 0, 0, 0, 0);
- frameOffset.set(ogOffX, ogOffY);
- }
-
- override function updateHitbox():Void {
- width = Math.abs(scale.x) * textWidth;
- height = Math.abs(scale.y) * textHeight;
- offset.set(-0.5 * (width - textWidth), -0.5 * (height - textHeight));
- origin.set(textWidth * 0.5 + originOffset.x, textHeight * 0.5 + originOffset.y);
- }
-
- function recalcSizes():Void {
- __queueResize = false;
- var curLine = 0;
- var daText = switch (forceCase) {
- case UPPER: text.toUpperCase();
- case LOWER: text.toLowerCase();
- case NONE: text;
- }
- @:bypassAccessor textWidth = 0;
- textHeight = lineGap;
- __laneWidths = [0];
- for (i in 0...daText.length) {
- var letter = daText.charAt(i);
- if (letter == "\n") {
- textHeight += lineGap;
- __laneWidths.push(0);
- curLine++;
- continue;
- }
-
- var data = getData(letter);
- __laneWidths[curLine] += (data != null) ? getAdvance(letter, getLetterAnim(letter, data, data.components[0], 0), data) : defaultAdvance;
- @:bypassAccessor textWidth = Math.max(textWidth, __laneWidths[curLine]);
- }
-
- origin.set(textWidth * 0.5 + originOffset.x, textHeight * 0.5 + originOffset.y);
- }
-
- function getAdvance(letter:String, anim:FlxAnimation, data:AlphabetLetterData):Float {
- if (anim == null)
- return defaultAdvance;
-
- if (data.advanceEmpty && !data.isDefault) {
- data.advanceEmpty = false;
- data.advance = frames.frames[anim.frames[0]].sourceSize.x;
- }
- return (data.isDefault) ? frames.frames[anim.frames[0]].sourceSize.x : data.advance;
- }
-
- function getData(char:String):AlphabetLetterData {
- if (failedLetters.contains(char)) return null;
- for (i in 0...3) { // this feels wrong
- if (loaded[i].contains(char))
- return defaults[i];
- }
-
- var data:AlphabetLetterData = null;
- if (letterData.exists(char))
- data = letterData.get(char);
-
- if (data == null) {
- var charCode:Int = char.charCodeAt(0);
- if (charCode >= 'A'.code && charCode <= 'Z'.code && defaults[CaseMode.UPPER] != null)
- data = defaults[CaseMode.UPPER];
- else if (charCode >= 'a'.code && charCode <= 'z'.code && defaults[CaseMode.LOWER] != null)
- data = defaults[CaseMode.LOWER];
- else
- data = defaults[CaseMode.NONE];
- }
-
- if(data == null) {
- failedLetters.push(char);
- return null;
- } else if (data.isDefault)
- loaded[defaultsIndexOf(data)].push(char);
- return data;
- }
-
- final function defaultsIndexOf(data:AlphabetLetterData):Int {
- //while(i < defaults.length) {
- for(i in 0...3) {
- if(defaults[i] == data)
- return i;
- }
- return -1;
- }
-
- function getLetterAnim(char:String, data:AlphabetLetterData, component:AlphabetComponent, index:Int):FlxAnimation {
- if (data == null) return null;
- var name = char + Std.string(index);
- if (animation.exists(name)) return animation.getByName(name);
-
- var anim = (data.isDefault) ?
- component.data.anim.replace("LETTER", char) :
- component.data.anim;
- animation.addByPrefix(name, anim, fps);
- if (!animation.exists(name)) {
- failedLetters.push(char);
- return null;
- }
- return animation.getByName(name);
- }
-
- function checkNode(node:Xml):Void {
- switch (node.nodeName) {
- case "spritesheet":
- if (frames == null)
- frames = Paths.getFrames(node.firstChild().nodeValue);
- else {
- for (frame in Paths.getFrames(node.firstChild().nodeValue).frames)
- frames.pushFrame(frame);
- }
- case "defaultAnim":
- var idx = ["UPPER", "LOWER"].indexOf(node.get("casing").toUpperCase()) + 1;
-
- var angle:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0);
- var angleCos = Math.cos(angle);
- var angleSin = Math.sin(angle);
-
- // register character as component
- allComponents.set("DEFAULT_" + idx, {
- anim: node.firstChild().nodeValue.trim(),
- outline: null
- });
-
- var res:AlphabetLetterData = {
- isDefault: true,
- advance: 0.0,
- advanceEmpty: true,
- components: [{
- data: allComponents.get("DEFAULT_" + idx),
-
- x: 0.0,
- y: 0.0,
- scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0),
- scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0),
-
- cos: angleCos,
- sin: angleSin
- }]
- }
-
- defaults[idx] = res;
- case "component-data":
- if(!node.exists("id")) {
- Logs.error(" must have an id attribute", "Alphabet");
- return;
- }
-
- var id = node.get("id");
- var anim = node.firstChild().nodeValue.trim();
- var outline:AlphabetOutline = null;
- if(node.firstElement() != null) {
- var outlineNode = node.firstElement();
- switch(outlineNode.nodeName) {
- case "outline":
- outline = {
- anim: outlineNode.firstChild().nodeValue.trim(),
- x: Std.parseFloat(outlineNode.get("x")).getDefaultFloat(0.0),
- y: Std.parseFloat(outlineNode.get("y")).getDefaultFloat(0.0)
- };
- }
- }
-
- allComponents.set(id, {
- anim: anim,
- outline: outline
- });
- case "composite":
- if(!node.exists("char")) {
- Logs.error(" must have a char attribute", "Alphabet");
- return;
- }
- var char = node.get("char");
- var advance:Float = Std.parseFloat(node.get("advance"));
- var components:Array = [];
- for (component in node.elementsNamed("component")) {
- if(!component.exists("id")) {
- Logs.error(" must have a id attribute", "Alphabet");
- return;
- }
-
- var angle:Float = Std.parseFloat(component.get("angle")).getDefaultFloat(0.0);
- var angleCos = Math.cos(angle);
- var angleSin = Math.sin(angle);
-
- var xOff:Float = -Std.parseFloat(component.get("x")).getDefaultFloat(0.0);
- var yOff:Float = Std.parseFloat(component.get("y")).getDefaultFloat(0.0);
-
- var componentId = component.get("id").trim();
- if(!allComponents.exists(componentId)) {
- Logs.error("Component with id '" + componentId + "' does not exist", "Alphabet");
- return;
- }
-
- components.push({
- data: allComponents.get(componentId),
-
- x: xOff,
- y: yOff,
- scaleX: Std.parseFloat(component.get("scaleX")).getDefaultFloat(1.0),
- scaleY: Std.parseFloat(component.get("scaleY")).getDefaultFloat(1.0),
-
- cos: angleCos,
- sin: angleSin
- });
- }
- letterData.set(char, {
- isDefault: false,
- advance: advance,
- advanceEmpty: Math.isNaN(advance),
- components: components
- });
- case "anim":
- if(!node.exists("char")) {
- Logs.error(" must have a char attribute", "Alphabet");
- return;
- }
- var char = node.get("char");
-
- var angle:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0);
- var angleCos = Math.cos(angle);
- var angleSin = Math.sin(angle);
-
- var xOff:Float = -Std.parseFloat(node.get("x")).getDefaultFloat(0.0);
- var yOff:Float = Std.parseFloat(node.get("y")).getDefaultFloat(0.0);
- var advance:Float = Std.parseFloat(node.get("advance"));
-
- // register character as component
- allComponents.set(char, {
- anim: node.firstChild().nodeValue,
- outline: null
- });
-
- letterData.set(char, {
- isDefault: false,
- advance: advance,
- advanceEmpty: Math.isNaN(advance),
- components: [{
- data: allComponents.get(char),
-
- x: xOff,
- y: yOff,
- scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0),
- scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0),
-
- cos: angleCos,
- sin: angleSin
- }]
- });
- case "languageSection": // used to only parse characters if a specific language is selected
- if(!node.exists("langs")) {
- Logs.error(" must have a langs attribute", "Alphabet");
- return;
- }
- var langs = [for (lang in node.get("langs").split(",")) lang.trim()];
- // maybe add a way to toggle this off? like force it to add all
- if (langs.contains(Options.language)) {
- for (langNode in node.elements())
- checkNode(langNode);
- }
- }
- }
-
- function loadFont(value:String):Void {
- __queueResize = true;
-
- var xml:Xml = Xml.parse(Assets.getText(Paths.xml("alphabet/" + value))).firstElement();
-
- // reset old values
- letterData = new Map();
- defaults = {
- var v = new Vector(3);
- v[0] = null;
- v[1] = null;
- v[2] = null;
- v;
- }
- loaded = {
- var v = new Vector(3);
- v[0] = [];
- v[1] = [];
- v[2] = [];
- v;
- }
- failedLetters = [];
-
- defaultAdvance = Std.parseFloat(xml.get("advance")).getDefaultFloat(40.0);
- lineGap = Std.parseFloat(xml.get("lineGap")).getDefaultFloat(75.0);
- fps = Std.parseFloat(xml.get("fps")).getDefaultFloat(24.0);
- forceCase = xml.get("forceCasing").getDefault("none").toLowerCase();
- transformMult = (xml.get("useColorOffsets") == "true") ? 0 : 1;
-
- frames = null;
-
- for (node in xml.elements())
- checkNode(node);
- }
-
- override function destroy():Void {
- originOffset = FlxDestroyUtil.destroy(originOffset);
- failedLetters = [];
- super.destroy();
- }
-
- function get_textWidth():Float {
- if (__queueResize)
- recalcSizes();
- return textWidth;
- }
- function set_textWidth(value:Float):Float {
- if(!__queueResize)
- __queueResize = __forceWidth != value;
- return __forceWidth = value;
- }
-
- function get_textHeight():Float {
- if (__queueResize)
- recalcSizes();
- return textHeight;
- }
-
- function set_text(value:String):String {
- if(!__queueResize)
- __queueResize = text != value;
- return text = value;
- }
-
- function set_font(value:String):String {
- if (font != value) {
- loadFont(font = value);
- }
- return value;
- }
-}
\ No newline at end of file
diff --git a/source/funkin/menus/ui/effects/RegionEffect.hx b/source/funkin/menus/ui/effects/RegionEffect.hx
index 1eac1025..7f99b166 100644
--- a/source/funkin/menus/ui/effects/RegionEffect.hx
+++ b/source/funkin/menus/ui/effects/RegionEffect.hx
@@ -3,7 +3,7 @@ package funkin.menus.ui.effects;
import flixel.util.FlxColor;
class AlphabetRenderData {
- public var parent:NewAlphabet;
+ public var parent:Alphabet;
public var letter:String;
public var offsetX:Float = 0;
public var offsetY:Float = 0;
@@ -13,11 +13,11 @@ class AlphabetRenderData {
public var blue:Float = 0;
public var alpha:Float = 1;
- public function new(parent:NewAlphabet) {
+ public function new(parent:Alphabet) {
this.parent = parent;
}
- public function reset(parent:NewAlphabet, red:Float, green:Float, blue:Float, alpha:Float, letter:String) {
+ public function reset(parent:Alphabet, red:Float, green:Float, blue:Float, alpha:Float, letter:String) {
this.parent = parent;
this.letter = letter;
this.offsetX = 0;
diff --git a/source/funkin/options/keybinds/KeybindSetting.hx b/source/funkin/options/keybinds/KeybindSetting.hx
index 191c3233..c793e35a 100644
--- a/source/funkin/options/keybinds/KeybindSetting.hx
+++ b/source/funkin/options/keybinds/KeybindSetting.hx
@@ -20,7 +20,7 @@ class KeybindSetting extends FlxTypedSpriteGroup {
public function new(x:Float, y:Float, name:String, value:String, ?sparrowIcon:String, ?sparrowAnim:String) {
super();
this.value = value;
- title = new Alphabet(0, 0, name, true);
+ title = new Alphabet(0, 0, name, "bold");
add(title);
var controlArrayP1:Array = Reflect.field(Options, 'P1_${value}');
@@ -31,12 +31,13 @@ class KeybindSetting extends FlxTypedSpriteGroup {
for(i in 1...3) {
var b = null;
+ var bx = FlxG.width * (0.25 * (i+1)) - x;
if (i == 1)
- b = bind1 = new Alphabet(0, 0, "", false);
+ b = bind1 = new Alphabet(bx, 0, "", "normal");
else
- b = bind2 = new Alphabet(0, 0, "", false);
+ b = bind2 = new Alphabet(bx, 0, "", "normal");
- b.setPosition(FlxG.width * (0.25 * (i+1)) - x, -60);
+ //b.setPosition(FlxG.width * (0.25 * (i+1)) - x, -60);
add(b);
}
updateText();
diff --git a/source/funkin/options/keybinds/KeybindsOptions.hx b/source/funkin/options/keybinds/KeybindsOptions.hx
index b7ced2ee..56e8f0f5 100644
--- a/source/funkin/options/keybinds/KeybindsOptions.hx
+++ b/source/funkin/options/keybinds/KeybindsOptions.hx
@@ -19,7 +19,7 @@ class KeybindsOptions extends MusicBeatSubstate {
public var categories:Array = [];
public function translate(id:String, ?args:Array)
- return TU.translate("Keybinds." + id, args);
+ return TU.translate("KeybindsOptions." + id, args);
public var settingCam:FlxCamera;
@@ -148,7 +148,7 @@ class KeybindsOptions extends MusicBeatSubstate {
var k:Int = 0;
for(category in categories) {
k++;
- var title = new Alphabet(0, k * 75, category.name, true);
+ var title = new Alphabet(0, k * 75, category.name, "bold");
title.screenCenter(X);
add(title);
diff --git a/source/funkin/options/type/ArrayOption.hx b/source/funkin/options/type/ArrayOption.hx
index 5a1496ec..cede15dc 100644
--- a/source/funkin/options/type/ArrayOption.hx
+++ b/source/funkin/options/type/ArrayOption.hx
@@ -37,8 +37,8 @@ class ArrayOption extends OptionType {
this.optionName = optionName;
- add(__text = new Alphabet(100, 20, text, true));
- add(__selectiontext = new Alphabet(__text.width + 120, -30, formatTextOption(), false));
+ add(__text = new Alphabet(100, 20, text, "bold"));
+ add(__selectiontext = new Alphabet(__text.width + 120, 20, formatTextOption(), "normal"));
rawText = text;
}
diff --git a/source/funkin/options/type/NumOption.hx b/source/funkin/options/type/NumOption.hx
index 17c7ff80..10608350 100644
--- a/source/funkin/options/type/NumOption.hx
+++ b/source/funkin/options/type/NumOption.hx
@@ -39,8 +39,8 @@ class NumOption extends OptionType {
this.changeVal = changeVal;
this.optionName = optionName;
- add(__text = new Alphabet(100, 20, text, true));
- add(__number = new Alphabet(__text.width + 120, -30, ': $currentSelection', false));
+ add(__text = new Alphabet(100, 20, text, "bold"));
+ add(__number = new Alphabet(__text.width + 120, 20, ': $currentSelection', "normal"));
rawText = text;
}
diff --git a/source/funkin/options/type/TextOption.hx b/source/funkin/options/type/TextOption.hx
index b16d285d..8f7ec08d 100644
--- a/source/funkin/options/type/TextOption.hx
+++ b/source/funkin/options/type/TextOption.hx
@@ -20,7 +20,7 @@ class TextOption extends OptionType {
public function new(text:String, desc:String, ?suffix:String = "", ?selectCallback:Void->Void = null) {
super(desc);
this.selectCallback = selectCallback;
- add(__text = new Alphabet(100, 20, text, true));
+ add(__text = new Alphabet(100, 20, text, "bold"));
this.suffix = suffix;
rawText = text;
}
diff --git a/source/lime/system/System.hx b/source/lime/system/System.hx
index 98efafe3..2184f5fe 100644
--- a/source/lime/system/System.hx
+++ b/source/lime/system/System.hx
@@ -367,7 +367,10 @@ class System
#elseif mac
Sys.command("/usr/bin/open", [path]);
#elseif linux
- Sys.command("/usr/bin/xdg-open", [path]);
+ // generally `xdg-open` should work in every distro
+ var cmd = Sys.command("xdg-open", [path, "&"]);
+ // run old command JUST IN CASE it fails, which it shouldn't
+ if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [path, "&"]);
#elseif (js && html5)
Browser.window.open(path, "_blank");
#elseif flash
@@ -602,9 +605,7 @@ class System
{
// executes first!!
#if (sys && !macro)
- funkin.backend.utils.NativeAPI.registerAsDPICompatible();
- funkin.backend.system.CommandLineHandler.parseCommandLine(Sys.args());
- funkin.backend.system.Main.fixWorkingDirectory();
+ funkin.backend.system.Main.preInit();
#end
if (__applicationEntryPoint == null)