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

combat.py (14333B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\behaviors\actions\combat.py
      2 import logging
      3 import random
      4 import uthread2
      5 from ballpark.const import DESTINY_MODE_MISSILE
      6 from ballpark.messenger.const import MESSAGE_ENTITY_SUPER_WEAPON_STOPPED
      7 from behaviors import status
      8 from behaviors.const.behaviorroles import COMBAT_EFFECTS_BY_ROLES
      9 from behaviors.tasks import Task
     10 from behaviors.utility.dogmatic import can_activate_effect_on_target, is_my_effect_active_on_target, try_activate_effect
     11 from ccpProfile import TimedFunction
     12 from dogma.const import attributeExplosiveDamage, attributeKineticDamage, attributeThermalDamage
     13 from dogma.const import attributeFighterAbilityKamikazeDamageEM, attributeFighterAbilityKamikazeDamageExp
     14 from dogma.const import attributeFighterAbilityKamikazeDamageKin
     15 from dogma.const import attributeFighterAbilityKamikazeRange, attributeFighterSquadronSize
     16 from dogma.const import attributeFighterAbilityKamikazeSignatureRadius, attributeEmDamage
     17 from dogma.const import effectEntitySuperWeapon
     18 from dogma.const import effectEntitySuperWeaponLanceAllRaces
     19 from eve.server.script.mgt.fighters import GetControlledFighterRegistryForSolarsystem
     20 from eveexceptions import UserError
     21 logger = logging.getLogger(__name__)
     22 MIN_THREAT_VALUE = float('-inf')
     23 
     24 class ActivateSuperWeaponOnTarget(Task):
     25 
     26     @TimedFunction('behaviors::actions::combat::ActivateSuperWeaponOnTarget::OnEnter')
     27     def OnEnter(self):
     28         selectedTarget = self.GetLastBlackboardValue(self.attributes.selectedTargetAddress)
     29         self.SetStatusToFailed()
     30         item = self.context.ballpark.inventory2.GetItem(self.context.myItemId)
     31         if selectedTarget:
     32             if self.context.dogmaLM.dogmaStaticMgr.TypeHasEffect(item.typeID, effectEntitySuperWeapon):
     33                 self.SetStatusToSuspended()
     34                 self.TryActivateEffect(effectEntitySuperWeapon, item.ownerID, selectedTarget)
     35             if self.context.dogmaLM.dogmaStaticMgr.TypeHasEffect(item.typeID, effectEntitySuperWeaponLanceAllRaces):
     36                 self.SetStatusToSuspended()
     37                 self.TryActivateEffect(effectEntitySuperWeaponLanceAllRaces, item.ownerID, selectedTarget)
     38 
     39     @TimedFunction('behaviors::actions::combat::ActivateSuperWeaponOnTarget::TryActivateEffect')
     40     def TryActivateEffect(self, effectID, ownerID, targetID):
     41         try:
     42             self.SubscribeItem(self.context.myItemId, MESSAGE_ENTITY_SUPER_WEAPON_STOPPED, self.OnSuperWeaponStopped)
     43             self.context.dogmaLM.ActivateWithContext(ownerID, self.context.myItemId, effectID, targetid=targetID)
     44             logger.debug('SuperWeapon: %s: started super weapon on target %s with effect %s', self.context.myItemId, targetID, effectID)
     45         except UserError:
     46             logger.warn('SuperWeapon: %s: failed to activate on %s with effect %s', self.context.myItemId, targetID, effectID)
     47         except RuntimeError:
     48             logger.exception('Entity: %s - unexpected exception when starting effect %s on target %s', self.context.myItemId, effectID, targetID)
     49 
     50     def OnSuperWeaponStopped(self):
     51         if not self.IsInvalid():
     52             self.status = status.TaskSuccessStatus
     53             self.status.OnUpdated(self)
     54             logger.debug('SuperWeapon: %s: super weapon cycle stopped', self.context.myItemId)
     55 
     56     def CleanUp(self):
     57         if not self.IsInvalid():
     58             self.UnsubscribeItem(self.context.myItemId, MESSAGE_ENTITY_SUPER_WEAPON_STOPPED, self.OnSuperWeaponStopped)
     59             Task.CleanUp(self)
     60 
     61 
     62 class AddCharacterForPodding(Task):
     63 
     64     @TimedFunction('behaviors::actions::combat::AddCharacterForPodding::OnEnter')
     65     def OnEnter(self):
     66         characterIdForPodding = self.GetLastBlackboardValue(self.attributes.characterAddress)
     67         if characterIdForPodding:
     68             self.UpdatePoddingList(characterIdForPodding)
     69         self.status = status.TaskSuccessStatus
     70 
     71     def UpdatePoddingList(self, characterIdForPodding):
     72         poddingList = self.GetLastBlackboardValue(self.attributes.poddingTargetListAddress)
     73         if poddingList is None:
     74             poddingList = set()
     75         if characterIdForPodding not in poddingList:
     76             poddingList.add(characterIdForPodding)
     77             self.SendBlackboardValue(self.attributes.poddingTargetListAddress, poddingList)
     78 
     79 
     80 class KamikazeAttack(Task):
     81     explosionDelaySeconds = 1.0
     82     explosionScheduled = False
     83 
     84     def _ScheduleExplosion(self, targetId):
     85         if not self.explosionScheduled:
     86             self.explosionScheduled = True
     87             uthread2.call_after_simtime_delay(self._ExplodeSquadron, self.explosionDelaySeconds, targetId)
     88 
     89     def EngageTarget(self, targetId):
     90         myBall = self.context.myBall
     91         notifyRange = self.context.ballpark.dogmaLM.GetAttributeValue(self.context.myItemId, attributeFighterAbilityKamikazeRange)
     92         distBetweenBalls = self.context.ballpark.GetSurfaceDist(myBall.id, targetId)
     93         if distBetweenBalls <= 0.0:
     94             self._ScheduleExplosion(targetId)
     95         if self.context.myBall.mode == DESTINY_MODE_MISSILE:
     96             self.SetStatusToRunning()
     97             return
     98         if distBetweenBalls <= notifyRange:
     99             self.DoTargetTracking()
    100             self.SetStatusToRunning()
    101             return
    102         myBall.ApproachObject(targetId, approachRange=-999999.0, cruise=False)
    103         self.context.myBall.DoTargetTracking = self.DoTargetTracking
    104         self.context.myBall.DoCollision = self.DoCollision
    105         self.context.ballpark.SetTargetTracking(self.context.myItemId, targetId, notifyRange)
    106         self.SetStatusToRunning()
    107 
    108     @TimedFunction('behaviors::actions::combat::KamikazeAttack::OnEnter')
    109     def OnEnter(self):
    110         selfItem = self.GetSelfAsItem()
    111         if not selfItem:
    112             self.SetStatusToFailed()
    113             return
    114         self.Update()
    115 
    116     def _GetTargetBall(self):
    117         targetId = self.GetLastBlackboardValue(self.attributes.selectedTargetAddress)
    118         return self.context.ballpark.GetBallOrNone(targetId)
    119 
    120     def Abort(self):
    121 
    122         def noop(*args, **kwargs):
    123             pass
    124 
    125         if self.context.myBall.mode == DESTINY_MODE_MISSILE:
    126             self.SendBlackboardValue(self.attributes.shouldAbortAddress, False)
    127             targetBall = self._GetTargetBall()
    128             if targetBall is None:
    129                 self.context.ballpark.ExplodeEx(self.context.myBall.id)
    130             return
    131         self.context.myBall.DoCollision = noop
    132         self.context.myBall.DoTargetTracking = noop
    133         self.context.ballpark.StopEx(self.context.myItemId)
    134         self.SendBlackboardValue(self.attributes.shouldAbortAddress, False)
    135         self.SetStatusToSuccess()
    136 
    137     def Update(self):
    138         shouldAbort = self.GetLastBlackboardValue(self.attributes.shouldAbortAddress)
    139         targetBall = self._GetTargetBall()
    140         if targetBall is None or shouldAbort:
    141             self.Abort()
    142             return
    143         self.EngageTarget(targetBall.id)
    144 
    145     def GetSelfAsItem(self):
    146         return self.context.ballpark.inventory2.GetItem(self.context.myItemId)
    147 
    148     def GetTargetAsItem(self, targetId):
    149         return self.context.ballpark.inventory2.GetItem(targetId)
    150 
    151     def _ApplyDamageToTarget(self, fighterID):
    152         targetBall = self._GetTargetBall()
    153         if targetBall is None:
    154             return
    155         GAV = self.context.ballpark.dogmaLM.GetAttributeValue
    156         squadronSizeMultiplier = GAV(self.context.myItemId, attributeFighterSquadronSize)
    157         cloudSize = GAV(fighterID, attributeFighterAbilityKamikazeSignatureRadius)
    158         cloudFactor = self.context.ballpark.dogmaLM.GetCloudFactor(targetBall.id, cloudSize)
    159         damageAmount = squadronSizeMultiplier * cloudFactor
    160         damageByType = {attributeEmDamage: GAV(fighterID, attributeFighterAbilityKamikazeDamageEM),
    161          attributeExplosiveDamage: GAV(fighterID, attributeFighterAbilityKamikazeDamageExp),
    162          attributeKineticDamage: GAV(fighterID, attributeFighterAbilityKamikazeDamageKin),
    163          attributeThermalDamage: GAV(fighterID, attributeThermalDamage)}
    164         dogma = sm.GetService('dogma')
    165         dogma.ApplyDamage(None, self.context.myItemId, targetBall.id, damageAmount, indirectDogmaLM=self.context.ballpark.dogmaLM, splash=False, damageShieldOnly=False, sourceID=fighterID, weaponDamages=damageByType)
    166 
    167     def _ExplodeSquadron(self, targetID):
    168         fighterID = self.context.myItemId
    169         self._ApplyDamageToTarget(fighterID)
    170         self.context.ballpark.ExplodeEx(fighterID)
    171         self.SetStatusToSuccess()
    172 
    173     def DoCollision(self, otherID, otherX, otherY, otherZ):
    174         targetID = self.GetLastBlackboardValue(self.attributes.selectedTargetAddress)
    175         if otherID == targetID:
    176             self._ScheduleExplosion(targetID)
    177 
    178     def DoTargetTracking(self, *args):
    179         uthread2.start_tasklet(self._DoTargetTracking_Threaded, args)
    180 
    181     def _DoTargetTracking_Threaded(self, *args):
    182         targetID = self.GetLastBlackboardValue(self.attributes.selectedTargetAddress)
    183         if targetID is None:
    184             return
    185         fighterID = self.context.myItemId
    186         controllerRegistry = GetControlledFighterRegistryForSolarsystem(self.context.ballpark.solarsystemID)
    187         controllerID = controllerRegistry.GetControllerForFighter(self.context.myItemId)
    188         if controllerID is None:
    189             controllerID = 0
    190         self.context.ballpark.MissileFollow(fighterID, targetID, controllerID)
    191         self.context.ballpark.SetBallMassive(fighterID, False)
    192 
    193 
    194 class ActivateCombatEffectsOnTarget(Task):
    195 
    196     @TimedFunction('behaviors::actions::combat::ActivateCombatEffectsOnTarget::OnEnter')
    197     def OnEnter(self):
    198         self.SetStatusToFailed()
    199         selfItem = self.GetSelfAsItem()
    200         targetId = self.GetLastBlackboardValue(self.attributes.selectedTargetAddress)
    201         if targetId is None:
    202             return
    203         targetItem = self.GetTargetAsItem(targetId)
    204         if not selfItem or not targetItem:
    205             return
    206         self._ActivateCombatEffectsOnTarget(targetId, selfItem)
    207 
    208     def _ActivateCombatEffectsOnTarget(self, targetId, selfItem):
    209         reactionDelay = self._GetReactionDelay()
    210         for effectId in self.attributes.effectIds:
    211             if self._is_my_effect_active_on_target(effectId, targetId):
    212                 self.SetStatusToSuccess()
    213                 continue
    214             if self.CanActivateEffectOnTarget(effectId, selfItem.typeID, targetId):
    215                 self._TryActiveEffect(effectId, targetId, reactionDelay)
    216                 self.SetStatusToSuccess()
    217 
    218     def _TryActiveEffect(self, effectId, targetId, reactionDelay):
    219         uthread2.start_tasklet(try_activate_effect, self, effectId, self.attributes.repeat, target_id=targetId, delay=reactionDelay)
    220 
    221     def GetSelfAsItem(self):
    222         return self.context.ballpark.inventory2.GetItem(self.context.myItemId)
    223 
    224     def GetTargetAsItem(self, targetId):
    225         return self.context.ballpark.inventory2.GetItem(targetId)
    226 
    227     @TimedFunction('behaviors::actions::combat::ActivateCombatEffectsOnTarget::CanActivateEffectOnTarget')
    228     def CanActivateEffectOnTarget(self, effectId, typeId, targetId):
    229         return can_activate_effect_on_target(self, typeId, effectId, targetId)
    230 
    231     def _GetReactionDelay(self):
    232         if self.HasAttribute('reactionDelayMilliseconds'):
    233             return random.uniform(0.0, float(self.attributes.reactionDelayMilliseconds) / 1000.0)
    234         return random.random()
    235 
    236     def _is_my_effect_active_on_target(self, effectId, targetId):
    237         return is_my_effect_active_on_target(self, effectId, targetId)
    238 
    239 
    240 class StopCombatEffectsOnTarget(Task):
    241 
    242     @TimedFunction('behaviors::actions::combat::StopCombatEffectsOnTarget::OnEnter')
    243     def OnEnter(self):
    244         self.status = status.TaskSuccessStatus
    245         item = self.context.ballpark.inventory2.GetItem(self.context.myItemId)
    246         for effectId in self.attributes.effectIds:
    247             if self.context.dogmaLM.dogmaStaticMgr.TypeHasEffect(item.typeID, effectId):
    248                 self.TryStopEffect(effectId)
    249 
    250     @TimedFunction('behaviors::actions::combat::StopCombatEffectsOnTarget::TryStopEffect')
    251     def TryStopEffect(self, effectId):
    252         try:
    253             self.context.dogmaLM.StopEffect(effectId, self.context.myItemId, forced=True)
    254             logger.debug('CeaseFireOnTarget: %s: stopped attacking with effect %s', self.context.myItemId, effectId)
    255         except UserError:
    256             pass
    257 
    258 
    259 class OwnerListFilterAction(Task):
    260 
    261     @TimedFunction('behaviors::actions::combat::OwnerListFilterAction::OnEnter')
    262     def OnEnter(self):
    263         self.SetStatusToSuccess()
    264         itemIdSet = self.GetLastBlackboardValue(self.attributes.itemIdSetAddress)
    265         if not itemIdSet:
    266             return
    267         filteredItemIdSet = self.FilterSet(itemIdSet)
    268         if filteredItemIdSet != itemIdSet:
    269             self.SendBlackboardValue(self.attributes.itemIdSetAddress, filteredItemIdSet)
    270 
    271     def FilterSet(self, itemIdSet):
    272         filteredSet = itemIdSet.copy()
    273         for itemId in list(itemIdSet):
    274             slimItem = self.context.ballpark.slims.get(itemId)
    275             if slimItem and slimItem.ownerID in self.attributes.ownerIdSet:
    276                 filteredSet.discard(itemId)
    277 
    278         return filteredSet
    279 
    280 
    281 class GetCombatEffectsForRole(Task):
    282 
    283     @TimedFunction('behaviors::actions::combat::GetCombatEffectsForRole::OnEnter')
    284     def OnEnter(self):
    285         self.SetStatusToSuccess()
    286         if self._has_already_registered_combat_effects():
    287             return
    288         role = self.GetLastBlackboardValue(self.attributes.roleAddress)
    289         combatEffects = self._GetCombatEffectsOnTypeForRole(role)
    290         self.SendBlackboardValue(self.attributes.combatEffectsAddress, combatEffects)
    291 
    292     def _has_already_registered_combat_effects(self):
    293         return self.GetLastBlackboardValue(self.attributes.combatEffectsAddress) is not None
    294 
    295     def _GetCombatEffectsOnTypeForRole(self, role):
    296         combatEffectsByRole = COMBAT_EFFECTS_BY_ROLES.get(role, [])
    297         actualCombatEffectsByRoleOnType = []
    298         for effectId in combatEffectsByRole:
    299             if self.context.dogmaLM.dogmaStaticMgr.TypeHasEffect(self.context.mySlimItem.typeID, effectId):
    300                 actualCombatEffectsByRoleOnType.append(effectId)
    301 
    302         return actualCombatEffectsByRoleOnType