effectarea.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. // #define EFFECT_AREA_VISUAL_DEBUG
  2. // Mostly used for better readability
  3. enum eZoneType
  4. {
  5. STATIC = 1,
  6. DYNAMIC = 2
  7. }
  8. enum EEffectAreaType
  9. {
  10. UNDEFINED = 1,
  11. HOT_SPRING = 2,
  12. VOLCANIC = 4
  13. }
  14. // Class used for parameter transfer, all of the params are shadows of EffectArea member variables
  15. class EffectAreaParams
  16. {
  17. string m_ParamName = "Default setup";
  18. string m_ParamTriggerType = "ContaminatedTrigger";
  19. float m_ParamRadius = 100;
  20. float m_ParamPosHeight = 25;
  21. float m_ParamNegHeight = 10;
  22. int m_ParamInnerRings = 1;
  23. int m_ParamInnerSpace = 35;
  24. bool m_ParamOuterToggle = true;
  25. int m_ParamOuterSpace = 20;
  26. int m_ParamOuterOffset = -5;
  27. int m_ParamVertLayers = 0;
  28. int m_ParamVerticalOffset = 10;
  29. int m_ParamEffectInterval = 0;
  30. int m_ParamEffectDuration = 0;
  31. int m_ParamEffectModifier = 0;
  32. /*
  33. int m_ParamPartId = ParticleList.CONTAMINATED_AREA_GAS_BIGASS;
  34. int m_ParamAroundPartId = ParticleList.CONTAMINATED_AREA_GAS_AROUND;
  35. int m_ParamTinyPartId = ParticleList.CONTAMINATED_AREA_GAS_TINY;
  36. */
  37. int m_ParamPartId = 0;
  38. int m_ParamAroundPartId = 0;
  39. int m_ParamTinyPartId = 0;
  40. string m_ParamPpeRequesterType = "PPERequester_ContaminatedAreaTint";
  41. }
  42. // Base class for contaminated and other "Effect" areas
  43. class EffectArea : House
  44. {
  45. // Area Data
  46. string m_Name = "Default setup"; // The user defined name of the area
  47. int m_Type = eZoneType.STATIC; // If the zone is static or dynamic
  48. vector m_Position; // World position of area snapped to ground on creation (see: EffectAreaLoader)
  49. vector m_PositionTrigger; // World position adjusted according to trigger pivot (pivot is cylinder center)
  50. int m_EffectInterval; // If non persisent effect: determines intervals between effect activation
  51. int m_EffectDuration; // If non persisent effect: determines duration of effect
  52. bool m_EffectModifier; // Flag for modification of internal behavior of the effect
  53. // Trigger Data
  54. float m_Radius = 100; // Radius of the Contaminated Area
  55. float m_PositiveHeight = 25; // Distance between center and maximum height
  56. float m_NegativeHeight = 10; // Distance between center and minimum height
  57. // PCG parameters
  58. // Inner Particle data
  59. int m_InnerRings = 1; // The amount of inner rings one wants
  60. int m_InnerSpacing = 35; // Distance between two particles placed on inner rings of the area
  61. // Outer particle data
  62. bool m_OuterRingToggle = true; // Allow disabling outer ring if undesired
  63. int m_OuterRingOffset = -5; // Distance between the outermost ring of particles and area radius
  64. int m_OuterSpacing = 20; // Distance between two particles placed on the outer most ring of the area
  65. // Verticality handling
  66. int m_VerticalLayers = 0; // Used to add multiple layers vertically and set vertical spacing ( 0 don't do anything )
  67. int m_VerticalOffset = 10; // Used to determine vertical offset between two vertical layers
  68. // Particles and visual effects
  69. int m_ParticleID = ParticleList.CONTAMINATED_AREA_GAS_BIGASS;
  70. int m_AroundParticleID = ParticleList.CONTAMINATED_AREA_GAS_AROUND;
  71. int m_TinyParticleID = ParticleList.CONTAMINATED_AREA_GAS_TINY;
  72. string m_PPERequesterType;
  73. int m_PPERequesterIdx = -1;
  74. int m_EffectsPriority; // When multiple areas overlap, only the area with the highest priority will play its effects
  75. const int PARTICLES_MAX = 1000; // Better safe than sorry
  76. // Other values and storage
  77. string m_TriggerType = "ContaminatedTrigger"; // The trigger class used by this zone
  78. EffectTrigger m_Trigger; // The trigger used to determine if player is inside toxic area
  79. ref array<Particle> m_ToxicClouds; // All static toxic clouds in ContaminatedArea
  80. // ----------------------------------------------
  81. // INITIAL SETUP
  82. // ----------------------------------------------
  83. void EffectArea()
  84. {
  85. RegisterNetSyncVariableFloat("m_Radius", 0, 0, 2);
  86. RegisterNetSyncVariableFloat("m_PositiveHeight", 0, 0, 2);
  87. RegisterNetSyncVariableFloat("m_NegativeHeight", 0, 0, 2);
  88. RegisterNetSyncVariableInt("m_InnerRings");
  89. RegisterNetSyncVariableInt("m_InnerSpacing");
  90. RegisterNetSyncVariableInt("m_OuterRingOffset");
  91. RegisterNetSyncVariableInt("m_OuterSpacing");
  92. RegisterNetSyncVariableInt("m_VerticalLayers");
  93. RegisterNetSyncVariableInt("m_VerticalOffset");
  94. RegisterNetSyncVariableInt("m_ParticleID");
  95. /*
  96. RegisterNetSyncVariableInt("m_AroundParticleID");
  97. RegisterNetSyncVariableInt("m_TinyParticleID");
  98. RegisterNetSyncVariableInt("m_PPERequesterIdx");
  99. */
  100. RegisterNetSyncVariableBool("m_OuterRingToggle");
  101. }
  102. void ~EffectArea()
  103. {
  104. }
  105. void SetupZoneData( EffectAreaParams params )
  106. {
  107. // A lot of branching, allowing to use default values on specified params
  108. if ( params.m_ParamName != "" )
  109. m_Name = params.m_ParamName;
  110. if ( params.m_ParamTriggerType != "" )
  111. m_TriggerType = params.m_ParamTriggerType;
  112. if ( params.m_ParamRadius > 0 )
  113. m_Radius = params.m_ParamRadius;
  114. if ( params.m_ParamPosHeight > -1 )
  115. m_PositiveHeight = params.m_ParamPosHeight;
  116. if ( params.m_ParamNegHeight > -1 )
  117. m_NegativeHeight = params.m_ParamNegHeight;
  118. m_InnerRings = params.m_ParamInnerRings;
  119. if ( params.m_ParamInnerSpace > -1 )
  120. m_InnerSpacing = params.m_ParamInnerSpace;
  121. m_OuterRingToggle = params.m_ParamOuterToggle;
  122. if ( params.m_ParamOuterSpace > -1 )
  123. m_OuterSpacing = params.m_ParamOuterSpace;
  124. m_OuterRingOffset = params.m_ParamOuterOffset;
  125. if ( params.m_ParamVertLayers > 0 )
  126. m_VerticalLayers = params.m_ParamVertLayers;
  127. if ( params.m_ParamVerticalOffset > 0 )
  128. m_VerticalOffset = params.m_ParamVerticalOffset;
  129. m_EffectInterval = params.m_ParamEffectInterval;
  130. m_EffectDuration = params.m_ParamEffectDuration;
  131. m_EffectModifier = params.m_ParamEffectModifier;
  132. m_ParticleID = params.m_ParamPartId;
  133. m_AroundParticleID = params.m_ParamAroundPartId;
  134. m_TinyParticleID = params.m_ParamTinyPartId;
  135. if ( params.m_ParamPpeRequesterType != "" )
  136. {
  137. m_PPERequesterType = params.m_ParamPpeRequesterType;
  138. m_PPERequesterIdx = GetRequesterIndex(m_PPERequesterType);
  139. }
  140. // We get the PPE index for future usage and synchronization
  141. // DEVELOPER NOTE :
  142. // If you cannot register a new requester, add your own indexation and lookup methods to get an index and synchronize it
  143. // EXAMPLE : m_PPERequesterIdx = MyLookupMethod()
  144. #ifdef ENABLE_LOGGING
  145. Debug.Log(">>>> SetupZoneData: Finished: " + m_Name);
  146. #endif
  147. // We sync our data
  148. SetSynchDirty();
  149. }
  150. void Tick()
  151. {
  152. #ifdef DIAG_DEVELOPER
  153. #ifdef EFFECT_AREA_VISUAL_DEBUG
  154. CleanupDebugShapes(m_DebugTargets);
  155. #endif
  156. #endif
  157. }
  158. // Through this we will evaluate the resize of particles
  159. override void OnCEUpdate()
  160. {
  161. super.OnCEUpdate();
  162. Tick();
  163. }
  164. void InitZone()
  165. {
  166. // Debug.Log("------------------------------------------");
  167. // Debug.Log("InitZone: " + m_Name);
  168. m_Position = GetPosition();
  169. m_PositionTrigger = m_Position;
  170. m_PositionTrigger[1] = m_Position[1] + ((m_PositiveHeight - m_NegativeHeight) * 0.5); // Cylinder trigger pivot correction
  171. if (!GetGame().IsDedicatedServer())
  172. InitZoneClient();
  173. if (GetGame().IsServer())
  174. InitZoneServer();
  175. // Debug.Log("------------------------------------------");
  176. }
  177. // The following methods are to be overriden to execute specifc logic
  178. void InitZoneServer();
  179. void InitZoneClient();
  180. // ----------------------------------------------
  181. // INTERACTION SETUP
  182. // ----------------------------------------------
  183. override bool CanPutInCargo( EntityAI parent )
  184. {
  185. return false;
  186. }
  187. override bool CanPutIntoHands( EntityAI parent )
  188. {
  189. return false;
  190. }
  191. override bool DisableVicinityIcon()
  192. {
  193. return true;
  194. }
  195. override bool CanBeTargetedByAI( EntityAI ai )
  196. {
  197. return false;
  198. }
  199. // ----------------------------------------------
  200. // PARTICLE GENERATION
  201. // ----------------------------------------------
  202. // Used to position all particles procedurally
  203. void PlaceParticles( vector pos, float radius, int nbRings, int innerSpacing, bool outerToggle, int outerSpacing, int outerOffset, int partId )
  204. {
  205. // Debug.Log("PlaceParticles: " + pos);
  206. #ifdef NO_GUI
  207. return; // do not place any particles if there is no GUI
  208. #endif
  209. if (partId == 0)
  210. {
  211. Error("[WARNING] :: [EffectArea PlaceParticles] :: no particle defined, skipping area particle generation" );
  212. return;
  213. }
  214. if ( radius == 0 )
  215. {
  216. Error("[WARNING] :: [EffectArea PlaceParticles] :: Radius of contaminated zone is set to 0, this should not happen");
  217. return;
  218. }
  219. if ( outerToggle && radius == outerOffset )
  220. {
  221. Error("[WARNING] :: [EffectArea PlaceParticles] :: Your outerOffset is EQUAL to your Radius, this will result in division by 0");
  222. return;
  223. }
  224. int partCount = 0; // Number of spawned emitters
  225. ParticlePropertiesArray props = new ParticlePropertiesArray();
  226. // Inner spacing of 0 would cause infinite loops as no increment would happen
  227. if (innerSpacing == 0)
  228. innerSpacing = 1;
  229. // For each concentric ring, we place a particle emitter at a set offset
  230. for ( int i = 0; i <= nbRings + outerToggle; ++i )
  231. {
  232. if (i == 0) // Skipping 0, we want to start by placing a particle at center of area
  233. {
  234. SpawnParticles(props, pos, pos, partCount);
  235. }
  236. else
  237. {
  238. // We prepare the variables to use later in calculation
  239. float angleIncrement; // The value added to the offset angle to place following particle
  240. float ab; // Length of a side of triangle used to calculate particle positionning
  241. vector temp = vector.Zero; // Vector we rotate to position next spawn point
  242. // The particle density is not the same on the final ring which will only happen if toggled
  243. // Toggle uses bool parameter treated as int, thus i > nbRings test ( allows to limit branching )
  244. if ( i > nbRings )
  245. {
  246. ab = radius - outerOffset; // We want to leave some space to better see area demarcation
  247. // We calculate the rotation angle depending on particle spacing and distance from center
  248. angleIncrement = Math.Acos( 1 - ( ( outerSpacing * outerSpacing ) / ( 2 * Math.SqrInt(ab) ) ) );
  249. temp[2] = temp[2] + ab;
  250. //Debug.Log("Radius of last circle " + i + " is : " + ab);
  251. }
  252. else
  253. {
  254. ab = ( radius / ( nbRings + 1 ) ) * i; // We add the offset from one ring to another
  255. // We calculate the rotation angle depending on particle spacing and distance from center
  256. angleIncrement = Math.Acos( 1 - ( ( innerSpacing * innerSpacing ) / ( 2 * Math.SqrInt(ab) ) ) );
  257. temp[2] = temp[2] + ab;
  258. //Debug.Log("Radius of inner circle " + i + " is : " + ab);
  259. }
  260. for ( int j = 0; j <= ( Math.PI2 / angleIncrement ); j++ )
  261. {
  262. // Determine position of particle emitter
  263. // Use offset of current ring for vector length
  264. float sinAngle = Math.Sin(angleIncrement * j);
  265. float cosAngle = Math.Cos(angleIncrement * j);
  266. vector partPos = vector.RotateAroundZero( temp, vector.Up, cosAngle, sinAngle );
  267. partPos += pos;
  268. SpawnParticles(props, pos, partPos, partCount);
  269. }
  270. }
  271. }
  272. InsertParticles(props, partCount, partId);
  273. }
  274. // Fill the radius with particle emitters using the Circle packing in a circle method
  275. void FillWithParticles(vector pos, float areaRadius, float outwardsBleed, float partSize, int partId)
  276. {
  277. // Debug.Log("FillWithParticles: " + pos);
  278. #ifdef NO_GUI
  279. return; // do not place any particles if there is no GUI
  280. #endif
  281. if (partId == 0)
  282. return;
  283. if (partSize <= 0)
  284. partSize = 1;
  285. int partCount = 0; // Number of spawned emitters
  286. int ringCount = 0; // Number of area rings
  287. float ringDist = 0; // Distance between rings
  288. float radiusMax = areaRadius + outwardsBleed; // Visual radius of the area
  289. float radiusPart = partSize / 2; // Particle radius
  290. bool centerPart = true; // Spawn central particle?
  291. ParticlePropertiesArray props = new ParticlePropertiesArray();
  292. // Debug.Log("Area radius: " + radiusMax + "m, Particle radius: " + radiusPart + "m");
  293. if (radiusMax > radiusPart * 1.5) // Area has to be larger than particle, plus some margin
  294. {
  295. if (radiusMax < radiusPart * 2.5) // Area fits one ring of particles, but no center particle (minus some overlap margin)
  296. {
  297. ringDist = radiusMax - radiusPart; // Snap the particles to outer edge
  298. ringCount = 1;
  299. centerPart = false;
  300. }
  301. else // Area fits all
  302. {
  303. radiusMax -= radiusPart; // Snap the particles to outer edge
  304. ringCount = Math.Ceil(radiusMax / partSize); // Get number of inner rings
  305. ringDist = radiusMax / ringCount; // Adjust ring distance after rounding
  306. }
  307. }
  308. // Debug.Log("We have : " + ringCount + " rings, " + ringDist + "m apart, center: " + centerPart);
  309. // Debug.Log("We have : " + m_VerticalLayers + " layers, " + m_VerticalOffset + "m apart");
  310. // For each concentric ring, we place a particle emitter at a set offset
  311. for (int ring = 0; ring <= ringCount; ++ring)
  312. {
  313. if (ring == 0 && centerPart) // We start by placing particle at center of area
  314. {
  315. SpawnParticles(props, pos, pos, partCount);
  316. }
  317. else if (ring > 0)
  318. {
  319. float ringRadius = ringDist * ring;
  320. float circumference = 2 * Math.PI2 * ringRadius;
  321. int count = Math.Floor(circumference / partSize); // Get number of particles on ring (roughly)
  322. float angleInc = Math.PI2 / count; // Get angle between particles on ring
  323. for (int i = 0; i < count; ++i) // Insert particles around the ring
  324. {
  325. vector partPos = pos;
  326. float x = ringRadius * Math.Sin(angleInc * i);
  327. float z = ringRadius * Math.Cos(angleInc * i);
  328. partPos[0] = partPos[0] + x;
  329. partPos[2] = partPos[2] + z;
  330. SpawnParticles(props, pos, partPos, partCount);
  331. }
  332. }
  333. }
  334. InsertParticles(props, partCount, partId);
  335. }
  336. protected void SpawnParticles(ParticlePropertiesArray props, vector centerPos, vector partPos, inout int count)
  337. {
  338. partPos[1] = GetGame().SurfaceY(partPos[0], partPos[2]); // Snap particles to ground
  339. // We also populate vertically, layer 0 will be snapped to ground, subsequent layers will see particles floating by m_VerticalOffset
  340. for (int layer = 0; layer <= m_VerticalLayers; ++layer)
  341. {
  342. partPos[1] = partPos[1] + (m_VerticalOffset * layer);
  343. // We check that spawned particle is inside the trigger
  344. if (count < PARTICLES_MAX && Math.IsInRange(partPos[1], centerPos[1] - m_NegativeHeight, centerPos[1] + m_PositiveHeight))
  345. {
  346. props.Insert(ParticleProperties(partPos, ParticlePropertiesFlags.PLAY_ON_CREATION, null, GetGame().GetSurfaceOrientation( partPos[0], partPos[2] ), this));
  347. ++count;
  348. }
  349. }
  350. }
  351. private void InsertParticles(ParticlePropertiesArray props, int count, int partId)
  352. {
  353. m_ToxicClouds.Reserve(count);
  354. ParticleManager gPM = ParticleManager.GetInstance();
  355. array<ParticleSource> createdParticles = gPM.CreateParticlesByIdArr(partId, props, count);
  356. if (createdParticles.Count() != count)
  357. {
  358. if (gPM.IsFinishedAllocating())
  359. {
  360. ErrorEx(string.Format("Not enough particles in pool for EffectArea: %1", m_Name));
  361. OnParticleAllocation(gPM, createdParticles);
  362. }
  363. else
  364. {
  365. gPM.GetEvents().Event_OnAllocation.Insert(OnParticleAllocation);
  366. }
  367. }
  368. else
  369. {
  370. OnParticleAllocation(gPM, createdParticles);
  371. }
  372. // Debug.Log("Emitter count: " + count);
  373. }
  374. void OnParticleAllocation(ParticleManager pm, array<ParticleSource> particles)
  375. {
  376. foreach (ParticleSource p : particles)
  377. {
  378. if (p.GetOwner() == this) // Safety, since it can be unrelated particles when done through event
  379. {
  380. m_ToxicClouds.Insert(p);
  381. }
  382. }
  383. }
  384. int GetRequesterIndex(string type)
  385. {
  386. typename t = type.ToType();
  387. if (!t)
  388. return - 1;
  389. PPERequesterBase req = PPERequesterBank.GetRequester(t);
  390. if (req)
  391. return req.GetRequesterIDX();
  392. return -1;
  393. }
  394. // ----------------------------------------------
  395. // TRIGGER SETUP
  396. // ----------------------------------------------
  397. void CreateTrigger(vector pos, int radius)
  398. {
  399. #ifdef DIAG_DEVELOPER
  400. #ifdef EFFECT_AREA_VISUAL_DEBUG
  401. Shape dbgShape;
  402. CleanupDebugShapes(m_DebugTargets);
  403. #endif
  404. #endif
  405. // Create new trigger of specified type
  406. if (Class.CastTo(m_Trigger, GetGame().CreateObjectEx(m_TriggerType, pos, ECE_NONE)))
  407. {
  408. // We finalize trigger dimension setup
  409. float centerHeightCorrection = (m_PositiveHeight - m_NegativeHeight) * 0.5;
  410. m_Trigger.SetCollisionCylinderTwoWay(radius, -(m_NegativeHeight + centerHeightCorrection), (m_PositiveHeight - centerHeightCorrection));
  411. m_Trigger.SetPosition(pos);
  412. m_Trigger.Update();
  413. #ifdef DIAG_DEVELOPER
  414. #ifdef EFFECT_AREA_VISUAL_DEBUG
  415. /*
  416. vector cubePos = pos;
  417. cubePos[0] = cubePos[0] + radius;
  418. cubePos[1] = cubePos[1] + (m_PositiveHeight - centerHeightCorrection);
  419. cubePos[2] = cubePos[2] + radius;
  420. m_DebugTargets.Insert(Debug.DrawCube(cubePos, 0.5, 0x1fff0000));
  421. */
  422. vector colliderPosDebug = pos;
  423. //! upper limit
  424. colliderPosDebug[1] = pos[1] + (m_PositiveHeight - centerHeightCorrection);
  425. m_DebugTargets.Insert(Debug.DrawSphere(colliderPosDebug, 0.15, 0x1f0000ff, ShapeFlags.NOZWRITE));
  426. //m_DebugTargets.Insert(Debug.DrawLine(cubePos, colliderPosDebug, 0x1fff0000, ShapeFlags.NOZWRITE)); // connector
  427. //! center
  428. m_DebugTargets.Insert(Debug.DrawSphere(pos, 0.15, 0x1fff0000, ShapeFlags.NOZWRITE));
  429. //m_DebugTargets.Insert(Debug.DrawLine(cubePos, pos, 0x1fff0000, ShapeFlags.NOZWRITE)); // connector
  430. //! bottom limit
  431. colliderPosDebug[1] = pos[1] - (m_NegativeHeight + centerHeightCorrection);
  432. m_DebugTargets.Insert(Debug.DrawSphere(colliderPosDebug, 0.15, 0x1f00ff00, ShapeFlags.NOZWRITE));
  433. //m_DebugTargets.Insert(Debug.DrawLine(cubePos, colliderPosDebug, 0x1fff0000, ShapeFlags.NOZWRITE)); // connector
  434. float triggerHeight = (m_PositiveHeight + m_NegativeHeight);
  435. m_DebugTargets.Insert(Debug.DrawCylinder(pos, radius, triggerHeight, 0x1f0000ff, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE));
  436. #endif
  437. #endif
  438. // If the trigger is lower in hierarchy and can see it's local effects customized, we pass the new parameters
  439. if ( m_Trigger.IsInherited(EffectTrigger))
  440. {
  441. //Debug.Log("We override area local effects");
  442. EffectTrigger.Cast(m_Trigger).SetLocalEffects(m_AroundParticleID, m_TinyParticleID, m_PPERequesterIdx);
  443. }
  444. m_Trigger.Init(this, m_EffectsPriority);
  445. //Debug.Log("We created the trigger at : " + m_Trigger.GetWorldPosition() );
  446. }
  447. }
  448. // ----------------------------------------------
  449. // AREA DELETION
  450. // ----------------------------------------------
  451. override void EEDelete( EntityAI parent )
  452. {
  453. if ( m_Trigger )
  454. {
  455. GetGame().ObjectDelete( m_Trigger );
  456. }
  457. // We stop playing particles on this client when the base object is deleted ( out of range for example )
  458. if ( (GetGame().IsClient() || !GetGame().IsMultiplayer()) && m_ToxicClouds )
  459. {
  460. foreach ( Particle p : m_ToxicClouds )
  461. {
  462. p.Stop();
  463. }
  464. }
  465. super.EEDelete( parent );
  466. }
  467. void OnPlayerEnterServer(PlayerBase player, EffectTrigger trigger)
  468. {
  469. player.IncreaseEffectAreaCount();
  470. }
  471. void OnPlayerExitServer(PlayerBase player, EffectTrigger trigger)
  472. {
  473. player.DecreaseEffectAreaCount();
  474. }
  475. #ifdef DIAG_DEVELOPER
  476. #ifdef EFFECT_AREA_VISUAL_DEBUG
  477. protected ref array<Shape> m_DebugTargets = new array<Shape>();
  478. protected void CleanupDebugShapes(array<Shape> shapes)
  479. {
  480. foreach (Shape shape : shapes)
  481. Debug.RemoveShape(shape);
  482. shapes.Clear();
  483. }
  484. #endif
  485. #endif
  486. }