spaceObject.py (37795B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\spaceObject\spaceObject.py 2 import blue 3 import carbon.common.script.util.mathCommon as mathCommon 4 import decometaclass 5 import evegraphics.settings as gfxsettings 6 import evegraphics.utils as gfxutils 7 import eveSpaceObject 8 import eveSpaceObject.spaceobjaudio as spaceobjaudio 9 import fsdBuiltData.client.explosionBuckets as fsdExplosionBuckets 10 import geo2 11 import locks 12 import logging 13 import math 14 import random 15 import trinity 16 import uthread2 17 from cosmetics.common.ships.skins.static_data.skin_type import ShipSkinType 18 from destructionEffect import destructionType as destructionEffectType 19 from destructionEffect.destructioneffectmanager import DestructionEffectManager 20 from evedungeons.client.data import GetAssetManipulations, GetControllerReadyData 21 from evegraphics.controllers.clientControllerRegistry import ChangeControllerStateFromSlimItem 22 from evegraphics.explosions.spaceObjectExplosionManager import SpaceObjectExplosionManager 23 from evegraphics.graphicEffects.skinChange import ChangeSkin, RemoveAllTemporaryModels 24 from evetypes import GetGraphicID, GetGroupID 25 from eve.client.script.environment.sofService import GetSofService 26 from eve.client.script.environment.spaceObject.cosmeticsManager import CosmeticsManager 27 from eve.client.script.environment.spaceObject.ExplosionManager import ExplosionManager 28 from eve.common.lib.appConst import GRIDSIZE, groupMercenaryDen, groupMoon, groupPlanet 29 from fsdBuiltData.common.graphicIDs import GetControllerVariableOverrides, GetSofHullName 30 from inventorycommon.const import TYPES_THAT_ALWAYS_FACE_THE_SUN 31 from signals.signal import Signal 32 logger = logging.getLogger(__name__) 33 STRUCTURE_SKINCHANGE_MULTIEFFECT_GRAPHIC_ID = 26134 34 BALL_REMOVAL_DELAY_TO_EXPLOSION_BUCKETS = {240: [3, 4, 5]} 35 SERVER_CONTROLLER_EVENT_NAMES_TO_CONTROLLER_VARIABLES = {'Activated': 'IsTriggered'} 36 37 def modify_dna(base_dna, mat1 = None, mat2 = None, mat3 = None, mat4 = None, patternMat1 = None, patternMat2 = None, patternName = None, isStructure = False): 38 if isStructure: 39 materials = [None, 40 mat1, 41 mat3, 42 mat2] 43 else: 44 materials = [mat1, 45 mat3, 46 mat2, 47 None] 48 for i, material in enumerate(materials): 49 if material is None: 50 materials[i] = 'None' 51 52 materials_string = 'material?' + ';'.join(materials) 53 base_parts = base_dna.split(':') 54 if len(base_parts) > 3: 55 base_parts[3] = materials_string 56 else: 57 base_parts.append(materials_string) 58 if patternMat1 is not None or patternMat2 is not None: 59 pattern_components = [] 60 if patternName is not None: 61 pattern_components.append(patternName) 62 if patternMat1 is not None: 63 pattern_components.append(patternMat1) 64 if patternMat2 is not None: 65 pattern_components.append(patternMat2) 66 if pattern_components: 67 base_parts.append('pattern?' + ';'.join(pattern_components)) 68 return ':'.join(base_parts) 69 70 71 class SpaceObjectLogAdapter(logging.LoggerAdapter): 72 73 def __init__(self, logger, extra = None, so_id = None): 74 self.so_id = so_id 75 super(SpaceObjectLogAdapter, self).__init__(logger, extra) 76 77 def add_so_id(self, msg, *args, **kwargs): 78 msg, kwargs = self.process(msg, kwargs) 79 so_id_arg = [self.so_id] 80 if args: 81 new_args = list(args) 82 new_args = so_id_arg + new_args 83 else: 84 new_args = so_id_arg 85 msg = '[%s] ' + msg 86 return (msg, new_args, kwargs) 87 88 def debug(self, msg, *args, **kwargs): 89 new_msg, new_args, kwargs = self.add_so_id(msg, *args, **kwargs) 90 self.logger.debug(new_msg, *new_args, **kwargs) 91 92 def info(self, msg, *args, **kwargs): 93 new_msg, new_args, kwargs = self.add_so_id(msg, *args, **kwargs) 94 self.logger.info(new_msg, *new_args, **kwargs) 95 96 def warning(self, msg, *args, **kwargs): 97 new_msg, new_args, kwargs = self.add_so_id(msg, *args, **kwargs) 98 self.logger.warning(new_msg, *new_args, **kwargs) 99 100 def error(self, msg, *args, **kwargs): 101 new_msg, new_args, kwargs = self.add_so_id(msg, *args, **kwargs) 102 self.logger.error(new_msg, *new_args, **kwargs) 103 104 def critical(self, msg, *args, **kwargs): 105 new_msg, new_args, kwargs = self.add_so_id(msg, *args, **kwargs) 106 self.logger.critical(new_msg, *new_args, **kwargs) 107 108 def exception(self, msg, *args, **kwargs): 109 new_msg, new_args, kwargs = self.add_so_id(msg, *args, **kwargs) 110 kwargs['exc_info'] = 1 111 self.logger.exception(new_msg, *new_args, **kwargs) 112 113 def log(self, level, msg, *args, **kwargs): 114 new_msg, new_args, kwargs = self.add_so_id(msg, *args, **kwargs) 115 self.logger.log(level, new_msg, *new_args, **kwargs) 116 117 118 class SpaceObject(decometaclass.WrapBlueClass('destiny.ClientBall')): 119 __persistdeco__ = 0 120 __update_on_reload__ = 1 121 122 def __init__(self): 123 self.destructionEffectId = destructionEffectType.NONE 124 self.exploded = False 125 self.unloaded = getattr(self, '__unloadable__', False) 126 self.model = None 127 self.additionalModels = [] 128 self.animationStateObjects = {} 129 self.released = False 130 self.wreckID = None 131 self._audioEntity = None 132 self.logger = logging.getLogger('spaceObject.' + self.__class__.__name__) 133 self.logger = SpaceObjectLogAdapter(self.logger, so_id=self.id) 134 self.modelLoadedEvent = locks.Event() 135 self.modelLoadSignal = Signal(signalName='modelLoadSignal') 136 self.explosionModel = None 137 self.typeID = None 138 self.explosionBucketID = None 139 self.spaceMgr = None 140 self.sm = None 141 self.spaceObjectFactory = None 142 self.nextModel = None 143 self._isWaitingForModel = False 144 self._modelChangeCallbacks = [] 145 self._skinChangeTasklets = [] 146 self.skinMaterialSetID = None 147 self._dungeonAssetManipulations = None 148 self.lockedFromRelease = False 149 self.globalObjectHandlerThread = None 150 self.explosionManager = ExplosionManager() 151 152 def GetPositionCurve(self): 153 return self 154 155 def GetTypeID(self): 156 if self.typeID is None: 157 self.typeID = self.typeData.get('typeID', None) 158 return self.typeID 159 160 def SetServices(self, spaceMgr, serviceMgr): 161 self.spaceMgr = spaceMgr 162 self.sm = serviceMgr 163 self.spaceObjectFactory = GetSofService().spaceObjectFactory 164 165 def Prepare(self): 166 self.typeID = self.typeData.get('typeID', None) 167 self.explosionBucketID = fsdExplosionBuckets.GetExplosionBucketIDByTypeID(self.typeID) 168 self.LoadModel() 169 self.Assemble() 170 try: 171 self.SetControllerVariablesFromSlimItem(self.typeData.get('slimItem', None)) 172 self.SetControllerVariablesFromGraphicID() 173 self.SetDungeonAssetManipulations() 174 self.model.StartControllers() 175 except AttributeError: 176 pass 177 178 def SetControllerVariablesFromGraphicID(self): 179 gid = GetGraphicID(self.typeID) 180 overrides = GetControllerVariableOverrides(gid) 181 if overrides is not None: 182 for name, value in overrides.iteritems(): 183 self.SetControllerVariable(name, value) 184 185 def SetControllerVariablesFromSlimItem(self, slimItem): 186 if slimItem is None: 187 return 188 ChangeControllerStateFromSlimItem(self.GetTypeID(), self, slimItem) 189 190 def SetControllerVariableFromEvent(self, eventName, value, delay = None): 191 if delay: 192 blue.pyos.synchro.SleepSim(delay) 193 controllerVariableName = SERVER_CONTROLLER_EVENT_NAMES_TO_CONTROLLER_VARIABLES.get(eventName) 194 if controllerVariableName is None: 195 try: 196 self.model.HandleControllerEvent(eventName) 197 except AttributeError: 198 pass 199 200 else: 201 self.SetControllerVariable(controllerVariableName, value) 202 203 def LoadDungeonAssetManipulations(self): 204 slimItem = self.typeData.get('slimItem') 205 if slimItem.dunID and slimItem.dunObjectID: 206 return GetAssetManipulations(slimItem.dunID, slimItem.dunObjectID) 207 208 def SetDungeonAssetManipulations(self): 209 if self._dungeonAssetManipulations is None: 210 self._dungeonAssetManipulations = self.LoadDungeonAssetManipulations() 211 if self._dungeonAssetManipulations is None: 212 return 213 for name, value in self._dungeonAssetManipulations.iteritems(): 214 value = GetControllerReadyData(value) 215 if isinstance(value, float): 216 self.SetControllerVariable(name, value) 217 218 for externalParameter in blue.FindInterface(self.model, 'Tr2ExternalParameter'): 219 if externalParameter.name in self._dungeonAssetManipulations: 220 try: 221 externalParameter.SetValue(self._dungeonAssetManipulations[externalParameter.name]) 222 except TypeError: 223 pass 224 225 def UnblockModelEvent(self): 226 self.modelLoadedEvent.set() 227 228 def _GetComponentRegistry(self): 229 return self.ballpark.componentRegistry 230 231 def SetControllerVariable(self, name, value): 232 self.logger.debug('SpaceObject: set controller variable %s to %s', name, value) 233 try: 234 self.model.SetControllerVariable(name, float(value)) 235 except AttributeError: 236 pass 237 238 for state, item in self.animationStateObjects.items(): 239 try: 240 item.SetControllerVariable(name, float(value)) 241 except AttributeError: 242 pass 243 244 def TriggerStateObject(self, state): 245 if state in self.animationStateObjects: 246 self.RemoveAndClearModel(self.animationStateObjects[state]) 247 del self.animationStateObjects[state] 248 if state in self.typeData['animationStateObjects']: 249 dnaToLoad = self.typeData['animationStateObjects'][state] 250 stateObject = self.spaceObjectFactory.BuildFromDNA(dnaToLoad) 251 self._SetupModelAttributes(stateObject, '%d_%s' % (self.id, state)) 252 if self.model is not None: 253 stateObject.rotationCurve = self.model.rotationCurve 254 stateObject.StartControllers() 255 self._AddModelToScene(stateObject) 256 self.animationStateObjects[state] = stateObject 257 258 def GetModel(self): 259 if not self.model: 260 try: 261 self.modelLoadedEvent.wait() 262 except RuntimeError: 263 return None 264 265 return self.model 266 267 def GetSkinState(self): 268 characterID = self.typeData['slimItem'].charID 269 itemID = self.typeData['slimItem'].itemID 270 skinState = sm.GetService('cosmeticsSvc').GetCachedAppliedSkinState(characterID, itemID) 271 return skinState 272 273 def GetDNA(self): 274 if self.skinMaterialSetID is None: 275 skinState = self.GetSkinState() 276 if skinState is None: 277 materialSetID = self.typeData.get('slimItem').skinMaterialSetID 278 if materialSetID is not None: 279 self.skinMaterialSetID = materialSetID 280 self.logger.info('SKIN STATES - Apply SKIN to NPC, material set: %s', materialSetID) 281 else: 282 skin_type = skinState.skin_type 283 if skin_type == ShipSkinType.THIRD_PARTY_SKIN: 284 return CosmeticsManager.CreateSOFDNAfromSkinState(skinState, self.GetTypeID()) 285 if skin_type == ShipSkinType.FIRST_PARTY_SKIN: 286 skinData = skinState.skin_data 287 if skinData is not None: 288 self.skinMaterialSetID = sm.GetService('cosmeticsSvc').GetFirstPartySkinMaterialSetID(skinID=skinData.skin_id) 289 elif skin_type == ShipSkinType.NO_SKIN: 290 pass 291 return self._BuildSOFDNAFromTypeID() 292 293 def _BuildSOFDNAFromTypeID(self): 294 return gfxutils.BuildSOFDNAFromTypeID(typeID=self.GetTypeID(), materialSetID=self.skinMaterialSetID) 295 296 def _RetryLoadModelResource(self, fileName, sofDna, remainingRetryAttempts): 297 if self.released: 298 return 299 trinity.WaitForResourceLoads() 300 blue.synchro.Yield() 301 model = self._LoadModelResource(fileName, sofDna, retryOnFail=False) 302 if model is not None: 303 self._isWaitingForModel = False 304 self._ReplaceDummyModel(model) 305 return 306 if remainingRetryAttempts > 1: 307 remainingRetryAttempts -= 1 308 uthread2.call_after_wallclocktime_delay(self._RetryLoadModelResource, 1.0, fileName, sofDna, remainingRetryAttempts) 309 return 310 self._isWaitingForModel = False 311 graphic_id = self.typeData['graphicID'] 312 type_id = str(self.typeData['typeID']) 313 sof_hull_name = str(GetSofHullName(graphic_id)) 314 graphic_id = str(graphic_id) 315 logger.error('[%s] Error: Object type %s has invalid graphicFile, using graphicID: %s', str(self.id), type_id, graphic_id, extra={'sentry_tags': {'type_id': type_id, 316 'graphic_id': graphic_id, 317 'sof_hull_name': sof_hull_name}}) 318 319 def _ReplaceDummyModel(self, newModel): 320 if self.model is None or newModel is None: 321 return 322 if not self._isWaitingForModel: 323 return 324 self.logger.debug('Replacing dummyModel for type: %s', self.typeData['typeID']) 325 controllerEvents = self.model.GetControllerVariables() 326 self.RemoveAndClearModel(self.model) 327 self._SetupModelAndAddToScene(loadedModel=newModel) 328 for controllerEvent, value in controllerEvents.items(): 329 newModel.SetControllerVariable(controllerEvent, float(value)) 330 331 newModel.StartControllers() 332 333 def _LoadModelResource(self, fileName = None, sofDna = None, retryOnFail = True): 334 model = None 335 sofDNA = sofDna or self.GetDNA() 336 self.logger.debug("LoadModel fileName='%s' sofDNA='%s'", fileName, sofDNA) 337 if sofDNA is not None and fileName is None: 338 model = self.spaceObjectFactory.BuildFromDNA(sofDNA) 339 skinState = self.GetSkinState() 340 if skinState is not None: 341 skin_type = skinState.skin_type 342 if skin_type == ShipSkinType.THIRD_PARTY_SKIN: 343 CosmeticsManager.UpdatePatternProjectionParametersFromSkinState(skinState, model, self.GetTypeID()) 344 blend_mode = skinState.skin_data.slot_layout.pattern_blend_mode 345 if blend_mode is not None: 346 CosmeticsManager.SetBlendMode(model, blendMode=blend_mode) 347 else: 348 if fileName is None: 349 fileName = self.typeData.get('graphicFile') 350 if fileName is not None and len(fileName): 351 model = blue.resMan.LoadObject(fileName) 352 if model is None: 353 graphicID = self.typeData['graphicID'] 354 typeID = str(self.typeData['typeID']) 355 if graphicID is None and fileName is None and sofDNA is None: 356 logger.error('Type %s is attempting to load a model without a resource defined', typeID, extra={'sentry_tags': {'type_id': typeID}}) 357 elif retryOnFail: 358 logger.debug('Type %s failed to load and was assigned a temporary model resource', typeID) 359 model = trinity.EveShip2() 360 model.name = 'ToBeReplaced_' + typeID 361 retryAttempts = 3 362 self._isWaitingForModel = True 363 uthread2.StartTasklet(self._RetryLoadModelResource, fileName, sofDNA, retryAttempts) 364 else: 365 self._isWaitingForModel = False 366 return model 367 368 def _SetupModelAndAddToScene(self, fileName = None, loadedModel = None, addToScene = True, sofDna = None): 369 if loadedModel: 370 model = loadedModel 371 else: 372 model = self._LoadModelResource(fileName, sofDna) 373 if self.released: 374 return 375 if not model: 376 self.logger.error('Could not load model for spaceobject. FileName:%s SofDna:%s typeID:%s', fileName, sofDna, getattr(self, 'typeID', '?')) 377 return 378 self._SetupModelAttributes(model, '%d' % self.id) 379 if addToScene: 380 if self.spaceMgr.GetScene() is None: 381 self.logger.warning('scene no longer exists, skipping adding model to sceneFileName:%s SofDna:%s typeID:%s', fileName, sofDna, getattr(self, 'typeID', '?')) 382 else: 383 self._AddModelToScene(model) 384 return model 385 386 def _SetupModelAttributes(self, model, objectName): 387 model.translationCurve = self 388 model.rotationCurve = self 389 if not model.name.startswith('ToBeReplaced'): 390 model.name = objectName 391 if hasattr(model, 'albedoColor'): 392 model.albedoColor = eveSpaceObject.GetAlbedoColor(model) 393 394 def _AddModelToScene(self, model): 395 if model is not None: 396 scene = self.spaceMgr.GetScene() 397 if scene is not None: 398 objectIsGlobal = False 399 if hasattr(model, 'translationCurve'): 400 if model.translationCurve is not None and hasattr(model.translationCurve, 'isGlobal'): 401 objectIsGlobal = model.translationCurve.isGlobal 402 scene.objects.append(model) 403 if objectIsGlobal and type(model) == trinity.EveStation2: 404 if not self.globalObjectHandlerThread: 405 self.model = model 406 self.globalObjectHandlerThread = uthread2.StartTasklet(self._GlobalObjectHandlerThread) 407 else: 408 raise RuntimeError('Invalid object loaded by spaceObject: %s' % str(model)) 409 410 def IsModelWithinDistance(self, distance): 411 distanceToObject = 0.0 412 if self.model is not None: 413 try: 414 distanceToObject = self.model.translationCurve.surfaceDist 415 except AttributeError: 416 distanceToObject = geo2.Vec3Length(self.model.modelWorldPosition) 417 418 return distanceToObject < distance 419 420 def _GlobalObjectHandlerThread(self): 421 while True: 422 if self.model is not None: 423 try: 424 shouldUpdate = self.IsModelWithinDistance(float(GRIDSIZE) * 1000.0) 425 self.model.mute = not shouldUpdate 426 self.model.display = shouldUpdate 427 self.model.update = shouldUpdate 428 blue.synchro.SleepSim(1000) 429 except AttributeError: 430 break 431 432 else: 433 blue.synchro.Yield() 434 if not self.globalObjectHandlerThread: 435 break 436 437 def LoadAdditionalModel(self, fileName = None, sofDna = None): 438 model = self._SetupModelAndAddToScene(fileName=fileName, sofDna=sofDna) 439 if model is not None: 440 model.StartControllers() 441 if fileName is not None: 442 self.additionalModels.append(model) 443 return model 444 445 def NotifyModelLoaded(self): 446 if self.model is not None: 447 self.logger.debug('SpaceObject - NotifyModelLoaded') 448 self.modelLoadedEvent.set() 449 self.modelLoadSignal() 450 self.sm.GetService('FxSequencer').NotifyModelLoaded(self.id) 451 else: 452 self.logger.warning('SpaceObject - NotifyModelLoaded called without a model present, no notification was done') 453 454 def RegisterForModelLoad(self, func): 455 self.modelLoadSignal.connect(func) 456 457 def LoadModel(self, fileName = None, loadedModel = None, notify = True, addToScene = True): 458 self.model = self._SetupModelAndAddToScene(fileName, loadedModel, addToScene) 459 if self.model is None: 460 return 461 if GetGroupID(self.GetTypeID()) == groupMercenaryDen: 462 self.SetStaticRotation() 463 self.SetupAnimationInformation(self.model) 464 if notify: 465 self.NotifyModelLoaded() 466 467 def SetupAnimationInformation(self, model): 468 if not hasattr(model, 'animationUpdater'): 469 return 470 if self._audioEntity is None: 471 self._audioEntity = self._GetGeneralAudioEntity(model=model) 472 if model is not None and model.animationUpdater is not None: 473 model.animationUpdater.eventListener = self._audioEntity 474 475 def Assemble(self): 476 pass 477 478 def _ShouldFaceTheSun(self): 479 slimItem = self.typeData.get('slimItem') 480 return slimItem.typeID in TYPES_THAT_ALWAYS_FACE_THE_SUN 481 482 def _RotateToFaceTheSun(self): 483 if (self.x, self.y, self.z) == (0.0, 0.0, 0.0): 484 directionFromSunToMe = (1.0, 0.0, 0.0) 485 else: 486 directionFromSunToMe = (self.x, self.y, self.z) 487 self.AlignToDirection(directionFromSunToMe) 488 489 def SetStaticRotation(self): 490 if self.model is None: 491 return 492 if self._ShouldFaceTheSun(): 493 self._RotateToFaceTheSun() 494 return 495 self.model.rotationCurve = None 496 rot = self.typeData.get('dunRotation', None) 497 if rot: 498 yaw, pitch, roll = map(math.radians, rot) 499 quat = geo2.QuaternionRotationSetYawPitchRoll(yaw, pitch, roll) 500 if hasattr(self.model, 'rotation'): 501 self.model.rotation = quat 502 else: 503 self.model.rotationCurve = trinity.Tr2RotationAdapter() 504 self.model.rotationCurve.value = quat 505 for stateObject in self.animationStateObjects.values(): 506 stateObject.rotationCurve = self.model.rotationCurve 507 508 def _FindClosestBallDir(self, constgrp): 509 bp = self.sm.StartService('michelle').GetBallpark() 510 dist = 1e+100 511 closestID = None 512 for ballID, slimItem in bp.slimItems.iteritems(): 513 if slimItem.groupID == constgrp: 514 test = bp.DistanceBetween(self.id, ballID) 515 if test < dist: 516 dist = test 517 closestID = ballID 518 519 if closestID is None: 520 return (1.0, 0.0, 0.0) 521 ball = bp.GetBall(closestID) 522 direction = geo2.Vec3SubtractD((self.x, self.y, self.z), (ball.x, ball.y, ball.z)) 523 return direction 524 525 def FindClosestMoonDir(self): 526 return self._FindClosestBallDir(groupMoon) 527 528 def FindClosestPlanetDir(self): 529 return self._FindClosestBallDir(groupPlanet) 530 531 def GetStaticDirection(self): 532 return self.typeData.get('dunDirection', None) 533 534 def SetStaticDirection(self): 535 if self.model is None: 536 return 537 self.model.rotationCurve = None 538 direction = self.GetStaticDirection() 539 if direction is None: 540 self.logger.warning('No static direction defined - no rotation will be applied') 541 return 542 self.AlignToDirection(direction) 543 544 def AlignToDirection(self, direction): 545 if not self.model: 546 return 547 zaxis = direction 548 if geo2.Vec3LengthSqD(zaxis) > 0.0: 549 zaxis = geo2.Vec3NormalizeD(zaxis) 550 xaxis = geo2.Vec3CrossD(zaxis, (0, 1, 0)) 551 if geo2.Vec3LengthSqD(xaxis) == 0.0: 552 zaxis = geo2.Vec3AddD(zaxis, mathCommon.RandomVector(0.0001)) 553 zaxis = geo2.Vec3NormalizeD(zaxis) 554 xaxis = geo2.Vec3CrossD(zaxis, (0, 1, 0)) 555 xaxis = geo2.Vec3NormalizeD(xaxis) 556 yaxis = geo2.Vec3CrossD(xaxis, zaxis) 557 else: 558 self.logger.error('Invalid direction (%s). Unable to rotate it.', direction) 559 return 560 mat = ((xaxis[0], 561 xaxis[1], 562 xaxis[2], 563 0.0), 564 (yaxis[0], 565 yaxis[1], 566 yaxis[2], 567 0.0), 568 (-zaxis[0], 569 -zaxis[1], 570 -zaxis[2], 571 0.0), 572 (0.0, 0.0, 0.0, 1.0)) 573 quat = geo2.QuaternionRotationMatrix(mat) 574 if hasattr(self.model, 'modelRotationCurve'): 575 if not self.model.modelRotationCurve: 576 self.model.modelRotationCurve = trinity.Tr2RotationAdapter() 577 self.model.modelRotationCurve.value = quat 578 else: 579 self.model.rotationCurve = None 580 581 def UnSync(self, model = None): 582 if model is None: 583 model = self.model 584 if model is None: 585 return 586 scaling = 0.95 + random.random() * 0.1 587 for adapter in model.Find(['trinity.Tr2RotationAdapter', 'trinity.Tr2TranslationAdapter']): 588 adapter.RandomizeStart() 589 adapter.ScaleTime(scaling) 590 591 def Display(self, display = 1, canYield = True): 592 if self.model is None: 593 if display: 594 self.logger.warning('Display - No model') 595 return 596 if canYield: 597 blue.synchro.Yield() 598 if session.shipid == self.id and display and self.IsCloaked(): 599 self.sm.StartService('FxSequencer').OnSpecialFX(self.id, None, None, None, None, 'effects.CloakNoAmim', 0, 1, 0, 5, 0) 600 return 601 if self.model: 602 self.model.display = display 603 604 def IsCloaked(self): 605 return self.isCloaked 606 607 def OnDamageState(self, damageState): 608 pass 609 610 def _OnDamageState(self, damageState): 611 if self.model is not None and damageState is not None: 612 if not hasattr(self.model, 'SetImpactDamageState'): 613 return 614 states = [ (d if d is not None else 0.0) for d in damageState ] 615 self.model.SetImpactDamageState(states[0], states[1], states[2], False) 616 617 def GetDamageState(self): 618 bp = sm.GetService('michelle').GetBallpark() 619 if bp is not None: 620 return bp.GetDamageState(self.id) 621 622 def _UpdateImpacts(self, model = None): 623 if model is None: 624 model = self.model 625 states = self.GetDamageState() 626 if states is not None and model is not None: 627 damageState = [ (d if d is not None else 0.0) for d in states ] 628 model.SetImpactDamageState(damageState[0], damageState[1], damageState[2], True) 629 630 def DoFinalCleanup(self): 631 if not self.sm.IsServiceRunning('FxSequencer'): 632 return 633 self.sm.GetService('FxSequencer').RemoveAllBallActivations(self.id) 634 self.globalObjectHandlerThread = None 635 if destructionEffectType.isExplosionOrOverride(self.destructionEffectId): 636 self.ClearExplosion() 637 if not self.released: 638 self.destructionEffectId = destructionEffectType.NONE 639 self.Release() 640 SpaceObjectExplosionManager.AddExplosionTimeNotifications(self.id, 0, 0) 641 elif self.HasModels(): 642 scene = self.spaceMgr.GetScene() 643 self.ClearAndRemoveAllModels(scene) 644 645 def ClearExplosion(self, model = None): 646 if hasattr(self, 'gfx') and self.gfx is not None: 647 self.RemoveAndClearModel(self.gfx) 648 self.gfx = None 649 if self.explosionModel is not None: 650 if getattr(self, 'explosionDisplayBinding', False): 651 self.explosionDisplayBinding.destinationObject = None 652 self.explosionDisplayBinding = None 653 self.RemoveAndClearModel(self.explosionModel) 654 self.explosionModel = None 655 656 def Release(self, origin = None): 657 uthread2.StartTasklet(self._Release, origin) 658 659 def _Release(self, origin = None): 660 if self.released and not self.lockedFromRelease: 661 return 662 self.released = True 663 if self.lockedFromRelease: 664 return 665 self._modelChangeCallbacks = [] 666 if destructionEffectType.isExplosionOrOverride(self.destructionEffectId): 667 delay = self.Explode() 668 if delay: 669 blue.synchro.SleepSim(delay) 670 elif self.destructionEffectId == destructionEffectType.DISSOLVE: 671 destructionEffectManager = DestructionEffectManager(self) 672 destructionEffectManager.PlayDissolveEffect() 673 if self.destructionEffectId in destructionEffectType.DURATION_BY_DESTRUCTION_TYPE: 674 delay = destructionEffectType.DURATION_BY_DESTRUCTION_TYPE[self.destructionEffectId] 675 if delay: 676 blue.synchro.SleepSim(delay) 677 self.Display(display=0, canYield=False) 678 for model in self.additionalModels: 679 if model is not None: 680 model.display = False 681 682 if hasattr(self.model, 'animationUpdater') and self.model.animationUpdater is not None: 683 self.model.animationUpdater.eventListener = None 684 self.ClearAudio() 685 scene = self.spaceMgr.GetScene() 686 camera = sm.GetService('sceneManager').GetActiveSpaceCamera() 687 lookingAt = camera.GetLookAtItemID() 688 interestID = camera.GetTrackItemID() 689 if self.destructionEffectId != destructionEffectType.NONE and (self.id == lookingAt or interestID == self.id): 690 self.RemoveAllModelsFromScene(scene) 691 else: 692 self.ClearAndRemoveAllModels(scene) 693 694 def ClearAudio(self): 695 self._audioEntity = None 696 if self.model is not None: 697 if hasattr(self.model, 'observers') and len(self.model.observers) > 0: 698 for observer in self.model.observers: 699 observer.observer = None 700 701 if hasattr(self.model, 'animationUpdater') and self.model.animationUpdater is not None: 702 self.model.animationUpdater.eventListener = None 703 704 def HasModels(self): 705 return self.model is not None 706 707 def ClearAnimationStateObjects(self, scene = None): 708 scene = scene or self.spaceMgr.GetScene() 709 for stateObject in self.animationStateObjects.values(): 710 self.RemoveAndClearModel(stateObject, scene) 711 712 self.animationStateObjects.clear() 713 714 def ClearAndRemoveAllModels(self, scene): 715 self.RemoveAndClearModel(self.model, scene) 716 self.model = None 717 for m in self.additionalModels: 718 self.RemoveAndClearModel(m, scene) 719 720 self.additionalModels = [] 721 self.ClearAnimationStateObjects() 722 723 def RemoveAllModelsFromScene(self, scene): 724 if scene is None: 725 return 726 scene.objects.fremove(self.model) 727 for m in self.additionalModels: 728 scene.objects.fremove(m) 729 730 self.ClearAnimationStateObjects() 731 732 def RemoveAndClearModel(self, model, scene = None): 733 if model: 734 self._Clearcurves(model) 735 else: 736 self.released = True 737 return 738 self.RemoveFromScene(model, scene) 739 740 def _Clearcurves(self, model): 741 if hasattr(model, 'translationCurve'): 742 model.translationCurve = None 743 model.rotationCurve = None 744 745 def RemoveFromScene(self, model, scene): 746 if scene is None: 747 scene = self.spaceMgr.GetScene() 748 if scene: 749 scene.objects.fremove(model) 750 751 def GetExplosionLookAtDelay(self): 752 return eveSpaceObject.GetDeathExplosionLookDelay(self.model, self.radius) 753 754 def GetTotalDestructionEffectTime(self): 755 if destructionEffectType.isExplosionOrOverride(self.destructionEffectId): 756 for delay, explosionBucketList in BALL_REMOVAL_DELAY_TO_EXPLOSION_BUCKETS.iteritems(): 757 if self.explosionBucketID in explosionBucketList: 758 return delay 759 760 elif self.destructionEffectId in destructionEffectType.DURATION_BY_DESTRUCTION_TYPE: 761 return destructionEffectType.DURATION_BY_DESTRUCTION_TYPE[self.destructionEffectId] 762 return 60 763 764 def Explode(self): 765 if self.exploded: 766 return 0 767 slimItem = self.typeData.get('slimItem') 768 self.sm.ScatterEvent('OnObjectExplode', slimItem.typeID, slimItem.itemID) 769 self.exploded = True 770 if gfxsettings.Get(gfxsettings.UI_EXPLOSION_EFFECTS_ENABLED): 771 if self.explosionBucketID is not None and self.model is not None: 772 locator = getattr(self.model, 'lastDamageLocatorHit', -1) 773 self.logger.debug("Exploding explosion bucket '%s' at locator %s", self.explosionBucketID, locator) 774 scene = self.spaceMgr.GetScene() 775 special = self.destructionEffectId == destructionEffectType.EXPLOSION_OVERRIDE 776 wreckSwitchTime, _, __ = SpaceObjectExplosionManager.ExplodeBucketForBall(self, scene, locator, special=special) 777 return wreckSwitchTime 778 self.logger.warning('GraphicID %s does not have an explosionBucketID. typeID: %s', self.typeData['graphicID'], self.typeID) 779 return 0 780 781 def IsTriggeredForDestruction(self): 782 return self.destructionEffectId is not None and self.destructionEffectId is not destructionEffectType.NONE 783 784 def PrepareForFiring(self): 785 pass 786 787 def GetEventNameFromSlimItem(self, defaultSoundUrl): 788 slimItem = self.typeData.get('slimItem') 789 eventName = spaceobjaudio.GetSoundUrl(slimItem, defaultSoundUrl) 790 return eventName 791 792 def SetupAmbientAudio(self, defaultSoundUrl = None, model = None): 793 audioUrl = self.GetEventNameFromSlimItem(defaultSoundUrl) 794 if audioUrl is None: 795 return 796 audentity = self._GetGeneralAudioEntity(model=model) 797 if audentity is not None: 798 spaceobjaudio.PlayAmbientAudio(audentity, audioUrl) 799 800 def SetupSharedAmbientAudio(self, defaultSoundUrl = None): 801 eventName = self.GetEventNameFromSlimItem(defaultSoundUrl) 802 if eventName is None or self.model is None: 803 return 804 spaceobjaudio.SetupSharedEmitterForAudioEvent(self.id, self.model, eventName) 805 806 def LookAtMe(self): 807 pass 808 809 def _GetGeneralAudioEntity(self, model = None, recreate = False): 810 if model is None: 811 model = self.model 812 if model is None: 813 self._audioEntity = None 814 self.logger.warning('model is None, cannot play audio.') 815 elif recreate or self._audioEntity is None: 816 self._audioEntity = spaceobjaudio.SetupAudioEntity(model, self.id) 817 return self._audioEntity 818 819 def PlayGeneralAudioEvent(self, eventName): 820 audentity = self._GetGeneralAudioEntity() 821 if audentity is not None: 822 spaceobjaudio.SendEvent(audentity, eventName) 823 824 def GetNamedAudioEmitterFromObservers(self, emitterName): 825 if getattr(self, 'model', None) is None: 826 return 827 for triObserver in self.model.Find('trinity.TriObserverLocal'): 828 if triObserver.observer.name.lower() == emitterName: 829 return triObserver.observer 830 831 def PlaySound(self, event): 832 if self.model is None: 833 return 834 if hasattr(self.model, 'observers'): 835 for obs in self.model.observers: 836 obs.observer.SendEvent(unicode(event)) 837 return 838 839 self.logger.error("Space Object: %s can't play sound. Sound observer not found.", self.typeData.get('typeName', None)) 840 841 def OnSlimItemUpdated(self, slimItem): 842 oldSlim = self.typeData['slimItem'] 843 self.typeData['slimItem'] = slimItem 844 if getattr(oldSlim, 'skinMaterialSetID') != getattr(slimItem, 'skinMaterialSetID'): 845 self._skinChangeTasklets.append(uthread2.StartTasklet(self.ChangeSkin)) 846 self.SetControllerVariablesFromSlimItem(slimItem) 847 848 def ChangeSkin(self, structure_id = None, paintwork = None): 849 if self.model is None: 850 return 851 if sm.GetService('subway').InJump(): 852 return 853 oldModel = self.nextModel or self.model 854 nextModel = self._LoadModelResource(None) 855 blue.resMan.Wait() 856 self.fitted = False 857 self.nextModel = self._SetupModelAndAddToScene(loadedModel=nextModel, addToScene=False) 858 if self.nextModel is None: 859 return 860 self.SetupAnimationInformation(self.nextModel) 861 self._SetupModelAttributes(self.nextModel, '%d' % self.id) 862 self.ChangingSkin(self.nextModel) 863 ChangeSkin(oldModel, self.nextModel, self.spaceMgr.GetScene(), postSkinChangeCallback=self.PostSkinChangeCallback) 864 865 def ChangingSkin(self, nextModel): 866 uthread2.StartTasklet(self.logoLoader.Load, self.nextModel, self.typeData.get('slimItem')) 867 868 def PostChangingSkin(self): 869 pass 870 871 def CancelSkinChange(self): 872 for skinChangeTasklet in self._skinChangeTasklets: 873 skinChangeTasklet.kill() 874 875 self._skinChangeTasklets = [] 876 if self.model != self.nextModel and self.nextModel is not None: 877 self.PostSkinChangeCallback(self.model, self.nextModel) 878 RemoveAllTemporaryModels(self.spaceMgr.GetScene()) 879 880 def PostSkinChangeCallback(self, oldModel, newModel): 881 self.RemoveAndClearModel(oldModel) 882 self.model = newModel 883 self.model.clipSphereFactor2 = 0.0 884 self.PostChangingSkin() 885 self._GetGeneralAudioEntity(self.model, recreate=True) 886 self.NotifyModelLoaded() 887 for callback in self._modelChangeCallbacks: 888 callback(newModel) 889 890 def RegisterModelChangeNotification(self, callback): 891 self._modelChangeCallbacks.append(callback) 892 893 def UnregisterModelChangeNotification(self, callback): 894 if callback in self._modelChangeCallbacks: 895 self._modelChangeCallbacks.remove(callback) 896 897 def LockFromRelease(self): 898 self.lockedFromRelease = True 899 900 def UnlockFromRelease(self, scene): 901 self.lockedFromRelease = False 902 if self.released: 903 self.ClearAudio() 904 self.ClearAndRemoveAllModels(scene)