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

shipFighterState.py (18700B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\inflight\squadrons\shipFighterState.py
      2 from collections import namedtuple, defaultdict
      3 from eve.client.script.ui.services.menuSvcExtras import movementFunctions
      4 from fighters import ABILITY_SLOT_IDS, GetAbilityIDForSlot
      5 from fighters.client import ConvertAbilityFailureReason
      6 import gametime
      7 import signals
      8 import logging
      9 from eve.common.script.mgt.fighterConst import TUBE_STATE_READY, TUBE_STATE_EMPTY, TUBE_STATE_LANDING, TUBE_STATE_REFUELLING, TUBE_STATE_INSPACE
     10 logger = logging.getLogger('ShipFighterState')
     11 FighterInSpaceTuple = namedtuple('FighterInSpaceTuple', ['itemID',
     12  'typeID',
     13  'tubeFlagID',
     14  'squadronSize'])
     15 FighterInTubeTuple = namedtuple('FighterInTubeTuple', ['itemID',
     16  'typeID',
     17  'tubeFlagID',
     18  'squadronSize'])
     19 TubeStatusTuple = namedtuple('TubeStatusTuple', ['statusID', 'startTime', 'endTime'])
     20 AbilityActivationStatusTuple = namedtuple('AbilityActivationStatusTuple', ['isPending',
     21  'startTime',
     22  'durationMs',
     23  'isDeactivating'])
     24 EMPTY_TUBE = TubeStatusTuple(statusID=TUBE_STATE_EMPTY, startTime=None, endTime=None)
     25 
     26 class ShipFighterState(object):
     27     __notifyevents__ = ['OnSessionChanged',
     28      'OnFighterTubeContentUpdate',
     29      'OnFighterTubeContentEmpty',
     30      'OnFighterAddedToController',
     31      'OnFighterRemovedFromController',
     32      'OnFighterTubeTaskStatus',
     33      'OnFighterAbilitySlotActivated',
     34      'OnFighterAbilitySlotDeactivated',
     35      'OnInSpaceSquadronSizeChanged',
     36      'OnEwarStart',
     37      'OnEwarEnd']
     38     fightersSvc = None
     39     fightersInLaunchTubes = None
     40     fightersInSpaceByID = None
     41     fightersInSpaceByTube = None
     42     statusByTube = None
     43     activeAbilities = None
     44     abilityChargeCounts = None
     45     abilityTargetTracker = None
     46     incomingEwarByFighterID = None
     47     signalOnFighterTubeStateUpdate = None
     48     signalOnFighterTubeContentUpdate = None
     49     signalOnFighterInSpaceUpdate = None
     50     signalOnAbilityActivationStatusUpdate = None
     51     signalOnIncomingEwarStarted = None
     52     signalOnIncomingEwarEnded = None
     53     signalOnShipLocationChange = None
     54 
     55     def __init__(self, fightersSvc):
     56         self.fightersSvc = fightersSvc
     57         sm.RegisterNotify(self)
     58         self.fightersInLaunchTubes = {}
     59         self.fightersInSpaceByID = {}
     60         self.fightersInSpaceByTube = {}
     61         self.statusByTube = {}
     62         self.activeAbilities = {}
     63         self.abilityChargeCounts = {}
     64         self.abilityCooldowns = {}
     65         self.incomingEwarByFighterID = defaultdict(dict)
     66         self.abilityTargetTracker = AbilityTargetTracker()
     67         if session.shipid:
     68             self._UpdateStateForShip()
     69         self.signalOnFighterTubeStateUpdate = signals.Signal(signalName='signalOnFighterTubeStateUpdate')
     70         self.signalOnFighterTubeContentUpdate = signals.Signal(signalName='signalOnFighterTubeContentUpdate')
     71         self.signalOnFighterInSpaceUpdate = signals.Signal(signalName='signalOnFighterInSpaceUpdate')
     72         self.signalOnAbilityActivationStatusUpdate = signals.Signal(signalName='signalOnAbilityActivationStatusUpdate')
     73         self.signalOnIncomingEwarStarted = signals.Signal(signalName='signalOnIncomingEwarStarted')
     74         self.signalOnIncomingEwarEnded = signals.Signal(signalName='signalOnIncomingEwarEnded')
     75         self.signalOnShipLocationChange = signals.Signal(signalName='signalOnShipLocationChange')
     76 
     77     def OnSessionChanged(self, isRemote, sess, change):
     78         if 'shipid' in change:
     79             self._UpdateStateForShip()
     80         if 'locationid' in change or 'structureid' in change:
     81             for tubeFlagID, tubeStatus in self.statusByTube.iteritems():
     82                 if self.GetFightersInTube(tubeFlagID):
     83                     self._SetTubeStatus(tubeFlagID, TUBE_STATE_READY, None, None)
     84                 else:
     85                     self._SetTubeStatus(tubeFlagID, TUBE_STATE_EMPTY, None, None)
     86 
     87             self.signalOnShipLocationChange()
     88             self.abilityTargetTracker.FlushAllTargets()
     89 
     90     def OnFighterTubeContentUpdate(self, tubeFlagID, fighterID, fighterTypeID, squadronSize):
     91         fighterInTube = FighterInTubeTuple(fighterID, fighterTypeID, tubeFlagID, squadronSize)
     92         self.fightersInLaunchTubes[tubeFlagID] = fighterInTube
     93         self.signalOnFighterTubeContentUpdate(tubeFlagID)
     94 
     95     def OnFighterTubeContentEmpty(self, tubeFlagID):
     96         del self.fightersInLaunchTubes[tubeFlagID]
     97         self.signalOnFighterTubeContentUpdate(tubeFlagID)
     98 
     99     def _UpdateAbilitySlotStates(self, fighterID, abilitySlotStates):
    100         chargeStates, cooldownStates = abilitySlotStates
    101         for abilitySlotID, chargeCount in chargeStates.iteritems():
    102             self.abilityChargeCounts[fighterID, abilitySlotID] = chargeCount
    103 
    104         for abilitySlotID, cooldown in cooldownStates.iteritems():
    105             self.abilityCooldowns[fighterID, abilitySlotID] = cooldown
    106 
    107     def OnFighterAddedToController(self, fighterID, fighterTypeID, tubeFlagID, squadronSize, abilitySlotStates):
    108         logger.debug('OnFighterAddedToController %s', (fighterID,
    109          fighterTypeID,
    110          tubeFlagID,
    111          squadronSize,
    112          abilitySlotStates))
    113         fighterInSpace = FighterInSpaceTuple(fighterID, fighterTypeID, tubeFlagID, squadronSize)
    114         self.fightersInSpaceByID[fighterID] = fighterInSpace
    115         self.fightersInSpaceByTube[tubeFlagID] = fighterInSpace
    116         self._UpdateAbilitySlotStates(fighterID, abilitySlotStates)
    117         self.signalOnFighterInSpaceUpdate(fighterID, tubeFlagID)
    118 
    119     def OnFighterRemovedFromController(self, fighterID, tubeFlagID):
    120         logger.debug('OnFighterRemovedFromController %s', (fighterID, tubeFlagID))
    121         self.fightersInSpaceByID.pop(fighterID)
    122         self.fightersInSpaceByTube.pop(tubeFlagID)
    123         self.incomingEwarByFighterID.pop(fighterID, None)
    124         self._DiscardActivatedAbilitiesForFighter(fighterID)
    125         for abilitySlotID in ABILITY_SLOT_IDS:
    126             self.abilityChargeCounts.pop((fighterID, abilitySlotID), None)
    127             self.abilityCooldowns.pop((fighterID, abilitySlotID), None)
    128 
    129         self.abilityTargetTracker.RemoveFighter(fighterID)
    130         self.signalOnFighterInSpaceUpdate(fighterID, tubeFlagID)
    131         if self.GetTubeStatus(tubeFlagID).statusID not in (TUBE_STATE_LANDING, TUBE_STATE_REFUELLING):
    132             self._SetTubeStatus(tubeFlagID, TUBE_STATE_EMPTY, None, None)
    133             self.signalOnFighterTubeStateUpdate(tubeFlagID)
    134         sm.ScatterEvent('OnFighterRemovedFromControllerClient', fighterID)
    135 
    136     def _DiscardActivatedAbilitiesForFighter(self, fighterID):
    137         for abilitySlotID in ABILITY_SLOT_IDS:
    138             self.activeAbilities.pop((fighterID, abilitySlotID), None)
    139 
    140     def OnFighterTubeTaskStatus(self, tubeFlagID, statusID, statusStartTime, statusEndTime, userError = None):
    141         logger.debug('OnFighterTubeTaskStatus %s', (tubeFlagID,
    142          statusID,
    143          statusStartTime,
    144          statusEndTime,
    145          userError))
    146         if userError is not None:
    147             sm.GetService('gameui').OnRemoteMessage(*userError.args)
    148         self._SetTubeStatus(tubeFlagID, statusID, statusStartTime, statusEndTime)
    149         self.signalOnFighterTubeStateUpdate(tubeFlagID)
    150 
    151     def OnAbilityActivationPending(self, fighterID, abilitySlotID):
    152         self.activeAbilities[fighterID, abilitySlotID] = AbilityActivationStatusTuple(True, None, None, False)
    153         self.signalOnAbilityActivationStatusUpdate(fighterID, abilitySlotID)
    154 
    155     def OnFighterAbilitySlotActivated(self, fighterID, abilitySlotID, remainingChargeCount, startTime, durationMs, cooldown):
    156         logger.debug('OnFighterAbilitySlotActivated %s', (fighterID,
    157          abilitySlotID,
    158          remainingChargeCount,
    159          startTime,
    160          durationMs,
    161          cooldown))
    162         self.activeAbilities[fighterID, abilitySlotID] = AbilityActivationStatusTuple(False, startTime, durationMs, False)
    163         if remainingChargeCount is None:
    164             self.abilityChargeCounts.pop((fighterID, abilitySlotID), None)
    165         else:
    166             self.abilityChargeCounts[fighterID, abilitySlotID] = remainingChargeCount
    167         if cooldown is None:
    168             self.abilityCooldowns.pop((fighterID, abilitySlotID), None)
    169         else:
    170             self.abilityCooldowns[fighterID, abilitySlotID] = cooldown
    171         self.signalOnAbilityActivationStatusUpdate(fighterID, abilitySlotID)
    172 
    173     def OnAbilityDeactivationPending(self, fighterID, abilitySlotID):
    174         currentActivationStatus = self.activeAbilities.pop((fighterID, abilitySlotID), None)
    175         if currentActivationStatus:
    176             newActivationStatus = currentActivationStatus._replace(isDeactivating=True)
    177             self.activeAbilities[fighterID, abilitySlotID] = newActivationStatus
    178             self.signalOnAbilityActivationStatusUpdate(fighterID, abilitySlotID)
    179 
    180     def OnFighterAbilitySlotDeactivated(self, fighterID, abilitySlotID, failureReason):
    181         logger.debug('OnFighterAbilitySlotDeactivated %s', (fighterID, abilitySlotID, failureReason))
    182         self.activeAbilities.pop((fighterID, abilitySlotID), None)
    183         self.abilityTargetTracker.RemoveAbilityTarget(fighterID, abilitySlotID)
    184         self.signalOnAbilityActivationStatusUpdate(fighterID, abilitySlotID)
    185         if failureReason is not None:
    186             self._ShowAbilityFailureReason(fighterID, abilitySlotID, failureReason)
    187 
    188     def _ShowAbilityFailureReason(self, fighterID, abilitySlotID, failureReasonUserError):
    189         failureReasonUserError = ConvertAbilityFailureReason(fighterID, abilitySlotID, failureReasonUserError)
    190         sm.GetService('gameui').OnRemoteMessage(*failureReasonUserError.args)
    191 
    192     def OnInSpaceSquadronSizeChanged(self, fighterID, squadronSize):
    193         logger.debug('OnInSpaceSquadronSizeChanged %s', (fighterID, squadronSize))
    194         fighterInSpace = self.fightersInSpaceByID.get(fighterID, None)
    195         if fighterInSpace is None:
    196             return
    197         newFighterInSpace = fighterInSpace._replace(squadronSize=squadronSize)
    198         self.fightersInSpaceByID[fighterID] = newFighterInSpace
    199         self.fightersInSpaceByTube[fighterInSpace.tubeFlagID] = newFighterInSpace
    200         self.signalOnFighterInSpaceUpdate(fighterID, fighterInSpace.tubeFlagID)
    201 
    202     def _UpdateStateForShip(self):
    203         self.fightersInLaunchTubes.clear()
    204         self.fightersInSpaceByID.clear()
    205         self.fightersInSpaceByTube.clear()
    206         self.statusByTube.clear()
    207         self.activeAbilities.clear()
    208         self.abilityChargeCounts.clear()
    209         self.abilityCooldowns.clear()
    210         self.incomingEwarByFighterID.clear()
    211         self.abilityTargetTracker.FlushAllTargets()
    212         fightersInTubes, fightersInSpace, abilityStates = self.fightersSvc.GetFightersForShip()
    213         for tubeFlagID, fighterItemID, fighterTypeID, squadronSize in fightersInTubes:
    214             fighterInTube = FighterInTubeTuple(fighterItemID, fighterTypeID, tubeFlagID, squadronSize)
    215             self.fightersInLaunchTubes[tubeFlagID] = fighterInTube
    216             self._SetTubeStatus(tubeFlagID, TUBE_STATE_READY, None, None)
    217             self.signalOnFighterTubeContentUpdate(tubeFlagID)
    218             self.signalOnFighterTubeStateUpdate(tubeFlagID)
    219 
    220         for tubeFlagID, fighterItemID, fighterTypeID, squadronSize in fightersInSpace:
    221             fighterInSpace = FighterInSpaceTuple(fighterItemID, fighterTypeID, tubeFlagID, squadronSize)
    222             self.fightersInSpaceByID[fighterItemID] = fighterInSpace
    223             self.fightersInSpaceByTube[tubeFlagID] = fighterInSpace
    224             self._SetTubeStatus(tubeFlagID, TUBE_STATE_INSPACE, None, None)
    225             self.signalOnFighterInSpaceUpdate(fighterItemID, tubeFlagID)
    226             self.signalOnFighterTubeStateUpdate(tubeFlagID)
    227 
    228         for fighterID, abilitySlotStates in abilityStates.iteritems():
    229             self._UpdateAbilitySlotStates(fighterID, abilitySlotStates)
    230 
    231     def _SetTubeStatus(self, tubeFlagID, statusID, startTime, endTime):
    232         self.statusByTube[tubeFlagID] = TubeStatusTuple(statusID=statusID, startTime=startTime, endTime=endTime)
    233 
    234     def GetFightersInTube(self, tubeFlagID):
    235         return self.fightersInLaunchTubes.get(tubeFlagID)
    236 
    237     def GetFightersInSpace(self, tubeFlagID):
    238         return self.fightersInSpaceByTube.get(tubeFlagID)
    239 
    240     def GetAllFightersIDs(self):
    241         return [ f.itemID for f in self.GetAllFighters() ]
    242 
    243     def GetAllFighters(self):
    244         return self.fightersInLaunchTubes.values() + self.fightersInSpaceByID.values()
    245 
    246     def GetFightersForTubeID(self, tubeFlagID):
    247         return self.GetFightersInTube(tubeFlagID) or self.GetFightersInSpace(tubeFlagID)
    248 
    249     def GetFighterInSpaceByID(self, fighterID):
    250         return self.fightersInSpaceByID.get(fighterID)
    251 
    252     def GetAllFighterIDsInSpace(self):
    253         return self.fightersInSpaceByID.keys()
    254 
    255     def GetTubeStatus(self, tubeFlagID):
    256         return self.statusByTube.get(tubeFlagID, EMPTY_TUBE)
    257 
    258     def IsMyFighterInSpace(self, itemID):
    259         return itemID in self.GetAllFighterIDsInSpace()
    260 
    261     def IsAllAbilitiesInSlotActiveOrInCooldown(self, abilitySlotID):
    262         for fighterID in self.GetSelectedFightersInSpace():
    263             if self.GetAbilityIDForSlot(fighterID, abilitySlotID) is None:
    264                 continue
    265             if not self.IsAbilityActive(fighterID, abilitySlotID) and self.GetAbilityCooldown(fighterID, abilitySlotID) is None:
    266                 return False
    267 
    268         return True
    269 
    270     def GetAbilityIDForSlot(self, fighterID, abilitySlotID):
    271         fighter = self.GetFighterInSpaceByID(fighterID)
    272         if fighter:
    273             return GetAbilityIDForSlot(fighter.typeID, abilitySlotID)
    274 
    275     def GetSelectedFightersInSpace(self):
    276         return movementFunctions.GetFightersSelectedForNavigation()
    277 
    278     def IsAbilityActive(self, fighterID, abilitySlotID):
    279         return (fighterID, abilitySlotID) in self.activeAbilities
    280 
    281     def GetAbilityChargeCount(self, fighterID, abilitySlotID):
    282         return self.abilityChargeCounts.get((fighterID, abilitySlotID), None)
    283 
    284     def GetAbilityCooldown(self, fighterID, abilitySlotID):
    285         cooldown = self.abilityCooldowns.get((fighterID, abilitySlotID), None)
    286         if cooldown is not None:
    287             startTime, endTime = cooldown
    288             if startTime <= gametime.GetSimTime() < endTime:
    289                 return (startTime, endTime)
    290 
    291     def GetAbilityActivationStatus(self, fighterID, abilitySlotID):
    292         return self.activeAbilities.get((fighterID, abilitySlotID), None)
    293 
    294     def OnAbilityActivatedAtTarget(self, fighterID, abilitySlotID, targetID):
    295         self.abilityTargetTracker.RegisterAbilityTarget(fighterID, abilitySlotID, targetID)
    296 
    297     def GetAbilityTargetsForFighter(self, fighterID):
    298         return self.abilityTargetTracker.GetAbilityTargetsForFighter(fighterID)
    299 
    300     def ConnectFighterTargetUpdatedHandler(self, handler):
    301         return self.abilityTargetTracker.signalOnAbilityTargetsUpdate.connect(handler)
    302 
    303     def DisconnectFighterTargetUpdatedHandler(self, handler):
    304         return self.abilityTargetTracker.signalOnAbilityTargetsUpdate.disconnect(handler)
    305 
    306     def OnEwarStart(self, sourceBallID, sourceModuleID, targetBallID, jammingType):
    307         if targetBallID not in self.fightersInSpaceByID:
    308             return
    309         logger.debug('ShipFighterState.OnEwarStart %s', (sourceBallID,
    310          sourceModuleID,
    311          targetBallID,
    312          jammingType))
    313         self.incomingEwarByFighterID[targetBallID][sourceModuleID] = (sourceBallID, jammingType)
    314         self.signalOnIncomingEwarStarted(targetBallID, sourceBallID, sourceModuleID, jammingType)
    315 
    316     def OnEwarEnd(self, sourceBallID, sourceModuleID, targetBallID, jammingType):
    317         fighterEwar = self.incomingEwarByFighterID.get(targetBallID, None)
    318         if fighterEwar is None:
    319             return
    320         ewar = fighterEwar.pop(sourceModuleID, None)
    321         if ewar is None:
    322             return
    323         logger.debug('ShipFighterState.OnEwarEnd %s', (sourceBallID,
    324          sourceModuleID,
    325          targetBallID,
    326          jammingType))
    327         self.signalOnIncomingEwarEnded(targetBallID, sourceBallID, sourceModuleID, jammingType)
    328 
    329     def GetIncomingFighterEwar(self, fighterID):
    330         ewarByModuleID = self.incomingEwarByFighterID.get(fighterID, None)
    331         if ewarByModuleID is None:
    332             return []
    333         return [ (sourceModuleID, sourceBallID, jammingType) for sourceModuleID, (sourceBallID, jammingType) in ewarByModuleID.iteritems() ]
    334 
    335     def KillEffectsFromBall(self, ballID):
    336         for eachTargetID, eachAttackerDict in self.incomingEwarByFighterID.iteritems():
    337             effectsForBall = eachAttackerDict.get(ballID, None)
    338             if effectsForBall:
    339                 sourceBallID, jammingType = effectsForBall
    340                 self.OnEwarEnd(sourceBallID, ballID, eachTargetID, jammingType)
    341 
    342 
    343 class AbilityTargetTracker(object):
    344     targetsByFighterSlot = None
    345     signalOnAbilityTargetsUpdate = None
    346 
    347     def __init__(self):
    348         self.targetsByFighterSlot = defaultdict(dict)
    349         self.signalOnAbilityTargetsUpdate = signals.Signal(signalName='signalOnAbilityTargetsUpdate')
    350 
    351     def RegisterAbilityTarget(self, fighterID, abilitySlotID, targetID):
    352         self.targetsByFighterSlot[fighterID][abilitySlotID] = targetID
    353         self.signalOnAbilityTargetsUpdate(fighterID, abilitySlotID, targetID)
    354 
    355     def GetAbilityTargetsForFighter(self, fighterID):
    356         return set(self.targetsByFighterSlot.get(fighterID, {}).itervalues())
    357 
    358     def GetFighterAbilitiesForTarget(self, targetID):
    359         abilitiesOnTarget = []
    360         for fighterID, fighterAbilities in self.targetsByFighterSlot.iteritems():
    361             for abilitySlotID, abilityTargetID in fighterAbilities.iteritems():
    362                 if abilityTargetID == targetID:
    363                     abilitiesOnTarget.append((fighterID, abilitySlotID))
    364 
    365         return abilitiesOnTarget
    366 
    367     def RemoveFighter(self, fighterID):
    368         fighterAbilities = self.targetsByFighterSlot.pop(fighterID, None)
    369         if fighterAbilities:
    370             for abilitySlotID, targetID in fighterAbilities.iteritems():
    371                 self.signalOnAbilityTargetsUpdate(fighterID, abilitySlotID, targetID)
    372 
    373     def RemoveAbilityTarget(self, fighterID, abilitySlotID):
    374         fighterAbilities = self.targetsByFighterSlot.get(fighterID, None)
    375         if fighterAbilities:
    376             targetID = fighterAbilities.pop(abilitySlotID, None)
    377             if targetID:
    378                 self.signalOnAbilityTargetsUpdate(fighterID, abilitySlotID, targetID)
    379 
    380     def FlushAllTargets(self):
    381         for fighterID in self.targetsByFighterSlot.keys():
    382             self.RemoveFighter(fighterID)
    383 
    384 
    385 def GetShipFighterState():
    386     return sm.GetService('fighters').shipFighterState