NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

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}