kharlashkin 239 Posted March 11, 2014 (edited) Доброе время суток, уважаемые форумчане! "С восторгом предаюсь в руки родной милиции форума, надеюсь на нее него и уповаю." - Иван Васильевич Бунша © В прошлом году мною было опубликована "хотелка", которая блестяще была решена с помощью inj3ct0r, sirmax и другими. Должен сказать, что данный мод был довольно холодно воспринят интернет-сообществом (публикация на habrahabr хоть и набрала более 10k просмотров, но в минусе), после обновления 0.8.11 и обновления системного python в игре, ко мне достаточно много человек постучалось с просьбой поделиться обновленным модом. С этой целью я написал на официальном форуме подробную инструкцию. Таким образом хочу снова выразить огромную благодарность всем, кто участвовал в создании. Теперь перейдем к новым "хотелкам". На официальном форуме WoT в теме "корпоративная игра на 1м танке" меня посетила мысль об игровом режиме "Разделенный экран", т.е. одновременной игре вдвоем на одном экране во взводе/роте/команде. Разумеется ни о каком "нагибе", "подставных ротах" и прочем речь не идет, только "игра для фана" и "пыщь-пыщь" под пивасик с другом на геймпадах. Теория была мною опробована на виртуальных ПК под VMWare Player: Все заработало - геймпады пробросились в ВМ, картинку настроил в портретный режим, вибрации работают. Но неиграбельно - fps в среднем в каждой ВМ от 8 до 15. Более детальное изучение всех составляющих для танков вдвоем на ТВ привело к следующему:1. Настроить клиент игры для возможного запуска двух копий2. Необходимо разделить экран телевизора на два виртуальных.3. Решить проблему отправки нажатий кнопок/отклонения стиков с геймпада в неактивное окно.4. Отправить вибрацию в разные геймпады с разных клиентов. 1. Запуск 2-х клиентов.По-умолчанию, разработчики из Wargaming убрали возможность одновременного запуска двух копий. Не буду описывать все прелести «песочницы» — Sandboxie Вам в помощь. 2. Разделение экрана телевизора на две части.«WoT» в оконном режиме может иметь минимальное разрешение 1024х768, в случае разделения FullHD телевизора пополам, необходимо разрешение каждого окна минимум 960х1080, а учитывая рамки окна и заголовок и того меньше. Т.е. стандартными «горячими клавишами» через Snap разнеся окна в разные стороны мы получаем частичное перекрытие окон. Любые другие утилиты для разделения рабочего стола на две части используют похожий функционал и никаким образом не могут повлиять на минимальное разрешения игры по ширине.Перепробовав огромное количество, натолкнулся на Virtual Display Manager, подкупило отсутствие в названии слова desktop.Утилита сделала нужное — добавив конфигурацию двух виртуальных дисплеев и перемещая окно в нужный — игра принимает нужное нам значение, а именно занимает ровно половину экрана. 3. Отправка нажатий клавиш в неактивное окно.Это решение было для моего ума самым сложным. Два клиента запущены, окна разнесены в стороны и не перекрывают друг-дружку, но одно из окон активно, соответственно принимает нажатия кнопок и перемещения мышки, а вот второе не активно со всеми вытекающими. К решению этой проблемы меня подтолкнуло знакомство с 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. В общем, у меня получилось как-то так. Выбор типа снарядов переключением — один раз нажал — 1-й тип, второй — 2-й, третий — третий и сброс в начало (1-2-3). снаряды применяются сразу — AHK отдает двукратное нажатие в игру. Меню приказов — сочетание левого бампера и правого стика, лечение и ремонт кнопки «Х» и «В» в сочетании с левым стиком. Видео геймплея 4. Настройка вибраций для геймпадов.Так как данная модификация игры использует веб-сервис для отправки вибраций, то для отправки во второй геймпад, нужно было просто изменить порт Flask. Но, для игры в «Разделенном экране» на вибрирующих геймпадах нужно запускать в «песочнице» полную копию клиента (скопировать папку рядом с другим именем) со своим отдельным модом, также скопировать в «песочницу» Python27. Хочу отдельно выразить особую огромнейшую благодарность inj3ct0r, за всесторонние консультации и помощь, а так же всем участвовавшим! Спасибо Вам всем, без Вашей помощи, подсказок и поддержки у меня ничего не получилось бы. Думается, что данное решение можно попробовать применить ко многим играм. Решение получилось очень неудобным — много всяких «но». Но могу сказать что игра для «фана» удалась. Удачи всем в боях! Во вложении моды для основного/"песочного" клиентов и скрипт. WoTSplitScreen.7z Edited July 1, 2014 by kharlashkin 8 1 Share this post Link to post Short link Share on other sites
MakcT40 331 #161950 Posted March 11, 2014 1. Упаковка всей игры WoT как "тонкое приложение" с помощью VMWare ThinApp или Citrix XenApp, соответственно можно запустить два клиента WoT изолированных друг от друга со своими настройками и модами. Интересно как скажется на производительность игры ;) Портативный клиент пробовали? Я, правда, не знаю, можно ли запустить 2 клиента одновременно. Share this post Link to post Short link Share on other sites
kharlashkin 239 #161952 Posted March 11, 2014 Портативный клиент пробовали? Я, правда, не знаю, можно ли запустить 2 клиента одновременно. Спасибо за ссылку! Гуглил на днях портативный клиент - на эту тему не попадал. Протестирую - отпишусь. 1 1 Share this post Link to post Short link Share on other sites
goodman 664 #161953 Posted March 11, 2014 только "игра для фана" и "пыщь-пыщь" под пивасик с другом на геймпадах сорри за оффтоп, но представил себе это действо... чертовски весело же :-)) 1 Share this post Link to post Short link Share on other sites
kharlashkin 239 #161955 Posted March 11, 2014 сорри за оффтоп, но представил себе это действо... чертовски весело же :-)) Кстати я давно в гости к знакомому хожу с ноутбуком, проводным геймпадом и HDMI-кабелечком. Причем я играю на его ТВ в гостиной, он же скрючивается за ПК в соседней комнате. Уже начинает привыкать играть на контроллере - начинаем меняться местами. Для таких "покатушек" и держу танчики 1-2 уровня. 1 1 Share this post Link to post Short link Share on other sites
Uti-Puti 344 #161963 Posted March 11, 2014 (edited) Должен сказать, что данный мод был довольно холодно воспринят интернет-сообществом (публикация на habrahabr хоть и набрала более 10k просмотров, но в минусе) Статья на хабре получила бы более теплый приём, если бы в ней было больше технической информации, например описание проблем, с которыми пришлось столкнуться при разработке, и их решения. А в статье ни слова об этом, и ссылки на тему с "хотелкой", где можно посмотреть процесс борьбы за работоспособность там тоже не нашлось. + даже ссылка на XVM устаревшая(только гугл.код, а среднестатистическому хабраюзеру XVM может быть и неизвестен). Так что проблема не в моде, а в подаче информации. Если исправить эти недочёты в будущем, всё будет хорошо =) Удачи в работе над сплит-скрином, подпишусь на тему, буду поглядывать =) Edited March 11, 2014 by Krab Age 3 Share this post Link to post Short link Share on other sites
kharlashkin 239 #161964 Posted March 11, 2014 Статья на хабре получила бы более теплый приём, если бы в ней было больше технической информации, например описание проблем, с которыми пришлось столкнуться при разработке, и их решения. А в статье ни слова об этом, и ссылки на тему с "хотелкой", где можно посмотреть процесс борьбы за работоспособность там тоже не нашлось. + даже ссылка на XVM устаревшая(только гугл.код, а среднестатистическому хабраюзеру XVM может быть и неизвестен). Так что проблема не в моде, а в подаче информации. Если исправить эти недочёты в будущем, всё будет хорошо =) Удачи в работе над сплит-скрином, подпишусь на тему, буду поглядывать =) "И опыт, сын ошибок трудных" - А.С.Пушкин © Как бы пост писался "сгоряча" - согласен, что моя ошибка. К тому же, уже потом, было выяснено очень много деталей, которые очень подогрели бы интерес - но запал прошел у меня. Думаю, что будет версия 2 :) 1 1 Share this post Link to post Short link Share on other sites
kharlashkin 239 #162216 Posted March 13, 2014 (edited) Портативный клиент пробовали? Я, правда, не знаю, можно ли запустить 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 March 13, 2014 by kharlashkin 1 1 Share this post Link to post Short link Share on other sites
kharlashkin 239 #162410 Posted March 14, 2014 Нашел несколько интересных проектов, которые могут позволить подключать геймпады напрямую в 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 способом который использовался для "мода вибраций". 2 1 Share this post Link to post Short link Share on other sites
kharlashkin 239 #162567 Posted March 15, 2014 (edited) Вчера вечером пробовал 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 March 19, 2014 by kharlashkin 2 1 Share this post Link to post Short link Share on other sites
inq 11 #162568 Posted March 15, 2014 Играть на геймпаде в шуторо-подобные игры гейство. Любой более менее адекватный игрок на клаве и мыше вывертить гейпада в 0. Хотите играть лежа на диване, купите X-box и не страдайте фигней. 4 Share this post Link to post Short link Share on other sites
kharlashkin 239 #162569 Posted March 15, 2014 Играть на геймпаде в шуторо-подобные игры гейство. Любой более менее адекватный игрок на клаве и мыше вывертить гейпада в 0. Хотите играть лежа на диване, купите X-box и не страдайте фигней. На официальном форуме есть мой гайд с игрой в танки на геймпаде. Там же выложены мои мысли относительно управления в WoT в сравнении с шутерами. "Стата" - лучшее доказательство возможности так играть. Я не "цифродрочер" и играю без премиума, так что мои показатели ничем не лучше и не хуже других игроков. А Вы из тех, кто считает всех игроков "ракообразными", играющих для "пыщь-пыщь"? 2 1 Share this post Link to post Short link Share on other sites
inq 11 #162570 Posted March 15, 2014 (edited) "Стата" - лучшее доказательство возможности так играть. Я не "цифродрочер" и играю без премиума, так что мои показатели ничем не лучше и не хуже других игроков. А Вы из тех, кто считает всех игроков "ракообразными", играющих для "пыщь-пыщь"? Стата как раз доказывает, что так играть нельзя! Средний урон 490 это 2-3 пробития за бой, 49% побед говорит о дичайшем везении, тащила команда. Я из тех, кто ненавидит сливающихся аутистов в первые 3 мин боя или стоящих/катающихся черти где. У гейпадов есть своя резервация, ползайте там раз Вам так это нравиться. Смешивать консоль и PC это идиотизм. Edited March 15, 2014 by inq 4 Share this post Link to post Short link Share on other sites
kharlashkin 239 #162592 Posted March 15, 2014 (edited) Стата как раз доказывает, что так играть нельзя! Средний урон 490 это 2-3 пробития за бой, 49% побед говорит о дичайшем везении, тащила команда. Я из тех, кто ненавидит сливающихся аутистов в первые 3 мин боя или стоящих/катающихся черти где. У гейпадов есть своя резервация, ползайте там раз Вам так это нравиться. Смешивать консоль и PC это идиотизм. Между прочем мой первый "воин" (как и все остальные) был получен именно на геймпаде, до сих пор помню дрожь в руках. "Скриншот результатов" Все танки 6 лвл и выше игрались только на гемйпаде. 49% из более 10k боев "тащила команда" - Вы хоть понимаете о чем говорите? В общем прошу Вас не флудить в теме - идите на оф.форум и заведите свою тему по поводу кол-ва "крабов" в игре, иначе придется подать на Вас жалобу модераторам. Edited March 15, 2014 by kharlashkin 3 1 Share this post Link to post Short link Share on other sites
inq 11 #162615 Posted March 15, 2014 Между прочем мой первый "воин" (как и все остальные) был получен именно на геймпаде, до сих пор помню дрожь в руках. Все танки 6 лвл и выше игрались только на гемйпаде. 49% из более 10k боев "тащила команда" - Вы хоть понимаете о чем говорите? В общем прошу Вас не флудить в теме - идите на оф.форум и заведите свою тему по поводу кол-ва "крабов" в игре, иначе придется подать на Вас жалобу модераторам. Стата и медальки - это разные вещи. 17 воинов за 10к боев - это еще раз доказывает, что гейпад надо выбрасывать и играть как все нормальные люди. Нормальный показатель ~80-100 воинов на 10к боев. Я то как раз понимаю, про что говорю, вы не очень - 490 урона за бой это считаете вы тащили стреляя с авто прицела?! Быстро выцеливать слабые места противника на гейпаде не реально! Поэтому для них и предусмотрена резервация в большенстве нормальных игр. Подавайте хоть в Гамбургский суд, там пекутся за права геев и других больных, которых током нужно лечить давно. Предупреждение:Оскорбление участников форума Оффтоп в разделе оплачиваемых заказов 7 Share this post Link to post Short link Share on other sites
kharlashkin 239 #162617 Posted March 15, 2014 (edited) Стата и медальки - это разные вещи. 17 воинов за 10к боев - это еще раз доказывает, что гейпад надо выбрасывать и играть как все нормальные люди. Нормальный показатель ~80-100 воинов на 10к боев. Я то как раз понимаю, про что говорю, вы не очень - 490 урона за бой это считаете вы тащили стреляя с авто прицела?! Быстро выцеливать слабые места противника на гейпаде не реально! Поэтому для них и предусмотрена резервация в большенстве нормальных игр. Подавайте хоть в Гамбургский суд, там пекутся за права геев и других больных, которых током нужно лечить давно. Предупреждение:Оскорбление участников форума Оффтоп в разделе оплачиваемых заказов Невнимательно читаете, уважаемый inq. Как раз до игры на геймпаде я был "крабом" и "оленем". Играл на нетбуке 12' и не заморачивался вообще ни со стратегией, ни с логикой игры - только "Оло-ло раш! Пыщь-пыщь! И слив в первые пару минут". Так был отыгран почти год (думаю, что около 5k боев). Хочу так же напомнить, что на низких уровнях до 5 уровня включительно - попробуйте наносить хотя бы 300 урона среднего. С автоприцелом я никогда не играл, даже сейчас на AMX1375 с геймпадом (!) не использую автоприцел (121 бой на 15.03.2014, 50% побед, знак классности 2) Edited March 16, 2014 by kharlashkin 1 1 Share this post Link to post Short link Share on other sites
kharlashkin 239 #162976 Posted March 17, 2014 (edited) Просьба к форумчанам - кто нибудь подскажите, где лежат скрипты для клавиатуры/мыши в 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 March 17, 2014 by kharlashkin 1 1 Share this post Link to post Short link Share on other sites
kharlashkin 239 #163139 Posted March 18, 2014 (edited) Нашел скрипт управления курсором в игре '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 March 18, 2014 by kharlashkin 1 1 Share this post Link to post Short link Share on other sites
13 3,395 #163258 Posted March 18, 2014 @kharlashkin, я перенёс вашу тему уровнем выше, поскольку она носит скорее характер поиска решения, нежели конкретного заказа. Возможно в этом разделе она получит больший отклик. 1 Share this post Link to post Short link Share on other sites
kharlashkin 239 #163329 Posted March 19, 2014 (edited) @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 March 19, 2014 by kharlashkin 2 1 Share this post Link to post Short link Share on other sites