Ace of Spades
Member
Hey everyone, I ran into a bit of a problem with my RMXP project, and I'm looking for some help or advice. I'm using an Input module for custom key controls. I've tried a couple, but currently settled on using Blizz's input module for now.
The problem I'm running into is that all the scripts I've found completely rewrite the input module, and therefore don't allow for gamepad use that vanilla RMXP projects recognize by default. Is it possible to have gamepad inputs included, or at least restore the gamepad default functionality? I know there's other ways around this, but I'm trying to avoid making the player use an outside program like joy2key to map the controls to keyboard functions if they'd like to use a gamepad.
While on the topic, would there be any possibility of testing if a certain gamepad was plugged in? I'd love to have a function be able to tell which type of gamepad is plugged in, so I can show the correct controls accordingly ex (Xbox One controller prompts would vary from the PS4 controller prompts), but this might be biting off entirely more than I can chew for now. Any feedback or info would be appreciated, thanks!
Code:
# This work is protected by the following license:
#----------------------------------------------------------------------------
#
# Creative Commons - Attribution-NonCommercial-ShareAlike 3.0 Unported
# ( [url=http://creativecommons.org/licenses/by-nc-sa/3.0/]http://creativecommons.org/licenses/by-nc-sa/3.0/[/url] )
#
# You are free:
# to Share - to copy, distribute and transmit the work
# to Remix - to adapt the work
#
# Under the following conditions:
#
# Attribution. You must attribute the work in the manner specified by the
# author or licensor (but not in any way that suggests that they endorse you
# or your use of the work).
#
# Noncommercial. You may not use this work for commercial purposes.
#
# Share alike. If you alter, transform, or build upon this work, you may
# distribute the resulting work only under the same or similar license to
# this one.
#
# - For any reuse or distribution, you must make clear to others the license
# terms of this work. The best way to do this is with a link to this web
# page.
#
# - Any of the above conditions can be waived if you get permission from the
# copyright holder.
#
# - Nothing in this license impairs or restricts the author's moral rights.
#
#----------------------------------------------------------------------------
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
# Custom Game Controls by Blizzard
# Modified by kellessdee
# - added Input.keys?
# - returns array of last triggered key values
# - added Input.key?
# - optimized version of Input.keys?; returns first "found" triggered key value
# - NOTE: Some keys (such as Shift) will usually return 2 values (Shit and Left Shift or Right Shift)
# and it may NOT necessarily be the value you intended. Any values that do not exist in the
# key map WILL not be returned, i.e. removing "Shift" from the map will force only "Left Shift" or
# "Right Shift" to be returned.
# Version: 3.0
# Date: 19.4.2007
# Date v1.2: 20.10.2007
# Date v2.0b: 3.4.2008
# Date v2.1b: 25.10.2008
# Date v2.2b: 11.6.2009
# Date v3.0: 20.7.2009
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
#
# VERY IMPORTANT NOTE:
#
# DO NOT USE THIS ADD-ON IF YOU ARE USING BLIZZ-ABS!!!
#
#
# Compatiblity:
#
# 99% compatible with SDK 1.x, 90% compatible with SDK 2.x.
#
#
# Note:
#
# Why is this input module better than others? I has far less code and it
# can handle keyboard language layout.
#
#
# Explanation & Configuration:
#
# This Add-on will allow you to specify your own game controls. Just below
# is a list of possible keys, below that is the configuration. The default
# configuration is RMXP's real game control configuration. You can add any
# key specification into a key array and separate them with commas. Example:
#
# RIGHT = [Key['Arrow Right'], Key[','], Key['F'], Key['Ctrl'], Key['3'],
# Key['NumberPad 6'], Key['F3'], Key['\''], Key['\\']]
#
# This example would assign for the RIGHT button the following keys:
# - directional right (right arrow key)
# - comma
# - letter key F
# - Control key (CTRL)
# - Number Key 3 (on top over the letter keys)
# - Numberpad Key 6 (number 6 on the numberpad on the right)
# - Functional Key 3 (F3)
# - apostrophe (')
# - backslash (\)
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
#==============================================================================
# module Input
#==============================================================================
module Input
#----------------------------------------------------------------------------
# Simple ASCII table
#----------------------------------------------------------------------------
Key = {'A' => 65, 'B' => 66, 'C' => 67, 'D' => 68, 'E' => 69, 'F' => 70,
'G' => 71, 'H' => 72, 'I' => 73, 'J' => 74, 'K' => 75, 'L' => 76,
'M' => 77, 'N' => 78, 'O' => 79, 'P' => 80, 'Q' => 81, 'R' => 82,
'S' => 83, 'T' => 84, 'U' => 85, 'V' => 86, 'W' => 87, 'X' => 88,
'Y' => 89, 'Z' => 90,
'0' => 48, '1' => 49, '2' => 50, '3' => 51, '4' => 52, '5' => 53,
'6' => 54, '7' => 55, '8' => 56, '9' => 57,
'NumberPad 0' => 45, 'NumberPad 1' => 35, 'NumberPad 2' => 40,
'NumberPad 3' => 34, 'NumberPad 4' => 37, 'NumberPad 5' => 12,
'NumberPad 6' => 39, 'NumberPad 7' => 36, 'NumberPad 8' => 38,
'NumberPad 9' => 33,
'F1' => 112, 'F2' => 113, 'F3' => 114, 'F4' => 115, 'F5' => 116,
'F6' => 117, 'F7' => 118, 'F8' => 119, 'F9' => 120, 'F10' => 121,
'F11' => 122, 'F12' => 123,
';' => 186, '=' => 187, ',' => 188, '-' => 189, '.' => 190, '/' => 220,
'\\' => 191, '\'' => 222, '[' => 219, ']' => 221, '`' => 192,
'Backspace' => 8, 'Tab' => 9, 'Enter' => 13, 'Shift' => 16,
'Left Shift' => 160, 'Right Shift' => 161, 'Left Ctrl' => 162,
'Right Ctrl' => 163, 'Left Alt' => 164, 'Right Alt' => 165,
'Ctrl' => 17, 'Alt' => 18, 'Esc' => 27, 'Space' => 32, 'Page Up' => 33,
'Page Down' => 34, 'End' => 35, 'Home' => 36, 'Insert' => 45,
'Delete' => 46, 'Arrow Left' => 37, 'Arrow Up' => 38,
'Arrow Right' => 39, 'Arrow Down' => 40,
'Mouse Left' => 1, 'Mouse Right' => 2, 'Mouse Middle' => 4,
'Mouse 4' => 5, 'Mouse 5' => 6}
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# START Configuration
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
UP = [Key['Arrow Up'], Key['W']]
LEFT = [Key['Arrow Left'], Key['A']]
DOWN = [Key['Arrow Down'], Key['S']]
RIGHT = [Key['Arrow Right'], Key['D']]
A = [Key['Shift']]
B = [Key['Esc'], Key['X']]
C = [Key['Space'], Key['Enter'], Key['C']]
X = [Key['Q']]#[Key['A']]
Y = [Key['E']]#[Key['S']]
Z = [Key['R']]#[Key['D']]
L = [Key['Right Alt'], Key['Left Ctrl'] ]#[Key['Q'], Key['Page Down']]
R = [Key['Left Alt'], Key['Right Ctrl'] ]#[Key['W'], Key['Page Up']]
F5 = [Key['F5']]
F6 = [Key['F6']]
F7 = [Key['F7']]
F8 = [Key['F8']]
F9 = [Key['F9']]
SHIFT = [Key['Shift']]
CTRL = [Key['Ctrl']]
ALT = [Key['Alt']]
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# END Configuration
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# All keys
ALL_KEYS = (0...256).to_a
# Win32 API calls
GetKeyboardState = Win32API.new('user32','GetKeyboardState', 'P', 'I')
GetKeyboardLayout = Win32API.new('user32', 'GetKeyboardLayout','L', 'L')
MapVirtualKeyEx = Win32API.new('user32', 'MapVirtualKeyEx', 'IIL', 'I')
ToUnicodeEx = Win32API.new('user32', 'ToUnicodeEx', 'LLPPILL', 'L')
# some other constants
DOWN_STATE_MASK = 0x80
DEAD_KEY_MASK = 0x80000000
# data
@state = "\0" * 256
@triggered = Array.new(256, false)
@pressed = Array.new(256, false)
@released = Array.new(256, false)
@repeated = Array.new(256, 0)
#----------------------------------------------------------------------------
# update
# Updates input.
#----------------------------------------------------------------------------
def self.update
# prevents usage with Blizz-ABS
if $BlizzABS
# error message
raise 'Blizz-ABS was detected! Please turn off Custom Controls in Tons of Add-ons!'
end
# get current language layout
@language_layout = GetKeyboardLayout.call(0)
# get new keyboard state
GetKeyboardState.call(@state)
# for each key
ALL_KEYS.each {|key|
# if pressed state
if @state[key] & DOWN_STATE_MASK == DOWN_STATE_MASK
# not released anymore
@released[key] = false
# if not pressed yet
if !@pressed[key]
# pressed and triggered
@pressed[key] = true
@triggered[key] = true
else
# not triggered anymore
@triggered[key] = false
end
# update of repeat counter
@repeated[key] < 17 ? @repeated[key] += 1 : @repeated[key] = 15
# not released yet
elsif !@released[key]
# if still pressed
if @pressed[key]
# not triggered, pressed or repeated, but released
@triggered[key] = false
@pressed[key] = false
@repeated[key] = 0
@released[key] = true
end
else
# not released anymore
@released[key] = false
end}
end
#----------------------------------------------------------------------------
# dir4
# 4 direction check.
#----------------------------------------------------------------------------
def self.dir4
return 2 if self.press?(DOWN)
return 4 if self.press?(LEFT)
return 6 if self.press?(RIGHT)
return 8 if self.press?(UP)
return 0
end
#----------------------------------------------------------------------------
# dir8
# 8 direction check.
#----------------------------------------------------------------------------
def self.dir8
down = self.press?(DOWN)
left = self.press?(LEFT)
return 1 if down && left
right = self.press?(RIGHT)
return 3 if down && right
up = self.press?(UP)
return 7 if up && left
return 9 if up && right
return 2 if down
return 4 if left
return 6 if right
return 8 if up
return 0
end
#----------------------------------------------------------------------------
# trigger?
# Test if key was triggered once.
#----------------------------------------------------------------------------
def self.trigger?(keys)
keys = [keys] unless keys.is_a?(Array)
return keys.any? {|key| @triggered[key]}
end
#----------------------------------------------------------------------------
# press?
# Test if key is being pressed.
#----------------------------------------------------------------------------
def self.press?(keys)
keys = [keys] unless keys.is_a?(Array)
return keys.any? {|key| @pressed[key]}
end
#----------------------------------------------------------------------------
# repeat?
# Test if key is being pressed for repeating.
#----------------------------------------------------------------------------
def self.repeat?(keys)
keys = [keys] unless keys.is_a?(Array)
return keys.any? {|key| @repeated[key] == 1 || @repeated[key] == 16}
end
#----------------------------------------------------------------------------
# release?
# Test if key was released.
#----------------------------------------------------------------------------
def self.release?(keys)
keys = [keys] unless keys.is_a?(Array)
return keys.any? {|key| @released[key]}
end
#----------------------------------------------------------------------------
# get_character
# vk - virtual key
# Gets the character from keyboard input using the input locale identifier
# (formerly called keyboard layout handles).
#----------------------------------------------------------------------------
def self.get_character(vk)
# get corresponding character from virtual key
c = MapVirtualKeyEx.call(vk, 2, @language_layout)
# stop if character is non-printable and not a dead key
return '' if c < 32 && (c & DEAD_KEY_MASK != DEAD_KEY_MASK)
# get scan code
vsc = MapVirtualKeyEx.call(vk, 0, @language_layout)
# result string is never longer than 2 bytes (Unicode)
result = "\0" * 2
# get input string from Win32 API
length = ToUnicodeEx.call(vk, vsc, @state, result, 2, 0, @language_layout)
return (length == 0 ? '' : result)
end
#----------------------------------------------------------------------------
# get_input_string
# Gets the string that was entered using the keyboard over the input locale
# identifier (formerly called keyboard layout handles).
#----------------------------------------------------------------------------
def self.get_input_string
result = ''
# check every key
ALL_KEYS.each {|key|
# if repeated
if self.repeat?(key)
# get character from keyboard state
c = self.get_character(key)
# add character if there is a character
result += c if c != ''
end}
# empty if result is empty
return '' if result == ''
# convert string from Unicode to UTF-8
return self.unicode_to_utf8(result)
end
#----------------------------------------------------------------------------
# unicode_to_utf8
# string - string in Unicode format
# Converts a string from Unicode format to UTF-8 format as RGSS does not
# support Unicode.
#----------------------------------------------------------------------------
def self.unicode_to_utf8(string)
result = ''
string.unpack('S*').each {|c|
# characters under 0x80 are 1 byte characters
if c < 0x0080
result += c.chr
# other characters under 0x800 are 2 byte characters
elsif c < 0x0800
result += (0xC0 | (c >> 6)).chr
result += (0x80 | (c & 0x3F)).chr
# the rest are 3 byte characters
else
result += (0xE0 | (c >> 12)).chr
result += (0x80 | ((c >> 12) & 0x3F)).chr
result += (0x80 | (c & 0x3F)).chr
end}
return result
end
# Which keys were triggered?
# @author kellessdee
# @return [Integer] Key value(s) (ascii) which were triggered
def self.keys?
input = []
keys = Key.invert
@triggered.each_index { |i|
if @triggered[i] && keys[i]
input << i
end
}
return input
end
# Which key was triggered?
# @author kellessdee
# returns Integer key value (ascii) which was triggered
def self.key?
keys = Key.invert
@triggered.each_index { |i|
if @triggered[i] && key[i]
return i
end
}
end
end
The problem I'm running into is that all the scripts I've found completely rewrite the input module, and therefore don't allow for gamepad use that vanilla RMXP projects recognize by default. Is it possible to have gamepad inputs included, or at least restore the gamepad default functionality? I know there's other ways around this, but I'm trying to avoid making the player use an outside program like joy2key to map the controls to keyboard functions if they'd like to use a gamepad.
While on the topic, would there be any possibility of testing if a certain gamepad was plugged in? I'd love to have a function be able to tell which type of gamepad is plugged in, so I can show the correct controls accordingly ex (Xbox One controller prompts would vary from the PS4 controller prompts), but this might be biting off entirely more than I can chew for now. Any feedback or info would be appreciated, thanks!