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 | ||
|
|
351719ab13 | ||
|
|
4ecfe9863e | ||
|
|
48e140e5c0 | ||
|
|
e6bb965d6b | ||
|
|
b00376c88c | ||
|
|
96a1796905 | ||
|
|
541bb78da9 | ||
|
|
5f5fff870c | ||
|
|
52acd86cdf | ||
|
|
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 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 checked the Issues/Discussions pages to see if my issue has already been reported
|
||||||
- label: I have properly titled my issue
|
- label: I have properly titled my issue
|
||||||
|
- label: I did not have any mods installed when I encountered this issue
|
||||||
|
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
label: Platform
|
label: Platform
|
||||||
description: Which platform are you playing on?
|
description: Which platform are you playing on?
|
||||||
options:
|
options:
|
||||||
|
- Android
|
||||||
|
- iOS/iPadOS
|
||||||
- Newgrounds (Web/HTML5)
|
- Newgrounds (Web/HTML5)
|
||||||
- Itch.io (Web/HTML5)
|
- Itch.io (Web/HTML5)
|
||||||
- Itch.io (Downloadable Build) - Windows
|
- Windows (Downloadable Build)
|
||||||
- Itch.io (Downloadable Build) - MacOS
|
- MacOS (Downloadable Build)
|
||||||
- Itch.io (Downloadable Build) - Linux
|
- Linux (Downloadable Build)
|
||||||
- Google Playstore - Android
|
|
||||||
- App Store - iOS
|
|
||||||
- Compiled from GitHub Source Code
|
- Compiled from GitHub Source Code
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@@ -43,11 +44,23 @@ body:
|
|||||||
- Safari
|
- Safari
|
||||||
- Other (Specify in Description field)
|
- 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
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
||||||
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Issue Checklist
|
label: Issue Checklist
|
||||||
@@ -21,35 +21,25 @@ body:
|
|||||||
label: Platform
|
label: Platform
|
||||||
description: Which platform are you playing on?
|
description: Which platform are you playing on?
|
||||||
options:
|
options:
|
||||||
|
- Android
|
||||||
|
- iOS/iPadOS
|
||||||
- Newgrounds (Web/HTML5)
|
- Newgrounds (Web/HTML5)
|
||||||
- Itch.io (Web/HTML5)
|
- Itch.io (Web/HTML5)
|
||||||
- Itch.io (Downloadable Build) - Windows
|
- Windows (Downloadable Build)
|
||||||
- Itch.io (Downloadable Build) - MacOS
|
- MacOS (Downloadable Build)
|
||||||
- Itch.io (Downloadable Build) - Linux
|
- Linux (Downloadable Build)
|
||||||
- Compiled from GitHub Source Code
|
- Compiled from GitHub Source Code
|
||||||
validations:
|
validations:
|
||||||
required: true
|
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
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "## Describe the charting issue(s)."
|
value: "## Describe the charting issue(s)."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
||||||
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Issue Checklist
|
label: Issue Checklist
|
||||||
@@ -22,6 +22,8 @@ body:
|
|||||||
label: Platform
|
label: Platform
|
||||||
description: Which platform are you compiling for?
|
description: Which platform are you compiling for?
|
||||||
options:
|
options:
|
||||||
|
- Android
|
||||||
|
- iOS/iPadOS
|
||||||
- Web/HTML5
|
- Web/HTML5
|
||||||
- Desktop (Windows)
|
- Desktop (Windows)
|
||||||
- Desktop (Mac)
|
- Desktop (Mac)
|
||||||
@@ -33,11 +35,11 @@ body:
|
|||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
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.
|
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
|
placeholder: ex. 0.7.4
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "## Describe your compiling issue."
|
value: "## Describe your compiling issue."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
|
||||||
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Issue Checklist
|
label: Issue Checklist
|
||||||
@@ -21,11 +21,13 @@ body:
|
|||||||
label: Platform
|
label: Platform
|
||||||
description: Which platform are you playing on?
|
description: Which platform are you playing on?
|
||||||
options:
|
options:
|
||||||
|
- Android
|
||||||
|
- iOS/iPadOS
|
||||||
- Newgrounds (Web/HTML5)
|
- Newgrounds (Web/HTML5)
|
||||||
- Itch.io (Web/HTML5)
|
- Itch.io (Web/HTML5)
|
||||||
- Itch.io (Downloadable Build) - Windows
|
- Windows (Downloadable Build)
|
||||||
- Itch.io (Downloadable Build) - MacOS
|
- MacOS (Downloadable Build)
|
||||||
- Itch.io (Downloadable Build) - Linux
|
- Linux (Downloadable Build)
|
||||||
- Compiled from GitHub Source Code
|
- Compiled from GitHub Source Code
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@@ -42,11 +44,23 @@ body:
|
|||||||
- Safari
|
- Safari
|
||||||
- Other (Specify in Description field)
|
- 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
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@@ -61,7 +75,7 @@ body:
|
|||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Description
|
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...
|
placeholder: Describe the crash here...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@@ -77,7 +91,7 @@ body:
|
|||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Crash logs
|
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...
|
placeholder: Upload your logs here...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
+167
-5
@@ -4,6 +4,163 @@ All notable changes will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.7.4] - 2025-09-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- [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
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- 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
|
## [0.7.3] - 2025-07-21
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
@@ -43,7 +200,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Scripted classes can now create static fields and functions.
|
- Scripted classes can now create static fields and functions.
|
||||||
- Scripted classes can now create variables with the `final` keyword.
|
- 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")`
|
- 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()`
|
- This applies to functions too: `class.someFunction()`
|
||||||
- Scripted classes that don't extend another class can now be created!
|
- 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!
|
- 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 properties (`get_` and `set_` functions) (Thanks KoloInDaCrib!)
|
||||||
@@ -53,7 +210,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- You can import them in another script as usual.
|
- You can import them in another script as usual.
|
||||||
- Added support for renaming imported classes using the `as` keyword. (Thanks KoloInDaCrib!)
|
- Added support for renaming imported classes using the `as` keyword. (Thanks KoloInDaCrib!)
|
||||||
- Fixed `try`/`catch` blocks not working properly. (Thanks NotHyper-474!)
|
- 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 null-safe field access not working properly for functions (ex. `class?.someFunction()`). (Thanks KoloInDaCrib!)
|
||||||
|
- Fixed Linux being case-sensitive with filenames. (Thanks mikolka9144!)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@@ -88,6 +246,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
* @Smokey555 made their first contribution in [#3318](https://github.com/FunkinCrew/Funkin/pull/3318)
|
* @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)
|
* @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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -432,7 +591,7 @@ Select. ([3d3e2bd](https://github.com/FunkinCrew/Funkin/commit/3d3e2bd3786b85814
|
|||||||
* @JackXson-Real made their first contribution in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
|
* @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)
|
* @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)
|
* @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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -705,6 +864,7 @@ This patch resolves a critical issue that could cause user's save data to become
|
|||||||
|
|
||||||
## [0.5.0] - 2024-09-12
|
## [0.5.0] - 2024-09-12
|
||||||
The Playable Pico Update!
|
The Playable Pico Update!
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added a new Character Select screen to switch between playable characters in Freeplay.
|
- Added a new Character Select screen to switch between playable characters in Freeplay.
|
||||||
@@ -825,7 +985,7 @@ The Playable Pico Update!
|
|||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Control binds in the controls menu no longer overlap their names.
|
- 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))
|
- 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))
|
- 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.
|
- Pico's gameplay sprite no longer appears on the gameover screen when dying from an explosion in 2hot.
|
||||||
@@ -839,13 +999,14 @@ The Playable Pico Update!
|
|||||||
## New Contributors for 0.4.1
|
## New Contributors for 0.4.1
|
||||||
|
|
||||||
* @Hundrec made their first contribution in [#2661](https://github.com/FunkinCrew/Funkin/pull/2661)
|
* @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)
|
* @eltociear made their first contribution in [#2730](https://github.com/FunkinCrew/Funkin/pull/2730)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.4.0] - 2024-06-06
|
## [0.4.0] - 2024-06-06
|
||||||
The Pit Stop 1 update!
|
The Pit Stop 1 update!
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- 2 new Erect remixes, Eggnog and Satin Panties. Check them out from the Freeplay menu!
|
- 2 new Erect remixes, Eggnog and Satin Panties. Check them out from the Freeplay menu!
|
||||||
@@ -1017,6 +1178,7 @@ The Pit Stop 1 update!
|
|||||||
|
|
||||||
## [0.3.0] - 2024-04-30
|
## [0.3.0] - 2024-04-30
|
||||||
The Weekend 1 update!
|
The Weekend 1 update!
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- New Story Level: Weekend 1, starring Pico, Darnell, and Nene.
|
- 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.
|
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)
|
- [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)
|
- [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 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)
|
- [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
|
# Getting Started
|
||||||
|
|
||||||
**PLEASE USE THE LINKS ABOVE IF YOU JUST WANT TO PLAY THE GAME**
|
**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
|
## Special Thanks
|
||||||
- [Tom Fulp](https://twitter.com/tomfulp) - For being a great guy and for Newgrounds
|
- [Tom Fulp](https://twitter.com/tomfulp) - For being a great guy and for Newgrounds
|
||||||
- [JohnnyUtah](https://twitter.com/JohnnyUtahNG/) - Voice of Tankman
|
- [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: 69c02fa501...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": {
|
"props": {
|
||||||
"ignoreExtern": true,
|
"ignoreExtern": true,
|
||||||
"format": "^[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$",
|
"format": "^?:_[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$",
|
||||||
"tokens": ["INLINE", "NOTINLINE"]
|
"tokens": ["INLINE", "NOTINLINE"]
|
||||||
},
|
},
|
||||||
"type": "ConstantName"
|
"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
|
## Requirements
|
||||||
Make sure you're playing:
|
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
|
- without any mods
|
||||||
- on [Newgrounds](https://www.newgrounds.com/portal/view/770371) or downloaded from [itch.io](https://ninja-muffin24.itch.io/funkin)
|
- 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.
|
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!**
|
**Thank you for opening issues!**
|
||||||
|
|
||||||
# Part 3: Pull Requests
|
# 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).
|
This section covers guidelines for opening and managing pull requests (PRs).
|
||||||
|
|
||||||
## Choosing a base branch
|
## 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]
|
> [!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!
|
> 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`).
|
> 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:
|
Choose the `develop` branch if you modify:
|
||||||
- Game code (`.hx` files)
|
- Game code (`.hx` files)
|
||||||
- Any other type of file
|
- 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]
|
> [!TIP]
|
||||||
> When in doubt, base your branch on the `develop` branch.
|
> 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.
|
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
|
||||||
Charting PRs make changes such as **adjusting chart metadata**.
|
Charting PRs make changes such as **adjusting chart metadata**.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"name": "EliteMasterEric"
|
"name": "EliteMasterEric"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"api_version": "0.5.0",
|
"api_version": "0.7.0",
|
||||||
"mod_version": "1.0.0",
|
"mod_version": "1.0.0",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"name": "EliteMasterEric"
|
"name": "EliteMasterEric"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"api_version": "0.5.0",
|
"api_version": "0.7.0",
|
||||||
"mod_version": "1.0.0",
|
"mod_version": "1.0.0",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,42 +11,42 @@
|
|||||||
"name": "astc-compressor",
|
"name": "astc-compressor",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "8c9f56927c523df7b849352c6951f04112fe15cc",
|
"ref": "b7a91b3072dc16e785a9bde847f17ba0ef15dd47",
|
||||||
"url": "https://github.com/KarimAkra/astc-compressor"
|
"url": "https://github.com/KarimAkra/astc-compressor"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "extension-admob",
|
"name": "extension-admob",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "a53d5916bdcb2e48913f94d9ae1d949b049dcdc1",
|
"ref": "02334589ff9603a5f483077a44395009644f6274",
|
||||||
"url": "https://github.com/FunkinCrew/extension-admob"
|
"url": "https://github.com/FunkinCrew/extension-admob"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "extension-androidtools",
|
"name": "extension-androidtools",
|
||||||
"type": "haxelib",
|
"type": "haxelib",
|
||||||
"version": "2.2.1"
|
"version": "2.2.2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "extension-haptics",
|
"name": "extension-haptics",
|
||||||
"type": "haxelib",
|
"type": "haxelib",
|
||||||
"version": "1.0.3"
|
"version": "1.0.4"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "extension-iapcore",
|
"name": "extension-iapcore",
|
||||||
"type": "haxelib",
|
"type": "haxelib",
|
||||||
"version": "1.0.3"
|
"version": "1.0.4"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "extension-iarcore",
|
"name": "extension-iarcore",
|
||||||
"type": "haxelib",
|
"type": "haxelib",
|
||||||
"version": "1.0.2"
|
"version": "1.0.3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flixel",
|
"name": "flixel",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "08fc955ca87f192a971719a675f1d3b21709725d",
|
"ref": "ac2a34fe4b3400bc04218f46320a65af0f337fc0",
|
||||||
"url": "https://github.com/FunkinCrew/flixel-mobile"
|
"url": "https://github.com/FunkinCrew/flixel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flixel-addons",
|
"name": "flixel-addons",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"name": "flxanimate",
|
"name": "flxanimate",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "39c1572add28869c558b218fffed13df1b64f376",
|
"ref": "49214278b9124823582cdcecd94f4a1de9a4b36b",
|
||||||
"url": "https://github.com/FunkinCrew/flxanimate"
|
"url": "https://github.com/FunkinCrew/flxanimate"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -104,14 +104,14 @@
|
|||||||
"name": "hscript",
|
"name": "hscript",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "d60bb2947fa609fdc875ccfae89666a6984eeaf2",
|
"ref": "8c1d238b069ef97cec13f5121b29d2afe2b8985d",
|
||||||
"url": "https://github.com/FunkinCrew/hscript"
|
"url": "https://github.com/FunkinCrew/hscript"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "hxcpp",
|
"name": "hxcpp",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "4e24283a047f11bded6affabbc9ec405156e026e",
|
"ref": "5a0dc3f644dc676a4a092b7e6c8edc8be941f024",
|
||||||
"url": "https://github.com/FunkinCrew/hxcpp"
|
"url": "https://github.com/FunkinCrew/hxcpp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -170,36 +170,8 @@
|
|||||||
"name": "lime",
|
"name": "lime",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "c750ebf6b48c4bc018abe9855fbae5ffdbc4771a",
|
"ref": "e5f8c27124598505917a001588b560244731adfb",
|
||||||
"url": "https://github.com/FunkinCrew/lime-mobile"
|
"url": "https://github.com/FunkinCrew/lime"
|
||||||
},
|
|
||||||
{
|
|
||||||
"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"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "newgrounds",
|
"name": "newgrounds",
|
||||||
@@ -213,20 +185,20 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "a0df7c3afe360c9af59a76e45007dbf4e53b5131",
|
"ref": "a0df7c3afe360c9af59a76e45007dbf4e53b5131",
|
||||||
"url": "https://github.com/FunkinCrew/openfl-mobile"
|
"url": "https://github.com/FunkinCrew/openfl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "polymod",
|
"name": "polymod",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "866f19edbcd872b3358f9a41f2f6a24c71c191d1",
|
"ref": "55ee00236806330c89c3bff1e1563eb6425a9d96",
|
||||||
"url": "https://github.com/larsiusprime/polymod"
|
"url": "https://github.com/larsiusprime/polymod"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "thx.core",
|
"name": "thx.core",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "76d87418fadd92eb8e1b61f004cff27d656e53dd",
|
"ref": "2bf2b992e06159510f595554e6b952e47922f128",
|
||||||
"url": "https://github.com/fponticelli/thx.core"
|
"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 !!"
|
||||||
+203
-120
@@ -5,8 +5,10 @@ import hxp.*;
|
|||||||
import lime.tools.*;
|
import lime.tools.*;
|
||||||
import sys.FileSystem;
|
import sys.FileSystem;
|
||||||
import sys.io.File;
|
import sys.io.File;
|
||||||
|
import haxe.io.Bytes;
|
||||||
import haxe.io.Path;
|
import haxe.io.Path;
|
||||||
import haxe.ds.Map;
|
import haxe.ds.Map;
|
||||||
|
import haxe.xml.Printer;
|
||||||
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
@@ -29,13 +31,13 @@ class Project extends HXProject
|
|||||||
* REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES!
|
* REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES!
|
||||||
* You only have to change it here, the rest of the game will query this value.
|
* You only have to change it here, the rest of the game will query this value.
|
||||||
*/
|
*/
|
||||||
static final VERSION:String = "0.7.3";
|
static final VERSION:String = "0.7.4";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The build's number and version code.
|
* The build's number and version code.
|
||||||
* Used when publishing the game to mobile app stores. Should increment with each patch.
|
* 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.
|
* The game's name. Used as the default window title.
|
||||||
@@ -58,6 +60,11 @@ class Project extends HXProject
|
|||||||
*/
|
*/
|
||||||
static final SOURCE_DIR:String = "source";
|
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.
|
* The fully qualified class path for the game's preloader.
|
||||||
* Particularly important on HTML5 but we use it on all platforms.
|
* Particularly important on HTML5 but we use it on all platforms.
|
||||||
@@ -103,30 +110,11 @@ class Project extends HXProject
|
|||||||
|
|
||||||
static final ANDROID_EXTENSIONS:Array<String> = ["funkin.extensions.CallbackUtil"];
|
static final ANDROID_EXTENSIONS:Array<String> = ["funkin.extensions.CallbackUtil"];
|
||||||
|
|
||||||
//
|
|
||||||
// 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";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The team ID to use for the iOS app. Configured in XCode.
|
* The team ID to use for the iOS app. Configured in XCode.
|
||||||
*/
|
*/
|
||||||
static var IOS_TEAM_ID:String = "Z7G7AVNGSH";
|
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.
|
* A list of asset file globs to exclude from ASTC compression when creating optimized mobile builds.
|
||||||
*/
|
*/
|
||||||
@@ -330,10 +318,10 @@ class Project extends HXProject
|
|||||||
static final FEATURE_HAPTICS:FeatureFlag = "FEATURE_HAPTICS";
|
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.
|
* 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`
|
* `-DFEATURE_LOG_TRACE`
|
||||||
@@ -487,6 +475,7 @@ class Project extends HXProject
|
|||||||
envConfig = readEnvironmentFile("./.env");
|
envConfig = readEnvironmentFile("./.env");
|
||||||
|
|
||||||
flair();
|
flair();
|
||||||
|
|
||||||
configureApp();
|
configureApp();
|
||||||
|
|
||||||
displayTarget();
|
displayTarget();
|
||||||
@@ -498,14 +487,12 @@ class Project extends HXProject
|
|||||||
configureOutputDir();
|
configureOutputDir();
|
||||||
configurePolymod();
|
configurePolymod();
|
||||||
configureHaxelibs();
|
configureHaxelibs();
|
||||||
|
configureASTCTextures();
|
||||||
configureAssets();
|
configureAssets();
|
||||||
configureIcons();
|
|
||||||
|
|
||||||
readASTCExclusion();
|
if (!isLinux())
|
||||||
|
|
||||||
if (FEATURE_COMPRESSED_TEXTURES.isEnabled(this))
|
|
||||||
{
|
{
|
||||||
runASTCCompressor();
|
configureIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FEATURE_MOBILE_ADVERTISEMENTS.isEnabled(this))
|
if (FEATURE_MOBILE_ADVERTISEMENTS.isEnabled(this))
|
||||||
@@ -522,8 +509,6 @@ class Project extends HXProject
|
|||||||
{
|
{
|
||||||
configureIOS();
|
configureIOS();
|
||||||
}
|
}
|
||||||
|
|
||||||
info("Done configuring project.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -532,10 +517,9 @@ class Project extends HXProject
|
|||||||
function flair()
|
function flair()
|
||||||
{
|
{
|
||||||
// TODO: Implement this.
|
// TODO: Implement this.
|
||||||
info("Friday Night Funkin'");
|
info("Friday Night Funkin' - " + VERSION);
|
||||||
info("Initializing build...");
|
info("Initializing build...");
|
||||||
|
|
||||||
info("Target Version: " + VERSION);
|
|
||||||
info("Git Branch: " + getGitBranch());
|
info("Git Branch: " + getGitBranch());
|
||||||
info("Git Commit: " + getGitCommit());
|
info("Git Commit: " + getGitCommit());
|
||||||
info("Git Modified? " + getGitModified());
|
info("Git Modified? " + getGitModified());
|
||||||
@@ -566,6 +550,9 @@ class Project extends HXProject
|
|||||||
// If for some reason we have multiple source directories, we can add more entries here.
|
// If for some reason we have multiple source directories, we can add more entries here.
|
||||||
this.sources.push(SOURCE_DIR);
|
this.sources.push(SOURCE_DIR);
|
||||||
|
|
||||||
|
// Templates stuff
|
||||||
|
this.templatePaths.push(TEMPLATES_DIR);
|
||||||
|
|
||||||
// Tell Lime to run some prebuild and postbuild scripts.
|
// Tell Lime to run some prebuild and postbuild scripts.
|
||||||
this.preBuildCallbacks.push(buildHaxeCLICommand(PREBUILD_HX));
|
this.preBuildCallbacks.push(buildHaxeCLICommand(PREBUILD_HX));
|
||||||
this.postBuildCallbacks.push(buildHaxeCLICommand(POSTBUILD_HX));
|
this.postBuildCallbacks.push(buildHaxeCLICommand(POSTBUILD_HX));
|
||||||
@@ -829,7 +816,7 @@ class Project extends HXProject
|
|||||||
FEATURE_STAGE_EDITOR.apply(this, !(isWeb() || isMobile()));
|
FEATURE_STAGE_EDITOR.apply(this, !(isWeb() || isMobile()));
|
||||||
|
|
||||||
// Should be true except on web builds (some asset stuff breaks it)
|
// 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.
|
// Should be true except on web and mobile builds.
|
||||||
// Screenshots doesn't work there, and mobile has its own screenshots anyway.
|
// Screenshots doesn't work there, and mobile has its own screenshots anyway.
|
||||||
@@ -860,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.
|
// 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).
|
// 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());
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -890,7 +950,7 @@ class Project extends HXProject
|
|||||||
|
|
||||||
if (FEATURE_LOG_TRACE.isDisabled(this))
|
if (FEATURE_LOG_TRACE.isDisabled(this))
|
||||||
{
|
{
|
||||||
addHaxeFlag("--no-traces");
|
setHaxedef("no-traces");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable the built in pause screen when unfocusing the game.
|
// Disable the built in pause screen when unfocusing the game.
|
||||||
@@ -973,17 +1033,17 @@ class Project extends HXProject
|
|||||||
// we use a dedicated 'tracy' folder, since it generally needs a recompile when in use
|
// we use a dedicated 'tracy' folder, since it generally needs a recompile when in use
|
||||||
if (FEATURE_DEBUG_TRACY.isEnabled(this)) buildDir += "-tracy";
|
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!
|
// trailing slash might not be needed, works fine on macOS without it, but I haven't tested on Windows!
|
||||||
buildDir += "/";
|
buildDir += "/";
|
||||||
|
|
||||||
info('Output directory: $buildDir');
|
|
||||||
// setenv('BUILD_DIR', buildDir);
|
|
||||||
app.path = buildDir;
|
app.path = buildDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
function configureAndroid()
|
function configureAndroid()
|
||||||
{
|
{
|
||||||
javaPaths.push(Path.join([SOURCE_DIR, 'funkin/mobile/external/android/java']));
|
javaPaths.push(Path.join([SOURCE_DIR, 'funkin/external/android/java']));
|
||||||
|
|
||||||
if (isRelease())
|
if (isRelease())
|
||||||
{
|
{
|
||||||
@@ -995,47 +1055,37 @@ class Project extends HXProject
|
|||||||
keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD'));
|
keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Reflect.setField(config.get('android.manifest'), 'xmlns:tools', 'http://schemas.android.com/tools');
|
|
||||||
|
|
||||||
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 xmlChildren:Null<Array<String>> = config.get('android.manifest').xmlChildren;
|
|
||||||
|
|
||||||
if (xmlChildren != null)
|
|
||||||
{
|
|
||||||
config.get('android.manifest').xmlChildren = xmlChildren.concat(permissionsConfig);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
config.get('android.manifest').xmlChildren = permissionsConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBundle())
|
if (isBundle())
|
||||||
{
|
{
|
||||||
config.set("android.gradle-project-directory", "bin-bundle");
|
config.set("android.gradle-project-directory", "bin-bundle");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final modsFolderProvider = [
|
final modsFolderProvider:Xml = Xml.createElement('provider');
|
||||||
'<provider android:authorities="$PACKAGE_NAME.docprovider" android:name="funkin.provider.DataFolderProvider" android:grantUriPermissions="true" android:exported="true" android:permission="android.permission.MANAGE_DOCUMENTS">',
|
modsFolderProvider.set('android:authorities', '$PACKAGE_NAME.docprovider');
|
||||||
' <intent-filter>',
|
modsFolderProvider.set('android:name', 'funkin.provider.DataFolderProvider');
|
||||||
' <action android:name="android.content.action.DOCUMENTS_PROVIDER" />',
|
modsFolderProvider.set('android:grantUriPermissions', 'true');
|
||||||
' </intent-filter>',
|
modsFolderProvider.set('android:exported', 'true');
|
||||||
'</provider>'
|
modsFolderProvider.set('android:permission', 'android.permission.MANAGE_DOCUMENTS');
|
||||||
];
|
|
||||||
|
|
||||||
final xmlChildren:Null<Array<String>> = config.get('android.application').xmlChildren;
|
final intentFilter:Xml = Xml.createElement('intent-filter');
|
||||||
|
|
||||||
if (xmlChildren != null)
|
final action:Xml = Xml.createElement('action');
|
||||||
|
action.set('android:name', 'android.content.action.DOCUMENTS_PROVIDER');
|
||||||
|
intentFilter.addChild(action);
|
||||||
|
|
||||||
|
modsFolderProvider.addChild(intentFilter);
|
||||||
|
|
||||||
|
@:nullSafety(Off)
|
||||||
{
|
{
|
||||||
config.get('android.application').xmlChildren = xmlChildren.concat(modsFolderProvider);
|
if (config.get('android.application').xmlChildren != null)
|
||||||
}
|
{
|
||||||
else
|
config.get('android.application').xmlChildren.push(Printer.print(modsFolderProvider));
|
||||||
{
|
}
|
||||||
config.get('android.application').xmlChildren = modsFolderProvider;
|
else
|
||||||
|
{
|
||||||
|
config.get('android.application').xmlChildren = [Printer.print(modsFolderProvider)];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final extensions:Null<Array<String>> = config.getArrayString('android.extension');
|
final extensions:Null<Array<String>> = config.getArrayString('android.extension');
|
||||||
@@ -1238,7 +1288,14 @@ class Project extends HXProject
|
|||||||
addAsset("LICENSE.md", "LICENSE.md", "art", shouldEmbedArt);
|
addAsset("LICENSE.md", "LICENSE.md", "art", shouldEmbedArt);
|
||||||
addAsset("CHANGELOG.md", "CHANGELOG.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.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1251,27 +1308,7 @@ class Project extends HXProject
|
|||||||
if (isAndroid())
|
if (isAndroid())
|
||||||
{
|
{
|
||||||
// Adaptive icons
|
// Adaptive icons
|
||||||
// TODO: Add Adapative Icons in Lime
|
adaptiveIcon = new AdaptiveIcon('art/icons/android/', true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else if (isIOS())
|
else if (isIOS())
|
||||||
{
|
{
|
||||||
@@ -1303,7 +1340,24 @@ class Project extends HXProject
|
|||||||
addIcon("art/icons/icon64.png", 64);
|
addIcon("art/icons/icon64.png", 64);
|
||||||
addIcon("art/icons/iconOG.png");
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1360,6 +1414,21 @@ class Project extends HXProject
|
|||||||
return this.architectures.contains(Architecture.X64);
|
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
|
public function isWindows():Bool
|
||||||
{
|
{
|
||||||
return this.target == Platform.WINDOWS;
|
return this.target == Platform.WINDOWS;
|
||||||
@@ -1703,7 +1772,8 @@ class Project extends HXProject
|
|||||||
*/
|
*/
|
||||||
public function error(message:String):Void
|
public function error(message:String):Void
|
||||||
{
|
{
|
||||||
Log.error('${message}');
|
Sys.stderr().write(Bytes.ofString('[ERROR] ${message}'));
|
||||||
|
Sys.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1713,7 +1783,7 @@ class Project extends HXProject
|
|||||||
{
|
{
|
||||||
if (command != "display")
|
if (command != "display")
|
||||||
{
|
{
|
||||||
Log.info('[INFO] ${message}');
|
Sys.println('[INFO] ${message}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1740,46 +1810,67 @@ class Project extends HXProject
|
|||||||
var env = new Map<String, Dynamic>();
|
var env = new Map<String, Dynamic>();
|
||||||
for (line in envFile.split('\n'))
|
for (line in envFile.split('\n'))
|
||||||
{
|
{
|
||||||
if (line == "" || line.startsWith("#")) continue;
|
if (line.length <= 0 || line.startsWith("#") || shouldExcludeEnvKey(line)) continue;
|
||||||
|
|
||||||
var parts = line.split('=');
|
var index:Int = line.indexOf('=');
|
||||||
if (parts.length != 2) continue;
|
|
||||||
|
|
||||||
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;
|
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
|
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)
|
for (i in 0...astcExcludes.length)
|
||||||
astcExcludes[i] = astcExcludes[i].trim();
|
astcExcludes[i] = astcExcludes[i].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isASTCExcluded(filePath:String):Bool
|
public function isASTCExcluded(file:String):Bool
|
||||||
{
|
{
|
||||||
for (exclusion in astcExcludes)
|
for (exclusion in astcExcludes)
|
||||||
{
|
{
|
||||||
if (exclusion.endsWith("/*"))
|
if (exclusion.endsWith("/"))
|
||||||
{
|
{
|
||||||
var normalizedFilePath = Path.normalize(filePath);
|
var normalizedFilePath = Path.normalize(file);
|
||||||
var normalizedExclusion = Path.normalize(exclusion.substr(0, exclusion.length - 2));
|
var normalizedExclusion = Path.normalize(exclusion);
|
||||||
|
|
||||||
if (normalizedFilePath.startsWith(normalizedExclusion)) return true;
|
if (normalizedFilePath.startsWith(normalizedExclusion)) return true;
|
||||||
}
|
}
|
||||||
else if (exclusion.endsWith("/"))
|
else if (exclusion.endsWith("/*"))
|
||||||
{
|
{
|
||||||
var normalizedExclusion = Path.normalize(exclusion);
|
var normalizedExclusion = Path.normalize(exclusion.substr(0, exclusion.length - 2));
|
||||||
var fileDirectory = Path.directory(Path.normalize(filePath));
|
var fileDirectory = Path.directory(Path.normalize(file));
|
||||||
|
|
||||||
if (fileDirectory == normalizedExclusion) return true;
|
if (fileDirectory == normalizedExclusion) return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (filePath == exclusion) return true;
|
if (file == exclusion) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1790,16 +1881,12 @@ class Project extends HXProject
|
|||||||
{
|
{
|
||||||
info('Compressing ASTC textures...');
|
info('Compressing ASTC textures...');
|
||||||
|
|
||||||
var args:Array<String> = ['run', 'astc-compressor'];
|
var args:Array<String> = ['run', 'astc-compressor', 'compress-from-json'];
|
||||||
args = args.concat(['-i', './assets']);
|
args = args.concat(['-json', './astc-compression-data.json']);
|
||||||
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']);
|
|
||||||
|
|
||||||
Sys.command('haxelib', args);
|
Sys.command('haxelib', args);
|
||||||
|
|
||||||
info('Finished compressing ASTC textures!');
|
info('Done compressing ASTC textures.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1807,9 +1894,9 @@ class Project extends HXProject
|
|||||||
* An object representing a feature flag, which can be enabled or disabled.
|
* An object representing a feature flag, which can be enabled or disabled.
|
||||||
* Includes features such as automatic generation of compile defines and inversion.
|
* 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)
|
public function new(input:String)
|
||||||
{
|
{
|
||||||
@@ -1834,13 +1921,11 @@ abstract FeatureFlag(String)
|
|||||||
if (isEnabled(project))
|
if (isEnabled(project))
|
||||||
{
|
{
|
||||||
// If this flag was already enabled, disable the inverse.
|
// If this flag was already enabled, disable the inverse.
|
||||||
project.info('Enabling feature flag ${this}');
|
|
||||||
getInverse().disable(project, false);
|
getInverse().disable(project, false);
|
||||||
}
|
}
|
||||||
else if (getInverse().isEnabled(project))
|
else if (getInverse().isEnabled(project))
|
||||||
{
|
{
|
||||||
// If the inverse flag was already enabled, disable this flag.
|
// If the inverse flag was already enabled, disable this flag.
|
||||||
project.info('Disabling feature flag ${this}');
|
|
||||||
disable(project, false);
|
disable(project, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1848,13 +1933,11 @@ abstract FeatureFlag(String)
|
|||||||
if (enableByDefault)
|
if (enableByDefault)
|
||||||
{
|
{
|
||||||
// Enable this flag if it was unset, and disable the inverse.
|
// Enable this flag if it was unset, and disable the inverse.
|
||||||
project.info('Enabling feature flag ${this}');
|
|
||||||
enable(project, true);
|
enable(project, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Disable this flag if it was unset, and enable the inverse.
|
// Disable this flag if it was unset, and enable the inverse.
|
||||||
project.info('Disabling feature flag ${this}');
|
|
||||||
disable(project, true);
|
disable(project, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ class Main extends Sprite
|
|||||||
|
|
||||||
#if FEATURE_DEBUG_FUNCTIONS
|
#if FEATURE_DEBUG_FUNCTIONS
|
||||||
game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil());
|
game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil());
|
||||||
|
funkin.util.macro.ConsoleMacro.init();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#if !html5
|
#if !html5
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ class Postbuild
|
|||||||
|
|
||||||
var buildTime:Float = roundToTwoDecimals(end - start);
|
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 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
|
static function main():Void
|
||||||
{
|
{
|
||||||
var start:Float = Sys.time();
|
var start:Float = Sys.time();
|
||||||
trace('[PREBUILD] Performing pre-build tasks...');
|
// Sys.println('[INFO] Performing pre-build tasks...');
|
||||||
|
|
||||||
saveBuildTime();
|
saveBuildTime();
|
||||||
|
|
||||||
buildCredsFile();
|
|
||||||
|
|
||||||
var end:Float = Sys.time();
|
var end:Float = Sys.time();
|
||||||
var duration:Float = end - start;
|
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
|
static function saveBuildTime():Void
|
||||||
@@ -41,20 +29,4 @@ class NewgroundsCredentials
|
|||||||
fo.writeDouble(now);
|
fo.writeDouble(now);
|
||||||
fo.close();
|
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 flixel.math.FlxMath;
|
||||||
import funkin.data.song.SongData.SongTimeChange;
|
import funkin.data.song.SongData.SongTimeChange;
|
||||||
import funkin.data.song.SongDataUtils;
|
import funkin.data.song.SongDataUtils;
|
||||||
|
import funkin.play.PlayState;
|
||||||
import funkin.save.Save;
|
import funkin.save.Save;
|
||||||
import funkin.util.TimerUtil.SongSequence;
|
import funkin.util.TimerUtil.SongSequence;
|
||||||
import haxe.Timer;
|
import haxe.Timer;
|
||||||
@@ -113,6 +114,17 @@ class Conductor
|
|||||||
|
|
||||||
if (currentTimeChange == null) return Constants.DEFAULT_BPM;
|
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;
|
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 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)
|
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;
|
this.songPositionDelta += FlxG.elapsed * 1000 * FlxG.sound.music.pitch;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -117,10 +117,7 @@ class FunkinMemory
|
|||||||
*/
|
*/
|
||||||
public static function cacheTexture(key:String):Void
|
public static function cacheTexture(key:String):Void
|
||||||
{
|
{
|
||||||
if (currentCachedTextures.exists(key))
|
if (currentCachedTextures.exists(key)) return;
|
||||||
{
|
|
||||||
return; // Already cached.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previousCachedTextures.exists(key))
|
if (previousCachedTextures.exists(key))
|
||||||
{
|
{
|
||||||
@@ -135,13 +132,13 @@ class FunkinMemory
|
|||||||
if (graphic == null)
|
if (graphic == null)
|
||||||
{
|
{
|
||||||
FlxG.log.warn('Failed to cache graphic: $key');
|
FlxG.log.warn('Failed to cache graphic: $key');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
trace('Successfully cached graphic: $key');
|
||||||
trace('Successfully cached graphic: $key');
|
graphic.persist = true;
|
||||||
graphic.persist = true;
|
currentCachedTextures.set(key, graphic);
|
||||||
currentCachedTextures.set(key, graphic);
|
forceRender(graphic);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,26 +147,54 @@ class FunkinMemory
|
|||||||
*/
|
*/
|
||||||
static function permanentCacheTexture(key:String):Void
|
static function permanentCacheTexture(key:String):Void
|
||||||
{
|
{
|
||||||
if (permanentCachedTextures.exists(key))
|
if (permanentCachedTextures.exists(key)) return;
|
||||||
{
|
|
||||||
return; // Already cached.
|
|
||||||
}
|
|
||||||
|
|
||||||
var graphic:Null<FlxGraphic> = FlxGraphic.fromAssetKey(key, false, null, true);
|
var graphic:Null<FlxGraphic> = FlxGraphic.fromAssetKey(key, false, null, true);
|
||||||
if (graphic == null)
|
if (graphic == null)
|
||||||
{
|
{
|
||||||
FlxG.log.warn('Failed to cache graphic: $key');
|
FlxG.log.warn('Failed to cache graphic: $key');
|
||||||
}
|
return;
|
||||||
else
|
|
||||||
{
|
|
||||||
trace('Successfully cached graphic: $key');
|
|
||||||
graphic.persist = true;
|
|
||||||
permanentCachedTextures.set(key, graphic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace('Successfully cached graphic: $key');
|
||||||
|
graphic.persist = true;
|
||||||
|
permanentCachedTextures.set(key, graphic);
|
||||||
|
forceRender(graphic);
|
||||||
currentCachedTextures = permanentCachedTextures;
|
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.
|
* Prepares the cache for purging unused textures.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+144
-130
@@ -11,9 +11,9 @@ import flixel.math.FlxRect;
|
|||||||
import flixel.system.debug.log.LogStyle;
|
import flixel.system.debug.log.LogStyle;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import funkin.graphics.FunkinSprite;
|
import funkin.graphics.FunkinSprite;
|
||||||
import funkin.data.dialogue.conversation.ConversationRegistry;
|
import funkin.data.dialogue.ConversationRegistry;
|
||||||
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
|
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||||
import funkin.data.dialogue.speaker.SpeakerRegistry;
|
import funkin.data.dialogue.SpeakerRegistry;
|
||||||
import funkin.data.freeplay.album.AlbumRegistry;
|
import funkin.data.freeplay.album.AlbumRegistry;
|
||||||
import funkin.data.freeplay.player.PlayerRegistry;
|
import funkin.data.freeplay.player.PlayerRegistry;
|
||||||
import funkin.data.freeplay.style.FreeplayStyleRegistry;
|
import funkin.data.freeplay.style.FreeplayStyleRegistry;
|
||||||
@@ -55,6 +55,13 @@ import funkin.api.newgrounds.NewgroundsClient;
|
|||||||
@:nullSafety
|
@:nullSafety
|
||||||
class InitState extends FlxState
|
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.
|
* Perform a bunch of game setup, then immediately transition to the title screen.
|
||||||
*/
|
*/
|
||||||
@@ -78,159 +85,163 @@ class InitState extends FlxState
|
|||||||
*/
|
*/
|
||||||
function setupShit():Void
|
function setupShit():Void
|
||||||
{
|
{
|
||||||
//
|
if (!_coreInitialized)
|
||||||
// GAME SETUP
|
{
|
||||||
//
|
//
|
||||||
|
// GAME SETUP
|
||||||
|
//
|
||||||
|
|
||||||
// Setup window events (like callbacks for onWindowClose)
|
// Setup window events (like callbacks for onWindowClose) and fullscreen keybind setup
|
||||||
// and fullscreen keybind setup
|
WindowUtil.initWindowEvents();
|
||||||
WindowUtil.initWindowEvents();
|
|
||||||
// Disable the thing on Windows where it tries to send a bug report to Microsoft because why do they care?
|
|
||||||
WindowUtil.disableCrashHandler();
|
|
||||||
|
|
||||||
#if FEATURE_DEBUG_TRACY
|
#if FEATURE_DEBUG_TRACY
|
||||||
funkin.util.WindowUtil.initTracy();
|
funkin.util.WindowUtil.initTracy();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#if FEATURE_HAPTICS
|
#if FEATURE_HAPTICS
|
||||||
// Setup Haptic feedback
|
// Setup Haptic feedback
|
||||||
extension.haptics.Haptic.initialize();
|
extension.haptics.Haptic.initialize();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#if FEATURE_MOBILE_ADVERTISEMENTS
|
#if FEATURE_MOBILE_ADVERTISEMENTS
|
||||||
// Setup Admob
|
// Setup Admob
|
||||||
funkin.mobile.util.AdMobUtil.init();
|
funkin.mobile.util.AdMobUtil.init();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#if FEATURE_MOBILE_IAP
|
#if FEATURE_MOBILE_IAP
|
||||||
// Setup In-App purchases
|
// Setup In-App purchases
|
||||||
funkin.mobile.util.InAppPurchasesUtil.init();
|
funkin.mobile.util.InAppPurchasesUtil.init();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#if FEATURE_MOBILE_IAR
|
#if FEATURE_MOBILE_IAR
|
||||||
// Setup In-App purchases
|
// Setup In-App reviews
|
||||||
funkin.mobile.util.InAppReviewUtil.init();
|
funkin.mobile.util.InAppReviewUtil.init();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#if ios
|
#if android
|
||||||
// Setup Audio session
|
// Setup Callback util.
|
||||||
funkin.mobile.external.ios.AudioSession.initialize();
|
funkin.external.android.CallbackUtil.init();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
// This ain't a pixel art game! (most of the time)
|
#if ios
|
||||||
FlxSprite.defaultAntialiasing = true;
|
// Setup Audio session
|
||||||
|
funkin.external.ios.AudioSession.initialize();
|
||||||
|
#end
|
||||||
|
|
||||||
// Disable default keybinds for volume (we manually control volume in MusicBeatState with custom binds)
|
// This ain't a pixel art game! (most of the time)
|
||||||
FlxG.sound.volumeUpKeys = [];
|
FlxSprite.defaultAntialiasing = true;
|
||||||
FlxG.sound.volumeDownKeys = [];
|
|
||||||
FlxG.sound.muteKeys = [];
|
|
||||||
|
|
||||||
// A small jumpstart to the soundtray, it usually sets itself to inactive (somewhere...)
|
// Disable default keybinds for volume (we manually control volume in MusicBeatState with custom binds)
|
||||||
// but that makes our soundtray not show up on init if we have the game muted.
|
FlxG.sound.volumeUpKeys = [];
|
||||||
// We set it to active so it at least calls it's update function once (see FlxGame.onEnterFrame(), it's called there)
|
FlxG.sound.volumeDownKeys = [];
|
||||||
// and also see FunkinSoundTray.update() to see what we do and how we check if we are muted or not
|
FlxG.sound.muteKeys = [];
|
||||||
#if !mobile
|
|
||||||
FlxG.game.soundTray.active = true;
|
|
||||||
#end
|
|
||||||
|
|
||||||
// Set the game to a lower frame rate while it is in the background.
|
// A small jumpstart to the soundtray, it usually sets itself to inactive (somewhere...)
|
||||||
FlxG.game.focusLostFramerate = 30;
|
// 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
|
// Set the game to a lower frame rate while it is in the background.
|
||||||
FlxG.fixedTimestep = false;
|
FlxG.game.focusLostFramerate = 30;
|
||||||
|
|
||||||
setupFlixelDebug();
|
// Makes Flixel use frame times instead of locked movements per frame for things like tweens
|
||||||
|
FlxG.fixedTimestep = false;
|
||||||
|
|
||||||
//
|
setupFlixelDebug();
|
||||||
// FLIXEL TRANSITIONS
|
|
||||||
//
|
|
||||||
|
|
||||||
// Diamond Transition
|
//
|
||||||
var diamond:FlxGraphic = FlxGraphic.fromClass(GraphicTransTileDiamond);
|
// FLIXEL TRANSITIONS
|
||||||
diamond.persist = true;
|
//
|
||||||
diamond.destroyOnNoUse = false;
|
|
||||||
|
|
||||||
// NOTE: tileData is ignored if TransitionData.type is FADE instead of TILES.
|
// Diamond Transition
|
||||||
var tileData:TransitionTileData = {asset: diamond, width: 32, height: 32};
|
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,
|
// NOTE: tileData is ignored if TransitionData.type is FADE instead of TILES.
|
||||||
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
var tileData:TransitionTileData = {asset: diamond, width: 32, height: 32};
|
||||||
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;
|
|
||||||
|
|
||||||
FlxG.signals.gameResized.add(function(width:Int, height:Int) {
|
|
||||||
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
|
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));
|
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,
|
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));
|
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||||
});
|
|
||||||
|
|
||||||
// SDL for some reason enables VSync on focus lost/gained in Android
|
FlxG.signals.gameResized.add(function(width:Int, height:Int) {
|
||||||
// Since we don't really need VSync on Android we're gonna forcefully disable it on these signals for now
|
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
|
||||||
// This is fixed on SDL3 from what I've heared but that doodoo isn't working poperly for Android
|
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||||
#if android
|
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData,
|
||||||
FlxG.signals.focusLost.add(function() {
|
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||||
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
});
|
||||||
});
|
|
||||||
FlxG.signals.focusGained.add(function() {
|
|
||||||
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
|
||||||
});
|
|
||||||
#end
|
|
||||||
|
|
||||||
//
|
// SDL for some reason enables VSync on focus lost/gained in Android
|
||||||
// NEWGROUNDS API SETUP
|
// 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 FEATURE_NEWGROUNDS
|
#if android
|
||||||
NewgroundsClient.instance.init();
|
FlxG.signals.focusLost.add(function() {
|
||||||
#end
|
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
||||||
|
});
|
||||||
|
FlxG.signals.focusGained.add(function() {
|
||||||
|
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
||||||
|
});
|
||||||
|
#end
|
||||||
|
|
||||||
//
|
//
|
||||||
// DISCORD API SETUP
|
// NEWGROUNDS API SETUP
|
||||||
//
|
//
|
||||||
#if FEATURE_DISCORD_RPC
|
#if FEATURE_NEWGROUNDS
|
||||||
DiscordClient.instance.init();
|
NewgroundsClient.instance.init();
|
||||||
|
#end
|
||||||
|
|
||||||
lime.app.Application.current.onExit.add(function(exitCode) {
|
//
|
||||||
DiscordClient.instance.shutdown();
|
// DISCORD API SETUP
|
||||||
});
|
//
|
||||||
#end
|
#if FEATURE_DISCORD_RPC
|
||||||
|
DiscordClient.instance.init();
|
||||||
|
|
||||||
//
|
lime.app.Application.current.onExit.add(function(exitCode) {
|
||||||
// ANDROID SETUP
|
DiscordClient.instance.shutdown();
|
||||||
//
|
});
|
||||||
#if android
|
#end
|
||||||
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
|
|
||||||
funkin.mobile.external.android.CallbackUtil.init();
|
|
||||||
#end
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// FLIXEL PLUGINS
|
// ANDROID SETUP
|
||||||
//
|
//
|
||||||
// Plugins provide a useful interface for globally active Flixel objects,
|
#if android
|
||||||
// that receive update events regardless of the current state.
|
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
|
||||||
// TODO: Move scripted Module behavior to a Flixel plugin.
|
#end
|
||||||
#if FEATURE_DEBUG_FUNCTIONS
|
|
||||||
funkin.util.plugins.MemoryGCPlugin.initialize();
|
//
|
||||||
#end
|
// FLIXEL PLUGINS
|
||||||
#if FEATURE_SCREENSHOTS
|
//
|
||||||
funkin.util.plugins.ScreenshotPlugin.initialize();
|
// Plugins provide a useful interface for globally active Flixel objects,
|
||||||
#end
|
// that receive update events regardless of the current state.
|
||||||
#if FEATURE_NEWGROUNDS
|
// TODO: Move scripted Module behavior to a Flixel plugin.
|
||||||
funkin.util.plugins.NewgroundsMedalPlugin.initialize();
|
#if FEATURE_DEBUG_FUNCTIONS
|
||||||
#end
|
funkin.util.plugins.MemoryGCPlugin.initialize();
|
||||||
funkin.util.plugins.EvacuateDebugPlugin.initialize();
|
#end
|
||||||
funkin.util.plugins.ForceCrashPlugin.initialize();
|
#if FEATURE_SCREENSHOTS
|
||||||
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
|
funkin.util.plugins.ScreenshotPlugin.initialize();
|
||||||
#if !mobile
|
#end
|
||||||
funkin.util.plugins.VolumePlugin.initialize();
|
#if FEATURE_NEWGROUNDS
|
||||||
#end
|
funkin.util.plugins.NewgroundsMedalPlugin.initialize();
|
||||||
funkin.util.plugins.WatchPlugin.initialize();
|
#end
|
||||||
#if mobile
|
funkin.util.plugins.EvacuateDebugPlugin.initialize();
|
||||||
funkin.util.plugins.TouchPointerPlugin.initialize();
|
funkin.util.plugins.ForceCrashPlugin.initialize();
|
||||||
funkin.mobile.input.ControlsHandler.initInputTrackers();
|
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
|
||||||
#end
|
#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
|
// GAME DATA PARSING
|
||||||
@@ -279,6 +290,9 @@ class InitState extends FlxState
|
|||||||
*/
|
*/
|
||||||
function startGame():Void
|
function startGame():Void
|
||||||
{
|
{
|
||||||
|
// Don't play transition in when entering the title state.
|
||||||
|
FlxTransitionableState.skipNextTransIn = true;
|
||||||
|
|
||||||
#if SONG
|
#if SONG
|
||||||
// -DSONG=bopeebo
|
// -DSONG=bopeebo
|
||||||
startSong(defineSong(), defineDifficulty());
|
startSong(defineSong(), defineDifficulty());
|
||||||
@@ -386,7 +400,7 @@ class InitState extends FlxState
|
|||||||
*/
|
*/
|
||||||
function startSong(songId:String, difficultyId:String = 'normal'):Void
|
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)
|
if (songData == null)
|
||||||
{
|
{
|
||||||
@@ -458,7 +472,7 @@ class InitState extends FlxState
|
|||||||
|
|
||||||
var targetSong:Null<funkin.play.song.Song> = null;
|
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)
|
if (targetSongId == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package funkin.api.discord;
|
package funkin.api.discord;
|
||||||
|
|
||||||
|
import funkin.util.macro.EnvironmentConfigMacro;
|
||||||
#if FEATURE_DISCORD_RPC
|
#if FEATURE_DISCORD_RPC
|
||||||
import hxdiscord_rpc.Discord;
|
import hxdiscord_rpc.Discord;
|
||||||
import hxdiscord_rpc.Types.DiscordButton;
|
import hxdiscord_rpc.Types.DiscordButton;
|
||||||
@@ -11,7 +12,7 @@ import sys.thread.Thread;
|
|||||||
@:nullSafety
|
@:nullSafety
|
||||||
class DiscordClient
|
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;
|
public static var instance(get, never):DiscordClient;
|
||||||
static var _instance:Null<DiscordClient> = null;
|
static var _instance:Null<DiscordClient> = null;
|
||||||
@@ -40,12 +41,28 @@ class DiscordClient
|
|||||||
{
|
{
|
||||||
trace('[DISCORD] Initializing connection...');
|
trace('[DISCORD] Initializing connection...');
|
||||||
|
|
||||||
// Discord.initialize(CLIENT_ID, handlers, true, null);
|
if (!hasValidCredentials())
|
||||||
Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
|
{
|
||||||
|
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();
|
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;
|
var daemon:Null<Thread> = null;
|
||||||
|
|
||||||
function createDaemon():Void
|
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;
|
package funkin.api.newgrounds;
|
||||||
|
|
||||||
|
import funkin.util.macro.EnvironmentConfigMacro;
|
||||||
import funkin.save.Save;
|
import funkin.save.Save;
|
||||||
import funkin.api.newgrounds.Medals.Medal;
|
import funkin.api.newgrounds.Medals.Medal;
|
||||||
#if FEATURE_NEWGROUNDS
|
#if FEATURE_NEWGROUNDS
|
||||||
@@ -10,13 +11,18 @@ import io.newgrounds.NGLite.LoginOutcome;
|
|||||||
import io.newgrounds.NGLite.LoginFail;
|
import io.newgrounds.NGLite.LoginFail;
|
||||||
import io.newgrounds.objects.events.Outcome;
|
import io.newgrounds.objects.events.Outcome;
|
||||||
import io.newgrounds.utils.MedalList;
|
import io.newgrounds.utils.MedalList;
|
||||||
|
import io.newgrounds.utils.SaveSlotList;
|
||||||
import io.newgrounds.utils.ScoreBoardList;
|
import io.newgrounds.utils.ScoreBoardList;
|
||||||
import io.newgrounds.objects.User;
|
import io.newgrounds.objects.User;
|
||||||
|
|
||||||
@:nullSafety
|
@:nullSafety
|
||||||
class NewgroundsClient
|
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;
|
public static var instance(get, never):NewgroundsClient;
|
||||||
|
|
||||||
static var _instance:Null<NewgroundsClient> = null;
|
static var _instance:Null<NewgroundsClient> = null;
|
||||||
|
|
||||||
static function get_instance():NewgroundsClient
|
static function get_instance():NewgroundsClient
|
||||||
@@ -29,14 +35,15 @@ class NewgroundsClient
|
|||||||
public var user(get, never):Null<User>;
|
public var user(get, never):Null<User>;
|
||||||
public var medals(get, never):Null<MedalList>;
|
public var medals(get, never):Null<MedalList>;
|
||||||
public var leaderboards(get, never):Null<ScoreBoardList>;
|
public var leaderboards(get, never):Null<ScoreBoardList>;
|
||||||
|
public var saveSlots(get, never):Null<SaveSlotList>;
|
||||||
|
|
||||||
private function new()
|
private function new()
|
||||||
{
|
{
|
||||||
trace('[NEWGROUNDS] Initializing client...');
|
trace('[NEWGROUNDS] Initializing client...');
|
||||||
|
|
||||||
#if FEATURE_NEWGROUNDS_DEBUG
|
#if FEATURE_NEWGROUNDS_DEBUG
|
||||||
trace('[NEWGROUNDS] App ID: ${NewgroundsCredentials.APP_ID}');
|
trace('[NEWGROUNDS] App ID: ${APP_ID}');
|
||||||
trace('[NEWGROUNDS] Encryption Key: ${NewgroundsCredentials.ENCRYPTION_KEY}');
|
trace('[NEWGROUNDS] Encryption Key: ${ENCRYPTION_KEY}');
|
||||||
#end
|
#end
|
||||||
|
|
||||||
if (!hasValidCredentials())
|
if (!hasValidCredentials())
|
||||||
@@ -45,9 +52,12 @@ class NewgroundsClient
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var debug = #if FEATURE_NEWGROUNDS_DEBUG true #else false #end;
|
@:nullSafety(Off)
|
||||||
NG.create(NewgroundsCredentials.APP_ID, getSessionId(), debug, onLoginResolved);
|
{
|
||||||
NG.core.setupEncryption(NewgroundsCredentials.ENCRYPTION_KEY);
|
NG.create(APP_ID, getSessionId(), #if FEATURE_NEWGROUNDS_DEBUG true #else false #end, onLoginResolved);
|
||||||
|
|
||||||
|
NG.core.setupEncryption(ENCRYPTION_KEY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
@@ -166,12 +176,12 @@ class NewgroundsClient
|
|||||||
*/
|
*/
|
||||||
static function hasValidCredentials():Bool
|
static function hasValidCredentials():Bool
|
||||||
{
|
{
|
||||||
return !(NewgroundsCredentials.APP_ID == null
|
return !(APP_ID == null
|
||||||
|| NewgroundsCredentials.APP_ID == ""
|
|| APP_ID == ""
|
||||||
|| NewgroundsCredentials.APP_ID.contains(" ")
|
|| (APP_ID != null && APP_ID.contains(" "))
|
||||||
|| NewgroundsCredentials.ENCRYPTION_KEY == null
|
|| ENCRYPTION_KEY == null
|
||||||
|| NewgroundsCredentials.ENCRYPTION_KEY == ""
|
|| ENCRYPTION_KEY == ""
|
||||||
|| NewgroundsCredentials.ENCRYPTION_KEY.contains(" "));
|
|| (ENCRYPTION_KEY != null && ENCRYPTION_KEY.contains(" ")));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoginResolved(outcome:LoginOutcome):Void
|
function onLoginResolved(outcome:LoginOutcome):Void
|
||||||
@@ -236,6 +246,8 @@ class NewgroundsClient
|
|||||||
|
|
||||||
trace('[NEWGROUNDS] Submitting leaderboard request...');
|
trace('[NEWGROUNDS] Submitting leaderboard request...');
|
||||||
NG.core.scoreBoards.loadList(onFetchedLeaderboards);
|
NG.core.scoreBoards.loadList(onFetchedLeaderboards);
|
||||||
|
trace('[NEWGROUNDS] Submitting save slot request...');
|
||||||
|
NG.core.saveSlots.loadList(onFetchedSaveSlots);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoginFailed(result:LoginFail):Void
|
function onLoginFailed(result:LoginFail):Void
|
||||||
@@ -301,6 +313,13 @@ class NewgroundsClient
|
|||||||
// trace(funkin.api.newgrounds.Leaderboards.listLeaderboardData());
|
// 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>
|
function get_user():Null<User>
|
||||||
{
|
{
|
||||||
if (NG.core == null || !this.isLoggedIn()) return null;
|
if (NG.core == null || !this.isLoggedIn()) return null;
|
||||||
@@ -319,6 +338,12 @@ class NewgroundsClient
|
|||||||
return NG.core.scoreBoards;
|
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>
|
static function getSessionId():Null<String>
|
||||||
{
|
{
|
||||||
#if js
|
#if js
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||||||
override function set_volume(value:Float):Float
|
override function set_volume(value:Float):Float
|
||||||
{
|
{
|
||||||
// Uncap the volume.
|
// Uncap the volume.
|
||||||
_volume = FlxMath.bound(value, 0.0, MAX_VOLUME);
|
_volume = value.clamp(0.0, MAX_VOLUME);
|
||||||
updateTransform();
|
updateTransform();
|
||||||
return _volume;
|
return _volume;
|
||||||
}
|
}
|
||||||
@@ -568,8 +568,8 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||||||
if (_sound == null) return;
|
if (_sound == null) return;
|
||||||
|
|
||||||
// Create a channel manually if the sound is considered important.
|
// Create a channel manually if the sound is considered important.
|
||||||
var pan:Float = FlxMath.bound(SoundMixer.__soundTransform.pan + _transform.pan, -1, 1);
|
var pan:Float = (SoundMixer.__soundTransform.pan + _transform.pan).clamp(-1, 1);
|
||||||
var volume:Float = FlxMath.bound(SoundMixer.__soundTransform.volume * _transform.volume, 0, MAX_VOLUME);
|
var volume:Float = (SoundMixer.__soundTransform.volume * _transform.volume).clamp(0, MAX_VOLUME);
|
||||||
|
|
||||||
var audioSource:AudioSource = new AudioSource(_sound.__buffer);
|
var audioSource:AudioSource = new AudioSource(_sound.__buffer);
|
||||||
audioSource.offset = Std.int(startTime);
|
audioSource.offset = Std.int(startTime);
|
||||||
|
|||||||
@@ -5,20 +5,21 @@ import funkin.util.VersionUtil;
|
|||||||
import haxe.Constraints.Constructible;
|
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.
|
* 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 T The type to construct. Must implement `IRegistryEntry`.
|
||||||
* @param J The type of the JSON data used when constructing.
|
* @param J The type of the JSON data used when constructing.
|
||||||
|
* @param P The type of the parameters used for `fetchEntry()`.
|
||||||
*/
|
*/
|
||||||
@:nullSafety
|
@:nullSafety
|
||||||
@:generic
|
@:generic
|
||||||
@:autoBuild(funkin.util.macro.DataRegistryMacro.buildRegistry())
|
@: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.
|
* 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.
|
* @param id The ID of the entry to fetch.
|
||||||
* @return The entry, or `null` if it does not exist.
|
* @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);
|
return entries.get(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class DataParse
|
|||||||
case JArray(values):
|
case JArray(values):
|
||||||
return Either.Left(legacyNoteSectionArray(json, name));
|
return Either.Left(legacyNoteSectionArray(json, name));
|
||||||
case JObject(fields):
|
case JObject(fields):
|
||||||
return Either.Right(cast Tools.getValue(json));
|
return Either.Right(legacyNoteData(json, name));
|
||||||
default:
|
default:
|
||||||
throw 'Expected property $name to be note data, but it was ${json.value}.';
|
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)
|
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)
|
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.
|
* 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.Conversation;
|
||||||
import funkin.play.cutscene.dialogue.ScriptedConversation;
|
import funkin.play.cutscene.dialogue.ScriptedConversation;
|
||||||
@@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
|
|||||||
import funkin.data.DefaultRegistryImpl;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* 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);
|
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;
|
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.play.cutscene.dialogue.DialogueBox;
|
||||||
import funkin.data.dialogue.dialoguebox.DialogueBoxData;
|
import funkin.data.dialogue.DialogueBoxData;
|
||||||
import funkin.play.cutscene.dialogue.ScriptedDialogueBox;
|
import funkin.play.cutscene.dialogue.ScriptedDialogueBox;
|
||||||
import funkin.util.tools.ISingleton;
|
import funkin.util.tools.ISingleton;
|
||||||
import funkin.data.DefaultRegistryImpl;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* 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);
|
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;
|
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.Speaker;
|
||||||
import funkin.play.cutscene.dialogue.ScriptedSpeaker;
|
import funkin.play.cutscene.dialogue.ScriptedSpeaker;
|
||||||
@@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
|
|||||||
import funkin.data.DefaultRegistryImpl;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* 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);
|
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;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* 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);
|
super('ALBUM', 'ui/freeplay/albums', ALBUM_DATA_VERSION_RULE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef AlbumEntryParams = {}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import funkin.util.tools.ISingleton;
|
|||||||
import funkin.data.DefaultRegistryImpl;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* The current version string for the stage data format.
|
||||||
@@ -147,3 +147,5 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData> impleme
|
|||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef PlayerEntryParams = {}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import funkin.util.tools.ISingleton;
|
|||||||
import funkin.data.DefaultRegistryImpl;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* 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);
|
super('FREEPLAYSTYLE', 'ui/freeplay/styles', FREEPLAYSTYLE_DATA_VERSION_RULE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef FreeplayStyleEntryParams = {}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ typedef NoteStyleAssetData<T> =
|
|||||||
var assetPath:String;
|
var assetPath:String;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The scale to render the prop at.
|
* The scale to render the note at.
|
||||||
* @default 1.0
|
* @default 1.0
|
||||||
*/
|
*/
|
||||||
@:default(1.0)
|
@:default(1.0)
|
||||||
@@ -181,7 +181,7 @@ typedef NoteStyleAssetData<T> =
|
|||||||
var offsets:Null<Array<Float>>;
|
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)
|
@:default(false)
|
||||||
@:optional
|
@:optional
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import funkin.util.tools.ISingleton;
|
|||||||
import funkin.data.DefaultRegistryImpl;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* The current version string for the note style data format.
|
||||||
@@ -30,3 +30,5 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData> implement
|
|||||||
return notestyle;
|
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}'
|
return 'SongNoteData(${this.time}ms, ' + (this.length > 0 ? '[${this.length}ms hold]' : '') + ' ${this.data}'
|
||||||
+ (this.kind != '' ? ' [kind: ${this.kind}])' : ')');
|
+ (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;
|
using funkin.data.song.migrator.SongDataMigrator;
|
||||||
|
|
||||||
@:nullSafety
|
@:nullSafety class SongRegistry extends BaseRegistry<Song, SongMetadata, SongEntryParams> implements ISingleton implements DefaultRegistryImpl
|
||||||
class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleton implements DefaultRegistryImpl
|
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The current version string for the stage data format.
|
* 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 static var DEFAULT_GENERATEDBY(get, never):String;
|
||||||
|
|
||||||
|
public var scriptedSongVariations:Map<String, Song> = new Map<String, Song>();
|
||||||
|
|
||||||
static function get_DEFAULT_GENERATEDBY():String
|
static function get_DEFAULT_GENERATEDBY():String
|
||||||
{
|
{
|
||||||
return '${Constants.TITLE} - ${Constants.VERSION}';
|
return '${Constants.TITLE} - ${Constants.VERSION}';
|
||||||
@@ -63,9 +64,17 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
|
|||||||
|
|
||||||
if (entry != null)
|
if (entry != null)
|
||||||
{
|
{
|
||||||
log('Successfully created scripted entry (${entryCls} = ${entry.id})');
|
if (entry.variation != null)
|
||||||
entries.set(entry.id, entry);
|
{
|
||||||
scriptedEntryIds.set(entry.id, entryCls);
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -120,6 +129,28 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
|
|||||||
return parseEntryMetadataRaw(contents);
|
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>
|
public function parseEntryMetadata(id:String, ?variation:String):Null<SongMetadata>
|
||||||
{
|
{
|
||||||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||||
@@ -517,3 +548,11 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
|
|||||||
return allDifficulties;
|
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 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)
|
@:default(funkin.data.song.importer.ChartManifestData.CHART_MANIFEST_DATA_VERSION)
|
||||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||||
@@ -19,7 +21,11 @@ class ChartManifestData
|
|||||||
* The internal song ID for this chart.
|
* The internal song ID for this chart.
|
||||||
* The metadata and chart data file names are derived from this.
|
* 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)
|
public function new(songId:String)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -120,6 +120,11 @@ class LegacyNote
|
|||||||
|
|
||||||
public inline function getKind():String
|
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})';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ class FNFLegacyImporter
|
|||||||
switch (songData.song.speed)
|
switch (songData.song.speed)
|
||||||
{
|
{
|
||||||
case Left(speed):
|
case Left(speed):
|
||||||
// All difficulties will use the one scroll speed.
|
// Sets the scroll speed for the difficulty.
|
||||||
songChartData.scrollSpeed.set('default', speed);
|
songChartData.scrollSpeed.set(difficulty, speed);
|
||||||
case Right(speeds):
|
case Right(speeds):
|
||||||
if (speeds.easy != null) songChartData.scrollSpeed.set('easy', speeds.easy);
|
if (speeds.easy != null) songChartData.scrollSpeed.set('easy', speeds.easy);
|
||||||
if (speeds.normal != null) songChartData.scrollSpeed.set('normal', speeds.normal);
|
if (speeds.normal != null) songChartData.scrollSpeed.set('normal', speeds.normal);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
|
|||||||
import funkin.data.DefaultRegistryImpl;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* 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);
|
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;
|
import funkin.ui.transition.stickers.ScriptedStickerPack;
|
||||||
|
|
||||||
@:nullSafety
|
@:nullSafety
|
||||||
class StickerRegistry extends BaseRegistry<StickerPack, StickerData>
|
class StickerRegistry extends BaseRegistry<StickerPack, StickerData, StickerEntryParams>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The current version string for the sticker pack data format.
|
* The current version string for the sticker pack data format.
|
||||||
@@ -90,3 +90,5 @@ class StickerRegistry extends BaseRegistry<StickerPack, StickerData>
|
|||||||
return ScriptedStickerPack.listScriptClasses();
|
return ScriptedStickerPack.listScriptClasses();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef StickerEntryParams = {}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import funkin.util.tools.ISingleton;
|
|||||||
import funkin.data.DefaultRegistryImpl;
|
import funkin.data.DefaultRegistryImpl;
|
||||||
|
|
||||||
@:nullSafety
|
@: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.
|
* The current version string for the level data format.
|
||||||
@@ -56,3 +56,5 @@ class LevelRegistry extends BaseRegistry<Level, LevelData> implements ISingleton
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef LevelEntryParams = {}
|
||||||
|
|||||||
+4
-6
@@ -1,5 +1,6 @@
|
|||||||
package funkin.mobile.external.android;
|
package funkin.external.android;
|
||||||
|
|
||||||
|
#if android
|
||||||
import lime.system.JNI;
|
import lime.system.JNI;
|
||||||
import flixel.util.FlxSignal;
|
import flixel.util.FlxSignal;
|
||||||
|
|
||||||
@@ -9,7 +10,6 @@ import flixel.util.FlxSignal;
|
|||||||
@:unreflective
|
@:unreflective
|
||||||
class CallbackUtil
|
class CallbackUtil
|
||||||
{
|
{
|
||||||
#if android
|
|
||||||
/**
|
/**
|
||||||
* The result code for `DATA_FOLDER_CLOSED` activity.
|
* The result code for `DATA_FOLDER_CLOSED` activity.
|
||||||
*/
|
*/
|
||||||
@@ -42,7 +42,6 @@ class CallbackUtil
|
|||||||
initCallBackJNI(new CallbackHandler());
|
initCallBackJNI(new CallbackHandler());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,8 +49,7 @@ class CallbackUtil
|
|||||||
*/
|
*/
|
||||||
class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
|
class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
|
||||||
{
|
{
|
||||||
#if android
|
@:allow(funkin.external.android.CallbackUtil)
|
||||||
@:allow(funkin.mobile.external.android.CallbackUtil)
|
|
||||||
function new():Void {}
|
function new():Void {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,5 +66,5 @@ class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
|
|||||||
{
|
{
|
||||||
if (CallbackUtil.onActivityResult != null) CallbackUtil.onActivityResult.dispatch(requestCode, resultCode);
|
if (CallbackUtil.onActivityResult != null) CallbackUtil.onActivityResult.dispatch(requestCode, resultCode);
|
||||||
}
|
}
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
package funkin.mobile.external.android;
|
package funkin.external.android;
|
||||||
|
|
||||||
|
#if android
|
||||||
/**
|
/**
|
||||||
* A Utility class to manage the Application's Data folder on Android.
|
* A Utility class to manage the Application's Data folder on Android.
|
||||||
*/
|
*/
|
||||||
@:unreflective
|
@:unreflective
|
||||||
class DataFolderUtil
|
class DataFolderUtil
|
||||||
{
|
{
|
||||||
#if android
|
|
||||||
/**
|
/**
|
||||||
* Opens the data folder on an Android device using JNI.
|
* Opens the data folder on an Android device using JNI.
|
||||||
*/
|
*/
|
||||||
@@ -19,5 +19,5 @@ class DataFolderUtil
|
|||||||
openDataFolderJNI(CallbackUtil.DATA_FOLDER_CLOSED);
|
openDataFolderJNI(CallbackUtil.DATA_FOLDER_CLOSED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package funkin.mobile.external.android;
|
package funkin.external.android;
|
||||||
|
|
||||||
#if android
|
#if android
|
||||||
import lime.system.JNI;
|
import lime.system.JNI;
|
||||||
Vendored
+3
-3
@@ -1,5 +1,6 @@
|
|||||||
package funkin.mobile.external.android;
|
package funkin.external.android;
|
||||||
|
|
||||||
|
#if android
|
||||||
import lime.math.Rectangle;
|
import lime.math.Rectangle;
|
||||||
import lime.system.JNI;
|
import lime.system.JNI;
|
||||||
|
|
||||||
@@ -9,7 +10,6 @@ import lime.system.JNI;
|
|||||||
@:unreflective
|
@:unreflective
|
||||||
class ScreenUtil
|
class ScreenUtil
|
||||||
{
|
{
|
||||||
#if android
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the dimensions of display cutouts (such as notches) on Android devices.
|
* Retrieves the dimensions of display cutouts (such as notches) on Android devices.
|
||||||
*
|
*
|
||||||
@@ -48,5 +48,5 @@ class ScreenUtil
|
|||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
Vendored
+4
-2
@@ -1,9 +1,10 @@
|
|||||||
package funkin.mobile.external.ios;
|
package funkin.external.ios;
|
||||||
|
|
||||||
|
#if ios
|
||||||
/**
|
/**
|
||||||
* A Utility class to manage iOS audio.
|
* 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')
|
@:include('AudioSession.hpp')
|
||||||
@:unreflective
|
@:unreflective
|
||||||
extern class AudioSession
|
extern class AudioSession
|
||||||
@@ -13,3 +14,4 @@ extern class AudioSession
|
|||||||
@:native('setActive')
|
@:native('setActive')
|
||||||
static function setActive(active:Bool):Void;
|
static function setActive(active:Bool):Void;
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
+4
-2
@@ -1,9 +1,10 @@
|
|||||||
package funkin.mobile.external.ios;
|
package funkin.external.ios;
|
||||||
|
|
||||||
|
#if ios
|
||||||
/**
|
/**
|
||||||
* A Utility class to get iOS screen related informations.
|
* A Utility class to get iOS screen related informations.
|
||||||
*/
|
*/
|
||||||
@:build(funkin.mobile.macros.LinkerMacro.xml('project/Build.xml'))
|
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||||
@:include('ScreenUtil.hpp')
|
@:include('ScreenUtil.hpp')
|
||||||
@:unreflective
|
@:unreflective
|
||||||
extern class ScreenUtil
|
extern class ScreenUtil
|
||||||
@@ -14,3 +15,4 @@ extern class ScreenUtil
|
|||||||
@:native('getScreenSize')
|
@:native('getScreenSize')
|
||||||
static function getScreenSize(width:cpp.RawPointer<Float>, height:cpp.RawPointer<Float>):Void;
|
static function getScreenSize(width:cpp.RawPointer<Float>, height:cpp.RawPointer<Float>):Void;
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
Vendored
+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.graphics.frames.FlxFrame.FlxFrameAngle;
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
import openfl.system.System;
|
import openfl.system.System;
|
||||||
|
import funkin.FunkinMemory;
|
||||||
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
@@ -23,20 +24,6 @@ using StringTools;
|
|||||||
@:nullSafety
|
@:nullSafety
|
||||||
class FunkinSprite extends FlxSprite
|
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 x Starting X position
|
||||||
* @param y Starting Y position
|
* @param y Starting Y position
|
||||||
@@ -216,122 +203,40 @@ class FunkinSprite extends FlxSprite
|
|||||||
return FlxG.bitmap.get(key) != null;
|
return FlxG.bitmap.get(key) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@:deprecated("Use FunkinMemory.cacheTexture() instead")
|
||||||
* Ensure the texture with the given key is cached.
|
|
||||||
* @param key The key of the texture to cache.
|
|
||||||
*/
|
|
||||||
public static function cacheTexture(key:String):Void
|
public static function cacheTexture(key:String):Void
|
||||||
{
|
{
|
||||||
// We don't want to cache the same texture twice.
|
FunkinMemory.cacheTexture(Paths.image(key));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@:deprecated("Use FunkinMemory.permanentCacheTexture() instead")
|
||||||
public static function permanentCacheTexture(key:String):Void
|
public static function permanentCacheTexture(key:String):Void
|
||||||
{
|
{
|
||||||
// We don't want to cache the same texture twice.
|
@:privateAccess FunkinMemory.permanentCacheTexture(Paths.image(key));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@:deprecated("Use FunkinMemory.cacheTexture() instead")
|
||||||
public static function cacheSparrow(key:String):Void
|
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
|
public static function cachePacker(key:String):Void
|
||||||
{
|
{
|
||||||
cacheTexture(Paths.image(key));
|
FunkinMemory.cacheTexture(Paths.image(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@:deprecated("Use FunkinMemory.preparePurgeTextureCache() instead")
|
||||||
* Call this, then `cacheTexture` to keep the textures we still need, then `purgeCache` to remove the textures that we won't be using anymore.
|
|
||||||
*/
|
|
||||||
public static function preparePurgeCache():Void
|
public static function preparePurgeCache():Void
|
||||||
{
|
{
|
||||||
previousCachedTextures = currentCachedTextures;
|
FunkinMemory.preparePurgeTextureCache();
|
||||||
|
|
||||||
for (graphicKey in previousCachedTextures.keys())
|
|
||||||
{
|
|
||||||
if (!permanentCachedTextures.exists(graphicKey)) continue;
|
|
||||||
previousCachedTextures.remove(graphicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentCachedTextures = permanentCachedTextures;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@:deprecated("Use FunkinMemory.purgeCache() instead")
|
||||||
public static function purgeCache():Void
|
public static function purgeCache():Void
|
||||||
{
|
{
|
||||||
// Everything that is in previousCachedTextures but not in currentCachedTextures should be destroyed.
|
FunkinMemory.purgeCache();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static function isGraphicCached(graphic:FlxGraphic):Bool
|
static function isGraphicCached(graphic:FlxGraphic):Bool
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class FlxAtlasSprite extends FlxAnimate
|
|||||||
onAnimationComplete.add(cleanupAnimation);
|
onAnimationComplete.add(cleanupAnimation);
|
||||||
|
|
||||||
// This defaults the sprite to play the first animation in the atlas,
|
// 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.play('');
|
||||||
this.anim.pause();
|
this.anim.pause();
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class PureColor extends FlxShader
|
|||||||
|
|
||||||
function set_col(val:FlxColor):FlxColor
|
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;
|
return val;
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ class PureColor extends FlxShader
|
|||||||
vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
|
vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
|
||||||
|
|
||||||
if (color.a > 0.0 && colSet)
|
if (color.a > 0.0 && colSet)
|
||||||
color = vec4(funnyColor.r, funnyColor.g, funnyColor.b, color.a);
|
color = funnyColor * color.a;
|
||||||
|
|
||||||
gl_FragColor = color;
|
gl_FragColor = color;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class ControlsHandler
|
|||||||
@:noCompletion
|
@:noCompletion
|
||||||
private static function get_hasExternalInputDevice():Bool
|
private static function get_hasExternalInputDevice():Bool
|
||||||
{
|
{
|
||||||
return FlxG.gamepads.numActiveGamepads > 0;
|
return FlxG.gamepads.numActiveGamepads > 0 #if android || extension.androidtools.Tools.isChromebook() #end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@:noCompletion
|
@: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;
|
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;
|
public var restingOpacity:Float;
|
||||||
|
|
||||||
@@ -86,7 +93,7 @@ class FunkinBackButton extends FunkinButton
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
confirming = true;
|
_confirming = true;
|
||||||
|
|
||||||
FlxTween.cancelTweensOf(this);
|
FlxTween.cancelTweensOf(this);
|
||||||
HapticUtil.vibrate(0, 0.05, 0.5);
|
HapticUtil.vibrate(0, 0.05, 0.5);
|
||||||
@@ -98,7 +105,7 @@ class FunkinBackButton extends FunkinButton
|
|||||||
|
|
||||||
animation.onFinish.addOnce(function(name:String) {
|
animation.onFinish.addOnce(function(name:String) {
|
||||||
if (name != 'confirm') return;
|
if (name != 'confirm') return;
|
||||||
confirming = false;
|
_confirming = false;
|
||||||
held = false;
|
held = false;
|
||||||
onConfirmEnd.dispatch();
|
onConfirmEnd.dispatch();
|
||||||
});
|
});
|
||||||
@@ -127,7 +134,7 @@ class FunkinBackButton extends FunkinButton
|
|||||||
onDown.removeAll();
|
onDown.removeAll();
|
||||||
onOut.removeAll();
|
onOut.removeAll();
|
||||||
|
|
||||||
confirming = false;
|
_confirming = false;
|
||||||
held = false;
|
held = false;
|
||||||
|
|
||||||
onUp.add(playConfirmAnim);
|
onUp.add(playConfirmAnim);
|
||||||
|
|||||||
@@ -12,7 +12,16 @@ class FunkinOptionsButton extends FunkinButton
|
|||||||
public var onConfirmStart(default, null):FlxSignal = new FlxSignal();
|
public var onConfirmStart(default, null):FlxSignal = new FlxSignal();
|
||||||
public var onConfirmEnd(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 instant:Bool = false;
|
||||||
var held:Bool = false;
|
var held:Bool = false;
|
||||||
|
|
||||||
@@ -49,7 +58,7 @@ class FunkinOptionsButton extends FunkinButton
|
|||||||
|
|
||||||
function playHoldAnim():Void
|
function playHoldAnim():Void
|
||||||
{
|
{
|
||||||
if (confirming || held) return;
|
if (confirming || held || !enabled) return;
|
||||||
|
|
||||||
held = true;
|
held = true;
|
||||||
|
|
||||||
@@ -70,7 +79,7 @@ class FunkinOptionsButton extends FunkinButton
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
confirming = true;
|
_confirming = true;
|
||||||
|
|
||||||
FlxTween.cancelTweensOf(this);
|
FlxTween.cancelTweensOf(this);
|
||||||
HapticUtil.vibrate(0, 0.05, 0.5);
|
HapticUtil.vibrate(0, 0.05, 0.5);
|
||||||
@@ -86,17 +95,20 @@ class FunkinOptionsButton extends FunkinButton
|
|||||||
|
|
||||||
animation.onFinish.addOnce(function(name:String) {
|
animation.onFinish.addOnce(function(name:String) {
|
||||||
if (name != 'confirm') return;
|
if (name != 'confirm') return;
|
||||||
|
_confirming = false;
|
||||||
|
held = false;
|
||||||
onConfirmEnd.dispatch();
|
onConfirmEnd.dispatch();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function playOutAnim():Void
|
function playOutAnim():Void
|
||||||
{
|
{
|
||||||
if (confirming) return;
|
if (confirming || !enabled) return;
|
||||||
|
|
||||||
FlxTween.cancelTweensOf(this);
|
FlxTween.cancelTweensOf(this);
|
||||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||||
animation.play('idle');
|
animation.play('idle');
|
||||||
|
held = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function resetCallbacks():Void
|
public function resetCallbacks():Void
|
||||||
@@ -105,7 +117,7 @@ class FunkinOptionsButton extends FunkinButton
|
|||||||
onDown.removeAll();
|
onDown.removeAll();
|
||||||
onOut.removeAll();
|
onOut.removeAll();
|
||||||
|
|
||||||
confirming = false;
|
_confirming = false;
|
||||||
held = false;
|
held = false;
|
||||||
|
|
||||||
onUp.add(playConfirmAnim);
|
onUp.add(playConfirmAnim);
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ class ControlsSchemeMenu extends MusicBeatSubState
|
|||||||
*/
|
*/
|
||||||
function setSelection(index:Int):Void
|
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)
|
if (currentIndex != newIndex)
|
||||||
{
|
{
|
||||||
@@ -348,7 +348,7 @@ class ControlsSchemeMenu extends MusicBeatSubState
|
|||||||
hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, showcasesTargetX, elapsed, 0.5);
|
hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, showcasesTargetX, elapsed, 0.5);
|
||||||
|
|
||||||
final minShowcasesX:Float = -1500 * availableSchemes.length;
|
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);
|
final targetIndex:Int = Math.round(hitboxShowcases.x / -1500);
|
||||||
|
|
||||||
@@ -356,8 +356,9 @@ class ControlsSchemeMenu extends MusicBeatSubState
|
|||||||
}
|
}
|
||||||
else
|
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.
|
* 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.
|
* Test ad unit IDs for development and testing purposes.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package funkin.mobile.util;
|
package funkin.mobile.util;
|
||||||
|
|
||||||
#if ios
|
#if ios
|
||||||
import funkin.mobile.external.ios.ScreenUtil as NativeScreenUtil;
|
import funkin.external.ios.ScreenUtil as NativeScreenUtil;
|
||||||
#elseif android
|
#elseif android
|
||||||
import funkin.mobile.external.android.ScreenUtil as NativeScreenUtil;
|
import funkin.external.android.ScreenUtil as NativeScreenUtil;
|
||||||
#end
|
#end
|
||||||
import lime.math.Rectangle;
|
import lime.math.Rectangle;
|
||||||
import lime.system.System;
|
import lime.system.System;
|
||||||
|
|||||||
@@ -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
|
@:nullSafety
|
||||||
class PolymodErrorHandler
|
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
|
public static function onPolymodError(error:PolymodError):Void
|
||||||
{
|
{
|
||||||
// Perform an action based on the error code.
|
// Perform an action based on the error code.
|
||||||
@@ -37,12 +24,12 @@ class PolymodErrorHandler
|
|||||||
// A syntax error when parsing a script.
|
// A syntax error when parsing a script.
|
||||||
logError(error.message);
|
logError(error.message);
|
||||||
// Notify the user via popup.
|
// 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:
|
case SCRIPT_RUNTIME_EXCEPTION:
|
||||||
// A runtime error when running a script.
|
// A runtime error when running a script.
|
||||||
logError(error.message);
|
logError(error.message);
|
||||||
// Notify the user via popup.
|
// 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:
|
case SCRIPT_CLASS_MODULE_NOT_FOUND:
|
||||||
// A scripted class tried to reference an unknown class or module.
|
// A scripted class tried to reference an unknown class or module.
|
||||||
logError(error.message);
|
logError(error.message);
|
||||||
@@ -54,13 +41,12 @@ class PolymodErrorHandler
|
|||||||
msg += '\nCheck to ensure the class exists and is spelled correctly.';
|
msg += '\nCheck to ensure the class exists and is spelled correctly.';
|
||||||
|
|
||||||
// Notify the user via popup.
|
// 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:
|
case SCRIPT_CLASS_MODULE_BLACKLISTED:
|
||||||
// A scripted class tried to reference a blacklisted class or module.
|
// A scripted class tried to reference a blacklisted class or module.
|
||||||
logError(error.message);
|
logError(error.message);
|
||||||
// Notify the user via popup.
|
// Notify the user via popup.
|
||||||
showAlert('Polymod Script Blacklist Violation', error.message);
|
funkin.util.WindowUtil.showError('Polymod Script Blacklist Violation', error.message);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Log the message based on its severity.
|
// Log the message based on its severity.
|
||||||
switch (error.severity)
|
switch (error.severity)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package funkin.modding;
|
package funkin.modding;
|
||||||
|
|
||||||
import polymod.fs.ZipFileSystem;
|
import polymod.fs.ZipFileSystem;
|
||||||
import funkin.data.dialogue.conversation.ConversationRegistry;
|
import funkin.data.dialogue.ConversationRegistry;
|
||||||
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
|
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||||
import funkin.data.dialogue.speaker.SpeakerRegistry;
|
import funkin.data.dialogue.SpeakerRegistry;
|
||||||
import funkin.data.event.SongEventRegistry;
|
import funkin.data.event.SongEventRegistry;
|
||||||
import funkin.data.story.level.LevelRegistry;
|
import funkin.data.story.level.LevelRegistry;
|
||||||
import funkin.data.notestyle.NoteStyleRegistry;
|
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.ScriptedMusicBeatState', funkin.ui.ScriptedMusicBeatState);
|
||||||
Polymod.addImportAlias('funkin.modding.base.ScriptedMusicBeatSubState', funkin.ui.ScriptedMusicBeatSubState);
|
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.
|
// `funkin.util.FileUtil` has unrestricted access to the file system.
|
||||||
Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtilSandboxed);
|
Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtilSandboxed);
|
||||||
|
|
||||||
@@ -557,12 +562,10 @@ class PolymodHandler
|
|||||||
Polymod.clearScripts();
|
Polymod.clearScripts();
|
||||||
|
|
||||||
// Forcibly reload Polymod so it finds any new files.
|
// Forcibly reload Polymod so it finds any new files.
|
||||||
|
// This will also register all scripts.
|
||||||
// TODO: Replace this with loadEnabledMods().
|
// TODO: Replace this with loadEnabledMods().
|
||||||
funkin.modding.PolymodHandler.loadAllMods();
|
funkin.modding.PolymodHandler.loadAllMods();
|
||||||
|
|
||||||
// Reload scripted classes so stages and modules will update.
|
|
||||||
Polymod.registerAllScriptClasses();
|
|
||||||
|
|
||||||
// Reload everything that is cached.
|
// Reload everything that is cached.
|
||||||
// Currently this freezes the game for a second but I guess that's tolerable?
|
// 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.IScriptedClass.IStateChangingScriptedClass;
|
||||||
import funkin.modding.events.ScriptEvent;
|
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.
|
* 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.
|
* 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;
|
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.
|
* Called when the module is initialized.
|
||||||
* It may not be safe to reference other modules here since they may not be loaded yet.
|
* 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.
|
* 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.moduleId = moduleId;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
|
|
||||||
|
if (params != null)
|
||||||
|
{
|
||||||
|
this.state = params.state ?? null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toString()
|
public function toString()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import funkin.modding.events.ScriptEvent;
|
|||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.modding.module.Module;
|
import funkin.modding.module.Module;
|
||||||
import funkin.modding.module.ScriptedModule;
|
import funkin.modding.module.ScriptedModule;
|
||||||
|
import flixel.FlxG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility functions for loading and manipulating active modules.
|
* Utility functions for loading and manipulating active modules.
|
||||||
@@ -145,6 +146,14 @@ class ModuleHandler
|
|||||||
// The module needs to be active to receive events.
|
// The module needs to be active to receive events.
|
||||||
if (module != null && module.active)
|
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);
|
ScriptEventDispatcher.callEvent(module, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import funkin.util.MathUtil;
|
|||||||
import funkin.effects.RetroCameraFade;
|
import funkin.effects.RetroCameraFade;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import funkin.util.TouchUtil;
|
import funkin.util.TouchUtil;
|
||||||
import openfl.utils.Assets;
|
|
||||||
#if FEATURE_MOBILE_ADVERTISEMENTS
|
#if FEATURE_MOBILE_ADVERTISEMENTS
|
||||||
import funkin.mobile.util.AdMobUtil;
|
import funkin.mobile.util.AdMobUtil;
|
||||||
#end
|
#end
|
||||||
@@ -102,18 +101,6 @@ class GameOverSubState extends MusicBeatSubState
|
|||||||
|
|
||||||
var canInput:Bool = false;
|
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)
|
public function new(params:GameOverParams)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
@@ -175,7 +162,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||||||
if ((parentPlayState?.isMinimalMode ?? true)) {}
|
if ((parentPlayState?.isMinimalMode ?? true)) {}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
boyfriend = parentPlayState?.currentStage.getBoyfriend(true);
|
boyfriend = parentPlayState?.currentStage?.getBoyfriend(true);
|
||||||
if (boyfriend != null)
|
if (boyfriend != null)
|
||||||
{
|
{
|
||||||
boyfriend.canPlayOtherAnims = true;
|
boyfriend.canPlayOtherAnims = true;
|
||||||
@@ -198,16 +185,17 @@ class GameOverSubState extends MusicBeatSubState
|
|||||||
addBackButton(FlxG.width - 230, FlxG.height - 200, FlxColor.WHITE, goBack);
|
addBackButton(FlxG.width - 230, FlxG.height - 200, FlxColor.WHITE, goBack);
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION);
|
||||||
|
|
||||||
// Allow input a second later to prevent accidental gameover skips.
|
// Allow input a second later to prevent accidental gameover skips.
|
||||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||||
canInput = true;
|
canInput = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@:nullSafety(Off)
|
|
||||||
function setCameraTarget():Void
|
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.
|
// Assign a camera follow point to the boyfriend's position.
|
||||||
cameraFollowPoint = new FlxObject(parentPlayState.cameraFollowPoint.x, parentPlayState.cameraFollowPoint.y, 1, 1);
|
cameraFollowPoint = new FlxObject(parentPlayState.cameraFollowPoint.x, parentPlayState.cameraFollowPoint.y, 1, 1);
|
||||||
@@ -218,6 +206,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||||||
cameraFollowPoint.y += offsets[1];
|
cameraFollowPoint.y += offsets[1];
|
||||||
add(cameraFollowPoint);
|
add(cameraFollowPoint);
|
||||||
|
|
||||||
|
@:nullSafety(Off)
|
||||||
FlxG.camera.target = null;
|
FlxG.camera.target = null;
|
||||||
FlxG.camera.follow(cameraFollowPoint, LOCKON, Constants.DEFAULT_CAMERA_FOLLOW_RATE / 2);
|
FlxG.camera.follow(cameraFollowPoint, LOCKON, Constants.DEFAULT_CAMERA_FOLLOW_RATE / 2);
|
||||||
targetCameraZoom = (parentPlayState?.currentStage?.camZoom ?? 1.0) * boyfriend.getDeathCameraZoom();
|
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
|
// Start death music before firstDeath gets replaced
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
}
|
}
|
||||||
@@ -408,7 +394,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||||||
// Readd Boyfriend to the stage.
|
// Readd Boyfriend to the stage.
|
||||||
boyfriend.isDead = false;
|
boyfriend.isDead = false;
|
||||||
remove(boyfriend);
|
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.
|
// Snap reset the camera which may have changed because of the player character data.
|
||||||
@@ -454,7 +440,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||||||
#else
|
#else
|
||||||
resetPlaying();
|
resetPlaying();
|
||||||
#end
|
#end
|
||||||
});
|
}, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -578,11 +564,11 @@ class GameOverSubState extends MusicBeatSubState
|
|||||||
PlayStatePlaylist.reset();
|
PlayStatePlaylist.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
var stickerPackId:Null<String> = parentPlayState?.currentChart.stickerPack;
|
var stickerPackId:Null<String> = parentPlayState?.currentChart?.stickerPack;
|
||||||
|
|
||||||
if (stickerPackId == null)
|
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);
|
var playerCharacter:Null<PlayableCharacter> = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? Constants.DEFAULT_CHARACTER);
|
||||||
|
|
||||||
if (playerCharacter != null)
|
if (playerCharacter != null)
|
||||||
@@ -615,108 +601,6 @@ class GameOverSubState extends MusicBeatSubState
|
|||||||
|
|
||||||
var hasPlayedDeathQuote:Bool = false;
|
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
|
public override function destroy():Void
|
||||||
{
|
{
|
||||||
super.destroy();
|
super.destroy();
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ class GitarooPause extends MusicBeatState
|
|||||||
|
|
||||||
var replaySelect:Bool = false;
|
var replaySelect:Bool = false;
|
||||||
|
|
||||||
var previousParams:PlayStateParams;
|
var previousParams:Null<PlayStateParams>;
|
||||||
|
|
||||||
public function new(previousParams:PlayStateParams):Void
|
public function new(?previousParams:PlayStateParams):Void
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|||||||
@@ -213,6 +213,11 @@ class PauseSubState extends MusicBeatSubState
|
|||||||
*/
|
*/
|
||||||
var menuEntryText:FlxTypedSpriteGroup<AtlasText>;
|
var menuEntryText:FlxTypedSpriteGroup<AtlasText>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that gets called once substate gets open.
|
||||||
|
*/
|
||||||
|
var onPause:Void->Void;
|
||||||
|
|
||||||
// ===============
|
// ===============
|
||||||
// Audio Variables
|
// Audio Variables
|
||||||
// ===============
|
// ===============
|
||||||
@@ -222,10 +227,11 @@ class PauseSubState extends MusicBeatSubState
|
|||||||
// Constructor
|
// Constructor
|
||||||
// ===============
|
// ===============
|
||||||
|
|
||||||
public function new(?params:PauseSubStateParams)
|
public function new(?params:PauseSubStateParams, ?onPause:Void->Void)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
this.currentMode = params?.mode ?? Standard;
|
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);
|
AdMobUtil.addBanner(extension.admob.AdmobBannerSize.BANNER, extension.admob.AdmobBannerAlign.TOP_LEFT);
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
if (onPause != null) onPause();
|
||||||
|
|
||||||
super.create();
|
super.create();
|
||||||
|
|
||||||
startPauseMusic();
|
startPauseMusic();
|
||||||
@@ -286,6 +294,7 @@ class PauseSubState extends MusicBeatSubState
|
|||||||
hapticTimer.cancel();
|
hapticTimer.cancel();
|
||||||
hapticTimer = null;
|
hapticTimer = null;
|
||||||
pauseMusic.stop();
|
pauseMusic.stop();
|
||||||
|
onPause = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===============
|
// ===============
|
||||||
@@ -930,7 +939,8 @@ class PauseSubState extends MusicBeatSubState
|
|||||||
*/
|
*/
|
||||||
static function changeDifficulty(state:PauseSubState, difficulty:String):Void
|
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
|
// 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.
|
// So if you switch difficulty on the last song of a week you get a really low overall score.
|
||||||
|
|||||||
+360
-242
File diff suppressed because it is too large
Load Diff
@@ -527,8 +527,7 @@ class ResultState extends MusicBeatSubState
|
|||||||
bgFlash.visible = true;
|
bgFlash.visible = true;
|
||||||
FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24);
|
FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24);
|
||||||
// NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors.
|
// NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors.
|
||||||
var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : (params.scoreData.tallies.sick + params.scoreData.tallies.good
|
var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : Scoring.tallyCompletion(params.scoreData.tallies) * 100;
|
||||||
- params.scoreData.tallies.missed) / params.scoreData.tallies.totalNotes * 100;
|
|
||||||
clearPercentTarget = Math.floor(clearPercentFloat);
|
clearPercentTarget = Math.floor(clearPercentFloat);
|
||||||
// Prevent off-by-one errors.
|
// Prevent off-by-one errors.
|
||||||
|
|
||||||
@@ -744,6 +743,7 @@ class ResultState extends MusicBeatSubState
|
|||||||
super.draw();
|
super.draw();
|
||||||
|
|
||||||
songName.clipRect = FlxRect.get(Math.max(0, 520 - songName.x), 0, FlxG.width, songName.height);
|
songName.clipRect = FlxRect.get(Math.max(0, 520 - songName.x), 0, FlxG.width, songName.height);
|
||||||
|
clearPercentSmall.forEachAlive(spr -> spr.clipRect = FlxRect.get(Math.max(0, 520 - spr.x), 0, FlxG.width, spr.height));
|
||||||
|
|
||||||
// PROBABLY SHOULD FIX MEMORY FREE OR WHATEVER THE PUT() FUNCTION DOES !!!! FEELS LIKE IT STUTTERS!!!
|
// PROBABLY SHOULD FIX MEMORY FREE OR WHATEVER THE PUT() FUNCTION DOES !!!! FEELS LIKE IT STUTTERS!!!
|
||||||
|
|
||||||
@@ -751,105 +751,6 @@ class ResultState extends MusicBeatSubState
|
|||||||
// maskShaderSongName.frameUV = songName.frame.uv;
|
// maskShaderSongName.frameUV = songName.frame.uv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleAnimationVibrations()
|
|
||||||
{
|
|
||||||
for (atlas in characterAtlasAnimations)
|
|
||||||
{
|
|
||||||
if (atlas == null || atlas.sprite == null) continue;
|
|
||||||
|
|
||||||
switch (rank)
|
|
||||||
{
|
|
||||||
case ScoringRank.PERFECT | ScoringRank.PERFECT_GOLD:
|
|
||||||
switch (playerCharacterId)
|
|
||||||
{
|
|
||||||
// Feel the bed fun :freaky:
|
|
||||||
case "bf":
|
|
||||||
if (atlas.sprite.anim.curFrame > 87 && atlas.sprite.anim.curFrame % 5 == 0)
|
|
||||||
{
|
|
||||||
HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GF slams into the wall.
|
|
||||||
if (atlas.sprite.anim.curFrame == 51)
|
|
||||||
{
|
|
||||||
HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pico drop-kicking Nene.
|
|
||||||
case "pico":
|
|
||||||
if (atlas.sprite.anim.curFrame == 52)
|
|
||||||
{
|
|
||||||
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD, Constants.DEFAULT_VIBRATION_DURATION * 5, Constants.MAX_VIBRATION_AMPLITUDE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ScoringRank.GREAT | ScoringRank.EXCELLENT:
|
|
||||||
switch (playerCharacterId)
|
|
||||||
{
|
|
||||||
// Pico explodes the targets with a rocket launcher.
|
|
||||||
case "pico":
|
|
||||||
// Pico shoots.
|
|
||||||
if (atlas.sprite.anim.curFrame == 45)
|
|
||||||
{
|
|
||||||
HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The targets explode.
|
|
||||||
if (atlas.sprite.anim.curFrame == 50)
|
|
||||||
{
|
|
||||||
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD, Constants.DEFAULT_VIBRATION_DURATION, Constants.MAX_VIBRATION_AMPLITUDE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ScoringRank.GOOD:
|
|
||||||
switch (playerCharacterId)
|
|
||||||
{
|
|
||||||
// Pico shooting the targets.
|
|
||||||
case "pico":
|
|
||||||
if (atlas.sprite.anim.curFrame % 2 != 0) continue;
|
|
||||||
|
|
||||||
final frames:Array<Array<Int>> = [[40, 50], [80, 90], [140, 157]];
|
|
||||||
for (i in 0...frames.length)
|
|
||||||
{
|
|
||||||
if (atlas.sprite.anim.curFrame < frames[i][0] || atlas.sprite.anim.curFrame > frames[i][1]) continue;
|
|
||||||
|
|
||||||
HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ScoringRank.SHIT:
|
|
||||||
switch (playerCharacterId)
|
|
||||||
{
|
|
||||||
// BF falling and GF slams on BF with her ass.
|
|
||||||
case "bf":
|
|
||||||
if (atlas.sprite.anim.curFrame == 5 || atlas.sprite.anim.curFrame == 90)
|
|
||||||
{
|
|
||||||
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD * 2, Constants.DEFAULT_VIBRATION_DURATION * 2, Constants.MAX_VIBRATION_AMPLITUDE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override function update(elapsed:Float):Void
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
maskShaderDifficulty.swagSprX = difficulty.x;
|
maskShaderDifficulty.swagSprX = difficulty.x;
|
||||||
@@ -931,7 +832,10 @@ class ResultState extends MusicBeatSubState
|
|||||||
|
|
||||||
var stickerPackId:Null<String> = null;
|
var stickerPackId:Null<String> = null;
|
||||||
|
|
||||||
var song:Null<Song> = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId);
|
var song:Null<Song> = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId,
|
||||||
|
{
|
||||||
|
variation: params?.variationId
|
||||||
|
});
|
||||||
|
|
||||||
if (song != null)
|
if (song != null)
|
||||||
{
|
{
|
||||||
@@ -1035,8 +939,6 @@ class ResultState extends MusicBeatSubState
|
|||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HapticUtil.hapticsAvailable) handleAnimationVibrations();
|
|
||||||
|
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -580,7 +580,7 @@ class AnimateAtlasCharacter extends BaseCharacter
|
|||||||
|
|
||||||
override function set_alpha(value:Float):Float
|
override function set_alpha(value:Float):Float
|
||||||
{
|
{
|
||||||
value = FlxMath.bound(value, 0, 1);
|
value = value.clamp(0, 1);
|
||||||
|
|
||||||
if (exists && alpha != value)
|
if (exists && alpha != value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -288,35 +288,23 @@ class CharacterDataParser
|
|||||||
{
|
{
|
||||||
var charPath:String = "freeplay/icons/";
|
var charPath:String = "freeplay/icons/";
|
||||||
|
|
||||||
// FunkinCrew please dont skin me alive for copying pixelated icon and changing it a tiny bit
|
final charIDParts:Array<String> = char.split("-");
|
||||||
switch (char)
|
var iconName:String = "";
|
||||||
|
var lastValidIconName:String = "";
|
||||||
|
for (i in 0...charIDParts.length)
|
||||||
{
|
{
|
||||||
case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf" | "bf-dark":
|
iconName += charIDParts[i];
|
||||||
charPath += "bfpixel";
|
|
||||||
case "monster-christmas":
|
if (Assets.exists(Paths.image(charPath + '${iconName}pixel')))
|
||||||
charPath += "monsterpixel";
|
{
|
||||||
case "mom" | "mom-car":
|
lastValidIconName = iconName;
|
||||||
charPath += "mommypixel";
|
}
|
||||||
case "pico-blazin" | "pico-playable" | "pico-speaker" | "pico-pixel" | "pico-holding-nene":
|
|
||||||
charPath += "picopixel";
|
if (i < charIDParts.length - 1) iconName += '-';
|
||||||
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen" | "gf-dark":
|
|
||||||
charPath += "gfpixel";
|
|
||||||
case "dad":
|
|
||||||
charPath += "dadpixel";
|
|
||||||
case "darnell-blazin":
|
|
||||||
charPath += "darnellpixel";
|
|
||||||
case "senpai-angry":
|
|
||||||
charPath += "senpaipixel";
|
|
||||||
case "spooky-dark":
|
|
||||||
charPath += "spookypixel";
|
|
||||||
case "tankman-atlas" | "tankman-bloody":
|
|
||||||
charPath += "tankmanpixel";
|
|
||||||
case "pico-christmas" | "pico-dark":
|
|
||||||
charPath += "picopixel";
|
|
||||||
default:
|
|
||||||
charPath += '${char}pixel';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
charPath += '${lastValidIconName}pixel';
|
||||||
|
|
||||||
if (!Assets.exists(Paths.image(charPath)))
|
if (!Assets.exists(Paths.image(charPath)))
|
||||||
{
|
{
|
||||||
trace('[WARN] Character ${char} has no freeplay icon.');
|
trace('[WARN] Character ${char} has no freeplay icon.');
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ class MultiSparrowCharacter extends BaseCharacter
|
|||||||
{
|
{
|
||||||
trace('Concatenating multi-sparrow atlas: ${asset}');
|
trace('Concatenating multi-sparrow atlas: ${asset}');
|
||||||
subTexture.parent.destroyOnNoUse = false;
|
subTexture.parent.destroyOnNoUse = false;
|
||||||
|
FunkinMemory.cacheTexture(Paths.image(asset));
|
||||||
}
|
}
|
||||||
|
|
||||||
texture.addAtlas(subTexture);
|
texture.addAtlas(subTexture);
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ class HealthIcon extends FunkinSprite
|
|||||||
*/
|
*/
|
||||||
static final POSITION_OFFSET:Int = 26;
|
static final POSITION_OFFSET:Int = 26;
|
||||||
|
|
||||||
|
public var iconOffset:FlxPoint = FlxPoint.get();
|
||||||
|
|
||||||
public function new(char:Null<String>, playerId:Int = 0)
|
public function new(char:Null<String>, playerId:Int = 0)
|
||||||
{
|
{
|
||||||
super(0, 0);
|
super(0, 0);
|
||||||
@@ -151,10 +153,12 @@ class HealthIcon extends FunkinSprite
|
|||||||
*/
|
*/
|
||||||
public function toggleOldIcon():Void
|
public function toggleOldIcon():Void
|
||||||
{
|
{
|
||||||
|
final playState:Null<PlayState> = PlayState.instance;
|
||||||
|
if (playState == null || playState.currentStage == null) return;
|
||||||
if (characterId == 'bf-old')
|
if (characterId == 'bf-old')
|
||||||
{
|
{
|
||||||
isPixel = PlayState.instance.currentStage.getBoyfriend().isPixel;
|
isPixel = playState.currentStage.getBoyfriend()?.isPixel ?? false;
|
||||||
PlayState.instance.currentStage.getBoyfriend().initHealthIcon(false);
|
playState.currentStage.getBoyfriend()?.initHealthIcon(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -180,8 +184,7 @@ class HealthIcon extends FunkinSprite
|
|||||||
loadCharacter(characterId);
|
loadCharacter(characterId);
|
||||||
|
|
||||||
this.size.set(1.0, 1.0);
|
this.size.set(1.0, 1.0);
|
||||||
this.offset.x = 0.0;
|
this.iconOffset.set();
|
||||||
this.offset.y = 0.0;
|
|
||||||
this.flipX = false;
|
this.flipX = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -192,8 +195,15 @@ class HealthIcon extends FunkinSprite
|
|||||||
loadCharacter(characterId);
|
loadCharacter(characterId);
|
||||||
|
|
||||||
this.size.set(data.scale ?? 1.0, data.scale ?? 1.0);
|
this.size.set(data.scale ?? 1.0, data.scale ?? 1.0);
|
||||||
this.offset.x = (data.offsets != null) ? data.offsets[0] : 0.0;
|
if (data.offsets != null && data.offsets.length == 2)
|
||||||
this.offset.y = (data.offsets != null) ? data.offsets[1] : 0.0;
|
{
|
||||||
|
this.iconOffset.set(data.offsets[0], data.offsets[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.iconOffset.set(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
this.flipX = data.flipX ?? false; // Face the OTHER way by default, since that is more common.
|
this.flipX = data.flipX ?? false; // Face the OTHER way by default, since that is more common.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,6 +299,8 @@ class HealthIcon extends FunkinSprite
|
|||||||
|
|
||||||
// Keep the icon centered vertically on the health bar.
|
// Keep the icon centered vertically on the health bar.
|
||||||
this.y = PlayState.instance.healthBar.y - (this.height / 2); // - (PlayState.instance.healthBar.height / 2)
|
this.y = PlayState.instance.healthBar.y - (this.height / 2); // - (PlayState.instance.healthBar.height / 2)
|
||||||
|
|
||||||
|
offset += iconOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class VideoCutscene
|
|||||||
if (!openfl.Assets.exists(filePath))
|
if (!openfl.Assets.exists(filePath))
|
||||||
{
|
{
|
||||||
// Display a popup.
|
// Display a popup.
|
||||||
// lime.app.Application.current.window.alert('Video file does not exist: ${filePath}', 'Error playing video');
|
// funkin.util.WindowUtil.showError('Error playing video', 'Video file does not exist: ${filePath}');
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
// TODO: After moving videos to their own library,
|
// TODO: After moving videos to their own library,
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import flixel.tweens.FlxTween;
|
|||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.util.FlxSort;
|
import flixel.util.FlxSort;
|
||||||
import funkin.audio.FunkinSound;
|
import funkin.audio.FunkinSound;
|
||||||
import funkin.data.dialogue.conversation.ConversationData;
|
import funkin.data.dialogue.ConversationData;
|
||||||
import funkin.data.dialogue.conversation.ConversationData.DialogueEntryData;
|
import funkin.data.dialogue.ConversationData.DialogueEntryData;
|
||||||
import funkin.data.dialogue.conversation.ConversationRegistry;
|
import funkin.data.dialogue.ConversationRegistry;
|
||||||
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
|
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||||
import funkin.data.dialogue.speaker.SpeakerRegistry;
|
import funkin.data.dialogue.SpeakerRegistry;
|
||||||
import funkin.data.IRegistryEntry;
|
import funkin.data.IRegistryEntry;
|
||||||
import funkin.graphics.FunkinSprite;
|
import funkin.graphics.FunkinSprite;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
@@ -89,7 +89,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl
|
|||||||
|
|
||||||
var currentDialogueBox:Null<DialogueBox>;
|
var currentDialogueBox:Null<DialogueBox>;
|
||||||
|
|
||||||
public function new(id:String)
|
public function new(id:String, ?params:Dynamic)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import funkin.audio.FunkinSound;
|
|||||||
import funkin.modding.IScriptedClass.IDialogueScriptedClass;
|
import funkin.modding.IScriptedClass.IDialogueScriptedClass;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import funkin.ui.FullScreenScaleMode;
|
import funkin.ui.FullScreenScaleMode;
|
||||||
import funkin.data.dialogue.dialoguebox.DialogueBoxData;
|
import funkin.data.dialogue.DialogueBoxData;
|
||||||
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
|
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||||
|
|
||||||
class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry<DialogueBoxData>
|
class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry<DialogueBoxData>
|
||||||
{
|
{
|
||||||
@@ -106,7 +106,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
|||||||
return FullScreenScaleMode.wideScale.x - 0.05;
|
return FullScreenScaleMode.wideScale.x - 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function new(id:String)
|
public function new(id:String, ?params:Dynamic)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import funkin.modding.events.ScriptEvent;
|
|||||||
import flixel.graphics.frames.FlxFramesCollection;
|
import flixel.graphics.frames.FlxFramesCollection;
|
||||||
import funkin.util.assets.FlxAnimationUtil;
|
import funkin.util.assets.FlxAnimationUtil;
|
||||||
import funkin.modding.IScriptedClass.IDialogueScriptedClass;
|
import funkin.modding.IScriptedClass.IDialogueScriptedClass;
|
||||||
import funkin.data.dialogue.speaker.SpeakerData;
|
import funkin.data.dialogue.SpeakerData;
|
||||||
import funkin.data.dialogue.speaker.SpeakerRegistry;
|
import funkin.data.dialogue.SpeakerRegistry;
|
||||||
import funkin.ui.FullScreenScaleMode;
|
import funkin.ui.FullScreenScaleMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,7 +86,7 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
|||||||
return FullScreenScaleMode.wideScale.x - 0.05;
|
return FullScreenScaleMode.wideScale.x - 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function new(id:String)
|
public function new(id:String, ?params:Dynamic)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user