├── .gitattributes ├── .gitignore ├── builder.cfg ├── builder.py ├── gui ├── actionscript │ ├── AdvancedAimingSystem.as3proj │ └── src │ │ └── net │ │ └── GPCracker │ │ └── AdvancedAimingSystem │ │ └── LibraryMain.as └── python │ ├── GuiClasses.py │ └── GuiController.py ├── readme.md ├── resource ├── configs │ ├── gui.xml │ ├── main │ │ └── AdvancedAimingSystem.xml │ ├── modules.xml │ ├── modules │ │ ├── AimCorrection.xml │ │ ├── AimingInfo.xml │ │ └── TargetScanner.xml │ ├── plugins.xml │ ├── plugins │ │ ├── AdvancedArtyExtension.xml │ │ ├── AutoAimExtension.xml │ │ ├── ExpertPerkExtension.xml │ │ ├── RadialMenuExtension.xml │ │ ├── SafeShotExtension.xml │ │ └── SniperModeSPGExtension.xml │ └── root.xml ├── localizations │ ├── en-US │ │ └── AdvancedAimingSystem.po │ └── ru-RU │ │ └── AdvancedAimingSystem.po ├── packages │ └── AdvancedAimingSystem │ │ └── meta.xml └── textures │ └── AimingInfoBackground.png └── source ├── hooks ├── Account.py ├── AvatarInputHandler.py ├── BattleEntry.py ├── BattleShared.py ├── OperatingControlMode.py └── Vehicle.py ├── local ├── AimCorrection.py ├── AimingInfo.py ├── TargetInfo.py └── TargetScanner.py ├── main ├── config.py ├── globals.py ├── header.py ├── imports.py ├── injector.py └── launcher.py └── plugins ├── AdvancedArtyExtension.py ├── AimCorrectionGunMarkerFix.py ├── AutoAimExtension.py ├── ExpertPerkExtension.py ├── RadialMenuExtension.py ├── SafeShotExtension.py └── SniperModeSPGExtension.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # all text files in repository should have their line endings normalized 2 | * text=auto 3 | 4 | # these files are text ones 5 | *.as text 6 | *.md text 7 | *.py text 8 | *.cfg text 9 | *.xml text 10 | 11 | # these text files have specific line endings 12 | *.po text eol=lf 13 | *.as3proj text eol=crlf 14 | 15 | # these files are binaries 16 | *.png binary 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # excluding actionscript binaries 2 | /gui/actionscript/bin 3 | /gui/actionscript/obj 4 | /gui/actionscript/swc 5 | 6 | # excluding output files 7 | /output 8 | 9 | # excluding builder tools 10 | /tools 11 | 12 | # excluding personal files 13 | /topic 14 | /temp 15 | -------------------------------------------------------------------------------- /builder.cfg: -------------------------------------------------------------------------------- 1 | { 2 | "globalMacros": { 3 | "<>": "GPCracker", 4 | "<>": "AdvancedAimingSystem", 5 | "<>": "ru-RU", 6 | "<>": "<>", 7 | "<>": "<>", 8 | "<>": "1.0.0.1" 9 | }, 10 | "pathsMacros": { 11 | "<>": "./output/build/", 12 | "<>": "./output/release/", 13 | "<>": "./", 14 | "<>": "./", 15 | "<>": "./res/scripts/common/Lib", 16 | "<>": "./res/scripts/client/mods/", 17 | "<>": "./res/scripts/client/gui/mods/", 18 | "<>": "./res/text/lc_messages/", 19 | "<>": "./res/gui/flash/", 20 | "<>": "./res/gui/flash/atlases/", 21 | "<>": "./res/mods/<>.<>/", 22 | "<>": "./<>/", 23 | "<>": "./configs/<>.<>/" 24 | }, 25 | "cleanupPaths": [ 26 | "<>", 27 | "<>" 28 | ], 29 | "releaseArchives": [ 30 | { 31 | "archive": "<>/<>_<>(main).zip", 32 | "comment": "This archive contains assembled files (main packages) of Advanced Aiming System Mod (<>).", 33 | "packages": [ 34 | { 35 | "name": "<>.<>_<>.wotmod", 36 | "build": "<>/<>.<>_<>.wotmod", 37 | "release": "<>/<>/<>.<>_<>.wotmod", 38 | "metadata": [ 39 | ["./resource/packages/<>/meta.xml", "<>/meta.xml", "utf-8"] 40 | ], 41 | "actionscript": [ 42 | [ 43 | "./gui/actionscript/<>.as3proj", 44 | [ 45 | ["./gui/actionscript/bin/<>.swf", "<>/<>/<>.swf"] 46 | ] 47 | ] 48 | ], 49 | "python": { 50 | "encoding": "ascii", 51 | "modules": [], 52 | "sources": [ 53 | [ 54 | "<>.py", 55 | [ 56 | "./source/main/header.py", 57 | "./source/main/imports.py", 58 | "./source/main/globals.py", 59 | "./source/main/config.py", 60 | "./source/local/AimingInfo.py", 61 | "./source/local/TargetInfo.py", 62 | "./source/local/TargetScanner.py", 63 | "./source/local/AimCorrection.py", 64 | "./gui/python/GuiClasses.py", 65 | "./gui/python/GuiController.py", 66 | "./source/main/injector.py", 67 | "./source/hooks/BattleShared.py", 68 | "./source/hooks/BattleEntry.py", 69 | "./source/hooks/Account.py", 70 | "./source/hooks/Vehicle.py", 71 | "./source/hooks/AvatarInputHandler.py", 72 | "./source/hooks/OperatingControlMode.py", 73 | "./source/plugins/AimCorrectionGunMarkerFix.py", 74 | "./source/plugins/SafeShotExtension.py", 75 | "./source/plugins/ExpertPerkExtension.py", 76 | "./source/plugins/AdvancedArtyExtension.py", 77 | "./source/plugins/SniperModeSPGExtension.py", 78 | "./source/plugins/AutoAimExtension.py", 79 | "./source/plugins/RadialMenuExtension.py", 80 | "./source/main/launcher.py" 81 | ], 82 | "<>/<>.py", 83 | "<>/<>.pyc", 84 | "<>/<>/mod_<>.pyc" 85 | ] 86 | ] 87 | }, 88 | "resources": [ 89 | ["./resource/configs/main/<>.xml", "<>/<>/mod_<>.xml"], 90 | ["./resource/textures/AimingInfoBackground.png", "<>/<>/icons/AimingInfoBackground.png"] 91 | ], 92 | "localizations": [ 93 | [ 94 | "./resource/localizations/en-US/<>.po", 95 | "<>/localizations/en-US/<>.mo", 96 | "<>/<>/en-US/<>.mo" 97 | ], 98 | [ 99 | "./resource/localizations/ru-RU/<>.po", 100 | "<>/localizations/ru-RU/<>.mo", 101 | "<>/<>/ru-RU/<>.mo" 102 | ], 103 | [ 104 | "./resource/localizations/<>/<>.po", 105 | "<>/<>.mo", 106 | "<>/<>/<>.mo" 107 | ] 108 | ], 109 | "atlases": [] 110 | } 111 | ], 112 | "resources": [ 113 | ["./resource/configs/root.xml", "<>/<>/root.xml"], 114 | ["./resource/configs/modules.xml", "<>/<>/modules.xml"], 115 | ["./resource/configs/plugins.xml", "<>/<>/plugins.xml"], 116 | ["./resource/configs/modules/", "<>/<>/modules/"], 117 | ["./resource/configs/plugins/", "<>/<>/plugins/"], 118 | ["./resource/configs/gui.xml", "<>/<>/gui.xml"] 119 | ] 120 | }, 121 | { 122 | "archive": "<>/<>_<>(extra).zip", 123 | "comment": "This archive contains assembled files (extra packages) of Advanced Aiming System Mod (<>).", 124 | "packages": [], 125 | "resources": [] 126 | } 127 | ] 128 | } 129 | -------------------------------------------------------------------------------- /gui/actionscript/AdvancedAimingSystem.as3proj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /gui/actionscript/src/net/GPCracker/AdvancedAimingSystem/LibraryMain.as: -------------------------------------------------------------------------------- 1 | package net.GPCracker.AdvancedAimingSystem 2 | { 3 | import flash.display.MovieClip; 4 | 5 | public class LibraryMain extends MovieClip 6 | { 7 | public function LibraryMain() 8 | { 9 | super(); 10 | return; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gui/python/GuiClasses.py: -------------------------------------------------------------------------------- 1 | # ----------------- # 2 | # Gui Classes # 3 | # ----------------- # 4 | class GuiInfoPanelContextMenuHandler(XModLib.pygui.battle.views.handlers.ContextMenuHandler.ContextMenuHandler): 5 | OPTIONS = ( 6 | ('hideInfoPanel', '_hideInfoPanel', g_config['gui']['panels']['context']['hideInfoPanel'], '', True, None), 7 | ('resetInfoPanel', '_resetInfoPanel', g_config['gui']['panels']['context']['resetInfoPanel'], '', True, None) 8 | ) 9 | 10 | @classmethod 11 | def _getOptionsHandlers(cls): 12 | def _getOptionsHandlersGenerator(options): 13 | for idx, handler, label, icon, enabled, submenu in options: 14 | if submenu is not None: 15 | for entry in _getOptionsHandlersGenerator(submenu): 16 | yield entry 17 | else: 18 | yield idx, handler 19 | return 20 | return dict(_getOptionsHandlersGenerator(cls.OPTIONS)) 21 | 22 | @classmethod 23 | def _getOptionsItems(cls): 24 | def _makeOptionsItems(options): 25 | return [cls._makeItem(idx, label, optInitData={'enabled': enabled, 'iconType': icon}, optSubMenu=_makeOptionsItems(submenu) if submenu is not None else submenu) for idx, handler, label, icon, enabled, submenu in options] 26 | return _makeOptionsItems(cls.OPTIONS) 27 | 28 | def __init__(self, cmProxy, ctx=None): 29 | super(GuiInfoPanelContextMenuHandler, self).__init__(cmProxy, ctx=ctx, handlers=self._getOptionsHandlers()) 30 | return 31 | 32 | def fini(self): 33 | super(GuiInfoPanelContextMenuHandler, self).fini() 34 | return 35 | 36 | def _initFlashValues(self, ctx): 37 | super(GuiInfoPanelContextMenuHandler, self)._initFlashValues(ctx) 38 | self._alias = ctx.alias 39 | return 40 | 41 | def _clearFlashValues(self): 42 | super(GuiInfoPanelContextMenuHandler, self)._clearFlashValues() 43 | self._alias = None 44 | return 45 | 46 | def _hideInfoPanel(self): 47 | gui.shared.g_eventBus.handleEvent(GuiEvent(GuiEvent.INFO_PANEL_INGAME_CONFIG, {'alias': self._alias, 'config': {'visible': False}}), gui.shared.EVENT_BUS_SCOPE.BATTLE) 48 | return 49 | 50 | def _resetInfoPanel(self): 51 | gui.shared.g_eventBus.handleEvent(GuiEvent(GuiEvent.INFO_PANEL_INGAME_RESET, {'alias': self._alias}), gui.shared.EVENT_BUS_SCOPE.BATTLE) 52 | return 53 | 54 | def _generateOptions(self, ctx=None): 55 | return self._getOptionsItems() 56 | 57 | class GuiCorrectionPanelContextMenuHandler(GuiInfoPanelContextMenuHandler): 58 | pass 59 | 60 | class GuiTargetPanelContextMenuHandler(GuiInfoPanelContextMenuHandler): 61 | pass 62 | 63 | class GuiAimingPanelContextMenuHandler(GuiInfoPanelContextMenuHandler): 64 | pass 65 | 66 | class GuiInfoPanel(XModLib.pygui.battle.views.components.panels.TextPanel.TextPanel): 67 | def __init__(self, *args, **kwargs): 68 | super(GuiInfoPanel, self).__init__(*args, **kwargs) 69 | self.__config = { 70 | 'template': '' 71 | } 72 | return 73 | 74 | def py_onPanelDrag(self, x, y): 75 | super(GuiInfoPanel, self).py_onPanelDrag(x, y) 76 | self.fireEvent(GuiEvent(GuiEvent.INFO_PANEL_DRAG, {'alias': self.getAlias(), 'position': (x, y)}), gui.shared.EVENT_BUS_SCOPE.BATTLE) 77 | return 78 | 79 | def py_onPanelDrop(self, x, y): 80 | super(GuiInfoPanel, self).py_onPanelDrop(x, y) 81 | self.fireEvent(GuiEvent(GuiEvent.INFO_PANEL_DROP, {'alias': self.getAlias(), 'position': (x, y)}), gui.shared.EVENT_BUS_SCOPE.BATTLE) 82 | return 83 | 84 | def _populate(self): 85 | super(GuiInfoPanel, self)._populate() 86 | self.addListener(GuiEvent.INFO_PANEL_CONFIG, self._handlePanelConfigEvent, gui.shared.EVENT_BUS_SCOPE.BATTLE) 87 | self.addListener(GuiEvent.INFO_PANEL_UPDATE, self._handlePanelUpdateEvent, gui.shared.EVENT_BUS_SCOPE.BATTLE) 88 | return 89 | 90 | def _dispose(self): 91 | self.removeListener(GuiEvent.INFO_PANEL_CONFIG, self._handlePanelConfigEvent, gui.shared.EVENT_BUS_SCOPE.BATTLE) 92 | self.removeListener(GuiEvent.INFO_PANEL_UPDATE, self._handlePanelUpdateEvent, gui.shared.EVENT_BUS_SCOPE.BATTLE) 93 | super(GuiInfoPanel, self)._dispose() 94 | return 95 | 96 | def _handlePanelConfigEvent(self, event): 97 | if event.ctx['alias'] == self.getAlias(): 98 | self.updateConfig(event.ctx['config']) 99 | return 100 | 101 | def _handlePanelUpdateEvent(self, event): 102 | if event.ctx['alias'] == self.getAlias(): 103 | self.updateMacroData(event.ctx['macrodata']) 104 | return 105 | 106 | def getConfig(self): 107 | config = super(GuiInfoPanel, self).getConfig() 108 | config.update(self.__config) 109 | return config 110 | 111 | def updateConfig(self, config): 112 | super(GuiInfoPanel, self).updateConfig(config) 113 | self.__config.update(self._computeConfigPatch(config, self.__config)) 114 | return 115 | 116 | def updateMacroData(self, macrodata): 117 | self.updateText(self.__config['template'](**macrodata) if macrodata is not None else '') 118 | return 119 | 120 | class GuiCorrectionPanel(GuiInfoPanel): 121 | pass 122 | 123 | class GuiTargetPanel(GuiInfoPanel): 124 | pass 125 | 126 | class GuiAimingPanel(GuiInfoPanel): 127 | pass 128 | 129 | class GuiSettings(object): 130 | CORRECTION_PANEL_ALIAS = 'AdvancedAimingSystemCorrectionPanel' 131 | TARGET_PANEL_ALIAS = 'AdvancedAimingSystemTargetPanel' 132 | AIMING_PANEL_ALIAS = 'AdvancedAimingSystemAimingPanel' 133 | 134 | @staticmethod 135 | def getContextMenuHandlers(): 136 | return ( 137 | GuiCorrectionPanelContextMenuHandler.getHandler(GuiSettings.CORRECTION_PANEL_ALIAS), 138 | GuiTargetPanelContextMenuHandler.getHandler(GuiSettings.TARGET_PANEL_ALIAS), 139 | GuiAimingPanelContextMenuHandler.getHandler(GuiSettings.AIMING_PANEL_ALIAS) 140 | ) 141 | 142 | @staticmethod 143 | def getViewSettings(): 144 | return ( 145 | GuiCorrectionPanel.getSettings(GuiSettings.CORRECTION_PANEL_ALIAS), 146 | GuiTargetPanel.getSettings(GuiSettings.TARGET_PANEL_ALIAS), 147 | GuiAimingPanel.getSettings(GuiSettings.AIMING_PANEL_ALIAS) 148 | ) 149 | 150 | class GuiEvent(gui.shared.events.GameEvent): 151 | INFO_PANEL_INGAME_CONFIG = 'game/AdvancedAimingSystem/InfoPanelIngameConfig' 152 | INFO_PANEL_INGAME_RESET = 'game/AdvancedAimingSystem/InfoPanelIngameReset' 153 | INFO_PANEL_CONFIG = 'game/AdvancedAimingSystem/InfoPanelConfig' 154 | INFO_PANEL_UPDATE = 'game/AdvancedAimingSystem/InfoPanelUpdate' 155 | INFO_PANEL_DRAG = 'game/AdvancedAimingSystem/InfoPanelDrag' 156 | INFO_PANEL_DROP = 'game/AdvancedAimingSystem/InfoPanelDrop' 157 | AVATAR_CTRL_MODE = 'game/AdvancedAimingSystem/AvatarCtrlMode' 158 | 159 | class GuiBaseBusinessHandler(gui.Scaleform.framework.package_layout.PackageBusinessHandler): 160 | @staticmethod 161 | def _updatePanelConfig(alias, config): 162 | gui.shared.g_eventBus.handleEvent(GuiEvent(GuiEvent.INFO_PANEL_CONFIG, {'alias': alias, 'config': config}), gui.shared.EVENT_BUS_SCOPE.BATTLE) 163 | return 164 | 165 | class GuiBattleBusinessHandler(GuiBaseBusinessHandler): 166 | def __init__(self, staticConfigs, ingameConfigs): 167 | self._ctrlModeName = 'default' 168 | self._staticConfigs = staticConfigs 169 | self._ingameConfigs = ingameConfigs 170 | super(GuiBattleBusinessHandler, self).__init__( 171 | ( 172 | (GuiEvent.INFO_PANEL_INGAME_CONFIG, self._handleInfoPanelIngameConfigEvent), 173 | (GuiEvent.INFO_PANEL_INGAME_RESET, self._handleInfoPanelIngameResetEvent), 174 | (GuiEvent.INFO_PANEL_DRAG, self._handleInfoPanelDragEvent), 175 | (GuiEvent.INFO_PANEL_DROP, self._handleInfoPanelDropEvent), 176 | (GuiEvent.AVATAR_CTRL_MODE, self._handleAvatarCtrlModeEvent) 177 | ), 178 | gui.app_loader.settings.APP_NAME_SPACE.SF_BATTLE, 179 | gui.shared.EVENT_BUS_SCOPE.BATTLE 180 | ) 181 | return 182 | 183 | def _reconfigureInfoPanel(self, alias): 184 | config = self._staticConfigs.get(alias, {}).get('default', {}).copy() 185 | config.update(self._ingameConfigs.get(alias, {}).get('default', {})) 186 | if self._ctrlModeName != 'default': 187 | config.update(self._staticConfigs.get(alias, {}).get(self._ctrlModeName, {})) 188 | config.update(self._ingameConfigs.get(alias, {}).get(self._ctrlModeName, {})) 189 | self._updatePanelConfig(alias, config) 190 | return 191 | 192 | def _handleInfoPanelIngameConfigEvent(self, event): 193 | if self._ctrlModeName != 'default': 194 | self._ingameConfigs.setdefault(event.ctx['alias'], {}).setdefault(self._ctrlModeName, {}).update(event.ctx['config']) 195 | self._reconfigureInfoPanel(event.ctx['alias']) 196 | self._ingameConfigs.save() 197 | return 198 | 199 | def _handleInfoPanelIngameResetEvent(self, event): 200 | if self._ctrlModeName != 'default': 201 | self._ingameConfigs.setdefault(event.ctx['alias'], {}).setdefault(self._ctrlModeName, {}).clear() 202 | self._reconfigureInfoPanel(event.ctx['alias']) 203 | self._ingameConfigs.save() 204 | return 205 | 206 | def _handleInfoPanelDragEvent(self, event): 207 | if self._ctrlModeName != 'default': 208 | self._ingameConfigs.setdefault(event.ctx['alias'], {}).setdefault(self._ctrlModeName, {})['position'] = event.ctx['position'] 209 | self._ingameConfigs.save() 210 | return 211 | 212 | def _handleInfoPanelDropEvent(self, event): 213 | if self._ctrlModeName != 'default': 214 | self._ingameConfigs.setdefault(event.ctx['alias'], {}).setdefault(self._ctrlModeName, {})['position'] = event.ctx['position'] 215 | self._ingameConfigs.save() 216 | return 217 | 218 | def _handleAvatarCtrlModeEvent(self, event): 219 | ctrlMode = event.ctx['ctrlMode'] 220 | if ctrlMode == AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARCADE: 221 | ctrlModeName = 'arcade' 222 | elif ctrlMode == AvatarInputHandler.aih_constants.CTRL_MODE_NAME.SNIPER: 223 | ctrlModeName = 'sniper' 224 | elif ctrlMode == AvatarInputHandler.aih_constants.CTRL_MODE_NAME.STRATEGIC: 225 | ctrlModeName = 'strategic' 226 | elif ctrlMode == AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARTY: 227 | ctrlModeName = 'arty' 228 | else: 229 | ctrlModeName = 'default' 230 | self._ctrlModeName = ctrlModeName 231 | for alias in (GuiSettings.CORRECTION_PANEL_ALIAS, GuiSettings.TARGET_PANEL_ALIAS, GuiSettings.AIMING_PANEL_ALIAS): 232 | config = self._staticConfigs.get(alias, {}).get(ctrlModeName, {}).copy() 233 | config.update(self._ingameConfigs.get(alias, {}).get(ctrlModeName, {})) 234 | self._updatePanelConfig(alias, config) 235 | return 236 | 237 | class GuiGlobalBusinessHandler(GuiBaseBusinessHandler): 238 | def __init__(self, staticConfigs, ingameConfigs): 239 | self._staticConfigs = staticConfigs 240 | self._ingameConfigs = ingameConfigs 241 | super(GuiGlobalBusinessHandler, self).__init__( 242 | ( 243 | (gui.shared.events.ComponentEvent.COMPONENT_REGISTERED, self._handleComponentRegistrationEvent), 244 | ), 245 | gui.app_loader.settings.APP_NAME_SPACE.SF_BATTLE, 246 | gui.shared.EVENT_BUS_SCOPE.GLOBAL 247 | ) 248 | return 249 | 250 | def _handleComponentRegistrationEvent(self, event): 251 | if event.alias in (GuiSettings.CORRECTION_PANEL_ALIAS, GuiSettings.TARGET_PANEL_ALIAS, GuiSettings.AIMING_PANEL_ALIAS): 252 | config = self._staticConfigs.get(event.alias, {}).get('default', {}).copy() 253 | config.update(self._ingameConfigs.get(event.alias, {}).get('default', {})) 254 | self._updatePanelConfig(event.alias, config) 255 | return 256 | -------------------------------------------------------------------------------- /gui/python/GuiController.py: -------------------------------------------------------------------------------- 1 | # --------------------------- # 2 | # GuiController Classes # 3 | # --------------------------- # 4 | class GuiController(object): 5 | __slots__ = ('__weakref__', '_updateInterval', '_updateCallbackLoop') 6 | 7 | avatarCtrlMode = AvatarInputHandler.aih_global_binding.bindRO(AvatarInputHandler.aih_global_binding.BINDING_ID.CTRL_MODE_NAME) 8 | 9 | @staticmethod 10 | def dispatchEvent(eventType, ctx=None, scope=gui.shared.EVENT_BUS_SCOPE.BATTLE): 11 | gui.shared.g_eventBus.handleEvent(GuiEvent(eventType, ctx), scope) 12 | return 13 | 14 | @property 15 | def updateInterval(self): 16 | return self._updateInterval 17 | 18 | @updateInterval.setter 19 | def updateInterval(self, value): 20 | if self.isUpdateActive: 21 | raise RuntimeError('update interval could not be changed while controller is running') 22 | self._updateInterval = value 23 | # Recreate internal components. 24 | self._initInternalComponents() 25 | return 26 | 27 | def __init__(self, updateInterval=0.04): 28 | super(GuiController, self).__init__() 29 | self._updateInterval = updateInterval 30 | # Initialize internal components. 31 | self._initInternalComponents() 32 | return 33 | 34 | def _initInternalComponents(self): 35 | self._updateCallbackLoop = XModLib.CallbackUtils.CallbackLoop( 36 | self._updateInterval, XModLib.CallbackUtils.getMethodProxy(self._updateInfoPanels) 37 | ) 38 | return 39 | 40 | def enable(self): 41 | aihGlobalBinding = AvatarInputHandler.aih_global_binding 42 | aihGlobalBinding.subscribe(aihGlobalBinding.BINDING_ID.CTRL_MODE_NAME, self.__onAvatarControlModeChanged) 43 | self.dispatchEvent(GuiEvent.AVATAR_CTRL_MODE, {'ctrlMode': self.avatarCtrlMode}) 44 | return 45 | 46 | def disable(self): 47 | aihGlobalBinding = AvatarInputHandler.aih_global_binding 48 | aihGlobalBinding.unsubscribe(aihGlobalBinding.BINDING_ID.CTRL_MODE_NAME, self.__onAvatarControlModeChanged) 49 | return 50 | 51 | def __onAvatarControlModeChanged(self, ctrlMode): 52 | self.dispatchEvent(GuiEvent.AVATAR_CTRL_MODE, {'ctrlMode': ctrlMode}) 53 | return 54 | 55 | def _getAimCorrectionMacroData(self): 56 | aimCorrection = getattr(BigWorld.player().inputHandler.ctrl, 'XAimCorrection', None) 57 | return aimCorrection.getMacroData() if aimCorrection is not None else None 58 | 59 | def _getTargetInfoMacroData(self): 60 | targetInfo = getattr(BigWorld.player().inputHandler, 'XTargetInfo', None) 61 | return targetInfo.getMacroData() if targetInfo is not None else None 62 | 63 | def _getAimingInfoMacroData(self): 64 | aimingInfo = getattr(BigWorld.player().inputHandler, 'XAimingInfo', None) 65 | return aimingInfo.getMacroData() if aimingInfo is not None else None 66 | 67 | def _updateInfoPanelMacroData(self, alias, macrodata): 68 | self.dispatchEvent(GuiEvent.INFO_PANEL_UPDATE, {'alias': alias, 'macrodata': macrodata}) 69 | return 70 | 71 | def _updateInfoPanels(self): 72 | self._updateInfoPanelMacroData(GuiSettings.CORRECTION_PANEL_ALIAS, self._getAimCorrectionMacroData()) 73 | self._updateInfoPanelMacroData(GuiSettings.TARGET_PANEL_ALIAS, self._getTargetInfoMacroData()) 74 | self._updateInfoPanelMacroData(GuiSettings.AIMING_PANEL_ALIAS, self._getAimingInfoMacroData()) 75 | return 76 | 77 | @property 78 | def isUpdateActive(self): 79 | return self._updateCallbackLoop.isActive 80 | 81 | def start(self, delay=None): 82 | self._updateCallbackLoop.start(delay) 83 | return 84 | 85 | def stop(self): 86 | self._updateCallbackLoop.stop() 87 | return 88 | 89 | def __repr__(self): 90 | return '{!s}(updateInterval={!r})'.format(self.__class__.__name__, self._updateInterval) 91 | 92 | def __del__(self): 93 | self._updateCallbackLoop = None 94 | return 95 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Advanced Aiming System 2 | 3 | [Advanced Aiming System][1] is a modification for World of Tanks. It is based on 4 | X-Mod library. 5 | 6 | ## Manual assembling 7 | 8 | Manual is expected in near future. 9 | 10 | ## Downloading installation-ready assemblies 11 | 12 | Beta and release assemblies are available for downloading in 13 | [releases](../releases), alpha versions are posted and discussed only in 14 | [official topic][1]. 15 | 16 | ## Installation 17 | 18 | Modification does not require any special installation, just unpack archive to 19 | your *mods* folder. Ensure you have backed up your config and binary files of 20 | modification before upgrading, especially to alpha versions (for possible 21 | rollback). Transfer changes from old config files if required. 22 | 23 | **Do not forget to install X-Mod library.** 24 | 25 | ## Configuration 26 | 27 | All configuration could be made by editing config files. All descriptions are 28 | provided in comments to parameters. 29 | 30 | **Important**: Do not use standard *Windows Notepad* for editing config files! 31 | It breaks encoding and file will not be read properly ingame. 32 | 33 | ## Info, Updating, Bugs, Errors, Discussion 34 | 35 | Author tries to update a modification as soon as possible after new WoT patch is 36 | released. 37 | 38 | All additional information, discussion, feature requests and bug reports are 39 | also available in English or Russian in [official topic][1]. 40 | 41 | [1]: http://www.koreanrandom.com/forum/topic/16559-/ 42 | -------------------------------------------------------------------------------- /resource/configs/gui.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | True 9 | 10 | 11 | 0.04 12 | 13 | 14 | 15 | 16 | 17 | 18 | #AdvancedAimingSystem:gui/panels/context/hideInfoPanel; 19 | #AdvancedAimingSystem:gui/panels/context/resetInfoPanel; 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 1.0 31 | False 32 | 33 | #AdvancedAimingSystem:gui/panels/CorrectionPanel/default/tooltip; 34 | 35 | 0.0 0.3 36 | 450.0 25.0 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | True 47 | 48 | 49 | 50 | True 51 | 52 | 53 | 54 | True 55 | 56 | 57 | 58 | True 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 1.0 67 | False 68 | 69 | #AdvancedAimingSystem:gui/panels/TargetPanel/default/tooltip; 70 | 71 | 0.0 0.4 72 | 450.0 25.0 73 | 74 | 75 | True 76 | 77 | 78 | True 79 | 80 | 81 | True 82 | 83 | 84 | True 85 | 86 | 87 | 88 | 89 | 90 | 91 | 1.0 92 | False 93 | img://mods/GPCracker.AdvancedAimingSystem/icons/AimingInfoBackground.png 94 | #AdvancedAimingSystem:gui/panels/AimingPanel/default/tooltip; 95 | 96 | 0.4 -0.1 97 | 200.0 130.0 98 | 99 | 100 | True 101 | 102 | 0.4 -0.1 103 | 104 | 105 | True 106 | 107 | 0.4 -0.25 108 | 109 | 110 | True 111 | 112 | -0.3 -0.4 113 | 114 | 115 | True 116 | 117 | -0.3 -0.4 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /resource/configs/main/AdvancedAimingSystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resource/configs/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /resource/configs/modules/AimCorrection.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | False 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | True 21 | 22 | 23 | 24 | True 25 | KEY_LALT 26 | 27 | 28 | 29 | 30 | True 31 | True 32 | 33 | KEY_LCONTROL+KEY_K 34 | True 35 | False 36 | 37 | 38 | #AdvancedAimingSystem:arcadeAimCorrection/targetMode/onActivate; 39 | #AdvancedAimingSystem:arcadeAimCorrection/targetMode/onDeactivate; 40 | 41 | 42 | 43 | 50.0 720.0 44 | 45 | 46 | 47 | 48 | 49 | True 50 | 51 | 52 | True 53 | 54 | 55 | 56 | True 57 | KEY_LALT 58 | 59 | 60 | 61 | 62 | True 63 | True 64 | 65 | KEY_LCONTROL+KEY_K 66 | True 67 | False 68 | 69 | 70 | #AdvancedAimingSystem:sniperAimCorrection/targetMode/onActivate; 71 | #AdvancedAimingSystem:sniperAimCorrection/targetMode/onDeactivate; 72 | 73 | 74 | 75 | 10.0 720.0 76 | 77 | 78 | 79 | 80 | 81 | True 82 | 83 | 84 | False 85 | 86 | 87 | 88 | True 89 | KEY_LALT 90 | 91 | 92 | 93 | 94 | True 95 | False 96 | 97 | KEY_LCONTROL+KEY_K 98 | True 99 | False 100 | 101 | 102 | #AdvancedAimingSystem:strategicAimCorrection/targetMode/onActivate; 103 | #AdvancedAimingSystem:strategicAimCorrection/targetMode/onDeactivate; 104 | 105 | 106 | 107 | 0.5 108 | 109 | 110 | 111 | False 112 | 113 | 114 | 115 | 116 | False 117 | 118 | 119 | False 120 | 121 | 122 | 123 | True 124 | KEY_LALT 125 | 126 | 127 | 128 | 129 | True 130 | True 131 | 132 | KEY_LCONTROL+KEY_K 133 | True 134 | False 135 | 136 | 137 | #AdvancedAimingSystem:artyAimCorrection/targetMode/onActivate; 138 | #AdvancedAimingSystem:artyAimCorrection/targetMode/onDeactivate; 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /resource/configs/modules/AimingInfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | True 9 | 10 | 11 | 1.05 12 | 13 | 14 | -------------------------------------------------------------------------------- /resource/configs/modules/TargetScanner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | True 11 | 12 | 13 | 14 | 15 | 16 | True 17 | 18 | 19 | False 20 | 21 | 22 | False 23 | 24 | 25 | False 26 | 27 | 28 | 720.0 29 | 30 | 31 | 2.5 32 | 33 | 34 | 0.04 35 | 36 | 37 | 10.0 38 | 39 | 40 | 0.16 41 | 42 | 43 | 44 | 45 | True 46 | True 47 | 48 | KEY_LCONTROL+KEY_N 49 | True 50 | False 51 | 52 | 53 | #AdvancedAimingSystem:targetScanner/autoScan/onActivate; 54 | #AdvancedAimingSystem:targetScanner/autoScan/onDeactivate; 55 | 56 | 57 | 58 | 59 | 60 | False 61 | KEY_NONE 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /resource/configs/plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /resource/configs/plugins/AdvancedArtyExtension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | False 9 | 10 | 11 | 12 | True 13 | 14 | 15 | 5.0 16 | 17 | 18 | True 19 | 20 | 21 | True 22 | 23 | 24 | 25 | 26 | True 27 | False 28 | 29 | KEY_LALT+KEY_MIDDLEMOUSE 30 | True 31 | False 32 | 33 | 34 | 35 | 700.0 36 | 37 | 38 | True 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /resource/configs/plugins/AutoAimExtension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | False 9 | 10 | 11 | False 12 | 13 | 14 | False 15 | 16 | 17 | -------------------------------------------------------------------------------- /resource/configs/plugins/ExpertPerkExtension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | False 11 | 12 | 13 | False 14 | 15 | 16 | 30.0 17 | 18 | 19 | 5.0 20 | 21 | 22 | -------------------------------------------------------------------------------- /resource/configs/plugins/RadialMenuExtension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | False 9 | 10 | 11 | False 12 | 13 | 14 | False 15 | 16 | 17 | -------------------------------------------------------------------------------- /resource/configs/plugins/SafeShotExtension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | False 9 | True 10 | 11 | KEY_LALT 12 | False 13 | True 14 | 15 | 16 | #AdvancedAimingSystem:safeShot/onActivate; 17 | #AdvancedAimingSystem:safeShot/onDeactivate; 18 | 19 | 20 | 21 | True 22 | 23 | 24 | False 25 | 26 | 27 | 2.0 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | True 38 | 39 | 40 | 41 | True 42 | #AdvancedAimingSystem:safeShot/reasons/team/chat/message; 43 | 44 | 45 | 46 | 47 | 48 | 49 | True 50 | 51 | 52 | 53 | 54 | 55 | False 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /resource/configs/plugins/SniperModeSPGExtension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | False 9 | KEY_E 10 | 11 | 12 | -------------------------------------------------------------------------------- /resource/configs/root.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | True 11 | 12 | 13 | False 14 | 15 | 16 | #AdvancedAimingSystem:appSuccessMessage; 17 | 18 | 19 | #AdvancedAimingSystem:appWarningMessage; 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /resource/localizations/en-US/AdvancedAimingSystem.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Advanced Aiming System Mod (AdvancedAimingSystem)\n" 4 | "Report-Msgid-Bugs-To: http://www.koreanrandom.com/forum/topic/16559-/\n" 5 | "Last-Translator: Vladislav Ignatenko \n" 6 | "Language: en_US\n" 7 | "MIME-Version: 1.0\n" 8 | "Content-Type: text/plain; charset=utf-8\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | 11 | msgid "appSuccessMessage" 12 | msgstr "\"Advanced Aiming System\" was successfully loaded." 13 | 14 | msgid "appWarningMessage" 15 | msgstr "\"Advanced Aiming System\" was not tested with current client version." 16 | 17 | msgid "targetScanner/autoScan/onActivate" 18 | msgstr "[TargetScanner] AutoMode ENABLED." 19 | 20 | msgid "targetScanner/autoScan/onDeactivate" 21 | msgstr "[TargetScanner] AutoMode DISABLED." 22 | 23 | msgid "arcadeAimCorrection/targetMode/onActivate" 24 | msgstr "[ArcadeAimCorrection] TargetMode ENABLED." 25 | 26 | msgid "arcadeAimCorrection/targetMode/onDeactivate" 27 | msgstr "[ArcadeAimCorrection] TargetMode DISABLED." 28 | 29 | msgid "sniperAimCorrection/targetMode/onActivate" 30 | msgstr "[SniperAimCorrection] TargetMode ENABLED." 31 | 32 | msgid "sniperAimCorrection/targetMode/onDeactivate" 33 | msgstr "[SniperAimCorrection] TargetMode DISABLED." 34 | 35 | msgid "strategicAimCorrection/targetMode/onActivate" 36 | msgstr "[StrategicAimCorrection] TargetMode ENABLED." 37 | 38 | msgid "strategicAimCorrection/targetMode/onDeactivate" 39 | msgstr "[StrategicAimCorrection] TargetMode DISABLED." 40 | 41 | msgid "artyAimCorrection/targetMode/onActivate" 42 | msgstr "[ArtyAimCorrection] TargetMode ENABLED." 43 | 44 | msgid "artyAimCorrection/targetMode/onDeactivate" 45 | msgstr "[ArtyAimCorrection] TargetMode DISABLED." 46 | 47 | msgid "safeShot/onActivate" 48 | msgstr "[SafeShot] ENABLED." 49 | 50 | msgid "safeShot/onDeactivate" 51 | msgstr "[SafeShot] DISABLED." 52 | 53 | msgid "safeShot/error/template" 54 | msgstr "[{reason}] Shot has been blocked." 55 | 56 | msgid "safeShot/reasons/team/chat/message" 57 | msgstr "{player} ({vehicle}), you're in my line of fire!" 58 | 59 | msgid "safeShot/reasons/team/template" 60 | msgstr "friendly" 61 | 62 | msgid "safeShot/reasons/dead/template" 63 | msgstr "corpse" 64 | 65 | msgid "safeShot/reasons/waste/template" 66 | msgstr "waste" 67 | 68 | msgid "gui/panels/context/hideInfoPanel" 69 | msgstr "Hide this panel" 70 | 71 | msgid "gui/panels/context/resetInfoPanel" 72 | msgstr "Reset ingame settings" 73 | 74 | msgid "gui/panels/CorrectionPanel/default/tooltip" 75 | msgstr "Aim correction info panel." 76 | 77 | msgid "gui/panels/CorrectionPanel/default/text" 78 | msgstr "

Distance locked: {manualInfo:.1f}m.

" 79 | 80 | msgid "gui/panels/CorrectionPanel/arcade/text" 81 | msgstr "

Distance locked: {manualInfo:.1f}m.

" 82 | 83 | msgid "gui/panels/CorrectionPanel/sniper/text" 84 | msgstr "

Distance locked: {manualInfo:.1f}m.

" 85 | 86 | msgid "gui/panels/CorrectionPanel/strategic/text" 87 | msgstr "

Altitude locked: {manualInfo:.1f}m.

" 88 | 89 | msgid "gui/panels/CorrectionPanel/arty/text" 90 | msgstr "

Unknown parameter locked: {manualInfo:.1f}m.

" 91 | 92 | msgid "gui/panels/TargetPanel/default/tooltip" 93 | msgstr "Target scanner info panel." 94 | 95 | msgid "gui/panels/TargetPanel/default/text" 96 | msgstr "

Target: {shortName}; Distance: {distance:.1f}m; Speed: {speedMS:.1f}m/s.

" 97 | 98 | msgid "gui/panels/AimingPanel/default/tooltip" 99 | msgstr "Aiming info panel." 100 | 101 | msgid "gui/panels/AimingPanel/default/text" 102 | msgstr "\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;" 103 | 104 | msgid "gui/panels/AimingPanel/arcade/text" 105 | msgstr "\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;" 106 | 107 | msgid "gui/panels/AimingPanel/sniper/text" 108 | msgstr "\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;" 109 | 110 | msgid "gui/panels/AimingPanel/strategic/text" 111 | msgstr "\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;" 112 | 113 | msgid "gui/panels/AimingPanel/arty/text" 114 | msgstr "\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;" 115 | -------------------------------------------------------------------------------- /resource/localizations/ru-RU/AdvancedAimingSystem.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Advanced Aiming System Mod (AdvancedAimingSystem)\n" 4 | "Report-Msgid-Bugs-To: http://www.koreanrandom.com/forum/topic/16559-/\n" 5 | "Last-Translator: Vladislav Ignatenko \n" 6 | "Language: ru_RU\n" 7 | "MIME-Version: 1.0\n" 8 | "Content-Type: text/plain; charset=utf-8\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | 11 | msgid "appSuccessMessage" 12 | msgstr "\"Улучшенная система прицеливания\" (AdvancedAimingSystem) успешно загружена." 13 | 14 | msgid "appWarningMessage" 15 | msgstr "\"Улучшенная система прицеливания\" (AdvancedAimingSystem) не была протестирована с текущей версией клиента." 16 | 17 | msgid "targetScanner/autoScan/onActivate" 18 | msgstr "[СКАНЕР] АВТОРЕЖИМ АКТИВИРОВАН." 19 | 20 | msgid "targetScanner/autoScan/onDeactivate" 21 | msgstr "[СКАНЕР] АВТОРЕЖИМ ДЕАКТИВИРОВАН." 22 | 23 | msgid "arcadeAimCorrection/targetMode/onActivate" 24 | msgstr "[ДАЛЬНОМЕР] АРКАДНЫЙ по цели АКТИВИРОВАН." 25 | 26 | msgid "arcadeAimCorrection/targetMode/onDeactivate" 27 | msgstr "[ДАЛЬНОМЕР] АРКАДНЫЙ по цели ДЕАКТИВИРОВАН." 28 | 29 | msgid "sniperAimCorrection/targetMode/onActivate" 30 | msgstr "[ДАЛЬНОМЕР] СНАЙПЕРСКИЙ по цели АКТИВИРОВАН." 31 | 32 | msgid "sniperAimCorrection/targetMode/onDeactivate" 33 | msgstr "[ДАЛЬНОМЕР] СНАЙПЕРСКИЙ по цели ДЕАКТИВИРОВАН." 34 | 35 | msgid "strategicAimCorrection/targetMode/onActivate" 36 | msgstr "[ДАЛЬНОМЕР] СТРАТЕГИЧЕСКИЙ по цели АКТИВИРОВАН." 37 | 38 | msgid "strategicAimCorrection/targetMode/onDeactivate" 39 | msgstr "[ДАЛЬНОМЕР] СТРАТЕГИЧЕСКИЙ по цели ДЕАКТИВИРОВАН." 40 | 41 | msgid "artyAimCorrection/targetMode/onActivate" 42 | msgstr "[ДАЛЬНОМЕР] БАЛЛИСТИЧЕСКИЙ по цели АКТИВИРОВАН." 43 | 44 | msgid "artyAimCorrection/targetMode/onDeactivate" 45 | msgstr "[ДАЛЬНОМЕР] БАЛЛИСТИЧЕСКИЙ по цели ДЕАКТИВИРОВАН." 46 | 47 | msgid "safeShot/onActivate" 48 | msgstr "[ПРЕДОХРАНИТЕЛЬ] АКТИВИРОВАН." 49 | 50 | msgid "safeShot/onDeactivate" 51 | msgstr "[ПРЕДОХРАНИТЕЛЬ] ДЕАКТИВИРОВАН." 52 | 53 | msgid "safeShot/error/template" 54 | msgstr "[{reason}] Выстрел заблокирован предохранителем." 55 | 56 | msgid "safeShot/reasons/team/chat/message" 57 | msgstr "{player} ({vehicle}), не мешай стрелять!" 58 | 59 | msgid "safeShot/reasons/team/template" 60 | msgstr "союзник" 61 | 62 | msgid "safeShot/reasons/dead/template" 63 | msgstr "труп" 64 | 65 | msgid "safeShot/reasons/waste/template" 66 | msgstr "промах" 67 | 68 | msgid "gui/panels/context/hideInfoPanel" 69 | msgstr "Спрятать эту панель" 70 | 71 | msgid "gui/panels/context/resetInfoPanel" 72 | msgstr "Сбросить динамические настройки" 73 | 74 | msgid "gui/panels/CorrectionPanel/default/tooltip" 75 | msgstr "Панель информации корректировщика дальномера." 76 | 77 | msgid "gui/panels/CorrectionPanel/default/text" 78 | msgstr "

Дальномер: {manualInfo:.1f}м.

" 79 | 80 | msgid "gui/panels/CorrectionPanel/arcade/text" 81 | msgstr "

Дальномер: {manualInfo:.1f}м.

" 82 | 83 | msgid "gui/panels/CorrectionPanel/sniper/text" 84 | msgstr "

Дальномер: {manualInfo:.1f}м.

" 85 | 86 | msgid "gui/panels/CorrectionPanel/strategic/text" 87 | msgstr "

Абсолютная высота: {manualInfo:.1f}м.

" 88 | 89 | msgid "gui/panels/CorrectionPanel/arty/text" 90 | msgstr "

Неопределенный параметр: {manualInfo:.1f}м.

" 91 | 92 | msgid "gui/panels/TargetPanel/default/tooltip" 93 | msgstr "Панель информации сканера целей." 94 | 95 | msgid "gui/panels/TargetPanel/default/text" 96 | msgstr "

Цель: {shortName}; Дистанция: {distance:.1f}м; Скорость: {speedMS:.1f}м/с.

" 97 | 98 | msgid "gui/panels/AimingPanel/default/tooltip" 99 | msgstr "Панель информации о сведении." 100 | 101 | msgid "gui/panels/AimingPanel/default/text" 102 | msgstr "\tОсталось:\t{remainingAimingTime:.2f}с;\n\tРасстояние:\t{aimingDistance:.1f}м;\n\tРазброс:\t{deviation:.2f}м;\n\tВремя полета:\t{flyTime:.2f}с;\n\tУгол падения:\t{hitAngleDeg:+.1f}°;" 103 | 104 | msgid "gui/panels/AimingPanel/arcade/text" 105 | msgstr "\tОсталось:\t{remainingAimingTime:.2f}с;\n\tРасстояние:\t{aimingDistance:.1f}м;\n\tРазброс:\t{deviation:.2f}м;\n\tВремя полета:\t{flyTime:.2f}с;\n\tУгол падения:\t{hitAngleDeg:+.1f}°;" 106 | 107 | msgid "gui/panels/AimingPanel/sniper/text" 108 | msgstr "\tОсталось:\t{remainingAimingTime:.2f}с;\n\tРасстояние:\t{aimingDistance:.1f}м;\n\tРазброс:\t{deviation:.2f}м;\n\tВремя полета:\t{flyTime:.2f}с;\n\tУгол падения:\t{hitAngleDeg:+.1f}°;" 109 | 110 | msgid "gui/panels/AimingPanel/strategic/text" 111 | msgstr "\tОсталось:\t{remainingAimingTime:.2f}с;\n\tРасстояние:\t{aimingDistance:.1f}м;\n\tРазброс:\t{deviation:.2f}м;\n\tВремя полета:\t{flyTime:.2f}с;\n\tУгол падения:\t{hitAngleDeg:+.1f}°;" 112 | 113 | msgid "gui/panels/AimingPanel/arty/text" 114 | msgstr "\tОсталось:\t{remainingAimingTime:.2f}с;\n\tРасстояние:\t{aimingDistance:.1f}м;\n\tРазброс:\t{deviation:.2f}м;\n\tВремя полета:\t{flyTime:.2f}с;\n\tУгол падения:\t{hitAngleDeg:+.1f}°;" 115 | -------------------------------------------------------------------------------- /resource/packages/AdvancedAimingSystem/meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{author}}.{{application}} 12 | 13 | 14 | {{signature}} 15 | 16 | 17 | Advanced Aiming System Mod 18 | 19 | 20 | Корректировка дальномера (аркадный и снайперский режимы прицеливания) и высоты (артиллерийский режим) в ручном и автоматическом режиме для стрельбы по быстро движущимся или пропавшим из засвета целям. Исправляет так называемый "перелет", когда снаряд проходит над целью. Ознакомиться с подробным описанием, а также задать вопрос можно в официальной теме модификации (http://www.koreanrandom.com/forum/topic/16559-/). 21 | 22 | -------------------------------------------------------------------------------- /resource/textures/AimingInfoBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GPCracker/AdvancedAimingSystem/e89004896e37620c473752718e75630b15bfb1cc/resource/textures/AimingInfoBackground.png -------------------------------------------------------------------------------- /source/hooks/Account.py: -------------------------------------------------------------------------------- 1 | # ------------------- # 2 | # Account Hooks # 3 | # ------------------- # 4 | @XModLib.HookUtils.methodHookExt(g_inject_basis, Account.Account, 'onBecomePlayer') 5 | def new_Account_onBecomePlayer(self, *args, **kwargs): 6 | if g_globals['appLoadingMessage']: 7 | XModLib.ClientMessages.SystemMessageFormatter().install(__application__[1]) 8 | def handler(model, entityID, action): 9 | if action == '{0[1]}.official_topic'.format(__application__): 10 | BigWorld.wg_openWebBrowser(__official_topic__) 11 | return 12 | XModLib.ClientMessages.SystemMessageActionHandler(handler).install() 13 | XModLib.ClientMessages.pushSystemMessage( 14 | { 15 | 'message': g_globals['appLoadingMessage'], 16 | 'timestamp': time.time(), 17 | 'icon': 'img://gui/maps/icons/library/InformationIcon-1.png', 18 | }, 19 | __application__[1], 20 | auxData=['Information'] 21 | ) 22 | g_globals['appLoadingMessage'] = None 23 | return 24 | -------------------------------------------------------------------------------- /source/hooks/AvatarInputHandler.py: -------------------------------------------------------------------------------- 1 | # ------------------------------ # 2 | # AvatarInputHandler Hooks # 3 | # ------------------------------ # 4 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.AvatarInputHandler, '__init__', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 5 | def new_AvatarInputHandler_init(self, *args, **kwargs): 6 | config = g_config['modules']['targetScanner'] 7 | self.XTargetScanner = TargetScanner( 8 | targetScanMode=TargetScanMode(**config['scanMode']), 9 | autoScanActivated=config['autoScan']['enabled'] and config['autoScan']['activated'] 10 | ) if config['enabled'] else None 11 | config = g_config['modules']['aimingInfo'] 12 | self.XAimingInfo = AimingInfo( 13 | aimingThreshold=config['aimingThreshold'] 14 | ) if config['enabled'] else None 15 | config = g_config['gui'] 16 | self.XGuiController = GuiController( 17 | updateInterval=config['updateInterval'] 18 | ) if config['enabled'] else None 19 | return 20 | 21 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.AvatarInputHandler, 'start', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 22 | def new_AvatarInputHandler_start(self, *args, **kwargs): 23 | targetScanner = getattr(self, 'XTargetScanner', None) 24 | if targetScanner is not None: 25 | targetScanner.enable() 26 | aimingInfo = getattr(self, 'XAimingInfo', None) 27 | if aimingInfo is not None: 28 | aimingInfo.enable() 29 | guiController = getattr(self, 'XGuiController', None) 30 | if guiController is not None: 31 | guiController.enable() 32 | return 33 | 34 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.AvatarInputHandler, 'stop', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 35 | def new_AvatarInputHandler_stop(self, *args, **kwargs): 36 | targetScanner = getattr(self, 'XTargetScanner', None) 37 | if targetScanner is not None: 38 | targetScanner.disable() 39 | aimingInfo = getattr(self, 'XAimingInfo', None) 40 | if aimingInfo is not None: 41 | aimingInfo.disable() 42 | guiController = getattr(self, 'XGuiController', None) 43 | if guiController is not None: 44 | guiController.disable() 45 | return 46 | 47 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.AvatarInputHandler, 'handleKeyEvent', invoke=XModLib.HookUtils.HookInvoke.MASTER) 48 | def new_AvatarInputHandler_handleKeyEvent(old_AvatarInputHandler_handleKeyEvent, self, event): 49 | result = old_AvatarInputHandler_handleKeyEvent(self, event) 50 | ## Keyboard event parsing 51 | kbevent = XModLib.KeyboardUtils.KeyboardEvent(event) 52 | ## Operating control modes 53 | operatingControlModes = ( 54 | AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARCADE, 55 | AvatarInputHandler.aih_constants.CTRL_MODE_NAME.SNIPER, 56 | AvatarInputHandler.aih_constants.CTRL_MODE_NAME.STRATEGIC, 57 | AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARTY 58 | ) 59 | ## AvatarInputHandler started, control mode supported, event not handled by game (for AvatarInputHandler switches) 60 | if self._AvatarInputHandler__isStarted and self.ctrlModeName in operatingControlModes and not result: 61 | ## HotKeys - TargetScanner 62 | mconfig = g_config['modules']['targetScanner'] 63 | if mconfig['enabled']: 64 | ## HotKeys - TargetScanner - AutoScan 65 | fconfig = mconfig['autoScan'] 66 | shortcutHandle = fconfig['enabled'] and fconfig['shortcut'](kbevent) 67 | if shortcutHandle and (not shortcutHandle.switch or shortcutHandle.pushed): 68 | fconfig['activated'] = shortcutHandle(fconfig['activated']) 69 | if shortcutHandle.switch and fconfig['activated']: 70 | XModLib.ClientMessages.showMessageOnPanel( 71 | 'Player', 72 | None, 73 | fconfig['message']['onActivate'], 74 | 'green' 75 | ) 76 | elif shortcutHandle.switch: 77 | XModLib.ClientMessages.showMessageOnPanel( 78 | 'Player', 79 | None, 80 | fconfig['message']['onDeactivate'], 81 | 'red' 82 | ) 83 | targetScanner = getattr(self, 'XTargetScanner', None) 84 | if targetScanner is not None: 85 | targetScanner.autoScanActivated = fconfig['activated'] 86 | ## HotKeys - TargetScanner - ManualOverride 87 | fconfig = mconfig['manualOverride'] 88 | shortcutHandle = fconfig['enabled'] and fconfig['shortcut'](kbevent) 89 | if shortcutHandle and shortcutHandle.pushed: 90 | targetScanner = getattr(self, 'XTargetScanner', None) 91 | if targetScanner is not None: 92 | targetScanner.engageManualOverride() 93 | ## HotKeys - AimCorrection 94 | mconfig = g_config['modules']['aimCorrection'][self.ctrlModeName] 95 | if mconfig['enabled']: 96 | ## HotKeys - AimCorrection - Target Mode 97 | fconfig = mconfig['targetMode'] 98 | shortcutHandle = fconfig['enabled'] and fconfig['shortcut'](kbevent) 99 | if shortcutHandle and (not shortcutHandle.switch or shortcutHandle.pushed): 100 | fconfig['activated'] = shortcutHandle(fconfig['activated']) 101 | if shortcutHandle.switch and fconfig['activated']: 102 | XModLib.ClientMessages.showMessageOnPanel( 103 | 'Player', 104 | None, 105 | fconfig['message']['onActivate'], 106 | 'green' 107 | ) 108 | elif shortcutHandle.switch: 109 | XModLib.ClientMessages.showMessageOnPanel( 110 | 'Player', 111 | None, 112 | fconfig['message']['onDeactivate'], 113 | 'red' 114 | ) 115 | aimCorrection = getattr(self.ctrl, 'XAimCorrection', None) 116 | if aimCorrection is not None: 117 | aimCorrection.targetEnabled = fconfig['activated'] 118 | ## AvatarInputHandler started, not detached, control mode supported (for AvatarInputHandler shortcuts) 119 | if self._AvatarInputHandler__isStarted and not self.isDetached and self.ctrlModeName in operatingControlModes: 120 | ## HotKeys - AimCorrection 121 | mconfig = g_config['modules']['aimCorrection'][self.ctrlModeName] 122 | if mconfig['enabled']: 123 | ## HotKeys - AimCorrection - ManualMode 124 | fconfig = mconfig['manualMode'] 125 | shortcutHandle = fconfig['enabled'] and fconfig['shortcut'](kbevent) 126 | if shortcutHandle: 127 | aimCorrection = getattr(self.ctrl, 'XAimCorrection', None) 128 | if aimCorrection is not None: 129 | aimCorrection.updateManualInfo(shortcutHandle.pushed) 130 | ## AvatarInputHandler started, event not handled by game (for avatar switches) 131 | if self._AvatarInputHandler__isStarted and not result: 132 | pass 133 | ## AvatarInputHandler started (for avatar shortcuts) 134 | if self._AvatarInputHandler__isStarted: 135 | pass 136 | return result 137 | -------------------------------------------------------------------------------- /source/hooks/BattleEntry.py: -------------------------------------------------------------------------------- 1 | # ----------------------- # 2 | # BattleEntry Hooks # 3 | # ----------------------- # 4 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, gui.Scaleform.battle_entry.BattleEntry, '_getRequiredLibraries', invoke=XModLib.HookUtils.HookInvoke.MASTER) 5 | def new_BattleEntry_getRequiredLibraries(old_BattleEntry_getRequiredLibraries, self, *args, **kwargs): 6 | return old_BattleEntry_getRequiredLibraries(self, *args, **kwargs) + ('{0[1]}.swf'.format(__application__), ) 7 | -------------------------------------------------------------------------------- /source/hooks/BattleShared.py: -------------------------------------------------------------------------------- 1 | # ------------------------ # 2 | # BattleShared Hooks # 3 | # ------------------------ # 4 | @XModLib.HookUtils.staticMethodHookExt(g_inject_hooks, gui.Scaleform.daapi.view.battle.shared, 'getContextMenuHandlers', invoke=XModLib.HookUtils.HookInvoke.MASTER) 5 | def new_BattleShared_getContextMenuHandlers(old_BattleShared_getContextMenuHandlers, *args, **kwargs): 6 | result = old_BattleShared_getContextMenuHandlers(*args, **kwargs) 7 | if g_config['gui']['enabled']: 8 | result += GuiSettings.getContextMenuHandlers() 9 | return result 10 | 11 | @XModLib.HookUtils.staticMethodHookExt(g_inject_hooks, gui.Scaleform.daapi.view.battle.shared, 'getViewSettings', invoke=XModLib.HookUtils.HookInvoke.MASTER) 12 | def new_BattleShared_getViewSettings(old_BattleShared_getViewSettings, *args, **kwargs): 13 | result = old_BattleShared_getViewSettings(*args, **kwargs) 14 | if g_config['gui']['enabled']: 15 | result += GuiSettings.getViewSettings() 16 | return result 17 | 18 | @XModLib.HookUtils.staticMethodHookExt(g_inject_hooks, gui.Scaleform.daapi.view.battle.shared, 'getBusinessHandlers', invoke=XModLib.HookUtils.HookInvoke.MASTER) 19 | def new_BattleShared_getBusinessHandlers(old_BattleShared_getBusinessHandlers, *args, **kwargs): 20 | result = old_BattleShared_getBusinessHandlers(*args, **kwargs) 21 | config = g_config['gui'] 22 | if config['enabled']: 23 | result += ( 24 | GuiBattleBusinessHandler(config['panels']['static'], config['panels']['ingame']), 25 | GuiGlobalBusinessHandler(config['panels']['static'], config['panels']['ingame']) 26 | ) 27 | return result 28 | 29 | # ---------------------- # 30 | # SharedPage Hooks # 31 | # ---------------------- # 32 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, gui.Scaleform.daapi.view.battle.shared.SharedPage, '_populate', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 33 | def new_SharedPage_populate(self, *args, **kwargs): 34 | if g_config['gui']['enabled']: 35 | self.as_createBattlePagePanelS(GuiSettings.CORRECTION_PANEL_ALIAS, 'TextPanel', 0) 36 | self.as_createBattlePagePanelS(GuiSettings.TARGET_PANEL_ALIAS, 'TextPanel', 1) 37 | self.as_createBattlePagePanelS(GuiSettings.AIMING_PANEL_ALIAS, 'TextPanel', 2) 38 | return 39 | -------------------------------------------------------------------------------- /source/hooks/OperatingControlMode.py: -------------------------------------------------------------------------------- 1 | # -------------------------------- # 2 | # OperatingControlMode Hooks # 3 | # -------------------------------- # 4 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.ArcadeControlMode, '__init__') 5 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.SniperControlMode, '__init__') 6 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.StrategicControlMode, '__init__') 7 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.ArtyControlMode, '__init__') 8 | def new_OperatingControlMode_init(self, *args, **kwargs): 9 | # These strict type checks ensure hooks will work only in original classes themselves, but not in their subclasses. 10 | if type(self) is AvatarInputHandler.control_modes.ArcadeControlMode: 11 | config = g_config['modules']['aimCorrection'][AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARCADE] 12 | self.XAimCorrection = ArcadeAimCorrection( 13 | self, 14 | fixGunMarker=config['fixGunMarker'], 15 | manualEnabled=config['manualMode']['enabled'], 16 | targetEnabled=config['targetMode']['enabled'] and config['targetMode']['activated'], 17 | minDistance=config['targetMode']['distance'][0], 18 | maxDistance=config['targetMode']['distance'][1] 19 | ) if config['enabled'] else None 20 | elif type(self) is AvatarInputHandler.control_modes.SniperControlMode: 21 | config = g_config['modules']['aimCorrection'][AvatarInputHandler.aih_constants.CTRL_MODE_NAME.SNIPER] 22 | self.XAimCorrection = SniperAimCorrection( 23 | self, 24 | fixGunMarker=config['fixGunMarker'], 25 | manualEnabled=config['manualMode']['enabled'], 26 | targetEnabled=config['targetMode']['enabled'] and config['targetMode']['activated'], 27 | minDistance=config['targetMode']['distance'][0], 28 | maxDistance=config['targetMode']['distance'][1] 29 | ) if config['enabled'] else None 30 | elif type(self) is AvatarInputHandler.control_modes.StrategicControlMode: 31 | config = g_config['modules']['aimCorrection'][AvatarInputHandler.aih_constants.CTRL_MODE_NAME.STRATEGIC] 32 | self.XAimCorrection = StrategicAimCorrection( 33 | self, 34 | fixGunMarker=config['fixGunMarker'], 35 | manualEnabled=config['manualMode']['enabled'], 36 | targetEnabled=config['targetMode']['enabled'] and config['targetMode']['activated'], 37 | ignoreVehicles=config['ignoreVehicles'], 38 | heightMultiplier=config['targetMode']['heightMultiplier'] 39 | ) if config['enabled'] else None 40 | elif type(self) is AvatarInputHandler.control_modes.ArtyControlMode: 41 | config = g_config['modules']['aimCorrection'][AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARTY] 42 | self.XAimCorrection = ArtyAimCorrection( 43 | self, 44 | fixGunMarker=config['fixGunMarker'], 45 | manualEnabled=config['manualMode']['enabled'], 46 | targetEnabled=config['targetMode']['enabled'] and config['targetMode']['activated'] 47 | ) if config['enabled'] else None 48 | return 49 | 50 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.ArcadeControlMode, 'enable', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 51 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.SniperControlMode, 'enable', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 52 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.StrategicControlMode, 'enable', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 53 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.ArtyControlMode, 'enable', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 54 | def new_OperatingControlMode_enable(self, *args, **kwargs): 55 | # These strict type checks ensure hooks will work only in original classes themselves, but not in their subclasses. 56 | if type(self) in (AvatarInputHandler.control_modes.ArcadeControlMode, AvatarInputHandler.control_modes.SniperControlMode, AvatarInputHandler.control_modes.StrategicControlMode, AvatarInputHandler.control_modes.ArtyControlMode): 57 | aimCorrection = getattr(self, 'XAimCorrection', None) 58 | if aimCorrection is not None: 59 | aimCorrection.enable() 60 | targetScanner = getattr(self._aih, 'XTargetScanner', None) 61 | if targetScanner is not None and not targetScanner.isUpdateActive: 62 | targetScanner.start() 63 | guiController = getattr(self._aih, 'XGuiController', None) 64 | if guiController is not None and not guiController.isUpdateActive: 65 | guiController.start() 66 | return 67 | 68 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.ArcadeControlMode, 'disable', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 69 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.SniperControlMode, 'disable', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 70 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.StrategicControlMode, 'disable', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 71 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.ArtyControlMode, 'disable', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 72 | def new_OperatingControlMode_disable(self, *args, **kwargs): 73 | # These strict type checks ensure hooks will work only in original classes themselves, but not in their subclasses. 74 | if type(self) in (AvatarInputHandler.control_modes.ArcadeControlMode, AvatarInputHandler.control_modes.SniperControlMode, AvatarInputHandler.control_modes.StrategicControlMode, AvatarInputHandler.control_modes.ArtyControlMode): 75 | aimCorrection = getattr(self, 'XAimCorrection', None) 76 | if aimCorrection is not None: 77 | aimCorrection.disable() 78 | targetScanner = getattr(self._aih, 'XTargetScanner', None) 79 | if targetScanner is not None and targetScanner.isUpdateActive: 80 | targetScanner.stop() 81 | guiController = getattr(self._aih, 'XGuiController', None) 82 | if guiController is not None and guiController.isUpdateActive: 83 | guiController.stop() 84 | return 85 | 86 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.ArcadeControlMode, 'getDesiredShotPoint', invoke=XModLib.HookUtils.HookInvoke.MASTER) 87 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.SniperControlMode, 'getDesiredShotPoint', invoke=XModLib.HookUtils.HookInvoke.MASTER) 88 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.StrategicControlMode, 'getDesiredShotPoint', invoke=XModLib.HookUtils.HookInvoke.MASTER) 89 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, AvatarInputHandler.control_modes.ArtyControlMode, 'getDesiredShotPoint', invoke=XModLib.HookUtils.HookInvoke.MASTER) 90 | def new_OperatingControlMode_getDesiredShotPoint(old_OperatingControlMode_getDesiredShotPoint, self, *args, **kwargs): 91 | shotPoint = old_OperatingControlMode_getDesiredShotPoint(self, *args, **kwargs) 92 | # These strict type checks ensure hooks will work only in original classes themselves, but not in their subclasses. 93 | if type(self) in (AvatarInputHandler.control_modes.ArcadeControlMode, AvatarInputHandler.control_modes.SniperControlMode, AvatarInputHandler.control_modes.StrategicControlMode, AvatarInputHandler.control_modes.ArtyControlMode): 94 | aimCorrection = getattr(self, 'XAimCorrection', None) 95 | if aimCorrection is not None: 96 | return aimCorrection.getDesiredShotPoint(shotPoint) 97 | return shotPoint 98 | -------------------------------------------------------------------------------- /source/hooks/Vehicle.py: -------------------------------------------------------------------------------- 1 | # ------------------- # 2 | # Vehicle Hooks # 3 | # ------------------- # 4 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, Vehicle.Vehicle, 'startVisual', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 5 | def new_Vehicle_startVisual(self, *args, **kwargs): 6 | if not hasattr(self, 'collisionBounds'): 7 | setattr(self, 'collisionBounds', XModLib.VehicleBounds.getVehicleBoundsMatrixProvider(self)) 8 | return 9 | 10 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, Vehicle.Vehicle, 'stopVisual', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 11 | def new_Vehicle_stopVisual(self, *args, **kwargs): 12 | if hasattr(self, 'collisionBounds'): 13 | delattr(self, 'collisionBounds') 14 | return 15 | 16 | @XModLib.HookUtils.methodHookExt(g_inject_hooks, Vehicle.Vehicle, '_Vehicle__onVehicleDeath') 17 | def new_Vehicle_onVehicleDeath(self, isDeadStarted=False): 18 | targetScanner = getattr(BigWorld.player().inputHandler, 'XTargetScanner', None) 19 | if targetScanner is not None: 20 | targetScanner.handleVehicleDeath(self) 21 | return 22 | -------------------------------------------------------------------------------- /source/local/AimCorrection.py: -------------------------------------------------------------------------------- 1 | # --------------------------- # 2 | # AimCorrection Classes # 3 | # --------------------------- # 4 | class BaseAimCorrection(object): 5 | __slots__ = ('__weakref__', '_aihc', 'manualEnabled', 'targetEnabled', 'fixGunMarker', 'manualInfo') 6 | 7 | def __init__(self, avatarInputHandlerCtrl, manualEnabled=False, targetEnabled=False, fixGunMarker=False): 8 | super(BaseAimCorrection, self).__init__() 9 | self._aihc = weakref.proxy(avatarInputHandlerCtrl) 10 | self.manualEnabled = manualEnabled 11 | self.targetEnabled = targetEnabled 12 | self.fixGunMarker = fixGunMarker 13 | self.manualInfo = None 14 | return 15 | 16 | @property 17 | def targetInfo(self): 18 | return getattr(BigWorld.player().inputHandler, 'XTargetInfo', None) 19 | 20 | def getMacroData(self): 21 | return { 22 | 'manualInfo': self.manualInfo 23 | } if self.manualEnabled and self.manualInfo is not None else None 24 | 25 | def setManualInfo(self): 26 | if self.manualEnabled: 27 | self.manualInfo = None 28 | return 29 | 30 | def resetManualInfo(self): 31 | if self.manualEnabled: 32 | self.manualInfo = None 33 | return 34 | 35 | def updateManualInfo(self, setRequired=True, resetRequired=True): 36 | if resetRequired: 37 | self.resetManualInfo() 38 | if setRequired: 39 | self.setManualInfo() 40 | return 41 | 42 | def enable(self): 43 | self.manualInfo = None 44 | return 45 | 46 | def disable(self): 47 | self.manualInfo = None 48 | return 49 | 50 | def _getManualDesiredShotPoint(self, shotPoint): 51 | return None 52 | 53 | def _getTargetDesiredShotPoint(self, shotPoint): 54 | return None 55 | 56 | def getDesiredShotPoint(self, shotPoint): 57 | return self._getManualDesiredShotPoint(shotPoint) or self._getTargetDesiredShotPoint(shotPoint) or shotPoint 58 | 59 | def getGunMarkerCollisionPoint(self, start, end): 60 | return None 61 | 62 | def __repr__(self): 63 | return '{!s}(avatarInputHandlerCtrl={!r}, manualEnabled={!r}, targetEnabled={!r})'.format( 64 | self.__class__.__name__, 65 | self._aihc, self.manualEnabled, self.targetEnabled 66 | ) 67 | 68 | def __del__(self): 69 | return 70 | 71 | class ArcadeAimCorrection(BaseAimCorrection): 72 | __slots__ = ('minDistance', 'maxDistance') 73 | 74 | def __init__(self, avatarInputHandlerCtrl, manualEnabled=False, targetEnabled=False, fixGunMarker=True, minDistance=50.0, maxDistance=720.0): 75 | super(ArcadeAimCorrection, self).__init__(avatarInputHandlerCtrl, manualEnabled, targetEnabled, fixGunMarker) 76 | self.minDistance = minDistance 77 | self.maxDistance = maxDistance 78 | return 79 | 80 | def _getScanRayAndPoint(self): 81 | aimingSystemMatrix = self._aihc.camera.aimingSystem.matrix 82 | return aimingSystemMatrix.applyToAxis(2), aimingSystemMatrix.applyToOrigin() 83 | 84 | def _getPositionAboveVehicle(self): 85 | return self._aihc.camera.aimingSystem.positionAboveVehicleProv.value[0:3] 86 | 87 | def setManualInfo(self): 88 | if self.manualEnabled: 89 | shotPoint = self._aihc.getDesiredShotPoint() 90 | if shotPoint is not None: 91 | self.manualInfo = self._getPositionAboveVehicle().flatDistTo(shotPoint) 92 | return 93 | 94 | def _getManualDesiredShotPoint(self, shotPoint): 95 | if self.manualEnabled and shotPoint is not None: 96 | if self.manualInfo is not None: 97 | scanRay, scanPoint = self._getScanRayAndPoint() 98 | flatDistance = scanPoint.flatDistTo(self._getPositionAboveVehicle()) + self.manualInfo 99 | flatScanRayLength = scanRay.flatDistTo(Math.Vector3(0.0, 0.0, 0.0)) 100 | return scanPoint + scanRay.scale(flatDistance / flatScanRayLength) 101 | return None 102 | 103 | def _getTargetDesiredShotPoint(self, shotPoint): 104 | if self.targetEnabled and shotPoint is not None: 105 | if self.targetInfo is not None and not self.targetInfo.isExpired: 106 | target = BigWorld.target() 107 | if target is None or target.id != self.targetInfo: 108 | scanRay, scanPoint = self._getScanRayAndPoint() 109 | if self.minDistance <= self.targetInfo.getDistance() <= self.maxDistance: 110 | flatDistance = scanPoint.flatDistTo(self.targetInfo.getPosition()) 111 | flatScanRayLength = scanRay.flatDistTo(Math.Vector3(0.0, 0.0, 0.0)) 112 | return scanPoint + scanRay.scale(flatDistance / flatScanRayLength) 113 | return None 114 | 115 | def getGunMarkerCollisionPoint(self, start, end): 116 | if self.fixGunMarker: 117 | flatDistance = None 118 | positionAboveVehicle = self._getPositionAboveVehicle() 119 | if self.manualEnabled and self.manualInfo is not None: 120 | flatDistance = self.manualInfo 121 | elif self.targetEnabled and self.targetInfo is not None and not self.targetInfo.isExpired: 122 | flatDistance = positionAboveVehicle.flatDistTo(self.targetInfo.getPosition()) 123 | if flatDistance is not None: 124 | if positionAboveVehicle.flatDistSqrTo(start) <= flatDistance * flatDistance <= positionAboveVehicle.flatDistSqrTo(end): 125 | return start + (end - start).scale((flatDistance - positionAboveVehicle.flatDistTo(start)) / start.flatDistTo(end)) 126 | return None 127 | 128 | def __repr__(self): 129 | return '{!s}(avatarInputHandlerCtrl={!r}, manualEnabled={!r}, targetEnabled={!r}, minDistance={!r}, maxDistance={!r})'.format( 130 | self.__class__.__name__, 131 | self._aihc, self.manualEnabled, self.targetEnabled, self.minDistance, self.maxDistance 132 | ) 133 | 134 | class SniperAimCorrection(BaseAimCorrection): 135 | __slots__ = ('minDistance', 'maxDistance') 136 | 137 | def __init__(self, avatarInputHandlerCtrl, manualEnabled=False, targetEnabled=False, fixGunMarker=True, minDistance=10.0, maxDistance=720.0): 138 | super(SniperAimCorrection, self).__init__(avatarInputHandlerCtrl, manualEnabled, targetEnabled, fixGunMarker) 139 | self.minDistance = minDistance 140 | self.maxDistance = maxDistance 141 | return 142 | 143 | def _getScanRayAndPoint(self): 144 | aimingSystemMatrix = self._aihc.camera.aimingSystem.matrix 145 | return aimingSystemMatrix.applyToAxis(2), aimingSystemMatrix.applyToOrigin() 146 | 147 | def setManualInfo(self): 148 | if self.manualEnabled: 149 | shotPoint = self._aihc.getDesiredShotPoint() 150 | if shotPoint is not None: 151 | scanRay, scanPoint = self._getScanRayAndPoint() 152 | self.manualInfo = (shotPoint - scanPoint).length 153 | return 154 | 155 | def _getManualDesiredShotPoint(self, shotPoint): 156 | if self.manualEnabled and shotPoint is not None: 157 | if self.manualInfo is not None: 158 | scanRay, scanPoint = self._getScanRayAndPoint() 159 | return scanPoint + scanRay.scale(self.manualInfo) 160 | return None 161 | 162 | def _getTargetDesiredShotPoint(self, shotPoint): 163 | if self.targetEnabled and shotPoint is not None: 164 | if self.targetInfo is not None and not self.targetInfo.isExpired: 165 | target = BigWorld.target() 166 | if target is None or target.id != self.targetInfo: 167 | scanRay, scanPoint = self._getScanRayAndPoint() 168 | if self.minDistance <= self.targetInfo.getDistance() <= self.maxDistance: 169 | return scanPoint + scanRay.scale(scanPoint.distTo(self.targetInfo.getPosition())) 170 | return None 171 | 172 | def getGunMarkerCollisionPoint(self, start, end): 173 | if self.fixGunMarker: 174 | distance = None 175 | scanRay, scanPoint = self._getScanRayAndPoint() 176 | if self.manualEnabled and self.manualInfo is not None: 177 | distance = self.manualInfo 178 | elif self.targetEnabled and self.targetInfo is not None and not self.targetInfo.isExpired: 179 | distance = scanPoint.distTo(self.targetInfo.getPosition()) 180 | if distance is not None: 181 | if scanPoint.distSqrTo(start) <= distance * distance <= scanPoint.distSqrTo(end): 182 | # Scan point is generally far from small segment of collision test. 183 | # In that case we can consider that vectors from scan point to start and end is parallel. 184 | # So we can use linear algorithm instead of square one to get some calculation speed. 185 | baseDistance = scanPoint.distTo(start) 186 | return start + (end - start).scale((distance - baseDistance) / (scanPoint.distTo(end) - baseDistance)) 187 | return None 188 | 189 | def __repr__(self): 190 | return '{!s}(avatarInputHandlerCtrl={!r}, manualEnabled={!r}, targetEnabled={!r}, minDistance={!r}, maxDistance={!r})'.format( 191 | self.__class__.__name__, 192 | self._aihc, self.manualEnabled, self.targetEnabled, self.minDistance, self.maxDistance 193 | ) 194 | 195 | class StrategicAimCorrection(BaseAimCorrection): 196 | __slots__ = ('ignoreVehicles', 'heightMultiplier') 197 | 198 | def __init__(self, avatarInputHandlerCtrl, manualEnabled=False, targetEnabled=False, fixGunMarker=False, ignoreVehicles=False, heightMultiplier=0.5): 199 | super(StrategicAimCorrection, self).__init__(avatarInputHandlerCtrl, manualEnabled, targetEnabled, fixGunMarker) 200 | self.ignoreVehicles = ignoreVehicles 201 | self.heightMultiplier = heightMultiplier 202 | return 203 | 204 | def _getScanRayAndPoint(self): 205 | aimingSystemMatrix = self._aihc.camera.aimingSystem.matrix 206 | return Math.Vector3(0.0, -1.0, 0.0), aimingSystemMatrix.applyToOrigin() 207 | 208 | def setManualInfo(self): 209 | if self.manualEnabled: 210 | shotPoint = self._aihc.getDesiredShotPoint() 211 | if shotPoint is not None: 212 | self.manualInfo = shotPoint.y 213 | return 214 | 215 | def _getManualDesiredShotPoint(self, shotPoint): 216 | if self.manualEnabled and shotPoint is not None: 217 | if self.manualInfo is not None: 218 | result = Math.Vector3(shotPoint) 219 | result.y = self.manualInfo 220 | return result 221 | return None 222 | 223 | def _getTargetDesiredShotPoint(self, shotPoint): 224 | if self.targetEnabled and shotPoint is not None: 225 | if self.targetInfo is not None and not self.targetInfo.isExpired: 226 | target = BigWorld.target() 227 | if target is None or target.id != self.targetInfo or self.ignoreVehicles: 228 | return shotPoint + self.targetInfo.getHeightVector().scale(self.heightMultiplier) 229 | return None 230 | 231 | def _getGroundDesiredShotPoint(self, shotPoint): 232 | if self.ignoreVehicles and shotPoint is not None: 233 | scanRay, scanPoint = self._getScanRayAndPoint() 234 | result = XModLib.CollisionUtils.collideStatic(scanPoint, scanPoint + scanRay.scale(10000.0)) 235 | return result.closestPoint if result is not None else None 236 | return None 237 | 238 | def getDesiredShotPoint(self, shotPoint): 239 | return super(StrategicAimCorrection, self).getDesiredShotPoint(self._getGroundDesiredShotPoint(shotPoint) or shotPoint) 240 | 241 | def getGunMarkerCollisionPoint(self, start, end): 242 | # This is not required in strategic mode - gun marker is always on ground. 243 | return None 244 | 245 | def __repr__(self): 246 | return '{!s}(avatarInputHandlerCtrl={!r}, manualEnabled={!r}, targetEnabled={!r}, ignoreVehicles={!r}, heightMultiplier={!r})'.format( 247 | self.__class__.__name__, 248 | self._aihc, self.manualEnabled, self.targetEnabled, self.ignoreVehicles, self.heightMultiplier 249 | ) 250 | 251 | class ArtyAimCorrection(BaseAimCorrection): 252 | __slots__ = () 253 | -------------------------------------------------------------------------------- /source/local/AimingInfo.py: -------------------------------------------------------------------------------- 1 | # ------------------------ # 2 | # AimingInfo Classes # 3 | # ------------------------ # 4 | class AimingInfo(object): 5 | __slots__ = ('__weakref__', 'aimingThreshold') 6 | 7 | def __init__(self, aimingThreshold=1.05): 8 | super(AimingInfo, self).__init__() 9 | self.aimingThreshold = aimingThreshold 10 | return 11 | 12 | def getMacroData(self): 13 | playerAimingInfo = XModLib.BallisticsMath.getPlayerAimingInfo() 14 | if playerAimingInfo is not None: 15 | staticDispersionAngle, aimingStartTime, aimingStartFactor, dispersionFactor, expAimingTime = playerAimingInfo 16 | aimingFactor = XModLib.BallisticsMath.getAimingFactor( 17 | aimingStartTime, aimingStartFactor, dispersionFactor, expAimingTime, 18 | aimingThreshold=self.aimingThreshold 19 | ) 20 | fullAimingTime = XModLib.BallisticsMath.getFullAimingTime(aimingStartFactor, dispersionFactor, expAimingTime) 21 | remainingAimingTime = XModLib.BallisticsMath.getRemainingAimingTime(aimingStartTime, fullAimingTime) 22 | realDispersionAngle = XModLib.BallisticsMath.getDispersionAngle(staticDispersionAngle, aimingFactor) 23 | aimingDistance, flyTime, shotAngleRad, hitAngleRad = XModLib.BallisticsMath.getPlayerBallisticsInfo() 24 | deviation = XModLib.BallisticsMath.getDeviation(realDispersionAngle, aimingDistance) 25 | shotAngleDeg = math.degrees(shotAngleRad) 26 | hitAngleDeg = math.degrees(hitAngleRad) 27 | return { 28 | 'expAimingTime': expAimingTime, 29 | 'fullAimingTime': fullAimingTime, 30 | 'remainingAimingTime': remainingAimingTime, 31 | 'staticDispersionAngle': staticDispersionAngle, 32 | 'realDispersionAngle': realDispersionAngle, 33 | 'dispersionFactor': dispersionFactor, 34 | 'aimingDistance': aimingDistance, 35 | 'aimingFactor': aimingFactor, 36 | 'shotAngleRad': shotAngleRad, 37 | 'shotAngleDeg': shotAngleDeg, 38 | 'hitAngleRad': hitAngleRad, 39 | 'hitAngleDeg': hitAngleDeg, 40 | 'deviation': deviation, 41 | 'flyTime': flyTime 42 | } 43 | return None 44 | 45 | def enable(self): 46 | # nothing 47 | return 48 | 49 | def disable(self): 50 | # nothing 51 | return 52 | 53 | def __repr__(self): 54 | return '{!s}(aimingThreshold={!r})'.format(self.__class__.__name__, self.aimingThreshold) 55 | 56 | def __del__(self): 57 | return 58 | -------------------------------------------------------------------------------- /source/local/TargetInfo.py: -------------------------------------------------------------------------------- 1 | # ------------------------ # 2 | # TargetInfo Classes # 3 | # ------------------------ # 4 | class TargetInfo(int): 5 | __slots__ = ('__weakref__', 'lastLockTime', 'expiryTimeout', 'relockTimeout', 'shortName', '_height', '_lastHeightVector', '_lastPosition') 6 | 7 | def __new__(cls, target, *args, **kwargs): 8 | return super(TargetInfo, cls).__new__(cls, target.id) if XModLib.VehicleInfo.isVehicle(target) else None 9 | 10 | def __init__(self, target, lastLockTime=None, expiryTimeout=10.0, relockTimeout=0.16): 11 | super(TargetInfo, self).__init__(target.id) 12 | self.lastLockTime = lastLockTime 13 | self.expiryTimeout = expiryTimeout 14 | self.relockTimeout = relockTimeout 15 | self.shortName = XModLib.ArenaInfo.getShortName(target.id) 16 | self._height = XModLib.VehicleMath.getVehicleHeight(target) 17 | self._lastHeightVector = XModLib.VehicleMath.getVehicleHeightVector(target, self._height) 18 | self._lastPosition = target.position 19 | return 20 | 21 | @property 22 | def isAutoLocked(self): 23 | return self.lastLockTime is not None 24 | 25 | @property 26 | def isExpired(self): 27 | return self.lastLockTime is not None and self.lastLockTime + self.expiryTimeout <= BigWorld.time() 28 | 29 | @property 30 | def isInsight(self): 31 | return self.lastLockTime is None or self.lastLockTime + self.relockTimeout >= BigWorld.time() 32 | 33 | def getMacroData(self): 34 | speed = self.getSpeed() or 0.0 35 | return { 36 | 'insight': self.isInsight, 37 | 'shortName': self.shortName, 38 | 'distance': self.getDistance() or 0.0, 39 | 'speedMS': speed, 40 | 'speedMH': speed * 2.24, 41 | 'speedKMH': speed * 3.6 42 | } if not self.isExpired else None 43 | 44 | def getVehicle(self): 45 | return BigWorld.entity(self) 46 | 47 | def getSpeed(self): 48 | vehicle = self.getVehicle() 49 | return abs(vehicle.filter.speedInfo.value[0]) if vehicle is not None else None 50 | 51 | def getVelocity(self): 52 | # This method is obsolete and can not longer be used. 53 | # New vehicle filter does not provide velocity vector value. 54 | vehicle = self.getVehicle() 55 | return vehicle.velocity if vehicle is not None else None 56 | 57 | def getPosition(self, actualOnly=False): 58 | vehicle = self.getVehicle() 59 | if vehicle is not None: 60 | self._lastPosition = vehicle.position 61 | return self._lastPosition 62 | return self._lastPosition if not actualOnly else None 63 | 64 | def getDistance(self, actualOnly=False): 65 | position = self.getPosition(actualOnly) 66 | return position.distTo(BigWorld.player().getOwnVehiclePosition()) if position is not None else None 67 | 68 | def getHeightVector(self, actualOnly=False): 69 | vehicle = self.getVehicle() 70 | if vehicle is not None: 71 | self._lastHeightVector = XModLib.VehicleMath.getVehicleHeightVector(vehicle, self._height) 72 | return self._lastHeightVector 73 | return self._lastHeightVector if not actualOnly else None 74 | 75 | def __repr__(self): 76 | return '{!s}(target=BigWorld.entity({!s}), lastLockTime={!r}, expiryTimeout={!r}, relockTimeout={!r})'.format( 77 | self.__class__.__name__, 78 | int.__repr__(self), self.lastLockTime, self.expiryTimeout, self.relockTimeout 79 | ) 80 | 81 | def __del__(self): 82 | return 83 | -------------------------------------------------------------------------------- /source/local/TargetScanner.py: -------------------------------------------------------------------------------- 1 | # --------------------------- # 2 | # TargetScanner Classes # 3 | # --------------------------- # 4 | class TargetScanMode(tuple): 5 | __slots__ = () 6 | 7 | _fields, _defaults = zip( 8 | ('useStandardMode', True), 9 | ('useXRayMode', False), 10 | ('useBBoxMode', False), 11 | ('useBEpsMode', False), 12 | ('maxDistance', 720.0), 13 | ('boundsScalar', 2.5), 14 | ('autoScanInterval', 0.04), 15 | ('autoScanExpiryTimeout', 10.0), 16 | ('autoScanRelockTimeout', 0.16) 17 | ) 18 | 19 | @staticmethod 20 | def filterID(vehicleID): 21 | return XModLib.ArenaInfo.isEnemy(vehicleID) 22 | 23 | @staticmethod 24 | def filterVehicle(vehicle): 25 | return XModLib.VehicleInfo.isAlive(vehicle) 26 | 27 | def __new__(cls, **kwargs): 28 | return super(TargetScanMode, cls).__new__(cls, itertools.imap(kwargs.get, cls._fields, cls._defaults)) 29 | 30 | def __getattr__(self, name): 31 | if name not in self._fields: 32 | raise AttributeError('{!r} object has no attribute {!r}'.format(self.__class__.__name__, name)) 33 | return operator.getitem(self, self._fields.index(name)) 34 | 35 | def __repr__(self): 36 | args = itertools.imap('{!s}={!r}'.format, self._fields, self) 37 | return '{!s}({!s})'.format(self.__class__.__name__, ', '.join(args)) 38 | 39 | class TargetScanResultCategory(enum.Enum): 40 | NOTHING = 'nothing' 41 | PRIMARY = 'primary' 42 | SECONDARY = 'secondary' 43 | AMBIGUOUS = 'ambiguous' 44 | 45 | class TargetScanResult(collections.namedtuple('TargetScanResult', ('category', 'target'))): 46 | __slots__ = () 47 | 48 | @property 49 | def isNothing(self): 50 | return self.category == TargetScanResultCategory.NOTHING 51 | 52 | @property 53 | def isPrimary(self): 54 | return self.category == TargetScanResultCategory.PRIMARY 55 | 56 | @property 57 | def isSecondary(self): 58 | return self.category == TargetScanResultCategory.SECONDARY 59 | 60 | @property 61 | def isAmbiguous(self): 62 | return self.category == TargetScanResultCategory.AMBIGUOUS 63 | 64 | class TargetScanner(object): 65 | __slots__ = ('__weakref__', 'autoScanActivated', '_targetScanMode', '_standardScanner', '_xrayScanner', '_bboxScanner', '_bepsScanner', '_updateCallbackLoop') 66 | 67 | @property 68 | def targetInfo(self): 69 | return getattr(BigWorld.player().inputHandler, 'XTargetInfo', None) 70 | 71 | @targetInfo.setter 72 | def targetInfo(self, value): 73 | setattr(BigWorld.player().inputHandler, 'XTargetInfo', value) 74 | return 75 | 76 | @property 77 | def targetScanMode(self): 78 | return self._targetScanMode 79 | 80 | @targetScanMode.setter 81 | def targetScanMode(self, value): 82 | if self.isUpdateActive: 83 | raise RuntimeError('target scan mode could not be changed while scanner is running') 84 | self._targetScanMode = value 85 | # Recreate target scanners and callback loop. 86 | self._initInternalComponents() 87 | return 88 | 89 | def __init__(self, targetScanMode=TargetScanMode(), autoScanActivated=True): 90 | super(TargetScanner, self).__init__() 91 | self._targetScanMode = targetScanMode 92 | self.autoScanActivated = autoScanActivated 93 | # Initialize target scanners and callback loop. 94 | self._initInternalComponents() 95 | return 96 | 97 | def _initInternalComponents(self): 98 | self._standardScanner = XModLib.TargetScanners.StandardScanner( 99 | self._targetScanMode.filterID, 100 | self._targetScanMode.filterVehicle 101 | ) 102 | self._xrayScanner = XModLib.TargetScanners.XRayScanner( 103 | self._targetScanMode.filterID, 104 | self._targetScanMode.filterVehicle, 105 | self._targetScanMode.maxDistance 106 | ) 107 | self._bboxScanner = XModLib.TargetScanners.BBoxScanner( 108 | self._targetScanMode.filterID, 109 | self._targetScanMode.filterVehicle, 110 | self._targetScanMode.maxDistance, 111 | self._targetScanMode.boundsScalar 112 | ) 113 | self._bepsScanner = XModLib.TargetScanners.BEllipseScanner( 114 | self._targetScanMode.filterID, 115 | self._targetScanMode.filterVehicle, 116 | self._targetScanMode.maxDistance, 117 | self._targetScanMode.boundsScalar 118 | ) 119 | self._updateCallbackLoop = XModLib.CallbackUtils.CallbackLoop( 120 | self._targetScanMode.autoScanInterval, 121 | XModLib.CallbackUtils.getMethodProxy(self._updateTargetInfo) 122 | ) 123 | return 124 | 125 | def enable(self): 126 | # nothing 127 | return 128 | 129 | def disable(self): 130 | # nothing 131 | return 132 | 133 | def _performScanningProcedure(self): 134 | collidableEntities = XModLib.TargetScanners.getCollidableEntities( 135 | self._targetScanMode.filterID, 136 | self._targetScanMode.filterVehicle 137 | ) 138 | primaryTarget = ( 139 | self._standardScanner.getTarget() if self._targetScanMode.useStandardMode else None 140 | ) or ( 141 | self._xrayScanner.getTarget(collidableEntities) if self._targetScanMode.useXRayMode else None 142 | ) 143 | secondaryTargets = set( 144 | self._bboxScanner.getTargets(collidableEntities) if self._targetScanMode.useBBoxMode else [] 145 | ) | set( 146 | self._bepsScanner.getTargets(collidableEntities) if self._targetScanMode.useBEpsMode else [] 147 | ) if primaryTarget is None else set([]) 148 | return primaryTarget, secondaryTargets 149 | 150 | def scanTarget(self): 151 | primaryTarget, secondaryTargets = self._performScanningProcedure() 152 | if primaryTarget is not None: 153 | return TargetScanResult(TargetScanResultCategory.PRIMARY, primaryTarget) 154 | if len(secondaryTargets) == 1: 155 | return TargetScanResult(TargetScanResultCategory.SECONDARY, secondaryTargets.pop()) 156 | if secondaryTargets: 157 | return TargetScanResult(TargetScanResultCategory.AMBIGUOUS, None) 158 | return TargetScanResult(TargetScanResultCategory.NOTHING, None) 159 | 160 | def _updateTargetInfo(self): 161 | if self.isManualOverrideInEffect or not self.autoScanActivated: 162 | return 163 | primaryTarget, secondaryTargets = self._performScanningProcedure() 164 | if primaryTarget is not None: 165 | if self.targetInfo is not None and self.targetInfo.isAutoLocked and primaryTarget.id == self.targetInfo: 166 | self.targetInfo.lastLockTime = BigWorld.time() 167 | elif self.targetInfo is None or self.targetInfo.isAutoLocked: 168 | self.targetInfo = TargetInfo( 169 | primaryTarget, 170 | lastLockTime=BigWorld.time(), 171 | expiryTimeout=self._targetScanMode.autoScanExpiryTimeout, 172 | relockTimeout=self._targetScanMode.autoScanRelockTimeout 173 | ) 174 | elif len(secondaryTargets) == 1 and (self.targetInfo is None or self.targetInfo.isExpired): 175 | self.targetInfo = TargetInfo( 176 | secondaryTargets.pop(), 177 | lastLockTime=BigWorld.time(), 178 | expiryTimeout=self._targetScanMode.autoScanExpiryTimeout, 179 | relockTimeout=self._targetScanMode.autoScanRelockTimeout 180 | ) 181 | elif self.targetInfo is not None and self.targetInfo.isAutoLocked and not self.targetInfo.isExpired and self.targetInfo.getVehicle() in secondaryTargets: 182 | self.targetInfo.lastLockTime = BigWorld.time() 183 | return 184 | 185 | def handleVehicleDeath(self, vehicle): 186 | if vehicle.id == self.targetInfo: 187 | self.targetInfo = None 188 | return 189 | 190 | @property 191 | def isManualOverrideInEffect(self): 192 | return self.targetInfo is not None and not self.targetInfo.isAutoLocked 193 | 194 | def engageManualOverride(self): 195 | primaryTarget, secondaryTargets = self._performScanningProcedure() 196 | if primaryTarget is not None: 197 | self.targetInfo = TargetInfo(primaryTarget) 198 | elif len(secondaryTargets) == 1: 199 | self.targetInfo = TargetInfo(secondaryTargets.pop()) 200 | elif not secondaryTargets: 201 | self.targetInfo = None 202 | return 203 | 204 | def disengageManualOverride(self): 205 | self.targetInfo = None 206 | return 207 | 208 | @property 209 | def isUpdateActive(self): 210 | return self._updateCallbackLoop.isActive 211 | 212 | def start(self, delay=None): 213 | self._updateCallbackLoop.start(delay) 214 | return 215 | 216 | def stop(self): 217 | self._updateCallbackLoop.stop() 218 | return 219 | 220 | def __repr__(self): 221 | return '{!s}(targetScanMode={!r}, autoScanActivated={!r})'.format( 222 | self.__class__.__name__, 223 | self._targetScanMode, self.autoScanActivated 224 | ) 225 | 226 | def __del__(self): 227 | self._updateCallbackLoop = None 228 | return 229 | -------------------------------------------------------------------------------- /source/main/config.py: -------------------------------------------------------------------------------- 1 | # -------------------------------------- # 2 | # Application configuration reader # 3 | # -------------------------------------- # 4 | g_globals['appConfigReader'] = XModLib.XMLConfigReader.XMLConfigReader(( 5 | ('SimpleShortcut', XModLib.XMLConfigReader.DataObjectXMLReaderMeta.construct( 6 | 'SimpleShortcutXMLReader', 7 | constructor=lambda shortcut, **kwargs: XModLib.KeyboardUtils.Shortcut(shortcut, **kwargs), 8 | sectionType='String' 9 | )), 10 | ('AdvancedShortcut', XModLib.XMLConfigReader.DataObjectXMLReaderMeta.construct( 11 | 'AdvancedShortcutXMLReader', 12 | constructor=lambda shortcut: XModLib.KeyboardUtils.Shortcut(**shortcut), 13 | sectionType='Dict' 14 | )), 15 | ('CorrectionPanelSettings', XModLib.XMLConfigReader.OptionalDictXMLReaderMeta.construct( 16 | 'PanelSettingsXMLReader', 17 | requiredKeys=('visible', ), 18 | defaultKeys=('visible', ) 19 | )), 20 | ('TargetPanelSettings', XModLib.XMLConfigReader.OptionalDictXMLReaderMeta.construct( 21 | 'PanelSettingsXMLReader', 22 | requiredKeys=('visible', ), 23 | defaultKeys=('visible', ) 24 | )), 25 | ('AimingPanelSettings', XModLib.XMLConfigReader.OptionalDictXMLReaderMeta.construct( 26 | 'PanelSettingsXMLReader', 27 | requiredKeys=('visible', ), 28 | defaultKeys=('visible', 'template', 'position') 29 | )), 30 | ('InfoPanelsIngameSettings', XModLib.IngameSettings.IngameSettingsXMLReaderMeta.construct( 31 | 'InfoPanelsIngameSettingsXMLReader', 32 | constructor=XModLib.IngameSettings.IngameSettingsDictDataObject.loader('mods/GPCracker.AdvancedAimingSystem/gui/panels/ingame', True) 33 | )) 34 | )) 35 | 36 | # --------------------------------------- # 37 | # Application default configuration # 38 | # --------------------------------------- # 39 | g_globals['appDefaultConfig'] = { 40 | 'applicationEnabled': ('Bool', True), 41 | 'ignoreClientVersion': ('Bool', False), 42 | 'appSuccessMessage': ('LocalizedWideString', u'"Advanced Aiming System" was successfully loaded.'), 43 | 'appWarningMessage': ('LocalizedWideString', u'"Advanced Aiming System" was not tested with current client version.'), 44 | 'modules': { 45 | 'aimingInfo': { 46 | 'enabled': ('Bool', True), 47 | 'aimingThreshold': ('Float', 1.05) 48 | }, 49 | 'targetScanner': { 50 | 'enabled': ('Bool', True), 51 | 'scanMode': { 52 | 'useStandardMode': ('Bool', True), 53 | 'useXRayMode': ('Bool', False), 54 | 'useBBoxMode': ('Bool', False), 55 | 'useBEpsMode': ('Bool', False), 56 | 'maxDistance': ('Float', 720.0), 57 | 'boundsScalar': ('Float', 2.5), 58 | 'autoScanInterval': ('Float', 0.04), 59 | 'autoScanExpiryTimeout': ('Float', 10.0), 60 | 'autoScanRelockTimeout': ('Float', 0.16) 61 | }, 62 | 'autoScan': { 63 | 'enabled': ('Bool', True), 64 | 'activated': ('Bool', True), 65 | 'shortcut': ('AdvancedShortcut', { 66 | 'sequence': ('String', 'KEY_LCONTROL+KEY_N'), 67 | 'switch': ('Bool', True), 68 | 'invert': ('Bool', False) 69 | }), 70 | 'message': { 71 | 'onActivate': ('LocalizedWideString', u'[TargetScanner] AutoMode ENABLED.'), 72 | 'onDeactivate': ('LocalizedWideString', u'[TargetScanner] AutoMode DISABLED.') 73 | } 74 | }, 75 | 'manualOverride': { 76 | 'enabled': ('Bool', False), 77 | 'shortcut': ('SimpleShortcut', 'KEY_NONE', {'switch': True, 'invert': False}) 78 | } 79 | }, 80 | 'aimCorrection': { 81 | 'arcade': { 82 | 'enabled': ('Bool', False), 83 | 'fixGunMarker': ('Bool', True), 84 | 'manualMode': { 85 | 'enabled': ('Bool', True), 86 | 'shortcut': ('SimpleShortcut', 'KEY_LALT', {'switch': False, 'invert': False}) 87 | }, 88 | 'targetMode': { 89 | 'enabled': ('Bool', True), 90 | 'activated': ('Bool', True), 91 | 'shortcut': ('AdvancedShortcut', { 92 | 'sequence': ('String', 'KEY_LCONTROL+KEY_K'), 93 | 'switch': ('Bool', True), 94 | 'invert': ('Bool', False) 95 | }), 96 | 'message': { 97 | 'onActivate': ('LocalizedWideString', u'[ArcadeAimCorrection] TargetMode ENABLED.'), 98 | 'onDeactivate': ('LocalizedWideString', u'[ArcadeAimCorrection] TargetMode DISABLED.') 99 | }, 100 | 'distance': ('Vector2AsTuple', (50.0, 720.0)) 101 | } 102 | }, 103 | 'sniper': { 104 | 'enabled': ('Bool', True), 105 | 'fixGunMarker': ('Bool', True), 106 | 'manualMode': { 107 | 'enabled': ('Bool', True), 108 | 'shortcut': ('SimpleShortcut', 'KEY_LALT', {'switch': False, 'invert': False}) 109 | }, 110 | 'targetMode': { 111 | 'enabled': ('Bool', True), 112 | 'activated': ('Bool', True), 113 | 'shortcut': ('AdvancedShortcut', { 114 | 'sequence': ('String', 'KEY_LCONTROL+KEY_K'), 115 | 'switch': ('Bool', True), 116 | 'invert': ('Bool', False) 117 | }), 118 | 'message': { 119 | 'onActivate': ('LocalizedWideString', u'[SniperAimCorrection] TargetMode ENABLED.'), 120 | 'onDeactivate': ('LocalizedWideString', u'[SniperAimCorrection] TargetMode DISABLED.') 121 | }, 122 | 'distance': ('Vector2AsTuple', (10.0, 720.0)) 123 | } 124 | }, 125 | 'strategic': { 126 | 'enabled': ('Bool', True), 127 | 'fixGunMarker': ('Bool', False), 128 | 'manualMode': { 129 | 'enabled': ('Bool', True), 130 | 'shortcut': ('SimpleShortcut', 'KEY_LALT', {'switch': False, 'invert': False}) 131 | }, 132 | 'targetMode': { 133 | 'enabled': ('Bool', True), 134 | 'activated': ('Bool', False), 135 | 'shortcut': ('AdvancedShortcut', { 136 | 'sequence': ('String', 'KEY_LCONTROL+KEY_K'), 137 | 'switch': ('Bool', True), 138 | 'invert': ('Bool', False) 139 | }), 140 | 'message': { 141 | 'onActivate': ('LocalizedWideString', u'[StrategicAimCorrection] TargetMode ENABLED.'), 142 | 'onDeactivate': ('LocalizedWideString', u'[StrategicAimCorrection] TargetMode DISABLED.') 143 | }, 144 | 'heightMultiplier': ('Float', 0.5) 145 | }, 146 | 'ignoreVehicles': ('Bool', False) 147 | }, 148 | 'arty': { 149 | 'enabled': ('Bool', False), 150 | 'fixGunMarker': ('Bool', False), 151 | 'manualMode': { 152 | 'enabled': ('Bool', True), 153 | 'shortcut': ('SimpleShortcut', 'KEY_LALT', {'switch': False, 'invert': False}) 154 | }, 155 | 'targetMode': { 156 | 'enabled': ('Bool', True), 157 | 'activated': ('Bool', True), 158 | 'shortcut': ('AdvancedShortcut', { 159 | 'sequence': ('String', 'KEY_LCONTROL+KEY_K'), 160 | 'switch': ('Bool', True), 161 | 'invert': ('Bool', False) 162 | }), 163 | 'message': { 164 | 'onActivate': ('LocalizedWideString', u'[ArtyAimCorrection] TargetMode ENABLED.'), 165 | 'onDeactivate': ('LocalizedWideString', u'[ArtyAimCorrection] TargetMode DISABLED.') 166 | } 167 | } 168 | } 169 | } 170 | }, 171 | 'plugins': {}, 172 | 'gui': { 173 | 'enabled': ('Bool', True), 174 | 'updateInterval': ('Float', 0.04), 175 | 'panels': { 176 | 'context': { 177 | 'hideInfoPanel': ('LocalizedWideString', u'Hide this panel'), 178 | 'resetInfoPanel': ('LocalizedWideString', u'Reset ingame settings') 179 | }, 180 | 'static': { 181 | 'AdvancedAimingSystemCorrectionPanel': { 182 | 'default': { 183 | 'alpha': ('Float', 1.0), 184 | 'visible': ('Bool', False), 185 | 'background': ('String', ''), 186 | 'tooltip': ('LocalizedWideString', u'Aim correction info panel.'), 187 | 'template': ('LocalizedExtendedTemplate', u'

Distance locked: {manualInfo:.1f}m.

'), 188 | 'position': ('Vector2AsTuple', (0.0, 0.3)), 189 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 190 | }, 191 | 'arcade': ('CorrectionPanelSettings', { 192 | 'alpha': ('Float', 1.0), 193 | 'visible': ('Bool', True), 194 | 'background': ('String', ''), 195 | 'tooltip': ('LocalizedWideString', u'Aim correction info panel.'), 196 | 'template': ('LocalizedExtendedTemplate', u'

Distance locked: {manualInfo:.1f}m.

'), 197 | 'position': ('Vector2AsTuple', (0.0, 0.3)), 198 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 199 | }), 200 | 'sniper': ('CorrectionPanelSettings', { 201 | 'alpha': ('Float', 1.0), 202 | 'visible': ('Bool', True), 203 | 'background': ('String', ''), 204 | 'tooltip': ('LocalizedWideString', u'Aim correction info panel.'), 205 | 'template': ('LocalizedExtendedTemplate', u'

Distance locked: {manualInfo:.1f}m.

'), 206 | 'position': ('Vector2AsTuple', (0.0, 0.3)), 207 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 208 | }), 209 | 'strategic': ('CorrectionPanelSettings', { 210 | 'alpha': ('Float', 1.0), 211 | 'visible': ('Bool', True), 212 | 'background': ('String', ''), 213 | 'tooltip': ('LocalizedWideString', u'Aim correction info panel.'), 214 | 'template': ('LocalizedExtendedTemplate', u'

Altitude locked: {manualInfo:.1f}m.

'), 215 | 'position': ('Vector2AsTuple', (0.0, 0.3)), 216 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 217 | }), 218 | 'arty': ('CorrectionPanelSettings', { 219 | 'alpha': ('Float', 1.0), 220 | 'visible': ('Bool', True), 221 | 'background': ('String', ''), 222 | 'tooltip': ('LocalizedWideString', u'Aim correction info panel.'), 223 | 'template': ('LocalizedExtendedTemplate', u'

Unknown parameter locked: {manualInfo:.1f}m.

'), 224 | 'position': ('Vector2AsTuple', (0.0, 0.3)), 225 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 226 | }) 227 | }, 228 | 'AdvancedAimingSystemTargetPanel': { 229 | 'default': { 230 | 'alpha': ('Float', 1.0), 231 | 'visible': ('Bool', False), 232 | 'background': ('String', ''), 233 | 'tooltip': ('LocalizedWideString', u'Target scanner info panel.'), 234 | 'template': ('LocalizedExtendedTemplate', u'

Target: {shortName}; Distance: {distance:.1f}m; Speed: {speedMS:.1f}m/s.

'), 235 | 'position': ('Vector2AsTuple', (0.0, 0.4)), 236 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 237 | }, 238 | 'arcade': ('TargetPanelSettings', { 239 | 'alpha': ('Float', 1.0), 240 | 'visible': ('Bool', True), 241 | 'background': ('String', ''), 242 | 'tooltip': ('LocalizedWideString', u'Target scanner info panel.'), 243 | 'template': ('LocalizedExtendedTemplate', u'

Target: {shortName}; Distance: {distance:.1f}m; Speed: {speedMS:.1f}m/s.

'), 244 | 'position': ('Vector2AsTuple', (0.0, 0.4)), 245 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 246 | }), 247 | 'sniper': ('TargetPanelSettings', { 248 | 'alpha': ('Float', 1.0), 249 | 'visible': ('Bool', True), 250 | 'background': ('String', ''), 251 | 'tooltip': ('LocalizedWideString', u'Target scanner info panel.'), 252 | 'template': ('LocalizedExtendedTemplate', u'

Target: {shortName}; Distance: {distance:.1f}m; Speed: {speedMS:.1f}m/s.

'), 253 | 'position': ('Vector2AsTuple', (0.0, 0.4)), 254 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 255 | }), 256 | 'strategic': ('TargetPanelSettings', { 257 | 'alpha': ('Float', 1.0), 258 | 'visible': ('Bool', True), 259 | 'background': ('String', ''), 260 | 'tooltip': ('LocalizedWideString', u'Target scanner info panel.'), 261 | 'template': ('LocalizedExtendedTemplate', u'

Target: {shortName}; Distance: {distance:.1f}m; Speed: {speedMS:.1f}m/s.

'), 262 | 'position': ('Vector2AsTuple', (0.0, 0.4)), 263 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 264 | }), 265 | 'arty': ('TargetPanelSettings', { 266 | 'alpha': ('Float', 1.0), 267 | 'visible': ('Bool', True), 268 | 'background': ('String', ''), 269 | 'tooltip': ('LocalizedWideString', u'Target scanner info panel.'), 270 | 'template': ('LocalizedExtendedTemplate', u'

Target: {shortName}; Distance: {distance:.1f}m; Speed: {speedMS:.1f}m/s.

'), 271 | 'position': ('Vector2AsTuple', (0.0, 0.4)), 272 | 'size': ('Vector2AsTuple', (450.0, 25.0)) 273 | }) 274 | }, 275 | 'AdvancedAimingSystemAimingPanel': { 276 | 'default': { 277 | 'alpha': ('Float', 1.0), 278 | 'visible': ('Bool', False), 279 | 'background': ('String', 'img://mods/GPCracker.AdvancedAimingSystem/icons/AimingInfoBackground.png'), 280 | 'tooltip': ('LocalizedWideString', u'Aiming info panel.'), 281 | 'template': ('LocalizedExtendedTemplate', u'\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;'), 282 | 'position': ('Vector2AsTuple', (0.4, -0.1)), 283 | 'size': ('Vector2AsTuple', (175.0, 130.0)) 284 | }, 285 | 'arcade': ('AimingPanelSettings', { 286 | 'alpha': ('Float', 1.0), 287 | 'visible': ('Bool', True), 288 | 'background': ('String', 'img://mods/GPCracker.AdvancedAimingSystem/icons/AimingInfoBackground.png'), 289 | 'tooltip': ('LocalizedWideString', u'Aiming info panel.'), 290 | 'template': ('LocalizedExtendedTemplate', u'\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;'), 291 | 'position': ('Vector2AsTuple', (0.4, -0.1)), 292 | 'size': ('Vector2AsTuple', (175.0, 130.0)) 293 | }), 294 | 'sniper': ('AimingPanelSettings', { 295 | 'alpha': ('Float', 1.0), 296 | 'visible': ('Bool', True), 297 | 'background': ('String', 'img://mods/GPCracker.AdvancedAimingSystem/icons/AimingInfoBackground.png'), 298 | 'tooltip': ('LocalizedWideString', u'Aiming info panel.'), 299 | 'template': ('LocalizedExtendedTemplate', u'\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;'), 300 | 'position': ('Vector2AsTuple', (0.4, -0.25)), 301 | 'size': ('Vector2AsTuple', (175.0, 130.0)) 302 | }), 303 | 'strategic': ('AimingPanelSettings', { 304 | 'alpha': ('Float', 1.0), 305 | 'visible': ('Bool', True), 306 | 'background': ('String', 'img://mods/GPCracker.AdvancedAimingSystem/icons/AimingInfoBackground.png'), 307 | 'tooltip': ('LocalizedWideString', u'Aiming info panel.'), 308 | 'template': ('LocalizedExtendedTemplate', u'\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;'), 309 | 'position': ('Vector2AsTuple', (-0.3, -0.4)), 310 | 'size': ('Vector2AsTuple', (175.0, 130.0)) 311 | }), 312 | 'arty': ('AimingPanelSettings', { 313 | 'alpha': ('Float', 1.0), 314 | 'visible': ('Bool', True), 315 | 'background': ('String', 'img://mods/GPCracker.AdvancedAimingSystem/icons/AimingInfoBackground.png'), 316 | 'tooltip': ('LocalizedWideString', u'Aiming info panel.'), 317 | 'template': ('LocalizedExtendedTemplate', u'\tRemains:\t{remainingAimingTime:.2f}s;\n\tDistance:\t{aimingDistance:.1f}m;\n\tDeviation:\t{deviation:.2f}m;\n\tFly time:\t{flyTime:.2f}s;\n\tHit angle:\t{hitAngleDeg:+.1f}dg;'), 318 | 'position': ('Vector2AsTuple', (-0.3, -0.4)), 319 | 'size': ('Vector2AsTuple', (175.0, 130.0)) 320 | }) 321 | } 322 | }, 323 | 'ingame': ('InfoPanelsIngameSettings', 'KGRwMQou') 324 | } 325 | } 326 | } 327 | 328 | # ----------------------------------------- # 329 | # Application configuration root file # 330 | # ----------------------------------------- # 331 | g_globals['appConfigFile'] = os.path.splitext(__file__)[0] + '.xml' 332 | 333 | # --------------------------------------------- # 334 | # Application configuration reading stage # 335 | # --------------------------------------------- # 336 | g_config = g_globals['appConfigReader']( 337 | XModLib.XMLConfigReader.openSection(g_globals['appConfigFile']), 338 | g_globals['appDefaultConfig'] 339 | ) 340 | -------------------------------------------------------------------------------- /source/main/globals.py: -------------------------------------------------------------------------------- 1 | # ---------------------- # 2 | # Global variables # 3 | # ---------------------- # 4 | g_globals = { 5 | 'appConfigFile': None, 6 | 'appConfigReader': None, 7 | 'appDefaultConfig': None, 8 | 'appLoadingMessage': None 9 | } 10 | -------------------------------------------------------------------------------- /source/main/header.py: -------------------------------------------------------------------------------- 1 | __application__ = ('Advanced Aiming System Mod', 'AdvancedAimingSystem', 'GPCracker.AdvancedAimingSystem') 2 | __official_topic__ = 'http://www.koreanrandom.com/forum/topic/16559-/' 3 | __authors__ = ('GPCracker', ) 4 | __bugfixes__ = ('Tempora', ) 5 | __version__ = ('<>', None) 6 | __xmodlib__ = ('v0.1.18', None) 7 | __client__ = (('ru', ), '<>') 8 | 9 | # ---------------------- # 10 | # Application info # 11 | # ---------------------- # 12 | if __name__ == '__main__': 13 | appinfo = '{appname} ({appid}) {version} ({client} {clusters}) by {authors}'.format( 14 | appname = __application__[0], 15 | appid = __application__[2], 16 | version = __version__[0], 17 | client = __client__[1], 18 | clusters = ', '.join(__client__[0]).upper(), 19 | authors = ', '.join(__authors__) 20 | ) 21 | import sys, time 22 | print >> sys.stdout, appinfo 23 | time.sleep(len(appinfo) * 0.05) 24 | sys.exit(0) 25 | 26 | # -------------------------------------- # 27 | # X-Mod Library compatibility test # 28 | # -------------------------------------- # 29 | import XModLib 30 | if not XModLib.isCompatibleLibVersion(__xmodlib__): 31 | raise ImportError('XModLib version does not suit this version of application') 32 | -------------------------------------------------------------------------------- /source/main/imports.py: -------------------------------------------------------------------------------- 1 | # ------------ # 2 | # Python # 3 | # ------------ # 4 | import os 5 | import sys 6 | import enum 7 | import math 8 | import time 9 | import marshal 10 | import weakref 11 | import zipfile 12 | import operator 13 | import functools 14 | import itertools 15 | import collections 16 | 17 | # -------------- # 18 | # BigWorld # 19 | # -------------- # 20 | import Math 21 | import BigWorld 22 | 23 | # ---------------- # 24 | # WoT Client # 25 | # ---------------- # 26 | import constants 27 | import gui.shared.personality 28 | import AvatarInputHandler.cameras 29 | import AvatarInputHandler.aih_constants 30 | import AvatarInputHandler.aih_global_binding 31 | 32 | # -------------------- # 33 | # WoT Client GUI # 34 | # -------------------- # 35 | import gui.shared 36 | import gui.shared.events 37 | import gui.app_loader.settings 38 | import gui.Scaleform.framework.package_layout 39 | 40 | # ---------------------- # 41 | # WoT Client Hooks # 42 | # ---------------------- # 43 | import Avatar 44 | import Account 45 | import Vehicle 46 | import AvatarInputHandler 47 | import AvatarInputHandler.control_modes 48 | import AvatarInputHandler.DynamicCameras.StrategicCamera 49 | import AvatarInputHandler.AimingSystems.StrategicAimingSystem 50 | 51 | # -------------------------- # 52 | # WoT Client GUI Hooks # 53 | # -------------------------- # 54 | import gui.Scaleform.battle_entry 55 | import gui.Scaleform.daapi.view.battle.shared 56 | 57 | # ------------------- # 58 | # X-Mod Library # 59 | # ------------------- # 60 | import XModLib.ArenaInfo 61 | import XModLib.HookUtils 62 | import XModLib.MathUtils 63 | import XModLib.TextUtils 64 | import XModLib.ClientUtils 65 | import XModLib.EngineUtils 66 | import XModLib.VehicleInfo 67 | import XModLib.VehicleMath 68 | import XModLib.VehicleBounds 69 | import XModLib.CallbackUtils 70 | import XModLib.KeyboardUtils 71 | import XModLib.BallisticsMath 72 | import XModLib.ClientMessages 73 | import XModLib.CollisionUtils 74 | import XModLib.IngameSettings 75 | import XModLib.TargetScanners 76 | import XModLib.XMLConfigReader 77 | 78 | # ----------------------- # 79 | # X-Mod GUI Library # 80 | # ----------------------- # 81 | import XModLib.pygui.battle.library 82 | import XModLib.pygui.battle.views.handlers.ContextMenuHandler 83 | import XModLib.pygui.battle.views.components.panels.TextPanel 84 | -------------------------------------------------------------------------------- /source/main/injector.py: -------------------------------------------------------------------------------- 1 | # ---------------------------- # 2 | # Hooks injection events # 3 | # ---------------------------- # 4 | g_inject_loads = XModLib.HookUtils.HookEvent() 5 | g_inject_basis = XModLib.HookUtils.HookEvent() 6 | g_inject_hooks = XModLib.HookUtils.HookEvent() 7 | g_inject_ovrds = XModLib.HookUtils.HookEvent() 8 | 9 | # ---------------------------- # 10 | # Hooks injection chains # 11 | # ---------------------------- # 12 | g_inject_stage_init = XModLib.HookUtils.HookChain() 13 | g_inject_stage_main = XModLib.HookUtils.HookChain() 14 | p_inject_stage_init = XModLib.HookUtils.HookChain() 15 | p_inject_stage_main = XModLib.HookUtils.HookChain() 16 | 17 | # -------------------------------- # 18 | # Hooks injection main stage # 19 | # -------------------------------- # 20 | @XModLib.HookUtils.staticMethodHookExt(g_inject_loads, gui.shared.personality, 'start', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 21 | def new_Personality_start(*args, **kwargs): 22 | g_inject_stage_main() 23 | p_inject_stage_main() 24 | return 25 | -------------------------------------------------------------------------------- /source/main/launcher.py: -------------------------------------------------------------------------------- 1 | # ---------------------------- # 2 | # Application init stage # 3 | # ---------------------------- # 4 | if g_config['applicationEnabled']: 5 | g_inject_stage_init += g_inject_loads 6 | g_inject_stage_main += g_inject_basis 7 | g_inject_stage_main += g_inject_hooks 8 | g_inject_stage_init += g_inject_ovrds 9 | 10 | # --------------------------------- # 11 | # Application loading message # 12 | # --------------------------------- # 13 | if not g_config['applicationEnabled']: 14 | print >> sys.stdout, '[{0[1]}] {0[0]} is globally disabled and was not loaded.'.format(__application__) 15 | elif not g_config['ignoreClientVersion'] and not XModLib.ClientUtils.isCompatibleClientVersion(__client__): 16 | print >> sys.stdout, '[{0[1]}] {0[0]} was not tested with current client version.'.format(__application__) 17 | g_globals['appLoadingMessage'] = g_config['appWarningMessage'] 18 | else: 19 | print >> sys.stdout, '[{0[1]}] {0[0]} was successfully loaded.'.format(__application__) 20 | g_globals['appLoadingMessage'] = g_config['appSuccessMessage'] 21 | 22 | # -------------------------------- # 23 | # Hooks injection init stage # 24 | # -------------------------------- # 25 | g_inject_stage_init() 26 | p_inject_stage_init() 27 | -------------------------------------------------------------------------------- /source/plugins/AdvancedArtyExtension.py: -------------------------------------------------------------------------------- 1 | # ------------ # 2 | # Python # 3 | # ------------ # 4 | import math 5 | 6 | # -------------- # 7 | # BigWorld # 8 | # -------------- # 9 | import Math 10 | import BigWorld 11 | 12 | # ---------------- # 13 | # WoT Client # 14 | # ---------------- # 15 | import AvatarInputHandler.cameras 16 | 17 | # ---------------------- # 18 | # WoT Client Hooks # 19 | # ---------------------- # 20 | import gui.battle_control.matrix_factory 21 | import AvatarInputHandler.control_modes 22 | import AvatarInputHandler.DynamicCameras.ArtyCamera 23 | 24 | # ------------------- # 25 | # X-Mod Library # 26 | # ------------------- # 27 | import XModLib.HookUtils 28 | import XModLib.MathUtils 29 | import XModLib.KeyboardUtils 30 | import XModLib.ClientMessages 31 | 32 | # ----------------------------------- # 33 | # Plug-in default configuration # 34 | # ----------------------------------- # 35 | g_globals['appDefaultConfig']['plugins']['advancedArty'] = { 36 | 'enabled': ('Bool', False), 37 | 'cameraAdjustment': { 38 | 'enabled': ('Bool', True), 39 | 'interpolationSpeed': ('Float', 5.0), 40 | 'disableInterpolation': ('Bool', True), 41 | 'disableHighPitchLevel': ('Bool', True) 42 | }, 43 | 'orthogonalView': { 44 | 'enabled': ('Bool', True), 45 | 'activated': ('Bool', False), 46 | 'shortcut': ('AdvancedShortcut', { 47 | 'sequence': ('String', 'KEY_LALT+KEY_MIDDLEMOUSE'), 48 | 'switch': ('Bool', True), 49 | 'invert': ('Bool', False) 50 | }), 51 | 'cameraDistance': ('Float', 700.0), 52 | 'preserveLastView': ('Bool', True) 53 | } 54 | } 55 | 56 | # ----------------------------------------- # 57 | # Plug-in configuration reading stage # 58 | # ----------------------------------------- # 59 | g_config['plugins']['advancedArty'] = g_globals['appConfigReader']( 60 | XModLib.XMLConfigReader.overrideOpenSubSection(g_globals['appConfigFile'], 'plugins/advancedArty'), 61 | g_globals['appDefaultConfig']['plugins']['advancedArty'] 62 | ) 63 | 64 | # ------------------------------------ # 65 | # Plug-in hooks injection events # 66 | # ------------------------------------ # 67 | p_inject_hooks = XModLib.HookUtils.HookEvent() 68 | p_inject_ovrds = XModLib.HookUtils.HookEvent() 69 | 70 | # ------------------------ # 71 | # Plug-in init stage # 72 | # ------------------------ # 73 | if g_config['applicationEnabled'] and g_config['plugins']['advancedArty']['enabled']: 74 | p_inject_stage_main += p_inject_hooks 75 | p_inject_stage_init += p_inject_ovrds 76 | 77 | # ---------------------- # 78 | # ArtyCamera Hooks # 79 | # ---------------------- # 80 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.DynamicCameras.ArtyCamera.ArtyCamera, '__init__') 81 | def new_ArtyCamera_init(self, *args, **kwargs): 82 | config = g_config['plugins']['advancedArty'] 83 | if config['enabled']: 84 | if config['cameraAdjustment']['enabled']: 85 | self._ArtyCamera__cfg['interpolationSpeed'] = config['cameraAdjustment']['interpolationSpeed'] 86 | if config['orthogonalView']['enabled']: 87 | self._ArtyCamera__strategicAreaViewScaleMatrix = XModLib.MathUtils.getIdentityMatrix() 88 | return 89 | 90 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.DynamicCameras.ArtyCamera.ArtyCamera, '_ArtyCamera__interpolateStates', invoke=XModLib.HookUtils.HookInvoke.MASTER) 91 | def new_ArtyCamera_interpolateStates(old_ArtyCamera_interpolateStates, self, deltaTime, translation, rotation): 92 | config = g_config['plugins']['advancedArty'] 93 | if config['enabled'] and config['cameraAdjustment']['enabled'] and config['cameraAdjustment']['disableInterpolation']: 94 | self._ArtyCamera__sourceMatrix = rotation 95 | self._ArtyCamera__targetMatrix.translation = translation 96 | return self._ArtyCamera__sourceMatrix, self._ArtyCamera__targetMatrix 97 | return old_ArtyCamera_interpolateStates(self, deltaTime, translation, rotation) 98 | 99 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.DynamicCameras.ArtyCamera.ArtyCamera, '_ArtyCamera__choosePitchLevel', invoke=XModLib.HookUtils.HookInvoke.MASTER) 100 | def new_ArtyCamera_choosePitchLevel(old_ArtyCamera_choosePitchLevel, self, *args, **kwargs): 101 | result = old_ArtyCamera_choosePitchLevel(self, *args, **kwargs) 102 | config = g_config['plugins']['advancedArty'] 103 | if config['enabled'] and config['cameraAdjustment']['enabled']: 104 | return result and not config['cameraAdjustment']['disableHighPitchLevel'] 105 | return result 106 | 107 | @XModLib.HookUtils.propertyAddExt(p_inject_ovrds, AvatarInputHandler.DynamicCameras.ArtyCamera.ArtyCamera, 'strategicAreaViewScaleMatrix', XModLib.HookUtils.PropertyAction.GET) 108 | def new_ArtyCamera_strategicAreaViewScaleMatrix_getter(self): 109 | return getattr(self, '_ArtyCamera__strategicAreaViewScaleMatrix', XModLib.MathUtils.getIdentityMatrix()) 110 | 111 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.DynamicCameras.ArtyCamera.ArtyCamera, '_ArtyCamera__calculateIdealState', invoke=XModLib.HookUtils.HookInvoke.MASTER) 112 | def new_ArtyCamera_calculateIdealState(old_ArtyCamera_calculateIdealState, self, *args, **kwargs): 113 | translation, rotation = old_ArtyCamera_calculateIdealState(self, *args, **kwargs) 114 | config = g_config['plugins']['advancedArty'] 115 | if config['enabled'] and config['orthogonalView']['enabled'] and config['orthogonalView']['activated']: 116 | translation = self.aimingSystem.aimPoint - self._ArtyCamera__getCameraDirection().scale(config['orthogonalView']['cameraDistance']) 117 | return translation, rotation 118 | 119 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.DynamicCameras.ArtyCamera.ArtyCamera, '_ArtyCamera__cameraUpdate') 120 | def new_ArtyCamera_cameraUpdate(self, *args, **kwargs): 121 | config = g_config['plugins']['advancedArty'] 122 | if config['enabled'] and config['orthogonalView']['enabled']: 123 | cameraDistanceRate = self._ArtyCamera__desiredCamDist / self._ArtyCamera__camDist 124 | verticalFovHalf = AvatarInputHandler.cameras.FovExtended.instance().actualDefaultVerticalFov * 0.5 125 | fovCorrectionMultiplier = math.atan(math.tan(verticalFovHalf) * cameraDistanceRate) / verticalFovHalf 126 | AvatarInputHandler.cameras.FovExtended.instance().setFovByMultiplier(fovCorrectionMultiplier) 127 | self._ArtyCamera__strategicAreaViewScaleMatrix.setScale(Math.Vector3(1.0, 1.0, 1.0).scale(cameraDistanceRate)) 128 | return 129 | 130 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.DynamicCameras.ArtyCamera.ArtyCamera, 'enable', invoke=XModLib.HookUtils.HookInvoke.SECONDARY) 131 | def new_ArtyCamera_enable(self, *args, **kwargs): 132 | config = g_config['plugins']['advancedArty'] 133 | if config['enabled'] and config['orthogonalView']['enabled']: 134 | AvatarInputHandler.cameras.FovExtended.instance().resetFov() 135 | config['orthogonalView']['activated'] = config['orthogonalView']['activated'] and config['orthogonalView']['preserveLastView'] 136 | return 137 | 138 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.DynamicCameras.ArtyCamera.ArtyCamera, 'disable', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 139 | def new_ArtyCamera_disable(self, *args, **kwargs): 140 | config = g_config['plugins']['advancedArty'] 141 | if config['enabled'] and config['orthogonalView']['enabled']: 142 | AvatarInputHandler.cameras.FovExtended.instance().resetFov() 143 | config['orthogonalView']['activated'] = config['orthogonalView']['activated'] and config['orthogonalView']['preserveLastView'] 144 | return 145 | 146 | # ------------------------- # 147 | # MatrixFactory Hooks # 148 | # ------------------------- # 149 | @XModLib.HookUtils.staticMethodHookExt(p_inject_hooks, gui.battle_control.matrix_factory, 'makeArtyAimPointMatrix', invoke=XModLib.HookUtils.HookInvoke.MASTER) 150 | def new_MatrixFactory_makeArtyAimPointMatrix(old_MatrixFactory_makeArtyAimPointMatrix, *args, **kwargs): 151 | result = old_MatrixFactory_makeArtyAimPointMatrix(*args, **kwargs) 152 | config = g_config['plugins']['advancedArty'] 153 | if config['enabled'] and config['orthogonalView']['enabled']: 154 | return XModLib.MathUtils.getMatrixProduct(BigWorld.player().inputHandler.ctrl.camera.strategicAreaViewScaleMatrix, result) 155 | return result 156 | 157 | # --------------------------- # 158 | # ArtyControlMode Hooks # 159 | # --------------------------- # 160 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes.ArtyControlMode, 'handleKeyEvent', invoke=XModLib.HookUtils.HookInvoke.MASTER) 161 | def new_ArtyControlMode_handleKeyEvent(old_ArtyControlMode_handleKeyEvent, self, isDown, key, mods, event=None): 162 | result = old_ArtyControlMode_handleKeyEvent(self, isDown, key, mods, event) 163 | ## Keyboard event parsing 164 | kbevent = XModLib.KeyboardUtils.KeyboardEvent(event) 165 | ## AvatarInputHandler started, not detached, control mode supported, event not handled by game (for AvatarInputHandler core switches) 166 | if not result: 167 | ## HotKeys - AdvancedArty 168 | mconfig = g_config['plugins']['advancedArty'] 169 | if mconfig['enabled']: 170 | ## HotKeys - AdvancedArty - OrthogonalView 171 | fconfig = mconfig['orthogonalView'] 172 | shortcutHandle = fconfig['enabled'] and fconfig['shortcut'](kbevent) 173 | if shortcutHandle and (not shortcutHandle.switch or shortcutHandle.pushed): 174 | fconfig['activated'] = shortcutHandle(fconfig['activated']) 175 | return result 176 | -------------------------------------------------------------------------------- /source/plugins/AimCorrectionGunMarkerFix.py: -------------------------------------------------------------------------------- 1 | # ------------ # 2 | # Python # 3 | # ------------ # 4 | # nothing 5 | 6 | # -------------- # 7 | # BigWorld # 8 | # -------------- # 9 | import Math 10 | import BigWorld 11 | 12 | # ---------------- # 13 | # WoT Client # 14 | # ---------------- # 15 | import BattleReplay 16 | import ProjectileMover 17 | 18 | # ---------------------- # 19 | # WoT Client Hooks # 20 | # ---------------------- # 21 | import VehicleGunRotator 22 | 23 | # ------------------- # 24 | # X-Mod Library # 25 | # ------------------- # 26 | import XModLib.HookUtils 27 | import XModLib.MathUtils 28 | import XModLib.CollisionUtils 29 | 30 | # ----------------------------------- # 31 | # Plug-in default configuration # 32 | # ----------------------------------- # 33 | # nothing 34 | 35 | # ----------------------------------------- # 36 | # Plug-in configuration reading stage # 37 | # ----------------------------------------- # 38 | # nothing 39 | 40 | # ------------------------------------ # 41 | # Plug-in hooks injection events # 42 | # ------------------------------------ # 43 | p_inject_hooks = XModLib.HookUtils.HookEvent() 44 | p_inject_ovrds = XModLib.HookUtils.HookEvent() 45 | 46 | # ------------------------ # 47 | # Plug-in init stage # 48 | # ------------------------ # 49 | if g_config['applicationEnabled']: 50 | sections = g_config['modules']['aimCorrection'].viewvalues() 51 | if any(section['fixGunMarker'] for section in sections if section['enabled']): 52 | p_inject_stage_main += p_inject_hooks 53 | p_inject_stage_init += p_inject_ovrds 54 | del sections 55 | 56 | # ----------------------------- # 57 | # VehicleGunRotator Hooks # 58 | # ----------------------------- # 59 | @XModLib.HookUtils.methodAddExt(p_inject_ovrds, VehicleGunRotator.VehicleGunRotator, '_VehicleGunRotator__getGunMarkerPosition') 60 | def new_VehicleGunRotator_getGunMarkerPosition(self, shotPoint, shotVector, dispersionAngles): 61 | aimCorrection = getattr(self._VehicleGunRotator__avatar.inputHandler.ctrl, 'XAimCorrection', None) 62 | def colliderCorrection(collisionTestStart, collisionTestStop): 63 | if aimCorrection is not None: 64 | result = aimCorrection.getGunMarkerCollisionPoint(collisionTestStart, collisionTestStop) 65 | return (result, ) if result is not None else None 66 | return None 67 | def colliderMaterial(collisionTestStart, collisionTestStop): 68 | return ProjectileMover.collideDynamicAndStatic(collisionTestStart, collisionTestStop, (self.getAttachedVehicleID(), )) 69 | def colliderSpace(collisionTestStart, collisionTestStop): 70 | result = self._VehicleGunRotator__avatar.arena.collideWithSpaceBB(collisionTestStart, collisionTestStop) 71 | return (result, ) if result is not None else None 72 | colliders = (colliderCorrection, colliderMaterial, colliderSpace) 73 | vehicleTypeDescriptor = self._VehicleGunRotator__avatar.getVehicleDescriptor() 74 | shotGravity = Math.Vector3(0.0, -1.0, 0.0).scale(vehicleTypeDescriptor.shot.gravity) 75 | shotMaxDistance = vehicleTypeDescriptor.shot.maxDistance 76 | hitPoint, hitVector, hitResult, hitCollider = XModLib.CollisionUtils.computeProjectileTrajectoryEnd(shotPoint, shotVector, shotGravity, colliders) 77 | hitData = hitResult[1] if hitCollider is colliderMaterial and hitResult[1] is not None and hitResult[1].isVehicle() else None 78 | markerDistance = shotPoint.distTo(hitPoint) 79 | if hitCollider is colliderSpace and markerDistance >= shotMaxDistance: 80 | hitVector = XModLib.MathUtils.getNormalisedVector(hitPoint - shotPoint) 81 | hitPoint = shotPoint + hitVector.scale(shotMaxDistance) 82 | markerDistance = shotMaxDistance 83 | markerDiameter = 2.0 * markerDistance * dispersionAngles[0] 84 | idealMarkerDiameter = 2.0 * markerDistance * dispersionAngles[1] 85 | if BattleReplay.g_replayCtrl.isPlaying and BattleReplay.g_replayCtrl.isClientReady: 86 | markerDiameter, hitPoint, hitVector = BattleReplay.g_replayCtrl.getGunMarkerParams(hitPoint, hitVector) 87 | return hitPoint, hitVector, markerDiameter, idealMarkerDiameter, hitData 88 | -------------------------------------------------------------------------------- /source/plugins/AutoAimExtension.py: -------------------------------------------------------------------------------- 1 | # ------------ # 2 | # Python # 3 | # ------------ # 4 | # nothing 5 | 6 | # -------------- # 7 | # BigWorld # 8 | # -------------- # 9 | import Math 10 | import BigWorld 11 | 12 | # ---------------- # 13 | # WoT Client # 14 | # ---------------- # 15 | import CommandMapping 16 | 17 | # ---------------------- # 18 | # WoT Client Hooks # 19 | # ---------------------- # 20 | import AvatarInputHandler.control_modes 21 | 22 | # ------------------- # 23 | # X-Mod Library # 24 | # ------------------- # 25 | import XModLib.HookUtils 26 | 27 | # ----------------------------------- # 28 | # Plug-in default configuration # 29 | # ----------------------------------- # 30 | g_globals['appDefaultConfig']['plugins']['autoAim'] = { 31 | 'enabled': ('Bool', False), 32 | 'useTargetScan': ('Bool', False), 33 | 'useTargetInfo': ('Bool', False) 34 | } 35 | 36 | # ----------------------------------------- # 37 | # Plug-in configuration reading stage # 38 | # ----------------------------------------- # 39 | g_config['plugins']['autoAim'] = g_globals['appConfigReader']( 40 | XModLib.XMLConfigReader.overrideOpenSubSection(g_globals['appConfigFile'], 'plugins/autoAim'), 41 | g_globals['appDefaultConfig']['plugins']['autoAim'] 42 | ) 43 | 44 | # ------------------------------------ # 45 | # Plug-in hooks injection events # 46 | # ------------------------------------ # 47 | p_inject_hooks = XModLib.HookUtils.HookEvent() 48 | p_inject_ovrds = XModLib.HookUtils.HookEvent() 49 | 50 | # ------------------------ # 51 | # Plug-in init stage # 52 | # ------------------------ # 53 | if g_config['applicationEnabled'] and g_config['plugins']['autoAim']['enabled']: 54 | p_inject_stage_main += p_inject_hooks 55 | p_inject_stage_init += p_inject_ovrds 56 | 57 | # -------------------------- # 58 | # CommandMapping Hooks # 59 | # -------------------------- # 60 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, CommandMapping.CommandMapping, 'isFired', invoke=XModLib.HookUtils.HookInvoke.MASTER) 61 | def new_CommandMapping_isFired(old_CommandMapping_isFired, self, command, key): 62 | return command != CommandMapping.CMD_CM_LOCK_TARGET and old_CommandMapping_isFired(self, command, key) 63 | 64 | # ------------------------------ # 65 | # AutoAimControlMode Hooks # 66 | # ------------------------------ # 67 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes.ArcadeControlMode, 'handleKeyEvent', invoke=XModLib.HookUtils.HookInvoke.MASTER) 68 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes.SniperControlMode, 'handleKeyEvent', invoke=XModLib.HookUtils.HookInvoke.MASTER) 69 | def new_AutoAimControlMode_handleKeyEvent(old_AutoAimControlMode_handleKeyEvent, self, isDown, key, mods, event=None): 70 | result = old_AutoAimControlMode_handleKeyEvent(self, isDown, key, mods, event) 71 | if not result and CommandMapping.g_instance.get('CMD_CM_LOCK_TARGET') == key and isDown: 72 | target = BigWorld.target() 73 | # Target substitution begins. 74 | if target is None and g_config['plugins']['autoAim']['useTargetScan']: 75 | targetScanner = getattr(self._aih, 'XTargetScanner', None) 76 | if targetScanner is not None: 77 | target = targetScanner.scanTarget().target 78 | if target is None and g_config['plugins']['autoAim']['useTargetInfo']: 79 | targetInfo = getattr(self._aih, 'XTargetInfo', None) 80 | if targetInfo is not None and not targetInfo.isExpired: 81 | target = targetInfo.getVehicle() 82 | # Target substitution ends. 83 | BigWorld.player().autoAim(target) 84 | return result 85 | -------------------------------------------------------------------------------- /source/plugins/ExpertPerkExtension.py: -------------------------------------------------------------------------------- 1 | # ------------ # 2 | # Python # 3 | # ------------ # 4 | import weakref 5 | import operator 6 | import collections 7 | 8 | # -------------- # 9 | # BigWorld # 10 | # -------------- # 11 | import BigWorld 12 | 13 | # ---------------- # 14 | # WoT Client # 15 | # ---------------- # 16 | # nothing 17 | 18 | # ---------------------- # 19 | # WoT Client Hooks # 20 | # ---------------------- # 21 | import Avatar 22 | import Vehicle 23 | 24 | # ------------------- # 25 | # X-Mod Library # 26 | # ------------------- # 27 | import XModLib.HookUtils 28 | import XModLib.VehicleInfo 29 | 30 | # ----------------------------------- # 31 | # Plug-in default configuration # 32 | # ----------------------------------- # 33 | g_globals['appDefaultConfig']['plugins']['expertPerk'] = { 34 | 'enabled': ('Bool', False), 35 | 'cacheExtrasInfo': ('Bool', False), 36 | 'cacheExpiryTimeout': ('Float', 30.0), 37 | 'responseTimeout': ('Float', 5.0) 38 | } 39 | 40 | # ----------------------------------------- # 41 | # Plug-in configuration reading stage # 42 | # ----------------------------------------- # 43 | g_config['plugins']['expertPerk'] = g_globals['appConfigReader']( 44 | XModLib.XMLConfigReader.overrideOpenSubSection(g_globals['appConfigFile'], 'plugins/expertPerk'), 45 | g_globals['appDefaultConfig']['plugins']['expertPerk'] 46 | ) 47 | 48 | # ------------------------------------ # 49 | # Plug-in hooks injection events # 50 | # ------------------------------------ # 51 | p_inject_hooks = XModLib.HookUtils.HookEvent() 52 | p_inject_ovrds = XModLib.HookUtils.HookEvent() 53 | 54 | # ------------------------ # 55 | # Plug-in init stage # 56 | # ------------------------ # 57 | if g_config['applicationEnabled'] and g_config['plugins']['expertPerk']['enabled']: 58 | p_inject_stage_main += p_inject_hooks 59 | p_inject_stage_init += p_inject_ovrds 60 | 61 | # ------------------------ # 62 | # ExpertPerk Classes # 63 | # ------------------------ # 64 | class ExtrasInfoEntry(collections.namedtuple('ExtrasInfoEntry', ('criticalExtras', 'destroyedExtras'))): 65 | __slots__ = () 66 | 67 | def __new__(cls, criticalExtras=(), destroyedExtras=()): 68 | return super(ExtrasInfoEntry, cls).__new__(cls, criticalExtras, destroyedExtras) 69 | 70 | class ExtrasInfoCacheEntry(collections.namedtuple('ExtrasInfoCacheEntry', ('extrasInfoEntry', 'receiptTime', 'expiryTimeout'))): 71 | __slots__ = () 72 | 73 | def __new__(cls, extrasInfoEntry, receiptTime=None, expiryTimeout=30.0): 74 | if receiptTime is None: 75 | receiptTime = BigWorld.time() 76 | return super(ExtrasInfoCacheEntry, cls).__new__(cls, extrasInfoEntry, receiptTime, expiryTimeout) 77 | 78 | @property 79 | def isExpired(self): 80 | return self.receiptTime + self.expiryTimeout <= BigWorld.time() 81 | 82 | class ExtrasInfoCache(dict): 83 | __slots__ = () 84 | 85 | def cleanup(self): 86 | vehicleIDs = tuple(vehicleID for vehicleID, cacheEntry in self.viewitems() if cacheEntry.isExpired) 87 | return tuple((vehicleID, self.pop(vehicleID)) for vehicleID in vehicleIDs) 88 | 89 | class ExtrasInfoRequest(collections.namedtuple('ExtrasInfoRequest', ('vehicleID', 'requestTime', 'responseTimeout'))): 90 | __slots__ = () 91 | 92 | def __new__(cls, vehicleID, requestTime=None, responseTimeout=5.0): 93 | if requestTime is None: 94 | requestTime = BigWorld.time() 95 | return super(ExtrasInfoRequest, cls).__new__(cls, vehicleID, requestTime, responseTimeout) 96 | 97 | @property 98 | def isExpired(self): 99 | return self.requestTime + self.responseTimeout <= BigWorld.time() 100 | 101 | class ExtrasInfoResponse(collections.namedtuple('ExtrasInfoResponse', ('vehicleID', 'extrasInfoEntry', 'responseTime'))): 102 | __slots__ = () 103 | 104 | def __new__(cls, vehicleID, extrasInfoEntry, responseTime=None): 105 | if responseTime is None: 106 | responseTime = BigWorld.time() 107 | return super(ExtrasInfoResponse, cls).__new__(cls, vehicleID, extrasInfoEntry, responseTime) 108 | 109 | class ExtrasInfoRequester(object): 110 | __slots__ = ('__weakref__', '_avatar', '_activeRequest', '_lastResponse') 111 | 112 | def __init__(self, avatar): 113 | super(ExtrasInfoRequester, self).__init__() 114 | self._avatar = weakref.proxy(avatar) 115 | self._activeRequest = None 116 | self._lastResponse = None 117 | return 118 | 119 | @property 120 | def _maySeeOtherVehicleDamagedDevices(self): 121 | return self._avatar._maySeeOtherVehicleDamagedDevices 122 | 123 | @property 124 | def isRequested(self): 125 | return self._activeRequest is not None 126 | 127 | @property 128 | def isExpired(self): 129 | return self._activeRequest is not None and self._activeRequest.isExpired 130 | 131 | @property 132 | def isResponded(self): 133 | result = self._activeRequest is not None and self._lastResponse is not None 134 | result = result and self._activeRequest.vehicleID == self._lastResponse.vehicleID 135 | return result and self._activeRequest.requestTime <= self._lastResponse.responseTime 136 | 137 | @property 138 | def lastResponse(self): 139 | return self._lastResponse 140 | 141 | @property 142 | def activeRequest(self): 143 | return self._activeRequest 144 | 145 | @activeRequest.setter 146 | def activeRequest(self, value): 147 | if self._maySeeOtherVehicleDamagedDevices: 148 | if self._activeRequest.vehicleID != value.vehicleID: 149 | # Monitoring is actually activated only four seconds after sending request. 150 | # Monitoring means that we immediately receive all updates about vehicle modules. 151 | # After monitoring is successfully activated, we also receive initial data to show. 152 | # Monitoring could be maintained only if player's target is in direct visibility area. 153 | # So for most effective usage we should cancel request only when player changes his target. 154 | # Server may not respond if target has no critical or destroyed extras or is invisible for player. 155 | # This situations should be ignored, because server will keep us posted anyway when data will be available. 156 | if self._activeRequest is not None: 157 | self._avatar.cell.monitorVehicleDamagedDevices(0) 158 | self._activeRequest = value 159 | if self._activeRequest is not None: 160 | self._avatar.cell.monitorVehicleDamagedDevices(self._activeRequest.vehicleID) 161 | return 162 | 163 | def onExtrasInfoReceived(self, extrasInfoResponse): 164 | self._lastResponse = extrasInfoResponse 165 | return 166 | 167 | def __del__(self): 168 | if self._activeRequest is not None: 169 | raise RuntimeError('ExtrasInfoRequester is about to be removed with an active request') 170 | return 171 | 172 | class ExtrasInfoController(object): 173 | __slots__ = ('__weakref__', '_cache', '_requester', '_cacheExtrasInfo', '_cacheExpiryTimeout', '_responseTimeout') 174 | 175 | def __init__(self, avatar, cacheExtrasInfo=False, cacheExpiryTimeout=30.0, responseTimeout=5.0): 176 | super(ExtrasInfoController, self).__init__() 177 | self._cache = ExtrasInfoCache() 178 | self._requester = ExtrasInfoRequester(avatar) 179 | self._cacheExtrasInfo = cacheExtrasInfo 180 | self._cacheExpiryTimeout = cacheExpiryTimeout 181 | self._responseTimeout = responseTimeout 182 | return 183 | 184 | cacheExtrasInfo = property(operator.attrgetter('_cacheExtrasInfo')) 185 | 186 | def isMonitored(self, vehicleID): 187 | return self._requester.isResponded and self._requester.activeRequest.vehicleID == vehicleID 188 | 189 | @property 190 | def activeRequestVehicleID(self): 191 | return self._requester.activeRequest.vehicleID if self._requester.activeRequest is not None else 0 192 | 193 | @activeRequestVehicleID.setter 194 | def activeRequestVehicleID(self, value): 195 | self._requester.activeRequest = ExtrasInfoRequest(value, responseTimeout=self._responseTimeout) if value != 0 else None 196 | return 197 | 198 | def cancelExtrasInfoRequest(self, vehicleID=None): 199 | if vehicleID is None or self.activeRequestVehicleID == vehicleID: 200 | self.activeRequestVehicleID = 0 201 | return 202 | 203 | def getCachedExtrasInfoEntry(self, vehicleID): 204 | # Cache provides access to information about modules of vehicles that were monitored before. 205 | # Cache entries lifetime may expire, but they are still considered actual if target is being monitored now. 206 | # Monitored vehicle means that active request is related to requested vehicle and has already been replied. 207 | # For correct operating when caching is disabled we return data about actually monitored vehicles, but only about them. 208 | cacheEntry = self._cache.get(vehicleID, None) 209 | if cacheEntry is not None and (self._cacheExtrasInfo and not cacheEntry.isExpired or self.isMonitored(vehicleID)): 210 | return cacheEntry.extrasInfoEntry 211 | return None 212 | 213 | def onExtrasInfoReceived(self, vehicleID, criticalExtras, destroyedExtras): 214 | extrasInfoEntry = ExtrasInfoEntry(criticalExtras, destroyedExtras) 215 | self._requester.onExtrasInfoReceived(ExtrasInfoResponse(vehicleID, extrasInfoEntry)) 216 | self._cache[vehicleID] = ExtrasInfoCacheEntry(extrasInfoEntry, expiryTimeout=self._cacheExpiryTimeout) 217 | return 218 | 219 | def __del__(self): 220 | if self._requester.activeRequest is not None: 221 | raise RuntimeError('ExtrasInfoController is about to be removed with an active request') 222 | return 223 | 224 | # ------------------------ # 225 | # PlayerAvatar Hooks # 226 | # ------------------------ # 227 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, Avatar.PlayerAvatar, '__init__') 228 | def new_PlayerAvatar_init(self, *args, **kwargs): 229 | config = g_config['plugins']['expertPerk'] 230 | self.XExtrasInfoController = ExtrasInfoController( 231 | self, 232 | cacheExtrasInfo=config['cacheExtrasInfo'], 233 | cacheExpiryTimeout=config['cacheExpiryTimeout'], 234 | responseTimeout=config['responseTimeout'] 235 | ) if config['enabled'] else None 236 | return 237 | 238 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, Avatar.PlayerAvatar, 'showOtherVehicleDamagedDevices') 239 | def new_PlayerAvatar_showOtherVehicleDamagedDevices(self, vehicleID, damagedExtras, destroyedExtras): 240 | extrasInfoController = getattr(self, 'XExtrasInfoController', None) 241 | if extrasInfoController is not None and getattr(self, '_maySeeOtherVehicleDamagedDevices', False): 242 | extrasInfoController.onExtrasInfoReceived(vehicleID, damagedExtras, destroyedExtras) 243 | # This code is executed when server sends data and advanced expert handler is activated. 244 | # First action we should do here is to cache data received from server for use in future. 245 | # Target scanner is useless here because expert works only on vehicles in direct visibility area. 246 | # If received data is about current target, and it is alive, we should show this information to player at once. 247 | target = BigWorld.target() 248 | if XModLib.VehicleInfo.isVehicle(target) and target.isAlive() and target.id == vehicleID: 249 | self.guiSessionProvider.shared.feedback.showVehicleDamagedDevices(vehicleID, damagedExtras, destroyedExtras) 250 | return 251 | 252 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, Avatar.PlayerAvatar, 'targetBlur') 253 | def new_PlayerAvatar_targetBlur(self, prevEntity): 254 | extrasInfoController = getattr(self, 'XExtrasInfoController', None) 255 | if extrasInfoController is not None and getattr(self, '_maySeeOtherVehicleDamagedDevices', False): 256 | # Default handler cancels expert request here, forcing player to hold crosshairs over target for some time. 257 | # We moved this code to vehicle visual stop handler, so request will be cancelled only when target disappears. 258 | # Also request is automatically cancelled before new request is sent or when target dies (all modules break). 259 | # Code below just hides expert information panel because data is useless when no target is specified. 260 | if XModLib.VehicleInfo.isVehicle(prevEntity) and prevEntity.isAlive(): 261 | self.guiSessionProvider.shared.feedback.hideVehicleDamagedDevices() 262 | return 263 | 264 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, Avatar.PlayerAvatar, 'targetFocus') 265 | def new_PlayerAvatar_targetFocus(self, entity): 266 | extrasInfoController = getattr(self, 'XExtrasInfoController', None) 267 | if extrasInfoController is not None and getattr(self, '_maySeeOtherVehicleDamagedDevices', False): 268 | # Like default handler does, we send a request if focused target is an alive vehicle. 269 | # Unless request is cancelled, actual data will be shown just after client receives it. 270 | # But for now we check if we have actual cached data that will be displayed until the server sends an update. 271 | if XModLib.VehicleInfo.isVehicle(entity) and entity.isAlive(): 272 | extrasInfoController.activeRequestVehicleID = entity.id 273 | extrasInfoEntry = extrasInfoController.getCachedExtrasInfoEntry(entity.id) 274 | if extrasInfoEntry is not None: 275 | self.guiSessionProvider.shared.feedback.showVehicleDamagedDevices(entity.id, *extrasInfoEntry) 276 | return 277 | 278 | @XModLib.HookUtils.propertyHookExt(p_inject_hooks, Avatar.PlayerAvatar, '_PlayerAvatar__maySeeOtherVehicleDamagedDevices', XModLib.HookUtils.PropertyAction.GET, '_maySeeOtherVehicleDamagedDevices', invoke=XModLib.HookUtils.HookInvoke.MASTER) 279 | def new_PlayerAvatar_maySeeOtherVehicleDamagedDevices_getter(old_PlayerAvatar_maySeeOtherVehicleDamagedDevices_getter, self): 280 | return old_PlayerAvatar_maySeeOtherVehicleDamagedDevices_getter(self) and getattr(self, 'XExtrasInfoController', None) is None 281 | 282 | # ------------------- # 283 | # Vehicle Hooks # 284 | # ------------------- # 285 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, Vehicle.Vehicle, 'stopVisual', invoke=XModLib.HookUtils.HookInvoke.PRIMARY) 286 | def new_Vehicle_stopVisual(self, *args, **kwargs): 287 | # When vehicle disappears we should cancel an active request related to it. 288 | extrasInfoController = getattr(BigWorld.player(), 'XExtrasInfoController', None) 289 | if extrasInfoController is not None: 290 | extrasInfoController.cancelExtrasInfoRequest(self.id) 291 | return 292 | 293 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, Vehicle.Vehicle, '_Vehicle__onVehicleDeath') 294 | def new_Vehicle_onVehicleDeath(self, isDeadStarted=False): 295 | # This method is also called when dead target appears (player enters its drawing area). 296 | if isDeadStarted: 297 | return 298 | # When vehicle dies we should cancel an active request related to it. 299 | extrasInfoController = getattr(BigWorld.player(), 'XExtrasInfoController', None) 300 | if extrasInfoController is not None: 301 | extrasInfoController.cancelExtrasInfoRequest(self.id) 302 | return 303 | -------------------------------------------------------------------------------- /source/plugins/RadialMenuExtension.py: -------------------------------------------------------------------------------- 1 | # ------------ # 2 | # Python # 3 | # ------------ # 4 | # nothing 5 | 6 | # -------------- # 7 | # BigWorld # 8 | # -------------- # 9 | import GUI 10 | import Math 11 | import BigWorld 12 | 13 | # ---------------- # 14 | # WoT Client # 15 | # ---------------- # 16 | import CommandMapping 17 | 18 | # ---------------------- # 19 | # WoT Client Hooks # 20 | # ---------------------- # 21 | import gui.battle_control.controllers.chat_cmd_ctrl 22 | import gui.Scaleform.daapi.view.battle.shared.radial_menu 23 | 24 | # ------------------- # 25 | # X-Mod Library # 26 | # ------------------- # 27 | import XModLib.HookUtils 28 | 29 | # ----------------------------------- # 30 | # Plug-in default configuration # 31 | # ----------------------------------- # 32 | g_globals['appDefaultConfig']['plugins']['radialMenu'] = { 33 | 'enabled': ('Bool', False), 34 | 'useTargetScan': ('Bool', False), 35 | 'useTargetInfo': ('Bool', False) 36 | } 37 | 38 | # ----------------------------------------- # 39 | # Plug-in configuration reading stage # 40 | # ----------------------------------------- # 41 | g_config['plugins']['radialMenu'] = g_globals['appConfigReader']( 42 | XModLib.XMLConfigReader.overrideOpenSubSection(g_globals['appConfigFile'], 'plugins/radialMenu'), 43 | g_globals['appDefaultConfig']['plugins']['radialMenu'] 44 | ) 45 | 46 | # ------------------------------------ # 47 | # Plug-in hooks injection events # 48 | # ------------------------------------ # 49 | p_inject_hooks = XModLib.HookUtils.HookEvent() 50 | p_inject_ovrds = XModLib.HookUtils.HookEvent() 51 | 52 | # ------------------------ # 53 | # Plug-in init stage # 54 | # ------------------------ # 55 | if g_config['applicationEnabled'] and g_config['plugins']['radialMenu']['enabled']: 56 | p_inject_stage_main += p_inject_hooks 57 | p_inject_stage_init += p_inject_ovrds 58 | 59 | # ---------------------- # 60 | # RadialMenu Hooks # 61 | # ---------------------- # 62 | @XModLib.HookUtils.methodAddExt(p_inject_ovrds, gui.Scaleform.daapi.view.battle.shared.radial_menu.RadialMenu, 'show') 63 | def new_RadialMenu_show(self): 64 | player = BigWorld.player() 65 | target = BigWorld.target() 66 | # Target substitution begins. 67 | config = g_config['plugins']['radialMenu'] 68 | if target is None and config['useTargetScan']: 69 | targetScanner = getattr(player.inputHandler, 'XTargetScanner', None) 70 | if targetScanner is not None: 71 | target = targetScanner.scanTarget().target 72 | if target is None and config['useTargetInfo']: 73 | targetInfo = getattr(player.inputHandler, 'XTargetInfo', None) 74 | if targetInfo is not None and not targetInfo.isExpired: 75 | target = targetInfo.getVehicle() 76 | # Target substitution ends. 77 | self._RadialMenu__targetID = target.id if target is not None else None 78 | ctrl = self.sessionProvider.shared.crosshair 79 | guiScreenWidth, guiScreenHeight = GUI.screenResolution() 80 | screenRatio = float(guiScreenWidth / BigWorld.screenWidth()), float(guiScreenHeight / BigWorld.screenHeight()) 81 | screenPosition = ctrl.getDisaredPosition() if ctrl is not None else (guiScreenWidth * 0.5, guiScreenHeight * 0.5) 82 | crosshairType = self._RadialMenu__getCrosshairType(player, target) 83 | if self.app is not None: 84 | self.app.registerGuiKeyHandler(self) 85 | self.as_showS(crosshairType, screenPosition, screenRatio) 86 | return 87 | 88 | # ---------------------------------- # 89 | # ChatCommandsController Hooks # 90 | # ---------------------------------- # 91 | @XModLib.HookUtils.methodAddExt(p_inject_ovrds, gui.battle_control.controllers.chat_cmd_ctrl.ChatCommandsController, 'handleShortcutChatCommand') 92 | def new_ChatCommandsController_handleShortcutChatCommand(self, key): 93 | player = BigWorld.player() 94 | target = BigWorld.target() 95 | # Target substitution begins. 96 | config = g_config['plugins']['radialMenu'] 97 | if target is None and config['useTargetScan']: 98 | targetScanner = getattr(player.inputHandler, 'XTargetScanner', None) 99 | if targetScanner is not None: 100 | target = targetScanner.scanTarget().target 101 | if target is None and config['useTargetInfo']: 102 | targetInfo = getattr(player.inputHandler, 'XTargetInfo', None) 103 | if targetInfo is not None and not targetInfo.isExpired: 104 | target = targetInfo.getVehicle() 105 | # Target substitution ends. 106 | for chatCommand, keyboardCommand in gui.battle_control.controllers.chat_cmd_ctrl.KB_MAPPING.iteritems(): 107 | if CommandMapping.g_instance.isFired(keyboardCommand, key): 108 | crosshairType = self._ChatCommandsController__getCrosshairType(player, target) 109 | action = chatCommand 110 | if crosshairType != gui.battle_control.controllers.chat_cmd_ctrl.DEFAULT_CUT: 111 | if chatCommand in gui.battle_control.controllers.chat_cmd_ctrl.TARGET_TRANSLATION_MAPPING: 112 | if crosshairType in gui.battle_control.controllers.chat_cmd_ctrl.TARGET_TRANSLATION_MAPPING[chatCommand]: 113 | action = gui.battle_control.controllers.chat_cmd_ctrl.TARGET_TRANSLATION_MAPPING[chatCommand][crosshairType] 114 | if action in gui.battle_control.controllers.chat_cmd_ctrl.TARGET_ACTIONS: 115 | if crosshairType != gui.battle_control.controllers.chat_cmd_ctrl.DEFAULT_CUT: 116 | self.handleChatCommand(action, target.id) 117 | else: 118 | self.handleChatCommand(action) 119 | return 120 | -------------------------------------------------------------------------------- /source/plugins/SafeShotExtension.py: -------------------------------------------------------------------------------- 1 | # ------------ # 2 | # Python # 3 | # ------------ # 4 | # nothing 5 | 6 | # -------------- # 7 | # BigWorld # 8 | # -------------- # 9 | import BigWorld 10 | 11 | # ---------------- # 12 | # WoT Client # 13 | # ---------------- # 14 | import AvatarInputHandler.aih_constants 15 | 16 | # ---------------------- # 17 | # WoT Client Hooks # 18 | # ---------------------- # 19 | import Avatar 20 | import Vehicle 21 | import AvatarInputHandler.control_modes 22 | 23 | # ------------------- # 24 | # X-Mod Library # 25 | # ------------------- # 26 | import XModLib.ArenaInfo 27 | import XModLib.HookUtils 28 | import XModLib.KeyboardUtils 29 | import XModLib.ClientMessages 30 | import XModLib.TargetScanners 31 | 32 | # ----------------------------------- # 33 | # Plug-in default configuration # 34 | # ----------------------------------- # 35 | g_globals['appDefaultConfig']['plugins']['safeShot'] = { 36 | 'enabled': ('Bool', False), 37 | 'activated': ('Bool', True), 38 | 'shortcut': ('AdvancedShortcut', { 39 | 'sequence': ('String', 'KEY_LALT'), 40 | 'switch': ('Bool', False), 41 | 'invert': ('Bool', True) 42 | }), 43 | 'message': { 44 | 'onActivate': ('LocalizedWideString', u'[SafeShot] ENABLED.'), 45 | 'onDeactivate': ('LocalizedWideString', u'[SafeShot] DISABLED.') 46 | }, 47 | 'useGunTarget': ('Bool', True), 48 | 'considerBlueHostile': ('Bool', False), 49 | 'fragExpirationTimeout': ('Float', 2.0), 50 | 'template': ('LocalizedStandardTemplate', u'[{reason}] Shot has been blocked.'), 51 | 'reasons': { 52 | 'team': { 53 | 'enabled': ('Bool', True), 54 | 'chat': { 55 | 'enabled': ('Bool', True), 56 | 'message': ('LocalizedStandardTemplate', u'{player} ({vehicle}), you\'re in my line of fire!') 57 | }, 58 | 'template': ('LocalizedWideString', u'friendly') 59 | }, 60 | 'dead': { 61 | 'enabled': ('Bool', True), 62 | 'template': ('LocalizedWideString', u'corpse') 63 | }, 64 | 'waste': { 65 | 'enabled': ('Bool', False), 66 | 'template': ('LocalizedWideString', u'waste') 67 | } 68 | } 69 | } 70 | 71 | # ----------------------------------------- # 72 | # Plug-in configuration reading stage # 73 | # ----------------------------------------- # 74 | g_config['plugins']['safeShot'] = g_globals['appConfigReader']( 75 | XModLib.XMLConfigReader.overrideOpenSubSection(g_globals['appConfigFile'], 'plugins/safeShot'), 76 | g_globals['appDefaultConfig']['plugins']['safeShot'] 77 | ) 78 | 79 | # ------------------------------------ # 80 | # Plug-in hooks injection events # 81 | # ------------------------------------ # 82 | p_inject_hooks = XModLib.HookUtils.HookEvent() 83 | p_inject_ovrds = XModLib.HookUtils.HookEvent() 84 | 85 | # ------------------------ # 86 | # Plug-in init stage # 87 | # ------------------------ # 88 | if g_config['applicationEnabled'] and g_config['plugins']['safeShot']['enabled']: 89 | p_inject_stage_main += p_inject_hooks 90 | p_inject_stage_init += p_inject_ovrds 91 | 92 | # -------------------------- # 93 | # GunControlMode Hooks # 94 | # -------------------------- # 95 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes._GunControlMode, 'updateGunMarker') 96 | def new_GunControlMode_updateGunMarker(self, markerType, pos, dir, size, relaxTime, collData): 97 | gunTarget = collData.entity if collData is not None else None 98 | if markerType == AvatarInputHandler.aih_constants.GUN_MARKER_TYPE.CLIENT: 99 | self._clientTarget = gunTarget 100 | elif markerType == AvatarInputHandler.aih_constants.GUN_MARKER_TYPE.SERVER: 101 | self._serverTarget = gunTarget 102 | return 103 | 104 | # ------------------- # 105 | # Vehicle Hooks # 106 | # ------------------- # 107 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, Vehicle.Vehicle, '_Vehicle__onVehicleDeath') 108 | def new_Vehicle_onVehicleDeath(self, isDeadStarted=False): 109 | if not isDeadStarted: 110 | self._deathTime = BigWorld.time() 111 | return 112 | 113 | # ------------------------------- # 114 | # SafeShotControlMode Hooks # 115 | # ------------------------------- # 116 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes.ArcadeControlMode, 'handleKeyEvent') 117 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes.SniperControlMode, 'handleKeyEvent') 118 | def new_SafeShotControlMode_handleKeyEvent(self, isDown, key, mods, event=None): 119 | ## Keyboard event parsing 120 | kbevent = XModLib.KeyboardUtils.KeyboardEvent(event) 121 | ## AvatarInputHandler started, not detached, control mode supported (for AvatarInputHandler shortcuts) 122 | if True: 123 | ## HotKeys - SafeShot 124 | mconfig = g_config['plugins']['safeShot'] 125 | if mconfig['enabled']: 126 | ## HotKeys - SafeShot - Global 127 | fconfig = mconfig 128 | shortcutHandle = fconfig['enabled'] and fconfig['shortcut'](kbevent) 129 | if shortcutHandle and (not shortcutHandle.switch or shortcutHandle.pushed): 130 | fconfig['activated'] = shortcutHandle(fconfig['activated']) 131 | if shortcutHandle.switch and fconfig['activated']: 132 | XModLib.ClientMessages.showMessageOnPanel( 133 | 'Player', 134 | None, 135 | fconfig['message']['onActivate'], 136 | 'green' 137 | ) 138 | elif shortcutHandle.switch: 139 | XModLib.ClientMessages.showMessageOnPanel( 140 | 'Player', 141 | None, 142 | fconfig['message']['onDeactivate'], 143 | 'red' 144 | ) 145 | pass 146 | return 147 | 148 | # ------------------------ # 149 | # PlayerAvatar Hooks # 150 | # ------------------------ # 151 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, Avatar.PlayerAvatar, 'shoot', invoke=XModLib.HookUtils.HookInvoke.MASTER) 152 | def new_PlayerAvatar_shoot(old_PlayerAvatar_shoot, self, *args, **kwargs): 153 | config = g_config['plugins']['safeShot'] 154 | def isIgnoredCtrlMode(ctrlModeName): 155 | return ctrlModeName not in (AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARCADE, AvatarInputHandler.aih_constants.CTRL_MODE_NAME.SNIPER) 156 | if not config['enabled'] or not config['activated'] or isIgnoredCtrlMode(self.inputHandler.ctrlModeName): 157 | return old_PlayerAvatar_shoot(self, *args, **kwargs) 158 | gunTargetClient = getattr(self.inputHandler.ctrl, '_clientTarget', None) 159 | gunTargetServer = getattr(self.inputHandler.ctrl, '_serverTarget', None) 160 | gunTarget = gunTargetClient or gunTargetServer 161 | aimTarget = XModLib.TargetScanners.StandardScanner().getTarget() 162 | aimTarget = aimTarget or (gunTarget if config['useGunTarget'] else None) 163 | isEnemy = XModLib.ArenaInfo.isEnemy 164 | isTeamKiller = XModLib.ArenaInfo.isTeamKiller 165 | getShortName = XModLib.ArenaInfo.getShortName 166 | getPlayerName = XModLib.ArenaInfo.getPlayerName 167 | def isHostile(vehicleID): 168 | return isEnemy(vehicleID) or config['considerBlueHostile'] and isTeamKiller(vehicleID) 169 | def isFreshFrag(vehicle): 170 | return not vehicle.isAlive() and getattr(vehicle, '_deathTime', BigWorld.time()) + config['fragExpirationTimeout'] >= BigWorld.time() 171 | def isWasteCtrlMode(ctrlModeName): 172 | return ctrlModeName in (AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARCADE, ) 173 | reason, target = None, None 174 | if config['reasons']['waste']['enabled'] and gunTarget is None and isWasteCtrlMode(self.inputHandler.ctrlModeName): 175 | reason, target = 'waste', gunTarget 176 | elif config['reasons']['team']['enabled'] and aimTarget is not None and aimTarget.isAlive() and not isHostile(aimTarget.id): 177 | reason, target = 'team', aimTarget 178 | elif config['reasons']['dead']['enabled'] and aimTarget is not None and isFreshFrag(aimTarget) and isHostile(aimTarget.id): 179 | reason, target = 'dead', aimTarget 180 | if reason is None: 181 | return old_PlayerAvatar_shoot(self, *args, **kwargs) 182 | rconfig = config['reasons'][reason] 183 | error = config['template'](reason=rconfig['template']) 184 | XModLib.ClientMessages.showMessageOnPanel('VehicleError', reason, error, 'red') 185 | if reason == 'team' and rconfig['chat']['enabled']: 186 | channel = XModLib.ClientMessages.getBattleChatControllers()[1] 187 | if channel is not None and channel.canSendMessage()[0]: 188 | message = rconfig['chat']['message'](player=getPlayerName(target.id), vehicle=getShortName(target.id)) 189 | channel.sendMessage(message.encode('utf-8')) 190 | if self._PlayerAvatar__tryShootCallbackId is None: 191 | self._PlayerAvatar__tryShootCallbackId = BigWorld.callback(0.0, self._PlayerAvatar__tryShootCallback) 192 | return 193 | -------------------------------------------------------------------------------- /source/plugins/SniperModeSPGExtension.py: -------------------------------------------------------------------------------- 1 | # ------------ # 2 | # Python # 3 | # ------------ # 4 | # nothing 5 | 6 | # -------------- # 7 | # BigWorld # 8 | # -------------- # 9 | import BigWorld 10 | 11 | # ---------------- # 12 | # WoT Client # 13 | # ---------------- # 14 | import AvatarInputHandler.aih_constants 15 | 16 | # ---------------------- # 17 | # WoT Client Hooks # 18 | # ---------------------- # 19 | import AvatarInputHandler.control_modes 20 | import gui.Scaleform.daapi.view.battle.shared.crosshair.gm_factory 21 | 22 | # ------------------- # 23 | # X-Mod Library # 24 | # ------------------- # 25 | import XModLib.HookUtils 26 | import XModLib.KeyboardUtils 27 | 28 | # ----------------------------------- # 29 | # Plug-in default configuration # 30 | # ----------------------------------- # 31 | g_globals['appDefaultConfig']['plugins']['sniperModeSPG'] = { 32 | 'enabled': ('Bool', False), 33 | 'shortcut': ('SimpleShortcut', 'KEY_E', {'switch': True, 'invert': False}) 34 | } 35 | 36 | # ----------------------------------------- # 37 | # Plug-in configuration reading stage # 38 | # ----------------------------------------- # 39 | g_config['plugins']['sniperModeSPG'] = g_globals['appConfigReader']( 40 | XModLib.XMLConfigReader.overrideOpenSubSection(g_globals['appConfigFile'], 'plugins/sniperModeSPG'), 41 | g_globals['appDefaultConfig']['plugins']['sniperModeSPG'] 42 | ) 43 | 44 | # ------------------------------------ # 45 | # Plug-in hooks injection events # 46 | # ------------------------------------ # 47 | p_inject_hooks = XModLib.HookUtils.HookEvent() 48 | p_inject_ovrds = XModLib.HookUtils.HookEvent() 49 | 50 | # ------------------------ # 51 | # Plug-in init stage # 52 | # ------------------------ # 53 | if g_config['applicationEnabled'] and g_config['plugins']['sniperModeSPG']['enabled']: 54 | p_inject_stage_main += p_inject_hooks 55 | p_inject_stage_init += p_inject_ovrds 56 | 57 | # --------------------------------- # 58 | # ControlMarkersFactory Hooks # 59 | # --------------------------------- # 60 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, gui.Scaleform.daapi.view.battle.shared.crosshair.gm_factory._ControlMarkersFactory, '_createSPGMarkers', invoke=XModLib.HookUtils.HookInvoke.MASTER) 61 | def new_ControlMarkersFactory_createSPGMarkers(old_ControlMarkersFactory_createSPGMarkers, self, markersInfo, components=None): 62 | result = old_ControlMarkersFactory_createSPGMarkers(self, markersInfo, components=components) 63 | if markersInfo.isServerMarkerActivated: 64 | dataProvider = markersInfo.serverMarkerDataProvider 65 | markerType = AvatarInputHandler.aih_constants.GUN_MARKER_TYPE.SERVER 66 | elif markersInfo.isClientMarkerActivated: 67 | dataProvider = markersInfo.clientMarkerDataProvider 68 | markerType = AvatarInputHandler.aih_constants.GUN_MARKER_TYPE.CLIENT 69 | else: 70 | dataProvider = None 71 | markerType = AvatarInputHandler.aih_constants.GUN_MARKER_TYPE.UNDEFINED 72 | return result + (self._createSniperMarker(markerType, dataProvider, components=components), ) 73 | 74 | # ----------------------------- # 75 | # ArcadeControlMode Hooks # 76 | # ----------------------------- # 77 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes.ArcadeControlMode, '_ArcadeControlMode__activateAlternateMode') 78 | def new_ArcadeControlMode_activateAlternateMode(self, pos=None, bByScroll=False): 79 | if g_config['plugins']['sniperModeSPG']['enabled']: 80 | if not BigWorld.player().isGunLocked and not BigWorld.player().isOwnBarrelUnderWater: 81 | if self._aih.isSPG and bByScroll: 82 | self._aih.onControlModeChanged( 83 | AvatarInputHandler.aih_constants.CTRL_MODE_NAME.SNIPER, 84 | preferredPos=self.camera.aimingSystem.getDesiredShotPoint(), 85 | aimingMode=self.aimingMode, 86 | saveZoom=False, 87 | equipmentID=None 88 | ) 89 | return 90 | 91 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes.ArcadeControlMode, 'handleKeyEvent', invoke=XModLib.HookUtils.HookInvoke.MASTER) 92 | def new_ArcadeControlMode_handleKeyEvent(old_ArcadeControlMode_handleKeyEvent, self, isDown, key, mods, event=None): 93 | result = old_ArcadeControlMode_handleKeyEvent(self, isDown, key, mods, event) 94 | ## Keyboard event parsing 95 | kbevent = XModLib.KeyboardUtils.KeyboardEvent(event) 96 | ## AvatarInputHandler started, not detached, control mode supported, event not handled by game (for AvatarInputHandler core switches) 97 | if not result and self._aih.isSPG: 98 | ## HotKeys - SPG Sniper Mode 99 | mconfig = g_config['plugins']['sniperModeSPG'] 100 | if mconfig['enabled']: 101 | ## HotKeys - SPG Sniper Mode - Global 102 | fconfig = mconfig 103 | shortcutHandle = fconfig['enabled'] and fconfig['shortcut'](kbevent) 104 | if shortcutHandle and (not shortcutHandle.switch or shortcutHandle.pushed): 105 | if not BigWorld.player().isGunLocked and not BigWorld.player().isOwnBarrelUnderWater: 106 | self._aih.onControlModeChanged( 107 | AvatarInputHandler.aih_constants.CTRL_MODE_NAME.SNIPER, 108 | preferredPos=self.camera.aimingSystem.getDesiredShotPoint(), 109 | aimingMode=self.aimingMode, 110 | saveZoom=True, 111 | equipmentID=None 112 | ) 113 | return result 114 | 115 | # ----------------------------- # 116 | # SniperControlMode Hooks # 117 | # ----------------------------- # 118 | @XModLib.HookUtils.methodHookExt(p_inject_hooks, AvatarInputHandler.control_modes.SniperControlMode, 'handleKeyEvent', invoke=XModLib.HookUtils.HookInvoke.MASTER) 119 | def new_SniperControlMode_handleKeyEvent(old_SniperControlMode_handleKeyEvent, self, isDown, key, mods, event=None): 120 | result = old_SniperControlMode_handleKeyEvent(self, isDown, key, mods, event) 121 | ## Keyboard event parsing 122 | kbevent = XModLib.KeyboardUtils.KeyboardEvent(event) 123 | ## AvatarInputHandler started, not detached, control mode supported, event not handled by game (for AvatarInputHandler core switches) 124 | if not result and self._aih.isSPG: 125 | ## HotKeys - SPG Sniper Mode 126 | mconfig = g_config['plugins']['sniperModeSPG'] 127 | if mconfig['enabled']: 128 | ## HotKeys - SPG Sniper Mode - Global 129 | fconfig = mconfig 130 | shortcutHandle = fconfig['enabled'] and fconfig['shortcut'](kbevent) 131 | if shortcutHandle and (not shortcutHandle.switch or shortcutHandle.pushed): 132 | if not BigWorld.player().isGunLocked and not BigWorld.player().isOwnBarrelUnderWater: 133 | self._aih.onControlModeChanged( 134 | AvatarInputHandler.aih_constants.CTRL_MODE_NAME.ARCADE, 135 | preferredPos=self.camera.aimingSystem.getDesiredShotPoint(), 136 | turretYaw=self.camera.aimingSystem.turretYaw, 137 | gunPitch=self.camera.aimingSystem.gunPitch, 138 | aimingMode=self.aimingMode, 139 | closesDist=False 140 | ) 141 | return result 142 | --------------------------------------------------------------------------------