entity.py (7907B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\behaviors\entity.py 2 from ballpark.helpers.aggression import get_kill_owner_id 3 from ccpProfile import TimedFunction 4 from collections import defaultdict 5 import math 6 import evetypes 7 from ballpark.entities.entitywrapper import EntityWrapper 8 from ballpark.messenger.const import MESSAGE_ON_BALL_MODE_CHANGED, MESSAGE_ON_ADDITIONAL_LOOT_REMOVED 9 from ballpark.messenger.const import MESSAGE_ON_UNSPAWNED_WITH_ADDITIONAL_LOOT 10 from ballpark.messenger.const import MESSAGE_ON_DROPPED_ADDITIONAL_LOOT 11 from ballpark.warpDisruptionBalls import IsBallIntersectingAnyWarpDisruptionField 12 from inventorycommon.const import groupSolarSystem, ownerUnknown 13 import logging 14 from ballpark.const import DESTINY_MODE_WARP 15 from eveexceptions import UserError 16 from inventorycommon.const import ownerNone 17 from inventorycommon.util import IsNPC 18 from dogma import const as dogmaConst 19 from spacecomponents.common.helper import HasCargoBayComponent 20 logger = logging.getLogger(__name__) 21 INVULNERABLE_LABEL = 'BehaviorWarp' 22 23 class NPCWarpError(Exception): 24 pass 25 26 27 class NpcWarpScrambledError(NPCWarpError): 28 pass 29 30 31 class NpcWarpBubbledError(NPCWarpError): 32 pass 33 34 35 class NpcWarpPathBubbledError(NPCWarpError): 36 pass 37 38 39 class BehaviorEntity(EntityWrapper): 40 __destiny_ball_class_id__ = 58 41 42 def DoSetup(self, entitySettings): 43 self.additionalLoot = defaultdict(int) 44 self.additionalLootTables = [] 45 46 def OnAddedToPark(self): 47 pass 48 49 @TimedFunction('behaviors::entity::BehaviorEntity::NPCWarpTo') 50 def NPCWarpTo(self, destination, jitterRadius = 20000, warpAtRange = 0.0, warpSpeed = 6): 51 if hasattr(self, 'ballpark') and self.ballpark is not None: 52 if self.ballpark.IsWarpScrambled(self.id): 53 raise NpcWarpScrambledError('Warp Scrambled') 54 if self.mode == DESTINY_MODE_WARP: 55 raise NPCWarpError('Already Warping') 56 destination = self.ballpark.AdjustDstByJitter(destination, jitterRadius) 57 if not self.ballpark.dogmaLM.GetAttributeValue(self.id, dogmaConst.attributeWarpBubbleImmune): 58 if IsBallIntersectingAnyWarpDisruptionField(self.ballpark, self.id): 59 raise NpcWarpBubbledError('Warp bubble disruptor') 60 try: 61 destination = self.ballpark.ApplyWarpDisruptors(self.id, self.GetPosition(), destination, warpAtRange) 62 except UserError: 63 raise NpcWarpPathBubbledError('Warp path too short due to bubble disruptor') 64 65 self.ballpark.bubbleKeepAliveController.add_bubble_keep_alive_ball_at_coordinates(self.id, destination) 66 self.ballpark.EntityWarpStart(self.id, pos=destination, makeItReal=True, notWarpingException=NPCWarpError, minRange=warpAtRange, warpSpeed=warpSpeed) 67 else: 68 raise NPCWarpError('No ballpark') 69 70 @TimedFunction('behaviors::entity::BehaviorEntity::NPCEndWarp') 71 def NPCEndWarp(self): 72 if hasattr(self, 'ballpark') and self.ballpark is not None: 73 self.ballpark.bubbleKeepAliveController.remove_bubble_keep_alive_ball(self.id) 74 else: 75 raise NPCWarpError() 76 77 def _GetKillOwner(self): 78 return get_kill_owner_id(self.ballpark, self.id) 79 80 def _GetWreckID(self, item): 81 return evetypes.GetWreckTypeIDOrNone(item.typeID) 82 83 def _GetShipAccess(self, item): 84 shipAccess = self.ballpark.broker.ship.GetShipAccessEx(item.locationID, groupSolarSystem) 85 shipAccess.Bind() 86 return shipAccess 87 88 def OnExplode(self): 89 item = self.ballpark.inventory2.GetItem(self.id) 90 if HasCargoBayComponent(item.typeID): 91 logger.debug('Entity %s of type %s has cargo bay component which is managing dropped loot.', self.id, item.typeID) 92 return 93 offenderOwnerID = self._GetKillOwner() 94 wreckTypeID = self._GetWreckID(item) 95 shipAccess = self._GetShipAccess(item) 96 itemList = self.GetLootForEntity(item.typeID, item.ownerID) 97 if wreckTypeID is not None: 98 shipAccess.Jettison_DropWreck_Ex(offenderOwnerID, self.id, item.typeID, self.GetPosition(), wreckTypeID, itemList, npcLoot=True) 99 super(BehaviorEntity, self).OnExplode() 100 101 def OnUnspawn(self): 102 additionalLoot = self.GetAdditionalLoot() 103 if additionalLoot: 104 self.SendMessage(self.id, MESSAGE_ON_UNSPAWNED_WITH_ADDITIONAL_LOOT, additionalLoot) 105 106 def DoModeChange(self, oldMode, newMode): 107 self.SendMessage(self.id, MESSAGE_ON_BALL_MODE_CHANGED, oldMode, newMode) 108 if oldMode == DESTINY_MODE_WARP: 109 self.NPCEndWarp() 110 111 def _GetAdditionalLootForWreck(self): 112 additionalLoot = [] 113 for lootTableId in self.additionalLootTables: 114 additionalLoot.extend(self.ballpark.broker.lootSvc.GetLootFromTable(lootTableId)) 115 116 for typeId, amount in self.additionalLoot.iteritems(): 117 additionalLoot.append((typeId, amount, None)) 118 119 return additionalLoot 120 121 def AddTypeAndAmountToAdditionalLoot(self, typeId, amount = 1, overload = True): 122 if not overload: 123 amount = self._GetAvailableAmountForTypeInAdditionalLoot(typeId, amount) 124 self.additionalLoot[typeId] += amount 125 126 def RemoveTypeFromAdditionalLoot(self, typeId): 127 if typeId in self.additionalLoot: 128 del self.additionalLoot[typeId] 129 130 def HasTypeInAdditionalLoot(self, typeId): 131 return typeId in self.additionalLoot 132 133 def GetLootForEntity(self, typeId, ownerId): 134 itemDict = self.ballpark.broker.lootSvc.GetLootForEntity(typeId, self.id, ownerId) 135 itemList = [] 136 for typeID, pickList in itemDict.iteritems(): 137 for quantity, customInfo in pickList: 138 itemList.append((typeID, quantity, customInfo)) 139 140 additionalLoot = self._GetAdditionalLootForWreck() 141 if additionalLoot: 142 self.SendMessage(self.id, MESSAGE_ON_DROPPED_ADDITIONAL_LOOT, {(typeId, amount) for typeId, amount, _ in additionalLoot}) 143 itemList.extend(additionalLoot) 144 return itemList 145 146 def AddLootTableToAdditionalLoot(self, lootTableId): 147 self.additionalLootTables.append(lootTableId) 148 149 def HasLootTableInAdditionalLoot(self, lootTableId): 150 return lootTableId in self.additionalLootTables 151 152 def HasEnoughAdditionalCargoCapacityForType(self, typeId, quantity = 1): 153 requiredCargoCapacity = evetypes.GetAttributeForType(typeId, 'volume') * quantity 154 return self.GetAvailableAdditionalCargoCapacity() - requiredCargoCapacity >= 0 155 156 def GetAvailableAdditionalCargoCapacity(self): 157 selfTypeId = self.ballpark.inventory2.GetItem(self.id).typeID 158 cargoCapacity = evetypes.GetAttributeForType(selfTypeId, 'capacity') 159 usedCargoCapacity = self._GetUsedAdditionalCargoCapacity() 160 return cargoCapacity - usedCargoCapacity 161 162 def _GetUsedAdditionalCargoCapacity(self): 163 usedCapacity = 0 164 for typeId, amount in self.additionalLoot.iteritems(): 165 usedCapacity += evetypes.GetAttributeForType(typeId, 'volume') * amount 166 167 return usedCapacity 168 169 def _GetAvailableAmountForTypeInAdditionalLoot(self, typeId, amount): 170 availabeCargoCapacity = self.GetAvailableAdditionalCargoCapacity() 171 totalVolumeNeeded = evetypes.GetAttributeForType(typeId, 'volume') * amount 172 if availabeCargoCapacity < totalVolumeNeeded: 173 return math.floor(amount * (availabeCargoCapacity / totalVolumeNeeded)) 174 return amount 175 176 def RemoveAdditionalLoot(self): 177 additionalLoot = self.additionalLoot.copy() 178 self.additionalLoot.clear() 179 self.SendMessage(self.id, MESSAGE_ON_ADDITIONAL_LOOT_REMOVED) 180 return additionalLoot 181 182 def GetAdditionalLoot(self): 183 return self.additionalLoot.copy()