missile.py (12818B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\spaceObject\missile.py 2 import math 3 import random 4 import blue 5 import carbon.common.script.util.logUtil as log 6 import eveSpaceObject.missile as gfxmissile 7 import evegraphics.settings as gfxsettings 8 import geo2 9 import inventorycommon.typeHelpers 10 import pytelemetry.zoning as telemetry 11 import uthread 12 from carbon.client.script.environment.audioService import GetAudioService 13 from eve.client.script.environment.spaceObject.spaceObject import SpaceObject 14 from eve.client.script.parklife.sceneManager import GetSceneManager 15 from eve.client.script.remote.michelle import GetMichelle 16 from fsdBuiltData.client.groupGraphics import GetGraphicIdsFromTypeID 17 from fsdBuiltData.common.graphicIDs import GetGraphicFile 18 from destructionEffect import destructionType as destructionEffectType 19 SECOND = 10000000 20 21 def _DestroyClientBall(ball): 22 bp = GetMichelle().GetBallpark() 23 if bp is not None and ball.ballpark is not None: 24 bp.RemoveBall(ball.id) 25 26 27 def _SpawnClientBall(position): 28 bp = GetMichelle().GetBallpark() 29 if bp is None: 30 return 31 egopos = bp.GetCurrentEgoPos() 32 explosionPosition = (position[0] + egopos[0], position[1] + egopos[1], position[2] + egopos[2]) 33 return bp.AddClientSideBall(explosionPosition) 34 35 36 def _ShakeCamera(shakeMagnitude, explosionPosition): 37 camera = GetSceneManager().GetActiveSpaceCamera() 38 if camera: 39 camera.ShakeCamera(shakeMagnitude, explosionPosition, 'Missile') 40 41 42 def _GetScene(): 43 return GetSceneManager().GetRegisteredScene('default') 44 45 46 def _GetMissilesEnabled(): 47 missilesDesired = gfxsettings.Get(gfxsettings.UI_MISSILES_ENABLED) 48 if missilesDesired: 49 scene = GetSceneManager().GetRegisteredScene('default', allowBlocking=False) 50 if scene is None: 51 return False 52 updateTime = scene.updateTime 53 now = blue.os.GetSimTime() 54 if now < updateTime: 55 return False 56 delta = blue.os.TimeDiffInMs(updateTime, now) 57 return delta < 2000 58 return missilesDesired 59 60 61 class Missile(SpaceObject): 62 __guid__ = 'spaceObject.Missile' 63 64 def __init__(self): 65 SpaceObject.__init__(self) 66 self.exploded = False 67 self.collided = False 68 self.targetId = None 69 self.sourceShipID = None 70 self.sourceModuleIDList = [] 71 self._delayedBall = None 72 self._randomizeImpactRotation = True 73 self.enabled = _GetMissilesEnabled() 74 self.missileRadius = 100 75 self._missileModel = None 76 77 @telemetry.ZONE_METHOD 78 def LoadModel(self, fileName = None, loadedModel = None): 79 if not self.enabled: 80 return 81 slimItem = self.typeData['slimItem'] 82 self.sourceShipID = slimItem.sourceShipID 83 self.sourceModuleIDList = slimItem.launchModules 84 impactFileName = inventorycommon.typeHelpers.GetGraphicFile(slimItem.typeID) 85 if impactFileName == '': 86 log.LogError('missile::LoadModel failed to get impact red filename for missile typeID ' + str(slimItem.typeID) + ' missileID : ' + str(self.id) + ' sourceShipID: ' + str(self.sourceShipID)) 87 return 88 missileGraphicID = GetGraphicIdsFromTypeID(slimItem.typeID, [None])[0] 89 if missileGraphicID is None: 90 log.LogError('missile::LoadModel failed to get missile graphicID for typeID %s from groupGraphics', slimItem.typeID) 91 return 92 missileFileName = GetGraphicFile(missileGraphicID) 93 if missileFileName is None: 94 log.LogError('missile::LoadModel failed to get missile red filename for missile typeID ' + str(slimItem.typeID) + ' missileID : ' + str(self.id) + ' sourceShipID: ' + str(self.sourceShipID)) 95 return 96 self.targetId = self.followId 97 self._missileModel = gfxmissile.MissileGraphics('Missile in %s' % self.id, missileFileName, impactFileName, self) 98 self.model = self._missileModel.LoadModel() 99 if self.model is None: 100 self.LogError('missile::LoadModel failed to load a model ' + str(missileFileName)) 101 return 102 self._missileModel.SetMissileCurves(self._GetMissileTranslationCurve(), self._GetMissileRotationCurve()) 103 scene = GetSceneManager().GetRegisteredScene('default') 104 scene.objects.append(self.model) 105 106 def _GetMissileTranslationCurve(self): 107 return self 108 109 def _GetMissileRotationCurve(self): 110 return self 111 112 def _GetModelTurret(self, moduleIdx): 113 if len(self.sourceModuleIDList) <= moduleIdx: 114 self.logger.warning('moduleIdx: + %s is too high to index into list!', str(moduleIdx)) 115 return 116 slimItemID = self.sourceModuleIDList[moduleIdx] 117 sourceShipBall = GetMichelle().GetBall(self.sourceShipID) 118 if sourceShipBall is not None: 119 if not hasattr(sourceShipBall, 'modules'): 120 return 121 if sourceShipBall.modules is None: 122 return 123 if slimItemID in sourceShipBall.modules: 124 return sourceShipBall.modules[slimItemID] 125 126 def _EstimateTimeToTarget(self, toCenter = False): 127 targetBall = GetMichelle().GetBall(self.targetId) 128 if targetBall is None: 129 return 5.0 130 now = blue.os.GetSimTime() 131 myPos = self.GetVectorAt(now) 132 targetPos = targetBall.GetVectorAt(now) 133 if toCenter: 134 targetRadius = 0 135 else: 136 targetRadius = targetBall.radius 137 return gfxmissile.EstimateTimeToTarget(myPos, targetPos, targetRadius, self.maxVelocity) 138 139 def _GetTimeToTarget(self): 140 timeToTarget = self._EstimateTimeToTarget() 141 doSpread = True 142 if timeToTarget < 1.6: 143 doSpread = False 144 timeToTargetCenter = max(0.5, self._EstimateTimeToTarget(toCenter=True)) 145 if timeToTarget > 0: 146 timeToTarget = (timeToTarget + timeToTargetCenter) * 0.5 147 else: 148 timeToTarget = timeToTargetCenter * 0.5 149 return (doSpread, timeToTarget) 150 151 def _GetAllTurretSets(self, moduleCount): 152 turretWrappers = [] 153 for moduleIdx in range(0, moduleCount): 154 turrets = self._GetModelTurret(moduleIdx) 155 if not isinstance(turrets, list): 156 turrets = [turrets] 157 for turretIndex, turret in enumerate(turrets): 158 if turret is not None and len(turret.turretSets) > 0: 159 turretWrappers.append(gfxmissile.MissileTurretWrapper(turret.turretSets[0], turret.StartShooting)) 160 if hasattr(turret.turretSets[0], 'randomizeExplosionRotation'): 161 self._randomizeImpactRotation = turret.turretSets[0].randomizeExplosionRotation 162 else: 163 turretWrappers.append(gfxmissile.MissileTurretWrapper(None)) 164 165 return turretWrappers 166 167 @telemetry.ZONE_METHOD 168 def Prepare(self): 169 if not self.enabled: 170 return 171 if self.collided: 172 return 173 SpaceObject.Prepare(self) 174 if self.model is None: 175 return 176 if getattr(self, 'sourceModuleIDList', None) is None: 177 self.sourceModuleIDList = [0] 178 moduleCount = max(len(self.sourceModuleIDList), 1) 179 doSpread, timeToTarget = self._GetTimeToTarget() 180 if not doSpread: 181 self.DoCollision(self.targetId, 0, 0, 0) 182 if len(self.model.warheads) != 1: 183 log.LogError('There must be one and only one warhead per missile in: ' + str(self.model.name)) 184 return 185 if self.targetId: 186 targetBall = GetMichelle().GetBall(self.targetId) 187 if targetBall is not None: 188 self._missileModel.SetTarget(targetBall.model, targetBall.radius) 189 self._missileModel.SetSourceTranslationCurve(GetMichelle().GetBall(self.sourceShipID)) 190 turretSetInfos = self._GetAllTurretSets(moduleCount) 191 self._missileModel.Start(turretSetInfos, timeToTarget, doSpread) 192 193 @telemetry.ZONE_METHOD 194 def RemoveAndClearModel(self, model, scene = None): 195 if model is not None: 196 SpaceObject.RemoveAndClearModel(self, model, scene=scene) 197 198 def DoCollision(self, targetId, fx, fy, fz, fake = False): 199 if self.collided: 200 return 201 self.collided = True 202 if self.model is None: 203 return 204 uthread.new(self._DoCollision) 205 206 def _DoCollision(self): 207 if self.model is None: 208 return 209 pos = self.GetVectorAt(blue.os.GetSimTime()) 210 self._delayedBall = _SpawnClientBall((pos.x, pos.y, pos.z)) 211 self._missileModel.SetMissileCurves(self._delayedBall, self) 212 213 def Expire(self): 214 self.exploded = True 215 216 def ApplyImpactTorqueToTarget(self, targetLocatorID, blastSize): 217 targetBall = GetMichelle().GetBall(self.targetId) 218 if hasattr(targetBall, 'ApplyTorqueAtDamageLocator') and targetLocatorID != -1 and not math.isnan(blastSize): 219 velocity = self.GetVectorDotAt(blue.os.GetSimTime()) 220 targetBall.ApplyTorqueAtDamageLocator(targetLocatorID, (velocity.x, velocity.y, velocity.z), blastSize) 221 222 def Explode(self): 223 return self.collided 224 225 @telemetry.ZONE_METHOD 226 def Release(self, origin = None): 227 if not self.collided and self.enabled and destructionEffectType.isExplosionOrOverride(self.destructionEffectId): 228 self.Expire() 229 self.ReleaseAll() 230 231 def DoFinalCleanup(self): 232 SpaceObject.DoFinalCleanup(self) 233 self.ReleaseAll() 234 235 def ReleaseAll(self): 236 if self._missileModel is not None: 237 self._missileModel.ClearAll() 238 self._missileModel = None 239 self.model = None 240 self._ClearDelayedBall() 241 242 def Display(self, display = 1, canYield = True): 243 if self.enabled: 244 SpaceObject.Display(self, display, canYield) 245 246 def _ClearDelayedBall(self): 247 if self._delayedBall: 248 _DestroyClientBall(self._delayedBall) 249 self._delayedBall = None 250 251 def OnAllMissileWarheadsExploded(self): 252 self._ClearDelayedBall() 253 self.exploded = True 254 255 def OnMissileModelCleared(self, model): 256 self.RemoveAndClearModel(model, _GetScene()) 257 258 def PlaceMissileImpact(self, impactModel, impactPosition, targetLocatorID): 259 explosionBall = _SpawnClientBall(impactPosition) 260 impactModel.translationCurve = explosionBall 261 useRandomRotation = True 262 if not self._randomizeImpactRotation: 263 targetBall = GetMichelle().GetBall(self.targetId) 264 if targetBall is not None and targetLocatorID != -1: 265 if hasattr(targetBall.model, 'GetLocatorRotationFromSet'): 266 impactDirection = targetBall.model.GetLocatorRotationFromSet(targetLocatorID, True, 'damage') 267 impactRotation = geo2.QuaternionRotationArc((0, 1, 0), impactDirection) 268 useRandomRotation = False 269 if useRandomRotation: 270 impactRotation = geo2.QuaternionRotationSetYawPitchRoll(random.random() * 2.0 * math.pi, random.random() * 2.0 * math.pi, random.random() * 2.0 * math.pi) 271 impactModel.rotation = impactRotation 272 scene = _GetScene() 273 if scene is not None: 274 scene.objects.append(impactModel) 275 shakeMagnitude = min(impactModel.boundingSphereRadius, 250) 276 shakeMagnitude = max(shakeMagnitude, 50) 277 _ShakeCamera(shakeMagnitude, impactPosition) 278 self.ApplyImpactTorqueToTarget(targetLocatorID, self.missileRadius) 279 280 def RemoveMissileImpact(self, impactModel, isFinalExplosion): 281 if impactModel.translationCurve is not None: 282 _DestroyClientBall(impactModel.translationCurve) 283 self.RemoveAndClearModel(impactModel, _GetScene()) 284 if isFinalExplosion: 285 self.ReleaseAll() 286 287 288 class Bomb(Missile): 289 290 def __init__(self): 291 Missile.__init__(self) 292 self.impactPosition = None 293 294 def Release(self): 295 if self.model is not None: 296 self.impactPosition = self.model.worldPosition 297 if self._missileModel is not None: 298 self._missileModel.DetonateWarhead(0) 299 SpaceObject.Release(self, 'Bomb') 300 301 def _EstimateTimeToTarget(self, toCenter = False): 302 return 20.0 303 304 def PlaceMissileImpact(self, impactModel, impactPosition, targetLocatorID): 305 if self.impactPosition is not None: 306 impactPosition = self.impactPosition 307 Missile.PlaceMissileImpact(self, impactModel, impactPosition, targetLocatorID) 308 309 def _GetMissileTranslationCurve(self): 310 return self 311 312 def _GetMissileRotationCurve(self): 313 sourceShipBall = GetMichelle().GetBall(self.sourceShipID) 314 return sourceShipBall