Virtual Skipper Wiki
Register
Advertisement



AutoHotkey (ahkscript.org) has a small memory footprint and can be configured to provide additional hotkeys beyond those default and/or customized key bindings that you may have configured within the game client. The script is written in plain ascii and very simply it allows frequently used key stroke (and mouse movement/click) sequences to be sent to the game client in response to the user pressing some key or collection of keys. Also a form of auto-correct (string substitution) is possible using so-called hotstrings.

A description of the scripting language for AutoHotkey is beyond the scope of this article and so the reader is referred to the included help file as well as the AutoHotkey community forum and wiki for technical support.

The scripts suggested in this article are not any form of standard but should be used much like an artist's palette. Leverage the ideas to customize your own game experience.

AHK Versions[]

Autohotkey_L is recommended however, there are also other versions of Autohotkey available. For a brief history of AHK read this.

When VSK is run in fullscreen mode under Windows 7 or Windows 8 the AutoHotkey functions PixelSearch and PixelGetColor might fail to recognize pixel colors. This author does not fully understand why but has read that this has something to do with hardware acceleration being used to send graphics info to the graphics card without involving Windows. One convenient workaround is to always run VSK in Windows Vista compatibility mode.

  1. Right click the shortcut used to launch the game and select "Properties".
  2. At the Compatibility tab, tick the checkbox labeled "Run this program in compatibility mode for:"
  3. Select "Windows Vista" from the selection list.

Basic structure[]

You can copy the following basic structure and then add any or all of the suggested ideas in this article to help automate frequently used key sequences and mouse sequences. The order of hotkey (and hotstring) definitions is not significant. As your script file grows longer and longer you may find that simple alphabetical/numerical ordering is the easiest to scan.

; VSK hot keys
;
<nowiki>#</nowiki>IfWinActive ahk_class GbxApp ; this caters for both VskAC32 and Vsk5Online

SetKeyDelay, 4,4 ; not always necessary

Pause := 40 ; mS delay used in various places
 
^s:: send {Enter} starboard {Enter}
 
; For scripting help see: www.autohotkey.com
; Please read the QUICK-START TUTORIAL near the top of the help file.
; It explains how to perform common automation tasks such as sending
; keystrokes and mouse clicks.  It also explains more about hotkeys.

The #IfWinActive directive ensures that any hotkeys defined in this script will have effect only if the active window belongs to a Virtual Skipper game client.

The SetKeyDelay command is not always required. If you find that your predefined strings of key strokes are not being sent correctly then tweak the delay.

The line with ^s:: is declaring a hotkey Ctrl+S and defining that this send the "STARBOARD" hail to the in-game chat window.

Text between a semicolon (;) and end-of-line is a comment in the script file.

You can copy the above basic structure and then add any or all of the following suggested ideas to help automate frequently used key sequences and mouse sequences. The sequence of hot key (and hotstring) definitions is not important. As your script file grows longer you may find that simple alphabetical/numerical ordering is the easiest to scan.

Hotkey palette[]

In the listing below the modifier keys are represented as

  • ^ Ctrl
  • ! Alt
  • + Shift
  • # Win

So ^s:: defines a key sequence to be sent whenever Ctrl+S is pressed.

^1::Send {Enter} Restart coming {Enter}

^2::Send {Enter} Restart & go at start minus 1.30 {Enter}

^3::Send {Enter} no more restarts {Enter}

^4::Send {Enter} NPC - no pen cancel race {Enter}

^5::Send {Enter} Cancel only wrong pens please {Enter}

^6::Send {Enter} this is the start {Enter}

^8::send {Enter}better to$ff0 spin and fin$g than to $ff0fit and quit {Enter}

^9::send {Enter} when did $ff0overlap$g begin? {Enter}

!a::send {Enter}testing something ... will you help? {Enter}

^b::send {Enter} ty host  ty fleet    $ff0good-bye {Enter}

^c::send {Enter} cheers {Enter}

!c::send {Enter}"getting clear" & "taking a penalty" are $ff0separate$g actions (r44.2) {Enter}

!d::send {Enter} pen. boat may assert ROW while "getting clear" not while taking penalty turns {Enter}

!e::send {Enter}i had $ff0ROW$g so resp. to keep clear was $ff0KC$g boat's .. not mine {Enter} ;'

^f::send {Enter} fair winds .. I'll check back later {Enter} ;'

^g::Send {Enter} good winds {Enter}

!h::send {Enter} not hunting. I was racing and observing my right of way {Enter}

^l::send {Enter} Last race ... $ff0No more races, sorry! {Enter}

^n::Send {Enter} NPC - no pen cancel race {Enter}

;!n (see PC request guard below)

^o::send {Enter} room to tack $ff0RTT$g please {Enter}

^p::send {Enter} $ff0proper course$g please {Enter}

;!p (see PC request guard below)

;^!q (see House Rules below)

^r::send {Enter} room please {Enter}

^s:: send {Enter} starboard {Enter}

^t::send {Enter} tacking {Enter}

!u::send {Enter}host, I saw the "Falling back to $ff0TCP$g (from UDP)" alert when joining {Enter}

^w::send {Enter} $0ffwater$g please ... $ff0I have overlap {Enter}

!w:: ;displays weather bulletin
KeyWait Alt
send {Escape}
Sleep, 100
send {Down}
Sleep, 50
send {Down}
Sleep, 50
send {Enter} 
return

^x::Send {Enter} thanks for racing everyone {Enter}

^z:: send {Enter}please help me receive a penalty .. OK?{Enter}

!z::send {Enter} any $ff0eye-witness$g ... speak please {Enter}

As you see most of the above hotkey definitions are one-liners starting with {Enter} to place the cursor at the chat window input box and then a string of keystrokes for the message followed by {Enter} to submit the message.

For an explanation of the various dollar codes ($ff0, $g, $n, $m) see the article Style codes.

The multi-line definition for Alt+W warrants some explanation. (Use the scroll bar to scroll down if that part of the listing is not visible.) You've already read about the comment running from semicolon (;) to end of line. The 8 statements ahead of the return statement are what get executed whenever Alt+W is pressed. In this case the sequence is automating the keystroke method of accessing the in-game weather bulletin. Since the game does not immediately respond to certain key presses some additional delays (specified in mS) are inserted using the sleep function.

It is not a good idea to be too generous with these extra sleep delays. If the overall sequence takes too long to run then you might find yourself typing or tapping keys that seriously interrupt your predefined key sequence. The KeyWait statement prevents the next line from executing until after the user has released, in this case, the Alt key.

Note that no return statement was necessary for the one-line substitutions.

Send vs SendInput
In later examples you will see SendInput used for lengthy key sequences. Do not be tempted to include the {Enter} keystroke as an argument to the SendInput function. It seems that VSK likes to see about a 20mS delay after the {Enter} keystroke. SendInput issues the keystrokes much too rapidly for the {Enter} keystroke to cause the input cursor to appear at the chat window's input box. For this reason the one-liner hotkey definitions all use the Send function instead of SendInput.

The comment lines for !n, !p and ^!q are simply documentation to remind you that elsewhere in the script you have defined these hotkeys. It is no good having two definitions for the one hotkey.

Hotstring palette[]

The chat input box for VSK truncates messages at 85 characters. Included in this 85 character count are any "dollar-codes" often used to set font color or font style. Therefore the need for hotstrings is somewhat limited. Typically abbreviations such as ROW (right-of-way), KC (keep-clear), RTT (room to tack), etc are preferred over their expanded form. Even TY (thank you) and WD (well done) don't really need expansion via hotstrings.

So as you are inspired by opportunities to use hotstrings for auto-substitution be guided by the "less is more" paradigm.

The syntax for hotstring definitions resembles that for hotkey definitions with the important difference being that a string of characters is used rather than a single character plus optional modifiers for Ctrl, Alt, Shift or Win keys.

The Hotstring EndChars directive is not normally required but is included here because one of the default ending characters (the period or decimal place) is going to be used within many of the hotstrings and so we don't want that character to be interpreted as the end of the hot string and so trigger some substitution.

The basic format of each one-liner is :*:hotstring::targetstring and the only notable thing here is the use of the asterisk. It triggers substitution without waiting for one of the ending characters to be typed. Once again the "dollar-codes" are explained at the article Style codes.

; ISAF rules (hotstrings not hotkeys)
;
#Hotstring EndChars -()[]{}:;'"/\,?!`n `t ; remove period (.) from list of so-called ending characters

:*:i16::$n16.1 $ff0ROW$g boat changing course shall give other boat room to keep clear$m

:*:i17.1::
i171:
sendInput $n17.1 same tack: overlapped lee boat must not sail above $ff0proper course$g$m
return

:*:i18.2a::
i182a:
sendInput $n18.2a outside gives mark room to inside $ff0and$g must keep clear$m
return

:*:i18.2b::
i182b:
sendInput $n18.2b overlap $ff0before$g zone: original outside gives mark room to inside$m
return

:*:i18.2c::
i182c:
sendInput $n18.2c overlap $ff0after$g zone: astern gives mark room to ahead. HTW cancels$m
return

:*:i18.2d::
i182d:
sendInput $n18.2d ROW changing course to round a mark need not give room $ff0rule 16$g exonertated$m
return

:*:i18.3a::
i183a:
sendInput $n18.3a one tacker + fetcher outside: tacker must allow fetcher to fetch mark$m
return

:*:i18.3b::
i183b:
sendInput $n18.3b one tacker + fetcher inside: $ff0no rule 15$g tacker must give mark room to fetcher$m
return

:*:i18.4::
i184:
sendInput $n18.4 inside ROW needs gybe to sail proper course: don't sail too far from mark$m ;'
return

:*:i20.1::
i201:
sendInput $n20.1 Pen 30.1 boat shall keep clear until completely on pre-start side$m
return

:*:i20.2::
i202:
sendInput $n20.2 a boat taking a penalty shall keep clear of one that is not$m
return

:*:i20.3::
i203:
sendInput $n20.3 a boat moving astern shall keep clear of one that is not$m
return

:*:i22.1::
i221:
sendInput $n22.1 $ff0non-racer$g shall not interfere with a $ff0racer$g$m
return

:*:i22.2::
i222:
sendInput $n22.2 unless on proper course, do not interfere with penalty turns or another leg$m
return

Some of the above hotstring definitions are not one-liners. Without getting too much ahead of ourselves, suffice to say that the only reason for these multi-line definitions is to squeeze in a label before the active sendInput function call. Why this was done will be explained in the section titled {#Labels and subroutines}.

Mouse click palette[]

Suppose you want to assign three hotkeys that will cause mouse clicks on certain buttons within the VSK user interface:


  • PgDn to scroll the ISAF window
  • - and + to click the radar zoom out (-) and zoom in (+) buttons ... Note: + shares the same key as =

By trial and error or with the help of a screen shot and an image viewing program you painstakingly figure out the mouse coordinates for these three buttons and then hard code them as follows.

; ISAF window scroll down (PgDn)
;

PgDn::Click 1272, 14 ; scroll down ISAF list

-::Click 310, 983 ; radar zoom out

=::Click 329, 946 ; radar zoom in

Of course it is necessary that these three chosen keys not be bound to any in-game functions otherwise undesirable mayhem could result. Also realize that with AutoHotkey trapping the minus and equals key strokes then those two characters will not be able to be typed into the chat input box. With this in mind you should feel free to choose alternative unused keys for these hotkeys. e.g. F11 and F12 might be convenient and available to you.

It should be no surprise that the Click function simply sends a mouse click at the specified coordinates within the VSK game window.

That is all very well if you routinely use the same display resolution or if you never switch between full-screen mode and windowed mode. Oh and of course if and when you share this script with a friend then the chance that these hardcoded values correspond to your friend's user interface is small. A better solution follows but the above is the basic structure and for many users the above is all that you'll ever need. It will execute only marginally faster than what follows.

Advanced techniques[]

Teaching mouse coordinates[]

Two examples in this section revisit the previous example of assigning specific mouse click actions to certain hotkeys.


  • PgDn to scroll the ISAF window
  • - and + to click the radar zoom out (-) and in (+) buttons ... Note: + shares the same key as =

This time we'll use some advanced scripting to have the user teach the script the correct mouse coordinates as well as provide for the possible need to erase previously learned coordinates in the event that the window was re-sized for whatever reason.

; ISAF window scroll down (PgDn)
;

PgDn:: ; scroll down ISAF list ... takes effect on key release
If IsafScrollX {
 Click %IsafScrollX%, %IsafScrollY%
}else{
 MouseGetPos, IsafScrollX, IsafScrollY
 MsgBox,0,Mouse Coordinates
  ,Assigned`nX: %IsafScrollX%`nY:%IsafScrollY%`nRestoring window ...
  ,2 ; decrease this timeout delay to 0.1 once familiar with process
 WinRestore ahk_class GbxApp
}
return

!PgDn:: ; erase X,Y coords for ISAF scroll button (Alt+PgDn)
IsafScrollX := ""
IsafScrollY := ""
MsgBox,0,Mouse Coordinates,
(
Erased X`,Y coords 
Position mouse cursor over scroll down button
and press PageDn key

Click OK to restore game window.
)
WinRestore ahk_class GbxApp
return

Without going too deeply into the programming, the broad overview is this:


  1. PgDn is defined such as to trigger the sending of a mouse click at a pair of coordinates if those coordinates are not the null string.
  2. If the coordinates have not yet been assigned (i.e they are the null string) then the coordinates of the current location of the mouse cursor are assigned.
  3. On subsequent press of PgDn the coordinates will no longer be the null string and so the effect generated is a mouse click at the coordinates that were just taught to the script.

In the second half of the scrip Alt+PgDn is defined to set the coordinates to the null string. In this programming language the null string also represents Boolean False.

As you can see some simple yet powerful procedures can be written using AutoHotkey script language. A MsgBox was used in this example to communicate to the user what the script was doing. This was not strictly necessary but most users prefer to see some visible feedback as result of some action they have taken.

In one case the MsgBox auto closes (after 2 seconds - not 2 mS) while in the other case it asks for the user to read the instructions and then click the OK button before restoring the game window as the active window.

; radar zoom hotkeys are - and =
; make sure these keys are not bound to in-game functions
; If game window gets resized then erase old coords by pressing - and = at the same time
;
; Note: in chat window you will not be able to enter "-" or "=" characters
;

=:: ; radar zoom in (+) takes effect on key release
If RadarPlusX {
 Click %RadarPlusX%, %RadarPlusY%
}else{
 MouseGetPos, RadarPlusX, RadarPlusY
 MsgBox,0,Mouse Coordinates
  ,Assigned`nX: %RadarPlusX%`nY:%RadarPlusY%`nRestoring window ...
  ,2 ; decrease this timeout delay to 0.1 once familiar with process
 WinRestore ahk_class GbxApp
}
return

-:: ; radar zoom out (-) takes effect on key release
If RadarMinusX {
 Click %RadarMinusX%, %RadarMinusY%
}else{
 MouseGetPos, RadarMinusX, RadarMinusY
 MsgBox,0,Mouse Coordinates
  ,Assigned`nX: %RadarMinusX%`nY:%RadarMinusY%`nRestoring window ...
  ,2 ; decrease this timeout delay to 0.1 once familiar with process
 WinRestore ahk_class GbxApp
}
return

- & =:: ; erase X,Y coords for radar zoom buttons (press - and = at the same time and then release)
RadarPlusX := ""
RadarPlusY := ""
RadarMinusX := ""
RadarMinusY := ""
MsgBox,0,Mouse Coordinates,
(
Erased X`,Y coords for radar buttons
Position mouse cursor over button
and press either - or = key

Click OK to restore game window.
)
WinRestore ahk_class GbxApp
return

In this example the first two hotkey definitions follow the same process as explained for the previous example.

The third hotkey definition adds a subtle new feature but is fundamentally the same as the second part of the previous example in that it sets the coordinates to the null string.

The - & =:: hotkey indicates that the following block of code (down to the return statement) will excute whenever the underscore/minus (-) and the plus/equals (=) keys are pressed at the same time. Nothing new other than this subtle variation.


Trapping key strokes[]

In this example we'll provide a solution for those trigger happy skippers who reflexively press whatever key they have bound to the "Request Penalty Cancel" action. You don't need to be sailing online very long before coming across NPC races where users are kicked for requesting a pen cancel. This example will demonstrate how a key binding for Request Penalty Cancel can be trapped by AutoHotkey and thereby disable your ability to accidentally make such a request in an NPC race.

The virtue of using AutoHotkey for this is that you do not need to quit out to the VSK Settings screen to disable the key binding for that particular race. If you did that you'd most likely return to the race server and find that either the server is full or the race has already started! A better solution is needed.

The script below allows you to enable the square bracket "[" key for a PC allowed race and disable it for an NPC race. Any key could have been chosen but the square bracket is a character that is rarely needed for in-game chat and since we'll be trapping the keystroke we'd would not like to be trapping the letter C which is the default binding for Request Penalty Cancellation.

; PC Request guard (Alt+N and Alt+P)
;
; Guards against accidental penalty cancel request during NPC races
; This requires that "request penalty cancel" be bound to the open square brace key: "[" **
;                    Why?    ... because it is right next to "P" and it looks like a "C"
; NOTE: you cannot send "[" in any chat messages while disabled
; ** any key other than "[" could have been chosen
;

!n:: ; Alt+N to disable Request Penalty Cancel i.e. NPC
NPC := "NPC"
Loop 100
 Tooltip,DISABLED Pen Cancel (%NPC%),150,7
ToolTip
return

!p:: ; Alt+P to enable Request Penalty Cancel i.e. PC
NPC := ""
Loop 100
 Tooltip,ENABLED Pen Cancel (%NPC%PC),150,7
ToolTip
return

$[:: ; edit if "Request Pen Cancel" is bound to a different key than "["
If NPC
    return  ; i.e. do nothing, which causes press of [ key to do nothing
Send {[}
return

Alt+P assigns the null-string to the variable NPC, while Alt+N assigns any non-null string to the NPC variable. Anything other than the null-string is the equivalent of Boolean True in this scripting language and so when the "[" key is pressed and the NPC variable evaluates to true then no keystroke is forwarded to the VSK game client. If NPC variable evaluates to false then the "[" keystroke is forwarded to the game.

Of course this only works if you change the default binding for Request Penalty Cancellation from the C key to the [ key. You could choose any unused key that you don't mind living without in the chat window.

Labels and subroutines[]

Earlier we mentioned the use of labels in the script. If a label is present then it can be used as the target of a GoTo or a GoSub statement. In the example below we'll use this to echo the collection of ISAF rules for RRS 18 and RRS 23 in response to the hotstrings (not hotkeys) i18 and i23 respectively. For the sake of brevity we'll assume that the following script appears in the same file as the previous block using the same comment heading.

::i18:: ; list all rule 18
BlockInput On
Gosub i182a
Gosub Routine1
Gosub i182b
Gosub Routine1
Gosub i182c
Gosub Routine1
Gosub i182d
Gosub Routine1
Gosub i183a
Gosub Routine1
Gosub i183b
Gosub Routine1
Gosub i184
Sleep, %Pause%
send {Enter}
BlockInput off
return

::i22:: ; list all rule 22
BlockInput On
Gosub i221
Gosub Routine1
Gosub i222
Sleep, %Pause%
send {Enter}
BlockInput off
return

Routine1:
Sleep, 20
send {Enter}
Sleep, 20
send {Enter}
return

There is nothing but a string of Gosub calls and the occasional Sleep function. Also note that the asterisk is not used in the hotstring declaration. We would not want either of these two scripts triggered in the middle of typing one of the previously defined hotstrings containing i18 or i23.

User defined functions[]

When the same sequence of actions were to be re-used the GoSub call came in handy above. Sometimes, however, you want the same actions to operate on a different message or string. This calls for a function that accepts one (or more) parameters.

Consider the following function:

Chat(Msg) {
  ; fast and reliable way of sending Msg to chat window
  SetKeyDelay,40,40,0
  BlockInput On
  send {Enter}
  sendInput %Msg%
  send {Enter}
  BlockInput Off
  SetKeyDelay,gDelay,gPress,gPlay 
}

Msg is the parameter and in this case it is passing the string to be used as the chat message. Note how the function takes care of the special handling of the Enter key stroke which is required by the VSK game client.

The pair of BlockInput statements may seem overkill for very short messages but as the length of a message increases the possibility for some keystroke you unwittingly send interfering with the message also increases. Including it within the function is a fail-safe with no negative side-effects.

The final line is designed to restore those three delay values to whatever you had specified outside of the function call. If those three variables are not defined as global variables then comment out that last line but be aware that the function has a lasting side-effect to each delay timing.

With this function defined each of the hotkey definitions can be rewritten as a one line function call:

^1::Chat("Restart coming")

^2::Chat("Restart & go at start minus 1:45")

^3::Chat("no more restarts")

and so on for the remainder of the list at Hotkey palette.

Blocking user input while sending lengthy key sequences[]

In another article we discussed house rules. If you've frequented a particular host and wondered how they generate the identical announcement of so-called house rules every race then this section is for you!

It would be bad if your own key strokes interfered with a sequence of keystrokes hammering out your announcement of house rules. You can't block other users chat but you can increase the chances of your announcement appearing as a block of text by using the SendInput function rather than the Send function. You can also block your own keyboard input while your script is generating this announcement by using the BlockInput statement.

; Sailing Instructions  (Ctrl+Alt+Q)
;
^!q::
KeyWait Control
KeyWait Alt
BlockInput On
 Chat("$s$w{=} {=} {=} Sailing Instructions {=} {=} {=}")
 Chat("do NOT ask for a $ff0restart$g{!} Join servers earlier.")
 Chat("manual protest - auto-umpire's decision is final") ;'
 Chat("$nRule 19$m $ff0respond$g to hail for ROOM at an obstruction")
 Chat("$s$w{=} {=} {=} enjoy{!} {=} {=} {=}") 
BlockInput Off
return

You will recognize the reuse of of the Chat user-defined function and you may recall the function of the KeyWait statement from the second block of code in this article.

Advertisement