1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-11-25 21:55:55 +00:00

Merge remote-tracking branch 'upstream/main' into better-templates

This commit is contained in:
tposejank 2024-07-10 20:37:31 -04:00
commit a27e743c5f
10 changed files with 343 additions and 176 deletions

51
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View file

@ -0,0 +1,51 @@
---
name: Bug Report
about: Report a bug or critical performance issue
title: 'Bug Report: [DESCRIBE YOUR BUG IN DETAIL HERE]'
labels: 'status: pending triage'
---
<!-- FILL THIS ISSUE THING OUT AS MUCH AS POSSIBLE
OR ELSE YOUR ISSUE WILL BE LESS LIKELY TO BE SOLVED!
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 pages and report them there, thank you :)
Please check for duplicates or similar issues, as well as performing simple troubleshooting steps (such as clearing cookies, clearing AppData, trying another browser) before submitting an issue.
From Joel On Software:
"Its pretty easy to remember the rule for a good bug report. Every good bug report needs exactly three things.
1. Steps to reproduce,
2. What you expected to see, and
3. What you saw instead."
-->
## Describe the Bug
<!-- A clear and concise description of what the bug is. -->
## 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 that's impacted. -->
## Desktop
- OS:
<!-- [e.g. Windows 10, 11, Mac, Linux Mint, Ubuntu, Arch (btw)] -->
- Browser:
<!-- [e.g. Chrome, Safari, Firefox, Edge, OperaGX, or None if you're playing the downloaded version!] -->
- Version:
<!-- [e.g. 0.4.0, 0.3.3, this can be found in the bottom left corner of the main menu!] -->
## Additional Context
<!-- Add any other context about the problem here. -->
<!-- If your game is FROZEN and you're playing a web version, press F12 to open up the browser dev window, go to the "Console" tab, and copy-paste whatever red error you're getting below -->

8
.github/ISSUE_TEMPLATE/enhancement.md vendored Normal file
View file

@ -0,0 +1,8 @@
---
name: Enhancement
about: Suggest a new feature
title: 'Enhancement: '
labels: 'status: pending triage'
---
#### Please check for duplicates or similar issues before submitting this suggestion.
## What is your suggestion, and why should it be implemented?

10
.github/PULL_REQUEST_TEMPLATE/bug.md vendored Normal file
View file

@ -0,0 +1,10 @@
---
name: Bug Fix
about: Fix a bug or critical performance issue
title: 'Bug Fix: '
labels: 'status: pending triage'
---
#### Please check for duplicates or similar PRs before submitting this PR.
## Does this PR close any issues? If so, link them below.
## Briefly describe the issue(s) fixed.

View file

@ -0,0 +1,10 @@
---
name: Enhancement
about: Add a new feature
title: 'Enhancement: '
labels: 'status: pending triage'
---
#### Please check for duplicates or similar PRs before submitting this PR.
## Does this PR close any issues? If so, link them below.
## What do your changes add, and why should they be implemented?

12
.github/changed-lines-count-labeler.yml vendored Normal file
View file

@ -0,0 +1,12 @@
# Add 'small' to any changes below 10 lines
small:
max: 9
# Add 'medium' to any changes between 10 and 100 lines
medium:
min: 10
max: 99
# Add 'large' to any changes for more than 100 lines
large:
min: 100

View file

@ -9,6 +9,19 @@ jobs:
pull-requests: write pull-requests: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/labeler@v5 - name: Set basic labels
uses: actions/labeler@v5
with: with:
sync-labels: true sync-labels: true
changed-lines-count-labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
name: An action for automatically labelling pull requests based on the changed lines count
steps:
- name: Set change count labels
uses: vkirilichev/changed-lines-count-labeler@v0.2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/changed-lines-count-labeler.yml

View file

@ -81,19 +81,9 @@ which would remove their rank if they had a lower one.
- Fixed a crash on Linux caused by an old version of hxCodec (thanks Noobz4Life!) - Fixed a crash on Linux caused by an old version of hxCodec (thanks Noobz4Life!)
- Optimized animation handling for characters (thanks richTrash21!) - Optimized animation handling for characters (thanks richTrash21!)
- Made improvements to compiling documentation (thanks gedehari!) - 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 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 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 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!) - Fixed a bug where characters would sometimes use the wrong scale value (thanks PurSnake!)
- Additional bug fixes and optimizations. - Additional bug fixes and optimizations.

View file

@ -2,14 +2,19 @@
0. Setup 0. Setup
- Download Haxe from [Haxe.org](https://haxe.org) - Download Haxe from [Haxe.org](https://haxe.org)
1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - Download Git from [git-scm.com](https://www.git-scm.com)
- `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors!
- 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. - Instead, open a command prompt and do the following steps...
2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) 1. Run `cd the\directory\you\want\the\source\code\in` to specify which folder the command prompt is working in.
3. Download Git from [git-scm.com](https://www.git-scm.com) - For example, `cd C:\Users\YOURNAME\Documents` would instruct the command prompt to perform the next steps in your Documents folder.
4. Install all haxelibs of the current branch by running `hmm install` 2. Run `git clone https://github.com/FunkinCrew/funkin.git` to clone the base repository.
5. Setup lime: `haxelib run lime setup` 3. Run `cd funkin` to enter the cloned repository's directory.
6. Platform setup 4. Run `git submodule update --init --recursive` to download the game's assets.
- NOTE: By performing this operation, you are downloading Content which is proprietary and protected by national and international copyright and trademark laws. See [the LICENSE.md file for the Funkin.assets](https://github.com/FunkinCrew/funkin.assets/blob/main/LICENSE.md) repo for more information.
5. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json
6. Run `hmm install` to install all haxelibs of the current branch
7. Run `haxelib run lime setup` to set up lime
8. Platform setup
- For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - 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: - When prompted, select "Individual Components" and make sure to download the following:
- MSVC v143 VS 2022 C++ x64/x86 build tools - MSVC v143 VS 2022 C++ x64/x86 build tools
@ -17,10 +22,12 @@
- Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/) - 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/) - Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/)
- HTML5: Compiles without any extra setup - HTML5: Compiles without any extra setup
7. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` 9. 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). 10. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State).
# Troubleshooting # Troubleshooting - GO THROUGH THESE STEPS BEFORE OPENING ISSUES ON GITHUB!
- 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`. - 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`.
- Make sure your game directory has an `assets` folder! If it's missing, copy the path to your `funkin` folder and run `cd the\path\you\copied`. Then follow the guide starting from **Step 4**.
- Check that your `assets` folder is not empty! If it is, go back to **Step 4** and follow the guide from there.
- The compilation process often fails due to having the wrong versions of the required libraries. Many errors can be resolved by deleting the `.haxelib` folder and following the guide starting from **Step 5**.

View file

@ -31,6 +31,7 @@ class Controls extends FlxActionSet
* Uses FlxActions to funnel various inputs to a single action. * Uses FlxActions to funnel various inputs to a single action.
*/ */
var _ui_up = new FunkinAction(Action.UI_UP); var _ui_up = new FunkinAction(Action.UI_UP);
var _ui_left = new FunkinAction(Action.UI_LEFT); var _ui_left = new FunkinAction(Action.UI_LEFT);
var _ui_right = new FunkinAction(Action.UI_RIGHT); var _ui_right = new FunkinAction(Action.UI_RIGHT);
var _ui_down = new FunkinAction(Action.UI_DOWN); var _ui_down = new FunkinAction(Action.UI_DOWN);
@ -325,19 +326,18 @@ class Controls extends FlxActionSet
add(_volume_down); add(_volume_down);
add(_volume_mute); add(_volume_mute);
for (action in digitalActions) { for (action in digitalActions)
if (Std.isOfType(action, FunkinAction)) { {
if (Std.isOfType(action, FunkinAction))
{
var funkinAction:FunkinAction = cast action; var funkinAction:FunkinAction = cast action;
byName[funkinAction.name] = funkinAction; byName[funkinAction.name] = funkinAction;
if (funkinAction.namePressed != null) if (funkinAction.namePressed != null) byName[funkinAction.namePressed] = funkinAction;
byName[funkinAction.namePressed] = funkinAction; if (funkinAction.nameReleased != null) byName[funkinAction.nameReleased] = funkinAction;
if (funkinAction.nameReleased != null)
byName[funkinAction.nameReleased] = funkinAction;
} }
} }
if (scheme == null) if (scheme == null) scheme = None;
scheme = None;
setKeyboardScheme(scheme, false); setKeyboardScheme(scheme, false);
} }
@ -350,38 +350,38 @@ class Controls extends FlxActionSet
public function check(name:Action, trigger:FlxInputState = JUST_PRESSED, gamepadOnly:Bool = false):Bool public function check(name:Action, trigger:FlxInputState = JUST_PRESSED, gamepadOnly:Bool = false):Bool
{ {
#if debug #if debug
if (!byName.exists(name)) if (!byName.exists(name)) throw 'Invalid name: $name';
throw 'Invalid name: $name';
#end #end
var action = byName[name]; var action = byName[name];
if (gamepadOnly) if (gamepadOnly) return action.checkFiltered(trigger, GAMEPAD);
return action.checkFiltered(trigger, GAMEPAD);
else else
return action.checkFiltered(trigger); return action.checkFiltered(trigger);
} }
public function getKeysForAction(name:Action):Array<FlxKey> { public function getKeysForAction(name:Action):Array<FlxKey>
{
#if debug #if debug
if (!byName.exists(name)) if (!byName.exists(name)) throw 'Invalid name: $name';
throw 'Invalid name: $name';
#end #end
// TODO: Revert to `.map().filter()` once HashLink doesn't complain anymore. // TODO: Revert to `.map().filter()` once HashLink doesn't complain anymore.
var result:Array<FlxKey> = []; var result:Array<FlxKey> = [];
for (input in byName[name].inputs) { for (input in byName[name].inputs)
{
if (input.device == KEYBOARD) result.push(input.inputID); if (input.device == KEYBOARD) result.push(input.inputID);
} }
return result; return result;
} }
public function getButtonsForAction(name:Action):Array<FlxGamepadInputID> { public function getButtonsForAction(name:Action):Array<FlxGamepadInputID>
{
#if debug #if debug
if (!byName.exists(name)) if (!byName.exists(name)) throw 'Invalid name: $name';
throw 'Invalid name: $name';
#end #end
var result:Array<FlxGamepadInputID> = []; var result:Array<FlxGamepadInputID> = [];
for (input in byName[name].inputs) { for (input in byName[name].inputs)
{
if (input.device == GAMEPAD) result.push(input.inputID); if (input.device == GAMEPAD) result.push(input.inputID);
} }
return result; return result;
@ -405,7 +405,7 @@ class Controls extends FlxActionSet
function getActionFromControl(control:Control):FlxActionDigital function getActionFromControl(control:Control):FlxActionDigital
{ {
return switch(control) return switch (control)
{ {
case UI_UP: _ui_up; case UI_UP: _ui_up;
case UI_DOWN: _ui_down; case UI_DOWN: _ui_down;
@ -448,7 +448,7 @@ class Controls extends FlxActionSet
*/ */
function forEachBound(control:Control, func:FlxActionDigital->FlxInputState->Void) function forEachBound(control:Control, func:FlxActionDigital->FlxInputState->Void)
{ {
switch(control) switch (control)
{ {
case UI_UP: case UI_UP:
func(_ui_up, PRESSED); func(_ui_up, PRESSED);
@ -519,10 +519,9 @@ class Controls extends FlxActionSet
public function replaceBinding(control:Control, device:Device, toAdd:Int, toRemove:Int) public function replaceBinding(control:Control, device:Device, toAdd:Int, toRemove:Int)
{ {
if (toAdd == toRemove) if (toAdd == toRemove) return;
return;
switch(device) switch (device)
{ {
case Keys: case Keys:
forEachBound(control, function(action, state) replaceKey(action, toAdd, toRemove, state)); forEachBound(control, function(action, state) replaceKey(action, toAdd, toRemove, state));
@ -534,7 +533,8 @@ class Controls extends FlxActionSet
function replaceKey(action:FlxActionDigital, toAdd:FlxKey, toRemove:FlxKey, state:FlxInputState) function replaceKey(action:FlxActionDigital, toAdd:FlxKey, toRemove:FlxKey, state:FlxInputState)
{ {
if (action.inputs.length == 0) { if (action.inputs.length == 0)
{
// Add the keybind, don't replace. // Add the keybind, don't replace.
addKeys(action, [toAdd], state); addKeys(action, [toAdd], state);
return; return;
@ -548,34 +548,44 @@ class Controls extends FlxActionSet
if (input.device == KEYBOARD && input.inputID == toRemove) if (input.device == KEYBOARD && input.inputID == toRemove)
{ {
if (toAdd == FlxKey.NONE) { if (toAdd == FlxKey.NONE)
{
// Remove the keybind, don't replace. // Remove the keybind, don't replace.
action.inputs.remove(input); action.inputs.remove(input);
} else { }
else
{
// Replace the keybind. // Replace the keybind.
@:privateAccess @:privateAccess
action.inputs[i].inputID = toAdd; action.inputs[i].inputID = toAdd;
} }
hasReplaced = true; hasReplaced = true;
} else if (input.device == KEYBOARD && input.inputID == toAdd) { }
else if (input.device == KEYBOARD && input.inputID == toAdd)
{
// This key is already bound! // This key is already bound!
if (hasReplaced) { if (hasReplaced)
{
// Remove the duplicate keybind, don't replace. // Remove the duplicate keybind, don't replace.
action.inputs.remove(input); action.inputs.remove(input);
} else { }
else
{
hasReplaced = true; hasReplaced = true;
} }
} }
} }
if (!hasReplaced) { if (!hasReplaced)
{
addKeys(action, [toAdd], state); addKeys(action, [toAdd], state);
} }
} }
function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:FlxGamepadInputID, toRemove:FlxGamepadInputID, state:FlxInputState) function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:FlxGamepadInputID, toRemove:FlxGamepadInputID, state:FlxInputState)
{ {
if (action.inputs.length == 0) { if (action.inputs.length == 0)
{
addButtons(action, [toAdd], state, deviceID); addButtons(action, [toAdd], state, deviceID);
return; return;
} }
@ -594,7 +604,8 @@ class Controls extends FlxActionSet
} }
} }
if (!hasReplaced) { if (!hasReplaced)
{
addButtons(action, [toAdd], state, deviceID); addButtons(action, [toAdd], state, deviceID);
} }
} }
@ -606,18 +617,16 @@ class Controls extends FlxActionSet
var action = controls.byName[name]; var action = controls.byName[name];
for (input in action.inputs) for (input in action.inputs)
{ {
if (device == null || isDevice(input, device)) if (device == null || isDevice(input, device)) byName[name].add(cast input);
byName[name].add(cast input);
} }
} }
switch(device) switch (device)
{ {
case null: case null:
// add all // add all
for (gamepad in controls.gamepadsAdded) for (gamepad in controls.gamepadsAdded)
if (gamepadsAdded.indexOf(gamepad) == -1) if (gamepadsAdded.indexOf(gamepad) == -1) gamepadsAdded.push(gamepad);
gamepadsAdded.push(gamepad);
mergeKeyboardScheme(controls.keyboardScheme); mergeKeyboardScheme(controls.keyboardScheme);
@ -637,7 +646,7 @@ class Controls extends FlxActionSet
{ {
if (scheme != None) if (scheme != None)
{ {
switch(keyboardScheme) switch (keyboardScheme)
{ {
case None: case None:
keyboardScheme = scheme; keyboardScheme = scheme;
@ -672,7 +681,8 @@ class Controls extends FlxActionSet
static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState) static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState)
{ {
for (key in keys) { for (key in keys)
{
if (key == FlxKey.NONE) continue; // Ignore unbound keys. if (key == FlxKey.NONE) continue; // Ignore unbound keys.
action.addKey(key, state); action.addKey(key, state);
} }
@ -684,15 +694,13 @@ class Controls extends FlxActionSet
while (i-- > 0) while (i-- > 0)
{ {
var input = action.inputs[i]; var input = action.inputs[i];
if (input.device == KEYBOARD && keys.indexOf(cast input.inputID) != -1) if (input.device == KEYBOARD && keys.indexOf(cast input.inputID) != -1) action.remove(input);
action.remove(input);
} }
} }
public function setKeyboardScheme(scheme:KeyboardScheme, reset = true) public function setKeyboardScheme(scheme:KeyboardScheme, reset = true)
{ {
if (reset) if (reset) removeKeyboard();
removeKeyboard();
keyboardScheme = scheme; keyboardScheme = scheme;
@ -724,10 +732,13 @@ class Controls extends FlxActionSet
bindMobileLol(); bindMobileLol();
} }
function getDefaultKeybinds(scheme:KeyboardScheme, control:Control):Array<FlxKey> { function getDefaultKeybinds(scheme:KeyboardScheme, control:Control):Array<FlxKey>
switch (scheme) { {
switch (scheme)
{
case Solo: case Solo:
switch (control) { switch (control)
{
case Control.UI_UP: return [W, FlxKey.UP]; case Control.UI_UP: return [W, FlxKey.UP];
case Control.UI_DOWN: return [S, FlxKey.DOWN]; case Control.UI_DOWN: return [S, FlxKey.DOWN];
case Control.UI_LEFT: return [A, FlxKey.LEFT]; case Control.UI_LEFT: return [A, FlxKey.LEFT];
@ -754,7 +765,8 @@ class Controls extends FlxActionSet
case Control.VOLUME_MUTE: return [ZERO, NUMPADZERO]; case Control.VOLUME_MUTE: return [ZERO, NUMPADZERO];
} }
case Duo(true): case Duo(true):
switch (control) { switch (control)
{
case Control.UI_UP: return [W]; case Control.UI_UP: return [W];
case Control.UI_DOWN: return [S]; case Control.UI_DOWN: return [S];
case Control.UI_LEFT: return [A]; case Control.UI_LEFT: return [A];
@ -779,10 +791,10 @@ class Controls extends FlxActionSet
case Control.VOLUME_UP: return [PLUS]; case Control.VOLUME_UP: return [PLUS];
case Control.VOLUME_DOWN: return [MINUS]; case Control.VOLUME_DOWN: return [MINUS];
case Control.VOLUME_MUTE: return [ZERO]; case Control.VOLUME_MUTE: return [ZERO];
} }
case Duo(false): case Duo(false):
switch (control) { switch (control)
{
case Control.UI_UP: return [FlxKey.UP]; case Control.UI_UP: return [FlxKey.UP];
case Control.UI_DOWN: return [FlxKey.DOWN]; case Control.UI_DOWN: return [FlxKey.DOWN];
case Control.UI_LEFT: return [FlxKey.LEFT]; case Control.UI_LEFT: return [FlxKey.LEFT];
@ -807,7 +819,6 @@ class Controls extends FlxActionSet
case Control.VOLUME_UP: return [NUMPADPLUS]; case Control.VOLUME_UP: return [NUMPADPLUS];
case Control.VOLUME_DOWN: return [NUMPADMINUS]; case Control.VOLUME_DOWN: return [NUMPADMINUS];
case Control.VOLUME_MUTE: return [NUMPADZERO]; case Control.VOLUME_MUTE: return [NUMPADZERO];
} }
default: default:
// Fallthrough. // Fallthrough.
@ -834,8 +845,7 @@ class Controls extends FlxActionSet
#end #end
#if android #if android
forEachBound(Control.BACK, function(action, pres) forEachBound(Control.BACK, function(action, pres) {
{
action.add(new FlxActionInputDigitalAndroid(FlxAndroidKey.BACK, JUST_PRESSED)); action.add(new FlxActionInputDigitalAndroid(FlxAndroidKey.BACK, JUST_PRESSED));
}); });
#end #end
@ -849,8 +859,7 @@ class Controls extends FlxActionSet
while (i-- > 0) while (i-- > 0)
{ {
var input = action.inputs[i]; var input = action.inputs[i];
if (input.device == KEYBOARD) if (input.device == KEYBOARD) action.remove(input);
action.remove(input);
} }
} }
} }
@ -862,11 +871,13 @@ class Controls extends FlxActionSet
fromSaveData(padData, Gamepad(id)); fromSaveData(padData, Gamepad(id));
} }
public function getGamepadIds():Array<Int> { public function getGamepadIds():Array<Int>
{
return gamepadsAdded; return gamepadsAdded;
} }
public function getGamepads():Array<FlxGamepad> { public function getGamepads():Array<FlxGamepad>
{
return [for (id in gamepadsAdded) FlxG.gamepads.getByID(id)]; return [for (id in gamepadsAdded) FlxG.gamepads.getByID(id)];
} }
@ -886,8 +897,7 @@ class Controls extends FlxActionSet
while (i-- > 0) while (i-- > 0)
{ {
var input = action.inputs[i]; var input = action.inputs[i];
if (isGamepad(input, deviceID)) if (isGamepad(input, deviceID)) action.remove(input);
action.remove(input);
} }
} }
@ -924,32 +934,58 @@ class Controls extends FlxActionSet
]); ]);
} }
function getDefaultGamepadBinds(control:Control):Array<FlxGamepadInputID> { function getDefaultGamepadBinds(control:Control):Array<FlxGamepadInputID>
switch(control) { {
case Control.ACCEPT: return [#if switch B #else A #end]; switch (control)
case Control.BACK: return [#if switch A #else B #end]; {
case Control.UI_UP: return [DPAD_UP, LEFT_STICK_DIGITAL_UP]; case Control.ACCEPT:
case Control.UI_DOWN: return [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN]; return [#if switch B #else A #end];
case Control.UI_LEFT: return [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT]; case Control.BACK:
case Control.UI_RIGHT: return [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT]; return [#if switch A #else B #end];
case Control.NOTE_UP: return [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP]; case Control.UI_UP:
case Control.NOTE_DOWN: return [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN]; return [DPAD_UP, LEFT_STICK_DIGITAL_UP];
case Control.NOTE_LEFT: return [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT]; case Control.UI_DOWN:
case Control.NOTE_RIGHT: return [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT]; return [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN];
case Control.PAUSE: return [START]; case Control.UI_LEFT:
case Control.RESET: return [FlxGamepadInputID.BACK]; // Back (i.e. Select) return [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT];
case Control.WINDOW_FULLSCREEN: []; case Control.UI_RIGHT:
case Control.WINDOW_SCREENSHOT: []; return [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT];
case Control.CUTSCENE_ADVANCE: return [A]; case Control.NOTE_UP:
case Control.FREEPLAY_FAVORITE: [FlxGamepadInputID.BACK]; // Back (i.e. Select) return [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP];
case Control.FREEPLAY_LEFT: [LEFT_SHOULDER]; case Control.NOTE_DOWN:
case Control.FREEPLAY_RIGHT: [RIGHT_SHOULDER]; return [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN];
case Control.VOLUME_UP: []; case Control.NOTE_LEFT:
case Control.VOLUME_DOWN: []; return [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT];
case Control.VOLUME_MUTE: []; case Control.NOTE_RIGHT:
case Control.DEBUG_MENU: []; return [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT];
case Control.DEBUG_CHART: []; case Control.PAUSE:
case Control.DEBUG_STAGE: []; return [START];
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.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: default:
// Fallthrough. // Fallthrough.
} }
@ -967,8 +1003,7 @@ class Controls extends FlxActionSet
public function touchShit(control:Control, id) public function touchShit(control:Control, id)
{ {
forEachBound(control, function(action, state) forEachBound(control, function(action, state) {
{
// action // action
}); });
} }
@ -984,7 +1019,8 @@ class Controls extends FlxActionSet
inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id) inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id)
{ {
for (button in buttons) { for (button in buttons)
{
if (button == FlxGamepadInputID.NONE) continue; // Ignore unbound keys. if (button == FlxGamepadInputID.NONE) continue; // Ignore unbound keys.
action.addGamepad(button, state, id); action.addGamepad(button, state, id);
} }
@ -996,29 +1032,25 @@ class Controls extends FlxActionSet
while (i-- > 0) while (i-- > 0)
{ {
var input = action.inputs[i]; var input = action.inputs[i];
if (isGamepad(input, gamepadID) && buttons.indexOf(cast input.inputID) != -1) if (isGamepad(input, gamepadID) && buttons.indexOf(cast input.inputID) != -1) action.remove(input);
action.remove(input);
} }
} }
public function getInputsFor(control:Control, device:Device, ?list:Array<Int>):Array<Int> public function getInputsFor(control:Control, device:Device, ?list:Array<Int>):Array<Int>
{ {
if (list == null) if (list == null) list = [];
list = [];
switch(device) switch (device)
{ {
case Keys: case Keys:
for (input in getActionFromControl(control).inputs) for (input in getActionFromControl(control).inputs)
{ {
if (input.device == KEYBOARD) if (input.device == KEYBOARD) list.push(input.inputID);
list.push(input.inputID);
} }
case Gamepad(id): case Gamepad(id):
for (input in getActionFromControl(control).inputs) for (input in getActionFromControl(control).inputs)
{ {
if (isGamepad(input, id)) if (isGamepad(input, id)) list.push(input.inputID);
list.push(input.inputID);
} }
} }
return list; return list;
@ -1026,7 +1058,7 @@ class Controls extends FlxActionSet
public function removeDevice(device:Device) public function removeDevice(device:Device)
{ {
switch(device) switch (device)
{ {
case Keys: case Keys:
setKeyboardScheme(None); setKeyboardScheme(None);
@ -1040,27 +1072,32 @@ class Controls extends FlxActionSet
* An EMPTY array means the control is uninitialized and needs to be reset to default. * An EMPTY array means the control is uninitialized and needs to be reset to default.
* An array with a single FlxKey.NONE means the control was intentionally unbound by the user. * An array with a single FlxKey.NONE means the control was intentionally unbound by the user.
*/ */
public function fromSaveData(data:Dynamic, device:Device) public function fromSaveData(data:Dynamic, device:Device):Void
{ {
for (control in Control.createAll()) for (control in Control.createAll())
{ {
var inputs:Array<Int> = Reflect.field(data, control.getName()); var inputs:Array<Int> = Reflect.field(data, control.getName());
inputs = inputs.distinct(); inputs = inputs?.distinct();
if (inputs != null) if (inputs != null)
{ {
if (inputs.length == 0) { if (inputs.length == 0)
{
trace('Control ${control} is missing bindings, resetting to default.'); trace('Control ${control} is missing bindings, resetting to default.');
switch(device) switch (device)
{ {
case Keys: case Keys:
bindKeys(control, getDefaultKeybinds(Solo, control)); bindKeys(control, getDefaultKeybinds(Solo, control));
case Gamepad(id): case Gamepad(id):
bindButtons(control, id, getDefaultGamepadBinds(control)); bindButtons(control, id, getDefaultGamepadBinds(control));
} }
} else if (inputs == [FlxKey.NONE]) { }
else if (inputs == [FlxKey.NONE])
{
trace('Control ${control} is unbound, leaving it be.'); trace('Control ${control} is unbound, leaving it be.');
} else { }
switch(device) else
{
switch (device)
{ {
case Keys: case Keys:
bindKeys(control, inputs.copy()); bindKeys(control, inputs.copy());
@ -1068,9 +1105,11 @@ class Controls extends FlxActionSet
bindButtons(control, id, inputs.copy()); bindButtons(control, id, inputs.copy());
} }
} }
} else { }
else
{
trace('Control ${control} is missing bindings, resetting to default.'); trace('Control ${control} is missing bindings, resetting to default.');
switch(device) switch (device)
{ {
case Keys: case Keys:
bindKeys(control, getDefaultKeybinds(Solo, control)); bindKeys(control, getDefaultKeybinds(Solo, control));
@ -1095,9 +1134,12 @@ class Controls extends FlxActionSet
var inputs = getInputsFor(control, device); var inputs = getInputsFor(control, device);
isEmpty = isEmpty && inputs.length == 0; isEmpty = isEmpty && inputs.length == 0;
if (inputs.length == 0) { if (inputs.length == 0)
{
inputs = [FlxKey.NONE]; inputs = [FlxKey.NONE];
} else { }
else
{
inputs = inputs.distinct(); inputs = inputs.distinct();
} }
@ -1109,7 +1151,7 @@ class Controls extends FlxActionSet
static function isDevice(input:FlxActionInput, device:Device) static function isDevice(input:FlxActionInput, device:Device)
{ {
return switch(device) return switch (device)
{ {
case Keys: input.device == KEYBOARD; case Keys: input.device == KEYBOARD;
case Gamepad(id): isGamepad(input, id); case Gamepad(id): isGamepad(input, id);
@ -1141,7 +1183,8 @@ typedef Swipes =
* - Combining `pressed` and `released` inputs into one action. * - Combining `pressed` and `released` inputs into one action.
* - Filtering by input method (`KEYBOARD`, `MOUSE`, `GAMEPAD`, etc). * - Filtering by input method (`KEYBOARD`, `MOUSE`, `GAMEPAD`, etc).
*/ */
class FunkinAction extends FlxActionDigital { class FunkinAction extends FlxActionDigital
{
public var namePressed(default, null):Null<String>; public var namePressed(default, null):Null<String>;
public var nameReleased(default, null):Null<String>; public var nameReleased(default, null):Null<String>;
@ -1158,83 +1201,102 @@ class FunkinAction extends FlxActionDigital {
/** /**
* Input checks default to whether the input was just pressed, on any input device. * Input checks default to whether the input was just pressed, on any input device.
*/ */
public override function check():Bool { public override function check():Bool
{
return checkFiltered(JUST_PRESSED); return checkFiltered(JUST_PRESSED);
} }
/** /**
* Check whether the input is currently being held. * Check whether the input is currently being held.
*/ */
public function checkPressed():Bool { public function checkPressed():Bool
{
return checkFiltered(PRESSED); return checkFiltered(PRESSED);
} }
/** /**
* Check whether the input is currently being held, and was not held last frame. * Check whether the input is currently being held, and was not held last frame.
*/ */
public function checkJustPressed():Bool { public function checkJustPressed():Bool
{
return checkFiltered(JUST_PRESSED); return checkFiltered(JUST_PRESSED);
} }
/** /**
* Check whether the input is not currently being held. * Check whether the input is not currently being held.
*/ */
public function checkReleased():Bool { public function checkReleased():Bool
{
return checkFiltered(RELEASED); return checkFiltered(RELEASED);
} }
/** /**
* Check whether the input is not currently being held, and was held last frame. * Check whether the input is not currently being held, and was held last frame.
*/ */
public function checkJustReleased():Bool { public function checkJustReleased():Bool
{
return checkFiltered(JUST_RELEASED); return checkFiltered(JUST_RELEASED);
} }
/** /**
* Check whether the input is currently being held by a gamepad device. * Check whether the input is currently being held by a gamepad device.
*/ */
public function checkPressedGamepad():Bool { public function checkPressedGamepad():Bool
{
return checkFiltered(PRESSED, GAMEPAD); return checkFiltered(PRESSED, GAMEPAD);
} }
/** /**
* Check whether the input is currently being held by a gamepad device, and was not held last frame. * Check whether the input is currently being held by a gamepad device, and was not held last frame.
*/ */
public function checkJustPressedGamepad():Bool { public function checkJustPressedGamepad():Bool
{
return checkFiltered(JUST_PRESSED, GAMEPAD); return checkFiltered(JUST_PRESSED, GAMEPAD);
} }
/** /**
* Check whether the input is not currently being held by a gamepad device. * Check whether the input is not currently being held by a gamepad device.
*/ */
public function checkReleasedGamepad():Bool { public function checkReleasedGamepad():Bool
{
return checkFiltered(RELEASED, GAMEPAD); return checkFiltered(RELEASED, GAMEPAD);
} }
/** /**
* Check whether the input is not currently being held by a gamepad device, and was held last frame. * Check whether the input is not currently being held by a gamepad device, and was held last frame.
*/ */
public function checkJustReleasedGamepad():Bool { public function checkJustReleasedGamepad():Bool
{
return checkFiltered(JUST_RELEASED, GAMEPAD); return checkFiltered(JUST_RELEASED, GAMEPAD);
} }
public function checkMultiFiltered(?filterTriggers:Array<FlxInputState>, ?filterDevices:Array<FlxInputDevice>):Bool { public function checkMultiFiltered(?filterTriggers:Array<FlxInputState>, ?filterDevices:Array<FlxInputDevice>):Bool
if (filterTriggers == null) { {
if (filterTriggers == null)
{
filterTriggers = [PRESSED, JUST_PRESSED]; filterTriggers = [PRESSED, JUST_PRESSED];
} }
if (filterDevices == null) { if (filterDevices == null)
{
filterDevices = []; filterDevices = [];
} }
// Perform checkFiltered for each combination. // Perform checkFiltered for each combination.
for (i in filterTriggers) { for (i in filterTriggers)
if (filterDevices.length == 0) { {
if (checkFiltered(i)) { if (filterDevices.length == 0)
{
if (checkFiltered(i))
{
return true; return true;
} }
} else { }
for (j in filterDevices) { else
if (checkFiltered(i, j)) { {
for (j in filterDevices)
{
if (checkFiltered(i, j))
{
return true; return true;
} }
} }
@ -1249,52 +1311,56 @@ class FunkinAction extends FlxActionDigital {
* @param filterTrigger Optionally filter by trigger condition (`JUST_PRESSED`, `PRESSED`, `JUST_RELEASED`, `RELEASED`). * @param filterTrigger Optionally filter by trigger condition (`JUST_PRESSED`, `PRESSED`, `JUST_RELEASED`, `RELEASED`).
* @param filterDevice Optionally filter by device (`KEYBOARD`, `MOUSE`, `GAMEPAD`, `OTHER`). * @param filterDevice Optionally filter by device (`KEYBOARD`, `MOUSE`, `GAMEPAD`, `OTHER`).
*/ */
public function checkFiltered(?filterTrigger:FlxInputState, ?filterDevice:FlxInputDevice):Bool { public function checkFiltered(?filterTrigger:FlxInputState, ?filterDevice:FlxInputDevice):Bool
{
// The normal // The normal
// Make sure we only update the inputs once per frame. // Make sure we only update the inputs once per frame.
var key = '${filterTrigger}:${filterDevice}'; var key = '${filterTrigger}:${filterDevice}';
var cacheEntry = cache.get(key); var cacheEntry = cache.get(key);
if (cacheEntry != null && cacheEntry.timestamp == FlxG.game.ticks) { if (cacheEntry != null && cacheEntry.timestamp == FlxG.game.ticks)
{
return cacheEntry.value; return cacheEntry.value;
} }
// Use a for loop instead so we can remove inputs while iterating. // Use a for loop instead so we can remove inputs while iterating.
// We don't return early because we need to call check() on ALL inputs. // We don't return early because we need to call check() on ALL inputs.
var result = false; var result = false;
var len = inputs != null ? inputs.length : 0; var len = inputs != null ? inputs.length : 0;
for (i in 0...len) for (i in 0...len)
{ {
var j = len - i - 1; var j = len - i - 1;
var input = inputs[j]; var input = inputs[j];
// Filter out dead inputs. // Filter out dead inputs.
if (input.destroyed) if (input.destroyed)
{ {
inputs.splice(j, 1); inputs.splice(j, 1);
continue; continue;
} }
// Update the input. // Update the input.
input.update(); input.update();
// Check whether the input is the right trigger. // Check whether the input is the right trigger.
if (filterTrigger != null && input.trigger != filterTrigger) { if (filterTrigger != null && input.trigger != filterTrigger)
{
continue; continue;
} }
// Check whether the input is the right device. // Check whether the input is the right device.
if (filterDevice != null && input.device != filterDevice) { if (filterDevice != null && input.device != filterDevice)
{
continue; continue;
} }
// Check whether the input has triggered. // Check whether the input has triggered.
if (input.check(this)) if (input.check(this))
{ {
result = true; result = true;
} }
} }
// We need to cache this result. // We need to cache this result.
cache.set(key, {timestamp: FlxG.game.ticks, value: result}); cache.set(key, {timestamp: FlxG.game.ticks, value: result});
@ -1391,12 +1457,12 @@ class FlxActionInputDigitalMobileSwipeGameplay extends FlxActionInputDigital
{ {
var degAngle = FlxAngle.asDegrees(swp.touchAngle); var degAngle = FlxAngle.asDegrees(swp.touchAngle);
switch(trigger) switch (trigger)
{ {
case JUST_PRESSED: case JUST_PRESSED:
if (swp.touchLength >= activateLength) if (swp.touchLength >= activateLength)
{ {
switch(inputID) switch (inputID)
{ {
case FlxDirectionFlags.UP: case FlxDirectionFlags.UP:
if (degAngle >= 45 && degAngle <= 90 + 45) return properTouch(swp); if (degAngle >= 45 && degAngle <= 90 + 45) return properTouch(swp);
@ -1440,7 +1506,7 @@ class FlxActionInputDigitalAndroid extends FlxActionInputDigital
override public function check(Action:FlxAction):Bool override public function check(Action:FlxAction):Bool
{ {
return switch(trigger) return switch (trigger)
{ {
#if android #if android
case PRESSED: FlxG.android.checkStatus(inputID, PRESSED) || FlxG.android.checkStatus(inputID, PRESSED); case PRESSED: FlxG.android.checkStatus(inputID, PRESSED) || FlxG.android.checkStatus(inputID, PRESSED);

View file

@ -51,7 +51,7 @@ class AnsiTrace
public static function traceBF() public static function traceBF()
{ {
#if sys #if (sys && debug)
if (colorSupported) if (colorSupported)
{ {
for (line in ansiBF) for (line in ansiBF)