contaminatedarea_dynamic.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. enum eAreaDecayStage
  2. {
  3. INIT = 1, // The dynamic area is initializing
  4. START = 2, // The dynamic area is starting
  5. LIVE = 3, // The dynamic area is live
  6. DECAY_START = 4, // The dynamic area decay has started
  7. DECAY_END = 5, // The dynamic area will soon be deleted
  8. }
  9. // The parameters for the explosion light when creating dynamic area
  10. class ShellLight extends PointLightBase
  11. {
  12. protected float m_DefaultBrightness = 10;
  13. protected float m_DefaultRadius = 100;
  14. void ShellLight()
  15. {
  16. SetVisibleDuringDaylight(false);
  17. SetRadiusTo(m_DefaultRadius);
  18. SetBrightnessTo(m_DefaultBrightness);
  19. SetFlareVisible(false);
  20. SetAmbientColor(1.0, 1.0, 0.3);
  21. SetDiffuseColor(1.0, 1.0, 0.3);
  22. SetLifetime(0.15);
  23. SetDisableShadowsWithinRadius(-1);
  24. SetCastShadow( false );
  25. }
  26. }
  27. // The dynamic Contaminated area, using it's own default settings
  28. class ContaminatedArea_Dynamic : ContaminatedArea_Base
  29. {
  30. protected ref Timer m_StartupTimer;
  31. protected ref Timer m_FXTimer;
  32. protected FlareLight m_FlareLight;
  33. protected ShellLight m_ShellLight; // Light used upon ariborne shell detonation
  34. protected vector m_OffsetPos; // This will be the position at which we spawn all future airborne FX
  35. protected int m_DecayState = eAreaDecayStage.INIT; // The current state in which the area is
  36. // Constants used for startup events
  37. const int AIRBORNE_EXPLOSION_DELAY = 20;
  38. const int AREA_SETUP_DELAY = 10;
  39. const float AIRBORNE_FX_OFFSET = 50;
  40. const float ARTILLERY_SHELL_SPEED = 100; // Units per second
  41. // Constants used for dissapearing events
  42. const float DECAY_START_PART_SIZE = 32;
  43. const int DECAY_START_PART_BIRTH_RATE = 1;
  44. const float DECAY_END_PART_SIZE = 17;
  45. const int DECAY_END_PART_BIRTH_RATE = 1;
  46. const float START_DECAY_LIFETIME = 900;
  47. const float FINISH_DECAY_LIFETIME = 300;
  48. // Item Spawning upon area creation, the 4 arrays bellow have to have the same amount of elements
  49. const ref array<string> SPAWN_ITEM_TYPE = {"Grenade_ChemGas"};//item classnames
  50. const ref array<int> SPAWN_ITEM_COUNT = {Math.RandomIntInclusive(2,5)};//how many of each type
  51. const ref array<float> SPAWN_ITEM_RAD_MIN = {5};//min distance the item will be spawned from the area position(epicenter)
  52. const ref array<float> SPAWN_ITEM_RAD_MAX = {15};//max distance the item will be spawned from the area position(epicenter)
  53. void ContaminatedArea_Dynamic()
  54. {
  55. RegisterNetSyncVariableInt("m_DecayState");
  56. }
  57. override void EEOnCECreate()
  58. {
  59. // We get the PPE index for future usage and synchronization ( we must do it here for dynamic as it is not read through file )
  60. if ( GetGame().IsServer() )
  61. m_PPERequesterIdx = GetRequesterIndex(m_PPERequesterType);
  62. SetSynchDirty();
  63. // If this is the first initialization, we delay it in order to play cool effects
  64. if ( m_DecayState == eAreaDecayStage.INIT )
  65. {
  66. vector areaPos = GetPosition();
  67. m_OffsetPos = areaPos;
  68. m_OffsetPos[1] = m_OffsetPos[1] + AIRBORNE_FX_OFFSET;
  69. // play artillery sound, sent to be played for everyone on server
  70. array<vector> artilleryPoints = GetGame().GetMission().GetWorldData().GetArtyFiringPos();
  71. vector closestPoint = areaPos;
  72. int dist = 0;
  73. int temp;
  74. int index = 0;
  75. for ( int i = 0; i < artilleryPoints.Count(); i++ )
  76. {
  77. temp = vector.DistanceSq( artilleryPoints.Get( i ), areaPos );
  78. if ( temp < dist || dist == 0 )
  79. {
  80. dist = temp;
  81. index = i;
  82. }
  83. }
  84. closestPoint = artilleryPoints.Get( index );
  85. // We calculate the delay depending on distance from firing position to simulate shell travel time
  86. float delay = vector.Distance( closestPoint, areaPos );
  87. delay = delay / ARTILLERY_SHELL_SPEED;
  88. delay += AIRBORNE_EXPLOSION_DELAY; // We add the base, minimum time ( no area can start before this delay )
  89. Param3<vector, vector, float> pos; // The value to be sent through RPC
  90. array<ref Param> params; // The RPC params
  91. // We prepare to send the message
  92. pos = new Param3<vector, vector, float>( closestPoint, areaPos, delay );
  93. params = new array<ref Param>;
  94. // We send the message with this set of coords
  95. params.Insert( pos );
  96. GetGame().RPC( null, ERPCs.RPC_SOUND_ARTILLERY_SINGLE, params, true );
  97. m_FXTimer = new Timer( CALL_CATEGORY_GAMEPLAY );
  98. m_FXTimer.Run( delay, this, "PlayFX" );
  99. delay += AREA_SETUP_DELAY; // We have an additional delay between shell detonation and finalization of area creation
  100. // setup zone
  101. m_StartupTimer = new Timer( CALL_CATEGORY_GAMEPLAY );
  102. m_StartupTimer.Run( delay, this, "InitZone" );
  103. }
  104. }
  105. float GetRemainingTime()
  106. {
  107. return GetLifetime();
  108. }
  109. float GetStartDecayLifetime()
  110. {
  111. return START_DECAY_LIFETIME;
  112. }
  113. float GetFinishDecayLifetime()
  114. {
  115. return FINISH_DECAY_LIFETIME;
  116. }
  117. override void Tick()
  118. {
  119. if ( GetRemainingTime() < GetFinishDecayLifetime() )
  120. {
  121. // The second state of decay, further reduction of particle density and size
  122. SetDecayState( eAreaDecayStage.DECAY_END );
  123. }
  124. else if ( GetRemainingTime() < GetStartDecayLifetime() )
  125. {
  126. // The first state of decay, slight reduction in particle density and size
  127. SetDecayState( eAreaDecayStage.DECAY_START );
  128. }
  129. }
  130. // Set the new state of the Area
  131. void SetDecayState( int newState )
  132. {
  133. if (m_DecayState != newState)
  134. {
  135. m_DecayState = newState;
  136. // We update the trigger state values as we also want to update player bound effects
  137. if ( m_Trigger )
  138. ContaminatedTrigger_Dynamic.Cast( m_Trigger ).SetAreaState( m_DecayState );
  139. SetSynchDirty();
  140. }
  141. }
  142. override void EEInit()
  143. {
  144. // We make sure we have the particle array
  145. if ( !m_ToxicClouds )
  146. m_ToxicClouds = new array<Particle>;
  147. // We set the values for dynamic area as these are not set through JSON and are standardized
  148. m_Name = "Default Dynamic";
  149. m_Radius = 120;
  150. m_PositiveHeight = 7;
  151. m_NegativeHeight = 10;
  152. m_InnerRings = 1;
  153. m_InnerSpacing = 40;
  154. m_OuterSpacing = 30;
  155. m_OuterRingOffset = 0;
  156. m_Type = eZoneType.DYNAMIC;
  157. m_TriggerType = "ContaminatedTrigger_Dynamic";
  158. SetSynchDirty();
  159. #ifdef DEVELOPER
  160. // Debugs when placing entity by hand using internal tools
  161. /*if ( GetGame().IsServer() && !GetGame().IsMultiplayer() )
  162. {
  163. Debug.Log("YOU CAN IGNORE THE FOLLOWING DUMP");
  164. InitZone();
  165. Debug.Log("YOU CAN USE FOLLOWING DATA PROPERLY");
  166. }*/
  167. #endif
  168. m_OffsetPos = GetPosition();
  169. m_OffsetPos[1] = m_OffsetPos[1] + AIRBORNE_FX_OFFSET;
  170. // If a player arrives slightly later during the creation process we check if playing the flare FX is relevant
  171. if ( m_DecayState == eAreaDecayStage.INIT )
  172. PlayFlareVFX();
  173. if ( m_DecayState == eAreaDecayStage.LIVE )
  174. InitZone(); // If it has already been created, we simply do the normal setup, no cool effects, force the LIVE state
  175. else if ( GetGame().IsClient() && m_DecayState > eAreaDecayStage.LIVE )
  176. InitZoneClient(); // Same as before but without state forcing
  177. super.EEInit();
  178. }
  179. // We spawn particles and setup trigger
  180. override void InitZone()
  181. {
  182. m_DecayState = eAreaDecayStage.LIVE;
  183. SetSynchDirty();
  184. super.InitZone();
  185. }
  186. override void InitZoneServer()
  187. {
  188. super.InitZoneServer();
  189. SpawnItems();
  190. // We create the trigger on server
  191. if ( m_TriggerType != "" )
  192. CreateTrigger( m_Position, m_Radius );
  193. }
  194. void SpawnItems()
  195. {
  196. //Print("---------============ Spawning items at pos:"+m_Position);
  197. foreach (int j, string type:SPAWN_ITEM_TYPE)
  198. {
  199. //Print("----------------------------------");
  200. for (int i = 0; i < SPAWN_ITEM_COUNT[j]; i++)
  201. {
  202. vector randomDir2d = vector.RandomDir2D();
  203. float randomDist = Math.RandomFloatInclusive(SPAWN_ITEM_RAD_MIN[j],SPAWN_ITEM_RAD_MAX[j]);
  204. vector spawnPos = m_Position + (randomDir2d * randomDist);
  205. InventoryLocation il = new InventoryLocation;
  206. vector mat[4];
  207. Math3D.MatrixIdentity4(mat);
  208. mat[3] = spawnPos;
  209. il.SetGround(NULL, mat);
  210. //Print("Spawning item:"+ type + " at position:" + il.GetPos());
  211. GetGame().CreateObjectEx(type, il.GetPos(), ECE_PLACE_ON_SURFACE);
  212. }
  213. }
  214. }
  215. override void InitZoneClient()
  216. {
  217. super.InitZoneClient();
  218. if ( !m_ToxicClouds )
  219. m_ToxicClouds = new array<Particle>;
  220. // We spawn VFX on client
  221. PlaceParticles( GetWorldPosition(), m_Radius, m_InnerRings, m_InnerSpacing, m_OuterRingToggle, m_OuterSpacing, m_OuterRingOffset, m_ParticleID );
  222. }
  223. override void OnParticleAllocation(ParticleManager pm, array<ParticleSource> particles)
  224. {
  225. super.OnParticleAllocation(pm, particles);
  226. if ( m_DecayState > eAreaDecayStage.LIVE )
  227. {
  228. foreach ( ParticleSource p : particles )
  229. {
  230. if ( m_DecayState == eAreaDecayStage.DECAY_END )
  231. {
  232. p.SetParameter( 0, EmitorParam.BIRTH_RATE, DECAY_END_PART_BIRTH_RATE );
  233. p.SetParameter( 0, EmitorParam.SIZE, DECAY_END_PART_SIZE );
  234. }
  235. else
  236. {
  237. p.SetParameter( 0, EmitorParam.BIRTH_RATE, DECAY_START_PART_BIRTH_RATE );
  238. p.SetParameter( 0, EmitorParam.SIZE, DECAY_START_PART_SIZE );
  239. }
  240. }
  241. }
  242. }
  243. override void CreateTrigger( vector pos, int radius )
  244. {
  245. super.CreateTrigger( pos, radius );
  246. // This handles the specific case of dynamic triggers as some additionnal parameters are present
  247. ContaminatedTrigger_Dynamic dynaTrigger = ContaminatedTrigger_Dynamic.Cast( m_Trigger );
  248. if ( dynaTrigger )
  249. {
  250. dynaTrigger.SetLocalEffects( m_AroundParticleID, m_TinyParticleID, m_PPERequesterIdx );
  251. dynaTrigger.SetAreaState( m_DecayState );
  252. }
  253. }
  254. void PlayFX()
  255. {
  256. if ( GetGame().IsServer() )
  257. {
  258. Param1<vector> pos; // The value to be sent through RPC
  259. array<ref Param> params; // The RPC params
  260. // We prepare to send the message
  261. pos = new Param1<vector>( vector.Zero );
  262. params = new array<ref Param>;
  263. // We send the message with this set of coords
  264. pos.param1 = m_OffsetPos;
  265. params.Insert( pos );
  266. GetGame().RPC( null, ERPCs.RPC_SOUND_CONTAMINATION, params, true );
  267. // We go to the next stage
  268. m_DecayState = eAreaDecayStage.START;
  269. SetSynchDirty();
  270. }
  271. }
  272. void PlayExplosionLight()
  273. {
  274. m_ShellLight = ShellLight.Cast( ScriptedLightBase.CreateLight( ShellLight, m_OffsetPos ) );
  275. }
  276. void PlayFlareVFX()
  277. {
  278. if ( GetGame().IsClient() || ( GetGame().IsServer() && !GetGame().IsMultiplayer() ) )
  279. {
  280. // We spawn locally the dummy object which will be used to move and manage the particle
  281. DynamicArea_Flare dummy = DynamicArea_Flare.Cast( GetGame().CreateObjectEx( "DynamicArea_Flare", m_OffsetPos, ECE_SETUP | ECE_LOCAL ) );
  282. // We add some light to reinforce the effect
  283. m_FlareLight = FlareLightContamination.Cast(ScriptedLightBase.CreateLight( FlareLightContamination, m_OffsetPos ));
  284. }
  285. }
  286. override void EEDelete( EntityAI parent )
  287. {
  288. super.EEDelete( parent );
  289. }
  290. override void OnVariablesSynchronized()
  291. {
  292. super.OnVariablesSynchronized();
  293. if ( !m_ToxicClouds )
  294. m_ToxicClouds = new array<Particle>;
  295. switch ( m_DecayState )
  296. {
  297. case eAreaDecayStage.START:
  298. PlayExplosionLight();
  299. break;
  300. case eAreaDecayStage.LIVE:
  301. InitZoneClient();
  302. break;
  303. case eAreaDecayStage.DECAY_START:
  304. {
  305. // We go through all the particles bound to this area and update relevant parameters
  306. //Debug.Log("We start decay");
  307. foreach ( Particle p : m_ToxicClouds )
  308. {
  309. p.SetParameter( 0, EmitorParam.BIRTH_RATE, DECAY_START_PART_BIRTH_RATE );
  310. p.SetParameter( 0, EmitorParam.SIZE, DECAY_START_PART_SIZE );
  311. }
  312. break;
  313. }
  314. case eAreaDecayStage.DECAY_END:
  315. {
  316. // We go through all the particles bound to this area and update relevant parameters
  317. //Debug.Log("We finish decay");
  318. foreach ( Particle prt : m_ToxicClouds )
  319. {
  320. prt.SetParameter( 0, EmitorParam.BIRTH_RATE, DECAY_END_PART_BIRTH_RATE );
  321. prt.SetParameter( 0, EmitorParam.SIZE, DECAY_END_PART_SIZE );
  322. }
  323. break;
  324. }
  325. default:
  326. break;
  327. }
  328. }
  329. }