Compare commits

..
278 Commits
Author SHA1 Message Date
EricandGitHub 0b976b0d71 Merge pull request #4942 from AbnormalPoof/0.6.4-changelog-version-bump
Build and Upload nightly game builds / gather-changes (push) Canceled after 0s
Build and Upload nightly game builds / gather-tags (push) Canceled after 0s
Build and Upload nightly game builds / build-game-on-host (macos, macos) (push) Canceled after 0s
Build and Upload nightly game builds / build-game-on-host (windows, windows) (push) Canceled after 0s
Build and Upload nightly game builds / docker-image (push) Canceled after 0s
Build and Upload nightly game builds / build-game-in-container (html5) (push) Canceled after 0s
Build and Upload nightly game builds / build-game-in-container (linux) (push) Canceled after 0s
[DOCS] CHANGELOG for 0.6.4 + Bump version to 0.6.4
2025-05-02 22:28:13 -04:00
Abnormal ae1239c57d CHANGELOG for 0.6.4 2025-05-02 21:20:14 -05:00
Abnormal bfbbf9cb01 Bump game version to 0.6.4 2025-05-02 21:02:22 -05:00
EliteMasterEric d29bb47d22 Update assets submodule 2025-05-02 18:52:21 -04:00
EricandGitHub d29ecfb7c4 Merge pull request #4936 from CrusherNotDrip/bugfix/tooltip-chart-editor
[BUGFIX] Prevent the game from crashing when clicking a recent chart before tooltip shows up
2025-05-02 17:55:41 -04:00
EricandGitHub cdd17ce033 Merge pull request #4938 from AbnormalPoof/bugfix/quick-web-fix
2 more commits from #3032
2025-05-02 17:54:38 -04:00
EricandGitHub 8e8b26f029 Merge pull request #4564 from AbnormalPoof/bugfix/texture-atlas-looping
[BUGFIX] Fix texture atlas sprites looping infinitely
2025-05-02 17:52:13 -04:00
cyn0x8andAbnormal 6f43438cdb hopefully fix #4814 2025-05-02 16:41:03 -05:00
cyn0x8andAbnormal a74369c77e fixes + stronger sandboxing 2025-05-02 16:40:50 -05:00
CrusherNotDrip def1b74eb6 Stop the game from crashing when clicking a recent chart before tooltip appears 2025-05-02 16:40:31 -04:00
EricandGitHub c70fa7c94a Merge pull request #4933 from VioletSnowLeopard/patch2
Fix the Changelog on the develop branch again
2025-05-02 16:06:29 -04:00
Abnormal c0353bca2b remove extra callback (already gets called in _onAnimationComplete() by dispatching the animation name to onAnimationComplete 2025-05-02 13:53:30 -05:00
Abnormal f385cf9fd3 fix: Fix texture atlases looping indefinitely 2025-05-02 13:52:16 -05:00
EricandGitHub 301f71b428 Merge pull request #4930 from Hundrec/Lasercar/commit-recovery
<RECOVERY> Restore two of Lasercar's commits
2025-05-02 14:30:11 -04:00
EricandGitHub 189e028442 Merge pull request #4931 from ACrazyTown/fix/crash-log-memory-overflow
Fix memory overflow in crash log (Redo)
2025-05-02 14:30:03 -04:00
EricandGitHub a9647cf9fc Merge pull request #4617 from Lasercar/destroy-the-volume-plugin
[BUGFIX] Destroy the volume plugin when wiping save data
2025-05-02 14:29:54 -04:00
EricandGitHub 05b9d68645 Merge pull request #4227 from Lasercar/main-menu-not-remembering-freeplay-menu-selection-fix
Main menu not remembering freeplay menu selection fix
2025-05-02 14:29:29 -04:00
EricandGitHub 45f0f14ed9 Merge pull request #4318 from Lasercar/fix-strumline-offset-when-downscroll
Fix strumline offset when downscroll
2025-05-02 14:29:11 -04:00
EricandGitHub a8262a8d3b Merge pull request #3090 from anysad/fix-animation-file-save
[BUGFIX] Animation Editor not saving the file name
2025-05-02 14:21:46 -04:00
EricandGitHub 264c2d8e77 Merge pull request #4266 from NotHyper-474/bugfix/chart-editor-broken-resize
[BUGFIX] Chart Editor - Fix resolution breaking after returning from PlayState
2025-05-02 14:21:33 -04:00
EricandGitHub b2f231e889 Merge pull request #4871 from NotHyper-474/bugfix/initstate-spam-medal-crash-fix
[BUGFIX] Fix crash when spamming enter when opening game (properly this time)
2025-05-02 14:21:21 -04:00
VioletSnowLeopard 34bb201104 Fix the changelog again 2025-05-02 11:02:29 -04:00
HundrecandLasercar d9fcaf0e63 Restore Lasercar's changes
Dropped them somewhere whoopsie

Co-Authored-By: Lasercar <64717068+Lasercar@users.noreply.github.com>
2025-05-02 05:02:29 -06:00
EricandGitHub 56259b9143 Merge pull request #4923 from Hundrec/negative-freeplay-clear
[BUGFIX] Fix `Fix Fix scoring changes`
2025-05-01 23:43:49 -04:00
HundrecandHyper_ dc2acac152 no more negative clear percent shenanigans
"a thousand curses upon you for not testing your prs before making them /j"
- AbnormalPoof

Co-Authored-By: Hyper_ <40342021+NotHyper-474@users.noreply.github.com>
2025-05-01 21:23:55 -06:00
EricandGitHub bf5a669592 Merge pull request #4919 from Kade-github/real-capsule-fix
Sorting in freeplay is weird
2025-05-01 21:56:24 -04:00
kade-github 8b9775d91f the falsening by VioletSnowLeopard 2025-05-01 18:50:03 -07:00
EricandGitHub 6caeda9a9d Merge pull request #4898 from Lasercar/fix-fix-scoring-changes
[BUGFIX] Fix `Fix scoring changes`
2025-05-01 21:42:29 -04:00
EricandGitHub 9983556748 Merge pull request #4358 from VioletSnowLeopard/fix-special-pause-menu-chance
[BUGFIX] Fix the chance of getting the special pause menu
2025-05-01 21:40:58 -04:00
EliteMasterEric 651298e908 Update assets submodule 2025-05-01 21:37:52 -04:00
Hyper_ 142ea6db0c fix: Force UI Camera resize 2025-05-01 22:32:02 -03:00
EricandGitHub 5397924edd Merge pull request #4913 from VioletSnowLeopard/fix-switching-filters
[BUGFIX] Fix the Behavior of Selected Song when Changing Filters in Freeplay
2025-05-01 21:24:52 -04:00
EliteMasterEric 94bd18fe17 Merge remote-tracking branch 'public/main' into develop 2025-05-01 21:10:40 -04:00
EricandGitHub 664d9e0fb3 Merge pull request #4214 from Lasercar/freeplay-ui-fixes
Freeplay UI fixes
2025-05-01 21:07:51 -04:00
VioletSnowLeopard edf6889af9 fix switching filters to do its intended behavior 2025-05-01 20:57:39 -04:00
EricandGitHub c65dddacc0 Merge pull request #4894 from AbnormalPoof/chore/art-submod
[GITHUB] Update `art` submodule to the latest commit
2025-05-01 20:43:41 -04:00
EricandGitHub 659ea41206 Merge pull request #4649 from NotHyper-474/feature/chart-more-song-info
[ENHANCEMENT] Chart Editor - Display current beat & step
2025-05-01 20:37:40 -04:00
EricandGitHub 5cd47a4414 Merge pull request #4553 from VioletSnowLeopard/fix-dialogue-pause-menu
Change order of Restart and Skip in Dialogue pause menu
2025-05-01 20:37:18 -04:00
Eric 65699ee865 Update CONTRIBUTING.md 2025-05-01 01:38:44 -04:00
HundrecandEric d423eb3ddd Update Charting PR policy in CONTRIBUTING.md
We no longer accept chart modifications.
2025-05-01 01:38:44 -04:00
unknown c67085a46a Fix memory overflow in crash log 2025-04-30 23:41:14 +02:00
Hyper_ a83a4a599c fix: Null-check MedalList's internal map 2025-04-30 12:31:01 -03:00
Lasercar f6ac4cad43 Whoops, missed this one 2025-04-30 23:58:09 +10:00
VioletSnowLeopard 0101bae7ec convert decimal chance to percent 2025-04-30 09:31:22 -04:00
Abnormal 4c5335d7a3 art submod 2025-04-30 02:37:13 -05:00
EliteMasterEric b811ca91a6 Update link to art submodule to include latest assets 2025-04-30 02:01:54 -04:00
Hyper_andEric c5f2a74494 fix: Prevent loading invalid variation 2025-04-30 00:44:12 -04:00
KoloandEric 3e0dbe2758 performCleanup on reload 2025-04-30 00:43:37 -04:00
bbc0546c74 Shortened statement based on suggestion.
Co-authored-by: Abnormal <86753001+AbnormalPoof@users.noreply.github.com>
2025-04-30 00:42:15 -04:00
thesuperpig56andEric 617d5ffa06 Called the wrong character here, whoops! 2025-04-30 00:42:15 -04:00
thesuperpig56andEric 73114e2da6 Avoid crash when restarting song without characters. 2025-04-30 00:42:15 -04:00
KoloandEric 808698ace7 medal fix thing 2025-04-30 00:41:28 -04:00
VioletSnowLeopardandEric 832bc5bd83 fix two issues with random capsule 2025-04-30 00:37:08 -04:00
TechnikTilandEric b15e809c63 fix freeplay dj softlock 2025-04-29 18:01:55 -04:00
LasercarandEric 8074d57186 Don't add the default difficulty to song metadata
It'll now only add the current difficulty to the metadata if it doesn't exist in it when saved.
2025-04-29 18:01:23 -04:00
KoloandEric 31d3718e5c music file priority thing
(updated a misleading comment, lols!)
2025-04-29 16:27:21 -04:00
LasercarandEric 5fdbd23a17 Misses now actually make you miss your grade
Sustain notes no longer add to the miss tally
2025-04-29 16:24:50 -04:00
EricandGitHub 23a9e7f944 Merge pull request #4881 from Lasercar/fix-preferences-not-saving-due-to-enums-AGAIN
[BIGFIX] Fix Options not loading from Save Data
2025-04-29 16:23:47 -04:00
EricandGitHub 0397a41a7f Merge pull request #4883 from Hundrec/patch-11
[DOCS] Credit Lasercar for a crash fix from an assets PR
2025-04-29 16:23:31 -04:00
Lasercar afbb335993 Destroy the volume plugin 2025-04-29 20:23:52 +10:00
Lasercar 2bc1a31427 fix capsule jumpin and random capsule starting x position 2025-04-29 20:04:56 +10:00
Lasercar dafbb43532 capsule jump in 2025-04-29 19:59:35 +10:00
Lasercar 4978c89ac3 capsule options menu null reference fix 2025-04-29 19:57:31 +10:00
LasercarandLasercar 29b541af38 First rank overrides itself fix 2025-04-29 19:57:31 +10:00
LasercarandLasercar fc09350be7 This should fix the squashed capsule text, right? 2025-04-29 19:57:23 +10:00
LasercarandLasercar e1ca3c69f0 Fixed letter sort animation 2025-04-29 19:53:32 +10:00
LasercarandLasercar a15bd36943 Freeplay UI fixes
Input is disabled as soon as possible when a capsule is open
Input re-enables after capsule errors
instrument selector uses the capsule's target position as it's position, rather than the capsule's position
2025-04-29 19:53:32 +10:00
HundrecandGitHub 51605e50f8 Credit Lasercar for a crash fix from assets PR 2025-04-29 01:31:19 -06:00
Lasercar 3a1b573ea3 Fix save being loaded twice for no reason 2025-04-29 17:14:23 +10:00
Lasercar ef6b214872 Save vsync mode as string but get as vsync mode 2025-04-29 17:14:23 +10:00
EricandGitHub 8f2a8c8335 Merge pull request #4873 from NotHyper-474/patch-1
Remove leftover from merge-conflict in CHANGELOG
2025-04-28 17:59:02 -04:00
Hyper_andGitHub 7f5b4974c3 Update CHANGELOG.md
Remove left-over from merge conflict.
2025-04-28 18:30:39 -03:00
EricandGitHub d63782ee9d Merge pull request #4869 from ShadzXD/patch-3
[DOCS] Update CHANGELOG.MD to include my pull request.
2025-04-28 17:01:12 -04:00
EliteMasterEric 19b1c32124 Merge remote-tracking branch 'public/develop' into develop 2025-04-28 16:56:51 -04:00
ShadzXDandGitHub d183876514 Merge pull request #1 from Hundrec/patch-11
Tiny polish for Eggnog cutscene Changelog entry
2025-04-28 22:50:36 +02:00
HundrecandGitHub db0ac48192 Tiny polish for Eggnog cutscene Changelog entry 2025-04-28 14:47:42 -06:00
EricandGitHub ba37405e4b Merge pull request #4868 from Hundrec/remove-changelog
Remove Changelog from game files
2025-04-28 16:40:53 -04:00
ShadzXDandGitHub 66197b34f6 Update CHANGELOG.md 2025-04-28 22:40:26 +02:00
HundrecandHyper_ a7da71c8a8 Remove Changelog from game files
since it keeps getting revised after each release

Co-Authored-By: Hyper_ <40342021+NotHyper-474@users.noreply.github.com>
2025-04-28 14:38:52 -06:00
EricandGitHub 5bd081c2af Merge pull request #4866 from Hundrec/0.6.3-changelog-additions
[DOCS] Additional 0.6.3 Changelog bugfix entries + updated commit links
2025-04-28 16:26:51 -04:00
EricandGitHub aefc4db277 Merge branch 'main' into 0.6.3-changelog-additions 2025-04-28 16:26:43 -04:00
EricandGitHub adc14cc15d Merge pull request #4867 from FunkinCrew/develop2
Copied some files that weren't in main before back into develop
2025-04-28 16:21:54 -04:00
EricandGitHub a8294a9cce Merge pull request #4865 from FunkinCrew/0.6.3-release
Move public fix to main branch
2025-04-28 16:15:57 -04:00
EliteMasterEric 80569c806c Merge remote-tracking branch 'public/main' into 0.6.3-release 2025-04-28 16:15:09 -04:00
EliteMasterEric d6837eba2e Merge remote-tracking branch 'public/main' into 0.6.3-release 2025-04-28 16:14:37 -04:00
HundrecandGitHub 924b1cf386 Remove Newgrounds authentication entry
because it wasn't actually fixed
2025-04-27 20:43:09 -06:00
HundrecandGitHub 0839c16535 More 0.6.3 entries + updated commit links 2025-04-27 06:04:15 -06:00
HundrecandGitHub db3e012cb0 [DOCS] Changelog for 0.6.3 (#4816)
* Changelog for 0.6.3

* Clarify mod API version entry

* 0.6.3 Changelog tweaks
2025-04-26 05:18:01 -04:00
6c2ebff680 bump to 0.6.3 (#4448)
Co-authored-by: Eric <ericmyllyoja@gmail.com>
2025-04-26 00:48:37 -04:00
EricandGitHub df0f915be5 Merge pull request #1014 from FunkinCrew/kade-github/pico-mix-bugfix
Build and Upload nightly game builds / gather-changes (push) Canceled after 0s
Build and Upload nightly game builds / gather-tags (push) Canceled after 0s
Build and Upload nightly game builds / build-game-on-host (macos, macos) (push) Canceled after 0s
Build and Upload nightly game builds / build-game-on-host (windows, windows) (push) Canceled after 0s
Build and Upload nightly game builds / docker-image (push) Canceled after 0s
Build and Upload nightly game builds / build-game-in-container (html5) (push) Canceled after 0s
Build and Upload nightly game builds / build-game-in-container (linux) (push) Canceled after 0s
FUNK-1944 Bugfix for Pico (Pico Mix's) that would cause the doppelganger cutscene bad shit
2025-04-25 22:29:54 -04:00
EliteMasterEric 104d3cffcb Merge branch 'rewrite/master' into kade-github/pico-mix-bugfix 2025-04-25 22:29:32 -04:00
EliteMasterEric 584b1ece07 Update assets submodule 2025-04-25 22:27:57 -04:00
Eric 4555b6736e Rewrote recommendation to use Funkin haxelib and hmm 2025-04-25 21:41:16 -04:00
HundrecandEric 0c674ac23b Specify "flip horizontally" shortcut in Changelog entry 2025-04-25 21:37:11 -04:00
Eric c5e5ab16df Recommend Funkin's fork of hmm (fewer issues with Lime) 2025-04-25 17:50:45 -04:00
EliteMasterEricandKade adefa902e7 Fix several issues preventing builds on HTML5 2025-04-25 12:14:28 -07:00
kade-github 63f216ea71 assets submodule update 2025-04-25 12:06:48 -07:00
EliteMasterEric 03d46084b4 Update game version to v0.6.3. 2025-04-25 02:32:28 -04:00
kade-githubandCameron Taylor 19eb138666 vwooshin for retry 2025-04-25 00:32:07 -04:00
EliteMasterEricandCameron Taylor e21b01aa66 Fix a broken usage. 2025-04-25 00:32:00 -04:00
EliteMasterEricandCameron Taylor c41c800998 Fix a bug where render distance ignores scroll speed (test on 0.5x scroll speed), then do some code cleanup and documentation. 2025-04-25 00:32:00 -04:00
Cameron Taylor b7675aa440 assets submod (Cheer overrides lightning scare from kade) 2025-04-25 00:20:46 -04:00
Cameron TaylorandKade 745f49ca6d tiny little cleans in char select nametag 2025-04-24 19:26:35 -07:00
Cameron TaylorandKade 78646c4635 fix for difficulty stars not appearing when returning from character select 2025-04-24 19:14:11 -07:00
Cameron Taylor f1cc092820 quickie haxe checkstyle thing that annoy me all the time grr.. 2025-04-24 21:29:18 -04:00
Cameron Taylor 7e200241ef assets submod (satin panties fixins) 2025-04-24 20:54:57 -04:00
EricandGitHub 1c4b8751b7 Pico Results Screen Easter Egg (#1007) 2025-04-23 16:52:29 -07:00
926d8c0517 Contributor's Guide Maintenance Policy
Co-authored-by: Hundrec <hundrecard@gmail.com>
Co-authored-by: Abnormal <86753001+AbnormalPoof@users.noreply.github.com>
2025-04-23 17:06:13 -04:00
EliteMasterEricandCameron Taylor e290e0c702 Make merch referrals error-proof (they don't work if the API is disabled whoops!) 2025-04-22 19:14:15 -04:00
nebulazoruaandCameron Taylor 53dec057be vuln patch part 1
so now you cant open files as easily i think

oops
2025-04-22 19:14:15 -04:00
Cameron Taylor b3f1798320 assets submod (tankman deathlines for bf) 2025-04-22 19:02:58 -04:00
Hyper_andCameron Taylor f20b938f3f fix: Pad number with 1 decimal place with zero
Merge pull request #1000 from FunkinCrew/hyper/chart-editor-display-beat

[PUBLIC PR] Display current beat and step in the Chart Editor
2025-04-22 19:02:56 -04:00
Hyper_andCameron Taylor 85915eae8c Implement suggestion: Fractional beat display 2025-04-22 19:02:09 -04:00
Hyper_andCameron Taylor 83bb3bb507 feat: Show current beat and step next to song position 2025-04-22 19:02:09 -04:00
KadeandCameron Taylor 642b2142ca Merge pull request #999 from FunkinCrew/kolo/chart-event-crash-fix
[PUBLIC PR] Fix a crash when messing with events in the chart editor
2025-04-22 19:00:24 -04:00
KadeandCameron Taylor 0c6b730647 Merge pull request #998 from FunkinCrew/kolo/cap-score-display
[PUBLIC PR] Cap Score Displays to their biggest possible values
2025-04-22 19:00:24 -04:00
KoloandCameron Taylor 83c367e23b another bugfix yeah
i hate dropdowns i hate dropdowns i hate dropdowns
2025-04-22 19:00:24 -04:00
KadeandCameron Taylor fe7bf3dd55 Merge pull request #994 from FunkinCrew/cyn0x8/fileutil-sandbox
[PUBLIC PR] Sandboxing for FileUtil (prevent deleting important game files etc)
2025-04-22 19:00:24 -04:00
KadeandCameron Taylor 273d950899 Merge branch 'rewrite/master' into kolo/cap-score-display 2025-04-22 19:00:24 -04:00
KoloandCameron Taylor 9e61481cfe Merge branch 'develop' of https://github.com/FunkinCrew/Funkin into chart-events-fix 2025-04-22 19:00:24 -04:00
SuperandCameron Taylor e572c7c2fb Remove FlxG.fixedTimeStep from PlayState
It's in TitleState now
2025-04-22 19:00:24 -04:00
cyn0x8andCameron Taylor 95ade2a08b FileUtil overhaul + sandboxing 2025-04-22 19:00:24 -04:00
KoloandCameron Taylor 51324e9c28 mmyes 2025-04-22 19:00:24 -04:00
AbnormalandCameron Taylor e6e3a30c0c docs: Add missing documentation to the remaining functions in Module 2025-04-22 19:00:24 -04:00
SuperandCameron Taylor 2b7254fbd2 Add FlxG.fixedTimestep change to initState 2025-04-22 19:00:24 -04:00
HundrecandCameron Taylor d54df57b95 Two more Chart Editor entries in Changelog 2025-04-22 19:00:24 -04:00
Cameron Taylor b295e31266 Merge branch 'rewrite/master' into public-changes 2025-04-22 19:00:24 -04:00
HundrecandCameron Taylor 695c708cb0 Add a few fixes and changes from 0.6.0 2025-04-22 19:00:24 -04:00
EliteMasterEricandCameron Taylor 4a7f56a7ad Merge remote-tracking branch 'public/develop' into develop 2025-04-22 19:00:24 -04:00
HundrecandCameron Taylor 030abed207 Oops, one more lil space 2025-04-22 19:00:24 -04:00
HundrecandCameron Taylor 0d8cfaf104 Remove 0.6.0 Changelog entries that weren't implemented + polish 2025-04-22 19:00:24 -04:00
EliteMasterEricandCameron Taylor caf708fe4a Merge remote-tracking branch 'public/main' into develop 2025-04-22 18:41:41 -04:00
HundrecandCameron Taylor e7cf5fbbdc Grammatical fix in style-guide.md 2025-04-22 18:41:41 -04:00
HundrecandCameron Taylor ccaa3d1070 Consistent hyphens in CODESTYLE.md 2025-04-22 18:41:41 -04:00
4b883d4339 Restore Contributing Guide to latest version
Merge conflicts yay

Co-Authored-By: Abnormal <86753001+AbnormalPoof@users.noreply.github.com>
2025-04-22 18:41:41 -04:00
sector-aandEric b40b4b0369 Fix Animation Editor Crash
Fix poor code formatting

This is unnecessary now
2025-04-22 15:53:53 -04:00
Cameron TaylorandEric aa37745bbd assets submod (week 6 shader fixin) 2025-04-22 15:51:45 -04:00
EliteMasterEricandCameron Taylor df6ffca01f Update assets submodule (charting revert stuff) 2025-04-19 19:41:27 -04:00
EliteMasterEricandCameron Taylor 9dd8a257df Update assets submodule to include twenty-seven chart tweak commits 2025-04-19 19:38:18 -04:00
mint 2984ce4852 hotfix(ci): use our own runners again :) 2025-04-19 03:23:27 +02:00
mint 8fb35ac822 hotfix(ci): for now, disable fail-fast so we can get some windows builds 2025-04-19 03:17:47 +02:00
mint e8c42c281f hotfix: mac lime ndlls again? 2025-04-19 02:58:52 +02:00
mint 68892b92ab hotfix(ci): bump neko version to 2.4.1 2025-04-19 02:32:22 +02:00
Cameron Taylor 12ee60f776 Mac ndlls lime hmm bump 2025-04-18 20:18:39 -04:00
Cameron Taylor 945314d271 lime bump for windows fix 2025-04-18 19:41:53 -04:00
EliteMasterEricandCameron Taylor 93d4f30cad Make ScreenshotPlugin null-safe. 2025-04-18 19:40:39 -04:00
AbnormalandCameron Taylor dda91cf770 change requested by eric 2025-04-18 19:35:24 -04:00
AbnormalandCameron Taylor 6a1160feab chore: Enable null safety for 11 classes 2025-04-18 19:35:24 -04:00
kade-githubandEric b48b5ae1aa remember diff for generate song list 2025-04-18 17:33:31 -04:00
fabsandCameron Taylor 5bf8154e0b otis muzzle flashing (assets submod) 2025-04-17 19:59:59 -04:00
EliteMasterEricandCameron Taylor 6de51848fd Update assets submodule 2025-04-17 19:59:59 -04:00
Cameron Taylor ee906ae8c0 fix: make the sound tray volume be less loud (#990) 2025-04-17 19:59:59 -04:00
7eebce432d [PUBLIC PR] Prevent softlock caused by removing all keybinds for a UI control (#988)
* prevent removing last keybind for UI controls

* add popup to alert you cannot remove keybind

* ensure controls list is unique

* Use public submodule URLs (would be nice if this was automatic but oh well)

---------

Co-authored-by: VioletSnowLeopard <ngzeretzke@gmail.com>
Co-authored-by: EliteMasterEric <ericmyllyoja@gmail.com>
2025-04-17 19:59:59 -04:00
EliteMasterEric 750779a3fb Disable the pop-in animation on the Random capsule for consistency. 2025-04-17 18:25:44 -04:00
kade-githubandEric 095bc9006d animation on all diffs 2025-04-17 18:25:44 -04:00
kade-githubandEric 09fdd0c91e going to erect with the erect takes you to the closest erect 2025-04-17 18:25:44 -04:00
kade-githubandEric be5d6227a7 erect nearest neighbor select 2025-04-17 18:25:44 -04:00
KoloandCameron Taylor 855deb5828 set enabled when prompt gone 2025-04-16 16:06:39 -04:00
EliteMasterEricandCameron Taylor 963e2ecd63 Edit RegistryMacro to automatically populate baseGameEntryIds list. 2025-04-15 20:25:18 -04:00
lemz1andCameron Taylor 14975212a9 macro
registry macro

Merge branch 'lemz1/registry-entry-data' into lemz1/registry-macro-stufff
2025-04-15 20:25:18 -04:00
Hyper_andCameron Taylor 3ad14bac32 fix: Double check if events are the same when doing indexOf
This is basically copy-pasted from SongNoteDataArrayTools lol
2025-04-15 19:47:39 -04:00
Cameron Taylor e672c0978f hmm lime bump 2025-04-14 23:35:43 -04:00
Cameron Taylor 0d5b7d4ed4 also conditional for WindowUtils.setVSyncMode() for mac 2025-04-14 23:27:34 -04:00
kade-githubandCameron Taylor 7e90423495 Pre-Processor to disable VSync on MacOS 2025-04-14 23:27:34 -04:00
kade-githubandCameron Taylor c7c2620217 Set vsync on launch (might need to be set elsewhere too) 2025-04-14 21:42:24 -04:00
kade-githubandCameron Taylor 3944816021 Error handling from wiki 2025-04-14 21:42:24 -04:00
kade-githubandCameron Taylor db00feff93 Vsync does work now 2025-04-14 21:42:24 -04:00
kade-githubandCameron Taylor 6e2a077d6b Fixes from merge 2025-04-14 21:42:22 -04:00
EliteMasterEricandCameron Taylor 1ee8246b48 Added a preference to enable VSync. 2025-04-14 21:40:58 -04:00
EricandCameron Taylor 20c3228d8c Don't discount the score so much on miss, but still keep it meaningful. 2025-04-14 19:32:55 -04:00
EliteMasterEricandCameron Taylor 01bf3bee11 Dropping hold notes should no longer cost health by default. 2025-04-14 19:32:55 -04:00
EliteMasterEricandCameron Taylor 4d9638f0ac Fix the clamping. 2025-04-14 19:32:55 -04:00
EliteMasterEricandCameron Taylor b00589367c Make hold drop penalty threshold match the hit window. 2025-04-14 19:32:55 -04:00
EliteMasterEricandCameron Taylor 577421a782 Scoring rework: Penalize the player (deducting health and score) for dropping a hold note before it's finished. 2025-04-14 19:32:54 -04:00
EliteMasterEricandCameron Taylor 72e9fb641e Scoring rework: Reduce health given by hold notes by 20%. 2025-04-14 19:32:16 -04:00
EliteMasterEricandCameron Taylor 429f6f9bc0 Remove the unused input splitter. 2025-04-14 19:32:16 -04:00
EliteMasterEricandCameron Taylor 550bf95c76 Split miss penalty and ghost miss penalty into two variables (but make them equal for now). 2025-04-14 19:32:16 -04:00
EliteMasterEricandCameron Taylor da686ed0c4 Scoring rebalance: Misses now reduce your final grade, which impacts your rank 2025-04-14 19:32:16 -04:00
EliteMasterEricandCameron Taylor 9f3711bf43 Scoring rebalance: Misses are now worth -250 points (up from -10). 2025-04-14 19:32:16 -04:00
EliteMasterEricandCameron Taylor 6390725ef0 Redo data format for sticker packs to make it simpler and allow for song-specific selections. 2025-04-14 19:25:42 -04:00
AbnormalandCameron Taylor cdc468ba15 feat: Sticker Registry 2025-04-14 19:25:29 -04:00
EliteMasterEricandCameron Taylor deb4e1921d Fix an issue where Stress would crash on the second playthrough 2025-04-14 15:45:29 -04:00
KoloandCameron Taylor 068c9fb43d no thoughts save empty
also more code clarity ig
2025-04-14 15:28:14 -04:00
AbnormalandCameron Taylor 5be4f3bbb9 chore: Remove ComboMilestone 2025-04-14 15:28:14 -04:00
AbnormalandCameron Taylor b83ae65a65 docs: Add missing documentation to the remaining functions in Module 2025-04-14 15:28:14 -04:00
HundrecandCameron Taylor dc876be57c Two more Chart Editor entries in Changelog 2025-04-14 15:28:14 -04:00
HundrecandCameron Taylor c08847f48d Add a few fixes and changes from 0.6.0 2025-04-14 15:28:14 -04:00
HundrecandCameron Taylor 19912b5f89 Oops, one more lil space 2025-04-14 15:28:14 -04:00
HundrecandCameron Taylor 019473dec2 Remove 0.6.0 Changelog entries that weren't implemented + polish 2025-04-14 15:28:14 -04:00
EliteMasterEricandCameron Taylor f001562270 Update HXVLC to 2.1.2 2025-04-13 21:45:39 -04:00
HundrecandEric 34bb58015d Add Combo Milestones to Rejected Features list 2025-04-13 00:37:16 -04:00
AbnormalandEric d86fe68d80 docs: Add missing documentation to the remaining functions in Module 2025-04-12 00:54:04 -04:00
LasercarandEric 1434820d10 remove removed debug keys 2025-04-12 00:53:51 -04:00
HundrecandEric 08fb5b8459 Two more Chart Editor entries in Changelog 2025-04-12 00:53:29 -04:00
Hyper_andEric 8e8dcaa7f9 docs: (TROUBLESHOOTING.md) Correct outdated information about Lime troubleshooting
The commit of Lime that Funkin uses now includes Linux NDLLs. These are built on a system with GLibC 2.38 though which breaks compatibility with lots of stable distributions troubleshooting was added for those systems too.
2025-04-12 00:53:18 -04:00
Hyper_andEric b35059ec99 docs: (COMPILING.md) Remove outdated info about Lime fork 2025-04-12 00:53:18 -04:00
EliteMasterEricandCameron Taylor b28ba8f465 Update assets submodule 2025-04-11 16:07:53 -04:00
EliteMasterEricandCameron Taylor dcc8085691 Update assets submodule 2025-04-11 16:00:16 -04:00
EricandCameron Taylor c186666402 Update mod version number (this requires mods to check they are working, then change their metadata) 2025-04-11 15:58:51 -04:00
EliteMasterEricandCameron Taylor 6f93960cb5 Update HaxeUI to their latest versions. 2025-04-11 15:58:34 -04:00
Cameron TaylorandEric a756ceac22 properly initialize soundtray on game start if muted 2025-04-10 18:43:54 -04:00
Cameron TaylorandEric c394520019 small cleanups / tidying of FunkinSoundTray.hx 2025-04-10 18:43:54 -04:00
Cameron TaylorandEric 2d93baf968 properly save mute preferences by doing the save on show() in the soundtray 2025-04-10 18:43:54 -04:00
EliteMasterEricandCameron Taylor 4a98e6999f Update assets submodule 2025-04-09 18:13:30 -04:00
EliteMasterEricandCameron Taylor 9ff14aed42 Fix an issue where Pico's retry animation is broken on Ugh/Guns Pico Mix 2025-04-09 15:58:44 -04:00
EliteMasterEricandCameron Taylor 0a7bd3111f Fix a crash with the chart editor Note window (via Lasercar) 2025-04-09 15:52:47 -04:00
VioletSnowLeopardandCameron Taylor 1f88a3b6e1 swap restart and skip in dialogue pause menu 2025-04-09 15:33:54 -04:00
HundrecandEric fcb47ff4fc Grammatical fix in style-guide.md 2025-04-08 23:18:17 -04:00
HundrecandEric 3218ceeecd Consistent hyphens in CODESTYLE.md 2025-04-08 23:18:17 -04:00
HundrecandEric 1823373d7e Add a few fixes and changes from 0.6.0 2025-04-08 23:18:04 -04:00
HundrecandEric af14d42939 Oops, one more lil space 2025-04-08 23:18:04 -04:00
HundrecandEric 3dd3338ab2 Remove 0.6.0 Changelog entries that weren't implemented + polish 2025-04-08 23:18:04 -04:00
Hyper_ 26f2578bf2 fix: Pad number with 1 decimal place with zero 2025-04-08 16:02:03 -03:00
Hyper_ c32ade49b9 Implement suggestion: Fractional beat display 2025-04-07 23:59:55 -03:00
Hyper_ d4eee38fd2 feat: Show current beat and step next to song position 2025-04-07 21:08:01 -03:00
6543c58363 Restore Contributing Guide to latest version
Merge conflicts yay

Co-Authored-By: Abnormal <86753001+AbnormalPoof@users.noreply.github.com>
2025-04-06 03:50:32 -04:00
EliteMasterEric 158c015682 Use public URLs for git submodules 2025-04-04 15:16:58 -04:00
EricandGitHub 278bac7b6d Merge pull request #4554 from FunkinCrew/release/v0.6.2
Release the source code for v0.6.2
2025-04-04 15:03:23 -04:00
EliteMasterEric 29cddfb528 Merge remote-tracking branch 'public/main' into release/v0.6.2 2025-04-04 15:01:59 -04:00
VioletSnowLeopard da9f17cf87 swap restart and skip in dialogue pause menu 2025-04-04 14:41:33 -04:00
EliteMasterEricandCameron Taylor 02dc353032 Update assets submodule 2025-04-04 14:41:22 -04:00
EliteMasterEric d31ef12363 Use public submodule URLs (would be nice if this was automatic but oh well) 2025-04-04 14:28:12 -04:00
EliteMasterEric c7d4406a34 Fix results screen music continuing to play when using the debugger 2025-04-03 22:01:32 -04:00
EliteMasterEricandCameron Taylor 68e9daffe8 Zero out the ABot visualizer before the song starts. 2025-04-03 16:04:19 -04:00
EliteMasterEricandCameron Taylor c157338783 Fix certain characters pixel icons not displaying properly in the chart editor. 2025-04-03 16:04:07 -04:00
EliteMasterEricandCameron Taylor eb9c2c2c99 Fix Pixo's freeplay AFK animation not playing (properly this time). 2025-04-03 16:02:10 -04:00
EliteMasterEricandCameron Taylor afb4a3dd68 Remove some unused/redundant debug hotkeys. 2025-04-03 16:02:10 -04:00
Cameron Taylor 9b5032ee84 quick patch for cory 2025-04-02 21:54:27 -04:00
Cameron Taylor 62621881c0 data -> data.score 2025-04-02 16:18:58 -04:00
Cameron Taylor 52be941b45 Auto Pause -> Pause on Unfocus (was changed before but prob bad merge somewhere reverted it?) 2025-04-02 13:55:54 -04:00
Cameron Taylor c94b3a3b65 2024 -> 2025 0.6.x changelog date fixins (closes FUNK-1740) 2025-04-02 13:41:54 -04:00
EliteMasterEricandCameron Taylor 8291fe0407 Make the back of ABot's stereo display slightly wider. 2025-04-02 13:37:50 -04:00
EliteMasterEricandCameron Taylor f935526baa Update assets submodule 2025-04-02 13:36:22 -04:00
EliteMasterEricandCameron Taylor 102ac4bfc8 Fix a bug where restarting the level would add more Tankmen to the background. 2025-04-02 13:34:23 -04:00
EliteMasterEricandCameron Taylor f8b212a318 Improve null safety of medal and leaderboard classes. 2025-04-02 13:33:32 -04:00
EliteMasterEricandCameron Taylor 3bd79b822e Fix a deprecation warning. 2025-04-02 13:33:32 -04:00
EliteMasterEricandCameron Taylor 17a4be66b9 Fix highscores not submitting to the website unless they are a personal best. 2025-04-02 13:33:06 -04:00
EliteMasterEricandCameron Taylor 54cd52db76 Fix an issue where story mode medals would not be awarded without a personal best. 2025-04-02 13:33:06 -04:00
Cameron Taylor 4fa0334e7b easier script for creating macos universal binaries 2025-04-01 16:41:46 -04:00
HundrecandEric 3c6466bec7 Bump repo versions to 0.6.2 2025-03-31 21:20:18 -04:00
HundrecandEric 3ccd1b04da Bump repo versions to 0.6.1 2025-03-31 18:42:17 -04:00
HundrecandEric 439be3cb26 Update templates and docs to 0.6.0 2025-03-31 13:21:55 -04:00
871709bb7d Some additions to the Contributor's Guide
Co-authored-by: Hundrec <hundrecard@gmail.com>
Co-authored-by: Abnormal <86753001+AbnormalPoof@users.noreply.github.com>
2025-03-25 15:18:43 -04:00
HundrecandLasercar bf36a86877 Remember selection when exiting to Title Screen 2025-03-18 20:47:21 +10:00
anysadandEric 9829103671 Fix Animation Editor not saving the file name 2025-03-17 07:13:14 -04:00
HundrecandEric b74df06f48 Reword some debug hotkeys 2025-03-14 23:37:25 -04:00
LasercarandEric 055c4d9b75 Add remaining debug keys 2025-03-14 23:37:25 -04:00
Lasercar ea93ec90cb Fix strumline offset when downscroll 2025-03-15 13:20:39 +10:00
HundrecandEric 4383fcf32c Correctly list all new contributors since Weekend 1
Gives those contributors their credit!
Lists all first-time GitHub contributors, even if their change isn't listed as an entry in the Changelog.
Quadruple-checked for good measure.
2025-03-14 02:17:46 -04:00
HundrecandEric cef0cbf9d4 Remove docs entries in Changelog + add punctuation 2025-03-14 02:17:46 -04:00
HundrecandEric 7d80e6657b A few more rewordings 2025-03-11 19:26:36 -04:00
HundrecandEric bb8d3ae3dc Combine those lines
Thanks Hyper!
2025-03-11 19:26:36 -04:00
HundrecandEric 56033b3288 Changelog overhaul for 0.4.1, 0.4.0, 0.3.3, 0.3.2, 0.3.1, 0.3.0 2025-03-11 19:26:36 -04:00
HundrecandEric 6e44484a1a Changelog overhaul for 0.5.3, 0.5.2, 0.5.1
Plus added line breaks to match Keep A Changelog's format
2025-03-11 19:26:36 -04:00
Hyper_andEric 9708e20dd4 Minor adjustments
Some reformatting and shortening
2025-03-10 12:56:42 -04:00
Hyper_andEric df353a1ee8 docs: Some Linux troubleshooting 2025-03-10 12:56:42 -04:00
HundrecandEric 6c9c3e418a Add more rejected features to Contributing Guide 2025-03-10 12:54:48 -04:00
Lasercar 07c872c775 A fix for a thing 2025-02-26 01:02:42 +10:00
HundrecandEric bd3acc319c Remove issue reopening action 2025-02-22 23:28:23 -05:00
HundrecandEric 894d0743c9 Table of Contents for CONTRIBUTING.md 2025-02-16 21:36:59 -05:00
MidyGamyandEric 33f943a672 More dump save info + force crash now accurate 2025-02-14 18:10:01 -05:00
MidyGamyandEric f5efc16611 Rewording missing Funkin' Debug Hotkeys.md (thx Hundrec) 2025-02-14 18:10:01 -05:00
MidyGamyandEric 7e820f9497 Added missing debug hotkeys 2025-02-14 18:10:01 -05:00
HundrecandEric 07b6d8f86f adjust link in PR Template 2025-02-13 21:23:18 -05:00
HundrecandEric 8126cca29b PR Template 2025-02-13 21:23:18 -05:00
HundrecandEric ba78ded457 Enhancement Template 2025-02-13 21:23:18 -05:00
HundrecandEric bda5d90bbc Crash Template 2025-02-13 21:23:18 -05:00
HundrecandEric a9d0c1cb88 Compiling Help Template 2025-02-13 21:23:18 -05:00
HundrecandEric f8f533ad37 Charting Template 2025-02-13 21:23:18 -05:00
HundrecandEric 0bc0ab4eec Revert spacing 2025-02-13 21:23:18 -05:00
HundrecandEric d11027fb08 A lil spacing 2025-02-13 21:23:18 -05:00
HundrecandEric 7b9e375fea Fix Contributing link 2025-02-13 21:23:18 -05:00
HundrecandEric dd215687d4 quote 2025-02-13 21:23:18 -05:00
HundrecandEric e3eddc892d Contributing Guide in Minor Bug Template 2025-02-13 21:23:18 -05:00
HundrecandEric 36fd05b1f9 Link CONTRIBUTING.md in README 2025-02-13 21:23:18 -05:00
HundrecandEric c6ef9b17a2 Add more console outputs to Troubleshooting Guide
Includes a line about warnings and deprecated functions, as well as a line about the hxcpp prompt.
2025-02-13 19:04:07 -05:00
943c38767b Contributor's guide
Co-authored-by: Hundrec <hundrecard@gmail.com>
Co-authored-by: Abnormal <86753001+AbnormalPoof@users.noreply.github.com>
2025-02-13 19:01:55 -05:00
Kolo 71ed154b81 fix that one annoying ass bug for events 2024-12-05 22:24:49 +01:00
142 changed files with 3957 additions and 2329 deletions
+1 -2
View File
@@ -6,7 +6,6 @@ body:
- type: markdown
attributes:
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
- type: checkboxes
attributes:
label: Issue Checklist
@@ -46,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.5.3
placeholder: ex. 0.6.3
validations:
required: true
+1 -1
View File
@@ -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.5.3
placeholder: ex. 0.6.3
validations:
required: true
+1 -1
View File
@@ -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.5.3
placeholder: ex. 0.6.3
validations:
required: true
+1 -1
View File
@@ -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.5.3
placeholder: ex. 0.6.3
validations:
required: true
+1 -1
View File
@@ -30,7 +30,7 @@ runs:
echo "TIMER_HAXE=$(date +%s)" >> "$GITHUB_ENV"
- name: Install Haxe
uses: funkincrew/ci-haxe@v3.2.6
uses: funkincrew/ci-haxe@v3.2.7
with:
haxe-version: ${{ inputs.haxe }}
+2 -1
View File
@@ -118,10 +118,11 @@ jobs:
build-game-on-host:
strategy:
fail-fast: false
matrix:
include:
- target: windows
runs-on: windows-latest
runs-on: windows
- target: macos
runs-on: macos
runs-on:
+1
View File
@@ -4,6 +4,7 @@
APIStuff.hx
dump/
export/
project/
RECOVER_*.fla
shitAudio/
.build_time
+2 -2
View File
@@ -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
+188 -38
View File
@@ -4,20 +4,161 @@ All notable changes will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.6.2] - 2024-03-31
### Added
- 0.6 credits list updated
### Fixed
- Additional shader fix for Stress Pico crashing at the end (was the same issue as Senpai Pico shader error, just in a different shaderfile)
## [0.6.1] - 2024-03-31
## [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
- Updated the 0.6 credits list
### Fixed
- Additional shader fix for Stress (Pico Mix) crashing at the end (was the same issue as Senpai Pico shader error, just in a different shaderfile)
## [0.6.1] - 2025-03-31
### Fixed
- Hopefully Senpai Pico/Erect mix shader isn't brokey
- NG API encryption key was added proper, so medals + leaderboards posting should work
## [0.6.0] - 2024-03-31
## [0.6.0] - 2025-03-31
The Pit Stop 2 update!
### Added
- Added six (!) new playable songs! Check them out in the Freeplay menu for their respective characters.
- Cocoa (Pico Mix)
- Senpai (Pico Mix)
@@ -38,66 +179,75 @@ The Pit Stop 2 update!
- The new sticker system isn't fully available to mods yet, but we're working on it!
- New option in the Preferences menu: Strumline Backgrounds!
- Options in the Preferences menu now display an on-screen description when selected.
- Added a little easter egg to Pico's Good Results Screen animation.
- New song event type in the Chart Editor: Set Health Icon!
- This event is now used in Stress (Pico Mix).
- The HOME and END keys now jump to the top and bottom of the Freeplay song list, respectively. ([bb974c2](https://github.com/FunkinCrew/Funkin/commit/bb974c264270d10ff503784063e5d77bb352b3f7)) - by @AbnormalPoof in [#4103](https://github.com/FunkinCrew/Funkin/pull/4103)
- 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 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 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)
- Added script events for losing/gaining focus. ([4b127b6](https://github.com/FunkinCrew/Funkin/commit/4b127b64130f6f753d0574ec66a1672322e4bd13)) - by @AbnormalPoof in [#3721](https://github.com/FunkinCrew/Funkin/pull/3721)
- Added 10 new functions to `ReflectUtil`.([6216655](https://github.com/FunkinCrew/Funkin/commit/62166554e7a176245d1a63bd15122033044c4e40)) - by @AbnormalPoof in [#3622](https://github.com/FunkinCrew/Funkin/pull/3622), [#3809](https://github.com/FunkinCrew/Funkin/pull/3809), and [#4019](https://github.com/FunkinCrew/Funkin/pull/4019)
- Added 10 new functions to `ReflectUtil`. ([6216655](https://github.com/FunkinCrew/Funkin/commit/62166554e7a176245d1a63bd15122033044c4e40)) - by @AbnormalPoof in [#3622](https://github.com/FunkinCrew/Funkin/pull/3622), [#3809](https://github.com/FunkinCrew/Funkin/pull/3809), and [#4019](https://github.com/FunkinCrew/Funkin/pull/4019)
- Added `DEBUG_BUILD` value to `Constants` to indicate whether a build has debug functions enabled. ([ad45b72](https://github.com/FunkinCrew/Funkin/commit/ad45b72b1ae8eb73a12dc51bcb59f66cc55e7bbd)) - by @AbnormalPoof in [#3853](https://github.com/FunkinCrew/Funkin/pull/3853)
### Changed
- Switched from hxCodec to hxvlc for video playback. This may break a mod or two.
- Check the [Funkin Modding Docs](https://funkincrew.github.io/funkin-modding-docs/09-migration/09-02-0.5.0-to-0.6.0.html) for more info on how to update your mods.
- Polymod should now ignore `.git` files when loading mods.
- Lots of improvements to issue and pull request organization. - by @Hundrec and @AbnormalPoof
- The pause menu can now be opened and closed rapidly.
- Adjusted difficulty ratings and scroll speeds for many songs.
- Chart Editor event fields now allow for values to be as specific as desired.
- 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)
- Renamed “Auto Pause” preference to “Pause on Unfocus” for clarity. ([49a21c1](https://github.com/FunkinCrew/Funkin/commit/49a21c198236fbecbe5902bb106f78939cc6442a)) - by @JackXson-Real in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
- Made scrolling smoother in the Chart Editor. ([20d9016](https://github.com/FunkinCrew/Funkin/commit/20d90169845f1e50f849e39f4c5f818359756c78)) - by @ninjamuffin99 in [#3768](https://github.com/FunkinCrew/Funkin/pull/3768)
- The Chart Editor now clears the undo/redo history after loading a new song. ([c7bdc1a](https://github.com/FunkinCrew/Funkin/commit/c7bdc1abe265678468920ca3cc3d7eface5d6925)) - by @Lasercar in [#4308](https://github.com/FunkinCrew/Funkin/pull/4308)
- Mods with missing dependencies are now skipped instead of preventing all mods from loading. ([1c2fb43](https://github.com/FunkinCrew/Funkin/commit/1c2fb43ae16cf40be5ef94c40b047e8e772b1211)) - by @AbnormalPoof in [#3993](https://github.com/FunkinCrew/Funkin/pull/3993)
- Slightly improved flexibility for modding note hit animations. ([3aad825](https://github.com/FunkinCrew/Funkin/commit/3aad825f865c4ed87016983d44121e2c1610d332)) - by @TechnikTil in [#3936](https://github.com/FunkinCrew/Funkin/pull/3936)
- Introduced several QoL modding changes. ([785c4be](https://github.com/FunkinCrew/Funkin/commit/785c4be88b52dc1b5899013822fc004ba7d9894d)) - by @Kade-github in [#4009](https://github.com/FunkinCrew/Funkin/pull/4009)
- Lots of smaller bug fixes.
- Lots of smaller changes.
### Fixed
- Shaders no longer create thin seams within atlas sprites.
- Completing a song in Practice Mode no longer plays a new rank animation in the Freeplay menu.
- Fixed lots of charting issues across many songs.
- The Chart Editor grid now properly adjusts to the new BPM after switching variations.
- Fixed a few crashes in the Stage Editor.
- Fixed a bug where the song would restart from the beginning instead of moving to the Results screen. ([3667c51](https://github.com/FunkinCrew/Funkin/commit/3667c51c1efe14cfe7c810e2f35991f08f50781a)) - by @KoloInDaCrib in [#4309](https://github.com/FunkinCrew/Funkin/pull/4309) and @Lasercar in [#4330](https://github.com/FunkinCrew/Funkin/pull/4330)
- 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. ([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. ([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. ([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 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.
## New Contributors for 0.6.0
* @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)
* @Lasercar made their first contribution in [#4100](https://github.com/FunkinCrew/Funkin/pull/4065)
* @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)
* @JackXson-Real made their first contribution in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
# Fixed
- Fixed a bug where the song would restart from the beginning instead of moving to the Results screen. ([3667c51](https://github.com/FunkinCrew/Funkin/commit/3667c51c1efe14cfe7c810e2f35991f08f50781a)) - by @KoloInDaCrib in [#4309](https://github.com/FunkinCrew/Funkin/pull/4309) and @Lasercar in [#4330](https://github.com/FunkinCrew/Funkin/pull/4330)
- 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 mouse cursor is no longer visible outside of debug editors. ([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)
- 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 millions place digit of the Freeplay score now updates properly. ([3719ca6](https://github.com/FunkinCrew/Funkin/commit/3719ca6ce7a170c3c890cdb2aeea3e4534b91736)) - by @Lasercar in [#4065](https://github.com/FunkinCrew/Funkin/pull/4065)
- 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)
- 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)
- 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.
## [0.5.3] - 2024-10-18
@@ -374,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.
+3 -3
View File
@@ -14,8 +14,8 @@ Code Quality is handled by the `vshaxe.haxe-checkstyle` extension, which include
* `IndentationCharacter` checks what is used to indent, `Indentation` checks how deep the indentation is.
* `CommentedOutCode` check is in place because old code should be retrieved via Git history.
* TODO items: Enable these one-by-one and fix them to improve the overall code quality.
- Reconfigure `MethodLength`
- Reconfigure `CyclomaticComplexity`
- Re-configure `MethodLength`
- Re-configure `CyclomaticComplexity`
- Re-enable `MagicNumber`
- Re-configure `NestedControlFlow`
- Re-configure `NestedIfDepth`
@@ -28,5 +28,5 @@ Prettier automatically handles formatting of JSON files, and can be configured i
### Prettier Notes
* Prettier will automatically attempt to place expressions on a single line if they fit, but will keep them multi-line if they are manually made multi-line.
* This means that long singleline objects are automatically expanded, and short multiline objects aren't automatically collapsed.
* This means that long single-line objects are automatically expanded, and short multi-line objects aren't automatically collapsed.
* You may want to use regex replacement to manually remove the first newline in short multi-line objects to convince Prettier to collapse them.
+1 -1
Submodule art updated: 4efa0536a3...8402339e2e
+1 -1
Submodule assets updated: 24eb271f3b...c108a7ff0d
+1 -1
View File
@@ -102,7 +102,7 @@ RUN <<EOF
# | jq '.[] | select(.name == "'"$neko_version"'")' \
# | jq '.assets[] | select(.name | endswith("linux64.tar.gz"))' \
# | jq -r '.browser_download_url')
neko_url="https://geo.thei.rs/funkin/neko-2.4.0-linux64.tar.gz"
neko_url="https://geo.thei.rs/funkin/neko-2.4.1-linux64.tar.gz"
curl -fL "$neko_url" | tar -xz -C /usr/local
EOF
+1 -1
View File
@@ -347,7 +347,7 @@
{
"props": {
"allowSingleLineStatement": true,
"tokens": ["FOR", "IF", "ELSE_IF", "WHILE", "DO_WHILE"]
"tokens": ["FOR", "ELSE_IF", "WHILE", "DO_WHILE"]
},
"type": "NeedBraces"
},
+7 -6
View File
@@ -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
+10
View File
@@ -0,0 +1,10 @@
# Mac Compiling Guide + Considerations
There's a few extra considerations when compiling FNF for Mac that *we* have to handle when creating a wider release.
- [Creating a Universal Binary](#creating-a-universal-binary)
- Code-signing
- Notarizing
## Creating a Universal Binary
Run the `art/macos-universal.sh` script, which automatically compiles release versions of both arm64 and x86 of Funkin. You can also see there for reference of how it's done.
+184 -66
View File
@@ -1,6 +1,6 @@
# Contributing
Welcome to the Contributing Guide!
You can contribute to the Funkin' repository by opening issues or pull requests.
You can contribute to the Funkin' repository by opening issues or pull requests.
This guide will cover best practices for each type of contribution.
@@ -11,7 +11,7 @@ This guide will cover best practices for each type of contribution.
[Part 1: Etiquette](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#part-1-etiquette)
<details>
<summary><a href="https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#part-2-issues">Part 2: Issues</a></summary>
<summary><a href="https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#part-2-issues">Part 2: Issues</a></summary>
* [Requirements](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#requirements)
@@ -24,8 +24,8 @@ This guide will cover best practices for each type of contribution.
</details>
<details>
<summary><a href="https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#part-3-pull-requests">Part 3: Pull Requests</a></summary>
<summary><a href="https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#part-3-pull-requests">Part 3: Pull Requests</a></summary>
* [Choosing a base branch](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#choosing-a-base-branch)
@@ -39,6 +39,8 @@ This guide will cover best practices for each type of contribution.
* [funkin.assets PRs](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#funkinassets-prs)
* [Charting PRs](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#charting-prs)
</details>
[Closing](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md#closing)
@@ -54,11 +56,11 @@ This guide will cover best practices for each type of contribution.
# Part 2: Issues
Issues serve many purposes, from reporting bugs to suggesting new features.
This section provides guidelines to follow when [opening an issue](https://github.com/FunkinCrew/Funkin/issues).
This section provides guidelines to follow when [opening an issue](https://github.com/FunkinCrew/Funkin/issues).
## Requirements
Make sure you're playing:
- the latest version of the game (currently v0.5.3)
- the latest version of the game (currently v0.6.3)
- without any mods
- on [Newgrounds](https://www.newgrounds.com/portal/view/770371) or downloaded from [itch.io](https://ninja-muffin24.itch.io/funkin)
@@ -72,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 |
@@ -84,7 +87,7 @@ Here's a list of commonly suggested features and the reasons why they won't be a
| "Philly Glow" Effect from Psych Engine | https://github.com/FunkinCrew/Funkin/issues/3788#issuecomment-2688966982 |
| Importing Charts from Psych Engine (and other mod content) | https://github.com/FunkinCrew/Funkin/issues/2586#issuecomment-2125733327 |
| Backwards Compatibility for Modding | https://github.com/FunkinCrew/Funkin/issues/3949#issuecomment-2608391329 |
| Lua Support | https://github.com/FunkinCrew/Funkin/issues/2643#issuecomment-2143718093 |
| Lua Support | https://github.com/FunkinCrew/Funkin/issues/2643#issuecomment-2143718093 |
## Issue Types
@@ -122,6 +125,9 @@ Also only report one issue or enhancement at a time! If you have multiple bug re
Once you're sure your issue is unique and specific, feel free to submit it.
> [!IMPORTANT]
> **DO NOT CLOSE YOUR ISSUE FOR ANY REASON!** Your issue will be taken care of by a moderator!
**Thank you for opening issues!**
# Part 3: Pull Requests
@@ -133,6 +139,7 @@ When creating a branch in your fork, base your branch on either the `main` or `d
> [!CAUTION]
> Avoid using your fork's default branch (`main` in this case) for your PR. This is considered an [anti-pattern](https://jmeridth.com/posts/do-not-issue-pull-requests-from-your-master-branch/) by GitHub themselves!
> Instead, make a separate branch for your additions (ex. `docs/fix-typo` or `minor-bugfix`).
Choose the `main` branch if you modify:
- Documentation (`.md` files)
@@ -148,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.
@@ -165,85 +184,173 @@ This process reapplies your changes on top of the updated branch and cleanly res
> This guide does not cover compiling. If you have trouble compiling the game, refer to the [Compilation Guide](https://github.com/FunkinCrew/Funkin/blob/main/docs/COMPILING.md).
Code-based PRs make changes such as **fixing bugs** or **implementing new features** in the game.
This involves modifying one or several of the repository’s `.hx` files, found within the `source/` folder.
### Codestyle
Before submitting your PR, check that your code follows the [Style Guide](https://github.com/FunkinCrew/Funkin/blob/main/docs/style-guide.md).
Before submitting your PR, check that your code follows the [Style Guide](https://github.com/FunkinCrew/Funkin/blob/main/docs/style-guide.md). This keeps your code consistent with the rest of the codebase!
### Code comments
Code comments help others understand your changes, so the way you write them is important!
Here are some guidelines for writing comments in your code:
- Leave comments only when you believe a piece of code warrants explanation.
- Leave comments only when you believe a piece of code warrants explanation. If a piece of code is self-explanatory, it does not need a comment.
- Ensure that your comments provide meaningful insight into the function or purpose of the code.
- Write your comments in a clear and concise manner.
- Only sign your comments with your name when your changes are complex and may require further explanation.
### Example Comments
### Example code comments
#### DO NOT:
Below are examples of what you SHOULD NOT do when writing code comments.
```haxe
/**
* jumps around the song
* works with bpm changes but skipped notes still hurt
* @param sections how many sections to jump, negative = backwards
*/
function changeSection(sections:Int):Void
/**
* jumps around the song
* works with bpm changes but skipped notes still hurt
* @param sections how many sections to jump, negative = backwards
*/
function changeSection(sections:Int):Void
{
// Pause the music, as you probably guessed
// FlxG.sound.music.pause();
// Set the target time in steps, I don’t really get how this works though lol - [GitHub username]
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.stepsPerMeasure * sections);
var targetTimeMs:Float = Conductor.instance.getStepTimeInMs(targetTimeSteps);
// Don't go back in time to before the song started, that would probably break a lot of things and cause a bunch of problems!
targetTimeMs = Math.max(0, targetTimeMs);
if (FlxG.sound.music != null) // If the music is not null, set the time to the target time
{
// Pause the music, as you probably guessed
// FlxG.sound.music.pause();
// Set the target time in steps, I don’t really get how this works though lol - [GitHub username]
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.stepsPerMeasure * sections);
var targetTimeMs:Float = Conductor.instance.getStepTimeInMs(targetTimeSteps);
// Don't go back in time to before the song started, that would probably break a lot of things and cause a whole bunch of problems!
targetTimeMs = Math.max(0, targetTimeMs);
if (FlxG.sound.music != null) // If the music is not null, set the time to the target time
{
FlxG.sound.music.time = targetTimeMs;
}
// Handle skipped notes and events and all that jazz
handleSkippedNotes();
SongEventRegistry.handleSkippedEvents(songEvents, Conductor.instance.songPosition);
// regenNoteData(FlxG.sound.music.time);
Conductor.instance.update(FlxG.sound?.music?.time ?? 0.0);
// I hate this function - [GitHub username]
resyncVocals();
FlxG.sound.music.time = targetTimeMs;
}
// Handle skipped notes and events and all that jazz
handleSkippedNotes();
SongEventRegistry.handleSkippedEvents(songEvents, Conductor.instance.songPosition);
// regenNoteData(FlxG.sound.music.time);
Conductor.instance.update(FlxG.sound?.music?.time ?? 0.0);
// I hate this function - [GitHub username]
resyncVocals();
}
```
```haxe
// End the song when the music is complete.
FlxG.sound.music.onComplete = function() {
endSong(skipEndingTransition);
};
// A negative instrumental offset means the song skips the first few milliseconds of the track.
// This just gets added into the startTimestamp behavior so we don't need to do anything extra.
FlxG.sound.music.play(true, Math.max(0, startTimestamp - Conductor.instance.combinedOffset));
FlxG.sound.music.pitch = playbackRate;
// Prevent the volume from being wrong.
FlxG.sound.music.volume = 1.0;
// IF fadetween is not null we cancel it
if (FlxG.sound.music.fadeTween != null) FlxG.sound.music.fadeTween.cancel();
// Play the vocals
trace('Playing vocals...');
// Add the vocals
add(vocals);
// Play the vocals for real this time lol
vocals.play();
// Set the vocals volume
vocals.volume = 1.0;
// Set the vocals pitch
vocals.pitch = playbackRate;
// Set the vocals time to the music time
vocals.time = FlxG.sound.music.time;
// trace('${FlxG.sound.music.time}');
// trace('${vocals.time}');
// functionThatWasntHereBeforeThisPRorSomethingIdKLOL();
/* testsprite = new FlxSprite(0, 0);
testsprite.loadGraphic(Paths.image('test'));
testsprite.screenCenter();
add(testsprite); */
// Me too [GitHub username] I hate this function it gave me pain and suffering - Girlfriend
resyncVocals();
```
```haxe
#if FEATURE_DEBUG_FUNCTIONS
// PAGEUP: who knows what this does
// SHIFT+PAGEUP: There will be dire consequences.
if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 20 : 2);
// PAGEUP: who knows what this does
// SHIFT+PAGEUP: There will be dire consequences.
if (FlxG.keys.justPressed.PAGEDOWN) changeSection(FlxG.keys.pressed.SHIFT ? -20 : -2);
#end
```
#### DO:
Below are examples on what you SHOULD do when writing code comments.
```haxe
/**
* Jumps forward or backward a number of sections in the song.
* Accounts for BPM changes, does not prevent death from skipped notes.
* @param sections The number of sections to jump, negative to go backwards.
*/
function changeSection(sections:Int):Void
/**
* Jumps forward or backward a number of sections in the song.
* Accounts for BPM changes, does not prevent death from skipped notes.
* @param sections The number of sections to jump, negative to go backwards.
*/
function changeSection(sections:Int):Void
{
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.stepsPerMeasure * sections);
var targetTimeMs:Float = Conductor.instance.getStepTimeInMs(targetTimeSteps);
// Don't go back in time to before the song started.
targetTimeMs = Math.max(0, targetTimeMs);
if (FlxG.sound.music != null)
{
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.stepsPerMeasure * sections);
var targetTimeMs:Float = Conductor.instance.getStepTimeInMs(targetTimeSteps);
// Don't go back in time to before the song started.
targetTimeMs = Math.max(0, targetTimeMs);
if (FlxG.sound.music != null)
{
FlxG.sound.music.time = targetTimeMs;
}
handleSkippedNotes();
SongEventRegistry.handleSkippedEvents(songEvents, Conductor.instance.songPosition);
Conductor.instance.update(FlxG.sound?.music?.time ?? 0.0);
resyncVocals();
FlxG.sound.music.time = targetTimeMs;
}
handleSkippedNotes();
SongEventRegistry.handleSkippedEvents(songEvents, Conductor.instance.songPosition);
Conductor.instance.update(FlxG.sound?.music?.time ?? 0.0);
resyncVocals();
}
```
```haxe
FlxG.sound.music.onComplete = function() {
endSong(skipEndingTransition);
};
// A negative instrumental offset means the song skips the first few milliseconds of the track.
// This just gets added into the startTimestamp behavior so we don't need to do anything extra.
FlxG.sound.music.play(true, Math.max(0, startTimestamp - Conductor.instance.combinedOffset));
FlxG.sound.music.pitch = playbackRate;
// Prevent the volume from being wrong.
FlxG.sound.music.volume = 1.0;
if (FlxG.sound.music.fadeTween != null) FlxG.sound.music.fadeTween.cancel();
trace('Playing vocals...');
add(vocals);
vocals.play();
vocals.volume = 1.0;
vocals.pitch = playbackRate;
vocals.time = FlxG.sound.music.time;
resyncVocals();
```
```haxe
#if FEATURE_DEBUG_FUNCTIONS
// PAGEUP: Skip forward two sections.
// SHIFT+PAGEUP: Skip forward twenty sections.
if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 20 : 2);
// PAGEDOWN: Skip backward two section. Doesn't replace notes.
// SHIFT+PAGEDOWN: Skip backward twenty sections.
if (FlxG.keys.justPressed.PAGEDOWN) changeSection(FlxG.keys.pressed.SHIFT ? -20 : -2);
#end
```
## Documentation PRs
Documentation-based PRs make changes such as **fixing typos** or **adding new information** in documentation files.
Documentation-based PRs make changes such as **fixing typos** or **adding new information** in documentation files.
This involves modifying one or several of the repository’s `.md` files, found throughout the repository.
@@ -297,6 +404,17 @@ 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 **adjusting chart metadata**.
This involves modifying one or several of the `funkin.assets` repository's `*-metadata.json` files, found in the `preload/data/songs/` directory.
> [!CAUTION]
> 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.
These PRs should only be opened in the `funkin.assets` repository.
# Closing
Thank you for reading the Contributing Guide.
+13 -5
View File
@@ -3,6 +3,7 @@
Most of this functionality is only available on debug builds of the game!
## Any State
- `INSERT`: ***MEMORY GARBAGE COLLECTION***: Immediately perform memory garbage collection.
- `F2`: ***OVERLAY***: Enables the Flixel debug overlay, which has partial support for scripting.
- `F3`: ***SCREENSHOT***: Takes a screenshot of the game and saves it to the local `screenshots` directory. Works outside of debug builds too!
- `F4`: ***EJECT***: Forcibly switch state to the Main Menu (with no extra transition). Useful if you're stuck in a level and you need to get out!
@@ -19,20 +20,27 @@ Most of this functionality is only available on debug builds of the game!
- `9`: NEATO!
- `PAGEUP` (MacOS: `Fn-Up`): ***FORWARDS TIME TRAVEL***: Move forward by 2 sections. Hold SHIFT to move forward by 20 sections instead.
- `PAGEDOWN` (MacOS: `Fn-Down`): ***BACKWARDS TIME TRAVEL***: Move backward by 2 sections. Hold SHIFT to move backward by 20 sections instead.
- `CHART Hotkey`: ***OPEN CHART EDITOR***: Open the Chart Editor with the current song loaded.
- `STAGE Hotkey`: ***OPEN STAGE EDITOR***: Open the old Stage Editor overlay.
## **Freeplay State**
- `F` (Freeplay Menu) - Move to Favorites
- `Q` (Freeplay Menu) - Back one category
- `E` (Freeplay Menu) - Forward one category
- `P`: ***SWITCH TO PICO/BF***: Switch to Pico or BF (reopens Freeplay as the other character).
- `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.
## **Title State**
- `Y` - WOAH
- `ESCAPE`: ***QUIT***: Quit the game.
- `D`: COOL
- `UI LEFT`/`UI RIGHT Hotkey`: 2COOL
- `Y`: WOAH
## **Main Menu**
- `~`: ***DEBUG***: Opens a menu to access the Chart Editor and other work-in-progress editors. Rebindable in the options menu.
- `SHIFT-ACCEPT`: ***OPEN FREEPLAY AS PICO***: When selecting Freeplay, open the Freeplay menu with the playable character set to Pico.
- `CTRL-ALT-SHIFT-W`: ***ALL ACCESS***: Unlocks all songs in Freeplay. Only available on debug builds.
- `CTRL-ALT-SHIFT-M`: ***NO MORE ACCESS***: Re-locks all songs in Freeplay except those unlocked by default. Only available on debug builds.
- `CTRL-ALT-SHIFT-R`: ***GREAT SCORE?***: Give the user a hypothetical overridden score, and see if we can maintain that golden P rank. Only available on debug builds.
- `CTRL-ALT-SHIFT-P`: ***CHARACTER UNLOCK SCREEN***: Forces the Character Select screen to play Pico's unlocking animation. Only available on debug builds.
- `CTRL-ALT-SHIFT-N`: ***CHARACTER NOT SEEN***: Marks all characters as not seen and enables BF's new character unlocked animation in Freeplay. Only available on debug builds.
- `CTRL-ALT-SHIFT-E`: ***DUMP SAVE DATA***: Prompts the user to save their save data as a JSON file, so its contents can be viewed. Only available on debug builds.
- `CTRL-ALT-SHIFT-E`: ***DUMP SAVE DATA***: Prompts the user to save their save data as a JSON file, so its contents can be viewed. Only available on debug builds.
+5 -2
View File
@@ -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.
+1 -1
View File
@@ -4,7 +4,7 @@ This short document is designed to give a run-down on how code should be formatt
## Notes on IDEs and Extensions
The Visual Studio Code IDE is highly recommended, as this repo contains various configuration that works with VSCode extensions to automatically style certain things for you.
The Visual Studio Code IDE is highly recommended, as this repo contains various configurations that work with VSCode extensions to automatically style certain things for you.
VSCode is also the only IDE that has any good tools for Haxe so yeah.
+6 -6
View File
@@ -32,8 +32,8 @@
"name": "flxanimate",
"type": "git",
"dir": null,
"ref": "713d3de0e566d6cd54cde13711ab3e4f60f59f4b",
"url": "https://github.com/Dot-Stuff/flxanimate"
"ref": "f8842cea9883d6112a2c854cf93fa72856171428",
"url": "https://github.com/FunkinCrew/flxanimate"
},
{
"name": "format",
@@ -63,14 +63,14 @@
"name": "haxeui-core",
"type": "git",
"dir": null,
"ref": "74ba53387eab0c4c2d3825e103fe70df8e46d9b1",
"ref": "07fc7aa8098deaea633b0726d01f83eb4ef8a832",
"url": "https://github.com/haxeui/haxeui-core"
},
{
"name": "haxeui-flixel",
"type": "git",
"dir": null,
"ref": "34fe148e45292439a9c3caf00890c93bcc6f9453",
"ref": "b899a4c7d7318c5ff2b1bb645fbc73728fad1ac9",
"url": "https://github.com/haxeui/haxeui-flixel"
},
{
@@ -116,7 +116,7 @@
{
"name": "hxvlc",
"type": "haxelib",
"version": "2.0.1"
"version": "2.1.2"
},
{
"name": "json2object",
@@ -143,7 +143,7 @@
"name": "lime",
"type": "git",
"dir": null,
"ref": "d1322e60f97b5c6e977f9e3e8a04f22b5190e7d9",
"ref": "be81ad7e4e1a92c3482bcc009648a4ac892cfa35",
"url": "https://github.com/FunkinCrew/lime"
},
{
+2 -3
View File
@@ -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.2";
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);
}
/**
+12 -6
View File
@@ -13,6 +13,7 @@ import openfl.events.Event;
import openfl.Lib;
import openfl.media.Video;
import openfl.net.NetStream;
import funkin.util.WindowUtil;
/**
* The main class which initializes HaxeFlixel and starts the game in its initial state.
@@ -61,12 +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
if (hasEventListener(Event.ADDED_TO_STAGE))
{
removeEventListener(Event.ADDED_TO_STAGE, init);
@@ -104,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
+1
View File
@@ -6,6 +6,7 @@ import openfl.utils.Future;
* A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions.
* Later we'll add Funkin-specific caching to this.
*/
@:nullSafety
class Assets
{
/**
+12 -1
View File
@@ -18,6 +18,7 @@ import funkin.data.freeplay.player.PlayerRegistry;
import funkin.data.freeplay.style.FreeplayStyleRegistry;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.data.song.SongRegistry;
import funkin.data.stickers.StickerRegistry;
import funkin.data.event.SongEventRegistry;
import funkin.data.stage.StageRegistry;
import funkin.data.story.level.LevelRegistry;
@@ -97,9 +98,18 @@ class InitState extends FlxState
FlxG.sound.volumeDownKeys = [];
FlxG.sound.muteKeys = [];
// A small jumpstart to the soundtray, it usually sets itself to inactive (somewhere...)
// but that makes our soundtray not show up on init if we have the game muted.
// We set it to active so it at least calls it's update function once (see FlxGame.onEnterFrame(), it's called there)
// and also see FunkinSoundTray.update() to see what we do and how we check if we are muted or not
FlxG.game.soundTray.active = true;
// Set the game to a lower frame rate while it is in the background.
FlxG.game.focusLostFramerate = 30;
// Makes Flixel use frame times instead of locked movements per frame for things like tweens
FlxG.fixedTimestep = false;
setupFlixelDebug();
//
@@ -184,6 +194,7 @@ class InitState extends FlxState
FreeplayStyleRegistry.instance.loadEntries();
AlbumRegistry.instance.loadEntries();
StageRegistry.instance.loadEntries();
StickerRegistry.instance.loadEntries();
// TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers and more prone to syntax errors.
// Move it to use a BaseRegistry.
@@ -241,7 +252,7 @@ class InitState extends FlxState
title: "Cum Song Erect by Kawai Sprite",
songId: "cum",
characterId: "pico",
difficultyId: "nightmare",
difficultyId: "hard",
isNewHighscore: true,
scoreData:
{
+49
View File
@@ -1,6 +1,7 @@
package funkin;
import funkin.save.Save;
import funkin.util.WindowUtil;
/**
* A core class which provides a store of user-configurable, globally relevant values.
@@ -176,6 +177,53 @@ class Preferences
return value;
}
/**
* If enabled, the game will utilize VSync (or adaptive VSync) on startup.
* @default `OFF`
*/
public static var vsyncMode(get, set):lime.ui.WindowVSyncMode;
static function get_vsyncMode():lime.ui.WindowVSyncMode
{
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 = string;
save.flush();
return value;
}
public static var unlockedFramerate(get, set):Bool;
static function get_unlockedFramerate():Bool
@@ -340,6 +388,7 @@ class Preferences
{
// Apply the autoPause setting (enables automatic pausing on focus lost).
FlxG.autoPause = Preferences.autoPause;
// WindowUtil.setVSyncMode(Preferences.vsyncMode);
// Apply the debugDisplay setting (enables the FPS and RAM display).
toggleDebugDisplay(Preferences.debugDisplay);
#if web
+1
View File
@@ -8,6 +8,7 @@ import io.newgrounds.objects.events.Result;
/**
* Use Newgrounds to perform basic telemetry. Ignore if not logged in to Newgrounds.
*/
@:nullSafety
class Events
{
// Only allow letters, numbers, spaces, dashes, and underscores.
+19 -12
View File
@@ -4,29 +4,33 @@ package funkin.api.newgrounds;
import io.newgrounds.Call.CallError;
import io.newgrounds.objects.ScoreBoard as LeaderboardData;
import io.newgrounds.objects.events.Outcome;
import io.newgrounds.utils.ScoreBoardList;
class Leaderboards
{
public static function listLeaderboardData():Map<Leaderboard, LeaderboardData>
{
if (NewgroundsClient.instance.leaderboards == null)
var leaderboardList:Null<ScoreBoardList> = NewgroundsClient.instance.leaderboards;
if (leaderboardList == null)
{
trace('[NEWGROUNDS] Not logged in, cannot fetch medal data!');
return [];
}
var result:Map<Leaderboard, LeaderboardData> = [];
for (leaderboardId in NewgroundsClient.instance.leaderboards.keys())
else
{
var leaderboardData = NewgroundsClient.instance.leaderboards.get(leaderboardId);
if (leaderboardData == null) continue;
var result:Map<Leaderboard, LeaderboardData> = [];
// A little hacky, but it works.
result.set(cast leaderboardId, leaderboardData);
for (leaderboardId in leaderboardList.keys())
{
var leaderboardData = leaderboardList.get(leaderboardId);
if (leaderboardData == null) continue;
// A little hacky, but it works.
result.set(cast leaderboardId, leaderboardData);
}
return result;
}
return result;
}
/**
@@ -42,7 +46,10 @@ class Leaderboards
if (NewgroundsClient.instance.isLoggedIn())
{
var leaderboardData = NewgroundsClient.instance.leaderboards.get(leaderboard.getId());
var leaderboardList = NewgroundsClient.instance.leaderboards;
if (leaderboardList == null) return;
var leaderboardData:Null<LeaderboardData> = leaderboardList.get(leaderboard.getId());
if (leaderboardData != null)
{
leaderboardData.postScore(score, function(outcome:Outcome<CallError>):Void {
+35 -31
View File
@@ -5,54 +5,56 @@ import io.newgrounds.objects.Medal as MedalData;
import funkin.util.plugins.NewgroundsMedalPlugin;
import flixel.graphics.FlxGraphic;
import openfl.display.BitmapData;
import io.newgrounds.utils.MedalList;
import haxe.Json;
class Medals
{
public static var medalJSON:Array<MedalJSON>;
public static var medalJSON:Array<MedalJSON> = [];
public static function listMedalData():Map<Medal, MedalData>
{
if (NewgroundsClient.instance.medals == null)
var medalList = NewgroundsClient.instance.medals;
if (medalList == null)
{
trace('[NEWGROUNDS] Not logged in, cannot fetch medal data!');
return [];
}
var result:Map<Medal, MedalData> = [];
for (medalId in NewgroundsClient.instance.medals.keys())
else
{
var medalData = NewgroundsClient.instance.medals.get(medalId);
if (medalData == null) continue;
// TODO: Why do I have to do this, @:nullSafety is fucked up
var result:Map<Medal, MedalData> = [];
// A little hacky, but it works.
result.set(cast medalId, medalData);
for (medalId in medalList.keys())
{
var medalData = medalList.get(medalId);
if (medalData == null) continue;
// A little hacky, but it works.
result.set(cast medalId, medalData);
}
return result;
}
return result;
}
static function isValid(medalData:MedalData):Bool
{
// IDK why medalData can exist but _data can be null...
// TODO: Move checks to the NG library.
@:privateAccess
return (medalData != null && medalData._data != null);
}
public static function award(medal:Medal):Void
{
if (NewgroundsClient.instance.isLoggedIn())
{
var medalData = NewgroundsClient.instance.medals.get(medal.getId());
if (!isValid(medalData))
var medalList = NewgroundsClient.instance.medals;
@:privateAccess
if (medalList == null || medalList._map == null) return;
var medalData:Null<MedalData> = medalList.get(medal.getId());
@:privateAccess
if (medalData == null || medalData._data == null)
{
trace('[NEWGROUNDS] Could not retrieve data for medal: ${medal}');
return;
}
if (!medalData.unlocked)
else if (!medalData.unlocked)
{
trace('[NEWGROUNDS] Awarding medal (${medal}).');
medalData.sendUnlock();
@@ -66,12 +68,10 @@ 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?
var g:FlxGraphic = null;
var localMedalData:Null<MedalJSON> = medalJSON.filter(function(jsonMedal) {
#if FEATURE_NEWGROUNDS_TESTING_MEDALS
return medal == jsonMedal.idTest;
@@ -86,10 +86,14 @@ class Medals
// Lime/OpenFL parses it without the included prefix stuff, so we remove it.
str = str.replace("data:image/png;base64,", "").trim();
var bitmapData = BitmapData.fromBase64(str, "image/png");
if (str != null) g = FlxGraphic.fromBitmapData(bitmapData);
g.persist = true;
var medalGraphic:Null<FlxGraphic> = null;
if (str != null)
{
medalGraphic = FlxGraphic.fromBitmapData(bitmapData);
medalGraphic.persist = true;
}
NewgroundsMedalPlugin.play(medalData.value, medalData.name, g);
NewgroundsMedalPlugin.play(medalData.value, medalData.name, medalGraphic);
#end
}
else
@@ -119,7 +123,7 @@ class Medals
trace('[NEWGROUNDS] Failed to parse local medal data!');
for (error in parser.errors)
funkin.data.DataError.printError(error);
medalJSON = null;
medalJSON = [];
}
else
{
@@ -149,6 +149,9 @@ class NewgroundsClient
Save.instance.ngSessionId = null;
}
/**
* @return `true` if the user is logged in to Newgrounds.
*/
public function isLoggedIn():Bool
{
#if FEATURE_NEWGROUNDS
+55
View File
@@ -0,0 +1,55 @@
package funkin.api.newgrounds;
#if FEATURE_NEWGROUNDS
import io.newgrounds.NG;
#end
import funkin.api.newgrounds.NewgroundsClient;
import funkin.util.WindowUtil;
/**
* Handles referrals to links retrieved from the Newgrounds API.
*/
class Referral
{
/**
* Opens the referral page with the given name.
* @param referralName The name of the referral.
* @param fallbackUrl The URL to open if the referral fails or is not available.
*/
static function doReferral(referralName:String, fallbackUrl:String):Void
{
#if FEATURE_NEWGROUNDS
if (NewgroundsClient.instance.isLoggedIn())
{
NG.core?.calls.loader.loadReferral(false)
.addComponentParameter("referral_name", referralName)
.addResponseHandler(response -> {
trace(response);
if (response.success)
{
WindowUtil.openURL(response.result.data.url);
}
else
{
WindowUtil.openURL(fallbackUrl);
}
})
.send();
}
else
{
WindowUtil.openURL(fallbackUrl);
}
#else
WindowUtil.openURL(fallbackUrl);
#end
}
/**
* Opens the merch referral page.
*/
public static function doMerchReferral():Void
{
doReferral("merch_link", Constants.URL_MERCH_FALLBACK);
}
}
+58 -4
View File
@@ -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;
}
+32 -92
View File
@@ -9,14 +9,17 @@ import funkin.vis.dsp.SpectralAnalyzer;
using Lambda;
@:nullSafety
class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
{
// public var vis:VisShit;
var analyzer:SpectralAnalyzer;
var analyzer:Null<SpectralAnalyzer> = null;
var volumes:Array<Float> = [];
public var snd:FlxSound;
public var snd:Null<FlxSound> = null;
static final BAR_COUNT:Int = 7;
// TODO: Make the sprites easier to soft code.
public function new(snd:FlxSound, pixel:Bool)
@@ -25,7 +28,7 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
this.snd = snd;
var visCount = pixel ? (7 + 1) : (7 + 1);
var visCount = pixel ? (BAR_COUNT + 1) : (BAR_COUNT + 1);
var visScale = pixel ? 6 : 1;
var visFrms:FlxAtlasFrames = Paths.getSparrowAtlas(pixel ? 'characters/abotPixel/aBotVizPixel' : 'characters/abot/aBotViz');
@@ -58,8 +61,10 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
public function initAnalyzer():Void
{
if (snd == null) return;
@:privateAccess
analyzer = new SpectralAnalyzer(snd._channel.__audioSource, 7, 0.1, 40);
analyzer = new SpectralAnalyzer(snd._channel.__audioSource, BAR_COUNT, 0.1, 40);
// A-Bot tuning...
analyzer.minDb = -65;
analyzer.maxDb = -25;
@@ -77,16 +82,17 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
// analyzer.fftN = 2048;
}
public function dumpSound():Void
{
snd = null;
analyzer = null;
}
var visTimer:Float = -1;
var visTimeMax:Float = 1 / 30;
override function update(elapsed:Float)
{
// updateViz();
// updateFFT(elapsed);
//
super.update(elapsed);
}
@@ -97,8 +103,8 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
override function draw()
{
if (analyzer != null) drawFFT();
super.draw();
drawFFT();
}
/**
@@ -106,7 +112,7 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
*/
function drawFFT():Void
{
var levels = analyzer.getLevels();
var levels = (analyzer != null) ? analyzer.getLevels() : getDefaultLevels();
for (i in 0...min(group.members.length, levels.length))
{
@@ -118,12 +124,6 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
// decrement our animFrame, so we can get a value from 0-5 for animation frames
animFrame -= 1;
#if desktop
// Web version scales with the Flixel volume level.
// This line brings platform parity but looks worse.
// animFrame = Math.round(animFrame * FlxG.sound.volume);
#end
animFrame = Math.floor(Math.min(5, animFrame));
animFrame = Math.floor(Math.max(0, animFrame));
@@ -133,79 +133,19 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
}
}
// function updateFFT(elapsed:Float)
// {
// if (vis.snd != null)
// {
// vis.checkAndSetBuffer();
// if (vis.setBuffer)
// {
// var remappedShit:Int = 0;
// if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, vis.numSamples));
// else
// remappedShit = Std.int(FlxMath.remapToRange(Conductor.instance.songPosition, 0, vis.snd.length, 0, vis.numSamples));
// var fftSamples:Array<Float> = [];
// var swagBucks = remappedShit;
// for (i in remappedShit...remappedShit + (Std.int((44100 * (1 / 144)))))
// {
// var left = vis.audioData[swagBucks] / 32767;
// var right = vis.audioData[swagBucks + 1] / 32767;
// var balanced = (left + right) / 2;
// swagBucks += 2;
// fftSamples.push(balanced);
// }
// var freqShit = vis.funnyFFT(fftSamples);
// for (i in 0...group.members.length)
// {
// var getSliceShit = function(s:Int) {
// var powShit = FlxMath.remapToRange(s, 0, group.members.length, 0, MathUtil.logBase(10, freqShit[0].length));
// return Math.round(Math.pow(10, powShit));
// };
// // var powShit:Float = getSliceShit(i);
// var hzSliced:Int = getSliceShit(i);
// var sliceLength:Int = Std.int(freqShit[0].length / group.members.length);
// var volSlice = freqShit[0].slice(hzSliced, getSliceShit(i + 1));
// var avgVel:Float = 0;
// for (slice in volSlice)
// {
// avgVel += slice;
// }
// avgVel /= volSlice.length;
// avgVel *= 10000000;
// volumes[i] += avgVel - (elapsed * (volumes[i] * 50));
// var animFrame:Int = Std.int(volumes[i]);
// animFrame = Math.floor(Math.min(5, animFrame));
// animFrame = Math.floor(Math.max(0, animFrame));
// animFrame = Std.int(Math.abs(animFrame - 5)); // shitty dumbass flip, cuz dave got da shit backwards lol!
// group.members[i].animation.curAnim.curFrame = animFrame;
// if (FlxG.keys.justPressed.U)
// {
// trace(avgVel);
// trace(group.members[i].animation.curAnim.curFrame);
// }
// }
// // group.members[0].animation.curAnim.curFrame =
// }
// }
// }
// public function updateViz()
// {
// if (vis.snd != null)
// {
// var remappedShit:Int = 0;
// vis.checkAndSetBuffer();
// if (vis.setBuffer)
// {
// // var startingSample:Int = Std.int(FlxMath.remapToRange)
// if (vis.snd.playing) remappedShit = Std.int(FlxMath.remapToRange(vis.snd.time, 0, vis.snd.length, 0, vis.numSamples));
// for (i in 0...group.members.length)
// {
// var sampleApprox:Int = Std.int(FlxMath.remapToRange(i, 0, group.members.length, remappedShit, remappedShit + 500));
// var left = vis.audioData[sampleApprox] / 32767;
// var animFrame:Int = Std.int(FlxMath.remapToRange(left, -1, 1, 0, 6));
// group.members[i].animation.curAnim.curFrame = animFrame;
// }
// }
// }
// }
/**
* Explicitly define the default levels to draw when the analyzer is not available.
* @return Array<Bar>
*/
static function getDefaultLevels():Array<Bar>
{
var result:Array<Bar> = [];
for (i in 0...BAR_COUNT)
{
result.push({value: 0, peak: 0.0});
}
return result;
}
}
+14
View File
@@ -16,6 +16,7 @@ typedef EntryConstructorFunction = String->Void;
* @param J The type of the JSON data used when constructing.
*/
@:generic
@:autoBuild(funkin.util.macro.DataRegistryMacro.buildRegistry())
abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructorFunction>), J>
{
/**
@@ -189,6 +190,19 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
return entries.get(id);
}
/**
* A list of all entries included in the base game.
* The actual function exists and is auto-generated on each registry at build time.
* @return Array<String>
*/
// public function listBaseGameEntryIds():Array<String> {}
/**
* A list of all entries that are not included in the base game.
* @return Array<String>
*/
// public function listModdedEntryIds():Array<String> {}
public function toString():String
{
return 'Registry(' + registryId + ', ${countEntries()} entries)';
@@ -0,0 +1,9 @@
package funkin.data;
/**
* An interface which automatically implements essential fields for a class extending `BaseRegistry`.
*
* @see `funkin.data.BaseRegistry`
*/
@:autoBuild(funkin.util.macro.RegistryMacro.buildRegistry())
interface DefaultRegistryImpl {}
+1
View File
@@ -5,6 +5,7 @@ package funkin.data;
* A `String->Void` constructor is also mandatory, but enforced elsewhere.
* @param T The JSON data type of the registry entry.
*/
@:autoBuild(funkin.util.macro.RegistryMacro.buildEntry())
interface IRegistryEntry<T>
{
public final id:String;
@@ -2,8 +2,10 @@ package funkin.data.dialogue.conversation;
import funkin.play.cutscene.dialogue.Conversation;
import funkin.play.cutscene.dialogue.ScriptedConversation;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class ConversationRegistry extends BaseRegistry<Conversation, ConversationData>
class ConversationRegistry extends BaseRegistry<Conversation, ConversationData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the dialogue box data format.
@@ -14,74 +16,8 @@ class ConversationRegistry extends BaseRegistry<Conversation, ConversationData>
public static final CONVERSATION_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
public static var instance(get, never):ConversationRegistry;
static var _instance:Null<ConversationRegistry> = null;
static function get_instance():ConversationRegistry
{
if (_instance == null) _instance = new ConversationRegistry();
return _instance;
}
public function new()
{
super('CONVERSATION', 'dialogue/conversations', CONVERSATION_DATA_VERSION_RULE);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<ConversationData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<ConversationData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<ConversationData>
{
var parser = new json2object.JsonParser<ConversationData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):Conversation
{
return ScriptedConversation.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedConversation.listScriptClasses();
}
}
@@ -3,8 +3,10 @@ package funkin.data.dialogue.dialoguebox;
import funkin.play.cutscene.dialogue.DialogueBox;
import funkin.data.dialogue.dialoguebox.DialogueBoxData;
import funkin.play.cutscene.dialogue.ScriptedDialogueBox;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData>
class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the dialogue box data format.
@@ -15,74 +17,8 @@ class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData>
public static final DIALOGUEBOX_DATA_VERSION_RULE:thx.semver.VersionRule = ">=1.0.0 <1.2.0";
public static var instance(get, never):DialogueBoxRegistry;
static var _instance:Null<DialogueBoxRegistry> = null;
static function get_instance():DialogueBoxRegistry
{
if (_instance == null) _instance = new DialogueBoxRegistry();
return _instance;
}
public function new()
{
super('DIALOGUEBOX', 'dialogue/boxes', DIALOGUEBOX_DATA_VERSION_RULE);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<DialogueBoxData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<DialogueBoxData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<DialogueBoxData>
{
var parser = new json2object.JsonParser<DialogueBoxData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):DialogueBox
{
return ScriptedDialogueBox.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedDialogueBox.listScriptClasses();
}
}
@@ -2,8 +2,10 @@ package funkin.data.dialogue.speaker;
import funkin.play.cutscene.dialogue.Speaker;
import funkin.play.cutscene.dialogue.ScriptedSpeaker;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData>
class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the speaker data format.
@@ -14,74 +16,8 @@ class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData>
public static final SPEAKER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
public static var instance(get, never):SpeakerRegistry;
static var _instance:Null<SpeakerRegistry> = null;
static function get_instance():SpeakerRegistry
{
if (_instance == null) _instance = new SpeakerRegistry();
return _instance;
}
public function new()
{
super('SPEAKER', 'dialogue/speakers', SPEAKER_DATA_VERSION_RULE);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<SpeakerData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<SpeakerData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<SpeakerData>
{
var parser = new json2object.JsonParser<SpeakerData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):Speaker
{
return ScriptedSpeaker.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedSpeaker.listScriptClasses();
}
}
@@ -3,8 +3,10 @@ package funkin.data.freeplay.album;
import funkin.ui.freeplay.Album;
import funkin.data.freeplay.album.AlbumData;
import funkin.ui.freeplay.ScriptedAlbum;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class AlbumRegistry extends BaseRegistry<Album, AlbumData>
class AlbumRegistry extends BaseRegistry<Album, AlbumData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the album data format.
@@ -15,70 +17,8 @@ class AlbumRegistry extends BaseRegistry<Album, AlbumData>
public static final ALBUM_DATA_VERSION_RULE:thx.semver.VersionRule = '1.0.x';
public static final instance:AlbumRegistry = new AlbumRegistry();
public function new()
{
super('ALBUM', 'ui/freeplay/albums', ALBUM_DATA_VERSION_RULE);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
* @param id The ID of the entry to load.
* @return The parsed data object.
*/
public function parseEntryData(id:String):Null<AlbumData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser:json2object.JsonParser<AlbumData> = new json2object.JsonParser<AlbumData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
* @return The parsed data object.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<AlbumData>
{
var parser:json2object.JsonParser<AlbumData> = new json2object.JsonParser<AlbumData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):Album
{
return ScriptedAlbum.init(clsName, 'unknown');
}
function getScriptedClassNames():Array<String>
{
return ScriptedAlbum.listScriptClasses();
}
}
@@ -5,5 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.1]
### Added
- Added `stickerPack` to specify which sticker pack to use when transitioning into and out of song featuring this character.
- If the value isn't present, it will use a default sticker pack instead.
## [1.0.0]
Initial release.
@@ -31,6 +31,15 @@ class PlayerData
@:default(false)
public var showUnownedChars:Bool = false;
/**
* The default sticker pack to use for songs featuring this playable character.
* Can be overridden by specific songs.
* @default `default`
*/
@:optional
@:default(funkin.util.Constants.DEFAULT_STICKER_PACK)
public var stickerPack:String = Constants.DEFAULT_STICKER_PACK;
/**
* Which freeplay style to use for this character.
*/
@@ -325,7 +334,11 @@ typedef PlayerResultsAnimationData =
*/
var renderType:String;
var assetPath:String;
@:optional
var assetPath:Null<String>;
@:optional
var scriptClass:Null<String>;
@:optional
@:default([0, 0])
@@ -4,8 +4,10 @@ import funkin.data.freeplay.player.PlayerData;
import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.freeplay.charselect.ScriptedPlayableCharacter;
import funkin.save.Save;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the stage data format.
@@ -16,15 +18,6 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
public static final PLAYER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
public static var instance(get, never):PlayerRegistry;
static var _instance:Null<PlayerRegistry> = null;
static function get_instance():PlayerRegistry
{
if (_instance == null) _instance = new PlayerRegistry();
return _instance;
}
/**
* A mapping between stage character IDs and Freeplay playable character IDs.
*/
@@ -130,79 +123,4 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
{
return ownedCharacterIds.exists(characterId);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<PlayerData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<PlayerData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<PlayerData>
{
var parser = new json2object.JsonParser<PlayerData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):PlayableCharacter
{
return ScriptedPlayableCharacter.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedPlayableCharacter.listScriptClasses();
}
/**
* A list of all the playable characters from the base game, in order.
*/
public function listBaseGamePlayerIds():Array<String>
{
return ["bf", "pico"];
}
/**
* A list of all installed playable characters that are not from the base game.
*/
public function listModdedPlayerIds():Array<String>
{
return listEntryIds().filter(function(id:String):Bool {
return listBaseGamePlayerIds().indexOf(id) == -1;
});
}
}
@@ -3,8 +3,10 @@ package funkin.data.freeplay.style;
import funkin.ui.freeplay.FreeplayStyle;
import funkin.data.freeplay.style.FreeplayStyleData;
import funkin.ui.freeplay.ScriptedFreeplayStyle;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleData>
class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the style data format.
@@ -15,70 +17,8 @@ class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleDat
public static final FREEPLAYSTYLE_DATA_VERSION_RULE:thx.semver.VersionRule = '1.0.x';
public static final instance:FreeplayStyleRegistry = new FreeplayStyleRegistry();
public function new()
{
super('FREEPLAYSTYLE', 'ui/freeplay/styles', FREEPLAYSTYLE_DATA_VERSION_RULE);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
* @param id The ID of the entry to load.
* @return The parsed data object.
*/
public function parseEntryData(id:String):Null<FreeplayStyleData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser:json2object.JsonParser<FreeplayStyleData> = new json2object.JsonParser<FreeplayStyleData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
* @return The parsed data object.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<FreeplayStyleData>
{
var parser:json2object.JsonParser<FreeplayStyleData> = new json2object.JsonParser<FreeplayStyleData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):FreeplayStyle
{
return ScriptedFreeplayStyle.init(clsName, 'unknown');
}
function getScriptedClassNames():Array<String>
{
return ScriptedFreeplayStyle.listScriptClasses();
}
}
@@ -3,8 +3,10 @@ package funkin.data.notestyle;
import funkin.play.notes.notestyle.NoteStyle;
import funkin.play.notes.notestyle.ScriptedNoteStyle;
import funkin.data.notestyle.NoteStyleData;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData>
class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the note style data format.
@@ -15,15 +17,6 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData>
public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = ">=1.0.0 <1.2.0";
public static var instance(get, never):NoteStyleRegistry;
static var _instance:Null<NoteStyleRegistry> = null;
static function get_instance():NoteStyleRegistry
{
if (_instance == null) _instance = new NoteStyleRegistry();
return _instance;
}
public function new()
{
super('NOTESTYLE', 'notestyles', NOTE_STYLE_DATA_VERSION_RULE);
@@ -33,61 +26,4 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData>
{
return fetchEntry(Constants.DEFAULT_NOTE_STYLE);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<NoteStyleData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<NoteStyleData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<NoteStyleData>
{
var parser = new json2object.JsonParser<NoteStyleData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):NoteStyle
{
return ScriptedNoteStyle.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedNoteStyle.listScriptClasses();
}
}
+5
View File
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.2.5]
### Added
- Added `playData.stickerPack` to specify which sticker pack to use when transitioning into and out of a song.
- If the value isn't present, it will use the character's sticker pack instead.
## [2.2.4]
### Added
- Added `playData.characters.opponentVocals` to specify which vocal track(s) to play for the opponent.
+7
View File
@@ -471,6 +471,13 @@ class SongPlayData implements ICloneable<SongPlayData>
@:optional
public var album:Null<String>;
/**
* The sticker pack for the song to use during transitions.
* If `null`, display the character's sticker pack.
*/
@:optional
public var stickerPack:Null<String>;
/**
* The start time for the audio preview in Freeplay.
* Defaults to 0 seconds in.
+3 -70
View File
@@ -10,11 +10,13 @@ import funkin.play.song.ScriptedSong;
import funkin.play.song.Song;
import funkin.util.assets.DataAssets;
import funkin.util.VersionUtil;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
using funkin.data.song.migrator.SongDataMigrator;
@:nullSafety
class SongRegistry extends BaseRegistry<Song, SongMetadata>
class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the stage data format.
@@ -40,19 +42,6 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return '${Constants.TITLE} - ${Constants.VERSION}';
}
/**
* TODO: What if there was a Singleton macro which automatically created the property for us?
*/
public static var instance(get, never):SongRegistry;
static var _instance:Null<SongRegistry> = null;
static function get_instance():SongRegistry
{
if (_instance == null) _instance = new SongRegistry();
return _instance;
}
public function new()
{
super('SONG', 'songs', SONG_METADATA_VERSION_RULE);
@@ -419,16 +408,6 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
}
}
function createScriptedEntry(clsName:String):Song
{
return ScriptedSong.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedSong.listScriptClasses();
}
function loadEntryMetadataFile(id:String, ?variation:String):Null<JsonFile>
{
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
@@ -503,52 +482,6 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
return chartData;
}
/**
* A list of all the story weeks from the base game, in order.
* TODO: Should this be hardcoded?
*/
public function listBaseGameSongIds():Array<String>
{
return [
"tutorial",
"bopeebo",
"fresh",
"dadbattle",
"spookeez",
"south",
"monster",
"pico",
"philly-nice",
"blammed",
"satin-panties",
"high",
"milf",
"cocoa",
"eggnog",
"winter-horrorland",
"senpai",
"roses",
"thorns",
"ugh",
"guns",
"stress",
"darnell",
"lit-up",
"2hot",
"blazin"
];
}
/**
* A list of all installed story weeks that are not from the base game.
*/
public function listModdedSongIds():Array<String>
{
return listEntryIds().filter(function(id:String):Bool {
return listBaseGameSongIds().indexOf(id) == -1;
});
}
/**
* A list of all difficulties for a specific character.
*/
+3 -103
View File
@@ -2,8 +2,10 @@ package funkin.data.stage;
import funkin.play.stage.Stage;
import funkin.play.stage.ScriptedStage;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class StageRegistry extends BaseRegistry<Stage, StageData>
class StageRegistry extends BaseRegistry<Stage, StageData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the stage data format.
@@ -14,110 +16,8 @@ class StageRegistry extends BaseRegistry<Stage, StageData>
public static final STAGE_DATA_VERSION_RULE:thx.semver.VersionRule = ">=1.0.0 <1.1.0";
public static var instance(get, never):StageRegistry;
static var _instance:Null<StageRegistry> = null;
static function get_instance():StageRegistry
{
if (_instance == null) _instance = new StageRegistry();
return _instance;
}
public function new()
{
super('STAGE', 'stages', STAGE_DATA_VERSION_RULE);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<StageData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<StageData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<StageData>
{
var parser = new json2object.JsonParser<StageData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):Stage
{
return ScriptedStage.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedStage.listScriptClasses();
}
/**
* A list of all the stages from the base game, in order.
* TODO: Should this be hardcoded?
*/
public function listBaseGameStageIds():Array<String>
{
return [
"mainStage",
"mainStageErect",
"spookyMansion",
"phillyTrain",
"phillyTrainErect",
"limoRide",
"limoRideErect",
"mallXmas",
"mallXmasErect",
"mallEvil",
"school",
"schoolEvil",
"tankmanBattlefield",
"phillyStreets",
"phillyStreetsErect",
"phillyBlazin",
];
}
/**
* A list of all installed story weeks that are not from the base game.
*/
public function listModdedStageIds():Array<String>
{
return listEntryIds().filter(function(id:String):Bool {
return listBaseGameStageIds().indexOf(id) == -1;
});
}
}
+9
View File
@@ -0,0 +1,9 @@
# Sticker Pack Data Schema Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0]
Initial release. Sticker packs have been restructured compared to previous versions to be less complicated.
@@ -0,0 +1,30 @@
package funkin.data.stickers;
/**
* A type definition for a sticker pack.
* It includes things like its name, the artist, and what sticker graphics to use.
* @see https://lib.haxe.org/p/json2object/
*/
typedef StickerData =
{
/**
* Semantic version of the sticker set data.
*/
public var version:String;
/**
* Readable name of the sticker set.
*/
public var name:String;
/**
* The artist of the sticker set.
*/
public var artist:String;
/**
* The stickers in the set.
* This is simply a list of asset files to use.
*/
public var stickers:Array<String>;
}
@@ -0,0 +1,89 @@
package funkin.data.stickers;
import funkin.data.stickers.StickerData;
import funkin.ui.transition.stickers.StickerPack;
import funkin.ui.transition.stickers.ScriptedStickerPack;
class StickerRegistry extends BaseRegistry<StickerPack, StickerData>
{
/**
* The current version string for the sticker pack data format.
* Handle breaking changes by incrementing this value
* and adding migration to the `migrateStickerData()` function.
*/
public static final STICKER_DATA_VERSION:thx.semver.Version = '1.0.0';
public static final STICKER_DATA_VERSION_RULE:thx.semver.VersionRule = '1.0.x';
public static final instance:StickerRegistry = new StickerRegistry();
public function new()
{
super('STICKER', 'stickerpacks', STICKER_DATA_VERSION_RULE);
}
public function fetchDefault():StickerPack
{
return fetchEntry(Constants.DEFAULT_STICKER_PACK);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
* @param id The ID of the entry to load.
* @return The parsed data object.
*/
public function parseEntryData(id:String):Null<StickerData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser:json2object.JsonParser<StickerData> = new json2object.JsonParser<StickerData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
* @return The parsed data object.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<StickerData>
{
var parser:json2object.JsonParser<StickerData> = new json2object.JsonParser<StickerData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):StickerPack
{
return ScriptedStickerPack.init(clsName, 'unknown');
}
function getScriptedClassNames():Array<String>
{
return ScriptedStickerPack.listScriptClasses();
}
}
+23 -90
View File
@@ -3,8 +3,10 @@ package funkin.data.story.level;
import funkin.util.SortUtil;
import funkin.ui.story.Level;
import funkin.ui.story.ScriptedLevel;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
class LevelRegistry extends BaseRegistry<Level, LevelData>
class LevelRegistry extends BaseRegistry<Level, LevelData> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the level data format.
@@ -15,110 +17,41 @@ class LevelRegistry extends BaseRegistry<Level, LevelData>
public static final LEVEL_DATA_VERSION_RULE:thx.semver.VersionRule = ">=1.0.0 <1.1.0";
public static var instance(get, never):LevelRegistry;
static var _instance:Null<LevelRegistry> = null;
static function get_instance():LevelRegistry
{
if (_instance == null) _instance = new LevelRegistry();
return _instance;
}
public function new()
{
super('LEVEL', 'levels', LEVEL_DATA_VERSION_RULE);
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<LevelData>
{
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<LevelData>();
parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
default:
return null;
}
if (parser.errors.length > 0)
{
printErrors(parser.errors, id);
return null;
}
return parser.value;
}
/**
* Parse and validate the JSON data and produce the corresponding data object.
*
* NOTE: Must be implemented on the implementation class.
* @param contents The JSON as a string.
* @param fileName An optional file name for error reporting.
*/
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<LevelData>
{
var parser = new json2object.JsonParser<LevelData>();
parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
{
printErrors(parser.errors, fileName);
return null;
}
return parser.value;
}
function createScriptedEntry(clsName:String):Level
{
return ScriptedLevel.init(clsName, "unknown");
}
function getScriptedClassNames():Array<String>
{
return ScriptedLevel.listScriptClasses();
}
/**
* A list of all the story weeks from the base game, in order.
* TODO: Should this be hardcoded?
* @return Array<String>
*/
public function listBaseGameLevelIds():Array<String>
public function listBaseGameEntryIds():Array<String>
{
// This MUST be hard-coded (overriding the auto-generated method)
// because the auto-generated method spits out values in alphabetical order.
return [
"tutorial",
"week1",
"week2",
"week3",
"week4",
"week5",
"week6",
"week7",
"weekend1"
'tutorial',
'week1',
'week2',
'week3',
'week4',
'week5',
'week6',
'week7',
'weekend1'
];
}
/**
* A list of all the story weeks in the game, in order.
* Modded levels are in alphabetical order at the end of the list.
* @return Array<String>
*/
public function listSortedLevelIds():Array<String>
{
var result = listEntryIds();
result.sort(SortUtil.defaultsThenAlphabetically.bind(listBaseGameLevelIds()));
var result:Array<String> = listEntryIds();
result.sort(SortUtil.defaultsThenAlphabetically.bind(listBaseGameEntryIds()));
return result;
}
/**
* A list of all installed story weeks that are not from the base game.
*/
public function listModdedLevelIds():Array<String>
{
return listEntryIds().filter(function(id:String):Bool {
return listBaseGameLevelIds().indexOf(id) == -1;
});
}
}
+1
View File
@@ -10,6 +10,7 @@ import flixel.math.FlxPoint;
* An offshoot of FlxTrail, but accomodates the way Funkin
* does offsets for characters. example, fixes Spirits trail
*/
@:nullSafety
class FunkTrail extends FlxTrail
{
/**
+1
View File
@@ -4,6 +4,7 @@ import flixel.util.FlxTimer;
import flixel.FlxCamera;
import openfl.filters.ColorMatrixFilter;
@:nullSafety
class RetroCameraFade
{
// im lazy, but we only use this for week 6
@@ -35,6 +35,11 @@ class FlxAtlasSprite extends FlxAnimate
*/
public var onAnimationComplete:FlxTypedSignal<String->Void> = new FlxTypedSignal();
/**
* Signal dispatched when a looping animation finishes playing.
*/
public var onAnimationLoop:FlxTypedSignal<String->Void> = new FlxTypedSignal();
var currentAnimation:String;
var canPlayOtherAnims:Bool = true;
@@ -61,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('');
@@ -307,12 +314,11 @@ class FlxAtlasSprite extends FlxAnimate
{
anim.curFrame = (fr != null) ? fr.index : 0;
anim.resume();
// _onAnimationComplete not called since this is a loop.
_onAnimationLoop();
}
else if (fr != null && anim.curFrame != anim.length - 1)
{
anim.curFrame--;
cleanupAnimation(currentAnimation ?? "");
_onAnimationComplete();
}
}
@@ -331,6 +337,18 @@ class FlxAtlasSprite extends FlxAnimate
}
}
function _onAnimationLoop():Void
{
if (currentAnimation != null)
{
onAnimationLoop.dispatch(currentAnimation);
}
else
{
onAnimationLoop.dispatch('');
}
}
var prevFrames:Map<Int, FlxFrame> = [];
public function replaceFrameGraphic(index:Int, ?graphic:FlxGraphicAsset):Void
+5
View File
@@ -74,6 +74,11 @@ interface INoteScriptedClass extends IScriptedClass
* Called when EITHER player (usually the player) misses a note.
*/
public function onNoteMiss(event:NoteScriptEvent):Void;
/**
* Called when EITHER player (usually the player) drops a hold note.
*/
public function onNoteHoldDrop(event:HoldNoteScriptEvent):Void;
}
/**
+12 -3
View File
@@ -11,6 +11,7 @@ import funkin.play.notes.notekind.NoteKindManager;
import funkin.data.song.SongRegistry;
import funkin.data.freeplay.player.PlayerRegistry;
import funkin.data.stage.StageRegistry;
import funkin.data.stickers.StickerRegistry;
import funkin.data.freeplay.album.AlbumRegistry;
import funkin.modding.module.ModuleHandler;
import funkin.play.character.CharacterData.CharacterDataParser;
@@ -38,7 +39,7 @@ class PolymodHandler
* Using more complex rules allows mods from older compatible versions to stay functioning,
* while preventing mods made for future versions from being installed.
*/
static final API_VERSION_RULE:String = ">=0.5.0 <0.6.0";
static final API_VERSION_RULE:String = ">=0.6.3 <0.7.0";
/**
* Where relative to the executable that mods are located.
@@ -79,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());
}
@@ -90,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);
}
@@ -102,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([]);
@@ -250,6 +255,9 @@ class PolymodHandler
Polymod.addImportAlias('lime.utils.Assets', funkin.Assets);
Polymod.addImportAlias('openfl.utils.Assets', funkin.Assets);
// `funkin.util.FileUtil` has unrestricted access to the file system.
Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtilSandboxed);
// Add blacklisting for prohibited classes and packages.
// `Sys`
@@ -480,6 +488,7 @@ class PolymodHandler
SpeakerRegistry.instance.loadEntries();
AlbumRegistry.instance.loadEntries();
StageRegistry.instance.loadEntries();
StickerRegistry.instance.loadEntries();
CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry.
NoteKindManager.loadScripts();
@@ -5,6 +5,7 @@ import funkin.data.song.SongData.SongEventData;
import flixel.FlxState;
import flixel.FlxSubState;
import funkin.play.notes.NoteSprite;
import funkin.play.notes.SustainTrail;
import funkin.play.cutscene.dialogue.Conversation;
import funkin.play.Countdown.CountdownStep;
import funkin.play.notes.NoteDirection;
@@ -226,6 +227,48 @@ class GhostMissNoteScriptEvent extends ScriptEvent
}
}
class HoldNoteScriptEvent extends NoteScriptEvent
{
/**
* The hold note that was hit (or dropped).
*/
public var holdNote:SustainTrail;
/**
* The score the player received for hitting the note.
*/
public var score:Int;
/**
* If the hit causes a combo break.
*/
public var isComboBreak:Bool = false;
/**
* The time difference when the player hit the note
*/
public var hitDiff:Float = 0;
/**
* Whether this note hit causes a note splash to display.
* Defaults to true only on "sick" notes.
*/
public var doesNotesplash:Bool = false;
public function new(type:ScriptEventType, holdNote:SustainTrail, healthChange:Float, score:Int, isComboBreak:Bool, cancelable:Bool = false):Void
{
super(type, null, healthChange, comboCount, true);
this.holdNote = holdNote;
this.score = score;
this.isComboBreak = isComboBreak;
}
public override function toString():String
{
return 'HoldNoteScriptEvent(type=$type, holdNote=$holdNote, healthChange=$healthChange, score=$score, isComboBreak=$isComboBreak, cancelable=$cancelable)';
}
}
/**
* An event that is fired when the song reaches an event.
*/
@@ -90,6 +90,9 @@ class ScriptEventDispatcher
case NOTE_MISS:
t.onNoteMiss(cast event);
return;
case NOTE_HOLD_DROP:
t.onNoteHoldDrop(cast event);
return;
default: // Continue;
}
}
@@ -88,6 +88,14 @@ enum abstract ScriptEventType(String) from String to String
*/
var NOTE_MISS = 'NOTE_MISS';
/**
* Called when a character lets go of a hold note.
* Important information such as note data, player/opponent, etc. are all provided.
*
* This event is not cancelable.
*/
var NOTE_HOLD_DROP = 'NOTE_HOLD_DROP';
/**
* Called when a character presses a note when there was none there, causing them to lose health.
* Important information such as direction pressed, etc. are all provided.
+88
View File
@@ -57,6 +57,9 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
// TODO: Half of these aren't actually being called!!!!!!!
/**
* Called when ANY script event is dispatched.
*/
public function onScriptEvent(event:ScriptEvent) {}
/**
@@ -71,55 +74,140 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
*/
public function onDestroy(event:ScriptEvent) {}
/**
* Called every frame.
*/
public function onUpdate(event:UpdateScriptEvent) {}
/**
* Called when the game is paused.
*/
public function onPause(event:PauseScriptEvent) {}
/**
* Called when the game is resumed.
*/
public function onResume(event:ScriptEvent) {}
/**
* Called when the song begins.
*/
public function onSongStart(event:ScriptEvent) {}
/**
* Called when the song ends.
*/
public function onSongEnd(event:ScriptEvent) {}
/**
* Called when the player dies.
*/
public function onGameOver(event:ScriptEvent) {}
/**
* Called when a note on the strumline has been rendered and is now onscreen.
* This gets dispatched for both the player and opponent strumlines.
*/
public function onNoteIncoming(event:NoteScriptEvent) {}
/**
* Called when a note has been hit.
* This gets dispatched for both the player and opponent strumlines.
*/
public function onNoteHit(event:HitNoteScriptEvent) {}
/**
* Called when a note has been missed.
* This gets dispatched for both the player and opponent strumlines.
*/
public function onNoteMiss(event:NoteScriptEvent) {}
public function onNoteHoldDrop(event:HoldNoteScriptEvent) {}
/**
* Called when the player presses a key without any notes present.
*/
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {}
/**
* Called when a step is hit in the song.
*/
public function onStepHit(event:SongTimeScriptEvent) {}
/**
* Called when a beat is hit in the song.
*/
public function onBeatHit(event:SongTimeScriptEvent) {}
/**
* Called when a song event is triggered.
*/
public function onSongEvent(event:SongEventScriptEvent) {}
/**
* Called when the countdown begins.
*/
public function onCountdownStart(event:CountdownScriptEvent) {}
/**
* Called for every step in the countdown.
*/
public function onCountdownStep(event:CountdownScriptEvent) {}
/**
* Called when the countdown ends, but BEFORE the song starts.
*/
public function onCountdownEnd(event:CountdownScriptEvent) {}
/**
* Called when the song's chart has been parsed and loaded.
*/
public function onSongLoaded(event:SongLoadScriptEvent) {}
/**
* Called when the game is about to switch to a new state.
*/
public function onStateChangeBegin(event:StateChangeScriptEvent) {}
/**
* Called after the game has switched to a new state.
*/
public function onStateChangeEnd(event:StateChangeScriptEvent) {}
/**
* Called when the game regains focus.
* This does not get called if "Auto Pause" is disabled.
*/
public function onFocusGained(event:FocusScriptEvent) {}
/**
* Called when the game loses focus.
* This does not get called if "Auto Pause" is disabled.
*/
public function onFocusLost(event:FocusScriptEvent) {}
/**
* Called when the game is about to open a substate.
*/
public function onSubStateOpenBegin(event:SubStateScriptEvent) {}
/**
* Called when a substate has been opened.
*/
public function onSubStateOpenEnd(event:SubStateScriptEvent) {}
/**
* Called when the game is about to close a substate.
*/
public function onSubStateCloseBegin(event:SubStateScriptEvent) {}
/**
* Called when a substate has been closed.
*/
public function onSubStateCloseEnd(event:SubStateScriptEvent) {}
/**
* Called when the song has been restarted.
*/
public function onSongRetry(event:SongRetryEvent) {}
}
+1 -1
View File
@@ -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
+14 -10
View File
@@ -291,7 +291,7 @@ class GameOverSubState extends MusicBeatSubState
}
else
{
var targetState:funkin.ui.transition.StickerSubState->FlxState = (PlayStatePlaylist.isStoryMode) ? (sticker) ->
var targetState:funkin.ui.transition.stickers.StickerSubState->FlxState = (PlayStatePlaylist.isStoryMode) ? (sticker) ->
new StoryMenuState(sticker) : (sticker) -> FreeplayState.build(sticker);
if (PlayStatePlaylist.isStoryMode)
@@ -299,16 +299,20 @@ class GameOverSubState extends MusicBeatSubState
PlayStatePlaylist.reset();
}
var playerCharacterId = PlayerRegistry.instance.getCharacterOwnerId(PlayState.instance.currentChart.characters.player);
var stickerSet = (playerCharacterId == "pico") ? "stickers-set-2" : "stickers-set-1";
var stickerPack = switch (PlayState.instance.currentChart.song.id)
{
case "tutorial": "tutorial";
case "darnell" | "lit-up" | "2hot": "weekend";
default: "all";
};
var stickerPackId:Null<String> = PlayState.instance.currentChart.stickerPack;
openSubState(new funkin.ui.transition.StickerSubState({targetState: targetState, stickerSet: stickerSet, stickerPack: stickerPack}));
if (stickerPackId == null)
{
var playerCharacterId = PlayerRegistry.instance.getCharacterOwnerId(PlayState.instance.currentChart.characters.player);
var playerCharacter = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? Constants.DEFAULT_CHARACTER);
if (playerCharacter != null)
{
stickerPackId = playerCharacter.getStickerPackID();
}
}
openSubState(new funkin.ui.transition.stickers.StickerSubState({targetState: targetState, stickerPack: stickerPackId}));
}
}
+16 -12
View File
@@ -82,8 +82,8 @@ class PauseSubState extends MusicBeatSubState
*/
static final PAUSE_MENU_ENTRIES_CONVERSATION:Array<PauseMenuEntry> = [
{text: 'Resume', callback: resume},
{text: 'Restart Dialogue', callback: restartConversation},
{text: 'Skip Dialogue', callback: skipConversation},
{text: 'Restart Dialogue', callback: restartConversation},
{text: 'Exit to Menu', callback: quitToMenu},
];
@@ -737,8 +737,8 @@ class PauseSubState extends MusicBeatSubState
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
var targetState:funkin.ui.transition.StickerSubState->FlxState = (PlayStatePlaylist.isStoryMode) ? (sticker) -> new StoryMenuState(sticker) : (sticker) ->
FreeplayState.build(sticker);
var targetState:funkin.ui.transition.stickers.StickerSubState->FlxState = (PlayStatePlaylist.isStoryMode) ? (sticker) ->
new StoryMenuState(sticker) : (sticker) -> FreeplayState.build(sticker);
// Do this AFTER because this resets the value of isStoryMode!
if (PlayStatePlaylist.isStoryMode)
@@ -746,16 +746,20 @@ class PauseSubState extends MusicBeatSubState
PlayStatePlaylist.reset();
}
var playerCharacterId = PlayerRegistry.instance.getCharacterOwnerId(PlayState.instance.currentChart.characters.player);
var stickerSet = (playerCharacterId == "pico") ? "stickers-set-2" : "stickers-set-1";
var stickerPack = switch (PlayState.instance.currentChart.song.id)
{
case "tutorial": "tutorial";
case "darnell" | "lit-up" | "2hot": "weekend";
default: "all";
};
var stickerPackId:Null<String> = PlayState.instance.currentChart.stickerPack;
state.openSubState(new funkin.ui.transition.StickerSubState({targetState: targetState, stickerSet: stickerSet, stickerPack: stickerPack}));
if (stickerPackId == null)
{
var playerCharacterId = PlayerRegistry.instance.getCharacterOwnerId(PlayState.instance.currentChart.characters.player);
var playerCharacter = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? Constants.DEFAULT_CHARACTER);
if (playerCharacter != null)
{
stickerPackId = playerCharacter.getStickerPackID();
}
}
state.openSubState(new funkin.ui.transition.stickers.StickerSubState({targetState: targetState, stickerPack: stickerPackId}));
}
/**
+77 -126
View File
@@ -32,7 +32,6 @@ import funkin.api.newgrounds.Events;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.play.character.BaseCharacter;
import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.play.components.ComboMilestone;
import funkin.play.components.HealthIcon;
import funkin.play.components.PopUpStuff;
import funkin.play.cutscene.dialogue.Conversation;
@@ -657,9 +656,6 @@ class PlayState extends MusicBeatSubState
cameraFollowPoint = new FlxObject(0, 0);
}
// Reduce physics accuracy (who cares!!!) to improve animation quality.
FlxG.fixedTimestep = false;
// This state receives update() even when a substate is active.
this.persistentUpdate = true;
// This state receives draw calls even when a substate is active.
@@ -838,8 +834,6 @@ class PlayState extends MusicBeatSubState
startingSong = true;
isPlayerDying = false;
inputSpitter = [];
// Reset music properly.
if (FlxG.sound.music != null)
{
@@ -881,6 +875,9 @@ class PlayState extends MusicBeatSubState
// Delete all notes and reset the arrays.
regenNoteData();
// 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;
@@ -889,11 +886,20 @@ class PlayState extends MusicBeatSubState
health = Constants.HEALTH_STARTING;
songScore = 0;
Highscore.tallies.combo = 0;
Countdown.performCountdown();
// timer for vwoosh
var vwooshTimer = new FlxTimer();
vwooshTimer.start(0.5, function(t:FlxTimer) {
Conductor.instance.update(startTimestamp - Conductor.instance.combinedOffset, false);
if (playerStrumline.notes.length == 0) playerStrumline.updateNotes();
if (opponentStrumline.notes.length == 0) opponentStrumline.updateNotes();
playerStrumline.vwooshInNotes();
opponentStrumline.vwooshInNotes();
Countdown.performCountdown();
});
// Reset the health icons.
currentStage.getBoyfriend().initHealthIcon(false);
currentStage.getDad().initHealthIcon(true);
currentStage?.getBoyfriend()?.initHealthIcon(false);
currentStage?.getDad()?.initHealthIcon(true);
needsReset = false;
}
@@ -942,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);
@@ -1132,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.
@@ -1415,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);
}
@@ -1510,35 +1517,6 @@ class PlayState extends MusicBeatSubState
// Break up into individual lines to aid debugging.
var shouldShowComboText:Bool = false;
// TODO: Re-enable combo text (how to do this without sections?).
// if (currentSong != null)
// {
// shouldShowComboText = (Conductor.instance.currentBeat % 8 == 7);
// var daSection = .getSong()[Std.int(Conductor.instance.currentBeat / 16)];
// shouldShowComboText = shouldShowComboText && (daSection != null && daSection.mustHitSection);
// shouldShowComboText = shouldShowComboText && (Highscore.tallies.combo > 5);
//
// var daNextSection = .getSong()[Std.int(Conductor.instance.currentBeat / 16) + 1];
// var isEndOfSong = .getSong().length < Std.int(Conductor.instance.currentBeat / 16);
// shouldShowComboText = shouldShowComboText && (isEndOfSong || (daNextSection != null && !daNextSection.mustHitSection));
// }
if (shouldShowComboText)
{
var animShit:ComboMilestone = new ComboMilestone(-100, 300, Highscore.tallies.combo);
animShit.scrollFactor.set(0.6, 0.6);
animShit.zIndex = 1100;
animShit.cameras = [camHUD];
add(animShit);
var frameShit:Float = (1 / 24) * 2; // equals 2 frames in the animation
new FlxTimer().start(((Conductor.instance.beatLengthMs / 1000) * 1.25) - frameShit, function(tmr) {
animShit.forceFinish();
});
}
if (playerStrumline != null) playerStrumline.onBeatHit();
if (opponentStrumline != null) opponentStrumline.onBeatHit();
@@ -1812,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];
@@ -2369,7 +2347,7 @@ class PlayState extends MusicBeatSubState
{
// Call an event to allow canceling the note miss.
// NOTE: This is what handles the character animations!
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, -Constants.HEALTH_MISS_PENALTY, 0, true);
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Constants.HEALTH_MISS_PENALTY, 0, true);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
@@ -2416,18 +2394,45 @@ class PlayState extends MusicBeatSubState
// The player dropped a hold note.
holdNote.handledMiss = true;
// Mute vocals and play miss animation, but don't penalize.
// Mute vocals and play miss animation.
// vocals.playerVolume = 0;
// if (currentStage != null && currentStage.getBoyfriend() != null) currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true);
if (!isBotPlayMode)
{
if (holdNote.sustainLength > Constants.HOLD_DROP_PENALTY_THRESHOLD_MS)
{
// Penalize the player for letting go of a hold note too early.
trace('Player dropped a hold note, penalizing... (has hit: ${holdNote.hitNote})');
// Different penalty based on whether the note itself was missed,
// or the note was hit and then the hold was dropped.
var remainingLengthSec = holdNote.sustainLength / Constants.MS_PER_SEC;
var healthChangeUncapped = remainingLengthSec * Constants.HEALTH_HOLD_DROP_PENALTY_PER_SECOND;
// If the base note of the hold was missed, don't penalize them more on top of that.
var healthChangeMax = Constants.HEALTH_HOLD_DROP_PENALTY_MAX - (holdNote.hitNote ? -Constants.HEALTH_MISS_PENALTY : 0);
var healthChange = healthChangeUncapped.clamp(healthChangeMax, 0);
var scoreChange = Std.int(Constants.SCORE_HOLD_DROP_PENALTY_PER_SECOND * remainingLengthSec);
var event:HoldNoteScriptEvent = new HoldNoteScriptEvent(NOTE_HOLD_DROP, holdNote, healthChange, scoreChange, true);
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, '', event.healthChange, event.isComboBreak);
// Play the miss sound.
vocals.playerVolume = 0;
FunkinSound.playOnce(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.5, 0.6));
}
else
{
trace('Hold note too short, not penalizing...');
}
}
}
}
}
/**
* Spitting out the input for ravy 🙇‍♂️!!
*/
var inputSpitter:Array<ScoreInput> = [];
function handleSkippedNotes():Void
{
for (note in playerStrumline.notes.members)
@@ -2612,31 +2617,10 @@ class PlayState extends MusicBeatSubState
{
if (pressArray[i]) indices.push(i);
}
if (indices.length > 0)
{
for (i in 0...indices.length)
{
inputSpitter.push(
{
t: Std.int(Conductor.instance.songPosition),
d: indices[i],
l: 20
});
}
}
else
{
inputSpitter.push(
{
t: Std.int(Conductor.instance.songPosition),
d: -1,
l: 20
});
}
}
vocals.playerVolume = 0;
applyScore(-10, 'miss', healthChange, true);
applyScore(Scoring.getMissScore(), 'miss', healthChange, true);
if (playSound)
{
@@ -2657,7 +2641,7 @@ class PlayState extends MusicBeatSubState
{
var event:GhostMissNoteScriptEvent = new GhostMissNoteScriptEvent(direction, // Direction missed in.
hasPossibleNotes, // Whether there was a note you could have hit.
- 1 * Constants.HEALTH_MISS_PENALTY, // How much health to add (negative).
Constants.HEALTH_GHOST_MISS_PENALTY, // How much health to add (negative).
- 10 // Amount of score to add (negative).
);
dispatchEvent(event);
@@ -2682,15 +2666,6 @@ class PlayState extends MusicBeatSubState
{
if (pressArray[i]) indices.push(i);
}
for (i in 0...indices.length)
{
inputSpitter.push(
{
t: Std.int(Conductor.instance.songPosition),
d: indices[i],
l: 20
});
}
}
if (event.playSound)
@@ -2754,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.
@@ -2764,8 +2739,6 @@ class PlayState extends MusicBeatSubState
// SHIFT+PAGEDOWN: Skip backward twenty sections.
if (FlxG.keys.justPressed.PAGEDOWN) changeSection(FlxG.keys.pressed.SHIFT ? -20 : -2);
#end
if (FlxG.keys.justPressed.B) trace(inputSpitter.join('\n'));
}
/**
@@ -2785,6 +2758,8 @@ class PlayState extends MusicBeatSubState
Highscore.tallies.shit += 1;
case 'miss':
Highscore.tallies.missed += 1;
default:
// Nothing!
}
health += healthChange;
if (isComboBreak)
@@ -2831,27 +2806,6 @@ class PlayState extends MusicBeatSubState
{
if (pressArray[i]) indices.push(i);
}
if (indices.length > 0)
{
for (i in 0...indices.length)
{
inputSpitter.push(
{
t: Std.int(Conductor.instance.songPosition),
d: indices[i],
l: 20
});
}
}
else
{
inputSpitter.push(
{
t: Std.int(Conductor.instance.songPosition),
d: -1,
l: 20
});
}
}
comboPopUps.displayRating(daRating);
if (combo >= 10) comboPopUps.displayCombo(combo);
@@ -2935,13 +2889,6 @@ class PlayState extends MusicBeatSubState
dispatchEvent(event);
if (event.eventCanceled) return;
#if sys
// spitter for ravy, teehee!!
var writer = new json2object.JsonWriter<Array<ScoreInput>>();
var output = writer.write(inputSpitter, ' ');
sys.io.File.saveContent("./scores.json", output);
#end
deathCounter = 0;
// TODO: This line of code makes me sad, but you can't really fix it without a breaking migration.
@@ -2975,6 +2922,10 @@ class PlayState extends MusicBeatSubState
// adds current song data into the tallies for the level (story levels)
Highscore.talliesLevel = Highscore.combineTallies(Highscore.tallies, Highscore.talliesLevel);
#if FEATURE_NEWGROUNDS
Leaderboards.submitSongScore(currentSong.id, suffixedDifficulty, songScore);
#end
if (!isPracticeMode && !isBotPlayMode)
{
#if FEATURE_NEWGROUNDS
@@ -3017,7 +2968,7 @@ class PlayState extends MusicBeatSubState
}
});
// Award various medals based on variation, difficulty, and scoring rank.
// Award various medals based on variation, difficulty, song ID, and scoring rank.
if (scoreRank == ScoringRank.SHIT) Medals.award(LossRating);
if (scoreRank >= ScoringRank.PERFECT && currentDifficulty == 'hard') Medals.award(PerfectRatingHard);
if (scoreRank == ScoringRank.PERFECT_GOLD && currentDifficulty == 'hard') Medals.award(GoldPerfectRatingHard);
@@ -3062,19 +3013,18 @@ class PlayState extends MusicBeatSubState
},
};
if (currentSong.validScore
&& Save.instance.isLevelHighScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data))
#if FEATURE_NEWGROUNDS
// Award a medal for beating a Story level.
Medals.awardStoryLevel(PlayStatePlaylist.campaignId);
// Submit the score for the Story level to Newgrounds.
Leaderboards.submitLevelScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, PlayStatePlaylist.campaignScore);
Events.logCompleteLevel(PlayStatePlaylist.campaignId);
#end
if (Save.instance.isLevelHighScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data))
{
#if FEATURE_NEWGROUNDS
// Award a medal for beating a Story level.
Medals.awardStoryLevel(PlayStatePlaylist.campaignId);
// Submit the score for the Story level to Newgrounds.
Leaderboards.submitLevelScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, PlayStatePlaylist.campaignScore);
Events.logCompleteLevel(PlayStatePlaylist.campaignId);
#end
Save.instance.setLevelScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data);
isNewHighscore = true;
}
@@ -3331,6 +3281,7 @@ class PlayState extends MusicBeatSubState
storyMode: PlayStatePlaylist.isStoryMode,
songId: currentChart.song.id,
difficultyId: currentDifficulty,
variationId: currentVariation,
characterId: currentChart.characters.player,
title: PlayStatePlaylist.isStoryMode ? ('${PlayStatePlaylist.campaignTitle}') : ('${currentChart.songName} by ${currentChart.songArtist}'),
prevScoreData: prevScoreData,
+2
View File
@@ -19,6 +19,8 @@ class ResultScore extends FlxTypedSpriteGroup<ScoreNum>
var dumbNumb = Std.parseInt(Std.string(val));
var prevNum:ScoreNum;
dumbNumb = Std.int(Math.min(dumbNumb, Math.pow(10, group.members.length) - 1));
while (dumbNumb > 0)
{
scoreStart += 1;
+77 -69
View File
@@ -1,34 +1,37 @@
package funkin.play;
import funkin.ui.story.StoryMenuState;
import funkin.graphics.adobeanimate.FlxAtlasSprite;
import flixel.addons.display.FlxBackdrop;
import flixel.effects.FlxFlicker;
import flixel.FlxSprite;
import flixel.FlxSubState;
import funkin.graphics.FunkinSprite;
import flixel.effects.FlxFlicker;
import flixel.graphics.frames.FlxBitmapFont;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxPoint;
import funkin.ui.MusicBeatSubState;
import flixel.math.FlxRect;
import flixel.text.FlxBitmapText;
import funkin.data.freeplay.player.PlayerRegistry;
import funkin.data.freeplay.player.PlayerData.PlayerResultsAnimationData;
import funkin.ui.freeplay.charselect.PlayableCharacter;
import flixel.util.FlxColor;
import flixel.tweens.FlxEase;
import funkin.graphics.FunkinCamera;
import funkin.ui.freeplay.FreeplayState;
import flixel.tweens.FlxTween;
import flixel.addons.display.FlxBackdrop;
import funkin.audio.FunkinSound;
import flixel.util.FlxColor;
import flixel.util.FlxGradient;
import flixel.util.FlxTimer;
import funkin.play.scoring.Scoring;
import funkin.save.Save.SaveScoreData;
import funkin.audio.FunkinSound;
import funkin.data.freeplay.player.PlayerData.PlayerResultsAnimationData;
import funkin.data.freeplay.player.PlayerRegistry;
import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinCamera;
import funkin.graphics.FunkinSprite;
import funkin.graphics.shaders.LeftMaskShader;
import funkin.play.components.TallyCounter;
import funkin.play.components.ClearPercentCounter;
import funkin.play.components.TallyCounter;
import funkin.play.scoring.Scoring;
import funkin.play.song.Song;
import funkin.data.song.SongRegistry;
import funkin.save.Save.SaveScoreData;
import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.freeplay.FreeplayState;
import funkin.ui.MusicBeatSubState;
import funkin.ui.story.StoryMenuState;
import funkin.modding.base.ScriptedFlxAtlasSprite;
#if FEATURE_NEWGROUNDS
import funkin.api.newgrounds.Medals;
#end
@@ -72,9 +75,10 @@ class ResultState extends MusicBeatSubState
delay:Float
}> = [];
var playerCharacterId:Null<String>;
var playerCharacterId:Null<String> = null;
var playerCharacter:Null<PlayableCharacter> = null;
var introMusicAudio:Null<FunkinSound>;
var introMusicAudio:Null<FunkinSound> = null;
var rankBg:FunkinSprite;
final cameraBG:FunkinCamera;
@@ -171,7 +175,7 @@ class ResultState extends MusicBeatSubState
// Fetch playable character data. Default to BF on the results screen if we can't find it.
playerCharacterId = PlayerRegistry.instance.getCharacterOwnerId(params.characterId);
var playerCharacter:Null<PlayableCharacter> = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? 'bf');
playerCharacter = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? 'bf');
trace('Got playable character: ${playerCharacter?.getName()}');
// Query JSON data based on the rank, then use that to build the animation(s) the player sees.
@@ -186,13 +190,27 @@ class ResultState extends MusicBeatSubState
if (Preferences.naughtyness && animData.filter != "naughty" || !Preferences.naughtyness && animData.filter != "safe") continue;
}
var animPath:String = Paths.stripLibrary(animData.assetPath);
var animLibrary:String = Paths.getLibrary(animData.assetPath);
var animPath:String = "";
var animLibrary:String = "";
if (animData.assetPath != null)
{
animPath = Paths.stripLibrary(animData.assetPath);
animLibrary = Paths.getLibrary(animData.assetPath);
}
var offsets = animData.offsets ?? [0, 0];
switch (animData.renderType)
{
case 'animateatlas':
var animation:FlxAtlasSprite = new FlxAtlasSprite(offsets[0], offsets[1], Paths.animateAtlas(animPath, animLibrary));
@:nullSafety(Off)
var animation:FlxAtlasSprite = null;
if (animData.scriptClass != null) animation = ScriptedFlxAtlasSprite.init(animData.scriptClass, offsets[0], offsets[1]);
else
animation = new FlxAtlasSprite(offsets[0], offsets[1], Paths.animateAtlas(animPath, animLibrary));
if (animation == null) continue;
animation.zIndex = animData.zIndex ?? 500;
animation.scale.set(animData.scale ?? 1.0, animData.scale ?? 1.0);
@@ -221,7 +239,6 @@ class ResultState extends MusicBeatSubState
animation.onAnimationComplete.add((_name:String) -> {
if (animation != null)
{
trace("AHAHAH");
animation.anim.curFrame = animData.loopFrame ?? 0;
animation.anim.play(); // unpauses this anim, since it's on PlayOnce!
}
@@ -267,7 +284,7 @@ class ResultState extends MusicBeatSubState
}
}
var diffSpr:String = 'diff_${params?.difficultyId ?? 'Normal'}';
var diffSpr:String = 'diff_${params?.difficultyId ?? Constants.DEFAULT_DIFFICULTY}';
difficulty.loadGraphic(Paths.image("resultScreen/" + diffSpr));
add(difficulty);
@@ -471,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.
@@ -489,7 +507,7 @@ class ResultState extends MusicBeatSubState
// Only play the tick sound if the number increased.
if (clearPercentLerp != clearPercentCounter.curNumber)
{
trace('$clearPercentLerp and ${clearPercentCounter.curNumber}');
// trace('$clearPercentLerp and ${clearPercentCounter.curNumber}');
clearPercentLerp = clearPercentCounter.curNumber;
FunkinSound.playOnce(Paths.sound('scrollMenu'));
}
@@ -502,8 +520,9 @@ class ResultState extends MusicBeatSubState
clearPercentCounter.curNumber = clearPercentTarget;
#if FEATURE_NEWGROUNDS
var isScoreValid = !(params?.isPracticeMode ?? false) && !(params?.isBotPlayMode ?? false);
// This is the easiest spot to do the medal calculation lol.
if (clearPercentTarget == 69) Medals.award(Nice);
if (isScoreValid && clearPercentTarget == 69) Medals.award(Nice);
#end
clearPercentCounter.flash(true);
@@ -697,34 +716,6 @@ class ResultState extends MusicBeatSubState
override function update(elapsed:Float):Void
{
// if(FlxG.keys.justPressed.R){
// FlxG.switchState(() -> new funkin.play.ResultState(
// {
// storyMode: false,
// title: "Cum Song Erect by Kawai Sprite",
// songId: "cum",
// difficultyId: "nightmare",
// isNewHighscore: true,
// scoreData:
// {
// score: 1_234_567,
// tallies:
// {
// sick: 200,
// good: 0,
// bad: 0,
// shit: 0,
// missed: 0,
// combo: 0,
// maxCombo: 69,
// totalNotesHit: 200,
// totalNotes: 200 // 0,
// }
// },
// }));
// }
// maskShaderSongName.swagSprX = songName.x;
maskShaderDifficulty.swagSprX = difficulty.x;
if (movingSongStuff)
@@ -745,8 +736,16 @@ class ResultState extends MusicBeatSubState
if (controls.PAUSE || controls.ACCEPT)
{
if (_parentState is funkin.ui.debug.results.ResultsDebugSubState)
{
if (introMusicAudio != null)
{
introMusicAudio.stop();
introMusicAudio.destroy();
introMusicAudio = null;
}
close(); // IF we are a substate, we will close ourselves. This is used from ResultsDebugSubState
if (introMusicAudio != null)
}
else if (introMusicAudio != null)
{
@:nullSafety(Off)
introMusicAudio.onComplete = null;
@@ -792,13 +791,18 @@ class ResultState extends MusicBeatSubState
var shouldTween = false;
var shouldUseSubstate = false;
var stickerSet = (playerCharacterId == "pico") ? "stickers-set-2" : "stickers-set-1";
var stickerPack = switch (PlayState.instance?.currentChart?.song?.id)
var stickerPackId:Null<String> = null;
var song:Null<Song> = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId);
if (song != null)
{
case "tutorial": "tutorial";
case "darnell" | "lit-up" | "2hot": "weekend";
default: "all";
};
stickerPackId = song.getStickerPackId(params?.difficultyId ?? Constants.DEFAULT_DIFFICULTY, params?.variationId ?? Constants.DEFAULT_VARIATION);
}
if (stickerPackId == null && playerCharacter != null)
{
stickerPackId = playerCharacter.getStickerPackID();
}
if (params.storyMode)
{
@@ -821,11 +825,10 @@ class ResultState extends MusicBeatSubState
// No new characters.
shouldTween = false;
shouldUseSubstate = true;
targetState = new funkin.ui.transition.StickerSubState(
targetState = new funkin.ui.transition.stickers.StickerSubState(
{
targetState: (sticker) -> new StoryMenuState(sticker),
stickerSet: stickerSet,
stickerPack: stickerPack
stickerPack: stickerPackId
});
}
}
@@ -859,11 +862,10 @@ class ResultState extends MusicBeatSubState
{
shouldTween = false;
shouldUseSubstate = true;
targetState = new funkin.ui.transition.StickerSubState(
targetState = new funkin.ui.transition.stickers.StickerSubState(
{
targetState: (sticker) -> FreeplayState.build(null, sticker),
stickerSet: stickerSet,
stickerPack: stickerPack
stickerPack: stickerPackId
});
}
}
@@ -943,10 +945,16 @@ typedef ResultsStateParams =
/**
* The difficulty ID of the song/week we just played.
* @default Normal
* @default `Constants.DEFAULT_DIFFICULTY`
*/
var ?difficultyId:String;
/**
* The variation ID of the song/week we just played.
* @default `Constants.DEFAULT_VARIATION`
*/
var ?variationId:String;
/**
* The score, accuracy, and judgements.
*/
+26 -3
View File
@@ -553,17 +553,40 @@ class BaseCharacter extends Bopper
if (event.note.noteData.getMustHitNote() && characterType == BF)
{
// If the note is from the same strumline, play the sing animation.
// If the note is from the same strumline, play the miss animation.
this.playSingAnimation(event.note.noteData.getDirection(), true);
}
else if (!event.note.noteData.getMustHitNote() && characterType == DAD)
{
// If the note is from the same strumline, play the sing animation.
// If the note is from the same strumline, play the miss animation.
this.playSingAnimation(event.note.noteData.getDirection(), true);
}
else if (event.note.noteData.getMustHitNote() && characterType == GF)
{
playComboDropAnimation(Highscore.tallies.combo);
playComboDropAnimation(event.comboCount);
}
}
public override function onNoteHoldDrop(event:HoldNoteScriptEvent)
{
super.onNoteHoldDrop(event);
// If another script cancelled the event, don't do anything.
if (event.eventCanceled) return;
if (event.holdNote.noteData.getMustHitNote() && characterType == BF)
{
// If the note is from the same strumline, play the miss animation.
this.playSingAnimation(event.holdNote.noteData.getDirection(), true);
}
else if (!event.holdNote.noteData.getMustHitNote() && characterType == DAD)
{
// If the note is from the same strumline, play the miss animation.
this.playSingAnimation(event.holdNote.noteData.getDirection(), true);
}
else if (event.holdNote.noteData.getMustHitNote() && event.isComboBreak && characterType == GF)
{
playComboDropAnimation(event.comboCount);
}
}
@@ -296,7 +296,7 @@ class CharacterDataParser
charPath += "monsterpixel";
case "mom" | "mom-car":
charPath += "mommypixel";
case "pico-blazin" | "pico-playable" | "pico-speaker":
case "pico-blazin" | "pico-playable" | "pico-speaker" | "pico-pixel" | "pico-holding-nene":
charPath += "picopixel";
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen" | "gf-dark":
charPath += "gfpixel";
@@ -308,7 +308,7 @@ class CharacterDataParser
charPath += "senpaipixel";
case "spooky-dark":
charPath += "spookypixel";
case "tankman-atlas":
case "tankman-atlas" | "tankman-bloody":
charPath += "tankmanpixel";
case "pico-christmas" | "pico-dark":
charPath += "picopixel";
@@ -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);
@@ -1,126 +0,0 @@
package funkin.play.components;
import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
import flixel.util.FlxTimer;
import funkin.audio.FunkinSound;
class ComboMilestone extends FlxTypedSpriteGroup<FlxSprite>
{
var effectStuff:FlxSprite;
var wasComboSetup:Bool = false;
var daCombo:Int = 0;
var grpNumbers:FlxTypedGroup<ComboMilestoneNumber>;
var onScreenTime:Float = 0;
public function new(x:Float, y:Float, daCombo:Int = 0)
{
super(x, y);
this.daCombo = daCombo;
effectStuff = new FlxSprite(0, 0);
effectStuff.frames = Paths.getSparrowAtlas('comboMilestone');
effectStuff.animation.addByPrefix('funny', 'NOTE COMBO animation', 24, false);
effectStuff.animation.play('funny');
effectStuff.animation.finishCallback = function(nameThing) {
kill();
};
effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7));
add(effectStuff);
grpNumbers = new FlxTypedGroup<ComboMilestoneNumber>();
// add(grpNumbers);
}
public function forceFinish():Void
{
if (onScreenTime < 0.9)
{
new FlxTimer().start((Conductor.instance.beatLengthMs / 1000) * 0.25, function(tmr) {
forceFinish();
});
}
else
effectStuff.animation.play('funny', true, false, 18);
}
override function update(elapsed:Float)
{
onScreenTime += elapsed;
if (effectStuff.animation.curAnim.curFrame == 17) effectStuff.animation.pause();
if (effectStuff.animation.curAnim.curFrame == 2 && !wasComboSetup)
{
setupCombo(daCombo);
}
if (effectStuff.animation.curAnim.curFrame == 18)
{
grpNumbers.forEach(function(spr:ComboMilestoneNumber) {
spr.animation.reset();
});
}
if (effectStuff.animation.curAnim.curFrame == 20)
{
grpNumbers.forEach(function(spr:ComboMilestoneNumber) {
spr.kill();
});
}
super.update(elapsed);
}
function setupCombo(daCombo:Int)
{
FunkinSound.playOnce(Paths.sound('comboSound'));
wasComboSetup = true;
var loopNum:Int = 0;
while (daCombo > 0)
{
var comboNumber:ComboMilestoneNumber = new ComboMilestoneNumber(450 - (100 * loopNum), 20 + 14 * loopNum, daCombo % 10);
comboNumber.setGraphicSize(Std.int(comboNumber.width * 0.7));
grpNumbers.add(comboNumber);
add(comboNumber);
loopNum += 1;
daCombo = Math.floor(daCombo / 10);
}
}
}
class ComboMilestoneNumber extends FlxSprite
{
public function new(x:Float, y:Float, digit:Int)
{
super(x - 20, y);
var stringNum:String = Std.string(digit);
frames = Paths.getSparrowAtlas('comboMilestoneNumbers');
animation.addByPrefix(stringNum, stringNum, 24, false);
animation.play(stringNum);
updateHitbox();
}
var shiftedX:Bool = false;
override function update(elapsed:Float)
{
if (animation.curAnim.curFrame == 2 && !shiftedX)
{
shiftedX = true;
x += 20;
}
super.update(elapsed);
}
}
@@ -8,6 +8,7 @@ import funkin.util.MathUtil;
/**
* Numerical counters used next to each judgement in the Results screen.
*/
@:nullSafety
class TallyCounter extends FlxTypedSpriteGroup<FlxSprite>
{
public var curNumber:Float = 0;
@@ -17,13 +18,13 @@ class TallyCounter extends FlxTypedSpriteGroup<FlxSprite>
public var align:FlxTextAlign = FlxTextAlign.LEFT;
public function new(x:Float, y:Float, neededNumber:Int = 0, ?flavour:Int = 0xFFFFFFFF, align:FlxTextAlign = FlxTextAlign.LEFT)
public function new(x:Float, y:Float, neededNumber:Int = 0, ?flavour:Int, align:FlxTextAlign = FlxTextAlign.LEFT)
{
super(x, y);
this.align = align;
this.flavour = flavour;
this.flavour = flavour ?? 0xFFFFFFFF;
this.neededNumber = neededNumber;
@@ -79,6 +80,7 @@ class TallyCounter extends FlxTypedSpriteGroup<FlxSprite>
}
}
@:nullSafety
class TallyNumber extends FlxSprite
{
public function new(x:Float, y:Float, digit:Int)
@@ -28,22 +28,11 @@ import funkin.util.EaseUtil;
@:nullSafety
class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry<ConversationData>
{
/**
* The ID of the conversation.
*/
public final id:String;
/**
* The current state of the conversation.
*/
var state:ConversationState = ConversationState.Start;
/**
* Conversation data as parsed from the JSON file.
* `null` if the data could not be parsed or loaded.
*/
public final _data:Null<ConversationData>;
/**
* The current entry in the dialogue.
*/
@@ -659,16 +648,6 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl
outroTween = null;
}
}
public override function toString():String
{
return 'Conversation($id)';
}
static function _fetchData(id:String):Null<ConversationData>
{
return ConversationRegistry.instance.parseEntryDataWithMigration(id, ConversationRegistry.instance.fetchEntryVersion(id));
}
}
// Managing things with a single enum is a lot easier than a multitude of flags.
@@ -16,8 +16,6 @@ import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry<DialogueBoxData>
{
public final id:String;
public var dialogueBoxName(get, never):String;
function get_dialogueBoxName():String
@@ -25,8 +23,6 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
return _data.name ?? 'UNKNOWN';
}
public final _data:DialogueBoxData;
/**
* Offset the speaker's sprite by this much when playing each animation.
*/
@@ -247,8 +243,8 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
var animNames:Array<String> = this.boxSprite?.animation?.getNameList() ?? [];
trace('[DIALOGUE BOX] Successfully loaded ${animNames.length} animations for ${id}');
boxSprite.animation.callback = this.onAnimationFrame;
boxSprite.animation.finishCallback = this.onAnimationFinished;
boxSprite.animation.onFrameChange.add(this.onAnimationFrame);
boxSprite.animation.onFinish.add(this.onAnimationFinished);
}
/**
@@ -419,14 +415,4 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
}
public function onScriptEvent(event:ScriptEvent):Void {}
public override function toString():String
{
return 'DialogueBox($id)';
}
static function _fetchData(id:String):Null<DialogueBoxData>
{
return DialogueBoxRegistry.instance.parseEntryDataWithMigration(id, DialogueBoxRegistry.instance.fetchEntryVersion(id));
}
}
@@ -16,16 +16,6 @@ import funkin.data.dialogue.speaker.SpeakerRegistry;
*/
class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRegistryEntry<SpeakerData>
{
/**
* The internal ID for this speaker.
*/
public final id:String;
/**
* The full data for a speaker.
*/
public final _data:SpeakerData;
/**
* A readable name for this speaker.
*/
@@ -298,14 +288,4 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
}
public function onScriptEvent(event:ScriptEvent):Void {}
public override function toString():String
{
return 'Speaker($id)';
}
static function _fetchData(id:String):Null<SpeakerData>
{
return SpeakerRegistry.instance.parseEntryDataWithMigration(id, SpeakerRegistry.instance.fetchEntryVersion(id));
}
}
+5
View File
@@ -104,6 +104,11 @@ class NoteSprite extends FunkinSprite
return noteData.length > 0;
}
/**
* The Y Offset of the note.
*/
public var yOffset:Float = 0.0;
/**
* Set this flag to true when hitting the note to avoid scoring it multiple times.
*/
+258 -52
View File
@@ -1,5 +1,6 @@
package funkin.play.notes;
import flixel.math.FlxPoint;
import flixel.util.FlxSignal.FlxTypedSignal;
import flixel.FlxG;
import funkin.play.notes.notestyle.NoteStyle;
@@ -18,35 +19,46 @@ import funkin.play.notes.notekind.NoteKindManager;
*/
class Strumline extends FlxSpriteGroup
{
/**
* The directions of the notes on the strumline, in order.
*/
public static final DIRECTIONS:Array<NoteDirection> = [NoteDirection.LEFT, NoteDirection.DOWN, NoteDirection.UP, NoteDirection.RIGHT];
/**
* A magic number for the size of the strumline, in pixels.
*/
public static final STRUMLINE_SIZE:Int = 104;
/**
* The spacing between notes on the strumline, in pixels.
*/
public static final NOTE_SPACING:Int = STRUMLINE_SIZE + 8;
// Positional fixes for new strumline graphics.
static final INITIAL_OFFSET = -0.275 * STRUMLINE_SIZE;
static final INITIAL_OFFSET:Float = -0.275 * STRUMLINE_SIZE;
static final NUDGE:Float = 2.0;
static final KEY_COUNT:Int = 4;
static final NOTE_SPLASH_CAP:Int = 6;
static var RENDER_DISTANCE_MS(get, never):Float;
var renderDistanceMs(get, never):Float;
/**
* The custom render distance for the strumline.
* This should be in miliseconds only! Not pixels.
*/
public static var CUSTOM_RENDER_DISTANCE_MS:Float = 0.0;
public var customRenderDistanceMs:Float = 0.0;
/**
* Whether to use the custom render distance.
* If false, the render distance will be calculated based on the screen height.
*/
public static var USE_CUSTOM_RENDER_DISTANCE:Bool = false;
public var useCustomRenderDistance:Bool = false;
static function get_RENDER_DISTANCE_MS():Float
function get_renderDistanceMs():Float
{
if (USE_CUSTOM_RENDER_DISTANCE) return CUSTOM_RENDER_DISTANCE_MS;
return FlxG.height / Constants.PIXELS_PER_MS;
if (useCustomRenderDistance) return customRenderDistanceMs;
return FlxG.height / Constants.PIXELS_PER_MS / scrollSpeed;
}
/**
@@ -71,6 +83,9 @@ class Strumline extends FlxSpriteGroup
// Used in-game to control the scroll speed within a song
public var scrollSpeed:Float = 1.0;
/**
* Reset the scroll speed to the current chart's scroll speed.
*/
public function resetScrollSpeed():Void
{
scrollSpeed = PlayState.instance?.currentChart?.scrollSpeed ?? 1.0;
@@ -101,8 +116,16 @@ class Strumline extends FlxSpriteGroup
*/
public var notes:FlxTypedSpriteGroup<NoteSprite>;
/**
* The hold notes currently being rendered on the strumline.
* This group iterates over this every frame to update hold note positions.
* The PlayState also iterates over this to calculate user inputs.
*/
public var holdNotes:FlxTypedSpriteGroup<SustainTrail>;
/**
* A signal that is dispatched when a note is spawned and heading towards the strumline.
*/
public var onNoteIncoming:FlxTypedSignal<NoteSprite->Void>;
var background:FunkinSprite;
@@ -218,6 +241,9 @@ class Strumline extends FlxSpriteGroup
return value;
}
/**
* Refresh the strumline, sorting its children by z-index.
*/
public function refresh():Void
{
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
@@ -241,7 +267,7 @@ class Strumline extends FlxSpriteGroup
#if FEATURE_GHOST_TAPPING
/**
* Returns `true` if no notes are in range of the strumline and the player can spam without penalty.
* @return `true` if no notes are in range of the strumline and the player can spam without penalty.
*/
public function mayGhostTap():Bool
{
@@ -286,31 +312,41 @@ class Strumline extends FlxSpriteGroup
});
}
public function getNoteSprite(noteData:SongNoteData):NoteSprite
/**
* Get a note sprite corresponding to the given note data.
* @param target The note data for the note sprite.
* @return The note sprite.
*/
public function getNoteSprite(target:SongNoteData):NoteSprite
{
if (noteData == null) return null;
if (target == null) return null;
for (note in notes.members)
{
if (note == null) continue;
if (note.alive) continue;
if (note.noteData == noteData) return note;
if (note.noteData == target) return note;
}
return null;
}
public function getHoldNoteSprite(noteData:SongNoteData):SustainTrail
/**
* Get a hold note sprite corresponding to the given note data.
* @param target The note data for the hold note.
* @return The hold note sprite.
*/
public function getHoldNoteSprite(target:SongNoteData):SustainTrail
{
if (noteData == null || ((noteData.length ?? 0.0) <= 0.0)) return null;
if (target == null || ((target.length ?? 0.0) <= 0.0)) return null;
for (holdNote in holdNotes.members)
{
if (holdNote == null) continue;
if (holdNote.alive) continue;
if (holdNote.noteData == noteData) return holdNote;
if (holdNote.noteData == target) return holdNote;
}
return null;
@@ -321,6 +357,8 @@ class Strumline extends FlxSpriteGroup
*/
public function vwooshNotes():Void
{
var vwooshTime:Float = 0.5;
for (note in notes.members)
{
if (note == null) continue;
@@ -331,7 +369,7 @@ class Strumline extends FlxSpriteGroup
var targetY:Float = FlxG.height + note.y;
if (Preferences.downscroll) targetY = 0 - note.height;
FlxTween.tween(note, {y: targetY}, 0.5,
FlxTween.tween(note, {y: targetY}, vwooshTime,
{
ease: FlxEase.expoIn,
onComplete: function(twn) {
@@ -352,7 +390,7 @@ class Strumline extends FlxSpriteGroup
var targetY:Float = FlxG.height + holdNote.y;
if (Preferences.downscroll) targetY = 0 - holdNote.height;
FlxTween.tween(holdNote, {y: targetY}, 0.5,
FlxTween.tween(holdNote, {y: targetY}, vwooshTime,
{
ease: FlxEase.expoIn,
onComplete: function(twn) {
@@ -365,23 +403,65 @@ class Strumline extends FlxSpriteGroup
}
/**
* For a note's strumTime, calculate its Y position relative to the strumline.
* NOTE: Assumes Conductor and PlayState are both initialized.
* @param strumTime
* @return Float
* Reverse of vwooshNotes, we bring the notes IN (by their offsets)
*/
public function calculateNoteYPos(strumTime:Float, vwoosh:Bool = true):Float
public function vwooshInNotes():Void
{
// Make the note move faster visually as it moves offscreen.
// var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 1.0;
// ^^^ commented this out... do NOT make it move faster as it moves offscreen!
var vwoosh:Float = 1.0;
var vwooshTime:Float = 0.5;
return
Constants.PIXELS_PER_MS * (conductorInUse.songPosition - strumTime - Conductor.instance.inputOffset) * scrollSpeed * vwoosh * (Preferences.downscroll ? 1 : -1);
for (note in notes.members)
{
if (note == null) continue;
if (!note.alive) continue;
note.yOffset = 200;
if (Preferences.downscroll)
{
note.yOffset = -200;
}
FlxTween.tween(note, {yOffset: 0}, vwooshTime,
{
ease: FlxEase.expoOut,
onComplete: function(twn) {
note.yOffset = 0;
}
});
}
for (holdNote in holdNotes.members)
{
if (holdNote == null) continue;
if (!holdNote.alive) continue;
holdNote.yOffset = 200;
if (Preferences.downscroll)
{
holdNote.yOffset = -200;
}
FlxTween.tween(holdNote, {yOffset: 0}, vwooshTime,
{
ease: FlxEase.expoOut,
onComplete: function(twn) {
holdNote.yOffset = 0;
}
});
}
}
function updateNotes():Void
/**
* For a note's strumTime, calculate its Y position relative to the strumline.
* NOTE: Assumes Conductor and PlayState are both initialized.
* @param strumTime The strumtime of the note.
* @return The Y position of the note.
*/
public function calculateNoteYPos(strumTime:Float):Float
{
return
Constants.PIXELS_PER_MS * (conductorInUse.songPosition - strumTime - Conductor.instance.inputOffset) * scrollSpeed * (Preferences.downscroll ? 1 : -1);
}
public function updateNotes():Void
{
if (noteData.length == 0) return;
@@ -392,7 +472,7 @@ class Strumline extends FlxSpriteGroup
var songStart:Float = PlayState.instance?.startTimestamp ?? 0.0;
var hitWindowStart:Float = conductorInUse.songPosition - Constants.HIT_WINDOW_MS;
var renderWindowStart:Float = conductorInUse.songPosition + RENDER_DISTANCE_MS;
var renderWindowStart:Float = conductorInUse.songPosition + renderDistanceMs;
for (noteIndex in nextNoteIndex...noteData.length)
{
@@ -407,7 +487,7 @@ class Strumline extends FlxSpriteGroup
}
if (note.time > renderWindowStart) break; // Note is too far ahead to render
var noteSprite = buildNoteSprite(note);
var noteSprite:NoteSprite = buildNoteSprite(note);
if (note.length > 0)
{
@@ -424,12 +504,11 @@ class Strumline extends FlxSpriteGroup
{
if (note == null || !note.alive) continue;
var vwoosh:Bool = note.holdNoteSprite == null;
// Set the note's position.
if (!customPositionData) note.y = this.y - INITIAL_OFFSET + calculateNoteYPos(note.strumTime, vwoosh);
if (!customPositionData) note.y = this.y - INITIAL_OFFSET + calculateNoteYPos(note.strumTime) + note.yOffset;
// If the note is miss
var isOffscreen = Preferences.downscroll ? note.y > FlxG.height : note.y < -note.height;
var isOffscreen:Bool = Preferences.downscroll ? note.y > FlxG.height : note.y < -note.height;
if (note.handledMiss && isOffscreen)
{
killNote(note);
@@ -453,7 +532,8 @@ class Strumline extends FlxSpriteGroup
}
}
var renderWindowEnd = holdNote.strumTime + holdNote.fullSustainLength + Constants.HIT_WINDOW_MS + RENDER_DISTANCE_MS / 8;
final magicNumberIGuess:Float = 8;
var renderWindowEnd:Float = holdNote.strumTime + holdNote.fullSustainLength + Constants.HIT_WINDOW_MS + (renderDistanceMs / magicNumberIGuess);
if (holdNote.missedNote && conductorInUse.songPosition >= renderWindowEnd)
{
@@ -494,17 +574,20 @@ class Strumline extends FlxSpriteGroup
var yOffset:Float = (holdNote.fullSustainLength - holdNote.sustainLength) * Constants.PIXELS_PER_MS;
var vwoosh:Bool = false;
if (!customPositionData)
{
if (Preferences.downscroll)
{
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime, vwoosh) - holdNote.height + STRUMLINE_SIZE / 2;
holdNote.y = this.y
- INITIAL_OFFSET
+ calculateNoteYPos(holdNote.strumTime)
- holdNote.height
+ STRUMLINE_SIZE / 2
+ holdNote.yOffset;
}
else
{
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime, vwoosh) + yOffset + STRUMLINE_SIZE / 2;
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime) + yOffset + STRUMLINE_SIZE / 2 + holdNote.yOffset;
}
}
@@ -544,17 +627,21 @@ class Strumline extends FlxSpriteGroup
{
// Hold note is new, render it normally.
holdNote.visible = true;
var vwoosh:Bool = false;
if (!customPositionData)
{
if (Preferences.downscroll)
{
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime, vwoosh) - holdNote.height + STRUMLINE_SIZE / 2;
holdNote.y = this.y
- INITIAL_OFFSET
+ calculateNoteYPos(holdNote.strumTime)
- holdNote.height
+ STRUMLINE_SIZE / 2
+ holdNote.yOffset;
}
else
{
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime, vwoosh) + STRUMLINE_SIZE / 2;
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime) + STRUMLINE_SIZE / 2 + holdNote.yOffset;
}
}
}
@@ -607,6 +694,9 @@ class Strumline extends FlxSpriteGroup
nextNoteIndex = 0;
}
/**
* Called on each beat of the song.
*/
public function onBeatHit():Void
{
if (notes.members.length > 1) notes.members.insertionSort(compareNoteSprites.bind(FlxSort.ASCENDING));
@@ -614,16 +704,29 @@ class Strumline extends FlxSpriteGroup
if (holdNotes.members.length > 1) holdNotes.members.insertionSort(compareHoldNoteSprites.bind(FlxSort.ASCENDING));
}
/**
* Called when a key is pressed.
* @param dir The direction of the key that was pressed.
*/
public function pressKey(dir:NoteDirection):Void
{
heldKeys[dir] = true;
}
/**
* Called when a key is released.
* @param dir The direction of the key that was released.
*/
public function releaseKey(dir:NoteDirection):Void
{
heldKeys[dir] = false;
}
/**
* Check if a key is held down.
* @param dir The direction of the key to check.
* @return `true` if the key is held down, `false` otherwise.
*/
public function isKeyHeld(dir:NoteDirection):Bool
{
return heldKeys[dir];
@@ -673,6 +776,11 @@ class Strumline extends FlxSpriteGroup
#end
}
/**
* Apply note data from a chart to this strumline.
* Note data should be valid and apply only to this strumline.
* @param data The note data to apply.
*/
public function applyNoteData(data:Array<SongNoteData>):Void
{
this.notes.clear();
@@ -685,6 +793,7 @@ class Strumline extends FlxSpriteGroup
}
/**
* Hit a note.
* @param note The note to hit.
* @param removeNote True to remove the note immediately, false to make it transparent and let it move offscreen.
*/
@@ -716,6 +825,10 @@ class Strumline extends FlxSpriteGroup
#end
}
/**
* Kill a note heading towards the strumline.
* @param note The note to kill. Gets recycled and reused for performance.
*/
public function killNote(note:NoteSprite):Void
{
if (note == null) return;
@@ -730,41 +843,76 @@ class Strumline extends FlxSpriteGroup
}
}
/**
* Get a strumline note sprite by its index.
* @param index The index of the note to get.
* @return The note.
*/
public function getByIndex(index:Int):StrumlineNote
{
return this.strumlineNotes.members[index];
}
/**
* Get a strumline note sprite by its direction.
* @param direction The direction of the note to get.
* @return The note.
*/
public function getByDirection(direction:NoteDirection):StrumlineNote
{
return getByIndex(DIRECTIONS.indexOf(direction));
}
/**
* Play a static animation for a given direction.
* @param direction The direction of the note to play the static animation for.
*/
public function playStatic(direction:NoteDirection):Void
{
getByDirection(direction).playStatic();
}
/**
* Play a press animation for a given direction.
* @param direction The direction of the note to play the press animation for.
*/
public function playPress(direction:NoteDirection):Void
{
getByDirection(direction).playPress();
}
/**
* Play a confirm animation for a given direction.
* @param direction The direction of the note to play the confirm animation for.
*/
public function playConfirm(direction:NoteDirection):Void
{
getByDirection(direction).playConfirm();
}
/**
* Play a confirm animation for a hold note.
* @param direction The direction of the note to play the confirm animation for.
*/
public function holdConfirm(direction:NoteDirection):Void
{
getByDirection(direction).holdConfirm();
}
/**
* Check if a given direction is playing the confirm animation.
* @param direction The direction of the note to check.
* @return `true` if the note is playing the confirm animation, `false` otherwise.
*/
public function isConfirm(direction:NoteDirection):Bool
{
return getByDirection(direction).isConfirm();
}
/**
* Play a note splash for a given direction.
* @param direction The direction of the note to play the splash animation for.
*/
public function playNoteSplash(direction:NoteDirection):Void
{
if (!showNotesplash) return;
@@ -787,6 +935,10 @@ class Strumline extends FlxSpriteGroup
}
}
/**
* Play a note hold cover for a given hold note.
* @param holdNote The hold note to play the cover animation for.
*/
public function playNoteHoldCover(holdNote:SustainTrail):Void
{
if (!showNotesplash) return;
@@ -817,6 +969,11 @@ class Strumline extends FlxSpriteGroup
}
}
/**
* Build a note sprite for a given note data.
* @param note The note data to build the note sprite for.
* @return The note sprite. Will recycle a note sprite from the pool if available for performance.
*/
public function buildNoteSprite(note:SongNoteData):NoteSprite
{
var noteSprite:NoteSprite = constructNoteSprite();
@@ -833,13 +990,17 @@ class Strumline extends FlxSpriteGroup
noteSprite.x += getXPos(DIRECTIONS[note.getDirection() % KEY_COUNT]);
noteSprite.x -= (noteSprite.width - Strumline.STRUMLINE_SIZE) / 2; // Center it
noteSprite.x -= NUDGE;
// noteSprite.x += INITIAL_OFFSET;
noteSprite.y = -9999;
}
return noteSprite;
}
/**
* Build a hold note sprite for a given note data.
* @param note The note data to build the hold note sprite for.
* @return The hold note sprite. Will recycle a hold note sprite from the pool if available for performance.
*/
public function buildHoldNoteSprite(note:SongNoteData):SustainTrail
{
var holdNoteSprite:SustainTrail = constructHoldNoteSprite();
@@ -874,7 +1035,7 @@ class Strumline extends FlxSpriteGroup
}
/**
* Custom recycling behavior.
* Custom recycling behavior for note splashes.
*/
function constructNoteSplash():NoteSplash
{
@@ -908,7 +1069,7 @@ class Strumline extends FlxSpriteGroup
}
/**
* Custom recycling behavior.
* Custom recycling behavior for note hold covers.
*/
function constructNoteHoldCover():NoteHoldCover
{
@@ -942,7 +1103,7 @@ class Strumline extends FlxSpriteGroup
}
/**
* Custom recycling behavior.
* Custom recycling behavior for note sprites.
*/
function constructNoteSprite():NoteSprite
{
@@ -968,7 +1129,7 @@ class Strumline extends FlxSpriteGroup
}
/**
* Custom recycling behavior.
* Custom recycling behavior for hold note sprites.
*/
function constructHoldNoteSprite():SustainTrail
{
@@ -1020,6 +1181,10 @@ class Strumline extends FlxSpriteGroup
FlxTween.tween(arrow, {y: arrow.y + 10, alpha: 1}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * index)});
}
/**
* Play a fade in animation on all arrows in the strumline.
* Used when starting a song in Freeplay mode.
*/
public function fadeInArrows():Void
{
for (index => arrow in this.strumlineNotes.members.keyValueIterator())
@@ -1028,24 +1193,50 @@ class Strumline extends FlxSpriteGroup
}
}
/**
* Compare two note data objects by their strumtime.
* @param order The order to sort the notes in.
* @param a The first note data object.
* @param b The second note data object.
* @return The comparison result, based on the time of the notes.
*/
function compareNoteData(order:Int, a:SongNoteData, b:SongNoteData):Int
{
return FlxSort.byValues(order, a.time, b.time);
}
/**
* Compare two note sprites by their strumtime.
* @param order The order to sort the notes in.
* @param a The first note sprite.
* @param b The second note sprite.
* @return The comparison result, based on the time of the notes.
*/
function compareNoteSprites(order:Int, a:NoteSprite, b:NoteSprite):Int
{
return FlxSort.byValues(order, a?.strumTime, b?.strumTime);
}
/**
* Compare two hold note sprites by their strumtime.
* @param order The order to sort the notes in.
* @param a The first hold note sprite.
* @param b The second hold note sprite.
* @return The comparison result, based on the time of the notes.
*/
function compareHoldNoteSprites(order:Int, a:SustainTrail, b:SustainTrail):Int
{
return FlxSort.byValues(order, a?.strumTime, b?.strumTime);
}
override function findMinYHelper()
/**
* Find the minimum Y position of the strumline.
* Ignores the background to ensure the strumline is positioned correctly.
* @return The minimum Y position of the strumline.
*/
override function findMinYHelper():Float
{
var value = Math.POSITIVE_INFINITY;
var value:Float = Math.POSITIVE_INFINITY;
for (member in group.members)
{
if (member == null) continue;
@@ -1053,18 +1244,28 @@ class Strumline extends FlxSpriteGroup
if (member == this.background) continue;
var minY:Float;
if (member.flixelType == SPRITEGROUP) minY = (cast member : FlxSpriteGroup).findMinY();
if (member.flixelType == SPRITEGROUP)
{
minY = (cast member : FlxSpriteGroup).findMinY();
}
else
{
minY = member.y;
}
if (minY < value) value = minY;
}
return value;
}
override function findMaxYHelper()
/**
* Find the maximum Y position of the strumline.
* Ignores the background to ensure the strumline is positioned correctly.
* @return The maximum Y position of the strumline.
*/
override function findMaxYHelper():Float
{
var value = Math.NEGATIVE_INFINITY;
var value:Float = Math.NEGATIVE_INFINITY;
for (member in group.members)
{
if (member == null) continue;
@@ -1072,9 +1273,14 @@ class Strumline extends FlxSpriteGroup
if (member == this.background) continue;
var maxY:Float;
if (member.flixelType == SPRITEGROUP) maxY = (cast member : FlxSpriteGroup).findMaxY();
if (member.flixelType == SPRITEGROUP)
{
maxY = (cast member : FlxSpriteGroup).findMaxY();
}
else
{
maxY = member.y + member.height;
}
if (maxY > value) value = maxY;
}
+7 -2
View File
@@ -26,6 +26,11 @@ class StrumlineNote extends FunkinSprite
return this.direction;
}
/**
* The Y Offset of the note.
*/
public var yOffset:Float = 0.0;
/**
* Set this flag to `true` to disable performance optimizations that cause
* the Strumline note sprite to ignore `velocity` and `acceleration`.
@@ -52,8 +57,8 @@ class StrumlineNote extends FunkinSprite
setup(noteStyle);
this.animation.callback = onAnimationFrame;
this.animation.finishCallback = onAnimationFinished;
this.animation.onFrameChange.add(onAnimationFrame);
this.animation.onFinish.add(onAnimationFinished);
// Must be true for animations to play.
this.active = true;
+5
View File
@@ -33,6 +33,11 @@ class SustainTrail extends FlxSprite
public var cover:NoteHoldCover = null;
/**
* The Y Offset of the note.
*/
public var yOffset:Float = 0.0;
/**
* Set to `true` if the user hit the note and is currently holding the sustain.
* Should display associated effects.
@@ -66,6 +66,8 @@ class NoteKind implements INoteScriptedClass
public function onNoteHit(event:HitNoteScriptEvent):Void {}
public function onNoteMiss(event:NoteScriptEvent):Void {}
public function onNoteHoldDrop(event:HoldNoteScriptEvent) {}
}
/**
@@ -49,7 +49,7 @@ class NoteKindManager
{
var noteEvent:NoteScriptEvent = cast(event, NoteScriptEvent);
var noteKind:NoteKind = noteKinds.get(noteEvent.note.kind);
var noteKind:NoteKind = noteKinds.get(noteEvent?.note?.kind);
if (noteKind != null)
{
@@ -19,11 +19,6 @@ using funkin.data.animation.AnimationData.AnimationDataUtil;
@:nullSafety
class NoteStyle implements IRegistryEntry<NoteStyleData>
{
/**
* The ID of the note style.
*/
public final id:String;
/**
* Note style data as parsed from the JSON file.
*/
+23 -5
View File
@@ -69,6 +69,19 @@ class Scoring
}
}
public static function getMissScore(scoringSystem:ScoringSystem = PBOT1):Int
{
return switch (scoringSystem)
{
case LEGACY: LEGACY_MISS_SCORE;
case WEEK7: WEEK7_MISS_SCORE;
case PBOT1: PBOT1_MISS_SCORE;
default:
FlxG.log.error('Unknown scoring system: ${scoringSystem}');
0;
}
}
/**
* The maximum score a note can receive.
*/
@@ -92,7 +105,7 @@ class Scoring
/**
* The score a note receives when it is missed.
*/
public static final PBOT1_MISS_SCORE:Int = 0;
public static final PBOT1_MISS_SCORE:Int = -100;
/**
* The threshold at which a note hit is considered perfect and always given the max score.
@@ -228,6 +241,11 @@ class Scoring
*/
public static final LEGACY_SHIT_SCORE:Int = 50;
/**
* The score a note receives when missed.
*/
public static final LEGACY_MISS_SCORE:Int = -10;
static function scoreNoteLEGACY(msTiming:Float):Int
{
var absTiming:Float = Math.abs(msTiming);
@@ -276,6 +294,7 @@ class Scoring
public static final WEEK7_BAD_THRESHOLD:Float = 0.8; // 80% of the hit window, or ~125ms
public static final WEEK7_GOOD_THRESHOLD:Float = 0.55; // 55% of the hit window, or ~91ms
public static final WEEK7_SICK_THRESHOLD:Float = 0.2; // 20% of the hit window, or ~33ms
public static final WEEK7_MISS_SCORE:Int = -10;
public static final WEEK7_SHIT_SCORE:Int = 50;
public static final WEEK7_BAD_SCORE:Int = 100;
public static final WEEK7_GOOD_SCORE:Int = 200;
@@ -363,10 +382,9 @@ class Scoring
// Else, use the standard grades
// Grade % (only good and sick), 1.00 is a full combo
var grade = (scoreData.tallies.sick + scoreData.tallies.good) / scoreData.tallies.totalNotes;
// Clear % (including bad and shit). 1.00 is a full clear but not a full combo
var clear = (scoreData.tallies.totalNotesHit) / scoreData.tallies.totalNotes;
// Final Grade = (Sick + Good - Miss) / (Total Notes)
var grade = (scoreData.tallies.sick + scoreData.tallies.good - scoreData.tallies.missed) / scoreData.tallies.totalNotes;
if (grade == Constants.RANK_PERFECT_THRESHOLD)
{
+20 -23
View File
@@ -65,19 +65,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
*/
public static final DEFAULT_SCROLLSPEED:Float = 1.0;
/**
* The internal ID of the song.
*/
public final id:String;
/**
* Song metadata as parsed from the JSON file.
* This is the data for the `default` variation specifically,
* and is needed for the IRegistryEntry interface.
* Will only be null if the song data could not be loaded.
*/
public final _data:Null<SongMetadata>;
// key = variation id, value = metadata
final _metadata:Map<String, SongMetadata>;
@@ -289,6 +276,14 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
return diff.album ?? '';
}
public function getStickerPackId(diffId:String, variation:String):Null<String>
{
var diff:Null<SongDifficulty> = getDifficulty(diffId, variation);
if (diff == null) return null;
return diff.stickerPack;
}
/**
* Populate the difficulty data from the provided metadata.
* Does not load chart data (that is triggered later when we want to play the song).
@@ -331,6 +326,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
difficulty.difficultyRating = metadata.playData.ratings.get(diffId) ?? 0;
difficulty.album = metadata.playData.album;
difficulty.stickerPack = metadata.playData.stickerPack;
difficulty.stage = metadata.playData.stage;
difficulty.noteStyle = metadata.playData.noteStyle;
@@ -624,13 +620,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
}
}
public function toString():String
{
return 'Song($id)';
}
public function destroy():Void {}
public function onPause(event:PauseScriptEvent):Void {};
public function onResume(event:ScriptEvent):Void {};
@@ -651,6 +640,8 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
public function onNoteMiss(event:NoteScriptEvent):Void {};
public function onNoteHoldDrop(event:HoldNoteScriptEvent) {}
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent):Void {};
public function onSongEvent(event:SongEventScriptEvent):Void {};
@@ -750,6 +741,7 @@ class SongDifficulty
public var difficultyRating:Int = 0;
public var album:Null<String> = null;
public var stickerPack:Null<String> = null;
public function new(song:Song, diffId:String, variation:String)
{
@@ -809,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);
@@ -956,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);
+4 -2
View File
@@ -107,8 +107,8 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
if (this.animation != null)
{
this.animation.callback = this.onAnimationFrame;
this.animation.finishCallback = this.onAnimationFinished;
this.animation.onFrameChange.add(this.onAnimationFrame);
this.animation.onFinish.add(this.onAnimationFinished);
}
}
@@ -371,6 +371,8 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
public function onNoteHit(event:HitNoteScriptEvent) {}
public function onNoteHoldDrop(event:HoldNoteScriptEvent) {}
public function onNoteMiss(event:NoteScriptEvent) {}
public function onSongEvent(event:SongEventScriptEvent) {}
+2 -14
View File
@@ -30,10 +30,6 @@ typedef StagePropGroup = FlxTypedSpriteGroup<StageProp>;
*/
class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements IRegistryEntry<StageData>
{
public final id:String;
public final _data:StageData;
public var stageName(get, never):String;
function get_stageName():String
@@ -859,16 +855,6 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
}
}
public override function toString():String
{
return 'Stage($id)';
}
static function _fetchData(id:String):Null<StageData>
{
return StageRegistry.instance.parseEntryDataWithMigration(id, StageRegistry.instance.fetchEntryVersion(id));
}
public function onScriptEvent(event:ScriptEvent)
{
// Ensure all custom events get broadcast to the elements of the stage.
@@ -901,6 +887,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
public function onNoteMiss(event:NoteScriptEvent) {}
public function onNoteHoldDrop(event:HoldNoteScriptEvent) {}
public function onSongEvent(event:SongEventScriptEvent) {}
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {}
+14 -6
View File
@@ -43,7 +43,7 @@ class Save
{
if (_instance == null)
{
return _instance = load();
return load();
}
return _instance;
}
@@ -55,7 +55,10 @@ class Save
trace("[SAVE] Loading save...");
// Bind save data.
return loadFromSlot(BASE_SAVE_SLOT);
var loadedSave:Save = loadFromSlot(BASE_SAVE_SLOT);
if (_instance == null) _instance = loadedSave;
return loadedSave;
}
public static function clearData():Void
@@ -112,6 +115,7 @@ class Save
zoomCamera: true,
debugDisplay: false,
autoPause: true,
vsyncMode: 'Off',
strumlineBackgroundOpacity: 0,
autoFullscreen: false,
inputOffset: 0,
@@ -730,10 +734,6 @@ class Save
// Directly set the highscore.
setSongScore(songId, difficultyId, newScoreData);
#if FEATURE_NEWGROUNDS
Leaderboards.submitSongScore(songId, difficultyId, newScoreData.score);
#end
return;
}
@@ -987,6 +987,7 @@ class Save
* If you set slot to `2`, it will load an independent save file from slot 2.
* @param slot
*/
@:haxe.warning("-WDeprecated")
static function loadFromSlot(slot:Int):Save
{
trace('[SAVE] Loading save from slot $slot...');
@@ -1128,6 +1129,7 @@ class Save
* @param slot The slot number to check.
* @return Whether the slot is not empty.
*/
@:haxe.warning("-WDeprecated")
static function querySlot(slot:Int):Bool
{
var targetSaveData = new FlxSave();
@@ -1397,6 +1399,12 @@ typedef SaveDataOptions =
*/
var autoPause:Bool;
/**
* If enabled, the game will utilize VSync (or adaptive VSync) on startup.
* @default `Off`
*/
var vsyncMode:String;
/**
* If >0, the game will display a semi-opaque background under the notes.
* `0` for no background, `100` for solid black if you're freaky like that
+4 -2
View File
@@ -18,6 +18,7 @@ import funkin.input.Controls;
* MusicBeatState actually represents the core utility FlxState of the game.
* It includes functionality for event handling, as well as maintaining BPM-based update events.
*/
@:nullSafety
class MusicBeatState extends FlxTransitionableState implements IEventHandler
{
var controls(get, never):Controls;
@@ -25,8 +26,8 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
inline function get_controls():Controls
return PlayerSettings.player1.controls;
public var leftWatermarkText:FlxText = null;
public var rightWatermarkText:FlxText = null;
public var leftWatermarkText:Null<FlxText> = null;
public var rightWatermarkText:Null<FlxText> = null;
public var conductorInUse(get, set):Conductor;
@@ -163,6 +164,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
}
@:nullSafety(Off)
override function startOutro(onComplete:() -> Void):Void
{
var event = new StateChangeScriptEvent(STATE_CHANGE_BEGIN, null, true);
+4 -2
View File
@@ -16,10 +16,11 @@ import funkin.input.Controls;
/**
* MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState.
*/
@:nullSafety
class MusicBeatSubState extends FlxSubState implements IEventHandler
{
public var leftWatermarkText:FlxText = null;
public var rightWatermarkText:FlxText = null;
public var leftWatermarkText:Null<FlxText> = null;
public var rightWatermarkText:Null<FlxText> = null;
public var conductorInUse(get, set):Conductor;
@@ -186,6 +187,7 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler
this._parentState.openSubState(substate);
}
@:nullSafety(Off)
override function startOutro(onComplete:() -> Void):Void
{
var event = new StateChangeScriptEvent(STATE_CHANGE_BEGIN, null, true);
+2 -1
View File
@@ -6,6 +6,7 @@ import funkin.graphics.FlxFilteredSprite;
* The icon that gets used for Freeplay capsules and char select
* NOT to be confused with the CharIcon class, which is for the in-game icons
*/
@:nullSafety
class PixelatedIcon extends FlxFilteredSprite
{
public function new(x:Float, y:Float)
@@ -28,7 +29,7 @@ class PixelatedIcon extends FlxFilteredSprite
charPath += "monsterpixel";
case "mom" | "mom-car":
charPath += "mommypixel";
case "pico-blazin" | "pico-playable" | "pico-speaker":
case "pico-blazin" | "pico-playable" | "pico-speaker" | "pico-pixel" | "pico-holding-nene":
charPath += "picopixel";
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen":
charPath += "gfpixel";
+3
View File
@@ -3,6 +3,7 @@ package funkin.ui;
import funkin.ui.AtlasText;
import funkin.ui.MenuList;
@:nullSafety
class TextMenuList extends MenuTypedList<TextMenuItem>
{
public function new(navControls:NavControls = Vertical, ?wrapMode)
@@ -18,6 +19,7 @@ class TextMenuList extends MenuTypedList<TextMenuItem>
}
}
@:nullSafety
class TextMenuItem extends TextTypedMenuItem<AtlasText>
{
public function new(x = 0.0, y = 0.0, name:String, font:AtlasFont = BOLD, ?callback:Void->Void)
@@ -27,6 +29,7 @@ class TextMenuItem extends TextTypedMenuItem<AtlasText>
}
}
@:nullSafety
class TextTypedMenuItem<T:AtlasText> extends MenuTypedItem<T>
{
public function new(x = 0.0, y = 0.0, label:T, name:String, ?callback:Void->Void)
@@ -54,20 +54,6 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
doFade(animInInfo);
default:
}
#if FEATURE_DEBUG_FUNCTIONS
if (FlxG.keys.justPressed.J)
{
alpha = 1;
x = y = 0;
fadingStatus = FADE_OUT;
}
if (FlxG.keys.justPressed.K)
{
alpha = 0;
fadingStatus = FADE_IN;
}
#end
}
public function onStepHit(event:SongTimeScriptEvent):Void {}
@@ -425,6 +425,7 @@ class CharSelectSubState extends MusicBeatSubState
else
{
#if FEATURE_NEWGROUNDS
// Make the character unlock medal retroactive.
if (availableChars.size() > 1) Medals.award(CharSelect);
#end
@@ -497,7 +498,7 @@ class CharSelectSubState extends MusicBeatSubState
grpIcons.scrollFactor.set();
}
function unLock()
function unLock():Void
{
var index = nonLocks[0];
@@ -570,6 +571,7 @@ class CharSelectSubState extends MusicBeatSubState
});
#if FEATURE_NEWGROUNDS
// Grant the medal when the player unlocks a character.
Medals.award(CharSelect);
#end
+4 -11
View File
@@ -18,9 +18,6 @@ class Nametag extends FlxSprite
shader = mosaicShader;
switchChar("bf");
FlxG.debugger.addTrackerProfile(new TrackerProfile(Nametag, ["midpointX", "midpointY"]));
FlxG.debugger.track(this, "Nametag");
}
public function updatePosition():Void
@@ -71,16 +68,12 @@ class Nametag extends FlxSprite
}
}
function setBlockTimer(frame:Int, ?forceX:Float, ?forceY:Float)
function setBlockTimer(frame:Int, ?forceX:Float, ?forceY:Float):Void
{
var daX:Float = 10 * FlxG.random.int(1, 4);
var daY:Float = 10 * FlxG.random.int(1, 4);
var daX:Float = forceX ?? 10 * FlxG.random.int(1, 4);
var daY:Float = forceY ?? 10 * FlxG.random.int(1, 4);
if (forceX != null) daX = forceX;
if (forceY != null) daY = forceY;
new FlxTimer().start(frame / 30, _ -> {
FlxTimer.wait(frame / 30, () -> {
mosaicShader.setBlockSize(daX, daY);
});
}
+1 -1
View File
@@ -123,7 +123,7 @@ class DebugMenuSubState extends MusicBeatSubState
function testStickers()
{
openSubState(new funkin.ui.transition.StickerSubState({}));
openSubState(new funkin.ui.transition.stickers.StickerSubState({}));
trace('opened stickers');
}
@@ -528,8 +528,11 @@ class DebugBoundingState extends FlxState
trace('Added ${offsetAnimationDropdown.dataSource.size} to HaxeUI dropdown');
offsetAnimationDropdown.onChange = function(event:UIEvent) {
trace('Selected animation ${event?.data?.id}');
playCharacterAnimation(event.data.id, true);
if (event.data != null)
{
trace('Selected animation ${event.data.id}');
playCharacterAnimation(event.data.id, true);
}
}
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
@@ -569,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);
}
}
@@ -22,9 +22,9 @@ class FlxAnimateTest extends MusicBeatState
{
super.create();
sprite = new FlxAtlasSprite(0, 0, 'assets/images/charSelect/maskTest');
sprite = new FlxAtlasSprite(-100, 0, 'week3:assets/week3/images/philly/erect/picoFlowers');
add(sprite);
sprite.playAnimation(null, false, false, true);
sprite.playAnimation(sprite.anim.stageInstance.symbol.name, false, false, true);
}
public override function update(elapsed:Float):Void
@@ -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;
}
@@ -2361,7 +2356,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
}
}
if (!FileUtil.doesFileExist(chartPath))
if (!FileUtil.fileExists(chartPath))
{
trace('Previously loaded chart file (${chartPath.toString()}) does not exist, disabling link...');
menuItemRecentChart.disabled = true;
@@ -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.');
@@ -5118,6 +5113,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
if (songPos < 0) songPosMinutes = '-' + songPosMinutes;
var songPosString:String = '${songPosMinutes}:${songPosSeconds}.${songPosMilliseconds}';
var roundedBeat = FlxMath.roundDecimal(Conductor.instance.currentBeatTime, 2);
var parts:Array<String> = Std.string(roundedBeat).split('.');
if (parts.length == 1) parts.push('00');
else if (parts[1].length < 2) parts[1] += '0';
songPosString += ' | Beat: ${parts.join('.')} | Step: ${Conductor.instance.currentStep}';
if (playbarSongPos.value != songPosString) playbarSongPos.value = songPosString;
var songRemaining:Float = Math.max(songLengthInMs - songPos, 0.0);
@@ -5837,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
@@ -106,7 +108,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
}
}
if (!FileUtil.doesFileExist(chartPath))
if (!FileUtil.fileExists(chartPath))
{
trace('Previously loaded chart file (${chartPath}) does not exist, disabling link...');
linkRecentChart.disabled = true;

Some files were not shown because too many files have changed in this diff Show More