1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2024-11-09 00:04:42 +00:00

Merge pull request #2 from ninjamuffin99/options

Options
This commit is contained in:
Cameron Taylor 2021-03-23 18:48:03 -04:00 committed by GitHub
commit dd14af7499
25 changed files with 2831 additions and 795 deletions

View file

@ -75,26 +75,26 @@
<library name="week7" preload="false" />
</section>
<assets path="assets/songs" library="songs" exclude="*.ogg" if="web"/>
<assets path="assets/songs" library="songs" exclude="*.mp3" unless="web"/>
<assets path="assets/shared" library="shared" exclude="*.ogg" if="web"/>
<assets path="assets/shared" library="shared" exclude="*.mp3" unless="web"/>
<assets path="assets/tutorial" library="tutorial" exclude="*.ogg" if="web"/>
<assets path="assets/tutorial" library="tutorial" exclude="*.mp3" unless="web"/>
<assets path="assets/week1" library="week1" exclude="*.ogg" if="web"/>
<assets path="assets/week1" library="week1" exclude="*.mp3" unless="web"/>
<assets path="assets/week2" library="week2" exclude="*.ogg" if="web"/>
<assets path="assets/week2" library="week2" exclude="*.mp3" unless="web"/>
<assets path="assets/week3" library="week3" exclude="*.ogg" if="web"/>
<assets path="assets/week3" library="week3" exclude="*.mp3" unless="web"/>
<assets path="assets/week4" library="week4" exclude="*.ogg" if="web"/>
<assets path="assets/week4" library="week4" exclude="*.mp3" unless="web"/>
<assets path="assets/week5" library="week5" exclude="*.ogg" if="web"/>
<assets path="assets/week5" library="week5" exclude="*.mp3" unless="web"/>
<assets path="assets/week6" library="week6" exclude="*.ogg" if="web"/>
<assets path="assets/week6" library="week6" exclude="*.mp3" unless="web"/>
<assets path="assets/week7" library="week7" exclude="*.ogg" if="web"/>
<assets path="assets/week7" library="week7" exclude="*.mp3" unless="web"/>
<assets path="assets/songs" library="songs" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/songs" library="songs" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/shared" library="shared" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/shared" library="shared" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/tutorial" library="tutorial" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/tutorial" library="tutorial" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/week1" library="week1" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/week1" library="week1" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/week2" library="week2" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/week2" library="week2" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/week3" library="week3" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/week3" library="week3" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/week4" library="week4" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/week4" library="week4" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/week5" library="week5" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/week5" library="week5" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/week6" library="week6" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/week6" library="week6" exclude="*.fla|*.mp3" unless="web"/>
<assets path="assets/week7" library="week7" exclude="*.fla|*.ogg" if="web"/>
<assets path="assets/week7" library="week7" exclude="*.fla|*.mp3" unless="web"/>
<!-- <assets path='example_mods' rename='mods' embed='false'/> -->
@ -122,9 +122,10 @@
<!--In case you want to use the ui package-->
<haxelib name="flixel-ui" />
<haxelib name="newgrounds"/>
<haxelib name="newgrounds" unless="switch"/>
<haxelib name="faxe" if='switch'/>
<haxelib name="polymod"/>
<haxelib name="hxcpp-debug-server" if="desktop debug"/>
<haxelib name="discord_rpc" if="desktop"/>
<!-- <haxelib name="hxcpp-debug-server" if="desktop"/> -->
@ -177,6 +178,21 @@
<!-- <haxedef name="SKIP_TO_PLAYSTATE" if="debug" /> -->
<haxedef name="NG_LOGIN" if="newgrounds" />
<haxedef name="CAN_OPEN_LINKS" unless="switch"/>
<haxedef name="CAN_CHEAT" if="switch debug"/>
<!-- <haxedef name="CLEAR_INPUT_SAVE"/> -->
<section if="newgrounds">
<!-- Enables Ng.core.verbose -->
<!-- <haxedef name="NG_VERBOSE" /> -->
<!-- Enables a NG debug session, so medals don't permently unlock -->
<!-- <haxedef name="NG_DEBUG" /> -->
<!-- pretends that the saved session Id was expired, forcing the reconnect prompt -->
<!-- <haxedef name="NG_FORCE_EXPIRED_SESSION" if="debug" /> -->
</section>
</project>

View file

@ -62,8 +62,9 @@ package;
class APIStuff
{
public static var API:String = "";
public static var EncKey:String = "";
inline public static var API:String = "51348:TtzK0rZ8";
inline public static var EncKey:String = "5NqKsSVSNKHbF9fPgZPqPg==";
inline public static var SESSION:String = null;
}
```

View file

@ -40,7 +40,7 @@ class Alphabet extends FlxSpriteGroup
var isBold:Bool = false;
public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false)
public function new(x:Float = 0.0, y:Float = 0.0, text:String = "", ?bold:Bool = false, typed:Bool = false)
{
super(x, y);

View file

@ -11,57 +11,6 @@ import flixel.input.gamepad.FlxGamepadButton;
import flixel.input.gamepad.FlxGamepadInputID;
import flixel.input.keyboard.FlxKey;
#if (haxe >= "4.0.0")
enum abstract Action(String) to String from String
{
var UP = "up";
var LEFT = "left";
var RIGHT = "right";
var DOWN = "down";
var UP_P = "up-press";
var LEFT_P = "left-press";
var RIGHT_P = "right-press";
var DOWN_P = "down-press";
var UP_R = "up-release";
var LEFT_R = "left-release";
var RIGHT_R = "right-release";
var DOWN_R = "down-release";
var ACCEPT = "accept";
var BACK = "back";
var PAUSE = "pause";
var RESET = "reset";
var CHEAT = "cheat";
}
#else
@:enum
abstract Action(String) to String from String
{
var UP = "up";
var LEFT = "left";
var RIGHT = "right";
var DOWN = "down";
var UP_P = "up-press";
var LEFT_P = "left-press";
var RIGHT_P = "right-press";
var DOWN_P = "down-press";
var UP_R = "up-release";
var LEFT_R = "left-release";
var RIGHT_R = "right-release";
var DOWN_R = "down-release";
var ACCEPT = "accept";
var BACK = "back";
var PAUSE = "pause";
var RESET = "reset";
var CHEAT = "cheat";
}
#end
enum Device
{
Keys;
Gamepad(id:Int);
}
/**
* Since, in many cases multiple actions should use similar keys, we don't want the
* rebinding UI to list every action. ActionBinders are what the user percieves as
@ -69,15 +18,64 @@ enum Device
*/
enum Control
{
UP;
LEFT;
RIGHT;
DOWN;
// List notes in order from left to right on gameplay screen.
NOTE_LEFT;
NOTE_DOWN;
NOTE_UP;
NOTE_RIGHT;
UI_UP;
UI_LEFT;
UI_RIGHT;
UI_DOWN;
RESET;
ACCEPT;
BACK;
PAUSE;
#if CAN_CHEAT
CHEAT;
#end
}
@:enum
abstract Action(String) to String from String
{
var UI_UP = "ui_up";
var UI_LEFT = "ui_left";
var UI_RIGHT = "ui_right";
var UI_DOWN = "ui_down";
var UI_UP_P = "ui_up-press";
var UI_LEFT_P = "ui_left-press";
var UI_RIGHT_P = "ui_right-press";
var UI_DOWN_P = "ui_down-press";
var UI_UP_R = "ui_up-release";
var UI_LEFT_R = "ui_left-release";
var UI_RIGHT_R = "ui_right-release";
var UI_DOWN_R = "ui_down-release";
var NOTE_UP = "note_up";
var NOTE_LEFT = "note_left";
var NOTE_RIGHT = "note_right";
var NOTE_DOWN = "note_down";
var NOTE_UP_P = "note_up-press";
var NOTE_LEFT_P = "note_left-press";
var NOTE_RIGHT_P = "note_right-press";
var NOTE_DOWN_P = "note_down-press";
var NOTE_UP_R = "note_up-release";
var NOTE_LEFT_R = "note_left-release";
var NOTE_RIGHT_R = "note_right-release";
var NOTE_DOWN_R = "note_down-release";
var ACCEPT = "accept";
var BACK = "back";
var PAUSE = "pause";
var RESET = "reset";
#if CAN_CHEAT
var CHEAT = "cheat";
#end
}
enum Device
{
Keys;
Gamepad(id:Int);
}
enum KeyboardScheme
@ -94,177 +92,125 @@ enum KeyboardScheme
*/
class Controls extends FlxActionSet
{
var _up = new FlxActionDigital(Action.UP);
var _left = new FlxActionDigital(Action.LEFT);
var _right = new FlxActionDigital(Action.RIGHT);
var _down = new FlxActionDigital(Action.DOWN);
var _upP = new FlxActionDigital(Action.UP_P);
var _leftP = new FlxActionDigital(Action.LEFT_P);
var _rightP = new FlxActionDigital(Action.RIGHT_P);
var _downP = new FlxActionDigital(Action.DOWN_P);
var _upR = new FlxActionDigital(Action.UP_R);
var _leftR = new FlxActionDigital(Action.LEFT_R);
var _rightR = new FlxActionDigital(Action.RIGHT_R);
var _downR = new FlxActionDigital(Action.DOWN_R);
var _ui_up = new FlxActionDigital(Action.UI_UP);
var _ui_left = new FlxActionDigital(Action.UI_LEFT);
var _ui_right = new FlxActionDigital(Action.UI_RIGHT);
var _ui_down = new FlxActionDigital(Action.UI_DOWN);
var _ui_upP = new FlxActionDigital(Action.UI_UP_P);
var _ui_leftP = new FlxActionDigital(Action.UI_LEFT_P);
var _ui_rightP = new FlxActionDigital(Action.UI_RIGHT_P);
var _ui_downP = new FlxActionDigital(Action.UI_DOWN_P);
var _ui_upR = new FlxActionDigital(Action.UI_UP_R);
var _ui_leftR = new FlxActionDigital(Action.UI_LEFT_R);
var _ui_rightR = new FlxActionDigital(Action.UI_RIGHT_R);
var _ui_downR = new FlxActionDigital(Action.UI_DOWN_R);
var _note_up = new FlxActionDigital(Action.NOTE_UP);
var _note_left = new FlxActionDigital(Action.NOTE_LEFT);
var _note_right = new FlxActionDigital(Action.NOTE_RIGHT);
var _note_down = new FlxActionDigital(Action.NOTE_DOWN);
var _note_upP = new FlxActionDigital(Action.NOTE_UP_P);
var _note_leftP = new FlxActionDigital(Action.NOTE_LEFT_P);
var _note_rightP = new FlxActionDigital(Action.NOTE_RIGHT_P);
var _note_downP = new FlxActionDigital(Action.NOTE_DOWN_P);
var _note_upR = new FlxActionDigital(Action.NOTE_UP_R);
var _note_leftR = new FlxActionDigital(Action.NOTE_LEFT_R);
var _note_rightR = new FlxActionDigital(Action.NOTE_RIGHT_R);
var _note_downR = new FlxActionDigital(Action.NOTE_DOWN_R);
var _accept = new FlxActionDigital(Action.ACCEPT);
var _back = new FlxActionDigital(Action.BACK);
var _pause = new FlxActionDigital(Action.PAUSE);
var _reset = new FlxActionDigital(Action.RESET);
#if CAN_CHEAT
var _cheat = new FlxActionDigital(Action.CHEAT);
#if (haxe >= "4.0.0")
var byName:Map<String, FlxActionDigital> = [];
#else
var byName:Map<String, FlxActionDigital> = new Map<String, FlxActionDigital>();
#end
var byName:Map<String, FlxActionDigital> = new Map<String, FlxActionDigital>();
public var gamepadsAdded:Array<Int> = [];
public var keyboardScheme = KeyboardScheme.None;
public var UP(get, never):Bool;
public var UI_UP (get, never):Bool; inline function get_UI_UP () return _ui_up .check();
public var UI_LEFT (get, never):Bool; inline function get_UI_LEFT () return _ui_left .check();
public var UI_RIGHT(get, never):Bool; inline function get_UI_RIGHT() return _ui_right.check();
public var UI_DOWN (get, never):Bool; inline function get_UI_DOWN () return _ui_down .check();
inline function get_UP()
return _up.check();
public var UI_UP_P (get, never):Bool; inline function get_UI_UP_P () return _ui_upP .check();
public var UI_LEFT_P (get, never):Bool; inline function get_UI_LEFT_P () return _ui_leftP .check();
public var UI_RIGHT_P(get, never):Bool; inline function get_UI_RIGHT_P() return _ui_rightP.check();
public var UI_DOWN_P (get, never):Bool; inline function get_UI_DOWN_P () return _ui_downP .check();
public var LEFT(get, never):Bool;
public var UI_UP_R (get, never):Bool; inline function get_UI_UP_R () return _ui_upR .check();
public var UI_LEFT_R (get, never):Bool; inline function get_UI_LEFT_R () return _ui_leftR .check();
public var UI_RIGHT_R(get, never):Bool; inline function get_UI_RIGHT_R() return _ui_rightR.check();
public var UI_DOWN_R (get, never):Bool; inline function get_UI_DOWN_R () return _ui_downR .check();
public var NOTE_UP (get, never):Bool; inline function get_NOTE_UP () return _note_up .check();
public var NOTE_LEFT (get, never):Bool; inline function get_NOTE_LEFT () return _note_left .check();
public var NOTE_RIGHT(get, never):Bool; inline function get_NOTE_RIGHT() return _note_right.check();
public var NOTE_DOWN (get, never):Bool; inline function get_NOTE_DOWN () return _note_down .check();
inline function get_LEFT()
return _left.check();
public var NOTE_UP_P (get, never):Bool; inline function get_NOTE_UP_P () return _note_upP .check();
public var NOTE_LEFT_P (get, never):Bool; inline function get_NOTE_LEFT_P () return _note_leftP .check();
public var NOTE_RIGHT_P(get, never):Bool; inline function get_NOTE_RIGHT_P() return _note_rightP.check();
public var NOTE_DOWN_P (get, never):Bool; inline function get_NOTE_DOWN_P () return _note_downP .check();
public var RIGHT(get, never):Bool;
public var NOTE_UP_R (get, never):Bool; inline function get_NOTE_UP_R () return _note_upR .check();
public var NOTE_LEFT_R (get, never):Bool; inline function get_NOTE_LEFT_R () return _note_leftR .check();
public var NOTE_RIGHT_R(get, never):Bool; inline function get_NOTE_RIGHT_R() return _note_rightR.check();
public var NOTE_DOWN_R (get, never):Bool; inline function get_NOTE_DOWN_R () return _note_downR .check();
inline function get_RIGHT()
return _right.check();
public var DOWN(get, never):Bool;
inline function get_DOWN()
return _down.check();
public var UP_P(get, never):Bool;
inline function get_UP_P()
return _upP.check();
public var LEFT_P(get, never):Bool;
inline function get_LEFT_P()
return _leftP.check();
public var RIGHT_P(get, never):Bool;
inline function get_RIGHT_P()
return _rightP.check();
public var DOWN_P(get, never):Bool;
inline function get_DOWN_P()
return _downP.check();
public var UP_R(get, never):Bool;
inline function get_UP_R()
return _upR.check();
public var LEFT_R(get, never):Bool;
inline function get_LEFT_R()
return _leftR.check();
public var RIGHT_R(get, never):Bool;
inline function get_RIGHT_R()
return _rightR.check();
public var DOWN_R(get, never):Bool;
inline function get_DOWN_R()
return _downR.check();
public var ACCEPT(get, never):Bool;
inline function get_ACCEPT()
return _accept.check();
public var BACK(get, never):Bool;
inline function get_BACK()
return _back.check();
public var PAUSE(get, never):Bool;
inline function get_PAUSE()
return _pause.check();
public var RESET(get, never):Bool;
inline function get_RESET()
return _reset.check();
public var CHEAT(get, never):Bool;
inline function get_CHEAT()
return _cheat.check();
#if (haxe >= "4.0.0")
public function new(name, scheme = None)
{
super(name);
add(_up);
add(_left);
add(_right);
add(_down);
add(_upP);
add(_leftP);
add(_rightP);
add(_downP);
add(_upR);
add(_leftR);
add(_rightR);
add(_downR);
add(_accept);
add(_back);
add(_pause);
add(_reset);
add(_cheat);
for (action in digitalActions)
byName[action.name] = action;
setKeyboardScheme(scheme, false);
}
#else
public var ACCEPT(get, never):Bool; inline function get_ACCEPT() return _accept.check();
public var BACK (get, never):Bool; inline function get_BACK () return _back .check();
public var PAUSE (get, never):Bool; inline function get_PAUSE () return _pause .check();
public var RESET (get, never):Bool; inline function get_RESET () return _reset .check();
#if CAN_CHEAT
public var CHEAT (get, never):Bool; inline function get_CHEAT () return _cheat.check ();
#end
public function new(name, scheme:KeyboardScheme = null)
{
super(name);
add(_up);
add(_left);
add(_right);
add(_down);
add(_upP);
add(_leftP);
add(_rightP);
add(_downP);
add(_upR);
add(_leftR);
add(_rightR);
add(_downR);
add(_ui_up);
add(_ui_left);
add(_ui_right);
add(_ui_down);
add(_ui_upP);
add(_ui_leftP);
add(_ui_rightP);
add(_ui_downP);
add(_ui_upR);
add(_ui_leftR);
add(_ui_rightR);
add(_ui_downR);
add(_note_up);
add(_note_left);
add(_note_right);
add(_note_down);
add(_note_upP);
add(_note_leftP);
add(_note_rightP);
add(_note_downP);
add(_note_upR);
add(_note_leftR);
add(_note_rightR);
add(_note_downR);
add(_accept);
add(_back);
add(_pause);
add(_reset);
#if CAN_CHEAT
add(_cheat);
#end
for (action in digitalActions)
byName[action.name] = action;
if (scheme == null)
scheme = None;
setKeyboardScheme(scheme, false);
}
#end
override function update()
{
@ -301,15 +247,21 @@ class Controls extends FlxActionSet
{
return switch (control)
{
case UP: _up;
case DOWN: _down;
case LEFT: _left;
case RIGHT: _right;
case UI_UP: _ui_up;
case UI_DOWN: _ui_down;
case UI_LEFT: _ui_left;
case UI_RIGHT: _ui_right;
case NOTE_UP: _note_up;
case NOTE_DOWN: _note_down;
case NOTE_LEFT: _note_left;
case NOTE_RIGHT: _note_right;
case ACCEPT: _accept;
case BACK: _back;
case PAUSE: _pause;
case RESET: _reset;
#if CAN_CHEAT
case CHEAT: _cheat;
#end
}
}
@ -329,22 +281,38 @@ class Controls extends FlxActionSet
{
switch (control)
{
case UP:
func(_up, PRESSED);
func(_upP, JUST_PRESSED);
func(_upR, JUST_RELEASED);
case LEFT:
func(_left, PRESSED);
func(_leftP, JUST_PRESSED);
func(_leftR, JUST_RELEASED);
case RIGHT:
func(_right, PRESSED);
func(_rightP, JUST_PRESSED);
func(_rightR, JUST_RELEASED);
case DOWN:
func(_down, PRESSED);
func(_downP, JUST_PRESSED);
func(_downR, JUST_RELEASED);
case UI_UP:
func(_ui_up, PRESSED);
func(_ui_upP, JUST_PRESSED);
func(_ui_upR, JUST_RELEASED);
case UI_LEFT:
func(_ui_left, PRESSED);
func(_ui_leftP, JUST_PRESSED);
func(_ui_leftR, JUST_RELEASED);
case UI_RIGHT:
func(_ui_right, PRESSED);
func(_ui_rightP, JUST_PRESSED);
func(_ui_rightR, JUST_RELEASED);
case UI_DOWN:
func(_ui_down, PRESSED);
func(_ui_downP, JUST_PRESSED);
func(_ui_downR, JUST_RELEASED);
case NOTE_UP:
func(_note_up, PRESSED);
func(_note_upP, JUST_PRESSED);
func(_note_upR, JUST_RELEASED);
case NOTE_LEFT:
func(_note_left, PRESSED);
func(_note_leftP, JUST_PRESSED);
func(_note_leftR, JUST_RELEASED);
case NOTE_RIGHT:
func(_note_right, PRESSED);
func(_note_rightP, JUST_PRESSED);
func(_note_rightR, JUST_RELEASED);
case NOTE_DOWN:
func(_note_down, PRESSED);
func(_note_downP, JUST_PRESSED);
func(_note_downR, JUST_RELEASED);
case ACCEPT:
func(_accept, JUST_PRESSED);
case BACK:
@ -353,12 +321,14 @@ class Controls extends FlxActionSet
func(_pause, JUST_PRESSED);
case RESET:
func(_reset, JUST_PRESSED);
#if CAN_CHEAT
case CHEAT:
func(_cheat, JUST_PRESSED);
#end
}
}
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)
return;
@ -366,31 +336,41 @@ class Controls extends FlxActionSet
switch (device)
{
case Keys:
if (toRemove != null)
unbindKeys(control, [toRemove]);
if (toAdd != null)
bindKeys(control, [toAdd]);
forEachBound(control, function(action, _) replaceKey(action, toAdd, toRemove));
case Gamepad(id):
if (toRemove != null)
unbindButtons(control, id, [toRemove]);
if (toAdd != null)
bindButtons(control, id, [toAdd]);
forEachBound(control, function(action, _) replaceButton(action, id, toAdd, toRemove));
}
}
function replaceKey(action:FlxActionDigital, toAdd:Int, toRemove:Int)
{
for (i in 0...action.inputs.length)
{
var input = action.inputs[i];
if (input.device == KEYBOARD && input.inputID == toRemove)
{
@:privateAccess
action.inputs[i].inputID = toAdd;
}
}
}
function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:Int, toRemove:Int)
{
for (i in 0...action.inputs.length)
{
var input = action.inputs[i];
if (isGamepad(input, deviceID) && input.inputID == toRemove)
{
@:privateAccess
action.inputs[i].inputID = toAdd;
}
}
}
public function copyFrom(controls:Controls, ?device:Device)
{
#if (haxe >= "4.0.0")
for (name => action in controls.byName)
{
for (input in action.inputs)
{
if (device == null || isDevice(input, device))
byName[name].add(cast input);
}
}
#else
for (name in controls.byName.keys())
{
var action = controls.byName[name];
@ -400,21 +380,14 @@ class Controls extends FlxActionSet
byName[name].add(cast input);
}
}
#end
switch (device)
{
case null:
// add all
#if (haxe >= "4.0.0")
for (gamepad in controls.gamepadsAdded)
if (!gamepadsAdded.contains(gamepad))
gamepadsAdded.push(gamepad);
#else
for (gamepad in controls.gamepadsAdded)
if (gamepadsAdded.indexOf(gamepad) == -1)
gamepadsAdded.push(gamepad);
#end
mergeKeyboardScheme(controls.keyboardScheme);
@ -450,11 +423,7 @@ class Controls extends FlxActionSet
*/
public function bindKeys(control:Control, keys:Array<FlxKey>)
{
#if (haxe >= "4.0.0")
inline forEachBound(control, (action, state) -> addKeys(action, keys, state));
#else
forEachBound(control, function(action, state) addKeys(action, keys, state));
#end
}
/**
@ -463,11 +432,7 @@ class Controls extends FlxActionSet
*/
public function unbindKeys(control:Control, keys:Array<FlxKey>)
{
#if (haxe >= "4.0.0")
inline forEachBound(control, (action, _) -> removeKeys(action, keys));
#else
forEachBound(control, function(action, _) removeKeys(action, keys));
#end
}
inline static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState)
@ -494,65 +459,43 @@ class Controls extends FlxActionSet
keyboardScheme = scheme;
#if (haxe >= "4.0.0")
switch (scheme)
{
case Solo:
inline bindKeys(Control.UP, [W, FlxKey.UP]);
inline bindKeys(Control.DOWN, [S, FlxKey.DOWN]);
inline bindKeys(Control.LEFT, [A, FlxKey.LEFT]);
inline bindKeys(Control.RIGHT, [D, FlxKey.RIGHT]);
inline bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]);
inline bindKeys(Control.BACK, [BACKSPACE, ESCAPE]);
inline bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]);
inline bindKeys(Control.RESET, [R]);
case Duo(true):
inline bindKeys(Control.UP, [W]);
inline bindKeys(Control.DOWN, [S]);
inline bindKeys(Control.LEFT, [A]);
inline bindKeys(Control.RIGHT, [D]);
inline bindKeys(Control.ACCEPT, [G, Z]);
inline bindKeys(Control.BACK, [H, X]);
inline bindKeys(Control.PAUSE, [ONE]);
inline bindKeys(Control.RESET, [R]);
case Duo(false):
inline bindKeys(Control.UP, [FlxKey.UP]);
inline bindKeys(Control.DOWN, [FlxKey.DOWN]);
inline bindKeys(Control.LEFT, [FlxKey.LEFT]);
inline bindKeys(Control.RIGHT, [FlxKey.RIGHT]);
inline bindKeys(Control.ACCEPT, [O]);
inline bindKeys(Control.BACK, [P]);
inline bindKeys(Control.PAUSE, [ENTER]);
inline bindKeys(Control.RESET, [BACKSPACE]);
case None: // nothing
case Custom: // nothing
}
#else
switch (scheme)
{
case Solo:
bindKeys(Control.UP, [W, FlxKey.UP]);
bindKeys(Control.DOWN, [S, FlxKey.DOWN]);
bindKeys(Control.LEFT, [A, FlxKey.LEFT]);
bindKeys(Control.RIGHT, [D, FlxKey.RIGHT]);
bindKeys(Control.UI_UP, [W, FlxKey.UP]);
bindKeys(Control.UI_DOWN, [S, FlxKey.DOWN]);
bindKeys(Control.UI_LEFT, [A, FlxKey.LEFT]);
bindKeys(Control.UI_RIGHT, [D, FlxKey.RIGHT]);
bindKeys(Control.NOTE_UP, [W, FlxKey.UP]);
bindKeys(Control.NOTE_DOWN, [S, FlxKey.DOWN]);
bindKeys(Control.NOTE_LEFT, [A, FlxKey.LEFT]);
bindKeys(Control.NOTE_RIGHT, [D, FlxKey.RIGHT]);
bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]);
bindKeys(Control.BACK, [BACKSPACE, ESCAPE]);
bindKeys(Control.BACK, [X, BACKSPACE, ESCAPE]);
bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]);
bindKeys(Control.RESET, [R]);
case Duo(true):
bindKeys(Control.UP, [W]);
bindKeys(Control.DOWN, [S]);
bindKeys(Control.LEFT, [A]);
bindKeys(Control.RIGHT, [D]);
bindKeys(Control.UI_UP, [W]);
bindKeys(Control.UI_DOWN, [S]);
bindKeys(Control.UI_LEFT, [A]);
bindKeys(Control.UI_RIGHT, [D]);
bindKeys(Control.NOTE_UP, [W]);
bindKeys(Control.NOTE_DOWN, [S]);
bindKeys(Control.NOTE_LEFT, [A]);
bindKeys(Control.NOTE_RIGHT, [D]);
bindKeys(Control.ACCEPT, [G, Z]);
bindKeys(Control.BACK, [H, X]);
bindKeys(Control.PAUSE, [ONE]);
bindKeys(Control.RESET, [R]);
case Duo(false):
bindKeys(Control.UP, [FlxKey.UP]);
bindKeys(Control.DOWN, [FlxKey.DOWN]);
bindKeys(Control.LEFT, [FlxKey.LEFT]);
bindKeys(Control.RIGHT, [FlxKey.RIGHT]);
bindKeys(Control.UI_UP, [FlxKey.UP]);
bindKeys(Control.UI_DOWN, [FlxKey.DOWN]);
bindKeys(Control.UI_LEFT, [FlxKey.LEFT]);
bindKeys(Control.UI_RIGHT, [FlxKey.RIGHT]);
bindKeys(Control.NOTE_UP, [FlxKey.UP]);
bindKeys(Control.NOTE_DOWN, [FlxKey.DOWN]);
bindKeys(Control.NOTE_LEFT, [FlxKey.LEFT]);
bindKeys(Control.NOTE_RIGHT, [FlxKey.RIGHT]);
bindKeys(Control.ACCEPT, [O]);
bindKeys(Control.BACK, [P]);
bindKeys(Control.PAUSE, [ENTER]);
@ -560,7 +503,6 @@ class Controls extends FlxActionSet
case None: // nothing
case Custom: // nothing
}
#end
}
function removeKeyboard()
@ -577,30 +519,19 @@ class Controls extends FlxActionSet
}
}
public function addGamepad(id:Int, ?buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
public function addGamepadWithSaveData(id:Int, ?padData:Dynamic):Void
{
gamepadsAdded.push(id);
#if (haxe >= "4.0.0")
for (control => buttons in buttonMap)
inline bindButtons(control, id, buttons);
#else
for (control in buttonMap.keys())
bindButtons(control, id, buttonMap[control]);
#end
fromSaveData(padData, Gamepad(id));
}
inline function addGamepadLiteral(id:Int, ?buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
{
gamepadsAdded.push(id);
#if (haxe >= "4.0.0")
for (control => buttons in buttonMap)
inline bindButtons(control, id, buttons);
#else
for (control in buttonMap.keys())
bindButtons(control, id, buttonMap[control]);
#end
}
public function removeGamepad(deviceID:Int = FlxInputDeviceID.ALL):Void
@ -611,7 +542,7 @@ class Controls extends FlxActionSet
while (i-- > 0)
{
var input = action.inputs[i];
if (input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID))
if (isGamepad(input, deviceID))
action.remove(input);
}
}
@ -621,32 +552,25 @@ class Controls extends FlxActionSet
public function addDefaultGamepad(id):Void
{
#if !switch
addGamepadLiteral(id, [
Control.ACCEPT => [A],
Control.BACK => [B],
Control.UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP],
Control.DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN],
Control.LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT],
Control.RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT],
Control.ACCEPT => [#if switch B #else A #end],
Control.BACK => [#if switch A #else B #end, FlxGamepadInputID.BACK],
Control.UI_UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP],
Control.UI_DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN],
Control.UI_LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT],
Control.UI_RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT],
// don't swap A/B or X/Y for switch on these. A is always the bottom face button
Control.NOTE_UP => [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP],
Control.NOTE_DOWN => [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN],
Control.NOTE_LEFT => [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT],
Control.NOTE_RIGHT => [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT],
Control.PAUSE => [START],
Control.RESET => [Y]
#if CAN_CHEAT
,Control.CHEAT => [X]
#end
]);
#else
addGamepadLiteral(id, [
//Swap A and B for switch
Control.ACCEPT => [B],
Control.BACK => [A],
Control.UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP],
Control.DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN],
Control.LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT],
Control.RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT],
Control.PAUSE => [START],
//Swap Y and X for switch
Control.RESET => [Y],
Control.CHEAT => [X]
]);
#end
}
/**
@ -655,11 +579,7 @@ class Controls extends FlxActionSet
*/
public function bindButtons(control:Control, id, buttons)
{
#if (haxe >= "4.0.0")
inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id));
#else
forEachBound(control, function(action, state) addButtons(action, buttons, state, id));
#end
}
/**
@ -668,11 +588,7 @@ class Controls extends FlxActionSet
*/
public function unbindButtons(control:Control, gamepadID:Int, buttons)
{
#if (haxe >= "4.0.0")
inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons));
#else
forEachBound(control, function(action, _) removeButtons(action, gamepadID, buttons));
#end
}
inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id)
@ -708,7 +624,7 @@ class Controls extends FlxActionSet
case Gamepad(id):
for (input in getActionFromControl(control).inputs)
{
if (input.deviceID == id)
if (isGamepad(input, id))
list.push(input.inputID);
}
}
@ -726,6 +642,37 @@ class Controls extends FlxActionSet
}
}
public function fromSaveData(data:Dynamic, device:Device)
{
for (control in Control.createAll())
{
var inputs:Array<Int> = Reflect.field(data, control.getName());
if (inputs != null)
{
switch(device)
{
case Keys: bindKeys(control, inputs.copy());
case Gamepad(id): bindButtons(control, id, inputs.copy());
}
}
}
}
public function createSaveData(device:Device):Dynamic
{
var isEmpty = true;
var data = {};
for (control in Control.createAll())
{
var inputs = getInputsFor(control, device);
isEmpty = isEmpty && inputs.length == 0;
Reflect.setField(data, control.getName(), inputs);
}
return isEmpty ? null : data;
}
static function isDevice(input:FlxActionInput, device:Device)
{
return switch device
@ -740,3 +687,6 @@ class Controls extends FlxActionSet
return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID);
}
}
typedef SaveInputLists = {?keys:Array<Int>, ?pad:Array<Int>};

View file

@ -201,8 +201,8 @@ class FreeplayState extends MusicBeatState
scoreText.text = "PERSONAL BEST:" + lerpScore;
var upP = controls.UP_P;
var downP = controls.DOWN_P;
var upP = controls.UI_UP_P;
var downP = controls.UI_DOWN_P;
var accepted = controls.ACCEPT;
if (upP)
@ -214,9 +214,9 @@ class FreeplayState extends MusicBeatState
changeSelection(1);
}
if (controls.LEFT_P)
if (controls.UI_LEFT_P)
changeDiff(-1);
if (controls.RIGHT_P)
if (controls.UI_RIGHT_P)
changeDiff(1);
if (controls.BACK)
@ -246,9 +246,7 @@ class FreeplayState extends MusicBeatState
if (curDifficulty > 2)
curDifficulty = 0;
#if !switch
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
#end
PlayState.storyDifficulty = curDifficulty;
@ -259,9 +257,7 @@ class FreeplayState extends MusicBeatState
function changeSelection(change:Int = 0)
{
#if !switch
NGio.logEvent('Fresh');
#end
// NGio.logEvent('Fresh');
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
@ -275,10 +271,8 @@ class FreeplayState extends MusicBeatState
// selector.y = (70 * curSelected) + 30;
#if !switch
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
// lerpScore = 0;
#end
#if PRELOAD_ALL
FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName), 0);

View file

@ -52,7 +52,7 @@ class GitarooPause extends MusicBeatState
override function update(elapsed:Float)
{
if (controls.LEFT_P || controls.RIGHT_P)
if (controls.UI_LEFT_P || controls.UI_RIGHT_P)
changeThing();
if (controls.ACCEPT)

View file

@ -13,51 +13,55 @@ class Highscore
public static function saveScore(song:String, score:Int = 0, ?diff:Int = 0):Void
{
var daSong:String = formatSong(song, diff);
var formattedSong:String = formatSong(song, diff);
#if !switch
#if newgrounds
NGio.postScore(score, song);
#end
if (songScores.exists(daSong))
if (songScores.exists(formattedSong))
{
if (songScores.get(daSong) < score)
setScore(daSong, score);
if (songScores.get(formattedSong) < score)
setScore(formattedSong, score);
}
else
setScore(daSong, score);
setScore(formattedSong, score);
}
public static function saveWeekScore(week:Int = 1, score:Int = 0, ?diff:Int = 0):Void
{
#if !switch
#if newgrounds
NGio.postScore(score, "Week " + week);
#end
var formattedSong:String = formatSong('week' + week, diff);
var daWeek:String = formatSong('week' + week, diff);
if (songScores.exists(daWeek))
if (songScores.exists(formattedSong))
{
if (songScores.get(daWeek) < score)
setScore(daWeek, score);
if (songScores.get(formattedSong) < score)
setScore(formattedSong, score);
}
else
setScore(daWeek, score);
setScore(formattedSong, score);
}
/**
* YOU SHOULD FORMAT SONG WITH formatSong() BEFORE TOSSING IN SONG VARIABLE
*/
static function setScore(song:String, score:Int):Void
static function setScore(formattedSong:String, score:Int):Void
{
/** GeoKureli
* References to Highscore were wrapped in `#if !switch` blocks. I wasn't sure if this
* is because switch doesn't use NGio, or because switch has a different saving method.
* I moved the compiler flag here, rather than using it everywhere else.
*/
#if !switch
// Reminder that I don't need to format this song, it should come formatted!
songScores.set(song, score);
songScores.set(formattedSong, score);
FlxG.save.data.songScores = songScores;
FlxG.save.flush();
#end
}
public static function formatSong(song:String, diff:Int):String

220
source/InputFormatter.hx Normal file
View file

@ -0,0 +1,220 @@
package ;
import Controls;
import flixel.FlxG;
import flixel.input.gamepad.FlxGamepad;
import flixel.input.gamepad.FlxGamepadInputID;
import flixel.input.keyboard.FlxKey;
using flixel.util.FlxStringUtil;
class InputFormatter
{
static public function format(id:Int, device:Device):String
{
return switch (device)
{
case Keys: getKeyName(id);
case Gamepad(gamepadID): getButtonName(id, FlxG.gamepads.getByID(gamepadID));
}
}
static public function getKeyName(id:Int):String
{
return switch(id)
{
case ZERO : "0";
case ONE : "1";
case TWO : "2";
case THREE : "3";
case FOUR : "4";
case FIVE : "5";
case SIX : "6";
case SEVEN : "7";
case EIGHT : "8";
case NINE : "9";
case PAGEUP : "PgUp";
case PAGEDOWN : "PgDown";
// case HOME : "Hm";
// case END : "End";
// case INSERT : "Ins";
// case ESCAPE : "Esc";
// case MINUS : "-";
// case PLUS : "+";
// case DELETE : "Del";
case BACKSPACE : "BckSpc";
case LBRACKET : "[";
case RBRACKET : "]";
case BACKSLASH : "\\";
case CAPSLOCK : "Caps";
case SEMICOLON : ";";
case QUOTE : "'";
// case ENTER : "Ent";
// case SHIFT : "Shf";
case COMMA : ",";
case PERIOD : ".";
case SLASH : "/";
case GRAVEACCENT : "`";
case CONTROL : "Ctrl";
case ALT : "Alt";
// case SPACE : "Spc";
// case UP : "Up";
// case DOWN : "Dn";
// case LEFT : "Lf";
// case RIGHT : "Rt";
// case TAB : "Tab";
case PRINTSCREEN : "PrtScrn";
case NUMPADZERO : "#0";
case NUMPADONE : "#1";
case NUMPADTWO : "#2";
case NUMPADTHREE : "#3";
case NUMPADFOUR : "#4";
case NUMPADFIVE : "#5";
case NUMPADSIX : "#6";
case NUMPADSEVEN : "#7";
case NUMPADEIGHT : "#8";
case NUMPADNINE : "#9";
case NUMPADMINUS : "#-";
case NUMPADPLUS : "#+";
case NUMPADPERIOD : "#.";
case NUMPADMULTIPLY: "#*";
default: titleCase(FlxKey.toStringMap[id]);
}
}
static var dirReg = ~/^(l|r).?-(left|right|down|up)$/;
inline static public function getButtonName(id:Int, gamepad:FlxGamepad):String
{
return switch(gamepad.getInputLabel(id))
{
// case null | "": shortenButtonName(FlxGamepadInputID.toStringMap[id]);
case label: shortenButtonName(label);
}
}
static function shortenButtonName(name:String)
{
return switch (name == null ? "" : name.toLowerCase())
{
case "": "[?]";
// case "square" : "[]";
// case "circle" : "()";
// case "triangle": "/\\";
// case "plus" : "+";
// case "minus" : "-";
// case "home" : "Hm";
// case "guide" : "Gd";
// case "back" : "Bk";
// case "select" : "Bk";
// case "start" : "St";
// case "left" : "Lf";
// case "right" : "Rt";
// case "down" : "Dn";
// case "up" : "Up";
case dir if (dirReg.match(dir)):
dirReg.matched(1).toUpperCase() + " " + titleCase(dirReg.matched(2));
case label: titleCase(label);
}
}
inline static function titleCaseTrim(str:String, length = 8)
{
return str.charAt(0).toUpperCase() + str.substr(1, length - 1).toLowerCase();
}
inline static function titleCase(str:String)
{
return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase();
}
inline static public function parsePadName(name:String):ControllerName
{
return ControllerName.parseName(name);
}
inline static public function getPadName(gamepad:FlxGamepad):ControllerName
{
return ControllerName.getName(gamepad);
}
inline static public function getPadNameById(id:Int):ControllerName
{
return ControllerName.getNameById(id);
}
}
@:forward
@:enum abstract ControllerName(String) from String to String
{
var OUYA = "Ouya" ;
var PS4 = "PS4" ;
var LOGI = "Logi" ;
var XBOX = "XBox" ;
var XINPUT = "XInput" ;
var WII = "Wii" ;
var PRO_CON = "Pro_Con" ;
var JOYCONS = "Joycons" ;
var JOYCON_L = "Joycon_L";
var JOYCON_R = "Joycon_R";
var MFI = "MFI" ;
var PAD = "Pad" ;
static public function getAssetByDevice(device:Device):String
{
return switch (device)
{
case Keys: getAsset(null);
case Gamepad(id): getAsset(FlxG.gamepads.getByID(id));
}
}
static public function getAsset(gamepad:FlxGamepad):String
{
if (gamepad == null)
return 'assets/images/ui/devices/Keys.png';
final name = parseName(gamepad.name);
var path = 'assets/images/ui/devices/$name.png';
if (openfl.utils.Assets.exists(path))
return path;
return 'assets/images/ui/devices/Pad.png';
}
inline static public function getNameById(id:Int):ControllerName return getName(FlxG.gamepads.getByID(id));
inline static public function getName(gamepad:FlxGamepad):ControllerName return parseName(gamepad.name);
static public function parseName(name:String):ControllerName
{
name = name.toLowerCase().remove("-").remove("_");
return
if (name.contains("ouya"))
OUYA;
else if (name.contains("wireless controller") || name.contains("ps4"))
PS4;
else if (name.contains("logitech"))
LOGI;
else if (name.contains("xbox"))
XBOX
else if (name.contains("xinput"))
XINPUT;
else if (name.contains("nintendo rvlcnt01tr") || name.contains("nintendo rvlcnt01"))
WII;
else if (name.contains("mayflash wiimote pc adapter"))
WII;
else if (name.contains("pro controller"))
PRO_CON;
else if (name.contains("joycon l+r"))
JOYCONS;
else if (name.contains("joycon (l)"))
JOYCON_L;
else if (name.contains("joycon (r)"))
JOYCON_R;
else if (name.contains("mfi"))
MFI;
else
PAD;
}
}

View file

@ -75,7 +75,7 @@ class LoadingState extends MusicBeatState
if (!Assets.cache.hasSound(path))
{
var library = Assets.getLibrary("songs");
final symbolPath = path.split(":").pop();
var symbolPath = path.split(":").pop();
// @:privateAccess
// library.types.set(symbolPath, SOUND);
// @:privateAccess

View file

@ -1,11 +1,12 @@
package;
#if desktop
import Discord.DiscordClient;
#end
import NGio;
import flixel.ui.FlxButton;
import flixel.FlxG;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.addons.transition.FlxTransitionableState;
import flixel.effects.FlxFlicker;
import flixel.graphics.frames.FlxAtlasFrames;
@ -14,22 +15,28 @@ import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import io.newgrounds.NG;
import flixel.util.FlxTimer;
import lime.app.Application;
#if desktop
import Discord.DiscordClient;
#end
#if newgrounds
import io.newgrounds.NG;
import ui.NgPrompt;
#end
import ui.AtlasMenuList;
import ui.MenuList;
import ui.OptionsState;
import ui.Prompt;
using StringTools;
class MainMenuState extends MusicBeatState
{
var curSelected:Int = 0;
var menuItems:FlxTypedGroup<FlxSprite>;
#if !switch
var optionShit:Array<String> = ['story mode', 'freeplay', 'donate', 'options'];
#else
var optionShit:Array<String> = ['story mode', 'freeplay'];
#end
var menuItems:MainMenuList;
var magenta:FlxSprite;
var camFollow:FlxObject;
@ -51,10 +58,10 @@ class MainMenuState extends MusicBeatState
persistentUpdate = persistentDraw = true;
var bg:FlxSprite = new FlxSprite(-80).loadGraphic(Paths.image('menuBG'));
var bg:FlxSprite = new FlxSprite(Paths.image('menuBG'));
bg.scrollFactor.x = 0;
bg.scrollFactor.y = 0.17;
bg.setGraphicSize(Std.int(bg.width * 1.1));
bg.setGraphicSize(Std.int(bg.width * 1.2));
bg.updateHitbox();
bg.screenCenter();
bg.antialiasing = true;
@ -63,38 +70,57 @@ class MainMenuState extends MusicBeatState
camFollow = new FlxObject(0, 0, 1, 1);
add(camFollow);
magenta = new FlxSprite(-80).loadGraphic(Paths.image('menuDesat'));
magenta.scrollFactor.x = 0;
magenta.scrollFactor.y = 0.17;
magenta.setGraphicSize(Std.int(magenta.width * 1.1));
magenta = new FlxSprite(Paths.image('menuDesat'));
magenta.scrollFactor.x = bg.scrollFactor.x;
magenta.scrollFactor.y = bg.scrollFactor.y;
magenta.setGraphicSize(Std.int(bg.width));
magenta.updateHitbox();
magenta.screenCenter();
magenta.x = bg.x;
magenta.y = bg.y;
magenta.visible = false;
magenta.antialiasing = true;
magenta.color = 0xFFfd719b;
add(magenta);
// magenta.scrollFactor.set();
menuItems = new FlxTypedGroup<FlxSprite>();
menuItems = new MainMenuList();
add(menuItems);
var tex = Paths.getSparrowAtlas('FNF_main_menu_assets');
for (i in 0...optionShit.length)
menuItems.onChange.add(onMenuItemChange);
menuItems.onAcceptPress.add(function(_)
{
var menuItem:FlxSprite = new FlxSprite(0, 60 + (i * 160));
menuItem.frames = tex;
menuItem.animation.addByPrefix('idle', optionShit[i] + " basic", 24);
menuItem.animation.addByPrefix('selected', optionShit[i] + " white", 24);
menuItem.animation.play('idle');
menuItem.ID = i;
menuItem.screenCenter(X);
menuItems.add(menuItem);
menuItem.scrollFactor.set();
menuItem.antialiasing = true;
FlxFlicker.flicker(magenta, 1.1, 0.15, false, true);
});
menuItems.enabled = false;// disable for intro
menuItems.createItem('story mode', function () startExitState(new StoryMenuState()));
menuItems.createItem('freeplay', function () startExitState(new FreeplayState()));
// addMenuItem('options', function () startExitState(new OptionMenu()));
#if CAN_OPEN_LINKS
var hasPopupBlocker = #if web true #else false #end;
menuItems.createItem('donate', selectDonate, hasPopupBlocker);
#end
menuItems.createItem('options', function () startExitState(new OptionsState()));
// #if newgrounds
// if (NGio.isLoggedIn)
// menuItems.createItem("logout", selectLogout);
// else
// menuItems.createItem("login", selectLogin);
// #end
// center vertically
var spacing = 160;
var top = (FlxG.height - (spacing * (menuItems.length - 1))) / 2;
for (i in 0...menuItems.length)
{
var menuItem = menuItems.members[i];
menuItem.x = FlxG.width / 2;
menuItem.y = top + spacing * i;
}
FlxG.camera.follow(camFollow, null, 0.06);
// FlxG.camera.setScrollBounds(bg.x, bg.x + bg.width, bg.y, bg.y + bg.height * 1.2);
var versionShit:FlxText = new FlxText(5, FlxG.height - 18, 0, "v" + Application.current.meta.get('version'), 12);
versionShit.scrollFactor.set();
@ -103,12 +129,113 @@ class MainMenuState extends MusicBeatState
// NG.core.calls.event.logEvent('swag').send();
changeItem();
super.create();
}
var selectedSomethin:Bool = false;
override function finishTransIn()
{
super.finishTransIn();
menuItems.enabled = true;
// #if newgrounds
// if (NGio.savedSessionFailed)
// showSavedSessionFailed();
// #end
}
function onMenuItemChange(selected:MenuItem)
{
camFollow.setPosition(selected.getGraphicMidpoint().x, selected.getGraphicMidpoint().y);
}
#if CAN_OPEN_LINKS
function selectDonate()
{
#if linux
Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]);
#else
FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
#end
}
#end
#if newgrounds
function selectLogin()
{
openNgPrompt(NgPrompt.showLogin());
}
function selectLogout()
{
openNgPrompt(NgPrompt.showLogout());
}
function showSavedSessionFailed()
{
openNgPrompt(NgPrompt.showSavedSessionFailed());
}
/**
* Calls openPrompt and redraws the login/logout button
* @param prompt
* @param onClose
*/
public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void)
{
var onPromptClose = checkLoginStatus;
if (onClose != null)
{
onPromptClose = function ()
{
checkLoginStatus();
onClose();
}
}
openPrompt(prompt, onPromptClose);
}
function checkLoginStatus()
{
var prevLoggedIn = menuItems.has("logout");
if (prevLoggedIn && !NGio.isLoggedIn)
menuItems.resetItem("login", "logout", selectLogout);
else if (!prevLoggedIn && NGio.isLoggedIn)
menuItems.resetItem("logout", "login", selectLogin);
}
#end
public function openPrompt(prompt:Prompt, onClose:Void->Void)
{
menuItems.enabled = false;
prompt.closeCallback = function ()
{
menuItems.enabled = true;
if (onClose != null)
onClose();
}
openSubState(prompt);
}
function startExitState(state:FlxState)
{
var duration = 0.4;
menuItems.forEach(function(item)
{
if (menuItems.selectedIndex != item.ID)
{
FlxTween.tween(item, {alpha: 0}, duration, { ease: FlxEase.quadOut });
}
else
{
item.visible = false;
}
});
new FlxTimer().start(duration, function(_) FlxG.switchState(state));
}
override function update(elapsed:Float)
{
@ -117,124 +244,52 @@ class MainMenuState extends MusicBeatState
FlxG.sound.music.volume += 0.5 * FlxG.elapsed;
}
if (!selectedSomethin)
{
if (controls.UP_P)
{
FlxG.sound.play(Paths.sound('scrollMenu'));
changeItem(-1);
}
if (controls.DOWN_P)
{
FlxG.sound.play(Paths.sound('scrollMenu'));
changeItem(1);
}
if (controls.BACK)
{
FlxG.switchState(new TitleState());
}
if (controls.ACCEPT)
{
if (optionShit[curSelected] == 'donate')
{
#if linux
Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]);
#else
FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
#end
}
else
{
selectedSomethin = true;
FlxG.sound.play(Paths.sound('confirmMenu'));
FlxFlicker.flicker(magenta, 1.1, 0.15, false);
menuItems.forEach(function(spr:FlxSprite)
{
if (curSelected != spr.ID)
{
FlxTween.tween(spr, {alpha: 0}, 0.4, {
ease: FlxEase.quadOut,
onComplete: function(twn:FlxTween)
{
spr.kill();
}
});
}
else
{
FlxFlicker.flicker(spr, 1, 0.06, false, false, function(flick:FlxFlicker)
{
var daChoice:String = optionShit[curSelected];
switch (daChoice)
{
case 'story mode':
FlxG.switchState(new StoryMenuState());
trace("Story Menu Selected");
case 'freeplay':
FlxG.switchState(new FreeplayState());
trace("Freeplay Menu Selected");
case 'options':
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
FlxG.switchState(new OptionsMenu());
}
});
}
});
}
}
}
if (menuItems.enabled && controls.BACK)
FlxG.switchState(new TitleState());
super.update(elapsed);
menuItems.forEach(function(spr:FlxSprite)
{
spr.screenCenter(X);
});
}
function changeItem(huh:Int = 0)
{
curSelected += huh;
if (curSelected >= menuItems.length)
curSelected = 0;
if (curSelected < 0)
curSelected = menuItems.length - 1;
menuItems.forEach(function(spr:FlxSprite)
{
spr.animation.play('idle');
if (spr.ID == curSelected)
{
camFollow.setPosition(spr.getGraphicMidpoint().x, spr.getGraphicMidpoint().y);
spr.animation.play('selected');
}
spr.updateHitbox();
if(spr.animation.curAnim.name == 'selected')
{
switch(optionShit[curSelected])
{
case 'story mode':
spr.offset.y += 26;
case 'freeplay':
spr.offset.y += 28;
case 'donate':
spr.offset.y += 21;
case 'options':
spr.offset.y += 26;
}
}
});
}
}
private class MainMenuList extends MenuTypedList<MainMenuItem>
{
public var atlas:FlxAtlasFrames;
public function new ()
{
atlas = Paths.getSparrowAtlas('main_menu');
super(Vertical);
}
public function createItem(x = 0.0, y = 0.0, name:String, callback, fireInstantly = false)
{
var item = new MainMenuItem(x, y, name, atlas, callback);
item.fireInstantly = fireInstantly;
item.ID = length;
return addItem(name, item);
}
override function destroy()
{
super.destroy();
atlas = null;
}
}
private class MainMenuItem extends AtlasMenuItem
{
public function new(x = 0.0, y = 0.0, name, atlas, callback)
{
super(x, y, name, atlas, callback);
scrollFactor.set();
}
override function changeAnim(anim:String)
{
super.changeAnim(anim);
// position by center
centerOrigin();
offset.copyFrom(origin);
}
}

View file

@ -1,10 +1,13 @@
package;
#if newgrounds
import flixel.FlxG;
import flixel.util.FlxSignal;
import flixel.util.FlxTimer;
import io.newgrounds.NG;
import io.newgrounds.NGLite;
import io.newgrounds.components.ScoreBoardComponent.Period;
import io.newgrounds.objects.Error;
import io.newgrounds.objects.Medal;
import io.newgrounds.objects.Score;
import io.newgrounds.objects.ScoreBoard;
@ -15,14 +18,24 @@ import lime.app.Application;
import openfl.display.Stage;
using StringTools;
#end
/**
* MADE BY GEOKURELI THE LEGENED GOD HERO MVP
*/
class NGio
{
public static var isLoggedIn:Bool = false;
#if newgrounds
/**
* True, if the saved sessionId was used in the initial login, and failed to connect.
* Used in MainMenuState to show a popup to establish a new connection
*/
public static var savedSessionFailed(default, null):Bool = false;
public static var scoreboardsLoaded:Bool = false;
public static var isLoggedIn(get, never):Bool;
inline static function get_isLoggedIn()
{
return NG.core != null && NG.core.loggedIn;
}
public static var scoreboardArray:Array<Score> = [];
@ -30,45 +43,67 @@ class NGio
public static var ngScoresLoaded(default, null):FlxSignal = new FlxSignal();
public static var GAME_VER:String = "";
public static var GAME_VER_NUMS:String = '';
public static var gotOnlineVer:Bool = false;
public static function noLogin(api:String)
static public function checkVersion(callback:String->Void)
{
trace('INIT NOLOGIN');
trace('checking NG.io version');
GAME_VER = "v" + Application.current.meta.get('version');
if (api.length != 0)
{
NG.create(api);
new FlxTimer().start(2, function(tmr:FlxTimer)
NG.core.calls.app.getCurrentVersion(GAME_VER)
.addDataHandler(function(response)
{
var call = NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response:Response<GetCurrentVersionResult>)
{
GAME_VER = response.result.data.currentVersion;
GAME_VER_NUMS = GAME_VER.split(" ")[0].trim();
trace('CURRENT NG VERSION: ' + GAME_VER);
trace('CURRENT NG VERSION: ' + GAME_VER_NUMS);
gotOnlineVer = true;
});
call.send();
});
}
GAME_VER = response.result.data.currentVersion;
trace('CURRENT NG VERSION: ' + GAME_VER);
callback(GAME_VER);
})
.send();
}
public function new(api:String, encKey:String, ?sessionId:String)
static public function init()
{
var api = APIStuff.API;
if (api == null || api.length == 0)
{
trace("Missing Newgrounds API key, aborting connection");
return;
}
trace("connecting to newgrounds");
NG.createAndCheckSession(api, sessionId);
NG.core.verbose = true;
#if NG_FORCE_EXPIRED_SESSION
var sessionId:String = "fake_session_id";
function onSessionFail(error:Error)
{
trace("Forcing an expired saved session. "
+ "To disable, comment out NG_FORCE_EXPIRED_SESSION in Project.xml");
savedSessionFailed = true;
}
#else
var sessionId:String = NGLite.getSessionId();
if (sessionId != null)
trace("found web session id");
#if (debug)
if (sessionId == null && APIStuff.SESSION != null)
{
trace("using debug session id");
sessionId = APIStuff.SESSION;
}
#end
var onSessionFail:Error->Void = null;
if (sessionId == null && FlxG.save.data.sessionId != null)
{
trace("using stored session id");
sessionId = FlxG.save.data.sessionId;
onSessionFail = function (error) savedSessionFailed = true;
}
#end
NG.create(api, sessionId, #if NG_DEBUG true #else false #end, onSessionFail);
#if NG_VERBOSE NG.core.verbose = true; #end
// Set the encryption cipher/format to RC4/Base64. AES128 and Hex are not implemented yet
NG.core.initEncryption(encKey); // Found in you NG project view
trace(NG.core.attemptingLogin);
NG.core.initEncryption(APIStuff.EncKey); // Found in you NG project view
if (NG.core.attemptingLogin)
{
@ -78,21 +113,58 @@ class NGio
trace("attempting login");
NG.core.onLogin.add(onNGLogin);
}
else
//GK: taking out auto login, adding a login button to the main menu
// else
// {
// /* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to.
// * Note: This will cause a new browser window to pop up where they can log in to newgrounds
// */
// NG.core.requestLogin(onNGLogin);
// }
}
/**
* Attempts to log in to newgrounds by requesting a new session ID, only call if no session ID was found automatically
* @param popupLauncher The function to call to open the login url, must be inside
* a user input event or the popup blocker will block it.
* @param onComplete A callback with the result of the connection.
*/
static public function login(?popupLauncher:(Void->Void)->Void, onComplete:ConnectionResult->Void)
{
trace("Logging in manually");
var onPending:Void->Void = null;
if (popupLauncher != null)
{
/* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to.
* Note: This will cause a new browser window to pop up where they can log in to newgrounds
*/
NG.core.requestLogin(onNGLogin);
onPending = function () popupLauncher(NG.core.openPassportUrl);
}
var onSuccess:Void->Void = onNGLogin;
var onFail:Error->Void = null;
var onCancel:Void->Void = null;
if (onComplete != null)
{
onSuccess = function ()
{
onNGLogin();
onComplete(Success);
}
onFail = function (e) onComplete(Fail(e.message));
onCancel = function() onComplete(Cancelled);
}
NG.core.requestLogin(onSuccess, onPending, onFail, onCancel);
}
inline static public function cancelLogin():Void
{
NG.core.cancelLoginRequest();
}
function onNGLogin():Void
static function onNGLogin():Void
{
trace('logged in! user:${NG.core.user.name}');
isLoggedIn = true;
FlxG.save.data.sessionId = NG.core.sessionId;
// FlxG.save.flush();
FlxG.save.flush();
// Load medals then call onNGMedalFetch()
NG.core.requestMedals(onNGMedalFetch);
@ -101,9 +173,17 @@ class NGio
ngDataLoaded.dispatch();
}
static public function logout()
{
NG.core.logOut();
FlxG.save.data.sessionId = null;
FlxG.save.flush();
}
// --- MEDALS
function onNGMedalFetch():Void
static function onNGMedalFetch():Void
{
/*
// Reading medal info
@ -121,7 +201,7 @@ class NGio
}
// --- SCOREBOARDS
function onNGBoardsFetch():Void
static function onNGBoardsFetch():Void
{
/*
// Reading medal info
@ -145,25 +225,7 @@ class NGio
// more info on scores --- http://www.newgrounds.io/help/components/#scoreboard-getscores
}
inline static public function postScore(score:Int = 0, song:String)
{
if (isLoggedIn)
{
for (id in NG.core.scoreBoards.keys())
{
var board = NG.core.scoreBoards.get(id);
if (song == board.name)
{
board.postScore(score, "Uhh meow?");
}
// trace('loaded scoreboard id:$id, name:${board.name}');
}
}
}
function onNGScoresFetch():Void
static function onNGScoresFetch():Void
{
scoreboardsLoaded = true;
@ -181,20 +243,61 @@ class NGio
// NGio.scoreboardArray = NG.core.scoreBoards.get(8004).scores;
}
#end
inline static public function logEvent(event:String)
static public function logEvent(event:String)
{
#if newgrounds
NG.core.calls.event.logEvent(event).send();
trace('should have logged: ' + event);
#else
#if debug trace('event:$event - not logged, missing NG.io lib'); #end
#end
}
inline static public function unlockMedal(id:Int)
static public function unlockMedal(id:Int)
{
#if newgrounds
if (isLoggedIn)
{
var medal = NG.core.medals.get(id);
if (!medal.unlocked)
medal.sendUnlock();
}
#else
#if debug trace('medal:$id - not unlocked, missing NG.io lib'); #end
#end
}
static public function postScore(score:Int = 0, song:String)
{
#if newgrounds
if (isLoggedIn)
{
for (id in NG.core.scoreBoards.keys())
{
var board = NG.core.scoreBoards.get(id);
if (song == board.name)
{
board.postScore(score, "Uhh meow?");
}
// trace('loaded scoreboard id:$id, name:${board.name}');
}
}
#else
#if debug trace('Song:$song, Score:$score - not posted, missing NG.io lib'); #end
#end
}
}
enum ConnectionResult
{
/** Log in successful */
Success;
/** Could not login */
Fail(msg:String);
/** User cancelled the login */
Cancelled;
}

View file

@ -12,7 +12,7 @@ import flixel.text.FlxText;
import flixel.util.FlxColor;
import lime.utils.Assets;
class OptionsMenu extends MusicBeatState
class OptionsMenu_old extends MusicBeatState
{
var selector:FlxText;
var curSelected:Int = 0;
@ -82,7 +82,7 @@ class OptionsMenu extends MusicBeatState
{
if (FlxG.keys.getIsDown().length > 0)
{
PlayerSettings.player1.controls.replaceBinding(Control.LEFT, Keys, FlxG.keys.getIsDown()[0].ID, null);
// PlayerSettings.player1.controls.replaceBinding(Control.LEFT, Keys, FlxG.keys.getIsDown()[0].ID, null);
}
// PlayerSettings.player1.controls.replaceBinding(Control)
}
@ -99,9 +99,7 @@ class OptionsMenu extends MusicBeatState
function changeSelection(change:Int = 0)
{
#if !switch
NGio.logEvent('Fresh');
#end
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);

View file

@ -97,7 +97,7 @@ class PauseSubState extends MusicBeatSubstate
regenMenu();
cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]];
// cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]];
}
private function regenMenu():Void
@ -126,8 +126,8 @@ class PauseSubState extends MusicBeatSubstate
super.update(elapsed);
var upP = controls.UP_P;
var downP = controls.DOWN_P;
var upP = controls.UI_UP_P;
var downP = controls.UI_DOWN_P;
var accepted = controls.ACCEPT;
if (upP)

View file

@ -153,9 +153,7 @@ class PlayState extends MusicBeatState
camHUD.bgColor.alpha = 0;
FlxG.cameras.reset(camGame);
FlxG.cameras.add(camHUD);
FlxCamera.defaultCameras = [camGame];
FlxG.cameras.add(camHUD, false);
persistentUpdate = true;
persistentDraw = true;
@ -1462,7 +1460,13 @@ class PlayState extends MusicBeatState
FlxG.switchState(new GitarooPause());
}
else
openSubState(new PauseSubState(boyfriend.getScreenPosition().x, boyfriend.getScreenPosition().y));
{
var boyfriendPos = boyfriend.getScreenPosition();
var pauseSubState = new PauseSubState(boyfriendPos.x, boyfriendPos.y);
openSubState(pauseSubState);
pauseSubState.camera = camHUD;
boyfriendPos.put();
}
#if desktop
DiscordClient.changePresence(detailsPausedText, SONG.song + " (" + storyDifficultyText + ")", iconRPC);
@ -1618,12 +1622,13 @@ class PlayState extends MusicBeatState
trace("RESET = True");
}
// CHEAT = brandon's a pussy
#if CAN_CHEAT // brandon's a pussy
if (controls.CHEAT)
{
health += 1;
trace("User is cheating!");
}
#end
if (health <= 0 && !practiceMode)
{
@ -1762,9 +1767,7 @@ class PlayState extends MusicBeatState
vocals.volume = 0;
if (SONG.validScore)
{
#if !switch
Highscore.saveScore(SONG.song, songScore, storyDifficulty);
#end
}
if (isStoryMode)
@ -1994,20 +1997,20 @@ class PlayState extends MusicBeatState
private function keyShit():Void
{
// HOLDING
var up = controls.UP;
var right = controls.RIGHT;
var down = controls.DOWN;
var left = controls.LEFT;
var up = controls.NOTE_UP;
var right = controls.NOTE_RIGHT;
var down = controls.NOTE_DOWN;
var left = controls.NOTE_LEFT;
var upP = controls.UP_P;
var rightP = controls.RIGHT_P;
var downP = controls.DOWN_P;
var leftP = controls.LEFT_P;
var upP = controls.NOTE_UP_P;
var rightP = controls.NOTE_RIGHT_P;
var downP = controls.NOTE_DOWN_P;
var leftP = controls.NOTE_LEFT_P;
var upR = controls.UP_R;
var rightR = controls.RIGHT_R;
var downR = controls.DOWN_R;
var leftR = controls.LEFT_R;
var upR = controls.NOTE_UP_R;
var rightR = controls.NOTE_RIGHT_R;
var downR = controls.NOTE_DOWN_R;
var leftR = controls.NOTE_LEFT_R;
var controlArray:Array<Bool> = [leftP, downP, upP, rightP];
@ -2231,10 +2234,10 @@ class PlayState extends MusicBeatState
{
// just double pasting this shit cuz fuk u
// REDO THIS SYSTEM!
var upP = controls.UP_P;
var rightP = controls.RIGHT_P;
var downP = controls.DOWN_P;
var leftP = controls.LEFT_P;
var upP = controls.NOTE_UP_P;
var rightP = controls.NOTE_RIGHT_P;
var downP = controls.NOTE_DOWN_P;
var leftP = controls.NOTE_LEFT_P;
if (leftP)
noteMiss(0);

View file

@ -1,8 +1,11 @@
package;
import Controls;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.input.actions.FlxActionInput;
import flixel.input.gamepad.FlxGamepad;
import flixel.util.FlxSignal;
// import ui.DeviceManager;
@ -14,143 +17,233 @@ class PlayerSettings
static public var player1(default, null):PlayerSettings;
static public var player2(default, null):PlayerSettings;
#if (haxe >= "4.0.0")
static public final onAvatarAdd = new FlxTypedSignal<PlayerSettings->Void>();
static public final onAvatarRemove = new FlxTypedSignal<PlayerSettings->Void>();
#else
static public var onAvatarAdd = new FlxTypedSignal<PlayerSettings->Void>();
static public var onAvatarRemove = new FlxTypedSignal<PlayerSettings->Void>();
#end
static public var onAvatarAdd(default, null) = new FlxTypedSignal<PlayerSettings->Void>();
static public var onAvatarRemove(default, null) = new FlxTypedSignal<PlayerSettings->Void>();
public var id(default, null):Int;
#if (haxe >= "4.0.0")
public final controls:Controls;
#else
public var controls:Controls;
#end
public var controls(default, null):Controls;
// public var avatar:Player;
// public var camera(get, never):PlayCamera;
function new(id, scheme)
function new(id)
{
this.id = id;
this.controls = new Controls('player$id', scheme);
this.controls = new Controls('player$id', None);
#if CLEAR_INPUT_SAVE
FlxG.save.data.controls = null;
FlxG.save.flush();
#end
var useDefault = true;
var controlData = FlxG.save.data.controls;
if (controlData != null)
{
var keyData:Dynamic = null;
if (id == 0 && controlData.p1 != null && controlData.p1.keys != null)
keyData = controlData.p1.keys;
else if (id == 1 && controlData.p2 != null && controlData.p2.keys != null)
keyData = controlData.p2.keys;
if (keyData != null)
{
useDefault = false;
trace("loaded key data: " + haxe.Json.stringify(keyData));
controls.fromSaveData(keyData, Keys);
}
}
if (useDefault)
controls.setKeyboardScheme(Solo);
}
function addGamepad(gamepad:FlxGamepad)
{
var useDefault = true;
var controlData = FlxG.save.data.controls;
if (controlData != null)
{
var padData:Dynamic = null;
if (id == 0 && controlData.p1 != null && controlData.p1.pad != null)
padData = controlData.p1.pad;
else if (id == 1 && controlData.p2 != null && controlData.p2.pad != null)
padData = controlData.p2.pad;
if (padData != null)
{
useDefault = false;
trace("loaded pad data: " + haxe.Json.stringify(padData));
controls.addGamepadWithSaveData(gamepad.id, padData);
}
}
if (useDefault)
controls.addDefaultGamepad(gamepad.id);
}
public function saveControls()
{
if (FlxG.save.data.controls == null)
FlxG.save.data.controls = {};
var playerData:{ ?keys:Dynamic, ?pad:Dynamic }
if (id == 0)
{
if (FlxG.save.data.controls.p1 == null)
FlxG.save.data.controls.p1 = {};
playerData = FlxG.save.data.controls.p1;
}
else
{
if (FlxG.save.data.controls.p2 == null)
FlxG.save.data.controls.p2 = {};
playerData = FlxG.save.data.controls.p2;
}
var keyData = controls.createSaveData(Keys);
if (keyData != null)
{
playerData.keys = keyData;
trace("saving key data: " + haxe.Json.stringify(keyData));
}
if (controls.gamepadsAdded.length > 0)
{
var padData = controls.createSaveData(Gamepad(controls.gamepadsAdded[0]));
if (padData != null)
{
trace("saving pad data: " + haxe.Json.stringify(padData));
playerData.pad = padData;
}
}
FlxG.save.flush();
}
static public function init():Void
{
if (player1 == null)
{
player1 = new PlayerSettings(0);
++numPlayers;
}
FlxG.gamepads.deviceConnected.add(onGamepadAdded);
var numGamepads = FlxG.gamepads.numActiveGamepads;
for (i in 0...numGamepads)
{
var gamepad = FlxG.gamepads.getByID(i);
if (gamepad != null)
onGamepadAdded(gamepad);
}
// player1.controls.addDefaultGamepad(0);
// }
// if (numGamepads > 1)
// {
// if (player2 == null)
// {
// player2 = new PlayerSettings(1, None);
// ++numPlayers;
// }
// var gamepad = FlxG.gamepads.getByID(1);
// if (gamepad == null)
// throw 'Unexpected null gamepad. id:0';
// player2.controls.addDefaultGamepad(1);
// }
// DeviceManager.init();
}
static function onGamepadAdded(gamepad:FlxGamepad)
{
player1.addGamepad(gamepad);
}
/*
public function setKeyboardScheme(scheme)
{
controls.setKeyboardScheme(scheme);
}
/*
static public function addAvatar(avatar:Player):PlayerSettings
{
var settings:PlayerSettings;
if (player1 == null)
{
player1 = new PlayerSettings(0, Solo);
++numPlayers;
}
if (player1.avatar == null)
settings = player1;
else
{
if (player2 == null)
{
if (player1.controls.keyboardScheme.match(Duo(true)))
player2 = new PlayerSettings(1, Duo(false));
else
player2 = new PlayerSettings(1, None);
++numPlayers;
}
if (player2.avatar == null)
settings = player2;
else
throw throw 'Invalid number of players: ${numPlayers + 1}';
}
++numAvatars;
settings.avatar = avatar;
avatar.settings = settings;
splitCameras();
onAvatarAdd.dispatch(settings);
return settings;
}
static public function removeAvatar(avatar:Player):Void
{
var settings:PlayerSettings;
if (player1 != null && player1.avatar == avatar)
settings = player1;
else if (player2 != null && player2.avatar == avatar)
{
settings = player2;
if (player1.controls.keyboardScheme.match(Duo(_)))
player1.setKeyboardScheme(Solo);
}
else
throw "Cannot remove avatar that is not for a player";
settings.avatar = null;
while (settings.controls.gamepadsAdded.length > 0)
{
final id = settings.controls.gamepadsAdded.shift();
settings.controls.removeGamepad(id);
DeviceManager.releaseGamepad(FlxG.gamepads.getByID(id));
}
--numAvatars;
splitCameras();
onAvatarRemove.dispatch(avatar.settings);
}
*/
static public function init():Void
static public function addAvatar(avatar:Player):PlayerSettings
{
var settings:PlayerSettings;
if (player1 == null)
{
player1 = new PlayerSettings(0, Solo);
++numPlayers;
}
var numGamepads = FlxG.gamepads.numActiveGamepads;
if (numGamepads > 0)
{
var gamepad = FlxG.gamepads.getByID(0);
if (gamepad == null)
throw 'Unexpected null gamepad. id:0';
player1.controls.addDefaultGamepad(0);
}
if (numGamepads > 1)
if (player1.avatar == null)
settings = player1;
else
{
if (player2 == null)
{
player2 = new PlayerSettings(1, None);
if (player1.controls.keyboardScheme.match(Duo(true)))
player2 = new PlayerSettings(1, Duo(false));
else
player2 = new PlayerSettings(1, None);
++numPlayers;
}
var gamepad = FlxG.gamepads.getByID(1);
if (gamepad == null)
throw 'Unexpected null gamepad. id:0';
if (player2.avatar == null)
settings = player2;
else
throw throw 'Invalid number of players: ${numPlayers + 1}';
}
++numAvatars;
settings.avatar = avatar;
avatar.settings = settings;
player2.controls.addDefaultGamepad(1);
splitCameras();
onAvatarAdd.dispatch(settings);
return settings;
}
static public function removeAvatar(avatar:Player):Void
{
var settings:PlayerSettings;
if (player1 != null && player1.avatar == avatar)
settings = player1;
else if (player2 != null && player2.avatar == avatar)
{
settings = player2;
if (player1.controls.keyboardScheme.match(Duo(_)))
player1.setKeyboardScheme(Solo);
}
else
throw "Cannot remove avatar that is not for a player";
settings.avatar = null;
while (settings.controls.gamepadsAdded.length > 0)
{
final id = settings.controls.gamepadsAdded.shift();
settings.controls.removeGamepad(id);
DeviceManager.releaseGamepad(FlxG.gamepads.getByID(id));
}
// DeviceManager.init();
--numAvatars;
splitCameras();
onAvatarRemove.dispatch(avatar.settings);
}
*/
static public function reset()
{
player1 = null;

View file

@ -245,29 +245,29 @@ class StoryMenuState extends MusicBeatState
{
if (!selectedWeek)
{
if (controls.UP_P)
if (controls.UI_UP_P)
{
changeWeek(-1);
}
if (controls.DOWN_P)
if (controls.UI_DOWN_P)
{
changeWeek(1);
}
if (controls.RIGHT)
if (controls.UI_RIGHT)
rightArrow.animation.play('press')
else
rightArrow.animation.play('idle');
if (controls.LEFT)
if (controls.UI_LEFT)
leftArrow.animation.play('press');
else
leftArrow.animation.play('idle');
if (controls.RIGHT_P)
if (controls.UI_RIGHT_P)
changeDifficulty(1);
if (controls.LEFT_P)
if (controls.UI_LEFT_P)
changeDifficulty(-1);
}
@ -360,10 +360,6 @@ class StoryMenuState extends MusicBeatState
sprDifficulty.y = leftArrow.y - 15;
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
#if !switch
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
#end
FlxTween.tween(sprDifficulty, {y: leftArrow.y + 15, alpha: 1}, 0.07);
}
@ -439,8 +435,6 @@ class StoryMenuState extends MusicBeatState
txtTracklist.screenCenter(X);
txtTracklist.x -= FlxG.width * 0.35;
#if !switch
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
#end
}
}

View file

@ -14,7 +14,6 @@ import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import io.newgrounds.NG;
import lime.app.Application;
import openfl.Assets;
import shaderslmfao.ColorSwap;
@ -54,25 +53,20 @@ class TitleState extends MusicBeatState
FlxG.sound.muteKeys = [ZERO];
PlayerSettings.init();
curWacky = FlxG.random.getObject(getIntroTextShit());
// DEBUG BULLSHIT
super.create();
NGio.noLogin(APIStuff.API);
#if ng
var ng:NGio = new NGio(APIStuff.API, APIStuff.EncKey);
trace('NEWGROUNDS LOL');
#end
FlxG.save.bind('funkin', 'ninjamuffin99');
PlayerSettings.init();
Highscore.load();
#if newgrounds
NGio.init();
#end
if (FlxG.save.data.weekUnlocked != null)
{
// FIX LATER!!!
@ -289,13 +283,11 @@ class TitleState extends MusicBeatState
if (pressedEnter && !transitioning && skippedIntro)
{
#if !switch
NGio.unlockMedal(60960);
// If it's Friday according to da clock
if (Date.now().getDay() == 5)
NGio.unlockMedal(61034);
#end
titleText.animation.play('press');
@ -305,26 +297,30 @@ class TitleState extends MusicBeatState
transitioning = true;
// FlxG.sound.music.stop();
new FlxTimer().start(2, function(tmr:FlxTimer)
#if newgrounds
if (!OutdatedSubState.leftState)
{
// Check if version is outdated
var version:String = "v" + Application.current.meta.get('version');
if (version.trim() != NGio.GAME_VER_NUMS.trim() && !OutdatedSubState.leftState)
NGio.checkVersion(function(version)
{
FlxG.switchState(new OutdatedSubState());
trace('OLD VERSION!');
trace('old ver');
trace(version.trim());
trace('cur ver');
trace(NGio.GAME_VER_NUMS.trim());
}
else
{
FlxG.switchState(new MainMenuState());
}
});
// Check if version is outdated
var localVersion:String = "v" + Application.current.meta.get('version');
var onlineVersion = version.split(" ")[0].trim();
if (version.trim() != onlineVersion)
{
trace('OLD VERSION!');
FlxG.switchState(new OutdatedSubState());
}
else
{
FlxG.switchState(new MainMenuState());
}
});
}
#else
FlxG.switchState(new MainMenuState());
#end
// FlxG.sound.play(Paths.music('titleShoot'), 0.7);
}
@ -333,12 +329,12 @@ class TitleState extends MusicBeatState
skipIntro();
}
if (controls.LEFT)
if (controls.UI_LEFT)
{
swagShader.update(-elapsed * 0.1);
}
if (controls.RIGHT)
if (controls.UI_RIGHT)
{
swagShader.update(elapsed * 0.1);
}

View file

@ -0,0 +1,81 @@
package ui;
import ui.MenuList;
import flixel.graphics.frames.FlxAtlasFrames;
typedef AtlasAsset = flixel.util.typeLimit.OneOfTwo<String, FlxAtlasFrames>;
class AtlasMenuList extends MenuTypedList<AtlasMenuItem>
{
public var atlas:FlxAtlasFrames;
public function new (atlas, navControls:NavControls = Vertical, ?wrapMode)
{
super(navControls, wrapMode);
if (Std.is(atlas, String))
this.atlas = Paths.getSparrowAtlas(cast atlas);
else
this.atlas = cast atlas;
}
public function createItem(x = 0.0, y = 0.0, name, callback, fireInstantly = false)
{
var item = new AtlasMenuItem(x, y, name, atlas, callback);
item.fireInstantly = fireInstantly;
return addItem(name, item);
}
override function destroy()
{
super.destroy();
atlas = null;
}
}
class AtlasMenuItem extends MenuItem
{
var atlas:FlxAtlasFrames;
public function new (x = 0.0, y = 0.0, name:String, atlas:FlxAtlasFrames, callback)
{
this.atlas = atlas;
super(x, y, name, callback);
}
override function setData(name:String, ?callback:Void->Void)
{
frames = atlas;
animation.addByPrefix('idle', '$name idle', 24);
animation.addByPrefix('selected', '$name selected', 24);
super.setData(name, callback);
}
function changeAnim(animName:String)
{
animation.play(animName);
updateHitbox();
}
override function idle()
{
changeAnim('idle');
}
override function select()
{
changeAnim('selected');
}
override function get_selected()
{
return animation.curAnim != null && animation.curAnim.name == "selected";
}
override function destroy()
{
super.destroy();
atlas = null;
}
}

262
source/ui/AtlasText.hx Normal file
View file

@ -0,0 +1,262 @@
package ui;
import flixel.FlxSprite;
import flixel.group.FlxSpriteGroup;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.util.FlxStringUtil;
@:forward
abstract BoldText(AtlasText) from AtlasText to AtlasText
{
inline public function new (x = 0.0, y = 0.0, text:String)
{
this = new AtlasText(x, y, text, Bold);
}
}
/**
* Alphabet.hx has a ton of bugs and does a bunch of stuff I don't need, fuck that class
*/
class AtlasText extends FlxTypedSpriteGroup<AtlasChar>
{
static var fonts = new Map<AtlasFont, AtlasFontData>();
static var casesAllowed = new Map<AtlasFont, Case>();
public var text(default, set):String = "";
var font:AtlasFontData;
public var atlas(get, never):FlxAtlasFrames;
inline function get_atlas() return font.atlas;
public var caseAllowed(get, never):Case;
inline function get_caseAllowed() return font.caseAllowed;
public var maxHeight(get, never):Float;
inline function get_maxHeight() return font.maxHeight;
public function new (x = 0.0, y = 0.0, text:String, fontName:AtlasFont = Default)
{
if (!fonts.exists(fontName))
fonts[fontName] = new AtlasFontData(fontName);
font = fonts[fontName];
super(x, y);
this.text = text;
}
function set_text(value:String)
{
if (value == null)
value = "";
var caseValue = restrictCase(value);
var caseText = restrictCase(this.text);
this.text = value;
if (caseText == caseValue)
return value; // cancel redraw
if (caseValue.indexOf(caseText) == 0)
{
// new text is just old text with additions at the end, append the difference
appendTextCased(caseValue.substr(caseText.length));
return this.text;
}
value = caseValue;
group.kill();
if (value == "")
return this.text;
appendTextCased(caseValue);
return this.text;
}
/**
* Adds new characters, without needing to redraw the previous characters
* @param text The text to add.
* @throws String if `text` is null.
*/
public function appendText(text:String)
{
if (text == null)
throw "cannot append null";
if (text == "")
return;
this.text = this.text + text;
}
/**
* Converts all characters to fit the font's `allowedCase`.
* @param text
*/
function restrictCase(text:String)
{
return switch(caseAllowed)
{
case Both: text;
case Upper: text.toUpperCase();
case Lower: text.toLowerCase();
}
}
/**
* Adds new text on top of the existing text. Helper for other methods; DOESN'T CHANGE `this.text`.
* @param text The text to add, assumed to match the font's `caseAllowed`.
*/
function appendTextCased(text:String)
{
var charCount = group.countLiving();
var xPos:Float = 0;
var yPos:Float = 0;
// `countLiving` returns -1 if group is empty
if (charCount == -1)
charCount = 0;
else if (charCount > 0)
{
var lastChar = group.members[charCount - 1];
xPos = lastChar.x + lastChar.width - x;
yPos = lastChar.y + lastChar.height - maxHeight - y;
}
var splitValues = text.split("");
for (i in 0...splitValues.length)
{
switch(splitValues[i])
{
case " ":
{
xPos += 40;
}
case "\n":
{
xPos = 0;
yPos += maxHeight;
}
case char:
{
var charSprite:AtlasChar;
if (group.members.length <= charCount)
charSprite = new AtlasChar(atlas, char);
else
{
charSprite = group.members[charCount];
charSprite.revive();
charSprite.char = char;
charSprite.alpha = 1;//gets multiplied when added
}
charSprite.x = xPos;
charSprite.y = yPos + maxHeight - charSprite.height;
add(charSprite);
xPos += charSprite.width;
charCount++;
}
}
}
}
override function toString()
{
return "InputItem, " + FlxStringUtil.getDebugString(
[ LabelValuePair.weak("x", x)
, LabelValuePair.weak("y", y)
, LabelValuePair.weak("text", text)
]
);
}
}
class AtlasChar extends FlxSprite
{
public var char(default, set):String;
public function new(x = 0.0, y = 0.0, atlas:FlxAtlasFrames, char:String)
{
super(x, y);
frames = atlas;
this.char = char;
antialiasing = true;
}
function set_char(value:String)
{
if (this.char != value)
{
var prefix = getAnimPrefix(value);
animation.addByPrefix("anim", prefix, 24);
animation.play("anim");
updateHitbox();
}
return this.char = value;
}
function getAnimPrefix(char:String)
{
return switch (char)
{
case '-': '-dash-';
case '.': '-period-';
case ",": '-comma-';
case "'": '-apostraphie-';
case "?": '-question mark-';
case "!": '-exclamation point-';
case "\\": '-back slash-';
case "/": '-forward slash-';
case "*": '-multiply x-';
case "": '-start quote-';
case "": '-end quote-';
default: char;
}
}
}
private class AtlasFontData
{
static public var upperChar = ~/^[A-Z]\d+$/;
static public var lowerChar = ~/^[a-z]\d+$/;
public var atlas:FlxAtlasFrames;
public var maxHeight:Float = 0.0;
public var caseAllowed:Case = Both;
public function new (name:AtlasFont)
{
atlas = Paths.getSparrowAtlas("fonts/" + name.getName().toLowerCase());
atlas.parent.destroyOnNoUse = false;
atlas.parent.persist = true;
var containsUpper = false;
var containsLower = false;
for (frame in atlas.frames)
{
maxHeight = Math.max(maxHeight, frame.frame.height);
if (!containsUpper)
containsUpper = upperChar.match(frame.name);
if (!containsLower)
containsLower = lowerChar.match(frame.name);
}
if (containsUpper != containsLower)
caseAllowed = containsUpper ? Upper : Lower;
}
}
enum Case
{
Both;
Upper;
Lower;
}
enum AtlasFont
{
Default;
Bold;
}

356
source/ui/ControlsMenu.hx Normal file
View file

@ -0,0 +1,356 @@
package ui;
import flixel.input.actions.FlxActionInput;
import flixel.input.gamepad.FlxGamepadInputID;
import flixel.FlxG;
import flixel.FlxCamera;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.group.FlxGroup;
import flixel.input.keyboard.FlxKey;
import Controls;
import ui.AtlasText;
import ui.MenuList;
import ui.TextMenuList;
class ControlsMenu extends ui.OptionsState.Page
{
inline static public var COLUMNS = 2;
static var controlList = Control.createAll();
/*
* Defines groups of controls that cannot share inputs, like left and right. Say, if ACCEPT is Z, Back is X,
* if the player sets Back to Z it also set ACCEPT to X. This prevents the player from setting the controls in
* a way the prevents them from changing more controls or exiting the menu.
*/
static var controlGroups:Array<Array<Control>> =
[ [ NOTE_UP, NOTE_DOWN, NOTE_LEFT, NOTE_RIGHT ]
, [ UI_UP, UI_DOWN, UI_LEFT, UI_RIGHT, ACCEPT, BACK ]
];
var itemGroups:Array<Array<InputItem>> = [for (i in 0...controlGroups.length) []];
var controlGrid:MenuTypedList<InputItem>;
var deviceList:TextMenuList;
var menuCamera:FlxCamera;
var prompt:Prompt;
var camFollow:FlxObject;
var labels:FlxTypedGroup<AtlasText>;
var currentDevice:Device = Keys;
var deviceListSelected = false;
public function new()
{
super();
menuCamera = new FlxCamera();
FlxG.cameras.add(menuCamera, false);
menuCamera.bgColor = 0x0;
camera = menuCamera;
labels = new FlxTypedGroup<AtlasText>();
var headers = new FlxTypedGroup<AtlasText>();
controlGrid = new MenuTypedList(Columns(COLUMNS), Vertical);
add(labels);
add(headers);
add(controlGrid);
if (FlxG.gamepads.numActiveGamepads > 0)
{
var devicesBg = new FlxSprite();
devicesBg.makeGraphic(FlxG.width, 100, 0xFFfafd6d);
add(devicesBg);
deviceList = new TextMenuList(Horizontal, None);
add(deviceList);
deviceListSelected = true;
var item;
item = deviceList.createItem("Keyboard", Bold, selectDevice.bind(Keys));
item.x = FlxG.width / 2 - item.width - 30;
item.y = (devicesBg.height - item.height) / 2;
item = deviceList.createItem("Gamepad", Bold, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id)));
item.x = FlxG.width / 2 + 30;
item.y = (devicesBg.height - item.height) / 2;
}
// FlxG.debugger.drawDebug = true;
var y = deviceList == null ? 30 : 120;
var spacer = 70;
var currentHeader:String = null;
// list order is determined by enum order
for (i in 0...controlList.length)
{
var control = controlList[i];
var name = control.getName();
if (currentHeader != "UI_" && name.indexOf("UI_") == 0)
{
currentHeader = "UI_";
headers.add(new BoldText(0, y, "UI")).screenCenter(X);
y += spacer;
}
else if (currentHeader != "NOTE_" && name.indexOf("NOTE_") == 0)
{
currentHeader = "NOTE_";
headers.add(new BoldText(0, y, "NOTES")).screenCenter(X);
y += spacer;
}
if (currentHeader != null && name.indexOf(currentHeader) == 0)
name = name.substr(currentHeader.length);
var label = labels.add(new BoldText(150, y, name));
label.alpha = 0.6;
for (i in 0...COLUMNS)
createItem(label.x + 400 + i * 300, y, control, i);
y += spacer;
}
camFollow = new FlxObject(FlxG.width / 2, 0, 70, 70);
if (deviceList != null)
{
camFollow.y = deviceList.selectedItem.y;
controlGrid.selectedItem.idle();
controlGrid.enabled = false;
}
else
camFollow.y = controlGrid.selectedItem.y;
menuCamera.follow(camFollow, null, 0.06);
var margin = 100;
menuCamera.deadzone.set(0, margin, menuCamera.width, menuCamera.height - margin * 2);
menuCamera.minScrollY = 0;
controlGrid.onChange.add(function (selected)
{
camFollow.y = selected.y;
labels.forEach((label)->label.alpha = 0.6);
labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0;
});
prompt = new Prompt("\nPress any key to rebind\n\n\n\n Escape to cancel", None);
prompt.create();
prompt.createBgFromMargin(100, 0xFFfafd6d);
prompt.back.scrollFactor.set(0, 0);
prompt.exists = false;
add(prompt);
}
function createItem(x = 0.0, y = 0.0, control:Control, index:Int)
{
var item = new InputItem(x, y, currentDevice, control, index, onSelect);
for (i in 0...controlGroups.length)
{
if (controlGroups[i].contains(control))
itemGroups[i].push(item);
}
return controlGrid.addItem(item.name, item);
}
function onSelect():Void
{
controlGrid.enabled = false;
canExit = false;
prompt.exists = true;
}
function goToDeviceList()
{
controlGrid.selectedItem.idle();
labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 0.6;
controlGrid.enabled = false;
deviceList.enabled = true;
canExit = true;
camFollow.y = deviceList.selectedItem.y;
deviceListSelected = true;
}
function selectDevice(device:Device)
{
currentDevice = device;
for (item in controlGrid.members)
item.updateDevice(currentDevice);
var inputName = device == Keys ? "key" : "button";
var cancel = device == Keys ? "Escape" : "Back";
//todo: alignment
if (device == Keys)
prompt.setText('\nPress any key to rebind\n\n\n\n $cancel to cancel');
else
prompt.setText('\nPress any button\n to rebind\n\n\n $cancel to cancel');
controlGrid.selectedItem.select();
labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0;
controlGrid.enabled = true;
deviceList.enabled = false;
deviceListSelected = false;
canExit = false;
}
override function update(elapsed:Float)
{
super.update(elapsed);
var controls = PlayerSettings.player1.controls;
if (controlGrid.enabled && deviceList != null && deviceListSelected == false && controls.BACK)
goToDeviceList();
if (prompt.exists)
{
switch (currentDevice)
{
case Keys:
{
// check released otherwise bugs can happen when you change the BACK key
var key = FlxG.keys.firstJustReleased();
if (key != NONE)
{
if (key != ESCAPE)
onInputSelect(key);
closePrompt();
}
}
case Gamepad(id):
{
var button = FlxG.gamepads.getByID(id).firstJustReleasedID();
if (button != NONE)
{
if (button != BACK)
onInputSelect(button);
closePrompt();
}
}
}
}
}
function onInputSelect(input:Int)
{
var item = controlGrid.selectedItem;
// check if that key is already set for this
var column0 = Math.floor(controlGrid.selectedIndex / 2) * 2;
for (i in 0...COLUMNS)
{
if (controlGrid.members[column0 + i].input == input)
return;
}
// Check if items in the same group already have the new input
for (group in itemGroups)
{
if (group.contains(item))
{
for (otherItem in group)
{
if (otherItem != item && otherItem.input == input)
{
// replace that input with this items old input.
PlayerSettings.player1.controls.replaceBinding(otherItem.control, currentDevice, item.input, otherItem.input);
// Don't use resetItem() since items share names/labels
otherItem.input = item.input;
otherItem.label.text = item.label.text;
}
}
}
}
PlayerSettings.player1.controls.replaceBinding(item.control, currentDevice, input, item.input);
// Don't use resetItem() since items share names/labels
item.input = input;
item.label.text = item.getLabel(input);
PlayerSettings.player1.saveControls();
}
function closePrompt()
{
prompt.exists = false;
controlGrid.enabled = true;
if (deviceList == null)
canExit = true;
}
override function destroy()
{
super.destroy();
itemGroups = null;
if (FlxG.cameras.list.contains(menuCamera))
FlxG.cameras.remove(menuCamera);
}
override function set_enabled(value:Bool)
{
if (value == false)
{
controlGrid.enabled = false;
if (deviceList != null)
deviceList.enabled = false;
}
else
{
controlGrid.enabled = !deviceListSelected;
if (deviceList != null)
deviceList.enabled = deviceListSelected;
}
return super.set_enabled(value);
}
}
class InputItem extends TextMenuItem
{
public var device(default, null):Device = Keys;
public var control:Control;
public var input:Int = -1;
public var index:Int = -1;
public function new (x = 0.0, y = 0.0, device, control, index, ?callback)
{
this.device = device;
this.control = control;
this.index = index;
this.input = getInput();
super(x, y, getLabel(input), Default, callback);
}
public function updateDevice(device:Device)
{
if (this.device != device)
{
this.device = device;
input = getInput();
label.text = getLabel(input);
}
}
function getInput()
{
var list = PlayerSettings.player1.controls.getInputsFor(control, device);
if (list.length > index)
{
if (list[index] != FlxKey.ESCAPE || list[index] != FlxGamepadInputID.BACK)
return list[index];
if (list.length > ControlsMenu.COLUMNS)
// Escape isn't mappable, show a third option, instead.
return list[ControlsMenu.COLUMNS];
}
return -1;
}
public function getLabel(input:Int)
{
return input == -1 ? "---" : InputFormatter.format(input, device);
}
}

366
source/ui/MenuList.hx Normal file
View file

@ -0,0 +1,366 @@
package ui;
import flixel.math.FlxPoint;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.effects.FlxFlicker;
import flixel.group.FlxGroup;
import flixel.util.FlxSignal;
class MenuTypedList<T:MenuItem> extends FlxTypedGroup<T>
{
public var selectedIndex(default, null) = 0;
public var selectedItem(get, never):T;
/** Called when a new item is highlighted */
public var onChange(default, null) = new FlxTypedSignal<T->Void>();
/** Called when an item is accepted */
public var onAcceptPress(default, null) = new FlxTypedSignal<T->Void>();
/** The navigation control scheme to use */
public var navControls:NavControls;
/** Set to false to disable nav control */
public var enabled:Bool = true;
/** */
public var wrapMode:WrapMode = Both;
var byName = new Map<String, T>();
/** Set to true, internally to disable controls, without affecting vars like `enabled` */
var busy:Bool = false;
public function new (navControls:NavControls = Vertical, ?wrapMode:WrapMode)
{
this.navControls = navControls;
if (wrapMode != null)
this.wrapMode = wrapMode;
else
this.wrapMode = switch (navControls)
{
case Horizontal: Horizontal;
case Vertical: Vertical;
default: Both;
}
super();
}
public function addItem(name:String, item:T):T
{
if (length == selectedIndex)
item.select();
byName[name] = item;
return add(item);
}
public function resetItem(oldName:String, newName:String, ?callback:Void->Void):T
{
if (!byName.exists(oldName))
throw "No item named:" + oldName;
var item = byName[oldName];
byName.remove(oldName);
byName[newName] = item;
item.setItem(newName, callback);
return item;
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (enabled && !busy)
updateControls();
}
inline function updateControls()
{
var controls = PlayerSettings.player1.controls;
var wrapX = wrapMode.match(Horizontal | Both);
var wrapY = wrapMode.match(Vertical | Both);
var newIndex = switch(navControls)
{
case Vertical : navList(controls.UI_UP_P , controls.UI_DOWN_P, wrapY);
case Horizontal : navList(controls.UI_LEFT_P, controls.UI_RIGHT_P, wrapX);
case Both : navList(controls.UI_LEFT_P || controls.UI_UP_P, controls.UI_RIGHT_P || controls.UI_DOWN_P, !wrapMode.match(None));
case Columns(num): navGrid(num, controls.UI_LEFT_P, controls.UI_RIGHT_P, wrapX, controls.UI_UP_P , controls.UI_DOWN_P , wrapY);
case Rows (num): navGrid(num, controls.UI_UP_P , controls.UI_DOWN_P , wrapY, controls.UI_LEFT_P, controls.UI_RIGHT_P, wrapX);
}
if (newIndex != selectedIndex)
{
FlxG.sound.play(Paths.sound('scrollMenu'));
selectItem(newIndex);
}
//Todo: bypass popup blocker on firefox
if (controls.ACCEPT)
accept();
}
function navAxis(index:Int, size:Int, prev:Bool, next:Bool, allowWrap:Bool):Int
{
if (prev == next)
return index;
if (prev)
{
if (index > 0)
index--;
else if (allowWrap)
index = size - 1;
}
else
{
if (index < size - 1)
index++;
else if (allowWrap)
index = 0;
}
return index;
}
/**
* Controls navigation on a linear list of items such as Vertical.
* @param prev
* @param next
* @param allowWrap
*/
inline function navList(prev:Bool, next:Bool, allowWrap:Bool)
{
return navAxis(selectedIndex, length, prev, next, allowWrap);
}
/**
* Controls navigation on a grid
* @param latSize The size of the fixed axis of the grid, or the "lateral axis"
* @param latPrev Whether the 'prev' key is pressed along the fixed-lengthed axis. eg: "left" in Column mode
* @param latNext Whether the 'next' key is pressed along the fixed-lengthed axis. eg: "right" in Column mode
* @param prev Whether the 'prev' key is pressed along the variable-lengthed axis. eg: "up" in Column mode
* @param next Whether the 'next' key is pressed along the variable-lengthed axis. eg: "down" in Column mode
* @param allowWrap unused
*/
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
var size = Math.ceil(length / latSize);
// The selected position along the variable-length axis
var index = Math.floor(selectedIndex / latSize);
// The selected position along the fixed axis
var latIndex = selectedIndex % latSize;
latIndex = navAxis(latIndex, latSize, latPrev, latNext, latAllowWrap);
index = navAxis(index, size, prev, next, allowWrap);
return Std.int(Math.min(length - 1, index * latSize + latIndex));
}
public function accept()
{
var selected = members[selectedIndex];
onAcceptPress.dispatch(selected);
if (selected.fireInstantly)
selected.callback();
else
{
busy = true;
FlxG.sound.play(Paths.sound('confirmMenu'));
FlxFlicker.flicker(selected, 1, 0.06, true, false, function(_)
{
busy = false;
selected.callback();
});
}
}
public function selectItem(index:Int)
{
members[selectedIndex].idle();
selectedIndex = index;
var selected = members[selectedIndex];
selected.select();
onChange.dispatch(selected);
}
public function has(name:String)
{
return byName.exists(name);
}
public function getItem(name:String)
{
return byName[name];
}
override function destroy()
{
super.destroy();
byName.clear();
onChange.removeAll();
onAcceptPress.removeAll();
}
inline function get_selectedItem():T
{
return members[selectedIndex];
}
}
class MenuItem extends FlxSprite
{
public var callback:Void->Void;
public var name:String;
/**
* Set to true for things like opening URLs otherwise, it may it get blocked.
*/
public var fireInstantly = false;
public var selected(get, never):Bool;
function get_selected() return alpha == 1.0;
public function new (x = 0.0, y = 0.0, name:String, callback)
{
super(x, y);
antialiasing = true;
setData(name, callback);
idle();
}
function setData(name:String, ?callback:Void->Void)
{
this.name = name;
if (callback != null)
this.callback = callback;
}
/**
* Calls setData and resets/redraws the state of the item
* @param name the label.
* @param callback Unchanged if null.
*/
public function setItem(name:String, ?callback:Void->Void)
{
setData(name, callback);
if (selected)
select();
else
idle();
}
public function idle()
{
alpha = 0.6;
}
public function select()
{
alpha = 1.0;
}
}
class MenuTypedItem<T:FlxSprite> extends MenuItem
{
public var label(default, set):T;
public function new (x = 0.0, y = 0.0, label:T, name:String, callback)
{
super(x, y, name, callback);
// set label after super otherwise setters fuck up
this.label = label;
}
/**
* Use this when you only want to show the label
*/
function setEmptyBackground()
{
var oldWidth = width;
var oldHeight = height;
makeGraphic(1, 1, 0x0);
width = oldWidth;
height = oldHeight;
}
function set_label(value:T)
{
if (value != null)
{
value.x = x;
value.y = y;
value.alpha = alpha;
}
return this.label = value;
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (label != null)
label.update(elapsed);
}
override function draw()
{
super.draw();
if (label != null)
{
label.cameras = cameras;
label.scrollFactor.copyFrom(scrollFactor);
label.draw();
}
}
override function set_alpha(value:Float):Float
{
super.set_alpha(value);
if (label != null)
label.alpha = alpha;
return alpha;
}
override function set_x(value:Float):Float
{
super.set_x(value);
if (label != null)
label.x = x;
return x;
}
override function set_y(Value:Float):Float
{
super.set_y(Value);
if (label != null)
label.y = y;
return y;
}
}
enum NavControls
{
Horizontal;
Vertical;
Both;
Columns(num:Int);
Rows(num:Int);
}
enum WrapMode
{
Horizontal;
Vertical;
Both;
None;
}

107
source/ui/NgPrompt.hx Normal file
View file

@ -0,0 +1,107 @@
package ui;
import NGio;
import ui.Prompt;
class NgPrompt extends Prompt
{
public function new (text:String, style:ButtonStyle = Yes_No)
{
super(text, style);
}
static public function showLogin()
{
return showLoginPrompt(true);
}
static public function showSavedSessionFailed()
{
return showLoginPrompt(false);
}
static function showLoginPrompt(fromUi:Bool)
{
var prompt = new NgPrompt("Talking to server...", None);
prompt.openCallback = NGio.login.bind
(
function popupLauncher(openPassportUrl)
{
var choiceMsg = fromUi
? #if web "Log in to Newgrounds?" #else null #end // User-input needed to allow popups
: "Your session has expired.\n Please login again.";
if (choiceMsg != null)
{
prompt.setText(choiceMsg);
prompt.setButtons(Yes_No);
#if web
prompt.buttons.getItem("yes").fireInstantly = true;
#end
prompt.onYes = function()
{
prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end);
prompt.setButtons(None);
openPassportUrl();
};
prompt.onNo = function()
{
prompt.close();
prompt = null;
NGio.cancelLogin();
};
}
else
{
prompt.setText("Connecting...");
openPassportUrl();
}
},
function onLoginComplete(result:ConnectionResult)
{
switch (result)
{
case Success:
{
prompt.setText("Login Successful");
prompt.setButtons(Ok);
prompt.onYes = prompt.close;
}
case Fail(msg):
{
trace("Login Error:" + msg);
prompt.setText("Login failed");
prompt.setButtons(Ok);
prompt.onYes = prompt.close;
}
case Cancelled:
{
if (prompt != null)
{
prompt.setText("Login cancelled by user");
prompt.setButtons(Ok);
prompt.onYes = prompt.close;
}
else
trace("Login cancelled via prompt");
}
}
}
);
return prompt;
}
static public function showLogout()
{
var user = io.newgrounds.NG.core.user.name;
var prompt = new NgPrompt('Log out of $user?', Yes_No);
prompt.onYes = function()
{
NGio.logout();
prompt.close();
};
prompt.onNo = prompt.close;
return prompt;
}
}

260
source/ui/OptionsState.hx Normal file
View file

@ -0,0 +1,260 @@
package ui;
import flixel.FlxSubState;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.group.FlxGroup;
import flixel.util.FlxSignal;
import flixel.addons.transition.FlxTransitionableState;
// typedef OptionsState = OptionsMenu_old;
// class OptionsState_new extends MusicBeatState
class OptionsState extends MusicBeatState
{
var pages = new Map<PageName, Page>();
var currentName:PageName = #if newgrounds Options #else Controls #end;
var currentPage(get, never):Page;
inline function get_currentPage() return pages[currentName];
override function create()
{
var menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
menuBG.color = 0xFFea71fd;
menuBG.setGraphicSize(Std.int(menuBG.width * 1.1));
menuBG.updateHitbox();
menuBG.screenCenter();
menuBG.scrollFactor.set(0, 0);
add(menuBG);
var options = addPage(Options, new OptionsMenu(false));
var controls = addPage(Controls, new ControlsMenu());
if (options.hasMultipleOptions())
{
options.onExit.add(exitToMainMenu);
controls.onExit.add(switchPage.bind(Options));
}
else
{
// No need to show Options page
controls.onExit.add(exitToMainMenu);
setPage(Controls);
}
// disable for intro transition
currentPage.enabled = false;
super.create();
}
function addPage<T:Page>(name:PageName, page:T)
{
page.onSwitch.add(switchPage);
pages[name] = page;
add(page);
page.exists = currentName == name;
return page;
}
function setPage(name:PageName)
{
if (pages.exists(currentName))
currentPage.exists = false;
currentName = name;
if (pages.exists(currentName))
currentPage.exists = true;
}
override function finishTransIn()
{
super.finishTransIn();
currentPage.enabled = true;
}
function switchPage(name:PageName)
{
//Todo animate?
setPage(name);
}
function exitToMainMenu()
{
currentPage.enabled = false;
//Todo animate?
FlxG.switchState(new MainMenuState());
}
}
class Page extends FlxGroup
{
public var onSwitch(default, null) = new FlxTypedSignal<PageName->Void>();
public var onExit(default, null) = new FlxSignal();
public var enabled(default, set) = true;
public var canExit = true;
var controls(get, never):Controls;
inline function get_controls() return PlayerSettings.player1.controls;
var subState:FlxSubState;
inline function switchPage(name:PageName)
{
onSwitch.dispatch(name);
}
inline function exit()
{
onExit.dispatch();
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (enabled)
updateEnabled(elapsed);
}
function updateEnabled(elapsed:Float)
{
if (canExit && controls.BACK)
exit();
}
function set_enabled(value:Bool)
{
return this.enabled = value;
}
function openPrompt(prompt:Prompt, onClose:Void->Void)
{
enabled = false;
prompt.closeCallback = function ()
{
enabled = true;
if (onClose != null)
onClose();
}
FlxG.state.openSubState(prompt);
}
override function destroy()
{
super.destroy();
onSwitch.removeAll();
}
}
class OptionsMenu extends Page
{
var items:TextMenuList;
public function new (showDonate:Bool)
{
super();
add(items = new TextMenuList());
createItem("controls", function() switchPage(Controls));
#if CAN_OPEN_LINKS
if (showDonate)
{
var hasPopupBlocker = #if web true #else false #end;
createItem('donate', selectDonate, hasPopupBlocker);
}
#end
#if newgrounds
if (NGio.isLoggedIn)
createItem("logout", selectLogout);
else
createItem("login", selectLogin);
#end
createItem("exit", exit);
}
function createItem(name:String, callback:Void->Void, fireInstantly = false)
{
var item = items.createItem(0, 100 + items.length * 100, name, Bold, callback);
item.fireInstantly = fireInstantly;
item.screenCenter(X);
return item;
}
override function set_enabled(value:Bool)
{
items.enabled = value;
return super.set_enabled(value);
}
/**
* True if this page has multiple options, ecluding the exit option.
* If false, there's no reason to ever show this page.
*/
public function hasMultipleOptions():Bool
{
return items.length > 2;
}
#if CAN_OPEN_LINKS
function selectDonate()
{
#if linux
Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]);
#else
FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
#end
}
#end
#if newgrounds
function selectLogin()
{
openNgPrompt(NgPrompt.showLogin());
}
function selectLogout()
{
openNgPrompt(NgPrompt.showLogout());
}
/**
* Calls openPrompt and redraws the login/logout button
* @param prompt
* @param onClose
*/
public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void)
{
var onPromptClose = checkLoginStatus;
if (onClose != null)
{
onPromptClose = function ()
{
checkLoginStatus();
onClose();
}
}
openPrompt(prompt, onPromptClose);
}
function checkLoginStatus()
{
var prevLoggedIn = items.has("logout");
if (prevLoggedIn && !NGio.isLoggedIn)
items.resetItem("login", "logout", selectLogout);
else if (!prevLoggedIn && NGio.isLoggedIn)
items.resetItem("logout", "login", selectLogin);
}
#end
}
enum PageName
{
Options;
Controls;
}

121
source/ui/Prompt.hx Normal file
View file

@ -0,0 +1,121 @@
package ui;
import ui.AtlasText;
import ui.MenuList;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.text.FlxText;
import flixel.util.FlxColor;
class Prompt extends flixel.FlxSubState
{
inline static var MARGIN = 100;
public var onYes:Void->Void;
public var onNo:Void->Void;
public var buttons:TextMenuList;
public var field:AtlasText;
public var back:FlxSprite;
var style:ButtonStyle;
public function new (text:String, style:ButtonStyle = Ok)
{
this.style = style;
super(0x80000000);
buttons = new TextMenuList(Horizontal);
field = new BoldText(text);
field.scrollFactor.set(0, 0);
}
override function create()
{
super.create();
field.y = MARGIN;
field.screenCenter(X);
add(field);
createButtons();
add(buttons);
}
public function createBg(width:Int, height:Int, color = 0xFF808080)
{
back = new FlxSprite();
back.makeGraphic(width, height, color, false, "prompt-bg");
back.screenCenter(XY);
add(back);
members.unshift(members.pop());// bring to front
}
public function createBgFromMargin(margin = MARGIN, color = 0xFF808080)
{
createBg(Std.int(FlxG.width - margin * 2), Std.int(FlxG.height - margin * 2), color);
}
public function setButtons(style:ButtonStyle)
{
if (this.style != style)
{
this.style = style;
createButtons();
}
}
function createButtons()
{
// destroy previous buttons
while(buttons.members.length > 0)
{
buttons.remove(buttons.members[0], true).destroy();
}
switch(style)
{
case Yes_No : createButtonsHelper("yes", "no");
case Ok : createButtonsHelper("ok");
case Custom(yes, no): createButtonsHelper(yes, no);
case None : buttons.exists = false;
};
}
function createButtonsHelper(yes:String, ?no:String)
{
buttons.exists = true;
// pass anonymous functions rather than the current callbacks, in case they change later
var yesButton = buttons.createItem(yes, function() onYes());
yesButton.screenCenter(X);
yesButton.y = FlxG.height - yesButton.height - MARGIN;
yesButton.scrollFactor.set(0, 0);
if (no != null)
{
// place right
yesButton.x = FlxG.width - yesButton.width - MARGIN;
var noButton = buttons.createItem(no, function() onNo());
noButton.x = MARGIN;
noButton.y = FlxG.height - noButton.height - MARGIN;
noButton.scrollFactor.set(0, 0);
}
}
public function setText(text:String)
{
field.text = text;
field.screenCenter(X);
}
}
enum ButtonStyle
{
Ok;
Yes_No;
Custom(yes:String, no:Null<String>);//Todo: more than 2
None;
}

56
source/ui/TextMenuList.hx Normal file
View file

@ -0,0 +1,56 @@
package ui;
import ui.AtlasText;
import ui.MenuList;
class TextMenuList extends MenuTypedList<TextMenuItem>
{
public function new (navControls:NavControls = Vertical, ?wrapMode)
{
super(navControls, wrapMode);
}
public function createItem(x = 0.0, y = 0.0, name:String, font:AtlasFont = Bold, callback, fireInstantly = false)
{
var item = new TextMenuItem(x, y, name, font, callback);
item.fireInstantly = fireInstantly;
return addItem(name, item);
}
}
class TextMenuItem extends TextTypedMenuItem<AtlasText>
{
public function new (x = 0.0, y = 0.0, name:String, font:AtlasFont = Bold, callback)
{
super(x, y, new AtlasText(0, 0, name, font), name, callback);
setEmptyBackground();
}
}
class TextTypedMenuItem<T:AtlasText> extends MenuTypedItem<T>
{
public function new (x = 0.0, y = 0.0, label:T, name:String, callback)
{
super(x, y, label, name, callback);
}
override function setItem(name:String, ?callback:Void -> Void)
{
if (label != null)
{
label.text = name;
label.alpha = alpha;
width = label.width;
height = label.height;
}
super.setItem(name, callback);
}
override function set_label(value:T):T
{
super.set_label(value);
setItem(name, callback);
return value;
}
}