baseDogmaLocation.py (86279B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\common\script\dogma\baseDogmaLocation.py 2 import math 3 import sys 4 import weakref 5 from collections import defaultdict 6 from dogma.const import dgmOperatorIDtoIndex 7 from dogma.eventCounters import EventCount, BrainUpdate 8 from dogma.dogmaLogging import * 9 import blue 10 import bluepy 11 import dynamicitemattributes 12 import evetypes 13 import stackless 14 import telemetry 15 import uthread 16 import utillib 17 from carbon.common.script.sys.basesession import FindSessionClient 18 from ccpProfile import TimedFunction 19 from dogma.effects.environment import Environment 20 from dogma.items.notWantedDogmaItem import NotWantedDogmaItem 21 from dogma.items.dblessDogmaItem import DBLessDogmaItem 22 from dogma.items.shipDogmaItem import ShipDogmaItem 23 from dogma.items.probeDogmaItem import ProbeDogmaItem 24 from dogma.items.chargeDogmaItem import ChargeDogmaItem 25 from dogma.items.moduleDogmaItem import ModuleDogmaItem 26 from dogma.items.controlTowerDogmaItem import ControlTowerDogmaItem 27 from dogma.items.starbaseDogmaItem import StarbaseDogmaItem 28 from dogma.items.droneDogmaItem import DroneDogmaItem 29 from dogma.items.structureDogmaItem import StructureDogmaItem 30 from dogma.items.structureModuleDogmaItem import StructureModuleDogmaItem 31 from dogma.items.baseDogmaItem import BaseDogmaItem 32 from dogma.items.characterDogmaItem import CharacterDogmaItem 33 from dogma.items.fighterDogmaItem import FighterDogmaItem 34 from dogma.exceptions import EmbarkOnlineError, EffectFailedButShouldBeStopped 35 from dogma.attributes import BrainLiteralAttribute 36 from eve.common.script.dogma.effect import ApplyBrainEffect, RemoveBrainEffect 37 from eve.common.script.util.inventoryUtil import GetComputedBasePrice 38 from eve.common.script.util.industryCommon import CHARACTER_MODIFIERS 39 from eveexceptions import UserError 40 from inventorycommon.const import categoryShip, categoryStructure, serviceSlotFlags, DEPRECATED_POS_MODULE_GROUPS 41 from inventorycommon.util import IsShipFittingFlag, IsFittingModule, IsSubsystemFlag 42 from logging import getLogger 43 from utillib import keydefaultdict 44 logger = getLogger('dogmaLocation') 45 import log 46 47 def OpName(opIdx): 48 try: 49 return opDecoder[opIdx] 50 except KeyError: 51 return opIdx 52 53 54 opDecoder = {None: '..', 55 const.dgmAssPreAssignment: '=.', 56 const.dgmAssPreMul: '*.', 57 const.dgmAssPreDiv: '/.', 58 const.dgmAssModAdd: ' +', 59 const.dgmAssModSub: ' -', 60 const.dgmAssPostMul: '.*', 61 const.dgmAssPostDiv: './', 62 const.dgmAssPostPercent: '.%', 63 const.dgmAssPostAssignment: '.=', 64 const.dgmAssSpecialSkillRelationship: '.!', 65 const.dgmAssPostPercentUnNerfed: '.%'} 66 CATEGORY_SHIPS_AND_STRUCTURES = {categoryShip, categoryStructure} 67 PROBE_GROUPS = (const.groupSurveyProbe, 68 const.groupWarpDisruptionProbe, 69 const.groupBurstEffectProbe, 70 const.groupScannerProbe, 71 const.groupBomb, 72 const.groupBombECM, 73 const.groupBombEnergy) 74 75 class MessageMgr(object): 76 77 def __init__(self): 78 self.attributes = set() 79 80 def AddAttribute(self, attr): 81 self.attributes.add(attr) 82 83 def GetMessages(self): 84 messages = [] 85 for each in self.attributes: 86 msg = each.ConstructMessage() 87 if msg is not None: 88 messages.append(msg) 89 90 return messages 91 92 def Clear(self): 93 self.attributes.clear() 94 95 96 class BaseDogmaLocation(object): 97 98 def __init__(self, broker): 99 self.broker = broker 100 self.onlineEffects = {const.effectOnline: True, 101 const.effectOnlineForStructures: True} 102 self.shipsByPilotID = {} 103 self.pilotsByShipID = {} 104 self.dogmaItems = {} 105 self.locationID = None 106 self.locationName = None 107 self.locationGroup = const.groupSystem 108 self.moduleListsByShipGroup = defaultdict(lambda : defaultdict(set)) 109 self.moduleListsByShipType = defaultdict(lambda : defaultdict(set)) 110 self.attributeChangeCallbacksByAttributeID = {} 111 self.loadingItems = {} 112 self.failedLoadingItems = {} 113 self.crits = {} 114 self.activatingEffects = {} 115 self.deactivatingEffects = set() 116 self.activatingEffectsByLocation = {} 117 self.activatingEffectsByTarget = {} 118 self.activeEffectsByShip = {} 119 self.activeDungeonEffectsByItem = defaultdict(lambda : defaultdict(set)) 120 self.instanceFlagQuantityCache = {} 121 self.unloadingItems = set() 122 self.extraTracebacks = True 123 self.pendingPowerCpuRequirementChecks = set() 124 self.ignoredOwnerEvents = EventCount() 125 self.itemsMissingLocation = defaultdict(set) 126 self.itemsMissingOwner = defaultdict(set) 127 self.modifiersAwaitingTarget = defaultdict(set) 128 self.slaveModulesByMasterModule = {} 129 self.onlineByShip = {} 130 self.embarkingShips = {} 131 self.instanceRowDescriptor = blue.DBRowDescriptor((('instanceID', const.DBTYPE_I8), 132 ('online', const.DBTYPE_BOOL), 133 ('damage', const.DBTYPE_R5), 134 ('charge', const.DBTYPE_R5), 135 ('skillPoints', const.DBTYPE_I4), 136 ('armorDamage', const.DBTYPE_R5), 137 ('shieldCharge', const.DBTYPE_R5), 138 ('incapacitated', const.DBTYPE_BOOL))) 139 l = [0] + [0] * (len(self.instanceRowDescriptor) - 1) 140 self.fakeInstanceRow = blue.DBRow(self.instanceRowDescriptor, l) 141 self.RegisterCallback(const.attributeCpuLoad, self.OnCpuAttributeChanged) 142 self.RegisterCallback(const.attributePowerLoad, self.OnPowerAttributeChanged) 143 self.RegisterCallback(const.attributeMaxGroupOnline, self.OnMaxGroupOnlineChanged) 144 self.msgMgr = MessageMgr() 145 self.brainLiterals = keydefaultdict(BrainLiteralAttribute) 146 self.scarecrows = {} 147 self.brainUpdate = BrainUpdate(self.CheckPowerCpuRequirementForOnlineModules) 148 149 def LoadItem(self, itemKey, instanceRow = None, wait = False, invItem = None, newInstance = False, timerName = None): 150 if itemKey == self.locationID: 151 return itemKey 152 if self.IsItemLoaded(itemKey): 153 return itemKey 154 self.EnterCriticalSection('LoadItem', itemKey) 155 try: 156 if itemKey in self.dogmaItems: 157 return itemKey 158 self.loadingItems[itemKey] = stackless.channel() 159 try: 160 dogmaItem = self._LoadItem(itemKey, instanceRow=instanceRow, invItem=invItem, newInstance=newInstance) 161 finally: 162 while self.loadingItems[itemKey].queue: 163 self.loadingItems[itemKey].send(None) 164 165 del self.loadingItems[itemKey] 166 167 if dogmaItem is not None: 168 if itemKey in self.modifiersAwaitingTarget: 169 LogNotice('LoadItem: Calling (and clearing) pending modifiers via itemKey {} for dogmaItem {}\n modifiers = {}'.format(itemKey, dogmaItem, self.modifiersAwaitingTarget[itemKey])) 170 for callTuple in self.modifiersAwaitingTarget[itemKey]: 171 callTuple[0](*callTuple[1:]) 172 173 del self.modifiersAwaitingTarget[itemKey] 174 dogmaItem.PostLoadAction() 175 except Exception as e: 176 log.LogException('Failed to load a dogma item %s' % str(itemKey)) 177 try: 178 if not len(e.args) or not e.args[0].startswith('GetItem: Item not here'): 179 self.failedLoadingItems[itemKey] = 'Exception: ' + strx(e) 180 except Exception: 181 pass 182 183 raise e 184 finally: 185 self.LeaveCriticalSection('LoadItem', itemKey) 186 187 return itemKey 188 189 def _LoadItem_LogOnEntry(self, itemKey, invItem): 190 givenInvItemDescription = self.DescribeInvItem(invItem) 191 foundDogmaItem = self.dogmaItems.get(itemKey, None) 192 foundInvItemDescription = self.DescribeInvItem(foundDogmaItem.invItem) if foundDogmaItem else None 193 totalItems = len(self.dogmaItems) 194 LogNotice('LoadItem requested for itemKey: {itemKey}, with given invItem {givenInvItemDescription}, having existing dogmaItems entry {foundDogmaItem} with invItem {foundInvItemDescription} (totalItems = {totalItems}) '.format(**locals())) 195 196 def _LoadItem_LogOnExit(self, itemKey): 197 dogmaItem = self.dogmaItems.get(itemKey, None) 198 invItemDescription = self.DescribeInvItem(dogmaItem.invItem) if dogmaItem else None 199 LogNotice('LoadItem completed for itemKey: {itemKey}, having dogmaItems entry {dogmaItem} and invItem {invItemDescription}'.format(**locals())) 200 201 def GetQuantityFromCache(self, locationID, flagID): 202 return self.instanceFlagQuantityCache[locationID][flagID].quantity 203 204 @TimedFunction('BaseDogmaLocation::_InstantiateDogmaItem') 205 def _InstantiateDogmaItem(self, itemKey, invItem, instanceRow, newInstance): 206 self.LogInfo('BaseDogmaLocation::_InstantiateDogmaItem', itemKey) 207 if isinstance(itemKey, tuple): 208 dogmaItemClass = DBLessDogmaItem 209 invItem = utillib.KeyVal() 210 invItem.itemID = itemKey 211 invItem.locationID, invItem.flagID, invItem.typeID = itemKey 212 invItem.groupID = evetypes.GetGroupID(invItem.typeID) 213 invItem.categoryID = evetypes.GetCategoryID(invItem.typeID) 214 invItem.ownerID = self.dogmaItems[invItem.locationID].ownerID 215 else: 216 if invItem is None: 217 invItem = self.GetItem(itemKey) 218 try: 219 typeID = invItem.typeID 220 except AttributeError: 221 raise RuntimeError('Dogma was unable to get invItem to load dogmaItem!') 222 223 if not self.IsItemWanted(typeID) or newInstance: 224 instanceRow = self.IntroduceNewItem(invItem) 225 else: 226 instanceRow = self.GetInstance(invItem) 227 dogmaItemClass = self.GetClassForItem(invItem) 228 dogmaItem = dogmaItemClass(weakref.proxy(self), invItem, cfg, FindSessionClient) 229 return (dogmaItem, invItem, instanceRow) 230 231 @TimedFunction('BaseDogmaLocation::_LoadItem') 232 def _LoadItem(self, itemKey, instanceRow = None, invItem = None, newInstance = False): 233 dogmaItem, invItem, instanceRow = self._InstantiateDogmaItem(itemKey, invItem, instanceRow, newInstance) 234 self.LogInfo('BaseDogmaLocation::Actually Loading dogma item', itemKey) 235 dogmaItem.Load(invItem, instanceRow) 236 if invItem.flagID != const.flagPilot: 237 self.FitItemToLocation(invItem.locationID, itemKey, invItem.flagID) 238 dogmaItem.OnItemLoaded() 239 return dogmaItem 240 241 def GetDogmaItem(self, itemID): 242 return self.dogmaItems[itemID] 243 244 def SafeGetDogmaItem(self, itemID): 245 return self.dogmaItems.get(itemID, None) 246 247 def CheckApplyBrainEffects(self, shipID): 248 pass 249 250 def SetBrainData(self, charID, brain): 251 pass 252 253 def GetBrainData(self, charID): 254 pass 255 256 def InitBrain(self, charID, brain = None): 257 pass 258 259 def GetEmptyBrain(self): 260 return (-1, 261 [], 262 [], 263 []) 264 265 @telemetry.ZONE_METHOD 266 def GetModuleListByShipGroup(self, locationID, groupID): 267 return self.moduleListsByShipGroup.get(locationID, {}).get(groupID, []) 268 269 @telemetry.ZONE_METHOD 270 def GetModuleListByShipType(self, locationID, typeID): 271 return self.moduleListsByShipType.get(locationID, {}).get(typeID, []) 272 273 def HandleDogmaLocationEffectsOnItem(self, dogmaItem): 274 pass 275 276 def ApplyBrainEffects(self, shipID, charID, timerName = 'unknown'): 277 self.broker.LogInfo('%s ApplyBrainEffects %s %s. Called from %s' % (self.locationName, 278 shipID, 279 charID, 280 timerName)) 281 if shipID is None: 282 self.broker.LogError('ApplyBrainEffects - shipID is None') 283 if charID is None: 284 self.broker.LogError('ApplyBrainEffects - charID is None!') 285 return 286 startTime = blue.os.GetWallclockTimeNow() 287 currentShipID = self.shipsByPilotID.get(charID, None) 288 if shipID != currentShipID: 289 self.shipsByPilotID[charID] = shipID 290 currentPilotID = self.pilotsByShipID.get(shipID, None) 291 if charID != currentPilotID: 292 self.pilotsByShipID[shipID] = charID 293 if not self.CheckApplyBrainEffects(shipID): 294 endTime = blue.os.GetWallclockTimeNow() 295 self.broker.LogInfo('%s - ApplyBrainEffects took %s ms' % (self.locationID, blue.os.TimeDiffInMs(startTime, endTime))) 296 return 297 log.LogInfo('%s - ApplyBrainEffects to shipID %s' % (self.locationID, shipID)) 298 shipDogmaItem = self.dogmaItems.get(shipID, None) 299 if not shipDogmaItem: 300 self.LogError('ApplyBrainEffects:ShipDogmaItem not found!') 301 return 302 _, charEffects, shipEffects, structureEffects = self.GetBrainData(charID) 303 self._UpdateShipEffectsFromBrain(ApplyBrainEffect, 'Apply', shipEffects, structureEffects, shipID, shipDogmaItem) 304 self._UpdateCharacterEffectsFromBrain(ApplyBrainEffect, 'Apply', charEffects, charID) 305 self._ApplyPilotSecurityStatusToShip(shipID, charID) 306 endTime = blue.os.GetWallclockTimeNow() 307 self.broker.LogInfo('%s - ApplyBrainEffects took %s ms' % (self.locationID, blue.os.TimeDiffInMs(startTime, endTime))) 308 309 def RemoveBrainEffects(self, shipID, charID, timerName = None): 310 self.broker.LogInfo('%s RemoveBrainEffects %s %s. Called from %s' % (self.locationName, 311 shipID, 312 charID, 313 timerName)) 314 if charID is None: 315 return 316 startTime = blue.os.GetWallclockTimeNow() 317 _, charEffects, shipEffects, structureEffects = self.GetBrainData(charID) 318 if shipID is not None: 319 shipDogmaItem = self.dogmaItems.get(shipID, None) 320 if not shipDogmaItem: 321 self.LogError('RemoveBrainEffects:ShipDogmaItem not found!') 322 return 323 self._UpdateShipEffectsFromBrain(RemoveBrainEffect, 'Remove', shipEffects, structureEffects, shipID, shipDogmaItem) 324 self._ApplyPilotSecurityStatusToShip(shipID, None) 325 self.broker.LogInfo('Removing %s character effects from %s' % (len(charEffects), charID)) 326 for effect in charEffects: 327 literal = self.brainLiterals[effect.GetLiteralKey()] 328 effType = effect.modifierType 329 RemoveBrainEffect[effType](self, charID, literal, *effect.GetApplicationArgs()) 330 331 endTime = blue.os.GetWallclockTimeNow() 332 self.broker.LogInfo('%s RemoveBrainEffects took %s ms' % (self.locationID, blue.os.TimeDiffInMs(startTime, endTime))) 333 334 def OnCharacterEmbarkation(self, charID, shipID, switching = False, raiseOnOnlineFailure = False): 335 self.LogInfo('OnCharacterEmbarkation charID', charID, 'shipID', shipID) 336 self.embarkingShips[shipID] = charID 337 try: 338 self._OnCharacterEmbarkation(charID, shipID, switching, raiseOnOnlineFailure) 339 finally: 340 if shipID in self.embarkingShips: 341 del self.embarkingShips[shipID] 342 343 def _OnCharacterEmbarkation(self, charID, shipID, switching, raiseOnOnlineFailure): 344 if self.shipsByPilotID.get(charID, None) == shipID: 345 self.LogError('OnCharacterEmbarkation - character already embarked? Are we doing things out of order?', charID, shipID, switching) 346 return 347 if not self.IsItemLoaded(charID): 348 self.LoadItem(charID) 349 if charID in self.shipsByPilotID: 350 skipCheck = False 351 oldShipID = self.shipsByPilotID.get(charID, None) 352 else: 353 skipCheck = True 354 oldShipID = None 355 if oldShipID == shipID: 356 self.LogError("OnCharacterEmbarkation - character seem to have been already fitted to this ship so I'm returning", charID, shipID, switching) 357 return 358 if not self.IsItemLoaded(shipID): 359 self.LoadItem(shipID) 360 shipDogmaItem = self.dogmaItems[shipID] 361 charDogmaItem = self.dogmaItems[charID] 362 charDogmaItem.SetLocation(shipID, shipDogmaItem, const.flagPilot) 363 shipFittedItems = shipDogmaItem.GetFittedItems() 364 if self.extraTracebacks: 365 self.LogInfo('Embark, sorting modules and ship', shipID, shipFittedItems.keys()) 366 for itemKey in shipFittedItems: 367 self.InstallPilotForItem(itemKey, shipID, charID) 368 369 for droneID in shipDogmaItem.GetDronesInBay(): 370 if droneID not in self.dogmaItems: 371 self.LogError("drone in ship's drone dict but not actually loadedd", droneID, charID, shipID) 372 continue 373 self.InstallPilotForItem(droneID, shipID, charID) 374 375 if self.extraTracebacks: 376 self.LogInfo('Embark, sorted modules and ship', shipID, shipFittedItems.keys()) 377 self.broker.LogInfo('OnCharacterEmbarkation - char adoption', charID) 378 self.LogInfo('OnCharacterEmbarkation - char', charID, 'reonlining modules on ship', shipID) 379 self.ApplyBrainEffects(shipID, charID, 'baseDogmaLocation._OnCharacterEmbarkation') 380 if shipID in self.onlineByShip: 381 d = self.onlineByShip[shipID] 382 del self.onlineByShip[shipID] 383 self.LogInfo('OnCharacterEmbarkation - ship', shipID, 'had online cache hit', d) 384 flags = d.keys() 385 flags.sort() 386 brokenByID = {} 387 for i in range(len(flags) - 1, -1, -1): 388 moduleID = d[flags[i]] 389 try: 390 self.StopEffect(const.effectOnline, moduleID, 0) 391 except UserError as e: 392 brokenByID[moduleID] = True 393 item = self.inventory2.GetItem(moduleID) 394 self.broker.LogError('Char', charID, 'piloting', shipID, 'module', moduleID, evetypes.GetName(item.typeID), 'not offline, reason', e) 395 log.LogException(channel='svc.dogmaIM') 396 sys.exc_clear() 397 398 for flag in flags: 399 moduleID = d[flag] 400 if moduleID not in brokenByID: 401 try: 402 self.StartModuleOnlineEffect(moduleID, charID, shipID, raiseUserError=raiseOnOnlineFailure) 403 except UserError as e: 404 if raiseOnOnlineFailure: 405 log.LogException('Failed to online modules on undock', severity=log.LGWARN, channel='svc.dogmaIM') 406 raise EmbarkOnlineError('FailedToOnlineModulesOnUndock') 407 if e.msg != 'OnlineHasSkillPrerequisites': 408 item = self.inventory2.GetItem(moduleID) 409 self.broker.LogWarn('OnCharacterEmbarkation - char', charID, 'piloting', shipID, 'module', moduleID, evetypes.GetName(item.typeID), 'not onlined again, reason', e) 410 log.LogException(channel='svc.dogmaIM') 411 sys.exc_clear() 412 413 self.LogInfo('OnCharacterEmbarkation - char', charID, 'reonlined modules on ship', shipID, 'candidates', d) 414 415 def InstallPilotForItem(self, itemKey, shipID, pilotID): 416 dogmaItem = self.dogmaItems[itemKey] 417 itemTypeID = dogmaItem.typeID 418 itemGroupID = evetypes.GetGroupID(itemTypeID) 419 fittableNonSingleton = evetypes.GetIsGroupFittableNonSingletonByGroup(itemGroupID) 420 targetID = shipID 421 otherID = None 422 aeff = dogmaItem.activeEffects 423 for effectID in self.dogmaStaticMgr.GetPassiveFilteredEffectsByType(itemTypeID): 424 if effectID in aeff: 425 continue 426 if otherID is None and fittableNonSingleton: 427 otherID = self.GetSlotOther(shipID, dogmaItem.flagID) 428 if otherID is None and self.effectCompiler.IsEffectDomainOther(effectID): 429 continue 430 self.LogInfo('InstallPilotForItem.effect', itemKey, effectID, dogma_data.get_effect_name(effectID)) 431 try: 432 structureId = self.GetStructureIdForEnvironment(shipID) 433 env = Environment(itemKey, pilotID, shipID, targetID, otherID, effectID, weakref.proxy(self), None, structureId) 434 self.StartEffect(effectID, itemKey, env) 435 except UserError as e: 436 sys.exc_clear() 437 ek, ea = e.msg, e.dict 438 if ek == 'ShipExploding': 439 self.LogInfo('InstallPilotForItem - ship', shipID, 'exploding, so module', itemKey, 'fit failed, mid effect start loop', effectID) 440 return 441 442 @TimedFunction('BaseDogmaLocation::FitItemToLocation') 443 def FitItemToLocation(self, locationID, itemID, flagID): 444 self.LogInfo('FitItemToLocation', locationID, itemID, flagID) 445 if locationID == self.locationID: 446 self.LogInfo('Attempting to fit item to self.locationID. Ignored!') 447 return 448 if locationID not in self.dogmaItems: 449 wasItemLoaded = itemID in self.dogmaItems 450 if locationID != self.locationID: 451 self.LoadItem(locationID) 452 if not wasItemLoaded: 453 self.LogInfo('Neither location not item was loaded. Loaded location and returned.', locationID, itemID) 454 return 455 if itemID not in self.dogmaItems: 456 self.LoadItem(itemID) 457 return 458 locationDogmaItem = self.dogmaItems.get(locationID, None) 459 if locationDogmaItem is None: 460 self.LogInfo('FitItemToLocation::Fitted to None item', itemID, locationID, flagID) 461 return 462 dogmaItem = self.dogmaItems[itemID] 463 if not locationDogmaItem.CanFitItem(dogmaItem, flagID): 464 if locationDogmaItem.categoryID == categoryShip and IsShipFittingFlag(flagID): 465 logger.error("Somebody asked us to fit an item to a ship that can't be fitted") 466 return 467 oldInfo = dogmaItem.GetLocationInfo() 468 dogmaItem.SetLocation(locationID, locationDogmaItem, flagID) 469 startedEffects = self.StartPassiveEffects(itemID, dogmaItem.typeID) 470 if startedEffects is not None: 471 self.UnsetItemLocation(itemID, locationID) 472 for effectID in startedEffects: 473 try: 474 self.StopEffect(effectID, itemID, 1) 475 except UserError as e: 476 log.LogException('Failed to start effect %s' % effectID, channel='svc.dogmaIM') 477 sys.exc_clear() 478 479 if IsSubsystemFlag(dogmaItem.flagID): 480 raise RuntimeError('Failed to start passive effects on a subsystem') 481 raise UserError('ModuleFitFailed', {'moduleName': (const.UE_GROUPID, dogmaItem.groupID), 482 'reason': ''}) 483 locationCategoryID = locationDogmaItem.categoryID 484 if locationCategoryID in CATEGORY_SHIPS_AND_STRUCTURES and dogmaItem.attributes.get(const.attributeIsOnline, False) and self.dogmaStaticMgr.TypeHasEffect(dogmaItem.typeID, const.effectOnline): 485 pilotID = dogmaItem.GetPilot() 486 self.StartModuleOnlineEffect(dogmaItem.itemID, pilotID, dogmaItem.locationID) 487 return oldInfo 488 489 @telemetry.ZONE_METHOD 490 def _StartModuleOnlineEffect(self, itemID, pilotID, locationID, context = None, raiseUserError = False): 491 environment = self.GetEnvironment(self.dogmaItems[itemID].GetEnvironmentInfo(), const.effectOnline) 492 if context is not None: 493 for k, v in context.iteritems(): 494 setattr(environment, k, v) 495 496 try: 497 self.StartEffect(const.effectOnline, itemID, environment, 1, byUser=True) 498 except UserError as e: 499 if e.msg.startswith('EffectAlreadyActive'): 500 self.LogInfo('Tried to online', itemID, 'in', locationID, 'but failed because of', e) 501 else: 502 self.SetAttributeValue(itemID, const.attributeIsOnline, 0) 503 if raiseUserError: 504 raise 505 sys.exc_clear() 506 507 def StartModuleOnlineEffect(self, itemID, pilotID, locationID, context = None, raiseUserError = False): 508 item = self.GetItem(itemID) 509 if item.locationID != locationID or not IsShipFittingFlag(item.flagID): 510 self.LogInfo('StartModuleOnlineEffect::Unexpected location', itemID, item.locationID, locationID) 511 return 512 if not self.dogmaStaticMgr.TypeHasEffect(item.typeID, const.effectOnline): 513 logger.error('Cannot start module online effect as this type does not have the online effect', extra={'typeID': item.typeID}) 514 return 515 if pilotID is not None or item.categoryID == const.categoryStructureModule: 516 self._StartModuleOnlineEffect(itemID, pilotID, locationID, context=context, raiseUserError=raiseUserError) 517 return 518 if locationID not in self.onlineByShip: 519 self.onlineByShip[locationID] = {} 520 dogmaItem = self.dogmaItems[itemID] 521 flagID = dogmaItem.flagID 522 dogmaItem.attributes[const.attributeIsOnline].SetBaseValue(1) 523 self.onlineByShip[locationID][flagID] = itemID 524 525 def UnfitItemFromLocation(self, locationID, itemID, flushEffects = False): 526 dogmaItem = self.dogmaItems[itemID] 527 itemTypeID = dogmaItem.typeID 528 self.UnsetItemLocation(itemID, locationID) 529 for effectID, effect in dogmaItem.activeEffects.items(): 530 if type(effectID) is tuple: 531 effectID = effectID[0] 532 if not flushEffects and effect[const.ACT_IDX_DURATION] != -1: 533 raise UserError('ModuleEffectActive', {'module': itemTypeID}) 534 if self.dogmaStaticMgr.effects[effectID].effectCategory != const.dgmEffPassive: 535 self.StopEffect(effectID, itemID, forced=flushEffects) 536 537 self.StopPassiveEffects(dogmaItem, itemID, itemTypeID) 538 539 def StopPassiveEffects(self, dogmaItem, itemID, itemTypeID): 540 effectsCompleted = set() 541 if itemTypeID not in self.dogmaStaticMgr.effectsByType: 542 return 543 for effectID in self.dogmaStaticMgr.TypeGetOrderedEffectIDs(itemTypeID): 544 if effectID not in dogmaItem.activeEffects: 545 continue 546 effect = self.dogmaStaticMgr.effects[effectID] 547 if effect.effectCategory in [const.dgmEffPassive, const.dgmEffSystem]: 548 try: 549 self.StopEffect(effectID, itemID) 550 effectsCompleted.add(effectID) 551 except UserError as e: 552 for _effectID in effectsCompleted: 553 try: 554 info = dogmaItem.GetEnvironmentInfo() 555 env = self.GetEnvironment(info, _effectID) 556 self.StartEffect(_effectID, itemID, env) 557 except UserError as e: 558 log.LogException(channel='svc.dogmaIM') 559 sys.exc_clear() 560 561 sys.exc_clear() 562 563 def UnsetItemLocation(self, itemKey, locationID): 564 locationDogmaItem = self.dogmaItems.get(locationID, None) 565 dogmaItem = self.dogmaItems[itemKey] 566 if locationDogmaItem is None: 567 self.LogError("UnsetItemLocation::called for location that wasn't loaded", itemKey, locationID) 568 return 569 dogmaItem.UnsetLocation(locationDogmaItem) 570 571 @bluepy.TimedFunction('DogmaLocation::StartEffect') 572 def StartEffect(self, effectID, itemKey, environment, repeat = 0, byUser = 0, checksOnly = None): 573 dogmaItem = None 574 try: 575 dogmaItem = self.dogmaItems[itemKey] 576 except KeyError: 577 self.LogError('StartEffect::Item neither loaded nor being loaded', effectID, itemKey) 578 self.LoadItem(itemKey) 579 dogmaItem = self.dogmaItems[itemKey] 580 581 activateKey = (itemKey, effectID) 582 if effectID not in self.dogmaStaticMgr.effectsByType.get(dogmaItem.typeID, []): 583 if effectID == const.effectOnline: 584 self.LogInfo("StartEffect::Online Effect being started on a type that doesn't have it", itemKey, dogmaItem.typeID, effectID) 585 else: 586 self.LogError("StartEffect::Effect being started on type that doesn't have it (unexpected)", itemKey, dogmaItem.typeID, effectID) 587 if evetypes.GetGroupID(dogmaItem.typeID) in DEPRECATED_POS_MODULE_GROUPS: 588 raise TypeError('DeprecatedPosModule') 589 return 590 if dogmaItem.IsEffectRegistered(effectID, environment): 591 raise UserError('EffectAlreadyActive2', {'modulename': (const.UE_TYPEID, dogmaItem.typeID)}) 592 if activateKey in self.activatingEffects: 593 self.broker.LogWarn('Effect already activating o_O', activateKey, blue.os.TimeDiffInMs(self.activatingEffects[activateKey][0], blue.os.GetSimTime()) / 1000.0) 594 raise UserError('EffectAlreadyActive2', {'modulename': (const.UE_TYPEID, dogmaItem.typeID)}) 595 self.activatingEffects[activateKey] = [blue.os.GetSimTime(), None, environment] 596 itemLocationID = dogmaItem.locationID 597 if itemLocationID not in self.activatingEffectsByLocation: 598 self.activatingEffectsByLocation[itemLocationID] = set([activateKey]) 599 else: 600 self.activatingEffectsByLocation[itemLocationID].add(activateKey) 601 targetID = environment.targetID 602 if targetID: 603 if targetID not in self.activatingEffectsByTarget: 604 self.activatingEffectsByTarget[targetID] = set([activateKey]) 605 else: 606 self.activatingEffectsByTarget[targetID].add(activateKey) 607 try: 608 effect = self.dogmaStaticMgr.effects[effectID] 609 self.StartEffect_PreChecks(effect, dogmaItem, environment, byUser) 610 duration = self.GetEffectDuration(effect, environment) 611 try: 612 effectStart = self._StartEffect(effect, dogmaItem, environment, duration, repeat, byUser, checksOnly) 613 except EffectFailedButShouldBeStopped: 614 effectStart = blue.os.GetSimTime() 615 repeat = 0 616 sys.exc_clear() 617 618 self.RegisterEffect(effect, dogmaItem, environment, effectStart, duration, repeat) 619 finally: 620 del self.activatingEffects[activateKey] 621 self.activatingEffectsByLocation[itemLocationID].remove(activateKey) 622 if not len(self.activatingEffectsByLocation[itemLocationID]): 623 del self.activatingEffectsByLocation[itemLocationID] 624 if targetID: 625 self.activatingEffectsByTarget[targetID].remove(activateKey) 626 if not len(self.activatingEffectsByTarget[targetID]): 627 del self.activatingEffectsByTarget[targetID] 628 629 def _StartEffect(self, effect, dogmaItem, environment, duration, repeat, byUser, checksOnly): 630 effectID = effect.effectID 631 self.effectCompiler.PreStartEffectChecks(effectID, environment) 632 if effectID == const.effectOnline: 633 for onlineEffectID in self.dogmaStaticMgr.effectsByType[dogmaItem.typeID]: 634 if self.dogmaStaticMgr.effects[onlineEffectID].effectCategory == const.dgmEffOnline: 635 self.effectCompiler.PreStartEffectChecks(onlineEffectID, environment) 636 637 self.ConsumeResources(environment) 638 itemKey = dogmaItem.itemID 639 effectStart = blue.os.GetSimTime() 640 environment.OnStart(effect, effectStart) 641 if environment.startTime is None: 642 environment.startTime = effectStart 643 if checksOnly is None: 644 targetID = environment.targetID 645 flag = self.CheckApplyModifiers(environment.shipID, itemKey, effect, targetID, environment) 646 if flag is not None: 647 checksOnly = not flag 648 self.PossiblyStartOverloadingEffects(effect.effectCategory, effectID, environment.shipID, itemKey, dogmaItem.typeID, environment) 649 if checksOnly: 650 self.effectCompiler.StartEffectChecks(effectID, environment) 651 else: 652 self.effectCompiler.StartEffect(effectID, environment) 653 if effectID == const.effectOnline: 654 try: 655 for onlineEffectID in self.dogmaStaticMgr.effectsByType[dogmaItem.typeID]: 656 if self.dogmaStaticMgr.effects[onlineEffectID].effectCategory != const.dgmEffOnline: 657 continue 658 try: 659 self.StartEffect(onlineEffectID, itemKey, environment, repeat, 0) 660 except UserError: 661 self.LogError('Failed to start online effect', onlineEffectID, effectID, itemKey) 662 sys.exc_clear() 663 664 except KeyError: 665 self.LogWarn('Onlining item with no online effects', itemKey) 666 667 return effectStart 668 669 def PossiblyStartOverloadingEffects(self, *args): 670 pass 671 672 def ConsumeResources(self, environment): 673 pass 674 675 def GetEffectDuration(self, effect, environment): 676 if effect.durationAttributeID: 677 duration = self.GetAttributeValue(environment.itemID, effect.durationAttributeID) 678 if duration < 0: 679 self.FullyLogAttribute(environment.itemID, effect.durationAttributeID, '[EffectCantHaveNegativeDuration %s]' % effect.effectName, force=True) 680 raise UserError('EffectCantHaveNegativeDuration', {'modulename': (const.UE_TYPEID, environment.itemTypeID), 681 'duration': duration}) 682 elif duration < 20: 683 self.broker.LogInfo('Effect', effect.effectName, 'has very conspicuous short duration', duration) 684 else: 685 duration = -1 686 return duration 687 688 def RegisterEffect(self, effect, dogmaItem, environment, effectStart, duration, repeat): 689 effectID = effect.effectID 690 dogmaItem.RegisterEffect(effectID, environment.currentStartTime, duration, environment, repeat) 691 if environment.shipID not in self.activeEffectsByShip: 692 self.activeEffectsByShip[environment.shipID] = {effectID: dogmaItem.itemID} 693 else: 694 self.activeEffectsByShip[environment.shipID][effectID] = dogmaItem.itemID 695 696 def StartEffect_PreChecks(self, effect, dogmaItem, environment, byUser): 697 if byUser and effect.effectCategory in [const.dgmEffOnline, const.dgmEffOverload]: 698 raise RuntimeError('TheseEffectsShouldNeverBeUserStarted', effect.effectID) 699 if effect.effectID == const.effectOnline: 700 moduleHp = self.GetAttributeValue(dogmaItem.itemID, const.attributeHp) 701 moduleDamage = self.GetAttributeValue(dogmaItem.itemID, const.attributeDamage) 702 if moduleHp and moduleHp <= moduleDamage: 703 raise UserError('ModuleTooDamagedToBeOnlined') 704 705 def CheckApplyModifiers(self, *args): 706 pass 707 708 @bluepy.TimedFunction('DogmaLocation::StopEffect') 709 @telemetry.ZONE_METHOD 710 def StopEffect(self, effectID, itemKey, byUser = False, activationInfo = None, forced = False, forceStopRepeating = False, possiblyStopRepeat = True): 711 try: 712 dogmaItem = self.dogmaItems[itemKey] 713 except KeyError: 714 self.LogInfo("StopEffect called on an item that isn't loaded", itemKey, effectID) 715 return 716 717 deactivateKey = (itemKey, effectID) 718 if deactivateKey in self.deactivatingEffects: 719 self.LogWarn('Multiple concurrent StopEffect calls for', deactivateKey) 720 return 721 self.deactivatingEffects.add(deactivateKey) 722 try: 723 if activationInfo is None: 724 try: 725 activationInfo = dogmaItem.activeEffects[effectID] 726 except KeyError: 727 if (itemKey, effectID) in self.activatingEffects: 728 raise UserError('EffectStillActivating', {'moduleName': (const.UE_TYPEID, dogmaItem.typeID)}) 729 self.LogInfo("Stopping effect that isn't active", itemKey, effectID) 730 return 0 731 732 if not self.PreStopEffectChecks(effectID, dogmaItem, activationInfo, forced, byUser, possiblyStopRepeat): 733 return 734 error = self._StopEffect(effectID, dogmaItem, activationInfo, byUser, forced, forceStopRepeating, possiblyStopRepeat) 735 self.PostStopEffectAction(effectID, dogmaItem, activationInfo, forced, byUser, error) 736 finally: 737 self.deactivatingEffects.remove(deactivateKey) 738 739 @telemetry.ZONE_METHOD 740 def _StopEffect(self, effectID, dogmaItem, activationInfo, byUser, forced, forceStopRepeating, possiblyStopRepeat): 741 if effectID in self.onlineEffects: 742 effects = dogmaItem.activeEffects 743 itemKey = dogmaItem.itemID 744 for otherEffectID, activationInfo2 in effects.items(): 745 if otherEffectID != effectID and otherEffectID in effects: 746 otherDuration = effects[otherEffectID][const.ACT_IDX_DURATION] 747 if otherDuration == -1 and self.dogmaStaticMgr.effects[otherEffectID].effectCategory != const.dgmEffPassive: 748 self.StopEffect(otherEffectID, itemKey, False, activationInfo2) 749 750 environment = activationInfo[const.ACT_IDX_ENV] 751 self.effectCompiler.StopEffect(effectID, environment, forced) 752 753 def PreStopEffectChecks(self, effectID, dogmaItem, activationInfo, forced, byUser, possiblyStopRepeat, *args): 754 return True 755 756 @telemetry.ZONE_METHOD 757 def PostStopEffectAction(self, effectID, dogmaItem, activationInfo, forced, byUser, error): 758 environment = activationInfo[const.ACT_IDX_ENV] 759 if dogmaItem.IsEffectRegistered(effectID, environment): 760 effect = self.dogmaStaticMgr.effects[effectID] 761 self.UnregisterEffect(effect, dogmaItem, environment) 762 763 def UnregisterEffect(self, effect, dogmaItem, environment): 764 effectID = effect.effectID 765 dogmaItem.UnregisterEffect(effectID, environment) 766 shipID = environment.shipID 767 try: 768 if shipID in self.activeEffectsByShip: 769 if effectID in self.activeEffectsByShip[shipID]: 770 del self.activeEffectsByShip[shipID][effectID] 771 if len(self.activeEffectsByShip[shipID]) == 0: 772 del self.activeEffectsByShip[shipID] 773 if shipID in self.activeDungeonEffectsByItem: 774 beaconID = environment.itemID 775 if effectID in self.activeDungeonEffectsByItem[shipID]: 776 try: 777 self.activeDungeonEffectsByItem[shipID][effectID].remove(beaconID) 778 except ValueError: 779 pass 780 781 if len(self.activeDungeonEffectsByItem[shipID][effectID]) == 0: 782 del self.activeDungeonEffectsByItem[shipID][effectID] 783 if len(self.activeDungeonEffectsByItem[shipID]) == 0: 784 del self.activeDungeonEffectsByItem[shipID] 785 except KeyError: 786 sys.exc_clear() 787 788 @telemetry.ZONE_METHOD 789 def StartPassiveEffects(self, itemID, typeID): 790 effectIDList = self.dogmaStaticMgr.GetPassiveFilteredEffectsByType(typeID) 791 if not effectIDList: 792 self.LogInfo('No effects to start for item %s of type %s' % (itemID, evetypes.GetName(typeID))) 793 return 794 dogmaItem = self.dogmaItems[itemID] 795 brokerEffects = self.dogmaStaticMgr.effects 796 pilotID = dogmaItem.GetPilot() 797 shipID = dogmaItem.GetShip() 798 sharedEnv = None 799 effectsCompleted = set() 800 effectID = None 801 try: 802 for effectID in effectIDList: 803 if pilotID is None and self.effectCompiler.IsEffectCharacterModifier(effectID): 804 continue 805 if shipID is None and self.effectCompiler.IsEffectShipModifier(effectID): 806 continue 807 if effectID in dogmaItem.activeEffects: 808 continue 809 chanceAttributeID = brokerEffects[effectID].fittingUsageChanceAttributeID 810 if chanceAttributeID and self.dogmaStaticMgr.TypeHasAttribute(typeID, chanceAttributeID): 811 if not self.ShouldStartChanceBasedEffect(effectID, itemID, chanceAttributeID): 812 continue 813 if self.dogmaStaticMgr.effects[effectID].effectCategory == const.dgmEffSystem: 814 self.StartSystemEffect(itemID, effectID) 815 continue 816 pythonEffect = self.effectCompiler.IsEffectPythonOverridden(effectID) 817 if sharedEnv and not pythonEffect: 818 env = sharedEnv 819 else: 820 with bluepy.Timer('LocationManager::Environment'): 821 info = dogmaItem.GetEnvironmentInfo() 822 env = self.GetEnvironment(info, effectID) 823 if sharedEnv is None and not pythonEffect: 824 sharedEnv = env 825 if env.otherID is None and self.effectCompiler.IsEffectDomainOther(effectID): 826 continue 827 try: 828 self.StartEffect(effectID, itemID, env) 829 except UserError as e: 830 if not e.msg.startswith('EffectAlreadyActive'): 831 raise 832 sys.exc_clear() 833 834 effectsCompleted.add(effectID) 835 836 except Exception: 837 log.LogException('Failed to start passive effect %s on %s class %s' % (effectID, itemID, dogmaItem.__class__)) 838 return effectsCompleted 839 840 def ShouldStartChanceBasedEffect(self, effectID, itemID, chanceAttributeID): 841 return False 842 843 def GetActiveEffectEnvironment(self, itemKey, effectID): 844 dogmaItem = self.dogmaItems[itemKey] 845 if effectID not in dogmaItem.activeEffects: 846 return None 847 return dogmaItem.activeEffects[effectID][const.ACT_IDX_ENV] 848 849 def GetEffect(self, effectID): 850 return self.dogmaStaticMgr.effects[effectID] 851 852 def GetSubLocation(self, locationID, flag): 853 if not self.IsItemLoaded(locationID): 854 raise RuntimeError('GetSubLocation called for unloaded location', locationID, flag) 855 try: 856 return self.dogmaItems[locationID].subLocations[flag] 857 except (KeyError, AttributeError): 858 return None 859 860 def IsItemSubLocation(self, itemKey): 861 return type(itemKey) is tuple 862 863 def SetWeaponBanks(self, shipID, data): 864 self.slaveModulesByMasterModule[shipID] = defaultdict(set) 865 if data is None: 866 return 867 for masterID, slaveIDs in data.iteritems(): 868 for slaveID in slaveIDs: 869 self.slaveModulesByMasterModule[shipID][masterID].add(slaveID) 870 871 def GetMasterModuleID(self, shipID, slaveID): 872 for masterID, slaves in self.slaveModulesByMasterModule.get(shipID, {}).iteritems(): 873 if slaveID in slaves: 874 return masterID 875 876 def GetSlaveModules(self, moduleID, shipID): 877 if shipID in self.slaveModulesByMasterModule and moduleID in self.slaveModulesByMasterModule[shipID]: 878 return self.slaveModulesByMasterModule[shipID][moduleID].copy() 879 880 def IsModuleMaster(self, moduleID, shipID): 881 return moduleID in self.slaveModulesByMasterModule.get(shipID, {}) 882 883 def IsModuleSlave(self, moduleID, shipID): 884 return self.GetMasterModuleID(shipID, moduleID) is not None 885 886 def GetAttributesForType(self, typeID): 887 ret = {} 888 for attribute in dogma_data.get_type_attributes(typeID): 889 ret[attribute.attributeID] = attribute.value 890 891 return ret 892 893 def TypeHasAttribute(self, typeID, attributeID): 894 return self.dogmaStaticMgr.TypeHasAttribute(typeID, attributeID) 895 896 def TypeHasEffect(self, typeID, effectID): 897 return self.dogmaStaticMgr.TypeHasEffect(typeID, effectID) 898 899 def GetMissingSkills(self, typeID, currentSkills): 900 ret = {} 901 for skillID, skillLevel in self.dogmaStaticMgr.GetRequiredSkills(typeID).iteritems(): 902 if skillID not in currentSkills or currentSkills[skillID].effectiveSkillLevel < skillLevel: 903 ret[skillID] = skillLevel 904 905 return ret 906 907 @WrappedMethod 908 def UnloadItem(self, itemKey, item = None): 909 if itemKey in self.unloadingItems: 910 self.LogInfo('Item already being unloaded', itemKey) 911 return 912 self.unloadingItems.add(itemKey) 913 try: 914 self._UnloadItem(itemKey, item) 915 finally: 916 self.unloadingItems.remove(itemKey) 917 918 def _UnloadItem_LogOnEntry(self, itemKey, item): 919 givenInvItemDescription = self.DescribeInvItem(item) 920 foundDogmaItem = self.dogmaItems.get(itemKey, None) 921 foundInvItemDescription = self.DescribeInvItem(foundDogmaItem.invItem) if foundDogmaItem else None 922 totalItems = len(self.dogmaItems) 923 LogNotice('UnloadItem requested for itemKey: {itemKey}, with given invItem {givenInvItemDescription}, having found dogmaItems entry {foundDogmaItem} with invItem {foundInvItemDescription} (totalItems = {totalItems}) '.format(**locals())) 924 925 def _UnloadItem(self, itemKey, item): 926 dogmaItem = self.dogmaItems.get(itemKey, None) 927 if dogmaItem is not None: 928 dogmaItem.Unload() 929 self.RemoveItem(itemKey) 930 self.LogInfo('_UnloadItem::Deleting dogmaItem', itemKey) 931 del self.dogmaItems[itemKey] 932 933 def RemoveSubLocationFromLocation(self, itemKey): 934 locationID = itemKey[0] 935 self.dogmaItems[locationID].RemoveSubLocation(itemKey) 936 937 def GetClassForItem(self, item): 938 if item.categoryID == categoryShip: 939 return ShipDogmaItem 940 elif item.categoryID == categoryStructure: 941 return StructureDogmaItem 942 elif item.categoryID == const.categoryStructureModule: 943 return StructureModuleDogmaItem 944 elif item.groupID == const.groupCharacter: 945 return CharacterDogmaItem 946 elif item.groupID in PROBE_GROUPS: 947 return ProbeDogmaItem 948 elif item.categoryID == const.categoryCharge: 949 return ChargeDogmaItem 950 elif item.categoryID in (const.categoryModule, const.categorySubSystem): 951 return ModuleDogmaItem 952 elif item.categoryID in (const.categoryStructureModule,): 953 return StructureModuleDogmaItem 954 elif item.groupID == const.groupControlTower: 955 return ControlTowerDogmaItem 956 elif item.categoryID == const.categoryStarbase: 957 return StarbaseDogmaItem 958 elif item.categoryID == const.categoryDrone: 959 return DroneDogmaItem 960 elif item.categoryID == const.categoryFighter: 961 return FighterDogmaItem 962 else: 963 return BaseDogmaItem 964 965 def GetInstance(self, item): 966 return self.fakeInstanceRow 967 968 def IntroduceNewItem(self, item): 969 return self.fakeInstanceRow 970 971 def GetShipItemForFighter(self, itemID): 972 return None 973 974 def IsFighterOwnerModifiablesAtLocation(self, locationID): 975 return True 976 977 def IsItemWanted(self, typeID, groupID = None): 978 return True 979 980 def RemoveDroneFromLocation(self, shipID, droneID): 981 dogmaItem = self.dogmaItems[shipID] 982 dogmaItem.UnregisterDrone(droneID) 983 984 def AddDroneToLocation(self, shipID, droneID): 985 shipItem = self.dogmaItems[shipID] 986 droneItem = self.dogmaItems[droneID] 987 shipItem.RegisterDrone(droneItem) 988 989 def IsDroneFitted(self, shipID, droneID): 990 try: 991 shipItem = self.dogmaItems[shipID] 992 return droneID in shipItem.GetDronesInBay() 993 except KeyError: 994 return False 995 996 def RegisterCallback(self, attributeID, cb): 997 if isinstance(attributeID, str): 998 attributeID = self.dogmaStaticMgr.attributesByName[attributeID].attributeID 999 if attributeID in self.attributeChangeCallbacksByAttributeID: 1000 log.LogException('RegisterCallback::Overwriting a callback %s %s' % (attributeID, self.attributeChangeCallbacksByAttributeID[attributeID].__name__)) 1001 self.attributeChangeCallbacksByAttributeID[attributeID] = cb 1002 1003 def UnregisterCallback(self, attributeID): 1004 if isinstance(attributeID, str): 1005 attributeID = self.dogmaStaticMgr.attributesByName[attributeID].attributeID 1006 if attributeID in self.attributeChangeCallbacksByAttributeID: 1007 del self.attributeChangeCallbacksByAttributeID[attributeID] 1008 1009 def OnCpuAttributeChanged(self, attributeID, itemID, newValue, oldValue = None): 1010 self.OnFittingChanged(itemID, const.attributeCpuLoad, const.attributeCpuOutput) 1011 1012 def OnPowerAttributeChanged(self, attributeID, itemID, newValue, oldValue = None): 1013 self.OnFittingChanged(itemID, const.attributePowerLoad, const.attributePowerOutput) 1014 1015 def OnFittingChanged(self, itemID, loadAttributeID, outputAttributeID): 1016 if isinstance(itemID, tuple): 1017 return 1018 if itemID not in self.pilotsByShipID: 1019 return 1020 if getattr(self.dogmaItems[itemID], 'isBeingDisembarked', False): 1021 return 1022 load = self.GetAttributeValue(itemID, loadAttributeID) 1023 output = self.GetAttributeValue(itemID, outputAttributeID) 1024 if output - load < 1e-07: 1025 self.CheckPowerCpuRequirementForOnlineModules(itemID) 1026 1027 def ScheduleCheckPowerCpuRequirementForOnlineModules(self, shipID): 1028 if shipID not in self.pendingPowerCpuRequirementChecks: 1029 self.pendingPowerCpuRequirementChecks.add(shipID) 1030 uthread.pool('DogmaLocation::CheckPowerCpuRequirementForOnlineModules', self.CheckPowerCpuRequirementForOnlineModules, shipID) 1031 1032 def CheckPowerCpuRequirementForOnlineModules(self, shipID): 1033 if self.brainUpdate.IsEventHappening(shipID): 1034 return 1035 self.pendingPowerCpuRequirementChecks.discard(shipID) 1036 if self.IsItemLoading(shipID): 1037 return 1038 if self.broker is None: 1039 return 1040 if not self.IsShipLoaded(shipID): 1041 return 1042 if self.IsItemUnloading(shipID): 1043 return 1044 dogmaItem = self.dogmaItems[shipID] 1045 if getattr(dogmaItem, 'isBeingDisembarked', False): 1046 return 1047 if dogmaItem.categoryID not in CATEGORY_SHIPS_AND_STRUCTURES: 1048 return 1049 powerLoad = self.GetAttributeValue(shipID, const.attributePowerLoad) 1050 powerOutput = self.GetAttributeValue(shipID, const.attributePowerOutput) 1051 cpuLoad = self.GetAttributeValue(shipID, const.attributeCpuLoad) 1052 cpuOutput = self.GetAttributeValue(shipID, const.attributeCpuOutput) 1053 powerUsage = powerLoad / max(powerOutput, 1e-07) 1054 cpuUsage = cpuLoad / max(cpuOutput, 1e-07) 1055 if cpuUsage <= 1.0 and powerUsage <= 1.0: 1056 return 1057 resourceAttributeID = const.attributePower if powerUsage > cpuUsage else const.attributeCpu 1058 self._OfflineModuleUsingMostResource(shipID, dogmaItem, resourceAttributeID) 1059 1060 def _OfflineModuleUsingMostResource(self, shipID, dogmaItem, resourceAttributeID): 1061 if resourceAttributeID not in (const.attributePower, const.attributeCpu): 1062 raise ValueError('_OfflineModuleUsingMostResource called with invalid resourceAttributeID') 1063 if not self.IsShipLoaded(shipID): 1064 return 1065 candidates = [] 1066 for moduleID, moduleDogmaItem in dogmaItem.GetFittedItems().iteritems(): 1067 if (moduleID, const.effectOnline) in self.deactivatingEffects: 1068 continue 1069 if not moduleDogmaItem.IsOnline(): 1070 continue 1071 resourceUsed = self.GetAttributeValue(moduleID, resourceAttributeID) 1072 if resourceUsed: 1073 if moduleDogmaItem.flagID in serviceSlotFlags: 1074 sortKey = (0, resourceUsed) 1075 else: 1076 sortKey = (1, resourceUsed) 1077 candidates.append((sortKey, moduleID)) 1078 1079 if len(candidates) == 0: 1080 self.broker.LogError('The ship', shipID, 'has something modifying resource attribute', resourceAttributeID, "that isn't an online effect") 1081 return 1082 itemKey = max(candidates)[1] 1083 if (itemKey, const.effectOnline) not in self.deactivatingEffects: 1084 self.OfflineModule(itemKey) 1085 1086 def OnMaxGroupOnlineChanged(self, attributeID, itemID, newValue, oldValue = None): 1087 pass 1088 1089 def OfflineModule(self, moduleID): 1090 self.StopEffect(const.effectOnline, moduleID) 1091 1092 def FitItemToSelf(self, item): 1093 self.OnItemAddedToLocation(item.itemID, item, shipid=item.itemID) 1094 1095 def GetCharacter(self, itemID, flush = False): 1096 pass 1097 1098 def LoadSublocations(self, itemID): 1099 for flagID, sublocation in self.instanceFlagQuantityCache.get(itemID, {}).iteritems(): 1100 self.LoadItem((sublocation[0], sublocation[1], sublocation[2])) 1101 1102 def GetAttributesByIndex(self): 1103 return const.dgmAttributesByIdx 1104 1105 def LoadItemsInLocation(self, itemID): 1106 pass 1107 1108 def UnloadItemsInLocation(self, locationID): 1109 pass 1110 1111 def GetItem(self, itemID): 1112 pass 1113 1114 def OnSublocationAddedToLocation(self, itemKey, shipid = None, pilotID = None, fitting = False, locationName = None, timerName = 'AddUnknown2'): 1115 pass 1116 1117 def OnItemAddedToLocation(self, locationid, item, shipid = None, pilotID = None, fitting = False, locationName = None, timerName = 'AddUnknown1', byUser = False): 1118 pass 1119 1120 def GetAttributeValue(self, itemKey, attributeID): 1121 return self.dogmaItems[itemKey].attributes[attributeID].GetValue() 1122 1123 def GetIndustryCharacterModifiers(self, characterID): 1124 if not self.IsItemLoaded(characterID): 1125 raise RuntimeError('Characters must be loaded before you can query about their industry attributes. Try again later.') 1126 return {attributeID:self.GetAttributeValue(characterID, attributeID) for attributeID in (m[1] for m in CHARACTER_MODIFIERS)} 1127 1128 def GetAttributeDictForItem(self, itemKey): 1129 try: 1130 item = self.dogmaItems[itemKey] 1131 except KeyError: 1132 return {} 1133 1134 return {attribID:item.attributes[attribID].GetValue() for attribID in item.attributes} 1135 1136 def ItemHasAttribute(self, itemID, attributeID): 1137 return itemID in self.dogmaItems and attributeID in self.dogmaItems[itemID].attributes 1138 1139 def SetAttributeValue(self, itemKey, attributeID, value, dirty = True, keepLists = True): 1140 value, v, dirty = self._SetAttributeValue(itemKey, attributeID, value, dirty, keepLists) 1141 return value 1142 1143 def _SetAttributeValue(self, itemKey, attributeID, value, dirty, keepLists): 1144 if itemKey not in self.dogmaItems: 1145 self.broker.LogInfo('Item', itemKey, 'not loaded') 1146 return (0, 0, False) 1147 dogmaAttribute = self.dogmaItems[itemKey].attributes[attributeID] 1148 oldValue = dogmaAttribute.GetValue() 1149 if oldValue == value: 1150 dirty = False 1151 if value is None: 1152 logger.error('Tried to set attributesByItemAttribute to None', extra={'oldValue': oldValue}) 1153 return (0, 0, False) 1154 dogmaAttribute.SetBaseValue(value) 1155 if itemKey in self.dogmaItems: 1156 self.OnAttributeChanged(attributeID, itemKey, value, oldValue) 1157 return (value, oldValue, dirty) 1158 1159 def OnDamageAttributeChange(attributeID, itemKey, value, v): 1160 pass 1161 1162 @telemetry.ZONE_METHOD 1163 def OnAttributeChanged(self, attributeID, itemKey, value = None, oldValue = None): 1164 attrObj = self.dogmaItems[itemKey].attributes[attributeID] 1165 if value is None: 1166 value = attrObj.GetValue() 1167 for _opIdx, dependantAttrObj in attrObj.GetOutgoingModifiers(): 1168 try: 1169 if dependantAttrObj.item is None: 1170 continue 1171 affectedItemID = dependantAttrObj.item.itemID 1172 affectedAttributeID = dependantAttrObj.attribID 1173 if affectedItemID not in self.unloadingItems and affectedItemID in self.dogmaItems: 1174 self.OnAttributeChanged(affectedAttributeID, affectedItemID) 1175 except ReferenceError: 1176 logger.exception('BaseDogmaLocation::OnAttributeChanged: Orphan attribute detected as dependantAttrObj via attrObj', extra={'dependantAttrObj': dependantAttrObj, 1177 'attrObj': attrObj}) 1178 dependantAttrObj.CheckIntegrity() 1179 1180 return value 1181 1182 def GetDamageStateEx(self, itemID, ownerID = None): 1183 if itemID in self.failedLoadingItems: 1184 return None 1185 if itemID not in self.dogmaItems: 1186 return None 1187 dogmaItem = self.dogmaItems[itemID] 1188 if isinstance(dogmaItem, NotWantedDogmaItem): 1189 return None 1190 if ownerID and dogmaItem.ownerID != ownerID: 1191 return None 1192 ret = [] 1193 v = dogmaItem.attributes[const.attributeShieldCharge].GetFullChargedInfo() 1194 if v[1] > 0: 1195 ret.append((v[0] / v[1], v[2], blue.os.GetSimTime())) 1196 else: 1197 ret.append(v[0]) 1198 hpAttribute = const.attributeArmorHP 1199 damageAttribute = const.attributeArmorDamage 1200 maxDamage = dogmaItem.attributes[hpAttribute].GetValue() 1201 if maxDamage == 0: 1202 ret.append(0.0) 1203 else: 1204 ret.append(1.0 - dogmaItem.attributes[damageAttribute].GetValue() / maxDamage) 1205 maxDamage = dogmaItem.attributes[const.attributeHp].GetValue() 1206 if maxDamage == 0: 1207 ret.append(0.0) 1208 else: 1209 ret.append(1.0 - dogmaItem.attributes[const.attributeDamage].GetValue() / maxDamage) 1210 return ret 1211 1212 def IsItemLoaded(self, itemID): 1213 return itemID not in self.loadingItems and itemID in self.dogmaItems 1214 1215 def IsItemLoading(self, itemID): 1216 return itemID in self.loadingItems 1217 1218 def IsItemUnloading(self, itemKey): 1219 return itemKey in self.unloadingItems 1220 1221 def IsShipLoaded(self, shipID): 1222 dogmaItem = self.dogmaItems.get(shipID, None) 1223 if dogmaItem is None: 1224 return False 1225 else: 1226 return dogmaItem.categoryID in CATEGORY_SHIPS_AND_STRUCTURES 1227 1228 def DescribeInvItem(self, invItem): 1229 if invItem is None: 1230 return 'None' 1231 try: 1232 invItemCategoryName = evetypes.GetCategoryNameByCategory(invItem.categoryID) 1233 invItemGroupName = evetypes.GetGroupNameByGroup(invItem.groupID) 1234 invItemTypeName = evetypes.GetName(invItem.typeID) 1235 invItemID = invItem.itemID 1236 return "'{invItemCategoryName}/{invItemGroupName}/{invItemTypeName}' {invItemID}".format(**locals()) 1237 except: 1238 return '[failed to describe invItem: {}]'.format(invItem) 1239 1240 def _GenericAddModifier(self, operation, toItem, toAttribID, fromAttrib): 1241 toAttrib = toItem.attributes[toAttribID] 1242 oldValue = toAttrib.GetValue() 1243 fromAttrib.AddModifierTo(operation, toAttrib) 1244 self.OnAttributeChanged(toAttribID, toItem.itemID, oldValue=oldValue) 1245 1246 def _GenericRemoveModifier(self, operation, toItem, toAttribID, fromAttrib, weirdSpecialCase = False): 1247 toAttrib = toItem.attributes[toAttribID] 1248 oldValue = toAttrib.GetValue() 1249 fromAttrib.RemoveModifierTo(operation, toAttrib) 1250 if weirdSpecialCase: 1251 toItemID = toItem.itemID 1252 if not self.IsItemUnloading(toItemID) and oldValue is not None: 1253 self.OnAttributeChanged(toAttribID, toItemID, oldValue=oldValue) 1254 else: 1255 self.OnAttributeChanged(toAttribID, toItem.itemID, oldValue=oldValue) 1256 1257 def _FindOrDefer(self, itemID, deferFunc, *deferArgs): 1258 try: 1259 targetItem = self.dogmaItems[itemID] 1260 return targetItem 1261 except KeyError: 1262 self.modifiersAwaitingTarget[itemID].add((deferFunc,) + deferArgs) 1263 return None 1264 1265 def _FindOrAbandon(self, itemID, deferFunc, *deferArgs): 1266 try: 1267 targetItem = self.dogmaItems[itemID] 1268 return targetItem 1269 except KeyError: 1270 try: 1271 self.modifiersAwaitingTarget[itemID].remove((deferFunc,) + deferArgs) 1272 except KeyError: 1273 pass 1274 1275 return None 1276 1277 def _Add(self, theDict, index, toAdd, debugStr = '<none>'): 1278 try: 1279 innerContainer = theDict[index] 1280 except KeyError: 1281 logger.exception('_Add: no innerContainer found at theDict[index]') 1282 return 1283 1284 innerContainer.add(toAdd) 1285 1286 def _RemoveAndCleanup(self, theDict, index, toRemove, debugStr = '<none>'): 1287 try: 1288 innerContainer = theDict[index] 1289 except KeyError: 1290 logger.exception('_RemoveAndCleanup: no innerContainer found at theDict[index]') 1291 return 1292 1293 innerContainer.discard(toRemove) 1294 if not theDict[index]: 1295 del theDict[index] 1296 1297 def _Add_2D(self, theDict, index1, index2, toAdd, debugStr = '<none>'): 1298 try: 1299 innerContainer = theDict[index1][index2] 1300 except KeyError: 1301 logger.exception('_Add_2D: no innerContainer found at theDict[index1][index2]') 1302 return 1303 1304 innerContainer.add(toAdd) 1305 1306 def _RemoveAndCleanup_2D(self, theDict, index1, index2, toRemove, debugStr = '<none>'): 1307 try: 1308 innerContainer = theDict[index1][index2] 1309 except KeyError: 1310 logger.exception('_RemoveAndCleanup_2D: no innerContainer found at theDict[index1][index2]') 1311 return 1312 1313 innerContainer.discard(toRemove) 1314 if not theDict[index1][index2]: 1315 del theDict[index1][index2] 1316 if not theDict[index1]: 1317 del theDict[index1] 1318 1319 @telemetry.ZONE_METHOD 1320 def AddModifier(self, operation, toItemID, toAttribID, fromItemID, fromAttribID): 1321 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1322 self.AddModifierWithSource(operation, toItemID, toAttribID, fromAttrib) 1323 1324 def AddModifierWithSource(self, operation, toItemID, toAttribID, fromAttrib): 1325 self.CheckItemsMissingInAddModifier(toItemID, fromAttrib) 1326 toItem = self._FindOrDefer(toItemID, self.AddModifierWithSource, operation, toItemID, toAttribID, fromAttrib) 1327 if not toItem: 1328 return 1329 self._GenericAddModifier(operation, toItem, toAttribID, fromAttrib) 1330 1331 def CheckItemsMissingInAddModifier(self, toItemID, fromAttrib): 1332 if not toItemID or not fromAttrib: 1333 raise RuntimeError('Item missing from AddModifier') 1334 1335 @telemetry.ZONE_METHOD 1336 def RemoveModifier(self, operation, toItemID, toAttribID, fromItemID, fromAttribID): 1337 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1338 self.RemoveModifierWithSource(operation, toItemID, toAttribID, fromAttrib) 1339 1340 def RemoveModifierWithSource(self, operation, toItemID, toAttribID, fromAttrib): 1341 if not toItemID or not fromAttrib: 1342 return 1343 toItem = self._FindOrAbandon(toItemID, self.AddModifierWithSource, operation, toItemID, toAttribID, fromAttrib) 1344 if not toItem: 1345 return 1346 self._GenericRemoveModifier(operation, toItem, toAttribID, fromAttrib, weirdSpecialCase=True) 1347 1348 @WrappedMethod 1349 @telemetry.ZONE_METHOD 1350 def AddLocationModifier(self, operation, toLocationID, toAttribID, fromItemID, fromAttribID): 1351 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1352 self.AddLocationModifierWithSource(operation, toLocationID, toAttribID, fromAttrib) 1353 1354 def AddLocationModifierWithSource(self, operation, toLocationID, toAttribID, fromAttrib): 1355 if not toLocationID: 1356 raise RuntimeError('Item missing from AddLocationModifier') 1357 toLocation = self._FindOrDefer(toLocationID, self.AddLocationModifierWithSource, operation, toLocationID, toAttribID, fromAttrib) 1358 if not toLocation: 1359 return 1360 self._Add(toLocation.locationMods, toAttribID, (operation, fromAttrib), debugStr='LocMod for Location {}'.format(toLocation)) 1361 for toItem in filter(lambda i: toAttribID in i.attributes, self.IterFittedItems(toLocation)): 1362 self._GenericAddModifier(operation, toItem, toAttribID, fromAttrib) 1363 1364 @WrappedMethod 1365 @telemetry.ZONE_METHOD 1366 def RemoveLocationModifier(self, operation, toLocationID, toAttribID, fromItemID, fromAttribID): 1367 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1368 self.RemoveLocationModifierWithSource(operation, toLocationID, toAttribID, fromAttrib) 1369 1370 def RemoveLocationModifierWithSource(self, operation, toLocationID, toAttribID, fromAttrib): 1371 if not toLocationID: 1372 return 1373 toLocation = self._FindOrAbandon(toLocationID, self.AddLocationModifierWithSource, operation, toLocationID, toAttribID, fromAttrib) 1374 if not toLocation: 1375 return 1376 self._RemoveAndCleanup(toLocation.locationMods, toAttribID, (operation, fromAttrib), debugStr='LocMod for Location {}'.format(toLocation)) 1377 for toItem in filter(lambda i: toAttribID in i.attributes, self.IterFittedItems(toLocation)): 1378 self._GenericRemoveModifier(operation, toItem, toAttribID, fromAttrib) 1379 1380 @WrappedMethod 1381 @telemetry.ZONE_METHOD 1382 def AddLocationGroupModifier(self, operation, toLocationID, groupID, toAttribID, fromItemID, fromAttribID): 1383 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1384 self.AddLocationGroupModifierWithSource(operation, toLocationID, groupID, toAttribID, fromAttrib) 1385 1386 def AddLocationGroupModifierWithSource(self, operation, toLocationID, groupID, toAttribID, fromAttrib): 1387 if not toLocationID: 1388 raise RuntimeError('Item missing from AddLocationGroupModifier') 1389 toLocation = self._FindOrDefer(toLocationID, self.AddLocationGroupModifierWithSource, operation, toLocationID, groupID, toAttribID, fromAttrib) 1390 if not toLocation: 1391 return 1392 self._Add_2D(toLocation.locationGroupMods, groupID, toAttribID, (operation, fromAttrib), debugStr='LocGrpMod for Location {}'.format(toLocation)) 1393 for toItem in filter(lambda i: i.invItem.groupID == groupID and toAttribID in i.attributes, self.IterFittedItems(toLocation)): 1394 self._GenericAddModifier(operation, toItem, toAttribID, fromAttrib) 1395 1396 @WrappedMethod 1397 @telemetry.ZONE_METHOD 1398 def RemoveLocationGroupModifier(self, operation, toLocationID, groupID, toAttribID, fromItemID, fromAttribID): 1399 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1400 self.RemoveLocationGroupModifierWithSource(operation, toLocationID, groupID, toAttribID, fromAttrib) 1401 1402 def RemoveLocationGroupModifierWithSource(self, operation, toLocationID, groupID, toAttribID, fromAttrib): 1403 if not toLocationID: 1404 return 1405 toLocation = self._FindOrAbandon(toLocationID, self.AddLocationGroupModifierWithSource, operation, toLocationID, groupID, toAttribID, fromAttrib) 1406 if not toLocation: 1407 return 1408 self._RemoveAndCleanup_2D(toLocation.locationGroupMods, groupID, toAttribID, (operation, fromAttrib), debugStr='LocGrpMod for Location {}'.format(toLocation)) 1409 for toItem in filter(lambda i: i.invItem.groupID == groupID and toAttribID in i.attributes, self.IterFittedItems(toLocation)): 1410 self._GenericRemoveModifier(operation, toItem, toAttribID, fromAttrib) 1411 1412 @WrappedMethod 1413 @telemetry.ZONE_METHOD 1414 def AddLocationRequiredSkillModifier(self, operation, toLocationID, skillID, toAttribID, fromItemID, fromAttribID): 1415 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1416 self.AddLocationRequiredSkillModifierWithSource(operation, toLocationID, skillID, toAttribID, fromAttrib) 1417 1418 def AddLocationRequiredSkillModifierWithSource(self, operation, toLocationID, skillID, toAttribID, fromAttrib): 1419 if not toLocationID: 1420 raise RuntimeError('Item missing from AddLocationRequiredSkillModifier') 1421 toLocation = self._FindOrDefer(toLocationID, self.AddLocationRequiredSkillModifierWithSource, operation, toLocationID, skillID, toAttribID, fromAttrib) 1422 if not toLocation: 1423 return 1424 self._Add_2D(toLocation.locationReqSkillMods, skillID, toAttribID, (operation, fromAttrib), debugStr='LocReqSkillMod for Location {}'.format(toLocation)) 1425 for toItem in filter(lambda i: skillID in i.reqSkills and toAttribID in i.attributes, self.IterFittedItems(toLocation)): 1426 self._GenericAddModifier(operation, toItem, toAttribID, fromAttrib) 1427 1428 @WrappedMethod 1429 @telemetry.ZONE_METHOD 1430 def RemoveLocationRequiredSkillModifier(self, operation, toLocationID, skillID, toAttribID, fromItemID, fromAttribID): 1431 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1432 self.RemoveLocationRequiredSkillModifierWithSource(operation, toLocationID, skillID, toAttribID, fromAttrib) 1433 1434 def RemoveLocationRequiredSkillModifierWithSource(self, operation, toLocationID, skillID, toAttribID, fromAttrib): 1435 if not toLocationID: 1436 return 1437 toLocation = self._FindOrAbandon(toLocationID, self.AddLocationRequiredSkillModifierWithSource, operation, toLocationID, skillID, toAttribID, fromAttrib) 1438 if not toLocation: 1439 return 1440 self._RemoveAndCleanup_2D(toLocation.locationReqSkillMods, skillID, toAttribID, (operation, fromAttrib), debugStr='LocReqSkillMod for Location {}'.format(toLocation)) 1441 for toItem in filter(lambda i: skillID in i.reqSkills and toAttribID in i.attributes, self.IterFittedItems(toLocation)): 1442 self._GenericRemoveModifier(operation, toItem, toAttribID, fromAttrib) 1443 1444 def IterFittedItems(self, toLocation): 1445 try: 1446 return toLocation.fittedItems.itervalues() 1447 except AttributeError: 1448 return [] 1449 1450 @WrappedMethod 1451 @telemetry.ZONE_METHOD 1452 def AddOwnerRequiredSkillModifier(self, operation, ownerID, skillID, toAttribID, fromItemID, fromAttribID): 1453 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1454 self.AddOwnerRequiredSkillModifierWithSource(operation, ownerID, skillID, toAttribID, fromAttrib) 1455 1456 def AddOwnerRequiredSkillModifierWithSource(self, operation, ownerID, skillID, toAttribID, fromAttrib): 1457 if not ownerID: 1458 return 1459 toOwner = self._FindOrDefer(ownerID, self.AddOwnerRequiredSkillModifierWithSource, operation, ownerID, skillID, toAttribID, fromAttrib) 1460 if not toOwner: 1461 return 1462 self._Add_2D(toOwner.ownerReqSkillMods, skillID, toAttribID, (operation, fromAttrib), debugStr='OwnerReqSkillMod for Owner {}'.format(toOwner)) 1463 modItems = filter(lambda i: skillID in i.reqSkills and toAttribID in i.attributes, toOwner.GetOwnedItemsToModify()) 1464 for toItem in modItems: 1465 self._GenericAddModifier(operation, toItem, toAttribID, fromAttrib) 1466 1467 @WrappedMethod 1468 @telemetry.ZONE_METHOD 1469 def RemoveOwnerRequiredSkillModifier(self, operation, ownerID, skillID, toAttribID, fromItemID, fromAttribID): 1470 fromAttrib = self.dogmaItems[fromItemID].attributes[fromAttribID] 1471 self.RemoveOwnerRequiredSkillModifierWithSource(operation, ownerID, skillID, toAttribID, fromAttrib) 1472 1473 def RemoveOwnerRequiredSkillModifierWithSource(self, operation, ownerID, skillID, toAttribID, fromAttrib): 1474 if not ownerID: 1475 return 1476 toOwner = self._FindOrAbandon(ownerID, self.AddOwnerRequiredSkillModifierWithSource, operation, ownerID, skillID, toAttribID, fromAttrib) 1477 if not toOwner: 1478 return 1479 self._RemoveAndCleanup_2D(toOwner.ownerReqSkillMods, skillID, toAttribID, (operation, fromAttrib), debugStr='OwnerReqSkillMod for Owner {}'.format(toOwner)) 1480 modItems = filter(lambda i: skillID in i.reqSkills and toAttribID in i.attributes, toOwner.GetOwnedItemsToModify()) 1481 for toItem in modItems: 1482 self._GenericRemoveModifier(operation, toItem, toAttribID, fromAttrib) 1483 1484 @telemetry.ZONE_METHOD 1485 def RemoveItem(self, itemID): 1486 self.dogmaItems[itemID].FlushAllModifiers() 1487 1488 @telemetry.ZONE_METHOD 1489 def DescribeModifierGraphForDebugWindow(self, itemID, attributeID): 1490 try: 1491 itemAttributes = self.dogmaItems[itemID].attributes 1492 if attributeID in itemAttributes: 1493 attribRoot = itemAttributes[attributeID] 1494 else: 1495 return 'AttribID {} not found for ItemID {}'.format(attributeID, itemID) 1496 except: 1497 return 'ItemID {} not found in dogmaItems'.format(itemID) 1498 1499 levels = defaultdict(list) 1500 1501 def DescribeAttributeForDebugWindow(attrib): 1502 attribStr = attrib.friendlyStr() 1503 base = attrib.GetBaseValue() 1504 val = attrib.GetValue() 1505 attribDescription = '{attribStr}: base={base} val={val}'.format(**locals()) 1506 return attribDescription 1507 1508 def DescribeModifierForDebugWindow(operatorName, attrib, relationName = 'from'): 1509 attribDescription = DescribeAttributeForDebugWindow(attrib) 1510 modifierDescription = ' {operatorName} {relationName} {attribDescription}'.format(**locals()) 1511 return modifierDescription 1512 1513 def HarvestModifiers(curAttrib, curLevel, isIncoming): 1514 if isIncoming: 1515 direction, relationName, levelDelta = ('Incoming', 'from', -1) 1516 modifiers = curAttrib.GetIncomingModifiers() 1517 else: 1518 direction, relationName, levelDelta = 'Outgoing', 'to', +1 1519 modifiers = curAttrib.GetOutgoingModifiers() 1520 if not modifiers: 1521 return 1522 curAttribDescription = DescribeAttributeForDebugWindow(curAttrib) 1523 levels[curLevel].append('{} Mods for {}:'.format(direction, curAttribDescription)) 1524 for opIdx, attrib in modifiers: 1525 opName = OpName(opIdx) 1526 levels[curLevel].append(DescribeModifierForDebugWindow(opName, attrib, relationName)) 1527 HarvestModifiers(attrib, curLevel + levelDelta, isIncoming) 1528 1529 rootInfo = 'Root at address {:#x}'.format(id(attribRoot)) 1530 levels[0].append(rootInfo) 1531 levels[0].append(DescribeModifierForDebugWindow('Root', attribRoot, 'is')) 1532 HarvestModifiers(attribRoot, curLevel=-1, isIncoming=True) 1533 HarvestModifiers(attribRoot, curLevel=1, isIncoming=False) 1534 levels = sorted([ (level, strings) for level, strings in levels.iteritems() ]) 1535 ret = '\n\n'.join([ 'Level %d:\n\n' % (level,) + '\n'.join(strings) for level, strings in levels ]) 1536 return ret 1537 1538 def EnterCriticalSection(self, k, v): 1539 if (k, v) not in self.crits: 1540 self.crits[k, v] = uthread.CriticalSection((k, v)) 1541 self.crits[k, v].acquire() 1542 1543 def LeaveCriticalSection(self, k, v): 1544 self.crits[k, v].release() 1545 if (k, v) in self.crits and self.crits[k, v].IsCool(): 1546 del self.crits[k, v] 1547 1548 def FullyLogAttribute(self, itemID, attributeID, reason, force = 0): 1549 description = self.FullyDescribeAttribute(itemID, attributeID, reason, force) 1550 self.LogNotice(description) 1551 return description 1552 1553 def FullyDescribeAttribute(self, itemID, attributeID, reason, force = 0): 1554 if type(attributeID) is str: 1555 attribute = self.dogmaStaticMgr.attributesByName[attributeID] 1556 attributeID = attribute.attributeID 1557 else: 1558 attribute = self.dogmaStaticMgr.attributes[attributeID] 1559 rep = ['Full description requested for attribute %s on item %s, reason: %s' % (attribute.name, self.GetLocName(itemID), reason)] 1560 stackText = 'Stackable, ' if attribute.stackable else 'Not stackable, ' 1561 stackText += 'higher value is better.' if attribute.highIsGood else 'lower value is better.' 1562 rep.append(stackText) 1563 val = self.QueryAttributeValue(itemID, attributeID) 1564 rep.append('Value:%s' % val) 1565 if self.IsItemSubLocation(itemID): 1566 locationID = itemID[0] 1567 typeID = itemID[2] 1568 groupID = evetypes.GetGroupID(typeID) 1569 _locationItem = self.broker.i2.GetItemMx(locationID) 1570 ownerID = _locationItem.ownerID 1571 elif itemID in self.dogmaItems: 1572 _dogmaItem = self.dogmaItems[itemID] 1573 if _dogmaItem.location is not None: 1574 locationID = _dogmaItem.location.itemID 1575 else: 1576 locationID = None 1577 typeID, groupID, ownerID = _dogmaItem.typeID, _dogmaItem.groupID, _dogmaItem.ownerID 1578 else: 1579 item = self.broker.i2.GetItemMx(itemID) 1580 locationID = item.locationID 1581 typeID, groupID, ownerID = item.typeID, item.groupID, item.ownerID 1582 oval = self.dogmaStaticMgr.GetTypeAttribute(typeID, attributeID, 'No idea!') 1583 rep.append('Original Value:%s' % oval) 1584 rep.append('Attribute modification graph:') 1585 rep.append('') 1586 modifierInfo = self.DescribeModifierGraphForDebugWindow(itemID, attributeID) 1587 rep.extend(modifierInfo.split('\n')) 1588 return rep 1589 1590 def QueryAttributeValue(self, itemID, attributeID): 1591 try: 1592 val = self.GetAttributeValue(itemID, attributeID) 1593 except Exception: 1594 val = '[No idea!]' 1595 sys.exc_clear() 1596 1597 return val 1598 1599 def QueryAllAttributesForItem(self, itemID): 1600 return self.GetAttributeDictForItem(itemID) 1601 1602 def GetLocName(self, locid): 1603 dogmaItem = self.dogmaItems.get(locid, None) 1604 if dogmaItem is None: 1605 return 'Unknown Item' 1606 return evetypes.GetName(dogmaItem.typeID) 1607 1608 def LogInfo(self, *args): 1609 return self.broker.LogInfo(self.locationID, *args) 1610 1611 def LogNotice(self, *args): 1612 return self.broker.LogNotice(self.locationID, *args) 1613 1614 def LogWarn(self, *args): 1615 return self.broker.LogWarn(self.locationID, *args) 1616 1617 def LogError(self, *args): 1618 return self.broker.LogError(self.locationID, *args) 1619 1620 def IsIgnoringOwnerEvents(self, ownerID): 1621 return self.ignoredOwnerEvents.IsEventHappening(ownerID) 1622 1623 def ShouldMessageItemEvent(self, itemID, ownerID = None, locationID = None): 1624 return False 1625 1626 def SlotExists(self, shipID, flagID): 1627 attributeID = None 1628 if flagID in const.hiSlotFlags: 1629 attributeID = const.attributeHiSlots 1630 slotNumber = flagID - const.flagHiSlot0 1631 elif flagID in const.medSlotFlags: 1632 attributeID = const.attributeMedSlots 1633 slotNumber = flagID - const.flagMedSlot0 1634 elif flagID in const.loSlotFlags: 1635 attributeID = const.attributeLowSlots 1636 slotNumber = flagID - const.flagLoSlot0 1637 elif flagID in const.rigSlotFlags: 1638 attributeID = const.attributeRigSlots 1639 slotNumber = flagID - const.flagRigSlot0 1640 elif flagID in const.serviceSlotFlags: 1641 attributeID = const.attributeServiceSlots 1642 slotNumber = flagID - const.flagServiceSlot0 1643 if attributeID is not None: 1644 totalSlots = self.GetAttributeValue(shipID, attributeID) 1645 if slotNumber >= totalSlots: 1646 return False 1647 return True 1648 1649 def GetSublocations(self, shipID): 1650 ret = set() 1651 for subLocation in self.GetDogmaItem(shipID).subLocations.itervalues(): 1652 ret.add(self.GetDogmaItem(subLocation)) 1653 1654 return ret 1655 1656 def GetSlotOther(self, shipID, flagID): 1657 for item in self.dogmaItems[shipID].GetFittedItems().itervalues(): 1658 if item.flagID == flagID and IsFittingModule(item.categoryID): 1659 return item.itemID 1660 1661 @telemetry.ZONE_METHOD 1662 def CheckSkillRequirements(self, charID, skillID, errorMsgName): 1663 skillItem = self.dogmaItems[skillID] 1664 self.CheckSkillRequirementsForType(skillItem.typeID, errorMsgName) 1665 1666 def CheckSkillRequirementsForType(self, typeID, errorMsgName): 1667 pass 1668 1669 def ProcessBrainData(self, charID, brainData): 1670 brainVersion, grayMatter = brainData 1671 if grayMatter: 1672 charEffects, shipEffects, structureEffects = blue.marshal.Load(grayMatter) 1673 self.broker.LogInfo('Got a brain with', len(charEffects), 'char effects, ', len(shipEffects), 'ship effects and', len(structureEffects), 'structure effects') 1674 brain = (brainVersion, 1675 charEffects, 1676 shipEffects, 1677 structureEffects) 1678 self.SetBrainData(charID, brain) 1679 1680 def CanFitItemInSpace(self, charID, shipID, itemID): 1681 return True 1682 1683 def CheckSlotOnliningAndExistence(self, shipID, moduleID): 1684 pass 1685 1686 def PersistItem2(self, itemID): 1687 pass 1688 1689 def PersistItem(self, itemID): 1690 pass 1691 1692 def GetEnvironment(self, info, effectID, targetID = None): 1693 return Environment(itemID=info.itemID, charID=info.charID, shipID=info.shipID, targetID=targetID, otherID=info.otherID, effectID=effectID, dogmaLM=weakref.proxy(self), expressionID=None, structureID=info.structureID) 1694 1695 def _UpdateShipEffectsFromBrain(self, updateFunction, updateFunctionType, shipEffects, structureEffects, shipId, shipDogmaItem): 1696 effects = self._PickShipOrStructureEffects(shipEffects, structureEffects, shipDogmaItem.categoryID) 1697 self._LogUpdatedBrainEffects(updateFunctionType, len(effects), 'ship/structure', shipId) 1698 validAttributes = self._GetValidAttributes(shipDogmaItem.typeID) 1699 for effect in effects: 1700 if not self._IsShipEffectValid(effect, validAttributes): 1701 continue 1702 literal = self.brainLiterals[effect.GetLiteralKey()] 1703 effectType = effect.modifierType 1704 updateFunction[effectType](self, shipId, literal, *effect.GetApplicationArgs()) 1705 1706 def _PickShipOrStructureEffects(self, shipEffects, structureEffects, categoryId): 1707 if categoryId == categoryShip: 1708 return shipEffects 1709 return structureEffects 1710 1711 def _GetValidAttributes(self, shipTypeId): 1712 validAttributes = {x.attributeID for x in dogma_data.get_type_attributes(shipTypeId)} 1713 validAttributes.add(const.attributeCapacity) 1714 return validAttributes 1715 1716 def _IsShipEffectValid(self, effect, validAttributes): 1717 return effect.modifierType != 'M' or effect.toAttribID in validAttributes 1718 1719 def _UpdateCharacterEffectsFromBrain(self, updateFunction, updateFunctionType, characterEffects, characterId): 1720 self._LogUpdatedBrainEffects(updateFunctionType, len(characterEffects), 'character', characterId) 1721 for effect in characterEffects: 1722 literal = self.brainLiterals[effect.GetLiteralKey()] 1723 effType = effect.modifierType 1724 updateFunction[effType](self, characterId, literal, *effect.GetApplicationArgs()) 1725 1726 def _LogUpdatedBrainEffects(self, updateFunctionType, effectAmount, domainType, domainId): 1727 self.broker.LogInfo('%s - %s %s %s effects to %s' % (self.locationID, 1728 updateFunctionType, 1729 effectAmount, 1730 domainType, 1731 domainId)) 1732 1733 def GetStructureIdForEnvironment(self, shipId): 1734 if shipId is not None and self.IsItemIdStructure(shipId): 1735 return shipId 1736 1737 def IsItemIdStructure(self, itemId): 1738 shipDogmaItem = self.dogmaItems.get(itemId, None) 1739 return shipDogmaItem and shipDogmaItem.categoryID == categoryStructure 1740 1741 def UpdateStructureServices(self, structureID): 1742 pass 1743 1744 def UninstallPilotFromShipAndItems(self, oldShipID): 1745 dogmaItem = self.dogmaItems.get(oldShipID) 1746 if dogmaItem is None: 1747 return 1748 fittedItems = dogmaItem.GetFittedItems() 1749 for itemKey in fittedItems: 1750 self.UninstallPilotForItem(itemKey) 1751 1752 def UninstallPilotForItem(self, itemKey): 1753 dogmaItem = self.dogmaItems[itemKey] 1754 for effectID in dogmaItem.activeEffects.keys(): 1755 effect = self.dogmaStaticMgr.effects[effectID] 1756 if effect.effectCategory == const.dgmEffPassive and self.effectCompiler.IsEffectCharacterModifier(effectID): 1757 self.LogInfo('UninstallPilotForItem.effect', itemKey, effectID, dogma_data.get_effect_name(effectID)) 1758 self.StopEffect(effectID, itemKey, False) 1759 1760 if dogmaItem.categoryID == const.categoryDrone and dogmaItem.IsOwnerModifiable(): 1761 dogmaItem.RemoveDroneOwnerModifiers() 1762 1763 def GetCharacterSecurityStatus(self, charID): 1764 return 0.0 1765 1766 def _ApplyPilotSecurityStatusToShip(self, shipID, pilotID): 1767 if pilotID: 1768 secStatus = self.GetCharacterSecurityStatus(pilotID) 1769 else: 1770 secStatus = 0.0 1771 self.SetAttributeValue(shipID, const.attributePilotSecurityStatus, secStatus) 1772 1773 def FindNumUnitsToRepairModule(self, _charid, dmg, hp, itemTypeID, itemID): 1774 consumptionTypeID = const.consumptionTypeIDForModuleRepair 1775 basePrice = GetComputedBasePrice(itemTypeID, 1) 1776 if basePrice == 0.0: 1777 self.SetAttributeValue(itemID, const.attributeDamage, 0) 1778 return None 1779 unitPrice = basePrice / hp 1780 costToRepair = dmg * unitPrice * self.GetAttributeValue(_charid, const.attributeShipBrokenModuleRepairCostMultiplier) 1781 unitsConsumed = math.ceil(costToRepair / evetypes.GetBasePrice(consumptionTypeID)) 1782 return unitsConsumed 1783 1784 def GetAttributesForItem(self, itemID, typeID): 1785 if itemID and dynamicitemattributes.IsDynamicType(typeID): 1786 return sm.GetService('dynamicItemSvc').GetDynamicItemAttributes(itemID) 1787 ret = {} 1788 for attribute in dogma_data.get_type_attributes(typeID): 1789 ret[attribute.attributeID] = attribute.value 1790 1791 return ret 1792 1793 def IsAttributeInIcomingModifiers(self, itemID, toAttributeID, attributeToCheck, operation): 1794 dogmaItem = self.GetItem(itemID) 1795 if not dogmaItem: 1796 return False 1797 toAttrib = dogmaItem.attributes.get(toAttributeID, None) 1798 if not toAttrib: 1799 return False 1800 attributesForOp = toAttrib.incomingModifiers[dgmOperatorIDtoIndex[operation]] 1801 return attributeToCheck in attributesForOp