combat.py (16747B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\behaviors\monitors\combat.py 2 import logging 3 import ballpark.messenger.const as messages 4 import evetypes 5 from behaviors.blackboards import BlackboardDeletedError 6 from behaviors.groups.mixin import GroupTaskMixin 7 from behaviors.tasks import Task 8 from behaviors.utility.ballparks import get_ball_position 9 from behaviors.utility.owner import get_owner_id 10 from ccpProfile import TimedFunction 11 from crimewatch.util import GetAggressorId 12 from eveexceptions.exceptionEater import ExceptionEater 13 from eve.common.script.sys import idCheckers 14 logger = logging.getLogger(__name__) 15 16 class WaitForAggressiveAct(Task): 17 18 @TimedFunction('behaviors::monitors::combat::WaitForAggressiveAct::OnEnter') 19 def OnEnter(self): 20 self.SubscribeItem(self.context.myItemId, messages.MESSAGE_CRIMEWATCH_EVENT_STARTED, self._OnCrimewatchEventStarted) 21 self.SubscribeItem(self.context.myItemId, messages.MESSAGE_INDIRECT_OFFENSE_EVENT, self._OnIndirectOffenseEvent) 22 self.SetStatusToSuccess() 23 24 def CleanUp(self): 25 super(WaitForAggressiveAct, self).CleanUp() 26 self.UnsubscribeItem(self.context.myItemId, messages.MESSAGE_CRIMEWATCH_EVENT_STARTED, self._OnCrimewatchEventStarted) 27 self.UnsubscribeItem(self.context.myItemId, messages.MESSAGE_INDIRECT_OFFENSE_EVENT, self._OnIndirectOffenseEvent) 28 29 def AddAggressorAndResetTree(self, aggressorID, targetTypeID): 30 try: 31 combatSet = self.GetLastBlackboardValue(self.attributes.combatTargetsAddress) or set() 32 except BlackboardDeletedError: 33 logger.debug(u'WaitForAggressiveAct:::Combat target address references deleted blackboard: %s', unicode(self.attributes.combatTargetsAddress)) 34 return 35 36 if aggressorID not in combatSet: 37 logger.debug('Entity aggressed: %s', aggressorID) 38 combatSet.add(aggressorID) 39 self.SendBlackboardValue(self.attributes.combatTargetsAddress, combatSet) 40 self.behaviorTree.RequestReset(requestedBy=self) 41 self._LogAggression(aggressorID, targetTypeID) 42 self.OnNewAggressorDetected() 43 44 def AddAggressorsAndResetTree(self, categoryID, sourceOwnerID, aggressorID, targetTypeID): 45 aggressorID = GetAggressorId(categoryID, sourceOwnerID, aggressorID) 46 self.AddAggressorAndResetTree(aggressorID, targetTypeID) 47 48 @TimedFunction('behaviors::monitors::combat::WaitForAggressiveAct::_OnCrimewatchEventStarted') 49 def _OnCrimewatchEventStarted(self, effectKey, effectInfo): 50 if self.IsInvalid(): 51 return 52 if effectInfo.isAssistance: 53 return 54 aggressorID = effectKey.shipID 55 categoryID = evetypes.GetCategoryID(effectInfo.moduleTypeID) 56 if self.IsValidAggressor(aggressorID): 57 self.AddAggressorsAndResetTree(categoryID, effectInfo.sourceOwnerID, aggressorID, effectInfo.targetTypeID) 58 59 @TimedFunction('behaviors::monitors::combat::WaitForAggressiveAct::_OnIndirectOffenseEvent') 60 def _OnIndirectOffenseEvent(self, sourceOwnerId, targetItem): 61 if self.IsInvalid(): 62 return 63 try: 64 offendingOwner = self.context.ballpark.inventory2.GetItem(sourceOwnerId) 65 except RuntimeError: 66 pass 67 else: 68 if offendingOwner: 69 offendingShipID = offendingOwner.locationID 70 offendingItem = self.context.ballpark.inventory2.GetItem(offendingShipID) 71 if self.IsValidAggressor(offendingShipID): 72 self.AddAggressorsAndResetTree(offendingItem.categoryID, sourceOwnerId, offendingShipID, targetItem.typeID) 73 74 def _LogAggression(self, aggressorID, targetTypeID): 75 with ExceptionEater('WaitForAggressiveAct.LogAggression'): 76 entity_group = self.context.entityLocation.GetBehaviorEntityGroup(self.context.myEntityGroupId) 77 self.context.eventLogger.log_behavior_aggressed(get_owner_id(self), self.behaviorTree.GetBehaviorId(), self._GetGroupBehaviorId(entity_group), targetTypeID, aggressorID) 78 79 def OnNewAggressorDetected(self): 80 pass 81 82 def _GetGroupBehaviorId(self, entity_group): 83 if entity_group is not None: 84 return entity_group.GetBehaviorId() 85 return self.behaviorTree.GetBehaviorId() 86 87 def IsValidAggressor(self, aggressorID): 88 if not aggressorID: 89 return False 90 ballpark = self.context.ballpark 91 ball = ballpark.GetBallOrNone(aggressorID) 92 if ball is None: 93 return False 94 return all((filterfunction(ballpark, ball) for filterfunction in self._GetFilterFunctions())) 95 96 def _GetFilterFunctions(self): 97 return [self._PlayerAggressorFilter] 98 99 def _PlayerAggressorFilter(self, ballpark, ball): 100 if getattr(self.attributes, 'onlyPlayerAggressors', False): 101 slimItem = ballpark.GetSlimItem(ball.id) 102 if not slimItem or slimItem.ownerID is None: 103 return False 104 if not idCheckers.IsPlayerOwner(slimItem.ownerID): 105 return False 106 return True 107 108 109 class SetFlagAndResetWhenAggressed(WaitForAggressiveAct): 110 111 def AddAggressorsAndResetTree(self, categoryID, sourceOwnerID, aggressorID, targetTypeID): 112 self.SendBlackboardValue(self.attributes.flagAddress, True) 113 self.behaviorTree.RequestReset(requestedBy=self) 114 115 116 class FlagAggressionAndReportMyPosition(WaitForAggressiveAct): 117 118 def OnNewAggressorDetected(self): 119 my_position = get_ball_position(self, self.context.myItemId) 120 self.SendBlackboardValue(self.attributes.flagAddress, True) 121 self.SendBlackboardValue(self.attributes.myPositionAddress, my_position) 122 123 124 class ClearValueAndResetIfTargetCloaks(Task): 125 126 @TimedFunction('behaviors::monitors::combat::ClearValueAndResetIfTargetCloaks::OnEnter') 127 def OnEnter(self): 128 self.targetId = self.GetLastBlackboardValue(self.attributes.targetAddress) 129 if self.targetId is not None: 130 self.SubscribeToCloakChanged() 131 self.SetStatusToSuccess() 132 else: 133 self.SetStatusToFailed() 134 135 def CleanUp(self): 136 if not self.IsInvalid(): 137 self.UnsubscribeFromCloakChanged() 138 self.targetId = None 139 self.SetStatusToInvalid() 140 141 def SubscribeToCloakChanged(self): 142 self.SubscribeItem(self.targetId, messages.MESSAGE_ON_CLOAK_BALL, self._OnCloakBall) 143 144 def UnsubscribeFromCloakChanged(self): 145 self.UnsubscribeItem(self.targetId, messages.MESSAGE_ON_CLOAK_BALL, self._OnCloakBall) 146 147 @TimedFunction('behaviors::monitors::combat::ClearValueAndResetIfTargetCloaks::_OnCloakChanged') 148 def _OnCloakBall(self, itemId): 149 self.SendBlackboardValue(self.attributes.valueAddress, None) 150 self.behaviorTree.RequestReset(requestedBy=self) 151 152 153 class MaxLockedTargetsChangedMonitor(Task): 154 155 @TimedFunction('behaviors::monitors::combat::MaxLockedTargetsChangedMonitor::OnEnter') 156 def OnEnter(self): 157 self.SubscribeItem(self.context.myItemId, messages.MESSAGE_MAX_LOCKED_TARGETS_CHANGED, self._OnMaxLockedTargetsChanged) 158 self.SetStatusToSuccess() 159 160 def CleanUp(self): 161 if not self.IsInvalid(): 162 self.UnsubscribeItem(self.context.myItemId, messages.MESSAGE_MAX_LOCKED_TARGETS_CHANGED, self._OnMaxLockedTargetsChanged) 163 super(MaxLockedTargetsChangedMonitor, self).CleanUp() 164 165 @TimedFunction('behaviors::monitors::combat::MaxLockedTargetsChangedMonitor::_OnMaxLockedTargetsChanged') 166 def _OnMaxLockedTargetsChanged(self): 167 if not self.IsInvalid(): 168 self.behaviorTree.RequestReset(requestedBy=self) 169 170 171 class MonitorAssistanceToTargets(WaitForAggressiveAct): 172 173 @TimedFunction('behaviors::monitors::combat::MonitorAssistanceToTargets::OnEnter') 174 def OnEnter(self): 175 combatTargetIds = self.GetLastBlackboardValue(self.attributes.combatTargetsAddress) 176 self.SubscribeToTargetsInBubble(combatTargetIds, self.context.myBall.newBubbleId) 177 self.SetStatusToSuccess() 178 179 def SubscribeToTargetsInBubble(self, combatTargetIds, bubbleId): 180 if not combatTargetIds: 181 return 182 monitoredTargetSet = self.GetMonitoredTargets() 183 destinyBalls = self.context.ballpark.balls 184 for combatTargetId in combatTargetIds: 185 ball = destinyBalls.get(combatTargetId) 186 if ball and ball.newBubbleId == bubbleId: 187 monitoredTargetSet.add(combatTargetId) 188 self.SubscribeItem(combatTargetId, messages.MESSAGE_CRIMEWATCH_EVENT_STARTED, self._OnCrimewatchEventStarted) 189 190 def CleanUp(self): 191 if not self.IsInvalid(): 192 self.UnsubscribeFromMonitoredTargets() 193 self.SetStatusToInvalid() 194 195 def UnsubscribeFromMonitoredTargets(self): 196 monitoredTargetSet = self.GetMonitoredTargets() 197 if not monitoredTargetSet: 198 return 199 for monitoredTargetId in monitoredTargetSet: 200 self.UnsubscribeItem(monitoredTargetId, messages.MESSAGE_CRIMEWATCH_EVENT_STARTED, self._OnCrimewatchEventStarted) 201 202 self.SetMonitoredTargets(None) 203 204 @TimedFunction('behaviors::monitors::combat::MonitorAssistanceToTargets::_OnCrimewatchEventStarted') 205 def _OnCrimewatchEventStarted(self, effectKey, effectInfo): 206 if self.IsInvalid(): 207 return 208 if not effectInfo.isAssistance: 209 return 210 aggressorID = effectKey.shipID 211 categoryID = evetypes.GetCategoryID(effectInfo.moduleTypeID) 212 self.AddAggressorsAndResetTree(categoryID, effectInfo.sourceOwnerID, aggressorID, effectInfo.targetTypeID) 213 214 def GetMonitoredTargets(self): 215 if not self.HasContextValue('monitoredTargetSet'): 216 return set() 217 return self.GetContextValue('monitoredTargetSet') 218 219 def SetMonitoredTargets(self, monitoredTargetSet): 220 self.SetContextValue('monitoredTargetSet', monitoredTargetSet) 221 222 223 class MonitorAssistanceToGroupTargets(MonitorAssistanceToTargets, GroupTaskMixin): 224 225 def OnEnter(self): 226 combatTargetIds = self.GetLastBlackboardValue(self.attributes.combatTargetsAddress) 227 for bubbleId in self.GetMemberBubbleSet(): 228 self.SubscribeToTargetsInBubble(combatTargetIds, bubbleId) 229 230 self.SetStatusToSuccess() 231 232 233 class MonitorAggressionTowardsTargets(WaitForAggressiveAct): 234 235 @TimedFunction('behaviors::monitors::combat::MonitorAggressionTowardsTargets::OnEnter') 236 def OnEnter(self): 237 monitoredTargetIds = self.GetLastBlackboardValue(self.attributes.monitoredTargetsAddress) 238 self.SubscribeToTargetsInBubble(monitoredTargetIds, self.context.myBall.newBubbleId) 239 self.SetStatusToSuccess() 240 241 def SubscribeToTargetsInBubble(self, monitoredTargetIds, bubbleId): 242 if not monitoredTargetIds: 243 return 244 monitoredTargetSet = self.GetMonitoredTargets() 245 destinyBalls = self.context.ballpark.balls 246 for targetId in monitoredTargetIds: 247 ball = destinyBalls.get(targetId) 248 if ball and ball.newBubbleId == bubbleId: 249 monitoredTargetSet.add(targetId) 250 self.SubscribeItem(targetId, messages.MESSAGE_CRIMEWATCH_EVENT_STARTED, self._OnCrimewatchEventStarted) 251 self.SubscribeItem(targetId, messages.MESSAGE_INDIRECT_OFFENSE_EVENT, self._OnIndirectOffenseEvent) 252 253 def CleanUp(self): 254 if not self.IsInvalid(): 255 self.UnsubscribeFromMonitoredTargets() 256 self.SetStatusToInvalid() 257 258 def UnsubscribeFromMonitoredTargets(self): 259 monitoredTargetSet = self.GetMonitoredTargets() 260 if not monitoredTargetSet: 261 return 262 for monitoredTargetId in monitoredTargetSet: 263 self.UnsubscribeItem(monitoredTargetId, messages.MESSAGE_CRIMEWATCH_EVENT_STARTED, self._OnCrimewatchEventStarted) 264 self.UnsubscribeItem(self.context.myItemId, messages.MESSAGE_INDIRECT_OFFENSE_EVENT, self._OnIndirectOffenseEvent) 265 266 self.SetMonitoredTargets(None) 267 268 @TimedFunction('behaviors::monitors::combat::MonitorAggressionTowardsTargets::_OnCrimewatchEventStarted') 269 def _OnCrimewatchEventStarted(self, effectKey, effectInfo): 270 if self.IsInvalid(): 271 return 272 if not effectInfo.isOffensive: 273 return 274 aggressorID = effectKey.shipID 275 categoryID = evetypes.GetCategoryID(effectInfo.moduleTypeID) 276 self.AddAggressorsAndResetTree(categoryID, effectInfo.sourceOwnerID, aggressorID, effectInfo.targetTypeID) 277 278 def GetMonitoredTargets(self): 279 if not self.HasContextValue('monitoredTargetSet'): 280 return set() 281 return self.GetContextValue('monitoredTargetSet') 282 283 def SetMonitoredTargets(self, monitoredTargetSet): 284 self.SetContextValue('monitoredTargetSet', monitoredTargetSet) 285 286 @TimedFunction('behaviors::monitors::combat::MonitorAggressionTowardsTargets::_OnIndirectOffenseEvent') 287 def _OnIndirectOffenseEvent(self, sourceOwnerId, targetItem): 288 if self.IsInvalid(): 289 return 290 try: 291 offendingOwner = self.context.ballpark.inventory2.GetItem(sourceOwnerId) 292 except RuntimeError: 293 pass 294 else: 295 if offendingOwner: 296 offendingShipID = offendingOwner.locationID 297 offendingItem = self.context.ballpark.inventory2.GetItem(offendingShipID) 298 self.AddAggressorsAndResetTree(offendingItem.categoryID, sourceOwnerId, offendingShipID, targetItem.typeID) 299 300 def _LogAggression(self, aggressorID, targetTypeID): 301 with ExceptionEater('MonitorAggressionTowardsTargets.LogAggression'): 302 entity_group = self.context.entityLocation.GetBehaviorEntityGroup(self.context.myEntityGroupId) 303 self.context.eventLogger.log_monitored_target_aggressed(get_owner_id(self), self.behaviorTree.GetBehaviorId(), self._GetGroupBehaviorId(entity_group), targetTypeID, aggressorID) 304 305 306 class MonitorAggressionTowardsGroupTargets(MonitorAggressionTowardsTargets, GroupTaskMixin): 307 308 def OnEnter(self): 309 monitoredTargetIds = self.GetLastBlackboardValue(self.attributes.monitoredTargetsAddress) 310 for bubbleId in self.GetMemberBubbleSet(): 311 self.SubscribeToTargetsInBubble(monitoredTargetIds, bubbleId) 312 313 self.SetStatusToSuccess() 314 315 316 class MonitorSecurityStatusLoss(Task): 317 318 def GetServiceManager(self): 319 return sm 320 321 def __init__(self, attributes = None): 322 super(MonitorSecurityStatusLoss, self).__init__(attributes) 323 self._solarSystemID = None 324 325 @TimedFunction('behaviors::monitors::combat::MonitorSecurityStatusLoss::OnEnter') 326 def OnEnter(self): 327 if self.context.ballpark: 328 self._solarSystemID = self.context.ballpark.solarsystemID 329 self.SubscribeItem(self._solarSystemID, messages.MESSAGE_SEC_STATUS_LOST_EVENT, self.OnSecStatusLost) 330 self.SetStatusToSuccess() 331 332 def _GetBubbleID(self): 333 return self.context.myBall.newBubbleId 334 335 def CleanUp(self): 336 super(MonitorSecurityStatusLoss, self).CleanUp() 337 if self._solarSystemID: 338 self.UnsubscribeItem(self._solarSystemID, messages.MESSAGE_CRIMEWATCH_EVENT_STARTED, self.OnSecStatusLost) 339 340 @TimedFunction('behaviors::monitors::combat::MonitorSecurityStatusLoss::OnSecStatusLost') 341 def OnSecStatusLost(self, ownerID, shipID): 342 if self.IsInvalid() or shipID is None: 343 return 344 ballpark = self.context.ballpark 345 if not ballpark: 346 return 347 ball = ballpark.GetBallOrNone(shipID) 348 if not ball: 349 return 350 if not self._IsInSameBubble(ball): 351 return 352 if self.attributes.maxDistance is not None: 353 try: 354 distance = ballpark.DistanceBetween(self.context.myItemId, shipID) 355 if distance > self.attributes.maxDistance: 356 return 357 except Exception: 358 pass 359 360 self.AddBallAsTargetAndResetTree(shipID) 361 362 def _IsInSameBubble(self, ball): 363 return ball.newBubbleId == self._GetBubbleID() 364 365 def AddBallAsTargetAndResetTree(self, ballID): 366 try: 367 combatSet = self.GetLastBlackboardValue(self.attributes.combatTargetsAddress) or set() 368 except BlackboardDeletedError: 369 logger.debug(u'MonitorSecurityStatusLoss:::Combat target address references deleted blackboard: %s', unicode(self.attributes.combatTargetsAddress)) 370 return 371 372 if ballID not in combatSet: 373 combatSet.add(ballID) 374 self.SendBlackboardValue(self.attributes.combatTargetsAddress, combatSet) 375 self.behaviorTree.RequestReset(requestedBy=self)