diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index c60cd4083..fcf54a795 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -48,7 +48,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 diff --git a/.github/ISSUE_TEMPLATE/charting.yml b/.github/ISSUE_TEMPLATE/charting.yml index 810a3778d..61bfd25c4 100644 --- a/.github/ISSUE_TEMPLATE/charting.yml +++ b/.github/ISSUE_TEMPLATE/charting.yml @@ -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 diff --git a/.github/ISSUE_TEMPLATE/compiling.yml b/.github/ISSUE_TEMPLATE/compiling.yml index a3370e3c5..8215ddd7b 100644 --- a/.github/ISSUE_TEMPLATE/compiling.yml +++ b/.github/ISSUE_TEMPLATE/compiling.yml @@ -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 diff --git a/.github/ISSUE_TEMPLATE/crash.yml b/.github/ISSUE_TEMPLATE/crash.yml index 04fea1d13..459ed92a3 100644 --- a/.github/ISSUE_TEMPLATE/crash.yml +++ b/.github/ISSUE_TEMPLATE/crash.yml @@ -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 diff --git a/.gitignore b/.gitignore index 39cdddabb..7a31a73cc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ dump/ temp/ docs/temp/ export/ +project/ astc-textures/ RECOVER_*.fla shitAudio/ diff --git a/.gitmodules b/.gitmodules index ad8099e60..f9d5fd497 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b6fecc33..08012d204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,77 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [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. +- 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) + +- 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 +129,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 +148,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,20 +169,30 @@ 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. +### Fixed + +* @PatoFlamejanteTV made their first contribution in [#3843](https://github.com/FunkinCrew/Funkin/pull/3843) +* @sphis-Sinco made their first contribution in [#3881](https://github.com/FunkinCrew/Funkin/pull/3881) +* @MrMadera made their first contribution in [#3934](https://github.com/FunkinCrew/Funkin/pull/3934) +* @MidyGamy made their first contribution in [#4068](https://github.com/FunkinCrew/Funkin/pull/4068) +* @Lasercar made their first contribution in [#4082](https://github.com/FunkinCrew/Funkin/pull/4082) +* @MrScottyPieey made their first contribution in [#4085](https://github.com/FunkinCrew/Funkin/pull/4085) + + ## New Contributors for 0.6.0 * @PatoFlamejanteTV made their first contribution in [#3843](https://github.com/FunkinCrew/Funkin/pull/3843) @@ -397,7 +478,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. diff --git a/art b/art index e5eafbad4..0f19677c1 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit e5eafbad4ae15627130ffc21077106a85b144e48 +Subproject commit 0f19677c1e6a5f9b71707abe1bac88c05c107f79 diff --git a/assets b/assets index 223c5040d..a42401ffd 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 223c5040d3c20f014ff49f59a009385f6174e036 +Subproject commit a42401ffd331456cb3d9e3a027326926583d5006 diff --git a/docs/COMPILING.md b/docs/COMPILING.md index 41be40abd..c6fd9fb49 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -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 ` and `lime rebuild -debug` -10. `lime test ` 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 ` and `lime rebuild -debug` +11. `lime test ` to build and launch the game for your platform (for example, `lime test windows`) ## Build Flags diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index b03d67fc0..9240dd2ed 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -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. diff --git a/docs/Funkin' Debug Hotkeys.md b/docs/Funkin' Debug Hotkeys.md index 9c10f060c..dc567eda0 100644 --- a/docs/Funkin' Debug Hotkeys.md +++ b/docs/Funkin' Debug Hotkeys.md @@ -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. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index b17ee72b1..f488f28de 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -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/64/`, where `` is the current platform you're on, which can be either Windows, Linux or MacOS. diff --git a/project.hxp b/project.hxp index 8bdfbac36..a2e0f7b0d 100644 --- a/project.hxp +++ b/project.hxp @@ -969,13 +969,12 @@ class Project extends HXProject { addAssetLibrary("weekend1", shouldEmbed, shouldPreload); addAssetPath("assets/weekend1", "assets/weekend1", "weekend1", ["*"], exclude, shouldEmbed, "assets"); - // 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); info('Done configuring assets.'); } diff --git a/source/Main.hx b/source/Main.hx index b4762c8ff..ec6b68eed 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -74,14 +74,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); @@ -125,6 +117,15 @@ 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 diff --git a/source/funkin/Preferences.hx b/source/funkin/Preferences.hx index 00fc2e3d7..39406507b 100644 --- a/source/funkin/Preferences.hx +++ b/source/funkin/Preferences.hx @@ -213,15 +213,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; } diff --git a/source/funkin/api/newgrounds/Medals.hx b/source/funkin/api/newgrounds/Medals.hx index 5674ae15d..984362473 100644 --- a/source/funkin/api/newgrounds/Medals.hx +++ b/source/funkin/api/newgrounds/Medals.hx @@ -67,7 +67,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? diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 16ba457fe..91d2da75c 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -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; /** @@ -105,6 +109,11 @@ class FunkinSound extends FlxSound implements ICloneable 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? */ @@ -142,6 +151,14 @@ class FunkinSound extends FlxSound implements ICloneable else { super.update(elapsedSec); + + @:privateAccess + { + if (important && _channel != null && !SoundMixer.__soundChannels.contains(_channel)) + { + SoundMixer.__soundChannels.push(_channel); + } + } } } @@ -428,13 +445,14 @@ class FunkinSound extends FlxSound implements ICloneable * @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 + persist:Bool = false, ?onComplete:Void->Void, ?onLoad:Void->Void, important:Bool = false):Null { @: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; @@ -459,6 +477,7 @@ class FunkinSound extends FlxSound implements ICloneable 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. @@ -531,15 +550,50 @@ class FunkinSound extends FlxSound implements ICloneable 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 + public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void, important:Bool = false):Null { - var result:Null = FunkinSound.load(key, volume, false, true, true, false, onComplete, onLoad); + var result:Null = FunkinSound.load(key, volume, false, true, true, false, onComplete, onLoad, important); return result; } diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 9a9a14f10..c034ba763 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -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 diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 6c96eee69..feb3dc10e 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -961,14 +961,8 @@ class PlayState extends MusicBeatSubState }); // 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; } @@ -1518,9 +1512,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); } @@ -2577,7 +2572,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; @@ -2923,6 +2918,8 @@ class PlayState extends MusicBeatSubState Highscore.tallies.shit += 1; case 'miss': Highscore.tallies.missed += 1; + default: + // Nothing! } health += healthChange; if (isComboBreak) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 935e10a5d..49ab0c84f 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -510,7 +510,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. diff --git a/source/funkin/play/components/ClearPercentCounter.hx b/source/funkin/play/components/ClearPercentCounter.hx index 67c633a99..2e3a01a0b 100644 --- a/source/funkin/play/components/ClearPercentCounter.hx +++ b/source/funkin/play/components/ClearPercentCounter.hx @@ -65,7 +65,7 @@ class ClearPercentCounter extends FlxTypedSpriteGroup var seperatedScore:Array = []; var tempCombo:Int = Math.round(curNumber); - while (tempCombo != 0) + while (tempCombo > 0 && tempCombo != 0) { seperatedScore.push(tempCombo % 10); tempCombo = Math.floor(tempCombo / 10); diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index cbb947ab6..f0c4ebd4a 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -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) { diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index b41e252c1..803e62931 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -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); diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 84381d02b..3dcff855b 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -123,7 +123,7 @@ class Save debugDisplay: false, vibration: true, autoPause: true, - vsyncMode: lime.ui.WindowVSyncMode.OFF, + vsyncMode: 'Off', strumlineBackgroundOpacity: 0, autoFullscreen: false, inputOffset: 0, @@ -1450,9 +1450,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. diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index d05171f91..8e7a14420 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -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):Array { 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; } diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx index 327754a8c..1db4a2481 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx @@ -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; diff --git a/source/funkin/ui/freeplay/CapsuleOptionsMenu.hx b/source/funkin/ui/freeplay/CapsuleOptionsMenu.hx index 4ee3c846e..095f6352f 100644 --- a/source/funkin/ui/freeplay/CapsuleOptionsMenu.hx +++ b/source/funkin/ui/freeplay/CapsuleOptionsMenu.hx @@ -29,6 +29,10 @@ class CapsuleOptionsMenu extends FlxSpriteGroup var leftArrow:InstrumentalSelector; var rightArrow:InstrumentalSelector; + var leftArrow:InstrumentalSelector; + + var rightArrow:InstrumentalSelector; + public function new(parent:FreeplayState, x:Float = 0, y:Float = 0, instIds:Array):Void { super(x, y); @@ -126,6 +130,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; @@ -153,6 +159,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); @@ -191,7 +199,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(); diff --git a/source/funkin/ui/freeplay/CapsuleText.hx b/source/funkin/ui/freeplay/CapsuleText.hx index aae72544e..1c96dbc1e 100644 --- a/source/funkin/ui/freeplay/CapsuleText.hx +++ b/source/funkin/ui/freeplay/CapsuleText.hx @@ -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; diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index 792d0c8d9..be7e332cf 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -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. diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 796c5402e..c58fcb0d7 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -576,10 +576,10 @@ class FreeplayState extends MusicBeatSubState generateSongList({filterType: REGEXP, filterData: str}, true); } - // 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(); } @@ -733,7 +733,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; @@ -835,10 +836,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; @@ -850,8 +851,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(); @@ -866,7 +866,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); }; @@ -877,10 +877,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); // add(funnyMenu.theActualHitbox); @@ -1983,14 +1983,14 @@ class FreeplayState extends MusicBeatSubState if (change != 0) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); var previousVariation:String = currentVariation; + var daSong:Null = 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 = grpCapsules.members[curSelected].freeplayData?.data.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST; + var characterVariations:Array = daSong?.data.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST; var difficultiesAvailable:Array = allDifficulties; // Gets all available difficulties for our character, via our available variations - var songDifficulties:Array = grpCapsules.members[curSelected].freeplayData?.data.listDifficulties(null, - characterVariations) ?? Constants.DEFAULT_DIFFICULTY_LIST; + var songDifficulties:Array = daSong?.data.listDifficulties(null, characterVariations) ?? Constants.DEFAULT_DIFFICULTY_LIST; var currentDifficultyIndex:Int = difficultiesAvailable.indexOf(currentDifficulty); @@ -2003,15 +2003,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; @@ -2019,7 +2020,6 @@ class FreeplayState extends MusicBeatSubState } } - var daSong:Null = grpCapsules.members[curSelected].freeplayData; if (daSong != null) { var targetSong:Null = SongRegistry.instance.fetchEntry(daSong.data.id); @@ -2033,7 +2033,7 @@ class FreeplayState extends MusicBeatSubState intendedScore = songScore?.score ?? 0; intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes); rememberedDifficulty = currentDifficulty; - grpCapsules.members[curSelected].refreshDisplay(); + grpCapsules.members[curSelected].refreshDisplay((prepForNewRank == true) ? false : true); } else { @@ -2172,11 +2172,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 = 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; @@ -2190,6 +2194,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; } @@ -2219,9 +2225,7 @@ class FreeplayState extends MusicBeatSubState function openInstrumentalList(cap:SongMenuItem, instrumentalIds:Array):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); @@ -2236,6 +2240,7 @@ class FreeplayState extends MusicBeatSubState public function cleanupCapsuleOptionsMenu():Void { this.busy = false; + letterSort.inputEnabled = true; if (capsuleOptionsMenu != null) { @@ -2261,6 +2266,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; @@ -2272,6 +2279,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; } @@ -2414,10 +2423,11 @@ class FreeplayState extends MusicBeatSubState { var songScore:Null = 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 { @@ -2425,6 +2435,8 @@ class FreeplayState extends MusicBeatSubState intendedCompletion = 0.0; rememberedSongId = null; albumRoll.albumId = null; + changeDiff(); + daSongCapsule.refreshDisplay(); } for (index => capsule in grpCapsules.members) @@ -2434,8 +2446,10 @@ class FreeplayState extends MusicBeatSubState capsule.forceHighlight = false; capsule.selected = index == curSelected + 1; + capsule.curSelected = curSelected; + capsule.targetPos.y = capsule.intendedY(index - curSelected); - capsule.targetPos.x = (270 + (60 * (Math.sin(index - curSelected)))) + (CUTOUT_WIDTH * SONGS_POS_MULTI); + capsule.targetPos.x = capsule.intendedX(index - curSelected) + (CUTOUT_WIDTH * SONGS_POS_MULTI); if (index < curSelected) capsule.targetPos.y -= 100; // another 100 for good measure } diff --git a/source/funkin/ui/freeplay/LetterSort.hx b/source/funkin/ui/freeplay/LetterSort.hx index 1ec9cd67a..6b0cac6cc 100644 --- a/source/funkin/ui/freeplay/LetterSort.hx +++ b/source/funkin/ui/freeplay/LetterSort.hx @@ -284,6 +284,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"); + }); } } @@ -311,7 +314,7 @@ class FreeplayLetter extends FlxAtlasSprite animName = "T move"; } - this.anim.play(animName); + this.anim.play(animName, true); if (curSelection != curLetter) { this.anim.pause(); diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 2afdc67f7..c963d5832 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -82,6 +82,10 @@ class SongMenuItem extends FlxSpriteGroup public var theActualHitbox:FlxObject; + var index:Int; + + public var curSelected:Int; + public function new(x:Float, y:Float) { super(x, y); @@ -402,7 +406,7 @@ class SongMenuItem extends FlxSpriteGroup return evilTrail.color; } - public function refreshDisplay():Void + public function refreshDisplay(updateRank:Bool = true):Void { if (freeplayData == null) { @@ -420,7 +424,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; @@ -501,7 +505,7 @@ class SongMenuItem extends FlxSpriteGroup spr.visible = value; } - if (value) textAppear(); + textAppear(); updateSelected(); } @@ -512,10 +516,12 @@ class SongMenuItem extends FlxSpriteGroup this.y = y; } - public function initData(freeplayData:Null, ?styleData:FreeplayStyle = null):Void + public function initData(freeplayData:Null, ?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 @@ -543,7 +549,7 @@ class SongMenuItem extends FlxSpriteGroup var frameOutTypeBeat:Int = 0; var xFrames:Array = [1.7, 1.8, 0.85, 0.85, 0.97, 0.97, 1]; - var xPosLerpLol:Array = [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 = [0, 0, 0.16, 0.16, 0.22, 0.22, 0.245]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER var xPosOutLerpLol:Array = [0.245, 0.75, 0.98, 0.98, 1.2]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER public var realScaled:Float = 0.8; @@ -554,9 +560,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; }); @@ -617,12 +620,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; } } @@ -636,13 +646,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) @@ -669,7 +683,12 @@ class SongMenuItem extends FlxSpriteGroup } } - public function intendedY(index:Float):Float + public function intendedX(index:Int):Float + { + return 270 + (60 * (Math.sin(index))); + } + + public function intendedY(index:Int):Float { return (index * ((height * realScaled) + 10)) + 120; }