class GeyserTrigger : EffectTrigger { const float MOUTH_ADJUST_RADIUS = 0.2; // maxium radius geyser effect can move when using randomized position adjustment const string SOUND_BUBBLING = "Geyser_bubbling_loop_SoundSet"; const string SOUND_ERUPTION = "Geyser_eruption_loop_SoundSet"; const string SOUND_ERUPTION_START = "Geyser_eruption_start_SoundSet"; const string SOUND_ERUPTION_TALL = "Geyser_eruption_tall_loop_SoundSet"; const string SOUND_ERUPTION_TALL_START = "Geyser_eruption_tall_start_SoundSet"; const string SOUND_ERUPTION_TALL_END = "Geyser_eruption_tall_splash_SoundSet"; protected bool m_bIsDormant; protected bool m_bIsEruptingSoon; protected bool m_bIsErupting; protected bool m_bIsEruptingTall; protected float m_AdjustedX; //deprecated protected float m_AdjustedY; //deprecated protected vector m_DefaultPosition; protected EGeyserState m_GeyserState = EGeyserState.DORMANT; // synchronized state protected ParticleSource m_GeyserBubblesParticle; protected ParticleSource m_GeyserParticle; protected ParticleSource m_GeyserTallParticle; protected ParticleSource m_GeyserSplashParticle; protected EffectSound m_SoundBubbling; protected EffectSound m_SoundEruption; protected EffectSound m_SoundEruptionStart; protected EffectSound m_SoundEruptionSecondary; protected EffectSound m_SoundEruptionSecondaryStart; protected EffectSound m_SoundEruptionSecondaryEnd; void GeyserTrigger() { RegisterNetSyncVariableInt("m_GeyserState", 0, 32); } override void DeferredInit() { super.DeferredInit(); m_DefaultPosition = GetAdjustedPosition(); if (IsSubmerged()) RandomizeMouthPos(); if (!GetGame().IsDedicatedServer()) UpdateGeyserState(); } override string GetDisplayName() { return "#STR_geyser"; } override void EEDelete( EntityAI parent ) { StopEffects(); super.EEDelete(parent); } override void OnEnterServerEvent( TriggerInsider insider ) { super.OnEnterServerEvent(insider); if (insider && (m_GeyserState & EGeyserState.ERUPTING_PRIMARY)) { EntityAI entity = EntityAI.Cast(insider.GetObject()); if (entity) entity.ProcessDirectDamage(DamageType.CUSTOM, this, "", "HeatDamage", "0 0 0", 1000); } } override void OnLeaveServerEvent( TriggerInsider insider ) { super.OnLeaveServerEvent(insider); } override void OnEnterClientEvent( TriggerInsider insider ) { super.OnEnterClientEvent(insider); } override void OnLeaveClientEvent( TriggerInsider insider ) { super.OnLeaveClientEvent(insider); } override void OnVariablesSynchronized() { super.OnVariablesSynchronized(); if (IsInitialized()) UpdateGeyserState(); } // Updated from OnVariablesSynchronized protected void UpdateGeyserState() { // Debug.Log("UpdateGeyserState, state: " + m_GeyserState); if (CheckGeyserState(EGeyserState.DORMANT) && !m_bIsDormant) { m_bIsDormant = true; } else if (!CheckGeyserState(EGeyserState.DORMANT) && m_bIsDormant) { m_bIsDormant = false; } if (CheckGeyserState(EGeyserState.ERUPTION_SOON) && !m_bIsEruptingSoon) { m_GeyserBubblesParticle = ParticleManager.GetInstance().PlayInWorld(ParticleList.GEYSER_BUBBLES, m_DefaultPosition); m_SoundBubbling = SEffectManager.PlaySound(SOUND_BUBBLING, m_DefaultPosition, 2, 2, true); m_bIsEruptingSoon = true; } else if (!CheckGeyserState(EGeyserState.ERUPTION_SOON) && m_bIsEruptingSoon) { m_GeyserBubblesParticle.StopParticle(); m_SoundBubbling.Stop(); m_bIsEruptingSoon = false; } if (CheckGeyserState(EGeyserState.ERUPTING_PRIMARY) && !m_bIsErupting) { m_GeyserParticle = ParticleManager.GetInstance().PlayInWorld(ParticleList.GEYSER_NORMAL, m_DefaultPosition); m_SoundEruptionStart = SEffectManager.PlaySound(SOUND_ERUPTION_START, m_DefaultPosition, 0, 0, false); m_SoundEruption = SEffectManager.PlaySound(SOUND_ERUPTION, m_DefaultPosition, 2, 2, true); m_bIsErupting = true; } else if (!CheckGeyserState(EGeyserState.ERUPTING_PRIMARY) && m_bIsErupting) { m_GeyserParticle.StopParticle(); m_SoundEruptionStart.Stop(); m_SoundEruption.Stop(); m_bIsErupting = false; } if (CheckGeyserState(EGeyserState.ERUPTING_SECONDARY) && !m_bIsEruptingTall) { m_GeyserTallParticle = ParticleManager.GetInstance().PlayInWorld(ParticleList.GEYSER_STRONG, m_DefaultPosition); m_SoundEruptionSecondaryStart = SEffectManager.PlaySound(SOUND_ERUPTION_TALL_START, m_DefaultPosition, 0, 0, false); m_SoundEruptionSecondary = SEffectManager.PlaySound(SOUND_ERUPTION_TALL, m_DefaultPosition, 0, 0, false); m_bIsEruptingTall = true; } else if (!CheckGeyserState(EGeyserState.ERUPTING_SECONDARY) && m_bIsEruptingTall) { m_GeyserSplashParticle = ParticleManager.GetInstance().PlayInWorld(ParticleList.GEYSER_SPLASH, m_DefaultPosition); m_SoundEruptionSecondaryEnd = SEffectManager.PlaySound(SOUND_ERUPTION_TALL_END, m_DefaultPosition, 0, 0, false); m_GeyserTallParticle.StopParticle(); m_SoundEruptionSecondaryStart.Stop(); m_SoundEruptionSecondary.Stop(); m_bIsEruptingTall = false; } } // Slightly adjust position of geyser particles protected void RandomizeMouthPos() { m_DefaultPosition[0] = m_DefaultPosition[0] + Math.RandomFloat( -MOUTH_ADJUST_RADIUS, MOUTH_ADJUST_RADIUS); m_DefaultPosition[2] = m_DefaultPosition[2] + Math.RandomFloat( -MOUTH_ADJUST_RADIUS, MOUTH_ADJUST_RADIUS); //for backwards compatibility m_AdjustedX = m_DefaultPosition[0]; m_AdjustedY = m_DefaultPosition[2]; //typo, should be Z } // Get position on surface of water volume or terrain, add height offset protected vector GetAdjustedPosition(float height = 0) { vector pos = GetPosition(); pos[1] = GetGame().SurfaceRoadY(pos[0], pos[2], RoadSurfaceDetection.UNDER) + height; return pos; } void StopEffects() { if (m_bIsEruptingSoon) { m_GeyserBubblesParticle.StopParticle(); m_SoundBubbling.Stop(); m_bIsEruptingSoon = false; } if (m_bIsErupting) { m_GeyserParticle.StopParticle(); m_SoundEruption.Stop(); m_bIsErupting = false; } if (m_bIsEruptingTall) { m_GeyserTallParticle.StopParticle(); m_SoundEruptionSecondary.Stop(); m_bIsEruptingTall = false; } } void AddGeyserState(EGeyserState state) { m_GeyserState |= state; SetSynchDirty(); } void RemoveGeyserState(EGeyserState state) { m_GeyserState &= ~state; SetSynchDirty(); } bool CheckGeyserState(EGeyserState state) { if (state == EGeyserState.DORMANT) return (m_GeyserState == state); else return (m_GeyserState & state); } EGeyserState GetGeyserState() { return m_GeyserState; } // override for differences in logic between land & submerged geysers bool IsSubmerged() { return true; } }