ship.py (21015B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\spaceObject\ship.py 2 from eveexceptions.exceptionEater import ExceptionEater 3 from inventorycommon.util import IsModularShip 4 from inventorycommon.const import categoryFighter, flagFighterBay, flagFighterTube0, flagFighterTube1, flagFighterTube2, flagFighterTube3, flagFighterTube4 5 import blue 6 import uthread2 7 import uthread 8 import evetypes 9 import eve.common.lib.appConst as const 10 from eve.client.script.environment.model.turretSet import TurretSet 11 from eve.client.script.environment.spaceObject.spaceObject import SpaceObject 12 from eve.client.script.environment.spaceObject.cosmeticsManager import CosmeticsManager 13 from eveSpaceObject import multiHullComponent 14 from eveSpaceObject import spaceobjanimation 15 import destiny 16 import geo2 17 import evegraphics.utils as gfxutils 18 from evegraphics.CosmeticsLoader import CosmeticsLoader 19 import evetypes 20 from fsdBuiltData.common.graphicIDs import GetSofRaceName 21 from eveaudio.const import ActingParty 22 from eveaudio.turrets import setupTurretMovementAudio 23 from cosmetics.client.ships.ship_skin_signals import on_skin_state_set 24 from cosmetics.common.ships.skins.static_data.skin_type import ShipSkinType 25 from mining.client import mining_signals 26 import dogma.const as dogmaConst 27 from eve.common.script.mgt.fighterConst import TUBE_STATE_EMPTY, TUBE_STATE_LANDING, TUBE_STATE_REFUELLING 28 from logging import getLogger 29 logger = getLogger(__name__) 30 RACE_CONTROLLER_DICT = {'None': 0, 31 'gallente': 1, 32 'caldari': 2, 33 'minmatar': 3, 34 'amarr': 4} 35 TUBE_CONTROLLER_PARAM_DICT = {flagFighterTube0: 'carrier_fighterbay_1_race', 36 flagFighterTube1: 'carrier_fighterbay_2_race', 37 flagFighterTube2: 'carrier_fighterbay_3_race', 38 flagFighterTube3: 'carrier_fighterbay_4_race'} 39 FIGHTER_COUNT_PARAM_DICT = {flagFighterTube0: 'carrier_fighterbay_1_numFighters', 40 flagFighterTube1: 'carrier_fighterbay_2_numFighters', 41 flagFighterTube2: 'carrier_fighterbay_3_numFighters', 42 flagFighterTube3: 'carrier_fighterbay_4_numFighters'} 43 FIGHTER_LAUNCHED_PARAM_DICT = {flagFighterTube0: 'carrier_fighterbay_1_launched', 44 flagFighterTube1: 'carrier_fighterbay_2_launched', 45 flagFighterTube2: 'carrier_fighterbay_3_launched', 46 flagFighterTube3: 'carrier_fighterbay_4_launched'} 47 48 class Ship(SpaceObject): 49 __notifyevents__ = ['OnAllianceLogoReady', 50 'OnCorpLogoReady', 51 'OnFighterAddedToController', 52 'OnFighterRemovedFromController', 53 'OnFighterTubeContentUpdate', 54 'OnFighterTubeContentEmpty', 55 'OnGodmaShipEffect'] 56 57 def __init__(self): 58 SpaceObject.__init__(self) 59 self.cosmeticsLoader = CosmeticsLoader((CosmeticsLoader.ALLIANCE, CosmeticsLoader.CORP)) 60 sm.RegisterNotify(self) 61 self.cosmeticsItems = [] 62 on_skin_state_set.connect(self._on_skin_state_set) 63 self.cosmeticsManager = CosmeticsManager() 64 self.activeTargetID = None 65 self.fitted = False 66 self.cachedShip = None 67 self.fittingThread = None 68 self.turrets = [] 69 self.modules = {} 70 self.stanceID = None 71 self.lastStanceID = None 72 self._multiHullComponent = multiHullComponent.MultiHullComponent(self.id) if self.IsModularShip() else None 73 self.damageOverTimeTaken = 0.0 74 self.visualDamageThread = None 75 self.isReceivingMiningEvents = False 76 self.isHarvesting = False 77 self.activeHarvestingModules = set() 78 if self._multiHullComponent is not None: 79 self._multiHullComponent.SetSubsystemModules(self.typeData.get('slimItem').modules) 80 81 def _UpdateFighterTubeVisuals(self, tubeFlagID, fighterTypeID = None, squadronSize = None): 82 if not self.model: 83 return 84 if tubeFlagID in TUBE_CONTROLLER_PARAM_DICT and fighterTypeID is not None: 85 gid = evetypes.GetGraphicID(fighterTypeID) 86 raceName = GetSofRaceName(gid) 87 self.model.SetControllerVariable(TUBE_CONTROLLER_PARAM_DICT[tubeFlagID], RACE_CONTROLLER_DICT.get(raceName, 0)) 88 if tubeFlagID in FIGHTER_COUNT_PARAM_DICT and squadronSize is not None: 89 self.model.SetControllerVariable(FIGHTER_COUNT_PARAM_DICT[tubeFlagID], squadronSize) 90 91 def _UpdateFighterTubeVisualsFromState(self, tubeFlagID): 92 fightersSvc = sm.GetService('fighters') 93 shipFighterState = getattr(fightersSvc, 'shipFighterState', None) 94 if shipFighterState is None: 95 return 96 inSpace = shipFighterState.GetFightersInSpace(tubeFlagID) 97 if inSpace is not None: 98 _, fighterTypeID, _, squadronSize = inSpace 99 self._UpdateFighterTubeVisuals(tubeFlagID, fighterTypeID=fighterTypeID, squadronSize=squadronSize) 100 if tubeFlagID in FIGHTER_LAUNCHED_PARAM_DICT and self.model: 101 self.model.SetControllerVariable(FIGHTER_LAUNCHED_PARAM_DICT[tubeFlagID], 1.0) 102 return 103 inTube = shipFighterState.GetFightersInTube(tubeFlagID) 104 if inTube is not None: 105 _, fighterTypeID, _, squadronSize = inTube 106 self._UpdateFighterTubeVisuals(tubeFlagID, fighterTypeID=fighterTypeID, squadronSize=squadronSize) 107 if tubeFlagID in FIGHTER_LAUNCHED_PARAM_DICT and self.model: 108 self.model.SetControllerVariable(FIGHTER_LAUNCHED_PARAM_DICT[tubeFlagID], 0.0) 109 return 110 tubeStatus = shipFighterState.GetTubeStatus(tubeFlagID) 111 if tubeStatus and tubeStatus.statusID in (TUBE_STATE_LANDING, TUBE_STATE_REFUELLING): 112 return 113 self._UpdateFighterTubeVisuals(tubeFlagID, squadronSize=0) 114 if tubeFlagID in TUBE_CONTROLLER_PARAM_DICT and self.model: 115 self.model.SetControllerVariable(TUBE_CONTROLLER_PARAM_DICT[tubeFlagID], 0) 116 if tubeFlagID in FIGHTER_LAUNCHED_PARAM_DICT and self.model: 117 self.model.SetControllerVariable(FIGHTER_LAUNCHED_PARAM_DICT[tubeFlagID], 0.0) 118 119 def OnFighterAddedToController(self, fighterID, fighterTypeID, tubeFlagID, squadronSize, abilitySlotStates): 120 if session.shipid != self.id: 121 return 122 self._UpdateFighterTubeVisualsFromState(tubeFlagID) 123 124 def OnFighterRemovedFromController(self, fighterID, tubeFlagID): 125 if session.shipid != self.id: 126 return 127 self._UpdateFighterTubeVisualsFromState(tubeFlagID) 128 129 def OnFighterTubeContentUpdate(self, tubeFlagID, fighterID, fighterTypeID, squadronSize): 130 if session.shipid != self.id: 131 return 132 self._UpdateFighterTubeVisualsFromState(tubeFlagID) 133 134 def OnFighterTubeContentEmpty(self, tubeFlagID): 135 if session.shipid != self.id: 136 return 137 self._UpdateFighterTubeVisualsFromState(tubeFlagID) 138 139 def OnGodmaShipEffect(self, itemID, effectID, t, start, active, environment, startTime, duration, repeat, error, actualStopTime = None): 140 if itemID not in self.modules: 141 return 142 if effectID in (dogmaConst.effectMining, dogmaConst.effectMiningLaser, dogmaConst.effectMiningClouds): 143 if active: 144 self.activeHarvestingModules.add(itemID) 145 else: 146 self.activeHarvestingModules.discard(itemID) 147 self._UpdateHarvestingState() 148 149 def _CheckHarvestingState(self): 150 return len(self.activeHarvestingModules) > 0 151 152 def _UpdateHarvestingState(self): 153 self.isHarvesting = self._CheckHarvestingState() 154 if self.isHarvesting and self.model: 155 self.model.SetControllerVariable('isHarvesting', 1.0) 156 else: 157 self.model.SetControllerVariable('isHarvesting', 0.0) 158 159 def _on_skin_state_set(self, ship_instance_id, skin_state): 160 if ship_instance_id != self.id: 161 return 162 logger.info('SKIN STATES - space object %s received on_skin_state_set with %s' % (ship_instance_id, skin_state)) 163 if skin_state is not None: 164 skin_type = skin_state.skin_type 165 if skin_type == ShipSkinType.FIRST_PARTY_SKIN: 166 skinID = skin_state.skin_data.skin_id 167 self.skinMaterialSetID = sm.GetService('cosmeticsSvc').GetFirstPartySkinMaterialSetID(skinID) 168 self._ApplySkin(skinMaterialSetID=self.skinMaterialSetID) 169 elif skin_type == ShipSkinType.NO_SKIN: 170 self.skinMaterialSetID = None 171 self._ApplySkin(skinMaterialSetID=self.skinMaterialSetID) 172 elif skin_type == ShipSkinType.THIRD_PARTY_SKIN: 173 self.skinMaterialSetID = None 174 self._ApplySkin(skinMaterialSetID=self.skinMaterialSetID) 175 else: 176 logger.error('Failed to apply SKIN to ship: SKIN type %s is not supported', skin_type) 177 178 def _ApplySkin(self, skinMaterialSetID): 179 self._skinChangeTasklets.append(uthread2.StartTasklet(self.ChangeSkin)) 180 181 def IsModularShip(self): 182 return IsModularShip(self.GetTypeID()) 183 184 def _BuildSOFDNAFromTypeID(self): 185 multiHullTypeIDList = None if self._multiHullComponent is None else self._multiHullComponent.GetTypeIDList() 186 return gfxutils.BuildSOFDNAFromTypeID(typeID=self.GetTypeID(), materialSetID=self.skinMaterialSetID, multiHullTypeIDList=multiHullTypeIDList) 187 188 def LoadModel(self, fileName = None, loadedModel = None, notify = True, addToScene = True, canYield = True): 189 SpaceObject.LoadModel(self, fileName, loadedModel, notify, addToScene) 190 self.UpdateCosmeticsItems() 191 self.Display(1, canYield=False) 192 193 def OnSubSystemChanged(self, newSlim): 194 self.typeData['slimItem'] = newSlim 195 if self._multiHullComponent is not None: 196 self._multiHullComponent.SetSubsystemModules(self.typeData.get('slimItem').modules) 197 self.UnfitHardpoints() 198 self.RemoveAndClearModel(self.model) 199 self.LoadModel(canYield=False) 200 self.FitHardpoints(self.model) 201 202 def GetStanceIDFromSlimItem(self, slimItem): 203 if slimItem.shipStance is None: 204 return 205 _, _, stanceID = slimItem.shipStance 206 return stanceID 207 208 def OnAllianceLogoReady(self, allianceID, _size): 209 if self.cosmeticsLoader.HasID(allianceID): 210 uthread.new(self.cosmeticsLoader.Update, self.model, self.cosmeticsItems) 211 212 def OnCorpLogoReady(self, corpID, _size): 213 if self.cosmeticsLoader.HasID(corpID): 214 uthread.new(self.cosmeticsLoader.Update, self.model, self.cosmeticsItems) 215 216 def GetCosmeticsFromSlimItem(self, slimItem): 217 if slimItem.cosmeticsItems is None: 218 return 219 items = slimItem.cosmeticsItems 220 return items 221 222 def UpdateCosmeticsItems(self): 223 uthread.new(self.cosmeticsLoader.Load, self.model, self.typeData['slimItem']) 224 225 def OnDamageState(self, damageState): 226 self._OnDamageState(damageState) 227 228 def GracefulEffectPropagatorRemover(self, sleepTime): 229 self.model.SetControllerVariable('gracefullyRemove', 1.0) 230 blue.synchro.SleepSim(sleepTime) 231 if not self.visualDamageThread: 232 self.model.SetControllerVariable('gracefullyRemove', 0.0) 233 self.model.SetControllerVariable('explosive_damageAmount', self.damageOverTimeTaken) 234 return 235 fxSequencer = sm.GetService('FxSequencer') 236 fxSequencer.OnSpecialFX(self.id, None, None, self.id, None, 'effects.VisualDamageSystemShipEffect', 1, 0, None, graphicInfo={}) 237 238 def UpdateDamageOverTime(self): 239 currentDamageOverTimeTaken = getattr(self.typeData['slimItem'], 'dotHpPercentageApplied', 0.0) 240 if currentDamageOverTimeTaken > 0.0: 241 self.damageOverTimeTaken = currentDamageOverTimeTaken / 4.0 242 self.visualDamageThread = None 243 fxSequencer = sm.GetService('FxSequencer') 244 fxSequencer.OnSpecialFX(self.id, None, None, self.id, None, 'effects.VisualDamageSystemShipEffect', 1, 1, None, graphicInfo={}) 245 self.model.SetControllerVariable('gracefullyRemove', 0.0) 246 self.model.SetControllerVariable('explosive_damageAmount', self.damageOverTimeTaken) 247 if self.model: 248 self.model.SetControllerVariable('boundingSphereRadius', self.model.boundingSphereRadius) 249 elif self.damageOverTimeTaken > 0.0 and self.visualDamageThread is None: 250 self.visualDamageThread = uthread.new(self.GracefulEffectPropagatorRemover, 18000) 251 252 def OnSlimItemUpdated(self, slimItem): 253 SpaceObject.OnSlimItemUpdated(self, slimItem) 254 with ExceptionEater('ship::OnSlimItemUpdated failed'): 255 self.typeData['slimItem'] = slimItem 256 stanceID = self.GetStanceIDFromSlimItem(self.typeData['slimItem']) 257 if stanceID != self.stanceID: 258 self.lastStanceID = self.stanceID 259 self.stanceID = stanceID 260 spaceobjanimation.SetShipAnimationStance(self.model, stanceID) 261 if hasattr(self.typeData['slimItem'], 'cosmeticsItems'): 262 self.cosmeticsItems = self.GetCosmeticsFromSlimItem(slimItem) 263 self.UpdateCosmeticsItems() 264 if hasattr(self.typeData['slimItem'], 'dotHpPercentageApplied'): 265 self.UpdateDamageOverTime() 266 267 def AddChildEffect(self, effect): 268 if self.model: 269 self.model.effectChildren.append(effect) 270 271 def RemoveChildEffect(self, effect): 272 if not self.model: 273 return 274 if effect in self.model.effectChildren: 275 effect.mute = True 276 self.model.effectChildren.fremove(effect) 277 278 def _SetInitialState(self, model = None): 279 if model is None: 280 model = self.model 281 stanceID = self.GetStanceIDFromSlimItem(self.typeData['slimItem']) 282 if stanceID is not None: 283 self.stanceID = stanceID 284 spaceobjanimation.SetShipAnimationStance(model, self.stanceID) 285 if self.mode == destiny.DSTBALL_WARP: 286 self.SetControllerVariable('IsWarping', True) 287 try: 288 model.boosters.warpIntensity = 1 289 except AttributeError: 290 pass 291 292 if session.shipid != self.id: 293 self.sm.GetService('FxSequencer').OnSpecialFX(self.id, None, None, None, None, 'effects.WarpIn', 0, 1, 0) 294 if session.shipid == self.id: 295 self.SetControllerVariable(ActingParty.__name__, ActingParty.firstParty.value) 296 for tubeFlagID in TUBE_CONTROLLER_PARAM_DICT: 297 self._UpdateFighterTubeVisualsFromState(tubeFlagID) 298 299 self._UpdateHarvestingState() 300 301 def Assemble(self): 302 self.AssembleModel(self.model) 303 304 def AssembleModel(self, model): 305 if model is None: 306 return 307 self.UnSync(model) 308 if self.id == session.shipid: 309 self.CheckAmbientAudio() 310 self.FitHardpoints(model) 311 self._SetInitialState(model) 312 self._UpdateImpacts(model) 313 314 def Release(self): 315 if self.released: 316 return 317 if self.model is None: 318 return 319 self.modules = {} 320 self.activeHarvestingModules.clear() 321 self.isHarvesting = False 322 if self.isReceivingMiningEvents: 323 mining_signals.on_ore_mined.disconnect(self.HandleMiningEvent) 324 SpaceObject.Release(self, 'Ship') 325 audsvc = self.sm.GetServiceIfRunning('audio') 326 if audsvc.lastLookedAt == self: 327 audsvc.lastLookedAt = None 328 329 def LookAtMe(self): 330 if not self.model: 331 return 332 if not self.fitted: 333 self.FitHardpoints() 334 self.CheckAmbientAudio() 335 336 def CheckAmbientAudio(self): 337 audsvc = self.sm.GetServiceIfRunning('audio') 338 if audsvc.active: 339 lookedAt = audsvc.lastLookedAt 340 if lookedAt is None: 341 self.SetupAmbientAudio() 342 audsvc.lastLookedAt = self 343 elif lookedAt is not self: 344 lookedAt.PlayGeneralAudioEvent('shipsounds_stop') 345 self.SetupAmbientAudio() 346 audsvc.lastLookedAt = self 347 else: 348 return 349 350 def FitBoosters(self, alwaysOn = False, enableTrails = True, isNPC = False): 351 if self.typeID is None: 352 return 353 raceName = self.typeData.get('sofRaceName', None) 354 if raceName is None: 355 self.logger.error('SpaceObject type %s has invaldi raceID (not set!)', self.typeID) 356 raceName = 'generic' 357 if self.model is None: 358 self.logger.warning('No model to fit boosters') 359 return 360 if not hasattr(self.model, 'boosters'): 361 self.logger.warning('Model has no attribute boosters') 362 return 363 if self.model.boosters: 364 self.model.boosters.maxVel = self.maxVelocity 365 self.model.boosters.alwaysOn = alwaysOn 366 if not enableTrails: 367 self.model.boosters.trails = None 368 dogmaAttr = const.attributeMaxVelocity 369 if isNPC: 370 dogmaAttr = const.attributeEntityCruiseSpeed 371 velocity = self.sm.GetService('godma').GetTypeAttribute(self.typeID, dogmaAttr) 372 if velocity is None: 373 velocity = 1.0 374 self.model.maxSpeed = velocity 375 376 def EnterWarp(self): 377 for t in self.turrets: 378 t.EnterWarp() 379 380 self.SetControllerVariable('IsWarping', True) 381 try: 382 self.model.boosters.warpIntensity = 1 383 except AttributeError: 384 pass 385 386 def ExitWarp(self): 387 for t in self.turrets: 388 t.ExitWarp() 389 390 self.SetControllerVariable('IsWarping', False) 391 try: 392 self.model.boosters.warpIntensity = 0 393 except AttributeError: 394 pass 395 396 def UnfitHardpoints(self): 397 if not self.fitted: 398 return 399 newModules = {} 400 for key, val in self.modules.iteritems(): 401 if val not in self.turrets: 402 newModules[key] = val 403 404 self.modules = newModules 405 del self.turrets[:] 406 self.fitted = False 407 408 def FitHardpoints(self, model = None, blocking = False): 409 if model is None: 410 model = self.model 411 if getattr(self.fittingThread, 'alive', False): 412 self.fitted = False 413 self.fittingThread.kill() 414 if blocking: 415 self._FitHardpoints(model) 416 else: 417 self.fittingThread = uthread2.StartTasklet(self._FitHardpoints, model) 418 419 def _FitHardpoints(self, model = None): 420 if self.fitted: 421 return 422 if model is None: 423 model = self.model 424 if model is None: 425 self.logger.warning('FitHardpoints - No model') 426 return 427 self.fitted = True 428 newTurretSetDict = TurretSet.FitTurrets(self.id, model, self.typeData.get('sofFactionName', None)) 429 self.turrets = [] 430 for key, val in newTurretSetDict.iteritems(): 431 self.modules[key] = val 432 self.turrets.append(val) 433 moduleGroupID = evetypes.GetGroupID(val.turretTypeID) 434 if not self.isReceivingMiningEvents and moduleGroupID in const.miningModuleGroups: 435 self.isReceivingMiningEvents = True 436 mining_signals.on_ore_mined.connect(self.HandleMiningEvent) 437 438 if model and hasattr(model, 'locators') and hasattr(model, 'GetLocalBoundingBox'): 439 setupTurretMovementAudio([ x.GetTurretSet() for x in self.turrets ], model.locators, model.GetLocalBoundingBox()) 440 441 def HandleMiningEvent(self, asteroid_id, module_item_id, type_id, yield_quantity, crit_quantity, wasted_quantity): 442 if module_item_id in self.modules: 443 if crit_quantity > 0: 444 if self.modules[module_item_id] is not None: 445 self.modules[module_item_id].SetControllerVariable('miningCrit', 1.0) 446 447 def ApplyTorqueAtDamageLocator(self, damageLocatorID, impactVelocity, impactObjectMass): 448 if not self.model: 449 return 450 damageLocatorPosition = self.model.GetDamageLocator(damageLocatorID) 451 bsCenter = geo2.Vector(*self.model.GetBoundingSphereCenter()) 452 damageLocatorPosition -= bsCenter 453 q = self.GetQuaternionAt(blue.os.GetSimTime()) 454 damageLocatorPosition = geo2.QuaternionTransformVector((q.x, 455 q.y, 456 q.z, 457 q.w), damageLocatorPosition) 458 self.ApplyTorqueAtPosition(damageLocatorPosition, impactVelocity, impactObjectMass) 459 460 def ApplyTorqueAtPosition(self, position, impactVelocity, impactObjectMass): 461 if not self.model: 462 return 463 impactForce = geo2.Vec3Scale(impactVelocity, impactObjectMass) 464 self.ApplyImpulsiveForceAtPosition(impactForce, geo2.Vector(*position)) 465 466 def ChangingSkin(self, nextModel): 467 self._FitHardpoints(self.nextModel) 468 469 def PostChangingSkin(self): 470 self.FitBoosters() 471 if self.stanceID is not None: 472 spaceobjanimation.SetShipAnimationStance(self.model, self.stanceID) 473 self.UpdateCosmeticsItems()