class ShockHandler { protected const float UPDATE_THRESHOLD = 3; //NOTE : The lower, the more precise but the more synchronization const float VALUE_CHECK_INTERVAL = 0.95; //in seconds private const int INTENSITY_FACTOR = 1; //How intense the vignette effect will be, the higher the value, the stronger the effect private const int VIGNETTE_INTENSITY_MAX = 1; //Max BASE intensity of the vignette effect (w/o. bobbing). 0..2, where 2 is full screen coverage private const int VIGNETTE_INTENSITY_MAX_TOTAL = 2; //Max TOTAL intensity of the vignette effect (w. bobbing). 0..2, where 2 is full screen coverage private const float SMOOTHING_MAX_INCR = 0.05; //max smoothing change INCREASE per update private const float SMOOTHING_MAX_DECR = 0.01; //max smoothing change DECREASE per update //bobbing constants private const float PULSE_PERIOD = 0.5; //The time it takes for pulse to do a full cycle private const float PULSE_AMPLITUDE = 0.05; //This is a multiplier, keep below 1 or expect the unexpected protected float m_Shock; protected float m_LastEffectIntensityValue; //for interpolation only protected float m_ShockValueMax; protected float m_CumulatedShock; //! works ok on server, but does nothing for client. See deprecated stuff. private float m_TimeSinceLastTick = VALUE_CHECK_INTERVAL + 1; private float m_ShockMultiplier = 1; private float m_PrevVignette; //Previous shock-adjecent value (some normalization required). Client sets it only on regular ShockHandler update! //bobbing effect private float m_PulseTimer; //PPE PPERequester_TunnelVisionEffects m_Requester; protected ref Param1 m_Param; protected PlayerBase m_Player; void ShockHandler(PlayerBase player) { m_Player = player; m_Player.m_CurrentShock = m_Player.GetMaxHealth("", "Shock"); m_ShockValueMax = m_Player.GetMaxHealth("", "Shock"); m_PrevVignette = m_Player.m_CurrentShock / m_ShockValueMax; //Equivalent to divided by 100 m_Requester = PPERequester_TunnelVisionEffects.Cast(PPERequesterBank.GetRequester(PPERequester_TunnelVisionEffects)); m_Param = new Param1(0); //loegacy stuff m_ShockValueThreshold = m_ShockValueMax * 0.95; } void Update(float deltaT) { m_TimeSinceLastTick += deltaT; m_PulseTimer += deltaT; //periodical update, just in case if (m_TimeSinceLastTick > VALUE_CHECK_INTERVAL) { if (GetGame().IsClient()) { //ShockHitEffect(m_PrevVignette * m_ShockValueMax); m_PrevVignette = m_Player.m_CurrentShock / m_ShockValueMax; } CheckValue(false); m_TimeSinceLastTick = 0; } if (GetGame().IsClient()) { float valAdjusted = BaseEffectIntensityCalc(); if (valAdjusted <= 0) { if (m_Requester.IsRequesterRunning()) m_Requester.Stop(); return; } //Print("dbgShock | valAdjusted: " + valAdjusted); //Add bobbing to create pulsing effect valAdjusted = AddEffectBobbing(valAdjusted); m_Param.param1 = valAdjusted; m_Requester.Start(m_Param); } } float GetCurrentShock() { return m_Player.m_CurrentShock; } float GetShock() { return m_Shock; } void SetShock(float dealtShock) { m_Shock += dealtShock; CheckValue(false); } //Inflict shock damage private void DealShock() { if (GetGame().IsServer()) m_Player.GiveShock(-m_CumulatedShock); } //Passing a value of FALSE will only check the values, a value of TRUE will force a synchronization (don't use too often) void CheckValue(bool forceUpdate) { m_CumulatedShock += m_Shock; // increment on both client and server if (forceUpdate) m_PrevVignette = NormalizeShockVal(m_Player.m_CurrentShock); if (GetGame().IsServer()) { if (m_CumulatedShock >= UPDATE_THRESHOLD || forceUpdate) { m_CumulatedShock *= m_ShockMultiplier; DealShock(); m_CumulatedShock = 0; m_Shock = 0; Synchronize(); } m_Player.m_CurrentShock = m_Player.GetHealth("", "Shock"); } } protected void Synchronize() { DayZPlayerSyncJunctures.SendShock(m_Player, m_Player.m_CurrentShock); } protected float BaseEffectIntensityCalc() { float effectIntensity = 1 - Easing.EaseInQuart(NormalizeShockVal(m_Player.m_CurrentShock)); //smoothing if (effectIntensity != m_LastEffectIntensityValue) effectIntensity = Math.Clamp(effectIntensity,m_LastEffectIntensityValue - SMOOTHING_MAX_DECR, m_LastEffectIntensityValue + SMOOTHING_MAX_INCR); m_LastEffectIntensityValue = effectIntensity; return effectIntensity; } //! adds bobbing, also clamps to valid range protected float AddEffectBobbing(float baseVal) { float ret = baseVal; float bobbingVal = 0.0; if (m_Player.m_CurrentShock > m_ShockValueMax * 0.8) bobbingVal = MiscGameplayFunctions.Bobbing(PULSE_PERIOD, PULSE_AMPLITUDE, m_PulseTimer); ret += bobbingVal; ret = Math.Clamp(ret,0,VIGNETTE_INTENSITY_MAX_TOTAL); return ret; } float SetMultiplier(float mult) { m_ShockMultiplier = mult; return m_ShockMultiplier; } private float NormalizeShockVal(float shock) { float base = m_ShockValueMax * INTENSITY_FACTOR; float normShock = shock / base; return normShock; } private float LerpVignette(float x, float y, float deltaT) { float output; output = Math.Lerp(x, y, deltaT); return output; } /////////////////////////// //Deprecated stuff below// ///////////////////////// private const int LIGHT_SHOCK_HIT = 33; //!Deprecated private const int MID_SHOCK_HIT = 67; //!Deprecated private const int HEAVY_SHOCK_HIT = 100; //!Deprecated protected float m_ShockValueThreshold; //!Deprecated private float m_LerpRes; //!Deprecated //!Deprecated private void ShockHitEffect(float compareBase) { float shockDifference = compareBase - m_Player.m_CurrentShock; if (shockDifference >= UPDATE_THRESHOLD) { if (m_CumulatedShock < 25) m_Player.SpawnShockEffect(MiscGameplayFunctions.Normalize(LIGHT_SHOCK_HIT, 100)); else if (m_CumulatedShock < 50) m_Player.SpawnShockEffect(MiscGameplayFunctions.Normalize(MID_SHOCK_HIT, 100)); else m_Player.SpawnShockEffect(MiscGameplayFunctions.Normalize(HEAVY_SHOCK_HIT, 100)); } } };