Button remapping work
This commit is contained in:
287
Zennysoft.Game.Ma/addons/input_helper/InputHelper.cs
Normal file
287
Zennysoft.Game.Ma/addons/input_helper/InputHelper.cs
Normal file
@@ -0,0 +1,287 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace NathanHoad
|
||||
{
|
||||
public static class InputHelper
|
||||
{
|
||||
public delegate void DeviceChangedEventHandler(string device, int deviceIndex);
|
||||
public delegate void KeyboardInputChangedEventHandler(string action, InputEvent input);
|
||||
public delegate void JoypadInputChangedEventHandler(string action, InputEvent input);
|
||||
public delegate void JoypadChangedEventHandler(int deviceIndex, bool isConnected);
|
||||
|
||||
public static DeviceChangedEventHandler? DeviceChanged;
|
||||
public static KeyboardInputChangedEventHandler? KeyboardInputChanged;
|
||||
public static JoypadInputChangedEventHandler? JoypadInputChanged;
|
||||
public static JoypadChangedEventHandler? JoypadChanged;
|
||||
|
||||
|
||||
public const string DEVICE_KEYBOARD = "keyboard";
|
||||
public const string DEVICE_XBOX_CONTROLLER = "xbox";
|
||||
public const string DEVICE_SWITCH_CONTROLLER = "switch";
|
||||
public const string DEVICE_PLAYSTATION_CONTROLLER = "playstation";
|
||||
public const string DEVICE_STEAMDECK_CONTROLLER = "steamdeck";
|
||||
public const string DEVICE_GENERIC = "generic";
|
||||
|
||||
public const string SUB_DEVICE_XBOX_ONE_CONTROLLER = "xbox_one";
|
||||
public const string SUB_DEVICE_XBOX_SERIES_CONTROLLER = "xbox_series";
|
||||
public const string SUB_DEVICE_PLAYSTATION3_CONTROLLER = "playstation3";
|
||||
public const string SUB_DEVICE_PLAYSTATION4_CONTROLLER = "playstation4";
|
||||
public const string SUB_DEVICE_PLAYSTATION5_CONTROLLER = "playstation5";
|
||||
public const string SUB_DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER = "switch_left_joycon";
|
||||
public const string SUB_DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER = "switch_right_joycon";
|
||||
|
||||
|
||||
private static Node instance;
|
||||
public static Node Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = (Node)Engine.GetSingleton("InputHelper");
|
||||
instance.Connect("device_changed", Callable.From((string device, int deviceIndex) => DeviceChanged?.Invoke(device, deviceIndex)));
|
||||
instance.Connect("keyboard_input_changed", Callable.From((string action, InputEvent input) => KeyboardInputChanged?.Invoke(action, input)));
|
||||
instance.Connect("joypad_input_changed", Callable.From((string action, InputEvent input) => JoypadInputChanged?.Invoke(action, input)));
|
||||
instance.Connect("joypad_changed", Callable.From((int deviceIndex, bool isConnected) => JoypadChanged?.Invoke(deviceIndex, isConnected)));
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string Device
|
||||
{
|
||||
get => (string)Instance.Get("device");
|
||||
}
|
||||
|
||||
|
||||
public static int DeviceIndex
|
||||
{
|
||||
get => (int)Instance.Get("device_index");
|
||||
}
|
||||
|
||||
|
||||
public static string LastKnownJoypadDevice
|
||||
{
|
||||
get => (string)Instance.Get("last_known_joypad_device");
|
||||
}
|
||||
|
||||
|
||||
public static string LastKnownJoypadIndex
|
||||
{
|
||||
get => (string)Instance.Get("last_known_joypad_index");
|
||||
}
|
||||
|
||||
|
||||
public static float Deadzone
|
||||
{
|
||||
get => (float)Instance.Get("deadzone");
|
||||
set => Instance.Set("deadzone", value);
|
||||
}
|
||||
|
||||
|
||||
public static int MouseMotionThreshold
|
||||
{
|
||||
get => (int)Instance.Get("mouse_motion_threshold");
|
||||
set => Instance.Set("mouse_motion_threshold", value);
|
||||
}
|
||||
|
||||
|
||||
public static string GetSimplifiedDeviceName(string rawName)
|
||||
{
|
||||
return (string)Instance.Call("get_simplified_device_name", rawName);
|
||||
}
|
||||
|
||||
|
||||
public static string GetDeviceFromEvent(InputEvent @event)
|
||||
{
|
||||
return (string)Instance.Call("get_device_from_event", @event);
|
||||
}
|
||||
|
||||
|
||||
public static int GetDeviceIndexFromEvent(InputEvent @event)
|
||||
{
|
||||
return (int)Instance.Call("get_device_index_from_event", @event);
|
||||
}
|
||||
|
||||
|
||||
public static bool HasJoypad()
|
||||
{
|
||||
return (bool)Instance.Call("has_joypad");
|
||||
}
|
||||
|
||||
|
||||
public static string GuessDeviceName()
|
||||
{
|
||||
return (string)Instance.Call("guess_device_name");
|
||||
}
|
||||
|
||||
|
||||
public static void ResetAllActions()
|
||||
{
|
||||
Instance.Call("reset_all_actions");
|
||||
}
|
||||
|
||||
|
||||
public static void SetKeyboardOrJoypadInputForAction(string action, InputEvent input, bool swapIfTaken = true)
|
||||
{
|
||||
Instance.Call("set_keyboard_or_joypad_input_for_action", action, input, swapIfTaken);
|
||||
}
|
||||
|
||||
|
||||
public static InputEvent GetKeyboardOrJoypadInputForAction(string action, InputEvent input, bool swapIfTaken = true)
|
||||
{
|
||||
return (InputEvent)Instance.Call("get_keyboard_or_joypad_input_for_action", action, input, swapIfTaken);
|
||||
}
|
||||
|
||||
|
||||
public static Array<InputEvent> GetKeyboardOrJoypadInputsForAction(string action)
|
||||
{
|
||||
return (Array<InputEvent>)Instance.Call("get_keyboard_or_joypad_inputs_for_action", action);
|
||||
}
|
||||
|
||||
|
||||
public static string GetLabelForInput(InputEvent input)
|
||||
{
|
||||
return (string)Instance.Call("get_label_for_input", input);
|
||||
}
|
||||
|
||||
|
||||
public static string SerializeInputsForAction(string action)
|
||||
{
|
||||
return (string)Instance.Call("serialize_inputs_for_action", action);
|
||||
}
|
||||
|
||||
|
||||
public static string SerializeInputsForActions(Array<string> actions = null)
|
||||
{
|
||||
if (actions == null)
|
||||
{
|
||||
actions = new Array<string>();
|
||||
}
|
||||
return (string)Instance.Call("serialize_inputs_for_actions", actions);
|
||||
}
|
||||
|
||||
|
||||
public static void DeserializeInputsForAction(string action, string serializedInputs)
|
||||
{
|
||||
Instance.Call("desserialize_inputs_for_action", action, serializedInputs);
|
||||
}
|
||||
|
||||
public static void DeserializeInputsForActions(string serializedInputs)
|
||||
{
|
||||
Instance.Call("deserialize_inputs_for_actions", serializedInputs);
|
||||
}
|
||||
|
||||
|
||||
#region Keyboard/Mouse
|
||||
|
||||
public static Array<InputEvent> GetKeyboardInputsForAction(string action)
|
||||
{
|
||||
return (Array<InputEvent>)Instance.Call("get_keyboard_inputs_for_action", action);
|
||||
}
|
||||
|
||||
|
||||
public static InputEvent GetKeyboardInputForAction(string action)
|
||||
{
|
||||
return (InputEvent)Instance.Call("get_keyboard_input_for_action", action);
|
||||
}
|
||||
|
||||
|
||||
public static void SetKeyboardInputForAction(string action, InputEvent input, bool swapIfTaken = true)
|
||||
{
|
||||
Instance.Call("set_keyboard_input_for_action", action, input, swapIfTaken);
|
||||
}
|
||||
|
||||
|
||||
public static void ReplaceKeyboardInputForAction(string action, InputEvent currentInput, InputEvent input, bool swapIfTaken = true)
|
||||
{
|
||||
Instance.Call("replace_keyboard_input_for_action", action, currentInput, input, swapIfTaken);
|
||||
}
|
||||
|
||||
|
||||
public static void ReplaceKeyboardInputAtIndex(string action, int index, InputEvent input, bool swapIfTaken = true)
|
||||
{
|
||||
Instance.Call("replace_keyboard_input_at_index", action, index, input, swapIfTaken);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Joypad
|
||||
|
||||
public static Array<InputEvent> GetJoypadInputsForAction(string action)
|
||||
{
|
||||
return (Array<InputEvent>)Instance.Call("get_joypad_inputs_for_action", action);
|
||||
}
|
||||
|
||||
|
||||
public static InputEvent GetJoypadInputForAction(string action)
|
||||
{
|
||||
return (InputEvent)Instance.Call("get_joypad_input_for_action", action);
|
||||
}
|
||||
|
||||
|
||||
public static void SetJoypadInputForAction(string action, InputEvent input, bool swapIfTaken = true)
|
||||
{
|
||||
Instance.Call("set_joypad_input_for_action", action, input, swapIfTaken);
|
||||
}
|
||||
|
||||
|
||||
public static void ReplaceJoypadInputForAction(string action, InputEvent currentInput, InputEvent input, bool swapIfTaken = true)
|
||||
{
|
||||
Instance.Call("replace_joypad_input_for_action", action, currentInput, input, swapIfTaken);
|
||||
}
|
||||
|
||||
|
||||
public static void ReplaceJoypadInputAtIndex(string action, int index, InputEvent input, bool swapIfTaken = true)
|
||||
{
|
||||
Instance.Call("replace_joypad_input_at_index", action, index, input, swapIfTaken);
|
||||
}
|
||||
|
||||
|
||||
public static void RumbleSmall(int targetDevice = 0)
|
||||
{
|
||||
Instance.Call("rumble_small", targetDevice);
|
||||
}
|
||||
|
||||
|
||||
public static void RumbleMedium(int targetDevice = 0)
|
||||
{
|
||||
Instance.Call("rumble_medium", targetDevice);
|
||||
}
|
||||
|
||||
|
||||
public static void RumbleLarge(int targetDevice = 0)
|
||||
{
|
||||
Instance.Call("rumble_large", targetDevice);
|
||||
}
|
||||
|
||||
|
||||
public static void StartRumbleSmall(int targetDevice = 0)
|
||||
{
|
||||
Instance.Call("start_rumble_small", targetDevice);
|
||||
}
|
||||
|
||||
|
||||
public static void StartRumbleMedium(int targetDevice = 0)
|
||||
{
|
||||
Instance.Call("start_rumble_medium", targetDevice);
|
||||
}
|
||||
|
||||
|
||||
public static void StartRumbleLarge(int targetDevice = 0)
|
||||
{
|
||||
Instance.Call("start_rumble_large", targetDevice);
|
||||
}
|
||||
|
||||
|
||||
public static void StopRumble(int targetDevice = 0)
|
||||
{
|
||||
Instance.Call("stop_rumble", targetDevice);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1
Zennysoft.Game.Ma/addons/input_helper/InputHelper.cs.uid
Normal file
1
Zennysoft.Game.Ma/addons/input_helper/InputHelper.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cfs4dgwrfvb11
|
||||
21
Zennysoft.Game.Ma/addons/input_helper/LICENSE
Normal file
21
Zennysoft.Game.Ma/addons/input_helper/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-present Nathan Hoad
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
230
Zennysoft.Game.Ma/addons/input_helper/assets/update.svg
Normal file
230
Zennysoft.Game.Ma/addons/input_helper/assets/update.svg
Normal file
@@ -0,0 +1,230 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="300"
|
||||
height="80"
|
||||
viewBox="0 0 79.374999 21.166667"
|
||||
version="1.1"
|
||||
id="svg291"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
sodipodi:docname="update.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview293"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
width="1920px"
|
||||
units="px"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="121.625"
|
||||
inkscape:cy="43.25"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-x="2552"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showguides="true">
|
||||
<sodipodi:guide
|
||||
position="-15.575132,19.553027"
|
||||
orientation="0,-1"
|
||||
id="guide2089"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs288" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g2335"
|
||||
transform="matrix(0.93072355,0,0,0.92874157,17.062816,18.409989)"
|
||||
style="stroke-width:0.85374062;stroke-dasharray:none;stroke:none">
|
||||
<path
|
||||
id="rect2095"
|
||||
style="fill:#349684;fill-opacity:1;stroke:#152f2c;stroke-width:0.56916041;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:normal"
|
||||
d="m 24.364404,-16.012976 a 1.7811118,1.7811118 0 0 0 -1.568379,0.938961 1.7811118,1.7811118 0 0 0 -1.557528,-0.918291 1.7811118,1.7811118 0 0 0 -1.383895,0.660942 c -1.816891,-1.243302 -4.115395,-0.49791 -5.162476,1.678967 l -2.844787,5.914368 c -1.050638,2.1842718 -0.431729,4.957652 1.388029,6.2187419 1.819758,1.26108987 4.130958,0.5177068 5.181596,-1.666565 l 1.240234,-2.5786539 h 6.335531 l 1.240234,2.5786539 c 1.050638,2.1842718 3.361322,2.92765487 5.181079,1.666565 1.819758,-1.2610899 2.439184,-4.0344701 1.388546,-6.2187419 L 30.9578,-13.652397 c -1.050637,-2.184271 -3.361838,-2.927654 -5.181596,-1.666565 -1.97e-4,1.37e-4 -3.19e-4,3.8e-4 -5.16e-4,5.17e-4 a 1.7811118,1.7811118 0 0 0 -1.411284,-0.694531 z" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.85374062;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="rect2101"
|
||||
width="1.403165"
|
||||
height="4.2796535"
|
||||
x="17.394386"
|
||||
y="-13.427068"
|
||||
ry="0.75541198"
|
||||
rx="0.75380331" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.85374062;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="rect2103"
|
||||
width="1.403165"
|
||||
height="4.2796535"
|
||||
x="-11.988823"
|
||||
y="-20.235798"
|
||||
ry="0.75380331"
|
||||
rx="0.75541198"
|
||||
transform="rotate(90)" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.85374062;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="ellipse2105"
|
||||
cx="28.192503"
|
||||
cy="2.7587082"
|
||||
rx="0.82257289"
|
||||
ry="0.79261416"
|
||||
transform="rotate(-30)" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.85374062;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="ellipse2107"
|
||||
cx="28.216581"
|
||||
cy="4.7943339"
|
||||
rx="0.82257289"
|
||||
ry="0.79261416"
|
||||
transform="rotate(-30)" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.85374062;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="ellipse2109"
|
||||
cx="30.279696"
|
||||
cy="2.6987464"
|
||||
rx="0.82257289"
|
||||
ry="0.79261416"
|
||||
transform="rotate(-30)" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.85374062;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="ellipse2111"
|
||||
cx="30.320311"
|
||||
cy="4.767447"
|
||||
rx="0.82257289"
|
||||
ry="0.79261416"
|
||||
transform="rotate(-30)" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.85374062;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="rect2117"
|
||||
width="1.109892"
|
||||
height="2.1669323"
|
||||
x="22.090807"
|
||||
y="-10.531444"
|
||||
ry="0.52878839"
|
||||
rx="0.52766228" />
|
||||
</g>
|
||||
<path
|
||||
id="rect1625"
|
||||
style="fill:#50fa7b;fill-opacity:1;stroke:#0f451d;stroke-width:0.593381;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
d="m 50.52517,5.3570989 c -0.643062,0 -1.160653,0.5900216 -1.160653,1.3229166 v 1.0764201 h -1.07642 c -0.732895,0 -1.322916,0.5175908 -1.322916,1.1606533 0,0.643061 0.590021,1.1611691 1.322916,1.1611691 h 1.07642 v 1.076421 c 0,0.732895 0.517591,1.322917 1.160653,1.322917 0.643062,0 1.160653,-0.590022 1.160653,-1.322917 v -1.076421 h 1.076937 c 0.732895,0 1.322916,-0.5181081 1.322916,-1.1611691 0,-0.6430625 -0.590021,-1.1606533 -1.322916,-1.1606533 H 51.685823 V 6.6800155 c 0,-0.732895 -0.517591,-1.3229166 -1.160653,-1.3229166 z" />
|
||||
<path
|
||||
id="path2678"
|
||||
style="fill:#50fa7b;fill-opacity:1;stroke:#0f451d;stroke-width:0.593381;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
d="m 57.791691,5.357099 c -0.643062,0 -1.160653,0.5900216 -1.160653,1.3229166 v 1.0764201 h -1.07642 c -0.732895,0 -1.322916,0.5175908 -1.322916,1.1606522 0,0.643062 0.590021,1.1611701 1.322916,1.1611701 h 1.07642 v 1.076421 c 0,0.732895 0.517591,1.322917 1.160653,1.322917 0.643062,0 1.160653,-0.590022 1.160653,-1.322917 v -1.076421 h 1.076937 c 0.732895,0 1.322916,-0.5181081 1.322916,-1.1611701 0,-0.6430614 -0.590021,-1.1606522 -1.322916,-1.1606522 H 58.952344 V 6.6800156 c 0,-0.732895 -0.517591,-1.3229166 -1.160653,-1.3229166 z" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.72829;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:0.3;paint-order:markers stroke fill"
|
||||
id="rect413"
|
||||
width="10.200269"
|
||||
height="8.7847834"
|
||||
x="-21.316095"
|
||||
y="3.0372066" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.622475;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:0.3;paint-order:markers stroke fill"
|
||||
id="rect415"
|
||||
width="7.9928446"
|
||||
height="15.312933"
|
||||
x="-20.546837"
|
||||
y="12.977094"
|
||||
ry="4.375123"
|
||||
transform="matrix(0.82192626,0.56959391,-0.43346431,0.90117073,0,0)"
|
||||
rx="4.375123" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.622475;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:0.3;paint-order:markers stroke fill"
|
||||
id="rect471"
|
||||
width="7.9928446"
|
||||
height="15.312933"
|
||||
x="9.1578636"
|
||||
y="-5.7980561"
|
||||
ry="4.375123"
|
||||
transform="matrix(-0.82192626,0.56959391,0.43346431,0.90117073,0,0)"
|
||||
rx="4.375123" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="rect1137"
|
||||
width="1.403165"
|
||||
height="4.2796535"
|
||||
x="-22.240166"
|
||||
y="3.9990206"
|
||||
ry="0.70158249"
|
||||
rx="0.70158249" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="rect1295"
|
||||
width="1.403165"
|
||||
height="4.2796535"
|
||||
x="5.4372649"
|
||||
y="19.398754"
|
||||
ry="0.70158249"
|
||||
rx="0.70158249"
|
||||
transform="rotate(90)" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="ellipse1297"
|
||||
cx="-13.849753"
|
||||
cy="-1.3102485"
|
||||
rx="0.82257289"
|
||||
ry="0.79261416"
|
||||
transform="rotate(-30)" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="ellipse1299"
|
||||
cx="-13.825675"
|
||||
cy="0.72537726"
|
||||
rx="0.82257289"
|
||||
ry="0.79261416"
|
||||
transform="rotate(-30)" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="ellipse1301"
|
||||
cx="-11.762562"
|
||||
cy="-1.3702103"
|
||||
rx="0.82257289"
|
||||
ry="0.79261416"
|
||||
transform="rotate(-30)" />
|
||||
<ellipse
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="ellipse1303"
|
||||
cx="-11.721946"
|
||||
cy="0.69849032"
|
||||
rx="0.82257289"
|
||||
ry="0.79261416"
|
||||
transform="rotate(-30)" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="path2085"
|
||||
cx="-17.863199"
|
||||
cy="3.3752983"
|
||||
r="1.7811118" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="circle2087"
|
||||
cx="-14.737003"
|
||||
cy="3.3542202"
|
||||
r="1.7811118" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0.3;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="rect2091"
|
||||
width="0.98221546"
|
||||
height="1.917659"
|
||||
x="-16.946886"
|
||||
y="7.1795278"
|
||||
ry="0.49110773"
|
||||
rx="0.49110773" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ddixs2ish5bi6"
|
||||
path="res://.godot/imported/update.svg-3137f1f7d53c08c0ae65aabe138d898b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/input_helper/assets/update.svg"
|
||||
dest_files=["res://.godot/imported/update.svg-3137f1f7d53c08c0ae65aabe138d898b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1,83 @@
|
||||
@tool
|
||||
|
||||
extends Control
|
||||
|
||||
|
||||
signal failed()
|
||||
signal updated(updated_to_version: String)
|
||||
|
||||
|
||||
const TEMP_FILE_NAME = "user://temp.zip"
|
||||
|
||||
|
||||
@onready var logo: TextureRect = %Logo
|
||||
@onready var label: Label = $VBox/Label
|
||||
@onready var http_request: HTTPRequest = $HTTPRequest
|
||||
@onready var download_button: Button = %DownloadButton
|
||||
|
||||
var next_version: String = "":
|
||||
set(next_next_version):
|
||||
next_version = next_next_version
|
||||
label.text = "Version %s is available for download!" % next_version
|
||||
get:
|
||||
return next_version
|
||||
|
||||
|
||||
func save_zip(bytes: PackedByteArray) -> void:
|
||||
var file: FileAccess = FileAccess.open(TEMP_FILE_NAME, FileAccess.WRITE)
|
||||
file.store_buffer(bytes)
|
||||
file.flush()
|
||||
|
||||
|
||||
### Signals
|
||||
|
||||
|
||||
func _on_download_button_pressed() -> void:
|
||||
# Safeguard the actual input helper repo from accidentally updating itself
|
||||
if FileAccess.file_exists("res://examples/device_tester.gd"):
|
||||
prints("You can't update the input helper from within itself.")
|
||||
failed.emit()
|
||||
return
|
||||
|
||||
http_request.request("https://github.com/nathanhoad/godot_input_helper/archive/refs/tags/v%s.zip" % next_version)
|
||||
download_button.disabled = true
|
||||
download_button.text = "Downloading..."
|
||||
|
||||
|
||||
func _on_http_request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
if result != HTTPRequest.RESULT_SUCCESS:
|
||||
failed.emit()
|
||||
return
|
||||
|
||||
# Save the downloaded zip
|
||||
save_zip(body)
|
||||
|
||||
OS.move_to_trash(ProjectSettings.globalize_path("res://addons/input_helper"))
|
||||
|
||||
var zip_reader: ZIPReader = ZIPReader.new()
|
||||
zip_reader.open(TEMP_FILE_NAME)
|
||||
var files: PackedStringArray = zip_reader.get_files()
|
||||
|
||||
var base_path = files[1]
|
||||
# Remove archive folder
|
||||
files.remove_at(0)
|
||||
# Remove assets folder
|
||||
files.remove_at(0)
|
||||
|
||||
for path in files:
|
||||
var new_file_path: String = path.replace(base_path, "")
|
||||
if path.ends_with("/"):
|
||||
DirAccess.make_dir_recursive_absolute("res://addons/%s" % new_file_path)
|
||||
else:
|
||||
var file: FileAccess = FileAccess.open("res://addons/%s" % new_file_path, FileAccess.WRITE)
|
||||
file.store_buffer(zip_reader.read_file(path))
|
||||
|
||||
zip_reader.close()
|
||||
|
||||
DirAccess.remove_absolute(TEMP_FILE_NAME)
|
||||
|
||||
updated.emit(next_version)
|
||||
|
||||
|
||||
func _on_notes_button_pressed() -> void:
|
||||
OS.shell_open("https://github.com/nathanhoad/godot_input_helper/releases/tag/v%s" % next_version)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dcff0mowkn6km
|
||||
@@ -0,0 +1,60 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://b7mst0qu7vjk1"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dcff0mowkn6km" path="res://addons/input_helper/components/download_update_panel.gd" id="1_4tm1k"]
|
||||
[ext_resource type="Texture2D" uid="uid://ddixs2ish5bi6" path="res://addons/input_helper/assets/update.svg" id="2_j7shv"]
|
||||
|
||||
[node name="DownloadUpdatePanel" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_4tm1k")
|
||||
|
||||
[node name="HTTPRequest" type="HTTPRequest" parent="."]
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -1.0
|
||||
offset_top = 9.0
|
||||
offset_right = -1.0
|
||||
offset_bottom = 9.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="Logo" type="TextureRect" parent="VBox"]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = true
|
||||
custom_minimum_size = Vector2(300, 80)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("2_j7shv")
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="Label" type="Label" parent="VBox"]
|
||||
layout_mode = 2
|
||||
text = "v1.2.3 is available for download."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Center" type="CenterContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DownloadButton" type="Button" parent="VBox/Center"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Download and install update"
|
||||
|
||||
[node name="Center2" type="CenterContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="NotesButton" type="LinkButton" parent="VBox/Center2"]
|
||||
layout_mode = 2
|
||||
text = "Read release notes..."
|
||||
|
||||
[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"]
|
||||
[connection signal="pressed" from="VBox/Center/DownloadButton" to="." method="_on_download_button_pressed"]
|
||||
[connection signal="pressed" from="VBox/Center2/NotesButton" to="." method="_on_notes_button_pressed"]
|
||||
601
Zennysoft.Game.Ma/addons/input_helper/input_helper.gd
Normal file
601
Zennysoft.Game.Ma/addons/input_helper/input_helper.gd
Normal file
@@ -0,0 +1,601 @@
|
||||
extends Node
|
||||
|
||||
|
||||
signal device_changed(device: String, device_index: int)
|
||||
signal keyboard_input_changed(action: String, input: InputEvent)
|
||||
signal joypad_input_changed(action: String, input: InputEvent)
|
||||
signal joypad_changed(device_index: int, is_connected: bool)
|
||||
|
||||
|
||||
const DEVICE_KEYBOARD = "keyboard"
|
||||
const DEVICE_XBOX_CONTROLLER = "xbox"
|
||||
const DEVICE_SWITCH_CONTROLLER = "switch"
|
||||
const DEVICE_PLAYSTATION_CONTROLLER = "playstation"
|
||||
const DEVICE_STEAMDECK_CONTROLLER = "steamdeck"
|
||||
const DEVICE_GENERIC = "generic"
|
||||
|
||||
const SUB_DEVICE_XBOX_ONE_CONTROLLER = "xbox_one"
|
||||
const SUB_DEVICE_XBOX_SERIES_CONTROLLER = "xbox_series"
|
||||
|
||||
const SUB_DEVICE_PLAYSTATION3_CONTROLLER = "playstation3"
|
||||
const SUB_DEVICE_PLAYSTATION4_CONTROLLER = "playstation4"
|
||||
const SUB_DEVICE_PLAYSTATION5_CONTROLLER = "playstation5"
|
||||
|
||||
const SUB_DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER = "switch_left_joycon"
|
||||
const SUB_DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER = "switch_right_joycon"
|
||||
|
||||
const XBOX_BUTTON_LABELS = ["A", "B", "X", "Y", "Back", "Guide", "Start", "Left Stick", "Right Stick", "LB", "RB", "Up", "Down", "Left", "Right", "Share", "Paddle 1", "Paddle 2", "Paddle 3", "Paddle 4"]
|
||||
const XBOX_ONE_BUTTON_LABELS = ["A", "B", "X", "Y", "View", "Guide", "Menu", "Left Stick", "Right Stick", "LB", "RB", "Up", "Down", "Left", "Right", "Share", "Paddle 1", "Paddle 2", "Paddle 3", "Paddle 4"]
|
||||
const XBOX_SERIES_BUTTON_LABELS = ["A", "B", "X", "Y", "View", "Guide", "Menu", "Left Stick", "Right Stick", "LB", "RB", "Up", "Down", "Left", "Right", "Share", "Paddle 1", "Paddle 2", "Paddle 3", "Paddle 4"]
|
||||
const STEAMDECK_BUTTON_LABELS = ["A", "B", "X", "Y", "View", "?", "Options", "Left Stick", "Right Stick", "L1", "R1", "Up", "Down", "Left", "Right", "", "", "", "", ""]
|
||||
# Note: share and home buttons are not recognized
|
||||
const SWITCH_BUTTON_LABELS = ["B", "A", "Y", "X", "Minus", "", "Plus", "Left Stick", "Right Stick", "LS", "RS", "Up", "Down", "Left", "Right", "Capture"]
|
||||
# Mapping for left and right joypad connected together (extended gamepad)
|
||||
# Left Stick is Axis 0 and 1
|
||||
# Right Stick is Axis 2 and 3
|
||||
# ZL and ZR are Axis 4 and 5
|
||||
const SWITCH_EXTENDED_GAMEPAD_BUTTON_LABELS = ["B", "A", "Y", "X", "Minus", "", "Plus", "Left Stick", "Right Stick", "L", "R", "Up", "Down", "Left", "Right", "Capture"]
|
||||
const PLAYSTATION_3_4_BUTTON_LABELS = ["Cross", "Circle", "Square", "Triangle", "Share", "PS", "Options", "L3", "R3", "L1", "R1", "Up", "Down", "Left", "Right", "Microphone", "", "", "", "", "Touchpad"]
|
||||
# Note: Microphone does not work on PC / touchpad is not recognized
|
||||
const PLAYSTATION_5_BUTTON_LABELS = ["Cross", "Circle", "Square", "Triangle", "Create", "PS", "Options", "L3", "R3", "L1", "R1", "Up", "Down", "Left", "Right", "Microphone", "", "", "", "", "Touchpad"]
|
||||
|
||||
const SERIAL_VERSION = 1
|
||||
|
||||
## The deadzone to ignore for joypad motion
|
||||
var deadzone: float = 0.5
|
||||
## The mouse distance to ignore before movement is assumed
|
||||
var mouse_motion_threshold: int = 100
|
||||
## The last known joypad device name (or "" if no joypad detected)
|
||||
var last_known_joypad_device: String = get_simplified_device_name(Input.get_joy_name(0))
|
||||
## The last known joypad index
|
||||
var last_known_joypad_index: int = 0 if Input.get_connected_joypads().size() > 0 else -1
|
||||
|
||||
## Used internally
|
||||
var device_last_changed_at: int = 0
|
||||
var _last_known_granular_joypad_device: String = get_simplified_device_name(Input.get_joy_name(0), true)
|
||||
|
||||
@onready var device: String = guess_device_name()
|
||||
@onready var device_index: int = 0 if has_joypad() else -1
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
|
||||
if not Engine.has_singleton("InputHelper"):
|
||||
Engine.register_singleton("InputHelper", self)
|
||||
|
||||
Input.joy_connection_changed.connect(func(device_index, is_connected): joypad_changed.emit(device_index, is_connected))
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
var next_device: String = device
|
||||
var next_device_index: int = device_index
|
||||
|
||||
# Did we just press a key on the keyboard or move the mouse?
|
||||
if event is InputEventKey \
|
||||
or event is InputEventMouseButton \
|
||||
or (event is InputEventMouseMotion and (event as InputEventMouseMotion).relative.length_squared() > mouse_motion_threshold):
|
||||
next_device = DEVICE_KEYBOARD
|
||||
next_device_index = -1
|
||||
|
||||
# Did we just use a joypad?
|
||||
elif event is InputEventJoypadButton \
|
||||
or (event is InputEventJoypadMotion and abs(event.axis_value) > deadzone):
|
||||
next_device = get_simplified_device_name(get_joy_name(event.device))
|
||||
last_known_joypad_device = next_device
|
||||
next_device_index = event.device
|
||||
last_known_joypad_index = next_device_index
|
||||
|
||||
_last_known_granular_joypad_device = get_simplified_device_name(get_joy_name(event.device), true)
|
||||
|
||||
# Debounce changes for 1 second because some joypads register twice in Windows for some reason
|
||||
var not_changed_in_last_second = Engine.get_frames_drawn() - device_last_changed_at > Engine.get_frames_per_second()
|
||||
if (next_device != device or next_device_index != device_index) and not_changed_in_last_second:
|
||||
device_last_changed_at = Engine.get_frames_drawn()
|
||||
|
||||
device = next_device
|
||||
device_index = next_device_index
|
||||
device_changed.emit(device, device_index)
|
||||
|
||||
|
||||
## Get the name of a joypad
|
||||
func get_joy_name(at_device_index: int) -> String:
|
||||
var joy_name: String = Input.get_joy_name(at_device_index)
|
||||
if joy_name == "" and Input.get_joy_info(at_device_index).size() > 0 and "xinput" in Input.get_joy_info(at_device_index).keys()[0]:
|
||||
joy_name = "XInput"
|
||||
return joy_name
|
||||
|
||||
|
||||
## Get the device name for an event
|
||||
func get_device_from_event(event: InputEvent) -> String:
|
||||
if event is InputEventKey or event is InputEventMouseButton or event is InputEventMouseMotion:
|
||||
return DEVICE_KEYBOARD
|
||||
elif event is InputEventJoypadButton or event is InputEventJoypadMotion:
|
||||
return get_simplified_device_name(get_joy_name(event.device))
|
||||
else:
|
||||
return DEVICE_GENERIC
|
||||
|
||||
|
||||
## Get the device name for an event
|
||||
func get_device_index_from_event(event: InputEvent) -> int:
|
||||
if event is InputEventJoypadButton or event is InputEventJoypadMotion:
|
||||
return event.device
|
||||
else:
|
||||
return -1
|
||||
|
||||
|
||||
## Convert a Godot device identifier to a simplified string
|
||||
func get_simplified_device_name(raw_name: String, force_granular_identifier: bool = false) -> String:
|
||||
var use_granular_identifier: bool = force_granular_identifier or InputHelperSettings.get_setting(InputHelperSettings.USE_GRANULAR_DEVICE_IDENTIFIERS, false)
|
||||
|
||||
var keywords: Dictionary = {
|
||||
SUB_DEVICE_XBOX_ONE_CONTROLLER: ["Xbox One Controller"],
|
||||
SUB_DEVICE_XBOX_SERIES_CONTROLLER: ["Xbox Series Controller", "Xbox Wireless Controller"],
|
||||
DEVICE_XBOX_CONTROLLER: ["XInput", "XBox"],
|
||||
SUB_DEVICE_PLAYSTATION3_CONTROLLER: ["PS3"],
|
||||
SUB_DEVICE_PLAYSTATION4_CONTROLLER:["Nacon Revolution Unlimited Pro Controller", "PS4", "DUALSHOCK 4"],
|
||||
SUB_DEVICE_PLAYSTATION5_CONTROLLER:["Sony DualSense", "PS5", "DualSense Wireless Controller"],
|
||||
DEVICE_STEAMDECK_CONTROLLER: ["Steam"],
|
||||
DEVICE_SWITCH_CONTROLLER: ["Switch", "Joy-Con (L/R)", "PowerA Core Controller"],
|
||||
SUB_DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER: ["Joy-Con (L)"],
|
||||
SUB_DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER: ["joy-Con (R)"],
|
||||
} if use_granular_identifier else {
|
||||
DEVICE_XBOX_CONTROLLER: ["XBox", "XInput"],
|
||||
DEVICE_PLAYSTATION_CONTROLLER: ["Sony", "PS3", "PS5", "PS4", "DUALSHOCK 4", "DualSense", "Nacon Revolution Unlimited Pro Controller"],
|
||||
DEVICE_STEAMDECK_CONTROLLER: ["Steam"],
|
||||
DEVICE_SWITCH_CONTROLLER: ["Switch", "Joy-Con", "PowerA Core Controller"],
|
||||
}
|
||||
|
||||
for device_key in keywords:
|
||||
for keyword in keywords[device_key]:
|
||||
if keyword.to_lower() in raw_name.to_lower():
|
||||
return device_key
|
||||
|
||||
return DEVICE_GENERIC
|
||||
|
||||
|
||||
## Check if there is a connected joypad
|
||||
func has_joypad() -> bool:
|
||||
return Input.get_connected_joypads().size() > 0
|
||||
|
||||
|
||||
## Guess the initial input device
|
||||
func guess_device_name() -> String:
|
||||
if has_joypad():
|
||||
return get_simplified_device_name(get_joy_name(0))
|
||||
else:
|
||||
return DEVICE_KEYBOARD
|
||||
|
||||
|
||||
#region Mapping
|
||||
|
||||
|
||||
func reset_all_actions() -> void:
|
||||
InputMap.load_from_project_settings()
|
||||
for action in InputMap.get_actions():
|
||||
var input: InputEvent = get_joypad_input_for_action(action)
|
||||
if input != null:
|
||||
joypad_input_changed.emit(action, input)
|
||||
|
||||
input = get_keyboard_input_for_action(action)
|
||||
if input != null:
|
||||
keyboard_input_changed.emit(action, input)
|
||||
|
||||
|
||||
## Set the key or button for an action
|
||||
func set_keyboard_or_joypad_input_for_action(action: String, event: InputEvent, swap_if_taken: bool = true) -> void:
|
||||
if event is InputEventKey or event is InputEventMouse:
|
||||
set_keyboard_input_for_action(action, event, swap_if_taken)
|
||||
elif event is InputEventJoypadButton:
|
||||
set_joypad_input_for_action(action, event, swap_if_taken)
|
||||
|
||||
|
||||
## Get the key or button for a given action depending on the current device
|
||||
func get_keyboard_or_joypad_input_for_action(action: String) -> InputEvent:
|
||||
if device == DEVICE_KEYBOARD:
|
||||
return get_keyboard_input_for_action(action)
|
||||
else:
|
||||
return get_joypad_input_for_action(action)
|
||||
|
||||
|
||||
## Get the key or button for a given action depending on the current device
|
||||
func get_keyboard_or_joypad_inputs_for_action(action: String) -> Array[InputEvent]:
|
||||
if device == DEVICE_KEYBOARD:
|
||||
return get_keyboard_inputs_for_action(action)
|
||||
else:
|
||||
return get_joypad_inputs_for_action(action)
|
||||
|
||||
|
||||
## Get a text label for a given input
|
||||
func get_label_for_input(input: InputEvent) -> String:
|
||||
if input == null: return ""
|
||||
|
||||
if input is InputEventKey:
|
||||
if input.physical_keycode > 0 :
|
||||
var keycode: Key = DisplayServer.keyboard_get_keycode_from_physical(input.physical_keycode) if DisplayServer.keyboard_get_current_layout() > -1 else input.physical_keycode
|
||||
return OS.get_keycode_string(keycode)
|
||||
elif input.keycode > 0:
|
||||
return OS.get_keycode_string(input.keycode)
|
||||
else:
|
||||
return input.as_text()
|
||||
|
||||
elif input is InputEventMouseButton:
|
||||
match input.button_index:
|
||||
MOUSE_BUTTON_LEFT:
|
||||
return "Mouse Left Button"
|
||||
MOUSE_BUTTON_MIDDLE:
|
||||
return "Mouse Middle Button"
|
||||
MOUSE_BUTTON_RIGHT:
|
||||
return "Mouse Right Button"
|
||||
return "Mouse Button %d" % input.button_index
|
||||
|
||||
elif input is InputEventJoypadButton:
|
||||
var labels = []
|
||||
match _last_known_granular_joypad_device:
|
||||
DEVICE_XBOX_CONTROLLER, DEVICE_GENERIC:
|
||||
labels = XBOX_BUTTON_LABELS
|
||||
SUB_DEVICE_XBOX_ONE_CONTROLLER:
|
||||
labels = XBOX_ONE_BUTTON_LABELS
|
||||
SUB_DEVICE_XBOX_SERIES_CONTROLLER:
|
||||
labels = XBOX_SERIES_BUTTON_LABELS
|
||||
SUB_DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER, SUB_DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER:
|
||||
labels = SWITCH_BUTTON_LABELS
|
||||
DEVICE_SWITCH_CONTROLLER:
|
||||
labels = SWITCH_EXTENDED_GAMEPAD_BUTTON_LABELS
|
||||
SUB_DEVICE_PLAYSTATION3_CONTROLLER, SUB_DEVICE_PLAYSTATION4_CONTROLLER:
|
||||
labels = PLAYSTATION_3_4_BUTTON_LABELS
|
||||
DEVICE_PLAYSTATION_CONTROLLER, SUB_DEVICE_PLAYSTATION5_CONTROLLER:
|
||||
labels = PLAYSTATION_5_BUTTON_LABELS
|
||||
DEVICE_STEAMDECK_CONTROLLER:
|
||||
labels = STEAMDECK_BUTTON_LABELS
|
||||
if input.button_index < labels.size():
|
||||
return "%s Button" % labels[input.button_index]
|
||||
else:
|
||||
return "Button %d" % input.button_index
|
||||
|
||||
elif input is InputEventJoypadMotion:
|
||||
var motion: InputEventJoypadMotion = input as InputEventJoypadMotion
|
||||
match motion.axis:
|
||||
JOY_AXIS_LEFT_X:
|
||||
return "Left Stick %s" % ("Left" if motion.axis_value < 0 else "Right")
|
||||
JOY_AXIS_LEFT_Y:
|
||||
return "Left Stick %s" % ("Up" if motion.axis_value < 0 else "Down")
|
||||
JOY_AXIS_RIGHT_X:
|
||||
return "Right Stick %s" % ("Left" if motion.axis_value < 0 else "Right")
|
||||
JOY_AXIS_RIGHT_Y:
|
||||
return "Right Stick %s" % ("Up" if motion.axis_value < 0 else "Down")
|
||||
JOY_AXIS_TRIGGER_LEFT:
|
||||
return "Left Trigger"
|
||||
JOY_AXIS_TRIGGER_RIGHT:
|
||||
return "Right Trigger"
|
||||
|
||||
return input.as_text()
|
||||
|
||||
|
||||
## Serialize a single action's inputs.
|
||||
func serialize_inputs_for_action(action: StringName) -> String:
|
||||
var action_inputs: PackedStringArray = []
|
||||
var inputs: Array[InputEvent] = InputMap.action_get_events(action)
|
||||
for input in inputs:
|
||||
if input is InputEventKey:
|
||||
var s: String = get_label_for_input(input)
|
||||
var modifiers: Array[String] = []
|
||||
if input.alt_pressed:
|
||||
modifiers.append("alt")
|
||||
if input.shift_pressed:
|
||||
modifiers.append("shift")
|
||||
if input.ctrl_pressed:
|
||||
modifiers.append("ctrl")
|
||||
if input.meta_pressed:
|
||||
modifiers.append("meta")
|
||||
if not modifiers.is_empty():
|
||||
s += "|" + ",".join(modifiers)
|
||||
action_inputs.append("key:%s" % s)
|
||||
elif input is InputEventMouseButton:
|
||||
action_inputs.append("mouse:%d" % input.button_index)
|
||||
elif input is InputEventJoypadButton:
|
||||
action_inputs.append("joypad:%d" % input.button_index)
|
||||
elif input is InputEventJoypadMotion:
|
||||
action_inputs.append("joypad:%d|%f" % [input.axis, input.axis_value])
|
||||
|
||||
return ";".join(action_inputs)
|
||||
|
||||
|
||||
## Serialize a list of action inputs to string. If actions is empty then it will serialize
|
||||
## all actions.
|
||||
func serialize_inputs_for_actions(actions: PackedStringArray = []) -> String:
|
||||
if actions == null or actions.is_empty():
|
||||
actions = InputMap.get_actions()
|
||||
|
||||
var map: Dictionary = {}
|
||||
for action in actions:
|
||||
map[action] = serialize_inputs_for_action(action)
|
||||
|
||||
return JSON.stringify({
|
||||
version = SERIAL_VERSION,
|
||||
map = map
|
||||
})
|
||||
|
||||
|
||||
## Deserialize a single action's inputs.
|
||||
func deserialize_inputs_for_action(action: String, string: String) -> void:
|
||||
InputMap.action_erase_events(action)
|
||||
var action_inputs: PackedStringArray = string.split(";")
|
||||
for action_input in action_inputs:
|
||||
var bits: PackedStringArray = action_input.split(":")
|
||||
|
||||
# Ignore any empty actions
|
||||
if bits.size() < 2: continue
|
||||
|
||||
var input_type: String = bits[0]
|
||||
var input_details: String = bits[1]
|
||||
|
||||
match input_type:
|
||||
"key":
|
||||
var keyboard_input = InputEventKey.new()
|
||||
if "|" in input_details:
|
||||
var detail_bits = input_details.split("|")
|
||||
keyboard_input.keycode = OS.find_keycode_from_string(detail_bits[0])
|
||||
detail_bits = detail_bits[1].split(",")
|
||||
if detail_bits.has("alt"):
|
||||
keyboard_input.alt_pressed = true
|
||||
if detail_bits.has("shift"):
|
||||
keyboard_input.shift_pressed = true
|
||||
if detail_bits.has("ctrl"):
|
||||
keyboard_input.ctrl_pressed = true
|
||||
if detail_bits.has("meta"):
|
||||
keyboard_input.meta_pressed = true
|
||||
else:
|
||||
keyboard_input.keycode = OS.find_keycode_from_string(input_details)
|
||||
InputMap.action_add_event(action, keyboard_input)
|
||||
keyboard_input_changed.emit(action, keyboard_input)
|
||||
|
||||
"mouse":
|
||||
var mouse_input = InputEventMouseButton.new()
|
||||
mouse_input.button_index = int(input_details)
|
||||
InputMap.action_add_event(action, mouse_input)
|
||||
keyboard_input_changed.emit(action, mouse_input)
|
||||
|
||||
"joypad":
|
||||
if "|" in str(input_details):
|
||||
var joypad_motion_input = InputEventJoypadMotion.new()
|
||||
var joypad_bits = input_details.split("|")
|
||||
joypad_motion_input.axis = int(joypad_bits[0])
|
||||
joypad_motion_input.axis_value = float(joypad_bits[1])
|
||||
InputMap.action_add_event(action, joypad_motion_input)
|
||||
joypad_input_changed.emit(action, joypad_motion_input)
|
||||
else:
|
||||
var joypad_input = InputEventJoypadButton.new()
|
||||
joypad_input.button_index = int(input_details)
|
||||
InputMap.action_add_event(action, joypad_input)
|
||||
joypad_input_changed.emit(action, joypad_input)
|
||||
|
||||
|
||||
## Deserialise a list of actions' inputs.
|
||||
func deserialize_inputs_for_actions(string: String) -> void:
|
||||
var data: Dictionary = JSON.parse_string(string)
|
||||
|
||||
# Use legacy deserialization
|
||||
if not data.has("version"):
|
||||
_deprecated_deserialize_inputs_for_actions(string)
|
||||
return
|
||||
|
||||
# Version 1
|
||||
for action in data.map.keys():
|
||||
deserialize_inputs_for_action(action, data.map[action])
|
||||
|
||||
|
||||
# Load inputs from a serialized string. [deprecated]
|
||||
func _deprecated_deserialize_inputs_for_actions(string: String) -> void:
|
||||
var map: Dictionary = JSON.parse_string(string)
|
||||
for action in map.keys():
|
||||
InputMap.action_erase_events(action)
|
||||
|
||||
for key in map[action]["keyboard"]:
|
||||
var keyboard_input = InputEventKey.new()
|
||||
if "|" in key:
|
||||
var bits = key.split("|")
|
||||
keyboard_input.keycode = OS.find_keycode_from_string(bits[0])
|
||||
bits = bits[1].split(",")
|
||||
if bits.has("alt"):
|
||||
keyboard_input.alt_pressed = true
|
||||
if bits.has("shift"):
|
||||
keyboard_input.shift_pressed = true
|
||||
if bits.has("ctrl"):
|
||||
keyboard_input.ctrl_pressed = true
|
||||
if bits.has("meta"):
|
||||
keyboard_input.meta_pressed = true
|
||||
else:
|
||||
keyboard_input.keycode = OS.find_keycode_from_string(key)
|
||||
InputMap.action_add_event(action, keyboard_input)
|
||||
|
||||
for button_index in map[action]["mouse"]:
|
||||
var mouse_input = InputEventMouseButton.new()
|
||||
mouse_input.button_index = int(button_index)
|
||||
InputMap.action_add_event(action, mouse_input)
|
||||
|
||||
for button_index_or_motion in map[action]["joypad"]:
|
||||
if "|" in str(button_index_or_motion):
|
||||
var joypad_motion_input = InputEventJoypadMotion.new()
|
||||
var bits = button_index_or_motion.split("|")
|
||||
joypad_motion_input.axis = int(bits[0])
|
||||
joypad_motion_input.axis_value = float(bits[1])
|
||||
InputMap.action_add_event(action, joypad_motion_input)
|
||||
else:
|
||||
var joypad_input = InputEventJoypadButton.new()
|
||||
joypad_input.button_index = int(button_index_or_motion)
|
||||
InputMap.action_add_event(action, joypad_input)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Keyboard/mouse input
|
||||
|
||||
|
||||
## Get all of the keys/mouse buttons used for an action.
|
||||
func get_keyboard_inputs_for_action(action: String) -> Array[InputEvent]:
|
||||
return InputMap.action_get_events(action).filter(func(event):
|
||||
return event is InputEventKey or event is InputEventMouseButton
|
||||
)
|
||||
|
||||
|
||||
## Get the first key for an action
|
||||
func get_keyboard_input_for_action(action: String) -> InputEvent:
|
||||
var inputs: Array[InputEvent] = get_keyboard_inputs_for_action(action)
|
||||
return null if inputs.is_empty() else inputs[0]
|
||||
|
||||
|
||||
## Set the key used for an action
|
||||
func set_keyboard_input_for_action(action: String, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
||||
return _update_keyboard_input_for_action(action, input, swap_if_taken, null)
|
||||
|
||||
|
||||
## Replace a specific key with another key
|
||||
func replace_keyboard_input_for_action(action: String, current_input: InputEvent, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
||||
return _update_keyboard_input_for_action(action, input, swap_if_taken, current_input)
|
||||
|
||||
|
||||
## Replace a specific key, given its index
|
||||
func replace_keyboard_input_at_index(action: String, index: int, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
||||
var inputs: Array[InputEvent] = get_keyboard_inputs_for_action(action)
|
||||
var replacing_input = InputEventKey.new() if (inputs.is_empty() or inputs.size() <= index) else inputs[index]
|
||||
return _update_keyboard_input_for_action(action, input, swap_if_taken, replacing_input)
|
||||
|
||||
|
||||
func _update_keyboard_input_for_action(action: String, input: InputEvent, swap_if_taken: bool, replacing_input: InputEvent = null) -> Error:
|
||||
if not (input is InputEventKey or input is InputEventMouseButton): return ERR_INVALID_DATA
|
||||
|
||||
var is_valid_keyboard_event = func(event):
|
||||
return event is InputEventKey or event is InputEventMouseButton
|
||||
|
||||
return _update_input_for_action(action, input, swap_if_taken, replacing_input, is_valid_keyboard_event, keyboard_input_changed)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Joypad input
|
||||
|
||||
|
||||
## Get all buttons used for an action
|
||||
func get_joypad_inputs_for_action(action: String) -> Array[InputEvent]:
|
||||
return InputMap.action_get_events(action).filter(func(event):
|
||||
return event is InputEventJoypadButton or event is InputEventJoypadMotion
|
||||
)
|
||||
|
||||
|
||||
## Get the first button for an action
|
||||
func get_joypad_input_for_action(action: String) -> InputEvent:
|
||||
var buttons: Array[InputEvent] = get_joypad_inputs_for_action(action)
|
||||
return null if buttons.is_empty() else buttons[0]
|
||||
|
||||
|
||||
## Set the button for an action
|
||||
func set_joypad_input_for_action(action: String, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
||||
return _update_joypad_input_for_action(action, input, swap_if_taken, null)
|
||||
|
||||
|
||||
## Replace a specific button for an action
|
||||
func replace_joypad_input_for_action(action: String, current_input: InputEvent, input: InputEventJoypadButton, swap_if_taken: bool = true) -> Error:
|
||||
return _update_joypad_input_for_action(action, input, swap_if_taken, current_input)
|
||||
|
||||
|
||||
## Replace a button, given its index
|
||||
func replace_joypad_input_at_index(action: String, index: int, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
||||
var inputs: Array[InputEvent] = get_joypad_inputs_for_action(action)
|
||||
var replacing_input
|
||||
if inputs.is_empty() or inputs.size() <= index:
|
||||
replacing_input = InputEventJoypadButton.new()
|
||||
replacing_input.button_index = JOY_BUTTON_INVALID
|
||||
else:
|
||||
replacing_input = inputs[index]
|
||||
return _update_joypad_input_for_action(action, input, swap_if_taken, replacing_input)
|
||||
|
||||
|
||||
## Set the action used for a button
|
||||
func _update_joypad_input_for_action(action: String, input: InputEvent, swap_if_taken: bool = true, replacing_input: InputEvent = null) -> Error:
|
||||
var is_valid_keyboard_event = func(event):
|
||||
return event is InputEventJoypadButton or event is InputEventJoypadMotion
|
||||
|
||||
return _update_input_for_action(action, input, swap_if_taken, replacing_input, is_valid_keyboard_event, joypad_input_changed)
|
||||
|
||||
|
||||
func _update_input_for_action(action: String, input: InputEvent, swap_if_taken: bool, replacing_input: InputEvent, check_is_valid: Callable, did_change_signal: Signal) -> Error:
|
||||
# Find any action that is already mapped to this input
|
||||
var clashing_action = ""
|
||||
var clashing_event
|
||||
if swap_if_taken:
|
||||
for other_action in InputMap.get_actions():
|
||||
if other_action == action: continue
|
||||
|
||||
for event in InputMap.action_get_events(other_action):
|
||||
if event.is_match(input):
|
||||
clashing_action = other_action
|
||||
clashing_event = event
|
||||
|
||||
# Find the key based event for the target action
|
||||
var action_events: Array[InputEvent] = InputMap.action_get_events(action)
|
||||
var is_replacing: bool = false
|
||||
for i in range(0, action_events.size()):
|
||||
var event: InputEvent = action_events[i]
|
||||
if check_is_valid.call(event):
|
||||
if replacing_input != null and not event.is_match(replacing_input):
|
||||
continue
|
||||
|
||||
# Remap the other event if there is a clashing one
|
||||
if clashing_action:
|
||||
_update_input_for_action(clashing_action, event, false, clashing_event, check_is_valid, did_change_signal)
|
||||
|
||||
# Replace the event
|
||||
action_events[i] = input
|
||||
is_replacing = true
|
||||
break
|
||||
|
||||
# If we were trying to replace something but didn't find it then just add it to the end
|
||||
if not is_replacing:
|
||||
action_events.append(input)
|
||||
|
||||
# Apply the changes
|
||||
InputMap.action_erase_events(action)
|
||||
for event in action_events:
|
||||
if event != null:
|
||||
InputMap.action_add_event(action, event)
|
||||
|
||||
did_change_signal.emit(action, input)
|
||||
|
||||
return OK
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rumbling
|
||||
|
||||
|
||||
func rumble_small(target_device: int = 0) -> void:
|
||||
Input.start_joy_vibration(target_device, 0.4, 0, 0.1)
|
||||
|
||||
|
||||
func rumble_medium(target_device: int = 0) -> void:
|
||||
Input.start_joy_vibration(target_device, 0, 0.7, 0.1)
|
||||
|
||||
|
||||
func rumble_large(target_device: int = 0) -> void:
|
||||
Input.start_joy_vibration(target_device, 0, 1, 0.1)
|
||||
|
||||
|
||||
func start_rumble_small(target_device: int = 0) -> void:
|
||||
Input.start_joy_vibration(target_device, 0.4, 0, 0)
|
||||
|
||||
|
||||
func start_rumble_medium(target_device: int = 0) -> void:
|
||||
Input.start_joy_vibration(target_device, 0, 0.7, 0)
|
||||
|
||||
|
||||
func start_rumble_large(target_device: int = 0) -> void:
|
||||
Input.start_joy_vibration(target_device, 0, 1, 0)
|
||||
|
||||
|
||||
func stop_rumble(target_device: int = 0) -> void:
|
||||
Input.stop_joy_vibration(target_device)
|
||||
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1 @@
|
||||
uid://cholww48njaeh
|
||||
7
Zennysoft.Game.Ma/addons/input_helper/plugin.cfg
Normal file
7
Zennysoft.Game.Ma/addons/input_helper/plugin.cfg
Normal file
@@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="Input Helper"
|
||||
description="Detect which input device the player is using and manage input actions"
|
||||
author="Nathan Hoad"
|
||||
version="4.7.0"
|
||||
script="plugin.gd"
|
||||
112
Zennysoft.Game.Ma/addons/input_helper/plugin.gd
Normal file
112
Zennysoft.Game.Ma/addons/input_helper/plugin.gd
Normal file
@@ -0,0 +1,112 @@
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
const REMOTE_RELEASES_URL = "https://api.github.com/repos/nathanhoad/godot_input_helper/releases"
|
||||
const LOCAL_CONFIG_PATH = "res://addons/input_helper/plugin.cfg"
|
||||
|
||||
const DownloadDialogScene = preload("res://addons/input_helper/views/download_dialog.tscn")
|
||||
|
||||
|
||||
var http_request: HTTPRequest = HTTPRequest.new()
|
||||
var next_version: String = ""
|
||||
|
||||
|
||||
func _enter_tree():
|
||||
add_autoload_singleton("InputHelper", "res://addons/input_helper/input_helper.gd")
|
||||
|
||||
# Configure settings
|
||||
InputHelperSettings.prepare()
|
||||
|
||||
# Check for updates on GitHub
|
||||
get_editor_interface().get_base_control().add_child(http_request)
|
||||
http_request.request_completed.connect(_on_http_request_request_completed)
|
||||
http_request.request(REMOTE_RELEASES_URL)
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
remove_autoload_singleton("InputHelper")
|
||||
|
||||
if next_version != "":
|
||||
remove_tool_menu_item("Update Input Helper to v%s" % next_version)
|
||||
|
||||
|
||||
# Get the current version
|
||||
func get_version() -> String:
|
||||
var config: ConfigFile = ConfigFile.new()
|
||||
config.load(LOCAL_CONFIG_PATH)
|
||||
return config.get_value("plugin", "version")
|
||||
|
||||
|
||||
# Convert a version number to an actually comparable number
|
||||
func version_to_number(version: String) -> int:
|
||||
var bits = version.split(".")
|
||||
return bits[0].to_int() * 1000000 + bits[1].to_int() * 1000 + bits[2].to_int()
|
||||
|
||||
|
||||
### Signals
|
||||
|
||||
|
||||
func _on_http_request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
http_request.queue_free()
|
||||
|
||||
if result != HTTPRequest.RESULT_SUCCESS: return
|
||||
|
||||
var current_version: String = get_version()
|
||||
|
||||
# Work out the next version from the releases information on GitHub
|
||||
var response = JSON.parse_string(body.get_string_from_utf8())
|
||||
if typeof(response) != TYPE_ARRAY: return
|
||||
|
||||
# GitHub releases are in order of creation, not order of version
|
||||
var versions = (response as Array).filter(func(release):
|
||||
var version: String = release.tag_name.substr(1)
|
||||
return version_to_number(version) > version_to_number(current_version)
|
||||
)
|
||||
if versions.size() > 0:
|
||||
next_version = versions[0].tag_name.substr(1)
|
||||
add_tool_menu_item("Update Input Helper to v%s" % next_version, _update_input_helper)
|
||||
|
||||
|
||||
func _update_input_helper() -> void:
|
||||
var download_dialog := DownloadDialogScene.instantiate()
|
||||
download_dialog.next_version = next_version
|
||||
|
||||
var scale: float = get_editor_interface().get_editor_scale()
|
||||
download_dialog.min_size = Vector2(300, 250) * scale
|
||||
|
||||
download_dialog.update_finished.connect(_on_download_dialog_update_finished)
|
||||
download_dialog.update_failed.connect(_on_download_dialog_update_failed)
|
||||
|
||||
get_editor_interface().get_base_control().add_child(download_dialog)
|
||||
download_dialog.show()
|
||||
|
||||
|
||||
func _on_download_dialog_update_finished() -> void:
|
||||
remove_tool_menu_item("Update Input Helper to v%s" % next_version)
|
||||
|
||||
get_editor_interface().get_resource_filesystem().scan()
|
||||
|
||||
print_rich("\n[b]Updated Input Helper to v%s[/b]\n" % next_version)
|
||||
|
||||
var finished_dialog: AcceptDialog = AcceptDialog.new()
|
||||
finished_dialog.dialog_text = "Your Input Helper is now up to date."
|
||||
|
||||
var restart_addon = func():
|
||||
finished_dialog.queue_free()
|
||||
get_editor_interface().call_deferred("set_plugin_enabled", "input_helper", true)
|
||||
get_editor_interface().set_plugin_enabled("input_helper", false)
|
||||
|
||||
finished_dialog.canceled.connect(restart_addon)
|
||||
finished_dialog.confirmed.connect(restart_addon)
|
||||
get_editor_interface().get_base_control().add_child(finished_dialog)
|
||||
finished_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_download_dialog_update_failed() -> void:
|
||||
var failed_dialog: AcceptDialog = AcceptDialog.new()
|
||||
failed_dialog.dialog_text = "There was a problem downloading the update."
|
||||
failed_dialog.canceled.connect(func(): failed_dialog.queue_free())
|
||||
failed_dialog.confirmed.connect(func(): failed_dialog.queue_free())
|
||||
get_editor_interface().get_base_control().add_child(failed_dialog)
|
||||
failed_dialog.popup_centered()
|
||||
1
Zennysoft.Game.Ma/addons/input_helper/plugin.gd.uid
Normal file
1
Zennysoft.Game.Ma/addons/input_helper/plugin.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cul6evy00vr55
|
||||
42
Zennysoft.Game.Ma/addons/input_helper/settings.gd
Normal file
42
Zennysoft.Game.Ma/addons/input_helper/settings.gd
Normal file
@@ -0,0 +1,42 @@
|
||||
class_name InputHelperSettings extends Node
|
||||
|
||||
const USE_GRANULAR_DEVICE_IDENTIFIERS = "devices/use_granular_device_identifiers"
|
||||
|
||||
const SETTINGS_CONFIGURATION = {
|
||||
USE_GRANULAR_DEVICE_IDENTIFIERS: {
|
||||
value = false,
|
||||
type = TYPE_BOOL,
|
||||
is_advanced = true
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
static func prepare() -> void:
|
||||
for key: String in SETTINGS_CONFIGURATION:
|
||||
var setting_config: Dictionary = SETTINGS_CONFIGURATION[key]
|
||||
var setting_name: String = "input_helper/%s" % key
|
||||
if not ProjectSettings.has_setting(setting_name):
|
||||
ProjectSettings.set_setting(setting_name, setting_config.value)
|
||||
ProjectSettings.set_initial_value(setting_name, setting_config.value)
|
||||
ProjectSettings.add_property_info({
|
||||
"name" = setting_name,
|
||||
"type" = setting_config.type,
|
||||
"hint" = setting_config.get("hint", PROPERTY_HINT_NONE),
|
||||
"hint_string" = setting_config.get("hint_string", "")
|
||||
})
|
||||
ProjectSettings.set_as_basic(setting_name, not setting_config.has("is_advanced"))
|
||||
ProjectSettings.set_as_internal(setting_name, setting_config.has("is_hidden"))
|
||||
|
||||
|
||||
static func set_setting(key: String, value) -> void:
|
||||
if get_setting(key, value) != value:
|
||||
ProjectSettings.set_setting("input_helper/%s" % key, value)
|
||||
ProjectSettings.set_initial_value("input_helper/%s" % key, SETTINGS_CONFIGURATION[key].value)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
static func get_setting(key: String, default):
|
||||
if ProjectSettings.has_setting("input_helper/%s" % key):
|
||||
return ProjectSettings.get_setting("input_helper/%s" % key)
|
||||
else:
|
||||
return default
|
||||
1
Zennysoft.Game.Ma/addons/input_helper/settings.gd.uid
Normal file
1
Zennysoft.Game.Ma/addons/input_helper/settings.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://yqplm6a6focp
|
||||
@@ -0,0 +1,30 @@
|
||||
@tool
|
||||
|
||||
extends AcceptDialog
|
||||
|
||||
|
||||
signal update_finished()
|
||||
signal update_failed()
|
||||
|
||||
|
||||
@onready var download_update_panel := $DownloadUpdatePanel
|
||||
|
||||
|
||||
var next_version: String
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
download_update_panel.next_version = next_version
|
||||
|
||||
|
||||
### Signals
|
||||
|
||||
|
||||
func _on_download_update_panel_updated(updated_to_version) -> void:
|
||||
update_finished.emit()
|
||||
queue_free()
|
||||
|
||||
|
||||
func _on_download_update_panel_failed() -> void:
|
||||
update_failed.emit()
|
||||
queue_free()
|
||||
@@ -0,0 +1 @@
|
||||
uid://1t3qhgrro2es
|
||||
@@ -0,0 +1,14 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://bownbkcmm43gn"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://b7mst0qu7vjk1" path="res://addons/input_helper/components/download_update_panel.tscn" id="1_37q37"]
|
||||
[ext_resource type="Script" uid="uid://1t3qhgrro2es" path="res://addons/input_helper/views/download_dialog.gd" id="1_ltktf"]
|
||||
|
||||
[node name="DownloadDialog" type="AcceptDialog"]
|
||||
initial_position = 2
|
||||
ok_button_text = "Close"
|
||||
script = ExtResource("1_ltktf")
|
||||
|
||||
[node name="DownloadUpdatePanel" parent="." instance=ExtResource("1_37q37")]
|
||||
|
||||
[connection signal="failed" from="DownloadUpdatePanel" to="." method="_on_download_update_panel_failed"]
|
||||
[connection signal="updated" from="DownloadUpdatePanel" to="." method="_on_download_update_panel_updated"]
|
||||
Reference in New Issue
Block a user