1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-08-30 10:25:00 +00:00

Compare commits

...

285 commits

Author SHA1 Message Date
Lasercar 909dfddc63
Merge 8a79873535 into 54ad34e5d0 2025-08-28 19:27:13 -05:00
MAJigsaw77 54ad34e5d0 Fix the app icon not working on Linux. 2025-08-27 14:57:20 -07:00
Cameron Taylor f6fb11de4c hardcode 'touch here to play' graphic to be scaled at 0.5x instead FUNK-2710 2025-08-28 03:25:53 +08:00
MoonDroid 83aa45a318 4x4 Compression for all of pico's results animations 2025-08-28 01:19:11 +08:00
MoonDroid 65186a336d Update Mobile build number 2025-08-28 00:40:24 +08:00
Kolo 3a41b696d3 Ugh, I'm NEW 2025-08-27 23:19:36 +08:00
Hundrec 9c08a6ce68 Refactor alt inst checks in song scripts 2025-08-27 23:18:32 +08:00
EliteMasterEric 62650836b8 Fix a busted Pico metadata JSON (why wasn't there an error?) 2025-08-27 23:18:32 +08:00
EliteMasterEric e4fdf1019a Explicitly specify the variation for a bunch of song requests to fix some bugs 2025-08-27 23:18:32 +08:00
Hundrec 9dfda77d1e Bump submod for a few regressions in song scripts 2025-08-27 21:12:41 +08:00
Hyper_ fe6c8cabd6 the living dead capsules 😱 2025-08-27 15:19:56 +08:00
Hundrec 8b8d249813 Bump submod for New song labels 2025-08-25 22:54:31 +08:00
MAJigsaw77 c6ee23fec2 Add back the callbackutil initialization oops. 2025-08-24 20:06:04 +08:00
MAJigsaw77 345d96a7bc Oops 2025-08-23 23:32:33 -05:00
MAJigsaw77 7f7fc3fa90 Prevent re-initialization of specific core features.
Co-Authored-By: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-23 23:32:33 -05:00
Abnormal 7c17e72e63 add proper blend modes to the freeplay djs (now with 100% less black lines) 2025-08-24 01:52:59 +08:00
Hundrec 1d2f971a26 Bump submod to fix Tankman not fading 2025-08-24 01:26:52 +08:00
Hundrec 7d97bb525a Bump submod to fix Darnell Erect isSongNew check 2025-08-24 01:24:21 +08:00
MAJigsaw77 da74f782d3 Fix censored cutscenes. 2025-08-23 20:06:51 +08:00
EliteMasterEric 107ef7c83f Bump polymod 2025-08-23 11:15:21 +03:00
Hundrec 0412d865f1 Fix later notes being hit before earlier ones
Bug introduced in 0.6.3 by this commit: c41c800998
2025-08-22 11:47:36 -05:00
Hundrec 50e114fc12 Bump submod for gradient optimization
Co-Authored-By: Mario Hernando <97055307+maybemaru@users.noreply.github.com>
2025-08-22 16:13:07 +08:00
MAJigsaw77 ce6577a9ed Update astc-compressor again. 2025-08-21 18:33:00 -05:00
MAJigsaw77 7fbe7b55c1 Update astc-compressor. 2025-08-21 18:33:00 -05:00
MoonDroid fb649c5d48 Exclude week 2 stairs from compression 2025-08-21 18:33:00 -05:00
MoonDroid 0ae8d62520 Scripts to organize astc data because its gettin' big and i love alphabetically sorting everything 2025-08-21 18:33:00 -05:00
MoonDroid 5b34446469 Updaet astc exclusion list so things would look better!! 2025-08-21 18:33:00 -05:00
MaybeMaru 8d1bfa105c Update PureColor.hx 2025-08-21 18:30:01 -05:00
Hyper_ a2e9277851 the swappening part 2 2025-08-21 23:14:09 +03:00
Hundrec 2425054940 Clamp clear percent to avoid floating point errors
A clear percent higher than 100% would play the rank slam animation of a pink P over an E, but the P would immediately revert to an E.
This change makes sure any values above 100% become exactly 100%.

Update Scoring.hx

Update Scoring.hx

Co-Authored-By: Kolo <67389779+koloindacrib@users.noreply.github.com>
2025-08-22 00:02:53 +08:00
Abnormal 2bbfbc8968 NOTE TO ERIC: typedefs are not classes. These should be fine to remove since I don't think you can import them anyway. 2025-08-21 09:51:01 -05:00
EliteMasterEric 5922b927b7 Add backwards compatibility for some classes that got moved. 2025-08-21 09:51:01 -05:00
EliteMasterEric ebc5133062 Rename dialogue registry classes to make them build properly on Windows 2025-08-21 09:51:01 -05:00
Abnormal 7e2094070c make entry params typed woohoo!!! 2025-08-21 09:51:01 -05:00
Abnormal 20187540b3 fix a null-safey compilation error 2025-08-21 09:51:01 -05:00
Abnormal c94d6bbb66 feat: Variation-specific song scripts 2025-08-21 09:51:01 -05:00
EliteMasterEric 3d8b9a18b3 Fixed an issue where legacy note data wasn't returning the right note kind. 2025-08-21 22:32:39 +08:00
EliteMasterEric f428f5860d Fix an issue where some formats of legacy note data weren't parsed correctly. 2025-08-21 22:32:39 +08:00
AwesomezPro99 2645d56514 Fixed Legacy Chart Scroll Speed 2025-08-21 22:32:39 +08:00
Burgerballs 3f5ef4abe0 Camera bop offset 2025-08-20 23:41:32 -04:00
anysad 75d8c5cc7a fix: fixing the fix 2025-08-20 16:41:27 -04:00
Hundrec 9c7dded259 Bump submod for two more charts 2025-08-20 20:11:01 +08:00
MAJigsaw77 6a564905c0 Small adjustments to prints. 2025-08-20 17:04:05 +07:00
MAJigsaw77 8a3d36d3fc Dont print additional trailing slash. 2025-08-20 17:04:05 +07:00
MAJigsaw77 de9e30534e Use Xml instead of a string array for the mods provider on Android. 2025-08-20 17:04:05 +07:00
MAJigsaw77 d42be225ca Remove unnecessary permissions removal on Android. 2025-08-20 17:04:05 +07:00
Hundrec f7af64111c
Offsets and notekinds for Pico animations
Includes hey, cheer, cock, and shoot
For Pico (Dark), hey and cheer

Adds hey and cheer to both

Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-20 16:59:24 +07:00
Hundrec 069bebb127
Fix duplicate gasp in Pico Doppelganger cutscene
Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-20 16:26:14 +07:00
Hundrec da8609f1d9
Adjust note splash offsets
Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-20 15:59:10 +07:00
MAJigsaw77 a11ae53c51 Check if the host platform is Windows. 2025-08-20 05:43:48 +08:00
MaybeMaru 1d22e073a2 Disable blue shader when unnecessary 2025-08-20 05:30:24 +08:00
MaybeMaru 9da770f6cd oop 2025-08-20 05:30:24 +08:00
MaybeMaru d414a4974a Mobile fix 2025-08-20 05:30:24 +08:00
MaybeMaru 6f193c4bd8 A lot of stuff 2025-08-20 05:30:24 +08:00
Cameron Taylor 520f7fbe68 create ConsoleMacro to allow easy initialization of classes into flixels console 2025-08-19 16:05:08 -04:00
Cameron Taylor aaa722abcf change debug_dumpSave -> debug_dumpSaveJsonSave 2025-08-19 16:05:08 -04:00
anysad f878624f44 fix: no more playtesting while dialog is open 2025-08-19 15:43:30 -04:00
Hundrec 355ea561fa Fix lighting on DJ turntable lights
Finally.

Co-Authored-By: johfy <85341402+johferson@users.noreply.github.com>
2025-08-19 20:45:03 +08:00
Cameron Taylor 7f6c0d56cd fix options menu being able to be pressed during fade 2025-08-19 19:28:23 +07:00
Cameron Taylor a21eace70c fix main menu spam when FlxTransition stuff ends 2025-08-19 19:28:23 +07:00
anysad 47813eac31 fix: oopsie! no more big ranks 2025-08-19 00:10:26 -05:00
Cameron Taylor 943afe2ffc remove mconsole, mcover, mockatoo, and munit from hmm 2025-08-19 04:53:39 +08:00
Hundrec 89127b2b00 Fix Week 3 Erect script error
Co-Authored-By: Rozebud <35246975+thatrozebuddude@users.noreply.github.com>
2025-08-19 04:45:34 +08:00
MoonDroid aab1330531 chore: Use .set() instead of getting a new instance of flxpoint, also add an if statement for better clarity!
woo!
2025-08-18 15:16:00 -05:00
PurSnake 8bab6196e5 RESPECT 2025-08-18 15:16:00 -05:00
Hundrec 4e183a4dc9 Additional 0.7.4 charting changes
1. Blazin' - fixed an incorrect rhythm
2. South Erect - fixed minor misalignments
3. Cocoa Erect - added hey animation
4. Eggnog (Pico Mix) - fixed minor misalignments
2025-08-19 04:12:07 +08:00
Hundrec ec2cc44cc3 Revert "Fix events still moving when not moved"
This reverts commit 45ba0cb7a6.
2025-08-17 16:38:03 -05:00
Hundrec 663987fbcd Change Monster and Winter Horrorland to Vol 2 2025-08-18 04:39:57 +08:00
Lasercar 172041d4f7 chart editor context menu solitaire fix 2025-08-17 13:47:41 -05:00
Hundrec 422da38662 Add min scale to SetHealthIcon event 2025-08-17 13:44:27 -05:00
Lasercar 9b81ebc4f2 The default events now have minimum values
lol, lmao, imagine coding a input to have minimum values but just.. not setting a value?
2025-08-17 13:44:27 -05:00
Lasercar 0b9ad0905e Toggle both hitsound audio volumes to 0 2025-08-17 13:41:05 -05:00
Lasercar 1e1b5643a8 Audio playback volume toggle hotkeys
Shift + M for metronome
Shift + H for hitsounds
Shift + I for instrumental
Shift + V for vocals
Shift + B for Boyfriend vocals
Shift + D for Dad vocals
2025-08-17 13:41:05 -05:00
Lasercar 45ba0cb7a6 Fix events still moving when not moved 2025-08-17 13:34:27 -05:00
Lasercar b1406e07f5 Mention that this prevents the issue from happening 2025-08-17 13:34:27 -05:00
Lasercar 4b655879f7 Don't move the notes if they weren't actually moved 2025-08-17 13:34:27 -05:00
JVN-Pixels fabfaa3a9c Fix Typo In MusicBeatState 2025-08-17 13:26:57 -05:00
Hundrec 6d5249df79 Bump submod for charter field
Co-Authored-By: Hyper_ <40342021+nothyper-474@users.noreply.github.com>
2025-08-18 02:03:37 +08:00
Hyper_ 23b163cb69 Charter for new chart metadata dialog 2025-08-18 02:03:37 +08:00
Karim Akra 1b84c21c36 Remove "accidentally" leftover math on the conductor 2025-08-18 01:53:50 +08:00
Hundrec a87eb25765 Bump submod to fix Nene freezing
Co-Authored-By: Hyper_ <40342021+nothyper-474@users.noreply.github.com>
2025-08-17 09:59:01 +08:00
cyn0x8 df67b642a8 chart editor song id option + manifest id fix 2025-08-17 09:46:41 +08:00
Hundrec 6eefe9e31e Fix "Starting BPM" text being tiny 2025-08-17 09:40:04 +08:00
Hundrec fea34c4a7b Bring back Pause Menu offset adjustment for now 2025-08-16 17:11:24 -07:00
sector-a 10e8cbbac3 Fix pixel icon search 2025-08-17 07:12:51 +08:00
MAJigsaw77 37812ae2f8 Add isWindows check back. 2025-08-17 05:22:12 +08:00
MAJigsaw77 acb1930d7e Make the unicode check acually work. 2025-08-17 05:22:12 +08:00
Hundrec b98095f8aa Fix the Chart Editor character switcher whoopsie
A public PR made it no longer selectable

Co-Authored-By: Kolo <67389779+koloindacrib@users.noreply.github.com>
2025-08-16 15:38:47 -05:00
Karim Akra 724992d3b3 Prevent the conductor from doing excessive nromal updates 2025-08-17 03:45:35 +08:00
Hundrec 89e07dac76 Add 2 sec fade and shorten timer to 37.5 sec
Co-Authored-By: Abnormal <86753001+AbnormalPoof@users.noreply.github.com>
2025-08-17 03:37:38 +08:00
EliteMasterEric 2fb973049a Tweak a couple clamp calls 2025-08-17 03:37:38 +08:00
EliteMasterEric dc20586e95 Switch from random weights to cycling, and remove Gameshark. 2025-08-17 03:37:38 +08:00
EliteMasterEric ccb4c03b85 Implement new videos for attract state. 2025-08-17 03:37:38 +08:00
EliteMasterEric f2732cb253 Rework attract state to play multiple videos. 2025-08-17 03:37:38 +08:00
Abnormal bf2b1af185 cut down senpai's length by 8 seconds like how it was in legacy 2025-08-17 02:23:22 +08:00
MaybeMaru a2b347e31e Improve note batching 2025-08-15 12:27:53 -05:00
Kolo 4317f129e7 fuuuuuuuucckkkkkkkkkkkkkkkkkkk 2025-08-15 10:00:50 -05:00
VioletSnowLeopard 51eec4522e ensure song starts at starting bpm
works when start time isn't zero
2025-08-15 16:59:21 +03:00
Hundrec 21c16c3a50 Adjust a few notekind names 2025-08-15 16:38:33 +03:00
Lasercar b7a5761730 Add missing note kinds
Also clarifies that ugh and pretty good are for tankman only
2025-08-15 16:38:33 +03:00
unknown ff09cb88cd use System.totalMemoryNumber 2025-08-15 16:27:00 +03:00
JVN-Pixels d1a96f791f Home + End Keybinds - Story Mode 2025-08-15 16:24:28 +03:00
Hundrec 84bafa43b7 Remove extra line 2025-08-15 16:08:35 +03:00
Lasercar 223e837fe8 Character icon clickable in playstate fix 2025-08-15 16:08:35 +03:00
Kolo 50c601a7f0 FOUND media 2025-08-15 06:16:45 +08:00
Kolo f870ed2858 don't register classes twice silly 2025-08-15 06:13:02 +08:00
Hundrec f1d6906616 Remove laginator because song restarting fix works 2025-08-15 05:31:04 +08:00
Karim Akra aec3c646d5 make sure the conductor's lerp doesn't exceed the resync threshold 2025-08-15 05:31:04 +08:00
Hyper_ e7f7f4794f oops, you forgot this! 2025-08-14 16:16:58 -05:00
Cameron Taylor 517eb4eb48 input util functions for less verbose key combos 2025-08-14 14:34:55 -04:00
Hundrec 8cba81d207 Add nene-tankmen and apply shaders to A-Bot
Co-Authored-By: sector-a <82838084+sector-a@users.noreply.github.com>
2025-08-14 22:02:32 +08:00
Cameron Taylor 5831852edf use x and o as placeholder cross and checkmark 2025-08-13 19:50:33 -07:00
Cameron Taylor 114bb37c7f some unicode/utf windows fixies 2025-08-13 19:50:33 -07:00
Cameron Taylor 1db75cc55e use different unicode for checkmark and X 2025-08-13 19:50:33 -07:00
Cameron Taylor 248516244c string sort the feature flags 2025-08-13 19:50:33 -07:00
Cameron Taylor b108c0fd95 render feature flags in the compile debug into cute table 2025-08-13 19:50:33 -07:00
Cameron Taylor 2a804f29fd rework main menu a lot more to use our state machine stuff 2025-08-13 19:49:21 -07:00
Cameron Taylor b03828adf3 implement more verbose state machine to handle main menu navigation 2025-08-13 19:49:21 -07:00
Abnormal 657a25996b use params 2025-08-13 13:15:26 -04:00
Abnormal 842ec2c1cd feat: Scoped Modules 2025-08-13 13:15:26 -04:00
EliteMasterEric 4268462d90 Bump... but real? 2025-08-13 11:53:49 -05:00
Hundrec 031007992a Revert "Prevent the song from drifting away after a lagspike"
This reverts commit 7fd74edf2b.
2025-08-13 12:46:49 -04:00
Hundrec cea7db410c Nudge Stress audio files 23ms earlier 2025-08-13 11:40:08 -05:00
anysad 0e181877cf typo fix: notes are not props! 2025-08-14 00:23:46 +08:00
Hundrec ef1216de46 Add comment to clarify double note in offset menu 2025-08-13 11:09:08 -05:00
Eric b4b2e8da9d
Freeplay Favorites now separated by variation (#1575)
Absolute Cinema

Co-authored-by: Kolo <67389779+KoloInDaCrib@users.noreply.github.com>
2025-08-14 00:01:10 +08:00
JackXson-Real d95f239dfb fix mobile finally(?) 2025-08-13 14:35:28 +08:00
Karim Akra 495690f41c
Don't use any lerping on the conductor when the song isn't playing! 2025-08-13 10:47:47 +07:00
Kolo b166cd9a7e save the goddamn changes graahhhhhh 2025-08-12 20:18:56 -05:00
Kolo 7f82b48e3f whatever. go my destroy() override 2025-08-12 20:18:56 -05:00
JackXson-Real 1fc611d130 fix main menu scrolling during transition 2025-08-13 05:35:33 +08:00
Kolo 57a276d17c lasercar nagged me to move the change somewhere else 2025-08-13 02:06:17 +08:00
Kolo 9b1b22c9d5 i literally had to set the holdNoteSprite to null that's all 2025-08-13 02:06:17 +08:00
MaybeMaru f6de4c54d3 Small optimizations 2025-08-12 12:49:54 -05:00
PurSnake e3aceacb59 one commit - one condition in changeDiff
welp, its fully done
i hope
💯
2025-08-12 23:27:33 +08:00
PurSnake 546375087c make albumRoll great again season 2 2025-08-12 23:27:33 +08:00
PurSnake c093e0ca1c make albumRoll great again 2025-08-12 23:27:33 +08:00
Abnormal 7c6efba577 bump submod again FUUUUUUUUUUUUUUUUUUUUUUUUUCK 2025-08-12 15:56:39 +08:00
MoonDroid 6f5e3a50d2 Embarrassing update flixel ref 2025-08-11 21:35:41 -04:00
MoonDroid 2d0d5f8a2e For some reason if you don't do this the saved playbackrate becomes inconsistent :( 2025-08-11 21:35:41 -04:00
MoonDroid 826249f78c Update hmm flixel ref 2025-08-11 21:35:41 -04:00
MoonDroid 0af4c82723 Change floor to round for leftover pitch/playbackRate rounding 2025-08-11 21:35:41 -04:00
MoonDroid c16e45049b chore: Switch FlxMath.clamp calls to .clamp instead 2025-08-11 21:35:41 -04:00
MoonDroid ae00e14a5b chore: Remove useless line from WelcomeDialog 2025-08-11 21:35:41 -04:00
MoonDroid 42a9c43127 chore: Switch all FlxMath.bound calls to .clamp calls instead 2025-08-11 21:35:41 -04:00
Cameron Taylor 17ece1423a don't log "starting prebuild tasks" 2025-08-11 18:26:50 -07:00
Abnormal 6540faefdd update assets submodule with the latest changes 2025-08-12 06:57:28 +08:00
Hundrec eb24d4d19e Polish all the charts
Makes various changes to each chart:
* Adjust the length of every hold note trail to match the vocals as closely as possible, across all variations and difficulties
* Revise Easy and Normal charts to be more consistent with the player's chart (from Week 1 to Week 3)
* Fix a handful of charting errors reported on GitHub with Cory
* Fix even more charting errors not reported on GitHub
* Additional bits to make these the most polished charts yet
2025-08-11 09:58:25 -07:00
Kolo 01a2458c12 force the tween graaahhhhhhhhh 2025-08-11 11:37:06 -05:00
Abnormal 38e7e230d8 bump hscript to fix an issue with final variables 2025-08-10 01:13:41 +08:00
Mikolka 84f119036b Update hmm.json 2025-08-10 00:47:51 +08:00
Abnormal 74a783c3db bump assets 2025-08-09 02:41:54 +08:00
Abnormal 09d5926455 bump polymod hscript and flxanimate woohoo!!!!!!!! 2025-08-09 02:31:49 +08:00
sector-a 5f8b7787cb Remove a left-over trace 2025-08-09 01:34:15 +08:00
Cameron Taylor a7156978a1 small regex change to checkstyle to allow leading underscores (_instance is now an allowed variable name) 2025-08-07 13:29:44 -07:00
Hundrec 65349f4714
Trim env lines to fix issues with CRLF line endings
Co-authored-by: Hyper_ <40342021+NotHyper-474@users.noreply.github.com>
Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-08 01:04:58 +07:00
MoonDroid 93ad7e8932 Update exclusions to ASTC textures 2025-08-08 01:04:18 +07:00
Karim Akra df388dfaa6 why are we configuring assets before ASTC?? 2025-08-08 01:04:18 +07:00
Karim Akra c142add8a1 fixed an oopsie with exclusions 2025-08-08 01:04:18 +07:00
Karim Akra a75c41b813 Update astc-compressor!! 2025-08-08 01:04:18 +07:00
Karim Akra 7fd74edf2b Prevent the song from drifting away after a lagspike 2025-08-08 01:02:55 +07:00
Karim Akra 44370c2653 Fix chromebooks using mobile layout! 2025-08-07 09:56:03 -05:00
Hyper_ 1fbb7604dd nuh uh *gives back ability to fullscreen* 2025-08-06 15:30:15 -07:00
Hundrec 0e52744718
Fix visual bugs when selecting between two hold note trails in Chart Editor
Co-authored-by: Kolo <67389779+KoloInDaCrib@users.noreply.github.com>
2025-08-06 15:26:24 -07:00
PurSnake feb55b2625 stuff 2025-08-06 11:47:10 -05:00
Hundrec 4341a53486 Revert turntable lights changes 2025-08-05 16:21:05 -05:00
Karim Akra 0566cd3e58 get this out of here at once 2025-08-06 01:02:34 +08:00
Karim Akra a2f7fa9ef4 Fixed the lerp speed when changing playback rate 2025-08-06 01:02:34 +08:00
Karim Akra 3a30b681b6 tweaked the ease ratio 2025-08-06 01:02:34 +08:00
Karim Akra 0bd4ce4c97 Adjusted the lerp ratio to work better on lower FPS & added threshold for normal conductor updates 2025-08-06 01:02:34 +08:00
Karim Akra df0d977e6c Adjust the conductor arguments for smoother notes scrolling 2025-08-06 01:02:34 +08:00
Lasercar 1df0cb95f6 Fix onNoteHoldDrop logic
Co-Authored-By: Lasercar <64717068+Lasercar@users.noreply.github.com>
2025-08-05 15:00:01 +03:00
MAJigsaw77 ce83e962bf Fix swapped args oops. 2025-08-05 01:14:22 -04:00
MAJigsaw77 fb293ebcd2 Add a custom ApplicationMain to enable some winapi stuff. 2025-08-05 01:14:22 -04:00
MAJigsaw77 bb7b00cf77 Replace the application popup. 2025-08-05 01:14:22 -04:00
MAJigsaw77 e16391a05c Add winapi stuff. 2025-08-05 01:14:22 -04:00
Abnormal 5aba4b54a5 Fix some warnings/crashes in Week 6 2025-08-05 06:14:42 +08:00
Eric cb611f77a0 Update documentation in FreeplayDJ.hx 2025-08-05 06:04:08 +08:00
Abnormal b5345e1da2 lol we forgot 2025-08-05 05:54:22 +08:00
Abnormal 8d6590e765 bump art submod 2025-08-05 05:09:29 +08:00
Abnormal e39b32bab0
Merge pull request #1530 from FunkinCrew/cool-merging 2025-08-04 15:42:34 -05:00
Hundrec d5754c5da2
Merge public docs changes
Merge main into develop because it would be so awesome
2025-08-05 04:17:09 +08:00
Hundrec fbe83be936
Merge branch 'develop' into cool-merging 2025-08-05 04:15:55 +08:00
Abnormal 8d14df77ff bump the submod wooHOOOOOOOOO 2025-08-05 03:49:07 +08:00
Herman Khomich 55358463f8 Remove repeated creation of strumlineScale in a row / Strumline.hx 2025-08-04 14:21:26 -05:00
Abnormal 4ea99656f2 no more PROPER TURNTABLE LIGHTS FIX *vine boom* 2025-08-05 01:58:10 +08:00
Hyper_ 34ce13c5df chore: Add null safety to PlayState and LoadingState 2025-08-05 01:18:53 +08:00
Abnormal 1d8679941e fix tankman variant names 2025-08-04 20:12:22 +03:00
Abnormal 086f28fef1
fix invisible pixels on some sprites
Co-authored-by: MAJigsaw77 <77043862+MAJigsaw77@users.noreply.github.com>
2025-08-04 19:51:50 +03:00
Abnormal fb4967c94b
fix the pico blazin death confirm offsets
Co-authored-by: MAJigsaw77 <77043862+MAJigsaw77@users.noreply.github.com>
2025-08-04 19:46:24 +03:00
Abnormal 293b70bbed update the submod 2025-08-04 19:31:56 +03:00
Abnormal 64f4620b5a dude........fuck yes. 2025-08-04 19:31:56 +03:00
JackXson-Real 9c1e22309b decrease preview volume from 80% to 70% 2025-08-03 15:59:12 -05:00
JackXson-Real 4ecef6db74 increase preview volume to 80% 2025-08-03 15:59:12 -05:00
Kolo b128741075 mb gang 2025-08-03 15:49:36 -05:00
AwesomezPro99 86e69cfbd1 Fixed Window Title When Pressing F4 2025-08-03 15:36:26 -05:00
Karim Akra aa8690fce3
Merge pull request #1489 from FunkinCrew/pr-5565/SrtHero278/pointer-fixes
[PUBLIC PR] Properly add the touch pointer on playstate
2025-08-03 17:45:12 +03:00
MrMadera e281bdddca south button now works! 2025-08-03 21:29:29 +08:00
MoonDroid 19fdda7cd9 fix: Call Math.round and FlxMath.clamp for better readability and slightly better functionality
what a mouthful :(
2025-08-03 15:54:16 +03:00
MoonDroid d72b989bf5 Delete useless line 2025-08-03 15:54:16 +03:00
Lasercar cd99905bdf Fix volumes being reset to 100% after playtest 2025-08-03 15:54:16 +03:00
Lasercar 3f82f3792b Set playspeed after variation load 2025-08-03 15:54:16 +03:00
Lasercar 8a79873535 Right click select
Can deselect selected sprite with control
Can right click on selection mode and current selection labels
Ctrl Deez None!
Current selection text no longer appears while hovering over UI or testing
Prevent inputs when welcome dialog open
2025-08-03 21:35:03 +10:00
trofim.al 1d8ed5198c api version 0.7.0
Update example mod's API version so it actually loads. part 2.
2025-08-03 13:41:33 +03:00
AwesomezPro99 8cd3785e3c Fix Blazin' Death Pause 2025-08-03 13:01:13 +03:00
MAJigsaw77 7e01e7332b Fix html5 compile. 2025-08-03 12:48:54 +07:00
MAJigsaw77 c3a837b054 Dont compile the android classes on other targets. 2025-08-03 12:48:54 +07:00
MAJigsaw77 a92567886c Relocate the external folder outside of mobile package. 2025-08-03 12:48:54 +07:00
Abnormal 4cbfaefc1a Slightly longer fade transition
Co-authored-by: VirtuGuy <103977942+VirtuGuy@users.noreply.github.com>
2025-08-02 22:10:26 +03:00
AwesomezPro99 705e6b3308 Add Camera Fade To Song Select 2025-08-02 22:10:26 +03:00
Abnormal 56878befef update the haxelibs.......yippee...... 2025-08-02 21:59:30 +03:00
Abnormal 44cdbb08c4 asset changes wooooooooooooooo 2025-08-02 17:18:18 +03:00
Kolo d6fe54ee79 gyafyhaswguhyshbughsd 2025-08-02 17:18:18 +03:00
Kolo 32c775ef3f tooltips....BUT FOR NOTES! 2025-08-02 17:18:18 +03:00
PurSnake 1605514424 Seriously? 2025-08-02 17:02:31 +03:00
Hyper_ 743e8b13c2 me...................................................................................... 2025-08-02 16:22:18 +03:00
Eric cf89d672e7
Merge pull request #5392 from SantiagoCalebe/main
Just a lil' update of the readme
2025-08-02 08:55:57 -04:00
Kolo 661ddb15f7 me......................................................................... 2025-08-02 07:16:05 -05:00
Hundrec 72fa82716d A little more README polish 2025-08-02 18:54:18 +08:00
Santiago a069ca2dc6
I think i have a problem 2025-08-02 07:46:57 -03:00
Santiago 90a2d7a1ad
??? 2025-08-02 07:41:13 -03:00
cyn0x8 3170346b83 Create ModStore.hx 2025-08-02 11:32:44 +03:00
anysad e6d737125e feature: are you sure? 2025-08-02 11:28:17 +03:00
AwesomezPro99 3d88fbb11f Update PreferencesMenu.hx 2025-08-02 11:10:03 +03:00
JVN-Pixels 0d6bc2bedd Fix NOR in Freeplay after spamming the letter keybinds 2025-08-02 11:07:04 +03:00
anysad da5f691865 fix: no more animation editor crash 2025-08-02 11:03:23 +03:00
Hyper_ f63cbf073a Restore a lost change from #4787 2025-08-02 10:55:58 +03:00
Kolo 30721c3a30 tfw changeDiff wasn't even being called 2025-08-02 10:49:15 +03:00
JackXson-Real 2b1f346097 Add enter/exit animation for charSelect nametag part 2 2025-08-02 10:38:32 +03:00
Mihai Alexandru 12117557c7
Merge branch 'master' into pr-5565/SrtHero278/pointer-fixes 2025-08-02 10:33:33 +03:00
Hundrec 6b0e607af0 Update another contributor's name 2025-08-02 04:37:19 +08:00
Hundrec 2263b36cd7 Add a new contributor to Changelog and adjust spacing 2025-08-02 04:37:19 +08:00
Abnormal 2dcc528847 bump polymod to optimize some shiiiiiiiiiiiiiiiit 2025-08-02 02:19:14 +08:00
ExcodeFNF 5cb33e895c Freeplay CoolColors Array Removal 2025-08-02 02:01:36 +08:00
AwesomezPro99 1b91f3b57c Fixed DJ Flicker After New Rank 2025-08-02 01:28:14 +08:00
Eric e5e3270fef Update hmm.json to optimize FlxAnimate 2025-08-02 00:43:57 +08:00
EliteMasterEric e003a38cab Exclude the debug print function when we aren't in the build step 2025-08-01 18:32:37 +03:00
SrtHero278 890d1dafcb include the camera parameter in the comment 2025-08-01 19:48:39 +07:00
SrtHero278 9d2cfa9686 Fix touch pointer positioning when in PlayState 2025-08-01 19:48:39 +07:00
Karim Akra e6fd5d9b12
Make the Android back button close the game when in TitleState
Co-authored-by: Hyper_ <40342021+NotHyper-474@users.noreply.github.com>
Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-01 13:59:02 +07:00
Cameron Taylor 967397311b remove tiny uncommented code, and simplify an unneeded variable initialization 2025-08-01 13:15:56 +07:00
Cameron Taylor 542f59ef72 tallyCompletion() function for cleanup + constency 2025-08-01 13:15:56 +07:00
Kade 065ee74aea
Scoreable notekinds
Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-01 13:05:11 +07:00
Mihai Alexandru 312b5c8812
Flair adjustments to the build process
Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-01 12:05:21 +07:00
Mihai Alexandru e9aa6872a3
Bump libs refs
Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-01 12:03:58 +07:00
Karim Akra 4f9b63f22c
Configure the android adaptive icons through lime & moved the icons stuff to art submodule
Co-authored-by: MoonDroid <81515012+moondroidcoder@users.noreply.github.com>
2025-08-01 01:17:29 +07:00
MAJigsaw77 962519e7f0 Fix android compile. 2025-07-28 10:41:31 +03:00
MAJigsaw77 34bae4a431 Fix compile once again. 2025-07-28 10:41:31 +03:00
Karim Akra 79478080f6 oops 2025-07-28 10:41:31 +03:00
Karim Akra 65a3ccc8fe add the same changes to the .env parser on project.hxp !! 2025-07-28 10:41:31 +03:00
Karim Akra 4ac851c387 add support for target specific environment values
and renamed `GLOBAL_ADMOB_PUBLISHER` to `MOBILE_GLOBAL_ADMOB_PUBLISHER` !!
2025-07-28 10:41:31 +03:00
MAJigsaw77 70d5730b70 Fix compile. 2025-07-27 00:56:31 -05:00
Abnormal c527fed70c conflict stuffs lol 2025-07-27 00:56:31 -05:00
TechnikTil 054a5cd00b fix fullscreen bug and show clear save data if logged out of ng 2025-07-27 00:56:31 -05:00
TechnikTil 19c5de8fe2 HOPEFULLY FIX EVERYTHING WRONG (this time it was tested between two computers) 2025-07-27 00:56:31 -05:00
TechnikTil dbc26fd621 hopefully fix everything with load from ng 2025-07-27 00:56:31 -05:00
TechnikTil eb1986b246 move clear save data to the top, add clear ng save data 2025-07-27 00:56:31 -05:00
TechnikTil d661d93184 rewrite NGSaveSlot.load to be async, dont precache assets, error handling 2025-07-27 00:56:31 -05:00
TechnikTil 4a07d0a347 make newgrounds functions in Save static and only available if newgrounds feature flag is enabled, make sure base save slot is loaded and is not empty, and recover save data in case newgrounds doesnt wanna play nice 2025-07-27 00:56:31 -05:00
TechnikTil 832f01345f Add Save Data Options, also allow user to load and save with Newgrounds Save Slots. 2025-07-27 00:56:31 -05:00
MAJigsaw77 e8d41c938c Use no-traces define instead of the flag. 2025-07-26 13:46:48 -05:00
MAJigsaw77 a94bd7f36c Fix compiling. 2025-07-26 13:43:19 -05:00
EliteMasterEric c185f10e65 Move Discord client ID to .env 2025-07-26 13:43:19 -05:00
Cameron Taylor 52f3c63071 mobile fix where the capsules don't properly shift away from the letter sorting crap 2025-07-26 12:31:38 +03:00
Cameron Taylor e71062b914 some lil variable renaming in SongMenuItem.hx 2025-07-26 12:31:38 +03:00
Cameron Taylor 3aa6a36b5e small function renaming 2025-07-26 12:31:38 +03:00
MAJigsaw77 a89fa9c121 Acually fix it (oops). 2025-07-26 11:55:36 +03:00
MAJigsaw77 cb2015f51a Fix a workflow compile error. 2025-07-26 11:55:36 +03:00
MAJigsaw77 139ce3847c Fix for env vars that contain = as one of thier characters. 2025-07-26 11:55:36 +03:00
EliteMasterEric 87b40c1203 Obsolete NewgroundsCredentials.hx for storing secret keys in favor of .env 2025-07-26 11:55:36 +03:00
Kade b9852c316b
offset menu changes
Co-authored-by: Mihai Alexandru <77043862+MAJigsaw77@users.noreply.github.com>
2025-07-26 10:36:29 +03:00
Cameron Taylor 444697620c
Use round instead of floor to fix rounding errors in Preferences.
Co-authored-by: Mihai Alexandru <77043862+MAJigsaw77@users.noreply.github.com>
2025-07-26 10:04:00 +03:00
sector-a 6abeefb15d
Set cliprect for clear percent.
Co-authored-by: Mihai Alexandru <77043862+MAJigsaw77@users.noreply.github.com>
2025-07-25 17:43:34 +03:00
sector-a 530bd1f11d
Add onPause callback to pause substate.
Co-authored-by: Mihai Alexandru <77043862+MAJigsaw77@users.noreply.github.com>
2025-07-25 17:36:34 +03:00
sector-a 76c0dea92b
Move results and death haptics to scripts. 2025-07-25 17:23:04 +03:00
Karim Akra 2d7a2202f5
Fix song restarting bug (for good??).
Co-authored-by: Mihai Alexandru <77043862+MAJigsaw77@users.noreply.github.com>
2025-07-25 16:45:55 +03:00
sector-a 1f18a3d1f4
Fix diff dots alignment.
Co-authored-by: Mihai Alexandru <77043862+MAJigsaw77@users.noreply.github.com>
2025-07-25 16:37:39 +03:00
sector-a 0d57f61ffa
Scale up the bg in debug menu on widescreen
Co-authored-by: Mihai Alexandru <77043862+MAJigsaw77@users.noreply.github.com>
2025-07-25 16:19:46 +03:00
MAJigsaw77 9fa70775b9 Use funkin.Assets instead of openfl.utils.Assets. 2025-07-25 16:15:51 +03:00
sector-a 463afb43c0 assets submod 2025-07-25 16:15:51 +03:00
sector-a 2833827fbf Change the way game searches for pixel icons 2025-07-25 16:15:51 +03:00
Santiago 01cd72e805
Merge branch 'main' into main 2025-07-22 14:13:22 -03:00
Eric 72e0ff6c9b
Merge pull request #5487 from Lasercar/bunker-infiltrated
Bump to 0.7.4
2025-07-22 11:59:45 -04:00
Lasercar 82367dffc2 Bunker infiltrated 2025-07-23 00:30:15 +10:00
Santiago 96a1796905
readme update 2025-07-17 17:39:53 -03:00
211 changed files with 4244 additions and 2225 deletions

View file

@ -59,7 +59,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.7.3
placeholder: ex. 0.7.4
validations:
required: true

View file

@ -36,7 +36,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.7.3
placeholder: ex. 0.7.4
validations:
required: true

View file

@ -36,7 +36,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.7.3
placeholder: ex. 0.7.4
validations:
required: true

View file

@ -60,7 +60,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.7.3
placeholder: ex. 0.7.4
validations:
required: true

View file

@ -53,7 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- You can import them in another script as usual.
- Added support for renaming imported classes using the `as` keyword. (Thanks KoloInDaCrib!)
- Fixed `try`/`catch` blocks not working properly. (Thanks NotHyper-474!)
- Fixed null-safe field access not working properly for functions (ex. `class?.someFunction()). (Thanks KoloInDaCrib!)
- Fixed null-safe field access not working properly for functions (ex. `class?.someFunction()`). (Thanks KoloInDaCrib!)
- Fixed Linux being case-sensitive with filenames. (Thanks mikolka9144!)
### Fixed
@ -88,6 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* @Smokey555 made their first contribution in [#3318](https://github.com/FunkinCrew/Funkin/pull/3318)
* @CCobaltDev made their first contribution in [#3318](https://github.com/FunkinCrew/Funkin/pull/3318)
* @mikolka9144 made their first contribution in [polymod#212](https://github.com/larsiusprime/polymod/pull/212)
@ -705,6 +707,7 @@ This patch resolves a critical issue that could cause user's save data to become
## [0.5.0] - 2024-09-12
The Playable Pico Update!
### Added
- Added a new Character Select screen to switch between playable characters in Freeplay.
@ -825,7 +828,7 @@ The Playable Pico Update!
### Fixed
- Control binds in the controls menu no longer overlap their names.
- Attempting to exit the gameover screen and retry the song at the same time no longer crashes the game. ([thanks DMMaster636 for the PR!](https://github.com/FunkinCrew/Funkin/pull/2709))
- Attempting to exit the gameover screen and retry the song at the same time no longer crashes the game. ([thanks DM-kun for the PR!](https://github.com/FunkinCrew/Funkin/pull/2709))
- Botplay mode now handles the player's animations properly during hold notes. ([thanks Hundrec!](https://github.com/FunkinCrew/Funkin/pull/2683))
- 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.
@ -839,13 +842,14 @@ The Playable Pico Update!
## New Contributors for 0.4.1
* @Hundrec made their first contribution in [#2661](https://github.com/FunkinCrew/Funkin/pull/2661)
* @DMMaster636 made their first contribution in [#2709](https://github.com/FunkinCrew/Funkin/pull/2709)
* @DM-kun made their first contribution in [#2709](https://github.com/FunkinCrew/Funkin/pull/2709)
* @eltociear made their first contribution in [#2730](https://github.com/FunkinCrew/Funkin/pull/2730)
## [0.4.0] - 2024-06-06
The Pit Stop 1 update!
### Added
- 2 new Erect remixes, Eggnog and Satin Panties. Check them out from the Freeplay menu!
@ -1017,6 +1021,7 @@ The Pit Stop 1 update!
## [0.3.0] - 2024-04-30
The Weekend 1 update!
### Added
- New Story Level: Weekend 1, starring Pico, Darnell, and Nene.

View file

@ -1,14 +1,25 @@
# Friday Night Funkin'
<div align='center'><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Friday_Night_Funkin%27_logo.svg/2560px-Friday_Night_Funkin%27_logo.svg.png" width="800">
Friday Night Funkin' is a rhythm game. Built using HaxeFlixel for Ludum Dare 47.
<h2>Friday Night Funkin' is a rhythm game. Built using HaxeFlixel for <a href="https://ldjam.com/events/ludum-dare/47">Ludum Dare 47.</a></h2>
This game was made with love to Newgrounds and its community. Extra love to Tom Fulp.
</div>
- [Playable web demo on Newgrounds!](https://www.newgrounds.com/portal/view/770371)
- [Demo download builds for Windows, Mac, and Linux from Itch.io!](https://ninja-muffin24.itch.io/funkin)
- [Download Android builds from Google Play!](https://play.google.com/store/apps/details?id=me.funkin.fnf)
- [Download iOS builds from the App Store!](https://apps.apple.com/app/id6740428530)
<div align='center'>
<table>
<tr>
<td><img src="https://fridaynightfunkin.wiki.gg/images/d/d7/Title_Card.gif" alt="Title Screen" width="350"/></td>
<td><img src="https://fridaynightfunkin.wiki.gg/images/9/99/Menu.png" alt="Main Menu" width="350"/></td>
</tr>
</table>
</div>
# Getting Started
**PLEASE USE THE LINKS ABOVE IF YOU JUST WANT TO PLAY THE GAME**
@ -52,4 +63,4 @@ Full credits can be found in-game, or in the `credits.json` file which is locate
## Special Thanks
- [Tom Fulp](https://twitter.com/tomfulp) - For being a great guy and for Newgrounds
- [JohnnyUtah](https://twitter.com/JohnnyUtahNG/) - Voice of Tankman
- [L0Litsmonica](https://twitter.com/L0Litsmonica) - Voice of Mommy Mearest
- [L0Litsmonica](https://twitter.com/L0Litsmonica) - Voice of Mommy Mearest

2
art

@ -1 +1 @@
Subproject commit 67e550dbd22a8ea429eecc8ce078a36f74ea7723
Subproject commit 094ff109197e35d21342bdf2d51132be23948d3f

2
assets

@ -1 +1 @@
Subproject commit 69c02fa5019f603324a7d2ae362327a1eef9d109
Subproject commit 78957491ef2b6def24971693812605f82f55675e

131
astc-compression-data.json Normal file
View file

@ -0,0 +1,131 @@
{
"input": "./assets",
"output": "./astc-textures/",
"quality": "medium",
"blocksize": "10x10",
"colorprofile": "cl",
"custom": [
{
"asset": "asset/shared/images/resultScreen/results-pico/resultsPERFECT/spritemap1.png",
"blocksize": "4x4"
},
{
"asset": "asset/shared/images/resultScreen/results-pico/resultsGREAT/spritemap1.png",
"blocksize": "4x4"
},
{
"asset": "asset/shared/images/resultScreen/results-pico/resultsGOOD/spritemap1.png",
"blocksize": "4x4"
},
{
"asset": "asset/shared/images/resultScreen/results-pico/resultsSHIT/spritemap1.png",
"blocksize": "4x4"
},
{
"asset": "assets/preload/images/NOTE_assets.png",
"blocksize": "4x4"
},
{
"asset": "assets/preload/images/NOTE_hold_assets.png",
"blocksize": "4x4"
},
{
"asset": "assets/preload/images/StrumlineNotes.png",
"blocksize": "4x4"
},
{
"asset": "assets/preload/images/alphabet.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/NOTE_hold_assets.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/holdCoverBlue.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/holdCoverGreen.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/holdCoverPurple.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/holdCoverRed.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/latencyArrow.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/latencyReceptor.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/noteSplashes.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/noteStrumline.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/notes.png",
"blocksize": "4x4"
},
{
"asset": "assets/week1/image/erect/bg.png",
"blocksize": "8x8"
},
{
"asset": "assets/week1/image/erect/lights.png",
"blocksize": "6x6"
},
{
"asset": "assets/week1/images/erect/crowd.png",
"blocksize": "8x8"
},
{
"asset": "assets/week1/images/erect/server.png",
"blocksize": "8x8"
}
],
"excludes": [
"assets/preload/images/cursor/",
"assets/preload/images/fonts/",
"assets/preload/images/freeplay/",
"assets/preload/images/icons/",
"assets/preload/images/soundtray/",
"assets/preload/images/stageBuild/",
"assets/preload/images/titleEnter.png",
"assets/preload/images/titleEnter_mobile.png",
"assets/preload/images/ui/popup/pixel/*",
"assets/shared/images/characters/abotPixel/*",
"assets/shared/images/characters/bfPixel.png",
"assets/shared/images/characters/bfPixelsDEAD.png",
"assets/shared/images/characters/gfPixel.png",
"assets/shared/images/characters/nenePixel/*",
"assets/shared/images/characters/picoPixel/*",
"assets/shared/images/characters/senpai.png",
"assets/shared/images/characters/spirit.png",
"assets/shared/images/healthBar.png",
"assets/shared/images/resultScreen/*",
"assets/shared/images/resultScreen/clearPercent/",
"assets/shared/images/resultScreen/rankText/",
"assets/shared/images/ui/chart-editor/",
"assets/shared/images/ui/countdown/pixel/*",
"assets/week1/*",
"assets/week1/images/erect/brightLightSmall.png",
"assets/week1/images/erect/lightAbove.png",
"assets/week1/images/erect/lightgreen.png",
"assets/week1/images/erect/lightred.png",
"assets/week1/images/erect/orangeLight.png",
"assets/week2/images/erect/stairsDark.png",
"assets/week2/images/erect/stairsLight.png",
"assets/week6/"
]
}

View file

@ -80,7 +80,7 @@
{
"props": {
"ignoreExtern": true,
"format": "^[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$",
"format": "^?:_[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$",
"tokens": ["INLINE", "NOTINLINE"]
},
"type": "ConstantName"

View file

@ -1,21 +0,0 @@
assets/preload/images/cursor/*
assets/preload/images/freeplay/*
assets/preload/images/icons/*
assets/preload/images/titleEnter.png
assets/preload/images/titleEnter_mobile.png
assets/preload/images/soundtray/*
assets/preload/images/stageBuild/*
assets/preload/images/ui/popup/pixel/
assets/shared/images/characters/abotPixel/
assets/shared/images/characters/bfPixel.png
assets/shared/images/characters/bfPixelsDEAD.png
assets/shared/images/characters/gfPixel.png
assets/shared/images/characters/nenePixel/
assets/shared/images/characters/picoPixel/
assets/shared/images/characters/senpai.png
assets/shared/images/characters/spirit.png
assets/shared/images/resultScreen/*
assets/shared/images/ui/chart-editor/*
assets/shared/images/ui/countdown/pixel/
assets/week1/
assets/week6/*

View file

@ -60,7 +60,7 @@ This section provides guidelines to follow when [opening an issue](https://githu
## Requirements
Make sure you're playing:
- the latest version of the game (currently v0.7.3)
- the latest version of the game (currently v0.7.4)
- without any mods
- on [Newgrounds](https://www.newgrounds.com/portal/view/770371) or downloaded from [itch.io](https://ninja-muffin24.itch.io/funkin)

View file

@ -6,7 +6,7 @@
"name": "EliteMasterEric"
}
],
"api_version": "0.5.0",
"api_version": "0.7.0",
"mod_version": "1.0.0",
"license": "Apache-2.0"
}

View file

@ -6,7 +6,7 @@
"name": "EliteMasterEric"
}
],
"api_version": "0.5.0",
"api_version": "0.7.0",
"mod_version": "1.0.0",
"license": "Apache-2.0"
}

View file

@ -11,41 +11,41 @@
"name": "astc-compressor",
"type": "git",
"dir": null,
"ref": "8c9f56927c523df7b849352c6951f04112fe15cc",
"ref": "b7a91b3072dc16e785a9bde847f17ba0ef15dd47",
"url": "https://github.com/KarimAkra/astc-compressor"
},
{
"name": "extension-admob",
"type": "git",
"dir": null,
"ref": "a53d5916bdcb2e48913f94d9ae1d949b049dcdc1",
"ref": "02334589ff9603a5f483077a44395009644f6274",
"url": "https://github.com/FunkinCrew/extension-admob"
},
{
"name": "extension-androidtools",
"type": "haxelib",
"version": "2.2.1"
"version": "2.2.2"
},
{
"name": "extension-haptics",
"type": "haxelib",
"version": "1.0.3"
"version": "1.0.4"
},
{
"name": "extension-iapcore",
"type": "haxelib",
"version": "1.0.3"
"version": "1.0.4"
},
{
"name": "extension-iarcore",
"type": "haxelib",
"version": "1.0.2"
"version": "1.0.3"
},
{
"name": "flixel",
"type": "git",
"dir": null,
"ref": "08fc955ca87f192a971719a675f1d3b21709725d",
"ref": "ac2a34fe4b3400bc04218f46320a65af0f337fc0",
"url": "https://github.com/FunkinCrew/flixel"
},
{
@ -59,7 +59,7 @@
"name": "flxanimate",
"type": "git",
"dir": null,
"ref": "39c1572add28869c558b218fffed13df1b64f376",
"ref": "49214278b9124823582cdcecd94f4a1de9a4b36b",
"url": "https://github.com/FunkinCrew/flxanimate"
},
{
@ -104,14 +104,14 @@
"name": "hscript",
"type": "git",
"dir": null,
"ref": "d60bb2947fa609fdc875ccfae89666a6984eeaf2",
"ref": "8c1d238b069ef97cec13f5121b29d2afe2b8985d",
"url": "https://github.com/FunkinCrew/hscript"
},
{
"name": "hxcpp",
"type": "git",
"dir": null,
"ref": "4e24283a047f11bded6affabbc9ec405156e026e",
"ref": "5a0dc3f644dc676a4a092b7e6c8edc8be941f024",
"url": "https://github.com/FunkinCrew/hxcpp"
},
{
@ -170,37 +170,9 @@
"name": "lime",
"type": "git",
"dir": null,
"ref": "c750ebf6b48c4bc018abe9855fbae5ffdbc4771a",
"ref": "e5f8c27124598505917a001588b560244731adfb",
"url": "https://github.com/FunkinCrew/lime"
},
{
"name": "mconsole",
"type": "git",
"dir": null,
"ref": "06c0499ed8f80628a0e6e55ffa32c3cbd688a838",
"url": "https://github.com/massive-oss/mconsole"
},
{
"name": "mcover",
"type": "git",
"dir": "src",
"ref": "c3c47cd682b0b202a41caee95321989391b617ef",
"url": "https://github.com/massive-oss/mcover"
},
{
"name": "mockatoo",
"type": "git",
"dir": "src",
"ref": "13d77a0a8eaf5e789ef5dae6cd33eee812deda36",
"url": "https://github.com/FunkinCrew/mockatoo"
},
{
"name": "munit",
"type": "git",
"dir": "src",
"ref": "f61be7f7ba796595f45023ca65164a485aba0e7e",
"url": "https://github.com/FunkinCrew/MassiveUnit"
},
{
"name": "newgrounds",
"type": "git",
@ -219,14 +191,14 @@
"name": "polymod",
"type": "git",
"dir": null,
"ref": "866f19edbcd872b3358f9a41f2f6a24c71c191d1",
"ref": "55ee00236806330c89c3bff1e1563eb6425a9d96",
"url": "https://github.com/larsiusprime/polymod"
},
{
"name": "thx.core",
"type": "git",
"dir": null,
"ref": "76d87418fadd92eb8e1b61f004cff27d656e53dd",
"ref": "2bf2b992e06159510f595554e6b952e47922f128",
"url": "https://github.com/fponticelli/thx.core"
},
{

22
organize_astc_data.ps1 Normal file
View file

@ -0,0 +1,22 @@
# Organizes astc-compression-data alphabetically because i hate it when it's not organized -zack
$InputFile = "./astc-compression-data.json"
$OutputFile = "./astc-compression-data.json"
if (-not (Test-Path $InputFile)) {
Write-Host "❌ File $InputFile not found"
exit 1
}
if (-not (Get-Command jq -ErrorAction SilentlyContinue)) {
Write-Host "jq is not installed. Download from https://jqlang.github.io/jq/ or install via:"
Write-Host " winget install jqlang.jq"
exit 1
}
jq '.
| .custom = ( .custom | sort_by(.asset) )
| .excludes = ( .excludes | sort )
' $InputFile | Out-File -Encoding utf8 $OutputFile
Write-Host "✅ Sorted JSON written to $OutputFile WOOHOOO !!"

25
organize_astc_data.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
# Organizes astc-compression-data alphabetically because i hate it when it's not organized -zack
INPUT_FILE="./astc-compression-data.json"
OUTPUT_FILE="./astc-compression-data.json"
# Check jq
if ! command -v jq &> /dev/null; then
echo "jq is not installed. Install it with:"
echo " macOS: brew install jq"
echo " Linux (Debian/Ubuntu): sudo apt-get install jq"
exit 1
fi
if [ ! -f "$INPUT_FILE" ]; then
echo "❌ File $INPUT_FILE not found"
exit 1
fi
jq '.
| .custom = ( .custom | sort_by(.asset) )
| .excludes = ( .excludes | sort )
' "$INPUT_FILE" > "$OUTPUT_FILE".tmp && mv "$OUTPUT_FILE".tmp "$OUTPUT_FILE"
echo "✅ Sorted JSON written to $OUTPUT_FILE WOOHOOO !!"

View file

@ -5,8 +5,10 @@ import hxp.*;
import lime.tools.*;
import sys.FileSystem;
import sys.io.File;
import haxe.io.Bytes;
import haxe.io.Path;
import haxe.ds.Map;
import haxe.xml.Printer;
using StringTools;
@ -35,7 +37,7 @@ class Project extends HXProject
* The build's number and version code.
* Used when publishing the game to mobile app stores. Should increment with each patch.
*/
static final BUILD_NUMBER:Int = 2;
static final BUILD_NUMBER:Int = 51;
/**
* The game's name. Used as the default window title.
@ -58,6 +60,11 @@ class Project extends HXProject
*/
static final SOURCE_DIR:String = "source";
/**
* The relative location of the templates folder.
*/
static final TEMPLATES_DIR:String = "templates";
/**
* The fully qualified class path for the game's preloader.
* Particularly important on HTML5 but we use it on all platforms.
@ -103,30 +110,11 @@ class Project extends HXProject
static final ANDROID_EXTENSIONS:Array<String> = ["funkin.extensions.CallbackUtil"];
//
// ASTC COMPRESSION
//
/**
* The quality of the compression process.
* This doesn't exactly reduce the quality of the outputted texture..
* But it changes how hard the CPU gotta work to maintin the quality and details of the image while compressing.
* possible values: fastest, fast, medium, thorough, exhaustive.
*/
static var ASTC_QUALITY:String = "medium";
/**
* The team ID to use for the iOS app. Configured in XCode.
*/
static var IOS_TEAM_ID:String = "Z7G7AVNGSH";
/**
* The block size of the ASTC compressed textures.
* Higher block size means lower quality and lighter file size for the generated compressed textures.
* possible block sizes: 4x4, 4x6, 6x6, 8x8 ... 12x12.
*/
static var ASTC_BLOCKSIZE:String = "10x10";
/**
* A list of asset file globs to exclude from ASTC compression when creating optimized mobile builds.
*/
@ -330,10 +318,10 @@ class Project extends HXProject
static final FEATURE_HAPTICS:FeatureFlag = "FEATURE_HAPTICS";
/**
* `-DFEATURE_INPUT_OFFSETS`
* `-DFEATURE_LAG_ADJUSTMENT`
* If this flag is enabled, the input offsets menu will be available to configure your audio and visual offsets.
*/
static final FEATURE_INPUT_OFFSETS:FeatureFlag = "FEATURE_INPUT_OFFSETS";
static final FEATURE_LAG_ADJUSTMENT:FeatureFlag = "FEATURE_LAG_ADJUSTMENT";
/**
* `-DFEATURE_LOG_TRACE`
@ -487,6 +475,7 @@ class Project extends HXProject
envConfig = readEnvironmentFile("./.env");
flair();
configureApp();
displayTarget();
@ -498,14 +487,12 @@ class Project extends HXProject
configureOutputDir();
configurePolymod();
configureHaxelibs();
configureASTCTextures();
configureAssets();
configureIcons();
readASTCExclusion();
if (FEATURE_COMPRESSED_TEXTURES.isEnabled(this))
if (!isLinux())
{
runASTCCompressor();
configureIcons();
}
if (FEATURE_MOBILE_ADVERTISEMENTS.isEnabled(this))
@ -522,8 +509,6 @@ class Project extends HXProject
{
configureIOS();
}
info("Done configuring project.");
}
/**
@ -532,10 +517,9 @@ class Project extends HXProject
function flair()
{
// TODO: Implement this.
info("Friday Night Funkin'");
info("Friday Night Funkin' - " + VERSION);
info("Initializing build...");
info("Target Version: " + VERSION);
info("Git Branch: " + getGitBranch());
info("Git Commit: " + getGitCommit());
info("Git Modified? " + getGitModified());
@ -566,6 +550,9 @@ class Project extends HXProject
// If for some reason we have multiple source directories, we can add more entries here.
this.sources.push(SOURCE_DIR);
// Templates stuff
this.templatePaths.push(TEMPLATES_DIR);
// Tell Lime to run some prebuild and postbuild scripts.
this.preBuildCallbacks.push(buildHaxeCLICommand(PREBUILD_HX));
this.postBuildCallbacks.push(buildHaxeCLICommand(POSTBUILD_HX));
@ -829,7 +816,7 @@ class Project extends HXProject
FEATURE_STAGE_EDITOR.apply(this, !(isWeb() || isMobile()));
// Should be true except on web builds (some asset stuff breaks it)
FEATURE_INPUT_OFFSETS.apply(this, !isWeb());
FEATURE_LAG_ADJUSTMENT.apply(this, !isWeb());
// Should be true except on web and mobile builds.
// Screenshots doesn't work there, and mobile has its own screenshots anyway.
@ -860,6 +847,79 @@ class Project extends HXProject
// Since mobile is pretty memory safe (I'm looking at you Apple...) we need them BADLY to have the game run properly.
// It's kept off for desktop due to the low ASTC support on desktop GPUs (which seem to be only among Intergrated Graphics).
FEATURE_COMPRESSED_TEXTURES.apply(this, isMobile() && isRelease());
renderFlagsTable();
}
/**
* Renders a cute little table of which feature flags are enabled and which are disabled
*/
function renderFlagsTable():Void
{
var enabledWidth:Int = 0;
var disabledWidth:Int = 0;
var enabledFlags:Array<String> = [];
var disabledFlags:Array<String> = [];
var unicodeCheck:String = " ";
var unicodeCross:String = "× ";
if (isWindowsHost())
{
#if (!target.unicode)
// lime build tools uses neko (unless compiling with `-eval`), and neko on windows doesn't support UTF8/unicode characters
// so we need to remove our cute emoji!
unicodeCheck = "o ";
unicodeCross = "x ";
#end
}
for (flagStr in this.haxedefs.keys())
{
if (!flagStr.startsWith(FeatureFlag.INVERSE_PREFIX))
{
// we do flagStr.length + 3 to accomodate for adding the "✓ " at the beginning
enabledWidth = Std.int(Math.max(flagStr.length + 3, enabledWidth));
enabledFlags.push(unicodeCheck + flagStr);
}
else
{
disabledWidth = Std.int(Math.max(flagStr.length, disabledWidth));
disabledFlags.push(unicodeCross + flagStr.substring(FeatureFlag.INVERSE_PREFIX.length));
}
}
enabledFlags.sort(function(a, b) return a > b ? 1 : -1);
disabledFlags.sort(function(a, b) return a > b ? 1 : -1);
var horizontalDivider:String = "".rpad("-", enabledWidth + disabledWidth);
info(horizontalDivider);
// Header
info("FEATURE FLAGS");
info('Enabled Flags $unicodeCheck'.rpad(" ", enabledWidth + 5) + 'Disabled Flags $unicodeCross');
info(horizontalDivider);
while (enabledFlags.length > 0 || disabledFlags.length > 0)
{
var outputLine:String = "";
if (enabledFlags.length > 0)
outputLine += enabledFlags.shift();
outputLine = outputLine.rpad(" ", enabledWidth + 5);
outputLine += "| ";
if (disabledFlags.length > 0)
outputLine += disabledFlags.shift() ?? "";
info(outputLine);
}
info(horizontalDivider);
}
/**
@ -890,7 +950,7 @@ class Project extends HXProject
if (FEATURE_LOG_TRACE.isDisabled(this))
{
addHaxeFlag("--no-traces");
setHaxedef("no-traces");
}
// Disable the built in pause screen when unfocusing the game.
@ -973,17 +1033,17 @@ class Project extends HXProject
// we use a dedicated 'tracy' folder, since it generally needs a recompile when in use
if (FEATURE_DEBUG_TRACY.isEnabled(this)) buildDir += "-tracy";
info('Output directory: $buildDir');
// trailing slash might not be needed, works fine on macOS without it, but I haven't tested on Windows!
buildDir += "/";
info('Output directory: $buildDir');
// setenv('BUILD_DIR', buildDir);
app.path = buildDir;
}
function configureAndroid()
{
javaPaths.push(Path.join([SOURCE_DIR, 'funkin/mobile/external/android/java']));
javaPaths.push(Path.join([SOURCE_DIR, 'funkin/external/android/java']));
if (isRelease())
{
@ -995,47 +1055,37 @@ class Project extends HXProject
keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD'));
}
Reflect.setField(config.get('android.manifest'), 'xmlns:tools', 'http://schemas.android.com/tools');
final permissionsConfig:Array<String> = [
'<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />',
'<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" />'
];
final xmlChildren:Null<Array<String>> = config.get('android.manifest').xmlChildren;
if (xmlChildren != null)
{
config.get('android.manifest').xmlChildren = xmlChildren.concat(permissionsConfig);
}
else
{
config.get('android.manifest').xmlChildren = permissionsConfig;
}
if (isBundle())
{
config.set("android.gradle-project-directory", "bin-bundle");
}
}
final modsFolderProvider = [
'<provider android:authorities="$PACKAGE_NAME.docprovider" android:name="funkin.provider.DataFolderProvider" android:grantUriPermissions="true" android:exported="true" android:permission="android.permission.MANAGE_DOCUMENTS">',
' <intent-filter>',
' <action android:name="android.content.action.DOCUMENTS_PROVIDER" />',
' </intent-filter>',
'</provider>'
];
final modsFolderProvider:Xml = Xml.createElement('provider');
modsFolderProvider.set('android:authorities', '$PACKAGE_NAME.docprovider');
modsFolderProvider.set('android:name', 'funkin.provider.DataFolderProvider');
modsFolderProvider.set('android:grantUriPermissions', 'true');
modsFolderProvider.set('android:exported', 'true');
modsFolderProvider.set('android:permission', 'android.permission.MANAGE_DOCUMENTS');
final xmlChildren:Null<Array<String>> = config.get('android.application').xmlChildren;
final intentFilter:Xml = Xml.createElement('intent-filter');
if (xmlChildren != null)
final action:Xml = Xml.createElement('action');
action.set('android:name', 'android.content.action.DOCUMENTS_PROVIDER');
intentFilter.addChild(action);
modsFolderProvider.addChild(intentFilter);
@:nullSafety(Off)
{
config.get('android.application').xmlChildren = xmlChildren.concat(modsFolderProvider);
}
else
{
config.get('android.application').xmlChildren = modsFolderProvider;
if (config.get('android.application').xmlChildren != null)
{
config.get('android.application').xmlChildren.push(Printer.print(modsFolderProvider));
}
else
{
config.get('android.application').xmlChildren = [Printer.print(modsFolderProvider)];
}
}
final extensions:Null<Array<String>> = config.getArrayString('android.extension');
@ -1238,7 +1288,14 @@ class Project extends HXProject
addAsset("LICENSE.md", "LICENSE.md", "art", shouldEmbedArt);
addAsset("CHANGELOG.md", "CHANGELOG.md", "art", shouldEmbedArt);
info('Done configuring assets.');
if (shouldEmbed)
{
info('Done embedding assets.');
}
else
{
info('Done including assets.');
}
}
/**
@ -1251,27 +1308,7 @@ class Project extends HXProject
if (isAndroid())
{
// Adaptive icons
// TODO: Add Adapative Icons in Lime
templatePaths.push("templates");
var androidTheme:String = "@style/LimeAppMainTheme";
if (window.fullscreen != null && window.fullscreen)
{
androidTheme += "Fullscreen";
}
final iconXmlChild:Dynamic = {
"android:label": meta.title,
"android:allowBackup": "true",
"android:theme": androidTheme,
"android:hardwareAccelerated": "true",
"android:allowNativeHeapPointerTagging": ANDROID_TARGET_SDK_VERSION >= 30 ? "false" : null,
"android:largeHeap": "true",
"android:icon": "@mipmap/ic_launcher",
"android:roundIcon": "@mipmap/ic_launcher_round"
};
config.set('android.application', iconXmlChild);
adaptiveIcon = new AdaptiveIcon('art/icons/android/', true);
}
else if (isIOS())
{
@ -1303,7 +1340,24 @@ class Project extends HXProject
addIcon("art/icons/icon64.png", 64);
addIcon("art/icons/iconOG.png");
info('Done configuring icons.');
}
info('Done configuring icons.');
}
/**
* Configure the astc textures.
*/
function configureASTCTextures()
{
if (command != "display")
{
readASTCExclusion();
if (FEATURE_COMPRESSED_TEXTURES.isEnabled(this))
{
runASTCCompressor();
}
}
}
@ -1360,6 +1414,21 @@ class Project extends HXProject
return this.architectures.contains(Architecture.X64);
}
public function isWindowsHost():Bool
{
return System.hostPlatform == WINDOWS;
}
public function isMacHost():Bool
{
return System.hostPlatform == MAC;
}
public function isLinuxHost():Bool
{
return System.hostPlatform == LINUX;
}
public function isWindows():Bool
{
return this.target == Platform.WINDOWS;
@ -1703,7 +1772,8 @@ class Project extends HXProject
*/
public function error(message:String):Void
{
Log.error('${message}');
Sys.stderr().write(Bytes.ofString('[ERROR] ${message}'));
Sys.exit(1);
}
/**
@ -1713,7 +1783,7 @@ class Project extends HXProject
{
if (command != "display")
{
Log.info('[INFO] ${message}');
Sys.println('[INFO] ${message}');
}
}
@ -1740,46 +1810,67 @@ class Project extends HXProject
var env = new Map<String, Dynamic>();
for (line in envFile.split('\n'))
{
if (line == "" || line.startsWith("#")) continue;
if (line.length <= 0 || line.startsWith("#") || shouldExcludeEnvKey(line)) continue;
var parts = line.split('=');
if (parts.length != 2) continue;
var index:Int = line.indexOf('=');
env.set(parts[0], parts[1]);
if (index == -1) continue;
var field:String = line.substr(0, index);
var value:String = line.substr(index + 1);
env.set(field, value);
}
return env;
}
private function shouldExcludeEnvKey(key:String):Bool
{
final android:Bool = key.startsWith('ANDROID_');
final ios:Bool = key.startsWith('IOS_');
final mobile:Bool = key.startsWith('MOBILE_') || ios || android;
final web:Bool = key.startsWith('WEB_');
final desktop:Bool = key.startsWith('DESKTOP_');
if (isWeb() && (mobile || desktop)) return true;
if (isDesktop() && (mobile || web)) return true;
if (isAndroid() && (ios || web || desktop)) return true;
if (isIOS() && (android || web || desktop)) return true;
return false;
}
public function readASTCExclusion():Void
{
astcExcludes = File.getContent('./compression-excludes.txt').trim().split('\n');
@:nullSafety(Off)
astcExcludes = haxe.Json.parse(File.getContent('./astc-compression-data.json')).excludes;
for (i in 0...astcExcludes.length)
astcExcludes[i] = astcExcludes[i].trim();
}
public function isASTCExcluded(filePath:String):Bool
public function isASTCExcluded(file:String):Bool
{
for (exclusion in astcExcludes)
{
if (exclusion.endsWith("/*"))
if (exclusion.endsWith("/"))
{
var normalizedFilePath = Path.normalize(filePath);
var normalizedExclusion = Path.normalize(exclusion.substr(0, exclusion.length - 2));
var normalizedFilePath = Path.normalize(file);
var normalizedExclusion = Path.normalize(exclusion);
if (normalizedFilePath.startsWith(normalizedExclusion)) return true;
}
else if (exclusion.endsWith("/"))
else if (exclusion.endsWith("/*"))
{
var normalizedExclusion = Path.normalize(exclusion);
var fileDirectory = Path.directory(Path.normalize(filePath));
var normalizedExclusion = Path.normalize(exclusion.substr(0, exclusion.length - 2));
var fileDirectory = Path.directory(Path.normalize(file));
if (fileDirectory == normalizedExclusion) return true;
}
else
{
if (filePath == exclusion) return true;
if (file == exclusion) return true;
}
}
@ -1790,16 +1881,12 @@ class Project extends HXProject
{
info('Compressing ASTC textures...');
var args:Array<String> = ['run', 'astc-compressor'];
args = args.concat(['-i', './assets']);
args = args.concat(['-o', './astc-textures/assets/']);
args = args.concat(['-blocksize', ASTC_BLOCKSIZE]);
args = args.concat(['-quality', ASTC_QUALITY]);
args = args.concat(['-excludes', './compression-excludes.txt']);
var args:Array<String> = ['run', 'astc-compressor', 'compress-from-json'];
args = args.concat(['-json', './astc-compression-data.json']);
Sys.command('haxelib', args);
info('Finished compressing ASTC textures!');
info('Done compressing ASTC textures.');
}
}
@ -1807,9 +1894,9 @@ class Project extends HXProject
* An object representing a feature flag, which can be enabled or disabled.
* Includes features such as automatic generation of compile defines and inversion.
*/
abstract FeatureFlag(String)
abstract FeatureFlag(String) from String to String
{
static final INVERSE_PREFIX:String = "NO_";
public static final INVERSE_PREFIX:String = "NO_";
public function new(input:String)
{
@ -1834,13 +1921,11 @@ abstract FeatureFlag(String)
if (isEnabled(project))
{
// If this flag was already enabled, disable the inverse.
project.info('Enabling feature flag ${this}');
getInverse().disable(project, false);
}
else if (getInverse().isEnabled(project))
{
// If the inverse flag was already enabled, disable this flag.
project.info('Disabling feature flag ${this}');
disable(project, false);
}
else
@ -1848,13 +1933,11 @@ abstract FeatureFlag(String)
if (enableByDefault)
{
// Enable this flag if it was unset, and disable the inverse.
project.info('Enabling feature flag ${this}');
enable(project, true);
}
else
{
// Disable this flag if it was unset, and enable the inverse.
project.info('Disabling feature flag ${this}');
disable(project, true);
}
}

View file

@ -145,6 +145,7 @@ class Main extends Sprite
#if FEATURE_DEBUG_FUNCTIONS
game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil());
funkin.util.macro.ConsoleMacro.init();
#end
#if !html5

View file

@ -29,7 +29,7 @@ class Postbuild
var buildTime:Float = roundToTwoDecimals(end - start);
trace('Build took: ${buildTime} seconds');
Sys.println('[INFO] Build took: ${buildTime} seconds');
}
}

View file

@ -9,28 +9,16 @@ class Prebuild
{
static inline final BUILD_TIME_FILE:String = '.build_time';
static final NG_CREDS_PATH:String = './source/funkin/api/newgrounds/NewgroundsCredentials.hx';
static final NG_CREDS_TEMPLATE:String = "package funkin.api.newgrounds;
class NewgroundsCredentials
{
public static final APP_ID:String = #if API_NG_APP_ID haxe.macro.Compiler.getDefine(\"API_NG_APP_ID\") #else 'INSERT APP ID HERE' #end;
public static final ENCRYPTION_KEY:String = #if API_NG_ENC_KEY haxe.macro.Compiler.getDefine(\"API_NG_ENC_KEY\") #else 'INSERT ENCRYPTION KEY HERE' #end;
}";
static function main():Void
{
var start:Float = Sys.time();
trace('[PREBUILD] Performing pre-build tasks...');
// Sys.println('[INFO] Performing pre-build tasks...');
saveBuildTime();
buildCredsFile();
var end:Float = Sys.time();
var duration:Float = end - start;
trace('[PREBUILD] Finished pre-build tasks in $duration seconds.');
// Sys.println('[INFO] Finished pre-build tasks in $duration seconds.');
}
static function saveBuildTime():Void
@ -41,20 +29,4 @@ class NewgroundsCredentials
fo.writeDouble(now);
fo.close();
}
static function buildCredsFile():Void
{
if (sys.FileSystem.exists(NG_CREDS_PATH))
{
trace('[PREBUILD] NewgroundsCredentials.hx already exists, skipping.');
}
else
{
trace('[PREBUILD] Creating NewgroundsCredentials.hx...');
var fileContents:String = NG_CREDS_TEMPLATE;
sys.io.File.saveContent(NG_CREDS_PATH, fileContents);
}
}
}

View file

@ -5,6 +5,7 @@ import flixel.util.FlxSignal;
import flixel.math.FlxMath;
import funkin.data.song.SongData.SongTimeChange;
import funkin.data.song.SongDataUtils;
import funkin.play.PlayState;
import funkin.save.Save;
import funkin.util.TimerUtil.SongSequence;
import haxe.Timer;
@ -113,6 +114,17 @@ class Conductor
if (currentTimeChange == null) return Constants.DEFAULT_BPM;
@:privateAccess
if (PlayState.instance != null && PlayState.instance.startingSong)
{
for (i in 0...timeChanges.length)
{
if (PlayState.instance.startTimestamp >= timeChanges[i].timeStamp) currentTimeChange = timeChanges[i];
if (PlayState.instance.startTimestamp < timeChanges[i].timeStamp) break;
}
}
return currentTimeChange.bpm;
}
@ -417,7 +429,7 @@ class Conductor
// If the song is playing, limit the song position to the length of the song or beginning of the song.
if (FlxG.sound.music != null && FlxG.sound.music.playing)
{
this.songPosition = FlxMath.bound(Math.min(this.combinedOffset, 0), songPos, currentLength);
this.songPosition = Math.min(this.combinedOffset, 0).clamp(songPos, currentLength);
this.songPositionDelta += FlxG.elapsed * 1000 * FlxG.sound.music.pitch;
}
else

View file

@ -117,10 +117,7 @@ class FunkinMemory
*/
public static function cacheTexture(key:String):Void
{
if (currentCachedTextures.exists(key))
{
return; // Already cached.
}
if (currentCachedTextures.exists(key)) return;
if (previousCachedTextures.exists(key))
{
@ -135,13 +132,13 @@ class FunkinMemory
if (graphic == null)
{
FlxG.log.warn('Failed to cache graphic: $key');
return;
}
else
{
trace('Successfully cached graphic: $key');
graphic.persist = true;
currentCachedTextures.set(key, graphic);
}
trace('Successfully cached graphic: $key');
graphic.persist = true;
currentCachedTextures.set(key, graphic);
forceRender(graphic);
}
/**
@ -150,26 +147,54 @@ class FunkinMemory
*/
static function permanentCacheTexture(key:String):Void
{
if (permanentCachedTextures.exists(key))
{
return; // Already cached.
}
if (permanentCachedTextures.exists(key)) return;
var graphic:Null<FlxGraphic> = FlxGraphic.fromAssetKey(key, false, null, true);
if (graphic == null)
{
FlxG.log.warn('Failed to cache graphic: $key');
}
else
{
trace('Successfully cached graphic: $key');
graphic.persist = true;
permanentCachedTextures.set(key, graphic);
return;
}
trace('Successfully cached graphic: $key');
graphic.persist = true;
permanentCachedTextures.set(key, graphic);
forceRender(graphic);
currentCachedTextures = permanentCachedTextures;
}
/**
* Forces the GPU to load and upload a FlxGraphic.
*/
private static function forceRender(graphic:FlxGraphic):Void
{
if (graphic == null) return;
var bmp:Null<FlxGraphic> = FlxG.bitmap.get(graphic.key);
if (bmp != null && bmp.bitmap != null) var _:Int = bmp.bitmap.width; // Trigger
// Draws sprite and actually caches it.
var sprite = new flixel.FlxSprite();
sprite.loadGraphic(graphic);
sprite.draw(); // Draw sprite and load it into game's memory.
sprite.destroy();
}
/**
* Checks, if graphic with given path cached in memory.
*/
public static inline function isGraphicCached(path:String):Bool
return permanentCachedTextures.exists(path) || currentCachedTextures.exists(path) || previousCachedTextures.exists(path);
public static function getCachedGraphic(path:String):Null<FlxGraphic>
{
if (permanentCachedTextures.exists(path)) return permanentCachedTextures.get(path);
if (currentCachedTextures.exists(path)) return currentCachedTextures.get(path);
if (previousCachedTextures.exists(path)) return previousCachedTextures.get(path); // just in case
return null;
}
/**
* Prepares the cache for purging unused textures.
*/

View file

@ -11,9 +11,9 @@ import flixel.math.FlxRect;
import flixel.system.debug.log.LogStyle;
import flixel.util.FlxColor;
import funkin.graphics.FunkinSprite;
import funkin.data.dialogue.conversation.ConversationRegistry;
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
import funkin.data.dialogue.speaker.SpeakerRegistry;
import funkin.data.dialogue.ConversationRegistry;
import funkin.data.dialogue.DialogueBoxRegistry;
import funkin.data.dialogue.SpeakerRegistry;
import funkin.data.freeplay.album.AlbumRegistry;
import funkin.data.freeplay.player.PlayerRegistry;
import funkin.data.freeplay.style.FreeplayStyleRegistry;
@ -55,6 +55,13 @@ import funkin.api.newgrounds.NewgroundsClient;
@:nullSafety
class InitState extends FlxState
{
/**
* Simply states whether the "core stuff" is ready or not.
* This is used to prevent re-initialization of specific core features.
*/
@:noCompletion
static var _coreInitialized:Bool = false;
/**
* Perform a bunch of game setup, then immediately transition to the title screen.
*/
@ -78,159 +85,163 @@ class InitState extends FlxState
*/
function setupShit():Void
{
//
// GAME SETUP
//
if (!_coreInitialized)
{
//
// GAME SETUP
//
// Setup window events (like callbacks for onWindowClose)
// and fullscreen keybind setup
WindowUtil.initWindowEvents();
// Disable the thing on Windows where it tries to send a bug report to Microsoft because why do they care?
WindowUtil.disableCrashHandler();
// Setup window events (like callbacks for onWindowClose) and fullscreen keybind setup
WindowUtil.initWindowEvents();
#if FEATURE_DEBUG_TRACY
funkin.util.WindowUtil.initTracy();
#end
#if FEATURE_DEBUG_TRACY
funkin.util.WindowUtil.initTracy();
#end
#if FEATURE_HAPTICS
// Setup Haptic feedback
extension.haptics.Haptic.initialize();
#end
#if FEATURE_HAPTICS
// Setup Haptic feedback
extension.haptics.Haptic.initialize();
#end
#if FEATURE_MOBILE_ADVERTISEMENTS
// Setup Admob
funkin.mobile.util.AdMobUtil.init();
#end
#if FEATURE_MOBILE_ADVERTISEMENTS
// Setup Admob
funkin.mobile.util.AdMobUtil.init();
#end
#if FEATURE_MOBILE_IAP
// Setup In-App purchases
funkin.mobile.util.InAppPurchasesUtil.init();
#end
#if FEATURE_MOBILE_IAP
// Setup In-App purchases
funkin.mobile.util.InAppPurchasesUtil.init();
#end
#if FEATURE_MOBILE_IAR
// Setup In-App purchases
funkin.mobile.util.InAppReviewUtil.init();
#end
#if FEATURE_MOBILE_IAR
// Setup In-App reviews
funkin.mobile.util.InAppReviewUtil.init();
#end
#if ios
// Setup Audio session
funkin.mobile.external.ios.AudioSession.initialize();
#end
#if android
// Setup Callback util.
funkin.external.android.CallbackUtil.init();
#end
// This ain't a pixel art game! (most of the time)
FlxSprite.defaultAntialiasing = true;
#if ios
// Setup Audio session
funkin.external.ios.AudioSession.initialize();
#end
// Disable default keybinds for volume (we manually control volume in MusicBeatState with custom binds)
FlxG.sound.volumeUpKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.muteKeys = [];
// This ain't a pixel art game! (most of the time)
FlxSprite.defaultAntialiasing = true;
// 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
#if !mobile
FlxG.game.soundTray.active = true;
#end
// Disable default keybinds for volume (we manually control volume in MusicBeatState with custom binds)
FlxG.sound.volumeUpKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.muteKeys = [];
// Set the game to a lower frame rate while it is in the background.
FlxG.game.focusLostFramerate = 30;
// 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
#if !mobile
FlxG.game.soundTray.active = true;
#end
// Makes Flixel use frame times instead of locked movements per frame for things like tweens
FlxG.fixedTimestep = false;
// Set the game to a lower frame rate while it is in the background.
FlxG.game.focusLostFramerate = 30;
setupFlixelDebug();
// Makes Flixel use frame times instead of locked movements per frame for things like tweens
FlxG.fixedTimestep = false;
//
// FLIXEL TRANSITIONS
//
setupFlixelDebug();
// Diamond Transition
var diamond:FlxGraphic = FlxGraphic.fromClass(GraphicTransTileDiamond);
diamond.persist = true;
diamond.destroyOnNoUse = false;
//
// FLIXEL TRANSITIONS
//
// NOTE: tileData is ignored if TransitionData.type is FADE instead of TILES.
var tileData:TransitionTileData = {asset: diamond, width: 32, height: 32};
// Diamond Transition
var diamond:FlxGraphic = FlxGraphic.fromClass(GraphicTransTileDiamond);
diamond.persist = true;
diamond.destroyOnNoUse = false;
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData,
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
// Don't play transition in when entering the title state.
FlxTransitionableState.skipNextTransIn = true;
// NOTE: tileData is ignored if TransitionData.type is FADE instead of TILES.
var tileData:TransitionTileData = {asset: diamond, width: 32, height: 32};
FlxG.signals.gameResized.add(function(width:Int, height:Int) {
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData,
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
});
// SDL for some reason enables VSync on focus lost/gained in Android
// Since we don't really need VSync on Android we're gonna forcefully disable it on these signals for now
// This is fixed on SDL3 from what I've heared but that doodoo isn't working poperly for Android
#if android
FlxG.signals.focusLost.add(function() {
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
});
FlxG.signals.focusGained.add(function() {
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
});
#end
FlxG.signals.gameResized.add(function(width:Int, height:Int) {
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData,
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
});
//
// NEWGROUNDS API SETUP
//
#if FEATURE_NEWGROUNDS
NewgroundsClient.instance.init();
#end
// SDL for some reason enables VSync on focus lost/gained in Android
// Since we don't really need VSync on Android we're gonna forcefully disable it on these signals for now
// This is fixed on SDL3 from what I've heared but that doodoo isn't working poperly for Android
#if android
FlxG.signals.focusLost.add(function() {
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
});
FlxG.signals.focusGained.add(function() {
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
});
#end
//
// DISCORD API SETUP
//
#if FEATURE_DISCORD_RPC
DiscordClient.instance.init();
//
// NEWGROUNDS API SETUP
//
#if FEATURE_NEWGROUNDS
NewgroundsClient.instance.init();
#end
lime.app.Application.current.onExit.add(function(exitCode) {
DiscordClient.instance.shutdown();
});
#end
//
// DISCORD API SETUP
//
#if FEATURE_DISCORD_RPC
DiscordClient.instance.init();
//
// ANDROID SETUP
//
#if android
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
funkin.mobile.external.android.CallbackUtil.init();
#end
lime.app.Application.current.onExit.add(function(exitCode) {
DiscordClient.instance.shutdown();
});
#end
//
// FLIXEL PLUGINS
//
// Plugins provide a useful interface for globally active Flixel objects,
// that receive update events regardless of the current state.
// TODO: Move scripted Module behavior to a Flixel plugin.
#if FEATURE_DEBUG_FUNCTIONS
funkin.util.plugins.MemoryGCPlugin.initialize();
#end
#if FEATURE_SCREENSHOTS
funkin.util.plugins.ScreenshotPlugin.initialize();
#end
#if FEATURE_NEWGROUNDS
funkin.util.plugins.NewgroundsMedalPlugin.initialize();
#end
funkin.util.plugins.EvacuateDebugPlugin.initialize();
funkin.util.plugins.ForceCrashPlugin.initialize();
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
#if !mobile
funkin.util.plugins.VolumePlugin.initialize();
#end
funkin.util.plugins.WatchPlugin.initialize();
#if mobile
funkin.util.plugins.TouchPointerPlugin.initialize();
funkin.mobile.input.ControlsHandler.initInputTrackers();
#end
//
// ANDROID SETUP
//
#if android
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
#end
//
// FLIXEL PLUGINS
//
// Plugins provide a useful interface for globally active Flixel objects,
// that receive update events regardless of the current state.
// TODO: Move scripted Module behavior to a Flixel plugin.
#if FEATURE_DEBUG_FUNCTIONS
funkin.util.plugins.MemoryGCPlugin.initialize();
#end
#if FEATURE_SCREENSHOTS
funkin.util.plugins.ScreenshotPlugin.initialize();
#end
#if FEATURE_NEWGROUNDS
funkin.util.plugins.NewgroundsMedalPlugin.initialize();
#end
funkin.util.plugins.EvacuateDebugPlugin.initialize();
funkin.util.plugins.ForceCrashPlugin.initialize();
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
#if !mobile
funkin.util.plugins.VolumePlugin.initialize();
#end
funkin.util.plugins.WatchPlugin.initialize();
#if mobile
funkin.util.plugins.TouchPointerPlugin.initialize();
funkin.mobile.input.ControlsHandler.initInputTrackers();
#end
_coreInitialized = true;
}
//
// GAME DATA PARSING
@ -279,6 +290,9 @@ class InitState extends FlxState
*/
function startGame():Void
{
// Don't play transition in when entering the title state.
FlxTransitionableState.skipNextTransIn = true;
#if SONG
// -DSONG=bopeebo
startSong(defineSong(), defineDifficulty());
@ -386,7 +400,7 @@ class InitState extends FlxState
*/
function startSong(songId:String, difficultyId:String = 'normal'):Void
{
var songData:Null<funkin.play.song.Song> = funkin.data.song.SongRegistry.instance.fetchEntry(songId);
var songData:Null<funkin.play.song.Song> = funkin.data.song.SongRegistry.instance.fetchEntry(songId, {variation: Constants.DEFAULT_VARIATION});
if (songData == null)
{
@ -458,7 +472,7 @@ class InitState extends FlxState
var targetSong:Null<funkin.play.song.Song> = null;
if (targetSongId != null) targetSong = SongRegistry.instance.fetchEntry(targetSongId);
if (targetSongId != null) targetSong = SongRegistry.instance.fetchEntry(targetSongId, {variation: Constants.DEFAULT_VARIATION});
if (targetSongId == null)
{

View file

@ -1,5 +1,6 @@
package funkin.api.discord;
import funkin.util.macro.EnvironmentConfigMacro;
#if FEATURE_DISCORD_RPC
import hxdiscord_rpc.Discord;
import hxdiscord_rpc.Types.DiscordButton;
@ -11,7 +12,7 @@ import sys.thread.Thread;
@:nullSafety
class DiscordClient
{
static final CLIENT_ID:String = "816168432860790794";
static final CLIENT_ID:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("DESKTOP_DISCORD_CLIENT_ID");
public static var instance(get, never):DiscordClient;
static var _instance:Null<DiscordClient> = null;
@ -40,12 +41,28 @@ class DiscordClient
{
trace('[DISCORD] Initializing connection...');
// Discord.initialize(CLIENT_ID, handlers, true, null);
Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
if (!hasValidCredentials())
{
FlxG.log.warn("Tried to initialize Discord connection, but credentials are invalid!");
return;
}
@:nullSafety(Off)
{
Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
}
createDaemon();
}
/**
* @returns `false` if the client ID is invalid.
*/
static function hasValidCredentials():Bool
{
return !(CLIENT_ID == null || CLIENT_ID == "" || (CLIENT_ID != null && CLIENT_ID.contains(" ")));
}
var daemon:Null<Thread> = null;
function createDaemon():Void

View file

@ -0,0 +1,156 @@
package funkin.api.newgrounds;
#if FEATURE_NEWGROUNDS
import io.newgrounds.utils.SaveSlotList;
import io.newgrounds.objects.SaveSlot;
import io.newgrounds.Call.CallError;
import io.newgrounds.objects.events.Outcome;
import funkin.save.Save;
@:nullSafety
@:access(funkin.save.Save)
class NGSaveSlot
{
public static var instance(get, never):NGSaveSlot;
static var _instance:Null<NGSaveSlot> = null;
static function get_instance():NGSaveSlot
{
if (_instance == null)
{
return loadInstance();
}
return _instance;
}
public static function loadInstance():NGSaveSlot
{
var loadedSave:NGSaveSlot = loadSlot(Save.BASE_SAVE_SLOT);
if (_instance == null) _instance = loadedSave;
return loadedSave;
}
static function loadSlot(slot:Int):NGSaveSlot
{
trace('[NEWGROUNDS] Getting save slot from ID $slot');
var saveSlot:Null<SaveSlot> = NewgroundsClient.instance.saveSlots?.getById(slot);
var saveSlotObj:NGSaveSlot = new NGSaveSlot(saveSlot);
return saveSlotObj;
}
public var ngSaveSlot:Null<SaveSlot> = null;
public function new(?ngSaveSlot:Null<SaveSlot>)
{
this.ngSaveSlot = ngSaveSlot;
#if FLX_DEBUG
FlxG.console.registerClass(NGSaveSlot);
FlxG.console.registerClass(Save);
#end
}
/**
* Saves `data` to the newgrounds save slot.
* @param data The raw save data.
*/
public function save(data:RawSaveData):Void
{
var encodedData:String = haxe.Serializer.run(data);
try
{
ngSaveSlot?.save(encodedData, function(outcome:Outcome<CallError>) {
switch (outcome)
{
case SUCCESS:
trace('[NEWGROUNDS] Successfully saved save data to save slot!');
case FAIL(error):
trace('[NEWGROUNDS] Failed to save data to save slot!');
trace(error);
}
});
}
catch (error:String)
{
trace('[NEWGROUNDS] Failed to save data to save slot!');
trace(error);
}
}
public function load(?onComplete:Null<Dynamic->Void>, ?onError:Null<CallError->Void>):Void
{
try
{
ngSaveSlot?.load(function(outcome:SaveSlotOutcome):Void {
switch (outcome)
{
case SUCCESS(value):
trace('[NEWGROUNDS] Loaded save slot with the ID of ${ngSaveSlot?.id}!');
#if FEATURE_DEBUG_FUNCTIONS
trace('Save Slot Data:');
trace(value);
#end
if (onComplete != null && value != null)
{
var decodedData:Dynamic = haxe.Unserializer.run(value);
onComplete(decodedData);
}
case FAIL(error):
trace('[NEWGROUNDS] Failed to load save slot with the ID of ${ngSaveSlot?.id}!');
trace(error);
if (onError != null)
{
onError(error);
}
}
});
}
catch (error:String)
{
trace('[NEWGROUNDS] Failed to load save slot with the ID of ${ngSaveSlot?.id}!');
trace(error);
if (onError != null)
{
onError(RESPONSE({message: error, code: 500}));
}
}
}
public function clear():Void
{
try
{
ngSaveSlot?.clear(function(outcome:Outcome<CallError>) {
switch (outcome)
{
case SUCCESS:
trace('[NEWGROUNDS] Successfully cleared save slot!');
case FAIL(error):
trace('[NEWGROUNDS] Failed to clear save slot!');
trace(error);
}
});
}
catch (error:String)
{
trace('[NEWGROUNDS] Failed to clear save slot!');
trace(error);
}
}
public function checkSlot():Void
{
trace('[NEWGROUNDS] Checking save slot with the ID of ${ngSaveSlot?.id}...');
trace(' Is null? ${ngSaveSlot == null}');
trace(' Is empty? ${ngSaveSlot?.isEmpty() ?? false}');
}
}
#end

View file

@ -1,5 +1,6 @@
package funkin.api.newgrounds;
import funkin.util.macro.EnvironmentConfigMacro;
import funkin.save.Save;
import funkin.api.newgrounds.Medals.Medal;
#if FEATURE_NEWGROUNDS
@ -10,13 +11,18 @@ import io.newgrounds.NGLite.LoginOutcome;
import io.newgrounds.NGLite.LoginFail;
import io.newgrounds.objects.events.Outcome;
import io.newgrounds.utils.MedalList;
import io.newgrounds.utils.SaveSlotList;
import io.newgrounds.utils.ScoreBoardList;
import io.newgrounds.objects.User;
@:nullSafety
class NewgroundsClient
{
static final APP_ID:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("API_NG_APP_ID");
static final ENCRYPTION_KEY:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("API_NG_ENC_KEY");
public static var instance(get, never):NewgroundsClient;
static var _instance:Null<NewgroundsClient> = null;
static function get_instance():NewgroundsClient
@ -29,14 +35,15 @@ class NewgroundsClient
public var user(get, never):Null<User>;
public var medals(get, never):Null<MedalList>;
public var leaderboards(get, never):Null<ScoreBoardList>;
public var saveSlots(get, never):Null<SaveSlotList>;
private function new()
{
trace('[NEWGROUNDS] Initializing client...');
#if FEATURE_NEWGROUNDS_DEBUG
trace('[NEWGROUNDS] App ID: ${NewgroundsCredentials.APP_ID}');
trace('[NEWGROUNDS] Encryption Key: ${NewgroundsCredentials.ENCRYPTION_KEY}');
trace('[NEWGROUNDS] App ID: ${APP_ID}');
trace('[NEWGROUNDS] Encryption Key: ${ENCRYPTION_KEY}');
#end
if (!hasValidCredentials())
@ -45,9 +52,12 @@ class NewgroundsClient
return;
}
var debug = #if FEATURE_NEWGROUNDS_DEBUG true #else false #end;
NG.create(NewgroundsCredentials.APP_ID, getSessionId(), debug, onLoginResolved);
NG.core.setupEncryption(NewgroundsCredentials.ENCRYPTION_KEY);
@:nullSafety(Off)
{
NG.create(APP_ID, getSessionId(), #if FEATURE_NEWGROUNDS_DEBUG true #else false #end, onLoginResolved);
NG.core.setupEncryption(ENCRYPTION_KEY);
}
}
public function init()
@ -166,12 +176,12 @@ class NewgroundsClient
*/
static function hasValidCredentials():Bool
{
return !(NewgroundsCredentials.APP_ID == null
|| NewgroundsCredentials.APP_ID == ""
|| NewgroundsCredentials.APP_ID.contains(" ")
|| NewgroundsCredentials.ENCRYPTION_KEY == null
|| NewgroundsCredentials.ENCRYPTION_KEY == ""
|| NewgroundsCredentials.ENCRYPTION_KEY.contains(" "));
return !(APP_ID == null
|| APP_ID == ""
|| (APP_ID != null && APP_ID.contains(" "))
|| ENCRYPTION_KEY == null
|| ENCRYPTION_KEY == ""
|| (ENCRYPTION_KEY != null && ENCRYPTION_KEY.contains(" ")));
}
function onLoginResolved(outcome:LoginOutcome):Void
@ -236,6 +246,8 @@ class NewgroundsClient
trace('[NEWGROUNDS] Submitting leaderboard request...');
NG.core.scoreBoards.loadList(onFetchedLeaderboards);
trace('[NEWGROUNDS] Submitting save slot request...');
NG.core.saveSlots.loadList(onFetchedSaveSlots);
}
function onLoginFailed(result:LoginFail):Void
@ -301,6 +313,13 @@ class NewgroundsClient
// trace(funkin.api.newgrounds.Leaderboards.listLeaderboardData());
}
function onFetchedSaveSlots(outcome:Outcome<CallError>):Void
{
trace('[NEWGROUNDS] Fetched save slots!');
NGSaveSlot.instance.checkSlot();
}
function get_user():Null<User>
{
if (NG.core == null || !this.isLoggedIn()) return null;
@ -319,6 +338,12 @@ class NewgroundsClient
return NG.core.scoreBoards;
}
function get_saveSlots():Null<SaveSlotList>
{
if (NG.core == null || !this.isLoggedIn()) return null;
return NG.core.saveSlots;
}
static function getSessionId():Null<String>
{
#if js

View file

@ -72,7 +72,7 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
override function set_volume(value:Float):Float
{
// Uncap the volume.
_volume = FlxMath.bound(value, 0.0, MAX_VOLUME);
_volume = value.clamp(0.0, MAX_VOLUME);
updateTransform();
return _volume;
}
@ -568,8 +568,8 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
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 pan:Float = (SoundMixer.__soundTransform.pan + _transform.pan).clamp(-1, 1);
var volume:Float = (SoundMixer.__soundTransform.volume * _transform.volume).clamp(0, MAX_VOLUME);
var audioSource:AudioSource = new AudioSource(_sound.__buffer);
audioSource.offset = Std.int(startTime);

View file

@ -5,20 +5,21 @@ import funkin.util.VersionUtil;
import haxe.Constraints.Constructible;
/**
* The entry's constructor function must take a single argument, the entry's ID.
* The entry's constructor function takes 2 arguments, the entry ID and optional parameters.
*/
typedef EntryConstructorFunction = String->Void;
typedef EntryConstructorFunction = (String, ?Dynamic) -> Void;
/**
* A base type for a Registry, which is an object which handles loading scriptable objects.
*
* @param T The type to construct. Must implement `IRegistryEntry`.
* @param J The type of the JSON data used when constructing.
* @param P The type of the parameters used for `fetchEntry()`.
*/
@:nullSafety
@:generic
@:autoBuild(funkin.util.macro.DataRegistryMacro.buildRegistry())
abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructorFunction>), J>
abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructorFunction>), J, P>
{
/**
* The ID of the registry. Used when logging.
@ -186,7 +187,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
* @param id The ID of the entry to fetch.
* @return The entry, or `null` if it does not exist.
*/
public function fetchEntry(id:String):Null<T>
public function fetchEntry(id:String, ?params:Null<P>):Null<T>
{
return entries.get(id);
}

View file

@ -99,7 +99,7 @@ class DataParse
case JArray(values):
return Either.Left(legacyNoteSectionArray(json, name));
case JObject(fields):
return Either.Right(cast Tools.getValue(json));
return Either.Right(legacyNoteData(json, name));
default:
throw 'Expected property $name to be note data, but it was ${json.value}.';
}
@ -121,7 +121,7 @@ class DataParse
}
}
public static function backdropData(json:Json, name:String):funkin.data.dialogue.conversation.ConversationData.BackdropData
public static function backdropData(json:Json, name:String):funkin.data.dialogue.ConversationData.BackdropData
{
switch (json.value)
{
@ -152,7 +152,7 @@ class DataParse
}
}
public static function outroData(json:Json, name:String):Null<funkin.data.dialogue.conversation.ConversationData.OutroData>
public static function outroData(json:Json, name:String):Null<funkin.data.dialogue.ConversationData.OutroData>
{
switch (json.value)
{

View file

@ -0,0 +1,39 @@
# Dialogue Conversation Data Schema Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0]
Initial release.
# Dialogue Box Data Schema Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.0]
### Added
- Added an option to specify the font used by the dialogue box. Defaults to `Arial` if unspecified.
## [1.0.0]
Initial release.
# Dialogue Speaker Data Schema Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0]
Initial release.

View file

@ -1,4 +1,4 @@
package funkin.data.dialogue.conversation;
package funkin.data.dialogue;
/**
* A type definition for the data for a specific conversation.

View file

@ -1,4 +1,4 @@
package funkin.data.dialogue.conversation;
package funkin.data.dialogue;
import funkin.play.cutscene.dialogue.Conversation;
import funkin.play.cutscene.dialogue.ScriptedConversation;
@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class ConversationRegistry extends BaseRegistry<Conversation, ConversationData> implements ISingleton implements DefaultRegistryImpl
class ConversationRegistry extends BaseRegistry<Conversation, ConversationData, ConversationEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the dialogue box data format.
@ -22,3 +22,8 @@ class ConversationRegistry extends BaseRegistry<Conversation, ConversationData>
super('CONVERSATION', 'dialogue/conversations', CONVERSATION_DATA_VERSION_RULE);
}
}
typedef ConversationEntryParams =
{
var placeholder:String;
}

View file

@ -1,4 +1,4 @@
package funkin.data.dialogue.dialoguebox;
package funkin.data.dialogue;
import funkin.data.animation.AnimationData;

View file

@ -1,13 +1,13 @@
package funkin.data.dialogue.dialoguebox;
package funkin.data.dialogue;
import funkin.play.cutscene.dialogue.DialogueBox;
import funkin.data.dialogue.dialoguebox.DialogueBoxData;
import funkin.data.dialogue.DialogueBoxData;
import funkin.play.cutscene.dialogue.ScriptedDialogueBox;
import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData> implements ISingleton implements DefaultRegistryImpl
class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData, DialogueBoxEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the dialogue box data format.
@ -23,3 +23,5 @@ class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData> imp
super('DIALOGUEBOX', 'dialogue/boxes', DIALOGUEBOX_DATA_VERSION_RULE);
}
}
typedef DialogueBoxEntryParams = Dynamic;

View file

@ -1,4 +1,4 @@
package funkin.data.dialogue.speaker;
package funkin.data.dialogue;
import funkin.data.animation.AnimationData;

View file

@ -1,4 +1,4 @@
package funkin.data.dialogue.speaker;
package funkin.data.dialogue;
import funkin.play.cutscene.dialogue.Speaker;
import funkin.play.cutscene.dialogue.ScriptedSpeaker;
@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData> implements ISingleton implements DefaultRegistryImpl
class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData, SpeakerEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the speaker data format.
@ -22,3 +22,5 @@ class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData> implements ISin
super('SPEAKER', 'dialogue/speakers', SPEAKER_DATA_VERSION_RULE);
}
}
typedef SpeakerEntryParams = {}

View file

@ -1,9 +0,0 @@
# Dialogue Conversation Data Schema Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0]
Initial release.

View file

@ -1,13 +0,0 @@
# Dialogue Box Data Schema Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.0]
### Added
- Added an option to specify the font used by the dialogue box. Defaults to `Arial` if unspecified.
## [1.0.0]
Initial release.

View file

@ -1,9 +0,0 @@
# Dialogue Speaker Data Schema Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0]
Initial release.

View file

@ -7,7 +7,7 @@ import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class AlbumRegistry extends BaseRegistry<Album, AlbumData> implements ISingleton implements DefaultRegistryImpl
class AlbumRegistry extends BaseRegistry<Album, AlbumData, AlbumEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the album data format.
@ -23,3 +23,5 @@ class AlbumRegistry extends BaseRegistry<Album, AlbumData> implements ISingleton
super('ALBUM', 'ui/freeplay/albums', ALBUM_DATA_VERSION_RULE);
}
}
typedef AlbumEntryParams = {}

View file

@ -8,7 +8,7 @@ import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData> implements ISingleton implements DefaultRegistryImpl
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData, PlayerEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the stage data format.
@ -147,3 +147,5 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData> impleme
#end
}
}
typedef PlayerEntryParams = {}

View file

@ -7,7 +7,8 @@ import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleData> implements ISingleton implements DefaultRegistryImpl
class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleData, FreeplayStyleEntryParams> implements ISingleton
implements DefaultRegistryImpl
{
/**
* The current version string for the style data format.
@ -23,3 +24,5 @@ class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleDat
super('FREEPLAYSTYLE', 'ui/freeplay/styles', FREEPLAYSTYLE_DATA_VERSION_RULE);
}
}
typedef FreeplayStyleEntryParams = {}

View file

@ -165,7 +165,7 @@ typedef NoteStyleAssetData<T> =
var assetPath:String;
/**
* The scale to render the prop at.
* The scale to render the note at.
* @default 1.0
*/
@:default(1.0)
@ -181,7 +181,7 @@ typedef NoteStyleAssetData<T> =
var offsets:Null<Array<Float>>;
/**
* If true, the prop is a pixel sprite, and will be rendered without anti-aliasing.
* If true, the note is a pixel sprite, and will be rendered without anti-aliasing.
*/
@:default(false)
@:optional

View file

@ -7,7 +7,7 @@ import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData> implements ISingleton implements DefaultRegistryImpl
class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData, NoteStyleEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the note style data format.
@ -30,3 +30,5 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData> implement
return notestyle;
}
}
typedef NoteStyleEntryParams = {}

View file

@ -1117,6 +1117,23 @@ class SongNoteDataRaw implements ICloneable<SongNoteDataRaw>
return 'SongNoteData(${this.time}ms, ' + (this.length > 0 ? '[${this.length}ms hold]' : '') + ' ${this.data}'
+ (this.kind != '' ? ' [kind: ${this.kind}])' : ')');
}
public function buildTooltip():String
{
if ((this.kind?.length ?? 0) == 0) return "";
var result:String = 'Kind: ${this.kind}';
if (this.params.length == 0) return result;
result += "\nParams:";
for (param in params)
{
result += '\n- ${param.name}: ${param.value}';
}
return result;
}
}
/**

View file

@ -15,8 +15,7 @@ import funkin.data.DefaultRegistryImpl;
using funkin.data.song.migrator.SongDataMigrator;
@:nullSafety
class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleton implements DefaultRegistryImpl
@:nullSafety class SongRegistry extends BaseRegistry<Song, SongMetadata, SongEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the stage data format.
@ -37,6 +36,8 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
public static var DEFAULT_GENERATEDBY(get, never):String;
public var scriptedSongVariations:Map<String, Song> = new Map<String, Song>();
static function get_DEFAULT_GENERATEDBY():String
{
return '${Constants.TITLE} - ${Constants.VERSION}';
@ -63,9 +64,17 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
if (entry != null)
{
log('Successfully created scripted entry (${entryCls} = ${entry.id})');
entries.set(entry.id, entry);
scriptedEntryIds.set(entry.id, entryCls);
if (entry.variation != null)
{
scriptedSongVariations.set('${entry.id}:${entry.variation}', entry);
log('Successfully created scripted entry (${entryCls} = ${entry.id}, ${entry.variation})');
}
else
{
entries.set(entry.id, entry);
scriptedEntryIds.set(entry.id, entryCls);
log('Successfully created scripted entry (${entryCls} = ${entry.id})');
}
}
else
{
@ -120,6 +129,28 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
return parseEntryMetadataRaw(contents);
}
/**
* We override `fetchEntry` to handle song variations!
*/
public override function fetchEntry(id:String, ?params:SongEntryParams):Null<Song>
{
var variation:String = params?.variation ?? Constants.DEFAULT_VARIATION;
if (variation != Constants.DEFAULT_VARIATION)
{
if (scriptedSongVariations.exists('${id}:${variation}'))
{
var variationSongScript:Null<Song> = scriptedSongVariations.get('${id}:${variation}');
if (variationSongScript != null)
{
return variationSongScript;
}
}
}
return super.fetchEntry(id, params);
}
public function parseEntryMetadata(id:String, ?variation:String):Null<SongMetadata>
{
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
@ -517,3 +548,11 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
return allDifficulties;
}
}
typedef SongEntryParams =
{
/**
* The variation ID for the song.
*/
var variation:String;
}

View file

@ -10,6 +10,8 @@ class ChartManifestData
*/
public static final CHART_MANIFEST_DATA_VERSION:thx.semver.Version = "1.0.0";
public static final invalidIdRegex:EReg = ~/[\/\\:*?"<>|]/g;
@:default(funkin.data.song.importer.ChartManifestData.CHART_MANIFEST_DATA_VERSION)
@:jcustomparse(funkin.data.DataParse.semverVersion)
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
@ -19,7 +21,11 @@ class ChartManifestData
* The internal song ID for this chart.
* The metadata and chart data file names are derived from this.
*/
public var songId:String;
public var songId(default, set):String;
public function set_songId(value:String):String
{
return songId = invalidIdRegex.replace(value.trim(), '');
}
public function new(songId:String)
{

View file

@ -120,6 +120,11 @@ class LegacyNote
public inline function getKind():String
{
return this.alt ? 'alt' : 'normal';
return this.alt ? 'alt' : '';
}
public function toString():String
{
return 'LegacyNote(${this.time}, ${this.data}, ${this.length}, ${this.alt})';
}
}

View file

@ -95,8 +95,8 @@ class FNFLegacyImporter
switch (songData.song.speed)
{
case Left(speed):
// All difficulties will use the one scroll speed.
songChartData.scrollSpeed.set('default', speed);
// Sets the scroll speed for the difficulty.
songChartData.scrollSpeed.set(difficulty, speed);
case Right(speeds):
if (speeds.easy != null) songChartData.scrollSpeed.set('easy', speeds.easy);
if (speeds.normal != null) songChartData.scrollSpeed.set('normal', speeds.normal);

View file

@ -6,7 +6,7 @@ import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class StageRegistry extends BaseRegistry<Stage, StageData> implements ISingleton implements DefaultRegistryImpl
class StageRegistry extends BaseRegistry<Stage, StageData, StageEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the stage data format.
@ -22,3 +22,5 @@ class StageRegistry extends BaseRegistry<Stage, StageData> implements ISingleton
super('STAGE', 'stages', STAGE_DATA_VERSION_RULE);
}
}
typedef StageEntryParams = {}

View file

@ -5,7 +5,7 @@ import funkin.ui.transition.stickers.StickerPack;
import funkin.ui.transition.stickers.ScriptedStickerPack;
@:nullSafety
class StickerRegistry extends BaseRegistry<StickerPack, StickerData>
class StickerRegistry extends BaseRegistry<StickerPack, StickerData, StickerEntryParams>
{
/**
* The current version string for the sticker pack data format.
@ -90,3 +90,5 @@ class StickerRegistry extends BaseRegistry<StickerPack, StickerData>
return ScriptedStickerPack.listScriptClasses();
}
}
typedef StickerEntryParams = {}

View file

@ -7,7 +7,7 @@ import funkin.util.tools.ISingleton;
import funkin.data.DefaultRegistryImpl;
@:nullSafety
class LevelRegistry extends BaseRegistry<Level, LevelData> implements ISingleton implements DefaultRegistryImpl
class LevelRegistry extends BaseRegistry<Level, LevelData, LevelEntryParams> implements ISingleton implements DefaultRegistryImpl
{
/**
* The current version string for the level data format.
@ -56,3 +56,5 @@ class LevelRegistry extends BaseRegistry<Level, LevelData> implements ISingleton
return result;
}
}
typedef LevelEntryParams = {}

View file

@ -1,5 +1,6 @@
package funkin.mobile.external.android;
package funkin.external.android;
#if android
import lime.system.JNI;
import flixel.util.FlxSignal;
@ -9,7 +10,6 @@ import flixel.util.FlxSignal;
@:unreflective
class CallbackUtil
{
#if android
/**
* The result code for `DATA_FOLDER_CLOSED` activity.
*/
@ -42,7 +42,6 @@ class CallbackUtil
initCallBackJNI(new CallbackHandler());
}
}
#end
}
/**
@ -50,8 +49,7 @@ class CallbackUtil
*/
class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
{
#if android
@:allow(funkin.mobile.external.android.CallbackUtil)
@:allow(funkin.external.android.CallbackUtil)
function new():Void {}
/**
@ -68,5 +66,5 @@ class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
{
if (CallbackUtil.onActivityResult != null) CallbackUtil.onActivityResult.dispatch(requestCode, resultCode);
}
#end
}
#end

View file

@ -1,12 +1,12 @@
package funkin.mobile.external.android;
package funkin.external.android;
#if android
/**
* A Utility class to manage the Application's Data folder on Android.
*/
@:unreflective
class DataFolderUtil
{
#if android
/**
* Opens the data folder on an Android device using JNI.
*/
@ -19,5 +19,5 @@ class DataFolderUtil
openDataFolderJNI(CallbackUtil.DATA_FOLDER_CLOSED);
}
}
#end
}
#end

View file

@ -1,4 +1,4 @@
package funkin.mobile.external.android;
package funkin.external.android;
#if android
import lime.system.JNI;

View file

@ -1,5 +1,6 @@
package funkin.mobile.external.android;
package funkin.external.android;
#if android
import lime.math.Rectangle;
import lime.system.JNI;
@ -9,7 +10,6 @@ import lime.system.JNI;
@:unreflective
class ScreenUtil
{
#if android
/**
* Retrieves the dimensions of display cutouts (such as notches) on Android devices.
*
@ -48,5 +48,5 @@ class ScreenUtil
return [];
}
#end
}
#end

View file

@ -1,9 +1,10 @@
package funkin.mobile.external.ios;
package funkin.external.ios;
#if ios
/**
* A Utility class to manage iOS audio.
*/
@:build(funkin.mobile.macros.LinkerMacro.xml('project/Build.xml'))
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
@:include('AudioSession.hpp')
@:unreflective
extern class AudioSession
@ -13,3 +14,4 @@ extern class AudioSession
@:native('setActive')
static function setActive(active:Bool):Void;
}
#end

View file

@ -1,9 +1,10 @@
package funkin.mobile.external.ios;
package funkin.external.ios;
#if ios
/**
* A Utility class to get iOS screen related informations.
*/
@:build(funkin.mobile.macros.LinkerMacro.xml('project/Build.xml'))
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
@:include('ScreenUtil.hpp')
@:unreflective
extern class ScreenUtil
@ -14,3 +15,4 @@ extern class ScreenUtil
@:native('getScreenSize')
static function getScreenSize(width:cpp.RawPointer<Float>, height:cpp.RawPointer<Float>):Void;
}
#end

View file

@ -0,0 +1,75 @@
package funkin.external.windows;
#if (windows && cpp)
/**
* This class provides handling for Windows API-related functions.
*/
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
@:include('winapi.hpp')
extern class WinAPI
{
/**
* Shows a message box with an error icon.
*
* @param message The message to display.
* @param title The title of the message box.
*/
@:native('WINAPI_ShowError')
static function showError(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
/**
* Shows a message box with a warning icon.
*
* @param message The message to display.
* @param title The title of the message box.
*/
@:native('WINAPI_ShowWarning')
static function showWarning(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
/**
* Shows a message box with an information icon.
*
* @param message The message to display.
* @param title The title of the message box.
*/
@:native('WINAPI_ShowInformation')
static function showInformation(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
/**
* Shows a message box with a question icon.
*
* @param message The message to display.
* @param title The title of the message box.
*/
@:native('WINAPI_ShowQuestion')
static function showQuestion(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
/**
* Disables the "Report to Microsoft" dialog that appears when the application crashes.
*/
@:native('WINAPI_DisableErrorReporting')
static function disableErrorReporting():Void;
/**
* Disables Windows ghosting, which prevents the system from marking unresponsive windows as "Not Responding."
*/
@:native('WINAPI_DisableWindowsGhosting')
static function disableWindowsGhosting():Void;
/**
* Sets the dark mode for the active window.
*
* @param enable A boolean value indicating whether to enable (true) or disable (false) dark mode.
*/
@:native('WINAPI_SetDarkMode')
static function setDarkMode(enable:Bool):Void;
/**
* Checks if the system is using dark mode.
*
* @return True if system is in dark mode, false otherwise.
*/
@:native('WINAPI_IsSystemDarkMode')
static function isSystemDarkMode():Bool;
}
#end

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<pragma once="true" />
<files id="haxe">
<compilerflag value="-I${this_dir}/include" />
</files>
<files id="__main__">
<compilerflag value="-I${this_dir}/include" />
</files>
<files id="winapi">
<compilerflag value="-I${this_dir}/include" />
<file name="${this_dir}/src/winapi.cpp" />
</files>
<target id="haxe">
<section if="mingw">
<lib name="-luser32" />
<lib name="-lkernel32" />
<lib name="-ldwmapi" />
<lib name="-ladvapi32" />
</section>
<section if="windows" unless="mingw">
<lib name="user32.lib" />
<lib name="kernel32.lib" />
<lib name="dwmapi.lib" />
<lib name="advapi32.lib" />
</section>
<files id="winapi" />
</target>
</xml>

View file

@ -0,0 +1,51 @@
#pragma once
/**
* @brief Shows an error message box
* @param message The error message to display
* @param title The title of the message box
*/
void WINAPI_ShowError(const char *message, const char *title);
/**
* @brief Shows a warning message box
* @param message The warning message to display
* @param title The title of the message box
*/
void WINAPI_ShowWarning(const char *message, const char *title);
/**
* @brief Shows an information message box
* @param message The information message to display
* @param title The title of the message box
*/
void WINAPI_ShowInformation(const char *message, const char *title);
/**
* @brief Shows a question message box with OK/Cancel buttons
* @param message The question message to display
* @param title The title of the message box
*/
void WINAPI_ShowQuestion(const char *message, const char *title);
/**
* @brief Disables Windows error reporting dialogs
*/
void WINAPI_DisableErrorReporting();
/**
* @brief Disables Windows ghosting for the current process
*/
void WINAPI_DisableWindowsGhosting();
/**
* @brief Sets dark mode for the active window
* @param enable True to enable dark mode, false to disable
*/
void WINAPI_SetDarkMode(bool enable);
/**
* @brief Checks if the system is using dark mode
* @return True if system is in dark mode, false otherwise
*/
bool WINAPI_IsSystemDarkMode();

View file

@ -0,0 +1,75 @@
#define WIN32_LEAN_AND_MEAN // Excludes rarely-used APIs like cryptography, DDE, RPC, and shell functions, reducing compile time and binary size.
#define NOMINMAX // Prevents Windows from defining min() and max() macros, which can conflict with standard C++ functions.
#define NOCRYPT // Excludes Cryptographic APIs, such as Encrypt/Decrypt functions.
#define NOCOMM // Excludes serial communication APIs, such as COM port handling.
#define NOKANJI // Excludes Kanji character set support (not needed unless working with Japanese text processing).
#define NOHELP // Excludes Windows Help APIs, removing functions related to WinHelp and other help systems.
#include <windows.h>
#include <psapi.h>
#include <dwmapi.h>
#include <stdint.h>
#include <stdio.h>
void WINAPI_ShowError(const char *message, const char *title)
{
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONERROR);
}
void WINAPI_ShowWarning(const char *message, const char *title)
{
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONWARNING);
}
void WINAPI_ShowInformation(const char *message, const char *title)
{
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONINFORMATION);
}
void WINAPI_ShowQuestion(const char *message, const char *title)
{
MessageBox(GetActiveWindow(), message, title, MB_OKCANCEL | MB_ICONQUESTION);
}
void WINAPI_DisableErrorReporting()
{
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
}
void WINAPI_DisableWindowsGhosting()
{
DisableProcessWindowsGhosting();
}
void WINAPI_SetDarkMode(bool enable)
{
HWND window = GetActiveWindow();
int darkMode = enable ? 1 : 0;
if (DwmSetWindowAttribute(window, 20, &darkMode, sizeof(darkMode)) != S_OK)
DwmSetWindowAttribute(window, 19, &darkMode, sizeof(darkMode));
UpdateWindow(window);
}
bool WINAPI_IsSystemDarkMode()
{
HKEY hKey;
DWORD dwValue = 0;
DWORD dwSize = sizeof(DWORD);
DWORD dwType = REG_DWORD;
if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
if (RegQueryValueEx(hKey, "AppsUseLightTheme", NULL, &dwType, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS)
{
RegCloseKey(hKey);
return dwValue == 0;
}
RegCloseKey(hKey);
}
return false;
}

View file

@ -12,6 +12,7 @@ import flixel.math.FlxPoint;
import flixel.graphics.frames.FlxFrame.FlxFrameAngle;
import flixel.FlxCamera;
import openfl.system.System;
import funkin.FunkinMemory;
using StringTools;
@ -23,20 +24,6 @@ using StringTools;
@:nullSafety
class FunkinSprite extends FlxSprite
{
/**
* An internal list of all the textures cached with `cacheTexture`.
* This excludes any temporary textures like those from `FlxText` or `makeSolidColor`.
*/
static var currentCachedTextures:Map<String, FlxGraphic> = [];
/**
* An internal list of textures that were cached in the previous state.
* We don't know whether we want to keep them cached or not.
*/
static var previousCachedTextures:Map<String, FlxGraphic> = [];
static var permanentCachedTextures:Map<String, FlxGraphic> = [];
/**
* @param x Starting X position
* @param y Starting Y position
@ -216,122 +203,40 @@ class FunkinSprite extends FlxSprite
return FlxG.bitmap.get(key) != null;
}
/**
* Ensure the texture with the given key is cached.
* @param key The key of the texture to cache.
*/
@:deprecated("Use FunkinMemory.cacheTexture() instead")
public static function cacheTexture(key:String):Void
{
// We don't want to cache the same texture twice.
if (currentCachedTextures.exists(key)) return;
if (previousCachedTextures.exists(key))
{
// Move the graphic from the previous cache to the current cache.
var graphic = previousCachedTextures.get(key);
previousCachedTextures.remove(key);
if (graphic != null) currentCachedTextures.set(key, graphic);
return;
}
// Else, texture is currently uncached.
var graphic:FlxGraphic = FlxGraphic.fromAssetKey(key, false, null, true);
if (graphic == null)
{
FlxG.log.warn('Failed to cache graphic: $key');
}
else
{
trace('Successfully cached graphic: $key');
graphic.persist = true;
currentCachedTextures.set(key, graphic);
}
FunkinMemory.cacheTexture(Paths.image(key));
}
@:deprecated("Use FunkinMemory.permanentCacheTexture() instead")
public static function permanentCacheTexture(key:String):Void
{
// We don't want to cache the same texture twice.
if (permanentCachedTextures.exists(key)) return;
// Else, texture is currently uncached.
var graphic:FlxGraphic = FlxGraphic.fromAssetKey(key, false, null, true);
if (graphic == null)
{
FlxG.log.warn('Failed to cache graphic: $key');
}
else
{
trace('Successfully cached graphic: $key');
graphic.persist = true;
permanentCachedTextures.set(key, graphic);
}
currentCachedTextures = permanentCachedTextures;
@:privateAccess FunkinMemory.permanentCacheTexture(Paths.image(key));
}
@:deprecated("Use FunkinMemory.cacheTexture() instead")
public static function cacheSparrow(key:String):Void
{
cacheTexture(Paths.image(key));
FunkinMemory.cacheTexture(Paths.image(key));
}
@:deprecated("Use FunkinMemory.cacheTexture() instead")
public static function cachePacker(key:String):Void
{
cacheTexture(Paths.image(key));
FunkinMemory.cacheTexture(Paths.image(key));
}
/**
* Call this, then `cacheTexture` to keep the textures we still need, then `purgeCache` to remove the textures that we won't be using anymore.
*/
@:deprecated("Use FunkinMemory.preparePurgeTextureCache() instead")
public static function preparePurgeCache():Void
{
previousCachedTextures = currentCachedTextures;
for (graphicKey in previousCachedTextures.keys())
{
if (!permanentCachedTextures.exists(graphicKey)) continue;
previousCachedTextures.remove(graphicKey);
}
currentCachedTextures = permanentCachedTextures;
FunkinMemory.preparePurgeTextureCache();
}
@:deprecated("Use FunkinMemory.purgeCache() instead")
public static function purgeCache():Void
{
// Everything that is in previousCachedTextures but not in currentCachedTextures should be destroyed.
for (graphicKey in previousCachedTextures.keys())
{
var graphic = previousCachedTextures.get(graphicKey);
if (graphic == null) continue;
FlxG.bitmap.remove(graphic);
graphic.destroy();
previousCachedTextures.remove(graphicKey);
}
@:privateAccess
if (FlxG.bitmap._cache == null)
{
@:privateAccess
FlxG.bitmap._cache = new Map();
System.gc();
return;
}
@:privateAccess
for (key in FlxG.bitmap._cache.keys())
{
var obj:Null<FlxGraphic> = FlxG.bitmap.get(key);
if (obj == null) continue;
if (obj.persist) continue;
if (permanentCachedTextures.exists(key)) continue;
if (!(obj.useCount <= 0 || key.contains("characters") || key.contains("charSelect") || key.contains("results"))) continue;
FlxG.bitmap.removeKey(key);
obj.destroy();
}
openfl.Assets.cache.clear("songs");
openfl.Assets.cache.clear("sounds");
openfl.Assets.cache.clear("music");
System.gc();
FunkinMemory.purgeCache();
}
static function isGraphicCached(graphic:FlxGraphic):Bool

View file

@ -71,7 +71,7 @@ class FlxAtlasSprite extends FlxAnimate
onAnimationComplete.add(cleanupAnimation);
// This defaults the sprite to play the first animation in the atlas,
// then pauses it. This ensures symbols are intialized properly.
// then pauses it. This ensures symbols are initialized properly.
this.anim.play('');
this.anim.pause();

View file

@ -17,7 +17,7 @@ class PureColor extends FlxShader
function set_col(val:FlxColor):FlxColor
{
funnyColor.value = [val.red, val.green, val.blue, val.alpha];
funnyColor.value = [val.redFloat, val.greenFloat, val.blueFloat, val.alphaFloat];
return val;
}
@ -33,7 +33,7 @@ class PureColor extends FlxShader
vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
if (color.a > 0.0 && colSet)
color = vec4(funnyColor.r, funnyColor.g, funnyColor.b, color.a);
color = funnyColor * color.a;
gl_FragColor = color;
}

View file

@ -125,7 +125,7 @@ class ControlsHandler
@:noCompletion
private static function get_hasExternalInputDevice():Bool
{
return FlxG.gamepads.numActiveGamepads > 0;
return FlxG.gamepads.numActiveGamepads > 0 #if android || extension.androidtools.Tools.isChromebook() #end;
}
@:noCompletion

View file

@ -1,37 +0,0 @@
package funkin.mobile.macros;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.xml.Printer;
import sys.FileSystem;
using haxe.io.Path;
/**
* This class provides a macro to include an XML build file in the metadata of a Haxe class.
* The file must be located relative to the directory of the Haxe class that uses this macro.
*/
@:nullSafety
class LinkerMacro
{
/**
* Adds an XML `<include>` element to the class's metadata, pointing to a specified build file.
* @param file_name The name of the XML file to include. Defaults to `Build.xml` if not provided.
* @return An array of fields that are processed during the build.
*/
public static macro function xml(?file_name:String = 'Build.xml'):Array<Field>
{
final pos:Position = Context.currentPos();
final sourcePath:String = FileSystem.absolutePath(Context.getPosInfos(pos).file.directory()).removeTrailingSlashes();
final fileToInclude:String = Path.join([sourcePath, file_name?.length > 0 ? file_name : 'Build.xml']);
if (!FileSystem.exists(fileToInclude)) Context.error('The specified file "$fileToInclude" could not be found at "$sourcePath".', pos);
final includeElement:Xml = Xml.createElement('include');
includeElement.set('name', fileToInclude);
Context.getLocalClass().get().meta.add(':buildXml', [
{expr: EConst(CString(Printer.print(includeElement, true))), pos: pos}], pos);
return Context.getBuildFields();
}
}

View file

@ -15,7 +15,14 @@ class FunkinBackButton extends FunkinButton
public var enabled:Bool = true;
var confirming:Bool = false;
public var confirming(get, never):Bool;
function get_confirming():Bool
{
return _confirming;
}
var _confirming:Bool = false;
public var restingOpacity:Float;
@ -86,7 +93,7 @@ class FunkinBackButton extends FunkinButton
return;
}
confirming = true;
_confirming = true;
FlxTween.cancelTweensOf(this);
HapticUtil.vibrate(0, 0.05, 0.5);
@ -98,7 +105,7 @@ class FunkinBackButton extends FunkinButton
animation.onFinish.addOnce(function(name:String) {
if (name != 'confirm') return;
confirming = false;
_confirming = false;
held = false;
onConfirmEnd.dispatch();
});
@ -127,7 +134,7 @@ class FunkinBackButton extends FunkinButton
onDown.removeAll();
onOut.removeAll();
confirming = false;
_confirming = false;
held = false;
onUp.add(playConfirmAnim);

View file

@ -12,7 +12,16 @@ class FunkinOptionsButton extends FunkinButton
public var onConfirmStart(default, null):FlxSignal = new FlxSignal();
public var onConfirmEnd(default, null):FlxSignal = new FlxSignal();
var confirming:Bool = false;
public var enabled:Bool = true;
public var confirming(get, never):Bool;
function get_confirming():Bool
{
return _confirming;
}
var _confirming:Bool = false;
var instant:Bool = false;
var held:Bool = false;
@ -49,7 +58,7 @@ class FunkinOptionsButton extends FunkinButton
function playHoldAnim():Void
{
if (confirming || held) return;
if (confirming || held || !enabled) return;
held = true;
@ -70,7 +79,7 @@ class FunkinOptionsButton extends FunkinButton
return;
}
confirming = true;
_confirming = true;
FlxTween.cancelTweensOf(this);
HapticUtil.vibrate(0, 0.05, 0.5);
@ -86,17 +95,20 @@ class FunkinOptionsButton extends FunkinButton
animation.onFinish.addOnce(function(name:String) {
if (name != 'confirm') return;
_confirming = false;
held = false;
onConfirmEnd.dispatch();
});
}
function playOutAnim():Void
{
if (confirming) return;
if (confirming || !enabled) return;
FlxTween.cancelTweensOf(this);
HapticUtil.vibrate(0, 0.01, 0.2);
animation.play('idle');
held = false;
}
public function resetCallbacks():Void
@ -105,7 +117,7 @@ class FunkinOptionsButton extends FunkinButton
onDown.removeAll();
onOut.removeAll();
confirming = false;
_confirming = false;
held = false;
onUp.add(playConfirmAnim);

View file

@ -270,7 +270,7 @@ class ControlsSchemeMenu extends MusicBeatSubState
*/
function setSelection(index:Int):Void
{
final newIndex:Int = Math.floor(FlxMath.bound(index, 0, hitboxShowcases.length - 1));
final newIndex:Int = Math.floor(index.clamp(0, hitboxShowcases.length - 1));
if (currentIndex != newIndex)
{
@ -348,7 +348,7 @@ class ControlsSchemeMenu extends MusicBeatSubState
hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, showcasesTargetX, elapsed, 0.5);
final minShowcasesX:Float = -1500 * availableSchemes.length;
hitboxShowcases.x = FlxMath.bound(hitboxShowcases.x, minShowcasesX, 400);
hitboxShowcases.x = hitboxShowcases.x.clamp(minShowcasesX, 400);
final targetIndex:Int = Math.round(hitboxShowcases.x / -1500);
@ -356,8 +356,9 @@ class ControlsSchemeMenu extends MusicBeatSubState
}
else
{
hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, (-1500 * currentIndex) + (-1500 / (availableSchemes.length + 1) * currentIndex), elapsed, 0.5);
hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, (-1500 * currentIndex) + (-1500 / (availableSchemes.length + 1) * currentIndex),
elapsed, 0.5);
}
#end
}
#end
}

View file

@ -29,7 +29,7 @@ class AdMobUtil
/**
* AdMob publisher ID used for the application.
*/
static final ADMOB_PUBLISHER:String = EnvironmentConfigMacro.environmentConfig.get("GLOBAL_ADMOB_PUBLISHER");
static final ADMOB_PUBLISHER:String = EnvironmentConfigMacro.environmentConfig.get("MOBILE_GLOBAL_ADMOB_PUBLISHER");
/**
* Test ad unit IDs for development and testing purposes.

View file

@ -1,9 +1,9 @@
package funkin.mobile.util;
#if ios
import funkin.mobile.external.ios.ScreenUtil as NativeScreenUtil;
import funkin.external.ios.ScreenUtil as NativeScreenUtil;
#elseif android
import funkin.mobile.external.android.ScreenUtil as NativeScreenUtil;
import funkin.external.android.ScreenUtil as NativeScreenUtil;
#end
import lime.math.Rectangle;
import lime.system.System;

View file

@ -0,0 +1,54 @@
package funkin.modding;
import haxe.ds.StringMap;
/**
* Temporary persistent data storage for mods to use.
*/
@:nullSafety
class ModStore
{
/**
* All registered stores for this session.
*/
public static final stores:StringMap<Dynamic> = new StringMap<Dynamic>();
/**
* Attempts to register a new store with the given ID and return it.
* If a store with the same ID already exists, that store will be returned instead (discards `data`).
*
* @id The unique ID for this store.
* @data Optional initial data, uses an empty object by default.
* @return The store data at the given ID.
*/
public static function register(id:String, ?data:Dynamic):Dynamic
{
if (stores.exists(id)) return stores.get(id);
stores.set(id, data ??= {});
return data;
}
/**
* Helper function to get a store by ID.
*
* @id The target ID of the store.
* @return The store data, or `null` if the store did not exist.
*/
public static function get(id:String):Null<Dynamic>
{
return stores.get(id);
}
/**
* Helper function to remove a store by ID and return it.
*
* @id The target ID of the store.
* @return The store data, or `null` if the store did not exist.
*/
public static function remove(id:String):Null<Dynamic>
{
var data:Null<Dynamic> = stores.get(id);
stores.remove(id);
return data;
}
}

View file

@ -5,19 +5,6 @@ import polymod.Polymod;
@:nullSafety
class PolymodErrorHandler
{
/**
* Show a popup with the given text.
* This displays a system popup, it WILL interrupt the game.
* Make sure to only use this when it's important, like when there's a script error.
*
* @param name The name at the top of the popup.
* @param desc The body text of the popup.
*/
public static function showAlert(name:String, desc:String):Void
{
lime.app.Application.current.window.alert(desc, name);
}
public static function onPolymodError(error:PolymodError):Void
{
// Perform an action based on the error code.
@ -37,12 +24,12 @@ class PolymodErrorHandler
// A syntax error when parsing a script.
logError(error.message);
// Notify the user via popup.
showAlert('Polymod Script Parsing Error', error.message);
funkin.util.WindowUtil.showError('Polymod Script Parsing Error', error.message);
case SCRIPT_RUNTIME_EXCEPTION:
// A runtime error when running a script.
logError(error.message);
// Notify the user via popup.
showAlert('Polymod Script Exception', error.message);
funkin.util.WindowUtil.showError('Polymod Script Exception', error.message);
case SCRIPT_CLASS_MODULE_NOT_FOUND:
// A scripted class tried to reference an unknown class or module.
logError(error.message);
@ -54,13 +41,12 @@ class PolymodErrorHandler
msg += '\nCheck to ensure the class exists and is spelled correctly.';
// Notify the user via popup.
showAlert('Polymod Script Import Error', msg);
funkin.util.WindowUtil.showError('Polymod Script Import Error', msg);
case SCRIPT_CLASS_MODULE_BLACKLISTED:
// A scripted class tried to reference a blacklisted class or module.
logError(error.message);
// Notify the user via popup.
showAlert('Polymod Script Blacklist Violation', error.message);
funkin.util.WindowUtil.showError('Polymod Script Blacklist Violation', error.message);
default:
// Log the message based on its severity.
switch (error.severity)

View file

@ -1,9 +1,9 @@
package funkin.modding;
import polymod.fs.ZipFileSystem;
import funkin.data.dialogue.conversation.ConversationRegistry;
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
import funkin.data.dialogue.speaker.SpeakerRegistry;
import funkin.data.dialogue.ConversationRegistry;
import funkin.data.dialogue.DialogueBoxRegistry;
import funkin.data.dialogue.SpeakerRegistry;
import funkin.data.event.SongEventRegistry;
import funkin.data.story.level.LevelRegistry;
import funkin.data.notestyle.NoteStyleRegistry;
@ -267,6 +267,11 @@ class PolymodHandler
Polymod.addImportAlias('funkin.modding.base.ScriptedMusicBeatState', funkin.ui.ScriptedMusicBeatState);
Polymod.addImportAlias('funkin.modding.base.ScriptedMusicBeatSubState', funkin.ui.ScriptedMusicBeatSubState);
// Backward compatibility for some classes that moved.
Polymod.addImportAlias('funkin.data.dialogue.conversation.ConversationRegistry', funkin.data.dialogue.ConversationRegistry);
Polymod.addImportAlias('funkin.data.dialogue.dialoguebox.DialogueBoxRegistry', funkin.data.dialogue.DialogueBoxRegistry);
Polymod.addImportAlias('funkin.data.dialogue.speaker.SpeakerRegistry', funkin.data.dialogue.SpeakerRegistry);
// `funkin.util.FileUtil` has unrestricted access to the file system.
Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtilSandboxed);
@ -557,12 +562,10 @@ class PolymodHandler
Polymod.clearScripts();
// Forcibly reload Polymod so it finds any new files.
// This will also register all scripts.
// TODO: Replace this with loadEnabledMods().
funkin.modding.PolymodHandler.loadAllMods();
// Reload scripted classes so stages and modules will update.
Polymod.registerAllScriptClasses();
// Reload everything that is cached.
// Currently this freezes the game for a second but I guess that's tolerable?

View file

@ -4,6 +4,18 @@ import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
import funkin.modding.IScriptedClass.IStateChangingScriptedClass;
import funkin.modding.events.ScriptEvent;
/**
* Parameters used to initialize a module.
*/
typedef ModuleParams =
{
/**
* The state this module is associated with.
* If set, this module will only receive events when the game is in this state.
*/
?state:Class<Dynamic>
}
/**
* A module is a scripted class which receives all events without requiring a specific context.
* You may have the module active at all times, or only when another script enables it.
@ -39,16 +51,27 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
return value;
}
/**
* The state this module is associated with.
* If set, this module will only receive events when the game is in this state.
*/
public var state:Null<Class<Dynamic>> = null;
/**
* Called when the module is initialized.
* It may not be safe to reference other modules here since they may not be loaded yet.
*
* NOTE: To make the module start inactive, call `this.active = false` in the constructor.
*/
public function new(moduleId:String, priority:Int = 1000):Void
public function new(moduleId:String, priority:Int = 1000, ?params:ModuleParams):Void
{
this.moduleId = moduleId;
this.priority = priority;
if (params != null)
{
this.state = params.state ?? null;
}
}
public function toString()

View file

@ -6,6 +6,7 @@ import funkin.modding.events.ScriptEvent;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.modding.module.Module;
import funkin.modding.module.ScriptedModule;
import flixel.FlxG;
/**
* Utility functions for loading and manipulating active modules.
@ -145,6 +146,14 @@ class ModuleHandler
// The module needs to be active to receive events.
if (module != null && module.active)
{
if (module.state != null)
{
// Only call the event if the current state is what the module's state is.
if (!(Type.getClass(FlxG.state) == module.state) && !(Type.getClass(FlxG.state?.subState) == module.state))
{
continue;
}
}
ScriptEventDispatcher.callEvent(module, event);
}
}

View file

@ -21,7 +21,6 @@ import funkin.util.MathUtil;
import funkin.effects.RetroCameraFade;
import flixel.math.FlxPoint;
import funkin.util.TouchUtil;
import openfl.utils.Assets;
#if FEATURE_MOBILE_ADVERTISEMENTS
import funkin.mobile.util.AdMobUtil;
#end
@ -102,18 +101,6 @@ class GameOverSubState extends MusicBeatSubState
var canInput:Bool = false;
var justDied:Bool = true;
var isSpecialAnimation:Bool = false;
var gameOverVibrationPreset:VibrationPreset =
{
period: 0,
duration: Constants.DEFAULT_VIBRATION_DURATION,
amplitude: Constants.MIN_VIBRATION_AMPLITUDE,
sharpness: Constants.DEFAULT_VIBRATION_SHARPNESS
};
public function new(params:GameOverParams)
{
super();
@ -175,7 +162,7 @@ class GameOverSubState extends MusicBeatSubState
if ((parentPlayState?.isMinimalMode ?? true)) {}
else
{
boyfriend = parentPlayState?.currentStage.getBoyfriend(true);
boyfriend = parentPlayState?.currentStage?.getBoyfriend(true);
if (boyfriend != null)
{
boyfriend.canPlayOtherAnims = true;
@ -198,16 +185,17 @@ class GameOverSubState extends MusicBeatSubState
addBackButton(FlxG.width - 230, FlxG.height - 200, FlxColor.WHITE, goBack);
#end
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION);
// Allow input a second later to prevent accidental gameover skips.
new FlxTimer().start(1, function(tmr:FlxTimer) {
canInput = true;
});
}
@:nullSafety(Off)
function setCameraTarget():Void
{
if ((parentPlayState?.isMinimalMode ?? true) || boyfriend == null) return;
if (parentPlayState == null || parentPlayState.isMinimalMode || boyfriend == null) return;
// Assign a camera follow point to the boyfriend's position.
cameraFollowPoint = new FlxObject(parentPlayState.cameraFollowPoint.x, parentPlayState.cameraFollowPoint.y, 1, 1);
@ -218,6 +206,7 @@ class GameOverSubState extends MusicBeatSubState
cameraFollowPoint.y += offsets[1];
add(cameraFollowPoint);
@:nullSafety(Off)
FlxG.camera.target = null;
FlxG.camera.follow(cameraFollowPoint, LOCKON, Constants.DEFAULT_CAMERA_FOLLOW_RATE / 2);
targetCameraZoom = (parentPlayState?.currentStage?.camZoom ?? 1.0) * boyfriend.getDeathCameraZoom();
@ -330,9 +319,6 @@ class GameOverSubState extends MusicBeatSubState
}
}
// Handle vibrations on update.
if (HapticUtil.hapticsAvailable) handleAnimationVibrations();
// Start death music before firstDeath gets replaced
super.update(elapsed);
}
@ -408,7 +394,7 @@ class GameOverSubState extends MusicBeatSubState
// Readd Boyfriend to the stage.
boyfriend.isDead = false;
remove(boyfriend);
parentPlayState?.currentStage.addCharacter(boyfriend, BF);
parentPlayState?.currentStage?.addCharacter(boyfriend, BF);
}
// Snap reset the camera which may have changed because of the player character data.
@ -454,7 +440,7 @@ class GameOverSubState extends MusicBeatSubState
#else
resetPlaying();
#end
});
}, true);
}
});
}
@ -578,11 +564,11 @@ class GameOverSubState extends MusicBeatSubState
PlayStatePlaylist.reset();
}
var stickerPackId:Null<String> = parentPlayState?.currentChart.stickerPack;
var stickerPackId:Null<String> = parentPlayState?.currentChart?.stickerPack;
if (stickerPackId == null)
{
var playerCharacterId:Null<String> = PlayerRegistry.instance.getCharacterOwnerId(parentPlayState?.currentChart.characters.player);
var playerCharacterId:Null<String> = PlayerRegistry.instance.getCharacterOwnerId(parentPlayState?.currentChart?.characters.player);
var playerCharacter:Null<PlayableCharacter> = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? Constants.DEFAULT_CHARACTER);
if (playerCharacter != null)
@ -615,108 +601,6 @@ class GameOverSubState extends MusicBeatSubState
var hasPlayedDeathQuote:Bool = false;
/**
* Used for death haptics.
*/
var startedTimerHaptics:Bool = false;
/**
* Unique vibrations for each death animation.
*/
function handleAnimationVibrations():Void
{
if ((parentPlayState?.isMinimalMode ?? true) || boyfriend == null) return;
if (justDied)
{
if (isSpecialAnimation)
{
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION * 5);
trace("It's a special game over animation.");
}
else
{
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION);
}
justDied = false;
}
if (boyfriend.animation == null) return;
final curFrame:Int = (boyfriend.animation.curAnim != null) ? boyfriend.animation.curAnim.curFrame : -1;
if (boyfriend.characterId.startsWith("bf"))
{
// BF's mic drops.
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && curFrame == 27)
{
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
}
// BF's balls pulsating.
if (boyfriend.getCurrentAnimation().startsWith('deathLoop') && (curFrame == 0 || curFrame == 18))
{
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
}
return;
}
// Pico dies because of Darnell beating him up.
if (boyfriend.characterId == "pico-blazin")
{
if (!startedTimerHaptics)
{
startedTimerHaptics = true;
new FlxTimer().start(0.5, function(tmr:FlxTimer) {
// Pico falls on his knees.
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
new FlxTimer().start(0.6, function(tmr:FlxTimer) {
// Pico falls "asleep". :)
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
});
});
return;
}
}
else if (boyfriend.characterId.startsWith("pico") && boyfriend.characterId != "pico-holding-nene")
{
if (isSpecialAnimation)
{
if (startedTimerHaptics) return;
startedTimerHaptics = true;
// Death by Darnell's can.
new FlxTimer().start(1.85, function(tmr:FlxTimer) {
// Pico falls on his knees.
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
});
}
else
{
// Pico falls on his back.
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && curFrame == 20)
{
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
}
// Blood firework woohoo!!!!
if (boyfriend.getCurrentAnimation().startsWith('deathLoop') && curFrame % 2 == 0)
{
final randomAmplitude:Float = FlxG.random.float(Constants.MIN_VIBRATION_AMPLITUDE / 100, Constants.MIN_VIBRATION_AMPLITUDE);
final randomDuration:Float = FlxG.random.float(Constants.DEFAULT_VIBRATION_DURATION / 10, Constants.DEFAULT_VIBRATION_DURATION);
HapticUtil.vibrate(0, randomDuration, randomAmplitude);
}
}
return;
}
}
public override function destroy():Void
{
super.destroy();

View file

@ -18,9 +18,9 @@ class GitarooPause extends MusicBeatState
var replaySelect:Bool = false;
var previousParams:PlayStateParams;
var previousParams:Null<PlayStateParams>;
public function new(previousParams:PlayStateParams):Void
public function new(?previousParams:PlayStateParams):Void
{
super();

View file

@ -213,6 +213,11 @@ class PauseSubState extends MusicBeatSubState
*/
var menuEntryText:FlxTypedSpriteGroup<AtlasText>;
/**
* Callback that gets called once substate gets open.
*/
var onPause:Void->Void;
// ===============
// Audio Variables
// ===============
@ -222,10 +227,11 @@ class PauseSubState extends MusicBeatSubState
// Constructor
// ===============
public function new(?params:PauseSubStateParams)
public function new(?params:PauseSubStateParams, ?onPause:Void->Void)
{
super();
this.currentMode = params?.mode ?? Standard;
this.onPause = onPause;
}
// ===============
@ -244,6 +250,8 @@ class PauseSubState extends MusicBeatSubState
AdMobUtil.addBanner(extension.admob.AdmobBannerSize.BANNER, extension.admob.AdmobBannerAlign.TOP_LEFT);
#end
if (onPause != null) onPause();
super.create();
startPauseMusic();
@ -286,6 +294,7 @@ class PauseSubState extends MusicBeatSubState
hapticTimer.cancel();
hapticTimer = null;
pauseMusic.stop();
onPause = null;
}
// ===============
@ -930,7 +939,8 @@ class PauseSubState extends MusicBeatSubState
*/
static function changeDifficulty(state:PauseSubState, difficulty:String):Void
{
PlayState.instance.currentSong = SongRegistry.instance.fetchEntry(PlayState.instance.currentSong.id.toLowerCase());
PlayState.instance.currentSong = SongRegistry.instance.fetchEntry(PlayState.instance.currentSong.id.toLowerCase(),
{variation: PlayState.instance.currentChart.variation});
// Reset campaign score when changing difficulty
// So if you switch difficulty on the last song of a week you get a really low overall score.

File diff suppressed because it is too large Load diff

View file

@ -527,8 +527,7 @@ class ResultState extends MusicBeatSubState
bgFlash.visible = true;
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.missed) / params.scoreData.tallies.totalNotes * 100;
var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : Scoring.tallyCompletion(params.scoreData.tallies) * 100;
clearPercentTarget = Math.floor(clearPercentFloat);
// Prevent off-by-one errors.
@ -744,6 +743,7 @@ class ResultState extends MusicBeatSubState
super.draw();
songName.clipRect = FlxRect.get(Math.max(0, 520 - songName.x), 0, FlxG.width, songName.height);
clearPercentSmall.forEachAlive(spr -> spr.clipRect = FlxRect.get(Math.max(0, 520 - spr.x), 0, FlxG.width, spr.height));
// PROBABLY SHOULD FIX MEMORY FREE OR WHATEVER THE PUT() FUNCTION DOES !!!! FEELS LIKE IT STUTTERS!!!
@ -751,105 +751,6 @@ class ResultState extends MusicBeatSubState
// maskShaderSongName.frameUV = songName.frame.uv;
}
private function handleAnimationVibrations()
{
for (atlas in characterAtlasAnimations)
{
if (atlas == null || atlas.sprite == null) continue;
switch (rank)
{
case ScoringRank.PERFECT | ScoringRank.PERFECT_GOLD:
switch (playerCharacterId)
{
// Feel the bed fun :freaky:
case "bf":
if (atlas.sprite.anim.curFrame > 87 && atlas.sprite.anim.curFrame % 5 == 0)
{
HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
// GF slams into the wall.
if (atlas.sprite.anim.curFrame == 51)
{
HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
break;
}
// Pico drop-kicking Nene.
case "pico":
if (atlas.sprite.anim.curFrame == 52)
{
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD, Constants.DEFAULT_VIBRATION_DURATION * 5, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
default:
break;
}
case ScoringRank.GREAT | ScoringRank.EXCELLENT:
switch (playerCharacterId)
{
// Pico explodes the targets with a rocket launcher.
case "pico":
// Pico shoots.
if (atlas.sprite.anim.curFrame == 45)
{
HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
break;
}
// The targets explode.
if (atlas.sprite.anim.curFrame == 50)
{
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD, Constants.DEFAULT_VIBRATION_DURATION, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
default:
break;
}
case ScoringRank.GOOD:
switch (playerCharacterId)
{
// Pico shooting the targets.
case "pico":
if (atlas.sprite.anim.curFrame % 2 != 0) continue;
final frames:Array<Array<Int>> = [[40, 50], [80, 90], [140, 157]];
for (i in 0...frames.length)
{
if (atlas.sprite.anim.curFrame < frames[i][0] || atlas.sprite.anim.curFrame > frames[i][1]) continue;
HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
default:
break;
}
case ScoringRank.SHIT:
switch (playerCharacterId)
{
// BF falling and GF slams on BF with her ass.
case "bf":
if (atlas.sprite.anim.curFrame == 5 || atlas.sprite.anim.curFrame == 90)
{
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD * 2, Constants.DEFAULT_VIBRATION_DURATION * 2, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
default:
break;
}
}
}
}
override function update(elapsed:Float):Void
{
maskShaderDifficulty.swagSprX = difficulty.x;
@ -931,7 +832,10 @@ class ResultState extends MusicBeatSubState
var stickerPackId:Null<String> = null;
var song:Null<Song> = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId);
var song:Null<Song> = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId,
{
variation: params?.variationId
});
if (song != null)
{
@ -1035,8 +939,6 @@ class ResultState extends MusicBeatSubState
#end
}
if (HapticUtil.hapticsAvailable) handleAnimationVibrations();
super.update(elapsed);
}

View file

@ -580,7 +580,7 @@ class AnimateAtlasCharacter extends BaseCharacter
override function set_alpha(value:Float):Float
{
value = FlxMath.bound(value, 0, 1);
value = value.clamp(0, 1);
if (exists && alpha != value)
{

View file

@ -288,35 +288,23 @@ class CharacterDataParser
{
var charPath:String = "freeplay/icons/";
// FunkinCrew please dont skin me alive for copying pixelated icon and changing it a tiny bit
switch (char)
final charIDParts:Array<String> = char.split("-");
var iconName:String = "";
var lastValidIconName:String = "";
for (i in 0...charIDParts.length)
{
case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf" | "bf-dark":
charPath += "bfpixel";
case "monster-christmas":
charPath += "monsterpixel";
case "mom" | "mom-car":
charPath += "mommypixel";
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";
case "dad":
charPath += "dadpixel";
case "darnell-blazin":
charPath += "darnellpixel";
case "senpai-angry":
charPath += "senpaipixel";
case "spooky-dark":
charPath += "spookypixel";
case "tankman-atlas" | "tankman-bloody":
charPath += "tankmanpixel";
case "pico-christmas" | "pico-dark":
charPath += "picopixel";
default:
charPath += '${char}pixel';
iconName += charIDParts[i];
if (Assets.exists(Paths.image(charPath + '${iconName}pixel')))
{
lastValidIconName = iconName;
}
if (i < charIDParts.length - 1) iconName += '-';
}
charPath += '${lastValidIconName}pixel';
if (!Assets.exists(Paths.image(charPath)))
{
trace('[WARN] Character ${char} has no freeplay icon.');

View file

@ -94,6 +94,7 @@ class MultiSparrowCharacter extends BaseCharacter
{
trace('Concatenating multi-sparrow atlas: ${asset}');
subTexture.parent.destroyOnNoUse = false;
FunkinMemory.cacheTexture(Paths.image(asset));
}
texture.addAtlas(subTexture);

View file

@ -115,6 +115,8 @@ class HealthIcon extends FunkinSprite
*/
static final POSITION_OFFSET:Int = 26;
public var iconOffset:FlxPoint = FlxPoint.get();
public function new(char:Null<String>, playerId:Int = 0)
{
super(0, 0);
@ -151,10 +153,12 @@ class HealthIcon extends FunkinSprite
*/
public function toggleOldIcon():Void
{
final playState:Null<PlayState> = PlayState.instance;
if (playState == null || playState.currentStage == null) return;
if (characterId == 'bf-old')
{
isPixel = PlayState.instance.currentStage.getBoyfriend().isPixel;
PlayState.instance.currentStage.getBoyfriend().initHealthIcon(false);
isPixel = playState.currentStage.getBoyfriend()?.isPixel ?? false;
playState.currentStage.getBoyfriend()?.initHealthIcon(false);
}
else
{
@ -180,8 +184,7 @@ class HealthIcon extends FunkinSprite
loadCharacter(characterId);
this.size.set(1.0, 1.0);
this.offset.x = 0.0;
this.offset.y = 0.0;
this.iconOffset.set();
this.flipX = false;
}
else
@ -192,8 +195,15 @@ class HealthIcon extends FunkinSprite
loadCharacter(characterId);
this.size.set(data.scale ?? 1.0, data.scale ?? 1.0);
this.offset.x = (data.offsets != null) ? data.offsets[0] : 0.0;
this.offset.y = (data.offsets != null) ? data.offsets[1] : 0.0;
if (data.offsets != null && data.offsets.length == 2)
{
this.iconOffset.set(data.offsets[0], data.offsets[1]);
}
else
{
this.iconOffset.set(0, 0);
}
this.flipX = data.flipX ?? false; // Face the OTHER way by default, since that is more common.
}
}
@ -289,6 +299,8 @@ class HealthIcon extends FunkinSprite
// Keep the icon centered vertically on the health bar.
this.y = PlayState.instance.healthBar.y - (this.height / 2); // - (PlayState.instance.healthBar.height / 2)
offset += iconOffset;
}
}

View file

@ -66,7 +66,7 @@ class VideoCutscene
if (!openfl.Assets.exists(filePath))
{
// Display a popup.
// lime.app.Application.current.window.alert('Video file does not exist: ${filePath}', 'Error playing video');
// funkin.util.WindowUtil.showError('Error playing video', 'Video file does not exist: ${filePath}');
// return;
// TODO: After moving videos to their own library,

View file

@ -6,11 +6,11 @@ import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxSort;
import funkin.audio.FunkinSound;
import funkin.data.dialogue.conversation.ConversationData;
import funkin.data.dialogue.conversation.ConversationData.DialogueEntryData;
import funkin.data.dialogue.conversation.ConversationRegistry;
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
import funkin.data.dialogue.speaker.SpeakerRegistry;
import funkin.data.dialogue.ConversationData;
import funkin.data.dialogue.ConversationData.DialogueEntryData;
import funkin.data.dialogue.ConversationRegistry;
import funkin.data.dialogue.DialogueBoxRegistry;
import funkin.data.dialogue.SpeakerRegistry;
import funkin.data.IRegistryEntry;
import funkin.graphics.FunkinSprite;
import funkin.modding.events.ScriptEvent;
@ -89,7 +89,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl
var currentDialogueBox:Null<DialogueBox>;
public function new(id:String)
public function new(id:String, ?params:Dynamic)
{
super();

View file

@ -11,8 +11,8 @@ import funkin.audio.FunkinSound;
import funkin.modding.IScriptedClass.IDialogueScriptedClass;
import flixel.util.FlxColor;
import funkin.ui.FullScreenScaleMode;
import funkin.data.dialogue.dialoguebox.DialogueBoxData;
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
import funkin.data.dialogue.DialogueBoxData;
import funkin.data.dialogue.DialogueBoxRegistry;
class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry<DialogueBoxData>
{
@ -106,7 +106,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
return FullScreenScaleMode.wideScale.x - 0.05;
}
public function new(id:String)
public function new(id:String, ?params:Dynamic)
{
super();
this.id = id;

View file

@ -6,8 +6,8 @@ import funkin.modding.events.ScriptEvent;
import flixel.graphics.frames.FlxFramesCollection;
import funkin.util.assets.FlxAnimationUtil;
import funkin.modding.IScriptedClass.IDialogueScriptedClass;
import funkin.data.dialogue.speaker.SpeakerData;
import funkin.data.dialogue.speaker.SpeakerRegistry;
import funkin.data.dialogue.SpeakerData;
import funkin.data.dialogue.SpeakerRegistry;
import funkin.ui.FullScreenScaleMode;
/**
@ -86,7 +86,7 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
return FullScreenScaleMode.wideScale.x - 0.05;
}
public function new(id:String)
public function new(id:String, ?params:Dynamic)
{
super();

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