Script.ReloadScript("scripts/Entities/Sound/Shared/AudioUtils.lua");

Lightning =
{
	Properties =
	{
		bActive = 1,
		fDistance = 0, -- Distance from entity position where strike occurs.
		fDistanceVariation = 0.2, --Variation of the Distance.
		bRelativeToPlayer = 0,
		Timing =
		{
			fDelay = 5,  -- Delay between strikes in seconds
			fDelayVariation = 0.5,  -- Variation of Delay between strikes in percent.
			fLightningDuration = 0.2,  -- in seconds
		},
		Effects =
		{
			LightRadius = 1000,
			LightIntensity = 1,
			ParticleEffect = "weather.lightning.lightningbolt1",
			ParticleScale = 1,
			ParticleScaleVariation = 0.2,
			--SkyMultiplier = 1,
			--color_SkyColor = {x=1,y=1,z=1},
			SkyHighlightVerticalOffset = 0,
			SkyHighlightMultiplier = 1,
			color_SkyHighlightColor = {x=0.8,y=0.8,z=1},
			SkyHighlightAtten = 10,
		},
		Audio =
		{
			audioTriggerPlayTrigger = "",
			audioTriggerStopTrigger = "",
			audioRTPCDistanceRtpc = "",
			fSpeedOfSoundScale = 1.0,
		},
	},

	TempPos = {x=0.0,y=0.0,z=0.0},

	Editor =
	{
		Model="Editor/Objects/Particles.cgf",
		Icon="Lightning.bmp",
	},

	_LightTable =
	{
		diffuse_color = { x=1, y=1,z=1 };
		specular_color = { x=1, y=1,z=1 };
	},
	_ParticleTable = {},
	_SkyHighlight = { size=0,color={x=0,y=0,z=0},position={x=0,y=0,z=0} },
	_StrikeCount = 0,
	hOnTriggerID = nil,
	hOffTriggerID = nil,
	hRtpcID = nil,
	nAudioTimerMinID = 2, -- Audio Timer IDs start at this value and can grow dynamically, any additional timers must not come after audio timers ID wise!
	aAudioThunders = {},
	nNumAuxAudioProxies = 4,
	nNumThunder = 1,
}

----------------------------------------------------------------------------------------
function Lightning:_LookupControlIDs()
	self.hOnTriggerID = AudioUtils.LookupTriggerID(self.Properties.Audio.audioTriggerPlayTrigger);	
	self.hOffTriggerID = AudioUtils.LookupTriggerID(self.Properties.Audio.audioTriggerStopTrigger);
	self.hRtpcID = AudioUtils.LookupRtpcID(self.Properties.Audio.audioRTPCDistanceRtpc);
end

----------------------------------------------------------------------------------------
function Lightning:_KillAllAudioTimers()
	local nIndex = 1;

	while (self.aAudioThunders[nIndex] ~= nil) do
		self:KillTimer(self.aAudioThunders[nIndex].nTimerID);
		self.aAudioThunders[nIndex].fDistance = 0.0;
		self.aAudioThunders[nIndex].vStrikeOffset.x = 0.0;
		self.aAudioThunders[nIndex].vStrikeOffset.y = 0.0;
		self.aAudioThunders[nIndex].vStrikeOffset.z = 0.0;		
		nIndex = nIndex + 1;
	end
end

----------------------------------------------------------------------------------------
function Lightning:OnSpawn()
	self:SetFlags(ENTITY_FLAG_CLIENT_ONLY,0);
end

----------------------------------------------------------------------------------------
function Lightning:OnInit()
	self.bStriking = 0;
	self.light_fade = 0;
	self.light_intensity = 0;
	self.vStrikeOffset = {x=0,y=0,z=0};
	self.vSkyHighlightPos = {x=0,y=0,z=0};

	--self:NetPresent(0);
	self.bActive = self.Properties.bActive;
	
	-- This is the default and always existing AudioProxy.
	local oAudioThunderEntry = {nTimerID = self.nAudioTimerMinID, nAudioProxyID = self:GetDefaultAuxAudioProxyID(), fDistance = 0.0, vStrikeOffset = {x = 0.0, y = 0.0, z = 0.0}};
	table.insert(self.aAudioThunders, oAudioThunderEntry);
		
	-- This script creates self.nNumAuxAudioProxies additional AudioProxies on the entity which we use for up to self.nNumAuxAudioProxies+1 simultaneous thunder audio events.
	for i = 1, self.nNumAuxAudioProxies do
		oAudioThunderEntry = {nTimerID = self.nAudioTimerMinID + i, nAudioProxyID = self:CreateAuxAudioProxy(), fDistance = 0.0, vStrikeOffset = {x = 0.0, y = 0.0, z = 0.0}};
		table.insert(self.aAudioThunders, oAudioThunderEntry);
	end
	
	self:_LookupControlIDs();
	self:ScheduleNextStrike();
	self:CacheResources();
end

----------------------------------------------------------------------------------------
function Lightning:CacheResources()
	self:PreLoadParticleEffect( self.Properties.Effects.ParticleEffect );
end

----------------------------------------------------------------------------------------
function Lightning:OnShutDown()
	self:StopStrike();
end

----------------------------------------------------------------------------------------
function Lightning:OnLoad(table)
	self.bActive = table.bActive;
	self:StopStrike();
	self:ScheduleNextStrike();
end

----------------------------------------------------------------------------------------
function Lightning:OnSave(table)
	table.bActive = self.bActive;
end

----------------------------------------------------------------------------------------
function Lightning:OnPropertyChange()
	self.bActive = self.Properties.bActive;
	
	if (self.bActive == 1) then
		self:_LookupControlIDs();
		self:ScheduleNextStrike();
	else
		-- Kill all possibly pending timers.
		self:KillTimer(0);
		self:KillTimer(1);
		self:_KillAllAudioTimers();
		
		if (self.hOffTriggerID ~= nil) then
			-- The stop trigger addresses all of the AuxAudioProxies.
			-- Note: Maybe it's more feasible to execute a stop trigger only on the default AuxAudioProxy!?
			self:ExecuteAudioTrigger(self.hOffTriggerID, self:GetAllAuxAudioProxiesID());
		end
	end
end

----------------------------------------------------------------------------------------
function Lightning:LoadLightToSlot( nSlot )
	local props = self.Properties;
	local Effects = props.Effects;

	local lt = self._LightTable;
	lt.style = 0;
	lt.deferred_light = 1;
	lt.corona_scale = 1;
	lt.corona_dist_size_factor = 1;
	lt.corona_dist_intensity_factor = 1;
	lt.radius = Effects.LightRadius;

	local clr = self.light_intensity * Effects.LightIntensity;
	lt.diffuse_color.x = clr;
	lt.diffuse_color.y = clr;
	lt.diffuse_color.z = clr;

	lt.specular_color.x = clr;
	lt.specular_color.y = clr;
	lt.specular_color.z = clr;

	--lt.diffuse_color = { x=Color.clrDiffuse.x*diffuse_mul, y=Color.clrDiffuse.y*diffuse_mul, z=Color.clrDiffuse.z*diffuse_mul };
	--lt.specular_color = { x=Color.clrSpecular.x*specular_mul, y=Color.clrSpecular.y*specular_mul, z=Color.clrSpecular.z*specular_mul };
	lt.hdrdyn = 0;
	lt.lifetime = 0;
	lt.realtime = 1;
	lt.dot3 = 1;
	lt.cast_shadow = 0;

	self:LoadLight( nSlot,lt );
	self:SetSlotPos( nSlot,self.vStrikeOffset );
end

----------------------------------------------------------------------------------------
-- Lightning effect stopped in OnTimer
----------------------------------------------------------------------------------------
function Lightning:OnTimer( nTimerId )
	if (nTimerId == 0) then
		self:Event_Strike();
	elseif (nTimerId == 1) then
		self.bStopStrike = 1;
	else
		-- Is this one of the audio timers?
		local nIndex = 1;
		
		while (self.aAudioThunders[nIndex] ~= nil) do
			if (self.aAudioThunders[nIndex].nTimerID == nTimerId) then
				-- Set supplied data and execute the play audio trigger.	
				if (self.hOnTriggerID ~= nil) then
					if (self.hRtpcID ~= nil) then
						self:SetAudioRtpcValue(self.hRtpcID, self.aAudioThunders[nIndex].fDistance, self.aAudioThunders[nIndex].nAudioProxyID);
					end
					
					self:SetAudioProxyOffset(self.aAudioThunders[nIndex].vStrikeOffset, self.aAudioThunders[nIndex].nAudioProxyID);
					self:ExecuteAudioTrigger(self.hOnTriggerID, self.aAudioThunders[nIndex].nAudioProxyID);
				end
				
				self.aAudioThunders[nIndex].fDistance = 0.0;
				self.aAudioThunders[nIndex].vStrikeOffset.x = 0.0;
				self.aAudioThunders[nIndex].vStrikeOffset.y = 0.0;
				self.aAudioThunders[nIndex].vStrikeOffset.z = 0.0;
				break;				
			end
			
			nIndex = nIndex + 1;
		end
	end
end

----------------------------------------------------------------------------------------
function Lightning:StopStrike()
	if (self.bStriking == 0) then
		self:ScheduleNextStrike();
		return;
	end
	-- Stop lightning effect.
	self:FreeSlot(0); -- Free light slot.
	self:FreeSlot(1); -- Free particle slot.
	-- Stop particle effects.
	self:Activate(0);
	self.bStriking = 0;
	self.bStopStrike = 0;

	self:ScheduleNextStrike();

	-- Restore zero sky highlight.
	self._SkyHighlight.size = 0;
	self._SkyHighlight.color.x = 0;
	self._SkyHighlight.color.y = 0;
	self._SkyHighlight.color.z = 0;
	self._SkyHighlight.position.x = 0;
	self._SkyHighlight.position.y = 0;
	self._SkyHighlight.position.z = 0;
	System.SetSkyHighlight( self._SkyHighlight );

	-- Restore original sky color.
	--if (self.vPrevSkyColor) then
		--System.SetSkyColor( self.vPrevSkyColor );
		--self.vPrevSkyColor = nil;
	--end

	self._StrikeCount = self._StrikeCount - 1;
end

----------------------------------------------------------------------------------------
function Lightning:OnUpdate( dt )
	self.light_intensity = self.light_intensity - self.light_fade*dt;
	if (self.light_intensity <= 0) then
		if (self.bStopStrike == 1) then
		  self:StopStrike();
		  do return end;
		end
		self.light_intensity = 1 - math.random()*0.5;
		self.light_fade = 3 + math.random()*5;
	end

	self:UpdateLightningParams();
end

----------------------------------------------------------------------------------------
function Lightning:OnStartGame()
	-- Force object updates. The lightning has no AI and would get stuck without updates.
	CryAction.ForceGameObjectUpdate(self.id, true);
end

----------------------------------------------------------------------------------------
function Lightning:UpdateLightningParams()
	self:LoadLightToSlot( 0 );

	local Effects = self.Properties.Effects;

	local highlight = self.light_intensity * Effects.SkyHighlightMultiplier;
	self._SkyHighlight.color.x = highlight * Effects.color_SkyHighlightColor.x;
	self._SkyHighlight.color.y = highlight * Effects.color_SkyHighlightColor.y;
	self._SkyHighlight.color.z = highlight * Effects.color_SkyHighlightColor.z;

	self._SkyHighlight.size = Effects.SkyHighlightAtten;

	self._SkyHighlight.position.x = self.vSkyHighlightPos.x;
	self._SkyHighlight.position.y = self.vSkyHighlightPos.y;
	self._SkyHighlight.position.z = self.vSkyHighlightPos.z + Effects.SkyHighlightVerticalOffset;

	System.SetSkyHighlight( self._SkyHighlight );

	--local skylight = self.light_intensity * Effects.SkyMultiplier;
	--self._SkyHighlight.color.x = skylight * Effects.color_SkyColor.x;
	--self._SkyHighlight.color.y = skylight * Effects.color_SkyColor.y;
	--self._SkyHighlight.color.z = skylight * Effects.color_SkyColor.z;
	--System.SetSkyColor( self._SkyHighlight.color );
end

----------------------------------------------------------------------------------------
function Lightning:GetValueWithVariation( v,variation )
	return v + (math.random()*2 - 1)*v*variation;
end

----------------------------------------------------------------------------------------
function Lightning:ScheduleNextStrike()
	if (self.bActive == 1) then
		local delay = self.Properties.Timing.fDelay;
		delay = self.Properties.Timing.fLightningDuration + self:GetValueWithVariation(delay,self.Properties.Timing.fDelayVariation);
		self:SetTimer( 0,delay*1000 );
	end
end

----------------------------------------------------------------------------------------
-- Start lightning effect stopped in OnTimer
----------------------------------------------------------------------------------------
function Lightning:Event_Strike()
	if (self.bStriking == 0) then
		self.bStriking = 1;

		--self.vPrevSkyColor = nil;
		--if (self._StrikeCount == 0) then
			--self.vPrevSkyColor = new(System.GetSkyColor());
		--end

		self._StrikeCount = self._StrikeCount+1;

		local props = self.Properties;
		local Effects = props.Effects;

		self.light_intensity = 1 - math.random()*0.5;
		self.light_fade = 3 + math.random()*5;

		local vEntityPos = self:GetPos();

		local vCamDir = System.GetViewCameraDir();
		local vCamPos = System.GetViewCameraPos();
		local vStrikePos = vEntityPos;

		local fDistance = self:GetValueWithVariation(props.fDistance,props.fDistanceVariation);

		local minAngle = 0;
		local maxAngle = 360;

		if (props.bRelativeToPlayer == 1) then
			if (vCamDir.x > 0 and vCamDir.y > 0) then
				minAngle = -90; maxAngle = 180;
			end
			if (vCamDir.x > 0 and vCamDir.y < 0) then
				minAngle = 0; maxAngle = 270;
			end
			if (vCamDir.x < 0 and vCamDir.y < 0) then
				minAngle = 90; maxAngle = 360;
			end
			if (vCamDir.x < 0 and vCamDir.y > 0) then
				minAngle = 180; maxAngle = 360+90;
			end
			minAngle = minAngle+100;
			maxAngle = maxAngle-100;
		end

		-- Generate random position at radius fDstance from entity center.
		local phi = (minAngle + math.random()*(maxAngle-minAngle))*3.14/180;
		--local phi = (minAngle + maxAngle)/2*3.14/180;
		self.vStrikeOffset.x = fDistance*math.sin(phi);
		self.vStrikeOffset.y = fDistance*math.cos(phi);

		if (props.bRelativeToPlayer == 1) then
			local vCamDir = System.GetViewCameraDir();
			self.vStrikeOffset.x = self.vStrikeOffset.x + (vCamPos.x - vEntityPos.x);
			self.vStrikeOffset.y = self.vStrikeOffset.y + (vCamPos.y - vEntityPos.y);
		end

		-- Find Distance to the camera from the light strike (in XY plane only).
		local dx = vCamPos.x-(vEntityPos.x+self.vStrikeOffset.x);
		local dy = vCamPos.y-(vEntityPos.y+self.vStrikeOffset.y);
		local toCameraDistance = math.sqrt( dx*dx + dy*dy);

		self.vSkyHighlightPos.x = vEntityPos.x + self.vStrikeOffset.x;
		self.vSkyHighlightPos.y = vEntityPos.y + self.vStrikeOffset.y;
		self.vSkyHighlightPos.z = vEntityPos.z;

		-- Spawn Particle effect.
		if (Effects.ParticleEffect ~= "") then
		  -- Scale effect to keep the same size regardless of the distance to the lightning.
			self._ParticleTable.Scale = self:GetValueWithVariation(Effects.ParticleScale,Effects.ParticleScaleVariation)*toCameraDistance;
			self:LoadParticleEffect( 1, Effects.ParticleEffect, self._ParticleTable );
			self:SetSlotPos( 1,self.vStrikeOffset );
		end

		self:UpdateLightningParams();

		local Timing = self.Properties.Timing;
		
		if (self.hOnTriggerID ~= nil) then			
			if (self.nNumThunder > (self.nNumAuxAudioProxies + 1)) then
				self.nNumThunder = 1;
			end
			
			local oAudioThunder = self.aAudioThunders[self.nNumThunder];
			
			oAudioThunder.fDistance = toCameraDistance;
			oAudioThunder.vStrikeOffset.x = self.vStrikeOffset.x;
			oAudioThunder.vStrikeOffset.y = self.vStrikeOffset.y;
			oAudioThunder.vStrikeOffset.z = self.vStrikeOffset.z;
			
			-- Set timer for thunder sound depending on speed of sound at sea level (340.29 m/s) multiplied by a scalar.
			local fTimeDelayOfThunderSound = (toCameraDistance / 340.29) * self.Properties.Audio.fSpeedOfSoundScale;
			
			-- Note: pending timers get killed!
			self:SetTimer(oAudioThunder.nTimerID, fTimeDelayOfThunderSound * 1000.0);
			self.nNumThunder = self.nNumThunder + 1;
		end

		self:SetTimer( 1,self:GetValueWithVariation(Timing.fLightningDuration,0.5)*1000 );
		self:Activate(1);
	end
	BroadcastEvent( self,"Strike" );
end

----------------------------------------------------------------------------------------
function Lightning:Event_Enable()
	if (self.bActive == 0) then
		self.bActive = 1;
		self:ScheduleNextStrike();
	end
end

----------------------------------------------------------------------------------------
function Lightning:Event_Disable()
	self.bActive = 0;
end

----------------------------------------------------------------------------------------
-- Event descriptions.
----------------------------------------------------------------------------------------
Lightning.FlowEvents =
{
	Inputs =
	{
		Strike = { Lightning.Event_Strike,"bool" },
		Enable = { Lightning.Event_Enable,"bool" },
		Disable = { Lightning.Event_Disable,"bool" },
	},
	Outputs =
	{
		Strike = "bool",
	},
}
