godma.py (115524B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\godma.py 2 from future.utils import raise_ 3 import cPickle 4 import itertools 5 import math 6 import numbers 7 import random 8 import sys 9 from collections import defaultdict 10 from operator import itemgetter 11 import blue 12 import dogma.data 13 import evetypes 14 import log 15 import telemetry 16 import uthread 17 import utillib 18 from caching import Memoize 19 from carbon.common.script.sys.crowset import CRowset 20 from carbon.common.script.sys.row import Row 21 from carbon.common.script.sys.service import Service 22 from carbon.common.script.sys.serviceConst import ROLE_ANY, ROLE_CONTENT, ROLE_GML, ROLE_PROGRAMMER, ROLE_QA 23 from carbon.common.script.util.timerstuff import ClockThis 24 from carbonui import uiconst 25 from dogma.const import attributeDataTypeInstanceBoolean, attributeDataTypeInstanceInteger, attributeDataTypeTypeAttributeId, attributeDataTypeTypeBoolean, attributeDataTypeTypeDamage, attributeDataTypeTypeGroupId, attributeDataTypeTypeInteger, attributeDataTypeTypeMirror, attributeDataTypeTypeTypeId 26 from eve.client.script.dogma import dogma_signals 27 from eve.client.script.dogma.clientDogmaUtilFunc import WasLaunchingDroneOrFighter 28 from player_messaging.client.ui_message import message_player, prompt_player_fsd_dialog 29 from eve.client.script.ui.inflight.drones.droneSettings import drones_aggressive_setting, drones_focus_fire_setting 30 from eve.client.script.util import godmarowset 31 from eve.common.lib import appConst as const 32 from eve.common.script.net import eveMoniker 33 from eve.common.script.util.inventoryFlagsCommon import GetCapacityAttributeForFlag 34 from eve.common.script.sys import idCheckers 35 from eve.common.script.sys.dbrow import ConstValueExists, LookupConstValue 36 from eve.common.script.sys.eveCfg import IsControllingStructure 37 from eve.common.script.sys.rowset import FilterRowset 38 from eve.common.script.util import eveCommonUtils, inventoryFlagsCommon 39 from eveexceptions import ExceptionEater, UserError 40 from gametime import GetDurationInClient 41 from inventorycommon.util import GetItemVolume, IsShipFittingFlag, IsFittingModule 42 from localization import GetByLabel 43 from functools import reduce 44 MODULE_NOT_OVERLOADED = 0 45 MODULE_OVERLOADED = 1 46 MODULE_PENDING_OVERLOADING = 2 47 MODULE_PENDING_STOPOVERLOADING = 3 48 FLAGS_TO_IGNORE_ITEM_CHANGES = list(eveCommonUtils.Flatten((inventoryFlagsCommon.inventoryFlagData, 49 const.flagCorpSAGs, 50 const.flagDroneBay, 51 const.flagCargo, 52 const.flagHangar, 53 const.flagStructureFuel, 54 const.flagStructureDeed))) 55 56 def remDups(x, y): 57 if x[-1] != y: 58 x.append(y) 59 return x 60 61 62 def GetGodma(): 63 return sm.GetService('godma') 64 65 66 class Godma(Service): 67 __guid__ = 'svc.godma' 68 __exportedcalls__ = {'GetItem': {'role': ROLE_ANY, 69 'fastcall': 1}, 70 'GetType': {'role': ROLE_ANY, 71 'fastcall': 1}, 72 'GetStateManager': [ROLE_ANY], 73 'LogAttributeViaGodma': [ROLE_ANY], 74 'GetDogmaLM': [ROLE_ANY], 75 'GetAttributeValueByID': [ROLE_ANY], 76 'GetFlagsWithValidCapacity': [ROLE_ANY]} 77 __notifyevents__ = ['OnModuleAttributeChange', 78 'OnModuleAttributeChanges', 79 'OnGodmaShipEffect', 80 'OnEffectMessage', 81 'OnDamageMessage', 82 'OnDamageMessages', 83 'OnJamStart', 84 'OnJamEnd', 85 'OnMultiEvent', 86 'ProcessGodmaPrimeLocation', 87 'OnGodmaPrimeItem', 88 'OnLocationAttributeChange', 89 'OnGodmaEffectInfo', 90 'OnGodmaFlushLocation', 91 'OnGodmaFlushLocationProfile', 92 'OnJumpCloneTransitionCompleted', 93 'OnSystemEffectStarted', 94 'OnDroneControlLost', 95 'OnDroneStateChange2', 96 'OnFighterRemovedFromControllerClient', 97 'DoSimClockRebase', 98 'OnSessionReset'] 99 __dependencies__ = ['invCache'] 100 __startupdependencies__ = ['settings'] 101 102 def __init__(self): 103 super(Godma, self).__init__() 104 self.__itemrd = None 105 self.__sublocrd = None 106 self.__subloc_internalrd = None 107 self.Initialize() 108 109 def Run(self, ms): 110 if ms: 111 ms.Seek(0) 112 dump = ms.Read() 113 if len(dump): 114 number = cPickle.loads(str(dump)) 115 if number == 1: 116 self.GetStateManager().ProcessSessionChange(0, session, {}) 117 sm.FavourMe(self.OnModuleAttributeChange) 118 sm.FavourMe(self.OnModuleAttributeChanges) 119 120 def Initialize(self): 121 self.stateManager = None 122 self.activeJams = set() 123 self.michelle = None 124 125 @property 126 def itemrd(self): 127 if self.__itemrd is None: 128 self.__itemrd = sm.GetService('invCache').GetItemHeader() 129 return self.__itemrd 130 131 @property 132 def sublocrd(self): 133 if not self.__sublocrd: 134 columns = [] 135 for key, dbtype, size in list(self.itemrd): 136 if dbtype == 0 and size == 0: 137 continue 138 if key == 'itemID': 139 columns.append(('itemID', const.DBTYPE_STR)) 140 else: 141 columns.append((key, dbtype)) 142 143 self.__sublocrd = blue.DBRowDescriptor(tuple(columns)) 144 self.__sublocrd.virtual = self.itemrd.virtual 145 return self.__sublocrd 146 147 @property 148 def subloc_internalrd(self): 149 if not self.__subloc_internalrd: 150 columns = [] 151 for key, dbtype, size in list(self.itemrd): 152 if dbtype == 0 and size == 0: 153 continue 154 if key in ('quantity', 'customInfo'): 155 continue 156 if key == 'itemID': 157 columns.append(('itemID', const.DBTYPE_STR)) 158 else: 159 columns.append((key, dbtype)) 160 161 self.__subloc_internalrd = blue.DBRowDescriptor(tuple(columns)) 162 return self.__subloc_internalrd 163 164 def OnSessionReset(self): 165 self.ReleaseStateManager() 166 self.Initialize() 167 168 def GetType(self, typeID): 169 return self.GetStateManager().GetType(typeID) 170 171 def TypeHasEffect(self, typeID, effectID): 172 return self.GetStateManager().TypeHasEffect(typeID, effectID) 173 174 def LogAttributeViaGodma(self, itemID, attributeID, reason = None): 175 mask = ROLE_CONTENT | ROLE_QA | ROLE_PROGRAMMER | ROLE_GML 176 if session.role & mask == 0: 177 return 178 return self.GetStateManager().LogAttributeViaStateManager(itemID, attributeID, reason) 179 180 def Stop(self, ms): 181 if ms is not None: 182 ms.Write(cPickle.dumps(1)) 183 184 def OnJamStart(self, sourceBallID, moduleID, targetBallID, jammingType, startTime, duration): 185 jamTuple = (sourceBallID, 186 moduleID, 187 targetBallID, 188 jammingType) 189 self.activeJams.add(jamTuple) 190 uthread.new(self.StopJam, GetDurationInClient(startTime, duration), jamTuple) 191 192 def StopJam(self, duration, jamTuple): 193 blue.pyos.synchro.SleepSim(duration) 194 if jamTuple in self.activeJams: 195 self.activeJams.remove(jamTuple) 196 sm.ScatterEvent('OnJamEnd', *jamTuple) 197 198 def OnJamEnd(self, sourceBallID, moduleID, targetBallID, jammingType): 199 try: 200 self.activeJams.remove((sourceBallID, 201 moduleID, 202 targetBallID, 203 jammingType)) 204 except KeyError: 205 pass 206 207 def OnMultiEvent(self, events): 208 for _, eventBucket in itertools.groupby(sorted(events, key=itemgetter(-1)), key=itemgetter(-1)): 209 self.OnMultiEvent_([ e[0] for e in eventBucket ]) 210 211 def OnMultiEvent_(self, events): 212 if not events: 213 return 214 dmg = [] 215 attr = [] 216 target = [] 217 gsf = [] 218 gfl = [] 219 dastatedict = {'OnDamageMessage': dmg, 220 'OnModuleAttributeChange': attr, 221 'OnTarget': target, 222 'OnGodmaShipEffect': gsf, 223 'OnGodmaFlushLocation': gfl, 224 'OnGodmaFlushLocationProfile': gfl, 225 'OnJamStart': gfl, 226 'OnJamStop': gfl} 227 other = [] 228 for ev in events: 229 dastatedict.get(ev[0], other).append(ev) 230 231 laters = [] 232 for ev in other: 233 if ev[0].startswith('Process'): 234 laters.append(ev) 235 else: 236 sm.ScatterEvent(*ev) 237 238 if len(gsf): 239 self.BroadcastFilteredGSF(gsf) 240 if dmg: 241 sm.ScatterEvent(*('OnDamageMessages', dmg)) 242 if target: 243 target = reduce(remDups, target, [target[0]]) 244 sm.ScatterEvent(*('OnTargets', target)) 245 if attr: 246 self.BroadcastFilteredMAC(attr) 247 for ev in gfl: 248 sm.ScatterEvent(*ev) 249 250 for ev in laters: 251 sm.ChainEvent(*ev) 252 253 def BroadcastFilteredGSF(self, gsf): 254 gsf.sort(lambda x, y: cmp(x[3], y[3])) 255 eventsByID = {} 256 for line in gsf: 257 k = (line[1], line[2]) 258 if k not in eventsByID: 259 eventsByID[k] = [] 260 eventsByID[k].append(line) 261 262 skipCount = 0 263 sendCount = 0 264 for k, lines in eventsByID.iteritems(): 265 skipCount += len(lines) - 1 266 sm.ScatterEvent(*lines[-1]) 267 sendCount += 1 268 269 self.LogInfo('BroadcastFilteredGSF', 'OnGodmaShipEffect - skips', skipCount, 'sends', sendCount) 270 271 def BroadcastFilteredMAC(self, mac): 272 mac.sort(key=lambda x: x[4]) 273 eventsByID = defaultdict(list) 274 for line in mac: 275 eventsByID[line[1], line[2], line[3]].append(line) 276 277 skipCount = 0 278 result = [] 279 for k, lines in eventsByID.iteritems(): 280 skipCount += len(lines) - 1 281 result.append(lines[-1]) 282 283 result.sort(key=lambda x: x[4]) 284 self.LogInfo('BroadcastFilteredMAC', 'OnModuleAttributeChanges - skips', skipCount, 'sends', len(result)) 285 sm.ScatterEvent(*('OnModuleAttributeChanges', result)) 286 287 def GetStateManager(self): 288 if self.stateManager == None: 289 self.stateManager = StateManager(self) 290 return self.stateManager 291 292 def ReleaseStateManager(self): 293 if self.stateManager: 294 self.stateManager.Release() 295 self.stateManager = None 296 297 def GetDogmaLM(self): 298 return self.GetStateManager().GetDogmaLM() 299 300 def GetItem(self, itemID): 301 return self.GetStateManager().GetItem(itemID) 302 303 def IsItemReady(self, itemID): 304 return self.GetStateManager().IsItemReady(itemID) 305 306 def IsPriming(self): 307 return self.GetStateManager().IsPriming() 308 309 def GetAttributeValueByID(self, itemID, attributeID): 310 return self.GetStateManager().GetAttributeValueByID(itemID, attributeID) 311 312 @Memoize 313 def GetFlagsWithValidCapacity(self, itemID, typeID, useOnlyCargo = False): 314 categoryID = evetypes.GetCategoryID(typeID) 315 if not idCheckers.IsShip(categoryID): 316 self.LogError('Cannot get flags with valid capacity for {typeName} ({itemID}), as it is not a Ship but a {categoryName}'.format(typeName=evetypes.GetName(typeID), itemID=itemID, categoryName=evetypes.GetCategoryName(categoryID))) 317 return set() 318 flagsWithValidCapacity = {const.flagCargo} 319 if not useOnlyCargo: 320 flagsToCheck = [const.flagDroneBay, 321 const.flagGeneralMiningHold, 322 const.flagSpecializedGasHold, 323 const.flagSpecializedIceHold, 324 const.flagSpecializedAmmoHold] 325 dogmaIM = sm.GetService('clientDogmaIM') 326 groupID = evetypes.GetGroupID(typeID) 327 for flagID in flagsToCheck: 328 try: 329 capacityAttributeID = GetCapacityAttributeForFlag(flagID) 330 if capacityAttributeID is None: 331 actualCapacity = None 332 if flagID == const.flagCargo: 333 actualCapacity = dogmaIM.GetCapacityForItem(itemID, const.attributeCapacity) 334 if actualCapacity is None: 335 actualCapacity = evetypes.GetCapacity(typeID) 336 else: 337 actualCapacity = dogmaIM.GetCapacityForItem(itemID, capacityAttributeID) 338 if actualCapacity is None: 339 typeAttributes = dogma.data.get_type_attributes(typeID) 340 capacityAttributes = [ x for x in typeAttributes if x.attributeID == capacityAttributeID ] 341 if groupID == const.groupSilo and categoryID == const.categoryStarbase: 342 actualCapacity = self.GetSiloCapacityByItemID(itemID) 343 if actualCapacity is None and len(capacityAttributes): 344 actualCapacity = capacityAttributes[0].value 345 if actualCapacity is None: 346 actualCapacity = dogma.data.get_attribute_default_value(capacityAttributeID) 347 if actualCapacity: 348 flagsWithValidCapacity.add(flagID) 349 except Exception as exc: 350 self.LogException('Failed to get capacity for {typeName} ({itemID}) and flag {flag}: {exc}'.format(typeName=evetypes.GetName(typeID), itemID=itemID, flag=flagID, exc=exc)) 351 352 return flagsWithValidCapacity 353 354 def OnModuleAttributeChange(self, *args): 355 self.GetStateManager().OnModuleAttributeChange(*args) 356 357 def OnModuleAttributeChanges(self, changes): 358 self.GetStateManager().OnModuleAttributeChanges(changes) 359 360 def OnGodmaShipEffect(self, *args, **kwargs): 361 self.GetStateManager().OnGodmaShipEffect(*args, **kwargs) 362 363 def ProcessGodmaPrimeLocation(self, locationID, data): 364 self.GetStateManager().ProcessGodmaPrimeLocation(locationID, data) 365 366 def OnLocationAttributeChange(self, ownerID, locationID, itemKey, attributeID, t, value, oldValue): 367 self.GetStateManager().OnLocationAttributeChange(ownerID, locationID, itemKey, attributeID, t, value, oldValue) 368 369 def OnGodmaEffectInfo(self, *args, **kwargs): 370 self.GetStateManager().OnGodmaEffectInfo(*args, **kwargs) 371 372 def OnGodmaPrimeItem(self, locationID, row): 373 stateMgr = self.GetStateManager() 374 stateMgr.OnGodmaPrimeItem(locationID, row) 375 if stateMgr.IsSubLocation(row.itemID): 376 rd = self.sublocrd 377 locationID, flag, typeID = row.itemID 378 quantity = row.attributes[const.attributeQuantity] 379 self.LogInfo('Godma:OnGodmaPrimeItem - QUANTITY', quantity) 380 ownerID = None 381 change = {const.ixLocationID: None, 382 const.ixFlag: None} 383 else: 384 rd = self.itemrd 385 flag = row.invItem.flagID 386 typeID = row.invItem.typeID 387 quantity = row.invItem.quantity 388 ownerID = row.invItem.ownerID 389 change = {} 390 stackSize = max(1, quantity) 391 location = None 392 groupID = evetypes.GetGroupID(typeID) 393 categoryID = evetypes.GetCategoryID(typeID) 394 item = blue.DBRow(rd, [row.itemID, 395 typeID, 396 ownerID, 397 locationID, 398 flag, 399 quantity, 400 groupID, 401 categoryID, 402 stackSize]) 403 sm.ScatterEvent('OnItemChange', item, change, location) 404 405 def OnGodmaFlushLocation(self, locationID): 406 self.GetStateManager().OnGodmaFlushLocation(locationID) 407 408 def OnGodmaFlushLocationProfile(self, locationID, profileID): 409 self.GetStateManager().OnGodmaFlushLocationProfile(locationID, profileID) 410 411 @telemetry.ZONE_METHOD 412 def DamageMessageGrouped(self, turretSets, hitQuality): 413 if hitQuality <= 0: 414 for t in turretSets: 415 t.SetShotMissed(True) 416 417 elif hitQuality >= 3: 418 for t in turretSets: 419 t.SetShotMissed(False) 420 421 else: 422 missPct = [0.5, 0.25][hitQuality - 1] 423 for t in turretSets: 424 t.SetShotMissed(random.random() < missPct) 425 426 @telemetry.ZONE_METHOD 427 def DamageMessageTurretPropogation(self, shipID, ship, hitQuality, args, banked): 428 weaponID = args.get('weapon', 0) 429 canGetBanks = banked and shipID == session.shipid 430 if getattr(ship, 'modules', None): 431 minQueue = None 432 minTime = None 433 minTimeID = None 434 minTimeTurrets = [] 435 shotTimeVariance = 0.0 436 for modID, module in ship.modules.iteritems(): 437 if hasattr(module, 'GetTurretSets'): 438 turretSets = module.GetTurretSets() 439 if not turretSets: 440 continue 441 shooting = module.isShooting 442 targetMatch = shooting 443 shotTimeVariance = max(shotTimeVariance, turretSets[0].GetShotTimeVariance()) 444 if shooting and 'target' in args: 445 targetMatch = module.targetID == args['target'] 446 typeMatch = shooting 447 if shooting and weaponID is not 0: 448 typeMatch = module.turretTypeID == weaponID 449 if shooting and targetMatch and typeMatch: 450 queueSize = turretSets[0].MissQueueSize() 451 shotTime = turretSets[0].GetLastShotTime() 452 turretID = modID 453 if canGetBanks: 454 turretID = self.GetStateManager().dogmaLocation.IsInWeaponBank(shipID, modID) or modID 455 if turretID == minTimeID: 456 minTimeTurrets.append(module) 457 elif minQueue is None or queueSize < minQueue: 458 minQueue = queueSize 459 minTime = shotTime 460 minTimeID = turretID 461 minTimeTurrets = [module] 462 elif queueSize == minQueue and (minTime is None or shotTime < minTime): 463 minTime = shotTime 464 minTimeID = turretID 465 minTimeTurrets = [module] 466 elif banked: 467 if minTime is None or shotTime < minTime + shotTimeVariance: 468 if minTime is None: 469 minTime = shotTime 470 else: 471 minTime = min(shotTime, minTime) 472 minTimeID = turretID 473 minTimeTurrets.append(module) 474 elif minQueue is None or queueSize < minQueue: 475 minQueue = queueSize 476 minTime = shotTime 477 minTimeTurrets = [module] 478 elif queueSize == minQueue and (minTime is None or shotTime < minTime): 479 minTime = shotTime 480 minTimeTurrets = [module] 481 482 allTurretSets = [] 483 for turret in minTimeTurrets: 484 for turretSet in turret.GetTurretSets(): 485 if canGetBanks or minTime + shotTimeVariance >= turretSet.GetLastShotTime(): 486 allTurretSets.append(turretSet) 487 488 self.DamageMessageGrouped(allTurretSets, hitQuality) 489 490 @telemetry.ZONE_METHOD 491 def OnDamageMessage(self, damageMessagesArgs): 492 if self.michelle is None: 493 self.michelle = sm.GetService('michelle') 494 try: 495 hitQuality = damageMessagesArgs.get('hitQuality', 5) 496 banked = damageMessagesArgs.get('isBanked', False) 497 if 'observed' in damageMessagesArgs: 498 sourceID = damageMessagesArgs['source'] 499 ship = self.michelle.GetBall(sourceID) 500 if sourceID and ship: 501 self.DamageMessageTurretPropogation(sourceID, ship, hitQuality, damageMessagesArgs, banked) 502 elif 'target' in damageMessagesArgs: 503 ship = self.michelle.GetBall(session.shipid) 504 if ship and hasattr(ship, 'turrets'): 505 self.DamageMessageTurretPropogation(session.shipid, ship, hitQuality, damageMessagesArgs, banked) 506 elif 'owner' in damageMessagesArgs: 507 ownerData, ownerID = damageMessagesArgs['owner'] 508 if ownerData == const.UE_OWNERID: 509 targetSvc = sm.GetService('target') 510 ship = None 511 shipID = targetSvc.ownerToShipIDCache.get(ownerID, None) 512 if shipID is not None: 513 ship = self.michelle.GetBall(shipID) 514 if shipID and ship: 515 self.DamageMessageTurretPropogation(shipID, ship, hitQuality, damageMessagesArgs, banked) 516 elif 'source' in damageMessagesArgs: 517 shipID = damageMessagesArgs['source'] 518 ship = self.michelle.GetBall(shipID) 519 if hasattr(ship, 'turrets'): 520 self.DamageMessageTurretPropogation(shipID, ship, hitQuality, damageMessagesArgs, banked) 521 elif hasattr(ship, 'model') and hasattr(ship.model, 'turretSets'): 522 self.DamageMessageGrouped(ship.model.turretSets, hitQuality) 523 except: 524 log.LogException('Error setting hit/miss state on a turret') 525 526 if not settings.user.ui.Get('damageMessages', 1): 527 return 528 if 'damage' in damageMessagesArgs and damageMessagesArgs['damage'] == 0.0 and not settings.user.ui.Get('damageMessagesNoDamage', 1): 529 return 530 if 'owner' in damageMessagesArgs or 'source' in damageMessagesArgs: 531 sm.ScatterEvent('OnShipDamage', damageMessagesArgs.get('damageTypes', {})) 532 if not settings.user.ui.Get('damageMessagesEnemy', 1): 533 return 534 elif not settings.user.ui.Get('damageMessagesMine', 1): 535 return 536 sm.GetService('logger').AddCombatMessageFromDict(damageMessagesArgs) 537 538 @telemetry.ZONE_METHOD 539 def OnDamageMessages(self, dmgmsgs): 540 if not settings.user.ui.Get('damageMessages', 1): 541 return 542 for msg in dmgmsgs: 543 self.OnDamageMessage(*msg[1:]) 544 545 def OnEffectMessage(self, msgkey, args): 546 msg = cfg.GetMessage(msgkey, args) 547 message_player(msg.text) 548 549 def ProcessSessionChange(self, isRemote, session, change): 550 self.GetStateManager().ProcessSessionChange(isRemote, session, change) 551 552 def OnItemChange(self, *args): 553 try: 554 self.GetStateManager().OnItemChange(*args) 555 finally: 556 sm.GetService('clientDogmaIM').GodmaItemChanged(*args) 557 558 def OnDroneControlLost(self, droneID): 559 if self.stateManager: 560 self.stateManager.OnDroneControlLost(droneID) 561 562 def OnDroneStateChange2(self, droneID, oldState, newState): 563 if self.stateManager: 564 self.stateManager.OnDroneStateChange2(droneID, oldState, newState) 565 566 def OnFighterRemovedFromControllerClient(self, fighterID): 567 if self.stateManager: 568 self.stateManager.OnFighterRemovedFromControllerClient(fighterID) 569 570 def OverloadRack(self, moduleID): 571 if moduleID is None: 572 return 573 if self.stateManager: 574 self.stateManager.OverloadRack(moduleID) 575 576 def StopOverloadRack(self, moduleID): 577 if self.stateManager: 578 self.stateManager.StopOverloadRack(moduleID) 579 580 def OnJumpCloneTransitionCompleted(self): 581 if self.stateManager: 582 self.stateManager.OnJumpCloneTransitionCompleted() 583 584 def GetTypeAttribute(self, typeID, attributeID, defaultValue = None): 585 for typeAttribute in dogma.data.get_type_attributes(typeID): 586 if typeAttribute.attributeID == attributeID: 587 return typeAttribute.value 588 589 return defaultValue 590 591 def GetTypeAttribute2(self, typeID, attributeID): 592 val = self.GetTypeAttribute(typeID, attributeID) 593 if val is None and dogma.data.is_attribute(attributeID): 594 val = dogma.data.get_attribute_default_value(attributeID) 595 return val 596 597 def CheckSkillRequirementsForType(self, typeID): 598 skillSvc = sm.StartService('skills') 599 requiredSkills = skillSvc.GetRequiredSkills(typeID) 600 for skillTypeID, level in requiredSkills.iteritems(): 601 skillInfo = skillSvc.GetSkill(skillTypeID) 602 if skillInfo and skillInfo.effectiveSkillLevel >= level: 603 continue 604 return False 605 606 return True 607 608 def GetFlagsCompatibleWithChargeType(self, chargeTypeID): 609 chargeGroupID = self.GetType(chargeTypeID).groupID 610 chargeChargeSize = self.GetTypeAttribute(chargeTypeID, const.attributeChargeSize) 611 if not hasattr(self, 'chargeGroupAttrs'): 612 self.chargeGroupAttrs = [] 613 for attribute in dogma.data.get_charge_group_attributes(): 614 self.chargeGroupAttrs.append(attribute.name) 615 616 shipItem = self.GetStateManager().GetItem(session.shipid) 617 flags = [] 618 for module in shipItem.modules: 619 groupCompatible = False 620 for cgaID in self.chargeGroupAttrs: 621 if cgaID in self.stateManager.attributesByItemAttribute.get(module.itemID, {}): 622 if self.stateManager.GetAttribute(module.itemID, cgaID) == chargeGroupID: 623 groupCompatible = True 624 continue 625 626 if groupCompatible is False: 627 continue 628 if 'chargeSize' in self.stateManager.attributesByItemAttribute.get(module.itemID, {}): 629 if self.stateManager.GetAttribute(module.itemID, 'chargeSize') == chargeChargeSize: 630 flags.append(module.flagID) 631 else: 632 flags.append(module.flagID) 633 634 return flags 635 636 def OnSkillPointsRemovedDueToShipLoss(self, skillTypeID, skillPoints, shipTypeID): 637 entry = utillib.KeyVal(skillTypeID=skillTypeID, skillPoints=skillPoints, shipTypeID=shipTypeID) 638 settings.char.generic.Set('skillLossNotification', entry) 639 640 def OnSystemEffectStarted(self, typeIDs): 641 body = '' 642 for typeID in typeIDs: 643 if len(body) > 0: 644 body += '<br><br>' 645 body += evetypes.GetDescription(typeID) 646 647 prompt_player_fsd_dialog('CustomNotify', {'notify': body}) 648 649 def DoSimClockRebase(self, times): 650 if self.stateManager: 651 self.stateManager.DoSimClockRebase(times) 652 653 def GetPowerEffectForType(self, typeID): 654 powerEffects = [const.effectHiPower, 655 const.effectMedPower, 656 const.effectLoPower, 657 const.effectRigSlot, 658 const.effectSubSystem, 659 const.effectServiceSlot] 660 for effect in dogma.data.get_type_effects(typeID): 661 if effect.effectID in powerEffects: 662 return effect.effectID 663 664 def ShipOnlineModules(self): 665 self.GetDogmaLM().ShipOnlineModules() 666 667 668 class TypeWrapper(object): 669 670 def __init__(self, statemanager, typeID): 671 self.statemanager = statemanager 672 self.typeID = typeID 673 self.categoryID = evetypes.GetCategoryID(typeID) 674 675 def __getattr__(self, attributeName): 676 result = self.__InnerGetattr(attributeName) 677 setattr(self, attributeName, result) 678 return result 679 680 def __InnerGetattr(self, attributeName): 681 if attributeName == 'itemID': 682 return 683 typeID = self.typeID 684 if attributeName == 'displayAttributes': 685 rowDescriptor = blue.DBRowDescriptor((('displayName', const.DBTYPE_STR), 686 ('value', const.DBTYPE_R5), 687 ('unitID', const.DBTYPE_UI1), 688 ('iconID', const.DBTYPE_I4), 689 ('attributeID', const.DBTYPE_I2))) 690 rowset = CRowset(rowDescriptor, []) 691 for typeAttribute in dogma.data.get_type_attributes(typeID): 692 attrib = self.statemanager.attributesByID[typeAttribute.attributeID] 693 name = dogma.data.get_attribute_display_name(typeAttribute.attributeID) or attrib.name 694 if attrib.dataType == attributeDataTypeTypeMirror: 695 val = evetypes.GetAttributeForType(typeID, attrib.name) 696 elif typeAttribute.value is not None: 697 val = typeAttribute.value 698 else: 699 val = attrib.defaultValue 700 rowset.InsertNew([name, 701 val, 702 getattr(attrib, 'unitID', None), 703 getattr(attrib, 'iconID', None), 704 typeAttribute.attributeID]) 705 706 return rowset 707 if attributeName == 'effects': 708 self.statemanager.CacheTypeEffects(typeID) 709 return self.statemanager.effectsByType[typeID] 710 if attributeName in self.statemanager.attributesByName: 711 value = None 712 attribute = self.statemanager.attributesByName[attributeName] 713 if attribute.dataType == attributeDataTypeTypeMirror: 714 value = evetypes.GetAttributeForType(typeID, attributeName) 715 attributeID = attribute.attributeID 716 for typeAttribute in dogma.data.get_type_attributes(typeID): 717 if typeAttribute.attributeID == attributeID: 718 value = typeAttribute.value 719 break 720 721 if value is None: 722 value = self.statemanager.attributesByName[attributeName].defaultValue 723 if dogma.data.get_attribute_data_type(attributeID) in (attributeDataTypeInstanceBoolean, 724 attributeDataTypeInstanceInteger, 725 attributeDataTypeTypeBoolean, 726 attributeDataTypeTypeInteger, 727 attributeDataTypeTypeAttributeId, 728 attributeDataTypeTypeTypeId, 729 attributeDataTypeTypeGroupId): 730 return int(value) 731 return value 732 if attributeName == 'name': 733 return evetypes.GetName(typeID) 734 return evetypes.GetAttributeForType(typeID, attributeName) 735 736 def AttributeExists(self, attributeName): 737 attributeID = self.statemanager.attributesByName[attributeName].attributeID 738 for typeAttribute in dogma.data.get_type_attributes(self.typeID): 739 if typeAttribute.attributeID == attributeID: 740 return 1 741 742 return 0 743 744 def __str__(self): 745 return self.typeName 746 747 def __repr__(self): 748 return '<godma-type typeID="%d">' % self.typeID 749 750 751 class ItemWrapper(): 752 __guid__ = 'godma.ItemWrapper' 753 754 def __init__(self, statemanager, header, dbrow): 755 self.statemanager = statemanager 756 self.dbrow = dbrow 757 self.header = header 758 self.defaultEffectName = None 759 760 def __str__(self): 761 try: 762 tname = self.name 763 except: 764 sys.exc_clear() 765 tname = '<Unknown type name>' 766 767 try: 768 name = self.name 769 if tname != name: 770 name = tname + ':' + name 771 except: 772 sys.exc_clear() 773 name = '' 774 775 name += ' (row data: %s)' % str(self.dbrow) 776 return name 777 778 def __getattr__(self, attribute): 779 d = self.__dict__ 780 if attribute == 'defaultEffect': 781 if d['defaultEffectName'] is None: 782 for effect in d['statemanager'].effectsByItemEffect.get(self.itemID, {}).itervalues(): 783 if effect.isDefault: 784 d['defaultEffectName'] = effect.effectName 785 break 786 787 effectName = d['defaultEffectName'] 788 return d['statemanager'].effectsByItemEffect[self.itemID].get(effectName, None) 789 if attribute == 'line': 790 return list(d['dbrow']) 791 if attribute in d['dbrow'].__columns__: 792 return d['dbrow'][attribute] 793 if attribute in d['dbrow'].__class__.__dict__: 794 return getattr(d['dbrow'], attribute) 795 if attribute in self.__class__.__dict__: 796 return self.__class__.__dict__[attribute] 797 if attribute.startswith('__') and attribute.endswith('__'): 798 raise AttributeError('ItemWrapper has no %s' % attribute) 799 else: 800 if attribute == 'inventory': 801 return d['statemanager'].invByID.get(self.itemID, None) 802 return d['statemanager'].GetAttribute(self.itemID, attribute) 803 804 def __nonzero__(self): 805 return True 806 807 def GetChargeEffect(self, typeID): 808 eff = self.statemanager.GetDefaultEffect(typeID) 809 if eff is None: 810 return 811 eff = eff.__class__(self.statemanager, eff.header, eff.dbrow) 812 idx = eff.header.Index('itemID') 813 eff[idx] = self.itemID 814 return eff 815 816 def IsFitted(self): 817 return IsShipFittingFlag(self.flagID) 818 819 def GetSlotOccupants(self, flag): 820 return [ item for item in self.statemanager.invitems.itervalues() if item.locationID == self.itemID and item.flagID == flag ] 821 822 def GetCapacity(self, flag = None): 823 used = None 824 capacity = 0 825 contents = [] 826 if self.categoryID in (const.categoryShip, const.categoryStructure): 827 if IsShipFittingFlag(flag): 828 occupants = self.GetSlotOccupants(flag) 829 modules = filter(lambda x: IsFittingModule(x.categoryID), occupants) 830 if len(modules): 831 capacity = self.statemanager.GetItem(modules[0].itemID).capacity 832 else: 833 capacity = 0 834 sublocation = self.statemanager.GetSubLocation(self.itemID, flag) 835 if sublocation is not None: 836 contents = [sublocation] 837 else: 838 occupants = self.GetSlotOccupants(flag) 839 contents = filter(lambda x: x.categoryID == const.categoryCharge, occupants) 840 elif flag == const.flagDroneBay: 841 contents = self.statemanager.invByID[self.itemID].ListDroneBay() 842 capacity = self.droneCapacity 843 elif flag is None or flag == const.flagCargo: 844 contents = self.statemanager.invByID[self.itemID].ListCargo() 845 capacity = self.capacity 846 elif flag == const.flagShipHangar: 847 if self.itemID in self.statemanager.invByID: 848 inv = self.statemanager.invByID[self.itemID] 849 elif self.statemanager.godma.invCache.IsInventoryPrimedAndListed(self.itemID): 850 inv = sm.GetService('invCache').GetInventoryFromId(self.itemID) 851 contents = inv.List(flag) 852 capacity = self.shipMaintenanceBayCapacity 853 elif flag in const.flagCorpSAGsAndHangar: 854 if self.itemID in self.statemanager.invByID: 855 inv = self.statemanager.invByID[self.itemID] 856 elif self.statemanager.godma.invCache.IsInventoryPrimedAndListed(self.itemID): 857 inv = sm.GetService('invCache').GetInventoryFromId(self.itemID) 858 contents = [ item for item in inv.List() if item.flagID in const.flagCorpSAGsAndHangar ] 859 capacity = self.corporateHangarCapacity 860 elif flag in inventoryFlagsCommon.inventoryFlagData: 861 capacity = self.statemanager.GetAttributeValueByID(self.itemID, inventoryFlagsCommon.inventoryFlagData[flag]['attribute']) 862 if self.itemID in self.statemanager.invByID: 863 inv = self.statemanager.invByID[self.itemID] 864 elif self.statemanager.godma.invCache.IsInventoryPrimedAndListed(self.itemID): 865 inv = sm.GetService('invCache').GetInventoryFromId(self.itemID) 866 contents = [ item for item in inv.List() if item.flagID == flag ] 867 else: 868 raise RuntimeError('BadSlotForCapacity', flag) 869 elif self.categoryID == const.categoryStarbase: 870 capacity = self.capacity 871 if self.groupID in (const.groupMobileMissileSentry, const.groupMobileProjectileSentry, const.groupMobileHybridSentry): 872 contents = self.sublocations 873 else: 874 contents = sm.GetService('invCache').GetInventoryFromId(self.itemID).List(flag) 875 if used is None: 876 used = 0 877 for item in contents: 878 if flag and item.flagID != flag: 879 continue 880 vol = GetItemVolume(item) 881 if vol > 0: 882 used = used + vol 883 884 return Row(['capacity', 'used'], [capacity, used]) 885 886 887 class EffectWrapper(): 888 __guid__ = 'godma.EffectWrapper' 889 890 def __init__(self, statemanager, header, dbrow): 891 d = self.__dict__ 892 d['statemanager'] = statemanager 893 d['dbrow'] = dbrow 894 d['header'] = header 895 d['defaultEffectName'] = None 896 897 def __getattr__(self, attribute): 898 d = self.__dict__ 899 if attribute in d: 900 return d[attribute] 901 try: 902 return d['dbrow'][attribute] 903 except: 904 sys.exc_clear() 905 ret = d['statemanager'].GetEffectAttributeEx(self, d['dbrow'].itemID, attribute) 906 if ret is None: 907 raise_(AttributeError, attribute) 908 return ret 909 910 def __setattr__(self, attr, value): 911 d = self.__dict__ 912 if attr in d['header'].Keys(): 913 d['dbrow'][attr] = value 914 else: 915 d[attr] = value 916 917 def __setitem__(self, idx, value): 918 self.dbrow[idx] = value 919 920 def Activate(self, target = None, repeat = 0): 921 if self.isActive and self.duration <= 0: 922 self.statemanager.godma.LogError('Module effect already active', self.effectName, self.itemID) 923 return 924 elif not session.IsItSafe(): 925 return 926 elif self.effectCategory == const.dgmEffOverload: 927 return self.statemanager.Overload(self.itemID, self.effectID) 928 else: 929 return self.statemanager.Activate(self.itemID, self.effectName, target, repeat) 930 931 def Deactivate(self): 932 if not self.isActive: 933 self.statemanager.godma.LogError('Module effect not active', self.effectName, self.itemID) 934 return 935 elif self.effectCategory == const.dgmEffOverload: 936 return self.statemanager.StopOverload(self.itemID, self.effectID) 937 else: 938 return self.statemanager.Deactivate(self.itemID, self.effectName) 939 940 def Toggle(self): 941 if self.isActive: 942 return self.Deactivate() 943 else: 944 return self.Activate() 945 946 947 ENV_IDX_SELF = 0 948 ENV_IDX_CHAR = 1 949 ENV_IDX_SHIP = 2 950 ENV_IDX_TARGET = 3 951 ENV_IDX_OTHER = 4 952 ENV_IDX_AREA = 5 953 ENV_IDX_EFFECT = 6 954 ENV_IDX_LINE = 7 955 956 class StateManager(): 957 __guid__ = 'godma.StateManager' 958 __effectheader__ = blue.DBRowDescriptor((('itemID', const.DBTYPE_STR), 959 ('effectID', const.DBTYPE_I4), 960 ('effectName', const.DBTYPE_STR), 961 ('effectCategory', const.DBTYPE_I2), 962 ('isDefault', const.DBTYPE_BOOL), 963 ('isActive', const.DBTYPE_BOOL), 964 ('isOffensive', const.DBTYPE_BOOL), 965 ('isAssistance', const.DBTYPE_BOOL), 966 ('description', const.DBTYPE_STR), 967 ('trackingSpeedAttributeID', const.DBTYPE_I2), 968 ('rangeAttributeID', const.DBTYPE_I2), 969 ('falloffAttributeID', const.DBTYPE_I4), 970 ('dischargeAttributeID', const.DBTYPE_I2), 971 ('durationAttributeID', const.DBTYPE_I2), 972 ('iconID', const.DBTYPE_I4), 973 ('targetID', const.DBTYPE_I8), 974 ('startTime', const.DBTYPE_FILETIME), 975 ('duration', const.DBTYPE_I4), 976 ('repeat', const.DBTYPE_I4), 977 ('isDeactivating', const.DBTYPE_BOOL))) 978 979 def __init__(self, godma): 980 self.godma = godma 981 self.activatedLocation = {} 982 self.invitems = godmarowset.GodmaIndexedRowset(godma.itemrd, 'itemID', rowClass=self.GetItemWrapper) 983 self.sublocationsByKey = godmarowset.GodmaIndexedRowset(self.godma.subloc_internalrd, 'itemID', rowClass=self.GetSubLocationWrapper) 984 self.sublocationsByLocationFlag = godmarowset.GodmaRowset(self.godma.subloc_internalrd, [], rowClass=self.GetSubLocationWrapper).Filter('locationID', 'flagID') 985 self.itemsByLocationID = {} 986 self.inited = False 987 self.priming = False 988 self.primingChannel = None 989 self.attributesByName = None 990 self.attributesByID = None 991 self.effectsByType = {} 992 self.defaultEffectsByType = {} 993 self.chargedAttributeTauCaps = {} 994 self.droneSettingAttributes = {} 995 self.lastAttributeChange = {} 996 self.attributesByItemAttribute = {} 997 self.effectsByItemEffect = {} 998 self.chargedAttributesByItemAttribute = {} 999 self.modulesBeingRepaired = {} 1000 self.slaveModulesByMasterModule = {} 1001 self.activatingEffects = set() 1002 self.chargeTypeByModule = {} 1003 self.lastStopTimesByItemID = {} 1004 self.invByID = {} 1005 self.dogmaLM = None 1006 self.pending = {} 1007 self.pendingOverloading = set() 1008 self.pendingStopOverloading = set() 1009 self.itemKeyChannels = {} 1010 self.requiredEffects = {const.effectOnline: True, 1011 const.effectHiPower: True, 1012 const.effectMedPower: True, 1013 const.effectLoPower: True, 1014 const.effectRigSlot: True} 1015 self.electronicAttributes = {const.attributeScanGravimetricStrengthBonus: const.attributeScanGravimetricStrength, 1016 const.attributeScanLadarStrengthBonus: const.attributeScanLadarStrength, 1017 const.attributeScanMagnetometricStrengthBonus: const.attributeScanMagnetometricStrength, 1018 const.attributeScanRadarStrengthBonus: const.attributeScanRadarStrength} 1019 self.propulsionAttributes = {const.attributePropulsionFusionStrengthBonus: const.attributePropulsionFusionStrength, 1020 const.attributePropulsionIonStrengthBonus: const.attributePropulsionIonStrength, 1021 const.attributePropulsionMagpulseStrengthBonus: const.attributePropulsionMagpulseStrength, 1022 const.attributePropulsionPlasmaStrengthBonus: const.attributePropulsionPlasmaStrength} 1023 self.dogmaLocation = None 1024 1025 def Release(self): 1026 self.dogmaLM = None 1027 self.invitems = None 1028 self.sublocationsByLocationFlag = None 1029 self.sublocationsByKey = None 1030 self.itemsByLocationID = None 1031 self.godma = None 1032 self.effectsByItemEffect = None 1033 self.defaultEffectsByType = None 1034 self.attributesByItemAttribute = None 1035 1036 def IsLocationLoaded(self, locationID): 1037 return locationID in self.itemsByLocationID 1038 1039 def IsItemLoaded(self, itemKey): 1040 return itemKey in self.invitems or itemKey in self.sublocationsByKey 1041 1042 def GetDogmaLM(self): 1043 if not self.dogmaLM: 1044 self.dogmaLM = eveMoniker.CharGetDogmaLocation() 1045 return self.dogmaLM 1046 1047 def LogAttributeViaStateManager(self, itemID, attributeID, reason = None): 1048 if reason is None: 1049 reason = '' 1050 else: 1051 reason += ' ' 1052 val = None 1053 try: 1054 val = self.GetAttribute(itemID, dogma.data.get_attribute_name(attributeID)) 1055 except: 1056 sys.exc_clear() 1057 val = 'screwed' 1058 1059 if isinstance(itemID, basestring): 1060 dogmaLocation = sm.GetService('clientDogmaIM').GetFittingDogmaLocation() 1061 else: 1062 dogmaLocation = self.GetDogmaLM() 1063 logStuff = dogmaLocation.FullyDescribeAttribute(itemID, attributeID, reason + 'Value on client was %s.' % val) 1064 for line in logStuff: 1065 self.godma.LogError(line) 1066 1067 def ProcessSessionChange(self, isRemote, session, change): 1068 if session.charid: 1069 if 'stationid' in change or 'solarsystemid' in change or 'shipid' in change or 'charid' in change or 'structureid' in change: 1070 self.priming = True 1071 keepList = [session.charid, session.shipid] 1072 if 'solarsystemid' not in change: 1073 keepList.append(session.solarsystemid) 1074 for invItem in self.invitems.itervalues(): 1075 if invItem.locationID == session.solarsystemid and invItem.categoryID == const.categoryStarbase: 1076 keepList.append(invItem.itemID) 1077 1078 self.PurgeInventories(keepList, skipCapacityEvent=True) 1079 self.dogmaLM = None 1080 self.godma.LogInfo('ProcessSessionChange: Prime (pre)') 1081 self.lastAttributeChange = {} 1082 if session.shipid is None: 1083 self.Prime() 1084 else: 1085 oldAttribs = {} 1086 try: 1087 del self.invByID[session.shipid] 1088 oldAttribs = self.attributesByItemAttribute[session.shipid].copy() 1089 except KeyError: 1090 pass 1091 1092 self.Prime() 1093 if session.shipid in self.attributesByItemAttribute and self.attributesByItemAttribute[session.shipid]: 1094 newAttribs = self.attributesByItemAttribute[session.shipid] 1095 attribChanges = [] 1096 shipItem = self.invitems[session.shipid] 1097 for introducedAttribute in set(newAttribs).difference(set(oldAttribs)): 1098 attribChanges.append((introducedAttribute, shipItem, newAttribs[introducedAttribute])) 1099 1100 for commonAttribute in set(newAttribs).intersection(set(oldAttribs)): 1101 if oldAttribs[commonAttribute] != newAttribs[commonAttribute]: 1102 attribChanges.append((commonAttribute, shipItem, newAttribs[commonAttribute])) 1103 1104 if attribChanges: 1105 sm.ScatterEvent('OnAttributes', attribChanges) 1106 self.ClearPendingOverloadingFlags() 1107 self.godma.LogInfo('ProcessSessionChange: Prime (post)') 1108 self.priming = False 1109 if session.stationid or session.solarsystemid: 1110 dogmaLM = self.GetDogmaLM() 1111 dogmaLM.Bind() 1112 for moduleID in self.modulesBeingRepaired.keys(): 1113 self.StopRepairModule(moduleID) 1114 1115 if 'shipid' in change: 1116 self.chargeTypeByModule = {} 1117 else: 1118 self.dogmaLM = None 1119 1120 def OnItemChange(self, item, change, location): 1121 if self.IsSubLocation(item.itemID): 1122 return 1123 if item.locationID == session.structureid and not IsControllingStructure(): 1124 if not (const.ixLocationID in change and change[const.ixLocationID] == session.shipid): 1125 return 1126 if item.itemID in self.chargeTypeByModule and const.ixFlag in change: 1127 del self.chargeTypeByModule[item.itemID] 1128 self.godma.LogInfo('OnItemChange', item, change) 1129 if const.ixLocationID in change or const.ixStackSize in change and item.stacksize == 0: 1130 flag = item.flagID 1131 oFlagOk = IsShipFittingFlag(flag) or flag == const.flagPilot 1132 wasLaunchingDronesOrFighters = False 1133 if const.ixFlag in change: 1134 cFlag = change[const.ixFlag] 1135 wasLaunchingDronesOrFighters = self.WasLaunchingDroneOrFighter(item, change) 1136 cFlagOk = IsShipFittingFlag(cFlag) or cFlag == const.flagPilot or wasLaunchingDronesOrFighters 1137 if not (oFlagOk or cFlagOk or wasLaunchingDronesOrFighters): 1138 return 1139 elif not oFlagOk: 1140 return 1141 if item.locationID in self.invByID and not (const.ixStackSize in change and item.stacksize == 0): 1142 self.UpdateItem(item) 1143 if item.itemID in self.invitems: 1144 sm.ScatterEvent('OnGodmaItemChange', self.invitems[item.itemID], change) 1145 elif wasLaunchingDronesOrFighters: 1146 self.UpdateItem(item) 1147 elif item.itemID in self.invitems: 1148 oldLocationID = change.get(const.ixLocationID, item.locationID) 1149 if item.itemID not in self.invByID: 1150 self.invitems[item.itemID].locationID = item.locationID 1151 sm.ScatterEvent('OnGodmaItemChange', self.invitems[item.itemID], change) 1152 del self.invitems[item.itemID] 1153 else: 1154 self.invitems[item.itemID].locationID = item.locationID 1155 self.invitems[item.itemID].flagID = item.flagID 1156 sm.ScatterEvent('OnGodmaItemChange', self.invitems[item.itemID], change) 1157 if oldLocationID != item.locationID: 1158 if oldLocationID in self.itemsByLocationID: 1159 self.itemsByLocationID[oldLocationID].discard(item.itemID) 1160 if item.locationID in self.itemsByLocationID: 1161 self.itemsByLocationID[item.locationID].add(item.itemID) 1162 elif item.locationID in self.invByID: 1163 if item.itemID in self.invitems: 1164 if const.ixFlag in change and not (IsShipFittingFlag(item.flagID) or item.flagID in (const.flagSkill, const.flagSkillInTraining, const.flagBooster)): 1165 sm.ScatterEvent('OnGodmaItemChange', item, change) 1166 del self.invitems[item.itemID] 1167 if item.locationID in self.itemsByLocationID: 1168 self.itemsByLocationID[item.locationID].remove(item.itemID) 1169 return 1170 if item.flagID in FLAGS_TO_IGNORE_ITEM_CHANGES: 1171 return 1172 if item.groupID == const.groupCapsule and item.ownerID == session.charid: 1173 if item.locationID == session.locationid and item.flagID == const.flagNone or item.locationID == session.shipid and item.flagID == const.flagCapsule: 1174 return 1175 if len(change) == 1 and const.ixStackSize in change and not (item.singleton and change[const.ixStackSize] > 0): 1176 self.UpdateItem(item, dontGetInfo=1) 1177 else: 1178 self.UpdateItem(item) 1179 if item.itemID in self.invitems: 1180 sm.ScatterEvent('OnGodmaItemChange', self.invitems[item.itemID], change) 1181 1182 def WasLaunchingDroneOrFighter(self, item, change): 1183 return WasLaunchingDroneOrFighter(item, change, session.solarsystemid) 1184 1185 def ProcessGodmaPrimeLocation(self, locationID, data): 1186 self.godma.LogInfo('ProcessGodmaPrimeLocation', locationID, 'rows:', len(data)) 1187 self.itemsByLocationID[locationID] = set() 1188 if locationID in data: 1189 row = data[locationID] 1190 self.UpdateItem(row.invItem, row) 1191 for itemKey in data.iterkeys(): 1192 if itemKey == locationID: 1193 continue 1194 row = data[itemKey] 1195 self.UpdateItem(row.invItem, row) 1196 1197 sm.ChainEvent('ProcessGodmaLocationPrimed', locationID) 1198 1199 def OnGodmaPrimeItem(self, locationID, row): 1200 if locationID not in self.itemsByLocationID: 1201 log.LogError('OnGodmaPrimeItem received row %s for locationID %s, location primed!' % (row, locationID)) 1202 self.itemsByLocationID[locationID] = set() 1203 self.UpdateItem(row.invItem, row) 1204 1205 def OnGodmaFlushLocation(self, locationID): 1206 self.godma.LogInfo('OnGodmaFlushLocation', locationID) 1207 self.PurgeEntries(locationID) 1208 1209 def OnGodmaFlushLocationProfile(self, locationID, profileID): 1210 self.godma.LogInfo('OnGodmaFlushLocationProfile', locationID, profileID) 1211 if profileID[0] == 'effect' and profileID[1] != locationID: 1212 self.PurgeEntry(profileID[1], locationID) 1213 1214 def OnModuleAttributeChange(self, ownerID, itemKey, attributeID, time, newValue, oldValue, wallclockTime, scatterAttr = True): 1215 if self.primingChannel is not None: 1216 if not self.primingChannel.receive(): 1217 self.godma.LogError('OnModuleAttributeChange - Discarded due to response from receive', ownerID, itemKey, attributeID, time, newValue, oldValue, scatterAttr) 1218 return 1219 self.godma.LogWarn('OnModuleAttributeChange - Stalled, but now in action', ownerID, itemKey, attributeID, time, newValue, oldValue, scatterAttr) 1220 self.OnModuleAttributeChange_(ownerID, itemKey, attributeID, time, newValue, oldValue, wallclockTime, scatterAttr, useTasket=True) 1221 1222 def OnModuleAttributeChange_(self, ownerID, itemKey, attributeID, time, newValue, oldValue, wallclockTime, scatterAttr = True, useTasket = False): 1223 if not self._IsAttributeChangeRelevant(itemKey, attributeID, wallclockTime): 1224 self.godma.LogInfo('Ignoring attribute change to attribute', attributeID, 'on', itemKey, 'as it is too old', (oldValue, newValue)) 1225 return 1226 if wallclockTime: 1227 self.lastAttributeChange[itemKey, attributeID] = wallclockTime 1228 if not (itemKey == session.charid or ownerID == session.charid): 1229 if session.charid is None: 1230 return 1231 log.LogTraceback('Got moduleattributechange for object I do not own!!!!!!!! Yayz0r - ownerID = %s, itemKey = %s, attributeID = %s, session.charid = %s' % (ownerID, 1232 itemKey, 1233 attributeID, 1234 session.charid), channel='svc.godma') 1235 self.godma.LogError('OnModuleAttributeChange', ownerID, itemKey, attributeID, time, newValue, oldValue, scatterAttr) 1236 return 1237 if useTasket: 1238 uthread.pool('godma::ApplyAttributeChange::OnModuleAttributeChange', self.ApplyAttributeChange, ownerID, itemKey, attributeID, time, newValue, oldValue, scatterAttr) 1239 else: 1240 self.ApplyAttributeChange(ownerID, itemKey, attributeID, time, newValue, oldValue, scatterAttr) 1241 1242 def OnLocationAttributeChange(self, ownerID, locationID, itemKey, attributeID, time, newValue, oldValue): 1243 if locationID not in self.itemsByLocationID: 1244 log.LogInfo('Got attribute change event for unknown location: %s ' % [ownerID, 1245 locationID, 1246 itemKey, 1247 attributeID, 1248 time, 1249 newValue, 1250 oldValue]) 1251 return 1252 ClockThis('godma::ApplyAttributeChange::OnLocationAttributeChange', self.ApplyAttributeChange, ownerID, itemKey, attributeID, time, newValue, oldValue) 1253 1254 def OnModuleAttributeChanges(self, changes): 1255 if self.primingChannel is not None: 1256 if not self.primingChannel.receive(): 1257 self.godma.LogError('OnModuleAttributeChanges - Discarded due to response from receive', changes) 1258 return 1259 self.godma.LogWarn('OnModuleAttributeChanges - Stalled, but now in action', changes) 1260 qtyChanges = set() 1261 itemKeyIdx, attributeIdx, newValueIdx = (2, 3, 5) 1262 for each in changes: 1263 if type(each[itemKeyIdx]) is tuple and each[attributeIdx] == const.attributeQuantity and each[newValueIdx] == 0.0: 1264 qtyChanges.add(each[itemKeyIdx]) 1265 1266 for each in changes: 1267 if each[itemKeyIdx] in qtyChanges and each[attributeIdx] != const.attributeQuantity: 1268 continue 1269 with ExceptionEater('Actioning on an attribute change in Godma'): 1270 self.OnModuleAttributeChange_(*(each[1:] + (0,))) 1271 1272 self._emit_signal(changes) 1273 1274 def _emit_signal(self, changes): 1275 ch = [] 1276 for change in changes: 1277 item_id = change[2] 1278 attribute_id = change[3] 1279 value = change[5] 1280 old_value = change[6] 1281 item = self._get_item(item_id) 1282 if not item: 1283 continue 1284 attribute_name = dogma.data.get_attribute_name(attribute_id) 1285 ch.append((attribute_name, item, value)) 1286 dogma_signals.on_attribute_changed(item, attribute_id, value, old_value) 1287 1288 sm.ScatterEvent('OnAttributes', ch) 1289 1290 def _get_item(self, item_id): 1291 entry = None 1292 if item_id in self.invitems: 1293 entry = self.invitems[item_id] 1294 elif item_id in self.sublocationsByKey: 1295 entry = self.sublocationsByKey[item_id] 1296 return entry 1297 1298 def GetActiveModulesOnTargetID(self, targetID): 1299 activeModulesOnTargetID = [] 1300 for module in self._IterActiveModules(): 1301 try: 1302 effect = module.defaultEffect 1303 if effect is not None and effect.targetID == targetID: 1304 activeModulesOnTargetID.append(module) 1305 except KeyError: 1306 continue 1307 1308 return activeModulesOnTargetID 1309 1310 def GetActiveModules(self): 1311 active_modules = [] 1312 for module in self._IterActiveModules(): 1313 effect = module.defaultEffect 1314 if effect is not None and not self.effectsByItemEffect[module.itemID][effect.effectName].isDeactivating: 1315 active_modules.append(module) 1316 1317 return active_modules 1318 1319 def GetActiveModuleTypes(self): 1320 active_module_type_ids = [] 1321 for module in self._IterActiveModules(): 1322 effect = module.defaultEffect 1323 if effect is not None and not self.effectsByItemEffect[module.itemID][effect.effectName].isDeactivating: 1324 active_module_type_ids.append(module.typeID) 1325 1326 return active_module_type_ids 1327 1328 def GetActiveModuleGroups(self): 1329 active_module_group_ids = [] 1330 for module in self._IterActiveModules(): 1331 effect = module.defaultEffect 1332 if effect is not None and not self.effectsByItemEffect[module.itemID][effect.effectName].isDeactivating: 1333 active_module_group_ids.append(module.groupID) 1334 1335 return active_module_group_ids 1336 1337 def _IterActiveModules(self): 1338 if session.shipid is None: 1339 return 1340 for module in self.GetItem(session.shipid).modules: 1341 try: 1342 effect = module.defaultEffect 1343 if effect is not None and effect.isActive: 1344 yield module 1345 except KeyError: 1346 continue 1347 1348 def OnGodmaShipEffect(self, itemID, effectID, t, start, active, environment, startTime, duration, repeat, error, actualStopTime = None, stall = True): 1349 if stall and self.primingChannel is not None: 1350 if not self.primingChannel.receive(): 1351 self.godma.LogError('OnGodmaShipEffect - Discarded due to response from receive', itemID, effectID, t, start, active, environment, startTime, duration, repeat, error) 1352 return 1353 self.godma.LogWarn('OnGodmaShipEffect - Stalled, but now in action', itemID, effectID, t, start, active, environment, startTime, duration, repeat, error) 1354 effect = dogma.data.get_effect(effectID) 1355 effectName = effect.effectName 1356 if itemID in self.effectsByItemEffect and effectName in self.effectsByItemEffect[itemID]: 1357 self.godma.LogInfo('OnGodmaShipEffect.effectsByItemEffect', itemID, effectName, active, duration, repeat) 1358 self.effectsByItemEffect[itemID][effectName].isDeactivating = False 1359 self.effectsByItemEffect[itemID][effectName].isActive = active 1360 self.effectsByItemEffect[itemID][effectName].startTime = startTime 1361 self.effectsByItemEffect[itemID][effectName].duration = duration 1362 self.effectsByItemEffect[itemID][effectName].repeat = repeat 1363 self.effectsByItemEffect[itemID][effectName].targetID = environment[ENV_IDX_TARGET] 1364 if active: 1365 self._NotifyOfEffectActivation(itemID, effectID) 1366 if environment[ENV_IDX_SHIP] == session.shipid or environment[ENV_IDX_SELF] and sm.GetService('pwn').GetCurrentControl(environment[ENV_IDX_SELF]): 1367 effectState = utillib.KeyVal(itemID=itemID, effectName=effectName, time=t, start=start, active=active, environment=environment, duration=duration, repeat=repeat, error=error) 1368 uthread.new(self.ActualStopEffect, itemID, effect, actualStopTime, effectState) 1369 if effect.effectCategory == const.dgmEffOverload: 1370 if start and itemID in self.pendingOverloading: 1371 self.StopPendingOverloadModule(itemID) 1372 elif not start and itemID in self.pendingStopOverloading: 1373 self.StopPendingStopOverloadModule(itemID) 1374 1375 def _NotifyOfEffectActivation(self, itemID, effectID): 1376 sm.ScatterEvent('OnModuleActivated', itemID, effectID) 1377 1378 def ActualStopEffect(self, itemID, effect, stopTime, effectState): 1379 if not effectState.start and stopTime is not None: 1380 timeDiffInMs = (stopTime - blue.os.GetSimTime()) / const.MSEC 1381 if timeDiffInMs > 0: 1382 self.godma.LogInfo('ActualStopEffect::Sleeping for', timeDiffInMs) 1383 blue.pyos.synchro.SleepSim(timeDiffInMs) 1384 self.TryRecordLastStopTime(itemID, effectState, effect, time=blue.os.GetSimTime()) 1385 self.ClearPendingOverloadingFlags() 1386 sm.ChainEvent('ProcessShipEffect', self, effectState) 1387 1388 def TryRecordLastStopTime(self, itemID, effectState, effect, time): 1389 if effectState.start or effect.effectID in (const.effectOnline, const.effectOverloadRofBonus): 1390 return 1391 if effect.effectCategory == const.dgmEffPassive: 1392 return 1393 try: 1394 self.RecordLastStopTime(itemID, time) 1395 except KeyError: 1396 self.RemoveOldCooldownTimers() 1397 1398 def RecordLastStopTime(self, itemID, time): 1399 delayTime = self.dogmaLocation.GetAccurateAttributeValue(itemID, const.attributeModuleReactivationDelay) 1400 if not delayTime: 1401 return 1402 self.RemoveOldCooldownTimers() 1403 self.lastStopTimesByItemID[itemID] = (time, delayTime) 1404 1405 def OnGodmaEffectInfo(self, itemID, itemTypeID, effectInfo, snapshotTime): 1406 info = utillib.KeyVal(activeEffects=effectInfo, time=snapshotTime) 1407 self.RefreshItemEffects(itemID, itemTypeID, info=info, doUpdate=True) 1408 1409 def DoSimClockRebase(self, rebaseTimes): 1410 oldSimTime, newSimTime = rebaseTimes 1411 delta = newSimTime - oldSimTime 1412 for moduleID, times in self.lastStopTimesByItemID.items(): 1413 time, delayTime = times 1414 time += delta 1415 self.lastStopTimesByItemID[moduleID] = (time, delayTime) 1416 1417 def InitializeData(self): 1418 if not self.inited: 1419 self.attributesByName = dogma.data.get_all_attributes_by_name() 1420 self.attributesByID = dogma.data.get_all_attributes_by_id() 1421 chargedAttributes = dogma.data.get_attribute_list_by_charge_recharge_time_id() 1422 chargedAttributeTauCaps = {} 1423 chargedAttributeTauCapsByMax = {} 1424 chargedAttributesByTau = {} 1425 for tauID, attributes in chargedAttributes.iteritems(): 1426 if not tauID: 1427 continue 1428 tauName = self.attributesByID[tauID].name 1429 for attribute in attributes: 1430 if attribute.dataType != attributeDataTypeTypeDamage: 1431 attName = attribute.name 1432 capName = self.attributesByID[attribute.maxAttributeID].name 1433 chargedAttributeTauCaps[attName] = (tauName, capName) 1434 if capName not in chargedAttributeTauCapsByMax: 1435 chargedAttributeTauCapsByMax[capName] = [] 1436 chargedAttributeTauCapsByMax[capName].append(attName) 1437 if tauName not in chargedAttributesByTau: 1438 chargedAttributesByTau[tauName] = [] 1439 chargedAttributesByTau[tauName].append(attName) 1440 1441 self.chargedAttributeTauCaps = chargedAttributeTauCaps 1442 self.chargedAttributeTauCapsByMax = chargedAttributeTauCapsByMax 1443 self.chargedAttributesByTau = chargedAttributesByTau 1444 self.typeWrappers = {} 1445 self.inited = True 1446 1447 def IsSubLocation(self, itemKey): 1448 return type(itemKey) is tuple 1449 1450 def UpdateItem(self, item, infoz = None, dontGetInfo = 0): 1451 self.godma.LogInfo('UpdateItem:', item, infoz, dontGetInfo) 1452 isSubLocation = item is not None and self.IsSubLocation(item.itemID) or infoz is not None and self.IsSubLocation(infoz.itemID) 1453 if isSubLocation: 1454 itemKey = infoz.itemID 1455 itemLocationID, itemFlag, itemTypeID = itemKey 1456 itemGroupID = evetypes.GetGroupID(itemTypeID) 1457 itemCategoryID = evetypes.GetCategoryID(itemTypeID) 1458 else: 1459 itemKey = item.itemID 1460 itemLocationID = item.locationID 1461 itemTypeID, itemGroupID, itemCategoryID, itemFlag = (item.typeID, 1462 item.groupID, 1463 item.categoryID, 1464 item.flagID) 1465 if itemCategoryID not in (const.categoryEntity, 1466 const.categoryStarbase, 1467 const.categorySovereigntyStructure, 1468 const.categoryShip, 1469 const.categoryModule, 1470 const.categoryCharge, 1471 const.categoryDrone, 1472 const.categorySubSystem, 1473 const.categoryFighter, 1474 const.categoryStructure, 1475 const.categoryStructureModule) and itemGroupID != const.groupCharacter: 1476 self.godma.LogTraceback('Godma location received unexpected and unwanted item') 1477 return 1478 if itemFlag in (const.flagCargo, const.flagDroneBay) or itemFlag in const.fighterTubeFlags: 1479 return 1480 doUpdate = False 1481 if isSubLocation: 1482 if itemLocationID not in self.invitems: 1483 self.godma.LogWarn('Godma:UpdateItem - could not find location object', itemLocationID, ', skipping update of', itemKey) 1484 return 1485 locationItem = self.invitems[itemLocationID] 1486 l = blue.DBRow(self.godma.subloc_internalrd, [itemKey, 1487 itemTypeID, 1488 locationItem.ownerID, 1489 itemLocationID, 1490 itemFlag, 1491 itemGroupID, 1492 itemCategoryID]) 1493 if itemFlag not in self.sublocationsByKey: 1494 self.godma.LogInfo('Godma:UpdateItem - Inserting sublocation', itemKey) 1495 if const.attributeQuantity not in infoz.attributes: 1496 log.LogTraceback('Godma:UpdateItem - QUANTITY MISSING', channel='svc.godma') 1497 self.godma.LogError('Godma:UpdateItem - QUANTITY MISSING') 1498 else: 1499 quantity = infoz.attributes[const.attributeQuantity] 1500 item = blue.DBRow(self.godma.sublocrd, [itemKey, 1501 itemTypeID, 1502 locationItem.ownerID, 1503 itemLocationID, 1504 itemFlag, 1505 quantity, 1506 itemGroupID, 1507 itemCategoryID, 1508 None]) 1509 if self.godma.invCache.IsInventoryPrimedAndListed(itemLocationID): 1510 self.godma.LogInfo('Godma:UpdateItem - QUANTITY', infoz.attributes[const.attributeQuantity]) 1511 change = {const.ixLocationID: None, 1512 const.ixFlag: None} 1513 location = None 1514 sm.ScatterEvent('OnItemChange', item, change, location) 1515 self.sublocationsByKey[itemKey] = l 1516 if itemLocationID not in self.sublocationsByLocationFlag: 1517 self.sublocationsByLocationFlag[itemLocationID] = godmarowset.GodmaIndexedRowset(self.godma.subloc_internalrd, 'flagID', rowClass=self.GetSubLocationWrapper) 1518 elif itemFlag in self.sublocationsByLocationFlag[itemLocationID]: 1519 doUpdate = True 1520 self.sublocationsByLocationFlag[itemLocationID][itemFlag] = l 1521 else: 1522 if itemKey not in self.invitems: 1523 self.godma.LogInfo('Godma:UpdateItem - Inserting item', itemKey) 1524 else: 1525 doUpdate = True 1526 self.invitems[itemKey] = item 1527 if itemCategoryID in (const.categoryShip, const.categoryStarbase, const.categoryStructure) or itemGroupID == const.groupCharacter: 1528 self.itemsByLocationID[itemKey] = set() 1529 if not (idCheckers.IsSolarSystem(itemLocationID) or idCheckers.IsStation(itemLocationID)): 1530 self.itemsByLocationID[itemLocationID].add(itemKey) 1531 if IsShipFittingFlag(itemFlag) and not isSubLocation and itemCategoryID not in (const.categoryModule, 1532 const.categoryStructureModule, 1533 const.categoryCharge, 1534 const.categorySubSystem): 1535 self.godma.LogError('Godma:UpdateItem.skip2', itemKey) 1536 return 1537 if (isSubLocation or not item.singleton) and (itemCategoryID != const.categoryCharge or itemGroupID == const.groupMine): 1538 if itemFlag != const.flagCapsule: 1539 self.godma.LogError('Godma:UpdateItem.skip3', itemKey) 1540 return 1541 if dontGetInfo: 1542 self.godma.LogError('Godma:UpdateItem.skip4', itemKey) 1543 return 1544 if infoz is None: 1545 infoz = self.GetDogmaLM().ItemGetInfo(itemKey) 1546 if infoz is None: 1547 return 1548 self.RefreshItemAttributes(itemKey, infoz, doUpdate) 1549 self.RefreshItemEffects(itemKey, itemTypeID, infoz, doUpdate) 1550 if isSubLocation: 1551 regKey = (itemLocationID, itemFlag, None) 1552 else: 1553 regKey = itemKey 1554 if regKey in self.itemKeyChannels: 1555 while self.itemKeyChannels[regKey].queue: 1556 self.godma.LogInfo('GetWithWait-END', regKey) 1557 self.itemKeyChannels[regKey].send(ret) 1558 1559 del self.itemKeyChannels[regKey] 1560 1561 def RefreshItemEffects(self, itemID, itemTypeID, info = None, doUpdate = False): 1562 if info is None: 1563 info = self.GetDogmaLM().ItemGetInfo(itemID) 1564 if info is None: 1565 return 1566 effLines = [] 1567 eventLines = [] 1568 activeDict = info.activeEffects 1569 typeEffects = dogma.data.get_type_effects(itemTypeID) 1570 for row in typeEffects: 1571 effectID = row.effectID 1572 eff = dogma.data.get_effect(effectID) 1573 if row.isDefault and effectID != const.effectSkillEffect or effectID in self.requiredEffects or eff.effectCategory in (const.dgmEffActivation, 1574 const.dgmEffTarget, 1575 const.dgmEffArea, 1576 const.dgmEffOverload) or eff.fittingUsageChanceAttributeID is not None: 1577 line = blue.DBRow(self.__effectheader__, [itemID, 1578 eff.effectID, 1579 eff.effectName, 1580 eff.effectCategory, 1581 row.isDefault, 1582 effectID in activeDict, 1583 eff.isOffensive, 1584 eff.isAssistance, 1585 dogma.data.get_effect_description(eff.effectID), 1586 eff.trackingSpeedAttributeID, 1587 eff.rangeAttributeID, 1588 eff.falloffAttributeID, 1589 eff.dischargeAttributeID, 1590 eff.durationAttributeID, 1591 eff.iconID, 1592 None, 1593 None, 1594 None, 1595 None, 1596 False]) 1597 effLines.append(line) 1598 self.godma.LogInfo('RefreshItemEffects.effectsByItemEffect', itemID, effectID, effLines[-1]) 1599 if effectID in activeDict and eff.effectCategory in (const.dgmEffActivation, 1600 const.dgmEffTarget, 1601 const.dgmEffArea, 1602 const.dgmEffOverload) and effectID not in self.requiredEffects: 1603 d = activeDict[effectID] 1604 env = d[:ENV_IDX_LINE] 1605 startTime, duration, repeat = d[7], d[8], d[9] 1606 eventLines.append((itemID, 1607 effectID, 1608 info.time, 1609 True, 1610 True, 1611 env, 1612 startTime, 1613 duration, 1614 repeat, 1615 None)) 1616 1617 if doUpdate and itemID in self.effectsByItemEffect: 1618 ind = self.effectsByItemEffect[itemID].header.Index('effectName') 1619 for i in effLines: 1620 self.effectsByItemEffect[itemID][i[ind]] = i 1621 1622 else: 1623 self.effectsByItemEffect[itemID] = godmarowset.GodmaRowset(self.__effectheader__, effLines, rowClass=self.GetEffectWrapper).Index('effectName') 1624 kwArgs = {'stall': False} 1625 for line in eventLines: 1626 self.OnGodmaShipEffect(*line, **kwArgs) 1627 1628 def RefreshItemAttributes(self, itemID, info = None, doUpdate = False): 1629 if info is None: 1630 info = self.GetDogmaLM().ItemGetInfo(itemID) 1631 if info is None: 1632 return 1633 if doUpdate: 1634 atts = self.attributesByItemAttribute.get(itemID, {}) 1635 else: 1636 atts = {} 1637 time = info.time 1638 wallclockTime = info.wallclockTime 1639 for attributeID, val in info.attributes.iteritems(): 1640 if not self._IsAttributeChangeRelevant(itemID, attributeID, wallclockTime): 1641 continue 1642 attributeName = dogma.data.get_attribute_name(attributeID) 1643 self.godma.LogInfo('UpdateAttributeIDs', itemID, attributeID, attributeName, val) 1644 atts[attributeName] = val 1645 1646 return self.UpdateAttribute(itemID, atts, time) 1647 1648 def _IsAttributeChangeRelevant(self, itemID, attributeID, wallclockTime): 1649 if not wallclockTime: 1650 return True 1651 if (itemID, attributeID) not in self.lastAttributeChange: 1652 return True 1653 if self.lastAttributeChange[itemID, attributeID] <= wallclockTime: 1654 return True 1655 return False 1656 1657 def UpdateAttribute(self, itemID, attributes, time): 1658 self.attributesByItemAttribute[itemID] = attributes 1659 if time is None: 1660 time = blue.os.GetSimTime() 1661 for k in attributes.iterkeys(): 1662 if k in self.chargedAttributeTauCaps: 1663 if itemID not in self.chargedAttributesByItemAttribute: 1664 self.chargedAttributesByItemAttribute[itemID] = {} 1665 if self.attributesByItemAttribute[itemID].get(self.chargedAttributeTauCaps[k][0]) and self.attributesByItemAttribute[itemID].get(self.chargedAttributeTauCaps[k][1]): 1666 self.CreateChargedAttribute(itemID, k, attributes[k], time, self.chargedAttributeTauCaps[k]) 1667 1668 def CreateChargedAttribute(self, itemID, attribute, value, time, taucap): 1669 tau, cap = taucap 1670 tauValue = self.GetAttribute(itemID, tau) / 5.0 1671 capValue = self.GetAttribute(itemID, cap) 1672 self.chargedAttributesByItemAttribute[itemID][attribute] = [value, 1673 time, 1674 tauValue, 1675 capValue] 1676 self.godma.LogInfo('CreateChargedAttribute', itemID, attribute, time, self.chargedAttributesByItemAttribute[itemID][attribute]) 1677 1678 def NameToGroup(self, name): 1679 k = 'group' + name[0].capitalize() + name[1:] 1680 if ConstValueExists(k): 1681 return LookupConstValue(k, None) 1682 1683 def NameToType(self, name): 1684 k = 'type' + name[0].capitalize() + name[1:] 1685 if ConstValueExists(k): 1686 return LookupConstValue(k, None) 1687 1688 def LocateItem(self, id, name): 1689 typeID = self.NameToType(name) 1690 if typeID is not None: 1691 loc = FilterRowset(self.invitems.header, self.invitems.items.values(), 'locationID', self.GetItemWrapper) 1692 for item in loc[id]: 1693 if item.flagID in [const.flagSkill, const.flagSkillInTraining] and item.typeID == typeID: 1694 return item 1695 1696 def GetDisplayAttributes(self, id): 1697 rowDescriptor = blue.DBRowDescriptor((('displayName', const.DBTYPE_STR), 1698 ('value', const.DBTYPE_R5), 1699 ('unitID', const.DBTYPE_UI1), 1700 ('iconID', const.DBTYPE_I4), 1701 ('attributeID', const.DBTYPE_I2))) 1702 rowset = CRowset(rowDescriptor, []) 1703 for k, v in self.GetAttributes(id).iteritems(): 1704 attribute = self.attributesByName[k] 1705 realK = dogma.data.get_attribute_display_name(attribute.attributeID) 1706 if not len(realK): 1707 realK = k 1708 if attribute.published: 1709 rowset.InsertNew([realK, 1710 v, 1711 getattr(attribute, 'unitID', None), 1712 getattr(attribute, 'iconID', None), 1713 attribute.attributeID]) 1714 1715 return rowset 1716 1717 def GetItemWrapper(self, header, dbrow): 1718 return ItemWrapper(self, header, dbrow) 1719 1720 def GetSubLocationWrapper(self, header, dbrow): 1721 return ItemWrapper(self, header, dbrow) 1722 1723 def GetItem(self, itemID, waitForPrime = False): 1724 if type(itemID) is tuple: 1725 return self.GetSubLocation(itemID[0], itemID[1], waitForPrime) 1726 while self.priming: 1727 blue.pyos.synchro.SleepWallclock(1000) 1728 1729 try: 1730 return self.invitems[itemID] 1731 except KeyError: 1732 pass 1733 1734 if waitForPrime: 1735 self.godma.LogInfo('GetItem.GetWithWait', itemID) 1736 return self.GetWithWait(itemID) 1737 1738 def IsItemReady(self, itemID): 1739 return itemID in self.invitems 1740 1741 def IsPriming(self): 1742 return self.priming 1743 1744 def GetSubLocation(self, locationID, flag, waitForPrime = False): 1745 while self.priming: 1746 blue.pyos.synchro.SleepWallclock(1000) 1747 1748 try: 1749 return self.sublocationsByLocationFlag[locationID][flag] 1750 except KeyError: 1751 if waitForPrime: 1752 self.godma.LogInfo('GetSubLocation.GetWithWait', locationID, flag) 1753 return self.GetWithWait((locationID, flag, None)) 1754 1755 def GetWithWait(self, itemKey): 1756 try: 1757 channel = self.itemKeyChannels[itemKey] 1758 except KeyError: 1759 channel = uthread.Channel(('godma::GetItem', itemKey)) 1760 self.itemKeyChannels[itemKey] = channel 1761 1762 ret = channel.receive() 1763 self.godma.LogInfo('GetWithWait', itemKey, 'DONE', ret) 1764 return ret 1765 1766 def GetAttributes(self, id): 1767 try: 1768 return self.attributesByItemAttribute[id] 1769 except KeyError: 1770 return {} 1771 1772 def GetAttribute(self, itemKey, attrKey): 1773 if self.IsSubLocation(itemKey): 1774 if attrKey == 'singleton': 1775 return 0 1776 if attrKey == 'stacksize': 1777 attrKey = 'quantity' 1778 if itemKey in self.chargedAttributesByItemAttribute and attrKey in self.chargedAttributesByItemAttribute[itemKey]: 1779 chargedAttribute = self.chargedAttributesByItemAttribute[itemKey][attrKey] 1780 try: 1781 return self.GetChargeValue(*chargedAttribute) 1782 except ZeroDivisionError: 1783 typeID = self.invitems[itemKey].typeID if not isinstance(itemKey, tuple) else itemKey[2] 1784 log.LogException('GetChargeValue got a ZeroDivisionError for item %s of type %s' % (itemKey, evetypes.GetName(typeID))) 1785 raise 1786 1787 try: 1788 return self.attributesByItemAttribute[itemKey][attrKey] 1789 except KeyError: 1790 pass 1791 1792 if itemKey in self.sublocationsByKey: 1793 typeID = itemKey[2] 1794 locationID = itemKey[0] 1795 else: 1796 typeID = self.invitems[itemKey].typeID 1797 locationID = self.invitems[itemKey].locationID 1798 categoryID = evetypes.GetCategoryID(typeID) 1799 if categoryID == const.categorySkill: 1800 raise RuntimeError('StateManager::GetAttribute called for skill item! Skill items should not exist!') 1801 try: 1802 return self.attributesByName[attrKey].defaultValue 1803 except KeyError: 1804 pass 1805 1806 if attrKey == 'invItem': 1807 if itemKey in self.sublocationsByKey: 1808 rd = self.godma.sublocrd 1809 item = self.GetSubLocation(locationID, itemKey[1]) 1810 else: 1811 rd = self.godma.itemrd 1812 item = self.Item(itemKey) 1813 return blue.DBRow(rd, [itemKey, 1814 typeID, 1815 item.ownerID, 1816 locationID, 1817 item.flagID, 1818 item.quantity, 1819 evetypes.GetGroupID(typeID), 1820 categoryID, 1821 None]) 1822 if attrKey == 'displayAttributes': 1823 return self.GetDisplayAttributes(itemKey) 1824 if attrKey == 'attributes': 1825 return self.GetAttributes(itemKey) 1826 if attrKey == 'typeID': 1827 return typeID 1828 if attrKey == 'type': 1829 log.LogTraceback("Someone is asking for 'type' as an attribute in godma.py::GetAttribute. We don't support that anymore") 1830 if attrKey == 'description': 1831 return evetypes.GetDescription(typeID) 1832 if attrKey == 'group': 1833 log.LogTraceback("Someone is asking for 'group' as an attribute in godma.py::GetAttribute. We don't support that anymore") 1834 if attrKey == 'category': 1835 log.LogTraceback("Someone is asking for 'category' as an attribute in godma.py::GetAttribute. We don't support that anymore") 1836 if attrKey == 'parent': 1837 return self.invitems[locationID] 1838 if attrKey == 'name': 1839 return evetypes.GetName(typeID) 1840 if attrKey == 'graphicID': 1841 return evetypes.GetGraphicID(typeID) 1842 if attrKey == 'iconID': 1843 return evetypes.GetIconID(typeID) 1844 if attrKey == 'effects': 1845 return self.effectsByItemEffect.get(itemKey, {}) 1846 if attrKey == 'skills': 1847 raise RuntimeError("Godma::GetAttribute called for 'skills'") 1848 else: 1849 if attrKey == 'implants': 1850 return [ implant for implant in sm.StartService('skills').GetImplants().itervalues() ] 1851 if attrKey == 'boosters': 1852 boosters = [ booster for booster in sm.StartService('skills').GetBoosters().itervalues() ] 1853 for booster in boosters: 1854 setattr(booster, 'typeID', booster.boosterTypeID) 1855 1856 return boosters 1857 if attrKey == 'modules': 1858 li = [] 1859 if itemKey in self.itemsByLocationID: 1860 for itemKey2 in self.itemsByLocationID[itemKey]: 1861 if itemKey2 in self.invitems: 1862 module = self.invitems[itemKey2] 1863 if IsShipFittingFlag(module.flagID): 1864 li.append(self.invitems[itemKey2].dbrow) 1865 1866 return godmarowset.GodmaRowset(self.invitems.header, li, self.GetItemWrapper) 1867 if attrKey == 'sublocations': 1868 li = [] 1869 if itemKey in self.sublocationsByLocationFlag: 1870 li = [ i.dbrow for i in self.sublocationsByLocationFlag[itemKey].values() if IsShipFittingFlag(i[0][1]) ] 1871 return godmarowset.GodmaRowset(self.sublocationsByLocationFlag.header, li, self.GetItemWrapper) 1872 if itemKey not in self.invitems: 1873 raise RuntimeError('Item (or its parent) has not been primed', itemKey) 1874 loc = self.LocateItem(itemKey, attrKey) 1875 if loc is not None: 1876 return loc 1877 eff = self.GetEffect(itemKey, attrKey) 1878 if eff: 1879 return eff 1880 raise_(AttributeError, attrKey) 1881 1882 def GetEffectAttributeEx(self, eff, itemID, attribute): 1883 if attribute == 'item' or attribute == 'module': 1884 return self.GetItem(itemID) 1885 if attribute == 'trackingSpeed': 1886 trackingSpeedAttributeName = dogma.data.get_attribute_name(eff.trackingSpeedAttributeID) 1887 return self.GetAttribute(itemID, trackingSpeedAttributeName) 1888 if attribute == 'falloff': 1889 falloffAttributeName = dogma.data.get_attribute_name(eff.falloffAttributeID) 1890 return self.GetAttribute(itemID, falloffAttributeName) 1891 if attribute == 'range': 1892 rangeAttributeName = dogma.data.get_attribute_name(eff.rangeAttributeID) 1893 return self.GetAttribute(itemID, rangeAttributeName) 1894 1895 def GetEffectWrapper(self, header, line): 1896 return EffectWrapper(self, header, line) 1897 1898 def GetEffect(self, id, effect): 1899 try: 1900 return self.effectsByItemEffect[id][effect] 1901 except KeyError: 1902 return None 1903 1904 def GetDefaultEffect(self, typeID): 1905 if typeID not in self.defaultEffectsByType: 1906 possibleEffects = dogma.data.get_type_effects(typeID) 1907 eff = None 1908 for typeEffect in possibleEffects: 1909 if typeEffect.isDefault: 1910 eff = dogma.data.get_effect(typeEffect.effectID) 1911 1912 if eff is None: 1913 return 1914 line = [None, 1915 eff.effectID, 1916 eff.effectName, 1917 eff.effectCategory, 1918 1, 1919 0, 1920 eff.isOffensive, 1921 eff.isAssistance, 1922 dogma.data.get_effect_description(eff.effectID), 1923 eff.trackingSpeedAttributeID, 1924 eff.rangeAttributeID, 1925 eff.falloffAttributeID, 1926 eff.dischargeAttributeID, 1927 eff.durationAttributeID, 1928 eff.iconID, 1929 None, 1930 None, 1931 None, 1932 None, 1933 0] 1934 self.defaultEffectsByType[typeID] = EffectWrapper(self, self.__effectheader__, blue.DBRow(self.__effectheader__, line)) 1935 return self.defaultEffectsByType[typeID] 1936 1937 def TypeHasEffect(self, typeID, effectID): 1938 self.CacheTypeEffects(typeID) 1939 return effectID in self.effectsByType[typeID] 1940 1941 def GetEffectsByTypeID(self, typeID): 1942 self.CacheTypeEffects(typeID) 1943 return self.effectsByType[typeID] 1944 1945 def CacheTypeEffects(self, typeID): 1946 if typeID not in self.effectsByType: 1947 self.effectsByType[typeID] = {typeEffect.effectID:dogma.data.get_effect(typeEffect.effectID) for typeEffect in dogma.data.get_type_effects(typeID)} 1948 1949 def GetShipType(self, shipTypeID): 1950 return self.GetType(shipTypeID) 1951 1952 def GetType(self, genericTypeID): 1953 self.InitializeData() 1954 if genericTypeID in self.typeWrappers: 1955 return self.typeWrappers[genericTypeID] 1956 result = TypeWrapper(self, genericTypeID) 1957 self.typeWrappers[genericTypeID] = result 1958 return result 1959 1960 def GetChargeValue(self, oldVal, oldTime, tau, Ec, newTime = None): 1961 if Ec == 0: 1962 return 0 1963 if newTime is None: 1964 newTime = blue.os.GetSimTime() 1965 sq = math.sqrt(max(oldVal / Ec, 0)) 1966 timePassed = min(oldTime - newTime, 0) / float(const.dgmTauConstant) 1967 exp = math.exp(timePassed / tau) 1968 ret = (1.0 + (sq - 1.0) * exp) ** 2 * Ec 1969 return ret 1970 1971 def Activate(self, itemID, effectName, target, repeat): 1972 ret = None 1973 activateKey = (itemID, effectName) 1974 if activateKey in self.activatingEffects: 1975 self.godma.LogWarn('Trying to activate effect twice', effectName, itemID) 1976 return 1977 self.activatingEffects.add(activateKey) 1978 try: 1979 if target is not None and not isinstance(target, numbers.Integral): 1980 raise RuntimeError('InvalidTargetType', type(target)) 1981 sm.GetService('invCache').TryLockItem(itemID, 'lockItemGodmaActivate', {}, 1) 1982 try: 1983 ret = self.GetDogmaLM().Activate(itemID, effectName, target, repeat) 1984 if effectName != 'online': 1985 self.activatedLocation[itemID, effectName] = session.locationid 1986 return ret 1987 finally: 1988 sm.GetService('invCache').UnlockItem(itemID) 1989 1990 finally: 1991 self.activatingEffects.remove(activateKey) 1992 1993 def Overload(self, itemID, effectID): 1994 self.GetDogmaLM().Overload(itemID, effectID) 1995 self.pendingOverloading.add(itemID) 1996 self.StopPendingStopOverloadModule(itemID) 1997 self.dogmaLocation.Overload(itemID, effectID) 1998 return itemID 1999 2000 def OverloadRack(self, itemID): 2001 moduleIDs = self.GetDogmaLM().OverloadRack(itemID) 2002 for moduleID in moduleIDs: 2003 if not self.IsModuleOverloaded(moduleID): 2004 self.pendingOverloading.add(moduleID) 2005 2006 self.dogmaLocation.OverloadRack(moduleIDs) 2007 sm.ChainEvent('ProcessPendingOverloadUpdate', moduleIDs) 2008 return moduleIDs 2009 2010 def Deactivate(self, itemID, effectName): 2011 sm.GetService('invCache').TryLockItem(itemID, 'lockItemGodmaDeactivate', {}, 1) 2012 try: 2013 if (itemID, effectName) in self.activatedLocation and self.activatedLocation[itemID, effectName] != session.locationid: 2014 return 2015 if itemID in self.effectsByItemEffect and effectName in self.effectsByItemEffect[itemID]: 2016 self.effectsByItemEffect[itemID][effectName].isDeactivating = True 2017 return self.GetDogmaLM().Deactivate(itemID, effectName) 2018 except UserError: 2019 self.effectsByItemEffect[itemID][effectName].isDeactivating = False 2020 raise 2021 finally: 2022 sm.GetService('invCache').UnlockItem(itemID) 2023 2024 def IsModuleActivating(self, itemID, effectName): 2025 activateKey = (itemID, effectName) 2026 if activateKey in self.activatingEffects: 2027 return True 2028 return False 2029 2030 def StopOverload(self, itemID, effectID): 2031 self.GetDogmaLM().StopOverload(itemID, effectID) 2032 self.pendingStopOverloading.add(itemID) 2033 self.StopPendingOverloadModule(itemID) 2034 self.dogmaLocation.StopOverloading(itemID, effectID) 2035 return itemID 2036 2037 def StopOverloadRack(self, itemID): 2038 moduleIDs = self.GetDogmaLM().StopOverloadRack(itemID) 2039 self.pendingStopOverloading.update(moduleIDs) 2040 self.dogmaLocation.StopOverloadingRack(moduleIDs) 2041 sm.ChainEvent('ProcessPendingOverloadUpdate', moduleIDs) 2042 2043 def StopPendingOverloadModule(self, itemID): 2044 try: 2045 self.pendingOverloading.remove(itemID) 2046 except KeyError: 2047 pass 2048 2049 sm.ChainEvent('ProcessPendingOverloadUpdate', [itemID]) 2050 2051 def StopPendingStopOverloadModule(self, itemID): 2052 try: 2053 self.pendingStopOverloading.remove(itemID) 2054 except KeyError: 2055 pass 2056 2057 sm.ChainEvent('ProcessPendingOverloadUpdate', [itemID]) 2058 2059 def IsModuleOverloaded(self, itemID): 2060 for effect in self.GetItem(itemID).effects.itervalues(): 2061 if effect.effectCategory == const.dgmEffOverload and effect.isActive: 2062 return True 2063 2064 return False 2065 2066 def GetOverloadState(self, itemID): 2067 item = self.GetItem(itemID) 2068 for effectName, effect in item.effects.iteritems(): 2069 if effect.effectCategory == const.dgmEffOverload and effect.isActive: 2070 if itemID in self.pendingStopOverloading: 2071 return MODULE_PENDING_STOPOVERLOADING 2072 return MODULE_OVERLOADED 2073 break 2074 else: 2075 if itemID in self.pendingOverloading: 2076 return MODULE_PENDING_OVERLOADING 2077 return MODULE_NOT_OVERLOADED 2078 2079 def ClearPendingOverloadingFlags(self): 2080 moduleIDs = self.pendingOverloading.copy() 2081 moduleIDs.update(self.pendingStopOverloading.copy()) 2082 self.pendingOverloading.clear() 2083 self.pendingStopOverloading.clear() 2084 sm.ChainEvent('ProcessPendingOverloadUpdate', moduleIDs) 2085 2086 def GetMaxDamagedModuleInGroup(self, shipID, itemID): 2087 moduleIDs = self.dogmaLocation.GetModulesInBank(shipID, itemID) 2088 if moduleIDs is None: 2089 item = self.GetItem(itemID) 2090 damage = item.damage if item else 0 2091 return (itemID, damage) 2092 maxDamage = 0 2093 for moduleID in moduleIDs: 2094 item = self.GetItem(moduleID) 2095 if not item: 2096 continue 2097 if item.damage > maxDamage: 2098 maxDamage = item.damage 2099 2100 masterID = self.dogmaLocation.GetMasterModuleID(shipID, itemID) 2101 masterID = masterID if masterID is not None else itemID 2102 return (masterID, maxDamage) 2103 2104 def RepairManyModules(self, itemIDs): 2105 timeStamp = blue.os.GetSimTime() 2106 success, missingCharges, failed = self.GetDogmaLM().InitiateModuleRepairMany(itemIDs) 2107 textList = [] 2108 if success: 2109 textList.append(GetByLabel('UI/Inflight/SuccessfullyStartedRepairing', numModules=len(success))) 2110 if missingCharges: 2111 textList.append(GetByLabel('UI/Inflight/NotEnoughNaniteToRepairAll', typeName=evetypes.GetName(const.typeNaniteRepairPaste), numModules=len(missingCharges))) 2112 if failed: 2113 textList.append(GetByLabel('UI/Inflight/FailedToStartRepair', numModules=len(failed))) 2114 if textList: 2115 text = '<br><br>'.join(textList) 2116 prompt_player_fsd_dialog('CustomNotify', {'notify': text}) 2117 for itemID in success: 2118 self.modulesBeingRepaired[itemID] = timeStamp 2119 uthread.pool('godma::RepairManyModules::RepairModule_thread', self.RepairModule_thread, itemID, timeStamp) 2120 2121 if success: 2122 sm.ScatterEvent('OnMultiModuleRepairStarted', success) 2123 2124 def RepairModule(self, itemID): 2125 timeStamp = blue.os.GetSimTime() 2126 res = self.GetDogmaLM().InitiateModuleRepair(itemID) 2127 if res == False: 2128 return False 2129 self.modulesBeingRepaired[itemID] = timeStamp 2130 uthread.pool('godma::RepairModule::RepairModule_thread', self.RepairModule_thread, itemID, timeStamp) 2131 return True 2132 2133 def IsBeingRepaired(self, itemID): 2134 if itemID in self.modulesBeingRepaired: 2135 return True 2136 slaves = self.GetSlaveModules(itemID) 2137 for slave in slaves: 2138 if slave in self.modulesBeingRepaired: 2139 return True 2140 2141 return False 2142 2143 def GetSlaveModules(self, itemID): 2144 return self.dogmaLocation.GetSlaveModules(itemID, session.shipid) or [] 2145 2146 def RepairModule_thread(self, itemID, timeStamp): 2147 dmg = self.GetAttribute(itemID, 'damage') 2148 owner = session.charid 2149 rateOfRepair = self.GetAttribute(owner, 'moduleRepairRate') 2150 timeToSleep = dmg / rateOfRepair 2151 timeToSleepMs = int(timeToSleep * 60 * 1000) 2152 blue.pyos.synchro.SleepSim(timeToSleepMs + 1000) 2153 if itemID in self.modulesBeingRepaired and self.modulesBeingRepaired[itemID] == timeStamp: 2154 self.StopRepairModule(itemID) 2155 2156 def StopRepairModule(self, itemID): 2157 if itemID in self.modulesBeingRepaired: 2158 try: 2159 self.GetDogmaLM().StopModuleRepair(itemID) 2160 finally: 2161 sm.ScatterEvent('OnModuleRepaired', itemID) 2162 del self.modulesBeingRepaired[itemID] 2163 2164 def StopRepairModuleManyModules(self, itemIDs): 2165 for itemID in itemIDs: 2166 self.StopRepairModule(itemID) 2167 2168 def GetRepairTimeStamp(self, itemID): 2169 return self.modulesBeingRepaired.get(itemID, None) 2170 2171 def OnDroneControlLost(self, droneID): 2172 self.invitems.pop(droneID, None) 2173 2174 def OnDroneStateChange2(self, droneID, oldState, newState): 2175 if newState == -1: 2176 return 2177 if droneID in self.invitems: 2178 return 2179 slimItem = sm.GetService('michelle').GetItem(droneID) 2180 if slimItem and slimItem.ownerID == session.charid: 2181 typeID = slimItem.typeID 2182 item = blue.DBRow(self.godma.itemrd, [slimItem.itemID, 2183 typeID, 2184 slimItem.ownerID, 2185 session.solarsystemid, 2186 0, 2187 1, 2188 evetypes.GetGroupID(typeID), 2189 evetypes.GetCategoryID(typeID), 2190 1]) 2191 self.UpdateItem(item) 2192 2193 def OnFighterRemovedFromControllerClient(self, fighterID): 2194 self.invitems.pop(fighterID, None) 2195 2196 def IsModuleReloading(self, moduleID): 2197 return self.GetReloadTimes(moduleID) is not None 2198 2199 def GetReloadTimes(self, moduleID): 2200 nextCycle = self.GetAttributeValueByID(moduleID, const.attributeNextActivationTime) 2201 if nextCycle is None or nextCycle < blue.os.GetSimTime(): 2202 return 2203 duration = self.GetAttributeValueByID(moduleID, const.attributeReloadTime) 2204 startTime = nextCycle - duration * const.MSEC 2205 return (startTime, duration) 2206 2207 def GetCooldownTimes(self, moduleID): 2208 return self.lastStopTimesByItemID.get(moduleID, None) 2209 2210 def GetDurationTimeLeft(self, moduleID, typeID): 2211 defaultEffect = self.GetDefaultEffect(typeID) 2212 if defaultEffect: 2213 effectsForItem = self.effectsByItemEffect.get(moduleID, None) 2214 if not effectsForItem: 2215 return 2216 effectInfo = effectsForItem.get(defaultEffect.effectName, None) 2217 if effectInfo and effectInfo.isActive and effectInfo.startTime and effectInfo.duration: 2218 now = blue.os.GetSimTime() 2219 portionDone = blue.os.TimeDiffInMs(effectInfo.startTime, now) / effectInfo.duration 2220 if portionDone < 1: 2221 return (effectInfo.startTime, effectInfo.duration) 2222 newStart = effectInfo.startTime + int(portionDone) * effectInfo.duration * const.MSEC 2223 return (newStart, effectInfo.duration) 2224 2225 def RemoveOldCooldownTimers(self): 2226 now = blue.os.GetSimTime() 2227 for moduleID, times in self.lastStopTimesByItemID.items(): 2228 time, delayTime = times 2229 if time + delayTime + 5 * const.SEC < now: 2230 del self.lastStopTimesByItemID[moduleID] 2231 2232 def SendDroneSettings(self): 2233 droneSettingChanges = {const.attributeDroneIsAggressive: drones_aggressive_setting.is_enabled(), 2234 const.attributeDroneFocusFire: drones_focus_fire_setting.is_enabled()} 2235 self.GetDogmaLM().ChangeDroneSettings(droneSettingChanges) 2236 2237 def ChangeDroneSettings(self, droneSettingChanges): 2238 validDroneSettingAttribs = [const.attributeDroneIsAggressive, const.attributeDroneFocusFire] 2239 for key in droneSettingChanges: 2240 if key in validDroneSettingAttribs: 2241 self.droneSettingAttributes[key] = droneSettingChanges[key] 2242 continue 2243 else: 2244 return 2245 2246 self.GetDogmaLM().ChangeDroneSettings(droneSettingChanges) 2247 2248 def GetDroneSettingAttributes(self): 2249 return self.droneSettingAttributes 2250 2251 def RefreshTargets(self): 2252 for objectID in self.GetDogmaLM().GetTargets(): 2253 sm.ScatterEvent('OnTarget', 'add', objectID) 2254 2255 for objectID in self.GetDogmaLM().GetTargeters(): 2256 sm.ScatterEvent('OnTarget', 'otheradd', objectID) 2257 2258 def ForcePrimeLocation(self, locationIDs): 2259 keepLocationIDs = {session.charid, session.shipid, session.structureid}.difference(locationIDs) 2260 self.PurgeInventories(keepLocationIDs) 2261 primeCharacter = session.charid not in keepLocationIDs 2262 primeShip = session.shipid not in keepLocationIDs 2263 primeStructure = session.structureid not in keepLocationIDs 2264 if primeCharacter or primeShip or primeStructure: 2265 self.ProcessAllInfo(self.GetDogmaLM().GetAllInfo(primeCharacter, primeShip, primeStructure)) 2266 2267 def PrimeLocation(self, itemID, contents, locationID, priority = None, force = False): 2268 if itemID not in self.invByID or force: 2269 keys = contents.keys() 2270 for priorityID in reversed(priority or []): 2271 try: 2272 keys.remove(priorityID) 2273 keys.insert(0, priorityID) 2274 except ValueError: 2275 pass 2276 2277 for key in keys: 2278 row = contents[key] 2279 self.UpdateItem(row.invItem, row) 2280 2281 self.invByID[itemID] = sm.GetService('invCache').GetInventoryFromId(itemID, 1) 2282 else: 2283 self.invByID[itemID].locationID = locationID 2284 2285 def Prime(self): 2286 self.godma.LogInfo('Godma Prime:', session.shipid) 2287 self.priming = True 2288 try: 2289 self.primingChannel = uthread.Channel(('godma::Prime', session.shipid)) 2290 self.InitializeData() 2291 primeCharacter = session.charid and session.charid not in self.invByID 2292 primeShip = session.shipid and session.shipid not in self.invByID 2293 primeStructure = session.structureid and session.structureid not in self.invByID 2294 if primeCharacter or primeShip or primeStructure: 2295 allInfo = self.GetDogmaLM().GetAllInfo(primeCharacter, primeShip, primeStructure) 2296 self.ProcessAllInfo(allInfo) 2297 else: 2298 allInfo = None 2299 finally: 2300 self.priming = False 2301 2302 while self.primingChannel.queue: 2303 self.primingChannel.send(True) 2304 2305 self.primingChannel = None 2306 char = self.GetItem(session.charid) 2307 for att in ['memory', 2308 'perception', 2309 'willpower', 2310 'charisma', 2311 'intelligence']: 2312 sm.ScatterEvent('OnAttribute', att, char, getattr(char, att)) 2313 2314 sm.ScatterEvent('OnCapacityChange', session.shipid) 2315 if session.charid: 2316 if allInfo and allInfo.charInfo: 2317 _, charBrain = allInfo.charInfo 2318 else: 2319 charBrain = () 2320 self.dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation(charBrain=charBrain) 2321 if allInfo is not None: 2322 self.dogmaLocation.MakeShipActive(session.shipid, allInfo.shipState) 2323 2324 def ProcessAllInfo(self, allInfo): 2325 if allInfo.structureInfo: 2326 self.PrimeLocation(session.structureid, allInfo.structureInfo, session.solarsystemid, [session.structureid, session.shipid, session.charid]) 2327 if allInfo.shipInfo: 2328 forcePriming = bool(session.shipid == session.structureid and bool(allInfo.shipInfo)) 2329 self.PrimeLocation(session.shipid, allInfo.shipInfo, session.solarsystemid or session.stationid, [session.shipid, session.charid], force=forcePriming) 2330 if allInfo.charInfo: 2331 self.PrimeLocation(session.charid, allInfo.charInfo[0], session.shipid, [session.charid]) 2332 if allInfo.shipModifiedCharAttribs: 2333 self.RefreshItemAttributes(session.charid, info=allInfo.shipModifiedCharAttribs, doUpdate=True) 2334 if allInfo.systemWideEffectsOnShip: 2335 sm.ScatterEvent('OnUpdateSystemWideEffectsInfo', allInfo.systemWideEffectsOnShip) 2336 2337 def PurgeInventories(self, keepList, skipCapacityEvent = False): 2338 self.godma.LogInfo('PurgeInventories: 1, Inventories.') 2339 for itemID in self.invByID.keys(): 2340 if itemID not in keepList: 2341 self.PurgeEntries(itemID, skipCapacityEvent) 2342 else: 2343 for itemID2 in self.invitems.iterkeys(): 2344 if self.invitems[itemID2].locationID == itemID and itemID2 not in self.invByID: 2345 if itemID2 in self.effectsByItemEffect: 2346 for effect in self.effectsByItemEffect[itemID2].itervalues(): 2347 if effect.isActive and effect.effectCategory in (const.dgmEffActivation, const.dgmEffTarget) and effect.effectName != 'online': 2348 effect.isActive = 0 2349 effect.isDeactivating = False 2350 2351 self.godma.LogInfo('PurgeInventories: 2, Locations.') 2352 for locationID in self.itemsByLocationID.keys(): 2353 if locationID not in keepList: 2354 self.PurgeEntries(locationID, skipCapacityEvent) 2355 2356 def PurgeEntries(self, locationID, skipCapacityEvent = False): 2357 self.godma.LogInfo('PurgeEntries', locationID, 'with pending', self.pending) 2358 if locationID in self.pending: 2359 self.godma.LogError('PurgeEntries: failed,', locationID, 'is marked as pending.') 2360 return 2361 if not (locationID in self.invByID or locationID in self.itemsByLocationID): 2362 self.godma.LogError('PurgeEntries: failed,', locationID, 'is not primed.') 2363 return 2364 self.godma.LogInfo('PurgeEntries: Proceeding with', locationID) 2365 for itemID in self.invitems.keys(): 2366 if self.invitems[itemID].locationID == locationID and itemID not in self.invByID: 2367 self.PurgeEntry(itemID) 2368 del self.invitems[itemID] 2369 2370 for itemKey in self.sublocationsByKey.keys(): 2371 if itemKey[0] == locationID: 2372 self.PurgeEntry(itemKey) 2373 try: 2374 del self.sublocationsByKey[itemKey] 2375 del self.sublocationsByLocationFlag[locationID][itemKey[1]] 2376 except KeyError: 2377 self.godma.LogError("Failed to delete key because it wasn't there", locationID, self.sublocationsByKey, self.sublocationsByLocationFlag, itemKey) 2378 2379 self.PurgeEntry(locationID) 2380 for container in (self.invitems, self.invByID, self.itemsByLocationID): 2381 try: 2382 del container[locationID] 2383 except KeyError: 2384 pass 2385 2386 if not skipCapacityEvent: 2387 sm.ScatterEvent('OnCapacityChange', locationID) 2388 self.godma.LogInfo('PurgeEntries: Done with', locationID) 2389 2390 def PurgeEntry(self, itemKey, locationID = None): 2391 if locationID is not None: 2392 self.godma.LogInfo('PurgeEntry', itemKey, locationID) 2393 if self.IsSubLocation(itemKey): 2394 if itemKey[0] == locationID: 2395 del self.sublocationsByKey[itemKey] 2396 del self.sublocationsByLocationFlag[locationID][itemKey[1]] 2397 elif itemKey in self.invitems: 2398 del self.invitems[itemKey] 2399 if itemKey in self.itemsByLocationID[locationID]: 2400 self.itemsByLocationID[locationID].remove(itemKey) 2401 for container in (self.effectsByItemEffect, self.attributesByItemAttribute, self.chargedAttributesByItemAttribute): 2402 try: 2403 del container[itemKey] 2404 except KeyError: 2405 pass 2406 2407 def ApplyAttributeChange(self, ownerID, itemKey, attributeID, time, newValue, oldValue, scatterAttr = True): 2408 item = self._get_item(itemKey) 2409 if item is None or itemKey not in self.attributesByItemAttribute: 2410 if itemKey not in self.attributesByItemAttribute: 2411 self.godma.LogInfo('ApplyAttributeChange - item not found', item, ownerID, itemKey, attributeID, time, newValue, oldValue, scatterAttr) 2412 return 2413 attributeName = dogma.data.get_attribute_name(attributeID) 2414 att = getattr(item, attributeName, 'not there') 2415 argumentOldValue = oldValue 2416 oldValue = self.GetAttribute(itemKey, attributeName) 2417 if oldValue == newValue: 2418 self.godma.LogInfo('Reduntant update of attribute %s. Values are %s' % (attributeName, newValue)) 2419 return 2420 self.godma.LogInfo('Processing attribute %s changing from %s (server sent oldVal as %s) to %s' % (attributeName, 2421 oldValue, 2422 argumentOldValue, 2423 newValue)) 2424 self.attributesByItemAttribute[itemKey][attributeName] = newValue 2425 if attributeName in self.chargedAttributeTauCaps: 2426 if itemKey not in self.chargedAttributesByItemAttribute: 2427 self.chargedAttributesByItemAttribute[itemKey] = {} 2428 self.godma.LogInfo('Charge Attribute %s created with value %s' % (attributeName, newValue)) 2429 self.CreateChargedAttribute(itemKey, attributeName, newValue, time, self.chargedAttributeTauCaps[attributeName]) 2430 elif attributeName in self.chargedAttributeTauCapsByMax: 2431 for chargeAttName in self.chargedAttributeTauCapsByMax[attributeName]: 2432 self.godma.LogInfo('Processing chargedAttributeTauCapsByMax attribute %s for attribute %s' % (chargeAttName, attributeName)) 2433 if itemKey in self.chargedAttributesByItemAttribute: 2434 if chargeAttName in self.chargedAttributesByItemAttribute[itemKey]: 2435 charge = self.GetAttribute(itemKey, chargeAttName) 2436 if oldValue != newValue: 2437 maxChangeRatio = float(newValue) / oldValue 2438 if charge == oldValue: 2439 charge = charge * maxChangeRatio 2440 if charge > newValue: 2441 charge = newValue 2442 self.godma.LogInfo('ChargedAttributeTauCapsByMax Attribute %s re-created with value %s' % (chargeAttName, charge)) 2443 self.CreateChargedAttribute(itemKey, chargeAttName, charge, time, self.chargedAttributeTauCaps[chargeAttName]) 2444 break 2445 elif self.attributesByItemAttribute[itemKey].get(self.chargedAttributeTauCaps[chargeAttName][0]) and self.attributesByItemAttribute[itemKey].get(self.chargedAttributeTauCaps[chargeAttName][1]): 2446 self.godma.LogInfo('ChargedAttributeTauCapsByMax Attribute %s initialized with value %s' % (chargeAttName, self.attributesByItemAttribute[itemKey][chargeAttName])) 2447 self.CreateChargedAttribute(itemKey, chargeAttName, self.attributesByItemAttribute[itemKey][chargeAttName], time, self.chargedAttributeTauCaps[chargeAttName]) 2448 break 2449 2450 elif attributeName in self.chargedAttributesByTau: 2451 for attName in self.chargedAttributesByTau[attributeName]: 2452 self.godma.LogInfo('Processing chargedAttributesByTau attribute %s for attribute %s' % (attName, attributeName)) 2453 if itemKey in self.chargedAttributesByItemAttribute and attName in self.chargedAttributesByItemAttribute[itemKey]: 2454 charge = self.GetAttribute(itemKey, attName) 2455 self.godma.LogInfo('ChargedAttributesByTau %s created with value %s' % (attName, charge)) 2456 self.CreateChargedAttribute(itemKey, attName, charge, time, self.chargedAttributeTauCaps[attName]) 2457 break 2458 2459 self.godma.LogInfo('Attribute', attributeName, 'of module', itemKey, 'changed from', oldValue, 'to', newValue, 'value I had was', att) 2460 if attributeID in (const.attributeCapacity, 2461 const.attributeDroneCapacity, 2462 const.attributeShipMaintenanceBayCapacity, 2463 const.attributeFleetHangarCapacity): 2464 sm.ScatterEvent('OnCapacityChange', itemKey) 2465 elif attributeID == const.attributeQuantity: 2466 self.godma.LogInfo('Changing quantity from', att, 'to', newValue) 2467 if newValue == att: 2468 return 2469 locationID, flag, typeID = itemKey 2470 currSubLocation = self.GetSubLocation(locationID, flag) 2471 if currSubLocation and currSubLocation.typeID != typeID: 2472 self.godma.LogWarn("Trying to modify quantity of sublocation that isn't loaded with my type", currSubLocation, itemKey) 2473 return 2474 if newValue == 0: 2475 self.godma.LogInfo('SubLoc.quantityExpelled', itemKey, (oldValue, newValue)) 2476 locationID, flag = itemKey[0], itemKey[1] 2477 if locationID in self.sublocationsByLocationFlag and flag in self.sublocationsByLocationFlag[locationID]: 2478 del self.sublocationsByLocationFlag[locationID][flag] 2479 del self.sublocationsByKey[itemKey] 2480 item = blue.DBRow(self.godma.sublocrd, [itemKey, 2481 typeID, 2482 None, 2483 locationID, 2484 flag, 2485 newValue, 2486 evetypes.GetGroupID(typeID), 2487 evetypes.GetCategoryID(typeID), 2488 None]) 2489 change = {const.ixStackSize: oldValue} 2490 location = None 2491 sm.ScatterEvent('OnItemChange', item, change, location) 2492 if attributeID == const.attributeIsOnline: 2493 sm.ScatterEvent('OnModuleOnlineChange', item, oldValue, newValue) 2494 if newValue == 0 and oldValue == 1: 2495 if item.online.isActive: 2496 self.godma.LogWarn('Module put offline silently, faking event', itemKey) 2497 self.OnGodmaShipEffect(itemKey, const.effectOnline, blue.os.GetSimTime(), 0, 0, [itemKey, 2498 item.ownerID, 2499 item.locationID, 2500 None, 2501 None, 2502 [], 2503 const.effectOnline], None, -1, -1, None, None) 2504 if scatterAttr: 2505 sm.ScatterEvent('OnAttribute', attributeName, item, newValue) 2506 dogma_signals.on_attribute_changed(item, attributeID, newValue, oldValue) 2507 2508 def OnJumpCloneTransitionCompleted(self): 2509 self.priming = True 2510 self.PurgeInventories([], skipCapacityEvent=True) 2511 self.dogmaLM = None 2512 self.godma.LogInfo('OnJumpCloneTransitionCompleted: Prime (pre)') 2513 self.Prime() 2514 self.godma.LogInfo('OnJumpCloneTransitionCompleted: Prime (post)') 2515 self.priming = False 2516 if session.stationid or session.solarsystemid: 2517 dogmaLM = self.GetDogmaLM() 2518 dogmaLM.Bind() 2519 sm.StartService('skills').OnJumpCloneTransitionCompleted() 2520 2521 def CheckFutileSubSystemSwitch(self, typeID, itemID): 2522 flag = self.godma.GetTypeAttribute(typeID, const.attributeSubSystemSlot) 2523 if flag is None: 2524 return False 2525 ship = self.GetItem(session.shipid) 2526 slotOccupants = ship.GetSlotOccupants(flag) 2527 if len(slotOccupants) > 0: 2528 if slotOccupants[0].itemID == itemID: 2529 return True 2530 if slotOccupants[0].typeID == typeID: 2531 if prompt_player_fsd_dialog('SubSystemTypeAlreadyFitted', {}, uiconst.OKCANCEL) != uiconst.ID_OK: 2532 return True 2533 return False 2534 2535 def DelayedOnlineAttempt(self, shipID, moduleID, delay = 500): 2536 blue.pyos.synchro.SleepSim(delay) 2537 if shipID != session.shipid: 2538 return 2539 module = self.GetItem(moduleID) 2540 if module is None: 2541 return 2542 if 'online' not in module.effects: 2543 return 2544 effect = module.effects['online'] 2545 if effect.isActive: 2546 return 2547 effect.Activate() 2548 2549 def GetSensorStrengthAttribute(self, shipID): 2550 ret = None 2551 max = -1 2552 ship = self.GetItem(shipID) 2553 for attribute in ['scanGravimetricStrength', 2554 'scanLadarStrength', 2555 'scanMagnetometricStrength', 2556 'scanRadarStrength']: 2557 val = getattr(ship, attribute, None) 2558 if val > max: 2559 max = val 2560 ret = (attribute, val) 2561 2562 return (self.attributesByName[ret[0]].attributeID, ret[1]) 2563 2564 def GetAttributeValueByID(self, itemID, attributeID): 2565 attribute = self.attributesByID.get(attributeID, None) 2566 if attribute is None: 2567 return 2568 return self.GetAttribute(itemID, attribute.name) 2569 2570 def ChangeAmmoTypeForModule(self, moduleID, typeID): 2571 self.chargeTypeByModule[moduleID] = typeID 2572 2573 def GetAmmoTypeForModule(self, moduleID): 2574 return self.chargeTypeByModule.get(moduleID, None) 2575 2576 2577 exports = {'godma.MODULE_NOT_OVERLOADED': MODULE_NOT_OVERLOADED, 2578 'godma.MODULE_OVERLOADED': MODULE_OVERLOADED, 2579 'godma.MODULE_PENDING_OVERLOADING': MODULE_PENDING_OVERLOADING, 2580 'godma.MODULE_PENDING_STOPOVERLOADING': MODULE_PENDING_STOPOVERLOADING}