DIY Split Screen or DIY Split Screen

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


    Nostalgia.

    Unfortunately, at present, many game developers do not add this mode - new games with splitscreen can be counted almost on the fingers of one hand. One spring evening, the idea came to my mind to try to circumvent the restriction imposed by the developers and make the split-screen game mode more accessible.


    As a guinea pig of the game, I chose “World of Tanks” 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 in performance should pull 2 ​​copies.
    3. The gameplay of the tanks is fairly straightforward, although the developers are positioned as "a massively multiplayer online game in the genre of action with elements of role-playing game, shooter and strategy" ( Wikipedia ). But as for me - an arcade for pimple-pimple .
    4. Probably the most basic reason - I, for several hours a week, and my youngest son love to shoot. With the elder, we sometimes run to Portal2, there is a split-screen mode for TV.


    I hope these guys are familiar to many;)

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

    Read more about the decision process.


    1. Launch of 2 clients.
    By default, developers from Wargaming removed the ability to simultaneously launch two copies. I will not describe all the charms of the "sandbox" - Sandboxie to help you.

    2. Splitting the TV screen into two parts.
    “WoT” in windowed mode can have a minimum resolution of 1024x768, in the case of splitting a FullHD TV in half, the resolution of each window must be at least 960x1080, and given the window frame and title, it is even less. Those. using standard “hot keys” through Snap, when we open the windows in different directions, we get a partial overlap of the windows. Any other utilities for dividing the desktop into two parts use similar functionality and in no way can affect the minimum resolution of the game in width.
    After trying a huge amount, I came across Virtual Display Manager , bribed by the lack of the word desktop in the name.
    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 on the value we need, namely it occupies exactly half the screen. By the way, you need to check the division into a larger number.

    3. Send keystrokes to an inactive window.
    This decision was the most difficult for my mind . Two clients are launched, 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.


    Resolution 1366x768.

    To solve this problem, I was prompted by an acquaintance with AutoHotkey . Truly “AutoHotkey is a free open-source utility for Windows and a scripting language with great features, which in principle does not even require installation.” ( Link )

    The first script that even allows you to sometimes ride 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 were still unknown to me.

    After many unsuccessful attempts, a solution was found. Via SendMessage tell the window that it is active and send keystrokes. Such a peculiar deception.
    The script sends arrows, WASD and a space (reassigned to a shot in the game) to 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 the window title

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

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; first gamepad move 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
    PgDn = FirstJoyMoveForwardAndZoom
    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 move left / right in a sleep 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, 0x06T1, ,, First, Client
    ,% xTT , 1, Send, Client1, Jot% , Start, End, % Client1, Jot WoT Client
    }
    }
    if FirstJoyMoveRotatePrev
    {
    IfWinNotActive, WoT Client
    {
    SendMessage, 0x06, 1 ,,, WoT Client
    ControlSend ,, {% FirstJoyMoveRotatePrev% up}, WoT Client
    }
    }
    return

    ;;;;;;;;;;;;;;;; first gamepad consumables menu 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
    = Vk32 FirstJoyConsumables
    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 a sleep 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
    FirstJoyCameraRotateVert if
    {
    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 up / down review in the inactive

    WatchAxisFirstJoyCameraRotateHoriz window :
    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 commands

    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

    ;;;;;;;;;;;;;;;;;;;;;;;;;;; gamepad first 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 cross 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

    ;;;;;;;;;;;;;;;; the first Space gamepad in the 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 shell selection in the inactive window

    1Joy1 ::
    Gosub, FirstSubToggle
    Return

    FirstSubToggle:
    FirstToggle ++
    If FirstToggle = 1
    {
    IfWinNotActive, WoT Client
    {
    SendMessage, 0x06, 1 ,,, WoT Client
    ControlSend ,, {vk31 down}
    , 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

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

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

    ;;;;;;;;;;;;;;;; first gamepad auto-

    sight in the inactive window 1Joy6 ::
    {
    IfWinNotActive, WoT Client
    {
    SendMessage, 0x204, 1 ,,, WoT Client
    KeyWait, 1Joy6
    SendMessage, 0x205, 1 ,,, WoT Client
    }
    }
    return

    ;;;;;;;;;;;;;;;;;;; ;;; first gamepad menu in the 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

    ;;;;;;;;;;;;;;; The 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

    ;;;;;;;;;;;;;;;; second gamepad move 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 move 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 ,, {% SecondJoyMvRotate} [#]
    }
    return

    ;;;;;;;;;;;;;; second gamepad of the consumables menu

    SecondJoyConsumables:
    SecondJoyConsumablesPrev =% SecondJoyConsumables%

    if 2JoyX <20
    {
    if 2JoyY <20
    SecondJoyConsumables = vk38
    else if 2JoyY between 40 and 60
    SecondJoyConsumables = vk37
    else if 2JoyY> 80
    SecondJoyC36umables
    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 2 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 into 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
    }
    The else the if 2JoyU <% JoyThresholdLower%
    {
    MouseNeedsToBeMoved: = to true
    DeltaU: = 2JoyU - JoyThresholdLower
    }
    the else
    DeltaU = 0
    the if 2JoyR>% JoyThresholdUpper%
    {
    MouseNeedsToBeMoved: = to true
    DeltaR: = 2JoyR - JoyThresholdUpper
    }
    the else the 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 commands

    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

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gamepad second shot in the 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 cross 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

    ;;; ;;;;;;;;;;; second Space controller in the active window

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

    ;;; ;;;;;;;;;;; second gamepad selects 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

    ;;; ;;;;;;;;;;; second gamepad auto-

    sight in the active window 2Joy6 ::
    {
    Send, {RButton Down}
    KeyWait, 2Joy6
    Send, {RButton up}
    }
    return

    ;;;;;;;;;;;;;;;; second menu controller in the inactive window

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

    ;;; ;;;;;;;;;;; second gamepad hide the minimap in the active window

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

    ; ;;;;;;;;;;;;;; the second gamepad hide the mini-map in the 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 Down}, [#] WoT Client [#]
    }
    return

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

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


    Of course, no one was going to play on TV on keyboards / mice. Tank control is carried out using two gamepads from the Xbox360. The basis was chosen control from the version for the Xbox 360.
    Xbox 360 Management Settings
    image


    In general, I did it somehow.
    PC Management Settings

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

    The orders menu is a combination of the left bumper and the right stick, treatment and repair of the “X” and “B” buttons 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 bar shot.

    4. Configuring vibrations for gamepads.
    Here I already wrote about adding vibrations to the game on the gamepad. Since this modification of the game uses a web service to send vibrations, to send it 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 in the "sandbox" a full copy of the client (copy the folder next to a different name) with its own mod, also copy Python27 to the "sandbox".

    Total.


    It seems that this solution can be tried to apply to many games. The solution turned out to be very uncomfortable - a lot of all sorts of “buts.” 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 teadrinker moderator, Korean Random and separately inj3ct0r. Thanks!!! And also to all those who advised, helped and supported me.

    PS I spread the mods (menu of 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 frames around the clients by pressing the WinL + LButton key combination.

    Also popular now: