ExplosionManager.py (8285B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\spaceObject\ExplosionManager.py 2 from __future__ import print_function 3 from collections import deque 4 import blue 5 import trinity 6 import geo2 7 import telemetry 8 import uthread 9 import random 10 import carbon.common.script.util.logUtil as log 11 from tacticalNavigation.ballparkFunctions import AddClientBall, RemoveClientBall 12 SECOND = 10000000 13 14 class Singleton(type): 15 16 def __init__(cls, mcs, bases, dic): 17 super(Singleton, cls).__init__(mcs, bases, dic) 18 cls.instance = None 19 20 def __call__(cls, *args, **kw): 21 if cls.instance is None: 22 cls.instance = super(Singleton, cls).__call__(*args, **kw) 23 return cls.instance 24 25 26 class PooledExplosion: 27 28 def __init__(self, path): 29 self.path = path 30 self.refCount = 0 31 self.timestamp = -1 32 self.timeoutCountdown = 0 33 self.total = 1 34 self.active = 0 35 self.maxActiveTick = 0 36 self.inactive = 1 37 self.maxRecentUsed = deque([1], 30) 38 self.totalLoads = 1 39 self.pool = [trinity.Load(self.path)] 40 self.effectDuration = self.pool[0].duration 41 if self.effectDuration == -1: 42 self.effectDuration = 10 43 self.effectDuration = self.effectDuration * SECOND 44 self.boundingSphereRadius = self.pool[0].boundingSphereRadius 45 46 def GetDuration(self): 47 return self.effectDuration 48 49 def AddRef(self, count): 50 self.refCount += count 51 52 def DecRef(self, count): 53 self.refCount -= count 54 55 def _AddActive(self, count): 56 self.active += count 57 self.maxActiveTick = max(self.maxActiveTick, self.active) 58 59 def Pop(self): 60 self._AddActive(1) 61 if self.inactive > 0: 62 self.inactive -= 1 63 return self.pool.pop() 64 self.total += 1 65 self.totalLoads += 1 66 return trinity.Load(self.path) 67 68 def Push(self, explosion): 69 self.pool.append(explosion) 70 self._AddActive(-1) 71 self.inactive += 1 72 73 def PrunePool(self, timestamp): 74 if self.refCount > 0: 75 self.timestamp = -1 76 self.maxRecentUsed.append(self.maxActiveTick) 77 self.maxActiveTick = 0 78 recentMax = max(self.maxRecentUsed) 79 if recentMax < self.total: 80 recentMax = min(self.total - recentMax, 4) 81 self.total -= recentMax 82 self.inactive -= recentMax 83 del self.pool[:recentMax] 84 if self.refCount <= 0: 85 if self.timestamp < 0: 86 self.timestamp = timestamp 87 if timestamp - self.timestamp > 15 * SECOND: 88 return True 89 return False 90 91 92 class ExplosionManager(object): 93 __metaclass__ = Singleton 94 95 def __init__(self, limit = 100): 96 self.queue = [] 97 self.pooledExplosions = {} 98 self.ageLimit = 20 * SECOND 99 self.limit = limit 100 self.running = True 101 self.Start() 102 103 def Run(self): 104 while self.running: 105 blue.synchro.SleepSim(1000) 106 self._Prune() 107 108 def Preload(self, path, count = 1): 109 if path not in self.pooledExplosions: 110 self.pooledExplosions[path] = PooledExplosion(path) 111 self.pooledExplosions[path].AddRef(count) 112 113 def Cancel(self, path, count = 1): 114 if path not in self.pooledExplosions: 115 log.LogWarn('ExplosionManager::Cancel ' + path + ' not loaded.') 116 return 117 self.pooledExplosions[path].DecRef(count) 118 119 @telemetry.ZONE_METHOD 120 def GetExplosion(self, path, scale = 1.0, preloaded = False, callback = None): 121 if path not in self.pooledExplosions: 122 if preloaded: 123 log.LogWarn('ExplosionManager::GetExplosion ' + path + ' not loaded.') 124 self.Preload(path) 125 elif not preloaded: 126 self.pooledExplosions[path].AddRef(1) 127 explosion = self.pooledExplosions[path].Pop() 128 explosion.scaling = (scale, scale, scale) 129 explosion.Start() 130 self._Append(callback, explosion, self.pooledExplosions[path]) 131 return explosion 132 133 def GetBoundingSphereRadius(self, path): 134 if path not in self.pooledExplosions: 135 log.LogWarn('ExplosionManager::GetBoundingSphereRadius ' + path + ' not loaded.') 136 self.Preload(path, count=0) 137 return self.pooledExplosions[path].boundingSphereRadius 138 139 def _Append(self, callback, model, pool): 140 delay = pool.GetDuration() 141 if delay > self.ageLimit: 142 delay = self.ageLimit 143 stamp = blue.os.GetSimTime() + delay 144 self.queue.append((stamp, 145 callback, 146 model, 147 pool)) 148 if len(self.queue) > self.limit: 149 self._Delete(self.queue.pop(0)) 150 151 def _Prune(self): 152 if not len(self.queue) and not len(self.pooledExplosions): 153 return 154 now = blue.os.GetSimTime() 155 if len(self.queue): 156 self.queue.sort() 157 while True: 158 blue.synchro.Yield() 159 now = blue.os.GetSimTime() 160 if not len(self.queue) or not self.running: 161 break 162 if self.queue[0][0] < now: 163 self._Delete(self.queue.pop(0)) 164 else: 165 break 166 167 delKeys = [] 168 for val in self.pooledExplosions.itervalues(): 169 if val.PrunePool(now): 170 delKeys.append(val.path) 171 172 for key in delKeys: 173 del self.pooledExplosions[key] 174 175 def _Delete(self, item): 176 if item is not None: 177 stamp, callback, model, pool = item 178 if model is None: 179 log.LogError('ExplosionManager::_Delete item has no model') 180 if pool is None: 181 log.LogError('ExplosionManager::_Delete item has no pool') 182 if callback is not None: 183 callback(model) 184 model.Stop() 185 pool.Push(model) 186 pool.DecRef(1) 187 188 def GetCount(self): 189 return len(self.queue) 190 191 def SetLimit(self, limit): 192 self.limit = limit 193 194 def Inspect(self): 195 print(self.queue) 196 197 def Start(self): 198 self.running = True 199 uthread.new(self.Run) 200 201 def Stop(self): 202 self.running = False 203 204 @staticmethod 205 def SetUpChildExplosion(explosionModel, explosionLocatorSets): 206 explosionChildren = explosionModel.Find('trinity.EveChildExplosion', 2) 207 if explosionLocatorSets is not None: 208 transforms = [] 209 locators = [ (each[0], each[1]) for each in (explosionLocatorSets.locators if explosionLocatorSets else []) ] 210 random.shuffle(locators) 211 for position, direction in locators: 212 rotation = geo2.QuaternionRotationArc((0, 1, 0), direction) 213 transform = geo2.MatrixTransformation((0, 0, 0), (0, 0, 0, 1), (1, 1, 1), (0, 0, 0), rotation, position) 214 transforms.append(transform) 215 216 for each in explosionChildren: 217 each.SetLocalExplosionTransforms(transforms) 218 219 for each in explosionChildren: 220 each.Play() 221 222 @staticmethod 223 def PlayClientSideExplosionBall(gfxResPath, worldPos, rotation, explosionLocatorSets = None): 224 scene = sm.GetService('space').GetScene() 225 clientBall = AddClientBall(worldPos) 226 explosionModel = trinity.Load(gfxResPath) 227 explosionModel.translationCurve = clientBall 228 explosionModel.rotation = rotation 229 ExplosionManager.SetUpChildExplosion(explosionModel, explosionLocatorSets) 230 scene.objects.append(explosionModel) 231 secTillBallRemove = 60.0 232 if isinstance(explosionModel, trinity.EveEffectRoot2): 233 for child in explosionModel.effectChildren: 234 secTillBallRemove = max(secTillBallRemove, child.globalDuration) 235 236 uthread.new(ExplosionManager._DelayedBallRemove, clientBall, explosionModel, secTillBallRemove) 237 238 @staticmethod 239 def _DelayedBallRemove(clientBall, explosionModel, secTillBallRemove): 240 blue.synchro.Sleep(secTillBallRemove * 1000) 241 scene = sm.GetService('space').GetScene() 242 scene.objects.fremove(explosionModel) 243 RemoveClientBall(clientBall)