Derrick
05/02/2009, 10:37 pm
I wrote an AutoHotkey script that will help users control the movement in W&G with the mouse instead of the keyboard.
There are 2 methods of movement when using this script. Both are accessed by pressing either the left or right mouse button. You may find that when you use the left button for moving, you may accidentally click and trigger a selectable object. That is why I also allow for using the right button to move. It will not trigger the items, so I highly recommend using it instead of the left button.
Method 1:
When you click on the edge of the screen, it will simulate the arrow key for as long as you hold the mouse button down. The corners of the screen will press 2 arrows for diagonal movement.
Method 2:
When the cursor is not at the edges of the screen, you can press and hold the mouse button for movement. After a short user specified delay, the mouse movement will be limited to a small box. Moving to the edges and diagonals of this box will cause the corresponding arrow keys to be pressed. You can use this method anywhere on the screen as long as it is 1 movement box in size from the edge of the screen. The box defaults to 100 pixels, but you can change that.
When the game is running in a window instead of fullscreen, the movement works the same. When you click the mouse in the window it will lock itself into the window so you can easily use Movement Method 1. The mouse will free itself to the full window by pressing the 'F' key or by [Alt][Tab]ing to another running program.
To get this running you will need to install AutoHotkey available here:
www.autohotkey.com
Then download the attached script. Unzip the file some where. When you run it, it will launch the game and you are good to go. When you quit the game, the script will quit so mouse clicks are no longer remapped as arrow keys.
You can also right click on the script file and compile it if you like. Then it can be run on other computers without installing AutoHotkey.
You can edit settings in the script file by right clicking on it and picking Edit Script. Then at the start of the file, look for the following lines to change as you like:
How to change the game:
set episode to 0 for the demo. 1, 2, 3, 4 for the corresponding episode. Other values will give you an error and quit the script.
As of writing this, episodes 3&4 have not been released so I can not test them. I believe it should work fine.
; Set this to the W&G episode you want to run with this script
; 0 = Demo
episode := 1
How to change the button functions:
* Remember using the left button for movement can cause triggerable items to be selected.
set the following values to 0 to disable and 1 to enable.
useEdgesL - enables Movement Method 1 for the left mouse button.
useCenterL - enables Movement Method 2 for the left mouse button.
useEdgesR - enables Movement Method 1 for the right mouse button.
useCenterR - enables Movement Method 2 for the right mouse button.
; set options to 0=disable 1=enable
; Left Button options
useEdgesL := 1
useCenterL := 1
; Right Button options
useEdgesR := 1
useCenterR := 1
How to change the delay before Movement Method 2 is activated:
buttonDelayL - time in milliseconds to press the left mouse button.
buttonDelayR - time in milliseconds to press the right mouse button.
; how long in milliseconds to press mouse button in center screen before movement is controlled by mouse dragging
; left move delay
buttonDelayL := 150
; right move delay
buttonDelayR := 50
How to change the Movement Method 2 box size:
boxSize - height and width in pixels of the restricted mouse movement for Movement Method 2
; movement box size in pixels
boxSize := 100
How to change how wide of a selection area at the edge of the game view window that can be pressed for Movement Method 1:
borderPixels - number of pixels in from the edge
; pixels at edge used to select movement
; must be 1 to 25
borderPixels := 5
Have fun. Hopefully TTG does not delete this. If they feel the need to, then please p-mail me with the reason for removing this thread/info.
If for some reason the download is corrupt, you can just copy and paste the following code into a new script file called wg.ahk
; Script Function:
; Map mouse clicks at the edges of the screen as arrow keys
; You can also move by holding down the button and dragging the mouse
; The script will time out after 2 minutes if game is not launched
; Press 'F' in windowed mode to free the cursor
; Set this to the W&G episode you want to run with this script
; 0 = Demo
episode := 1
; set options to 0=disable 1=enable
; Left Button options
useEdgesL := 1
useCenterL := 1
; Right Button options
useEdgesR := 1
useCenterR := 1
; how long in milliseconds to press mouse button in center screen before movement is controlled by mouse dragging
; left move delay
buttonDelayL := 150
; right move delay
buttonDelayR := 50
; movement box size in pixels
boxSize := 100
; pixels at edge used to select movement
; must be 1 to 25
borderPixels := 5
; ==================================
; only change values above this line
; ==================================
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
#SingleInstance ignore ; only allow 1 running script
SetTitleMatchMode, 3 ; Names must be exact
; check values
if (borderPixels < 1 OR borderPixels > 25)
{
MsgBox, borderPixels value out of range
ExitApp
}
timeDelayL := buttonDelayL // 10
timeDelayR := buttonDelayR // 10
movePixels := boxSize // 2
edgeMargin := movePixels * 2 + borderPixels
border := borderPixels
mouseLocked := 0
; setup paths and file
if (episode = 0)
{
gameFile = WallaceGromitDemo.exe
launcherName = Wallace & Gromit Demo
}
else if (episode = 1)
{
gameFile = WallaceGromit101.exe
launcherName = Fright of the Bumblebees
}
else if (episode = 2)
{
gameFile = WallaceGromit102.exe
launcherName = The Last Resort
}
else if (episode = 3)
{
gameFile = WallaceGromit103.exe
launcherName = Muzzled!
}
else if (episode = 4)
{
gameFile = WallaceGromit104.exe
launcherName = The Bogey Man
}
else
{
MsgBox, Invalid Episode #
ExitApp
}
gameReg = SOFTWARE\Telltale Games\%gameFile%
RegRead, gameDir, HKEY_LOCAL_MACHINE, %gameReg% , Install Location
; this is the name of the game as shown on the task bar
; Demo, Ep1 and Ep2 all use the same name, so I assume all episodes will
gameName = Telltale Games
; launch game
Run, %gameFile%, %gameDir%, UseErrorLevel
if ErrorLevel = ERROR
{
MsgBox, Episode %episode% could not be found
ExitApp
}
winwait, %gameName%, , 120
if ErrorLevel
{
ExitApp
}
gameID := WinExist(gameName)
WinActivate, ahk_id %gameID%
SetTimer, gameCheck ; check every 250ms
; setup rect variable for DLL call
VarSetCapacity(rect, 16)
return
gameCheck:
;------------------------------
; quit script on game exit
;------------------------------
if ((mouseLocked AND isFullScreen()) OR !WinActive("ahk_id" . gameID))
{
; free the mouse if we switched modes or window no longer active
DllCall("ClipCursor")
mouseLocked := 0
}
IfWinNotExist, ahk_id %gameID%
{
; free the mouse just in case
DllCall("ClipCursor")
ExitApp
}
return
~LButton::
useEdges := useEdgesL
useCenter := useCenterL
timeDelay := timeDelayL
testButton = LButton
Gosub, mouseToArrows
return
~RButton::
useEdges := useEdgesR
useCenter := useCenterR
timeDelay := timeDelayR
testButton = RButton
Gosub, mouseToArrows
return
mouseToArrows:
;------------------------------
; process mouse movement
;------------------------------
usingCenterMove := 0
if (isFullScreen())
{
; fullscreen
CoordMode, Mouse, Screen
width := A_ScreenWidth
height := A_ScreenHeight
winXpos := 0
winYpos := 0
winXoffset := 0
winYoffset := 0
}
else
{
; only do mouse to key conversion in game window
IfWinNotActive, ahk_id %gameID%
Return,
; get window information
CoordMode, Mouse, Relative
WinGetPos, winXpos, winYpos, winWidth, winHeight, ahk_id %gameID%
; calculate border info from client window
; we do it at every mouse press because the size could change
DllCall("GetClientRect", "UInt", gameID, "UInt", &rect)
width := NumGet(rect, 8, "int")
height := NumGet(rect, 12, "int")
winBorderPixels := (winWidth - width) // 2
winBarPixels := winHeight - height - winBorderPixels
; free up mouse if on top bar
MouseGetPos, , curY
if (curY < winBarPixels)
Return,
winXoffset := winBorderPixels
winYoffset := winBarPixels
; set top left of game window
; keep locking the window so the mouse does not get free
mouseLocked := 1
Gosub, lockWindow
}
; check aspect ratio and adjust
if (width * 9 // height < 16)
{
; top and bottom letterboxed
newHeight := width * 9 // 16
edgeLeft := 0
edgeRight := width
edgeUp := (height - newHeight) // 2
edgeDown := edgeUp + newHeight
}
else
{
; sides letterboxed
newWidth := height * 16 // 9
edgeLeft := (width - newWidth) // 2
edgeRight := edgeLeft + newWidth
edgeUp := 0
edgeDown := height
}
; bottom corner is one pixel in
edgeRight--
edgeDown--
if (useCenter = 1)
{
timePressed := 0
; the valid center movement area
centerLeft := edgeLeft + edgeMargin
centerRight := edgeRight - edgeMargin
centerUp := edgeUp + edgeMargin
centerDown := edgeDown - edgeMargin
}
While GetKeyState(testButton)
{
MouseGetPos, curX, curY
if (!usingCenterMove)
{
; adjust for window border
curX -= winXoffset
curY -= winYoffset
}
; remap mouse to arrow keys
; only press the down key once
if (useEdges = 1 OR usingCenterMove = 1)
{
if (curX < edgeLeft + border)
{
if (!downL)
{
Send {Left Down}
downL := 1
}
}
else
{
if (downL)
{
Send {Left Up}
downL := 0
}
}
if (curX > edgeRight - border)
{
if (!downR)
{
Send {Right Down}
downR := 1
}
}
else
{
if (downR)
{
Send {Right Up}
downR := 0
}
}
if (curY < edgeUp + border)
{
if (!downU)
{
Send {Up Down}
downU := 1
}
}
else
{
if (downU)
{
Send {Up Up}
downU := 0
}
}
if (curY > edgeDown - border)
{
if (!downD)
{
Send {Down Down}
downD := 1
}
}
else
{
if (downD)
{
Send {Down Up}
downD := 0
}
}
}
if (useCenter AND !usingCenterMove)
{
; start counting if in center of screen
if (curX > centerLeft AND curX < centerRight AND curY > centerUp AND curY < centerDown)
{
timePressed++
if (timePressed > timeDelay)
{
; button down long enough, start using center move
; readjust mousepos to real and setup movement box
curX += winXoffset
curY += winYoffset
edgeLeft := curX - movePixels
edgeUp := curY - movePixels
edgeRight := curX + movePixels
edgeDown := curY + movePixels
; fill the RECT structure with UInt values
NumPut(winXpos + edgeLeft , &rect + 0)
NumPut(winYpos + edgeUp , &rect + 4)
NumPut(winXpos + edgeRight + 1, &rect + 8)
NumPut(winYpos + edgeDown + 1, &rect + 12)
DllCall("ClipCursor", UInt, &rect)
usingCenterMove := 1
border := 1
}
}
else
{
; reset count when not in center
timePressed := 0
}
}
Sleep, 10
}
if (usingCenterMove)
{
; free the cursor
if (mouseLocked)
Gosub, lockWindow
else
DllCall("ClipCursor")
; reset to view border
border := borderPixels
}
; unpress any keys that are down
if (downL)
{
Send {Left Up}
downL := 0
}
if (downR)
{
Send {Right Up}
downR := 0
}
if (downU)
{
Send {Up Up}
downU := 0
}
if (downD)
{
Send {Down Up}
downD := 0
}
Return,
isFullScreen()
;------------------------------
; check to see if fullscreen
;------------------------------
{
Global gameID
WinGet, style, Style, ahk_id %gameID%
; 0x800000 is WS_BORDER.
; 0x20000000 is WS_MINIMIZE.
; no border and not minimized
Return, (style & 0x20800000) ? 0 : 1
}
~f::
;------------------------------
; free mouse from window
;------------------------------
if (mouseLocked)
{
IfWinActive, ahk_id %gameID%
{
; free the cursor
DllCall("ClipCursor")
mouseLocked := 0
}
}
Return,
lockWindow:
;------------------------------
; lock mouse to window
;------------------------------
; fill the RECT structure with UInt values
NumPut(winXpos + winBorderPixels, &rect + 0)
NumPut(winYpos + winBarPixels, &rect + 4)
NumPut(winXpos + winBorderPixels + width, &rect + 8)
NumPut(winYpos + winBarPixels + height, &rect + 12)
DllCall("ClipCursor", UInt, &rect)
Return,
There are 2 methods of movement when using this script. Both are accessed by pressing either the left or right mouse button. You may find that when you use the left button for moving, you may accidentally click and trigger a selectable object. That is why I also allow for using the right button to move. It will not trigger the items, so I highly recommend using it instead of the left button.
Method 1:
When you click on the edge of the screen, it will simulate the arrow key for as long as you hold the mouse button down. The corners of the screen will press 2 arrows for diagonal movement.
Method 2:
When the cursor is not at the edges of the screen, you can press and hold the mouse button for movement. After a short user specified delay, the mouse movement will be limited to a small box. Moving to the edges and diagonals of this box will cause the corresponding arrow keys to be pressed. You can use this method anywhere on the screen as long as it is 1 movement box in size from the edge of the screen. The box defaults to 100 pixels, but you can change that.
When the game is running in a window instead of fullscreen, the movement works the same. When you click the mouse in the window it will lock itself into the window so you can easily use Movement Method 1. The mouse will free itself to the full window by pressing the 'F' key or by [Alt][Tab]ing to another running program.
To get this running you will need to install AutoHotkey available here:
www.autohotkey.com
Then download the attached script. Unzip the file some where. When you run it, it will launch the game and you are good to go. When you quit the game, the script will quit so mouse clicks are no longer remapped as arrow keys.
You can also right click on the script file and compile it if you like. Then it can be run on other computers without installing AutoHotkey.
You can edit settings in the script file by right clicking on it and picking Edit Script. Then at the start of the file, look for the following lines to change as you like:
How to change the game:
set episode to 0 for the demo. 1, 2, 3, 4 for the corresponding episode. Other values will give you an error and quit the script.
As of writing this, episodes 3&4 have not been released so I can not test them. I believe it should work fine.
; Set this to the W&G episode you want to run with this script
; 0 = Demo
episode := 1
How to change the button functions:
* Remember using the left button for movement can cause triggerable items to be selected.
set the following values to 0 to disable and 1 to enable.
useEdgesL - enables Movement Method 1 for the left mouse button.
useCenterL - enables Movement Method 2 for the left mouse button.
useEdgesR - enables Movement Method 1 for the right mouse button.
useCenterR - enables Movement Method 2 for the right mouse button.
; set options to 0=disable 1=enable
; Left Button options
useEdgesL := 1
useCenterL := 1
; Right Button options
useEdgesR := 1
useCenterR := 1
How to change the delay before Movement Method 2 is activated:
buttonDelayL - time in milliseconds to press the left mouse button.
buttonDelayR - time in milliseconds to press the right mouse button.
; how long in milliseconds to press mouse button in center screen before movement is controlled by mouse dragging
; left move delay
buttonDelayL := 150
; right move delay
buttonDelayR := 50
How to change the Movement Method 2 box size:
boxSize - height and width in pixels of the restricted mouse movement for Movement Method 2
; movement box size in pixels
boxSize := 100
How to change how wide of a selection area at the edge of the game view window that can be pressed for Movement Method 1:
borderPixels - number of pixels in from the edge
; pixels at edge used to select movement
; must be 1 to 25
borderPixels := 5
Have fun. Hopefully TTG does not delete this. If they feel the need to, then please p-mail me with the reason for removing this thread/info.
If for some reason the download is corrupt, you can just copy and paste the following code into a new script file called wg.ahk
; Script Function:
; Map mouse clicks at the edges of the screen as arrow keys
; You can also move by holding down the button and dragging the mouse
; The script will time out after 2 minutes if game is not launched
; Press 'F' in windowed mode to free the cursor
; Set this to the W&G episode you want to run with this script
; 0 = Demo
episode := 1
; set options to 0=disable 1=enable
; Left Button options
useEdgesL := 1
useCenterL := 1
; Right Button options
useEdgesR := 1
useCenterR := 1
; how long in milliseconds to press mouse button in center screen before movement is controlled by mouse dragging
; left move delay
buttonDelayL := 150
; right move delay
buttonDelayR := 50
; movement box size in pixels
boxSize := 100
; pixels at edge used to select movement
; must be 1 to 25
borderPixels := 5
; ==================================
; only change values above this line
; ==================================
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
#SingleInstance ignore ; only allow 1 running script
SetTitleMatchMode, 3 ; Names must be exact
; check values
if (borderPixels < 1 OR borderPixels > 25)
{
MsgBox, borderPixels value out of range
ExitApp
}
timeDelayL := buttonDelayL // 10
timeDelayR := buttonDelayR // 10
movePixels := boxSize // 2
edgeMargin := movePixels * 2 + borderPixels
border := borderPixels
mouseLocked := 0
; setup paths and file
if (episode = 0)
{
gameFile = WallaceGromitDemo.exe
launcherName = Wallace & Gromit Demo
}
else if (episode = 1)
{
gameFile = WallaceGromit101.exe
launcherName = Fright of the Bumblebees
}
else if (episode = 2)
{
gameFile = WallaceGromit102.exe
launcherName = The Last Resort
}
else if (episode = 3)
{
gameFile = WallaceGromit103.exe
launcherName = Muzzled!
}
else if (episode = 4)
{
gameFile = WallaceGromit104.exe
launcherName = The Bogey Man
}
else
{
MsgBox, Invalid Episode #
ExitApp
}
gameReg = SOFTWARE\Telltale Games\%gameFile%
RegRead, gameDir, HKEY_LOCAL_MACHINE, %gameReg% , Install Location
; this is the name of the game as shown on the task bar
; Demo, Ep1 and Ep2 all use the same name, so I assume all episodes will
gameName = Telltale Games
; launch game
Run, %gameFile%, %gameDir%, UseErrorLevel
if ErrorLevel = ERROR
{
MsgBox, Episode %episode% could not be found
ExitApp
}
winwait, %gameName%, , 120
if ErrorLevel
{
ExitApp
}
gameID := WinExist(gameName)
WinActivate, ahk_id %gameID%
SetTimer, gameCheck ; check every 250ms
; setup rect variable for DLL call
VarSetCapacity(rect, 16)
return
gameCheck:
;------------------------------
; quit script on game exit
;------------------------------
if ((mouseLocked AND isFullScreen()) OR !WinActive("ahk_id" . gameID))
{
; free the mouse if we switched modes or window no longer active
DllCall("ClipCursor")
mouseLocked := 0
}
IfWinNotExist, ahk_id %gameID%
{
; free the mouse just in case
DllCall("ClipCursor")
ExitApp
}
return
~LButton::
useEdges := useEdgesL
useCenter := useCenterL
timeDelay := timeDelayL
testButton = LButton
Gosub, mouseToArrows
return
~RButton::
useEdges := useEdgesR
useCenter := useCenterR
timeDelay := timeDelayR
testButton = RButton
Gosub, mouseToArrows
return
mouseToArrows:
;------------------------------
; process mouse movement
;------------------------------
usingCenterMove := 0
if (isFullScreen())
{
; fullscreen
CoordMode, Mouse, Screen
width := A_ScreenWidth
height := A_ScreenHeight
winXpos := 0
winYpos := 0
winXoffset := 0
winYoffset := 0
}
else
{
; only do mouse to key conversion in game window
IfWinNotActive, ahk_id %gameID%
Return,
; get window information
CoordMode, Mouse, Relative
WinGetPos, winXpos, winYpos, winWidth, winHeight, ahk_id %gameID%
; calculate border info from client window
; we do it at every mouse press because the size could change
DllCall("GetClientRect", "UInt", gameID, "UInt", &rect)
width := NumGet(rect, 8, "int")
height := NumGet(rect, 12, "int")
winBorderPixels := (winWidth - width) // 2
winBarPixels := winHeight - height - winBorderPixels
; free up mouse if on top bar
MouseGetPos, , curY
if (curY < winBarPixels)
Return,
winXoffset := winBorderPixels
winYoffset := winBarPixels
; set top left of game window
; keep locking the window so the mouse does not get free
mouseLocked := 1
Gosub, lockWindow
}
; check aspect ratio and adjust
if (width * 9 // height < 16)
{
; top and bottom letterboxed
newHeight := width * 9 // 16
edgeLeft := 0
edgeRight := width
edgeUp := (height - newHeight) // 2
edgeDown := edgeUp + newHeight
}
else
{
; sides letterboxed
newWidth := height * 16 // 9
edgeLeft := (width - newWidth) // 2
edgeRight := edgeLeft + newWidth
edgeUp := 0
edgeDown := height
}
; bottom corner is one pixel in
edgeRight--
edgeDown--
if (useCenter = 1)
{
timePressed := 0
; the valid center movement area
centerLeft := edgeLeft + edgeMargin
centerRight := edgeRight - edgeMargin
centerUp := edgeUp + edgeMargin
centerDown := edgeDown - edgeMargin
}
While GetKeyState(testButton)
{
MouseGetPos, curX, curY
if (!usingCenterMove)
{
; adjust for window border
curX -= winXoffset
curY -= winYoffset
}
; remap mouse to arrow keys
; only press the down key once
if (useEdges = 1 OR usingCenterMove = 1)
{
if (curX < edgeLeft + border)
{
if (!downL)
{
Send {Left Down}
downL := 1
}
}
else
{
if (downL)
{
Send {Left Up}
downL := 0
}
}
if (curX > edgeRight - border)
{
if (!downR)
{
Send {Right Down}
downR := 1
}
}
else
{
if (downR)
{
Send {Right Up}
downR := 0
}
}
if (curY < edgeUp + border)
{
if (!downU)
{
Send {Up Down}
downU := 1
}
}
else
{
if (downU)
{
Send {Up Up}
downU := 0
}
}
if (curY > edgeDown - border)
{
if (!downD)
{
Send {Down Down}
downD := 1
}
}
else
{
if (downD)
{
Send {Down Up}
downD := 0
}
}
}
if (useCenter AND !usingCenterMove)
{
; start counting if in center of screen
if (curX > centerLeft AND curX < centerRight AND curY > centerUp AND curY < centerDown)
{
timePressed++
if (timePressed > timeDelay)
{
; button down long enough, start using center move
; readjust mousepos to real and setup movement box
curX += winXoffset
curY += winYoffset
edgeLeft := curX - movePixels
edgeUp := curY - movePixels
edgeRight := curX + movePixels
edgeDown := curY + movePixels
; fill the RECT structure with UInt values
NumPut(winXpos + edgeLeft , &rect + 0)
NumPut(winYpos + edgeUp , &rect + 4)
NumPut(winXpos + edgeRight + 1, &rect + 8)
NumPut(winYpos + edgeDown + 1, &rect + 12)
DllCall("ClipCursor", UInt, &rect)
usingCenterMove := 1
border := 1
}
}
else
{
; reset count when not in center
timePressed := 0
}
}
Sleep, 10
}
if (usingCenterMove)
{
; free the cursor
if (mouseLocked)
Gosub, lockWindow
else
DllCall("ClipCursor")
; reset to view border
border := borderPixels
}
; unpress any keys that are down
if (downL)
{
Send {Left Up}
downL := 0
}
if (downR)
{
Send {Right Up}
downR := 0
}
if (downU)
{
Send {Up Up}
downU := 0
}
if (downD)
{
Send {Down Up}
downD := 0
}
Return,
isFullScreen()
;------------------------------
; check to see if fullscreen
;------------------------------
{
Global gameID
WinGet, style, Style, ahk_id %gameID%
; 0x800000 is WS_BORDER.
; 0x20000000 is WS_MINIMIZE.
; no border and not minimized
Return, (style & 0x20800000) ? 0 : 1
}
~f::
;------------------------------
; free mouse from window
;------------------------------
if (mouseLocked)
{
IfWinActive, ahk_id %gameID%
{
; free the cursor
DllCall("ClipCursor")
mouseLocked := 0
}
}
Return,
lockWindow:
;------------------------------
; lock mouse to window
;------------------------------
; fill the RECT structure with UInt values
NumPut(winXpos + winBorderPixels, &rect + 0)
NumPut(winYpos + winBarPixels, &rect + 4)
NumPut(winXpos + winBorderPixels + width, &rect + 8)
NumPut(winYpos + winBarPixels + height, &rect + 12)
DllCall("ClipCursor", UInt, &rect)
Return,