planet.py (18293B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\spaceObject\planet.py 2 import random 3 import datetime 4 import math 5 import blue 6 import evetypes 7 import trinity 8 import uthread 9 import pytelemetry.zoning as telemetry 10 import eve.common.lib.appConst as appConst 11 import carbon.common.lib.const as const 12 from eve.client.script.environment.spaceObject.spaceObject import SpaceObject 13 from eve.client.script.parklife.environmentSvc import GetEnvironmentService 14 import inventorycommon.typeHelpers 15 from eveuniverse.security import is_low_sec_or_higher_solar_system 16 from fsdBuiltData.common.graphicIDs import GetGraphicFile 17 import evegraphics.effects as gfxeffects 18 from moonmining.const import MOON_CRATER_SLIMITEM_NAME 19 from sovereignty.resource.shared.planetary_resources_cache import DataUnavailableError 20 AURORA_PERIOD = (302400.0, 561600.0) 21 AURORA_DURATION = (510.0, 930.0) 22 23 def _FindAuroraCurveSet(model): 24 for cs in model.Find('trinity.TriCurveSet'): 25 if cs.name == 'PlayAurora': 26 return cs 27 28 29 def _StartAurora(model, rand, length, offset): 30 cs = _FindAuroraCurveSet(model) 31 if not cs: 32 return 33 seedCurve = cs.curves.FindByName('AuroraSeed') 34 if seedCurve: 35 seedCurve.value = (rand.random(), 36 rand.random(), 37 rand.random(), 38 rand.random()) 39 cs.scale = 1.0 / length 40 cs.PlayFrom(offset * cs.scale) 41 42 43 def _AuroraTasklet(itemID, model): 44 if not _FindAuroraCurveSet(model.object): 45 return 46 r = random.Random() 47 r.seed(itemID) 48 period = blue.os.TimeFromDouble(r.uniform(*AURORA_PERIOD)) 49 while model.object: 50 t = blue.os.GetWallclockTime() 51 phase = t % period 52 r.seed(itemID + t / period) 53 duration = blue.os.TimeFromDouble(r.uniform(*AURORA_DURATION)) 54 if phase < duration: 55 _StartAurora(model.object, r, blue.os.TimeAsDouble(duration), blue.os.TimeAsDouble(phase)) 56 wait = blue.os.TimeAsDouble(period - phase) 57 blue.synchro.Sleep(min(wait, 82800) * 1000) 58 59 60 def GetTimeUntilNextAurora(itemID): 61 r = random.Random() 62 r.seed(itemID) 63 period = blue.os.TimeFromDouble(r.uniform(*AURORA_PERIOD)) 64 t = blue.os.GetWallclockTime() 65 phase = t % period 66 r.seed(itemID + t / period) 67 duration = blue.os.TimeFromDouble(r.uniform(*AURORA_DURATION)) 68 if phase < duration: 69 return 0.0 70 return blue.os.TimeAsDouble(period - phase) 71 72 73 UNLOADED = 0 74 TEXTURES_UNLOADED = 1 75 LOADED = 2 76 77 class Planet(SpaceObject): 78 __notifyevents__ = ['OnSlimItemUpdated'] 79 80 def __init__(self): 81 SpaceObject.__init__(self) 82 self.isInflightPlanet = True 83 self.attributes = None 84 self.largeTextures = False 85 self._crater = None 86 self.itemID = None 87 self.impactDirection = (0, 0, 0) 88 self.impactStartTime = 0 89 self.chunk = None 90 self._auroraTasklet = None 91 if not hasattr(self, 'typeData'): 92 self.typeData = {} 93 self.OnSlimItemUpdated(self.typeData.get('slimItem')) 94 sm.RegisterNotify(self) 95 96 def OnSlimItemUpdated(self, item): 97 celestialEffect = getattr(item, 'celestialEffect', None) 98 if celestialEffect is not None: 99 effectGuid, graphicInfo = celestialEffect 100 self.logger.debug('Planet.OnSlimItemUpdated found celestialEffect %s %s', effectGuid, graphicInfo) 101 sm.ScatterEvent('OnSpecialFX', self.id, None, None, None, None, effectGuid, False, 1, True, graphicInfo=graphicInfo) 102 if getattr(item, MOON_CRATER_SLIMITEM_NAME, None) is not None: 103 self.impactDirection, self.impactStartTime, chunkID = getattr(item, MOON_CRATER_SLIMITEM_NAME) 104 self.chunk = sm.GetService('michelle').GetBall(chunkID) 105 if self.chunk is None or not hasattr(self.chunk, 'GetModel'): 106 return 107 if self.chunk.GetModel() is None: 108 self.chunk.RegisterForModelLoad(self._SpawnImpactCraterFromSlimItemUpdate) 109 else: 110 self._SpawnImpactCraterFromSlimItemUpdate() 111 112 def Display(self, display = 1, canYield = True): 113 pass 114 115 def LoadModel(self, fileName = None, loadedModel = None, notify = True, addToScene = True): 116 uthread.new(self.LoadPlanet) 117 118 def _IsMoon(self): 119 return evetypes.GetGroupID(self.typeID) == appConst.groupMoon 120 121 def _ApplyPlanetRotation(self, model): 122 if self._IsMoon(): 123 return 124 rotationDirection = 1 125 if self.id % 2: 126 rotationDirection = -1 127 r = random.Random() 128 r.seed(self.id) 129 rotationTime = r.random() * 2000 + 3000 130 curve = trinity.Tr2CurveEulerRotation() 131 curve.SetExtrapolation(trinity.Tr2CurveExtrapolation.CYCLE) 132 curve.yaw.AddKey(0.0, math.radians(1.0), trinity.Tr2CurveInterpolation.LINEAR) 133 curve.yaw.AddKey(rotationTime, math.radians(rotationDirection * 360.0), trinity.Tr2CurveInterpolation.LINEAR) 134 tilt = r.random() * 60.0 - 30.0 135 curve.pitch.AddKey(0.0, math.radians(1.0)) 136 curve.pitch.AddKey(6000.0, math.radians(tilt)) 137 curve.pitch.AddKey(12000.0, 0.0) 138 model.rotationCurve = trinity.Tr2RotationAdapter() 139 model.rotationCurve.curve = curve 140 141 def _GetModelPath(self): 142 graphicFile = self.typeData.get('graphicFile') 143 if self.largeTextures: 144 graphicFile = graphicFile.replace('.red', '_HI.red') 145 return graphicFile 146 147 @telemetry.ZONE_METHOD 148 def LoadPlanet(self, itemID = None, forPhotoService = False, rotate = True, hiTextures = False, notifyLoaded = True): 149 if itemID is None: 150 itemID = self.id 151 self.itemID = itemID 152 fsdData = cfg.mapSolarSystemContentCache.celestials[self.itemID] 153 if self.attributes is None: 154 self.attributes = fsdData.planetAttributes 155 if hasattr(fsdData, 'environmentTemplateID'): 156 GetEnvironmentService().AddBallEnvironment(self, fsdData.environmentTemplateID) 157 self.largeTextures = hiTextures 158 self.isInflightPlanet = not forPhotoService 159 self.model = trinity.Load(self._GetPresetPath()) 160 if self.model is None: 161 self.logger.error('No planet was loaded! %s', self._GetPresetPath()) 162 return 163 self._SetupPlanet(planet=self.model, itemID=itemID, rotate=rotate) 164 self.model.StartControllers() 165 if self.isInflightPlanet: 166 scene = self.spaceMgr.GetScene() 167 if scene is not None: 168 scene.planets.append(self.model) 169 if notifyLoaded: 170 self.NotifyModelLoaded() 171 172 def GetTimeUntilNextAurora(self): 173 return GetTimeUntilNextAurora(self.itemID) 174 175 def ForceAuroraNow(self): 176 if self._auroraTasklet: 177 self._auroraTasklet.kill() 178 self._auroraTasklet = None 179 duration = random.uniform(*AURORA_DURATION) 180 _StartAurora(self.model, random, duration, duration / 4.0) 181 182 def _SetupTraffic(self, itemID): 183 bp = getattr(self, 'ballpark', None) 184 populationLevel = 1.0 185 if bp is None or self.model is None: 186 return 187 solarSystemId = bp.solarsystemID 188 if is_low_sec_or_higher_solar_system(solarSystemId): 189 populationLevel = 10.0 190 else: 191 try: 192 colonyResourcesSvc = sm.GetService('sovereigntyResourceSvc') 193 workforce = colonyResourcesSvc.GetPlanetWorkforceProduction(itemID) 194 if workforce > 0 and workforce is not None: 195 workforceConfigurations = colonyResourcesSvc.GetAllPlanetWorkforceProduction() 196 max_workforce = max(workforceConfigurations.values()) 197 if max_workforce > 0: 198 populationLevel = float(workforce) / float(max_workforce) * 10 199 elif itemID % 100 > 0: 200 populationLevel = itemID % 100 / 10 201 except DataUnavailableError as e: 202 self.logger.warning('planet._SetupTraffic failed to get colony data') 203 204 if self.model is not None and hasattr(self.model, 'SetControllerVariable'): 205 self.model.SetControllerVariable('populationLevel', int(populationLevel)) 206 207 @telemetry.ZONE_METHOD 208 def _SetupPlanet(self, planet, itemID, rotate): 209 if rotate: 210 self._ApplyPlanetRotation(planet) 211 planet.translationCurve = self 212 planet.scaling = (self.radius, self.radius, self.radius) 213 planet.radius = self.radius 214 planet.name = '%d' % itemID 215 self.model.SetControllerVariable('radius', self.radius) 216 if self.typeID == appConst.typePlanetOcean or self.typeID == appConst.typePlanetSandstorm or self.typeID == appConst.typePlanetEarthlike: 217 uthread.new(self._SetupTraffic, itemID) 218 gfx = inventorycommon.typeHelpers.GetGraphic(self.typeID) 219 if hasattr(gfx, 'albedoColor') and gfx.albedoColor: 220 planet.albedoColor = tuple(gfx.albedoColor) 221 if hasattr(gfx, 'emissiveColor') and gfx.emissiveColor: 222 planet.emissiveColor = tuple(gfx.emissiveColor) 223 self._ModifyPlanetShader() 224 self.SetEffectParameters() 225 sm.GetService('audio').SetupPlanet3DAudio(planet, self.typeID) 226 self._auroraTasklet = uthread.new(_AuroraTasklet, itemID, blue.BluePythonWeakRef(planet)) 227 228 def Release(self, origin = None): 229 if self._auroraTasklet: 230 self._auroraTasklet.kill() 231 self._auroraTasklet = None 232 scene = self.spaceMgr.GetScene() 233 if scene: 234 scene.planets.fremove(self.model) 235 SpaceObject.Release(self, 'Planet') 236 237 def RemoveFromScene(self, model, scene = None): 238 pass 239 240 @staticmethod 241 def GetPlanetByID(itemID, typeID): 242 planet = Planet() 243 planet.logger.debug('GetPlanetByID called') 244 planet.typeID = typeID 245 planet.LoadPlanet(itemID, True, notifyLoaded=False) 246 return planet 247 248 def SetEffectParameters(self): 249 planetParent = self._GetPlanetParent() 250 if not planetParent: 251 return 252 for effect in planetParent.Find('trinity.Tr2Effect'): 253 param1 = trinity.TriTextureParameter() 254 param1.name = 'NormalHeight1' 255 param1.resourcePath = GetGraphicFile(self._GetHeightMap1()) 256 effect.resources.append(param1) 257 param2 = trinity.TriTextureParameter() 258 param2.name = 'NormalHeight2' 259 param2.resourcePath = GetGraphicFile(self._GetHeightMap2()) 260 effect.resources.append(param2) 261 param3 = trinity.Tr2FloatParameter() 262 param3.name = 'Random' 263 param3.value = float(self.itemID % 100) 264 effect.parameters.append(param3) 265 ind = -1 266 for r in range(0, len(effect.resources)): 267 resource = effect.resources[r] 268 if resource.name.lower() == 'heightmap': 269 ind = r 270 271 if ind > 0: 272 effect.resources.removeAt(ind) 273 274 def _GetAttribute(self, name): 275 if self.attributes is None: 276 raise RuntimeError('Planet was not loaded. Can not get population of an unloaded planet.') 277 try: 278 return getattr(self.attributes, name) 279 except: 280 self.logger.exception('Could not get attribute population. %s', str(self.attributes)) 281 282 def _GetPopulation(self): 283 return self._GetAttribute('population') 284 285 def _GetShaderPreset(self): 286 return self._GetAttribute('shaderPreset') 287 288 def _GetPresetPath(self): 289 presetPath = self._GetModelPath() if self._GetShaderPreset() is None else GetGraphicFile(self._GetShaderPreset()) 290 return presetPath 291 292 def _GetHeightMap1(self): 293 return self._GetAttribute('heightMap1') 294 295 def _GetHeightMap2(self): 296 return self._GetAttribute('heightMap2') 297 298 def _GetPlanetParent(self): 299 return self.model.effectChildren.FindByName('Planet') 300 301 def _GetPlanetShaderResources(self, paramName = ''): 302 retList = [] 303 if not self.model: 304 return retList 305 planetParent = self.model 306 if planetParent is not None: 307 for param in planetParent.Find('trinity.TriTextureParameter'): 308 if paramName == param.name or paramName == '': 309 retList.append(param) 310 311 return retList 312 313 def _ModifyPlanetShaderParameter(self, paramName, x = None, y = None, z = None, w = None): 314 if not self.model: 315 return 316 planetParent = self._GetPlanetParent() 317 if not planetParent: 318 return 319 for effect in planetParent.Find('trinity.Tr2Effect'): 320 for i, param in enumerate(effect.constParameters): 321 name, value = param 322 if name == paramName: 323 value = list(value) 324 if x is not None: 325 value[0] = x 326 if y is not None: 327 value[1] = y 328 if z is not None: 329 value[2] = z 330 if w is not None: 331 value[3] = w 332 effect.constParameters.removeAt(i) 333 effect.constParameters.append((name, tuple(value))) 334 effect.RebuildCachedData() 335 break 336 337 for param in effect.parameters: 338 if param.name == paramName: 339 if isinstance(param, trinity.Tr2FloatParameter): 340 if x is not None: 341 param.value = x 342 else: 343 if x is not None: 344 param.x = x 345 if y is not None: 346 param.y = y 347 if z is not None: 348 param.z = z 349 if w is not None: 350 param.w = w 351 break 352 353 def _ModifyPlanetShader(self): 354 if self.typeID == appConst.typePlanetEarthlike or self.typeID == appConst.typePlanetSandstorm: 355 self.RandomizeShaderClouds() 356 if self.typeID is appConst.typePlanetOcean or self.typeID is appConst.typePlanetEarthlike: 357 self.AdjustShaderPopulation() 358 359 def AdjustShaderPopulation(self): 360 if self._GetPopulation() == 0: 361 for textureParamName in ['CityLight', 'CityDistributionTexture', 'CityDistributionMask']: 362 textureParamList = self._GetPlanetShaderResources(textureParamName) 363 for textureParam in textureParamList: 364 textureParam.resourcePath = '' 365 366 self._ModifyPlanetShaderParameter('CoverageFactors', w=0.0) 367 368 def RandomizeShaderClouds(self): 369 now = datetime.datetime.now() 370 r = random.Random() 371 r.seed(now.year + now.month * 30 + now.day + self.itemID) 372 val = r.randint(1, 5) 373 useDense = val % 5 == 0 374 if self.typeID == appConst.typePlanetEarthlike: 375 if useDense: 376 cloudMapIDs = (3857, 3858, 3859, 3860) 377 cloudCapMapIDs = (3861, 3862, 3863, 3864) 378 else: 379 cloudMapIDs = (3848, 3849, 3851, 3852) 380 cloudCapMapIDs = (3853, 3854, 3855, 3856) 381 else: 382 cloudMapIDs = (3956, 3957, 3958, 3959) 383 cloudCapMapIDs = (3960, 3961, 3962, 3963) 384 cloudMapIdx = r.randint(0, 3) 385 cloudCapMapIdx = r.randint(0, 3) 386 cloudCapTexResPath = GetGraphicFile(cloudCapMapIDs[cloudCapMapIdx]) 387 cloudTexResPath = GetGraphicFile(cloudMapIDs[cloudMapIdx]) 388 cloudCapParamList = self._GetPlanetShaderResources('CloudCapTexture') 389 for cloudCapParam in cloudCapParamList: 390 cloudCapParam.resourcePath = cloudCapTexResPath 391 392 cloudParamList = self._GetPlanetShaderResources('CloudsTexture') 393 for cloudParam in cloudParamList: 394 cloudParam.resourcePath = cloudTexResPath 395 396 cloudsBrightness = r.random() * 0.4 + 0.6 397 cloudsTransparency = r.random() * 2.0 + 1.0 398 self._ModifyPlanetShaderParameter('CloudsFactors', y=cloudsBrightness, z=cloudsTransparency) 399 400 def SpawnImpactCrater(self, direction, size, timeFromStart): 401 uthread.new(self._SpawnImpactCrater, direction, size, timeFromStart) 402 403 def _SpawnImpactCrater(self, direction, size, timeFromStart): 404 self.RemoveImpactCrater() 405 radians = None 406 if size is not None: 407 size = max(0.0, min(1.0, 0.5 * size / self.radius)) 408 radians = math.asin(size) * 2 409 pin = trinity.Load('res:/fisfx/structure/moonmining/moonmining_surfacecrater_01a.red') 410 pin.centerNormal = direction 411 if radians is not None: 412 pin.pinRadius = radians 413 pin.pinMaxRadius = radians 414 self._crater = pin 415 while self.GetModel() is None or len(self.GetModel().effectChildren) < 1: 416 blue.synchro.Sleep(100) 417 418 for curveSet in self._crater.curveSets: 419 curveSet.PlayFrom(timeFromStart) 420 421 self.GetModel().effectChildren.append(self._crater) 422 423 def RemoveImpactCrater(self): 424 model = self.GetModel() 425 if model is not None and self._crater is not None: 426 if self._crater in model.effectChildren: 427 model.effectChildren.remove(self._crater) 428 self._crater = None 429 430 def _SpawnImpactCraterFromSlimItemUpdate(self): 431 currentSimTime = blue.os.GetSimTime() 432 size = 2.0 * self.chunk.GetModel().boundingSphereRadius 433 self.SpawnImpactCrater(self.impactDirection, size, float((currentSimTime - self.impactStartTime) / const.SEC)) 434 435 def _ApplyCraterEffectAttributes(self): 436 if not self.model: 437 return 438 planetParent = self._GetPlanetParent() 439 if not planetParent: 440 return 441 for effect in planetParent.Find('trinity.Tr2Effect'): 442 gfxeffects.ApplyEffectOverride(self._crater.pinEffect, effect) 443 444 def SetImpactIntensity(self, value): 445 if self._crater is None: 446 return 447 effect = self._crater.pinEffect 448 gfxeffects.SetParameterComponentValue(effect, 'IntensityParameters', value, componentIndex=0)