Advertisement
FlipelyFlip

Lone Wolf Gamepad Input

Oct 21st, 2024 (edited)
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 17.81 KB | None | 0 0
  1. #==============================================================================
  2. # Gamepad Extender v1.2 (10/22/24)
  3. # by Lone Wolf & FlipelyFlip
  4. #------------------------------------------------------------------------------
  5. # This allows scripters to utilize the extra buttons on modern
  6. # XInput-compatible gamepads. It requires DirectX 9.0 or later and
  7. # an XInput compatible gamepad. Incompatible pads will default to
  8. # using the standard Input module functionality.
  9. #
  10. # This is not a plug-and-play script.
  11. # Some knowledge of RGSS is required for proper use.
  12. #
  13. # Instructions:
  14. #     1) Paste in the Materials section
  15. #  2) Replace button calls in (other) scripts to use gamepad buttons as needed
  16. #        (see Command Reference below)
  17. # Optional:
  18. #     2) Use the PadConfig section to specify button differences between
  19. #          Gamepad and Keyboard (for players without gamepads)
  20. #     3) Replace direct button calls in your scripts (and the defaults) with
  21. #          PadConfig calls or these will do nothing
  22. #        (see Command Reference below)
  23. #------------------------------------------------------------------------------
  24. # Command Reference:
  25. #------------------------------------------------------------------------------
  26. # All calls to extended buttons on Pad 1 can be made through the Input module.
  27. # When using multiple pads, send calls to WolfPad with pad number (0...3)
  28. #    as the final parameter. (i.e.  WolfPad.press?(:A, 3)  )
  29. #
  30. # The current list of mappable buttons is as follows:
  31. #
  32. #     :A, :B, :X, :Y     - XBOX 360 standard face buttons.
  33. #     :L1, :L2, :R1, :R2 - Four triggers (LB, LT, RB, RT)
  34. #     :SELECT, :START    - Back and Start buttons
  35. #     :L3, :R3                    - Clickable Analog buttons
  36. #
  37. #     :UP, :DOWN,
  38. #     :LEFT, :RIGHT        - D-Pad buttons
  39. #
  40. #  :L_UP, :L_DOWN
  41. #  :L_LEFT, :L_RIGHT  - Left stick directions
  42. #
  43. #  :R_UP, :R_DOWN
  44. #  :R_LEFT, :R_RIGHT  - Right stick directions
  45. #
  46. # NON-STANDARD MAPPINGS WILL DO NOTHING without a compatible gamepad.
  47. # To that end, use calls to PadConfig to remap non-standard keys into
  48. # the standard domain where possible.
  49. #
  50. #         for example:    Input.trigger?(PadConfig.page_down)
  51. #         will return :R1 if a gamepad is plugged in, but :R otherwise
  52. #
  53. # Analog values can be referenced with the following commands:
  54. #     left_stick
  55. #     right_stick
  56. #     left_trigger
  57. #     right_trigger
  58. #
  59. # Directional values of analog sticks can be referenced with these:
  60. #     lstick4
  61. #     lstick8
  62. #     rstick4
  63. #     rstick8
  64. #
  65. # Controller vibration can be accessed with these:
  66. #     vibrate(left_motor, right_motor, frame_duration)
  67. #     set_motors(left_motor, right_motor)
  68. #
  69. # All functions take an optional gamepad id (0-3) as their final parameter.
  70. #
  71. #------------------------------------------------------------------------------
  72. # Terms of Use:
  73. #------------------------------------------------------------------------------
  74. #     If you use it, give credit. With a few caveats:
  75. #
  76. #     Can't call it alpha nay more, but I consider this script in-development.
  77. #     I make no guarantees of compatibility with other scripts.
  78. #
  79. #  Contact me via PM is you plan on using this in a commercial game.
  80. #  I probably won't charge anything, but I will want to know it's out there.
  81. #
  82. #     This script was posted at the official RPG Maker forums.
  83. #     Do modify or redistribute this script by itself, though you may
  84. #     include a configured version with your own script demos provided
  85. #     you inclide this header in its entirety.
  86. #------------------------------------------------------------------------------
  87. # Contact Info:
  88. #------------------------------------------------------------------------------
  89. # I can be reached via PM only at the RPG Maker Web forums.
  90. #                                                                                     (http://forums.rpgmakerweb.com)
  91. #
  92. # PM'ing the user Lone Wolf at other RPG Maker sites may have...
  93. # unpredicatable results. I made someone really happy the day I registered...
  94. #------------------------------------------------------------------------------
  95. # Credits:
  96. # Lone Wolf (99% of the code)
  97. # raulf17 (directional movement bug fix; now obsolete)
  98. #------------------------------------------------------------------------------
  99. # 翻訳したいんですか?いいんだが、まだ未完成です。
  100. #==============================================================================
  101. module FFS
  102.   module GamepadConfig
  103.     Keys = {
  104.       15    => "D-Pad Hoch", #d-pad up
  105.       14 => "D-Pad Runter", #d-pad left
  106.       13 => "Eingabe/D-Pad Links", #d-pad down ist auch Enter
  107.       12 => "D-Pad Rechts", #d-pad right
  108.       3     => "GP-A", #lower face button
  109.       2     => "GP-B", #right face button
  110.       1     => "GP-X", #left face button
  111.       0     => "GP-Y", #upper face button
  112.       7    => "GP-L1", #upper left trigger
  113.       6    => "GP-R1", #upper right trigger
  114.       11 => "GP-Start", #start
  115.       10 => "GP-Select", #select
  116.       9    => "GP-L3", #left thubstick button
  117.       8    => "GP-R3", #right thubstick button
  118.       16    => "Umschalt/GP-L2", #lower left trigger (press only) ist auch Shift
  119.       17    => "GP-R2", #lower right trigger (press only)
  120.       21    => "LS-Hoch", #left stick up
  121.       20    => "LS-Runter", #left stick down
  122.       19    => "LS-Links", #left stick left
  123.       18    => "LS-Rechts", #left stick right
  124.       25    => "RS-Hoch", #right stick up
  125.       24    => "RS-Runter", #right stick down
  126.       23    => "RS-Links", #right stick left
  127.       22    => "RS-Rechts" #right stick right
  128.     }
  129.   end
  130. end
  131.  
  132. module PadConfig
  133.   # Basic configuration settings:
  134.   # static:
  135.   CONTROLLERS = 1 # can be any number from 1-4, higher values may cause errors
  136.  
  137.   # can be redefined by other scripts and changed in-game:
  138.   def self.deadzone # Deadzone for axis input (%), may require adjusting
  139.     0.1
  140.   end
  141.   def self.move_with_stick  # Use left-analog stick with dir4 and dir8
  142.     true
  143.   end
  144.   def self.enable_vibration # Enable force-feedback
  145.     true
  146.   end
  147.  
  148.   # Use this section to write flexible button-mappings for your scripts.
  149.   # Add additional methods as needed.
  150.   # Format:  
  151.   #      WolfPad.plugged_in? ? (gamepad binding) : (fallback binding)
  152.   def self.confirm
  153.     WolfPad.plugged_in? ? :A : :C
  154.   end
  155.   def self.cancel
  156.     WolfPad.plugged_in? ? :B : :B
  157.   end
  158.   def self.dash
  159.     WolfPad.plugged_in? ? :X : :A
  160.   end
  161.   def self.menu
  162.     WolfPad.plugged_in? ? :Y : :B
  163.   end
  164.   def self.page_up
  165.     WolfPad.plugged_in? ? :L1 : :L
  166.   end
  167.   def self.page_down
  168.     WolfPad.plugged_in? ? :R1 : :R
  169.   end
  170.   def self.debug
  171.     WolfPad.plugged_in? ? :L2 : :CTRL
  172.   end
  173.   def self.debug2
  174.     WolfPad.plugged_in? ? :R3 : :F9
  175.   end
  176. end
  177. # Main module:
  178. module WolfPad
  179.   def self.update
  180.     for pad_index in 0...PadConfig::CONTROLLERS
  181.       input = get_state(pad_index)
  182.       if @packet[pad_index] == input[0]
  183.       set_holds(pad_index)
  184.       next
  185.       end
  186.       @packet[pad_index] = input[0]
  187.       @buttons[pad_index] = (input[1] | 0x10000).to_s(2)
  188.       @triggers[pad_index] = [input[2], input[3]]
  189.       @lstick[pad_index] = [constrain_axis(input[4]), -constrain_axis(input[5])]
  190.       @rstick[pad_index] = [constrain_axis(input[6]), -constrain_axis(input[7])]
  191.       set_holds(pad_index)
  192.     end
  193.     update_vibration
  194.     # Extra readout functions go here.
  195.     #dircheck
  196.   end
  197.   def self.test # Prints no. of detected controllers (debugging use only)
  198.     detected = 0
  199.     for i in 0...PadConfig::CONTROLLERS
  200.       self.update
  201.       detected += plugged_in?(i) ? 1 : 0
  202.     end
  203.     puts sprintf("%d XInput controller(s) in use.", detected)
  204.   end
  205.   def self.readout # prints a range from the holds table (debugging use only)
  206.     for i in 00...16
  207.       print sprintf(" %d", @holds[0, i])
  208.       print sprintf(" %d", @holds[1, i])
  209.     end
  210.     puts ";"
  211.   end
  212.   def self.dircheck
  213.     for i in 0...PadConfig::CONTROLLERS
  214.       print sprintf(" %d", key_holds(:UP, i))
  215.       print sprintf(" %d", key_holds(:LEFT, i))
  216.       print sprintf(" %d", key_holds(:DOWN, i))
  217.       print sprintf(" %d", key_holds(:RIGHT, i))
  218.       print " : "
  219.     end
  220.     puts ";"
  221.   end
  222.   # Basic vibration call.
  223.   # For simplicity, motor values should be floats from 0.0 to 1.0
  224.   def self.vibrate(left, right, duration, pad_index = 0)
  225.     return unless PadConfig.enable_vibration
  226.     set_motors(left, right, pad_index)
  227.     @vibrations[pad_index] = duration
  228.   end
  229.   # Counts down vibration event timers
  230.   def self.update_vibration
  231.     for pad in 0...PadConfig::CONTROLLERS
  232.        next if @vibrations[pad] == -1
  233.        @vibrations[pad] -= 1
  234.        if @vibrations[pad] == 0
  235.           @vibrations[pad] = -1
  236.           set_motors(0, 0, pad)
  237.        end
  238.     end
  239.   end
  240.   # Set left and right motor speeds. Vibration continues until stopped.
  241.   # Repeated calls with different values can create vibration effects.
  242.   # For simplicity, input values should be floats from 0.0 to 1.0
  243.   def self.set_motors(left, right, pad_index = 0)
  244.     return unless (PadConfig.enable_vibration) || (left == 0 && right == 0)
  245.     left_v = [left * 65535, 65535].min
  246.     right_v = [right * 65535, 65535].min
  247.     vibration = [left_v, right_v].pack("SS")
  248.     @set_state.call(pad_index, vibration)
  249.   end
  250.   def self.press?(button, pad_index = 0)
  251.     key_holds(button, pad_index) > 0
  252.   end
  253.   def self.trigger?(button, pad_index = 0)
  254.     key_holds(button, pad_index) == 1
  255.   end
  256.   def self.repeat?(button, p_i = 0)
  257.     return true if key_holds(button, p_i) == 1
  258.     return true if key_holds(button, p_i) > 30 && key_holds(button, p_i) % 5 == 0
  259.   end
  260.   # Returns left stick as a pair of floats [x, y] between -1.0 and 1.0
  261.   def self.left_stick(pad_index = 0)
  262.     @lstick[pad_index]
  263.   end
  264.   # Returns right stick as a pair of floats [x, y] between -1.0 and 1.0
  265.   def self.right_stick(pad_index = 0)
  266.     @rstick[pad_index]
  267.   end
  268.   # Returns left trigger as float between 0.0 to 1.0
  269.   def self.left_trigger(pad_index = 0)
  270.     @triggers[pad_index][0] / 255.0
  271.   end
  272.   # Returns right trigger as float between 0.0 to 1.0
  273.   def self.right_trigger(pad_index = 0)
  274.     @triggers[pad_index][1] / 255.0
  275.   end
  276.   def self.dir4(p_i = 0)
  277.     return lstick4(p_i) if PadConfig.move_with_stick
  278.     if press?(:UP, p_i)
  279.      8
  280.     elsif press?(:RIGHT, p_i)
  281.      6
  282.     elsif press?(:LEFT, p_i)
  283.      4
  284.     elsif press?(:DOWN, p_i)
  285.      2
  286.     else
  287.      0
  288.     end
  289.   end
  290.   def self.dir8(p_i = 0)
  291.     return lstick8(p_i) if PadConfig.move_with_stick
  292.     if press?(:UP, p_i) and press?(:LEFT, p_i)
  293.      7
  294.     elsif press?(:UP, p_i) and press?(:RIGHT, p_i)
  295.      9
  296.     elsif press?(:DOWN, p_i) and press?(:LEFT, p_i)
  297.      1
  298.     elsif press?(:DOWN, p_i) and press?(:RIGHT, p_i)
  299.      3
  300.     else
  301.      dir4(p_i)
  302.     end
  303.   end
  304.   # Left-stick direction
  305.   def self.lstick8(p_i = 0)
  306.       flags_to_dir8(key_holds(:L_RIGHT, p_i),key_holds(:L_LEFT, p_i),
  307.                     key_holds(:L_DOWN, p_i),key_holds(:L_UP, p_i))
  308.   end
  309.   def self.lstick4(p_i = 0)
  310.       flags_to_dir4(key_holds(:L_RIGHT, p_i),key_holds(:L_LEFT, p_i),
  311.                     key_holds(:L_DOWN, p_i),key_holds(:L_UP, p_i))
  312.   end
  313.   # Right-stick direction
  314.   def self.rstick8(p_i = 0)
  315.       flags_to_dir8(key_holds(:R_RIGHT, p_i),key_holds(:R_LEFT, p_i),
  316.                     key_holds(:R_DOWN, p_i),key_holds(:R_UP, p_i))
  317.   end
  318.   def self.rstick4(p_i = 0)
  319.       flags_to_dir4(key_holds(:R_RIGHT, p_i),key_holds(:R_LEFT, p_i),
  320.                     key_holds(:R_DOWN, p_i),key_holds(:R_UP, p_i))
  321.   end
  322.   def self.plugged_in?(pad_index = 0)
  323.     @packet[pad_index] && @packet[pad_index] > 0
  324.   end
  325.   def self.keyboard_key?(button)
  326.     !@keys.has_key?(button)
  327.   end
  328.  
  329.   #Helper functions:
  330.   # convert signed half-word axis state to float
  331.   def self.constrain_axis(axis)
  332.     val = axis.to_f / 2**15
  333.     val.abs > PadConfig.deadzone ? val : 0
  334.   end
  335.  
  336.   # derives a dir8 value from directional hold values
  337.   def self.flags_to_dir8(right, left, down, up)
  338.     x = right == left ? 0 : (right > left ? 1 : 2)
  339.     y = down == up ? 0 : (down > up ? 1 : 2)
  340.     table = [[0, 2, 8],
  341.             [ 6, 3, 9],
  342.             [ 4, 1, 7]]
  343.     return table[x][y]
  344.   end
  345.   # derives a dir4 value from directional hold values
  346.   def self.flags_to_dir4(right, left, down, up)
  347.     selection = [right, left, down, up].max
  348.     table = [0,0,down,0,left,0,right,0,up]
  349.     return table.index(selection)
  350.   end
  351.  
  352.   # calculates the precise geometric angle from stick axis values [x,y]
  353.   def self.axis_to_angle(axis)
  354.     cy = -axis[1]
  355.     cx = -axis[0]
  356.     return -1 if cy == 0 && cx == 0
  357.     angle = Math.atan2(cx, cy) * 180 / Math::PI
  358.     angle = angle < 0 ? angle + 360 : angle
  359.     return angle
  360.   end
  361.  
  362.   # Original angle-conversion handlers for analog sticks
  363.   # OBSOLETE: preserved for reference only
  364.   def self.axis_to_dir8(axis)
  365.     angle_to_dir8(axis_to_angle(axis))
  366.   end
  367.   def self.axis_to_dir4(axis)
  368.     angle_to_dir4(axis_to_angle(axis))
  369.   end
  370.  
  371.   def self.angle_to_dir8(angle)
  372.     return 0 if angle == -1
  373.     d = 0
  374.     if angle < 22.5 || angle >= 337.5
  375.      d = 8
  376.     elsif angle < 67.5
  377.      d = 7
  378.     elsif angle < 112.5
  379.      d = 4
  380.     elsif angle < 157.5
  381.      d = 1
  382.     elsif angle < 202.5
  383.      d = 2
  384.     elsif angle < 247.5
  385.      d = 3
  386.     elsif angle < 292.5
  387.      d = 6
  388.     elsif angle < 337.5
  389.      d = 9
  390.     end
  391.     return d
  392.   end
  393.   def self.angle_to_dir4(angle)
  394.     return 0 if angle == -1
  395.     d = 0
  396.     if angle < 45 || angle >= 315
  397.      d = 8
  398.     elsif angle < 135
  399.      d = 4
  400.     elsif angle < 225
  401.      d = 2
  402.     elsif angle < 315
  403.      d = 6
  404.     end
  405.     return d
  406.   end
  407.  
  408.   private # methods past here can't be called from outside
  409.   # This hash correlates RM's Input to XInput keys. Experienced Users only!
  410.   # The Input module will handle any keys not listed here (i.e. :CTRL) itself.
  411.   # Integers refer to specific gamepad button IDs.
  412.   @keys = {
  413.     :UP    => 15, #d-pad up
  414.     :DOWN => 14, #d-pad left
  415.     :LEFT => 13, #d-pad down
  416.     :RIGHT => 12, #d-pad right
  417.     :A     => 3, #lower face button
  418.     :B     => 2, #right face button
  419.     :X     => 1, #left face button
  420.     :Y     => 0, #upper face button
  421.     :L1    => 7, #upper left trigger
  422.     :R1    => 6, #upper right trigger
  423.     :START => 11,
  424.     :SELECT => 10,
  425.     :L3    => 9, #left thubstick button
  426.     :R3    => 8, #right thubstick button
  427.     :L2    => 16, #lower left trigger (press only)
  428.     :R2    => 17, #lower right trigger (press only)
  429.     :L_UP    => 21,
  430.     :L_DOWN    => 20,
  431.     :L_LEFT    => 19,
  432.     :L_RIGHT    =>18,
  433.     :R_UP    => 25,
  434.     :R_DOWN    => 24,
  435.     :R_LEFT    => 23,
  436.     :R_RIGHT    => 22
  437.     }
  438.   #Win32API calls. Leave these alone.
  439.   # Calls to XInput9_1_0.dll now only occur if DirectX is missing
  440.   @set_state = Win32API.new("XINPUT1_3", "XInputSetState", "IP", "V") rescue
  441.                 Win32API.new("XINPUT9_1_0", "XInputSetState", "IP", "V")
  442.   @get_state = Win32API.new("XINPUT1_3", "XInputGetState", "IP", "L") rescue
  443.                 Win32API.new("XINPUT9_1_0", "XInputGetState", "IP", "L")
  444.   #Initializers
  445.   # Will store data for number of gamepads in use.
  446.   @packet = Array.new(PadConfig::CONTROLLERS)
  447.   @buttons = Array.new(PadConfig::CONTROLLERS)
  448.   @triggers = Array.new(PadConfig::CONTROLLERS)
  449.   @lstick = Array.new(PadConfig::CONTROLLERS)
  450.   @rstick = Array.new(PadConfig::CONTROLLERS)
  451.   # tracks how long buttons have been pressed
  452.   @holds = Table.new(PadConfig::CONTROLLERS, 26)
  453.   # stores vibration event timers
  454.   @vibrations = Array.new(PadConfig::CONTROLLERS, -1)
  455.   def self.key_holds(symbol, pad_index)
  456.     return 0 if keyboard_key?(symbol)
  457.     @holds[pad_index, @keys[symbol]]
  458.   end
  459.   def self.get_state(pad_index)
  460.     state = "\0" * 16
  461.     @get_state.call(pad_index, state)
  462.     return state.unpack("LSCCssss")
  463.   end
  464.   def self.set_holds(p_i)
  465.     for i in 1...17
  466.      @holds[p_i, i-1] = @buttons[p_i][i].to_i > 0 ? @holds[p_i, i-1] + 1 : 0
  467.     end
  468.     @holds[p_i, 16] = left_trigger(p_i) >= 0.5 ? @holds[p_i, 16]+1 : 0
  469.     @holds[p_i, 17] = right_trigger(p_i) >= 0.5 ? @holds[p_i, 17]+1 : 0
  470.     @holds[p_i, 18] = left_stick(p_i)[0] >= 0.5 ? @holds[p_i, 18]+1 : 0
  471.     @holds[p_i, 19] = left_stick(p_i)[0] <= -0.5 ? @holds[p_i, 19]+1 : 0
  472.     @holds[p_i, 20] = left_stick(p_i)[1] >= 0.5 ? @holds[p_i, 20]+1 : 0
  473.     @holds[p_i, 21] = left_stick(p_i)[1] <= -0.5 ? @holds[p_i, 21]+1 : 0
  474.     @holds[p_i, 22] = right_stick(p_i)[0] >= 0.5 ? @holds[p_i, 22]+1 : 0
  475.     @holds[p_i, 23] = right_stick(p_i)[0] <= -0.5 ? @holds[p_i, 23]+1 : 0
  476.     @holds[p_i, 24] = right_stick(p_i)[1] >= 0.5 ? @holds[p_i, 24]+1 : 0
  477.     @holds[p_i, 25] = right_stick(p_i)[1] <= -0.5 ? @holds[p_i, 25]+1 : 0
  478.   end
  479. end
  480. # Aliases to tie the above into VXAce's Input module
  481. module Input
  482.   class <<self
  483.     alias :vxa_update :update
  484.     alias :vxa_press? :press?
  485.     alias :vxa_trigger? :trigger?
  486.     alias :vxa_repeat? :repeat?
  487.     alias :vxa_get_key_name :get_key_name
  488. #~     alias :vxa_dir4 :dir4
  489. #~     alias :vxa_dir8 :dir8
  490.   end
  491.   def self.update
  492.     WolfPad.update
  493.     vxa_update
  494.   end
  495.   def self.press?(button)
  496.     return vxa_press?(button) if WolfPad.keyboard_key?(button)
  497.     return WolfPad.press?(button) if WolfPad.plugged_in?
  498.     return vxa_press?(button)
  499.   end
  500.   def self.trigger?(button)
  501.     return vxa_trigger?(button) if WolfPad.keyboard_key?(button)
  502.     return WolfPad.trigger?(button) if WolfPad.plugged_in?
  503.     return vxa_trigger?(button)
  504.   end
  505.   def self.repeat?(button)
  506.     return vxa_repeat?(button) if WolfPad.keyboard_key?(button)
  507.     return WolfPad.repeat?(button) if WolfPad.plugged_in?
  508.     return vxa_repeat?(button)
  509.   end
  510. #~   def self.dir4
  511. #~     WolfPad.plugged_in? ? WolfPad.dir4 : vxa_dir4
  512. #~   end
  513. #~   def self.dir8
  514. #~     WolfPad.plugged_in? ? WolfPad.dir8 : vxa_dir8
  515. #~   end
  516.  
  517.   def self.get_key_name(key)
  518.     return FFS::GamepadConfig::Keys[key] if FFS::GamepadConfig::Keys.include?(key)
  519.     return vxa_get_key_name(key)
  520.   end
  521. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement