mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-14 19:25:16 -05:00
Merge branch 'rewrite/master' into bugfix/instrumental-id
This commit is contained in:
commit
c05f053fe3
35 changed files with 724 additions and 395 deletions
56
.github/ISSUE_TEMPLATE/bug.md
vendored
56
.github/ISSUE_TEMPLATE/bug.md
vendored
|
@ -5,40 +5,46 @@ title: 'Bug Report: [DESCRIBE YOUR BUG IN DETAIL HERE]'
|
|||
labels: bug
|
||||
---
|
||||
|
||||
[weed]: <> (FILL THIS ISSUE THING OUT AS MUCH AS POSSIBLE)
|
||||
[weed]: <> (OR ELSE YOUR ISSUE WILL BE LESS LIKELY TO BE SOLVED!)
|
||||
[weed]: <> (DO NOT POST ABOUT ISSUES FROM OTHER FNF MOD ENGINES! I CANNOT AND PROBABLY WON'T SOLVE THOSE!)
|
||||
[weed]: <> (GO TO THEIR RESPECTIVE GITHUB ISSUES AND REPORT THEM THERE LOL!)
|
||||
<!-- FILL THIS ISSUE THING OUT AS MUCH AS POSSIBLE
|
||||
OR ELSE YOUR ISSUE WILL BE LESS LIKELY TO BE SOLVED!
|
||||
|
||||
[weed]: <> (ALSO MAKE SURE THAT YOU USE PROPER LABELS, IF YOU'RE RUNNING INTO COMPILER ISSUES, USE THE compiler issue LABEL!!!)
|
||||
Do not post about issues from other FNF mod engines!
|
||||
We cannot and probably won't solve those!
|
||||
You can hopefully go to their respective GitHub issues and report them there, thank you :)
|
||||
|
||||
#### Please check for duplicates or similar issues, as well performing simple troubleshooting steps (such as clearing cookies, clearing AppData, trying another browser) before submitting an issue.
|
||||
### If you are playing the game in a browser, what site are you playing it from?
|
||||
Please check for duplicates or similar issues, as well performing simple troubleshooting steps (such as clearing cookies, clearing AppData, trying another browser) before submitting an issue.
|
||||
|
||||
[weed]: <> (Put an X in the [ ] thingies to fill out checkbox!)
|
||||
[weed]: <> (something like [x] pretty much, don't screw up or you will look stupid)
|
||||
From Joel On Software:
|
||||
|
||||
- [ ] [Newgrounds](https://www.newgrounds.com/portal/view/770371)
|
||||
- [ ] [Itch.io](https://ninja-muffin24.itch.io/funkin)? Specify below
|
||||
- - [ ] Windows
|
||||
- - [ ] Mac
|
||||
- - [ ] Linux
|
||||
"It’s pretty easy to remember the rule for a good bug report. Every good bug report needs exactly three things.
|
||||
|
||||
### If you are playing the game in a browser, what browser are you using?
|
||||
1. Steps to reproduce,
|
||||
2. What you expected to see, and
|
||||
3. What you saw instead."
|
||||
|
||||
[weed]: <> (Again, put an x in the [ ] box!)
|
||||
-->
|
||||
|
||||
- [ ] Google Chrome (or chomium based like Brave, vivaldi, MS Edge)
|
||||
- [ ] Firefox
|
||||
- [ ] Safari
|
||||
## Describe the bug
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
## What version of the game are you using? Look in the bottom left corner of the main menu. (ex: 0.2.7, 0.2.1, shit like that)
|
||||
## To Reproduce
|
||||
<!-- Describe in DETAIL how to reproduce the bug/issue you are running into. -->
|
||||
## Expected behavior
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
## Screenshots/Video
|
||||
<!-- If applicable, add screenshots/video to help explain your problem.
|
||||
Remember to mark the area in the application thats impacted. -->
|
||||
|
||||
## Have you identified any steps to reproduce the bug? If so, please describe them below in as much detail as possible. Use images if possible.
|
||||
## Desktop
|
||||
- OS:
|
||||
<!-- [e.g. Windows 10, 11, Mac, Linux Mint, Ubuntu, Arch (btw)] -->
|
||||
- Browser
|
||||
<!-- [e.g. chrome, safari, firefox, edge, operaGX] -->
|
||||
- Version:
|
||||
<!-- [e.g. 0.4.0, 0.3.3, this can be found in the bottom left corner of the main menu!] -->
|
||||
|
||||
## Please describe your issue. Provide extensive detail and images if possible.
|
||||
## Additional context
|
||||
<!-- Add any other context about the problem here. -->
|
||||
|
||||
|
||||
|
||||
## If you're game is FROZEN and you're playing a web version, press F12 to open up browser dev window, and go to console, and copy-paste whatever red error you're getting
|
||||
<!-- If you're game is FROZEN and you're playing a web version, press F12 to open up browser dev window, and go to console, and copy-paste whatever red error you're getting -->
|
||||
|
|
25
.github/ISSUE_TEMPLATE/compiling.md
vendored
25
.github/ISSUE_TEMPLATE/compiling.md
vendored
|
@ -1,25 +0,0 @@
|
|||
---
|
||||
name: Compiling help
|
||||
about: If you need help compiling the game, and you're running into issues. (Look through the 'compiling help' label in case it's been solved!)
|
||||
title: 'Compiling help: [BRIEF DESCRIPTION / ERROR MESSAGE OUTPUT]'
|
||||
labels: compiling help
|
||||
---
|
||||
|
||||
[weed]: <> (FILL THIS ISSUE THING OUT AS MUCH AS POSSIBLE)
|
||||
[weed]: <> (OR ELSE YOUR ISSUE WILL BE LESS LIKELY TO BE SOLVED!)
|
||||
[weed]: <> (DO NOT POST ABOUT ISSUES FROM OTHER FNF MOD ENGINES! I CANNOT AND PROBABLY WON'T SOLVE THOSE!)
|
||||
[weed]: <> (GO TO THEIR RESPECTIVE GITHUB ISSUES AND REPORT THEM THERE LOL!)
|
||||
|
||||
#### Please check for duplicates or similar compiler issues by filtering for 'compiler help'
|
||||
|
||||
[weed]: <> (Put an X in the [ ] thingies to fill out checkbox!)
|
||||
[weed]: <> (something like [x] pretty much, don't screw up or you will look stupid)
|
||||
|
||||
|
||||
- [ ] Windows
|
||||
- [ ] Mac
|
||||
- [ ] Linux
|
||||
- [ ] HTML5
|
||||
|
||||
## Please describe your issue. Provide extensive detail and images if possible.
|
||||
|
12
.github/ISSUE_TEMPLATE/question.md
vendored
12
.github/ISSUE_TEMPLATE/question.md
vendored
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
name: Question
|
||||
about: Ask a general question
|
||||
title: 'Question: '
|
||||
labels: question
|
||||
---
|
||||
|
||||
[weed]: <> (This isn't a place for AMA type questions, if you want to ask any of the devs something, reach out to them on twitter prob )
|
||||
[weed]: <> (any biz bullshit can go to cameron.taylor.ninja@gmail.com)
|
||||
|
||||
#### Please check for duplicates or similar issues before asking your question.
|
||||
## What is your question?
|
35
CHANGELOG.md
35
CHANGELOG.md
|
@ -4,7 +4,19 @@ 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.4.0] - 2024-05-??
|
||||
## [0.4.1]
|
||||
### Changed
|
||||
- Tweaked the chart for Lit Up some more to fix some offset notes.
|
||||
### Fixed
|
||||
- Bug where Dadbattle shows up as Dadbattle Erect when returning to freeplay
|
||||
- Fixed 2Hot not appearing under the "#" category in Freeplay menu
|
||||
- Fixed a bug where the Chart Editor would crash when attempting to select an event with the Event toolbox open
|
||||
- Improved offsets for Pico and Tankman opponents so they don't slide around as much.
|
||||
- Fixed an issue where M.I.L.F. displayed the wrong difficulty rating in the Freeplay menu.
|
||||
|
||||
|
||||
|
||||
## [0.4.0] - 2024-06-06
|
||||
### Added
|
||||
- 2 new Erect remixes, Eggnog and Satin Panties. Check them out from the Freeplay menu!
|
||||
- Major visual improvements to the Results screen, with additional animations and audio based on your performance.
|
||||
|
@ -12,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Freeplay now plays a preview of songs when you hover over them.
|
||||
- Added a Charter field to the chart format, to allow for crediting the creator of a level's chart.
|
||||
- You can see who charted a song from the Pause menu.
|
||||
- Added a new Scroll Speed chart event to change the note speed mid-song (thanks )
|
||||
- Added a new Scroll Speed chart event to change the note speed mid-song (thanks burgerballs!)
|
||||
### Changed
|
||||
- Tweaked the charts for several songs:
|
||||
- Tutorial (increased the note speed slightly)
|
||||
|
@ -27,14 +39,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Stress
|
||||
- Lit Up
|
||||
- Favorite songs marked in Freeplay are now stored between sessions.
|
||||
- The Freeplay easter eggs are now easier to see.
|
||||
- In the event that the game cannot load your save data, it will now perform a backup before clearing it, so that we can try to repair it in the future.
|
||||
- Custom note styles are now properly supported for songs; add new notestyles via JSON, then select it for use from the Chart Editor Metadata toolbox. (thanks Keoiki!)
|
||||
- Health icons now support a Winning frame without requiring a spritesheet, simply include a third frame in the icon file. (thanks gamerbross!)
|
||||
- Remember that for more complex behaviors such as animations or transitions, you should use an XML file to define each frame.
|
||||
- Improved the Event Toolbox in the Chart Editor; dropdowns are now bigger, include search field, and display elements in alphabetical order rather than a random order.
|
||||
### Fixed
|
||||
- Fixed an issue where Nene's visualizer would not play on Desktop builds
|
||||
- Fixed a bug where the game would silently fail to load saves on HTML5
|
||||
- Fixed some bugs with the props on the Story Menu not bopping properly
|
||||
- Improved offsets for Pico and Tankman opponents so they don't slide around as much.
|
||||
- Additional fixes to the Loading bar on HTML5 (thanks lemz1!)
|
||||
- Fixed several bugs with the TitleState, including missing music when returning from the Main Menu (thanks gamerbross!)
|
||||
- Fixed a camera bug in the Main Menu (thanks richTrash21!)
|
||||
|
@ -49,6 +63,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Made improvements to compiling documentation (thanks gedehari!)
|
||||
- Fixed a crash on Linux caused by an old version of hxCodec (thanks Noobz4Life!)
|
||||
- Optimized animation handling for characters (thanks richTrash21!)
|
||||
- Made improvements to compiling documentation (thanks gedehari!)
|
||||
- Fixed a bug where pressing the volume keys would stop the Toy commercial (thanks gamerbross!)
|
||||
- Fixed a bug where the Chart Editor Playtest would crash when losing (thanks gamerbross!)
|
||||
- Removed a large number of unused imports to optimize builds (thanks Ethan-makes-music!)
|
||||
- Fixed a bug where hold notes would be positioned wrong on downscroll (thanks MaybeMaru!)
|
||||
- Additional fixes to the Loading bar on HTML5 (thanks lemz1!)
|
||||
- Fixed a crash in Freeplay caused by a level referencing an invalid song (thanks gamerbross!)
|
||||
- Improved debug logging for unscripted stages (thanks gamerbross!)
|
||||
- Fixed a bug where changing difficulties in Story mode wouldn't update the score (thanks sectorA!)
|
||||
- Fixed an issue where the Chart Editor would use an incorrect instrumental on imported Legacy songs (thanks gamerbross!)
|
||||
- Fixed a camera bug in the Main Menu (thanks richTrash21!)
|
||||
- Fixed several bugs with the TitleState, including missing music when returning from the Main Menu (thanks gamerbross!)
|
||||
- Fixed a bug where opening the game from the command line would crash the preloader (thanks NotHyper474!)
|
||||
- Fixed a bug where hold notes would display improperly in the Chart Editor when downscroll was enabled for gameplay (thanks gamerbross!)
|
||||
- Fixed a bug where characters would sometimes use the wrong scale value (thanks PurSnake!)
|
||||
- Additional bug fixes and optimizations.
|
||||
|
||||
## [0.3.3] - 2024-05-14
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Friday Night Funkin' is a rhythm game. Built using HaxeFlixel for Ludum Dare 47.
|
||||
|
||||
This game was made with love to Newgrounds and it's community. Extra love to Tom Fulp.
|
||||
This game was made with love to Newgrounds and its community. Extra love to Tom Fulp.
|
||||
|
||||
- [Playable web demo on Newgrounds!](https://www.newgrounds.com/portal/view/770371)
|
||||
- [Demo download builds for Windows, Mac, and Linux from Itch.io!](https://ninja-muffin24.itch.io/funkin)
|
||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit 4427687c487d643c6051f8c4610470326c87ea4d
|
||||
Subproject commit c22ed15e83182f628d7830773021e3e48c596174
|
|
@ -6,9 +6,10 @@
|
|||
- `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git`
|
||||
- If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way.
|
||||
2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`)
|
||||
3. Install all haxelibs of the current branch by running `hmm install`
|
||||
4. Setup lime: `haxelib run lime setup`
|
||||
5. Platform setup
|
||||
3. Download Git from [git-scm.com](https://www.git-scm.com)
|
||||
4. Install all haxelibs of the current branch by running `hmm install`
|
||||
5. Setup lime: `haxelib run lime setup`
|
||||
6. Platform setup
|
||||
- For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe)
|
||||
- When prompted, select "Individual Components" and make sure to download the following:
|
||||
- MSVC v143 VS 2022 C++ x64/x86 build tools
|
||||
|
@ -16,9 +17,10 @@
|
|||
- Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/)
|
||||
- Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/)
|
||||
- HTML5: Compiles without any extra setup
|
||||
6. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug`
|
||||
7. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State).
|
||||
7. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug`
|
||||
8. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State).
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
- During the cloning process, you may experience an error along the lines of `error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)` due to poor connectivity. A common fix is to run ` git config --global http.postBuffer 4096M`.
|
||||
|
||||
|
|
|
@ -227,12 +227,12 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
// already paused before we lost focus.
|
||||
if (_lostFocus && !_alreadyPaused)
|
||||
{
|
||||
trace('Resuming audio (${this._label}) on focus!');
|
||||
// trace('Resuming audio (${this._label}) on focus!');
|
||||
resume();
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Not resuming audio (${this._label}) on focus!');
|
||||
// trace('Not resuming audio (${this._label}) on focus!');
|
||||
}
|
||||
_lostFocus = false;
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
*/
|
||||
override function onFocusLost():Void
|
||||
{
|
||||
trace('Focus lost, pausing audio!');
|
||||
// trace('Focus lost, pausing audio!');
|
||||
_lostFocus = true;
|
||||
_alreadyPaused = _paused;
|
||||
pause();
|
||||
|
|
|
@ -54,12 +54,12 @@ class ABotVis extends FlxTypedSpriteGroup<FlxSprite>
|
|||
public function initAnalyzer()
|
||||
{
|
||||
@:privateAccess
|
||||
analyzer = new SpectralAnalyzer(snd._channel.__source, 7, 0.1, 30);
|
||||
analyzer = new SpectralAnalyzer(snd._channel.__source, 7, 0.1, 40);
|
||||
|
||||
#if desktop
|
||||
// On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5
|
||||
// So we want to manually change it!
|
||||
analyzer.fftN = 512;
|
||||
analyzer.fftN = 256;
|
||||
#end
|
||||
|
||||
// analyzer.maxDb = -35;
|
||||
|
|
|
@ -58,7 +58,11 @@ class Controls extends FlxActionSet
|
|||
var _back = new FunkinAction(Action.BACK);
|
||||
var _pause = new FunkinAction(Action.PAUSE);
|
||||
var _reset = new FunkinAction(Action.RESET);
|
||||
var _screenshot = new FunkinAction(Action.SCREENSHOT);
|
||||
var _window_screenshot = new FunkinAction(Action.WINDOW_SCREENSHOT);
|
||||
var _window_fullscreen = new FunkinAction(Action.WINDOW_FULLSCREEN);
|
||||
var _freeplay_favorite = new FunkinAction(Action.FREEPLAY_FAVORITE);
|
||||
var _freeplay_left = new FunkinAction(Action.FREEPLAY_LEFT);
|
||||
var _freeplay_right = new FunkinAction(Action.FREEPLAY_RIGHT);
|
||||
var _cutscene_advance = new FunkinAction(Action.CUTSCENE_ADVANCE);
|
||||
var _debug_menu = new FunkinAction(Action.DEBUG_MENU);
|
||||
var _debug_chart = new FunkinAction(Action.DEBUG_CHART);
|
||||
|
@ -66,7 +70,6 @@ class Controls extends FlxActionSet
|
|||
var _volume_up = new FunkinAction(Action.VOLUME_UP);
|
||||
var _volume_down = new FunkinAction(Action.VOLUME_DOWN);
|
||||
var _volume_mute = new FunkinAction(Action.VOLUME_MUTE);
|
||||
var _fullscreen = new FunkinAction(Action.FULLSCREEN);
|
||||
|
||||
var byName:Map<String, FunkinAction> = new Map<String, FunkinAction>();
|
||||
|
||||
|
@ -233,10 +236,30 @@ class Controls extends FlxActionSet
|
|||
inline function get_RESET()
|
||||
return _reset.check();
|
||||
|
||||
public var SCREENSHOT(get, never):Bool;
|
||||
public var WINDOW_FULLSCREEN(get, never):Bool;
|
||||
|
||||
inline function get_SCREENSHOT()
|
||||
return _screenshot.check();
|
||||
inline function get_WINDOW_FULLSCREEN()
|
||||
return _window_fullscreen.check();
|
||||
|
||||
public var WINDOW_SCREENSHOT(get, never):Bool;
|
||||
|
||||
inline function get_WINDOW_SCREENSHOT()
|
||||
return _window_screenshot.check();
|
||||
|
||||
public var FREEPLAY_FAVORITE(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_FAVORITE()
|
||||
return _freeplay_favorite.check();
|
||||
|
||||
public var FREEPLAY_LEFT(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_LEFT()
|
||||
return _freeplay_left.check();
|
||||
|
||||
public var FREEPLAY_RIGHT(get, never):Bool;
|
||||
|
||||
inline function get_FREEPLAY_RIGHT()
|
||||
return _freeplay_right.check();
|
||||
|
||||
public var CUTSCENE_ADVANCE(get, never):Bool;
|
||||
|
||||
|
@ -273,11 +296,6 @@ class Controls extends FlxActionSet
|
|||
inline function get_VOLUME_MUTE()
|
||||
return _volume_mute.check();
|
||||
|
||||
public var FULLSCREEN(get, never):Bool;
|
||||
|
||||
inline function get_FULLSCREEN()
|
||||
return _fullscreen.check();
|
||||
|
||||
public function new(name, scheme:KeyboardScheme = null)
|
||||
{
|
||||
super(name);
|
||||
|
@ -294,7 +312,11 @@ class Controls extends FlxActionSet
|
|||
add(_back);
|
||||
add(_pause);
|
||||
add(_reset);
|
||||
add(_screenshot);
|
||||
add(_window_screenshot);
|
||||
add(_window_fullscreen);
|
||||
add(_freeplay_favorite);
|
||||
add(_freeplay_left);
|
||||
add(_freeplay_right);
|
||||
add(_cutscene_advance);
|
||||
add(_debug_menu);
|
||||
add(_debug_chart);
|
||||
|
@ -302,7 +324,6 @@ class Controls extends FlxActionSet
|
|||
add(_volume_up);
|
||||
add(_volume_down);
|
||||
add(_volume_mute);
|
||||
add(_fullscreen);
|
||||
|
||||
for (action in digitalActions) {
|
||||
if (Std.isOfType(action, FunkinAction)) {
|
||||
|
@ -398,7 +419,11 @@ class Controls extends FlxActionSet
|
|||
case BACK: _back;
|
||||
case PAUSE: _pause;
|
||||
case RESET: _reset;
|
||||
case SCREENSHOT: _screenshot;
|
||||
case WINDOW_SCREENSHOT: _window_screenshot;
|
||||
case WINDOW_FULLSCREEN: _window_fullscreen;
|
||||
case FREEPLAY_FAVORITE: _freeplay_favorite;
|
||||
case FREEPLAY_LEFT: _freeplay_left;
|
||||
case FREEPLAY_RIGHT: _freeplay_right;
|
||||
case CUTSCENE_ADVANCE: _cutscene_advance;
|
||||
case DEBUG_MENU: _debug_menu;
|
||||
case DEBUG_CHART: _debug_chart;
|
||||
|
@ -406,7 +431,6 @@ class Controls extends FlxActionSet
|
|||
case VOLUME_UP: _volume_up;
|
||||
case VOLUME_DOWN: _volume_down;
|
||||
case VOLUME_MUTE: _volume_mute;
|
||||
case FULLSCREEN: _fullscreen;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,8 +490,16 @@ class Controls extends FlxActionSet
|
|||
func(_pause, JUST_PRESSED);
|
||||
case RESET:
|
||||
func(_reset, JUST_PRESSED);
|
||||
case SCREENSHOT:
|
||||
func(_screenshot, JUST_PRESSED);
|
||||
case WINDOW_SCREENSHOT:
|
||||
func(_window_screenshot, JUST_PRESSED);
|
||||
case WINDOW_FULLSCREEN:
|
||||
func(_window_fullscreen, JUST_PRESSED);
|
||||
case FREEPLAY_FAVORITE:
|
||||
func(_freeplay_favorite, JUST_PRESSED);
|
||||
case FREEPLAY_LEFT:
|
||||
func(_freeplay_left, JUST_PRESSED);
|
||||
case FREEPLAY_RIGHT:
|
||||
func(_freeplay_right, JUST_PRESSED);
|
||||
case CUTSCENE_ADVANCE:
|
||||
func(_cutscene_advance, JUST_PRESSED);
|
||||
case DEBUG_MENU:
|
||||
|
@ -482,8 +514,6 @@ class Controls extends FlxActionSet
|
|||
func(_volume_down, JUST_PRESSED);
|
||||
case VOLUME_MUTE:
|
||||
func(_volume_mute, JUST_PRESSED);
|
||||
case FULLSCREEN:
|
||||
func(_fullscreen, JUST_PRESSED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -678,7 +708,11 @@ class Controls extends FlxActionSet
|
|||
bindKeys(Control.BACK, getDefaultKeybinds(scheme, Control.BACK));
|
||||
bindKeys(Control.PAUSE, getDefaultKeybinds(scheme, Control.PAUSE));
|
||||
bindKeys(Control.RESET, getDefaultKeybinds(scheme, Control.RESET));
|
||||
bindKeys(Control.SCREENSHOT, getDefaultKeybinds(scheme, Control.SCREENSHOT));
|
||||
bindKeys(Control.WINDOW_SCREENSHOT, getDefaultKeybinds(scheme, Control.WINDOW_SCREENSHOT));
|
||||
bindKeys(Control.WINDOW_FULLSCREEN, getDefaultKeybinds(scheme, Control.WINDOW_FULLSCREEN));
|
||||
bindKeys(Control.FREEPLAY_FAVORITE, getDefaultKeybinds(scheme, Control.FREEPLAY_FAVORITE));
|
||||
bindKeys(Control.FREEPLAY_LEFT, getDefaultKeybinds(scheme, Control.FREEPLAY_LEFT));
|
||||
bindKeys(Control.FREEPLAY_RIGHT, getDefaultKeybinds(scheme, Control.FREEPLAY_RIGHT));
|
||||
bindKeys(Control.CUTSCENE_ADVANCE, getDefaultKeybinds(scheme, Control.CUTSCENE_ADVANCE));
|
||||
bindKeys(Control.DEBUG_MENU, getDefaultKeybinds(scheme, Control.DEBUG_MENU));
|
||||
bindKeys(Control.DEBUG_CHART, getDefaultKeybinds(scheme, Control.DEBUG_CHART));
|
||||
|
@ -686,7 +720,6 @@ class Controls extends FlxActionSet
|
|||
bindKeys(Control.VOLUME_UP, getDefaultKeybinds(scheme, Control.VOLUME_UP));
|
||||
bindKeys(Control.VOLUME_DOWN, getDefaultKeybinds(scheme, Control.VOLUME_DOWN));
|
||||
bindKeys(Control.VOLUME_MUTE, getDefaultKeybinds(scheme, Control.VOLUME_MUTE));
|
||||
bindKeys(Control.FULLSCREEN, getDefaultKeybinds(scheme, Control.FULLSCREEN));
|
||||
|
||||
bindMobileLol();
|
||||
}
|
||||
|
@ -707,7 +740,11 @@ class Controls extends FlxActionSet
|
|||
case Control.BACK: return [X, BACKSPACE, ESCAPE];
|
||||
case Control.PAUSE: return [P, ENTER, ESCAPE];
|
||||
case Control.RESET: return [R];
|
||||
case Control.SCREENSHOT: return [F3]; // TODO: Change this back to PrintScreen
|
||||
case Control.WINDOW_FULLSCREEN: return [F11]; // We use F for other things LOL.
|
||||
case Control.WINDOW_SCREENSHOT: return [F3];
|
||||
case Control.FREEPLAY_FAVORITE: return [F]; // Favorite a song on the menu
|
||||
case Control.FREEPLAY_LEFT: return [Q]; // Switch tabs on the menu
|
||||
case Control.FREEPLAY_RIGHT: return [E]; // Switch tabs on the menu
|
||||
case Control.CUTSCENE_ADVANCE: return [Z, ENTER];
|
||||
case Control.DEBUG_MENU: return [GRAVEACCENT];
|
||||
case Control.DEBUG_CHART: return [];
|
||||
|
@ -715,8 +752,6 @@ class Controls extends FlxActionSet
|
|||
case Control.VOLUME_UP: return [PLUS, NUMPADPLUS];
|
||||
case Control.VOLUME_DOWN: return [MINUS, NUMPADMINUS];
|
||||
case Control.VOLUME_MUTE: return [ZERO, NUMPADZERO];
|
||||
case Control.FULLSCREEN: return [FlxKey.F11]; // We use F for other things LOL.
|
||||
|
||||
}
|
||||
case Duo(true):
|
||||
switch (control) {
|
||||
|
@ -732,7 +767,11 @@ class Controls extends FlxActionSet
|
|||
case Control.BACK: return [H, X];
|
||||
case Control.PAUSE: return [ONE];
|
||||
case Control.RESET: return [R];
|
||||
case Control.SCREENSHOT: return [PRINTSCREEN];
|
||||
case Control.WINDOW_SCREENSHOT: return [F3];
|
||||
case Control.WINDOW_FULLSCREEN: return [F11];
|
||||
case Control.FREEPLAY_FAVORITE: return [F]; // Favorite a song on the menu
|
||||
case Control.FREEPLAY_LEFT: return [Q]; // Switch tabs on the menu
|
||||
case Control.FREEPLAY_RIGHT: return [E]; // Switch tabs on the menu
|
||||
case Control.CUTSCENE_ADVANCE: return [G, Z];
|
||||
case Control.DEBUG_MENU: return [GRAVEACCENT];
|
||||
case Control.DEBUG_CHART: return [];
|
||||
|
@ -740,7 +779,6 @@ class Controls extends FlxActionSet
|
|||
case Control.VOLUME_UP: return [PLUS];
|
||||
case Control.VOLUME_DOWN: return [MINUS];
|
||||
case Control.VOLUME_MUTE: return [ZERO];
|
||||
case Control.FULLSCREEN: return [FlxKey.F];
|
||||
|
||||
}
|
||||
case Duo(false):
|
||||
|
@ -757,15 +795,18 @@ class Controls extends FlxActionSet
|
|||
case Control.BACK: return [ESCAPE];
|
||||
case Control.PAUSE: return [ONE];
|
||||
case Control.RESET: return [R];
|
||||
case Control.SCREENSHOT: return [PRINTSCREEN];
|
||||
case Control.WINDOW_SCREENSHOT: return [];
|
||||
case Control.WINDOW_FULLSCREEN: return [];
|
||||
case Control.FREEPLAY_FAVORITE: return [];
|
||||
case Control.FREEPLAY_LEFT: return [];
|
||||
case Control.FREEPLAY_RIGHT: return [];
|
||||
case Control.CUTSCENE_ADVANCE: return [ENTER];
|
||||
case Control.DEBUG_MENU: return [GRAVEACCENT];
|
||||
case Control.DEBUG_MENU: return [];
|
||||
case Control.DEBUG_CHART: return [];
|
||||
case Control.DEBUG_STAGE: return [];
|
||||
case Control.VOLUME_UP: return [NUMPADPLUS];
|
||||
case Control.VOLUME_DOWN: return [NUMPADMINUS];
|
||||
case Control.VOLUME_MUTE: return [NUMPADZERO];
|
||||
case Control.FULLSCREEN: return [];
|
||||
|
||||
}
|
||||
default:
|
||||
|
@ -856,34 +897,37 @@ class Controls extends FlxActionSet
|
|||
public function addDefaultGamepad(id):Void
|
||||
{
|
||||
addGamepadLiteral(id, [
|
||||
|
||||
Control.ACCEPT => getDefaultGamepadBinds(Control.ACCEPT),
|
||||
Control.BACK => getDefaultGamepadBinds(Control.BACK),
|
||||
Control.UI_UP => getDefaultGamepadBinds(Control.UI_UP),
|
||||
Control.UI_DOWN => getDefaultGamepadBinds(Control.UI_DOWN),
|
||||
Control.UI_LEFT => getDefaultGamepadBinds(Control.UI_LEFT),
|
||||
Control.UI_RIGHT => getDefaultGamepadBinds(Control.UI_RIGHT),
|
||||
// don't swap A/B or X/Y for switch on these. A is always the bottom face button
|
||||
Control.NOTE_UP => getDefaultGamepadBinds(Control.NOTE_UP),
|
||||
Control.NOTE_DOWN => getDefaultGamepadBinds(Control.NOTE_DOWN),
|
||||
Control.NOTE_LEFT => getDefaultGamepadBinds(Control.NOTE_LEFT),
|
||||
Control.NOTE_RIGHT => getDefaultGamepadBinds(Control.NOTE_RIGHT),
|
||||
Control.PAUSE => getDefaultGamepadBinds(Control.PAUSE),
|
||||
Control.RESET => getDefaultGamepadBinds(Control.RESET),
|
||||
// Control.SCREENSHOT => [],
|
||||
// Control.VOLUME_UP => [RIGHT_SHOULDER],
|
||||
// Control.VOLUME_DOWN => [LEFT_SHOULDER],
|
||||
// Control.VOLUME_MUTE => [RIGHT_TRIGGER],
|
||||
Control.WINDOW_FULLSCREEN => getDefaultGamepadBinds(Control.WINDOW_FULLSCREEN),
|
||||
Control.WINDOW_SCREENSHOT => getDefaultGamepadBinds(Control.WINDOW_SCREENSHOT),
|
||||
Control.CUTSCENE_ADVANCE => getDefaultGamepadBinds(Control.CUTSCENE_ADVANCE),
|
||||
// Control.DEBUG_MENU
|
||||
// Control.DEBUG_CHART
|
||||
Control.FREEPLAY_FAVORITE => getDefaultGamepadBinds(Control.FREEPLAY_FAVORITE),
|
||||
Control.FREEPLAY_LEFT => getDefaultGamepadBinds(Control.FREEPLAY_LEFT),
|
||||
Control.FREEPLAY_RIGHT => getDefaultGamepadBinds(Control.FREEPLAY_RIGHT),
|
||||
Control.VOLUME_UP => getDefaultGamepadBinds(Control.VOLUME_UP),
|
||||
Control.VOLUME_DOWN => getDefaultGamepadBinds(Control.VOLUME_DOWN),
|
||||
Control.VOLUME_MUTE => getDefaultGamepadBinds(Control.VOLUME_MUTE),
|
||||
Control.DEBUG_MENU => getDefaultGamepadBinds(Control.DEBUG_MENU),
|
||||
Control.DEBUG_CHART => getDefaultGamepadBinds(Control.DEBUG_CHART),
|
||||
Control.DEBUG_STAGE => getDefaultGamepadBinds(Control.DEBUG_STAGE),
|
||||
]);
|
||||
}
|
||||
|
||||
function getDefaultGamepadBinds(control:Control):Array<FlxGamepadInputID> {
|
||||
switch(control) {
|
||||
case Control.ACCEPT: return [#if switch B #else A #end];
|
||||
case Control.BACK: return [#if switch A #else B #end, FlxGamepadInputID.BACK];
|
||||
case Control.BACK: return [#if switch A #else B #end];
|
||||
case Control.UI_UP: return [DPAD_UP, LEFT_STICK_DIGITAL_UP];
|
||||
case Control.UI_DOWN: return [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN];
|
||||
case Control.UI_LEFT: return [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT];
|
||||
|
@ -893,15 +937,19 @@ class Controls extends FlxActionSet
|
|||
case Control.NOTE_LEFT: return [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT];
|
||||
case Control.NOTE_RIGHT: return [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT];
|
||||
case Control.PAUSE: return [START];
|
||||
case Control.RESET: return [RIGHT_SHOULDER];
|
||||
case Control.SCREENSHOT: return [];
|
||||
case Control.VOLUME_UP: return [];
|
||||
case Control.VOLUME_DOWN: return [];
|
||||
case Control.VOLUME_MUTE: return [];
|
||||
case Control.RESET: return [FlxGamepadInputID.BACK]; // Back (i.e. Select)
|
||||
case Control.WINDOW_FULLSCREEN: [];
|
||||
case Control.WINDOW_SCREENSHOT: [];
|
||||
case Control.CUTSCENE_ADVANCE: return [A];
|
||||
case Control.DEBUG_MENU: return [];
|
||||
case Control.DEBUG_CHART: return [];
|
||||
case Control.FULLSCREEN: return [];
|
||||
case Control.FREEPLAY_FAVORITE: [FlxGamepadInputID.BACK]; // Back (i.e. Select)
|
||||
case Control.FREEPLAY_LEFT: [LEFT_SHOULDER];
|
||||
case Control.FREEPLAY_RIGHT: [RIGHT_SHOULDER];
|
||||
case Control.VOLUME_UP: [];
|
||||
case Control.VOLUME_DOWN: [];
|
||||
case Control.VOLUME_MUTE: [];
|
||||
case Control.DEBUG_MENU: [];
|
||||
case Control.DEBUG_CHART: [];
|
||||
case Control.DEBUG_STAGE: [];
|
||||
default:
|
||||
// Fallthrough.
|
||||
}
|
||||
|
@ -1392,7 +1440,7 @@ class FlxActionInputDigitalAndroid extends FlxActionInputDigital
|
|||
|
||||
override public function check(Action:FlxAction):Bool
|
||||
{
|
||||
returnswitch(trigger)
|
||||
return switch(trigger)
|
||||
{
|
||||
#if android
|
||||
case PRESSED: FlxG.android.checkStatus(inputID, PRESSED) || FlxG.android.checkStatus(inputID, PRESSED);
|
||||
|
@ -1425,14 +1473,18 @@ enum Control
|
|||
UI_RIGHT;
|
||||
UI_DOWN;
|
||||
RESET;
|
||||
SCREENSHOT;
|
||||
ACCEPT;
|
||||
BACK;
|
||||
PAUSE;
|
||||
FULLSCREEN;
|
||||
// CUTSCENE
|
||||
CUTSCENE_ADVANCE;
|
||||
// SCREENSHOT
|
||||
// FREEPLAY
|
||||
FREEPLAY_FAVORITE;
|
||||
FREEPLAY_LEFT;
|
||||
FREEPLAY_RIGHT;
|
||||
// WINDOW
|
||||
WINDOW_SCREENSHOT;
|
||||
WINDOW_FULLSCREEN;
|
||||
// VOLUME
|
||||
VOLUME_UP;
|
||||
VOLUME_DOWN;
|
||||
|
@ -1475,11 +1527,15 @@ enum abstract Action(String) to String from String
|
|||
var BACK = "back";
|
||||
var PAUSE = "pause";
|
||||
var RESET = "reset";
|
||||
var FULLSCREEN = "fullscreen";
|
||||
// SCREENSHOT
|
||||
var SCREENSHOT = "screenshot";
|
||||
// WINDOW
|
||||
var WINDOW_FULLSCREEN = "window_fullscreen";
|
||||
var WINDOW_SCREENSHOT = "window_screenshot";
|
||||
// CUTSCENE
|
||||
var CUTSCENE_ADVANCE = "cutscene_advance";
|
||||
// FREEPLAY
|
||||
var FREEPLAY_FAVORITE = "freeplay_favorite";
|
||||
var FREEPLAY_LEFT = "freeplay_left";
|
||||
var FREEPLAY_RIGHT = "freeplay_right";
|
||||
// VOLUME
|
||||
var VOLUME_UP = "volume_up";
|
||||
var VOLUME_DOWN = "volume_down";
|
||||
|
|
|
@ -140,16 +140,36 @@ class HitNoteScriptEvent extends NoteScriptEvent
|
|||
*/
|
||||
public var score:Int;
|
||||
|
||||
public function new(note:NoteSprite, healthChange:Float, score:Int, judgement:String, comboCount:Int = 0):Void
|
||||
/**
|
||||
* If the hit causes a combo break.
|
||||
*/
|
||||
public var isComboBreak:Bool = false;
|
||||
|
||||
/**
|
||||
* The time difference when the player hit the note
|
||||
*/
|
||||
public var hitDiff:Float = 0;
|
||||
|
||||
/**
|
||||
* If the hit causes a notesplash
|
||||
*/
|
||||
public var doesNotesplash:Bool = false;
|
||||
|
||||
public function new(note:NoteSprite, healthChange:Float, score:Int, judgement:String, isComboBreak:Bool, comboCount:Int = 0, hitDiff:Float = 0,
|
||||
doesNotesplash:Bool = false):Void
|
||||
{
|
||||
super(NOTE_HIT, note, healthChange, comboCount, true);
|
||||
this.score = score;
|
||||
this.judgement = judgement;
|
||||
this.isComboBreak = isComboBreak;
|
||||
this.doesNotesplash = doesNotesplash;
|
||||
this.hitDiff = hitDiff;
|
||||
}
|
||||
|
||||
public override function toString():String
|
||||
{
|
||||
return 'HitNoteScriptEvent(note=' + note + ', comboCount=' + comboCount + ', judgement=' + judgement + ', score=' + score + ')';
|
||||
return 'HitNoteScriptEvent(note=' + note + ', comboCount=' + comboCount + ', judgement=' + judgement + ', score=' + score + ', isComboBreak='
|
||||
+ isComboBreak + ', hitDiff=' + hitDiff + ', doesNotesplash=' + doesNotesplash + ')';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||
var gameOverMusic:Null<FunkinSound> = null;
|
||||
|
||||
/**
|
||||
* Whether the player has confirmed and prepared to restart the level.
|
||||
* Whether the player has confirmed and prepared to restart the level or to go back to the freeplay menu.
|
||||
* This means the animation and transition have already started.
|
||||
*/
|
||||
var isEnding:Bool = false;
|
||||
|
@ -237,15 +237,16 @@ class GameOverSubState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
// KEYBOARD ONLY: Restart the level when pressing the assigned key.
|
||||
if (controls.ACCEPT && blueballed)
|
||||
if (controls.ACCEPT && blueballed && !mustNotExit)
|
||||
{
|
||||
blueballed = false;
|
||||
confirmDeath();
|
||||
}
|
||||
|
||||
// KEYBOARD ONLY: Return to the menu when pressing the assigned key.
|
||||
if (controls.BACK && !mustNotExit)
|
||||
if (controls.BACK && !mustNotExit && !isEnding)
|
||||
{
|
||||
isEnding = true;
|
||||
blueballed = false;
|
||||
PlayState.instance.deathCounter = 0;
|
||||
// PlayState.seenCutscene = false; // old thing...
|
||||
|
|
|
@ -449,13 +449,14 @@ class PauseSubState extends MusicBeatSubState
|
|||
*/
|
||||
function changeSelection(change:Int = 0):Void
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
|
||||
var prevEntry:Int = currentEntry;
|
||||
currentEntry += change;
|
||||
|
||||
if (currentEntry < 0) currentEntry = currentMenuEntries.length - 1;
|
||||
if (currentEntry >= currentMenuEntries.length) currentEntry = 0;
|
||||
|
||||
if (currentEntry != prevEntry) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
|
||||
for (entryIndex in 0...currentMenuEntries.length)
|
||||
{
|
||||
var isCurrent:Bool = entryIndex == currentEntry;
|
||||
|
|
|
@ -1218,6 +1218,9 @@ class PlayState extends MusicBeatSubState
|
|||
cameraTweensPausedBySubState.add(cameraZoomTween);
|
||||
}
|
||||
|
||||
// Pause camera follow
|
||||
FlxG.camera.followLerp = 0;
|
||||
|
||||
for (tween in scrollSpeedTweens)
|
||||
{
|
||||
if (tween != null && tween.active)
|
||||
|
@ -1262,6 +1265,9 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
cameraTweensPausedBySubState.clear();
|
||||
|
||||
// Resume camera follow
|
||||
FlxG.camera.followLerp = Constants.DEFAULT_CAMERA_FOLLOW_RATE;
|
||||
|
||||
if (currentConversation != null)
|
||||
{
|
||||
currentConversation.resumeMusic();
|
||||
|
@ -2122,7 +2128,8 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
// Call an event to allow canceling the note hit.
|
||||
// NOTE: This is what handles the character animations!
|
||||
var event:NoteScriptEvent = new HitNoteScriptEvent(note, 0.0, 0, 'perfect', 0);
|
||||
|
||||
var event:NoteScriptEvent = new HitNoteScriptEvent(note, 0.0, 0, 'perfect', false, 0);
|
||||
dispatchEvent(event);
|
||||
|
||||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||
|
@ -2218,7 +2225,7 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
// Call an event to allow canceling the note hit.
|
||||
// NOTE: This is what handles the character animations!
|
||||
var event:NoteScriptEvent = new HitNoteScriptEvent(note, 0.0, 0, 'perfect', 0);
|
||||
var event:NoteScriptEvent = new HitNoteScriptEvent(note, 0.0, 0, 'perfect', false, 0);
|
||||
dispatchEvent(event);
|
||||
|
||||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||
|
@ -2276,11 +2283,20 @@ class PlayState extends MusicBeatSubState
|
|||
if (holdNote == null || !holdNote.alive) continue;
|
||||
|
||||
// While the hold note is being hit, and there is length on the hold note...
|
||||
if (!isBotPlayMode && holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0)
|
||||
if (holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0)
|
||||
{
|
||||
// Grant the player health.
|
||||
health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed;
|
||||
songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed);
|
||||
if (!isBotPlayMode)
|
||||
{
|
||||
health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed;
|
||||
songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed);
|
||||
}
|
||||
|
||||
// Make sure the player keeps singing while the note is held by the bot.
|
||||
if (isBotPlayMode && currentStage != null && currentStage.getBoyfriend() != null && currentStage.getBoyfriend().isSinging())
|
||||
{
|
||||
currentStage.getBoyfriend().holdTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (holdNote.missedNote && !holdNote.handledMiss)
|
||||
|
@ -2427,27 +2443,41 @@ class PlayState extends MusicBeatSubState
|
|||
var daRating = Scoring.judgeNote(noteDiff, PBOT1);
|
||||
|
||||
var healthChange = 0.0;
|
||||
var isComboBreak = false;
|
||||
switch (daRating)
|
||||
{
|
||||
case 'sick':
|
||||
healthChange = Constants.HEALTH_SICK_BONUS;
|
||||
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
|
||||
case 'good':
|
||||
healthChange = Constants.HEALTH_GOOD_BONUS;
|
||||
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
|
||||
case 'bad':
|
||||
healthChange = Constants.HEALTH_BAD_BONUS;
|
||||
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
|
||||
case 'shit':
|
||||
isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
|
||||
healthChange = Constants.HEALTH_SHIT_BONUS;
|
||||
}
|
||||
|
||||
// Send the note hit event.
|
||||
var event:HitNoteScriptEvent = new HitNoteScriptEvent(note, healthChange, score, daRating, Highscore.tallies.combo + 1);
|
||||
var event:HitNoteScriptEvent = new HitNoteScriptEvent(note, healthChange, score, daRating, isComboBreak, Highscore.tallies.combo + 1, noteDiff,
|
||||
daRating == 'sick');
|
||||
dispatchEvent(event);
|
||||
|
||||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||
if (event.eventCanceled) return;
|
||||
|
||||
Highscore.tallies.totalNotesHit++;
|
||||
// Display the hit on the strums
|
||||
playerStrumline.hitNote(note, !isComboBreak);
|
||||
if (event.doesNotesplash) playerStrumline.playNoteSplash(note.noteData.getDirection());
|
||||
if (note.isHoldNote && note.holdNoteSprite != null) playerStrumline.playNoteHoldCover(note.holdNoteSprite);
|
||||
vocals.playerVolume = 1;
|
||||
|
||||
// Display the combo meter and add the calculation to the score.
|
||||
popUpScore(note, event.score, event.judgement, event.healthChange);
|
||||
applyScore(event.score, event.judgement, event.healthChange, event.isComboBreak);
|
||||
popUpScore(event.judgement);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2458,9 +2488,6 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
// If we are here, we already CALLED the onNoteMiss script hook!
|
||||
|
||||
health += healthChange;
|
||||
songScore -= 10;
|
||||
|
||||
if (!isPracticeMode)
|
||||
{
|
||||
// messy copy paste rn lol
|
||||
|
@ -2500,14 +2527,9 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
vocals.playerVolume = 0;
|
||||
|
||||
Highscore.tallies.missed++;
|
||||
if (Highscore.tallies.combo != 0) if (Highscore.tallies.combo >= 10) comboPopUps.displayCombo(0);
|
||||
|
||||
if (Highscore.tallies.combo != 0)
|
||||
{
|
||||
// Break the combo.
|
||||
if (Highscore.tallies.combo >= 10) comboPopUps.displayCombo(0);
|
||||
Highscore.tallies.combo = 0;
|
||||
}
|
||||
applyScore(-10, 'miss', healthChange, true);
|
||||
|
||||
if (playSound)
|
||||
{
|
||||
|
@ -2595,20 +2617,12 @@ class PlayState extends MusicBeatSubState
|
|||
// Redirect to the chart editor playing the current song.
|
||||
if (controls.DEBUG_CHART)
|
||||
{
|
||||
if (isChartingMode)
|
||||
{
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.pause(); // Don't reset song position!
|
||||
this.close(); // This only works because PlayState is a substate!
|
||||
}
|
||||
else
|
||||
{
|
||||
disableKeys = true;
|
||||
persistentUpdate = false;
|
||||
FlxG.switchState(() -> new ChartEditorState(
|
||||
{
|
||||
targetSongId: currentSong.id,
|
||||
}));
|
||||
}
|
||||
disableKeys = true;
|
||||
persistentUpdate = false;
|
||||
FlxG.switchState(() -> new ChartEditorState(
|
||||
{
|
||||
targetSongId: currentSong.id,
|
||||
}));
|
||||
}
|
||||
#end
|
||||
|
||||
|
@ -2639,46 +2653,24 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
/**
|
||||
* Handles health, score, and rating popups when a note is hit.
|
||||
* Handles applying health, score, and ratings.
|
||||
*/
|
||||
function popUpScore(daNote:NoteSprite, score:Int, daRating:String, healthChange:Float):Void
|
||||
function applyScore(score:Int, daRating:String, healthChange:Float, isComboBreak:Bool)
|
||||
{
|
||||
if (daRating == 'miss')
|
||||
{
|
||||
// If daRating is 'miss', that means we made a mistake and should not continue.
|
||||
FlxG.log.warn('popUpScore judged a note as a miss!');
|
||||
// TODO: Remove this.
|
||||
// comboPopUps.displayRating('miss');
|
||||
return;
|
||||
}
|
||||
|
||||
vocals.playerVolume = 1;
|
||||
|
||||
var isComboBreak = false;
|
||||
switch (daRating)
|
||||
{
|
||||
case 'sick':
|
||||
Highscore.tallies.sick += 1;
|
||||
Highscore.tallies.totalNotesHit++;
|
||||
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
|
||||
case 'good':
|
||||
Highscore.tallies.good += 1;
|
||||
Highscore.tallies.totalNotesHit++;
|
||||
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
|
||||
case 'bad':
|
||||
Highscore.tallies.bad += 1;
|
||||
Highscore.tallies.totalNotesHit++;
|
||||
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
|
||||
case 'shit':
|
||||
Highscore.tallies.shit += 1;
|
||||
Highscore.tallies.totalNotesHit++;
|
||||
isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
|
||||
default:
|
||||
FlxG.log.error('Wuh? Buh? Guh? Note hit judgement was $daRating!');
|
||||
case 'miss':
|
||||
Highscore.tallies.missed += 1;
|
||||
}
|
||||
|
||||
health += healthChange;
|
||||
|
||||
if (isComboBreak)
|
||||
{
|
||||
// Break the combo, but don't increment tallies.misses.
|
||||
|
@ -2690,15 +2682,23 @@ class PlayState extends MusicBeatSubState
|
|||
Highscore.tallies.combo++;
|
||||
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
|
||||
}
|
||||
|
||||
playerStrumline.hitNote(daNote, !isComboBreak);
|
||||
|
||||
if (daRating == 'sick')
|
||||
{
|
||||
playerStrumline.playNoteSplash(daNote.noteData.getDirection());
|
||||
}
|
||||
|
||||
songScore += score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles rating popups when a note is hit.
|
||||
*/
|
||||
function popUpScore(daRating:String, ?combo:Int):Void
|
||||
{
|
||||
if (daRating == 'miss')
|
||||
{
|
||||
// If daRating is 'miss', that means we made a mistake and should not continue.
|
||||
FlxG.log.warn('popUpScore judged a note as a miss!');
|
||||
// TODO: Remove this.
|
||||
// comboPopUps.displayRating('miss');
|
||||
return;
|
||||
}
|
||||
if (combo == null) combo = Highscore.tallies.combo;
|
||||
|
||||
if (!isPracticeMode)
|
||||
{
|
||||
|
@ -2738,12 +2738,7 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
comboPopUps.displayRating(daRating);
|
||||
if (Highscore.tallies.combo >= 10 || Highscore.tallies.combo == 0) comboPopUps.displayCombo(Highscore.tallies.combo);
|
||||
|
||||
if (daNote.isHoldNote && daNote.holdNoteSprite != null)
|
||||
{
|
||||
playerStrumline.playNoteHoldCover(daNote.holdNoteSprite);
|
||||
}
|
||||
if (combo >= 10 || combo == 0) comboPopUps.displayCombo(combo);
|
||||
|
||||
vocals.playerVolume = 1;
|
||||
}
|
||||
|
@ -2830,8 +2825,13 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
deathCounter = 0;
|
||||
|
||||
// TODO: This line of code makes me sad, but you can't really fix it without a breaking migration.
|
||||
// `easy`, `erect`, `normal-pico`, etc.
|
||||
var suffixedDifficulty = (currentVariation != Constants.DEFAULT_VARIATION
|
||||
&& currentVariation != 'erect') ? '$currentDifficulty-${currentVariation}' : currentDifficulty;
|
||||
|
||||
var isNewHighscore = false;
|
||||
var prevScoreData:Null<SaveScoreData> = Save.instance.getSongScore(currentSong.id, currentDifficulty);
|
||||
var prevScoreData:Null<SaveScoreData> = Save.instance.getSongScore(currentSong.id, suffixedDifficulty);
|
||||
|
||||
if (currentSong != null && currentSong.validScore)
|
||||
{
|
||||
|
@ -2856,13 +2856,21 @@ class PlayState extends MusicBeatSubState
|
|||
// adds current song data into the tallies for the level (story levels)
|
||||
Highscore.talliesLevel = Highscore.combineTallies(Highscore.tallies, Highscore.talliesLevel);
|
||||
|
||||
if (!isPracticeMode && !isBotPlayMode && Save.instance.isSongHighScore(currentSong.id, currentDifficulty, data))
|
||||
if (!isPracticeMode && !isBotPlayMode)
|
||||
{
|
||||
Save.instance.setSongScore(currentSong.id, currentDifficulty, data);
|
||||
#if newgrounds
|
||||
NGio.postScore(score, currentSong.id);
|
||||
#end
|
||||
isNewHighscore = true;
|
||||
isNewHighscore = Save.instance.isSongHighScore(currentSong.id, suffixedDifficulty, data);
|
||||
|
||||
// If no high score is present, save both score and rank.
|
||||
// If score or rank are better, save the highest one.
|
||||
// If neither are higher, nothing will change.
|
||||
Save.instance.applySongRank(currentSong.id, suffixedDifficulty, data);
|
||||
|
||||
if (isNewHighscore)
|
||||
{
|
||||
#if newgrounds
|
||||
NGio.postScore(score, currentSong.id);
|
||||
#end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3097,7 +3105,7 @@ class PlayState extends MusicBeatSubState
|
|||
FlxG.camera.targetOffset.x += 20;
|
||||
|
||||
// Replace zoom animation with a fade out for now.
|
||||
camGame.fade(FlxColor.BLACK, 0.6);
|
||||
FlxG.camera.fade(FlxColor.BLACK, 0.6);
|
||||
|
||||
FlxTween.tween(camHUD, {alpha: 0}, 0.6,
|
||||
{
|
||||
|
@ -3192,7 +3200,7 @@ class PlayState extends MusicBeatSubState
|
|||
cancelAllCameraTweens();
|
||||
}
|
||||
|
||||
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.04);
|
||||
FlxG.camera.follow(cameraFollowPoint, LOCKON, Constants.DEFAULT_CAMERA_FOLLOW_RATE);
|
||||
FlxG.camera.targetOffset.set();
|
||||
|
||||
if (resetZoom)
|
||||
|
|
|
@ -53,8 +53,9 @@ class HealthIcon extends FunkinSprite
|
|||
|
||||
/**
|
||||
* Apply the "bop" animation once every X steps.
|
||||
* Defaults to once per beat.
|
||||
*/
|
||||
public var bopEvery:Int = 4;
|
||||
public var bopEvery:Int = Constants.STEPS_PER_BEAT;
|
||||
|
||||
/**
|
||||
* The amount, in degrees, to rotate the icon by when boping.
|
||||
|
|
|
@ -356,7 +356,10 @@ class Scoring
|
|||
|
||||
// Perfect (Platinum) is a Sick Full Clear
|
||||
var isPerfectGold = scoreData.tallies.sick == scoreData.tallies.totalNotes;
|
||||
if (isPerfectGold) return ScoringRank.PERFECT_GOLD;
|
||||
if (isPerfectGold)
|
||||
{
|
||||
return ScoringRank.PERFECT_GOLD;
|
||||
}
|
||||
|
||||
// Else, use the standard grades
|
||||
|
||||
|
@ -397,62 +400,79 @@ enum abstract ScoringRank(String)
|
|||
var GOOD;
|
||||
var SHIT;
|
||||
|
||||
@:op(A > B) static function compare(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||
/**
|
||||
* Converts ScoringRank to an integer value for comparison.
|
||||
* Better ranks should be tied to a higher value.
|
||||
*/
|
||||
static function getValue(rank:Null<ScoringRank>):Int
|
||||
{
|
||||
if (rank == null) return -1;
|
||||
switch (rank)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return 5;
|
||||
case PERFECT:
|
||||
return 4;
|
||||
case EXCELLENT:
|
||||
return 3;
|
||||
case GREAT:
|
||||
return 2;
|
||||
case GOOD:
|
||||
return 1;
|
||||
case SHIT:
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Yes, we really need a different function for each comparison operator.
|
||||
@:op(A > B) static function compareGT(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||
{
|
||||
if (a != null && b == null) return true;
|
||||
if (a == null || b == null) return false;
|
||||
|
||||
var temp1:Int = 0;
|
||||
var temp2:Int = 0;
|
||||
var temp1:Int = getValue(a);
|
||||
var temp2:Int = getValue(b);
|
||||
|
||||
// temp 1
|
||||
switch (a)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
temp1 = 5;
|
||||
case PERFECT:
|
||||
temp1 = 4;
|
||||
case EXCELLENT:
|
||||
temp1 = 3;
|
||||
case GREAT:
|
||||
temp1 = 2;
|
||||
case GOOD:
|
||||
temp1 = 1;
|
||||
case SHIT:
|
||||
temp1 = 0;
|
||||
default:
|
||||
temp1 = -1;
|
||||
}
|
||||
|
||||
// temp 2
|
||||
switch (b)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
temp2 = 5;
|
||||
case PERFECT:
|
||||
temp2 = 4;
|
||||
case EXCELLENT:
|
||||
temp2 = 3;
|
||||
case GREAT:
|
||||
temp2 = 2;
|
||||
case GOOD:
|
||||
temp2 = 1;
|
||||
case SHIT:
|
||||
temp2 = 0;
|
||||
default:
|
||||
temp2 = -1;
|
||||
}
|
||||
|
||||
if (temp1 > temp2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return temp1 > temp2;
|
||||
}
|
||||
|
||||
@:op(A >= B) static function compareGTEQ(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||
{
|
||||
if (a != null && b == null) return true;
|
||||
if (a == null || b == null) return false;
|
||||
|
||||
var temp1:Int = getValue(a);
|
||||
var temp2:Int = getValue(b);
|
||||
|
||||
return temp1 >= temp2;
|
||||
}
|
||||
|
||||
@:op(A < B) static function compareLT(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||
{
|
||||
if (a != null && b == null) return true;
|
||||
if (a == null || b == null) return false;
|
||||
|
||||
var temp1:Int = getValue(a);
|
||||
var temp2:Int = getValue(b);
|
||||
|
||||
return temp1 < temp2;
|
||||
}
|
||||
|
||||
@:op(A <= B) static function compareLTEQ(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||
{
|
||||
if (a != null && b == null) return true;
|
||||
if (a == null || b == null) return false;
|
||||
|
||||
var temp1:Int = getValue(a);
|
||||
var temp2:Int = getValue(b);
|
||||
|
||||
return temp1 <= temp2;
|
||||
}
|
||||
|
||||
// @:op(A == B) isn't necessary!
|
||||
|
||||
/**
|
||||
* Delay in seconds
|
||||
*/
|
||||
|
@ -462,15 +482,15 @@ enum abstract ScoringRank(String)
|
|||
{
|
||||
case PERFECT_GOLD | PERFECT:
|
||||
// return 2.5;
|
||||
return 95/24;
|
||||
return 95 / 24;
|
||||
case EXCELLENT:
|
||||
return 0;
|
||||
case GREAT:
|
||||
return 5/24;
|
||||
return 5 / 24;
|
||||
case GOOD:
|
||||
return 3/24;
|
||||
return 3 / 24;
|
||||
case SHIT:
|
||||
return 2/24;
|
||||
return 2 / 24;
|
||||
default:
|
||||
return 3.5;
|
||||
}
|
||||
|
@ -482,15 +502,15 @@ enum abstract ScoringRank(String)
|
|||
{
|
||||
case PERFECT_GOLD | PERFECT:
|
||||
// return 2.5;
|
||||
return 95/24;
|
||||
return 95 / 24;
|
||||
case EXCELLENT:
|
||||
return 97/24;
|
||||
return 97 / 24;
|
||||
case GREAT:
|
||||
return 95/24;
|
||||
return 95 / 24;
|
||||
case GOOD:
|
||||
return 95/24;
|
||||
return 95 / 24;
|
||||
case SHIT:
|
||||
return 95/24;
|
||||
return 95 / 24;
|
||||
default:
|
||||
return 3.5;
|
||||
}
|
||||
|
@ -502,15 +522,15 @@ enum abstract ScoringRank(String)
|
|||
{
|
||||
case PERFECT_GOLD | PERFECT:
|
||||
// return 2.5;
|
||||
return 129/24;
|
||||
return 129 / 24;
|
||||
case EXCELLENT:
|
||||
return 122/24;
|
||||
return 122 / 24;
|
||||
case GREAT:
|
||||
return 109/24;
|
||||
return 109 / 24;
|
||||
case GOOD:
|
||||
return 107/24;
|
||||
return 107 / 24;
|
||||
case SHIT:
|
||||
return 186/24;
|
||||
return 186 / 24;
|
||||
default:
|
||||
return 3.5;
|
||||
}
|
||||
|
@ -522,15 +542,15 @@ enum abstract ScoringRank(String)
|
|||
{
|
||||
case PERFECT_GOLD | PERFECT:
|
||||
// return 2.5;
|
||||
return 140/24;
|
||||
return 140 / 24;
|
||||
case EXCELLENT:
|
||||
return 140/24;
|
||||
return 140 / 24;
|
||||
case GREAT:
|
||||
return 129/24;
|
||||
return 129 / 24;
|
||||
case GOOD:
|
||||
return 127/24;
|
||||
return 127 / 24;
|
||||
case SHIT:
|
||||
return 207/24;
|
||||
return 207 / 24;
|
||||
default:
|
||||
return 3.5;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package funkin.save;
|
||||
|
||||
import flixel.util.FlxSave;
|
||||
import funkin.util.FileUtil;
|
||||
import funkin.input.Controls.Device;
|
||||
import funkin.play.scoring.Scoring;
|
||||
import funkin.play.scoring.Scoring.ScoringRank;
|
||||
|
@ -58,7 +59,7 @@ class Save
|
|||
this.data = data;
|
||||
|
||||
// Make sure the verison number is up to date before we flush.
|
||||
this.data.version = Save.SAVE_DATA_VERSION;
|
||||
updateVersionToLatest();
|
||||
}
|
||||
|
||||
public static function getDefault():RawSaveData
|
||||
|
@ -503,7 +504,7 @@ class Save
|
|||
}
|
||||
|
||||
/**
|
||||
* Apply the score the user achieved for a given song on a given difficulty.
|
||||
* Directly set the score the user achieved for a given song on a given difficulty.
|
||||
*/
|
||||
public function setSongScore(songId:String, difficultyId:String, score:SaveScoreData):Void
|
||||
{
|
||||
|
@ -518,6 +519,44 @@ class Save
|
|||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only replace the ranking data for the song, because the old score is still better.
|
||||
*/
|
||||
public function applySongRank(songId:String, difficultyId:String, newScoreData:SaveScoreData):Void
|
||||
{
|
||||
var newRank = Scoring.calculateRank(newScoreData);
|
||||
if (newScoreData == null || newRank == null) return;
|
||||
|
||||
var song = data.scores.songs.get(songId);
|
||||
if (song == null)
|
||||
{
|
||||
song = [];
|
||||
data.scores.songs.set(songId, song);
|
||||
}
|
||||
|
||||
var previousScoreData = song.get(difficultyId);
|
||||
|
||||
var previousRank = Scoring.calculateRank(previousScoreData);
|
||||
|
||||
if (previousScoreData == null || previousRank == null)
|
||||
{
|
||||
// Directly set the highscore.
|
||||
setSongScore(songId, difficultyId, newScoreData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the high score and the high rank separately.
|
||||
var newScore:SaveScoreData =
|
||||
{
|
||||
score: (previousScoreData.score > newScoreData.score) ? previousScoreData.score : newScoreData.score,
|
||||
tallies: (previousRank > newRank) ? previousScoreData.tallies : newScoreData.tallies
|
||||
};
|
||||
|
||||
song.set(difficultyId, newScore);
|
||||
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the provided score data better than the current high score for the given song?
|
||||
* @param songId The song ID to check.
|
||||
|
@ -543,6 +582,39 @@ class Save
|
|||
return score.score > currentScore.score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the provided score data better than the current rank for the given song?
|
||||
* @param songId The song ID to check.
|
||||
* @param difficultyId The difficulty to check.
|
||||
* @param score The score to check the rank for.
|
||||
* @return Whether the score's rank is better than the current rank.
|
||||
*/
|
||||
public function isSongHighRank(songId:String, difficultyId:String = 'normal', score:SaveScoreData):Bool
|
||||
{
|
||||
var newScoreRank = Scoring.calculateRank(score);
|
||||
if (newScoreRank == null)
|
||||
{
|
||||
// The provided score is invalid.
|
||||
return false;
|
||||
}
|
||||
|
||||
var song = data.scores.songs.get(songId);
|
||||
if (song == null)
|
||||
{
|
||||
song = [];
|
||||
data.scores.songs.set(songId, song);
|
||||
}
|
||||
var currentScore = song.get(difficultyId);
|
||||
var currentScoreRank = Scoring.calculateRank(currentScore);
|
||||
if (currentScoreRank == null)
|
||||
{
|
||||
// There is no primary highscore for this song.
|
||||
return true;
|
||||
}
|
||||
|
||||
return newScoreRank > currentScoreRank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the provided song been beaten on one of the listed difficulties?
|
||||
* @param songId The song ID to check.
|
||||
|
@ -832,6 +904,29 @@ class Save
|
|||
return cast legacySave.data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize this Save into a JSON string.
|
||||
* @param pretty Whether the JSON should be big ol string (false),
|
||||
* or formatted with tabs (true)
|
||||
* @return The JSON string.
|
||||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
var ignoreNullOptionals = true;
|
||||
var writer = new json2object.JsonWriter<RawSaveData>(ignoreNullOptionals);
|
||||
return writer.write(data, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
public function updateVersionToLatest():Void
|
||||
{
|
||||
this.data.version = Save.SAVE_DATA_VERSION;
|
||||
}
|
||||
|
||||
public function debug_dumpSave():Void
|
||||
{
|
||||
FileUtil.saveFile(haxe.io.Bytes.ofString(this.serialize()), [FileUtil.FILE_FILTER_JSON], null, null, './save.json', 'Write save data as JSON...');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -904,6 +999,9 @@ typedef SaveHighScoresData =
|
|||
typedef SaveDataMods =
|
||||
{
|
||||
var enabledMods:Array<String>;
|
||||
|
||||
// TODO: Make this not trip up the serializer when debugging.
|
||||
@:jignored
|
||||
var modOptions:Map<String, Dynamic>;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ class MenuTypedList<T:MenuListItem> extends FlxTypedGroup<T>
|
|||
|
||||
if (newIndex != selectedIndex)
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'));
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
selectItem(newIndex);
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@ class MenuTypedList<T:MenuListItem> extends FlxTypedGroup<T>
|
|||
*/
|
||||
function navGrid(latSize:Int, latPrev:Bool, latNext:Bool, latAllowWrap:Bool, prev:Bool, next:Bool, allowWrap:Bool):Int
|
||||
{
|
||||
// The grid lenth along the variable-length axis
|
||||
// The grid length along the variable-length axis
|
||||
var size = Math.ceil(length / latSize);
|
||||
// The selected position along the variable-length axis
|
||||
var index = Math.floor(selectedIndex / latSize);
|
||||
|
|
|
@ -904,7 +904,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
|
||||
function set_notePreviewDirty(value:Bool):Bool
|
||||
{
|
||||
trace('Note preview dirtied!');
|
||||
// trace('Note preview dirtied!');
|
||||
return notePreviewDirty = value;
|
||||
}
|
||||
|
||||
|
@ -6304,7 +6304,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
var tempNote:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault());
|
||||
tempNote.noteData = noteData;
|
||||
tempNote.scrollFactor.set(0, 0);
|
||||
var event:NoteScriptEvent = new HitNoteScriptEvent(tempNote, 0.0, 0, 'perfect', 0);
|
||||
var event:NoteScriptEvent = new HitNoteScriptEvent(tempNote, 0.0, 0, 'perfect', false, 0);
|
||||
dispatchEvent(event);
|
||||
|
||||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||
|
|
|
@ -35,7 +35,15 @@ class SetItemSelectionCommand implements ChartEditorCommand
|
|||
{
|
||||
var eventSelected = this.events[0];
|
||||
|
||||
state.eventKindToPlace = eventSelected.eventKind;
|
||||
if (state.eventKindToPlace == eventSelected.eventKind)
|
||||
{
|
||||
trace('Target event kind matches selection: ${eventSelected.eventKind}');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Switching target event kind to match selection: ${state.eventKindToPlace} != ${eventSelected.eventKind}');
|
||||
state.eventKindToPlace = eventSelected.eventKind;
|
||||
}
|
||||
|
||||
// This code is here to parse event data that's not built as a struct for some reason.
|
||||
// TODO: Clean this up or get rid of it.
|
||||
|
|
|
@ -201,7 +201,8 @@ class ChartEditorThemeHandler
|
|||
// Selection borders horizontally in the middle.
|
||||
for (i in 1...(Conductor.instance.stepsPerMeasure))
|
||||
{
|
||||
if ((i % Conductor.instance.beatsPerMeasure) == 0)
|
||||
// There may be a different number of beats per measure, but there's always 4 steps per beat.
|
||||
if ((i % Constants.STEPS_PER_BEAT) == 0)
|
||||
{
|
||||
state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (GRID_BEAT_DIVIDER_WIDTH / 2), state.gridBitmap.width,
|
||||
GRID_BEAT_DIVIDER_WIDTH),
|
||||
|
|
|
@ -58,17 +58,8 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
|
||||
function initialize():Void
|
||||
{
|
||||
toolboxEventsEventKind.dataSource = new ArrayDataSource();
|
||||
|
||||
var songEvents:Array<SongEvent> = SongEventRegistry.listEvents();
|
||||
|
||||
for (event in songEvents)
|
||||
{
|
||||
toolboxEventsEventKind.dataSource.add({text: event.getTitle(), value: event.id});
|
||||
}
|
||||
|
||||
toolboxEventsEventKind.onChange = function(event:UIEvent) {
|
||||
var eventType:String = event.data.value;
|
||||
var eventType:String = event.data.id;
|
||||
|
||||
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType');
|
||||
|
||||
|
@ -83,7 +74,7 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
return;
|
||||
}
|
||||
|
||||
buildEventDataFormFromSchema(toolboxEventsDataGrid, schema);
|
||||
buildEventDataFormFromSchema(toolboxEventsDataGrid, schema, chartEditorState.eventKindToPlace);
|
||||
|
||||
if (!_initializing && chartEditorState.currentEventSelection.length > 0)
|
||||
{
|
||||
|
@ -98,14 +89,40 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
chartEditorState.notePreviewDirty = true;
|
||||
}
|
||||
}
|
||||
toolboxEventsEventKind.value = chartEditorState.eventKindToPlace;
|
||||
var startingEventValue = ChartEditorDropdowns.populateDropdownWithSongEvents(toolboxEventsEventKind, chartEditorState.eventKindToPlace);
|
||||
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Starting event kind: ${startingEventValue}');
|
||||
toolboxEventsEventKind.value = startingEventValue;
|
||||
}
|
||||
|
||||
public override function refresh():Void
|
||||
{
|
||||
super.refresh();
|
||||
|
||||
toolboxEventsEventKind.value = chartEditorState.eventKindToPlace;
|
||||
var newDropdownElement = ChartEditorDropdowns.findDropdownElement(chartEditorState.eventKindToPlace, toolboxEventsEventKind);
|
||||
|
||||
if (newDropdownElement == null)
|
||||
{
|
||||
throw 'ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event kind not in dropdown: ${chartEditorState.eventKindToPlace}';
|
||||
}
|
||||
else if (toolboxEventsEventKind.value != newDropdownElement || lastEventKind != toolboxEventsEventKind.value.id)
|
||||
{
|
||||
toolboxEventsEventKind.value = newDropdownElement;
|
||||
|
||||
var schema:SongEventSchema = SongEventRegistry.getEventSchema(chartEditorState.eventKindToPlace);
|
||||
if (schema == null)
|
||||
{
|
||||
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Unknown event kind: ${chartEditorState.eventKindToPlace}');
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event kind changed: ${toolboxEventsEventKind.value.id} != ${newDropdownElement.id} != ${lastEventKind}, rebuilding form');
|
||||
buildEventDataFormFromSchema(toolboxEventsDataGrid, schema, chartEditorState.eventKindToPlace);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event kind not changed: ${toolboxEventsEventKind.value} == ${newDropdownElement} == ${lastEventKind}');
|
||||
}
|
||||
|
||||
for (pair in chartEditorState.eventDataToPlace.keyValueIterator())
|
||||
{
|
||||
|
@ -116,7 +133,7 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
|
||||
if (field == null)
|
||||
{
|
||||
throw 'ChartEditorToolboxHandler.refresh() - Field "${fieldId}" does not exist in the event data form.';
|
||||
throw 'ChartEditorToolboxHandler.refresh() - Field "${fieldId}" does not exist in the event data form for kind ${lastEventKind}.';
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -141,9 +158,15 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
}
|
||||
}
|
||||
|
||||
function buildEventDataFormFromSchema(target:Box, schema:SongEventSchema):Void
|
||||
var lastEventKind:String = 'unknown';
|
||||
|
||||
function buildEventDataFormFromSchema(target:Box, schema:SongEventSchema, eventKind:String):Void
|
||||
{
|
||||
trace(schema);
|
||||
trace('Building event data form from schema for event kind: ${eventKind}');
|
||||
// trace(schema);
|
||||
|
||||
lastEventKind = eventKind ?? 'unknown';
|
||||
|
||||
// Clear the frame.
|
||||
target.removeAllComponents();
|
||||
|
||||
|
@ -188,6 +211,9 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
var dropDown:DropDown = new DropDown();
|
||||
dropDown.id = field.name;
|
||||
dropDown.width = 200.0;
|
||||
dropDown.dropdownSize = 10;
|
||||
dropDown.dropdownWidth = 300;
|
||||
dropDown.searchable = true;
|
||||
dropDown.dataSource = new ArrayDataSource();
|
||||
|
||||
if (field.keys == null) throw 'Field "${field.name}" is of Enum type but has no keys.';
|
||||
|
@ -197,12 +223,15 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
for (optionName in field.keys.keys())
|
||||
{
|
||||
var optionValue:Null<Dynamic> = field.keys.get(optionName);
|
||||
trace('$optionName : $optionValue');
|
||||
// trace('$optionName : $optionValue');
|
||||
dropDown.dataSource.add({value: optionValue, text: optionName});
|
||||
}
|
||||
|
||||
dropDown.value = field.defaultValue;
|
||||
|
||||
// TODO: Add an option to customize sort.
|
||||
dropDown.dataSource.sort('text', ASCENDING);
|
||||
|
||||
input = dropDown;
|
||||
case STRING:
|
||||
input = new TextField();
|
||||
|
|
|
@ -3,11 +3,13 @@ package funkin.ui.debug.charting.util;
|
|||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.play.notes.notestyle.NoteStyle;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.play.event.SongEvent;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.play.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;
|
||||
|
||||
/**
|
||||
|
@ -81,6 +83,42 @@ class ChartEditorDropdowns
|
|||
return returnValue;
|
||||
}
|
||||
|
||||
public static function populateDropdownWithSongEvents(dropDown:DropDown, startingEventId:String):DropDownEntry
|
||||
{
|
||||
dropDown.dataSource.clear();
|
||||
|
||||
var returnValue:DropDownEntry = {id: "FocusCamera", text: "Focus Camera"};
|
||||
|
||||
var songEvents:Array<SongEvent> = SongEventRegistry.listEvents();
|
||||
|
||||
for (event in songEvents)
|
||||
{
|
||||
var value = {id: event.id, text: event.getTitle()};
|
||||
if (startingEventId == event.id) returnValue = value;
|
||||
dropDown.dataSource.add(value);
|
||||
}
|
||||
|
||||
dropDown.dataSource.sort('text', ASCENDING);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the ID of a dropdown element, find the corresponding entry in the dropdown's dataSource.
|
||||
*/
|
||||
public static function findDropdownElement(id:String, dropDown:DropDown):Null<DropDownEntry>
|
||||
{
|
||||
// Attempt to find the entry.
|
||||
for (entryIndex in 0...dropDown.dataSource.size)
|
||||
{
|
||||
var entry = dropDown.dataSource.get(entryIndex);
|
||||
if (entry.id == id) return entry;
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate a dropdown with a list of note styles.
|
||||
*/
|
||||
|
|
|
@ -266,7 +266,7 @@ class DJBoyfriend extends FlxAtlasSprite
|
|||
|
||||
// Fade out music to 40% volume over 1 second.
|
||||
// This helps make the TV a bit more audible.
|
||||
FlxG.sound.music.fadeOut(1.0, 0.4);
|
||||
FlxG.sound.music.fadeOut(1.0, 0.1);
|
||||
|
||||
// Play the cartoon at a random time between the start and 5 seconds from the end.
|
||||
cartoonSnd.time = FlxG.random.float(0, Math.max(cartoonSnd.length - (5 * Constants.MS_PER_SEC), 0.0));
|
||||
|
|
|
@ -230,6 +230,12 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
FlxTransitionableState.skipNextTransIn = true;
|
||||
|
||||
// dedicated camera for the state so we don't need to fuk around with camera scrolls from the mainmenu / elsewhere
|
||||
funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height);
|
||||
funnyCam.bgColor = FlxColor.TRANSPARENT;
|
||||
FlxG.cameras.add(funnyCam, false);
|
||||
this.cameras = [funnyCam];
|
||||
|
||||
if (stickerSubState != null)
|
||||
{
|
||||
this.persistentUpdate = true;
|
||||
|
@ -285,7 +291,10 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
// Only display songs which actually have available difficulties for the current character.
|
||||
var displayedVariations = song.getVariationsByCharId(currentCharacter);
|
||||
trace(songId);
|
||||
trace(displayedVariations);
|
||||
var availableDifficultiesForSong:Array<String> = song.listDifficulties(displayedVariations, false);
|
||||
trace(availableDifficultiesForSong);
|
||||
if (availableDifficultiesForSong.length == 0) continue;
|
||||
|
||||
songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations));
|
||||
|
@ -583,6 +592,8 @@ class FreeplayState extends MusicBeatSubState
|
|||
generateSongList({filterType: FAVORITE}, true);
|
||||
case 'ALL':
|
||||
generateSongList(null, true);
|
||||
case '#':
|
||||
generateSongList({filterType: REGEXP, filterData: '0-9'}, true);
|
||||
default:
|
||||
generateSongList({filterType: REGEXP, filterData: str}, true);
|
||||
}
|
||||
|
@ -591,6 +602,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
// that is, only if there's more than one song in the group!
|
||||
if (grpCapsules.members.length > 0)
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
curSelected = 1;
|
||||
changeSelection();
|
||||
}
|
||||
|
@ -652,6 +664,9 @@ class FreeplayState extends MusicBeatSubState
|
|||
alsoOrangeLOL.visible = true;
|
||||
grpTxtScrolls.visible = true;
|
||||
|
||||
// render optimisation
|
||||
if (_parentState != null) _parentState.persistentDraw = false;
|
||||
|
||||
cardGlow.visible = true;
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut});
|
||||
|
||||
|
@ -956,16 +971,18 @@ class FreeplayState extends MusicBeatSubState
|
|||
grpCapsules.members[curSelected].ranking.scale.set(20, 20);
|
||||
grpCapsules.members[curSelected].blurredRanking.scale.set(20, 20);
|
||||
|
||||
grpCapsules.members[curSelected].ranking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
// grpCapsules.members[curSelected].ranking.animation.curAnim.name, true);
|
||||
if (fromResults?.newRank != null) {
|
||||
grpCapsules.members[curSelected].ranking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
}
|
||||
|
||||
FlxTween.tween(grpCapsules.members[curSelected].ranking, {"scale.x": 1, "scale.y": 1}, 0.1);
|
||||
|
||||
grpCapsules.members[curSelected].blurredRanking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
if (fromResults?.newRank != null) {
|
||||
grpCapsules.members[curSelected].blurredRanking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
}
|
||||
FlxTween.tween(grpCapsules.members[curSelected].blurredRanking, {"scale.x": 1, "scale.y": 1}, 0.1);
|
||||
|
||||
new FlxTimer().start(0.1, _ -> {
|
||||
// trace(grpCapsules.members[curSelected].ranking.rank);
|
||||
if (fromResults?.oldRank != null)
|
||||
{
|
||||
grpCapsules.members[curSelected].fakeRanking.visible = false;
|
||||
|
@ -994,7 +1011,6 @@ class FreeplayState extends MusicBeatSubState
|
|||
FunkinSound.playOnce(Paths.sound('ranks/rankinnormal'));
|
||||
}
|
||||
rankCamera.zoom = 1.3;
|
||||
// FlxTween.tween(rankCamera, {"zoom": 1.4}, 0.3, {ease: FlxEase.elasticOut});
|
||||
|
||||
FlxTween.tween(rankCamera, {"zoom": 1.5}, 0.3, {ease: FlxEase.backInOut});
|
||||
|
||||
|
@ -1012,13 +1028,11 @@ class FreeplayState extends MusicBeatSubState
|
|||
new FlxTimer().start(0.4, _ -> {
|
||||
FlxTween.tween(funnyCam, {"zoom": 1}, 0.8, {ease: FlxEase.sineIn});
|
||||
FlxTween.tween(rankCamera, {"zoom": 1.2}, 0.8, {ease: FlxEase.backIn});
|
||||
// IntervalShake.shake(grpCapsules.members[curSelected], 0.8 + 0.5, 1 / 24, 0, 2, FlxEase.quadIn);
|
||||
FlxTween.tween(grpCapsules.members[curSelected], {x: originalPos.x - 7, y: originalPos.y - 80}, 0.8 + 0.5, {ease: FlxEase.quartIn});
|
||||
});
|
||||
|
||||
new FlxTimer().start(0.6, _ -> {
|
||||
rankAnimSlam(fromResults);
|
||||
// IntervalShake.shake(grpCapsules.members[curSelected].capsule, 0.3, 1 / 30, 0, 0.3, FlxEase.quartIn);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1183,51 +1197,9 @@ class FreeplayState extends MusicBeatSubState
|
|||
// {
|
||||
// rankAnimSlam(fromResultsParams);
|
||||
// }
|
||||
|
||||
if (FlxG.keys.justPressed.G)
|
||||
{
|
||||
sparks.y -= 2;
|
||||
trace(sparks.x, sparks.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.V)
|
||||
{
|
||||
sparks.x -= 2;
|
||||
trace(sparks.x, sparks.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.N)
|
||||
{
|
||||
sparks.x += 2;
|
||||
trace(sparks.x, sparks.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.B)
|
||||
{
|
||||
sparks.y += 2;
|
||||
trace(sparks.x, sparks.y);
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.I)
|
||||
{
|
||||
sparksADD.y -= 2;
|
||||
trace(sparksADD.x, sparksADD.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.J)
|
||||
{
|
||||
sparksADD.x -= 2;
|
||||
trace(sparksADD.x, sparksADD.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.L)
|
||||
{
|
||||
sparksADD.x += 2;
|
||||
trace(sparksADD.x, sparksADD.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.K)
|
||||
{
|
||||
sparksADD.y += 2;
|
||||
trace(sparksADD.x, sparksADD.y);
|
||||
}
|
||||
#end
|
||||
|
||||
if (FlxG.keys.justPressed.F && !busy)
|
||||
if (controls.FREEPLAY_FAVORITE && !busy)
|
||||
{
|
||||
var targetSong = grpCapsules.members[curSelected]?.songData;
|
||||
if (targetSong != null)
|
||||
|
@ -1571,6 +1543,8 @@ class FreeplayState extends MusicBeatSubState
|
|||
{
|
||||
clearDaCache(daSong.songName);
|
||||
}
|
||||
// remove and destroy freeplay camera
|
||||
FlxG.cameras.remove(funnyCam);
|
||||
}
|
||||
|
||||
function changeDiff(change:Int = 0, force:Bool = false):Void
|
||||
|
@ -1591,7 +1565,19 @@ class FreeplayState extends MusicBeatSubState
|
|||
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].songData;
|
||||
if (daSong != null)
|
||||
{
|
||||
var songScore:SaveScoreData = Save.instance.getSongScore(grpCapsules.members[curSelected].songData.songId, currentDifficulty);
|
||||
// TODO: Make this actually be the variation you're focused on. We don't need to fetch the song metadata just to calculate it.
|
||||
var targetSong:Song = SongRegistry.instance.fetchEntry(grpCapsules.members[curSelected].songData.songId);
|
||||
if (targetSong == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find song with id (${grpCapsules.members[curSelected].songData.songId})');
|
||||
return;
|
||||
}
|
||||
var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty);
|
||||
|
||||
// TODO: This line of code makes me sad, but you can't really fix it without a breaking migration.
|
||||
var suffixedDifficulty = (targetVariation != Constants.DEFAULT_VARIATION
|
||||
&& targetVariation != 'erect') ? '$currentDifficulty-${targetVariation}' : currentDifficulty;
|
||||
var songScore:SaveScoreData = Save.instance.getSongScore(grpCapsules.members[curSelected].songData.songId, suffixedDifficulty);
|
||||
intendedScore = songScore?.score ?? 0;
|
||||
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
|
||||
rememberedDifficulty = currentDifficulty;
|
||||
|
@ -1827,12 +1813,12 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
function changeSelection(change:Int = 0):Void
|
||||
{
|
||||
if (!prepForNewRank) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
|
||||
var prevSelected:Int = curSelected;
|
||||
|
||||
curSelected += change;
|
||||
|
||||
if (!prepForNewRank && curSelected != prevSelected) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
|
||||
if (curSelected < 0) curSelected = grpCapsules.countLiving() - 1;
|
||||
if (curSelected >= grpCapsules.countLiving()) curSelected = 0;
|
||||
|
||||
|
@ -2086,7 +2072,7 @@ class FreeplaySongData
|
|||
this.songDifficulties = song.listDifficulties(null, variations, false, false);
|
||||
if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||
|
||||
var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, variations);
|
||||
var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, null, variations);
|
||||
if (songDifficulty == null) return;
|
||||
this.songStartingBpm = songDifficulty.getStartingBPM();
|
||||
this.songName = songDifficulty.songName;
|
||||
|
|
|
@ -8,6 +8,7 @@ import flixel.tweens.FlxTween;
|
|||
import flixel.tweens.FlxEase;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.input.Controls;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
|
||||
class LetterSort extends FlxTypedSpriteGroup<FlxSprite>
|
||||
|
@ -69,14 +70,19 @@ class LetterSort extends FlxTypedSpriteGroup<FlxSprite>
|
|||
changeSelection(0);
|
||||
}
|
||||
|
||||
var controls(get, never):Controls;
|
||||
|
||||
inline function get_controls():Controls
|
||||
return PlayerSettings.player1.controls;
|
||||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
if (inputEnabled)
|
||||
{
|
||||
if (FlxG.keys.justPressed.E) changeSelection(1);
|
||||
if (FlxG.keys.justPressed.Q) changeSelection(-1);
|
||||
if (controls.FREEPLAY_LEFT) changeSelection(-1);
|
||||
if (controls.FREEPLAY_RIGHT) changeSelection(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -219,7 +219,7 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
favIconBlurred.visible = false;
|
||||
add(favIconBlurred);
|
||||
|
||||
favIcon = new FlxSprite(380, 40);
|
||||
favIcon = new FlxSprite(favIconBlurred.x, favIconBlurred.y);
|
||||
favIcon.frames = Paths.getSparrowAtlas('freeplay/favHeart');
|
||||
favIcon.animation.addByPrefix('fav', 'favorite heart', 24, false);
|
||||
favIcon.animation.play('fav');
|
||||
|
@ -294,21 +294,34 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
}
|
||||
}
|
||||
|
||||
// 255, 27 normal
|
||||
// 220, 27 favourited
|
||||
/**
|
||||
* Checks whether the song is favorited, and/or has a rank, and adjusts the clipping
|
||||
* for the scenario when the text could be too long
|
||||
*/
|
||||
public function checkClip():Void
|
||||
{
|
||||
var clipSize:Int = 290;
|
||||
var clipType:Int = 0;
|
||||
|
||||
if (ranking.visible == true) clipType += 1;
|
||||
if (favIcon.visible == true) clipType = 2;
|
||||
if (ranking.visible)
|
||||
{
|
||||
favIconBlurred.x = this.x + 370;
|
||||
favIcon.x = favIconBlurred.x;
|
||||
clipType += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
favIconBlurred.x = favIcon.x = this.x + 405;
|
||||
}
|
||||
|
||||
if (favIcon.visible) clipType += 1;
|
||||
|
||||
switch (clipType)
|
||||
{
|
||||
case 2:
|
||||
clipSize = 220;
|
||||
clipSize = 210;
|
||||
case 1:
|
||||
clipSize = 255;
|
||||
clipSize = 245;
|
||||
}
|
||||
songText.clipWidth = clipSize;
|
||||
}
|
||||
|
|
|
@ -371,6 +371,33 @@ class MainMenuState extends MusicBeatState
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.R)
|
||||
{
|
||||
// Give the user a hypothetical overridden score,
|
||||
// and see if we can maintain that golden P rank.
|
||||
funkin.save.Save.instance.setSongScore('tutorial', 'easy',
|
||||
{
|
||||
score: 1234567,
|
||||
tallies:
|
||||
{
|
||||
sick: 0,
|
||||
good: 0,
|
||||
bad: 0,
|
||||
shit: 1,
|
||||
missed: 0,
|
||||
combo: 0,
|
||||
maxCombo: 0,
|
||||
totalNotesHit: 1,
|
||||
totalNotes: 10,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.E)
|
||||
{
|
||||
funkin.save.Save.instance.debug_dumpSave();
|
||||
}
|
||||
#end
|
||||
|
||||
if (FlxG.sound.music != null && FlxG.sound.music.volume < 0.8)
|
||||
|
|
|
@ -28,6 +28,8 @@ class ControlsMenu extends funkin.ui.options.OptionsState.Page
|
|||
[NOTE_UP, NOTE_DOWN, NOTE_LEFT, NOTE_RIGHT],
|
||||
[UI_UP, UI_DOWN, UI_LEFT, UI_RIGHT, ACCEPT, BACK],
|
||||
[CUTSCENE_ADVANCE],
|
||||
[FREEPLAY_FAVORITE, FREEPLAY_LEFT, FREEPLAY_RIGHT],
|
||||
[WINDOW_FULLSCREEN, WINDOW_SCREENSHOT],
|
||||
[VOLUME_UP, VOLUME_DOWN, VOLUME_MUTE],
|
||||
[DEBUG_MENU, DEBUG_CHART]
|
||||
];
|
||||
|
@ -108,6 +110,18 @@ class ControlsMenu extends funkin.ui.options.OptionsState.Page
|
|||
headers.add(new AtlasText(0, y, "CUTSCENE", AtlasFont.BOLD)).screenCenter(X);
|
||||
y += spacer;
|
||||
}
|
||||
else if (currentHeader != "FREEPLAY_" && name.indexOf("FREEPLAY_") == 0)
|
||||
{
|
||||
currentHeader = "FREEPLAY_";
|
||||
headers.add(new AtlasText(0, y, "FREEPLAY", AtlasFont.BOLD)).screenCenter(X);
|
||||
y += spacer;
|
||||
}
|
||||
else if (currentHeader != "WINDOW_" && name.indexOf("WINDOW_") == 0)
|
||||
{
|
||||
currentHeader = "WINDOW_";
|
||||
headers.add(new AtlasText(0, y, "WINDOW", AtlasFont.BOLD)).screenCenter(X);
|
||||
y += spacer;
|
||||
}
|
||||
else if (currentHeader != "VOLUME_" && name.indexOf("VOLUME_") == 0)
|
||||
{
|
||||
currentHeader = "VOLUME_";
|
||||
|
@ -123,10 +137,10 @@ class ControlsMenu extends funkin.ui.options.OptionsState.Page
|
|||
|
||||
if (currentHeader != null && name.indexOf(currentHeader) == 0) name = name.substr(currentHeader.length);
|
||||
|
||||
var label = labels.add(new AtlasText(150, y, name, AtlasFont.BOLD));
|
||||
var label = labels.add(new AtlasText(100, y, name, AtlasFont.BOLD));
|
||||
label.alpha = 0.6;
|
||||
for (i in 0...COLUMNS)
|
||||
createItem(label.x + 400 + i * 300, y, control, i);
|
||||
createItem(label.x + 550 + i * 400, y, control, i);
|
||||
|
||||
y += spacer;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ class OptionsState extends MusicBeatState
|
|||
|
||||
override function create():Void
|
||||
{
|
||||
persistentUpdate = true;
|
||||
|
||||
var menuBG = new FlxSprite().loadGraphic(Paths.image('menuBG'));
|
||||
var hsv = new HSVShader();
|
||||
hsv.hue = -0.6;
|
||||
|
@ -55,8 +57,6 @@ class OptionsState extends MusicBeatState
|
|||
setPage(Controls);
|
||||
}
|
||||
|
||||
// disable for intro transition
|
||||
currentPage.enabled = false;
|
||||
super.create();
|
||||
}
|
||||
|
||||
|
@ -86,13 +86,6 @@ class OptionsState extends MusicBeatState
|
|||
}
|
||||
}
|
||||
|
||||
override function finishTransIn()
|
||||
{
|
||||
super.finishTransIn();
|
||||
|
||||
currentPage.enabled = true;
|
||||
}
|
||||
|
||||
function switchPage(name:PageName)
|
||||
{
|
||||
// TODO: Animate this transition?
|
||||
|
@ -266,11 +259,11 @@ class OptionsMenu extends Page
|
|||
#end
|
||||
}
|
||||
|
||||
enum PageName
|
||||
enum abstract PageName(String)
|
||||
{
|
||||
Options;
|
||||
Controls;
|
||||
Colors;
|
||||
Mods;
|
||||
Preferences;
|
||||
var Options = "options";
|
||||
var Controls = "controls";
|
||||
var Colors = "colors";
|
||||
var Mods = "mods";
|
||||
var Preferences = "preferences";
|
||||
}
|
||||
|
|
|
@ -387,6 +387,7 @@ class StoryMenuState extends MusicBeatState
|
|||
function changeLevel(change:Int = 0):Void
|
||||
{
|
||||
var currentIndex:Int = levelList.indexOf(currentLevelId);
|
||||
var prevIndex:Int = currentIndex;
|
||||
|
||||
currentIndex += change;
|
||||
|
||||
|
@ -417,7 +418,7 @@ class StoryMenuState extends MusicBeatState
|
|||
}
|
||||
}
|
||||
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
if (currentIndex != prevIndex) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
|
||||
updateText();
|
||||
updateBackground(previousLevelId);
|
||||
|
|
|
@ -265,6 +265,13 @@ class TitleState extends MusicBeatState
|
|||
if (FlxG.keys.pressed.DOWN) FlxG.sound.music.pitch -= 0.5 * elapsed;
|
||||
#end
|
||||
|
||||
#if desktop
|
||||
if (FlxG.keys.justPressed.ESCAPE)
|
||||
{
|
||||
Sys.exit(0);
|
||||
}
|
||||
#end
|
||||
|
||||
Conductor.instance.update();
|
||||
|
||||
/* if (FlxG.onMobile)
|
||||
|
|
|
@ -19,6 +19,7 @@ import haxe.ui.containers.dialogs.Dialogs.FileDialogExtensionInfo;
|
|||
class FileUtil
|
||||
{
|
||||
public static final FILE_FILTER_FNFC:FileFilter = new FileFilter("Friday Night Funkin' Chart (.fnfc)", "*.fnfc");
|
||||
public static final FILE_FILTER_JSON:FileFilter = new FileFilter("JSON Data File (.json)", "*.json");
|
||||
public static final FILE_FILTER_ZIP:FileFilter = new FileFilter("ZIP Archive (.zip)", "*.zip");
|
||||
public static final FILE_FILTER_PNG:FileFilter = new FileFilter("PNG Image (.png)", "*.png");
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ class WindowUtil
|
|||
});
|
||||
|
||||
openfl.Lib.current.stage.addEventListener(openfl.events.KeyboardEvent.KEY_DOWN, (e:openfl.events.KeyboardEvent) -> {
|
||||
for (key in PlayerSettings.player1.controls.getKeysForAction(FULLSCREEN))
|
||||
for (key in PlayerSettings.player1.controls.getKeysForAction(WINDOW_FULLSCREEN))
|
||||
{
|
||||
if (e.keyCode == key)
|
||||
{
|
||||
|
|
|
@ -103,7 +103,7 @@ class ScreenshotPlugin extends FlxBasic
|
|||
|
||||
public function hasPressedScreenshot():Bool
|
||||
{
|
||||
return PlayerSettings.player1.controls.SCREENSHOT;
|
||||
return PlayerSettings.player1.controls.WINDOW_SCREENSHOT;
|
||||
}
|
||||
|
||||
public function updatePreferences():Void
|
||||
|
|
Loading…
Reference in a new issue