diff --git a/Project.xml b/Project.xml
index 3572496c25..192a2dc7d3 100644
--- a/Project.xml
+++ b/Project.xml
@@ -75,26 +75,26 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -122,9 +122,10 @@
-
+
+
@@ -177,6 +178,21 @@
-
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index d4ad6bcdc6..8b14b23cd9 100644
--- a/README.md
+++ b/README.md
@@ -62,8 +62,9 @@ package;
class APIStuff
{
- public static var API:String = "";
- public static var EncKey:String = "";
+ inline public static var API:String = "51348:TtzK0rZ8";
+ inline public static var EncKey:String = "5NqKsSVSNKHbF9fPgZPqPg==";
+ inline public static var SESSION:String = null;
}
```
diff --git a/art/flashFiles/FNF_main_menu_assets.fla b/art/flashFiles/FNF_main_menu_assets.fla
index af724a866a..d143b18695 100644
Binary files a/art/flashFiles/FNF_main_menu_assets.fla and b/art/flashFiles/FNF_main_menu_assets.fla differ
diff --git a/assets/preload/images/FNF_main_menu_assets-2019.png b/assets/preload/images/FNF_main_menu_assets-2019.png
new file mode 100644
index 0000000000..ccecc93eb9
Binary files /dev/null and b/assets/preload/images/FNF_main_menu_assets-2019.png differ
diff --git a/assets/preload/images/FNF_main_menu_assets-2019.xml b/assets/preload/images/FNF_main_menu_assets-2019.xml
new file mode 100644
index 0000000000..fac33b181d
--- /dev/null
+++ b/assets/preload/images/FNF_main_menu_assets-2019.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/preload/images/FNF_main_menu_assets.json b/assets/preload/images/FNF_main_menu_assets.json
new file mode 100644
index 0000000000..8da9568cec
--- /dev/null
+++ b/assets/preload/images/FNF_main_menu_assets.json
@@ -0,0 +1,587 @@
+{"frames": {
+
+"donate basic0000":
+{
+ "frame": {"x":0,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate basic0001":
+{
+ "frame": {"x":0,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate basic0002":
+{
+ "frame": {"x":0,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate basic0003":
+{
+ "frame": {"x":444,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate basic0004":
+{
+ "frame": {"x":444,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate basic0005":
+{
+ "frame": {"x":444,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate basic0006":
+{
+ "frame": {"x":888,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate basic0007":
+{
+ "frame": {"x":888,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate basic0008":
+{
+ "frame": {"x":888,"y":0,"w":444,"h":117},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":444,"h":117},
+ "sourceSize": {"w":444,"h":117}
+},
+"donate white0000":
+{
+ "frame": {"x":1332,"y":0,"w":590,"h":157},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":2,"w":590,"h":159},
+ "sourceSize": {"w":590,"h":159}
+},
+"donate white0001":
+{
+ "frame": {"x":0,"y":157,"w":587,"h":154},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":1,"y":5,"w":590,"h":159},
+ "sourceSize": {"w":590,"h":159}
+},
+"donate white0002":
+{
+ "frame": {"x":587,"y":157,"w":585,"h":155},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":3,"y":0,"w":590,"h":159},
+ "sourceSize": {"w":590,"h":159}
+},
+"freeplay basic0000":
+{
+ "frame": {"x":1172,"y":157,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay basic0001":
+{
+ "frame": {"x":1172,"y":157,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay basic0002":
+{
+ "frame": {"x":1172,"y":157,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay basic0003":
+{
+ "frame": {"x":0,"y":312,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay basic0004":
+{
+ "frame": {"x":0,"y":312,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay basic0005":
+{
+ "frame": {"x":0,"y":312,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay basic0006":
+{
+ "frame": {"x":484,"y":312,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay basic0007":
+{
+ "frame": {"x":484,"y":312,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay basic0008":
+{
+ "frame": {"x":484,"y":312,"w":484,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":484,"h":122},
+ "sourceSize": {"w":484,"h":122}
+},
+"freeplay white0000":
+{
+ "frame": {"x":968,"y":312,"w":627,"h":169},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":635,"h":174},
+ "sourceSize": {"w":635,"h":174}
+},
+"freeplay white0001":
+{
+ "frame": {"x":0,"y":481,"w":632,"h":170},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":3,"y":1,"w":635,"h":174},
+ "sourceSize": {"w":635,"h":174}
+},
+"freeplay white0002":
+{
+ "frame": {"x":632,"y":481,"w":629,"h":173},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":4,"y":1,"w":635,"h":174},
+ "sourceSize": {"w":635,"h":174}
+},
+"login basic0000":
+{
+ "frame": {"x":1261,"y":481,"w":359,"h":109},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login basic0001":
+{
+ "frame": {"x":1261,"y":481,"w":359,"h":109},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login basic0002":
+{
+ "frame": {"x":1261,"y":481,"w":359,"h":109},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login basic0003":
+{
+ "frame": {"x":1620,"y":481,"w":359,"h":109},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login basic0004":
+{
+ "frame": {"x":1620,"y":481,"w":359,"h":109},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login basic0005":
+{
+ "frame": {"x":1620,"y":481,"w":359,"h":109},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login basic0006":
+{
+ "frame": {"x":0,"y":654,"w":358,"h":109},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login basic0007":
+{
+ "frame": {"x":0,"y":654,"w":358,"h":109},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login basic0008":
+{
+ "frame": {"x":0,"y":654,"w":358,"h":109},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":359,"h":109},
+ "sourceSize": {"w":359,"h":109}
+},
+"login white0000":
+{
+ "frame": {"x":358,"y":654,"w":449,"h":137},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":449,"h":137},
+ "sourceSize": {"w":449,"h":137}
+},
+"login white0001":
+{
+ "frame": {"x":807,"y":654,"w":449,"h":137},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":449,"h":137},
+ "sourceSize": {"w":449,"h":137}
+},
+"login white0002":
+{
+ "frame": {"x":1256,"y":654,"w":448,"h":137},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":449,"h":137},
+ "sourceSize": {"w":449,"h":137}
+},
+"logout basic0000":
+{
+ "frame": {"x":0,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout basic0001":
+{
+ "frame": {"x":0,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout basic0002":
+{
+ "frame": {"x":0,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout basic0003":
+{
+ "frame": {"x":428,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout basic0004":
+{
+ "frame": {"x":428,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout basic0005":
+{
+ "frame": {"x":428,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout basic0006":
+{
+ "frame": {"x":856,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout basic0007":
+{
+ "frame": {"x":856,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout basic0008":
+{
+ "frame": {"x":856,"y":791,"w":428,"h":110},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":428,"h":110},
+ "sourceSize": {"w":428,"h":110}
+},
+"logout white0000":
+{
+ "frame": {"x":1284,"y":791,"w":537,"h":138},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":538,"h":138},
+ "sourceSize": {"w":538,"h":138}
+},
+"logout white0001":
+{
+ "frame": {"x":0,"y":929,"w":538,"h":138},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":538,"h":138},
+ "sourceSize": {"w":538,"h":138}
+},
+"logout white0002":
+{
+ "frame": {"x":538,"y":929,"w":536,"h":138},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":1,"y":0,"w":538,"h":138},
+ "sourceSize": {"w":538,"h":138}
+},
+"options basic0000":
+{
+ "frame": {"x":1074,"y":929,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options basic0001":
+{
+ "frame": {"x":1074,"y":929,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options basic0002":
+{
+ "frame": {"x":1074,"y":929,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options basic0003":
+{
+ "frame": {"x":1561,"y":929,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options basic0004":
+{
+ "frame": {"x":1561,"y":929,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options basic0005":
+{
+ "frame": {"x":1561,"y":929,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options basic0006":
+{
+ "frame": {"x":0,"y":1067,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options basic0007":
+{
+ "frame": {"x":0,"y":1067,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options basic0008":
+{
+ "frame": {"x":0,"y":1067,"w":487,"h":112},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":487,"h":112},
+ "sourceSize": {"w":487,"h":112}
+},
+"options white0000":
+{
+ "frame": {"x":487,"y":1067,"w":607,"h":155},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":1,"y":1,"w":609,"h":163},
+ "sourceSize": {"w":609,"h":163}
+},
+"options white0001":
+{
+ "frame": {"x":1094,"y":1067,"w":606,"h":158},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":3,"y":1,"w":609,"h":163},
+ "sourceSize": {"w":609,"h":163}
+},
+"options white0002":
+{
+ "frame": {"x":0,"y":1225,"w":609,"h":163},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":609,"h":163},
+ "sourceSize": {"w":609,"h":163}
+},
+"story mode basic0000":
+{
+ "frame": {"x":609,"y":1225,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode basic0001":
+{
+ "frame": {"x":609,"y":1225,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode basic0002":
+{
+ "frame": {"x":609,"y":1225,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode basic0003":
+{
+ "frame": {"x":1224,"y":1225,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode basic0004":
+{
+ "frame": {"x":1224,"y":1225,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode basic0005":
+{
+ "frame": {"x":1224,"y":1225,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode basic0006":
+{
+ "frame": {"x":0,"y":1388,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode basic0007":
+{
+ "frame": {"x":0,"y":1388,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode basic0008":
+{
+ "frame": {"x":0,"y":1388,"w":615,"h":122},
+ "rotated": false,
+ "trimmed": false,
+ "spriteSourceSize": {"x":0,"y":0,"w":615,"h":122},
+ "sourceSize": {"w":615,"h":122}
+},
+"story mode white0000":
+{
+ "frame": {"x":615,"y":1388,"w":796,"h":173},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":3,"w":796,"h":181},
+ "sourceSize": {"w":796,"h":181}
+},
+"story mode white0001":
+{
+ "frame": {"x":0,"y":1561,"w":794,"h":174},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":2,"y":2,"w":796,"h":181},
+ "sourceSize": {"w":796,"h":181}
+},
+"story mode white0002":
+{
+ "frame": {"x":794,"y":1561,"w":794,"h":181},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":796,"h":181},
+ "sourceSize": {"w":796,"h":181}
+}},
+"meta": {
+ "app": "Adobe Animate",
+ "version": "19.2.1.408",
+ "image": "FNF_main_menu_assets.png",
+ "format": "RGBA8888",
+ "size": {"w":2048,"h":2048},
+ "scale": "1"
+}
+}
diff --git a/assets/preload/images/FNF_main_menu_assets.png b/assets/preload/images/FNF_main_menu_assets.png
index 4399ed2dd0..bd0ffbd0e4 100644
Binary files a/assets/preload/images/FNF_main_menu_assets.png and b/assets/preload/images/FNF_main_menu_assets.png differ
diff --git a/assets/preload/images/FNF_main_menu_assets.xml b/assets/preload/images/FNF_main_menu_assets.xml
index 01d1a5eb48..fe5bb0c514 100644
--- a/assets/preload/images/FNF_main_menu_assets.xml
+++ b/assets/preload/images/FNF_main_menu_assets.xml
@@ -1,53 +1,77 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/preload/images/fonts/bold.fla b/assets/preload/images/fonts/bold.fla
new file mode 100644
index 0000000000..f98b5865c1
Binary files /dev/null and b/assets/preload/images/fonts/bold.fla differ
diff --git a/assets/preload/images/fonts/bold.png b/assets/preload/images/fonts/bold.png
new file mode 100644
index 0000000000..a8d80f55bc
Binary files /dev/null and b/assets/preload/images/fonts/bold.png differ
diff --git a/assets/preload/images/fonts/bold.xml b/assets/preload/images/fonts/bold.xml
new file mode 100644
index 0000000000..0d3578fed4
--- /dev/null
+++ b/assets/preload/images/fonts/bold.xml
@@ -0,0 +1,257 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/preload/images/fonts/default.fla b/assets/preload/images/fonts/default.fla
new file mode 100644
index 0000000000..07d6c4e441
Binary files /dev/null and b/assets/preload/images/fonts/default.fla differ
diff --git a/assets/preload/images/fonts/default.png b/assets/preload/images/fonts/default.png
new file mode 100644
index 0000000000..3f82b1244b
Binary files /dev/null and b/assets/preload/images/fonts/default.png differ
diff --git a/assets/preload/images/fonts/default.xml b/assets/preload/images/fonts/default.xml
new file mode 100644
index 0000000000..1f08703993
--- /dev/null
+++ b/assets/preload/images/fonts/default.xml
@@ -0,0 +1,401 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/preload/images/main_menu.fla b/assets/preload/images/main_menu.fla
new file mode 100644
index 0000000000..5f6d77456f
Binary files /dev/null and b/assets/preload/images/main_menu.fla differ
diff --git a/assets/preload/images/main_menu.png b/assets/preload/images/main_menu.png
new file mode 100644
index 0000000000..a2267f5f97
Binary files /dev/null and b/assets/preload/images/main_menu.png differ
diff --git a/assets/preload/images/main_menu.xml b/assets/preload/images/main_menu.xml
new file mode 100644
index 0000000000..1ff056ceca
--- /dev/null
+++ b/assets/preload/images/main_menu.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/preload/images/prompt-ng_login.fla b/assets/preload/images/prompt-ng_login.fla
new file mode 100644
index 0000000000..ead7b53db4
Binary files /dev/null and b/assets/preload/images/prompt-ng_login.fla differ
diff --git a/assets/preload/images/prompt-ng_login.png b/assets/preload/images/prompt-ng_login.png
new file mode 100644
index 0000000000..d7cba070e0
Binary files /dev/null and b/assets/preload/images/prompt-ng_login.png differ
diff --git a/assets/preload/images/prompt-ng_login.xml b/assets/preload/images/prompt-ng_login.xml
new file mode 100644
index 0000000000..351b2b10eb
--- /dev/null
+++ b/assets/preload/images/prompt-ng_login.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/Alphabet.hx b/source/Alphabet.hx
index 77ac5dc8eb..6fca9d0a55 100644
--- a/source/Alphabet.hx
+++ b/source/Alphabet.hx
@@ -40,7 +40,7 @@ class Alphabet extends FlxSpriteGroup
var isBold:Bool = false;
- public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false)
+ public function new(x:Float = 0.0, y:Float = 0.0, text:String = "", ?bold:Bool = false, typed:Bool = false)
{
super(x, y);
diff --git a/source/Controls.hx b/source/Controls.hx
index 7d1e2c40e9..760fc0af0c 100644
--- a/source/Controls.hx
+++ b/source/Controls.hx
@@ -11,50 +11,66 @@ import flixel.input.gamepad.FlxGamepadButton;
import flixel.input.gamepad.FlxGamepadInputID;
import flixel.input.keyboard.FlxKey;
-#if (haxe >= "4.0.0")
-enum abstract Action(String) to String from String
+/**
+ * Since, in many cases multiple actions should use similar keys, we don't want the
+ * rebinding UI to list every action. ActionBinders are what the user percieves as
+ * an input so, for instance, they can't set jump-press and jump-release to different keys.
+ */
+enum Control
{
- var UP = "up";
- var LEFT = "left";
- var RIGHT = "right";
- var DOWN = "down";
- var UP_P = "up-press";
- var LEFT_P = "left-press";
- var RIGHT_P = "right-press";
- var DOWN_P = "down-press";
- var UP_R = "up-release";
- var LEFT_R = "left-release";
- var RIGHT_R = "right-release";
- var DOWN_R = "down-release";
- var ACCEPT = "accept";
- var BACK = "back";
- var PAUSE = "pause";
- var RESET = "reset";
- var CHEAT = "cheat";
+ // List notes in order from left to right on gameplay screen.
+ NOTE_LEFT;
+ NOTE_DOWN;
+ NOTE_UP;
+ NOTE_RIGHT;
+ UI_UP;
+ UI_LEFT;
+ UI_RIGHT;
+ UI_DOWN;
+ RESET;
+ ACCEPT;
+ BACK;
+ PAUSE;
+ #if CAN_CHEAT
+ CHEAT;
+ #end
}
-#else
+
@:enum
abstract Action(String) to String from String
{
- var UP = "up";
- var LEFT = "left";
- var RIGHT = "right";
- var DOWN = "down";
- var UP_P = "up-press";
- var LEFT_P = "left-press";
- var RIGHT_P = "right-press";
- var DOWN_P = "down-press";
- var UP_R = "up-release";
- var LEFT_R = "left-release";
- var RIGHT_R = "right-release";
- var DOWN_R = "down-release";
+ var UI_UP = "ui_up";
+ var UI_LEFT = "ui_left";
+ var UI_RIGHT = "ui_right";
+ var UI_DOWN = "ui_down";
+ var UI_UP_P = "ui_up-press";
+ var UI_LEFT_P = "ui_left-press";
+ var UI_RIGHT_P = "ui_right-press";
+ var UI_DOWN_P = "ui_down-press";
+ var UI_UP_R = "ui_up-release";
+ var UI_LEFT_R = "ui_left-release";
+ var UI_RIGHT_R = "ui_right-release";
+ var UI_DOWN_R = "ui_down-release";
+ var NOTE_UP = "note_up";
+ var NOTE_LEFT = "note_left";
+ var NOTE_RIGHT = "note_right";
+ var NOTE_DOWN = "note_down";
+ var NOTE_UP_P = "note_up-press";
+ var NOTE_LEFT_P = "note_left-press";
+ var NOTE_RIGHT_P = "note_right-press";
+ var NOTE_DOWN_P = "note_down-press";
+ var NOTE_UP_R = "note_up-release";
+ var NOTE_LEFT_R = "note_left-release";
+ var NOTE_RIGHT_R = "note_right-release";
+ var NOTE_DOWN_R = "note_down-release";
var ACCEPT = "accept";
var BACK = "back";
var PAUSE = "pause";
var RESET = "reset";
+ #if CAN_CHEAT
var CHEAT = "cheat";
+ #end
}
-#end
enum Device
{
@@ -62,24 +78,6 @@ enum Device
Gamepad(id:Int);
}
-/**
- * Since, in many cases multiple actions should use similar keys, we don't want the
- * rebinding UI to list every action. ActionBinders are what the user percieves as
- * an input so, for instance, they can't set jump-press and jump-release to different keys.
- */
-enum Control
-{
- UP;
- LEFT;
- RIGHT;
- DOWN;
- RESET;
- ACCEPT;
- BACK;
- PAUSE;
- CHEAT;
-}
-
enum KeyboardScheme
{
Solo;
@@ -94,177 +92,125 @@ enum KeyboardScheme
*/
class Controls extends FlxActionSet
{
- var _up = new FlxActionDigital(Action.UP);
- var _left = new FlxActionDigital(Action.LEFT);
- var _right = new FlxActionDigital(Action.RIGHT);
- var _down = new FlxActionDigital(Action.DOWN);
- var _upP = new FlxActionDigital(Action.UP_P);
- var _leftP = new FlxActionDigital(Action.LEFT_P);
- var _rightP = new FlxActionDigital(Action.RIGHT_P);
- var _downP = new FlxActionDigital(Action.DOWN_P);
- var _upR = new FlxActionDigital(Action.UP_R);
- var _leftR = new FlxActionDigital(Action.LEFT_R);
- var _rightR = new FlxActionDigital(Action.RIGHT_R);
- var _downR = new FlxActionDigital(Action.DOWN_R);
+ var _ui_up = new FlxActionDigital(Action.UI_UP);
+ var _ui_left = new FlxActionDigital(Action.UI_LEFT);
+ var _ui_right = new FlxActionDigital(Action.UI_RIGHT);
+ var _ui_down = new FlxActionDigital(Action.UI_DOWN);
+ var _ui_upP = new FlxActionDigital(Action.UI_UP_P);
+ var _ui_leftP = new FlxActionDigital(Action.UI_LEFT_P);
+ var _ui_rightP = new FlxActionDigital(Action.UI_RIGHT_P);
+ var _ui_downP = new FlxActionDigital(Action.UI_DOWN_P);
+ var _ui_upR = new FlxActionDigital(Action.UI_UP_R);
+ var _ui_leftR = new FlxActionDigital(Action.UI_LEFT_R);
+ var _ui_rightR = new FlxActionDigital(Action.UI_RIGHT_R);
+ var _ui_downR = new FlxActionDigital(Action.UI_DOWN_R);
+ var _note_up = new FlxActionDigital(Action.NOTE_UP);
+ var _note_left = new FlxActionDigital(Action.NOTE_LEFT);
+ var _note_right = new FlxActionDigital(Action.NOTE_RIGHT);
+ var _note_down = new FlxActionDigital(Action.NOTE_DOWN);
+ var _note_upP = new FlxActionDigital(Action.NOTE_UP_P);
+ var _note_leftP = new FlxActionDigital(Action.NOTE_LEFT_P);
+ var _note_rightP = new FlxActionDigital(Action.NOTE_RIGHT_P);
+ var _note_downP = new FlxActionDigital(Action.NOTE_DOWN_P);
+ var _note_upR = new FlxActionDigital(Action.NOTE_UP_R);
+ var _note_leftR = new FlxActionDigital(Action.NOTE_LEFT_R);
+ var _note_rightR = new FlxActionDigital(Action.NOTE_RIGHT_R);
+ var _note_downR = new FlxActionDigital(Action.NOTE_DOWN_R);
var _accept = new FlxActionDigital(Action.ACCEPT);
var _back = new FlxActionDigital(Action.BACK);
var _pause = new FlxActionDigital(Action.PAUSE);
var _reset = new FlxActionDigital(Action.RESET);
+ #if CAN_CHEAT
var _cheat = new FlxActionDigital(Action.CHEAT);
-
- #if (haxe >= "4.0.0")
- var byName:Map = [];
- #else
- var byName:Map = new Map();
#end
+
+ var byName:Map = new Map();
public var gamepadsAdded:Array = [];
public var keyboardScheme = KeyboardScheme.None;
- public var UP(get, never):Bool;
-
- inline function get_UP()
- return _up.check();
-
- public var LEFT(get, never):Bool;
-
- inline function get_LEFT()
- return _left.check();
-
- public var RIGHT(get, never):Bool;
-
- inline function get_RIGHT()
- return _right.check();
-
- public var DOWN(get, never):Bool;
-
- inline function get_DOWN()
- return _down.check();
-
- public var UP_P(get, never):Bool;
-
- inline function get_UP_P()
- return _upP.check();
-
- public var LEFT_P(get, never):Bool;
-
- inline function get_LEFT_P()
- return _leftP.check();
-
- public var RIGHT_P(get, never):Bool;
-
- inline function get_RIGHT_P()
- return _rightP.check();
-
- public var DOWN_P(get, never):Bool;
-
- inline function get_DOWN_P()
- return _downP.check();
-
- public var UP_R(get, never):Bool;
-
- inline function get_UP_R()
- return _upR.check();
-
- public var LEFT_R(get, never):Bool;
-
- inline function get_LEFT_R()
- return _leftR.check();
-
- public var RIGHT_R(get, never):Bool;
-
- inline function get_RIGHT_R()
- return _rightR.check();
-
- public var DOWN_R(get, never):Bool;
-
- inline function get_DOWN_R()
- return _downR.check();
-
- public var ACCEPT(get, never):Bool;
-
- inline function get_ACCEPT()
- return _accept.check();
-
- public var BACK(get, never):Bool;
-
- inline function get_BACK()
- return _back.check();
-
- public var PAUSE(get, never):Bool;
-
- inline function get_PAUSE()
- return _pause.check();
-
- public var RESET(get, never):Bool;
-
- inline function get_RESET()
- return _reset.check();
-
- public var CHEAT(get, never):Bool;
-
- inline function get_CHEAT()
- return _cheat.check();
-
- #if (haxe >= "4.0.0")
- public function new(name, scheme = None)
- {
- super(name);
-
- add(_up);
- add(_left);
- add(_right);
- add(_down);
- add(_upP);
- add(_leftP);
- add(_rightP);
- add(_downP);
- add(_upR);
- add(_leftR);
- add(_rightR);
- add(_downR);
- add(_accept);
- add(_back);
- add(_pause);
- add(_reset);
- add(_cheat);
-
- for (action in digitalActions)
- byName[action.name] = action;
-
- setKeyboardScheme(scheme, false);
- }
- #else
+ public var UI_UP (get, never):Bool; inline function get_UI_UP () return _ui_up .check();
+ public var UI_LEFT (get, never):Bool; inline function get_UI_LEFT () return _ui_left .check();
+ public var UI_RIGHT(get, never):Bool; inline function get_UI_RIGHT() return _ui_right.check();
+ public var UI_DOWN (get, never):Bool; inline function get_UI_DOWN () return _ui_down .check();
+
+ public var UI_UP_P (get, never):Bool; inline function get_UI_UP_P () return _ui_upP .check();
+ public var UI_LEFT_P (get, never):Bool; inline function get_UI_LEFT_P () return _ui_leftP .check();
+ public var UI_RIGHT_P(get, never):Bool; inline function get_UI_RIGHT_P() return _ui_rightP.check();
+ public var UI_DOWN_P (get, never):Bool; inline function get_UI_DOWN_P () return _ui_downP .check();
+
+ public var UI_UP_R (get, never):Bool; inline function get_UI_UP_R () return _ui_upR .check();
+ public var UI_LEFT_R (get, never):Bool; inline function get_UI_LEFT_R () return _ui_leftR .check();
+ public var UI_RIGHT_R(get, never):Bool; inline function get_UI_RIGHT_R() return _ui_rightR.check();
+ public var UI_DOWN_R (get, never):Bool; inline function get_UI_DOWN_R () return _ui_downR .check();
+
+ public var NOTE_UP (get, never):Bool; inline function get_NOTE_UP () return _note_up .check();
+ public var NOTE_LEFT (get, never):Bool; inline function get_NOTE_LEFT () return _note_left .check();
+ public var NOTE_RIGHT(get, never):Bool; inline function get_NOTE_RIGHT() return _note_right.check();
+ public var NOTE_DOWN (get, never):Bool; inline function get_NOTE_DOWN () return _note_down .check();
+
+ public var NOTE_UP_P (get, never):Bool; inline function get_NOTE_UP_P () return _note_upP .check();
+ public var NOTE_LEFT_P (get, never):Bool; inline function get_NOTE_LEFT_P () return _note_leftP .check();
+ public var NOTE_RIGHT_P(get, never):Bool; inline function get_NOTE_RIGHT_P() return _note_rightP.check();
+ public var NOTE_DOWN_P (get, never):Bool; inline function get_NOTE_DOWN_P () return _note_downP .check();
+
+ public var NOTE_UP_R (get, never):Bool; inline function get_NOTE_UP_R () return _note_upR .check();
+ public var NOTE_LEFT_R (get, never):Bool; inline function get_NOTE_LEFT_R () return _note_leftR .check();
+ public var NOTE_RIGHT_R(get, never):Bool; inline function get_NOTE_RIGHT_R() return _note_rightR.check();
+ public var NOTE_DOWN_R (get, never):Bool; inline function get_NOTE_DOWN_R () return _note_downR .check();
+
+ public var ACCEPT(get, never):Bool; inline function get_ACCEPT() return _accept.check();
+ public var BACK (get, never):Bool; inline function get_BACK () return _back .check();
+ public var PAUSE (get, never):Bool; inline function get_PAUSE () return _pause .check();
+ public var RESET (get, never):Bool; inline function get_RESET () return _reset .check();
+ #if CAN_CHEAT
+ public var CHEAT (get, never):Bool; inline function get_CHEAT () return _cheat.check ();
+ #end
+
public function new(name, scheme:KeyboardScheme = null)
{
super(name);
- add(_up);
- add(_left);
- add(_right);
- add(_down);
- add(_upP);
- add(_leftP);
- add(_rightP);
- add(_downP);
- add(_upR);
- add(_leftR);
- add(_rightR);
- add(_downR);
+ add(_ui_up);
+ add(_ui_left);
+ add(_ui_right);
+ add(_ui_down);
+ add(_ui_upP);
+ add(_ui_leftP);
+ add(_ui_rightP);
+ add(_ui_downP);
+ add(_ui_upR);
+ add(_ui_leftR);
+ add(_ui_rightR);
+ add(_ui_downR);
+ add(_note_up);
+ add(_note_left);
+ add(_note_right);
+ add(_note_down);
+ add(_note_upP);
+ add(_note_leftP);
+ add(_note_rightP);
+ add(_note_downP);
+ add(_note_upR);
+ add(_note_leftR);
+ add(_note_rightR);
+ add(_note_downR);
add(_accept);
add(_back);
add(_pause);
add(_reset);
+ #if CAN_CHEAT
add(_cheat);
+ #end
for (action in digitalActions)
byName[action.name] = action;
-
+
if (scheme == null)
scheme = None;
+
setKeyboardScheme(scheme, false);
}
- #end
override function update()
{
@@ -301,15 +247,21 @@ class Controls extends FlxActionSet
{
return switch (control)
{
- case UP: _up;
- case DOWN: _down;
- case LEFT: _left;
- case RIGHT: _right;
+ case UI_UP: _ui_up;
+ case UI_DOWN: _ui_down;
+ case UI_LEFT: _ui_left;
+ case UI_RIGHT: _ui_right;
+ case NOTE_UP: _note_up;
+ case NOTE_DOWN: _note_down;
+ case NOTE_LEFT: _note_left;
+ case NOTE_RIGHT: _note_right;
case ACCEPT: _accept;
case BACK: _back;
case PAUSE: _pause;
case RESET: _reset;
+ #if CAN_CHEAT
case CHEAT: _cheat;
+ #end
}
}
@@ -329,22 +281,38 @@ class Controls extends FlxActionSet
{
switch (control)
{
- case UP:
- func(_up, PRESSED);
- func(_upP, JUST_PRESSED);
- func(_upR, JUST_RELEASED);
- case LEFT:
- func(_left, PRESSED);
- func(_leftP, JUST_PRESSED);
- func(_leftR, JUST_RELEASED);
- case RIGHT:
- func(_right, PRESSED);
- func(_rightP, JUST_PRESSED);
- func(_rightR, JUST_RELEASED);
- case DOWN:
- func(_down, PRESSED);
- func(_downP, JUST_PRESSED);
- func(_downR, JUST_RELEASED);
+ case UI_UP:
+ func(_ui_up, PRESSED);
+ func(_ui_upP, JUST_PRESSED);
+ func(_ui_upR, JUST_RELEASED);
+ case UI_LEFT:
+ func(_ui_left, PRESSED);
+ func(_ui_leftP, JUST_PRESSED);
+ func(_ui_leftR, JUST_RELEASED);
+ case UI_RIGHT:
+ func(_ui_right, PRESSED);
+ func(_ui_rightP, JUST_PRESSED);
+ func(_ui_rightR, JUST_RELEASED);
+ case UI_DOWN:
+ func(_ui_down, PRESSED);
+ func(_ui_downP, JUST_PRESSED);
+ func(_ui_downR, JUST_RELEASED);
+ case NOTE_UP:
+ func(_note_up, PRESSED);
+ func(_note_upP, JUST_PRESSED);
+ func(_note_upR, JUST_RELEASED);
+ case NOTE_LEFT:
+ func(_note_left, PRESSED);
+ func(_note_leftP, JUST_PRESSED);
+ func(_note_leftR, JUST_RELEASED);
+ case NOTE_RIGHT:
+ func(_note_right, PRESSED);
+ func(_note_rightP, JUST_PRESSED);
+ func(_note_rightR, JUST_RELEASED);
+ case NOTE_DOWN:
+ func(_note_down, PRESSED);
+ func(_note_downP, JUST_PRESSED);
+ func(_note_downR, JUST_RELEASED);
case ACCEPT:
func(_accept, JUST_PRESSED);
case BACK:
@@ -353,12 +321,14 @@ class Controls extends FlxActionSet
func(_pause, JUST_PRESSED);
case RESET:
func(_reset, JUST_PRESSED);
+ #if CAN_CHEAT
case CHEAT:
func(_cheat, JUST_PRESSED);
+ #end
}
}
- public function replaceBinding(control:Control, device:Device, ?toAdd:Int, ?toRemove:Int)
+ public function replaceBinding(control:Control, device:Device, toAdd:Int, toRemove:Int)
{
if (toAdd == toRemove)
return;
@@ -366,31 +336,41 @@ class Controls extends FlxActionSet
switch (device)
{
case Keys:
- if (toRemove != null)
- unbindKeys(control, [toRemove]);
- if (toAdd != null)
- bindKeys(control, [toAdd]);
+ forEachBound(control, function(action, _) replaceKey(action, toAdd, toRemove));
case Gamepad(id):
- if (toRemove != null)
- unbindButtons(control, id, [toRemove]);
- if (toAdd != null)
- bindButtons(control, id, [toAdd]);
+ forEachBound(control, function(action, _) replaceButton(action, id, toAdd, toRemove));
}
}
-
- public function copyFrom(controls:Controls, ?device:Device)
+
+ function replaceKey(action:FlxActionDigital, toAdd:Int, toRemove:Int)
{
- #if (haxe >= "4.0.0")
- for (name => action in controls.byName)
+ for (i in 0...action.inputs.length)
{
- for (input in action.inputs)
+ var input = action.inputs[i];
+ if (input.device == KEYBOARD && input.inputID == toRemove)
{
- if (device == null || isDevice(input, device))
- byName[name].add(cast input);
+ @:privateAccess
+ action.inputs[i].inputID = toAdd;
+ }
+ }
+ }
+
+ function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:Int, toRemove:Int)
+ {
+ for (i in 0...action.inputs.length)
+ {
+ var input = action.inputs[i];
+ if (isGamepad(input, deviceID) && input.inputID == toRemove)
+ {
+ @:privateAccess
+ action.inputs[i].inputID = toAdd;
}
}
- #else
+ }
+
+ public function copyFrom(controls:Controls, ?device:Device)
+ {
for (name in controls.byName.keys())
{
var action = controls.byName[name];
@@ -400,21 +380,14 @@ class Controls extends FlxActionSet
byName[name].add(cast input);
}
}
- #end
switch (device)
{
case null:
// add all
- #if (haxe >= "4.0.0")
- for (gamepad in controls.gamepadsAdded)
- if (!gamepadsAdded.contains(gamepad))
- gamepadsAdded.push(gamepad);
- #else
for (gamepad in controls.gamepadsAdded)
if (gamepadsAdded.indexOf(gamepad) == -1)
gamepadsAdded.push(gamepad);
- #end
mergeKeyboardScheme(controls.keyboardScheme);
@@ -450,11 +423,7 @@ class Controls extends FlxActionSet
*/
public function bindKeys(control:Control, keys:Array)
{
- #if (haxe >= "4.0.0")
- inline forEachBound(control, (action, state) -> addKeys(action, keys, state));
- #else
forEachBound(control, function(action, state) addKeys(action, keys, state));
- #end
}
/**
@@ -463,11 +432,7 @@ class Controls extends FlxActionSet
*/
public function unbindKeys(control:Control, keys:Array)
{
- #if (haxe >= "4.0.0")
- inline forEachBound(control, (action, _) -> removeKeys(action, keys));
- #else
forEachBound(control, function(action, _) removeKeys(action, keys));
- #end
}
inline static function addKeys(action:FlxActionDigital, keys:Array, state:FlxInputState)
@@ -494,65 +459,43 @@ class Controls extends FlxActionSet
keyboardScheme = scheme;
- #if (haxe >= "4.0.0")
switch (scheme)
{
case Solo:
- inline bindKeys(Control.UP, [W, FlxKey.UP]);
- inline bindKeys(Control.DOWN, [S, FlxKey.DOWN]);
- inline bindKeys(Control.LEFT, [A, FlxKey.LEFT]);
- inline bindKeys(Control.RIGHT, [D, FlxKey.RIGHT]);
- inline bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]);
- inline bindKeys(Control.BACK, [BACKSPACE, ESCAPE]);
- inline bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]);
- inline bindKeys(Control.RESET, [R]);
- case Duo(true):
- inline bindKeys(Control.UP, [W]);
- inline bindKeys(Control.DOWN, [S]);
- inline bindKeys(Control.LEFT, [A]);
- inline bindKeys(Control.RIGHT, [D]);
- inline bindKeys(Control.ACCEPT, [G, Z]);
- inline bindKeys(Control.BACK, [H, X]);
- inline bindKeys(Control.PAUSE, [ONE]);
- inline bindKeys(Control.RESET, [R]);
- case Duo(false):
- inline bindKeys(Control.UP, [FlxKey.UP]);
- inline bindKeys(Control.DOWN, [FlxKey.DOWN]);
- inline bindKeys(Control.LEFT, [FlxKey.LEFT]);
- inline bindKeys(Control.RIGHT, [FlxKey.RIGHT]);
- inline bindKeys(Control.ACCEPT, [O]);
- inline bindKeys(Control.BACK, [P]);
- inline bindKeys(Control.PAUSE, [ENTER]);
- inline bindKeys(Control.RESET, [BACKSPACE]);
- case None: // nothing
- case Custom: // nothing
- }
- #else
- switch (scheme)
- {
- case Solo:
- bindKeys(Control.UP, [W, FlxKey.UP]);
- bindKeys(Control.DOWN, [S, FlxKey.DOWN]);
- bindKeys(Control.LEFT, [A, FlxKey.LEFT]);
- bindKeys(Control.RIGHT, [D, FlxKey.RIGHT]);
+ bindKeys(Control.UI_UP, [W, FlxKey.UP]);
+ bindKeys(Control.UI_DOWN, [S, FlxKey.DOWN]);
+ bindKeys(Control.UI_LEFT, [A, FlxKey.LEFT]);
+ bindKeys(Control.UI_RIGHT, [D, FlxKey.RIGHT]);
+ bindKeys(Control.NOTE_UP, [W, FlxKey.UP]);
+ bindKeys(Control.NOTE_DOWN, [S, FlxKey.DOWN]);
+ bindKeys(Control.NOTE_LEFT, [A, FlxKey.LEFT]);
+ bindKeys(Control.NOTE_RIGHT, [D, FlxKey.RIGHT]);
bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]);
- bindKeys(Control.BACK, [BACKSPACE, ESCAPE]);
+ bindKeys(Control.BACK, [X, BACKSPACE, ESCAPE]);
bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]);
bindKeys(Control.RESET, [R]);
case Duo(true):
- bindKeys(Control.UP, [W]);
- bindKeys(Control.DOWN, [S]);
- bindKeys(Control.LEFT, [A]);
- bindKeys(Control.RIGHT, [D]);
+ bindKeys(Control.UI_UP, [W]);
+ bindKeys(Control.UI_DOWN, [S]);
+ bindKeys(Control.UI_LEFT, [A]);
+ bindKeys(Control.UI_RIGHT, [D]);
+ bindKeys(Control.NOTE_UP, [W]);
+ bindKeys(Control.NOTE_DOWN, [S]);
+ bindKeys(Control.NOTE_LEFT, [A]);
+ bindKeys(Control.NOTE_RIGHT, [D]);
bindKeys(Control.ACCEPT, [G, Z]);
bindKeys(Control.BACK, [H, X]);
bindKeys(Control.PAUSE, [ONE]);
bindKeys(Control.RESET, [R]);
case Duo(false):
- bindKeys(Control.UP, [FlxKey.UP]);
- bindKeys(Control.DOWN, [FlxKey.DOWN]);
- bindKeys(Control.LEFT, [FlxKey.LEFT]);
- bindKeys(Control.RIGHT, [FlxKey.RIGHT]);
+ bindKeys(Control.UI_UP, [FlxKey.UP]);
+ bindKeys(Control.UI_DOWN, [FlxKey.DOWN]);
+ bindKeys(Control.UI_LEFT, [FlxKey.LEFT]);
+ bindKeys(Control.UI_RIGHT, [FlxKey.RIGHT]);
+ bindKeys(Control.NOTE_UP, [FlxKey.UP]);
+ bindKeys(Control.NOTE_DOWN, [FlxKey.DOWN]);
+ bindKeys(Control.NOTE_LEFT, [FlxKey.LEFT]);
+ bindKeys(Control.NOTE_RIGHT, [FlxKey.RIGHT]);
bindKeys(Control.ACCEPT, [O]);
bindKeys(Control.BACK, [P]);
bindKeys(Control.PAUSE, [ENTER]);
@@ -560,7 +503,6 @@ class Controls extends FlxActionSet
case None: // nothing
case Custom: // nothing
}
- #end
}
function removeKeyboard()
@@ -577,30 +519,19 @@ class Controls extends FlxActionSet
}
}
- public function addGamepad(id:Int, ?buttonMap:Map>):Void
+ public function addGamepadWithSaveData(id:Int, ?padData:Dynamic):Void
{
gamepadsAdded.push(id);
- #if (haxe >= "4.0.0")
- for (control => buttons in buttonMap)
- inline bindButtons(control, id, buttons);
- #else
- for (control in buttonMap.keys())
- bindButtons(control, id, buttonMap[control]);
- #end
+ fromSaveData(padData, Gamepad(id));
}
inline function addGamepadLiteral(id:Int, ?buttonMap:Map>):Void
{
gamepadsAdded.push(id);
- #if (haxe >= "4.0.0")
- for (control => buttons in buttonMap)
- inline bindButtons(control, id, buttons);
- #else
for (control in buttonMap.keys())
bindButtons(control, id, buttonMap[control]);
- #end
}
public function removeGamepad(deviceID:Int = FlxInputDeviceID.ALL):Void
@@ -611,7 +542,7 @@ class Controls extends FlxActionSet
while (i-- > 0)
{
var input = action.inputs[i];
- if (input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID))
+ if (isGamepad(input, deviceID))
action.remove(input);
}
}
@@ -621,32 +552,25 @@ class Controls extends FlxActionSet
public function addDefaultGamepad(id):Void
{
- #if !switch
addGamepadLiteral(id, [
- Control.ACCEPT => [A],
- Control.BACK => [B],
- Control.UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP],
- Control.DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN],
- Control.LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT],
- Control.RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT],
+
+ Control.ACCEPT => [#if switch B #else A #end],
+ Control.BACK => [#if switch A #else B #end, FlxGamepadInputID.BACK],
+ Control.UI_UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP],
+ Control.UI_DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN],
+ Control.UI_LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT],
+ Control.UI_RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT],
+ // don't swap A/B or X/Y for switch on these. A is always the bottom face button
+ Control.NOTE_UP => [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP],
+ Control.NOTE_DOWN => [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN],
+ Control.NOTE_LEFT => [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT],
+ Control.NOTE_RIGHT => [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT],
Control.PAUSE => [START],
Control.RESET => [Y]
+ #if CAN_CHEAT
+ ,Control.CHEAT => [X]
+ #end
]);
- #else
- addGamepadLiteral(id, [
- //Swap A and B for switch
- Control.ACCEPT => [B],
- Control.BACK => [A],
- Control.UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP],
- Control.DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN],
- Control.LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT],
- Control.RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT],
- Control.PAUSE => [START],
- //Swap Y and X for switch
- Control.RESET => [Y],
- Control.CHEAT => [X]
- ]);
- #end
}
/**
@@ -655,11 +579,7 @@ class Controls extends FlxActionSet
*/
public function bindButtons(control:Control, id, buttons)
{
- #if (haxe >= "4.0.0")
- inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id));
- #else
forEachBound(control, function(action, state) addButtons(action, buttons, state, id));
- #end
}
/**
@@ -668,11 +588,7 @@ class Controls extends FlxActionSet
*/
public function unbindButtons(control:Control, gamepadID:Int, buttons)
{
- #if (haxe >= "4.0.0")
- inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons));
- #else
forEachBound(control, function(action, _) removeButtons(action, gamepadID, buttons));
- #end
}
inline static function addButtons(action:FlxActionDigital, buttons:Array, state, id)
@@ -708,7 +624,7 @@ class Controls extends FlxActionSet
case Gamepad(id):
for (input in getActionFromControl(control).inputs)
{
- if (input.deviceID == id)
+ if (isGamepad(input, id))
list.push(input.inputID);
}
}
@@ -726,6 +642,37 @@ class Controls extends FlxActionSet
}
}
+ public function fromSaveData(data:Dynamic, device:Device)
+ {
+ for (control in Control.createAll())
+ {
+ var inputs:Array = Reflect.field(data, control.getName());
+ if (inputs != null)
+ {
+ switch(device)
+ {
+ case Keys: bindKeys(control, inputs.copy());
+ case Gamepad(id): bindButtons(control, id, inputs.copy());
+ }
+ }
+ }
+ }
+
+ public function createSaveData(device:Device):Dynamic
+ {
+ var isEmpty = true;
+ var data = {};
+ for (control in Control.createAll())
+ {
+ var inputs = getInputsFor(control, device);
+ isEmpty = isEmpty && inputs.length == 0;
+
+ Reflect.setField(data, control.getName(), inputs);
+ }
+
+ return isEmpty ? null : data;
+ }
+
static function isDevice(input:FlxActionInput, device:Device)
{
return switch device
@@ -740,3 +687,6 @@ class Controls extends FlxActionSet
return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID);
}
}
+
+
+typedef SaveInputLists = {?keys:Array, ?pad:Array};
\ No newline at end of file
diff --git a/source/FreeplayState.hx b/source/FreeplayState.hx
index 802f557f34..4c3e492e38 100644
--- a/source/FreeplayState.hx
+++ b/source/FreeplayState.hx
@@ -201,8 +201,8 @@ class FreeplayState extends MusicBeatState
scoreText.text = "PERSONAL BEST:" + lerpScore;
- var upP = controls.UP_P;
- var downP = controls.DOWN_P;
+ var upP = controls.UI_UP_P;
+ var downP = controls.UI_DOWN_P;
var accepted = controls.ACCEPT;
if (upP)
@@ -214,9 +214,9 @@ class FreeplayState extends MusicBeatState
changeSelection(1);
}
- if (controls.LEFT_P)
+ if (controls.UI_LEFT_P)
changeDiff(-1);
- if (controls.RIGHT_P)
+ if (controls.UI_RIGHT_P)
changeDiff(1);
if (controls.BACK)
@@ -246,9 +246,7 @@ class FreeplayState extends MusicBeatState
if (curDifficulty > 2)
curDifficulty = 0;
- #if !switch
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
- #end
PlayState.storyDifficulty = curDifficulty;
@@ -259,9 +257,7 @@ class FreeplayState extends MusicBeatState
function changeSelection(change:Int = 0)
{
- #if !switch
NGio.logEvent('Fresh');
- #end
// NGio.logEvent('Fresh');
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
@@ -275,10 +271,8 @@ class FreeplayState extends MusicBeatState
// selector.y = (70 * curSelected) + 30;
- #if !switch
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
// lerpScore = 0;
- #end
#if PRELOAD_ALL
FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName), 0);
diff --git a/source/GitarooPause.hx b/source/GitarooPause.hx
index cb9705c556..1e9a1c8cdb 100644
--- a/source/GitarooPause.hx
+++ b/source/GitarooPause.hx
@@ -52,7 +52,7 @@ class GitarooPause extends MusicBeatState
override function update(elapsed:Float)
{
- if (controls.LEFT_P || controls.RIGHT_P)
+ if (controls.UI_LEFT_P || controls.UI_RIGHT_P)
changeThing();
if (controls.ACCEPT)
diff --git a/source/Highscore.hx b/source/Highscore.hx
index 5fbc732983..2fee324fda 100644
--- a/source/Highscore.hx
+++ b/source/Highscore.hx
@@ -13,51 +13,55 @@ class Highscore
public static function saveScore(song:String, score:Int = 0, ?diff:Int = 0):Void
{
- var daSong:String = formatSong(song, diff);
+ var formattedSong:String = formatSong(song, diff);
-
- #if !switch
+ #if newgrounds
NGio.postScore(score, song);
#end
-
- if (songScores.exists(daSong))
+ if (songScores.exists(formattedSong))
{
- if (songScores.get(daSong) < score)
- setScore(daSong, score);
+ if (songScores.get(formattedSong) < score)
+ setScore(formattedSong, score);
}
else
- setScore(daSong, score);
+ setScore(formattedSong, score);
}
public static function saveWeekScore(week:Int = 1, score:Int = 0, ?diff:Int = 0):Void
{
-
- #if !switch
+ #if newgrounds
NGio.postScore(score, "Week " + week);
#end
+ var formattedSong:String = formatSong('week' + week, diff);
- var daWeek:String = formatSong('week' + week, diff);
-
- if (songScores.exists(daWeek))
+ if (songScores.exists(formattedSong))
{
- if (songScores.get(daWeek) < score)
- setScore(daWeek, score);
+ if (songScores.get(formattedSong) < score)
+ setScore(formattedSong, score);
}
else
- setScore(daWeek, score);
+ setScore(formattedSong, score);
}
/**
* YOU SHOULD FORMAT SONG WITH formatSong() BEFORE TOSSING IN SONG VARIABLE
*/
- static function setScore(song:String, score:Int):Void
+ static function setScore(formattedSong:String, score:Int):Void
{
+ /** GeoKureli
+ * References to Highscore were wrapped in `#if !switch` blocks. I wasn't sure if this
+ * is because switch doesn't use NGio, or because switch has a different saving method.
+ * I moved the compiler flag here, rather than using it everywhere else.
+ */
+ #if !switch
+
// Reminder that I don't need to format this song, it should come formatted!
- songScores.set(song, score);
+ songScores.set(formattedSong, score);
FlxG.save.data.songScores = songScores;
FlxG.save.flush();
+ #end
}
public static function formatSong(song:String, diff:Int):String
diff --git a/source/InputFormatter.hx b/source/InputFormatter.hx
new file mode 100644
index 0000000000..4139105437
--- /dev/null
+++ b/source/InputFormatter.hx
@@ -0,0 +1,220 @@
+package ;
+
+import Controls;
+
+import flixel.FlxG;
+import flixel.input.gamepad.FlxGamepad;
+import flixel.input.gamepad.FlxGamepadInputID;
+import flixel.input.keyboard.FlxKey;
+
+using flixel.util.FlxStringUtil;
+
+class InputFormatter
+{
+ static public function format(id:Int, device:Device):String
+ {
+ return switch (device)
+ {
+ case Keys: getKeyName(id);
+ case Gamepad(gamepadID): getButtonName(id, FlxG.gamepads.getByID(gamepadID));
+ }
+ }
+
+ static public function getKeyName(id:Int):String
+ {
+ return switch(id)
+ {
+ case ZERO : "0";
+ case ONE : "1";
+ case TWO : "2";
+ case THREE : "3";
+ case FOUR : "4";
+ case FIVE : "5";
+ case SIX : "6";
+ case SEVEN : "7";
+ case EIGHT : "8";
+ case NINE : "9";
+ case PAGEUP : "PgUp";
+ case PAGEDOWN : "PgDown";
+ // case HOME : "Hm";
+ // case END : "End";
+ // case INSERT : "Ins";
+ // case ESCAPE : "Esc";
+ // case MINUS : "-";
+ // case PLUS : "+";
+ // case DELETE : "Del";
+ case BACKSPACE : "BckSpc";
+ case LBRACKET : "[";
+ case RBRACKET : "]";
+ case BACKSLASH : "\\";
+ case CAPSLOCK : "Caps";
+ case SEMICOLON : ";";
+ case QUOTE : "'";
+ // case ENTER : "Ent";
+ // case SHIFT : "Shf";
+ case COMMA : ",";
+ case PERIOD : ".";
+ case SLASH : "/";
+ case GRAVEACCENT : "`";
+ case CONTROL : "Ctrl";
+ case ALT : "Alt";
+ // case SPACE : "Spc";
+ // case UP : "Up";
+ // case DOWN : "Dn";
+ // case LEFT : "Lf";
+ // case RIGHT : "Rt";
+ // case TAB : "Tab";
+ case PRINTSCREEN : "PrtScrn";
+ case NUMPADZERO : "#0";
+ case NUMPADONE : "#1";
+ case NUMPADTWO : "#2";
+ case NUMPADTHREE : "#3";
+ case NUMPADFOUR : "#4";
+ case NUMPADFIVE : "#5";
+ case NUMPADSIX : "#6";
+ case NUMPADSEVEN : "#7";
+ case NUMPADEIGHT : "#8";
+ case NUMPADNINE : "#9";
+ case NUMPADMINUS : "#-";
+ case NUMPADPLUS : "#+";
+ case NUMPADPERIOD : "#.";
+ case NUMPADMULTIPLY: "#*";
+ default: titleCase(FlxKey.toStringMap[id]);
+ }
+ }
+
+ static var dirReg = ~/^(l|r).?-(left|right|down|up)$/;
+ inline static public function getButtonName(id:Int, gamepad:FlxGamepad):String
+ {
+ return switch(gamepad.getInputLabel(id))
+ {
+ // case null | "": shortenButtonName(FlxGamepadInputID.toStringMap[id]);
+ case label: shortenButtonName(label);
+ }
+ }
+
+ static function shortenButtonName(name:String)
+ {
+ return switch (name == null ? "" : name.toLowerCase())
+ {
+ case "": "[?]";
+ // case "square" : "[]";
+ // case "circle" : "()";
+ // case "triangle": "/\\";
+ // case "plus" : "+";
+ // case "minus" : "-";
+ // case "home" : "Hm";
+ // case "guide" : "Gd";
+ // case "back" : "Bk";
+ // case "select" : "Bk";
+ // case "start" : "St";
+ // case "left" : "Lf";
+ // case "right" : "Rt";
+ // case "down" : "Dn";
+ // case "up" : "Up";
+ case dir if (dirReg.match(dir)):
+ dirReg.matched(1).toUpperCase() + " " + titleCase(dirReg.matched(2));
+ case label: titleCase(label);
+ }
+ }
+
+ inline static function titleCaseTrim(str:String, length = 8)
+ {
+ return str.charAt(0).toUpperCase() + str.substr(1, length - 1).toLowerCase();
+ }
+
+ inline static function titleCase(str:String)
+ {
+ return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase();
+ }
+
+ inline static public function parsePadName(name:String):ControllerName
+ {
+ return ControllerName.parseName(name);
+ }
+
+ inline static public function getPadName(gamepad:FlxGamepad):ControllerName
+ {
+ return ControllerName.getName(gamepad);
+ }
+
+ inline static public function getPadNameById(id:Int):ControllerName
+ {
+ return ControllerName.getNameById(id);
+ }
+}
+
+@:forward
+@:enum abstract ControllerName(String) from String to String
+{
+ var OUYA = "Ouya" ;
+ var PS4 = "PS4" ;
+ var LOGI = "Logi" ;
+ var XBOX = "XBox" ;
+ var XINPUT = "XInput" ;
+ var WII = "Wii" ;
+ var PRO_CON = "Pro_Con" ;
+ var JOYCONS = "Joycons" ;
+ var JOYCON_L = "Joycon_L";
+ var JOYCON_R = "Joycon_R";
+ var MFI = "MFI" ;
+ var PAD = "Pad" ;
+
+ static public function getAssetByDevice(device:Device):String
+ {
+ return switch (device)
+ {
+ case Keys: getAsset(null);
+ case Gamepad(id): getAsset(FlxG.gamepads.getByID(id));
+ }
+ }
+
+ static public function getAsset(gamepad:FlxGamepad):String
+ {
+ if (gamepad == null)
+ return 'assets/images/ui/devices/Keys.png';
+
+ final name = parseName(gamepad.name);
+ var path = 'assets/images/ui/devices/$name.png';
+ if (openfl.utils.Assets.exists(path))
+ return path;
+
+ return 'assets/images/ui/devices/Pad.png';
+ }
+
+
+
+ inline static public function getNameById(id:Int):ControllerName return getName(FlxG.gamepads.getByID(id));
+ inline static public function getName(gamepad:FlxGamepad):ControllerName return parseName(gamepad.name);
+ static public function parseName(name:String):ControllerName
+ {
+ name = name.toLowerCase().remove("-").remove("_");
+ return
+ if (name.contains("ouya"))
+ OUYA;
+ else if (name.contains("wireless controller") || name.contains("ps4"))
+ PS4;
+ else if (name.contains("logitech"))
+ LOGI;
+ else if (name.contains("xbox"))
+ XBOX
+ else if (name.contains("xinput"))
+ XINPUT;
+ else if (name.contains("nintendo rvlcnt01tr") || name.contains("nintendo rvlcnt01"))
+ WII;
+ else if (name.contains("mayflash wiimote pc adapter"))
+ WII;
+ else if (name.contains("pro controller"))
+ PRO_CON;
+ else if (name.contains("joycon l+r"))
+ JOYCONS;
+ else if (name.contains("joycon (l)"))
+ JOYCON_L;
+ else if (name.contains("joycon (r)"))
+ JOYCON_R;
+ else if (name.contains("mfi"))
+ MFI;
+ else
+ PAD;
+ }
+}
\ No newline at end of file
diff --git a/source/LoadingState.hx b/source/LoadingState.hx
index 52a883b776..9ed103e549 100644
--- a/source/LoadingState.hx
+++ b/source/LoadingState.hx
@@ -75,7 +75,7 @@ class LoadingState extends MusicBeatState
if (!Assets.cache.hasSound(path))
{
var library = Assets.getLibrary("songs");
- final symbolPath = path.split(":").pop();
+ var symbolPath = path.split(":").pop();
// @:privateAccess
// library.types.set(symbolPath, SOUND);
// @:privateAccess
diff --git a/source/MainMenuState.hx b/source/MainMenuState.hx
index a4c45ee1a7..58494cc47b 100644
--- a/source/MainMenuState.hx
+++ b/source/MainMenuState.hx
@@ -1,11 +1,12 @@
package;
-#if desktop
-import Discord.DiscordClient;
-#end
+import NGio;
+
+import flixel.ui.FlxButton;
import flixel.FlxG;
import flixel.FlxObject;
import flixel.FlxSprite;
+import flixel.FlxState;
import flixel.addons.transition.FlxTransitionableState;
import flixel.effects.FlxFlicker;
import flixel.graphics.frames.FlxAtlasFrames;
@@ -14,22 +15,28 @@ import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
-import io.newgrounds.NG;
+import flixel.util.FlxTimer;
import lime.app.Application;
+#if desktop
+import Discord.DiscordClient;
+#end
+
+#if newgrounds
+import io.newgrounds.NG;
+import ui.NgPrompt;
+#end
+
+import ui.AtlasMenuList;
+import ui.MenuList;
+import ui.OptionsState;
+import ui.Prompt;
+
using StringTools;
class MainMenuState extends MusicBeatState
{
- var curSelected:Int = 0;
-
- var menuItems:FlxTypedGroup;
-
- #if !switch
- var optionShit:Array = ['story mode', 'freeplay', 'donate', 'options'];
- #else
- var optionShit:Array = ['story mode', 'freeplay'];
- #end
+ var menuItems:MainMenuList;
var magenta:FlxSprite;
var camFollow:FlxObject;
@@ -51,10 +58,10 @@ class MainMenuState extends MusicBeatState
persistentUpdate = persistentDraw = true;
- var bg:FlxSprite = new FlxSprite(-80).loadGraphic(Paths.image('menuBG'));
+ var bg:FlxSprite = new FlxSprite(Paths.image('menuBG'));
bg.scrollFactor.x = 0;
bg.scrollFactor.y = 0.17;
- bg.setGraphicSize(Std.int(bg.width * 1.1));
+ bg.setGraphicSize(Std.int(bg.width * 1.2));
bg.updateHitbox();
bg.screenCenter();
bg.antialiasing = true;
@@ -63,38 +70,57 @@ class MainMenuState extends MusicBeatState
camFollow = new FlxObject(0, 0, 1, 1);
add(camFollow);
- magenta = new FlxSprite(-80).loadGraphic(Paths.image('menuDesat'));
- magenta.scrollFactor.x = 0;
- magenta.scrollFactor.y = 0.17;
- magenta.setGraphicSize(Std.int(magenta.width * 1.1));
+ magenta = new FlxSprite(Paths.image('menuDesat'));
+ magenta.scrollFactor.x = bg.scrollFactor.x;
+ magenta.scrollFactor.y = bg.scrollFactor.y;
+ magenta.setGraphicSize(Std.int(bg.width));
magenta.updateHitbox();
- magenta.screenCenter();
+ magenta.x = bg.x;
+ magenta.y = bg.y;
magenta.visible = false;
magenta.antialiasing = true;
magenta.color = 0xFFfd719b;
add(magenta);
// magenta.scrollFactor.set();
- menuItems = new FlxTypedGroup();
+ menuItems = new MainMenuList();
add(menuItems);
-
- var tex = Paths.getSparrowAtlas('FNF_main_menu_assets');
-
- for (i in 0...optionShit.length)
+ menuItems.onChange.add(onMenuItemChange);
+ menuItems.onAcceptPress.add(function(_)
{
- var menuItem:FlxSprite = new FlxSprite(0, 60 + (i * 160));
- menuItem.frames = tex;
- menuItem.animation.addByPrefix('idle', optionShit[i] + " basic", 24);
- menuItem.animation.addByPrefix('selected', optionShit[i] + " white", 24);
- menuItem.animation.play('idle');
- menuItem.ID = i;
- menuItem.screenCenter(X);
- menuItems.add(menuItem);
- menuItem.scrollFactor.set();
- menuItem.antialiasing = true;
+ FlxFlicker.flicker(magenta, 1.1, 0.15, false, true);
+ });
+
+
+
+ menuItems.enabled = false;// disable for intro
+ menuItems.createItem('story mode', function () startExitState(new StoryMenuState()));
+ menuItems.createItem('freeplay', function () startExitState(new FreeplayState()));
+ // addMenuItem('options', function () startExitState(new OptionMenu()));
+ #if CAN_OPEN_LINKS
+ var hasPopupBlocker = #if web true #else false #end;
+ menuItems.createItem('donate', selectDonate, hasPopupBlocker);
+ #end
+ menuItems.createItem('options', function () startExitState(new OptionsState()));
+ // #if newgrounds
+ // if (NGio.isLoggedIn)
+ // menuItems.createItem("logout", selectLogout);
+ // else
+ // menuItems.createItem("login", selectLogin);
+ // #end
+
+ // center vertically
+ var spacing = 160;
+ var top = (FlxG.height - (spacing * (menuItems.length - 1))) / 2;
+ for (i in 0...menuItems.length)
+ {
+ var menuItem = menuItems.members[i];
+ menuItem.x = FlxG.width / 2;
+ menuItem.y = top + spacing * i;
}
FlxG.camera.follow(camFollow, null, 0.06);
+ // FlxG.camera.setScrollBounds(bg.x, bg.x + bg.width, bg.y, bg.y + bg.height * 1.2);
var versionShit:FlxText = new FlxText(5, FlxG.height - 18, 0, "v" + Application.current.meta.get('version'), 12);
versionShit.scrollFactor.set();
@@ -103,138 +129,167 @@ class MainMenuState extends MusicBeatState
// NG.core.calls.event.logEvent('swag').send();
- changeItem();
-
super.create();
}
-
- var selectedSomethin:Bool = false;
-
- override function update(elapsed:Float)
+
+ override function finishTransIn()
{
- if (FlxG.sound.music.volume < 0.8)
- {
- FlxG.sound.music.volume += 0.5 * FlxG.elapsed;
- }
-
- if (!selectedSomethin)
+ super.finishTransIn();
+
+ menuItems.enabled = true;
+
+ // #if newgrounds
+ // if (NGio.savedSessionFailed)
+ // showSavedSessionFailed();
+ // #end
+ }
+
+ function onMenuItemChange(selected:MenuItem)
+ {
+ camFollow.setPosition(selected.getGraphicMidpoint().x, selected.getGraphicMidpoint().y);
+ }
+
+ #if CAN_OPEN_LINKS
+ function selectDonate()
+ {
+ #if linux
+ Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]);
+ #else
+ FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
+ #end
+ }
+ #end
+
+ #if newgrounds
+ function selectLogin()
+ {
+ openNgPrompt(NgPrompt.showLogin());
+ }
+
+ function selectLogout()
+ {
+ openNgPrompt(NgPrompt.showLogout());
+ }
+
+ function showSavedSessionFailed()
+ {
+ openNgPrompt(NgPrompt.showSavedSessionFailed());
+ }
+
+ /**
+ * Calls openPrompt and redraws the login/logout button
+ * @param prompt
+ * @param onClose
+ */
+ public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void)
+ {
+ var onPromptClose = checkLoginStatus;
+ if (onClose != null)
{
- if (controls.UP_P)
+ onPromptClose = function ()
{
- FlxG.sound.play(Paths.sound('scrollMenu'));
- changeItem(-1);
+ checkLoginStatus();
+ onClose();
}
-
- if (controls.DOWN_P)
- {
- FlxG.sound.play(Paths.sound('scrollMenu'));
- changeItem(1);
- }
-
- if (controls.BACK)
+ }
+
+ openPrompt(prompt, onPromptClose);
+ }
+
+ function checkLoginStatus()
+ {
+ var prevLoggedIn = menuItems.has("logout");
+ if (prevLoggedIn && !NGio.isLoggedIn)
+ menuItems.resetItem("login", "logout", selectLogout);
+ else if (!prevLoggedIn && NGio.isLoggedIn)
+ menuItems.resetItem("logout", "login", selectLogin);
+ }
+ #end
+
+ public function openPrompt(prompt:Prompt, onClose:Void->Void)
+ {
+ menuItems.enabled = false;
+ prompt.closeCallback = function ()
+ {
+ menuItems.enabled = true;
+ if (onClose != null)
+ onClose();
+ }
+
+ openSubState(prompt);
+ }
+
+ function startExitState(state:FlxState)
+ {
+ var duration = 0.4;
+ menuItems.forEach(function(item)
+ {
+ if (menuItems.selectedIndex != item.ID)
{
- FlxG.switchState(new TitleState());
+ FlxTween.tween(item, {alpha: 0}, duration, { ease: FlxEase.quadOut });
}
-
- if (controls.ACCEPT)
+ else
{
- if (optionShit[curSelected] == 'donate')
- {
- #if linux
- Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]);
- #else
- FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
- #end
- }
- else
- {
- selectedSomethin = true;
- FlxG.sound.play(Paths.sound('confirmMenu'));
-
- FlxFlicker.flicker(magenta, 1.1, 0.15, false);
-
- menuItems.forEach(function(spr:FlxSprite)
- {
- if (curSelected != spr.ID)
- {
- FlxTween.tween(spr, {alpha: 0}, 0.4, {
- ease: FlxEase.quadOut,
- onComplete: function(twn:FlxTween)
- {
- spr.kill();
- }
- });
- }
- else
- {
- FlxFlicker.flicker(spr, 1, 0.06, false, false, function(flick:FlxFlicker)
- {
- var daChoice:String = optionShit[curSelected];
-
- switch (daChoice)
- {
- case 'story mode':
- FlxG.switchState(new StoryMenuState());
- trace("Story Menu Selected");
- case 'freeplay':
- FlxG.switchState(new FreeplayState());
-
- trace("Freeplay Menu Selected");
-
- case 'options':
- FlxTransitionableState.skipNextTransIn = true;
- FlxTransitionableState.skipNextTransOut = true;
- FlxG.switchState(new OptionsMenu());
- }
- });
- }
- });
- }
+ item.visible = false;
}
- }
-
- super.update(elapsed);
-
- menuItems.forEach(function(spr:FlxSprite)
- {
- spr.screenCenter(X);
});
+
+ new FlxTimer().start(duration, function(_) FlxG.switchState(state));
}
- function changeItem(huh:Int = 0)
+ override function update(elapsed:Float)
{
- curSelected += huh;
-
- if (curSelected >= menuItems.length)
- curSelected = 0;
- if (curSelected < 0)
- curSelected = menuItems.length - 1;
-
- menuItems.forEach(function(spr:FlxSprite)
+ if (FlxG.sound.music.volume < 0.8)
{
- spr.animation.play('idle');
+ FlxG.sound.music.volume += 0.5 * FlxG.elapsed;
+ }
- if (spr.ID == curSelected)
- {
- camFollow.setPosition(spr.getGraphicMidpoint().x, spr.getGraphicMidpoint().y);
- spr.animation.play('selected');
- }
+ if (menuItems.enabled && controls.BACK)
+ FlxG.switchState(new TitleState());
- spr.updateHitbox();
- if(spr.animation.curAnim.name == 'selected')
- {
- switch(optionShit[curSelected])
- {
- case 'story mode':
- spr.offset.y += 26;
- case 'freeplay':
- spr.offset.y += 28;
- case 'donate':
- spr.offset.y += 21;
- case 'options':
- spr.offset.y += 26;
- }
- }
- });
+ super.update(elapsed);
+ }
+}
+
+private class MainMenuList extends MenuTypedList
+{
+ public var atlas:FlxAtlasFrames;
+
+ public function new ()
+ {
+ atlas = Paths.getSparrowAtlas('main_menu');
+ super(Vertical);
+
+ }
+
+ public function createItem(x = 0.0, y = 0.0, name:String, callback, fireInstantly = false)
+ {
+ var item = new MainMenuItem(x, y, name, atlas, callback);
+ item.fireInstantly = fireInstantly;
+ item.ID = length;
+
+ return addItem(name, item);
+ }
+
+ override function destroy()
+ {
+ super.destroy();
+ atlas = null;
}
}
+private class MainMenuItem extends AtlasMenuItem
+{
+ public function new(x = 0.0, y = 0.0, name, atlas, callback)
+ {
+ super(x, y, name, atlas, callback);
+ scrollFactor.set();
+ }
+
+ override function changeAnim(anim:String)
+ {
+ super.changeAnim(anim);
+ // position by center
+ centerOrigin();
+ offset.copyFrom(origin);
+ }
+}
\ No newline at end of file
diff --git a/source/NGio.hx b/source/NGio.hx
index f8d8948e54..f82323e4b8 100644
--- a/source/NGio.hx
+++ b/source/NGio.hx
@@ -1,10 +1,13 @@
package;
+#if newgrounds
import flixel.FlxG;
import flixel.util.FlxSignal;
import flixel.util.FlxTimer;
import io.newgrounds.NG;
+import io.newgrounds.NGLite;
import io.newgrounds.components.ScoreBoardComponent.Period;
+import io.newgrounds.objects.Error;
import io.newgrounds.objects.Medal;
import io.newgrounds.objects.Score;
import io.newgrounds.objects.ScoreBoard;
@@ -15,14 +18,24 @@ import lime.app.Application;
import openfl.display.Stage;
using StringTools;
-
+#end
/**
* MADE BY GEOKURELI THE LEGENED GOD HERO MVP
*/
class NGio
{
- public static var isLoggedIn:Bool = false;
+ #if newgrounds
+ /**
+ * True, if the saved sessionId was used in the initial login, and failed to connect.
+ * Used in MainMenuState to show a popup to establish a new connection
+ */
+ public static var savedSessionFailed(default, null):Bool = false;
public static var scoreboardsLoaded:Bool = false;
+ public static var isLoggedIn(get, never):Bool;
+ inline static function get_isLoggedIn()
+ {
+ return NG.core != null && NG.core.loggedIn;
+ }
public static var scoreboardArray:Array = [];
@@ -30,45 +43,67 @@ class NGio
public static var ngScoresLoaded(default, null):FlxSignal = new FlxSignal();
public static var GAME_VER:String = "";
- public static var GAME_VER_NUMS:String = '';
- public static var gotOnlineVer:Bool = false;
-
- public static function noLogin(api:String)
+
+ static public function checkVersion(callback:String->Void)
{
- trace('INIT NOLOGIN');
+ trace('checking NG.io version');
GAME_VER = "v" + Application.current.meta.get('version');
- if (api.length != 0)
- {
- NG.create(api);
-
- new FlxTimer().start(2, function(tmr:FlxTimer)
+ NG.core.calls.app.getCurrentVersion(GAME_VER)
+ .addDataHandler(function(response)
{
- var call = NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response:Response)
- {
- GAME_VER = response.result.data.currentVersion;
- GAME_VER_NUMS = GAME_VER.split(" ")[0].trim();
- trace('CURRENT NG VERSION: ' + GAME_VER);
- trace('CURRENT NG VERSION: ' + GAME_VER_NUMS);
- gotOnlineVer = true;
- });
-
- call.send();
- });
- }
+ GAME_VER = response.result.data.currentVersion;
+ trace('CURRENT NG VERSION: ' + GAME_VER);
+ callback(GAME_VER);
+ })
+ .send();
}
- public function new(api:String, encKey:String, ?sessionId:String)
+ static public function init()
{
+ var api = APIStuff.API;
+ if (api == null || api.length == 0)
+ {
+ trace("Missing Newgrounds API key, aborting connection");
+ return;
+ }
trace("connecting to newgrounds");
-
- NG.createAndCheckSession(api, sessionId);
-
- NG.core.verbose = true;
+
+ #if NG_FORCE_EXPIRED_SESSION
+ var sessionId:String = "fake_session_id";
+ function onSessionFail(error:Error)
+ {
+ trace("Forcing an expired saved session. "
+ + "To disable, comment out NG_FORCE_EXPIRED_SESSION in Project.xml");
+ savedSessionFailed = true;
+ }
+ #else
+ var sessionId:String = NGLite.getSessionId();
+ if (sessionId != null)
+ trace("found web session id");
+
+ #if (debug)
+ if (sessionId == null && APIStuff.SESSION != null)
+ {
+ trace("using debug session id");
+ sessionId = APIStuff.SESSION;
+ }
+ #end
+
+ var onSessionFail:Error->Void = null;
+ if (sessionId == null && FlxG.save.data.sessionId != null)
+ {
+ trace("using stored session id");
+ sessionId = FlxG.save.data.sessionId;
+ onSessionFail = function (error) savedSessionFailed = true;
+ }
+ #end
+
+ NG.create(api, sessionId, #if NG_DEBUG true #else false #end, onSessionFail);
+
+ #if NG_VERBOSE NG.core.verbose = true; #end
// Set the encryption cipher/format to RC4/Base64. AES128 and Hex are not implemented yet
- NG.core.initEncryption(encKey); // Found in you NG project view
-
- trace(NG.core.attemptingLogin);
+ NG.core.initEncryption(APIStuff.EncKey); // Found in you NG project view
if (NG.core.attemptingLogin)
{
@@ -78,21 +113,58 @@ class NGio
trace("attempting login");
NG.core.onLogin.add(onNGLogin);
}
- else
+ //GK: taking out auto login, adding a login button to the main menu
+ // else
+ // {
+ // /* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to.
+ // * Note: This will cause a new browser window to pop up where they can log in to newgrounds
+ // */
+ // NG.core.requestLogin(onNGLogin);
+ // }
+ }
+
+ /**
+ * Attempts to log in to newgrounds by requesting a new session ID, only call if no session ID was found automatically
+ * @param popupLauncher The function to call to open the login url, must be inside
+ * a user input event or the popup blocker will block it.
+ * @param onComplete A callback with the result of the connection.
+ */
+ static public function login(?popupLauncher:(Void->Void)->Void, onComplete:ConnectionResult->Void)
+ {
+ trace("Logging in manually");
+ var onPending:Void->Void = null;
+ if (popupLauncher != null)
{
- /* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to.
- * Note: This will cause a new browser window to pop up where they can log in to newgrounds
- */
- NG.core.requestLogin(onNGLogin);
+ onPending = function () popupLauncher(NG.core.openPassportUrl);
+ }
+
+ var onSuccess:Void->Void = onNGLogin;
+ var onFail:Error->Void = null;
+ var onCancel:Void->Void = null;
+ if (onComplete != null)
+ {
+ onSuccess = function ()
+ {
+ onNGLogin();
+ onComplete(Success);
+ }
+ onFail = function (e) onComplete(Fail(e.message));
+ onCancel = function() onComplete(Cancelled);
}
+
+ NG.core.requestLogin(onSuccess, onPending, onFail, onCancel);
+ }
+
+ inline static public function cancelLogin():Void
+ {
+ NG.core.cancelLoginRequest();
}
- function onNGLogin():Void
+ static function onNGLogin():Void
{
trace('logged in! user:${NG.core.user.name}');
- isLoggedIn = true;
FlxG.save.data.sessionId = NG.core.sessionId;
- // FlxG.save.flush();
+ FlxG.save.flush();
// Load medals then call onNGMedalFetch()
NG.core.requestMedals(onNGMedalFetch);
@@ -101,9 +173,17 @@ class NGio
ngDataLoaded.dispatch();
}
+
+ static public function logout()
+ {
+ NG.core.logOut();
+
+ FlxG.save.data.sessionId = null;
+ FlxG.save.flush();
+ }
// --- MEDALS
- function onNGMedalFetch():Void
+ static function onNGMedalFetch():Void
{
/*
// Reading medal info
@@ -121,7 +201,7 @@ class NGio
}
// --- SCOREBOARDS
- function onNGBoardsFetch():Void
+ static function onNGBoardsFetch():Void
{
/*
// Reading medal info
@@ -145,25 +225,7 @@ class NGio
// more info on scores --- http://www.newgrounds.io/help/components/#scoreboard-getscores
}
- inline static public function postScore(score:Int = 0, song:String)
- {
- if (isLoggedIn)
- {
- for (id in NG.core.scoreBoards.keys())
- {
- var board = NG.core.scoreBoards.get(id);
-
- if (song == board.name)
- {
- board.postScore(score, "Uhh meow?");
- }
-
- // trace('loaded scoreboard id:$id, name:${board.name}');
- }
- }
- }
-
- function onNGScoresFetch():Void
+ static function onNGScoresFetch():Void
{
scoreboardsLoaded = true;
@@ -181,20 +243,61 @@ class NGio
// NGio.scoreboardArray = NG.core.scoreBoards.get(8004).scores;
}
+ #end
- inline static public function logEvent(event:String)
+ static public function logEvent(event:String)
{
+ #if newgrounds
NG.core.calls.event.logEvent(event).send();
trace('should have logged: ' + event);
+ #else
+ #if debug trace('event:$event - not logged, missing NG.io lib'); #end
+ #end
}
- inline static public function unlockMedal(id:Int)
+ static public function unlockMedal(id:Int)
{
+ #if newgrounds
if (isLoggedIn)
{
var medal = NG.core.medals.get(id);
if (!medal.unlocked)
medal.sendUnlock();
}
+ #else
+ #if debug trace('medal:$id - not unlocked, missing NG.io lib'); #end
+ #end
}
+
+ static public function postScore(score:Int = 0, song:String)
+ {
+ #if newgrounds
+ if (isLoggedIn)
+ {
+ for (id in NG.core.scoreBoards.keys())
+ {
+ var board = NG.core.scoreBoards.get(id);
+
+ if (song == board.name)
+ {
+ board.postScore(score, "Uhh meow?");
+ }
+
+ // trace('loaded scoreboard id:$id, name:${board.name}');
+ }
+ }
+ #else
+ #if debug trace('Song:$song, Score:$score - not posted, missing NG.io lib'); #end
+ #end
+ }
+}
+
+enum ConnectionResult
+{
+ /** Log in successful */
+ Success;
+ /** Could not login */
+ Fail(msg:String);
+ /** User cancelled the login */
+ Cancelled;
}
diff --git a/source/OptionsMenu.hx b/source/OptionsMenu_old.hx
similarity index 94%
rename from source/OptionsMenu.hx
rename to source/OptionsMenu_old.hx
index 0c5b284ccc..4ec8443236 100644
--- a/source/OptionsMenu.hx
+++ b/source/OptionsMenu_old.hx
@@ -12,7 +12,7 @@ import flixel.text.FlxText;
import flixel.util.FlxColor;
import lime.utils.Assets;
-class OptionsMenu extends MusicBeatState
+class OptionsMenu_old extends MusicBeatState
{
var selector:FlxText;
var curSelected:Int = 0;
@@ -82,7 +82,7 @@ class OptionsMenu extends MusicBeatState
{
if (FlxG.keys.getIsDown().length > 0)
{
- PlayerSettings.player1.controls.replaceBinding(Control.LEFT, Keys, FlxG.keys.getIsDown()[0].ID, null);
+ // PlayerSettings.player1.controls.replaceBinding(Control.LEFT, Keys, FlxG.keys.getIsDown()[0].ID, null);
}
// PlayerSettings.player1.controls.replaceBinding(Control)
}
@@ -99,9 +99,7 @@ class OptionsMenu extends MusicBeatState
function changeSelection(change:Int = 0)
{
- #if !switch
NGio.logEvent('Fresh');
- #end
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
diff --git a/source/PauseSubState.hx b/source/PauseSubState.hx
index f0215ee106..a469559026 100644
--- a/source/PauseSubState.hx
+++ b/source/PauseSubState.hx
@@ -97,7 +97,7 @@ class PauseSubState extends MusicBeatSubstate
regenMenu();
- cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]];
+ // cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]];
}
private function regenMenu():Void
@@ -126,8 +126,8 @@ class PauseSubState extends MusicBeatSubstate
super.update(elapsed);
- var upP = controls.UP_P;
- var downP = controls.DOWN_P;
+ var upP = controls.UI_UP_P;
+ var downP = controls.UI_DOWN_P;
var accepted = controls.ACCEPT;
if (upP)
diff --git a/source/PlayState.hx b/source/PlayState.hx
index 013a8c2de5..2d6afdda20 100644
--- a/source/PlayState.hx
+++ b/source/PlayState.hx
@@ -153,9 +153,7 @@ class PlayState extends MusicBeatState
camHUD.bgColor.alpha = 0;
FlxG.cameras.reset(camGame);
- FlxG.cameras.add(camHUD);
-
- FlxCamera.defaultCameras = [camGame];
+ FlxG.cameras.add(camHUD, false);
persistentUpdate = true;
persistentDraw = true;
@@ -1462,7 +1460,13 @@ class PlayState extends MusicBeatState
FlxG.switchState(new GitarooPause());
}
else
- openSubState(new PauseSubState(boyfriend.getScreenPosition().x, boyfriend.getScreenPosition().y));
+ {
+ var boyfriendPos = boyfriend.getScreenPosition();
+ var pauseSubState = new PauseSubState(boyfriendPos.x, boyfriendPos.y);
+ openSubState(pauseSubState);
+ pauseSubState.camera = camHUD;
+ boyfriendPos.put();
+ }
#if desktop
DiscordClient.changePresence(detailsPausedText, SONG.song + " (" + storyDifficultyText + ")", iconRPC);
@@ -1618,12 +1622,13 @@ class PlayState extends MusicBeatState
trace("RESET = True");
}
- // CHEAT = brandon's a pussy
+ #if CAN_CHEAT // brandon's a pussy
if (controls.CHEAT)
{
health += 1;
trace("User is cheating!");
}
+ #end
if (health <= 0 && !practiceMode)
{
@@ -1762,9 +1767,7 @@ class PlayState extends MusicBeatState
vocals.volume = 0;
if (SONG.validScore)
{
- #if !switch
Highscore.saveScore(SONG.song, songScore, storyDifficulty);
- #end
}
if (isStoryMode)
@@ -1994,20 +1997,20 @@ class PlayState extends MusicBeatState
private function keyShit():Void
{
// HOLDING
- var up = controls.UP;
- var right = controls.RIGHT;
- var down = controls.DOWN;
- var left = controls.LEFT;
+ var up = controls.NOTE_UP;
+ var right = controls.NOTE_RIGHT;
+ var down = controls.NOTE_DOWN;
+ var left = controls.NOTE_LEFT;
- var upP = controls.UP_P;
- var rightP = controls.RIGHT_P;
- var downP = controls.DOWN_P;
- var leftP = controls.LEFT_P;
+ var upP = controls.NOTE_UP_P;
+ var rightP = controls.NOTE_RIGHT_P;
+ var downP = controls.NOTE_DOWN_P;
+ var leftP = controls.NOTE_LEFT_P;
- var upR = controls.UP_R;
- var rightR = controls.RIGHT_R;
- var downR = controls.DOWN_R;
- var leftR = controls.LEFT_R;
+ var upR = controls.NOTE_UP_R;
+ var rightR = controls.NOTE_RIGHT_R;
+ var downR = controls.NOTE_DOWN_R;
+ var leftR = controls.NOTE_LEFT_R;
var controlArray:Array = [leftP, downP, upP, rightP];
@@ -2231,10 +2234,10 @@ class PlayState extends MusicBeatState
{
// just double pasting this shit cuz fuk u
// REDO THIS SYSTEM!
- var upP = controls.UP_P;
- var rightP = controls.RIGHT_P;
- var downP = controls.DOWN_P;
- var leftP = controls.LEFT_P;
+ var upP = controls.NOTE_UP_P;
+ var rightP = controls.NOTE_RIGHT_P;
+ var downP = controls.NOTE_DOWN_P;
+ var leftP = controls.NOTE_LEFT_P;
if (leftP)
noteMiss(0);
diff --git a/source/PlayerSettings.hx b/source/PlayerSettings.hx
index b275b9b514..a0ed2429a4 100644
--- a/source/PlayerSettings.hx
+++ b/source/PlayerSettings.hx
@@ -1,8 +1,11 @@
package;
import Controls;
+
import flixel.FlxCamera;
import flixel.FlxG;
+import flixel.input.actions.FlxActionInput;
+import flixel.input.gamepad.FlxGamepad;
import flixel.util.FlxSignal;
// import ui.DeviceManager;
@@ -14,143 +17,233 @@ class PlayerSettings
static public var player1(default, null):PlayerSettings;
static public var player2(default, null):PlayerSettings;
- #if (haxe >= "4.0.0")
- static public final onAvatarAdd = new FlxTypedSignalVoid>();
- static public final onAvatarRemove = new FlxTypedSignalVoid>();
- #else
- static public var onAvatarAdd = new FlxTypedSignalVoid>();
- static public var onAvatarRemove = new FlxTypedSignalVoid>();
- #end
+ static public var onAvatarAdd(default, null) = new FlxTypedSignalVoid>();
+ static public var onAvatarRemove(default, null) = new FlxTypedSignalVoid>();
public var id(default, null):Int;
- #if (haxe >= "4.0.0")
- public final controls:Controls;
- #else
- public var controls:Controls;
- #end
+ public var controls(default, null):Controls;
// public var avatar:Player;
// public var camera(get, never):PlayCamera;
- function new(id, scheme)
+ function new(id)
{
this.id = id;
- this.controls = new Controls('player$id', scheme);
+ this.controls = new Controls('player$id', None);
+
+ #if CLEAR_INPUT_SAVE
+ FlxG.save.data.controls = null;
+ FlxG.save.flush();
+ #end
+
+ var useDefault = true;
+ var controlData = FlxG.save.data.controls;
+ if (controlData != null)
+ {
+ var keyData:Dynamic = null;
+ if (id == 0 && controlData.p1 != null && controlData.p1.keys != null)
+ keyData = controlData.p1.keys;
+ else if (id == 1 && controlData.p2 != null && controlData.p2.keys != null)
+ keyData = controlData.p2.keys;
+
+ if (keyData != null)
+ {
+ useDefault = false;
+ trace("loaded key data: " + haxe.Json.stringify(keyData));
+ controls.fromSaveData(keyData, Keys);
+ }
+ }
+
+ if (useDefault)
+ controls.setKeyboardScheme(Solo);
}
-
- public function setKeyboardScheme(scheme)
+
+ function addGamepad(gamepad:FlxGamepad)
{
- controls.setKeyboardScheme(scheme);
- }
-
- /*
- static public function addAvatar(avatar:Player):PlayerSettings
+ var useDefault = true;
+ var controlData = FlxG.save.data.controls;
+ if (controlData != null)
{
- var settings:PlayerSettings;
-
- if (player1 == null)
+ var padData:Dynamic = null;
+ if (id == 0 && controlData.p1 != null && controlData.p1.pad != null)
+ padData = controlData.p1.pad;
+ else if (id == 1 && controlData.p2 != null && controlData.p2.pad != null)
+ padData = controlData.p2.pad;
+
+ if (padData != null)
{
- player1 = new PlayerSettings(0, Solo);
- ++numPlayers;
+ useDefault = false;
+ trace("loaded pad data: " + haxe.Json.stringify(padData));
+ controls.addGamepadWithSaveData(gamepad.id, padData);
}
-
- if (player1.avatar == null)
- settings = player1;
- else
+ }
+
+ if (useDefault)
+ controls.addDefaultGamepad(gamepad.id);
+ }
+
+ public function saveControls()
+ {
+ if (FlxG.save.data.controls == null)
+ FlxG.save.data.controls = {};
+
+ var playerData:{ ?keys:Dynamic, ?pad:Dynamic }
+ if (id == 0)
+ {
+ if (FlxG.save.data.controls.p1 == null)
+ FlxG.save.data.controls.p1 = {};
+ playerData = FlxG.save.data.controls.p1;
+ }
+ else
+ {
+ if (FlxG.save.data.controls.p2 == null)
+ FlxG.save.data.controls.p2 = {};
+ playerData = FlxG.save.data.controls.p2;
+ }
+
+ var keyData = controls.createSaveData(Keys);
+ if (keyData != null)
+ {
+ playerData.keys = keyData;
+ trace("saving key data: " + haxe.Json.stringify(keyData));
+ }
+
+ if (controls.gamepadsAdded.length > 0)
+ {
+ var padData = controls.createSaveData(Gamepad(controls.gamepadsAdded[0]));
+ if (padData != null)
{
- if (player2 == null)
- {
- if (player1.controls.keyboardScheme.match(Duo(true)))
- player2 = new PlayerSettings(1, Duo(false));
- else
- player2 = new PlayerSettings(1, None);
- ++numPlayers;
- }
-
- if (player2.avatar == null)
- settings = player2;
- else
- throw throw 'Invalid number of players: ${numPlayers + 1}';
+ trace("saving pad data: " + haxe.Json.stringify(padData));
+ playerData.pad = padData;
}
- ++numAvatars;
- settings.avatar = avatar;
- avatar.settings = settings;
-
- splitCameras();
-
- onAvatarAdd.dispatch(settings);
-
- return settings;
}
+
+ FlxG.save.flush();
+ }
+
+ static public function init():Void
+ {
+ if (player1 == null)
+ {
+ player1 = new PlayerSettings(0);
+ ++numPlayers;
+ }
+
+ FlxG.gamepads.deviceConnected.add(onGamepadAdded);
- static public function removeAvatar(avatar:Player):Void
+ var numGamepads = FlxG.gamepads.numActiveGamepads;
+ for (i in 0...numGamepads)
{
- var settings:PlayerSettings;
+ var gamepad = FlxG.gamepads.getByID(i);
+ if (gamepad != null)
+ onGamepadAdded(gamepad);
+ }
- if (player1 != null && player1.avatar == avatar)
- settings = player1;
- else if (player2 != null && player2.avatar == avatar)
- {
- settings = player2;
- if (player1.controls.keyboardScheme.match(Duo(_)))
- player1.setKeyboardScheme(Solo);
- }
- else
- throw "Cannot remove avatar that is not for a player";
+ // player1.controls.addDefaultGamepad(0);
+ // }
- settings.avatar = null;
- while (settings.controls.gamepadsAdded.length > 0)
- {
- final id = settings.controls.gamepadsAdded.shift();
- settings.controls.removeGamepad(id);
- DeviceManager.releaseGamepad(FlxG.gamepads.getByID(id));
- }
+ // if (numGamepads > 1)
+ // {
+ // if (player2 == null)
+ // {
+ // player2 = new PlayerSettings(1, None);
+ // ++numPlayers;
+ // }
- --numAvatars;
+ // var gamepad = FlxG.gamepads.getByID(1);
+ // if (gamepad == null)
+ // throw 'Unexpected null gamepad. id:0';
- splitCameras();
+ // player2.controls.addDefaultGamepad(1);
+ // }
- onAvatarRemove.dispatch(avatar.settings);
- }
+ // DeviceManager.init();
+ }
+
+ static function onGamepadAdded(gamepad:FlxGamepad)
+ {
+ player1.addGamepad(gamepad);
+ }
+
- */
- static public function init():Void
+ /*
+ public function setKeyboardScheme(scheme)
+ {
+ controls.setKeyboardScheme(scheme);
+ }
+
+ static public function addAvatar(avatar:Player):PlayerSettings
{
+ var settings:PlayerSettings;
+
if (player1 == null)
{
player1 = new PlayerSettings(0, Solo);
++numPlayers;
}
- var numGamepads = FlxG.gamepads.numActiveGamepads;
- if (numGamepads > 0)
- {
- var gamepad = FlxG.gamepads.getByID(0);
- if (gamepad == null)
- throw 'Unexpected null gamepad. id:0';
-
- player1.controls.addDefaultGamepad(0);
- }
-
- if (numGamepads > 1)
+ if (player1.avatar == null)
+ settings = player1;
+ else
{
if (player2 == null)
{
- player2 = new PlayerSettings(1, None);
+ if (player1.controls.keyboardScheme.match(Duo(true)))
+ player2 = new PlayerSettings(1, Duo(false));
+ else
+ player2 = new PlayerSettings(1, None);
++numPlayers;
}
- var gamepad = FlxG.gamepads.getByID(1);
- if (gamepad == null)
- throw 'Unexpected null gamepad. id:0';
+ if (player2.avatar == null)
+ settings = player2;
+ else
+ throw throw 'Invalid number of players: ${numPlayers + 1}';
+ }
+ ++numAvatars;
+ settings.avatar = avatar;
+ avatar.settings = settings;
+
+ splitCameras();
+
+ onAvatarAdd.dispatch(settings);
+
+ return settings;
+ }
+
+ static public function removeAvatar(avatar:Player):Void
+ {
+ var settings:PlayerSettings;
- player2.controls.addDefaultGamepad(1);
+ if (player1 != null && player1.avatar == avatar)
+ settings = player1;
+ else if (player2 != null && player2.avatar == avatar)
+ {
+ settings = player2;
+ if (player1.controls.keyboardScheme.match(Duo(_)))
+ player1.setKeyboardScheme(Solo);
}
+ else
+ throw "Cannot remove avatar that is not for a player";
- // DeviceManager.init();
+ settings.avatar = null;
+ while (settings.controls.gamepadsAdded.length > 0)
+ {
+ final id = settings.controls.gamepadsAdded.shift();
+ settings.controls.removeGamepad(id);
+ DeviceManager.releaseGamepad(FlxG.gamepads.getByID(id));
+ }
+
+ --numAvatars;
+
+ splitCameras();
+
+ onAvatarRemove.dispatch(avatar.settings);
}
+ */
+
static public function reset()
{
player1 = null;
diff --git a/source/StoryMenuState.hx b/source/StoryMenuState.hx
index 1c2dc87832..3c9d629ec5 100644
--- a/source/StoryMenuState.hx
+++ b/source/StoryMenuState.hx
@@ -245,29 +245,29 @@ class StoryMenuState extends MusicBeatState
{
if (!selectedWeek)
{
- if (controls.UP_P)
+ if (controls.UI_UP_P)
{
changeWeek(-1);
}
- if (controls.DOWN_P)
+ if (controls.UI_DOWN_P)
{
changeWeek(1);
}
- if (controls.RIGHT)
+ if (controls.UI_RIGHT)
rightArrow.animation.play('press')
else
rightArrow.animation.play('idle');
- if (controls.LEFT)
+ if (controls.UI_LEFT)
leftArrow.animation.play('press');
else
leftArrow.animation.play('idle');
- if (controls.RIGHT_P)
+ if (controls.UI_RIGHT_P)
changeDifficulty(1);
- if (controls.LEFT_P)
+ if (controls.UI_LEFT_P)
changeDifficulty(-1);
}
@@ -360,10 +360,6 @@ class StoryMenuState extends MusicBeatState
sprDifficulty.y = leftArrow.y - 15;
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
- #if !switch
- intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
- #end
-
FlxTween.tween(sprDifficulty, {y: leftArrow.y + 15, alpha: 1}, 0.07);
}
@@ -439,8 +435,6 @@ class StoryMenuState extends MusicBeatState
txtTracklist.screenCenter(X);
txtTracklist.x -= FlxG.width * 0.35;
- #if !switch
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
- #end
}
}
diff --git a/source/TitleState.hx b/source/TitleState.hx
index ca5340b933..98c681e7d7 100644
--- a/source/TitleState.hx
+++ b/source/TitleState.hx
@@ -14,7 +14,6 @@ import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
-import io.newgrounds.NG;
import lime.app.Application;
import openfl.Assets;
import shaderslmfao.ColorSwap;
@@ -54,25 +53,20 @@ class TitleState extends MusicBeatState
FlxG.sound.muteKeys = [ZERO];
- PlayerSettings.init();
-
curWacky = FlxG.random.getObject(getIntroTextShit());
// DEBUG BULLSHIT
super.create();
- NGio.noLogin(APIStuff.API);
-
- #if ng
- var ng:NGio = new NGio(APIStuff.API, APIStuff.EncKey);
- trace('NEWGROUNDS LOL');
- #end
-
FlxG.save.bind('funkin', 'ninjamuffin99');
-
+ PlayerSettings.init();
Highscore.load();
-
+
+ #if newgrounds
+ NGio.init();
+ #end
+
if (FlxG.save.data.weekUnlocked != null)
{
// FIX LATER!!!
@@ -289,13 +283,11 @@ class TitleState extends MusicBeatState
if (pressedEnter && !transitioning && skippedIntro)
{
- #if !switch
NGio.unlockMedal(60960);
// If it's Friday according to da clock
if (Date.now().getDay() == 5)
NGio.unlockMedal(61034);
- #end
titleText.animation.play('press');
@@ -305,26 +297,30 @@ class TitleState extends MusicBeatState
transitioning = true;
// FlxG.sound.music.stop();
- new FlxTimer().start(2, function(tmr:FlxTimer)
+ #if newgrounds
+ if (!OutdatedSubState.leftState)
{
- // Check if version is outdated
-
- var version:String = "v" + Application.current.meta.get('version');
-
- if (version.trim() != NGio.GAME_VER_NUMS.trim() && !OutdatedSubState.leftState)
- {
- FlxG.switchState(new OutdatedSubState());
- trace('OLD VERSION!');
- trace('old ver');
- trace(version.trim());
- trace('cur ver');
- trace(NGio.GAME_VER_NUMS.trim());
- }
- else
+ NGio.checkVersion(function(version)
{
- FlxG.switchState(new MainMenuState());
- }
- });
+ // Check if version is outdated
+
+ var localVersion:String = "v" + Application.current.meta.get('version');
+ var onlineVersion = version.split(" ")[0].trim();
+
+ if (version.trim() != onlineVersion)
+ {
+ trace('OLD VERSION!');
+ FlxG.switchState(new OutdatedSubState());
+ }
+ else
+ {
+ FlxG.switchState(new MainMenuState());
+ }
+ });
+ }
+ #else
+ FlxG.switchState(new MainMenuState());
+ #end
// FlxG.sound.play(Paths.music('titleShoot'), 0.7);
}
@@ -333,12 +329,12 @@ class TitleState extends MusicBeatState
skipIntro();
}
- if (controls.LEFT)
+ if (controls.UI_LEFT)
{
swagShader.update(-elapsed * 0.1);
}
- if (controls.RIGHT)
+ if (controls.UI_RIGHT)
{
swagShader.update(elapsed * 0.1);
}
diff --git a/source/ui/AtlasMenuList.hx b/source/ui/AtlasMenuList.hx
new file mode 100644
index 0000000000..68232da5ce
--- /dev/null
+++ b/source/ui/AtlasMenuList.hx
@@ -0,0 +1,81 @@
+package ui;
+
+import ui.MenuList;
+
+import flixel.graphics.frames.FlxAtlasFrames;
+
+typedef AtlasAsset = flixel.util.typeLimit.OneOfTwo;
+
+class AtlasMenuList extends MenuTypedList
+{
+ public var atlas:FlxAtlasFrames;
+
+ public function new (atlas, navControls:NavControls = Vertical, ?wrapMode)
+ {
+ super(navControls, wrapMode);
+
+ if (Std.is(atlas, String))
+ this.atlas = Paths.getSparrowAtlas(cast atlas);
+ else
+ this.atlas = cast atlas;
+ }
+
+ public function createItem(x = 0.0, y = 0.0, name, callback, fireInstantly = false)
+ {
+ var item = new AtlasMenuItem(x, y, name, atlas, callback);
+ item.fireInstantly = fireInstantly;
+ return addItem(name, item);
+ }
+
+ override function destroy()
+ {
+ super.destroy();
+ atlas = null;
+ }
+}
+
+class AtlasMenuItem extends MenuItem
+{
+ var atlas:FlxAtlasFrames;
+ public function new (x = 0.0, y = 0.0, name:String, atlas:FlxAtlasFrames, callback)
+ {
+ this.atlas = atlas;
+ super(x, y, name, callback);
+ }
+
+ override function setData(name:String, ?callback:Void->Void)
+ {
+ frames = atlas;
+ animation.addByPrefix('idle', '$name idle', 24);
+ animation.addByPrefix('selected', '$name selected', 24);
+
+ super.setData(name, callback);
+ }
+
+ function changeAnim(animName:String)
+ {
+ animation.play(animName);
+ updateHitbox();
+ }
+
+ override function idle()
+ {
+ changeAnim('idle');
+ }
+
+ override function select()
+ {
+ changeAnim('selected');
+ }
+
+ override function get_selected()
+ {
+ return animation.curAnim != null && animation.curAnim.name == "selected";
+ }
+
+ override function destroy()
+ {
+ super.destroy();
+ atlas = null;
+ }
+}
\ No newline at end of file
diff --git a/source/ui/AtlasText.hx b/source/ui/AtlasText.hx
new file mode 100644
index 0000000000..e9d06e7110
--- /dev/null
+++ b/source/ui/AtlasText.hx
@@ -0,0 +1,262 @@
+package ui;
+
+import flixel.FlxSprite;
+import flixel.group.FlxSpriteGroup;
+import flixel.graphics.frames.FlxAtlasFrames;
+import flixel.util.FlxStringUtil;
+
+@:forward
+abstract BoldText(AtlasText) from AtlasText to AtlasText
+{
+ inline public function new (x = 0.0, y = 0.0, text:String)
+ {
+ this = new AtlasText(x, y, text, Bold);
+ }
+}
+
+/**
+ * Alphabet.hx has a ton of bugs and does a bunch of stuff I don't need, fuck that class
+ */
+class AtlasText extends FlxTypedSpriteGroup
+{
+ static var fonts = new Map();
+ static var casesAllowed = new Map();
+ public var text(default, set):String = "";
+
+ var font:AtlasFontData;
+
+ public var atlas(get, never):FlxAtlasFrames;
+ inline function get_atlas() return font.atlas;
+ public var caseAllowed(get, never):Case;
+ inline function get_caseAllowed() return font.caseAllowed;
+ public var maxHeight(get, never):Float;
+ inline function get_maxHeight() return font.maxHeight;
+
+ public function new (x = 0.0, y = 0.0, text:String, fontName:AtlasFont = Default)
+ {
+ if (!fonts.exists(fontName))
+ fonts[fontName] = new AtlasFontData(fontName);
+ font = fonts[fontName];
+
+ super(x, y);
+
+ this.text = text;
+ }
+
+ function set_text(value:String)
+ {
+ if (value == null)
+ value = "";
+
+ var caseValue = restrictCase(value);
+ var caseText = restrictCase(this.text);
+
+ this.text = value;
+ if (caseText == caseValue)
+ return value; // cancel redraw
+
+ if (caseValue.indexOf(caseText) == 0)
+ {
+ // new text is just old text with additions at the end, append the difference
+ appendTextCased(caseValue.substr(caseText.length));
+ return this.text;
+ }
+
+ value = caseValue;
+
+ group.kill();
+
+ if (value == "")
+ return this.text;
+
+ appendTextCased(caseValue);
+ return this.text;
+ }
+
+ /**
+ * Adds new characters, without needing to redraw the previous characters
+ * @param text The text to add.
+ * @throws String if `text` is null.
+ */
+ public function appendText(text:String)
+ {
+ if (text == null)
+ throw "cannot append null";
+
+ if (text == "")
+ return;
+
+ this.text = this.text + text;
+ }
+
+ /**
+ * Converts all characters to fit the font's `allowedCase`.
+ * @param text
+ */
+ function restrictCase(text:String)
+ {
+ return switch(caseAllowed)
+ {
+ case Both: text;
+ case Upper: text.toUpperCase();
+ case Lower: text.toLowerCase();
+ }
+ }
+
+ /**
+ * Adds new text on top of the existing text. Helper for other methods; DOESN'T CHANGE `this.text`.
+ * @param text The text to add, assumed to match the font's `caseAllowed`.
+ */
+ function appendTextCased(text:String)
+ {
+ var charCount = group.countLiving();
+ var xPos:Float = 0;
+ var yPos:Float = 0;
+ // `countLiving` returns -1 if group is empty
+ if (charCount == -1)
+ charCount = 0;
+ else if (charCount > 0)
+ {
+ var lastChar = group.members[charCount - 1];
+ xPos = lastChar.x + lastChar.width - x;
+ yPos = lastChar.y + lastChar.height - maxHeight - y;
+ }
+
+ var splitValues = text.split("");
+ for (i in 0...splitValues.length)
+ {
+ switch(splitValues[i])
+ {
+ case " ":
+ {
+ xPos += 40;
+ }
+ case "\n":
+ {
+ xPos = 0;
+ yPos += maxHeight;
+ }
+ case char:
+ {
+ var charSprite:AtlasChar;
+ if (group.members.length <= charCount)
+ charSprite = new AtlasChar(atlas, char);
+ else
+ {
+ charSprite = group.members[charCount];
+ charSprite.revive();
+ charSprite.char = char;
+ charSprite.alpha = 1;//gets multiplied when added
+ }
+ charSprite.x = xPos;
+ charSprite.y = yPos + maxHeight - charSprite.height;
+ add(charSprite);
+
+ xPos += charSprite.width;
+ charCount++;
+ }
+ }
+ }
+ }
+
+ override function toString()
+ {
+ return "InputItem, " + FlxStringUtil.getDebugString(
+ [ LabelValuePair.weak("x", x)
+ , LabelValuePair.weak("y", y)
+ , LabelValuePair.weak("text", text)
+ ]
+ );
+ }
+}
+
+class AtlasChar extends FlxSprite
+{
+ public var char(default, set):String;
+ public function new(x = 0.0, y = 0.0, atlas:FlxAtlasFrames, char:String)
+ {
+ super(x, y);
+ frames = atlas;
+ this.char = char;
+ antialiasing = true;
+ }
+
+ function set_char(value:String)
+ {
+ if (this.char != value)
+ {
+ var prefix = getAnimPrefix(value);
+ animation.addByPrefix("anim", prefix, 24);
+ animation.play("anim");
+ updateHitbox();
+ }
+
+ return this.char = value;
+ }
+
+ function getAnimPrefix(char:String)
+ {
+ return switch (char)
+ {
+ case '-': '-dash-';
+ case '.': '-period-';
+ case ",": '-comma-';
+ case "'": '-apostraphie-';
+ case "?": '-question mark-';
+ case "!": '-exclamation point-';
+ case "\\": '-back slash-';
+ case "/": '-forward slash-';
+ case "*": '-multiply x-';
+ case "“": '-start quote-';
+ case "”": '-end quote-';
+ default: char;
+ }
+ }
+}
+
+private class AtlasFontData
+{
+ static public var upperChar = ~/^[A-Z]\d+$/;
+ static public var lowerChar = ~/^[a-z]\d+$/;
+
+ public var atlas:FlxAtlasFrames;
+ public var maxHeight:Float = 0.0;
+ public var caseAllowed:Case = Both;
+
+ public function new (name:AtlasFont)
+ {
+ atlas = Paths.getSparrowAtlas("fonts/" + name.getName().toLowerCase());
+ atlas.parent.destroyOnNoUse = false;
+ atlas.parent.persist = true;
+
+ var containsUpper = false;
+ var containsLower = false;
+
+ for (frame in atlas.frames)
+ {
+ maxHeight = Math.max(maxHeight, frame.frame.height);
+
+ if (!containsUpper)
+ containsUpper = upperChar.match(frame.name);
+
+ if (!containsLower)
+ containsLower = lowerChar.match(frame.name);
+ }
+
+ if (containsUpper != containsLower)
+ caseAllowed = containsUpper ? Upper : Lower;
+ }
+}
+
+enum Case
+{
+ Both;
+ Upper;
+ Lower;
+}
+
+enum AtlasFont
+{
+ Default;
+ Bold;
+}
\ No newline at end of file
diff --git a/source/ui/ControlsMenu.hx b/source/ui/ControlsMenu.hx
new file mode 100644
index 0000000000..cd1e8b9f81
--- /dev/null
+++ b/source/ui/ControlsMenu.hx
@@ -0,0 +1,356 @@
+package ui;
+
+import flixel.input.actions.FlxActionInput;
+import flixel.input.gamepad.FlxGamepadInputID;
+import flixel.FlxG;
+import flixel.FlxCamera;
+import flixel.FlxObject;
+import flixel.FlxSprite;
+import flixel.group.FlxGroup;
+import flixel.input.keyboard.FlxKey;
+
+import Controls;
+import ui.AtlasText;
+import ui.MenuList;
+import ui.TextMenuList;
+
+class ControlsMenu extends ui.OptionsState.Page
+{
+ inline static public var COLUMNS = 2;
+ static var controlList = Control.createAll();
+ /*
+ * Defines groups of controls that cannot share inputs, like left and right. Say, if ACCEPT is Z, Back is X,
+ * if the player sets Back to Z it also set ACCEPT to X. This prevents the player from setting the controls in
+ * a way the prevents them from changing more controls or exiting the menu.
+ */
+ static var controlGroups:Array> =
+ [ [ NOTE_UP, NOTE_DOWN, NOTE_LEFT, NOTE_RIGHT ]
+ , [ UI_UP, UI_DOWN, UI_LEFT, UI_RIGHT, ACCEPT, BACK ]
+ ];
+
+ var itemGroups:Array> = [for (i in 0...controlGroups.length) []];
+
+ var controlGrid:MenuTypedList;
+ var deviceList:TextMenuList;
+ var menuCamera:FlxCamera;
+ var prompt:Prompt;
+ var camFollow:FlxObject;
+ var labels:FlxTypedGroup;
+
+ var currentDevice:Device = Keys;
+ var deviceListSelected = false;
+
+ public function new()
+ {
+ super();
+
+ menuCamera = new FlxCamera();
+ FlxG.cameras.add(menuCamera, false);
+ menuCamera.bgColor = 0x0;
+ camera = menuCamera;
+
+ labels = new FlxTypedGroup();
+ var headers = new FlxTypedGroup();
+ controlGrid = new MenuTypedList(Columns(COLUMNS), Vertical);
+
+ add(labels);
+ add(headers);
+ add(controlGrid);
+
+ if (FlxG.gamepads.numActiveGamepads > 0)
+ {
+ var devicesBg = new FlxSprite();
+ devicesBg.makeGraphic(FlxG.width, 100, 0xFFfafd6d);
+ add(devicesBg);
+ deviceList = new TextMenuList(Horizontal, None);
+ add(deviceList);
+ deviceListSelected = true;
+
+ var item;
+
+ item = deviceList.createItem("Keyboard", Bold, selectDevice.bind(Keys));
+ item.x = FlxG.width / 2 - item.width - 30;
+ item.y = (devicesBg.height - item.height) / 2;
+
+ item = deviceList.createItem("Gamepad", Bold, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id)));
+ item.x = FlxG.width / 2 + 30;
+ item.y = (devicesBg.height - item.height) / 2;
+ }
+
+ // FlxG.debugger.drawDebug = true;
+ var y = deviceList == null ? 30 : 120;
+ var spacer = 70;
+ var currentHeader:String = null;
+ // list order is determined by enum order
+ for (i in 0...controlList.length)
+ {
+ var control = controlList[i];
+ var name = control.getName();
+ if (currentHeader != "UI_" && name.indexOf("UI_") == 0)
+ {
+ currentHeader = "UI_";
+ headers.add(new BoldText(0, y, "UI")).screenCenter(X);
+ y += spacer;
+ }
+ else if (currentHeader != "NOTE_" && name.indexOf("NOTE_") == 0)
+ {
+ currentHeader = "NOTE_";
+ headers.add(new BoldText(0, y, "NOTES")).screenCenter(X);
+ y += spacer;
+ }
+
+ if (currentHeader != null && name.indexOf(currentHeader) == 0)
+ name = name.substr(currentHeader.length);
+
+ var label = labels.add(new BoldText(150, y, name));
+ label.alpha = 0.6;
+ for (i in 0...COLUMNS)
+ createItem(label.x + 400 + i * 300, y, control, i);
+
+ y += spacer;
+ }
+
+ camFollow = new FlxObject(FlxG.width / 2, 0, 70, 70);
+ if (deviceList != null)
+ {
+ camFollow.y = deviceList.selectedItem.y;
+ controlGrid.selectedItem.idle();
+ controlGrid.enabled = false;
+ }
+ else
+ camFollow.y = controlGrid.selectedItem.y;
+
+ menuCamera.follow(camFollow, null, 0.06);
+ var margin = 100;
+ menuCamera.deadzone.set(0, margin, menuCamera.width, menuCamera.height - margin * 2);
+ menuCamera.minScrollY = 0;
+ controlGrid.onChange.add(function (selected)
+ {
+ camFollow.y = selected.y;
+
+ labels.forEach((label)->label.alpha = 0.6);
+ labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0;
+ });
+
+ prompt = new Prompt("\nPress any key to rebind\n\n\n\n Escape to cancel", None);
+ prompt.create();
+ prompt.createBgFromMargin(100, 0xFFfafd6d);
+ prompt.back.scrollFactor.set(0, 0);
+ prompt.exists = false;
+ add(prompt);
+ }
+
+ function createItem(x = 0.0, y = 0.0, control:Control, index:Int)
+ {
+ var item = new InputItem(x, y, currentDevice, control, index, onSelect);
+ for (i in 0...controlGroups.length)
+ {
+ if (controlGroups[i].contains(control))
+ itemGroups[i].push(item);
+ }
+
+ return controlGrid.addItem(item.name, item);
+ }
+
+ function onSelect():Void
+ {
+ controlGrid.enabled = false;
+ canExit = false;
+ prompt.exists = true;
+ }
+
+ function goToDeviceList()
+ {
+ controlGrid.selectedItem.idle();
+ labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 0.6;
+ controlGrid.enabled = false;
+ deviceList.enabled = true;
+ canExit = true;
+ camFollow.y = deviceList.selectedItem.y;
+ deviceListSelected = true;
+ }
+
+ function selectDevice(device:Device)
+ {
+ currentDevice = device;
+
+ for (item in controlGrid.members)
+ item.updateDevice(currentDevice);
+
+ var inputName = device == Keys ? "key" : "button";
+ var cancel = device == Keys ? "Escape" : "Back";
+ //todo: alignment
+ if (device == Keys)
+ prompt.setText('\nPress any key to rebind\n\n\n\n $cancel to cancel');
+ else
+ prompt.setText('\nPress any button\n to rebind\n\n\n $cancel to cancel');
+
+
+ controlGrid.selectedItem.select();
+ labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0;
+ controlGrid.enabled = true;
+ deviceList.enabled = false;
+ deviceListSelected = false;
+ canExit = false;
+ }
+
+ override function update(elapsed:Float)
+ {
+ super.update(elapsed);
+
+ var controls = PlayerSettings.player1.controls;
+ if (controlGrid.enabled && deviceList != null && deviceListSelected == false && controls.BACK)
+ goToDeviceList();
+
+ if (prompt.exists)
+ {
+ switch (currentDevice)
+ {
+ case Keys:
+ {
+ // check released otherwise bugs can happen when you change the BACK key
+ var key = FlxG.keys.firstJustReleased();
+ if (key != NONE)
+ {
+ if (key != ESCAPE)
+ onInputSelect(key);
+ closePrompt();
+ }
+ }
+ case Gamepad(id):
+ {
+ var button = FlxG.gamepads.getByID(id).firstJustReleasedID();
+ if (button != NONE)
+ {
+ if (button != BACK)
+ onInputSelect(button);
+ closePrompt();
+ }
+ }
+ }
+ }
+ }
+
+ function onInputSelect(input:Int)
+ {
+ var item = controlGrid.selectedItem;
+
+ // check if that key is already set for this
+ var column0 = Math.floor(controlGrid.selectedIndex / 2) * 2;
+ for (i in 0...COLUMNS)
+ {
+ if (controlGrid.members[column0 + i].input == input)
+ return;
+ }
+
+ // Check if items in the same group already have the new input
+ for (group in itemGroups)
+ {
+ if (group.contains(item))
+ {
+ for (otherItem in group)
+ {
+ if (otherItem != item && otherItem.input == input)
+ {
+ // replace that input with this items old input.
+ PlayerSettings.player1.controls.replaceBinding(otherItem.control, currentDevice, item.input, otherItem.input);
+ // Don't use resetItem() since items share names/labels
+ otherItem.input = item.input;
+ otherItem.label.text = item.label.text;
+ }
+ }
+ }
+ }
+
+ PlayerSettings.player1.controls.replaceBinding(item.control, currentDevice, input, item.input);
+ // Don't use resetItem() since items share names/labels
+ item.input = input;
+ item.label.text = item.getLabel(input);
+
+ PlayerSettings.player1.saveControls();
+ }
+
+ function closePrompt()
+ {
+ prompt.exists = false;
+ controlGrid.enabled = true;
+ if (deviceList == null)
+ canExit = true;
+ }
+
+ override function destroy()
+ {
+ super.destroy();
+
+ itemGroups = null;
+
+ if (FlxG.cameras.list.contains(menuCamera))
+ FlxG.cameras.remove(menuCamera);
+ }
+
+ override function set_enabled(value:Bool)
+ {
+ if (value == false)
+ {
+ controlGrid.enabled = false;
+ if (deviceList != null)
+ deviceList.enabled = false;
+ }
+ else
+ {
+ controlGrid.enabled = !deviceListSelected;
+ if (deviceList != null)
+ deviceList.enabled = deviceListSelected;
+ }
+ return super.set_enabled(value);
+ }
+}
+
+class InputItem extends TextMenuItem
+{
+ public var device(default, null):Device = Keys;
+ public var control:Control;
+ public var input:Int = -1;
+ public var index:Int = -1;
+
+ public function new (x = 0.0, y = 0.0, device, control, index, ?callback)
+ {
+ this.device = device;
+ this.control = control;
+ this.index = index;
+ this.input = getInput();
+
+ super(x, y, getLabel(input), Default, callback);
+ }
+
+ public function updateDevice(device:Device)
+ {
+ if (this.device != device)
+ {
+ this.device = device;
+ input = getInput();
+ label.text = getLabel(input);
+ }
+ }
+
+ function getInput()
+ {
+ var list = PlayerSettings.player1.controls.getInputsFor(control, device);
+ if (list.length > index)
+ {
+ if (list[index] != FlxKey.ESCAPE || list[index] != FlxGamepadInputID.BACK)
+ return list[index];
+
+ if (list.length > ControlsMenu.COLUMNS)
+ // Escape isn't mappable, show a third option, instead.
+ return list[ControlsMenu.COLUMNS];
+ }
+
+ return -1;
+ }
+
+ public function getLabel(input:Int)
+ {
+ return input == -1 ? "---" : InputFormatter.format(input, device);
+ }
+}
\ No newline at end of file
diff --git a/source/ui/MenuList.hx b/source/ui/MenuList.hx
new file mode 100644
index 0000000000..6d20d7d827
--- /dev/null
+++ b/source/ui/MenuList.hx
@@ -0,0 +1,366 @@
+package ui;
+
+import flixel.math.FlxPoint;
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.effects.FlxFlicker;
+import flixel.group.FlxGroup;
+import flixel.util.FlxSignal;
+
+class MenuTypedList extends FlxTypedGroup
+{
+ public var selectedIndex(default, null) = 0;
+ public var selectedItem(get, never):T;
+ /** Called when a new item is highlighted */
+ public var onChange(default, null) = new FlxTypedSignalVoid>();
+ /** Called when an item is accepted */
+ public var onAcceptPress(default, null) = new FlxTypedSignalVoid>();
+ /** The navigation control scheme to use */
+ public var navControls:NavControls;
+ /** Set to false to disable nav control */
+ public var enabled:Bool = true;
+ /** */
+ public var wrapMode:WrapMode = Both;
+
+ var byName = new Map();
+ /** Set to true, internally to disable controls, without affecting vars like `enabled` */
+ var busy:Bool = false;
+
+ public function new (navControls:NavControls = Vertical, ?wrapMode:WrapMode)
+ {
+ this.navControls = navControls;
+
+ if (wrapMode != null)
+ this.wrapMode = wrapMode;
+ else
+ this.wrapMode = switch (navControls)
+ {
+ case Horizontal: Horizontal;
+ case Vertical: Vertical;
+ default: Both;
+ }
+ super();
+ }
+
+ public function addItem(name:String, item:T):T
+ {
+ if (length == selectedIndex)
+ item.select();
+
+ byName[name] = item;
+ return add(item);
+ }
+
+ public function resetItem(oldName:String, newName:String, ?callback:Void->Void):T
+ {
+ if (!byName.exists(oldName))
+ throw "No item named:" + oldName;
+
+ var item = byName[oldName];
+ byName.remove(oldName);
+ byName[newName] = item;
+ item.setItem(newName, callback);
+
+ return item;
+ }
+
+ override function update(elapsed:Float)
+ {
+ super.update(elapsed);
+
+ if (enabled && !busy)
+ updateControls();
+ }
+
+ inline function updateControls()
+ {
+ var controls = PlayerSettings.player1.controls;
+
+ var wrapX = wrapMode.match(Horizontal | Both);
+ var wrapY = wrapMode.match(Vertical | Both);
+ var newIndex = switch(navControls)
+ {
+ case Vertical : navList(controls.UI_UP_P , controls.UI_DOWN_P, wrapY);
+ case Horizontal : navList(controls.UI_LEFT_P, controls.UI_RIGHT_P, wrapX);
+ case Both : navList(controls.UI_LEFT_P || controls.UI_UP_P, controls.UI_RIGHT_P || controls.UI_DOWN_P, !wrapMode.match(None));
+
+ case Columns(num): navGrid(num, controls.UI_LEFT_P, controls.UI_RIGHT_P, wrapX, controls.UI_UP_P , controls.UI_DOWN_P , wrapY);
+ case Rows (num): navGrid(num, controls.UI_UP_P , controls.UI_DOWN_P , wrapY, controls.UI_LEFT_P, controls.UI_RIGHT_P, wrapX);
+ }
+
+ if (newIndex != selectedIndex)
+ {
+ FlxG.sound.play(Paths.sound('scrollMenu'));
+ selectItem(newIndex);
+ }
+
+ //Todo: bypass popup blocker on firefox
+ if (controls.ACCEPT)
+ accept();
+ }
+
+ function navAxis(index:Int, size:Int, prev:Bool, next:Bool, allowWrap:Bool):Int
+ {
+ if (prev == next)
+ return index;
+
+ if (prev)
+ {
+ if (index > 0)
+ index--;
+ else if (allowWrap)
+ index = size - 1;
+ }
+ else
+ {
+ if (index < size - 1)
+ index++;
+ else if (allowWrap)
+ index = 0;
+ }
+
+ return index;
+ }
+
+ /**
+ * Controls navigation on a linear list of items such as Vertical.
+ * @param prev
+ * @param next
+ * @param allowWrap
+ */
+ inline function navList(prev:Bool, next:Bool, allowWrap:Bool)
+ {
+ return navAxis(selectedIndex, length, prev, next, allowWrap);
+ }
+
+ /**
+ * Controls navigation on a grid
+ * @param latSize The size of the fixed axis of the grid, or the "lateral axis"
+ * @param latPrev Whether the 'prev' key is pressed along the fixed-lengthed axis. eg: "left" in Column mode
+ * @param latNext Whether the 'next' key is pressed along the fixed-lengthed axis. eg: "right" in Column mode
+ * @param prev Whether the 'prev' key is pressed along the variable-lengthed axis. eg: "up" in Column mode
+ * @param next Whether the 'next' key is pressed along the variable-lengthed axis. eg: "down" in Column mode
+ * @param allowWrap unused
+ */
+ function navGrid(latSize:Int, latPrev:Bool, latNext:Bool, latAllowWrap:Bool, prev:Bool, next:Bool, allowWrap:Bool):Int
+ {
+ // The grid lenth along the variable-length axis
+ var size = Math.ceil(length / latSize);
+ // The selected position along the variable-length axis
+ var index = Math.floor(selectedIndex / latSize);
+ // The selected position along the fixed axis
+ var latIndex = selectedIndex % latSize;
+
+ latIndex = navAxis(latIndex, latSize, latPrev, latNext, latAllowWrap);
+ index = navAxis(index, size, prev, next, allowWrap);
+
+ return Std.int(Math.min(length - 1, index * latSize + latIndex));
+ }
+
+ public function accept()
+ {
+ var selected = members[selectedIndex];
+ onAcceptPress.dispatch(selected);
+
+ if (selected.fireInstantly)
+ selected.callback();
+ else
+ {
+ busy = true;
+ FlxG.sound.play(Paths.sound('confirmMenu'));
+ FlxFlicker.flicker(selected, 1, 0.06, true, false, function(_)
+ {
+ busy = false;
+ selected.callback();
+ });
+ }
+ }
+
+ public function selectItem(index:Int)
+ {
+ members[selectedIndex].idle();
+
+ selectedIndex = index;
+
+ var selected = members[selectedIndex];
+ selected.select();
+ onChange.dispatch(selected);
+ }
+
+ public function has(name:String)
+ {
+ return byName.exists(name);
+ }
+
+ public function getItem(name:String)
+ {
+ return byName[name];
+ }
+
+ override function destroy()
+ {
+ super.destroy();
+ byName.clear();
+ onChange.removeAll();
+ onAcceptPress.removeAll();
+ }
+
+ inline function get_selectedItem():T
+ {
+ return members[selectedIndex];
+ }
+}
+
+class MenuItem extends FlxSprite
+{
+ public var callback:Void->Void;
+ public var name:String;
+ /**
+ * Set to true for things like opening URLs otherwise, it may it get blocked.
+ */
+ public var fireInstantly = false;
+ public var selected(get, never):Bool;
+ function get_selected() return alpha == 1.0;
+
+ public function new (x = 0.0, y = 0.0, name:String, callback)
+ {
+ super(x, y);
+
+ antialiasing = true;
+ setData(name, callback);
+ idle();
+ }
+
+ function setData(name:String, ?callback:Void->Void)
+ {
+ this.name = name;
+
+ if (callback != null)
+ this.callback = callback;
+ }
+
+ /**
+ * Calls setData and resets/redraws the state of the item
+ * @param name the label.
+ * @param callback Unchanged if null.
+ */
+ public function setItem(name:String, ?callback:Void->Void)
+ {
+ setData(name, callback);
+
+ if (selected)
+ select();
+ else
+ idle();
+ }
+
+ public function idle()
+ {
+ alpha = 0.6;
+ }
+
+ public function select()
+ {
+ alpha = 1.0;
+ }
+}
+
+class MenuTypedItem extends MenuItem
+{
+ public var label(default, set):T;
+
+ public function new (x = 0.0, y = 0.0, label:T, name:String, callback)
+ {
+ super(x, y, name, callback);
+ // set label after super otherwise setters fuck up
+ this.label = label;
+ }
+
+ /**
+ * Use this when you only want to show the label
+ */
+ function setEmptyBackground()
+ {
+ var oldWidth = width;
+ var oldHeight = height;
+ makeGraphic(1, 1, 0x0);
+ width = oldWidth;
+ height = oldHeight;
+ }
+
+ function set_label(value:T)
+ {
+ if (value != null)
+ {
+ value.x = x;
+ value.y = y;
+ value.alpha = alpha;
+ }
+ return this.label = value;
+ }
+
+ override function update(elapsed:Float)
+ {
+ super.update(elapsed);
+ if (label != null)
+ label.update(elapsed);
+ }
+
+ override function draw()
+ {
+ super.draw();
+ if (label != null)
+ {
+ label.cameras = cameras;
+ label.scrollFactor.copyFrom(scrollFactor);
+ label.draw();
+ }
+ }
+
+ override function set_alpha(value:Float):Float
+ {
+ super.set_alpha(value);
+
+ if (label != null)
+ label.alpha = alpha;
+
+ return alpha;
+ }
+
+ override function set_x(value:Float):Float
+ {
+ super.set_x(value);
+
+ if (label != null)
+ label.x = x;
+
+ return x;
+ }
+
+ override function set_y(Value:Float):Float
+ {
+ super.set_y(Value);
+
+ if (label != null)
+ label.y = y;
+
+ return y;
+ }
+}
+
+enum NavControls
+{
+ Horizontal;
+ Vertical;
+ Both;
+ Columns(num:Int);
+ Rows(num:Int);
+}
+
+enum WrapMode
+{
+ Horizontal;
+ Vertical;
+ Both;
+ None;
+}
\ No newline at end of file
diff --git a/source/ui/NgPrompt.hx b/source/ui/NgPrompt.hx
new file mode 100644
index 0000000000..4bf75997fa
--- /dev/null
+++ b/source/ui/NgPrompt.hx
@@ -0,0 +1,107 @@
+package ui;
+
+import NGio;
+import ui.Prompt;
+
+class NgPrompt extends Prompt
+{
+ public function new (text:String, style:ButtonStyle = Yes_No)
+ {
+ super(text, style);
+ }
+
+ static public function showLogin()
+ {
+ return showLoginPrompt(true);
+ }
+
+ static public function showSavedSessionFailed()
+ {
+ return showLoginPrompt(false);
+ }
+
+ static function showLoginPrompt(fromUi:Bool)
+ {
+ var prompt = new NgPrompt("Talking to server...", None);
+ prompt.openCallback = NGio.login.bind
+ (
+ function popupLauncher(openPassportUrl)
+ {
+ var choiceMsg = fromUi
+ ? #if web "Log in to Newgrounds?" #else null #end // User-input needed to allow popups
+ : "Your session has expired.\n Please login again.";
+
+ if (choiceMsg != null)
+ {
+ prompt.setText(choiceMsg);
+ prompt.setButtons(Yes_No);
+ #if web
+ prompt.buttons.getItem("yes").fireInstantly = true;
+ #end
+ prompt.onYes = function()
+ {
+ prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end);
+ prompt.setButtons(None);
+ openPassportUrl();
+ };
+ prompt.onNo = function()
+ {
+ prompt.close();
+ prompt = null;
+ NGio.cancelLogin();
+ };
+ }
+ else
+ {
+ prompt.setText("Connecting...");
+ openPassportUrl();
+ }
+ },
+ function onLoginComplete(result:ConnectionResult)
+ {
+ switch (result)
+ {
+ case Success:
+ {
+ prompt.setText("Login Successful");
+ prompt.setButtons(Ok);
+ prompt.onYes = prompt.close;
+ }
+ case Fail(msg):
+ {
+ trace("Login Error:" + msg);
+ prompt.setText("Login failed");
+ prompt.setButtons(Ok);
+ prompt.onYes = prompt.close;
+ }
+ case Cancelled:
+ {
+ if (prompt != null)
+ {
+ prompt.setText("Login cancelled by user");
+ prompt.setButtons(Ok);
+ prompt.onYes = prompt.close;
+ }
+ else
+ trace("Login cancelled via prompt");
+ }
+ }
+ }
+ );
+
+ return prompt;
+ }
+
+ static public function showLogout()
+ {
+ var user = io.newgrounds.NG.core.user.name;
+ var prompt = new NgPrompt('Log out of $user?', Yes_No);
+ prompt.onYes = function()
+ {
+ NGio.logout();
+ prompt.close();
+ };
+ prompt.onNo = prompt.close;
+ return prompt;
+ }
+}
\ No newline at end of file
diff --git a/source/ui/OptionsState.hx b/source/ui/OptionsState.hx
new file mode 100644
index 0000000000..0e4fae95c6
--- /dev/null
+++ b/source/ui/OptionsState.hx
@@ -0,0 +1,260 @@
+package ui;
+
+import flixel.FlxSubState;
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.group.FlxGroup;
+import flixel.util.FlxSignal;
+
+import flixel.addons.transition.FlxTransitionableState;
+
+// typedef OptionsState = OptionsMenu_old;
+
+// class OptionsState_new extends MusicBeatState
+class OptionsState extends MusicBeatState
+{
+ var pages = new Map();
+ var currentName:PageName = #if newgrounds Options #else Controls #end;
+ var currentPage(get, never):Page;
+ inline function get_currentPage() return pages[currentName];
+
+ override function create()
+ {
+ var menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
+ menuBG.color = 0xFFea71fd;
+ menuBG.setGraphicSize(Std.int(menuBG.width * 1.1));
+ menuBG.updateHitbox();
+ menuBG.screenCenter();
+ menuBG.scrollFactor.set(0, 0);
+ add(menuBG);
+
+ var options = addPage(Options, new OptionsMenu(false));
+ var controls = addPage(Controls, new ControlsMenu());
+
+ if (options.hasMultipleOptions())
+ {
+ options.onExit.add(exitToMainMenu);
+ controls.onExit.add(switchPage.bind(Options));
+ }
+ else
+ {
+ // No need to show Options page
+ controls.onExit.add(exitToMainMenu);
+ setPage(Controls);
+ }
+
+ // disable for intro transition
+ currentPage.enabled = false;
+ super.create();
+ }
+
+ function addPage(name:PageName, page:T)
+ {
+ page.onSwitch.add(switchPage);
+ pages[name] = page;
+ add(page);
+ page.exists = currentName == name;
+ return page;
+ }
+
+ function setPage(name:PageName)
+ {
+ if (pages.exists(currentName))
+ currentPage.exists = false;
+
+ currentName = name;
+
+ if (pages.exists(currentName))
+ currentPage.exists = true;
+ }
+
+ override function finishTransIn()
+ {
+ super.finishTransIn();
+
+ currentPage.enabled = true;
+ }
+
+ function switchPage(name:PageName)
+ {
+ //Todo animate?
+ setPage(name);
+ }
+
+ function exitToMainMenu()
+ {
+ currentPage.enabled = false;
+ //Todo animate?
+ FlxG.switchState(new MainMenuState());
+ }
+}
+
+class Page extends FlxGroup
+{
+ public var onSwitch(default, null) = new FlxTypedSignalVoid>();
+ public var onExit(default, null) = new FlxSignal();
+
+ public var enabled(default, set) = true;
+ public var canExit = true;
+
+ var controls(get, never):Controls;
+ inline function get_controls() return PlayerSettings.player1.controls;
+
+ var subState:FlxSubState;
+
+ inline function switchPage(name:PageName)
+ {
+ onSwitch.dispatch(name);
+ }
+
+ inline function exit()
+ {
+ onExit.dispatch();
+ }
+
+ override function update(elapsed:Float)
+ {
+ super.update(elapsed);
+
+ if (enabled)
+ updateEnabled(elapsed);
+ }
+
+ function updateEnabled(elapsed:Float)
+ {
+ if (canExit && controls.BACK)
+ exit();
+ }
+
+ function set_enabled(value:Bool)
+ {
+ return this.enabled = value;
+ }
+
+ function openPrompt(prompt:Prompt, onClose:Void->Void)
+ {
+ enabled = false;
+ prompt.closeCallback = function ()
+ {
+ enabled = true;
+ if (onClose != null)
+ onClose();
+ }
+
+ FlxG.state.openSubState(prompt);
+ }
+
+ override function destroy()
+ {
+ super.destroy();
+ onSwitch.removeAll();
+ }
+}
+
+class OptionsMenu extends Page
+{
+ var items:TextMenuList;
+
+ public function new (showDonate:Bool)
+ {
+ super();
+
+ add(items = new TextMenuList());
+ createItem("controls", function() switchPage(Controls));
+ #if CAN_OPEN_LINKS
+ if (showDonate)
+ {
+ var hasPopupBlocker = #if web true #else false #end;
+ createItem('donate', selectDonate, hasPopupBlocker);
+ }
+ #end
+ #if newgrounds
+ if (NGio.isLoggedIn)
+ createItem("logout", selectLogout);
+ else
+ createItem("login", selectLogin);
+ #end
+ createItem("exit", exit);
+ }
+
+ function createItem(name:String, callback:Void->Void, fireInstantly = false)
+ {
+ var item = items.createItem(0, 100 + items.length * 100, name, Bold, callback);
+ item.fireInstantly = fireInstantly;
+ item.screenCenter(X);
+ return item;
+ }
+
+ override function set_enabled(value:Bool)
+ {
+ items.enabled = value;
+ return super.set_enabled(value);
+ }
+
+ /**
+ * True if this page has multiple options, ecluding the exit option.
+ * If false, there's no reason to ever show this page.
+ */
+ public function hasMultipleOptions():Bool
+ {
+ return items.length > 2;
+ }
+
+ #if CAN_OPEN_LINKS
+ function selectDonate()
+ {
+ #if linux
+ Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]);
+ #else
+ FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
+ #end
+ }
+ #end
+
+ #if newgrounds
+ function selectLogin()
+ {
+ openNgPrompt(NgPrompt.showLogin());
+ }
+
+ function selectLogout()
+ {
+ openNgPrompt(NgPrompt.showLogout());
+ }
+
+ /**
+ * Calls openPrompt and redraws the login/logout button
+ * @param prompt
+ * @param onClose
+ */
+ public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void)
+ {
+ var onPromptClose = checkLoginStatus;
+ if (onClose != null)
+ {
+ onPromptClose = function ()
+ {
+ checkLoginStatus();
+ onClose();
+ }
+ }
+
+ openPrompt(prompt, onPromptClose);
+ }
+
+ function checkLoginStatus()
+ {
+ var prevLoggedIn = items.has("logout");
+ if (prevLoggedIn && !NGio.isLoggedIn)
+ items.resetItem("login", "logout", selectLogout);
+ else if (!prevLoggedIn && NGio.isLoggedIn)
+ items.resetItem("logout", "login", selectLogin);
+ }
+ #end
+}
+
+enum PageName
+{
+ Options;
+ Controls;
+}
diff --git a/source/ui/Prompt.hx b/source/ui/Prompt.hx
new file mode 100644
index 0000000000..0ef80007d1
--- /dev/null
+++ b/source/ui/Prompt.hx
@@ -0,0 +1,121 @@
+package ui;
+
+import ui.AtlasText;
+import ui.MenuList;
+
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.graphics.frames.FlxAtlasFrames;
+import flixel.text.FlxText;
+import flixel.util.FlxColor;
+
+class Prompt extends flixel.FlxSubState
+{
+ inline static var MARGIN = 100;
+
+ public var onYes:Void->Void;
+ public var onNo:Void->Void;
+ public var buttons:TextMenuList;
+ public var field:AtlasText;
+ public var back:FlxSprite;
+
+ var style:ButtonStyle;
+
+ public function new (text:String, style:ButtonStyle = Ok)
+ {
+ this.style = style;
+ super(0x80000000);
+
+ buttons = new TextMenuList(Horizontal);
+
+ field = new BoldText(text);
+ field.scrollFactor.set(0, 0);
+ }
+
+ override function create()
+ {
+ super.create();
+
+ field.y = MARGIN;
+ field.screenCenter(X);
+ add(field);
+
+ createButtons();
+ add(buttons);
+ }
+
+ public function createBg(width:Int, height:Int, color = 0xFF808080)
+ {
+ back = new FlxSprite();
+ back.makeGraphic(width, height, color, false, "prompt-bg");
+ back.screenCenter(XY);
+ add(back);
+ members.unshift(members.pop());// bring to front
+ }
+
+
+ public function createBgFromMargin(margin = MARGIN, color = 0xFF808080)
+ {
+ createBg(Std.int(FlxG.width - margin * 2), Std.int(FlxG.height - margin * 2), color);
+ }
+
+ public function setButtons(style:ButtonStyle)
+ {
+ if (this.style != style)
+ {
+ this.style = style;
+ createButtons();
+ }
+ }
+
+ function createButtons()
+ {
+ // destroy previous buttons
+ while(buttons.members.length > 0)
+ {
+ buttons.remove(buttons.members[0], true).destroy();
+ }
+
+ switch(style)
+ {
+ case Yes_No : createButtonsHelper("yes", "no");
+ case Ok : createButtonsHelper("ok");
+ case Custom(yes, no): createButtonsHelper(yes, no);
+ case None : buttons.exists = false;
+ };
+ }
+
+ function createButtonsHelper(yes:String, ?no:String)
+ {
+ buttons.exists = true;
+ // pass anonymous functions rather than the current callbacks, in case they change later
+ var yesButton = buttons.createItem(yes, function() onYes());
+ yesButton.screenCenter(X);
+ yesButton.y = FlxG.height - yesButton.height - MARGIN;
+ yesButton.scrollFactor.set(0, 0);
+ if (no != null)
+ {
+ // place right
+ yesButton.x = FlxG.width - yesButton.width - MARGIN;
+
+ var noButton = buttons.createItem(no, function() onNo());
+ noButton.x = MARGIN;
+ noButton.y = FlxG.height - noButton.height - MARGIN;
+ noButton.scrollFactor.set(0, 0);
+ }
+ }
+
+ public function setText(text:String)
+ {
+ field.text = text;
+ field.screenCenter(X);
+ }
+}
+
+enum ButtonStyle
+{
+ Ok;
+ Yes_No;
+ Custom(yes:String, no:Null);//Todo: more than 2
+ None;
+}
\ No newline at end of file
diff --git a/source/ui/TextMenuList.hx b/source/ui/TextMenuList.hx
new file mode 100644
index 0000000000..d1c617b17f
--- /dev/null
+++ b/source/ui/TextMenuList.hx
@@ -0,0 +1,56 @@
+package ui;
+
+import ui.AtlasText;
+import ui.MenuList;
+
+class TextMenuList extends MenuTypedList
+{
+ public function new (navControls:NavControls = Vertical, ?wrapMode)
+ {
+ super(navControls, wrapMode);
+ }
+
+ public function createItem(x = 0.0, y = 0.0, name:String, font:AtlasFont = Bold, callback, fireInstantly = false)
+ {
+ var item = new TextMenuItem(x, y, name, font, callback);
+ item.fireInstantly = fireInstantly;
+ return addItem(name, item);
+ }
+}
+
+class TextMenuItem extends TextTypedMenuItem
+{
+ public function new (x = 0.0, y = 0.0, name:String, font:AtlasFont = Bold, callback)
+ {
+ super(x, y, new AtlasText(0, 0, name, font), name, callback);
+ setEmptyBackground();
+ }
+}
+
+class TextTypedMenuItem extends MenuTypedItem
+{
+ public function new (x = 0.0, y = 0.0, label:T, name:String, callback)
+ {
+ super(x, y, label, name, callback);
+ }
+
+ override function setItem(name:String, ?callback:Void -> Void)
+ {
+ if (label != null)
+ {
+ label.text = name;
+ label.alpha = alpha;
+ width = label.width;
+ height = label.height;
+ }
+
+ super.setItem(name, callback);
+ }
+
+ override function set_label(value:T):T
+ {
+ super.set_label(value);
+ setItem(name, callback);
+ return value;
+ }
+}
\ No newline at end of file