📜 ⬆️ ⬇️

Split Screen mode or Split Screen do it yourself

Many of us spent the evening in front of a warm tube TV with friends, playing on consoles. It was always especially pleasant to play at the same time together, not waiting for their turn.


Nostalgia.

Unfortunately, nowadays many game developers do not add such a mode - new splitscreen games can be counted almost on the fingers of one hand. One spring evening, I got the idea to try to get around the limitation imposed by the developers and make the game mode with screen sharing more accessible.

')
I chose World of Tanks as an experimental game rabbit for several reasons:
1. The ability to play together, this mode is called " Platoon ".
2. Playing at minimum settings is not demanding enough - any average PC performance should pull 2 ​​copies.
3. The gameplay of tanks is fairly straightforward, although the developers are positioning themselves as “a massively multiplayer online game in the genre of action with elements of a role-playing game, shooter and strategy” ( Wikipedia ). But as for me - an arcade for pysch-pysch .
4. Probably the most basic reason - I, several hours a week, and my youngest son love to shoot. With the older we sometimes run to Portal2, there is a split screen mode for TV.


These guys hope to know a lot;)

A more detailed study of all the components for tanks together on TV led to the following:
1. Configure the game client to launch two copies
2. It is necessary to divide the TV screen into two virtual ones.
3. Solve the problem of sending keystrokes / deflecting sticks from a gamepad to an inactive window.
4. Send vibration to different gamepads from different clients.

Read more about the decision process.


1. Run 2 clients.
By default, the developers of Wargaming have removed the ability to simultaneously run two copies. I will not describe all the delights of the "sandbox" - Sandboxie to help you.

2. The division of the TV screen into two parts.
“WoT” in windowed mode can have a minimum resolution of 1024x768, in the case of dividing FullHD TV in half, the resolution of each window must be at least 960x1080, and taking into account the window frames and the title and even less. Those. using standard “hot keys” through Snap, splitting the windows in different directions, we get a partial overlap of the windows. Any other utilities to split the desktop into two parts use similar functionality and in no way can affect the minimum resolution of the game in width.
Having tried a huge amount, I came across a Virtual Display Manager , bribed the lack of the word desktop in the title.
The utility did the right thing - by adding the configuration of two virtual displays and moving the window to the right one - the game takes the value we need, namely, it takes exactly half of the screen. By the way, you need to check the separation for a larger amount.

3. Send keystrokes to an inactive window.
This decision was the most difficult for my mind . Two clients are running, the windows are spaced apart and do not overlap each other, but one of the windows is active, respectively, accepts button presses and mouse movements, but the second is not active with all the consequences.


The resolution is 1366x768.

To solve this problem, I was pushed acquaintance with AutoHotkey . Truly, “AutoHotkey is a free open source Windows-based utility and a scripting language with great features, in principle, not even requiring installation.” ( Link )

The first script, allowing even sometimes to go in battle
#InstallKeybdHook
w ::
WinGet, wot, PID, WoT Client
ControlSend ,, {sc11 Down}, ahk_pid% wot%
KeyWait, w
ControlSend ,, {sc11 Up}, ahk_pid% wot%
Return
a ::
WinGet, wot, PID, WoT Client
ControlSend ,, {sc1E Down}, ahk_pid% wot%
KeyWait, a
ControlSend ,, {sc1E Up}, ahk_pid% wot%
Return
s ::
WinGet, wot, PID, WoT Client
ControlSend ,, {sc1F Down}, ahk_pid% wot%
KeyWait, s
ControlSend ,, {sc1F Up}, ahk_pid% wot%
Return
d ::
WinGet, wot, PID, WoT Client
ControlSend ,, {sc20 Down}, ahk_pid% wot%
KeyWait, d
ControlSend ,, {sc20 Up}, ahk_pid% wot%
Return

The reasons why the script worked, I still remain unknown.

After many unsuccessful attempts, the solution was found. Via SendMessage, inform the window that it is active and send keystrokes. Such a kind of deception.
The script sends arrows, WASD and space (reassigned to a shot in the game) in an inactive window.
#SingleInstance
#InstallKeybdHook
SetControlDelay -1
vk49 ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk57 Down}, WoT Client
KeyWait, vk49
ControlSend ,, {vk57 Up}, WoT Client
Return
vk4A ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk41 Down}, WoT Client
KeyWait, vk4A
ControlSend ,, {vk41 Up}, WoT Client
Return
vk4B ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk53 Down}, WoT Client
KeyWait, vk4B
ControlSend ,, {vk53 Up}, WoT Client
Return
vk4C ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk44 Down}, WoT Client
KeyWait, vk4C
ControlSend ,, {vk44 Up}, WoT Client
Return
numpadup ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {up down}, WoT Client
KeyWait, numpadup
ControlSend ,, {up Up}, WoT Client
Return
numpaddown ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {down down}, WoT Client
KeyWait, numpaddown
ControlSend ,, {down Up}, WoT Client
Return
numpadleft ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {left Down}, WoT Client
KeyWait, numpadleft
ControlSend ,, {left Up}, WoT Client
Return
numpadright ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {right Down}, WoT Client
KeyWait, numpadright
ControlSend ,, {right Up}, WoT Client
Return
NumpadEnter ::
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk20 Down}, WoT Client
KeyWait, NumpadEnter
ControlSend ,, {vk20 Up}, WoT Client
Return


Then it went more fun.

I ask you to constructively criticize the code for perfection there is no limit .
World_Of_Tanks_Split_screen
JoyMultiplier = 5
JoyThreshold = 5
JoyThresholdUpper: = 50 + JoyThreshold
JoyThresholdLower: = 50 - JoyThreshold
#Persistent
SetTimer, WatchAxisFirstJoyMoveForwardAndZoom, 10
SetTimer, WatchAxisFirstJoyMoveRotate, 10
SetTimer, WatchAxisFirstJoyCameraRotateVert, 10
SetTimer, WatchAxisFirstJoyCameraRotateHoriz, 10
SetTimer, WatchAxisFirstJoyShoot, 10
SetTimer, WatchFirstJoyPOV, 10
SetTimer, WatchAxisSecondJoyMoveForwardAndZoom, 10
SetTimer, WatchAxisSecondJoyMoveRotate, 10
SetTimer, WatchAxisSecondJoyCameraRotate, 10
SetTimer, WatchAxisSecondJoyShoot, 10
SetTimer, WatchSecondJoyPOV, 10
return

;;;;;;;;;;;; remove windows header

^! + s ::
WinWait, WoT Client
WinSet, Style, -0xC00000
WinWait, [#] WoT Client [#]
WinSet, Style, -0xC00000
return

;;;;;;;;;;;; first gamepad forward / backward in an inactive window and zoom

WatchAxisFirstJoyMoveForwardAndZoom:
GetKeyState, 1JoyY, 1JoyY
GetKeyState, 1JoyZ, 1JoyZ
GetKeyState, 1Joy2, 1Joy2
GetKeyState, 1Joy3, 1Joy3
FirstJoyMoveForwardAndZoomPrev =% FirstJoyMoveForwardAndZoom%

if 1Joy2 = D
GoSub, FirstJoyConsumables
else if 1Joy3 = D
GoSub, FirstJoyConsumables
else
{
if 1JoyZ> 70
{
if 1JoyY <30
FirstJoyMoveForwardAndZoom = PgDn
else if 1JoyY> 70
FirstJoyMoveForwardAndZoom = PgUp
else
FirstJoyMoveForwardAndZoom =
}
else if 1JoyY <30
FirstJoyMoveForwardAndZoom = vk57
else if 1JoyY> 70
FirstJoyMoveForwardAndZoom = vk53
else
FirstJoyMoveForwardAndZoom =
}

if FirstJoyMoveForwardAndZoom =% FirstJoyMoveForwardAndZoomPrev%
return

SetKeyDelay -1
if FirstJoyMoveForwardAndZoom
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyMoveForwardAndZoom% down}, WoT Client
}
}
if FirstJoyMoveForwardAndZoomPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyMoveForwardAndZoomPrev% up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad movement left / right in an inactive window

WatchAxisFirstJoyMoveRotate:
GetKeyState, 1JoyX, 1JoyX
GetKeyState, 1Joy2, 1Joy2
GetKeyState, 1Joy3, 1Joy3
FirstJoyMoveRotatePrev =% FirstJoyMoveRotate%

if 1Joy2 = D
GoSub, SecondJoyConsumables
else if 1Joy3 = D
GoSub, SecondJoyConsumables
else
{
if 1JoyX> 80
FirstJoyMoveRotate = vk44
else if 1JoyX <20
FirstJoyMoveRotate = vk41
else
FirstJoyMoveRotate =
}

if FirstJoyMoveRotate =% FirstJoyMoveRotatePrev%
return

SetKeyDelay -1
if FirstJoyMoveRotate
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyMoveRotate% down}, WoT Client
}
}
if FirstJoyMoveRotatePrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyMoveRotatePrev% up}, WoT Client
}
}
return

;;;;;;;;;;;; the first gamepad menu consumables in an inactive window

FirstJoyConsumables:
FirstJoyConsumablesPrev =% FirstJoyConsumables%

if 1JoyX <20
{
if 1JoyY <20
FirstJoyConsumables = vk38
else if 1JoyY between 40 and 60
FirstJoyConsumables = vk37
else if 1JoyY> 80
FirstJoyConsumables = vk36
else FirstJoyConsumables =
}
else if 1JoyX between 40 and 60
{
if 1JoyY <10
FirstJoyConsumables = vk31
else if 1JoyY> 90
FirstJoyConsumables = vk35
else FirstJoyConsumables =
}
else if 1JoyX> 80
{
if 1JoyY <20
FirstJoyConsumables = vk32
else if 1JoyY between 40 and 60
FirstJoyConsumables = vk33
else if 1JoyY> 80
FirstJoyConsumables = vk34
else FirstJoyConsumables =
}
else FirstJoyConsumables =

if FirstJoyConsumables =% SFirstJoyConsumablesPrev%
return

SetKeyDelay -1
if FirstJoyConsumables
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyConsumables% down}, WoT Client
}
}
if FirstJoyConsumablesPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyConsumablesPrev% up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad review left / right in an inactive window

WatchAxisFirstJoyCameraRotateVert:
GetKeyState, 1JoyU, 1JoyU
GetKeyState, 1Joy5, 1Joy5
FirstJoyCameraRotateVertPrev =% FirstJoyCameraRotateVert%

if 1Joy5 = D
GoSub, FirstJoyCommandMenu
else
{
if 1JoyU> 70
FirstJoyCameraRotateVert = Right
else if 1JoyU <30
FirstJoyCameraRotateVert = Left
else
FirstJoyCameraRotateVert =
}

if FirstJoyCameraRotateVert =% FirstJoyCameraRotateVertPrev%
return

SetKeyDelay -1
if FirstJoyCameraRotateVert
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyCameraRotateVert% down}, WoT Client
}
}
if FirstJoyCameraRotateVertPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyCameraRotateVertPrev% up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad review up / down in an inactive window

WatchAxisFirstJoyCameraRotateHoriz:
GetKeyState, 1JoyR, 1JoyR
GetKeyState, 1Joy5, 1Joy5
FirstJoyCameraRotateHorizPrev =% FirstJoyCameraRotateHoriz%

if 1Joy5 = D
GoSub, FirstJoyCommandMenu
else
{
if 1JoyR> 70
FirstJoyCameraRotateHoriz = Down
else if 1JoyR <30
FirstJoyCameraRotateHoriz = Up
else
FirstJoyCameraRotateHoriz =
}

if FirstJoyCameraRotateHoriz =% FirstJoyCameraRotateHorizPrev%
return

SetKeyDelay -1
if FirstJoyCameraRotateHoriz
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyCameraRotateHoriz% down}, WoT Client
}
}
if FirstJoyCameraRotateHorizPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyCameraRotateHorizPrev% up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad menu orders

FirstJoyCommandMenu:
FirstJoyCommandMenuPrev =% FirstJoyCommandMenu%

if 1JoyU <20
{
if 1JoyR <20
FirstJoyCommandMenu = Numpad8
else if 1JoyR between 40 and 60
FirstJoyCommandMenu = Numpad7
else if 1JoyR> 80
FirstJoyCommandMenu = Numpad6
else FirstJoyCommandMenu =
}
else if 1JoyU between 40 and 60
{
if 1JoyR <10
FirstJoyCommandMenu = vk54
else if 1JoyR> 90
FirstJoyCommandMenu = Numpad5
else FirstJoyCommandMenu =
}
else if 1JoyU> 80
{
if 1JoyR <20
FirstJoyCommandMenu = Numpad2
else if 1JoyR between 40 and 60
FirstJoyCommandMenu = Numpad3
else if 1JoyR> 80
FirstJoyCommandMenu = Numpad4
else FirstJoyCommandMenu =
}
else FirstJoyCommandMenu =

if FirstJoyCommandMenu =% FirstJoyCommandMenuPrev%
return

SetKeyDelay -1
if FirstJoyCommandMenu
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyCommandMenu% down}, WoT Client
}
}
if FirstJoyCommandMenuPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyCommandMenuPrev% up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad shot in an inactive window

WatchAxisFirstJoyShoot:
GetKeyState, 1JoyZ, 1JoyZ
FirstJoyShootPrev =% FirstJoyShoot%

if 1JoyZ <30
FirstJoyShoot = LButton
else
FirstJoyShoot =

if FirstJoyShoot =% FirstJoyShootPrev%
return

SetKeyDelay -1
if FirstJoyShoot
{
IfWinNotActive, WoT Client
{
SendMessage, 0x201 ,,,, WoT Client
}
}
if FirstJoyShootPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x202 ,,,, WoT Client
}
}
return

;;;;;;;;;;;;; first gamepad spider in an inactive window

WatchFirstJoyPOV:
GetKeyState, 1JoyPOV, 1JoyPOV
FirstJoyPOVPrev =% FirstJoyPOV%

if 1JoyPOV = 0
FirstJoyPOV = vk52
else if 1JoyPOV = 18000
FirstJoyPOV = vk46
else if 1JoyPOV = 27000
FirstJoyPOV = vk58
else if 1JoyPOV = 9000
FirstJoyPOV = vk43
else FirstJoyPOV =

if FirstJoyPOV =% FirstJoyPOVPrev%
return

SetKeyDelay -1
if FirstJoyPOV
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyPOV% down}, WoT Client
}
}
if FirstJoyPOVPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {% FirstJoyPOVprev% up}, WoT Client
}
}
return

;;;;;;;;;;;; first LShift gamepad in an inactive window

1Joy10 ::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vkA0 Down}, WoT Client
KeyWait, 1Joy10
ControlSend ,, {vkA0 Up}, WoT Client
}
}
return

;;;;;;;;;;;; Space's first gamepad in an inactive window

1Joy9 ::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk20 Down}, WoT Client
KeyWait, 1Joy9
ControlSend ,, {vk20 Up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad selection of shells in the inactive window

1Joy1 ::
Gosub, FirstSubToggle
Return

FirstSubToggle:
FirstToggle ++
If FirstToggle = 1
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk31 down}, WoT Client
Sleep, 10
ControlSend ,, {vk31 up}, WoT Client
Sleep, 10
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk31 down}, WoT Client
Sleep, 10
ControlSend ,, {vk31 up}, WoT Client
}
}
If FirstToggle = 2
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk32 down}, WoT Client
Sleep, 10
ControlSend ,, {vk32 up}, WoT Client
Sleep, 10
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk32 down}, WoT Client
Sleep, 10
ControlSend ,, {vk32 up}, WoT Client
}
}
If FirstToggle = 3
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk33 down}, WoT Client
Sleep, 10
ControlSend ,, {vk33 up}, WoT Client
Sleep, 10
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk33 down}, WoT Client
Sleep, 10
ControlSend ,, {vk33 up}, WoT Client
}
FirstToggle = 0
}
return

;;;;;;;;;;;; the first gamepad fire extinguisher in an inactive window

1Joy4 ::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk35 Down}, WoT Client
KeyWait, 1Joy4
ControlSend ,, {vk35 Up}, WoT Client
}
}
return

;;;;;;;;;;;; the first gamepad auto-aim in an inactive window

1Joy6 ::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x204, 1 ,,,, WoT Client
KeyWait, 1Joy6
SendMessage, 0x205, 1 ,,, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad menu in an inactive window

1Joy8 ::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk1B Down}, WoT Client
KeyWait, 1Joy8
ControlSend ,, {vk1B Up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad hide mini map in inactive window

1Joy7 ::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk4D Down}, WoT Client
KeyWait, 1Joy7
ControlSend ,, {vk4D Up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad repair in an inactive window

1Joy3 ::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk34 Down}, WoT Client
KeyWait, 1Joy3
ControlSend ,, {vk34 Up}, WoT Client
}
}
return

;;;;;;;;;;;; first gamepad treatment in an inactive window

1Joy2 ::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1 ,,, WoT Client
ControlSend ,, {vk36 Down}, WoT Client
KeyWait, 1Joy2
ControlSend ,, {vk36 Up}, WoT Client
}
}
return

;;;;;;;;;;;; second gamepad

;;;;;;;;;;;; the second gamepad moves forward / backward in the active window and zoom

WatchAxisSecondJoyMoveForwardAndZoom:
GetKeyState, 2JoyY, 2JoyY
GetKeyState, 2JoyZ, 2JoyZ
GetKeyState, 2Joy2, 2Joy2
GetKeyState, 2Joy3, 2Joy3
SecondJoyMoveForwardAndZoomPrev =% SecondJoyMoveForwardAndZoom%

if 2Joy2 = D
GoSub, SecondJoyConsumables
else if 2Joy3 = D
GoSub, SecondJoyConsumables
else
{
if 2JoyZ> 70
{
if 2JoyY <30
SecondJoyMoveForwardAndZoom = PgDn
else if 2JoyY> 70
SecondJoyMoveForwardAndZoom = PgUp
else
SecondJoyMoveForwardAndZoom =
}
else if 2JoyY <30
SecondJoyMoveForwardAndZoom = vk57
else if 2JoyY> 70
SecondJoyMoveForwardAndZoom = vk53
else
SecondJoyMoveForwardAndZoom =
}

if SecondJoyMoveForwardAndZoom =% SecondJoyMoveForwardAndZoomPrev%
return

SetKeyDelay -1
if SecondJoyMoveForwardAndZoom
{
ControlSend ,, {% SecondJoyMoveForwardAndZoom% down}, [#] WoT Client [#]
}
if SecondJoyMoveForwardAndZoomPrev
{
ControlSend ,, {% SecondJoyMoveForwardAndZoomPrev% up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad movement left / right in the active window

WatchAxisSecondJoyMoveRotate:
GetKeyState, 2JoyX, 2JoyX
GetKeyState, 2Joy2, 2Joy2
GetKeyState, 2Joy3, 2Joy3
SecondJoyMoveRotatePrev =% SecondJoyMoveRotate%

if 2Joy2 = D
GoSub, SecondJoyConsumables
else if 2Joy3 = D
GoSub, SecondJoyConsumables
else
{
if 2JoyX> 80
SecondJoyMoveRotate = vk44
else if 2JoyX <20
SecondJoyMoveRotate = vk41
else
SecondJoyMoveRotate =
}

if SecondJoyMoveRotate =% SecondJoyMoveRotatePrev%
return

SetKeyDelay -1
if SecondJoyMoveRotate
{
ControlSend ,, {% SecondJoyMoveRotate% down}, [#] WoT Client [#]
}
if SecondJoyMoveRotatePrev
{
ControlSend ,, {% SecondJoyMoveRotatePrev% up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad menu consumables

SecondJoyConsumables:
SecondJoyConsumablesPrev =% SecondJoyConsumables%

if 2JoyX <20
{
if 2JoyY <20
SecondJoyConsumables = vk38
else if 2JoyY between 40 and 60
SecondJoyConsumables = vk37
else if 2JoyY> 80
SecondJoyConsumables = vk36
else SecondJoyConsumables =
}
else if 2JoyX between 40 and 60
{
if 2JoyY <10
SecondJoyConsumables = vk31
else if 2JoyY> 90
SecondJoyConsumables = vk35
else SecondJoyConsumables =
}
else if 2JoyX> 80
{
if 2JoyY <20
SecondJoyConsumables = vk32
else if 2JoyY between 40 and 60
SecondJoyConsumables = vk33
else if 2JoyY> 80
SecondJoyConsumables = vk34
else SecondJoyConsumables =
}
else SecondJoyConsumables =

if SecondJoyConsumables =% SecondJoyConsumablesPrev%
return

SetKeyDelay -1
if SecondJoyConsumables
{
ControlSend ,, {% SecondJoyConsumables% down}, [#] WoT Client [#]
}
if SecondJoyConsumablesPrev
{
ControlSend ,, {% SecondJoyConsumablesPrev% up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad review and mouse in the active window

WatchAxisSecondJoyCameraRotate:
MouseNeedsToBeMoved: = false
SetFormat, float, 03
GetKeyState, 2JoyU, 2JoyU
GetKeyState, 2JoyR, 2JoyR
GetKeyState, 2Joy5, 2Joy5

if 2Joy5 = D
GoSub, SecondJoyCommandMenu
else if 2Joy5 = U
{
if 2JoyU>% JoyThresholdUpper%
{
MouseNeedsToBeMoved: = true
DeltaU: = 2JoyU - JoyThresholdUpper
}
else if 2JoyU <% JoyThresholdLower%
{
MouseNeedsToBeMoved: = true
DeltaU: = 2JoyU - JoyThresholdLower
}
else
DeltaU = 0
if 2JoyR>% JoyThresholdUpper%
{
MouseNeedsToBeMoved: = true
DeltaR: = 2JoyR - JoyThresholdUpper
}
else if 2JoyR <% JoyThresholdLower%
{
MouseNeedsToBeMoved: = true
DeltaR: = 2JoyR - JoyThresholdLower
}
else
DeltaR = 0
}

SetKeyDelay -1
if MouseNeedsToBeMoved
{
SetMouseDelay, -1; Makes movement smoother
x: = (DeltaU / 30) * (ABS (DeltaU) / 30) * JoyMultiplier
y: = (DeltaR / 30) * (ABS (DeltaR) / 30) * JoyMultiplier
DllCall ("mouse_event", uint, 1, int, x, int, y, uint, 0, int, 0)
}
return

;;;;;;;;;;;; second gamepad menu orders

SecondJoyCommandMenu:
SecondJoyCommandMenuPrev =% SecondJoyCommandMenu%

if 2JoyU <20
{
if 2JoyR <20
SecondJoyCommandMenu = Numpad8
else if 2JoyR between 40 and 60
SecondJoyCommandMenu = Numpad7
else if 2JoyR> 80
SecondJoyCommandMenu = Numpad6
else SecondJoyCommandMenu =
}
else if 2JoyU between 40 and 60
{
if 2JoyR <10
SecondJoyCommandMenu = vk54
else if 2JoyR> 90
SecondJoyCommandMenu = Numpad5
else SecondJoyCommandMenu =
}
else if 2JoyU> 80
{
if 2JoyR <20
SecondJoyCommandMenu = Numpad2
else if 2JoyR between 40 and 60
SecondJoyCommandMenu = Numpad3
else if 2JoyR> 80
SecondJoyCommandMenu = Numpad4
else SecondJoyCommandMenu =
}
else SecondJoyCommandMenu =

if SecondJoyCommandMenu =% SecondJoyCommandMenuPrev%
return

SetKeyDelay -1
if SecondJoyCommandMenu
{
ControlSend ,, {% SecondJoyCommandMenu% down}, [#] WoT Client [#]
}
if SecondJoyCommandMenuPrev
{
ControlSend ,, {% SecondJoyCommandMenuPrev% up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad shot in active window

WatchAxisSecondJoyShoot:
GetKeyState, 2JoyZ, 2JoyZ
SecondJoyShootPrev =% SecondJoyShoot%

if 2JoyZ <30
SecondJoyShoot = LButton
else
SecondJoyShoot =

if SecondJoyShoot =% SecondJoyShootPrev%
return

SetKeyDelay -1
if SecondJoyShoot
{
Send, {% SecondJoyShoot% down}
}
if SecondJoyShootPrev
{
Send, {% SecondJoyShootPrev% up}
}
return

;;;;;;;;;;;;; second gamepad spider in the active window

WatchSecondJoyPOV:
GetKeyState, 2JoyPOV, 2JoyPOV
SecondJoyPOVPrev =% SecondJoyPOV%

if 2JoyPOV = 0
SecondJoyPOV = vk52
else if 2JoyPOV = 18000
SecondJoyPOV = vk46
else if 2JoyPOV = 27000
SecondJoyPOV = vk58
else if 2JoyPOV = 9000
SecondJoyPOV = vk43
else SecondJoyPOV =

if SecondJoyPOV =% SecondJoyPOVPrev%
return

SetKeyDelay -1
if SecondJoyPOV
{
ControlSend ,, {% SecondJoyPOV% down}, [#] WoT Client [#]
}
if SecondJoyPOVPrev
{
ControlSend ,, {% SecondJoyPOVprev% up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second LShift gamepad in the active window

2Joy10 ::
{
ControlSend ,, {vkA0 Down}, [#] WoT Client [#]
KeyWait, 2Joy10
ControlSend ,, {vkA0 Up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; the second gamepad Space in the active window

2Joy9 ::
{
ControlSend ,, {vk20 Down}, [#] WoT Client [#]
KeyWait, 2Joy9
ControlSend ,, {vk20 Up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad selection of shells in the inactive window

2Joy1 ::
Gosub, SecondSubToggle
Return

SecondSubToggle:
SecondToggle ++
If SecondToggle = 1
{
ControlSend ,, {vk31 down}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk31 up}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk31 down}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk31 up}, [#] WoT Client [#]
}
If SecondToggle = 2
{
ControlSend ,, {vk32 down}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk32 up}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk32 down}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk32 up}, [#] WoT Client [#]
}
If SecondToggle = 3
{
ControlSend ,, {vk33 down}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk33 up}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk33 down}, [#] WoT Client [#]
Sleep, 10
ControlSend ,, {vk33 up}, [#] WoT Client [#]
SecondToggle = 0
}
return

;;;;;;;;;;;; second gamepad fire extinguisher in the active window

2Joy4 ::
{
ControlSend ,, {vk35 Down}, [#] WoT Client [#]
KeyWait, 2Joy4
ControlSend ,, {vk35 Up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; the second gamepad avtopritsel in the active window

2Joy6 ::
{
Send, {RButton Down}
KeyWait, 2Joy6
Send, {RButton up}
}
return

;;;;;;;;;;;; second gamepad menu in an inactive window

2Joy8 ::
{
ControlSend ,, {vk1B Down}, [#] WoT Client [#]
KeyWait, 2Joy8
ControlSend ,, {vk1B Up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad hide mini map in active window

2Joy7 ::
{
ControlSend ,, {vk4D Down}, [#] WoT Client [#]
KeyWait, 2Joy7
ControlSend ,, {vk4D Up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad hide mini map in active window

2Joy5 ::
{
ControlSend ,, {vk5A Down}, [#] WoT Client [#]
KeyWait, 2Joy5
ControlSend ,, {vk5A Up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad repair

2Joy3 ::
{
ControlSend ,, {vk34 Down}, [#] WoT Client [#]
KeyWait, 2Joy3
ControlSend ,, {vk34 Up}, [#] WoT Client [#]
}
return

;;;;;;;;;;;; second gamepad treatment

2Joy2 ::
{
ControlSend ,, {vk36 Down}, [#] WoT Client [#]
KeyWait, 2Joy2
ControlSend ,, {vk36 Up}, [#] WoT Client [#]
}
return


Of course, no one was going to play in front of the TV on keyboards / mice. Management of tanks occurs with the help of two gamepads from the Xbox360. The basis was chosen from the management version for the Xbox 360.
Xbox 360 Management Settings
image


In general, I did something like that.
PC control settings

Selecting the type of shells by switching - once pressed - the 1st type, the second - the 2nd, the third - the third and the reset to the beginning (1-2-3). shells are applied immediately - AHK gives a double tap into the game.

Menu orders - a combination of the left bumper and the right stick, treatment and repair of the button "X" and "B" in combination with the left stick.


Video gameplay in split screen mode:

The video was recorded with a draft script - when there was only control of the tank, turret, sniper mode and a space shot.

4. Adjustment of vibrations for gamepads.
Here I already wrote about adding vibrations to the game on a gamepad. Since this modification of the game uses a web service to send vibrations, in order to send to the second gamepad, you just had to change the Flask port.

But, to play in the "Split Screen" on vibrating gamepads, you need to run a full copy of the client in the sandbox (copy the folder next to another name) with its own mod, and also copy it into the sandbox Python27.

Total.


It seems that this solution can be tried to apply to many games. The solution turned out to be very inconvenient - there are a lot of “but”. But I can say that the game for the "fan" was a success. Good luck to everyone in the battles!

I want to express my gratitude to the Gray Forum and separately to the moderator teadrinker, Korean Random and separately inj3ct0r. Thank!!! And also to all those who advised, helped and supported me.

PS Posting mods (menu orders, switching sniper mode and vibro) for the main and sand clients. Also in the archive is a script with a complete removal of the frame around the clients by pressing the WinL + LButton key combination.

Source: https://habr.com/ru/post/227749/


All Articles