• Do not use Discord to host any images you post, these links expire quickly! You can learn how to add images to your posts here.
Resource icon

Essentials Gamepad Compatibility 2017-09-24

Pokémon Essentials Version
v16.2 ➖
I was investigating a bit with ruby and i wanted to see in deep how did win32API worked. For my first try i decided to get on with the XInput dll and... well, this come out.
Essentials Gamepad Input is an easy to install script ready to be use directly in essentials with any xinput compatible gamepad and without any need of doing modifications or external programs as xpadder.
You can also use Dualshock controllers, but you will need to use Xinput Wrapper in order to make it work.

Installing it is as easy as following one simple step:

Replace PSystem_Controls with this
Code:
Expand Collapse Copy
def pbSameThread(wnd)
  return false if wnd==0
  processid=[0].pack('l')
  getCurrentThreadId=Win32API.new('kernel32','GetCurrentThreadId', '%w()','l')
  getWindowThreadProcessId=Win32API.new('user32','GetWindowThreadProcessId', '%w(l p)','l')
  threadid=getCurrentThreadId.call
  wndthreadid=getWindowThreadProcessId.call(wnd,processid)
  return (wndthreadid==threadid)
end
 
 
 
module Input
  DOWN  = 2
  LEFT  = 4
  RIGHT = 6
  UP	= 8
  A	 = 11
  B	 = 12
  C	 = 13
  X	 = 14
  Y	 = 15
  Z	 = 16
  L	 = 17
  R	 = 18
  SHIFT = 21
  CTRL  = 22
  ALT   = 23
  F5	= 25
  F6	= 26
  F7	= 27
  F8	= 28
  F9	= 29
  LeftMouseKey  = 1
  RightMouseKey = 2
  # GetAsyncKeyState or GetKeyState will work here
  @GetKeyState=Win32API.new("user32", "GetAsyncKeyState", "i", "i")
  @GetForegroundWindow=Win32API.new("user32", "GetForegroundWindow", "", "i")
  # Returns whether a key is being pressed
  @PadState=Win32API.new("xinput1_3.dll","XInputGetState","ip","l")
  Buttons=[64,128,32,16,256,512,1024,2048,1,16384,2,32768]
  Assign=[64,128,32,16,256,512,1024,2048,1,16384,2,32768]
  @counter=0
  @DeadZone=[]
  def self.getstate(key)
	return (@GetKeyState.call(key)&0x8000)>0
  end
 
 
  def self.PadState(key) #Basicamente, el nucleo de todo esto, all hail this method
	inputData= "\0" * 16
	@PadState.call(0,inputData)
	arr=inputData.unpack("ssnnssss")
	 if @DeadZone.empty?
	   @DeadZone.push(arr[4],arr[5],arr[6],arr[7])
	 end
	 if key==Assign[6] && (arr[4]==-32767||arr[6]==-32767)
	   return true
	 end
	 if key==Assign[7] && (arr[6]==32767 || arr[4]==32767)
	   return true
	 end
	 if key==Assign[4] && (arr[5]==32767 || arr[7]==32767)
	   return true
	 end
	 if key==Assign[5] && (arr[5]==-32767 || arr[7]==-32767)
	   return true
	 end
	 if key==Assign[3] && arr[3]>100
	   return true
	 end
	 
	 
	if arr[2]==key
	  return true
	end
	return false
  end
 
  def self.State2(key)
	data=self.PadState(key)
	code=self.GetCode(key)
	if data==true && @padPState[code]==false
	  @padPState[code]=true
	  @padKeyState[code]+=1
	 return true
   end
	if data==false
	  @padPState[code]=false
	  @padKeyState[code]=0
	  return false
	end
	if data==true && @padPState[code]==true
	  @padKeyState[code]+=1
	return false
  end
	return false
  end
 
  def self.State(key)
	data=self.PadState(key)
	code=self.GetCode(key)
	if data==true && @padPressState[code]==false
	  @padPressState[code]=true
	  @padKeyState[code]+=1
	 return true
   end
	if data==false
	  @padPressState[code]=false
	  @padKeyState[code]=0
	  return false
	end
	if data==true && @padPressState[code]==true
	  @padKeyState[code]+=1
	return false
  end
	return false
  end
   
 
  def self.padVibrate(vibration1,vibration2)
	@SetState=Win32API.new("xinput1_3.dll","XInputSetState","ip","")
	@SetState.call(0,"#{vibration1},#{vibration2}")
  end
 
  def self.updateKeyState(i)
	gfw=pbSameThread(@GetForegroundWindow.call())
	if !@stateUpdated[i]
	  newstate=self.getstate(i) && gfw
	  @triggerstate[i]=(newstate&&@keystate[i]==0)
	  @releasestate[i]=(!newstate&&@keystate[i]>0)
	  @keystate[i]=newstate ? @keystate[i]+1 : 0
	  @stateUpdated[i]=true
	end
  end
 
  def self.Update(i,identifier=0)
	gfw = pbSameThread(@GetForegroundWindow.call())
	if !@padStateUpdated[i]
	state=(self.PadState(Assign[i]) && gfw)
	 @padTriggerState[i]=(state && @padKeyState[i]==0)
	 @padKeyState[i]=(state ? @padKeyState[i]+1 : 0)
	 @padReleaseState[i]=(!state && @padKeyState[i]>0)
	 @padStateUpdated[i]=true
   end
 end
 
  def self.update
	if @keystate
	  for i in 0...256
		# just noting that the state should be updated
		# instead of thunking to Win32 256 times
		@stateUpdated[i]=false
		if @keystate[i] > 0
		  # If there is a repeat count, update anyway
		  # (will normally apply only to a very few keys)
		  updateKeyState(i)
		end
		   if @counter>Assign.size
			 @counter=0
		   end
		   @padStateUpdated[@counter]=false
			if @padKeyState
		   if @padKeyState[@counter]>0
			 self.Update(@counter)
		   end
		 end
		  @counter+=1
		end
	   else
	  @stateUpdated=[]
	  @keystate=[]
	  @triggerstate=[]
	  @releasestate=[]
	  @padStateUpdated=[]
	  @padKeyState=[]
	  @padTriggerState=[]
	  @padPressState=[]
	  @padPState=[]
	  @padReleaseState=[]
	  for i in 0...256
		@stateUpdated[i]=true
		@keystate[i]=self.getstate(i) ? 1 : 0
		@triggerstate[i]=false
		@releasestate[i]=false
	   if i<=Assign.size
		@padStateUpdated[i]=true
		@padKeyState[i]= self.PadState(i) ? 1 : 0
		@padTriggerState[i]=false
		@padPressState[i]=false
		@padReleaseState[i]=false
		@padPState[i]=false
	   end
	  end
	end
  end
 
  def self.buttonToKey(button)
	case button
	when Input::DOWN
	  return [0x28] # Down
	when Input::LEFT
	  return [0x25] # Left
	when Input::RIGHT
	  return [0x27] # Right
	when Input::UP
	  return [0x26] # Up
	when Input::A
	  return [0x5A,0x10] # Z, Shift
	when Input::B
	  return [0x58,0x1B] # X, ESC
	when Input::C
	  return [0x43,0x0D,0x20] # C, ENTER, Space
	when Input::X
	  return [0x41] # A
	when Input::Y
	  return [0x53] # S
	when Input::Z
	  return [0x44] # D
	when Input::L
	  return [0x51,0x21] # Q, Page Up
	when Input::R
	  return [0x57,0x22] # W, Page Down
	when Input::SHIFT
	  return [0x10] # Shift
	when Input::CTRL
	  return [0x11] # Ctrl
	when Input::ALT
	  return [0x12] # Alt
	when Input::F5
	  return [0x74] # F5
	when Input::F6
	  return [0x75] # F6
	when Input::F7
	  return [0x76] # F7
	when Input::F8
	  return [0x77] # F8
	when Input::F9
	  return [0x78] # F9
	else
	  return []
	end
  end
 
	def self.PadCode(button) #Returns the key value
	case button
	when Input::UP; return [Assign[4]]
	when Input::DOWN; return [Assign[5]]
	when Input::LEFT; return [Assign[6]]
	when Input::RIGHT; return [Assign[7]]
	when Input::B; return [Assign[1]]
	when Input::C; return [Assign[2]]
	when Input::A; return [Assign[3]]
	when Input::L; return [Assign[8]]
	when Input::R; return [Assign[10]]
	end
	return []
  end
 
  def self.dir4
	button=0
	repeatcount=0
	if self.press?(Input::DOWN) && self.press?(Input::UP)
	  return 0
	end
	if self.press?(Input::LEFT) && self.press?(Input::RIGHT)
	  return 0
	end
	for b in [Input::DOWN,Input::LEFT,Input::RIGHT,Input::UP]
	  rc=self.count(b)
	  if rc>0
		if repeatcount==0 || rc<repeatcount
		  button=b
		  repeatcount=rc
		end
	  end
	end
	return button
  end
 
  def self.dir8
	buttons=[]
	for b in [Input::DOWN,Input::LEFT,Input::RIGHT,Input::UP]
	  rc=self.count(b)
	  if rc>0
		buttons.push([b,rc])
	  end
	end
	if buttons.length==0
	  return 0
	elsif buttons.length==1
	  return buttons[0][0]
	elsif buttons.length==2
	  # since buttons sorted by button, no need to sort here
	  if (buttons[0][0]==Input::DOWN && buttons[1][0]==Input::UP)
		return 0
	  end
	  if (buttons[0][0]==Input::LEFT && buttons[1][0]==Input::RIGHT)
		return 0
	  end
	end
	buttons.sort!{|a,b| a[1]<=>b[1]}
	updown=0
	leftright=0
	for b in buttons
	  if updown==0 && (b[0]==Input::UP || b[0]==Input::DOWN)
		updown=b[0]
	  end
	  if leftright==0 && (b[0]==Input::LEFT || b[0]==Input::RIGHT)
		leftright=b[0]
	  end
	end
	if updown==Input::DOWN
	  return 1 if leftright==Input::LEFT
	  return 3 if leftright==Input::RIGHT
	  return 2
	elsif updown==Input::UP
	  return 7 if leftright==Input::LEFT
	  return 9 if leftright==Input::RIGHT
	  return 8
	else
	  return 4 if leftright==Input::LEFT
	  return 6 if leftright==Input::RIGHT
	  return 0
	end
  end
 
  def self.count(button)
	for btn in self.buttonToKey(button)
	  c=self.repeatcount(btn)
	  return c if c>0
	end
	for btn in self.PadCode(button)
	  c=self.padRepeatCount(btn)
	  return c if c>0
	end
	return 0
  end
 
  def self.release?(button)
	rc=0
	for btn in self.buttonToKey(button)
	  c=self.repeatcount(btn)
	  return false if c>0
	  rc+=1 if self.releaseex?(btn)
	end
	for btn in self.PadCode(button)
	  c=self.padRepeatCount(btn)
	  return false if c>0
	  rc+=1 if self.padReleaseex?(btn)
	end
	return rc>0
  end
 
  def self.trigger?(button)
	trigger= self.buttonToKey(button).any? {|item| self.triggerex?(item) }
	pad = self.PadCode(button).any? {|item| self.padTriggerex?(item)}
	return trigger || pad
  end
 
  def self.repeat?(button)
	trigger= self.buttonToKey(button).any? {|item| self.repeatex?(item) }
	pad= self.PadCode(button).any? {|item| self.padRepeatex?(item)}
	return trigger || pad
  end
 
  def self.press?(button)
	return self.count(button)>0
  end
 
  def self.repeatex?(key)
	return false if !@keystate
	updateKeyState(key)
	return @keystate[key]==1 || (@keystate[key]>20 && (@keystate[key]&1)==0)
  end
 
  def self.padRepeatex?(key)
	return false if !@padKeyState
	code=GetCode(key)
	self.State2(key)
	return @padKeyState[code]==1 || (@padKeyState[code]>20 && (@padKeyState[code]&1)==0)
  end
 
  def self.releaseex?(key)
	return false if !@releasestate
	updateKeyState(key)
	return @releasestate[key]
  end
 
  def self.padReleasex?(key)
	return false if !@padReleaseState
	code=self.GetCode(key)
	self.Update(code)
	return @padReleaseState[code]
  end
 
  def self.triggerex?(key)
	return false if !@triggerstate
	updateKeyState(key)
	return @triggerstate[key]
  end
 
  def self.padTriggerex?(key)
	return false if !@padTriggerState
	code= self.GetCode(key)
	result=self.State(key)
	return result
  end
 
  def self.repeatcount(key)
	return 0 if !@keystate
	updateKeyState(key)
	return @keystate[key]
  end
 
  def self.padRepeatCount(key)
	return 0 if !@padKeyState
	code= self.GetCode(key)
	result=self.Update(code)
	return @padKeyState[code]
  end
 
  def self.pressex?(key)
	trigger= self.repeatcount(key)>0
	pad= self.repeatcount(key)>0
	return trigger || pad
  end
 
  def self.RunToogle
	  Kernel.pbMessage(_INTL("wds"))
	  $PokemonSystem.runstyle=1
	  $PokemonGlobal.runtoogle=true
  end
 
  def self.GetCode(key)
	for i in 0..Assign.size
	  if Assign[i]==key
		return i
	  end
	end
	return 100
  end
end
 
# Requires Win32API
module Mouse
  gsm = Win32API.new('user32', 'GetSystemMetrics', 'i', 'i')
  @GetCursorPos = Win32API.new('user32', 'GetCursorPos', 'p', 'i')
  @SetCapture = Win32API.new('user32', 'SetCapture', 'p', 'i')
  @ReleaseCapture = Win32API.new('user32', 'ReleaseCapture', '', 'i')
  module_function
  def getMouseGlobalPos
	pos = [0, 0].pack('ll')
	if @GetCursorPos.call(pos) != 0
	  return pos.unpack('ll')
	else
	  return nil
	end
  end
 
  def screen_to_client(x, y)
	return nil unless x and y
	screenToClient = Win32API.new('user32', 'ScreenToClient', %w(l p), 'i')
	pos = [x, y].pack('ll')
	if screenToClient.call(Win32API.pbFindRgssWindow, pos) != 0
	  return pos.unpack('ll')
	else
	  return nil
	end
  end
 
  def setCapture
	@SetCapture.call(Win32API.pbFindRgssWindow)
  end
 
  def releaseCapture
	@ReleaseCapture.call
  end
 
  # Returns the position of the mouse relative to the game window.
  def getMousePos(catch_anywhere = false)
	resizeFactor=($ResizeFactor) ? $ResizeFactor : 1
	x, y = screen_to_client(*getMouseGlobalPos)
	width, height = Win32API.client_size
	if catch_anywhere or (x >= 0 and y >= 0 and x < width and y < height)
	  return (x/resizeFactor).to_i, (y/resizeFactor).to_i
	else
	  return nil
	end
  end
 
  def del
	if @oldcursor == nil
	  return
	else
	  @SetClassLong.call(Win32API.pbFindRgssWindow,-12, @oldcursor)
	  @oldcursor = nil
	end
  end
end

CURRENT ERRORS
-As this is an XInput based script is possible that some controllers
aren´t compatible with this script, in order to make them work you will need to use external programs as XInputWrapper (for dualshocks) or x360ce (i dind´t test out if this one works, so i can´t ensure it to be compatible with rpg maker)


-Although is very rare that it occurs,for some reasons i still don´t know in some menus as the default essentials pause menu you will need to press the A button several times in order for it to response.
CONTROLS(Following an xbox controller structure)
button B= C key
button A=Z key
button Y=X key
DPAD or Joysticks=Directional arrows
R2 or L2=Run

TO BE DONE
- Give functionality to the remaining buttons
-Create a screen to customize the button functionality
-Solve problems as the one of the pause menu


if you have any suggestion or you want to report an error, please leave a comment.
Credits
  • Kyu
Author
Kyu
Views
3,737
First release
Last update

Ratings

0.00 star(s) 0 ratings

More resources from Kyu

Back
Top