Jump to content
Korean Random

kharlashkin

User
  • Posts

    829
  • Joined

  • Last visited

  • Days Won

    7

Posts posted by kharlashkin

  1. А что я нарушаю? Это заказ для программистов.Я готов заплатить и работать в дальнейшем. "Подставные бои/роты/прочее"  На раз два три..... тоже запретить?

    Я разве сказал, что Вы что-то нарушаете? По моему мнению нет. Но мало информации в запросе дают "полет для мыслей", а тема Ваша в запросе -  наподобие этой. В которой нелестно отзывались сами разработчики модов. Например в моей теме я конкретно указываю что мне нужно и пытаюсь всеми силами помочь. Описывайте все что Вам надо детальней и много сэкономите времени всем.

     

    Не ужели трудно в личке у автора приведенной мною темы спросить что как и к чему сначала - ему ведь кто-то сделал нужный Вам мод, взвесить все "за" и "против". И если уже не найдете ничего - тогда заводить отдельный топик ведь 1,5k рублей - недорого.

     

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

     

    Я прошу прощения - если вдруг я что-то написал обидчивое для Вас.

    • Upvote 2
  2. На просторах интернета нашел вот такую вещь.

    Multiзапуск

    Если не внимательно читали, то проблем с запуском двух клиентов WoT у меня нет. Но все равно спасибо!

     

    Кстати, пришел к одному выводу - можно попробовать сделать это все "малой кровью". Относительно вывода двух окон было сообщение в самом начале (цена вопроса 35$), все управление вторым танком можно повесить на клавиатуру (без мыши), кнопок на клавиатуре больше чем в игре используется - хватит, Xpadder поддерживает одновременно много геймпадов, мод вибрации перепишу специально для второго экземпляра игры - второй геймпад.

     

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

    • Upvote 1
    • Downvote 1
  3. Скрипт SettingsInterface интересный, там все есть - обработка нажатий кнопок, настройки мыши. Публикую его полностью:

    'SettingsInterface'

    import sys
    from functools import partial
    import BigWorld
    import itertools
    import SoundGroups
    import ResMgr
    import ArenaType
    from gui.Scaleform.daapi.view.lobby.settings.options import APPLY_METHOD
    from helpers import i18n
    from gui.Scaleform.managers.windows_stored_data import g_windowsStoredData
    import nations
    from account_helpers import gameplay_ctx
    from account_helpers.SettingsCore import g_settingsCore
    from gui import GUI_SETTINGS, g_guiResetters
    from gui.BattleContext import g_battleContext
    from gui.GraphicsPresets import GraphicsPresets
    from gui.GraphicsResolutions import g_graficsResolutions
    from gui.shared.utils.key_mapping import getScaleformKey
    from gui.Scaleform import VoiceChatInterface
    from windows import UIInterface
    from debug_utils import LOG_DEBUG, LOG_NOTE, LOG_ERROR
    from account_helpers.AccountSettings import AccountSettings
    from post_processing import g_postProcessing
    import CommandMapping
    import Settings
    from adisp import process
    from gui.Scaleform.Waiting import Waiting
    from Vibroeffects import VibroManager
    from LogitechMonitor import LogitechMonitor
    from helpers import getClientOverride
    
    class SettingsInterface(UIInterface):
        KEYBOARD_MAPPING_COMMANDS = {'movement': {'forward': 'CMD_MOVE_FORWARD',
                      'backward': 'CMD_MOVE_BACKWARD',
                      'left': 'CMD_ROTATE_LEFT',
                      'right': 'CMD_ROTATE_RIGHT',
                      'auto_rotation': 'CMD_CM_VEHICLE_SWITCH_AUTOROTATION'},
         'cruis_control': {'forward': 'CMD_INCREMENT_CRUISE_MODE',
                           'backward': 'CMD_DECREMENT_CRUISE_MODE',
                           'stop_fire': 'CMD_STOP_UNTIL_FIRE'},
         'firing': {'fire': 'CMD_CM_SHOOT',
                    'lock_target': 'CMD_CM_LOCK_TARGET',
                    'lock_target_off': 'CMD_CM_LOCK_TARGET_OFF',
                    'alternate_mode': 'CMD_CM_ALTERNATE_MODE',
                    'reloadPartialClip': 'CMD_RELOAD_PARTIAL_CLIP'},
         'vehicle_other': {'showHUD': 'CMD_TOGGLE_GUI',
                           'showRadialMenu': 'CMD_RADIAL_MENU_SHOW'},
         'equipment': {'item01': 'CMD_AMMO_CHOICE_1',
                       'item02': 'CMD_AMMO_CHOICE_2',
                       'item03': 'CMD_AMMO_CHOICE_3',
                       'item04': 'CMD_AMMO_CHOICE_4',
                       'item05': 'CMD_AMMO_CHOICE_5',
                       'item06': 'CMD_AMMO_CHOICE_6',
                       'item07': 'CMD_AMMO_CHOICE_7',
                       'item08': 'CMD_AMMO_CHOICE_8'},
         'shortcuts': {'attack': 'CMD_CHAT_SHORTCUT_ATTACK',
                       'to_base': 'CMD_CHAT_SHORTCUT_BACKTOBASE',
                       'positive': 'CMD_CHAT_SHORTCUT_POSITIVE',
                       'negative': 'CMD_CHAT_SHORTCUT_NEGATIVE',
                       'help_me': 'CMD_CHAT_SHORTCUT_HELPME',
                       'reload': 'CMD_CHAT_SHORTCUT_RELOAD'},
         'camera': {'camera_up': 'CMD_CM_CAMERA_ROTATE_UP',
                    'camera_down': 'CMD_CM_CAMERA_ROTATE_DOWN',
                    'camera_left': 'CMD_CM_CAMERA_ROTATE_LEFT',
                    'camera_right': 'CMD_CM_CAMERA_ROTATE_RIGHT'},
         'voicechat': {'pushToTalk': 'CMD_VOICECHAT_MUTE'},
         'logitech_keyboard': {'switch_view': 'CMD_LOGITECH_SWITCH_VIEW'},
         'minimap': {'sizeUp': 'CMD_MINIMAP_SIZE_UP',
                     'sizeDown': 'CMD_MINIMAP_SIZE_DOWN',
                     'visible': 'CMD_MINIMAP_VISIBLE'}}
        KEYBOARD_MAPPING_BLOCKS = {'movement': ('forward', 'backward', 'left', 'right', 'auto_rotation'),
         'cruis_control': ('forward', 'backward', 'stop_fire'),
         'firing': ('fire', 'lock_target', 'lock_target_off', 'alternate_mode', 'reloadPartialClip'),
         'vehicle_other': ('showHUD', 'showRadialMenu'),
         'equipment': ('item01', 'item02', 'item03', 'item04', 'item05', 'item06', 'item07', 'item08'),
         'shortcuts': ('attack', 'to_base', 'positive', 'negative', 'help_me', 'reload'),
         'camera': ('camera_up', 'camera_down', 'camera_left', 'camera_right'),
         'voicechat': ('pushToTalk',),
         'logitech_keyboard': ('switch_view',),
         'minimap': ('sizeUp', 'sizeDown', 'visible')}
        KEYBOARD_MAPPING_BLOCKS_ORDER = ('movement', 'cruis_control', 'firing', 'vehicle_other', 'equipment', 'shortcuts', 'camera', 'voicechat', 'minimap', 'logitech_keyboard')
        POPULATE_UI = 'SettingsDialog.PopulateUI'
        APPLY_SETTINGS = 'SettingsDialog.ApplySettings'
        COMMIT_SETTINGS = 'SettingsDialog.CommitSettings'
        DELAY_SETTINGS = 'SettingsDialog.DelaySettings'
        AUTODETECT_QUALITY = 'SettingsDialog.AutodetectQuality'
        CURSOR_VALUES = {'mixing': 3,
         'gunTag': 15,
         'centralTag': 14,
         'net': 4,
         'reloader': 0,
         'condition': 0,
         'cassette': 0,
         'reloaderTimer': 0}
        SNIPER_VALUES = {'snpMixing': 3,
         'snpGunTag': 6,
         'snpCentralTag': 14,
         'snpNet': 4,
         'snpReloader': 0,
         'snpCondition': 0,
         'snpCassette': 0,
         'snpReloaderTimer': 0}
        MARKER_VALUES = {'Hp': 4,
         'Name': 3}
        MARKER_TYPES = ['Base', 'Alt']
        MOUSE_KEYS = {'ingame': {'arcadeSens': ('arcade', 'sensitivity'),
                    'sniperSens': ('sniper', 'sensitivity'),
                    'artSens': ('strategic', 'sensitivity'),
                    'horInvert': ('arcade', 'horzInvert'),
                    'vertInvert': ('arcade', 'vertInvert'),
                    'backDraftInvert': ('arcade', 'backDraftInvert')},
         'lobby': {'arcadeSens': ('arcadeMode/camera', 'sensitivity', 'float'),
                   'sniperSens': ('sniperMode/camera', 'sensitivity', 'float'),
                   'artSens': ('strategicMode/camera', 'sensitivity', 'float'),
                   'horInvert': ('arcadeMode/camera', 'horzInvert', 'bool'),
                   'vertInvert': ('arcadeMode/camera', 'vertInvert', 'bool'),
                   'backDraftInvert': ('arcadeMode/camera', 'backDraftInvert', 'bool')},
         'default': {'arcadeSens': 1,
                     'sniperSens': 1,
                     'artSens': 1,
                     'horInvert': False,
                     'vertInvert': False,
                     'backDraftInvert': False}}
        GAMEPLAY_KEY_FORMAT = 'gameplay_{0:>s}'
        GAMEPLAY_PREFIX = 'gameplay_'
    
        def __init__(self, enableRedefineKeysMode = True):
            UIInterface.__init__(self)
            if not GUI_SETTINGS.minimapSize and self.KEYBOARD_MAPPING_BLOCKS.has_key('minimap'):
                del self.KEYBOARD_MAPPING_BLOCKS['minimap']
            self.__enableRedefineKeysMode = enableRedefineKeysMode
            self.graphicsPresets = GraphicsPresets()
            self.resolutions = g_graficsResolutions
            self.__currentSettings = None
            self.__settingsUI = None
            self.__altVoiceSetting = g_settingsCore.options.getSetting('alternativeVoices')
            if not GUI_SETTINGS.voiceChat and self.KEYBOARD_MAPPING_COMMANDS.has_key('voicechat'):
                if self.KEYBOARD_MAPPING_COMMANDS.has_key('voicechat'):
                    del self.KEYBOARD_MAPPING_COMMANDS['voicechat']
                if self.KEYBOARD_MAPPING_BLOCKS.has_key('voicechat'):
                    del self.KEYBOARD_MAPPING_BLOCKS['voicechat']
                self.KEYBOARD_MAPPING_BLOCKS_ORDER = list(self.KEYBOARD_MAPPING_BLOCKS_ORDER)
                del self.KEYBOARD_MAPPING_BLOCKS_ORDER[self.KEYBOARD_MAPPING_BLOCKS_ORDER.index('voicechat')]
                self.KEYBOARD_MAPPING_BLOCKS_ORDER = tuple(self.KEYBOARD_MAPPING_BLOCKS_ORDER)
            return
    
        def populateUI(self, proxy):
            UIInterface.populateUI(self, proxy)
            self.uiHolder.addExternalCallbacks({SettingsInterface.POPULATE_UI: self.onPopulateUI,
             SettingsInterface.APPLY_SETTINGS: self.onApplySettings,
             SettingsInterface.COMMIT_SETTINGS: self.onCommitSettings,
             SettingsInterface.DELAY_SETTINGS: self.onDelaySettings,
             SettingsInterface.AUTODETECT_QUALITY: self.onAutodetectSettings,
             'SettingsDialog.useRedifineKeysMode': self.onUseRedifineKeyMode,
             'SettingsDialog.processVivoxTest': self.onProcessVivoxTest,
             'SettingsDialog.voiceChatEnable': self.onVoiceChatEnable,
             'SettingsDialog.updateCaptureDevices': self.onUpdateCaptureDevices,
             'SettingsDialog.setVivoxMicVolume': self.onSetVivoxMicVolume,
             'SettingsDialog.killDialog': self.onDialogClose})
            VibroManager.g_instance.onConnect += self.__vm_onConnect
            VibroManager.g_instance.onDisconnect += self.__vm_onDisconnect
            g_guiResetters.add(self.onRecreateDevice)
            BigWorld.wg_setAdapterOrdinalNotifyCallback(self.onRecreateDevice)
    
        def dispossessUI(self):
            if self.__settingsUI:
                self.__settingsUI.script = None
                self.__settingsUI = None
            self.__altVoiceSetting = None
            self.uiHolder.removeExternalCallbacks(SettingsInterface.POPULATE_UI, SettingsInterface.APPLY_SETTINGS, SettingsInterface.COMMIT_SETTINGS, SettingsInterface.DELAY_SETTINGS, SettingsInterface.AUTODETECT_QUALITY, 'SettingsDialog.useRedifineKeysMode', 'SettingsDialog.processVivoxTest', 'SettingsDialog.voiceChatEnable', 'SettingsDialog.updateCaptureDevices', 'SettingsDialog.setVivoxMicVolume', 'SettingsDialog.killDialog')
            VibroManager.g_instance.onConnect -= self.__vm_onConnect
            VibroManager.g_instance.onDisconnect -= self.__vm_onDisconnect
            g_guiResetters.discard(self.onRecreateDevice)
            BigWorld.wg_setAdapterOrdinalNotifyCallback(None)
            UIInterface.dispossessUI(self)
            return
    
        def altVoicesPreview(self, soundMode):
            if not self.__altVoiceSetting.isOptionEnabled():
                return True
            if not g_battleContext.isInBattle:
                SoundGroups.g_instance.enableVoiceSounds(True)
            self.__altVoiceSetting.preview(soundMode)
            return self.__altVoiceSetting.playPreviewSound(self.uiHolder.soundManager)
    
        def isSoundModeValid(self, soundMode):
            self.__altVoiceSetting.preview(soundMode)
            valid = self.__altVoiceSetting.isSoundModeValid()
            self.__altVoiceSetting.revert()
            return valid
    
        def onSetVivoxMicVolume(self, callbackId, value):
            import VOIP
            if round(SoundGroups.g_instance.getVolume('micVivox') * 100) != value:
                SoundGroups.g_instance.setVolume('micVivox', value / 100)
                VOIP.getVOIPManager().setMicrophoneVolume(int(value))
    
        def onVoiceChatEnable(self, callbackId, isEnable):
            self.__voiceChatEnable(isEnable)
    
        def __voiceChatEnable(self, isEnable):
            if isEnable is None:
                return
            else:
                preveVoIP = Settings.g_instance.userPrefs.readBool(Settings.KEY_ENABLE_VOIP)
                import VOIP
                if preveVoIP != isEnable:
                    VOIP.getVOIPManager().enable(isEnable)
                    Settings.g_instance.userPrefs.writeBool(Settings.KEY_ENABLE_VOIP, bool(isEnable))
                    from gui.WindowsManager import g_windowsManager
                    if g_windowsManager.battleWindow is not None and not isEnable:
                        g_windowsManager.battleWindow.speakingPlayersReset()
                    LOG_NOTE('Change state of voip: %s' % str(isEnable))
                return
    
        def __changeCaptureDevice(self, captureDeviceIdx):
            if captureDeviceIdx is None or captureDeviceIdx == -1:
                return
            else:
                import VOIP
                rh = VOIP.getVOIPManager()
                devices = [ i18n.encodeUtf8(device.decode(sys.getfilesystemencoding())) for device in rh.captureDevices ]
                if captureDeviceIdx < len(devices):
                    newCaptureDevice = devices[captureDeviceIdx]
                    previousDevice = Settings.g_instance.userPrefs.readString(Settings.KEY_VOIP_DEVICE)
                    if previousDevice != newCaptureDevice:
                        Settings.g_instance.userPrefs.writeString(Settings.KEY_VOIP_DEVICE, newCaptureDevice)
                        LOG_NOTE('Change device of voip: %s' % str(newCaptureDevice))
                return
    
        def onUseRedifineKeyMode(self, callbackId, isUse):
            if self.__enableRedefineKeysMode:
                BigWorld.wg_setRedefineKeysMode(isUse)
    
        def onProcessVivoxTest(self, callbackId, isStart):
            LOG_DEBUG('Vivox test: %s' % str(isStart))
            import VOIP
            rh = VOIP.getVOIPManager()
            rh.enterTestChannel() if isStart else rh.leaveTestChannel()
            self.respond([callbackId, False])
    
        def __vm_onConnect(self):
            self.call('SettingsDialog.VibroManager.Connect')
    
        def __vm_onDisconnect(self):
            self.call('SettingsDialog.VibroManager.Disconnect')
    
        def __getVideoSettings(self):
            settings = {}
            settings['monitor'] = {'current': self.resolutions.monitorIndex,
             'real': self.resolutions.realMonitorIndex,
             'options': self.resolutions.monitorsList}
            settings['fullScreen'] = not self.resolutions.isVideoWindowed
            settings['windowSize'] = {'current': self.resolutions.windowSizeIndex,
             'options': self.resolutions.windowSizesList}
            settings['resolution'] = {'current': self.resolutions.videoModeIndex,
             'options': self.resolutions.videoModesList}
            return settings
    
        def __getSettings(self):
            settings = [self.graphicsPresets.getGraphicsPresetsData()]
            import VOIP
            rh = VOIP.getVOIPManager()
            g_windowsStoredData.start()
            vManager = VibroManager.g_instance
            vEffGroups = vManager.getGroupsSettings()
            vEffDefGroup = VibroManager.VibroManager.GroupSettings()
            vEffDefGroup.enabled = False
            vEffDefGroup.gain = 0
            markers = {'enemy': g_settingsCore.getSetting('enemy'),
             'dead': g_settingsCore.getSetting('dead'),
             'ally': g_settingsCore.getSetting('ally')}
            datetimeIdx = g_settingsCore.getSetting('showDateMessage') << 0 | g_settingsCore.getSetting('showTimeMessage') << 1
            config = {'locale': getClientOverride(),
             'aspectRatio': {'current': self.resolutions.aspectRatioIndex,
                             'options': self.resolutions.aspectRatiosList},
             'vertSync': self.resolutions.isVideoVSync,
             'tripleBuffered': self.resolutions.isTripleBuffered,
             'multisampling': {'current': self.resolutions.multisamplingTypeIndex,
                               'options': self.resolutions.multisamplingTypesList},
             'customAA': {'current': self.resolutions.customAAModeIndex,
                          'options': self.resolutions.customAAModesList},
             'gamma': self.resolutions.gamma,
             'masterVolume': round(SoundGroups.g_instance.getMasterVolume() * 100),
             'musicVolume': round(SoundGroups.g_instance.getVolume('music') * 100),
             'voiceVolume': round(SoundGroups.g_instance.getVolume('voice') * 100),
             'vehiclesVolume': round(SoundGroups.g_instance.getVolume('vehicles') * 100),
             'effectsVolume': round(SoundGroups.g_instance.getVolume('effects') * 100),
             'guiVolume': round(SoundGroups.g_instance.getVolume('gui') * 100),
             'ambientVolume': round(SoundGroups.g_instance.getVolume('ambient') * 100),
             'masterVivoxVolume': round(SoundGroups.g_instance.getVolume('masterVivox') * 100),
             'micVivoxVolume': round(SoundGroups.g_instance.getVolume('micVivox') * 100),
             'masterFadeVivoxVolume': round(SoundGroups.g_instance.getVolume('masterFadeVivox') * 100),
             'captureDevice': self.__getCaptureDeviceSettings(),
             'voiceChatNotSupported': rh.vivoxDomain == '' or not VoiceChatInterface.g_instance.ready,
             'datetimeIdx': datetimeIdx,
             'enableOlFilter': g_settingsCore.getSetting('enableOlFilter'),
             'enableSpamFilter': g_settingsCore.getSetting('enableSpamFilter'),
             'enableStoreChatMws': g_settingsCore.getSetting('enableStoreMws'),
             'enableStoreChatCws': g_settingsCore.getSetting('enableStoreCws'),
             'invitesFromFriendsOnly': g_settingsCore.getSetting('invitesFromFriendsOnly'),
             'storeReceiverInBattle': g_settingsCore.getSetting('storeReceiverInBattle'),
             'disableBattleChat': g_settingsCore.getSetting('disableBattleChat'),
             'dynamicCamera': g_settingsCore.getSetting('dynamicCamera'),
             'horStabilizationSnp': g_settingsCore.getSetting('horStabilizationSnp'),
             'enableVoIP': VOIP.getVOIPManager().channelsMgr.enabled,
             'enablePostMortemEffect': g_settingsCore.getSetting('enablePostMortemEffect'),
             'enablePostMortemDelay': g_settingsCore.getSetting('enablePostMortemDelay'),
             'nationalVoices': AccountSettings.getSettings('nationalVoices'),
             'isColorBlind': AccountSettings.getSettings('isColorBlind'),
             'useServerAim': g_settingsCore.getSetting('useServerAim'),
             'showVehiclesCounter': g_settingsCore.getSetting('showVehiclesCounter'),
             'minimapAlpha': g_settingsCore.getSetting('minimapAlpha'),
             'vibroIsConnected': vManager.connect(),
             'vibroGain': vManager.getGain() * 100,
             'vibroEngine': vEffGroups.get('engine', vEffDefGroup).gain * 100,
             'vibroAcceleration': vEffGroups.get('acceleration', vEffDefGroup).gain * 100,
             'vibroShots': vEffGroups.get('shots', vEffDefGroup).gain * 100,
             'vibroHits': vEffGroups.get('hits', vEffDefGroup).gain * 100,
             'vibroCollisions': vEffGroups.get('collisions', vEffDefGroup).gain * 100,
             'vibroDamage': vEffGroups.get('damage', vEffDefGroup).gain * 100,
             'vibroGUI': vEffGroups.get('gui', vEffDefGroup).gain * 100,
             'ppShowLevels': g_settingsCore.getSetting('ppShowLevels'),
             'ppShowTypes': AccountSettings.getSettings('players_panel')['showTypes'],
             'replayEnabled': g_settingsCore.options.getSetting('replayEnabled').pack(),
             'fpsPerfomancer': g_settingsCore.getSetting('fpsPerfomancer'),
             'arcade': {'values': g_settingsCore.options.getSetting('arcade').toAccountSettings(),
                        'options': SettingsInterface.CURSOR_VALUES},
             'sniper': {'values': g_settingsCore.options.getSetting('sniper').toAccountSettings(),
                        'options': SettingsInterface.SNIPER_VALUES},
             'markers': {'values': markers,
                         'options': SettingsInterface.MARKER_VALUES,
                         'types': SettingsInterface.MARKER_TYPES}}
            if self.__altVoiceSetting.isOptionEnabled():
                altVoices = []
                for idx, desc in enumerate(self.__altVoiceSetting.getOptions()):
                    altVoices.append({'data': idx,
                     'label': desc})
    
                config['alternativeVoices'] = {'current': self.__altVoiceSetting.get(),
                 'options': altVoices}
            for name in ('ctf', 'domination', 'assault', 'nations'):
                key = self.GAMEPLAY_KEY_FORMAT.format(name)
                config[key] = g_settingsCore.getSetting(key)
    
            settings.append(config)
            if not LogitechMonitor.isPresentColor():
                if self.KEYBOARD_MAPPING_BLOCKS.has_key('logitech_keyboard'):
                    del self.KEYBOARD_MAPPING_BLOCKS['logitech_keyboard']
            else:
                self.KEYBOARD_MAPPING_BLOCKS['logitech_keyboard'] = ('switch_view',)
            cmdMap = CommandMapping.g_instance
            defaults = cmdMap.getDefaults()
            keyboard = []
            for group_name in self.KEYBOARD_MAPPING_BLOCKS_ORDER:
                if group_name in self.KEYBOARD_MAPPING_BLOCKS.keys():
                    group = {'id': group_name,
                     'commands': []}
                    keyboard.append(group)
                    for key_setting in self.KEYBOARD_MAPPING_BLOCKS[group_name]:
                        command = cmdMap.getCommand(self.KEYBOARD_MAPPING_COMMANDS[group_name][key_setting])
                        keyCode = cmdMap.get(self.KEYBOARD_MAPPING_COMMANDS[group_name][key_setting])
                        defaultCode = defaults[command] if defaults.has_key(command) else 0
                        key = {'id': key_setting,
                         'command': command,
                         'key': getScaleformKey(keyCode),
                         'keyDefault': getScaleformKey(defaultCode)}
                        group['commands'].append(key)
    
            settings.append(keyboard)
            mouse = {}
            player = BigWorld.player()
            if hasattr(player.inputHandler, 'ctrls'):
                for key, path in SettingsInterface.MOUSE_KEYS['ingame'].items():
                    if key == 'horInvert':
                        value = g_settingsCore.getSetting('mouseHorzInvert')
                    elif key == 'vertInvert':
                        value = g_settingsCore.getSetting('mouseVertInvert')
                    elif key == 'backDraftInvert':
                        value = g_settingsCore.getSetting('backDraftInvert')
                    else:
                        value = player.inputHandler.ctrls[path[0]].camera.getUserConfigValue(path[1])
                    mouse[key] = {'defaultValue': SettingsInterface.MOUSE_KEYS['default'][key],
                     'value': value}
    
            else:
                ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE]
                for key, path in SettingsInterface.MOUSE_KEYS['lobby'].items():
                    default = SettingsInterface.MOUSE_KEYS['default'][key]
                    value = default
                    if key == 'horInvert':
                        value = g_settingsCore.getSetting('mouseHorzInvert')
                    elif key == 'vertInvert':
                        value = g_settingsCore.getSetting('mouseVertInvert')
                    elif key == 'backDraftInvert':
                        value = g_settingsCore.getSetting('backDraftInvert')
                    elif ds is not None:
                        if path[2] == 'float':
                            value = ds[path[0]].readFloat(path[1], default)
                        elif path[2] == 'bool':
                            value = ds[path[0]].readBool(path[1], default)
                        else:
                            LOG_DEBUG('Unknown mouse settings type %s %s' % (key, path))
                    mouse[key] = {'defaultValue': default,
                     'value': value}
    
            settings.append(mouse)
            g_windowsStoredData.stop()
            return settings
    
        def __getCaptureDeviceSettings(self):
            import VOIP
            rh = VOIP.getVOIPManager()
            devices = [ i18n.encodeUtf8(device.decode(sys.getfilesystemencoding())) for device in rh.captureDevices ]
            currentDeviceName = Settings.g_instance.userPrefs.readString(Settings.KEY_VOIP_DEVICE)
            currentCaptureDeviceIdx = -1
            try:
                currentCaptureDeviceIdx = devices.index(currentDeviceName)
            except:
                try:
                    currentCaptureDeviceIdx = rh.captureDevices.index(rh.currentCaptureDevice)
                except:
                    pass
    
            settings = {'current': currentCaptureDeviceIdx,
             'options': devices}
            return settings
    
        def onUpdateCaptureDevices(self, callbackId):
            self.__updateCaptureDevices()
    
        @process
        def __updateCaptureDevices(self):
            Waiting.show('__updateCaptureDevices')
            devices = yield VoiceChatInterface.g_instance.requestCaptureDevices()
            currentDeviceName = Settings.g_instance.userPrefs.readString(Settings.KEY_VOIP_DEVICE)
            currentCaptureDeviceIdx = -1
            try:
                currentCaptureDeviceIdx = devices.index(currentDeviceName)
            except Exception:
                try:
                    import VOIP
                    currentCaptureDeviceIdx = devices.index(VOIP.getVOIPManager().currentCaptureDevice)
                except Exception:
                    pass
    
            value = [currentCaptureDeviceIdx]
            value.extend([ i18n.encodeUtf8(d.decode(sys.getfilesystemencoding())) for d in devices ])
            Waiting.hide('__updateCaptureDevices')
            self.call('SettingsDialog.updateCaptureDevices', value)
    
        def onRecreateDevice(self):
            if self.__settingsUI:
                if self.__currentSettings and self.__currentSettings != self.__getVideoSettings():
                    self.__currentSettings = self.__getVideoSettings()
                    self.__settingsUI.buildGraphicsData(self.__getVideoSettings())
    
        def onAutodetectSettings(self, callbackID):
            presetIndex = BigWorld.autoDetectGraphicsSettings()
            self.call('SettingsDialog.setPreset', [presetIndex])
    
        def onPopulateUI(self, *args):
            self.graphicsPresets.checkCurrentPreset(True)
            self.__currentSettings = self.__getVideoSettings()
            VoiceChatInterface.g_instance.processFailedMessage()
            if self.__settingsUI:
                self.__settingsUI.script = None
                self.__settingsUI = None
            settingsDialogName = args[1]
            self.__settingsUI = self.uiHolder.getMember(settingsDialogName)
            if self.__settingsUI:
                settings = self.__getSettings()
                self.__settingsUI.buildData(settings[0], settings[1], settings[2], settings[3])
                self.__settingsUI.buildGraphicsData(self.__getVideoSettings())
                self.__settingsUI.script = self
            else:
                LOG_ERROR('settingsDialog is not found in flash by name {0}'.format(settingsDialogName))
            return
    
        def onApplySettings(self, callbackId, settings):
            monitorIndex, presetIndex, settingsList, fullscreen = settings
            if (not self.resolutions.isVideoWindowed or fullscreen) and (monitorIndex != self.resolutions.realMonitorIndex or self.resolutions.monitorChanged):
                self.call('SettingsDialog.ApplySettings', ['restartNeeded'])
                return
            applyMethod = g_settingsCore.options.getApplyMethod(settingsList)
            method = 'apply'
            if applyMethod == APPLY_METHOD.RESTART:
                method = 'restartNeeded'
            elif applyMethod == APPLY_METHOD.DELAYED:
                method = 'hasPendingSettings'
            self.call('SettingsDialog.ApplySettings', [method])
    
        def onDelaySettings(self, *args):
            self.apply(False, *args)
    
        def onCommitSettings(self, *args):
            self.apply(True, *args)
    
        def apply(self, restartApproved, callbackId, settings):
            restartClient = False
            import VOIP
            ppSettings = dict(AccountSettings.getSettings('players_panel'))
            ppSettings['showTypes'] = settings['ppShowTypes']
            if (not self.resolutions.isVideoWindowed or settings['fullScreen']) and (settings['monitor'] != self.resolutions.realMonitorIndex or self.resolutions.monitorChanged):
                restartClient = True
            AccountSettings.setSettings('players_panel', ppSettings)
            g_settingsCore.applySetting('ppShowLevels', settings['ppShowLevels'])
            g_settingsCore.applySetting('replayEnabled', settings['replayEnabled'])
            g_settingsCore.applySetting('fpsPerfomancer', settings['fpsPerfomancer'])
            AccountSettings.setSettings('nationalVoices', settings['nationalVoices'])
            AccountSettings.setSettings('isColorBlind', settings['isColorBlind'])
            g_settingsCore.applySetting('useServerAim', settings['useServerAim'])
            g_settingsCore.applySetting('showVehiclesCounter', settings['showVehiclesCounter'])
            g_settingsCore.applySetting('minimapAlpha', settings['minimapAlpha'])
            arcade = g_settingsCore.options.getSetting('arcade').fromAccountSettings(settings['arcade'])
            sniper = g_settingsCore.options.getSetting('sniper').fromAccountSettings(settings['sniper'])
            g_settingsCore.applySetting('arcade', arcade)
            g_settingsCore.applySetting('sniper', sniper)
            g_settingsCore.applySetting('enemy', settings['markers']['enemy'])
            g_settingsCore.applySetting('dead', settings['markers']['dead'])
            g_settingsCore.applySetting('ally', settings['markers']['ally'])
            g_settingsCore.applySetting('dynamicCamera', settings['dynamicCamera'])
            g_settingsCore.applySetting('horStabilizationSnp', settings['horStabilizationSnp'])
            if self.__altVoiceSetting.isOptionEnabled():
                altVoices = settings.get('alternativeVoices')
                if altVoices is not None:
                    self.__altVoiceSetting.apply(altVoices)
            vManager = VibroManager.g_instance
            vManager.setGain(settings['vibroGain'] / 100.0)
            vEffGroups = vManager.getGroupsSettings()
            for groupName, newValue in [('engine', settings['vibroEngine']),
             ('acceleration', settings['vibroAcceleration']),
             ('shots', settings['vibroShots']),
             ('hits', settings['vibroHits']),
             ('collisions', settings['vibroCollisions']),
             ('damage', settings['vibroDamage']),
             ('gui', settings['vibroGUI'])]:
                if groupName in vEffGroups:
                    vEffGroups[groupName].gain = newValue / 100.0
                    vEffGroups[groupName].enabled = newValue > 0
    
            vManager.setGroupsSettings(vEffGroups)
            self.__voiceChatEnable(settings['enableVoIP'])
            self.__changeCaptureDevice(settings[Settings.KEY_VOIP_DEVICE])
            g_settingsCore.applySetting('enablePostMortemEffect', settings['enablePostMortemEffect'])
            g_settingsCore.applySetting('enablePostMortemDelay', settings['enablePostMortemDelay'])
            self.uiHolder.clearCommands()
            keyboard = settings['controls']['keyboard']
            keyboardMapping = {}
            keysLayout = dict(g_settingsCore.options.getSetting('keyboard').KEYS_LAYOUT)
            layout = list(itertools.chain(*keysLayout.values()))
            for i in xrange(len(self.KEYBOARD_MAPPING_BLOCKS)):
                group_name = keyboard[i]['id']
                for j in xrange(len(self.KEYBOARD_MAPPING_BLOCKS[group_name])):
                    key_name = keyboard[i]['commands'][j]['id']
                    value = keyboard[i]['commands'][j]['key']
                    cmd = self.KEYBOARD_MAPPING_COMMANDS[group_name][key_name]
                    for item in layout:
                        key, command = item[0], item[1]
                        if command == cmd:
                            keyboardMapping[key] = value
                            break
    
            g_settingsCore.applySetting('keyboard', keyboardMapping)
            self.uiHolder.bindCommands()
            player = BigWorld.player()
            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']))
            self.resolutions.applyChanges(settings['fullScreen'], settings['vertSync'], settings['tripleBuffered'], settings['windowSize'] if not settings['fullScreen'] else settings['resolution'], settings['aspectRatio'], settings['multisampling'], settings['customAA'], settings['gamma'], settings['monitor'])
            if round(SoundGroups.g_instance.getVolume('masterVivox') * 100) != settings['masterVivoxVolume']:
                VOIP.getVOIPManager().setMasterVolume(settings['masterVivoxVolume'])
            if round(SoundGroups.g_instance.getVolume('micVivox') * 100) != settings['micVivoxVolume']:
                VOIP.getVOIPManager().setMicrophoneVolume(settings['micVivoxVolume'])
            SoundGroups.g_instance.setMasterVolume(float(settings['masterVolume']) / 100)
            SoundGroups.g_instance.setVolume('music', float(settings['musicVolume']) / 100)
            SoundGroups.g_instance.setVolume('voice', float(settings['voiceVolume']) / 100)
            SoundGroups.g_instance.setVolume('vehicles', float(settings['vehiclesVolume']) / 100)
            SoundGroups.g_instance.setVolume('effects', float(settings['effectsVolume']) / 100)
            SoundGroups.g_instance.setVolume('gui', float(settings['guiVolume']) / 100)
            SoundGroups.g_instance.setVolume('ambient', float(settings['ambientVolume']) / 100)
            SoundGroups.g_instance.setVolume('masterVivox', float(settings['masterVivoxVolume']) / 100)
            SoundGroups.g_instance.setVolume('micVivox', float(settings['micVivoxVolume']) / 100)
            SoundGroups.g_instance.setVolume('masterFadeVivox', float(settings['masterFadeVivoxVolume']) / 100)
            if len(VOIP.getVOIPManager().captureDevices):
                device = VOIP.getVOIPManager().captureDevices[0]
                if len(VOIP.getVOIPManager().captureDevices) > settings['captureDevice']:
                    device = VOIP.getVOIPManager().captureDevices[settings['captureDevice']]
                VOIP.getVOIPManager().setCaptureDevice(device)
            g_settingsCore.applySetting('showDateMessage', settings['datetimeIdx'] & 1)
            g_settingsCore.applySetting('showTimeMessage', settings['datetimeIdx'] & 2)
            g_settingsCore.applySetting('enableOlFilter', settings['enableOlFilter'])
            g_settingsCore.applySetting('enableSpamFilter', settings['enableSpamFilter'])
            g_windowsStoredData.start()
            g_settingsCore.applySetting('enableStoreMws', settings['enableStoreChatMws'])
            g_settingsCore.applySetting('enableStoreCws', settings['enableStoreChatCws'])
            g_windowsStoredData.stop()
            g_settingsCore.applySetting('invitesFromFriendsOnly', settings['invitesFromFriendsOnly'])
            g_settingsCore.applySetting('storeReceiverInBattle', settings['storeReceiverInBattle'])
            g_settingsCore.applySetting('disableBattleChat', settings['disableBattleChat'])
            gameplayKeys = filter(lambda item: item.startswith(self.GAMEPLAY_PREFIX), settings.keys())
            for key in gameplayKeys:
                g_settingsCore.applySetting(key, settings[key])
    
            qualitySettings = settings['quality']
            applyMethod = g_settingsCore.options.getApplyMethod(qualitySettings)
            for key in GraphicsPresets.GRAPHICS_QUALITY_SETTINGS:
                value = qualitySettings.get(key)
                if value is not None:
                    g_settingsCore.applySetting(key, value)
    
            if applyMethod == APPLY_METHOD.RESTART:
                BigWorld.commitPendingGraphicsSettings()
                restartClient = True
            elif applyMethod == APPLY_METHOD.DELAYED:
                BigWorld.commitPendingGraphicsSettings()
            g_settingsCore.applyStorages()
            g_postProcessing.refresh()
            if restartClient:
                BigWorld.savePreferences()
                if restartApproved:
                    from BattleReplay import g_replayCtrl
                    if g_replayCtrl.isPlaying and g_replayCtrl.playbackSpeed == 0:
                        g_replayCtrl.setPlaybackSpeedIdx(5)
                    BigWorld.callback(0.3, BigWorld.restartGame)
                else:
                    BigWorld.callback(0.0, partial(BigWorld.changeVideoMode, -1, BigWorld.isVideoWindowed()))
            return
    
        def onDialogClose(self, _):
            if self.__altVoiceSetting.isOptionEnabled():
                self.__altVoiceSetting.revert()
            if not g_battleContext.isInBattle:
                SoundGroups.g_instance.enableVoiceSounds(False)
            elif hasattr(BigWorld.player(), 'vehicle'):
                SoundGroups.g_instance.soundModes.setCurrentNation(nations.NAMES[BigWorld.player().vehicle.typeDescriptor.type.id[0]])
            g_settingsCore.clearStorages()
            if self.__settingsUI:
                self.__settingsUI.script = None
                self.__settingsUI = None
            return 

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

    • Upvote 1
    • Downvote 1
  4. @kharlashkin, Эх, я б вам помог, если бы знал пайтон... Но если будет необходимость в exe'шках и dll'ках, то пишите мне, я помогу.

    Лучше поспрашивайте на форумах по пайтону. Я уверен, что среди програмистов должны быть любители танков.

    Спасибо за поддержку . В принципе, по логике вещей, ответ где-то рядом.

     

    Мое видение данного мода такое - таким же образом как в "вибромоде" вызываем системный python, с помощью pygame берем данные о нажатии кнопок/перемещении стиков, переназначаем их в соответствии с управлением в игре (я довольно долго экспериментировал с удобством игры на геймпаде - моя тема на оф.форуме), и отдаем команды непосредственно в игру. Вот с последним моментом пока проблема - ищу.

    • Upvote 2
    • Downvote 1
  5. @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.

    • Upvote 2
    • Downvote 1
  6. Нашел скрипт управления курсором в игре

    '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) 

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


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

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


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

    • Upvote 1
    • Downvote 1
  7. Просьба к форумчанам - кто нибудь подскажите, где лежат скрипты для клавиатуры/мыши в 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 отлавливает нажатия кнопок и перемещение стиков/триггеров и одновременно геймпад вибрирует.

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

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

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

     

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

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

     

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

     

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

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

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

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

    8dw5.jpg6e1d.jpg

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

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

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

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

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

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

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

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

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

    • Upvote 2
    • Downvote 1
  12. Нашел несколько интересных проектов, которые могут позволить подключать геймпады напрямую в 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
  13. Портативный клиент пробовали? Я, правда, не знаю, можно ли запустить 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.

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

     

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

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

     

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

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

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

    • Upvote 1
    • Downvote 1
  16. Доброе время суток, уважаемые форумчане!

     

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

     

    В прошлом году мною было опубликована "хотелка", которая блестяще была решена с помощью 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

    • Upvote 8
    • Downvote 1
  17. Доброе время суток, просмотрел код - у меня возник вопрос. Пытаюсь, по возможности, решить проблему подключения к игре сторонних девайсов (сам в python новичок). Хочется прицепить в WoT геймпад Xbox 360 без сторонних программ, типа xpadder и т.п. Чтобы команды напрямую принимались игрой как бы от клавиатуры/мыши. В коде уважаемого cep62rus, вроде есть нужное направление. Если я правильно понимаю, то игра может принимать команды, но они не назначены, соответственно бесполезны. Есть идеи?

  18. А к картохе в саппорт не писал?

    Вибрация не вибронакидки, вибрация для геймпада - пошлют они меня "за картошкой".

    Насколько я понимаю, они поменяли какой-то метод, но понять что именно пока умишком слаб.

    Вот код из версии 0.8.09

        def update(self, vehicle, isLeftTrackBroken, isRightTrackBroken):
            if not VibroManager.g_instance.canWork():
                return
            self.__deathVibrationCtrl.update(vehicle.isAlive())
            self.__updateEngine(vehicle)
            if vehicle.isAlive():
                self.__trackBreakingVibrationCtrl.update(vehicle, isLeftTrackBroken, isRightTrackBroken)
            else:
                self.__fireVibrationCtrl.switch(False)
            VibroManager.g_instance.update()
    

  19. После обновления 0.8.11 при использовании "вибромода", стали сыпаться ошибки:

    'python.log'

    [EXCEPTION] (scripts/client/VehicleAppearance.py, 1098):
    Traceback (most recent call last):
    File "scripts/client/VehicleAppearance.py", line 1096, in __onPeriodicTimer
    File "scripts/client/VehicleAppearance.py", line 1405, in __updateVibrations
    File "scripts/client/Vibroeffects/ControllersManager.py", line 41, in update
    File "scripts/client/Vibroeffects/Controllers/TrackBreakingController.py", line 94, in update
    File "scripts/client/Vibroeffects/Controllers/TrackBreakingController.py", line 35, in __breakWithDirection
    AttributeError: 'BigWorld.HomingCamera' object has no attribute 'source'

    Декомпилировав игровые скрипты - выкладываю код.

    'VehicleAppearance'

    def __onPeriodicTimer(self):
            self.__periodicTimerID = None
            try:
                self.__update	Vibrations()
            except Exception:
                LOG_CURRENT_EXCEPTION()
    
            try:
                if self.__lightFxCtrl is not None:
                    self.__lightFxCtrl.update(self.__vehicle)
                if self.__auxiliaryFxCtrl is not None:
                    self.__auxiliaryFxCtrl.update(self.__vehicle)
                self.__updateWaterStatus()
            except:
                LOG_CURRENT_EXCEPTION()
    
            if not self.__vehicle.isAlive():
                self.__periodicTimerID = BigWorld.callback(_PERIODIC_TIME, self.__onPeriodicTimer)
                return
            else:
                try:
                    self.__distanceFromPlayer = (BigWorld.camera().position - self.__vehicle.position).length
                    for extraData in self.__vehicle.extras.values():
                        extra = extraData.get('extra', None)
                        if isinstance(extra, vehicle_extras.Fire):
                            extra.checkUnderwater(extraData, self.__vehicle, self.isUnderwater)
                            break
    
                    self.__updateCurrTerrainMatKinds()
                    self.__updateMovementSounds()
                    self.__updateBlockedMovement()
                    self.__updateEffectsLOD()
                    self.__trailEffects.update()
                except:
                    LOG_CURRENT_EXCEPTION()
    
                self.__periodicTimerID = BigWorld.callback(_PERIODIC_TIME, self.__onPeriodicTimer)
                return
    

     

    'VehicleAppearance'

        def __updateVibrations(self):
            if self.__vibrationsCtrl is None:
                return
            else:
                vehicle = self.__vehicle
                crashedTrackCtrl = self.__crashedTracksCtrl
                self.__vibrationsCtrl.update(vehicle, crashedTrackCtrl.isLeftTrackBroken(), crashedTrackCtrl.isRightTrackBroken())
                return
    

     

    'ControllersManager'

        def update(self, vehicle, isLeftTrackBroken, isRightTrackBroken):
            if self._TrackBreakingController__wasLeftTrackBroken == False and isLeftTrackBroken:
                self._TrackBreakingController__breakWithDirection(vehicle, True)
            elif self._TrackBreakingController__wasRightTrackBroken == False and isRightTrackBroken:
                self._TrackBreakingController__breakWithDirection(vehicle, False)
            self._TrackBreakingController__wasLeftTrackBroken = isLeftTrackBroken
            self._TrackBreakingController__wasRightTrackBroken = isRightTrackBroken
    

     

    'TrackBreakingController'

        def _TrackBreakingController__breakWithDirection(self, vehicle, isLeftTrackBroken):
            directionMatrix = Math.Matrix(BigWorld.camera().source)
            directionMatrix.setElement(3, 0, 0)
            directionMatrix.setElement(3, 1, 0)
            directionMatrix.setElement(3, 2, 0)
            cameraDirection = directionMatrix.applyToAxis(0)
            cameraDirection.y = 0
            vehicleMatrix = Math.Matrix(vehicle.matrix)
            vehicleMatrix.setElement(3, 0, 0)
            vehicleMatrix.setElement(3, 1, 0)
            vehicleMatrix.setElement(3, 2, 0)
            vehicleMatrix.invert()
            cameraDirection = vehicleMatrix.applyVector(cameraDirection)
            brokenTrackAngle = 0
            if isLeftTrackBroken:
                brokenTrackAngle = math.pi
            dirAngleRelative = math.atan2(cameraDirection.z, cameraDirection.x)
            minDirDelta = math.pi * 2
            vibrationToPlay = ''
            for angleVibration in TrackBreakingController._TrackBreakingController__ANGLES_VIBRATIONS:
                curAngle = angleVibration[0]
                curAngle += dirAngleRelative
                if curAngle > math.pi * 2:
                    curAngle -= math.pi * 2
                if curAngle < 0:
                    curAngle += math.pi * 2
                curDirDelta = abs(curAngle - brokenTrackAngle)
                if curDirDelta > math.pi:
                    curDirDelta = math.pi * 2 - curDirDelta
                if curDirDelta < minDirDelta:
                    minDirDelta = curDirDelta
                    vibrationToPlay = angleVibration[1]
                    continue
            OnceController(vibrationToPlay)
    

     

    'TrackBreakingController'

        def update(self, vehicle, isLeftTrackBroken, isRightTrackBroken):
            if self._TrackBreakingController__wasLeftTrackBroken == False and isLeftTrackBroken:
                self._TrackBreakingController__breakWithDirection(vehicle, True)
            elif self._TrackBreakingController__wasRightTrackBroken == False and isRightTrackBroken:
                self._TrackBreakingController__breakWithDirection(vehicle, False)
            self._TrackBreakingController__wasLeftTrackBroken = isLeftTrackBroken
            self._TrackBreakingController__wasRightTrackBroken = isRightTrackBroken
    

     

    Ошибки на вибрацию в игре никак не влияют, но мое мнение что это не есть хорошо. Может кто-то из форума откликнется и укажет нубу в python как и что сделать для правильной работы "вибромода".

  20. Вряд ли я кому-то открою глаза, но существуют специальные вибронакидки для игры в WoT, которые официально поддерживаются разработчиками. Соответственно, под эти вибронакидки есть уже готовый модуль в игре для передачи обратной связи.

    Покопав тему глубже, нашел следующие вводные данные:
    На сайте производителей "виброжопки" есть SDK, т.е. очень просто понять что, как и почему работает.
    Там же есть и стандартные, и усиленные эффекты для WoT, открывая которые обычным тестовым редактором понятно, как именно сделаны виброэффекты.
    Полазив в папках самой WoT нашел папку с скриптами, написанными на Python, которые относятся именно к вибронакидке "X:\Games\World_of_Tanks\res\scripts\client\vibroeffects".
    В интернетах нашелся нужный код. Из редактора IDLE все работает - то есть запуская этот скрипт, геймпад действительно начинает вибрировать в зависимости от заданной скорости моторов. При этом отработка скрипта никак не влияла на работу Xpadder.
    С помощью этого форума была "запилена" обратная вибросвязь для геймпада, ссылка. Выражаю огромнейшую благодарность sirmaxinj3ct0r

     

    В итоге все получилось - данные для двигателей берутся из родных файлов для вибронакидки *.uwv, работает смешивание эффектов (одновременная вибрация, например пожар/выстрел/рикошет), работают "ползунки" в настройках игры для вибронакидки (можно увеличить вибрации/уменьшить/отключить).

    Должен сказать, что играть стало намного интереснее и теперь не нужны многие вспомогательные картинки (руками чувствуется когда зарядилось орудие, когда по тебе попали и каким снарядом). Я кстати понял, почему разработчики хвалятся тем, что эффекты делал звукорежиссер (пожар только чего стоит - как биение сердца).

    Для установки вибромода для геймпада нужно:
    1. Необходимо установить Python версий 2.7.х (у меня стоит 2.7.6).
    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
    3. Распаковать мод в нужную папку.
    4. Играть.

     

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

    • Upvote 1
×
×
  • Create New...