diff --git a/Content/Sprites/Controls/Nintendo Switch/Back.png b/Content/Sprites/Controls/Nintendo Switch/Back.png new file mode 100644 index 00000000..3087f312 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/Back.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/ButtonDown.png b/Content/Sprites/Controls/Nintendo Switch/ButtonDown.png new file mode 100644 index 00000000..970817ef Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/ButtonDown.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/ButtonLeft.png b/Content/Sprites/Controls/Nintendo Switch/ButtonLeft.png new file mode 100644 index 00000000..c8e972c7 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/ButtonLeft.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/ButtonRight.png b/Content/Sprites/Controls/Nintendo Switch/ButtonRight.png new file mode 100644 index 00000000..304a73ee Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/ButtonRight.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/ButtonUp.png b/Content/Sprites/Controls/Nintendo Switch/ButtonUp.png new file mode 100644 index 00000000..86129c7d Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/ButtonUp.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/confirm.png b/Content/Sprites/Controls/Nintendo Switch/East.png similarity index 100% rename from Content/Sprites/Controls/Nintendo Switch/confirm.png rename to Content/Sprites/Controls/Nintendo Switch/East.png diff --git a/Content/Sprites/Controls/Nintendo Switch/copyfile.png b/Content/Sprites/Controls/Nintendo Switch/LeftShoulder.png similarity index 100% rename from Content/Sprites/Controls/Nintendo Switch/copyfile.png rename to Content/Sprites/Controls/Nintendo Switch/LeftShoulder.png diff --git a/Content/Sprites/Controls/Nintendo Switch/LeftStick.png b/Content/Sprites/Controls/Nintendo Switch/LeftStick.png new file mode 100644 index 00000000..e04bbf39 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/LeftStick.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/LeftTriggerPositive.png b/Content/Sprites/Controls/Nintendo Switch/LeftTriggerPositive.png new file mode 100644 index 00000000..4d8a4f63 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/LeftTriggerPositive.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/LeftX.png b/Content/Sprites/Controls/Nintendo Switch/LeftX.png new file mode 100644 index 00000000..7d3ea538 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/LeftX.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/LeftXNegative.png b/Content/Sprites/Controls/Nintendo Switch/LeftXNegative.png new file mode 100644 index 00000000..0fa8948d Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/LeftXNegative.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/LeftXPositive.png b/Content/Sprites/Controls/Nintendo Switch/LeftXPositive.png new file mode 100644 index 00000000..53df6eb8 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/LeftXPositive.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/LeftY.png b/Content/Sprites/Controls/Nintendo Switch/LeftY.png new file mode 100644 index 00000000..b7277227 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/LeftY.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/LeftYNegative.png b/Content/Sprites/Controls/Nintendo Switch/LeftYNegative.png new file mode 100644 index 00000000..297a4355 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/LeftYNegative.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/LeftYPositive.png b/Content/Sprites/Controls/Nintendo Switch/LeftYPositive.png new file mode 100644 index 00000000..184733d5 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/LeftYPositive.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/createfile.png b/Content/Sprites/Controls/Nintendo Switch/North.png similarity index 100% rename from Content/Sprites/Controls/Nintendo Switch/createfile.png rename to Content/Sprites/Controls/Nintendo Switch/North.png diff --git a/Content/Sprites/Controls/Nintendo Switch/deletefile.png b/Content/Sprites/Controls/Nintendo Switch/RightShoulder.png similarity index 100% rename from Content/Sprites/Controls/Nintendo Switch/deletefile.png rename to Content/Sprites/Controls/Nintendo Switch/RightShoulder.png diff --git a/Content/Sprites/Controls/Nintendo Switch/RightStick.png b/Content/Sprites/Controls/Nintendo Switch/RightStick.png new file mode 100644 index 00000000..bdeaf51f Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/RightStick.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/RightTriggerPositive.png b/Content/Sprites/Controls/Nintendo Switch/RightTriggerPositive.png new file mode 100644 index 00000000..e871088a Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/RightTriggerPositive.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/RightX.png b/Content/Sprites/Controls/Nintendo Switch/RightX.png new file mode 100644 index 00000000..7bf05473 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/RightX.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/RightXNegative.png b/Content/Sprites/Controls/Nintendo Switch/RightXNegative.png new file mode 100644 index 00000000..86418acd Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/RightXNegative.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/RightXPositive.png b/Content/Sprites/Controls/Nintendo Switch/RightXPositive.png new file mode 100644 index 00000000..43814dcf Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/RightXPositive.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/RightY.png b/Content/Sprites/Controls/Nintendo Switch/RightY.png new file mode 100644 index 00000000..95d07d35 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/RightY.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/RightYNegative.png b/Content/Sprites/Controls/Nintendo Switch/RightYNegative.png new file mode 100644 index 00000000..5d8541f0 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/RightYNegative.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/RightYPositive.png b/Content/Sprites/Controls/Nintendo Switch/RightYPositive.png new file mode 100644 index 00000000..0e6e24ae Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/RightYPositive.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/Select.png b/Content/Sprites/Controls/Nintendo Switch/Select.png new file mode 100644 index 00000000..31b5c644 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/Select.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/cancel.png b/Content/Sprites/Controls/Nintendo Switch/South.png similarity index 100% rename from Content/Sprites/Controls/Nintendo Switch/cancel.png rename to Content/Sprites/Controls/Nintendo Switch/South.png diff --git a/Content/Sprites/Controls/Nintendo Switch/Start.png b/Content/Sprites/Controls/Nintendo Switch/Start.png new file mode 100644 index 00000000..c4476c0d Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/Start.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/West.png b/Content/Sprites/Controls/Nintendo Switch/West.png new file mode 100644 index 00000000..737632d4 Binary files /dev/null and b/Content/Sprites/Controls/Nintendo Switch/West.png differ diff --git a/Content/Sprites/Controls/Nintendo Switch/pause.png b/Content/Sprites/Controls/Nintendo Switch/pause.png deleted file mode 100644 index 67c6b5b0..00000000 Binary files a/Content/Sprites/Controls/Nintendo Switch/pause.png and /dev/null differ diff --git a/Content/Sprites/Controls/PC/A.png b/Content/Sprites/Controls/PC/A.png new file mode 100644 index 00000000..d845fa44 Binary files /dev/null and b/Content/Sprites/Controls/PC/A.png differ diff --git a/Content/Sprites/Controls/PC/Apostrophe.png b/Content/Sprites/Controls/PC/Apostrophe.png new file mode 100644 index 00000000..ca275d4d Binary files /dev/null and b/Content/Sprites/Controls/PC/Apostrophe.png differ diff --git a/Content/Sprites/Controls/PC/B.png b/Content/Sprites/Controls/PC/B.png new file mode 100644 index 00000000..2f42d8a4 Binary files /dev/null and b/Content/Sprites/Controls/PC/B.png differ diff --git a/Content/Sprites/Controls/PC/Backslash.png b/Content/Sprites/Controls/PC/Backslash.png new file mode 100644 index 00000000..ae3724dd Binary files /dev/null and b/Content/Sprites/Controls/PC/Backslash.png differ diff --git a/Content/Sprites/Controls/PC/Backspace.png b/Content/Sprites/Controls/PC/Backspace.png new file mode 100644 index 00000000..f71ec7aa Binary files /dev/null and b/Content/Sprites/Controls/PC/Backspace.png differ diff --git a/Content/Sprites/Controls/PC/C.png b/Content/Sprites/Controls/PC/C.png new file mode 100644 index 00000000..0a530ef7 Binary files /dev/null and b/Content/Sprites/Controls/PC/C.png differ diff --git a/Content/Sprites/Controls/PC/Capslock.png b/Content/Sprites/Controls/PC/Capslock.png new file mode 100644 index 00000000..188e6d2b Binary files /dev/null and b/Content/Sprites/Controls/PC/Capslock.png differ diff --git a/Content/Sprites/Controls/PC/Comma.png b/Content/Sprites/Controls/PC/Comma.png new file mode 100644 index 00000000..d54e5967 Binary files /dev/null and b/Content/Sprites/Controls/PC/Comma.png differ diff --git a/Content/Sprites/Controls/PC/Command.png b/Content/Sprites/Controls/PC/Command.png new file mode 100644 index 00000000..7cdea627 Binary files /dev/null and b/Content/Sprites/Controls/PC/Command.png differ diff --git a/Content/Sprites/Controls/PC/D.png b/Content/Sprites/Controls/PC/D.png new file mode 100644 index 00000000..2436d62f Binary files /dev/null and b/Content/Sprites/Controls/PC/D.png differ diff --git a/Content/Sprites/Controls/PC/D0.png b/Content/Sprites/Controls/PC/D0.png new file mode 100644 index 00000000..a3b22463 Binary files /dev/null and b/Content/Sprites/Controls/PC/D0.png differ diff --git a/Content/Sprites/Controls/PC/D1.png b/Content/Sprites/Controls/PC/D1.png new file mode 100644 index 00000000..6e07ea75 Binary files /dev/null and b/Content/Sprites/Controls/PC/D1.png differ diff --git a/Content/Sprites/Controls/PC/D2.png b/Content/Sprites/Controls/PC/D2.png new file mode 100644 index 00000000..b82cfe17 Binary files /dev/null and b/Content/Sprites/Controls/PC/D2.png differ diff --git a/Content/Sprites/Controls/PC/D3.png b/Content/Sprites/Controls/PC/D3.png new file mode 100644 index 00000000..4bc72954 Binary files /dev/null and b/Content/Sprites/Controls/PC/D3.png differ diff --git a/Content/Sprites/Controls/PC/D4.png b/Content/Sprites/Controls/PC/D4.png new file mode 100644 index 00000000..98388efe Binary files /dev/null and b/Content/Sprites/Controls/PC/D4.png differ diff --git a/Content/Sprites/Controls/PC/D5.png b/Content/Sprites/Controls/PC/D5.png new file mode 100644 index 00000000..77edaf82 Binary files /dev/null and b/Content/Sprites/Controls/PC/D5.png differ diff --git a/Content/Sprites/Controls/PC/D6.png b/Content/Sprites/Controls/PC/D6.png new file mode 100644 index 00000000..2e328a6b Binary files /dev/null and b/Content/Sprites/Controls/PC/D6.png differ diff --git a/Content/Sprites/Controls/PC/D7.png b/Content/Sprites/Controls/PC/D7.png new file mode 100644 index 00000000..8cf28afc Binary files /dev/null and b/Content/Sprites/Controls/PC/D7.png differ diff --git a/Content/Sprites/Controls/PC/D8.png b/Content/Sprites/Controls/PC/D8.png new file mode 100644 index 00000000..eb418fef Binary files /dev/null and b/Content/Sprites/Controls/PC/D8.png differ diff --git a/Content/Sprites/Controls/PC/D9.png b/Content/Sprites/Controls/PC/D9.png new file mode 100644 index 00000000..a20ac2e3 Binary files /dev/null and b/Content/Sprites/Controls/PC/D9.png differ diff --git a/Content/Sprites/Controls/PC/Delete.png b/Content/Sprites/Controls/PC/Delete.png new file mode 100644 index 00000000..96ec55b9 Binary files /dev/null and b/Content/Sprites/Controls/PC/Delete.png differ diff --git a/Content/Sprites/Controls/PC/Down.png b/Content/Sprites/Controls/PC/Down.png new file mode 100644 index 00000000..08f49a62 Binary files /dev/null and b/Content/Sprites/Controls/PC/Down.png differ diff --git a/Content/Sprites/Controls/PC/E.png b/Content/Sprites/Controls/PC/E.png new file mode 100644 index 00000000..18d44a79 Binary files /dev/null and b/Content/Sprites/Controls/PC/E.png differ diff --git a/Content/Sprites/Controls/PC/End.png b/Content/Sprites/Controls/PC/End.png new file mode 100644 index 00000000..42bc15ff Binary files /dev/null and b/Content/Sprites/Controls/PC/End.png differ diff --git a/Content/Sprites/Controls/PC/Enter.png b/Content/Sprites/Controls/PC/Enter.png new file mode 100644 index 00000000..d789807f Binary files /dev/null and b/Content/Sprites/Controls/PC/Enter.png differ diff --git a/Content/Sprites/Controls/PC/Equals.png b/Content/Sprites/Controls/PC/Equals.png new file mode 100644 index 00000000..4d6bb6b8 Binary files /dev/null and b/Content/Sprites/Controls/PC/Equals.png differ diff --git a/Content/Sprites/Controls/PC/Escape.png b/Content/Sprites/Controls/PC/Escape.png new file mode 100644 index 00000000..968d5bfe Binary files /dev/null and b/Content/Sprites/Controls/PC/Escape.png differ diff --git a/Content/Sprites/Controls/PC/Exclamation.png b/Content/Sprites/Controls/PC/Exclamation.png new file mode 100644 index 00000000..fb54c793 Binary files /dev/null and b/Content/Sprites/Controls/PC/Exclamation.png differ diff --git a/Content/Sprites/Controls/PC/F.png b/Content/Sprites/Controls/PC/F.png new file mode 100644 index 00000000..5ea2b7eb Binary files /dev/null and b/Content/Sprites/Controls/PC/F.png differ diff --git a/Content/Sprites/Controls/PC/F1.png b/Content/Sprites/Controls/PC/F1.png new file mode 100644 index 00000000..3b37c4ca Binary files /dev/null and b/Content/Sprites/Controls/PC/F1.png differ diff --git a/Content/Sprites/Controls/PC/F10.png b/Content/Sprites/Controls/PC/F10.png new file mode 100644 index 00000000..369f3e80 Binary files /dev/null and b/Content/Sprites/Controls/PC/F10.png differ diff --git a/Content/Sprites/Controls/PC/F11.png b/Content/Sprites/Controls/PC/F11.png new file mode 100644 index 00000000..f08c0333 Binary files /dev/null and b/Content/Sprites/Controls/PC/F11.png differ diff --git a/Content/Sprites/Controls/PC/F12.png b/Content/Sprites/Controls/PC/F12.png new file mode 100644 index 00000000..2a206159 Binary files /dev/null and b/Content/Sprites/Controls/PC/F12.png differ diff --git a/Content/Sprites/Controls/PC/F2.png b/Content/Sprites/Controls/PC/F2.png new file mode 100644 index 00000000..24f86c85 Binary files /dev/null and b/Content/Sprites/Controls/PC/F2.png differ diff --git a/Content/Sprites/Controls/PC/F3.png b/Content/Sprites/Controls/PC/F3.png new file mode 100644 index 00000000..3cd91809 Binary files /dev/null and b/Content/Sprites/Controls/PC/F3.png differ diff --git a/Content/Sprites/Controls/PC/F4.png b/Content/Sprites/Controls/PC/F4.png new file mode 100644 index 00000000..b216eb44 Binary files /dev/null and b/Content/Sprites/Controls/PC/F4.png differ diff --git a/Content/Sprites/Controls/PC/F5.png b/Content/Sprites/Controls/PC/F5.png new file mode 100644 index 00000000..e0a8284e Binary files /dev/null and b/Content/Sprites/Controls/PC/F5.png differ diff --git a/Content/Sprites/Controls/PC/F6.png b/Content/Sprites/Controls/PC/F6.png new file mode 100644 index 00000000..ea9c6733 Binary files /dev/null and b/Content/Sprites/Controls/PC/F6.png differ diff --git a/Content/Sprites/Controls/PC/F7.png b/Content/Sprites/Controls/PC/F7.png new file mode 100644 index 00000000..8216aa6b Binary files /dev/null and b/Content/Sprites/Controls/PC/F7.png differ diff --git a/Content/Sprites/Controls/PC/F8.png b/Content/Sprites/Controls/PC/F8.png new file mode 100644 index 00000000..b91adf2d Binary files /dev/null and b/Content/Sprites/Controls/PC/F8.png differ diff --git a/Content/Sprites/Controls/PC/F9.png b/Content/Sprites/Controls/PC/F9.png new file mode 100644 index 00000000..63ec5486 Binary files /dev/null and b/Content/Sprites/Controls/PC/F9.png differ diff --git a/Content/Sprites/Controls/PC/Function.png b/Content/Sprites/Controls/PC/Function.png new file mode 100644 index 00000000..b7aa5623 Binary files /dev/null and b/Content/Sprites/Controls/PC/Function.png differ diff --git a/Content/Sprites/Controls/PC/G.png b/Content/Sprites/Controls/PC/G.png new file mode 100644 index 00000000..8e1eaac8 Binary files /dev/null and b/Content/Sprites/Controls/PC/G.png differ diff --git a/Content/Sprites/Controls/PC/H.png b/Content/Sprites/Controls/PC/H.png new file mode 100644 index 00000000..5a6fafd5 Binary files /dev/null and b/Content/Sprites/Controls/PC/H.png differ diff --git a/Content/Sprites/Controls/PC/Home.png b/Content/Sprites/Controls/PC/Home.png new file mode 100644 index 00000000..a9caec7a Binary files /dev/null and b/Content/Sprites/Controls/PC/Home.png differ diff --git a/Content/Sprites/Controls/PC/I.png b/Content/Sprites/Controls/PC/I.png new file mode 100644 index 00000000..0e58a398 Binary files /dev/null and b/Content/Sprites/Controls/PC/I.png differ diff --git a/Content/Sprites/Controls/PC/Insert.png b/Content/Sprites/Controls/PC/Insert.png new file mode 100644 index 00000000..b6522fe8 Binary files /dev/null and b/Content/Sprites/Controls/PC/Insert.png differ diff --git a/Content/Sprites/Controls/PC/J.png b/Content/Sprites/Controls/PC/J.png new file mode 100644 index 00000000..417836f4 Binary files /dev/null and b/Content/Sprites/Controls/PC/J.png differ diff --git a/Content/Sprites/Controls/PC/K.png b/Content/Sprites/Controls/PC/K.png new file mode 100644 index 00000000..52ba37d7 Binary files /dev/null and b/Content/Sprites/Controls/PC/K.png differ diff --git a/Content/Sprites/Controls/PC/Keypad0.png b/Content/Sprites/Controls/PC/Keypad0.png new file mode 100644 index 00000000..a3b22463 Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad0.png differ diff --git a/Content/Sprites/Controls/PC/Keypad1.png b/Content/Sprites/Controls/PC/Keypad1.png new file mode 100644 index 00000000..6e07ea75 Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad1.png differ diff --git a/Content/Sprites/Controls/PC/Keypad2.png b/Content/Sprites/Controls/PC/Keypad2.png new file mode 100644 index 00000000..b82cfe17 Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad2.png differ diff --git a/Content/Sprites/Controls/PC/Keypad3.png b/Content/Sprites/Controls/PC/Keypad3.png new file mode 100644 index 00000000..4bc72954 Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad3.png differ diff --git a/Content/Sprites/Controls/PC/Keypad4.png b/Content/Sprites/Controls/PC/Keypad4.png new file mode 100644 index 00000000..98388efe Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad4.png differ diff --git a/Content/Sprites/Controls/PC/Keypad5.png b/Content/Sprites/Controls/PC/Keypad5.png new file mode 100644 index 00000000..77edaf82 Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad5.png differ diff --git a/Content/Sprites/Controls/PC/Keypad6.png b/Content/Sprites/Controls/PC/Keypad6.png new file mode 100644 index 00000000..2e328a6b Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad6.png differ diff --git a/Content/Sprites/Controls/PC/Keypad7.png b/Content/Sprites/Controls/PC/Keypad7.png new file mode 100644 index 00000000..8cf28afc Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad7.png differ diff --git a/Content/Sprites/Controls/PC/Keypad8.png b/Content/Sprites/Controls/PC/Keypad8.png new file mode 100644 index 00000000..eb418fef Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad8.png differ diff --git a/Content/Sprites/Controls/PC/Keypad9.png b/Content/Sprites/Controls/PC/Keypad9.png new file mode 100644 index 00000000..a20ac2e3 Binary files /dev/null and b/Content/Sprites/Controls/PC/Keypad9.png differ diff --git a/Content/Sprites/Controls/PC/KeypadColon.png b/Content/Sprites/Controls/PC/KeypadColon.png new file mode 100644 index 00000000..9112f83b Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadColon.png differ diff --git a/Content/Sprites/Controls/PC/KeypadDivide.png b/Content/Sprites/Controls/PC/KeypadDivide.png new file mode 100644 index 00000000..535aa23b Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadDivide.png differ diff --git a/Content/Sprites/Controls/PC/KeypadEnter.png b/Content/Sprites/Controls/PC/KeypadEnter.png new file mode 100644 index 00000000..c8a7ca1f Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadEnter.png differ diff --git a/Content/Sprites/Controls/PC/KeypadGreater.png b/Content/Sprites/Controls/PC/KeypadGreater.png new file mode 100644 index 00000000..2897392c Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadGreater.png differ diff --git a/Content/Sprites/Controls/PC/KeypadLess.png b/Content/Sprites/Controls/PC/KeypadLess.png new file mode 100644 index 00000000..b89852ba Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadLess.png differ diff --git a/Content/Sprites/Controls/PC/KeypadMinus.png b/Content/Sprites/Controls/PC/KeypadMinus.png new file mode 100644 index 00000000..2ed42b8d Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadMinus.png differ diff --git a/Content/Sprites/Controls/PC/KeypadMultiply.png b/Content/Sprites/Controls/PC/KeypadMultiply.png new file mode 100644 index 00000000..ba7ab9d4 Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadMultiply.png differ diff --git a/Content/Sprites/Controls/PC/KeypadPlus.png b/Content/Sprites/Controls/PC/KeypadPlus.png new file mode 100644 index 00000000..66963dc5 Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadPlus.png differ diff --git a/Content/Sprites/Controls/PC/KeypadPower.png b/Content/Sprites/Controls/PC/KeypadPower.png new file mode 100644 index 00000000..e7a4905c Binary files /dev/null and b/Content/Sprites/Controls/PC/KeypadPower.png differ diff --git a/Content/Sprites/Controls/PC/L.png b/Content/Sprites/Controls/PC/L.png new file mode 100644 index 00000000..82e1dfae Binary files /dev/null and b/Content/Sprites/Controls/PC/L.png differ diff --git a/Content/Sprites/Controls/PC/Left.png b/Content/Sprites/Controls/PC/Left.png new file mode 100644 index 00000000..88bdf43d Binary files /dev/null and b/Content/Sprites/Controls/PC/Left.png differ diff --git a/Content/Sprites/Controls/PC/LeftAlt.png b/Content/Sprites/Controls/PC/LeftAlt.png new file mode 100644 index 00000000..1f664cda Binary files /dev/null and b/Content/Sprites/Controls/PC/LeftAlt.png differ diff --git a/Content/Sprites/Controls/PC/LeftBracket.png b/Content/Sprites/Controls/PC/LeftBracket.png new file mode 100644 index 00000000..cccd4d24 Binary files /dev/null and b/Content/Sprites/Controls/PC/LeftBracket.png differ diff --git a/Content/Sprites/Controls/PC/LeftControl.png b/Content/Sprites/Controls/PC/LeftControl.png new file mode 100644 index 00000000..13810938 Binary files /dev/null and b/Content/Sprites/Controls/PC/LeftControl.png differ diff --git a/Content/Sprites/Controls/PC/LeftOS.png b/Content/Sprites/Controls/PC/LeftOS.png new file mode 100644 index 00000000..dec89bb1 Binary files /dev/null and b/Content/Sprites/Controls/PC/LeftOS.png differ diff --git a/Content/Sprites/Controls/PC/LeftShift.png b/Content/Sprites/Controls/PC/LeftShift.png new file mode 100644 index 00000000..58940419 Binary files /dev/null and b/Content/Sprites/Controls/PC/LeftShift.png differ diff --git a/Content/Sprites/Controls/PC/M.png b/Content/Sprites/Controls/PC/M.png new file mode 100644 index 00000000..7ae6cad3 Binary files /dev/null and b/Content/Sprites/Controls/PC/M.png differ diff --git a/Content/Sprites/Controls/PC/Minus.png b/Content/Sprites/Controls/PC/Minus.png new file mode 100644 index 00000000..2ed42b8d Binary files /dev/null and b/Content/Sprites/Controls/PC/Minus.png differ diff --git a/Content/Sprites/Controls/PC/Mouse.png b/Content/Sprites/Controls/PC/Mouse.png new file mode 100644 index 00000000..f9acb453 Binary files /dev/null and b/Content/Sprites/Controls/PC/Mouse.png differ diff --git a/Content/Sprites/Controls/PC/MouseLeft.png b/Content/Sprites/Controls/PC/MouseLeft.png new file mode 100644 index 00000000..9ca5d22b Binary files /dev/null and b/Content/Sprites/Controls/PC/MouseLeft.png differ diff --git a/Content/Sprites/Controls/PC/MouseMiddle.png b/Content/Sprites/Controls/PC/MouseMiddle.png new file mode 100644 index 00000000..440c2fa5 Binary files /dev/null and b/Content/Sprites/Controls/PC/MouseMiddle.png differ diff --git a/Content/Sprites/Controls/PC/MouseMove.png b/Content/Sprites/Controls/PC/MouseMove.png new file mode 100644 index 00000000..8219b0ba Binary files /dev/null and b/Content/Sprites/Controls/PC/MouseMove.png differ diff --git a/Content/Sprites/Controls/PC/MouseRight.png b/Content/Sprites/Controls/PC/MouseRight.png new file mode 100644 index 00000000..3c1c9591 Binary files /dev/null and b/Content/Sprites/Controls/PC/MouseRight.png differ diff --git a/Content/Sprites/Controls/PC/MouseScroll.png b/Content/Sprites/Controls/PC/MouseScroll.png new file mode 100644 index 00000000..2c1ed1d5 Binary files /dev/null and b/Content/Sprites/Controls/PC/MouseScroll.png differ diff --git a/Content/Sprites/Controls/PC/MouseX.png b/Content/Sprites/Controls/PC/MouseX.png new file mode 100644 index 00000000..0b38e0eb Binary files /dev/null and b/Content/Sprites/Controls/PC/MouseX.png differ diff --git a/Content/Sprites/Controls/PC/MouseY.png b/Content/Sprites/Controls/PC/MouseY.png new file mode 100644 index 00000000..170f4622 Binary files /dev/null and b/Content/Sprites/Controls/PC/MouseY.png differ diff --git a/Content/Sprites/Controls/PC/N.png b/Content/Sprites/Controls/PC/N.png new file mode 100644 index 00000000..41f13363 Binary files /dev/null and b/Content/Sprites/Controls/PC/N.png differ diff --git a/Content/Sprites/Controls/PC/Numlock.png b/Content/Sprites/Controls/PC/Numlock.png new file mode 100644 index 00000000..78528e55 Binary files /dev/null and b/Content/Sprites/Controls/PC/Numlock.png differ diff --git a/Content/Sprites/Controls/PC/O.png b/Content/Sprites/Controls/PC/O.png new file mode 100644 index 00000000..ec18de8c Binary files /dev/null and b/Content/Sprites/Controls/PC/O.png differ diff --git a/Content/Sprites/Controls/PC/Option.png b/Content/Sprites/Controls/PC/Option.png new file mode 100644 index 00000000..bea912fc Binary files /dev/null and b/Content/Sprites/Controls/PC/Option.png differ diff --git a/Content/Sprites/Controls/PC/P.png b/Content/Sprites/Controls/PC/P.png new file mode 100644 index 00000000..3cc24045 Binary files /dev/null and b/Content/Sprites/Controls/PC/P.png differ diff --git a/Content/Sprites/Controls/PC/PageDown.png b/Content/Sprites/Controls/PC/PageDown.png new file mode 100644 index 00000000..79ee3729 Binary files /dev/null and b/Content/Sprites/Controls/PC/PageDown.png differ diff --git a/Content/Sprites/Controls/PC/PageUp.png b/Content/Sprites/Controls/PC/PageUp.png new file mode 100644 index 00000000..b79dd519 Binary files /dev/null and b/Content/Sprites/Controls/PC/PageUp.png differ diff --git a/Content/Sprites/Controls/PC/Period.png b/Content/Sprites/Controls/PC/Period.png new file mode 100644 index 00000000..56f478c0 Binary files /dev/null and b/Content/Sprites/Controls/PC/Period.png differ diff --git a/Content/Sprites/Controls/PC/PrintScreen.png b/Content/Sprites/Controls/PC/PrintScreen.png new file mode 100644 index 00000000..e65ba4a1 Binary files /dev/null and b/Content/Sprites/Controls/PC/PrintScreen.png differ diff --git a/Content/Sprites/Controls/PC/Question.png b/Content/Sprites/Controls/PC/Question.png new file mode 100644 index 00000000..d50b3976 Binary files /dev/null and b/Content/Sprites/Controls/PC/Question.png differ diff --git a/Content/Sprites/Controls/PC/Quote.png b/Content/Sprites/Controls/PC/Quote.png new file mode 100644 index 00000000..86cc155a Binary files /dev/null and b/Content/Sprites/Controls/PC/Quote.png differ diff --git a/Content/Sprites/Controls/PC/Return.png b/Content/Sprites/Controls/PC/Return.png new file mode 100644 index 00000000..4cbc61d3 Binary files /dev/null and b/Content/Sprites/Controls/PC/Return.png differ diff --git a/Content/Sprites/Controls/PC/Right.png b/Content/Sprites/Controls/PC/Right.png new file mode 100644 index 00000000..144c3abc Binary files /dev/null and b/Content/Sprites/Controls/PC/Right.png differ diff --git a/Content/Sprites/Controls/PC/RightAlt.png b/Content/Sprites/Controls/PC/RightAlt.png new file mode 100644 index 00000000..1f664cda Binary files /dev/null and b/Content/Sprites/Controls/PC/RightAlt.png differ diff --git a/Content/Sprites/Controls/PC/RightBracket.png b/Content/Sprites/Controls/PC/RightBracket.png new file mode 100644 index 00000000..f64d1f63 Binary files /dev/null and b/Content/Sprites/Controls/PC/RightBracket.png differ diff --git a/Content/Sprites/Controls/PC/RightControl.png b/Content/Sprites/Controls/PC/RightControl.png new file mode 100644 index 00000000..13810938 Binary files /dev/null and b/Content/Sprites/Controls/PC/RightControl.png differ diff --git a/Content/Sprites/Controls/PC/RightShift.png b/Content/Sprites/Controls/PC/RightShift.png new file mode 100644 index 00000000..58940419 Binary files /dev/null and b/Content/Sprites/Controls/PC/RightShift.png differ diff --git a/Content/Sprites/Controls/PC/Semicolon.png b/Content/Sprites/Controls/PC/Semicolon.png new file mode 100644 index 00000000..971c10af Binary files /dev/null and b/Content/Sprites/Controls/PC/Semicolon.png differ diff --git a/Content/Sprites/Controls/PC/Slash.png b/Content/Sprites/Controls/PC/Slash.png new file mode 100644 index 00000000..535aa23b Binary files /dev/null and b/Content/Sprites/Controls/PC/Slash.png differ diff --git a/Content/Sprites/Controls/PC/Space.png b/Content/Sprites/Controls/PC/Space.png new file mode 100644 index 00000000..8de87a46 Binary files /dev/null and b/Content/Sprites/Controls/PC/Space.png differ diff --git a/Content/Sprites/Controls/PC/Tab.png b/Content/Sprites/Controls/PC/Tab.png new file mode 100644 index 00000000..a9124b69 Binary files /dev/null and b/Content/Sprites/Controls/PC/Tab.png differ diff --git a/Content/Sprites/Controls/PC/Tilde.png b/Content/Sprites/Controls/PC/Tilde.png new file mode 100644 index 00000000..ec915256 Binary files /dev/null and b/Content/Sprites/Controls/PC/Tilde.png differ diff --git a/Content/Sprites/Controls/PC/Up.png b/Content/Sprites/Controls/PC/Up.png new file mode 100644 index 00000000..944adfe6 Binary files /dev/null and b/Content/Sprites/Controls/PC/Up.png differ diff --git a/Content/Sprites/Controls/PC/cancel.png b/Content/Sprites/Controls/PC/cancel.png deleted file mode 100644 index 170b55c5..00000000 Binary files a/Content/Sprites/Controls/PC/cancel.png and /dev/null differ diff --git a/Content/Sprites/Controls/PC/confirm.png b/Content/Sprites/Controls/PC/confirm.png deleted file mode 100644 index d87b2d92..00000000 Binary files a/Content/Sprites/Controls/PC/confirm.png and /dev/null differ diff --git a/Content/Sprites/Controls/PC/copyfile.png b/Content/Sprites/Controls/PC/copyfile.png deleted file mode 100644 index 7bbde29b..00000000 Binary files a/Content/Sprites/Controls/PC/copyfile.png and /dev/null differ diff --git a/Content/Sprites/Controls/PC/createfile.png b/Content/Sprites/Controls/PC/createfile.png deleted file mode 100644 index eb2fadff..00000000 Binary files a/Content/Sprites/Controls/PC/createfile.png and /dev/null differ diff --git a/Content/Sprites/Controls/PC/deletefile.png b/Content/Sprites/Controls/PC/deletefile.png deleted file mode 100644 index fb97b637..00000000 Binary files a/Content/Sprites/Controls/PC/deletefile.png and /dev/null differ diff --git a/Content/Sprites/Controls/PC/pause.png b/Content/Sprites/Controls/PC/pause.png deleted file mode 100644 index df735308..00000000 Binary files a/Content/Sprites/Controls/PC/pause.png and /dev/null differ diff --git a/Content/Sprites/Controls/PC/q.png b/Content/Sprites/Controls/PC/q.png new file mode 100644 index 00000000..ac8a10db Binary files /dev/null and b/Content/Sprites/Controls/PC/q.png differ diff --git a/Content/Sprites/Controls/PC/r.png b/Content/Sprites/Controls/PC/r.png new file mode 100644 index 00000000..57ee08b9 Binary files /dev/null and b/Content/Sprites/Controls/PC/r.png differ diff --git a/Content/Sprites/Controls/PC/s.png b/Content/Sprites/Controls/PC/s.png new file mode 100644 index 00000000..2c16d711 Binary files /dev/null and b/Content/Sprites/Controls/PC/s.png differ diff --git a/Content/Sprites/Controls/PC/t.png b/Content/Sprites/Controls/PC/t.png new file mode 100644 index 00000000..c122913f Binary files /dev/null and b/Content/Sprites/Controls/PC/t.png differ diff --git a/Content/Sprites/Controls/PC/u.png b/Content/Sprites/Controls/PC/u.png new file mode 100644 index 00000000..6dc351c1 Binary files /dev/null and b/Content/Sprites/Controls/PC/u.png differ diff --git a/Content/Sprites/Controls/PC/v.png b/Content/Sprites/Controls/PC/v.png new file mode 100644 index 00000000..ffe6d9eb Binary files /dev/null and b/Content/Sprites/Controls/PC/v.png differ diff --git a/Content/Sprites/Controls/PC/w.png b/Content/Sprites/Controls/PC/w.png new file mode 100644 index 00000000..c79a4537 Binary files /dev/null and b/Content/Sprites/Controls/PC/w.png differ diff --git a/Content/Sprites/Controls/PC/x.png b/Content/Sprites/Controls/PC/x.png new file mode 100644 index 00000000..fdf7e5d6 Binary files /dev/null and b/Content/Sprites/Controls/PC/x.png differ diff --git a/Content/Sprites/Controls/PC/y.png b/Content/Sprites/Controls/PC/y.png new file mode 100644 index 00000000..e8469b02 Binary files /dev/null and b/Content/Sprites/Controls/PC/y.png differ diff --git a/Content/Sprites/Controls/PC/z.png b/Content/Sprites/Controls/PC/z.png new file mode 100644 index 00000000..575a424b Binary files /dev/null and b/Content/Sprites/Controls/PC/z.png differ diff --git a/Content/Sprites/Controls/PlayStation 4/copyfile.png b/Content/Sprites/Controls/PlayStation 4/copyfile.png deleted file mode 100644 index 931dfb2d..00000000 Binary files a/Content/Sprites/Controls/PlayStation 4/copyfile.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation 4/deletefile.png b/Content/Sprites/Controls/PlayStation 4/deletefile.png deleted file mode 100644 index 4e91c3a1..00000000 Binary files a/Content/Sprites/Controls/PlayStation 4/deletefile.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation 4/pause.png b/Content/Sprites/Controls/PlayStation 4/pause.png deleted file mode 100644 index e489e997..00000000 Binary files a/Content/Sprites/Controls/PlayStation 4/pause.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation 5/cancel.png b/Content/Sprites/Controls/PlayStation 5/cancel.png deleted file mode 100644 index 4729bb60..00000000 Binary files a/Content/Sprites/Controls/PlayStation 5/cancel.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation 5/confirm.png b/Content/Sprites/Controls/PlayStation 5/confirm.png deleted file mode 100644 index 2dc8d215..00000000 Binary files a/Content/Sprites/Controls/PlayStation 5/confirm.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation 5/copyfile.png b/Content/Sprites/Controls/PlayStation 5/copyfile.png deleted file mode 100644 index 931dfb2d..00000000 Binary files a/Content/Sprites/Controls/PlayStation 5/copyfile.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation 5/createfile.png b/Content/Sprites/Controls/PlayStation 5/createfile.png deleted file mode 100644 index 4bed134a..00000000 Binary files a/Content/Sprites/Controls/PlayStation 5/createfile.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation 5/deletefile.png b/Content/Sprites/Controls/PlayStation 5/deletefile.png deleted file mode 100644 index 4e91c3a1..00000000 Binary files a/Content/Sprites/Controls/PlayStation 5/deletefile.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation 5/pause.png b/Content/Sprites/Controls/PlayStation 5/pause.png deleted file mode 100644 index 5887078b..00000000 Binary files a/Content/Sprites/Controls/PlayStation 5/pause.png and /dev/null differ diff --git a/Content/Sprites/Controls/PlayStation/Back.png b/Content/Sprites/Controls/PlayStation/Back.png new file mode 100644 index 00000000..2465af22 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/Back.png differ diff --git a/Content/Sprites/Controls/PlayStation/ButtonDown.png b/Content/Sprites/Controls/PlayStation/ButtonDown.png new file mode 100644 index 00000000..1cfc6602 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/ButtonDown.png differ diff --git a/Content/Sprites/Controls/PlayStation/ButtonLeft.png b/Content/Sprites/Controls/PlayStation/ButtonLeft.png new file mode 100644 index 00000000..20834332 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/ButtonLeft.png differ diff --git a/Content/Sprites/Controls/PlayStation/ButtonRight.png b/Content/Sprites/Controls/PlayStation/ButtonRight.png new file mode 100644 index 00000000..ade49bb2 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/ButtonRight.png differ diff --git a/Content/Sprites/Controls/PlayStation/ButtonUp.png b/Content/Sprites/Controls/PlayStation/ButtonUp.png new file mode 100644 index 00000000..3e01c93d Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/ButtonUp.png differ diff --git a/Content/Sprites/Controls/PlayStation 4/cancel.png b/Content/Sprites/Controls/PlayStation/East.png similarity index 100% rename from Content/Sprites/Controls/PlayStation 4/cancel.png rename to Content/Sprites/Controls/PlayStation/East.png diff --git a/Content/Sprites/Controls/PlayStation/LeftShoulder.png b/Content/Sprites/Controls/PlayStation/LeftShoulder.png new file mode 100644 index 00000000..b737fcac Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftShoulder.png differ diff --git a/Content/Sprites/Controls/PlayStation/LeftStick.png b/Content/Sprites/Controls/PlayStation/LeftStick.png new file mode 100644 index 00000000..e04bbf39 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftStick.png differ diff --git a/Content/Sprites/Controls/PlayStation/LeftTriggerPositive.png b/Content/Sprites/Controls/PlayStation/LeftTriggerPositive.png new file mode 100644 index 00000000..428f8b05 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftTriggerPositive.png differ diff --git a/Content/Sprites/Controls/PlayStation/LeftX.png b/Content/Sprites/Controls/PlayStation/LeftX.png new file mode 100644 index 00000000..7d3ea538 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftX.png differ diff --git a/Content/Sprites/Controls/PlayStation/LeftXNegative.png b/Content/Sprites/Controls/PlayStation/LeftXNegative.png new file mode 100644 index 00000000..0fa8948d Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftXNegative.png differ diff --git a/Content/Sprites/Controls/PlayStation/LeftXPositive.png b/Content/Sprites/Controls/PlayStation/LeftXPositive.png new file mode 100644 index 00000000..53df6eb8 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftXPositive.png differ diff --git a/Content/Sprites/Controls/PlayStation/LeftY.png b/Content/Sprites/Controls/PlayStation/LeftY.png new file mode 100644 index 00000000..b7277227 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftY.png differ diff --git a/Content/Sprites/Controls/PlayStation/LeftYNegative.png b/Content/Sprites/Controls/PlayStation/LeftYNegative.png new file mode 100644 index 00000000..297a4355 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftYNegative.png differ diff --git a/Content/Sprites/Controls/PlayStation/LeftYPositive.png b/Content/Sprites/Controls/PlayStation/LeftYPositive.png new file mode 100644 index 00000000..9f998e6f Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/LeftYPositive.png differ diff --git a/Content/Sprites/Controls/PlayStation 4/createfile.png b/Content/Sprites/Controls/PlayStation/North.png similarity index 100% rename from Content/Sprites/Controls/PlayStation 4/createfile.png rename to Content/Sprites/Controls/PlayStation/North.png diff --git a/Content/Sprites/Controls/PlayStation/PS3_Select.png b/Content/Sprites/Controls/PlayStation/PS3_Select.png new file mode 100644 index 00000000..36df2ecf Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/PS3_Select.png differ diff --git a/Content/Sprites/Controls/PlayStation/PS3_Start.png b/Content/Sprites/Controls/PlayStation/PS3_Start.png new file mode 100644 index 00000000..a4937585 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/PS3_Start.png differ diff --git a/Content/Sprites/Controls/PlayStation/PS4_Select.png b/Content/Sprites/Controls/PlayStation/PS4_Select.png new file mode 100644 index 00000000..e3e44f42 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/PS4_Select.png differ diff --git a/Content/Sprites/Controls/PlayStation/PS4_Start.png b/Content/Sprites/Controls/PlayStation/PS4_Start.png new file mode 100644 index 00000000..24fefabd Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/PS4_Start.png differ diff --git a/Content/Sprites/Controls/PlayStation/RIghtTriggerPositive.png b/Content/Sprites/Controls/PlayStation/RIghtTriggerPositive.png new file mode 100644 index 00000000..d8125f90 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RIghtTriggerPositive.png differ diff --git a/Content/Sprites/Controls/PlayStation/RIghtX.png b/Content/Sprites/Controls/PlayStation/RIghtX.png new file mode 100644 index 00000000..7bf05473 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RIghtX.png differ diff --git a/Content/Sprites/Controls/PlayStation/RightShoulder.png b/Content/Sprites/Controls/PlayStation/RightShoulder.png new file mode 100644 index 00000000..c8fef141 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RightShoulder.png differ diff --git a/Content/Sprites/Controls/PlayStation/RightStick.png b/Content/Sprites/Controls/PlayStation/RightStick.png new file mode 100644 index 00000000..bdeaf51f Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RightStick.png differ diff --git a/Content/Sprites/Controls/PlayStation/RightXNegative.png b/Content/Sprites/Controls/PlayStation/RightXNegative.png new file mode 100644 index 00000000..86418acd Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RightXNegative.png differ diff --git a/Content/Sprites/Controls/PlayStation/RightXPositive.png b/Content/Sprites/Controls/PlayStation/RightXPositive.png new file mode 100644 index 00000000..43814dcf Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RightXPositive.png differ diff --git a/Content/Sprites/Controls/PlayStation/RightY.png b/Content/Sprites/Controls/PlayStation/RightY.png new file mode 100644 index 00000000..95d07d35 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RightY.png differ diff --git a/Content/Sprites/Controls/PlayStation/RightYNegative.png b/Content/Sprites/Controls/PlayStation/RightYNegative.png new file mode 100644 index 00000000..5d8541f0 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RightYNegative.png differ diff --git a/Content/Sprites/Controls/PlayStation/RightYPositive.png b/Content/Sprites/Controls/PlayStation/RightYPositive.png new file mode 100644 index 00000000..0e6e24ae Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/RightYPositive.png differ diff --git a/Content/Sprites/Controls/PlayStation/Select.png b/Content/Sprites/Controls/PlayStation/Select.png new file mode 100644 index 00000000..396e2c9d Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/Select.png differ diff --git a/Content/Sprites/Controls/PlayStation 4/confirm.png b/Content/Sprites/Controls/PlayStation/South.png similarity index 100% rename from Content/Sprites/Controls/PlayStation 4/confirm.png rename to Content/Sprites/Controls/PlayStation/South.png diff --git a/Content/Sprites/Controls/PlayStation/Start.png b/Content/Sprites/Controls/PlayStation/Start.png new file mode 100644 index 00000000..82d87055 Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/Start.png differ diff --git a/Content/Sprites/Controls/PlayStation/West.png b/Content/Sprites/Controls/PlayStation/West.png new file mode 100644 index 00000000..006f920a Binary files /dev/null and b/Content/Sprites/Controls/PlayStation/West.png differ diff --git a/Content/Sprites/Controls/Xbox Series/Back.png b/Content/Sprites/Controls/Xbox Series/Back.png new file mode 100644 index 00000000..f5d56147 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/Back.png differ diff --git a/Content/Sprites/Controls/Xbox Series/ButtonDown.png b/Content/Sprites/Controls/Xbox Series/ButtonDown.png new file mode 100644 index 00000000..3e31fb01 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/ButtonDown.png differ diff --git a/Content/Sprites/Controls/Xbox Series/ButtonLeft.png b/Content/Sprites/Controls/Xbox Series/ButtonLeft.png new file mode 100644 index 00000000..aa06a8a2 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/ButtonLeft.png differ diff --git a/Content/Sprites/Controls/Xbox Series/ButtonRight.png b/Content/Sprites/Controls/Xbox Series/ButtonRight.png new file mode 100644 index 00000000..dc3f1fd0 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/ButtonRight.png differ diff --git a/Content/Sprites/Controls/Xbox Series/ButtonUp.png b/Content/Sprites/Controls/Xbox Series/ButtonUp.png new file mode 100644 index 00000000..99ed9b8a Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/ButtonUp.png differ diff --git a/Content/Sprites/Controls/Xbox Series/cancel.png b/Content/Sprites/Controls/Xbox Series/East.png similarity index 100% rename from Content/Sprites/Controls/Xbox Series/cancel.png rename to Content/Sprites/Controls/Xbox Series/East.png diff --git a/Content/Sprites/Controls/Xbox Series/copyfile.png b/Content/Sprites/Controls/Xbox Series/LeftShoulder.png similarity index 100% rename from Content/Sprites/Controls/Xbox Series/copyfile.png rename to Content/Sprites/Controls/Xbox Series/LeftShoulder.png diff --git a/Content/Sprites/Controls/Xbox Series/LeftStick.png b/Content/Sprites/Controls/Xbox Series/LeftStick.png new file mode 100644 index 00000000..e04bbf39 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/LeftStick.png differ diff --git a/Content/Sprites/Controls/Xbox Series/LeftTriggerPositive.png b/Content/Sprites/Controls/Xbox Series/LeftTriggerPositive.png new file mode 100644 index 00000000..f649d877 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/LeftTriggerPositive.png differ diff --git a/Content/Sprites/Controls/Xbox Series/LeftX.png b/Content/Sprites/Controls/Xbox Series/LeftX.png new file mode 100644 index 00000000..7d3ea538 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/LeftX.png differ diff --git a/Content/Sprites/Controls/Xbox Series/LeftXNegative.png b/Content/Sprites/Controls/Xbox Series/LeftXNegative.png new file mode 100644 index 00000000..0fa8948d Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/LeftXNegative.png differ diff --git a/Content/Sprites/Controls/Xbox Series/LeftXPositive.png b/Content/Sprites/Controls/Xbox Series/LeftXPositive.png new file mode 100644 index 00000000..53df6eb8 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/LeftXPositive.png differ diff --git a/Content/Sprites/Controls/Xbox Series/LeftY.png b/Content/Sprites/Controls/Xbox Series/LeftY.png new file mode 100644 index 00000000..b7277227 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/LeftY.png differ diff --git a/Content/Sprites/Controls/Xbox Series/LeftYNegative.png b/Content/Sprites/Controls/Xbox Series/LeftYNegative.png new file mode 100644 index 00000000..297a4355 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/LeftYNegative.png differ diff --git a/Content/Sprites/Controls/Xbox Series/LeftYPositive.png b/Content/Sprites/Controls/Xbox Series/LeftYPositive.png new file mode 100644 index 00000000..184733d5 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/LeftYPositive.png differ diff --git a/Content/Sprites/Controls/Xbox Series/createfile.png b/Content/Sprites/Controls/Xbox Series/North.png similarity index 100% rename from Content/Sprites/Controls/Xbox Series/createfile.png rename to Content/Sprites/Controls/Xbox Series/North.png diff --git a/Content/Sprites/Controls/Xbox Series/deletefile.png b/Content/Sprites/Controls/Xbox Series/RightShoulder.png similarity index 100% rename from Content/Sprites/Controls/Xbox Series/deletefile.png rename to Content/Sprites/Controls/Xbox Series/RightShoulder.png diff --git a/Content/Sprites/Controls/Xbox Series/RightStick.png b/Content/Sprites/Controls/Xbox Series/RightStick.png new file mode 100644 index 00000000..bdeaf51f Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/RightStick.png differ diff --git a/Content/Sprites/Controls/Xbox Series/RightTriggerPositive.png b/Content/Sprites/Controls/Xbox Series/RightTriggerPositive.png new file mode 100644 index 00000000..0c49d7d1 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/RightTriggerPositive.png differ diff --git a/Content/Sprites/Controls/Xbox Series/RightX.png b/Content/Sprites/Controls/Xbox Series/RightX.png new file mode 100644 index 00000000..7bf05473 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/RightX.png differ diff --git a/Content/Sprites/Controls/Xbox Series/RightXNegative.png b/Content/Sprites/Controls/Xbox Series/RightXNegative.png new file mode 100644 index 00000000..86418acd Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/RightXNegative.png differ diff --git a/Content/Sprites/Controls/Xbox Series/RightXPositive.png b/Content/Sprites/Controls/Xbox Series/RightXPositive.png new file mode 100644 index 00000000..43814dcf Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/RightXPositive.png differ diff --git a/Content/Sprites/Controls/Xbox Series/RightY.png b/Content/Sprites/Controls/Xbox Series/RightY.png new file mode 100644 index 00000000..95d07d35 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/RightY.png differ diff --git a/Content/Sprites/Controls/Xbox Series/RightYNegative.png b/Content/Sprites/Controls/Xbox Series/RightYNegative.png new file mode 100644 index 00000000..5d8541f0 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/RightYNegative.png differ diff --git a/Content/Sprites/Controls/Xbox Series/RightYPositive.png b/Content/Sprites/Controls/Xbox Series/RightYPositive.png new file mode 100644 index 00000000..0e6e24ae Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/RightYPositive.png differ diff --git a/Content/Sprites/Controls/Xbox Series/Select.png b/Content/Sprites/Controls/Xbox Series/Select.png new file mode 100644 index 00000000..4c11c269 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/Select.png differ diff --git a/Content/Sprites/Controls/Xbox Series/confirm.png b/Content/Sprites/Controls/Xbox Series/South.png similarity index 100% rename from Content/Sprites/Controls/Xbox Series/confirm.png rename to Content/Sprites/Controls/Xbox Series/South.png diff --git a/Content/Sprites/Controls/Xbox Series/Start.png b/Content/Sprites/Controls/Xbox Series/Start.png new file mode 100644 index 00000000..f6f1ce27 Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/Start.png differ diff --git a/Content/Sprites/Controls/Xbox Series/West.png b/Content/Sprites/Controls/Xbox Series/West.png new file mode 100644 index 00000000..368fa45e Binary files /dev/null and b/Content/Sprites/Controls/Xbox Series/West.png differ diff --git a/Content/Sprites/Controls/Xbox Series/pause.png b/Content/Sprites/Controls/Xbox Series/pause.png deleted file mode 100644 index 4f4c719b..00000000 Binary files a/Content/Sprites/Controls/Xbox Series/pause.png and /dev/null differ diff --git a/Content/Text/English.json b/Content/Text/English.json index d57bb2b4..e68fd0cf 100644 --- a/Content/Text/English.json +++ b/Content/Text/English.json @@ -14,6 +14,9 @@ "Create": "Create", "Delete": "Delete", "Copy": "Copy", + "Bind": "Bind", + "Reset": "Reset", + "Clear": "Clear", "RestartLevel": "Clear Save & Restart Level", "PauseTitle": "Paused", "PauseResume": "Resume", @@ -53,6 +56,41 @@ "OptionsToggleOff": "OFF", "OptionsYes": "Yes", "OptionsNo": "No", + + "KeyboardConfig": "Keyboard Config", + "KeyboardConfig.desc": "Rebind Keyboard/Mouse Controls", + "ControllerConfig": "Controller Config", + "ControllerConfig.desc": "Rebind Controller Controls", + "ControlsHeaderGame": "Game", + "ControlsHeaderMenu": "Menu (Requires at least 1 binding)", + "ControlsHeaderAdvanced": "Advanced", + "ControlsResetToDefault": "Reset To Default", + "PressToBind": "Press any button to bind it to \"{0}\"", + "Jump": "Jump", + "Dash": "Dash", + "Climb": "Climb", + "Pause": "Pause", + "MoveDown": "Move Down", + "MoveUp": "Move Up", + "MoveRight": "Move Right", + "MoveLeft": "Move Left", + "CameraDown": "Camera Down", + "CameraUp": "Camera Up", + "CameraRight": "Camera Right", + "CameraLeft": "Camera Left", + "CreateFile": "Create File", + "CopyFile": "Create File", + "DeleteFile": "Delete File", + "ResetBindings": "Reset Bindings", + "ClearBindings": "Clear Bindings", + "MenuDown": "Menu Down", + "MenuUp": "Menu Up", + "MenuRight": "Menu Right", + "MenuLeft": "Menu Left", + "DebugMenu": "Debug Menu", + "FullScreen": "Full Screen", + "ReloadAssets": "Reload Assets", + "Skin": "Skin", "Skin.desc": "Choose a Skin to Wear", "SaveCreateFile": "Create new file?", diff --git a/Source/Data/Controls.cs b/Source/Data/Controls.cs index 64517e0b..6c014e7e 100644 --- a/Source/Data/Controls.cs +++ b/Source/Data/Controls.cs @@ -1,5 +1,4 @@ using Celeste64.Mod; -using System.Text.Json; namespace Celeste64; @@ -17,6 +16,12 @@ public static class Controls public static readonly VirtualButton CopyFile = new("CopyFile"); public static readonly VirtualButton DeleteFile = new("DeleteFile"); public static readonly VirtualButton CreateFile = new("CreateFile"); + public static readonly VirtualButton ResetBindings = new("ResetBindings"); + public static readonly VirtualButton ClearBindings = new("ClearBindings"); + public static readonly VirtualButton DebugMenu = new("DebugMenu"); + public static readonly VirtualButton Restart = new("Restart"); + public static readonly VirtualButton FullScreen = new("FullScreen"); + public static readonly VirtualButton ReloadAssets = new("ReloadAssets"); public static ControlsConfig_V01 Instance = new(); @@ -43,19 +48,208 @@ internal static void LoadControlsByFileName(string file_name) if (controls == null) { - controls = ControlsConfig_V01.Defaults; - using var stream = File.Create(controlsFile); - JsonSerializer.Serialize(stream, ControlsConfig_V01.Defaults, ControlsConfig_V01Context.Default.ControlsConfig_V01); - stream.Flush(); + controls = new ControlsConfig_V01(); + } + + // Add any default actions and sticks that are missing. + foreach (var defaultAction in ControlsConfig_V01.Defaults.Actions) + { + if (!controls.Actions.ContainsKey(defaultAction.Key)) + { + controls.Actions[defaultAction.Key] = [.. defaultAction.Value]; + } + } + foreach (var defaultStick in ControlsConfig_V01.Defaults.Sticks) + { + if (!controls.Sticks.ContainsKey(defaultStick.Key)) + { + controls.Sticks[defaultStick.Key] = new ControlsConfigStick() + { + Up = [.. defaultStick.Value.Up], + Down = [.. defaultStick.Value.Down], + Left = [.. defaultStick.Value.Left], + Right = [.. defaultStick.Value.Right], + Deadzone = defaultStick.Value.Deadzone + }; + } } Instance = controls; LoadConfig(Instance); } - public static void LoadConfig(ControlsConfig_V01? config = null) + [DisallowHooks] + internal static void AddBinding(VirtualButton virtualButton, Keys key) + { + var bindings = GetButtonBindings(Instance, virtualButton); + if (bindings != null && !bindings.Any(a => a.Key == key)) + { + bindings.Add(new(key)); + bindings.Last().BindTo(virtualButton); + } + Consume(); + } + + [DisallowHooks] + internal static void AddBinding(VirtualButton virtualButton, Buttons button) + { + var bindings = GetButtonBindings(Instance, virtualButton); + if (bindings != null && !bindings.Any(a => a.Button == button)) + { + bindings.Add(new(button)); + bindings.Last().BindTo(virtualButton); + } + Consume(); + } + + [DisallowHooks] + internal static void AddBinding(VirtualButton virtualButton, MouseButtons mouseButton) + { + var bindings = GetButtonBindings(Instance, virtualButton); + if (bindings != null && !bindings.Any(a => a.MouseButton == mouseButton)) + { + bindings.Add(new(mouseButton)); + bindings.Last().BindTo(virtualButton); + } + Consume(); + } + + [DisallowHooks] + internal static void AddBinding(VirtualButton virtualButton, Axes axis, bool inverted, float deadzone = 0.0f) + { + var bindings = GetButtonBindings(Instance, virtualButton); + if (bindings != null && !bindings.Any(a => a.Axis == axis && a.AxisInverted == inverted)) + { + bindings.Add(new(axis, deadzone, inverted)); + bindings.Last().BindTo(virtualButton); + } + Consume(); + } + + [DisallowHooks] + internal static void ClearBinding(VirtualButton virtualButton, bool forController, bool requiresBinding = false) + { + var bindings = GetButtonBindings(Instance, virtualButton); + if (bindings != null) + { + virtualButton.Clear(); + // Remove all bindings for this control type + if (requiresBinding) + { + var toRemove = bindings.Where(x => x.IsForController() == forController).SkipLast(1).ToList(); + foreach (var removeItem in toRemove) + { + bindings.Remove(removeItem); + } + } + else + { + bindings.RemoveAll(x => x.IsForController() == forController); + } + + // Rebind remaining bindings. + // Needed to rebind other control type, i.e. to not lose controller binds when clearing for keyboard. + foreach (var binding in bindings) + { + binding.BindTo(virtualButton); + } + } + } + + [DisallowHooks] + internal static void ResetBinding(VirtualButton virtualButton, bool forController) + { + var bindings = GetButtonBindings(Instance, virtualButton); + var defaultBindings = GetButtonBindings(ControlsConfig_V01.Defaults, virtualButton); + if (bindings != null && defaultBindings != null) + { + virtualButton.Clear(); + + // Remove all bindings for this control type + bindings.RemoveAll(x => x.IsForController() == forController); + + // Readd all default bindings for this control type + foreach (var binding in defaultBindings) + { + if (binding.IsForController() == forController) + { + bindings.Add(binding); + } + } + + // Rebind bindings to the virtual button. + foreach (var binding in bindings) + { + binding.BindTo(virtualButton); + } + } + } + + [DisallowHooks] + internal static void ResetAllBindings(bool forController) + { + // Remove all bindings for this control type + foreach (var action in Instance.Actions) + { + Instance.Actions[action.Key].RemoveAll(x => x.IsForController() == forController); + } + foreach (var action in Instance.Sticks) + { + Instance.Sticks[action.Key].Up.RemoveAll(x => x.IsForController() == forController); + Instance.Sticks[action.Key].Down.RemoveAll(x => x.IsForController() == forController); + Instance.Sticks[action.Key].Left.RemoveAll(x => x.IsForController() == forController); + Instance.Sticks[action.Key].Right.RemoveAll(x => x.IsForController() == forController); + } + + // Readd default bindings for this control type. + foreach (var action in ControlsConfig_V01.Defaults.Actions) + { + foreach (var binding in action.Value) + { + if (binding.IsForController() == forController) + { + Instance.Actions[action.Key].Add(binding); + } + } + } + foreach (var stick in ControlsConfig_V01.Defaults.Sticks) + { + Instance.Sticks[stick.Key].Up.AddRange(stick.Value.Up.Where(b => b.IsForController() == forController)); + Instance.Sticks[stick.Key].Down.AddRange(stick.Value.Down.Where(b => b.IsForController() == forController)); + Instance.Sticks[stick.Key].Left.AddRange(stick.Value.Left.Where(b => b.IsForController() == forController)); + Instance.Sticks[stick.Key].Right.AddRange(stick.Value.Right.Where(b => b.IsForController() == forController)); + Instance.Sticks[stick.Key].Deadzone = stick.Value.Deadzone; + } + + // Reload bindings + LoadConfig(Instance); + } + + [DisallowHooks] + public static void SaveToFile() { - static ControlsConfigStick_V01 FindStick(ControlsConfig_V01? config, string name) + var savePath = Path.Join(App.UserPath, DefaultFileName); + var tempPath = Path.Join(App.UserPath, DefaultFileName + ".backup"); + + // first save to a temporary file + { + using var stream = File.Create(tempPath); + Instance.Serialize(stream, Instance); + stream.Flush(); + } + + // validate that the temp path worked, and overwrite existing if it did. + if (File.Exists(tempPath) && + Instance.Deserialize(File.ReadAllText(tempPath)) != null) + { + File.Copy(tempPath, savePath, true); + } + } + + [DisallowHooks] + internal static void LoadConfig(ControlsConfig_V01? config = null) + { + static ControlsConfigStick FindStick(ControlsConfig_V01? config, string name) { if (config != null && config.Sticks.TryGetValue(name, out var stick)) return stick; @@ -64,7 +258,7 @@ static ControlsConfigStick_V01 FindStick(ControlsConfig_V01? config, string name throw new Exception($"Missing Stick Binding for '{name}'"); } - static List FindAction(ControlsConfig_V01? config, string name) + static List FindAction(ControlsConfig_V01? config, string name) { if (config != null && config.Actions.TryGetValue(name, out var action)) return action; @@ -97,7 +291,18 @@ static List FindAction(ControlsConfig_V01? config, st it.BindTo(DeleteFile); foreach (var it in FindAction(config, "CreateFile")) it.BindTo(CreateFile); - + foreach (var it in FindAction(config, "ResetBindings")) + it.BindTo(ResetBindings); + foreach (var it in FindAction(config, "ClearBindings")) + it.BindTo(ClearBindings); + foreach (var it in FindAction(config, "DebugMenu")) + it.BindTo(DebugMenu); + foreach (var it in FindAction(config, "Restart")) + it.BindTo(Restart); + foreach (var it in FindAction(config, "FullScreen")) + it.BindTo(FullScreen); + foreach (var it in FindAction(config, "ReloadAssets")) + it.BindTo(ReloadAssets); } public static void Clear() @@ -114,6 +319,12 @@ public static void Clear() CopyFile.Clear(); DeleteFile.Clear(); CreateFile.Clear(); + ResetBindings.Clear(); + ClearBindings.Clear(); + DebugMenu.Clear(); + Restart.Clear(); + FullScreen.Clear(); + ReloadAssets.Clear(); } public static void Consume() @@ -130,54 +341,220 @@ public static void Consume() CopyFile.Consume(); DeleteFile.Consume(); CreateFile.Consume(); + ResetBindings.Consume(); + ClearBindings.Consume(); + DebugMenu.Consume(); + Restart.Consume(); + FullScreen.Consume(); + ReloadAssets.Consume(); } private static readonly Dictionary> prompts = []; - private static string GetControllerName(Gamepads pad) => pad switch + /// + /// Get the icon texture for the first binding of a virtual button. + /// + /// Virtual button to get an icon for. + /// + public static Subtexture GetPrompt(VirtualButton button) { - Gamepads.DualShock4 => "PlayStation 4", - Gamepads.DualSense => "PlayStation 5", - Gamepads.Nintendo => "Nintendo Switch", - Gamepads.Xbox => "Xbox Series", - _ => "Xbox Series", - }; + return Assets.Subtextures.GetValueOrDefault(GetPromptLocations(button, Input.Controllers.Any() && Input.Controllers[0].Connected).FirstOrDefault("")); + } - private static string GetPromptLocation(string name) + /// + /// Get icon textures for all bindings for a virtual button. + /// + /// Virtual button to get icons for. + /// True if we should show controller icons. False to show keyboard icons. + /// + public static List GetPrompts(VirtualButton button, bool isForController) { + List subtextures = []; + foreach (var location in GetPromptLocations(button, isForController)) + subtextures.Add(Assets.Subtextures.GetValueOrDefault(location)); + return subtextures; + } + + private static List GetPromptLocations(VirtualButton button, bool isForController) + { + List locations = []; var gamepad = Input.Controllers[0]; var deviceTypeName = gamepad.Connected ? GetControllerName(gamepad.Gamepad) : "PC"; - if (!prompts.TryGetValue(deviceTypeName, out var list)) - prompts[deviceTypeName] = list = []; + var config = GetButtonBindings(Instance, button); + + if (config != null) + { + foreach (var binding in config) + { + if (binding.IsForController() != isForController) + { + continue; + } + + string promptDeviceTypeName = "PC"; + var buttonName = binding.GetBindingName(); + if (binding.IsForController()) + promptDeviceTypeName = GetControllerName(gamepad.Gamepad); + + if (!prompts.TryGetValue(deviceTypeName, out var list)) + prompts[deviceTypeName] = list = []; + + buttonName = GetButtonOverrides(binding, buttonName, gamepad.Gamepad); - if (!list.TryGetValue(name, out var lookup)) - list[name] = lookup = $"Controls/{deviceTypeName}/{name}"; + if (!list.TryGetValue(buttonName, out var lookup)) + list[buttonName] = lookup = $"Controls/{promptDeviceTypeName}/{buttonName}"; - return lookup; + if (Gamepads.Nintendo.Equals(binding.NotFor) || !Gamepads.Nintendo.Equals(binding.OnlyFor) || (binding.NotFor == null && binding.OnlyFor == null)) //only non switch prompts atm + locations.Add(lookup); + } + } + + return locations; } - public static string GetPromptLocation(VirtualButton button) + private static List? GetButtonBindings(ControlsConfig_V01 config, VirtualButton virtualButton) { - // TODO: instead, query the button's actual bindings and look up a - // texture based on that! no time tho - if (button == Confirm) - return GetPromptLocation("confirm"); - else if (button == Cancel) - return GetPromptLocation("cancel"); - else if (button == CreateFile) - return GetPromptLocation("createfile"); - else if (button == DeleteFile) - return GetPromptLocation("deletefile"); - else if (button == CopyFile) - return GetPromptLocation("copyfile"); - else - return GetPromptLocation("pause"); + string[] nameParts = virtualButton.Name.Split("/"); + + if (nameParts.Length == 3) + { + var stickConfig = config.Sticks[nameParts[0]]; + + if (stickConfig != null) + { + if (nameParts[1] == "Horizontal" && nameParts[2] == "Positive") + { + return stickConfig.Right; + } + else if (nameParts[1] == "Horizontal" && nameParts[2] == "Negative") + { + return stickConfig.Left; + } + else if (nameParts[1] == "Vertical" && nameParts[2] == "Negative") + { + return stickConfig.Up; + } + else if (nameParts[1] == "Vertical" && nameParts[2] == "Positive") + { + return stickConfig.Down; + } + } + } + else if (config.Actions.ContainsKey(virtualButton.Name)) + { + return config.Actions[virtualButton.Name]; + } + + return null; } - public static Subtexture GetPrompt(VirtualButton button) + /// + /// Gets a controller name for a gamepad. This is used to determine which folder we pull icons from. + /// PS4 and PS5 are shared, since most of their icons are the same. This is different from how this was handled in vanilla Celeste 64, which had them split. + /// + /// + /// + private static string GetControllerName(Gamepads pad) => pad switch { - return Assets.Subtextures.GetValueOrDefault(GetPromptLocation(button)); + Gamepads.DualShock4 => "PlayStation", + Gamepads.DualSense => "PlayStation", + Gamepads.Nintendo => "Nintendo Switch", + Gamepads.Xbox => "Xbox Series", + _ => "Xbox Series", + }; + + /// + /// Used for special cases where there may be multiple options for a button, like with different control types. + /// + /// + /// + /// + private static string GetButtonOverrides(ControlsConfigBinding binding, string buttonName, Gamepads gamepadType) + { + // Mouse buttons need special names, since Left and Right could refer to Keyboard Left or Mouse Left. + switch (binding.MouseButton) + { + case MouseButtons.Left: + buttonName = "MouseLeft"; + break; + case MouseButtons.Right: + buttonName = "MouseRight"; + break; + case MouseButtons.Middle: + buttonName = "MouseMiddle"; + break; + default: + break; + } + + // This is needed to workaround 2 problems with how foster's buttons are set up. + // Normally, these can get their name from the enum name directly, but these have some special cases. + // The first issue is that the face buttons have multiple enum entries for the same value, with some being obsolete. We need to force it to get the correct name. + // The second issue is that Select and Back seem to currently be swapped. So we account for this here. If this gets fixed in foster, we should remove the last 2 cases. + switch (binding.Button) + { + case Buttons.South: + buttonName = "South"; + break; + case Buttons.East: + buttonName = "East"; + break; + case Buttons.West: + buttonName = "West"; + break; + case Buttons.North: + buttonName = "North"; + break; + case Buttons.Up: + buttonName = "ButtonUp"; + break; + case Buttons.Down: + buttonName = "ButtonDown"; + break; + case Buttons.Left: + buttonName = "ButtonLeft"; + break; + case Buttons.Right: + buttonName = "ButtonRight"; + break; + case Buttons.Select: + buttonName = "Back"; + break; + case Buttons.Back: + buttonName = "Select"; + break; + default: + break; + } + + // PS4 has unique icons separate from PS5. We account for that here. + if (gamepadType == Gamepads.DualShock4) + { + if (buttonName == "Start") + { + buttonName = "PS4_Start"; + } + else if (buttonName == "Select") + { + buttonName = "PS4_Select"; + } + } + + // Separate MacOS special names from Windows. + if (OperatingSystem.IsMacOS()) + { + if (buttonName == "LeftAlt" || buttonName == "RightAlt") + { + buttonName = "Option"; + } + else if (buttonName == "LeftOS" || buttonName == "RightOS") + { + buttonName = "Command"; + } + } + + return buttonName; } } diff --git a/Source/Data/PersistedData/ControlsConfigBinding_V01.cs b/Source/Data/PersistedData/ControlsConfigBinding.cs similarity index 55% rename from Source/Data/PersistedData/ControlsConfigBinding_V01.cs rename to Source/Data/PersistedData/ControlsConfigBinding.cs index afd1bc47..2eec032b 100644 --- a/Source/Data/PersistedData/ControlsConfigBinding_V01.cs +++ b/Source/Data/PersistedData/ControlsConfigBinding.cs @@ -1,13 +1,10 @@ using System.Text.Json; using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; namespace Celeste64; -public sealed class ControlsConfigBinding_V01 : PersistedData +public sealed class ControlsConfigBinding { - public override int Version => 1; - public Keys? Key { get; set; } public MouseButtons? MouseButton { get; set; } public Buttons? Button { get; set; } @@ -17,11 +14,11 @@ public sealed class ControlsConfigBinding_V01 : PersistedData public Gamepads? OnlyFor { get; set; } public Gamepads? NotFor { get; set; } - public ControlsConfigBinding_V01() { } - public ControlsConfigBinding_V01(Keys input) => Key = input; - public ControlsConfigBinding_V01(MouseButtons input) => MouseButton = input; - public ControlsConfigBinding_V01(Buttons input) => Button = input; - public ControlsConfigBinding_V01(Axes input, float deadzone, bool inverted) + public ControlsConfigBinding() { } + public ControlsConfigBinding(Keys input) => Key = input; + public ControlsConfigBinding(MouseButtons input) => MouseButton = input; + public ControlsConfigBinding(Buttons input) => Button = input; + public ControlsConfigBinding(Axes input, float deadzone, bool inverted) { Axis = input; AxisDeadzone = deadzone; @@ -65,38 +62,58 @@ public void BindTo(VirtualButton button) button.Add(Condition, 0, Axis.Value, AxisInverted ? -1 : 1, AxisDeadzone); } - public override JsonTypeInfo GetTypeInfo() + public string GetBindingName() + { + if (Key != null) + return Key.ToString() ?? ""; + if (Button != null) + return Button.ToString() ?? ""; + if (Axis != null) + return Axis.ToString() + (AxisInverted ? "Negative" : "Positive"); + if (MouseButton != null) + return MouseButton.ToString() ?? ""; + return ""; + } + + public bool IsForController() { - return ControlsConfigBinding_V01Context.Default.ControlsConfigBinding_V01; + if (Key != null) + return false; + if (Button != null) + return true; + if (Axis != null) + return true; + if (MouseButton != null) + return false; + return false; } } -public class ControlsConfigBinding_V01Converter : JsonConverter +public class ControlsConfigBinding_Converter : JsonConverter { - public override ControlsConfigBinding_V01? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ControlsConfigBinding? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using (var jsonDoc = JsonDocument.ParseValue(ref reader)) { - return new ControlsConfigBinding_V01().Deserialize(jsonDoc.RootElement.GetRawText()); + return JsonSerializer.Deserialize(jsonDoc.RootElement.GetRawText(), ControlsConfigBindingContext.Default.ControlsConfigBinding); } } - public override void Write(Utf8JsonWriter writer, ControlsConfigBinding_V01 value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ControlsConfigBinding value, JsonSerializerOptions options) { // All of this is just so the Binding values are on a single line to increase readability var data = "\n" + new string(' ', writer.CurrentDepth * 2) + - JsonSerializer.Serialize(value, ControlsConfigBinding_V01Context.Default.ControlsConfigBinding_V01); + JsonSerializer.Serialize(value, ControlsConfigBindingContext.Default.ControlsConfigBinding); writer.WriteRawValue(data); } } [JsonSourceGenerationOptions( - WriteIndented = true, UseStringEnumConverter = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, AllowTrailingCommas = true )] -[JsonSerializable(typeof(ControlsConfigBinding_V01))] -internal partial class ControlsConfigBinding_V01Context : JsonSerializerContext { } +[JsonSerializable(typeof(ControlsConfigBinding))] +internal partial class ControlsConfigBindingContext : JsonSerializerContext { } diff --git a/Source/Data/PersistedData/ControlsConfigStick.cs b/Source/Data/PersistedData/ControlsConfigStick.cs new file mode 100644 index 00000000..a5fb1781 --- /dev/null +++ b/Source/Data/PersistedData/ControlsConfigStick.cs @@ -0,0 +1,37 @@ +using System.Text.Json.Serialization; + +namespace Celeste64; + +public sealed class ControlsConfigStick +{ + public float Deadzone { get; set; } = 0; + public List Up { get; set; } = []; + public List Down { get; set; } = []; + public List Left { get; set; } = []; + public List Right { get; set; } = []; + + public void BindTo(VirtualStick stick) + { + stick.CircularDeadzone = Deadzone; + stick.Horizontal.OverlapBehaviour = VirtualAxis.Overlaps.TakeNewer; + stick.Vertical.OverlapBehaviour = VirtualAxis.Overlaps.TakeNewer; + foreach (var it in Up) + it.BindTo(stick.Vertical.Negative); + foreach (var it in Down) + it.BindTo(stick.Vertical.Positive); + foreach (var it in Left) + it.BindTo(stick.Horizontal.Negative); + foreach (var it in Right) + it.BindTo(stick.Horizontal.Positive); + } +} + +[JsonSourceGenerationOptions( + WriteIndented = true, + UseStringEnumConverter = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, + Converters = [typeof(ControlsConfigBinding_Converter)], + AllowTrailingCommas = true +)] +[JsonSerializable(typeof(ControlsConfigStick))] +internal partial class ControlsConfigStickContext : JsonSerializerContext { } diff --git a/Source/Data/PersistedData/ControlsConfigStick_V01.cs b/Source/Data/PersistedData/ControlsConfigStick_V01.cs deleted file mode 100644 index e9b78fa0..00000000 --- a/Source/Data/PersistedData/ControlsConfigStick_V01.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace Celeste64; - -public sealed class ControlsConfigStick_V01 : PersistedData -{ - public override int Version => 1; - - public float Deadzone { get; set; } = 0; - public List Up { get; set; } = []; - public List Down { get; set; } = []; - public List Left { get; set; } = []; - public List Right { get; set; } = []; - - public void BindTo(VirtualStick stick) - { - stick.CircularDeadzone = Deadzone; - stick.Horizontal.OverlapBehaviour = VirtualAxis.Overlaps.TakeNewer; - stick.Vertical.OverlapBehaviour = VirtualAxis.Overlaps.TakeNewer; - foreach (var it in Up) - it.BindTo(stick.Vertical.Negative); - foreach (var it in Down) - it.BindTo(stick.Vertical.Positive); - foreach (var it in Left) - it.BindTo(stick.Horizontal.Negative); - foreach (var it in Right) - it.BindTo(stick.Horizontal.Positive); - } - - public override JsonTypeInfo GetTypeInfo() - { - return ControlsConfigStick_V01Context.Default.ControlsConfigStick_V01; - } -} - -public class ControlsConfigStick_V01Converter : JsonConverter -{ - public override ControlsConfigStick_V01? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - using (var jsonDoc = JsonDocument.ParseValue(ref reader)) - { - return new ControlsConfigStick_V01().Deserialize(jsonDoc.RootElement.GetRawText()); - } - } - - public override void Write(Utf8JsonWriter writer, ControlsConfigStick_V01 value, JsonSerializerOptions options) - { - value.Serialize(writer, value); - } -} - -[JsonSourceGenerationOptions( - WriteIndented = true, - UseStringEnumConverter = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, - AllowTrailingCommas = true -)] -[JsonSerializable(typeof(ControlsConfigStick_V01))] -internal partial class ControlsConfigStick_V01Context : JsonSerializerContext { } diff --git a/Source/Data/PersistedData/ControlsConfig_V01.cs b/Source/Data/PersistedData/ControlsConfig_V01.cs index a66250d9..c8375f75 100644 --- a/Source/Data/PersistedData/ControlsConfig_V01.cs +++ b/Source/Data/PersistedData/ControlsConfig_V01.cs @@ -7,8 +7,8 @@ public class ControlsConfig_V01 : PersistedData { public override int Version => 1; - public Dictionary> Actions { get; set; } = []; - public Dictionary Sticks { get; set; } = []; + public Dictionary> Actions { get; set; } = []; + public Dictionary Sticks { get; set; } = []; public static ControlsConfig_V01 Defaults = new() { @@ -45,8 +45,8 @@ public class ControlsConfig_V01 : PersistedData new(Buttons.South) { OnlyFor = Gamepads.Nintendo }, ], ["Pause"] = [ - new(Keys.Enter), new(Keys.Escape), + new(Keys.Enter), new(Buttons.Start), new(Buttons.Select), new(Buttons.Back) @@ -63,6 +63,26 @@ public class ControlsConfig_V01 : PersistedData new(Keys.B), new(Buttons.North) ], + ["ResetBindings"] = [ + new(Keys.V), + new(Buttons.LeftShoulder) + ], + ["ClearBindings"] = [ + new(Keys.B), + new(Buttons.North) + ], + ["FullScreen"] = [ + new(Keys.F4), + ], + ["ReloadAssets"] = [ + new(Keys.F5), + ], + ["DebugMenu"] = [ + new(Keys.F6), + ], + ["Restart"] = [ + new(Keys.R), + ], }, Sticks = new() @@ -149,7 +169,7 @@ public override JsonTypeInfo GetTypeInfo() UseStringEnumConverter = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, AllowTrailingCommas = true, - Converters = [typeof(ControlsConfigBinding_V01Converter), typeof(ControlsConfigStick_V01Converter)] + Converters = [typeof(ControlsConfigBinding_Converter)] )] [JsonSerializable(typeof(ControlsConfig_V01))] internal partial class ControlsConfig_V01Context : JsonSerializerContext { } diff --git a/Source/Game.cs b/Source/Game.cs index e133e15f..d23a2d56 100644 --- a/Source/Game.cs +++ b/Source/Game.cs @@ -202,14 +202,6 @@ private void HandleError(Exception e) public override void Update() { - if (Input.Keyboard.Pressed(Keys.F5) && !IsMidTransition) - { - if (Scene is Startup || Scene is GameErrorMessage) return; - - Log.Info($"--- User has initiated a{(Input.Keyboard.CtrlOrCommand ? " full" : String.Empty)} manual reload. ---"); - ReloadAssets(Input.Keyboard.CtrlOrCommand); // F5 - Reload changed; Ctrl + F5 - Reload all - } - if (IsDynamicRes) { if (Height_old != Height || Width_old != Width) @@ -221,8 +213,6 @@ public override void Update() Width_old = Width; } - imGuiManager.UpdateHandlers(); - scenes.TryPeek(out var scene); // gets the top scene // update top scene @@ -248,6 +238,8 @@ public override void Update() HandleError(e); } + imGuiManager.UpdateHandlers(); + // handle transitions if (transitionStep == TransitionStep.FadeOut) { @@ -296,6 +288,7 @@ public override void Update() { Save.SaveToFile(); Settings.SaveToFile(); + Controls.SaveToFile(); ModSettings.SaveToFile(); } @@ -481,11 +474,17 @@ public override void Update() } - if (scene is not Celeste64.Startup) + if (scene is not Celeste64.Startup && Scene is not GameErrorMessage) { - // toggle fullsrceen - if ((Input.Keyboard.Alt && Input.Keyboard.Pressed(Keys.Enter)) || Input.Keyboard.Pressed(Keys.F4)) + // toggle fullscreen + if (Controls.FullScreen.ConsumePress()) Settings.ToggleFullscreen(); + + if (Controls.ReloadAssets.ConsumePress() && !IsMidTransition) + { + Log.Info($"--- User has initiated a{(Input.Keyboard.CtrlOrCommand ? " full" : string.Empty)} manual reload. ---"); + ReloadAssets(Input.Keyboard.CtrlOrCommand); // F5 - Reload changed; Ctrl + F5 - Reload all + } } } diff --git a/Source/Helpers/Menu.cs b/Source/Helpers/Menu.cs index 6aee868b..1d52901e 100644 --- a/Source/Helpers/Menu.cs +++ b/Source/Helpers/Menu.cs @@ -4,7 +4,7 @@ public class Menu { public static float Spacing => 4 * Game.RelativeScale; public static float SpacerHeight => 12 * Game.RelativeScale; - public const float TitleScale = 0.75f; + protected float TitleScale = 0.75f; public abstract class Item { @@ -41,6 +41,46 @@ public Item Describe(Loc.Localized description) } } + /// + /// Represents a Input Binding Menu Item. + /// This will show all the current bindings for a virtual buttons, and if you click it, it will open a binding menu to add new bindings. + /// + /// Localization key for label that shows. Shold represent the button name. + /// Virtual button we are trying to bind to + /// Root menu for the menu we are currently in + /// True if this is a controller binding, false for keyboard + public class InputBind(Loc.Localized locString, VirtualButton button, Menu? rootMenu, bool isForController) : Item + { + public override Loc.Localized? LocString => locString; + + public float DeadZone; + public bool RequiresBinding; + public bool IsForController => isForController; + public override bool Pressed() + { + Audio.Play(Sfx.ui_select); + rootMenu?.PushSubMenu(new BindControlMenu(rootMenu, button, locString, isForController, DeadZone)); + return true; + } + + public virtual List GetTextures() + { + return Controls.GetPrompts(button, isForController); + } + + public VirtualButton GetButton() + { + return button; + } + } + + /// + /// Represents a submenu menu item. + /// When pressed, it will open the given submenu + /// + /// Localization key for the submenu label + /// Root Menu for the menu that we are currently inside of. + /// Submenu to open when clicked. public class Submenu(Loc.Localized locString, Menu? rootMenu, Menu? submenu = null) : Item { public override Loc.Localized? LocString => locString; @@ -59,11 +99,17 @@ public override bool Pressed() } } + /// + /// Spacer menu item to allow a row of separation between menu items. + /// public class Spacer : Item { public override bool Selectable => false; } + /// + /// Basic slider component that allows you to pick an int value between a min and a max. + /// public class Slider : Item { private readonly List labels = []; @@ -87,12 +133,21 @@ public Slider(Loc.Localized locString, int min, int max, Func get, Action set(Calc.Clamp(get() + dir, min, max)); } + /// + /// Represents a subheader menu item. + /// This allows a basic text only entry in a list of menu items to represent a subheader for a group of items. + /// + /// public class SubHeader(Loc.Localized locString) : Item { public override Loc.Localized? LocString => locString; public override bool Selectable { get; } = false; } + /// + /// Menu item that allows you to pick an item from a list of strings. + /// Note: For static lists of options, a Multiselect using an enum is recommended. This item is more for dynamic lists where the options can change dynamically. + /// public class OptionList : Item { private readonly int min; @@ -137,6 +192,11 @@ private int getId() } } + /// + /// Basic menu item that will run an action when clicked + /// + /// Localization key for Option Label + /// Action to run when pressed public class Option(Loc.Localized locString, Action? action = null) : Item { public override Loc.Localized? LocString => locString; @@ -153,6 +213,12 @@ public override bool Pressed() } } + /// + /// Menu item representing a basic boolean value that can be turned off and on. + /// + /// Localization key for Toggle Label + /// action to run when pressed. This should be where you set the boolean value + /// function that gets the current state of the boolean value public class Toggle(Loc.Localized locString, Action action, Func get) : Item { private string labelOff => $"{locString} : {Loc.Str("OptionsToggleOff")}"; @@ -172,6 +238,13 @@ public override bool Pressed() } } + /// + /// Multiselect component that lets you pick a list of strings. + /// + /// + /// + /// + /// public class MultiSelect(Loc.Localized locString, List options, Func get, Action set) : Item { public override Loc.Localized LocString => locString; @@ -190,6 +263,13 @@ public override void Slide(int dir) } } + /// + /// Multiselect component that lets you pick a value from an enum. + /// + /// + /// + /// + /// public class MultiSelect(Loc.Localized locString, Action set, Func get) : MultiSelect(locString, GetEnumOptions(), () => (int)(object)get(), i => set((T)(object)i)) where T : struct, Enum @@ -365,7 +445,7 @@ protected virtual void HandleInput() step = -1; Index += step; - while (!items[(items.Count + Index) % items.Count].Selectable) + while (step != 0 && !items[(items.Count + Index) % items.Count].Selectable) Index += step; Index = (items.Count + Index) % items.Count; @@ -392,6 +472,20 @@ protected virtual void HandleInput() if (Controls.Confirm.Pressed && items[Index].Pressed()) Controls.Consume(); + + if (items[Index] is InputBind bind) + { + if (Controls.ResetBindings.ConsumePress()) + { + // Reset current binding to it's default value + Controls.ResetBinding(bind.GetButton(), bind.IsForController); + } + else if (Controls.ClearBindings.ConsumePress()) + { + // Clear current binding so there are no bindings (Or 1 binding if RequiredBinding flag is true, where it will keep just the last binding) + Controls.ClearBinding(bind.GetButton(), bind.IsForController, bind.RequiresBinding); + } + } } } @@ -456,6 +550,25 @@ protected virtual void RenderItems(Batcher batch) batch.PopMatrix(); position.Y += font.LineHeight; } + else if (items[i] is InputBind) + { + UI.Text(batch, text, position, new Vec2(1.0f, 0), color); + + InputBind item = (InputBind)items[i]; + var textures = item.GetTextures(); + foreach (var texture in textures) + { + batch.PushMatrix( + Matrix3x2.CreateScale(TitleScale) * + Matrix3x2.CreateTranslation(position)); + position.X += (24 * Game.RelativeScale); + UI.Icon(batch, texture, "", Vec2.Zero); + batch.PopMatrix(); + } + position.Y += font.LineHeight; + position.Y += Spacing; + position.X = 0; + } else { UI.Text(batch, text, position, justify, color); @@ -465,20 +578,20 @@ protected virtual void RenderItems(Batcher batch) } batch.PopMatrix(); - // Render a scrolbar if there are too many items to show on screen at once + // Render a scrollbar if there are too many items to show on screen at once if (showScrollbar && items.Count > maxItemsCount) { // TODO: This will need to be redone if we implement mouse support and want it to interact with menus. - int padding = 4; - int scrollSize = 16; - int xPos = Game.Width - scrollSize - padding; - int scrollBarHeight = Game.Height - (scrollSize * 2) - padding * 4; - int scrollStartPos = padding * 2 + scrollSize; + float padding = 4 * Game.RelativeScale; + float scrollSize = 16 * Game.RelativeScale; + float xPos = Game.Width - scrollSize - padding; + float scrollBarHeight = Game.Height - (scrollSize * 2) - padding * 4; + float scrollStartPos = padding * 2 + scrollSize; batch.PushMatrix(Vec2.Zero, false); batch.Rect(new Rect(xPos, padding, scrollSize, scrollSize), Color.White); batch.Rect(new Rect(xPos, scrollStartPos, scrollSize, scrollBarHeight), Color.Gray); - int scrollYPos = (int)MathF.Ceiling(scrollStartPos + ((float)scrolledAmount * scrollBarHeight / items.Count)); - int scrollYHeight = scrollBarHeight * maxItemsCount / items.Count; + float scrollYPos = (int)MathF.Ceiling(scrollStartPos + (scrolledAmount * scrollBarHeight / items.Count)); + float scrollYHeight = scrollBarHeight * maxItemsCount / items.Count; batch.Rect(new Rect(xPos, scrollYPos, scrollSize, scrollYHeight), Color.White); batch.Rect(new Rect(xPos, Game.Height - scrollSize - padding, scrollSize, scrollSize), Color.White); batch.PopMatrix(); diff --git a/Source/Mod/ImGui/DebugActorMenu.cs b/Source/Mod/ImGui/DebugActorMenu.cs index 0ec43d82..79aee6c3 100644 --- a/Source/Mod/ImGui/DebugActorMenu.cs +++ b/Source/Mod/ImGui/DebugActorMenu.cs @@ -16,7 +16,7 @@ internal class DebugActorMenu : ImGuiHandler public override void Update() { - if (Input.Keyboard.Pressed(Keys.F6)) + if (Controls.DebugMenu.Pressed) { if (Visible) Visible = false; if (actorListWindowVisible) actorListWindowVisible = false; diff --git a/Source/Mod/ImGui/FujiDebugMenu.cs b/Source/Mod/ImGui/FujiDebugMenu.cs index b59b8581..dbac65c2 100644 --- a/Source/Mod/ImGui/FujiDebugMenu.cs +++ b/Source/Mod/ImGui/FujiDebugMenu.cs @@ -11,7 +11,7 @@ internal class FujiDebugMenu : ImGuiHandler public override void Update() { - if (Input.Keyboard.Pressed(Keys.F6)) + if (Controls.DebugMenu.Pressed) { visible = !visible; } diff --git a/Source/Mod/Menu/BindControlMenu.cs b/Source/Mod/Menu/BindControlMenu.cs new file mode 100644 index 00000000..12ab2237 --- /dev/null +++ b/Source/Mod/Menu/BindControlMenu.cs @@ -0,0 +1,96 @@ +namespace Celeste64; + +public class BindControlMenu : Menu +{ + private float inMenuTime = 0; + + private const float waitBeforeClosingTime = 5; + private VirtualButton button; + private bool isForController; + private float deadZone; + + public BindControlMenu(Menu? rootMenu, VirtualButton button, string buttonName, bool isForController, float deadZone = 0) + { + RootMenu = rootMenu; + + this.button = button; + this.deadZone = deadZone; + this.isForController = isForController; + Title = string.Format(Loc.Str("PressToBind"), buttonName); + TitleScale = 1; + + inMenuTime = 0; + } + + protected override void HandleInput() + { + // Only accept keys and mouse buttons if this is not for controller. Otherwise, only accept controller buttons and sticks + if (!isForController) + { + Keys? pressed = Input.Keyboard.FirstPressed(); + if (pressed != null && pressed != Keys.Unknown && pressed != Keys.Application && pressed != Keys.ScrollLock) // We don't have icons for these currently, so don't allow them + { + Controls.AddBinding(button, (Keys)pressed); + RootMenu?.PopSubMenu(); + return; + } + + // TODO: Maybe find a better way to do this? Foster doesn't expose a FirstPressed for mouse buttons. + foreach (var button in Enum.GetValues(typeof(MouseButtons))) + { + if (Input.Mouse.Pressed((MouseButtons)button)) + { + Controls.AddBinding(this.button, (MouseButtons)button); + RootMenu?.PopSubMenu(); + return; + } + } + } + else + { + // TODO: Maybe find a better way to do this? Foster doesn't expose a FirstPressed for buttons. + Controller? controller = Input.Controllers.FirstOrDefault(); + if (controller != null) + { + foreach (var button in Enum.GetValues(typeof(Buttons))) + { + if (controller.Pressed((Buttons)button)) + { + Controls.AddBinding(this.button, (Buttons)button); + RootMenu?.PopSubMenu(); + return; + } + } + + foreach (var axis in Enum.GetValues(typeof(Axes))) + { + if (Math.Abs(controller.Axis((Axes)axis)) > 0.5f) + { + Controls.AddBinding(this.button, (Axes)axis, controller.Axis((Axes)axis) < -0.5f, deadZone); + RootMenu?.PopSubMenu(); + return; + } + } + } + } + + // Closes automatically after seconds + // This is to prevent users from being forced to bind if they click by mistake. + inMenuTime += Time.Delta; + if (inMenuTime > waitBeforeClosingTime) + { + RootMenu?.PopSubMenu(); + } + } + + protected override void RenderItems(Batcher batch) + { + batch.PushMatrix(Matrix3x2.CreateScale(TitleScale)); + UI.Text(batch, Title, Vec2.Zero, new Vec2(0.5f, 0), Color.White); + batch.PopMatrix(); + + batch.PushMatrix(Matrix3x2.CreateScale(0.75f)); + UI.Text(batch, $"Menu will close automatically in {(int)Math.Ceiling(waitBeforeClosingTime - inMenuTime)}", new(0, Game.Height * 0.15f), new Vec2(0.5f, 0), Color.White); + batch.PopMatrix(); + } +} diff --git a/Source/Mod/Menu/ControlsMenu.cs b/Source/Mod/Menu/ControlsMenu.cs new file mode 100644 index 00000000..32c9cd4b --- /dev/null +++ b/Source/Mod/Menu/ControlsMenu.cs @@ -0,0 +1,101 @@ +namespace Celeste64; + +public class ControlsMenu : Menu +{ + private Target GameTarget; + + public override void Closed() + { + base.Closed(); + Controls.SaveToFile(); + } + + public override void Initialized() + { + // The first item is a subheader, so we need to start at 1 to account for this. + Index = 1; + } + + public ControlsMenu(Menu? rootMenu, bool isForController) + { + RootMenu = rootMenu; + + GameTarget = new Target(Game.Width, Game.Height); + Game.OnResolutionChanged += () => GameTarget = new Target(Game.Width, Game.Height); + + // Standard gameplay bindings. + Add(new SubHeader("ControlsHeaderGame")); + Add(new InputBind(Controls.Jump.Name, Controls.Jump, rootMenu, isForController)); + Add(new InputBind(Controls.Dash.Name, Controls.Dash, rootMenu, isForController)); + Add(new InputBind(Controls.Climb.Name, Controls.Climb, rootMenu, isForController)); + Add(new InputBind(Controls.Move.Name + "Up", Controls.Move.Vertical.Negative, rootMenu, isForController)); + Add(new InputBind(Controls.Move.Name + "Down", Controls.Move.Vertical.Positive, rootMenu, isForController)); + Add(new InputBind(Controls.Move.Name + "Left", Controls.Move.Horizontal.Negative, rootMenu, isForController)); + Add(new InputBind(Controls.Move.Name + "Right", Controls.Move.Horizontal.Positive, rootMenu, isForController)); + Add(new InputBind(Controls.Camera.Name + "Up", Controls.Camera.Vertical.Negative, rootMenu, isForController)); + Add(new InputBind(Controls.Camera.Name + "Down", Controls.Camera.Vertical.Positive, rootMenu, isForController)); + Add(new InputBind(Controls.Camera.Name + "Left", Controls.Camera.Horizontal.Negative, rootMenu, isForController)); + Add(new InputBind(Controls.Camera.Name + "Right", Controls.Camera.Horizontal.Positive, rootMenu, isForController)); + + // Menu Item bindings are special, because they must always have at least 1 binding, to prevent the user from getting stuck. + // This is what the RequiresBinding flag is for. + Add(new SubHeader("ControlsHeaderMenu")); + Add(new InputBind(Controls.Pause.Name, Controls.Pause, rootMenu, isForController) { RequiresBinding = true }); + Add(new InputBind(Controls.Confirm.Name, Controls.Confirm, rootMenu, isForController) { RequiresBinding = true }); + Add(new InputBind(Controls.Cancel.Name, Controls.Cancel, rootMenu, isForController) { RequiresBinding = true }); + Add(new InputBind(Controls.CopyFile.Name, Controls.CopyFile, rootMenu, isForController) { RequiresBinding = true }); + Add(new InputBind(Controls.CreateFile.Name, Controls.CreateFile, rootMenu, isForController) { RequiresBinding = true }); + Add(new InputBind(Controls.DeleteFile.Name, Controls.DeleteFile, rootMenu, isForController) { RequiresBinding = true }); + Add(new InputBind(Controls.ResetBindings.Name, Controls.ResetBindings, rootMenu, isForController) { RequiresBinding = true }); + Add(new InputBind(Controls.ClearBindings.Name, Controls.ClearBindings, rootMenu, isForController) { RequiresBinding = true }); + Add(new InputBind(Controls.Menu.Name + "Up", Controls.Menu.Vertical.Negative, rootMenu, isForController) { DeadZone = 0.5f, RequiresBinding = true }); + Add(new InputBind(Controls.Menu.Name + "Down", Controls.Menu.Vertical.Positive, rootMenu, isForController) { DeadZone = 0.5f, RequiresBinding = true }); + Add(new InputBind(Controls.Menu.Name + "Left", Controls.Menu.Horizontal.Negative, rootMenu, isForController) { DeadZone = 0.5f, RequiresBinding = true }); + Add(new InputBind(Controls.Menu.Name + "Right", Controls.Menu.Horizontal.Positive, rootMenu, isForController) { DeadZone = 0.5f, RequiresBinding = true }); + + // Advanced Bindings for special things that don't really fit into any category. + Add(new SubHeader("ControlsHeaderAdvanced")); + Add(new InputBind(Controls.FullScreen.Name, Controls.FullScreen, rootMenu, isForController)); + Add(new InputBind(Controls.ReloadAssets.Name, Controls.ReloadAssets, rootMenu, isForController)); + Add(new InputBind(Controls.DebugMenu.Name, Controls.DebugMenu, rootMenu, isForController)); + Add(new InputBind(Controls.Restart.Name, Controls.Restart, rootMenu, isForController)); + + Add(new Spacer()); + + // Reset all bindings for this control type to their default value. + Add(new Option("ControlsResetToDefault", () => + { + Controls.ResetAllBindings(isForController); + })); + + Add(new Option("Exit", () => + { + PopRootSubMenu(); + })); + } + + /// + /// Render bindings and input legend + /// + /// + protected override void RenderItems(Batcher batch) + { + batch.PushMatrix(new Vec2(GameTarget.Bounds.Center.X, GameTarget.Bounds.Center.Y - 16f), false); + base.RenderItems(batch); + batch.PopMatrix(); + + batch.PushMatrix(Vec2.Zero, false); + var at = GameTarget.Bounds.BottomRight + new Vec2(-32, -4) * Game.RelativeScale + new Vec2(0, -UI.PromptSize); + UI.Prompt(batch, Controls.Cancel, Loc.Str("Back"), at, out var width, 1.0f); + at.X -= width + 8 * Game.RelativeScale; + + UI.Prompt(batch, Controls.Confirm, Loc.Str("Bind"), at, out width, 1.0f); + at.X -= width + 8 * Game.RelativeScale; + + UI.Prompt(batch, Controls.CreateFile, Loc.Str("Clear"), at, out width, 1.0f); + at.X -= width + 8 * Game.RelativeScale; + + UI.Prompt(batch, Controls.CopyFile, Loc.Str("Reset"), at, out width, 1.0f); + batch.PopMatrix(); + } +} diff --git a/Source/Mod/Menu/GameOptionsMenu.cs b/Source/Mod/Menu/GameOptionsMenu.cs index 80a133d3..e965d842 100644 --- a/Source/Mod/Menu/GameOptionsMenu.cs +++ b/Source/Mod/Menu/GameOptionsMenu.cs @@ -5,12 +5,14 @@ namespace Celeste64; public class GameOptionsMenu : Menu { public Menu FujiOptionsMenu; - + public ControlsMenu KeyboardControlsMenu; + public ControlsMenu ControllerControlsMenu; public override void Closed() { base.Closed(); Settings.SaveToFile(); + Controls.SaveToFile(); } public GameOptionsMenu(Menu? rootMenu) @@ -18,6 +20,8 @@ public GameOptionsMenu(Menu? rootMenu) RootMenu = rootMenu; // Setup fuji options menu FujiOptionsMenu = new Menu { Title = Loc.Str("FujiOptions") }; + KeyboardControlsMenu = new ControlsMenu(rootMenu, false) { Title = Loc.Str("KeyboardConfig") }; + ControllerControlsMenu = new ControlsMenu(rootMenu, true) { Title = Loc.Str("ControllerConfig") }; FujiOptionsMenu.Add(new Toggle("FujiEnableDebugMenu", Settings.ToggleEnableDebugMenu, () => Settings.EnableDebugMenu)); FujiOptionsMenu.Add(new Toggle("FujiWriteLog", Settings.ToggleWriteLog, () => Settings.WriteLog)); @@ -32,7 +36,7 @@ public GameOptionsMenu(Menu? rootMenu) })); FujiOptionsMenu.Add(new Option("Exit", () => { - PopSubMenu(); + PopRootSubMenu(); })); // Setup this menu @@ -57,7 +61,14 @@ public GameOptionsMenu(Menu? rootMenu) Add(new Spacer()); Add(new Slider("OptionsBGM", 0, 10, () => Settings.MusicVolume, Settings.SetMusicVolume)); Add(new Slider("OptionsSFX", 0, 10, () => Settings.SfxVolume, Settings.SetSfxVolume)); + Add(new Spacer()); - Add(new Submenu("FujiOptions", this, FujiOptionsMenu)); + Add(new Submenu("KeyboardConfig", rootMenu, KeyboardControlsMenu)); + Add(new Submenu("ControllerConfig", rootMenu, ControllerControlsMenu)); + Add(new Submenu("FujiOptions", rootMenu, FujiOptionsMenu)); + Add(new Option("Exit", () => + { + PopRootSubMenu(); + })); } } diff --git a/Source/Scenes/World.cs b/Source/Scenes/World.cs index be46e224..70b583ea 100644 --- a/Source/Scenes/World.cs +++ b/Source/Scenes/World.cs @@ -384,16 +384,14 @@ public override void Update() { if (Paused) { + pauseMenu.Update(); + if (Controls.Pause.ConsumePress() || (pauseMenu.IsInMainMenu && Controls.Cancel.ConsumePress())) { pauseMenu.CloseSubMenus(); SetPaused(false); Audio.Play(Sfx.ui_unpause); } - else - { - pauseMenu.Update(); - } } if (Panicked) @@ -458,6 +456,16 @@ public override void Update() return; } + // Fuji Custom + // Quick Restart if the player presses the restart button. + if (Controls.Restart.ConsumePress() && Get() is { Dead: false } livingPlayer) + { + SetPaused(false); + Audio.StopBus(Sfx.bus_dialog, false); + livingPlayer?.Kill(); + return; + } + // ONLY update the player when dead if (Get() is { Dead: true } player) {