Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b13c6c53b | ||
|
|
036de96143 | ||
|
|
0b046cae9c | ||
|
|
a4c5341b3b | ||
|
|
5cbfab4f99 | ||
|
|
baf887155b | ||
|
|
a5bbe94871 | ||
|
|
ab26b28130 | ||
|
|
2606031ed2 | ||
|
|
72f9d32a2f | ||
|
|
4b87bbf1cd | ||
|
|
c9802fdcf2 | ||
|
|
e7f252953d | ||
|
|
7f299d8348 | ||
|
|
9d83d05c96 | ||
|
|
d6919683c3 | ||
|
|
879955c4a2 | ||
|
|
6026e0b908 | ||
|
|
32ef5404d8 | ||
|
|
3544a36de0 | ||
|
|
06a1894946 | ||
|
|
40bb5b7a74 | ||
|
|
062c2fa331 | ||
|
|
49846dbc13 | ||
|
|
e5a47aa74d | ||
|
|
77fffdf370 | ||
|
|
eaad0f2450 | ||
|
|
77faf38d61 | ||
|
|
134d220298 | ||
|
|
d4610f5f16 | ||
|
|
f5129b3283 | ||
|
|
ee6583fa82 | ||
|
|
af72895e54 | ||
|
|
8d17ad3dd1 | ||
|
|
723dd81e2b | ||
|
|
5c45d24e2e | ||
|
|
ffe72b89a7 | ||
|
|
bc92d6cebf | ||
|
|
b4bc3ca6cf | ||
|
|
8439272d00 | ||
|
|
c5a7442415 | ||
|
|
31b7fdf02b | ||
|
|
dafe4fe918 | ||
|
|
6737146571 | ||
|
|
33d1fd578e | ||
|
|
0c9edf93c3 | ||
|
|
241f140a98 | ||
|
|
0311c7373e | ||
|
|
ca95fa78c4 | ||
|
|
7fa5d77db7 | ||
|
|
dcc3baae26 | ||
|
|
ccb270612c | ||
|
|
404b601012 | ||
|
|
02d83a766d | ||
|
|
f40da353a2 | ||
|
|
c0c550e209 | ||
|
|
29ac2ad58b | ||
|
|
9b0b273004 | ||
|
|
17f120f5ee | ||
|
|
284cfc7ebc | ||
|
|
aa5d30eecc | ||
|
|
c11ecfcb6f | ||
|
|
aa251af116 | ||
|
|
ea7bc750d2 | ||
|
|
a043512f0e | ||
|
|
5805c310c1 | ||
|
|
bf0135d48e | ||
|
|
e1f950d3e6 | ||
|
|
018780620d | ||
|
|
ba2f8df30b | ||
|
|
ba77fc6677 | ||
|
|
ff3a5c4b9b | ||
|
|
3aef0e5432 | ||
|
|
83fb754e8e | ||
|
|
5042448e98 | ||
|
|
bca18170e7 | ||
|
|
524468135d | ||
|
|
de5fe9b950 | ||
|
|
17ed98ee4d | ||
|
|
1b6c2d1f93 | ||
|
|
7c90aee994 | ||
|
|
96bdfac0b9 | ||
|
|
359661b549 | ||
|
|
1e43902fff | ||
|
|
84a79f6833 | ||
|
|
bcd3b03a75 | ||
|
|
1ff79a7825 | ||
|
|
c3cdddc46e | ||
|
|
ac2f1f602b | ||
|
|
40ea803c56 | ||
|
|
dce987b345 | ||
|
|
5804aa100d | ||
|
|
f1276a64a0 | ||
|
|
503bbaaa46 | ||
|
|
0dbd8439bd | ||
|
|
40e5d1a4c1 | ||
|
|
58803cd221 | ||
|
|
ff0a226f53 | ||
|
|
1d19286f6b | ||
|
|
5cf114143c | ||
|
|
8331a1fa7f | ||
|
|
db7bee7902 | ||
|
|
2ba0f37a5a | ||
|
|
31f873ffc2 | ||
|
|
f7b5d5c62f | ||
|
|
94570d8c7c | ||
|
|
f6036343c9 | ||
|
|
cc8060db5b | ||
|
|
511f4d5b64 | ||
|
|
b04a7708b1 | ||
|
|
dd512d02e7 | ||
|
|
ff89c09b6f | ||
|
|
429e018749 | ||
|
|
babe37a0f0 | ||
|
|
122df21223 | ||
|
|
016c53061c | ||
|
|
429704676a | ||
|
|
cda1ca6897 | ||
|
|
8954dee6d6 | ||
|
|
b71e12ec5c | ||
|
|
c2055a322d | ||
|
|
583700006c | ||
|
|
928c2fcc19 | ||
|
|
3c913d29e3 | ||
|
|
52e015ad14 | ||
|
|
bef8cf9a0e | ||
|
|
eae4cb2160 | ||
|
|
955c94ed79 | ||
|
|
95109daedb | ||
|
|
00f28fdb79 | ||
|
|
aad014a0ba | ||
|
|
4917bd0591 | ||
|
|
54943b22f8 | ||
|
|
ec6e26c298 | ||
|
|
3db958a76b | ||
|
|
f5f2a2ac4c | ||
|
|
26fe00b559 | ||
|
|
899d793e1c | ||
|
|
430e2b9dcc | ||
|
|
9189c35585 | ||
|
|
0855b4da4e | ||
|
|
ad9c6581ff | ||
|
|
293d303c16 | ||
|
|
e4a4197482 | ||
|
|
2886e29d58 | ||
|
|
5868cb1d78 |
+126
-2
@@ -4,6 +4,130 @@ All notable changes will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.8.2] - 2026-02-23
|
||||
|
||||
### Added
|
||||
|
||||
- Added 14 new blend modes, allowing the engine to render better visual effects.
|
||||
- Added a PlayState parameter that mirrors notes during a song.
|
||||
- Added preview graphs for Chart Editor event easing types. ([eea1265](https://github.com/FunkinCrew/Funkin/commit/eea12657b1d8131014a3fdc270c3f351fae6e851)) - by @PurSnake in [#6374](https://github.com/FunkinCrew/Funkin/pull/6374)
|
||||
- Added the ability to mirror notes on the X and Y axes in the Chart Editor. ([fd0c2df](https://github.com/FunkinCrew/Funkin/commit/fd0c2df35ee8cda3f1566a297c4faec984d809bb)) - by @Lasercar in [#4372](https://github.com/FunkinCrew/Funkin/pull/4372)
|
||||
- Added a deselect button and shortcut to the Stage Editor. ([a9f1d41](https://github.com/FunkinCrew/Funkin/commit/a9f1d41771f0c97fdd70d4d6c54087ac0e64ff38)) - by @Lasercar in [#5281](https://github.com/FunkinCrew/Funkin/pull/5281)
|
||||
|
||||
### Changed
|
||||
|
||||
- Made some improvements to Polymod:
|
||||
- Added string interpolation support. (Thanks NotHyper-474!)
|
||||
- Scripted classes can now extend nothing. (Thanks NotHyper-474!)
|
||||
- Scripted classes can now extend other scripted classes. (Thanks KoloInDaCrib!)
|
||||
- Tweaked some error messages to be more verbose.
|
||||
- Added `scriptHas` and `staticScriptHas` functions to check if a script has a specific variable/function.
|
||||
- The `is` operator is now more accurate to regular Haxe. (Thanks NotHyper-474!)
|
||||
- Static functions can now use static extensions (`using`s). (Thanks NotHyper-474!)
|
||||
- `interface`s can now be properly parsed. (Thanks Starexify!)
|
||||
- Optional fields and extensions can now be used in `typedef`s. (Thanks Starexify!)
|
||||
- Added stricter checking for default/optional arguments in functions. (Thanks Starexify!)
|
||||
- May be a breaking change for some scripts, so please update your scripts to account for this!
|
||||
- Enums now have multiple arguments. (Thanks Starexify!)
|
||||
- Basic types (e.g `String`, `Int`, `Bool`, etc.) can now be used with the `is` operator without having to import the type first.
|
||||
- Tweaked charts for the following songs:
|
||||
- Bopeebo (Pico Mix) [Normal/Hard] - Adjusted note timings and added a “hey” animation event in Sections 18, 26, and 27.
|
||||
- Fresh Erect [Nightmare] - Added 2 notes where Boyfriend’s vocals echo.
|
||||
- DadBattle Erect [Nightmare] - Added a missing jack note in Section 38, added holds to two notes in Section 63.
|
||||
- DadBattle (Pico Mix) [all difficulties] - Adjusted timings for various notes in Sections 37-40 and 60.
|
||||
- Spookeez Erect [Erect/Nightmare] - Adjusted many hold note lengths to better match vocals.
|
||||
- Spookeez (Pico Mix) - Added one missing note to Section 14.
|
||||
- South Erect [Erect/Nightmare] - Adjusted many hold note lengths to better match vocals.
|
||||
- Pico [Easy] - Adjusted timings for one note each in Sections 36 and 38.
|
||||
- Pico [Normal/Hard] - Adjusted timings for Boyfriend’s right notes to be a bit earlier in Sections 31-42.
|
||||
- Pico [all difficulties] - Added a Focus Camera event on Girlfriend during the duet.
|
||||
- Satin Panties Erect [Erect/Nightmare] - Split a hold note into two for Mom in Sections 14 and 18.
|
||||
- High Erect [Erect/Nightmare] - Adjusted timing for one note each in Sections 25 and 41.
|
||||
- M.I.L.F [Easy] - Adjusted timings for two notes in Section 73.
|
||||
- M.I.L.F [Hard] - Changed one down note into an up note in Section 15 for consistency.
|
||||
- Cocoa Erect [Nightmare] - Fixed one note not being a Mom notekind in Section 24, added holds to 2 notes in Section 67.
|
||||
- Cocoa (Pico Mix) [all difficulties] - Adjusted timings for two notes in Section 15.
|
||||
- Eggnog Erect [Erect/Nightmare] - Adjusted how a grace note is charted in Section 52, added 2 missing notes for Mom in Section 72, adjusted Camera Bop event in Section 63.
|
||||
- Senpai (Pico Mix) [all difficulties] - Adjusted timing for one note in Section 10.
|
||||
- Roses [Normal] - Adjusted timings for four notes in Sections 30, 32, 41, and 43.
|
||||
- Thorns Erect [Erect] - Adjusted timings for two notes in Section 36, removed some double notes to differentiate the chart from Nightmare.
|
||||
- Ugh Erect [Erect/Nightmare] - Adjusted some Zoom Camera events around Section 24.
|
||||
- Guns (Pico Mix) [all difficulties] - Fixed many timing issues throughout the whole song.
|
||||
- SPAGHETTI [Normal/Hard] - Added one note to match Boyfriend’s rapping.
|
||||
- Darnell (BF Mix) [all difficulties] - Removed extra notes from Sections 49 and 50, adjusted various note timings throughout the song.
|
||||
- Lit Up [Normal] - Added one missing note each in Sections 40 and 60.
|
||||
- Lit Up (BF Mix) [Normal/Hard] - Adjusted timings for various grace notes in Sections 1, 52, 55, and 56.
|
||||
- 2hot [Easy/Normal] - Recharted many sections to be more predictable.
|
||||
- [macOS] Disabled high DPI to significantly improve performance on some displays.
|
||||
- Improved the Lag Adjustment menu to better handle very large offsets.
|
||||
- Chart Editor exporting now omits default values and orders values more consistently.
|
||||
- Chart Editor playtests now processes all events before the starting position.
|
||||
- Optimized the Week 2 Spooky Mansion stage background by using a texture atlas. ([2bdd5a7](https://github.com/FunkinCrew/funkin.assets/commit/2bdd5a713f0b5c7e945c457e2a4a92bdb367cc02)) - by @MaybeMaru and @Trayfellow in [funkin.assets#348](https://github.com/FunkinCrew/funkin.assets/pull/348)
|
||||
- Added more default imports for mods. ([1620cf3](https://github.com/FunkinCrew/Funkin/commit/1620cf388973eab5d83e151eec28b2c8abc84eb1)) - by @Starexify in [#6706](https://github.com/FunkinCrew/Funkin/pull/6706)
|
||||
- The Set Camera Bop event now supports decimal values for rate and offset at 0.25 precision. ([ffaba34](https://github.com/FunkinCrew/Funkin/commit/ffaba343258c4a0e5ee7e1bd7fa0e8138ae60bc1)) - by @KoloInDaCrib in [#6741](https://github.com/FunkinCrew/Funkin/pull/6741)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed some issues with Polymod:
|
||||
- The super constructor for scripted classes can now accept more than 4 arguments. (Thanks NotHyper-474!)
|
||||
- Functions can now accept more than 8 arguments. (Thanks NotHyper-474!)
|
||||
- Script variables are no longer dropped early when passing them to a callback (ex. `FlxTimer`). (Thanks NotHyper-474!)
|
||||
- Fix a memory leak that could happen if a script tried to reference an anonymous object with a lot of `StringMap`s. (Thanks NotHyper-474!)
|
||||
- Properties (`get_`/`set_` functions) are now much more accurate to regular Haxe. (Thanks NotHyper-474!)
|
||||
- Re-exported some assets to fix some issues:
|
||||
- Re-exported all variations of Pico as texture atlases to optimize memory usage.
|
||||
- Freeplay DJ Pico’s table sparks no longer persist when his gun is lifted.
|
||||
- Pico (Blazin’)’s game over retry animation now properly displays its blend modes.
|
||||
- Pico (Christmas) no longer shows several stray marks around his sprites.
|
||||
- Nene (Christmas) no longer becomes slightly offset when her knife is raised.
|
||||
- The running tankmen in Stress (Pico Mix) no longer tank the game’s performance.
|
||||
- Daddy Dearest now properly holds his up pose.
|
||||
- Monster (Christmas)’s hat no longer moves while he holds a pose.
|
||||
- Darnell’s can in 2hot now shows a sparkling effect.
|
||||
- [DESKTOP] Modded atlas characters with both .png and .astc spritesheets no longer crash the game.
|
||||
- [DESKTOP] The game display no longer sometimes becomes offset from the window.
|
||||
- [MOBILE] Selecting a song using touch controls when a keyboard is connected no longer disables the keyboard strumline layout.
|
||||
- [ANDROID] Improved Freeplay scrolling behavior on high framerate displays.
|
||||
- Ctrl-clicking on a hold note no longer crashes the Chart Editor.
|
||||
- [WINDOWS] Chart Editor save dialog windows no longer freeze the game.
|
||||
- The Chart Editor Results Screen playtest option now consistently shows the Results Screen after each playtest.
|
||||
- Scrolling really quickly in Freeplay no longer crashes the game. ([76699b2](https://github.com/FunkinCrew/Funkin/commit/76699b235cd5b359bc34e6950cd05bbbb13918f8)) - by @ComedyLost in [#6962](https://github.com/FunkinCrew/Funkin/pull/6962)
|
||||
- Fixed a memory leak in the Freeplay menu. ([910ba65](https://github.com/FunkinCrew/Funkin/commit/910ba650b0c1011802b151b85f4a9d8177ae95c7)) - by @KoloInDaCrib in [#6615](https://github.com/FunkinCrew/Funkin/pull/6615)
|
||||
- Modded difficulty dots and graphics now render correctly in Freeplay. ([90b856d](https://github.com/FunkinCrew/Funkin/commit/90b856d88635ac4e5517e934298e3820b2e5f45c)) - by @ComedyLost in [#6205](https://github.com/FunkinCrew/Funkin/pull/6205)
|
||||
- Freeplay capsules with long song names no longer display too far to the left during the new rank animation. ([17a914c](https://github.com/FunkinCrew/Funkin/commit/17a914c933592cb3ba79881ca6a8d900b8513cbc)) - by @VirtuGuy in [#6690](https://github.com/FunkinCrew/Funkin/pull/6690)
|
||||
- Freeplay capsule BPM values ending in 1 now display with proper spacing. ([e5b4351](https://github.com/FunkinCrew/Funkin/commit/e5b43510dd810a74a7d06f7f02c52892d11d6f8b)) - by @VirtuGuy in [#6696](https://github.com/FunkinCrew/Funkin/pull/6696)
|
||||
- The Freeplay pixel icon now properly plays its confirm animation when mashing the capsule. ([a56bc44](https://github.com/FunkinCrew/Funkin/commit/a56bc4462d1c49e8ec71462b9bd7ff338b743c93)) - by @VirtuGuy in [#6723](https://github.com/FunkinCrew/Funkin/pull/6723)
|
||||
- [HTML5] Scrolling with the mouse now works properly in Story Mode and Freeplay. ([506d80b](https://github.com/FunkinCrew/Funkin/commit/506d80b287b969121dc3601714c2459cc71f7e55)) - by @VirtuGuy in [#6621](https://github.com/FunkinCrew/Funkin/pull/6621)
|
||||
- Optimized rendering in the Character Select menu. ([2ecebf7](https://github.com/FunkinCrew/Funkin/commit/2ecebf79205a66b64148d64bbb3162c9fc073d85)) - by @MaybeMaru in [#6789](https://github.com/FunkinCrew/Funkin/pull/6789)
|
||||
- [MOBILE] Tapping on a lock in Character Select now plays its reject animation. ([05148f0](https://github.com/FunkinCrew/Funkin/commit/05148f0e5dad4d60089e6074e616b583ba29a5e6)) - by @VirtuGuy in [#6779](https://github.com/FunkinCrew/Funkin/pull/6779)
|
||||
- Hold note covers now follow when the strumline position is modified. ([77c6651](https://github.com/FunkinCrew/Funkin/commit/77c6651e245ea276b1bdf33e5d6732073dcb6037)) - by @MightyTheArmiddilo in [#6746](https://github.com/FunkinCrew/Funkin/pull/6746)
|
||||
- The pause and game over screens no longer sometimes play the wrong character's themes. ([878644a](https://github.com/FunkinCrew/Funkin/commit/878644aee7ba425e482707b94b9f887ec23f6306)) - by @KoloInDaCrib in [#6812](https://github.com/FunkinCrew/Funkin/pull/6812)
|
||||
- The game over camera focus point is now consistent, regardless of which animation was previously playing. ([18cceb4](https://github.com/FunkinCrew/Funkin/commit/18cceb4946a51fda0009d77baae1115437694415)) - by @KoloInDaCrib in [#6705](https://github.com/FunkinCrew/Funkin/pull/6705)
|
||||
- Pico (Pixel) and other pixel characters now use RetroCameraFade as Boyfriend (Pixel) does. ([d1382aa](https://github.com/FunkinCrew/Funkin/commit/d1382aa985f97e3b40b0b629cb3d04301ebfb55e)) - by @afreetoplaynoob in [#6526](https://github.com/FunkinCrew/Funkin/pull/6526)
|
||||
- [MOBILE] The back button can no longer exit during the Boyfriend fakeout death animation. ([9d06018](https://github.com/FunkinCrew/Funkin/commit/9d0601813e912de9e25a851ad9204a654316625a)) - by @ActualMandM in [#6917](https://github.com/FunkinCrew/Funkin/pull/6917)
|
||||
- The song name text is now properly hidden at the beginning of the Results Screen animation. ([a5b15b9](https://github.com/FunkinCrew/Funkin/commit/a5b15b9424f16d8eadc7a92f1c355da647990c3a)) - by @NotHyper-474 and @Trayfellow in [#6931](https://github.com/FunkinCrew/Funkin/pull/6931)
|
||||
- The sticker transition no longer covers the volume soundtray. ([616546d](https://github.com/FunkinCrew/Funkin/commit/616546d298a06c4135a32ffad41483be208552f1)) - by @VirtuGuy in [#6727](https://github.com/FunkinCrew/Funkin/pull/6727)
|
||||
- Lightning now appears more regularly during South Erect and South (Pico Mix). ([9ca2092](https://github.com/FunkinCrew/funkin.assets/commit/9ca20929bc3dde6b07d74100c45fb45e48769c35)) - by @JackXson-Real in [funkin.assets#345](https://github.com/FunkinCrew/funkin.assets/pull/345)
|
||||
- [HTML5] The city lights in the Week 3 stages now show up. ([603684f](https://github.com/FunkinCrew/funkin.assets/commit/603684ffb327f03d8216aaeaba143ca5f9cd77f0)) - by @VirtuGuy in [funkin.assets#342](https://github.com/FunkinCrew/funkin.assets/pull/342)
|
||||
- The Week 3 Pico doppelganger cutscene background music no longer cuts off at the end. ([8ef58aa](https://github.com/FunkinCrew/funkin.assets/commit/8ef58aabdba8287d4c88808e2f0ecc0ac385b383)) - by @Trayfellow in [funkin.assets#349](https://github.com/FunkinCrew/funkin.assets/pull/349)
|
||||
- The Story Mode cutscene before Stress now properly plays when viewed more than once. ([6ec45ec](https://github.com/FunkinCrew/funkin.assets/commit/6ec45ec82ca77188b1b2b9c58ada172f5c357103)) - by @NotHyper-474 in [funkin.assets#341](https://github.com/FunkinCrew/funkin.assets/pull/341)
|
||||
- Darnell's can sound effects in 2hot no longer play twice with high offsets or when lagging. ([6315e25](https://github.com/FunkinCrew/funkin.assets/commit/6315e25eaaa8be4ca85e9b607f547595c699997a)) - by @JackXson-Real in [funkin.assets#354](https://github.com/FunkinCrew/funkin.assets/pull/354)
|
||||
- [MOBILE] The cutscene after 2hot now properly accounts for different resolutions. ([dd3371a](https://github.com/FunkinCrew/Funkin/commit/dd3371a549f4e10f68f1a458f40ec1a342f07876)) - by @ActualMandM in [#6937](https://github.com/FunkinCrew/Funkin/pull/6937)
|
||||
- Made the AttractState video skipping pie smoother. ([5a161b1](https://github.com/FunkinCrew/Funkin/commit/5a161b1d7342d7931950edf37554ce3a338736f9)) - by @PurSnake in [#6728](https://github.com/FunkinCrew/Funkin/pull/6728)
|
||||
- The screenshot preview no longer breaks when changing states. ([dc78db8](https://github.com/FunkinCrew/Funkin/commit/dc78db8808a7ea205fe75eba006e4ec1c57a8190)) - by @Lasercar in [#6792](https://github.com/FunkinCrew/Funkin/pull/6792)
|
||||
- Holding Ctrl and clicking on a hold note trail no longer crashes the Chart Editor (again). ([86ac336](https://github.com/FunkinCrew/Funkin/commit/86ac336d62a65fb420cdd6b1be739c3262d21159)) - by @Lasercar in [#6912](https://github.com/FunkinCrew/Funkin/pull/6912)
|
||||
- Chart Editor tooltips no longer appear when hovering over a deleted note or event. ([091c18b](https://github.com/FunkinCrew/Funkin/commit/091c18bccd95b588ba0d4577a94d83acf5942417)) - by @KoloInDaCrib in [#6929](https://github.com/FunkinCrew/Funkin/pull/6929)
|
||||
- Chart Editor vocal waveforms now render more accurately after BPM changes in the song. ([4e8f48d](https://github.com/FunkinCrew/Funkin/commit/4e8f48d5f0f1be4438c4ef9571137eaa35935f67)) - by @gamerbross in [#6833](https://github.com/FunkinCrew/Funkin/pull/6833)
|
||||
- The Chart Editor vocal waveforms now display in the proper position regardless of the game's aspect ratio. ([1fb9040](https://github.com/FunkinCrew/Funkin/commit/1fb9040470432528a0441d7938d217e424ce27fd)) - by @Starexify in [#6193](https://github.com/FunkinCrew/Funkin/pull/6193)
|
||||
- The second section number and divider now renders consistently in the Chart Editor. ([ac539bd](https://github.com/FunkinCrew/Funkin/commit/ac539bdf26ee7b5ecdcc52665c7c95287ef491c0)) - by @NotHyper-474 in [#6765](https://github.com/FunkinCrew/Funkin/pull/6765)
|
||||
- The Chart Editor now saves the Show Results Screen setting. ([c893a73](https://github.com/FunkinCrew/Funkin/commit/c893a7320d72abd3743bc5c1ddeaecf073035eb1)) - by @MightyTheArmiddilo in [#6854](https://github.com/FunkinCrew/Funkin/pull/6854)
|
||||
- Closing the game while in the Stage Editor no longer crashes the game. ([99cd4e2](https://github.com/FunkinCrew/Funkin/commit/99cd4e2cbd7fba87249b02b072a1e60f07c2c907)) - by @VirtuGuy in [#6775](https://github.com/FunkinCrew/Funkin/pull/6775)
|
||||
- The onion skin now properly renders as transparent after changing characters in the Animation Editor. ([5c444f1](https://github.com/FunkinCrew/Funkin/commit/5c444f1109e5af04735a4383e577c69682b3713f)) - by @VirtuGuy in [#6545](https://github.com/FunkinCrew/Funkin/pull/6545)
|
||||
- Blacklisted a class for security. ([3a113c2](https://github.com/FunkinCrew/Funkin/commit/3a113c270f92a6b14dfcda221124742437e1670a)) - by @charlesisfeline in [#6573](https://github.com/FunkinCrew/Funkin/pull/6573)
|
||||
- Even more tiny fixes!
|
||||
|
||||
|
||||
|
||||
## [0.8.1] - 2026-01-04
|
||||
|
||||
### Added
|
||||
@@ -20,7 +144,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
|
||||
|
||||
## [0.8.0] - 2025-12-31
|
||||
## [0.8.0] - 2026-01-03
|
||||
|
||||
The LE SSERAFIM collab update!
|
||||
|
||||
@@ -112,7 +236,6 @@ The LE SSERAFIM collab update!
|
||||
- [iOS] The navigation bar is no longer unlocked after watching an ad.
|
||||
- [iOS] The “larger text” accessibility setting no longer causes the game to not fill the whole screen.
|
||||
- [iOS] The game now displays error popups.
|
||||
- [ANDROID] Keyboard detection and strumline positioning now work properly.
|
||||
- [ANDROID] Taking a screenshot no longer pauses the game for a moment.
|
||||
- The Title Screen no longer switches to Attract Mode while the Girlfriend’s Ringtone easter egg is playing.
|
||||
- Holding Escape in the Main Menu no longer quickly closes the game.
|
||||
@@ -151,6 +274,7 @@ The LE SSERAFIM collab update!
|
||||
- Hold note covers no longer get permanently stuck playing after a lag spike. ([66882d9](https://github.com/FunkinCrew/Funkin/commit/66882d9677268396320ee003478a3e2627655755)) - by @KoloInDaCrib in [#6066](https://github.com/FunkinCrew/Funkin/pull/6066)
|
||||
- Scrolling through Freeplay songs for the first time no longer stutters. ([cc486ad](https://github.com/FunkinCrew/Funkin/commit/cc486ad94b5e9806d18fb65165663fea0ce0d7b4)) - by @mikolka9144 in [#4851](https://github.com/FunkinCrew/Funkin/pull/4851)
|
||||
- Re-implemented an error that appears when a video file does not exist. ([2ad5933](https://github.com/FunkinCrew/Funkin/commit/2ad593341f8bea8384646f4803dc8795a9efe32a)) - by @TechnikTil in [#6253](https://github.com/FunkinCrew/Funkin/pull/6253)
|
||||
- [ANDROID] Keyboard detection and strumline positioning now work properly. ([5763ae5](https://github.com/FunkinCrew/Funkin/commit/5763ae5b728359c5639bbc578f15b65a16fa57b1)) - by @NotHyper-474 in [#6328](https://github.com/FunkinCrew/Funkin/pull/6328)
|
||||
- The game now properly parses .json files beginning with a bracket. ([c7367ae](https://github.com/FunkinCrew/Funkin/commit/c7367ae33d7e11f57b821f7d5fbff39a3edc1604)) - by @NotHyper-474 in [#6164](https://github.com/FunkinCrew/Funkin/pull/6164)
|
||||
- The Newgrounds logo no longer covers the intro text. ([28883fb](https://github.com/FunkinCrew/Funkin/commit/28883fb9ed025abf9f9aee2621f365cd6f4d8b6c)) - by @hucks5 in [#6196](https://github.com/FunkinCrew/Funkin/pull/6196)
|
||||
- Exiting from the Save Data Options menu no longer exits the Options Menu as well. ([26b253d](https://github.com/FunkinCrew/Funkin/commit/26b253d8cac97f0dcd09a5ef233f7058cd26008e)) - by @VirtuGuy in [#6036](https://github.com/FunkinCrew/Funkin/pull/6036)
|
||||
|
||||
+1
-1
@@ -197,7 +197,7 @@ You can view the `funkin-assets` license here: (https://github.com/FunkinCrew/fu
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020-2024 The Funkin' Crew Inc.
|
||||
Copyright 2020-2026 The Funkin' Crew Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
Submodule art updated: 2e68e371f8...429ab728b1
+1
-1
Submodule assets updated: 39d4b3fc95...5be03b9cd8
@@ -41,6 +41,22 @@
|
||||
"asset": "assets/shared/images/characters/sserafim/chaewon/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/characters/pico-dark/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/characters/pico/basic-animations/spritemap1.png",
|
||||
"blocksize": "6x6"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/characters/pico/explosion-death/spritemap1.png",
|
||||
"blocksize": "8x8"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/characters/pico/playable-animations/spritemap1.png",
|
||||
"blocksize": "6x6"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/holdCoverBlue.png",
|
||||
"blocksize": "4x4"
|
||||
@@ -109,6 +125,10 @@
|
||||
"asset": "assets/week1/image/erect/lights.png",
|
||||
"blocksize": "6x6"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/characters/pico/death/spritemap1.png",
|
||||
"blocksize": "6x6"
|
||||
},
|
||||
{
|
||||
"asset": "assets/week1/images/erect/crowd.png",
|
||||
"blocksize": "8x8"
|
||||
@@ -134,8 +154,8 @@
|
||||
"assets/preload/images/soundtray/",
|
||||
"assets/preload/images/stageBuild/",
|
||||
"assets/preload/images/storymenu/titles/",
|
||||
"assets/preload/images/titleEnter.png",
|
||||
"assets/preload/images/titleEnter_mobile.png",
|
||||
"assets/preload/images/title-screen-text/spritemap1.png",
|
||||
"assets/preload/images/title-screen-text-mobile/spritemap1.png",
|
||||
"assets/preload/images/ui/popup/pixel/*",
|
||||
"assets/shared/images/characters/abotPixel/*",
|
||||
"assets/shared/images/characters/bfPixel.png",
|
||||
|
||||
+318
-163
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"exclude": {},
|
||||
"defaultSeverity": "INFO",
|
||||
"extendsConfigPath": "",
|
||||
"numberOfCheckerThreads": 5,
|
||||
@@ -6,113 +8,124 @@
|
||||
"defineCombinations": [],
|
||||
"checks": [
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, prevents using anonymous types inline without creating a typedef for them.",
|
||||
"props": { "severity": "INFO" },
|
||||
"type": "Anonymous"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates spacing around array access syntax.",
|
||||
"props": {
|
||||
"spaceBefore": false,
|
||||
"spaceInside": false
|
||||
"spaceInside": false,
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "ArrayAccess"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, mandates array literals be defined with [] instead of 'new'.",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "ArrayLiteral"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates specific syntax for the use of array literals.",
|
||||
"props": {
|
||||
"allowSingleArgParens": true,
|
||||
"allowReturn": true,
|
||||
"allowFunction": true,
|
||||
"allowCurlyBody": true
|
||||
"allowCurlyBody": true,
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "ArrowFunction"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"avoidIdentifiers": []
|
||||
},
|
||||
"comment": "Specify a list of variable and function names which are not allowed and should be renamed. Doesn't support regex.",
|
||||
"props": { "avoidIdentifiers": [], "severity": "WARNING" },
|
||||
"type": "AvoidIdentifier"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, prevents imports like 'import flixel.*' from being used.",
|
||||
"props": { "severity": "INFO" },
|
||||
"type": "AvoidStarImport"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, prevents conditional compilation from crossing blocks. This prevents the case where conditional compilation could potentially create invalid syntax.",
|
||||
"props": { "severity": "INFO" },
|
||||
"type": "BlockBreakingConditional"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for the parameter of a catch block.",
|
||||
"props": {
|
||||
"format": "^(e|t|ex|[a-z][a-z][a-zA-Z]+)$"
|
||||
"format": "^(e|t|ex|[a-z][a-z][a-zA-Z]+)$",
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "CatchParameterName"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, flags when large blocks of similar code should be extracted to a function.",
|
||||
"props": {
|
||||
"thresholdSimilar": 120,
|
||||
"severityIdentical": "WARNING",
|
||||
"thresholdIdentical": 60
|
||||
"thresholdIdentical": 60,
|
||||
"severityIdentical": "IGNORE"
|
||||
},
|
||||
"type": "CodeSimilarity"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, flags Haxe code that is commented out. Unused code should be removed and accessed via version control later if needed.",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "CommentedOutCode"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, flags when conditional compilation blocks are not properly aligned. NOTE:Disabled because it's kinda broken.",
|
||||
"props": {
|
||||
"severity": "IGNORE",
|
||||
"policy": "aligned",
|
||||
"allowSingleline": true
|
||||
"allowSingleline": true,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "ConditionalCompilation"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for constant (static) identifiers.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^?:_[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$",
|
||||
"tokens": ["INLINE", "NOTINLINE"]
|
||||
"tokens": ["INLINE", "NOTINLINE"],
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "ConstantName"
|
||||
},
|
||||
{
|
||||
"comment": "Calculates the complexity of methods using the McCabe metric, and throws warnings at certain thresholds. Indicates that a function should be refactored or split up.",
|
||||
"props": {
|
||||
"thresholds": [
|
||||
{
|
||||
"complexity": 20,
|
||||
"severity": "WARNING"
|
||||
},
|
||||
{
|
||||
"complexity": 25,
|
||||
"severity": "ERROR"
|
||||
}
|
||||
{ "complexity": 20, "severity": "WARNING" },
|
||||
{ "complexity": 25, "severity": "ERROR" }
|
||||
],
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "CyclomaticComplexity"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, the default case of a switch statement must be last.",
|
||||
"props": { "severity": "INFO" },
|
||||
"type": "DefaultComesLast"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"lineStyle": "onestar",
|
||||
"startStyle": "ignore"
|
||||
},
|
||||
"comment": "Specifies the format for doc-style comments ().",
|
||||
"props": { "lineStyle": "onestar", "startStyle": "ignore" },
|
||||
"type": "DocCommentStyle"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, prevents the use of the Dynamic type.",
|
||||
"props": { "severity": "IGNORE" },
|
||||
"type": "Dynamic"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, ensures ~/.../ is used instead of 'new EReg()'",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "ERegLiteral"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces specific syntax on empty blocks (require only one line, etc.)",
|
||||
"props": {
|
||||
"tokens": [
|
||||
"CLASS_DEF",
|
||||
@@ -129,11 +142,13 @@
|
||||
"TRY",
|
||||
"CATCH"
|
||||
],
|
||||
"option": "empty"
|
||||
"option": "empty",
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "EmptyBlock"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, flags when consecutive empty lines are used.",
|
||||
"props": {
|
||||
"requireEmptyLineAfterPackage": true,
|
||||
"requireEmptyLineAfterInterface": true,
|
||||
@@ -141,17 +156,18 @@
|
||||
"allowEmptyLineAfterSingleLineComment": true,
|
||||
"max": 1,
|
||||
"requireEmptyLineAfterClass": true,
|
||||
"allowEmptyLineAfterMultiLineComment": true
|
||||
"allowEmptyLineAfterMultiLineComment": true,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "EmptyLines"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"enforceEmptyPackage": true
|
||||
},
|
||||
"comment": "If enabled, mandates the use of 'package;' on root level modules.",
|
||||
"props": { "enforceEmptyPackage": true, "severity": "WARNING" },
|
||||
"type": "EmptyPackage"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, provides advanced checking for when consecutive empty lines are used.",
|
||||
"props": {
|
||||
"none": [],
|
||||
"upto": [],
|
||||
@@ -160,11 +176,13 @@
|
||||
"max": 1,
|
||||
"ignore": [],
|
||||
"atleast": [],
|
||||
"skipSingleLineTypes": true
|
||||
"skipSingleLineTypes": true,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "ExtendedEmptyLines"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates documentation comments for public fields.",
|
||||
"props": {
|
||||
"requireParams": true,
|
||||
"fieldType": "BOTH",
|
||||
@@ -172,33 +190,33 @@
|
||||
"ignoreOverride": true,
|
||||
"tokens": ["ABSTRACT_DEF", "CLASS_DEF", "ENUM_DEF", "INTERFACE_DEF"],
|
||||
"modifier": "PUBLIC",
|
||||
"excludeNames": ["new", "toString"]
|
||||
"excludeNames": ["new", "toString"],
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "FieldDocComment"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"max": 1000,
|
||||
"ignoreEmptyLines": true,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"comment": "If enabled, warns if files exceed a certain number of lines.",
|
||||
"props": { "max": 1000, "ignoreEmptyLines": true, "severity": "IGNORE" },
|
||||
"type": "FileLength"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, requires modules match the casing of their file name.",
|
||||
"type": "FileNameCase",
|
||||
"props": {}
|
||||
"props": { "severity": "WARNING" }
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, requires inline/static variables to be declared final.",
|
||||
"type": "Final",
|
||||
"props": {}
|
||||
"props": { "severity": "IGNORE" }
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"option": "upperCase"
|
||||
},
|
||||
"comment": "If enabled, requires hexadecimal literals to be in upper case. 0xFFFFFF is valid and 0xffffff is invalid.",
|
||||
"props": { "option": "upperCase", "severity": "WARNING" },
|
||||
"type": "HexadecimalLiteral"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, warns if a local variable or parameter shadows an existing field.",
|
||||
"props": {
|
||||
"ignoreSetter": true,
|
||||
"ignoreFormat": "^(main|run)$",
|
||||
@@ -207,108 +225,167 @@
|
||||
"type": "HiddenField"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"character": " ",
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"comment": "If enabled, mandates indentation.",
|
||||
"props": { "character": "", "severity": "IGNORE" },
|
||||
"type": "Indentation"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates characters used for indentation.",
|
||||
"props": {
|
||||
"character": "space",
|
||||
"severity": "INFO",
|
||||
"severity": "IGNORE",
|
||||
"ignorePattern": "^$"
|
||||
},
|
||||
"type": "IndentationCharacter"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"ignoreReturnAssignments": true,
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"comment": "If enabled, warns about assignments performed within another expression, like 'if (a=b){}'",
|
||||
"props": { "ignoreReturnAssignments": true, "severity": "WARNING" },
|
||||
"type": "InnerAssignment"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces that interfaces should not contain properties.",
|
||||
"props": {
|
||||
"allowProperties": false,
|
||||
"allowMarkerInterfaces": true
|
||||
"allowMarkerInterfaces": true,
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"type": "Interface"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces a newline before the left curly brace of a block.",
|
||||
"props": {
|
||||
"option": "nl",
|
||||
"ignoreEmptySingleline": true,
|
||||
"tokens": [
|
||||
"CLASS_DEF",
|
||||
"ENUM_DEF",
|
||||
"ABSTRACT_DEF",
|
||||
"TYPEDEF_DEF",
|
||||
"INTERFACE_DEF",
|
||||
"FUNCTION",
|
||||
"FOR",
|
||||
"IF",
|
||||
"WHILE",
|
||||
"SWITCH",
|
||||
"TRY",
|
||||
"CATCH",
|
||||
"REIFICATION",
|
||||
"ARRAY_COMPREHENSION"
|
||||
],
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "LeftCurly"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces the left curly brace occurs on the end of the line, without a newline.",
|
||||
"props": {
|
||||
"option": "eol",
|
||||
"ignoreEmptySingleline": true,
|
||||
"tokens": ["OBJECT_DECL"],
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "LeftCurly"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, warns when lines of code exceed a certain length.",
|
||||
"props": {
|
||||
"max": 160,
|
||||
"ignorePattern": "^(\\s*['\"][^'\"]*['\"].*)$"
|
||||
"ignorePattern": "^(\\s*['\"][^'\"]*['\"].*)$",
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "LineLength"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates a specific naming convention for listener methods.",
|
||||
"props": {
|
||||
"listeners": ["addEventListener", "addListener", "on", "once"],
|
||||
"format": "^_?[a-z][a-zA-Z0-9]*$"
|
||||
"format": "^_?[a-z][a-zA-Z0-9]*$",
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "ListenerName"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for local variables.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[a-z][a-zA-Z0-9]*$",
|
||||
"tokens": []
|
||||
"tokens": [],
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "LocalVariableName"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"ignoreNumbers": [-1, 0, 1, 2],
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"comment": "If enabled, warns when unlabelled numeric values get used. Use named constants instead to improve code clarity.",
|
||||
"props": { "ignoreNumbers": [-1, 0, 1, 2], "severity": "INFO" },
|
||||
"type": "MagicNumber"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"format": "^[A-Z][a-zA-Z0-9]*$",
|
||||
"tokens": ["ABSTRACT"]
|
||||
},
|
||||
"type": "MemberName"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for public abstract member fields.",
|
||||
"props": {
|
||||
"format": "^[_a-zA-Z][a-z][a-zA-Z0-9]*$",
|
||||
"tokens": ["ABSTRACT"]
|
||||
"tokens": ["PUBLIC", "ABSTRACT"]
|
||||
},
|
||||
"type": "MemberName"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for private abstract member fields.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[_a-z][a-zA-Z0-9]*$",
|
||||
"tokens": ["PUBLIC", "PRIVATE", "CLASS", "TYPEDEF"]
|
||||
"format": "^[_a-zA-Z][a-z][a-zA-Z0-9]*$",
|
||||
"tokens": ["PRIVATE", "ABSTRACT"]
|
||||
},
|
||||
"type": "MemberName"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "Specifies a specific naming format for public class member fields.",
|
||||
"props": {
|
||||
"format": "^[_a-zA-Z][a-z][a-zA-Z0-9]*$",
|
||||
"tokens": ["PUBLIC", "CLASS"]
|
||||
},
|
||||
"type": "MemberName"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for private class member fields.",
|
||||
"props": {
|
||||
"format": "^[_a-zA-Z][a-z][a-zA-Z0-9]*$",
|
||||
"tokens": ["PRIVATE", "CLASS"]
|
||||
},
|
||||
"type": "MemberName"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for enum member fields.",
|
||||
"props": {
|
||||
"format": "^[_a-zA-Z][a-z][a-zA-Z0-9]*$",
|
||||
"tokens": ["ENUM"]
|
||||
},
|
||||
"type": "MemberName"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for typedef member fields.",
|
||||
"props": {
|
||||
"format": "^[_a-zA-Z][a-z][a-zA-Z0-9]*$",
|
||||
"tokens": ["TYPEDEF"]
|
||||
},
|
||||
"type": "MemberName"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces that modules should not exceed a certain number of methods.",
|
||||
"props": {
|
||||
"maxPrivate": 100,
|
||||
"maxPublic": 100,
|
||||
"maxTotal": 100
|
||||
"maxTotal": 100,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "MethodCount"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"max": 50,
|
||||
"ignoreEmptyLines": true,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"comment": "If enabled, enforces that methods should not exceed a certain number of lines of code, excluding empty lines.",
|
||||
"props": { "max": 50, "ignoreEmptyLines": true, "severity": "IGNORE" },
|
||||
"type": "MethodLength"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for method names.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[a-z][a-zA-Z0-9]*$",
|
||||
@@ -318,6 +395,7 @@
|
||||
"type": "MethodName"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific order for modifiers on fields.",
|
||||
"props": {
|
||||
"modifiers": [
|
||||
"PUBLIC_PRIVATE",
|
||||
@@ -333,57 +411,64 @@
|
||||
"type": "ModifierOrder"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, complains when a specific string literal is used in the same file multiple times. Use a named constant instead.",
|
||||
"props": {
|
||||
"minLength": 2,
|
||||
"ignore": "^\\s+$",
|
||||
"allowDuplicates": 5
|
||||
"allowDuplicates": 5,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "MultipleStringLiterals"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, complains when a multiple variables are declared in a single expression. Use individual lines.",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "MultipleVariableDeclarations"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates the use of braces on specific block types, unless they fit on a single line.",
|
||||
"props": {
|
||||
"allowSingleLineStatement": true,
|
||||
"tokens": ["ELSE_IF", "WHILE", "DO_WHILE"]
|
||||
"tokens": [
|
||||
"FUNCTION",
|
||||
"FOR",
|
||||
"IF",
|
||||
"ELSE_IF",
|
||||
"WHILE",
|
||||
"DO_WHILE",
|
||||
"CATCH"
|
||||
],
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "NeedBraces"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"max": 3,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"comment": "If enabled, complains when control flow statements are nested too deep (indicating high complexity).",
|
||||
"props": { "max": 3, "severity": "IGNORE" },
|
||||
"type": "NestedControlFlow"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"max": 1,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"comment": "If enabled, complains when 'for' loops are nested too deep (indicating high complexity).",
|
||||
"props": { "max": 1, "severity": "IGNORE" },
|
||||
"type": "NestedForDepth"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"max": 4
|
||||
},
|
||||
"comment": "If enabled, complains when 'if' statements are nested too deep (indicating high complexity).",
|
||||
"props": { "max": 4, "severity": "IGNORE" },
|
||||
"type": "NestedIfDepth"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"max": 1
|
||||
},
|
||||
"comment": "If enabled, complains when try/catch statements are nested too deep (indicating high complexity).",
|
||||
"props": { "max": 1, "severity": "INFO" },
|
||||
"type": "NestedTryDepth"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"option": "questionMark"
|
||||
},
|
||||
"comment": "If enabled, enforces ?value rather than value = null.",
|
||||
"props": { "option": "questionMark", "severity": "INFO" },
|
||||
"type": "NullableParameter"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces that operators should be surrounded by whitespace.",
|
||||
"props": {
|
||||
"oldFunctionTypePolicy": "none",
|
||||
"unaryOpPolicy": "none",
|
||||
@@ -401,6 +486,7 @@
|
||||
"type": "OperatorWhitespace"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces that most operators should be after a newline rather than before.",
|
||||
"props": {
|
||||
"tokens": [
|
||||
"+",
|
||||
@@ -443,54 +529,52 @@
|
||||
"type": "OperatorWrap"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"tokens": ["="],
|
||||
"option": "eol"
|
||||
},
|
||||
"comment": "If enabled, enforces that assignment operators should be before a newline rather than after.",
|
||||
"props": { "tokens": ["="], "option": "eol" },
|
||||
"type": "OperatorWrap"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for method parameter names.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^(_|[a-z][a-zA-Z0-9]*$)",
|
||||
"tokens": []
|
||||
"tokens": [],
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "ParameterName"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"max": 7,
|
||||
"ignoreOverriddenMethods": false
|
||||
},
|
||||
"comment": "If enabled, complains when there are too many parameters on a method.",
|
||||
"props": { "max": 7, "ignoreOverriddenMethods": false },
|
||||
"type": "ParameterNumber"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"comment": "If enabled, complains when you make a property accessor method public.",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "PublicAccessor"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"prohibitMeta": false
|
||||
},
|
||||
"comment": "If enabled, complains when an @:access metadata is redundant.",
|
||||
"props": { "prohibitMeta": false, "severity": "INFO" },
|
||||
"type": "RedundantAccessMeta"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"prohibitMeta": false
|
||||
},
|
||||
"comment": "If enabled, complains when an @:allow metadata is redundant.",
|
||||
"props": { "prohibitMeta": false, "severity": "INFO" },
|
||||
"type": "RedundantAllowMeta"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, complains when an access modifier is redundant.",
|
||||
"props": {
|
||||
"enforcePrivate": false,
|
||||
"enforcePublic": true,
|
||||
"enforcePublicPrivate": false
|
||||
"enforcePublicPrivate": false,
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"type": "RedundantModifier"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces that a return type is specified for all methods.",
|
||||
"props": {
|
||||
"enforceReturnTypeForAnonymous": false,
|
||||
"allowEmptyReturn": true,
|
||||
@@ -499,44 +583,63 @@
|
||||
"type": "Return"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"ignoreFormat": "^$",
|
||||
"max": 2,
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"comment": "If enabled, mandates a maximum number of 'return' statements in a method.",
|
||||
"props": { "ignoreFormat": "^$", "max": 2, "severity": "IGNORE" },
|
||||
"type": "ReturnCount"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforces a specific placement for curly braces. TODO:Choose between Allman/K&R and enforce project-wide.",
|
||||
"props": {
|
||||
"option": "aloneorsingle",
|
||||
"tokens": [
|
||||
"CLASS_DEF",
|
||||
"ENUM_DEF",
|
||||
"ABSTRACT_DEF",
|
||||
"TYPEDEF_DEF",
|
||||
"INTERFACE_DEF",
|
||||
"OBJECT_DECL",
|
||||
"FUNCTION",
|
||||
"FOR",
|
||||
"IF",
|
||||
"WHILE",
|
||||
"SWITCH",
|
||||
"TRY",
|
||||
"CATCH",
|
||||
"REIFICATION",
|
||||
"ARRAY_COMPREHENSION"
|
||||
],
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "RightCurly"
|
||||
},
|
||||
{
|
||||
"comment": "Enforces specific whitespace around certain separators, such as commas and semicolons.",
|
||||
"props": {
|
||||
"commaPolicy": "after",
|
||||
"semicolonPolicy": "after",
|
||||
"allowTrailingComma": false,
|
||||
"dotPolicy": "none"
|
||||
"dotPolicy": "none",
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "SeparatorWhitespace"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"tokens": [","],
|
||||
"option": "eol"
|
||||
},
|
||||
"comment": "Enforces specific line wrapping for certain separators, such as commas and semicolons.",
|
||||
"props": { "tokens": [","], "option": "eol" },
|
||||
"type": "SeparatorWrap"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, complains about overly complicated boolean expressions.",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "SimplifyBooleanExpression"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, complains about overly complicated boolean return statements.",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "SimplifyBooleanReturn"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, enforce spacing in and around conditional statements and certain operators.",
|
||||
"props": {
|
||||
"spaceIfCondition": "should",
|
||||
"spaceAroundBinop": true,
|
||||
@@ -550,94 +653,147 @@
|
||||
"type": "Spacing"
|
||||
},
|
||||
{
|
||||
"comment": "Enforce a specific string literal style (either ' or \").",
|
||||
"props": {
|
||||
"policy": "onlySingle",
|
||||
"allowException": true,
|
||||
"severity": "IGNORE"
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "StringLiteral"
|
||||
},
|
||||
{
|
||||
"comment": "Puts a warning on unresolved TODO/FIXME comments.",
|
||||
"props": {
|
||||
"format": "^\\s*(TODO|FIXME|HACK|XXX|BUG)"
|
||||
"format": "^\\s*(TODO|FIXME|HACK|XXX|BUG)",
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "TODOComment"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"ignorePattern": "^$"
|
||||
},
|
||||
"comment": "Checks if there are any tabs in the middle of a line.",
|
||||
"props": { "severity": "INFO" },
|
||||
"type": "TabForAligning"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"comment": "If enabled, complains whenever trace() gets used in code.",
|
||||
"props": { "severity": "IGNORE" },
|
||||
"type": "Trace"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, complains about trailing whitespaces at the end of lines.",
|
||||
"props": { "severity": "INFO" },
|
||||
"type": "TrailingWhitespace"
|
||||
},
|
||||
{
|
||||
"type": "Type",
|
||||
"props": {
|
||||
"ignoreEnumAbstractValues": true
|
||||
}
|
||||
"comment": "If enabled, requires type be specified on member fields.",
|
||||
"props": { "ignoreEnumAbstractValues": true, "severity": "INFO" },
|
||||
"type": "Type"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates documentation comments on classes and interfaces.",
|
||||
"props": {
|
||||
"tokens": ["CLASS_DEF", "ENUM_DEF", "INTERFACE_DEF", "TYPEDEF_DEF"]
|
||||
"tokens": ["CLASS_DEF", "ENUM_DEF", "INTERFACE_DEF", "TYPEDEF_DEF"],
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "TypeDocComment"
|
||||
},
|
||||
{
|
||||
"comment": "Specifies a specific naming format for abstract types.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[A-Z]+[a-zA-Z0-9]*$",
|
||||
"tokens": []
|
||||
"tokens": ["ABSTRACT"],
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"type": "TypeName"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "Specifies a specific naming format for class types.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[A-Z]+[a-zA-Z0-9]*$",
|
||||
"tokens": ["CLASS"],
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"type": "TypeName"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "Specifies a specific naming format for enum types.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[A-Z]+[a-zA-Z0-9]*$",
|
||||
"tokens": ["ENUM"],
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"type": "TypeName"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "Specifies a specific naming format for interface types.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[A-Z]+[a-zA-Z0-9]*$",
|
||||
"tokens": ["INTERFACE"],
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"type": "TypeName"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "Specifies a specific naming format for typedefs.",
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[A-Z]+[a-zA-Z0-9]*$",
|
||||
"tokens": ["TYPEDEF"],
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"type": "TypeName"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"comment": "If enabled, complains when a class with only static fields and values has a redundant constructor.",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "UnnecessaryConstructor"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"moduleTypeMap": {},
|
||||
"ignoreModules": []
|
||||
},
|
||||
"comment": "If enabled, complains when a module contains unused imports. NOTE:Sometimes complains about required imports.",
|
||||
"props": { "moduleTypeMap": {}, "ignoreModules": [], "severity": "INFO" },
|
||||
"type": "UnusedImport"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"severity": "WARNING"
|
||||
},
|
||||
"comment": "If enabled, complains when a local variable is unused.",
|
||||
"props": { "severity": "WARNING" },
|
||||
"type": "UnusedLocalVar"
|
||||
},
|
||||
{
|
||||
"type": "VarTypeHint",
|
||||
"comment": "If enabled, requires type hints for certain variables.",
|
||||
"props": {
|
||||
"typeHintPolicy": "enforce_all",
|
||||
"ignoreEnumAbstractValues": true,
|
||||
"severity": "IGNORE"
|
||||
}
|
||||
},
|
||||
"type": "VarTypeHint"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, complains when a class member variable is initialized at the class level rather than the constructor.",
|
||||
"type": "VariableInitialisation",
|
||||
"props": {
|
||||
"allowFinal": false
|
||||
"allowFinal": true,
|
||||
"severity": "IGNORE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates whitespace after certain tokens.",
|
||||
"props": {
|
||||
"tokens": [",", ";"],
|
||||
"allowTrailingComma": false
|
||||
"allowTrailingComma": false,
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "WhitespaceAfter"
|
||||
},
|
||||
{
|
||||
"comment": "If enabled, mandates whitespace around certain tokens.",
|
||||
"props": {
|
||||
"tokens": [
|
||||
"=",
|
||||
@@ -672,11 +828,10 @@
|
||||
"&=",
|
||||
"^=",
|
||||
"=>"
|
||||
]
|
||||
],
|
||||
"severity": "INFO"
|
||||
},
|
||||
"type": "WhitespaceAround"
|
||||
}
|
||||
],
|
||||
"exclude": {},
|
||||
"version": 1
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"name": "EliteMasterEric"
|
||||
}
|
||||
],
|
||||
"api_version": "0.7.0",
|
||||
"api_version": "0.8.0",
|
||||
"mod_version": "1.0.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"name": "EliteMasterEric"
|
||||
}
|
||||
],
|
||||
"api_version": "0.7.0",
|
||||
"api_version": "0.8.0",
|
||||
"mod_version": "1.0.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"name": "flixel-animate",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "c61476f4b3a3d225631ab3065e4e925a4b63c076",
|
||||
"ref": "c5e3393c70b71a191f20fa902114cea92042e486",
|
||||
"url": "https://github.com/MaybeMaru/flixel-animate"
|
||||
},
|
||||
{
|
||||
@@ -107,13 +107,6 @@
|
||||
"ref": "100f2c96beab619cfe72c567a058c41c71e3e998",
|
||||
"url": "https://github.com/haxeui/haxeui-flixel"
|
||||
},
|
||||
{
|
||||
"name": "hscript",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "dcc6f822e0866ca4fda300ed23e99559260f7bf5",
|
||||
"url": "https://github.com/FunkinCrew/hscript"
|
||||
},
|
||||
{
|
||||
"name": "hxcpp",
|
||||
"type": "git",
|
||||
@@ -159,7 +152,7 @@
|
||||
"name": "json2object",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "a7e4cef3d444bea8d8244d6e59ab82f5a593f597",
|
||||
"ref": "59e0467c953d1f26e3cbf2a070f140e2d2e8457d",
|
||||
"url": "https://github.com/FunkinCrew/json2object"
|
||||
},
|
||||
{
|
||||
@@ -176,7 +169,7 @@
|
||||
"name": "lime",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "318253ea3a28a3299a167748912c40444f403777",
|
||||
"ref": "cae0c0bc10915f6281625d07e5527f3cecb4347f",
|
||||
"url": "https://github.com/FunkinCrew/lime"
|
||||
},
|
||||
{
|
||||
@@ -190,15 +183,15 @@
|
||||
"name": "openfl",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "a69a6e778816c6a57eba7a91bfb0b60295508e88",
|
||||
"ref": "5399946b68fd98f345d6b3e52b95ed6b78bf427c",
|
||||
"url": "https://github.com/FunkinCrew/openfl"
|
||||
},
|
||||
{
|
||||
"name": "polymod",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "9824bf4f00863c152d5afe3ac4bbcac173d80684",
|
||||
"url": "https://github.com/larsiusprime/polymod"
|
||||
"ref": "d8f036378c4ff472f1922ad623b8800b6a061c1a",
|
||||
"url": "https://github.com/FunkinCrew/polymod"
|
||||
},
|
||||
{
|
||||
"name": "thx.core",
|
||||
|
||||
+282
-6
@@ -1,30 +1,306 @@
|
||||
{
|
||||
"disableFormatting": false,
|
||||
|
||||
"emptyLines": {
|
||||
"finalNewline": true,
|
||||
|
||||
"maxAnywhereInFile": 1,
|
||||
|
||||
"beforePackage": 0,
|
||||
"afterPackage": 1,
|
||||
|
||||
"importAndUsing": {
|
||||
"betweenImports": 0,
|
||||
"betweenImportsLevel": "firstLevelPackage",
|
||||
"beforeUsing": 1,
|
||||
"beforeType": 1
|
||||
},
|
||||
|
||||
"betweenTypes": 1,
|
||||
|
||||
"lineCommentsBetweenTypes": "keep",
|
||||
"lineCommentsBetweenFunctions": "keep",
|
||||
|
||||
"betweenSingleLineTypes": 0,
|
||||
|
||||
"beforeRightCurly": "remove",
|
||||
"afterLeftCurly": "remove",
|
||||
"afterReturn": "remove",
|
||||
|
||||
"beforeBlocks": "remove",
|
||||
"afterBlocks": "remove",
|
||||
|
||||
"enumAbstractEmptyLines": {
|
||||
"beginType": 0,
|
||||
"endType": 0,
|
||||
|
||||
"betweenVars": 0,
|
||||
"afterVars": 1,
|
||||
"betweenFunctions": 1,
|
||||
"existingBetweenFields": "keep"
|
||||
},
|
||||
|
||||
"classEmptyLines": {
|
||||
"beginType": 0,
|
||||
"endType": 0,
|
||||
|
||||
"betweenStaticVars": 0,
|
||||
"afterStaticVars": 1,
|
||||
"afterPrivateVars": 1,
|
||||
"betweenVars": 0,
|
||||
"afterVars": 1,
|
||||
"afterStaticFunctions": 1,
|
||||
"betweenStaticFunctions": 1,
|
||||
"afterPrivateFunctions": 1,
|
||||
"betweenFunctions": 1,
|
||||
|
||||
"existingBetweenFields": "keep"
|
||||
},
|
||||
|
||||
"macroClassEmptyLines": {
|
||||
"beginType": 0,
|
||||
"endType": 0,
|
||||
|
||||
"betweenStaticVars": 0,
|
||||
"afterStaticVars": 1,
|
||||
"afterPrivateVars": 1,
|
||||
"betweenVars": 0,
|
||||
"afterVars": 1,
|
||||
"afterStaticFunctions": 1,
|
||||
"betweenStaticFunctions": 1,
|
||||
"afterPrivateFunctions": 1,
|
||||
"betweenFunctions": 1,
|
||||
|
||||
"existingBetweenFields": "keep"
|
||||
},
|
||||
|
||||
"externClassEmptyLines": {
|
||||
"beginType": 0,
|
||||
"endType": 0,
|
||||
|
||||
"betweenVars": 0,
|
||||
"afterVars": 0,
|
||||
"betweenFunctions": 0,
|
||||
"existingBetweenFields": "keep"
|
||||
},
|
||||
|
||||
"abstractEmptyLines": {
|
||||
"beginType": 0,
|
||||
"endType": 0,
|
||||
|
||||
"betweenStaticVars": 0,
|
||||
"afterStaticVars": 1,
|
||||
"afterPrivateVars": 1,
|
||||
"betweenVars": 0,
|
||||
"afterVars": 1,
|
||||
"afterStaticFunctions": 1,
|
||||
"betweenStaticFunctions": 1,
|
||||
"afterPrivateFunctions": 1,
|
||||
"betweenFunctions": 1,
|
||||
|
||||
"existingBetweenFields": "keep"
|
||||
},
|
||||
|
||||
"interfaceEmptyLines": {
|
||||
"beginType": 0,
|
||||
"endType": 0,
|
||||
|
||||
"betweenVars": 0,
|
||||
"afterVars": 0,
|
||||
"betweenFunctions": 0,
|
||||
"existingBetweenFields": "keep"
|
||||
},
|
||||
|
||||
"enumEmptyLines": {
|
||||
"beginType": 0,
|
||||
"endType": 0,
|
||||
|
||||
"betweenFields": 0,
|
||||
"existingBetweenFields": "keep"
|
||||
},
|
||||
|
||||
"typedefEmptyLines": {
|
||||
"beginType": 0,
|
||||
"endType": 0,
|
||||
|
||||
"betweenFields": 0,
|
||||
"existingBetweenFields": "keep"
|
||||
},
|
||||
|
||||
"conditionalsEmptyLines": {
|
||||
"afterIf": 0,
|
||||
"beforeElse": 0,
|
||||
"afterElse": 0,
|
||||
"beforeEnd": 0,
|
||||
"beforeError": 0,
|
||||
"afterError": 0
|
||||
},
|
||||
|
||||
"beforeDocCommentEmptyLines": "one",
|
||||
"afterFieldsWithDocComments": "one",
|
||||
|
||||
"afterFileHeaderComment": 1,
|
||||
"betweenMultilineComments": 0
|
||||
},
|
||||
|
||||
"indentation": {
|
||||
"conditionalPolicy": "aligned",
|
||||
|
||||
"character": " ",
|
||||
"tabWidth": 2,
|
||||
|
||||
"trailingWhitespace": false,
|
||||
|
||||
"indentObjectLiteral": true,
|
||||
"indentComplexValueExpressions": false,
|
||||
"indentCaseLabels": true
|
||||
},
|
||||
|
||||
"lineEnds": {
|
||||
"lineEndCharacter": "auto",
|
||||
|
||||
"metadataType": "none",
|
||||
"metadataVar": "none",
|
||||
"metadataFunction": "none",
|
||||
"metadataOther": "none",
|
||||
|
||||
"caseColon": "after",
|
||||
"sharp": "after",
|
||||
|
||||
"leftCurly": "both",
|
||||
"rightCurly": "both",
|
||||
"emptyCurly": "noBreak",
|
||||
|
||||
"blockCurly": {
|
||||
"emptyCurly": "break",
|
||||
"leftCurly": "both",
|
||||
"rightCurly": "both"
|
||||
},
|
||||
|
||||
"anonFunctionCurly": {
|
||||
"emptyCurly": "break",
|
||||
"leftCurly": "both",
|
||||
"rightCurly": "both"
|
||||
},
|
||||
|
||||
"anonTypeCurly": {
|
||||
"emptyCurly": "break",
|
||||
"leftCurly": "both",
|
||||
"rightCurly": "both"
|
||||
},
|
||||
|
||||
"objectLiteralCurly": {
|
||||
"emptyCurly": "break",
|
||||
"leftCurly": "after",
|
||||
"rightCurly": "both"
|
||||
},
|
||||
"leftCurly": "both",
|
||||
"rightCurly": "both"
|
||||
|
||||
"typedefCurly": {
|
||||
"emptyCurly": "break",
|
||||
"leftCurly": "both",
|
||||
"rightCurly": "both"
|
||||
}
|
||||
},
|
||||
|
||||
"sameLine": {
|
||||
"ifBody": "same",
|
||||
"elseBody": "next",
|
||||
|
||||
"ifElse": "next",
|
||||
"doWhile": "next",
|
||||
|
||||
"elseIf": "same",
|
||||
"ifElseSemicolonNextLine": true,
|
||||
|
||||
"expressionIf": "same",
|
||||
"expressionIfWithBlocks": false,
|
||||
|
||||
"forBody": "next",
|
||||
|
||||
"comprehensionFor": "same",
|
||||
|
||||
"whileBody": "next",
|
||||
"doWhileBody": "next",
|
||||
|
||||
"doWhile": "same",
|
||||
|
||||
"tryBody": "next",
|
||||
"tryCatch": "next"
|
||||
"catchBody": "next",
|
||||
|
||||
"tryCatch": "next",
|
||||
|
||||
"caseBody": "next",
|
||||
"expressionCase": "keep",
|
||||
"expressionTry": "same",
|
||||
|
||||
"functionBody": "same",
|
||||
"anonFunctionBody": "same",
|
||||
"returnBody": "same",
|
||||
"returnBodySingleLine": "same",
|
||||
|
||||
"untypedBody": "same"
|
||||
},
|
||||
|
||||
"whitespace": {
|
||||
"switchPolicy": "around"
|
||||
}
|
||||
"parenConfig": {},
|
||||
"bracesConfig": {},
|
||||
"bracketConfig": {},
|
||||
|
||||
"typeParamOpenPolicy": "none",
|
||||
"typeParamClosePolicy": "none",
|
||||
|
||||
"typeExtensionPolicy": "after",
|
||||
|
||||
"commaPolicy": "onlyAfter",
|
||||
"dotPolicy": "none",
|
||||
"colonPolicy": "none",
|
||||
"caseColonPolicy": "onlyAfter",
|
||||
"objectFieldColonPolicy": "after",
|
||||
"typeHintColonPolicy": "none",
|
||||
"typeCheckColonPolicy": "around",
|
||||
"ternaryPolicy": "around",
|
||||
"semicolonPolicy": "onlyAfter",
|
||||
"ifPolicy": "after",
|
||||
"doPolicy": "after",
|
||||
"whilePolicy": "after",
|
||||
"forPolicy": "after",
|
||||
"switchPolicy": "around",
|
||||
"tryPolicy": "after",
|
||||
"catchPolicy": "after",
|
||||
"binopPolicy": "around",
|
||||
"intervalPolicy": "none",
|
||||
"arrowFunctionsPolicy": "around",
|
||||
"functionTypeHaxe3Policy": "none",
|
||||
"functionTypeHaxe4Policy": "around",
|
||||
|
||||
"compressSuccessiveParenthesis": true,
|
||||
"formatStringInterpolation": true,
|
||||
"addLineCommentSpace": true
|
||||
},
|
||||
|
||||
"wrapping": {
|
||||
"maxLineLength": 160,
|
||||
|
||||
"arrayWrap": {
|
||||
"defaultWrap": "noWrap",
|
||||
"defaultLocation": "afterLast",
|
||||
"defaultAdditionalIndent": 0
|
||||
},
|
||||
|
||||
"mapWrap": {
|
||||
"defaultWrap": "noWrap",
|
||||
"defaultLocation": "afterLast",
|
||||
"defaultAdditionalIndent": 0
|
||||
},
|
||||
|
||||
"typeParameter": {
|
||||
"defaultWrap": "noWrap"
|
||||
}
|
||||
},
|
||||
|
||||
"excludes": [
|
||||
"_Provide a list of regular expressions to exclude from formatting.",
|
||||
".haxelib",
|
||||
".git",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
+30
-20
@@ -32,13 +32,13 @@ class Project extends HXProject
|
||||
* REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES!
|
||||
* You only have to change it here, the rest of the game will query this value.
|
||||
*/
|
||||
static final VERSION:String = "0.8.1";
|
||||
static final VERSION:String = "0.8.2";
|
||||
|
||||
/**
|
||||
* 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 = 70;
|
||||
static final BUILD_NUMBER:Int = 76;
|
||||
|
||||
/**
|
||||
* The game's name. Used as the default window title.
|
||||
@@ -184,9 +184,7 @@ class Project extends HXProject
|
||||
/**
|
||||
* The songs whose audio files should be embedded inside the executable.
|
||||
*/
|
||||
static final EMBED_SONGS_AUDIO:Array<String> = [
|
||||
"spaghetti"
|
||||
];
|
||||
static final EMBED_SONGS_AUDIO:Array<String> = ["spaghetti"];
|
||||
|
||||
//
|
||||
// BUILD FLAGS
|
||||
@@ -216,6 +214,13 @@ class Project extends HXProject
|
||||
*/
|
||||
static final EXCLUDE_ARMV7:FeatureFlag = "EXCLUDE_ARMV7";
|
||||
|
||||
/**
|
||||
* `-DFORCE_BLEND_SHADER`
|
||||
* If this flag is enabled, the game will force the use of shaders to render
|
||||
* certain blend modes, even if the device supports the needed OpenGL extensions.
|
||||
*/
|
||||
static final FORCE_BLEND_SHADER:FeatureFlag = "FORCE_BLEND_SHADER";
|
||||
|
||||
/**
|
||||
* `-DGITHUB_BUILD`
|
||||
* If this flag is enabled, the game will use the configuration used by GitHub Actions
|
||||
@@ -659,7 +664,7 @@ class Project extends HXProject
|
||||
this.window.hardware = true;
|
||||
this.window.vsync = false;
|
||||
// force / allow high DPI
|
||||
this.window.allowHighDPI = true;
|
||||
this.window.allowHighDPI = !isMac();
|
||||
|
||||
if (isWeb())
|
||||
{
|
||||
@@ -855,6 +860,11 @@ class Project extends HXProject
|
||||
// Should be true on mobile builds.
|
||||
CENSOR_EXPLETIVES.apply(this, isMobile());
|
||||
|
||||
// Should be true on Android.
|
||||
// Some Android devices support native blends, but it causes issues
|
||||
// with other shaders, so we'll disable it for now.
|
||||
FORCE_BLEND_SHADER.apply(this, isAndroid());
|
||||
|
||||
//
|
||||
// FEATURE FLAGS
|
||||
//
|
||||
@@ -1325,7 +1335,6 @@ class Project extends HXProject
|
||||
addHaxelib('flixel'); // Game engine
|
||||
|
||||
addHaxelib('flixel-addons'); // Additional utilities for Flixel
|
||||
addHaxelib('hscript'); // Scripting
|
||||
|
||||
if (FEATURE_HAXEUI.isEnabled(this))
|
||||
{
|
||||
@@ -1500,8 +1509,6 @@ class Project extends HXProject
|
||||
addAssetLibrary("sserafim", shouldEmbed, shouldPreload);
|
||||
addAssetPath("assets/sserafim", "assets/sserafim", "sserafim", ["*"], exclude, shouldEmbed, "assets");
|
||||
|
||||
|
||||
|
||||
// Art asset library (where README and CHANGELOG pull from)
|
||||
var shouldEmbedArt = false;
|
||||
var shouldPreloadArt = false;
|
||||
@@ -1548,6 +1555,10 @@ class Project extends HXProject
|
||||
{
|
||||
assetsPath = Path.join([basePath, "app", "src", "main", "assets", "assets"]);
|
||||
}
|
||||
else if (isIOS())
|
||||
{
|
||||
assetsPath = Path.join([exportPath, platform, '${this.app.file}', "assets", "assets"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
@@ -2092,7 +2103,7 @@ class Project extends HXProject
|
||||
@SuppressWarnings('checkstyle:Dynamic')
|
||||
function checkLibraries():Void
|
||||
{
|
||||
var diffrentLibraries:Map<String, Array<String>> = new Map<String, Array<String>>();
|
||||
var differentLibraries:Map<String, Array<String>> = new Map<String, Array<String>>();
|
||||
|
||||
var hmmData:Dynamic = haxe.Json.parse(sys.io.File.getContent(#if ios '../../../../../' + #end 'hmm.json'));
|
||||
|
||||
@@ -2113,11 +2124,11 @@ class Project extends HXProject
|
||||
switch (library.type)
|
||||
{
|
||||
case 'haxelib':
|
||||
diffrentLibraries.set(libraryName, [libraryDev, libraryCurrentCommitHash, library.version, 'haxelib']);
|
||||
differentLibraries.set(libraryName, [libraryDev, libraryCurrentCommitHash, library.version, 'haxelib']);
|
||||
case 'git':
|
||||
if (libraryCurrentCommitHash != library.ref)
|
||||
{
|
||||
diffrentLibraries.set(libraryName, [libraryDev, libraryCurrentCommitHash, library.ref, 'git']);
|
||||
differentLibraries.set(libraryName, [libraryDev, libraryCurrentCommitHash, library.ref, 'git']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2128,18 +2139,18 @@ class Project extends HXProject
|
||||
case 'haxelib':
|
||||
if (library.version != libraryCurrentVersion)
|
||||
{
|
||||
diffrentLibraries.set(libraryName, ['', libraryCurrentVersion, library.version, 'haxelib']);
|
||||
differentLibraries.set(libraryName, ['', libraryCurrentVersion, library.version, 'haxelib']);
|
||||
}
|
||||
case 'git':
|
||||
if (libraryCurrentCommitHash != library.ref)
|
||||
{
|
||||
diffrentLibraries.set(libraryName, ['', libraryCurrentCommitHash, library.ref, 'git']);
|
||||
differentLibraries.set(libraryName, ['', libraryCurrentCommitHash, library.ref, 'git']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Lambda.count(diffrentLibraries) > 0)
|
||||
if (Lambda.count(differentLibraries) > 0)
|
||||
{
|
||||
warn("Some libraries differ from the versions defined in hmm.json.".bold().yellow());
|
||||
warn("To ensure consistency, it's recommended to stop the build and run `hmm reinstall [library]`.".bold().yellow());
|
||||
@@ -2147,9 +2158,9 @@ class Project extends HXProject
|
||||
|
||||
Sys.println('');
|
||||
|
||||
for (libraryName in diffrentLibraries.keys())
|
||||
for (libraryName in differentLibraries.keys())
|
||||
{
|
||||
var infos:Null<Array<String>> = diffrentLibraries.get(libraryName);
|
||||
var infos:Null<Array<String>> = differentLibraries.get(libraryName);
|
||||
|
||||
if (infos == null) continue;
|
||||
|
||||
@@ -2162,7 +2173,6 @@ class Project extends HXProject
|
||||
{
|
||||
Sys.println("- " + libraryName.replace(",", ".") + " (Development Build)".blue().bold());
|
||||
Sys.println((" Path: " + haxe.io.Path.removeTrailingSlashes(devPath)).blue());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2181,7 +2191,7 @@ class Project extends HXProject
|
||||
{
|
||||
var path = getLibraryCurrentFile(libraryName);
|
||||
|
||||
if (sys.FileSystem.exists(path)) return sys.io.File.getContent(path);
|
||||
if (sys.FileSystem.exists(path)) return sys.io.File.getContent(path).trim();
|
||||
|
||||
return '';
|
||||
}
|
||||
@@ -2190,7 +2200,7 @@ class Project extends HXProject
|
||||
{
|
||||
var path = getLibraryDevFile(libraryName);
|
||||
|
||||
if (sys.FileSystem.exists(path)) return sys.io.File.getContent(path);
|
||||
if (sys.FileSystem.exists(path)) return sys.io.File.getContent(path).trim();
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package scripts;
|
||||
|
||||
using DateTools;
|
||||
|
||||
class Postbuild
|
||||
{
|
||||
static function main()
|
||||
{
|
||||
/* trace('creating NG zip');
|
||||
var binFolder:String = 'export/release/html5/bin/';
|
||||
|
||||
var date = Date.now();
|
||||
var outputName:String = 'NG-' + date.format("%Y%m%d--%H%M") + '.zip';
|
||||
|
||||
var out = sys.io.File.write(outputName);
|
||||
var zip = new haxe.zip.Writer(out);
|
||||
zip.write(getEntries(binFolder));
|
||||
trace('Finished creating ZIP');
|
||||
*/
|
||||
}
|
||||
|
||||
static function getEntries(dir:String, entries:List<haxe.zip.Entry> = null, inDir:Null<String> = null)
|
||||
{
|
||||
if (entries == null) entries = new List<haxe.zip.Entry>();
|
||||
if (inDir == null) inDir = dir;
|
||||
|
||||
for (file in sys.FileSystem.readDirectory(dir))
|
||||
{
|
||||
var path = haxe.io.Path.join([dir, file]);
|
||||
if (sys.FileSystem.isDirectory(path)) getEntries(path, entries, inDir);
|
||||
else
|
||||
{
|
||||
var bytes:haxe.io.Bytes = haxe.io.Bytes.ofData(sys.io.File.getBytes(path).getData());
|
||||
var entry:haxe.zip.Entry =
|
||||
{
|
||||
fileName: StringTools.replace(path, inDir, ""),
|
||||
fileSize: bytes.length,
|
||||
fileTime: Date.now(),
|
||||
compressed: false,
|
||||
dataSize: sys.FileSystem.stat(path).size,
|
||||
data: bytes,
|
||||
crc32: haxe.crypto.Crc32.make(bytes)
|
||||
};
|
||||
entries.push(entry);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
import haxe.Json;
|
||||
import haxe.Log;
|
||||
import haxe.macro.Compiler;
|
||||
import sys.FileSystem;
|
||||
import sys.io.File;
|
||||
|
||||
using StringTools;
|
||||
|
||||
#if !interp
|
||||
package scripts;
|
||||
|
||||
// needs this thing to be run by interp, just for me lol!
|
||||
#end
|
||||
// general use of this!!
|
||||
// if run in INTERP MODE:
|
||||
// - scripts>haxe --main SongConverter --interp
|
||||
// - song to convert: ../assets/preload/data/
|
||||
// it will run through EVERY folder in there and convert the old songs to the new format!
|
||||
// TODO make instructions for the C# script version!
|
||||
class SongConverter
|
||||
{
|
||||
public static var VERSION:String = '0.1.0';
|
||||
|
||||
// TODO
|
||||
// Update engine shit, to accomodate single file
|
||||
static function main()
|
||||
{
|
||||
// trace(Compiler.get);
|
||||
// trace(Compiler.getDefine('sysfsd'));
|
||||
|
||||
Sys.stdout().writeString('Please type directory you want to convert: ');
|
||||
Sys.stdout().flush();
|
||||
final input = Sys.stdin().readLine();
|
||||
trace('Hello ${input}!');
|
||||
|
||||
/* Sys.stdout().writeString('What type of conversion do you need?\n\t0: Old charts to new chart\n\t1: new chart to .... new chart? lol\n>');
|
||||
Sys.stdout().flush();
|
||||
final deleteShitIDK = Std.parseInt(Sys.stdin().readLine());
|
||||
|
||||
trace(deleteShitIDK); */
|
||||
|
||||
var songCount:Int = 0;
|
||||
|
||||
for (fileThing in FileSystem.readDirectory('${input}/.'))
|
||||
{
|
||||
// trace(fileThing);
|
||||
// trace(FileSystem.isDirectory(${input} + "/" + fileThing));
|
||||
|
||||
if (FileSystem.isDirectory(${input} + "/" + fileThing) && fileThing.toLowerCase() != 'smash')
|
||||
{
|
||||
trace('Formatting $fileThing');
|
||||
formatSongs(fileThing, input);
|
||||
|
||||
songCount++;
|
||||
}
|
||||
}
|
||||
|
||||
trace('CONVERTED $songCount SONGS!!');
|
||||
|
||||
var timer:Int = 5;
|
||||
|
||||
while (timer > 0)
|
||||
{
|
||||
Sys.stdout().writeString('CLOSING IN $timer SECONDS!');
|
||||
Sys.sleep(1);
|
||||
Sys.print("\r");
|
||||
timer -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static function formatSongs(songName:String, root:String)
|
||||
{
|
||||
var existsArray:Array<Bool> = [];
|
||||
|
||||
var songMap:Map<String, Dynamic> = new Map();
|
||||
var speedMap:Map<String, Dynamic> = new Map();
|
||||
|
||||
for (songFile in FileSystem.readDirectory('$root/$songName/'))
|
||||
{
|
||||
if (!songFile.endsWith('json') || !songFile.startsWith(songName)) continue; // ACCOMODATE FOR PICO SPEAKER EVENTUALLY!
|
||||
|
||||
var funnyFile:Dynamic = cast Json.parse(File.getContent('$root/$songName/$songFile'));
|
||||
|
||||
var songSplit:Array<String> = songFile.split('-');
|
||||
var songShit:String = 'normal';
|
||||
|
||||
if (songSplit.length > 1)
|
||||
{
|
||||
if (songSplit[1] == 'new.json')
|
||||
{
|
||||
FileSystem.deleteFile('$root/$songName/$songFile');
|
||||
trace('DELETEING $songFile');
|
||||
continue;
|
||||
}
|
||||
|
||||
songShit = songSplit[1].substr(0, songSplit[1].length - 5);
|
||||
}
|
||||
|
||||
if (songShit != 'normal')
|
||||
{
|
||||
songMap[songShit] = funnyFile.song.notes;
|
||||
speedMap[songShit] = funnyFile.song.speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
|
||||
if (funnyFile.song.notes is Array)
|
||||
{
|
||||
songMap[songShit] = funnyFile.song.notes[1];
|
||||
speedMap[songShit] = funnyFile.song.speed[1];
|
||||
}
|
||||
else if (Type.typeof(funnyFile.song.notes) == TObject)
|
||||
{
|
||||
var dipshitMap:Map<String, Dynamic> = cast funnyFile.song.notes;
|
||||
|
||||
// songMap[songShit] = dipshitMap.get('normal');
|
||||
}
|
||||
else
|
||||
{
|
||||
songMap[songShit] = funnyFile.song.notes;
|
||||
speedMap[songShit] = funnyFile.song.speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fileNormal:Dynamic = cast Json.parse(File.getContent('$root/$songName/$songName.json'));
|
||||
|
||||
fileNormal.song.notes = songMap;
|
||||
fileNormal.song.speed = speedMap;
|
||||
|
||||
fileNormal.song.hasDialogueFile = false;
|
||||
fileNormal.song.stageDefault = getStage(songName);
|
||||
|
||||
fileNormal.version = 'FNF SongConverter $VERSION';
|
||||
|
||||
var daJson = Json.stringify(fileNormal, null, "\t");
|
||||
// trace(daJson);
|
||||
File.saveContent('$root/$songName/$songName.json', daJson);
|
||||
}
|
||||
|
||||
static function noteCleaner(notes:Array<Dynamic>):Array<Dynamic>
|
||||
{
|
||||
var swagArray:Array<Dynamic> = [];
|
||||
for (i in notes)
|
||||
{
|
||||
if (i.sectionNotes.length > 0) swagArray.push(i);
|
||||
}
|
||||
|
||||
return swagArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quicky lil function just for me formatting the old songs hehehe
|
||||
*/
|
||||
static function getStage(songName:String):String
|
||||
{
|
||||
switch (songName)
|
||||
{
|
||||
case 'spookeez' | 'monster' | 'south':
|
||||
return "spooky";
|
||||
case 'pico' | 'blammed' | 'philly':
|
||||
return 'philly';
|
||||
case "milf" | 'satin-panties' | 'high':
|
||||
return 'limo';
|
||||
case "cocoa" | 'eggnog':
|
||||
return 'mall';
|
||||
case 'winter-horrorland':
|
||||
return 'mallEvil';
|
||||
case 'senpai' | 'roses':
|
||||
return 'school';
|
||||
case 'thorns':
|
||||
return 'schoolEvil';
|
||||
case 'guns' | 'stress' | 'ugh':
|
||||
return 'tank';
|
||||
default:
|
||||
return 'stage';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import csv
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
# This script imports a CSV file, and bulk updates the chart metadata files
|
||||
#
|
||||
# Requirements:
|
||||
# python3 I believe
|
||||
# and you need to have npm/npx, as we use that to run prettier to keep the same formatting
|
||||
#
|
||||
# Usage:
|
||||
# In our Google Sheets, export as .csv file
|
||||
#
|
||||
# Then in here from within this directory, run:
|
||||
# python3 sheetsToCharts.py path/to/csv/file.csv
|
||||
#
|
||||
# use from within this directory, as the paths to the chart metadata files are relative to this directory
|
||||
|
||||
|
||||
# Get the command line argument, if one wasn't already provided
|
||||
if len(sys.argv) < 2:
|
||||
csv_file = input("Please provide the CSV file path: ")
|
||||
else:
|
||||
csv_file = sys.argv[1]
|
||||
|
||||
# Open the CSV file
|
||||
with open(csv_file, 'r') as file:
|
||||
reader = csv.reader(file)
|
||||
|
||||
# Which rows for each difficulty are we looking for
|
||||
diff_rows = {}
|
||||
|
||||
# Iterate over each row in the CSV file
|
||||
for idx, row in enumerate(reader):
|
||||
|
||||
# The first row has headers for the columns, stuff like "Easy - Difficulty", so we want
|
||||
# to know which rows corresponde to which difficulty
|
||||
if idx == 0:
|
||||
pattern = r"\b(?:difficulty)\b"
|
||||
|
||||
diff_cols = []
|
||||
diff_words = []
|
||||
|
||||
# Go through each column, and see if it includes the word "difficulty"
|
||||
# then append the column index, and the first word of the column (usually what difficulty it is, "easy", "normal", "hard", etc.)
|
||||
for colindex, column in enumerate(row):
|
||||
txt = re.findall(pattern, column, flags=re.IGNORECASE)
|
||||
if txt:
|
||||
diff_cols.append(colindex)
|
||||
diff_words.append(column.split()[0].lower())
|
||||
|
||||
# We then create a dictionary for use later, where the key is the difficulty, and the value is the column index
|
||||
diff_rows = dict(zip(diff_words, diff_cols))
|
||||
else:
|
||||
# Some song title parsing, to match the filenames we used in for the chart files
|
||||
song_title = row[1].lower() # we use lowercase for filenames, SOUTH -> south
|
||||
song_title = song_title.replace(" ", "-") # usually we change spaces to dashes, PHILLY NICE -> philly-nice
|
||||
song_title = song_title.replace(".", "") # M.I.L.F. -> milf
|
||||
song_title = song_title.replace("'", "") # blazin' -> blazin
|
||||
|
||||
# we open the chart metadata json file for writing and reading ("r+")
|
||||
with open(f"../../../assets/preload/data/songs/{song_title}/{song_title}-metadata.json", "r+") as chart_metadata_file:
|
||||
|
||||
json_data = json.load(chart_metadata_file) # load the file as a python data structure/dict
|
||||
play_data = json_data["playData"] # we want to modify the "playData" section, as that holds the difficulty
|
||||
|
||||
# note to self, python equals(=) operator seems to create a reference for the variable,
|
||||
# so modifying play_data will also modify json_data, so we can save json_data easily later
|
||||
|
||||
# if the chart metadata file doesn't already have a "ratings" dict/section, we create one here with 0 for each
|
||||
if "ratings" not in play_data:
|
||||
play_data["ratings"] = {'easy': 0, 'normal': 0, 'hard': 0}
|
||||
|
||||
ratings = play_data["ratings"]
|
||||
|
||||
# Now we go through our data from the csv file, and the data we kept from the columns there
|
||||
# will be put into our new ratings var, if it exists
|
||||
for diff, col in diff_rows.items():
|
||||
if row[col] == "":
|
||||
continue
|
||||
if diff in ratings:
|
||||
ratings[diff] = round(float(row[col])) # convert the string to a float, and then round it to nearest int
|
||||
|
||||
# Convert the python json_data dict back to a json string
|
||||
json_output = json.dumps(json_data)
|
||||
|
||||
# Write the json string back to the file, and truncate the rest of the file
|
||||
chart_metadata_file.seek(0)
|
||||
chart_metadata_file.write(json_output)
|
||||
chart_metadata_file.truncate()
|
||||
chart_metadata_file.close()
|
||||
|
||||
# Bit hacky, but we simply run `npx prettier` using the same rules we use for FNF here
|
||||
# essnetially running it via cli and passing in the songs folder and our prettier config file
|
||||
# This should make the spacing and formatting consistent
|
||||
command = "npx prettier ../../../assets/preload/data/songs --write --config ../../../.prettierrc.js"
|
||||
subprocess.run(command, shell=True)
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Find all files in OGG format and execute ffmpeg to convert them to MP3
|
||||
# Skips files that are already in MP3 format
|
||||
find "../assets/" -name "*.ogg" -exec sh -c 'ffmpeg -n -i {} "${0%.*}.mp3"' {} \;
|
||||
@@ -0,0 +1,218 @@
|
||||
import requests
|
||||
import sys
|
||||
|
||||
PROJECT_ID = 5582053
|
||||
|
||||
TEMPLATE = 'jQuery("#new-event-name").val("{event}"); jQuery("#save-new-event").click();'
|
||||
|
||||
EVENT_TEMPLATES = [
|
||||
"start-game",
|
||||
"watch-cartoon",
|
||||
"open-credits",
|
||||
|
||||
"start-song_{songId}-{variation}",
|
||||
"blueballs_{songId}-{variation}",
|
||||
"complete-song_{songId}-{variation}",
|
||||
"start-level_{levelId}",
|
||||
"complete-level_{levelId}",
|
||||
"earn-rank_{rankName}",
|
||||
]
|
||||
|
||||
SONG_IDS = [
|
||||
{ "songId": "tutorial", "variation": "default" },
|
||||
{ "songId": "tutorial", "variation": "erect" },
|
||||
{ "songId": "tutorial", "variation": "pico" },
|
||||
|
||||
{ "songId": "bopeebo", "variation": "default" },
|
||||
{ "songId": "bopeebo", "variation": "erect" },
|
||||
{ "songId": "bopeebo", "variation": "pico" },
|
||||
|
||||
{ "songId": "fresh", "variation": "default" },
|
||||
{ "songId": "fresh", "variation": "erect" },
|
||||
{ "songId": "fresh", "variation": "pico" },
|
||||
|
||||
{ "songId": "dadbattle", "variation": "default" },
|
||||
{ "songId": "dadbattle", "variation": "erect" },
|
||||
{ "songId": "dadbattle", "variation": "pico" },
|
||||
|
||||
{ "songId": "spookeez", "variation": "default" },
|
||||
{ "songId": "spookeez", "variation": "erect" },
|
||||
{ "songId": "spookeez", "variation": "pico" },
|
||||
|
||||
{ "songId": "south", "variation": "default" },
|
||||
{ "songId": "south", "variation": "erect" },
|
||||
{ "songId": "south", "variation": "pico" },
|
||||
|
||||
{ "songId": "monster", "variation": "default" },
|
||||
{ "songId": "monster", "variation": "erect" },
|
||||
{ "songId": "monster", "variation": "pico" },
|
||||
|
||||
{ "songId": "pico", "variation": "default" },
|
||||
{ "songId": "pico", "variation": "erect" },
|
||||
{ "songId": "pico", "variation": "pico" },
|
||||
|
||||
{ "songId": "philly-nice", "variation": "default" },
|
||||
{ "songId": "philly-nice", "variation": "erect" },
|
||||
{ "songId": "philly-nice", "variation": "pico" },
|
||||
|
||||
{ "songId": "blammed", "variation": "default" },
|
||||
{ "songId": "blammed", "variation": "erect" },
|
||||
{ "songId": "blammed", "variation": "pico" },
|
||||
|
||||
{ "songId": "satin-panties", "variation": "default" },
|
||||
{ "songId": "satin-panties", "variation": "erect" },
|
||||
{ "songId": "satin-panties", "variation": "pico" },
|
||||
|
||||
{ "songId": "high", "variation": "default" },
|
||||
{ "songId": "high", "variation": "erect" },
|
||||
{ "songId": "high", "variation": "pico" },
|
||||
|
||||
{ "songId": "milf", "variation": "default" },
|
||||
{ "songId": "milf", "variation": "erect" },
|
||||
{ "songId": "milf", "variation": "pico" },
|
||||
|
||||
{ "songId": "cocoa", "variation": "default" },
|
||||
{ "songId": "cocoa", "variation": "erect" },
|
||||
{ "songId": "cocoa", "variation": "pico" },
|
||||
|
||||
{ "songId": "eggnog", "variation": "default" },
|
||||
{ "songId": "eggnog", "variation": "erect" },
|
||||
{ "songId": "eggnog", "variation": "pico" },
|
||||
|
||||
{ "songId": "winter-horrorland", "variation": "default" },
|
||||
{ "songId": "winter-horrorland", "variation": "erect" },
|
||||
{ "songId": "winter-horrorland", "variation": "pico" },
|
||||
|
||||
{ "songId": "senpai", "variation": "default" },
|
||||
{ "songId": "senpai", "variation": "erect" },
|
||||
{ "songId": "senpai", "variation": "pico" },
|
||||
|
||||
{ "songId": "roses", "variation": "default" },
|
||||
{ "songId": "roses", "variation": "erect" },
|
||||
{ "songId": "roses", "variation": "pico" },
|
||||
|
||||
{ "songId": "thorns", "variation": "default" },
|
||||
{ "songId": "thorns", "variation": "erect" },
|
||||
{ "songId": "thorns", "variation": "pico" },
|
||||
|
||||
{ "songId": "ugh", "variation": "default" },
|
||||
{ "songId": "ugh", "variation": "erect" },
|
||||
{ "songId": "ugh", "variation": "pico" },
|
||||
|
||||
{ "songId": "guns", "variation": "default" },
|
||||
{ "songId": "guns", "variation": "erect" },
|
||||
{ "songId": "guns", "variation": "pico" },
|
||||
|
||||
{ "songId": "stress", "variation": "default" },
|
||||
{ "songId": "stress", "variation": "erect" },
|
||||
{ "songId": "stress", "variation": "pico" },
|
||||
|
||||
{ "songId": "darnell", "variation": "default" },
|
||||
{ "songId": "darnell", "variation": "erect" },
|
||||
{ "songId": "darnell", "variation": "bf" },
|
||||
|
||||
{ "songId": "lit-up", "variation": "default" },
|
||||
{ "songId": "lit-up", "variation": "erect" },
|
||||
{ "songId": "lit-up", "variation": "bf" },
|
||||
|
||||
{ "songId": "2hot", "variation": "default" },
|
||||
{ "songId": "2hot", "variation": "erect" },
|
||||
{ "songId": "2hot", "variation": "bf" },
|
||||
|
||||
{ "songId": "blazin", "variation": "default" },
|
||||
{ "songId": "blazin", "variation": "erect" },
|
||||
{ "songId": "blazin", "variation": "bf" },
|
||||
]
|
||||
|
||||
LEVEL_IDS = [
|
||||
"tutorial",
|
||||
"week1",
|
||||
"week2",
|
||||
"week3",
|
||||
"week4",
|
||||
"week5",
|
||||
"week6",
|
||||
"week7",
|
||||
"weekend1",
|
||||
"week8",
|
||||
]
|
||||
|
||||
RANKS = [
|
||||
"PERFECT_GOLD",
|
||||
"PERFECT",
|
||||
"EXCELLENT",
|
||||
"GREAT",
|
||||
"GOOD",
|
||||
"SHIT",
|
||||
]
|
||||
|
||||
|
||||
def formatEventSong(event):
|
||||
result = []
|
||||
for song in SONG_IDS:
|
||||
result.append(event.format(
|
||||
songId = song["songId"],
|
||||
variation = song["variation"]
|
||||
))
|
||||
return result
|
||||
|
||||
def formatEventLevel(event):
|
||||
result = []
|
||||
for level in LEVEL_IDS:
|
||||
result.append(event.format(
|
||||
levelId = level
|
||||
))
|
||||
return result
|
||||
|
||||
def formatEventRank(event):
|
||||
result = []
|
||||
for rank in RANKS:
|
||||
result.append(event.format(
|
||||
rankName = rank
|
||||
))
|
||||
return result
|
||||
|
||||
def formatEvent(event):
|
||||
if "{songId}" in event or "{variation}" in event:
|
||||
return formatEventSong(event)
|
||||
|
||||
if "{levelId}" in event:
|
||||
return formatEventLevel(event)
|
||||
|
||||
if "{rankName}" in event:
|
||||
return formatEventRank(event)
|
||||
|
||||
return [event]
|
||||
|
||||
def createEvent(event):
|
||||
print(TEMPLATE.format(event=event))
|
||||
|
||||
def main():
|
||||
event_list = []
|
||||
|
||||
for event in EVENT_TEMPLATES:
|
||||
event_list.extend(formatEvent(event))
|
||||
|
||||
# Filter out duplicates then sort alphabetically
|
||||
event_list = list(set(event_list))
|
||||
event_list.sort()
|
||||
|
||||
for event in event_list:
|
||||
createEvent(event)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
#function sleep(ms) {
|
||||
# console.log("sleeping for " + ms + "ms");
|
||||
# return new Promise(resolve => setTimeout(resolve, ms));
|
||||
#}
|
||||
#async function example() {
|
||||
# jQuery("#new-event-name").val("blueballs_blazin-erect"); jQuery("#save-new-event").click();
|
||||
# await sleep(1000);
|
||||
# jQuery("#new-event-name").val("blueballs_blazin-erect"); jQuery("#save-new-event").click();
|
||||
# await sleep(1000);
|
||||
#}
|
||||
#example();
|
||||
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
color 0a
|
||||
cd ..
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build html5 -final
|
||||
echo UPLOADING TO ITCH
|
||||
butler push ./export/release/html5/bin ninja-muffin24/funkin-secret:html5
|
||||
butler status ninja-muffin24/funkin-secret:html5
|
||||
echo ITCH SHIT UPDATED LMAOOOOO
|
||||
pause
|
||||
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
color 0a
|
||||
cd ..
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build html5 -debug
|
||||
echo UPLOADING TO ITCH
|
||||
butler push ./export/debug/html5/bin ninja-muffin24/funkin-secret:html5
|
||||
butler status ninja-muffin24/funkin-secret:html5
|
||||
echo ITCH SHIT UPDATED LMAOOOOO
|
||||
pause
|
||||
@@ -0,0 +1,15 @@
|
||||
@echo off
|
||||
color 0a
|
||||
cd ..
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build windows -final -clean
|
||||
echo UPLOADING 64 BIT VERSION TO ITCH
|
||||
butler push ./export/release/windows/bin ninja-muffin24/funkin:windows-64bit
|
||||
lime build windows -final -32
|
||||
echo UPLOADING 32 BIT VERSION TO ITCH
|
||||
butler push ./export/release/windows/bin ninja-muffin24/funkin:windows-32bit
|
||||
butler status ninja-muffin24/funkin:windows-32bit
|
||||
butler status ninja-muffin24/funkin:windows-64bit
|
||||
echo ITCH SHIT UPDATED LMAOOOOO
|
||||
pause
|
||||
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
color 0a
|
||||
cd ..
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build windows -debug
|
||||
echo UPLOADING 64 BIT VERSION TO ITCH
|
||||
butler push ./export/debug/windows/bin ninja-muffin24/funkin:windows-secretBeta
|
||||
butler status ninja-muffin24/funkin:windows-secretBeta
|
||||
echo ITCH SHIT UPDATED LMAOOOOO
|
||||
pause
|
||||
@@ -0,0 +1,15 @@
|
||||
@echo off
|
||||
color 0a
|
||||
cd ..
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build windows -release
|
||||
echo UPLOADING 64 BIT VERSION TO ITCH
|
||||
butler push ./export/release/windows/bin ninja-muffin24/funkin:windows-64bit
|
||||
lime build windows -release -32
|
||||
echo UPLOADING 32 BIT VERSION TO ITCH
|
||||
butler push ./export/release/windows/bin ninja-muffin24/funkin:windows-32bit
|
||||
butler status ninja-muffin24/funkin:windows-32bit
|
||||
butler status ninja-muffin24/funkin:windows-64bit
|
||||
echo ITCH SHIT UPDATED LMAOOOOO
|
||||
pause
|
||||
@@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
color 0a
|
||||
cd ..
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build html5 -final -Dng
|
||||
echo ZIPPING
|
||||
cd export
|
||||
7z a -tzip -r swag release\html5\bin
|
||||
start .
|
||||
start https://www.newgrounds.com/projects/games/1528775
|
||||
pause
|
||||
@@ -0,0 +1,27 @@
|
||||
@echo off
|
||||
cd ..
|
||||
@echo on
|
||||
echo REBUILDING LIME FOR EXPORT (VERBOSE)
|
||||
@echo off
|
||||
color 0b
|
||||
@echo on
|
||||
lime rebuild windows -clean -v
|
||||
@echo off
|
||||
color 0a
|
||||
@echo on
|
||||
lime rebuild switch -clean -v
|
||||
@echo off
|
||||
color 0e
|
||||
@echo on
|
||||
lime rebuild tools -clean -v
|
||||
echo HOPE AND PRAY...
|
||||
@echo off
|
||||
color 0a
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build switch -final -clean -v
|
||||
@echo off
|
||||
color 0b
|
||||
@echo on
|
||||
echo NSP FILE CREATED
|
||||
pause
|
||||
@@ -0,0 +1,110 @@
|
||||
// Highlight keyframes in flash
|
||||
// drag .jsfl script in
|
||||
// will export the layer info of the first(?) element it finds?
|
||||
// maybe someone can extend this to add more depth / functionality!
|
||||
// see FramesJSFLParser.hx and CharSelectGF.hx for sample implementation
|
||||
|
||||
function splitArrayIntoChunks(array, chunkSize) {
|
||||
var result = [];
|
||||
for (var i = 0; i < array.length; i += chunkSize) {
|
||||
var chunk = array.slice(i, i + chunkSize);
|
||||
result.push(chunk);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
var isJson = false;
|
||||
|
||||
function addField(name, variable, end)
|
||||
{
|
||||
return (isJson) ? addFJson(name, variable, end) : addF(variable, end);
|
||||
}
|
||||
function addFJson(name, variable, end) {
|
||||
return "\"" + name + "\": " + variable + ((!end) ? "," : "");
|
||||
}
|
||||
function addF(variable, end) {
|
||||
return variable + ((end)? "" : " ");
|
||||
}
|
||||
|
||||
|
||||
fl.outputPanel.clear();
|
||||
|
||||
var timeline = fl.getDocumentDOM().getTimeline();
|
||||
var daSelection = timeline.getSelectedFrames();
|
||||
var splitArrays = splitArrayIntoChunks(daSelection, 3);
|
||||
var uri = undefined;
|
||||
|
||||
|
||||
if (fl.version.substring(4).split(",")[0] < 13)
|
||||
{
|
||||
var macFormat = "Folder|TEXT[*.||";
|
||||
var winFormat = "Folder|*.||";
|
||||
var previewArea = {};
|
||||
uri = fl.browseForFileURL("save", "where to save files pick folder...", previewArea, macFormat, winFormat);
|
||||
}
|
||||
else
|
||||
uri = fl.browseForFileURL("save", "where to save files pick folder..", "Folder(*.)", "*.");
|
||||
|
||||
|
||||
for (var i = 0; i < splitArrays.length; i+=1)
|
||||
{
|
||||
var curLayerInfo = splitArrays[i];
|
||||
var curLayerInd = curLayerInfo[0];
|
||||
var curLayer = fl.getDocumentDOM().getTimeline().layers[curLayerInd];
|
||||
|
||||
var layerURI = uri;
|
||||
var fileName = "";
|
||||
var uris = uri.split("/");
|
||||
|
||||
fileName = uris[uris.length - 1];
|
||||
|
||||
|
||||
FLfile.createFolder(layerURI);
|
||||
|
||||
|
||||
var fileData = "{\n";
|
||||
if (!isJson)
|
||||
fileData = "";
|
||||
|
||||
var selectedFrames = curLayer.frames;
|
||||
var sliceLength = curLayerInfo[2] - curLayerInfo[1];
|
||||
selectedFrames =selectedFrames.slice(curLayerInfo[1],curLayerInfo[2]);
|
||||
|
||||
if (isJson)
|
||||
fileData += "\"frames\": [";
|
||||
|
||||
for (var frameInd = curLayerInfo[1]; frameInd < curLayerInfo[2]; frameInd+=1)
|
||||
{
|
||||
if (isJson)
|
||||
fileData += "\n{"
|
||||
var curFrame = curLayer.frames[frameInd];
|
||||
if (curFrame.isEmpty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var elementInd = 0; elementInd < curFrame.elements.length; elementInd+=1)
|
||||
{
|
||||
|
||||
var curElement = curFrame.elements[elementInd];
|
||||
|
||||
fileData += addField("x", curElement.left, false);
|
||||
fileData += addField("y", curElement.top, false);
|
||||
fileData += addField("alpha", (curElement.colorAlphaPercent == undefined) ? 100 : curElement.colorAlphaPercent, false);
|
||||
fileData += addField("scaleX", curElement.scaleX, false);
|
||||
fileData += addField("scaleY", curElement.scaleY, true);
|
||||
if (frameInd != curLayerInfo[2] - 1)
|
||||
fileData += "\n";
|
||||
}
|
||||
|
||||
if (isJson)
|
||||
fileData += "},";
|
||||
}
|
||||
if (isJson)
|
||||
fileData += "]";
|
||||
|
||||
if (isJson)
|
||||
fileData += "\n}";
|
||||
|
||||
FLfile.write(layerURI + "/" + fileName + ".txt", fileData);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# README
|
||||
|
||||
jsfl is a scripting format with javascript syntax, I believe it's old js (no `let`, idk the spec!)
|
||||
|
||||
Official Adobe JSFL docuementation
|
||||
[https://github.com/AdobeDocs/developers-animatesdk-docs/tree/master](https://github.com/AdobeDocs/developers-animatesdk-docs/tree/master)
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
set -e # Exit immediately if a command exits with a non-zero status
|
||||
|
||||
echo "Note! This should be run from the root of the main Funkin repo, after creating a universal macOS build!"
|
||||
echo "This script will assist in codesigning the Funkin.app for macOS"
|
||||
echo ""
|
||||
|
||||
TEAM_ID="Z7G7AVNGSH"
|
||||
ISSUER_ID="5205caca-2a9b-4b5e-9390-1db124cf51f4"
|
||||
DEVELOPER_CERT_NAME="Developer ID Application: The Funkin' Crew Incorporated ($TEAM_ID)"
|
||||
APP_PATH="export/release/macosUniversal/Funkin.app"
|
||||
|
||||
# Check if app exists
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
echo "Error: $APP_PATH does not exist! Please build the app first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Preparing/Checking the AuthKey"
|
||||
team_key_path=~/.appstoreconnect/private_keys/
|
||||
# Check if directory exists, if not, create it
|
||||
if [ ! -d "$team_key_path" ]; then
|
||||
echo "The directory $team_key_path does not exist! Creating it now."
|
||||
mkdir -p "$team_key_path"
|
||||
echo ""
|
||||
echo "$team_key_path created, put the private key file in there (should be called 'AuthKey_XXXXXXX.p8')"
|
||||
echo "Ask Cameron for the private key file!"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# wait for the user to put the file in the directory, if it's not there
|
||||
while [ ! -f "${team_key_path}"AuthKey_*.p8 ]; do
|
||||
echo "Waiting for the private key file to be placed in $team_key_path... (press Ctrl+C to exit)"
|
||||
sleep 5
|
||||
done
|
||||
|
||||
key_id=$(ls "$team_key_path" | grep -oE '[A-Z0-9]{10}' | head -n 1)
|
||||
if [ -z "$key_id" ]; then
|
||||
echo "Error: Could not extract key ID from the AuthKey file name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found AuthKey with key id: $key_id"
|
||||
key_path="${team_key_path}AuthKey_${key_id}.p8"
|
||||
|
||||
cd export/release/macosUniversal || { echo "Failed to change directory"; exit 1; }
|
||||
|
||||
echo "Ensuring properly formatted Entitlements.plist"
|
||||
if [ ! -f "Funkin.app/Contents/Entitlements.plist" ]; then
|
||||
echo "Error: Entitlements.plist not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
plutil -convert xml1 Funkin.app/Contents/Entitlements.plist
|
||||
plutil -convert xml1 Funkin.app/Contents/Info.plist
|
||||
plutil -lint Funkin.app/Contents/Entitlements.plist
|
||||
plutil -lint Funkin.app/Contents/Info.plist
|
||||
echo "Entitlements.plist and Info.plist should be properly formatted..."
|
||||
echo "Codesigning - Funkin.app"
|
||||
# Check the keychain for the funkin crew certificate
|
||||
echo "Checking for The Funkin' Crew certificate in the keychain..."
|
||||
if ! security find-identity -p codesigning | grep -q "The Funkin' Crew Incorporated"; then
|
||||
echo "The Funkin' Crew Developer ID Application certificate was not found in the keychain!"
|
||||
echo "Download the certificate from:"
|
||||
echo "https://developer.apple.com/account/resources/certificates/download/Z62SXF29U7"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Codesigning - Funkin.app"
|
||||
codesign --force --deep --timestamp --sign "$DEVELOPER_CERT_NAME" --options runtime Funkin.app || {
|
||||
echo "Code signing failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Codesigning - Funkin.app done!"
|
||||
# Debug info to see if the app is properly signed
|
||||
codesign -vv --deep --strict Funkin.app || {
|
||||
echo "Code signing verification failed"
|
||||
exit 1
|
||||
}
|
||||
echo ""
|
||||
echo "Zipping Funkin.app for notarization (using ditto)..."
|
||||
# Check if Funkin.app.zip already exists, if so, delete it
|
||||
if [ -f Funkin.app.zip ]; then
|
||||
echo "Funkin.app.zip already exists, deleting it..."
|
||||
rm -rf Funkin.app.zip
|
||||
fi
|
||||
|
||||
ditto -c -k --keepParent Funkin.app Funkin.app.zip || {
|
||||
echo "Failed to create zip file"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Funkin.app zipped, now sending to Apple for notarization..."
|
||||
xcrun notarytool submit Funkin.app.zip --issuer "$ISSUER_ID" --key-id "$key_id" --key "$key_path" --wait || {
|
||||
echo "Notarization submission failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Clean up zip file
|
||||
rm -rf Funkin.app.zip
|
||||
|
||||
echo "Stapling Funkin.app"
|
||||
xcrun stapler staple Funkin.app || {
|
||||
echo "Stapling failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Code sign information!"
|
||||
codesign -dv --verbose=4 Funkin.app
|
||||
|
||||
echo "Notarization information (stapler):"
|
||||
xcrun stapler validate Funkin.app || {
|
||||
echo "Stapler validation failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cd ../../.. || { echo "Failed to return to original directory"; exit 1; }
|
||||
echo "Notarization complete! Funkin.app is now codesigned and notarized!"
|
||||
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
echo "Note! This should be run from the root of the main Funkin repo, after creating a universal macOS build!"
|
||||
echo "This script will assist in creating a universal build of Funkin.app for macOS"
|
||||
echo ""
|
||||
lime build mac -64 -release
|
||||
cd export/release
|
||||
rm -rf macosUniversal
|
||||
mkdir macosUniversal
|
||||
cp -r macos/bin/Funkin.app macosUniversal
|
||||
cd ../..
|
||||
lime build mac -arm64 -release
|
||||
cd export/release
|
||||
cp -r macos/bin/Funkin.app/Contents/MacOS/Funkin macosUniversal/Funkin.app/Contents/MacOS/FunkinArm
|
||||
cp -r macos/bin/Funkin.app/Contents/MacOS/lime.ndll macosUniversal/Funkin.app/Contents/MacOS/limeArm.ndll
|
||||
cd macosUniversal/Funkin.app/Contents/MacOS
|
||||
mv Funkin Funkin64
|
||||
mv lime.ndll lime64.ndll
|
||||
lipo -create -output lime.ndll lime64.ndll limeArm.ndll
|
||||
lipo -create -output Funkin Funkin64 FunkinArm
|
||||
rm FunkinArm Funkin64 lime64.ndll limeArm.ndll
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Find all files in PNG format and execute oxipng to optimize file sizes
|
||||
cd ../assets/ && oxipng -o 6 --strip safe --alpha {} **/*.png
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Resize each health icon to 512x512
|
||||
find "../art/discord/" -name "icon-*.png" -exec sh -c 'ffmpeg -i {} -vf scale=512:512 "${0%.*}2.png"' {} \;
|
||||
+6
-1
@@ -136,7 +136,8 @@ class Main extends Sprite
|
||||
|
||||
#if hxvlc
|
||||
// Initialize hxvlc's Handle here so the videos are loading faster.
|
||||
Handle.initAsync(function(success:Bool):Void {
|
||||
Handle.initAsync(function(success:Bool):Void
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
trace(' HXVLC '.bold().bg_orange() + ' LibVLC instance initialized!');
|
||||
@@ -157,6 +158,10 @@ class Main extends Sprite
|
||||
|
||||
WindowUtil.setVSyncMode(funkin.Preferences.vsyncMode);
|
||||
|
||||
// Force a `FunkinCamera` to be the default camera.
|
||||
// This allows the blend mode shader to work everywhere.
|
||||
untyped FlxG.cameras = new funkin.graphics.FunkinCameraFrontEnd();
|
||||
|
||||
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, Preferences.framerate, Preferences.framerate, skipSplash,
|
||||
(FlxG.stage.window.fullscreen || Preferences.autoFullscreen));
|
||||
|
||||
|
||||
+1
-6
@@ -36,12 +36,7 @@ class Postbuild
|
||||
|
||||
static function format(time:Float, decimals:Int = 1):String
|
||||
{
|
||||
var units = [
|
||||
{name: "day", secs: 86400},
|
||||
{name: "hour", secs: 3600},
|
||||
{name: "minute", secs: 60},
|
||||
{name: "second", secs: 1}
|
||||
];
|
||||
var units = [{name: "day", secs: 86400}, {name: "hour", secs: 3600}, {name: "minute", secs: 60}, {name: "second", secs: 1}];
|
||||
|
||||
var parts:Array<String> = [];
|
||||
var remaining:Float = time;
|
||||
|
||||
@@ -89,44 +89,35 @@ extern class Native_TracyProfiler
|
||||
class Cppia_TracyProfiler
|
||||
{
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.frameMark)
|
||||
public static function frameMark()
|
||||
Native_TracyProfiler.frameMark();
|
||||
public static function frameMark() Native_TracyProfiler.frameMark();
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.frameMarkNamed)
|
||||
public static function frameMarkNamed(_name:String)
|
||||
Native_TracyProfiler.frameMarkNamed(_name);
|
||||
public static function frameMarkNamed(_name:String) Native_TracyProfiler.frameMarkNamed(_name);
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.frameMarkStart)
|
||||
public static function frameMarkStart(_name:String)
|
||||
Native_TracyProfiler.frameMarkStart(_name);
|
||||
public static function frameMarkStart(_name:String) Native_TracyProfiler.frameMarkStart(_name);
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.frameMarkEnd)
|
||||
public static function frameMarkEnd(_name:String)
|
||||
Native_TracyProfiler.frameMarkEnd(_name);
|
||||
public static function frameMarkEnd(_name:String) Native_TracyProfiler.frameMarkEnd(_name);
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.message)
|
||||
public static function message(_msg:String, ?_color:Int = 0x000000)
|
||||
Native_TracyProfiler.message(_msg, _color);
|
||||
public static function message(_msg:String, ?_color:Int = 0x000000) Native_TracyProfiler.message(_msg, _color);
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.messageAppInfo)
|
||||
public static function messageAppInfo(_info:String)
|
||||
Native_TracyProfiler.messageAppInfo(_info);
|
||||
public static function messageAppInfo(_info:String) Native_TracyProfiler.messageAppInfo(_info);
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.plot)
|
||||
public static function plot(_name:String, _val:Float)
|
||||
Native_TracyProfiler.plot(_name, _val);
|
||||
public static function plot(_name:String, _val:Float) Native_TracyProfiler.plot(_name, _val);
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.plotConfig)
|
||||
public static function plotConfig(_name:String, _format:PlotFormatType, ?_step:Bool = false, ?_fill:Bool = false, ?_color:Int = 0x000000)
|
||||
Native_TracyProfiler.plotConfig(_name, _format, _step, _fill, _color);
|
||||
public static function plotConfig(_name:String, _format:PlotFormatType, ?_step:Bool = false, ?_fill:Bool = false,
|
||||
?_color:Int = 0x000000) Native_TracyProfiler.plotConfig(_name, _format, _step, _fill, _color);
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.setThreadName)
|
||||
public static function setThreadName(_name:String, ?_groupHint:Int = 1)
|
||||
Native_TracyProfiler.setThreadName(_name, _groupHint);
|
||||
public static function setThreadName(_name:String, ?_groupHint:Int = 1) Native_TracyProfiler.setThreadName(_name, _groupHint);
|
||||
|
||||
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.zoneScoped)
|
||||
public static function zoneScoped(_name:String)
|
||||
Native_TracyProfiler.zoneScoped(_name);
|
||||
public static function zoneScoped(_name:String) Native_TracyProfiler.zoneScoped(_name);
|
||||
}
|
||||
|
||||
typedef TracyProfiler = Cppia_TracyProfiler;
|
||||
|
||||
@@ -381,7 +381,9 @@ class Conductor
|
||||
/**
|
||||
* The constructor.
|
||||
*/
|
||||
public function new() {}
|
||||
public function new()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly defines the current BPM of the song.
|
||||
|
||||
@@ -12,7 +12,7 @@ import lime.app.Promise;
|
||||
|
||||
/**
|
||||
* Handles caching of textures and sounds for the game.
|
||||
* TODO: Remove this once Eric finishes the memory system.
|
||||
* I did this hello, this can be improved later on and I have ideas on how, but for now this functions well enough. -Zack
|
||||
*/
|
||||
@:nullSafety
|
||||
class FunkinMemory
|
||||
@@ -21,7 +21,6 @@ class FunkinMemory
|
||||
static var currentCachedTextures:Map<String, FlxGraphic> = [];
|
||||
static var previousCachedTextures:Map<String, FlxGraphic> = [];
|
||||
|
||||
// waow
|
||||
static var permanentCachedSounds:Map<String, Sound> = [];
|
||||
static var currentCachedSounds:Map<String, Sound> = [];
|
||||
static var previousCachedSounds:Map<String, Sound> = [];
|
||||
@@ -97,6 +96,7 @@ class FunkinMemory
|
||||
|
||||
/**
|
||||
* Clears the current texture and sound caches.
|
||||
* @param callGarbageCollector Whether to call the system's garbage collector after purging.
|
||||
*/
|
||||
public static inline function purgeCache(callGarbageCollector:Bool = false):Void
|
||||
{
|
||||
@@ -111,16 +111,6 @@ class FunkinMemory
|
||||
|
||||
///// TEXTURES /////
|
||||
|
||||
/**
|
||||
* Determine whether the texture with the given key is cached.
|
||||
* @param key The key of the texture to check.
|
||||
* @return Whether the texture is cached.
|
||||
*/
|
||||
public static function isTextureCached(key:String):Bool
|
||||
{
|
||||
return FlxG.bitmap.get(key) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a texture with the given key is cached.
|
||||
* @param key The key of the texture to cache.
|
||||
@@ -173,30 +163,6 @@ class FunkinMemory
|
||||
currentCachedTextures = permanentCachedTextures.copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
graphic.bitmap?.getTexture(FlxG.stage.context3D); // Just in case that didn't work...
|
||||
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);
|
||||
@@ -281,8 +247,42 @@ class FunkinMemory
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the GPU to load and upload a FlxGraphic.
|
||||
* @param graphic The graphic to force render.
|
||||
*/
|
||||
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.
|
||||
graphic.bitmap?.getTexture(FlxG.stage.context3D); // Just in case that didn't work...
|
||||
sprite.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the texture with the given key is cached.
|
||||
* @param key The key of the texture to check.
|
||||
* @return Whether the texture is cached.
|
||||
*/
|
||||
public static function isTextureCached(key:String):Bool
|
||||
{
|
||||
return FlxG.bitmap.get(key) != null
|
||||
&& (permanentCachedTextures.exists(key) || currentCachedTextures.exists(key) || previousCachedTextures.exists(key));
|
||||
}
|
||||
|
||||
///// NOTE STYLE //////
|
||||
|
||||
/**
|
||||
* Caches all assets for the given note style.
|
||||
* @param style The note style to cache.
|
||||
*/
|
||||
public static function cacheNoteStyle(style:NoteStyle):Void
|
||||
{
|
||||
// TODO: Texture paths should fall back to the default values.
|
||||
@@ -325,6 +325,10 @@ class FunkinMemory
|
||||
|
||||
///// SOUND //////
|
||||
|
||||
/**
|
||||
* Caches a sound with the given key.
|
||||
* @param key The key of the sound to cache.
|
||||
*/
|
||||
public static function cacheSound(key:String):Void
|
||||
{
|
||||
if (currentCachedSounds.exists(key)) return;
|
||||
@@ -344,6 +348,10 @@ class FunkinMemory
|
||||
currentCachedSounds.set(key, sound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently caches a sound with the given key.
|
||||
* @param key The key of the sound to cache.
|
||||
*/
|
||||
public static function permanentCacheSound(key:String):Void
|
||||
{
|
||||
if (permanentCachedSounds.exists(key)) return;
|
||||
@@ -356,6 +364,9 @@ class FunkinMemory
|
||||
if (sound != null) currentCachedSounds.set(key, sound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the cache for purging unused sounds.
|
||||
*/
|
||||
public static function preparePurgeSoundCache():Void
|
||||
{
|
||||
previousCachedSounds = currentCachedSounds.copy();
|
||||
@@ -405,6 +416,9 @@ class FunkinMemory
|
||||
|
||||
///// MISC /////
|
||||
|
||||
/**
|
||||
* Clears all Freeplay assets from memory.
|
||||
*/
|
||||
public static inline function clearFreeplay():Void
|
||||
{
|
||||
var keysToRemove:Array<String> = [];
|
||||
@@ -436,6 +450,9 @@ class FunkinMemory
|
||||
purgeSoundCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all sticker assets from memory.
|
||||
*/
|
||||
public static inline function clearStickers():Void
|
||||
{
|
||||
var keysToRemove:Array<String> = [];
|
||||
@@ -464,7 +481,11 @@ class FunkinMemory
|
||||
}
|
||||
}
|
||||
|
||||
static function log(message:String):Void
|
||||
/**
|
||||
* Sends a trace with fancy ANSI colors.
|
||||
* @param message The message to log.
|
||||
*/
|
||||
private static function log(message:String):Void
|
||||
{
|
||||
trace(' MEMORY '.bg_bright_lilac().bold() + ' ${message}');
|
||||
}
|
||||
|
||||
+13
-14
@@ -49,20 +49,19 @@ abstract Tallies(RawTallies)
|
||||
{
|
||||
public function new()
|
||||
{
|
||||
this =
|
||||
{
|
||||
combo: 0,
|
||||
missed: 0,
|
||||
shit: 0,
|
||||
bad: 0,
|
||||
good: 0,
|
||||
sick: 0,
|
||||
totalNotes: 0,
|
||||
totalNotesHit: 0,
|
||||
maxCombo: 0,
|
||||
score: 0,
|
||||
isNewHighscore: false
|
||||
}
|
||||
this = {
|
||||
combo: 0,
|
||||
missed: 0,
|
||||
shit: 0,
|
||||
bad: 0,
|
||||
good: 0,
|
||||
sick: 0,
|
||||
totalNotes: 0,
|
||||
totalNotesHit: 0,
|
||||
maxCombo: 0,
|
||||
score: 0,
|
||||
isNewHighscore: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+61
-57
@@ -20,6 +20,7 @@ import funkin.data.freeplay.style.FreeplayStyleRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.data.stickers.StickerRegistry;
|
||||
import funkin.play.event.SongEventHelper;
|
||||
import funkin.data.event.SongEventRegistry;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.story.level.LevelRegistry;
|
||||
@@ -139,6 +140,8 @@ class InitState extends FlxState
|
||||
funkin.mobile.util.FNFCUtil.init();
|
||||
#end
|
||||
|
||||
SongEventHelper.generateEaseGraphsBitmaps();
|
||||
|
||||
// This ain't a pixel art game! (most of the time)
|
||||
FlxSprite.defaultAntialiasing = true;
|
||||
|
||||
@@ -185,7 +188,8 @@ class InitState extends FlxState
|
||||
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));
|
||||
|
||||
FlxG.signals.gameResized.add(function(width:Int, height:Int) {
|
||||
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,
|
||||
@@ -196,10 +200,12 @@ class InitState extends FlxState
|
||||
// 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() {
|
||||
FlxG.signals.focusLost.add(function()
|
||||
{
|
||||
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
||||
});
|
||||
FlxG.signals.focusGained.add(function() {
|
||||
FlxG.signals.focusGained.add(function()
|
||||
{
|
||||
WindowUtil.setVSyncMode(lime.ui.WindowVSyncMode.OFF);
|
||||
});
|
||||
#end
|
||||
@@ -217,7 +223,8 @@ class InitState extends FlxState
|
||||
#if FEATURE_DISCORD_RPC
|
||||
DiscordClient.instance.init();
|
||||
|
||||
lime.app.Application.current.onExit.add(function(exitCode) {
|
||||
lime.app.Application.current.onExit.add(function(exitCode)
|
||||
{
|
||||
DiscordClient.instance.shutdown();
|
||||
});
|
||||
#end
|
||||
@@ -359,37 +366,34 @@ class InitState extends FlxState
|
||||
FlxG.switchState(() -> new funkin.ui.debug.stage.StageBuilderState());
|
||||
#elseif RESULTS
|
||||
// -DRESULTS
|
||||
FlxG.switchState(() -> new funkin.play.ResultState(
|
||||
{
|
||||
storyMode: true,
|
||||
title: "Cum Song Erect by Kawai Sprite",
|
||||
songId: "cum",
|
||||
characterId: "pico",
|
||||
difficultyId: "hard",
|
||||
isNewHighscore: true,
|
||||
scoreData:
|
||||
{
|
||||
score: 1_234_567,
|
||||
tallies:
|
||||
{
|
||||
sick: 130,
|
||||
good: 60,
|
||||
bad: 69,
|
||||
shit: 69,
|
||||
missed: 69,
|
||||
combo: 69,
|
||||
maxCombo: 69,
|
||||
totalNotesHit: 140,
|
||||
totalNotes: 240
|
||||
}
|
||||
// 2400 total notes = 7% = LOSS
|
||||
// 275 total notes = 69% = NICE
|
||||
// 240 total notes = 79% = GOOD
|
||||
// 230 total notes = 82% = GREAT
|
||||
// 210 total notes = 91% = EXCELLENT
|
||||
// 190 total notes = PERFECT
|
||||
},
|
||||
}));
|
||||
FlxG.switchState(() -> new funkin.play.ResultState({
|
||||
storyMode: true,
|
||||
title: "Cum Song Erect by Kawai Sprite",
|
||||
songId: "cum",
|
||||
characterId: "pico",
|
||||
difficultyId: "hard",
|
||||
isNewHighscore: true,
|
||||
scoreData: {
|
||||
score: 1_234_567,
|
||||
tallies: {
|
||||
sick: 130,
|
||||
good: 60,
|
||||
bad: 69,
|
||||
shit: 69,
|
||||
missed: 69,
|
||||
combo: 69,
|
||||
maxCombo: 69,
|
||||
totalNotesHit: 140,
|
||||
totalNotes: 240
|
||||
}
|
||||
// 2400 total notes = 7% = LOSS
|
||||
// 275 total notes = 69% = NICE
|
||||
// 240 total notes = 79% = GOOD
|
||||
// 230 total notes = 82% = GREAT
|
||||
// 210 total notes = 91% = EXCELLENT
|
||||
// 190 total notes = PERFECT
|
||||
},
|
||||
}));
|
||||
#elseif ANIMDEBUG
|
||||
// -DANIMDEBUG
|
||||
FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState());
|
||||
@@ -412,10 +416,9 @@ class InitState extends FlxState
|
||||
if (params.chart.shouldLoadChart)
|
||||
{
|
||||
#if FEATURE_CHART_EDITOR
|
||||
FlxG.switchState(() -> new ChartEditorState(
|
||||
{
|
||||
fnfcTargetPath: params.chart.chartPath,
|
||||
}));
|
||||
FlxG.switchState(() -> new ChartEditorState({
|
||||
fnfcTargetPath: params.chart.chartPath,
|
||||
}));
|
||||
#else
|
||||
FlxG.switchState(() -> new TitleState());
|
||||
#end
|
||||
@@ -423,10 +426,9 @@ class InitState extends FlxState
|
||||
else if (params.stage.shouldLoadStage)
|
||||
{
|
||||
#if FEATURE_STAGE_EDITOR
|
||||
FlxG.switchState(() -> new StageEditorState(
|
||||
{
|
||||
fnfsTargetPath: params.stage.stagePath,
|
||||
}));
|
||||
FlxG.switchState(() -> new StageEditorState({
|
||||
fnfsTargetPath: params.stage.stagePath,
|
||||
}));
|
||||
#else
|
||||
FlxG.switchState(() -> new TitleState());
|
||||
#end
|
||||
@@ -443,7 +445,8 @@ class InitState extends FlxState
|
||||
{
|
||||
// FlxG.sound.cache(Paths.music('freakyMenu/freakyMenu'));
|
||||
#if mobile
|
||||
funkin.mobile.util.FNFCUtil.onFNFCOpen.add(function(fnfcFile:String) {
|
||||
funkin.mobile.util.FNFCUtil.onFNFCOpen.add(function(fnfcFile:String)
|
||||
{
|
||||
flixel.tweens.FlxTween.globalManager.clear();
|
||||
flixel.util.FlxTimer.globalManager.clear();
|
||||
@:nullSafety(Off)
|
||||
@@ -517,11 +520,10 @@ class InitState extends FlxState
|
||||
}
|
||||
|
||||
@:nullSafety(Off) // Cannot unify?
|
||||
LoadingState.loadPlayState(
|
||||
{
|
||||
targetSong: songData,
|
||||
targetDifficulty: difficultyId,
|
||||
});
|
||||
LoadingState.loadPlayState({
|
||||
targetSong: songData,
|
||||
targetDifficulty: difficultyId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -560,11 +562,10 @@ class InitState extends FlxState
|
||||
}
|
||||
|
||||
@:nullSafety(Off)
|
||||
LoadingState.loadPlayState(
|
||||
{
|
||||
targetSong: targetSong,
|
||||
targetDifficulty: difficultyId,
|
||||
});
|
||||
LoadingState.loadPlayState({
|
||||
targetSong: targetSong,
|
||||
targetDifficulty: difficultyId,
|
||||
});
|
||||
}
|
||||
|
||||
@:nullSafety(Off) // Meh, remove when flixel.system.debug.log.LogStyle is null safe
|
||||
@@ -592,7 +593,8 @@ class InitState extends FlxState
|
||||
|
||||
// Adds a red button to the debugger.
|
||||
// This pauses the game AND the music! This ensures the Conductor stops.
|
||||
FlxG.debugger.addButton(CENTER, new BitmapData(20, 20, true, 0xFFCC2233), function() {
|
||||
FlxG.debugger.addButton(CENTER, new BitmapData(20, 20, true, 0xFFCC2233), function()
|
||||
{
|
||||
if (FlxG.vcr.paused)
|
||||
{
|
||||
FlxG.vcr.resume();
|
||||
@@ -619,7 +621,8 @@ class InitState extends FlxState
|
||||
|
||||
// Adds a blue button to the debugger.
|
||||
// This skips forward in the song.
|
||||
FlxG.debugger.addButton(CENTER, new BitmapData(20, 20, true, 0xFF2222CC), function() {
|
||||
FlxG.debugger.addButton(CENTER, new BitmapData(20, 20, true, 0xFF2222CC), function()
|
||||
{
|
||||
FlxG.game.debugger.vcr.onStep();
|
||||
|
||||
for (snd in FlxG.sound.list)
|
||||
@@ -636,7 +639,8 @@ class InitState extends FlxState
|
||||
// This big obnoxious white button is for MOBILE, so that you can press it
|
||||
// easily with your finger when debug bullshit pops up during testing lol!
|
||||
#if mobile
|
||||
FlxG.debugger.addButton(LEFT, new BitmapData(200, 200), function() {
|
||||
FlxG.debugger.addButton(LEFT, new BitmapData(200, 200), function()
|
||||
{
|
||||
FlxG.debugger.visible = false;
|
||||
|
||||
// Make errors and warnings less annoying.
|
||||
|
||||
+12
-13
@@ -193,19 +193,18 @@ class Paths implements ConsoleClass
|
||||
graphicKey = Paths.animateAtlas(key);
|
||||
}
|
||||
|
||||
var validatedSettings:AtlasSpriteSettings =
|
||||
{
|
||||
swfMode: settings?.swfMode ?? false,
|
||||
cacheOnLoad: settings?.cacheOnLoad ?? false,
|
||||
filterQuality: settings?.filterQuality ?? MEDIUM,
|
||||
spritemaps: settings?.spritemaps ?? null,
|
||||
metadataJson: settings?.metadataJson ?? null,
|
||||
cacheKey: settings?.cacheKey ?? null,
|
||||
uniqueInCache: settings?.uniqueInCache ?? false,
|
||||
onSymbolCreate: settings?.onSymbolCreate ?? null,
|
||||
applyStageMatrix: settings?.applyStageMatrix ?? false,
|
||||
useRenderTexture: settings?.useRenderTexture ?? false
|
||||
};
|
||||
var validatedSettings:AtlasSpriteSettings = {
|
||||
swfMode: settings?.swfMode ?? false,
|
||||
cacheOnLoad: settings?.cacheOnLoad ?? false,
|
||||
filterQuality: settings?.filterQuality ?? MEDIUM,
|
||||
spritemaps: settings?.spritemaps ?? null,
|
||||
metadataJson: settings?.metadataJson ?? null,
|
||||
cacheKey: settings?.cacheKey ?? null,
|
||||
uniqueInCache: settings?.uniqueInCache ?? false,
|
||||
onSymbolCreate: settings?.onSymbolCreate ?? null,
|
||||
applyStageMatrix: settings?.applyStageMatrix ?? false,
|
||||
useRenderTexture: settings?.useRenderTexture ?? false
|
||||
};
|
||||
|
||||
// Validate asset path.
|
||||
if (!Assets.exists('${graphicKey}/Animation.json'))
|
||||
|
||||
@@ -384,7 +384,8 @@ class Preferences
|
||||
{
|
||||
var currTime = Date.now().getTime();
|
||||
var timeToCall = 0;
|
||||
var id = js.Browser.window.setTimeout(function() {
|
||||
var id = js.Browser.window.setTimeout(function()
|
||||
{
|
||||
callback(currTime + timeToCall);
|
||||
}, timeToCall);
|
||||
return id;
|
||||
|
||||
@@ -51,7 +51,8 @@ class Leaderboards
|
||||
var leaderboardData:Null<LeaderboardData> = listLeaderboardData().get(leaderboard.getId());
|
||||
if (leaderboardData != null)
|
||||
{
|
||||
leaderboardData.postScore(score, function(outcome:Outcome<CallError>):Void {
|
||||
leaderboardData.postScore(score, function(outcome:Outcome<CallError>):Void
|
||||
{
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS:
|
||||
@@ -82,7 +83,8 @@ class Leaderboards
|
||||
if ((params?.useCurrentUser ?? false) && NewgroundsClient.instance.isLoggedIn()) user = NewgroundsClient.instance.user;
|
||||
|
||||
leaderboardData.requestScores(params?.limit ?? 10, params?.skip ?? 0, params?.period ?? ALL, params?.social ?? false, params?.tag, user,
|
||||
function(outcome:Outcome<CallError>):Void {
|
||||
function(outcome:Outcome<CallError>):Void
|
||||
{
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS:
|
||||
|
||||
@@ -58,7 +58,8 @@ class Medals
|
||||
// Play the medal unlock animation, but only if the user has not already unlocked it.
|
||||
#if html5
|
||||
// Web builds support parsing the bitmap data from the URL directly.
|
||||
BitmapData.loadFromFile("https:" + medalData.icon).onComplete(function(bmp:BitmapData) {
|
||||
BitmapData.loadFromFile("https:" + medalData.icon).onComplete(function(bmp:BitmapData)
|
||||
{
|
||||
var medalGraphic = FlxGraphic.fromBitmapData(bmp);
|
||||
medalGraphic.persist = true;
|
||||
NewgroundsMedalPlugin.play(medalData.value, medalData.name, medalGraphic);
|
||||
@@ -68,7 +69,8 @@ class Medals
|
||||
// We have to use a medal image from the game files. We use a Base64 encoded image that NG spits out.
|
||||
// TODO: Wait, don't they give us the medal icon?
|
||||
|
||||
var localMedalData:Null<MedalJSON> = medalJSON.filter(function(jsonMedal) {
|
||||
var localMedalData:Null<MedalJSON> = medalJSON.filter(function(jsonMedal)
|
||||
{
|
||||
#if FEATURE_NEWGROUNDS_TESTING_MEDALS
|
||||
return medal == jsonMedal.idTest;
|
||||
#else
|
||||
@@ -436,27 +438,7 @@ enum abstract Medal(Int) from Int to Int
|
||||
*/
|
||||
public static function getAllMedals():Array<Medal>
|
||||
{
|
||||
return [
|
||||
StartGame,
|
||||
StoryTutorial,
|
||||
StoryWeek1,
|
||||
StoryWeek2,
|
||||
StoryWeek3,
|
||||
StoryWeek4,
|
||||
StoryWeek5,
|
||||
StoryWeek6,
|
||||
StoryWeek7,
|
||||
StoryWeekend1,
|
||||
CharSelect,
|
||||
FreeplayPicoMix,
|
||||
FreeplayStressPico,
|
||||
LossRating,
|
||||
PerfectRatingHard,
|
||||
GoldPerfectRatingHard,
|
||||
ErectDifficulty,
|
||||
GoldPerfectRatingNightmare,
|
||||
FridayNight,
|
||||
Nice
|
||||
];
|
||||
return
|
||||
[StartGame, StoryTutorial, StoryWeek1, StoryWeek2, StoryWeek3, StoryWeek4, StoryWeek5, StoryWeek6, StoryWeek7, StoryWeekend1, CharSelect, FreeplayPicoMix, FreeplayStressPico, LossRating, PerfectRatingHard, GoldPerfectRatingHard, ErectDifficulty, GoldPerfectRatingNightmare, FridayNight, Nice];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,8 @@ class NGSaveSlot
|
||||
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.save(encodedData, function(outcome:Outcome<CallError>) {
|
||||
ngSaveSlot?.save(encodedData, function(outcome:Outcome<CallError>)
|
||||
{
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS:
|
||||
@@ -80,7 +81,8 @@ class NGSaveSlot
|
||||
{
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.load(function(outcome:SaveSlotOutcome):Void {
|
||||
ngSaveSlot?.load(function(outcome:SaveSlotOutcome):Void
|
||||
{
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS(value):
|
||||
@@ -122,7 +124,8 @@ class NGSaveSlot
|
||||
{
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.clear(function(outcome:Outcome<CallError>) {
|
||||
ngSaveSlot?.clear(function(outcome:Outcome<CallError>)
|
||||
{
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS:
|
||||
|
||||
@@ -113,14 +113,16 @@ class NewgroundsClient
|
||||
return;
|
||||
}
|
||||
|
||||
var passportHandler:String->Void = function(passportUrl:String) {
|
||||
var passportHandler:String->Void = function(passportUrl:String)
|
||||
{
|
||||
// This exists so we can create a popup on mobile but with a WebView instead.
|
||||
#if FEATURE_MOBILE_WEBVIEW
|
||||
if (passportUrl != null)
|
||||
{
|
||||
NG.core.logVerbose('Loading passport from WebView: ${passportUrl}');
|
||||
|
||||
WebViewUtil.openURL(passportUrl, function():Void {
|
||||
WebViewUtil.openURL(passportUrl, function():Void
|
||||
{
|
||||
NG.core.cancelLoginRequest();
|
||||
});
|
||||
|
||||
@@ -151,7 +153,8 @@ class NewgroundsClient
|
||||
return;
|
||||
}
|
||||
|
||||
var dummyPassport:String->Void = function(_) {
|
||||
var dummyPassport:String->Void = function(_)
|
||||
{
|
||||
// just a dummy passport, so we don't create a popup
|
||||
// otherwise `NG.core.requestLogin()` will automatically attempt to open a tab at the beginning of the game
|
||||
// users should go to the Options Menu to login to NG
|
||||
|
||||
@@ -23,7 +23,8 @@ class Referral
|
||||
{
|
||||
NG.core?.calls.loader.loadReferral(false)
|
||||
.addComponentParameter("referral_name", referralName)
|
||||
.addResponseHandler(response -> {
|
||||
.addResponseHandler(response ->
|
||||
{
|
||||
trace(response);
|
||||
if (response.success)
|
||||
{
|
||||
|
||||
@@ -42,7 +42,8 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
if (_onVolumeChanged == null)
|
||||
{
|
||||
_onVolumeChanged = new FlxTypedSignal<Float->Void>();
|
||||
FlxG.sound.onVolumeChange.add(function(volume:Float) {
|
||||
FlxG.sound.onVolumeChange.add(function(volume:Float)
|
||||
{
|
||||
_onVolumeChanged.dispatch(volume);
|
||||
});
|
||||
}
|
||||
@@ -350,7 +351,8 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
partialQueue.push(music);
|
||||
|
||||
@:nullSafety(Off)
|
||||
music.future.onComplete(function(partialMusic:Null<FunkinSound>) {
|
||||
music.future.onComplete(function(partialMusic:Null<FunkinSound>)
|
||||
{
|
||||
FlxG.sound.music = partialMusic;
|
||||
FlxG.sound.list.remove(FlxG.sound.music);
|
||||
|
||||
@@ -496,11 +498,13 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.future.onError(function(e) {
|
||||
promise.future.onError(function(e)
|
||||
{
|
||||
soundRequest.error("Sound loading was errored or cancelled");
|
||||
});
|
||||
|
||||
soundRequest.future.onComplete(function(partialSound) {
|
||||
soundRequest.future.onComplete(function(partialSound)
|
||||
{
|
||||
var snd = FunkinSound.load(partialSound, volume, looped, autoDestroy, autoPlay, false, onComplete, onLoad);
|
||||
promise.complete(snd);
|
||||
});
|
||||
|
||||
@@ -58,7 +58,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
{
|
||||
var error:Float = 0;
|
||||
|
||||
forEachAlive(function(snd) {
|
||||
forEachAlive(function(snd)
|
||||
{
|
||||
if (targetTime == null) targetTime = snd.time;
|
||||
else
|
||||
{
|
||||
@@ -85,7 +86,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
// result.pause();
|
||||
result.time = this.time;
|
||||
|
||||
result.onComplete = function() {
|
||||
result.onComplete = function()
|
||||
{
|
||||
this.onComplete();
|
||||
}
|
||||
|
||||
@@ -96,14 +98,17 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
return result;
|
||||
}
|
||||
|
||||
public dynamic function onComplete():Void {}
|
||||
public dynamic function onComplete():Void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause all the sounds in the group.
|
||||
*/
|
||||
public function pause()
|
||||
{
|
||||
forEachAlive(function(sound:FunkinSound) {
|
||||
forEachAlive(function(sound:FunkinSound)
|
||||
{
|
||||
sound.pause();
|
||||
});
|
||||
}
|
||||
@@ -113,7 +118,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
*/
|
||||
public function play(forceRestart:Bool = false, startTime:Float = 0.0, ?endTime:Float)
|
||||
{
|
||||
forEachAlive(function(sound:FunkinSound) {
|
||||
forEachAlive(function(sound:FunkinSound)
|
||||
{
|
||||
if (sound.length < startTime)
|
||||
{
|
||||
// trace('Queuing sound (${sound.toString()} past its length! Skipping...)');
|
||||
@@ -128,7 +134,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
*/
|
||||
public function resume()
|
||||
{
|
||||
forEachAlive(function(sound:FunkinSound) {
|
||||
forEachAlive(function(sound:FunkinSound)
|
||||
{
|
||||
sound.resume();
|
||||
});
|
||||
}
|
||||
@@ -139,7 +146,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
@:nullSafety(Off)
|
||||
public function fadeIn(duration:Float, ?from:Float = 0.0, ?to:Float = 1.0, ?onComplete:FlxTween->Void):Void
|
||||
{
|
||||
forEachAlive(function(sound:FunkinSound) {
|
||||
forEachAlive(function(sound:FunkinSound)
|
||||
{
|
||||
sound.fadeIn(duration, from, to, onComplete);
|
||||
});
|
||||
}
|
||||
@@ -150,7 +158,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
@:nullSafety(Off)
|
||||
public function fadeOut(duration:Float, ?to:Float = 0.0, ?onComplete:FlxTween->Void):Void
|
||||
{
|
||||
forEachAlive(function(sound:FunkinSound) {
|
||||
forEachAlive(function(sound:FunkinSound)
|
||||
{
|
||||
sound.fadeOut(duration, to, onComplete);
|
||||
});
|
||||
}
|
||||
@@ -162,7 +171,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
{
|
||||
if (members != null)
|
||||
{
|
||||
forEachAlive(function(sound:FunkinSound) {
|
||||
forEachAlive(function(sound:FunkinSound)
|
||||
{
|
||||
sound.stop();
|
||||
});
|
||||
}
|
||||
@@ -198,7 +208,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
|
||||
function set_time(time:Float):Float
|
||||
{
|
||||
forEachAlive(function(snd:FunkinSound) {
|
||||
forEachAlive(function(snd:FunkinSound)
|
||||
{
|
||||
// account for different offsets per sound?
|
||||
snd.time = time;
|
||||
});
|
||||
@@ -233,7 +244,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
// in PlayState, adjust the code so that it only mutes the player1 vocal tracks?
|
||||
function set_volume(volume:Float):Float
|
||||
{
|
||||
forEachAlive(function(snd:FunkinSound) {
|
||||
forEachAlive(function(snd:FunkinSound)
|
||||
{
|
||||
snd.volume = volume;
|
||||
});
|
||||
|
||||
@@ -249,7 +261,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
|
||||
function set_muted(muted:Bool):Bool
|
||||
{
|
||||
forEachAlive(function(snd:FunkinSound) {
|
||||
forEachAlive(function(snd:FunkinSound)
|
||||
{
|
||||
snd.muted = muted;
|
||||
});
|
||||
|
||||
@@ -269,7 +282,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
||||
{
|
||||
#if FLX_PITCH
|
||||
trace('Setting audio pitch to ' + val);
|
||||
forEachAlive(function(snd:FunkinSound) {
|
||||
forEachAlive(function(snd:FunkinSound)
|
||||
{
|
||||
snd.pitch = val;
|
||||
});
|
||||
#end
|
||||
|
||||
@@ -47,7 +47,8 @@ class VoicesGroup extends SoundGroup
|
||||
|
||||
function set_playerVolume(volume:Float):Float
|
||||
{
|
||||
playerVoices?.forEachAlive(function(voice:FunkinSound) {
|
||||
playerVoices?.forEachAlive(function(voice:FunkinSound)
|
||||
{
|
||||
voice.volume = volume;
|
||||
});
|
||||
return playerVolume = volume;
|
||||
@@ -55,15 +56,18 @@ class VoicesGroup extends SoundGroup
|
||||
|
||||
override function set_time(time:Float):Float
|
||||
{
|
||||
forEachAlive(function(snd) {
|
||||
forEachAlive(function(snd)
|
||||
{
|
||||
// account for different offsets per sound?
|
||||
snd.time = time;
|
||||
});
|
||||
|
||||
playerVoices?.forEachAlive(function(voice:FunkinSound) {
|
||||
playerVoices?.forEachAlive(function(voice:FunkinSound)
|
||||
{
|
||||
voice.time -= playerVoicesOffset;
|
||||
});
|
||||
opponentVoices?.forEachAlive(function(voice:FunkinSound) {
|
||||
opponentVoices?.forEachAlive(function(voice:FunkinSound)
|
||||
{
|
||||
voice.time -= opponentVoicesOffset;
|
||||
});
|
||||
|
||||
@@ -72,7 +76,8 @@ class VoicesGroup extends SoundGroup
|
||||
|
||||
function set_playerVoicesOffset(offset:Float):Float
|
||||
{
|
||||
playerVoices?.forEachAlive(function(voice:FunkinSound) {
|
||||
playerVoices?.forEachAlive(function(voice:FunkinSound)
|
||||
{
|
||||
voice.time += playerVoicesOffset;
|
||||
voice.time -= offset;
|
||||
});
|
||||
@@ -81,7 +86,8 @@ class VoicesGroup extends SoundGroup
|
||||
|
||||
function set_opponentVoicesOffset(offset:Float):Float
|
||||
{
|
||||
opponentVoices?.forEachAlive(function(voice:FunkinSound) {
|
||||
opponentVoices?.forEachAlive(function(voice:FunkinSound)
|
||||
{
|
||||
voice.time += opponentVoicesOffset;
|
||||
voice.time -= offset;
|
||||
});
|
||||
@@ -99,7 +105,8 @@ class VoicesGroup extends SoundGroup
|
||||
|
||||
function set_opponentVolume(volume:Float):Float
|
||||
{
|
||||
opponentVoices?.forEachAlive(function(voice:FunkinSound) {
|
||||
opponentVoices?.forEachAlive(function(voice:FunkinSound)
|
||||
{
|
||||
voice.volume = volume;
|
||||
});
|
||||
return opponentVolume = volume;
|
||||
|
||||
@@ -69,7 +69,8 @@ class SpectogramSprite extends FlxTypedSpriteGroup<FlxSprite>
|
||||
default:
|
||||
}
|
||||
|
||||
forEach(spr -> {
|
||||
forEach(spr ->
|
||||
{
|
||||
spr.visible = spr.ID % wavOptimiz == 0;
|
||||
});
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ class VisShit
|
||||
|
||||
// helpers, note that spectrum indexes suppose non-negative frequencies
|
||||
final binSize = fs / fftN;
|
||||
final indexToFreq = function(k:Int) {
|
||||
final indexToFreq = function(k:Int)
|
||||
{
|
||||
var powShit:Float = FlxMath.remapToRange(k, 0, halfN, 0, MathUtil.logBase(10, halfN)); // 4.3 is almost the log of 20Khz or so. Close enuf lol
|
||||
|
||||
return 1.0 * (Math.pow(10, powShit)); // we need the `1.0` to avoid overflows
|
||||
@@ -49,7 +50,8 @@ class VisShit
|
||||
// "melodic" band-pass filter
|
||||
final minFreq = 20.70;
|
||||
final maxFreq = 4000.01;
|
||||
final melodicBandPass = function(k:Int, s:Float) {
|
||||
final melodicBandPass = function(k:Int, s:Float)
|
||||
{
|
||||
final freq = indexToFreq(k);
|
||||
final filter = freq > minFreq - binSize && freq < maxFreq + binSize ? 1 : 0;
|
||||
return s;
|
||||
@@ -62,10 +64,7 @@ class VisShit
|
||||
while (c < samples.length)
|
||||
{
|
||||
// take a chunk (zero-padded if needed) and apply the window
|
||||
final chunk = [
|
||||
for (n in 0...fftN)
|
||||
(c + n < samples.length ? samples[c + n] : 0.0) * window(n)
|
||||
];
|
||||
final chunk = [for (n in 0...fftN) (c + n < samples.length ? samples[c + n] : 0.0) * window(n)];
|
||||
|
||||
// compute positive spectrum with sampling correction and BP filter
|
||||
final freqs = FFT.rfft(chunk).map(z -> z.scale(1 / fftN).magnitude).mapi(melodicBandPass);
|
||||
|
||||
@@ -10,58 +10,48 @@ abstract Complex({
|
||||
final imag:Float;
|
||||
})
|
||||
{
|
||||
public inline function new(real:Float, imag:Float)
|
||||
this = {real: real, imag: imag};
|
||||
public inline function new(real:Float, imag:Float) this = {real: real, imag: imag};
|
||||
|
||||
/**
|
||||
Makes a Complex number with the given Float as its real part and a zero imag part.
|
||||
**/
|
||||
@:from
|
||||
public static inline function fromReal(r:Float)
|
||||
return new Complex(r, 0);
|
||||
public static inline function fromReal(r:Float) return new Complex(r, 0);
|
||||
|
||||
/**
|
||||
Complex argument, in radians.
|
||||
**/
|
||||
public var angle(get, never):Float;
|
||||
|
||||
inline function get_angle()
|
||||
return Math.atan2(this.imag, this.real);
|
||||
inline function get_angle() return Math.atan2(this.imag, this.real);
|
||||
|
||||
/**
|
||||
Complex module.
|
||||
**/
|
||||
public var magnitude(get, never):Float;
|
||||
|
||||
inline function get_magnitude()
|
||||
return Math.sqrt(this.real * this.real + this.imag * this.imag);
|
||||
inline function get_magnitude() return Math.sqrt(this.real * this.real + this.imag * this.imag);
|
||||
|
||||
@:op(A + B)
|
||||
public inline function add(rhs:Complex):Complex
|
||||
return new Complex(this.real + rhs.real, this.imag + rhs.imag);
|
||||
public inline function add(rhs:Complex):Complex return new Complex(this.real + rhs.real, this.imag + rhs.imag);
|
||||
|
||||
@:op(A - B)
|
||||
public inline function sub(rhs:Complex):Complex
|
||||
return new Complex(this.real - rhs.real, this.imag - rhs.imag);
|
||||
public inline function sub(rhs:Complex):Complex return new Complex(this.real - rhs.real, this.imag - rhs.imag);
|
||||
|
||||
@:op(A * B)
|
||||
public inline function mult(rhs:Complex):Complex
|
||||
return new Complex(this.real * rhs.real - this.imag * rhs.imag, this.real * rhs.imag + this.imag * rhs.real);
|
||||
public inline function mult(rhs:Complex):Complex return new Complex(this.real * rhs.real - this.imag * rhs.imag, this.real * rhs.imag + this.imag * rhs.real);
|
||||
|
||||
/**
|
||||
Returns the complex conjugate, does not modify this object.
|
||||
**/
|
||||
public inline function conj():Complex
|
||||
return new Complex(this.real, -this.imag);
|
||||
public inline function conj():Complex return new Complex(this.real, -this.imag);
|
||||
|
||||
/**
|
||||
Multiplication by a real factor, does not modify this object.
|
||||
**/
|
||||
public inline function scale(k:Float):Complex
|
||||
return new Complex(this.real * k, this.imag * k);
|
||||
public inline function scale(k:Float):Complex return new Complex(this.real * k, this.imag * k);
|
||||
|
||||
public inline function copy():Complex
|
||||
return new Complex(this.real, this.imag);
|
||||
public inline function copy():Complex return new Complex(this.real, this.imag);
|
||||
|
||||
/**
|
||||
The imaginary unit.
|
||||
@@ -76,6 +66,5 @@ abstract Complex({
|
||||
/**
|
||||
Computes the complex exponential `e^(iw)`.
|
||||
**/
|
||||
public static inline function exp(w:Float)
|
||||
return new Complex(Math.cos(w), Math.sin(w));
|
||||
public static inline function exp(w:Float) return new Complex(Math.cos(w), Math.sin(w));
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ class FFT
|
||||
samples from the Discrete-Time Fourier Transform (DTFT) - which is Fs-periodic -
|
||||
with a spacing of Fs/N Hz between them and a scaling factor of Fs.
|
||||
**/
|
||||
public static function fft(input:Array<Complex>):Array<Complex>
|
||||
return do_fft(input, false);
|
||||
public static function fft(input:Array<Complex>):Array<Complex> return do_fft(input, false);
|
||||
|
||||
/**
|
||||
Like `fft`, but for a real (Float) sequence input.
|
||||
@@ -41,8 +40,7 @@ class FFT
|
||||
from each other, the result will consist of N data points as sampled
|
||||
from a time signal at intervals of 1/Fs with a scaling factor of 1/Fs.
|
||||
**/
|
||||
public static function ifft(input:Array<Complex>):Array<Complex>
|
||||
return do_fft(input, true);
|
||||
public static function ifft(input:Array<Complex>):Array<Complex> return do_fft(input, true);
|
||||
|
||||
// Handles padding and scaling for forwards and inverse FFTs.
|
||||
static function do_fft(input:Array<Complex>, inverse:Bool):Array<Complex>
|
||||
|
||||
@@ -12,35 +12,28 @@ abstract OffsetArray<T>({
|
||||
final offset:Int;
|
||||
})
|
||||
{
|
||||
public inline function new(array:Array<T>, offset:Int)
|
||||
this = {array: array, offset: offset};
|
||||
public inline function new(array:Array<T>, offset:Int) this = {array: array, offset: offset};
|
||||
|
||||
public var length(get, never):Int;
|
||||
|
||||
inline function get_length()
|
||||
return this.array.length;
|
||||
inline function get_length() return this.array.length;
|
||||
|
||||
@:arrayAccess
|
||||
public inline function get(index:Int):T
|
||||
return this.array[index - this.offset];
|
||||
public inline function get(index:Int):T return this.array[index - this.offset];
|
||||
|
||||
@:arrayAccess
|
||||
public inline function set(index:Int, value:T):Void
|
||||
this.array[index - this.offset] = value;
|
||||
public inline function set(index:Int, value:T):Void this.array[index - this.offset] = value;
|
||||
|
||||
/**
|
||||
Iterates through items in their original order while providing the altered indexes as keys.
|
||||
**/
|
||||
public inline function keyValueIterator():KeyValueIterator<Int, T>
|
||||
return new OffsetArrayIterator(this.array, this.offset);
|
||||
public inline function keyValueIterator():KeyValueIterator<Int, T> return new OffsetArrayIterator(this.array, this.offset);
|
||||
|
||||
@:from
|
||||
static inline function fromArray<T>(array:Array<T>)
|
||||
return new OffsetArray(array, 0);
|
||||
static inline function fromArray<T>(array:Array<T>) return new OffsetArray(array, 0);
|
||||
|
||||
@:to
|
||||
inline function toArray()
|
||||
return this.array;
|
||||
inline function toArray() return this.array;
|
||||
|
||||
/**
|
||||
Makes a shifted version of the given `array`, where elements are in the
|
||||
@@ -76,12 +69,12 @@ private class OffsetArrayIterator<T>
|
||||
this.enumeration = 0;
|
||||
}
|
||||
|
||||
public inline function next():{key:Int, value:T}
|
||||
public inline function next():
|
||||
{key:Int, value:T}
|
||||
{
|
||||
final i = this.enumeration++;
|
||||
return {key: i + this.offset, value: this.array[i]};
|
||||
}
|
||||
|
||||
public inline function hasNext():Bool
|
||||
return this.enumeration < this.array.length;
|
||||
public inline function hasNext():Bool return this.enumeration < this.array.length;
|
||||
}
|
||||
|
||||
@@ -86,30 +86,25 @@ class Signal
|
||||
/**
|
||||
Returns the average value of an array.
|
||||
**/
|
||||
public static function mean(y:Array<Float>):Float
|
||||
return sum(y) / y.length;
|
||||
public static function mean(y:Array<Float>):Float return sum(y) / y.length;
|
||||
|
||||
/**
|
||||
Returns the global maximum.
|
||||
**/
|
||||
public static function max(y:Array<Float>):Float
|
||||
return y.fold(Math.max, y[0]);
|
||||
public static function max(y:Array<Float>):Float return y.fold(Math.max, y[0]);
|
||||
|
||||
/**
|
||||
Returns the global maximum's index.
|
||||
**/
|
||||
public static function maxi(y:Array<Float>):Int
|
||||
return y.foldi((yi, m, i) -> yi > y[m] ? i : m, 0);
|
||||
public static function maxi(y:Array<Float>):Int return y.foldi((yi, m, i) -> yi > y[m] ? i : m, 0);
|
||||
|
||||
/**
|
||||
Returns the global minimum.
|
||||
**/
|
||||
public static function min(y:Array<Float>):Float
|
||||
return y.fold(Math.min, y[0]);
|
||||
public static function min(y:Array<Float>):Float return y.fold(Math.min, y[0]);
|
||||
|
||||
/**
|
||||
Returns the global minimum's index.
|
||||
**/
|
||||
public static function mini(y:Array<Float>):Int
|
||||
return y.foldi((yi, m, i) -> yi < y[m] ? i : m, 0);
|
||||
public static function mini(y:Array<Float>):Int return y.foldi((yi, m, i) -> yi < y[m] ? i : m, 0);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package funkin.audio.waveform;
|
||||
|
||||
import funkin.graphics.rendering.MeshRender;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
|
||||
/**
|
||||
* A sprite which displays the waveform of audio data.
|
||||
@@ -17,6 +18,8 @@ class WaveformSprite extends MeshRender
|
||||
static final DEFAULT_WIDTH:Float = 100.0;
|
||||
static final DEFAULT_HEIGHT:Float = 100.0;
|
||||
|
||||
public var iconId:CharacterType;
|
||||
|
||||
/**
|
||||
* Set this to true to tell the waveform to rebuild itself.
|
||||
* Do this any time the data or drawable area of the waveform changes.
|
||||
|
||||
@@ -109,7 +109,8 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
||||
// UNSCRIPTED ENTRIES
|
||||
//
|
||||
var entryIdList:Array<String> = DataAssets.listDataFilesInPath('${dataFilePath}/');
|
||||
var unscriptedEntryIds:Array<String> = entryIdList.filter(function(entryId:String):Bool {
|
||||
var unscriptedEntryIds:Array<String> = entryIdList.filter(function(entryId:String):Bool
|
||||
{
|
||||
return !entries.exists(entryId);
|
||||
});
|
||||
log(' INFO '.info() + 'Parsing ${unscriptedEntryIds.length} unscripted entries...');
|
||||
|
||||
@@ -243,11 +243,10 @@ class DataParse
|
||||
switch (json.value)
|
||||
{
|
||||
case JObject(fields):
|
||||
var result:LegacyNoteSection =
|
||||
{
|
||||
mustHitSection: false,
|
||||
sectionNotes: [],
|
||||
};
|
||||
var result:LegacyNoteSection = {
|
||||
mustHitSection: false,
|
||||
sectionNotes: [],
|
||||
};
|
||||
for (field in fields)
|
||||
{
|
||||
switch (field.name)
|
||||
|
||||
@@ -6,4 +6,6 @@ package funkin.data;
|
||||
* @see `funkin.data.BaseRegistry`
|
||||
*/
|
||||
@:autoBuild(funkin.util.macro.RegistryMacro.buildRegistry())
|
||||
interface DefaultRegistryImpl {}
|
||||
interface DefaultRegistryImpl
|
||||
{
|
||||
}
|
||||
|
||||
@@ -147,4 +147,20 @@ typedef UnnamedAnimationData =
|
||||
@:default("framelabel")
|
||||
@:optional
|
||||
var animType:String;
|
||||
|
||||
/**
|
||||
* The render type of the animation.
|
||||
* Only available for multi-sparrow and multi-animateatlas characters.
|
||||
* Options: "sparrow", "animateatlas"
|
||||
*/
|
||||
@:default("")
|
||||
@:optional
|
||||
var renderType:String;
|
||||
|
||||
/**
|
||||
* Various settings for the sprite.
|
||||
* Only available for texture atlases.
|
||||
*/
|
||||
@:optional
|
||||
var atlasSettings:funkin.data.stage.StageData.TextureAtlasData;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ class CharacterDataParser
|
||||
// UNSCRIPTED CHARACTERS
|
||||
//
|
||||
var charIdList:Array<String> = DataAssets.listDataFilesInPath('characters/');
|
||||
var unscriptedCharIds:Array<String> = charIdList.filter(function(charId:String):Bool {
|
||||
var unscriptedCharIds:Array<String> = charIdList.filter(function(charId:String):Bool
|
||||
{
|
||||
return !characterCache.exists(charId);
|
||||
});
|
||||
log('Fetching data for ${unscriptedCharIds.length} characters...');
|
||||
@@ -93,7 +94,7 @@ class CharacterDataParser
|
||||
{
|
||||
try
|
||||
{
|
||||
var character:SparrowCharacter = ScriptedSparrowCharacter.init(charCls, DEFAULT_CHAR_ID);
|
||||
var character:SparrowCharacter = ScriptedSparrowCharacter.scriptInit(charCls, DEFAULT_CHAR_ID);
|
||||
log('Loaded character ${character.characterName} (scripted: $charCls)');
|
||||
characterScriptedClass.set(character.characterId, charCls);
|
||||
}
|
||||
@@ -113,7 +114,7 @@ class CharacterDataParser
|
||||
{
|
||||
try
|
||||
{
|
||||
var character:PackerCharacter = ScriptedPackerCharacter.init(charCls, DEFAULT_CHAR_ID);
|
||||
var character:PackerCharacter = ScriptedPackerCharacter.scriptInit(charCls, DEFAULT_CHAR_ID);
|
||||
log('Loaded character ${character.characterName} (scripted: $charCls)');
|
||||
characterScriptedClass.set(character.characterId, charCls);
|
||||
}
|
||||
@@ -133,7 +134,7 @@ class CharacterDataParser
|
||||
{
|
||||
try
|
||||
{
|
||||
var character:MultiSparrowCharacter = ScriptedMultiSparrowCharacter.init(charCls, DEFAULT_CHAR_ID);
|
||||
var character:MultiSparrowCharacter = ScriptedMultiSparrowCharacter.scriptInit(charCls, DEFAULT_CHAR_ID);
|
||||
log('Loaded character ${character.characterName} (scripted: $charCls)');
|
||||
characterScriptedClass.set(character.characterId, charCls);
|
||||
}
|
||||
@@ -153,7 +154,7 @@ class CharacterDataParser
|
||||
{
|
||||
try
|
||||
{
|
||||
var character:AnimateAtlasCharacter = ScriptedAnimateAtlasCharacter.init(charCls, DEFAULT_CHAR_ID);
|
||||
var character:AnimateAtlasCharacter = ScriptedAnimateAtlasCharacter.scriptInit(charCls, DEFAULT_CHAR_ID);
|
||||
log('Loaded character ${character.characterName} (scripted: $charCls)');
|
||||
characterScriptedClass.set(character.characterId, charCls);
|
||||
}
|
||||
@@ -173,7 +174,7 @@ class CharacterDataParser
|
||||
{
|
||||
try
|
||||
{
|
||||
var character:MultiAnimateAtlasCharacter = ScriptedMultiAnimateAtlasCharacter.init(charCls, DEFAULT_CHAR_ID);
|
||||
var character:MultiAnimateAtlasCharacter = ScriptedMultiAnimateAtlasCharacter.scriptInit(charCls, DEFAULT_CHAR_ID);
|
||||
log('Loaded character ${character.characterName} (scripted: $charCls)');
|
||||
characterScriptedClass.set(character.characterId, charCls);
|
||||
}
|
||||
@@ -188,7 +189,8 @@ class CharacterDataParser
|
||||
// NOTE: Only instantiate the ones not populated above.
|
||||
// ScriptedBaseCharacter.listScriptClasses() will pick up scripts extending the other classes.
|
||||
var scriptedCharClassNames:Array<String> = ScriptedBaseCharacter.listScriptClasses();
|
||||
scriptedCharClassNames = scriptedCharClassNames.filter(function(charCls:String):Bool {
|
||||
scriptedCharClassNames = scriptedCharClassNames.filter(function(charCls:String):Bool
|
||||
{
|
||||
return !(scriptedCharClassNames1.contains(charCls)
|
||||
|| scriptedCharClassNames2.contains(charCls)
|
||||
|| scriptedCharClassNames3.contains(charCls)
|
||||
@@ -201,7 +203,7 @@ class CharacterDataParser
|
||||
log('Instantiating ${scriptedCharClassNames.length} (Base) scripted characters...');
|
||||
for (charCls in scriptedCharClassNames)
|
||||
{
|
||||
var character:BaseCharacter = ScriptedBaseCharacter.init(charCls, DEFAULT_CHAR_ID, Custom);
|
||||
var character:BaseCharacter = ScriptedBaseCharacter.scriptInit(charCls, DEFAULT_CHAR_ID, Custom);
|
||||
if (character == null)
|
||||
{
|
||||
log(' ERROR '.error() + 'Failed to initialize scripted character: $charCls');
|
||||
@@ -245,18 +247,18 @@ class CharacterDataParser
|
||||
if (charData != null) switch (charData.renderType)
|
||||
{
|
||||
case CharacterRenderType.AnimateAtlas:
|
||||
char = ScriptedAnimateAtlasCharacter.init(charScriptClass, charId);
|
||||
char = ScriptedAnimateAtlasCharacter.scriptInit(charScriptClass, charId);
|
||||
case CharacterRenderType.MultiSparrow:
|
||||
char = ScriptedMultiSparrowCharacter.init(charScriptClass, charId);
|
||||
char = ScriptedMultiSparrowCharacter.scriptInit(charScriptClass, charId);
|
||||
case CharacterRenderType.Sparrow:
|
||||
char = ScriptedSparrowCharacter.init(charScriptClass, charId);
|
||||
char = ScriptedSparrowCharacter.scriptInit(charScriptClass, charId);
|
||||
case CharacterRenderType.Packer:
|
||||
char = ScriptedPackerCharacter.init(charScriptClass, charId);
|
||||
char = ScriptedPackerCharacter.scriptInit(charScriptClass, charId);
|
||||
case CharacterRenderType.MultiAnimateAtlas:
|
||||
char = ScriptedMultiAnimateAtlasCharacter.init(charScriptClass, charId);
|
||||
char = ScriptedMultiAnimateAtlasCharacter.scriptInit(charScriptClass, charId);
|
||||
default:
|
||||
// We're going to assume that the script class does the rendering.
|
||||
char = ScriptedBaseCharacter.init(charScriptClass, charId, CharacterRenderType.Custom);
|
||||
char = ScriptedBaseCharacter.scriptInit(charScriptClass, charId, CharacterRenderType.Custom);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -353,7 +355,8 @@ class CharacterDataParser
|
||||
{
|
||||
var frames = Paths.getSparrowAtlas(charPath);
|
||||
|
||||
var idleFrame:Null<FlxFrame> = frames.frames.find(function(frame:FlxFrame):Bool {
|
||||
var idleFrame:Null<FlxFrame> = frames.frames.find(function(frame:FlxFrame):Bool
|
||||
{
|
||||
return frame.name.startsWith('idle');
|
||||
});
|
||||
|
||||
@@ -463,14 +466,13 @@ class CharacterDataParser
|
||||
public static final DEFAULT_STARTINGANIM:String = 'idle';
|
||||
public static final DEFAULT_APPLYSTAGEMATRIX:Bool = false;
|
||||
public static final DEFAULT_ANIMTYPE:String = "framelabel";
|
||||
public static final DEFAULT_ATLASSETTINGS:funkin.data.stage.StageData.TextureAtlasData =
|
||||
{
|
||||
swfMode: true,
|
||||
cacheOnLoad: false,
|
||||
filterQuality: 1,
|
||||
applyStageMatrix: false,
|
||||
useRenderTexture: false
|
||||
};
|
||||
public static final DEFAULT_ATLASSETTINGS:funkin.data.stage.StageData.TextureAtlasData = {
|
||||
swfMode: true,
|
||||
cacheOnLoad: false,
|
||||
filterQuality: 1,
|
||||
applyStageMatrix: false,
|
||||
useRenderTexture: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Set unspecified parameters to their defaults.
|
||||
@@ -528,14 +530,13 @@ class CharacterDataParser
|
||||
|
||||
if (input.healthIcon == null)
|
||||
{
|
||||
input.healthIcon =
|
||||
{
|
||||
id: null,
|
||||
scale: null,
|
||||
flipX: null,
|
||||
isPixel: null,
|
||||
offsets: null
|
||||
};
|
||||
input.healthIcon = {
|
||||
id: null,
|
||||
scale: null,
|
||||
flipX: null,
|
||||
isPixel: null,
|
||||
offsets: null
|
||||
};
|
||||
}
|
||||
|
||||
if (input.healthIcon.id == null)
|
||||
@@ -705,7 +706,7 @@ enum abstract CharacterRenderType(String) from String to String
|
||||
typedef CharacterData =
|
||||
{
|
||||
/**
|
||||
* The sematic version number of the character data JSON format.
|
||||
* The semantic version number of the character data JSON format.
|
||||
*/
|
||||
var version:String;
|
||||
|
||||
@@ -723,7 +724,7 @@ typedef CharacterData =
|
||||
/**
|
||||
* Behavior varies by render type:
|
||||
* - SPARROW: Path to retrieve both the spritesheet and the XML data from.
|
||||
* - PACKER: Path to retrieve both the spritsheet and the TXT data from.
|
||||
* - PACKER: Path to retrieve both the spritesheet and the TXT data from.
|
||||
*/
|
||||
var assetPath:String;
|
||||
|
||||
|
||||
@@ -23,4 +23,6 @@ class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData, SpeakerEntryPar
|
||||
}
|
||||
}
|
||||
|
||||
typedef SpeakerEntryParams = {}
|
||||
typedef SpeakerEntryParams =
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package funkin.data.event;
|
||||
|
||||
import funkin.play.event.SongEvent;
|
||||
import flixel.util.FlxSort;
|
||||
import funkin.data.song.SongData.SongEventData;
|
||||
import funkin.util.macro.ClassMacro;
|
||||
import funkin.play.event.ScriptedSongEvent;
|
||||
import funkin.play.event.SongEvent;
|
||||
import funkin.util.macro.ClassMacro;
|
||||
import funkin.util.SortUtil;
|
||||
|
||||
/**
|
||||
* This class statically handles the parsing of internal and scripted song event handlers.
|
||||
@@ -64,7 +66,7 @@ class SongEventRegistry
|
||||
|
||||
for (eventCls in scriptedEventClassNames)
|
||||
{
|
||||
var event:SongEvent = ScriptedSongEvent.init(eventCls, "UKNOWN");
|
||||
var event:SongEvent = ScriptedSongEvent.scriptInit(eventCls, "UKNOWN");
|
||||
|
||||
if (event != null)
|
||||
{
|
||||
@@ -108,8 +110,7 @@ class SongEventRegistry
|
||||
|
||||
public static function handleEvent(data:SongEventData):Void
|
||||
{
|
||||
var eventKind:String = data.eventKind;
|
||||
var eventHandler:Null<SongEvent> = eventCache.get(eventKind);
|
||||
var eventHandler:Null<SongEvent> = getEvent(data.eventKind);
|
||||
|
||||
if (eventHandler != null)
|
||||
{
|
||||
@@ -117,7 +118,7 @@ class SongEventRegistry
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('WARNING: No event handler for event with kind: ${eventKind}');
|
||||
trace('WARNING: No event handler for event with kind: ${data.eventKind}');
|
||||
}
|
||||
|
||||
data.activated = true;
|
||||
@@ -132,12 +133,14 @@ class SongEventRegistry
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of song events and the current timestamp,
|
||||
* return a list of events that should be handled.
|
||||
* @param events The list of available song events.
|
||||
* @param currentTime The current time in milliseconds.
|
||||
* @return The list of events which haven't been handled yet.
|
||||
*/
|
||||
public static function queryEvents(events:Array<SongEventData>, currentTime:Float):Array<SongEventData>
|
||||
{
|
||||
return events.filter(function(event:SongEventData):Bool {
|
||||
var result:Array<SongEventData> = events.filter(function(event:SongEventData):Bool
|
||||
{
|
||||
// If the event is already activated, don't activate it again.
|
||||
if (event.activated) return false;
|
||||
|
||||
@@ -146,6 +149,10 @@ class SongEventRegistry
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
result.sort(SortUtil.eventDataByTime.bind(FlxSort.ASCENDING));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,4 +24,6 @@ class AlbumRegistry extends BaseRegistry<Album, AlbumData, AlbumEntryParams> imp
|
||||
}
|
||||
}
|
||||
|
||||
typedef AlbumEntryParams = {}
|
||||
typedef AlbumEntryParams =
|
||||
{
|
||||
}
|
||||
|
||||
@@ -129,10 +129,10 @@ class PlayerFreeplayDJData
|
||||
var charSelect:Null<PlayerFreeplayDJCharSelectData>;
|
||||
|
||||
@:optional
|
||||
var cartoon:Null<PlayerFreeplayDJCartoonData>;
|
||||
var fistPump:Null<PlayerFreeplayDJFistPumpData>;
|
||||
|
||||
@:optional
|
||||
var fistPump:Null<PlayerFreeplayDJFistPumpData>;
|
||||
public var atlasSettings:funkin.data.stage.StageData.TextureAtlasData;
|
||||
|
||||
@:optional
|
||||
@:default("animateatlas")
|
||||
@@ -142,12 +142,20 @@ class PlayerFreeplayDJData
|
||||
@:default("")
|
||||
public var scriptClass:Null<String>;
|
||||
|
||||
@:optional
|
||||
@:default(false)
|
||||
public var useAnimatePosition:Bool;
|
||||
|
||||
@:optional
|
||||
@:default([0, 0])
|
||||
var offsets:Array<Float>;
|
||||
|
||||
public function new()
|
||||
{
|
||||
animationMap = new Map();
|
||||
}
|
||||
|
||||
function mapAnimations()
|
||||
function mapAnimations():Void
|
||||
{
|
||||
if (animationMap == null) animationMap = new Map();
|
||||
if (prefixToOffsetsMap == null) prefixToOffsetsMap = new Map();
|
||||
@@ -161,17 +169,39 @@ class PlayerFreeplayDJData
|
||||
}
|
||||
}
|
||||
|
||||
public inline function getAssetPath():String
|
||||
return assetPath; // return assetPath;
|
||||
public inline function getAssetPath():String return assetPath; // return assetPath;
|
||||
|
||||
public inline function getAnimationsList():Array<AnimationData>
|
||||
return animations;
|
||||
public inline function getAnimationsList():Array<AnimationData> return animations;
|
||||
|
||||
public function useApplyStageMatrix():Bool
|
||||
{
|
||||
return applyStageMatrix;
|
||||
}
|
||||
|
||||
public function getGlobalOffsets():Array<Float>
|
||||
{
|
||||
return offsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally, we'd let `FunkinSprite` handle the settings validation, but
|
||||
* Freeplay DJs have a special case where the turntable lights use a movieclip
|
||||
* that remains static without SWF mode enabled!
|
||||
* So we have to manually validate the settings to have SWF mode enabled by default.
|
||||
*
|
||||
* @return The configuration for the texture atlas.
|
||||
*/
|
||||
public function getAtlasSettings():funkin.graphics.FunkinSprite.AtlasSpriteSettings
|
||||
{
|
||||
return {
|
||||
swfMode: atlasSettings?.swfMode ?? true,
|
||||
cacheOnLoad: atlasSettings?.cacheOnLoad ?? false,
|
||||
filterQuality: cast atlasSettings?.filterQuality ?? animate.FlxAnimateFrames.FilterQuality.MEDIUM,
|
||||
applyStageMatrix: atlasSettings?.applyStageMatrix ?? false,
|
||||
useRenderTexture: atlasSettings?.useRenderTexture ?? false
|
||||
}
|
||||
}
|
||||
|
||||
public function getFreeplayDJText(index:Int):String
|
||||
{
|
||||
switch (index)
|
||||
@@ -208,33 +238,6 @@ class PlayerFreeplayDJData
|
||||
return getAnimationOffsetsByPrefix(getAnimationPrefix(name));
|
||||
}
|
||||
|
||||
// TODO: These should really be frame labels, ehe.
|
||||
|
||||
public function getCartoonSoundClickFrame():Int
|
||||
{
|
||||
return cartoon?.soundClickFrame ?? 80;
|
||||
}
|
||||
|
||||
public function getCartoonSoundCartoonFrame():Int
|
||||
{
|
||||
return cartoon?.soundCartoonFrame ?? 85;
|
||||
}
|
||||
|
||||
public function getCartoonLoopBlinkFrame():Int
|
||||
{
|
||||
return cartoon?.loopBlinkFrame ?? 112;
|
||||
}
|
||||
|
||||
public function getCartoonLoopFrame():Int
|
||||
{
|
||||
return cartoon?.loopFrame ?? 166;
|
||||
}
|
||||
|
||||
public function getCartoonChannelChangeFrame():Int
|
||||
{
|
||||
return cartoon?.channelChangeFrame ?? 60;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroStartFrame():Int
|
||||
{
|
||||
return fistPump?.introStartFrame ?? 0;
|
||||
@@ -406,15 +409,6 @@ typedef PlayerFreeplayDJCharSelectData =
|
||||
var transitionDelay:Float;
|
||||
}
|
||||
|
||||
typedef PlayerFreeplayDJCartoonData =
|
||||
{
|
||||
var soundClickFrame:Int;
|
||||
var soundCartoonFrame:Int;
|
||||
var loopBlinkFrame:Int;
|
||||
var loopFrame:Int;
|
||||
var channelChangeFrame:Int;
|
||||
}
|
||||
|
||||
typedef PlayerFreeplayDJFistPumpData =
|
||||
{
|
||||
@:default(0)
|
||||
|
||||
@@ -148,4 +148,6 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData, PlayerE
|
||||
}
|
||||
}
|
||||
|
||||
typedef PlayerEntryParams = {}
|
||||
typedef PlayerEntryParams =
|
||||
{
|
||||
}
|
||||
|
||||
@@ -25,4 +25,6 @@ class FreeplayStyleRegistry extends BaseRegistry<FreeplayStyle, FreeplayStyleDat
|
||||
}
|
||||
}
|
||||
|
||||
typedef FreeplayStyleEntryParams = {}
|
||||
typedef FreeplayStyleEntryParams =
|
||||
{
|
||||
}
|
||||
|
||||
@@ -219,9 +219,17 @@ typedef NoteStyleData_Countdown =
|
||||
var audioPath:String;
|
||||
}
|
||||
|
||||
typedef NoteStyleData_HoldNote = {}
|
||||
typedef NoteStyleData_Judgement = {}
|
||||
typedef NoteStyleData_ComboNum = {}
|
||||
typedef NoteStyleData_HoldNote =
|
||||
{
|
||||
}
|
||||
|
||||
typedef NoteStyleData_Judgement =
|
||||
{
|
||||
}
|
||||
|
||||
typedef NoteStyleData_ComboNum =
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Data on animations for each direction of the strumline.
|
||||
|
||||
@@ -31,4 +31,6 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData, NoteStyle
|
||||
}
|
||||
}
|
||||
|
||||
typedef NoteStyleEntryParams = {}
|
||||
typedef NoteStyleEntryParams =
|
||||
{
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ class SongMetadata implements ICloneable<SongMetadata>
|
||||
* A semantic versioning string for the song data format.
|
||||
*
|
||||
*/
|
||||
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
@@ -53,7 +52,6 @@ class SongMetadata implements ICloneable<SongMetadata>
|
||||
*/
|
||||
public var playData:SongPlayData;
|
||||
|
||||
@:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY)
|
||||
public var generatedBy:String;
|
||||
|
||||
@:optional
|
||||
@@ -352,7 +350,6 @@ class SongMusicData implements ICloneable<SongMusicData>
|
||||
* A semantic versioning string for the song data format.
|
||||
*
|
||||
*/
|
||||
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
@@ -463,6 +460,7 @@ class SongPlayData implements ICloneable<SongPlayData>
|
||||
*/
|
||||
@:optional
|
||||
@:default(['normal' => 0])
|
||||
@:order(funkin.util.Constants.DEFAULT_DIFFICULTY_LIST_FULL)
|
||||
public var ratings:Map<String, Int>;
|
||||
|
||||
/**
|
||||
@@ -568,7 +566,7 @@ class SongCharacterData implements ICloneable<SongCharacterData>
|
||||
this.opponent = opponent;
|
||||
this.instrumental = instrumental;
|
||||
|
||||
this.altInstrumentals = altInstrumentals;
|
||||
this.altInstrumentals = altInstrumentals ?? [];
|
||||
this.opponentVocals = opponentVocals;
|
||||
this.playerVocals = playerVocals;
|
||||
|
||||
@@ -595,16 +593,16 @@ class SongCharacterData implements ICloneable<SongCharacterData>
|
||||
|
||||
class SongChartData implements ICloneable<SongChartData>
|
||||
{
|
||||
@:default(funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
|
||||
@:order(funkin.util.Constants.DEFAULT_DIFFICULTY_LIST_FULL)
|
||||
public var scrollSpeed:Map<String, Float>;
|
||||
public var events:Array<SongEventData>;
|
||||
@:order(funkin.util.Constants.DEFAULT_DIFFICULTY_LIST_FULL)
|
||||
public var notes:Map<String, Array<SongNoteData>>;
|
||||
|
||||
@:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY)
|
||||
public var generatedBy:String;
|
||||
|
||||
/**
|
||||
@@ -782,7 +780,9 @@ class SongEventDataRaw implements ICloneable<SongEventDataRaw>
|
||||
|
||||
public function valueAsStruct(?defaultKey:String = "key"):Dynamic
|
||||
{
|
||||
if (this.value == null) return {};
|
||||
if (this.value == null) return
|
||||
{
|
||||
};
|
||||
if (Std.isOfType(this.value, Array))
|
||||
{
|
||||
var result:haxe.DynamicAccess<Dynamic> = {};
|
||||
|
||||
@@ -24,7 +24,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function offsetSongNoteData(notes:Array<SongNoteData>, offset:Float):Array<SongNoteData>
|
||||
{
|
||||
var offsetNote = function(note:SongNoteData):SongNoteData {
|
||||
var offsetNote = function(note:SongNoteData):SongNoteData
|
||||
{
|
||||
var time:Float = note.time + offset;
|
||||
var data:Int = note.data;
|
||||
var length:Float = note.length;
|
||||
@@ -46,7 +47,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function offsetSongEventData(events:Array<SongEventData>, offset:Float):Array<SongEventData>
|
||||
{
|
||||
return events.map(function(event:SongEventData):SongEventData {
|
||||
return events.map(function(event:SongEventData):SongEventData
|
||||
{
|
||||
return new SongEventData(event.time + offset, event.eventKind, event.value);
|
||||
});
|
||||
}
|
||||
@@ -61,7 +63,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function clampSongNoteData(notes:Array<SongNoteData>, startTime:Float, endTime:Float):Array<SongNoteData>
|
||||
{
|
||||
return notes.filter(function(note:SongNoteData):Bool {
|
||||
return notes.filter(function(note:SongNoteData):Bool
|
||||
{
|
||||
return note.time >= startTime && note.time <= endTime;
|
||||
});
|
||||
}
|
||||
@@ -76,7 +79,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function clampSongEventData(events:Array<SongEventData>, startTime:Float, endTime:Float):Array<SongEventData>
|
||||
{
|
||||
return events.filter(function(event:SongEventData):Bool {
|
||||
return events.filter(function(event:SongEventData):Bool
|
||||
{
|
||||
return event.time >= startTime && event.time <= endTime;
|
||||
});
|
||||
}
|
||||
@@ -92,7 +96,8 @@ class SongDataUtils
|
||||
{
|
||||
if (notes.length == 0 || subtrahend.length == 0) return notes;
|
||||
|
||||
var result = notes.filter(function(note:SongNoteData):Bool {
|
||||
var result = notes.filter(function(note:SongNoteData):Bool
|
||||
{
|
||||
for (x in subtrahend)
|
||||
{
|
||||
// The currently iterated note is in the subtrahend array.
|
||||
@@ -117,7 +122,8 @@ class SongDataUtils
|
||||
{
|
||||
if (events.length == 0 || subtrahend.length == 0) return events;
|
||||
|
||||
return events.filter(function(event:SongEventData):Bool {
|
||||
return events.filter(function(event:SongEventData):Bool
|
||||
{
|
||||
for (x in subtrahend)
|
||||
{
|
||||
// The currently iterated event is in the subtrahend array.
|
||||
@@ -134,7 +140,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function flipNotes(notes:Array<SongNoteData>, strumlineSize:Int = 4):Array<SongNoteData>
|
||||
{
|
||||
return notes.map(function(note:SongNoteData):SongNoteData {
|
||||
return notes.map(function(note:SongNoteData):SongNoteData
|
||||
{
|
||||
var newData = note.data;
|
||||
|
||||
if (newData < strumlineSize) newData += strumlineSize;
|
||||
@@ -145,6 +152,63 @@ class SongDataUtils
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of notes whose note data is mirrored.
|
||||
* Does not mutate the original array.
|
||||
*
|
||||
* @param flip Flip the notes if the notes given are in both strumlines, so that result isn't inverted when mirrored.
|
||||
* @param mirrorX Mirror along the X axis, aka the directions of the notes.
|
||||
* @param mirrorY Mirror along the Y axis, aka the time of the notes.
|
||||
*/
|
||||
public static function mirrorNotes(notes:Array<SongNoteData>, strumlineSize:Int = 4, flip:Bool = false, mirrorX:Bool = true,
|
||||
mirrorY:Bool = true):Array<SongNoteData>
|
||||
{
|
||||
var minTime = notes[0].time;
|
||||
var maxTime = notes[0].time;
|
||||
var minStrumline = notes[0].data;
|
||||
var maxStrumline = notes[0].data;
|
||||
for (note in notes)
|
||||
{
|
||||
// Find the maximum and minimum time and strumline positions
|
||||
// I wish there was a better way of doing this
|
||||
if (flip)
|
||||
{
|
||||
if (note.data < minStrumline) minStrumline = note.data;
|
||||
else if (note.data > maxStrumline) maxStrumline = note.data;
|
||||
}
|
||||
if (note.time < minTime) minTime = note.time;
|
||||
else if (note.time > maxTime) maxTime = note.time;
|
||||
}
|
||||
|
||||
var timeDiff = minTime + (maxTime - minTime) / 2;
|
||||
if (flip && minStrumline < strumlineSize && strumlineSize < maxStrumline)
|
||||
{
|
||||
// Flip the notes if one of the notes is on the other strum
|
||||
// Otherwise they'll be inverted when mirrored
|
||||
notes = flipNotes(notes);
|
||||
}
|
||||
|
||||
return notes.map(function(note:SongNoteData):SongNoteData
|
||||
{
|
||||
var newData = note.data;
|
||||
var newTime = note.time;
|
||||
|
||||
if (mirrorX)
|
||||
{
|
||||
if (newData < strumlineSize) newData = strumlineSize - 1 - newData;
|
||||
else
|
||||
newData = strumlineSize + strumlineSize * 2 - 1 - newData;
|
||||
}
|
||||
if (mirrorY)
|
||||
{
|
||||
if (newTime < timeDiff) newTime += (timeDiff - newTime) * 2;
|
||||
else if (newTime > timeDiff) newTime -= (newTime - timeDiff) * 2;
|
||||
}
|
||||
|
||||
return new SongNoteData(newTime, newData, note.length, note.kind);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an array of notes to be used as the clipboard data.
|
||||
*
|
||||
@@ -175,7 +239,8 @@ class SongDataUtils
|
||||
public static function sortNotes(notes:Array<SongNoteData>, desc:Bool = false):Array<SongNoteData>
|
||||
{
|
||||
// TODO: Modifies the array in place. Is this okay?
|
||||
notes.sort(function(a:SongNoteData, b:SongNoteData):Int {
|
||||
notes.sort(function(a:SongNoteData, b:SongNoteData):Int
|
||||
{
|
||||
return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time);
|
||||
});
|
||||
return notes;
|
||||
@@ -187,7 +252,8 @@ class SongDataUtils
|
||||
public static function sortEvents(events:Array<SongEventData>, desc:Bool = false):Array<SongEventData>
|
||||
{
|
||||
// TODO: Modifies the array in place. Is this okay?
|
||||
events.sort(function(a:SongEventData, b:SongEventData):Int {
|
||||
events.sort(function(a:SongEventData, b:SongEventData):Int
|
||||
{
|
||||
return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time);
|
||||
});
|
||||
return events;
|
||||
@@ -199,7 +265,8 @@ class SongDataUtils
|
||||
public static function sortTimeChanges(timeChanges:Array<SongTimeChange>, desc:Bool = false):Array<SongTimeChange>
|
||||
{
|
||||
// TODO: Modifies the array in place. Is this okay?
|
||||
timeChanges.sort(function(a:SongTimeChange, b:SongTimeChange):Int {
|
||||
timeChanges.sort(function(a:SongTimeChange, b:SongTimeChange):Int
|
||||
{
|
||||
return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.timeStamp, b.timeStamp);
|
||||
});
|
||||
return timeChanges;
|
||||
@@ -256,7 +323,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function getNotesInTimeRange(notes:Array<SongNoteData>, start:Float, end:Float):Array<SongNoteData>
|
||||
{
|
||||
return notes.filter(function(note:SongNoteData):Bool {
|
||||
return notes.filter(function(note:SongNoteData):Bool
|
||||
{
|
||||
return note.time >= start && note.time <= end;
|
||||
});
|
||||
}
|
||||
@@ -266,7 +334,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function getEventsInTimeRange(events:Array<SongEventData>, start:Float, end:Float):Array<SongEventData>
|
||||
{
|
||||
return events.filter(function(event:SongEventData):Bool {
|
||||
return events.filter(function(event:SongEventData):Bool
|
||||
{
|
||||
return event.time >= start && event.time <= end;
|
||||
});
|
||||
}
|
||||
@@ -276,7 +345,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function getNotesInDataRange(notes:Array<SongNoteData>, start:Int, end:Int):Array<SongNoteData>
|
||||
{
|
||||
return notes.filter(function(note:SongNoteData):Bool {
|
||||
return notes.filter(function(note:SongNoteData):Bool
|
||||
{
|
||||
return note.data >= start && note.data <= end;
|
||||
});
|
||||
}
|
||||
@@ -286,7 +356,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function getNotesWithData(notes:Array<SongNoteData>, data:Array<Int>):Array<SongNoteData>
|
||||
{
|
||||
return notes.filter(function(note:SongNoteData):Bool {
|
||||
return notes.filter(function(note:SongNoteData):Bool
|
||||
{
|
||||
return data.indexOf(note.data) != -1;
|
||||
});
|
||||
}
|
||||
@@ -296,7 +367,8 @@ class SongDataUtils
|
||||
*/
|
||||
public static function getEventsWithKind(events:Array<SongEventData>, kinds:Array<String>):Array<SongEventData>
|
||||
{
|
||||
return events.filter(function(event:SongEventData):Bool {
|
||||
return events.filter(function(event:SongEventData):Bool
|
||||
{
|
||||
return kinds.indexOf(event.eventKind) != -1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,10 +85,12 @@ using funkin.data.song.migrator.SongDataMigrator;
|
||||
//
|
||||
// UNSCRIPTED ENTRIES
|
||||
//
|
||||
var entryIdList:Array<String> = DataAssets.listDataFilesInPath('songs/', '-metadata.json').map(function(songDataPath:String):String {
|
||||
var entryIdList:Array<String> = DataAssets.listDataFilesInPath('songs/', '-metadata.json').map(function(songDataPath:String):String
|
||||
{
|
||||
return songDataPath.split('/')[0];
|
||||
});
|
||||
var unscriptedEntryIds:Array<String> = entryIdList.filter(function(entryId:String):Bool {
|
||||
var unscriptedEntryIds:Array<String> = entryIdList.filter(function(entryId:String):Bool
|
||||
{
|
||||
return !entries.exists(entryId);
|
||||
});
|
||||
log('Parsing ${unscriptedEntryIds.length} unscripted entries...');
|
||||
@@ -563,6 +565,8 @@ using funkin.data.song.migrator.SongDataMigrator;
|
||||
}
|
||||
}
|
||||
|
||||
allDifficulties.sort(funkin.util.SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST_FULL));
|
||||
|
||||
if (allDifficulties.length == 0)
|
||||
{
|
||||
trace(' WARNING '.bold().bg_yellow() + ' No difficulties found. Returning default difficulty list.');
|
||||
|
||||
@@ -12,7 +12,6 @@ class ChartManifestData
|
||||
|
||||
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)
|
||||
public var version:thx.semver.Version;
|
||||
|
||||
@@ -27,7 +27,9 @@ class LegacySongData
|
||||
public var notes:Either<Array<LegacyNoteSection>, LegacyNoteData>;
|
||||
public var song:String; // Song name
|
||||
|
||||
public function new() {}
|
||||
public function new()
|
||||
{
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
|
||||
@@ -31,7 +31,8 @@ class OsuManiaImporter
|
||||
currentSection = sectionRegex.matched(1);
|
||||
if (nonCSVLikeSections.contains(currentSection))
|
||||
{
|
||||
Reflect.setField(result, currentSection, {});
|
||||
Reflect.setField(result, currentSection, {
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -46,7 +47,8 @@ class OsuManiaImporter
|
||||
var parts:Array<String> = line.split(":");
|
||||
var key:String = StringTools.trim(parts.shift());
|
||||
var value:String = StringTools.trim(parts.join(":"));
|
||||
if (Reflect.field(result, currentSection) == null) Reflect.setField(result, currentSection, {});
|
||||
if (Reflect.field(result, currentSection) == null) Reflect.setField(result, currentSection, {
|
||||
});
|
||||
Reflect.setField(Reflect.field(result, currentSection), key, value);
|
||||
}
|
||||
// For CSV-like sections
|
||||
@@ -163,7 +165,8 @@ class OsuManiaImporter
|
||||
|
||||
static function parseTimingPoints(timingLines:Array<String>):Array<TimingPoint>
|
||||
{
|
||||
return timingLines.map(function(line:String):TimingPoint {
|
||||
return timingLines.map(function(line:String):TimingPoint
|
||||
{
|
||||
var parts = line.split(",");
|
||||
var time = Std.parseFloat(parts[0]);
|
||||
var beatLength = Std.parseFloat(parts[1]);
|
||||
@@ -180,7 +183,8 @@ class OsuManiaImporter
|
||||
|
||||
static function parseManiaHitObjects(hitObjectsLines:Array<String>, ?columns:Int = 4):Array<ManiaHitObject>
|
||||
{
|
||||
return hitObjectsLines.map(function(line:String):ManiaHitObject {
|
||||
return hitObjectsLines.map(function(line:String):ManiaHitObject
|
||||
{
|
||||
var parts = line.split(",");
|
||||
|
||||
var x:Int = Std.parseInt(parts[0]);
|
||||
|
||||
@@ -10,7 +10,6 @@ typedef StepManiaData =
|
||||
var Credit:String;
|
||||
var Banner:String;
|
||||
var Background:String;
|
||||
|
||||
var Offset:Float;
|
||||
var SampleStart:Float;
|
||||
};
|
||||
@@ -19,13 +18,15 @@ typedef StepManiaData =
|
||||
var Difficulties:Array<StepDifficulty>;
|
||||
}
|
||||
|
||||
enum StepManiaChartType {
|
||||
enum StepManiaChartType
|
||||
{
|
||||
DanceSingle;
|
||||
DanceDouble;
|
||||
Unknown;
|
||||
}
|
||||
|
||||
enum StepManiaNoteType {
|
||||
enum StepManiaNoteType
|
||||
{
|
||||
Tap;
|
||||
Head;
|
||||
Tail;
|
||||
@@ -44,7 +45,8 @@ class StepNote
|
||||
{
|
||||
this.beat = beat;
|
||||
this.column = column;
|
||||
switch (t) {
|
||||
switch (t)
|
||||
{
|
||||
case "2":
|
||||
this.type = StepManiaNoteType.Head;
|
||||
case "3":
|
||||
@@ -72,7 +74,8 @@ class StepDifficulty
|
||||
|
||||
public function parseChartType(chartTypeStr:String):StepManiaChartType
|
||||
{
|
||||
switch (chartTypeStr) {
|
||||
switch (chartTypeStr)
|
||||
{
|
||||
case "dance-single":
|
||||
return StepManiaChartType.DanceSingle;
|
||||
case "dance-double":
|
||||
|
||||
@@ -5,7 +5,6 @@ import funkin.data.song.SongData.SongChartData;
|
||||
import funkin.data.song.SongData.SongCharacterData;
|
||||
import funkin.data.song.SongData.SongNoteData;
|
||||
import funkin.data.song.SongData.SongTimeChange;
|
||||
|
||||
import funkin.data.song.importer.StepManiaData.StepTimingPoint;
|
||||
import funkin.data.song.importer.StepManiaData.StepDifficulty;
|
||||
import funkin.data.song.importer.StepManiaData.StepManiaChartType;
|
||||
@@ -223,7 +222,6 @@ class StepManiaImporter
|
||||
tp.endTimestamp = tp.startTimestamp;
|
||||
tpIndex++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -324,7 +322,7 @@ class StepManiaImporter
|
||||
SampleStart: 0
|
||||
},
|
||||
TimingPoints: [],
|
||||
Stops: [],
|
||||
Stops: [],
|
||||
Difficulties: []
|
||||
};
|
||||
|
||||
@@ -430,7 +428,6 @@ class StepManiaImporter
|
||||
measure.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (workingDiff != null && workingDiff.notes.length > 0)
|
||||
@@ -484,8 +481,8 @@ class StepManiaImporter
|
||||
return (time * 1000) - (offset * 1000); // convert to ms
|
||||
}
|
||||
|
||||
static function convertStepNotes(offset:Float, type:StepManiaChartType, stepNotes:Array<StepNote>,
|
||||
stepTimingPoints:Array<StepTimingPoint>, stepStops:Array<StepStop>):Array<SongNoteData>
|
||||
static function convertStepNotes(offset:Float, type:StepManiaChartType, stepNotes:Array<StepNote>, stepTimingPoints:Array<StepTimingPoint>,
|
||||
stepStops:Array<StepStop>):Array<SongNoteData>
|
||||
{
|
||||
var result:Array<SongNoteData> = [];
|
||||
var holdArray:Array<Float> = [];
|
||||
@@ -539,7 +536,6 @@ class StepManiaImporter
|
||||
result.push(snd);
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,7 +588,8 @@ class StepManiaImporter
|
||||
* @param songData The StepManiaData to migrate.
|
||||
* @return SongChartData The migrated SongChartData.
|
||||
*/
|
||||
public static function migrateChartData(songData:StepManiaData):SongChartData {
|
||||
public static function migrateChartData(songData:StepManiaData):SongChartData
|
||||
{
|
||||
var scrollsMap:Map<String, Float> = new Map<String, Float>();
|
||||
var stepNoteMap:Map<String, Array<SongNoteData>> = new Map<String, Array<SongNoteData>>();
|
||||
|
||||
|
||||
@@ -49,7 +49,9 @@ class SongMetadata_v2_0_0
|
||||
|
||||
public var timeChanges:Array<SongData.SongTimeChange>;
|
||||
|
||||
public function new() {}
|
||||
public function new()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a string representation suitable for debugging.
|
||||
@@ -88,7 +90,9 @@ class SongPlayData_v2_0_0
|
||||
|
||||
public var stage:String;
|
||||
|
||||
public function new() {}
|
||||
public function new()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a string representation suitable for debugging.
|
||||
|
||||
@@ -100,7 +100,9 @@ class SongPlayData_v2_1_0
|
||||
public var characters:SongData.SongCharacterData;
|
||||
public var stage:String;
|
||||
|
||||
public function new() {}
|
||||
public function new()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a string representation suitable for debugging.
|
||||
|
||||
@@ -33,27 +33,24 @@ class StageData
|
||||
function makeDefaultCharacters():StageDataCharacters
|
||||
{
|
||||
return {
|
||||
bf:
|
||||
{
|
||||
zIndex: 0,
|
||||
scale: 1,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [-100, -100]
|
||||
},
|
||||
dad:
|
||||
{
|
||||
zIndex: 0,
|
||||
scale: 1,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [100, -100]
|
||||
},
|
||||
gf:
|
||||
{
|
||||
zIndex: 0,
|
||||
scale: 1,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [0, 0]
|
||||
}
|
||||
bf: {
|
||||
zIndex: 0,
|
||||
scale: 1,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [-100, -100]
|
||||
},
|
||||
dad: {
|
||||
zIndex: 0,
|
||||
scale: 1,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [100, -100]
|
||||
},
|
||||
gf: {
|
||||
zIndex: 0,
|
||||
scale: 1,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [0, 0]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,4 +23,6 @@ class StageRegistry extends BaseRegistry<Stage, StageData, StageEntryParams> imp
|
||||
}
|
||||
}
|
||||
|
||||
typedef StageEntryParams = {}
|
||||
typedef StageEntryParams =
|
||||
{
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class StickerRegistry extends BaseRegistry<StickerPack, StickerData, StickerEntr
|
||||
|
||||
function createScriptedEntry(clsName:String):StickerPack
|
||||
{
|
||||
return ScriptedStickerPack.init(clsName, 'unknown');
|
||||
return ScriptedStickerPack.scriptInit(clsName, 'unknown');
|
||||
}
|
||||
|
||||
function getScriptedClassNames():Array<String>
|
||||
@@ -91,4 +91,6 @@ class StickerRegistry extends BaseRegistry<StickerPack, StickerData, StickerEntr
|
||||
}
|
||||
}
|
||||
|
||||
typedef StickerEntryParams = {}
|
||||
typedef StickerEntryParams =
|
||||
{
|
||||
}
|
||||
|
||||
@@ -31,18 +31,7 @@ class LevelRegistry extends BaseRegistry<Level, LevelData, LevelEntryParams> imp
|
||||
{
|
||||
// This MUST be hard-coded (overriding the auto-generated method)
|
||||
// because the auto-generated method spits out values in alphabetical order.
|
||||
return [
|
||||
'tutorial',
|
||||
'week1',
|
||||
'week2',
|
||||
'week3',
|
||||
'week4',
|
||||
'week5',
|
||||
'week6',
|
||||
'week7',
|
||||
'weekend1',
|
||||
'sserafim'
|
||||
];
|
||||
return ['tutorial', 'week1', 'week2', 'week3', 'week4', 'week5', 'week6', 'week7', 'weekend1', 'sserafim'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,4 +47,6 @@ class LevelRegistry extends BaseRegistry<Level, LevelData, LevelEntryParams> imp
|
||||
}
|
||||
}
|
||||
|
||||
typedef LevelEntryParams = {}
|
||||
typedef LevelEntryParams =
|
||||
{
|
||||
}
|
||||
|
||||
@@ -236,5 +236,7 @@ class IntervalShake implements IFlxDestroyable
|
||||
* Internal constructor. Use static methods.
|
||||
*/
|
||||
@:keep
|
||||
function new() {}
|
||||
function new()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ class RetroCameraFade
|
||||
var steps:Int = 0;
|
||||
var stepsTotal:Int = camSteps;
|
||||
|
||||
new FlxTimer().start(time / stepsTotal, _ -> {
|
||||
new FlxTimer().start(time / stepsTotal, _ ->
|
||||
{
|
||||
var V:Float = (1 / stepsTotal) * steps;
|
||||
if (steps == stepsTotal) V = 1;
|
||||
|
||||
@@ -42,7 +43,8 @@ class RetroCameraFade
|
||||
];
|
||||
camera.filters = [new ColorMatrixFilter(matrixDerp)];
|
||||
|
||||
new FlxTimer().start(time / stepsTotal, _ -> {
|
||||
new FlxTimer().start(time / stepsTotal, _ ->
|
||||
{
|
||||
var V:Float = (1 / stepsTotal) * steps;
|
||||
if (steps == stepsTotal) V = 1;
|
||||
|
||||
@@ -62,7 +64,8 @@ class RetroCameraFade
|
||||
var steps:Int = 0;
|
||||
var stepsTotal:Int = camSteps;
|
||||
|
||||
new FlxTimer().start(time / stepsTotal, _ -> {
|
||||
new FlxTimer().start(time / stepsTotal, _ ->
|
||||
{
|
||||
var V:Float = (1 / stepsTotal) * steps;
|
||||
if (steps == stepsTotal) V = 1;
|
||||
|
||||
@@ -90,7 +93,8 @@ class RetroCameraFade
|
||||
];
|
||||
camera.filters = [new ColorMatrixFilter(matrixDerp)];
|
||||
|
||||
new FlxTimer().start(time / stepsTotal, _ -> {
|
||||
new FlxTimer().start(time / stepsTotal, _ ->
|
||||
{
|
||||
var V:Float = (1 / stepsTotal) * steps;
|
||||
if (steps == stepsTotal) V = 1;
|
||||
|
||||
|
||||
+3
-1
@@ -66,7 +66,9 @@ class CallbackUtil #if (lime >= "8.0.0") implements JNISafety #end
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
private function new() {}
|
||||
private function new()
|
||||
{
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
@:keep
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.graphics;
|
||||
|
||||
import animate.internal.RenderTexture;
|
||||
import flash.geom.ColorTransform;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.graphics.FlxGraphic;
|
||||
@@ -7,22 +8,35 @@ import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.math.FlxMatrix;
|
||||
import flixel.math.FlxRect;
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
import funkin.graphics.shaders.RuntimeCustomBlendShader;
|
||||
import funkin.graphics.framebuffer.BitmapDataUtil;
|
||||
import funkin.graphics.framebuffer.FixedBitmapData;
|
||||
import funkin.graphics.shaders.RuntimeCustomBlendShader;
|
||||
import openfl.display.OpenGLRenderer;
|
||||
import openfl.Lib;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.BlendMode;
|
||||
import openfl.display3D.textures.TextureBase;
|
||||
import openfl.filters.BitmapFilter;
|
||||
import openfl.filters.ShaderFilter;
|
||||
import flixel.graphics.tile.FlxDrawQuadsItem;
|
||||
import flixel.graphics.tile.FlxDrawTrianglesItem;
|
||||
|
||||
using funkin.graphics.framebuffer.BitmapDataUtil;
|
||||
|
||||
/**
|
||||
* A FlxCamera with additional powerful features:
|
||||
* - Grab the camera screen as a `BitmapData` and use it as a texture
|
||||
* - Support `sprite.blend = DARKEN/HARDLIGHT/LIGHTEN/OVERLAY` to apply visual effects using certain sprites
|
||||
* - NOTE: Several other blend modes work without FunkinCamera. Some still do not work.
|
||||
* - NOTE: Framerate-independent camera tweening is fixed in Flixel 6.x. Rest in peace, SwagCamera.
|
||||
* - Added the ability to grab the camera screen as a `BitmapData` and use it as a texture.
|
||||
* - Added support for the following blend modes for a sprite through shaders:
|
||||
* - DARKEN
|
||||
* - HARDLIGHT
|
||||
* - LIGHTEN
|
||||
* - OVERLAY
|
||||
* - DIFFERENCE
|
||||
* - INVERT
|
||||
* - COLORDODGE
|
||||
* - COLORBURN
|
||||
* - SOFTLIGHT
|
||||
* - EXCLUSION
|
||||
* - HUE
|
||||
* - SATURATION
|
||||
* - COLOR
|
||||
* - LUMINOSITY
|
||||
*/
|
||||
@:nullSafety
|
||||
@:access(openfl.display.DisplayObject)
|
||||
@@ -31,183 +45,163 @@ import openfl.filters.ShaderFilter;
|
||||
@:access(openfl.display3D.textures.TextureBase)
|
||||
@:access(flixel.graphics.FlxGraphic)
|
||||
@:access(flixel.graphics.frames.FlxFrame)
|
||||
@:access(openfl.display.OpenGLRenderer)
|
||||
@:access(openfl.geom.ColorTransform)
|
||||
class FunkinCamera extends FlxCamera
|
||||
{
|
||||
final grabbed:Array<BitmapData> = [];
|
||||
final texturePool:Array<TextureBase> = [];
|
||||
/**
|
||||
* Whether or not the device supports the OpenGL extension `KHR_blend_equation_advanced`.
|
||||
* If `false`, a shader implementation will be used to render certain blend modes.
|
||||
*/
|
||||
public static var hasKhronosExtension(get, never):Bool;
|
||||
|
||||
final bgTexture:TextureBase;
|
||||
final bgBitmap:BitmapData;
|
||||
final bgFrame:FlxFrame;
|
||||
static inline function get_hasKhronosExtension():Bool
|
||||
{
|
||||
#if FORCE_BLEND_SHADER
|
||||
return false;
|
||||
#else
|
||||
@:privateAccess
|
||||
return OpenGLRenderer.__complexBlendsSupported ?? false;
|
||||
#end
|
||||
}
|
||||
|
||||
final customBlendShader:RuntimeCustomBlendShader;
|
||||
final customBlendFilter:ShaderFilter;
|
||||
/**
|
||||
* A list of blend modes that require the OpenGL extension `KHR_blend_equation_advanced`.
|
||||
*
|
||||
* NOTE:
|
||||
* - `LIGHTEN` is supported natively on desktop, but not other platforms.
|
||||
* - While `DARKEN` is supported natively on desktop, it causes issues with transparency.
|
||||
*/
|
||||
static final KHR_BLEND_MODES:Array<BlendMode> = [DARKEN, HARDLIGHT, #if !desktop LIGHTEN, #end OVERLAY, DIFFERENCE, COLORDODGE, COLORBURN, SOFTLIGHT, EXCLUSION, HUE, SATURATION, COLOR, LUMINOSITY];
|
||||
|
||||
var filtersApplied:Bool = false;
|
||||
var bgItemCount:Int = 0;
|
||||
/**
|
||||
* A list of blend modes that require the shader no matter what.
|
||||
* This is due to these blend modes not being supported on any platform.
|
||||
*/
|
||||
static final SHADER_REQUIRED_BLEND_MODES:Array<BlendMode> = [INVERT];
|
||||
|
||||
public var shouldDraw:Bool = true;
|
||||
/**
|
||||
* The ID of this camera, used for debugging.
|
||||
*/
|
||||
public var id:String;
|
||||
|
||||
// Used to identify the camera during debugging.
|
||||
final id:String = 'unknown';
|
||||
/**
|
||||
* If `true` the blend shader will try to blend with the cameras underneath it.
|
||||
* This is useful for, say, making a strumline note have a shader-only blend mode like `INVERT`.
|
||||
*
|
||||
* Defaults to `false` since this can impact performance.
|
||||
*/
|
||||
public var crossCameraBlending:Bool;
|
||||
|
||||
var _blendShader:RuntimeCustomBlendShader;
|
||||
var _backgroundFrame:FlxFrame;
|
||||
|
||||
var _blendRenderTexture:RenderTexture;
|
||||
var _backgroundRenderTexture:RenderTexture;
|
||||
|
||||
var _cameraTexture:FixedBitmapData;
|
||||
var _cameraMatrix:FlxMatrix;
|
||||
|
||||
@:nullSafety(Off)
|
||||
public function new(id:String = 'unknown', x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, zoom:Float = 0)
|
||||
{
|
||||
super(x, y, width, height, zoom);
|
||||
|
||||
this.id = id;
|
||||
bgTexture = @:nullSafety(Off) pickTexture(width, height);
|
||||
bgBitmap = FixedBitmapData.fromTexture(bgTexture);
|
||||
bgFrame = new FlxFrame(new FlxGraphic('', null));
|
||||
bgFrame.parent.bitmap = bgBitmap;
|
||||
bgFrame.frame = new FlxRect();
|
||||
customBlendShader = new RuntimeCustomBlendShader();
|
||||
customBlendFilter = new ShaderFilter(customBlendShader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs the camera screen and returns it as a `BitmapData`. The returned bitmap
|
||||
* will not be referred by the camera so, changing it will not affect the scene.
|
||||
* The returned bitmap **will be reused in the next frame**, so the content is available
|
||||
* only in the current frame.
|
||||
* @param applyFilters if this is `true`, the camera's filters will be applied to the grabbed bitmap,
|
||||
* and the camera's filters will be disabled until the beginning of the next frame
|
||||
* @param isolate if this is `true`, sprites to be rendered will only be rendered to the grabbed bitmap,
|
||||
* and the grabbed bitmap will not include any previously rendered sprites
|
||||
* @return the grabbed bitmap data
|
||||
*/
|
||||
public function grabScreen(applyFilters:Bool, isolate:Bool = false):Null<BitmapData>
|
||||
{
|
||||
final texture = pickTexture(width, height);
|
||||
final bitmap = FixedBitmapData.fromTexture(texture);
|
||||
if (bitmap != null)
|
||||
{
|
||||
squashTo(bitmap, applyFilters, isolate);
|
||||
grabbed.push(bitmap);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
_backgroundFrame = new FlxFrame(new FlxGraphic('', null));
|
||||
_backgroundFrame.frame = new FlxRect();
|
||||
|
||||
/**
|
||||
* Applies the filter immediately to the camera. This will be done independently from
|
||||
* the camera's filters. This method can only be called after the first `grabScreen`
|
||||
* in the frame.
|
||||
* @param filter the filter
|
||||
*/
|
||||
public function applyFilter(filter:BitmapFilter):Void
|
||||
{
|
||||
if (grabbed.length == 0)
|
||||
{
|
||||
FlxG.log.error('grab screen before you can apply a filter!');
|
||||
return;
|
||||
}
|
||||
BitmapDataUtil.applyFilter(bgBitmap, filter);
|
||||
}
|
||||
_blendShader = new RuntimeCustomBlendShader();
|
||||
|
||||
function squashTo(bitmap:BitmapData, applyFilters:Bool, isolate:Bool, clearScreen:Bool = false):Void
|
||||
{
|
||||
if (applyFilters && isolate)
|
||||
{
|
||||
FlxG.log.error('cannot apply filters while isolating!');
|
||||
}
|
||||
if (filtersApplied && applyFilters)
|
||||
{
|
||||
FlxG.log.warn('filters already applied!');
|
||||
}
|
||||
static final matrix = new FlxMatrix();
|
||||
_backgroundRenderTexture = new RenderTexture(this.width, this.height);
|
||||
_blendRenderTexture = new RenderTexture(this.width, this.height);
|
||||
|
||||
// resize the background bitmap if needed
|
||||
if (bgTexture.__width != width || bgTexture.__height != height)
|
||||
{
|
||||
BitmapDataUtil.resizeTexture(bgTexture, width, height);
|
||||
bgBitmap.__resize(width, height);
|
||||
bgFrame.parent.bitmap = bgBitmap;
|
||||
}
|
||||
_cameraMatrix = new FlxMatrix();
|
||||
_cameraTexture = FixedBitmapData.create(this.width, this.height);
|
||||
|
||||
// grab the bitmap
|
||||
renderSkipping(isolate ? bgItemCount : 0);
|
||||
bitmap.fillRect(bitmap.rect, 0);
|
||||
matrix.setTo(1, 0, 0, 1, flashSprite.x, flashSprite.y);
|
||||
if (applyFilters)
|
||||
{
|
||||
bitmap.draw(flashSprite, matrix);
|
||||
@:nullSafety(Off) // TODO: Remove this once openfl.display.Sprite has been null safed.
|
||||
flashSprite.filters = null;
|
||||
filtersApplied = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
final tmp = flashSprite.filters;
|
||||
@:nullSafety(Off)
|
||||
flashSprite.filters = null;
|
||||
bitmap.draw(flashSprite, matrix);
|
||||
flashSprite.filters = tmp;
|
||||
}
|
||||
|
||||
if (!isolate)
|
||||
{
|
||||
// also copy to the background bitmap
|
||||
bgBitmap.fillRect(bgBitmap.rect, 0);
|
||||
bgBitmap.draw(bitmap);
|
||||
}
|
||||
|
||||
if (clearScreen)
|
||||
{
|
||||
// clear graphics data
|
||||
super.clearDrawStack();
|
||||
canvas.graphics.clear();
|
||||
}
|
||||
|
||||
// render the background bitmap
|
||||
bgFrame.frame.set(0, 0, width, height);
|
||||
matrix.setTo(viewWidth / width, 0, 0, viewHeight / height, viewMarginLeft, viewMarginTop);
|
||||
drawPixels(bgFrame, matrix);
|
||||
|
||||
// count background draw items for future isolation
|
||||
bgItemCount = 0;
|
||||
{
|
||||
var item = _headOfDrawStack;
|
||||
while (item != null)
|
||||
{
|
||||
item = item.next;
|
||||
bgItemCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderSkipping(count:Int):Void
|
||||
{
|
||||
var item = _headOfDrawStack;
|
||||
while (item != null)
|
||||
{
|
||||
if (--count < 0) item.render(this);
|
||||
item = item.next;
|
||||
}
|
||||
crossCameraBlending = false;
|
||||
}
|
||||
|
||||
override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false,
|
||||
?shader:FlxShader):Void
|
||||
{
|
||||
if (!shouldDraw) return;
|
||||
var shouldUseShader:Bool = (!hasKhronosExtension && KHR_BLEND_MODES.contains(blend)) || SHADER_REQUIRED_BLEND_MODES.contains(blend);
|
||||
|
||||
if ( switch blend
|
||||
{
|
||||
case DARKEN | HARDLIGHT | LIGHTEN | OVERLAY: true;
|
||||
case _: false;
|
||||
})
|
||||
// Fallback to the shader implementation if the device doesn't support `KHR_blend_equation_advanced`, or if
|
||||
// the specified blend mode requires the shader.
|
||||
if (shouldUseShader)
|
||||
{
|
||||
// squash the screen
|
||||
grabScreen(false);
|
||||
// render without blend
|
||||
super.drawPixels(frame, pixels, matrix, transform, null, smoothing, shader);
|
||||
// get the isolated bitmap
|
||||
final isolated = grabScreen(false, true);
|
||||
// apply fullscreen blend
|
||||
customBlendShader.blendSwag = blend;
|
||||
@:nullSafety(Off) // I hope this doesn't cause issues
|
||||
customBlendShader.sourceSwag = isolated;
|
||||
customBlendShader.updateViewInfo(FlxG.width, FlxG.height, this);
|
||||
applyFilter(customBlendFilter);
|
||||
if (crossCameraBlending)
|
||||
{
|
||||
var camerasUnderneath:Array<FlxCamera> = FlxG.cameras.list.copy();
|
||||
|
||||
for (i in camerasUnderneath.length - 1...-1)
|
||||
{
|
||||
if (i > FlxG.cameras.list.indexOf(this))
|
||||
{
|
||||
camerasUnderneath.remove(camerasUnderneath[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_cameraTexture.drawCameraScreens(camerasUnderneath);
|
||||
|
||||
for (camera in camerasUnderneath)
|
||||
{
|
||||
camera.clearDrawStack();
|
||||
camera.canvas.graphics.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_cameraTexture.drawCameraScreen(this);
|
||||
}
|
||||
|
||||
_backgroundFrame.frame.set(0, 0, this.width, this.height);
|
||||
|
||||
// Clear the camera's graphics
|
||||
// It'll get redrawn anyway
|
||||
this.clearDrawStack();
|
||||
this.canvas.graphics.clear();
|
||||
|
||||
_blendRenderTexture.init(this.width, this.height);
|
||||
_blendRenderTexture.drawToCamera((camera, frameMatrix) ->
|
||||
{
|
||||
var pivotX:Float = width / 2;
|
||||
var pivotY:Float = height / 2;
|
||||
|
||||
frameMatrix.copyFrom(matrix);
|
||||
frameMatrix.translate(-pivotX, -pivotY);
|
||||
frameMatrix.scale(this.scaleX, this.scaleY);
|
||||
frameMatrix.translate(pivotX, pivotY);
|
||||
camera.drawPixels(frame, pixels, frameMatrix, transform, null, smoothing, shader);
|
||||
});
|
||||
_blendRenderTexture.render();
|
||||
|
||||
_blendShader.sourceSwag = _blendRenderTexture.graphic.bitmap;
|
||||
_blendShader.backgroundSwag = _cameraTexture;
|
||||
|
||||
_blendShader.blendSwag = blend;
|
||||
_blendShader.updateViewInfo(width, height, this);
|
||||
|
||||
_backgroundFrame.parent.bitmap = _blendRenderTexture.graphic.bitmap;
|
||||
|
||||
_backgroundRenderTexture.init(Std.int(this.width * Lib.current.stage.window.scale), Std.int(this.height * Lib.current.stage.window.scale));
|
||||
_backgroundRenderTexture.drawToCamera((camera, matrix) ->
|
||||
{
|
||||
camera.zoom = this.zoom;
|
||||
matrix.scale(Lib.current.stage.window.scale, Lib.current.stage.window.scale);
|
||||
camera.drawPixels(_backgroundFrame, null, matrix, canvas.transform.colorTransform, null, false, _blendShader);
|
||||
});
|
||||
|
||||
_backgroundRenderTexture.render();
|
||||
|
||||
// Resize the frame so it always fills the screen
|
||||
_cameraMatrix.identity();
|
||||
_cameraMatrix.scale(1 / (this.scaleX * Lib.current.stage.window.scale), 1 / (this.scaleY * Lib.current.stage.window.scale));
|
||||
_cameraMatrix.translate(((width - width / this.scaleX) * 0.5), ((height - height / this.scaleY) * 0.5));
|
||||
|
||||
super.drawPixels(_backgroundRenderTexture.graphic.imageFrame.frame, null, _cameraMatrix, null, null, smoothing, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -215,56 +209,76 @@ class FunkinCamera extends FlxCamera
|
||||
}
|
||||
}
|
||||
|
||||
override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false,
|
||||
?shader:FlxShader):FlxDrawQuadsItem
|
||||
{
|
||||
// Can't batch complex non-coherent blends, so always force a new batch
|
||||
if (hasKhronosExtension && !(OpenGLRenderer.__coherentBlendsSupported ?? false) && KHR_BLEND_MODES.contains(blend))
|
||||
{
|
||||
var itemToReturn = null;
|
||||
|
||||
if (FlxCamera._storageTilesHead != null)
|
||||
{
|
||||
itemToReturn = FlxCamera._storageTilesHead;
|
||||
var newHead = FlxCamera._storageTilesHead.nextTyped;
|
||||
itemToReturn.reset();
|
||||
FlxCamera._storageTilesHead = newHead;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemToReturn = new FlxDrawQuadsItem();
|
||||
}
|
||||
|
||||
// TODO: catch this error when the dev actually messes up, not in the draw phase
|
||||
if (graphic.isDestroyed) throw 'Cannot queue ${graphic.key}. This sprite was destroyed.';
|
||||
|
||||
itemToReturn.graphics = graphic;
|
||||
itemToReturn.antialiasing = smooth;
|
||||
itemToReturn.colored = colored;
|
||||
itemToReturn.hasColorOffsets = hasColorOffsets;
|
||||
itemToReturn.blend = blend;
|
||||
@:nullSafety(Off)
|
||||
itemToReturn.shader = shader;
|
||||
|
||||
itemToReturn.nextTyped = _headTiles;
|
||||
_headTiles = itemToReturn;
|
||||
|
||||
if (_headOfDrawStack == null)
|
||||
{
|
||||
_headOfDrawStack = itemToReturn;
|
||||
}
|
||||
|
||||
if (_currentDrawItem != null)
|
||||
{
|
||||
_currentDrawItem.next = itemToReturn;
|
||||
}
|
||||
|
||||
_currentDrawItem = itemToReturn;
|
||||
|
||||
return itemToReturn;
|
||||
}
|
||||
|
||||
return super.startQuadBatch(graphic, colored, hasColorOffsets, blend, smooth, shader);
|
||||
}
|
||||
|
||||
override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool,
|
||||
?shader:FlxShader):FlxDrawTrianglesItem
|
||||
{
|
||||
// Can't batch complex non-coherent blends, so always force a new batch
|
||||
if (hasKhronosExtension
|
||||
&& !(OpenGLRenderer.__coherentBlendsSupported ?? false)
|
||||
&& KHR_BLEND_MODES.contains(blend)) return getNewDrawTrianglesItem(graphic, smoothing, isColored, blend, hasColorOffsets, shader);
|
||||
|
||||
return super.startTrianglesBatch(graphic, smoothing, isColored, blend, hasColorOffsets, shader);
|
||||
}
|
||||
|
||||
override function destroy():Void
|
||||
{
|
||||
super.destroy();
|
||||
disposeTextures();
|
||||
}
|
||||
|
||||
override function clearDrawStack():Void
|
||||
{
|
||||
super.clearDrawStack();
|
||||
// also clear grabbed bitmaps
|
||||
for (bitmap in grabbed)
|
||||
{
|
||||
texturePool.push(bitmap.__texture);
|
||||
bitmap.dispose(); // this doesn't release the texture
|
||||
}
|
||||
grabbed.clear();
|
||||
// clear filters applied flag
|
||||
filtersApplied = false;
|
||||
bgItemCount = 0;
|
||||
}
|
||||
_blendRenderTexture.destroy();
|
||||
_backgroundRenderTexture.destroy();
|
||||
|
||||
function pickTexture(width:Int, height:Int):Null<TextureBase>
|
||||
{
|
||||
// zero-sized textures will be problematic
|
||||
width = width < 1 ? 1 : width;
|
||||
height = height < 1 ? 1 : height;
|
||||
if (texturePool.length > 0)
|
||||
{
|
||||
final res = texturePool.pop();
|
||||
if (res != null) BitmapDataUtil.resizeTexture(res, width, height);
|
||||
else
|
||||
trace('huh? why is this null? $texturePool');
|
||||
return res;
|
||||
}
|
||||
return Lib.current.stage.context3D.createTexture(width, height, BGRA, true);
|
||||
}
|
||||
|
||||
function disposeTextures():Void
|
||||
{
|
||||
for (bitmap in grabbed)
|
||||
{
|
||||
bitmap.dispose();
|
||||
}
|
||||
grabbed.clear();
|
||||
for (texture in texturePool)
|
||||
{
|
||||
texture.dispose();
|
||||
}
|
||||
texturePool.resize(0);
|
||||
bgTexture.dispose();
|
||||
bgBitmap.dispose();
|
||||
_cameraTexture.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package funkin.graphics;
|
||||
|
||||
import flixel.FlxCamera;
|
||||
import flixel.system.frontEnds.CameraFrontEnd;
|
||||
|
||||
/**
|
||||
* A `CameraFrontEnd` override that uses `FunkinCamera`!
|
||||
*/
|
||||
@:nullSafety
|
||||
class FunkinCameraFrontEnd extends CameraFrontEnd
|
||||
{
|
||||
public override function reset(?newCamera:FlxCamera):Void
|
||||
{
|
||||
super.reset(newCamera ?? new FunkinCamera());
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,7 @@ typedef AtlasSpriteSettings =
|
||||
* - TODO: Better cache handling for textures.
|
||||
*/
|
||||
@:nullSafety
|
||||
@:access(animate.FlxAnimateController)
|
||||
class FunkinSprite extends FlxAnimate
|
||||
{
|
||||
/**
|
||||
@@ -255,7 +256,8 @@ class FunkinSprite extends FlxAnimate
|
||||
trace('[ASYNC] Start loading image (${key})');
|
||||
graphic.persist = true;
|
||||
openfl.Assets.loadBitmapData(key)
|
||||
.onComplete(function(bitmapData:openfl.display.BitmapData) {
|
||||
.onComplete(function(bitmapData:openfl.display.BitmapData)
|
||||
{
|
||||
trace('[ASYNC] Finished loading image');
|
||||
var cache:Bool = false;
|
||||
loadBitmapData(bitmapData, cache);
|
||||
@@ -266,7 +268,8 @@ class FunkinSprite extends FlxAnimate
|
||||
FlxTween.tween(this, {alpha: 1.0}, 0.25);
|
||||
}
|
||||
})
|
||||
.onError(function(error:Dynamic) {
|
||||
.onError(function(error:Dynamic)
|
||||
{
|
||||
trace('[ASYNC] Failed to load image: ${error}');
|
||||
if (fadeTween != null)
|
||||
{
|
||||
@@ -274,7 +277,8 @@ class FunkinSprite extends FlxAnimate
|
||||
this.alpha = 1.0;
|
||||
}
|
||||
})
|
||||
.onProgress(function(progress:Int, total:Int) {
|
||||
.onProgress(function(progress:Int, total:Int)
|
||||
{
|
||||
trace('[ASYNC] Loading image progress: ${progress}/${total}');
|
||||
});
|
||||
}
|
||||
@@ -397,7 +401,7 @@ class FunkinSprite extends FlxAnimate
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.isAnimate && !animationList.contains(id))
|
||||
else if (this.anim.hasAnimateAtlas && !animationList.contains(id))
|
||||
{
|
||||
return addAnimationIfMissing(id);
|
||||
}
|
||||
@@ -438,9 +442,9 @@ class FunkinSprite extends FlxAnimate
|
||||
*/
|
||||
public function getFramesWithKeyword(keyword:String):Array<animate.internal.Frame>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
if (!this.anim.hasAnimateAtlas)
|
||||
{
|
||||
trace('WARNING: getFramesWithKeyword() only works texture atlases!');
|
||||
trace('WARNING: getFramesWithKeyword() only works on texture atlases!');
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -461,8 +465,10 @@ class FunkinSprite extends FlxAnimate
|
||||
|
||||
for (symbolItem in symbolItems)
|
||||
{
|
||||
symbolItem.timeline.forEachLayer((layer) -> {
|
||||
layer.forEachFrame((frame) -> {
|
||||
symbolItem.timeline.forEachLayer((layer) ->
|
||||
{
|
||||
layer.forEachFrame((frame) ->
|
||||
{
|
||||
frames.push(frame);
|
||||
});
|
||||
});
|
||||
@@ -528,9 +534,9 @@ class FunkinSprite extends FlxAnimate
|
||||
*/
|
||||
public function getFrameLabelList():Array<String>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
if (!this.anim.hasAnimateAtlas)
|
||||
{
|
||||
trace('WARNING: getFrameLabelList() only works texture atlases!');
|
||||
trace('WARNING: getFrameLabelList() only works on texture atlases!');
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -557,15 +563,15 @@ class FunkinSprite extends FlxAnimate
|
||||
* @param name The name of the frame label to retrieve.
|
||||
* @return The frame label, or null if it doesn't exist.
|
||||
*/
|
||||
public function getFrameLabel(name:String):Null<animate.internal.Frame>
|
||||
public function getFrameLabel(name:String, ?timeline:animate.internal.Timeline):Null<animate.internal.Frame>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
if (!this.anim.hasAnimateAtlas)
|
||||
{
|
||||
trace('WARNING: getFrameLabel() only works texture atlases!');
|
||||
trace('WARNING: getFrameLabel() only works on texture atlases!');
|
||||
return null;
|
||||
}
|
||||
|
||||
for (layer in this.timeline.layers)
|
||||
for (layer in (timeline ?? this.timeline).layers)
|
||||
{
|
||||
@:nullSafety(Off)
|
||||
for (frame in layer.frames)
|
||||
@@ -576,6 +582,7 @@ class FunkinSprite extends FlxAnimate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -584,9 +591,9 @@ class FunkinSprite extends FlxAnimate
|
||||
*/
|
||||
public function getDefaultSymbol():String
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
if (!this.anim.hasAnimateAtlas)
|
||||
{
|
||||
trace('WARNING: getDefaultSymbol() only works texture atlases!');
|
||||
trace('WARNING: getDefaultSymbol() only works on texture atlases!');
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -601,9 +608,9 @@ class FunkinSprite extends FlxAnimate
|
||||
*/
|
||||
public function replaceSymbolGraphic(symbol:String, ?graphic:Null<FlxGraphicAsset>, ?adjustScale:Bool = true):Void
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
if (!this.anim.hasAnimateAtlas)
|
||||
{
|
||||
trace('WARNING: replaceSymbolGraphic() only works texture atlases!');
|
||||
trace('WARNING: replaceSymbolGraphic() only works on texture atlases!');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -626,9 +633,9 @@ class FunkinSprite extends FlxAnimate
|
||||
*/
|
||||
public function getFirstElement(symbol:String):Null<Element>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
if (!this.anim.hasAnimateAtlas)
|
||||
{
|
||||
trace('WARNING: getFirstElement() only works texture atlases!');
|
||||
trace('WARNING: getFirstElement() only works on texture atlases!');
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -642,9 +649,9 @@ class FunkinSprite extends FlxAnimate
|
||||
*/
|
||||
public function getSymbolElements(symbol:String):Array<Element>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
if (!this.anim.hasAnimateAtlas)
|
||||
{
|
||||
trace('WARNING: getSymbolElements() only works texture atlases!');
|
||||
trace('WARNING: getSymbolElements() only works on texture atlases!');
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -675,9 +682,9 @@ class FunkinSprite extends FlxAnimate
|
||||
*/
|
||||
public function scaleElement(element:Element, scale:Float, positionOffset:Float = 0, scaleEverything:Bool = false):Void
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
if (!this.anim.hasAnimateAtlas)
|
||||
{
|
||||
trace('WARNING: scaleElement() only works texture atlases!');
|
||||
trace('WARNING: scaleElement() only works on texture atlases!');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,4 +5,6 @@ package funkin.graphics;
|
||||
* Create a scripted class that extends FunkinSprite to use this.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedFunkinSprite extends funkin.graphics.FunkinSprite implements polymod.hscript.HScriptedClass {}
|
||||
class ScriptedFunkinSprite extends funkin.graphics.FunkinSprite implements polymod.hscript.HScriptedClass
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,126 +1,187 @@
|
||||
package funkin.graphics.framebuffer;
|
||||
|
||||
import flixel.FlxG;
|
||||
import openfl.Lib;
|
||||
import openfl.display.Bitmap;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.display3D.Context3DTextureFormat;
|
||||
import openfl.display3D.Context3D;
|
||||
import openfl.display3D.textures.TextureBase;
|
||||
import openfl.filters.BitmapFilter;
|
||||
import animate.internal.FilterRenderer;
|
||||
import flixel.math.FlxMatrix;
|
||||
import openfl.display.OpenGLRenderer;
|
||||
import flixel.FlxCamera;
|
||||
import openfl.Lib;
|
||||
import openfl.geom.Matrix;
|
||||
import openfl.geom.ColorTransform;
|
||||
|
||||
/**
|
||||
* Provides cool stuff for `BitmapData`s that have a hardware texture internally.
|
||||
* A utility class for `BitmapData`s.
|
||||
*/
|
||||
@:nullSafety
|
||||
@:access(openfl.display.BitmapData)
|
||||
@:access(openfl.display3D.textures.TextureBase)
|
||||
@:access(openfl.display3D.Context3D)
|
||||
@:access(openfl.display.OpenGLRenderer)
|
||||
@:access(flixel.FlxCamera)
|
||||
@:access(openfl.display.Sprite)
|
||||
@:access(openfl.geom.ColorTransform)
|
||||
class BitmapDataUtil
|
||||
{
|
||||
static function getCache():{sprite:Sprite, bitmap:Bitmap}
|
||||
static var renderer(get, never):OpenGLRenderer;
|
||||
static var _renderer:Null<OpenGLRenderer>;
|
||||
|
||||
static inline function get_renderer():OpenGLRenderer
|
||||
{
|
||||
static var cache:Null<{sprite:Sprite, bitmap:Bitmap}> = null;
|
||||
if (cache == null)
|
||||
if (_renderer == null)
|
||||
{
|
||||
final sprite = new Sprite();
|
||||
final bitmap = new Bitmap();
|
||||
sprite.addChild(bitmap);
|
||||
cache =
|
||||
{
|
||||
sprite: sprite,
|
||||
bitmap: bitmap
|
||||
}
|
||||
_renderer = new OpenGLRenderer(FlxG.stage.context3D);
|
||||
_renderer.__worldTransform = new Matrix();
|
||||
_renderer.__worldColorTransform = new ColorTransform();
|
||||
}
|
||||
return cache;
|
||||
|
||||
return _renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a bitmap filter to a bitmap immediately. The bitmap filter may refer
|
||||
* the bitmap itself as a shader input.
|
||||
* @param bitmap the bitmap data
|
||||
* @param filter the bitmap filter
|
||||
* Draws the contents of multiple cameras onto a `BitmapData` object.
|
||||
*
|
||||
* @param bitmap The bitmap to draw onto.
|
||||
* @param cameras The cameras to grab the screens from.
|
||||
*
|
||||
* @return The combined camera screens as a `BitmapData`.
|
||||
*/
|
||||
public static function applyFilter(bitmap:BitmapData, filter:BitmapFilter):Void
|
||||
public static function drawCameraScreens(bitmap:BitmapData, cameras:Array<FlxCamera>):BitmapData
|
||||
{
|
||||
hardwareCheck(bitmap);
|
||||
final cache = getCache();
|
||||
cache.bitmap.bitmapData = bitmap;
|
||||
cache.sprite.filters = [filter];
|
||||
bitmap.draw(cache.sprite);
|
||||
bitmap.__fillRect(bitmap.rect, 0, true);
|
||||
|
||||
for (camera in cameras)
|
||||
{
|
||||
if (camera.filters != null && camera.filters.length > 0)
|
||||
{
|
||||
drawCameraScreen(bitmap, camera, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawCameraScreen(bitmap, camera, false);
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bitmap with a hardware texture.
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param format the format if the internal texture
|
||||
* @return the bitmap
|
||||
* Draws the contents of a camera onto a `BitmapData` object.
|
||||
*
|
||||
* Mostly copied from flixel-animate's `RenderTexture`
|
||||
* Shoutouts to ACrazyTown and MaybeMaru this is some crazy work
|
||||
* https://github.com/MaybeMaru/flixel-animate/blob/main/src/animate/internal/RenderTexture.hx
|
||||
*
|
||||
* @param bitmap The bitmap to draw onto.
|
||||
* @param camera The camera to grab the screen from.
|
||||
* @param clearBitmap Whether to clear the bitmap before drawing.
|
||||
* @param drawFlashSprite Whether to draw the camera's flash sprite instead of the canvas.
|
||||
*
|
||||
* @return The camera screen as a `BitmapData`.
|
||||
*/
|
||||
public static function create(width:Int, height:Int, format:Context3DTextureFormat = BGRA):Null<FixedBitmapData>
|
||||
public static function drawCameraScreen(bitmap:BitmapData, camera:FlxCamera, clearBitmap:Bool = true, drawFlashSprite:Bool = false):BitmapData
|
||||
{
|
||||
final texture = Lib.current.stage.context3D.createTexture(width, height, format, true);
|
||||
return FixedBitmapData.fromTexture(texture);
|
||||
var matrix:FlxMatrix = new FlxMatrix();
|
||||
var pivotX:Float = FlxG.scaleMode.scale.x;
|
||||
var pivotY:Float = FlxG.scaleMode.scale.y;
|
||||
|
||||
matrix.setTo(1 / pivotX, 0, 0, 1 / pivotY, camera.flashSprite.x / pivotX, camera.flashSprite.y / pivotY);
|
||||
|
||||
if (clearBitmap) bitmap.__fillRect(bitmap.rect, 0, true);
|
||||
|
||||
camera.render();
|
||||
camera.flashSprite.__update(false, true);
|
||||
|
||||
renderer.__cleanup();
|
||||
|
||||
renderer.setShader(renderer.__defaultShader);
|
||||
renderer.__allowSmoothing = false;
|
||||
renderer.__pixelRatio = Lib.current.stage.window.scale;
|
||||
renderer.__worldAlpha = 1 / camera.flashSprite.__worldAlpha;
|
||||
renderer.__worldTransform.copyFrom(camera.flashSprite.__renderTransform);
|
||||
renderer.__worldTransform.invert();
|
||||
renderer.__worldTransform.concat(matrix);
|
||||
renderer.__worldColorTransform.__copyFrom(camera.flashSprite.__worldColorTransform);
|
||||
renderer.__worldColorTransform.__invert();
|
||||
renderer.__setRenderTarget(bitmap);
|
||||
|
||||
if (drawFlashSprite)
|
||||
{
|
||||
bitmap.__drawGL(camera.flashSprite, renderer);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap.__drawGL(camera.canvas, renderer);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a `BitmapFilter` to a bitmap.
|
||||
* @param bitmap The `BitmapData` to apply the filter to.
|
||||
* @param filter The filter to apply.
|
||||
*
|
||||
* @return The `BitmapData` with the filter applied.
|
||||
*/
|
||||
public static function applyFilter(bitmap:BitmapData, filter:BitmapFilter):BitmapData
|
||||
{
|
||||
return FilterRenderer.applyFilter(null, bitmap, [filter]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the bitmap.
|
||||
* @param bitmap the bitmap data
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param bitmap The `BitmapData` to resize.
|
||||
* @param width The new width.
|
||||
* @param height The new height.
|
||||
*/
|
||||
public static function resize(bitmap:BitmapData, width:Int, height:Int):Void
|
||||
{
|
||||
hardwareCheck(bitmap);
|
||||
if (bitmap.width == width && bitmap.height == height) return;
|
||||
|
||||
bitmap.width = width;
|
||||
bitmap.height = height;
|
||||
resizeTexture(bitmap.__texture, width, height);
|
||||
|
||||
if (!bitmap.readable)
|
||||
{
|
||||
resizeTexture(bitmap.__texture, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the texture.
|
||||
* @param texture the texture
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* Resizes the hardware texture.
|
||||
* @param texture The `TextureBase` to resize.
|
||||
* @param width The new width.
|
||||
* @param height The new height.
|
||||
*/
|
||||
@:nullSafety(Off) // the final context there is causing an error, idk how to fix it
|
||||
public static function resizeTexture(texture:TextureBase, width:Int, height:Int):Void
|
||||
{
|
||||
if (texture.__width == width && texture.__height == height) return;
|
||||
|
||||
var context:Context3D = texture.__context;
|
||||
|
||||
texture.__width = width;
|
||||
texture.__height = height;
|
||||
final context = texture.__context;
|
||||
final gl = context.gl;
|
||||
|
||||
context.__bindGLTexture2D(texture.__textureID);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, texture.__internalFormat, width, height, 0, texture.__format, gl.UNSIGNED_BYTE, null);
|
||||
context.gl.texImage2D(context.gl.TEXTURE_2D, 0, texture.__internalFormat, width, height, 0, texture.__format, context.gl.UNSIGNED_BYTE, null);
|
||||
|
||||
@:nullSafety(Off)
|
||||
context.__bindGLTexture2D(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the content of `src` to `dst`. The destination bitmap `dst` will be resized
|
||||
* so that it has the same size as `src`.
|
||||
* @param dst the destination bitmap
|
||||
* @param src the source bitmap
|
||||
* Copies the contents of `source` to `destination`. `destination` bitmap will be resized
|
||||
* so that it has the same size as `source`.
|
||||
* @param source The source `BitmapData`.
|
||||
* @param destination The destination `BitmapData`.
|
||||
*/
|
||||
@:nullSafety(Off) // TODO: Remove this once openfl.display.Sprite has been null safed.
|
||||
public static function copy(dst:BitmapData, src:BitmapData):Void
|
||||
public static function copy(source:BitmapData, destination:BitmapData):Void
|
||||
{
|
||||
hardwareCheck(dst);
|
||||
hardwareCheck(src);
|
||||
final cache = getCache();
|
||||
cache.bitmap.bitmapData = src;
|
||||
cache.sprite.filters = null;
|
||||
resize(dst, src.width, src.height);
|
||||
dst.fillRect(dst.rect, 0);
|
||||
dst.draw(cache.sprite);
|
||||
}
|
||||
|
||||
static function hardwareCheck(bitmap:BitmapData):Void
|
||||
{
|
||||
if (bitmap.readable)
|
||||
{
|
||||
FlxG.log.error('do not use `BitmapDataUtil` for non-GPU bitmaps!');
|
||||
}
|
||||
resize(destination, source.width, source.height);
|
||||
destination.fillRect(destination.rect, 0);
|
||||
destination.draw(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import openfl.display.DisplayObjectContainer;
|
||||
import openfl.display.IBitmapDrawable;
|
||||
import openfl.display.OpenGLRenderer;
|
||||
import openfl.display3D.textures.TextureBase;
|
||||
import openfl.Lib;
|
||||
|
||||
/**
|
||||
* `BitmapData` is kinda broken so I fixed it.
|
||||
@@ -26,18 +27,47 @@ class FixedBitmapData extends BitmapData
|
||||
}
|
||||
|
||||
/**
|
||||
* Never use `BitmapData.fromTexture`, always use this.
|
||||
* @param texture the texture
|
||||
* @return the bitmap data
|
||||
* Creates a `FixedBitmapData` with the given dimensions.
|
||||
* @param width The width of the bitmap
|
||||
* @param height The height of the bitmap
|
||||
* @param useGPU Whether or not this bitmap should use a hardware texture
|
||||
* @return The newly created `FixedBitmapData`
|
||||
*/
|
||||
public static function fromTexture(texture:Null<TextureBase>):Null<FixedBitmapData>
|
||||
public static function create(width:Int, height:Int, useGPU:Bool = true):FixedBitmapData
|
||||
{
|
||||
if (texture == null) return null;
|
||||
final bitmapData:FixedBitmapData = new FixedBitmapData(texture.__width, texture.__height, true, 0);
|
||||
// bitmapData.readable = false;
|
||||
if (useGPU)
|
||||
{
|
||||
var texture:TextureBase = _createTexture(width, height);
|
||||
return fromTexture(texture);
|
||||
}
|
||||
|
||||
return new FixedBitmapData(width, height, true, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a `FixedBitmapData` from a hardware texture.
|
||||
* @param texture The texture
|
||||
* @return The newly created `FixedBitmapData`
|
||||
*/
|
||||
public static function fromTexture(texture:TextureBase):FixedBitmapData
|
||||
{
|
||||
var bitmapData:FixedBitmapData = new FixedBitmapData(texture.__width, texture.__height, true, 0);
|
||||
bitmapData.readable = false;
|
||||
bitmapData.__texture = texture;
|
||||
bitmapData.__textureContext = texture.__textureContext;
|
||||
// bitmapData.image = null;
|
||||
|
||||
@:nullSafety(Off)
|
||||
bitmapData.image = null;
|
||||
|
||||
return bitmapData;
|
||||
}
|
||||
|
||||
static function _createTexture(width:Int, height:Int):TextureBase
|
||||
{
|
||||
// Zero-sized textures will be problematic.
|
||||
width = width < 1 ? 1 : width;
|
||||
height = height < 1 ? 1 : height;
|
||||
|
||||
return Lib.current.stage.context3D.createTexture(width, height, BGRA, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,8 @@ class DropShadowShader extends FlxShader
|
||||
public function loadAltMask(path:String):Void
|
||||
{
|
||||
#if html5
|
||||
BitmapData.loadFromFile(path).onComplete(function(bmp:BitmapData) {
|
||||
BitmapData.loadFromFile(path).onComplete(function(bmp:BitmapData)
|
||||
{
|
||||
altMaskImage = bmp;
|
||||
});
|
||||
#else
|
||||
|
||||
@@ -14,6 +14,14 @@ class RuntimeCustomBlendShader extends RuntimePostEffectShader
|
||||
return sourceSwag = value;
|
||||
}
|
||||
|
||||
public var backgroundSwag(default, set):BitmapData;
|
||||
|
||||
function set_backgroundSwag(value:BitmapData):BitmapData
|
||||
{
|
||||
this.setBitmapData("backgroundSwag", value);
|
||||
return backgroundSwag = value;
|
||||
}
|
||||
|
||||
// name change make sure it's not the same variable name as whatever is in the shader file
|
||||
public var blendSwag(default, set):BlendMode;
|
||||
|
||||
|
||||
@@ -133,14 +133,12 @@ class RuntimeRainShader extends RuntimePostEffectShader
|
||||
super.__processGLData(source, storageType);
|
||||
if (storageType == 'uniform')
|
||||
{
|
||||
lights = [
|
||||
for (i in 0...MAX_LIGHTS)
|
||||
{
|
||||
position: addFloatUniform('lights[$i].position', 2),
|
||||
color: addFloatUniform('lights[$i].color', 3),
|
||||
radius: addFloatUniform('lights[$i].radius', 1),
|
||||
}
|
||||
];
|
||||
lights = [for (i in 0...MAX_LIGHTS)
|
||||
{
|
||||
position: addFloatUniform('lights[$i].position', 2),
|
||||
color: addFloatUniform('lights[$i].color', 3),
|
||||
radius: addFloatUniform('lights[$i].radius', 1),
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ class TextureSwap extends FlxShader
|
||||
public function loadSwapImage(path:String)
|
||||
{
|
||||
#if html5
|
||||
BitmapData.loadFromFile(path).onComplete(function(bmp:BitmapData) {
|
||||
BitmapData.loadFromFile(path).onComplete(function(bmp:BitmapData)
|
||||
{
|
||||
swappedImage = bmp;
|
||||
});
|
||||
#else
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package funkin.graphics.shaders;
|
||||
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
|
||||
class TitleOutline extends FlxShader
|
||||
{
|
||||
public var funnyX(default, set):Float = 0;
|
||||
public var funnyY(default, set):Float = 0;
|
||||
|
||||
function set_funnyX(x:Float):Float
|
||||
{
|
||||
xPos.value[0] = x;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
function set_funnyY(y:Float):Float
|
||||
{
|
||||
yPos.value[0] = y;
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
@:glFragmentSource('
|
||||
#pragma header
|
||||
|
||||
// uniform float alphaShit;
|
||||
uniform float xPos;
|
||||
uniform float yPos;
|
||||
|
||||
uniform int numoutlines = 1;
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
|
||||
vec2 size = vec2(xPos, yPos);
|
||||
|
||||
if (color.a == 0.0) {
|
||||
float w = size.x / openfl_TextureSize.x;
|
||||
float h = size.y / openfl_TextureSize.y;
|
||||
|
||||
vec4 colorOffset = flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y - h));
|
||||
|
||||
|
||||
vec3 hsvShit = rgb2hsv(vec3(colorOffset.r, colorOffset.g, colorOffset.b));
|
||||
|
||||
if (hsvShit.b <= 0.1 && colorOffset.a != 0.)
|
||||
color = vec4(0.0, 1.0, 0.8, color.a);
|
||||
}
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
|
||||
')
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
xPos.value = [0];
|
||||
yPos.value = [0];
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,8 @@ class FunkinVideoSprite extends FlxVideoSprite
|
||||
|
||||
if (bitmap != null)
|
||||
{
|
||||
bitmap.onOpening.add(function():Void {
|
||||
bitmap.onOpening.add(function():Void
|
||||
{
|
||||
if (bitmap != null)
|
||||
{
|
||||
if (PlayState.instance != null)
|
||||
|
||||
@@ -6,5 +6,7 @@ package funkin.graphics.video;
|
||||
*/
|
||||
#if hxvlc
|
||||
@:hscriptClass
|
||||
class ScriptedFunkinVideoSprite extends funkin.graphics.video.FunkinVideoSprite implements polymod.hscript.HScriptedClass {}
|
||||
class ScriptedFunkinVideoSprite extends funkin.graphics.video.FunkinVideoSprite implements polymod.hscript.HScriptedClass
|
||||
{
|
||||
}
|
||||
#end
|
||||
|
||||
+56
-143
@@ -69,271 +69,219 @@ class Controls extends FlxActionSet
|
||||
|
||||
public var UI_UP(get, never):Bool;
|
||||
|
||||
inline function get_UI_UP()
|
||||
return _ui_up.checkPressed();
|
||||
inline function get_UI_UP() return _ui_up.checkPressed();
|
||||
|
||||
public var UI_LEFT(get, never):Bool;
|
||||
|
||||
inline function get_UI_LEFT()
|
||||
return _ui_left.checkPressed();
|
||||
inline function get_UI_LEFT() return _ui_left.checkPressed();
|
||||
|
||||
public var UI_RIGHT(get, never):Bool;
|
||||
|
||||
inline function get_UI_RIGHT()
|
||||
return _ui_right.checkPressed();
|
||||
inline function get_UI_RIGHT() return _ui_right.checkPressed();
|
||||
|
||||
public var UI_DOWN(get, never):Bool;
|
||||
|
||||
inline function get_UI_DOWN()
|
||||
return _ui_down.checkPressed();
|
||||
inline function get_UI_DOWN() return _ui_down.checkPressed();
|
||||
|
||||
public var UI_UP_P(get, never):Bool;
|
||||
|
||||
inline function get_UI_UP_P()
|
||||
return _ui_up.checkJustPressed();
|
||||
inline function get_UI_UP_P() return _ui_up.checkJustPressed();
|
||||
|
||||
public var UI_LEFT_P(get, never):Bool;
|
||||
|
||||
inline function get_UI_LEFT_P()
|
||||
return _ui_left.checkJustPressed();
|
||||
inline function get_UI_LEFT_P() return _ui_left.checkJustPressed();
|
||||
|
||||
public var UI_RIGHT_P(get, never):Bool;
|
||||
|
||||
inline function get_UI_RIGHT_P()
|
||||
return _ui_right.checkJustPressed();
|
||||
inline function get_UI_RIGHT_P() return _ui_right.checkJustPressed();
|
||||
|
||||
public var UI_DOWN_P(get, never):Bool;
|
||||
|
||||
inline function get_UI_DOWN_P()
|
||||
return _ui_down.checkJustPressed();
|
||||
inline function get_UI_DOWN_P() return _ui_down.checkJustPressed();
|
||||
|
||||
public var UI_UP_R(get, never):Bool;
|
||||
|
||||
inline function get_UI_UP_R()
|
||||
return _ui_up.checkJustReleased();
|
||||
inline function get_UI_UP_R() return _ui_up.checkJustReleased();
|
||||
|
||||
public var UI_LEFT_R(get, never):Bool;
|
||||
|
||||
inline function get_UI_LEFT_R()
|
||||
return _ui_left.checkJustReleased();
|
||||
inline function get_UI_LEFT_R() return _ui_left.checkJustReleased();
|
||||
|
||||
public var UI_RIGHT_R(get, never):Bool;
|
||||
|
||||
inline function get_UI_RIGHT_R()
|
||||
return _ui_right.checkJustReleased();
|
||||
inline function get_UI_RIGHT_R() return _ui_right.checkJustReleased();
|
||||
|
||||
public var UI_DOWN_R(get, never):Bool;
|
||||
|
||||
inline function get_UI_DOWN_R()
|
||||
return _ui_down.checkJustReleased();
|
||||
inline function get_UI_DOWN_R() return _ui_down.checkJustReleased();
|
||||
|
||||
public var NOTE_UP(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_UP()
|
||||
return _note_up.checkPressed();
|
||||
inline function get_NOTE_UP() return _note_up.checkPressed();
|
||||
|
||||
public var NOTE_LEFT(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_LEFT()
|
||||
return _note_left.checkPressed();
|
||||
inline function get_NOTE_LEFT() return _note_left.checkPressed();
|
||||
|
||||
public var NOTE_RIGHT(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_RIGHT()
|
||||
return _note_right.checkPressed();
|
||||
inline function get_NOTE_RIGHT() return _note_right.checkPressed();
|
||||
|
||||
public var NOTE_DOWN(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_DOWN()
|
||||
return _note_down.checkPressed();
|
||||
inline function get_NOTE_DOWN() return _note_down.checkPressed();
|
||||
|
||||
public var NOTE_UP_P(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_UP_P()
|
||||
return _note_up.checkJustPressed();
|
||||
inline function get_NOTE_UP_P() return _note_up.checkJustPressed();
|
||||
|
||||
public var NOTE_LEFT_P(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_LEFT_P()
|
||||
return _note_left.checkJustPressed();
|
||||
inline function get_NOTE_LEFT_P() return _note_left.checkJustPressed();
|
||||
|
||||
public var NOTE_RIGHT_P(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_RIGHT_P()
|
||||
return _note_right.checkJustPressed();
|
||||
inline function get_NOTE_RIGHT_P() return _note_right.checkJustPressed();
|
||||
|
||||
public var NOTE_DOWN_P(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_DOWN_P()
|
||||
return _note_down.checkJustPressed();
|
||||
inline function get_NOTE_DOWN_P() return _note_down.checkJustPressed();
|
||||
|
||||
public var NOTE_UP_R(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_UP_R()
|
||||
return _note_up.checkJustReleased();
|
||||
inline function get_NOTE_UP_R() return _note_up.checkJustReleased();
|
||||
|
||||
public var NOTE_LEFT_R(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_LEFT_R()
|
||||
return _note_left.checkJustReleased();
|
||||
inline function get_NOTE_LEFT_R() return _note_left.checkJustReleased();
|
||||
|
||||
public var NOTE_RIGHT_R(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_RIGHT_R()
|
||||
return _note_right.checkJustReleased();
|
||||
inline function get_NOTE_RIGHT_R() return _note_right.checkJustReleased();
|
||||
|
||||
public var NOTE_DOWN_R(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_DOWN_R()
|
||||
return _note_down.checkJustReleased();
|
||||
inline function get_NOTE_DOWN_R() return _note_down.checkJustReleased();
|
||||
|
||||
public var ACCEPT(get, never):Bool;
|
||||
|
||||
inline function get_ACCEPT()
|
||||
return _accept.checkPressed();
|
||||
inline function get_ACCEPT() return _accept.checkPressed();
|
||||
|
||||
public var ACCEPT_P(get, never):Bool;
|
||||
|
||||
inline function get_ACCEPT_P()
|
||||
return _accept.checkJustPressed();
|
||||
inline function get_ACCEPT_P() return _accept.checkJustPressed();
|
||||
|
||||
public var ACCEPT_R(get, never):Bool;
|
||||
|
||||
inline function get_ACCEPT_R()
|
||||
return _accept.checkJustReleased();
|
||||
inline function get_ACCEPT_R() return _accept.checkJustReleased();
|
||||
|
||||
public var BACK(get, never):Bool;
|
||||
|
||||
inline function get_BACK()
|
||||
return _back.checkPressed();
|
||||
inline function get_BACK() return _back.checkPressed();
|
||||
|
||||
public var BACK_P(get, never):Bool;
|
||||
|
||||
inline function get_BACK_P()
|
||||
return _back.checkJustPressed();
|
||||
inline function get_BACK_P() return _back.checkJustPressed();
|
||||
|
||||
public var BACK_R(get, never):Bool;
|
||||
|
||||
inline function get_BACK_R()
|
||||
return _back.checkJustReleased();
|
||||
inline function get_BACK_R() return _back.checkJustReleased();
|
||||
|
||||
public var PAUSE(get, never):Bool;
|
||||
|
||||
inline function get_PAUSE()
|
||||
return _pause.checkPressed();
|
||||
inline function get_PAUSE() return _pause.checkPressed();
|
||||
|
||||
public var PAUSE_P(get, never):Bool;
|
||||
|
||||
inline function get_PAUSE_P()
|
||||
return _pause.checkJustPressed();
|
||||
inline function get_PAUSE_P() return _pause.checkJustPressed();
|
||||
|
||||
public var PAUSE_R(get, never):Bool;
|
||||
|
||||
inline function get_PAUSE_R()
|
||||
return _pause.checkJustReleased();
|
||||
inline function get_PAUSE_R() return _pause.checkJustReleased();
|
||||
|
||||
public var RESET(get, never):Bool;
|
||||
|
||||
inline function get_RESET()
|
||||
return _reset.checkPressed();
|
||||
inline function get_RESET() return _reset.checkPressed();
|
||||
|
||||
public var RESET_P(get, never):Bool;
|
||||
|
||||
inline function get_RESET_P()
|
||||
return _reset.checkJustPressed();
|
||||
inline function get_RESET_P() return _reset.checkJustPressed();
|
||||
|
||||
public var RESET_R(get, never):Bool;
|
||||
|
||||
inline function get_RESET_R()
|
||||
return _reset.checkJustReleased();
|
||||
inline function get_RESET_R() return _reset.checkJustReleased();
|
||||
|
||||
public var WINDOW_FULLSCREEN(get, never):Bool;
|
||||
|
||||
inline function get_WINDOW_FULLSCREEN()
|
||||
return _window_fullscreen.check();
|
||||
inline function get_WINDOW_FULLSCREEN() return _window_fullscreen.check();
|
||||
|
||||
#if FEATURE_SCREENSHOTS
|
||||
public var WINDOW_SCREENSHOT(get, never):Bool;
|
||||
|
||||
inline function get_WINDOW_SCREENSHOT()
|
||||
return _window_screenshot.check();
|
||||
inline function get_WINDOW_SCREENSHOT() return _window_screenshot.check();
|
||||
#end
|
||||
|
||||
public var FREEPLAY_FAVORITE(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_FAVORITE()
|
||||
return _freeplay_favorite.check();
|
||||
inline function get_FREEPLAY_FAVORITE() return _freeplay_favorite.check();
|
||||
|
||||
public var FREEPLAY_LEFT(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_LEFT()
|
||||
return _freeplay_left.check();
|
||||
inline function get_FREEPLAY_LEFT() return _freeplay_left.check();
|
||||
|
||||
public var FREEPLAY_RIGHT(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_RIGHT()
|
||||
return _freeplay_right.check();
|
||||
inline function get_FREEPLAY_RIGHT() return _freeplay_right.check();
|
||||
|
||||
public var FREEPLAY_CHAR_SELECT(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_CHAR_SELECT()
|
||||
return _freeplay_char_select.check();
|
||||
inline function get_FREEPLAY_CHAR_SELECT() return _freeplay_char_select.check();
|
||||
|
||||
public var FREEPLAY_JUMP_TO_TOP(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_JUMP_TO_TOP()
|
||||
return _freeplay_jump_to_top.check();
|
||||
inline function get_FREEPLAY_JUMP_TO_TOP() return _freeplay_jump_to_top.check();
|
||||
|
||||
public var FREEPLAY_JUMP_TO_BOTTOM(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_JUMP_TO_BOTTOM()
|
||||
return _freeplay_jump_to_bottom.check();
|
||||
inline function get_FREEPLAY_JUMP_TO_BOTTOM() return _freeplay_jump_to_bottom.check();
|
||||
|
||||
public var CUTSCENE_ADVANCE(get, never):Bool;
|
||||
|
||||
inline function get_CUTSCENE_ADVANCE()
|
||||
return _cutscene_advance.check();
|
||||
inline function get_CUTSCENE_ADVANCE() return _cutscene_advance.check();
|
||||
|
||||
#if FEATURE_DEBUG_MENU
|
||||
public var DEBUG_MENU(get, never):Bool;
|
||||
|
||||
inline function get_DEBUG_MENU()
|
||||
return _debug_menu.check();
|
||||
inline function get_DEBUG_MENU() return _debug_menu.check();
|
||||
#end
|
||||
|
||||
#if FEATURE_CHART_EDITOR
|
||||
public var DEBUG_CHART(get, never):Bool;
|
||||
|
||||
inline function get_DEBUG_CHART()
|
||||
return _debug_chart.check();
|
||||
inline function get_DEBUG_CHART() return _debug_chart.check();
|
||||
#end
|
||||
|
||||
#if FEATURE_STAGE_EDITOR
|
||||
public var DEBUG_STAGE(get, never):Bool;
|
||||
|
||||
inline function get_DEBUG_STAGE()
|
||||
return _debug_stage.check();
|
||||
inline function get_DEBUG_STAGE() return _debug_stage.check();
|
||||
#end
|
||||
|
||||
public var DEBUG_DISPLAY(get, never):Bool;
|
||||
|
||||
inline function get_DEBUG_DISPLAY()
|
||||
return _debug_display.check();
|
||||
inline function get_DEBUG_DISPLAY() return _debug_display.check();
|
||||
|
||||
public var VOLUME_UP(get, never):Bool;
|
||||
|
||||
inline function get_VOLUME_UP()
|
||||
return _volume_up.check();
|
||||
inline function get_VOLUME_UP() return _volume_up.check();
|
||||
|
||||
public var VOLUME_DOWN(get, never):Bool;
|
||||
|
||||
inline function get_VOLUME_DOWN()
|
||||
return _volume_down.check();
|
||||
inline function get_VOLUME_DOWN() return _volume_down.check();
|
||||
|
||||
public var VOLUME_MUTE(get, never):Bool;
|
||||
|
||||
inline function get_VOLUME_MUTE()
|
||||
return _volume_mute.check();
|
||||
inline function get_VOLUME_MUTE() return _volume_mute.check();
|
||||
|
||||
public function new(name, ?scheme:KeyboardScheme)
|
||||
{
|
||||
@@ -976,44 +924,8 @@ class Controls extends FlxActionSet
|
||||
|
||||
public function addDefaultGamepad(id:Int):Void
|
||||
{
|
||||
addGamepadLiteral(id, [
|
||||
Control.ACCEPT => getDefaultGamepadBinds(Control.ACCEPT),
|
||||
Control.BACK => getDefaultGamepadBinds(Control.BACK),
|
||||
Control.UI_UP => getDefaultGamepadBinds(Control.UI_UP),
|
||||
Control.UI_DOWN => getDefaultGamepadBinds(Control.UI_DOWN),
|
||||
Control.UI_LEFT => getDefaultGamepadBinds(Control.UI_LEFT),
|
||||
Control.UI_RIGHT => getDefaultGamepadBinds(Control.UI_RIGHT),
|
||||
Control.NOTE_UP => getDefaultGamepadBinds(Control.NOTE_UP),
|
||||
Control.NOTE_DOWN => getDefaultGamepadBinds(Control.NOTE_DOWN),
|
||||
Control.NOTE_LEFT => getDefaultGamepadBinds(Control.NOTE_LEFT),
|
||||
Control.NOTE_RIGHT => getDefaultGamepadBinds(Control.NOTE_RIGHT),
|
||||
Control.PAUSE => getDefaultGamepadBinds(Control.PAUSE),
|
||||
Control.RESET => getDefaultGamepadBinds(Control.RESET),
|
||||
Control.WINDOW_FULLSCREEN => getDefaultGamepadBinds(Control.WINDOW_FULLSCREEN),
|
||||
#if FEATURE_SCREENSHOTS
|
||||
Control.WINDOW_SCREENSHOT => getDefaultGamepadBinds(Control.WINDOW_SCREENSHOT),
|
||||
#end
|
||||
Control.CUTSCENE_ADVANCE => getDefaultGamepadBinds(Control.CUTSCENE_ADVANCE),
|
||||
Control.FREEPLAY_FAVORITE => getDefaultGamepadBinds(Control.FREEPLAY_FAVORITE),
|
||||
Control.FREEPLAY_LEFT => getDefaultGamepadBinds(Control.FREEPLAY_LEFT),
|
||||
Control.FREEPLAY_RIGHT => getDefaultGamepadBinds(Control.FREEPLAY_RIGHT),
|
||||
Control.FREEPLAY_CHAR_SELECT => getDefaultGamepadBinds(Control.FREEPLAY_CHAR_SELECT),
|
||||
Control.FREEPLAY_JUMP_TO_TOP => getDefaultGamepadBinds(Control.FREEPLAY_JUMP_TO_TOP),
|
||||
Control.FREEPLAY_JUMP_TO_BOTTOM => getDefaultGamepadBinds(Control.FREEPLAY_JUMP_TO_BOTTOM),
|
||||
Control.VOLUME_UP => getDefaultGamepadBinds(Control.VOLUME_UP),
|
||||
Control.VOLUME_DOWN => getDefaultGamepadBinds(Control.VOLUME_DOWN),
|
||||
Control.VOLUME_MUTE => getDefaultGamepadBinds(Control.VOLUME_MUTE),
|
||||
#if FEATURE_DEBUG_MENU
|
||||
Control.DEBUG_MENU => getDefaultGamepadBinds(Control.DEBUG_MENU),
|
||||
#end
|
||||
#if FEATURE_CHART_EDITOR
|
||||
Control.DEBUG_CHART => getDefaultGamepadBinds(Control.DEBUG_CHART),
|
||||
#end
|
||||
#if FEATURE_STAGE_EDITOR
|
||||
Control.DEBUG_STAGE => getDefaultGamepadBinds(Control.DEBUG_STAGE),
|
||||
#end
|
||||
Control.DEBUG_DISPLAY => getDefaultGamepadBinds(Control.DEBUG_DISPLAY),
|
||||
]);
|
||||
addGamepadLiteral(id,
|
||||
[Control.ACCEPT => getDefaultGamepadBinds(Control.ACCEPT), Control.BACK => getDefaultGamepadBinds(Control.BACK), Control.UI_UP => getDefaultGamepadBinds(Control.UI_UP), Control.UI_DOWN => getDefaultGamepadBinds(Control.UI_DOWN), Control.UI_LEFT => getDefaultGamepadBinds(Control.UI_LEFT), Control.UI_RIGHT => getDefaultGamepadBinds(Control.UI_RIGHT), Control.NOTE_UP => getDefaultGamepadBinds(Control.NOTE_UP), Control.NOTE_DOWN => getDefaultGamepadBinds(Control.NOTE_DOWN), Control.NOTE_LEFT => getDefaultGamepadBinds(Control.NOTE_LEFT), Control.NOTE_RIGHT => getDefaultGamepadBinds(Control.NOTE_RIGHT), Control.PAUSE => getDefaultGamepadBinds(Control.PAUSE), Control.RESET => getDefaultGamepadBinds(Control.RESET), Control.WINDOW_FULLSCREEN => getDefaultGamepadBinds(Control.WINDOW_FULLSCREEN), #if FEATURE_SCREENSHOTS Control.WINDOW_SCREENSHOT => getDefaultGamepadBinds(Control.WINDOW_SCREENSHOT), #end Control.CUTSCENE_ADVANCE => getDefaultGamepadBinds(Control.CUTSCENE_ADVANCE), Control.FREEPLAY_FAVORITE => getDefaultGamepadBinds(Control.FREEPLAY_FAVORITE), Control.FREEPLAY_LEFT => getDefaultGamepadBinds(Control.FREEPLAY_LEFT), Control.FREEPLAY_RIGHT => getDefaultGamepadBinds(Control.FREEPLAY_RIGHT), Control.FREEPLAY_CHAR_SELECT => getDefaultGamepadBinds(Control.FREEPLAY_CHAR_SELECT), Control.FREEPLAY_JUMP_TO_TOP => getDefaultGamepadBinds(Control.FREEPLAY_JUMP_TO_TOP), Control.FREEPLAY_JUMP_TO_BOTTOM => getDefaultGamepadBinds(Control.FREEPLAY_JUMP_TO_BOTTOM), Control.VOLUME_UP => getDefaultGamepadBinds(Control.VOLUME_UP), Control.VOLUME_DOWN => getDefaultGamepadBinds(Control.VOLUME_DOWN), Control.VOLUME_MUTE => getDefaultGamepadBinds(Control.VOLUME_MUTE), #if FEATURE_DEBUG_MENU Control.DEBUG_MENU => getDefaultGamepadBinds(Control.DEBUG_MENU), #end#if FEATURE_CHART_EDITOR Control.DEBUG_CHART => getDefaultGamepadBinds(Control.DEBUG_CHART), #end#if FEATURE_STAGE_EDITOR Control.DEBUG_STAGE => getDefaultGamepadBinds(Control.DEBUG_STAGE), #end Control.DEBUG_DISPLAY => getDefaultGamepadBinds(Control.DEBUG_DISPLAY),]);
|
||||
}
|
||||
|
||||
function getDefaultGamepadBinds(control:Control):Array<FlxGamepadInputID>
|
||||
@@ -1267,7 +1179,8 @@ class FunkinAction extends FlxActionDigital
|
||||
public var namePressed(default, null):Null<String>;
|
||||
public var nameReleased(default, null):Null<String>;
|
||||
|
||||
var cache:Map<String, {timestamp:Int, value:Bool}> = [];
|
||||
var cache:Map<String,
|
||||
{timestamp:Int, value:Bool}> = [];
|
||||
|
||||
public function new(?name:String = "", ?namePressed:String, ?nameReleased:String)
|
||||
{
|
||||
|
||||
+103
-103
@@ -47,121 +47,108 @@ class Cursor
|
||||
}
|
||||
}
|
||||
|
||||
public static final CURSOR_DEFAULT_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-default.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_DEFAULT_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-default.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorDefault:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_CROSS_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-cross.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_CROSS_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-cross.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorCross:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_ERASER_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-eraser.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_ERASER_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-eraser.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorEraser:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_GRABBING_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-grabbing.png",
|
||||
scale: 1.0,
|
||||
offsetX: -8,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_GRABBING_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-grabbing.png",
|
||||
scale: 1.0,
|
||||
offsetX: -8,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorGrabbing:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_HOURGLASS_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-hourglass.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_HOURGLASS_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-hourglass.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorHourglass:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_POINTER_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-pointer.png",
|
||||
scale: 1.0,
|
||||
offsetX: -8,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_POINTER_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-pointer.png",
|
||||
scale: 1.0,
|
||||
offsetX: -8,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorPointer:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_TEXT_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-text.png",
|
||||
scale: 0.2,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_TEXT_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-text.png",
|
||||
scale: 0.2,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorText:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_TEXT_VERTICAL_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-text-vertical.png",
|
||||
scale: 0.2,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_TEXT_VERTICAL_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-text-vertical.png",
|
||||
scale: 0.2,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorTextVertical:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_ZOOM_IN_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-zoom-in.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_ZOOM_IN_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-zoom-in.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorZoomIn:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_ZOOM_OUT_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-zoom-out.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
public static final CURSOR_ZOOM_OUT_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-zoom-out.png",
|
||||
scale: 1.0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorZoomOut:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_CROSSHAIR_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-crosshair.png",
|
||||
scale: 1.0,
|
||||
offsetX: -16,
|
||||
offsetY: -16,
|
||||
};
|
||||
public static final CURSOR_CROSSHAIR_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-crosshair.png",
|
||||
scale: 1.0,
|
||||
offsetX: -16,
|
||||
offsetY: -16,
|
||||
};
|
||||
static var assetCursorCrosshair:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_CELL_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-cell.png",
|
||||
scale: 1.0,
|
||||
offsetX: -16,
|
||||
offsetY: -16,
|
||||
};
|
||||
public static final CURSOR_CELL_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-cell.png",
|
||||
scale: 1.0,
|
||||
offsetX: -16,
|
||||
offsetY: -16,
|
||||
};
|
||||
static var assetCursorCell:Null<BitmapData> = null;
|
||||
|
||||
public static final CURSOR_SCROLL_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-scroll.png",
|
||||
scale: 0.2,
|
||||
offsetX: -15,
|
||||
offsetY: -15,
|
||||
};
|
||||
public static final CURSOR_SCROLL_PARAMS:CursorParams = {
|
||||
graphic: "assets/images/cursor/cursor-scroll.png",
|
||||
scale: 0.2,
|
||||
offsetX: -15,
|
||||
offsetY: -15,
|
||||
};
|
||||
static var assetCursorScroll:Null<BitmapData> = null;
|
||||
|
||||
static function set_cursorMode(value:Null<CursorMode>):Null<CursorMode>
|
||||
@@ -353,7 +340,8 @@ class Cursor
|
||||
if (assetCursorDefault == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_DEFAULT_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorDefault = bitmapData;
|
||||
applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS);
|
||||
});
|
||||
@@ -368,7 +356,8 @@ class Cursor
|
||||
if (assetCursorCross == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_CROSS_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorCross = bitmapData;
|
||||
applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS);
|
||||
});
|
||||
@@ -383,7 +372,8 @@ class Cursor
|
||||
if (assetCursorEraser == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ERASER_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorEraser = bitmapData;
|
||||
applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS);
|
||||
});
|
||||
@@ -398,7 +388,8 @@ class Cursor
|
||||
if (assetCursorGrabbing == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_GRABBING_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorGrabbing = bitmapData;
|
||||
applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS);
|
||||
});
|
||||
@@ -413,7 +404,8 @@ class Cursor
|
||||
if (assetCursorHourglass == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_HOURGLASS_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorHourglass = bitmapData;
|
||||
applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS);
|
||||
});
|
||||
@@ -428,7 +420,8 @@ class Cursor
|
||||
if (assetCursorPointer == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_POINTER_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorPointer = bitmapData;
|
||||
applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS);
|
||||
});
|
||||
@@ -443,7 +436,8 @@ class Cursor
|
||||
if (assetCursorText == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_TEXT_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorText = bitmapData;
|
||||
applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS);
|
||||
});
|
||||
@@ -458,7 +452,8 @@ class Cursor
|
||||
if (assetCursorZoomIn == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ZOOM_IN_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorZoomIn = bitmapData;
|
||||
applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS);
|
||||
});
|
||||
@@ -473,7 +468,8 @@ class Cursor
|
||||
if (assetCursorZoomOut == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ZOOM_OUT_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorZoomOut = bitmapData;
|
||||
applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS);
|
||||
});
|
||||
@@ -488,7 +484,8 @@ class Cursor
|
||||
if (assetCursorCrosshair == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_CROSSHAIR_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorCrosshair = bitmapData;
|
||||
applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS);
|
||||
});
|
||||
@@ -503,7 +500,8 @@ class Cursor
|
||||
if (assetCursorCell == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_CELL_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorCell = bitmapData;
|
||||
applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS);
|
||||
});
|
||||
@@ -518,7 +516,8 @@ class Cursor
|
||||
if (assetCursorScroll == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_SCROLL_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
future.onComplete(function(bitmapData:BitmapData)
|
||||
{
|
||||
assetCursorScroll = bitmapData;
|
||||
applyCursorParams(assetCursorScroll, CURSOR_SCROLL_PARAMS);
|
||||
});
|
||||
@@ -554,6 +553,7 @@ class Cursor
|
||||
registerHaxeUICursor('grabbing', CURSOR_GRABBING_PARAMS);
|
||||
registerHaxeUICursor('hourglass', CURSOR_HOURGLASS_PARAMS);
|
||||
registerHaxeUICursor('pointer', CURSOR_POINTER_PARAMS);
|
||||
registerHaxeUICursor('move', CURSOR_POINTER_PARAMS);
|
||||
registerHaxeUICursor('text', CURSOR_TEXT_PARAMS);
|
||||
registerHaxeUICursor('text-vertical', CURSOR_TEXT_VERTICAL_PARAMS);
|
||||
registerHaxeUICursor('zoom-in', CURSOR_ZOOM_IN_PARAMS);
|
||||
|
||||
@@ -192,11 +192,10 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||
clearButtons();
|
||||
|
||||
var limeGamepad = FlxGamepadUtil.getLimeGamepad(gamepad);
|
||||
var callbacks =
|
||||
{
|
||||
onButtonDown: handleButtonDown.bind(gamepad),
|
||||
onButtonUp: handleButtonUp.bind(gamepad)
|
||||
};
|
||||
var callbacks = {
|
||||
onButtonDown: handleButtonDown.bind(gamepad),
|
||||
onButtonUp: handleButtonUp.bind(gamepad)
|
||||
};
|
||||
limeGamepad.onButtonDownPrecise.add(callbacks.onButtonDown);
|
||||
limeGamepad.onButtonUpPrecise.add(callbacks.onButtonUp);
|
||||
|
||||
@@ -300,12 +299,11 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||
|
||||
if (getInputByKey(key)?.justPressed ?? false)
|
||||
{
|
||||
onInputPressed.dispatch(
|
||||
{
|
||||
noteDirection: getDirectionForKey(key),
|
||||
timestamp: timestamp,
|
||||
keyCode: keyCode
|
||||
});
|
||||
onInputPressed.dispatch({
|
||||
noteDirection: getDirectionForKey(key),
|
||||
timestamp: timestamp,
|
||||
keyCode: keyCode
|
||||
});
|
||||
_dirPressTimestamps.set(getDirectionForKey(key), timestamp);
|
||||
}
|
||||
}
|
||||
@@ -323,12 +321,11 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||
|
||||
if (getInputByKey(key)?.justReleased ?? false)
|
||||
{
|
||||
onInputReleased.dispatch(
|
||||
{
|
||||
noteDirection: getDirectionForKey(key),
|
||||
timestamp: timestamp,
|
||||
keyCode: keyCode
|
||||
});
|
||||
onInputReleased.dispatch({
|
||||
noteDirection: getDirectionForKey(key),
|
||||
timestamp: timestamp,
|
||||
keyCode: keyCode
|
||||
});
|
||||
_dirReleaseTimestamps.set(getDirectionForKey(key), timestamp);
|
||||
}
|
||||
}
|
||||
@@ -348,12 +345,11 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||
|
||||
if (getInputByButton(gamepad, buttonId)?.justPressed ?? false)
|
||||
{
|
||||
onInputPressed.dispatch(
|
||||
{
|
||||
noteDirection: getDirectionForButton(gamepad, buttonId),
|
||||
timestamp: timestamp,
|
||||
keyCode: button // implicit cast to int
|
||||
});
|
||||
onInputPressed.dispatch({
|
||||
noteDirection: getDirectionForButton(gamepad, buttonId),
|
||||
timestamp: timestamp,
|
||||
keyCode: button // implicit cast to int
|
||||
});
|
||||
_dirPressTimestamps.set(getDirectionForButton(gamepad, buttonId), timestamp);
|
||||
}
|
||||
}
|
||||
@@ -373,12 +369,11 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||
|
||||
if (getInputByButton(gamepad, buttonId)?.justReleased ?? false)
|
||||
{
|
||||
onInputReleased.dispatch(
|
||||
{
|
||||
noteDirection: getDirectionForButton(gamepad, buttonId),
|
||||
timestamp: timestamp,
|
||||
keyCode: button // implicit cast to int
|
||||
});
|
||||
onInputReleased.dispatch({
|
||||
noteDirection: getDirectionForButton(gamepad, buttonId),
|
||||
timestamp: timestamp,
|
||||
keyCode: button // implicit cast to int
|
||||
});
|
||||
_dirReleaseTimestamps.set(getDirectionForButton(gamepad, buttonId), timestamp);
|
||||
}
|
||||
}
|
||||
@@ -465,23 +460,19 @@ class PreciseInputList extends FlxKeyList
|
||||
|
||||
public var NOTE_LEFT(get, never):Bool;
|
||||
|
||||
function get_NOTE_LEFT():Bool
|
||||
return checkDir(NoteDirection.LEFT);
|
||||
function get_NOTE_LEFT():Bool return checkDir(NoteDirection.LEFT);
|
||||
|
||||
public var NOTE_DOWN(get, never):Bool;
|
||||
|
||||
function get_NOTE_DOWN():Bool
|
||||
return checkDir(NoteDirection.DOWN);
|
||||
function get_NOTE_DOWN():Bool return checkDir(NoteDirection.DOWN);
|
||||
|
||||
public var NOTE_UP(get, never):Bool;
|
||||
|
||||
function get_NOTE_UP():Bool
|
||||
return checkDir(NoteDirection.UP);
|
||||
function get_NOTE_UP():Bool return checkDir(NoteDirection.UP);
|
||||
|
||||
public var NOTE_RIGHT(get, never):Bool;
|
||||
|
||||
function get_NOTE_RIGHT():Bool
|
||||
return checkDir(NoteDirection.RIGHT);
|
||||
function get_NOTE_RIGHT():Bool return checkDir(NoteDirection.RIGHT);
|
||||
}
|
||||
|
||||
typedef PreciseInputEvent =
|
||||
|
||||
@@ -78,19 +78,23 @@ class ControlsHandler
|
||||
switch (hint.noteDirection)
|
||||
{
|
||||
case NoteDirection.LEFT:
|
||||
controls.forEachBound(Control.NOTE_LEFT, function(action:FlxActionDigital, state:FlxInputState):Void {
|
||||
controls.forEachBound(Control.NOTE_LEFT, function(action:FlxActionDigital, state:FlxInputState):Void
|
||||
{
|
||||
addButton(action, hint, state, cachedInput);
|
||||
});
|
||||
case NoteDirection.DOWN:
|
||||
controls.forEachBound(Control.NOTE_DOWN, function(action:FlxActionDigital, state:FlxInputState):Void {
|
||||
controls.forEachBound(Control.NOTE_DOWN, function(action:FlxActionDigital, state:FlxInputState):Void
|
||||
{
|
||||
addButton(action, hint, state, cachedInput);
|
||||
});
|
||||
case NoteDirection.UP:
|
||||
controls.forEachBound(Control.NOTE_UP, function(action:FlxActionDigital, state:FlxInputState):Void {
|
||||
controls.forEachBound(Control.NOTE_UP, function(action:FlxActionDigital, state:FlxInputState):Void
|
||||
{
|
||||
addButton(action, hint, state, cachedInput);
|
||||
});
|
||||
case NoteDirection.RIGHT:
|
||||
controls.forEachBound(Control.NOTE_RIGHT, function(action:FlxActionDigital, state:FlxInputState):Void {
|
||||
controls.forEachBound(Control.NOTE_RIGHT, function(action:FlxActionDigital, state:FlxInputState):Void
|
||||
{
|
||||
addButton(action, hint, state, cachedInput);
|
||||
});
|
||||
}
|
||||
@@ -128,7 +132,9 @@ class ControlsHandler
|
||||
@:noCompletion
|
||||
private static function get_hasExternalInputDevice():Bool
|
||||
{
|
||||
return FlxG.gamepads.numActiveGamepads > 0 #if android || KeyboardUtil.keyboardConnected || extension.androidtools.Tools.isChromebook() #end;
|
||||
return FlxG.gamepads.numActiveGamepads > 0 #if android
|
||||
|| KeyboardUtil.keyboardConnected
|
||||
|| extension.androidtools.Tools.isChromebook() #end;
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user