Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e62c5a223c | ||
|
|
5fc7491121 | ||
|
|
9598455118 | ||
|
|
0e4b9753a0 | ||
|
|
2bbe3d788d | ||
|
|
cd731d7e40 | ||
|
|
e11e3d51c2 | ||
|
|
b19b78df5d | ||
|
|
1a061cbbcc | ||
|
|
9908c8be32 | ||
|
|
8522deab64 | ||
|
|
7e93521666 | ||
|
|
5aeb3a28fb | ||
|
|
b5db93726a | ||
|
|
20de8db41b | ||
|
|
5d73d65fdc | ||
|
|
8c750d368d | ||
|
|
8d328ace84 | ||
|
|
9d825f00aa | ||
|
|
c7b68507a8 | ||
|
|
970ccf2f4a | ||
|
|
479036a400 | ||
|
|
80d7ace714 | ||
|
|
a7d5736e14 | ||
|
|
b31be1043c | ||
|
|
6c4d27a842 | ||
|
|
fdab26ce56 | ||
|
|
8ead5eeb08 | ||
|
|
c4ebd3175d | ||
|
|
e593bc8a61 | ||
|
|
cb554bd5f7 | ||
|
|
dd54acfbd1 | ||
|
|
6783f04139 | ||
|
|
11eb4060d0 | ||
|
|
b5357acf98 | ||
|
|
f85f2f03d9 | ||
|
|
146a6cde68 | ||
|
|
65105df9ac | ||
|
|
9106ef215a | ||
|
|
38426bc5d7 | ||
|
|
79e8b5d71e | ||
|
|
6e954cfac0 | ||
|
|
041d33b2d3 | ||
|
|
1b5b0925ed | ||
|
|
cd0bc85b76 | ||
|
|
d4929164b6 | ||
|
|
7bc06861b5 | ||
|
|
3ccbed2cbb | ||
|
|
29c0b0f8eb | ||
|
|
7ff0753472 | ||
|
|
19328abb9e | ||
|
|
feb0863358 | ||
|
|
43e51e9a4a | ||
|
|
3b610d8910 | ||
|
|
63ec749c7a | ||
|
|
c8e6a78154 | ||
|
|
89b2b7fcc3 | ||
|
|
1b1c781426 | ||
|
|
5c8a888b77 | ||
|
|
257576b4d3 | ||
|
|
56f0a7780d | ||
|
|
faa47e8f3e | ||
|
|
b99d599bce | ||
|
|
92bc856f5b | ||
|
|
0490ca8145 | ||
|
|
c9b8f21f61 | ||
|
|
60caa80a84 | ||
|
|
477f85a71e | ||
|
|
b5aed5fb4f | ||
|
|
7d714ecfeb | ||
|
|
f3ff132e90 | ||
|
|
1912155b32 | ||
|
|
daa87c8c58 | ||
|
|
ad87832662 | ||
|
|
b8b0a5bf73 | ||
|
|
5a8b681e2f | ||
|
|
5a6a3f5599 | ||
|
|
bab422029b | ||
|
|
88fc5f9ed6 | ||
|
|
f3c7cfd799 | ||
|
|
231439d808 | ||
|
|
d6f80730a5 | ||
|
|
9cfee0398a | ||
|
|
aa5956a30b | ||
|
|
b9d68957e0 | ||
|
|
1b9f5c62fb | ||
|
|
a0a57ca94d | ||
|
|
271451b9df | ||
|
|
21458f26f2 | ||
|
|
c1b4d4d53e | ||
|
|
28d71dc2fe | ||
|
|
a10bbf60ac | ||
|
|
4e572b171f | ||
|
|
cc768c83aa | ||
|
|
035f3770d5 | ||
|
|
2d95754d49 | ||
|
|
47b1cee6d5 | ||
|
|
4ebd00bc06 | ||
|
|
66554ec4b6 | ||
|
|
bfc798c629 | ||
|
|
6b334b941a | ||
|
|
b06090a477 | ||
|
|
ca5fbb65e5 | ||
|
|
c2305f32f0 | ||
|
|
747446e2e4 | ||
|
|
250d2188b9 | ||
|
|
61c710fe20 | ||
|
|
0fd6f5b3b7 | ||
|
|
8a204c606a | ||
|
|
4401ea0c9a | ||
|
|
12ace54b2b | ||
|
|
f15cd73dff | ||
|
|
b39d7bb5a0 | ||
|
|
f1066a2b0d | ||
|
|
b04b555b38 | ||
|
|
f349eedc72 | ||
|
|
ee2f3c32eb | ||
|
|
0f99a1c7c2 | ||
|
|
95aba09cd6 | ||
|
|
811b9e3343 | ||
|
|
37b9823b36 | ||
|
|
555684afda | ||
|
|
0d5c6ac963 | ||
|
|
db67a6928c | ||
|
|
9c34ab5f9c | ||
|
|
f09074b79d | ||
|
|
a5842fbe3f | ||
|
|
8db654e63d | ||
|
|
60144c0dc7 | ||
|
|
830b5dfd71 | ||
|
|
12d81f0f3b | ||
|
|
8c835447c8 |
@@ -48,19 +48,19 @@ body:
|
||||
attributes:
|
||||
label: Mobile Device Model
|
||||
description: (Mobile users only) What mobile device are you playing on?
|
||||
placeholder: ex. iPhone 16, Galaxy S25, iPad 11th Gen
|
||||
placeholder: ex. iPhone 17, Galaxy S25, iPad 11th Gen
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Mobile OS Version
|
||||
description: (Mobile users only) What version is your Operating System?
|
||||
placeholder: ex. iOS 18.5, Android 15, iPadOS 18.5
|
||||
placeholder: ex. iOS 26.0, Android 15, iPadOS 26.0
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.7.4
|
||||
placeholder: ex. 0.7.5
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ body:
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.7.4
|
||||
placeholder: ex. 0.7.5
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ body:
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you compiling? The game version is in the bottom left corner of the main menu or in the project.hxp file.
|
||||
placeholder: ex. 0.7.4
|
||||
placeholder: ex. 0.7.5
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ body:
|
||||
- label: I have read the [Contributing Guide](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md)
|
||||
- label: I have checked the Issues/Discussions pages to see if my issue has already been reported
|
||||
- label: I have properly titled my issue
|
||||
- label: I did not have any mods installed when I encountered this issue
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
@@ -48,19 +49,19 @@ body:
|
||||
attributes:
|
||||
label: Mobile Device Model
|
||||
description: (Mobile users only) What mobile device are you playing on?
|
||||
placeholder: ex. iPhone 16, Galaxy S25, iPad 11th Gen
|
||||
placeholder: ex. iPhone 17, Galaxy S25, iPad 11th Gen
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Mobile OS Version
|
||||
description: (Mobile users only) What version is your Operating System?
|
||||
placeholder: ex. iOS 18.5, Android 15, iPadOS 18.5
|
||||
placeholder: ex. iOS 26.0, Android 15, iPadOS 26.0
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.7.4
|
||||
placeholder: ex. 0.7.5
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ NewgroundsCredentials.hx
|
||||
# Mobile signing stuff
|
||||
.apple/
|
||||
.env
|
||||
key.keystore
|
||||
*.keystore
|
||||
|
||||
# Exclude JS stuff
|
||||
node_modules/
|
||||
|
||||
+67
-1
@@ -4,6 +4,72 @@ 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.7.5] - 2025-09-19
|
||||
|
||||
### Added
|
||||
|
||||
- Added an `onStateCreate` event to the Module class.
|
||||
|
||||
### Changed
|
||||
|
||||
- Gave the debug display a huge upgrade!
|
||||
- Choose from three settings in the Preferences Menu: Off/Simple/Advanced
|
||||
- New “Task memory” counter shows how much RAM the game is using on your computer.
|
||||
- Advanced mode displays graphs for FPS and RAM usage.
|
||||
- Hot reloading now works in scripted states.
|
||||
- Made a few more classes scriptable.
|
||||
- FunkinVideoSprite
|
||||
- FlxStrip
|
||||
- FlxTypedGroup
|
||||
- Strumline
|
||||
- StageProp
|
||||
- The Random capsule now brings up the option to select a random instrumental. ([0d5c6ac](https://github.com/FunkinCrew/Funkin/commit/0d5c6ac9632ee3bf3977b9bd369daab9f2d95fea)) - by @VioletSnowLeopard in [#5891](https://github.com/FunkinCrew/Funkin/pull/5891)
|
||||
- The Character Select menu now returns to Freeplay after pressing a back button. ([250d218](https://github.com/FunkinCrew/Funkin/commit/250d2188b9cb2c92d283d8b12ed7de0286324592)) - by @MrMadera in [#5887](https://github.com/FunkinCrew/Funkin/pull/5887)
|
||||
- Pressing the Shift key in the Credits menu now pauses scrolling. ([479036a](https://github.com/FunkinCrew/Funkin/commit/479036a4008fe1c54adc970c0a476fae6b1b3e94)) - by @JVNpixels in [#2924](https://github.com/FunkinCrew/Funkin/pull/2924)
|
||||
- Chart Editor and Stage Editor backup files are now given relevant filenames. ([b31be10](https://github.com/FunkinCrew/Funkin/commit/b31be1043ccd1e906a656913b947bae160b451fe)) - by @Lasercar in [#4118](https://github.com/FunkinCrew/Funkin/pull/4118)
|
||||
- The Animation Editor's onion skin now properly accounts for offsets and displays the idle animation. ([0fd6f5b](https://github.com/FunkinCrew/Funkin/commit/0fd6f5b3b7bd115a3b6821096daa929c220bd235)) - by @KoloInDaCrib in [#5810](https://github.com/FunkinCrew/Funkin/pull/5810)
|
||||
|
||||
### Fixed
|
||||
|
||||
- The Freeplay new rank animation now shows the new rank slamming onto the old one.
|
||||
- Freeplay capsules with a Gold Perfect rank no longer become randomly offset when scrolling.
|
||||
- The Pico Freeplay DJ’s new rank animation now properly renders its lighting.
|
||||
- Freeplay song previews no longer play replaced or deleted audio files.
|
||||
- The strumline no longer disappears after re-entering Lag Adjustment.
|
||||
- Fixed various issues with Week 6 dialogue boxes, portraits, and audio.
|
||||
- [MOBILE] The strumlines are no longer offset to the left in Blazin’.
|
||||
- The preloader no longer disables fullscreen mode after completion.
|
||||
- Attempting to log in to Newgrounds during an ongoing login attempt no longer crashes the game. ([4e572b1](https://github.com/FunkinCrew/Funkin/commit/4e572b171fe901a84f12b4566963fd3c1f4ae6fb)) - by @KoloInDaCrib in [#5922](https://github.com/FunkinCrew/Funkin/pull/5922)
|
||||
- Added null-safety to Leaderboards and Medals to prevent a rare crash. ([88fc5f9](https://github.com/FunkinCrew/Funkin/commit/88fc5f9ed684e287d7411a59fb4fdf170acfed5a)) - by @NotHyper-474 in [#5926](https://github.com/FunkinCrew/Funkin/pull/5926)
|
||||
- Freeplay ranks no longer pop out when scrolling through song capsules. ([747446e](https://github.com/FunkinCrew/Funkin/commit/747446e2e420449f422fbc69aafda6128ac8d16e)) - by @VioletSnowLeopard in [#5822](https://github.com/FunkinCrew/Funkin/pull/5822)
|
||||
- The Freeplay intro no longer plays when returning from a sticker transition. ([63ec749](https://github.com/FunkinCrew/Funkin/commit/63ec749c7a3c90d529e9e8f2f3ed1c8913e8f9a1)) - by @PurSnake in [#5942](https://github.com/FunkinCrew/Funkin/pull/5942)
|
||||
- Hot-reloading with F5 after earning a rank no longer crashes Freeplay. ([79e8b5d](https://github.com/FunkinCrew/Funkin/commit/79e8b5d71e052953b77b81b66e0a83a7bbeeeeed)) - by @KoloInDaCrib in [#5971](https://github.com/FunkinCrew/Funkin/pull/5971)
|
||||
- The Main Menu music now fades in consistently when exiting from Freeplay to the Title Screen. ([a10bbf6](https://github.com/FunkinCrew/Funkin/commit/a10bbf60acbbeadfb5a91930745ef4fd338b1a24)) - by @VirtuGuy in [#5855](https://github.com/FunkinCrew/Funkin/pull/5855)
|
||||
- The game now falls back to default audio files when files specified by the metadata don't exist. ([b5357ac](https://github.com/FunkinCrew/Funkin/commit/b5357acf98bde81a4121a3723061f7c559a0640f)) - by @VioletSnowLeopard in [#5954](https://github.com/FunkinCrew/Funkin/pull/5954)
|
||||
- The cutscene pause menu no longer persists across songs after exiting a video cutscene. ([c2305f3](https://github.com/FunkinCrew/Funkin/commit/c2305f32f0147f6326e8665d21a6b8a793f45033)) - by @KoloInDaCrib in [#5880](https://github.com/FunkinCrew/Funkin/pull/5880)
|
||||
- Mashing to skip the Eggnog Erect cutscene no longer crashes the game. ([5ca3c6a](https://github.com/FunkinCrew/funkin.assets/commit/5ca3c6a5745b61a457db149c289784c08e560ee9)) - by @VioletSnowLeopard in [funkin.assets#258](https://github.com/FunkinCrew/funkin.assets/pull/258)
|
||||
- Stress (Pico Mix) no longer lag spikes when loading in the background tankmen. ([6701405](https://github.com/FunkinCrew/funkin.assets/commit/670140571239ca08c1ae1c5286773d99c2875c2d)) - by @NotHyper-474 and @KoloInDaCrib in [funkin.assets#242](https://github.com/FunkinCrew/funkin.assets/pull/242)
|
||||
- Stress (Pico Mix) no longer lag spikes when Tankman changes sprites. ([aa5956a](https://github.com/FunkinCrew/Funkin/commit/aa5956a30bdc47d53ebebb4b61a7ebb3f04c7dc6)) - by @NotHyper-474 in [#5939](https://github.com/FunkinCrew/Funkin/pull/5939)
|
||||
- The bricks in Stress (Pico Mix) no longer move after restarting the song. ([9cfee03](https://github.com/FunkinCrew/Funkin/commit/9cfee0398a057567f80ad68ff6052964da63af09)) - by @VirtuGuy in [#5875](https://github.com/FunkinCrew/Funkin/pull/5875)
|
||||
- Pico now properly shoots after restarting Stress. ([b0ef0d3](https://github.com/FunkinCrew/funkin.assets/commit/b0ef0d352c199e93903652a95ffdf16a3dc2d569)) - by @7oltan in [funkin.assets#254](https://github.com/FunkinCrew/funkin.assets/pull/254)
|
||||
- A-Bot's visualizer now zeroes out when restarting the song. ([ba3a8b0](https://github.com/FunkinCrew/funkin.assets/commit/ba3a8b0cbbf785a0729dcf85e25b6e59c0c12657)) - by @KoloInDaCrib in [funkin.assets#246](https://github.com/FunkinCrew/funkin.assets/pull/246)
|
||||
- Notekinds now properly receive dispatched events last in PlayState. ([89b2b7f](https://github.com/FunkinCrew/Funkin/commit/89b2b7fcc3130db0af1f782594af72ae1f4ce320)) - by @Az989YT in [#5936](https://github.com/FunkinCrew/Funkin/pull/5936)
|
||||
- Text from .json files is now properly sanitized before it is parsed. ([80d7ace](https://github.com/FunkinCrew/Funkin/commit/80d7ace714efb729cb8f1b23b61fdab4ac655275)) - by @KoloInDaCrib in [#5108](https://github.com/FunkinCrew/Funkin/pull/5108)
|
||||
- Fixed a crash when exiting the Chart Editor after interacting with a text field. ([f85f2f0](https://github.com/FunkinCrew/Funkin/commit/f85f2f03d97fc1332275f088ddce2b4056f0ded2)) - by @NotHyper-474 in [#5992](https://github.com/FunkinCrew/Funkin/pull/5992)
|
||||
- Fixed some issues with variation handling in the Chart Editor. ([970ccf2](https://github.com/FunkinCrew/Funkin/commit/970ccf2f4a9b5802b70d0b3c269c8b9e541d53f2)) - by @NotHyper-474 in [#4396](https://github.com/FunkinCrew/Funkin/pull/4396)
|
||||
- Audio no longer plays when dragging a playhead in the Chart Editor. ([2d95754](https://github.com/FunkinCrew/Funkin/commit/2d95754d492da06920d56de2b2bcbb9b81182af0)) - by @Lasercar in [#4140](https://github.com/FunkinCrew/Funkin/pull/4140)
|
||||
- The Chart Editor Offsets window song preview no longer continues playing during playtesting. ([11eb406](https://github.com/FunkinCrew/Funkin/commit/11eb4060d03b18a2f3604297eb9a24e54077fe3c)) - by @PurSnake and @Lasercar in [#5973](https://github.com/FunkinCrew/Funkin/pull/5973)
|
||||
- The fullscreen keybind no longer triggers when typing in a field. ([4ebd00b](https://github.com/FunkinCrew/Funkin/commit/4ebd00bc06a5c94e935f557ba14ba9729faec104)) - by @lemz1 in [#4131](https://github.com/FunkinCrew/Funkin/pull/4131)
|
||||
- The Stage Editor new stage menu can no longer be opened multiple times at once. ([47b1cee](https://github.com/FunkinCrew/Funkin/commit/47b1cee6d5ee37cd18146ca8098da85610c6b544)) - by @VioletSnowLeopard in [#5873](https://github.com/FunkinCrew/Funkin/pull/5873)
|
||||
- Adjusted the save hotkeys in the Animation Editor to be usable on Windows. ([8ead5ee](https://github.com/FunkinCrew/Funkin/commit/8ead5eeb0840756a843fdcf0f3f347b087e7182d)) - by @VioletSnowLeopard in [#5974](https://github.com/FunkinCrew/Funkin/pull/5974)
|
||||
- A few more tiny fixes.
|
||||
|
||||
## New Contributors for 0.7.5
|
||||
|
||||
* @Az989YT made their first contribution in [#5936](https://github.com/FunkinCrew/Funkin/pull/5936)
|
||||
|
||||
|
||||
|
||||
## [0.7.4] - 2025-09-01
|
||||
|
||||
### Added
|
||||
@@ -47,7 +113,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Improved properties support, behaving more like regular Haxe. (Thanks NotHyper-474!)
|
||||
- Fixed a null object reference when evaluating static variables with no explicit values. (Thanks NotHyper-474!)
|
||||
- Fixed a null object reference when iterating over a null variable. (Thanks NotHyper-474!)
|
||||
- Scripts are now automatically cleared when Polymod is initialized. (Thanks KoloInDaKrib!)
|
||||
- Scripts are now automatically cleared when Polymod is initialized. (Thanks KoloInDaCrib!)
|
||||
- Polished every chart in the game!
|
||||
- The lengths of hold notes have been adjusted to match the vocals as closely as possible, across all variations and difficulties.
|
||||
- Easy and Normal charts from Week 1 to Week 3 have been adjusted for more consistency between the player and opponent.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div align='center'><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Friday_Night_Funkin%27_logo.svg/2560px-Friday_Night_Funkin%27_logo.svg.png" width="800">
|
||||
<div align='center'><img src="https://fridaynightfunkin.wiki.gg/images/FNF_logo.png" width="800">
|
||||
|
||||
<h2>Friday Night Funkin' is a rhythm game. Built using HaxeFlixel for <a href="https://ldjam.com/events/ludum-dare/47">Ludum Dare 47.</a></h2>
|
||||
|
||||
@@ -63,4 +63,4 @@ Full credits can be found in-game, or in the `credits.json` file which is locate
|
||||
## Special Thanks
|
||||
- [Tom Fulp](https://twitter.com/tomfulp) - For being a great guy and for Newgrounds
|
||||
- [JohnnyUtah](https://twitter.com/JohnnyUtahNG/) - Voice of Tankman
|
||||
- [L0Litsmonica](https://twitter.com/L0Litsmonica) - Voice of Mommy Mearest
|
||||
- [L0Litsmonica](https://twitter.com/L0Litsmonica) - Voice of Mommy Mearest
|
||||
|
||||
+1
-1
Submodule assets updated: e6f463fb64...5ca3c6a574
@@ -17,6 +17,10 @@
|
||||
"asset": "asset/shared/images/resultScreen/results-pico/resultsGOOD/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "assets/shared/images/resultScreen/results-bf/resultsEXCELLENT/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
},
|
||||
{
|
||||
"asset": "asset/shared/images/resultScreen/results-pico/resultsSHIT/spritemap1.png",
|
||||
"blocksize": "4x4"
|
||||
|
||||
+33
-20
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"extendsConfigPath": "",
|
||||
"defineCombinations": [],
|
||||
"defaultSeverity": "INFO",
|
||||
"extendsConfigPath": "",
|
||||
"numberOfCheckerThreads": 5,
|
||||
"baseDefines": [],
|
||||
"version": 1,
|
||||
"exclude": {},
|
||||
"defineCombinations": [],
|
||||
"checks": [
|
||||
{
|
||||
"props": {},
|
||||
@@ -40,12 +39,6 @@
|
||||
"props": {},
|
||||
"type": "AvoidStarImport"
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"severity": "IGNORE"
|
||||
},
|
||||
"type": "AvoidTernaryOperator"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"type": "BlockBreakingConditional"
|
||||
@@ -71,7 +64,6 @@
|
||||
{
|
||||
"props": {
|
||||
"severity": "IGNORE",
|
||||
|
||||
"policy": "aligned",
|
||||
"allowSingleline": true
|
||||
},
|
||||
@@ -108,13 +100,12 @@
|
||||
{
|
||||
"props": {
|
||||
"lineStyle": "onestar",
|
||||
"startStyle": "twostars",
|
||||
"severity": "IGNORE"
|
||||
"startStyle": "ignore"
|
||||
},
|
||||
"type": "DocCommentStyle"
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"props": { "severity": "IGNORE" },
|
||||
"type": "Dynamic"
|
||||
},
|
||||
{
|
||||
@@ -193,6 +184,14 @@
|
||||
},
|
||||
"type": "FileLength"
|
||||
},
|
||||
{
|
||||
"type": "FileNameCase",
|
||||
"props": {}
|
||||
},
|
||||
{
|
||||
"type": "Final",
|
||||
"props": {}
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"option": "upperCase"
|
||||
@@ -245,7 +244,7 @@
|
||||
{
|
||||
"props": {
|
||||
"max": 160,
|
||||
"ignorePattern": "^$"
|
||||
"ignorePattern": "^(\\s*['\"][^'\"]*['\"].*)$"
|
||||
},
|
||||
"type": "LineLength"
|
||||
},
|
||||
@@ -348,7 +347,7 @@
|
||||
{
|
||||
"props": {
|
||||
"allowSingleLineStatement": true,
|
||||
"tokens": ["FOR", "ELSE_IF", "WHILE", "DO_WHILE"]
|
||||
"tokens": ["ELSE_IF", "WHILE", "DO_WHILE"]
|
||||
},
|
||||
"type": "NeedBraces"
|
||||
},
|
||||
@@ -580,6 +579,12 @@
|
||||
"props": {},
|
||||
"type": "TrailingWhitespace"
|
||||
},
|
||||
{
|
||||
"type": "Type",
|
||||
"props": {
|
||||
"ignoreEnumAbstractValues": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"tokens": ["CLASS_DEF", "ENUM_DEF", "INTERFACE_DEF", "TYPEDEF_DEF"]
|
||||
@@ -612,11 +617,18 @@
|
||||
"type": "UnusedLocalVar"
|
||||
},
|
||||
{
|
||||
"type": "VarTypeHint",
|
||||
"props": {
|
||||
"typeHintPolicy": "enforce_all",
|
||||
"ignoreEnumAbstractValues": true
|
||||
},
|
||||
"type": "VarTypeHint"
|
||||
"ignoreEnumAbstractValues": true,
|
||||
"severity": "IGNORE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "VariableInitialisation",
|
||||
"props": {
|
||||
"allowFinal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
@@ -665,5 +677,6 @@
|
||||
"type": "WhitespaceAround"
|
||||
}
|
||||
],
|
||||
"numberOfCheckerThreads": 5
|
||||
"exclude": {},
|
||||
"version": 1
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ This section provides guidelines to follow when [opening an issue](https://githu
|
||||
|
||||
## Requirements
|
||||
Make sure you're playing:
|
||||
- the latest version of the game (currently v0.7.4)
|
||||
- the latest version of the game (currently v0.7.5)
|
||||
- without any mods
|
||||
- on [Newgrounds](https://www.newgrounds.com/portal/view/770371) or downloaded from [itch.io](https://ninja-muffin24.itch.io/funkin)
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"name": "FlxPartialSound",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "3c9f63e3501c20c0b60442089dc05306f5a87968",
|
||||
"url": "https://github.com/FunkinDroidTeam/FlxPartialSound.git"
|
||||
"ref": "4e86b724ca43489a59ea465a46ffc12598e5df15",
|
||||
"url": "https://github.com/FunkinCrew/FlxPartialSound.git"
|
||||
},
|
||||
{
|
||||
"name": "astc-compressor",
|
||||
@@ -18,7 +18,7 @@
|
||||
"name": "extension-admob",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "02334589ff9603a5f483077a44395009644f6274",
|
||||
"ref": "96385202f6f951b2bde0cbe836c5da21dde1f36e",
|
||||
"url": "https://github.com/FunkinCrew/extension-admob"
|
||||
},
|
||||
{
|
||||
@@ -45,7 +45,7 @@
|
||||
"name": "flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "ac2a34fe4b3400bc04218f46320a65af0f337fc0",
|
||||
"ref": "5cf3515e665812179a9b413629325b608b3a9815",
|
||||
"url": "https://github.com/FunkinCrew/flixel"
|
||||
},
|
||||
{
|
||||
@@ -111,14 +111,14 @@
|
||||
"name": "hxcpp",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "5a0dc3f644dc676a4a092b7e6c8edc8be941f024",
|
||||
"ref": "363d3d0a8d3ac450f5ba1b4c3357353656b71e82",
|
||||
"url": "https://github.com/FunkinCrew/hxcpp"
|
||||
},
|
||||
{
|
||||
"name": "hxcpp-debug-server",
|
||||
"type": "git",
|
||||
"dir": "hxcpp-debug-server",
|
||||
"ref": "147294123f983e35f50a966741474438069a7a8f",
|
||||
"ref": "7459934666a473a4cc4d066ba4a93ef92f1ce94c",
|
||||
"url": "https://github.com/FunkinCrew/hxcpp-debugger"
|
||||
},
|
||||
{
|
||||
@@ -143,7 +143,7 @@
|
||||
{
|
||||
"name": "hxvlc",
|
||||
"type": "haxelib",
|
||||
"version": "2.2.2"
|
||||
"version": "2.2.4"
|
||||
},
|
||||
{
|
||||
"name": "json2object",
|
||||
@@ -170,7 +170,7 @@
|
||||
"name": "lime",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "e5f8c27124598505917a001588b560244731adfb",
|
||||
"ref": "f109a083023a9e5622342257993fc745d9bbb959",
|
||||
"url": "https://github.com/FunkinCrew/lime"
|
||||
},
|
||||
{
|
||||
@@ -184,14 +184,14 @@
|
||||
"name": "openfl",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "a0df7c3afe360c9af59a76e45007dbf4e53b5131",
|
||||
"ref": "e6296200292a76b5f7b8684a991ead6b05ae4507",
|
||||
"url": "https://github.com/FunkinCrew/openfl"
|
||||
},
|
||||
{
|
||||
"name": "polymod",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "55ee00236806330c89c3bff1e1563eb6425a9d96",
|
||||
"ref": "47d9a2775e3fd8f6dd17d6f4b99b8798709da435",
|
||||
"url": "https://github.com/larsiusprime/polymod"
|
||||
},
|
||||
{
|
||||
|
||||
+180
-38
@@ -31,13 +31,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.7.4";
|
||||
static final VERSION:String = "0.7.5";
|
||||
|
||||
/**
|
||||
* 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 = 53;
|
||||
static final BUILD_NUMBER:Int = 56;
|
||||
|
||||
/**
|
||||
* The game's name. Used as the default window title.
|
||||
@@ -330,6 +330,13 @@ class Project extends HXProject
|
||||
*/
|
||||
static final FEATURE_LOG_TRACE:FeatureFlag = "FEATURE_LOG_TRACE";
|
||||
|
||||
/**
|
||||
* `-DFEATURE_DEBUG_FILE_LOGGING`
|
||||
* If this flag is enabled, the game will print debug traces to a log file near the game's executable.
|
||||
* Disable to improve performance a bunch.
|
||||
*/
|
||||
static final FEATURE_DEBUG_FILE_LOGGING:FeatureFlag = "FEATURE_DEBUG_FILE_LOGGING";
|
||||
|
||||
/**
|
||||
* `-DFEATURE_MOBILE_ADVERTISEMENTS`
|
||||
* If this flag is enabled, Google AdMob will be enabled.
|
||||
@@ -509,6 +516,11 @@ class Project extends HXProject
|
||||
{
|
||||
configureIOS();
|
||||
}
|
||||
|
||||
if (!isDisplay())
|
||||
{
|
||||
checkLibraries();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -535,8 +547,7 @@ class Project extends HXProject
|
||||
this.meta.title = TITLE;
|
||||
|
||||
// ios uses this character code instead of spaces...
|
||||
if (isIOS())
|
||||
this.meta.title = ~/ /ig.replace(TITLE, "\u2002");
|
||||
if (isIOS()) this.meta.title = ~/ /ig.replace(TITLE, "\u2002");
|
||||
|
||||
this.meta.version = VERSION;
|
||||
this.meta.packageName = PACKAGE_NAME;
|
||||
@@ -782,8 +793,13 @@ class Project extends HXProject
|
||||
// FEATURE FLAGS
|
||||
//
|
||||
|
||||
// Should be true on debug builds or if GITHUB_BUILD is enabled.
|
||||
FEATURE_DEBUG_FUNCTIONS.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
|
||||
FEATURE_RESULTS_DEBUG.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
|
||||
|
||||
FEATURE_FUNKVIS.apply(this, true);
|
||||
FEATURE_LOG_TRACE.apply(this, true);
|
||||
FEATURE_DEBUG_FILE_LOGGING.apply(this, FEATURE_DEBUG_FUNCTIONS.isEnabled(this));
|
||||
FEATURE_NAUGHTYNESS.apply(this, true);
|
||||
FEATURE_OPEN_URL.apply(this, true);
|
||||
FEATURE_PARTIAL_SOUNDS.apply(this, true);
|
||||
@@ -793,10 +809,6 @@ class Project extends HXProject
|
||||
// HXCPP has trouble with iOS simulator builds though...
|
||||
FEATURE_VIDEO_PLAYBACK.apply(this, !isIOSSimulator());
|
||||
|
||||
// Should be true on debug builds or if GITHUB_BUILD is enabled.
|
||||
FEATURE_DEBUG_FUNCTIONS.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
|
||||
FEATURE_RESULTS_DEBUG.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
|
||||
|
||||
// Should be true on desktop, non-tester builds.
|
||||
// We don't want testers to accidentally leak songs to their Discord friends!
|
||||
FEATURE_DISCORD_RPC.apply(this, isDesktop() && !FEATURE_DEBUG_FUNCTIONS.isEnabled(this));
|
||||
@@ -849,8 +861,6 @@ class Project extends HXProject
|
||||
FEATURE_COMPRESSED_TEXTURES.apply(this, isMobile() && isRelease());
|
||||
|
||||
renderFlagsTable();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -893,7 +903,6 @@ class Project extends HXProject
|
||||
enabledFlags.sort(function(a, b) return a > b ? 1 : -1);
|
||||
disabledFlags.sort(function(a, b) return a > b ? 1 : -1);
|
||||
|
||||
|
||||
var horizontalDivider:String = "".rpad("-", enabledWidth + disabledWidth);
|
||||
|
||||
info(horizontalDivider);
|
||||
@@ -902,19 +911,16 @@ class Project extends HXProject
|
||||
info('Enabled Flags $unicodeCheck'.rpad(" ", enabledWidth + 5) + 'Disabled Flags $unicodeCross');
|
||||
info(horizontalDivider);
|
||||
|
||||
|
||||
while (enabledFlags.length > 0 || disabledFlags.length > 0)
|
||||
{
|
||||
var outputLine:String = "";
|
||||
|
||||
if (enabledFlags.length > 0)
|
||||
outputLine += enabledFlags.shift();
|
||||
if (enabledFlags.length > 0) outputLine += enabledFlags.shift();
|
||||
|
||||
outputLine = outputLine.rpad(" ", enabledWidth + 5);
|
||||
outputLine += "| ";
|
||||
|
||||
if (disabledFlags.length > 0)
|
||||
outputLine += disabledFlags.shift() ?? "";
|
||||
if (disabledFlags.length > 0) outputLine += disabledFlags.shift() ?? "";
|
||||
|
||||
info(outputLine);
|
||||
}
|
||||
@@ -1050,9 +1056,9 @@ class Project extends HXProject
|
||||
if (envConfig != null)
|
||||
{
|
||||
keystore = new Keystore();
|
||||
keystore.path = Std.string(envConfig.get('ANDROID_KEYSTORE_PATH'));
|
||||
keystore.alias = Std.string(envConfig.get('ANDROID_KEYSTORE_ALIAS'));
|
||||
keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD'));
|
||||
keystore.path = Std.string(envConfig.get('KEYSTORE_PATH'));
|
||||
keystore.alias = Std.string(envConfig.get('KEYSTORE_ALIAS'));
|
||||
keystore.password = Std.string(envConfig.get('KEYSTORE_PASSWORD'));
|
||||
}
|
||||
|
||||
if (isBundle())
|
||||
@@ -1339,7 +1345,6 @@ class Project extends HXProject
|
||||
addIcon("art/icons/icon32.png", 32);
|
||||
addIcon("art/icons/icon64.png", 64);
|
||||
addIcon("art/icons/iconOG.png");
|
||||
|
||||
}
|
||||
|
||||
info('Done configuring icons.');
|
||||
@@ -1350,7 +1355,7 @@ class Project extends HXProject
|
||||
*/
|
||||
function configureASTCTextures()
|
||||
{
|
||||
if (command != "display")
|
||||
if (!(isDisplay() || isClean()))
|
||||
{
|
||||
readASTCExclusion();
|
||||
|
||||
@@ -1368,15 +1373,7 @@ class Project extends HXProject
|
||||
{
|
||||
if (envConfig == null) return;
|
||||
|
||||
if (isAndroid())
|
||||
{
|
||||
setenv("ADMOB_APPID", Std.string(envConfig.get('ANDROID_ADMOB_APP_ID')));
|
||||
}
|
||||
|
||||
if (isIOS())
|
||||
{
|
||||
setenv("ADMOB_APPID", Std.string(envConfig.get('IOS_ADMOB_APP_ID')));
|
||||
}
|
||||
setenv("ADMOB_APPID", Std.string(envConfig.get('ADMOB_APP_ID')));
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1489,6 +1486,11 @@ class Project extends HXProject
|
||||
return this.command == "display";
|
||||
}
|
||||
|
||||
public function isClean():Bool
|
||||
{
|
||||
return this.command == "clean";
|
||||
}
|
||||
|
||||
public function isDebug():Bool
|
||||
{
|
||||
return this.debug;
|
||||
@@ -1711,6 +1713,101 @@ class Project extends HXProject
|
||||
return commitHashSplice;
|
||||
}
|
||||
|
||||
@SuppressWarnings('checkstyle:Dynamic')
|
||||
static function checkLibraries():Void
|
||||
{
|
||||
var outdatedLibraries:Map<String, Array<String>> = new Map<String, Array<String>>();
|
||||
|
||||
var hmmData:Dynamic = haxe.Json.parse(sys.io.File.getContent(#if ios '../../../../../' + #end 'hmm.json'));
|
||||
|
||||
var dependencies:Array<Dynamic> = cast hmmData.dependencies;
|
||||
|
||||
for (library in dependencies)
|
||||
{
|
||||
var libraryName:String = cast(library.name, String).replace(".", ",");
|
||||
|
||||
var libraryDev:String = readLibraryDev(libraryName);
|
||||
|
||||
var libraryCurrentVersion:String = readLibraryCurrentVersion(libraryName);
|
||||
|
||||
switch (library.type)
|
||||
{
|
||||
case 'haxelib':
|
||||
if (libraryDev != "")
|
||||
{
|
||||
outdatedLibraries.set(libraryName, [libraryDev, library.version]);
|
||||
}
|
||||
else if (library.version != libraryCurrentVersion)
|
||||
{
|
||||
outdatedLibraries.set(libraryName, [libraryCurrentVersion, library.version]);
|
||||
}
|
||||
case 'git':
|
||||
if (libraryDev != "")
|
||||
{
|
||||
outdatedLibraries.set(libraryName, [libraryDev, library.ref]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var commitHash:String = readLibraryGitCommitHash(libraryName);
|
||||
|
||||
if (commitHash != library.ref && commitHash != "")
|
||||
outdatedLibraries.set(libraryName, [commitHash, library.ref]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Lambda.count(outdatedLibraries) > 0)
|
||||
{
|
||||
Sys.println("\x1b[1;33m\nWARNING: The following haxelibs diverge from the versions set in hmm.json."
|
||||
+ "\n\nThey may be outdated, so it is recommended to abort compilation and run `hmm reinstall [library]` to update each library."
|
||||
+ "\n\nYou may ignore this warning if your libraries are newer than the versions in hmm.json, or if you know what you're doing.\n\x1b[0m");
|
||||
|
||||
for (libraryName in outdatedLibraries.keys())
|
||||
{
|
||||
var versions:Null<Array<String>> = outdatedLibraries.get(libraryName);
|
||||
if (versions == null) continue;
|
||||
|
||||
var outdatedVersion:String = versions[0];
|
||||
var expectedVersion:String = versions[1];
|
||||
|
||||
var isDev:Bool = outdatedVersion.contains("/");
|
||||
|
||||
Sys.println("- " + libraryName.replace(",", ".") + (isDev ? "\x1b[1;34m (Development Build)\x1b[0m" : ""));
|
||||
Sys.println(" \x1b[38;5;203mCurrent version: " + outdatedVersion + "\x1b[0m");
|
||||
Sys.println(" \x1b[38;5;82mExpected version: " + expectedVersion + "\x1b[0m\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function readLibraryCurrentVersion(libraryName:String):String
|
||||
{
|
||||
var path = haxe.io.Path.join([haxe.io.Path.addTrailingSlash(Sys.getCwd()), '.haxelib', libraryName, '.current']);
|
||||
|
||||
if (sys.FileSystem.exists(path))
|
||||
return sys.io.File.getContent(path);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
static function readLibraryDev(libraryName:String):String
|
||||
{
|
||||
var path = haxe.io.Path.join([haxe.io.Path.addTrailingSlash(Sys.getCwd()), '.haxelib', libraryName, '.dev']);
|
||||
|
||||
if (sys.FileSystem.exists(path))
|
||||
return sys.io.File.getContent(path);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
static function readLibraryGitCommitHash(libraryName:String):String
|
||||
{
|
||||
var gitProccess = new sys.io.Process("git", ["-C", ".haxelib/" + libraryName + "/git", "rev-parse", "HEAD"]);
|
||||
|
||||
gitProccess.exitCode(true);
|
||||
|
||||
return gitProccess.stdout.readAll().toString().trim();
|
||||
}
|
||||
|
||||
public function getGitBranch():String
|
||||
{
|
||||
// Cannibalized from GitCommit.hx
|
||||
@@ -1781,7 +1878,7 @@ class Project extends HXProject
|
||||
*/
|
||||
public function info(message:String):Void
|
||||
{
|
||||
if (command != "display")
|
||||
if (!(isDisplay() || isClean()))
|
||||
{
|
||||
Sys.println('[INFO] ${message}');
|
||||
}
|
||||
@@ -1808,24 +1905,37 @@ class Project extends HXProject
|
||||
}
|
||||
|
||||
var env = new Map<String, Dynamic>();
|
||||
|
||||
for (line in envFile.split('\n'))
|
||||
{
|
||||
if (line.length <= 0 || line.startsWith("#") || shouldExcludeEnvKey(line)) continue;
|
||||
line = line.trim();
|
||||
|
||||
var index:Int = line.indexOf('=');
|
||||
if (line.length <= 0 || line.startsWith('#') || shouldExcludeEnvKey(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index == -1) continue;
|
||||
final index:Int = line.indexOf('=');
|
||||
|
||||
var field:String = line.substr(0, index);
|
||||
var value:String = line.substr(index + 1);
|
||||
if (index == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
env.set(field, value);
|
||||
final value:String = line.substr(index + 1);
|
||||
|
||||
if (value.length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
env.set(stripEnvTargetPrefix(line.substr(0, index)), value);
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
private function shouldExcludeEnvKey(key:String):Bool
|
||||
public function shouldExcludeEnvKey(key:String):Bool
|
||||
{
|
||||
final android:Bool = key.startsWith('ANDROID_');
|
||||
final ios:Bool = key.startsWith('IOS_');
|
||||
@@ -1841,6 +1951,38 @@ class Project extends HXProject
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stripEnvTargetPrefix(key:String):String
|
||||
{
|
||||
final index:Int = key.indexOf('_');
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
final prefix:String = key.substr(0, index);
|
||||
final rest:String = key.substr(index + 1);
|
||||
|
||||
if (isAndroid() && (prefix == 'ANDROID' || prefix == 'MOBILE'))
|
||||
{
|
||||
return rest;
|
||||
}
|
||||
else if (isIOS() && (prefix == 'IOS' || prefix == 'MOBILE'))
|
||||
{
|
||||
return rest;
|
||||
}
|
||||
else if (isWeb() && prefix == 'WEB')
|
||||
{
|
||||
return rest;
|
||||
}
|
||||
else if (isDesktop() && prefix == 'DESKTOP')
|
||||
{
|
||||
return rest;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public function readASTCExclusion():Void
|
||||
{
|
||||
@:nullSafety(Off)
|
||||
|
||||
+22
-47
@@ -6,13 +6,12 @@ import flixel.FlxState;
|
||||
import funkin.ui.FullScreenScaleMode;
|
||||
import funkin.Preferences;
|
||||
import funkin.util.logging.CrashHandler;
|
||||
import funkin.ui.debug.MemoryCounter;
|
||||
import funkin.ui.debug.FunkinDebugDisplay;
|
||||
import funkin.save.Save;
|
||||
import haxe.ui.Toolkit;
|
||||
#if hxvlc
|
||||
import hxvlc.util.Handle;
|
||||
#end
|
||||
import openfl.display.FPS;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.events.Event;
|
||||
import openfl.Lib;
|
||||
@@ -30,7 +29,6 @@ class Main extends Sprite
|
||||
var initialState:Class<FlxState> = funkin.InitState; // The FlxState the game starts with.
|
||||
var zoom:Float = -1; // If -1, zoom is automatically calculated to fit the window dimensions.
|
||||
var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode.
|
||||
var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets
|
||||
|
||||
// You can pretty much ignore everything from here on - your code should go in your states.
|
||||
|
||||
@@ -84,32 +82,17 @@ class Main extends Sprite
|
||||
setupGame();
|
||||
}
|
||||
|
||||
var video:Video;
|
||||
var netStream:NetStream;
|
||||
var overlay:Sprite;
|
||||
|
||||
/**
|
||||
* A frame counter displayed at the top left.
|
||||
* The debug display at the top left.
|
||||
*/
|
||||
public static var fpsCounter:FPS;
|
||||
|
||||
/**
|
||||
* A RAM counter displayed at the top left.
|
||||
*/
|
||||
public static var memoryCounter:MemoryCounter;
|
||||
public static var debugDisplay:FunkinDebugDisplay;
|
||||
|
||||
function setupGame():Void
|
||||
{
|
||||
initHaxeUI();
|
||||
|
||||
// addChild gets called by the user settings code.
|
||||
fpsCounter = new FPS(10, 3, 0xFFFFFF);
|
||||
|
||||
#if !html5
|
||||
// addChild gets called by the user settings code.
|
||||
// TODO: disabled on HTML5 (todo: find another method that works?)
|
||||
memoryCounter = new MemoryCounter(10, 13, 0xFFFFFF);
|
||||
#end
|
||||
debugDisplay = new FunkinDebugDisplay(10, 10, 0xFFFFFF);
|
||||
|
||||
#if mobile
|
||||
// Add this signal so we can reposition and resize the memory and fps counter.
|
||||
@@ -121,7 +104,16 @@ class Main extends Sprite
|
||||
|
||||
#if hxvlc
|
||||
// Initialize hxvlc's Handle here so the videos are loading faster.
|
||||
Handle.init();
|
||||
Handle.initAsync(function(success:Bool):Void {
|
||||
if (success)
|
||||
{
|
||||
trace('[HXVLC] LibVLC instance initialized!');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('[HXVLC] LibVLC instance failed to initialize!');
|
||||
}
|
||||
});
|
||||
#end
|
||||
|
||||
// Don't call anything from the preferences until the save is loaded!
|
||||
@@ -133,7 +125,8 @@ class Main extends Sprite
|
||||
|
||||
WindowUtil.setVSyncMode(funkin.Preferences.vsyncMode);
|
||||
|
||||
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, Preferences.framerate, Preferences.framerate, skipSplash, startFullscreen);
|
||||
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, Preferences.framerate, Preferences.framerate, skipSplash,
|
||||
(FlxG.stage.window.fullscreen || Preferences.autoFullscreen));
|
||||
|
||||
// FlxG.game._customSoundTray wants just the class, it calls new from
|
||||
// create() in there, which gets called when it's added to the stage
|
||||
@@ -191,41 +184,23 @@ class Main extends Sprite
|
||||
scale = Math.min(scale, 1);
|
||||
#end
|
||||
final thypos:Float = Math.max(FullScreenScaleMode.notchSize.x, 10);
|
||||
if (fpsCounter != null)
|
||||
|
||||
if (debugDisplay != null)
|
||||
{
|
||||
fpsCounter.scaleX = fpsCounter.scaleY = scale;
|
||||
debugDisplay.scaleX = debugDisplay.scaleY = scale;
|
||||
|
||||
if (FlxG.game != null)
|
||||
{
|
||||
if (lerp)
|
||||
{
|
||||
fpsCounter.x = flixel.math.FlxMath.lerp(fpsCounter.x, FlxG.game.x + thypos, FlxG.elapsed * 3);
|
||||
debugDisplay.x = flixel.math.FlxMath.lerp(debugDisplay.x, FlxG.game.x + thypos, FlxG.elapsed * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
fpsCounter.x = FlxG.game.x + FullScreenScaleMode.notchSize.x + 10;
|
||||
debugDisplay.x = FlxG.game.x + FullScreenScaleMode.notchSize.x + 10;
|
||||
}
|
||||
|
||||
fpsCounter.y = FlxG.game.y + (3 * scale);
|
||||
}
|
||||
}
|
||||
|
||||
if (memoryCounter != null)
|
||||
{
|
||||
memoryCounter.scaleX = memoryCounter.scaleY = scale;
|
||||
|
||||
if (FlxG.game != null)
|
||||
{
|
||||
if (lerp)
|
||||
{
|
||||
memoryCounter.x = flixel.math.FlxMath.lerp(memoryCounter.x, FlxG.game.x + thypos, FlxG.elapsed * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
memoryCounter.x = FlxG.game.x + FullScreenScaleMode.notchSize.x + 10;
|
||||
}
|
||||
|
||||
memoryCounter.y = FlxG.game.y + (13 * scale);
|
||||
debugDisplay.y = FlxG.game.y + (3 * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-5
@@ -3,6 +3,8 @@ package source; // Yeah, I know...
|
||||
import sys.FileSystem;
|
||||
import sys.io.File;
|
||||
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* A script which executes after the game is built.
|
||||
*/
|
||||
@@ -27,14 +29,43 @@ class Postbuild
|
||||
|
||||
sys.FileSystem.deleteFile(BUILD_TIME_FILE);
|
||||
|
||||
var buildTime:Float = roundToTwoDecimals(end - start);
|
||||
|
||||
Sys.println('[INFO] Build took: ${buildTime} seconds');
|
||||
Sys.println('[INFO] Build took: ${format(end - start)}');
|
||||
}
|
||||
}
|
||||
|
||||
static function roundToTwoDecimals(value:Float):Float
|
||||
static function format(time:Float, decimals:Int = 1):String
|
||||
{
|
||||
return Math.round(value * 100) / 100;
|
||||
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;
|
||||
|
||||
for (unit in units)
|
||||
{
|
||||
var value:Float;
|
||||
if (unit.name == "second")
|
||||
{
|
||||
// handle seconds with decimals
|
||||
value = Math.round(remaining * Math.pow(10, decimals)) / Math.pow(10, decimals);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = Math.floor(remaining / unit.secs);
|
||||
remaining %= unit.secs;
|
||||
}
|
||||
|
||||
//
|
||||
if (value > 0 || (unit.name == "second" && parts.length == 0))
|
||||
{
|
||||
parts.push('${value} ${unit.name}${value == 1 ? "" : "s"}');
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,7 @@ class FunkinMemory
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import funkin.data.event.SongEventRegistry;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.story.level.LevelRegistry;
|
||||
import funkin.modding.module.ModuleHandler;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.notes.notekind.NoteKindManager;
|
||||
import funkin.play.PlayStatePlaylist;
|
||||
import funkin.ui.debug.charting.ChartEditorState;
|
||||
@@ -125,7 +125,7 @@ class InitState extends FlxState
|
||||
|
||||
#if ios
|
||||
// Setup Audio session
|
||||
funkin.external.ios.AudioSession.initialize();
|
||||
funkin.external.apple.AudioSession.initialize();
|
||||
#end
|
||||
|
||||
// This ain't a pixel art game! (most of the time)
|
||||
|
||||
@@ -7,6 +7,7 @@ import funkin.mobile.util.InAppPurchasesUtil;
|
||||
import funkin.save.Save;
|
||||
import funkin.util.WindowUtil;
|
||||
import funkin.util.HapticUtil.HapticsMode;
|
||||
import funkin.ui.debug.FunkinDebugDisplay.DebugDisplayMode;
|
||||
|
||||
/**
|
||||
* A core class which provides a store of user-configurable, globally relevant values.
|
||||
@@ -137,27 +138,70 @@ class Preferences
|
||||
/**
|
||||
* If enabled, an FPS and memory counter will be displayed even if this is not a debug build.
|
||||
* Always disabled on mobile.
|
||||
* @default `false`
|
||||
* @default `Off`
|
||||
*/
|
||||
public static var debugDisplay(get, set):Bool;
|
||||
public static var debugDisplay(get, set):DebugDisplayMode;
|
||||
|
||||
static function get_debugDisplay():Bool
|
||||
static function get_debugDisplay():DebugDisplayMode
|
||||
{
|
||||
#if mobile
|
||||
return false;
|
||||
return DebugDisplayMode.OFF;
|
||||
#end
|
||||
return Save?.instance?.options?.debugDisplay ?? false;
|
||||
var value = Save?.instance?.options?.debugDisplay ?? 'Off';
|
||||
|
||||
return switch (value)
|
||||
{
|
||||
case "Simple":
|
||||
DebugDisplayMode.SIMPLE;
|
||||
case "Advanced":
|
||||
DebugDisplayMode.ADVANCED;
|
||||
default:
|
||||
DebugDisplayMode.OFF;
|
||||
};
|
||||
}
|
||||
|
||||
static function set_debugDisplay(value:Bool):Bool
|
||||
static function set_debugDisplay(value:DebugDisplayMode):DebugDisplayMode
|
||||
{
|
||||
if (value != Save.instance.options.debugDisplay)
|
||||
var string;
|
||||
|
||||
switch (value)
|
||||
{
|
||||
toggleDebugDisplay(value);
|
||||
case DebugDisplayMode.SIMPLE:
|
||||
string = "Simple";
|
||||
case DebugDisplayMode.ADVANCED:
|
||||
string = "Advanced";
|
||||
default:
|
||||
string = "Off";
|
||||
};
|
||||
|
||||
if (string != Save.instance.options.debugDisplay)
|
||||
{
|
||||
setDebugDisplayMode(value);
|
||||
}
|
||||
|
||||
var save = Save.instance;
|
||||
save.options.debugDisplay = value;
|
||||
save.options.debugDisplay = string;
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opacity of the debug display's background.
|
||||
* @default `50`
|
||||
*/
|
||||
public static var debugDisplayBGOpacity(get, set):Int;
|
||||
|
||||
static function get_debugDisplayBGOpacity():Int
|
||||
{
|
||||
return Save?.instance?.options?.debugDisplayBGOpacity ?? 50;
|
||||
}
|
||||
|
||||
static function set_debugDisplayBGOpacity(value:Int):Int
|
||||
{
|
||||
setDebugDisplayBGOpacity(value / 100);
|
||||
|
||||
var save:Save = Save.instance;
|
||||
save.options.debugDisplayBGOpacity = value;
|
||||
save.flush();
|
||||
return value;
|
||||
}
|
||||
@@ -459,18 +503,15 @@ class Preferences
|
||||
{
|
||||
// Apply the autoPause setting (enables automatic pausing on focus lost).
|
||||
FlxG.autoPause = Preferences.autoPause;
|
||||
// WindowUtil.setVSyncMode(Preferences.vsyncMode);
|
||||
|
||||
// Apply the debugDisplay setting (enables the FPS and RAM display).
|
||||
toggleDebugDisplay(Preferences.debugDisplay);
|
||||
setDebugDisplayMode(Preferences.debugDisplay);
|
||||
setDebugDisplayBGOpacity(Preferences.debugDisplayBGOpacity / 100);
|
||||
|
||||
#if web
|
||||
toggleFramerateCap(Preferences.unlockedFramerate);
|
||||
#end
|
||||
|
||||
#if desktop
|
||||
// Apply the autoFullscreen setting (launches the game in fullscreen automatically)
|
||||
FlxG.fullscreen = Preferences.autoFullscreen;
|
||||
#end
|
||||
|
||||
#if mobile
|
||||
// Apply the allowScreenTimeout setting.
|
||||
lime.system.System.allowScreenTimeout = Preferences.screenTimeout;
|
||||
@@ -485,26 +526,25 @@ class Preferences
|
||||
#end
|
||||
}
|
||||
|
||||
static function toggleDebugDisplay(show:Bool):Void
|
||||
static function setDebugDisplayMode(mode:DebugDisplayMode):Void
|
||||
{
|
||||
if (show)
|
||||
if (FlxG.game.parent.contains(Main.debugDisplay))
|
||||
{
|
||||
// Enable the debug display.
|
||||
FlxG.game.parent.addChild(Main.fpsCounter);
|
||||
|
||||
#if !html5
|
||||
FlxG.game.parent.addChild(Main.memoryCounter);
|
||||
#end
|
||||
FlxG.game.parent.removeChild(Main.debugDisplay);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable the debug display.
|
||||
FlxG.game.parent.removeChild(Main.fpsCounter);
|
||||
|
||||
#if !html5
|
||||
FlxG.game.parent.removeChild(Main.memoryCounter);
|
||||
#end
|
||||
}
|
||||
if (mode == DebugDisplayMode.OFF) return;
|
||||
|
||||
Main.debugDisplay.isAdvanced = (mode == DebugDisplayMode.ADVANCED);
|
||||
|
||||
FlxG.game.parent.addChild(Main.debugDisplay);
|
||||
}
|
||||
|
||||
static function setDebugDisplayBGOpacity(value:Float):Void
|
||||
{
|
||||
if (Main.debugDisplay == null) return;
|
||||
|
||||
Main.debugDisplay.backgroundOpacity = value;
|
||||
}
|
||||
|
||||
#if mobile
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package funkin.api.discord;
|
||||
|
||||
import funkin.util.macro.EnvironmentConfigMacro;
|
||||
#if FEATURE_DISCORD_RPC
|
||||
import hxdiscord_rpc.Discord;
|
||||
import hxdiscord_rpc.Types.DiscordButton;
|
||||
@@ -9,10 +8,12 @@ import hxdiscord_rpc.Types.DiscordRichPresence;
|
||||
import hxdiscord_rpc.Types.DiscordUser;
|
||||
import sys.thread.Thread;
|
||||
|
||||
@:build(funkin.util.macro.EnvironmentMacro.build())
|
||||
@:nullSafety
|
||||
class DiscordClient
|
||||
{
|
||||
static final CLIENT_ID:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("DESKTOP_DISCORD_CLIENT_ID");
|
||||
@:envField
|
||||
static final DISCORD_CLIENT_ID:Null<String>;
|
||||
|
||||
public static var instance(get, never):DiscordClient;
|
||||
static var _instance:Null<DiscordClient> = null;
|
||||
@@ -49,7 +50,7 @@ class DiscordClient
|
||||
|
||||
@:nullSafety(Off)
|
||||
{
|
||||
Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
|
||||
Discord.Initialize(DISCORD_CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
|
||||
}
|
||||
|
||||
createDaemon();
|
||||
@@ -60,7 +61,7 @@ class DiscordClient
|
||||
*/
|
||||
static function hasValidCredentials():Bool
|
||||
{
|
||||
return !(CLIENT_ID == null || CLIENT_ID == "" || (CLIENT_ID != null && CLIENT_ID.contains(" ")));
|
||||
return !(DISCORD_CLIENT_ID == null || DISCORD_CLIENT_ID == "" || (DISCORD_CLIENT_ID != null && DISCORD_CLIENT_ID.contains(" ")));
|
||||
}
|
||||
|
||||
var daemon:Null<Thread> = null;
|
||||
|
||||
@@ -37,10 +37,7 @@ class Leaderboards
|
||||
|
||||
if (NewgroundsClient.instance.isLoggedIn())
|
||||
{
|
||||
var leaderboardList = NewgroundsClient.instance.leaderboards;
|
||||
if (leaderboardList == null) return;
|
||||
|
||||
var leaderboardData:Null<LeaderboardData> = leaderboardList.get(leaderboard.getId());
|
||||
var leaderboardData:Null<LeaderboardData> = listLeaderboardData().get(leaderboard.getId());
|
||||
if (leaderboardData != null)
|
||||
{
|
||||
leaderboardData.postScore(score, function(outcome:Outcome<CallError>):Void {
|
||||
@@ -67,10 +64,7 @@ class Leaderboards
|
||||
// Silently reject retrieving scores from unknown leaderboards.
|
||||
if (leaderboard == Leaderboard.Unknown) return;
|
||||
|
||||
var leaderboardList = NewgroundsClient.instance.leaderboards;
|
||||
if (leaderboardList == null) return;
|
||||
|
||||
var leaderboardData:Null<LeaderboardData> = leaderboardList.get(leaderboard.getId());
|
||||
var leaderboardData:Null<LeaderboardData> = listLeaderboardData().get(leaderboard.getId());
|
||||
if (leaderboardData == null) return;
|
||||
|
||||
var user:Null<User> = null;
|
||||
|
||||
@@ -30,11 +30,7 @@ class Medals
|
||||
{
|
||||
if (NewgroundsClient.instance.isLoggedIn())
|
||||
{
|
||||
var medalList = NewgroundsClient.instance.medals;
|
||||
@:privateAccess
|
||||
if (medalList == null || medalList._map == null) return;
|
||||
|
||||
var medalData:Null<MedalData> = medalList.get(medal.getId());
|
||||
var medalData:Null<MedalData> = listMedalData().get(medal.getId());
|
||||
@:privateAccess
|
||||
if (medalData == null || medalData._data == null)
|
||||
{
|
||||
@@ -120,11 +116,7 @@ class Medals
|
||||
|
||||
public static function fetchMedalData(medal:Medal):Null<FetchedMedalData>
|
||||
{
|
||||
var medalList = NewgroundsClient.instance.medals;
|
||||
@:privateAccess
|
||||
if (medalList == null || medalList._map == null) return null;
|
||||
|
||||
var medalData:Null<MedalData> = medalList.get(medal.getId());
|
||||
var medalData:Null<MedalData> = listMedalData().get(medal.getId());
|
||||
@:privateAccess
|
||||
if (medalData == null || medalData._data == null)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package funkin.api.newgrounds;
|
||||
|
||||
import funkin.util.macro.EnvironmentConfigMacro;
|
||||
import funkin.save.Save;
|
||||
import funkin.api.newgrounds.Medals.Medal;
|
||||
#if FEATURE_NEWGROUNDS
|
||||
@@ -15,11 +14,15 @@ import io.newgrounds.utils.SaveSlotList;
|
||||
import io.newgrounds.utils.ScoreBoardList;
|
||||
import io.newgrounds.objects.User;
|
||||
|
||||
@:build(funkin.util.macro.EnvironmentMacro.build())
|
||||
@:nullSafety
|
||||
class NewgroundsClient
|
||||
{
|
||||
static final APP_ID:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("API_NG_APP_ID");
|
||||
static final ENCRYPTION_KEY:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("API_NG_ENC_KEY");
|
||||
@:envField
|
||||
static final API_NG_APP_ID:Null<String>;
|
||||
|
||||
@:envField
|
||||
static final API_NG_ENC_KEY:Null<String>;
|
||||
|
||||
public static var instance(get, never):NewgroundsClient;
|
||||
|
||||
@@ -42,8 +45,8 @@ class NewgroundsClient
|
||||
trace('[NEWGROUNDS] Initializing client...');
|
||||
|
||||
#if FEATURE_NEWGROUNDS_DEBUG
|
||||
trace('[NEWGROUNDS] App ID: ${APP_ID}');
|
||||
trace('[NEWGROUNDS] Encryption Key: ${ENCRYPTION_KEY}');
|
||||
trace('[NEWGROUNDS] App ID: ${API_NG_APP_ID}');
|
||||
trace('[NEWGROUNDS] Encryption Key: ${API_NG_ENC_KEY}');
|
||||
#end
|
||||
|
||||
if (!hasValidCredentials())
|
||||
@@ -54,9 +57,9 @@ class NewgroundsClient
|
||||
|
||||
@:nullSafety(Off)
|
||||
{
|
||||
NG.create(APP_ID, getSessionId(), #if FEATURE_NEWGROUNDS_DEBUG true #else false #end, onLoginResolved);
|
||||
NG.create(API_NG_APP_ID, getSessionId(), #if FEATURE_NEWGROUNDS_DEBUG true #else false #end, onLoginResolved);
|
||||
|
||||
NG.core.setupEncryption(ENCRYPTION_KEY);
|
||||
NG.core.setupEncryption(API_NG_ENC_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +105,12 @@ class NewgroundsClient
|
||||
return;
|
||||
}
|
||||
|
||||
if (NG.core.attemptingLogin)
|
||||
{
|
||||
trace("[NEWGROUNDS] Login attempt ongoing, will not login until finished.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (onSuccess != null && onError != null)
|
||||
{
|
||||
NG.core.requestLogin(onLoginResolvedWithCallbacks.bind(_, onSuccess, onError));
|
||||
@@ -176,12 +185,12 @@ class NewgroundsClient
|
||||
*/
|
||||
static function hasValidCredentials():Bool
|
||||
{
|
||||
return !(APP_ID == null
|
||||
|| APP_ID == ""
|
||||
|| (APP_ID != null && APP_ID.contains(" "))
|
||||
|| ENCRYPTION_KEY == null
|
||||
|| ENCRYPTION_KEY == ""
|
||||
|| (ENCRYPTION_KEY != null && ENCRYPTION_KEY.contains(" ")));
|
||||
return !(API_NG_APP_ID == null
|
||||
|| API_NG_APP_ID == ""
|
||||
|| (API_NG_APP_ID != null && API_NG_APP_ID.contains(" "))
|
||||
|| API_NG_ENC_KEY == null
|
||||
|| API_NG_ENC_KEY == ""
|
||||
|| (API_NG_ENC_KEY != null && API_NG_ENC_KEY.contains(" ")));
|
||||
}
|
||||
|
||||
function onLoginResolved(outcome:LoginOutcome):Void
|
||||
|
||||
+6
-1
@@ -1,4 +1,4 @@
|
||||
package funkin.play.character;
|
||||
package funkin.data.character;
|
||||
|
||||
import funkin.data.animation.AnimationData;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
@@ -8,6 +8,11 @@ import funkin.play.character.ScriptedCharacter.ScriptedBaseCharacter;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedMultiSparrowCharacter;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedPackerCharacter;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedSparrowCharacter;
|
||||
import funkin.play.character.AnimateAtlasCharacter;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.character.SparrowCharacter;
|
||||
import funkin.play.character.MultiSparrowCharacter;
|
||||
import funkin.play.character.PackerCharacter;
|
||||
import funkin.util.assets.DataAssets;
|
||||
import funkin.util.VersionUtil;
|
||||
import haxe.Json;
|
||||
@@ -426,12 +426,13 @@ using funkin.data.song.migrator.SongDataMigrator;
|
||||
}
|
||||
}
|
||||
|
||||
public function parseEntryChartDataRawWithMigration(contents:String, ?fileName:String = 'raw', version:thx.semver.Version):Null<SongChartData>
|
||||
public function parseEntryChartDataRawWithMigration(contents:String, ?fileName:String = 'raw', version:thx.semver.Version,
|
||||
?variation:String):Null<SongChartData>
|
||||
{
|
||||
// If a version rule is not specified, do not check against it.
|
||||
if (SONG_CHART_DATA_VERSION_RULE == null || VersionUtil.validateVersion(version, SONG_CHART_DATA_VERSION_RULE))
|
||||
{
|
||||
return parseEntryChartDataRaw(contents, fileName);
|
||||
return parseEntryChartDataRaw(contents, fileName, variation);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package funkin.external.apple;
|
||||
|
||||
#if ((ios || macos) && cpp)
|
||||
/**
|
||||
* A utility class to manage the audio session on apple devices.
|
||||
*/
|
||||
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('AudioSession.hpp')
|
||||
@:unreflective
|
||||
extern class AudioSession
|
||||
{
|
||||
/**
|
||||
* Initializes the audio session for playback.
|
||||
*
|
||||
* This function configures the shared AVAudioSession instance with the following settings:
|
||||
* - Sets the audio session category to Playback, allowing Bluetooth A2DP and mixing with spoken audio interruptions.
|
||||
* - For iOS 17.0 and above, disables interruption on route disconnect.
|
||||
* - For iOS 14.5 and above, prefers no interruptions from system alerts.
|
||||
*/
|
||||
@:native('Apple_AudioSession_Initialize')
|
||||
static function initialize():Void;
|
||||
|
||||
/**
|
||||
* Sets the active state of the shared AVAudioSession.
|
||||
*
|
||||
* @param active Whether to activate or deactivate the audio session.
|
||||
*/
|
||||
@:native('Apple_AudioSession_SetActive')
|
||||
static function setActive(active:Bool):Void;
|
||||
}
|
||||
#end
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package funkin.external.apple;
|
||||
|
||||
#if ((ios || macos) && cpp)
|
||||
/**
|
||||
* A utility class to get information about the mem usage.
|
||||
*/
|
||||
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('MemoryUtil.hpp')
|
||||
@:unreflective
|
||||
extern class MemoryUtil
|
||||
{
|
||||
/**
|
||||
* Retrieves the current process's resident set size (RSS) in bytes on Apple platforms.
|
||||
*
|
||||
* @return The resident set size (RSS) in bytes if successful; otherwise, returns 0 on failure.
|
||||
*/
|
||||
@:native('Apple_MemoryUtil_GetCurrentProcessRss')
|
||||
static function getCurrentProcessRss():cpp.SizeT;
|
||||
}
|
||||
#end
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package funkin.external.apple;
|
||||
|
||||
#if ((ios || macos) && cpp)
|
||||
/**
|
||||
* A utility class to get screen related informations on apple devices.
|
||||
*/
|
||||
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('ScreenUtil.hpp')
|
||||
@:unreflective
|
||||
extern class ScreenUtil
|
||||
{
|
||||
/**
|
||||
* Retrieves the safe area insets for the current screen on iOS devices.
|
||||
*
|
||||
* This function populates the provided pointers with the safe area insets (in pixels) for the top, bottom, left, and right edges of the screen.
|
||||
* On iOS 11 and later, it uses the main window's safeAreaInsets and converts them to pixel values using the screen scale.
|
||||
* On earlier iOS versions, all insets are set to 0.0.
|
||||
*
|
||||
* @param top Pointer to a double to receive the top safe area inset (in pixels).
|
||||
* @param bottom Pointer to a double to receive the bottom safe area inset (in pixels).
|
||||
* @param left Pointer to a double to receive the left safe area inset (in pixels).
|
||||
* @param right Pointer to a double to receive the right safe area inset (in pixels).
|
||||
*/
|
||||
@:native('Apple_ScreenUtil_GetSafeAreaInsets')
|
||||
static function getSafeAreaInsets(top:cpp.RawPointer<Float>, bottom:cpp.RawPointer<Float>, left:cpp.RawPointer<Float>, right:cpp.RawPointer<Float>):Void;
|
||||
|
||||
/**
|
||||
* Retrieves the size of the main screen in pixels.
|
||||
*
|
||||
* @param width Pointer to a double where the screen width (in pixels) will be stored.
|
||||
* @param height Pointer to a double where the screen height (in pixels) will be stored.
|
||||
*/
|
||||
@:native('Apple_ScreenUtil_GetScreenSize')
|
||||
static function getScreenSize(width:cpp.RawPointer<Float>, height:cpp.RawPointer<Float>):Void;
|
||||
}
|
||||
#end
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xml>
|
||||
<pragma once="true" />
|
||||
|
||||
<files id="haxe">
|
||||
<compilerflag value="-I${this_dir}/include" />
|
||||
</files>
|
||||
|
||||
<files id="__main__">
|
||||
<compilerflag value="-I${this_dir}/include" />
|
||||
</files>
|
||||
|
||||
<files id="external-apple">
|
||||
<compilerflag value="-I${this_dir}/include" />
|
||||
|
||||
<file name="${this_dir}/src/AudioSession.mm" />
|
||||
<file name="${this_dir}/src/MemoryUtil.mm" />
|
||||
<file name="${this_dir}/src/ScreenUtil.mm" />
|
||||
</files>
|
||||
|
||||
<target id="haxe">
|
||||
<vflag name="-framework" value="UIKit" unless="macos" />
|
||||
<vflag name="-framework" value="Foundation" />
|
||||
<vflag name="-framework" value="AVFAudio" />
|
||||
|
||||
<files id="external-apple" />
|
||||
</target>
|
||||
</xml>
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Initializes the audio session for playback.
|
||||
*
|
||||
* This function configures the shared AVAudioSession instance with the following settings:
|
||||
* - Sets the audio session category to Playback, allowing Bluetooth A2DP and mixing with spoken audio interruptions.
|
||||
* - For iOS 17.0 and above, disables interruption on route disconnect.
|
||||
* - For iOS 14.5 and above, prefers no interruptions from system alerts.
|
||||
*/
|
||||
void Apple_AudioSession_Initialize();
|
||||
|
||||
/**
|
||||
* Sets the active state of the shared AVAudioSession.
|
||||
*
|
||||
* @param active Whether to activate or deactivate the audio session.
|
||||
*/
|
||||
void Apple_AudioSession_SetActive(bool active);
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
/**
|
||||
* Retrieves the current process's resident set size (RSS) in bytes on Apple platforms.
|
||||
*
|
||||
* @return The resident set size (RSS) in bytes if successful; otherwise, returns 0 on failure.
|
||||
*/
|
||||
size_t Apple_MemoryUtil_GetCurrentProcessRss();
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Retrieves the safe area insets for the current screen on iOS devices.
|
||||
*
|
||||
* This function populates the provided pointers with the safe area insets (in pixels) for the top, bottom, left, and right edges of the screen.
|
||||
* On iOS 11 and later, it uses the main window's safeAreaInsets and converts them to pixel values using the screen scale.
|
||||
* On earlier iOS versions, all insets are set to 0.0.
|
||||
*
|
||||
* @param top Pointer to a double to receive the top safe area inset (in pixels).
|
||||
* @param bottom Pointer to a double to receive the bottom safe area inset (in pixels).
|
||||
* @param left Pointer to a double to receive the left safe area inset (in pixels).
|
||||
* @param right Pointer to a double to receive the right safe area inset (in pixels).
|
||||
*/
|
||||
void Apple_ScreenUtil_GetSafeAreaInsets(double* top, double* bottom, double* left, double* right);
|
||||
|
||||
/**
|
||||
* Retrieves the size of the main screen in pixels.
|
||||
*
|
||||
* @param width Pointer to a double where the screen width (in pixels) will be stored.
|
||||
* @param height Pointer to a double where the screen height (in pixels) will be stored.
|
||||
*/
|
||||
void Apple_ScreenUtil_GetScreenSize(double* width, double* height);
|
||||
@@ -0,0 +1,43 @@
|
||||
#import "AudioSession.hpp"
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFAudio/AVFAudio.h>
|
||||
|
||||
void Apple_AudioSession_Initialize()
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
|
||||
NSError *error;
|
||||
|
||||
[session setCategory:AVAudioSessionCategoryPlayback mode:AVAudioSessionModeDefault options:AVAudioSessionCategoryOptionAllowBluetoothA2DP | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:&error];
|
||||
|
||||
if (@available(iOS 17.0, *))
|
||||
{
|
||||
[session setPrefersInterruptionOnRouteDisconnect:false error:nil];
|
||||
}
|
||||
|
||||
if (@available(iOS 14.5, *))
|
||||
{
|
||||
[session setPrefersNoInterruptionsFromSystemAlerts:true error:nil];
|
||||
}
|
||||
|
||||
if (error)
|
||||
NSLog(@"Unable to set category of audio session: %@", error);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Apple_AudioSession_SetActive(bool active)
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
|
||||
NSError *error;
|
||||
|
||||
[session setActive:active error:&error];
|
||||
|
||||
if (error)
|
||||
NSLog(@"Unable to set active of audio session: %@", error);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#import "MemoryUtil.hpp"
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
size_t Apple_MemoryUtil_GetCurrentProcessRss()
|
||||
{
|
||||
struct task_basic_info info;
|
||||
|
||||
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
|
||||
|
||||
if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count) != KERN_SUCCESS)
|
||||
return 0;
|
||||
|
||||
return info.resident_size;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#import "ScreenUtil.hpp"
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void Apple_ScreenUtil_GetSafeAreaInsets(double* top, double* bottom, double* left, double* right)
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
if (@available(iOS 11, *))
|
||||
{
|
||||
UIWindow* window = [UIApplication sharedApplication].windows[0];
|
||||
UIEdgeInsets safeAreaInsets = window.safeAreaInsets;
|
||||
float scale = [UIScreen mainScreen].scale;
|
||||
|
||||
(*top) = safeAreaInsets.top * scale;
|
||||
(*bottom) = safeAreaInsets.bottom * scale;
|
||||
(*left) = safeAreaInsets.left * scale;
|
||||
(*right) = safeAreaInsets.right * scale;
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
(*top) = 0.0;
|
||||
(*bottom) = 0.0;
|
||||
(*left) = 0.0;
|
||||
(*right) = 0.0;
|
||||
}
|
||||
|
||||
void Apple_ScreenUtil_GetScreenSize(double* width, double* height)
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
CGRect screenRect = [[UIScreen mainScreen] bounds];
|
||||
float scale = [UIScreen mainScreen].scale;
|
||||
|
||||
(*width) = (double)screenRect.size.width * scale;
|
||||
(*height) = (double)screenRect.size.height * scale;
|
||||
#else
|
||||
(*width) = 0.0;
|
||||
(*width) = 0.0;
|
||||
#endif
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package funkin.external.ios;
|
||||
|
||||
#if ios
|
||||
/**
|
||||
* A Utility class to manage iOS audio.
|
||||
*/
|
||||
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('AudioSession.hpp')
|
||||
@:unreflective
|
||||
extern class AudioSession
|
||||
{
|
||||
@:native('initialize')
|
||||
static function initialize():Void;
|
||||
@:native('setActive')
|
||||
static function setActive(active:Bool):Void;
|
||||
}
|
||||
#end
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package funkin.external.ios;
|
||||
|
||||
#if ios
|
||||
/**
|
||||
* A Utility class to get iOS screen related informations.
|
||||
*/
|
||||
@:build(funkin.util.macro.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('ScreenUtil.hpp')
|
||||
@:unreflective
|
||||
extern class ScreenUtil
|
||||
{
|
||||
@:native('getSafeAreaInsets')
|
||||
static function getSafeAreaInsets(top:cpp.RawPointer<Float>, bottom:cpp.RawPointer<Float>, left:cpp.RawPointer<Float>, right:cpp.RawPointer<Float>):Void;
|
||||
|
||||
@:native('getScreenSize')
|
||||
static function getScreenSize(width:cpp.RawPointer<Float>, height:cpp.RawPointer<Float>):Void;
|
||||
}
|
||||
#end
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xml>
|
||||
<pragma once="true" />
|
||||
|
||||
<files id="haxe">
|
||||
<compilerflag value="-I${this_dir}/ios/include" if="iphoneos || iphonesim" />
|
||||
</files>
|
||||
|
||||
<files id="__main__">
|
||||
<compilerflag value="-I${this_dir}/ios/include" if="iphoneos || iphonesim" />
|
||||
</files>
|
||||
|
||||
<files id="external-ios" dir="${this_dir}/ios" if="iphoneos || iphonesim">
|
||||
<compilerflag value="-I${this_dir}/ios/include" />
|
||||
|
||||
<file name="src/ScreenUtil.mm" />
|
||||
<file name="src/AudioSession.mm" />
|
||||
</files>
|
||||
|
||||
<target id="haxe">
|
||||
<section if="iphoneos || iphonesim">
|
||||
<vflag name="-framework" value="UIKit" />
|
||||
<vflag name="-framework" value="Foundation" />
|
||||
<vflag name="-framework" value="AVFAudio" />
|
||||
</section>
|
||||
|
||||
<files id="external-ios" if="iphoneos || iphonesim" />
|
||||
</target>
|
||||
</xml>
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
void initialize();
|
||||
void setActive(bool active);
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
void getSafeAreaInsets(double* top, double* bottom, double* left, double* right);
|
||||
void getScreenSize(double* width, double* height);
|
||||
@@ -1,41 +0,0 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFAudio/AVFAudio.h>
|
||||
|
||||
void initialize()
|
||||
{
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
|
||||
NSError *error;
|
||||
[session setCategory:AVAudioSessionCategoryPlayback
|
||||
mode:AVAudioSessionModeDefault
|
||||
options:AVAudioSessionCategoryOptionAllowBluetoothA2DP|AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers
|
||||
error:&error];
|
||||
|
||||
if (@available(iOS 17.0, *))
|
||||
{
|
||||
[session setPrefersInterruptionOnRouteDisconnect:false error:nil];
|
||||
}
|
||||
|
||||
if (@available(iOS 14.5, *))
|
||||
{
|
||||
[session setPrefersNoInterruptionsFromSystemAlerts:true error:nil];
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"Unable to set category of audio session: %@", error);
|
||||
}
|
||||
}
|
||||
|
||||
void setActive(bool active)
|
||||
{
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
NSError *error;
|
||||
|
||||
[session setActive:YES error:&error];
|
||||
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"Unable to set active of audio session: %@", error);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#import "ScreenUtil.hpp"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void getSafeAreaInsets(double* top, double* bottom, double* left, double* right)
|
||||
{
|
||||
if (@available(iOS 11, *))
|
||||
{
|
||||
UIWindow* window = [UIApplication sharedApplication].windows[0];
|
||||
UIEdgeInsets safeAreaInsets = window.safeAreaInsets;
|
||||
|
||||
float scale = [UIScreen mainScreen].scale;
|
||||
|
||||
(*top) = safeAreaInsets.top * scale;
|
||||
(*bottom) = safeAreaInsets.bottom * scale;
|
||||
(*left) = safeAreaInsets.left * scale;
|
||||
(*right) = safeAreaInsets.right * scale;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
(*top) = 0.0;
|
||||
(*bottom) = 0.0;
|
||||
(*left) = 0.0;
|
||||
(*right) = 0.0;
|
||||
}
|
||||
|
||||
void getScreenSize(double* width, double* height)
|
||||
{
|
||||
CGRect screenRect = [[UIScreen mainScreen] bounds];
|
||||
float scale = [UIScreen mainScreen].scale;
|
||||
|
||||
(*width) = (double)screenRect.size.width * scale;
|
||||
(*height) = (double)screenRect.size.height * scale;
|
||||
}
|
||||
+9
-1
@@ -56,10 +56,18 @@ extern class WinAPI
|
||||
@:native('WINAPI_DisableWindowsGhosting')
|
||||
static function disableWindowsGhosting():Void;
|
||||
|
||||
/**
|
||||
* Retrieves the current working set size (in bytes) of the process.
|
||||
*
|
||||
* @return The size of the working set memory used by the process.
|
||||
*/
|
||||
@:native('WINAPI_GetProcessMemoryWorkingSetSize')
|
||||
static function getProcessMemoryWorkingSetSize():cpp.SizeT;
|
||||
|
||||
/**
|
||||
* Sets the dark mode for the active window.
|
||||
*
|
||||
* @param enable A boolean value indicating whether to enable (true) or disable (false) dark mode.
|
||||
* @param enable Whether to enable or disable dark mode.
|
||||
*/
|
||||
@:native('WINAPI_SetDarkMode')
|
||||
static function setDarkMode(enable:Bool):Void;
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@
|
||||
<compilerflag value="-I${this_dir}/include" />
|
||||
</files>
|
||||
|
||||
<files id="winapi">
|
||||
<files id="external-windows">
|
||||
<compilerflag value="-I${this_dir}/include" />
|
||||
|
||||
<file name="${this_dir}/src/winapi.cpp" />
|
||||
@@ -24,13 +24,13 @@
|
||||
<lib name="-ladvapi32" />
|
||||
</section>
|
||||
|
||||
<section if="windows" unless="mingw">
|
||||
<section unless="mingw">
|
||||
<lib name="user32.lib" />
|
||||
<lib name="kernel32.lib" />
|
||||
<lib name="dwmapi.lib" />
|
||||
<lib name="advapi32.lib" />
|
||||
</section>
|
||||
|
||||
<files id="winapi" />
|
||||
<files id="external-windows" />
|
||||
</target>
|
||||
</xml>
|
||||
|
||||
+23
-8
@@ -1,51 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief Shows an error message box
|
||||
* Shows an error message box.
|
||||
*
|
||||
* @param message The error message to display
|
||||
* @param title The title of the message box
|
||||
*/
|
||||
void WINAPI_ShowError(const char *message, const char *title);
|
||||
|
||||
/**
|
||||
* @brief Shows a warning message box
|
||||
* Shows a warning message box.
|
||||
*
|
||||
* @param message The warning message to display
|
||||
* @param title The title of the message box
|
||||
*/
|
||||
void WINAPI_ShowWarning(const char *message, const char *title);
|
||||
|
||||
/**
|
||||
* @brief Shows an information message box
|
||||
* Shows an information message box.
|
||||
*
|
||||
* @param message The information message to display
|
||||
* @param title The title of the message box
|
||||
*/
|
||||
void WINAPI_ShowInformation(const char *message, const char *title);
|
||||
|
||||
/**
|
||||
* @brief Shows a question message box with OK/Cancel buttons
|
||||
* Shows a question message box with OK/Cancel buttons.
|
||||
*
|
||||
* @param message The question message to display
|
||||
* @param title The title of the message box
|
||||
*/
|
||||
void WINAPI_ShowQuestion(const char *message, const char *title);
|
||||
|
||||
/**
|
||||
* @brief Disables Windows error reporting dialogs
|
||||
* Disables Windows error reporting dialogs.
|
||||
*/
|
||||
void WINAPI_DisableErrorReporting();
|
||||
|
||||
/**
|
||||
* @brief Disables Windows ghosting for the current process
|
||||
* Disables Windows ghosting for the current process.
|
||||
*/
|
||||
void WINAPI_DisableWindowsGhosting();
|
||||
|
||||
/**
|
||||
* @brief Sets dark mode for the active window
|
||||
* Retrieves the current working set size (in bytes) of the calling process.
|
||||
*
|
||||
* This function queries the operating system for the amount of physical memory currently allocated to the process (its working set).
|
||||
*
|
||||
* @return The working set size in bytes. Returns 0 if the query fails.
|
||||
*/
|
||||
size_t WINAPI_GetProcessMemoryWorkingSetSize();
|
||||
|
||||
/**
|
||||
* Sets dark mode for the active window.
|
||||
*
|
||||
* @param enable True to enable dark mode, false to disable
|
||||
*/
|
||||
void WINAPI_SetDarkMode(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Checks if the system is using dark mode
|
||||
* Checks if the system is using dark mode.
|
||||
*
|
||||
* @return True if system is in dark mode, false otherwise
|
||||
*/
|
||||
bool WINAPI_IsSystemDarkMode();
|
||||
|
||||
@@ -41,6 +41,16 @@ void WINAPI_DisableWindowsGhosting()
|
||||
DisableProcessWindowsGhosting();
|
||||
}
|
||||
|
||||
size_t WINAPI_GetProcessMemoryWorkingSetSize()
|
||||
{
|
||||
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||
|
||||
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
|
||||
return pmc.WorkingSetSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WINAPI_SetDarkMode(bool enable)
|
||||
{
|
||||
HWND window = GetActiveWindow();
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package funkin.graphics.video;
|
||||
|
||||
/**
|
||||
* A script that can be tied to a FunkinVideoSprite.
|
||||
* Create a scripted class that extends FunkinVideoSprite to use this.
|
||||
*/
|
||||
#if hxvlc
|
||||
@:hscriptClass
|
||||
class ScriptedFunkinVideoSprite extends funkin.graphics.video.FunkinVideoSprite implements polymod.hscript.HScriptedClass {}
|
||||
#end
|
||||
@@ -7,11 +7,11 @@ import extension.admob.AdmobBannerSize;
|
||||
import extension.admob.AdmobEvent;
|
||||
import flixel.FlxG;
|
||||
import funkin.play.cutscene.VideoCutscene;
|
||||
import funkin.util.macro.EnvironmentConfigMacro;
|
||||
|
||||
/**
|
||||
* Provides utility functions for working with admob advertisements.
|
||||
*/
|
||||
@:build(funkin.util.macro.EnvironmentMacro.build())
|
||||
@:nullSafety
|
||||
class AdMobUtil
|
||||
{
|
||||
@@ -29,27 +29,20 @@ class AdMobUtil
|
||||
/**
|
||||
* AdMob publisher ID used for the application.
|
||||
*/
|
||||
static final ADMOB_PUBLISHER:String = EnvironmentConfigMacro.environmentConfig.get("MOBILE_GLOBAL_ADMOB_PUBLISHER");
|
||||
@:envField
|
||||
static final ADMOB_PUBLISHER:Null<String>;
|
||||
|
||||
/**
|
||||
* Test ad unit IDs for development and testing purposes.
|
||||
* These IDs are provided by Google AdMob for testing ads without incurring costs.
|
||||
* They should not be used in production applications.
|
||||
*/
|
||||
/**
|
||||
* Ad unit ID for displaying banner ads.
|
||||
*/
|
||||
static final BANNER_AD_UNIT_ID:String = #if mobile EnvironmentConfigMacro.environmentConfig.get(#if android "ANDROID_ADMOB_BANNER_ID" #else "IOS_ADMOB_BANNER_ID" #end) #else "" #end;
|
||||
@:envField
|
||||
static final ADMOB_BANNER_AD_UNIT_ID:Null<String>;
|
||||
|
||||
/**
|
||||
* Ad unit ID for displaying interstitial ads.
|
||||
*/
|
||||
static final INTERSTITIAL_AD_UNIT_ID:String = #if mobile EnvironmentConfigMacro.environmentConfig.get(#if android "ANDROID_ADMOB_INTERSTITIAL_ID" #else "IOS_ADMOB_INTERSTITIAL_ID" #end) #else "" #end;
|
||||
|
||||
/**
|
||||
* Ad unit ID for displaying rewarded ads.
|
||||
*/
|
||||
static final REWARDED_AD_UNIT_ID:String = "";
|
||||
@:envField
|
||||
static final ADMOB_INTERSTITIAL_AD_UNIT_ID:Null<String>;
|
||||
#else
|
||||
|
||||
/**
|
||||
@@ -67,7 +60,7 @@ class AdMobUtil
|
||||
* - Android: "9214589741" (test ad unit ID)
|
||||
* - iOS: "2435281174" (test ad unit ID)
|
||||
*/
|
||||
static final BANNER_AD_UNIT_ID:String = #if android "9214589741" #elseif ios "2435281174" #else "" #end;
|
||||
static final ADMOB_BANNER_AD_UNIT_ID:String = #if android "9214589741" #elseif ios "2435281174" #else "" #end;
|
||||
|
||||
/**
|
||||
* Ad unit ID for displaying interstitial ads.
|
||||
@@ -77,17 +70,7 @@ class AdMobUtil
|
||||
* - Android: "1033173712" (test ad unit ID)
|
||||
* - iOS: "4411468910" (test ad unit ID)
|
||||
*/
|
||||
static final INTERSTITIAL_AD_UNIT_ID:String = #if android "1033173712" #elseif ios "4411468910" #else "" #end;
|
||||
|
||||
/**
|
||||
* Ad unit ID for displaying rewarded ads.
|
||||
* Test IDs are used for Android and iOS platforms, while non-supported platforms default to an empty string.
|
||||
* Replace with your actual interstitial video ad unit ID for production.
|
||||
*
|
||||
* - Android: "8691691433" (test ad unit ID)
|
||||
* - iOS: "5135589807" (test ad unit ID)
|
||||
*/
|
||||
static final REWARDED_AD_UNIT_ID:String = #if android "8691691433" #elseif ios "5135589807" #else "" #end;
|
||||
static final ADMOB_INTERSTITIAL_AD_UNIT_ID:String = #if android "1033173712" #elseif ios "4411468910" #else "" #end;
|
||||
#end
|
||||
|
||||
/**
|
||||
@@ -129,7 +112,7 @@ class AdMobUtil
|
||||
}
|
||||
#end
|
||||
|
||||
trace(event.toString());
|
||||
Sys.println(event.toString());
|
||||
});
|
||||
|
||||
Admob.configureConsentMetadata(Admob.getTCFConsentForPurpose(0) == 1, StringTools.startsWith(Admob.getUSPrivacy(), '1Y'));
|
||||
@@ -145,9 +128,13 @@ class AdMobUtil
|
||||
public static inline function addBanner(size:Int = AdmobBannerSize.BANNER, align:Int = AdmobBannerAlign.BOTTOM_CENTER):Void
|
||||
{
|
||||
#if FEATURE_MOBILE_IAP
|
||||
if (InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID)) return;
|
||||
if (InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#end
|
||||
Admob.showBanner([AdMobUtil.ADMOB_PUBLISHER, AdMobUtil.BANNER_AD_UNIT_ID].join('/'), size, align);
|
||||
|
||||
Admob.showBanner([AdMobUtil.ADMOB_PUBLISHER, AdMobUtil.ADMOB_BANNER_AD_UNIT_ID].join('/'), size, align);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,8 +143,12 @@ class AdMobUtil
|
||||
public static inline function removeBanner():Void
|
||||
{
|
||||
#if FEATURE_MOBILE_IAP
|
||||
if (InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID)) return;
|
||||
if (InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#end
|
||||
|
||||
Admob.hideBanner();
|
||||
}
|
||||
|
||||
@@ -195,44 +186,7 @@ class AdMobUtil
|
||||
|
||||
Admob.onEvent.add(interstitialEvent);
|
||||
|
||||
Admob.loadInterstitial([AdMobUtil.ADMOB_PUBLISHER, AdMobUtil.INTERSTITIAL_AD_UNIT_ID].join('/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a rewarded ad using Admob.
|
||||
*
|
||||
* @param onRewardedFinish Callback function to be called when the rewarded ad has been completed by the user.
|
||||
*/
|
||||
public static function loadRewarded(onRewardedFinish:Void->Void):Void
|
||||
{
|
||||
#if FEATURE_MOBILE_IAP
|
||||
if (InAppPurchasesUtil.isPurchased(InAppPurchasesUtil.UPGRADE_PRODUCT_ID))
|
||||
{
|
||||
if (onRewardedFinish != null) onRewardedFinish();
|
||||
|
||||
return;
|
||||
}
|
||||
#end
|
||||
|
||||
function rewardedEvent(event:AdmobEvent):Void
|
||||
{
|
||||
if (event.name == AdmobEvent.REWARDED_LOADED)
|
||||
{
|
||||
Admob.showRewarded();
|
||||
}
|
||||
else if (event.name == AdmobEvent.REWARDED_DISMISSED
|
||||
|| event.name == AdmobEvent.REWARDED_FAILED_TO_LOAD
|
||||
|| event.name == AdmobEvent.REWARDED_FAILED_TO_SHOW)
|
||||
{
|
||||
if (onRewardedFinish != null) onRewardedFinish();
|
||||
|
||||
Admob.onEvent.remove(rewardedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
Admob.onEvent.add(rewardedEvent);
|
||||
|
||||
Admob.loadRewarded([AdMobUtil.ADMOB_PUBLISHER, AdMobUtil.REWARDED_AD_UNIT_ID].join('/'));
|
||||
Admob.loadInterstitial([AdMobUtil.ADMOB_PUBLISHER, AdMobUtil.ADMOB_INTERSTITIAL_AD_UNIT_ID].join('/'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package funkin.mobile.util;
|
||||
|
||||
#if ios
|
||||
import funkin.external.ios.ScreenUtil as NativeScreenUtil;
|
||||
import funkin.external.apple.ScreenUtil as NativeScreenUtil;
|
||||
#elseif android
|
||||
import funkin.external.android.ScreenUtil as NativeScreenUtil;
|
||||
#end
|
||||
|
||||
@@ -15,7 +15,7 @@ import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.stickers.StickerRegistry;
|
||||
import funkin.data.freeplay.album.AlbumRegistry;
|
||||
import funkin.modding.module.ModuleHandler;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.save.Save;
|
||||
import funkin.util.FileUtil;
|
||||
import funkin.util.macro.ClassMacro;
|
||||
@@ -271,6 +271,8 @@ class PolymodHandler
|
||||
Polymod.addImportAlias('funkin.data.dialogue.conversation.ConversationRegistry', funkin.data.dialogue.ConversationRegistry);
|
||||
Polymod.addImportAlias('funkin.data.dialogue.dialoguebox.DialogueBoxRegistry', funkin.data.dialogue.DialogueBoxRegistry);
|
||||
Polymod.addImportAlias('funkin.data.dialogue.speaker.SpeakerRegistry', funkin.data.dialogue.SpeakerRegistry);
|
||||
Polymod.addImportAlias('funkin.play.character.CharacterDataParser', funkin.data.character.CharacterData.CharacterDataParser);
|
||||
Polymod.addImportAlias('funkin.play.character.CharacterData.CharacterDataParser', funkin.data.character.CharacterData.CharacterDataParser);
|
||||
|
||||
// `funkin.util.FileUtil` has unrestricted access to the file system.
|
||||
Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtilSandboxed);
|
||||
@@ -379,9 +381,6 @@ class PolymodHandler
|
||||
// Can load native processes on the host operating system.
|
||||
Polymod.blacklistImport('openfl.desktop.NativeProcess');
|
||||
|
||||
// Contains critical private environment variables.
|
||||
Polymod.blacklistImport('funkin.util.macro.EnvironmentConfigMacro');
|
||||
|
||||
// `funkin.api.*`
|
||||
// Contains functions which may allow for cheating and such.
|
||||
for (cls in ClassMacro.listClassesInPackage('funkin.api'))
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package funkin.modding.base;
|
||||
|
||||
/**
|
||||
* A script that can be tied to a FlxStrip.
|
||||
* Create a scripted class that extends FlxStrip to use this.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedFlxStrip extends flixel.FlxStrip implements HScriptedClass {}
|
||||
@@ -0,0 +1,8 @@
|
||||
package funkin.modding.base;
|
||||
|
||||
/**
|
||||
* A script that can be tied to an FlxTypedGroup.
|
||||
* Create a scripted class that extends FlxTypedGroup to use this.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedFlxTypedGroup extends flixel.group.FlxGroup.FlxTypedGroup<Dynamic> implements HScriptedClass {}
|
||||
@@ -2,6 +2,7 @@ package funkin.modding.events;
|
||||
|
||||
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
||||
import funkin.modding.IScriptedClass;
|
||||
import funkin.modding.module.Module;
|
||||
|
||||
/**
|
||||
* Utility functions to assist with handling scripted classes.
|
||||
@@ -32,6 +33,13 @@ class ScriptEventDispatcher
|
||||
case CREATE:
|
||||
target.onCreate(event);
|
||||
return;
|
||||
case STATE_CREATE:
|
||||
if (Std.isOfType(target, Module))
|
||||
{
|
||||
var t:Module = cast(target, Module);
|
||||
t.onStateCreate(event);
|
||||
}
|
||||
return;
|
||||
case DESTROY:
|
||||
target.onDestroy(event);
|
||||
return;
|
||||
|
||||
@@ -12,6 +12,14 @@ enum abstract ScriptEventType(String) from String to String
|
||||
*/
|
||||
var CREATE = 'CREATE';
|
||||
|
||||
/**
|
||||
* Called when the relevant object is fully created and ready to be used.
|
||||
* This assumes all data is loaded and ready to go.
|
||||
*
|
||||
* This event is not cancelable.
|
||||
*/
|
||||
var STATE_CREATE = 'STATE_CREATE';
|
||||
|
||||
/**
|
||||
* Called when the relevant object is destroyed.
|
||||
* This should perform relevant cleanup to ensure good performance.
|
||||
|
||||
@@ -234,4 +234,8 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
|
||||
* Called when the song has been restarted.
|
||||
*/
|
||||
public function onSongRetry(event:SongRetryEvent) {}
|
||||
/**
|
||||
* Called when any state is created.
|
||||
*/
|
||||
public function onStateCreate(event:ScriptEvent) {}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import funkin.modding.events.ScriptEvent;
|
||||
import funkin.api.newgrounds.Events;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.components.HealthIcon;
|
||||
import funkin.play.components.PopUpStuff;
|
||||
import funkin.play.cutscene.dialogue.Conversation;
|
||||
@@ -972,6 +972,8 @@ class PlayState extends MusicBeatSubState
|
||||
|
||||
previousDifficulty = currentDifficulty;
|
||||
|
||||
currentStage?.resetStage();
|
||||
|
||||
dispatchEvent(retryEvent);
|
||||
|
||||
resetCamera();
|
||||
@@ -1016,8 +1018,6 @@ class PlayState extends MusicBeatSubState
|
||||
vocals.opponentVolume = 1;
|
||||
}
|
||||
|
||||
currentStage?.resetStage();
|
||||
|
||||
if (!fromDeathState)
|
||||
{
|
||||
playerStrumline.vwooshNotes();
|
||||
@@ -1114,7 +1114,7 @@ class PlayState extends MusicBeatSubState
|
||||
|
||||
var pauseButtonCheck:Bool = false;
|
||||
var androidPause:Bool = false;
|
||||
// So the player wouldn't miss when pressing the pause utton
|
||||
// So the player wouldn't miss when pressing the pause button
|
||||
#if mobile
|
||||
pauseButtonCheck = TouchUtil.pressAction(pauseButton);
|
||||
#end
|
||||
@@ -1424,9 +1424,6 @@ class PlayState extends MusicBeatSubState
|
||||
// super.dispatchEvent(event) dispatches event to module scripts.
|
||||
super.dispatchEvent(event);
|
||||
|
||||
// Dispatch event to note kind scripts
|
||||
NoteKindManager.callEvent(event);
|
||||
|
||||
// Dispatch event to stage script.
|
||||
ScriptEventDispatcher.callEvent(currentStage, event);
|
||||
|
||||
@@ -1438,6 +1435,9 @@ class PlayState extends MusicBeatSubState
|
||||
|
||||
// Dispatch event to conversation script.
|
||||
ScriptEventDispatcher.callEvent(currentConversation, event);
|
||||
|
||||
// Dispatch event to note kind scripts
|
||||
NoteKindManager.callEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3174,7 +3174,7 @@ class PlayState extends MusicBeatSubState
|
||||
#end
|
||||
|
||||
#if mobile
|
||||
pauseButtonCheck = TouchUtil.pressAction(pauseButton);
|
||||
pauseButtonCheck = TouchUtil.overlapsComplex(pauseButton);
|
||||
#end
|
||||
|
||||
if (currentConversation != null)
|
||||
@@ -3534,6 +3534,8 @@ class PlayState extends MusicBeatSubState
|
||||
|
||||
forEachPausedSound((s) -> s.destroy());
|
||||
|
||||
if (VideoCutscene.isPlaying()) VideoCutscene.destroyVideo();
|
||||
|
||||
FlxTween.globalManager.clear();
|
||||
FlxTimer.globalManager.clear();
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ package funkin.play;
|
||||
class PlayStatePlaylist
|
||||
{
|
||||
/**
|
||||
* Whether the game is currently in Story Mode. If false, we are in Free Play Mode.
|
||||
* Whether the game is currently in Story Mode. If false, we are in Freeplay Mode.
|
||||
*/
|
||||
public static var isStoryMode:Bool = false;
|
||||
|
||||
/**
|
||||
* The loist of upcoming songs to be played.
|
||||
* The list of upcoming songs to be played.
|
||||
* When the user completes a song in Story Mode, the first entry in this list is played.
|
||||
* When this list is empty, move to the Results screen instead.
|
||||
*/
|
||||
@@ -26,13 +26,13 @@ class PlayStatePlaylist
|
||||
public static var campaignScore:Int = 0;
|
||||
|
||||
/**
|
||||
* The title of this playlist, for example `Week 4` or `Weekend 1`
|
||||
* The title of this playlist, for example `Week 4` or `Weekend 1`.
|
||||
*/
|
||||
public static var campaignTitle:String = 'UNKNOWN';
|
||||
|
||||
/**
|
||||
* The internal ID of the current playlist, for example `week4` or `weekend-1`.
|
||||
* @default `null`, used when no playlist is loaded
|
||||
* @default `null`, used when no playlist is loaded.
|
||||
*/
|
||||
public static var campaignId:Null<String> = null;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import flixel.util.FlxColor;
|
||||
import flixel.util.FlxDestroyUtil;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.play.character.CharacterData.CharacterRenderType;
|
||||
import funkin.data.character.CharacterData.CharacterRenderType;
|
||||
import flixel.util.FlxDirectionFlags;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.BlendMode;
|
||||
|
||||
@@ -2,8 +2,9 @@ package funkin.play.character;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.character.CharacterData.CharacterRenderType;
|
||||
import funkin.data.character.CharacterData;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterRenderType;
|
||||
import funkin.play.stage.Bopper;
|
||||
import funkin.play.notes.NoteDirection;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
import funkin.play.character.CharacterData.CharacterRenderType;
|
||||
import funkin.data.character.CharacterData.CharacterRenderType;
|
||||
|
||||
/**
|
||||
* For some characters which use Sparrow atlases, the spritesheets need to be split
|
||||
|
||||
@@ -2,7 +2,7 @@ package funkin.play.character;
|
||||
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.play.character.CharacterData.CharacterRenderType;
|
||||
import funkin.data.character.CharacterData.CharacterRenderType;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@ package funkin.play.character;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import funkin.play.character.CharacterData.CharacterRenderType;
|
||||
import funkin.data.character.CharacterData.CharacterRenderType;
|
||||
|
||||
/**
|
||||
* A SparrowCharacter is a Character which is rendered by
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package funkin.play.components;
|
||||
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.data.character.CharacterData;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.util.MathUtil;
|
||||
|
||||
|
||||
@@ -347,6 +347,38 @@ class VideoCutscene
|
||||
// throw "Not implemented!";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the active cutscene, if any. Separate from finishVideo() so that it doesn't trigger onCutsceneFinish().
|
||||
*/
|
||||
public static function destroyVideo()
|
||||
{
|
||||
#if html5
|
||||
if (vid != null) PlayState.instance.remove(vid);
|
||||
#end
|
||||
|
||||
#if hxvlc
|
||||
if (vid != null)
|
||||
{
|
||||
vid.stop();
|
||||
PlayState.instance.remove(vid);
|
||||
}
|
||||
#end
|
||||
|
||||
#if (html5 || hxvlc)
|
||||
if (vid != null)
|
||||
{
|
||||
vid?.destroy();
|
||||
vid = null;
|
||||
}
|
||||
#end
|
||||
|
||||
if (blackScreen != null)
|
||||
{
|
||||
PlayState.instance.remove(blackScreen);
|
||||
blackScreen = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CutsceneType
|
||||
|
||||
@@ -10,7 +10,6 @@ import funkin.modding.events.ScriptEvent;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.modding.IScriptedClass.IDialogueScriptedClass;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.ui.FullScreenScaleMode;
|
||||
import funkin.data.dialogue.DialogueBoxData;
|
||||
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||
|
||||
@@ -28,6 +27,11 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
||||
*/
|
||||
var animationOffsets:Map<String, Array<Float>> = new Map<String, Array<Float>>();
|
||||
|
||||
/**
|
||||
* The position without offsets applied.
|
||||
*/
|
||||
var originalPosition:Array<Float> = [0, 0];
|
||||
|
||||
/**
|
||||
* The current animation offset being used.
|
||||
*/
|
||||
@@ -36,13 +40,10 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
||||
function set_animOffsets(value:Array<Float>):Array<Float>
|
||||
{
|
||||
if (animOffsets == null) animOffsets = [0, 0];
|
||||
if ((animOffsets[0] == value[0]) && (animOffsets[1] == value[1])) return value;
|
||||
if (animOffsets == value) return value;
|
||||
|
||||
var xDiff:Float = value[0] - animOffsets[0];
|
||||
var yDiff:Float = value[1] - animOffsets[1];
|
||||
|
||||
this.x += xDiff;
|
||||
this.y += yDiff;
|
||||
this.x = globalOffsets[0] + originalPosition[0] + value[0];
|
||||
this.y = globalOffsets[1] + originalPosition[1] + value[1];
|
||||
|
||||
return animOffsets = value;
|
||||
}
|
||||
@@ -57,17 +58,13 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
||||
if (globalOffsets == null) globalOffsets = [0, 0];
|
||||
if (globalOffsets == value) return value;
|
||||
|
||||
var xDiff:Float = value[0] - globalOffsets[0];
|
||||
var yDiff:Float = value[1] - globalOffsets[1];
|
||||
this.screenCenter();
|
||||
|
||||
this.x += xDiff;
|
||||
this.y += yDiff;
|
||||
originalPosition[0] = this.x;
|
||||
originalPosition[1] = this.y;
|
||||
|
||||
if (FullScreenScaleMode.wideScale.x != 1)
|
||||
{
|
||||
this.x *= fullscreenScale;
|
||||
this.y = this.y * fullscreenScale + (-100 * fullscreenScale);
|
||||
}
|
||||
this.x = value[0] + originalPosition[0] + animOffsets[0];
|
||||
this.y = value[1] + originalPosition[1] + animOffsets[1];
|
||||
|
||||
return globalOffsets = value;
|
||||
}
|
||||
@@ -96,16 +93,6 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
||||
return this.speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* A value used for scaling object's parameters on mobile.
|
||||
*/
|
||||
var fullscreenScale(get, never):Float;
|
||||
|
||||
function get_fullscreenScale():Float
|
||||
{
|
||||
return FullScreenScaleMode.wideScale.x - 0.05;
|
||||
}
|
||||
|
||||
public function new(id:String, ?params:Dynamic)
|
||||
{
|
||||
super();
|
||||
@@ -120,9 +107,9 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
||||
|
||||
public function onCreate(event:ScriptEvent):Void
|
||||
{
|
||||
this.globalOffsets = [0, 0];
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.globalOffsets = [0, 0];
|
||||
this.alpha = 1;
|
||||
|
||||
loadSpritesheet();
|
||||
@@ -207,11 +194,6 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
||||
{
|
||||
if (scale == null) scale = 1.0;
|
||||
|
||||
if (FullScreenScaleMode.wideScale.x != 1)
|
||||
{
|
||||
scale *= fullscreenScale;
|
||||
}
|
||||
|
||||
this.boxSprite.scale.x = scale;
|
||||
this.boxSprite.scale.y = scale;
|
||||
this.boxSprite.updateHitbox();
|
||||
@@ -241,6 +223,9 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
||||
{
|
||||
super.revive();
|
||||
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.globalOffsets = [0, 0];
|
||||
this.visible = true;
|
||||
this.alpha = 1.0;
|
||||
}
|
||||
@@ -309,13 +294,6 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple
|
||||
textDisplay.x += _data.text.offsets[0];
|
||||
textDisplay.y += _data.text.offsets[1];
|
||||
|
||||
if (FullScreenScaleMode.wideScale.x != 1)
|
||||
{
|
||||
textDisplay.fieldWidth *= fullscreenScale;
|
||||
textDisplay.x *= fullscreenScale;
|
||||
textDisplay.y *= fullscreenScale;
|
||||
}
|
||||
|
||||
add(textDisplay);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import funkin.util.assets.FlxAnimationUtil;
|
||||
import funkin.modding.IScriptedClass.IDialogueScriptedClass;
|
||||
import funkin.data.dialogue.SpeakerData;
|
||||
import funkin.data.dialogue.SpeakerRegistry;
|
||||
import funkin.ui.FullScreenScaleMode;
|
||||
|
||||
/**
|
||||
* The character sprite which displays during dialogue.
|
||||
@@ -32,6 +31,11 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||
*/
|
||||
var animationOffsets:Map<String, Array<Float>> = new Map<String, Array<Float>>();
|
||||
|
||||
/**
|
||||
* The position without offsets applied.
|
||||
*/
|
||||
var originalPosition:Array<Float> = [0, 0];
|
||||
|
||||
/**
|
||||
* The current animation offset being used.
|
||||
*/
|
||||
@@ -40,13 +44,10 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||
function set_animOffsets(value:Array<Float>):Array<Float>
|
||||
{
|
||||
if (animOffsets == null) animOffsets = [0, 0];
|
||||
if ((animOffsets[0] == value[0]) && (animOffsets[1] == value[1])) return value;
|
||||
if (animOffsets == value) return value;
|
||||
|
||||
var xDiff:Float = value[0] - animOffsets[0];
|
||||
var yDiff:Float = value[1] - animOffsets[1];
|
||||
|
||||
this.x += xDiff;
|
||||
this.y += yDiff;
|
||||
this.x = globalOffsets[0] + originalPosition[0] + value[0];
|
||||
this.y = globalOffsets[1] + originalPosition[1] + value[1];
|
||||
|
||||
return animOffsets = value;
|
||||
}
|
||||
@@ -61,31 +62,12 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||
if (globalOffsets == null) globalOffsets = [0, 0];
|
||||
if (globalOffsets == value) return value;
|
||||
|
||||
var xDiff:Float = value[0] - globalOffsets[0];
|
||||
var yDiff:Float = value[1] - globalOffsets[1];
|
||||
|
||||
this.x += xDiff;
|
||||
this.y += yDiff;
|
||||
|
||||
if (FullScreenScaleMode.wideScale.x != 1)
|
||||
{
|
||||
this.x *= fullscreenScale;
|
||||
this.y = this.y * fullscreenScale + (-100 * fullscreenScale);
|
||||
}
|
||||
this.x = value[0] + originalPosition[0] + animOffsets[0];
|
||||
this.y = value[1] + originalPosition[1] + animOffsets[1];
|
||||
|
||||
return globalOffsets = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* A value used for scaling object's parameters on mobile.
|
||||
*/
|
||||
var fullscreenScale(get, never):Float;
|
||||
|
||||
function get_fullscreenScale():Float
|
||||
{
|
||||
return FullScreenScaleMode.wideScale.x - 0.05;
|
||||
}
|
||||
|
||||
public function new(id:String, ?params:Dynamic)
|
||||
{
|
||||
super();
|
||||
@@ -105,9 +87,9 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||
*/
|
||||
public function onCreate(event:ScriptEvent):Void
|
||||
{
|
||||
this.globalOffsets = [0, 0];
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.globalOffsets = [0, 0];
|
||||
this.alpha = 1;
|
||||
|
||||
loadSpritesheet();
|
||||
@@ -127,6 +109,9 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||
{
|
||||
super.revive();
|
||||
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.globalOffsets = [0, 0];
|
||||
this.visible = true;
|
||||
this.alpha = 1.0;
|
||||
|
||||
@@ -156,6 +141,11 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||
this.antialiasing = true;
|
||||
}
|
||||
|
||||
this.screenCenter();
|
||||
|
||||
originalPosition[0] = this.x;
|
||||
originalPosition[1] = this.y;
|
||||
|
||||
this.flipX = _data.flipX;
|
||||
this.flipY = _data.flipY;
|
||||
this.globalOffsets = _data.offsets;
|
||||
@@ -170,11 +160,6 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||
{
|
||||
if (scale == null) scale = 1.0;
|
||||
|
||||
if (FullScreenScaleMode.wideScale.x != 1)
|
||||
{
|
||||
scale *= fullscreenScale;
|
||||
}
|
||||
|
||||
this.scale.x = scale;
|
||||
this.scale.y = scale;
|
||||
this.updateHitbox();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package funkin.play.event;
|
||||
|
||||
import funkin.data.event.SongEventSchema;
|
||||
import funkin.play.character.CharacterData.HealthIconData;
|
||||
import funkin.data.character.CharacterData.HealthIconData;
|
||||
import funkin.data.song.SongData;
|
||||
import funkin.data.song.SongData.SongEventData;
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package funkin.play.notes;
|
||||
|
||||
/**
|
||||
* A script that can be tied to a Strumline.
|
||||
* Create a scripted class that extends Strumline to use this.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedStrumline extends funkin.play.notes.Strumline implements polymod.hscript.HScriptedClass {}
|
||||
@@ -142,9 +142,15 @@ class Strumline extends FlxSpriteGroup
|
||||
|
||||
var background:FunkinSprite;
|
||||
|
||||
var strumlineNotes:FlxTypedSpriteGroup<StrumlineNote>;
|
||||
/**
|
||||
* The strumline notes (the receptors) themselves.
|
||||
*/
|
||||
public var strumlineNotes:FlxTypedSpriteGroup<StrumlineNote>;
|
||||
var noteSplashes:FlxTypedSpriteGroup<NoteSplash>;
|
||||
var noteHoldCovers:FlxTypedSpriteGroup<NoteHoldCover>;
|
||||
/**
|
||||
* Hold note covers.
|
||||
*/
|
||||
public var noteHoldCovers:FlxTypedSpriteGroup<NoteHoldCover>;
|
||||
|
||||
var notesVwoosh:FlxTypedSpriteGroup<NoteSprite>;
|
||||
var holdNotesVwoosh:FlxTypedSpriteGroup<SustainTrail>;
|
||||
|
||||
@@ -846,61 +846,91 @@ class SongDifficulty
|
||||
{
|
||||
var suffix:String = (variation != null && variation != '' && variation != 'default') ? '-$variation' : '';
|
||||
|
||||
// Automatically resolve voices by removing suffixes.
|
||||
// For example, if `Voices-bf-car-erect.ogg` does not exist, check for `Voices-bf-erect.ogg`.
|
||||
// Then, check for `Voices-bf-car.ogg`, then `Voices-bf.ogg`.
|
||||
|
||||
if (characters.playerVocals == null)
|
||||
{
|
||||
var playerId:String = characters.player;
|
||||
var playerVoice:String = Paths.voices(this.song.id, '-${playerId}$suffix');
|
||||
|
||||
while (playerVoice != null && !Assets.exists(playerVoice))
|
||||
{
|
||||
// Remove the last suffix.
|
||||
// For example, bf-car becomes bf.
|
||||
playerId = playerId.split('-').slice(0, -1).join('-');
|
||||
// Try again.
|
||||
playerVoice = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix');
|
||||
}
|
||||
if (playerVoice == null)
|
||||
{
|
||||
// Try again without $suffix.
|
||||
playerId = characters.player;
|
||||
playerVoice = Paths.voices(this.song.id, '-${playerId}');
|
||||
while (playerVoice != null && !Assets.exists(playerVoice))
|
||||
{
|
||||
// Remove the last suffix.
|
||||
playerId = playerId.split('-').slice(0, -1).join('-');
|
||||
// Try again.
|
||||
playerVoice = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix');
|
||||
}
|
||||
}
|
||||
|
||||
return playerVoice != null ? [playerVoice] : [];
|
||||
}
|
||||
else
|
||||
if (characters.playerVocals != null)
|
||||
{
|
||||
// The metadata explicitly defines the list of voices.
|
||||
var playerIds:Array<String> = characters?.playerVocals ?? [characters.player];
|
||||
var playerVoices:Array<String> = playerIds.map((id) -> Paths.voices(this.song.id, '-$id$suffix'));
|
||||
var validVoices:Bool = true;
|
||||
|
||||
return playerVoices;
|
||||
// Check if all voice paths exist before returning
|
||||
// If not, fallback to the default method for resolving voices
|
||||
for (voice in playerVoices)
|
||||
{
|
||||
if (voice == null || !Assets.exists(voice)) validVoices = false;
|
||||
}
|
||||
if (validVoices) return playerVoices;
|
||||
}
|
||||
|
||||
// Automatically resolve voices by removing suffixes.
|
||||
// For example, if `Voices-bf-car-erect.ogg` does not exist, check for `Voices-bf-erect.ogg`.
|
||||
// Then, check for `Voices-bf-car.ogg`, then `Voices-bf.ogg`.
|
||||
var playerId:String = characters.player;
|
||||
var playerVoice:String = Paths.voices(this.song.id, '-${playerId}$suffix');
|
||||
|
||||
while (playerVoice != null && !Assets.exists(playerVoice))
|
||||
{
|
||||
// Remove the last suffix.
|
||||
// For example, bf-car becomes bf.
|
||||
playerId = playerId.split('-').slice(0, -1).join('-');
|
||||
// Try again.
|
||||
playerVoice = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix');
|
||||
}
|
||||
if (playerVoice == null)
|
||||
{
|
||||
// Try again without $suffix.
|
||||
playerId = characters.player;
|
||||
playerVoice = Paths.voices(this.song.id, '-${playerId}');
|
||||
while (playerVoice != null && !Assets.exists(playerVoice))
|
||||
{
|
||||
// Remove the last suffix.
|
||||
playerId = playerId.split('-').slice(0, -1).join('-');
|
||||
// Try again.
|
||||
playerVoice = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix');
|
||||
}
|
||||
}
|
||||
|
||||
return playerVoice != null ? [playerVoice] : [];
|
||||
}
|
||||
|
||||
public function buildOpponentVoiceList():Array<String>
|
||||
{
|
||||
var suffix:String = (variation != null && variation != '' && variation != 'default') ? '-$variation' : '';
|
||||
|
||||
if (characters.opponentVocals != null)
|
||||
{
|
||||
// The metadata explicitly defines the list of voices.
|
||||
var opponentIds:Array<String> = characters?.opponentVocals ?? [characters.opponent];
|
||||
var opponentVoices:Array<String> = opponentIds.map((id) -> Paths.voices(this.song.id, '-$id$suffix'));
|
||||
var validVoices:Bool = true;
|
||||
|
||||
// Check if all voice paths exist before returning
|
||||
// If not, fallback to the default method for resolving voices
|
||||
for (voice in opponentVoices)
|
||||
{
|
||||
if (voice == null || !Assets.exists(voice)) validVoices = false;
|
||||
}
|
||||
if (validVoices) return opponentVoices;
|
||||
}
|
||||
|
||||
// Automatically resolve voices by removing suffixes.
|
||||
// For example, if `Voices-bf-car-erect.ogg` does not exist, check for `Voices-bf-erect.ogg`.
|
||||
// Then, check for `Voices-bf-car.ogg`, then `Voices-bf.ogg`.
|
||||
|
||||
if (characters.opponentVocals == null)
|
||||
var opponentId:String = characters.opponent;
|
||||
var opponentVoice:String = Paths.voices(this.song.id, '-${opponentId}$suffix');
|
||||
while (opponentVoice != null && !Assets.exists(opponentVoice))
|
||||
{
|
||||
var opponentId:String = characters.opponent;
|
||||
var opponentVoice:String = Paths.voices(this.song.id, '-${opponentId}$suffix');
|
||||
// Remove the last suffix.
|
||||
opponentId = opponentId.split('-').slice(0, -1).join('-');
|
||||
// Try again.
|
||||
opponentVoice = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix');
|
||||
}
|
||||
if (opponentVoice == null)
|
||||
{
|
||||
// Try again without $suffix.
|
||||
opponentId = characters.opponent;
|
||||
opponentVoice = Paths.voices(this.song.id, '-${opponentId}');
|
||||
while (opponentVoice != null && !Assets.exists(opponentVoice))
|
||||
{
|
||||
// Remove the last suffix.
|
||||
@@ -908,30 +938,9 @@ class SongDifficulty
|
||||
// Try again.
|
||||
opponentVoice = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix');
|
||||
}
|
||||
if (opponentVoice == null)
|
||||
{
|
||||
// Try again without $suffix.
|
||||
opponentId = characters.opponent;
|
||||
opponentVoice = Paths.voices(this.song.id, '-${opponentId}');
|
||||
while (opponentVoice != null && !Assets.exists(opponentVoice))
|
||||
{
|
||||
// Remove the last suffix.
|
||||
opponentId = opponentId.split('-').slice(0, -1).join('-');
|
||||
// Try again.
|
||||
opponentVoice = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix');
|
||||
}
|
||||
}
|
||||
|
||||
return opponentVoice != null ? [opponentVoice] : [];
|
||||
}
|
||||
else
|
||||
{
|
||||
// The metadata explicitly defines the list of voices.
|
||||
var opponentIds:Array<String> = characters?.opponentVocals ?? [characters.opponent];
|
||||
var opponentVoices:Array<String> = opponentIds.map((id) -> Paths.voices(this.song.id, '-$id$suffix'));
|
||||
|
||||
return opponentVoices;
|
||||
}
|
||||
return opponentVoice != null ? [opponentVoice] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package funkin.play.stage;
|
||||
|
||||
/**
|
||||
* A script that can be tied to StageProp.
|
||||
* Create a scripted class that extends StageProp to use this.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedStageProp extends funkin.play.stage.StageProp implements polymod.hscript.HScriptedClass {}
|
||||
@@ -2,7 +2,7 @@ package funkin.save;
|
||||
|
||||
import flixel.util.FlxSave;
|
||||
import funkin.input.Controls.Device;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.scoring.Scoring;
|
||||
import funkin.play.scoring.Scoring.ScoringRank;
|
||||
import funkin.save.migrator.RawSaveData_v1_0_0;
|
||||
@@ -122,7 +122,8 @@ class Save implements ConsoleClass
|
||||
downscroll: false,
|
||||
flashingLights: true,
|
||||
zoomCamera: true,
|
||||
debugDisplay: false,
|
||||
debugDisplay: 'Off',
|
||||
debugDisplayBGOpacity: 50,
|
||||
hapticsMode: 'All',
|
||||
hapticsIntensityMultiplier: 1,
|
||||
autoPause: true,
|
||||
@@ -1606,9 +1607,15 @@ typedef SaveDataOptions =
|
||||
|
||||
/**
|
||||
* If enabled, an FPS and memory counter will be displayed even if this is not a debug build.
|
||||
* @default `false`
|
||||
* @default `Off`
|
||||
*/
|
||||
var debugDisplay:Bool;
|
||||
var debugDisplay:String;
|
||||
|
||||
/**
|
||||
* Opacity of the debug display's background.
|
||||
* @default `50`
|
||||
*/
|
||||
var debugDisplayBGOpacity:Int;
|
||||
|
||||
/**
|
||||
* If enabled, haptic feedback will be enabled.
|
||||
|
||||
@@ -140,6 +140,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
|
||||
|
||||
Conductor.beatHit.add(this.beatHit);
|
||||
Conductor.stepHit.add(this.stepHit);
|
||||
dispatchEvent(new ScriptEvent(STATE_CREATE));
|
||||
}
|
||||
|
||||
public override function destroy():Void
|
||||
|
||||
@@ -755,6 +755,11 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
FlxTween.tween(member, {y: member.y + 300}, 0.8, {ease: FlxEase.backIn});
|
||||
}
|
||||
FlxG.camera.follow(camFollow, LOCKON);
|
||||
// going to freeplay so fast makes the fade effects and the camera to bug, that's why we cancel the tweens
|
||||
FlxTween.cancelTweensOf(transitionGradient);
|
||||
FlxTween.cancelTweensOf(fadeShader);
|
||||
FlxTween.cancelTweensOf(camFollow);
|
||||
|
||||
FlxTween.tween(transitionGradient, {y: -150}, 0.8, {ease: FlxEase.backIn});
|
||||
fadeShader.fade(1.0, 0, 0.8, {ease: FlxEase.quadIn});
|
||||
FlxTween.tween(camFollow, {y: camFollow.y - 150}, 0.8,
|
||||
@@ -764,7 +769,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
FlxG.switchState(() -> FreeplayState.build(
|
||||
{
|
||||
{
|
||||
character: curChar,
|
||||
character: wentBackToFreeplay ? rememberedChar : curChar,
|
||||
fromCharSelect: true
|
||||
}
|
||||
}));
|
||||
@@ -784,6 +789,8 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
var mobileDeny:Bool = false;
|
||||
var mobileAccept:Bool = false;
|
||||
|
||||
var wentBackToFreeplay:Bool = false;
|
||||
|
||||
override public function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
@@ -899,6 +906,14 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
holdTmrRight = 0;
|
||||
selectSound.play(true);
|
||||
}
|
||||
|
||||
if (controls.BACK)
|
||||
{
|
||||
wentBackToFreeplay = true;
|
||||
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||
FlxTween.tween(FlxG.sound.music, {volume: 0.0}, 0.7, {ease: FlxEase.quadInOut});
|
||||
goToFreeplay();
|
||||
}
|
||||
}
|
||||
|
||||
if (cursorX < -1)
|
||||
|
||||
@@ -8,7 +8,7 @@ import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.components.HealthIcon;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
|
||||
@@ -68,10 +68,15 @@ class CreditsState extends MusicBeatState
|
||||
static final CREDITS_SCROLL_BASE_SPEED = 100.0;
|
||||
|
||||
/**
|
||||
* The speed the credits scroll at while the button is held, in pixels per second.
|
||||
* The speed the credits scroll at while accept keybind or spacebar is held, in pixels per second.
|
||||
*/
|
||||
static final CREDITS_SCROLL_FAST_SPEED = CREDITS_SCROLL_BASE_SPEED * 4.0;
|
||||
|
||||
/**
|
||||
* The speed the credits scroll at while the pause keybind is held, in pixels per second.
|
||||
*/
|
||||
static final CREDITS_SCROLL_PAUSE_SPEED = 0.0;
|
||||
|
||||
/**
|
||||
* The actual sprites and text used to display the credits.
|
||||
*/
|
||||
@@ -266,24 +271,24 @@ class CreditsState extends MusicBeatState
|
||||
// TODO: Replace with whatever the special note button is.
|
||||
if (FlxG.keys.pressed.ENTER || FlxG.keys.pressed.SPACE #if mobile || TouchUtil.pressed && !TouchUtil.overlaps(backButton) #end)
|
||||
{
|
||||
// Move the whole group.
|
||||
// Move the whole group by the base scroll speed.
|
||||
creditsGroup.y -= CREDITS_SCROLL_FAST_SPEED * elapsed;
|
||||
}
|
||||
else if (controls.PAUSE || FlxG.keys.pressed.SHIFT)
|
||||
{
|
||||
// Stop the whole group from moving.
|
||||
creditsGroup.y -= CREDITS_SCROLL_PAUSE_SPEED * elapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move the whole group.
|
||||
// Move the whole group by the base scroll speed.
|
||||
creditsGroup.y -= CREDITS_SCROLL_BASE_SPEED * elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (controls.BACK || hasEnded())
|
||||
{
|
||||
exit();
|
||||
}
|
||||
else if (controls.PAUSE)
|
||||
{
|
||||
// scrollPaused = !scrollPaused;
|
||||
}
|
||||
}
|
||||
|
||||
function hasEnded():Bool
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
package funkin.ui.debug;
|
||||
|
||||
import flixel.util.FlxStringUtil;
|
||||
import funkin.ui.debug.stats.FunkinStatsGraph;
|
||||
import funkin.util.MemoryUtil;
|
||||
import openfl.display.Shape;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.text.TextField;
|
||||
import openfl.text.TextFormat;
|
||||
|
||||
/**
|
||||
* A debug overlay showing useful info.
|
||||
*/
|
||||
#if cpp
|
||||
@:access(lime._internal.backend.native.NativeCFFI)
|
||||
#end
|
||||
class FunkinDebugDisplay extends Sprite
|
||||
{
|
||||
static final UPDATE_DELAY:Int = 100;
|
||||
static final INNER_RECT_DIFF:Int = 3;
|
||||
static final OUTER_RECT_DIMENSIONS:Array<Int> = [234, 201];
|
||||
static final OTHERS_OFFSET:Int = 8;
|
||||
|
||||
/**
|
||||
* Indicates whether the debug display is in advanced mode.
|
||||
*/
|
||||
public var isAdvanced(default, set):Bool = false;
|
||||
|
||||
/**
|
||||
* The opacity of the debug display's background.
|
||||
*/
|
||||
public var backgroundOpacity(default, set):Float = 0.5;
|
||||
|
||||
var currentFPS:Int;
|
||||
var deltaTimeout:Float;
|
||||
var times:Array<Float>;
|
||||
var color:Int;
|
||||
|
||||
#if !html5
|
||||
var gcMem:Float;
|
||||
var gcMemPeak:Float;
|
||||
|
||||
var taskMem:Float;
|
||||
var taskMemPeak:Float;
|
||||
#end
|
||||
|
||||
var background:Shape;
|
||||
|
||||
var fpsGraph:FunkinStatsGraph;
|
||||
var gcMemGraph:FunkinStatsGraph;
|
||||
var taskMemGraph:FunkinStatsGraph;
|
||||
|
||||
var infoDisplay:TextField;
|
||||
|
||||
public function new(x:Float = 10, y:Float = 10, color:Int = 0x000000):Void
|
||||
{
|
||||
super();
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.currentFPS = 0;
|
||||
this.deltaTimeout = 0.0;
|
||||
#if !html5
|
||||
this.gcMem = 0.0;
|
||||
this.gcMemPeak = 0.0;
|
||||
this.taskMem = 0.0;
|
||||
this.taskMemPeak = 0.0;
|
||||
#end
|
||||
this.times = [];
|
||||
this.color = color;
|
||||
this.backgroundOpacity = 0;
|
||||
this.isAdvanced = false;
|
||||
}
|
||||
|
||||
function buildDebugDisplay(advanced:Bool):Void
|
||||
{
|
||||
removeChildren(0, numChildren);
|
||||
|
||||
final BG_WIDTH_MULTIPLIER:Float = #if html5 advanced ? 1 : 0.3 #else 1 #end;
|
||||
|
||||
#if html5
|
||||
final BG_HEIGHT_MULTIPLIER:Float = advanced ? 0.45 : 0.15;
|
||||
#else
|
||||
final BG_HEIGHT_MULTIPLIER:Float = advanced ? 1 : (MemoryUtil.supportsTaskMem()) ? 0.3 : 0.2;
|
||||
#end
|
||||
|
||||
background = new Shape();
|
||||
background.graphics.beginFill(0x3d3f41, 1);
|
||||
background.graphics.drawRect(0, 0, (OUTER_RECT_DIMENSIONS[0] * BG_WIDTH_MULTIPLIER) + (INNER_RECT_DIFF * 2),
|
||||
(OUTER_RECT_DIMENSIONS[1] * BG_HEIGHT_MULTIPLIER) + (INNER_RECT_DIFF * 2));
|
||||
background.graphics.endFill();
|
||||
background.graphics.beginFill(0x2c2f30, 1);
|
||||
background.graphics.drawRect(INNER_RECT_DIFF, INNER_RECT_DIFF, OUTER_RECT_DIMENSIONS[0] * BG_WIDTH_MULTIPLIER,
|
||||
OUTER_RECT_DIMENSIONS[1] * BG_HEIGHT_MULTIPLIER);
|
||||
background.graphics.endFill();
|
||||
background.alpha = backgroundOpacity;
|
||||
addChild(background);
|
||||
|
||||
if (advanced)
|
||||
{
|
||||
createAdvancedElements();
|
||||
updateAdvancedDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
createSimpleElements();
|
||||
updateSimpleDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
function createAdvancedElements():Void
|
||||
{
|
||||
final graphsWidth:Int = OUTER_RECT_DIMENSIONS[0] + (INNER_RECT_DIFF * 2) - (OTHERS_OFFSET * 3);
|
||||
final graphsHeight:Int = 25;
|
||||
|
||||
fpsGraph = new FunkinStatsGraph(OTHERS_OFFSET, OTHERS_OFFSET + 49, graphsWidth, graphsHeight, color);
|
||||
fpsGraph.textDisplay.y = -49;
|
||||
fpsGraph.minValue = 0;
|
||||
addChild(fpsGraph);
|
||||
|
||||
#if !html5
|
||||
gcMemGraph = new FunkinStatsGraph(OTHERS_OFFSET, Math.floor(OTHERS_OFFSET + (fpsGraph.y + fpsGraph.axisHeight) + 22), graphsWidth, graphsHeight, color);
|
||||
gcMemGraph.minValue = 0;
|
||||
addChild(gcMemGraph);
|
||||
|
||||
if (MemoryUtil.supportsTaskMem())
|
||||
{
|
||||
taskMemGraph = new FunkinStatsGraph(OTHERS_OFFSET, Math.floor(OTHERS_OFFSET + (gcMemGraph.y + gcMemGraph.axisHeight) + 22), graphsWidth, graphsHeight,
|
||||
color);
|
||||
taskMemGraph.minValue = 0;
|
||||
addChild(taskMemGraph);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
function createSimpleElements():Void
|
||||
{
|
||||
infoDisplay = new TextField();
|
||||
infoDisplay.x = OTHERS_OFFSET;
|
||||
infoDisplay.y = OTHERS_OFFSET;
|
||||
infoDisplay.width = 500;
|
||||
infoDisplay.selectable = false;
|
||||
infoDisplay.mouseEnabled = false;
|
||||
infoDisplay.defaultTextFormat = new TextFormat('Monsterrat', 12, color, JUSTIFY);
|
||||
infoDisplay.antiAliasType = NORMAL;
|
||||
infoDisplay.sharpness = 100;
|
||||
infoDisplay.multiline = true;
|
||||
addChild(infoDisplay);
|
||||
}
|
||||
|
||||
override function __enterFrame(deltaTime:Int):Void
|
||||
{
|
||||
#if cpp
|
||||
final currentTime:Float = lime._internal.backend.native.NativeCFFI.lime_sdl_get_ticks();
|
||||
#elseif html5
|
||||
final currentTime:Float = js.Browser.window.performance.now();
|
||||
#else
|
||||
final currentTime:Float = haxe.Timer.stamp() * 1000;
|
||||
#end
|
||||
|
||||
times.push(currentTime);
|
||||
|
||||
while (times[0] < currentTime - 1000)
|
||||
{
|
||||
times.shift();
|
||||
}
|
||||
|
||||
if (deltaTimeout < UPDATE_DELAY)
|
||||
{
|
||||
deltaTimeout += deltaTime;
|
||||
return;
|
||||
}
|
||||
|
||||
currentFPS = times.length;
|
||||
|
||||
#if !html5
|
||||
gcMem = MemoryUtil.getGCMemory();
|
||||
|
||||
if (gcMem > gcMemPeak) gcMemPeak = gcMem;
|
||||
|
||||
if (MemoryUtil.supportsTaskMem())
|
||||
{
|
||||
taskMem = MemoryUtil.getTaskMemory();
|
||||
|
||||
if (taskMem > taskMemPeak) taskMemPeak = taskMem;
|
||||
}
|
||||
#end
|
||||
|
||||
if (isAdvanced)
|
||||
{
|
||||
updateAdvancedDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
updateSimpleDisplay();
|
||||
}
|
||||
|
||||
deltaTimeout = 0.0;
|
||||
}
|
||||
|
||||
function updateAdvancedDisplay():Void
|
||||
{
|
||||
updateFPSGraph();
|
||||
#if !html5
|
||||
updateGcMemGraph();
|
||||
updateTaskMemGraph();
|
||||
#end
|
||||
|
||||
final info:Array<String> = [];
|
||||
info.push('FPS: $currentFPS');
|
||||
info.push('AVG FPS: ${Math.floor(fpsGraph.average())}');
|
||||
info.push('1% LOW FPS: ${Math.floor(fpsGraph.lowest())}');
|
||||
fpsGraph.textDisplay.text = info.join('\n');
|
||||
|
||||
#if !html5
|
||||
gcMemGraph.textDisplay.text = 'GC MEM: ${FlxStringUtil.formatBytes(gcMem).toLowerCase()} / ${FlxStringUtil.formatBytes(gcMemPeak).toLowerCase()}';
|
||||
|
||||
if (taskMemGraph != null)
|
||||
{
|
||||
taskMemGraph.textDisplay.text = 'TASK MEM: ${FlxStringUtil.formatBytes(taskMem).toLowerCase()} / ${FlxStringUtil.formatBytes(taskMemPeak).toLowerCase()}';
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
function updateSimpleDisplay():Void
|
||||
{
|
||||
if (infoDisplay != null)
|
||||
{
|
||||
final info:Array<String> = [];
|
||||
|
||||
info.push('FPS: $currentFPS');
|
||||
|
||||
#if !html5
|
||||
info.push('GC MEM: ${FlxStringUtil.formatBytes(gcMem).toLowerCase()} / ${FlxStringUtil.formatBytes(gcMemPeak).toLowerCase()}');
|
||||
|
||||
if (MemoryUtil.supportsTaskMem())
|
||||
info.push('TASK MEM: ${FlxStringUtil.formatBytes(taskMem).toLowerCase()} / ${FlxStringUtil.formatBytes(taskMemPeak).toLowerCase()}');
|
||||
#end
|
||||
|
||||
infoDisplay.text = info.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
function updateFPSGraph(?currentFPS:Int = 0):Void
|
||||
{
|
||||
fpsGraph.maxValue = FlxG.drawFramerate;
|
||||
fpsGraph.update(times.length);
|
||||
}
|
||||
|
||||
#if !html5
|
||||
function updateGcMemGraph(?currentFPS:Int = 0):Void
|
||||
{
|
||||
gcMemGraph.maxValue = gcMemPeak;
|
||||
gcMemGraph.update(gcMem);
|
||||
}
|
||||
|
||||
function updateTaskMemGraph(?currentFPS:Int = 0):Void
|
||||
{
|
||||
if (taskMemGraph != null)
|
||||
{
|
||||
taskMemGraph.maxValue = taskMemPeak;
|
||||
taskMemGraph.update(taskMem);
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
function set_isAdvanced(value:Bool):Bool
|
||||
{
|
||||
buildDebugDisplay(value);
|
||||
|
||||
return isAdvanced = value;
|
||||
}
|
||||
|
||||
function set_backgroundOpacity(value:Float):Float
|
||||
{
|
||||
if (background != null) background.alpha = value;
|
||||
|
||||
return backgroundOpacity = value;
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract DebugDisplayMode(Int) from Int to Int
|
||||
{
|
||||
/**
|
||||
* Debug display is disabled.
|
||||
*/
|
||||
var OFF = 0;
|
||||
|
||||
/**
|
||||
* Simple debug display.
|
||||
* FPS and Memory counters only.
|
||||
*/
|
||||
var SIMPLE = 1;
|
||||
|
||||
/**
|
||||
* Advanced debug display.
|
||||
* Full FPS and Memory info.
|
||||
*/
|
||||
var ADVANCED = 2;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package funkin.ui.debug;
|
||||
|
||||
import funkin.util.MemoryUtil;
|
||||
import openfl.text.TextFormat;
|
||||
import openfl.text.TextField;
|
||||
|
||||
class MemoryCounter extends TextField
|
||||
{
|
||||
var memPeak:Float = 0;
|
||||
|
||||
static final BYTES_PER_MEG:Float = 1024 * 1024;
|
||||
static final ROUND_TO:Float = 1 / 100;
|
||||
|
||||
public function new(x:Float = 10, y:Float = 10, color:Int = 0x000000)
|
||||
{
|
||||
super();
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = 500;
|
||||
this.selectable = false;
|
||||
this.mouseEnabled = false;
|
||||
defaultTextFormat = new TextFormat("_sans", 12, color);
|
||||
text = "RAM: ";
|
||||
|
||||
#if flash
|
||||
addEventListener(Event.ENTER_FRAME, function(e) {
|
||||
var time = Lib.getTimer();
|
||||
__enterFrame(time - currentTime);
|
||||
});
|
||||
#end
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
@:noCompletion
|
||||
#if !flash override #end function __enterFrame(deltaTime:Float):Void
|
||||
{
|
||||
var mem:Float = Math.fround(MemoryUtil.getMemoryUsed() / BYTES_PER_MEG / ROUND_TO) * ROUND_TO;
|
||||
|
||||
if (mem > memPeak) memPeak = mem;
|
||||
|
||||
text = 'RAM: ${mem}mb / ${memPeak}mb';
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import flixel.text.FlxText;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.input.Cursor;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.ui.mainmenu.MainMenuState;
|
||||
import funkin.util.MouseUtil;
|
||||
import funkin.util.SerializerUtil;
|
||||
@@ -171,6 +171,21 @@ class DebugBoundingState extends FlxState
|
||||
}
|
||||
}
|
||||
|
||||
function updateOnionSkin():Void
|
||||
{
|
||||
if (swagChar == null) return;
|
||||
if (swagChar.hasAnimation("idle")) swagChar.playAnimation("idle", true);
|
||||
|
||||
onionSkinChar.loadGraphicFromSprite(swagChar);
|
||||
onionSkinChar.frame = swagChar.frame;
|
||||
onionSkinChar.alpha = 0.6;
|
||||
onionSkinChar.flipX = swagChar.flipX;
|
||||
onionSkinChar.offset.x = swagChar.animOffsets[0];
|
||||
onionSkinChar.offset.y = swagChar.animOffsets[1];
|
||||
|
||||
swagChar.playAnimation(currentAnimationName, true); // reset animation to the one it should be
|
||||
}
|
||||
|
||||
function initOffsetView():Void
|
||||
{
|
||||
offsetView = new FlxGroup();
|
||||
@@ -332,6 +347,16 @@ class DebugBoundingState extends FlxState
|
||||
|
||||
function offsetControls():Void
|
||||
{
|
||||
// CTRL + S = Save Character Data
|
||||
// CTRL + SHIFT + S = Save Offsets
|
||||
// "WINDOWS" key code is the same keycode as COMMAND on mac
|
||||
if ((FlxG.keys.pressed.CONTROL || FlxG.keys.pressed.WINDOWS) && FlxG.keys.justPressed.S)
|
||||
{
|
||||
var outputString = FlxG.keys.pressed.SHIFT ? buildOutputStringOld() : buildOutputStringNew();
|
||||
saveOffsets(outputString, FlxG.keys.pressed.SHIFT ? swagChar.characterId + "Offsets.txt" : swagChar.characterId + ".json");
|
||||
return;
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.RBRACKET || FlxG.keys.justPressed.E)
|
||||
{
|
||||
if (offsetAnimationDropdown.selectedIndex + 1 <= offsetAnimationDropdown.dataSource.size)
|
||||
@@ -395,11 +420,13 @@ class DebugBoundingState extends FlxState
|
||||
if (FlxG.keys.justPressed.F)
|
||||
{
|
||||
onionSkinChar.visible = !onionSkinChar.visible;
|
||||
if (onionSkinChar.visible) updateOnionSkin();
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.G)
|
||||
{
|
||||
swagChar.flipX = !swagChar.flipX;
|
||||
if (onionSkinChar.visible) updateOnionSkin();
|
||||
}
|
||||
|
||||
// Plays the idle animation
|
||||
@@ -440,12 +467,6 @@ class DebugBoundingState extends FlxState
|
||||
|
||||
trace(animName);
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.ESCAPE)
|
||||
{
|
||||
var outputString = FlxG.keys.pressed.CONTROL ? buildOutputStringOld() : buildOutputStringNew();
|
||||
saveOffsets(outputString, FlxG.keys.pressed.CONTROL ? swagChar.characterId + "Offsets.txt" : swagChar.characterId + ".json");
|
||||
}
|
||||
}
|
||||
|
||||
function buildOutputStringOld():String
|
||||
@@ -489,8 +510,8 @@ class DebugBoundingState extends FlxState
|
||||
}
|
||||
|
||||
swagChar = CharacterDataParser.fetchCharacter(char);
|
||||
swagChar.x = 100;
|
||||
swagChar.y = 100;
|
||||
swagChar.x = onionSkinChar.x = 100;
|
||||
swagChar.y = onionSkinChar.y = 100;
|
||||
swagChar.debug = true;
|
||||
offsetView.add(swagChar);
|
||||
|
||||
@@ -545,14 +566,7 @@ class DebugBoundingState extends FlxState
|
||||
|
||||
function playCharacterAnimation(str:String, setOnionSkin:Bool = true)
|
||||
{
|
||||
if (setOnionSkin)
|
||||
{
|
||||
// clears the canvas
|
||||
onionSkinChar.pixels.fillRect(new Rectangle(0, 0, FlxG.width * 2, FlxG.height * 2), 0x00000000);
|
||||
|
||||
onionSkinChar.stamp(swagChar, Std.int(swagChar.x), Std.int(swagChar.y));
|
||||
onionSkinChar.alpha = 0.6;
|
||||
}
|
||||
if (setOnionSkin) updateOnionSkin();
|
||||
|
||||
// var animName = characterAnimNames[Std.parseInt(str)];
|
||||
var animName = str;
|
||||
|
||||
@@ -41,7 +41,7 @@ import funkin.input.TurboKeyHandler;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.play.notes.notekind.NoteKindManager;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.components.HealthIcon;
|
||||
import funkin.play.notes.NoteSprite;
|
||||
import funkin.play.PlayStatePlaylist;
|
||||
@@ -78,6 +78,7 @@ import funkin.ui.debug.charting.components.ChartEditorSelectionSquareSprite;
|
||||
import funkin.ui.debug.charting.toolboxes.ChartEditorDifficultyToolbox;
|
||||
import funkin.ui.debug.charting.toolboxes.ChartEditorFreeplayToolbox;
|
||||
import funkin.ui.debug.charting.toolboxes.ChartEditorOffsetsToolbox;
|
||||
import funkin.ui.debug.FunkinDebugDisplay.DebugDisplayMode;
|
||||
import funkin.ui.haxeui.components.CharacterPlayer;
|
||||
import funkin.ui.mainmenu.MainMenuState;
|
||||
import funkin.ui.transition.LoadingState;
|
||||
@@ -1279,6 +1280,20 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
*/
|
||||
var songMetadata:Map<String, SongMetadata> = [];
|
||||
|
||||
/**
|
||||
* Updates the current song's play data variations list.
|
||||
*/
|
||||
function refreshPlayDataVariations():Void
|
||||
{
|
||||
var songVariations:Array<String> = songMetadata.get(Constants.DEFAULT_VARIATION).playData.songVariations;
|
||||
songVariations.clear();
|
||||
for (variation in availableVariations)
|
||||
{
|
||||
if (variation == Constants.DEFAULT_VARIATION) continue;
|
||||
songVariations.push(variation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of variations for the current song.
|
||||
*/
|
||||
@@ -2172,6 +2187,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
*/
|
||||
var notePreviewPlayhead:Null<FlxSprite> = null;
|
||||
|
||||
/**
|
||||
* Whether the note preview playhead is currently being dragged with the mouse by the user.
|
||||
*/
|
||||
var notePreviewPlayHeadDragging:Bool = false;
|
||||
|
||||
/**
|
||||
* The rectangular sprite used for rendering the selection box.
|
||||
* Uses a 9-slice to stretch the selection box to the correct size without warping.
|
||||
@@ -2845,7 +2865,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
txtCopyNotif.zIndex = 120;
|
||||
add(txtCopyNotif);
|
||||
|
||||
if (!Preferences.debugDisplay) menubar.paddingLeft = null;
|
||||
if (Preferences.debugDisplay == DebugDisplayMode.OFF) menubar.paddingLeft = null;
|
||||
|
||||
this.setupNotifications();
|
||||
|
||||
@@ -4350,6 +4370,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
else if (measureTicks != null && FlxG.mouse.overlaps(measureTicks) && !isCursorOverHaxeUI)
|
||||
{
|
||||
gridPlayheadScrollAreaPressed = true;
|
||||
// Stop audio playback while dragging on the grid playhead.
|
||||
if (audioInstTrack != null && audioInstTrack.isPlaying)
|
||||
{
|
||||
playbarHeadDraggingWasPlaying = true;
|
||||
stopAudioPlayback();
|
||||
}
|
||||
}
|
||||
else if (notePreview != null && FlxG.mouse.overlaps(notePreview) && !isCursorOverHaxeUI)
|
||||
{
|
||||
@@ -4372,11 +4398,24 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
if (gridPlayheadScrollAreaPressed && FlxG.mouse.released)
|
||||
{
|
||||
gridPlayheadScrollAreaPressed = false;
|
||||
// If we were dragging the playhead while the song was playing, resume playing.
|
||||
if (playbarHeadDraggingWasPlaying)
|
||||
{
|
||||
playbarHeadDraggingWasPlaying = false;
|
||||
startAudioPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
if (notePreviewScrollAreaStartPos != null && FlxG.mouse.released)
|
||||
{
|
||||
notePreviewScrollAreaStartPos = null;
|
||||
notePreviewPlayHeadDragging = false;
|
||||
|
||||
if (playbarHeadDraggingWasPlaying)
|
||||
{
|
||||
playbarHeadDraggingWasPlaying = false;
|
||||
startAudioPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
if (gridPlayheadScrollAreaPressed)
|
||||
@@ -4651,6 +4690,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
}
|
||||
else if (notePreviewScrollAreaStartPos != null)
|
||||
{
|
||||
notePreviewPlayHeadDragging = true;
|
||||
// Stop audio playback while dragging on the note preview playhead.
|
||||
if (audioInstTrack != null && audioInstTrack.isPlaying)
|
||||
{
|
||||
playbarHeadDraggingWasPlaying = true;
|
||||
stopAudioPlayback();
|
||||
}
|
||||
|
||||
// Player is clicking and holding on note preview to scrub around.
|
||||
targetCursorMode = Grabbing;
|
||||
|
||||
@@ -5677,6 +5724,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
return;
|
||||
}
|
||||
|
||||
this.hideAllToolboxes();
|
||||
|
||||
stopWelcomeMusic();
|
||||
// TODO: PR Flixel to make onComplete nullable.
|
||||
if (audioInstTrack != null) audioInstTrack.onComplete = null;
|
||||
@@ -6009,6 +6058,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
stopWelcomeMusic();
|
||||
stopAudioPlayback();
|
||||
|
||||
// Force pauses audio preview from OffsetsToolbox, if it exists.
|
||||
cast(this.getToolbox(CHART_EDITOR_TOOLBOX_OFFSETS_LAYOUT), ChartEditorOffsetsToolbox)?.pauseAudioPreview();
|
||||
|
||||
var startTimestamp:Float = 0;
|
||||
if (playtestStartTime) startTimestamp = scrollPositionInMs + playheadPositionInMs;
|
||||
|
||||
@@ -6191,6 +6243,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
{
|
||||
if (audioInstTrack != null)
|
||||
{
|
||||
// Don't allow the audio to be played while we're dragging any of the playheads
|
||||
if (playbarHeadDragging || gridPlayheadScrollAreaPressed || notePreviewPlayHeadDragging) return;
|
||||
cast(this.getToolbox(CHART_EDITOR_TOOLBOX_OFFSETS_LAYOUT), ChartEditorOffsetsToolbox)?.pauseAudioPreview();
|
||||
audioInstTrack.play(false, audioInstTrack.time);
|
||||
audioVocalTrackGroup.play(false, audioInstTrack.time);
|
||||
}
|
||||
@@ -6302,7 +6357,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
|
||||
if (songMetadata.size() > 1)
|
||||
{
|
||||
if (variationMetadata.playData.difficulties.length == 0)
|
||||
if (variation != Constants.DEFAULT_VARIATION && variationMetadata.playData.difficulties.length == 0)
|
||||
{
|
||||
songMetadata.remove(variation);
|
||||
songChartData.remove(variation);
|
||||
@@ -6319,6 +6374,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
if (selectedDifficulty == difficulty
|
||||
|| !variationMetadata.playData.difficulties.contains(selectedDifficulty)) selectedDifficulty = variationMetadata.playData.difficulties[0];
|
||||
|
||||
refreshPlayDataVariations();
|
||||
difficultySelectDirty = true; // Force the Difficulty toolbox to update.
|
||||
}
|
||||
|
||||
@@ -6655,6 +6711,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't allow the audio to be played while we're dragging any of the playheads
|
||||
if (playbarHeadDragging || gridPlayheadScrollAreaPressed || notePreviewPlayHeadDragging) return;
|
||||
// Play
|
||||
startAudioPlayback();
|
||||
stopWelcomeMusic();
|
||||
|
||||
@@ -2,8 +2,8 @@ package funkin.ui.debug.charting.dialogs;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.components.HealthIcon;
|
||||
import funkin.util.SortUtil;
|
||||
import haxe.ui.components.Label;
|
||||
|
||||
@@ -4,7 +4,7 @@ import funkin.input.Cursor;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogDropTarget;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams;
|
||||
import funkin.util.FileUtil;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.data.character.CharacterData;
|
||||
import haxe.io.Path;
|
||||
import haxe.ui.components.Button;
|
||||
import haxe.ui.components.Label;
|
||||
|
||||
@@ -168,11 +168,10 @@ class ChartEditorDialogHandler
|
||||
var backupTimeLabel:Null<Label> = dialog.findComponent('backupTimeLabel', Label);
|
||||
if (backupTimeLabel == null) throw 'Could not locate backupTimeLabel button in Backup Available dialog';
|
||||
|
||||
var latestBackupDate:Null<Date> = ChartEditorImportExportHandler.getLatestBackupDate();
|
||||
var latestBackupDate:Null<String> = ChartEditorImportExportHandler.getLatestBackupDate();
|
||||
if (latestBackupDate != null)
|
||||
{
|
||||
var latestBackupDateStr:String = DateUtil.generateCleanTimestamp(latestBackupDate);
|
||||
backupTimeLabel.text = latestBackupDateStr;
|
||||
backupTimeLabel.text = latestBackupDate;
|
||||
}
|
||||
|
||||
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);
|
||||
@@ -1246,6 +1245,7 @@ class ChartEditorDialogHandler
|
||||
pendingVariation.timeChanges[0].bpm = dialogBPM.value;
|
||||
|
||||
state.songMetadata.set(pendingVariation.variation, pendingVariation);
|
||||
state.refreshPlayDataVariations();
|
||||
state.difficultySelectDirty = true; // Force the Difficulty toolbox to update.
|
||||
|
||||
// Don't update conductor since we haven't switched to the new variation yet.
|
||||
|
||||
@@ -278,8 +278,7 @@ class ChartEditorImportExportHandler
|
||||
var variChartDataString:String = mappedFileEntries.get(variChartDataPath)?.data?.toString() ?? throw 'Could not locate chart data ($variation).';
|
||||
var variChartDataVersion:SemverVersion = VersionUtil.getVersionFromJSON(variChartDataString) ?? throw 'Could not read chart data version ($variation).';
|
||||
var variChartData:SongChartData = SongRegistry.instance.parseEntryChartDataRawWithMigration(variChartDataString, variChartDataPath,
|
||||
variChartDataVersion) ?? throw 'Could not read chart data ($variation).';
|
||||
|
||||
variChartDataVersion, variation) ?? throw 'Could not read chart data ($variation).';
|
||||
songChartDatas.set(variation, variChartData);
|
||||
}
|
||||
loadSong(state, songMetadatas, songChartDatas, manifest);
|
||||
@@ -368,7 +367,7 @@ class ChartEditorImportExportHandler
|
||||
#end
|
||||
}
|
||||
|
||||
public static function getLatestBackupDate():Null<Date>
|
||||
public static function getLatestBackupDate():Null<String>
|
||||
{
|
||||
#if sys
|
||||
var latestBackupPath:Null<String> = getLatestBackupPath();
|
||||
@@ -377,19 +376,10 @@ class ChartEditorImportExportHandler
|
||||
var latestBackupName:String = haxe.io.Path.withoutDirectory(latestBackupPath);
|
||||
latestBackupName = haxe.io.Path.withoutExtension(latestBackupName);
|
||||
|
||||
var parts = latestBackupName.split('-');
|
||||
var stat = sys.FileSystem.stat(latestBackupPath);
|
||||
var sizeInMB = (stat.size / 1000000).round(3);
|
||||
|
||||
// var chart:String = parts[0];
|
||||
// var editor:String = parts[1];
|
||||
var year:Int = Std.parseInt(parts[2] ?? '0') ?? 0;
|
||||
var month:Int = Std.parseInt(parts[3] ?? '1') ?? 1;
|
||||
var day:Int = Std.parseInt(parts[4] ?? '0') ?? 0;
|
||||
var hour:Int = Std.parseInt(parts[5] ?? '0') ?? 0;
|
||||
var minute:Int = Std.parseInt(parts[6] ?? '0') ?? 0;
|
||||
var second:Int = Std.parseInt(parts[7] ?? '0') ?? 0;
|
||||
|
||||
var date:Date = new Date(year, month - 1, day, hour, minute, second);
|
||||
return date;
|
||||
return "Full Name: " + latestBackupName + "\nLast Modified: " + stat.mtime.toString() + "\nSize: " + sizeInMB + " MB";
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
@@ -470,9 +460,10 @@ class ChartEditorImportExportHandler
|
||||
{
|
||||
// Force writing to a generic path (autosave or crash recovery)
|
||||
targetMode = Skip;
|
||||
if (state.currentSongId == '') state.currentSongName = 'New Chart'; // Hopefully no one notices this silliness
|
||||
targetPath = Path.join([
|
||||
BACKUPS_PATH,
|
||||
'chart-editor-${DateUtil.generateTimestamp()}.${Constants.EXT_CHART}'
|
||||
'chart-editor-${state.currentSongId}-${DateUtil.generateTimestamp()}.${Constants.EXT_CHART}'
|
||||
]);
|
||||
// We have to force write because the program will die before the save dialog is closed.
|
||||
trace('Force exporting to $targetPath...');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package funkin.ui.debug.charting.toolboxes;
|
||||
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.data.character.CharacterData;
|
||||
import funkin.data.song.importer.ChartManifestData;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
|
||||
@@ -103,6 +103,7 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
||||
|
||||
function onClose(event:UIEvent)
|
||||
{
|
||||
stopAudioPreview(); // Pause it instead, maybe?
|
||||
chartEditorState.menubarItemToggleToolboxOffsets.selected = false;
|
||||
}
|
||||
|
||||
@@ -458,6 +459,7 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
||||
|
||||
public function playAudioPreview():Void
|
||||
{
|
||||
chartEditorState.stopAudioPlayback();
|
||||
audioPreviewTracks.play(false, audioPreviewTracks.time);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.play.notes.notestyle.NoteStyle;
|
||||
import funkin.play.event.SongEvent;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.data.character.CharacterData;
|
||||
import haxe.ui.components.DropDown;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.data.event.SongEventRegistry;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
|
||||
/**
|
||||
* Functions for populating dropdowns based on game data.
|
||||
|
||||
@@ -13,7 +13,7 @@ import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.addons.display.FlxGridOverlay;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.save.Save;
|
||||
import funkin.input.Cursor;
|
||||
import haxe.ui.backend.flixel.UIState;
|
||||
@@ -206,7 +206,7 @@ class StageEditorState extends UIState
|
||||
var data = this.packShitToZip();
|
||||
var path = haxe.io.Path.join([
|
||||
BACKUPS_PATH,
|
||||
'stage-editor-${funkin.util.DateUtil.generateTimestamp()}.${FileUtil.FILE_EXTENSION_INFO_FNFS.extension}'
|
||||
'stage-editor-${stageName}-${funkin.util.DateUtil.generateTimestamp()}.${FileUtil.FILE_EXTENSION_INFO_FNFS.extension}'
|
||||
]);
|
||||
|
||||
FileUtil.writeBytesToPath(path, data);
|
||||
@@ -638,7 +638,7 @@ class StageEditorState extends UIState
|
||||
if (FlxG.keys.justPressed.S) FlxG.keys.pressed.SHIFT ? onMenuItemClick("save stage as") : onMenuItemClick("save stage");
|
||||
if (FlxG.keys.justPressed.F) onMenuItemClick("find object");
|
||||
if (FlxG.keys.justPressed.O) onMenuItemClick("open stage");
|
||||
if (FlxG.keys.justPressed.N) onMenuItemClick("new stage");
|
||||
if (FlxG.keys.justPressed.N && welcomeDialog == null && userGuideDialog == null) onMenuItemClick("new stage");
|
||||
if (FlxG.keys.justPressed.Q) onMenuItemClick("exit");
|
||||
}
|
||||
|
||||
@@ -1259,7 +1259,7 @@ class StageEditorState extends UIState
|
||||
{
|
||||
if (exitConfirmDialog == null)
|
||||
{
|
||||
exitConfirmDialog = Dialogs.messageBox("You are about to leave the Editor without Saving.\n\nAre you sure? ", "Leave Editor",
|
||||
exitConfirmDialog = Dialogs.messageBox("You are about to leave the editor without saving.\n\nAre you sure? ", "Leave Editor",
|
||||
MessageBoxType.TYPE_YESNO, true, function(btn:DialogButton) {
|
||||
exitConfirmDialog = null;
|
||||
if (btn == DialogButton.YES)
|
||||
|
||||
@@ -9,7 +9,7 @@ import funkin.util.DateUtil;
|
||||
using StringTools;
|
||||
|
||||
@:xml('
|
||||
<dialog id="backupAvailableDialog" width="475" height="150" title="Hey! Listen!">
|
||||
<dialog id="backupAvailableDialog" width="475" height="200" title="Hey! Listen!">
|
||||
<vbox width="100%" height="100%">
|
||||
<label text="There is a stage backup available, would you like to open it?\n" width="100%" textAlign="center" />
|
||||
<spacer height="6" />
|
||||
@@ -34,20 +34,14 @@ class BackupAvailableDialog extends Dialog
|
||||
if (!FileUtil.fileExists(filePath)) return;
|
||||
|
||||
// time text
|
||||
var fileDate = Path.withoutExtension(Path.withoutDirectory(filePath));
|
||||
var dateParts = fileDate.split("-");
|
||||
var file = Path.withoutExtension(Path.withoutDirectory(filePath));
|
||||
|
||||
while (dateParts.length < 8)
|
||||
dateParts.push("0");
|
||||
#if sys
|
||||
var stat = sys.FileSystem.stat(filePath);
|
||||
var sizeInMB = (stat.size / 1000000).round(3);
|
||||
|
||||
var year:Int = Std.parseInt(dateParts[2]) ?? 0; // copied parts from ChartEditorImportExportHandler.hx
|
||||
var month:Int = Std.parseInt(dateParts[3]) ?? 1;
|
||||
var day:Int = Std.parseInt(dateParts[4]) ?? 0;
|
||||
var hour:Int = Std.parseInt(dateParts[5]) ?? 0;
|
||||
var minute:Int = Std.parseInt(dateParts[6]) ?? 0;
|
||||
var second:Int = Std.parseInt(dateParts[7]) ?? 0;
|
||||
|
||||
backupTimeLabel.text = DateUtil.generateCleanTimestamp(new Date(year, month - 1, day, hour, minute, second));
|
||||
backupTimeLabel.text = "Full Name: " + file + "\nLast Modified: " + stat.mtime.toString() + "\nSize: " + sizeInMB + " MB";
|
||||
#end
|
||||
|
||||
// button callbacks
|
||||
dialogCancel.onClick = function(_) hideDialog(DialogButton.CANCEL);
|
||||
|
||||
@@ -47,9 +47,9 @@ class WelcomeDialog extends Dialog
|
||||
|
||||
#if sys
|
||||
var stat = sys.FileSystem.stat(file);
|
||||
var sizeInMB = (stat.size / 1000000).round(2);
|
||||
var sizeInMB = (stat.size / 1000000).round(3);
|
||||
|
||||
fileText.tooltip = "Full Name: " + file + "\nLast Modified: " + stat.mtime.toString() + "\nSize: " + sizeInMB + "MB";
|
||||
fileText.tooltip = "Full Name: " + file + "\nLast Modified: " + stat.mtime.toString() + "\nSize: " + sizeInMB + " MB";
|
||||
#end
|
||||
|
||||
contentRecent.addComponent(fileText);
|
||||
|
||||
@@ -2,8 +2,8 @@ package funkin.ui.debug.stageeditor.toolboxes;
|
||||
|
||||
import haxe.ui.components.NumberStepper;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData;
|
||||
import funkin.util.SortUtil;
|
||||
import funkin.save.Save;
|
||||
import haxe.ui.components.Button;
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
package funkin.ui.debug.stats;
|
||||
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.util.FlxColor;
|
||||
import openfl.display.Shape;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.text.TextField;
|
||||
import openfl.text.TextFormat;
|
||||
|
||||
class FunkinStatsGraph extends Sprite
|
||||
{
|
||||
static inline var AXIS_COLOR:FlxColor = 0xffffff;
|
||||
static inline var AXIS_ALPHA:Float = 0.5;
|
||||
static inline var HISTORY_MAX:Int = 100;
|
||||
|
||||
public var minValue:Float = FlxMath.MAX_VALUE_FLOAT;
|
||||
|
||||
public var maxValue:Float = FlxMath.MIN_VALUE_FLOAT;
|
||||
|
||||
public var graphColor:FlxColor;
|
||||
|
||||
public var history:Array<Float> = [];
|
||||
|
||||
public var textDisplay:TextField;
|
||||
|
||||
public var axis:Shape;
|
||||
|
||||
public var axisWidth:Int;
|
||||
|
||||
public var axisHeight:Int;
|
||||
|
||||
public function new(x:Int, y:Int, width:Int, height:Int, graphColor:FlxColor):Void
|
||||
{
|
||||
super();
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.graphColor = graphColor;
|
||||
this.axisWidth = width;
|
||||
this.axisHeight = height;
|
||||
|
||||
textDisplay = new TextField();
|
||||
textDisplay.width = 500;
|
||||
textDisplay.y -= 22;
|
||||
textDisplay.selectable = false;
|
||||
textDisplay.mouseEnabled = false;
|
||||
textDisplay.defaultTextFormat = new TextFormat('Monsterrat', 12, graphColor, JUSTIFY);
|
||||
textDisplay.antiAliasType = NORMAL;
|
||||
textDisplay.sharpness = 100;
|
||||
textDisplay.multiline = true;
|
||||
addChild(textDisplay);
|
||||
|
||||
axis = new Shape();
|
||||
axis.x += 4;
|
||||
addChild(axis);
|
||||
|
||||
drawAxes();
|
||||
}
|
||||
|
||||
function drawAxes():Void
|
||||
{
|
||||
axis.graphics.clear();
|
||||
|
||||
axis.graphics.lineStyle(1, AXIS_COLOR, AXIS_ALPHA, false, null, null, MITER, 255);
|
||||
|
||||
axis.graphics.moveTo(0, 0);
|
||||
|
||||
axis.graphics.lineTo(0, axisHeight);
|
||||
|
||||
axis.graphics.moveTo(0, axisHeight);
|
||||
|
||||
axis.graphics.lineTo(axisWidth, axisHeight);
|
||||
}
|
||||
|
||||
function drawGraph():Void
|
||||
{
|
||||
graphics.clear();
|
||||
|
||||
graphics.lineStyle(1, graphColor, 1, false, null, null, MITER, 255);
|
||||
|
||||
if (history.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var inc:Float = (axisWidth - 2) / (HISTORY_MAX - 1);
|
||||
var range:Float = Math.max(maxValue - minValue, maxValue * 0.1);
|
||||
var scale:Float = axisHeight / range;
|
||||
|
||||
for (i in 0...history.length)
|
||||
{
|
||||
final pointY:Float = axisHeight - ((history[i] - minValue) * scale) - 1;
|
||||
|
||||
if (i == 0) graphics.moveTo(axis.x, pointY);
|
||||
|
||||
graphics.lineTo(axis.x + 1 + (i * inc), pointY);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(value:Float):Void
|
||||
{
|
||||
history.push(value);
|
||||
|
||||
if (history.length > HISTORY_MAX)
|
||||
{
|
||||
history.shift();
|
||||
}
|
||||
|
||||
maxValue = Math.max(maxValue, value);
|
||||
minValue = Math.min(minValue, value);
|
||||
|
||||
drawGraph();
|
||||
}
|
||||
|
||||
public function average():Float
|
||||
{
|
||||
if (history.length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var sum:Float = 0;
|
||||
|
||||
for (v in history)
|
||||
{
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum / history.length;
|
||||
}
|
||||
|
||||
public function lowest():Float
|
||||
{
|
||||
if (history.length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var val:Float = history[0];
|
||||
|
||||
for (v in history)
|
||||
{
|
||||
if (v < val)
|
||||
{
|
||||
val = v;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public function destroy():Void
|
||||
{
|
||||
if (axis != null)
|
||||
{
|
||||
removeChild(axis);
|
||||
axis = null;
|
||||
}
|
||||
|
||||
history = null;
|
||||
}
|
||||
}
|
||||
@@ -246,6 +246,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
var prepForNewRank:Bool = false;
|
||||
var styleData:Null<FreeplayStyle> = null;
|
||||
var fromCharSelect:Bool = false;
|
||||
var forceSkipIntro:Bool = false;
|
||||
|
||||
public var freeplayArrow:Null<FlxText>;
|
||||
|
||||
@@ -277,7 +278,11 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
super(FlxColor.TRANSPARENT);
|
||||
|
||||
if (stickers?.members != null) stickerSubState = stickers;
|
||||
if (stickers?.members != null)
|
||||
{
|
||||
stickerSubState = stickers;
|
||||
forceSkipIntro = true;
|
||||
}
|
||||
|
||||
var backingCardPrep:Null<BackingCard> = null;
|
||||
|
||||
@@ -348,6 +353,14 @@ class FreeplayState extends MusicBeatSubState
|
||||
stickerSubState.degenStickers();
|
||||
}
|
||||
|
||||
if (fromResultsParams != null)
|
||||
{
|
||||
@:privateAccess
|
||||
this._parentState._constructor = () -> {
|
||||
return FreeplayState.build(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
#if FEATURE_DISCORD_RPC
|
||||
// Updating Discord Rich Presence
|
||||
DiscordClient.instance.setPresence({state: 'In the Menus', details: null});
|
||||
@@ -498,7 +511,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
var overhangStuff:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 164, FlxColor.BLACK);
|
||||
overhangStuff.y -= overhangStuff.height;
|
||||
|
||||
if (fromCharSelect)
|
||||
if (fromCharSelect || forceSkipIntro)
|
||||
{
|
||||
blackOverlayBullshitLOLXD.visible = false;
|
||||
overhangStuff.y = -100;
|
||||
@@ -764,7 +777,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
albumRoll.skipIntro();
|
||||
albumRoll.showStars();
|
||||
}
|
||||
else if (fromCharSelect)
|
||||
else if (fromCharSelect || forceSkipIntro)
|
||||
{
|
||||
albumRoll.skipIntro();
|
||||
albumRoll.showStars();
|
||||
@@ -813,10 +826,11 @@ class FreeplayState extends MusicBeatSubState
|
||||
rankCamera.fade(0xFF000000, 0, false, null, true);
|
||||
}
|
||||
|
||||
if (fromCharSelect)
|
||||
if (fromCharSelect || forceSkipIntro)
|
||||
{
|
||||
enterFromCharSel();
|
||||
if (fromCharSelect) enterFromCharSel();
|
||||
onDJIntroDone();
|
||||
forceSkipIntro = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -900,10 +914,10 @@ class FreeplayState extends MusicBeatSubState
|
||||
var randomCapsule:SongMenuItem = grpCapsules.recycle(SongMenuItem);
|
||||
randomCapsule.initRandom(styleData);
|
||||
randomCapsule.onConfirm = function() {
|
||||
capsuleOnConfirmRandom(randomCapsule);
|
||||
capsuleOnOpenRandom(randomCapsule);
|
||||
};
|
||||
|
||||
if (fromCharSelect || noJumpIn) randomCapsule.forcePosition();
|
||||
if (fromCharSelect || forceSkipIntro || noJumpIn) randomCapsule.forcePosition();
|
||||
else
|
||||
{
|
||||
randomCapsule.initJumpIn(0, force);
|
||||
@@ -933,7 +947,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
funnyMenu.newText.animation.curAnim.curFrame = 45 - ((i * 4) % 45);
|
||||
|
||||
// Stop the bounce-in animation when returning to freeplay from the character selection screen, or if noJumpIn is set to true
|
||||
if (fromCharSelect || noJumpIn) funnyMenu.forcePosition();
|
||||
if (fromCharSelect || forceSkipIntro || noJumpIn) funnyMenu.forcePosition();
|
||||
else
|
||||
funnyMenu.initJumpIn(0, force);
|
||||
|
||||
@@ -1034,6 +1048,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (fromResults.oldRank != null)
|
||||
{
|
||||
capsuleToRank.fakeRanking.rank = fromResults.oldRank;
|
||||
capsuleToRank.fakeBlurredRanking.rank = fromResults.oldRank;
|
||||
|
||||
sparks.frames = Paths.getSparrowAtlas('freeplay/sparks');
|
||||
sparks.animation.addByPrefix('sparks', 'sparks', 24, false);
|
||||
@@ -1067,6 +1082,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
trace(originalPos);
|
||||
|
||||
capsuleToRank.ranking.visible = false;
|
||||
capsuleToRank.blurredRanking.visible = false;
|
||||
|
||||
// Rank animation vibrations.
|
||||
HapticUtil.increasingVibrate(Constants.MIN_VIBRATION_AMPLITUDE, Constants.MAX_VIBRATION_AMPLITUDE, 0.6);
|
||||
@@ -1090,23 +1106,29 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
function rankDisplayNew(fromResults:Null<FromResultsParams>, capsuleToRank:SongMenuItem):Void
|
||||
{
|
||||
capsuleToRank.ranking.visible = false;
|
||||
capsuleToRank.fakeRanking.visible = false;
|
||||
capsuleToRank.ranking.visible = true;
|
||||
capsuleToRank.blurredRanking.visible = true;
|
||||
capsuleToRank.ranking.scale.set(20, 20);
|
||||
capsuleToRank.blurredRanking.scale.set(20, 20);
|
||||
|
||||
if (fromResults != null && fromResults.newRank != null)
|
||||
{
|
||||
capsuleToRank.ranking.playAnimationEach(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
capsuleToRank.ranking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
}
|
||||
|
||||
FlxTween.tween(capsuleToRank.ranking, {"scale.x": 0.9, "scale.y": 0.9}, 0.1);
|
||||
|
||||
new FlxTimer().start(0.1, _ -> {
|
||||
capsuleToRank.ranking.visible = true;
|
||||
if (fromResults != null && fromResults.newRank != null)
|
||||
{
|
||||
capsuleToRank.blurredRanking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
}
|
||||
FlxTween.tween(capsuleToRank.blurredRanking, {"scale.x": 0.9, "scale.y": 0.9}, 0.1);
|
||||
|
||||
new FlxTimer().start(0.1, _ -> {
|
||||
if (fromResults?.oldRank != null)
|
||||
{
|
||||
capsuleToRank.fakeRanking.visible = false;
|
||||
capsuleToRank.fakeBlurredRanking.visible = false;
|
||||
|
||||
sparks.visible = true;
|
||||
sparksADD.visible = true;
|
||||
@@ -1454,7 +1476,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
fadeDots(false);
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
FlxTween.tween(backButton, {alpha: 0.0001}, 0.4, {ease: FlxEase.quadOut});
|
||||
backTransitioning = true;
|
||||
FlxTween.tween(backButton, {alpha: 0}, 0.4, {ease: FlxEase.quadOut});
|
||||
#end
|
||||
|
||||
funnyCam.filtersEnabled = true;
|
||||
@@ -1561,6 +1584,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
Conductor.instance.update(FlxG.sound?.music?.time ?? 0.0);
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
if (backButton != null && !backTransitioning)
|
||||
{
|
||||
@@ -1569,7 +1594,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
backButton.animation.play("idle");
|
||||
backButton.alpha = backButton.restingOpacity;
|
||||
}
|
||||
backButton.active = controls.active;
|
||||
backButton.enabled = controls.active;
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -2066,13 +2091,6 @@ class FreeplayState extends MusicBeatSubState
|
||||
}
|
||||
#end
|
||||
|
||||
override function beatHit():Bool
|
||||
{
|
||||
backingCard.beatHit();
|
||||
|
||||
return super.beatHit();
|
||||
}
|
||||
|
||||
public override function destroy():Void
|
||||
{
|
||||
super.destroy();
|
||||
@@ -2127,7 +2145,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
FlxTween.tween(backButton, {x: FlxG.width + 300}, 0.45, {ease: FlxEase.expoIn});
|
||||
FlxTween.tween(backButton, {alpha: 0.0001}, 0.3, {ease: FlxEase.quadOut, startDelay: 0.15});
|
||||
FlxTween.tween(backButton, {alpha: 0}, 0.3, {ease: FlxEase.quadOut, startDelay: 0.15});
|
||||
#end
|
||||
|
||||
for (caps in grpCapsules.members)
|
||||
@@ -2303,7 +2321,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
intendedCompletion = Math.max(0, Scoring.tallyCompletion(songScore?.tallies));
|
||||
rememberedDifficulty = currentDifficulty;
|
||||
if (!capsuleAnim) generateSongList(currentFilter, false, true, true);
|
||||
currentCapsule.refreshDisplay(!prepForNewRank);
|
||||
if (change != 0) currentCapsule.refreshDisplay(!prepForNewRank);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2410,7 +2428,10 @@ class FreeplayState extends MusicBeatSubState
|
||||
}
|
||||
#end
|
||||
|
||||
function capsuleOnConfirmRandom(randomCapsule:SongMenuItem):Void
|
||||
/**
|
||||
* Called when hitting ENTER to open the instrumental choice for random capsule
|
||||
*/
|
||||
function capsuleOnOpenRandom(randomCapsule:SongMenuItem):Void
|
||||
{
|
||||
trace('RANDOM SELECTED');
|
||||
|
||||
@@ -2434,18 +2455,81 @@ class FreeplayState extends MusicBeatSubState
|
||||
return;
|
||||
}
|
||||
|
||||
var targetSong:SongMenuItem = FlxG.random.getObject(availableSongCapsules);
|
||||
controls.active = false;
|
||||
#if NO_FEATURE_TOUCH_CONTROLS
|
||||
letterSort.inputEnabled = false;
|
||||
#end
|
||||
var instrumentalChoices:Array<String> = ['default', 'random'];
|
||||
|
||||
// Seeing if I can do an animation...
|
||||
curSelected = grpCapsules.members.indexOf(targetSong);
|
||||
changeSelection(0); // Trigger an update.
|
||||
capsuleOptionsMenu = new CapsuleOptionsMenu(this, randomCapsule.targetPos.x + 175, randomCapsule.targetPos.y + 115, instrumentalChoices);
|
||||
capsuleOptionsMenu.cameras = [funnyCam];
|
||||
capsuleOptionsMenu.zIndex = 10000;
|
||||
add(capsuleOptionsMenu);
|
||||
|
||||
capsuleOptionsMenu.onConfirm = function(instChoice:String) {
|
||||
capsuleOnConfirmRandom(availableSongCapsules, instChoice);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when hitting ENTER on an instrumental choice for random capsule
|
||||
*/
|
||||
function capsuleOnConfirmRandom(availableSongCapsules:Array<SongMenuItem>, instChoice:String):Void
|
||||
{
|
||||
cleanupCapsuleOptionsMenu();
|
||||
controls.active = false;
|
||||
#if NO_FEATURE_TOUCH_CONTROLS
|
||||
letterSort.inputEnabled = false;
|
||||
#end
|
||||
|
||||
// Act like we hit Confirm on that song.
|
||||
capsuleOnConfirmDefault(targetSong);
|
||||
var targetSongCap:SongMenuItem = FlxG.random.getObject(availableSongCapsules);
|
||||
// Seeing if I can do an animation...
|
||||
curSelected = grpCapsules.members.indexOf(targetSongCap);
|
||||
changeSelection(); // Trigger an update. This will also fix the target variation.
|
||||
|
||||
var targetSongId:String = targetSongCap?.freeplayData?.data.id ?? 'unknown';
|
||||
var targetSongNullable:Null<Song> = SongRegistry.instance.fetchEntry(targetSongId);
|
||||
if (targetSongNullable == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find song with id (${targetSongId})');
|
||||
controls.active = true;
|
||||
#if NO_FEATURE_TOUCH_CONTROLS
|
||||
letterSort.inputEnabled = true;
|
||||
#end
|
||||
return;
|
||||
}
|
||||
var targetSong:Song = targetSongNullable;
|
||||
var targetDifficultyId:String = currentDifficulty;
|
||||
var targetVariation:Null<String> = currentVariation;
|
||||
|
||||
var targetDifficulty:Null<SongDifficulty> = targetSong.getDifficulty(targetDifficultyId, targetVariation);
|
||||
if (targetDifficulty == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find difficulty with id (${targetDifficultyId})');
|
||||
controls.active = true;
|
||||
#if NO_FEATURE_TOUCH_CONTROLS
|
||||
letterSort.inputEnabled = true;
|
||||
#end
|
||||
return;
|
||||
}
|
||||
|
||||
if (instChoice == 'random')
|
||||
{
|
||||
var baseInstrumentalId:String = targetSong.getBaseInstrumentalId(targetDifficultyId, targetDifficulty?.variation ?? Constants.DEFAULT_VARIATION) ?? '';
|
||||
var altInstrumentalIds:Array<String> = targetSong.listAltInstrumentalIds(targetDifficultyId,
|
||||
targetDifficulty?.variation ?? Constants.DEFAULT_VARIATION) ?? [];
|
||||
|
||||
// Choose a random instrumental
|
||||
var instrumentalIds:Array<String> = [baseInstrumentalId].concat(altInstrumentalIds);
|
||||
var targetInstrumentalId:String = FlxG.random.getObject(instrumentalIds);
|
||||
// Hit Confirm on that song with random instrumental
|
||||
capsuleOnConfirmDefault(targetSongCap, targetInstrumentalId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hit Confirm on that song with default instrumental
|
||||
capsuleOnConfirmDefault(targetSongCap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2614,6 +2698,13 @@ class FreeplayState extends MusicBeatSubState
|
||||
new FlxTimer().start(styleData?.getStartDelay(), function(tmr:FlxTimer) {
|
||||
FunkinSound.emptyPartialQueue();
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
if (backButton != null)
|
||||
{
|
||||
backTransitioning = true;
|
||||
FlxTween.tween(backButton, {alpha: 0}, 0.2, {ease: FlxEase.quadOut});
|
||||
}
|
||||
#end
|
||||
funnyCam.fade(FlxColor.BLACK, 0.2, false, function() {
|
||||
Paths.setCurrentLevel(cap?.freeplayData?.levelId);
|
||||
LoadingState.loadPlayState(
|
||||
@@ -2732,9 +2823,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (currentCapsule.freeplayData == null) albumRoll.albumId = null;
|
||||
|
||||
changeDiff();
|
||||
if (currentCapsule.freeplayData == null) currentCapsule.refreshDisplay();
|
||||
else
|
||||
currentCapsule.refreshDisplay(!prepForNewRank);
|
||||
currentCapsule.refreshDisplay(currentCapsule.freeplayData == null);
|
||||
|
||||
for (index => capsule in grpCapsules.members)
|
||||
{
|
||||
@@ -2823,7 +2912,6 @@ class FreeplayState extends MusicBeatSubState
|
||||
if (songDifficulty != null)
|
||||
{
|
||||
Conductor.instance.mapTimeChanges(songDifficulty.timeChanges);
|
||||
Conductor.instance.update(FlxG.sound?.music?.time ?? 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,10 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
public var favIcon:FlxSprite;
|
||||
|
||||
public var ranking:FreeplayRank;
|
||||
public var blurredRanking:FreeplayRank;
|
||||
|
||||
public var fakeRanking:FreeplayRank;
|
||||
public var fakeBlurredRanking:FreeplayRank;
|
||||
|
||||
public var targetPos:FlxPoint = new FlxPoint();
|
||||
public var doLerp:Bool = false;
|
||||
@@ -144,14 +146,25 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
// doesn't get added, simply is here to help with visibility of things for the pop in!
|
||||
grpHide = new FlxGroup();
|
||||
|
||||
fakeRanking = new FreeplayRank(400, 15);
|
||||
fakeRanking = new FreeplayRank(420, 41);
|
||||
add(fakeRanking);
|
||||
|
||||
fakeBlurredRanking = new FreeplayRank(fakeRanking.x, fakeRanking.y);
|
||||
fakeBlurredRanking.shader = new GaussianBlurShader(1);
|
||||
add(fakeBlurredRanking);
|
||||
|
||||
fakeRanking.visible = false;
|
||||
fakeBlurredRanking.visible = false;
|
||||
|
||||
fakeRanking.visible = false;
|
||||
|
||||
ranking = new FreeplayRank(400, 15);
|
||||
ranking = new FreeplayRank(420, 41);
|
||||
add(ranking);
|
||||
|
||||
blurredRanking = new FreeplayRank(ranking.x, ranking.y);
|
||||
blurredRanking.shader = new GaussianBlurShader(1);
|
||||
add(blurredRanking);
|
||||
|
||||
sparkle = new FlxSprite(ranking.x, ranking.y);
|
||||
sparkle.frames = Paths.getSparrowAtlas('freeplay/sparkle');
|
||||
sparkle.animation.addByPrefix('sparkle', 'sparkle Export0', 24, false);
|
||||
@@ -464,6 +477,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
sparkle.visible = false;
|
||||
|
||||
this.ranking.rank = newRank;
|
||||
this.blurredRanking.rank = newRank;
|
||||
|
||||
if (newRank == PERFECT_GOLD)
|
||||
{
|
||||
@@ -704,7 +718,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
|
||||
public function intendedY(index:Float):Float
|
||||
{
|
||||
return (index * ((height * realScaled) + 10)) + 120;
|
||||
return (index * ((capsule.height * realScaled) + 10)) + 120;
|
||||
}
|
||||
|
||||
function set_selected(value:Bool):Bool
|
||||
@@ -754,16 +768,10 @@ class SongMenuItem extends FlxSpriteGroup
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds blurred and unblurred versions of the rank icon
|
||||
*/
|
||||
class FreeplayRank extends FlxSpriteGroup
|
||||
class FreeplayRank extends FlxSprite
|
||||
{
|
||||
public var rank(default, set):Null<ScoringRank> = null;
|
||||
|
||||
var spr:FlxSprite;
|
||||
var blur:FlxSprite;
|
||||
|
||||
function set_rank(val:Null<ScoringRank>):Null<ScoringRank>
|
||||
{
|
||||
rank = val;
|
||||
@@ -776,8 +784,7 @@ class FreeplayRank extends FlxSpriteGroup
|
||||
{
|
||||
this.visible = true;
|
||||
|
||||
spr.animation.play(val.getFreeplayRankIconAsset(), true, false);
|
||||
blur.animation.play(val.getFreeplayRankIconAsset(), true, false);
|
||||
animation.play(val.getFreeplayRankIconAsset(), true, false);
|
||||
|
||||
centerOffsets(false);
|
||||
|
||||
@@ -814,24 +821,14 @@ class FreeplayRank extends FlxSpriteGroup
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
blur = new FlxSprite();
|
||||
blur.frames = Paths.getSparrowAtlas('freeplay/rankbadges');
|
||||
blur.shader = new GaussianBlurShader(1);
|
||||
add(blur);
|
||||
frames = Paths.getSparrowAtlas('freeplay/rankbadges');
|
||||
|
||||
spr = new FlxSprite();
|
||||
spr.frames = Paths.getSparrowAtlas('freeplay/rankbadges');
|
||||
add(spr);
|
||||
|
||||
for (i in members)
|
||||
{
|
||||
i.animation.addByPrefix('PERFECT', 'PERFECT rank0', 24, false);
|
||||
i.animation.addByPrefix('EXCELLENT', 'EXCELLENT rank0', 24, false);
|
||||
i.animation.addByPrefix('GOOD', 'GOOD rank0', 24, false);
|
||||
i.animation.addByPrefix('PERFECTSICK', 'PERFECT rank GOLD', 24, false);
|
||||
i.animation.addByPrefix('GREAT', 'GREAT rank0', 24, false);
|
||||
i.animation.addByPrefix('LOSS', 'LOSS rank0', 24, false);
|
||||
}
|
||||
animation.addByPrefix('PERFECT', 'PERFECT rank0', 24, false);
|
||||
animation.addByPrefix('EXCELLENT', 'EXCELLENT rank0', 24, false);
|
||||
animation.addByPrefix('GOOD', 'GOOD rank0', 24, false);
|
||||
animation.addByPrefix('PERFECTSICK', 'PERFECT rank GOLD', 24, false);
|
||||
animation.addByPrefix('GREAT', 'GREAT rank0', 24, false);
|
||||
animation.addByPrefix('LOSS', 'LOSS rank0', 24, false);
|
||||
|
||||
blend = BlendMode.ADD;
|
||||
|
||||
@@ -841,22 +838,6 @@ class FreeplayRank extends FlxSpriteGroup
|
||||
scale.set(0.9, 0.9);
|
||||
updateHitbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays an animation for each member of the group
|
||||
* Just passes the arguments to `animation.play`, since that's not available in FlxGroups
|
||||
* @param animName The name of the animation to play
|
||||
* @param force false
|
||||
* @param reversed false
|
||||
* @param frame 0
|
||||
*/
|
||||
public function playAnimationEach(animName:String, force = false, reversed = false, frame = 0):Void
|
||||
{
|
||||
for (i in members)
|
||||
{
|
||||
i.animation.play(animName, force, reversed, frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CapsuleNumber extends FlxSprite
|
||||
|
||||
@@ -7,7 +7,7 @@ import funkin.modding.events.ScriptEvent.HitNoteScriptEvent;
|
||||
import funkin.modding.events.ScriptEvent.SongTimeScriptEvent;
|
||||
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.character.CharacterData.CharacterDataParser;
|
||||
import haxe.ui.containers.Box;
|
||||
import haxe.ui.core.Component;
|
||||
import haxe.ui.events.AnimationEvent;
|
||||
|
||||
@@ -394,7 +394,8 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
if (ControlsHandler.usingExternalInputDevice)
|
||||
{
|
||||
#end
|
||||
testStrumline.y = Preferences.downscroll ? FlxG.height - (testStrumline.height + 45) - Constants.STRUMLINE_Y_OFFSET : (testStrumline.height / 2)
|
||||
var height = testStrumline.strumlineNotes.members[0].height;
|
||||
testStrumline.y = Preferences.downscroll ? FlxG.height - (height + 45) - Constants.STRUMLINE_Y_OFFSET : (height / 2)
|
||||
- Constants.STRUMLINE_Y_OFFSET;
|
||||
if (Preferences.downscroll) jumpInText.y = FlxG.height - 425;
|
||||
testStrumline.isDownscroll = Preferences.downscroll;
|
||||
|
||||
@@ -290,6 +290,11 @@ class OptionsMenu extends Page<OptionsMenuPageName>
|
||||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
if ((FlxG.sound.music?.volume ?? 1.0) < 0.8)
|
||||
{
|
||||
FlxG.sound.music.volume += 0.5 * elapsed;
|
||||
}
|
||||
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
backButton.active = (!goingBack) ? !items.busy : true;
|
||||
#end
|
||||
|
||||
@@ -16,6 +16,7 @@ import funkin.ui.TextMenuList.TextMenuItem;
|
||||
import funkin.ui.options.items.CheckboxPreferenceItem;
|
||||
import funkin.ui.options.items.NumberPreferenceItem;
|
||||
import funkin.ui.options.items.EnumPreferenceItem;
|
||||
import funkin.ui.debug.FunkinDebugDisplay.DebugDisplayMode;
|
||||
#if mobile
|
||||
import funkin.mobile.ui.FunkinBackButton;
|
||||
import funkin.mobile.input.ControlsHandler;
|
||||
@@ -145,9 +146,21 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
Preferences.zoomCamera = value;
|
||||
}, Preferences.zoomCamera);
|
||||
#if !mobile
|
||||
createPrefItemCheckbox('Debug Display', 'If enabled, FPS and other debug stats will be displayed.', function(value:Bool):Void {
|
||||
createPrefItemEnum('Debug Display', 'If enabled, FPS and other debug stats will be displayed.', [
|
||||
"Advanced" => DebugDisplayMode.ADVANCED,
|
||||
"Simple" => DebugDisplayMode.SIMPLE,
|
||||
"Off" => DebugDisplayMode.OFF
|
||||
], function(key:String, value:DebugDisplayMode):Void {
|
||||
Preferences.debugDisplay = value;
|
||||
}, Preferences.debugDisplay);
|
||||
}, switch (Preferences.debugDisplay)
|
||||
{
|
||||
case DebugDisplayMode.SIMPLE: "Simple";
|
||||
case DebugDisplayMode.ADVANCED: "Advanced";
|
||||
default: "Off";
|
||||
});
|
||||
createPrefItemPercentage('Debug Display BG', "Change debug display's background opacity", function(value:Int):Void {
|
||||
Preferences.debugDisplayBGOpacity = value;
|
||||
}, Preferences.debugDisplayBGOpacity);
|
||||
createPrefItemCheckbox('Pause on Unfocus', 'If enabled, game automatically pauses when it loses focus.', function(value:Bool):Void {
|
||||
Preferences.autoPause = value;
|
||||
}, Preferences.autoPause);
|
||||
|
||||
@@ -330,6 +330,11 @@ class StoryMenuState extends MusicBeatState
|
||||
|
||||
handleKeyPresses();
|
||||
|
||||
if ((FlxG.sound.music?.volume ?? 1.0) < 0.8)
|
||||
{
|
||||
FlxG.sound.music.volume += 0.5 * elapsed;
|
||||
}
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
|
||||
@@ -317,6 +317,11 @@ class TitleState extends MusicBeatState
|
||||
}
|
||||
if (pressedEnter && !skippedIntro && initialized) skipIntro();
|
||||
|
||||
if ((FlxG.sound.music?.volume ?? 1.0) < 0.8 && initialized)
|
||||
{
|
||||
FlxG.sound.music.volume += 0.5 * elapsed;
|
||||
}
|
||||
|
||||
// TODO: Maybe use the dxdy method for swiping instead.
|
||||
if (controls.UI_LEFT #if mobile || SwipeUtil.justSwipedLeft #end) swagShader.update(-elapsed * 0.1);
|
||||
if (controls.UI_RIGHT #if mobile || SwipeUtil.justSwipedRight #end) swagShader.update(elapsed * 0.1);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user