-- Description: 		Artillery Barrage
-- Created by Michael Chimella 15March2008

--Version History:
--Version 4 -- air flak ability added with z variable
--Version 5 -- now kills all explosions set up on reset

--Specification:
--Barrage depth and width (x and y), latency (average time between hits), Time of barrage
--Individual explosion parameters: effect, effectscale, radius, damage, pressure
--AAA flak using z option and alternative particle effect

--Uses:
--Always start using flow graph, make active before starting
--Ground artillery barrage, choose x and y to set bounding box (entity is at corner), place entity on ground
--Hillside barrage, use several with long bounding boxes running across the hill
--AAA Flac, set z move entity to lowest explosion height and choose appropriate particle effect
--Cluster bombs, set particle effect, explode params small, very low latency, low Time, set x and y for correct box
--map sporadic explosions, put in corner of land, set x and y to cover only land, Time very high, latency high
--napalm attack, long box, very low latency, low Time, fire particle effect


--notes:
--use self:GetWorldPos() and modify randomly within the box
--entity on corner of barrage 'box'
--z values will have to remain constant meaning effect on hills would need several long thin barrages triggered
--effect could be used for AA flak in the air
--In the start function Time and Latency are used to calculate a number of explosions, enum
--.. these are then assigned random timinings in ms over the Time period, so Time will be pretty accurate

--To do
--The Deactivate and CeaseFire ports are working but cannot use while barrage firing, need some kind of function break
--Loop check box for continuing until stopped, for games where constant occasional barrage
--check if save and load half way through work
--cannot stop barrage once stopped even using reload scripts need to kill function! 
--save time barrage has left and load this onload as the Time, will create a new set of timing then
--For deactivate to work properly will have to KillTimer, need this is timing of end of barrage is critical

--Future version:
--Add sound for particle effects that have no sound
--Wining in sound for shells

Artillery = {
	
	Properties =
	{
		bActive						= 1,
		soclasses_SmartObjectClass = "",
		Explosion=
		{
			Effect					= "explosions.grenade_air.explosion",
			EffectScale			= 1,
			Radius					= 10,
			Pressure				= 1000,
			Damage					= 1000,
			Decal						= "textures/decal/explo_decal.dds",
			HoleSize				= 10,
			TerrainHoleSize	= 5,
			Direction				= {x=0, y=0, z=1},
		},
		--Barrage Distribution
		Spread=	{
			BarrageRangeX		= 100,
			BarrageRangeY		= 100,
			BarrageRangeZ		= 0,
			fLatency				= 1,
			--fAccuracy				= 0.5,
			--Amount					= 25,
			Time						= 60,
			},
		
		
		
	},
	Editor={
		Icon = "Item.bmp",
		IconOnTop=1,
	}
};

--Variables
--iTimer={}; --needs be global for loading and saving, I think!

----------------------------------------------------------------------------------------------------
function Artillery:OnPropertyChange()
	self:OnReset();
end

------------------------------------------------------------
function Artillery:OnReset()
	self.bActive = self.Properties.bActive;		--this is simply reseting bActive to the value in rollupbar on Reloading
	if(self.iTimer) then
		for i=1,self.enum do
			Script.KillTimer(self.iTimer[i]);	--kills all the explosions set up to run
		end
	end
	--self.bStarted = 0;
	self.bCounting = 0;
end

------------------------------------------------------------
function Artillery:OnInit()
	self:OnReset();
	self:PreLoadParticleEffect( self.Properties.Explosion.Effect );		--a listed lua function
end

----------------------------------------------------------------------------------------------------
function Artillery:OnSave(save)			--only save parameters that can change in game
	save.bActive = self.bActive;			--whether activated
	save.bCounting = self.bCounting;	--whether Started	
	if self.bCounting ~= 0 then
		assert(self.StartTime);
		save.Time = self.Time - (System.GetCurrTime() - self.StartTime);	--save the time left for barrage to run
	end 
end

----------------------------------------------------------------------------------------------------
function Artillery:OnLoad(saved)
	self:OnReset();
	self.bActive = saved.bActive;
	self.bCounting = saved.bCounting;
	if self.bCounting ~= 0 then
		local setTimerTo = 0.0001;
		if not saved.Time then
			LogWarning("Invalid data in Artillery restore");
		elseif saved.Time < self.Properties.Spread.Time then
			setTimerTo = saved.Time;
			self.Time = saved.Time;
		end
		
		--on DelayTrigger here you have the self.iTimer line but I cannot do that since long function
		--if barrage does not restart after death during it I will have to make a special function
		--which can be called here but using setTimerTo instead of self.Properties.Spread.Time
		
	end
end

----------------------------------------------------------------------------------------------------
function Artillery:Explode(shooterId)
	local expl = self.Properties.Explosion;
	local pos = self:GetWorldPos();
	pos.x = pos.x + (math.random(0,self.Properties.Spread.BarrageRangeX));
	pos.y = pos.y + (math.random(0,self.Properties.Spread.BarrageRangeY));
	pos.z = pos.z + (math.random(0,self.Properties.Spread.BarrageRangeZ));
	g_gameRules:CreateExplosion(NULL_ENTITY,self.id,expl.Damage,pos,expl.Direction,expl.Radius,nil,expl.Pressure,expl.HoleSize,expl.Effect,expl.EffectScale);
	--BroadcastEvent(self, "Explode");
	System.RemoveEntity(self.id);
end

----------------------------------------------------------------------------------------------------
function Artillery:Event_Start(sender)
	if (tonumber(self.bActive) ~= 0) then
		BroadcastEvent(self, "Start");
		self.StartTime = System.GetCurrTime();
		self.bCounting = 1;
		self.iTimer = {};	--declare empty array for holding self.enum number of random explosion timings
		--local enum = self.Properties.Spread.Time / self.Properties.Spread.fLatency; -- number of explosions
		self.enum = self.Properties.Spread.Time / self.Properties.Spread.fLatency; -- number of explosions
		for i=1,self.enum do
					local single = math.random(0,self.Properties.Spread.Time*1000);
					--CryAction.Persistant2DText(string.format(single),2,{255,255,255},"Event_Start",10); -- HUD timing in ms for debug
					self.iTimer[i] = Script.SetTimerForFunction(single,"Artillery.OnTimer",self);							
		end
		
	end
end

---------------------------------------------------------------------
function Artillery:OnTimer(timerid)
	self:Explode(NULL_ENTITY);
	--self.iTimer = nil; -- needs correcting based on fact now array
end

------------------------------------------------------------------------------------------------------
function Artillery:Event_Activate(sender)	
	self.bActive = 1;
	BroadcastEvent(self, "Activate");
end

------------------------------------------------------------------------------------------------------
function Artillery:Event_CeaseFire(sender)
	--if(self.iTimer) then
	--self:OnReset();
	--if self.bCounting ~= 0 then
	if (tonumber(self.bCounting) ~= 0) then
		for i=1,self.enum do
			Script.KillTimer(self.iTimer[i]);	--kills all the explosions set up to run
		end
	end
	--self.bStarted = 0;
	self.bCounting = 0;
	BroadcastEvent(self, "CeaseFire");
end

------------------------------------------------------------------------------------------------------
function Artillery:Event_Deactivate(sender)	
	self.bActive = 0;
	BroadcastEvent(self, "Deactivate");
end

------------------------------------------------------------------------------------------------------
Artillery.FlowEvents =
{
	Inputs =
	{
		Activate = { Artillery.Event_Activate, "bool" },
		Deactivate = { Artillery.Event_Deactivate, "bool" },
		Start = { Artillery.Event_Start, "bool" },
		--CeaseFire = { Artillery.Event_CeaseFire, "bool" },
	},
	Outputs =
	{
		Activate = "bool",
		Deactivate = "bool",
		Start = "bool",
		--CeaseFire = "bool",
	},
}