shipmodulebutton.py (61249B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\inflight\shipModuleButton\shipmodulebutton.py 2 import math 3 import numbers 4 import re 5 import dogma.data 6 import eveicon 7 import evetypes 8 from carbon.client.script.environment.AudioUtil import PlaySound 9 from carbon.client.script.util.misc import HasAttrs 10 from carbon.common.script.sys.serviceConst import ROLE_GML, ROLE_WORLDMOD 11 from carbon.common.script.util import timerstuff 12 from carbon.common.script.util.format import FmtAmt 13 from carbonui.control.contextMenu.menuEntryData import MenuEntryData 14 from carbonui.primitives.container import Container 15 from carbonui.primitives.sprite import Sprite 16 from carbonui.loggers.buttonLogger import log_button_clicked 17 from eve.client.script.environment.godma import MODULE_NOT_OVERLOADED, MODULE_OVERLOADED, MODULE_PENDING_OVERLOADING, MODULE_PENDING_STOPOVERLOADING 18 from eve.client.script.ui.control import eveIcon, eveLabel 19 from eveexceptions import UserError 20 from inventorycommon.util import GetItemVolume 21 import uthread 22 from eve.client.script.ui.util import uix 23 import blue 24 from eve.client.script.parklife import states as state 25 import carbonui.const as uiconst 26 import inventorycommon.const as invconst 27 import log 28 import localization 29 import trinity 30 from eve.client.script.ui.crimewatch import crimewatchConst 31 from eve.client.script.ui.inflight.shipModuleButton.moduleButtonTooltip import TooltipModuleWrapper 32 from eve.client.script.ui.inflight.shipModuleButton.ramps import DamageStateCont, ShipModuleButtonRamps, ShipModuleReactivationTimer 33 from eve.client.script.ui.tooltips.tooltipHandler import TOOLTIP_SETTINGS_MODULE, TOOLTIP_DELAY_MODULE 34 from eve.common.script.sys.eveCfg import IsDocked 35 from eve.common.script.sys.rowset import IndexedRows 36 from carbonui.uicore import uicore 37 from menu import MenuLabel 38 from player_messaging.client.ui_message import prompt_player_fsd_dialog 39 cgre = re.compile('chargeGroup\\d{1,2}') 40 GLOWCOLOR = (0.24, 0.67, 0.16, 0.75) 41 BUSYCOLOR = (1.0, 0.13, 0.0, 0.73) 42 OVERLOADBTN_INDEX = 1 43 MODULEHINTDELAY = 800 44 GROUPS_THAT_ALWAYS_AUTO_REPEAT = (invconst.groupMiningLaser, invconst.groupStripMiner, invconst.groupEntosisLink) 45 SOUND_MODULE_ONLINE = 'ui_module_online_play' 46 SOUND_MODULE_OFFLINE = 'ui_module_offline_play' 47 48 class ModuleButton(Container): 49 __guid__ = 'xtriui.ModuleButton' 50 __notifyevents__ = ['OnStateChange', 51 'OnModuleRepaired', 52 'OnAmmoInBankChanged', 53 'OnFailLockTarget', 54 'OnChargeBeingLoadedToModule', 55 'OnModulesDeactivating', 56 'OnMultiModuleRepairStarted'] 57 __update_on_reload__ = 1 58 __cgattrs__ = [] 59 __chargesizecache__ = {} 60 default_name = 'ModuleButton' 61 default_pickRadius = 20 62 isDragObject = True 63 def_effect = None 64 charge = None 65 target = None 66 waitingForActiveTarget = 0 67 online = False 68 stateManager = None 69 dogmaLocation = None 70 autorepeat = 0 71 autoreload = 0 72 quantity = None 73 invReady = 1 74 invCookie = None 75 isInvItem = 1 76 isBeingRepaired = 0 77 blinking = 0 78 blinkingDamage = 0 79 effect_activating = 0 80 typeName = '' 81 ramp_active = False 82 isMaster = 0 83 animation = None 84 isPendingUnlockForDeactivate = False 85 moduleHintTimer = None 86 shouldUpdate = False 87 moduleButtonHint = None 88 moduleinfo = None 89 tooltipPanelClassInfo = TooltipModuleWrapper() 90 91 @property 92 def reloadingAmmo(self): 93 try: 94 return self.stateManager.IsModuleReloading(self.moduleinfo.itemID) 95 except KeyError: 96 return False 97 98 def ApplyAttributes(self, attributes): 99 Container.ApplyAttributes(self, attributes) 100 self.icon = eveIcon.Icon(parent=self, align=uiconst.TOALL, state=uiconst.UI_DISABLED) 101 self.reloadAnimationThread = self.blinkButtonThread = None 102 sm.RegisterNotify(self) 103 104 def Close(self, *args, **kwds): 105 if getattr(self, 'invCookie', None) is not None: 106 sm.GetService('inv').Unregister(self.invCookie) 107 Container.Close(self, *args, **kwds) 108 109 def GetRepeatCount(self): 110 repeat = settings.char.autorepeat.Get(self.moduleinfo.itemID, -1) 111 alwaysAutoRepeats = evetypes.GetGroupID(self.moduleinfo.typeID) in GROUPS_THAT_ALWAYS_AUTO_REPEAT 112 containsRepeatableEffect = any(map(self.IsEffectRepeatable, self.moduleinfo.effects.itervalues())) 113 if alwaysAutoRepeats: 114 return 1000 115 if repeat != -1: 116 return repeat 117 if containsRepeatableEffect: 118 return 1000 119 return 0 120 121 def Setup(self, moduleinfo, grey = None): 122 self.crimewatchSvc = sm.GetService('crimewatchSvc') 123 if not len(self.__cgattrs__): 124 self.__cgattrs__.extend([dogma.data.get_charge_group_attribute_ids()]) 125 self.id = moduleinfo.itemID 126 self.moduleinfo = moduleinfo 127 self.locationFlag = moduleinfo.flagID 128 self.stateManager = sm.StartService('godma').GetStateManager() 129 self.dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 130 self.grey = grey 131 self.isInActiveState = True 132 self.isDeactivating = False 133 icon = self.parent.GetChild('overloadBtn') 134 icon.hint = localization.GetByLabel('UI/Inflight/Overload/TurnOnOverload') 135 icon.OnClick = self.ToggleOverload 136 icon.OnMouseDown = (self.OLButtonDown, icon) 137 icon.OnMouseUp = (self.OLButtonUp, icon) 138 icon.OnMouseEnter = (self.OLMouseEnter, icon) 139 icon.OnMouseExit = (self.OLMouseExit, icon) 140 icon.SetOrder(OVERLOADBTN_INDEX) 141 self.sr.overloadButton = icon 142 if cfg.IsChargeCompatible(moduleinfo): 143 self.invCookie = sm.GetService('inv').Register(self) 144 self.autoreload = settings.char.autoreload.Get(self.moduleinfo.itemID, 1) 145 if evetypes.GetCategoryID(moduleinfo.typeID) == const.categoryCharge: 146 self.SetCharge(moduleinfo) 147 else: 148 self.SetCharge(None) 149 self.autoreload = settings.char.autoreload.Get(self.moduleinfo.itemID, 1) 150 for key in moduleinfo.effects.iterkeys(): 151 effect = moduleinfo.effects[key] 152 if self.IsEffectActivatible(effect): 153 self.def_effect = effect 154 if effect.isActive: 155 if effect.isDeactivating: 156 self.SetDeactivating() 157 else: 158 self.SetActive() 159 if effect.effectName == 'online': 160 if effect.isActive: 161 self.ShowOnline() 162 else: 163 self.ShowOffline() 164 165 self.autoreload = settings.char.autoreload.Get(self.moduleinfo.itemID, 1) 166 repairTimeStamps = self.stateManager.GetRepairTimeStamp(self.id) 167 if repairTimeStamps: 168 self.isBeingRepaired = True 169 self.SetRepairing(repairTimeStamps) 170 self.TryStartCooldownTimers() 171 reloadTimes = self.stateManager.GetReloadTimes(self.id) 172 if reloadTimes: 173 startTime, duration = reloadTimes 174 self.DoReloadAnimation(duration, startTime=startTime) 175 repeatCount = self.GetRepeatCount() 176 self.SetRepeat(repeatCount) 177 self.autoreload = settings.char.autoreload.Get(self.moduleinfo.itemID, 1) 178 if not self.isDeactivating: 179 self.isInActiveState = True 180 else: 181 self.isInActiveState = False 182 self.slaves = self.dogmaLocation.GetSlaveModules(self.moduleinfo.itemID, session.shipid) 183 self.UpdateModuleDamage() 184 self.EnableDrag() 185 self.autoreload = settings.char.autoreload.Get(self.moduleinfo.itemID, 1) 186 uthread.new(self.BlinkIcon) 187 188 def UpdateModuleDamage(self): 189 moduleDamage = self.GetModuleDamage() 190 if moduleDamage: 191 self.SetDamage(moduleDamage / self.moduleinfo.hp) 192 else: 193 self.SetDamage(0.0) 194 195 def OLButtonDown(self, btn, *args): 196 btn.top = 6 197 198 def OLButtonUp(self, btn, *args): 199 btn.top = 5 200 201 def OLMouseExit(self, btn, *args): 202 btn.top = 5 203 204 def OLMouseEnter(self, btn, *args): 205 PlaySound(uiconst.SOUND_BUTTON_HOVER) 206 207 def ToggleOverload(self, *args): 208 icon = getattr(getattr(self, 'sr', None), 'overloadButton', None) 209 if icon is not None: 210 icon.analyticID = 'ship_hud.module_overload' 211 log_button_clicked(icon) 212 if settings.user.ui.Get('lockOverload', 0): 213 existingModal = uicore.registry.GetModalWindow() 214 if existingModal and existingModal.msgKey == 'LockedOverloadState': 215 return 216 prompt_player_fsd_dialog('error') 217 prompt_player_fsd_dialog('LockedOverloadState') 218 return 219 for effect in self.moduleinfo.effects.itervalues(): 220 if effect.effectCategory == const.dgmEffOverload: 221 effectID = effect.effectID 222 break 223 else: 224 return 225 226 overloadState = self.stateManager.GetOverloadState(self.moduleinfo.itemID) 227 prompt_player_fsd_dialog('click') 228 itemID = self.moduleinfo.itemID 229 if overloadState == MODULE_NOT_OVERLOADED: 230 self.stateManager.Overload(itemID, effectID) 231 self.sr.overloadButton.hint = localization.GetByLabel('UI/Inflight/Overload/TurnOffOverload') 232 elif overloadState == MODULE_OVERLOADED: 233 self.stateManager.StopOverload(itemID, effectID) 234 self.sr.overloadButton.hint = localization.GetByLabel('UI/Inflight/Overload/TurnOnOverload') 235 elif overloadState == MODULE_PENDING_OVERLOADING: 236 self.stateManager.StopOverload(itemID, effectID) 237 elif overloadState == MODULE_PENDING_STOPOVERLOADING: 238 self.stateManager.StopOverload(itemID, effectID) 239 240 def UpdateOverloadState(self): 241 overloadState = self.stateManager.GetOverloadState(self.moduleinfo.itemID) 242 if overloadState == MODULE_PENDING_OVERLOADING: 243 self.animation = uicore.animations.BlinkIn(self.sr.overloadButton, startVal=1.8, endVal=1.0, duration=0.5, loops=uiconst.ANIM_REPEAT) 244 elif overloadState == MODULE_PENDING_STOPOVERLOADING: 245 self.animation = uicore.animations.BlinkIn(self.sr.overloadButton, startVal=0.2, endVal=1.0, duration=0.5, loops=uiconst.ANIM_REPEAT) 246 else: 247 if self.animation: 248 self.animation.Stop() 249 self.sr.overloadButton.SetAlpha(1.0) 250 if overloadState == MODULE_OVERLOADED: 251 self.sr.overloadButton.hint = localization.GetByLabel('UI/Inflight/Overload/TurnOffOverload') 252 elif overloadState == MODULE_NOT_OVERLOADED: 253 self.sr.overloadButton.hint = localization.GetByLabel('UI/Inflight/Overload/TurnOnOverload') 254 255 def InitQuantityLabel(self): 256 if self.sr.qtylabel is None: 257 quantityParent = Container(parent=self, name='quantityParent', pos=(18, 27, 24, 10), align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, idx=0) 258 self.sr.qtylabel = eveLabel.Label(text='', parent=quantityParent, fontsize=9, letterspace=1, left=3, top=0, width=30, state=uiconst.UI_DISABLED) 259 underlay = Sprite(parent=quantityParent, name='underlay', pos=(0, 0, 0, 0), align=uiconst.TOALL, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/ShipUI/slotQuantityUnderlay.png', color=(0.0, 0.0, 0.0, 1.0)) 260 261 def SetCharge(self, charge): 262 if charge and charge.stacksize != 0: 263 if self.charge is None or charge.typeID != self.charge.typeID: 264 self.icon.LoadIconByTypeID(charge.typeID) 265 self.charge = charge 266 self.stateManager.ChangeAmmoTypeForModule(self.moduleinfo.itemID, charge.typeID) 267 self.id = charge.itemID 268 self.UpdateChargeQuantity(charge) 269 else: 270 self.icon.LoadIconByTypeID(self.moduleinfo.typeID) 271 if self.sr.qtylabel: 272 self.sr.qtylabel.parent.state = uiconst.UI_HIDDEN 273 self.quantity = 0 274 self.id = self.moduleinfo.itemID 275 self.charge = None 276 self.CheckOverload() 277 self.CheckOnline() 278 self.CheckMasterSlave() 279 280 def UpdateChargeQuantity(self, charge): 281 if charge is self.charge: 282 if evetypes.GetGroupID(charge.typeID) in cfg.GetCrystalGroups(): 283 if self.sr.qtylabel: 284 self.sr.qtylabel.parent.state = uiconst.UI_HIDDEN 285 return 286 self.InitQuantityLabel() 287 self.quantity = charge.stacksize 288 self.sr.qtylabel.text = '%s' % FmtAmt(charge.stacksize) 289 self.sr.qtylabel.parent.state = uiconst.UI_DISABLED 290 291 def ShowGroupHighlight(self): 292 self.dragging = True 293 if self.sr.groupHighlight is None: 294 groupHighlight = Container(parent=self.parent, name='groupHighlight', pos=(0, 0, 64, 64), align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED) 295 leftCircle = Sprite(parent=groupHighlight, name='leftCircle', pos=(0, 0, 32, 64), texturePath='res:/UI/Texture/classes/ShipUI/slotRampLeft.png') 296 rightCircle = Sprite(parent=groupHighlight, name='leftCircle', pos=(32, 0, 32, 64), texturePath='res:/UI/Texture/classes/ShipUI/slotRampRight.png') 297 self.sr.groupHighlight = groupHighlight 298 else: 299 self.sr.groupHighlight.state = uiconst.UI_DISABLED 300 uthread.new(self.PulseGroupHighlight) 301 302 def StopShowingGroupHighlight(self): 303 self.dragging = False 304 if self.sr.groupHighlight: 305 self.sr.groupHighlight.state = uiconst.UI_HIDDEN 306 307 def PulseGroupHighlight(self): 308 pulseSize = 0.4 309 opacity = 1.0 310 startTime = blue.os.GetSimTime() 311 while self.dragging: 312 self.sr.groupHighlight.opacity = opacity 313 blue.pyos.synchro.SleepWallclock(200) 314 if not self or self.destroyed: 315 break 316 sinWave = math.cos(float(blue.os.GetSimTime() - startTime) / (0.5 * const.SEC)) 317 opacity = min(sinWave * pulseSize + (1 - pulseSize / 2), 1) 318 319 def SetDamage(self, damage): 320 if not damage or damage < 0.0001: 321 if self.sr.damageState: 322 self.sr.damageState.state = uiconst.UI_HIDDEN 323 return 324 imageIndex = max(1, int(damage * 8)) 325 if self.sr.damageState is None: 326 if self.sr.ramps: 327 idx = OVERLOADBTN_INDEX + 2 328 else: 329 idx = OVERLOADBTN_INDEX + 1 330 self.sr.damageState = DamageStateCont(parent=self.parent, name='damageState', pos=(0, 0, 64, 64), align=uiconst.TOPLEFT, state=uiconst.UI_NORMAL, texturePath='res:/UI/Texture/classes/ShipUI/slotDamage_%s.png' % imageIndex, idx=idx) 331 self.sr.damageState.state = uiconst.UI_NORMAL 332 self.sr.damageState.SetDamage(damage) 333 amount = self.moduleinfo.damage / self.moduleinfo.hp * 100 334 self.sr.damageState.hint = localization.GetByLabel('UI/Inflight/Overload/hintDamagedModule', preText='', amount=amount) 335 self.sr.damageState.Blink(damage) 336 337 def GetVolume(self): 338 if self.charge: 339 return GetItemVolume(self.charge, 1) 340 341 def IsItemHere(self, rec): 342 ret = rec.locationID == session.shipid and rec.flagID == self.locationFlag and evetypes.GetCategoryID(rec.typeID) == const.categoryCharge 343 return ret 344 345 def AddItem(self, rec): 346 if evetypes.GetCategoryID(rec.typeID) == const.categoryCharge: 347 self.SetCharge(rec) 348 349 def UpdateItem(self, rec, change): 350 if evetypes.GetCategoryID(rec.typeID) == const.categoryCharge: 351 self.SetCharge(rec) 352 353 def RemoveItem(self, rec): 354 if evetypes.GetCategoryID(rec.typeID) == const.categoryCharge: 355 if self.charge and rec.itemID == self.id: 356 self.SetCharge(None) 357 358 def GetShell(self): 359 return sm.GetService('invCache').GetInventoryFromId(session.shipid) 360 361 def IsCorrectChargeSize(self, item, wantChargeSize): 362 if item.typeID not in self.__chargesizecache__: 363 cRS = dogma.data.get_type_attributes(item.typeID) 364 cAttribs = IndexedRows(cRS, ('attributeID',)) 365 if const.attributeChargeSize in cAttribs: 366 gotChargeSize = cAttribs[const.attributeChargeSize].value 367 else: 368 gotChargeSize = 0 369 self.__chargesizecache__[item.typeID] = gotChargeSize 370 else: 371 gotChargeSize = self.__chargesizecache__[item.typeID] 372 if wantChargeSize != gotChargeSize: 373 return 0 374 return 1 375 376 def BlinkIcon(self, time = None): 377 if self.destroyed or self.blinking: 378 return 379 startTime = blue.os.GetSimTime() 380 if time is not None: 381 timeToBlink = time * 10000 382 while self.reloadingAmmo or self.waitingForActiveTarget or time: 383 if time is not None: 384 if blue.os.GetSimTime() - startTime > timeToBlink: 385 break 386 blue.pyos.synchro.SleepWallclock(250) 387 if self.destroyed: 388 return 389 self.icon.SetAlpha(0.25) 390 blue.pyos.synchro.SleepWallclock(250) 391 if self.destroyed: 392 return 393 self.icon.SetAlpha(1.0) 394 395 if self.destroyed: 396 return 397 self.blinking = 0 398 self.CheckOverload() 399 self.CheckOnline() 400 401 def DoNothing(self, *args): 402 pass 403 404 def CopyItemIDToClipboard(self, itemID): 405 blue.pyos.SetClipboardData(str(itemID)) 406 407 def GetMenu(self): 408 ship = sm.GetService('godma').GetItem(session.shipid) 409 if ship is None: 410 return [] 411 m = [] 412 if session.role & (ROLE_GML | ROLE_WORLDMOD): 413 m.append(('GM / WM Extras', self._GetMenuElevated())) 414 groupID = evetypes.GetGroupID(self.moduleinfo.typeID) 415 if cfg.IsChargeCompatible(self.moduleinfo): 416 chargeTypeID, chargeQuantity, roomForReload = self.GetChargeReloadInfo() 417 m.extend(self.dogmaLocation.GetAmmoMenu(session.shipid, self.moduleinfo.itemID, self.charge, roomForReload)) 418 if self.autoreload == 0: 419 m.append((MenuLabel('UI/Inflight/ModuleRacks/AutoReloadOn'), self.SetAutoReload, (1,))) 420 else: 421 m.append((MenuLabel('UI/Inflight/ModuleRacks/AutoReloadOff'), self.SetAutoReload, (0,))) 422 overloadLock = settings.user.ui.Get('lockOverload', 0) 423 itemID = self.moduleinfo.itemID 424 slaves = self.dogmaLocation.GetSlaveModules(itemID, session.shipid) 425 thermodynamicsLevel = sm.GetService('skills').GetMyLevel(invconst.typeThermodynamics) 426 for key in self.moduleinfo.effects.iterkeys(): 427 effect = self.moduleinfo.effects[key] 428 if self.IsEffectRepeatable(effect) and groupID not in GROUPS_THAT_ALWAYS_AUTO_REPEAT: 429 if self.autorepeat == 0: 430 m.append((MenuLabel('UI/Inflight/ModuleRacks/AutoRepeatOn'), self.SetRepeat, (1000,))) 431 else: 432 m.append((MenuLabel('UI/Inflight/ModuleRacks/AutoRepeatOff'), self.SetRepeat, (0,))) 433 if effect.effectName == 'online': 434 m.append(None) 435 if not slaves: 436 if effect.isActive: 437 m.append((MenuLabel('UI/Inflight/ModuleRacks/PutModuleOffline'), self.ChangeOnline, (0,))) 438 else: 439 m.append((MenuLabel('UI/Inflight/ModuleRacks/PutModuleOnline'), self.ChangeOnline, (1,))) 440 if not overloadLock and effect.effectCategory == const.dgmEffOverload: 441 requiredThermoLevel = self.dogmaLocation.GetAttributeValue(itemID, const.attributeRequiredThermoDynamicsSkill) 442 if thermodynamicsLevel >= requiredThermoLevel: 443 active = effect.isActive 444 if active: 445 m.append(MenuEntryData(MenuLabel('UI/Inflight/Overload/TurnOffOverload'), lambda _effect = effect: self.Overload(False, _effect), texturePath=eveicon.pause)) 446 else: 447 m.append(MenuEntryData(MenuLabel('UI/Inflight/Overload/TurnOnOverload'), lambda _effect = effect: self.Overload(True, _effect), texturePath=eveicon.circle_plus)) 448 m.append((MenuLabel('UI/Inflight/OverloadRack'), self.OverloadRack, ())) 449 m.append((MenuLabel('UI/Inflight/StopOverloadingRack'), self.StopOverloadRack, ())) 450 451 moduleDamage = self.GetModuleDamage() 452 if moduleDamage: 453 if self.stateManager.IsBeingRepaired(self.moduleinfo.itemID): 454 m.append((MenuLabel('UI/Inflight/menuCancelRepair'), self.CancelRepair, ())) 455 else: 456 unitsToRepair = int(self.GetNumUnitsToRepair()) 457 if unitsToRepair: 458 m.append((MenuLabel('UI/Inflight/RepairWithQty', {'qty': unitsToRepair}), self.RepairModule, ())) 459 else: 460 m.append((MenuLabel('UI/Commands/Repair'), self.RepairModule, ())) 461 damagedModulesAndRepairCost = self.GetDamagedModulesAndRepairCost() 462 if len(damagedModulesAndRepairCost) > 1: 463 unitsToRepair = sum((x[1] for x in damagedModulesAndRepairCost)) 464 moduleIDs = [ x[0] for x in damagedModulesAndRepairCost ] 465 if self.moduleinfo.itemID in moduleIDs and moduleIDs.index(self.moduleinfo.itemID) != 0: 466 moduleIDs.remove(self.moduleinfo.itemID) 467 moduleIDs.insert(0, self.moduleinfo.itemID) 468 if unitsToRepair: 469 m.append((MenuLabel('UI/Inflight/RepairAllWithQty', {'qty': int(unitsToRepair)}), self.RepairManyModules, (moduleIDs,))) 470 else: 471 m.append((MenuLabel('UI/Commands/RepairAll'), self.RepairManyModules, (moduleIDs,))) 472 allRepairingModuleIDs = self.GetAllRepairingModules() 473 if len(allRepairingModuleIDs) > 1: 474 m.append((MenuLabel('UI/Inflight/menuCancelAllRepairs'), self.CancelRepairManyModuels, (allRepairingModuleIDs,))) 475 if slaves: 476 m.append((MenuLabel('UI/Fitting/ClearGroup'), self.UnlinkModule, ())) 477 m += [(MenuLabel('UI/Commands/ShowInfo'), sm.GetService('info').ShowInfo, (self.moduleinfo.typeID, 478 self.moduleinfo.itemID, 479 0, 480 self.moduleinfo))] 481 return m 482 483 def _GetMenuElevated(self): 484 m = [] 485 effectMenu = [] 486 if cfg.IsChargeCompatible(self.moduleinfo): 487 m += [('Launcher: ' + str(self.moduleinfo.itemID), self.CopyItemIDToClipboard, (self.moduleinfo.itemID,))] 488 if self.id != self.moduleinfo.itemID: 489 m += [('Charge: ' + str(self.id), self.CopyItemIDToClipboard, (self.id,)), None] 490 chargeTypeID, _, _ = self.GetChargeReloadInfo() 491 chargeDefaultEffectID, chargeEffectName = self._GetDefaultEffectIDAndName(chargeTypeID) 492 if chargeDefaultEffectID: 493 effectMenu += [('Charge defaultEffect: %s (%s)' % (chargeEffectName, chargeDefaultEffectID), self.CopyItemIDToClipboard, (chargeDefaultEffectID,))] 494 else: 495 m += [(str(self.id), self.CopyItemIDToClipboard, (self.id,)), None] 496 defaultEffectID, effectName = self._GetDefaultEffectIDAndName(self.moduleinfo.typeID) 497 if defaultEffectID: 498 effectMenu.insert(0, ('Module defaultEffect: %s (%s)' % (effectName, defaultEffectID), self.CopyItemIDToClipboard, (defaultEffectID,))) 499 if effectMenu: 500 m += effectMenu 501 m += [None] 502 m += sm.GetService('menu').GetGMTypeMenu(self.moduleinfo.typeID, itemID=self.id, divs=True, unload=True) 503 return m 504 505 def _GetDefaultEffectIDAndName(self, typeID): 506 try: 507 defaultEffectID = self.dogmaLocation.dogmaStaticMgr.GetDefaultEffect(typeID) 508 if not defaultEffectID: 509 return (None, None) 510 effect = self.dogmaLocation.dogmaStaticMgr.GetEffect(defaultEffectID) 511 return (defaultEffectID, effect.effectName) 512 except Exception: 513 return (None, None) 514 515 def RepairModule(self): 516 error = None 517 success = False 518 try: 519 success = self.stateManager.RepairModule(self.moduleinfo.itemID) 520 except UserError as e: 521 if e.msg == 'NotEnoughRepairMaterialToFinishAllRepairs': 522 error = e 523 else: 524 raise 525 526 if self.slaves: 527 for slave in self.slaves: 528 try: 529 success = self.stateManager.RepairModule(slave) or success 530 except UserError as e: 531 if e.msg == 'NotEnoughRepairMaterialToFinishAllRepairs': 532 error = e 533 else: 534 raise 535 536 if success: 537 self.isBeingRepaired = True 538 self.SetRepairing() 539 if error: 540 raise error 541 542 def RepairManyModules(self, moduleIDs): 543 self.stateManager.RepairManyModules(moduleIDs) 544 545 def CancelRepair(self): 546 success = self.stateManager.StopRepairModule(self.moduleinfo.itemID) 547 if self.slaves: 548 for slave in self.slaves: 549 success = self.stateManager.StopRepairModule(slave) and success 550 551 if success == True: 552 self.isBeingRepaired = False 553 self.RemoveRepairing() 554 555 def CancelRepairManyModuels(self, moduleIDs): 556 self.stateManager.StopRepairModuleManyModules(moduleIDs) 557 558 def OnModulesDeactivating(self, moduleIDs): 559 if self.moduleinfo.itemID in moduleIDs: 560 self.SetDeactivating() 561 562 def OnFailLockTarget(self, tid, *args): 563 self.waitingForActiveTarget = 0 564 565 def OnMultiModuleRepairStarted(self, moduleIDs): 566 if self.moduleinfo.itemID not in moduleIDs: 567 return 568 self.isBeingRepaired = True 569 self.SetRepairing() 570 571 def OnModuleRepaired(self, itemID): 572 if itemID == self.moduleinfo.itemID: 573 self.RemoveRepairing() 574 self.isBeingRepaired = False 575 self.UpdateModuleDamage() 576 577 def OnAmmoInBankChanged(self, masterID): 578 slaves = self.dogmaLocation.GetSlaveModules(masterID, session.shipid) 579 if self.moduleinfo.itemID in slaves: 580 self.SetCharge(self.moduleinfo) 581 582 def OnChargeBeingLoadedToModule(self, itemIDs, chargeTypeID, time): 583 if self.moduleinfo.itemID not in itemIDs: 584 return 585 if chargeTypeID is None: 586 if self.reloadAnimationThread and self.reloadAnimationThread.alive: 587 self.reloadAnimationThread.kill() 588 if self.blinkButtonThread and self.blinkButtonThread.alive: 589 self.blinkButtonThread.kill() 590 if self.sr.ramps: 591 self.sr.ramps.display = False 592 else: 593 chargeGroupID = evetypes.GetGroupID(chargeTypeID) 594 params = {'ammoGroupName': (const.UE_GROUPID, chargeGroupID), 595 'launcherGroupName': (const.UE_GROUPID, self.moduleinfo.groupID), 596 'time': time / 1000} 597 self.DoReloadAnimation(time) 598 self.DisplayLoadMessage(params) 599 600 def DoReloadAnimation(self, duration, startTime = None): 601 if startTime: 602 blinkTime = max(0, duration - (blue.os.GetSimTime() - startTime) / 10000) 603 else: 604 blinkTime = duration 605 self.reloadAnimationThread = uthread.new(self.ShowReloadLeft, duration, startTime) 606 self.blinkButtonThread = uthread.new(self.BlinkIcon, blinkTime) 607 608 def DisplayLoadMessage(self, params): 609 blue.pyos.synchro.SleepSim(0.1) 610 if self.reloadAnimationThread and self.reloadAnimationThread.alive: 611 prompt_player_fsd_dialog('LauncherLoadDelay', params) 612 613 def TryStartCooldownTimers(self): 614 cooldownTimes = self.stateManager.GetCooldownTimes(self.moduleinfo.itemID) 615 if cooldownTimes: 616 startTime, duration = cooldownTimes 617 self.DoReactivationAnimation(duration, startTime=startTime) 618 619 def DoReactivationAnimation(self, duration, startTime = None): 620 uthread.new(self.ShowReactivationLeft, duration, startTime) 621 622 def UnlinkModule(self): 623 self.dogmaLocation.DestroyWeaponBank(session.shipid, self.moduleinfo.itemID) 624 625 def Overload(self, onoff, eff): 626 if onoff: 627 eff.Activate() 628 else: 629 eff.Deactivate() 630 631 def OverloadRack(self): 632 sm.GetService('godma').OverloadRack(self.moduleinfo.itemID) 633 634 def StopOverloadRack(self): 635 sm.GetService('godma').StopOverloadRack(self.moduleinfo.itemID) 636 637 def GetChargeReloadInfo(self, ignoreCharge = 0): 638 evetypes.RaiseIFNotExists(self.moduleinfo.typeID) 639 lastChargeTypeID = self.stateManager.GetAmmoTypeForModule(self.moduleinfo.itemID) 640 if self.charge and not ignoreCharge: 641 chargeTypeID = self.charge.typeID 642 chargeQuantity = self.charge.stacksize 643 elif lastChargeTypeID is not None: 644 chargeTypeID = lastChargeTypeID 645 chargeQuantity = 0 646 else: 647 chargeTypeID = None 648 chargeQuantity = 0 649 if chargeTypeID is not None: 650 roomForReload = int(evetypes.GetCapacity(self.moduleinfo.typeID) / evetypes.GetVolume(chargeTypeID) - chargeQuantity + 1e-07) 651 else: 652 roomForReload = 0 653 return (chargeTypeID, chargeQuantity, roomForReload) 654 655 def SetAutoReload(self, on): 656 settings.char.autoreload.Set(self.moduleinfo.itemID, on) 657 self.autoreload = on 658 self.AutoReload() 659 660 def AutoReload(self, force = 0, useItemID = None, useQuant = None): 661 if self.reloadingAmmo is not False: 662 return 663 if not cfg.IsChargeCompatible(self.moduleinfo) or not (self.autoreload or force): 664 return 665 chargeTypeID, chargeQuantity, roomForReload = self.GetChargeReloadInfo() 666 if chargeQuantity > 0 and not force or roomForReload <= 0: 667 return 668 if not uicore.layer.shipui: 669 return 670 self.dogmaLocation.LoadAmmoTypeToModule(self.moduleinfo.itemID, chargeTypeID) 671 672 def CheckOverload(self): 673 if not self or self.destroyed: 674 return 675 isActive = False 676 hasOverloadEffect = False 677 if not HasAttrs(self, 'moduleinfo', 'effects'): 678 return 679 for key in self.moduleinfo.effects.iterkeys(): 680 effect = self.moduleinfo.effects[key] 681 if effect.effectCategory == const.dgmEffOverload: 682 if effect.isActive: 683 isActive = True 684 hasOverloadEffect = True 685 686 if hasOverloadEffect: 687 self.sr.overloadButton.top = 5 688 if self.online: 689 if isActive: 690 self.sr.overloadButton.LoadTexture('res:/UI/Texture/classes/ShipUI/slotOverloadOn.png') 691 self.sr.overloadButton.hint = localization.GetByLabel('UI/Inflight/Overload/TurnOffOverload') 692 else: 693 self.sr.overloadButton.LoadTexture('res:/UI/Texture/classes/ShipUI/slotOverloadOff.png') 694 self.sr.overloadButton.hint = localization.GetByLabel('UI/Inflight/Overload/TurnOnOverload') 695 self.sr.overloadButton.state = uiconst.UI_NORMAL 696 else: 697 self.sr.overloadButton.LoadTexture('res:/UI/Texture/classes/ShipUI/slotOverloadDisabled.png') 698 self.sr.overloadButton.state = uiconst.UI_DISABLED 699 else: 700 self.sr.overloadButton.top = 6 701 self.sr.overloadButton.LoadTexture('res:/UI/Texture/classes/ShipUI/slotOverloadDisabled.png') 702 self.sr.overloadButton.state = uiconst.UI_DISABLED 703 704 def CheckMasterSlave(self): 705 if not self or self.destroyed: 706 return 707 itemID = self.moduleinfo.itemID 708 slaves = self.dogmaLocation.GetSlaveModules(itemID, session.shipid) 709 if slaves: 710 if self.sr.stackParent is None: 711 stackParent = Container(parent=self, name='stackParent', pos=(6, 27, 12, 10), align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, idx=0) 712 self.sr.stacklabel = eveLabel.Label(text=len(slaves) + 1, parent=stackParent, fontsize=9, letterspace=1, left=5, top=0, width=30, state=uiconst.UI_DISABLED, shadowOffset=(0, 0), color=(1.0, 1.0, 1.0, 1)) 713 underlay = Sprite(parent=stackParent, name='underlay', pos=(0, 0, 0, 0), align=uiconst.TOALL, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/ShipUI/slotStackUnderlay.png', color=(0.51, 0.0, 0.0, 1.0)) 714 self.sr.stackParent = stackParent 715 else: 716 self.sr.stackParent.state = uiconst.UI_DISABLED 717 self.sr.stacklabel.text = len(slaves) + 1 718 elif self.sr.stackParent: 719 self.sr.stackParent.state = uiconst.UI_HIDDEN 720 721 def CheckOnline(self, sound = 0): 722 if not self or self.destroyed: 723 return 724 if not HasAttrs(self, 'moduleinfo', 'effects'): 725 return 726 for key in self.moduleinfo.effects.keys(): 727 effect = self.moduleinfo.effects[key] 728 if effect.effectName == 'online': 729 if effect.isActive: 730 self.ShowOnline() 731 if sound: 732 PlaySound(SOUND_MODULE_ONLINE) 733 else: 734 self.ShowOffline() 735 return 736 737 def ChangeOnline(self, on = 1): 738 uthread.new(self._ChangeOnline, on) 739 740 def _ChangeOnline(self, on): 741 masterID = self.dogmaLocation.IsInWeaponBank(session.shipid, self.moduleinfo.itemID) 742 if masterID: 743 if not on: 744 ret = prompt_player_fsd_dialog('CustomQuestion', {'header': 'OFFLINE', 745 'question': "When offlining this module you will destroy the weapons bank it's in. Are you sure you want to offline it? "}, uiconst.YESNO) 746 if ret != uiconst.ID_YES: 747 return 748 elif not on and prompt_player_fsd_dialog('PutOffline', {}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES: 749 return 750 for key in self.moduleinfo.effects.keys(): 751 effect = self.moduleinfo.effects[key] 752 if effect.effectName == 'online': 753 if on: 754 effect.Activate() 755 else: 756 self.ShowOffline(1) 757 try: 758 effect.Deactivate() 759 except UserError: 760 self.ShowOnline() 761 raise 762 763 return 764 765 def ShowOverload(self, on): 766 self.CheckOverload() 767 768 def ShowOnline(self): 769 self.isMaster = 0 770 if self.AreModulesOffline(): 771 self.ShowOffline() 772 return 773 self.online = True 774 if self.grey: 775 self.icon.SetAlpha(0.1) 776 else: 777 self.icon.SetAlpha(1.0) 778 self.CheckOverload() 779 780 def ShowOffline(self, ping = 0): 781 self.online = False 782 if self.grey: 783 self.icon.SetAlpha(0.1) 784 else: 785 self.icon.SetAlpha(0.25) 786 if ping: 787 PlaySound(SOUND_MODULE_OFFLINE) 788 self.CheckOverload() 789 self.isInActiveState = True 790 791 def AreModulesOffline(self): 792 slaves = self.dogmaLocation.GetSlaveModules(self.moduleinfo.itemID, session.shipid) 793 if not slaves: 794 return False 795 self.isMaster = 1 796 onlineEffect = self.stateManager.GetEffect(self.moduleinfo.itemID, 'online') 797 if onlineEffect is None or not onlineEffect.isActive: 798 return True 799 for slave in slaves: 800 onlineEffect = self.stateManager.GetEffect(slave, 'online') 801 if onlineEffect is None or not onlineEffect.isActive: 802 return True 803 804 return False 805 806 def IsEffectRepeatable(self, effect, activatibleKnown = 0): 807 if activatibleKnown or self.IsEffectActivatible(effect): 808 if not effect.item.disallowRepeatingActivation: 809 return effect.durationAttributeID is not None 810 return 0 811 812 def IsEffectActivatible(self, effect): 813 return effect.isDefault and effect.effectName != 'online' and effect.effectCategory in (const.dgmEffActivation, const.dgmEffTarget) 814 815 def SetRepeat(self, num): 816 settings.char.autorepeat.Set(self.moduleinfo.itemID, num) 817 self.autorepeat = num 818 819 def GetDefaultEffect(self): 820 if not self or self.destroyed: 821 return 822 if self.sr is None or self.moduleinfo is None or not self.stateManager.IsItemLoaded(self.moduleinfo.itemID): 823 return 824 for key in self.moduleinfo.effects.iterkeys(): 825 effect = self.moduleinfo.effects[key] 826 if self.IsEffectActivatible(effect): 827 return effect 828 829 def OnClick(self, *args): 830 if not self or self.IsBeingDragged() or not self.isInActiveState: 831 return 832 PlaySound(uiconst.SOUND_BUTTON_CLICK) 833 if uicore.uilib.Key(uiconst.VK_SHIFT): 834 self.ToggleOverload() 835 return 836 ctrlRepeat = 0 837 if uicore.uilib.Key(uiconst.VK_CONTROL): 838 ctrlRepeat = 1000 839 self.Click(ctrlRepeat) 840 841 def Click(self, ctrlRepeat = 0): 842 if self.stateManager.IsModuleReloading(self.moduleinfo.itemID): 843 return 844 if self.waitingForActiveTarget: 845 sm.GetService('target').CancelTargetOrder(self) 846 self.waitingForActiveTarget = 0 847 else: 848 if self.def_effect is None: 849 log.LogWarn('No default Effect available for this moduletypeID:', self.moduleinfo.typeID) 850 prompt_player_fsd_dialog('CustomNotify', {'notify': localization.GetByLabel('UI/Inflight/ModuleRacks/TryingToActivatePassiveModule')}) 851 return 852 if not self.online: 853 if getattr(self, 'isMaster', None): 854 prompt_player_fsd_dialog('ClickOffllineGroup') 855 else: 856 prompt_player_fsd_dialog('ClickOffllineModule') 857 return 858 if self.IsModuleActivatingInGodma(): 859 return 860 if self.def_effect.isActive: 861 self.DeactivateEffect(self.def_effect) 862 elif not self.effect_activating: 863 self.activationTimer = timerstuff.AutoTimer(500, self.ActivateEffectTimer) 864 self.effect_activating = 1 865 self.ActivateEffect(self.def_effect, ctrlRepeat=ctrlRepeat) 866 867 def ActivateModule(self): 868 if self.def_effect and not self.def_effect.isActive and not self.effect_activating: 869 self.Click() 870 871 def DeactivateModule(self): 872 if self.def_effect and self.def_effect.isActive: 873 self.Click() 874 875 def ActivateEffectTimer(self, *args): 876 self.effect_activating = 0 877 self.activationTimer = None 878 879 def OnEndDrag(self, *args): 880 uthread.new(uicore.layer.shipui.ResetSwapMode) 881 882 def GetDragData(self, *args): 883 if settings.user.ui.Get('lockModules', 0): 884 return [] 885 if self.charge: 886 fakeNode = uix.GetItemData(self.charge, 'icons') 887 fakeNode.isCharge = 1 888 else: 889 fakeNode = uix.GetItemData(self.moduleinfo, 'icons') 890 fakeNode.isCharge = 0 891 fakeNode.__guid__ = 'xtriui.ShipUIModule' 892 fakeNode.slotFlag = self.moduleinfo.flagID 893 uicore.layer.shipui.StartDragMode(self.moduleinfo.itemID, self.moduleinfo.typeID) 894 return [fakeNode] 895 896 def OnDropData(self, dragObj, nodes): 897 log.LogInfo('Module.OnDropData', self.id) 898 if not IsDocked() and dragObj.__guid__ == 'listentry.InvAssetItem': 899 return 900 flag1 = self.moduleinfo.flagID 901 flag2 = None 902 for node in nodes: 903 if node.Get('__guid__', None) == 'xtriui.ShipUIModule': 904 flag2 = node.slotFlag 905 break 906 907 if flag1 == flag2: 908 return 909 if flag2 is not None: 910 uicore.layer.shipui.ChangeSlots(flag1, flag2) 911 return 912 chargeTypeID = None 913 chargeItems = [] 914 for node in nodes: 915 if not hasattr(node, 'rec'): 916 return 917 chargeItem = node.rec 918 if not isinstance(chargeItem.itemID, numbers.Integral): 919 return 920 if not hasattr(chargeItem, 'categoryID'): 921 return 922 if chargeItem.categoryID != const.categoryCharge: 923 continue 924 if chargeTypeID is None: 925 chargeTypeID = chargeItem.typeID 926 if chargeItem.typeID == chargeTypeID: 927 chargeItems.append(chargeItem) 928 929 if len(chargeItems) > 0: 930 self.dogmaLocation.DropLoadChargeToModule(self.moduleinfo.itemID, chargeTypeID, chargeItems=chargeItems) 931 932 def OnMouseHover(self, *args): 933 if uicore.uilib.Key(uiconst.VK_SHIFT): 934 self.OverloadHiliteOn() 935 else: 936 self.OverloadHiliteOff() 937 938 def OnMouseDown(self, *args): 939 Container.OnMouseDown(self, *args) 940 log.LogInfo('Module.OnMouseDown', self.id) 941 if getattr(self, 'downTop', None) is not None or not self.isInActiveState or self.def_effect is None: 942 return 943 if not uicore.uilib.Key(uiconst.VK_SHIFT) and self.IsModuleActivatingInGodma(): 944 return 945 self.downTop = self.parent.top 946 self.parent.top += 2 947 948 def IsModuleActivatingInGodma(self): 949 effectName = self.def_effect.effectName 950 isActivating = self.stateManager.IsModuleActivating(self.moduleinfo.itemID, effectName) 951 if isActivating: 952 return True 953 return False 954 955 def OnMouseUp(self, *args): 956 Container.OnMouseUp(self, *args) 957 if self.destroyed: 958 return 959 log.LogInfo('Module.OnMouseUp', self.id) 960 if getattr(self, 'downTop', None) is not None: 961 self.parent.top = self.downTop 962 self.downTop = None 963 if len(args) > 0 and args[0] == uiconst.MOUSERIGHT and getattr(uicore.layer.hint, 'moduleButtonHint', None): 964 uicore.layer.hint.moduleButtonHint.FadeOpacity(0.0) 965 966 def OnMouseEnter(self, *args): 967 uthread.pool('ShipMobuleButton::MouseEnter', self.MouseEnter) 968 969 def MouseEnter(self, *args): 970 if self.destroyed or sm.GetService('godma').GetItem(self.moduleinfo.itemID) is None: 971 return 972 PlaySound(uiconst.SOUND_BUTTON_HOVER) 973 if uicore.uilib.Key(uiconst.VK_SHIFT): 974 self.OverloadHiliteOn() 975 self.SetHilite() 976 tacticalSvc = sm.GetService('tactical') 977 bracketMgr = sm.GetService('bracket') 978 maxRange, falloffDist, bombRadius, _ = tacticalSvc.FindMaxRange(self.moduleinfo, self.charge) 979 if maxRange > 0: 980 bracketMgr.ShowModuleRange(self.moduleinfo.itemID, maxRange + falloffDist) 981 bracketMgr.ShowHairlinesForModule(self.moduleinfo.itemID) 982 log.LogInfo('Module.OnMouseEnter', self.id) 983 if settings.user.ui.Get('showModuleTooltips', 1): 984 if self.tooltipPanelClassInfo is None: 985 self.tooltipPanelClassInfo = TooltipModuleWrapper() 986 else: 987 self.tooltipPanelClassInfo = None 988 uthread.new(tacticalSvc.ShowModuleRange, self.moduleinfo, self.charge) 989 990 def GetTooltipDelay(self): 991 return settings.user.ui.Get(TOOLTIP_SETTINGS_MODULE, TOOLTIP_DELAY_MODULE) 992 993 def OnMouseExit(self, *args): 994 self.RemoveHilite() 995 sm.GetService('bracket').StopShowingModuleRange(self.moduleinfo.itemID) 996 self.OverloadHiliteOff() 997 log.LogInfo('Module.OnMouseExit', self.id) 998 uthread.new(sm.GetService('tactical').ClearModuleRange) 999 self.OnMouseUp(None) 1000 1001 def UpdateInfo_TimedCall(self): 1002 self.UpdateInfo() 1003 1004 def UpdateInfo(self): 1005 if self.destroyed or not self.moduleButtonHint or self.moduleButtonHint.destroyed: 1006 self.moduleButtonHint = None 1007 self.updateTimer = None 1008 return False 1009 if not self.stateManager.IsItemLoaded(self.id): 1010 return False 1011 chargeItemID = None 1012 if self.charge: 1013 chargeItemID = self.charge.itemID 1014 self.moduleButtonHint.UpdateAllInfo(self.moduleinfo.itemID, chargeItemID) 1015 requiredSafetyLevel = self.GetRequiredSafetyLevel() 1016 if self.crimewatchSvc.CheckUnsafe(requiredSafetyLevel): 1017 self.moduleButtonHint.SetSafetyWarning(requiredSafetyLevel) 1018 else: 1019 self.moduleButtonHint.RemoveSafetyWarning() 1020 return True 1021 1022 def GetSafetyWarning(self): 1023 requiredSafetyLevel = self.GetRequiredSafetyLevel() 1024 if self.crimewatchSvc.CheckUnsafe(requiredSafetyLevel): 1025 return requiredSafetyLevel 1026 else: 1027 return None 1028 1029 def GetModuleDamage(self): 1030 return uicore.layer.shipui.controller.GetModuleGroupDamage(self.moduleinfo.itemID) 1031 1032 def GetNumUnitsToRepair(self): 1033 return uicore.layer.shipui.controller.GetNumUnitsToRepairModuleBank(self.moduleinfo.typeID, self.moduleinfo.itemID) 1034 1035 def GetDamagedModulesAndRepairCost(self): 1036 return uicore.layer.shipui.controller.GetDamagedModulesAndRepairCost() 1037 1038 def GetAllRepairingModules(self): 1039 return uicore.layer.shipui.controller.GetAllRepairingModules() 1040 1041 def GetAccuracy(self, targetID = None): 1042 if self is None or self.destroyed: 1043 return 1044 1045 def SetActive(self): 1046 self.InitGlow() 1047 self.sr.glow.state = uiconst.UI_DISABLED 1048 sm.GetService('ui').BlinkSpriteA(self.sr.glow, 0.75, 1000, None, passColor=0) 1049 self.effect_activating = 0 1050 self.activationTimer = None 1051 self.isInActiveState = True 1052 self.ActivateRamps() 1053 1054 def SetDeactivating(self): 1055 self.isDeactivating = True 1056 if self.sr.glow: 1057 self.sr.glow.state = uiconst.UI_HIDDEN 1058 self.InitBusyState() 1059 self.sr.busy.state = uiconst.UI_DISABLED 1060 sm.GetService('ui').BlinkSpriteA(self.sr.busy, 0.75, 1000, None, passColor=0) 1061 self.isInActiveState = False 1062 self.DeActivateRamps() 1063 1064 def SetIdle(self): 1065 self.isDeactivating = False 1066 if self.sr.glow: 1067 self.sr.glow.state = uiconst.UI_HIDDEN 1068 sm.GetService('ui').StopBlink(self.sr.glow) 1069 if self.sr.busy: 1070 self.sr.busy.state = uiconst.UI_HIDDEN 1071 sm.GetService('ui').StopBlink(self.sr.busy) 1072 self.isInActiveState = True 1073 self.IdleRamps() 1074 self.TryStartCooldownTimers() 1075 1076 def SetRepairing(self, startTime = None): 1077 self.InitGlow() 1078 self.sr.glow.state = uiconst.UI_DISABLED 1079 self.sr.glow.SetRGBA(1, 1, 1, 1) 1080 sm.GetService('ui').BlinkSpriteA(self.sr.glow, 0.9, 2500, None, passColor=0) 1081 self.isInActiveState = True 1082 uthread.new(self.ShowRepairLeft, startTime) 1083 1084 def RemoveRepairing(self): 1085 if self.sr.glow: 1086 sm.GetService('ui').StopBlink(self.sr.glow) 1087 self.sr.glow.SetRGBA(*GLOWCOLOR) 1088 self.sr.glow.state = uiconst.UI_HIDDEN 1089 if self.sr.damageState: 1090 self.sr.damageState.StopRepair() 1091 1092 def SetHilite(self): 1093 self.InitHilite() 1094 self.sr.hilite.display = True 1095 requiredSafetyLevel = self.GetRequiredSafetyLevel() 1096 if self.crimewatchSvc.CheckUnsafe(requiredSafetyLevel): 1097 self.InitSafetyGlow() 1098 if requiredSafetyLevel == const.shipSafetyLevelNone: 1099 color = crimewatchConst.Colors.Criminal 1100 else: 1101 color = crimewatchConst.Colors.Suspect 1102 self.sr.safetyGlow.SetRGBA(*color.GetRGBA()) 1103 self.sr.safetyGlow.display = True 1104 1105 def GetRequiredSafetyLevel(self): 1106 requiredSafetyLevel = self.crimewatchSvc.GetRequiredSafetyLevelForEffect(self.GetRelevantEffect(), targetID=None) 1107 return requiredSafetyLevel 1108 1109 def RemoveHilite(self): 1110 if self.sr.hilite: 1111 self.sr.hilite.display = False 1112 if self.sr.safetyGlow: 1113 self.sr.safetyGlow.display = False 1114 1115 def InitSafetyGlow(self): 1116 if self.sr.safetyGlow is None: 1117 self.sr.safetyGlow = Sprite(parent=self.parent, name='safetyGlow', padding=2, align=uiconst.TOALL, state=uiconst.UI_HIDDEN, texturePath='res:/UI/Texture/classes/ShipUI/slotGlow.png', color=crimewatchConst.Colors.Yellow.GetRGBA()) 1118 1119 def InitGlow(self): 1120 if self.sr.glow is None: 1121 self.sr.glow = Sprite(parent=self.parent, name='glow', padding=2, align=uiconst.TOALL, state=uiconst.UI_HIDDEN, texturePath='res:/UI/Texture/classes/ShipUI/slotGlow.png', color=GLOWCOLOR) 1122 1123 def InitBusyState(self): 1124 if self.sr.busy is None: 1125 self.sr.busy = Sprite(parent=self.parent, name='busy', padding=2, align=uiconst.TOALL, state=uiconst.UI_HIDDEN, texturePath='res:/UI/Texture/classes/ShipUI/slotGlow.png', color=BUSYCOLOR) 1126 1127 def InitHilite(self): 1128 if self.sr.hilite is None: 1129 if getattr(self.parent, 'mainShape', None) is not None: 1130 idx = max(-1, self.parent.mainShape.GetOrder() - 1) 1131 else: 1132 idx = -1 1133 self.sr.hilite = Sprite(parent=self.parent, name='hilite', padding=(10, 10, 10, 10), align=uiconst.TOALL, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/ShipUI/slotHilite.png', blendMode=trinity.TR2_SBM_ADDX2, idx=idx) 1134 self.sr.hilite.display = False 1135 1136 def OverloadHiliteOn(self): 1137 self.sr.overloadButton.SetAlpha(1.5) 1138 1139 def OverloadHiliteOff(self): 1140 self.sr.overloadButton.SetAlpha(1.0) 1141 1142 def GetEffectByName(self, effectName): 1143 for key in self.moduleinfo.effects.iterkeys(): 1144 effect = self.moduleinfo.effects[key] 1145 if effect.effectName == effectName: 1146 return effect 1147 1148 def Update(self, effectState): 1149 if not self or self.destroyed: 1150 return 1151 if not self.stateManager.IsItemLoaded(self.id): 1152 return 1153 if self.def_effect and effectState.effectName == self.def_effect.effectName: 1154 if effectState.start: 1155 self.SetActive() 1156 else: 1157 self.SetIdle() 1158 effect = self.GetEffectByName(effectState.effectName) 1159 if effect and effect.effectCategory == const.dgmEffOverload: 1160 self.ShowOverload(effect.isActive) 1161 if effectState.effectName == 'online': 1162 if effectState.active: 1163 self.ShowOnline() 1164 else: 1165 self.ShowOffline() 1166 if effect.effectCategory in [const.dgmEffTarget, const.dgmEffActivation, const.dgmEffArea] and effect.effectID != const.effectOnline: 1167 if not effectState.active and self.quantity == 0: 1168 self.AutoReload() 1169 self.UpdateInfo() 1170 1171 def GetRelevantEffect(self): 1172 if self.def_effect and (self.def_effect.effectName == 'useMissiles' or self.def_effect.effectName == 'warpDisruptSphere' and self.charge is not None): 1173 if self.charge is None: 1174 return 1175 effect = sm.GetService('godma').GetStateManager().GetDefaultEffect(self.charge.typeID) 1176 else: 1177 effect = self.def_effect 1178 return effect 1179 1180 def ActivateEffect(self, effect, targetID = None, ctrlRepeat = 0): 1181 if self.charge and self.charge.typeID in const.orbitalStrikeAmmo: 1182 return False 1183 relevantEffect = self.GetRelevantEffect() 1184 if relevantEffect is None: 1185 typeID, _ = self.GetModuleType() 1186 raise UserError('NoCharges', {'launcher': (const.UE_TYPEID, typeID)}) 1187 if relevantEffect and not targetID and relevantEffect.effectCategory == 2: 1188 targetID = sm.GetService('target').GetActiveTargetID() 1189 if not targetID: 1190 sm.GetService('target').OrderTarget(self) 1191 uthread.new(self.BlinkIcon) 1192 self.waitingForActiveTarget = 1 1193 return 1194 if self.sr.Get('moduleinfo'): 1195 for key in self.moduleinfo.effects.iterkeys(): 1196 checkeffect = self.moduleinfo.effects[key] 1197 if checkeffect.effectName == 'online': 1198 if not checkeffect.isActive: 1199 self._ChangeOnline(1) 1200 break 1201 1202 if self.def_effect: 1203 if relevantEffect.isOffensive: 1204 if not sm.GetService('consider').DoAttackConfirmations(targetID, relevantEffect): 1205 return 1206 repeats = ctrlRepeat or self.autorepeat 1207 if not self.IsEffectRepeatable(self.def_effect, 1): 1208 repeats = 0 1209 if not self.charge: 1210 self.stateManager.ChangeAmmoTypeForModule(self.moduleinfo.itemID, None) 1211 targetPointCount = self.moduleinfo.isPointTargeted 1212 if targetPointCount: 1213 eveCommands = sm.GetService('cmd') 1214 eveCommands.CmdAimWeapon(self.moduleinfo.itemID, self.def_effect, targetPointCount) 1215 return 1216 try: 1217 self.def_effect.Activate(targetID, repeats) 1218 except UserError as e: 1219 if e.msg == 'ModuleReactivationDelayed2': 1220 self.SetReactivationTime(e.dict) 1221 raise 1222 1223 sm.ScatterEvent('OnClientEvent_ActivateModule', self.def_effect.effectID) 1224 1225 def DeactivateEffect(self, effect): 1226 self.SetDeactivating() 1227 try: 1228 effect.Deactivate() 1229 sm.ScatterEvent('OnClientEvent_DeactivateModule', self.def_effect.effectID) 1230 except UserError as e: 1231 if e.msg == 'EffectStillActive': 1232 if not self.isPendingUnlockForDeactivate: 1233 self.isPendingUnlockForDeactivate = True 1234 uthread.new(self.DelayButtonUnlockForDeactivate, max(0, e.dict['timeLeft'])) 1235 raise 1236 1237 def SetReactivationTime(self, errorDict): 1238 try: 1239 timeSinceLastStop = errorDict['timeSinceLastStop'] 1240 itemID = errorDict['itemID'] 1241 cooldownTimes = self.stateManager.GetCooldownTimes(itemID) 1242 if cooldownTimes: 1243 return 1244 startTime = blue.os.GetSimTime() - timeSinceLastStop * const.MSEC 1245 if startTime < 0: 1246 return 1247 self.stateManager.RecordLastStopTime(itemID, startTime) 1248 self.TryStartCooldownTimers() 1249 except Exception as e: 1250 pass 1251 1252 def DelayButtonUnlockForDeactivate(self, sleepTimeBlue): 1253 blue.pyos.synchro.SleepSim(sleepTimeBlue / const.MSEC) 1254 self.isInActiveState = True 1255 self.isPendingUnlockForDeactivate = False 1256 1257 def OnStateChange(self, itemID, flag, isTrue, *args): 1258 if self and isTrue and flag == state.activeTarget and self.waitingForActiveTarget: 1259 self.waitingForActiveTarget = 0 1260 self.ActivateEffect(self.def_effect, itemID) 1261 sm.GetService('target').CancelTargetOrder(self) 1262 1263 def GetModuleType(self): 1264 return (self.moduleinfo.typeID, self.moduleinfo.itemID) 1265 1266 def ActivateRamps(self): 1267 if not self or self.destroyed: 1268 return 1269 if self.ramp_active: 1270 self.UpdateRamps() 1271 return 1272 self.DoActivateRamps() 1273 1274 def DeActivateRamps(self): 1275 self.UpdateRamps() 1276 1277 def IdleRamps(self): 1278 self.ramp_active = False 1279 if not uicore.layer.shipui: 1280 return 1281 moduleID = self.moduleinfo.itemID 1282 rampTimers = uicore.layer.shipui.sr.rampTimers 1283 if moduleID in rampTimers: 1284 del rampTimers[moduleID] 1285 if self.sr.ramps: 1286 self.sr.ramps.display = False 1287 1288 def UpdateRamps(self): 1289 self.DoActivateRamps() 1290 1291 def DoActivateRamps(self): 1292 if self.ramp_active: 1293 return 1294 uthread.new(self.DoActivateRampsThread) 1295 1296 def InitRamps(self): 1297 if self.sr.ramps and not self.sr.ramps.destroyed: 1298 return 1299 self.sr.ramps = ShipModuleButtonRamps(parent=self.parent, idx=OVERLOADBTN_INDEX + 1) 1300 1301 def InitReactivationRamps(self): 1302 if self.sr.reactivationRamps and not self.sr.reactivationRamps.destroyed: 1303 return 1304 self.sr.reactivationRamps = ShipModuleReactivationTimer(parent=self.parent, idx=-1) 1305 1306 def DoActivateRampsThread(self): 1307 if not self or self.destroyed: 1308 return 1309 (firstActivation, startTime), durationInMilliseconds = self.GetEffectTiming() 1310 if durationInMilliseconds <= 0: 1311 return 1312 now = blue.os.GetSimTime() 1313 if firstActivation: 1314 startTimeAdjustment = now - startTime 1315 if startTimeAdjustment > const.SEC: 1316 startTimeAdjustment = 0 1317 correctionTimeMS = durationInMilliseconds / 2 1318 adjustmentDecayPerSec = float(-startTimeAdjustment) / (correctionTimeMS / 1000) 1319 else: 1320 startTimeAdjustment = 0 1321 correctionTimeMS = 0 1322 self.ramp_active = True 1323 self.InitRamps() 1324 self.sr.ramps.display = True 1325 while self and not self.destroyed and self.ramp_active and durationInMilliseconds: 1326 newNow = blue.os.GetSimTime() 1327 deltaTime = newNow - now 1328 now = newNow 1329 if correctionTimeMS != 0: 1330 deltaMS = min(deltaTime / const.MSEC, correctionTimeMS) 1331 startTimeAdjustment += long(adjustmentDecayPerSec * (float(deltaMS) / 1000)) 1332 correctionTimeMS -= deltaMS 1333 else: 1334 startTimeAdjustment = 0 1335 portionDone = blue.os.TimeDiffInMs(startTime + startTimeAdjustment, now) / durationInMilliseconds 1336 if portionDone > 1: 1337 iterations = int(portionDone) 1338 startTime += long(durationInMilliseconds * iterations * const.MSEC) 1339 _, durationInMilliseconds = self.GetEffectTiming() 1340 try: 1341 uicore.layer.shipui.sr.rampTimers[self.moduleinfo.itemID] = (False, startTime) 1342 except AttributeError: 1343 pass 1344 1345 portionDone -= iterations 1346 if self.InLimboState(): 1347 self.IdleRamps() 1348 sm.ScatterEvent('OnClientEvent_DeactivateModule', self.def_effect.effectID) 1349 break 1350 self.sr.ramps.SetRampValues(portionDone) 1351 blue.pyos.synchro.Yield() 1352 1353 def InLimboState(self): 1354 for each in ['waitingForActiveTarget', 'reloadingAmmo', 'isDeactivating']: 1355 if getattr(self, each, False): 1356 return True 1357 1358 return False 1359 1360 def GetRampStartTime(self): 1361 if not uicore.layer.shipui: 1362 return 1363 moduleID = self.moduleinfo.itemID 1364 rampTimers = uicore.layer.shipui.sr.rampTimers 1365 if moduleID not in rampTimers: 1366 now = blue.os.GetSimTime() 1367 default = getattr(self.def_effect, 'startTime', now) or now 1368 rampTimers[moduleID] = (True, default) 1369 return rampTimers[moduleID] 1370 1371 def ShowRepairLeft(self, startTime = None): 1372 if self.sr.damageState is None: 1373 return 1374 dmg = self.GetModuleDamage() 1375 rateOfRepair = self.stateManager.GetAttribute(session.charid, 'moduleRepairRate') 1376 repairTime = dmg / rateOfRepair 1377 repairTime = int(repairTime * const.MIN) 1378 if startTime is None: 1379 startTime = blue.os.GetSimTime() 1380 hp = self.dogmaLocation.GetAttributeValue(self.moduleinfo.itemID, const.attributeHp) 1381 self.sr.damageState.AnimateRepair(dmg, hp, repairTime, startTime) 1382 1383 def ShowReloadLeft(self, reloadTime, startTime = None): 1384 if startTime is None: 1385 startTime = blue.os.GetSimTime() 1386 reloadTime = int(reloadTime * const.MSEC) 1387 if self.sr.reactivationRamps and self.sr.reactivationRamps.endTime > startTime + reloadTime: 1388 return 1389 self.InitRamps() 1390 self.sr.ramps.display = True 1391 self.sr.ramps.AnimateReload(startTime, reloadTime) 1392 1393 def ShowReactivationLeft(self, reactivationTime, startTime = None): 1394 self.InitReactivationRamps() 1395 reactivationTime = int(reactivationTime * const.MSEC) 1396 if startTime is None: 1397 startTime = blue.os.GetSimTime() 1398 self.sr.reactivationRamps.AnimateTimer(startTime, reactivationTime) 1399 1400 def GetEffectTiming(self): 1401 rampStartTime = self.GetRampStartTime() 1402 durationInMilliseconds = 0.0 1403 durationAttributeID = getattr(self.def_effect, 'durationAttributeID', None) 1404 attribute = dogma.data.get_attribute(durationAttributeID) if durationAttributeID is not None else None 1405 item = self.stateManager.GetItem(self.def_effect.itemID) 1406 if item is None: 1407 return (0, 0.0) 1408 if attribute: 1409 durationInMilliseconds = self.stateManager.GetAttribute(self.def_effect.itemID, attribute.name) 1410 if not durationInMilliseconds: 1411 durationInMilliseconds = getattr(self.def_effect, 'duration', 0.0) 1412 return (rampStartTime, durationInMilliseconds)