Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b976b0d71 | ||
|
|
ae1239c57d | ||
|
|
bfbbf9cb01 | ||
|
|
d29bb47d22 | ||
|
|
d29ecfb7c4 | ||
|
|
cdd17ce033 | ||
|
|
8e8b26f029 | ||
|
|
6f43438cdb | ||
|
|
a74369c77e | ||
|
|
def1b74eb6 | ||
|
|
c70fa7c94a | ||
|
|
c0353bca2b | ||
|
|
f385cf9fd3 | ||
|
|
301f71b428 | ||
|
|
189e028442 | ||
|
|
a9647cf9fc | ||
|
|
05b9d68645 | ||
|
|
45f0f14ed9 | ||
|
|
a8262a8d3b | ||
|
|
264c2d8e77 | ||
|
|
b2f231e889 | ||
|
|
34bb201104 | ||
|
|
d9fcaf0e63 | ||
|
|
56259b9143 | ||
|
|
dc2acac152 | ||
|
|
bf5a669592 | ||
|
|
8b9775d91f | ||
|
|
6caeda9a9d | ||
|
|
9983556748 | ||
|
|
651298e908 | ||
|
|
142ea6db0c | ||
|
|
5397924edd | ||
|
|
94bd18fe17 | ||
|
|
664d9e0fb3 | ||
|
|
edf6889af9 | ||
|
|
c65dddacc0 | ||
|
|
659ea41206 | ||
|
|
5cd47a4414 | ||
|
|
65699ee865 | ||
|
|
d423eb3ddd | ||
|
|
c67085a46a | ||
|
|
a83a4a599c | ||
|
|
f6ac4cad43 | ||
|
|
0101bae7ec | ||
|
|
4c5335d7a3 | ||
|
|
b811ca91a6 | ||
|
|
c5f2a74494 | ||
|
|
3e0dbe2758 | ||
|
|
bbc0546c74 | ||
|
|
617d5ffa06 | ||
|
|
73114e2da6 | ||
|
|
808698ace7 | ||
|
|
832bc5bd83 | ||
|
|
b15e809c63 | ||
|
|
8074d57186 | ||
|
|
31d3718e5c | ||
|
|
5fdbd23a17 | ||
|
|
23a9e7f944 | ||
|
|
0397a41a7f | ||
|
|
afbb335993 | ||
|
|
2bc1a31427 | ||
|
|
dafbb43532 | ||
|
|
4978c89ac3 | ||
|
|
29b541af38 | ||
|
|
fc09350be7 | ||
|
|
e1ca3c69f0 | ||
|
|
a15bd36943 | ||
|
|
51605e50f8 | ||
|
|
3a1b573ea3 | ||
|
|
ef6b214872 | ||
|
|
8f2a8c8335 | ||
|
|
7f5b4974c3 | ||
|
|
d63782ee9d | ||
|
|
19b1c32124 | ||
|
|
d183876514 | ||
|
|
db0ac48192 | ||
|
|
ba37405e4b | ||
|
|
66197b34f6 | ||
|
|
a7da71c8a8 | ||
|
|
5bd081c2af | ||
|
|
aefc4db277 | ||
|
|
adc14cc15d | ||
|
|
a8294a9cce | ||
|
|
80569c806c | ||
|
|
d6837eba2e | ||
|
|
924b1cf386 | ||
|
|
0839c16535 | ||
|
|
db3e012cb0 | ||
|
|
6c2ebff680 | ||
|
|
4555b6736e | ||
|
|
0c674ac23b | ||
|
|
c5e5ab16df | ||
|
|
926d8c0517 | ||
|
|
34bb58015d | ||
|
|
d86fe68d80 | ||
|
|
1434820d10 | ||
|
|
08fb5b8459 | ||
|
|
8e8dcaa7f9 | ||
|
|
b35059ec99 | ||
|
|
fcb47ff4fc | ||
|
|
3218ceeecd | ||
|
|
1823373d7e | ||
|
|
af14d42939 | ||
|
|
3dd3338ab2 | ||
|
|
26f2578bf2 | ||
|
|
c32ade49b9 | ||
|
|
d4eee38fd2 | ||
|
|
6543c58363 | ||
|
|
158c015682 | ||
|
|
da9f17cf87 | ||
|
|
d31ef12363 | ||
|
|
bf36a86877 | ||
|
|
9829103671 | ||
|
|
ea93ec90cb | ||
|
|
07c872c775 |
@@ -45,7 +45,7 @@ body:
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.6.2
|
||||
placeholder: ex. 0.6.3
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ body:
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.6.2
|
||||
placeholder: ex. 0.6.3
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ body:
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you compiling? The game version is in the bottom left corner of the main menu or in the project.hxp file.
|
||||
placeholder: ex. 0.6.2
|
||||
placeholder: ex. 0.6.3
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ body:
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.6.2
|
||||
placeholder: ex. 0.6.3
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
APIStuff.hx
|
||||
dump/
|
||||
export/
|
||||
project/
|
||||
RECOVER_*.fla
|
||||
shitAudio/
|
||||
.build_time
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[submodule "assets"]
|
||||
path = assets
|
||||
url = https://github.com/FunkinCrew/Funkin-assets-secret
|
||||
url = https://github.com/FunkinCrew/Funkin.assets
|
||||
[submodule "art"]
|
||||
path = art
|
||||
url = https://github.com/FunkinCrew/Funkin-art-secret
|
||||
url = https://github.com/FunkinCrew/Funkin.art
|
||||
|
||||
+134
-7
@@ -6,6 +6,133 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
|
||||
|
||||
## [0.6.4] - 2025-05-02
|
||||
|
||||
### Changed
|
||||
|
||||
- Misses now actually reduce your clear percentage and rank, as Eric intended. ([5fdbd23](https://github.com/FunkinCrew/Funkin/commit/5fdbd23a17b8eaf21582283400be2c6444e8b198)) - by @Lasercar in [#4880](https://github.com/FunkinCrew/Funkin/pull/4880)
|
||||
- The miss rebalance is now reflected on the Freeplay clear percent display. ([f6ac4ca](https://github.com/FunkinCrew/Funkin/commit/f6ac4cad43f4834a86a3fc156d6d39eca2c47c7e)) - by @Lasercar, @Hundrec, and @NotHyper-474 in [#4898](https://github.com/FunkinCrew/Funkin/pull/4898) and [#4923](https://github.com/FunkinCrew/Funkin/pull/4923)
|
||||
|
||||
### Fixed
|
||||
|
||||
- HTML5 builds are no longer literally unplayable. ([6f43438](https://github.com/FunkinCrew/Funkin/commit/6f43438cdbfecf26d4bcf62a0d9ff71e40333ac2)) - by @cyn0x8 in [#4398](https://github.com/FunkinCrew/Funkin/pull/4398)
|
||||
- Medals no longer crash the game when obtained. ([808698a](https://github.com/FunkinCrew/Funkin/commit/808698ace7457bc76a8a52d415d63fa4ee5b9400)) - by @KoloInDaCrib in [#4815](https://github.com/FunkinCrew/Funkin/pull/4815)
|
||||
- The game no longer crashes when reaching the main menu before authenticating with Newgrounds. ([a83a4a5](https://github.com/FunkinCrew/Funkin/commit/a83a4a599c2e407832ef9bbe47bb8b8c07ce712a)) - by @NotHyper-474 in [#4871](https://github.com/FunkinCrew/Funkin/pull/4871)
|
||||
- User preferences and other settings are now properly saved after restarting the game. ([23a9e7f](https://github.com/FunkinCrew/Funkin/commit/23a9e7f944fb703160c2ed7b0bef38c70c5370db)) - by @Lasercar in [#4881](https://github.com/FunkinCrew/Funkin/pull/4881)
|
||||
- Texture atlas sprites no longer loop infinitely, fixing issues with multiple cutscenes. ([f385cf9](https://github.com/FunkinCrew/Funkin/commit/f385cf9fd33e5358427f40dad3fecfed233c1706)) - by @AbnormalPoof in [#4564](https://github.com/FunkinCrew/Funkin/pull/4564)
|
||||
- The Week 3 Pico Mix cutscene no longer breaks with Naughtyness turned off. ([e3aba5c](https://github.com/FunkinCrew/funkin.assets/commit/e3aba5c4faa8e8c705c3e0daf04e30db7ac5a136)) - by @Lasercar in [funkin.assets#169](https://github.com/FunkinCrew/funkin.assets/pull/169)
|
||||
- Adjusted the pixel strumline’s position when Downscroll is enabled to match the regular notestyle. ([ea93ec9](https://github.com/FunkinCrew/Funkin/commit/ea93ec90cb5c5dbd4a5bea64114af58c70cb43f4)) - by @Lasercar in [#4318](https://github.com/FunkinCrew/Funkin/pull/4318)
|
||||
- The Main Menu now correctly remembers your most recently selected menu item. ([05b9d68](https://github.com/FunkinCrew/Funkin/commit/05b9d68645b71c9cc36a5cb0c0e93af6acb36132)) - by @Lasercar and @Hundrec in [#4227](https://github.com/FunkinCrew/Funkin/pull/4227)
|
||||
- Fixed an occasional softlock when returning to the Freeplay menu from a song. ([b15e809](https://github.com/FunkinCrew/Funkin/commit/b15e809c63664e039781592dcbdee4383e73800e)) - by @TechnikTil in [#4665](https://github.com/FunkinCrew/Funkin/pull/4665)
|
||||
- Switching to and from the Freeplay Random capsule now behaves more consistently. ([832bc5b](https://github.com/FunkinCrew/Funkin/commit/832bc5bd83d49a646b9c8cb2b767602c848be908)) - by @VioletSnowLeopard in [#4885](https://github.com/FunkinCrew/Funkin/pull/4885)
|
||||
- Changing Freeplay filters with the Random capsule selected now behaves more consistently. ([edf6889](https://github.com/FunkinCrew/Funkin/commit/edf6889af9e7f68caead9b8139178a36b4b5b2d6)) - by @VioletSnowLeopard in [#4913](https://github.com/FunkinCrew/Funkin/pull/4913)
|
||||
- Switching between identical Freeplay filters no longer makes the songlist invisible. ([8b9775d](https://github.com/FunkinCrew/Funkin/commit/8b9775d91f4f2b5896a637a014b4be770319968e)) - by @VioletSnowLeopard in [#4919](https://github.com/FunkinCrew/Funkin/pull/4919)
|
||||
- The millions place digit of the Freeplay score display now updates properly. ([d9fcaf0](https://github.com/FunkinCrew/Funkin/commit/d9fcaf0e6382b121f7773d23f4864364a1e6a577)) - by @Lasercar in [#4065](https://github.com/FunkinCrew/Funkin/pull/4065)
|
||||
- The Freeplay clear percent display is now more consistently aligned. ([329182e](https://github.com/FunkinCrew/funkin.assets/commit/329182ea1b6839187a1d800b2d009a4c1874479d)) - by @Hundrec in [funkin.assets#37](https://github.com/FunkinCrew/funkin.assets/pull/37)
|
||||
- The debris in Pico’s Great Results animation no longer grows in size. ([c108a7f](https://github.com/FunkinCrew/funkin.assets/commit/c108a7ff0d11bf328e7b232160b8f68c71e21bca)) - by @ThatRozebudDude in [funkin.assets#73](https://github.com/FunkinCrew/funkin.assets/pull/73)
|
||||
- Fixed the song not starting if more than 32 sounds are playing at once. ([31d3718](https://github.com/FunkinCrew/Funkin/commit/31d3718e5c33371cacd495219f9cc3908244bf71)) - by @KoloInDaCrib in [#4352](https://github.com/FunkinCrew/Funkin/pull/4352)
|
||||
- Hot reloading (pressing F5) during dialogue no longer crashes the game. ([3e0dbe2](https://github.com/FunkinCrew/Funkin/commit/3e0dbe2758d68ccf4ded1d08bec247ab05d70829)) - by @KoloInDaCrib in [#4769](https://github.com/FunkinCrew/Funkin/pull/4769)
|
||||
- Restarting a song without either a player or an opponent no longer crashes the game, fixing minimal playtest mode. ([bbc0546](https://github.com/FunkinCrew/Funkin/commit/bbc0546c7450b929dc78ad9271171ea899478e9d)) - by @thesuperpig56 in [#4778](https://github.com/FunkinCrew/Funkin/pull/4778)
|
||||
- The sound tray no longer behaves incorrectly after wiping save data. ([afbb335](https://github.com/FunkinCrew/Funkin/commit/afbb3359938f020707efd92a17419e3658528ea5)) - by @Lasercar in [#4617](https://github.com/FunkinCrew/Funkin/pull/4617)
|
||||
- Opening a chart from the “Open Recent” section no longer crashes the Chart Editor. ([def1b74](https://github.com/FunkinCrew/Funkin/commit/def1b74eb6987272bc360cbf586ddcd1c26c6381)) - by @CrusherNotDrip in [#4936](https://github.com/FunkinCrew/Funkin/pull/4936)
|
||||
- Loading an invalid variation in the Chart Editor no longer crashes the game. ([c5f2a74](https://github.com/FunkinCrew/Funkin/commit/c5f2a744940dbc685b25373fa2dabb548f0b26fa)) - by @NotHyper-474 in [#4391](https://github.com/FunkinCrew/Funkin/pull/4391)
|
||||
- Default difficulties are no longer re-added when saving and loading a .FNFC chart. ([8074d57](https://github.com/FunkinCrew/Funkin/commit/8074d571860aba2c2f6f1c642d8d0e671ff5355e)) - by @Lasercar in [#4216](https://github.com/FunkinCrew/Funkin/pull/4216)
|
||||
- The Chart Editor now resizes properly after entering Fullscreen during a playtest. ([142ea6d](https://github.com/FunkinCrew/Funkin/commit/142ea6db0c53f6f9288109604702c2e7e5bc6f18)) - by @NotHyper-474 in [#4266](https://github.com/FunkinCrew/Funkin/pull/4266)
|
||||
- Added three missing hotkeys to the Chart Editor user guide. ([73ccb9a](https://github.com/FunkinCrew/funkin.assets/commit/73ccb9a80d08f3311f51cf88950692d7c1ea5d60)) - by @NotHyper-474 and @Hundrec in [funkin.assets#83](https://github.com/FunkinCrew/funkin.assets/pull/83)
|
||||
- The “Skip Forward” button is no longer named “Skip Back” in the Chart Editor. ([78f9fb7](https://github.com/FunkinCrew/funkin.assets/commit/78f9fb7cb6eaf7466826f144a0142c008fbff2f8)) - by @Hundrec in [funkin.assets#22](https://github.com/FunkinCrew/funkin.assets/pull/22)
|
||||
- Exporting a character .JSON from the Animation Editor now fills in the file name. ([a8262a8](https://github.com/FunkinCrew/Funkin/commit/a8262a8d3b4d2169327131e21c98f803da4a5ee4)) - by @anysad in [#3090](https://github.com/FunkinCrew/Funkin/pull/3090)
|
||||
- Properly credited MtH as a Charter in Monster’s metadata. ([6a10799](https://github.com/FunkinCrew/funkin.assets/commit/6a10799a40eb2b2deb364bcd3cc4387abe8e8bc4)) - by @ChillyBeanBAM in [funkin.assets#66](https://github.com/FunkinCrew/funkin.assets/pull/66)
|
||||
- Corrected the chances of a Pause Menu easter egg appearing. ([0101bae](https://github.com/FunkinCrew/Funkin/commit/0101bae7ecc800e26efd9590fdfa1305039b4461)) - by @VioletSnowLeopard in [#4358](https://github.com/FunkinCrew/Funkin/pull/4358)
|
||||
- Removed spammy traces for Playable Pico’s animations. ([f87255d](https://github.com/FunkinCrew/funkin.assets/commit/f87255d4bcb7a7ce0dc920aefc36b98a96115c75)) - by @VioletSnowLeopard in [funkin.assets#179](https://github.com/FunkinCrew/funkin.assets/pull/179)
|
||||
- Fixed memory overflowing in crash logs. ([189e028](https://github.com/FunkinCrew/Funkin/commit/189e028442c7fb892ce4dab62a5d08db658f3b2b)) - by @ACrazyTown in [#4589](https://github.com/FunkinCrew/Funkin/pull/4589)
|
||||
|
||||
### Removed
|
||||
|
||||
- The CHANGELOG.md file will no longer be included in builds. Visit the [Funkin' GitHub](https://github.com/FunkinCrew/Funkin/blob/main/CHANGELOG.md) to view the latest changes! ([a7da71c](https://github.com/FunkinCrew/Funkin/commit/a7da71c8a87baa440a0bc6e23d7e9b36c7574e14)) - by @Hundrec and @NotHyper-474 in [#4868](https://github.com/FunkinCrew/Funkin/pull/4868)
|
||||
|
||||
## New Contributors for 0.6.4
|
||||
|
||||
* @thesuperpig56 made their first contribution in [#4778](https://github.com/FunkinCrew/Funkin/pull/4778)
|
||||
* @ThatRozebudDude made their first contribution in [funkin.assets#73](https://github.com/FunkinCrew/funkin.assets/pull/73)
|
||||
|
||||
|
||||
|
||||
## [0.6.3] - 2025-04-25
|
||||
|
||||
### Added
|
||||
|
||||
- New option in the Preferences menu: VSync!
|
||||
- Set it to Off, On, or Adaptive
|
||||
- Adaptive turns VSync off during FPS drops, which is better if supported by your graphics card.
|
||||
- Doesn't work on Mac, sorry!
|
||||
- Otis now has muzzle flashes when shooting.
|
||||
- Added a little easter egg to one of Pico's Results Screen animations.
|
||||
- Overhauled the sticker system to allow modders to easily add custom sticker sets. ([cdc468b](https://github.com/FunkinCrew/Funkin/commit/cdc468ba15683b3c0f54015e337673c77ddd7962)) - by @AbnormalPoof in [#4003](https://github.com/FunkinCrew/Funkin/pull/4003)
|
||||
- Implemented a macro to optimize registries and entries. ([1497521](https://github.com/FunkinCrew/Funkin/commit/14975212a918adb1a5d96a349ec9e8e63c9fc64b)) - by @lemz1 in [#3694](https://github.com/FunkinCrew/Funkin/pull/3694)
|
||||
- The macro is now used to retrieve base game assets. ([963e2ec](https://github.com/FunkinCrew/Funkin/commit/963e2ecd630fae24dae4206d315618cf4c4be6db)) - by @lemz1 in [#4707](https://github.com/FunkinCrew/Funkin/pull/4707)
|
||||
- The Chart Editor now displays the current beat and step of the playhead. ([83bb3bb](https://github.com/FunkinCrew/Funkin/commit/83bb3bb5074ecb5f50ee21979711efcda9d8017d)) - by @NotHyper-474 in [#4649](https://github.com/FunkinCrew/Funkin/pull/4649)
|
||||
|
||||
### Changed
|
||||
|
||||
- The mod API version is now 0.6.3.
|
||||
- Be sure to update the version in your mods' metadata, then ensure they are still working!
|
||||
- Eric's scoring rebalance update!
|
||||
- Misses (not ghost misses) are now worth -100 points, up from -10.
|
||||
- Hold note trails now grant 20% less health.
|
||||
- Dropping a hold note early now plays the miss animation and deducts health and score.
|
||||
- Made a LOT of charting changes!
|
||||
- Many missing, extra, and mistimed notes have been corrected.
|
||||
- Stacked notes that were preventing full combos have been removed.
|
||||
- Camera events have been adjusted to stay within the boundaries of the stage.
|
||||
- Some songs have been recharted.
|
||||
- The Freeplay menu can now always switch between variations.
|
||||
- The nearest song with an Erect variation will be selected when switching.
|
||||
- Switching difficulties now always plays the capsule jump-in animation.
|
||||
- The "Random" capsule now plays animations consistent with other capsules.
|
||||
- The Eggnog Erect cutscene can now be skipped. ([0303a03](https://github.com/FunkinCrew/funkin.assets/commit/1202651db8ea938fe11b6b734fbf7884d101e6ad)) - by @ShadzXD in [#62](https://github.com/FunkinCrew/funkin.assets/pull/62)
|
||||
- Swapped the positions of “skip” and “restart” items in the dialogue pause menu for consistency. ([1f88a3b](https://github.com/FunkinCrew/Funkin/commit/1f88a3b6e13e9283ad680805deab5e27ba141d96)) - by @VioletSnowLeopard in [#4553](https://github.com/FunkinCrew/Funkin/pull/4553)
|
||||
- The Controls menu now prevents unbinding essential UI controls to safeguard against softlocking save files. ([7eebce4](https://github.com/FunkinCrew/Funkin/commit/7eebce432d7627a1cf0bbd43f22a6f9bcb63ff65)) - by @VioletSnowLeopard in [#4382](https://github.com/FunkinCrew/Funkin/pull/4382)
|
||||
- Renamed “Auto Pause” preference to “Pause on Unfocus” for clarity. ([52be941](https://github.com/FunkinCrew/Funkin/commit/52be941b4503da0ac76918e2482ab1804866f2cf)) - by @JackXson-Real in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
|
||||
- Overhauled `FileUtil`, introducing various fixes, new functions, and sandboxing. ([95ade2a](https://github.com/FunkinCrew/Funkin/commit/95ade2a08b7709e8208ec1b3e123bf5b4308ba10)) - by @cyn0x8 in [#3032](https://github.com/FunkinCrew/Funkin/pull/3032)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Story mode medals are now awarded even without scoring a new personal best.
|
||||
- Highscores are now submitted to Newgrounds even without scoring a new personal best.
|
||||
- The Freeplay menu no longer displays songs without Erect variations when returning from an Erect variation song.
|
||||
- Fixed Freeplay DJ animations for Boyfriend and Pico when idling (properly this time).
|
||||
- Alternate instrumentals for Cocoa, Senpai, Roses, and Stress are now locked until their Pico Mix is beaten.
|
||||
- The Roses Pico alternate instrumental is now offset to align with the original song's voices.
|
||||
- Entering Stress or Stress (Pico Mix) a second time no longer crashes the game.
|
||||
- Restarting Stress or Stress (Pico Mix) no longer spawns too many Tankmen in the background.
|
||||
- Pico's game over animation now displays properly on Ugh (Pico Mix) and Guns (Pico Mix).
|
||||
- Tankman's death lines now play for Boyfriend in Ugh, Guns, and Ugh Erect.
|
||||
- Week 6 dialogue no longer plays for all non-default variations when entered through Freeplay.
|
||||
- Nene (Pixel) now plays her knife raising animation when the player has low health.
|
||||
- A-Bot's visualizer is now blank before the song starts.
|
||||
- Debug editor tooltips no longer display behind windows.
|
||||
- Clearing save data no longer crashes the game.
|
||||
- Adding custom parameters to note kinds no longer crashes the Chart Editor. ([0a7bd31](https://github.com/FunkinCrew/Funkin/commit/0a7bd3111f59efbecfe097f8bbcfdefa5ace299d)) - by @Lasercar in [#136](https://github.com/FunkinCrew/funkin.assets/pull/136)
|
||||
- Improved performance in the Freeplay menu before entering a song. ([2b7254f](https://github.com/FunkinCrew/Funkin/commit/2b7254fbd2356e9da91e216f178a88f17874a6eb)) - by @superpowers04 in [#4729](https://github.com/FunkinCrew/Funkin/pull/4729)
|
||||
- Prevented a crash by capping Freeplay and Results screen score displays at their largest possible values (actual scores are not capped). ([51324e9](https://github.com/FunkinCrew/Funkin/commit/51324e9c283c43ca861d3369ba9b3e1db9c89765)) - by @KoloInDaCrib in [#3634](https://github.com/FunkinCrew/Funkin/pull/3634)
|
||||
- The Animation Editor no longer crashes when opened. ([b40b4b0](https://github.com/FunkinCrew/Funkin/commit/b40b4b03692910afceff361cf6ca3298fd787e3c)) - by @sector-a in [#4582](https://github.com/FunkinCrew/Funkin/pull/4582)
|
||||
- Disabled navigation in the Options menu while the Clear Save Data prompt is open. ([855deb5](https://github.com/FunkinCrew/Funkin/commit/855deb58280e850c0ad1581807c70f05d6b6a2cb)) - by @KoloInDaCrib in [#4703](https://github.com/FunkinCrew/Funkin/pull/4703)
|
||||
- Save data is no longer wiped after failing to load the current save. ([068c9fb](https://github.com/FunkinCrew/Funkin/commit/068c9fb43d49ff6ea13e5a73f77a42207954be2c)) - by @KoloInDaCrib in [#4574](https://github.com/FunkinCrew/Funkin/pull/4574)
|
||||
- Selecting a different type of event in the Chart Editor no longer crashes or resets the event to its default values. ([71ed154](https://github.com/FunkinCrew/Funkin/commit/71ed154b81abbd002d78c09c2dada8a2ad3fa73e)) - by @KoloInDaCrib in [#3913](https://github.com/FunkinCrew/Funkin/pull/3913)
|
||||
- Deleting a stacked event in the Chart Editor now properly removes the deleted event’s sprite. ([3ad14ba](https://github.com/FunkinCrew/Funkin/commit/3ad14bac32096e45259997ab9957bec5fecf296f)) - by @NotHyper-474 in [#4724](https://github.com/FunkinCrew/Funkin/pull/4724)
|
||||
- Patched a security vulnerability. ([53dec05](https://github.com/FunkinCrew/Funkin/commit/53dec057bee0a913d60e0c70d45dbb59a58620b0)) - by @nebulazorua in [#4740](https://github.com/FunkinCrew/Funkin/pull/4740)
|
||||
- Added null safety to a bunch of classes.
|
||||
- Even more tiny bug fixes.
|
||||
|
||||
## New Contributors for 0.6.3
|
||||
|
||||
* @JackXson-Real made their first contribution in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
|
||||
* @VioletSnowLeopard made their first contribution in [#4382](https://github.com/FunkinCrew/Funkin/pull/4382)
|
||||
* @superpowers04 made their first contribution in [#4729](https://github.com/FunkinCrew/Funkin/pull/4729)
|
||||
* @ShadzXD made their first contribution in [#62](https://github.com/FunkinCrew/Funkin/pull/4729)
|
||||
|
||||
|
||||
|
||||
## [0.6.2] - 2025-03-31
|
||||
|
||||
### Added
|
||||
@@ -58,7 +185,7 @@ The Pit Stop 2 update!
|
||||
- Added an option to launch the game in fullscreen. ([ee53ccd](https://github.com/FunkinCrew/Funkin/commit/ee53ccd32721e0790adfe82c60d4aca419db0a7f)) - by @AbnormalPoof in [#3738](https://github.com/FunkinCrew/Funkin/pull/3738)
|
||||
- Added on-screen descriptions for each item in the Preferences menu. ([a17b0e8](https://github.com/FunkinCrew/Funkin/commit/a17b0e8b3cc1d56fcdc0b51eaca9fd57cdb5bce0)) - by @anysad in [#3872](https://github.com/FunkinCrew/Funkin/pull/3872)
|
||||
- Added precise scrolling in the Chart Editor using Ctrl-Mouse Wheel. ([0d8e4a5](https://github.com/FunkinCrew/Funkin/commit/0d8e4a53305d6d069454812766300122f3581e31)) - by @ninjamuffin99 in [#3806](https://github.com/FunkinCrew/Funkin/pull/3806)
|
||||
- Added a “None” option to the character selector in the Chart Editor. ([d9637d3](https://github.com/FunkinCrew/Funkin/commit/d9637d3a19466b9fc68a102676c46a74ef504909)) - by @Lasercar in [#4279](https://github.com/FunkinCrew/Funkin/pull/4279)
|
||||
- Added a “None” option to the character selector in the Chart Editor. ([9c2ef02](https://github.com/FunkinCrew/Funkin/commit/9c2ef0236818883ad1275571dac49eab70ca0ea0)) - by @Lasercar in [#4279](https://github.com/FunkinCrew/Funkin/pull/4279)
|
||||
- Added the ability to flip the character in the Animation Editor. ([de02137](https://github.com/FunkinCrew/Funkin/commit/de02137d7c7d1779e85aeda34743f506a5b9cc27)) - by @AbnormalPoof in [#3028](https://github.com/FunkinCrew/Funkin/pull/3028)
|
||||
- Added offsets support for album titles. ([69d8570](https://github.com/FunkinCrew/Funkin/commit/69d8570a9eb06011ed6dd95fcbef83d90f7f8684)) - by @AbnormalPoof in [#3618](https://github.com/FunkinCrew/Funkin/pull/3618)
|
||||
- Added three new properties to stage data: `angle`, `scroll`, and `alpha`. ([ff56b19](https://github.com/FunkinCrew/Funkin/commit/ff56b1948aef42bbb6bb4ede4f9b2012d49ab044)) - by @AbnormalPoof in [#3720](https://github.com/FunkinCrew/Funkin/pull/3720)
|
||||
@@ -77,7 +204,7 @@ The Pit Stop 2 update!
|
||||
- For example, the Zoom Camera event can now be set to 0.9857.
|
||||
- Lots of improvements to GitHub issue and pull request organization. - by @Hundrec and @AbnormalPoof
|
||||
- Overhauled the Changelog to improve readability and properly credit contributors. ([4383fcf](https://github.com/FunkinCrew/Funkin/commit/4383fcf32c280a1c0ee7b9c80d255611d497cabc)) - by @Hundrec in [#4296](https://github.com/FunkinCrew/Funkin/pull/4296) and [#4298](https://github.com/FunkinCrew/Funkin/pull/4298)
|
||||
- Made various improvements to the screenshot function. ([9ce7bbc](https://github.com/FunkinCrew/Funkin/commit/9ce7bbcfbb8f30ae120c876194f89bc4c787f585)) - by @Lasercar in [#4082](https://github.com/FunkinCrew/Funkin/pull/4082)
|
||||
- Made various improvements to the screenshot plugin. ([868932c](https://github.com/FunkinCrew/Funkin/commit/868932cd138fad4be5b541cbea3110e30479057b)) - by @Lasercar in [#4082](https://github.com/FunkinCrew/Funkin/pull/4082)
|
||||
- Accept keybinds (Z and Space by default) can now be used to exit the Results screen. ([edb270d](https://github.com/FunkinCrew/Funkin/commit/edb270d15e41784dccbf75639ac731840e80fe23)) - by @JVNpixels in [#3799](https://github.com/FunkinCrew/Funkin/pull/3799)
|
||||
- Reordered UI keybinds in the controls menu for consistency. ([a01bcc3](https://github.com/FunkinCrew/Funkin/commit/a01bcc3da836ec52851ca9de13ef459daf61269a)) - by @lemz1 in [#3027](https://github.com/FunkinCrew/Funkin/pull/3027)
|
||||
- New save files now have default Freeplay controls for gamepads. ([2b7f62e](https://github.com/FunkinCrew/Funkin/commit/2b7f62edd33de5527e259d9e5643f926d35da734)) - by @MrMadera in [#3934](https://github.com/FunkinCrew/Funkin/pull/3934)
|
||||
@@ -98,16 +225,16 @@ The Pit Stop 2 update!
|
||||
- Reduced stuttering when resyncing instrumental and voices tracks. ([22d41d2](https://github.com/FunkinCrew/Funkin/commit/22d41d21b88acb7422a0afcda8414682710bd2ed)) - by @TechnikTil in [#3955](https://github.com/FunkinCrew/Funkin/pull/3955)
|
||||
- Songs with only instrumental tracks no longer stutter. ([dfe02ec](https://github.com/FunkinCrew/Funkin/commit/dfe02ec668b61d6308f459c978d12a7487f9dc28)) - by @KoloInDaCrib in [#3861](https://github.com/FunkinCrew/Funkin/pull/3861)
|
||||
- The debug mouse cursor no longer flickers before the Title Screen loads. ([1c12b84](https://github.com/FunkinCrew/Funkin/commit/1c12b8467eca350eb28138473360d5358fa620e2)) - by @sphis-Sinco in [#3881](https://github.com/FunkinCrew/Funkin/pull/3881)
|
||||
- Unbound keys now display as [N/A] instead of crashing the game. ([94e5d3c](https://github.com/FunkinCrew/Funkin/commit/94e5d3ca258cbaa5d9ab6f0cb26feda83b3433a2)) - by @NotHyper-474 in [#4355](https://github.com/FunkinCrew/Funkin/pull/4355)
|
||||
- Unbound keys now display as [N/A] instead of crashing the game. ([099c309](https://github.com/FunkinCrew/Funkin/commit/099c309f9babdc1ea99b7dbed3fdccf1e952fc8e)) - by @NotHyper-474 in [#4355](https://github.com/FunkinCrew/Funkin/pull/4355)
|
||||
- Songs can no longer be spam-selected after selecting an instrumental in Freeplay. ([0e0c4ae](https://github.com/FunkinCrew/Funkin/commit/0e0c4aeb7745cfb9479685ccbb635cf3743cddbb)) - by @AbnormalPoof in [#3866](https://github.com/FunkinCrew/Funkin/pull/3866)
|
||||
- The Random capsule can now switch to Erect/Nightmare difficulties in Freeplay. ([6f32747](https://github.com/FunkinCrew/Funkin/commit/6f327474f30374f50774f432ea3ccb6d02579445)) - by @KoloInDaCrib in [#3838](https://github.com/FunkinCrew/Funkin/pull/3838)
|
||||
- The Random capsule can now switch to Erect/Nightmare difficulties in Freeplay. ([a90b911](https://github.com/FunkinCrew/Funkin/commit/a90b911653a1beaba57d64b1f05b840109fec42b)) - by @KoloInDaCrib in [#3838](https://github.com/FunkinCrew/Funkin/pull/3838)
|
||||
- Fixed a rare bug where a song would not register as beaten. ([a3e2373](https://github.com/FunkinCrew/Funkin/commit/a3e23733db104b1ef00cfcff17db3a5d032a4d67)) - by @AbnormalPoof in [#3820](https://github.com/FunkinCrew/Funkin/pull/3820)
|
||||
- The difficulty graphic on the Results screen no longer cuts off incorrectly. ([b13bf05](https://github.com/FunkinCrew/Funkin/commit/b13bf05d16ff2977309e0c7ba3f049c0134e8902)) - by @AbnormalPoof in [#4161](https://github.com/FunkinCrew/Funkin/pull/4161)
|
||||
- Four-digit long Total Notes values in the Results screen no longer overflow to the right. ([2be4c0c](https://github.com/FunkinCrew/Funkin/commit/2be4c0c7196e018a43f9697f015179f3efc3fcdb)) - by @Hundrec in [#4356](https://github.com/FunkinCrew/Funkin/pull/4356)
|
||||
- Four-digit long Total Notes values in the Results screen no longer overflow to the right. ([91a594c](https://github.com/FunkinCrew/Funkin/commit/91a594cc858ed086cd2146a1ac5d2379c6fdd27a)) - by @Hundrec in [#4356](https://github.com/FunkinCrew/Funkin/pull/4356)
|
||||
- The Character Select screen no longer plays the unlock animation for some locked characters. ([7058126](https://github.com/FunkinCrew/Funkin/commit/7058126e99adb55e43f5f487b007d3efa9f324d5)) - by @AbnormalPoof in [#3748](https://github.com/FunkinCrew/Funkin/pull/3748)
|
||||
- All time signatures in the Chart Editor now display the correct number of beat/step tick lines. ([e570dfb](https://github.com/FunkinCrew/Funkin/commit/e570dfb8e754f9cb29ac2d8fff6e8513bc68b630)) - by @Keoiki in [#2860](https://github.com/FunkinCrew/Funkin/pull/2860)
|
||||
- The Debug menu now opens with the correct camera position. ([090ddd1](https://github.com/FunkinCrew/Funkin/commit/090ddd1f1c2aa48fdb83127b2235041643c99af5)) - by @ninjamuffin99 in [#3769](https://github.com/FunkinCrew/Funkin/pull/3769)
|
||||
- Removed some Freeplay variables left over from legacy versions. ([6f6529f](https://github.com/FunkinCrew/Funkin/commit/6f6529f3c460721755dfdd0a7a26578c0de429d2)) - by @AbnormalPoof in [#4370](https://github.com/FunkinCrew/Funkin/pull/4370)
|
||||
- Removed an unused Freeplay class left over from legacy versions. ([abe4ac8](https://github.com/FunkinCrew/Funkin/commit/abe4ac8485539cbebe527a9a75698950232b68d2)) - by @AbnormalPoof in [#4370](https://github.com/FunkinCrew/Funkin/pull/4370)
|
||||
- Blacklisted an additional class for security. ([3492d41](https://github.com/FunkinCrew/Funkin/commit/3492d412c65c7f3fd61e6fc6c9410d8467122ab0)) - by @AbnormalPoof in [#4074](https://github.com/FunkinCrew/Funkin/pull/4074)
|
||||
- Removed an unused class from Polymod blacklist. ([06c12e3](https://github.com/FunkinCrew/Funkin/commit/06c12e36c6bd6df4e2be32a3bec540172e79e162)) - by @AbnormalPoof in [#3729](https://github.com/FunkinCrew/Funkin/pull/3729)
|
||||
- Many additional small bug fixes.
|
||||
@@ -397,7 +524,7 @@ The Playable Pico Update!
|
||||
- Botplay mode now handles the player's animations properly during hold notes. ([thanks Hundrec!](https://github.com/FunkinCrew/Funkin/pull/2683))
|
||||
- Camera movement now pauses when the game is paused. ([thanks Matriculaso!](https://github.com/FunkinCrew/Funkin/pull/2684))
|
||||
- Pico's gameplay sprite no longer appears on the gameover screen when dying from an explosion in 2hot.
|
||||
- Freeplay previews now properly fade in volume during the BF idle animation.
|
||||
- Freeplay previews now properly fade in volume during the DJ's idle animation.
|
||||
- DadBattle no longer incorrectly appears as DadBattle Erect when returning to Freeplay on Hard.
|
||||
- 2hot now appears under the "#" category in Freeplay menu.
|
||||
- The Chart Editor no longer crashes when selecting an event with the Event toolbox open.
|
||||
|
||||
+1
-1
Submodule art updated: 78dc310219...8402339e2e
+1
-1
Submodule assets updated: ac932c19f9...c108a7ff0d
+7
-6
@@ -13,19 +13,20 @@
|
||||
- NOTE: By performing this operation, you are downloading Content which is proprietary and protected by national and international copyright and trademark laws. See [the LICENSE.md file for the Funkin.assets](https://github.com/FunkinCrew/funkin.assets/blob/main/LICENSE.md) repo for more information.
|
||||
5. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json
|
||||
6. Run `hmm install` to install all haxelibs of the current branch
|
||||
7. Run `haxelib run lime setup` to set up Lime
|
||||
8. Perform additional platform setup
|
||||
- If you have issues installing Lime, try utilizing Funkin's patched libraries for hmm and haxelib: `haxelib --global git haxelib https://github.com/FunkinCrew/haxelib.git` and `haxelib --global git hmm https://github.com/FunkinCrew/hmm.git`
|
||||
8. Run `haxelib run lime setup` to set up Lime
|
||||
9. Perform additional platform setup
|
||||
- For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe)
|
||||
- When prompted, select "Individual Components" and make sure to download the following:
|
||||
- MSVC v143 VS 2022 C++ x64/x86 build tools
|
||||
- Windows 10/11 SDK
|
||||
- Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/)
|
||||
- Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/)
|
||||
- Note: Funkin's fork currently doesn't come with the necessary binaries so you'll have to rebuild Lime. See [Troubleshooting](TROUBLESHOOTING.md#lime-related-issues).
|
||||
- One of Funkin's dependencies uses libVLC, which requires you to install some packages to be able to compile: `sudo apt install libvlc-dev libvlccore-dev libvlccore9`
|
||||
- One of Funkin's dependencies uses libVLC, which requires you to install some development packages to be able to compile.
|
||||
Command for Ubuntu/Debian based systems: `sudo apt install libvlc-dev libvlccore-dev libvlccore9`
|
||||
- HTML5: Compiles without any extra setup
|
||||
9. If you are targeting for native, you may need to run `lime rebuild <PLATFORM>` and `lime rebuild <PLATFORM> -debug`
|
||||
10. `lime test <PLATFORM>` to build and launch the game for your platform (for example, `lime test windows`)
|
||||
10. If you are targeting for native, you may need to run `lime rebuild <PLATFORM>` and `lime rebuild <PLATFORM> -debug`
|
||||
11. `lime test <PLATFORM>` to build and launch the game for your platform (for example, `lime test windows`)
|
||||
|
||||
## Build Flags
|
||||
|
||||
|
||||
+20
-14
@@ -74,6 +74,7 @@ Here's a list of commonly suggested features and the reasons why they won't be a
|
||||
| Toggleable Ghost Tapping | https://github.com/FunkinCrew/Funkin/pull/2564#issuecomment-2119701802 |
|
||||
| Perfectly Centered Strumlines | _same as above^_ |
|
||||
| MultiKey, 9k, More than 4 keys, etc. | https://github.com/FunkinCrew/Funkin/issues/4243#issuecomment-2692371969 |
|
||||
| Flashy Combo Milestones | https://github.com/FunkinCrew/Funkin/pull/4700#issuecomment-2798916579 |
|
||||
| Losing Icons for DD and Parents | https://github.com/FunkinCrew/Funkin/issues/3048#issuecomment-2243491536 |
|
||||
| Playable GF / Speaker BF / Speaker Pico | https://github.com/FunkinCrew/Funkin/issues/2953#issuecomment-2216985230 |
|
||||
| Fresh (Chill Mix) as Title Screen Music | https://github.com/FunkinCrew/Funkin/pull/4282#issuecomment-2709334718 |
|
||||
@@ -154,11 +155,23 @@ Choose the `develop` branch if you modify:
|
||||
Choosing the right base branch helps keep your commit history clean and avoid merge conflicts.
|
||||
Once you’re satisfied with the changes you’ve made, open a PR and base it on the same branch you previously chose.
|
||||
|
||||
## Merge conflicts and rebasing
|
||||
## Maintaining your pull request
|
||||
Keeping your pull request clean and easy to review increases the chance that it will be accepted!
|
||||
|
||||
Our maintenance policy is as follows:
|
||||
- If we require changes to your PR, we will label your PR `status: needs revision`.
|
||||
- We may also leave a comment under your PR specifying what changes you should make.
|
||||
- If you receive a comment, you have 90 days to implement the requested changes.
|
||||
- After this period, your PR will be closed due to inactivity and labeled `status: stale`.
|
||||
- Even after your PR is closed, you may request for us to reopen it. Just be sure to address the issues!
|
||||
|
||||
This policy ensures that PRs awaiting review are up to date and ready to merge.
|
||||
|
||||
### Merge conflicts and rebasing
|
||||
Some game updates introduce significant breaking changes that may create merge conflicts in your PR. To resolve them, you will need to update or rebase your PR.
|
||||
|
||||
Most merge conflicts are small and will only require you to modify a few files to resolve them.
|
||||
However, some changes are so big that your commit history will look like a mess!
|
||||
However, some changes are so big that your commit history will be a total mess!
|
||||
In this case, you will have to perform a [**rebase**](https://docs.github.com/en/get-started/using-git/about-git-rebase).
|
||||
This process reapplies your changes on top of the updated branch and cleanly resolves the merge conflicts.
|
||||
|
||||
@@ -392,22 +405,15 @@ If you simultaneously modify files from both repositories, then open two separat
|
||||
Be sure to choose `main` as the base branch for `funkin.assets` PRs, as no `develop` branch exists for that repository.
|
||||
|
||||
### Charting PRs
|
||||
Charting PRs make changes such as **adding/removing notes** or **adjusting the placement of song events**.
|
||||
Charting PRs make changes such as **adjusting chart metadata**.
|
||||
|
||||
This involves modifying one or several of the `funkin.assets` repository's `.json` chart files, found in the `preload/data/songs/` directory.
|
||||
|
||||
These PRs should only be opened in the `funkin.assets` repository.
|
||||
This involves modifying one or several of the `funkin.assets` repository's `*-metadata.json` files, found in the `preload/data/songs/` directory.
|
||||
|
||||
> [!CAUTION]
|
||||
> **No Major Recharts!** Any PR that makes major chart modifications will be rejected.
|
||||
> Keep your PRs to small tweaks and fixes.
|
||||
> We **do not** accept changes to the chart `.json` file itself.
|
||||
> If you find an error in the game's charts, please open an issue in the `Funkin` repo, and the Funkin' Crew will review it and make tweaks themselves as necessary.
|
||||
|
||||
Here are some guidelines for opening a Charting PR:
|
||||
- **Explain the issue.** Which song, variation, difficulty, and section/timestamp is the problem in? Help us understand with screenshots and videos.
|
||||
- **Show your changes.** How does the chart look with your changes? Provide screenshots and videos here as well.
|
||||
- **Minimize the diff.** If your changes are very small (e.g. a few notes), do not re-export the chart using the Chart Editor. Instead, manually edit the `.json` chart files to help GitHub display your changes cleanly.
|
||||
|
||||
If your PR is accepted, you will be credited as a GitHub contributor (but not as a charter in the Pause Menu).
|
||||
These PRs should only be opened in the `funkin.assets` repository.
|
||||
|
||||
# Closing
|
||||
Thank you for reading the Contributing Guide.
|
||||
|
||||
@@ -12,9 +12,6 @@ Most of this functionality is only available on debug builds of the game!
|
||||
|
||||
- `CTRL-SHIFT-L`: ***FORCE CRASH***: Immediately crash the game with a detailed crash log and a stack trace.
|
||||
|
||||
## Loading State
|
||||
- `SPACE`: Trace callbacks.
|
||||
|
||||
## **Play State**
|
||||
- `H`: ***HIDE UI***: Makes the user interface invisible. Works in Pause Menu, great for screenshots.
|
||||
- `1`: ***END SONG***: Immediately ends the song and moves to Results Screen on Freeplay, or next song on Story Mode.
|
||||
@@ -31,8 +28,6 @@ Most of this functionality is only available on debug builds of the game!
|
||||
- `T`: ***TEST RANK ANIMATION***: Play the Gold Perfect rank animation on the currently selected song.
|
||||
- `SHIFT-ACCEPT`: ***BOTPLAY MODE***: Start a song with Botplay mode enabled.
|
||||
- `CTRL-Switch Song`: Play the first unlocked alternate instrumental as the song preview.
|
||||
- `CTRL-LEFT`/`RIGHT`/`UP`/`DOWN`: ***OFFSET BF DJ IN CARTOON STATE***: While BF DJ's TV animation is playing, offset the DJ by 1 pixel in the opposite direction. `SHIFT` to move 10 pixels, `ALT` to move 0.1 pixels.
|
||||
- `CTRL-C`: ***TRIGGER DJ CARTOON***: Play the Freeplay DJ's cartoon animation. Press again to return to its regular animation.
|
||||
|
||||
## **Title State**
|
||||
- `ESCAPE`: ***QUIT***: Quit the game.
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
(NOTE: Make sure you do this via `hmm` (e.g `hmm reinstall -f lime`) to guarantee you get Funkin's version of Lime.)
|
||||
|
||||
- `Uncaught exception - Could not find lime.ndll.` ... `Advanced users may run "lime rebuild cpp" instead.`
|
||||
- Usually specific to Linux. Running the commands below should fix it.
|
||||
- If on Linux:
|
||||
- The binaries GLibC version might more recent than the one your system supports. Running the commands below should fix it.
|
||||
```
|
||||
cd .haxelib/lime/git
|
||||
git submodule init
|
||||
@@ -47,4 +48,6 @@
|
||||
sudo apt install libgl1-mesa-dev libglu1-mesa-dev g++ g++-multilib gcc-multilib libasound2-dev libx11-dev libxext-dev libxi-dev libxrandr-dev libxinerama-dev libpulse-dev
|
||||
lime rebuild cpp -64 -release -clean
|
||||
```
|
||||
For Windows or MacOS you can download pre-built binaries from [Funkin's Lime](https://github.com/FunkinCrew/lime/tree/dev-funkin/ndll).
|
||||
|
||||
- The binaries are missing for some reason. You can download pre-built binaries from [Funkin's Lime](https://github.com/FunkinCrew/lime/tree/dev-funkin/ndll).
|
||||
You should copy them to `.haxelib/lime/git/ndll/<PLATFORM>64/`, where `<PLATFORM>` is the current platform you're on, which can be either Windows, Linux or MacOS.
|
||||
|
||||
+2
-3
@@ -25,7 +25,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.6.3";
|
||||
static final VERSION:String = "0.6.4";
|
||||
|
||||
/**
|
||||
* The game's name. Used as the default window title.
|
||||
@@ -773,13 +773,12 @@ class Project extends HXProject {
|
||||
addAssetLibrary("weekend1", shouldEmbed, shouldPreload);
|
||||
addAssetPath("assets/weekend1", "assets/weekend1", "weekend1", ["*"], exclude, shouldEmbed);
|
||||
|
||||
// Art asset library (where README and CHANGELOG pull from)
|
||||
// Art asset library (where README pulls from)
|
||||
var shouldEmbedArt = false;
|
||||
var shouldPreloadArt = false;
|
||||
addAssetLibrary("art", shouldEmbedArt, shouldPreloadArt);
|
||||
addAsset("art/readme.txt", "do NOT readme.txt", "art", shouldEmbedArt);
|
||||
addAsset("LICENSE.md", "LICENSE.md", "art", shouldEmbedArt);
|
||||
addAsset("CHANGELOG.md", "CHANGELOG.md", "art", shouldEmbedArt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-8
@@ -62,14 +62,6 @@ class Main extends Sprite
|
||||
|
||||
function init(?event:Event):Void
|
||||
{
|
||||
#if web
|
||||
// set this variable (which is a function) from the lime version at lime/_internal/backend/html5/HTML5Application.hx
|
||||
// The framerate cap will more thoroughly initialize via Preferences in InitState.hx
|
||||
funkin.Preferences.lockedFramerateFunction = untyped js.Syntax.code("window.requestAnimationFrame");
|
||||
#end
|
||||
|
||||
WindowUtil.setVSyncMode(funkin.Preferences.vsyncMode);
|
||||
|
||||
if (hasEventListener(Event.ADDED_TO_STAGE))
|
||||
{
|
||||
removeEventListener(Event.ADDED_TO_STAGE, init);
|
||||
@@ -107,6 +99,17 @@ class Main extends Sprite
|
||||
|
||||
// George recommends binding the save before FlxGame is created.
|
||||
Save.load();
|
||||
|
||||
// Don't call anything from the preferences until the save is loaded!
|
||||
#if web
|
||||
// set this variable (which is a function) from the lime version at lime/_internal/backend/html5/HTML5Application.hx
|
||||
// The framerate cap will more thoroughly initialize via Preferences in InitState.hx
|
||||
funkin.Preferences.lockedFramerateFunction = untyped js.Syntax.code("window.requestAnimationFrame");
|
||||
#end
|
||||
|
||||
WindowUtil.setVSyncMode(funkin.Preferences.vsyncMode);
|
||||
|
||||
|
||||
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, Preferences.framerate, Preferences.framerate, skipSplash, startFullscreen);
|
||||
|
||||
// FlxG.game._customSoundTray wants just the class, it calls new from
|
||||
|
||||
@@ -185,15 +185,41 @@ class Preferences
|
||||
|
||||
static function get_vsyncMode():lime.ui.WindowVSyncMode
|
||||
{
|
||||
return Save?.instance?.options?.vsyncMode ?? lime.ui.WindowVSyncMode.OFF;
|
||||
var value = Save?.instance?.options?.vsyncMode ?? "Off";
|
||||
|
||||
return switch (value)
|
||||
{
|
||||
case "Off":
|
||||
lime.ui.WindowVSyncMode.OFF;
|
||||
case "On":
|
||||
lime.ui.WindowVSyncMode.ON;
|
||||
case "Adaptive":
|
||||
lime.ui.WindowVSyncMode.ADAPTIVE;
|
||||
default:
|
||||
lime.ui.WindowVSyncMode.OFF;
|
||||
};
|
||||
}
|
||||
|
||||
static function set_vsyncMode(value:lime.ui.WindowVSyncMode):lime.ui.WindowVSyncMode
|
||||
{
|
||||
var string;
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case lime.ui.WindowVSyncMode.OFF:
|
||||
string = "Off";
|
||||
case lime.ui.WindowVSyncMode.ON:
|
||||
string = "On";
|
||||
case lime.ui.WindowVSyncMode.ADAPTIVE:
|
||||
string = "Adaptive";
|
||||
default:
|
||||
string = "Off";
|
||||
};
|
||||
|
||||
WindowUtil.setVSyncMode(value);
|
||||
|
||||
var save:Save = Save.instance;
|
||||
save.options.vsyncMode = value;
|
||||
save.options.vsyncMode = string;
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@ class Medals
|
||||
if (NewgroundsClient.instance.isLoggedIn())
|
||||
{
|
||||
var medalList = NewgroundsClient.instance.medals;
|
||||
if (medalList == null) return;
|
||||
@:privateAccess
|
||||
if (medalList == null || medalList._map == null) return;
|
||||
|
||||
var medalData:Null<MedalData> = medalList.get(medal.getId());
|
||||
@:privateAccess
|
||||
@@ -67,7 +68,7 @@ class Medals
|
||||
NewgroundsMedalPlugin.play(medalData.value, medalData.name, medalGraphic);
|
||||
});
|
||||
#else
|
||||
if (medalJSON == null) loadMedalJSON();
|
||||
if ((medalJSON?.length ?? 0) == 0) loadMedalJSON();
|
||||
// We have to use a medal image from the game files. We use a Base64 encoded image that NG spits out.
|
||||
// TODO: Wait, don't they give us the medal icon?
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ import funkin.util.tools.ICloneable;
|
||||
import funkin.util.flixel.sound.FlxPartialSound;
|
||||
import funkin.Paths.PathsFunction;
|
||||
import lime.app.Promise;
|
||||
import lime.media.AudioSource;
|
||||
import openfl.events.Event;
|
||||
import openfl.media.Sound;
|
||||
import openfl.media.SoundChannel;
|
||||
import openfl.media.SoundMixer;
|
||||
|
||||
#if (openfl >= "8.0.0")
|
||||
@@ -108,6 +112,11 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
return _waveformData;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the game will forcefully add this sound's channel to the list of playing sounds.
|
||||
*/
|
||||
public var important:Bool = false;
|
||||
|
||||
/**
|
||||
* Are we in a state where the song should play but time is negative?
|
||||
*/
|
||||
@@ -145,6 +154,14 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
else
|
||||
{
|
||||
super.update(elapsedSec);
|
||||
|
||||
@:privateAccess
|
||||
{
|
||||
if (important && _channel != null && !SoundMixer.__soundChannels.contains(_channel))
|
||||
{
|
||||
SoundMixer.__soundChannels.push(_channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,13 +448,14 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
* @param persist Whether to keep this `FunkinSound` between states, or destroy it.
|
||||
* @param onComplete Called when the sound finished playing.
|
||||
* @param onLoad Called when the sound finished loading. Called immediately for succesfully loaded embedded sounds.
|
||||
* @param important If `true`, the sound channel will forcefully be added onto the channel array, even if full. Use sparingly!
|
||||
* @return A `FunkinSound` object, or `null` if the sound could not be loaded.
|
||||
*/
|
||||
public static function load(embeddedSound:FlxSoundAsset, volume:Float = 1.0, looped:Bool = false, autoDestroy:Bool = false, autoPlay:Bool = false,
|
||||
persist:Bool = false, ?onComplete:Void->Void, ?onLoad:Void->Void):Null<FunkinSound>
|
||||
persist:Bool = false, ?onComplete:Void->Void, ?onLoad:Void->Void, important:Bool = false):Null<FunkinSound>
|
||||
{
|
||||
@:privateAccess
|
||||
if (SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS)
|
||||
if (SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS && !important)
|
||||
{
|
||||
FlxG.log.error('FunkinSound could not play sound, channels exhausted! Found ${SoundMixer.__soundChannels.length} active sound channels.');
|
||||
return null;
|
||||
@@ -462,6 +480,7 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
sound.volume = volume;
|
||||
sound.group = FlxG.sound.defaultSoundGroup;
|
||||
sound.persist = persist;
|
||||
sound.important = important;
|
||||
|
||||
// Make sure to add the sound to the list.
|
||||
// If it's already in, it won't get re-added.
|
||||
@@ -534,15 +553,50 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
this._label = 'unknown';
|
||||
}
|
||||
|
||||
@:access(openfl.media.Sound)
|
||||
@:access(openfl.media.SoundChannel)
|
||||
@:access(openfl.media.SoundMixer)
|
||||
override function startSound(startTime:Float)
|
||||
{
|
||||
if (!important)
|
||||
{
|
||||
super.startSound(startTime);
|
||||
return;
|
||||
}
|
||||
|
||||
_time = startTime;
|
||||
_paused = false;
|
||||
|
||||
if (_sound == null) return;
|
||||
|
||||
// Create a channel manually if the sound is considered important.
|
||||
var pan:Float = FlxMath.bound(SoundMixer.__soundTransform.pan + _transform.pan, -1, 1);
|
||||
var volume:Float = FlxMath.bound(SoundMixer.__soundTransform.volume * _transform.volume, 0, MAX_VOLUME);
|
||||
|
||||
var audioSource:AudioSource = new AudioSource(_sound.__buffer);
|
||||
audioSource.offset = Std.int(startTime);
|
||||
audioSource.gain = volume;
|
||||
|
||||
var position:lime.math.Vector4 = audioSource.position;
|
||||
position.x = pan;
|
||||
position.z = -1 * Math.sqrt(1 - Math.pow(pan, 2));
|
||||
audioSource.position = position;
|
||||
|
||||
_channel = new SoundChannel(_sound, audioSource, _transform);
|
||||
_channel.addEventListener(Event.SOUND_COMPLETE, stopped);
|
||||
pitch = _pitch;
|
||||
active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a sound effect once, then destroy it.
|
||||
* @param key
|
||||
* @param volume
|
||||
* @return A `FunkinSound` object, or `null` if the sound could not be loaded.
|
||||
*/
|
||||
public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Null<FunkinSound>
|
||||
public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void, important:Bool = false):Null<FunkinSound>
|
||||
{
|
||||
var result:Null<FunkinSound> = FunkinSound.load(key, volume, false, true, true, false, onComplete, onLoad);
|
||||
var result:Null<FunkinSound> = FunkinSound.load(key, volume, false, true, true, false, onComplete, onLoad, important);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ class FlxAtlasSprite extends FlxAnimate
|
||||
throw 'FlxAtlasSprite not initialized properly. Are you sure the path (${path}) exists?';
|
||||
}
|
||||
|
||||
onAnimationComplete.add(cleanupAnimation);
|
||||
|
||||
// This defaults the sprite to play the first animation in the atlas,
|
||||
// then pauses it. This ensures symbols are intialized properly.
|
||||
this.anim.play('');
|
||||
@@ -317,7 +319,6 @@ class FlxAtlasSprite extends FlxAnimate
|
||||
else if (fr != null && anim.curFrame != anim.length - 1)
|
||||
{
|
||||
anim.curFrame--;
|
||||
cleanupAnimation(currentAnimation ?? "");
|
||||
_onAnimationComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +80,10 @@ class PolymodHandler
|
||||
*/
|
||||
public static function loadAllMods():Void
|
||||
{
|
||||
#if sys
|
||||
// Create the mod root if it doesn't exist.
|
||||
createModRoot();
|
||||
#end
|
||||
trace('Initializing Polymod (using all mods)...');
|
||||
loadModsById(getAllModIds());
|
||||
}
|
||||
@@ -91,9 +93,10 @@ class PolymodHandler
|
||||
*/
|
||||
public static function loadEnabledMods():Void
|
||||
{
|
||||
#if sys
|
||||
// Create the mod root if it doesn't exist.
|
||||
createModRoot();
|
||||
|
||||
#end
|
||||
trace('Initializing Polymod (using configured mods)...');
|
||||
loadModsById(Save.instance.enabledModIds);
|
||||
}
|
||||
@@ -103,9 +106,10 @@ class PolymodHandler
|
||||
*/
|
||||
public static function loadNoMods():Void
|
||||
{
|
||||
#if sys
|
||||
// Create the mod root if it doesn't exist.
|
||||
createModRoot();
|
||||
|
||||
#end
|
||||
// We still need to configure the debug print calls etc.
|
||||
trace('Initializing Polymod (using no mods)...');
|
||||
loadModsById([]);
|
||||
|
||||
@@ -255,7 +255,7 @@ class Countdown
|
||||
var path = noteStyle.getCountdownSoundPath(step);
|
||||
if (path == null) return null;
|
||||
|
||||
return FunkinSound.playOnce(path, Constants.COUNTDOWN_VOLUME);
|
||||
return FunkinSound.playOnce(path, Constants.COUNTDOWN_VOLUME, null, null, true);
|
||||
}
|
||||
|
||||
public static function decrement(step:CountdownStep):CountdownStep
|
||||
|
||||
@@ -878,7 +878,6 @@ class PlayState extends MusicBeatSubState
|
||||
// so the song doesn't start too early :D
|
||||
Conductor.instance.update(-5000, false);
|
||||
|
||||
|
||||
// Reset camera zooming
|
||||
cameraBopIntensity = Constants.DEFAULT_BOP_INTENSITY;
|
||||
hudCameraZoomIntensity = (cameraBopIntensity - 1.0) * 2.0;
|
||||
@@ -898,16 +897,9 @@ class PlayState extends MusicBeatSubState
|
||||
Countdown.performCountdown();
|
||||
});
|
||||
|
||||
|
||||
// Reset the health icons.
|
||||
if (currentStage.getBoyfriend() != null)
|
||||
{
|
||||
currentStage.getBoyfriend().initHealthIcon(false);
|
||||
}
|
||||
if (currentStage.getDad() != null)
|
||||
{
|
||||
currentStage.getDad().initHealthIcon(true);
|
||||
}
|
||||
currentStage?.getBoyfriend()?.initHealthIcon(false);
|
||||
currentStage?.getDad()?.initHealthIcon(true);
|
||||
|
||||
needsReset = false;
|
||||
}
|
||||
@@ -956,7 +948,7 @@ class PlayState extends MusicBeatSubState
|
||||
// Attempt to pause the game.
|
||||
if ((controls.PAUSE || androidPause) && isInCountdown && mayPauseGame && !justUnpaused)
|
||||
{
|
||||
var event = new PauseScriptEvent(FlxG.random.bool(1 / 1000));
|
||||
var event = new PauseScriptEvent(FlxG.random.bool((1 / 1000) * 100));
|
||||
|
||||
dispatchEvent(event);
|
||||
|
||||
@@ -1146,7 +1138,7 @@ class PlayState extends MusicBeatSubState
|
||||
if (!isMinimalMode)
|
||||
{
|
||||
if (iconP1 != null) iconP1.updatePosition();
|
||||
if (iconP1 != null) iconP2.updatePosition();
|
||||
if (iconP2 != null) iconP2.updatePosition();
|
||||
}
|
||||
|
||||
// Transition to the game over substate.
|
||||
@@ -1429,9 +1421,10 @@ class PlayState extends MusicBeatSubState
|
||||
*/
|
||||
override function reloadAssets():Void
|
||||
{
|
||||
performCleanup();
|
||||
|
||||
funkin.modding.PolymodHandler.forceReloadAssets();
|
||||
lastParams.targetSong = SongRegistry.instance.fetchEntry(currentSong.id);
|
||||
this.remove(currentStage);
|
||||
LoadingState.loadPlayState(lastParams);
|
||||
}
|
||||
|
||||
@@ -1797,13 +1790,13 @@ class PlayState extends MusicBeatSubState
|
||||
// Position the player strumline on the right half of the screen
|
||||
playerStrumline.x = FlxG.width / 2 + Constants.STRUMLINE_X_OFFSET; // Classic style
|
||||
// playerStrumline.x = FlxG.width - playerStrumline.width - Constants.STRUMLINE_X_OFFSET; // Centered style
|
||||
playerStrumline.y = Preferences.downscroll ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
|
||||
playerStrumline.y = Preferences.downscroll ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET - noteStyle.getStrumlineOffsets()[1] : Constants.STRUMLINE_Y_OFFSET;
|
||||
playerStrumline.zIndex = 1001;
|
||||
playerStrumline.cameras = [camHUD];
|
||||
|
||||
// Position the opponent strumline on the left half of the screen
|
||||
opponentStrumline.x = Constants.STRUMLINE_X_OFFSET;
|
||||
opponentStrumline.y = Preferences.downscroll ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
|
||||
opponentStrumline.y = Preferences.downscroll ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET - noteStyle.getStrumlineOffsets()[1] : Constants.STRUMLINE_Y_OFFSET;
|
||||
opponentStrumline.zIndex = 1000;
|
||||
opponentStrumline.cameras = [camHUD];
|
||||
|
||||
@@ -2425,7 +2418,7 @@ class PlayState extends MusicBeatSubState
|
||||
dispatchEvent(event);
|
||||
|
||||
trace('Penalizing score by ${event.score} and health by ${event.healthChange} for dropping hold note (is combo break: ${event.isComboBreak})!');
|
||||
applyScore(event.score, 'miss', event.healthChange, event.isComboBreak);
|
||||
applyScore(event.score, '', event.healthChange, event.isComboBreak);
|
||||
|
||||
// Play the miss sound.
|
||||
vocals.playerVolume = 0;
|
||||
@@ -2736,7 +2729,7 @@ class PlayState extends MusicBeatSubState
|
||||
#end
|
||||
|
||||
// 9: Toggle the old icon.
|
||||
if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon();
|
||||
if (FlxG.keys.justPressed.NINE && iconP1 != null) iconP1.toggleOldIcon();
|
||||
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// PAGEUP: Skip forward two sections.
|
||||
@@ -2765,6 +2758,8 @@ class PlayState extends MusicBeatSubState
|
||||
Highscore.tallies.shit += 1;
|
||||
case 'miss':
|
||||
Highscore.tallies.missed += 1;
|
||||
default:
|
||||
// Nothing!
|
||||
}
|
||||
health += healthChange;
|
||||
if (isComboBreak)
|
||||
|
||||
@@ -488,7 +488,8 @@ class ResultState extends MusicBeatSubState
|
||||
FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24);
|
||||
// NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors.
|
||||
var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : (params.scoreData.tallies.sick +
|
||||
params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100;
|
||||
params.scoreData.tallies.good
|
||||
- params.scoreData.tallies.missed) / params.scoreData.tallies.totalNotes * 100;
|
||||
clearPercentTarget = Math.floor(clearPercentFloat);
|
||||
// Prevent off-by-one errors.
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class ClearPercentCounter extends FlxTypedSpriteGroup<FlxSprite>
|
||||
var seperatedScore:Array<Int> = [];
|
||||
var tempCombo:Int = Math.round(curNumber);
|
||||
|
||||
while (tempCombo != 0)
|
||||
while (tempCombo > 0 && tempCombo != 0)
|
||||
{
|
||||
seperatedScore.push(tempCombo % 10);
|
||||
tempCombo = Math.floor(tempCombo / 10);
|
||||
|
||||
@@ -384,7 +384,7 @@ class Scoring
|
||||
|
||||
// Final Grade = (Sick + Good - Miss) / (Total Notes)
|
||||
|
||||
var grade = (scoreData.tallies.sick + scoreData.tallies.good) / scoreData.tallies.totalNotes;
|
||||
var grade = (scoreData.tallies.sick + scoreData.tallies.good - scoreData.tallies.missed) / scoreData.tallies.totalNotes;
|
||||
|
||||
if (grade == Constants.RANK_PERFECT_THRESHOLD)
|
||||
{
|
||||
|
||||
@@ -801,7 +801,7 @@ class SongDifficulty
|
||||
{
|
||||
var suffix:String = (instId != '') ? '-$instId' : '';
|
||||
|
||||
FlxG.sound.music = FunkinSound.load(Paths.inst(this.song.id, suffix), volume, looped, false, true);
|
||||
FlxG.sound.music = FunkinSound.load(Paths.inst(this.song.id, suffix), volume, looped, false, true, false, null, null, true);
|
||||
|
||||
// Workaround for a bug where FlxG.sound.music.update() was being called twice.
|
||||
FlxG.sound.list.remove(FlxG.sound.music);
|
||||
@@ -948,16 +948,21 @@ class SongDifficulty
|
||||
for (playerVoice in playerVoiceList)
|
||||
{
|
||||
if (!Assets.exists(playerVoice)) continue;
|
||||
result.addPlayerVoice(FunkinSound.load(playerVoice));
|
||||
result.addPlayerVoice(FunkinSound.load(playerVoice, 1.0, false, false, false, false, null, null, true));
|
||||
}
|
||||
|
||||
// Add opponent vocals.
|
||||
for (opponentVoice in opponentVoiceList)
|
||||
{
|
||||
if (!Assets.exists(opponentVoice)) continue;
|
||||
result.addOpponentVoice(FunkinSound.load(opponentVoice));
|
||||
result.addOpponentVoice(FunkinSound.load(opponentVoice, 1.0, false, false, false, false, null, null, true));
|
||||
}
|
||||
|
||||
// Sometimes the sounds don't set their important value to true, so we have to do this manually.
|
||||
result.forEach(function(snd:FunkinSound) {
|
||||
snd.important = true;
|
||||
});
|
||||
|
||||
result.playerVoicesOffset = offsets.getVocalOffset(characters.player, instId);
|
||||
result.opponentVoicesOffset = offsets.getVocalOffset(characters.opponent, instId);
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ class Save
|
||||
zoomCamera: true,
|
||||
debugDisplay: false,
|
||||
autoPause: true,
|
||||
vsyncMode: lime.ui.WindowVSyncMode.OFF,
|
||||
vsyncMode: 'Off',
|
||||
strumlineBackgroundOpacity: 0,
|
||||
autoFullscreen: false,
|
||||
inputOffset: 0,
|
||||
@@ -1401,9 +1401,9 @@ typedef SaveDataOptions =
|
||||
|
||||
/**
|
||||
* If enabled, the game will utilize VSync (or adaptive VSync) on startup.
|
||||
* @default `OFF`
|
||||
* @default `Off`
|
||||
*/
|
||||
var vsyncMode:lime.ui.WindowVSyncMode;
|
||||
var vsyncMode:String;
|
||||
|
||||
/**
|
||||
* If >0, the game will display a semi-opaque background under the notes.
|
||||
|
||||
@@ -572,7 +572,7 @@ class DebugBoundingState extends FlxState
|
||||
_file.addEventListener(Event.COMPLETE, onSaveComplete);
|
||||
_file.addEventListener(Event.CANCEL, onSaveCancel);
|
||||
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
|
||||
_file.save(saveString,);
|
||||
_file.save(saveString, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1334,7 +1334,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
result = [];
|
||||
trace('Initializing blank chart for difficulty ' + selectedDifficulty);
|
||||
currentSongChartData.notes.set(selectedDifficulty, result);
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
@@ -1343,7 +1342,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
function set_currentSongChartNoteData(value:Array<SongNoteData>):Array<SongNoteData>
|
||||
{
|
||||
currentSongChartData.notes.set(selectedDifficulty, value);
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -1636,9 +1634,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
noteTooltipsDirty = true;
|
||||
notePreviewViewportBoundsDirty = true;
|
||||
|
||||
// Make sure the difficulty we selected is in the list of difficulties.
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
|
||||
return selectedDifficulty;
|
||||
}
|
||||
|
||||
@@ -3230,7 +3225,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
#if sys
|
||||
// TODO: Is there a way to open a folder and highlight a file in it?
|
||||
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), ChartEditorImportExportHandler.BACKUPS_PATH]);
|
||||
WindowUtil.openFolder(absoluteBackupsPath);
|
||||
FileUtil.openFolder(absoluteBackupsPath);
|
||||
return true;
|
||||
#else
|
||||
trace('No file system access, cannot open backups folder.');
|
||||
@@ -5843,6 +5838,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
{
|
||||
uiCamera.revive();
|
||||
FlxG.cameras.reset(uiCamera);
|
||||
uiCamera.onResize();
|
||||
|
||||
add(this.root);
|
||||
}
|
||||
|
||||
@@ -91,6 +91,8 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
|
||||
#end
|
||||
|
||||
linkRecentChart.onClick = function(_event) {
|
||||
linkRecentChart.hide();
|
||||
|
||||
this.hideDialog(DialogButton.CANCEL);
|
||||
|
||||
// Load chart from file
|
||||
|
||||
@@ -117,6 +117,12 @@ class ChartEditorImportExportHandler
|
||||
{
|
||||
state.songMetadata = newSongMetadata;
|
||||
state.songChartData = newSongChartData;
|
||||
state.selectedDifficulty = state.availableDifficulties[0];
|
||||
|
||||
if (!newSongMetadata.exists(state.selectedVariation))
|
||||
{
|
||||
state.selectedVariation = Constants.DEFAULT_VARIATION;
|
||||
}
|
||||
|
||||
Conductor.instance.forceBPM(null); // Disable the forced BPM.
|
||||
Conductor.instance.instrumentalOffset = state.currentInstrumentalOffset; // Loads from the metadata.
|
||||
@@ -343,6 +349,12 @@ class ChartEditorImportExportHandler
|
||||
|
||||
var variations = state.availableVariations;
|
||||
|
||||
if (state.currentSongMetadata.playData.difficulties.pushUnique(state.selectedDifficulty))
|
||||
{
|
||||
// Just in case the user deleted all or didn't add a difficulty
|
||||
state.difficultySelectDirty = true;
|
||||
}
|
||||
|
||||
for (variation in variations)
|
||||
{
|
||||
var variationId:String = variation;
|
||||
|
||||
@@ -1267,7 +1267,7 @@ class StageEditorState extends UIState
|
||||
case "open folder":
|
||||
#if sys
|
||||
var absoluteBackupsPath:String = haxe.io.Path.join([Sys.getCwd(), BACKUPS_PATH]);
|
||||
WindowUtil.openFolder(absoluteBackupsPath);
|
||||
FileUtil.openFolder(absoluteBackupsPath);
|
||||
#end
|
||||
|
||||
case "test stage":
|
||||
|
||||
@@ -5,7 +5,6 @@ import haxe.ui.containers.dialogs.Dialog.DialogButton;
|
||||
import funkin.util.FileUtil;
|
||||
import haxe.io.Path;
|
||||
import funkin.util.DateUtil;
|
||||
import funkin.util.WindowUtil;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -57,7 +56,7 @@ class BackupAvailableDialog extends Dialog
|
||||
// :[
|
||||
#if sys
|
||||
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), StageEditorState.BACKUPS_PATH]);
|
||||
WindowUtil.openFolder(absoluteBackupsPath);
|
||||
FileUtil.openFolder(absoluteBackupsPath);
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ class CapsuleOptionsMenu extends FlxSpriteGroup
|
||||
|
||||
var busy:Bool = false;
|
||||
|
||||
var leftArrow:InstrumentalSelector;
|
||||
|
||||
var rightArrow:InstrumentalSelector;
|
||||
|
||||
public function new(parent:FreeplayState, x:Float = 0, y:Float = 0, instIds:Array<String>):Void
|
||||
{
|
||||
super(x, y);
|
||||
@@ -41,8 +45,8 @@ class CapsuleOptionsMenu extends FlxSpriteGroup
|
||||
currentInstrumental.setFormat('VCR OSD Mono', 40, FlxTextAlign.CENTER, true);
|
||||
|
||||
final PAD = 4;
|
||||
var leftArrow = new InstrumentalSelector(parent, PAD, 30, false, parent.getControls());
|
||||
var rightArrow = new InstrumentalSelector(parent, capsuleMenuBG.width - leftArrow.width - PAD, 30, true, parent.getControls());
|
||||
leftArrow = new InstrumentalSelector(parent, PAD, 30, false, parent.getControls());
|
||||
rightArrow = new InstrumentalSelector(parent, capsuleMenuBG.width - leftArrow.width - PAD, 30, true, parent.getControls());
|
||||
|
||||
var label:FlxText = new FlxText(0, 5, capsuleMenuBG.width, 'INSTRUMENTAL');
|
||||
label.setFormat('VCR OSD Mono', 24, FlxTextAlign.CENTER, true);
|
||||
@@ -109,6 +113,8 @@ class CapsuleOptionsMenu extends FlxSpriteGroup
|
||||
{
|
||||
// Play in reverse.
|
||||
capsuleMenuBG.animation.play('open', true, true);
|
||||
if (leftArrow.moveShitDownTimer != null) leftArrow.moveShitDownTimer.cancel();
|
||||
if (rightArrow.moveShitDownTimer != null) rightArrow.moveShitDownTimer.cancel();
|
||||
capsuleMenuBG.animation.finishCallback = function(_) {
|
||||
parent.cleanupCapsuleOptionsMenu();
|
||||
queueDestroy = true;
|
||||
@@ -136,6 +142,8 @@ class InstrumentalSelector extends FunkinSprite
|
||||
|
||||
var baseScale:Float = 0.6;
|
||||
|
||||
public var moveShitDownTimer:FlxTimer;
|
||||
|
||||
public function new(parent:FreeplayState, x:Float, y:Float, flipped:Bool, controls:Controls)
|
||||
{
|
||||
super(x, y);
|
||||
@@ -174,7 +182,7 @@ class InstrumentalSelector extends FunkinSprite
|
||||
|
||||
scale.x = scale.y = 0.5 * baseScale;
|
||||
|
||||
new FlxTimer().start(2 / 24, function(tmr) {
|
||||
moveShitDownTimer = new FlxTimer().start(2 / 24, function(tmr) {
|
||||
scale.x = scale.y = 1 * baseScale;
|
||||
whiteShader.colorSet = false;
|
||||
updateHitbox();
|
||||
|
||||
@@ -168,6 +168,8 @@ class CapsuleText extends FlxSpriteGroup
|
||||
|
||||
public function resetText():Void
|
||||
{
|
||||
scale.x = 1;
|
||||
scale.y = 1;
|
||||
if (moveTween != null) moveTween.cancel();
|
||||
if (moveTimer != null) moveTimer.cancel();
|
||||
whiteText.offset.x = 0;
|
||||
|
||||
@@ -87,7 +87,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
||||
case Intro:
|
||||
// Play the intro animation then leave this state immediately.
|
||||
var animPrefix = playableCharData.getAnimationPrefix('intro');
|
||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true);
|
||||
if (getCurrentAnimation() != animPrefix || !this.anim.isPlaying) playFlashAnimation(animPrefix, true);
|
||||
timeIdling = 0;
|
||||
case Idle:
|
||||
// We are in this state the majority of the time.
|
||||
|
||||
@@ -35,7 +35,7 @@ class FreeplayScore extends FlxTypedSpriteGroup<ScoreNum>
|
||||
loopNum--;
|
||||
}
|
||||
|
||||
while (loopNum > 0)
|
||||
while (loopNum >= 0)
|
||||
{
|
||||
group.members[loopNum].digit = 0;
|
||||
loopNum--;
|
||||
|
||||
@@ -501,19 +501,19 @@ class FreeplayState extends MusicBeatSubState
|
||||
switch (str)
|
||||
{
|
||||
case 'fav':
|
||||
generateSongList({filterType: FAVORITE}, true);
|
||||
generateSongList({filterType: FAVORITE}, true, false);
|
||||
case 'ALL':
|
||||
generateSongList(null, true);
|
||||
generateSongList(null, true, false);
|
||||
case '#':
|
||||
generateSongList({filterType: REGEXP, filterData: '0-9'}, true);
|
||||
generateSongList({filterType: REGEXP, filterData: '0-9'}, true, false);
|
||||
default:
|
||||
generateSongList({filterType: REGEXP, filterData: str}, true);
|
||||
generateSongList({filterType: REGEXP, filterData: str}, true, false);
|
||||
}
|
||||
|
||||
// If the current song is still in the list (aka not null?), we'll land on it
|
||||
// If the current song is still in the list, or if it was random, we'll land on it
|
||||
// Otherwise we want to land on the first song of the group, rather than random song when changing letter sorts
|
||||
// that is, only if there's more than one song in the group!
|
||||
if (curSong != null && currentFilteredSongs.length > 1)
|
||||
if (curSong == null || currentFilteredSongs.contains(curSong))
|
||||
{
|
||||
changeSelection();
|
||||
}
|
||||
@@ -638,7 +638,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
allDifficulties = SongRegistry.instance.listAllDifficulties(currentCharacterId);
|
||||
|
||||
// Generates song list with the starter params (who our current character is, last remembered difficulty, etc.)
|
||||
generateSongList(null, false);
|
||||
// Set this to false if you prefer the 50% transparency on the capsules when they first appear.
|
||||
generateSongList(null, true);
|
||||
|
||||
// dedicated camera for the state so we don't need to fuk around with camera scrolls from the mainmenu / elsewhere
|
||||
funnyCam.bgColor = FlxColor.TRANSPARENT;
|
||||
@@ -734,10 +735,10 @@ 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);
|
||||
randomCapsule.initData(null, styleData, 1);
|
||||
randomCapsule.y = randomCapsule.intendedY(0) + 10;
|
||||
randomCapsule.targetPos.x = randomCapsule.x;
|
||||
randomCapsule.alpha = 0;
|
||||
randomCapsule.alpha = 0.5;
|
||||
randomCapsule.songText.visible = false;
|
||||
randomCapsule.favIcon.visible = false;
|
||||
randomCapsule.favIconBlurred.visible = false;
|
||||
@@ -749,8 +750,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (fromCharSelect) randomCapsule.forcePosition();
|
||||
else
|
||||
{
|
||||
// randomCapsule.initJumpIn(0, force);
|
||||
randomCapsule.forcePosition();
|
||||
randomCapsule.initJumpIn(0, force);
|
||||
}
|
||||
|
||||
var hsvShader:HSVShader = new HSVShader();
|
||||
@@ -765,7 +765,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
var funnyMenu:SongMenuItem = grpCapsules.recycle(SongMenuItem);
|
||||
|
||||
funnyMenu.initPosition(FlxG.width, 0);
|
||||
funnyMenu.initData(tempSong, styleData);
|
||||
funnyMenu.initData(tempSong, styleData, i + 1);
|
||||
funnyMenu.onConfirm = function() {
|
||||
capsuleOnOpenDefault(funnyMenu);
|
||||
};
|
||||
@@ -776,10 +776,10 @@ class FreeplayState extends MusicBeatSubState
|
||||
funnyMenu.hsvShader = hsvShader;
|
||||
funnyMenu.newText.animation.curAnim.curFrame = 45 - ((i * 4) % 45);
|
||||
|
||||
// This stops the bounce-in animation.
|
||||
funnyMenu.forcePosition();
|
||||
|
||||
// funnyMenu.initJumpIn(0, force);
|
||||
// Stop the bounce-in animation when returning to freeplay from the character selection screen.
|
||||
if (fromCharSelect) funnyMenu.forcePosition();
|
||||
else
|
||||
funnyMenu.initJumpIn(0, force);
|
||||
|
||||
grpCapsules.add(funnyMenu);
|
||||
}
|
||||
@@ -1717,14 +1717,14 @@ class FreeplayState extends MusicBeatSubState
|
||||
{
|
||||
touchTimer = 0;
|
||||
var previousVariation:String = currentVariation;
|
||||
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].freeplayData;
|
||||
|
||||
// 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> = grpCapsules.members[curSelected].freeplayData?.data.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST;
|
||||
var characterVariations:Array<String> = daSong?.data.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST;
|
||||
var difficultiesAvailable:Array<String> = allDifficulties;
|
||||
// Gets all available difficulties for our character, via our available variations
|
||||
var songDifficulties:Array<String> = grpCapsules.members[curSelected].freeplayData?.data.listDifficulties(null,
|
||||
characterVariations) ?? Constants.DEFAULT_DIFFICULTY_LIST;
|
||||
var songDifficulties:Array<String> = daSong?.data.listDifficulties(null, characterVariations) ?? Constants.DEFAULT_DIFFICULTY_LIST;
|
||||
|
||||
var currentDifficultyIndex:Int = difficultiesAvailable.indexOf(currentDifficulty);
|
||||
|
||||
@@ -1737,15 +1737,16 @@ class FreeplayState extends MusicBeatSubState
|
||||
// Update the current difficulty
|
||||
currentDifficulty = difficultiesAvailable[currentDifficultyIndex];
|
||||
// For when we change the difficulty, but the song doesn't have that difficulty!
|
||||
if (!songDifficulties.contains(difficultiesAvailable[currentDifficultyIndex]))
|
||||
if (daSong != null && !songDifficulties.contains(difficultiesAvailable[currentDifficultyIndex]))
|
||||
{
|
||||
curSelected = findClosestDiff(characterVariations, difficultiesAvailable[currentDifficultyIndex]);
|
||||
rememberedSongId = grpCapsules.members[curSelected].freeplayData?.data.id;
|
||||
daSong = grpCapsules.members[curSelected].freeplayData;
|
||||
rememberedSongId = daSong?.data.id;
|
||||
}
|
||||
|
||||
for (variation in characterVariations)
|
||||
{
|
||||
if (grpCapsules.members[curSelected].freeplayData?.data.hasDifficulty(currentDifficulty, variation) ?? false)
|
||||
if (daSong?.data.hasDifficulty(currentDifficulty, variation) ?? false)
|
||||
{
|
||||
currentVariation = variation;
|
||||
rememberedVariation = variation;
|
||||
@@ -1753,7 +1754,6 @@ class FreeplayState extends MusicBeatSubState
|
||||
}
|
||||
}
|
||||
|
||||
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].freeplayData;
|
||||
if (daSong != null)
|
||||
{
|
||||
var targetSong:Null<Song> = SongRegistry.instance.fetchEntry(daSong.data.id);
|
||||
@@ -1765,9 +1765,10 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSong.data.id, currentDifficulty, currentVariation);
|
||||
intendedScore = songScore?.score ?? 0;
|
||||
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
|
||||
intendedCompletion = songScore == null ? 0.0 : Math.max(0, ((songScore.tallies.sick +
|
||||
songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes));
|
||||
rememberedDifficulty = currentDifficulty;
|
||||
grpCapsules.members[curSelected].refreshDisplay();
|
||||
grpCapsules.members[curSelected].refreshDisplay((prepForNewRank == true) ? false : true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1873,11 +1874,15 @@ class FreeplayState extends MusicBeatSubState
|
||||
*/
|
||||
function capsuleOnOpenDefault(cap:SongMenuItem):Void
|
||||
{
|
||||
busy = true;
|
||||
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;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
var targetSong:Song = targetSongNullable;
|
||||
@@ -1891,6 +1896,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (targetDifficulty == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find difficulty with id (${targetDifficultyId})');
|
||||
busy = false;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1920,9 +1927,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function openInstrumentalList(cap:SongMenuItem, instrumentalIds:Array<String>):Void
|
||||
{
|
||||
busy = true;
|
||||
|
||||
capsuleOptionsMenu = new CapsuleOptionsMenu(this, cap.x + 175, cap.y + 115, instrumentalIds);
|
||||
capsuleOptionsMenu = new CapsuleOptionsMenu(this, cap.targetPos.x + 175, cap.targetPos.y + 115, instrumentalIds);
|
||||
capsuleOptionsMenu.cameras = [funnyCam];
|
||||
capsuleOptionsMenu.zIndex = 10000;
|
||||
add(capsuleOptionsMenu);
|
||||
@@ -1937,6 +1942,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
public function cleanupCapsuleOptionsMenu():Void
|
||||
{
|
||||
this.busy = false;
|
||||
letterSort.inputEnabled = true;
|
||||
|
||||
if (capsuleOptionsMenu != null)
|
||||
{
|
||||
@@ -1950,9 +1956,6 @@ class FreeplayState extends MusicBeatSubState
|
||||
*/
|
||||
function capsuleOnConfirmDefault(cap:SongMenuItem, ?targetInstId:String):Void
|
||||
{
|
||||
busy = true;
|
||||
letterSort.inputEnabled = false;
|
||||
|
||||
PlayStatePlaylist.isStoryMode = false;
|
||||
|
||||
var targetSongId:String = cap?.freeplayData?.data.id ?? 'unknown';
|
||||
@@ -1960,6 +1963,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (targetSongNullable == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find song with id (${targetSongId})');
|
||||
busy = false;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
var targetSong:Song = targetSongNullable;
|
||||
@@ -1971,6 +1976,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (targetDifficulty == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find difficulty with id (${currentDifficulty})');
|
||||
busy = false;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2061,10 +2068,11 @@ class FreeplayState extends MusicBeatSubState
|
||||
{
|
||||
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSongCapsule.freeplayData.data.id, currentDifficulty, currentVariation);
|
||||
intendedScore = songScore?.score ?? 0;
|
||||
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
|
||||
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick +
|
||||
songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes);
|
||||
rememberedSongId = daSongCapsule.freeplayData.data.id;
|
||||
changeDiff();
|
||||
daSongCapsule.refreshDisplay();
|
||||
daSongCapsule.refreshDisplay((prepForNewRank == true) ? false : true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2072,6 +2080,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
intendedCompletion = 0.0;
|
||||
rememberedSongId = null;
|
||||
albumRoll.albumId = null;
|
||||
changeDiff();
|
||||
daSongCapsule.refreshDisplay();
|
||||
}
|
||||
|
||||
for (index => capsule in grpCapsules.members)
|
||||
@@ -2080,8 +2090,10 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
capsule.selected = index == curSelected + 1;
|
||||
|
||||
capsule.curSelected = curSelected;
|
||||
|
||||
capsule.targetPos.y = capsule.intendedY(index - curSelected);
|
||||
capsule.targetPos.x = 270 + (60 * (Math.sin(index - curSelected)));
|
||||
capsule.targetPos.x = capsule.intendedX(index - curSelected);
|
||||
|
||||
if (index < curSelected) capsule.targetPos.y -= 100; // another 100 for good measure
|
||||
}
|
||||
|
||||
@@ -204,6 +204,9 @@ class FreeplayLetter extends FlxAtlasSprite
|
||||
this.anim.play(animLetters[letterInd] + " move");
|
||||
this.anim.pause();
|
||||
curLetter = letterInd;
|
||||
this.anim.onComplete.add(function() {
|
||||
this.anim.play(animLetters[curLetter] + " move");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +234,7 @@ class FreeplayLetter extends FlxAtlasSprite
|
||||
animName = "T move";
|
||||
}
|
||||
|
||||
this.anim.play(animName);
|
||||
this.anim.play(animName, true);
|
||||
if (curSelection != curLetter)
|
||||
{
|
||||
this.anim.pause();
|
||||
|
||||
@@ -74,6 +74,9 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
|
||||
var sparkleTimer:FlxTimer;
|
||||
|
||||
var index:Int;
|
||||
public var curSelected:Int;
|
||||
|
||||
public function new(x:Float, y:Float)
|
||||
{
|
||||
super(x, y);
|
||||
@@ -390,7 +393,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
return evilTrail.color;
|
||||
}
|
||||
|
||||
public function refreshDisplay():Void
|
||||
public function refreshDisplay(updateRank:Bool = true):Void
|
||||
{
|
||||
if (freeplayData == null)
|
||||
{
|
||||
@@ -408,7 +411,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
pixelIcon.visible = true;
|
||||
updateBPM(Std.int(freeplayData.songStartingBpm) ?? 0);
|
||||
updateDifficultyRating(freeplayData.difficultyRating ?? 0);
|
||||
updateScoringRank(freeplayData.scoringRank);
|
||||
if (updateRank) updateScoringRank(freeplayData.scoringRank);
|
||||
newText.visible = freeplayData.isNew;
|
||||
favIcon.visible = freeplayData.isFav;
|
||||
favIconBlurred.visible = freeplayData.isFav;
|
||||
@@ -489,7 +492,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
spr.visible = value;
|
||||
}
|
||||
|
||||
if (value) textAppear();
|
||||
textAppear();
|
||||
|
||||
updateSelected();
|
||||
}
|
||||
@@ -500,10 +503,12 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public function initData(freeplayData:Null<FreeplaySongData>, ?styleData:FreeplayStyle = null):Void
|
||||
public function initData(freeplayData:Null<FreeplaySongData>, ?styleData:FreeplayStyle = null, index:Int = null):Void
|
||||
{
|
||||
this.freeplayData = freeplayData;
|
||||
|
||||
if (index != null) this.index = index;
|
||||
|
||||
// im so mad i have to do this but im pretty sure with the capsules recycling i cant call the new function properly :/
|
||||
// if thats possible someone Please change the new function to be something like
|
||||
// capsule.frames = Paths.getSparrowAtlas(styleData == null ? 'freeplay/freeplayCapsule/capsule/freeplayCapsule' : styleData.getCapsuleAssetKey()); thank u luv u
|
||||
@@ -531,7 +536,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
var frameOutTypeBeat:Int = 0;
|
||||
|
||||
var xFrames:Array<Float> = [1.7, 1.8, 0.85, 0.85, 0.97, 0.97, 1];
|
||||
var xPosLerpLol:Array<Float> = [0.9, 0.4, 0.16, 0.16, 0.22, 0.22, 0.245]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
|
||||
var xPosLerpLol:Array<Float> = [0, 0, 0.16, 0.16, 0.22, 0.22, 0.245]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
|
||||
var xPosOutLerpLol:Array<Float> = [0.245, 0.75, 0.98, 0.98, 1.2]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
|
||||
|
||||
public var realScaled:Float = 0.8;
|
||||
@@ -542,9 +547,6 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
|
||||
new FlxTimer().start((1 / 24) * maxTimer, function(doShit) {
|
||||
doJumpIn = true;
|
||||
});
|
||||
|
||||
new FlxTimer().start((0.09 * maxTimer) + 0.85, function(lerpTmr) {
|
||||
doLerp = true;
|
||||
});
|
||||
|
||||
@@ -605,12 +607,19 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
|
||||
capsule.scale.x = xFrames[frameInTypeBeat];
|
||||
capsule.scale.y = 1 / xFrames[frameInTypeBeat];
|
||||
x = FlxG.width * xPosLerpLol[Std.int(Math.min(frameInTypeBeat, xPosLerpLol.length - 1))];
|
||||
targetPos.x = FlxG.width * xPosLerpLol[Std.int(Math.min(frameInTypeBeat, xPosLerpLol.length - 1))];
|
||||
|
||||
capsule.scale.x *= realScaled;
|
||||
capsule.scale.y *= realScaled;
|
||||
|
||||
frameInTypeBeat += 1;
|
||||
// Move the targetPos set to the if statement below if you want them to shift to their target positions after jumping in instead
|
||||
// I have no idea why this if instead of frameInTypeBeat == xFrames.length works even though they're the same thing
|
||||
if (targetPos.x <= 320) targetPos.x = intendedX(index - curSelected);
|
||||
}
|
||||
else if (frameInTypeBeat == xFrames.length)
|
||||
{
|
||||
doJumpIn = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,13 +633,17 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
|
||||
capsule.scale.x = xFrames[frameOutTypeBeat];
|
||||
capsule.scale.y = 1 / xFrames[frameOutTypeBeat];
|
||||
x = FlxG.width * xPosOutLerpLol[Std.int(Math.min(frameOutTypeBeat, xPosOutLerpLol.length - 1))];
|
||||
this.x = FlxG.width * xPosOutLerpLol[Std.int(Math.min(frameOutTypeBeat, xPosOutLerpLol.length - 1))];
|
||||
|
||||
capsule.scale.x *= realScaled;
|
||||
capsule.scale.y *= realScaled;
|
||||
|
||||
frameOutTypeBeat += 1;
|
||||
}
|
||||
else if (frameOutTypeBeat == xFrames.length)
|
||||
{
|
||||
doJumpOut = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (doLerp)
|
||||
@@ -654,6 +667,11 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
}
|
||||
}
|
||||
|
||||
public function intendedX(index:Int):Float
|
||||
{
|
||||
return 270 + (60 * (Math.sin(index)));
|
||||
}
|
||||
|
||||
public function intendedY(index:Int):Float
|
||||
{
|
||||
return (index * ((height * realScaled) + 10)) + 120;
|
||||
|
||||
@@ -99,6 +99,7 @@ class MainMenuState extends MusicBeatState
|
||||
createMenuItem('freeplay', 'mainmenu/freeplay', function() {
|
||||
persistentDraw = true;
|
||||
persistentUpdate = false;
|
||||
rememberedSelectedIndex = menuItems.selectedIndex;
|
||||
// Freeplay has its own custom transition
|
||||
FlxTransitionableState.skipNextTransIn = true;
|
||||
FlxTransitionableState.skipNextTransOut = true;
|
||||
@@ -413,6 +414,7 @@ class MainMenuState extends MusicBeatState
|
||||
|
||||
if (controls.BACK && menuItems.enabled && !menuItems.busy)
|
||||
{
|
||||
rememberedSelectedIndex = menuItems.selectedIndex;
|
||||
FlxG.switchState(() -> new TitleState());
|
||||
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||
}
|
||||
|
||||
+138
-57
@@ -52,24 +52,44 @@ class FileUtil
|
||||
/**
|
||||
* Paths which should not be deleted or modified by scripts.
|
||||
*/
|
||||
private static final PROTECTED_PATHS:Array<String> = [
|
||||
'',
|
||||
'assets',
|
||||
'manifest',
|
||||
'manifest/*',
|
||||
'plugins',
|
||||
'plugins/*',
|
||||
'Funkin.exe',
|
||||
'Funkin',
|
||||
'libvlc.dll',
|
||||
'libvlccore.dll',
|
||||
'lime.ndll'
|
||||
];
|
||||
public static var PROTECTED_PATHS(get, never):Array<String>;
|
||||
|
||||
public static function get_PROTECTED_PATHS():Array<String>
|
||||
{
|
||||
final protected:Array<String> = [
|
||||
'',
|
||||
'.',
|
||||
'assets',
|
||||
'assets/*',
|
||||
'backups',
|
||||
'backups/*',
|
||||
'manifest',
|
||||
'manifest/*',
|
||||
'plugins',
|
||||
'plugins/*',
|
||||
'Funkin.exe',
|
||||
'Funkin',
|
||||
'icon.ico',
|
||||
'libvlc.dll',
|
||||
'libvlccore.dll',
|
||||
'lime.ndll',
|
||||
'scores.json'
|
||||
];
|
||||
|
||||
#if sys
|
||||
for (i in 0...protected.length)
|
||||
{
|
||||
protected[i] = sys.FileSystem.fullPath(Path.join([gameDirectory, protected[i]]));
|
||||
}
|
||||
#end
|
||||
|
||||
return protected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid filesystem characters.
|
||||
* Regex for invalid filesystem characters.
|
||||
*/
|
||||
private static final INVALID_CHARS:Array<String> = [':', '*', '?', '"', '<', '>', '|'];
|
||||
public static final INVALID_CHARS:EReg = ~/[:*?"<>|\n\r\t]/g;
|
||||
|
||||
#if sys
|
||||
private static var _gameDirectory:Null<String> = null;
|
||||
@@ -82,7 +102,7 @@ class FileUtil
|
||||
return _gameDirectory;
|
||||
}
|
||||
|
||||
return _gameDirectory = Path.directory(Path.normalize(sys.FileSystem.fullPath(Sys.programPath())));
|
||||
return _gameDirectory = sys.FileSystem.fullPath(Path.directory(Sys.programPath()));
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -601,8 +621,6 @@ class FileUtil
|
||||
throw 'Target path is a directory, not a file: "$path"';
|
||||
}
|
||||
|
||||
createDirIfNotExists(Path.directory(path));
|
||||
|
||||
var shouldWrite:Bool = true;
|
||||
switch (mode)
|
||||
{
|
||||
@@ -791,14 +809,14 @@ class FileUtil
|
||||
*/
|
||||
public static function createDirIfNotExists(dir:String):Void
|
||||
{
|
||||
#if sys
|
||||
if (!directoryExists(dir))
|
||||
{
|
||||
#if sys
|
||||
sys.FileSystem.createDirectory(dir);
|
||||
#else
|
||||
throw 'Directory creation is not supported on this platform.';
|
||||
#end
|
||||
}
|
||||
#else
|
||||
throw 'Directory creation is not supported on this platform.';
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1121,8 +1139,64 @@ class FileUtil
|
||||
};
|
||||
}
|
||||
|
||||
public static function openFolder(pathFolder:String) // what is this for??
|
||||
WindowUtil.openFolder(pathFolder);
|
||||
/**
|
||||
* Runs platform-specific code to open a path in the file explorer.
|
||||
*
|
||||
* @param pathFolder The path of the folder to open.
|
||||
*/
|
||||
public static function openFolder(pathFolder:String):Void
|
||||
{
|
||||
#if sys
|
||||
pathFolder = pathFolder.trim();
|
||||
if (!directoryExists(pathFolder))
|
||||
{
|
||||
throw 'Path is not a directory: "$pathFolder"';
|
||||
}
|
||||
|
||||
#if windows
|
||||
Sys.command('explorer', [pathFolder.replace('/', '\\')]);
|
||||
#elseif mac
|
||||
// mac could be fuckie with where the log folder is relative to the game file...
|
||||
// if this comment is still here... it means it has NOT been verified on mac yet!
|
||||
//
|
||||
// FileUtil.hx note: this was originally used to open the logs specifically!
|
||||
// thats why the above comment is there!
|
||||
Sys.command('open', [pathFolder]);
|
||||
#elseif linux
|
||||
// TODO: implement linux
|
||||
// some shit with xdg-open :thinking: emoji...
|
||||
#end
|
||||
#else
|
||||
throw 'External folder open is not supported on this platform.';
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs platform-specific code to open a file explorer and select a specific file.
|
||||
*
|
||||
* @param path The path of the file to select.
|
||||
*/
|
||||
public static function openSelectFile(path:String):Void
|
||||
{
|
||||
#if sys
|
||||
path = path.trim();
|
||||
if (!pathExists(path))
|
||||
{
|
||||
throw 'Path does not exist: "$path"';
|
||||
}
|
||||
|
||||
#if windows
|
||||
Sys.command('explorer', ['/select,', path.replace('/', '\\')]);
|
||||
#elseif mac
|
||||
Sys.command('open', ['-R', path]);
|
||||
#elseif linux
|
||||
// TODO: unsure of the linux equivalent to opening a folder and then "selecting" a file.
|
||||
Sys.command('open', [path]);
|
||||
#end
|
||||
#else
|
||||
throw 'External file selection is not supported on this platform.';
|
||||
#end
|
||||
}
|
||||
|
||||
private static function convertTypeFilter(?typeFilter:Array<FileFilter>):Null<String>
|
||||
{
|
||||
@@ -1157,7 +1231,8 @@ class FileUtilSandboxed
|
||||
*/
|
||||
public static function sanitizePath(path:String):String
|
||||
{
|
||||
if (path == null)
|
||||
path = (path ?? '').trim();
|
||||
if (path == '')
|
||||
{
|
||||
#if sys
|
||||
return FileUtil.gameDirectory;
|
||||
@@ -1171,19 +1246,14 @@ class FileUtilSandboxed
|
||||
path = path.substring(path.lastIndexOf(':') + 1);
|
||||
}
|
||||
|
||||
path = path.trim().replace('\\', '/');
|
||||
path = path.replace('\\', '/');
|
||||
while (path.contains('//'))
|
||||
{
|
||||
path = path.replace('//', '/');
|
||||
}
|
||||
|
||||
@:privateAccess for (char in FileUtil.INVALID_CHARS)
|
||||
{
|
||||
path = path.replace(char, '');
|
||||
}
|
||||
|
||||
var parts:Array<String> = path.split('/');
|
||||
var sanitized:Array<String> = new Array<String>();
|
||||
final parts:Array<String> = FileUtil.INVALID_CHARS.replace(path, '').split('/');
|
||||
final sanitized:Array<String> = new Array<String>();
|
||||
for (part in parts)
|
||||
{
|
||||
switch (part)
|
||||
@@ -1193,25 +1263,31 @@ class FileUtilSandboxed
|
||||
case '..':
|
||||
sanitized.pop();
|
||||
default:
|
||||
sanitized.push(part);
|
||||
sanitized.push(part.trim());
|
||||
}
|
||||
}
|
||||
|
||||
var ret:String = sanitized.join('/');
|
||||
if (sanitized.length == 0)
|
||||
{
|
||||
#if sys
|
||||
return FileUtil.gameDirectory;
|
||||
#else
|
||||
return '';
|
||||
#end
|
||||
}
|
||||
|
||||
#if sys
|
||||
var realPath:String = Path.normalize(sys.FileSystem.fullPath(Path.join([FileUtil.gameDirectory, ret])));
|
||||
// ^ TODO: figure out how to get "real" path of symlinked paths
|
||||
|
||||
if (!realPath.startsWith(FileUtil.gameDirectory.toString()))
|
||||
// TODO: figure out how to get "real" path of symlinked paths
|
||||
final realPath:String = sys.FileSystem.fullPath(Path.join([FileUtil.gameDirectory, sanitized.join('/')]));
|
||||
if (!realPath.startsWith(FileUtil.gameDirectory))
|
||||
{
|
||||
return FileUtil.gameDirectory;
|
||||
}
|
||||
|
||||
ret = realPath;
|
||||
return realPath;
|
||||
#else
|
||||
return sanitized.join('/');
|
||||
#end
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1219,9 +1295,9 @@ class FileUtilSandboxed
|
||||
* @param path The path to check.
|
||||
* @return Whether the path is protected.
|
||||
*/
|
||||
public static function isProtected(path:String):Bool
|
||||
public static function isProtected(path:String, sanitizeFirst:Bool = true):Bool
|
||||
{
|
||||
path = sanitizePath(path);
|
||||
if (sanitizeFirst) path = sanitizePath(path);
|
||||
@:privateAccess for (protected in FileUtil.PROTECTED_PATHS)
|
||||
{
|
||||
if (path == protected || (protected.contains('*') && path.startsWith(protected.substring(0, protected.indexOf('*')))))
|
||||
@@ -1298,7 +1374,7 @@ class FileUtilSandboxed
|
||||
|
||||
public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, mode:FileWriteMode = Skip):Bool
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
|
||||
return FileUtil.saveFilesAsZIPToPath(resources, path, mode);
|
||||
}
|
||||
|
||||
@@ -1329,32 +1405,32 @@ class FileUtilSandboxed
|
||||
|
||||
public static function writeStringToPath(path:String, data:String, mode:FileWriteMode = Skip):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
|
||||
FileUtil.writeStringToPath(path, data, mode);
|
||||
}
|
||||
|
||||
public static function writeBytesToPath(path:String, data:Bytes, mode:FileWriteMode = Skip):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
|
||||
FileUtil.writeBytesToPath(path, data, mode);
|
||||
}
|
||||
|
||||
public static function appendStringToPath(path:String, data:String):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
|
||||
FileUtil.appendStringToPath(path, data);
|
||||
}
|
||||
|
||||
public static function moveFile(path:String, destination:String):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: $path';
|
||||
if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: $destination';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot move protected path: $path';
|
||||
if (isProtected(destination = sanitizePath(destination), false)) throw 'Cannot move to protected path: $destination';
|
||||
FileUtil.moveFile(path, destination);
|
||||
}
|
||||
|
||||
public static function deleteFile(path:String):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: $path';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot delete protected path: $path';
|
||||
FileUtil.deleteFile(path);
|
||||
}
|
||||
|
||||
@@ -1390,14 +1466,14 @@ class FileUtilSandboxed
|
||||
|
||||
public static function moveDir(path:String, destination:String, ?ignore:Array<String>, strict:Bool = true):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: "$path"';
|
||||
if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: "$destination"';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot move protected path: "$path"';
|
||||
if (isProtected(destination = sanitizePath(destination), false)) throw 'Cannot move to protected path: "$destination"';
|
||||
FileUtil.moveDir(path, destination, ignore, strict);
|
||||
}
|
||||
|
||||
public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array<String>):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: "$path"';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot delete protected path: "$path"';
|
||||
FileUtil.deleteDir(path, recursive, ignore);
|
||||
}
|
||||
|
||||
@@ -1413,7 +1489,7 @@ class FileUtilSandboxed
|
||||
|
||||
public static function rename(path:String, newName:String, keepExtension:Bool = true):Void
|
||||
{
|
||||
if (isProtected(path = sanitizePath(path))) throw 'Cannot rename protected path: "$path"';
|
||||
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot rename protected path: "$path"';
|
||||
FileUtil.rename(path, sanitizePath(newName), keepExtension);
|
||||
}
|
||||
|
||||
@@ -1442,9 +1518,14 @@ class FileUtilSandboxed
|
||||
return FileUtil.makeZIPEntryFromBytes(name, data);
|
||||
}
|
||||
|
||||
public static function openFolder(pathFolder:String)
|
||||
public static function openFolder(pathFolder:String):Void
|
||||
{
|
||||
FileUtil.openFolder(pathFolder);
|
||||
FileUtil.openFolder(sanitizePath(pathFolder));
|
||||
}
|
||||
|
||||
public static function openSelectFile(path:String):Void
|
||||
{
|
||||
FileUtil.openSelectFile(sanitizePath(path));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ class MemoryUtil
|
||||
{
|
||||
#if cpp
|
||||
var result:String = 'HXCPP-Immix:';
|
||||
result += '\n- Memory Used: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_USAGE)} bytes';
|
||||
result += '\n- Memory Reserved: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_RESERVED)} bytes';
|
||||
result += '\n- Memory Current Pool: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_CURRENT)} bytes';
|
||||
result += '\n- Memory Large Pool: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_LARGE)} bytes';
|
||||
result += '\n- Memory Used: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_USAGE)} bytes';
|
||||
result += '\n- Memory Reserved: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_RESERVED)} bytes';
|
||||
result += '\n- Memory Current Pool: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_CURRENT)} bytes';
|
||||
result += '\n- Memory Large Pool: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_LARGE)} bytes';
|
||||
result += '\n- HXCPP Debugger: ${#if HXCPP_DEBUGGER 'Enabled' #else 'Disabled' #end}';
|
||||
result += '\n- HXCPP Exp Generational Mode: ${#if HXCPP_GC_GENERATIONAL 'Enabled' #else 'Disabled' #end}';
|
||||
result += '\n- HXCPP Exp Moving GC: ${#if HXCPP_GC_MOVING 'Enabled' #else 'Disabled' #end}';
|
||||
|
||||
@@ -16,6 +16,39 @@ using StringTools;
|
||||
#end
|
||||
class WindowUtil
|
||||
{
|
||||
/**
|
||||
* A regex to match valid URLs.
|
||||
*/
|
||||
public static final URL_REGEX:EReg = ~/^https?:\/?\/?(?:www\.)?[-a-zA-Z0-9@:%_\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/;
|
||||
|
||||
/**
|
||||
* Sanitizes a URL via a regex.
|
||||
*
|
||||
* @param targetUrl The URL to sanitize.
|
||||
* @return The sanitized URL, or an empty string if the URL is invalid.
|
||||
*/
|
||||
public static function sanitizeURL(targetUrl:String):String
|
||||
{
|
||||
targetUrl = (targetUrl ?? '').trim();
|
||||
if (targetUrl == '')
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
final lowerUrl:String = targetUrl.toLowerCase();
|
||||
if (!lowerUrl.startsWith('http:') && !lowerUrl.startsWith('https:'))
|
||||
{
|
||||
targetUrl = 'http://' + targetUrl;
|
||||
}
|
||||
|
||||
if (URL_REGEX.match(targetUrl))
|
||||
{
|
||||
return URL_REGEX.matched(0);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs platform-specific code to open a URL in a web browser.
|
||||
* @param targetUrl The URL to open.
|
||||
@@ -28,6 +61,12 @@ class WindowUtil
|
||||
else if (protocol[0] != 'http' && protocol[0] != 'https') throw "openURL can only open http and https links.";
|
||||
|
||||
#if FEATURE_OPEN_URL
|
||||
targetUrl = sanitizeURL(targetUrl);
|
||||
if (targetUrl == '')
|
||||
{
|
||||
throw 'Invalid URL: "$targetUrl"';
|
||||
}
|
||||
|
||||
#if linux
|
||||
Sys.command('/usr/bin/xdg-open $targetUrl &');
|
||||
#else
|
||||
@@ -63,54 +102,6 @@ class WindowUtil
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Runs platform-specific code to open a path in the file explorer.
|
||||
* @param targetPath The path to open.
|
||||
*/
|
||||
public static function openFolder(targetPath:String):Void
|
||||
{
|
||||
#if html5
|
||||
throw 'Cannot open URLs on this platform.';
|
||||
#else
|
||||
if (!sys.FileSystem.exists(targetPath) || !sys.FileSystem.isDirectory(targetPath)) throw 'openFolder should only be used to open existing folders.';
|
||||
#if FEATURE_OPEN_URL
|
||||
#if windows
|
||||
Sys.command('explorer', [targetPath.replace('/', '\\')]);
|
||||
#elseif mac
|
||||
Sys.command('open', [targetPath]);
|
||||
#elseif linux
|
||||
Sys.command('open', [targetPath]);
|
||||
#end
|
||||
#else
|
||||
throw 'Cannot open URLs on this platform.';
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs platform-specific code to open a file explorer and select a specific file.
|
||||
* @param targetPath The path of the file to select.
|
||||
*/
|
||||
public static function openSelectFile(targetPath:String):Void
|
||||
{
|
||||
#if html5
|
||||
throw 'Cannot open URLs on this platform.';
|
||||
#else
|
||||
#if FEATURE_OPEN_URL
|
||||
#if windows
|
||||
Sys.command('explorer', ['/select,' + targetPath.replace('/', '\\')]);
|
||||
#elseif mac
|
||||
Sys.command('open', ['-R', targetPath]);
|
||||
#elseif linux
|
||||
// TODO: unsure of the linux equivalent to opening a folder and then "selecting" a file.
|
||||
Sys.command('open', [targetPath]);
|
||||
#end
|
||||
#else
|
||||
throw 'Cannot open URLs on this platform.';
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatched when the game window is closed.
|
||||
*/
|
||||
|
||||
@@ -31,4 +31,11 @@ class VolumePlugin extends FlxBasic
|
||||
else if (PlayerSettings.player1.controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
|
||||
}
|
||||
}
|
||||
|
||||
override public function destroy():Void
|
||||
{
|
||||
if (FlxG.plugins.list.contains(this)) FlxG.plugins.remove(this);
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user