attribute.py (15088B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\dogma\attributes\attribute.py 2 from __future__ import print_function 3 import dogma 4 import dogma.data as dogma_data 5 import gametime 6 from dogma.attributes import AttributeInterface 7 import pytelemetry.zoning as zoning 8 import itertools 9 import dogma.const as const 10 import weakref 11 from dogma.dogmaLogging import GetCategoryNameFromID 12 from dogma.dogmaLogging import GetGroupNameFromID 13 from dogma.dogmaLogging import GetTypeNameFromID 14 from dogma.dogmaLogging import LogError 15 from dogma.dogmaLogging import LogException 16 from dogma.dogmaLogging import WrappedMethod 17 18 def Seq(sequenceOrNone): 19 if sequenceOrNone is None: 20 return [] 21 return sequenceOrNone 22 23 24 class Attribute(AttributeInterface): 25 __slots__ = ['category', 26 'currentValue', 27 'baseValue', 28 'typeValue', 29 'invItem', 30 'incomingModifiers', 31 '_outgoingModifiers', 32 'capAttrib', 33 'item', 34 'dirty', 35 'highIsGood', 36 'dogmaLM', 37 'attribID', 38 '_onChangeCallbacks', 39 'nonModifyingDependants', 40 'clientSideValue', 41 'floorAttrib'] 42 43 def __str__(self): 44 return self.performantStr() 45 46 def performantStr(self): 47 try: 48 itemID = self.item.itemID 49 itemClass = self.item.__class__.__name__ 50 itemAddress = id(self.item) 51 except ReferenceError: 52 itemID = self.invItem.itemID 53 itemClass = 'UNLOADED-ITEM' 54 itemAddress = '[GONE]' 55 56 attribID = self.attribID 57 attributeClass = self.__class__.__name__ 58 invItemCategoryID = self.invItem.categoryID 59 invItemGroupID = self.invItem.groupID 60 invItemTypeID = self.invItem.typeID 61 return "'{attribID}' ({attributeClass}) on '{invItemCategoryID} / {invItemTypeID}' ({itemClass} {itemID}) ".format(**locals()) 62 63 def friendlyStr(self): 64 try: 65 itemID = self.item.itemID 66 itemClass = self.item.__class__.__name__ 67 itemAddress = id(self.item) 68 except ReferenceError: 69 itemID = self.invItem.itemID 70 itemClass = 'UNLOADED-ITEM' 71 itemAddress = '[GONE]' 72 73 attribID = self.attribID 74 attributeName = dogma_data.get_attribute_name(attribID) 75 attributeClass = self.__class__.__name__ 76 invItemCategoryName = GetCategoryNameFromID(self.invItem.categoryID) 77 invItemGroupName = GetGroupNameFromID(self.invItem.groupID) 78 invItemTypeName = GetTypeNameFromID(self.invItem.typeID) 79 numIncoming = self.CountIncomingModifiers() 80 numOutgoing = self.CountOutgoingModifiers() 81 return "'{attributeName}' ({attributeClass}) on '{invItemCategoryName} / {invItemTypeName}' ({itemClass} {itemID}) [i{numIncoming}:o{numOutgoing}] ".format(**locals()) 82 83 def printInvItemDetails(self, itemClass): 84 print('START INV ITEM DETAILS:') 85 print() 86 try: 87 i = self.invItem 88 print('invItem:', i) 89 print('type:', type(i)) 90 print('columns:', i.__columns__) 91 print() 92 for a in ['itemID', 93 'typeID', 94 'ownerID', 95 'locationID', 96 'flagID', 97 'quantity', 98 'groupID', 99 'categoryID', 100 'customInfo', 101 'stacksize', 102 'singleton']: 103 print(a, getattr(i, a)) 104 105 print() 106 print('Underlying item:', self.item) 107 print() 108 print('class:', itemClass) 109 except: 110 print("*** OUCH. THAT DIDN'T WORK! ***") 111 112 print() 113 print('END INV ITEM DETAILS') 114 print() 115 116 def __init__(self, attribID, baseValue, dogmaItem, dogmaLM, staticAttr): 117 self.attribID = attribID 118 self.item = dogmaItem 119 self.dogmaLM = dogmaLM 120 self.invItem = dogmaItem.invItem 121 if attribID is not None: 122 self.typeValue = dogma_data.get_type_attribute_or_default(dogmaItem.invItem.typeID, attribID) 123 else: 124 self.typeValue = baseValue 125 if baseValue is None: 126 baseValue = self.typeValue 127 self.baseValue = baseValue 128 self.incomingModifiers = None 129 self._outgoingModifiers = None 130 self.nonModifyingDependants = None 131 self._onChangeCallbacks = None 132 self.highIsGood = staticAttr.highIsGood 133 self.category = staticAttr.dataType 134 self.currentValue = self.baseValue 135 self.dirty = False 136 if getattr(staticAttr, 'maxAttributeID', False): 137 self.capAttrib = weakref.proxy(dogmaItem.attributes[staticAttr.maxAttributeID]) 138 self.capAttrib.AddNonModifyingDependant(self) 139 else: 140 self.capAttrib = None 141 if getattr(staticAttr, 'minAttributeID', False): 142 self.floorAttrib = weakref.proxy(dogmaItem.attributes[staticAttr.minAttributeID]) 143 self.floorAttrib.AddNonModifyingDependant(self) 144 else: 145 self.floorAttrib = None 146 self._PickUpInitialModifiers(dogmaItem) 147 148 def IsAnOrphan(self): 149 try: 150 testWhetherItemIsStillValid = self.item.invItem 151 return False 152 except ReferenceError: 153 return True 154 155 def _PickUpInitialModifiers(self, dogmaItem): 156 location = dogmaItem.GetLocation() 157 if location: 158 location._PickUpInitialModifiersFromLocationTo(self) 159 pilotID = dogmaItem.GetPilot() 160 ownerItem = self.dogmaLM.dogmaItems.get(pilotID, None) 161 if ownerItem and self.item.IsOwnerModifiable(): 162 ownerItem._PickUpInitialModifiersFromOwnerTo(self) 163 164 def Update(self): 165 prevValue = self.currentValue 166 capVal = None 167 floorVal = None 168 if self.capAttrib: 169 capVal = self.capAttrib.GetValue() 170 self.baseValue = min(self.baseValue, capVal) 171 if self.floorAttrib: 172 floorVal = self.floorAttrib.GetValue() 173 self.baseValue = max(self.baseValue, floorVal) 174 val = self.baseValue 175 val = self._ApplyModifierOperationsToVal(val) 176 if capVal is not None: 177 val = min(val, capVal) 178 if floorVal is not None: 179 val = max(val, floorVal) 180 if self.category in (10, 11, 12): 181 val = int(val) 182 if self.attribID in (const.attributeCpuLoad, 183 const.attributePowerLoad, 184 const.attributePowerOutput, 185 const.attributeCpuOutput, 186 const.attributeCpu, 187 const.attributePower): 188 val = round(val, 2) 189 self.currentValue = val 190 self.dirty = False 191 self._PerformCallbacksIfValueChanged(prevValue) 192 193 def _ApplyModifierOperationsToVal(self, val): 194 if self.incomingModifiers: 195 for idx, mods in enumerate(self.incomingModifiers): 196 dogmaOperator = const.dgmOperators[const.dgmOperatorPrecedence[idx]] 197 for modAttrib in mods: 198 val = dogmaOperator(val, modAttrib.GetValue()) 199 200 return val 201 202 def GetValue(self): 203 if self.dirty: 204 self.Update() 205 return self.currentValue 206 207 def MarkDirty(self, silent = False): 208 self.dirty = True 209 for op, nowDirtyAttrib in list(Seq(self._outgoingModifiers)): 210 try: 211 nowDirtyAttrib().MarkDirty() 212 except AttributeError: 213 continue 214 215 for nowDirtyAttrib in Seq(self.nonModifyingDependants): 216 try: 217 nowDirtyAttrib.MarkDirty() 218 except AttributeError: 219 continue 220 221 if silent: 222 return 223 ownerID = None 224 if True: 225 try: 226 ownerID = self.item.ownerID 227 except AttributeError: 228 LogError('Attribute {} has no `ownerID` on its `item`, just: {}'.format(self, sorted(vars(self.item)))) 229 230 itemID = self.item.itemID 231 if self.ShoulCallbackAttributeChange(itemID, ownerID): 232 try: 233 oldValue = self.currentValue 234 newValue = self.GetValue() 235 self.dogmaLM.attributeChangeCallbacksByAttributeID[self.attribID](self.attribID, itemID, newValue, oldValue) 236 except Exception: 237 LogException('Error broadcasting attribute change {}'.format(self.dogmaLM.attributeChangeCallbacksByAttributeID[self.attribID])) 238 239 if dogma.IsClient(): 240 return 241 if self.dogmaLM.ShouldMessageItemEvent(itemID): 242 try: 243 if self.attribID is not None: 244 self.dogmaLM.msgMgr.AddAttribute(self) 245 except Exception: 246 LogException('Error broadcasting attribute change') 247 248 def ShoulCallbackAttributeChange(self, itemID, ownerID): 249 if self.attribID not in self.dogmaLM.attributeChangeCallbacksByAttributeID: 250 return False 251 if not self.dogmaLM.ShouldMessageItemEvent(itemID, ownerID): 252 return False 253 return True 254 255 def AddCallbackForChanges(self, func, params): 256 if self._onChangeCallbacks is None: 257 self._onChangeCallbacks = {} 258 self._onChangeCallbacks[func] = params 259 260 def RemoveCallbackForChanges(self, func): 261 del self._onChangeCallbacks[func] 262 if not self._onChangeCallbacks: 263 self._onChangeCallbacks = None 264 265 def _PerformCallbacksIfValueChanged(self, prevValue): 266 if self._onChangeCallbacks is None: 267 return 268 if prevValue != self.currentValue: 269 for func, params in self._onChangeCallbacks.iteritems(): 270 func(self, prevValue, self.currentValue, *params) 271 272 def ConstructMessage(self): 273 try: 274 if self.item.ownerID is None: 275 return 276 except ReferenceError: 277 return 278 279 clientID = self.item.FindClientID('charid', [self.item.ownerID]) 280 if clientID is not None: 281 newValue = self.GetValue() 282 try: 283 oldValue = self.clientSideValue 284 if oldValue == newValue: 285 return 286 except AttributeError: 287 oldValue = newValue 288 289 self.clientSideValue = newValue 290 if self.dogmaLM.ShouldMessageItemEvent(self.item.itemID): 291 simTime = gametime.GetSimTime() 292 return (clientID, (('OnModuleAttributeChange', 293 self.item.ownerID, 294 self.item.itemID, 295 self.attribID, 296 simTime, 297 newValue, 298 oldValue, 299 gametime.GetWallclockTime()), simTime)) 300 else: 301 return 302 303 def _makeEmptyIncomingModifiers(self): 304 return tuple((set() for x in const.dgmOperatorPrecedence)) 305 306 def _initIncomingModifiers(self): 307 self.incomingModifiers = self._makeEmptyIncomingModifiers() 308 309 @WrappedMethod 310 def AddIncomingModifier(self, op, attribute): 311 if self.incomingModifiers is None: 312 self._initIncomingModifiers() 313 self.incomingModifiers[const.dgmOperatorIDtoIndex[op]].add(attribute) 314 self.MarkDirty() 315 316 @WrappedMethod 317 def RemoveIncomingModifier(self, op, attribute): 318 try: 319 self.incomingModifiers[const.dgmOperatorIDtoIndex[op]].remove(attribute) 320 self.MarkDirty() 321 except (TypeError, 322 IndexError, 323 KeyError, 324 ValueError): 325 return 326 327 if self.incomingModifiers is None: 328 return 329 if not any(self.incomingModifiers): 330 self.incomingModifiers = None 331 332 def GetIncomingModifiers(self): 333 if self.incomingModifiers is None: 334 return [] 335 result = [] 336 for opIdx, attribSet in enumerate(self.incomingModifiers): 337 result.extend(((const.dgmOperatorPrecedence[opIdx], attrib) for attrib in attribSet)) 338 339 return result 340 341 def CountIncomingModifiers(self): 342 return len(self.GetIncomingModifiers()) 343 344 def AddNonModifyingDependant(self, attribute): 345 if self.nonModifyingDependants is None: 346 self.nonModifyingDependants = set() 347 self.nonModifyingDependants.add(attribute) 348 attribute.MarkDirty(silent=True) 349 350 def _makeEmptyOutgoingModifiers(self): 351 return set() 352 353 def _initOutgoingModifiers(self): 354 self._outgoingModifiers = self._makeEmptyOutgoingModifiers() 355 356 @WrappedMethod 357 def AddOutgoingModifier(self, op, attribute): 358 if self._outgoingModifiers is None: 359 self._initOutgoingModifiers() 360 weak = weakref.ref(attribute) 361 self._outgoingModifiers.add((op, weak)) 362 363 @WrappedMethod 364 def RemoveOutgoingModifier(self, op, attribute): 365 weak = weakref.ref(attribute) 366 try: 367 self._outgoingModifiers.remove((op, weak)) 368 except (AttributeError, KeyError, ValueError): 369 return 370 371 if not any(self._outgoingModifiers): 372 self._outgoingModifiers = None 373 374 def GetOutgoingModifiers(self): 375 if self._outgoingModifiers is None: 376 return [] 377 return [ (opIdx, attrib_weakref()) for opIdx, attrib_weakref in self._outgoingModifiers ] 378 379 def CountOutgoingModifiers(self): 380 return len(self.GetOutgoingModifiers()) 381 382 def RemoveAllModifiers(self): 383 for operator, incomingAttrib in self.GetIncomingModifiers(): 384 incomingAttrib.RemoveOutgoingModifier(operator, self) 385 386 for operator, outgoingAttrib in self.GetOutgoingModifiers(): 387 outgoingAttrib.RemoveIncomingModifier(operator, self) 388 389 self._DiscardAllModifiers() 390 391 def _DiscardAllModifiers(self): 392 self.incomingModifiers = None 393 self._outgoingModifiers = None 394 395 def GetBaseValue(self): 396 return self.baseValue 397 398 def SetBaseValue(self, newBaseValue): 399 if self.baseValue != newBaseValue: 400 self.baseValue = newBaseValue 401 self.MarkDirty() 402 403 def ResetBaseValue(self): 404 if self.baseValue != self.typeValue: 405 self.baseValue = self.typeValue 406 self.MarkDirty() 407 408 def IncreaseBaseValue(self, addAmount): 409 if not self.item.CanAttributeBeModified(): 410 return 411 self.baseValue += addAmount 412 self.MarkDirty() 413 414 def DecreaseBaseValue(self, decAmount): 415 if not self.item.CanAttributeBeModified(): 416 return 417 self.baseValue -= decAmount 418 self.MarkDirty() 419 420 def GetModifyingItems(self): 421 modifyingItems = set() 422 if self.incomingModifiers is None: 423 return modifyingItems 424 for srcAttrib in itertools.chain(*self.incomingModifiers): 425 if srcAttrib.item is not None: 426 modifyingItems.add(srcAttrib.item.itemID) 427 428 return modifyingItems 429 430 def GetPersistData(self): 431 if self.baseValue != self.typeValue: 432 return self.baseValue 433 else: 434 return None 435 436 @zoning.ZONE_METHOD 437 def ApplyPersistData(self, pData): 438 self.SetBaseValue(pData)