Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4e52df156 | ||
|
|
cf70bf2c6c | ||
|
|
fb6d1b36d3 | ||
|
|
0c0c63bc03 | ||
|
|
01620fda9b | ||
|
|
3bcd6813c1 | ||
|
|
d44966efd2 | ||
|
|
79c3e2de11 | ||
|
|
8372d47d11 | ||
|
|
d89ebdf58f | ||
|
|
1e8c472084 | ||
|
|
17cef6f897 | ||
|
|
2cc43ee1f6 | ||
|
|
2277acccfa | ||
|
|
ed86b326ab | ||
|
|
596c5813d7 | ||
|
|
ae68925911 | ||
|
|
d7c3799562 | ||
|
|
47ab14a855 | ||
|
|
940e165612 | ||
|
|
5181023887 | ||
|
|
32e774b005 | ||
|
|
c353cc54a1 | ||
|
|
48d0798e5f | ||
|
|
0463b4e26b | ||
|
|
0c1842038f | ||
|
|
aa0b8cbbbe | ||
|
|
52cd4f6c36 | ||
|
|
6be7e89222 | ||
|
|
abea46939f | ||
|
|
3a5f1e14cf | ||
|
|
849e413bb7 | ||
|
|
b4a7bd7462 | ||
|
|
beb64528fb | ||
|
|
eac940bccb | ||
|
|
aa1c71cafd | ||
|
|
f4bedc359a | ||
|
|
01428dd5cf | ||
|
|
c6e19c8512 | ||
|
|
e30a4ebb7b | ||
|
|
949f409cff | ||
|
|
4c5aa9743e | ||
|
|
3fa2b3574b | ||
|
|
1e41ac3052 | ||
|
|
936812ae1e | ||
|
|
ad3bf2d2d2 | ||
|
|
1b1c2a124b | ||
|
|
2aff2fa201 | ||
|
|
7ec602f6ec | ||
|
|
4fbeffb57c | ||
|
|
f029a27a3f | ||
|
|
8a99fa4184 | ||
|
|
f21b015292 | ||
|
|
bc8e35409b | ||
|
|
006f1dce0a | ||
|
|
78186188d7 | ||
|
|
762486639e | ||
|
|
b5bb9721da | ||
|
|
4029e92181 | ||
|
|
9c7f87c225 | ||
|
|
43420092f3 | ||
|
|
d8cd580535 | ||
|
|
13c08b285d | ||
|
|
6238b72d8c | ||
|
|
bf92bf3f60 | ||
|
|
6467cd75a8 | ||
|
|
32dc25bbf6 | ||
|
|
11cb409bd2 | ||
|
|
f245365ed3 | ||
|
|
e0d0230b88 | ||
|
|
f8634e2b81 | ||
|
|
4f68f63e2d | ||
|
|
e9e0567f8b | ||
|
|
8db768fd2a | ||
|
|
cf814ee4e2 | ||
|
|
43b7be4e71 | ||
|
|
7d555b4155 | ||
|
|
88ad99c3ef | ||
|
|
d92b7ff3f5 | ||
|
|
66c6a78319 | ||
|
|
e6a2a09801 | ||
|
|
8944e07fe1 | ||
|
|
188260ea81 | ||
|
|
22c6ce42ba | ||
|
|
d8445fe54a | ||
|
|
c3592ee6f5 | ||
|
|
e402328969 | ||
|
|
150a1d5b0a | ||
|
|
d468477663 | ||
|
|
ecaf51ef41 | ||
|
|
05627a50d5 | ||
|
|
efe333da92 | ||
|
|
e946b54249 | ||
|
|
1d4a94041b | ||
|
|
11da19ce1e | ||
|
|
126ed3a67a | ||
|
|
468197e74e | ||
|
|
7f17ad8bb8 | ||
|
|
e9ef340c63 | ||
|
|
61bda6c2c2 | ||
|
|
07f649ca37 | ||
|
|
11ecd07606 | ||
|
|
822c5ba405 | ||
|
|
d61f89b0e4 | ||
|
|
9dfae07225 | ||
|
|
01fad012f2 | ||
|
|
9843f9549c | ||
|
|
044106e8a9 | ||
|
|
d338be9270 | ||
|
|
e4d3e18e4f | ||
|
|
dd104365e7 | ||
|
|
b0581f728a | ||
|
|
5e50432b30 | ||
|
|
314d8af9c8 | ||
|
|
f91261f748 | ||
|
|
0567849665 | ||
|
|
a701e6a4a9 | ||
|
|
abfc4ca79c | ||
|
|
797a8bfcba | ||
|
|
d70838cca9 | ||
|
|
0d845154ef | ||
|
|
9e04db2c5c | ||
|
|
c0d3b9daa2 | ||
|
|
0f336ae0e6 | ||
|
|
8c8eedf17a | ||
|
|
793b533b88 | ||
|
|
852a74c33d | ||
|
|
bfdafd7f37 | ||
|
|
37d0d1b14e | ||
|
|
2649e247fa | ||
|
|
fafe157a70 | ||
|
|
39a4292dc5 | ||
|
|
aad0a0afb6 | ||
|
|
f8516cfb4d |
@@ -6,7 +6,6 @@ dump/
|
||||
temp/
|
||||
docs/temp/
|
||||
export/
|
||||
project/
|
||||
astc-textures/
|
||||
RECOVER_*.fla
|
||||
shitAudio/
|
||||
|
||||
+278
-99
@@ -4,119 +4,298 @@ All notable changes will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.7.0] - 2025-06-??
|
||||
|
||||
### Added
|
||||
|
||||
- Friday Night Funkin' now has an OFFICIAL mobile version, available on the [Google Play Store]() and [Apple App Store]()!
|
||||
- This version is 100% feature complete with the desktop version of the game.
|
||||
- [MOBILE] New touch input compatiblity for all menus.
|
||||
- [MOBILE] Added banner and interstitial advertisements to the game. You can use an in-app purchase to upgrade to the full version to permanently disable advertisements.
|
||||
- [MOBILE] Graphics are compressed using the ASTC algorithm, decreasing memory usage in exchange for a slightly bigger file size.
|
||||
- [MOBILE] Added haptic feedback to several areas in the game.
|
||||
- A whopping ZERO new songs! Woah!
|
||||
- Added null-safety to a bunch of classes within the source code.
|
||||
- Added a blank `Object` class for scripts to extend, and made `FlxObject` and `FlxBasic` scriptable. ([1c36548](https://github.com/FunkinCrew/Funkin/commit/1c36548861a54e2a8ae49f715056139ce31bbd83)) - by @cyn0x8 in [#3119](https://github.com/FunkinCrew/Funkin/pull/3119)
|
||||
- The Chart Editor now highlights and deletes stacked notes and using a customizable threshold. ([8cae34e](https://github.com/FunkinCrew/Funkin/commit/8cae34eed711bff70e5348ffc6178a0fd69b5846)) - by @NotHyper-474 in [#3574](https://github.com/FunkinCrew/Funkin/pull/3574)
|
||||
- Added the ability to press the Chart Editor keybind in Freeplay with a song capsule selected. ([066c2d3](https://github.com/FunkinCrew/Funkin/commit/066c2d3db04162d3df6af883066fc91e05988d58)) - by @Lasercar in [#4114](https://github.com/FunkinCrew/Funkin/pull/4114)
|
||||
- Added more tween types to certain Chart Editor events. ([5177e12](https://github.com/FunkinCrew/Funkin/commit/5177e1275eb2fb2b016224c139e84debb421b895)) - by @Lasercar in [#4249](https://github.com/FunkinCrew/Funkin/pull/4249)
|
||||
- Added default gamepad controls for two recently added Freeplay controls. ([8dad4f5](https://github.com/FunkinCrew/Funkin/commit/8dad4f55b660a01d4e1a3a903fde684e9876f555)) - by @MrMadera in [#4559](https://github.com/FunkinCrew/Funkin/pull/4559)
|
||||
- Added Perfect (Gold) to the list of available ranks in Results Debug menu. ([0261355](https://github.com/FunkinCrew/Funkin/commit/02613557787581b9c5b7532ea6e69001f1989102)) - by @AbnormalPoof in [#4642](https://github.com/FunkinCrew/Funkin/pull/4642)
|
||||
- Added a few sandboxed classes to give mods limited access to the Discord and Newgrounds APIs. ([6bd6066](https://github.com/FunkinCrew/Funkin/commit/6bd6066d169fd1611a706e984bb8d3ad7109e31f)) - by @KoloInDaCrib in [#5040](https://github.com/FunkinCrew/Funkin/pull/5040)
|
||||
- Pressing Ctrl + N now creates a new stage in the Stage Editor. ([bc54634](https://github.com/FunkinCrew/Funkin/commit/bc546345377f9e9e7b69306b72d494cad02ebaba)) - by @Lasercar in [#5175](https://github.com/FunkinCrew/Funkin/pull/5175)
|
||||
- Added script support for Freeplay Backing Cards. ([92e7588](https://github.com/FunkinCrew/Funkin/commit/92e7588370fcbdc0e959e01e7e52eed5d62323f2)) - by @KoloInDaCrib in [#5233](https://github.com/FunkinCrew/Funkin/pull/5233)
|
||||
- Added a variation indicator next to the Chart Editor playbar difficulty. ([dc7fa97](https://github.com/FunkinCrew/Funkin/commit/dc7fa97d3ca3c00edbab0adc171135af0008735d)) - by @KoloInDaCrib in [#5236](https://github.com/FunkinCrew/Funkin/pull/5236)
|
||||
- Added "Flip character horizontally" to the list of shortcuts in the Animation Editor. ([f73e925](https://github.com/FunkinCrew/funkin.assets/commit/f73e925cbc1e99223095e444f9217839c7d8148e)) - by @AbnormalPoof in [funkin.assets#60](https://github.com/FunkinCrew/funkin.assets/pull/60)
|
||||
|
||||
### Changed
|
||||
|
||||
- Modified several parts of the game to look better on phone screens with wider aspect ratios, up to 20:9.
|
||||
- Fixed a ton of performance issues to help the game run better on mobile devices.
|
||||
- Completely reformatted and optimized every single chart file in the game, along with fixing several chart issues.
|
||||
- Completely reformatted every script file within the game's assets for better readability.
|
||||
- Notes now scroll more smoothly by rendering based on delta timing. ([6ad9ffc](https://github.com/FunkinCrew/Funkin/commit/6ad9ffc7f9d66bbaf6ba343663de4c7268f4be3b)) - by @KutikiPlayz in [#3544](https://github.com/FunkinCrew/Funkin/pull/3544)
|
||||
- Favorite songs in Freeplay are now sorted by Week order instead of alphabetically. ([da0964a](https://github.com/FunkinCrew/Funkin/commit/da0964a7b7bbe4ece1bdbd19233eb6dba0de3ac5)) - by @Hundrec in [#3609](https://github.com/FunkinCrew/Funkin/pull/3609)
|
||||
- The Character Select screen now opens on the currently selected character. ([b233a53](https://github.com/FunkinCrew/Funkin/commit/b233a53710d8ef6b0151d83f5d78e1733fe356f5)) - by @Lasercar in [#4072](https://github.com/FunkinCrew/Funkin/pull/4072)
|
||||
- The undo/redo history is now cleared when loading another song in the Chart Editor. ([4c4045e](https://github.com/FunkinCrew/Funkin/commit/4c4045e274f6a7fa0bc2574468abb3b70ca68f00)) - by @Lasercar in [#4308](https://github.com/FunkinCrew/Funkin/pull/4308)
|
||||
- The Options Menu can now scroll to display more menu items. ([70f0a54](https://github.com/FunkinCrew/Funkin/commit/70f0a54191597bd72a6d30d4d12ef5ece6ba078c)) - by @AbnormalPoof in [#4706](https://github.com/FunkinCrew/Funkin/pull/4706)
|
||||
- The Chart Editor will now only fall back to the first available difficulty if the selected difficulty cannot be found. ([19d75ff](https://github.com/FunkinCrew/Funkin/commit/19d75ffe72f08a6adb5b4ca00cae2f913767047a)) - by @Lasercar in [#4949](https://github.com/FunkinCrew/Funkin/pull/4949)
|
||||
- Adjusted the size of the Beat/Step display in the Chart Editor. ([44b1a77](https://github.com/FunkinCrew/Funkin/commit/44b1a77b3b62146504166b392ea2dfbe6f65b302)) - by @NotHyper-474 in [#4994](https://github.com/FunkinCrew/Funkin/pull/4994)
|
||||
- The Freeplay character select hint now always displays if you have more than one character unlocked. ([7ccf75c](https://github.com/FunkinCrew/Funkin/commit/7ccf75cd869ba4b6f18a5adc01e65e52ae7bb809)) - by @Hundrec in [#5023](https://github.com/FunkinCrew/Funkin/pull/5023)
|
||||
- Raised the FPS cap preference from 300 to 500. ([71d7169](https://github.com/FunkinCrew/Funkin/commit/71d7169ba35c01471b4274671e238b0ac2cf04ed)) - by @Hundrec in [#5044](https://github.com/FunkinCrew/Funkin/pull/5044)
|
||||
- Blacklisted more classes for security reasons. ([cadfa3b](https://github.com/FunkinCrew/Funkin/commit/cadfa3b7ceae2ecabe2d544ddc4c9f453b0dfd56)) - by @NotHyper-474 in [#5185](https://github.com/FunkinCrew/Funkin/pull/5185)
|
||||
- Visualizers now zero out when the game audio is muted. ([3d205e9](https://github.com/FunkinCrew/Funkin/commit/3d205e935553096ab7bbd1879d8cb69bdb550ead)) - by @Lasercar in [#5266](https://github.com/FunkinCrew/Funkin/pull/5266)
|
||||
- Shifted Mommy Mearest's pixel icon to the left in Freeplay. ([d9e8085](https://github.com/FunkinCrew/funkin.assets/commit/d9e8085020cfc516b7c38ec7773081619ab840a4)) - by @KoloInDaCrib in [funkin.assets#197](https://github.com/FunkinCrew/funkin.assets/pull/197)
|
||||
## [0.7.3] - 2025-07-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- Songs no longer skip forward at the beginning with high visual offsets. ([02c64ae](https://github.com/FunkinCrew/Funkin/commit/02c64aea9c2e7fe06f9116312d5ef6c049dc8e9c)) - by @xenkap in [#3732](https://github.com/FunkinCrew/Funkin/pull/3732)
|
||||
- Fixed various issues and added missing functionalities to the Stage Editor. ([15083a4](https://github.com/FunkinCrew/Funkin/commit/15083a477626f67125bfd60a844b42b85a82d8c3)) - by @KoloInDaCrib in [#3974](https://github.com/FunkinCrew/Funkin/pull/3974)
|
||||
- Pressing the Chart Editor keybind during a song now opens to the variation and difficulty you were playing. ([aeb2dd5](https://github.com/FunkinCrew/Funkin/commit/aeb2dd53983b12fa0152861cc51a161115ea2729)) - by @Lasercar in [#4116](https://github.com/FunkinCrew/Funkin/pull/4116)
|
||||
- Dragging a hold note in the Chart Editor now drags its trail along with its head. ([d3d8aaa](https://github.com/FunkinCrew/Funkin/commit/d3d8aaae7bfbfa8975a9573807b9a3ca68a1ff55)) - by @KoloInDaCrib in [#4127](https://github.com/FunkinCrew/Funkin/pull/4127)
|
||||
- The help guide in the Stage Editor can no longer be opened multiple times. ([8c1fa3e](https://github.com/FunkinCrew/Funkin/commit/8c1fa3e630e595eac774e81f2c0f8f32ff97d927)) - by @Lasercar in [#4128](https://github.com/FunkinCrew/Funkin/pull/4128)
|
||||
- The Chart Editor now properly saves audio levels when exiting. ([6da2718](https://github.com/FunkinCrew/Funkin/commit/6da2718f116b45dd969ee74c808b86f63631984c)) - by @Lasercar in [#4149](https://github.com/FunkinCrew/Funkin/pull/4149)
|
||||
- Holding Ctrl and clicking on a hold note trail no longer crashes the Chart Editor. ([279bf2a](https://github.com/FunkinCrew/Funkin/commit/279bf2acd0e61b890a63b2dc4c82c6f6dc3c9ffd)) - by @Lasercar in [#4203](https://github.com/FunkinCrew/Funkin/pull/4203)
|
||||
- Removed spammy console traces from `DiscordClient`. ([e89f9f5](https://github.com/FunkinCrew/Funkin/commit/e89f9f50dc6085e3550736459ce9e4fd02c1fc5b)) - by @AbnormalPoof in [#4207](https://github.com/FunkinCrew/Funkin/pull/4207)
|
||||
- The Debug Menu can no longer be opened after selecting an item in the Main Menu. ([5695bc2](https://github.com/FunkinCrew/Funkin/commit/5695bc20e721f0afd5b97a36167f13e550c12b16)) - by @Lasercar in [#4211](https://github.com/FunkinCrew/Funkin/pull/4211)
|
||||
- Fixed the Chart Editor timer occasionally displaying incorrect millisecond values. ([26dc895](https://github.com/FunkinCrew/Funkin/commit/26dc895a27e0d7e49469251cd68b83be66384e15)) - by @Hundrec in [#4257](https://github.com/FunkinCrew/Funkin/pull/4257)
|
||||
- The Chart Editor "Load Metadata File" and "Load Chart File" buttons now function properly. ([5eecef1](https://github.com/FunkinCrew/Funkin/commit/5eecef17a143cbc8cf2307bc29a20c96b8eed6a8)) - by @Lasercar in [#4278](https://github.com/FunkinCrew/Funkin/pull/4278)
|
||||
- The "New Highscore" text no longer appears more than once in the Results screen. ([c0d8e1b](https://github.com/FunkinCrew/Funkin/commit/c0d8e1b70356ec3492e57602a7d30fc2c595544b)) - by @Lasercar in [#4319](https://github.com/FunkinCrew/Funkin/pull/4319)
|
||||
- The Story Mode Weekend 1 level title no longer clips into other level titles below it. ([b86efaa](https://github.com/FunkinCrew/Funkin/commit/b86efaaf7bc6b96fc3234b8f22420c2178fa4306)) - by @KoloInDaCrib in [#4348](https://github.com/FunkinCrew/Funkin/pull/4348)
|
||||
- The debug cursor is now always hidden when the game starts. ([6222c38](https://github.com/FunkinCrew/Funkin/commit/6222c389e301fa2bb4939697376c4e51e29a9977)) - by @Hundrec in [#4520](https://github.com/FunkinCrew/Funkin/pull/4520)
|
||||
- Fixed Newgrounds score submissions for Lit Up and Lit Up (BF Mix). ([183cec6](https://github.com/FunkinCrew/Funkin/commit/183cec62dc1fd3c7f3f634dd3e2400e6ee77b476)) - by @Raltyro in [#4577](https://github.com/FunkinCrew/Funkin/pull/4577)
|
||||
- Freeplay song ranks no longer disappear after changing variations. ([c00f10a](https://github.com/FunkinCrew/Funkin/commit/c00f10adc8ebc7a6876bf20d6a4d15e44bb7d863)) - by @VioletSnowLeopard in [#4583](https://github.com/FunkinCrew/Funkin/pull/4583)
|
||||
- Freeplay song capsules now cycle through long names consistently. ([ae086e7](https://github.com/FunkinCrew/Funkin/commit/ae086e7810437069efd3a4afbd55a5c303f473fb)) - by @VioletSnowLeopard in [#4677](https://github.com/FunkinCrew/Funkin/pull/4677)
|
||||
- Properly blacklist a certain class from scripts. ([3dc7699](https://github.com/FunkinCrew/Funkin/commit/3dc7699aac737998b637ff9a9f16986a434424be)) - by @charlesisfeline in [#4773](https://github.com/FunkinCrew/Funkin/pull/4773)
|
||||
- The song countdown no longer stacks when restarting or continues behind the Pause Menu. ([63eca96](https://github.com/FunkinCrew/Funkin/commit/63eca96c98a87e7155df8b2a1735f269ea83e1b5)) - by @NotHyper-474 in [#4875](https://github.com/FunkinCrew/Funkin/pull/4875)
|
||||
- The charter field in the song metadata now properly displays the charter in the Chart Editor. ([4d24087](https://github.com/FunkinCrew/Funkin/commit/4d24087c2126f64f0bbdf32557e6f0ee7f2aee24)) - by @Lasercar in [#4879](https://github.com/FunkinCrew/Funkin/pull/4879)
|
||||
- The Results Debug menu now shows the correct rank after recent scoring changes. ([66e36b9](https://github.com/FunkinCrew/Funkin/commit/66e36b99051298ddf306d981b3b0f29ddc88ffe3)) - by @NotHyper-474 in [#4905](https://github.com/FunkinCrew/Funkin/pull/4905)
|
||||
- Opening the logs or backups folder before it's created no longer crashes the game. ([60e2b88](https://github.com/FunkinCrew/Funkin/commit/60e2b88703678776994d7a038687773a1c639019)) - by @NotHyper-474 in [#4940](https://github.com/FunkinCrew/Funkin/pull/4940)
|
||||
- An easter egg now restarts the song using the correct instrumental. ([a499f46](https://github.com/FunkinCrew/Funkin/commit/a499f46e6dcec301ab35e62b9e494c7fac2e9c9b)) - by @KoloInDaCrib in [#4956](https://github.com/FunkinCrew/Funkin/pull/4956)
|
||||
- Girlfriend's and Nene's combo drop animations now play consistently. ([34d5ed1](https://github.com/FunkinCrew/Funkin/commit/34d5ed11695cef7348c13505f13fb1da38b7988c)) - by @VioletSnowLeopard in [#4968](https://github.com/FunkinCrew/Funkin/pull/4968)
|
||||
- Fixed a few visual issues with Freeplay's rank slam animation. ([ab817bb](https://github.com/FunkinCrew/Funkin/commit/ab817bb1eab74ae71c1b1fd74d7512a11e6d4339)) - by @Lasercar in [#4986](https://github.com/FunkinCrew/Funkin/pull/4986)
|
||||
- Nonexistent characters no longer crash the Chart Editor. ([3bbb4b0](https://github.com/FunkinCrew/Funkin/commit/3bbb4b06c8c1a1885a18d4354fcfa4363a0c6c75)) - by @Lasercar in [#5008](https://github.com/FunkinCrew/Funkin/pull/5008)
|
||||
- The Chart Editor playhead can no longer be scrolled to before the beginning of the song. ([03a0975](https://github.com/FunkinCrew/Funkin/commit/03a09752991c830be12befd5488fc8a102273c99)) - by @Hundrec in [#5024](https://github.com/FunkinCrew/Funkin/pull/5024)
|
||||
- Fixed incorrect highlighting and squashed text on Freeplay song capsules. ([0c62428](https://github.com/FunkinCrew/Funkin/commit/0c62428fc883c0fa6d09cd403efb287ff3af8c53)) - by @VioletSnowLeopard in [#5036](https://github.com/FunkinCrew/Funkin/pull/5036)
|
||||
- Fixed stuttering throughout the game caused by the Polymod upgrade. (Thanks NotHyper-474!)
|
||||
- [MOBILE] Fixed buttons in the Main Menu not working.
|
||||
- [iOS] The Upgrade button no longer appears if you have already purchased it (actually this time).
|
||||
- Fixed the countdown overlapping itself when restarting the song. (Thanks NotHyper-474!)
|
||||
- Optimized the Week 6 Erect stage.
|
||||
- Fixed an oversight when clearing the cache. (Thanks cherrythecool!)
|
||||
- The Input Offset Test menu text now displays in the correct position.
|
||||
- Fixed script errors appearing in the Week 3 Erect stage.
|
||||
- Fixed adding variations in the Chart Editor erasing difficulties. (Thanks NotHyper-474!)
|
||||
|
||||
## New Contributors for 0.7.3
|
||||
|
||||
* @cherrythecool made their first contribution in [#5458](https://github.com/FunkinCrew/Funkin/pull/5458)
|
||||
|
||||
|
||||
|
||||
## [0.7.2] - 2025-07-18
|
||||
|
||||
### Added
|
||||
|
||||
- [ANDROID] Added a button in the Options menu to access the mods folder.
|
||||
- [MOBILE] Added a preference to adjust the intensity of haptic feedback, ranging from 0.1 to 5.
|
||||
- [MOBILE] Added an easter egg when tapping the player's healthbar icon.
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed default OpenAL configuration settings to improve audio quality. (Thanks Smokey555, cyn0x8, and CCobaltDev!)
|
||||
- The difference may be more or less noticeable on different devices and hardware.
|
||||
- Applies to Desktop and Android, but not iOS yet.
|
||||
- Made several improvements to Polymod and HScript. These changes might break some mods, so please update them accordingly!
|
||||
- Fixed an issue where scripted classes can define two or more fields with the same name.
|
||||
- Fixed an issue causing some syntax errors (such as missing commas) to be ignored by the parser.
|
||||
- Scripted classes can now create static fields and functions.
|
||||
- Scripted classes can now create variables with the `final` keyword.
|
||||
- Scripted classes can now access variables from another scripted class with `class.someVariable`, instead of `class.scriptGet("someVariable")`
|
||||
- This applies to functions too: `class.someFunction()`
|
||||
- Scripted classes that don't extend another class can now be created!
|
||||
- This only works if you access the class in a static context. Creating an instance of said class doesn't work just yet!
|
||||
- Added support for properties (`get_` and `set_` functions) (Thanks KoloInDaCrib!)
|
||||
- Added support for abstracts in a static context. (Thanks lemz1!)
|
||||
- You can now use classes like `FlxColor` properly!
|
||||
- Added support for creating and using enums. (Thanks lemz1!)
|
||||
- You can import them in another script as usual.
|
||||
- Added support for renaming imported classes using the `as` keyword. (Thanks KoloInDaCrib!)
|
||||
- Fixed `try`/`catch` blocks not working properly. (Thanks NotHyper-474!)
|
||||
- Fixed null-safe field access not working properly for functions (ex. `class?.someFunction()). (Thanks KoloInDaCrib!)
|
||||
|
||||
### Fixed
|
||||
|
||||
- [MOBILE] Weekend 1 Story Mode no longer crashes before loading into Blazin'.
|
||||
- [MOBILE] Beating 2hot from Freeplay no longer crashes in the Results screen.
|
||||
- [MOBILE] Retrying and pressing the Back button at the same time no longer crashes the game.
|
||||
- [MOBILE] Pressing the Options and Back buttons at the same time no longer softlocks the game.
|
||||
- [HTML5] Pausing while the train passes by on the Week 3 Erect stage no longer crashes.
|
||||
- [DESKTOP] Getting a Bad/Shit rating on Blazin' no longer breaks animations.
|
||||
- The scroll sound no longer plays once after entering Freeplay.
|
||||
- The Freeplay song preview and album cover now update properly when switching variations.
|
||||
- The Freeplay clear percent counter now consistently displays the correct value on unranked songs.
|
||||
- The Freeplay difficulty star flames no longer become offset from the stars.
|
||||
- The Freeplay difficulty star flames no longer appear during a new rank animation.
|
||||
- The Freeplay menu now correctly assigns the `currentCharacterId`. (Thanks TechnikTil!)
|
||||
- Boyfriend's Perfect (Gold) Results animation now loops properly.
|
||||
- [DESKTOP] The Input Offsets menu no longer activates the debug cursor.
|
||||
- The Input Offsets Test menu no longer generates stacked notes.
|
||||
- The Input Offsets Test menu drums no longer desync from the rest of the track.
|
||||
- The Input Offsets Test menu no longer breaks when a keyboard or controller is connected.
|
||||
- [MOBILE] Sustain trails now display properly with upscroll enabled.
|
||||
- [MOBILE] Added a Back button to the keyboard/gamepad Controls menu.
|
||||
- [iOS] Fixed app name spacing on the Home Screen.
|
||||
- [iOS] Adjusted the preloader to accommodate for different screen sizes.
|
||||
- [iOS] The Upgrade button no longer appears if you have already purchased it.
|
||||
- [ANDROID] Fixed some issues with scrolling.
|
||||
- [ANDROID] Toasts with blank messages no longer appear.
|
||||
- Fixed a critical security vulnerability that could be exploited in mods.
|
||||
- A few more bugfixes and optimizations here and there.
|
||||
|
||||
## New Contributors for 0.7.2
|
||||
|
||||
* @Smokey555 made their first contribution in [#3318](https://github.com/FunkinCrew/Funkin/pull/3318)
|
||||
* @CCobaltDev made their first contribution in [#3318](https://github.com/FunkinCrew/Funkin/pull/3318)
|
||||
|
||||
|
||||
|
||||
## [0.7.1] - 2025-07-15
|
||||
|
||||
### Fixed
|
||||
- Properly implemented ad playback on iOS devices.
|
||||
|
||||
|
||||
|
||||
## [0.7.0] - 2025-07-15
|
||||
|
||||
### Added
|
||||
|
||||
- Friday Night Funkin' now has OFFICIAL mobile versions for Android and iOS, available on the Google Play Store and Apple App Store!
|
||||
- This version contains 100% of the songs from the desktop version of the game.
|
||||
- [MOBILE] New touch input compatibility for all menus.
|
||||
- [MOBILE] Added banner and interstitial advertisements to the game. You can upgrade to the full version through an in-app purchase to permanently disable advertisements.
|
||||
- [MOBILE] Graphics are compressed using the ASTC algorithm, decreasing memory usage in exchange for a slightly larger file size.
|
||||
- [MOBILE] Added haptic feedback to several areas of the game.
|
||||
- Added a visual indicator that shows available difficulties for the currently selected song in Freeplay.
|
||||
- Overhauled the input offsets system, including:
|
||||
- One unified "offset" value.
|
||||
- An "Offset Calibration" screen where the game determines your ideal offset.
|
||||
- A "Test" screen where you can play a short note pattern to try out your offset.
|
||||
- A brand new offset testing theme: Syncobation by Kawai Sprite!
|
||||
- The ability to change your offsets in the Pause Menu, mid-song!
|
||||
- The Input Offsets menu isn't yet available on HTML5, but offsets are still configurable through the Pause Menu.
|
||||
- Added null-safety to a bunch of classes in the source code.
|
||||
- Added the Changelog back to the game files, written by Hundrec and AbnormalPoof!
|
||||
- Added a few sandboxed classes to give mods limited access to the Discord and Newgrounds APIs. ([50d9584](https://github.com/FunkinCrew/Funkin/commit/50d9584a388bd891aa2f8b68a5cde894a6e1ede6)) - by @KoloInDaCrib in [#5040](https://github.com/FunkinCrew/Funkin/pull/5040)
|
||||
- Added script support for Freeplay Backing Cards. ([0001017](https://github.com/FunkinCrew/Funkin/commit/0001017c003be653236c6cc56487c7d0ee33633e)) - by @KoloInDaCrib in [#5233](https://github.com/FunkinCrew/Funkin/pull/5233)
|
||||
- Sparrow results screen animations can now be scriptable. ([7bb2336](https://github.com/FunkinCrew/Funkin/commit/7bb23369727ca4955aa1fbe25e5798809e8169bd)) - by @KoloInDaCrib in [#5168](https://github.com/FunkinCrew/Funkin/pull/5168)
|
||||
- Added a blank `Object` class for scripts to extend, and made `FlxObject` and `FlxBasic` scriptable. ([eb6becc](https://github.com/FunkinCrew/Funkin/commit/eb6becc03fff76117ee3fcbeb32fe254236ca232)) - by @cyn0x8 in [#3119](https://github.com/FunkinCrew/Funkin/pull/3119)
|
||||
- Added default gamepad controls for two recently added Freeplay controls. ([a0d3f8e](https://github.com/FunkinCrew/Funkin/commit/a0d3f8ec553e06b625b463c7989658edbebbbdf5)) - by @MrMadera in [#4559](https://github.com/FunkinCrew/Funkin/pull/4559)
|
||||
- Added the ability to press the Chart Editor keybind in Freeplay with a song capsule selected. ([2221594](https://github.com/FunkinCrew/Funkin/commit/2221594883afa7cd0e518fca7ea975d05626692a)) - by @Lasercar in [#4114](https://github.com/FunkinCrew/Funkin/pull/4114)
|
||||
- The Chart Editor now highlights and deletes stacked notes using a customizable threshold. ([8cae34e](https://github.com/FunkinCrew/Funkin/commit/8cae34eed711bff70e5348ffc6178a0fd69b5846)) - by @NotHyper-474 in [#3574](https://github.com/FunkinCrew/Funkin/pull/3574)
|
||||
- Added a variation indicator next to the Chart Editor playbar difficulty. ([ccd0148](https://github.com/FunkinCrew/Funkin/commit/ccd0148e9b46d512a22b4958d3f289cfc7854965)) - by @KoloInDaCrib in [#5236](https://github.com/FunkinCrew/Funkin/pull/5236)
|
||||
- Added more tween types to certain Chart Editor events. ([5177e12](https://github.com/FunkinCrew/Funkin/commit/5177e1275eb2fb2b016224c139e84debb421b895)) - by @Lasercar in [#4249](https://github.com/FunkinCrew/Funkin/pull/4249)
|
||||
- Pressing Ctrl + N now creates a new stage in the Stage Editor. ([576f8e5](https://github.com/FunkinCrew/Funkin/commit/576f8e54ff8ca8e205241fafa33d0256b62d11d5)) - by @Lasercar in [#5175](https://github.com/FunkinCrew/Funkin/pull/5175)
|
||||
- Added "Flip character horizontally" to the list of shortcuts in the Animation Editor. ([c464cae](https://github.com/FunkinCrew/funkin.assets/commit/c464caec921dcefef7b0b74b2abf95e76ce64491)) - by @AbnormalPoof in [funkin.assets#60](https://github.com/FunkinCrew/funkin.assets/pull/60)
|
||||
- Added Perfect (Gold) to the list of available ranks in Results Debug menu. ([c5308cc](https://github.com/FunkinCrew/Funkin/commit/c5308ccbb9d2b98c62fa4974b8ad7ac1e1ec7d19)) - by @AbnormalPoof in [#4642](https://github.com/FunkinCrew/Funkin/pull/4642)
|
||||
- [MOBILE] Implemented Kevin and Michael.
|
||||
|
||||
### Changed
|
||||
|
||||
- The mod API version now supports v0.7.0, along with v0.6.3. Be sure to check that your mods still work!
|
||||
- Updated the app icon for Desktop platforms.
|
||||
- [MOBILE] Modified several parts of the game to look better on phone screens with wider aspect ratios, up to 20:9.
|
||||
- [DESKTOP] The game now tries to match the window's aspect ratio when changing states, extending as wide as 20:9.
|
||||
- [DESKTOP] Included Mobile stage expansions on Desktop as well. Now you'll have more room for camera events!
|
||||
- Playable Pico and Weekend 1 songs are now always unlocked in Freeplay, even on new saves.
|
||||
- The Freeplay difficulty graphic now scrolls smoothly when changing difficulties.
|
||||
- The "Pause on Unfocus" preference now opens the Pause Menu when unfocusing during a song.
|
||||
- Scripts can now make hold note trails semi-transparent.
|
||||
- Completely reformatted every script file within the game's assets for better readability.
|
||||
- Completely reformatted and optimized every single chart file in the game.
|
||||
- Recharted pico-speaker's chart in Stress
|
||||
- Tweaked charts for the following songs:
|
||||
- Bopeebo [all difficulties] - Removed an extra hey animation event
|
||||
- Bopeebo (Pico Mix) [Hard] - Added a missing note in Section 24
|
||||
- Fresh Erect [Nightmare] - Added a missing grace note for BF in Section 24
|
||||
- South Erect [Nightmare] - Added missing grace notes for BF in Sections 13, 17, and 53
|
||||
- Philly Nice [Hard] - Added missing grace notes for Pico in Sections 30 and 62
|
||||
- Philly Nice [all difficulties] - Added hey animations throughout the song
|
||||
- Philly Nice Erect [Erect] - Added a grace note for BF in Section 33, removed a stacked note for opponent in Section 12
|
||||
- Philly Nice (Pico Mix) [Normal] - Adjusted a left note by 1/96 in Section 60
|
||||
- Blammed (Pico Mix) [Hard] - Added a missing jack in Section 46
|
||||
- Satin Panties [Hard] - Added grace notes in Sections 7-10
|
||||
- Satin Panties [Normal/Hard] - Made Mom sing a sustain rather than two notes in Section 30
|
||||
- High Erect [Erect/Nightmare] - Added a missing note in Section 16
|
||||
- Cocoa [Easy] - Added some notes to reduce sparseness, fixed Mom singing Dad's notes
|
||||
- Cocoa Erect [Erect/Nightmare] - Reimplemented BF's censored notes for Nightmare, adjusted one note by 1/48 in Section 63
|
||||
- Eggnog Erect [Erect/Nightmare] Added two grace notes in Sections 10 and 14 and a missing note for Dad in Section 44
|
||||
- Eggnog (Pico Mix) [Hard] - Added a missing grace note for Pico that was present on Normal in Section 20
|
||||
- Roses [Normal/Hard] - Made Senpai sing a sustain rather than two notes (sneaky)
|
||||
- Roses Erect [Erect/Nightmare] - Mirrored the changes from normal Roses
|
||||
- Guns [all difficulties] - Added a missing note in Sections 28 and 32 and adjusted a hold note's length in Section 73
|
||||
- Stress [Hard] - Split whole notes in halves in Sections 57-60
|
||||
- Darnell [Hard] - Added one missing note for Pico in Section 35
|
||||
- Darnell [all difficulties] - Adjusted camera event timings for consistency
|
||||
- Darnell (BF Mix) [all difficulties] - Removed 3 extra notes and fixed Darnell's pattern being offset
|
||||
- Lit Up [all difficulties] - Added 4 sustains for Darnell throughout the song
|
||||
- Lit Up (BF Mix) [all difficulties] - Added 4 sustains for Darnell throughout the song
|
||||
- 2hot [Easy/Hard] - Fixed remaining offset rhythms (for real this time)
|
||||
- Notes now scroll more smoothly by rendering based on delta timing. ([6ad9ffc](https://github.com/FunkinCrew/Funkin/commit/6ad9ffc7f9d66bbaf6ba343663de4c7268f4be3b)) - by @KutikiPlayz in [#3544](https://github.com/FunkinCrew/Funkin/pull/3544)
|
||||
- The Freeplay character select hint now always displays if you have more than one character unlocked. ([7ccf75c](https://github.com/FunkinCrew/Funkin/commit/7ccf75cd869ba4b6f18a5adc01e65e52ae7bb809)) - by @Hundrec in [#5023](https://github.com/FunkinCrew/Funkin/pull/5023)
|
||||
- Favorite songs in Freeplay are now sorted by Week order instead of alphabetically. ([da0964a](https://github.com/FunkinCrew/Funkin/commit/da0964a7b7bbe4ece1bdbd19233eb6dba0de3ac5)) - by @Hundrec in [#3609](https://github.com/FunkinCrew/Funkin/pull/3609)
|
||||
- Shifted Mommy Mearest's pixel icon to the left in Freeplay. ([d861eba](https://github.com/FunkinCrew/funkin.assets/commit/d861ebac027dd07d0254c79d7c89b59ee04b38f1)) - by @KoloInDaCrib in [funkin.assets#197](https://github.com/FunkinCrew/funkin.assets/pull/197)
|
||||
- The Character Select screen now opens on the currently selected character. ([4819a74](https://github.com/FunkinCrew/Funkin/commit/4819a74c2959cc9b32dfe2cb76c3a4c00e7c7f9a)) - by @Lasercar in [#4072](https://github.com/FunkinCrew/Funkin/pull/4072)
|
||||
- Visualizers now zero out when the game audio is muted. ([6dcec59](https://github.com/FunkinCrew/Funkin/commit/6dcec592f467a0daeb8ff1e0ce122916e36ca869)) - by @Lasercar in [#5266](https://github.com/FunkinCrew/Funkin/pull/5266)
|
||||
- The Options Menu can now scroll to display more menu items. ([70f0a54](https://github.com/FunkinCrew/Funkin/commit/70f0a54191597bd72a6d30d4d12ef5ece6ba078c)) - by @AbnormalPoof in [#4706](https://github.com/FunkinCrew/Funkin/pull/4706)
|
||||
- Raised the FPS cap preference from 300 to 500. ([be73134](https://github.com/FunkinCrew/Funkin/commit/be7313453f70983fff55e69a8b52d741f0cc53b4)) - by @Hundrec in [#5044](https://github.com/FunkinCrew/Funkin/pull/5044)
|
||||
- The Credits menu now uses less memory, especially with many entries. ([1b68c3a](https://github.com/FunkinCrew/Funkin/commit/1b68c3a8d6f66905a9a508a1cb692fe3beb7b4a2)) - by @lemz1 in [#2655](https://github.com/FunkinCrew/Funkin/pull/2655)
|
||||
- Added a timer sequence class to queue up multiple timers in scripts with ease. ([9e182f7](https://github.com/FunkinCrew/Funkin/commit/9e182f70d2bcc92eb68d730d74af143c45f7dcf8)) - by @cyn0x8 in [#2391](https://github.com/FunkinCrew/Funkin/pull/2391)
|
||||
- Replaced smoothLerp and coolLerp with smoothLerpPrecision to fix a few lerp-related bugs. ([94eae11](https://github.com/FunkinCrew/Funkin/commit/94eae116c7a5e6039683d6391208b169378b5ff1)) - by @cyn0x8 in [#3617](https://github.com/FunkinCrew/Funkin/pull/3617)
|
||||
- Fixed empty text strings softlocking the dialogue box. ([88d0e8c](https://github.com/FunkinCrew/Funkin/commit/88d0e8c3b0529654fb7eee8aebb099f7fb346f66)) - by @xenkap in [#4671](https://github.com/FunkinCrew/Funkin/pull/4671)
|
||||
- Adjusted the size of the Beat/Step display in the Chart Editor. ([905181c](https://github.com/FunkinCrew/Funkin/commit/905181c9af29bb11280bda33ef9343069678a762)) - by @NotHyper-474 in [#4994](https://github.com/FunkinCrew/Funkin/pull/4994)
|
||||
- The Chart Editor will now only fall back to the first available difficulty if the selected difficulty cannot be found. ([1c25713](https://github.com/FunkinCrew/Funkin/commit/1c257134648ebd89acf6c9d07f5a0c088fb915c6)) - by @Lasercar in [#4949](https://github.com/FunkinCrew/Funkin/pull/4949)
|
||||
- The undo/redo history is now cleared when loading another song in the Chart Editor. ([426a9c0](https://github.com/FunkinCrew/Funkin/commit/426a9c0c108ac65a042295194679d46444ec1ea5)) - by @Lasercar in [#4308](https://github.com/FunkinCrew/Funkin/pull/4308)
|
||||
- Blacklisted more classes for security reasons. ([cadfa3b](https://github.com/FunkinCrew/Funkin/commit/cadfa3b7ceae2ecabe2d544ddc4c9f453b0dfd56)) - by @NotHyper-474 in [#5185](https://github.com/FunkinCrew/Funkin/pull/5185)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a ton of performance issues to help the game run better on mobile devices.
|
||||
- Exiting the Freeplay Menu no longer freezes the game for a really long time (thanks NotHyper-474!)
|
||||
- Notestyle graphics are now preloaded before the song starts, fixing the stutter at the beginning of the song.
|
||||
- Hitting many hold note trails in one song no longer leads to a lag spike.
|
||||
- The first lightning strike in Week 2 Erect no longer creates a lag spike.
|
||||
- [DESKTOP] The conductor and music no longer gradually drift out of sync to eventually trigger a resync.
|
||||
- Pixel notestyle strumlines are now properly positioned when Downscroll is enabled.
|
||||
- Added the missing graffiti to the wall in the Weekend 1 Blazin' stage.
|
||||
- Accept keybinds now properly scroll faster through the Credits.
|
||||
- Typing in most text fields in debug editors no longer triggers keyboard shortcuts.
|
||||
- The Chart Editor playback speed feature now works properly.
|
||||
- The Chart Editor metronome and hitsounds now play at exactly the right time.
|
||||
- The Chart Editor notification box no longer covers playbar info.
|
||||
- Selecting a Recent File too quickly in the Stage Editor no longer crashes the game.
|
||||
- Blacklisted a few classes for security.
|
||||
- Opening the logs or backups folder before it's created no longer crashes the game. ([d3490f8](https://github.com/FunkinCrew/Funkin/commit/d3490f8c9929eefb9879ad65ce43038c193642d6)) - by @NotHyper-474 in [#4940](https://github.com/FunkinCrew/Funkin/pull/4940)
|
||||
- Fixed a crash when mashing D or I during startup. ([b52c73f](https://github.com/FunkinCrew/Funkin/commit/b52c73f2b0fa32fbc349804621cf947dff2d364e)) - by @CrusherNotDrip in [#5160](https://github.com/FunkinCrew/Funkin/pull/5160)
|
||||
- Hot-reloading with F5 during gameplay no longer crashes the game. ([d2acb5d](https://github.com/FunkinCrew/Funkin/commit/d2acb5d167afd42299d7200ab8c67972044a09c6)) - by @AbnormalPoof in [#5065](https://github.com/FunkinCrew/Funkin/pull/5065)
|
||||
- Hot-reloading with F5 in the Input Offsets menu no longer crashes the game. ([58257f6](https://github.com/FunkinCrew/Funkin/commit/58257f6ac187925f3b23d3f1eef2c812ae569a6b)) - by @NotHyper-474 in [#5085](https://github.com/FunkinCrew/Funkin/pull/5085)
|
||||
- Songs no longer skip forward at the beginning with high offsets. ([1f75a64](https://github.com/FunkinCrew/Funkin/commit/1f75a641e0c80d15f1af10bae7ee71a6ffecf219)) - by @xenkap in [#3732](https://github.com/FunkinCrew/Funkin/pull/3732)
|
||||
- The song countdown no longer stacks when restarting or continues behind the Pause Menu.
|
||||
([63eca96](https://github.com/FunkinCrew/Funkin/commit/63eca96c98a87e7155df8b2a1735f269ea83e1b5)) - by @KoloInDaCrib and @NotHyper-474 in [#4875](https://github.com/FunkinCrew/Funkin/pull/4875)
|
||||
- Fixed incorrect highlighting and squashed text on Freeplay song capsules. ([0c62428](https://github.com/FunkinCrew/Funkin/commit/0c62428fc883c0fa6d09cd403efb287ff3af8c53)) - by @VioletSnowLeopard in [#5036](https://github.com/FunkinCrew/Funkin/pull/5036)
|
||||
- The Freeplay song preview no longer plays twice after returning from Character
|
||||
Select. ([3d3e2bd](https://github.com/FunkinCrew/Funkin/commit/3d3e2bd3786b858143d214caf55be2ee3e9483fc)) - by @Lasercar in [#5248](https://github.com/FunkinCrew/Funkin/pull/5248)
|
||||
- Freeplay song ranks no longer disappear after changing variations. ([7cc9464](https://github.com/FunkinCrew/Funkin/commit/7cc9464573d07996e4bd0d557f82847809d3a786)) - by @VioletSnowLeopard in [#4583](https://github.com/FunkinCrew/Funkin/pull/4583)
|
||||
- Freeplay song capsules now cycle through long names consistently. ([e193f73](https://github.com/FunkinCrew/Funkin/commit/e193f7392a83a04e4aac85fba4f441a78f7b6668)) - by @VioletSnowLeopard in [#4677](https://github.com/FunkinCrew/Funkin/pull/4677)
|
||||
- Fixed a few visual issues with Freeplay's rank slam animation. ([ab817bb](https://github.com/FunkinCrew/Funkin/commit/ab817bb1eab74ae71c1b1fd74d7512a11e6d4339)) - by @Lasercar in [#4986](https://github.com/FunkinCrew/Funkin/pull/4986)
|
||||
- Fixed visual errors in Freeplay after exiting Character Select. ([56a18e1](https://github.com/FunkinCrew/Funkin/commit/56a18e1cf6a15971feebcc828be8818037948cef)) - by @KoloInDaCrib in [#5245](https://github.com/FunkinCrew/Funkin/pull/5245)
|
||||
- Freeplay styles are now reloaded when hot-reloading with F5. ([f54e140](https://github.com/FunkinCrew/Funkin/commit/f54e140b65e36fcf810c52ce464799dbc0c73c6d)) - by @Keoiki in [#5286](https://github.com/FunkinCrew/Funkin/pull/5286)
|
||||
- Adjusted offsets for Freeplay DJ Pico's fistPump animation. ([382e286](https://github.com/FunkinCrew/funkin.assets/commit/382e286a2478939d3d6aca1c7c90719f33815014)) - by @AbnormalPoof in [funkin.assets#91](https://github.com/FunkinCrew/funkin.assets/pull/91)
|
||||
- The "New Highscore" text no longer appears more than once in the Results screen. ([4e31003](https://github.com/FunkinCrew/Funkin/commit/4e31003a0f60cd394edc2bd4586f9f385b3d07bc)) - by @Lasercar in [#4319](https://github.com/FunkinCrew/Funkin/pull/4319)
|
||||
- The Results Debug menu now shows the correct rank after recent scoring changes. ([11d9998](https://github.com/FunkinCrew/Funkin/commit/11d9998e5c73a3839ea39e06b7f217cbaab69c6d)) - by @NotHyper-474 in [#4905](https://github.com/FunkinCrew/Funkin/pull/4905)
|
||||
- The Input Offsets menu now exits to the Options menu instead of the Main menu. ([5361df2](https://github.com/FunkinCrew/Funkin/commit/5361df254470e68d7571b4534cf456f08d5ffd60)) - by @JackXson-Real in [#5076](https://github.com/FunkinCrew/Funkin/pull/5076)
|
||||
- The Debug Menu can no longer be opened after selecting an item in the Main Menu.
|
||||
([5695bc2](https://github.com/FunkinCrew/Funkin/commit/5695bc20e721f0afd5b97a36167f13e550c12b16)) - by @Lasercar in [#4211](https://github.com/FunkinCrew/Funkin/pull/4211)
|
||||
- The debug cursor is now always hidden when the game starts. ([6222c38](https://github.com/FunkinCrew/Funkin/commit/6222c389e301fa2bb4939697376c4e51e29a9977)) - by @Hundrec in [#4520](https://github.com/FunkinCrew/Funkin/pull/4520)
|
||||
- The Story Mode Weekend 1 level title no longer clips into other level titles below it. ([19d1a8c](https://github.com/FunkinCrew/Funkin/commit/19d1a8c59380009f0d0814c94fd0b4eccb0c80cd)) - by @KoloInDaCrib in [#4348](https://github.com/FunkinCrew/Funkin/pull/4348)
|
||||
- Hold note covers now display properly if a hold note was previously dropped. ([96d1324](https://github.com/FunkinCrew/Funkin/commit/96d1324af140858cb93edd07dc36a969c8ae84c0)) - by @T5mpler in [#5275](https://github.com/FunkinCrew/Funkin/pull/5275)
|
||||
- Girlfriend's and Nene's combo drop animations now play consistently. ([34d5ed1](https://github.com/FunkinCrew/Funkin/commit/34d5ed11695cef7348c13505f13fb1da38b7988c)) - by @VioletSnowLeopard in [#4968](https://github.com/FunkinCrew/Funkin/pull/4968)
|
||||
- Girlfriend (Tankman Stickup) now plays her combo drop animation. ([e329601](https://github.com/FunkinCrew/funkin.assets/commit/e329601834f270910ce80ea5539e4487b9895a8a)) - by @qt2k4 in [funkin.assets#149](https://github.com/FunkinCrew/funkin.assets/pull/149)
|
||||
- Darnell's idle animation now loops consistently. ([df64586](https://github.com/FunkinCrew/funkin.assets/commit/df64586771ea41c544913e049fd0f32bdc655417)) - by @qt2k4 in [funkin.assets#159](https://github.com/FunkinCrew/funkin.assets/pull/159)
|
||||
- Darnell's kneeCan animation now plays properly in 2hot. ([a2e9931](https://github.com/FunkinCrew/funkin.assets/commit/a2e993167aaa2ff6bff8806940f912e444608645)) - by @biomseed in [funkin.assets#78](https://github.com/FunkinCrew/funkin.assets/pull/78)
|
||||
- Otis and Pico (Speaker) no longer spaz out when playtesting Stress. ([3f6d75f](https://github.com/FunkinCrew/funkin.assets/commit/3f6d75f3b6f6c8deb660323e3fb9bf1974c06520)) - by @Lasercar in [funkin.assets#124](https://github.com/FunkinCrew/funkin.assets/pull/124)
|
||||
- The gasp sound now only plays once in the Week 3 Pico Mix doppelganger cutscene. ([ab4598b](https://github.com/FunkinCrew/funkin.assets/commit/ab4598baf3c6790d30cfb727d04c8b57fa18dd0d)) - by @KoloInDaCrib in [funkin.assets#126](https://github.com/FunkinCrew/funkin.assets/pull/126)
|
||||
- Fixed a few issues with the train in Week 3. ([8db2426](https://github.com/FunkinCrew/funkin.assets/commit/8db2426991caebde44d19c81b198e8e2ad86f700)) - by @ShadzXD in [funkin.assets#180](https://github.com/FunkinCrew/funkin.assets/pull/180)
|
||||
- The cars in Week 4 and Weekend 1 no longer get stuck when the song is restarted. ([9c511e3](https://github.com/FunkinCrew/funkin.assets/commit/9c511e371fd08a43ddf834766a6d35667bdef4f7)) - by @MetaBreeze in [funkin.assets#186](https://github.com/FunkinCrew/funkin.assets/pull/186)
|
||||
- A-Bot's visualizer no longer jumps to a random volume when the song ends. ([51cc118](https://github.com/FunkinCrew/funkin.assets/commit/51cc1186bc77a2ee45a47fdbde2d75d9ec69de3a)) - by @VioletSnowLeopard in [funkin.assets#183](https://github.com/FunkinCrew/funkin.assets/pull/183)
|
||||
- Pico's burpShit animation now re-enables volume for player vocals. ([cefda0e](https://github.com/FunkinCrew/funkin.assets/commit/cefda0e52fabbbe04afe60b7aed560267e2cb01e)) - by @Hundrec in [funkin.assets#71](https://github.com/FunkinCrew/funkin.assets/pull/71)
|
||||
- Removed vocals from Monster's instrumental on web builds. ([1c9473f](https://github.com/FunkinCrew/funkin.assets/commit/1c9473f3dfdfb97d97f6c8457001055322abf5ab)) - by @JVNpixels in [funkin.assets#182](https://github.com/FunkinCrew/funkin.assets/pull/182)
|
||||
- Fixed the retry sound not playing after a Tankman death quote finishes. ([e7c4b1b](https://github.com/FunkinCrew/Funkin/commit/e7c4b1ba38ba0739cfe347f6c4763f9811fb95b0)) - by @VioletSnowLeopard in [#4726](https://github.com/FunkinCrew/Funkin/pull/4726)
|
||||
- Darnell (BF Mix)'s alternate instrumental is now properly accessible. ([5abdabf](https://github.com/FunkinCrew/funkin.assets/commit/5abdabf69b39ca4eebd36ae6bbd77fab736d0b86)) - by @Hundrec in [funkin.assets#168](https://github.com/FunkinCrew/funkin.assets/pull/168)
|
||||
- Fixed Newgrounds score submissions for Lit Up and Lit Up (BF Mix). ([183cec6](https://github.com/FunkinCrew/Funkin/commit/183cec62dc1fd3c7f3f634dd3e2400e6ee77b476)) - by @Raltyro in [#4577](https://github.com/FunkinCrew/Funkin/pull/4577)
|
||||
- Inputs are now disabled before Senpai's dialogue appears. ([c43d906](https://github.com/FunkinCrew/funkin.assets/commit/c43d906d19d91d71b9096e65d5e0d3543af8cd31)) - by @anysad in [funkin.assets#165](https://github.com/FunkinCrew/funkin.assets/pull/165)
|
||||
- An easter egg now restarts the song using the correct instrumental. ([e657bc9](https://github.com/FunkinCrew/Funkin/commit/e657bc900bc62cc220276dc171dd47f0a176ac66)) - by @KoloInDaCrib in [#4956](https://github.com/FunkinCrew/Funkin/pull/4956)
|
||||
- Encountering an easter egg during a Chart Editor playtest no longer crashes the game. ([b53b5bd](https://github.com/FunkinCrew/funkin.assets/commit/b53b5bdaecf975555538725a4cdfe71d38565b08)) - by @NotHyper-474 in [funkin.assets#133](https://github.com/FunkinCrew/funkin.assets/pull/133)
|
||||
- Nonexistent characters no longer crash the Chart Editor. ([3bbb4b0](https://github.com/FunkinCrew/Funkin/commit/3bbb4b06c8c1a1885a18d4354fcfa4363a0c6c75)) - by @Lasercar in [#5008](https://github.com/FunkinCrew/Funkin/pull/5008)
|
||||
- Holding Ctrl and clicking on a hold note trail no longer crashes the Chart Editor. ([dc56cca](https://github.com/FunkinCrew/Funkin/commit/dc56ccada50e671996caf0557de1977b7ff8d236)) - by @Lasercar in [#4203](https://github.com/FunkinCrew/Funkin/pull/4203)
|
||||
- Tweens and timers are now canceled when returning to the Chart Editor. ([7e76cf6](https://github.com/FunkinCrew/Funkin/commit/7e76cf66340c00ef6ec84358e6304d62815173b6)) - by @KoloInDaCrib in [#5278](https://github.com/FunkinCrew/Funkin/pull/5278)
|
||||
- Reduced the severity of a memory leak in the Chart Editor. ([cce8c18](https://github.com/FunkinCrew/Funkin/commit/cce8c18822e083910200597f5db4d87b6e3b521f)) - by @NotHyper-474 in [#5247](https://github.com/FunkinCrew/Funkin/pull/5247)
|
||||
- Pressing the Chart Editor keybind during a song now opens to the variation and difficulty you were playing. ([e3fca16](https://github.com/FunkinCrew/Funkin/commit/e3fca167938642bea85398fe57347c10439c1892)) - by @Lasercar in [#4116](https://github.com/FunkinCrew/Funkin/pull/4116)
|
||||
- The Chart Editor now properly saves audio levels when exiting. ([f78ab4d](https://github.com/FunkinCrew/Funkin/commit/f78ab4da1db4f9527a2e1715d5cfb37670e11a74)) - by @Lasercar in [#4149](https://github.com/FunkinCrew/Funkin/pull/4149)
|
||||
- The Chart Editor "Load Metadata File" and "Load Chart File" buttons now function properly. ([9df5395](https://github.com/FunkinCrew/Funkin/commit/9df5395ff888cb6740e21204c8e19116e0472db4)) - by @Lasercar in [#4278](https://github.com/FunkinCrew/Funkin/pull/4278)
|
||||
- FNF Legacy files can now be opened in the Chart Editor on MacOS. ([d98628c](https://github.com/FunkinCrew/Funkin/commit/d98628ca0f9f60357715bd7f95fc686a83201209)) - by @AbnormalPoof in [#4580](https://github.com/FunkinCrew/Funkin/pull/4580)
|
||||
- The Chart Editor now consistently displays the correct waveform for vocal tracks. ([c0e0523](https://github.com/FunkinCrew/Funkin/commit/c0e0523651e8aaaae2a0eed6d5fef6c5ef1b7315)) - by @NotHyper-474 in [#5231](https://github.com/FunkinCrew/Funkin/pull/5231)
|
||||
- Fixed selection boxes duplicating in the Chart Editor. ([65ed583](https://github.com/FunkinCrew/Funkin/commit/65ed58350b798bca0044603510540cfe81b48611)) - by @NotHyper-474 in [#5073](https://github.com/FunkinCrew/Funkin/pull/5073)
|
||||
- Hot-reloading with F5 in the Input Offsets menu no longer crashes the game. ([22372e8](https://github.com/FunkinCrew/Funkin/commit/22372e83886c5ef9821228daa3fb9fe8fbcf0e07)) - by @NotHyper-474 in [#5085](https://github.com/FunkinCrew/Funkin/pull/5085)
|
||||
- The Chart Editor playbar no longer extends past the right of the grid. ([7edc99b](https://github.com/FunkinCrew/Funkin/commit/7edc99b8ba7d1c61eba737560512a07babf5dfe8)) - by @anysad in [#5090](https://github.com/FunkinCrew/Funkin/pull/5090)
|
||||
- Removed a spammy console trace from some Chart Editor events. ([000491c](https://github.com/FunkinCrew/Funkin/commit/000491c3bd3db9de433a4b668b6ceebda2fc6eae)) - by @anysad in [#5097](https://github.com/FunkinCrew/Funkin/pull/5097)
|
||||
- Fixed a crash when mashing D or I during startup. ([790af6d](https://github.com/FunkinCrew/Funkin/commit/790af6d9054a77248ecb7bf34d764186c4fa2243)) - by @CrusherNotDrip in [#5160](https://github.com/FunkinCrew/Funkin/pull/5160)
|
||||
- The Chart Editor now consistently displays the correct waveform for vocal tracks. ([788d0fe](https://github.com/FunkinCrew/Funkin/commit/788d0fe9be5754e4e193de56e4437e8f409564a2)) - by @NotHyper-474 in [#5231](https://github.com/FunkinCrew/Funkin/pull/5231)
|
||||
- Stage Editor windows are now able to be closed. ([990042e](https://github.com/FunkinCrew/Funkin/commit/990042e28f73ad0624dfd38179891163d0c2313a)) - by @Lasercar in [#5238](https://github.com/FunkinCrew/Funkin/pull/5238)
|
||||
- Fixed duplicate exit prompts appearing in the Stage Editor. ([a599d83](https://github.com/FunkinCrew/Funkin/commit/a599d830f08ba697f2b526747280a53e16ebae06)) - by @Lasercar in [#5239](https://github.com/FunkinCrew/Funkin/pull/5239)
|
||||
- Reduced the severity of a memory leak in the Chart Editor. ([6ec3191](https://github.com/FunkinCrew/Funkin/commit/6ec3191b2952533afeded514d582a56f0905afb8)) - by @NotHyper-474 in [#5247](https://github.com/FunkinCrew/Funkin/pull/5247)
|
||||
- The Freeplay song preview no longer plays twice after returning from Character Select. ([f9387c1](https://github.com/FunkinCrew/Funkin/commit/f9387c144a62ecae2ea2b9da7b93ee16693bff02)) - by @Lasercar in [#5248](https://github.com/FunkinCrew/Funkin/pull/5248)
|
||||
- Undoing and redoing hold note length changes now visually updates the trail in the Chart Editor. ([254be57](https://github.com/FunkinCrew/Funkin/commit/254be5798ec0b7b2e79612ae5ade438965e65249)) - by @NotHyper-474 in [#5265](https://github.com/FunkinCrew/Funkin/pull/5265)
|
||||
- Pico's burpShit animation now re-enables volume for player vocals. ([2bf3846](https://github.com/FunkinCrew/funkin.assets/commit/2bf38463c524839e85faec19b7f31e90070e0b87)) - by @Hundrec in [funkin.assets#71](https://github.com/FunkinCrew/funkin.assets/pull/71)
|
||||
- Darnell's kneeCan animation now plays properly in 2hot. ([f8030c4](https://github.com/FunkinCrew/funkin.assets/commit/f8030c441c73d203f7b92c243542f99402faf2a7)) - by @biomseed in [funkin.assets#78](https://github.com/FunkinCrew/funkin.assets/pull/78)
|
||||
- Adjusted offsets for Freeplay DJ Pico's fistPump animation. ([17996db](https://github.com/FunkinCrew/funkin.assets/commit/17996db752c3077e005d4f05286a7795746acb05)) - by @AbnormalPoof in [funkin.assets#91](https://github.com/FunkinCrew/funkin.assets/pull/91)
|
||||
- Chart Editor and Stage Editor windows now consistently show a close button. ([929f61f](https://github.com/FunkinCrew/funkin.assets/commit/929f61fe86bd49d61de27e09503085a04c2cddfb)) - by @Lasercar in [funkin.assets#121](https://github.com/FunkinCrew/funkin.assets/pull/121)
|
||||
- Otis and Pico (Speaker) no longer spaz out when playtesting Stress. ([2fd62d8](https://github.com/FunkinCrew/funkin.assets/commit/2fd62d8f4fdefffc8d4a9cc96b9e993fd0130eb4)) - by @Lasercar in [funkin.assets#124](https://github.com/FunkinCrew/funkin.assets/pull/124)
|
||||
- The gasp sound now only plays once in the Week 3 Pico Mix doppelganger cutscene. ([ecda30d](https://github.com/FunkinCrew/funkin.assets/commit/ecda30d7532df3c34a4b5ad25579cc960cb30c0f)) - by @KoloInDaCrib in [funkin.assets#126](https://github.com/FunkinCrew/funkin.assets/pull/126)
|
||||
- Encountering an easter egg during a Chart Editor playtest no longer crashes the game. ([6cf9fb7](https://github.com/FunkinCrew/funkin.assets/commit/6cf9fb7ba48bb336f63eb940290fa051859330ec)) - by @NotHyper-474 in [funkin.assets#133](https://github.com/FunkinCrew/funkin.assets/pull/133)
|
||||
- Girlfriend (Tankman Stickup) now plays her combo drop animation. ([cb0e506](https://github.com/FunkinCrew/funkin.assets/commit/cb0e506599d387b1d17faee2cf319914eb2dfc80)) - by @qt2k4 in [funkin.assets#149](https://github.com/FunkinCrew/funkin.assets/pull/149)
|
||||
- Changed "Tankman Battlefield (Erect)" to "Tankman Battlefield [Erect]" in the Chart Editor for consistency. ([f9149eb](https://github.com/FunkinCrew/funkin.assets/commit/f9149eb72698ddd275f743080effed08c842644e)) - by @JVN-Pixels in [funkin.assets#155](https://github.com/FunkinCrew/funkin.assets/pull/155)
|
||||
- Fixed Pico (Pixel) having the incorrect name in the Chart Editor. ([a668659](https://github.com/FunkinCrew/funkin.assets/commit/a668659a4bbd2094fde48041c73fd1ebef551288)) - by @ExtraCode75 in [funkin.assets#158](https://github.com/FunkinCrew/funkin.assets/pull/158)
|
||||
- Darnell (BF Mix)'s alternate instrumental is now properly accessible. ([7bf4361](https://github.com/FunkinCrew/funkin.assets/commit/7bf436153081c27fdb354eddb05b72bd87fb18b7)) - by @Hundrec in [funkin.assets#168](https://github.com/FunkinCrew/funkin.assets/pull/168)
|
||||
- Adjusted the font size for Starting BPM in the Chart Editor for consistency. ([801121a](https://github.com/FunkinCrew/funkin.assets/commit/801121a7d31ef3a1ac108bf16941d88009d1b12a)) - by @Lasercar in [funkin.assets#170](https://github.com/FunkinCrew/funkin.assets/pull/170)
|
||||
- Removed vocals from Monster's instrumental on web builds. ([bf7b032](https://github.com/FunkinCrew/funkin.assets/commit/bf7b032154fdc9751fbe8fdf88dbc5fd24f361db)) - by @JVN-Pixels in [funkin.assets#182](https://github.com/FunkinCrew/funkin.assets/pull/182)
|
||||
- A-Bot's visualizer no longer jumps to a random volume when the song ends. ([2904697](https://github.com/FunkinCrew/funkin.assets/commit/2904697b6fda66db4df2b47127f05520fdf831d1)) - by @VioletSnowLeopard in [funkin.assets#183](https://github.com/FunkinCrew/funkin.assets/pull/183)
|
||||
- The cars in Week 4 and Weekend 1 no longer get stuck when the song is restarted. ([ecb1bbd](https://github.com/FunkinCrew/funkin.assets/commit/ecb1bbd8c8ced028b96b2c9b5572d2ea48f36cf4)) - by @MetaBreeze in [funkin.assets#186](https://github.com/FunkinCrew/funkin.assets/pull/186)
|
||||
- Removed a spammy console trace from Spooky Kids (Dark). ([9ee98d6](https://github.com/FunkinCrew/funkin.assets/commit/9ee98d62aa336f1fccbefaf077b4ae3222e00ec4)) - by @NotHyper-474 in [funkin.assets#187](https://github.com/FunkinCrew/funkin.assets/pull/187)
|
||||
- Fixed the Chart Editor timer occasionally displaying incorrect millisecond values. ([26dc895](https://github.com/FunkinCrew/Funkin/commit/26dc895a27e0d7e49469251cd68b83be66384e15)) - by @Hundrec in [#4257](https://github.com/FunkinCrew/Funkin/pull/4257)
|
||||
- The Chart Editor playhead can no longer be scrolled to before the beginning of the song. ([7c7dc11](https://github.com/FunkinCrew/Funkin/commit/7c7dc11f18644882444df97ac927e11adaa4ce50)) - by @Hundrec in [#5024](https://github.com/FunkinCrew/Funkin/pull/5024)
|
||||
- The Chart Editor playbar no longer extends past the right of the grid. ([c7abb19](https://github.com/FunkinCrew/Funkin/commit/c7abb196989476cfa4db6354cc6fac5bacb2e56a)) - by @anysad in [#5090](https://github.com/FunkinCrew/Funkin/pull/5090)
|
||||
- Dragging a hold note in the Chart Editor now drags its trail along with its head. ([d3d8aaa](https://github.com/FunkinCrew/Funkin/commit/d3d8aaae7bfbfa8975a9573807b9a3ca68a1ff55)) - by @KoloInDaCrib in [#4127](https://github.com/FunkinCrew/Funkin/pull/4127)
|
||||
- Hold note trails will no longer disappear when dragged too far in the Chart Editor. ([37dc66b](https://github.com/FunkinCrew/Funkin/commit/37dc66bc189bc1941cf60587e1c068770aeec872)) - by @NotHyper-474 in
|
||||
[#5261](https://github.com/FunkinCrew/Funkin/pull/5261)
|
||||
- Undoing and redoing hold note length changes now visually updates the trail in the Chart Editor. ([06a440f](https://github.com/FunkinCrew/Funkin/commit/06a440f21c285666ce2d2bdf17a91ee82ab01061)) - by @NotHyper-474 in [#5265](https://github.com/FunkinCrew/Funkin/pull/5265)
|
||||
- The Chart Editor hold note context menu now displays the correct options. ([4801316](https://github.com/FunkinCrew/Funkin/commit/48013168ef09ddc09549268a2e5309520d3fcc18)) - by @Lasercar in [#4231](https://github.com/FunkinCrew/Funkin/pull/4231)
|
||||
- The buttons in the Chart Editor context menu for selections now do the right thing. ([62d24fc](https://github.com/FunkinCrew/Funkin/commit/62d24fcf4cbe7be328a995bad04f3eeb265262c5)) - by @Lasercar in [#4233](https://github.com/FunkinCrew/Funkin/pull/4233)
|
||||
- The charter field in the song metadata now properly displays the charter in the Chart Editor. ([894d8cb](https://github.com/FunkinCrew/Funkin/commit/894d8cb4637fd0a64359a5edc805a23403f3045c)) - by @Lasercar in [#4879](https://github.com/FunkinCrew/Funkin/pull/4879)
|
||||
- Chart Editor difficulties are now sorted in a consistent order. ([7aa77a1](https://github.com/FunkinCrew/Funkin/commit/7aa77a11cf7508fc05758045a13b2220aca96dd5)) - by @Lasercar in [#4528](https://github.com/FunkinCrew/Funkin/pull/4528)
|
||||
- Chart Editor and Stage Editor windows now consistently show a close button. ([b23b7b8](https://github.com/FunkinCrew/funkin.assets/commit/b23b7b81d843c8fa2334fbdbe1ef979633956bb9)) - by @Lasercar in [funkin.assets#121](https://github.com/FunkinCrew/funkin.assets/pull/121)
|
||||
- The Chart Editor playbar's font size no longer becomes too small. ([f9c1f7a](https://github.com/FunkinCrew/Funkin/commit/f9c1f7a5f7d3906155c2004ad2ec1d02556b9730)) - by @KoloInDaCrib in [#5253](https://github.com/FunkinCrew/Funkin/pull/5253)
|
||||
- The Chart Editor copy notification no longer chases the mouse cursor. ([0ea42e1](https://github.com/FunkinCrew/Funkin/commit/0ea42e18e93b0b57f1ae5499d70c4681f191dacb)) - by @KoloInDaCrib in [#4029](https://github.com/FunkinCrew/Funkin/pull/4029)
|
||||
- Changed "Tankman Battlefield (Erect)" to "Tankman Battlefield [Erect]" in the Chart Editor for consistency. ([52852a0](https://github.com/FunkinCrew/funkin.assets/commit/52852a02f60df81c281bfd0e49fd8fa09e118409)) - by @JVNpixels in [funkin.assets#155](https://github.com/FunkinCrew/funkin.assets/pull/155)
|
||||
- Fixed Pico (Pixel) having the incorrect name in the Chart Editor. ([3ff0e9c](https://github.com/FunkinCrew/funkin.assets/commit/3ff0e9ca4f8cdc5734c335f2d7d72fa310686d46)) - by @ExtraCode75 in [funkin.assets#158](https://github.com/FunkinCrew/funkin.assets/pull/158)
|
||||
- Fixed various issues and added missing functionalities to the Stage Editor. ([a776ce1](https://github.com/FunkinCrew/Funkin/commit/a776ce1a81a539f75f8bd2220a9768c03d347058)) - by @KoloInDaCrib in [#3974](https://github.com/FunkinCrew/Funkin/pull/3974)
|
||||
- Stage Editor windows are now able to be closed. ([65461d8](https://github.com/FunkinCrew/Funkin/commit/65461d839b3764659aab33f1a35edf64ee514952)) - by @Lasercar in [#5238](https://github.com/FunkinCrew/Funkin/pull/5238)
|
||||
- Fixed duplicate exit prompts appearing in the Stage Editor. ([136a5df](https://github.com/FunkinCrew/Funkin/commit/136a5dfad430b461a909be3a7e89f46c8be1d3b3)) - by @Lasercar in [#5239](https://github.com/FunkinCrew/Funkin/pull/5239)
|
||||
- The help guide in the Stage Editor can no longer be opened multiple times. ([564d679](https://github.com/FunkinCrew/Funkin/commit/564d679f969c500834426ce3cf50507091d69697)) - by @Lasercar in [#4128](https://github.com/FunkinCrew/Funkin/pull/4128)
|
||||
- Removed a spammy console trace from Spooky Kids (Dark). ([5935a61](https://github.com/FunkinCrew/funkin.assets/commit/5935a61dd71f51e6264dcd039f1164cd1ff295ef)) - by @NotHyper-474 in [funkin.assets#187](https://github.com/FunkinCrew/funkin.assets/pull/187)
|
||||
- Removed spammy console traces from `DiscordClient`. ([e89f9f5](https://github.com/FunkinCrew/Funkin/commit/e89f9f50dc6085e3550736459ce9e4fd02c1fc5b)) - by @AbnormalPoof in [#4207](https://github.com/FunkinCrew/Funkin/pull/4207)
|
||||
- Removed a spammy console trace from some Chart Editor events. ([b883ad3](https://github.com/FunkinCrew/Funkin/commit/b883ad3d50b990e605d7b79d042292c7371db5f0)) - by @anysad in [#5097](https://github.com/FunkinCrew/Funkin/pull/5097)
|
||||
- ANSI colors now display in the console on more computers. ([3747b94](https://github.com/FunkinCrew/Funkin/commit/3747b942461c3644fe31f25d2ce847fd74d1b1e0)) - by @AbnormalPoof in [#4676](https://github.com/FunkinCrew/Funkin/pull/4676)
|
||||
- Properly blacklist a certain class from scripts. ([3dc7699](https://github.com/FunkinCrew/Funkin/commit/3dc7699aac737998b637ff9a9f16986a434424be)) - by @charlesisfeline in [#4773](https://github.com/FunkinCrew/Funkin/pull/4773)
|
||||
|
||||
### Removed
|
||||
|
||||
- [MOBILE] The Input Offsets menu has been temporarily removed pending future reworks.
|
||||
- Removed a few non-functional screenshot preferences. ([8e40942](https://github.com/FunkinCrew/Funkin/commit/8e4094227f89ccc8ec7e58be49a5e1ad6911ed12)) - by @Lasercar in [#4895](https://github.com/FunkinCrew/Funkin/pull/4895)
|
||||
- Removed the VSync preference from web builds, where it's non-functional. ([1338834](https://github.com/FunkinCrew/Funkin/commit/1338834519e13326fa21585a236218cd984ed614)) - by @NotHyper-474 in [#5062](https://github.com/FunkinCrew/Funkin/pull/5062)
|
||||
- Removed the VSync preference from web builds, where it's non-functional. ([0b7a94b](https://github.com/FunkinCrew/Funkin/commit/0b7a94b1cc5c5d0dbac5a4c3d595349c4e6eb6e4)) - by @NotHyper-474 in [#5062](https://github.com/FunkinCrew/Funkin/pull/5062)
|
||||
- Removed a few non-functional screenshot preferences. ([93e4f79](https://github.com/FunkinCrew/Funkin/commit/93e4f799f4f0ea435b3d77ca8ef2ab6beeb0a955)) - by @Lasercar in [#4895](https://github.com/FunkinCrew/Funkin/pull/4895)
|
||||
|
||||
## New Contributors for 0.7.0
|
||||
|
||||
* @KutikiPlayz made their first contribution in [#3544](https://github.com/FunkinCrew/Funkin/pull/3544)
|
||||
* @xenkap made their first contribution in [#3732](https://github.com/FunkinCrew/Funkin/pull/3732)
|
||||
* @Raltyro made their first contribution in [#4577](https://github.com/FunkinCrew/Funkin/pull/4577)
|
||||
* @charlesisfeline made their first contribution in [#4773](https://github.com/FunkinCrew/Funkin/pull/4773)
|
||||
* @T5mpler made their first contribution in [#5275](https://github.com/FunkinCrew/Funkin/pull/5275)
|
||||
* @biomseed made their first contribution in [funkin.assets#78](https://github.com/FunkinCrew/funkin.assets/pull/78)
|
||||
* @qt2k4 made their first contribution in [funkin.assets#149](https://github.com/FunkinCrew/funkin.assets/pull/149)
|
||||
* @ExtraCode75 made their first contribution in [funkin.assets#158](https://github.com/FunkinCrew/funkin.assets/pull/158)
|
||||
|
||||
+1
-1
Submodule assets updated: 414e9a0248...69c02fa501
@@ -18,7 +18,7 @@
|
||||
"name": "extension-admob",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "b0fe062c5eacd7cde80fd21544fe27616e5b4f9e",
|
||||
"ref": "a53d5916bdcb2e48913f94d9ae1d949b049dcdc1",
|
||||
"url": "https://github.com/FunkinCrew/extension-admob"
|
||||
},
|
||||
{
|
||||
@@ -45,7 +45,7 @@
|
||||
"name": "flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "16962f601431d72ed104ca700d1e99ad4dcfffca",
|
||||
"ref": "08fc955ca87f192a971719a675f1d3b21709725d",
|
||||
"url": "https://github.com/FunkinCrew/flixel-mobile"
|
||||
},
|
||||
{
|
||||
@@ -104,7 +104,7 @@
|
||||
"name": "hscript",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "27c86f9a761c1d16d4433c4cf252eccb7b2e18de",
|
||||
"ref": "d60bb2947fa609fdc875ccfae89666a6984eeaf2",
|
||||
"url": "https://github.com/FunkinCrew/hscript"
|
||||
},
|
||||
{
|
||||
@@ -170,7 +170,7 @@
|
||||
"name": "lime",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "2d3784fe3fdbd81ce59d6ad035f52e12c59cf059",
|
||||
"ref": "c750ebf6b48c4bc018abe9855fbae5ffdbc4771a",
|
||||
"url": "https://github.com/FunkinCrew/lime-mobile"
|
||||
},
|
||||
{
|
||||
@@ -212,14 +212,14 @@
|
||||
"name": "openfl",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "db060809f0ac293fb873964d7a076762c85adb71",
|
||||
"ref": "a0df7c3afe360c9af59a76e45007dbf4e53b5131",
|
||||
"url": "https://github.com/FunkinCrew/openfl-mobile"
|
||||
},
|
||||
{
|
||||
"name": "polymod",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "6a3f3793c29ec2922630854ba5186f6f1d73d25a",
|
||||
"ref": "866f19edbcd872b3358f9a41f2f6a24c71c191d1",
|
||||
"url": "https://github.com/larsiusprime/polymod"
|
||||
},
|
||||
{
|
||||
|
||||
+76
-28
@@ -29,7 +29,7 @@ class Project extends HXProject
|
||||
* REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES!
|
||||
* You only have to change it here, the rest of the game will query this value.
|
||||
*/
|
||||
static final VERSION:String = "0.7.0";
|
||||
static final VERSION:String = "0.7.3";
|
||||
|
||||
/**
|
||||
* The build's number and version code.
|
||||
@@ -101,6 +101,8 @@ class Project extends HXProject
|
||||
*/
|
||||
static final ANDROID_TARGET_SDK_VERSION:Int = 35;
|
||||
|
||||
static final ANDROID_EXTENSIONS:Array<String> = ["funkin.extensions.CallbackUtil"];
|
||||
|
||||
//
|
||||
// ASTC COMPRESSION
|
||||
//
|
||||
@@ -488,6 +490,7 @@ class Project extends HXProject
|
||||
configureApp();
|
||||
|
||||
displayTarget();
|
||||
|
||||
configureFeatureFlags();
|
||||
configureCompileDefines();
|
||||
configureIncludeMacros();
|
||||
@@ -510,23 +513,14 @@ class Project extends HXProject
|
||||
configureAdMobKeys();
|
||||
}
|
||||
|
||||
// To sign in ios, use -xcode.
|
||||
if (isAndroid())
|
||||
{
|
||||
if (isRelease())
|
||||
{
|
||||
configureAndroidRelease();
|
||||
}
|
||||
configureAndroid();
|
||||
}
|
||||
|
||||
if (isIOS())
|
||||
{
|
||||
config.set("ios.non-exempt-encryption", "false");
|
||||
config.set("ios.team-id", IOS_TEAM_ID);
|
||||
config.set("ios.deployment", "16.0");
|
||||
|
||||
// launchStoryboard = new LaunchStoryboard();
|
||||
// launchStoryboard.path = "./LaunchScreen.storyboard";
|
||||
configureIOS();
|
||||
}
|
||||
|
||||
info("Done configuring project.");
|
||||
@@ -555,6 +549,11 @@ class Project extends HXProject
|
||||
function configureApp()
|
||||
{
|
||||
this.meta.title = TITLE;
|
||||
|
||||
// ios uses this character code instead of spaces...
|
||||
if (isIOS())
|
||||
this.meta.title = ~/ /ig.replace(TITLE, "\u2002");
|
||||
|
||||
this.meta.version = VERSION;
|
||||
this.meta.packageName = PACKAGE_NAME;
|
||||
this.meta.company = COMPANY;
|
||||
@@ -889,6 +888,11 @@ class Project extends HXProject
|
||||
setHaxedef("FLX_NO_DEBUG");
|
||||
}
|
||||
|
||||
if (FEATURE_LOG_TRACE.isDisabled(this))
|
||||
{
|
||||
addHaxeFlag("--no-traces");
|
||||
}
|
||||
|
||||
// Disable the built in pause screen when unfocusing the game.
|
||||
setHaxedef("FLX_NO_FOCUS_LOST_SCREEN");
|
||||
|
||||
@@ -977,40 +981,84 @@ class Project extends HXProject
|
||||
app.path = buildDir;
|
||||
}
|
||||
|
||||
function configureAndroidRelease()
|
||||
function configureAndroid()
|
||||
{
|
||||
if (envConfig != null)
|
||||
javaPaths.push(Path.join([SOURCE_DIR, 'funkin/mobile/external/android/java']));
|
||||
|
||||
if (isRelease())
|
||||
{
|
||||
keystore = new Keystore();
|
||||
keystore.path = Std.string(envConfig.get('ANDROID_KEYSTORE_PATH'));
|
||||
keystore.alias = Std.string(envConfig.get('ANDROID_KEYSTORE_ALIAS'));
|
||||
keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD'));
|
||||
if (envConfig != null)
|
||||
{
|
||||
keystore = new Keystore();
|
||||
keystore.path = Std.string(envConfig.get('ANDROID_KEYSTORE_PATH'));
|
||||
keystore.alias = Std.string(envConfig.get('ANDROID_KEYSTORE_ALIAS'));
|
||||
keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD'));
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
config.set("android.gradle-project-directory", "bin-bundle");
|
||||
}
|
||||
}
|
||||
|
||||
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 modsFolderProvider = [
|
||||
'<provider android:authorities="$PACKAGE_NAME.docprovider" android:name="funkin.provider.DataFolderProvider" android:grantUriPermissions="true" android:exported="true" android:permission="android.permission.MANAGE_DOCUMENTS">',
|
||||
' <intent-filter>',
|
||||
' <action android:name="android.content.action.DOCUMENTS_PROVIDER" />',
|
||||
' </intent-filter>',
|
||||
'</provider>'
|
||||
];
|
||||
|
||||
final xmlChildren:Null<Array<String>> = config.get('android.manifest').xmlChildren;
|
||||
final xmlChildren:Null<Array<String>> = config.get('android.application').xmlChildren;
|
||||
|
||||
if (xmlChildren != null)
|
||||
{
|
||||
config.get('android.manifest').xmlChildren = xmlChildren.concat(permissionsConfig);
|
||||
config.get('android.application').xmlChildren = xmlChildren.concat(modsFolderProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.get('android.manifest').xmlChildren = permissionsConfig;
|
||||
config.get('android.application').xmlChildren = modsFolderProvider;
|
||||
}
|
||||
|
||||
if (isBundle())
|
||||
final extensions:Null<Array<String>> = config.getArrayString('android.extension');
|
||||
|
||||
for (extension in ANDROID_EXTENSIONS)
|
||||
{
|
||||
config.set("android.gradle-project-directory", "bin-bundle");
|
||||
if (extensions == null || extensions != null && extensions.indexOf(extension) == -1)
|
||||
{
|
||||
config.push("android.extension", extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function configureIOS()
|
||||
{
|
||||
config.set("ios.non-exempt-encryption", "false");
|
||||
config.set("ios.team-id", IOS_TEAM_ID);
|
||||
config.set("ios.deployment", "16.0");
|
||||
|
||||
// launchStoryboard = new LaunchStoryboard();
|
||||
// launchStoryboard.path = "./LaunchScreen.storyboard";
|
||||
}
|
||||
|
||||
function configurePolymod()
|
||||
{
|
||||
// The file extension to use for script files.
|
||||
|
||||
+4
-5
@@ -38,12 +38,11 @@ class Main extends Sprite
|
||||
{
|
||||
// Set the current working directory for Android and iOS devices
|
||||
#if android
|
||||
// For Android we determine the appropriate directory based on Android version
|
||||
Sys.setCwd(haxe.io.Path.addTrailingSlash(extension.androidtools.os.Build.VERSION.SDK_INT > 30 ? extension.androidtools.content.Context.getObbDir() : // Use Obb directory for Android SDK version > 30
|
||||
extension.androidtools.content.Context.getExternalFilesDir() // Use External Files directory for Android SDK version < 30
|
||||
));
|
||||
// On Android use External Files Dir.
|
||||
Sys.setCwd(haxe.io.Path.addTrailingSlash(extension.androidtools.content.Context.getExternalFilesDir()));
|
||||
#elseif ios
|
||||
Sys.setCwd(haxe.io.Path.addTrailingSlash(lime.system.System.documentsDirectory)); // For iOS we use documents directory and this is only way we can do.
|
||||
// On iOS use Documents Dir.
|
||||
Sys.setCwd(haxe.io.Path.addTrailingSlash(lime.system.System.documentsDirectory));
|
||||
#end
|
||||
|
||||
// We need to make the crash handler LITERALLY FIRST so nothing EVER gets past it.
|
||||
|
||||
@@ -350,7 +350,7 @@ class FunkinMemory
|
||||
if (sound != null)
|
||||
{
|
||||
Assets.cache.removeSound(key);
|
||||
previousCachedTextures.remove(key);
|
||||
previousCachedSounds.remove(key);
|
||||
}
|
||||
}
|
||||
Assets.cache.clear("songs");
|
||||
|
||||
@@ -114,7 +114,7 @@ class InitState extends FlxState
|
||||
|
||||
#if ios
|
||||
// Setup Audio session
|
||||
funkin.mobile.external.AudioSession.initialize();
|
||||
funkin.mobile.external.ios.AudioSession.initialize();
|
||||
#end
|
||||
|
||||
// This ain't a pixel art game! (most of the time)
|
||||
@@ -202,6 +202,7 @@ class InitState extends FlxState
|
||||
//
|
||||
#if android
|
||||
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
|
||||
funkin.mobile.external.android.CallbackUtil.init();
|
||||
#end
|
||||
|
||||
//
|
||||
|
||||
@@ -2,6 +2,7 @@ package funkin;
|
||||
|
||||
#if mobile
|
||||
import funkin.mobile.ui.FunkinHitbox;
|
||||
import funkin.mobile.util.InAppPurchasesUtil;
|
||||
#end
|
||||
import funkin.save.Save;
|
||||
import funkin.util.WindowUtil;
|
||||
@@ -202,6 +203,25 @@ class Preferences
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplier of intensity for all the haptic feedback effects.
|
||||
* @default `2.5`
|
||||
*/
|
||||
public static var hapticsIntensityMultiplier(get, set):Float;
|
||||
|
||||
static function get_hapticsIntensityMultiplier():Float
|
||||
{
|
||||
return Save?.instance?.options?.hapticsIntensityMultiplier ?? 1;
|
||||
}
|
||||
|
||||
static function set_hapticsIntensityMultiplier(value:Float):Float
|
||||
{
|
||||
var save:Save = Save.instance;
|
||||
save.options.hapticsIntensityMultiplier = value;
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled, the game will automatically pause when tabbing out.
|
||||
* Always enabled on mobile.
|
||||
@@ -527,5 +547,31 @@ class Preferences
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
|
||||
#if FEATURE_MOBILE_IAP
|
||||
/**
|
||||
* If bought, the game will not show any ads.
|
||||
* @default `false`
|
||||
*/
|
||||
@:unreflective
|
||||
public static var noAds(get, set):Bool;
|
||||
|
||||
@:unreflective
|
||||
static function get_noAds():Bool
|
||||
{
|
||||
if (InAppPurchasesUtil.hasInitialized) noAds = InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID);
|
||||
var returnedValue = Save?.instance?.mobileOptions?.noAds ?? false;
|
||||
return returnedValue;
|
||||
}
|
||||
|
||||
@:unreflective
|
||||
static function set_noAds(value:Bool):Bool
|
||||
{
|
||||
var save:Save = Save.instance;
|
||||
save.mobileOptions.noAds = value;
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -243,9 +243,10 @@ class WaveformDataChannel
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a given minimum point at an index.
|
||||
* @param i Index
|
||||
* @return minimum point at an index.
|
||||
*/
|
||||
public function minSample(i:Int)
|
||||
public function minSample(i:Int):Int
|
||||
{
|
||||
var offset = (i * parent.channels + this.channelId) * 2;
|
||||
return inline parent.get(offset);
|
||||
|
||||
@@ -43,69 +43,58 @@ class WaveformDataParser
|
||||
|
||||
public static function interpretAudioBuffer(soundBuffer:lime.media.AudioBuffer):Null<WaveformData>
|
||||
{
|
||||
var sampleRate = soundBuffer.sampleRate;
|
||||
var channels = soundBuffer.channels;
|
||||
var bitsPerSample = soundBuffer.bitsPerSample;
|
||||
var samplesPerPoint:Int = 256; // I don't think we need to configure this.
|
||||
var pointsPerSecond:Float = sampleRate / samplesPerPoint; // 172 samples per second for most songs is plenty precise while still being performant..
|
||||
|
||||
// TODO: Make this work better on HTML5.
|
||||
var soundData:lime.utils.Int16Array = cast soundBuffer.data;
|
||||
|
||||
var soundDataRawLength:Int = soundData.length;
|
||||
var soundDataSampleCount:Int = Std.int(Math.ceil(soundDataRawLength / channels / (bitsPerSample == 16 ? 2 : 1)));
|
||||
var soundDataSampleCount:Int = Std.int(Math.ceil(soundData.length / channels / (bitsPerSample == 16 ? 2 : 1)));
|
||||
var outputPointCount:Int = Std.int(Math.ceil(soundDataSampleCount / samplesPerPoint));
|
||||
|
||||
// trace('Interpreting audio buffer:');
|
||||
// trace(' sampleRate: ${sampleRate}');
|
||||
// trace(' channels: ${channels}');
|
||||
// trace(' bitsPerSample: ${bitsPerSample}');
|
||||
// trace(' samplesPerPoint: ${samplesPerPoint}');
|
||||
// trace(' pointsPerSecond: ${pointsPerSecond}');
|
||||
// trace(' soundDataRawLength: ${soundDataRawLength}');
|
||||
// trace(' soundDataSampleCount: ${soundDataSampleCount}');
|
||||
// trace(' soundDataRawLength/4: ${soundDataRawLength / 4}');
|
||||
// trace(' outputPointCount: ${outputPointCount}');
|
||||
// Pre-allocate Vector with exact final size for better performance and memory efficiency
|
||||
var outputDataLength:Int = outputPointCount * channels * 2;
|
||||
var outputData = new haxe.ds.Vector<Int>(outputDataLength);
|
||||
|
||||
var minSampleValue:Int = bitsPerSample == 16 ? INT16_MIN : INT8_MIN;
|
||||
var maxSampleValue:Int = bitsPerSample == 16 ? INT16_MAX : INT8_MAX;
|
||||
|
||||
var outputData:Array<Int> = [];
|
||||
// Reusable min/max tracking arrays to avoid allocation in the loop
|
||||
var minValues = new haxe.ds.Vector<Int>(channels);
|
||||
var maxValues = new haxe.ds.Vector<Int>(channels);
|
||||
|
||||
for (pointIndex in 0...outputPointCount)
|
||||
{
|
||||
// minChannel1, maxChannel1, minChannel2, maxChannel2, ...
|
||||
var values:Array<Int> = [];
|
||||
var rangeStart:Int = pointIndex * samplesPerPoint;
|
||||
var rangeEnd:Int = Std.int(Math.min(rangeStart + samplesPerPoint, soundDataSampleCount));
|
||||
|
||||
// Reset min/max values for this range
|
||||
for (i in 0...channels)
|
||||
{
|
||||
values.push(bitsPerSample == 16 ? INT16_MAX : INT8_MAX);
|
||||
values.push(bitsPerSample == 16 ? INT16_MIN : INT8_MIN);
|
||||
minValues[i] = bitsPerSample == 16 ? INT16_MAX : INT8_MAX;
|
||||
maxValues[i] = bitsPerSample == 16 ? INT16_MIN : INT8_MIN;
|
||||
}
|
||||
|
||||
var rangeStart = pointIndex * samplesPerPoint;
|
||||
var rangeEnd = rangeStart + samplesPerPoint;
|
||||
if (rangeEnd > soundDataSampleCount) rangeEnd = soundDataSampleCount;
|
||||
|
||||
// Process all samples in this range
|
||||
for (sampleIndex in rangeStart...rangeEnd)
|
||||
{
|
||||
for (channelIndex in 0...channels)
|
||||
{
|
||||
var sampleIndex:Int = sampleIndex * channels + channelIndex;
|
||||
var sampleValue = soundData[sampleIndex];
|
||||
var sampleValue:Int = soundData[sampleIndex * channels + channelIndex];
|
||||
|
||||
if (sampleValue < values[channelIndex * 2]) values[(channelIndex * 2)] = sampleValue;
|
||||
if (sampleValue > values[channelIndex * 2 + 1]) values[(channelIndex * 2) + 1] = sampleValue;
|
||||
if (sampleValue < minValues[channelIndex]) minValues[channelIndex] = sampleValue;
|
||||
if (sampleValue > maxValues[channelIndex]) maxValues[channelIndex] = sampleValue;
|
||||
}
|
||||
}
|
||||
|
||||
// We now have the min and max values for the range.
|
||||
for (value in values)
|
||||
outputData.push(value);
|
||||
// Write directly to final positions in output Vector
|
||||
var baseIndex:Int = pointIndex * channels * 2;
|
||||
for (channelIndex in 0...channels)
|
||||
{
|
||||
outputData[baseIndex + channelIndex * 2] = minValues[channelIndex];
|
||||
outputData[baseIndex + channelIndex * 2 + 1] = maxValues[channelIndex];
|
||||
}
|
||||
}
|
||||
|
||||
var outputDataLength:Int = Std.int(outputData.length / channels / 2);
|
||||
var result = new WaveformData(null, channels, sampleRate, samplesPerPoint, bitsPerSample, outputPointCount, outputData);
|
||||
var result = new WaveformData(null, channels, soundBuffer.sampleRate, samplesPerPoint, bitsPerSample, outputPointCount, outputData.toArray());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class FNFLegacyImporter
|
||||
{
|
||||
trace('Migrating song metadata from FNF Legacy.');
|
||||
|
||||
var songMetadata:SongMetadata = new SongMetadata('Import', Constants.DEFAULT_ARTIST, 'default');
|
||||
var songMetadata:SongMetadata = new SongMetadata('Import', Constants.DEFAULT_ARTIST, Constants.DEFAULT_CHARTER, Constants.DEFAULT_VARIATION);
|
||||
|
||||
// Set generatedBy string for debugging.
|
||||
songMetadata.generatedBy = 'Chart Editor Import (FNF Legacy)';
|
||||
|
||||
@@ -24,7 +24,7 @@ class SongDataMigrator
|
||||
|
||||
public static function migrate_SongMetadata_v2_1_0(input:SongData_v2_1_0.SongMetadata_v2_1_0):SongMetadata
|
||||
{
|
||||
var result:SongMetadata = new SongMetadata(input.songName, input.artist, input.variation);
|
||||
var result:SongMetadata = new SongMetadata(input.songName, input.artist, Constants.DEFAULT_CHARTER, input.variation);
|
||||
result.version = SongRegistry.SONG_METADATA_VERSION;
|
||||
result.timeFormat = input.timeFormat;
|
||||
result.divisions = input.divisions;
|
||||
@@ -66,7 +66,7 @@ class SongDataMigrator
|
||||
|
||||
public static function migrate_SongMetadata_v2_0_0(input:SongData_v2_0_0.SongMetadata_v2_0_0):SongMetadata
|
||||
{
|
||||
var result:SongMetadata = new SongMetadata(input.songName, input.artist, input.variation);
|
||||
var result:SongMetadata = new SongMetadata(input.songName, input.artist, Constants.DEFAULT_CHARTER, input.variation);
|
||||
result.version = SongRegistry.SONG_METADATA_VERSION;
|
||||
result.timeFormat = input.timeFormat;
|
||||
result.divisions = input.divisions;
|
||||
|
||||
@@ -136,6 +136,9 @@ class DropShadowScreenspace extends DropShadowShader
|
||||
// essentially just stole this from the AngleMask shader but repurposed it to smooth
|
||||
// the threshold because without any sort of smoothing it produces horrible edges
|
||||
float antialias(vec2 fragCoord, float curThreshold, bool useMask) {
|
||||
if (AA_STAGES == 0.0) {
|
||||
return intensityPass(fragCoord, curThreshold, useMask);
|
||||
}
|
||||
|
||||
// In GLSL 100, we need to use constant loop bounds
|
||||
// Well assume a reasonable maximum for AA_STAGES and use a fixed loop
|
||||
|
||||
@@ -362,6 +362,9 @@ class DropShadowShader extends FlxShader
|
||||
// essentially just stole this from the AngleMask shader but repurposed it to smooth
|
||||
// the threshold because without any sort of smoothing it produces horrible edges
|
||||
float antialias(vec2 fragCoord, float curThreshold, bool useMask) {
|
||||
if (AA_STAGES == 0.0) {
|
||||
return intensityPass(fragCoord, curThreshold, useMask);
|
||||
}
|
||||
|
||||
// In GLSL 100, we need to use constant loop bounds
|
||||
// Well assume a reasonable maximum for AA_STAGES and use a fixed loop
|
||||
|
||||
@@ -324,7 +324,7 @@ class Controls extends FlxActionSet
|
||||
inline function get_VOLUME_MUTE()
|
||||
return _volume_mute.check();
|
||||
|
||||
public function new(name, scheme:KeyboardScheme = null)
|
||||
public function new(name, ?scheme:KeyboardScheme)
|
||||
{
|
||||
super(name);
|
||||
|
||||
@@ -372,7 +372,7 @@ class Controls extends FlxActionSet
|
||||
setKeyboardScheme(scheme, false);
|
||||
}
|
||||
|
||||
override function update()
|
||||
override function update():Void
|
||||
{
|
||||
super.update();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package funkin.mobile.external.android;
|
||||
|
||||
import lime.system.JNI;
|
||||
import flixel.util.FlxSignal;
|
||||
|
||||
/**
|
||||
* A Utility class to handle Android API level callbacks and events.
|
||||
*/
|
||||
@:unreflective
|
||||
class CallbackUtil
|
||||
{
|
||||
#if android
|
||||
/**
|
||||
* The result code for `DATA_FOLDER_CLOSED` activity.
|
||||
*/
|
||||
public static var DATA_FOLDER_CLOSED(get, never):Int;
|
||||
|
||||
@:noCompletion
|
||||
static function get_DATA_FOLDER_CLOSED():Int
|
||||
{
|
||||
final field:Null<Dynamic> = JNIUtil.createStaticField('funkin/extensions/CallbackUtil', 'DATA_FOLDER_CLOSED', 'I');
|
||||
|
||||
return field != null ? field.get() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal triggered when an activity result is received.
|
||||
*
|
||||
* First argument is the request code, second is the result code.
|
||||
*/
|
||||
public static var onActivityResult:FlxTypedSignal<Int->Int->Void> = new FlxTypedSignal<Int->Int->Void>();
|
||||
|
||||
/**
|
||||
* Initializes the callback utility.
|
||||
*/
|
||||
public static function init():Void
|
||||
{
|
||||
final initCallBackJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/extensions/CallbackUtil', 'initCallBack', '(Lorg/haxe/lime/HaxeObject;)V');
|
||||
|
||||
if (initCallBackJNI != null)
|
||||
{
|
||||
initCallBackJNI(new CallbackHandler());
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class to handle native callback events.
|
||||
*/
|
||||
class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
|
||||
{
|
||||
#if android
|
||||
@:allow(funkin.mobile.external.android.CallbackUtil)
|
||||
function new():Void {}
|
||||
|
||||
/**
|
||||
* Handles the activity result callback from native code.
|
||||
*
|
||||
* @param requestCode The request code of the acitivty.
|
||||
* @param resultCode The result code of the acitivty.
|
||||
*/
|
||||
@:keep
|
||||
#if (lime >= "8.0.0")
|
||||
@:runOnMainThread
|
||||
#end
|
||||
public function onActivityResult(requestCode:Int, resultCode:Int):Void
|
||||
{
|
||||
if (CallbackUtil.onActivityResult != null) CallbackUtil.onActivityResult.dispatch(requestCode, resultCode);
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package funkin.mobile.external.android;
|
||||
|
||||
/**
|
||||
* A Utility class to manage the Application's Data folder on Android.
|
||||
*/
|
||||
@:unreflective
|
||||
class DataFolderUtil
|
||||
{
|
||||
#if android
|
||||
/**
|
||||
* Opens the data folder on an Android device using JNI.
|
||||
*/
|
||||
public static function openDataFolder():Void
|
||||
{
|
||||
final openDataFolderJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/util/DataFolderUtil', 'openDataFolder', '(I)V');
|
||||
|
||||
if (openDataFolderJNI != null)
|
||||
{
|
||||
openDataFolderJNI(CallbackUtil.DATA_FOLDER_CLOSED);
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package funkin.mobile.external.android;
|
||||
|
||||
#if android
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* A utility class for caching JNI method and field references.
|
||||
*/
|
||||
class JNIUtil
|
||||
{
|
||||
@:noCompletion
|
||||
private static var staticMethodCache:Map<String, Dynamic> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var memberMethodCache:Map<String, Dynamic> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var staticFieldCache:Map<String, JNIStaticField> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var memberFieldCache:Map<String, JNIMemberField> = [];
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached static method reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the method.
|
||||
* @param methodName The name of the method.
|
||||
* @param signature The method signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A dynamic reference to the static method.
|
||||
*/
|
||||
public static function createStaticMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null<Dynamic>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$methodName::$signature';
|
||||
|
||||
if (cache && !staticMethodCache.exists(key)) staticMethodCache.set(key, JNI.createStaticMethod(className, methodName, signature));
|
||||
else if (!cache) return JNI.createStaticMethod(className, methodName, signature);
|
||||
|
||||
return staticMethodCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached member method reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the method.
|
||||
* @param methodName The name of the method.
|
||||
* @param signature The method signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A dynamic reference to the member method.
|
||||
*/
|
||||
public static function createMemberMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null<Dynamic>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$methodName::$signature';
|
||||
|
||||
if (cache && !memberMethodCache.exists(key)) memberMethodCache.set(key, JNI.createMemberMethod(className, methodName, signature));
|
||||
else if (!cache) return JNI.createMemberMethod(className, methodName, signature);
|
||||
|
||||
return memberMethodCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached static field reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the field.
|
||||
* @param fieldName The name of the field.
|
||||
* @param signature The field signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A reference to the static field.
|
||||
*/
|
||||
public static function createStaticField(className:String, fieldName:String, signature:String, cache:Bool = true):Null<JNIStaticField>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$fieldName::$signature';
|
||||
|
||||
if (cache && !staticFieldCache.exists(key)) staticFieldCache.set(key, JNI.createStaticField(className, fieldName, signature));
|
||||
else if (!cache) return JNI.createStaticField(className, fieldName, signature);
|
||||
|
||||
return staticFieldCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached member field reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the field.
|
||||
* @param fieldName The name of the field.
|
||||
* @param signature The field signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A reference to the member field.
|
||||
*/
|
||||
public static function createMemberField(className:String, fieldName:String, signature:String, cache:Bool = true):Null<JNIMemberField>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$fieldName::$signature';
|
||||
|
||||
if (cache && !memberFieldCache.exists(key)) memberFieldCache.set(key, JNI.createMemberField(className, fieldName, signature));
|
||||
else if (!cache) return JNI.createMemberField(className, fieldName, signature);
|
||||
|
||||
return memberFieldCache.get(key);
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,52 @@
|
||||
package funkin.mobile.external.android;
|
||||
|
||||
import lime.math.Rectangle;
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* A Utility class to get Android screen related informations.
|
||||
*/
|
||||
@:unreflective
|
||||
class ScreenUtil
|
||||
{
|
||||
#if android
|
||||
/**
|
||||
* Retrieves the dimensions of display cutouts (such as notches) on Android devices.
|
||||
*
|
||||
* @return An array of `Rectangle` objects, each representing a display cutout's position and size.
|
||||
*/
|
||||
public static function getCutoutDimensions():Array<Rectangle>
|
||||
{
|
||||
final getCutoutDimensionsJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/util/ScreenUtil', 'getCutoutDimensions', '()[Landroid/graphics/Rect;');
|
||||
|
||||
if (getCutoutDimensionsJNI != null)
|
||||
{
|
||||
final rectangles:Array<Rectangle> = [];
|
||||
|
||||
for (rectangle in cast(getCutoutDimensionsJNI(), Array<Dynamic>))
|
||||
{
|
||||
if (rectangle == null) continue;
|
||||
|
||||
final topJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'top', 'I');
|
||||
final leftJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'left', 'I');
|
||||
final rightJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'right', 'I');
|
||||
final bottomJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'bottom', 'I');
|
||||
|
||||
if (topJNI != null && leftJNI != null && rightJNI != null && bottomJNI != null)
|
||||
{
|
||||
final top:Int = topJNI.get(rectangle);
|
||||
final left:Int = leftJNI.get(rectangle);
|
||||
final right:Int = rightJNI.get(rectangle);
|
||||
final bottom:Int = bottomJNI.get(rectangle);
|
||||
|
||||
rectangles.push(new Rectangle(left, top, right - left, bottom - top));
|
||||
}
|
||||
}
|
||||
|
||||
return rectangles;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
#end
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package funkin.extensions;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
import org.haxe.lime.HaxeObject;
|
||||
|
||||
public class CallbackUtil extends Extension
|
||||
{
|
||||
/**
|
||||
* Constant representing the event when the data folder is closed.
|
||||
*/
|
||||
public static int DATA_FOLDER_CLOSED = 0x01;
|
||||
|
||||
private static HaxeObject haxeObject;
|
||||
|
||||
/**
|
||||
* Initializes the callback object for handling Haxe callbacks.
|
||||
*
|
||||
* @param haxeObject The HaxeObject instance to handle callbacks.
|
||||
*/
|
||||
public static void initCallBack(final HaxeObject haxeObject)
|
||||
{
|
||||
CallbackUtil.haxeObject = haxeObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (haxeObject != null)
|
||||
haxeObject.call2("onActivityResult", requestCode, resultCode);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+346
@@ -0,0 +1,346 @@
|
||||
package funkin.provider;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.graphics.Point;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.DocumentsContract.Document;
|
||||
import android.provider.DocumentsContract.Root;
|
||||
import android.provider.DocumentsProvider;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* @see https://github.com/termux/termux-app/blob/7bceab88e2272f961d1b94ef736f1a9e20173247/app/src/main/java/com/termux/filepicker/TermuxDocumentsProvider.java
|
||||
*/
|
||||
public class DataFolderProvider extends DocumentsProvider
|
||||
{
|
||||
private static File BASE_DIR;
|
||||
|
||||
private static String BASE_DIR_PATH;
|
||||
|
||||
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
|
||||
Root.COLUMN_ROOT_ID,
|
||||
Root.COLUMN_MIME_TYPES,
|
||||
Root.COLUMN_FLAGS,
|
||||
Root.COLUMN_ICON,
|
||||
Root.COLUMN_TITLE,
|
||||
Root.COLUMN_SUMMARY,
|
||||
Root.COLUMN_DOCUMENT_ID,
|
||||
Root.COLUMN_AVAILABLE_BYTES
|
||||
};
|
||||
|
||||
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
|
||||
Document.COLUMN_DOCUMENT_ID,
|
||||
Document.COLUMN_MIME_TYPE,
|
||||
Document.COLUMN_DISPLAY_NAME,
|
||||
Document.COLUMN_LAST_MODIFIED,
|
||||
Document.COLUMN_FLAGS,
|
||||
Document.COLUMN_SIZE
|
||||
};
|
||||
|
||||
@Override
|
||||
public Cursor queryRoots(String[] projection)
|
||||
{
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION);
|
||||
|
||||
if (BASE_DIR == null)
|
||||
return result;
|
||||
|
||||
final MatrixCursor.RowBuilder row = result.newRow();
|
||||
|
||||
row.add(Root.COLUMN_ROOT_ID, getDocIdForFile(BASE_DIR));
|
||||
row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(BASE_DIR));
|
||||
row.add(Root.COLUMN_SUMMARY, "Data Folder");
|
||||
row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD);
|
||||
row.add(Root.COLUMN_TITLE, "::APP_TITLE::");
|
||||
row.add(Root.COLUMN_MIME_TYPES, "*/*");
|
||||
row.add(Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace());
|
||||
::if (APP_PACKAGE != "")::
|
||||
row.add(Root.COLUMN_ICON, ::APP_PACKAGE::.R.mipmap.ic_launcher);
|
||||
::end::
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException
|
||||
{
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||
includeFile(result, documentId, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException
|
||||
{
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||
|
||||
File parent = getFileForDocId(parentDocumentId);
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
File[] children = null;
|
||||
|
||||
try {
|
||||
children = parent.listFiles();
|
||||
} catch (SecurityException e) {
|
||||
children = new File[0];
|
||||
}
|
||||
|
||||
if (children != null)
|
||||
{
|
||||
for (File file : children)
|
||||
includeFile(result, null, file);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal) throws FileNotFoundException
|
||||
{
|
||||
return ParcelFileDescriptor.open(getFileForDocId(documentId), ParcelFileDescriptor.parseMode(mode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException
|
||||
{
|
||||
final File file = getFileForDocId(documentId);
|
||||
final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
return new AssetFileDescriptor(pfd, 0, file.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate()
|
||||
{
|
||||
BASE_DIR = getContext().getExternalFilesDir(null);
|
||||
|
||||
if (BASE_DIR == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
BASE_DIR_PATH = BASE_DIR.getCanonicalPath();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
BASE_DIR_PATH = BASE_DIR.getAbsolutePath();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException
|
||||
{
|
||||
File parentFile = getFileForDocId(parentDocumentId);
|
||||
|
||||
File newFile = new File(parentFile, displayName);
|
||||
|
||||
int noConflictId = 2;
|
||||
|
||||
while (newFile.exists())
|
||||
{
|
||||
newFile = new File(parentFile, displayName + " (" + noConflictId++ + ")");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boolean succeeded;
|
||||
|
||||
if (Document.MIME_TYPE_DIR.equals(mimeType))
|
||||
succeeded = newFile.mkdir();
|
||||
else
|
||||
succeeded = newFile.createNewFile();
|
||||
|
||||
if (!succeeded)
|
||||
throw new FileNotFoundException("Failed to create document with id " + newFile.getPath());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new FileNotFoundException("Failed to create document with id " + newFile.getPath());
|
||||
}
|
||||
|
||||
return newFile.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDocument(String documentId) throws FileNotFoundException
|
||||
{
|
||||
if (!deleteRecursive(getFileForDocId(documentId)))
|
||||
throw new FileNotFoundException("Failed to delete document with id " + documentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDocumentType(String documentId) throws FileNotFoundException
|
||||
{
|
||||
return getMimeType(getFileForDocId(documentId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor querySearchDocuments(String rootId, String query, String[] projection) throws FileNotFoundException
|
||||
{
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||
|
||||
final LinkedList<File> pending = new LinkedList<>();
|
||||
|
||||
pending.add(getFileForDocId(rootId));
|
||||
|
||||
final int MAX_SEARCH_RESULTS = 50;
|
||||
|
||||
while (!pending.isEmpty() && result.getCount() < MAX_SEARCH_RESULTS)
|
||||
{
|
||||
final File file = pending.removeFirst();
|
||||
|
||||
boolean isInsideHome;
|
||||
|
||||
try
|
||||
{
|
||||
isInsideHome = file.getCanonicalPath().startsWith(BASE_DIR_PATH);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
isInsideHome = true;
|
||||
}
|
||||
|
||||
if (isInsideHome)
|
||||
{
|
||||
if (file.isDirectory())
|
||||
{
|
||||
try {
|
||||
Collections.addAll(pending, file.listFiles());
|
||||
} catch (SecurityException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
else if (file.getName().toLowerCase().contains(query))
|
||||
{
|
||||
includeFile(result, null, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildDocument(String parentDocumentId, String documentId)
|
||||
{
|
||||
try
|
||||
{
|
||||
File parent = getFileForDocId(parentDocumentId).getCanonicalFile();
|
||||
File child = getFileForDocId(documentId).getCanonicalFile();
|
||||
return child.getPath().startsWith(parent.getPath() + "/");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deleteRecursive(File file)
|
||||
{
|
||||
if (file.isDirectory())
|
||||
{
|
||||
File[] children = file.listFiles();
|
||||
|
||||
if (children != null)
|
||||
{
|
||||
for (File child : children)
|
||||
{
|
||||
if (!deleteRecursive(child))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
private static String getDocIdForFile(File file)
|
||||
{
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
private static File getFileForDocId(String docId) throws FileNotFoundException
|
||||
{
|
||||
if (BASE_DIR == null)
|
||||
throw new FileNotFoundException("Base directory not available");
|
||||
|
||||
final File f = (docId == null || docId.isEmpty()) ? BASE_DIR : new File(docId);
|
||||
|
||||
if (!f.exists())
|
||||
throw new FileNotFoundException(f.getAbsolutePath() + " not found");
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
private static String getMimeType(File file)
|
||||
{
|
||||
if (file == null || file.isDirectory())
|
||||
return Document.MIME_TYPE_DIR;
|
||||
|
||||
String name = file.getName();
|
||||
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
|
||||
if (lastDot >= 0)
|
||||
{
|
||||
String extension = name.substring(lastDot + 1).toLowerCase();
|
||||
|
||||
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
|
||||
if (mime != null)
|
||||
return mime;
|
||||
}
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
private void includeFile(MatrixCursor result, String docId, File file) throws FileNotFoundException
|
||||
{
|
||||
if (docId == null)
|
||||
docId = getDocIdForFile(file);
|
||||
else
|
||||
file = getFileForDocId(docId);
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if (file.isDirectory())
|
||||
{
|
||||
if (file.canWrite())
|
||||
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
|
||||
}
|
||||
else if (file.canWrite())
|
||||
{
|
||||
flags |= Document.FLAG_SUPPORTS_WRITE;
|
||||
}
|
||||
|
||||
if (file.getParentFile() != null && file.getParentFile().canWrite())
|
||||
flags |= Document.FLAG_SUPPORTS_DELETE;
|
||||
|
||||
final String mimeType = getMimeType(file);
|
||||
|
||||
if (mimeType.startsWith("image/"))
|
||||
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
|
||||
|
||||
final MatrixCursor.RowBuilder row = result.newRow();
|
||||
row.add(Document.COLUMN_DOCUMENT_ID, docId);
|
||||
row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
|
||||
row.add(Document.COLUMN_SIZE, file.length());
|
||||
row.add(Document.COLUMN_MIME_TYPE, mimeType);
|
||||
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
|
||||
row.add(Document.COLUMN_FLAGS, flags);
|
||||
::if (APP_PACKAGE != "")::
|
||||
row.add(Document.COLUMN_ICON, ::APP_PACKAGE::.R.mipmap.ic_launcher);
|
||||
::end::
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package funkin.util;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.content.pm.PackageInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
public class DataFolderUtil
|
||||
{
|
||||
/**
|
||||
* A method that opens the Application's data folder for browsing through the Storage Access Framework.
|
||||
* It's highly based on some code borrowed from Mterial Files
|
||||
* https://github.com/zhanghai/MaterialFiles
|
||||
*/
|
||||
public static void openDataFolder(int requestCode)
|
||||
{
|
||||
::if (APP_PACKAGE != "")::
|
||||
if (Extension.mainActivity != null)
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(DocumentsContract.buildRootUri("::APP_PACKAGE::.docprovider", ""), "vnd.android.document/directory");
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
Extension.mainActivity.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
::end::
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package funkin.util;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
public class ScreenUtil
|
||||
{
|
||||
public static Rect[] getCutoutDimensions()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
{
|
||||
if (Extension.mainActivity != null)
|
||||
{
|
||||
WindowInsets insets = Extension.mainActivity.getWindow().getDecorView().getRootWindowInsets();
|
||||
|
||||
if (insets != null)
|
||||
{
|
||||
DisplayCutout cutout = insets.getDisplayCutout();
|
||||
|
||||
if (cutout != null)
|
||||
{
|
||||
List<Rect> boundingRects = cutout.getBoundingRects();
|
||||
|
||||
if (boundingRects != null && !boundingRects.isEmpty())
|
||||
return boundingRects.toArray(new Rect[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Rect[0];
|
||||
}
|
||||
}
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.mobile.external;
|
||||
package funkin.mobile.external.ios;
|
||||
|
||||
/**
|
||||
* A Utility class to manage iOS audio.
|
||||
Vendored
+4
-3
@@ -1,15 +1,16 @@
|
||||
package funkin.mobile.external;
|
||||
package funkin.mobile.external.ios;
|
||||
|
||||
/**
|
||||
* A Utility class to get iOS screen related informations.
|
||||
*/
|
||||
@:build(funkin.mobile.macros.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('ScreenUtils.hpp')
|
||||
@:include('ScreenUtil.hpp')
|
||||
@:unreflective
|
||||
extern class ScreenUtils
|
||||
extern class ScreenUtil
|
||||
{
|
||||
@:native('getSafeAreaInsets')
|
||||
static function getSafeAreaInsets(top:cpp.RawPointer<Float>, bottom:cpp.RawPointer<Float>, left:cpp.RawPointer<Float>, right:cpp.RawPointer<Float>):Void;
|
||||
|
||||
@:native('getScreenSize')
|
||||
static function getScreenSize(width:cpp.RawPointer<Float>, height:cpp.RawPointer<Float>):Void;
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
<files id="external-ios" dir="${this_dir}/ios" if="iphoneos || iphonesim">
|
||||
<compilerflag value="-I${this_dir}/ios/include" />
|
||||
|
||||
<file name="src/ScreenUtils.mm" />
|
||||
<file name="src/ScreenUtil.mm" />
|
||||
<file name="src/AudioSession.mm" />
|
||||
</files>
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#import "ScreenUtils.hpp"
|
||||
#import "ScreenUtil.hpp"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -66,7 +66,7 @@ class FunkinBackButton extends FunkinButton
|
||||
held = true;
|
||||
|
||||
FlxTween.cancelTweensOf(this);
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
animation.play('hold');
|
||||
|
||||
alpha = 1;
|
||||
|
||||
@@ -28,6 +28,8 @@ class InAppPurchasesUtil
|
||||
*/
|
||||
public static final UPGRADE_PRODUCT_ID:String = 'no_ads';
|
||||
|
||||
public static var hasInitialized:Bool = false;
|
||||
|
||||
/**
|
||||
* A static variable that holds an array of currently loaded product details for in-app purchases.
|
||||
*/
|
||||
@@ -65,9 +67,14 @@ class InAppPurchasesUtil
|
||||
});
|
||||
|
||||
IAPAndroid.onProductDetailsResponse.add(function(result:IAPResult, productDetails:Array<IAPProductDetails>):Void {
|
||||
if (result.getResponseCode() == IAPResponseCode.OK) currentProductDetails = productDetails;
|
||||
if (result.getResponseCode() == IAPResponseCode.OK)
|
||||
{
|
||||
hasInitialized = true;
|
||||
currentProductDetails = productDetails;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasInitialized = false;
|
||||
trace('Failed to fetch product details: "$result"');
|
||||
}
|
||||
});
|
||||
@@ -101,15 +108,21 @@ class InAppPurchasesUtil
|
||||
IAPAndroid.startConnection();
|
||||
#else
|
||||
IAPIOS.onProductDetailsReceived.add(function(productDetails:Array<IAPProductDetails>):Void {
|
||||
if (productDetails != null) currentProductDetails = productDetails;
|
||||
if (productDetails != null)
|
||||
{
|
||||
currentProductDetails = productDetails;
|
||||
}
|
||||
});
|
||||
|
||||
IAPIOS.onProductDetailsFailed.add(function(error:IAPError):Void {
|
||||
trace('Failed to request product details: "$error"');
|
||||
hasInitialized = false;
|
||||
});
|
||||
|
||||
IAPIOS.onPurchasesUpdated.add(function(purchases:Array<IAPPurchase>):Void {
|
||||
handlePurchases(purchases);
|
||||
trace("iOS purchases updated: " + purchases.length);
|
||||
hasInitialized = true;
|
||||
trace("hasInitialized: " + hasInitialized);
|
||||
});
|
||||
|
||||
IAPIOS.init();
|
||||
@@ -147,20 +160,32 @@ class InAppPurchasesUtil
|
||||
{
|
||||
function purchasesUpdatedEvent(result:IAPResult, purchases:Array<IAPPurchase>):Void
|
||||
{
|
||||
for (purchase in purchases)
|
||||
if (result.getResponseCode() == IAPResponseCode.OK)
|
||||
{
|
||||
if (purchase.getProducts().contains(id))
|
||||
for (purchase in purchases)
|
||||
{
|
||||
if (purchase.getPurchaseState() == IAPPurchaseState.PURCHASED)
|
||||
if (purchase.getProducts().contains(id))
|
||||
{
|
||||
if (onPurchased != null) onPurchased();
|
||||
if (purchase.getPurchaseState() == IAPPurchaseState.PURCHASED)
|
||||
{
|
||||
if (onPurchased != null) onPurchased();
|
||||
|
||||
IAPAndroid.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IAPAndroid.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
}
|
||||
|
||||
IAPAndroid.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
final debugMessage:Null<String> = result.getDebugMessage();
|
||||
|
||||
Toast.makeText(result.getDebugMessage(), Toast.LENGTH_SHORT);
|
||||
if (debugMessage != null && debugMessage.length > 0)
|
||||
{
|
||||
Toast.makeText(debugMessage, Toast.LENGTH_SHORT);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IAPAndroid.onPurchasesUpdated.has(purchasesUpdatedEvent))
|
||||
@@ -180,14 +205,18 @@ class InAppPurchasesUtil
|
||||
{
|
||||
if (purchase.getPaymentProductIdentifier() == id)
|
||||
{
|
||||
if (purchase.getTransactionState() == IAPPurchaseState.PURCHASED)
|
||||
switch (purchase.getTransactionState())
|
||||
{
|
||||
if (onPurchased != null) onPurchased();
|
||||
case IAPPurchaseState.PURCHASED:
|
||||
if (onPurchased != null) onPurchased();
|
||||
|
||||
IAPIOS.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
case IAPPurchaseState.FAILED:
|
||||
IAPIOS.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IAPIOS.onPurchasesUpdated.remove(purchasesUpdatedEvent);
|
||||
}
|
||||
|
||||
if (!IAPIOS.onPurchasesUpdated.has(purchasesUpdatedEvent))
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package funkin.mobile.util;
|
||||
|
||||
#if ios
|
||||
import funkin.mobile.external.ScreenUtils;
|
||||
import funkin.mobile.external.ios.ScreenUtil as NativeScreenUtil;
|
||||
#elseif android
|
||||
import extension.androidtools.Tools;
|
||||
import funkin.mobile.external.android.ScreenUtil as NativeScreenUtil;
|
||||
#end
|
||||
import lime.math.Rectangle;
|
||||
import lime.system.System;
|
||||
@@ -26,7 +26,7 @@ class ScreenUtil
|
||||
final rectDimensions:Array<Array<Float>> = [[], [], [], []];
|
||||
|
||||
// Push all the dimensions of the cutouts into an array
|
||||
for (rect in Tools.getCutoutDimensions())
|
||||
for (rect in NativeScreenUtil.getCutoutDimensions())
|
||||
{
|
||||
rectDimensions[0].push(rect.x);
|
||||
rectDimensions[1].push(rect.y);
|
||||
@@ -59,13 +59,12 @@ class ScreenUtil
|
||||
var bottomInset:Float = -1;
|
||||
var deviceWidth:Float = -1;
|
||||
var deviceHeight:Float = -1;
|
||||
var displayOrientation:DisplayOrientation = System.getDisplayOrientation(0);
|
||||
|
||||
ScreenUtils.getSafeAreaInsets(cpp.RawPointer.addressOf(topInset), cpp.RawPointer.addressOf(bottomInset), cpp.RawPointer.addressOf(leftInset),
|
||||
NativeScreenUtil.getSafeAreaInsets(cpp.RawPointer.addressOf(topInset), cpp.RawPointer.addressOf(bottomInset), cpp.RawPointer.addressOf(leftInset),
|
||||
cpp.RawPointer.addressOf(rightInset));
|
||||
|
||||
ScreenUtils.getScreenSize(cpp.RawPointer.addressOf(deviceWidth), cpp.RawPointer.addressOf(deviceHeight));
|
||||
|
||||
var displayOrientation = System.getDisplayOrientation(0);
|
||||
NativeScreenUtil.getScreenSize(cpp.RawPointer.addressOf(deviceWidth), cpp.RawPointer.addressOf(deviceHeight));
|
||||
|
||||
notchRect.x = 0;
|
||||
notchRect.y = 0.0;
|
||||
|
||||
@@ -545,6 +545,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||
*/
|
||||
public function goBack():Void
|
||||
{
|
||||
if (blueballed == false) return;
|
||||
isEnding = true;
|
||||
blueballed = false;
|
||||
if (parentPlayState != null) parentPlayState.deathCounter = 0;
|
||||
|
||||
@@ -536,7 +536,7 @@ class PauseSubState extends MusicBeatSubState
|
||||
FlxTween.tween(pauseCircle, {alpha: 0}, 0.6, {ease: FlxEase.quartOut});
|
||||
|
||||
hapticTimer.start(0.2, function(_) {
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
});
|
||||
|
||||
dataFadeTimer.start(0.3, function(_) {
|
||||
@@ -640,12 +640,12 @@ class PauseSubState extends MusicBeatSubState
|
||||
if (i == currentEntry)
|
||||
{
|
||||
currentMenuEntries[currentEntry].callback(this);
|
||||
HapticUtil.vibrate(0, 0.05, 0.5);
|
||||
HapticUtil.vibrate(0, 0.05, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
changeSelection(i - currentEntry);
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -745,9 +745,6 @@ class PauseSubState extends MusicBeatSubState
|
||||
clearAndAddMenuEntries();
|
||||
updateMetadataText();
|
||||
changeSelection();
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
SwipeUtil.calculateSwipeThreshold(menuEntryText.members, Y);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -545,6 +545,8 @@ class PlayState extends MusicBeatSubState
|
||||
*/
|
||||
public var comboPopUps:PopUpStuff;
|
||||
|
||||
public var isSongEnd:Bool = false;
|
||||
|
||||
#if mobile
|
||||
/**
|
||||
* The pause button for the game, only appears in Mobile targets.
|
||||
@@ -1053,7 +1055,7 @@ class PlayState extends MusicBeatSubState
|
||||
}
|
||||
|
||||
#if mobile
|
||||
// note scrolling is shit on mobile without the arguments!!!
|
||||
// Note scrolling is less smooth on mobile without these arguments!!!
|
||||
Conductor.instance.update(Conductor.instance.songPosition + elapsed * 1000 * playbackRate, false);
|
||||
#else
|
||||
Conductor.instance.update(); // Normal conductor update.
|
||||
@@ -1063,7 +1065,7 @@ class PlayState extends MusicBeatSubState
|
||||
// This helps prevent a major bug where the level suddenly loops back to the start or middle.
|
||||
if (Conductor.instance.songPosition >= (FlxG.sound.music.endTime ?? FlxG.sound.music.length))
|
||||
{
|
||||
if (mayPauseGame) endSong(skipEndingTransition);
|
||||
if (mayPauseGame && !isSongEnd) endSong(skipEndingTransition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1425,8 +1427,11 @@ class PlayState extends MusicBeatSubState
|
||||
FlxG.sound.list.forEachAlive(function(sound:FlxSound) {
|
||||
if (!sound.active || sound == FlxG.sound.music) return;
|
||||
// In case it's a scheduled sound
|
||||
var funkinSound:FunkinSound = cast sound;
|
||||
if (funkinSound != null && !funkinSound.isPlaying) return;
|
||||
if (Std.isOfType(sound, FunkinSound))
|
||||
{
|
||||
var funkinSound:FunkinSound = cast sound;
|
||||
if (funkinSound != null && !funkinSound.isPlaying) return;
|
||||
}
|
||||
if (!sound.playing && sound.time >= 0) return;
|
||||
sound.pause();
|
||||
soundsPausedBySubState.add(sound);
|
||||
@@ -1442,6 +1447,8 @@ class PlayState extends MusicBeatSubState
|
||||
}
|
||||
}
|
||||
|
||||
if (!vwooshTimer.finished) vwooshTimer.active = false;
|
||||
|
||||
// Pause camera tweening, and keep track of which tweens we pause.
|
||||
if (cameraFollowTween != null && cameraFollowTween.active)
|
||||
{
|
||||
@@ -2960,7 +2967,8 @@ class PlayState extends MusicBeatSubState
|
||||
#end
|
||||
|
||||
// 9: Toggle the old icon.
|
||||
if (FlxG.keys.justPressed.NINE && iconP1 != null) iconP1.toggleOldIcon();
|
||||
if ((FlxG.keys.justPressed.NINE #if FEATURE_TOUCH_CONTROLS || (TouchUtil.justPressed && TouchUtil.overlapsComplex(iconP1)) #end)
|
||||
&& iconP1 != null) iconP1.toggleOldIcon();
|
||||
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// PAGEUP: Skip forward two sections.
|
||||
@@ -3106,6 +3114,7 @@ class PlayState extends MusicBeatSubState
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.volume = 0;
|
||||
vocals.volume = 0;
|
||||
mayPauseGame = false;
|
||||
isSongEnd = true;
|
||||
|
||||
// Prevent ghost misses while the song is ending.
|
||||
disableKeys = true;
|
||||
|
||||
@@ -773,7 +773,7 @@ class ResultState extends MusicBeatSubState
|
||||
// GF slams into the wall.
|
||||
if (atlas.sprite.anim.curFrame == 51)
|
||||
{
|
||||
HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE / 3);
|
||||
HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -797,7 +797,7 @@ class ResultState extends MusicBeatSubState
|
||||
// Pico shoots.
|
||||
if (atlas.sprite.anim.curFrame == 45)
|
||||
{
|
||||
HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE / 3);
|
||||
HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ class NoteVibrationsHandler
|
||||
|
||||
if (stackingAmplitude > Constants.MAX_VIBRATION_AMPLITUDE) stackingAmplitude = Constants.MAX_VIBRATION_AMPLITUDE;
|
||||
|
||||
if (stackingAmplitude > 0) HapticUtil.vibrate(0, 0.01, stackingAmplitude, 1, [HapticsMode.ALL, HapticsMode.NOTES_ONLY]);
|
||||
if (stackingAmplitude > 0) HapticUtil.vibrate(0, 0.01, stackingAmplitude * 2.5, 1, [HapticsMode.ALL, HapticsMode.NOTES_ONLY]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +84,7 @@ class NoteVibrationsHandler
|
||||
|
||||
if (stackingAmplitude > Constants.MAX_VIBRATION_AMPLITUDE) stackingAmplitude = Constants.MAX_VIBRATION_AMPLITUDE;
|
||||
|
||||
if (stackingAmplitude > 0) HapticUtil.vibrate(0, 0.01, stackingAmplitude, 1, [HapticsMode.ALL, HapticsMode.NOTES_ONLY]);
|
||||
if (stackingAmplitude > 0) HapticUtil.vibrate(0, 0.01, stackingAmplitude * 2.5, 1, [HapticsMode.ALL, HapticsMode.NOTES_ONLY]);
|
||||
|
||||
for (currentNoteStatus in noteStatuses)
|
||||
{
|
||||
|
||||
@@ -162,7 +162,7 @@ class Strumline extends FlxSpriteGroup
|
||||
final inArrowContorlSchemeMode:Bool = #if mobile (Preferences.controlsScheme == FunkinHitboxControlSchemes.Arrows
|
||||
&& !ControlsHandler.usingExternalInputDevice) #else false #end;
|
||||
|
||||
var isDownscroll:Bool = #if mobile (Preferences.controlsScheme == FunkinHitboxControlSchemes.Arrows
|
||||
public var isDownscroll:Bool = #if mobile (Preferences.controlsScheme == FunkinHitboxControlSchemes.Arrows
|
||||
&& !ControlsHandler.usingExternalInputDevice)
|
||||
|| #end Preferences.downscroll;
|
||||
|
||||
|
||||
@@ -208,7 +208,9 @@ class SustainTrail extends FlxSprite
|
||||
graphicHeight = sustainHeight(sustainLength, parentStrumline?.scrollSpeed ?? 1.0);
|
||||
// instead of scrollSpeed, PlayState.SONG.speed
|
||||
|
||||
flipY = Preferences.downscroll #if mobile || (Preferences.controlsScheme == FunkinHitboxControlSchemes.Arrows) #end;
|
||||
flipY = Preferences.downscroll #if mobile
|
||||
|| (Preferences.controlsScheme == FunkinHitboxControlSchemes.Arrows
|
||||
&& !funkin.mobile.input.ControlsHandler.usingExternalInputDevice) #end;
|
||||
|
||||
// alpha = 0.6;
|
||||
alpha = 1.0;
|
||||
|
||||
@@ -123,6 +123,7 @@ class Save
|
||||
zoomCamera: true,
|
||||
debugDisplay: false,
|
||||
hapticsMode: 'All',
|
||||
hapticsIntensityMultiplier: 1,
|
||||
autoPause: true,
|
||||
vsyncMode: 'Off',
|
||||
strumlineBackgroundOpacity: 0,
|
||||
@@ -158,7 +159,8 @@ class Save
|
||||
{
|
||||
// Reasonable defaults.
|
||||
screenTimeout: false,
|
||||
controlsScheme: FunkinHitboxControlSchemes.Arrows
|
||||
controlsScheme: FunkinHitboxControlSchemes.Arrows,
|
||||
noAds: false
|
||||
},
|
||||
#end
|
||||
|
||||
@@ -1553,6 +1555,12 @@ typedef SaveDataOptions =
|
||||
*/
|
||||
var hapticsMode:String;
|
||||
|
||||
/**
|
||||
* Multiplier of intensity for all the haptic feedback effects.
|
||||
* @default `1`
|
||||
*/
|
||||
var hapticsIntensityMultiplier:Float;
|
||||
|
||||
/**
|
||||
* If enabled, the game will automatically pause when tabbing out.
|
||||
* @default `true`
|
||||
@@ -1639,6 +1647,12 @@ typedef SaveDataMobileOptions =
|
||||
* @default `Arrows`
|
||||
*/
|
||||
var controlsScheme:String;
|
||||
|
||||
/**
|
||||
* If bought, the game will not show any ads.
|
||||
* @default `false`
|
||||
*/
|
||||
var noAds:Bool;
|
||||
};
|
||||
|
||||
#end
|
||||
|
||||
@@ -18,6 +18,7 @@ class MenuTypedList<T:MenuListItem> extends FlxTypedGroup<T>
|
||||
{
|
||||
// Pause input variable
|
||||
public static var pauseInput:Bool = false;
|
||||
|
||||
public var selectedIndex(default, null):Int = 0;
|
||||
public var selectedItem(get, never):T;
|
||||
|
||||
@@ -180,7 +181,7 @@ class MenuTypedList<T:MenuListItem> extends FlxTypedGroup<T>
|
||||
item.scale.set(1.1, 1.1);
|
||||
FlxTween.tween(item.scale, {x: 1, y: 1}, 0.3, {ease: FlxEase.backOut});
|
||||
|
||||
HapticUtil.vibrate(0, 0.05, 0.5);
|
||||
HapticUtil.vibrate(0, 0.05, 1);
|
||||
accept();
|
||||
}
|
||||
else
|
||||
@@ -189,7 +190,7 @@ class MenuTypedList<T:MenuListItem> extends FlxTypedGroup<T>
|
||||
item.scale.set(0.94, 0.94);
|
||||
FlxTween.tween(item.scale, {x: 1, y: 1}, 0.3, {ease: FlxEase.backOut});
|
||||
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -23,9 +23,10 @@ class Nametag extends FlxSprite
|
||||
shader = mosaicShader;
|
||||
|
||||
// So that's why there was that cursed sight (originally defaulted to bf)
|
||||
if (character != null) switchChar(character);
|
||||
// Made it not play the shader effect to prevent its being stuck, can't see it anyway.
|
||||
if (character != null) switchChar(character, false);
|
||||
else
|
||||
switchChar(Constants.DEFAULT_CHARACTER);
|
||||
switchChar(Constants.DEFAULT_CHARACTER, false);
|
||||
}
|
||||
|
||||
public function updatePosition():Void
|
||||
@@ -37,9 +38,9 @@ class Nametag extends FlxSprite
|
||||
y -= offsetY;
|
||||
}
|
||||
|
||||
public function switchChar(str:String):Void
|
||||
public function switchChar(str:String, playMosaicSequence:Bool = true):Void
|
||||
{
|
||||
shaderEffect();
|
||||
if (playMosaicSequence) shaderEffect();
|
||||
|
||||
new FlxTimer().start(4 / 30, _ -> {
|
||||
var path:String = str;
|
||||
@@ -54,7 +55,8 @@ class Nametag extends FlxSprite
|
||||
scale.x = scale.y = 0.77;
|
||||
|
||||
updatePosition();
|
||||
shaderEffect(true);
|
||||
|
||||
if (playMosaicSequence) shaderEffect(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,12 @@ class CreditsState extends MusicBeatState
|
||||
{
|
||||
super.create();
|
||||
|
||||
#if ios
|
||||
var fix = new FlxText();
|
||||
fix.font = CREDITS_FONT;
|
||||
fix.draw();
|
||||
#end
|
||||
|
||||
backersToBuild = CreditsDataHandler.fetchBackerEntries();
|
||||
|
||||
entriesToBuild = [];
|
||||
|
||||
@@ -1306,7 +1306,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
var result:Null<SongMetadata> = songMetadata.get(selectedVariation);
|
||||
if (result == null)
|
||||
{
|
||||
result = new SongMetadata('Default Song Name', Constants.DEFAULT_ARTIST, selectedVariation);
|
||||
result = new SongMetadata('Default Song Name', Constants.DEFAULT_ARTIST, Constants.DEFAULT_CHARTER, selectedVariation);
|
||||
songMetadata.set(selectedVariation, result);
|
||||
}
|
||||
return result;
|
||||
@@ -5151,8 +5151,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
function handleToolboxes():Void
|
||||
{
|
||||
handleDifficultyToolbox();
|
||||
handlePlayerPreviewToolbox();
|
||||
handleOpponentPreviewToolbox();
|
||||
// handlePlayerPreviewToolbox();
|
||||
// handleOpponentPreviewToolbox();
|
||||
}
|
||||
|
||||
function handleDifficultyToolbox():Void
|
||||
|
||||
@@ -580,7 +580,7 @@ class ChartEditorDialogHandler
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
}
|
||||
|
||||
var newSongMetadata:SongMetadata = new SongMetadata('', '', Constants.DEFAULT_VARIATION);
|
||||
var newSongMetadata:SongMetadata = new SongMetadata('', '', '', Constants.DEFAULT_VARIATION);
|
||||
|
||||
newSongMetadata.variation = targetVariation;
|
||||
newSongMetadata.playData.difficulties = (erect) ? ['erect', 'nightmare'] : ['easy', 'normal', 'hard'];
|
||||
@@ -1219,7 +1219,8 @@ class ChartEditorDialogHandler
|
||||
var dialogVariationName:Null<TextField> = dialog.findComponent('dialogVariationName', TextField);
|
||||
if (dialogVariationName == null) throw 'Could not locate dialogVariationName TextField in Add Variation dialog';
|
||||
|
||||
var pendingVariation:SongMetadata = new SongMetadata(dialogSongName.text, dialogSongArtist.text, dialogVariationName.text.toLowerCase());
|
||||
var pendingVariation:SongMetadata = new SongMetadata(dialogSongName.text, dialogSongArtist.text, dialogSongCharter.text,
|
||||
dialogVariationName.text.toLowerCase());
|
||||
|
||||
pendingVariation.playData.stage = dialogStage.value.id;
|
||||
pendingVariation.playData.noteStyle = dialogNoteStyle.value.id;
|
||||
|
||||
@@ -175,6 +175,8 @@ class AlbumRoll extends FlxSpriteGroup
|
||||
newAlbumArt.playAnimation('intro', true);
|
||||
|
||||
difficultyStars.visible = false;
|
||||
difficultyStars.flameCheck();
|
||||
|
||||
new FlxTimer().start(0.75, function(_) {
|
||||
showTitle();
|
||||
showStars();
|
||||
|
||||
@@ -64,6 +64,7 @@ class FreeplayFlames extends FlxSpriteGroup
|
||||
timers.remove(timer);
|
||||
}
|
||||
|
||||
this.properPositions = false;
|
||||
this.flameCount = value;
|
||||
var visibleCount:Int = 0;
|
||||
for (i in 0...5)
|
||||
|
||||
@@ -11,28 +11,16 @@ class FreeplayScore extends FlxTypedSpriteGroup<ScoreNum>
|
||||
function set_scoreShit(val):Int
|
||||
{
|
||||
if (group == null || group.members == null) return val;
|
||||
var loopNum:Int = group.members.length - 1;
|
||||
var dumbNumb = Std.parseInt(Std.string(val));
|
||||
if (dumbNumb == null) dumbNumb = 0;
|
||||
var prevNum:ScoreNum;
|
||||
|
||||
var dumbNumb:Int = Std.parseInt(Std.string(val)) ?? 0;
|
||||
dumbNumb = Std.int(Math.min(dumbNumb, Math.pow(10, group.members.length) - 1));
|
||||
|
||||
var loopNum:Int = group.members.length - 1;
|
||||
|
||||
while (dumbNumb > 0)
|
||||
{
|
||||
group.members[loopNum].digit = dumbNumb % 10;
|
||||
|
||||
// var funnyNum = group.members[loopNum];
|
||||
// prevNum = group.members[loopNum + 1];
|
||||
|
||||
// if (prevNum != null)
|
||||
// {
|
||||
// funnyNum.x = prevNum.x - (funnyNum.width * 0.7);
|
||||
// }
|
||||
|
||||
// funnyNum.y = (funnyNum.baseY - (funnyNum.height / 2)) + 73;
|
||||
// funnyNum.x = (funnyNum.baseX - (funnyNum.width / 2)) + 450; // this plus value is hand picked lol!
|
||||
|
||||
dumbNumb = Math.floor(dumbNumb / 10);
|
||||
loopNum--;
|
||||
}
|
||||
@@ -71,6 +59,11 @@ class FreeplayScore extends FlxTypedSpriteGroup<ScoreNum>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ScoreNum is the number graphic that is used for the completion percentage.
|
||||
* It handles offsetting / positioning of the numbers so they look a bit nicer placed
|
||||
* NOTE: this is actually a bit similar to the ResultScore class, should perhaps tidy the logic up?
|
||||
*/
|
||||
@:nullSafety
|
||||
class ScoreNum extends FlxSprite
|
||||
{
|
||||
@@ -106,18 +99,10 @@ class ScoreNum extends FlxSprite
|
||||
return val;
|
||||
}
|
||||
|
||||
public var baseY:Float = 0;
|
||||
public var baseX:Float = 0;
|
||||
|
||||
var numToString:Array<String> = ["ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"];
|
||||
|
||||
public function new(x:Float, y:Float, ?initDigit:Int = 0, ?styleData:FreeplayStyle)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
baseY = y;
|
||||
baseX = x;
|
||||
|
||||
if (styleData == null)
|
||||
{
|
||||
frames = Paths.getSparrowAtlas('digital_numbers');
|
||||
@@ -129,15 +114,22 @@ class ScoreNum extends FlxSprite
|
||||
|
||||
for (i in 0...10)
|
||||
{
|
||||
var stringNum:String = numToString[i];
|
||||
animation.addByPrefix(stringNum, '$stringNum DIGITAL', 24, false);
|
||||
animation.addByPrefix(getIntToString(i), '${getIntToString(i)} DIGITAL', 24, false);
|
||||
}
|
||||
|
||||
this.digit = initDigit ?? 0;
|
||||
|
||||
animation.play(numToString[digit], true);
|
||||
animation.play(getIntToString(digit), true);
|
||||
|
||||
setGraphicSize(Std.int(width * 0.4));
|
||||
updateHitbox();
|
||||
}
|
||||
|
||||
final numToString:Array<String> = ["ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"];
|
||||
|
||||
function getIntToString(number:Int):String
|
||||
{
|
||||
if (numToString[number] == null) return numToString[0];
|
||||
return numToString[number];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
var songs:Array<Null<FreeplaySongData>> = [];
|
||||
|
||||
var curSelected:Int = 0;
|
||||
// curSelectedFloat is used for mobile to get "inbetween" selections for swipe/scrolling/momentum stuff
|
||||
var curSelectedFloat:Float = 0;
|
||||
|
||||
/**
|
||||
@@ -158,6 +159,16 @@ class FreeplayState extends MusicBeatSubState
|
||||
return grpDifficulties.members.filter(d -> d.difficultyId == currentDifficulty)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Another utility var, this one gets our current selected capsule easily
|
||||
*/
|
||||
var currentCapsule(get, never):SongMenuItem;
|
||||
|
||||
function get_currentCapsule():SongMenuItem
|
||||
{
|
||||
return grpCapsules.members[curSelected];
|
||||
}
|
||||
|
||||
var coolColors:Array<Int> = [
|
||||
0xFF9271FD,
|
||||
0xFF9271FD,
|
||||
@@ -170,7 +181,6 @@ class FreeplayState extends MusicBeatSubState
|
||||
];
|
||||
|
||||
var grpCapsules:FlxTypedGroup<SongMenuItem>;
|
||||
var curPlaying:Bool = false;
|
||||
|
||||
var dj:Null<FreeplayDJ> = null;
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
@@ -187,8 +197,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
var letterSort:LetterSort;
|
||||
var exitMovers:ExitMoverData = new Map();
|
||||
|
||||
var diffSelLeft:Null<DifficultySelector> = null;
|
||||
var diffSelRight:Null<DifficultySelector> = null;
|
||||
var diffSelLeft:DifficultySelector;
|
||||
var diffSelRight:DifficultySelector;
|
||||
|
||||
var exitMoversCharSel:ExitMoverData = new Map();
|
||||
|
||||
@@ -215,8 +225,6 @@ class FreeplayState extends MusicBeatSubState
|
||||
*/
|
||||
public static var rememberedVariation:String = Constants.DEFAULT_VARIATION;
|
||||
|
||||
var allDifficulties:Array<String> = Constants.DEFAULT_DIFFICULTY_LIST_FULL;
|
||||
|
||||
public var funnyCam:FunkinCamera;
|
||||
|
||||
var rankCamera:FunkinCamera;
|
||||
@@ -261,11 +269,11 @@ class FreeplayState extends MusicBeatSubState
|
||||
};
|
||||
|
||||
currentCharacter = fetchPlayableCharacter();
|
||||
currentCharacterId = currentCharacter.getFreeplayStyleID();
|
||||
currentCharacterId = currentCharacter.id;
|
||||
|
||||
currentVariation = rememberedVariation;
|
||||
currentDifficulty = rememberedDifficulty;
|
||||
styleData = FreeplayStyleRegistry.instance.fetchEntry(currentCharacterId);
|
||||
styleData = FreeplayStyleRegistry.instance.fetchEntry(currentCharacter.getFreeplayStyleID());
|
||||
rememberedCharacterId = currentCharacter?.id ?? Constants.DEFAULT_CHARACTER;
|
||||
|
||||
fromCharSelect = params?.fromCharSelect ?? false;
|
||||
@@ -306,6 +314,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height);
|
||||
grpCapsules = new FlxTypedGroup<SongMenuItem>();
|
||||
grpDifficulties = new FlxTypedSpriteGroup<DifficultySprite>(-300, 80);
|
||||
|
||||
difficultyDots = new FlxTypedSpriteGroup<DifficultyDot>(203, 170);
|
||||
letterSort = new LetterSort((CUTOUT_WIDTH * SONGS_POS_MULTI) + 400, 75);
|
||||
rankBg = new FunkinSprite(0, 0);
|
||||
@@ -318,6 +327,10 @@ class FreeplayState extends MusicBeatSubState
|
||||
charSelectHint = new FlxText(-40, 18, FlxG.width - 8 - 8, 'Press [ LOL ] to change characters', 32);
|
||||
|
||||
backingImage = FunkinSprite.create(backingCard.pinkBack.width * 0.74, 0, styleData == null ? 'freeplay/freeplayBGweek1-bf' : styleData.getBgAssetKey());
|
||||
|
||||
// TODO: refactor DifficultySelector to *not* use `this` as input? Handle it's animations and style data in different manner
|
||||
diffSelLeft = new DifficultySelector((CUTOUT_WIDTH * DJ_POS_MULTI) + 20, grpDifficulties.y - 10, false, controls, styleData);
|
||||
diffSelRight = new DifficultySelector((CUTOUT_WIDTH * DJ_POS_MULTI) + 325, grpDifficulties.y - 10, true, controls, styleData);
|
||||
}
|
||||
|
||||
override function create():Void
|
||||
@@ -345,7 +358,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
#end
|
||||
|
||||
// Block input until the intro finishes.
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
|
||||
// Add a null entry that represents the RANDOM option
|
||||
songs.push(null);
|
||||
@@ -610,8 +623,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
// Reminder, this is a callback function being set, rather than these being called here in create()
|
||||
letterSort.changeSelectionCallback = (str) -> {
|
||||
var curSong:Null<FreeplaySongData> = grpCapsules.members[curSelected]?.freeplayData;
|
||||
grpCapsules.members[curSelected].selected = false;
|
||||
var curSong:Null<FreeplaySongData> = currentCapsule?.freeplayData;
|
||||
currentCapsule.selected = false;
|
||||
|
||||
switch (str)
|
||||
{
|
||||
@@ -652,20 +665,11 @@ class FreeplayState extends MusicBeatSubState
|
||||
wait: 0.1
|
||||
});
|
||||
|
||||
diffSelLeft = new DifficultySelector(this, (CUTOUT_WIDTH * DJ_POS_MULTI) + 20, grpDifficulties.y - 10, false, controls, styleData);
|
||||
diffSelRight = new DifficultySelector(this, (CUTOUT_WIDTH * DJ_POS_MULTI) + 325, grpDifficulties.y - 10, true, controls, styleData);
|
||||
diffSelLeft.visible = false;
|
||||
add(diffSelLeft);
|
||||
|
||||
if (diffSelLeft != null)
|
||||
{
|
||||
diffSelLeft.visible = false;
|
||||
add(diffSelLeft);
|
||||
}
|
||||
|
||||
if (diffSelRight != null)
|
||||
{
|
||||
diffSelRight.visible = false;
|
||||
add(diffSelRight);
|
||||
}
|
||||
diffSelRight.visible = false;
|
||||
add(diffSelRight);
|
||||
|
||||
// putting these here to fix the layering
|
||||
add(overhangStuff);
|
||||
@@ -681,14 +685,13 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
// be careful not to "add()" things in here unless it's to a group that's already added to the state
|
||||
// otherwise it won't be properly attatched to funnyCamera (relavent code should be at the bottom of create())
|
||||
var onDJIntroDone = function() {
|
||||
busy = false;
|
||||
var onDJIntroDone:Void->Void = function() {
|
||||
controls.active = true;
|
||||
|
||||
// when boyfriend hits dat shiii
|
||||
|
||||
albumRoll.playIntro();
|
||||
var daSong = grpCapsules.members[curSelected].freeplayData;
|
||||
albumRoll.albumId = daSong?.data.getAlbumId(currentDifficulty, currentVariation);
|
||||
albumRoll.albumId = currentCapsule.freeplayData?.data.getAlbumId(currentDifficulty, currentVariation);
|
||||
|
||||
if (!fromCharSelect)
|
||||
{
|
||||
@@ -711,30 +714,26 @@ class FreeplayState extends MusicBeatSubState
|
||||
FlxTween.cancelTweensOf(diff);
|
||||
FlxTween.tween(diff, {x: (CUTOUT_WIDTH * DJ_POS_MULTI) + 90}, 0.6, {ease: FlxEase.quartOut});
|
||||
diff.y = 80;
|
||||
final isCurrentDiff:Bool = diff == currentDifficultySprite;
|
||||
diff.visible = isCurrentDiff;
|
||||
diff.visible = diff == currentDifficultySprite;
|
||||
}
|
||||
FlxTween.tween(grpDifficulties, {x: (CUTOUT_WIDTH * DJ_POS_MULTI) + 90}, 0.6, {ease: FlxEase.quartOut});
|
||||
|
||||
if (diffSelLeft != null) diffSelLeft.visible = true;
|
||||
if (diffSelRight != null) diffSelRight.visible = true;
|
||||
diffSelLeft.visible = true;
|
||||
diffSelRight.visible = true;
|
||||
letterSort.visible = true;
|
||||
|
||||
if (diffSelLeft != null && diffSelRight != null)
|
||||
{
|
||||
exitMovers.set([diffSelLeft, diffSelRight],
|
||||
{
|
||||
x: -diffSelLeft.width * 2,
|
||||
speed: 0.26
|
||||
});
|
||||
exitMovers.set([diffSelLeft, diffSelRight],
|
||||
{
|
||||
x: -diffSelLeft.width * 2,
|
||||
speed: 0.26
|
||||
});
|
||||
|
||||
exitMoversCharSel.set([diffSelLeft, diffSelRight],
|
||||
{
|
||||
y: -270,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
}
|
||||
exitMoversCharSel.set([diffSelLeft, diffSelRight],
|
||||
{
|
||||
y: -270,
|
||||
speed: 0.8,
|
||||
wait: 0.1
|
||||
});
|
||||
|
||||
new FlxTimer().start(1 / 24, function(handShit) {
|
||||
fnfHighscoreSpr.visible = true;
|
||||
@@ -761,20 +760,19 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
if (prepForNewRank && fromResultsParams != null)
|
||||
{
|
||||
rankAnimStart(fromResultsParams, grpCapsules.members[curSelected]);
|
||||
rankAnimStart(fromResultsParams, currentCapsule);
|
||||
albumRoll.skipIntro();
|
||||
albumRoll.showStars();
|
||||
}
|
||||
|
||||
refreshDots(5, Constants.DEFAULT_DIFFICULTY_LIST_FULL.indexOf(currentDifficulty), Constants.DEFAULT_DIFFICULTY_LIST_FULL.indexOf(currentDifficulty));
|
||||
fadeDots(true);
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
SwipeUtil.calculateSwipeThreshold(grpCapsules.members, Y);
|
||||
FlxG.touches.swipeThreshold.x = 60;
|
||||
#end
|
||||
};
|
||||
|
||||
allDifficulties = SongRegistry.instance.listAllDifficulties(currentCharacterId);
|
||||
|
||||
// Generates song list with the starter params (who our current character is, last remembered difficulty, etc.)
|
||||
// Set this to false if you prefer the 50% transparency on the capsules when they first appear.
|
||||
generateSongList(null, true);
|
||||
@@ -895,15 +893,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
// Initialize the random capsule, with empty/blank info (which we display once bf/pico does his hand)
|
||||
var randomCapsule:SongMenuItem = grpCapsules.recycle(SongMenuItem);
|
||||
randomCapsule.initPosition(FlxG.width, 0);
|
||||
randomCapsule.initData(null, styleData, 1);
|
||||
randomCapsule.y = randomCapsule.intendedY(0) + 10;
|
||||
randomCapsule.targetPos.x = randomCapsule.x;
|
||||
randomCapsule.alpha = 0.5;
|
||||
randomCapsule.songText.visible = false;
|
||||
randomCapsule.favIcon.visible = false;
|
||||
randomCapsule.favIconBlurred.visible = false;
|
||||
randomCapsule.ranking.visible = false;
|
||||
randomCapsule.initRandom(styleData);
|
||||
randomCapsule.onConfirm = function() {
|
||||
capsuleOnConfirmRandom(randomCapsule);
|
||||
};
|
||||
@@ -1009,7 +999,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function rankAnimStart(fromResults:FromResultsParams, capsuleToRank:SongMenuItem):Void
|
||||
{
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
// We get the current selected capsule, in-case someone changes the song selection during a timer
|
||||
capsuleToRank.sparkle.alpha = 0;
|
||||
// capsuleToRank.forcePosition();
|
||||
@@ -1230,7 +1220,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
capsule.cameras = [funnyCam];
|
||||
|
||||
// NOW we can interact with the menu
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
capsule.sparkle.alpha = 0.7;
|
||||
playCurSongPreview(capsule);
|
||||
}, null);
|
||||
@@ -1306,7 +1296,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
{
|
||||
var distance:Int = 30;
|
||||
var shiftAmt:Float = (distance * amount) / 2;
|
||||
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].freeplayData;
|
||||
var daSong:Null<FreeplaySongData> = currentCapsule.freeplayData;
|
||||
|
||||
for (i in 0...difficultyDots.group.members.length)
|
||||
{
|
||||
@@ -1389,7 +1379,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
return;
|
||||
}
|
||||
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
|
||||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
|
||||
@@ -1408,8 +1398,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function transitionToCharSelect():Void
|
||||
{
|
||||
busy = true;
|
||||
var transitionGradient = new FlxSprite(0, 720).loadGraphic(Paths.image('freeplay/transitionGradient'));
|
||||
controls.active = false;
|
||||
var transitionGradient:FlxSprite = new FlxSprite(0, 720).loadGraphic(Paths.image('freeplay/transitionGradient'));
|
||||
transitionGradient.scale.set(1280, 1);
|
||||
transitionGradient.updateHitbox();
|
||||
transitionGradient.cameras = [rankCamera];
|
||||
@@ -1443,25 +1433,22 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
fadeShader.fade(1.0, 0.0, 0.8, {ease: FlxEase.quadIn});
|
||||
FlxG.sound.music?.fadeOut(0.9, 0);
|
||||
|
||||
// Passing the currrent Freeplay character to the CharSelect so we can start it with that character selected
|
||||
new FlxTimer().start(0.9, _ -> {
|
||||
FlxG.switchState(() ->
|
||||
new funkin.ui.charSelect.CharSelectSubState({character: currentCharacterId} // Passing the currrent Freeplay character to the CharSelect so we can start it with that character selected
|
||||
));
|
||||
FlxG.switchState(() -> new funkin.ui.charSelect.CharSelectSubState({character: currentCharacterId}));
|
||||
});
|
||||
|
||||
for (grpSpr in exitMoversCharSel.keys())
|
||||
{
|
||||
var moveData:Null<MoveData> = exitMoversCharSel.get(grpSpr);
|
||||
if (moveData == null) continue;
|
||||
if (exitMoversCharSel.get(grpSpr) == null) continue;
|
||||
|
||||
for (spr in grpSpr)
|
||||
{
|
||||
if (spr == null) continue;
|
||||
|
||||
var funnyMoveShit:MoveData = moveData;
|
||||
|
||||
var moveDataY = funnyMoveShit.y ?? spr.y;
|
||||
var moveDataSpeed = funnyMoveShit.speed ?? 0.2;
|
||||
var moveDataWait = funnyMoveShit.wait ?? 0.0;
|
||||
var moveDataY = exitMoversCharSel.get(grpSpr)?.y ?? spr.y;
|
||||
var moveDataSpeed = exitMoversCharSel.get(grpSpr)?.speed ?? 0.2;
|
||||
|
||||
FlxTween.tween(spr, {y: moveDataY + spr.y}, moveDataSpeed, {ease: FlxEase.backIn});
|
||||
}
|
||||
@@ -1471,7 +1458,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function enterFromCharSel():Void
|
||||
{
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
if (_parentState != null) _parentState.persistentDraw = false;
|
||||
|
||||
var transitionGradient = new FlxSprite(0, 720).loadGraphic(Paths.image('freeplay/transitionGradient'));
|
||||
@@ -1503,20 +1490,17 @@ class FreeplayState extends MusicBeatSubState
|
||||
fadeShader.fade(0.0, 1.0, 0.8, {ease: FlxEase.quadIn});
|
||||
for (grpSpr in exitMoversCharSel.keys())
|
||||
{
|
||||
var moveData:Null<MoveData> = exitMoversCharSel.get(grpSpr);
|
||||
if (moveData == null) continue;
|
||||
if (exitMoversCharSel.get(grpSpr) == null) continue;
|
||||
|
||||
for (spr in grpSpr)
|
||||
{
|
||||
if (spr == null) continue;
|
||||
|
||||
var funnyMoveShit:MoveData = moveData;
|
||||
|
||||
var moveDataY = funnyMoveShit.y ?? spr.y;
|
||||
var moveDataSpeed = funnyMoveShit.speed ?? 0.2;
|
||||
var moveDataWait = funnyMoveShit.wait ?? 0.0;
|
||||
var moveDataY = exitMoversCharSel.get(grpSpr)?.y ?? spr.y;
|
||||
var moveDataSpeed = exitMoversCharSel.get(grpSpr)?.speed ?? 0.2;
|
||||
|
||||
spr.y += moveDataY;
|
||||
|
||||
FlxTween.tween(spr, {y: spr.y - moveDataY}, moveDataSpeed * 1.2,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
@@ -1525,7 +1509,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
{
|
||||
capsule.doLerp = true;
|
||||
fromCharSelect = false;
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1536,11 +1520,6 @@ class FreeplayState extends MusicBeatSubState
|
||||
var spamTimer:Float = 0;
|
||||
var spamming:Bool = false;
|
||||
|
||||
/**
|
||||
* If true, disable interaction with the interface.
|
||||
*/
|
||||
public var busy:Bool = false;
|
||||
|
||||
var originalPos:FlxPoint = new FlxPoint();
|
||||
|
||||
var hintTimer:Float = 0;
|
||||
@@ -1556,12 +1535,12 @@ class FreeplayState extends MusicBeatSubState
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
if (backButton != null && !backTransitioning)
|
||||
{
|
||||
if (busy)
|
||||
if (!controls.active)
|
||||
{
|
||||
backButton.animation.play("idle");
|
||||
backButton.alpha = backButton.restingOpacity;
|
||||
}
|
||||
backButton.active = !busy;
|
||||
backButton.active = controls.active;
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -1591,34 +1570,37 @@ class FreeplayState extends MusicBeatSubState
|
||||
newRank: PERFECT_GOLD,
|
||||
songId: "tutorial",
|
||||
difficultyId: "hard"
|
||||
}, grpCapsules.members[curSelected]);
|
||||
}, currentCapsule);
|
||||
}
|
||||
#end // ^<-- FEATURE_DEBUG_FUNCTIONS
|
||||
|
||||
if ((controls.FREEPLAY_CHAR_SELECT #if FEATURE_TOUCH_CONTROLS
|
||||
|| (TouchUtil.pressAction(djHitbox, funnyCam, false) && !SwipeUtil.swipeAny) #end)
|
||||
&& !busy)
|
||||
&& controls.active)
|
||||
{
|
||||
tryOpenCharSelect();
|
||||
}
|
||||
|
||||
if (controls.FREEPLAY_FAVORITE && !busy)
|
||||
{
|
||||
favoriteSong();
|
||||
}
|
||||
if (controls.FREEPLAY_FAVORITE && controls.active) favoriteSong();
|
||||
|
||||
if (controls.FREEPLAY_JUMP_TO_TOP && !busy)
|
||||
{
|
||||
changeSelection(-curSelected);
|
||||
}
|
||||
if (controls.FREEPLAY_JUMP_TO_TOP && controls.active) changeSelection(-curSelected);
|
||||
|
||||
if (controls.FREEPLAY_JUMP_TO_BOTTOM && !busy)
|
||||
{
|
||||
changeSelection(grpCapsules.countLiving() - curSelected - 1);
|
||||
}
|
||||
if (controls.FREEPLAY_JUMP_TO_BOTTOM && controls.active) changeSelection(grpCapsules.countLiving() - curSelected - 1);
|
||||
|
||||
lerpScore = MathUtil.snap(MathUtil.smoothLerpPrecision(lerpScore, intendedScore, elapsed, 0.2), intendedScore, 1);
|
||||
lerpCompletion = MathUtil.snap(MathUtil.smoothLerpPrecision(lerpCompletion, intendedCompletion, elapsed, 0.5), intendedCompletion, 1 / 100);
|
||||
calculateCompletion();
|
||||
|
||||
handleInputs(elapsed);
|
||||
|
||||
if (dj != null) FlxG.watch.addQuick('dj-anim', dj.getCurrentAnimation());
|
||||
|
||||
// If the allowPicoBulletsVibration is true, trigger vibration each update (for pico shooting bullets animation).
|
||||
if (allowPicoBulletsVibration) HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
|
||||
}
|
||||
|
||||
function calculateCompletion():Void
|
||||
{
|
||||
lerpScore = MathUtil.snap(MathUtil.smoothLerpPrecision(lerpScore, intendedScore, FlxG.elapsed, 0.2), intendedScore, 1);
|
||||
lerpCompletion = MathUtil.snap(MathUtil.smoothLerpPrecision(lerpCompletion, intendedCompletion, FlxG.elapsed, 0.5), intendedCompletion, 1 / 100);
|
||||
|
||||
if (Math.isNaN(lerpScore))
|
||||
{
|
||||
@@ -1632,7 +1614,10 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
fp.updateScore(Std.int(lerpScore));
|
||||
|
||||
txtCompletion.text = '${Math.floor(lerpCompletion * 100)}';
|
||||
// sets the text of the completion percentage. Perhaps eventually we may want to generalize this,
|
||||
// but for now we can just clamp the values between 0 and 100.
|
||||
// Fixes issue where it rounds to negative integer overflow on Windows? Which occurs when switching to an unranked song?
|
||||
txtCompletion.text = '${FlxMath.clamp(Math.floor(lerpCompletion * 100), 0, 100)}';
|
||||
|
||||
// Right align the completion percentage
|
||||
switch (txtCompletion.text.length)
|
||||
@@ -1646,13 +1631,6 @@ class FreeplayState extends MusicBeatSubState
|
||||
default:
|
||||
txtCompletion.offset.x = 0;
|
||||
}
|
||||
|
||||
handleInputs(elapsed);
|
||||
|
||||
if (dj != null) FlxG.watch.addQuick('dj-anim', dj.getCurrentAnimation());
|
||||
|
||||
// If the allowPicoBulletsVibration is true, trigger vibration each update (for pico shooting bullets animation).
|
||||
if (allowPicoBulletsVibration) HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE / 3);
|
||||
}
|
||||
|
||||
var _dragOffset:Float = 0;
|
||||
@@ -1665,7 +1643,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function handleInputs(elapsed:Float):Void
|
||||
{
|
||||
if (busy) return;
|
||||
if (!controls.active) return;
|
||||
|
||||
final upP:Bool = controls.UI_UP_P;
|
||||
final downP:Bool = controls.UI_DOWN_P;
|
||||
@@ -1709,9 +1687,9 @@ class FreeplayState extends MusicBeatSubState
|
||||
goBack();
|
||||
}
|
||||
|
||||
if (accepted && !busy)
|
||||
if (accepted && controls.active)
|
||||
{
|
||||
grpCapsules.members[curSelected].onConfirm();
|
||||
currentCapsule.onConfirm();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1752,8 +1730,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
function handleDifficultySwitch():Void
|
||||
{
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
final leftPressed:Bool = controls.UI_LEFT_P || (diffSelLeft != null && TouchUtil.pressAction(diffSelLeft, funnyCam, false));
|
||||
final rightPressed:Bool = controls.UI_RIGHT_P || (diffSelRight != null && TouchUtil.pressAction(diffSelRight, funnyCam, false));
|
||||
final leftPressed:Bool = controls.UI_LEFT_P || TouchUtil.pressAction(diffSelLeft, funnyCam, false);
|
||||
final rightPressed:Bool = controls.UI_RIGHT_P || TouchUtil.pressAction(diffSelRight, funnyCam, false);
|
||||
#else
|
||||
final leftPressed:Bool = controls.UI_LEFT_P;
|
||||
final rightPressed:Bool = controls.UI_RIGHT_P;
|
||||
@@ -1777,12 +1755,12 @@ class FreeplayState extends MusicBeatSubState
|
||||
function handleDebugKeys():Void
|
||||
{
|
||||
#if FEATURE_CHART_EDITOR
|
||||
if (busy) return;
|
||||
if (!controls.active) return;
|
||||
if (!controls.DEBUG_CHART) return;
|
||||
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
|
||||
var targetSongID = grpCapsules.members[curSelected]?.freeplayData?.data.id ?? 'unknown';
|
||||
var targetSongID = currentCapsule?.freeplayData?.data.id ?? 'unknown';
|
||||
if (targetSongID == 'unknown')
|
||||
{
|
||||
letterSort.inputEnabled = false;
|
||||
@@ -1799,7 +1777,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (availableSongCapsules.length == 0)
|
||||
{
|
||||
trace('No songs available!');
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
letterSort.inputEnabled = true;
|
||||
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||
return;
|
||||
@@ -1810,7 +1788,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
// Seeing if I can do an animation...
|
||||
curSelected = grpCapsules.members.indexOf(targetSong);
|
||||
changeSelection(0);
|
||||
targetSongID = grpCapsules.members[curSelected]?.freeplayData?.data.id ?? 'unknown';
|
||||
targetSongID = currentCapsule?.freeplayData?.data.id ?? 'unknown';
|
||||
}
|
||||
// Play the confirm animation so the user knows they actually did something.
|
||||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
@@ -1852,7 +1830,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
curSelected = i;
|
||||
changeSelection(0);
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1860,7 +1838,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
if (TouchUtil.justPressed)
|
||||
{
|
||||
final selected = grpCapsules.members[curSelected].theActualHitbox;
|
||||
final selected = currentCapsule.theActualHitbox;
|
||||
_pressedOnSelected = selected != null && TouchUtil.overlaps(selected, funnyCam);
|
||||
}
|
||||
}
|
||||
@@ -1868,7 +1846,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
function handleTouchSelectionScroll(elapsed:Float):Void
|
||||
{
|
||||
if (draggingDifficulty || ControlsHandler.usingExternalInputDevice) return;
|
||||
if (TouchUtil.pressAction(grpCapsules.members[curSelected].theActualHitbox, funnyCam)) return;
|
||||
if (TouchUtil.pressAction(currentCapsule.theActualHitbox, funnyCam)) return;
|
||||
|
||||
if (TouchUtil.justPressed && TouchUtil.overlaps(capsuleHitbox, funnyCam))
|
||||
{
|
||||
@@ -1966,7 +1944,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
_flickEnded = true;
|
||||
|
||||
new FlxTimer().start(0.21, (afteranim) -> {
|
||||
grpCapsules.members[curSelected].doLerp = true;
|
||||
currentCapsule.doLerp = true;
|
||||
generateSongList(currentFilter, true, false, true);
|
||||
FlxG.touches.flickManager.destroy();
|
||||
});
|
||||
@@ -1985,7 +1963,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
_flickEnded = true;
|
||||
|
||||
new FlxTimer().start(0.21, (afteranim) -> {
|
||||
grpCapsules.members[curSelected].doLerp = true;
|
||||
currentCapsule.doLerp = true;
|
||||
generateSongList(currentFilter, true, false, true);
|
||||
FlxG.touches.flickManager.destroy();
|
||||
});
|
||||
@@ -2004,16 +1982,16 @@ class FreeplayState extends MusicBeatSubState
|
||||
}
|
||||
else
|
||||
{
|
||||
grpCapsules.members[curSelected].doLerp = true;
|
||||
currentCapsule.doLerp = true;
|
||||
}
|
||||
|
||||
if (busy) return;
|
||||
if (!controls.active) return;
|
||||
if (currentDifficultySprite == null) return;
|
||||
|
||||
// If we press onto our difficulty, we want to "grab" it rather than simply check if we are overlapping
|
||||
if (TouchUtil.overlapsComplex(currentDifficultySprite, funnyCam) && TouchUtil.justPressed && !draggingDifficulty)
|
||||
{
|
||||
HapticUtil.vibrate(0, 0.01, 0.15, 0.4);
|
||||
HapticUtil.vibrate(0, 0.01, 0.375, 0.4);
|
||||
draggingDifficulty = true;
|
||||
}
|
||||
|
||||
@@ -2026,7 +2004,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
var vibDist:Float = 5; // essentially how far the touch needs to be before it will trigger a tiny haptic feel
|
||||
if (Std.int((TouchUtil.touch.x - _dragOffset) / vibDist) * vibDist != _prevRoundedDragOffset)
|
||||
{
|
||||
HapticUtil.vibrate(0, 0.01, 0.08, 0.8);
|
||||
HapticUtil.vibrate(0, 0.01, 0.2, 0.8);
|
||||
}
|
||||
_prevRoundedDragOffset = Std.int((TouchUtil.touch.x - _dragOffset) / vibDist) * vibDist;
|
||||
|
||||
@@ -2056,8 +2034,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
currentDifficultySprite.offset.x = MathUtil.smoothLerpPrecision(currentDifficultySprite.offset.x, 0, FlxG.elapsed, 0.4);
|
||||
}
|
||||
|
||||
if (diffSelRight != null) diffSelRight.setPress(TouchUtil.overlaps(diffSelRight, funnyCam) && TouchUtil.justPressed);
|
||||
if (diffSelLeft != null) diffSelLeft.setPress(TouchUtil.overlaps(diffSelLeft, funnyCam) && TouchUtil.justPressed);
|
||||
diffSelRight.setPress(TouchUtil.overlaps(diffSelRight, funnyCam) && TouchUtil.justPressed);
|
||||
diffSelLeft.setPress(TouchUtil.overlaps(diffSelLeft, funnyCam) && TouchUtil.justPressed);
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -2077,7 +2055,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function goBack():Void
|
||||
{
|
||||
if (busy) return;
|
||||
if (!controls.active) return;
|
||||
backTransitioning = true;
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
if (backButton != null)
|
||||
@@ -2086,7 +2064,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
backButton.animation.play("confirm");
|
||||
}
|
||||
#end
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
FlxTween.globalManager.clear();
|
||||
FlxTimer.globalManager.clear();
|
||||
if (dj != null) dj.onIntroDone.removeAll();
|
||||
@@ -2201,18 +2179,18 @@ class FreeplayState extends MusicBeatSubState
|
||||
*/
|
||||
function changeDiff(change:Int = 0, force:Bool = false, capsuleAnim:Bool = false):Void
|
||||
{
|
||||
if (busy) return;
|
||||
if (!controls.active) return;
|
||||
|
||||
if (capsuleAnim)
|
||||
{
|
||||
if (grpCapsules.members[curSelected] != null)
|
||||
if (currentCapsule != null)
|
||||
{
|
||||
busy = true;
|
||||
grpCapsules.members[curSelected].doLerp = false;
|
||||
controls.active = false;
|
||||
currentCapsule.doLerp = false;
|
||||
|
||||
var movement:Float = (change > 0) ? 15 : -15;
|
||||
FlxTween.tween(grpCapsules.members[curSelected], {x: grpCapsules.members[curSelected].x - movement}, 0.1, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(grpCapsules.members[curSelected], {x: grpCapsules.members[curSelected].x + movement}, 0.1, {ease: FlxEase.expoIn, startDelay: 0.1});
|
||||
FlxTween.tween(currentCapsule, {x: currentCapsule.x - movement}, 0.1, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(currentCapsule, {x: currentCapsule.x + movement}, 0.1, {ease: FlxEase.expoIn, startDelay: 0.1});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2224,12 +2202,12 @@ class FreeplayState extends MusicBeatSubState
|
||||
diff.visible = true;
|
||||
final newX:Int = (change > 0) ? -320 : 500;
|
||||
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
FlxTween.tween(diff, {x: newX + (CUTOUT_WIDTH * DJ_POS_MULTI)}, 0.2,
|
||||
{
|
||||
ease: FlxEase.circInOut,
|
||||
onComplete: function(_) {
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
diff.x = 90 + (CUTOUT_WIDTH * DJ_POS_MULTI);
|
||||
diff.visible = false;
|
||||
}
|
||||
@@ -2238,18 +2216,18 @@ class FreeplayState extends MusicBeatSubState
|
||||
}
|
||||
if (change != 0)
|
||||
{
|
||||
HapticUtil.vibrate(0, 0.01, 0.2, 0.1);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5, 0.1);
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
}
|
||||
|
||||
var previousVariation:String = currentVariation;
|
||||
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].freeplayData;
|
||||
grpCapsules.members[curSelected].selected = false;
|
||||
var daSong:Null<FreeplaySongData> = currentCapsule.freeplayData;
|
||||
currentCapsule.selected = false;
|
||||
|
||||
// Available variations for current character. We get this since bf is usually `default` variation, and `pico` is `pico`
|
||||
// but sometimes pico can be the default variation (weekend 1 songs), and bf can be `bf` variation (darnell)
|
||||
var characterVariations:Array<String> = daSong?.data.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST;
|
||||
var difficultiesAvailable:Array<String> = allDifficulties;
|
||||
var difficultiesAvailable:Array<String> = SongRegistry.instance.listAllDifficulties(currentCharacterId) ?? Constants.DEFAULT_DIFFICULTY_LIST_FULL;
|
||||
// Gets all available difficulties for our character, via our available variations
|
||||
var songDifficulties:Array<String> = daSong?.data.listDifficulties(null, characterVariations) ?? Constants.DEFAULT_DIFFICULTY_LIST;
|
||||
|
||||
@@ -2267,9 +2245,13 @@ class FreeplayState extends MusicBeatSubState
|
||||
// For when we change the difficulty, but the song doesn't have that difficulty!
|
||||
if (daSong != null && !songDifficulties.contains(difficultiesAvailable[currentDifficultyIndex]))
|
||||
{
|
||||
// Switch to the closest song with that difficulty.
|
||||
curSelected = findClosestDiff(characterVariations, difficultiesAvailable[currentDifficultyIndex]);
|
||||
daSong = grpCapsules.members[curSelected].freeplayData;
|
||||
daSong = currentCapsule.freeplayData;
|
||||
rememberedSongId = daSong?.data.id;
|
||||
|
||||
// Update the variation list for the new song.
|
||||
characterVariations = daSong?.data.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST;
|
||||
}
|
||||
|
||||
for (variation in characterVariations)
|
||||
@@ -2297,7 +2279,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
((songScore.tallies.sick + songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes));
|
||||
rememberedDifficulty = currentDifficulty;
|
||||
if (!capsuleAnim) generateSongList(currentFilter, false, true, true);
|
||||
grpCapsules.members[curSelected].refreshDisplay((prepForNewRank == true) ? false : true);
|
||||
currentCapsule.refreshDisplay((prepForNewRank == true) ? false : true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2378,7 +2360,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
// Set difficulty star count.
|
||||
albumRoll.setDifficultyStars(daSong?.data.getDifficulty(currentDifficulty, currentVariation)?.difficultyRating ?? 0);
|
||||
|
||||
grpCapsules.members[curSelected].selected = true; // set selected again, so it can run its getter function to initialize movement
|
||||
currentCapsule.selected = true; // set selected again, so it can run its getter function to initialize movement
|
||||
}
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
@@ -2393,7 +2375,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function handleDiffBoundaryChange(change:Int):Void
|
||||
{
|
||||
if (busy) return;
|
||||
if (!controls.active) return;
|
||||
dj?.resetAFKTimer();
|
||||
changeDiff(change);
|
||||
generateSongList(currentFilter, true, false);
|
||||
@@ -2408,7 +2390,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
{
|
||||
trace('RANDOM SELECTED');
|
||||
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
#if NO_FEATURE_TOUCH_CONTROLS
|
||||
letterSort.inputEnabled = false;
|
||||
#end
|
||||
@@ -2425,7 +2407,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (availableSongCapsules.length == 0)
|
||||
{
|
||||
trace('No songs available!');
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
#if NO_FEATURE_TOUCH_CONTROLS
|
||||
letterSort.inputEnabled = true;
|
||||
#end
|
||||
@@ -2448,14 +2430,14 @@ class FreeplayState extends MusicBeatSubState
|
||||
*/
|
||||
function capsuleOnOpenDefault(cap:SongMenuItem):Void
|
||||
{
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
letterSort.inputEnabled = false;
|
||||
var targetSongId:String = cap?.freeplayData?.data.id ?? 'unknown';
|
||||
var targetSongNullable:Null<Song> = SongRegistry.instance.fetchEntry(targetSongId);
|
||||
if (targetSongNullable == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find song with id (${targetSongId})');
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
@@ -2470,7 +2452,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (targetDifficulty == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find difficulty with id (${targetDifficultyId})');
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
@@ -2521,7 +2503,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
public function cleanupCapsuleOptionsMenu():Void
|
||||
{
|
||||
this.busy = false;
|
||||
this.controls.active = true;
|
||||
letterSort.inputEnabled = true;
|
||||
|
||||
if (capsuleOptionsMenu != null)
|
||||
@@ -2536,7 +2518,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
*/
|
||||
function capsuleOnConfirmDefault(cap:SongMenuItem, ?targetInstId:String):Void
|
||||
{
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
#if NO_FEATURE_TOUCH_CONTROLS
|
||||
letterSort.inputEnabled = false;
|
||||
#end
|
||||
@@ -2548,7 +2530,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (targetSongNullable == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find song with id (${targetSongId})');
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
@@ -2561,7 +2543,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (targetDifficulty == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find difficulty with id (${currentDifficulty})');
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
@@ -2576,8 +2558,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
if (dj != null) dj.confirm();
|
||||
|
||||
grpCapsules.members[curSelected].forcePosition();
|
||||
grpCapsules.members[curSelected].confirm();
|
||||
currentCapsule.forcePosition();
|
||||
currentCapsule.confirm();
|
||||
|
||||
backingCard.confirm();
|
||||
fadeDots(false);
|
||||
@@ -2597,7 +2579,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
// A single vibration.
|
||||
default:
|
||||
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD, Constants.DEFAULT_VIBRATION_DURATION * 5, Constants.MAX_VIBRATION_AMPLITUDE / 3);
|
||||
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD, Constants.DEFAULT_VIBRATION_DURATION * 5, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2677,7 +2659,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (curSelected != prevSelected)
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
dj?.resetAFKTimer();
|
||||
_pressedOnSelected = false;
|
||||
}
|
||||
@@ -2711,7 +2693,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
if (!prepForNewRank && curSelected != prevSelected) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
|
||||
var daSongCapsule:SongMenuItem = grpCapsules.members[curSelected];
|
||||
var daSongCapsule:SongMenuItem = currentCapsule;
|
||||
if (daSongCapsule.freeplayData != null)
|
||||
{
|
||||
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSongCapsule.freeplayData.data.id, currentDifficulty, currentVariation);
|
||||
@@ -2747,21 +2729,21 @@ class FreeplayState extends MusicBeatSubState
|
||||
&& ControlsHandler.usingExternalInputDevice #end) capsule.targetPos.y -= 100; // another 100 for good measure
|
||||
}
|
||||
|
||||
if (grpCapsules.countLiving() > 0 && !prepForNewRank && !busy)
|
||||
if (grpCapsules.countLiving() > 0 && !prepForNewRank && controls.active)
|
||||
{
|
||||
playCurSongPreview(daSongCapsule);
|
||||
grpCapsules.members[curSelected].selected = true;
|
||||
currentCapsule.selected = true;
|
||||
|
||||
// switchBackingImage(daSongCapsule.freeplayData);
|
||||
}
|
||||
|
||||
// Small vibrations every selection change.
|
||||
if (change != 0) HapticUtil.vibrate(0, 0.01, 0.2);
|
||||
if (change != 0) HapticUtil.vibrate(0, 0.01, 0.5);
|
||||
}
|
||||
|
||||
public function playCurSongPreview(?daSongCapsule:SongMenuItem):Void
|
||||
{
|
||||
if (daSongCapsule == null) daSongCapsule = grpCapsules.members[curSelected];
|
||||
if (daSongCapsule == null) daSongCapsule = currentCapsule;
|
||||
if (curSelected == 0)
|
||||
{
|
||||
FunkinSound.playMusic('freeplayRandom',
|
||||
@@ -2843,7 +2825,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function favoriteSong():Void
|
||||
{
|
||||
var targetSong = grpCapsules.members[curSelected]?.freeplayData;
|
||||
var targetSong = currentCapsule?.freeplayData;
|
||||
if (targetSong != null)
|
||||
{
|
||||
var realShit:Int = curSelected;
|
||||
@@ -2857,7 +2839,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
FunkinSound.playOnce(Paths.sound('fav'), 1);
|
||||
grpCapsules.members[realShit].checkClip();
|
||||
grpCapsules.members[realShit].selected = true; // set selected again, so it can run its getter function to initialize movement
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
|
||||
grpCapsules.members[realShit].doLerp = false;
|
||||
FlxTween.tween(grpCapsules.members[realShit], {y: grpCapsules.members[realShit].y - 5}, 0.1, {ease: FlxEase.expoOut});
|
||||
@@ -2868,7 +2850,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
startDelay: 0.1,
|
||||
onComplete: function(_) {
|
||||
grpCapsules.members[realShit].doLerp = true;
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2884,7 +2866,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
grpCapsules.members[realShit].selected = true; // set selected again, so it can run its getter function to initialize movement
|
||||
});
|
||||
|
||||
busy = true;
|
||||
controls.active = false;
|
||||
grpCapsules.members[realShit].doLerp = false;
|
||||
FlxTween.tween(grpCapsules.members[realShit], {y: grpCapsules.members[realShit].y + 5}, 0.1, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(grpCapsules.members[realShit], {y: grpCapsules.members[realShit].y - 5}, 0.1,
|
||||
@@ -2893,7 +2875,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
startDelay: 0.1,
|
||||
onComplete: function(_) {
|
||||
grpCapsules.members[realShit].doLerp = true;
|
||||
busy = false;
|
||||
controls.active = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2910,34 +2892,30 @@ class DifficultySelector extends FlxSprite
|
||||
var controls:Controls;
|
||||
var whiteShader:PureColor;
|
||||
|
||||
var parent:FreeplayState;
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
public var pressed:Bool = false;
|
||||
var pressed:Bool = false;
|
||||
#end
|
||||
|
||||
public function new(parent:FreeplayState, x:Float, y:Float, flipped:Bool, controls:Controls, ?styleData:FreeplayStyle = null)
|
||||
public function new(x:Float, y:Float, flipped:Bool, controls:Controls, ?styleData:FreeplayStyle)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
this.parent = parent;
|
||||
this.controls = controls;
|
||||
whiteShader = new PureColor(FlxColor.WHITE);
|
||||
this.whiteShader = new PureColor(FlxColor.WHITE);
|
||||
|
||||
this.frames = Paths.getSparrowAtlas(styleData?.getSelectorAssetKey() ?? "freeplay/freeplaySelector");
|
||||
animation.addByPrefix('shine', 'arrow pointer loop', 24);
|
||||
animation.play('shine');
|
||||
|
||||
this.shader = whiteShader;
|
||||
|
||||
this.flipX = flipped;
|
||||
}
|
||||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
if (flipX && controls.UI_RIGHT_P && !parent.busy) moveShitDown();
|
||||
if (!flipX && controls.UI_LEFT_P && !parent.busy) moveShitDown();
|
||||
if (!controls.active) return;
|
||||
|
||||
if (flipX && controls.UI_RIGHT_P) moveShitDown();
|
||||
if (!flipX && controls.UI_LEFT_P) moveShitDown();
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ class LetterSort extends FlxSpriteGroup
|
||||
new FlxTimer().start(2 / 24, function(_) {
|
||||
arrowToMove.offset.x = 0;
|
||||
});
|
||||
if (playSound) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
if (playSound && diff != 0) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,7 +40,8 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
public var selected(default, set):Bool;
|
||||
public var forceHighlight(default, set):Bool;
|
||||
|
||||
public var songText:CapsuleText;
|
||||
var songText:CapsuleText;
|
||||
|
||||
public var favIconBlurred:FlxSprite;
|
||||
public var favIcon:FlxSprite;
|
||||
|
||||
@@ -541,6 +542,19 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
checkWeek(freeplayData?.data.id);
|
||||
}
|
||||
|
||||
public function initRandom(?styleData:FreeplayStyle = null):Void
|
||||
{
|
||||
initPosition(FlxG.width, 0);
|
||||
initData(null, styleData, 1);
|
||||
y = intendedY(0) + 10;
|
||||
targetPos.x = x;
|
||||
alpha = 0.5;
|
||||
songText.visible = false;
|
||||
favIcon.visible = false;
|
||||
favIconBlurred.visible = false;
|
||||
ranking.visible = false;
|
||||
}
|
||||
|
||||
var frameInTicker:Float = 0;
|
||||
var frameInTypeBeat:Int = 0;
|
||||
|
||||
|
||||
@@ -58,7 +58,20 @@ class MainMenuState extends MusicBeatState
|
||||
var overrideMusic:Bool = false;
|
||||
var goingToOptions:Bool = false;
|
||||
var goingBack:Bool = false;
|
||||
var canInteract:Bool = false;
|
||||
var canInteract(get, set):Bool;
|
||||
var _canInteract:Bool = false;
|
||||
|
||||
function get_canInteract():Bool
|
||||
{
|
||||
return _canInteract;
|
||||
}
|
||||
|
||||
function set_canInteract(value:Bool):Bool
|
||||
{
|
||||
_canInteract = value;
|
||||
trace('canInteract set to: ' + value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static var rememberedSelectedIndex:Int = 0;
|
||||
|
||||
@@ -88,7 +101,10 @@ class MainMenuState extends MusicBeatState
|
||||
transOut = FlxTransitionableState.defaultTransOut;
|
||||
|
||||
#if FEATURE_MOBILE_IAP
|
||||
hasUpgraded = InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID);
|
||||
trace("hasInitialized: " + InAppPurchasesUtil.hasInitialized);
|
||||
if (InAppPurchasesUtil.hasInitialized) Preferences.noAds = InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID);
|
||||
// If the user is faster than their shit wifi, it gets the saved noAds instead.
|
||||
hasUpgraded = Preferences.noAds;
|
||||
#else
|
||||
// just to make sure its never accidentally turned off
|
||||
hasUpgraded = true;
|
||||
@@ -126,7 +142,7 @@ class MainMenuState extends MusicBeatState
|
||||
add(menuItems);
|
||||
menuItems.onChange.add(onMenuItemChange);
|
||||
menuItems.onAcceptPress.add(function(_) {
|
||||
canInteract = false;
|
||||
// canInteract = false;
|
||||
FlxFlicker.flicker(magenta, 1.1, 0.15, false, true);
|
||||
});
|
||||
|
||||
@@ -194,7 +210,8 @@ class MainMenuState extends MusicBeatState
|
||||
});
|
||||
}
|
||||
|
||||
if (#if mobile ControlsHandler.usingExternalInputDevice #else true #end) {
|
||||
if (#if mobile ControlsHandler.usingExternalInputDevice #else true #end)
|
||||
{
|
||||
createMenuItem('options', 'mainmenu/options', function() {
|
||||
startExitState(() -> new funkin.ui.options.OptionsState());
|
||||
});
|
||||
@@ -275,8 +292,9 @@ class MainMenuState extends MusicBeatState
|
||||
if (!ControlsHandler.usingExternalInputDevice)
|
||||
{
|
||||
addOptionsButton(35, FlxG.height - 210, function() {
|
||||
if (!canInteract) return;
|
||||
canInteract = false;
|
||||
if (!canInteract || menuItems != null && menuItems.busy) return;
|
||||
|
||||
trace("OPTIONS: Interact complete.");
|
||||
startExitState(() -> new funkin.ui.options.OptionsState());
|
||||
});
|
||||
}
|
||||
@@ -285,9 +303,9 @@ class MainMenuState extends MusicBeatState
|
||||
{
|
||||
backButton.onConfirmStart.add(function():Void {
|
||||
if (backButton == null) return;
|
||||
backButton.active = true;
|
||||
goingBack = true;
|
||||
if (menuItems != null) menuItems.enabled = false;
|
||||
trace('BACK: Interact Start');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -295,9 +313,9 @@ class MainMenuState extends MusicBeatState
|
||||
{
|
||||
optionsButton.onConfirmStart.add(function():Void {
|
||||
if (optionsButton == null) return;
|
||||
optionsButton.active = true;
|
||||
goingToOptions = true;
|
||||
if (menuItems != null) menuItems.enabled = false;
|
||||
trace('OPTIONS: Interact Start');
|
||||
});
|
||||
}
|
||||
#end
|
||||
@@ -380,18 +398,24 @@ class MainMenuState extends MusicBeatState
|
||||
}
|
||||
#end
|
||||
super.closeSubState();
|
||||
canInteract = true;
|
||||
}
|
||||
|
||||
override function finishTransIn():Void
|
||||
{
|
||||
super.finishTransIn();
|
||||
canInteract = true;
|
||||
if (menuItems != null)
|
||||
{
|
||||
menuItems.busy = false;
|
||||
menuItems.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
function onMenuItemChange(selected:MenuListItem)
|
||||
{
|
||||
if (#if mobile ControlsHandler.usingExternalInputDevice #else true #end)
|
||||
camFollow.setPosition(selected.getGraphicMidpoint().x, selected.getGraphicMidpoint().y);
|
||||
if (#if mobile ControlsHandler.usingExternalInputDevice #else true #end) camFollow.setPosition(selected.getGraphicMidpoint().x,
|
||||
selected.getGraphicMidpoint().y);
|
||||
}
|
||||
|
||||
#if FEATURE_OPEN_URL
|
||||
@@ -422,6 +446,11 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
function startExitState(state:NextState):Void
|
||||
{
|
||||
#if mobile
|
||||
// This just softlocks the menu items and prevents any further interaction.. needs testing with keyboard.
|
||||
if (!canInteract && !ControlsHandler.usingExternalInputDevice) return;
|
||||
#end
|
||||
|
||||
if (menuItems != null)
|
||||
{
|
||||
menuItems.enabled = false; // disable for exit
|
||||
@@ -441,13 +470,15 @@ class MainMenuState extends MusicBeatState
|
||||
});
|
||||
|
||||
#if mobile
|
||||
if (optionsButton != null)
|
||||
FlxTween.tween(optionsButton, {alpha: 0}, duration, {ease: FlxEase.quadOut});
|
||||
if (backButton != null)
|
||||
FlxTween.tween(backButton, {alpha: 0}, duration, {ease: FlxEase.quadOut});
|
||||
if (optionsButton != null) FlxTween.tween(optionsButton, {alpha: 0}, duration, {ease: FlxEase.quadOut});
|
||||
if (backButton != null) FlxTween.tween(backButton, {alpha: 0}, duration, {ease: FlxEase.quadOut});
|
||||
#end
|
||||
|
||||
new FlxTimer().start(duration, function(_) FlxG.switchState(state));
|
||||
new FlxTimer().start(duration, function(_) {
|
||||
trace('Exiting MainMenuState...');
|
||||
FlxG.switchState(state);
|
||||
canInteract = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,8 +511,8 @@ class MainMenuState extends MusicBeatState
|
||||
if (menuItems != null)
|
||||
{
|
||||
#if mobile
|
||||
if (optionsButton != null) optionsButton.active = canInteract && (!menuItems.busy && !goingBack);
|
||||
if (backButton != null) backButton.active = canInteract && (!menuItems.busy && !goingToOptions);
|
||||
// if (optionsButton != null) optionsButton.active = canInteract && (!menuItems.busy && !goingBack);
|
||||
// if (backButton != null) backButton.active = canInteract && (!menuItems.busy && !goingToOptions);
|
||||
#end
|
||||
if (_exiting) menuItems.enabled = false;
|
||||
}
|
||||
@@ -610,14 +641,13 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
public function goBack():Void
|
||||
{
|
||||
if (canInteract)
|
||||
if (menuItems == null) return;
|
||||
if (canInteract && !menuItems.busy)
|
||||
{
|
||||
trace("BACK: Interact complete.");
|
||||
canInteract = false;
|
||||
if (menuItems != null)
|
||||
{
|
||||
menuItems.busy = true;
|
||||
rememberedSelectedIndex = menuItems.selectedIndex;
|
||||
}
|
||||
menuItems.busy = true;
|
||||
rememberedSelectedIndex = menuItems.selectedIndex;
|
||||
FlxG.switchState(() -> new TitleState());
|
||||
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ import funkin.ui.AtlasText;
|
||||
import funkin.ui.MenuList.MenuTypedList;
|
||||
import funkin.ui.TextMenuList;
|
||||
import funkin.ui.Page;
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
import funkin.mobile.ui.FunkinBackButton;
|
||||
#end
|
||||
|
||||
class ControlsMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
{
|
||||
@@ -191,6 +194,20 @@ class ControlsMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
popup.back.scrollFactor.set(0, 0);
|
||||
popup.exists = false;
|
||||
add(popup);
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
var backButton:FunkinBackButton = new FunkinBackButton(FlxG.width - 230, FlxG.height - 200, function():Void {
|
||||
if (controlGrid.enabled && deviceList != null && deviceListSelected == false)
|
||||
{
|
||||
goToDeviceList();
|
||||
}
|
||||
else if (canExit)
|
||||
{
|
||||
exit();
|
||||
}
|
||||
}, 1.0);
|
||||
add(backButton);
|
||||
#end
|
||||
}
|
||||
|
||||
function createItem(x = 0.0, y = 0.0, control:Control, index:Int)
|
||||
|
||||
@@ -7,6 +7,7 @@ import funkin.mobile.ui.FunkinBackButton;
|
||||
#if mobile
|
||||
import funkin.mobile.ui.FunkinHitbox;
|
||||
import funkin.mobile.ui.FunkinHitbox.FunkinHitboxControlSchemes;
|
||||
import funkin.mobile.input.ControlsHandler;
|
||||
import funkin.util.TouchUtil;
|
||||
#end
|
||||
import funkin.input.PreciseInputManager;
|
||||
@@ -241,23 +242,6 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
|
||||
testStrumline.cameras = [menuCamera];
|
||||
|
||||
#if mobile
|
||||
if (Preferences.controlsScheme == FunkinHitboxControlSchemes.Arrows)
|
||||
{
|
||||
final amplification:Float = (FlxG.width / FlxG.height) / (FlxG.initialWidth / FlxG.initialHeight);
|
||||
final playerStrumlineScale:Float = ((FlxG.height / FlxG.width) * 1.95) * amplification;
|
||||
final playerNoteSpacing:Float = ((FlxG.height / FlxG.width) * 2.8) * amplification;
|
||||
|
||||
testStrumline.strumlineScale.set(playerStrumlineScale, playerStrumlineScale);
|
||||
testStrumline.setNoteSpacing(playerNoteSpacing);
|
||||
testStrumline.width *= 2;
|
||||
|
||||
testStrumline.x = (FlxG.width - testStrumline.width) / 2 + Constants.STRUMLINE_X_OFFSET;
|
||||
testStrumline.y = (FlxG.height - testStrumline.height) * 0.95 - Constants.STRUMLINE_Y_OFFSET;
|
||||
testStrumline.y -= 10;
|
||||
}
|
||||
#end
|
||||
|
||||
testStrumline.conductorInUse = localConductor;
|
||||
testStrumline.zIndex = 1001;
|
||||
for (strum in testStrumline)
|
||||
@@ -321,6 +305,9 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
|
||||
calibrating = true;
|
||||
MenuTypedList.pauseInput = true;
|
||||
OptionsState.instance.drumsBG.pause();
|
||||
OptionsState.instance.drumsBG.time = FlxG.sound.music.time;
|
||||
OptionsState.instance.drumsBG.resume();
|
||||
OptionsState.instance.drumsBG.fadeIn(1, 0, 1);
|
||||
canExit = false;
|
||||
differences = [];
|
||||
@@ -345,30 +332,79 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
testStrumline.noteData = [];
|
||||
testStrumline.nextNoteIndex = 0;
|
||||
|
||||
OptionsState.instance.drumsBG.pause();
|
||||
OptionsState.instance.drumsBG.time = FlxG.sound.music.time;
|
||||
OptionsState.instance.drumsBG.resume();
|
||||
localConductor.update(FlxG.sound.music.time, true);
|
||||
|
||||
var floored = Math.floor(localConductor.currentBeatTime);
|
||||
arrowBeat = floored - (floored % 4);
|
||||
arrowBeat += 4;
|
||||
_lastDirection = 0;
|
||||
|
||||
if (arrowBeat - localConductor.currentBeatTime < 4) arrowBeat += 4;
|
||||
var diffBeats = Math.floor(arrowBeat - localConductor.currentBeatTime);
|
||||
|
||||
trace('Testing strumline at beat: ' + arrowBeat);
|
||||
trace('Prestart arrowBeat: ' + arrowBeat);
|
||||
|
||||
if (diffBeats < 4) arrowBeat += (arrowBeat % 4) + 4; // Ensure we have at least 4 beats to test.
|
||||
|
||||
trace('Testing strumline at beat: ' + arrowBeat + ' diff: ' + diffBeats);
|
||||
|
||||
jumpInText.text = 'Hit the notes as they come in!';
|
||||
#if mobile
|
||||
if (OptionsState.instance.hitbox != null) OptionsState.instance.hitbox.visible = true;
|
||||
if (!ControlsHandler.usingExternalInputDevice)
|
||||
{
|
||||
final amplification:Float = (FlxG.width / FlxG.height) / (FlxG.initialWidth / FlxG.initialHeight);
|
||||
final playerStrumlineScale:Float = ((FlxG.height / FlxG.width) * 1.95) * amplification;
|
||||
final playerNoteSpacing:Float = ((FlxG.height / FlxG.width) * 2.8) * amplification;
|
||||
|
||||
testStrumline.strumlineScale.set(playerStrumlineScale, playerStrumlineScale);
|
||||
testStrumline.setNoteSpacing(playerNoteSpacing);
|
||||
testStrumline.width *= 2;
|
||||
|
||||
testStrumline.x = (FlxG.width - testStrumline.width) / 2 + Constants.STRUMLINE_X_OFFSET;
|
||||
testStrumline.y = (FlxG.height - testStrumline.height) * 0.95 - Constants.STRUMLINE_Y_OFFSET;
|
||||
testStrumline.y -= 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (testStrumline != null)
|
||||
{
|
||||
testStrumline.destroy();
|
||||
remove(testStrumline);
|
||||
}
|
||||
|
||||
testStrumline = new Strumline(noteStyle, true);
|
||||
// center
|
||||
testStrumline.setPosition(FlxG.width / 2, FlxG.height / 2);
|
||||
testStrumline.x -= testStrumline.width / 2;
|
||||
testStrumline.scrollFactor.set(0, 0);
|
||||
add(testStrumline);
|
||||
}
|
||||
#end
|
||||
MenuTypedList.pauseInput = true;
|
||||
OptionsState.instance.drumsBG.fadeIn(1, 0, 1);
|
||||
canExit = false;
|
||||
differences = [];
|
||||
|
||||
#if !mobile
|
||||
testStrumline.y = Preferences.downscroll ? FlxG.height - (testStrumline.height + 45) - Constants.STRUMLINE_Y_OFFSET : (testStrumline.height / 2)
|
||||
jumpInText.y = 350;
|
||||
|
||||
#if mobile
|
||||
if (ControlsHandler.usingExternalInputDevice)
|
||||
{
|
||||
#end
|
||||
testStrumline.y = Preferences.downscroll ? FlxG.height - (testStrumline.height + 45) - Constants.STRUMLINE_Y_OFFSET : (testStrumline.height / 2)
|
||||
- Constants.STRUMLINE_Y_OFFSET;
|
||||
if (Preferences.downscroll) jumpInText.y = FlxG.height - 425;
|
||||
testStrumline.isDownscroll = Preferences.downscroll;
|
||||
#if mobile
|
||||
}
|
||||
else
|
||||
{
|
||||
jumpInText.y = FlxG.height - 425;
|
||||
}
|
||||
#end
|
||||
jumpInText.y = testStrumline.y + 175;
|
||||
#if !mobile if (Preferences.downscroll) #end jumpInText.y = testStrumline.y - 175;
|
||||
});
|
||||
PreciseInputManager.instance.onInputPressed.add(onKeyPress);
|
||||
PreciseInputManager.instance.onInputReleased.add(onKeyRelease);
|
||||
@@ -536,12 +572,16 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
|
||||
// Resync logic
|
||||
var diff:Float = Math.abs((FlxG.sound.music.time + localConductor.combinedOffset) - localConductor.songPosition);
|
||||
if (diff > 50)
|
||||
var diffBg:Float = Math.abs(FlxG.sound.music.time - OptionsState.instance.drumsBG.time);
|
||||
if (diff > 50 || diffBg > 50)
|
||||
{
|
||||
trace('Resyncing conductor: ' + diff + 'ms difference');
|
||||
trace('Resyncing conductor: ' + (diff > diffBg ? diff : diffBg) + 'ms difference');
|
||||
|
||||
// If the difference is greater than 50ms, we resync the conductor.
|
||||
localConductor.update(FlxG.sound.music.time, true);
|
||||
OptionsState.instance.drumsBG.pause();
|
||||
OptionsState.instance.drumsBG.time = FlxG.sound.music.time;
|
||||
OptionsState.instance.drumsBG.resume();
|
||||
b = localConductor.currentBeatTime;
|
||||
_lastBeat = b;
|
||||
}
|
||||
@@ -690,7 +730,7 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
var data:SongNoteData = new SongNoteData(arrowBeat * msPerBeat, _lastDirection, 0, null, null);
|
||||
testStrumline.addNoteData(data, false);
|
||||
|
||||
if (arrowBeat % 8 == 0)
|
||||
if (Math.floor(arrowBeat % 8) == 0)
|
||||
{
|
||||
var data:SongNoteData = new SongNoteData(arrowBeat * msPerBeat, 2, 0, null, null);
|
||||
testStrumline.addNoteData(data, false);
|
||||
@@ -698,6 +738,7 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
|
||||
_lastDirection = (_lastDirection + 1) % 4; // Cycle through directions 0-3
|
||||
}
|
||||
if (b >= 124 && _lastDirection != 0) _lastDirection = 0; // reset direction on loop
|
||||
}
|
||||
// Remove arrows and what not for when we are exiting calibration/testing
|
||||
else
|
||||
|
||||
@@ -194,6 +194,11 @@ class OptionsMenu extends Page<OptionsMenuPageName>
|
||||
InAppPurchasesUtil.restorePurchases();
|
||||
});
|
||||
#end
|
||||
#if android
|
||||
createItem("OPEN DATA FOLDER", function() {
|
||||
funkin.mobile.external.android.DataFolderUtil.openDataFolder();
|
||||
});
|
||||
#end
|
||||
#if FEATURE_NEWGROUNDS
|
||||
if (NewgroundsClient.instance.isLoggedIn())
|
||||
{
|
||||
@@ -252,6 +257,9 @@ class OptionsMenu extends Page<OptionsMenuPageName>
|
||||
onMenuChange(items.members[0]);
|
||||
|
||||
items.selectItem(OptionsState.rememberedSelectedIndex);
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
FlxG.touches.swipeThreshold.y = 100;
|
||||
#end
|
||||
}
|
||||
|
||||
function onMenuChange(selected:TextMenuItem):Void
|
||||
|
||||
@@ -135,6 +135,9 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
case HapticsMode.NONE: "None";
|
||||
default: "All";
|
||||
});
|
||||
createPrefItemNumber('Haptics Intensity', 'Multiplier of intensity for all the haptic feedback effects.', function(value:Float) {
|
||||
Preferences.hapticsIntensityMultiplier = value;
|
||||
}, null, Preferences.hapticsIntensityMultiplier, 0.1, 5, 0.1, 1);
|
||||
#end
|
||||
createPrefItemCheckbox('Flashing Lights', 'If disabled, it will dampen flashing effects. Useful for people with photosensitive epilepsy.',
|
||||
function(value:Bool):Void {
|
||||
@@ -267,8 +270,8 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
* @param step The value to increment/decrement by (default = 0.1)
|
||||
* @param precision Rounds decimals up to a `precision` amount of digits (ex: 4 -> 0.1234, 2 -> 0.12)
|
||||
*/
|
||||
function createPrefItemNumber(prefName:String, prefDesc:String, onChange:Float->Void, ?valueFormatter:Float->String, defaultValue:Int, min:Int, max:Int,
|
||||
step:Float = 0.1, precision:Int):Void
|
||||
function createPrefItemNumber(prefName:String, prefDesc:String, onChange:Float->Void, ?valueFormatter:Float->String, defaultValue:Float, min:Float,
|
||||
max:Float, step:Float = 0.1, precision:Int):Void
|
||||
{
|
||||
var item = new NumberPreferenceItem(funkin.ui.FullScreenScaleMode.gameNotchSize.x, (120 * items.length) + 30, prefName, defaultValue, min, max, step,
|
||||
precision, onChange, valueFormatter);
|
||||
|
||||
@@ -80,8 +80,8 @@ class NumberPreferenceItem extends TextMenuItem
|
||||
changeRateTimer -= elapsed;
|
||||
}
|
||||
|
||||
var jpLeft:Bool = controls().UI_LEFT_P #if mobile || SwipeUtil.justSwipedLeft #end;
|
||||
var jpRight:Bool = controls().UI_RIGHT_P #if mobile || SwipeUtil.justSwipedRight #end;
|
||||
var jpLeft:Bool = controls().UI_LEFT_P #if FEATURE_TOUCH_CONTROLS || SwipeUtil.justSwipedLeft #end;
|
||||
var jpRight:Bool = controls().UI_RIGHT_P #if FEATURE_TOUCH_CONTROLS || SwipeUtil.justSwipedRight #end;
|
||||
|
||||
if (jpLeft || jpRight)
|
||||
{
|
||||
@@ -93,21 +93,23 @@ class NumberPreferenceItem extends TextMenuItem
|
||||
var shouldIncrease:Bool = jpRight;
|
||||
var valueChangeMultiplier:Float = 1;
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
final dragThreshold:Float = 24 / elapsed / 100;
|
||||
|
||||
if (TouchUtil.touch != null && (TouchUtil.touch.deltaViewX <= -dragThreshold || TouchUtil.touch.deltaViewX >= dragThreshold))
|
||||
{
|
||||
valueChangeMultiplier = dragStepMultiplier;
|
||||
}
|
||||
#end
|
||||
|
||||
if (holdDelayTimer <= 0.0 && changeRateTimer <= 0.0)
|
||||
{
|
||||
if (controls().UI_LEFT || (TouchUtil.touch != null && TouchUtil.touch.deltaX <= -dragThreshold))
|
||||
if (controls().UI_LEFT #if FEATURE_TOUCH_CONTROLS || (TouchUtil.touch != null && TouchUtil.touch.deltaX <= -dragThreshold) #end)
|
||||
{
|
||||
shouldDecrease = true;
|
||||
changeRateTimer = CHANGE_RATE;
|
||||
}
|
||||
else if (controls().UI_RIGHT || (TouchUtil.touch != null && TouchUtil.touch.deltaX >= dragThreshold))
|
||||
else if (controls().UI_RIGHT #if FEATURE_TOUCH_CONTROLS || (TouchUtil.touch != null && TouchUtil.touch.deltaX >= dragThreshold) #end)
|
||||
{
|
||||
shouldIncrease = true;
|
||||
changeRateTimer = CHANGE_RATE;
|
||||
|
||||
@@ -40,6 +40,8 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
|
||||
static final BAR_HEIGHT:Int = 12;
|
||||
|
||||
static final PIECES_COUNT:Int = 16;
|
||||
|
||||
/**
|
||||
* Display takes this long (in seconds) to fade in.
|
||||
*/
|
||||
@@ -111,7 +113,7 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
|
||||
var vfdShader:VFDOverlay;
|
||||
var vfdBitmap:Bitmap;
|
||||
var box:Sprite;
|
||||
var rTextGroup:Sprite;
|
||||
var progressLines:Sprite;
|
||||
|
||||
public function new()
|
||||
@@ -131,29 +133,41 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
Lib.current.stage.color = Constants.COLOR_PRELOADER_BG;
|
||||
|
||||
// Width and height of the preloader.
|
||||
// Reference: Mobile resolution actually spits out smaller number
|
||||
// this._width is 893 on iPhone 14 Pro
|
||||
// and this._height is 393
|
||||
// so a few lines lower
|
||||
// ratio = 893 / 1280 / 2.0 = ~0.3 on iPhone
|
||||
// ratio = 1280 / 1280 / 2.0 = 0.5 on desktop
|
||||
// However on Android, this._width/_height are the devices actual resolution.
|
||||
this._width = Lib.current.stage.stageWidth;
|
||||
this._height = Lib.current.stage.stageHeight;
|
||||
|
||||
trace('Preloader size: ' + this._width + 'x' + this._height);
|
||||
|
||||
// Scale assets to the screen size.
|
||||
ratio = this._width / BASE_WIDTH / 2.0;
|
||||
// Desktop is always 1:1 scale, mobile needs DPI normalization for consistent positioning
|
||||
#if mobile
|
||||
var display = lime.system.System.getDisplay(0);
|
||||
var dpiScale = display.dpi / 160.0; // 160 is Android's baseline DPI
|
||||
var normalizedWidth = this._width / dpiScale;
|
||||
ratio = normalizedWidth / BASE_WIDTH;
|
||||
#else
|
||||
ratio = 1.0; // Desktop is always 1:1 scale
|
||||
#end
|
||||
|
||||
var amountOfPieces:Int = 16;
|
||||
progressBarPieces = [];
|
||||
var maxBarWidth:Float = this._width - BAR_PADDING * 2;
|
||||
var pieceWidth:Float = maxBarWidth / amountOfPieces;
|
||||
var pieceWidth:Float = maxBarWidth / PIECES_COUNT;
|
||||
var pieceGap:Int = 8;
|
||||
|
||||
progressLines = new openfl.display.Sprite();
|
||||
progressLines.graphics.lineStyle(2, Constants.COLOR_PRELOADER_BAR);
|
||||
progressLines.graphics.drawRect(-2, this._height - BAR_PADDING - BAR_HEIGHT - 208, this._width + 4, 30);
|
||||
progressLines.graphics.drawRect(-2, 0, this._width + 4, 30);
|
||||
progressLines.y = this._height * 0.67;
|
||||
addChild(progressLines);
|
||||
|
||||
var progressBarPiece:Sprite = new Sprite();
|
||||
progressBarPiece.graphics.beginFill(Constants.COLOR_PRELOADER_BAR);
|
||||
progressBarPiece.graphics.drawRoundRect(0, 0, pieceWidth - pieceGap, BAR_HEIGHT, 4, 4);
|
||||
progressBarPiece.graphics.endFill();
|
||||
|
||||
for (i in 0...amountOfPieces)
|
||||
for (i in 0...PIECES_COUNT)
|
||||
{
|
||||
var piece:Sprite = new Sprite();
|
||||
piece.graphics.beginFill(Constants.COLOR_PRELOADER_BAR);
|
||||
@@ -161,91 +175,70 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
piece.graphics.endFill();
|
||||
|
||||
piece.x = i * (piece.width + pieceGap);
|
||||
piece.y = this._height - BAR_PADDING - BAR_HEIGHT - 200;
|
||||
piece.y = progressLines.y + 8;
|
||||
addChild(piece);
|
||||
progressBarPieces.push(piece);
|
||||
}
|
||||
|
||||
// Create the progress message.
|
||||
progressLeftText = new TextField();
|
||||
dspText = new TextField();
|
||||
fnfText = new TextField();
|
||||
enhancedText = new TextField();
|
||||
stereoText = new TextField();
|
||||
|
||||
var progressLeftTextFormat = new TextFormat("DS-Digital", 32, Constants.COLOR_PRELOADER_BAR, true);
|
||||
var progressLeftTextFormat:TextFormat = new TextFormat("DS-Digital", Std.int(32 * ratio), Constants.COLOR_PRELOADER_BAR, true);
|
||||
progressLeftTextFormat.align = TextFormatAlign.LEFT;
|
||||
progressLeftText.defaultTextFormat = progressLeftTextFormat;
|
||||
|
||||
progressLeftText.selectable = false;
|
||||
progressLeftText.width = this._width - BAR_PADDING * 2;
|
||||
progressLeftText.text = 'Downloading assets...';
|
||||
progressLeftText.x = BAR_PADDING;
|
||||
progressLeftText.y = this._height - BAR_PADDING - BAR_HEIGHT - 290;
|
||||
// progressLeftText.shader = new VFDOverlay();
|
||||
addChild(progressLeftText);
|
||||
|
||||
// Create the progress %.
|
||||
progressRightText = new TextField();
|
||||
|
||||
var progressRightTextFormat = new TextFormat("DS-Digital", 16, Constants.COLOR_PRELOADER_BAR, true);
|
||||
var progressRightTextFormat:TextFormat = new TextFormat("DS-Digital", 16, Constants.COLOR_PRELOADER_BAR, true);
|
||||
progressRightTextFormat.align = TextFormatAlign.RIGHT;
|
||||
progressRightText.defaultTextFormat = progressRightTextFormat;
|
||||
|
||||
progressRightText.selectable = false;
|
||||
progressRightText.width = this._width - BAR_PADDING * 2;
|
||||
progressRightText.text = '0%';
|
||||
progressRightText.x = BAR_PADDING;
|
||||
progressRightText.y = this._height - BAR_PADDING - BAR_HEIGHT - 16 - 4;
|
||||
progressLeftText = makeText(BAR_PADDING * ratio, progressLines.y, 'Downloading assets...', Constants.COLOR_PRELOADER_BAR);
|
||||
progressLeftText.defaultTextFormat = progressLeftTextFormat;
|
||||
progressLeftText.width = this._width - BAR_PADDING * 2;
|
||||
addChild(progressLeftText);
|
||||
progressLeftText.y -= (progressLeftText.textHeight / ratio) * 2.5;
|
||||
|
||||
if (!isLandscapeFlipped()) progressLeftText.x += ScreenUtil.getNotchRect().width * ratio;
|
||||
|
||||
// Create the progress % in the bottom right
|
||||
// This displays in the bottom right corner, so it's generally safe from notches...
|
||||
// but we should do a sweep online to make sure that there's no hole-punch style cameras on android that may block this
|
||||
progressRightText = makeText(BAR_PADDING, this._height - BAR_PADDING - BAR_HEIGHT - 16 - 4, '0%', Constants.COLOR_PRELOADER_BAR);
|
||||
progressRightText.defaultTextFormat = progressRightTextFormat;
|
||||
addChild(progressRightText);
|
||||
|
||||
box = new Sprite();
|
||||
box.graphics.beginFill(Constants.COLOR_PRELOADER_BAR, 1);
|
||||
box.graphics.drawRoundRect(0, 0, 64, 20, 5, 5);
|
||||
box.graphics.drawRoundRect(70, 0, 58, 20, 5, 5);
|
||||
box.graphics.endFill();
|
||||
box.graphics.beginFill(Constants.COLOR_PRELOADER_BAR, 0.1);
|
||||
box.graphics.drawRoundRect(0, 0, 128, 20, 5, 5);
|
||||
box.graphics.endFill();
|
||||
box.x = this._width - BAR_PADDING - BAR_HEIGHT - 432;
|
||||
box.y = this._height - BAR_PADDING - BAR_HEIGHT - 244;
|
||||
addChild(box);
|
||||
// note: on mobile we generally dont want to scale these texts down
|
||||
// however should test on android + iPad to see how it fits!
|
||||
rTextGroup = new Sprite();
|
||||
rTextGroup.graphics.beginFill(Constants.COLOR_PRELOADER_BAR, 1);
|
||||
rTextGroup.graphics.drawRoundRect(0, 40, 64, 20, 5, 5);
|
||||
rTextGroup.graphics.drawRoundRect(70, 40, 58, 20, 5, 5);
|
||||
rTextGroup.graphics.endFill();
|
||||
rTextGroup.graphics.beginFill(Constants.COLOR_PRELOADER_BAR, 0.1);
|
||||
rTextGroup.graphics.drawRoundRect(0, 40, 128, 20, 5, 5);
|
||||
rTextGroup.graphics.endFill();
|
||||
rTextGroup.x = this._width * 0.64;
|
||||
rTextGroup.y = progressLeftText.y;
|
||||
addChild(rTextGroup);
|
||||
|
||||
dspText.selectable = false;
|
||||
dspText.textColor = 0x000000;
|
||||
dspText = makeText(10, 33, 'DSP', 0x000000);
|
||||
dspText.width = this._width;
|
||||
dspText.height = 30;
|
||||
dspText.text = 'DSP';
|
||||
dspText.x = 10;
|
||||
dspText.y = -7;
|
||||
box.addChild(dspText);
|
||||
rTextGroup.addChild(dspText);
|
||||
|
||||
fnfText.selectable = false;
|
||||
fnfText.textColor = 0x000000;
|
||||
fnfText = makeText(78, 33, 'FNF', 0x000000);
|
||||
fnfText.width = this._width;
|
||||
fnfText.height = 30;
|
||||
fnfText.x = 78;
|
||||
fnfText.y = -7;
|
||||
fnfText.text = 'FNF';
|
||||
box.addChild(fnfText);
|
||||
rTextGroup.addChild(fnfText);
|
||||
|
||||
enhancedText.selectable = false;
|
||||
enhancedText.textColor = Constants.COLOR_PRELOADER_BAR;
|
||||
enhancedText = makeText(-100, 40, 'ENHANCED', Constants.COLOR_PRELOADER_BAR);
|
||||
enhancedText.width = this._width;
|
||||
enhancedText.height = 100;
|
||||
enhancedText.text = 'ENHANCED';
|
||||
enhancedText.x = -100;
|
||||
enhancedText.y = 0;
|
||||
box.addChild(enhancedText);
|
||||
rTextGroup.addChild(enhancedText);
|
||||
|
||||
stereoText.selectable = false;
|
||||
stereoText.textColor = Constants.COLOR_PRELOADER_BAR;
|
||||
stereoText = makeText(0, 0, 'STEREO', Constants.COLOR_PRELOADER_BAR);
|
||||
stereoText.width = this._width;
|
||||
stereoText.height = 100;
|
||||
stereoText.text = 'STEREO';
|
||||
stereoText.x = 0;
|
||||
stereoText.y = -40;
|
||||
box.addChild(stereoText);
|
||||
rTextGroup.addChild(stereoText);
|
||||
|
||||
// todo: check if these actually overlap the notch with some rect check thing
|
||||
// im making more sweeping assumptions rn because i only have iOS
|
||||
if (isLandscapeFlipped()) rTextGroup.x -= ScreenUtil.getNotchRect().width * ratio;
|
||||
|
||||
vfdBitmap = new Bitmap(new BitmapData(this._width, this._height, true, 0xFFFFFFFF));
|
||||
addChild(vfdBitmap);
|
||||
@@ -270,6 +263,18 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
#end
|
||||
}
|
||||
|
||||
function makeText(txtX:Float, txtY:Float, txt:String, color:Int):TextField
|
||||
{
|
||||
var text:TextField = new TextField();
|
||||
text.selectable = false;
|
||||
text.width = this._width - BAR_PADDING * 2;
|
||||
text.x = txtX;
|
||||
text.y = txtY;
|
||||
text.text = txt;
|
||||
text.textColor = color;
|
||||
return text;
|
||||
}
|
||||
|
||||
var lastElapsed:Float = 0.0;
|
||||
|
||||
override function update(percent:Float):Void
|
||||
@@ -830,7 +835,7 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
}
|
||||
#end
|
||||
|
||||
static final TOTAL_STEPS:Int = 11;
|
||||
public static final TOTAL_STEPS:Int = 11;
|
||||
static final ELLIPSIS_TIME:Float = 0.5;
|
||||
|
||||
function updateGraphics(percent:Float, elapsed:Float):Void
|
||||
@@ -853,14 +858,10 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
else
|
||||
{
|
||||
// Render progress bar
|
||||
var maxWidth = this._width - BAR_PADDING * 2;
|
||||
var barWidth = maxWidth * percent;
|
||||
var piecesToRender:Int = Std.int(percent * progressBarPieces.length);
|
||||
|
||||
for (i => piece in progressBarPieces)
|
||||
{
|
||||
piece.alpha = i <= piecesToRender ? 0.9 : 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle ellipsis count to show loading
|
||||
@@ -876,7 +877,7 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
var percentage:Int = Math.floor(percent * 100);
|
||||
progressRightText.text = '$percentage%';
|
||||
|
||||
trace('Preloader state: ' + currentState + ' (' + percentage + '%, ' + elapsed + 's)');
|
||||
if (currentState.getProgressLeftText() != null) trace('Preloader state: ' + currentState + ' (' + percentage + '%, ' + elapsed + 's)');
|
||||
|
||||
super.update(percent);
|
||||
}
|
||||
@@ -889,9 +890,8 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
else if (progressLeftText.text != text)
|
||||
{
|
||||
// We have to keep updating the text format, because the font can take a frame or two to load.
|
||||
var progressLeftTextFormat:TextFormat = new TextFormat("DS-Digital", 32, Constants.COLOR_PRELOADER_BAR, true);
|
||||
progressLeftTextFormat.align = TextFormatAlign.LEFT;
|
||||
progressLeftText.defaultTextFormat = progressLeftTextFormat;
|
||||
progressLeftText.defaultTextFormat = new TextFormat("DS-Digital", 32, Constants.COLOR_PRELOADER_BAR, true);
|
||||
progressLeftText.defaultTextFormat.align = TextFormatAlign.LEFT;
|
||||
progressLeftText.text = text;
|
||||
|
||||
dspText.defaultTextFormat = new TextFormat("Quantico", 20, 0x000000, false);
|
||||
@@ -911,6 +911,16 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not we are in flipped landscape device rotation,
|
||||
* generally for mobile to accomodate the device notch!
|
||||
* @return Bool
|
||||
*/
|
||||
function isLandscapeFlipped():Bool
|
||||
{
|
||||
return lime.system.System.getDisplayOrientation(0) == DISPLAY_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
}
|
||||
|
||||
function immediatelyStartGame():Void
|
||||
{
|
||||
_loaded = true;
|
||||
@@ -930,11 +940,7 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
// Fade out progress bar too.
|
||||
progressLeftText.alpha = alphaToFade;
|
||||
progressRightText.alpha = alphaToFade;
|
||||
box.alpha = alphaToFade;
|
||||
dspText.alpha = alphaToFade;
|
||||
fnfText.alpha = alphaToFade;
|
||||
enhancedText.alpha = alphaToFade;
|
||||
stereoText.alpha = alphaToFade;
|
||||
rTextGroup.alpha = alphaToFade;
|
||||
progressLines.alpha = alphaToFade;
|
||||
|
||||
for (piece in progressBarPieces)
|
||||
@@ -960,7 +966,7 @@ class FunkinPreloader extends FlxBasePreloader
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract FunkinPreloaderState(Int) to Int
|
||||
enum abstract FunkinPreloaderState(String) to String
|
||||
{
|
||||
/**
|
||||
* The state before downloading has begun.
|
||||
@@ -1037,12 +1043,14 @@ enum abstract FunkinPreloaderState(Int) to Int
|
||||
|
||||
/**
|
||||
* Formats the status text for progress bar display.
|
||||
* @param steps The total number of steps
|
||||
* @param suffix What to append to the end of the text, usually those dynamic ellipsis
|
||||
* @param steps The total number of steps. Defaults to `FunkinPreloader.TOTAL_STEPS`.
|
||||
* @param suffix What to append to the end of the text, usually those dynamic ellipsis. Defaults to an empty string.
|
||||
* @return String 'Loading \n0/$steps $suffix' for example
|
||||
*/
|
||||
public function getProgressLeftText(steps:Int, suffix:String):String
|
||||
public function getProgressLeftText(?steps:Int, ?suffix:String):String
|
||||
{
|
||||
steps = steps ?? FunkinPreloader.TOTAL_STEPS;
|
||||
suffix = suffix ?? '';
|
||||
switch (this)
|
||||
{
|
||||
case NotStarted:
|
||||
|
||||
@@ -18,7 +18,7 @@ class CLIUtil
|
||||
var cwd:String = Path.addTrailingSlash(Sys.getCwd());
|
||||
var gameDir:String = '';
|
||||
#if android
|
||||
gameDir = Path.addTrailingSlash(extension.androidtools.os.Build.VERSION.SDK_INT > 30 ? extension.androidtools.content.Context.getObbDir() : extension.androidtools.content.Context.getExternalFilesDir());
|
||||
gameDir = Path.addTrailingSlash(extension.androidtools.content.Context.getExternalFilesDir());
|
||||
#elseif ios
|
||||
// Why? Because for some reason lime.system.System.documentsDirectory is returning a directory that's different and we're unable to read or write from, so it's disabled and no solution is found...
|
||||
trace('[WARN]: Reseting the Current Working Directory is unavailable on iOS targets');
|
||||
|
||||
@@ -614,7 +614,7 @@ class Constants
|
||||
/**
|
||||
* Min vibration amplitude.
|
||||
*/
|
||||
public inline static final MIN_VIBRATION_AMPLITUDE:Float = 0.1;
|
||||
public inline static final MIN_VIBRATION_AMPLITUDE:Float = 0.25;
|
||||
|
||||
/**
|
||||
* Default vibration amplitude.
|
||||
|
||||
@@ -44,11 +44,7 @@ class HapticUtil
|
||||
final hapticsModes:Array<HapticsMode> = targetHapticsModes ?? [HapticsMode.ALL];
|
||||
if (!hapticsModes.contains(Preferences.hapticsMode)) return;
|
||||
|
||||
#if ios
|
||||
final amplitudeValue = FlxMath.bound(amplitude * 2.5, 0, Constants.MAX_VIBRATION_AMPLITUDE);
|
||||
#else
|
||||
final amplitudeValue = amplitude;
|
||||
#end
|
||||
final amplitudeValue = FlxMath.bound(amplitude * Preferences.hapticsIntensityMultiplier, 0, Constants.MAX_VIBRATION_AMPLITUDE);
|
||||
|
||||
if (period > 0)
|
||||
{
|
||||
|
||||
@@ -10,9 +10,6 @@ class AnsiTrace
|
||||
*/
|
||||
public static function trace(v:Dynamic, ?info:haxe.PosInfos)
|
||||
{
|
||||
#if (NO_FEATURE_LOG_TRACE && !FEATURE_DEBUG_FUNCTIONS)
|
||||
return;
|
||||
#end
|
||||
var str = formatOutput(v, info);
|
||||
#if FEATURE_DEBUG_TRACY
|
||||
cpp.vm.tracy.TracyProfiler.message(str, flixel.util.FlxColor.WHITE);
|
||||
|
||||
@@ -4,6 +4,9 @@ import flixel.FlxG;
|
||||
import flixel.FlxBasic;
|
||||
import funkin.ui.MusicBeatState;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
#if android
|
||||
import funkin.mobile.external.android.CallbackUtil;
|
||||
#end
|
||||
|
||||
/**
|
||||
* A plugin which adds functionality to press `F5` to reload all game assets, then reload the current state.
|
||||
@@ -15,6 +18,10 @@ class ReloadAssetsDebugPlugin extends FlxBasic
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
#if android
|
||||
CallbackUtil.onActivityResult.add(onActivityResult);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function initialize():Void
|
||||
@@ -32,20 +39,44 @@ class ReloadAssetsDebugPlugin extends FlxBasic
|
||||
if (FlxG.keys.justPressed.F5)
|
||||
#end
|
||||
{
|
||||
var state:Dynamic = FlxG.state;
|
||||
if (state is MusicBeatState || state is MusicBeatSubState) state.reloadAssets();
|
||||
else
|
||||
{
|
||||
funkin.modding.PolymodHandler.forceReloadAssets();
|
||||
|
||||
// Create a new instance of the current state, so old data is cleared.
|
||||
FlxG.resetState();
|
||||
}
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
public override function destroy():Void
|
||||
{
|
||||
super.destroy();
|
||||
|
||||
#if android
|
||||
if (CallbackUtil.onActivityResult.has(onActivityResult))
|
||||
{
|
||||
CallbackUtil.onActivityResult.remove(onActivityResult);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
function reload():Void
|
||||
{
|
||||
var state:Dynamic = FlxG.state;
|
||||
if (state is MusicBeatState || state is MusicBeatSubState) state.reloadAssets();
|
||||
else
|
||||
{
|
||||
funkin.modding.PolymodHandler.forceReloadAssets();
|
||||
|
||||
// Create a new instance of the current state, so old data is cleared.
|
||||
FlxG.resetState();
|
||||
}
|
||||
}
|
||||
|
||||
#if android
|
||||
@:noCompletion
|
||||
function onActivityResult(resultCode:Int, requestCode:Int):Void
|
||||
{
|
||||
if (resultCode == CallbackUtil.DATA_FOLDER_CLOSED)
|
||||
{
|
||||
reload();
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user