diff --git a/Project.xml b/Project.xml index 3572496c2..192a2dc7d 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 d4ad6bcdc..8b14b23cd 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 af724a866..d143b1869 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 000000000..ccecc93eb 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 000000000..fac33b181 --- /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 000000000..8da9568ce --- /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 4399ed2dd..bd0ffbd0e 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 01d1a5eb4..fe5bb0c51 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 000000000..f98b5865c 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 000000000..a8d80f55b 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 000000000..0d3578fed --- /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 000000000..07d6c4e44 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 000000000..3f82b1244 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 000000000..1f0870399 --- /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 000000000..5f6d77456 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 000000000..a2267f5f9 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 000000000..1ff056cec --- /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 000000000..ead7b53db 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 000000000..d7cba070e 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 000000000..351b2b10e --- /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 77ac5dc8e..6fca9d0a5 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 7d1e2c40e..760fc0af0 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -11,57 +11,6 @@ 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 -{ - 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"; -} -#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 ACCEPT = "accept"; - var BACK = "back"; - var PAUSE = "pause"; - var RESET = "reset"; - var CHEAT = "cheat"; -} -#end - -enum Device -{ - Keys; - 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 @@ -69,15 +18,64 @@ enum Device */ enum Control { - UP; - LEFT; - RIGHT; - DOWN; + // 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 +} + +@:enum +abstract Action(String) to String from String +{ + 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 +} + +enum Device +{ + Keys; + Gamepad(id:Int); } enum KeyboardScheme @@ -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; + 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(); - inline function get_UP() - return _up.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 LEFT(get, never):Bool; + 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(); - inline function get_LEFT() - return _left.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 RIGHT(get, never):Bool; + 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(); - 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 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)); + } + } + + function replaceKey(action:FlxActionDigital, toAdd:Int, toRemove:Int) + { + for (i in 0...action.inputs.length) + { + var input = action.inputs[i]; + if (input.device == KEYBOARD && input.inputID == toRemove) + { + @: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; + } } } public function copyFrom(controls:Controls, ?device:Device) { - #if (haxe >= "4.0.0") - for (name => action in controls.byName) - { - for (input in action.inputs) - { - if (device == null || isDevice(input, device)) - byName[name].add(cast input); - } - } - #else 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 802f557f3..4c3e492e3 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 cb9705c55..1e9a1c8cd 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 5fbc73298..2fee324fd 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 000000000..413910543 --- /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 52a883b77..9ed103e54 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 a4c45ee1a..58494cc47 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,12 +129,113 @@ class MainMenuState extends MusicBeatState // NG.core.calls.event.logEvent('swag').send(); - changeItem(); - super.create(); } - - var selectedSomethin:Bool = false; + + override function finishTransIn() + { + 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) + { + onPromptClose = function () + { + checkLoginStatus(); + onClose(); + } + } + + 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) + { + FlxTween.tween(item, {alpha: 0}, duration, { ease: FlxEase.quadOut }); + } + else + { + item.visible = false; + } + }); + + new FlxTimer().start(duration, function(_) FlxG.switchState(state)); + } override function update(elapsed:Float) { @@ -117,124 +244,52 @@ class MainMenuState extends MusicBeatState FlxG.sound.music.volume += 0.5 * FlxG.elapsed; } - if (!selectedSomethin) - { - if (controls.UP_P) - { - FlxG.sound.play(Paths.sound('scrollMenu')); - changeItem(-1); - } - - if (controls.DOWN_P) - { - FlxG.sound.play(Paths.sound('scrollMenu')); - changeItem(1); - } - - if (controls.BACK) - { - FlxG.switchState(new TitleState()); - } - - if (controls.ACCEPT) - { - 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()); - } - }); - } - }); - } - } - } + if (menuItems.enabled && controls.BACK) + FlxG.switchState(new TitleState()); super.update(elapsed); - - menuItems.forEach(function(spr:FlxSprite) - { - spr.screenCenter(X); - }); - } - - function changeItem(huh:Int = 0) - { - curSelected += huh; - - if (curSelected >= menuItems.length) - curSelected = 0; - if (curSelected < 0) - curSelected = menuItems.length - 1; - - menuItems.forEach(function(spr:FlxSprite) - { - spr.animation.play('idle'); - - if (spr.ID == curSelected) - { - camFollow.setPosition(spr.getGraphicMidpoint().x, spr.getGraphicMidpoint().y); - spr.animation.play('selected'); - } - - 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; - } - } - }); } } + +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 f8d8948e5..f82323e4b 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 0c5b284cc..4ec844323 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 f0215ee10..a46955902 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 013a8c2de..2d6afdda2 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 b275b9b51..a0ed2429a 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); } + + function addGamepad(gamepad:FlxGamepad) + { + var useDefault = true; + var controlData = FlxG.save.data.controls; + if (controlData != 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) + { + useDefault = false; + trace("loaded pad data: " + haxe.Json.stringify(padData)); + controls.addGamepadWithSaveData(gamepad.id, padData); + } + } + + 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) + { + trace("saving pad data: " + haxe.Json.stringify(padData)); + playerData.pad = padData; + } + } + + FlxG.save.flush(); + } + + static public function init():Void + { + if (player1 == null) + { + player1 = new PlayerSettings(0); + ++numPlayers; + } + + FlxG.gamepads.deviceConnected.add(onGamepadAdded); + var numGamepads = FlxG.gamepads.numActiveGamepads; + for (i in 0...numGamepads) + { + var gamepad = FlxG.gamepads.getByID(i); + if (gamepad != null) + onGamepadAdded(gamepad); + } + + // player1.controls.addDefaultGamepad(0); + // } + + // if (numGamepads > 1) + // { + // if (player2 == null) + // { + // player2 = new PlayerSettings(1, None); + // ++numPlayers; + // } + + // var gamepad = FlxG.gamepads.getByID(1); + // if (gamepad == null) + // throw 'Unexpected null gamepad. id:0'; + + // player2.controls.addDefaultGamepad(1); + // } + + // DeviceManager.init(); + } + + static function onGamepadAdded(gamepad:FlxGamepad) + { + player1.addGamepad(gamepad); + } + + + /* 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; - } - - if (player1.avatar == null) - settings = player1; - else - { - 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}'; - } - ++numAvatars; - settings.avatar = avatar; - avatar.settings = settings; - - splitCameras(); - - onAvatarAdd.dispatch(settings); - - return settings; - } - - static public function removeAvatar(avatar:Player):Void - { - var settings:PlayerSettings; - - 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"; - - 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 init():Void + 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; - player2.controls.addDefaultGamepad(1); + splitCameras(); + + onAvatarAdd.dispatch(settings); + + return settings; + } + + static public function removeAvatar(avatar:Player):Void + { + var settings:PlayerSettings; + + 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"; + + 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)); } - // DeviceManager.init(); + --numAvatars; + + splitCameras(); + + onAvatarRemove.dispatch(avatar.settings); } + */ + static public function reset() { player1 = null; diff --git a/source/StoryMenuState.hx b/source/StoryMenuState.hx index 1c2dc8783..3c9d629ec 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 ca5340b93..98c681e7d 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) + NGio.checkVersion(function(version) { - FlxG.switchState(new OutdatedSubState()); - trace('OLD VERSION!'); - trace('old ver'); - trace(version.trim()); - trace('cur ver'); - trace(NGio.GAME_VER_NUMS.trim()); - } - else - { - 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 000000000..68232da5c --- /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 000000000..e9d06e711 --- /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 000000000..cd1e8b9f8 --- /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 000000000..6d20d7d82 --- /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 000000000..4bf75997f --- /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 000000000..0e4fae95c --- /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 000000000..0ef80007d --- /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 000000000..d1c617b17 --- /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