Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9b35b9a96 | ||
|
|
93cac00472 | ||
|
|
ab94fb633c | ||
|
|
bd79c8bf7d | ||
|
|
3ee0894f89 | ||
|
|
735b44c1cc | ||
|
|
d9580c580d | ||
|
|
0ac4bb1ba3 | ||
|
|
7bd9af1f04 | ||
|
|
81b21674e9 | ||
|
|
54ad34e5d0 | ||
|
|
f6fb11de4c | ||
|
|
83aa45a318 | ||
|
|
65186a336d | ||
|
|
3a41b696d3 | ||
|
|
9c08a6ce68 | ||
|
|
62650836b8 | ||
|
|
e4fdf1019a | ||
|
|
9dfda77d1e | ||
|
|
fe6c8cabd6 | ||
|
|
8b8d249813 | ||
|
|
c6ee23fec2 | ||
|
|
345d96a7bc | ||
|
|
7f7fc3fa90 | ||
|
|
7c17e72e63 | ||
|
|
1d2f971a26 | ||
|
|
7d97bb525a | ||
|
|
da74f782d3 | ||
|
|
107ef7c83f | ||
|
|
0412d865f1 | ||
|
|
50e114fc12 | ||
|
|
ce6577a9ed | ||
|
|
7fbe7b55c1 | ||
|
|
fb649c5d48 | ||
|
|
0ae8d62520 | ||
|
|
5b34446469 | ||
|
|
8d1bfa105c | ||
|
|
a2e9277851 | ||
|
|
2425054940 | ||
|
|
2bbfbc8968 | ||
|
|
5922b927b7 | ||
|
|
ebc5133062 | ||
|
|
7e2094070c | ||
|
|
20187540b3 | ||
|
|
c94d6bbb66 | ||
|
|
3d8b9a18b3 | ||
|
|
f428f5860d | ||
|
|
2645d56514 | ||
|
|
3f5ef4abe0 | ||
|
|
75d8c5cc7a | ||
|
|
9c7dded259 | ||
|
|
6a564905c0 | ||
|
|
8a3d36d3fc | ||
|
|
de9e30534e | ||
|
|
d42be225ca | ||
|
|
f7af64111c | ||
|
|
069bebb127 | ||
|
|
da8609f1d9 | ||
|
|
a11ae53c51 | ||
|
|
1d22e073a2 | ||
|
|
9da770f6cd | ||
|
|
d414a4974a | ||
|
|
6f193c4bd8 | ||
|
|
520f7fbe68 | ||
|
|
aaa722abcf | ||
|
|
f878624f44 | ||
|
|
355ea561fa | ||
|
|
7f6c0d56cd | ||
|
|
a21eace70c | ||
|
|
47813eac31 | ||
|
|
943afe2ffc | ||
|
|
89127b2b00 | ||
|
|
aab1330531 | ||
|
|
8bab6196e5 | ||
|
|
4e183a4dc9 | ||
|
|
ec2cc44cc3 | ||
|
|
663987fbcd | ||
|
|
172041d4f7 | ||
|
|
422da38662 | ||
|
|
9b81ebc4f2 | ||
|
|
0b9ad0905e | ||
|
|
1e1b5643a8 | ||
|
|
45ba0cb7a6 | ||
|
|
b1406e07f5 | ||
|
|
4b655879f7 | ||
|
|
fabfaa3a9c | ||
|
|
6d5249df79 | ||
|
|
23b163cb69 | ||
|
|
1b84c21c36 | ||
|
|
a87eb25765 | ||
|
|
df67b642a8 | ||
|
|
6eefe9e31e | ||
|
|
fea34c4a7b | ||
|
|
10e8cbbac3 | ||
|
|
37812ae2f8 | ||
|
|
acb1930d7e | ||
|
|
b98095f8aa | ||
|
|
724992d3b3 | ||
|
|
89e07dac76 | ||
|
|
2fb973049a | ||
|
|
dc20586e95 | ||
|
|
ccb4c03b85 | ||
|
|
f2732cb253 | ||
|
|
bf2b1af185 | ||
|
|
a2b347e31e | ||
|
|
4317f129e7 | ||
|
|
51eec4522e | ||
|
|
21c16c3a50 | ||
|
|
b7a5761730 | ||
|
|
ff09cb88cd | ||
|
|
d1a96f791f | ||
|
|
84bafa43b7 | ||
|
|
223e837fe8 | ||
|
|
50c601a7f0 | ||
|
|
f870ed2858 | ||
|
|
f1d6906616 | ||
|
|
aec3c646d5 | ||
|
|
e7f7f4794f | ||
|
|
517eb4eb48 | ||
|
|
8cba81d207 | ||
|
|
5831852edf | ||
|
|
114bb37c7f | ||
|
|
1db75cc55e | ||
|
|
248516244c | ||
|
|
b108c0fd95 | ||
|
|
2a804f29fd | ||
|
|
b03828adf3 | ||
|
|
657a25996b | ||
|
|
842ec2c1cd | ||
|
|
4268462d90 | ||
|
|
031007992a | ||
|
|
cea7db410c | ||
|
|
0e181877cf | ||
|
|
ef1216de46 | ||
|
|
b4b2e8da9d | ||
|
|
d95f239dfb | ||
|
|
495690f41c | ||
|
|
b166cd9a7e | ||
|
|
7f82b48e3f | ||
|
|
1fc611d130 | ||
|
|
57a276d17c | ||
|
|
9b1b22c9d5 | ||
|
|
f6de4c54d3 | ||
|
|
e3aceacb59 | ||
|
|
546375087c | ||
|
|
c093e0ca1c | ||
|
|
7c6efba577 | ||
|
|
6f5e3a50d2 | ||
|
|
2d0d5f8a2e | ||
|
|
826249f78c | ||
|
|
0af4c82723 | ||
|
|
c16e45049b | ||
|
|
ae00e14a5b | ||
|
|
42a9c43127 | ||
|
|
17ece1423a | ||
|
|
6540faefdd | ||
|
|
eb24d4d19e | ||
|
|
01a2458c12 | ||
|
|
38e7e230d8 | ||
|
|
84f119036b | ||
|
|
74a783c3db | ||
|
|
09d5926455 | ||
|
|
5f8b7787cb | ||
|
|
a7156978a1 | ||
|
|
65349f4714 | ||
|
|
93ad7e8932 | ||
|
|
df388dfaa6 | ||
|
|
c142add8a1 | ||
|
|
a75c41b813 | ||
|
|
7fd74edf2b | ||
|
|
44370c2653 | ||
|
|
1fbb7604dd | ||
|
|
0e52744718 | ||
|
|
feb55b2625 | ||
|
|
4341a53486 | ||
|
|
0566cd3e58 | ||
|
|
a2f7fa9ef4 | ||
|
|
3a30b681b6 | ||
|
|
0bd4ce4c97 | ||
|
|
df0d977e6c | ||
|
|
059b412e85 | ||
|
|
27a072f4e1 | ||
|
|
1df0cb95f6 | ||
|
|
ce83e962bf | ||
|
|
fb293ebcd2 | ||
|
|
bb7b00cf77 | ||
|
|
e16391a05c | ||
|
|
5aba4b54a5 | ||
|
|
cb611f77a0 | ||
|
|
b5345e1da2 | ||
|
|
8d6590e765 | ||
|
|
e39b32bab0 | ||
|
|
d5754c5da2 | ||
|
|
fbe83be936 | ||
|
|
8d14df77ff | ||
|
|
55358463f8 | ||
|
|
4ea99656f2 | ||
|
|
34ce13c5df | ||
|
|
1d8679941e | ||
|
|
086f28fef1 | ||
|
|
fb4967c94b | ||
|
|
293b70bbed | ||
|
|
64f4620b5a | ||
|
|
9c1e22309b | ||
|
|
4ecef6db74 | ||
|
|
b128741075 | ||
|
|
86e69cfbd1 | ||
|
|
aa8690fce3 | ||
|
|
e281bdddca | ||
|
|
19fdda7cd9 | ||
|
|
d72b989bf5 | ||
|
|
cd99905bdf | ||
|
|
3f82f3792b | ||
|
|
1d8ed5198c | ||
|
|
8cd3785e3c | ||
|
|
7e01e7332b | ||
|
|
c3a837b054 | ||
|
|
a92567886c | ||
|
|
4cbfaefc1a | ||
|
|
705e6b3308 | ||
|
|
56878befef | ||
|
|
44cdbb08c4 | ||
|
|
d6fe54ee79 | ||
|
|
32c775ef3f | ||
|
|
1605514424 | ||
|
|
743e8b13c2 | ||
|
|
cf89d672e7 | ||
|
|
661ddb15f7 | ||
|
|
72fa82716d | ||
|
|
a069ca2dc6 | ||
|
|
90a2d7a1ad | ||
|
|
3170346b83 | ||
|
|
e6d737125e | ||
|
|
3d88fbb11f | ||
|
|
0d6bc2bedd | ||
|
|
da5f691865 | ||
|
|
f63cbf073a | ||
|
|
30721c3a30 | ||
|
|
2b1f346097 | ||
|
|
12117557c7 | ||
|
|
6b0e607af0 | ||
|
|
2263b36cd7 | ||
|
|
2dcc528847 | ||
|
|
5cb33e895c | ||
|
|
1b91f3b57c | ||
|
|
e5e3270fef | ||
|
|
e003a38cab | ||
|
|
890d1dafcb | ||
|
|
9d2cfa9686 | ||
|
|
e6fd5d9b12 | ||
|
|
967397311b | ||
|
|
542f59ef72 | ||
|
|
065ee74aea | ||
|
|
312b5c8812 | ||
|
|
e9aa6872a3 | ||
|
|
4f9b63f22c | ||
|
|
962519e7f0 | ||
|
|
34bae4a431 | ||
|
|
79478080f6 | ||
|
|
65a3ccc8fe | ||
|
|
4ac851c387 | ||
|
|
70d5730b70 | ||
|
|
c527fed70c | ||
|
|
054a5cd00b | ||
|
|
19c5de8fe2 | ||
|
|
dbc26fd621 | ||
|
|
eb1986b246 | ||
|
|
d661d93184 | ||
|
|
4a07d0a347 | ||
|
|
832f01345f | ||
|
|
e8d41c938c | ||
|
|
a94bd7f36c | ||
|
|
c185f10e65 | ||
|
|
52f3c63071 | ||
|
|
e71062b914 | ||
|
|
3aa6a36b5e | ||
|
|
a89fa9c121 | ||
|
|
cb2015f51a | ||
|
|
139ce3847c | ||
|
|
87b40c1203 | ||
|
|
b9852c316b | ||
|
|
444697620c | ||
|
|
6abeefb15d | ||
|
|
530bd1f11d | ||
|
|
76c0dea92b | ||
|
|
2d7a2202f5 | ||
|
|
1f18a3d1f4 | ||
|
|
0d57f61ffa | ||
|
|
9fa70775b9 | ||
|
|
463afb43c0 | ||
|
|
2833827fbf | ||
|
|
01cd72e805 | ||
|
|
72e0ff6c9b | ||
|
|
2586154376 | ||
|
|
82367dffc2 | ||
|
|
7c675ecf17 | ||
|
|
b4e52df156 | ||
|
|
cf70bf2c6c | ||
|
|
fb6d1b36d3 | ||
|
|
0c0c63bc03 | ||
|
|
01620fda9b | ||
|
|
3bcd6813c1 | ||
|
|
d44966efd2 | ||
|
|
79c3e2de11 | ||
|
|
8372d47d11 | ||
|
|
d89ebdf58f | ||
|
|
1e8c472084 | ||
|
|
17cef6f897 | ||
|
|
2cc43ee1f6 | ||
|
|
2277acccfa | ||
|
|
351719ab13 | ||
|
|
4ecfe9863e | ||
|
|
48e140e5c0 | ||
|
|
e6bb965d6b | ||
|
|
b00376c88c | ||
|
|
ed86b326ab | ||
|
|
596c5813d7 | ||
|
|
ae68925911 | ||
|
|
d7c3799562 | ||
|
|
47ab14a855 | ||
|
|
940e165612 | ||
|
|
5181023887 | ||
|
|
32e774b005 | ||
|
|
c353cc54a1 | ||
|
|
48d0798e5f | ||
|
|
0463b4e26b | ||
|
|
0c1842038f | ||
|
|
aa0b8cbbbe | ||
|
|
52cd4f6c36 | ||
|
|
96a1796905 | ||
|
|
6be7e89222 | ||
|
|
abea46939f | ||
|
|
3a5f1e14cf | ||
|
|
849e413bb7 | ||
|
|
b4a7bd7462 | ||
|
|
beb64528fb | ||
|
|
eac940bccb | ||
|
|
aa1c71cafd | ||
|
|
f4bedc359a | ||
|
|
01428dd5cf | ||
|
|
541bb78da9 | ||
|
|
c6e19c8512 | ||
|
|
e30a4ebb7b | ||
|
|
949f409cff | ||
|
|
4c5aa9743e | ||
|
|
3fa2b3574b | ||
|
|
1e41ac3052 | ||
|
|
936812ae1e | ||
|
|
ad3bf2d2d2 | ||
|
|
1b1c2a124b | ||
|
|
2aff2fa201 | ||
|
|
7ec602f6ec | ||
|
|
4fbeffb57c | ||
|
|
f029a27a3f | ||
|
|
8a99fa4184 | ||
|
|
f21b015292 | ||
|
|
bc8e35409b | ||
|
|
006f1dce0a | ||
|
|
78186188d7 | ||
|
|
762486639e | ||
|
|
b5bb9721da | ||
|
|
4029e92181 | ||
|
|
9c7f87c225 | ||
|
|
43420092f3 | ||
|
|
d8cd580535 | ||
|
|
13c08b285d | ||
|
|
6238b72d8c | ||
|
|
bf92bf3f60 | ||
|
|
6467cd75a8 | ||
|
|
32dc25bbf6 | ||
|
|
11cb409bd2 | ||
|
|
f245365ed3 | ||
|
|
e0d0230b88 | ||
|
|
f8634e2b81 | ||
|
|
4f68f63e2d | ||
|
|
e9e0567f8b | ||
|
|
8db768fd2a | ||
|
|
cf814ee4e2 | ||
|
|
43b7be4e71 | ||
|
|
7d555b4155 | ||
|
|
88ad99c3ef | ||
|
|
d92b7ff3f5 | ||
|
|
66c6a78319 | ||
|
|
e6a2a09801 | ||
|
|
8944e07fe1 | ||
|
|
188260ea81 | ||
|
|
22c6ce42ba | ||
|
|
d8445fe54a | ||
|
|
c3592ee6f5 | ||
|
|
e402328969 | ||
|
|
150a1d5b0a | ||
|
|
d468477663 | ||
|
|
ecaf51ef41 | ||
|
|
05627a50d5 | ||
|
|
efe333da92 | ||
|
|
e946b54249 | ||
|
|
1d4a94041b | ||
|
|
11da19ce1e | ||
|
|
126ed3a67a | ||
|
|
468197e74e | ||
|
|
7f17ad8bb8 | ||
|
|
e9ef340c63 | ||
|
|
61bda6c2c2 | ||
|
|
07f649ca37 | ||
|
|
11ecd07606 | ||
|
|
822c5ba405 | ||
|
|
d61f89b0e4 | ||
|
|
9dfae07225 | ||
|
|
01fad012f2 | ||
|
|
9843f9549c | ||
|
|
044106e8a9 | ||
|
|
d338be9270 | ||
|
|
e4d3e18e4f | ||
|
|
5f5fff870c | ||
|
|
52acd86cdf | ||
|
|
dd104365e7 | ||
|
|
b0581f728a | ||
|
|
5e50432b30 | ||
|
|
314d8af9c8 | ||
|
|
f91261f748 | ||
|
|
0567849665 | ||
|
|
a701e6a4a9 | ||
|
|
abfc4ca79c | ||
|
|
797a8bfcba | ||
|
|
d70838cca9 | ||
|
|
0d845154ef | ||
|
|
9e04db2c5c | ||
|
|
c0d3b9daa2 | ||
|
|
0f336ae0e6 | ||
|
|
8c8eedf17a | ||
|
|
793b533b88 | ||
|
|
852a74c33d | ||
|
|
bfdafd7f37 | ||
|
|
37d0d1b14e | ||
|
|
2649e247fa | ||
|
|
fafe157a70 | ||
|
|
39a4292dc5 | ||
|
|
aad0a0afb6 | ||
|
|
f8516cfb4d | ||
|
|
29f7aca135 | ||
|
|
35d4d24731 | ||
|
|
c0dde5c936 |
@@ -14,19 +14,20 @@ body:
|
||||
- label: I have read the [Contributing Guide](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md)
|
||||
- label: I have checked the Issues/Discussions pages to see if my issue has already been reported
|
||||
- label: I have properly titled my issue
|
||||
- label: I did not have any mods installed when I encountered this issue
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Which platform are you playing on?
|
||||
options:
|
||||
- Android
|
||||
- iOS/iPadOS
|
||||
- Newgrounds (Web/HTML5)
|
||||
- Itch.io (Web/HTML5)
|
||||
- Itch.io (Downloadable Build) - Windows
|
||||
- Itch.io (Downloadable Build) - MacOS
|
||||
- Itch.io (Downloadable Build) - Linux
|
||||
- Google Playstore - Android
|
||||
- App Store - iOS
|
||||
- Windows (Downloadable Build)
|
||||
- MacOS (Downloadable Build)
|
||||
- Linux (Downloadable Build)
|
||||
- Compiled from GitHub Source Code
|
||||
validations:
|
||||
required: true
|
||||
@@ -43,11 +44,23 @@ body:
|
||||
- Safari
|
||||
- Other (Specify in Description field)
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Mobile Device Model
|
||||
description: (Mobile users only) What mobile device are you playing on?
|
||||
placeholder: ex. iPhone 16, Galaxy S25, iPad 11th Gen
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Mobile OS Version
|
||||
description: (Mobile users only) What version is your Operating System?
|
||||
placeholder: ex. iOS 18.5, Android 15, iPadOS 18.5
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.6.4
|
||||
placeholder: ex. 0.7.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Issue Checklist
|
||||
@@ -21,35 +21,25 @@ body:
|
||||
label: Platform
|
||||
description: Which platform are you playing on?
|
||||
options:
|
||||
- Android
|
||||
- iOS/iPadOS
|
||||
- Newgrounds (Web/HTML5)
|
||||
- Itch.io (Web/HTML5)
|
||||
- Itch.io (Downloadable Build) - Windows
|
||||
- Itch.io (Downloadable Build) - MacOS
|
||||
- Itch.io (Downloadable Build) - Linux
|
||||
- Windows (Downloadable Build)
|
||||
- MacOS (Downloadable Build)
|
||||
- Linux (Downloadable Build)
|
||||
- Compiled from GitHub Source Code
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Browser
|
||||
description: (Web/HTML5 users only) Which browser are you playing on?
|
||||
options:
|
||||
- Google Chrome
|
||||
- Microsoft Edge
|
||||
- Firefox
|
||||
- Opera
|
||||
- Safari
|
||||
- Other (Specify in Description field)
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.6.4
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.7.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "## Describe the charting issue(s)."
|
||||
|
||||
@@ -6,7 +6,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Issue Checklist
|
||||
@@ -22,6 +22,8 @@ body:
|
||||
label: Platform
|
||||
description: Which platform are you compiling for?
|
||||
options:
|
||||
- Android
|
||||
- iOS/iPadOS
|
||||
- Web/HTML5
|
||||
- Desktop (Windows)
|
||||
- Desktop (Mac)
|
||||
@@ -33,11 +35,11 @@ body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you compiling? The game version is in the bottom left corner of the main menu or in the project.hxp file.
|
||||
placeholder: ex. 0.6.4
|
||||
description: Which version are you compiling? The game version is in the bottom left corner of the main menu or in the project.hxp file.
|
||||
placeholder: ex. 0.7.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "## Describe your compiling issue."
|
||||
|
||||
@@ -6,7 +6,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Issue Checklist
|
||||
@@ -21,11 +21,13 @@ body:
|
||||
label: Platform
|
||||
description: Which platform are you playing on?
|
||||
options:
|
||||
- Android
|
||||
- iOS/iPadOS
|
||||
- Newgrounds (Web/HTML5)
|
||||
- Itch.io (Web/HTML5)
|
||||
- Itch.io (Downloadable Build) - Windows
|
||||
- Itch.io (Downloadable Build) - MacOS
|
||||
- Itch.io (Downloadable Build) - Linux
|
||||
- Windows (Downloadable Build)
|
||||
- MacOS (Downloadable Build)
|
||||
- Linux (Downloadable Build)
|
||||
- Compiled from GitHub Source Code
|
||||
validations:
|
||||
required: true
|
||||
@@ -42,11 +44,23 @@ body:
|
||||
- Safari
|
||||
- Other (Specify in Description field)
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Mobile Device Model
|
||||
description: (Mobile users only) What mobile device are you playing on?
|
||||
placeholder: ex. iPhone 16, Galaxy S25, iPad 11th Gen
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Mobile OS Version
|
||||
description: (Mobile users only) What version is your Operating System?
|
||||
placeholder: ex. iOS 18.5, Android 15, iPadOS 18.5
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.6.4
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.7.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -61,7 +75,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: Include screenshots or videos of the crash happening. Provide as much detail as you can.
|
||||
description: Include screenshots or videos of the crash happening. Provide as much detail as you can.
|
||||
placeholder: Describe the crash here...
|
||||
validations:
|
||||
required: true
|
||||
@@ -77,7 +91,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Crash logs
|
||||
description: These can be found in the logs folder where Funkin.exe is.
|
||||
description: These can be found in the logs folder where Funkin.exe is, or in your mobile device's file explorer.
|
||||
placeholder: Upload your logs here...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -6,7 +6,6 @@ dump/
|
||||
temp/
|
||||
docs/temp/
|
||||
export/
|
||||
project/
|
||||
astc-textures/
|
||||
RECOVER_*.fla
|
||||
shitAudio/
|
||||
|
||||
+437
-96
@@ -4,119 +4,457 @@ All notable changes will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.7.0] - 2025-06-??
|
||||
## [0.7.4] - 2025-09-01
|
||||
|
||||
### Added
|
||||
|
||||
- Friday Night Funkin' now has an OFFICIAL mobile version, available on the [Google Play Store]() and [Apple App Store]()!
|
||||
- This version is 100% feature complete with the desktop version of the game.
|
||||
- [MOBILE] New touch input compatiblity for all menus.
|
||||
- [MOBILE] Added banner and interstitial advertisements to the game. You can use an in-app purchase to upgrade to the full version to permanently disable advertisements.
|
||||
- [MOBILE] Graphics are compressed using the ASTC algorithm, decreasing memory usage in exchange for a slightly bigger file size.
|
||||
- [MOBILE] Added haptic feedback to several areas in the game.
|
||||
- A whopping ZERO new songs! Woah!
|
||||
- Added null-safety to a bunch of classes within the source code.
|
||||
- Added a blank `Object` class for scripts to extend, and made `FlxObject` and `FlxBasic` scriptable. ([1c36548](https://github.com/FunkinCrew/Funkin/commit/1c36548861a54e2a8ae49f715056139ce31bbd83)) - by @cyn0x8 in [#3119](https://github.com/FunkinCrew/Funkin/pull/3119)
|
||||
- The Chart Editor now highlights and deletes stacked notes and using a customizable threshold. ([8cae34e](https://github.com/FunkinCrew/Funkin/commit/8cae34eed711bff70e5348ffc6178a0fd69b5846)) - by @NotHyper-474 in [#3574](https://github.com/FunkinCrew/Funkin/pull/3574)
|
||||
- Added the ability to press the Chart Editor keybind in Freeplay with a song capsule selected. ([066c2d3](https://github.com/FunkinCrew/Funkin/commit/066c2d3db04162d3df6af883066fc91e05988d58)) - by @Lasercar in [#4114](https://github.com/FunkinCrew/Funkin/pull/4114)
|
||||
- Added more tween types to certain Chart Editor events. ([5177e12](https://github.com/FunkinCrew/Funkin/commit/5177e1275eb2fb2b016224c139e84debb421b895)) - by @Lasercar in [#4249](https://github.com/FunkinCrew/Funkin/pull/4249)
|
||||
- Added default gamepad controls for two recently added Freeplay controls. ([8dad4f5](https://github.com/FunkinCrew/Funkin/commit/8dad4f55b660a01d4e1a3a903fde684e9876f555)) - by @MrMadera in [#4559](https://github.com/FunkinCrew/Funkin/pull/4559)
|
||||
- Added Perfect (Gold) to the list of available ranks in Results Debug menu. ([0261355](https://github.com/FunkinCrew/Funkin/commit/02613557787581b9c5b7532ea6e69001f1989102)) - by @AbnormalPoof in [#4642](https://github.com/FunkinCrew/Funkin/pull/4642)
|
||||
- Added a few sandboxed classes to give mods limited access to the Discord and Newgrounds APIs. ([6bd6066](https://github.com/FunkinCrew/Funkin/commit/6bd6066d169fd1611a706e984bb8d3ad7109e31f)) - by @KoloInDaCrib in [#5040](https://github.com/FunkinCrew/Funkin/pull/5040)
|
||||
- Pressing Ctrl + N now creates a new stage in the Stage Editor. ([bc54634](https://github.com/FunkinCrew/Funkin/commit/bc546345377f9e9e7b69306b72d494cad02ebaba)) - by @Lasercar in [#5175](https://github.com/FunkinCrew/Funkin/pull/5175)
|
||||
- Added script support for Freeplay Backing Cards. ([92e7588](https://github.com/FunkinCrew/Funkin/commit/92e7588370fcbdc0e959e01e7e52eed5d62323f2)) - by @KoloInDaCrib in [#5233](https://github.com/FunkinCrew/Funkin/pull/5233)
|
||||
- Added a variation indicator next to the Chart Editor playbar difficulty. ([dc7fa97](https://github.com/FunkinCrew/Funkin/commit/dc7fa97d3ca3c00edbab0adc171135af0008735d)) - by @KoloInDaCrib in [#5236](https://github.com/FunkinCrew/Funkin/pull/5236)
|
||||
- Added "Flip character horizontally" to the list of shortcuts in the Animation Editor. ([f73e925](https://github.com/FunkinCrew/funkin.assets/commit/f73e925cbc1e99223095e444f9217839c7d8148e)) - by @AbnormalPoof in [funkin.assets#60](https://github.com/FunkinCrew/funkin.assets/pull/60)
|
||||
- [MOBILE] Added haptics to the can explosion in 2hot.
|
||||
- Added the `scorable` property to notekinds, which excludes them from tallies.
|
||||
- Added saving and loading save data using Newgrounds! Check out the new "Save Data Options" button in the Options menu after logging in. ([832f013](https://github.com/FunkinCrew/Funkin/commit/832f01345f441631a5372dbc3382dc8a13c150d6)) - by @TechnikTil in [#4900](https://github.com/FunkinCrew/Funkin/pull/4900)
|
||||
- Added variation-specific song scripts, improving compatibility between mods. ([c94d6bb](https://github.com/FunkinCrew/Funkin/commit/c94d6bbb6640f7a9fbce8a202fbecf19d36da2f7)) - by @AbnormalPoof in [#5165](https://github.com/FunkinCrew/Funkin/pull/5165)
|
||||
- Added scoped modules, which only run in a specified state. ([842ec2c](https://github.com/FunkinCrew/Funkin/commit/842ec2c1cd0243741170c431e4095bad2a4842e0)) - by @AbnormalPoof in [#5138](https://github.com/FunkinCrew/Funkin/pull/5138)
|
||||
- Added ModStore, a helper class that stores data across hot reloads until the game is closed. ([3170346](https://github.com/FunkinCrew/Funkin/commit/3170346b83e5f73a88b3e7135e75ec1be2384929)) - by @cyn0x8 in [#4230](https://github.com/FunkinCrew/Funkin/pull/4230)
|
||||
- Added hotkeys to toggle individual audio tracks in the Chart Editor. ([1e1b564](https://github.com/FunkinCrew/Funkin/commit/1e1b5643a8536a3061a237e058c45223b66039da)) - by @Lasercar in [#4247](https://github.com/FunkinCrew/Funkin/pull/4247)
|
||||
- Added visual indicators and tooltips for notekinds. ([32c775e](https://github.com/FunkinCrew/Funkin/commit/32c775ef3f131accd69a04591608420919a607a1)) - by @KoloInDaCrib in [#5284](https://github.com/FunkinCrew/Funkin/pull/5284)
|
||||
- Added a Charter field to the New Chart dialog in the Chart Editor. ([23b163c](https://github.com/FunkinCrew/Funkin/commit/23b163cb69ffbb8d2ae1c12d965a6ed3a26020e0)) - by @NotHyper-474 in [#5657](https://github.com/FunkinCrew/Funkin/pull/5657)
|
||||
- Added a Song ID field to the Chart Editor metadata toolbox. ([55f9d24](https://github.com/FunkinCrew/funkin.assets/commit/55f9d24e9dc475b981e4cc087101425e85d3c1e2)) - by @cyn0x8 in [funkin.assets#229](https://github.com/FunkinCrew/funkin.assets/pull/229)
|
||||
- Added a confirmation prompt when exiting the Chart Editor with an unsaved chart. ([e6d7371](https://github.com/FunkinCrew/Funkin/commit/e6d737125e0031d1efabc73266dfbde6d6c758de)) - by @anysad in [#5091](https://github.com/FunkinCrew/Funkin/pull/5091)
|
||||
- [ANDROID] Added support for monochrome icons on Android 12 and above. ([5de4e76](https://github.com/FunkinCrew/Funkin/commit/5de4e766b7317c50d3ae40b9525b7c391984d52c)) - by @ThatOneCalculator in [#5507](https://github.com/FunkinCrew/Funkin/pull/5507)
|
||||
|
||||
### Changed
|
||||
|
||||
- Modified several parts of the game to look better on phone screens with wider aspect ratios, up to 20:9.
|
||||
- Fixed a ton of performance issues to help the game run better on mobile devices.
|
||||
- Completely reformatted and optimized every single chart file in the game, along with fixing several chart issues.
|
||||
- Completely reformatted every script file within the game's assets for better readability.
|
||||
- Notes now scroll more smoothly by rendering based on delta timing. ([6ad9ffc](https://github.com/FunkinCrew/Funkin/commit/6ad9ffc7f9d66bbaf6ba343663de4c7268f4be3b)) - by @KutikiPlayz in [#3544](https://github.com/FunkinCrew/Funkin/pull/3544)
|
||||
- Favorite songs in Freeplay are now sorted by Week order instead of alphabetically. ([da0964a](https://github.com/FunkinCrew/Funkin/commit/da0964a7b7bbe4ece1bdbd19233eb6dba0de3ac5)) - by @Hundrec in [#3609](https://github.com/FunkinCrew/Funkin/pull/3609)
|
||||
- The Character Select screen now opens on the currently selected character. ([b233a53](https://github.com/FunkinCrew/Funkin/commit/b233a53710d8ef6b0151d83f5d78e1733fe356f5)) - by @Lasercar in [#4072](https://github.com/FunkinCrew/Funkin/pull/4072)
|
||||
- The undo/redo history is now cleared when loading another song in the Chart Editor. ([4c4045e](https://github.com/FunkinCrew/Funkin/commit/4c4045e274f6a7fa0bc2574468abb3b70ca68f00)) - by @Lasercar in [#4308](https://github.com/FunkinCrew/Funkin/pull/4308)
|
||||
- The Options Menu can now scroll to display more menu items. ([70f0a54](https://github.com/FunkinCrew/Funkin/commit/70f0a54191597bd72a6d30d4d12ef5ece6ba078c)) - by @AbnormalPoof in [#4706](https://github.com/FunkinCrew/Funkin/pull/4706)
|
||||
- The Chart Editor will now only fall back to the first available difficulty if the selected difficulty cannot be found. ([19d75ff](https://github.com/FunkinCrew/Funkin/commit/19d75ffe72f08a6adb5b4ca00cae2f913767047a)) - by @Lasercar in [#4949](https://github.com/FunkinCrew/Funkin/pull/4949)
|
||||
- Adjusted the size of the Beat/Step display in the Chart Editor. ([44b1a77](https://github.com/FunkinCrew/Funkin/commit/44b1a77b3b62146504166b392ea2dfbe6f65b302)) - by @NotHyper-474 in [#4994](https://github.com/FunkinCrew/Funkin/pull/4994)
|
||||
- The Freeplay character select hint now always displays if you have more than one character unlocked. ([7ccf75c](https://github.com/FunkinCrew/Funkin/commit/7ccf75cd869ba4b6f18a5adc01e65e52ae7bb809)) - by @Hundrec in [#5023](https://github.com/FunkinCrew/Funkin/pull/5023)
|
||||
- Raised the FPS cap preference from 300 to 500. ([71d7169](https://github.com/FunkinCrew/Funkin/commit/71d7169ba35c01471b4274671e238b0ac2cf04ed)) - by @Hundrec in [#5044](https://github.com/FunkinCrew/Funkin/pull/5044)
|
||||
- Blacklisted more classes for security reasons. ([cadfa3b](https://github.com/FunkinCrew/Funkin/commit/cadfa3b7ceae2ecabe2d544ddc4c9f453b0dfd56)) - by @NotHyper-474 in [#5185](https://github.com/FunkinCrew/Funkin/pull/5185)
|
||||
- Visualizers now zero out when the game audio is muted. ([3d205e9](https://github.com/FunkinCrew/Funkin/commit/3d205e935553096ab7bbd1879d8cb69bdb550ead)) - by @Lasercar in [#5266](https://github.com/FunkinCrew/Funkin/pull/5266)
|
||||
- Shifted Mommy Mearest's pixel icon to the left in Freeplay. ([d9e8085](https://github.com/FunkinCrew/funkin.assets/commit/d9e8085020cfc516b7c38ec7773081619ab840a4)) - by @KoloInDaCrib in [funkin.assets#197](https://github.com/FunkinCrew/funkin.assets/pull/197)
|
||||
- Reworked the Conductor to make note scrolling smoother.
|
||||
- Reworked Attract Mode.
|
||||
- The Title Screen fades out and plays a video after a certain amount of time.
|
||||
- The video alternates between the Mobile Trailer and the BOYFRIEND EVERYWHERE music video, replacing the previous toy commercial.
|
||||
- The “Input Offsets” menu is now named “Lag Adjustment”.
|
||||
- Freeplay difficulty dots are now displayed as rows of eight when modded difficulties are present.
|
||||
- Optimized the way the game searches for Freeplay pixel icons.
|
||||
- Results Screen and game over haptics are now softcoded.
|
||||
- [WINDOWS] The window title bar now matches the system theme, light or dark.
|
||||
- [LINUX] The game now displays with an icon.
|
||||
- [iOS] The edges of the app icon are now properly filled in.
|
||||
- Made several improvements and fixes to Polymod and HScript:
|
||||
- Significantly optimized mod loading, especially with many installed at once. (Thanks KoloInDaCrib!)
|
||||
- Fixed several cases where script variables would be lost. (Thanks NotHyper-474!)
|
||||
- Zip mod loading on Linux is now case-insensitive. (Thanks NotHyper-474!)
|
||||
- Added the ability to import scripted classes. (Thanks lemz1!)
|
||||
- NOTE: You currently can’t use them in a static context.
|
||||
- Added the ability to use the `??=` operator. (Thanks lemz1!)
|
||||
- Added the ability to use the `using` keyword. (Thanks KoloInDaCrib!)
|
||||
- `final` variables created inside of scripts are now treated as actual `final`s. (Thanks KoloInDaCrib!)
|
||||
- Added null-safe field assign. (ex. `someVar?.someVar2 = new Class();`) (Thanks NotHyper-474!)
|
||||
- Improved properties support, behaving more like regular Haxe. (Thanks NotHyper-474!)
|
||||
- Fixed a null object reference when evaluating static variables with no explicit values. (Thanks NotHyper-474!)
|
||||
- Fixed a null object reference when iterating over a null variable. (Thanks NotHyper-474!)
|
||||
- Scripts are now automatically cleared when Polymod is initialized. (Thanks KoloInDaKrib!)
|
||||
- Polished every chart in the game!
|
||||
- The lengths of hold notes have been adjusted to match the vocals as closely as possible, across all variations and difficulties.
|
||||
- Easy and Normal charts from Week 1 to Week 3 have been adjusted for more consistency between the player and opponent.
|
||||
- Many charting errors have been corrected.
|
||||
- Accept keybinds and gamepad south buttons now work on the Title Screen. ([e281bdd](https://github.com/FunkinCrew/Funkin/commit/e281bdddca5f31982bdc620e8ea92b080dcd416a)) - by @MrMadera in [#5650](https://github.com/FunkinCrew/Funkin/pull/5650)
|
||||
- [ANDROID] Pressing the Back button in the Title Screen now closes the game. ([e6fd5d9](https://github.com/FunkinCrew/Funkin/commit/e6fd5d9b129f74703d6bd96744f63cb201afc941)) - by @NotHyper-474 in [#5527](https://github.com/FunkinCrew/Funkin/pull/5527)
|
||||
- The HOME and END keys now jump to the top and bottom of the Story Mode menu, respectively. ([d1a96f7](https://github.com/FunkinCrew/Funkin/commit/d1a96f791f2ed35accc1c1e9ff548a75b2b9d7ef)) - by @JVNpixels in [#4591](https://github.com/FunkinCrew/Funkin/pull/4591)
|
||||
- Freeplay favorite songs are now separated by variation. ([b4b2e8d](https://github.com/FunkinCrew/Funkin/commit/b4b2e8da9dde41d730556a41e6bb18b0f3d8d7db)) - by @KoloInDaCrib in [#5154](https://github.com/FunkinCrew/Funkin/pull/5154)
|
||||
- Freeplay preview volumes now play at 70% volume, up from 40%. ([4ecef6d](https://github.com/FunkinCrew/Funkin/commit/4ecef6db744bab7e3fe5de0d680d813cc3bd1ea7)) - by @JackXson-Real in [#5645](https://github.com/FunkinCrew/Funkin/pull/5645)
|
||||
- Selecting a song in the Freeplay menu now plays a fade-out transition. ([705e6b3](https://github.com/FunkinCrew/Funkin/commit/705e6b330806c3157bca7301b1e43cd78acdb23a)) - by @VirtuGuy in [#5626](https://github.com/FunkinCrew/Funkin/pull/5626)
|
||||
- Implemented null safety to PlayState and LoadingState. ([34ce13c](https://github.com/FunkinCrew/Funkin/commit/34ce13c5dfd52760675a1d09efa489bcb108e607)) - by @NotHyper-474 in [#4789](https://github.com/FunkinCrew/Funkin/pull/4789)
|
||||
- Health icons now have an offset variable. ([8bab619](https://github.com/FunkinCrew/Funkin/commit/8bab6196e50420c6586441f2d321bd1c139567c3)) - by @PurSnake in [#5760](https://github.com/FunkinCrew/Funkin/pull/5760)
|
||||
- The "hey" notekind now plays the animation for BF (Christmas). ([91d5970](https://github.com/FunkinCrew/funkin.assets/commit/91d5970e0274aa0a31fc6f2b05c114730256408f)) - by @JackXson-Real in [funkin.assets#225](https://github.com/FunkinCrew/funkin.assets/pull/225)
|
||||
- Added an "offset" property to the Set Camera Bop event, enabling more flexible camera bops. ([3f5ef4a](https://github.com/FunkinCrew/Funkin/commit/3f5ef4abe09bbd222bb9e5e212d63973b1b87175)) - by @Burgerballs in [#3575](https://github.com/FunkinCrew/Funkin/pull/3575)
|
||||
- Added caching to multi-sparrow characters and fixed textures not being properly cached. ([feb55b2](https://github.com/FunkinCrew/Funkin/commit/feb55b26258e1dd0c4d209e2fa8af1dfb32ea684)) - by @PurSnake in [#5684](https://github.com/FunkinCrew/Funkin/pull/5684)
|
||||
- Optimized the way Freeplay backing card text is rendered. ([f6de4c5](https://github.com/FunkinCrew/Funkin/commit/f6de4c54d37d3febc01db20d84885ccd3e21e833)) - by @MaybeMaru in [#5730](https://github.com/FunkinCrew/Funkin/pull/5730)
|
||||
- Optimized the rendering of many visual elements in Freeplay. ([6f193c4](https://github.com/FunkinCrew/Funkin/commit/6f193c4bd88675f46d9455a5310129f1517c8e18)) - by @MaybeMaru in [#5773](https://github.com/FunkinCrew/Funkin/pull/5773)
|
||||
- Optimized the way notes are rendered, especially when many are visible at once. ([a2b347e](https://github.com/FunkinCrew/Funkin/commit/a2b347e31e7a539f12131980ffeaf980e3e677af)) - by @MaybeMaru in [#5752](https://github.com/FunkinCrew/Funkin/pull/5752)
|
||||
- Image assets for gradients are now smaller and stretched in-game for better optimization. ([fab646c](https://github.com/FunkinCrew/funkin.assets/commit/fab646ce4a3045dcd6aa2ab86c403a40ee89bfa3)) - by @MaybeMaru in [funkin.assets#227](https://github.com/FunkinCrew/funkin.assets/pull/227)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Songs no longer skip forward at the beginning with high visual offsets. ([02c64ae](https://github.com/FunkinCrew/Funkin/commit/02c64aea9c2e7fe06f9116312d5ef6c049dc8e9c)) - by @xenkap in [#3732](https://github.com/FunkinCrew/Funkin/pull/3732)
|
||||
- Fixed various issues and added missing functionalities to the Stage Editor. ([15083a4](https://github.com/FunkinCrew/Funkin/commit/15083a477626f67125bfd60a844b42b85a82d8c3)) - by @KoloInDaCrib in [#3974](https://github.com/FunkinCrew/Funkin/pull/3974)
|
||||
- Pressing the Chart Editor keybind during a song now opens to the variation and difficulty you were playing. ([aeb2dd5](https://github.com/FunkinCrew/Funkin/commit/aeb2dd53983b12fa0152861cc51a161115ea2729)) - by @Lasercar in [#4116](https://github.com/FunkinCrew/Funkin/pull/4116)
|
||||
- Dragging a hold note in the Chart Editor now drags its trail along with its head. ([d3d8aaa](https://github.com/FunkinCrew/Funkin/commit/d3d8aaae7bfbfa8975a9573807b9a3ca68a1ff55)) - by @KoloInDaCrib in [#4127](https://github.com/FunkinCrew/Funkin/pull/4127)
|
||||
- The help guide in the Stage Editor can no longer be opened multiple times. ([8c1fa3e](https://github.com/FunkinCrew/Funkin/commit/8c1fa3e630e595eac774e81f2c0f8f32ff97d927)) - by @Lasercar in [#4128](https://github.com/FunkinCrew/Funkin/pull/4128)
|
||||
- The Chart Editor now properly saves audio levels when exiting. ([6da2718](https://github.com/FunkinCrew/Funkin/commit/6da2718f116b45dd969ee74c808b86f63631984c)) - by @Lasercar in [#4149](https://github.com/FunkinCrew/Funkin/pull/4149)
|
||||
- Holding Ctrl and clicking on a hold note trail no longer crashes the Chart Editor. ([279bf2a](https://github.com/FunkinCrew/Funkin/commit/279bf2acd0e61b890a63b2dc4c82c6f6dc3c9ffd)) - by @Lasercar in [#4203](https://github.com/FunkinCrew/Funkin/pull/4203)
|
||||
- Removed spammy console traces from `DiscordClient`. ([e89f9f5](https://github.com/FunkinCrew/Funkin/commit/e89f9f50dc6085e3550736459ce9e4fd02c1fc5b)) - by @AbnormalPoof in [#4207](https://github.com/FunkinCrew/Funkin/pull/4207)
|
||||
- The Debug Menu can no longer be opened after selecting an item in the Main Menu. ([5695bc2](https://github.com/FunkinCrew/Funkin/commit/5695bc20e721f0afd5b97a36167f13e550c12b16)) - by @Lasercar in [#4211](https://github.com/FunkinCrew/Funkin/pull/4211)
|
||||
- Fixed the Chart Editor timer occasionally displaying incorrect millisecond values. ([26dc895](https://github.com/FunkinCrew/Funkin/commit/26dc895a27e0d7e49469251cd68b83be66384e15)) - by @Hundrec in [#4257](https://github.com/FunkinCrew/Funkin/pull/4257)
|
||||
- The Chart Editor "Load Metadata File" and "Load Chart File" buttons now function properly. ([5eecef1](https://github.com/FunkinCrew/Funkin/commit/5eecef17a143cbc8cf2307bc29a20c96b8eed6a8)) - by @Lasercar in [#4278](https://github.com/FunkinCrew/Funkin/pull/4278)
|
||||
- The "New Highscore" text no longer appears more than once in the Results screen. ([c0d8e1b](https://github.com/FunkinCrew/Funkin/commit/c0d8e1b70356ec3492e57602a7d30fc2c595544b)) - by @Lasercar in [#4319](https://github.com/FunkinCrew/Funkin/pull/4319)
|
||||
- The Story Mode Weekend 1 level title no longer clips into other level titles below it. ([b86efaa](https://github.com/FunkinCrew/Funkin/commit/b86efaaf7bc6b96fc3234b8f22420c2178fa4306)) - by @KoloInDaCrib in [#4348](https://github.com/FunkinCrew/Funkin/pull/4348)
|
||||
- The debug cursor is now always hidden when the game starts. ([6222c38](https://github.com/FunkinCrew/Funkin/commit/6222c389e301fa2bb4939697376c4e51e29a9977)) - by @Hundrec in [#4520](https://github.com/FunkinCrew/Funkin/pull/4520)
|
||||
- Fixed Newgrounds score submissions for Lit Up and Lit Up (BF Mix). ([183cec6](https://github.com/FunkinCrew/Funkin/commit/183cec62dc1fd3c7f3f634dd3e2400e6ee77b476)) - by @Raltyro in [#4577](https://github.com/FunkinCrew/Funkin/pull/4577)
|
||||
- Freeplay song ranks no longer disappear after changing variations. ([c00f10a](https://github.com/FunkinCrew/Funkin/commit/c00f10adc8ebc7a6876bf20d6a4d15e44bb7d863)) - by @VioletSnowLeopard in [#4583](https://github.com/FunkinCrew/Funkin/pull/4583)
|
||||
- Freeplay song capsules now cycle through long names consistently. ([ae086e7](https://github.com/FunkinCrew/Funkin/commit/ae086e7810437069efd3a4afbd55a5c303f473fb)) - by @VioletSnowLeopard in [#4677](https://github.com/FunkinCrew/Funkin/pull/4677)
|
||||
- Properly blacklist a certain class from scripts. ([3dc7699](https://github.com/FunkinCrew/Funkin/commit/3dc7699aac737998b637ff9a9f16986a434424be)) - by @charlesisfeline in [#4773](https://github.com/FunkinCrew/Funkin/pull/4773)
|
||||
- The song countdown no longer stacks when restarting or continues behind the Pause Menu. ([63eca96](https://github.com/FunkinCrew/Funkin/commit/63eca96c98a87e7155df8b2a1735f269ea83e1b5)) - by @NotHyper-474 in [#4875](https://github.com/FunkinCrew/Funkin/pull/4875)
|
||||
- The charter field in the song metadata now properly displays the charter in the Chart Editor. ([4d24087](https://github.com/FunkinCrew/Funkin/commit/4d24087c2126f64f0bbdf32557e6f0ee7f2aee24)) - by @Lasercar in [#4879](https://github.com/FunkinCrew/Funkin/pull/4879)
|
||||
- The Results Debug menu now shows the correct rank after recent scoring changes. ([66e36b9](https://github.com/FunkinCrew/Funkin/commit/66e36b99051298ddf306d981b3b0f29ddc88ffe3)) - by @NotHyper-474 in [#4905](https://github.com/FunkinCrew/Funkin/pull/4905)
|
||||
- Opening the logs or backups folder before it's created no longer crashes the game. ([60e2b88](https://github.com/FunkinCrew/Funkin/commit/60e2b88703678776994d7a038687773a1c639019)) - by @NotHyper-474 in [#4940](https://github.com/FunkinCrew/Funkin/pull/4940)
|
||||
- An easter egg now restarts the song using the correct instrumental. ([a499f46](https://github.com/FunkinCrew/Funkin/commit/a499f46e6dcec301ab35e62b9e494c7fac2e9c9b)) - by @KoloInDaCrib in [#4956](https://github.com/FunkinCrew/Funkin/pull/4956)
|
||||
- Girlfriend's and Nene's combo drop animations now play consistently. ([34d5ed1](https://github.com/FunkinCrew/Funkin/commit/34d5ed11695cef7348c13505f13fb1da38b7988c)) - by @VioletSnowLeopard in [#4968](https://github.com/FunkinCrew/Funkin/pull/4968)
|
||||
- Fixed a few visual issues with Freeplay's rank slam animation. ([ab817bb](https://github.com/FunkinCrew/Funkin/commit/ab817bb1eab74ae71c1b1fd74d7512a11e6d4339)) - by @Lasercar in [#4986](https://github.com/FunkinCrew/Funkin/pull/4986)
|
||||
- Nonexistent characters no longer crash the Chart Editor. ([3bbb4b0](https://github.com/FunkinCrew/Funkin/commit/3bbb4b06c8c1a1885a18d4354fcfa4363a0c6c75)) - by @Lasercar in [#5008](https://github.com/FunkinCrew/Funkin/pull/5008)
|
||||
- The Chart Editor playhead can no longer be scrolled to before the beginning of the song. ([03a0975](https://github.com/FunkinCrew/Funkin/commit/03a09752991c830be12befd5488fc8a102273c99)) - by @Hundrec in [#5024](https://github.com/FunkinCrew/Funkin/pull/5024)
|
||||
- Fixed incorrect highlighting and squashed text on Freeplay song capsules. ([0c62428](https://github.com/FunkinCrew/Funkin/commit/0c62428fc883c0fa6d09cd403efb287ff3af8c53)) - by @VioletSnowLeopard in [#5036](https://github.com/FunkinCrew/Funkin/pull/5036)
|
||||
- The song no longer restarts from an earlier point in the song instead of ending.
|
||||
- Perfect ranks no longer occasionally disappear after the rank slam animation.
|
||||
- Later notes are no longer hit before earlier notes on songs with high scroll speeds.
|
||||
- [MOBILE] Clearing save data no longer crashes the game.
|
||||
- [MOBILE] Cutscenes no longer continue playing behind the Pause Menu after losing focus.
|
||||
- [MOBILE] Freeplay capsules now properly shift away from under the letter sort bar.
|
||||
- [MOBILE] The Options button now animates properly after the first interaction.
|
||||
- [HTML5] The “Touch here to play” button is no longer too large.
|
||||
- Shaders now apply to A-Bot.
|
||||
- Adjusted offsets for some of Pico’s special animations.
|
||||
- Trimmed the extra silence at the beginning of Stress.
|
||||
- Trimmed the extra silence at the end of Senpai.
|
||||
- Monster and Winter Horrorland now display the correct album.
|
||||
- The “Haptics intensity” preference no longer displays inconsistent values.
|
||||
- The Lag Adjustment menu no longer permanently stops sending notes.
|
||||
- The Results Screen clear percentage now disappears behind the sound system.
|
||||
- The Debug Menu background now accounts for wider aspect ratios.
|
||||
- The Legacy Chart Importer now imports charts properly.
|
||||
- Fixed the “Starting BPM” text in the Chart Editor being tiny.
|
||||
- Chromebooks no longer use touch controls.
|
||||
- Enabled note recycling, resulting in a significant performance boost during songs. ([57a276d](https://github.com/FunkinCrew/Funkin/commit/57a276d17c293f40d6b27a89816b1659fc8b078f)) - by @KoloInDaCrib and @NotHyper-474 in [#5732](https://github.com/FunkinCrew/Funkin/pull/5732)
|
||||
- Misses are now properly processed for recycled notes. ([e7f7f47](https://github.com/FunkinCrew/Funkin/commit/e7f7f4794f410b74eab79941531f19c68737f94e)) - by @NotHyper-474 in [#5751](https://github.com/FunkinCrew/Funkin/pull/5751)
|
||||
- Bad save data no longer causes the game to immediately crash. ([84f1190](https://github.com/FunkinCrew/Funkin/commit/84f119036b44881488e47136f23f960855d47897)) - by @mikolka9144 in [#5703](https://github.com/FunkinCrew/Funkin/pull/5703)
|
||||
- Spamming the Reset and Accept buttons no longer softlocks the game over screen. ([01a2458](https://github.com/FunkinCrew/Funkin/commit/01a2458c1281440947f1aab586752e7ff490fe37)) - by @KoloInDaCrib in [#5714](https://github.com/FunkinCrew/Funkin/pull/5714)
|
||||
- Pressing F4 while calibrating or testing offsets no longer results in a softlock. ([7f82b48](https://github.com/FunkinCrew/Funkin/commit/7f82b48e3face75791955d28e807b7919d668be6)) - by @KoloInDaCrib in [#5716](https://github.com/FunkinCrew/Funkin/pull/5716)
|
||||
- Spamming through the Freeplay letter sort lists no longer crashes the game. ([0d6bc2b](https://github.com/FunkinCrew/Funkin/commit/0d6bc2bedd4494838db288247c4b13ec4031f0a8)) - by @JVNpixels in [#5578](https://github.com/FunkinCrew/Funkin/pull/5578)
|
||||
- Main menu items can no longer be scrolled and selected again after selecting an item. ([1fc611d](https://github.com/FunkinCrew/Funkin/commit/1fc611d1308e26f50cc01658b0ab7b848f05debe)) - by @JackXson-Real in [#5680](https://github.com/FunkinCrew/Funkin/pull/5680)
|
||||
- Fixed a crash caused by parsing CRLF line endings incorrectly. ([65349f4](https://github.com/FunkinCrew/Funkin/commit/65349f47145f44c94dae8a7a39004968da48cf0d)) - by @NotHyper-474 in [#5656](https://github.com/FunkinCrew/Funkin/pull/5656)
|
||||
- Fixed a script error when playing a Week 3 Erect song after a Week 3 (Pico Mix) song. ([6ecada8](https://github.com/FunkinCrew/funkin.assets/commit/6ecada82ac0072826132cad8f9e11f07006f9977)) - by @ThatRozebudDude in [funkin.assets#131](https://github.com/FunkinCrew/funkin.assets/pull/131)
|
||||
- Boyfriend's vocals no longer double up after restarting in Week 3. ([743e8b1](https://github.com/FunkinCrew/Funkin/commit/743e8b13c28d9f0471b392edc3127973f6fef329)) - by @NotHyper-474 in [#5408](https://github.com/FunkinCrew/Funkin/pull/5408)
|
||||
- Songs with BPM changes now restart at the correct BPM. ([51eec45](https://github.com/FunkinCrew/Funkin/commit/51eec4522e49ed7ed2c9457b5224c3b06e758958)) - by @VioletSnowLeopard in [#4721](https://github.com/FunkinCrew/Funkin/pull/4721)
|
||||
- Dropped hold notes are now properly handled. ([1df0cb9](https://github.com/FunkinCrew/Funkin/commit/1df0cb95f6a86d41dc5ef2d19a28c4c09952a69a)) - by @Lasercar in [#5172](https://github.com/FunkinCrew/Funkin/pull/5172)
|
||||
- Fullscreen keybinds now work properly on Web builds. ([1fbb760](https://github.com/FunkinCrew/Funkin/commit/1fbb7604ddf4fb8d311b9b414cbcfa0fd9bab255)) - by @NotHyper-474 in [#5664](https://github.com/FunkinCrew/Funkin/pull/5664)
|
||||
- Corrected the heights of characters in the intro text font. ([90242e2](https://github.com/FunkinCrew/funkin.assets/commit/90242e2e200fca38f5f535986db49b7a53282d1f)) - by @SpritersBlock in [funkin.assets#190](https://github.com/FunkinCrew/funkin.assets/pull/190)
|
||||
- PureColor shaders now properly apply anti-aliasing. ([8d1bfa1](https://github.com/FunkinCrew/Funkin/commit/8d1bfa105c89f68ab1f14a197f8c0a5e4fe7fb14)) - by @MaybeMaru in [#5781](https://github.com/FunkinCrew/Funkin/pull/5781)
|
||||
- The Preferences menu camera now remembers its position when exiting and re-entering. ([3d88fbb](https://github.com/FunkinCrew/Funkin/commit/3d88fbb11fea2b52d399f37cb8154795329c31c2)) - by @VirtuGuy in [#5608](https://github.com/FunkinCrew/Funkin/pull/5608)
|
||||
- The Freeplay DJ no longer flickers between animations. ([1b91f3b](https://github.com/FunkinCrew/Funkin/commit/1b91f3b57cda3310db2f5196650b89c8d88e69be)) - by @VirtuGuy in [#5598](https://github.com/FunkinCrew/Funkin/pull/5598)
|
||||
- The Freeplay DJ's turntable lights now glow properly. ([0c5a965](https://github.com/FunkinCrew/funkin.assets/commit/0c5a965400937cbdac55ed9317a2a00a9d4343ae)) - by @Johferson in [funkin.assets#211](https://github.com/FunkinCrew/funkin.assets/pull/211)
|
||||
- The Random capsule now opens the selected song with the correct variation. ([1605514](https://github.com/FunkinCrew/Funkin/commit/160551442453fccc0961c570701bdba7bee95ef0)) - by @PurSnake in [#5611](https://github.com/FunkinCrew/Funkin/pull/5611)
|
||||
- Newly earned Freeplay ranks no longer appear larger than others. ([47813ea](https://github.com/FunkinCrew/Funkin/commit/47813eac3157927f851713cc4c41c5eb5fac2e19)) - by @anysad in [#5768](https://github.com/FunkinCrew/Funkin/pull/5768)
|
||||
- Freeplay now displays the correct number of difficulty stars after returning from a song. ([30721c3](https://github.com/FunkinCrew/Funkin/commit/30721c3a30f7e980557d055daa30e7e5501b83e3)) - by @KoloInDaCrib in [#5560](https://github.com/FunkinCrew/Funkin/pull/5560)
|
||||
- Removed stray pixels around pixel icons in Freeplay and Character Select. ([b1d770b](https://github.com/FunkinCrew/funkin.assets/commit/b1d770b7f69cb73bd35a279ba5b26908e0cd94be)) - by @Hundrec in [funkin.assets#69](https://github.com/FunkinCrew/funkin.assets/pull/69)
|
||||
- The nametag in the Character Select screen now fades and moves as other elements do. ([2b1f346](https://github.com/FunkinCrew/Funkin/commit/2b1f346097efadcad19058c4ed9cefe4f84c8b64)) - by @JackXson-Real in [#5583](https://github.com/FunkinCrew/Funkin/pull/5583)
|
||||
- [MOBILE] The touch pointer now displays during songs. ([9d2cfa9](https://github.com/FunkinCrew/Funkin/commit/9d2cfa9686880170c5c398786c72d878974eaf93)) - by @SrtHero278 in [#5565](https://github.com/FunkinCrew/Funkin/pull/5565)
|
||||
- Note splashes are now more centered on the strumline. ([cfa6016](https://github.com/FunkinCrew/funkin.assets/commit/cfa6016eeacb745cb2787c99f8d1f156ab4d16e8)) - by @JVNpixels in [funkin.assets#234](https://github.com/FunkinCrew/funkin.assets/pull/234)
|
||||
- Removed stray pixels from BF's old icon and aligned its losing icon. ([e57dc54](https://github.com/FunkinCrew/funkin.assets/commit/e57dc54fe333f8fa3f3933164b423128c74f4c38)) - by @Hundrec in [funkin.assets#93](https://github.com/FunkinCrew/funkin.assets/pull/93)
|
||||
- Daddy Dearest's idle animation now plays on beat. ([b4c1aa9](https://github.com/FunkinCrew/funkin.assets/commit/b4c1aa95aad45c42256432ec81cce8bf35107fa7)) - by @qt2k4 in [funkin.assets#148](https://github.com/FunkinCrew/funkin.assets/pull/148)
|
||||
- Boyfriend (Dark)'s idle animation now properly syncs with his regular idle. ([d1610f1](https://github.com/FunkinCrew/funkin.assets/commit/d1610f1e6f0d5b0db0af34e3d00e7a1fc1d4a4b5)) - by @TechnikTil in [funkin.assets#203](https://github.com/FunkinCrew/funkin.assets/pull/203)
|
||||
- Spooky Kids (Dark)'s idle animation now properly syncs with their regular idle. ([aa28bd7](https://github.com/FunkinCrew/funkin.assets/commit/aa28bd74a33ab0026649d19e642c41b4f1c5cd06)) - by @Honton129 in [funkin.assets#213](https://github.com/FunkinCrew/funkin.assets/pull/213)
|
||||
- Lightning now appears more regularly during South and Monster in Story Mode. ([c5f4a8e](https://github.com/FunkinCrew/funkin.assets/commit/c5f4a8e55f18cbc2e57a72743320da79358b1816)) - by @JackXson-Real in [funkin.assets#222](https://github.com/FunkinCrew/funkin.assets/pull/222)
|
||||
- The Picos no longer gasp twice in the Week 3 doppelganger cutscene. ([7b30a14](https://github.com/FunkinCrew/funkin.assets/commit/7b30a1418ef4597e567a22a2ea1bc1d9e4b061da)) - by @Lasercar in [funkin.assets#231](https://github.com/FunkinCrew/funkin.assets/pull/231)
|
||||
- The Week 3 train now consistently plays its sound. ([469ce39](https://github.com/FunkinCrew/funkin.assets/commit/469ce397b8eef3be47f8fadaa61a500e1a87ca3a)) - by @Lasercar in [funkin.assets#212](https://github.com/FunkinCrew/funkin.assets/pull/212)
|
||||
- Nene no longer freezes after playing two animations at once. ([d346ebd](https://github.com/FunkinCrew/funkin.assets/commit/d346ebdbf4781b49a081e0719221cf584fc3fe55)) - by @NotHyper-474 in [funkin.assets#228](https://github.com/FunkinCrew/funkin.assets/pull/228)
|
||||
- The Week 4 car now properly pauses with the game. ([26d1cff](https://github.com/FunkinCrew/funkin.assets/commit/26d1cffe19793c67a89f299b7a60be95c5656282)) - by @NotHyper-474 in [funkin.assets#114](https://github.com/FunkinCrew/funkin.assets/pull/114)
|
||||
- Pressing Reset and Pause at the same time no longer breaks the game over animation in Blazin'. ([8cd3785](https://github.com/FunkinCrew/Funkin/commit/8cd3785e3c3e913ed791204814eba86eb7142a5d)) - by @VirtuGuy in [#5639](https://github.com/FunkinCrew/Funkin/pull/5639)
|
||||
- Pico's Blazin' death animation no longer lags and offsets improperly. ([2cb42fe](https://github.com/FunkinCrew/funkin.assets/commit/2cb42fec422313db4e8000c963be0539221c9e21)) - by @KoloInDaCrib in [funkin.assets#217](https://github.com/FunkinCrew/funkin.assets/pull/217)
|
||||
- Pico and Darnell now properly punch with their other arms in Blazin'. ([4fb015f](https://github.com/FunkinCrew/funkin.assets/commit/4fb015f20261995ce41370910989094f4f2f26bb)) - by @Honton129 in [funkin.assets#221](https://github.com/FunkinCrew/funkin.assets/pull/221)
|
||||
- Pico's Perfect Results Screen animation now syncs with the music. ([0531e27](https://github.com/FunkinCrew/funkin.assets/commit/0531e27feed83dcc9a1703b3df654fd3bba1bd8a)) - by @JackXson-Real in [funkin.assets#201](https://github.com/FunkinCrew/funkin.assets/pull/201)
|
||||
- Filled in invisible pixels in the Story Mode button. ([c83ea78](https://github.com/FunkinCrew/funkin.assets/commit/c83ea788485693e22d429d81db8eb4da202e0140)) - by @D4rkJony in [funkin.assets#214](https://github.com/FunkinCrew/funkin.assets/pull/214)
|
||||
- Filled in invisible pixels in Tankman's icons. ([cb46973](https://github.com/FunkinCrew/funkin.assets/commit/cb46973bf8d9f48cd4738d483957c1edb501b386)) - by @JackXson-Real in [funkin.assets#224](https://github.com/FunkinCrew/funkin.assets/pull/224)
|
||||
- Scripted classes are no longer registered twice after hot-reloading. ([f870ed2](https://github.com/FunkinCrew/Funkin/commit/f870ed2858507273c4dbbca3e4b8b97d80a8d2ec)) - by @KoloInDaCrib in [#5743](https://github.com/FunkinCrew/Funkin/pull/5743)
|
||||
- The Chart Editor now properly uses the song ID from the manifest. ([df67b64](https://github.com/FunkinCrew/Funkin/commit/df67b642a8d7f7460e36a42c570a1ff2303751fd)) - by @cyn0x8 in [#4168](https://github.com/FunkinCrew/Funkin/pull/4168)
|
||||
- Dragging and dropping a Chart Editor selection in the same place no longer breaks the selection. ([4b65587](https://github.com/FunkinCrew/Funkin/commit/4b655879f763f6dbe34c89fc4a781cae9ff4abd8)) - by @Lasercar in [#4185](https://github.com/FunkinCrew/Funkin/pull/4185)
|
||||
- Clicking between hold note trails no longer causes visual errors in the Chart Editor. ([0e52744](https://github.com/FunkinCrew/Funkin/commit/0e527447188dbd3d850154a73f61467c1b8e585a)) - by @KoloInDaCrib in [#5683](https://github.com/FunkinCrew/Funkin/pull/5683)
|
||||
- Holding Shift and Right Click over a note no longer produces a solitaire effect in the Chart Editor. ([172041d](https://github.com/FunkinCrew/Funkin/commit/172041d4f779271533e0d1c529297540bfec337a)) - by @Lasercar in [#4139](https://github.com/FunkinCrew/Funkin/pull/4139)
|
||||
- Chart Editor event properties now have minimum values. ([9b81ebc](https://github.com/FunkinCrew/Funkin/commit/9b81ebc4f2c1c70b33d7351210c40c9a0e48ecc8)) - by @Lasercar in [#4110](https://github.com/FunkinCrew/Funkin/pull/4110)
|
||||
- Added missing notekinds to the Chart Editor Notes window. ([b7a5761](https://github.com/FunkinCrew/Funkin/commit/b7a5761730d43f27891dfec1c23fcb5e66daccf1)) - by @Lasercar in [#4286](https://github.com/FunkinCrew/Funkin/pull/4286)
|
||||
- Playback speed is now preserved when a new song or variation is loaded in the Chart Editor. ([3f82f37](https://github.com/FunkinCrew/Funkin/commit/3f82f3792b056a03724a0d1a320b7549a7b59844)) - by @Lasercar in [#4212](https://github.com/FunkinCrew/Funkin/pull/4212)
|
||||
- The Legacy Chart Importer now properly imports scroll speeds. ([2645d56](https://github.com/FunkinCrew/Funkin/commit/2645d56514dd1a27a050ff432c3cd4122f958e10)) - by @VirtuGuy in [#5655](https://github.com/FunkinCrew/Funkin/pull/5655)
|
||||
- Opponent characters now play singing animations when selected as the player character. ([8ee24c8](https://github.com/FunkinCrew/funkin.assets/commit/8ee24c84de6aa6e6313699198f35f61991612d04)) - by @Lasercar in [funkin.assets#141](https://github.com/FunkinCrew/funkin.assets/pull/141)
|
||||
- The Chart Editor character selector window no longer opens when clicking while playtesting. ([223e837](https://github.com/FunkinCrew/Funkin/commit/223e837fe8d9ce227e289a38bdfc095e4d81c48a)) - by @Lasercar in [#4350](https://github.com/FunkinCrew/Funkin/pull/4350)
|
||||
- Renamed Nene's pixel variant to Nene (Pixel) in the Chart Editor. ([69a7898](https://github.com/FunkinCrew/funkin.assets/commit/69a78988e3d6042d2e4779c9a4055c5de73ee775)) - by @JVNpixels in [funkin.assets#208](https://github.com/FunkinCrew/funkin.assets/pull/208)
|
||||
- Renamed Tankman's variants in the Chart Editor. ([70c43b7](https://github.com/FunkinCrew/funkin.assets/commit/70c43b792078d913c4c4d9fc019e378249d08b78)) - by @ExtraCode75 in [funkin.assets#209](https://github.com/FunkinCrew/funkin.assets/pull/209)
|
||||
- Pressing F4 in the Chart Editor now properly resets the window title. ([86e69cf](https://github.com/FunkinCrew/Funkin/commit/86e69cfbd1b1bd3daa49181b827fa813cb716703)) - by @VirtuGuy in [#5651](https://github.com/FunkinCrew/Funkin/pull/5651)
|
||||
- Saving a character in the Animation Editor no longer crashes the game. ([da5f691](https://github.com/FunkinCrew/Funkin/commit/da5f6918656d4a1ad3e83233b358034db92ca675)) - by @anysad in [#5519](https://github.com/FunkinCrew/Funkin/pull/5519)
|
||||
- The Stage Editor can now properly load modded stages. ([50c601a](https://github.com/FunkinCrew/Funkin/commit/50c601a7f0f27fc0f155e2bfbcb8ef54631aeac7)) - by @KoloInDaCrib in [#5744](https://github.com/FunkinCrew/Funkin/pull/5744)
|
||||
- XML files exported from the Stage Editor now account for rotated frames. ([661ddb1](https://github.com/FunkinCrew/Funkin/commit/661ddb15f72c2dae22f41a922a521740ab3617f0)) - by @KoloInDaCrib in [#5303](https://github.com/FunkinCrew/Funkin/pull/5303)
|
||||
- A few more adjustments for good measure.
|
||||
|
||||
## New Contributors for 0.7.4
|
||||
|
||||
* @ThatOneCalculator made their first contribution in [#5507](https://github.com/FunkinCrew/Funkin/pull/5507)
|
||||
* @SrtHero278 made their first contribution in [#5565](https://github.com/FunkinCrew/Funkin/pull/5565)
|
||||
* @VirtuGuy made their first contribution in [#5598](https://github.com/FunkinCrew/Funkin/pull/5598)
|
||||
* @SpritersBlock made their first contribution in [funkin.assets#190](https://github.com/FunkinCrew/funkin.assets/pull/190)
|
||||
* @Johferson made their first contribution in [funkin.assets#211](https://github.com/FunkinCrew/funkin.assets/pull/211)
|
||||
* @Honton129 made their first contribution in [funkin.assets#213](https://github.com/FunkinCrew/funkin.assets/pull/213)
|
||||
* @D4rkJony made their first contribution in [funkin.assets#214](https://github.com/FunkinCrew/funkin.assets/pull/214)
|
||||
|
||||
|
||||
|
||||
## [0.7.3] - 2025-07-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed stuttering throughout the game caused by the Polymod upgrade. (Thanks NotHyper-474!)
|
||||
- [MOBILE] Fixed buttons in the Main Menu not working.
|
||||
- [iOS] The Upgrade button no longer appears if you have already purchased it (actually this time).
|
||||
- Fixed the countdown overlapping itself when restarting the song. (Thanks NotHyper-474!)
|
||||
- Optimized the Week 6 Erect stage.
|
||||
- Fixed an oversight when clearing the cache. (Thanks cherrythecool!)
|
||||
- The Input Offset Test menu text now displays in the correct position.
|
||||
- Fixed script errors appearing in the Week 3 Erect stage.
|
||||
- Fixed adding variations in the Chart Editor erasing difficulties. (Thanks NotHyper-474!)
|
||||
|
||||
## New Contributors for 0.7.3
|
||||
|
||||
* @cherrythecool made their first contribution in [#5458](https://github.com/FunkinCrew/Funkin/pull/5458)
|
||||
|
||||
|
||||
|
||||
## [0.7.2] - 2025-07-18
|
||||
|
||||
### Added
|
||||
|
||||
- [ANDROID] Added a button in the Options menu to access the mods folder.
|
||||
- [MOBILE] Added a preference to adjust the intensity of haptic feedback, ranging from 0.1 to 5.
|
||||
- [MOBILE] Added an easter egg when tapping the player's healthbar icon.
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed default OpenAL configuration settings to improve audio quality. (Thanks Smokey555, cyn0x8, and CCobaltDev!)
|
||||
- The difference may be more or less noticeable on different devices and hardware.
|
||||
- Applies to Desktop and Android, but not iOS yet.
|
||||
- Made several improvements to Polymod and HScript. These changes might break some mods, so please update them accordingly!
|
||||
- Fixed an issue where scripted classes can define two or more fields with the same name.
|
||||
- Fixed an issue causing some syntax errors (such as missing commas) to be ignored by the parser.
|
||||
- Scripted classes can now create static fields and functions.
|
||||
- Scripted classes can now create variables with the `final` keyword.
|
||||
- Scripted classes can now access variables from another scripted class with `class.someVariable`, instead of `class.scriptGet("someVariable")`
|
||||
- This applies to functions too: `class.someFunction()`
|
||||
- Scripted classes that don't extend another class can now be created!
|
||||
- This only works if you access the class in a static context. Creating an instance of said class doesn't work just yet!
|
||||
- Added support for properties (`get_` and `set_` functions) (Thanks KoloInDaCrib!)
|
||||
- Added support for abstracts in a static context. (Thanks lemz1!)
|
||||
- You can now use classes like `FlxColor` properly!
|
||||
- Added support for creating and using enums. (Thanks lemz1!)
|
||||
- You can import them in another script as usual.
|
||||
- Added support for renaming imported classes using the `as` keyword. (Thanks KoloInDaCrib!)
|
||||
- Fixed `try`/`catch` blocks not working properly. (Thanks NotHyper-474!)
|
||||
- Fixed null-safe field access not working properly for functions (ex. `class?.someFunction()`). (Thanks KoloInDaCrib!)
|
||||
- Fixed Linux being case-sensitive with filenames. (Thanks mikolka9144!)
|
||||
|
||||
### Fixed
|
||||
|
||||
- [MOBILE] Weekend 1 Story Mode no longer crashes before loading into Blazin'.
|
||||
- [MOBILE] Beating 2hot from Freeplay no longer crashes in the Results screen.
|
||||
- [MOBILE] Retrying and pressing the Back button at the same time no longer crashes the game.
|
||||
- [MOBILE] Pressing the Options and Back buttons at the same time no longer softlocks the game.
|
||||
- [HTML5] Pausing while the train passes by on the Week 3 Erect stage no longer crashes.
|
||||
- [DESKTOP] Getting a Bad/Shit rating on Blazin' no longer breaks animations.
|
||||
- The scroll sound no longer plays once after entering Freeplay.
|
||||
- The Freeplay song preview and album cover now update properly when switching variations.
|
||||
- The Freeplay clear percent counter now consistently displays the correct value on unranked songs.
|
||||
- The Freeplay difficulty star flames no longer become offset from the stars.
|
||||
- The Freeplay difficulty star flames no longer appear during a new rank animation.
|
||||
- The Freeplay menu now correctly assigns the `currentCharacterId`. (Thanks TechnikTil!)
|
||||
- Boyfriend's Perfect (Gold) Results animation now loops properly.
|
||||
- [DESKTOP] The Input Offsets menu no longer activates the debug cursor.
|
||||
- The Input Offsets Test menu no longer generates stacked notes.
|
||||
- The Input Offsets Test menu drums no longer desync from the rest of the track.
|
||||
- The Input Offsets Test menu no longer breaks when a keyboard or controller is connected.
|
||||
- [MOBILE] Sustain trails now display properly with upscroll enabled.
|
||||
- [MOBILE] Added a Back button to the keyboard/gamepad Controls menu.
|
||||
- [iOS] Fixed app name spacing on the Home Screen.
|
||||
- [iOS] Adjusted the preloader to accommodate for different screen sizes.
|
||||
- [iOS] The Upgrade button no longer appears if you have already purchased it.
|
||||
- [ANDROID] Fixed some issues with scrolling.
|
||||
- [ANDROID] Toasts with blank messages no longer appear.
|
||||
- Fixed a critical security vulnerability that could be exploited in mods.
|
||||
- A few more bugfixes and optimizations here and there.
|
||||
|
||||
## New Contributors for 0.7.2
|
||||
|
||||
* @Smokey555 made their first contribution in [#3318](https://github.com/FunkinCrew/Funkin/pull/3318)
|
||||
* @CCobaltDev made their first contribution in [#3318](https://github.com/FunkinCrew/Funkin/pull/3318)
|
||||
* @mikolka9144 made their first contribution in [polymod#212](https://github.com/larsiusprime/polymod/pull/212)
|
||||
|
||||
|
||||
|
||||
## [0.7.1] - 2025-07-15
|
||||
|
||||
### Fixed
|
||||
- Properly implemented ad playback on iOS devices.
|
||||
|
||||
|
||||
|
||||
## [0.7.0] - 2025-07-15
|
||||
|
||||
### Added
|
||||
|
||||
- Friday Night Funkin' now has OFFICIAL mobile versions for Android and iOS, available on the Google Play Store and Apple App Store!
|
||||
- This version contains 100% of the songs from the desktop version of the game.
|
||||
- [MOBILE] New touch input compatibility for all menus.
|
||||
- [MOBILE] Added banner and interstitial advertisements to the game. You can upgrade to the full version through an in-app purchase to permanently disable advertisements.
|
||||
- [MOBILE] Graphics are compressed using the ASTC algorithm, decreasing memory usage in exchange for a slightly larger file size.
|
||||
- [MOBILE] Added haptic feedback to several areas of the game.
|
||||
- Added a visual indicator that shows available difficulties for the currently selected song in Freeplay.
|
||||
- Overhauled the input offsets system, including:
|
||||
- One unified "offset" value.
|
||||
- An "Offset Calibration" screen where the game determines your ideal offset.
|
||||
- A "Test" screen where you can play a short note pattern to try out your offset.
|
||||
- A brand new offset testing theme: Syncobation by Kawai Sprite!
|
||||
- The ability to change your offsets in the Pause Menu, mid-song!
|
||||
- The Input Offsets menu isn't yet available on HTML5, but offsets are still configurable through the Pause Menu.
|
||||
- Added null-safety to a bunch of classes in the source code.
|
||||
- Added the Changelog back to the game files, written by Hundrec and AbnormalPoof!
|
||||
- Added a few sandboxed classes to give mods limited access to the Discord and Newgrounds APIs. ([50d9584](https://github.com/FunkinCrew/Funkin/commit/50d9584a388bd891aa2f8b68a5cde894a6e1ede6)) - by @KoloInDaCrib in [#5040](https://github.com/FunkinCrew/Funkin/pull/5040)
|
||||
- Added script support for Freeplay Backing Cards. ([0001017](https://github.com/FunkinCrew/Funkin/commit/0001017c003be653236c6cc56487c7d0ee33633e)) - by @KoloInDaCrib in [#5233](https://github.com/FunkinCrew/Funkin/pull/5233)
|
||||
- Sparrow results screen animations can now be scriptable. ([7bb2336](https://github.com/FunkinCrew/Funkin/commit/7bb23369727ca4955aa1fbe25e5798809e8169bd)) - by @KoloInDaCrib in [#5168](https://github.com/FunkinCrew/Funkin/pull/5168)
|
||||
- Added a blank `Object` class for scripts to extend, and made `FlxObject` and `FlxBasic` scriptable. ([eb6becc](https://github.com/FunkinCrew/Funkin/commit/eb6becc03fff76117ee3fcbeb32fe254236ca232)) - by @cyn0x8 in [#3119](https://github.com/FunkinCrew/Funkin/pull/3119)
|
||||
- Added default gamepad controls for two recently added Freeplay controls. ([a0d3f8e](https://github.com/FunkinCrew/Funkin/commit/a0d3f8ec553e06b625b463c7989658edbebbbdf5)) - by @MrMadera in [#4559](https://github.com/FunkinCrew/Funkin/pull/4559)
|
||||
- Added the ability to press the Chart Editor keybind in Freeplay with a song capsule selected. ([2221594](https://github.com/FunkinCrew/Funkin/commit/2221594883afa7cd0e518fca7ea975d05626692a)) - by @Lasercar in [#4114](https://github.com/FunkinCrew/Funkin/pull/4114)
|
||||
- The Chart Editor now highlights and deletes stacked notes using a customizable threshold. ([8cae34e](https://github.com/FunkinCrew/Funkin/commit/8cae34eed711bff70e5348ffc6178a0fd69b5846)) - by @NotHyper-474 in [#3574](https://github.com/FunkinCrew/Funkin/pull/3574)
|
||||
- Added a variation indicator next to the Chart Editor playbar difficulty. ([ccd0148](https://github.com/FunkinCrew/Funkin/commit/ccd0148e9b46d512a22b4958d3f289cfc7854965)) - by @KoloInDaCrib in [#5236](https://github.com/FunkinCrew/Funkin/pull/5236)
|
||||
- Added more tween types to certain Chart Editor events. ([5177e12](https://github.com/FunkinCrew/Funkin/commit/5177e1275eb2fb2b016224c139e84debb421b895)) - by @Lasercar in [#4249](https://github.com/FunkinCrew/Funkin/pull/4249)
|
||||
- Pressing Ctrl + N now creates a new stage in the Stage Editor. ([576f8e5](https://github.com/FunkinCrew/Funkin/commit/576f8e54ff8ca8e205241fafa33d0256b62d11d5)) - by @Lasercar in [#5175](https://github.com/FunkinCrew/Funkin/pull/5175)
|
||||
- Added "Flip character horizontally" to the list of shortcuts in the Animation Editor. ([c464cae](https://github.com/FunkinCrew/funkin.assets/commit/c464caec921dcefef7b0b74b2abf95e76ce64491)) - by @AbnormalPoof in [funkin.assets#60](https://github.com/FunkinCrew/funkin.assets/pull/60)
|
||||
- Added Perfect (Gold) to the list of available ranks in Results Debug menu. ([c5308cc](https://github.com/FunkinCrew/Funkin/commit/c5308ccbb9d2b98c62fa4974b8ad7ac1e1ec7d19)) - by @AbnormalPoof in [#4642](https://github.com/FunkinCrew/Funkin/pull/4642)
|
||||
- [MOBILE] Implemented Kevin and Michael.
|
||||
|
||||
### Changed
|
||||
|
||||
- The mod API version now supports v0.7.0, along with v0.6.3. Be sure to check that your mods still work!
|
||||
- Updated the app icon for Desktop platforms.
|
||||
- [MOBILE] Modified several parts of the game to look better on phone screens with wider aspect ratios, up to 20:9.
|
||||
- [DESKTOP] The game now tries to match the window's aspect ratio when changing states, extending as wide as 20:9.
|
||||
- [DESKTOP] Included Mobile stage expansions on Desktop as well. Now you'll have more room for camera events!
|
||||
- Playable Pico and Weekend 1 songs are now always unlocked in Freeplay, even on new saves.
|
||||
- The Freeplay difficulty graphic now scrolls smoothly when changing difficulties.
|
||||
- The "Pause on Unfocus" preference now opens the Pause Menu when unfocusing during a song.
|
||||
- Scripts can now make hold note trails semi-transparent.
|
||||
- Completely reformatted every script file within the game's assets for better readability.
|
||||
- Completely reformatted and optimized every single chart file in the game.
|
||||
- Recharted pico-speaker's chart in Stress
|
||||
- Tweaked charts for the following songs:
|
||||
- Bopeebo [all difficulties] - Removed an extra hey animation event
|
||||
- Bopeebo (Pico Mix) [Hard] - Added a missing note in Section 24
|
||||
- Fresh Erect [Nightmare] - Added a missing grace note for BF in Section 24
|
||||
- South Erect [Nightmare] - Added missing grace notes for BF in Sections 13, 17, and 53
|
||||
- Philly Nice [Hard] - Added missing grace notes for Pico in Sections 30 and 62
|
||||
- Philly Nice [all difficulties] - Added hey animations throughout the song
|
||||
- Philly Nice Erect [Erect] - Added a grace note for BF in Section 33, removed a stacked note for opponent in Section 12
|
||||
- Philly Nice (Pico Mix) [Normal] - Adjusted a left note by 1/96 in Section 60
|
||||
- Blammed (Pico Mix) [Hard] - Added a missing jack in Section 46
|
||||
- Satin Panties [Hard] - Added grace notes in Sections 7-10
|
||||
- Satin Panties [Normal/Hard] - Made Mom sing a sustain rather than two notes in Section 30
|
||||
- High Erect [Erect/Nightmare] - Added a missing note in Section 16
|
||||
- Cocoa [Easy] - Added some notes to reduce sparseness, fixed Mom singing Dad's notes
|
||||
- Cocoa Erect [Erect/Nightmare] - Reimplemented BF's censored notes for Nightmare, adjusted one note by 1/48 in Section 63
|
||||
- Eggnog Erect [Erect/Nightmare] Added two grace notes in Sections 10 and 14 and a missing note for Dad in Section 44
|
||||
- Eggnog (Pico Mix) [Hard] - Added a missing grace note for Pico that was present on Normal in Section 20
|
||||
- Roses [Normal/Hard] - Made Senpai sing a sustain rather than two notes (sneaky)
|
||||
- Roses Erect [Erect/Nightmare] - Mirrored the changes from normal Roses
|
||||
- Guns [all difficulties] - Added a missing note in Sections 28 and 32 and adjusted a hold note's length in Section 73
|
||||
- Stress [Hard] - Split whole notes in halves in Sections 57-60
|
||||
- Darnell [Hard] - Added one missing note for Pico in Section 35
|
||||
- Darnell [all difficulties] - Adjusted camera event timings for consistency
|
||||
- Darnell (BF Mix) [all difficulties] - Removed 3 extra notes and fixed Darnell's pattern being offset
|
||||
- Lit Up [all difficulties] - Added 4 sustains for Darnell throughout the song
|
||||
- Lit Up (BF Mix) [all difficulties] - Added 4 sustains for Darnell throughout the song
|
||||
- 2hot [Easy/Hard] - Fixed remaining offset rhythms (for real this time)
|
||||
- Notes now scroll more smoothly by rendering based on delta timing. ([6ad9ffc](https://github.com/FunkinCrew/Funkin/commit/6ad9ffc7f9d66bbaf6ba343663de4c7268f4be3b)) - by @KutikiPlayz in [#3544](https://github.com/FunkinCrew/Funkin/pull/3544)
|
||||
- The Freeplay character select hint now always displays if you have more than one character unlocked. ([7ccf75c](https://github.com/FunkinCrew/Funkin/commit/7ccf75cd869ba4b6f18a5adc01e65e52ae7bb809)) - by @Hundrec in [#5023](https://github.com/FunkinCrew/Funkin/pull/5023)
|
||||
- Favorite songs in Freeplay are now sorted by Week order instead of alphabetically. ([da0964a](https://github.com/FunkinCrew/Funkin/commit/da0964a7b7bbe4ece1bdbd19233eb6dba0de3ac5)) - by @Hundrec in [#3609](https://github.com/FunkinCrew/Funkin/pull/3609)
|
||||
- Shifted Mommy Mearest's pixel icon to the left in Freeplay. ([d861eba](https://github.com/FunkinCrew/funkin.assets/commit/d861ebac027dd07d0254c79d7c89b59ee04b38f1)) - by @KoloInDaCrib in [funkin.assets#197](https://github.com/FunkinCrew/funkin.assets/pull/197)
|
||||
- The Character Select screen now opens on the currently selected character. ([4819a74](https://github.com/FunkinCrew/Funkin/commit/4819a74c2959cc9b32dfe2cb76c3a4c00e7c7f9a)) - by @Lasercar in [#4072](https://github.com/FunkinCrew/Funkin/pull/4072)
|
||||
- Visualizers now zero out when the game audio is muted. ([6dcec59](https://github.com/FunkinCrew/Funkin/commit/6dcec592f467a0daeb8ff1e0ce122916e36ca869)) - by @Lasercar in [#5266](https://github.com/FunkinCrew/Funkin/pull/5266)
|
||||
- The Options Menu can now scroll to display more menu items. ([70f0a54](https://github.com/FunkinCrew/Funkin/commit/70f0a54191597bd72a6d30d4d12ef5ece6ba078c)) - by @AbnormalPoof in [#4706](https://github.com/FunkinCrew/Funkin/pull/4706)
|
||||
- Raised the FPS cap preference from 300 to 500. ([be73134](https://github.com/FunkinCrew/Funkin/commit/be7313453f70983fff55e69a8b52d741f0cc53b4)) - by @Hundrec in [#5044](https://github.com/FunkinCrew/Funkin/pull/5044)
|
||||
- The Credits menu now uses less memory, especially with many entries. ([1b68c3a](https://github.com/FunkinCrew/Funkin/commit/1b68c3a8d6f66905a9a508a1cb692fe3beb7b4a2)) - by @lemz1 in [#2655](https://github.com/FunkinCrew/Funkin/pull/2655)
|
||||
- Added a timer sequence class to queue up multiple timers in scripts with ease. ([9e182f7](https://github.com/FunkinCrew/Funkin/commit/9e182f70d2bcc92eb68d730d74af143c45f7dcf8)) - by @cyn0x8 in [#2391](https://github.com/FunkinCrew/Funkin/pull/2391)
|
||||
- Replaced smoothLerp and coolLerp with smoothLerpPrecision to fix a few lerp-related bugs. ([94eae11](https://github.com/FunkinCrew/Funkin/commit/94eae116c7a5e6039683d6391208b169378b5ff1)) - by @cyn0x8 in [#3617](https://github.com/FunkinCrew/Funkin/pull/3617)
|
||||
- Fixed empty text strings softlocking the dialogue box. ([88d0e8c](https://github.com/FunkinCrew/Funkin/commit/88d0e8c3b0529654fb7eee8aebb099f7fb346f66)) - by @xenkap in [#4671](https://github.com/FunkinCrew/Funkin/pull/4671)
|
||||
- Adjusted the size of the Beat/Step display in the Chart Editor. ([905181c](https://github.com/FunkinCrew/Funkin/commit/905181c9af29bb11280bda33ef9343069678a762)) - by @NotHyper-474 in [#4994](https://github.com/FunkinCrew/Funkin/pull/4994)
|
||||
- The Chart Editor will now only fall back to the first available difficulty if the selected difficulty cannot be found. ([1c25713](https://github.com/FunkinCrew/Funkin/commit/1c257134648ebd89acf6c9d07f5a0c088fb915c6)) - by @Lasercar in [#4949](https://github.com/FunkinCrew/Funkin/pull/4949)
|
||||
- The undo/redo history is now cleared when loading another song in the Chart Editor. ([426a9c0](https://github.com/FunkinCrew/Funkin/commit/426a9c0c108ac65a042295194679d46444ec1ea5)) - by @Lasercar in [#4308](https://github.com/FunkinCrew/Funkin/pull/4308)
|
||||
- Blacklisted more classes for security reasons. ([cadfa3b](https://github.com/FunkinCrew/Funkin/commit/cadfa3b7ceae2ecabe2d544ddc4c9f453b0dfd56)) - by @NotHyper-474 in [#5185](https://github.com/FunkinCrew/Funkin/pull/5185)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a ton of performance issues to help the game run better on mobile devices.
|
||||
- Exiting the Freeplay Menu no longer freezes the game for a really long time (thanks NotHyper-474!)
|
||||
- Notestyle graphics are now preloaded before the song starts, fixing the stutter at the beginning of the song.
|
||||
- Hitting many hold note trails in one song no longer leads to a lag spike.
|
||||
- The first lightning strike in Week 2 Erect no longer creates a lag spike.
|
||||
- [DESKTOP] The conductor and music no longer gradually drift out of sync to eventually trigger a resync.
|
||||
- Pixel notestyle strumlines are now properly positioned when Downscroll is enabled.
|
||||
- Added the missing graffiti to the wall in the Weekend 1 Blazin' stage.
|
||||
- Accept keybinds now properly scroll faster through the Credits.
|
||||
- Typing in most text fields in debug editors no longer triggers keyboard shortcuts.
|
||||
- The Chart Editor playback speed feature now works properly.
|
||||
- The Chart Editor metronome and hitsounds now play at exactly the right time.
|
||||
- The Chart Editor notification box no longer covers playbar info.
|
||||
- Selecting a Recent File too quickly in the Stage Editor no longer crashes the game.
|
||||
- Blacklisted a few classes for security.
|
||||
- Opening the logs or backups folder before it's created no longer crashes the game. ([d3490f8](https://github.com/FunkinCrew/Funkin/commit/d3490f8c9929eefb9879ad65ce43038c193642d6)) - by @NotHyper-474 in [#4940](https://github.com/FunkinCrew/Funkin/pull/4940)
|
||||
- Fixed a crash when mashing D or I during startup. ([b52c73f](https://github.com/FunkinCrew/Funkin/commit/b52c73f2b0fa32fbc349804621cf947dff2d364e)) - by @CrusherNotDrip in [#5160](https://github.com/FunkinCrew/Funkin/pull/5160)
|
||||
- Hot-reloading with F5 during gameplay no longer crashes the game. ([d2acb5d](https://github.com/FunkinCrew/Funkin/commit/d2acb5d167afd42299d7200ab8c67972044a09c6)) - by @AbnormalPoof in [#5065](https://github.com/FunkinCrew/Funkin/pull/5065)
|
||||
- Hot-reloading with F5 in the Input Offsets menu no longer crashes the game. ([58257f6](https://github.com/FunkinCrew/Funkin/commit/58257f6ac187925f3b23d3f1eef2c812ae569a6b)) - by @NotHyper-474 in [#5085](https://github.com/FunkinCrew/Funkin/pull/5085)
|
||||
- Songs no longer skip forward at the beginning with high offsets. ([1f75a64](https://github.com/FunkinCrew/Funkin/commit/1f75a641e0c80d15f1af10bae7ee71a6ffecf219)) - by @xenkap in [#3732](https://github.com/FunkinCrew/Funkin/pull/3732)
|
||||
- The song countdown no longer stacks when restarting or continues behind the Pause Menu.
|
||||
([63eca96](https://github.com/FunkinCrew/Funkin/commit/63eca96c98a87e7155df8b2a1735f269ea83e1b5)) - by @KoloInDaCrib and @NotHyper-474 in [#4875](https://github.com/FunkinCrew/Funkin/pull/4875)
|
||||
- Fixed incorrect highlighting and squashed text on Freeplay song capsules. ([0c62428](https://github.com/FunkinCrew/Funkin/commit/0c62428fc883c0fa6d09cd403efb287ff3af8c53)) - by @VioletSnowLeopard in [#5036](https://github.com/FunkinCrew/Funkin/pull/5036)
|
||||
- The Freeplay song preview no longer plays twice after returning from Character
|
||||
Select. ([3d3e2bd](https://github.com/FunkinCrew/Funkin/commit/3d3e2bd3786b858143d214caf55be2ee3e9483fc)) - by @Lasercar in [#5248](https://github.com/FunkinCrew/Funkin/pull/5248)
|
||||
- Freeplay song ranks no longer disappear after changing variations. ([7cc9464](https://github.com/FunkinCrew/Funkin/commit/7cc9464573d07996e4bd0d557f82847809d3a786)) - by @VioletSnowLeopard in [#4583](https://github.com/FunkinCrew/Funkin/pull/4583)
|
||||
- Freeplay song capsules now cycle through long names consistently. ([e193f73](https://github.com/FunkinCrew/Funkin/commit/e193f7392a83a04e4aac85fba4f441a78f7b6668)) - by @VioletSnowLeopard in [#4677](https://github.com/FunkinCrew/Funkin/pull/4677)
|
||||
- Fixed a few visual issues with Freeplay's rank slam animation. ([ab817bb](https://github.com/FunkinCrew/Funkin/commit/ab817bb1eab74ae71c1b1fd74d7512a11e6d4339)) - by @Lasercar in [#4986](https://github.com/FunkinCrew/Funkin/pull/4986)
|
||||
- Fixed visual errors in Freeplay after exiting Character Select. ([56a18e1](https://github.com/FunkinCrew/Funkin/commit/56a18e1cf6a15971feebcc828be8818037948cef)) - by @KoloInDaCrib in [#5245](https://github.com/FunkinCrew/Funkin/pull/5245)
|
||||
- Freeplay styles are now reloaded when hot-reloading with F5. ([f54e140](https://github.com/FunkinCrew/Funkin/commit/f54e140b65e36fcf810c52ce464799dbc0c73c6d)) - by @Keoiki in [#5286](https://github.com/FunkinCrew/Funkin/pull/5286)
|
||||
- Adjusted offsets for Freeplay DJ Pico's fistPump animation. ([382e286](https://github.com/FunkinCrew/funkin.assets/commit/382e286a2478939d3d6aca1c7c90719f33815014)) - by @AbnormalPoof in [funkin.assets#91](https://github.com/FunkinCrew/funkin.assets/pull/91)
|
||||
- The "New Highscore" text no longer appears more than once in the Results screen. ([4e31003](https://github.com/FunkinCrew/Funkin/commit/4e31003a0f60cd394edc2bd4586f9f385b3d07bc)) - by @Lasercar in [#4319](https://github.com/FunkinCrew/Funkin/pull/4319)
|
||||
- The Results Debug menu now shows the correct rank after recent scoring changes. ([11d9998](https://github.com/FunkinCrew/Funkin/commit/11d9998e5c73a3839ea39e06b7f217cbaab69c6d)) - by @NotHyper-474 in [#4905](https://github.com/FunkinCrew/Funkin/pull/4905)
|
||||
- The Input Offsets menu now exits to the Options menu instead of the Main menu. ([5361df2](https://github.com/FunkinCrew/Funkin/commit/5361df254470e68d7571b4534cf456f08d5ffd60)) - by @JackXson-Real in [#5076](https://github.com/FunkinCrew/Funkin/pull/5076)
|
||||
- The Debug Menu can no longer be opened after selecting an item in the Main Menu.
|
||||
([5695bc2](https://github.com/FunkinCrew/Funkin/commit/5695bc20e721f0afd5b97a36167f13e550c12b16)) - by @Lasercar in [#4211](https://github.com/FunkinCrew/Funkin/pull/4211)
|
||||
- The debug cursor is now always hidden when the game starts. ([6222c38](https://github.com/FunkinCrew/Funkin/commit/6222c389e301fa2bb4939697376c4e51e29a9977)) - by @Hundrec in [#4520](https://github.com/FunkinCrew/Funkin/pull/4520)
|
||||
- The Story Mode Weekend 1 level title no longer clips into other level titles below it. ([19d1a8c](https://github.com/FunkinCrew/Funkin/commit/19d1a8c59380009f0d0814c94fd0b4eccb0c80cd)) - by @KoloInDaCrib in [#4348](https://github.com/FunkinCrew/Funkin/pull/4348)
|
||||
- Hold note covers now display properly if a hold note was previously dropped. ([96d1324](https://github.com/FunkinCrew/Funkin/commit/96d1324af140858cb93edd07dc36a969c8ae84c0)) - by @T5mpler in [#5275](https://github.com/FunkinCrew/Funkin/pull/5275)
|
||||
- Girlfriend's and Nene's combo drop animations now play consistently. ([34d5ed1](https://github.com/FunkinCrew/Funkin/commit/34d5ed11695cef7348c13505f13fb1da38b7988c)) - by @VioletSnowLeopard in [#4968](https://github.com/FunkinCrew/Funkin/pull/4968)
|
||||
- Girlfriend (Tankman Stickup) now plays her combo drop animation. ([e329601](https://github.com/FunkinCrew/funkin.assets/commit/e329601834f270910ce80ea5539e4487b9895a8a)) - by @qt2k4 in [funkin.assets#149](https://github.com/FunkinCrew/funkin.assets/pull/149)
|
||||
- Darnell's idle animation now loops consistently. ([df64586](https://github.com/FunkinCrew/funkin.assets/commit/df64586771ea41c544913e049fd0f32bdc655417)) - by @qt2k4 in [funkin.assets#159](https://github.com/FunkinCrew/funkin.assets/pull/159)
|
||||
- Darnell's kneeCan animation now plays properly in 2hot. ([a2e9931](https://github.com/FunkinCrew/funkin.assets/commit/a2e993167aaa2ff6bff8806940f912e444608645)) - by @biomseed in [funkin.assets#78](https://github.com/FunkinCrew/funkin.assets/pull/78)
|
||||
- Otis and Pico (Speaker) no longer spaz out when playtesting Stress. ([3f6d75f](https://github.com/FunkinCrew/funkin.assets/commit/3f6d75f3b6f6c8deb660323e3fb9bf1974c06520)) - by @Lasercar in [funkin.assets#124](https://github.com/FunkinCrew/funkin.assets/pull/124)
|
||||
- The gasp sound now only plays once in the Week 3 Pico Mix doppelganger cutscene. ([ab4598b](https://github.com/FunkinCrew/funkin.assets/commit/ab4598baf3c6790d30cfb727d04c8b57fa18dd0d)) - by @KoloInDaCrib in [funkin.assets#126](https://github.com/FunkinCrew/funkin.assets/pull/126)
|
||||
- Fixed a few issues with the train in Week 3. ([8db2426](https://github.com/FunkinCrew/funkin.assets/commit/8db2426991caebde44d19c81b198e8e2ad86f700)) - by @ShadzXD in [funkin.assets#180](https://github.com/FunkinCrew/funkin.assets/pull/180)
|
||||
- The cars in Week 4 and Weekend 1 no longer get stuck when the song is restarted. ([9c511e3](https://github.com/FunkinCrew/funkin.assets/commit/9c511e371fd08a43ddf834766a6d35667bdef4f7)) - by @MetaBreeze in [funkin.assets#186](https://github.com/FunkinCrew/funkin.assets/pull/186)
|
||||
- A-Bot's visualizer no longer jumps to a random volume when the song ends. ([51cc118](https://github.com/FunkinCrew/funkin.assets/commit/51cc1186bc77a2ee45a47fdbde2d75d9ec69de3a)) - by @VioletSnowLeopard in [funkin.assets#183](https://github.com/FunkinCrew/funkin.assets/pull/183)
|
||||
- Pico's burpShit animation now re-enables volume for player vocals. ([cefda0e](https://github.com/FunkinCrew/funkin.assets/commit/cefda0e52fabbbe04afe60b7aed560267e2cb01e)) - by @Hundrec in [funkin.assets#71](https://github.com/FunkinCrew/funkin.assets/pull/71)
|
||||
- Removed vocals from Monster's instrumental on web builds. ([1c9473f](https://github.com/FunkinCrew/funkin.assets/commit/1c9473f3dfdfb97d97f6c8457001055322abf5ab)) - by @JVNpixels in [funkin.assets#182](https://github.com/FunkinCrew/funkin.assets/pull/182)
|
||||
- Fixed the retry sound not playing after a Tankman death quote finishes. ([e7c4b1b](https://github.com/FunkinCrew/Funkin/commit/e7c4b1ba38ba0739cfe347f6c4763f9811fb95b0)) - by @VioletSnowLeopard in [#4726](https://github.com/FunkinCrew/Funkin/pull/4726)
|
||||
- Darnell (BF Mix)'s alternate instrumental is now properly accessible. ([5abdabf](https://github.com/FunkinCrew/funkin.assets/commit/5abdabf69b39ca4eebd36ae6bbd77fab736d0b86)) - by @Hundrec in [funkin.assets#168](https://github.com/FunkinCrew/funkin.assets/pull/168)
|
||||
- Fixed Newgrounds score submissions for Lit Up and Lit Up (BF Mix). ([183cec6](https://github.com/FunkinCrew/Funkin/commit/183cec62dc1fd3c7f3f634dd3e2400e6ee77b476)) - by @Raltyro in [#4577](https://github.com/FunkinCrew/Funkin/pull/4577)
|
||||
- Inputs are now disabled before Senpai's dialogue appears. ([c43d906](https://github.com/FunkinCrew/funkin.assets/commit/c43d906d19d91d71b9096e65d5e0d3543af8cd31)) - by @anysad in [funkin.assets#165](https://github.com/FunkinCrew/funkin.assets/pull/165)
|
||||
- An easter egg now restarts the song using the correct instrumental. ([e657bc9](https://github.com/FunkinCrew/Funkin/commit/e657bc900bc62cc220276dc171dd47f0a176ac66)) - by @KoloInDaCrib in [#4956](https://github.com/FunkinCrew/Funkin/pull/4956)
|
||||
- Encountering an easter egg during a Chart Editor playtest no longer crashes the game. ([b53b5bd](https://github.com/FunkinCrew/funkin.assets/commit/b53b5bdaecf975555538725a4cdfe71d38565b08)) - by @NotHyper-474 in [funkin.assets#133](https://github.com/FunkinCrew/funkin.assets/pull/133)
|
||||
- Nonexistent characters no longer crash the Chart Editor. ([3bbb4b0](https://github.com/FunkinCrew/Funkin/commit/3bbb4b06c8c1a1885a18d4354fcfa4363a0c6c75)) - by @Lasercar in [#5008](https://github.com/FunkinCrew/Funkin/pull/5008)
|
||||
- Holding Ctrl and clicking on a hold note trail no longer crashes the Chart Editor. ([dc56cca](https://github.com/FunkinCrew/Funkin/commit/dc56ccada50e671996caf0557de1977b7ff8d236)) - by @Lasercar in [#4203](https://github.com/FunkinCrew/Funkin/pull/4203)
|
||||
- Tweens and timers are now canceled when returning to the Chart Editor. ([7e76cf6](https://github.com/FunkinCrew/Funkin/commit/7e76cf66340c00ef6ec84358e6304d62815173b6)) - by @KoloInDaCrib in [#5278](https://github.com/FunkinCrew/Funkin/pull/5278)
|
||||
- Reduced the severity of a memory leak in the Chart Editor. ([cce8c18](https://github.com/FunkinCrew/Funkin/commit/cce8c18822e083910200597f5db4d87b6e3b521f)) - by @NotHyper-474 in [#5247](https://github.com/FunkinCrew/Funkin/pull/5247)
|
||||
- Pressing the Chart Editor keybind during a song now opens to the variation and difficulty you were playing. ([e3fca16](https://github.com/FunkinCrew/Funkin/commit/e3fca167938642bea85398fe57347c10439c1892)) - by @Lasercar in [#4116](https://github.com/FunkinCrew/Funkin/pull/4116)
|
||||
- The Chart Editor now properly saves audio levels when exiting. ([f78ab4d](https://github.com/FunkinCrew/Funkin/commit/f78ab4da1db4f9527a2e1715d5cfb37670e11a74)) - by @Lasercar in [#4149](https://github.com/FunkinCrew/Funkin/pull/4149)
|
||||
- The Chart Editor "Load Metadata File" and "Load Chart File" buttons now function properly. ([9df5395](https://github.com/FunkinCrew/Funkin/commit/9df5395ff888cb6740e21204c8e19116e0472db4)) - by @Lasercar in [#4278](https://github.com/FunkinCrew/Funkin/pull/4278)
|
||||
- FNF Legacy files can now be opened in the Chart Editor on MacOS. ([d98628c](https://github.com/FunkinCrew/Funkin/commit/d98628ca0f9f60357715bd7f95fc686a83201209)) - by @AbnormalPoof in [#4580](https://github.com/FunkinCrew/Funkin/pull/4580)
|
||||
- The Chart Editor now consistently displays the correct waveform for vocal tracks. ([c0e0523](https://github.com/FunkinCrew/Funkin/commit/c0e0523651e8aaaae2a0eed6d5fef6c5ef1b7315)) - by @NotHyper-474 in [#5231](https://github.com/FunkinCrew/Funkin/pull/5231)
|
||||
- Fixed selection boxes duplicating in the Chart Editor. ([65ed583](https://github.com/FunkinCrew/Funkin/commit/65ed58350b798bca0044603510540cfe81b48611)) - by @NotHyper-474 in [#5073](https://github.com/FunkinCrew/Funkin/pull/5073)
|
||||
- Hot-reloading with F5 in the Input Offsets menu no longer crashes the game. ([22372e8](https://github.com/FunkinCrew/Funkin/commit/22372e83886c5ef9821228daa3fb9fe8fbcf0e07)) - by @NotHyper-474 in [#5085](https://github.com/FunkinCrew/Funkin/pull/5085)
|
||||
- The Chart Editor playbar no longer extends past the right of the grid. ([7edc99b](https://github.com/FunkinCrew/Funkin/commit/7edc99b8ba7d1c61eba737560512a07babf5dfe8)) - by @anysad in [#5090](https://github.com/FunkinCrew/Funkin/pull/5090)
|
||||
- Removed a spammy console trace from some Chart Editor events. ([000491c](https://github.com/FunkinCrew/Funkin/commit/000491c3bd3db9de433a4b668b6ceebda2fc6eae)) - by @anysad in [#5097](https://github.com/FunkinCrew/Funkin/pull/5097)
|
||||
- Fixed a crash when mashing D or I during startup. ([790af6d](https://github.com/FunkinCrew/Funkin/commit/790af6d9054a77248ecb7bf34d764186c4fa2243)) - by @CrusherNotDrip in [#5160](https://github.com/FunkinCrew/Funkin/pull/5160)
|
||||
- The Chart Editor now consistently displays the correct waveform for vocal tracks. ([788d0fe](https://github.com/FunkinCrew/Funkin/commit/788d0fe9be5754e4e193de56e4437e8f409564a2)) - by @NotHyper-474 in [#5231](https://github.com/FunkinCrew/Funkin/pull/5231)
|
||||
- Stage Editor windows are now able to be closed. ([990042e](https://github.com/FunkinCrew/Funkin/commit/990042e28f73ad0624dfd38179891163d0c2313a)) - by @Lasercar in [#5238](https://github.com/FunkinCrew/Funkin/pull/5238)
|
||||
- Fixed duplicate exit prompts appearing in the Stage Editor. ([a599d83](https://github.com/FunkinCrew/Funkin/commit/a599d830f08ba697f2b526747280a53e16ebae06)) - by @Lasercar in [#5239](https://github.com/FunkinCrew/Funkin/pull/5239)
|
||||
- Reduced the severity of a memory leak in the Chart Editor. ([6ec3191](https://github.com/FunkinCrew/Funkin/commit/6ec3191b2952533afeded514d582a56f0905afb8)) - by @NotHyper-474 in [#5247](https://github.com/FunkinCrew/Funkin/pull/5247)
|
||||
- The Freeplay song preview no longer plays twice after returning from Character Select. ([f9387c1](https://github.com/FunkinCrew/Funkin/commit/f9387c144a62ecae2ea2b9da7b93ee16693bff02)) - by @Lasercar in [#5248](https://github.com/FunkinCrew/Funkin/pull/5248)
|
||||
- Undoing and redoing hold note length changes now visually updates the trail in the Chart Editor. ([254be57](https://github.com/FunkinCrew/Funkin/commit/254be5798ec0b7b2e79612ae5ade438965e65249)) - by @NotHyper-474 in [#5265](https://github.com/FunkinCrew/Funkin/pull/5265)
|
||||
- Pico's burpShit animation now re-enables volume for player vocals. ([2bf3846](https://github.com/FunkinCrew/funkin.assets/commit/2bf38463c524839e85faec19b7f31e90070e0b87)) - by @Hundrec in [funkin.assets#71](https://github.com/FunkinCrew/funkin.assets/pull/71)
|
||||
- Darnell's kneeCan animation now plays properly in 2hot. ([f8030c4](https://github.com/FunkinCrew/funkin.assets/commit/f8030c441c73d203f7b92c243542f99402faf2a7)) - by @biomseed in [funkin.assets#78](https://github.com/FunkinCrew/funkin.assets/pull/78)
|
||||
- Adjusted offsets for Freeplay DJ Pico's fistPump animation. ([17996db](https://github.com/FunkinCrew/funkin.assets/commit/17996db752c3077e005d4f05286a7795746acb05)) - by @AbnormalPoof in [funkin.assets#91](https://github.com/FunkinCrew/funkin.assets/pull/91)
|
||||
- Chart Editor and Stage Editor windows now consistently show a close button. ([929f61f](https://github.com/FunkinCrew/funkin.assets/commit/929f61fe86bd49d61de27e09503085a04c2cddfb)) - by @Lasercar in [funkin.assets#121](https://github.com/FunkinCrew/funkin.assets/pull/121)
|
||||
- Otis and Pico (Speaker) no longer spaz out when playtesting Stress. ([2fd62d8](https://github.com/FunkinCrew/funkin.assets/commit/2fd62d8f4fdefffc8d4a9cc96b9e993fd0130eb4)) - by @Lasercar in [funkin.assets#124](https://github.com/FunkinCrew/funkin.assets/pull/124)
|
||||
- The gasp sound now only plays once in the Week 3 Pico Mix doppelganger cutscene. ([ecda30d](https://github.com/FunkinCrew/funkin.assets/commit/ecda30d7532df3c34a4b5ad25579cc960cb30c0f)) - by @KoloInDaCrib in [funkin.assets#126](https://github.com/FunkinCrew/funkin.assets/pull/126)
|
||||
- Encountering an easter egg during a Chart Editor playtest no longer crashes the game. ([6cf9fb7](https://github.com/FunkinCrew/funkin.assets/commit/6cf9fb7ba48bb336f63eb940290fa051859330ec)) - by @NotHyper-474 in [funkin.assets#133](https://github.com/FunkinCrew/funkin.assets/pull/133)
|
||||
- Girlfriend (Tankman Stickup) now plays her combo drop animation. ([cb0e506](https://github.com/FunkinCrew/funkin.assets/commit/cb0e506599d387b1d17faee2cf319914eb2dfc80)) - by @qt2k4 in [funkin.assets#149](https://github.com/FunkinCrew/funkin.assets/pull/149)
|
||||
- Changed "Tankman Battlefield (Erect)" to "Tankman Battlefield [Erect]" in the Chart Editor for consistency. ([f9149eb](https://github.com/FunkinCrew/funkin.assets/commit/f9149eb72698ddd275f743080effed08c842644e)) - by @JVN-Pixels in [funkin.assets#155](https://github.com/FunkinCrew/funkin.assets/pull/155)
|
||||
- Fixed Pico (Pixel) having the incorrect name in the Chart Editor. ([a668659](https://github.com/FunkinCrew/funkin.assets/commit/a668659a4bbd2094fde48041c73fd1ebef551288)) - by @ExtraCode75 in [funkin.assets#158](https://github.com/FunkinCrew/funkin.assets/pull/158)
|
||||
- Darnell (BF Mix)'s alternate instrumental is now properly accessible. ([7bf4361](https://github.com/FunkinCrew/funkin.assets/commit/7bf436153081c27fdb354eddb05b72bd87fb18b7)) - by @Hundrec in [funkin.assets#168](https://github.com/FunkinCrew/funkin.assets/pull/168)
|
||||
- Adjusted the font size for Starting BPM in the Chart Editor for consistency. ([801121a](https://github.com/FunkinCrew/funkin.assets/commit/801121a7d31ef3a1ac108bf16941d88009d1b12a)) - by @Lasercar in [funkin.assets#170](https://github.com/FunkinCrew/funkin.assets/pull/170)
|
||||
- Removed vocals from Monster's instrumental on web builds. ([bf7b032](https://github.com/FunkinCrew/funkin.assets/commit/bf7b032154fdc9751fbe8fdf88dbc5fd24f361db)) - by @JVN-Pixels in [funkin.assets#182](https://github.com/FunkinCrew/funkin.assets/pull/182)
|
||||
- A-Bot's visualizer no longer jumps to a random volume when the song ends. ([2904697](https://github.com/FunkinCrew/funkin.assets/commit/2904697b6fda66db4df2b47127f05520fdf831d1)) - by @VioletSnowLeopard in [funkin.assets#183](https://github.com/FunkinCrew/funkin.assets/pull/183)
|
||||
- The cars in Week 4 and Weekend 1 no longer get stuck when the song is restarted. ([ecb1bbd](https://github.com/FunkinCrew/funkin.assets/commit/ecb1bbd8c8ced028b96b2c9b5572d2ea48f36cf4)) - by @MetaBreeze in [funkin.assets#186](https://github.com/FunkinCrew/funkin.assets/pull/186)
|
||||
- Removed a spammy console trace from Spooky Kids (Dark). ([9ee98d6](https://github.com/FunkinCrew/funkin.assets/commit/9ee98d62aa336f1fccbefaf077b4ae3222e00ec4)) - by @NotHyper-474 in [funkin.assets#187](https://github.com/FunkinCrew/funkin.assets/pull/187)
|
||||
- Fixed the Chart Editor timer occasionally displaying incorrect millisecond values. ([26dc895](https://github.com/FunkinCrew/Funkin/commit/26dc895a27e0d7e49469251cd68b83be66384e15)) - by @Hundrec in [#4257](https://github.com/FunkinCrew/Funkin/pull/4257)
|
||||
- The Chart Editor playhead can no longer be scrolled to before the beginning of the song. ([7c7dc11](https://github.com/FunkinCrew/Funkin/commit/7c7dc11f18644882444df97ac927e11adaa4ce50)) - by @Hundrec in [#5024](https://github.com/FunkinCrew/Funkin/pull/5024)
|
||||
- The Chart Editor playbar no longer extends past the right of the grid. ([c7abb19](https://github.com/FunkinCrew/Funkin/commit/c7abb196989476cfa4db6354cc6fac5bacb2e56a)) - by @anysad in [#5090](https://github.com/FunkinCrew/Funkin/pull/5090)
|
||||
- Dragging a hold note in the Chart Editor now drags its trail along with its head. ([d3d8aaa](https://github.com/FunkinCrew/Funkin/commit/d3d8aaae7bfbfa8975a9573807b9a3ca68a1ff55)) - by @KoloInDaCrib in [#4127](https://github.com/FunkinCrew/Funkin/pull/4127)
|
||||
- Hold note trails will no longer disappear when dragged too far in the Chart Editor. ([37dc66b](https://github.com/FunkinCrew/Funkin/commit/37dc66bc189bc1941cf60587e1c068770aeec872)) - by @NotHyper-474 in
|
||||
[#5261](https://github.com/FunkinCrew/Funkin/pull/5261)
|
||||
- Undoing and redoing hold note length changes now visually updates the trail in the Chart Editor. ([06a440f](https://github.com/FunkinCrew/Funkin/commit/06a440f21c285666ce2d2bdf17a91ee82ab01061)) - by @NotHyper-474 in [#5265](https://github.com/FunkinCrew/Funkin/pull/5265)
|
||||
- The Chart Editor hold note context menu now displays the correct options. ([4801316](https://github.com/FunkinCrew/Funkin/commit/48013168ef09ddc09549268a2e5309520d3fcc18)) - by @Lasercar in [#4231](https://github.com/FunkinCrew/Funkin/pull/4231)
|
||||
- The buttons in the Chart Editor context menu for selections now do the right thing. ([62d24fc](https://github.com/FunkinCrew/Funkin/commit/62d24fcf4cbe7be328a995bad04f3eeb265262c5)) - by @Lasercar in [#4233](https://github.com/FunkinCrew/Funkin/pull/4233)
|
||||
- The charter field in the song metadata now properly displays the charter in the Chart Editor. ([894d8cb](https://github.com/FunkinCrew/Funkin/commit/894d8cb4637fd0a64359a5edc805a23403f3045c)) - by @Lasercar in [#4879](https://github.com/FunkinCrew/Funkin/pull/4879)
|
||||
- Chart Editor difficulties are now sorted in a consistent order. ([7aa77a1](https://github.com/FunkinCrew/Funkin/commit/7aa77a11cf7508fc05758045a13b2220aca96dd5)) - by @Lasercar in [#4528](https://github.com/FunkinCrew/Funkin/pull/4528)
|
||||
- Chart Editor and Stage Editor windows now consistently show a close button. ([b23b7b8](https://github.com/FunkinCrew/funkin.assets/commit/b23b7b81d843c8fa2334fbdbe1ef979633956bb9)) - by @Lasercar in [funkin.assets#121](https://github.com/FunkinCrew/funkin.assets/pull/121)
|
||||
- The Chart Editor playbar's font size no longer becomes too small. ([f9c1f7a](https://github.com/FunkinCrew/Funkin/commit/f9c1f7a5f7d3906155c2004ad2ec1d02556b9730)) - by @KoloInDaCrib in [#5253](https://github.com/FunkinCrew/Funkin/pull/5253)
|
||||
- The Chart Editor copy notification no longer chases the mouse cursor. ([0ea42e1](https://github.com/FunkinCrew/Funkin/commit/0ea42e18e93b0b57f1ae5499d70c4681f191dacb)) - by @KoloInDaCrib in [#4029](https://github.com/FunkinCrew/Funkin/pull/4029)
|
||||
- Changed "Tankman Battlefield (Erect)" to "Tankman Battlefield [Erect]" in the Chart Editor for consistency. ([52852a0](https://github.com/FunkinCrew/funkin.assets/commit/52852a02f60df81c281bfd0e49fd8fa09e118409)) - by @JVNpixels in [funkin.assets#155](https://github.com/FunkinCrew/funkin.assets/pull/155)
|
||||
- Fixed Pico (Pixel) having the incorrect name in the Chart Editor. ([3ff0e9c](https://github.com/FunkinCrew/funkin.assets/commit/3ff0e9ca4f8cdc5734c335f2d7d72fa310686d46)) - by @ExtraCode75 in [funkin.assets#158](https://github.com/FunkinCrew/funkin.assets/pull/158)
|
||||
- Fixed various issues and added missing functionalities to the Stage Editor. ([a776ce1](https://github.com/FunkinCrew/Funkin/commit/a776ce1a81a539f75f8bd2220a9768c03d347058)) - by @KoloInDaCrib in [#3974](https://github.com/FunkinCrew/Funkin/pull/3974)
|
||||
- Stage Editor windows are now able to be closed. ([65461d8](https://github.com/FunkinCrew/Funkin/commit/65461d839b3764659aab33f1a35edf64ee514952)) - by @Lasercar in [#5238](https://github.com/FunkinCrew/Funkin/pull/5238)
|
||||
- Fixed duplicate exit prompts appearing in the Stage Editor. ([136a5df](https://github.com/FunkinCrew/Funkin/commit/136a5dfad430b461a909be3a7e89f46c8be1d3b3)) - by @Lasercar in [#5239](https://github.com/FunkinCrew/Funkin/pull/5239)
|
||||
- The help guide in the Stage Editor can no longer be opened multiple times. ([564d679](https://github.com/FunkinCrew/Funkin/commit/564d679f969c500834426ce3cf50507091d69697)) - by @Lasercar in [#4128](https://github.com/FunkinCrew/Funkin/pull/4128)
|
||||
- Removed a spammy console trace from Spooky Kids (Dark). ([5935a61](https://github.com/FunkinCrew/funkin.assets/commit/5935a61dd71f51e6264dcd039f1164cd1ff295ef)) - by @NotHyper-474 in [funkin.assets#187](https://github.com/FunkinCrew/funkin.assets/pull/187)
|
||||
- Removed spammy console traces from `DiscordClient`. ([e89f9f5](https://github.com/FunkinCrew/Funkin/commit/e89f9f50dc6085e3550736459ce9e4fd02c1fc5b)) - by @AbnormalPoof in [#4207](https://github.com/FunkinCrew/Funkin/pull/4207)
|
||||
- Removed a spammy console trace from some Chart Editor events. ([b883ad3](https://github.com/FunkinCrew/Funkin/commit/b883ad3d50b990e605d7b79d042292c7371db5f0)) - by @anysad in [#5097](https://github.com/FunkinCrew/Funkin/pull/5097)
|
||||
- ANSI colors now display in the console on more computers. ([3747b94](https://github.com/FunkinCrew/Funkin/commit/3747b942461c3644fe31f25d2ce847fd74d1b1e0)) - by @AbnormalPoof in [#4676](https://github.com/FunkinCrew/Funkin/pull/4676)
|
||||
- Properly blacklist a certain class from scripts. ([3dc7699](https://github.com/FunkinCrew/Funkin/commit/3dc7699aac737998b637ff9a9f16986a434424be)) - by @charlesisfeline in [#4773](https://github.com/FunkinCrew/Funkin/pull/4773)
|
||||
|
||||
### Removed
|
||||
|
||||
- [MOBILE] The Input Offsets menu has been temporarily removed pending future reworks.
|
||||
- Removed a few non-functional screenshot preferences. ([8e40942](https://github.com/FunkinCrew/Funkin/commit/8e4094227f89ccc8ec7e58be49a5e1ad6911ed12)) - by @Lasercar in [#4895](https://github.com/FunkinCrew/Funkin/pull/4895)
|
||||
- Removed the VSync preference from web builds, where it's non-functional. ([1338834](https://github.com/FunkinCrew/Funkin/commit/1338834519e13326fa21585a236218cd984ed614)) - by @NotHyper-474 in [#5062](https://github.com/FunkinCrew/Funkin/pull/5062)
|
||||
- Removed the VSync preference from web builds, where it's non-functional. ([0b7a94b](https://github.com/FunkinCrew/Funkin/commit/0b7a94b1cc5c5d0dbac5a4c3d595349c4e6eb6e4)) - by @NotHyper-474 in [#5062](https://github.com/FunkinCrew/Funkin/pull/5062)
|
||||
- Removed a few non-functional screenshot preferences. ([93e4f79](https://github.com/FunkinCrew/Funkin/commit/93e4f799f4f0ea435b3d77ca8ef2ab6beeb0a955)) - by @Lasercar in [#4895](https://github.com/FunkinCrew/Funkin/pull/4895)
|
||||
|
||||
## New Contributors for 0.7.0
|
||||
|
||||
* @KutikiPlayz made their first contribution in [#3544](https://github.com/FunkinCrew/Funkin/pull/3544)
|
||||
* @xenkap made their first contribution in [#3732](https://github.com/FunkinCrew/Funkin/pull/3732)
|
||||
* @Raltyro made their first contribution in [#4577](https://github.com/FunkinCrew/Funkin/pull/4577)
|
||||
* @charlesisfeline made their first contribution in [#4773](https://github.com/FunkinCrew/Funkin/pull/4773)
|
||||
* @T5mpler made their first contribution in [#5275](https://github.com/FunkinCrew/Funkin/pull/5275)
|
||||
* @biomseed made their first contribution in [funkin.assets#78](https://github.com/FunkinCrew/funkin.assets/pull/78)
|
||||
* @qt2k4 made their first contribution in [funkin.assets#149](https://github.com/FunkinCrew/funkin.assets/pull/149)
|
||||
* @ExtraCode75 made their first contribution in [funkin.assets#158](https://github.com/FunkinCrew/funkin.assets/pull/158)
|
||||
@@ -253,7 +591,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
* @JackXson-Real made their first contribution in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
|
||||
* @VioletSnowLeopard made their first contribution in [#4382](https://github.com/FunkinCrew/Funkin/pull/4382)
|
||||
* @superpowers04 made their first contribution in [#4729](https://github.com/FunkinCrew/Funkin/pull/4729)
|
||||
* @ShadzXD made their first contribution in [#62](https://github.com/FunkinCrew/Funkin/pull/4729)
|
||||
* @ShadzXD made their first contribution in [funkin.assets#62](https://github.com/FunkinCrew/funkin.assets/pull/62)
|
||||
|
||||
|
||||
|
||||
@@ -526,6 +864,7 @@ This patch resolves a critical issue that could cause user's save data to become
|
||||
|
||||
## [0.5.0] - 2024-09-12
|
||||
The Playable Pico Update!
|
||||
|
||||
### Added
|
||||
|
||||
- Added a new Character Select screen to switch between playable characters in Freeplay.
|
||||
@@ -646,7 +985,7 @@ The Playable Pico Update!
|
||||
### Fixed
|
||||
|
||||
- Control binds in the controls menu no longer overlap their names.
|
||||
- Attempting to exit the gameover screen and retry the song at the same time no longer crashes the game. ([thanks DMMaster636 for the PR!](https://github.com/FunkinCrew/Funkin/pull/2709))
|
||||
- Attempting to exit the gameover screen and retry the song at the same time no longer crashes the game. ([thanks DM-kun for the PR!](https://github.com/FunkinCrew/Funkin/pull/2709))
|
||||
- Botplay mode now handles the player's animations properly during hold notes. ([thanks Hundrec!](https://github.com/FunkinCrew/Funkin/pull/2683))
|
||||
- Camera movement now pauses when the game is paused. ([thanks Matriculaso!](https://github.com/FunkinCrew/Funkin/pull/2684))
|
||||
- Pico's gameplay sprite no longer appears on the gameover screen when dying from an explosion in 2hot.
|
||||
@@ -660,13 +999,14 @@ The Playable Pico Update!
|
||||
## New Contributors for 0.4.1
|
||||
|
||||
* @Hundrec made their first contribution in [#2661](https://github.com/FunkinCrew/Funkin/pull/2661)
|
||||
* @DMMaster636 made their first contribution in [#2709](https://github.com/FunkinCrew/Funkin/pull/2709)
|
||||
* @DM-kun made their first contribution in [#2709](https://github.com/FunkinCrew/Funkin/pull/2709)
|
||||
* @eltociear made their first contribution in [#2730](https://github.com/FunkinCrew/Funkin/pull/2730)
|
||||
|
||||
|
||||
|
||||
## [0.4.0] - 2024-06-06
|
||||
The Pit Stop 1 update!
|
||||
|
||||
### Added
|
||||
|
||||
- 2 new Erect remixes, Eggnog and Satin Panties. Check them out from the Freeplay menu!
|
||||
@@ -838,6 +1178,7 @@ The Pit Stop 1 update!
|
||||
|
||||
## [0.3.0] - 2024-04-30
|
||||
The Weekend 1 update!
|
||||
|
||||
### Added
|
||||
|
||||
- New Story Level: Weekend 1, starring Pico, Darnell, and Nene.
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
# Friday Night Funkin'
|
||||
<div align='center'><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Friday_Night_Funkin%27_logo.svg/2560px-Friday_Night_Funkin%27_logo.svg.png" width="800">
|
||||
|
||||
Friday Night Funkin' is a rhythm game. Built using HaxeFlixel for Ludum Dare 47.
|
||||
<h2>Friday Night Funkin' is a rhythm game. Built using HaxeFlixel for <a href="https://ldjam.com/events/ludum-dare/47">Ludum Dare 47.</a></h2>
|
||||
|
||||
This game was made with love to Newgrounds and its community. Extra love to Tom Fulp.
|
||||
|
||||
</div>
|
||||
|
||||
- [Playable web demo on Newgrounds!](https://www.newgrounds.com/portal/view/770371)
|
||||
- [Demo download builds for Windows, Mac, and Linux from Itch.io!](https://ninja-muffin24.itch.io/funkin)
|
||||
- [Download Android builds from Google Play!](https://play.google.com/store/apps/details?id=me.funkin.fnf)
|
||||
- [Download iOS builds from the App Store!](https://apps.apple.com/app/id6740428530)
|
||||
|
||||
<div align='center'>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://fridaynightfunkin.wiki.gg/images/d/d7/Title_Card.gif" alt="Title Screen" width="350"/></td>
|
||||
<td><img src="https://fridaynightfunkin.wiki.gg/images/9/99/Menu.png" alt="Main Menu" width="350"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
# Getting Started
|
||||
|
||||
**PLEASE USE THE LINKS ABOVE IF YOU JUST WANT TO PLAY THE GAME**
|
||||
@@ -52,4 +63,4 @@ Full credits can be found in-game, or in the `credits.json` file which is locate
|
||||
## Special Thanks
|
||||
- [Tom Fulp](https://twitter.com/tomfulp) - For being a great guy and for Newgrounds
|
||||
- [JohnnyUtah](https://twitter.com/JohnnyUtahNG/) - Voice of Tankman
|
||||
- [L0Litsmonica](https://twitter.com/L0Litsmonica) - Voice of Mommy Mearest
|
||||
- [L0Litsmonica](https://twitter.com/L0Litsmonica) - Voice of Mommy Mearest
|
||||
+1
-1
Submodule art updated: 67e550dbd2...094ff10919
+1
-1
Submodule assets updated: 414e9a0248...e6f463fb64
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"input": "./assets",
|
||||
"output": "./astc-textures/",
|
||||
"quality": "medium",
|
||||
"blocksize": "10x10",
|
||||
"colorprofile": "cl",
|
||||
"custom": [
|
||||
{
|
||||
"asset": "asset/shared/images/resultScreen/results-pico/resultsPERFECT/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "asset/shared/images/resultScreen/results-pico/resultsGREAT/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "asset/shared/images/resultScreen/results-pico/resultsGOOD/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "asset/shared/images/resultScreen/results-pico/resultsSHIT/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/preload/images/NOTE_assets.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/preload/images/NOTE_hold_assets.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/preload/images/StrumlineNotes.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/preload/images/alphabet.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/NOTE_hold_assets.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/holdCoverBlue.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/holdCoverGreen.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/holdCoverPurple.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/holdCoverRed.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/latencyArrow.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/latencyReceptor.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/noteSplashes.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/noteStrumline.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/notes.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/week1/image/erect/bg.png",
|
||||
"blocksize": "8x8"
|
||||
},
|
||||
{
|
||||
"asset": "assets/week1/image/erect/lights.png",
|
||||
"blocksize": "6x6"
|
||||
},
|
||||
{
|
||||
"asset": "assets/week1/images/erect/crowd.png",
|
||||
"blocksize": "8x8"
|
||||
},
|
||||
{
|
||||
"asset": "assets/week1/images/erect/server.png",
|
||||
"blocksize": "8x8"
|
||||
}
|
||||
],
|
||||
"excludes": [
|
||||
"assets/preload/images/cursor/",
|
||||
"assets/preload/images/fonts/",
|
||||
"assets/preload/images/freeplay/",
|
||||
"assets/preload/images/icons/",
|
||||
"assets/preload/images/soundtray/",
|
||||
"assets/preload/images/stageBuild/",
|
||||
"assets/preload/images/titleEnter.png",
|
||||
"assets/preload/images/titleEnter_mobile.png",
|
||||
"assets/preload/images/ui/popup/pixel/*",
|
||||
"assets/shared/images/characters/abotPixel/*",
|
||||
"assets/shared/images/characters/bfPixel.png",
|
||||
"assets/shared/images/characters/bfPixelsDEAD.png",
|
||||
"assets/shared/images/characters/gfPixel.png",
|
||||
"assets/shared/images/characters/nenePixel/*",
|
||||
"assets/shared/images/characters/picoPixel/*",
|
||||
"assets/shared/images/characters/senpai.png",
|
||||
"assets/shared/images/characters/spirit.png",
|
||||
"assets/shared/images/healthBar.png",
|
||||
"assets/shared/images/resultScreen/*",
|
||||
"assets/shared/images/resultScreen/clearPercent/",
|
||||
"assets/shared/images/resultScreen/rankText/",
|
||||
"assets/shared/images/ui/chart-editor/",
|
||||
"assets/shared/images/ui/countdown/pixel/*",
|
||||
"assets/week1/*",
|
||||
"assets/week1/images/erect/brightLightSmall.png",
|
||||
"assets/week1/images/erect/lightAbove.png",
|
||||
"assets/week1/images/erect/lightgreen.png",
|
||||
"assets/week1/images/erect/lightred.png",
|
||||
"assets/week1/images/erect/orangeLight.png",
|
||||
"assets/week2/images/erect/stairsDark.png",
|
||||
"assets/week2/images/erect/stairsLight.png",
|
||||
"assets/week6/"
|
||||
]
|
||||
}
|
||||
+1
-1
@@ -80,7 +80,7 @@
|
||||
{
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$",
|
||||
"format": "^?:_[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$",
|
||||
"tokens": ["INLINE", "NOTINLINE"]
|
||||
},
|
||||
"type": "ConstantName"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
assets/preload/images/cursor/*
|
||||
assets/preload/images/freeplay/*
|
||||
assets/preload/images/icons/*
|
||||
assets/preload/images/titleEnter.png
|
||||
assets/preload/images/titleEnter_mobile.png
|
||||
assets/preload/images/soundtray/*
|
||||
assets/preload/images/stageBuild/*
|
||||
assets/preload/images/ui/popup/pixel/
|
||||
assets/shared/images/characters/abotPixel/
|
||||
assets/shared/images/characters/bfPixel.png
|
||||
assets/shared/images/characters/bfPixelsDEAD.png
|
||||
assets/shared/images/characters/gfPixel.png
|
||||
assets/shared/images/characters/nenePixel/
|
||||
assets/shared/images/characters/picoPixel/
|
||||
assets/shared/images/characters/senpai.png
|
||||
assets/shared/images/characters/spirit.png
|
||||
assets/shared/images/resultScreen/*
|
||||
assets/shared/images/ui/chart-editor/*
|
||||
assets/shared/images/ui/countdown/pixel/
|
||||
assets/week1/
|
||||
assets/week6/*
|
||||
+7
-10
@@ -60,7 +60,7 @@ This section provides guidelines to follow when [opening an issue](https://githu
|
||||
|
||||
## Requirements
|
||||
Make sure you're playing:
|
||||
- the latest version of the game (currently v0.6.4)
|
||||
- the latest version of the game (currently v0.7.4)
|
||||
- without any mods
|
||||
- on [Newgrounds](https://www.newgrounds.com/portal/view/770371) or downloaded from [itch.io](https://ninja-muffin24.itch.io/funkin)
|
||||
|
||||
@@ -122,9 +122,6 @@ Also only report one issue or enhancement at a time! If you have multiple bug re
|
||||
|
||||
Once you're sure your issue is unique and specific, feel free to submit it.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **DO NOT CLOSE YOUR ISSUE FOR ANY REASON!** Your issue will be taken care of by a moderator!
|
||||
|
||||
**Thank you for opening issues!**
|
||||
|
||||
# Part 3: Pull Requests
|
||||
@@ -132,20 +129,20 @@ Community members are welcome to contribute their changes by [opening pull reque
|
||||
This section covers guidelines for opening and managing pull requests (PRs).
|
||||
|
||||
## Choosing a base branch
|
||||
When creating a branch in your fork, base your branch on either the `main` or `develop` branch depending on the types of changes you want to make.
|
||||
When creating a branch in your fork, base your branch on either the `develop` or `main` branch depending on the types of changes you want to make.
|
||||
|
||||
> [!CAUTION]
|
||||
> Avoid using your fork's default branch (`main` in this case) for your PR. This is considered an [anti-pattern](https://jmeridth.com/posts/do-not-issue-pull-requests-from-your-master-branch/) by GitHub themselves!
|
||||
> Instead, make a separate branch for your additions (ex. `docs/fix-typo` or `minor-bugfix`).
|
||||
|
||||
Choose the `main` branch if you modify:
|
||||
- Documentation (`.md` files)
|
||||
- GitHub files (`.yml` files or anything in the `.github` folder)
|
||||
|
||||
Choose the `develop` branch if you modify:
|
||||
- Game code (`.hx` files)
|
||||
- Any other type of file
|
||||
|
||||
Choose the `main` branch if you modify:
|
||||
- Documentation (`.md` files)
|
||||
- GitHub files (`.yml` files or anything in the `.github` folder)
|
||||
|
||||
> [!TIP]
|
||||
> When in doubt, base your branch on the `develop` branch.
|
||||
|
||||
@@ -399,7 +396,7 @@ If you only modify files in the `assets` folder, open a PR in the `funkin.assets
|
||||
|
||||
If you simultaneously modify files from both repositories, then open two separate PRs and explain the connection in your PR descriptions.
|
||||
|
||||
Be sure to choose `main` as the base branch for `funkin.assets` PRs, as no `develop` branch exists for that repository.
|
||||
Be sure to choose `develop` as the base branch for `funkin.assets` PRs, just as you would in the `Funkin` repository.
|
||||
|
||||
### Charting PRs
|
||||
Charting PRs make changes such as **adjusting chart metadata**.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"name": "EliteMasterEric"
|
||||
}
|
||||
],
|
||||
"api_version": "0.5.0",
|
||||
"api_version": "0.7.0",
|
||||
"mod_version": "1.0.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"name": "EliteMasterEric"
|
||||
}
|
||||
],
|
||||
"api_version": "0.5.0",
|
||||
"api_version": "0.7.0",
|
||||
"mod_version": "1.0.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
||||
@@ -11,42 +11,42 @@
|
||||
"name": "astc-compressor",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "8c9f56927c523df7b849352c6951f04112fe15cc",
|
||||
"ref": "b7a91b3072dc16e785a9bde847f17ba0ef15dd47",
|
||||
"url": "https://github.com/KarimAkra/astc-compressor"
|
||||
},
|
||||
{
|
||||
"name": "extension-admob",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "b0fe062c5eacd7cde80fd21544fe27616e5b4f9e",
|
||||
"ref": "02334589ff9603a5f483077a44395009644f6274",
|
||||
"url": "https://github.com/FunkinCrew/extension-admob"
|
||||
},
|
||||
{
|
||||
"name": "extension-androidtools",
|
||||
"type": "haxelib",
|
||||
"version": "2.2.1"
|
||||
"version": "2.2.2"
|
||||
},
|
||||
{
|
||||
"name": "extension-haptics",
|
||||
"type": "haxelib",
|
||||
"version": "1.0.3"
|
||||
"version": "1.0.4"
|
||||
},
|
||||
{
|
||||
"name": "extension-iapcore",
|
||||
"type": "haxelib",
|
||||
"version": "1.0.3"
|
||||
"version": "1.0.4"
|
||||
},
|
||||
{
|
||||
"name": "extension-iarcore",
|
||||
"type": "haxelib",
|
||||
"version": "1.0.2"
|
||||
"version": "1.0.3"
|
||||
},
|
||||
{
|
||||
"name": "flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "16962f601431d72ed104ca700d1e99ad4dcfffca",
|
||||
"url": "https://github.com/FunkinCrew/flixel-mobile"
|
||||
"ref": "ac2a34fe4b3400bc04218f46320a65af0f337fc0",
|
||||
"url": "https://github.com/FunkinCrew/flixel"
|
||||
},
|
||||
{
|
||||
"name": "flixel-addons",
|
||||
@@ -59,7 +59,7 @@
|
||||
"name": "flxanimate",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "39c1572add28869c558b218fffed13df1b64f376",
|
||||
"ref": "49214278b9124823582cdcecd94f4a1de9a4b36b",
|
||||
"url": "https://github.com/FunkinCrew/flxanimate"
|
||||
},
|
||||
{
|
||||
@@ -104,14 +104,14 @@
|
||||
"name": "hscript",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "27c86f9a761c1d16d4433c4cf252eccb7b2e18de",
|
||||
"ref": "8c1d238b069ef97cec13f5121b29d2afe2b8985d",
|
||||
"url": "https://github.com/FunkinCrew/hscript"
|
||||
},
|
||||
{
|
||||
"name": "hxcpp",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "4e24283a047f11bded6affabbc9ec405156e026e",
|
||||
"ref": "5a0dc3f644dc676a4a092b7e6c8edc8be941f024",
|
||||
"url": "https://github.com/FunkinCrew/hxcpp"
|
||||
},
|
||||
{
|
||||
@@ -170,36 +170,8 @@
|
||||
"name": "lime",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "2d3784fe3fdbd81ce59d6ad035f52e12c59cf059",
|
||||
"url": "https://github.com/FunkinCrew/lime-mobile"
|
||||
},
|
||||
{
|
||||
"name": "mconsole",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "06c0499ed8f80628a0e6e55ffa32c3cbd688a838",
|
||||
"url": "https://github.com/massive-oss/mconsole"
|
||||
},
|
||||
{
|
||||
"name": "mcover",
|
||||
"type": "git",
|
||||
"dir": "src",
|
||||
"ref": "c3c47cd682b0b202a41caee95321989391b617ef",
|
||||
"url": "https://github.com/massive-oss/mcover"
|
||||
},
|
||||
{
|
||||
"name": "mockatoo",
|
||||
"type": "git",
|
||||
"dir": "src",
|
||||
"ref": "13d77a0a8eaf5e789ef5dae6cd33eee812deda36",
|
||||
"url": "https://github.com/FunkinCrew/mockatoo"
|
||||
},
|
||||
{
|
||||
"name": "munit",
|
||||
"type": "git",
|
||||
"dir": "src",
|
||||
"ref": "f61be7f7ba796595f45023ca65164a485aba0e7e",
|
||||
"url": "https://github.com/FunkinCrew/MassiveUnit"
|
||||
"ref": "e5f8c27124598505917a001588b560244731adfb",
|
||||
"url": "https://github.com/FunkinCrew/lime"
|
||||
},
|
||||
{
|
||||
"name": "newgrounds",
|
||||
@@ -212,21 +184,21 @@
|
||||
"name": "openfl",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "db060809f0ac293fb873964d7a076762c85adb71",
|
||||
"url": "https://github.com/FunkinCrew/openfl-mobile"
|
||||
"ref": "a0df7c3afe360c9af59a76e45007dbf4e53b5131",
|
||||
"url": "https://github.com/FunkinCrew/openfl"
|
||||
},
|
||||
{
|
||||
"name": "polymod",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "6a3f3793c29ec2922630854ba5186f6f1d73d25a",
|
||||
"ref": "55ee00236806330c89c3bff1e1563eb6425a9d96",
|
||||
"url": "https://github.com/larsiusprime/polymod"
|
||||
},
|
||||
{
|
||||
"name": "thx.core",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "76d87418fadd92eb8e1b61f004cff27d656e53dd",
|
||||
"ref": "2bf2b992e06159510f595554e6b952e47922f128",
|
||||
"url": "https://github.com/fponticelli/thx.core"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Organizes astc-compression-data alphabetically because i hate it when it's not organized -zack
|
||||
|
||||
$InputFile = "./astc-compression-data.json"
|
||||
$OutputFile = "./astc-compression-data.json"
|
||||
|
||||
if (-not (Test-Path $InputFile)) {
|
||||
Write-Host "❌ File $InputFile not found"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Get-Command jq -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "jq is not installed. Download from https://jqlang.github.io/jq/ or install via:"
|
||||
Write-Host " winget install jqlang.jq"
|
||||
exit 1
|
||||
}
|
||||
|
||||
jq '.
|
||||
| .custom = ( .custom | sort_by(.asset) )
|
||||
| .excludes = ( .excludes | sort )
|
||||
' $InputFile | Out-File -Encoding utf8 $OutputFile
|
||||
|
||||
Write-Host "✅ Sorted JSON written to $OutputFile WOOHOOO !!"
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# Organizes astc-compression-data alphabetically because i hate it when it's not organized -zack
|
||||
|
||||
INPUT_FILE="./astc-compression-data.json"
|
||||
OUTPUT_FILE="./astc-compression-data.json"
|
||||
|
||||
# Check jq
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "jq is not installed. Install it with:"
|
||||
echo " macOS: brew install jq"
|
||||
echo " Linux (Debian/Ubuntu): sudo apt-get install jq"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$INPUT_FILE" ]; then
|
||||
echo "❌ File $INPUT_FILE not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jq '.
|
||||
| .custom = ( .custom | sort_by(.asset) )
|
||||
| .excludes = ( .excludes | sort )
|
||||
' "$INPUT_FILE" > "$OUTPUT_FILE".tmp && mv "$OUTPUT_FILE".tmp "$OUTPUT_FILE"
|
||||
|
||||
echo "✅ Sorted JSON written to $OUTPUT_FILE WOOHOOO !!"
|
||||
+247
-116
@@ -5,8 +5,10 @@ import hxp.*;
|
||||
import lime.tools.*;
|
||||
import sys.FileSystem;
|
||||
import sys.io.File;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.Path;
|
||||
import haxe.ds.Map;
|
||||
import haxe.xml.Printer;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -29,13 +31,13 @@ class Project extends HXProject
|
||||
* REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES!
|
||||
* You only have to change it here, the rest of the game will query this value.
|
||||
*/
|
||||
static final VERSION:String = "0.7.0";
|
||||
static final VERSION:String = "0.7.4";
|
||||
|
||||
/**
|
||||
* The build's number and version code.
|
||||
* Used when publishing the game to mobile app stores. Should increment with each patch.
|
||||
*/
|
||||
static final BUILD_NUMBER:Int = 2;
|
||||
static final BUILD_NUMBER:Int = 53;
|
||||
|
||||
/**
|
||||
* The game's name. Used as the default window title.
|
||||
@@ -58,6 +60,11 @@ class Project extends HXProject
|
||||
*/
|
||||
static final SOURCE_DIR:String = "source";
|
||||
|
||||
/**
|
||||
* The relative location of the templates folder.
|
||||
*/
|
||||
static final TEMPLATES_DIR:String = "templates";
|
||||
|
||||
/**
|
||||
* The fully qualified class path for the game's preloader.
|
||||
* Particularly important on HTML5 but we use it on all platforms.
|
||||
@@ -101,30 +108,13 @@ class Project extends HXProject
|
||||
*/
|
||||
static final ANDROID_TARGET_SDK_VERSION:Int = 35;
|
||||
|
||||
//
|
||||
// ASTC COMPRESSION
|
||||
//
|
||||
|
||||
/**
|
||||
* The quality of the compression process.
|
||||
* This doesn't exactly reduce the quality of the outputted texture..
|
||||
* But it changes how hard the CPU gotta work to maintin the quality and details of the image while compressing.
|
||||
* possible values: fastest, fast, medium, thorough, exhaustive.
|
||||
*/
|
||||
static var ASTC_QUALITY:String = "medium";
|
||||
static final ANDROID_EXTENSIONS:Array<String> = ["funkin.extensions.CallbackUtil"];
|
||||
|
||||
/**
|
||||
* The team ID to use for the iOS app. Configured in XCode.
|
||||
*/
|
||||
static var IOS_TEAM_ID:String = "Z7G7AVNGSH";
|
||||
|
||||
/**
|
||||
* The block size of the ASTC compressed textures.
|
||||
* Higher block size means lower quality and lighter file size for the generated compressed textures.
|
||||
* possible block sizes: 4x4, 4x6, 6x6, 8x8 ... 12x12.
|
||||
*/
|
||||
static var ASTC_BLOCKSIZE:String = "10x10";
|
||||
|
||||
/**
|
||||
* A list of asset file globs to exclude from ASTC compression when creating optimized mobile builds.
|
||||
*/
|
||||
@@ -328,10 +318,10 @@ class Project extends HXProject
|
||||
static final FEATURE_HAPTICS:FeatureFlag = "FEATURE_HAPTICS";
|
||||
|
||||
/**
|
||||
* `-DFEATURE_INPUT_OFFSETS`
|
||||
* `-DFEATURE_LAG_ADJUSTMENT`
|
||||
* If this flag is enabled, the input offsets menu will be available to configure your audio and visual offsets.
|
||||
*/
|
||||
static final FEATURE_INPUT_OFFSETS:FeatureFlag = "FEATURE_INPUT_OFFSETS";
|
||||
static final FEATURE_LAG_ADJUSTMENT:FeatureFlag = "FEATURE_LAG_ADJUSTMENT";
|
||||
|
||||
/**
|
||||
* `-DFEATURE_LOG_TRACE`
|
||||
@@ -485,9 +475,11 @@ class Project extends HXProject
|
||||
envConfig = readEnvironmentFile("./.env");
|
||||
|
||||
flair();
|
||||
|
||||
configureApp();
|
||||
|
||||
displayTarget();
|
||||
|
||||
configureFeatureFlags();
|
||||
configureCompileDefines();
|
||||
configureIncludeMacros();
|
||||
@@ -495,14 +487,12 @@ class Project extends HXProject
|
||||
configureOutputDir();
|
||||
configurePolymod();
|
||||
configureHaxelibs();
|
||||
configureASTCTextures();
|
||||
configureAssets();
|
||||
configureIcons();
|
||||
|
||||
readASTCExclusion();
|
||||
|
||||
if (FEATURE_COMPRESSED_TEXTURES.isEnabled(this))
|
||||
if (!isLinux())
|
||||
{
|
||||
runASTCCompressor();
|
||||
configureIcons();
|
||||
}
|
||||
|
||||
if (FEATURE_MOBILE_ADVERTISEMENTS.isEnabled(this))
|
||||
@@ -510,26 +500,15 @@ class Project extends HXProject
|
||||
configureAdMobKeys();
|
||||
}
|
||||
|
||||
// To sign in ios, use -xcode.
|
||||
if (isAndroid())
|
||||
{
|
||||
if (isRelease())
|
||||
{
|
||||
configureAndroidRelease();
|
||||
}
|
||||
configureAndroid();
|
||||
}
|
||||
|
||||
if (isIOS())
|
||||
{
|
||||
config.set("ios.non-exempt-encryption", "false");
|
||||
config.set("ios.team-id", IOS_TEAM_ID);
|
||||
config.set("ios.deployment", "16.0");
|
||||
|
||||
// launchStoryboard = new LaunchStoryboard();
|
||||
// launchStoryboard.path = "./LaunchScreen.storyboard";
|
||||
configureIOS();
|
||||
}
|
||||
|
||||
info("Done configuring project.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,10 +517,9 @@ class Project extends HXProject
|
||||
function flair()
|
||||
{
|
||||
// TODO: Implement this.
|
||||
info("Friday Night Funkin'");
|
||||
info("Friday Night Funkin' - " + VERSION);
|
||||
info("Initializing build...");
|
||||
|
||||
info("Target Version: " + VERSION);
|
||||
info("Git Branch: " + getGitBranch());
|
||||
info("Git Commit: " + getGitCommit());
|
||||
info("Git Modified? " + getGitModified());
|
||||
@@ -555,6 +533,11 @@ class Project extends HXProject
|
||||
function configureApp()
|
||||
{
|
||||
this.meta.title = TITLE;
|
||||
|
||||
// ios uses this character code instead of spaces...
|
||||
if (isIOS())
|
||||
this.meta.title = ~/ /ig.replace(TITLE, "\u2002");
|
||||
|
||||
this.meta.version = VERSION;
|
||||
this.meta.packageName = PACKAGE_NAME;
|
||||
this.meta.company = COMPANY;
|
||||
@@ -567,6 +550,9 @@ class Project extends HXProject
|
||||
// If for some reason we have multiple source directories, we can add more entries here.
|
||||
this.sources.push(SOURCE_DIR);
|
||||
|
||||
// Templates stuff
|
||||
this.templatePaths.push(TEMPLATES_DIR);
|
||||
|
||||
// Tell Lime to run some prebuild and postbuild scripts.
|
||||
this.preBuildCallbacks.push(buildHaxeCLICommand(PREBUILD_HX));
|
||||
this.postBuildCallbacks.push(buildHaxeCLICommand(POSTBUILD_HX));
|
||||
@@ -830,7 +816,7 @@ class Project extends HXProject
|
||||
FEATURE_STAGE_EDITOR.apply(this, !(isWeb() || isMobile()));
|
||||
|
||||
// Should be true except on web builds (some asset stuff breaks it)
|
||||
FEATURE_INPUT_OFFSETS.apply(this, !isWeb());
|
||||
FEATURE_LAG_ADJUSTMENT.apply(this, !isWeb());
|
||||
|
||||
// Should be true except on web and mobile builds.
|
||||
// Screenshots doesn't work there, and mobile has its own screenshots anyway.
|
||||
@@ -861,6 +847,79 @@ class Project extends HXProject
|
||||
// Since mobile is pretty memory safe (I'm looking at you Apple...) we need them BADLY to have the game run properly.
|
||||
// It's kept off for desktop due to the low ASTC support on desktop GPUs (which seem to be only among Intergrated Graphics).
|
||||
FEATURE_COMPRESSED_TEXTURES.apply(this, isMobile() && isRelease());
|
||||
|
||||
renderFlagsTable();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a cute little table of which feature flags are enabled and which are disabled
|
||||
*/
|
||||
function renderFlagsTable():Void
|
||||
{
|
||||
var enabledWidth:Int = 0;
|
||||
var disabledWidth:Int = 0;
|
||||
var enabledFlags:Array<String> = [];
|
||||
var disabledFlags:Array<String> = [];
|
||||
var unicodeCheck:String = "✓ ";
|
||||
var unicodeCross:String = "× ";
|
||||
|
||||
if (isWindowsHost())
|
||||
{
|
||||
#if (!target.unicode)
|
||||
// lime build tools uses neko (unless compiling with `-eval`), and neko on windows doesn't support UTF8/unicode characters
|
||||
// so we need to remove our cute emoji!
|
||||
unicodeCheck = "o ";
|
||||
unicodeCross = "x ";
|
||||
#end
|
||||
}
|
||||
|
||||
for (flagStr in this.haxedefs.keys())
|
||||
{
|
||||
if (!flagStr.startsWith(FeatureFlag.INVERSE_PREFIX))
|
||||
{
|
||||
// we do flagStr.length + 3 to accomodate for adding the "✓ " at the beginning
|
||||
enabledWidth = Std.int(Math.max(flagStr.length + 3, enabledWidth));
|
||||
enabledFlags.push(unicodeCheck + flagStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
disabledWidth = Std.int(Math.max(flagStr.length, disabledWidth));
|
||||
disabledFlags.push(unicodeCross + flagStr.substring(FeatureFlag.INVERSE_PREFIX.length));
|
||||
}
|
||||
}
|
||||
|
||||
enabledFlags.sort(function(a, b) return a > b ? 1 : -1);
|
||||
disabledFlags.sort(function(a, b) return a > b ? 1 : -1);
|
||||
|
||||
|
||||
var horizontalDivider:String = "".rpad("-", enabledWidth + disabledWidth);
|
||||
|
||||
info(horizontalDivider);
|
||||
// Header
|
||||
info("FEATURE FLAGS");
|
||||
info('Enabled Flags $unicodeCheck'.rpad(" ", enabledWidth + 5) + 'Disabled Flags $unicodeCross');
|
||||
info(horizontalDivider);
|
||||
|
||||
|
||||
while (enabledFlags.length > 0 || disabledFlags.length > 0)
|
||||
{
|
||||
var outputLine:String = "";
|
||||
|
||||
if (enabledFlags.length > 0)
|
||||
outputLine += enabledFlags.shift();
|
||||
|
||||
outputLine = outputLine.rpad(" ", enabledWidth + 5);
|
||||
outputLine += "| ";
|
||||
|
||||
if (disabledFlags.length > 0)
|
||||
outputLine += disabledFlags.shift() ?? "";
|
||||
|
||||
info(outputLine);
|
||||
}
|
||||
|
||||
info(horizontalDivider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -889,6 +948,11 @@ class Project extends HXProject
|
||||
setHaxedef("FLX_NO_DEBUG");
|
||||
}
|
||||
|
||||
if (FEATURE_LOG_TRACE.isDisabled(this))
|
||||
{
|
||||
setHaxedef("no-traces");
|
||||
}
|
||||
|
||||
// Disable the built in pause screen when unfocusing the game.
|
||||
setHaxedef("FLX_NO_FOCUS_LOST_SCREEN");
|
||||
|
||||
@@ -969,48 +1033,82 @@ class Project extends HXProject
|
||||
// we use a dedicated 'tracy' folder, since it generally needs a recompile when in use
|
||||
if (FEATURE_DEBUG_TRACY.isEnabled(this)) buildDir += "-tracy";
|
||||
|
||||
info('Output directory: $buildDir');
|
||||
|
||||
// trailing slash might not be needed, works fine on macOS without it, but I haven't tested on Windows!
|
||||
buildDir += "/";
|
||||
|
||||
info('Output directory: $buildDir');
|
||||
// setenv('BUILD_DIR', buildDir);
|
||||
app.path = buildDir;
|
||||
}
|
||||
|
||||
function configureAndroidRelease()
|
||||
function configureAndroid()
|
||||
{
|
||||
if (envConfig != null)
|
||||
javaPaths.push(Path.join([SOURCE_DIR, 'funkin/external/android/java']));
|
||||
|
||||
if (isRelease())
|
||||
{
|
||||
keystore = new Keystore();
|
||||
keystore.path = Std.string(envConfig.get('ANDROID_KEYSTORE_PATH'));
|
||||
keystore.alias = Std.string(envConfig.get('ANDROID_KEYSTORE_ALIAS'));
|
||||
keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD'));
|
||||
if (envConfig != null)
|
||||
{
|
||||
keystore = new Keystore();
|
||||
keystore.path = Std.string(envConfig.get('ANDROID_KEYSTORE_PATH'));
|
||||
keystore.alias = Std.string(envConfig.get('ANDROID_KEYSTORE_ALIAS'));
|
||||
keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD'));
|
||||
}
|
||||
|
||||
if (isBundle())
|
||||
{
|
||||
config.set("android.gradle-project-directory", "bin-bundle");
|
||||
}
|
||||
}
|
||||
|
||||
Reflect.setField(config.get('android.manifest'), 'xmlns:tools', 'http://schemas.android.com/tools');
|
||||
final modsFolderProvider:Xml = Xml.createElement('provider');
|
||||
modsFolderProvider.set('android:authorities', '$PACKAGE_NAME.docprovider');
|
||||
modsFolderProvider.set('android:name', 'funkin.provider.DataFolderProvider');
|
||||
modsFolderProvider.set('android:grantUriPermissions', 'true');
|
||||
modsFolderProvider.set('android:exported', 'true');
|
||||
modsFolderProvider.set('android:permission', 'android.permission.MANAGE_DOCUMENTS');
|
||||
|
||||
final permissionsConfig:Array<String> = [
|
||||
'<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />',
|
||||
'<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" />'
|
||||
];
|
||||
final intentFilter:Xml = Xml.createElement('intent-filter');
|
||||
|
||||
final xmlChildren:Null<Array<String>> = config.get('android.manifest').xmlChildren;
|
||||
final action:Xml = Xml.createElement('action');
|
||||
action.set('android:name', 'android.content.action.DOCUMENTS_PROVIDER');
|
||||
intentFilter.addChild(action);
|
||||
|
||||
if (xmlChildren != null)
|
||||
modsFolderProvider.addChild(intentFilter);
|
||||
|
||||
@:nullSafety(Off)
|
||||
{
|
||||
config.get('android.manifest').xmlChildren = xmlChildren.concat(permissionsConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.get('android.manifest').xmlChildren = permissionsConfig;
|
||||
if (config.get('android.application').xmlChildren != null)
|
||||
{
|
||||
config.get('android.application').xmlChildren.push(Printer.print(modsFolderProvider));
|
||||
}
|
||||
else
|
||||
{
|
||||
config.get('android.application').xmlChildren = [Printer.print(modsFolderProvider)];
|
||||
}
|
||||
}
|
||||
|
||||
if (isBundle())
|
||||
final extensions:Null<Array<String>> = config.getArrayString('android.extension');
|
||||
|
||||
for (extension in ANDROID_EXTENSIONS)
|
||||
{
|
||||
config.set("android.gradle-project-directory", "bin-bundle");
|
||||
if (extensions == null || extensions != null && extensions.indexOf(extension) == -1)
|
||||
{
|
||||
config.push("android.extension", extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function configureIOS()
|
||||
{
|
||||
config.set("ios.non-exempt-encryption", "false");
|
||||
config.set("ios.team-id", IOS_TEAM_ID);
|
||||
config.set("ios.deployment", "16.0");
|
||||
|
||||
// launchStoryboard = new LaunchStoryboard();
|
||||
// launchStoryboard.path = "./LaunchScreen.storyboard";
|
||||
}
|
||||
|
||||
function configurePolymod()
|
||||
{
|
||||
// The file extension to use for script files.
|
||||
@@ -1190,7 +1288,14 @@ class Project extends HXProject
|
||||
addAsset("LICENSE.md", "LICENSE.md", "art", shouldEmbedArt);
|
||||
addAsset("CHANGELOG.md", "CHANGELOG.md", "art", shouldEmbedArt);
|
||||
|
||||
info('Done configuring assets.');
|
||||
if (shouldEmbed)
|
||||
{
|
||||
info('Done embedding assets.');
|
||||
}
|
||||
else
|
||||
{
|
||||
info('Done including assets.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1203,27 +1308,7 @@ class Project extends HXProject
|
||||
if (isAndroid())
|
||||
{
|
||||
// Adaptive icons
|
||||
// TODO: Add Adapative Icons in Lime
|
||||
templatePaths.push("templates");
|
||||
|
||||
var androidTheme:String = "@style/LimeAppMainTheme";
|
||||
if (window.fullscreen != null && window.fullscreen)
|
||||
{
|
||||
androidTheme += "Fullscreen";
|
||||
}
|
||||
|
||||
final iconXmlChild:Dynamic = {
|
||||
"android:label": meta.title,
|
||||
"android:allowBackup": "true",
|
||||
"android:theme": androidTheme,
|
||||
"android:hardwareAccelerated": "true",
|
||||
"android:allowNativeHeapPointerTagging": ANDROID_TARGET_SDK_VERSION >= 30 ? "false" : null,
|
||||
"android:largeHeap": "true",
|
||||
"android:icon": "@mipmap/ic_launcher",
|
||||
"android:roundIcon": "@mipmap/ic_launcher_round"
|
||||
};
|
||||
|
||||
config.set('android.application', iconXmlChild);
|
||||
adaptiveIcon = new AdaptiveIcon('art/icons/android/', true);
|
||||
}
|
||||
else if (isIOS())
|
||||
{
|
||||
@@ -1255,7 +1340,24 @@ class Project extends HXProject
|
||||
addIcon("art/icons/icon64.png", 64);
|
||||
addIcon("art/icons/iconOG.png");
|
||||
|
||||
info('Done configuring icons.');
|
||||
}
|
||||
|
||||
info('Done configuring icons.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the astc textures.
|
||||
*/
|
||||
function configureASTCTextures()
|
||||
{
|
||||
if (command != "display")
|
||||
{
|
||||
readASTCExclusion();
|
||||
|
||||
if (FEATURE_COMPRESSED_TEXTURES.isEnabled(this))
|
||||
{
|
||||
runASTCCompressor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1312,6 +1414,21 @@ class Project extends HXProject
|
||||
return this.architectures.contains(Architecture.X64);
|
||||
}
|
||||
|
||||
public function isWindowsHost():Bool
|
||||
{
|
||||
return System.hostPlatform == WINDOWS;
|
||||
}
|
||||
|
||||
public function isMacHost():Bool
|
||||
{
|
||||
return System.hostPlatform == MAC;
|
||||
}
|
||||
|
||||
public function isLinuxHost():Bool
|
||||
{
|
||||
return System.hostPlatform == LINUX;
|
||||
}
|
||||
|
||||
public function isWindows():Bool
|
||||
{
|
||||
return this.target == Platform.WINDOWS;
|
||||
@@ -1655,7 +1772,8 @@ class Project extends HXProject
|
||||
*/
|
||||
public function error(message:String):Void
|
||||
{
|
||||
Log.error('${message}');
|
||||
Sys.stderr().write(Bytes.ofString('[ERROR] ${message}'));
|
||||
Sys.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1665,7 +1783,7 @@ class Project extends HXProject
|
||||
{
|
||||
if (command != "display")
|
||||
{
|
||||
Log.info('[INFO] ${message}');
|
||||
Sys.println('[INFO] ${message}');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1692,46 +1810,67 @@ class Project extends HXProject
|
||||
var env = new Map<String, Dynamic>();
|
||||
for (line in envFile.split('\n'))
|
||||
{
|
||||
if (line == "" || line.startsWith("#")) continue;
|
||||
if (line.length <= 0 || line.startsWith("#") || shouldExcludeEnvKey(line)) continue;
|
||||
|
||||
var parts = line.split('=');
|
||||
if (parts.length != 2) continue;
|
||||
var index:Int = line.indexOf('=');
|
||||
|
||||
env.set(parts[0], parts[1]);
|
||||
if (index == -1) continue;
|
||||
|
||||
var field:String = line.substr(0, index);
|
||||
var value:String = line.substr(index + 1);
|
||||
|
||||
env.set(field, value);
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
private function shouldExcludeEnvKey(key:String):Bool
|
||||
{
|
||||
final android:Bool = key.startsWith('ANDROID_');
|
||||
final ios:Bool = key.startsWith('IOS_');
|
||||
final mobile:Bool = key.startsWith('MOBILE_') || ios || android;
|
||||
final web:Bool = key.startsWith('WEB_');
|
||||
final desktop:Bool = key.startsWith('DESKTOP_');
|
||||
|
||||
if (isWeb() && (mobile || desktop)) return true;
|
||||
if (isDesktop() && (mobile || web)) return true;
|
||||
if (isAndroid() && (ios || web || desktop)) return true;
|
||||
if (isIOS() && (android || web || desktop)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function readASTCExclusion():Void
|
||||
{
|
||||
astcExcludes = File.getContent('./compression-excludes.txt').trim().split('\n');
|
||||
@:nullSafety(Off)
|
||||
astcExcludes = haxe.Json.parse(File.getContent('./astc-compression-data.json')).excludes;
|
||||
|
||||
for (i in 0...astcExcludes.length)
|
||||
astcExcludes[i] = astcExcludes[i].trim();
|
||||
}
|
||||
|
||||
public function isASTCExcluded(filePath:String):Bool
|
||||
public function isASTCExcluded(file:String):Bool
|
||||
{
|
||||
for (exclusion in astcExcludes)
|
||||
{
|
||||
if (exclusion.endsWith("/*"))
|
||||
if (exclusion.endsWith("/"))
|
||||
{
|
||||
var normalizedFilePath = Path.normalize(filePath);
|
||||
var normalizedExclusion = Path.normalize(exclusion.substr(0, exclusion.length - 2));
|
||||
var normalizedFilePath = Path.normalize(file);
|
||||
var normalizedExclusion = Path.normalize(exclusion);
|
||||
|
||||
if (normalizedFilePath.startsWith(normalizedExclusion)) return true;
|
||||
}
|
||||
else if (exclusion.endsWith("/"))
|
||||
else if (exclusion.endsWith("/*"))
|
||||
{
|
||||
var normalizedExclusion = Path.normalize(exclusion);
|
||||
var fileDirectory = Path.directory(Path.normalize(filePath));
|
||||
var normalizedExclusion = Path.normalize(exclusion.substr(0, exclusion.length - 2));
|
||||
var fileDirectory = Path.directory(Path.normalize(file));
|
||||
|
||||
if (fileDirectory == normalizedExclusion) return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filePath == exclusion) return true;
|
||||
if (file == exclusion) return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1742,16 +1881,12 @@ class Project extends HXProject
|
||||
{
|
||||
info('Compressing ASTC textures...');
|
||||
|
||||
var args:Array<String> = ['run', 'astc-compressor'];
|
||||
args = args.concat(['-i', './assets']);
|
||||
args = args.concat(['-o', './astc-textures/assets/']);
|
||||
args = args.concat(['-blocksize', ASTC_BLOCKSIZE]);
|
||||
args = args.concat(['-quality', ASTC_QUALITY]);
|
||||
args = args.concat(['-excludes', './compression-excludes.txt']);
|
||||
var args:Array<String> = ['run', 'astc-compressor', 'compress-from-json'];
|
||||
args = args.concat(['-json', './astc-compression-data.json']);
|
||||
|
||||
Sys.command('haxelib', args);
|
||||
|
||||
info('Finished compressing ASTC textures!');
|
||||
info('Done compressing ASTC textures.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1759,9 +1894,9 @@ class Project extends HXProject
|
||||
* An object representing a feature flag, which can be enabled or disabled.
|
||||
* Includes features such as automatic generation of compile defines and inversion.
|
||||
*/
|
||||
abstract FeatureFlag(String)
|
||||
abstract FeatureFlag(String) from String to String
|
||||
{
|
||||
static final INVERSE_PREFIX:String = "NO_";
|
||||
public static final INVERSE_PREFIX:String = "NO_";
|
||||
|
||||
public function new(input:String)
|
||||
{
|
||||
@@ -1786,13 +1921,11 @@ abstract FeatureFlag(String)
|
||||
if (isEnabled(project))
|
||||
{
|
||||
// If this flag was already enabled, disable the inverse.
|
||||
project.info('Enabling feature flag ${this}');
|
||||
getInverse().disable(project, false);
|
||||
}
|
||||
else if (getInverse().isEnabled(project))
|
||||
{
|
||||
// If the inverse flag was already enabled, disable this flag.
|
||||
project.info('Disabling feature flag ${this}');
|
||||
disable(project, false);
|
||||
}
|
||||
else
|
||||
@@ -1800,13 +1933,11 @@ abstract FeatureFlag(String)
|
||||
if (enableByDefault)
|
||||
{
|
||||
// Enable this flag if it was unset, and disable the inverse.
|
||||
project.info('Enabling feature flag ${this}');
|
||||
enable(project, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable this flag if it was unset, and enable the inverse.
|
||||
project.info('Disabling feature flag ${this}');
|
||||
disable(project, true);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -38,12 +38,11 @@ class Main extends Sprite
|
||||
{
|
||||
// Set the current working directory for Android and iOS devices
|
||||
#if android
|
||||
// For Android we determine the appropriate directory based on Android version
|
||||
Sys.setCwd(haxe.io.Path.addTrailingSlash(extension.androidtools.os.Build.VERSION.SDK_INT > 30 ? extension.androidtools.content.Context.getObbDir() : // Use Obb directory for Android SDK version > 30
|
||||
extension.androidtools.content.Context.getExternalFilesDir() // Use External Files directory for Android SDK version < 30
|
||||
));
|
||||
// On Android use External Files Dir.
|
||||
Sys.setCwd(haxe.io.Path.addTrailingSlash(extension.androidtools.content.Context.getExternalFilesDir()));
|
||||
#elseif ios
|
||||
Sys.setCwd(haxe.io.Path.addTrailingSlash(lime.system.System.documentsDirectory)); // For iOS we use documents directory and this is only way we can do.
|
||||
// On iOS use Documents Dir.
|
||||
Sys.setCwd(haxe.io.Path.addTrailingSlash(lime.system.System.documentsDirectory));
|
||||
#end
|
||||
|
||||
// We need to make the crash handler LITERALLY FIRST so nothing EVER gets past it.
|
||||
@@ -146,6 +145,7 @@ class Main extends Sprite
|
||||
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil());
|
||||
funkin.util.macro.ConsoleMacro.init();
|
||||
#end
|
||||
|
||||
#if !html5
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class Postbuild
|
||||
|
||||
var buildTime:Float = roundToTwoDecimals(end - start);
|
||||
|
||||
trace('Build took: ${buildTime} seconds');
|
||||
Sys.println('[INFO] Build took: ${buildTime} seconds');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-30
@@ -9,28 +9,16 @@ class Prebuild
|
||||
{
|
||||
static inline final BUILD_TIME_FILE:String = '.build_time';
|
||||
|
||||
static final NG_CREDS_PATH:String = './source/funkin/api/newgrounds/NewgroundsCredentials.hx';
|
||||
|
||||
static final NG_CREDS_TEMPLATE:String = "package funkin.api.newgrounds;
|
||||
|
||||
class NewgroundsCredentials
|
||||
{
|
||||
public static final APP_ID:String = #if API_NG_APP_ID haxe.macro.Compiler.getDefine(\"API_NG_APP_ID\") #else 'INSERT APP ID HERE' #end;
|
||||
public static final ENCRYPTION_KEY:String = #if API_NG_ENC_KEY haxe.macro.Compiler.getDefine(\"API_NG_ENC_KEY\") #else 'INSERT ENCRYPTION KEY HERE' #end;
|
||||
}";
|
||||
|
||||
static function main():Void
|
||||
{
|
||||
var start:Float = Sys.time();
|
||||
trace('[PREBUILD] Performing pre-build tasks...');
|
||||
// Sys.println('[INFO] Performing pre-build tasks...');
|
||||
|
||||
saveBuildTime();
|
||||
|
||||
buildCredsFile();
|
||||
|
||||
var end:Float = Sys.time();
|
||||
var duration:Float = end - start;
|
||||
trace('[PREBUILD] Finished pre-build tasks in $duration seconds.');
|
||||
// Sys.println('[INFO] Finished pre-build tasks in $duration seconds.');
|
||||
}
|
||||
|
||||
static function saveBuildTime():Void
|
||||
@@ -41,20 +29,4 @@ class NewgroundsCredentials
|
||||
fo.writeDouble(now);
|
||||
fo.close();
|
||||
}
|
||||
|
||||
static function buildCredsFile():Void
|
||||
{
|
||||
if (sys.FileSystem.exists(NG_CREDS_PATH))
|
||||
{
|
||||
trace('[PREBUILD] NewgroundsCredentials.hx already exists, skipping.');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('[PREBUILD] Creating NewgroundsCredentials.hx...');
|
||||
|
||||
var fileContents:String = NG_CREDS_TEMPLATE;
|
||||
|
||||
sys.io.File.saveContent(NG_CREDS_PATH, fileContents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import flixel.util.FlxSignal;
|
||||
import flixel.math.FlxMath;
|
||||
import funkin.data.song.SongData.SongTimeChange;
|
||||
import funkin.data.song.SongDataUtils;
|
||||
import funkin.play.PlayState;
|
||||
import funkin.save.Save;
|
||||
import funkin.util.TimerUtil.SongSequence;
|
||||
import haxe.Timer;
|
||||
@@ -113,6 +114,17 @@ class Conductor
|
||||
|
||||
if (currentTimeChange == null) return Constants.DEFAULT_BPM;
|
||||
|
||||
@:privateAccess
|
||||
if (PlayState.instance != null && PlayState.instance.startingSong)
|
||||
{
|
||||
for (i in 0...timeChanges.length)
|
||||
{
|
||||
if (PlayState.instance.startTimestamp >= timeChanges[i].timeStamp) currentTimeChange = timeChanges[i];
|
||||
|
||||
if (PlayState.instance.startTimestamp < timeChanges[i].timeStamp) break;
|
||||
}
|
||||
}
|
||||
|
||||
return currentTimeChange.bpm;
|
||||
}
|
||||
|
||||
@@ -417,7 +429,7 @@ class Conductor
|
||||
// If the song is playing, limit the song position to the length of the song or beginning of the song.
|
||||
if (FlxG.sound.music != null && FlxG.sound.music.playing)
|
||||
{
|
||||
this.songPosition = FlxMath.bound(Math.min(this.combinedOffset, 0), songPos, currentLength);
|
||||
this.songPosition = Math.min(this.combinedOffset, 0).clamp(songPos, currentLength);
|
||||
this.songPositionDelta += FlxG.elapsed * 1000 * FlxG.sound.music.pitch;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -117,10 +117,7 @@ class FunkinMemory
|
||||
*/
|
||||
public static function cacheTexture(key:String):Void
|
||||
{
|
||||
if (currentCachedTextures.exists(key))
|
||||
{
|
||||
return; // Already cached.
|
||||
}
|
||||
if (currentCachedTextures.exists(key)) return;
|
||||
|
||||
if (previousCachedTextures.exists(key))
|
||||
{
|
||||
@@ -135,13 +132,13 @@ class FunkinMemory
|
||||
if (graphic == null)
|
||||
{
|
||||
FlxG.log.warn('Failed to cache graphic: $key');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Successfully cached graphic: $key');
|
||||
graphic.persist = true;
|
||||
currentCachedTextures.set(key, graphic);
|
||||
}
|
||||
|
||||
trace('Successfully cached graphic: $key');
|
||||
graphic.persist = true;
|
||||
currentCachedTextures.set(key, graphic);
|
||||
forceRender(graphic);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,26 +147,54 @@ class FunkinMemory
|
||||
*/
|
||||
static function permanentCacheTexture(key:String):Void
|
||||
{
|
||||
if (permanentCachedTextures.exists(key))
|
||||
{
|
||||
return; // Already cached.
|
||||
}
|
||||
if (permanentCachedTextures.exists(key)) return;
|
||||
|
||||
var graphic:Null<FlxGraphic> = FlxGraphic.fromAssetKey(key, false, null, true);
|
||||
if (graphic == null)
|
||||
{
|
||||
FlxG.log.warn('Failed to cache graphic: $key');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Successfully cached graphic: $key');
|
||||
graphic.persist = true;
|
||||
permanentCachedTextures.set(key, graphic);
|
||||
return;
|
||||
}
|
||||
|
||||
trace('Successfully cached graphic: $key');
|
||||
graphic.persist = true;
|
||||
permanentCachedTextures.set(key, graphic);
|
||||
forceRender(graphic);
|
||||
currentCachedTextures = permanentCachedTextures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the GPU to load and upload a FlxGraphic.
|
||||
*/
|
||||
private static function forceRender(graphic:FlxGraphic):Void
|
||||
{
|
||||
if (graphic == null) return;
|
||||
|
||||
var bmp:Null<FlxGraphic> = FlxG.bitmap.get(graphic.key);
|
||||
if (bmp != null && bmp.bitmap != null) var _:Int = bmp.bitmap.width; // Trigger
|
||||
|
||||
// Draws sprite and actually caches it.
|
||||
var sprite = new flixel.FlxSprite();
|
||||
sprite.loadGraphic(graphic);
|
||||
sprite.draw(); // Draw sprite and load it into game's memory.
|
||||
sprite.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, if graphic with given path cached in memory.
|
||||
*/
|
||||
public static inline function isGraphicCached(path:String):Bool
|
||||
return permanentCachedTextures.exists(path) || currentCachedTextures.exists(path) || previousCachedTextures.exists(path);
|
||||
|
||||
public static function getCachedGraphic(path:String):Null<FlxGraphic>
|
||||
{
|
||||
if (permanentCachedTextures.exists(path)) return permanentCachedTextures.get(path);
|
||||
if (currentCachedTextures.exists(path)) return currentCachedTextures.get(path);
|
||||
if (previousCachedTextures.exists(path)) return previousCachedTextures.get(path); // just in case
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the cache for purging unused textures.
|
||||
*/
|
||||
@@ -350,7 +375,7 @@ class FunkinMemory
|
||||
if (sound != null)
|
||||
{
|
||||
Assets.cache.removeSound(key);
|
||||
previousCachedTextures.remove(key);
|
||||
previousCachedSounds.remove(key);
|
||||
}
|
||||
}
|
||||
Assets.cache.clear("songs");
|
||||
|
||||
+144
-129
@@ -11,9 +11,9 @@ import flixel.math.FlxRect;
|
||||
import flixel.system.debug.log.LogStyle;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.data.dialogue.conversation.ConversationRegistry;
|
||||
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
|
||||
import funkin.data.dialogue.speaker.SpeakerRegistry;
|
||||
import funkin.data.dialogue.ConversationRegistry;
|
||||
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||
import funkin.data.dialogue.SpeakerRegistry;
|
||||
import funkin.data.freeplay.album.AlbumRegistry;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.data.freeplay.style.FreeplayStyleRegistry;
|
||||
@@ -55,6 +55,13 @@ import funkin.api.newgrounds.NewgroundsClient;
|
||||
@:nullSafety
|
||||
class InitState extends FlxState
|
||||
{
|
||||
/**
|
||||
* Simply states whether the "core stuff" is ready or not.
|
||||
* This is used to prevent re-initialization of specific core features.
|
||||
*/
|
||||
@:noCompletion
|
||||
static var _coreInitialized:Bool = false;
|
||||
|
||||
/**
|
||||
* Perform a bunch of game setup, then immediately transition to the title screen.
|
||||
*/
|
||||
@@ -78,158 +85,163 @@ class InitState extends FlxState
|
||||
*/
|
||||
function setupShit():Void
|
||||
{
|
||||
//
|
||||
// GAME SETUP
|
||||
//
|
||||
if (!_coreInitialized)
|
||||
{
|
||||
//
|
||||
// GAME SETUP
|
||||
//
|
||||
|
||||
// Setup window events (like callbacks for onWindowClose)
|
||||
// and fullscreen keybind setup
|
||||
WindowUtil.initWindowEvents();
|
||||
// Disable the thing on Windows where it tries to send a bug report to Microsoft because why do they care?
|
||||
WindowUtil.disableCrashHandler();
|
||||
// Setup window events (like callbacks for onWindowClose) and fullscreen keybind setup
|
||||
WindowUtil.initWindowEvents();
|
||||
|
||||
#if FEATURE_DEBUG_TRACY
|
||||
funkin.util.WindowUtil.initTracy();
|
||||
#end
|
||||
#if FEATURE_DEBUG_TRACY
|
||||
funkin.util.WindowUtil.initTracy();
|
||||
#end
|
||||
|
||||
#if FEATURE_HAPTICS
|
||||
// Setup Haptic feedback
|
||||
extension.haptics.Haptic.initialize();
|
||||
#end
|
||||
#if FEATURE_HAPTICS
|
||||
// Setup Haptic feedback
|
||||
extension.haptics.Haptic.initialize();
|
||||
#end
|
||||
|
||||
#if FEATURE_MOBILE_ADVERTISEMENTS
|
||||
// Setup Admob
|
||||
funkin.mobile.util.AdMobUtil.init();
|
||||
#end
|
||||
#if FEATURE_MOBILE_ADVERTISEMENTS
|
||||
// Setup Admob
|
||||
funkin.mobile.util.AdMobUtil.init();
|
||||
#end
|
||||
|
||||
#if FEATURE_MOBILE_IAP
|
||||
// Setup In-App purchases
|
||||
funkin.mobile.util.InAppPurchasesUtil.init();
|
||||
#end
|
||||
#if FEATURE_MOBILE_IAP
|
||||
// Setup In-App purchases
|
||||
funkin.mobile.util.InAppPurchasesUtil.init();
|
||||
#end
|
||||
|
||||
#if FEATURE_MOBILE_IAR
|
||||
// Setup In-App purchases
|
||||
funkin.mobile.util.InAppReviewUtil.init();
|
||||
#end
|
||||
#if FEATURE_MOBILE_IAR
|
||||
// Setup In-App reviews
|
||||
funkin.mobile.util.InAppReviewUtil.init();
|
||||
#end
|
||||
|
||||
#if ios
|
||||
// Setup Audio session
|
||||
funkin.mobile.external.AudioSession.initialize();
|
||||
#end
|
||||
#if android
|
||||
// Setup Callback util.
|
||||
funkin.external.android.CallbackUtil.init();
|
||||
#end
|
||||
|
||||
// This ain't a pixel art game! (most of the time)
|
||||
FlxSprite.defaultAntialiasing = true;
|
||||
#if ios
|
||||
// Setup Audio session
|
||||
funkin.external.ios.AudioSession.initialize();
|
||||
#end
|
||||
|
||||
// Disable default keybinds for volume (we manually control volume in MusicBeatState with custom binds)
|
||||
FlxG.sound.volumeUpKeys = [];
|
||||
FlxG.sound.volumeDownKeys = [];
|
||||
FlxG.sound.muteKeys = [];
|
||||
// This ain't a pixel art game! (most of the time)
|
||||
FlxSprite.defaultAntialiasing = true;
|
||||
|
||||
// A small jumpstart to the soundtray, it usually sets itself to inactive (somewhere...)
|
||||
// but that makes our soundtray not show up on init if we have the game muted.
|
||||
// We set it to active so it at least calls it's update function once (see FlxGame.onEnterFrame(), it's called there)
|
||||
// and also see FunkinSoundTray.update() to see what we do and how we check if we are muted or not
|
||||
#if !mobile
|
||||
FlxG.game.soundTray.active = true;
|
||||
#end
|
||||
// Disable default keybinds for volume (we manually control volume in MusicBeatState with custom binds)
|
||||
FlxG.sound.volumeUpKeys = [];
|
||||
FlxG.sound.volumeDownKeys = [];
|
||||
FlxG.sound.muteKeys = [];
|
||||
|
||||
// Set the game to a lower frame rate while it is in the background.
|
||||
FlxG.game.focusLostFramerate = 30;
|
||||
// A small jumpstart to the soundtray, it usually sets itself to inactive (somewhere...)
|
||||
// but that makes our soundtray not show up on init if we have the game muted.
|
||||
// We set it to active so it at least calls it's update function once (see FlxGame.onEnterFrame(), it's called there)
|
||||
// and also see FunkinSoundTray.update() to see what we do and how we check if we are muted or not
|
||||
#if !mobile
|
||||
FlxG.game.soundTray.active = true;
|
||||
#end
|
||||
|
||||
// Makes Flixel use frame times instead of locked movements per frame for things like tweens
|
||||
FlxG.fixedTimestep = false;
|
||||
// Set the game to a lower frame rate while it is in the background.
|
||||
FlxG.game.focusLostFramerate = 30;
|
||||
|
||||
setupFlixelDebug();
|
||||
// Makes Flixel use frame times instead of locked movements per frame for things like tweens
|
||||
FlxG.fixedTimestep = false;
|
||||
|
||||
//
|
||||
// FLIXEL TRANSITIONS
|
||||
//
|
||||
setupFlixelDebug();
|
||||
|
||||
// Diamond Transition
|
||||
var diamond:FlxGraphic = FlxGraphic.fromClass(GraphicTransTileDiamond);
|
||||
diamond.persist = true;
|
||||
diamond.destroyOnNoUse = false;
|
||||
//
|
||||
// FLIXEL TRANSITIONS
|
||||
//
|
||||
|
||||
// NOTE: tileData is ignored if TransitionData.type is FADE instead of TILES.
|
||||
var tileData:TransitionTileData = {asset: diamond, width: 32, height: 32};
|
||||
// Diamond Transition
|
||||
var diamond:FlxGraphic = FlxGraphic.fromClass(GraphicTransTileDiamond);
|
||||
diamond.persist = true;
|
||||
diamond.destroyOnNoUse = false;
|
||||
|
||||
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
|
||||
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData,
|
||||
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||
// Don't play transition in when entering the title state.
|
||||
FlxTransitionableState.skipNextTransIn = true;
|
||||
// NOTE: tileData is ignored if TransitionData.type is FADE instead of TILES.
|
||||
var tileData:TransitionTileData = {asset: diamond, width: 32, height: 32};
|
||||
|
||||
FlxG.signals.gameResized.add(function(width:Int, height:Int) {
|
||||
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
|
||||
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData,
|
||||
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||
});
|
||||
|
||||
// SDL for some reason enables VSync on focus lost/gained in Android
|
||||
// Since we don't really need VSync on Android we're gonna forcefully disable it on these signals for now
|
||||
// This is fixed on SDL3 from what I've heared but that doodoo isn't working poperly for Android
|
||||
#if android
|
||||
FlxG.signals.focusLost.add(function() {
|
||||
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
||||
});
|
||||
FlxG.signals.focusGained.add(function() {
|
||||
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
||||
});
|
||||
#end
|
||||
FlxG.signals.gameResized.add(function(width:Int, height:Int) {
|
||||
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
|
||||
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData,
|
||||
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||
});
|
||||
|
||||
//
|
||||
// NEWGROUNDS API SETUP
|
||||
//
|
||||
#if FEATURE_NEWGROUNDS
|
||||
NewgroundsClient.instance.init();
|
||||
#end
|
||||
// SDL for some reason enables VSync on focus lost/gained in Android
|
||||
// Since we don't really need VSync on Android we're gonna forcefully disable it on these signals for now
|
||||
// This is fixed on SDL3 from what I've heared but that doodoo isn't working poperly for Android
|
||||
#if android
|
||||
FlxG.signals.focusLost.add(function() {
|
||||
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
||||
});
|
||||
FlxG.signals.focusGained.add(function() {
|
||||
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
||||
});
|
||||
#end
|
||||
|
||||
//
|
||||
// DISCORD API SETUP
|
||||
//
|
||||
#if FEATURE_DISCORD_RPC
|
||||
DiscordClient.instance.init();
|
||||
//
|
||||
// NEWGROUNDS API SETUP
|
||||
//
|
||||
#if FEATURE_NEWGROUNDS
|
||||
NewgroundsClient.instance.init();
|
||||
#end
|
||||
|
||||
lime.app.Application.current.onExit.add(function(exitCode) {
|
||||
DiscordClient.instance.shutdown();
|
||||
});
|
||||
#end
|
||||
//
|
||||
// DISCORD API SETUP
|
||||
//
|
||||
#if FEATURE_DISCORD_RPC
|
||||
DiscordClient.instance.init();
|
||||
|
||||
//
|
||||
// ANDROID SETUP
|
||||
//
|
||||
#if android
|
||||
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
|
||||
#end
|
||||
lime.app.Application.current.onExit.add(function(exitCode) {
|
||||
DiscordClient.instance.shutdown();
|
||||
});
|
||||
#end
|
||||
|
||||
//
|
||||
// FLIXEL PLUGINS
|
||||
//
|
||||
// Plugins provide a useful interface for globally active Flixel objects,
|
||||
// that receive update events regardless of the current state.
|
||||
// TODO: Move scripted Module behavior to a Flixel plugin.
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
funkin.util.plugins.MemoryGCPlugin.initialize();
|
||||
#end
|
||||
#if FEATURE_SCREENSHOTS
|
||||
funkin.util.plugins.ScreenshotPlugin.initialize();
|
||||
#end
|
||||
#if FEATURE_NEWGROUNDS
|
||||
funkin.util.plugins.NewgroundsMedalPlugin.initialize();
|
||||
#end
|
||||
funkin.util.plugins.EvacuateDebugPlugin.initialize();
|
||||
funkin.util.plugins.ForceCrashPlugin.initialize();
|
||||
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
|
||||
#if !mobile
|
||||
funkin.util.plugins.VolumePlugin.initialize();
|
||||
#end
|
||||
funkin.util.plugins.WatchPlugin.initialize();
|
||||
#if mobile
|
||||
funkin.util.plugins.TouchPointerPlugin.initialize();
|
||||
funkin.mobile.input.ControlsHandler.initInputTrackers();
|
||||
#end
|
||||
//
|
||||
// ANDROID SETUP
|
||||
//
|
||||
#if android
|
||||
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
|
||||
#end
|
||||
|
||||
//
|
||||
// FLIXEL PLUGINS
|
||||
//
|
||||
// Plugins provide a useful interface for globally active Flixel objects,
|
||||
// that receive update events regardless of the current state.
|
||||
// TODO: Move scripted Module behavior to a Flixel plugin.
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
funkin.util.plugins.MemoryGCPlugin.initialize();
|
||||
#end
|
||||
#if FEATURE_SCREENSHOTS
|
||||
funkin.util.plugins.ScreenshotPlugin.initialize();
|
||||
#end
|
||||
#if FEATURE_NEWGROUNDS
|
||||
funkin.util.plugins.NewgroundsMedalPlugin.initialize();
|
||||
#end
|
||||
funkin.util.plugins.EvacuateDebugPlugin.initialize();
|
||||
funkin.util.plugins.ForceCrashPlugin.initialize();
|
||||
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
|
||||
#if !mobile
|
||||
funkin.util.plugins.VolumePlugin.initialize();
|
||||
#end
|
||||
funkin.util.plugins.WatchPlugin.initialize();
|
||||
#if mobile
|
||||
funkin.util.plugins.TouchPointerPlugin.initialize();
|
||||
funkin.mobile.input.ControlsHandler.initInputTrackers();
|
||||
#end
|
||||
|
||||
_coreInitialized = true;
|
||||
}
|
||||
|
||||
//
|
||||
// GAME DATA PARSING
|
||||
@@ -278,6 +290,9 @@ class InitState extends FlxState
|
||||
*/
|
||||
function startGame():Void
|
||||
{
|
||||
// Don't play transition in when entering the title state.
|
||||
FlxTransitionableState.skipNextTransIn = true;
|
||||
|
||||
#if SONG
|
||||
// -DSONG=bopeebo
|
||||
startSong(defineSong(), defineDifficulty());
|
||||
@@ -385,7 +400,7 @@ class InitState extends FlxState
|
||||
*/
|
||||
function startSong(songId:String, difficultyId:String = 'normal'):Void
|
||||
{
|
||||
var songData:Null<funkin.play.song.Song> = funkin.data.song.SongRegistry.instance.fetchEntry(songId);
|
||||
var songData:Null<funkin.play.song.Song> = funkin.data.song.SongRegistry.instance.fetchEntry(songId, {variation: Constants.DEFAULT_VARIATION});
|
||||
|
||||
if (songData == null)
|
||||
{
|
||||
@@ -457,7 +472,7 @@ class InitState extends FlxState
|
||||
|
||||
var targetSong:Null<funkin.play.song.Song> = null;
|
||||
|
||||
if (targetSongId != null) targetSong = SongRegistry.instance.fetchEntry(targetSongId);
|
||||
if (targetSongId != null) targetSong = SongRegistry.instance.fetchEntry(targetSongId, {variation: Constants.DEFAULT_VARIATION});
|
||||
|
||||
if (targetSongId == null)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ package funkin;
|
||||
|
||||
#if mobile
|
||||
import funkin.mobile.ui.FunkinHitbox;
|
||||
import funkin.mobile.util.InAppPurchasesUtil;
|
||||
#end
|
||||
import funkin.save.Save;
|
||||
import funkin.util.WindowUtil;
|
||||
@@ -202,6 +203,25 @@ class Preferences
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplier of intensity for all the haptic feedback effects.
|
||||
* @default `2.5`
|
||||
*/
|
||||
public static var hapticsIntensityMultiplier(get, set):Float;
|
||||
|
||||
static function get_hapticsIntensityMultiplier():Float
|
||||
{
|
||||
return Save?.instance?.options?.hapticsIntensityMultiplier ?? 1;
|
||||
}
|
||||
|
||||
static function set_hapticsIntensityMultiplier(value:Float):Float
|
||||
{
|
||||
var save:Save = Save.instance;
|
||||
save.options.hapticsIntensityMultiplier = value;
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled, the game will automatically pause when tabbing out.
|
||||
* Always enabled on mobile.
|
||||
@@ -527,5 +547,31 @@ class Preferences
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
|
||||
#if FEATURE_MOBILE_IAP
|
||||
/**
|
||||
* If bought, the game will not show any ads.
|
||||
* @default `false`
|
||||
*/
|
||||
@:unreflective
|
||||
public static var noAds(get, set):Bool;
|
||||
|
||||
@:unreflective
|
||||
static function get_noAds():Bool
|
||||
{
|
||||
if (InAppPurchasesUtil.hasInitialized) noAds = InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID);
|
||||
var returnedValue = Save?.instance?.mobileOptions?.noAds ?? false;
|
||||
return returnedValue;
|
||||
}
|
||||
|
||||
@:unreflective
|
||||
static function set_noAds(value:Bool):Bool
|
||||
{
|
||||
var save:Save = Save.instance;
|
||||
save.mobileOptions.noAds = value;
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.api.discord;
|
||||
|
||||
import funkin.util.macro.EnvironmentConfigMacro;
|
||||
#if FEATURE_DISCORD_RPC
|
||||
import hxdiscord_rpc.Discord;
|
||||
import hxdiscord_rpc.Types.DiscordButton;
|
||||
@@ -11,7 +12,7 @@ import sys.thread.Thread;
|
||||
@:nullSafety
|
||||
class DiscordClient
|
||||
{
|
||||
static final CLIENT_ID:String = "816168432860790794";
|
||||
static final CLIENT_ID:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("DESKTOP_DISCORD_CLIENT_ID");
|
||||
|
||||
public static var instance(get, never):DiscordClient;
|
||||
static var _instance:Null<DiscordClient> = null;
|
||||
@@ -40,12 +41,28 @@ class DiscordClient
|
||||
{
|
||||
trace('[DISCORD] Initializing connection...');
|
||||
|
||||
// Discord.initialize(CLIENT_ID, handlers, true, null);
|
||||
Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
|
||||
if (!hasValidCredentials())
|
||||
{
|
||||
FlxG.log.warn("Tried to initialize Discord connection, but credentials are invalid!");
|
||||
return;
|
||||
}
|
||||
|
||||
@:nullSafety(Off)
|
||||
{
|
||||
Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
|
||||
}
|
||||
|
||||
createDaemon();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `false` if the client ID is invalid.
|
||||
*/
|
||||
static function hasValidCredentials():Bool
|
||||
{
|
||||
return !(CLIENT_ID == null || CLIENT_ID == "" || (CLIENT_ID != null && CLIENT_ID.contains(" ")));
|
||||
}
|
||||
|
||||
var daemon:Null<Thread> = null;
|
||||
|
||||
function createDaemon():Void
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
package funkin.api.newgrounds;
|
||||
|
||||
#if FEATURE_NEWGROUNDS
|
||||
import io.newgrounds.utils.SaveSlotList;
|
||||
import io.newgrounds.objects.SaveSlot;
|
||||
import io.newgrounds.Call.CallError;
|
||||
import io.newgrounds.objects.events.Outcome;
|
||||
import funkin.save.Save;
|
||||
|
||||
@:nullSafety
|
||||
@:access(funkin.save.Save)
|
||||
class NGSaveSlot
|
||||
{
|
||||
public static var instance(get, never):NGSaveSlot;
|
||||
static var _instance:Null<NGSaveSlot> = null;
|
||||
|
||||
static function get_instance():NGSaveSlot
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
return loadInstance();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
public static function loadInstance():NGSaveSlot
|
||||
{
|
||||
var loadedSave:NGSaveSlot = loadSlot(Save.BASE_SAVE_SLOT);
|
||||
if (_instance == null) _instance = loadedSave;
|
||||
|
||||
return loadedSave;
|
||||
}
|
||||
|
||||
static function loadSlot(slot:Int):NGSaveSlot
|
||||
{
|
||||
trace('[NEWGROUNDS] Getting save slot from ID $slot');
|
||||
|
||||
var saveSlot:Null<SaveSlot> = NewgroundsClient.instance.saveSlots?.getById(slot);
|
||||
|
||||
var saveSlotObj:NGSaveSlot = new NGSaveSlot(saveSlot);
|
||||
return saveSlotObj;
|
||||
}
|
||||
|
||||
public var ngSaveSlot:Null<SaveSlot> = null;
|
||||
|
||||
public function new(?ngSaveSlot:Null<SaveSlot>)
|
||||
{
|
||||
this.ngSaveSlot = ngSaveSlot;
|
||||
|
||||
#if FLX_DEBUG
|
||||
FlxG.console.registerClass(NGSaveSlot);
|
||||
FlxG.console.registerClass(Save);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves `data` to the newgrounds save slot.
|
||||
* @param data The raw save data.
|
||||
*/
|
||||
public function save(data:RawSaveData):Void
|
||||
{
|
||||
var encodedData:String = haxe.Serializer.run(data);
|
||||
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.save(encodedData, function(outcome:Outcome<CallError>) {
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS:
|
||||
trace('[NEWGROUNDS] Successfully saved save data to save slot!');
|
||||
case FAIL(error):
|
||||
trace('[NEWGROUNDS] Failed to save data to save slot!');
|
||||
trace(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error:String)
|
||||
{
|
||||
trace('[NEWGROUNDS] Failed to save data to save slot!');
|
||||
trace(error);
|
||||
}
|
||||
}
|
||||
|
||||
public function load(?onComplete:Null<Dynamic->Void>, ?onError:Null<CallError->Void>):Void
|
||||
{
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.load(function(outcome:SaveSlotOutcome):Void {
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS(value):
|
||||
trace('[NEWGROUNDS] Loaded save slot with the ID of ${ngSaveSlot?.id}!');
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('Save Slot Data:');
|
||||
trace(value);
|
||||
#end
|
||||
|
||||
if (onComplete != null && value != null)
|
||||
{
|
||||
var decodedData:Dynamic = haxe.Unserializer.run(value);
|
||||
onComplete(decodedData);
|
||||
}
|
||||
case FAIL(error):
|
||||
trace('[NEWGROUNDS] Failed to load save slot with the ID of ${ngSaveSlot?.id}!');
|
||||
trace(error);
|
||||
|
||||
if (onError != null)
|
||||
{
|
||||
onError(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error:String)
|
||||
{
|
||||
trace('[NEWGROUNDS] Failed to load save slot with the ID of ${ngSaveSlot?.id}!');
|
||||
trace(error);
|
||||
|
||||
if (onError != null)
|
||||
{
|
||||
onError(RESPONSE({message: error, code: 500}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function clear():Void
|
||||
{
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.clear(function(outcome:Outcome<CallError>) {
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS:
|
||||
trace('[NEWGROUNDS] Successfully cleared save slot!');
|
||||
case FAIL(error):
|
||||
trace('[NEWGROUNDS] Failed to clear save slot!');
|
||||
trace(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error:String)
|
||||
{
|
||||
trace('[NEWGROUNDS] Failed to clear save slot!');
|
||||
trace(error);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkSlot():Void
|
||||
{
|
||||
trace('[NEWGROUNDS] Checking save slot with the ID of ${ngSaveSlot?.id}...');
|
||||
|
||||
trace(' Is null? ${ngSaveSlot == null}');
|
||||
trace(' Is empty? ${ngSaveSlot?.isEmpty() ?? false}');
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.api.newgrounds;
|
||||
|
||||
import funkin.util.macro.EnvironmentConfigMacro;
|
||||
import funkin.save.Save;
|
||||
import funkin.api.newgrounds.Medals.Medal;
|
||||
#if FEATURE_NEWGROUNDS
|
||||
@@ -10,13 +11,18 @@ import io.newgrounds.NGLite.LoginOutcome;
|
||||
import io.newgrounds.NGLite.LoginFail;
|
||||
import io.newgrounds.objects.events.Outcome;
|
||||
import io.newgrounds.utils.MedalList;
|
||||
import io.newgrounds.utils.SaveSlotList;
|
||||
import io.newgrounds.utils.ScoreBoardList;
|
||||
import io.newgrounds.objects.User;
|
||||
|
||||
@:nullSafety
|
||||
class NewgroundsClient
|
||||
{
|
||||
static final APP_ID:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("API_NG_APP_ID");
|
||||
static final ENCRYPTION_KEY:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("API_NG_ENC_KEY");
|
||||
|
||||
public static var instance(get, never):NewgroundsClient;
|
||||
|
||||
static var _instance:Null<NewgroundsClient> = null;
|
||||
|
||||
static function get_instance():NewgroundsClient
|
||||
@@ -29,14 +35,15 @@ class NewgroundsClient
|
||||
public var user(get, never):Null<User>;
|
||||
public var medals(get, never):Null<MedalList>;
|
||||
public var leaderboards(get, never):Null<ScoreBoardList>;
|
||||
public var saveSlots(get, never):Null<SaveSlotList>;
|
||||
|
||||
private function new()
|
||||
{
|
||||
trace('[NEWGROUNDS] Initializing client...');
|
||||
|
||||
#if FEATURE_NEWGROUNDS_DEBUG
|
||||
trace('[NEWGROUNDS] App ID: ${NewgroundsCredentials.APP_ID}');
|
||||
trace('[NEWGROUNDS] Encryption Key: ${NewgroundsCredentials.ENCRYPTION_KEY}');
|
||||
trace('[NEWGROUNDS] App ID: ${APP_ID}');
|
||||
trace('[NEWGROUNDS] Encryption Key: ${ENCRYPTION_KEY}');
|
||||
#end
|
||||
|
||||
if (!hasValidCredentials())
|
||||
@@ -45,9 +52,12 @@ class NewgroundsClient
|
||||
return;
|
||||
}
|
||||
|
||||
var debug = #if FEATURE_NEWGROUNDS_DEBUG true #else false #end;
|
||||
NG.create(NewgroundsCredentials.APP_ID, getSessionId(), debug, onLoginResolved);
|
||||
NG.core.setupEncryption(NewgroundsCredentials.ENCRYPTION_KEY);
|
||||
@:nullSafety(Off)
|
||||
{
|
||||
NG.create(APP_ID, getSessionId(), #if FEATURE_NEWGROUNDS_DEBUG true #else false #end, onLoginResolved);
|
||||
|
||||
NG.core.setupEncryption(ENCRYPTION_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
public function init()
|
||||
@@ -166,12 +176,12 @@ class NewgroundsClient
|
||||
*/
|
||||
static function hasValidCredentials():Bool
|
||||
{
|
||||
return !(NewgroundsCredentials.APP_ID == null
|
||||
|| NewgroundsCredentials.APP_ID == ""
|
||||
|| NewgroundsCredentials.APP_ID.contains(" ")
|
||||
|| NewgroundsCredentials.ENCRYPTION_KEY == null
|
||||
|| NewgroundsCredentials.ENCRYPTION_KEY == ""
|
||||
|| NewgroundsCredentials.ENCRYPTION_KEY.contains(" "));
|
||||
return !(APP_ID == null
|
||||
|| APP_ID == ""
|
||||
|| (APP_ID != null && APP_ID.contains(" "))
|
||||
|| ENCRYPTION_KEY == null
|
||||
|| ENCRYPTION_KEY == ""
|
||||
|| (ENCRYPTION_KEY != null && ENCRYPTION_KEY.contains(" ")));
|
||||
}
|
||||
|
||||
function onLoginResolved(outcome:LoginOutcome):Void
|
||||
@@ -236,6 +246,8 @@ class NewgroundsClient
|
||||
|
||||
trace('[NEWGROUNDS] Submitting leaderboard request...');
|
||||
NG.core.scoreBoards.loadList(onFetchedLeaderboards);
|
||||
trace('[NEWGROUNDS] Submitting save slot request...');
|
||||
NG.core.saveSlots.loadList(onFetchedSaveSlots);
|
||||
}
|
||||
|
||||
function onLoginFailed(result:LoginFail):Void
|
||||
@@ -301,6 +313,13 @@ class NewgroundsClient
|
||||
// trace(funkin.api.newgrounds.Leaderboards.listLeaderboardData());
|
||||
}
|
||||
|
||||
function onFetchedSaveSlots(outcome:Outcome<CallError>):Void
|
||||
{
|
||||
trace('[NEWGROUNDS] Fetched save slots!');
|
||||
|
||||
NGSaveSlot.instance.checkSlot();
|
||||
}
|
||||
|
||||
function get_user():Null<User>
|
||||
{
|
||||
if (NG.core == null || !this.isLoggedIn()) return null;
|
||||
@@ -319,6 +338,12 @@ class NewgroundsClient
|
||||
return NG.core.scoreBoards;
|
||||
}
|
||||
|
||||
function get_saveSlots():Null<SaveSlotList>
|
||||
{
|
||||
if (NG.core == null || !this.isLoggedIn()) return null;
|
||||
return NG.core.saveSlots;
|
||||
}
|
||||
|
||||
static function getSessionId():Null<String>
|
||||
{
|
||||
#if js
|
||||
|
||||
@@ -72,7 +72,7 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
override function set_volume(value:Float):Float
|
||||
{
|
||||
// Uncap the volume.
|
||||
_volume = FlxMath.bound(value, 0.0, MAX_VOLUME);
|
||||
_volume = value.clamp(0.0, MAX_VOLUME);
|
||||
updateTransform();
|
||||
return _volume;
|
||||
}
|
||||
@@ -568,8 +568,8 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
if (_sound == null) return;
|
||||
|
||||
// Create a channel manually if the sound is considered important.
|
||||
var pan:Float = FlxMath.bound(SoundMixer.__soundTransform.pan + _transform.pan, -1, 1);
|
||||
var volume:Float = FlxMath.bound(SoundMixer.__soundTransform.volume * _transform.volume, 0, MAX_VOLUME);
|
||||
var pan:Float = (SoundMixer.__soundTransform.pan + _transform.pan).clamp(-1, 1);
|
||||
var volume:Float = (SoundMixer.__soundTransform.volume * _transform.volume).clamp(0, MAX_VOLUME);
|
||||
|
||||
var audioSource:AudioSource = new AudioSource(_sound.__buffer);
|
||||
audioSource.offset = Std.int(startTime);
|
||||
|
||||
@@ -243,9 +243,10 @@ class WaveformDataChannel
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a given minimum point at an index.
|
||||
* @param i Index
|
||||
* @return minimum point at an index.
|
||||
*/
|
||||
public function minSample(i:Int)
|
||||
public function minSample(i:Int):Int
|
||||
{
|
||||
var offset = (i * parent.channels + this.channelId) * 2;
|
||||
return inline parent.get(offset);
|
||||
|
||||
@@ -43,69 +43,58 @@ class WaveformDataParser
|
||||
|
||||
public static function interpretAudioBuffer(soundBuffer:lime.media.AudioBuffer):Null<WaveformData>
|
||||
{
|
||||
var sampleRate = soundBuffer.sampleRate;
|
||||
var channels = soundBuffer.channels;
|
||||
var bitsPerSample = soundBuffer.bitsPerSample;
|
||||
var samplesPerPoint:Int = 256; // I don't think we need to configure this.
|
||||
var pointsPerSecond:Float = sampleRate / samplesPerPoint; // 172 samples per second for most songs is plenty precise while still being performant..
|
||||
|
||||
// TODO: Make this work better on HTML5.
|
||||
var soundData:lime.utils.Int16Array = cast soundBuffer.data;
|
||||
|
||||
var soundDataRawLength:Int = soundData.length;
|
||||
var soundDataSampleCount:Int = Std.int(Math.ceil(soundDataRawLength / channels / (bitsPerSample == 16 ? 2 : 1)));
|
||||
var soundDataSampleCount:Int = Std.int(Math.ceil(soundData.length / channels / (bitsPerSample == 16 ? 2 : 1)));
|
||||
var outputPointCount:Int = Std.int(Math.ceil(soundDataSampleCount / samplesPerPoint));
|
||||
|
||||
// trace('Interpreting audio buffer:');
|
||||
// trace(' sampleRate: ${sampleRate}');
|
||||
// trace(' channels: ${channels}');
|
||||
// trace(' bitsPerSample: ${bitsPerSample}');
|
||||
// trace(' samplesPerPoint: ${samplesPerPoint}');
|
||||
// trace(' pointsPerSecond: ${pointsPerSecond}');
|
||||
// trace(' soundDataRawLength: ${soundDataRawLength}');
|
||||
// trace(' soundDataSampleCount: ${soundDataSampleCount}');
|
||||
// trace(' soundDataRawLength/4: ${soundDataRawLength / 4}');
|
||||
// trace(' outputPointCount: ${outputPointCount}');
|
||||
// Pre-allocate Vector with exact final size for better performance and memory efficiency
|
||||
var outputDataLength:Int = outputPointCount * channels * 2;
|
||||
var outputData = new haxe.ds.Vector<Int>(outputDataLength);
|
||||
|
||||
var minSampleValue:Int = bitsPerSample == 16 ? INT16_MIN : INT8_MIN;
|
||||
var maxSampleValue:Int = bitsPerSample == 16 ? INT16_MAX : INT8_MAX;
|
||||
|
||||
var outputData:Array<Int> = [];
|
||||
// Reusable min/max tracking arrays to avoid allocation in the loop
|
||||
var minValues = new haxe.ds.Vector<Int>(channels);
|
||||
var maxValues = new haxe.ds.Vector<Int>(channels);
|
||||
|
||||
for (pointIndex in 0...outputPointCount)
|
||||
{
|
||||
// minChannel1, maxChannel1, minChannel2, maxChannel2, ...
|
||||
var values:Array<Int> = [];
|
||||
var rangeStart:Int = pointIndex * samplesPerPoint;
|
||||
var rangeEnd:Int = Std.int(Math.min(rangeStart + samplesPerPoint, soundDataSampleCount));
|
||||
|
||||
// Reset min/max values for this range
|
||||
for (i in 0...channels)
|
||||
{
|
||||
values.push(bitsPerSample == 16 ? INT16_MAX : INT8_MAX);
|
||||
values.push(bitsPerSample == 16 ? INT16_MIN : INT8_MIN);
|
||||
minValues[i] = bitsPerSample == 16 ? INT16_MAX : INT8_MAX;
|
||||
maxValues[i] = bitsPerSample == 16 ? INT16_MIN : INT8_MIN;
|
||||
}
|
||||
|
||||
var rangeStart = pointIndex * samplesPerPoint;
|
||||
var rangeEnd = rangeStart + samplesPerPoint;
|
||||
if (rangeEnd > soundDataSampleCount) rangeEnd = soundDataSampleCount;
|
||||
|
||||
// Process all samples in this range
|
||||
for (sampleIndex in rangeStart...rangeEnd)
|
||||
{
|
||||
for (channelIndex in 0...channels)
|
||||
{
|
||||
var sampleIndex:Int = sampleIndex * channels + channelIndex;
|
||||
var sampleValue = soundData[sampleIndex];
|
||||
var sampleValue:Int = soundData[sampleIndex * channels + channelIndex];
|
||||
|
||||
if (sampleValue < values[channelIndex * 2]) values[(channelIndex * 2)] = sampleValue;
|
||||
if (sampleValue > values[channelIndex * 2 + 1]) values[(channelIndex * 2) + 1] = sampleValue;
|
||||
if (sampleValue < minValues[channelIndex]) minValues[channelIndex] = sampleValue;
|
||||
if (sampleValue > maxValues[channelIndex]) maxValues[channelIndex] = sampleValue;
|
||||
}
|
||||
}
|
||||
|
||||
// We now have the min and max values for the range.
|
||||
for (value in values)
|
||||
outputData.push(value);
|
||||
// Write directly to final positions in output Vector
|
||||
var baseIndex:Int = pointIndex * channels * 2;
|
||||
for (channelIndex in 0...channels)
|
||||
{
|
||||
outputData[baseIndex + channelIndex * 2] = minValues[channelIndex];
|
||||
outputData[baseIndex + channelIndex * 2 + 1] = maxValues[channelIndex];
|
||||
}
|
||||
}
|
||||
|
||||
var outputDataLength:Int = Std.int(outputData.length / channels / 2);
|
||||
var result = new WaveformData(null, channels, sampleRate, samplesPerPoint, bitsPerSample, outputPointCount, outputData);
|
||||
var result = new WaveformData(null, channels, soundBuffer.sampleRate, samplesPerPoint, bitsPerSample, outputPointCount, outputData.toArray());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -5,20 +5,21 @@ import funkin.util.VersionUtil;
|
||||
import haxe.Constraints.Constructible;
|
||||
|
||||
/**
|
||||
* The entry's constructor function must take a single argument, the entry's ID.
|
||||
* The entry's constructor function takes 2 arguments, the entry ID and optional parameters.
|
||||
*/
|
||||
typedef EntryConstructorFunction = String->Void;
|
||||
typedef EntryConstructorFunction = (String, ?Dynamic) -> Void;
|
||||
|
||||
/**
|
||||
* A base type for a Registry, which is an object which handles loading scriptable objects.
|
||||
*
|
||||
* @param T The type to construct. Must implement `IRegistryEntry`.
|
||||
* @param J The type of the JSON data used when constructing.
|
||||
* @param P The type of the parameters used for `fetchEntry()`.
|
||||
*/
|
||||
@:nullSafety
|
||||
@:generic
|
||||
@:autoBuild(funkin.util.macro.DataRegistryMacro.buildRegistry())
|
||||
abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructorFunction>), J>
|
||||
abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructorFunction>), J, P>
|
||||
{
|
||||
/**
|
||||
* The ID of the registry. Used when logging.
|
||||
@@ -186,7 +187,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
||||
* @param id The ID of the entry to fetch.
|
||||
* @return The entry, or `null` if it does not exist.
|
||||
*/
|
||||
public function fetchEntry(id:String):Null<T>
|
||||
public function fetchEntry(id:String, ?params:Null<P>):Null<T>
|
||||
{
|
||||
return entries.get(id);
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ class DataParse
|
||||
case JArray(values):
|
||||
return Either.Left(legacyNoteSectionArray(json, name));
|
||||
case JObject(fields):
|
||||
return Either.Right(cast Tools.getValue(json));
|
||||
return Either.Right(legacyNoteData(json, name));
|
||||
default:
|
||||
throw 'Expected property $name to be note data, but it was ${json.value}.';
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class DataParse
|
||||
}
|
||||
}
|
||||
|
||||
public static function backdropData(json:Json, name:String):funkin.data.dialogue.conversation.ConversationData.BackdropData
|
||||
public static function backdropData(json:Json, name:String):funkin.data.dialogue.ConversationData.BackdropData
|
||||
{
|
||||
switch (json.value)
|
||||
{
|
||||
@@ -152,7 +152,7 @@ class DataParse
|
||||
}
|
||||
}
|
||||
|
||||
public static function outroData(json:Json, name:String):Null<funkin.data.dialogue.conversation.ConversationData.OutroData>
|
||||
public static function outroData(json:Json, name:String):Null<funkin.data.dialogue.ConversationData.OutroData>
|
||||
{
|
||||
switch (json.value)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Dialogue Conversation Data Schema Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.0]
|
||||
Initial release.
|
||||
|
||||
|
||||
|
||||
|
||||
# Dialogue Box Data Schema Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.1.0]
|
||||
### Added
|
||||
- Added an option to specify the font used by the dialogue box. Defaults to `Arial` if unspecified.
|
||||
|
||||
## [1.0.0]
|
||||
Initial release.
|
||||
|
||||
|
||||
|
||||
|
||||
# Dialogue Speaker Data Schema Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.0]
|
||||
Initial release.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.data.dialogue.conversation;
|
||||
package funkin.data.dialogue;
|
||||
|
||||
/**
|
||||
* A type definition for the data for a specific conversation.
|
||||
+7
-2
@@ -1,4 +1,4 @@
|
||||
package funkin.data.dialogue.conversation;
|
||||
package funkin.data.dialogue;
|
||||
|
||||
import funkin.play.cutscene.dialogue.Conversation;
|
||||
import funkin.play.cutscene.dialogue.ScriptedConversation;
|
||||
@@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class ConversationRegistry extends BaseRegistry<Conversation, ConversationData> implements ISingleton implements DefaultRegistryImpl
|
||||
class ConversationRegistry extends BaseRegistry<Conversation, ConversationData, ConversationEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the dialogue box data format.
|
||||
@@ -22,3 +22,8 @@ class ConversationRegistry extends BaseRegistry<Conversation, ConversationData>
|
||||
super('CONVERSATION', 'dialogue/conversations', CONVERSATION_DATA_VERSION_RULE);
|
||||
}
|
||||
}
|
||||
|
||||
typedef ConversationEntryParams =
|
||||
{
|
||||
var placeholder:String;
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.data.dialogue.dialoguebox;
|
||||
package funkin.data.dialogue;
|
||||
|
||||
import funkin.data.animation.AnimationData;
|
||||
|
||||
+5
-3
@@ -1,13 +1,13 @@
|
||||
package funkin.data.dialogue.dialoguebox;
|
||||
package funkin.data.dialogue;
|
||||
|
||||
import funkin.play.cutscene.dialogue.DialogueBox;
|
||||
import funkin.data.dialogue.dialoguebox.DialogueBoxData;
|
||||
import funkin.data.dialogue.DialogueBoxData;
|
||||
import funkin.play.cutscene.dialogue.ScriptedDialogueBox;
|
||||
import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData> implements ISingleton implements DefaultRegistryImpl
|
||||
class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData, DialogueBoxEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the dialogue box data format.
|
||||
@@ -23,3 +23,5 @@ class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData> imp
|
||||
super('DIALOGUEBOX', 'dialogue/boxes', DIALOGUEBOX_DATA_VERSION_RULE);
|
||||
}
|
||||
}
|
||||
|
||||
typedef DialogueBoxEntryParams = Dynamic;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.data.dialogue.speaker;
|
||||
package funkin.data.dialogue;
|
||||
|
||||
import funkin.data.animation.AnimationData;
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
package funkin.data.dialogue.speaker;
|
||||
package funkin.data.dialogue;
|
||||
|
||||
import funkin.play.cutscene.dialogue.Speaker;
|
||||
import funkin.play.cutscene.dialogue.ScriptedSpeaker;
|
||||
@@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData> implements ISingleton implements DefaultRegistryImpl
|
||||
class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData, SpeakerEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the speaker data format.
|
||||
@@ -22,3 +22,5 @@ class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData> implements ISin
|
||||
super('SPEAKER', 'dialogue/speakers', SPEAKER_DATA_VERSION_RULE);
|
||||
}
|
||||
}
|
||||
|
||||
typedef SpeakerEntryParams = {}
|
||||
@@ -1,9 +0,0 @@
|
||||
# Dialogue Conversation Data Schema Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.0]
|
||||
Initial release.
|
||||
@@ -1,13 +0,0 @@
|
||||
# Dialogue Box Data Schema Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.1.0]
|
||||
### Added
|
||||
- Added an option to specify the font used by the dialogue box. Defaults to `Arial` if unspecified.
|
||||
|
||||
## [1.0.0]
|
||||
Initial release.
|
||||
@@ -1,9 +0,0 @@
|
||||
# Dialogue Speaker Data Schema Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.0]
|
||||
Initial release.
|
||||
@@ -7,7 +7,7 @@ import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class AlbumRegistry extends BaseRegistry<Album, AlbumData> implements ISingleton implements DefaultRegistryImpl
|
||||
class AlbumRegistry extends BaseRegistry<Album, AlbumData, AlbumEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the album data format.
|
||||
@@ -23,3 +23,5 @@ class AlbumRegistry extends BaseRegistry<Album, AlbumData> implements ISingleton
|
||||
super('ALBUM', 'ui/freeplay/albums', ALBUM_DATA_VERSION_RULE);
|
||||
}
|
||||
}
|
||||
|
||||
typedef AlbumEntryParams = {}
|
||||
|
||||
@@ -8,7 +8,7 @@ import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData> implements ISingleton implements DefaultRegistryImpl
|
||||
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData, PlayerEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the stage data format.
|
||||
@@ -147,3 +147,5 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData> impleme
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
typedef PlayerEntryParams = {}
|
||||
|
||||
@@ -7,7 +7,8 @@ import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleData> implements ISingleton implements DefaultRegistryImpl
|
||||
class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleData, FreeplayStyleEntryParams> implements ISingleton
|
||||
implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the style data format.
|
||||
@@ -23,3 +24,5 @@ class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleDat
|
||||
super('FREEPLAYSTYLE', 'ui/freeplay/styles', FREEPLAYSTYLE_DATA_VERSION_RULE);
|
||||
}
|
||||
}
|
||||
|
||||
typedef FreeplayStyleEntryParams = {}
|
||||
|
||||
@@ -165,7 +165,7 @@ typedef NoteStyleAssetData<T> =
|
||||
var assetPath:String;
|
||||
|
||||
/**
|
||||
* The scale to render the prop at.
|
||||
* The scale to render the note at.
|
||||
* @default 1.0
|
||||
*/
|
||||
@:default(1.0)
|
||||
@@ -181,7 +181,7 @@ typedef NoteStyleAssetData<T> =
|
||||
var offsets:Null<Array<Float>>;
|
||||
|
||||
/**
|
||||
* If true, the prop is a pixel sprite, and will be rendered without anti-aliasing.
|
||||
* If true, the note is a pixel sprite, and will be rendered without anti-aliasing.
|
||||
*/
|
||||
@:default(false)
|
||||
@:optional
|
||||
|
||||
@@ -7,7 +7,7 @@ import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData> implements ISingleton implements DefaultRegistryImpl
|
||||
class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData, NoteStyleEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the note style data format.
|
||||
@@ -30,3 +30,5 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData> implement
|
||||
return notestyle;
|
||||
}
|
||||
}
|
||||
|
||||
typedef NoteStyleEntryParams = {}
|
||||
|
||||
@@ -1117,6 +1117,23 @@ class SongNoteDataRaw implements ICloneable<SongNoteDataRaw>
|
||||
return 'SongNoteData(${this.time}ms, ' + (this.length > 0 ? '[${this.length}ms hold]' : '') + ' ${this.data}'
|
||||
+ (this.kind != '' ? ' [kind: ${this.kind}])' : ')');
|
||||
}
|
||||
|
||||
public function buildTooltip():String
|
||||
{
|
||||
if ((this.kind?.length ?? 0) == 0) return "";
|
||||
|
||||
var result:String = 'Kind: ${this.kind}';
|
||||
if (this.params.length == 0) return result;
|
||||
|
||||
result += "\nParams:";
|
||||
|
||||
for (param in params)
|
||||
{
|
||||
result += '\n- ${param.name}: ${param.value}';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,8 +15,7 @@ import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
using funkin.data.song.migrator.SongDataMigrator;
|
||||
|
||||
@:nullSafety
|
||||
class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleton implements DefaultRegistryImpl
|
||||
@:nullSafety class SongRegistry extends BaseRegistry<Song, SongMetadata, SongEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the stage data format.
|
||||
@@ -37,6 +36,8 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
|
||||
|
||||
public static var DEFAULT_GENERATEDBY(get, never):String;
|
||||
|
||||
public var scriptedSongVariations:Map<String, Song> = new Map<String, Song>();
|
||||
|
||||
static function get_DEFAULT_GENERATEDBY():String
|
||||
{
|
||||
return '${Constants.TITLE} - ${Constants.VERSION}';
|
||||
@@ -63,9 +64,17 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
log('Successfully created scripted entry (${entryCls} = ${entry.id})');
|
||||
entries.set(entry.id, entry);
|
||||
scriptedEntryIds.set(entry.id, entryCls);
|
||||
if (entry.variation != null)
|
||||
{
|
||||
scriptedSongVariations.set('${entry.id}:${entry.variation}', entry);
|
||||
log('Successfully created scripted entry (${entryCls} = ${entry.id}, ${entry.variation})');
|
||||
}
|
||||
else
|
||||
{
|
||||
entries.set(entry.id, entry);
|
||||
scriptedEntryIds.set(entry.id, entryCls);
|
||||
log('Successfully created scripted entry (${entryCls} = ${entry.id})');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -120,6 +129,28 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
|
||||
return parseEntryMetadataRaw(contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* We override `fetchEntry` to handle song variations!
|
||||
*/
|
||||
public override function fetchEntry(id:String, ?params:SongEntryParams):Null<Song>
|
||||
{
|
||||
var variation:String = params?.variation ?? Constants.DEFAULT_VARIATION;
|
||||
|
||||
if (variation != Constants.DEFAULT_VARIATION)
|
||||
{
|
||||
if (scriptedSongVariations.exists('${id}:${variation}'))
|
||||
{
|
||||
var variationSongScript:Null<Song> = scriptedSongVariations.get('${id}:${variation}');
|
||||
if (variationSongScript != null)
|
||||
{
|
||||
return variationSongScript;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.fetchEntry(id, params);
|
||||
}
|
||||
|
||||
public function parseEntryMetadata(id:String, ?variation:String):Null<SongMetadata>
|
||||
{
|
||||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
@@ -517,3 +548,11 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
|
||||
return allDifficulties;
|
||||
}
|
||||
}
|
||||
|
||||
typedef SongEntryParams =
|
||||
{
|
||||
/**
|
||||
* The variation ID for the song.
|
||||
*/
|
||||
var variation:String;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ class ChartManifestData
|
||||
*/
|
||||
public static final CHART_MANIFEST_DATA_VERSION:thx.semver.Version = "1.0.0";
|
||||
|
||||
public static final invalidIdRegex:EReg = ~/[\/\\:*?"<>|]/g;
|
||||
|
||||
@:default(funkin.data.song.importer.ChartManifestData.CHART_MANIFEST_DATA_VERSION)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
@@ -19,7 +21,11 @@ class ChartManifestData
|
||||
* The internal song ID for this chart.
|
||||
* The metadata and chart data file names are derived from this.
|
||||
*/
|
||||
public var songId:String;
|
||||
public var songId(default, set):String;
|
||||
public function set_songId(value:String):String
|
||||
{
|
||||
return songId = invalidIdRegex.replace(value.trim(), '');
|
||||
}
|
||||
|
||||
public function new(songId:String)
|
||||
{
|
||||
|
||||
@@ -120,6 +120,11 @@ class LegacyNote
|
||||
|
||||
public inline function getKind():String
|
||||
{
|
||||
return this.alt ? 'alt' : 'normal';
|
||||
return this.alt ? 'alt' : '';
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
return 'LegacyNote(${this.time}, ${this.data}, ${this.length}, ${this.alt})';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class FNFLegacyImporter
|
||||
{
|
||||
trace('Migrating song metadata from FNF Legacy.');
|
||||
|
||||
var songMetadata:SongMetadata = new SongMetadata('Import', Constants.DEFAULT_ARTIST, 'default');
|
||||
var songMetadata:SongMetadata = new SongMetadata('Import', Constants.DEFAULT_ARTIST, Constants.DEFAULT_CHARTER, Constants.DEFAULT_VARIATION);
|
||||
|
||||
// Set generatedBy string for debugging.
|
||||
songMetadata.generatedBy = 'Chart Editor Import (FNF Legacy)';
|
||||
@@ -95,8 +95,8 @@ class FNFLegacyImporter
|
||||
switch (songData.song.speed)
|
||||
{
|
||||
case Left(speed):
|
||||
// All difficulties will use the one scroll speed.
|
||||
songChartData.scrollSpeed.set('default', speed);
|
||||
// Sets the scroll speed for the difficulty.
|
||||
songChartData.scrollSpeed.set(difficulty, speed);
|
||||
case Right(speeds):
|
||||
if (speeds.easy != null) songChartData.scrollSpeed.set('easy', speeds.easy);
|
||||
if (speeds.normal != null) songChartData.scrollSpeed.set('normal', speeds.normal);
|
||||
|
||||
@@ -24,7 +24,7 @@ class SongDataMigrator
|
||||
|
||||
public static function migrate_SongMetadata_v2_1_0(input:SongData_v2_1_0.SongMetadata_v2_1_0):SongMetadata
|
||||
{
|
||||
var result:SongMetadata = new SongMetadata(input.songName, input.artist, input.variation);
|
||||
var result:SongMetadata = new SongMetadata(input.songName, input.artist, Constants.DEFAULT_CHARTER, input.variation);
|
||||
result.version = SongRegistry.SONG_METADATA_VERSION;
|
||||
result.timeFormat = input.timeFormat;
|
||||
result.divisions = input.divisions;
|
||||
@@ -66,7 +66,7 @@ class SongDataMigrator
|
||||
|
||||
public static function migrate_SongMetadata_v2_0_0(input:SongData_v2_0_0.SongMetadata_v2_0_0):SongMetadata
|
||||
{
|
||||
var result:SongMetadata = new SongMetadata(input.songName, input.artist, input.variation);
|
||||
var result:SongMetadata = new SongMetadata(input.songName, input.artist, Constants.DEFAULT_CHARTER, input.variation);
|
||||
result.version = SongRegistry.SONG_METADATA_VERSION;
|
||||
result.timeFormat = input.timeFormat;
|
||||
result.divisions = input.divisions;
|
||||
|
||||
@@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class StageRegistry extends BaseRegistry<Stage, StageData> implements ISingleton implements DefaultRegistryImpl
|
||||
class StageRegistry extends BaseRegistry<Stage, StageData, StageEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the stage data format.
|
||||
@@ -22,3 +22,5 @@ class StageRegistry extends BaseRegistry<Stage, StageData> implements ISingleton
|
||||
super('STAGE', 'stages', STAGE_DATA_VERSION_RULE);
|
||||
}
|
||||
}
|
||||
|
||||
typedef StageEntryParams = {}
|
||||
|
||||
@@ -5,7 +5,7 @@ import funkin.ui.transition.stickers.StickerPack;
|
||||
import funkin.ui.transition.stickers.ScriptedStickerPack;
|
||||
|
||||
@:nullSafety
|
||||
class StickerRegistry extends BaseRegistry<StickerPack, StickerData>
|
||||
class StickerRegistry extends BaseRegistry<StickerPack, StickerData, StickerEntryParams>
|
||||
{
|
||||
/**
|
||||
* The current version string for the sticker pack data format.
|
||||
@@ -90,3 +90,5 @@ class StickerRegistry extends BaseRegistry<StickerPack, StickerData>
|
||||
return ScriptedStickerPack.listScriptClasses();
|
||||
}
|
||||
}
|
||||
|
||||
typedef StickerEntryParams = {}
|
||||
|
||||
@@ -7,7 +7,7 @@ import funkin.util.tools.ISingleton;
|
||||
import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
@:nullSafety
|
||||
class LevelRegistry extends BaseRegistry<Level, LevelData> implements ISingleton implements DefaultRegistryImpl
|
||||
class LevelRegistry extends BaseRegistry<Level, LevelData, LevelEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the level data format.
|
||||
@@ -56,3 +56,5 @@ class LevelRegistry extends BaseRegistry<Level, LevelData> implements ISingleton
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
typedef LevelEntryParams = {}
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package funkin.external.android;
|
||||
|
||||
#if android
|
||||
import lime.system.JNI;
|
||||
import flixel.util.FlxSignal;
|
||||
|
||||
/**
|
||||
* A Utility class to handle Android API level callbacks and events.
|
||||
*/
|
||||
@:unreflective
|
||||
class CallbackUtil
|
||||
{
|
||||
/**
|
||||
* The result code for `DATA_FOLDER_CLOSED` activity.
|
||||
*/
|
||||
public static var DATA_FOLDER_CLOSED(get, never):Int;
|
||||
|
||||
@:noCompletion
|
||||
static function get_DATA_FOLDER_CLOSED():Int
|
||||
{
|
||||
final field:Null<Dynamic> = JNIUtil.createStaticField('funkin/extensions/CallbackUtil', 'DATA_FOLDER_CLOSED', 'I');
|
||||
|
||||
return field != null ? field.get() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal triggered when an activity result is received.
|
||||
*
|
||||
* First argument is the request code, second is the result code.
|
||||
*/
|
||||
public static var onActivityResult:FlxTypedSignal<Int->Int->Void> = new FlxTypedSignal<Int->Int->Void>();
|
||||
|
||||
/**
|
||||
* Initializes the callback utility.
|
||||
*/
|
||||
public static function init():Void
|
||||
{
|
||||
final initCallBackJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/extensions/CallbackUtil', 'initCallBack', '(Lorg/haxe/lime/HaxeObject;)V');
|
||||
|
||||
if (initCallBackJNI != null)
|
||||
{
|
||||
initCallBackJNI(new CallbackHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class to handle native callback events.
|
||||
*/
|
||||
class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
|
||||
{
|
||||
@:allow(funkin.external.android.CallbackUtil)
|
||||
function new():Void {}
|
||||
|
||||
/**
|
||||
* Handles the activity result callback from native code.
|
||||
*
|
||||
* @param requestCode The request code of the acitivty.
|
||||
* @param resultCode The result code of the acitivty.
|
||||
*/
|
||||
@:keep
|
||||
#if (lime >= "8.0.0")
|
||||
@:runOnMainThread
|
||||
#end
|
||||
public function onActivityResult(requestCode:Int, resultCode:Int):Void
|
||||
{
|
||||
if (CallbackUtil.onActivityResult != null) CallbackUtil.onActivityResult.dispatch(requestCode, resultCode);
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,23 @@
|
||||
package funkin.external.android;
|
||||
|
||||
#if android
|
||||
/**
|
||||
* A Utility class to manage the Application's Data folder on Android.
|
||||
*/
|
||||
@:unreflective
|
||||
class DataFolderUtil
|
||||
{
|
||||
/**
|
||||
* Opens the data folder on an Android device using JNI.
|
||||
*/
|
||||
public static function openDataFolder():Void
|
||||
{
|
||||
final openDataFolderJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/util/DataFolderUtil', 'openDataFolder', '(I)V');
|
||||
|
||||
if (openDataFolderJNI != null)
|
||||
{
|
||||
openDataFolderJNI(CallbackUtil.DATA_FOLDER_CLOSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package funkin.external.android;
|
||||
|
||||
#if android
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* A utility class for caching JNI method and field references.
|
||||
*/
|
||||
class JNIUtil
|
||||
{
|
||||
@:noCompletion
|
||||
private static var staticMethodCache:Map<String, Dynamic> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var memberMethodCache:Map<String, Dynamic> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var staticFieldCache:Map<String, JNIStaticField> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var memberFieldCache:Map<String, JNIMemberField> = [];
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached static method reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the method.
|
||||
* @param methodName The name of the method.
|
||||
* @param signature The method signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A dynamic reference to the static method.
|
||||
*/
|
||||
public static function createStaticMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null<Dynamic>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$methodName::$signature';
|
||||
|
||||
if (cache && !staticMethodCache.exists(key)) staticMethodCache.set(key, JNI.createStaticMethod(className, methodName, signature));
|
||||
else if (!cache) return JNI.createStaticMethod(className, methodName, signature);
|
||||
|
||||
return staticMethodCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached member method reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the method.
|
||||
* @param methodName The name of the method.
|
||||
* @param signature The method signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A dynamic reference to the member method.
|
||||
*/
|
||||
public static function createMemberMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null<Dynamic>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$methodName::$signature';
|
||||
|
||||
if (cache && !memberMethodCache.exists(key)) memberMethodCache.set(key, JNI.createMemberMethod(className, methodName, signature));
|
||||
else if (!cache) return JNI.createMemberMethod(className, methodName, signature);
|
||||
|
||||
return memberMethodCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached static field reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the field.
|
||||
* @param fieldName The name of the field.
|
||||
* @param signature The field signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A reference to the static field.
|
||||
*/
|
||||
public static function createStaticField(className:String, fieldName:String, signature:String, cache:Bool = true):Null<JNIStaticField>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$fieldName::$signature';
|
||||
|
||||
if (cache && !staticFieldCache.exists(key)) staticFieldCache.set(key, JNI.createStaticField(className, fieldName, signature));
|
||||
else if (!cache) return JNI.createStaticField(className, fieldName, signature);
|
||||
|
||||
return staticFieldCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached member field reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the field.
|
||||
* @param fieldName The name of the field.
|
||||
* @param signature The field signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A reference to the member field.
|
||||
*/
|
||||
public static function createMemberField(className:String, fieldName:String, signature:String, cache:Bool = true):Null<JNIMemberField>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$fieldName::$signature';
|
||||
|
||||
if (cache && !memberFieldCache.exists(key)) memberFieldCache.set(key, JNI.createMemberField(className, fieldName, signature));
|
||||
else if (!cache) return JNI.createMemberField(className, fieldName, signature);
|
||||
|
||||
return memberFieldCache.get(key);
|
||||
}
|
||||
}
|
||||
#end
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package funkin.external.android;
|
||||
|
||||
#if android
|
||||
import lime.math.Rectangle;
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* A Utility class to get Android screen related informations.
|
||||
*/
|
||||
@:unreflective
|
||||
class ScreenUtil
|
||||
{
|
||||
/**
|
||||
* Retrieves the dimensions of display cutouts (such as notches) on Android devices.
|
||||
*
|
||||
* @return An array of `Rectangle` objects, each representing a display cutout's position and size.
|
||||
*/
|
||||
public static function getCutoutDimensions():Array<Rectangle>
|
||||
{
|
||||
final getCutoutDimensionsJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/util/ScreenUtil', 'getCutoutDimensions', '()[Landroid/graphics/Rect;');
|
||||
|
||||
if (getCutoutDimensionsJNI != null)
|
||||
{
|
||||
final rectangles:Array<Rectangle> = [];
|
||||
|
||||
for (rectangle in cast(getCutoutDimensionsJNI(), Array<Dynamic>))
|
||||
{
|
||||
if (rectangle == null) continue;
|
||||
|
||||
final topJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'top', 'I');
|
||||
final leftJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'left', 'I');
|
||||
final rightJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'right', 'I');
|
||||
final bottomJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'bottom', 'I');
|
||||
|
||||
if (topJNI != null && leftJNI != null && rightJNI != null && bottomJNI != null)
|
||||
{
|
||||
final top:Int = topJNI.get(rectangle);
|
||||
final left:Int = leftJNI.get(rectangle);
|
||||
final right:Int = rightJNI.get(rectangle);
|
||||
final bottom:Int = bottomJNI.get(rectangle);
|
||||
|
||||
rectangles.push(new Rectangle(left, top, right - left, bottom - top));
|
||||
}
|
||||
}
|
||||
|
||||
return rectangles;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,36 @@
|
||||
package funkin.extensions;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
import org.haxe.lime.HaxeObject;
|
||||
|
||||
public class CallbackUtil extends Extension
|
||||
{
|
||||
/**
|
||||
* Constant representing the event when the data folder is closed.
|
||||
*/
|
||||
public static int DATA_FOLDER_CLOSED = 0x01;
|
||||
|
||||
private static HaxeObject haxeObject;
|
||||
|
||||
/**
|
||||
* Initializes the callback object for handling Haxe callbacks.
|
||||
*
|
||||
* @param haxeObject The HaxeObject instance to handle callbacks.
|
||||
*/
|
||||
public static void initCallBack(final HaxeObject haxeObject)
|
||||
{
|
||||
CallbackUtil.haxeObject = haxeObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (haxeObject != null)
|
||||
haxeObject.call2("onActivityResult", requestCode, resultCode);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package funkin.provider;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.graphics.Point;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.DocumentsContract.Document;
|
||||
import android.provider.DocumentsContract.Root;
|
||||
import android.provider.DocumentsProvider;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* @see https://github.com/termux/termux-app/blob/7bceab88e2272f961d1b94ef736f1a9e20173247/app/src/main/java/com/termux/filepicker/TermuxDocumentsProvider.java
|
||||
*/
|
||||
public class DataFolderProvider extends DocumentsProvider
|
||||
{
|
||||
private static File BASE_DIR;
|
||||
|
||||
private static String BASE_DIR_PATH;
|
||||
|
||||
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
|
||||
Root.COLUMN_ROOT_ID,
|
||||
Root.COLUMN_MIME_TYPES,
|
||||
Root.COLUMN_FLAGS,
|
||||
Root.COLUMN_ICON,
|
||||
Root.COLUMN_TITLE,
|
||||
Root.COLUMN_SUMMARY,
|
||||
Root.COLUMN_DOCUMENT_ID,
|
||||
Root.COLUMN_AVAILABLE_BYTES
|
||||
};
|
||||
|
||||
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
|
||||
Document.COLUMN_DOCUMENT_ID,
|
||||
Document.COLUMN_MIME_TYPE,
|
||||
Document.COLUMN_DISPLAY_NAME,
|
||||
Document.COLUMN_LAST_MODIFIED,
|
||||
Document.COLUMN_FLAGS,
|
||||
Document.COLUMN_SIZE
|
||||
};
|
||||
|
||||
@Override
|
||||
public Cursor queryRoots(String[] projection)
|
||||
{
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION);
|
||||
|
||||
if (BASE_DIR == null)
|
||||
return result;
|
||||
|
||||
final MatrixCursor.RowBuilder row = result.newRow();
|
||||
|
||||
row.add(Root.COLUMN_ROOT_ID, getDocIdForFile(BASE_DIR));
|
||||
row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(BASE_DIR));
|
||||
row.add(Root.COLUMN_SUMMARY, "Data Folder");
|
||||
row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD);
|
||||
row.add(Root.COLUMN_TITLE, "::APP_TITLE::");
|
||||
row.add(Root.COLUMN_MIME_TYPES, "*/*");
|
||||
row.add(Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace());
|
||||
::if (APP_PACKAGE != "")::
|
||||
row.add(Root.COLUMN_ICON, ::APP_PACKAGE::.R.mipmap.ic_launcher);
|
||||
::end::
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException
|
||||
{
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||
includeFile(result, documentId, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException
|
||||
{
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||
|
||||
File parent = getFileForDocId(parentDocumentId);
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
File[] children = null;
|
||||
|
||||
try {
|
||||
children = parent.listFiles();
|
||||
} catch (SecurityException e) {
|
||||
children = new File[0];
|
||||
}
|
||||
|
||||
if (children != null)
|
||||
{
|
||||
for (File file : children)
|
||||
includeFile(result, null, file);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal) throws FileNotFoundException
|
||||
{
|
||||
return ParcelFileDescriptor.open(getFileForDocId(documentId), ParcelFileDescriptor.parseMode(mode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException
|
||||
{
|
||||
final File file = getFileForDocId(documentId);
|
||||
final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
return new AssetFileDescriptor(pfd, 0, file.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate()
|
||||
{
|
||||
BASE_DIR = getContext().getExternalFilesDir(null);
|
||||
|
||||
if (BASE_DIR == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
BASE_DIR_PATH = BASE_DIR.getCanonicalPath();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
BASE_DIR_PATH = BASE_DIR.getAbsolutePath();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException
|
||||
{
|
||||
File parentFile = getFileForDocId(parentDocumentId);
|
||||
|
||||
File newFile = new File(parentFile, displayName);
|
||||
|
||||
int noConflictId = 2;
|
||||
|
||||
while (newFile.exists())
|
||||
{
|
||||
newFile = new File(parentFile, displayName + " (" + noConflictId++ + ")");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boolean succeeded;
|
||||
|
||||
if (Document.MIME_TYPE_DIR.equals(mimeType))
|
||||
succeeded = newFile.mkdir();
|
||||
else
|
||||
succeeded = newFile.createNewFile();
|
||||
|
||||
if (!succeeded)
|
||||
throw new FileNotFoundException("Failed to create document with id " + newFile.getPath());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new FileNotFoundException("Failed to create document with id " + newFile.getPath());
|
||||
}
|
||||
|
||||
return newFile.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDocument(String documentId) throws FileNotFoundException
|
||||
{
|
||||
if (!deleteRecursive(getFileForDocId(documentId)))
|
||||
throw new FileNotFoundException("Failed to delete document with id " + documentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDocumentType(String documentId) throws FileNotFoundException
|
||||
{
|
||||
return getMimeType(getFileForDocId(documentId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor querySearchDocuments(String rootId, String query, String[] projection) throws FileNotFoundException
|
||||
{
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||
|
||||
final LinkedList<File> pending = new LinkedList<>();
|
||||
|
||||
pending.add(getFileForDocId(rootId));
|
||||
|
||||
final int MAX_SEARCH_RESULTS = 50;
|
||||
|
||||
while (!pending.isEmpty() && result.getCount() < MAX_SEARCH_RESULTS)
|
||||
{
|
||||
final File file = pending.removeFirst();
|
||||
|
||||
boolean isInsideHome;
|
||||
|
||||
try
|
||||
{
|
||||
isInsideHome = file.getCanonicalPath().startsWith(BASE_DIR_PATH);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
isInsideHome = true;
|
||||
}
|
||||
|
||||
if (isInsideHome)
|
||||
{
|
||||
if (file.isDirectory())
|
||||
{
|
||||
try {
|
||||
Collections.addAll(pending, file.listFiles());
|
||||
} catch (SecurityException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
else if (file.getName().toLowerCase().contains(query))
|
||||
{
|
||||
includeFile(result, null, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildDocument(String parentDocumentId, String documentId)
|
||||
{
|
||||
try
|
||||
{
|
||||
File parent = getFileForDocId(parentDocumentId).getCanonicalFile();
|
||||
File child = getFileForDocId(documentId).getCanonicalFile();
|
||||
return child.getPath().startsWith(parent.getPath() + "/");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deleteRecursive(File file)
|
||||
{
|
||||
if (file.isDirectory())
|
||||
{
|
||||
File[] children = file.listFiles();
|
||||
|
||||
if (children != null)
|
||||
{
|
||||
for (File child : children)
|
||||
{
|
||||
if (!deleteRecursive(child))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
private static String getDocIdForFile(File file)
|
||||
{
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
private static File getFileForDocId(String docId) throws FileNotFoundException
|
||||
{
|
||||
if (BASE_DIR == null)
|
||||
throw new FileNotFoundException("Base directory not available");
|
||||
|
||||
final File f = (docId == null || docId.isEmpty()) ? BASE_DIR : new File(docId);
|
||||
|
||||
if (!f.exists())
|
||||
throw new FileNotFoundException(f.getAbsolutePath() + " not found");
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
private static String getMimeType(File file)
|
||||
{
|
||||
if (file == null || file.isDirectory())
|
||||
return Document.MIME_TYPE_DIR;
|
||||
|
||||
String name = file.getName();
|
||||
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
|
||||
if (lastDot >= 0)
|
||||
{
|
||||
String extension = name.substring(lastDot + 1).toLowerCase();
|
||||
|
||||
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
|
||||
if (mime != null)
|
||||
return mime;
|
||||
}
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
private void includeFile(MatrixCursor result, String docId, File file) throws FileNotFoundException
|
||||
{
|
||||
if (docId == null)
|
||||
docId = getDocIdForFile(file);
|
||||
else
|
||||
file = getFileForDocId(docId);
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if (file.isDirectory())
|
||||
{
|
||||
if (file.canWrite())
|
||||
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
|
||||
}
|
||||
else if (file.canWrite())
|
||||
{
|
||||
flags |= Document.FLAG_SUPPORTS_WRITE;
|
||||
}
|
||||
|
||||
if (file.getParentFile() != null && file.getParentFile().canWrite())
|
||||
flags |= Document.FLAG_SUPPORTS_DELETE;
|
||||
|
||||
final String mimeType = getMimeType(file);
|
||||
|
||||
if (mimeType.startsWith("image/"))
|
||||
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
|
||||
|
||||
final MatrixCursor.RowBuilder row = result.newRow();
|
||||
row.add(Document.COLUMN_DOCUMENT_ID, docId);
|
||||
row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
|
||||
row.add(Document.COLUMN_SIZE, file.length());
|
||||
row.add(Document.COLUMN_MIME_TYPE, mimeType);
|
||||
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
|
||||
row.add(Document.COLUMN_FLAGS, flags);
|
||||
::if (APP_PACKAGE != "")::
|
||||
row.add(Document.COLUMN_ICON, ::APP_PACKAGE::.R.mipmap.ic_launcher);
|
||||
::end::
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package funkin.util;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.content.pm.PackageInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
public class DataFolderUtil
|
||||
{
|
||||
/**
|
||||
* A method that opens the Application's data folder for browsing through the Storage Access Framework.
|
||||
* It's highly based on some code borrowed from Mterial Files
|
||||
* https://github.com/zhanghai/MaterialFiles
|
||||
*/
|
||||
public static void openDataFolder(int requestCode)
|
||||
{
|
||||
::if (APP_PACKAGE != "")::
|
||||
if (Extension.mainActivity != null)
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(DocumentsContract.buildRootUri("::APP_PACKAGE::.docprovider", ""), "vnd.android.document/directory");
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
Extension.mainActivity.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
::end::
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package funkin.util;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
public class ScreenUtil
|
||||
{
|
||||
public static Rect[] getCutoutDimensions()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
{
|
||||
if (Extension.mainActivity != null)
|
||||
{
|
||||
WindowInsets insets = Extension.mainActivity.getWindow().getDecorView().getRootWindowInsets();
|
||||
|
||||
if (insets != null)
|
||||
{
|
||||
DisplayCutout cutout = insets.getDisplayCutout();
|
||||
|
||||
if (cutout != null)
|
||||
{
|
||||
List<Rect> boundingRects = cutout.getBoundingRects();
|
||||
|
||||
if (boundingRects != null && !boundingRects.isEmpty())
|
||||
return boundingRects.toArray(new Rect[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Rect[0];
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -1,9 +1,10 @@
|
||||
package funkin.mobile.external;
|
||||
package funkin.external.ios;
|
||||
|
||||
#if ios
|
||||
/**
|
||||
* A Utility class to manage iOS audio.
|
||||
*/
|
||||
@:build(funkin.mobile.macros.LinkerMacro.xml('project/Build.xml'))
|
||||
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('AudioSession.hpp')
|
||||
@:unreflective
|
||||
extern class AudioSession
|
||||
@@ -13,3 +14,4 @@ extern class AudioSession
|
||||
@:native('setActive')
|
||||
static function setActive(active:Bool):Void;
|
||||
}
|
||||
#end
|
||||
+7
-4
@@ -1,15 +1,18 @@
|
||||
package funkin.mobile.external;
|
||||
package funkin.external.ios;
|
||||
|
||||
#if ios
|
||||
/**
|
||||
* A Utility class to get iOS screen related informations.
|
||||
*/
|
||||
@:build(funkin.mobile.macros.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('ScreenUtils.hpp')
|
||||
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('ScreenUtil.hpp')
|
||||
@:unreflective
|
||||
extern class ScreenUtils
|
||||
extern class ScreenUtil
|
||||
{
|
||||
@:native('getSafeAreaInsets')
|
||||
static function getSafeAreaInsets(top:cpp.RawPointer<Float>, bottom:cpp.RawPointer<Float>, left:cpp.RawPointer<Float>, right:cpp.RawPointer<Float>):Void;
|
||||
|
||||
@:native('getScreenSize')
|
||||
static function getScreenSize(width:cpp.RawPointer<Float>, height:cpp.RawPointer<Float>):Void;
|
||||
}
|
||||
#end
|
||||
Vendored
+1
-1
@@ -13,7 +13,7 @@
|
||||
<files id="external-ios" dir="${this_dir}/ios" if="iphoneos || iphonesim">
|
||||
<compilerflag value="-I${this_dir}/ios/include" />
|
||||
|
||||
<file name="src/ScreenUtils.mm" />
|
||||
<file name="src/ScreenUtil.mm" />
|
||||
<file name="src/AudioSession.mm" />
|
||||
</files>
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#import "ScreenUtils.hpp"
|
||||
#import "ScreenUtil.hpp"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package funkin.external.windows;
|
||||
|
||||
#if (windows && cpp)
|
||||
/**
|
||||
* This class provides handling for Windows API-related functions.
|
||||
*/
|
||||
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('winapi.hpp')
|
||||
extern class WinAPI
|
||||
{
|
||||
/**
|
||||
* Shows a message box with an error icon.
|
||||
*
|
||||
* @param message The message to display.
|
||||
* @param title The title of the message box.
|
||||
*/
|
||||
@:native('WINAPI_ShowError')
|
||||
static function showError(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
|
||||
|
||||
/**
|
||||
* Shows a message box with a warning icon.
|
||||
*
|
||||
* @param message The message to display.
|
||||
* @param title The title of the message box.
|
||||
*/
|
||||
@:native('WINAPI_ShowWarning')
|
||||
static function showWarning(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
|
||||
|
||||
/**
|
||||
* Shows a message box with an information icon.
|
||||
*
|
||||
* @param message The message to display.
|
||||
* @param title The title of the message box.
|
||||
*/
|
||||
@:native('WINAPI_ShowInformation')
|
||||
static function showInformation(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
|
||||
|
||||
/**
|
||||
* Shows a message box with a question icon.
|
||||
*
|
||||
* @param message The message to display.
|
||||
* @param title The title of the message box.
|
||||
*/
|
||||
@:native('WINAPI_ShowQuestion')
|
||||
static function showQuestion(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
|
||||
|
||||
/**
|
||||
* Disables the "Report to Microsoft" dialog that appears when the application crashes.
|
||||
*/
|
||||
@:native('WINAPI_DisableErrorReporting')
|
||||
static function disableErrorReporting():Void;
|
||||
|
||||
/**
|
||||
* Disables Windows ghosting, which prevents the system from marking unresponsive windows as "Not Responding."
|
||||
*/
|
||||
@:native('WINAPI_DisableWindowsGhosting')
|
||||
static function disableWindowsGhosting():Void;
|
||||
|
||||
/**
|
||||
* Sets the dark mode for the active window.
|
||||
*
|
||||
* @param enable A boolean value indicating whether to enable (true) or disable (false) dark mode.
|
||||
*/
|
||||
@:native('WINAPI_SetDarkMode')
|
||||
static function setDarkMode(enable:Bool):Void;
|
||||
|
||||
/**
|
||||
* Checks if the system is using dark mode.
|
||||
*
|
||||
* @return True if system is in dark mode, false otherwise.
|
||||
*/
|
||||
@:native('WINAPI_IsSystemDarkMode')
|
||||
static function isSystemDarkMode():Bool;
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xml>
|
||||
<pragma once="true" />
|
||||
|
||||
<files id="haxe">
|
||||
<compilerflag value="-I${this_dir}/include" />
|
||||
</files>
|
||||
|
||||
<files id="__main__">
|
||||
<compilerflag value="-I${this_dir}/include" />
|
||||
</files>
|
||||
|
||||
<files id="winapi">
|
||||
<compilerflag value="-I${this_dir}/include" />
|
||||
|
||||
<file name="${this_dir}/src/winapi.cpp" />
|
||||
</files>
|
||||
|
||||
<target id="haxe">
|
||||
<section if="mingw">
|
||||
<lib name="-luser32" />
|
||||
<lib name="-lkernel32" />
|
||||
<lib name="-ldwmapi" />
|
||||
<lib name="-ladvapi32" />
|
||||
</section>
|
||||
|
||||
<section if="windows" unless="mingw">
|
||||
<lib name="user32.lib" />
|
||||
<lib name="kernel32.lib" />
|
||||
<lib name="dwmapi.lib" />
|
||||
<lib name="advapi32.lib" />
|
||||
</section>
|
||||
|
||||
<files id="winapi" />
|
||||
</target>
|
||||
</xml>
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief Shows an error message box
|
||||
* @param message The error message to display
|
||||
* @param title The title of the message box
|
||||
*/
|
||||
void WINAPI_ShowError(const char *message, const char *title);
|
||||
|
||||
/**
|
||||
* @brief Shows a warning message box
|
||||
* @param message The warning message to display
|
||||
* @param title The title of the message box
|
||||
*/
|
||||
void WINAPI_ShowWarning(const char *message, const char *title);
|
||||
|
||||
/**
|
||||
* @brief Shows an information message box
|
||||
* @param message The information message to display
|
||||
* @param title The title of the message box
|
||||
*/
|
||||
void WINAPI_ShowInformation(const char *message, const char *title);
|
||||
|
||||
/**
|
||||
* @brief Shows a question message box with OK/Cancel buttons
|
||||
* @param message The question message to display
|
||||
* @param title The title of the message box
|
||||
*/
|
||||
void WINAPI_ShowQuestion(const char *message, const char *title);
|
||||
|
||||
/**
|
||||
* @brief Disables Windows error reporting dialogs
|
||||
*/
|
||||
void WINAPI_DisableErrorReporting();
|
||||
|
||||
/**
|
||||
* @brief Disables Windows ghosting for the current process
|
||||
*/
|
||||
void WINAPI_DisableWindowsGhosting();
|
||||
|
||||
/**
|
||||
* @brief Sets dark mode for the active window
|
||||
* @param enable True to enable dark mode, false to disable
|
||||
*/
|
||||
void WINAPI_SetDarkMode(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Checks if the system is using dark mode
|
||||
* @return True if system is in dark mode, false otherwise
|
||||
*/
|
||||
bool WINAPI_IsSystemDarkMode();
|
||||
@@ -0,0 +1,75 @@
|
||||
#define WIN32_LEAN_AND_MEAN // Excludes rarely-used APIs like cryptography, DDE, RPC, and shell functions, reducing compile time and binary size.
|
||||
#define NOMINMAX // Prevents Windows from defining min() and max() macros, which can conflict with standard C++ functions.
|
||||
#define NOCRYPT // Excludes Cryptographic APIs, such as Encrypt/Decrypt functions.
|
||||
#define NOCOMM // Excludes serial communication APIs, such as COM port handling.
|
||||
#define NOKANJI // Excludes Kanji character set support (not needed unless working with Japanese text processing).
|
||||
#define NOHELP // Excludes Windows Help APIs, removing functions related to WinHelp and other help systems.
|
||||
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <dwmapi.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void WINAPI_ShowError(const char *message, const char *title)
|
||||
{
|
||||
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONERROR);
|
||||
}
|
||||
|
||||
void WINAPI_ShowWarning(const char *message, const char *title)
|
||||
{
|
||||
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
|
||||
void WINAPI_ShowInformation(const char *message, const char *title)
|
||||
{
|
||||
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
|
||||
void WINAPI_ShowQuestion(const char *message, const char *title)
|
||||
{
|
||||
MessageBox(GetActiveWindow(), message, title, MB_OKCANCEL | MB_ICONQUESTION);
|
||||
}
|
||||
|
||||
void WINAPI_DisableErrorReporting()
|
||||
{
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||
}
|
||||
|
||||
void WINAPI_DisableWindowsGhosting()
|
||||
{
|
||||
DisableProcessWindowsGhosting();
|
||||
}
|
||||
|
||||
void WINAPI_SetDarkMode(bool enable)
|
||||
{
|
||||
HWND window = GetActiveWindow();
|
||||
|
||||
int darkMode = enable ? 1 : 0;
|
||||
|
||||
if (DwmSetWindowAttribute(window, 20, &darkMode, sizeof(darkMode)) != S_OK)
|
||||
DwmSetWindowAttribute(window, 19, &darkMode, sizeof(darkMode));
|
||||
|
||||
UpdateWindow(window);
|
||||
}
|
||||
|
||||
bool WINAPI_IsSystemDarkMode()
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD dwValue = 0;
|
||||
DWORD dwSize = sizeof(DWORD);
|
||||
DWORD dwType = REG_DWORD;
|
||||
|
||||
if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
|
||||
{
|
||||
if (RegQueryValueEx(hKey, "AppsUseLightTheme", NULL, &dwType, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(hKey);
|
||||
return dwValue == 0;
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import flixel.math.FlxPoint;
|
||||
import flixel.graphics.frames.FlxFrame.FlxFrameAngle;
|
||||
import flixel.FlxCamera;
|
||||
import openfl.system.System;
|
||||
import funkin.FunkinMemory;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -23,20 +24,6 @@ using StringTools;
|
||||
@:nullSafety
|
||||
class FunkinSprite extends FlxSprite
|
||||
{
|
||||
/**
|
||||
* An internal list of all the textures cached with `cacheTexture`.
|
||||
* This excludes any temporary textures like those from `FlxText` or `makeSolidColor`.
|
||||
*/
|
||||
static var currentCachedTextures:Map<String, FlxGraphic> = [];
|
||||
|
||||
/**
|
||||
* An internal list of textures that were cached in the previous state.
|
||||
* We don't know whether we want to keep them cached or not.
|
||||
*/
|
||||
static var previousCachedTextures:Map<String, FlxGraphic> = [];
|
||||
|
||||
static var permanentCachedTextures:Map<String, FlxGraphic> = [];
|
||||
|
||||
/**
|
||||
* @param x Starting X position
|
||||
* @param y Starting Y position
|
||||
@@ -216,122 +203,40 @@ class FunkinSprite extends FlxSprite
|
||||
return FlxG.bitmap.get(key) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the texture with the given key is cached.
|
||||
* @param key The key of the texture to cache.
|
||||
*/
|
||||
@:deprecated("Use FunkinMemory.cacheTexture() instead")
|
||||
public static function cacheTexture(key:String):Void
|
||||
{
|
||||
// We don't want to cache the same texture twice.
|
||||
if (currentCachedTextures.exists(key)) return;
|
||||
|
||||
if (previousCachedTextures.exists(key))
|
||||
{
|
||||
// Move the graphic from the previous cache to the current cache.
|
||||
var graphic = previousCachedTextures.get(key);
|
||||
previousCachedTextures.remove(key);
|
||||
if (graphic != null) currentCachedTextures.set(key, graphic);
|
||||
return;
|
||||
}
|
||||
|
||||
// Else, texture is currently uncached.
|
||||
var graphic:FlxGraphic = FlxGraphic.fromAssetKey(key, false, null, true);
|
||||
if (graphic == null)
|
||||
{
|
||||
FlxG.log.warn('Failed to cache graphic: $key');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Successfully cached graphic: $key');
|
||||
graphic.persist = true;
|
||||
currentCachedTextures.set(key, graphic);
|
||||
}
|
||||
FunkinMemory.cacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.permanentCacheTexture() instead")
|
||||
public static function permanentCacheTexture(key:String):Void
|
||||
{
|
||||
// We don't want to cache the same texture twice.
|
||||
if (permanentCachedTextures.exists(key)) return;
|
||||
|
||||
// Else, texture is currently uncached.
|
||||
var graphic:FlxGraphic = FlxGraphic.fromAssetKey(key, false, null, true);
|
||||
if (graphic == null)
|
||||
{
|
||||
FlxG.log.warn('Failed to cache graphic: $key');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Successfully cached graphic: $key');
|
||||
graphic.persist = true;
|
||||
permanentCachedTextures.set(key, graphic);
|
||||
}
|
||||
|
||||
currentCachedTextures = permanentCachedTextures;
|
||||
@:privateAccess FunkinMemory.permanentCacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.cacheTexture() instead")
|
||||
public static function cacheSparrow(key:String):Void
|
||||
{
|
||||
cacheTexture(Paths.image(key));
|
||||
FunkinMemory.cacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.cacheTexture() instead")
|
||||
public static function cachePacker(key:String):Void
|
||||
{
|
||||
cacheTexture(Paths.image(key));
|
||||
FunkinMemory.cacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this, then `cacheTexture` to keep the textures we still need, then `purgeCache` to remove the textures that we won't be using anymore.
|
||||
*/
|
||||
@:deprecated("Use FunkinMemory.preparePurgeTextureCache() instead")
|
||||
public static function preparePurgeCache():Void
|
||||
{
|
||||
previousCachedTextures = currentCachedTextures;
|
||||
|
||||
for (graphicKey in previousCachedTextures.keys())
|
||||
{
|
||||
if (!permanentCachedTextures.exists(graphicKey)) continue;
|
||||
previousCachedTextures.remove(graphicKey);
|
||||
}
|
||||
|
||||
currentCachedTextures = permanentCachedTextures;
|
||||
FunkinMemory.preparePurgeTextureCache();
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.purgeCache() instead")
|
||||
public static function purgeCache():Void
|
||||
{
|
||||
// Everything that is in previousCachedTextures but not in currentCachedTextures should be destroyed.
|
||||
for (graphicKey in previousCachedTextures.keys())
|
||||
{
|
||||
var graphic = previousCachedTextures.get(graphicKey);
|
||||
if (graphic == null) continue;
|
||||
FlxG.bitmap.remove(graphic);
|
||||
graphic.destroy();
|
||||
previousCachedTextures.remove(graphicKey);
|
||||
}
|
||||
@:privateAccess
|
||||
if (FlxG.bitmap._cache == null)
|
||||
{
|
||||
@:privateAccess
|
||||
FlxG.bitmap._cache = new Map();
|
||||
System.gc();
|
||||
return;
|
||||
}
|
||||
|
||||
@:privateAccess
|
||||
for (key in FlxG.bitmap._cache.keys())
|
||||
{
|
||||
var obj:Null<FlxGraphic> = FlxG.bitmap.get(key);
|
||||
if (obj == null) continue;
|
||||
if (obj.persist) continue;
|
||||
if (permanentCachedTextures.exists(key)) continue;
|
||||
if (!(obj.useCount <= 0 || key.contains("characters") || key.contains("charSelect") || key.contains("results"))) continue;
|
||||
|
||||
FlxG.bitmap.removeKey(key);
|
||||
obj.destroy();
|
||||
}
|
||||
openfl.Assets.cache.clear("songs");
|
||||
openfl.Assets.cache.clear("sounds");
|
||||
openfl.Assets.cache.clear("music");
|
||||
|
||||
System.gc();
|
||||
FunkinMemory.purgeCache();
|
||||
}
|
||||
|
||||
static function isGraphicCached(graphic:FlxGraphic):Bool
|
||||
|
||||
@@ -71,7 +71,7 @@ class FlxAtlasSprite extends FlxAnimate
|
||||
onAnimationComplete.add(cleanupAnimation);
|
||||
|
||||
// This defaults the sprite to play the first animation in the atlas,
|
||||
// then pauses it. This ensures symbols are intialized properly.
|
||||
// then pauses it. This ensures symbols are initialized properly.
|
||||
this.anim.play('');
|
||||
this.anim.pause();
|
||||
|
||||
|
||||
@@ -136,6 +136,9 @@ class DropShadowScreenspace extends DropShadowShader
|
||||
// essentially just stole this from the AngleMask shader but repurposed it to smooth
|
||||
// the threshold because without any sort of smoothing it produces horrible edges
|
||||
float antialias(vec2 fragCoord, float curThreshold, bool useMask) {
|
||||
if (AA_STAGES == 0.0) {
|
||||
return intensityPass(fragCoord, curThreshold, useMask);
|
||||
}
|
||||
|
||||
// In GLSL 100, we need to use constant loop bounds
|
||||
// Well assume a reasonable maximum for AA_STAGES and use a fixed loop
|
||||
|
||||
@@ -362,6 +362,9 @@ class DropShadowShader extends FlxShader
|
||||
// essentially just stole this from the AngleMask shader but repurposed it to smooth
|
||||
// the threshold because without any sort of smoothing it produces horrible edges
|
||||
float antialias(vec2 fragCoord, float curThreshold, bool useMask) {
|
||||
if (AA_STAGES == 0.0) {
|
||||
return intensityPass(fragCoord, curThreshold, useMask);
|
||||
}
|
||||
|
||||
// In GLSL 100, we need to use constant loop bounds
|
||||
// Well assume a reasonable maximum for AA_STAGES and use a fixed loop
|
||||
|
||||
@@ -17,7 +17,7 @@ class PureColor extends FlxShader
|
||||
|
||||
function set_col(val:FlxColor):FlxColor
|
||||
{
|
||||
funnyColor.value = [val.red, val.green, val.blue, val.alpha];
|
||||
funnyColor.value = [val.redFloat, val.greenFloat, val.blueFloat, val.alphaFloat];
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class PureColor extends FlxShader
|
||||
vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
|
||||
|
||||
if (color.a > 0.0 && colSet)
|
||||
color = vec4(funnyColor.r, funnyColor.g, funnyColor.b, color.a);
|
||||
color = funnyColor * color.a;
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ class Controls extends FlxActionSet
|
||||
inline function get_VOLUME_MUTE()
|
||||
return _volume_mute.check();
|
||||
|
||||
public function new(name, scheme:KeyboardScheme = null)
|
||||
public function new(name, ?scheme:KeyboardScheme)
|
||||
{
|
||||
super(name);
|
||||
|
||||
@@ -372,7 +372,7 @@ class Controls extends FlxActionSet
|
||||
setKeyboardScheme(scheme, false);
|
||||
}
|
||||
|
||||
override function update()
|
||||
override function update():Void
|
||||
{
|
||||
super.update();
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ class ControlsHandler
|
||||
@:noCompletion
|
||||
private static function get_hasExternalInputDevice():Bool
|
||||
{
|
||||
return FlxG.gamepads.numActiveGamepads > 0;
|
||||
return FlxG.gamepads.numActiveGamepads > 0 #if android || extension.androidtools.Tools.isChromebook() #end;
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package funkin.mobile.macros;
|
||||
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.xml.Printer;
|
||||
import sys.FileSystem;
|
||||
|
||||
using haxe.io.Path;
|
||||
|
||||
/**
|
||||
* This class provides a macro to include an XML build file in the metadata of a Haxe class.
|
||||
* The file must be located relative to the directory of the Haxe class that uses this macro.
|
||||
*/
|
||||
@:nullSafety
|
||||
class LinkerMacro
|
||||
{
|
||||
/**
|
||||
* Adds an XML `<include>` element to the class's metadata, pointing to a specified build file.
|
||||
* @param file_name The name of the XML file to include. Defaults to `Build.xml` if not provided.
|
||||
* @return An array of fields that are processed during the build.
|
||||
*/
|
||||
public static macro function xml(?file_name:String = 'Build.xml'):Array<Field>
|
||||
{
|
||||
final pos:Position = Context.currentPos();
|
||||
final sourcePath:String = FileSystem.absolutePath(Context.getPosInfos(pos).file.directory()).removeTrailingSlashes();
|
||||
final fileToInclude:String = Path.join([sourcePath, file_name?.length > 0 ? file_name : 'Build.xml']);
|
||||
|
||||
if (!FileSystem.exists(fileToInclude)) Context.error('The specified file "$fileToInclude" could not be found at "$sourcePath".', pos);
|
||||
|
||||
final includeElement:Xml = Xml.createElement('include');
|
||||
includeElement.set('name', fileToInclude);
|
||||
Context.getLocalClass().get().meta.add(':buildXml', [
|
||||
{expr: EConst(CString(Printer.print(includeElement, true))), pos: pos}], pos);
|
||||
|
||||
return Context.getBuildFields();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,14 @@ class FunkinBackButton extends FunkinButton
|
||||
|
||||
public var enabled:Bool = true;
|
||||
|
||||
var confirming:Bool = false;
|
||||
public var confirming(get, never):Bool;
|
||||
|
||||
function get_confirming():Bool
|
||||
{
|
||||
return _confirming;
|
||||
}
|
||||
|
||||
var _confirming:Bool = false;
|
||||
|
||||
public var restingOpacity:Float;
|
||||
|
||||
@@ -66,7 +73,7 @@ class FunkinBackButton extends FunkinButton
|
||||
held = true;
|
||||
|
||||
FlxTween.cancelTweensOf(this);
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
animation.play('hold');
|
||||
|
||||
alpha = 1;
|
||||
@@ -86,7 +93,7 @@ class FunkinBackButton extends FunkinButton
|
||||
return;
|
||||
}
|
||||
|
||||
confirming = true;
|
||||
_confirming = true;
|
||||
|
||||
FlxTween.cancelTweensOf(this);
|
||||
HapticUtil.vibrate(0, 0.05, 0.5);
|
||||
@@ -98,7 +105,7 @@ class FunkinBackButton extends FunkinButton
|
||||
|
||||
animation.onFinish.addOnce(function(name:String) {
|
||||
if (name != 'confirm') return;
|
||||
confirming = false;
|
||||
_confirming = false;
|
||||
held = false;
|
||||
onConfirmEnd.dispatch();
|
||||
});
|
||||
@@ -127,7 +134,7 @@ class FunkinBackButton extends FunkinButton
|
||||
onDown.removeAll();
|
||||
onOut.removeAll();
|
||||
|
||||
confirming = false;
|
||||
_confirming = false;
|
||||
held = false;
|
||||
|
||||
onUp.add(playConfirmAnim);
|
||||
|
||||
@@ -12,7 +12,16 @@ class FunkinOptionsButton extends FunkinButton
|
||||
public var onConfirmStart(default, null):FlxSignal = new FlxSignal();
|
||||
public var onConfirmEnd(default, null):FlxSignal = new FlxSignal();
|
||||
|
||||
var confirming:Bool = false;
|
||||
public var enabled:Bool = true;
|
||||
|
||||
public var confirming(get, never):Bool;
|
||||
|
||||
function get_confirming():Bool
|
||||
{
|
||||
return _confirming;
|
||||
}
|
||||
|
||||
var _confirming:Bool = false;
|
||||
var instant:Bool = false;
|
||||
var held:Bool = false;
|
||||
|
||||
@@ -49,7 +58,7 @@ class FunkinOptionsButton extends FunkinButton
|
||||
|
||||
function playHoldAnim():Void
|
||||
{
|
||||
if (confirming || held) return;
|
||||
if (confirming || held || !enabled) return;
|
||||
|
||||
held = true;
|
||||
|
||||
@@ -70,7 +79,7 @@ class FunkinOptionsButton extends FunkinButton
|
||||
return;
|
||||
}
|
||||
|
||||
confirming = true;
|
||||
_confirming = true;
|
||||
|
||||
FlxTween.cancelTweensOf(this);
|
||||
HapticUtil.vibrate(0, 0.05, 0.5);
|
||||
@@ -86,17 +95,20 @@ class FunkinOptionsButton extends FunkinButton
|
||||
|
||||
animation.onFinish.addOnce(function(name:String) {
|
||||
if (name != 'confirm') return;
|
||||
_confirming = false;
|
||||
held = false;
|
||||
onConfirmEnd.dispatch();
|
||||
});
|
||||
}
|
||||
|
||||
function playOutAnim():Void
|
||||
{
|
||||
if (confirming) return;
|
||||
if (confirming || !enabled) return;
|
||||
|
||||
FlxTween.cancelTweensOf(this);
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
animation.play('idle');
|
||||
held = false;
|
||||
}
|
||||
|
||||
public function resetCallbacks():Void
|
||||
@@ -105,7 +117,7 @@ class FunkinOptionsButton extends FunkinButton
|
||||
onDown.removeAll();
|
||||
onOut.removeAll();
|
||||
|
||||
confirming = false;
|
||||
_confirming = false;
|
||||
held = false;
|
||||
|
||||
onUp.add(playConfirmAnim);
|
||||
|
||||
@@ -270,7 +270,7 @@ class ControlsSchemeMenu extends MusicBeatSubState
|
||||
*/
|
||||
function setSelection(index:Int):Void
|
||||
{
|
||||
final newIndex:Int = Math.floor(FlxMath.bound(index, 0, hitboxShowcases.length - 1));
|
||||
final newIndex:Int = Math.floor(index.clamp(0, hitboxShowcases.length - 1));
|
||||
|
||||
if (currentIndex != newIndex)
|
||||
{
|
||||
@@ -348,7 +348,7 @@ class ControlsSchemeMenu extends MusicBeatSubState
|
||||
hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, showcasesTargetX, elapsed, 0.5);
|
||||
|
||||
final minShowcasesX:Float = -1500 * availableSchemes.length;
|
||||
hitboxShowcases.x = FlxMath.bound(hitboxShowcases.x, minShowcasesX, 400);
|
||||
hitboxShowcases.x = hitboxShowcases.x.clamp(minShowcasesX, 400);
|
||||
|
||||
final targetIndex:Int = Math.round(hitboxShowcases.x / -1500);
|
||||
|
||||
@@ -356,8 +356,9 @@ class ControlsSchemeMenu extends MusicBeatSubState
|
||||
}
|
||||
else
|
||||
{
|
||||
hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, (-1500 * currentIndex) + (-1500 / (availableSchemes.length + 1) * currentIndex), elapsed, 0.5);
|
||||
hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, (-1500 * currentIndex) + (-1500 / (availableSchemes.length + 1) * currentIndex),
|
||||
elapsed, 0.5);
|
||||
}
|
||||
#end
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class AdMobUtil
|
||||
/**
|
||||
* AdMob publisher ID used for the application.
|
||||
*/
|
||||
static final ADMOB_PUBLISHER:String = EnvironmentConfigMacro.environmentConfig.get("GLOBAL_ADMOB_PUBLISHER");
|
||||
static final ADMOB_PUBLISHER:String = EnvironmentConfigMacro.environmentConfig.get("MOBILE_GLOBAL_ADMOB_PUBLISHER");
|
||||
|
||||
/**
|
||||
* Test ad unit IDs for development and testing purposes.
|
||||
|
||||
@@ -28,6 +28,8 @@ class InAppPurchasesUtil
|
||||
*/
|
||||
public static final UPGRADE_PRODUCT_ID:String = 'no_ads';
|
||||
|
||||
public static var hasInitialized:Bool = false;
|
||||
|
||||
/**
|
||||
* A static variable that holds an array of currently loaded product details for in-app purchases.
|
||||
*/
|
||||
@@ -65,9 +67,14 @@ class InAppPurchasesUtil
|
||||
});
|
||||
|
||||
IAPAndroid.onProductDetailsResponse.add(function(result:IAPResult, productDetails:Array<IAPProductDetails>):Void {
|
||||
if (result.getResponseCode() == IAPResponseCode.OK) currentProductDetails = productDetails;
|
||||
if (result.getResponseCode() == IAPResponseCode.OK)
|
||||
{
|
||||
hasInitialized = true;
|
||||
currentProductDetails = productDetails;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasInitialized = false;
|
||||
trace('Failed to fetch product details: "$result"');
|
||||
}
|
||||
});
|
||||
@@ -101,15 +108,21 @@ class InAppPurchasesUtil
|
||||
IAPAndroid.startConnection();
|
||||
#else
|
||||
IAPIOS.onProductDetailsReceived.add(function(productDetails:Array<IAPProductDetails>):Void {
|
||||
if (productDetails != null) currentProductDetails = productDetails;
|
||||
if (productDetails != null)
|
||||
{
|
||||
currentProductDetails = productDetails;
|
||||
}
|
||||
});
|
||||
|
||||
IAPIOS.onProductDetailsFailed.add(function(error:IAPError):Void {
|
||||
trace('Failed to request product details: "$error"');
|
||||
hasInitialized = false;
|
||||
});
|
||||
|
||||
IAPIOS.onPurchasesUpdated.add(function(purchases:Array<IAPPurchase>):Void {
|
||||
handlePurchases(purchases);
|
||||
trace("iOS purchases updated: " + purchases.length);
|
||||
hasInitialized = true;
|
||||
trace("hasInitialized: " + hasInitialized);
|
||||
});
|
||||
|
||||
IAPIOS.init();
|
||||
@@ -147,20 +160,32 @@ class InAppPurchasesUtil
|
||||
{
|
||||
function purchasesUpdatedEvent(result:IAPResult, purchases:Array<IAPPurchase>):Void
|
||||
{
|
||||
for (purchase in purchases)
|
||||
if (result.getResponseCode() == IAPResponseCode.OK)
|
||||
{
|
||||
if (purchase.getProducts().contains(id))
|
||||
for (purchase in purchases)
|
||||
{
|
||||
if (purchase.getPurchaseState() == IAPPurchaseState.PURCHASED)
|
||||
if (purchase.getProducts().contains(id))
|
||||
{
|
||||
if (onPurchased != null) onPurchased();
|
||||
if (purchase.getPurchaseState() == IAPPurchaseState.PURCHASED)
|
||||
{
|
||||
if (onPurchased != null) onPurchased();
|
||||
|
||||
IAPAndroid.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IAPAndroid.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
}
|
||||
|
||||
IAPAndroid.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
final debugMessage:Null<String> = result.getDebugMessage();
|
||||
|
||||
Toast.makeText(result.getDebugMessage(), Toast.LENGTH_SHORT);
|
||||
if (debugMessage != null && debugMessage.length > 0)
|
||||
{
|
||||
Toast.makeText(debugMessage, Toast.LENGTH_SHORT);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IAPAndroid.onPurchasesUpdated.has(purchasesUpdatedEvent))
|
||||
@@ -180,14 +205,18 @@ class InAppPurchasesUtil
|
||||
{
|
||||
if (purchase.getPaymentProductIdentifier() == id)
|
||||
{
|
||||
if (purchase.getTransactionState() == IAPPurchaseState.PURCHASED)
|
||||
switch (purchase.getTransactionState())
|
||||
{
|
||||
if (onPurchased != null) onPurchased();
|
||||
case IAPPurchaseState.PURCHASED:
|
||||
if (onPurchased != null) onPurchased();
|
||||
|
||||
IAPIOS.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
case IAPPurchaseState.FAILED:
|
||||
IAPIOS.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IAPIOS.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
}
|
||||
|
||||
if (!IAPIOS.onPurchasesUpdated.has(purchasesUpdatedEvent))
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package funkin.mobile.util;
|
||||
|
||||
#if ios
|
||||
import funkin.mobile.external.ScreenUtils;
|
||||
import funkin.external.ios.ScreenUtil as NativeScreenUtil;
|
||||
#elseif android
|
||||
import extension.androidtools.Tools;
|
||||
import funkin.external.android.ScreenUtil as NativeScreenUtil;
|
||||
#end
|
||||
import lime.math.Rectangle;
|
||||
import lime.system.System;
|
||||
@@ -26,7 +26,7 @@ class ScreenUtil
|
||||
final rectDimensions:Array<Array<Float>> = [[], [], [], []];
|
||||
|
||||
// Push all the dimensions of the cutouts into an array
|
||||
for (rect in Tools.getCutoutDimensions())
|
||||
for (rect in NativeScreenUtil.getCutoutDimensions())
|
||||
{
|
||||
rectDimensions[0].push(rect.x);
|
||||
rectDimensions[1].push(rect.y);
|
||||
@@ -59,13 +59,12 @@ class ScreenUtil
|
||||
var bottomInset:Float = -1;
|
||||
var deviceWidth:Float = -1;
|
||||
var deviceHeight:Float = -1;
|
||||
var displayOrientation:DisplayOrientation = System.getDisplayOrientation(0);
|
||||
|
||||
ScreenUtils.getSafeAreaInsets(cpp.RawPointer.addressOf(topInset), cpp.RawPointer.addressOf(bottomInset), cpp.RawPointer.addressOf(leftInset),
|
||||
NativeScreenUtil.getSafeAreaInsets(cpp.RawPointer.addressOf(topInset), cpp.RawPointer.addressOf(bottomInset), cpp.RawPointer.addressOf(leftInset),
|
||||
cpp.RawPointer.addressOf(rightInset));
|
||||
|
||||
ScreenUtils.getScreenSize(cpp.RawPointer.addressOf(deviceWidth), cpp.RawPointer.addressOf(deviceHeight));
|
||||
|
||||
var displayOrientation = System.getDisplayOrientation(0);
|
||||
NativeScreenUtil.getScreenSize(cpp.RawPointer.addressOf(deviceWidth), cpp.RawPointer.addressOf(deviceHeight));
|
||||
|
||||
notchRect.x = 0;
|
||||
notchRect.y = 0.0;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package funkin.modding;
|
||||
|
||||
import haxe.ds.StringMap;
|
||||
|
||||
/**
|
||||
* Temporary persistent data storage for mods to use.
|
||||
*/
|
||||
@:nullSafety
|
||||
class ModStore
|
||||
{
|
||||
/**
|
||||
* All registered stores for this session.
|
||||
*/
|
||||
public static final stores:StringMap<Dynamic> = new StringMap<Dynamic>();
|
||||
|
||||
/**
|
||||
* Attempts to register a new store with the given ID and return it.
|
||||
* If a store with the same ID already exists, that store will be returned instead (discards `data`).
|
||||
*
|
||||
* @id The unique ID for this store.
|
||||
* @data Optional initial data, uses an empty object by default.
|
||||
* @return The store data at the given ID.
|
||||
*/
|
||||
public static function register(id:String, ?data:Dynamic):Dynamic
|
||||
{
|
||||
if (stores.exists(id)) return stores.get(id);
|
||||
stores.set(id, data ??= {});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a store by ID.
|
||||
*
|
||||
* @id The target ID of the store.
|
||||
* @return The store data, or `null` if the store did not exist.
|
||||
*/
|
||||
public static function get(id:String):Null<Dynamic>
|
||||
{
|
||||
return stores.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to remove a store by ID and return it.
|
||||
*
|
||||
* @id The target ID of the store.
|
||||
* @return The store data, or `null` if the store did not exist.
|
||||
*/
|
||||
public static function remove(id:String):Null<Dynamic>
|
||||
{
|
||||
var data:Null<Dynamic> = stores.get(id);
|
||||
stores.remove(id);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -5,19 +5,6 @@ import polymod.Polymod;
|
||||
@:nullSafety
|
||||
class PolymodErrorHandler
|
||||
{
|
||||
/**
|
||||
* Show a popup with the given text.
|
||||
* This displays a system popup, it WILL interrupt the game.
|
||||
* Make sure to only use this when it's important, like when there's a script error.
|
||||
*
|
||||
* @param name The name at the top of the popup.
|
||||
* @param desc The body text of the popup.
|
||||
*/
|
||||
public static function showAlert(name:String, desc:String):Void
|
||||
{
|
||||
lime.app.Application.current.window.alert(desc, name);
|
||||
}
|
||||
|
||||
public static function onPolymodError(error:PolymodError):Void
|
||||
{
|
||||
// Perform an action based on the error code.
|
||||
@@ -37,12 +24,12 @@ class PolymodErrorHandler
|
||||
// A syntax error when parsing a script.
|
||||
logError(error.message);
|
||||
// Notify the user via popup.
|
||||
showAlert('Polymod Script Parsing Error', error.message);
|
||||
funkin.util.WindowUtil.showError('Polymod Script Parsing Error', error.message);
|
||||
case SCRIPT_RUNTIME_EXCEPTION:
|
||||
// A runtime error when running a script.
|
||||
logError(error.message);
|
||||
// Notify the user via popup.
|
||||
showAlert('Polymod Script Exception', error.message);
|
||||
funkin.util.WindowUtil.showError('Polymod Script Exception', error.message);
|
||||
case SCRIPT_CLASS_MODULE_NOT_FOUND:
|
||||
// A scripted class tried to reference an unknown class or module.
|
||||
logError(error.message);
|
||||
@@ -54,13 +41,12 @@ class PolymodErrorHandler
|
||||
msg += '\nCheck to ensure the class exists and is spelled correctly.';
|
||||
|
||||
// Notify the user via popup.
|
||||
showAlert('Polymod Script Import Error', msg);
|
||||
funkin.util.WindowUtil.showError('Polymod Script Import Error', msg);
|
||||
case SCRIPT_CLASS_MODULE_BLACKLISTED:
|
||||
// A scripted class tried to reference a blacklisted class or module.
|
||||
logError(error.message);
|
||||
// Notify the user via popup.
|
||||
showAlert('Polymod Script Blacklist Violation', error.message);
|
||||
|
||||
funkin.util.WindowUtil.showError('Polymod Script Blacklist Violation', error.message);
|
||||
default:
|
||||
// Log the message based on its severity.
|
||||
switch (error.severity)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package funkin.modding;
|
||||
|
||||
import polymod.fs.ZipFileSystem;
|
||||
import funkin.data.dialogue.conversation.ConversationRegistry;
|
||||
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
|
||||
import funkin.data.dialogue.speaker.SpeakerRegistry;
|
||||
import funkin.data.dialogue.ConversationRegistry;
|
||||
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||
import funkin.data.dialogue.SpeakerRegistry;
|
||||
import funkin.data.event.SongEventRegistry;
|
||||
import funkin.data.story.level.LevelRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
@@ -267,6 +267,11 @@ class PolymodHandler
|
||||
Polymod.addImportAlias('funkin.modding.base.ScriptedMusicBeatState', funkin.ui.ScriptedMusicBeatState);
|
||||
Polymod.addImportAlias('funkin.modding.base.ScriptedMusicBeatSubState', funkin.ui.ScriptedMusicBeatSubState);
|
||||
|
||||
// Backward compatibility for some classes that moved.
|
||||
Polymod.addImportAlias('funkin.data.dialogue.conversation.ConversationRegistry', funkin.data.dialogue.ConversationRegistry);
|
||||
Polymod.addImportAlias('funkin.data.dialogue.dialoguebox.DialogueBoxRegistry', funkin.data.dialogue.DialogueBoxRegistry);
|
||||
Polymod.addImportAlias('funkin.data.dialogue.speaker.SpeakerRegistry', funkin.data.dialogue.SpeakerRegistry);
|
||||
|
||||
// `funkin.util.FileUtil` has unrestricted access to the file system.
|
||||
Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtilSandboxed);
|
||||
|
||||
@@ -557,12 +562,10 @@ class PolymodHandler
|
||||
Polymod.clearScripts();
|
||||
|
||||
// Forcibly reload Polymod so it finds any new files.
|
||||
// This will also register all scripts.
|
||||
// TODO: Replace this with loadEnabledMods().
|
||||
funkin.modding.PolymodHandler.loadAllMods();
|
||||
|
||||
// Reload scripted classes so stages and modules will update.
|
||||
Polymod.registerAllScriptClasses();
|
||||
|
||||
// Reload everything that is cached.
|
||||
// Currently this freezes the game for a second but I guess that's tolerable?
|
||||
|
||||
|
||||
@@ -4,6 +4,18 @@ import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
||||
import funkin.modding.IScriptedClass.IStateChangingScriptedClass;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
|
||||
/**
|
||||
* Parameters used to initialize a module.
|
||||
*/
|
||||
typedef ModuleParams =
|
||||
{
|
||||
/**
|
||||
* The state this module is associated with.
|
||||
* If set, this module will only receive events when the game is in this state.
|
||||
*/
|
||||
?state:Class<Dynamic>
|
||||
}
|
||||
|
||||
/**
|
||||
* A module is a scripted class which receives all events without requiring a specific context.
|
||||
* You may have the module active at all times, or only when another script enables it.
|
||||
@@ -39,16 +51,27 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The state this module is associated with.
|
||||
* If set, this module will only receive events when the game is in this state.
|
||||
*/
|
||||
public var state:Null<Class<Dynamic>> = null;
|
||||
|
||||
/**
|
||||
* Called when the module is initialized.
|
||||
* It may not be safe to reference other modules here since they may not be loaded yet.
|
||||
*
|
||||
* NOTE: To make the module start inactive, call `this.active = false` in the constructor.
|
||||
*/
|
||||
public function new(moduleId:String, priority:Int = 1000):Void
|
||||
public function new(moduleId:String, priority:Int = 1000, ?params:ModuleParams):Void
|
||||
{
|
||||
this.moduleId = moduleId;
|
||||
this.priority = priority;
|
||||
|
||||
if (params != null)
|
||||
{
|
||||
this.state = params.state ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
public function toString()
|
||||
|
||||
@@ -6,6 +6,7 @@ import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.modding.module.Module;
|
||||
import funkin.modding.module.ScriptedModule;
|
||||
import flixel.FlxG;
|
||||
|
||||
/**
|
||||
* Utility functions for loading and manipulating active modules.
|
||||
@@ -145,6 +146,14 @@ class ModuleHandler
|
||||
// The module needs to be active to receive events.
|
||||
if (module != null && module.active)
|
||||
{
|
||||
if (module.state != null)
|
||||
{
|
||||
// Only call the event if the current state is what the module's state is.
|
||||
if (!(Type.getClass(FlxG.state) == module.state) && !(Type.getClass(FlxG.state?.subState) == module.state))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ScriptEventDispatcher.callEvent(module, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import funkin.util.MathUtil;
|
||||
import funkin.effects.RetroCameraFade;
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.util.TouchUtil;
|
||||
import openfl.utils.Assets;
|
||||
#if FEATURE_MOBILE_ADVERTISEMENTS
|
||||
import funkin.mobile.util.AdMobUtil;
|
||||
#end
|
||||
@@ -102,18 +101,6 @@ class GameOverSubState extends MusicBeatSubState
|
||||
|
||||
var canInput:Bool = false;
|
||||
|
||||
var justDied:Bool = true;
|
||||
|
||||
var isSpecialAnimation:Bool = false;
|
||||
|
||||
var gameOverVibrationPreset:VibrationPreset =
|
||||
{
|
||||
period: 0,
|
||||
duration: Constants.DEFAULT_VIBRATION_DURATION,
|
||||
amplitude: Constants.MIN_VIBRATION_AMPLITUDE,
|
||||
sharpness: Constants.DEFAULT_VIBRATION_SHARPNESS
|
||||
};
|
||||
|
||||
public function new(params:GameOverParams)
|
||||
{
|
||||
super();
|
||||
@@ -175,7 +162,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||
if ((parentPlayState?.isMinimalMode ?? true)) {}
|
||||
else
|
||||
{
|
||||
boyfriend = parentPlayState?.currentStage.getBoyfriend(true);
|
||||
boyfriend = parentPlayState?.currentStage?.getBoyfriend(true);
|
||||
if (boyfriend != null)
|
||||
{
|
||||
boyfriend.canPlayOtherAnims = true;
|
||||
@@ -198,16 +185,17 @@ class GameOverSubState extends MusicBeatSubState
|
||||
addBackButton(FlxG.width - 230, FlxG.height - 200, FlxColor.WHITE, goBack);
|
||||
#end
|
||||
|
||||
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION);
|
||||
|
||||
// Allow input a second later to prevent accidental gameover skips.
|
||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||
canInput = true;
|
||||
});
|
||||
}
|
||||
|
||||
@:nullSafety(Off)
|
||||
function setCameraTarget():Void
|
||||
{
|
||||
if ((parentPlayState?.isMinimalMode ?? true) || boyfriend == null) return;
|
||||
if (parentPlayState == null || parentPlayState.isMinimalMode || boyfriend == null) return;
|
||||
|
||||
// Assign a camera follow point to the boyfriend's position.
|
||||
cameraFollowPoint = new FlxObject(parentPlayState.cameraFollowPoint.x, parentPlayState.cameraFollowPoint.y, 1, 1);
|
||||
@@ -218,6 +206,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||
cameraFollowPoint.y += offsets[1];
|
||||
add(cameraFollowPoint);
|
||||
|
||||
@:nullSafety(Off)
|
||||
FlxG.camera.target = null;
|
||||
FlxG.camera.follow(cameraFollowPoint, LOCKON, Constants.DEFAULT_CAMERA_FOLLOW_RATE / 2);
|
||||
targetCameraZoom = (parentPlayState?.currentStage?.camZoom ?? 1.0) * boyfriend.getDeathCameraZoom();
|
||||
@@ -330,9 +319,6 @@ class GameOverSubState extends MusicBeatSubState
|
||||
}
|
||||
}
|
||||
|
||||
// Handle vibrations on update.
|
||||
if (HapticUtil.hapticsAvailable) handleAnimationVibrations();
|
||||
|
||||
// Start death music before firstDeath gets replaced
|
||||
super.update(elapsed);
|
||||
}
|
||||
@@ -408,7 +394,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||
// Readd Boyfriend to the stage.
|
||||
boyfriend.isDead = false;
|
||||
remove(boyfriend);
|
||||
parentPlayState?.currentStage.addCharacter(boyfriend, BF);
|
||||
parentPlayState?.currentStage?.addCharacter(boyfriend, BF);
|
||||
}
|
||||
|
||||
// Snap reset the camera which may have changed because of the player character data.
|
||||
@@ -454,7 +440,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||
#else
|
||||
resetPlaying();
|
||||
#end
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -545,6 +531,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||
*/
|
||||
public function goBack():Void
|
||||
{
|
||||
if (blueballed == false) return;
|
||||
isEnding = true;
|
||||
blueballed = false;
|
||||
if (parentPlayState != null) parentPlayState.deathCounter = 0;
|
||||
@@ -577,11 +564,11 @@ class GameOverSubState extends MusicBeatSubState
|
||||
PlayStatePlaylist.reset();
|
||||
}
|
||||
|
||||
var stickerPackId:Null<String> = parentPlayState?.currentChart.stickerPack;
|
||||
var stickerPackId:Null<String> = parentPlayState?.currentChart?.stickerPack;
|
||||
|
||||
if (stickerPackId == null)
|
||||
{
|
||||
var playerCharacterId:Null<String> = PlayerRegistry.instance.getCharacterOwnerId(parentPlayState?.currentChart.characters.player);
|
||||
var playerCharacterId:Null<String> = PlayerRegistry.instance.getCharacterOwnerId(parentPlayState?.currentChart?.characters.player);
|
||||
var playerCharacter:Null<PlayableCharacter> = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? Constants.DEFAULT_CHARACTER);
|
||||
|
||||
if (playerCharacter != null)
|
||||
@@ -614,108 +601,6 @@ class GameOverSubState extends MusicBeatSubState
|
||||
|
||||
var hasPlayedDeathQuote:Bool = false;
|
||||
|
||||
/**
|
||||
* Used for death haptics.
|
||||
*/
|
||||
var startedTimerHaptics:Bool = false;
|
||||
|
||||
/**
|
||||
* Unique vibrations for each death animation.
|
||||
*/
|
||||
function handleAnimationVibrations():Void
|
||||
{
|
||||
if ((parentPlayState?.isMinimalMode ?? true) || boyfriend == null) return;
|
||||
|
||||
if (justDied)
|
||||
{
|
||||
if (isSpecialAnimation)
|
||||
{
|
||||
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION * 5);
|
||||
trace("It's a special game over animation.");
|
||||
}
|
||||
else
|
||||
{
|
||||
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION);
|
||||
}
|
||||
justDied = false;
|
||||
}
|
||||
|
||||
if (boyfriend.animation == null) return;
|
||||
|
||||
final curFrame:Int = (boyfriend.animation.curAnim != null) ? boyfriend.animation.curAnim.curFrame : -1;
|
||||
if (boyfriend.characterId.startsWith("bf"))
|
||||
{
|
||||
// BF's mic drops.
|
||||
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && curFrame == 27)
|
||||
{
|
||||
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
|
||||
}
|
||||
|
||||
// BF's balls pulsating.
|
||||
if (boyfriend.getCurrentAnimation().startsWith('deathLoop') && (curFrame == 0 || curFrame == 18))
|
||||
{
|
||||
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Pico dies because of Darnell beating him up.
|
||||
if (boyfriend.characterId == "pico-blazin")
|
||||
{
|
||||
if (!startedTimerHaptics)
|
||||
{
|
||||
startedTimerHaptics = true;
|
||||
|
||||
new FlxTimer().start(0.5, function(tmr:FlxTimer) {
|
||||
// Pico falls on his knees.
|
||||
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
|
||||
|
||||
new FlxTimer().start(0.6, function(tmr:FlxTimer) {
|
||||
// Pico falls "asleep". :)
|
||||
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (boyfriend.characterId.startsWith("pico") && boyfriend.characterId != "pico-holding-nene")
|
||||
{
|
||||
if (isSpecialAnimation)
|
||||
{
|
||||
if (startedTimerHaptics) return;
|
||||
|
||||
startedTimerHaptics = true;
|
||||
|
||||
// Death by Darnell's can.
|
||||
new FlxTimer().start(1.85, function(tmr:FlxTimer) {
|
||||
// Pico falls on his knees.
|
||||
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pico falls on his back.
|
||||
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && curFrame == 20)
|
||||
{
|
||||
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
|
||||
}
|
||||
|
||||
// Blood firework woohoo!!!!
|
||||
if (boyfriend.getCurrentAnimation().startsWith('deathLoop') && curFrame % 2 == 0)
|
||||
{
|
||||
final randomAmplitude:Float = FlxG.random.float(Constants.MIN_VIBRATION_AMPLITUDE / 100, Constants.MIN_VIBRATION_AMPLITUDE);
|
||||
final randomDuration:Float = FlxG.random.float(Constants.DEFAULT_VIBRATION_DURATION / 10, Constants.DEFAULT_VIBRATION_DURATION);
|
||||
|
||||
HapticUtil.vibrate(0, randomDuration, randomAmplitude);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public override function destroy():Void
|
||||
{
|
||||
super.destroy();
|
||||
|
||||
@@ -18,9 +18,9 @@ class GitarooPause extends MusicBeatState
|
||||
|
||||
var replaySelect:Bool = false;
|
||||
|
||||
var previousParams:PlayStateParams;
|
||||
var previousParams:Null<PlayStateParams>;
|
||||
|
||||
public function new(previousParams:PlayStateParams):Void
|
||||
public function new(?previousParams:PlayStateParams):Void
|
||||
{
|
||||
super();
|
||||
|
||||
|
||||
@@ -213,6 +213,11 @@ class PauseSubState extends MusicBeatSubState
|
||||
*/
|
||||
var menuEntryText:FlxTypedSpriteGroup<AtlasText>;
|
||||
|
||||
/**
|
||||
* Callback that gets called once substate gets open.
|
||||
*/
|
||||
var onPause:Void->Void;
|
||||
|
||||
// ===============
|
||||
// Audio Variables
|
||||
// ===============
|
||||
@@ -222,10 +227,11 @@ class PauseSubState extends MusicBeatSubState
|
||||
// Constructor
|
||||
// ===============
|
||||
|
||||
public function new(?params:PauseSubStateParams)
|
||||
public function new(?params:PauseSubStateParams, ?onPause:Void->Void)
|
||||
{
|
||||
super();
|
||||
this.currentMode = params?.mode ?? Standard;
|
||||
this.onPause = onPause;
|
||||
}
|
||||
|
||||
// ===============
|
||||
@@ -244,6 +250,8 @@ class PauseSubState extends MusicBeatSubState
|
||||
AdMobUtil.addBanner(extension.admob.AdmobBannerSize.BANNER, extension.admob.AdmobBannerAlign.TOP_LEFT);
|
||||
#end
|
||||
|
||||
if (onPause != null) onPause();
|
||||
|
||||
super.create();
|
||||
|
||||
startPauseMusic();
|
||||
@@ -286,6 +294,7 @@ class PauseSubState extends MusicBeatSubState
|
||||
hapticTimer.cancel();
|
||||
hapticTimer = null;
|
||||
pauseMusic.stop();
|
||||
onPause = null;
|
||||
}
|
||||
|
||||
// ===============
|
||||
@@ -536,7 +545,7 @@ class PauseSubState extends MusicBeatSubState
|
||||
FlxTween.tween(pauseCircle, {alpha: 0}, 0.6, {ease: FlxEase.quartOut});
|
||||
|
||||
hapticTimer.start(0.2, function(_) {
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
});
|
||||
|
||||
dataFadeTimer.start(0.3, function(_) {
|
||||
@@ -640,12 +649,12 @@ class PauseSubState extends MusicBeatSubState
|
||||
if (i == currentEntry)
|
||||
{
|
||||
currentMenuEntries[currentEntry].callback(this);
|
||||
HapticUtil.vibrate(0, 0.05, 0.5);
|
||||
HapticUtil.vibrate(0, 0.05, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
changeSelection(i - currentEntry);
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -745,9 +754,6 @@ class PauseSubState extends MusicBeatSubState
|
||||
clearAndAddMenuEntries();
|
||||
updateMetadataText();
|
||||
changeSelection();
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
SwipeUtil.calculateSwipeThreshold(menuEntryText.members, Y);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -933,7 +939,8 @@ class PauseSubState extends MusicBeatSubState
|
||||
*/
|
||||
static function changeDifficulty(state:PauseSubState, difficulty:String):Void
|
||||
{
|
||||
PlayState.instance.currentSong = SongRegistry.instance.fetchEntry(PlayState.instance.currentSong.id.toLowerCase());
|
||||
PlayState.instance.currentSong = SongRegistry.instance.fetchEntry(PlayState.instance.currentSong.id.toLowerCase(),
|
||||
{variation: PlayState.instance.currentChart.variation});
|
||||
|
||||
// Reset campaign score when changing difficulty
|
||||
// So if you switch difficulty on the last song of a week you get a really low overall score.
|
||||
|
||||
+372
-245
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user