Jump to content
Korean Random
kharlashkin

[Заказ] Разделенный экран или Split screen

Recommended Posts

Доброе время суток, уважаемые форумчане!

 

"С восторгом предаюсь в руки родной милиции форума, надеюсь на нее него и уповаю." - Иван Васильевич Бунша ©

 

В прошлом году мною было опубликована "хотелка", которая блестяще была решена с помощью inj3ct0rsirmax и другими. Должен сказать, что данный мод был довольно холодно воспринят интернет-сообществом (публикация на habrahabr хоть и набрала более 10k просмотров, но в минусе), после обновления 0.8.11 и обновления системного python в игре, ко мне достаточно много человек постучалось с просьбой поделиться обновленным модом. С этой целью я написал на официальном форуме подробную инструкцию.

 

Таким образом хочу снова выразить огромную благодарность всем, кто участвовал в создании.

 

Теперь перейдем к новым "хотелкам".

На официальном форуме WoT в теме "корпоративная игра на 1м танке" меня посетила мысль об игровом режиме "Разделенный экран", т.е. одновременной игре вдвоем на одном экране во взводе/роте/команде. Разумеется ни о каком "нагибе", "подставных ротах" и прочем речь не идет, только "игра для фана" и "пыщь-пыщь" под пивасик с другом на геймпадах.

 

Теория была мною опробована на виртуальных ПК под VMWare Player:

post-19155-0-04642900-1394541352_thumb.jpg

Все заработало - геймпады пробросились в ВМ, картинку настроил в портретный режим, вибрации работают. Но неиграбельно - fps в среднем в каждой ВМ от 8 до 15.

 

Более детальное изучение всех составляющих для танков вдвоем на ТВ привело к следующему:
1. Настроить клиент игры для возможного запуска двух копий
2. Необходимо разделить экран телевизора на два виртуальных.
3. Решить проблему отправки нажатий кнопок/отклонения стиков с геймпада в неактивное окно.
4. Отправить вибрацию в разные геймпады с разных клиентов.

 

1. Запуск 2-х клиентов.
По-умолчанию, разработчики из Wargaming убрали возможность одновременного запуска двух копий. Не буду описывать все прелести «песочницы» — Sandboxie Вам в помощь.

 

2. Разделение экрана телевизора на две части.
«WoT» в оконном режиме может иметь минимальное разрешение 1024х768, в случае разделения FullHD телевизора пополам, необходимо разрешение каждого окна минимум 960х1080, а учитывая рамки окна и заголовок и того меньше. Т.е. стандартными «горячими клавишами» через Snap разнеся окна в разные стороны мы получаем частичное перекрытие окон. Любые другие утилиты для разделения рабочего стола на две части используют похожий функционал и никаким образом не могут повлиять на минимальное разрешения игры по ширине.
Перепробовав огромное количество, натолкнулся на Virtual Display Manager, подкупило отсутствие в названии слова desktop.
Утилита сделала нужное — добавив конфигурацию двух виртуальных дисплеев и перемещая окно в нужный — игра принимает нужное нам значение, а именно занимает ровно половину экрана.

 

3. Отправка нажатий клавиш в неактивное окно.
Это решение было для моего ума самым сложным. Два клиента запущены, окна разнесены в стороны и не перекрывают друг-дружку, но одно из окон активно, соответственно принимает нажатия кнопок и перемещения мышки, а вот второе не активно со всеми вытекающими.

post-19155-0-45038800-1404212378_thumb.jpg

К решению этой проблемы меня подтолкнуло знакомство с AutoHotkey.

'Первый скрипт, позволяющий даже иногда ездить в бою'

#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 

 

Причины, почему скрипт срабатывал, мне так и остались неизвестны.

После многих безуспешных попыток, решение нашлось. Через SendMessage сообщать окну, что оно активно и отправлять нажатия клавиш. Такой своеобразный обман.

'Скрипт отправляет стрелки, WASD и пробел (переназначеный на выстрел в игре) в неактивное окно'

#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 

 

Дальше пошло веселее, особенно хочу выразить благодарность Серому форуму, и отдельно модератору teadrinker. Спасибо!!!

 

'WoT Split Screen'

#Persistent
JoyMultiplier = 5
JoyThreshold = 5
JoyThresholdUpper := 50 + JoyThreshold
JoyThresholdLower := 50 - JoyThreshold
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

;;;;;;;;;;;;SplitScreen

LWIN & LButton::
{
WinWait, WoT Client
WinSet, Style, -0xC40000
WinMove, WoT Client,,960 ,0 ,960 ,1080
WinWait, [#] WoT Client [#]
WinSet, Style, -0xC40000
WinMove, [#] WoT Client [#],,0 ,0 , 960 ,1080
}
return

;;;;;;;;;;;;первый геймпад движение вперед/назад в неактивном окне и зум

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

;;;;;;;;;;;;первый геймпад движение влево/вправо в неактивном окне

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

if 1Joy2 = D
    GoSub, FirstJoyConsumables
else if 1Joy3 = D
    GoSub, FirstJoyConsumables
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

;;;;;;;;;;;;первый геймпад меню расходников в неактивном окне

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

;;;;;;;;;;;;первый геймпад обзор влево/вправо в неактивном окне

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

;;;;;;;;;;;;первый геймпад обзор вверх/вниз в неактивном окне

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

;;;;;;;;;;;;первый геймпад меню приказов

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

;;;;;;;;;;;;первый геймпад выстрел в неактивном окне

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

;;;;;;;;;;;;;первый геймпад крестовина в неактивном окне

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

;;;;;;;;;;;;первый геймпад LShift в неактивном окне

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

;;;;;;;;;;;;первый геймпад Space в неактивном окне

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

;;;;;;;;;;;;первый геймпад выбор снарядов в неактивном окне

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

;;;;;;;;;;;;первый геймпад огнетушитель в неактивном окне

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

;;;;;;;;;;;;первый геймпад автоприцел в неактивном окне

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

;;;;;;;;;;;;первый геймпад меню в неактивном окне

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

;;;;;;;;;;;;первый геймпад скрыть мини карту в неактивном окне

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

;;;;;;;;;;;;первый геймпад ремонт в неактивном окне

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

;;;;;;;;;;;;первый геймпад лечение в неактивном окне

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

;;;;;;;;;;;;второй геймпад

;;;;;;;;;;;;второй геймпад движение вперед/назад в активном окне и зум

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

;;;;;;;;;;;;второй геймпад движение влево/вправо в активном окне

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

;;;;;;;;;;;;второй геймпад меню расходников

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

;;;;;;;;;;;;второй геймпад обзор и мышь в активном окне

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

;;;;;;;;;;;;второй геймпад меню приказов

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

;;;;;;;;;;;;второй геймпад выстрел в активном окне

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

;;;;;;;;;;;;;второй геймпад крестовина в активном окне

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

;;;;;;;;;;;;второй геймпад LShift в активном окне

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

;;;;;;;;;;;;второй геймпад Space в активном окне

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

;;;;;;;;;;;;второй геймпад выбор снарядов в неактивном окне

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

;;;;;;;;;;;;второй геймпад огнетушитель в активном окне

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

;;;;;;;;;;;;второй геймпад автоприцел в активном окне

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

;;;;;;;;;;;;второй геймпад меню в неактивном окне

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

;;;;;;;;;;;;второй геймпад скрыть мини карту в активном окне

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

;;;;;;;;;;;;второй геймпад скрыть мини карту в активном окне

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

;;;;;;;;;;;;второй геймпад ремонт

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

;;;;;;;;;;;;второй геймпад лечение

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

 

Разумеется, играть перед ТВ никто не собирался на клавиатурах/мышах. Управление танками происходит с помощью двух геймпадов от Xbox360. За основу было выбрано управление от версии для Xbox 360.

post-19155-0-12776700-1404213248_thumb.jpg

 

В общем, у меня получилось как-то так.

post-19155-0-74697300-1404213570_thumb.jpg

Выбор типа снарядов переключением — один раз нажал — 1-й тип, второй — 2-й, третий — третий и сброс в начало (1-2-3). снаряды применяются сразу — AHK отдает двукратное нажатие в игру.

post-19155-0-86123700-1404213600_thumb.jpg

Меню приказов — сочетание левого бампера и правого стика, лечение и ремонт кнопки «Х» и «В» в сочетании с левым стиком.

 

Видео геймплея

 

4. Настройка вибраций для геймпадов.
Так как данная модификация игры использует веб-сервис для отправки вибраций, то для отправки во второй геймпад, нужно было просто изменить порт Flask.

Но, для игры в «Разделенном экране» на вибрирующих геймпадах нужно запускать в «песочнице» полную копию клиента (скопировать папку рядом с другим именем) со своим отдельным модом, также скопировать в «песочницу» Python27.

 

Хочу отдельно выразить особую огромнейшую благодарность inj3ct0r, за всесторонние консультации и помощь, а так же всем участвовавшим! Спасибо Вам всем, без Вашей помощи, подсказок и поддержки у меня ничего не получилось бы.

 

Думается, что данное решение можно попробовать применить ко многим играм. Решение получилось очень неудобным — много всяких «но». Но могу сказать что игра для «фана» удалась. Удачи всем в боях!

 

Во вложении моды для основного/"песочного" клиентов и скрипт.

WoTSplitScreen.7z

Edited by kharlashkin
  • Upvote 8
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

1. Упаковка всей игры WoT как "тонкое приложение" с помощью VMWare ThinApp или Citrix XenApp, соответственно можно запустить два клиента WoT изолированных друг от друга со своими настройками и модами. Интересно как скажется на производительность игры ;)

Портативный клиент пробовали? Я, правда, не знаю, можно ли запустить 2 клиента одновременно.

Share this post


Link to post

Short link
Share on other sites

Портативный клиент пробовали? Я, правда, не знаю, можно ли запустить 2 клиента одновременно.

Спасибо за ссылку! Гуглил на днях портативный клиент - на эту тему не попадал. Протестирую - отпишусь.

  • Upvote 1
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

 

 

только "игра для фана" и "пыщь-пыщь" под пивасик с другом на геймпадах
сорри за оффтоп, но представил себе это действо... чертовски весело же :-)) 
  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

сорри за оффтоп, но представил себе это действо... чертовски весело же :-)) 

Кстати я давно в гости к знакомому хожу с ноутбуком, проводным геймпадом и HDMI-кабелечком. Причем я играю на его ТВ в гостиной, он же скрючивается за ПК в соседней комнате. Уже начинает привыкать играть на контроллере - начинаем меняться местами. Для таких "покатушек" и держу танчики 1-2 уровня.

  • Upvote 1
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites
Должен сказать, что данный мод был довольно холодно воспринят интернет-сообществом (публикация на habrahabr хоть и набрала более 10k просмотров, но в минусе)

Статья на хабре получила бы более теплый приём, если бы в ней было больше технической информации, например описание проблем, с которыми пришлось столкнуться при разработке, и их решения. А в статье ни слова об этом, и ссылки на тему с "хотелкой", где можно посмотреть процесс борьбы за работоспособность там тоже не нашлось. + даже ссылка на XVM устаревшая(только гугл.код, а среднестатистическому хабраюзеру XVM может быть и неизвестен). Так что проблема не в моде, а в подаче информации. Если исправить эти недочёты в будущем, всё будет хорошо =)

 

Удачи в работе над сплит-скрином, подпишусь на тему, буду поглядывать =)

Edited by Krab Age
  • Upvote 3

Share this post


Link to post

Short link
Share on other sites

Статья на хабре получила бы более теплый приём, если бы в ней было больше технической информации, например описание проблем, с которыми пришлось столкнуться при разработке, и их решения. А в статье ни слова об этом, и ссылки на тему с "хотелкой", где можно посмотреть процесс борьбы за работоспособность там тоже не нашлось. + даже ссылка на XVM устаревшая(только гугл.код, а среднестатистическому хабраюзеру XVM может быть и неизвестен). Так что проблема не в моде, а в подаче информации. Если исправить эти недочёты в будущем, всё будет хорошо =)

 

Удачи в работе над сплит-скрином, подпишусь на тему, буду поглядывать =)

"И опыт, сын ошибок трудных" - А.С.Пушкин ©

 

Как бы пост писался "сгоряча" - согласен, что моя ошибка. К тому же, уже потом, было выяснено очень много деталей, которые очень подогрели бы интерес - но запал прошел у меня. Думаю, что будет версия 2 :)

  • Upvote 1
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

Портативный клиент пробовали? Я, правда, не знаю, можно ли запустить 2 клиента одновременно.

Скачал, распаковал, попробовал - фигня :( fps при худших текстурах тот же, два клиента не запускается (я глубоко не копал, жду ответа от разаботчика), хотя использует функционал Sandboxie.

 

Пробовал в виртуальной машине создать свою портативную версию танков с помощью VMWare ThinApp. На голой Windows 7 все сделал, получился монстр в 12,5 ГБ. Я правда выбрал виртуализировать все приложения, что нашла утилита создания. Надо протестировать и потом уже пробовать сокращать размер "портативных танков thinapp".

 

Тестировал эмулятор FreePIE. От неё осталось какое-то двойственное впечатление.

 

1. За пару часов почти все настроил, кстати делюсь своим "черновым" скриптом:

'xboxtest1.py'

def update():
      
   #LMouseClick  Attack with weapon or cast spell in right hand (primary hand maps to primary mouse)
   mouse.leftButton = xbox360[0].rightTrigger
   #RMouseClick  Attack with weapon or cast spell in left hand / Block if available
   mouse.rightButton = xbox360[0].rightShoulder
   
   #E      Activate/use/hold to manipulate objects
   keyboard.setKey(Key.Q, xbox360[0].x)
   
   #Alt    Sprint
   keyboard.setKey(Key.LeftShift, xbox360[0].rightThumb)
   
   #Shift  Walk (slower and quieter than default movement)
   keyboard.setKey(Key.R, xbox360[0].b)
   
   #Ctrl   Crouch/sneak mode
   keyboard.setKey(Key.LeftControl, xbox360[0].leftThumb)
   
   #Space  Jump
   keyboard.setKey(Key.F, xbox360[0].a)
   
   #F      Change views (1st and 3rd person)
   keyboard.setKey(Key.Z, xbox360[0].y)
   
   #Tab    Character Menu
   keyboard.setKey(Key.Return, xbox360[0].start)
   
   #Esc    Menu
   keyboard.setKey(Key.Escape, xbox360[0].back)
      
   #P      Magic menu
   keyboard.setKey(Key.W, xbox360[0].up)
   
   #M      Map   
   keyboard.setKey(Key.A, xbox360[0].left)
   
   #I      inventory
   keyboard.setKey(Key.S, xbox360[0].down)
   
   #T      Wait      
   keyboard.setKey(Key.D, xbox360[0].right)
   
   #Q      Favorites - not supported
   #C      Toggle Automove / Zoom item in inventory - not supported
   #/      Perk menu - not supported
   #Z      Racial power/Dragon shout - not supported
   #Mwheel  Scroll in menus or zoom while in 3rd person - not supported
   #1-8    Hotkeys (no numpad) - Not supported
   #`      Open/close the console (~ key) - not supported
   
if (xbox360[0].leftStickX) > 0.25:
   mouse.deltaX = (xbox360[0].leftStickX - 0.25) * 0.5

if (xbox360[0].leftStickX) < -0.25:
   mouse.deltaX = (xbox360[0].leftStickX + 0.25) * 0.5

if (xbox360[0].leftStickY) > 0.25:
   mouse.deltaY = -(xbox360[0].leftStickY - 0.25) * 0.5
   
if (xbox360[0].leftStickY) < -0.25:
   mouse.deltaY = -(xbox360[0].leftStickY + 0.25) * 0.5
   
   #MouseLook
if (xbox360[0].rightStickX) > 0.25:
   mouse.deltaX = (xbox360[0].rightStickX - 0.25) * 2

if (xbox360[0].rightStickX) < -0.25:
   mouse.deltaX = (xbox360[0].rightStickX + 0.25) * 2

if (xbox360[0].rightStickY) > 0.25:
   mouse.deltaY = -(xbox360[0].rightStickY - 0.25) * 2
   
if (xbox360[0].rightStickY) < -0.25:
   mouse.deltaY = -(xbox360[0].rightStickY + 0.25) * 2
   
if starting:
    freeTrack.update += update 

 

Прошу прощения сразу код делался простым копипастом, из обрывков кода на форумах, необходимо добавить комбинации клавиш (лечение. ремонт, выбор снарядов), поменять алгоритм работы стиков (более правильное поведение мыши), прокомментировать.

 

2. Так же можно явно указывать для какого геймпада какие команды (в квадратных скобках номер геймпада).

 

3. Проект бесплатен и довольно активно развивается.

 

4. Программа при запуске отъедает более 50 МБ, что не есть хорошо (тот же Xpadder кушает в 10 раз меньше).

 

5. Пока не понял каким образом сделать автоматический запуск, есть ссылки на консольную версию эмулятора, что может пригодиться.

 

Пока все выложил, что есть нового у меня.

Пробовал запустить "монстра" thinapp, две копии приложения не захотели запускаться. Возможно надо сделать отдельное "тонкое приложение" под другим именем. Из плюсов - прорисовка картинки никак не отличалась от локально запущенного клиента.

 

Пробовал запустить один клиент как обычно, второй в "песочнице", могу сказать что можно играть на моем ноутбуке (A4-4300M, 4 ГБ ОЗУ 1600 Mhz). Настройки в минимум, клиенты запущены в оконном режиме с разрешением 1024х768, обычный клиент 15-25 fps, "песочный" 10-20 fps. Думаю что на домашнем A10-5700, 8 ГБ ОЗУ 1866 Mhz с SSD и более быстрым WD Black будет веселее.

 

Таким образом, могу сказать, что проблема запуска двух клиентов для одновременной игры решена. Я как бы и сразу полагался на этот способ ;) В принципе и цена не такая пугающая - 15 евро, два месяца не донатить Wargaming.

Edited by kharlashkin
  • Upvote 1
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

Нашел несколько интересных проектов, которые могут позволить подключать геймпады напрямую в WoT. Разумеется требуется "доработка напильником" но направления интересные.

1. ioHub.

2. python_xbox_controller.

3. Using XInput to access an Xbox 360 Controller in Managed Code.

4. Xbox 360 Controller Input in C++ with XInput.

 

Начинаю склоняться в сторону написанию своего эмулятора на Python, для вызова непосредственно в WoT способом который использовался для "мода вибраций".

  • Upvote 2
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

Вчера вечером пробовал pygame, тестовый скрипт из раздела документации без проблем определил геймпад, захватывал значения стиков/тригеров нажатия кнопок.

Если я правильно понял - pygame использует библиотеку SDL, которая, в свою очередь, имеет реализацию для Python. К тому же с версии 2.0 поддерживается так же вибрационная связь с геймпадами. Направление наверное уже точно определено ;) Можно с помощью XPM заменить нужный метод и игра будет принимать значения от геймпада как от клавиатуры/мыши.

 

Просьба к форумчанам - кто нибудь подскажите, где лежат скрипты для клавиатуры/мыши в WoT?

 

P.S. Тестовый код для проверки работы pygame с геймпадами:

'testpygame.py'

# Sample Python/Pygame Programs
# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/
 
import pygame
 
# Define some colors
BLACK    = (   0,   0,   0)
WHITE    = ( 255, 255, 255)
 
# This is a simple class that will help us print to the screen
# It has nothing to do with the joysticks, just outputing the
# information.
class TextPrint:
    def __init__(self):
        self.reset()
        self.font = pygame.font.Font(None, 20)
 
    def Print(self, screen, textString):
        textBitmap = self.font.render(textString, True, BLACK)
        screen.blit(textBitmap, [self.x, self.y])
        self.y += self.line_height
         
    def reset(self):
        self.x = 10
        self.y = 10
        self.line_height = 15
         
    def indent(self):
        self.x += 10
         
    def unindent(self):
        self.x -= 10
     
 
pygame.init()
  
# Set the width and height of the screen [width,height]
size = [500, 700]
screen = pygame.display.set_mode(size)
 
pygame.display.set_caption("My Game")
 
#Loop until the user clicks the close button.
done = False
 
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
 
# Initialize the joysticks
pygame.joystick.init()
     
# Get ready to print
textPrint = TextPrint()
 
# -------- Main Program Loop -----------
while done==False:
    # EVENT PROCESSING STEP
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done=True # Flag that we are done so we exit this loop
         
        # Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION
        if event.type == pygame.JOYBUTTONDOWN:
            print("Joystick button pressed.")
        if event.type == pygame.JOYBUTTONUP:
            print("Joystick button released.")
             
  
    # DRAWING STEP
    # First, clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.
    screen.fill(WHITE)
    textPrint.reset()
 
    # Get count of joysticks
    joystick_count = pygame.joystick.get_count()
 
    textPrint.Print(screen, "Number of joysticks: {}".format(joystick_count) )
    textPrint.indent()
     
    # For each joystick:
    for i in range(joystick_count):
        joystick = pygame.joystick.Joystick(i)
        joystick.init()
     
        textPrint.Print(screen, "Joystick {}".format(i) )
        textPrint.indent()
     
        # Get the name from the OS for the controller/joystick
        name = joystick.get_name()
        textPrint.Print(screen, "Joystick name: {}".format(name) )
         
        # Usually axis run in pairs, up/down for one, and left/right for
        # the other.
        axes = joystick.get_numaxes()
        textPrint.Print(screen, "Number of axes: {}".format(axes) )
        textPrint.indent()
         
        for i in range( axes ):
            axis = joystick.get_axis( i )
            textPrint.Print(screen, "Axis {} value: {:>6.3f}".format(i, axis) )
        textPrint.unindent()
             
        buttons = joystick.get_numbuttons()
        textPrint.Print(screen, "Number of buttons: {}".format(buttons) )
        textPrint.indent()
 
        for i in range( buttons ):
            button = joystick.get_button( i )
            textPrint.Print(screen, "Button {:>2} value: {}".format(i,button) )
        textPrint.unindent()
             
        # Hat switch. All or nothing for direction, not like joysticks.
        # Value comes back in an array.
        hats = joystick.get_numhats()
        textPrint.Print(screen, "Number of hats: {}".format(hats) )
        textPrint.indent()
 
        for i in range( hats ):
            hat = joystick.get_hat( i )
            textPrint.Print(screen, "Hat {} value: {}".format(i, str(hat)) )
        textPrint.unindent()
         
        textPrint.unindent()
 
     
    # ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
     
    # Go ahead and update the screen with what we've drawn.
    pygame.display.flip()
 
    # Limit to 20 frames per second
    clock.tick(20)
     
# Close the window and quit.
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit () 

Edited by kharlashkin
  • Upvote 2
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

Играть на геймпаде в шуторо-подобные игры гейство. Любой более менее адекватный игрок на клаве и мыше вывертить гейпада в 0.

Хотите играть лежа на диване, купите X-box и не страдайте фигней.

  • Downvote 4

Share this post


Link to post

Short link
Share on other sites

Играть на геймпаде в шуторо-подобные игры гейство. Любой более менее адекватный игрок на клаве и мыше вывертить гейпада в 0.

Хотите играть лежа на диване, купите X-box и не страдайте фигней.

На официальном форуме есть мой гайд с игрой в танки на геймпаде. Там же выложены мои мысли относительно управления в WoT в сравнении с шутерами. "Стата" - лучшее доказательство возможности так играть. Я не "цифродрочер" и играю без премиума, так что мои показатели ничем не лучше и не хуже других игроков. А Вы из тех, кто считает всех игроков "ракообразными", играющих для "пыщь-пыщь"?
  • Upvote 2
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

"Стата" - лучшее доказательство возможности так играть. Я не "цифродрочер" и играю без премиума, так что мои показатели ничем не лучше и не хуже других игроков. А Вы из тех, кто считает всех игроков "ракообразными", играющих для "пыщь-пыщь"?

Стата как раз доказывает, что так играть нельзя! Средний урон 490 это 2-3 пробития за бой, 49% побед говорит о дичайшем везении, тащила команда.  

Я из тех, кто ненавидит сливающихся аутистов в первые 3 мин боя или стоящих/катающихся черти где.

У гейпадов есть своя резервация, ползайте там раз Вам так это нравиться. Смешивать консоль и PC это идиотизм.

8dw5.jpg6e1d.jpg

Edited by inq
  • Downvote 4

Share this post


Link to post

Short link
Share on other sites

Стата как раз доказывает, что так играть нельзя! Средний урон 490 это 2-3 пробития за бой, 49% побед говорит о дичайшем везении, тащила команда.  

Я из тех, кто ненавидит сливающихся аутистов в первые 3 мин боя или стоящих/катающихся черти где.

У гейпадов есть своя резервация, ползайте там раз Вам так это нравиться. Смешивать консоль и PC это идиотизм.

8dw5.jpg6e1d.jpg

Между прочем мой первый "воин" (как и все остальные) был получен именно на геймпаде, до сих пор помню дрожь в руках.

"Скриншот результатов"

post-19155-0-21462200-1394870821_thumb.jpg

Все танки 6 лвл и выше игрались только на гемйпаде.

49% из более 10k боев "тащила команда" - Вы хоть понимаете о чем говорите?

В общем прошу Вас не флудить в теме - идите на оф.форум и заведите свою тему по поводу кол-ва "крабов" в игре, иначе придется подать на Вас жалобу модераторам.

Edited by kharlashkin
  • Upvote 3
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

Между прочем мой первый "воин" (как и все остальные) был получен именно на геймпаде, до сих пор помню дрожь в руках.

Все танки 6 лвл и выше игрались только на гемйпаде.

49% из более 10k боев "тащила команда" - Вы хоть понимаете о чем говорите?

В общем прошу Вас не флудить в теме - идите на оф.форум и заведите свою тему по поводу кол-ва "крабов" в игре, иначе придется подать на Вас жалобу модераторам.

Стата и медальки - это разные вещи. 17 воинов за 10к боев - это еще раз доказывает, что гейпад надо выбрасывать и играть как все нормальные люди. Нормальный показатель ~80-100 воинов на 10к боев.

Я то как раз понимаю, про что говорю, вы не очень - 490 урона за бой это считаете вы тащили стреляя с авто прицела?! Быстро выцеливать слабые места противника на гейпаде не реально! Поэтому для них и предусмотрена резервация в большенстве нормальных игр.

Подавайте хоть в Гамбургский суд, там пекутся за права геев и других больных, которых током нужно лечить давно.

 

Предупреждение:
  • Оскорбление участников форума
  • Оффтоп в разделе оплачиваемых заказов

  • Downvote 7

Share this post


Link to post

Short link
Share on other sites

Стата и медальки - это разные вещи. 17 воинов за 10к боев - это еще раз доказывает, что гейпад надо выбрасывать и играть как все нормальные люди. Нормальный показатель ~80-100 воинов на 10к боев.

Я то как раз понимаю, про что говорю, вы не очень - 490 урона за бой это считаете вы тащили стреляя с авто прицела?! Быстро выцеливать слабые места противника на гейпаде не реально! Поэтому для них и предусмотрена резервация в большенстве нормальных игр.

Подавайте хоть в Гамбургский суд, там пекутся за права геев и других больных, которых током нужно лечить давно.

 

Предупреждение:
  • Оскорбление участников форума
  • Оффтоп в разделе оплачиваемых заказов

Невнимательно читаете, уважаемый inq. 

 

Как раз до игры на геймпаде я был "крабом" и "оленем". Играл на нетбуке 12' и не заморачивался вообще ни со стратегией, ни с логикой игры - только "Оло-ло раш! Пыщь-пыщь! И слив в первые пару минут". Так был отыгран почти год (думаю, что около 5k боев). Хочу так же напомнить, что на низких уровнях до 5 уровня включительно - попробуйте наносить хотя бы 300 урона среднего.

 

С автоприцелом я никогда не играл, даже сейчас на AMX1375 с геймпадом (!) не использую автоприцел (121 бой на 15.03.2014, 50% побед, знак классности 2)

Edited by kharlashkin
  • Upvote 1
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites
Просьба к форумчанам - кто нибудь подскажите, где лежат скрипты для клавиатуры/мыши в WoT?

 

Начал методично декомпилировать скрипты из игры и проверять. наткнулся на срипт graphicsresolutions.py в папке X:\Games\World_of_Tanks\res\scripts\client\gui\

'graphicsresolutions.py'

import BigWorld
from debug_utils import LOG_CURRENT_EXCEPTION, LOG_ERROR, LOG_DEBUG

class GraphicsResolutions(object):
    MIN_HEIGHT = 1024
    MIN_WIDTH = 768
    MIN_COLOR_DEPTH = 23

    def __gcd(self, a, b):
        while a != 0:
            a, b = b % a, a

        return b

    def __init__(self):
        self.__allVideoModes = []
        maxWidth = 0
        maxHeight = 0
        self.ASPECT_RATIO = []
        self.ASPECT_RATIO.append((4, 3))
        self.ASPECT_RATIO.append((16, 9))
        self.ASPECT_RATIO.append((16, 10))
        self.ASPECT_RATIO.append((19, 10))
        for monitorModes in BigWorld.listVideoModesAllMonitors():
            modes = []
            for mode in monitorModes:
                if self.__isVideoModeSuitable(mode):
                    modes.append(mode)
                    if mode[1] > maxWidth:
                        maxWidth = mode[1]
                    if mode[2] > maxHeight:
                        maxHeight = mode[2]

            self.__allVideoModes.append(modes)

        LOG_DEBUG('max resolution : %d / %d' % (maxWidth, maxHeight))
        if maxHeight > 0:
            _3dVisionAspectRatio = float(maxWidth) / float(maxHeight)
            LOG_DEBUG('aspect ratio : %f' % _3dVisionAspectRatio)
            if _3dVisionAspectRatio > 3.75:
                gcd = self.__gcd(maxWidth, maxHeight)
                LOG_DEBUG('aspect ratio inted: %d / %d' % (maxWidth / gcd, maxHeight / gcd))
                self.ASPECT_RATIO.append((maxWidth / gcd, maxHeight / gcd))
        self.__multisamplingTypes = BigWorld.getSupportedMultisamplingTypes()
        self.__multisamplingTypes.insert(0, 0)
        self.__customAAModes = BigWorld.getSupportedCustomAAModes()
        self.__monitors = BigWorld.wg_getMonitorNames()
        self.__curentMonitorIndex = BigWorld.wg_getActiveMonitorIndex()
        self.__monitorChanged = False
        self.__lastFullscreenSize = None
        self.__lastWindowedSize = None
        BigWorld.wg_setSavePreferencesCallback(self.onSavePreferencesXml)
        return

    @property
    def monitorChanged(self):
        return self.__monitorChanged

    @property
    def __windowSizes(self):
        __windowSizes = []
        for monitorModes in self.__allVideoModes:
            maxWindow = BigWorld.wg_getMaxWindowedResolution(len(__windowSizes))
            modes = []
            for m in monitorModes:
                if m[1] > maxWindow[0] or m[2] > maxWindow[1]:
                    continue
                modes.append((m[1], m[2]))

            if maxWindow not in modes:
                modes.append(maxWindow)
            __windowSizes.append(modes)

        return __windowSizes

    @property
    def __videoModes(self):
        return [ (m[0], m[1], m[2]) for m in self.__allVideoModes[self.monitorIndex] ]

    @property
    def __aspectRatios(self):
        return g_graficsResolutions.ASPECT_RATIO

    @property
    def __videoMode(self):
        return BigWorld.videoModeIndex()

    @property
    def __windowSize(self):
        return tuple(map(int, BigWorld.wg_getCurrentResolution(True)))

    @property
    def __aspectRatio(self):
        return round(BigWorld.getFullScreenAspectRatio(), 6)

    @property
    def __multisamplingType(self):
        return BigWorld.getMultisamplingType()

    @property
    def __customAAMode(self):
        return BigWorld.getCustomAAMode()

    @property
    def isVideoWindowed(self):
        return BigWorld.isVideoWindowed()

    @property
    def isVideoVSync(self):
        return BigWorld.isVideoVSync()

    @property
    def isTripleBuffered(self):
        return BigWorld.isTripleBuffered()

    @property
    def videoModeIndex(self):
        for index, videoModeInfo in enumerate(self.__videoModes):
            if videoModeInfo[0] == self.__videoMode:
                return index

        return -1

    @property
    def monitorIndex(self):
        return BigWorld.wg_getActiveMonitorIndex()

    @property
    def realMonitorIndex(self):
        return self.__curentMonitorIndex

    @property
    def windowSizeIndex(self):
        for index, size in enumerate(self.__windowSizes[self.__curentMonitorIndex]):
            if size == self.__windowSize:
                return index

        return len(self.__windowSizes[self.__curentMonitorIndex])

    @property
    def aspectRatioIndex(self):
        for index, size in enumerate(self.__aspectRatios):
            if round(float(size[0]) / size[1], 6) == self.__aspectRatio:
                return index

        return len(self.__aspectRatios)

    @property
    def multisamplingTypeIndex(self):
        if self.__multisamplingType in self.__multisamplingTypes:
            return self.__multisamplingTypes.index(self.__multisamplingType)
        return -1

    @property
    def customAAModeIndex(self):
        if self.__customAAMode in self.__customAAModes:
            return self.__customAAModes.index(self.__customAAMode)
        return -1

    @property
    def videoModesList(self):
        allModes = []
        for monitorModes in self.__allVideoModes:
            modes = []
            for m in monitorModes:
                modes.append('%dx%d' % (m[1], m[2]))

            allModes.append(modes)

        return allModes

    @property
    def monitorsList(self):
        return self.__monitors

    @property
    def windowSizesList(self):
        allModes = []
        for monitorModes in self.__windowSizes:
            modes = []
            for m in monitorModes:
                modes.append('%dx%d' % m)

            current = '%dx%d' % self.__windowSize
            if current not in modes:
                modes.append(current + '*')
            allModes.append(modes)

        return allModes

    @property
    def aspectRatiosList(self):
        aspectRatios = [ '%d:%d' % m for m in self.__aspectRatios ]
        if self.aspectRatioIndex == len(self.__aspectRatios):
            aspectRatios.append('%s:1*' % BigWorld.wg_getNiceNumberFormat(self.__aspectRatio))
        return aspectRatios

    @property
    def multisamplingTypesList(self):
        return [ '#settings:multisamplingType/type%s' % i for i in self.__multisamplingTypes ]

    @property
    def customAAModesList(self):
        return [ '#settings:customAAMode/mode%s' % i for i in self.__customAAModes ]

    def __isVideoModeSuitable(self, videoMode):
        return videoMode[1] >= GraphicsResolutions.MIN_WIDTH and videoMode[2] >= GraphicsResolutions.MIN_HEIGHT and videoMode[3] >= GraphicsResolutions.MIN_COLOR_DEPTH

    def getVideoModeByIndex(self, index):
        if len(self.__videoModes) > index > -1:
            return self.__videoModes[int(index)][0]
        else:
            return None
            return None

    def getWindowSizeByIndex(self, index):
        if len(self.__windowSizes[self.__curentMonitorIndex]) > index > -1:
            return self.__windowSizes[self.__curentMonitorIndex][int(index)]
        return self.__windowSize

    def getAspectRatioByIndex(self, index):
        if len(self.__aspectRatios) > index > -1:
            ars = self.__aspectRatios[int(index)]
            return round(float(ars[0]) / ars[1], 6)
        else:
            return None
            return None

    def getMultisamplingTypeByIndex(self, index):
        if len(self.__multisamplingTypes) > index > -1:
            return self.__multisamplingTypes[int(index)]
        else:
            return None
            return None

    def getCustomAAModeByIndex(self, index):
        if len(self.__customAAModes) > index > -1:
            return self.__customAAModes[int(index)]
        else:
            return None
            return None

    @property
    def gamma(self):
        return BigWorld.getGammaCorrection()

    def applyChanges(self, isFullScreen, isVideoVSync, isTripleBuffered, sizeIndex, aspectRatioIndex, multisamplingIndex, customAAIndex, gamma, monitorIndex):
        if self.__curentMonitorIndex != monitorIndex:
            self.__monitorChanged = True
        self.__curentMonitorIndex = monitorIndex
        BigWorld.wg_setActiveMonitorIndex(monitorIndex)
        if self.isVideoVSync != isVideoVSync:
            BigWorld.setVideoVSync(isVideoVSync)
        if self.isTripleBuffered != isTripleBuffered:
            BigWorld.setTripleBuffering(isTripleBuffered)
        if self.gamma != gamma:
            gamma = max(gamma, 0.5)
            gamma = min(gamma, 2.0)
            BigWorld.setGammaCorrection(gamma)
        aspectRatio = self.getAspectRatioByIndex(aspectRatioIndex)
        if aspectRatio is not None and aspectRatio != self.__aspectRatio:
            BigWorld.changeFullScreenAspectRatio(aspectRatio)
        multisamplingType = self.getMultisamplingTypeByIndex(multisamplingIndex)
        if self.__multisamplingType != multisamplingType:
            BigWorld.setMultisamplingType(multisamplingType)
        customAAMode = self.getCustomAAModeByIndex(customAAIndex)
        if self.__customAAMode != customAAMode:
            BigWorld.setCustomAAMode(customAAMode)
        if isFullScreen:
            videoMode = self.getVideoModeByIndex(sizeIndex)
            if not self.__monitorChanged and (videoMode != self.__videoMode or self.isVideoWindowed):
                BigWorld.changeVideoMode(videoMode, False)
            windowSize = self.getWindowSizeByIndex(sizeIndex)
            self.__lastIsWindowed = False
            self.__lastFullscreenSize = (windowSize[0], windowSize[1])
        else:
            if not self.__monitorChanged and not self.isVideoWindowed:
                BigWorld.changeVideoMode(self.getVideoModeByIndex(sizeIndex), True)
            windowSize = self.getWindowSizeByIndex(sizeIndex)
            oldResolution = BigWorld.wg_getCurrentResolution(True)
            if windowSize is not None and (oldResolution[0] != windowSize[0] or oldResolution[1] != windowSize[1]):
                BigWorld.resizeWindow(windowSize[0], windowSize[1])
            self.__lastIsWindowed = True
            self.__lastWindowedSize = (windowSize[0], windowSize[1])
        return

    def onSavePreferencesXml(self, root):
        if not self.__monitorChanged:
            return
        else:
            devPref = root['devicePreferences']
            devPref.writeBool('windowed', self.__lastIsWindowed)
            if self.__lastFullscreenSize is not None:
                devPref.writeInt('fullscreenWidth', self.__lastFullscreenSize[0])
                devPref.writeInt('fullscreenHeight', self.__lastFullscreenSize[1])
            if self.__lastWindowedSize is not None:
                devPref.writeInt('windowedWidth', self.__lastWindowedSize[0])
                devPref.writeInt('windowedHeight', self.__lastWindowedSize[1])
            return
            return


g_graficsResolutions = GraphicsResolutions() 

 

Простая замена MIN_HEIGHT = 1024 и MIN_WIDTH = 768, на MIN_HEIGHT = 800 и MIN_WIDTH = 600 ничего не дала (я как бы и не надеялся, что взлетит - но решил попробовать), но по крайней мере есть хоть какое-то направление :)

Так же пробовал одновременно запускать тестовую программку pygame и скрипт вибрации - не мешают друг другу, pygame отлавливает нажатия кнопок и перемещение стиков/триггеров и одновременно геймпад вибрирует.

Edited by kharlashkin
  • Upvote 1
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

Нашел скрипт управления курсором в игре

'cursor.py'

import BigWorld
import GUI
import Math
from debug_utils import *
from bwdebug import WARNING_MSG
_mouseModeRefCount = 0

def showCursor(show):
    global _mouseModeRefCount
    if show:
        _mouseModeRefCount += 1
        if _mouseModeRefCount > 0:
            BigWorld.setCursor(GUI.mcursor())
            GUI.mcursor().visible = True
    else:
        _mouseModeRefCount -= 1
        if _mouseModeRefCount == 0:
            BigWorld.setCursor(BigWorld.dcursor())
            GUI.mcursor().visible = False
        if _mouseModeRefCount < 0:
            WARNING_MSG('mouseModeRefCount is negative!')


def forceShowCursor(show):
    if show:
        BigWorld.setCursor(GUI.mcursor())
        GUI.mcursor().visible = True
    else:
        BigWorld.setCursor(BigWorld.dcursor())
        GUI.mcursor().visible = False


def pixelPosition():
    screenWidth, screenHeight = GUI.screenResolution()
    mouseLeft, mouseTop = GUI.mcursor().position
    width = round((1.0 + mouseLeft) / 2.0 * screenWidth)
    height = round(-(-1.0 + mouseTop) / 2.0 * screenHeight)
    return (width, height) 

Знающие люди помогите! Кто может рассказать и объяснить что к чему в скрипте. Общее понятие у меня есть о том что к чему, но хочется убедиться в правильности предположений.


Нашел интересную темку, которая возможно сможет помочь в направлении использования стиков вместо мыши: Ошибка мода, пропадает обзор мышью.

Нет там в теме ничего, что может помочь.


В принципе раскомпилировал уже всю игру - но не вывешивать же весь код сюда ;)

Edited by kharlashkin
  • Upvote 1
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites

@kharlashkin, я перенёс вашу тему уровнем выше, поскольку она носит скорее характер поиска решения, нежели конкретного заказа. Возможно в этом разделе она получит больший отклик.

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

@kharlashkin, я перенёс вашу тему уровнем выше, поскольку она носит скорее характер поиска решения, нежели конкретного заказа. Возможно в этом разделе она получит больший отклик.

Да, согласен. Если при написании мода для "вибрации" - было понятно куда копать, то в данном случае не понятно. Необходимо сначала найти решение, а потом уже писать мод.

В скрипте X:\Games\World_of_Tanks\res\scripts\client\gui\Scaleform\SettingsInterface.py вот это меня заинтересовало

'SettingsInterface.py'

        mouse = settings['controls']['mouse']
        if hasattr(player.inputHandler, 'ctrls'):
            player.inputHandler.ctrls['arcade'].camera.setUserConfigValue('sensitivity', mouse['arcadeSens']['value'])
            player.inputHandler.ctrls['sniper'].camera.setUserConfigValue('sensitivity', mouse['sniperSens']['value'])
            player.inputHandler.ctrls['strategic'].camera.setUserConfigValue('sensitivity', mouse['artSens']['value'])
        else:
            ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE]
            if ds:
                ds['arcadeMode/camera'].writeFloat('sensitivity', mouse['arcadeSens']['value'])
                ds['sniperMode/camera'].writeFloat('sensitivity', mouse['sniperSens']['value'])
                ds['strategicMode/camera'].writeFloat('sensitivity', mouse['artSens']['value'])
        g_settingsCore.applySetting('mouseHorzInvert', bool(mouse['horInvert']['value']))
        g_settingsCore.applySetting('mouseVertInvert', bool(mouse['vertInvert']['value']))
        g_settingsCore.applySetting('backDraftInvert', bool(mouse['backDraftInvert']['value'])) 

 

Насколько я понимаю это настройки для мышки. Может как то это можно использовать...

Только заметил, что не опубликовал код тестовый для pygame, исправил это в сообщении про pygame.

Edited by kharlashkin
  • Upvote 2
  • Downvote 1

Share this post


Link to post

Short link
Share on other sites
Guest
This topic is now closed to further replies.

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...