-- fuel.lua
-- @date 2022-02-20
-- @author MrRare
-- @brief Fuel logic, detects fuel cells based on shape tags, detect health of fuel cells, make fuel cells go boom, spawns particles, creates debris and spawns more particles

#include "light_spawner/lightspawner.lua"
#include "particle.lua"
#include "generic.lua"

Fuel_Cells = {} -- Stores all the found fuel cells
Fuel_FireSpawnTimeout = 30 -- how long fires/smoke can spawn (and spawn new fires)
Fuel_FireAmount = 5 -- How many random locations close to the tank should catch and spawn fires
Fuel_ExplosionAmount = 3 -- How many explosion tanks generate
Fuel_ExplosionSize = 2 -- How many explosion tanks generate
Fuel_ExplosionDelay = 0-- Delay between fuel tanks damaged and explosion.
Fuel_ExplosionFireAmount = 200  -- fuel explosion sudden fire spawn
Fuel_DebrisAmount = 3 -- AMount of shapes that can be assinged as "debris"
Fuel_Timer = 0	-- Counts every second
Fuel_AccurateTimer = 0 -- Counts every frame time
Fuel_RateTimer = 0 -- Counts every frame time
Fuel_Rate = (0.9 / 60) * 2 -- You can set the rate to for example (1/60) * 2 = 30 fps spawn and update rate, will improve performance but limit smoke and fire generation by a lot
Fuel_BigExplosionSound = {} -- List of sounds for explosions
Fuel_EmitLight = true -- Use pointlights to enhance visuals (slow as hell)
Fuel_ToggleLightKey = 'L' -- Toggle lights, to give ya a choice
Fuel_ToggleLightModeKey = 'N' -- Switch between legacy and new method
Fuel_LightRandomness = 0.3 -- This determines the flickering of the lights  (simulating fire)

-- If lights are enabled
Fuel_ExplosionBrightness = 500  -- Brightness of explosion
Fuel_BigFireBrightness = 100  -- Brightness of big fires
Fuel_SmallFireBrightness = 50 -- Brightness of smaller fires

Fuel_OldVersion = false -- Used for switching between old and new light methods

function init()
	-- Generic_ClearDebugPrinter()
	-- Load explosion sounds
	for i=0,3 do
		Fuel_BigExplosionSound[i] = LoadSound("MOD/plane/CRJ-200 FUEL/snd/fail_big/"..i)
	end

	-- Detect all fuel shapes
    local fuel_cell_shapes = FindShapes("fuel")

	-- Store them once, determine center, and how many voxels it consists of
    for i=1, #fuel_cell_shapes do
        local fuel_cell = fuel_cell_shapes[i]
		local fuel_cell_voxels = GetShapeVoxelCount(fuel_cell)
		local center = GetShapeWorldTransform(fuel_cell_shape).pos
        Fuel_Cells[fuel_cell] = {voxcount=fuel_cell_voxels, damaged=false, exploded=false, spawntime=0, center=center, fire_locations={}, debris=false, debris_count=0, explosion_count=0, strength=tonumber(GetTagValue(fuel_cell, "strength")), light=nil}
    end
end

function tick(dt)
	local last_pressed = InputLastPressedKey()
	if last_pressed == Fuel_ToggleLightKey then
		if Fuel_EmitLight then
			Fuel_EmitLight = false
			LightSpawner_DeleteAll()
			-- DebugWatch("Disabled light during fire/explosions on plane (YOUR PC IS GRATEFULL)")
		else
			Fuel_EmitLight = true
			if Fuel_OldVersion == false then
				LightSpawner_DeleteAll()
			end
			-- DebugWatch("Enabled light during fire/explosions on plane(FPS KILLER)")
		end
	end

	if last_pressed == Fuel_ToggleLightModeKey then
		if Fuel_OldVersion == false then
			Fuel_OldVersion = true
			LightSpawner_DeleteAll()
		else
			Fuel_OldVersion = false
		end
	end

	-- Accurate timer, counts in frame time
    Fuel_AccurateTimer = Fuel_AccurateTimer + dt

	-- Second timer, counts in seconds (to prevent rounding errors)
    if Fuel_AccurateTimer >= 1 then
        Fuel_Timer = Fuel_Timer + 1
        Fuel_AccurateTimer = 0
    end

	if Fuel_RateTimer > Fuel_Rate then
		-- For each existing fuel cell
		for key, value in pairs(Fuel_Cells) do
			-- Get voxel count and recalculate center every frame.
			local fuel_cell = value
			local fuel_cell_shape = key
			local fuel_cell_voxels = GetShapeVoxelCount(fuel_cell_shape)
			local center = GetShapeWorldTransform(fuel_cell_shape).pos

		-- Debris (See later) can have issues witht center location, if anything is close to center ignore it.
			-- Apply newly found center to fuel cell
			if fuel_cell_voxels > 0 then
				fuel_cell["center"] = center
			end
			-- If fuel cell is not damaged, calculate health of fuel cell
			if fuel_cell["damaged"] == false then
				local fuel_cell_health = (fuel_cell_voxels /  fuel_cell["voxcount"]) * 100

				-- DebugWatch("Fuel cell " .. GetTagValue(fuel_cell_shape, "fuel") .. " health (explodes on: " .. GetTagValue(fuel_cell_shape, "strength") .. ") Expl: " .. fuel_cell["explosion_count"], fuel_cell_health)

				if fuel_cell_health > 0 and fuel_cell_health < fuel_cell["strength"] then
					fuel_cell["damaged"] = true
					fuel_cell["spawntime"] = Generic_deepCopy(Fuel_Timer)
				end

			elseif Fuel_Timer - fuel_cell["spawntime"] >= Fuel_FireSpawnTimeout then
				for x=1, #fuel_cell["fire_locations"] do
					local fire_location = fuel_cell["fire_locations"][x]
					LightSpawner_DeleteLight(fire_location[3])
				end
				if fuel_cell["light"] ~= nil then
					LightSpawner_DeleteLight(fuel_cell["light"])
				end
				Fuel_Cells[fuel_cell_shape] = nil
			-- If fuel cell is damaged, and within the timeout range (set when it is detected that it is damaged), spawn fire and such
			elseif fuel_cell["damaged"] and Fuel_Timer - fuel_cell["spawntime"] < Fuel_FireSpawnTimeout then
				-- If fuel cell has exploded, detect debris and assign them as "fuel cells that has exploded and thus spawn fire/smoke"
				if fuel_cell["exploded"] == true then
					LightSpawner_DeleteTagged("explosion_light")

					-- Only original fuel cells can spawn ALOT of fire/smoke
					if fuel_cell["debris"] == false then
						-- DebugCross(center, 0,1,0,1)

						-- Detect debris caused by explosion (most likely) (they will be set on fire as well)
						if fuel_cell["debris_count"] < Fuel_DebrisAmount  then
							local outerpoints = Generic_CreateBox(fuel_cell["center"], 8, nil, {1, 0, 0}, false)
							local shapes = QueryAabbShapes(outerpoints[1], outerpoints[7])
							-- DebugPrint("Tank " .. GetTagValue(fuel_cell_shape, "fuel") .. "exploded, found " .. #shapes .. " shapes")
							for s=1, #shapes do
								local debris = shapes[s]
								if Fuel_Cells[debris] == nil then
									-- Make sure not every tiny voxel is counted as debris ;P
									local debris_center = GetShapeWorldTransform(debris).pos
									local debris_vox = GetShapeVoxelCount(debris)

									if debris_vox > 1 and debris_vox < 100 then
										SetTag(debris, "fuel", "debris_" .. GetTagValue(fuel_cell_shape, "fuel"))
										SpawnFire(debris_center)
										local light = nil
										if Fuel_EmitLight then
											local l = Generic_SpawnLight(debris_center,  Particle_Fire, Generic_rnd((Fuel_BigFireBrightness * Fuel_LightRandomness) / 2 ,Fuel_BigFireBrightness  / 2))
											light = LightSpawner_Spawn(l[1], l[2], l[3], l[4], true)
										end
										Fuel_Cells[debris] = {voxcount=0, damaged=true, exploded=true, spawntime=Generic_deepCopy(Fuel_Timer), center=debris_center, debris=true, debris_count=0, explosion_count=0, strength=nil, fire_locations={}, light=light}
										if fuel_cell["debris_count"] > Fuel_DebrisAmount then
											break
										end
										fuel_cell["debris_count"] = fuel_cell["debris_count"] + 1
									end

								end
							end
						end

						-- Give the game  100 chances to use raycast to find locations to spawn fire at
						for x = 0, 100 do
							if #fuel_cell["fire_locations"] < Fuel_FireAmount then
								local direction = Vec(Generic_rnd(-1,1),Generic_rnd(-1,1),Generic_rnd(-1,1))
								local hit, dist,n,s = QueryRaycast(fuel_cell["center"], direction, 10)
								if hit then
									local newpoint = VecAdd(fuel_cell["center"], VecScale(direction, dist))
									local hit, point, normal, shape = QueryClosestPoint(newpoint, 1)
									if hit then
										local l = Generic_SpawnLight(point,  Particle_Fire, Generic_rnd(Fuel_BigFireBrightness * Fuel_LightRandomness ,Fuel_BigFireBrightness))
										local light = LightSpawner_Spawn(l[1], l[2], l[3], l[4], true)
										fuel_cell["fire_locations"][#fuel_cell["fire_locations"] + 1] = {point, shape, light}
										SpawnFire(point)
									end
								end
							else
								break
							end
						end

						-- Then spawn per fire location particles, and doe a closest p oint query to set thhe nearest object on fire
						for x=1, #fuel_cell["fire_locations"] do
							local fire_location = fuel_cell["fire_locations"][x]
							local shape_mat = GetShapeMaterialAtPosition(fire_location[2], fire_location[1])
							if shape_mat == "" then
								local hit, point, normal, shape = QueryClosestPoint(fire_location[1], 3)
								if hit then
									SpawnFire(point)
									local random = Generic_rndInt(50,90)
									Particle_FireSmoke(Particle_Fire, Particle_Smoke, random, random + 10, point)
									if Fuel_EmitLight then
										if Fuel_OldVersion then
											PointLight(VecAdd(point, Generic_rndVec(0.01)), Particle_Fire["color"]["r"] /  1, Particle_Fire["color"]["g"] /  1.75, Particle_Fire["color"]["b"] / 4,  Generic_rnd(Fuel_BigFireBrightness * Fuel_LightRandomness ,Fuel_BigFireBrightness))
										else
											LightSpawner_SetNewLightLocation(fire_location[3], point)
										end
									end
									fuel_cell["fire_locations"][x][1] = point
									fuel_cell["fire_locations"][x][2] = shape
								else

									LightSpawner_DeleteLight(fire_location[3])
								end
							else
								local random = Generic_rndInt(50,90)
								Particle_FireSmoke(Particle_Fire, Particle_Smoke, random, random + 10, fire_location[1])
								if Fuel_EmitLight then
									if Fuel_OldVersion then
										PointLight(VecAdd(fire_location[1], Generic_rndVec(0.01)), Particle_Fire["color"]["r"] /  1, Particle_Fire["color"]["g"] /  1.75, Particle_Fire["color"]["b"] / 4,  Generic_rnd(Fuel_BigFireBrightness * Fuel_LightRandomness ,Fuel_BigFireBrightness))
									else
										if LightSpawner_UpdateLightIntensity(fire_location[3], Generic_rnd(Fuel_BigFireBrightness * Fuel_LightRandomness,Fuel_BigFireBrightness)) == nil then
											LightSpawner_DeleteLight(fire_location[3])
											local l = Generic_SpawnLight(fire_location[1],  Particle_Fire, Generic_rnd(Fuel_BigFireBrightness * Fuel_LightRandomness ,Fuel_BigFireBrightness))
											local light = LightSpawner_Spawn(l[1], l[2], l[3], l[4], false)
											fuel_cell["fire_locations"][x][3] = light
										end
									end
								end
							end
						end

						-- Give the game  100 chances to use raycast to find locations to cause remainder explosions
						for x = 0, 100 do
							if fuel_cell["explosion_count"] < Fuel_ExplosionAmount then
								local direction = Generic_rndVec(1)
								local hit, dist,n,s = QueryRaycast(fuel_cell["center"], direction, 10)
								if hit then
									local newpoint = VecAdd(fuel_cell["center"], VecScale(direction, dist))
									local explsize = Generic_rnd(Fuel_ExplosionSize / 2, Fuel_ExplosionSize)
									Explosion(newpoint, explsize)

									if Fuel_EmitLight then
										if Fuel_OldVersion == false then
											local l = Generic_SpawnLight(newpoint,  Particle_Fire, Generic_rnd(Fuel_ExplosionBrightness * explsize ,Fuel_ExplosionBrightness * explsize * 2))
											LightSpawner_Spawn(l[1], l[2], l[3], l[4], true, "explosion_light")
										end
									end
									PlaySound(Fuel_BigExplosionSound[math.random(1,3)],newpoint, explsize * 100)
									for a=0, Fuel_ExplosionFireAmount do
										Particle_FireSmoke(Particle_Fire_Expl, Particle_Smoke_Expl, 100, 100, newpoint)
									end
									-- For visual effect, spawn bright intensive light (Performance heavy!)
									fuel_cell["explosion_count"] = fuel_cell["explosion_count"]  + 1
									break
								end
							else
								break
							end
						end
					-- For debris in specific we want to just generate fire/smoke particles at the center of debris, but it can spawn actual when it is close to something
					elseif  fuel_cell_voxels > 0 then
						-- DebugCross(center, 1,0,0,1)
						local hit, point, normal, shape = QueryClosestPoint(fuel_cell["center"], 6)
						if hit then
							fuel_cell["center"] = point
							SpawnFire(point)
							LightSpawner_SetNewLightLocation(fuel_cell["light"], point)
							LightSpawner_UpdateLightIntensity(fuel_cell["light"], Generic_rnd(Fuel_BigFireBrightness * Fuel_LightRandomness,Fuel_BigFireBrightness))
							local random = Generic_rndInt(50,80)
							Particle_FireSmoke(Particle_Fire, Particle_Smoke, random, random+10, fuel_cell["center"])
						end
					-- If somehow we end up here, debris no longer exist so might as well forget it
					else
						for x=1, #fuel_cell["fire_locations"] do
							local fire_location = fuel_cell["fire_locations"][x]
							LightSpawner_DeleteLight(fire_location[3])
						end
						if fuel_cell["light"] ~= nil then
							LightSpawner_DeleteLight(fuel_cell["light"])
						end
						Fuel_Cells[fuel_cell_shape] = nil
					end
				-- If fuel cell after being damaged hits the explosion delay, create the explosion
				elseif (Fuel_Timer - fuel_cell["spawntime"] >= Fuel_ExplosionDelay) then
					-- DebugPrint("Explosion count for " .. GetTagValue(fuel_cell_shape, "fuel") .. " is " .. fuel_cell["explosion_count"] )
					local explsize = Generic_rnd(Fuel_ExplosionSize / 2, Fuel_ExplosionSize)
					Explosion(fuel_cell["center"], explsize)

					if Fuel_EmitLight then
						if Fuel_OldVersion == false then
							local l = Generic_SpawnLight(fuel_cell["center"],  Particle_Fire, Generic_rnd(Fuel_ExplosionBrightness * explsize ,Fuel_ExplosionBrightness * explsize * 2))
							LightSpawner_Spawn(l[1], l[2], l[3], l[4], true, "explosion_light")
						end
					end
					PlaySound(Fuel_BigExplosionSound[math.random(1,3)],fuel_cell["center"], explsize * 100)
					for a=0, Fuel_ExplosionFireAmount do
						Particle_FireSmoke(Particle_Fire_Expl, Particle_Smoke_Expl, 100, 100, fuel_cell["center"])
					end
					-- For visual effect, spawn bright intensive light (Performance heavy!)
					fuel_cell["explosion_count"] = fuel_cell["explosion_count"]  + 1

					fuel_cell["exploded"] = true
				-- Initial damage will cause a smaller fire, then after some time it will explode
				else
					local random = Generic_rndInt(30,60)
					Particle_FireSmoke(Particle_Fire_Expl, Particle_Smoke_Expl, random, random + 10, fuel_cell["center"])
				end
			end
		end
		Fuel_RateTimer = 0
	else
		Fuel_RateTimer = Fuel_RateTimer + dt
	end

end

