/* Author: Boris Vacula Description: The flare has 3 burning states during which it uses different particle effects while illumnatingthe environment. After it burns up, it still releases smoke for a while. When the flare is dropped while its burning, it is stood up on its stands. This makes the shadows, illumination and particles stand out better. */ enum RoadflareBurningState { NOT_BURNING, INITIAL_BURN, MAIN_BURN, FINAL_BURN, SMOKE_ONLY }; enum RoadflareModelStates { DEFAULT, UNCAPPED_UNIGNITED, UNCAPPED_IGNITED, UNCAPPED_BURNED_OUT }; class Roadflare : ItemBase { // Burning static protected const int INITIAL_BURNING_STATE_TIME = 5; static protected const int FINAL_BURNING_STATE_TIME = 60; static protected vector m_FlameLocalPos = "0 0.285 0"; private int m_BurningState = RoadflareBurningState.NOT_BURNING; // Light RoadflareLight m_Light; // Particles protected Particle m_ParInitialFire; protected Particle m_ParMainFire; protected Particle m_ParFinalFire; protected Particle m_ParJustSmoke; static protected int PARTICLE_INIT_FIRE = ParticleList.ROADFLARE_BURNING_INIT; static protected int PARTICLE_MAIN_FIRE = ParticleList.ROADFLARE_BURNING_MAIN; static protected int PARTICLE_FINAL_FIRE = ParticleList.ROADFLARE_BURNING_ENDING; static protected int PARTICLE_FINAL_SMOKE = ParticleList.ROADFLARE_BURNING_SMOKE; // Sounds protected EffectSound m_BurningSound; protected EffectSound m_IgniteSound; static protected const string BURNING_SOUND = "roadflareLoop_SoundSet"; static protected const string IGNITE_SOUND = "roadflareTurnOn_SoundSet"; static protected const int BURNING_NOISE_RANGE = 30; // Timers ref Timer m_FinalSmokeTimer; // Ignition states private int m_ModelState = RoadflareModelStates.DEFAULT; // Selections static const string STANDS_FOLDED = "Sticks_Flare_Folded"; static const string STANDS_UNFOLDED = "Sticks_Flare_Unfolded"; static const string FLARE_CAP = "Flare_cap"; static const string UNIGNITED_TIP = "Pristine"; static const string IGNITED_TIP = "Burning"; static const string EXTINGUISHED_TIP = "Burned_out"; static const int SELECTION_Burning = 0; static const int SELECTION_Burned_out = 1; static const int SELECTION_Pristine = 2; static const int SELECTION_All = 3; static const string DEFAULT_TEXTURE = "dz\\gear\\consumables\\data\\road_flare_co.paa"; static const string BURNING_TEXTURE = "dz\\gear\\consumables\\data\\road_flare_e_co.paa"; static const string DEFAULT_MATERIAL = "dz\\gear\\consumables\\data\\road_flare.rvmat"; static const string BURNING_MATERIAL = "dz\\gear\\consumables\\data\\road_flare_on.rvmat"; // Noise ref NoiseParams m_NoisePar; private float m_NoiseTimer; void Roadflare() { RegisterNetSyncVariableInt("m_BurningState"); } // Use RoadflareModelStates enum void SetModelState(int enum_state) { m_ModelState = enum_state; UpdateModelSelections(); } override void EEDelete(EntityAI parent) { super.EEDelete(parent); if ( GetGame() ) { SEffectManager.DestroyEffect( m_BurningSound ); SEffectManager.DestroyEffect( m_IgniteSound ); delete m_FinalSmokeTimer; } DestroyAllParticles(); if (m_Light) m_Light.FadeOut(0); } override void OnStoreSave(ParamsWriteContext ctx) { super.OnStoreSave(ctx); ctx.Write( m_ModelState ); } override bool OnStoreLoad(ParamsReadContext ctx, int version) { if ( !super.OnStoreLoad(ctx, version) ) return false; int state; if ( !ctx.Read(state) ) state = RoadflareModelStates.DEFAULT; SetModelState(state); return true; } void UpdateModelSelections() { switch (m_ModelState) { case RoadflareModelStates.DEFAULT: ShowSelection(FLARE_CAP); ShowSelection(UNIGNITED_TIP); HideSelection(IGNITED_TIP); HideSelection(EXTINGUISHED_TIP); SetObjectTexture(SELECTION_Pristine, DEFAULT_TEXTURE); SetObjectMaterial(SELECTION_Pristine, DEFAULT_MATERIAL); break; case RoadflareModelStates.UNCAPPED_UNIGNITED: ShowSelection(UNIGNITED_TIP); HideSelection(FLARE_CAP); HideSelection(IGNITED_TIP); HideSelection(EXTINGUISHED_TIP); SetObjectTexture(SELECTION_Pristine, DEFAULT_TEXTURE); SetObjectMaterial(SELECTION_Pristine, DEFAULT_MATERIAL); break; case RoadflareModelStates.UNCAPPED_IGNITED: ShowSelection(IGNITED_TIP); HideSelection(UNIGNITED_TIP); HideSelection(FLARE_CAP); HideSelection(EXTINGUISHED_TIP); // No texture/material change here because the model already contains them break; case RoadflareModelStates.UNCAPPED_BURNED_OUT: ShowSelection(EXTINGUISHED_TIP); HideSelection(UNIGNITED_TIP); HideSelection(FLARE_CAP); HideSelection(IGNITED_TIP); SetObjectTexture(SELECTION_Burned_out, DEFAULT_TEXTURE); SetObjectMaterial(SELECTION_Burned_out, DEFAULT_MATERIAL); break; } } // When the flare starts burning override void OnWorkStart() { if ( !GetGame().IsServer() || !GetGame().IsMultiplayer() ) { PlaySoundSetLoop( m_BurningSound, BURNING_SOUND, 0.5, 0 ); PlaySoundSet( m_IgniteSound, IGNITE_SOUND, 0, 0 ); m_Light = RoadflareLight.Cast( ScriptedLightBase.CreateLight( RoadflareLight, Vector(0,0,0) ) ); m_Light.AttachOnMemoryPoint( this, m_Light.m_MemoryPoint ); } if ( GetGame().IsServer() ) { /*m_NoisePar = new NoiseParams(); m_NoisePar.LoadFromPath("cfgVehicles Roadflare NoiseRoadFlare"); if ( GetGame().GetWorld().IsNight() ) { NoiseSystem noise = GetGame().GetNoiseSystem(); if ( noise ) { noise.AddNoisePos( this, GetPosition(), m_NoisePar, NoiseAIEvaluate.GetNoiseReduction(GetGame().GetWeather()) ); } }*/ } SetBurningState(RoadflareBurningState.INITIAL_BURN); SetModelState( RoadflareModelStates.UNCAPPED_IGNITED ); } // Insert RoadflareBurningState enum index as parameter void SetBurningState(int state_number) { m_BurningState = state_number; } // Insert RoadflareBurningState enum index as parameter. Activates server -> client synchronization void SetBurningStateSynchronized(int state_number) { if ( GetGame().IsServer() ) { m_BurningState = state_number; SetSynchDirty(); } } // Every second of the flare burning override void OnWork(float consumed_energy) { if ( GetGame().IsServer() ) { float burning_time = GetCompEM().GetEnergyMax() - GetCompEM().GetEnergy(); // Update burning states if (m_BurningState == RoadflareBurningState.INITIAL_BURN) { if (burning_time >= INITIAL_BURNING_STATE_TIME) { SetBurningStateSynchronized(RoadflareBurningState.MAIN_BURN); } } else if ( m_BurningState == RoadflareBurningState.MAIN_BURN ) { /*if ( GetGame().GetWorld().IsNight() ) { NoiseSystem noise = GetGame().GetNoiseSystem(); if ( noise ) { noise.AddNoisePos( this, GetPosition(), m_NoisePar, NoiseAIEvaluate.GetNoiseReduction(GetGame().GetWeather())); } }*/ if ( burning_time >= GetCompEM().GetEnergyMax() - FINAL_BURNING_STATE_TIME ) { SetBurningStateSynchronized(RoadflareBurningState.FINAL_BURN); } } // Burn containers of this roadflare. This might be removed if lit roadflare can't be put into inventory. /*EntityAI container_EAI = GetHierarchyParent(); if ( container_EAI && container_EAI.IsInherited(ItemBase) && !container_EAI.IsInherited(TripwireTrap) ) { ItemBase container_IB = ItemBase.Cast( container_EAI ); int c_size = container_IB.GetItemSize(); if (c_size == 0) c_size = 1; container_IB.AddHealth("","",-10/c_size); }*/ } UpdateActiveParticles(); } // When the flare stops burning override void OnWorkStop() { if ( GetGame().IsMissionMainMenu() ) // This is singleplayer main menu so no synchronization here! { SetBurningState(RoadflareBurningState.NOT_BURNING); UpdateActiveParticles(); } else { if ( GetGame().IsServer() ) { //Safeguard if item is turned off by another event than running out of energy if (GetCompEM().GetEnergy() > 0) { if (m_Light) { m_Light.FadeOut(); } SetBurningState(RoadflareBurningState.NOT_BURNING); return; } SetBurningStateSynchronized(RoadflareBurningState.SMOKE_ONLY); SetHealth("","",0); } UpdateActiveParticles(); m_FinalSmokeTimer = new Timer( CALL_CATEGORY_SYSTEM ); m_FinalSmokeTimer.Run(60, this, "StopSmoking", NULL, false); } if ( m_BurningSound ) StopSoundSet( m_BurningSound ); if (m_Light) m_Light.FadeOut(); SetModelState( RoadflareModelStates.UNCAPPED_BURNED_OUT ); } // Updates all (in)active particles void UpdateActiveParticles() { if ( GetGame().IsDedicatedServer() ) return; switch (m_BurningState) { case RoadflareBurningState.NOT_BURNING: DestroyAllParticles(); break; case RoadflareBurningState.INITIAL_BURN: if (!m_ParInitialFire) { DestroyAllParticles(); m_ParInitialFire = ParticleManager.GetInstance().PlayOnObject( PARTICLE_INIT_FIRE, this, m_FlameLocalPos); m_ParInitialFire.SetWiggle( 10, 0.3 ); } break; case RoadflareBurningState.MAIN_BURN: if (!m_ParMainFire) { m_ParMainFire = ParticleManager.GetInstance().PlayOnObject( PARTICLE_MAIN_FIRE, this, m_FlameLocalPos); m_ParMainFire.SetWiggle( 7, 0.3 ); } DestroyParticleEx(m_ParInitialFire); break; case RoadflareBurningState.FINAL_BURN: if (!m_ParFinalFire) { DestroyAllParticles(); m_ParFinalFire = ParticleManager.GetInstance().PlayOnObject( PARTICLE_FINAL_FIRE, this, m_FlameLocalPos); m_ParFinalFire.SetWiggle( 4, 0.3 ); } break; case RoadflareBurningState.SMOKE_ONLY: if (!m_ParJustSmoke) { DestroyAllParticles(); m_ParJustSmoke = ParticleManager.GetInstance().PlayOnObject( PARTICLE_FINAL_SMOKE, this, m_FlameLocalPos); m_ParJustSmoke.SetWiggle( 2, 0.3 ); } break; } } // Destroys the given particle void DestroyParticle( Particle p ) { if (p) { p.SetWiggle(0,0); p.Stop(); } } void DestroyParticleEx( out Particle p ) { DestroyParticle(p); p = null; } // Destroys all particles void DestroyAllParticles() { DestroyParticleEx(m_ParInitialFire); DestroyParticleEx(m_ParMainFire); DestroyParticleEx(m_ParFinalFire); DestroyParticleEx(m_ParJustSmoke); } // Stop releasing final smoke void StopSmoking() { SetBurningStateSynchronized(RoadflareBurningState.NOT_BURNING); UpdateActiveParticles(); } // Inventory manipulation override void OnInventoryExit(Man player) { super.OnInventoryExit(player); if (m_BurningState != RoadflareBurningState.NOT_BURNING) { HideSelection(STANDS_FOLDED); ShowSelection(STANDS_UNFOLDED); if (player) { vector ori_rotate = player.GetOrientation(); ori_rotate = ori_rotate + Vector(180, 32, 0); SetOrientation(ori_rotate); } } } override bool CanPutInCargo( EntityAI parent ) { if ( !super.CanPutInCargo(parent) ) return false; if ( parent && parent.IsInherited( FireplaceBase ) ) return true; if ( m_BurningState != RoadflareBurningState.NOT_BURNING ) return false; return true; } override void OnInventoryEnter(Man player) { super.OnInventoryEnter(player); HideSelection(STANDS_UNFOLDED); ShowSelection(STANDS_FOLDED); } override void OnActivatedByItem(notnull ItemBase item) { if (item.IsInherited(TripwireTrap)) { GetCompEM().SwitchOn(); } } override bool CanIgniteItem(EntityAI ignite_target = NULL) { return GetCompEM().IsWorking(); } override void OnVariablesSynchronized() { super.OnVariablesSynchronized(); UpdateActiveParticles(); } override void SetActions() { super.SetActions(); AddAction(ActionAttach); AddAction(ActionDetach); AddAction(ActionLightItemOnFire); AddAction(ActionTurnOnWhileInHands); } };