0,0
рейтинг
8 января 2014 в 17:26

Разработка → Поддержка обратной связи геймпадом в WoT recovery mode

Доброе время суток, уважаемое хабрасообщество!

Скоро будет год, как я играю на геймпаде в World of Tanks, если кто-то пропустил мимо, как я выбирал первый в своей жизни ПК для игры прошу сюда.

Играть геймпадом интересно, но разработчики не хотят официально поддерживать его, соответственно приходится всячески извращаться для получения «некоего функционала».



Начну с небольшого отступления и опишу подробности игры в WoT на геймпаде от «коробки 360».

Для более-менее вменяемой игры необходим Xpadder. Видео, как я настраивал


Как играть? Пример рядового боя


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

Теперь к главному.
Всем известно, что во многих играх используется так называемая обратная связь с геймпадом или ForceFeedback, которая дает более глубокое погружение в игровой процесс за счет вибраций (едешь по кочкам — есть вибрация, получаешь удар от противника — есть вибрация и т.п.). Разумеется, что меня заинтересовала возможность получить виброотдачу на геймпаде в «танчиках».
Покопав данную тему нашел следующие моменты, позволяющие это сделать:
1. Всё что нужно пишется на python, именно этот язык используется в игре для выполнения всего и вся (думаю, что кроме ядра BigWorld).
2. На stackoverflow, нашел нужный мне код, позволяющий передавать вибрации в геймпад.
3. Wargaming уже встроил в свою игру вибрации для виброжопки вибронакидки GameTrix, сам проект вибронакидки является открытым и весь SDK и прочее доступно для изучения и скачивания.
4. Поиск по папкам игры выдал местонахождение скриптов «World_of_Tanks\res\scripts\client\vibroeffect».

Разобрав «по полочкам» SDK вибронакидки, скрипты игры — выходит, что с помощью XVM (eXtended Visualization Mod for World of Tanks), можно попробовать подменить BigWorld.WGVibration на GamePadVibration, непосредственно через который вызывать нужные методы вибрации непосредственно в геймпаде.
Но оказалось, что в игровом python нет полноценного ctypes, соответственно появилась необходимость сделать сервис, позволяющий выполнять «вибрирующий» скрипт на обычном python, получая команды из игры. Такой сервис был создан с помощью Flask. Ниже привожу код исполняемого скрипта, передающий вибрацию на геймпад при выстрелах, получении попаданий/рикошетов и повреждений танка. Виброэффекты писались мною из оригинальных для вибронакидки.
GPService.py
ENABLE_LOG = False
d = dict()
from flask import Flask
from flask import request
import ctypes

# Define necessary structures
class XINPUT_VIBRATION(ctypes.Structure):
    _fields_ = [("wLeftMotorSpeed", ctypes.c_ushort),
                ("wRightMotorSpeed", ctypes.c_ushort)]

xinput = ctypes.windll.xinput1_3  # Load Xinput.dll

# Set up function argument types and return type
XInputSetState = xinput.XInputSetState
XInputSetState.argtypes = [ctypes.c_uint, ctypes.POINTER(XINPUT_VIBRATION)]
XInputSetState.restype = ctypes.c_uint

# You can also create a helper function like this:
def set_vibration(controller, left_motor, right_motor):
    vibration = XINPUT_VIBRATION(int(left_motor * 65535), int(right_motor * 65535))
    XInputSetState(controller, ctypes.byref(vibration))
    
if not ENABLE_LOG:
    import logging
    log = logging.getLogger('werkzeug')
    log.setLevel(logging.ERROR)

app = Flask(__name__)

@app.route('/connect')
def connect():
    print 'Connection checked - Ok.'
    return 'True'

@app.route('/loadEffectFromFile')
def loadEffectFromFile():
    effectHandle = request.args.get('effectHandle')
    fileName = request.args.get('fileName')
    #print 
    #print 'loadEffectFromFile'
    #print 'effectHandle =', request.args.get('effectHandle')
    #print 'fileName =', request.args.get('fileName')
    if not effectHandle in d:
        d[effectHandle] = fileName
    return ''
    
@app.route('/getEffectLength')
def getEffectLength():
    #print 
    #print 'getEffectLength'
    #print 'effectHandle =', request.args.get('effectHandle')
    #print 'returnedLength =', request.args.get('returnedLength')
    return ''

import threading, time
def shot_main():
    set_vibration(0, 1.0, 1.0)
    time.sleep(0.15)
    set_vibration(0, 0, 0)
    time.sleep(0.25)
    set_vibration(0, 0.5, 0)
    time.sleep(0.25)
    set_vibration(0, 0.0, 0.0)

def shot_large():
    set_vibration(0, 0.5, 0.6)
    time.sleep(0.1)
    set_vibration(0, 0.3, 0)
    time.sleep(0.1)
    set_vibration(0, 0.1, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.5)
    time.sleep(0.1)
    set_vibration(0, 0.0, 0.0)    

def shot_medium():
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.6, 0.6)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0)
    time.sleep(0.1)
    set_vibration(0, 0.2, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.6)
    time.sleep(0.2)
    set_vibration(0, 0.0, 0.0)

def shot_small():
    set_vibration(0, 0.6, 0.8)
    time.sleep(0.1)
    set_vibration(0, 0, 0.4)
    time.sleep(0.1)
    set_vibration(0, 0.0, 0.0)

def hit_nonpenetration():
    set_vibration(0, 0.5, 0.5)
    time.sleep(0.1)
    set_vibration(0, 0.0, 0.0)

def hit_ricochet():
    set_vibration(0, 0.8, 0.8)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.2)
    set_vibration(0, 0.0, 0.5)
    time.sleep(0.05)
    set_vibration(0, 0, 0)

def hit_splash():
    set_vibration(0, 0.8, 0)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0.8)
    time.sleep(0.1)
    set_vibration(0, 0, 0.4)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.05)
    set_vibration(0, 0, 0.4)
    time.sleep(0.15)
    set_vibration(0, 0, 0)

def hit():
    set_vibration(0, 1, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.05)
    set_vibration(0, 0, 1)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.05)
    set_vibration(0, 0.6, 0.6)
    time.sleep(0.1)
    set_vibration(0, 0.5, 0.5)
    time.sleep(0.05)
    set_vibration(0, 0.25, 0.25)
    time.sleep(0.05)
    set_vibration(0, 0, 0)

def crit_contusion():
    set_vibration(0, 1, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.2)
    set_vibration(0, 0, 0.4)
    time.sleep(0.2)
    set_vibration(0, 0, 0)
    time.sleep(0.3)
    set_vibration(0, 0, 0.3)
    time.sleep(0.2)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0, 0.25)
    time.sleep(0.2)
    set_vibration(0, 0, 0)

def crit_death():
    set_vibration(0, 1, 1)
    time.sleep(0.1)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 1)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.8)
    time.sleep(0.1)
    set_vibration(0, 0.8, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.8)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.5, 0.5)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0.3, 0.1)
    time.sleep(0.1)
    set_vibration(0, 0.2, 0)
    time.sleep(0.1)
    set_vibration(0, 0.1, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0)

def crit_engine():
    set_vibration(0, 1, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0.6)
    time.sleep(0.3)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.6)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0.6)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0.6)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.6)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0.6)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0)
    time.sleep(0.2)
    set_vibration(0, 0, 0)

def crit_fire():
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.3)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.5)
    set_vibration(0, 0.5, 0.5)
    time.sleep(2)
    set_vibration(0, 0, 0)

def crit_run():
    set_vibration(0, 1, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.2)
    set_vibration(0, 0, 0.8)
    time.sleep(0.3)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.5)
    time.sleep(0.3)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.8, 0)
    time.sleep(0.1)
    set_vibration(0, 0.6, 0)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0)
    time.sleep(0.1)
    set_vibration(0, 0.2, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.4, 0)
    time.sleep(0.1)
    set_vibration(0, 0.2, 0)
    time.sleep(0.1)
    set_vibration(0, 0.1, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0)

def crit_track_move():
    set_vibration(0, 0.6, 0.2)
    time.sleep(0.1)
    set_vibration(0, 0, 0.1)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.4)
    set_vibration(0, 0, 0.1)
    time.sleep(0.1)
    set_vibration(0, 0, 0.05)
    time.sleep(0.1)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.1)
    time.sleep(0.1)
    set_vibration(0, 0, 0.05)
    time.sleep(0.1)
    set_vibration(0, 0, 0)

def crit_track():
    set_vibration(0, 0.8, 0)
    time.sleep(0.05)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0, 0.8)
    time.sleep(0.05)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.5, 0.5)
    time.sleep(0.05)
    set_vibration(0, 0, 0)
    time.sleep(0.1)
    set_vibration(0, 0.5, 0.5)
    time.sleep(0.05)
    set_vibration(0, 0, 0)
        
@app.route('/startEffect')
def startEffect():
    handle = request.args.get('handle')
    count = request.args.get('count')
    
    if handle in d:
        print 'startEffect: '+ d[handle]
        if 'shot_main' in d[handle]:
            print 'shot_main'
            # init thread
            t1 = threading.Thread(target=shot_main)
            # start threads
            t1.start()
                       
        if 'shot_large' in d[handle]:
            print 'shot_large'
            # init thread
            t1 = threading.Thread(target=shot_large)
            # start threads
            t1.start()
             
        if 'shot_medium' in d[handle]:
            print 'shot_medium'
            # init thread
            t1 = threading.Thread(target=shot_medium)
            # start threads
            t1.start()
            
        if 'shot_small' in d[handle]:    
            print 'shot_small'
            # init thread
            t1 = threading.Thread(target=shot_small)
            # start threads
            t1.start()
            
        if 'hit_nonpenetration' in d[handle]:    
            print 'hit_nonpenetration'
            # init thread
            t1 = threading.Thread(target=hit_nonpenetration)
            # start threads
            t1.start()
            
        if 'hit_ricochet' in d[handle]:    
            print 'hit_ricochet'
            # init thread
            t1 = threading.Thread(target=hit_ricochet)
            # start threads
            t1.start()
            
        if 'hit_splash' in d[handle]:    
            print 'hit_splash'
            # init thread
            t1 = threading.Thread(target=hit_splash)
            # start threads
            t1.start()
            
        if 'hit' in d[handle]:    
            print 'hit'
            # init thread
            t1 = threading.Thread(target=hit)
            # start threads
            t1.start()

        if 'crit_contusion' in d[handle]:    
            print 'crit_contusion'
            # init thread
            t1 = threading.Thread(target=crit_contusion)
            # start threads
            t1.start()

        if 'crit_death' in d[handle]:    
            print 'crit_death'
            # init thread
            t1 = threading.Thread(target=crit_death)
            # start threads
            t1.start()

        if 'crit_engine' in d[handle]:    
            print 'crit_engine'
            # init thread
            t1 = threading.Thread(target=crit_engine)
            # start threads
            t1.start()

        if 'crit_fire' in d[handle]:    
            print 'crit_fire'
            # init thread
            t1 = threading.Thread(target=crit_fire)
            # start threads
            t1.start()

        if 'crit_run' in d[handle]:    
            print 'crit_run'
            # init thread
            t1 = threading.Thread(target=crit_run)
            # start threads
            t1.start()

        if 'crit_track_move' in d[handle]:    
            print 'crit_track_move'
            # init thread
            t1 = threading.Thread(target=crit_track_move)
            # start threads
            t1.start()

        if 'crit_track' in d[handle]:    
            print 'crit_track'
            # init thread
            t1 = threading.Thread(target=crit_track)
            # start threads
            t1.start()
            
            
    # print 
    # print 'startEffect'
    # print 'handle =', handle
    # print 'count =', count
    
    return ''

if __name__ == "__main__":
    app.debug = True
    app.run()



Так же для работы вибраций на геймпаде:
1. Необходимо установить Python (последняя версия), папка пусть будет по-умолчанию «C:\Python27».

2. Необходимо установить Flask, я делал по этой инструкции. Качаем файл distribute_setup.py в папку «C:\temp» (например), запускаем консоль «Win+R» — cmd и выполняем команду «C:\Python27\python.exe C:\temp\distribute_setup.py» наблюдаем процесс загрузки нужных файлов в папку «C:\Python27\Scripts», далее в консоли запускаем команды по очереди и наблюдаем их выполнение:
C:\Python27\python.exe C:\Python27\Scripts\easy_install-2.7-script.py Flask
C:\Python27\python.exe C:\Python27\Scripts\easy_install-2.7-script.py Jinja2
C:\Python27\python.exe C:\Python27\Scripts\easy_install-2.7-script.py Werkzeug
C:\Python27\python.exe C:\Python27\Scripts\easy_install-2.7-script.py Virtualenv

В файле GPsettings.xml прописан скрытый запуск системного python из папки по умолчанию «C:\Python27».

В настоящее время я тестирую скрипт для забора эффектов напрямую из файлов для вибронакидки со смешиванием эффектов.

Удачи всем в боях!

P.S. В связи с невысоким интересом хабрасообщества, всем интересующимся на официальный форум WoT.
Владимир Харлашкин @kharlashkin
карма
13,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (29)

  • –1
    Геймплей выглядит натянуто и неудобно. Особенно это касается прицеливания.
    • +1
      Ну так извините, перед 32" ТВ на обычном расстоянии (2,5 метра), в отдельном окошке в два раза меньше обычного (чтобы поместился Xpadder) было очень неудобно.
  • 0
    Как по мне джойстик очень неудобен по сравнению с мышкой при прицеливании, вечно на консолях в шутерах мучаюсь.
    • +1
      По моему мнению многие игроки в WoT не думают об особенностях игры, которые настолько выравнивают управление, что разницы то и нет:
      1. Время сведения. Как бы не выцеливали в любом режиме игроки противника, а ждать надо.
      2. Разброс снаряда. Особенно на дальних/средних дистанциях всегда есть вероятность не попасть или попасть не туда.
      3. Поворот башни. Можно мышей как угодно быстро вертеть — от этого ничего не зависит.
      4. Время заряда. Нельзя выпустить весь боекомплект сразу.
      Совокупность этих (а может я чего-то и не учел) особенностей ни дает никакому виду управления преимущества.
      Таким образом особенности управления вторичны, а вот удобства важнее.

      Ну а в шутерах — да, согласен. Хотя сам с удовольствием играю именно на геймпаде.
      • 0
        Не хочу разводить флуд по поводу WoT. Но скорость реакции мышь важна при стрельбе в движении. Или вы на светляках и ст останавливаетесь чтобы выстрелить?
        • 0
          Автоприцел/круиз-контроль/пробел. Кстати я так и не могу понять почему «круиз-контроль».
          • 0
            Автоприцел/круиз-контроль/пробел.

            Верный путь к проигрышу.
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Поворот корпуса крестовина, движение она же или «Y» вперед и «A» назад, стики настроены левый для медленного движения мышью (точное прицеливание), правый стик обзорность быстрое прицеливание. Стики то работают в двух плоскостях и ничем от мыши не отличаются.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Стик на наклон реагирует так же, как и мышь на скорость движения по столу. Кстати, я давно подумываю отказаться от «точного прицеливания» на левом стике, в пользу клавиш WASD. К тому же настройки в игре позволяют менять чувствительность мыши в обычном и снайперском режимах.
          Вам спасибо! Ваш комментарий дополнительно подтолкнул к этому решению — буду искать «золотую середину» в настройках игры и Xpadder.
          • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Если приноровиться, то на нормальном трекболе намного удобнее играть за арту, чем обычной (лазерной) мышкой.
  • 0
    То что вы это реализовали — это хорошо, это вы молодец, правда.
    Но вот по ряду причин очень не хотелось-бы оказаться с вами в одной команде в принципе, и уж когда вы на джойстике играете — тем-более, уж извините.
    • 0
      В командных боях все намного лучше рандома, можете не верить.
      • 0
        В КБ на ис-3 или 50-100 или 13-90 на джойстике?
        Так вы в команде будете слабым звеном, неужто вам невдомек?..
        • 0
          ИС-3 как раз любимый мой в КБ :) 13-90 в планах.
  • 0
    Кто же на Фьордах-то на СТ занимает верх, когда нужно было ехать на B7-B8, а так фланг слили :)
    Вообще, в целом это извращение. 49% статы — из-за игры на геймпаде? Исходя из ролика, при быстром геймплее, очень неудобно стрелять, передвигаться, и выцеливать.
    • 0
      Приятно, что досмотрели видео;) Повторюсь, что видео писалось с первого раза просто «для галочки», а про 49% статистики это ничего — подтянем.
      • 0
        на джойстике — никогда.
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Кстати говоря, у квг танки на коробку — унылые, и ужасные. Никогда не стал бы играть в них в том виде, в котором они есть сейчас, по крайней мере.
      • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Поддерживаю — унылые и ужасные.
    • 0
      А я так рассчитывал на нагиб консольщиков в общих боях…
  • 0
    А может попробовать сделать руль, педали и мышка?

    Давно хотел так сделать:
    управление танком — левая рука (руль) и нога (газ/тормоз)
    Управление прицелом — правая рука (мышка).

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

    Сам играю на 40'' ТВ, на 1920 невозможно играть на 1600 очень даже нравится.
    • 0
      Нет руля у меня с отдельным блоком газ/тормоз для ноги, есть только вот такой бублик, с ним будет не комфортно.
  • 0
    Готовый мод, который берет данные для вибраций из файлов для вибронакидки .uwv, с помощью коэффициентов преобразовывает их и передает данные для двигателей xinput геймпада. Работают смешивание (одновременное воспроизведение нескольких эффектов), нет никаких задержек (все вибрации отрабатываются вовремя), я понял почему разработчики хвалятся тем, что эффекты делал звукорежиссер (пожар только чего стоит — как биение сердца), работают «ползунки» в настройках игры для вибронакидки (можно увеличить вибрации/уменьшить/отключить), настройки для передачи вибраций в геймпад вынесены в отдельный xml файл.

    Теперь по порядку:
    1. Необходимо установить Python версий 2.6.х (у меня стоит 2.6.1), в принципе можно перекомпилировать файлы под любую версию.
    2. Установить Flask, качаем файл distribute_setup.py в папку «C:\temp» (например), запускаем консоль «Win+R» — cmd и выполняем команды:
    C:\Python26\python.exe C:\temp\distribute_setup.py
    C:\Python26\python.exe C:\Python26\Scripts\easy_install-2.6-script.py Flask
    C:\Python26\python.exe C:\Python26\Scripts\easy_install-2.6-script.py Jinja2
    C:\Python26\python.exe C:\Python26\Scripts\easy_install-2.6-script.py Werkzeug
    C:\Python26\python.exe C:\Python26\Scripts\easy_install-2.6-script.py Virtualenv
    3. Распаковать обновленный мод в нужную папку.
    4. Играть.

    В файле GPsettings.xml уже прописаны путь к системному python «c:/python26/pythonw.exe» со скрытым запуском, коэффициенты передачи эффекта, нужная dll ( для Windows 7/Vista), для XP нужно прописать xinput1_1, для 8-ки — XInput9_1_0.
    • 0
      Полностью поддерживаю данное направление на официальном форуме. Ссылка есть снизу поста.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.