ActorRegister={} -- list of new actors
FinishCells = {CPos.New(46,4),CPos.New(46,5),CPos.New(46,6) }
SpiceTriggers= {Spice_location1, Spice_location2,Spice_location3,Spice_location4}
SpiceTriggersNames = {}
SpiceTriggersNames[1] = ""
SpiceTriggersNames[2] = ""
SpiceTriggersNames[3] = ""
SpiceTriggersNames[4] = ""
TriggerScanRadius = {9,11,8,8}
RequiredSpicePerTrigger = {1000,1000,1000,1000}
Harvesters = {}
HarvestedSpice = {}
HarvestedSpice["Multi0"] = {0,0,0,0}
HarvestedSpice["Multi1"] = {0,0,0,0}
HarvestedSpice["Multi2"] = {0,0,0,0}
HarvestedSpice["Multi3"] = {0,0,0,0}
ActualCash = {}
ActualCash["Multi0"] = 0
ActualCash["Multi1"] = 0
ActualCash["Multi2"] = 0
ActualCash["Multi3"] = 0
PlayersNames = {"????","????","????","????"}
Objective_GatherSpice = {}
Objective_GatherSpice["Multi0"] = {}
Objective_GatherSpice["Multi1"] = {}
Objective_GatherSpice["Multi2"] = {}
Objective_GatherSpice["Multi3"] = {}
Objective_finishRace = {}
GameStage = {}
GameStage[1] = false
GameStage[2] = false
GameStage[3] = false
AttackLocations = {}
AirstrikeCounter = {}
StartingWalls = {Wall1,Wall2,Wall3,Wall4,Wall5, Wall6}
Turrets = {}
Intro = true
SecondaryObjectives = false -- when enabled player must harvest 1000$ from each field
MapCrates = true -- when disabled remove all crates from the map
Timer = false
Seconds = 0
Minutes = 0
AI_ItemChecks = {"item.repair1","item.repair2","item.repair3","item.repair4","item.repair5","item.hide","item.hide1","item.hide2","item.hide3","item.hide4","item.hide5"}
InitObjectives = function(player)
	for i = 1, #RequiredSpicePerTrigger do
		if SecondaryObjectives == false then
			if player.IsBot then
				Objective_GatherSpice[player.InternalName][i] = player.AddSecondaryObjective("Harvest " .. tostring(RequiredSpicePerTrigger[i]).." from "..tostring(i).." Spice location")
			end
			--Objective_GatherSpice[player.InternalName][i] = 0
		
		else
			Objective_GatherSpice[player.InternalName][i] = player.AddSecondaryObjective("Harvest " .. tostring(RequiredSpicePerTrigger[i]).." from "..tostring(i).." Spice location")
		end
	end
	Objective_finishRace[player.InternalName] = player.AddPrimaryObjective("Finish the race")
	Trigger.OnPlayerLost(player, function()
		Trigger.AfterDelay(DateTime.Seconds(1), function()
			Media.PlaySpeechNotification(player, "Lose")
		end)
	end)
	Trigger.OnPlayerWon(player, function()
		local harvSorted = Utils.Where(Harvesters, function(harv) 
			return harv.Owner == player and harv.IsDead ~= true
		end)
		if harvSorted[1] ~= nil then
			Camera.Position = harvSorted[1].CenterPosition
			harvSorted[1].GrantCondition("victory")
		end
		Media.DisplayMessage(tostring(player).."complete win the race at time: "..tostring(Minutes)..":"..tostring(Seconds), "Mentat", HSLColor.DarkRed)
		Trigger.AfterDelay(DateTime.Seconds(1), function()
			Media.PlaySpeechNotification(player, "Win")
		end)
	end)
	if SecondaryObjectives == true or player.IsBot then
		

		Trigger.OnObjectiveCompleted(player, function(p, id)
			Media.DisplayMessage(tostring(p.Name).." completed objective: "..p.GetObjectiveDescription(id).."at time: "..tostring(Minutes)..":"..tostring(Seconds), "Mentat", HSLColor.DarkRed)
			Media.PlaySpeechNotification(player,"objective_reached")
			if GameStage[1] == false then
				if id == Objective_GatherSpice[player.InternalName][1] then
					Media.Debug("INITIATING squads")
					InitMidGame()
				end
			end
			if GameStage[2] == false then
				local pon = 0
				for i, owner in pairs(Players) do
					if owner.IsObjectiveCompleted(Objective_GatherSpice[player.InternalName][1]) then
						pon = pon + 1
					end
				end
				if pon == #Players then
					Media.DisplayMessage("HarvesterRespawn taken to the middle of the map", "Mentat", HSLColor.DarkRed)
					GameStage[2] = true
				end
			end
			if GameStage[3] == false then
				if id == Objective_GatherSpice[player.InternalName][3] then
					InitLateGame()
				end

			end
			if player.IsBot then
				Trigger.AfterDelay(Utils.RandomInteger(0, 300), function()
					InitHarvester_AI(player)
				end)
			end
		end)
	end
end

WorldLoaded = function()
	--get players
	CameraActor = Actor.Create("dummy.camera", true, {Location = CPos.New(24,6),Owner = Player.GetPlayer("Neutral"), CenterPosition = Map.CenterOfCell(CPos.New(24,6))+ WVec.New(0,0,Actor.CruiseAltitude("dummy.camera"))})
	mp0=Player.GetPlayer("Multi0")
	mp1=Player.GetPlayer("Multi1")
	mp2=Player.GetPlayer("Multi2")
	mp3=Player.GetPlayer("Multi3")
	mp4=Player.GetPlayer("Multi4")
	mp5=Player.GetPlayer("Multi5")
	mp6=Player.GetPlayer("Multi6")
	mp7=Player.GetPlayer("Multi7")
	mp8=Player.GetPlayer("Multi8")
	Creeps = Player.GetPlayer("Creeps")
	NeutralPlayer = Player.GetPlayer("Neutral")
	Smugglers = Player.GetPlayer("Smugglers")
	Mercenary = Player.GetPlayer("Mercenary")
	Harkonnen = Player.GetPlayer("Harkonnen")
	Fremen = Player.GetPlayer("Fremen")
	Corrino = Player.GetPlayer("Corrino")
	Smugglers.GrantCondition("enable_harvester_module")
	Harkonnen.Cash = 500000
	Fremen.Cash = 500000
	Smugglers.Cash = 10000
	Corrino.Cash = 100000
	Players = {mp0, mp1, mp2, mp3, mp4, mp5}
	for i, player in pairs(Players) do
		if player.HasPrerequisites({"skip_intro"}) then
			Intro = false
		end
		if not player.HasPrerequisites({"mapCrates"}) then
			MapCrates = false
		end
		if player.HasPrerequisites({"secondary_objectives"}) then
			SecondaryObjectives = true
		end
		PlayersNames[i] = player.Name
		local playerHarvesters = player.GetActorsByTypes({ "harvester.ra", "harvester.cnc","harvester.d2k" })
		player.GrantCondition("invulnerable")
		HarvesterInsurance(playerHarvesters, player)
		for j, harvester in pairs(playerHarvesters) do
			table.insert(Harvesters, harvester)
			Trigger.OnKilled(harvester, function()
				--table.remove(Harvesters, j)
				--fuck this, why just actor can become nil?
				for position, deadActor in pairs(Harvesters) do
					if deadActor.IsDead then
						table.remove(Harvesters, position)
					end
				end
			end)
			if player.IsBot then
				AI_ItemLogic(harvester)
			end
		end
		InitObjectives(player)
	end
	AllWaypoints = NeutralPlayer.GetActorsByTypes({"waypoint","spice_location"})
	-- Spice location triggers
	for i, scriptTrigger in pairs(SpiceTriggers) do
		SpiceTriggersNames[i]  = tostring(SpiceTriggers[i])
		local wPOsPosition = Map.CenterOfCell(scriptTrigger.Location)
		Trigger.OnEnteredProximityTrigger(wPOsPosition, WDist.FromCells(TriggerScanRadius[i]), function (actor)
			if actor.Type == "harvester.d2k" or actor.Type == "harvester.ra" or actor.Type == "harvester.cnc" then
				Media.Debug("harvester enter"..tostring(actor.Owner))
				if not actor.IsDead then
					ActualCash[actor.Owner.InternalName] = actor.Owner.Cash
					actor.AddTag(SpiceTriggersNames[i])
					actor.AddTag("in_field")
				end
			end
		
		end)
		Trigger.OnExitedProximityTrigger(wPOsPosition, WDist.FromCells(TriggerScanRadius[i]), function (actor)
			if actor.Type == actor.Type == "harvester.d2k" or actor.Type == "harvester.ra" or actor.Type == "harvester.cnc"then
				Media.Debug("harvester exited"..tostring(actor.Owner))
				if not actor.IsDead then
					actor.RemoveTag(SpiceTriggersNames[i])
					actor.RemoveTag("in_field")
					
				end
			end
		
		end)
	end
	Trigger.OnEnteredFootprint(FinishCells, function(actor)
		local winner = CheckWinCondition(actor)
		if winner == true then
			actor.Owner.MarkCompletedObjective(Objective_finishRace[actor.Owner.InternalName])
		end
	end)
	-- Item production
	Trigger.OnAnyProduction(function(producer, produced)
		if produced.Type == "upgrade.turret_trike" or produced.Type == "upgrade.turret_cannon" or produced.Type == "upgrade.turret_buggy" or produced.Type == "upgrade.turret_atwr" or produced.Type == "upgrade.turret_flak" or produced.Type == "upgrade.turret_doble_cannon" then
			if Turrets[produced.Owner.InternalName] == nil then
				Turrets[produced.Owner.InternalName] = produced
			else
				Turrets[produced.Owner.InternalName].Destroy()
				Turrets[produced.Owner.InternalName] = produced
			end
			
		end
		if produced.Owner.IsBot then
			local item = Utils.Any(AI_ItemChecks, function(item)
				return item == produced.Type
			end)
			if item then 
				local repairItems = Utils.Take(5,AI_ItemChecks)
				local repair = Utils.Any(repairItems, function(reparItem)
					return reparItem == produced.Type
				end)
				if repair then 
					produced.AddTag("item_repair")
				else 
					produced.AddTag("item_hide")
				end
				table.insert(ItemList[produced.Owner.InternalName],produced)
			end
		end
	end)
	local AIInitDelay = 1000
	-- remove crates script
	if MapCrates == false then
		local neutralPlayer = Player.GetPlayer("Neutral")
		local crates = neutralPlayer.GetActorsByTypes({"crate", "crate.speedup", "crate.jammer", "crate.repair"})
		for i, crate in pairs(crates) do
			crate.Destroy()
		end
	end

	--World  script behaviour
	if Intro == true then
		InitIntro()
		AIInitDelay = 3000
	else
		CameraActor.Destroy()
		StartingCountDown()
	end
	InitEarlyGame()
	-- if no second objective mid game 
	Trigger.AfterDelay(AIInitDelay, function()
		InitMidGame()
	end)
	Trigger.AfterDelay(AIInitDelay+3000, function()
		InitLateGame()
	end)
end

InitIntro = function()
	Media.StopMusic()
	Media.PlayMusic("tribute_to_evil")
	CameraActor.Move(RevealAreaStart.Location)
	if SecondaryObjectives == true then
		
		Trigger.AfterDelay(DateTime.Seconds(40), function()
			local wait = DateTime.Seconds(3)
			CameraActor.Stop()
			CameraActor.Teleport(Spice_location1.Location)
			CameraActor.Wait(wait)
			CameraActor.Teleport(Spice_location2.Location)
			CameraActor.Wait(wait)
			CameraActor.Teleport(Spice_location3.Location)
			CameraActor.Wait(wait)
			CameraActor.Teleport(Spice_location4.Location)
			CameraActor.Wait(wait)
			CameraActor.Teleport(FinishCells[2])
			CameraActor.Wait(wait)
			Media.DisplayMessage("Harvest 1000$ from each Spice field","Mentat", HSLColor.DarkRed)
			Utils.Do(Players, function(player)
				Beacon.New(player, Map.CenterOfCell(SpiceTriggers[1].Location))
			end)
			Trigger.AfterDelay(wait, function()
				Utils.Do(Players, function(player)
					Beacon.New(player, Map.CenterOfCell(SpiceTriggers[2].Location))
				end)
			end)
			
			Trigger.AfterDelay(wait+wait, function()
				Utils.Do(Players, function(player)
					Beacon.New(player, Map.CenterOfCell(SpiceTriggers[3].Location))
				end)
			end)
			Trigger.AfterDelay(wait+wait+wait, function()
				Utils.Do(Players, function(player)
					Beacon.New(player, Map.CenterOfCell(SpiceTriggers[4].Location))
				end)
			end)
			Trigger.AfterDelay(wait+wait+wait+wait, function()
				Media.DisplayMessage("Be first at finish line","Mentat", HSLColor.DarkRed)
				Utils.Do(Players, function(player)
					Beacon.New(player, Map.CenterOfCell(CPos.New(44,5)))
				end)
			end)
			Trigger.AfterDelay(wait+wait+wait+wait+wait, function()
				CameraActor.Destroy()
				Intro = false
				Camera.Position = Map.CenterOfCell(RevealAreaStart.Location)
				Trigger.AfterDelay(DateTime.Seconds(1), function()
					StartingCountDown()
				end)
			end)
		end)
	end
	-- info messages
	local time =  DateTime.Seconds(5)
	local pauseBetweenMessages = DateTime.Seconds(5)
	Trigger.AfterDelay(time, function()
		Media.DisplayMessage("Welcome in OpenRA Harvester Race","Mentat", HSLColor.DarkRed)
	end)
	time = time + pauseBetweenMessages
	Trigger.AfterDelay(time, function()
		Media.DisplayMessage("You can buy Items in main BUILDING TAB","Mentat", HSLColor.DarkRed)
	end)
	time = time + pauseBetweenMessages
	Trigger.AfterDelay(time, function()
		Media.DisplayMessage("Each Item can by used just once, and are in limited supply","Mentat", HSLColor.DarkRed)
	end)
	time = time + pauseBetweenMessages
	Trigger.AfterDelay(time, function()
		Media.DisplayMessage("You can upgrade your harvester in UPGRADE TAB","Mentat", HSLColor.DarkRed)
	end)
	time = time + pauseBetweenMessages
	Trigger.AfterDelay(time, function()
		Media.DisplayMessage("All upgrades are permanent, except for turrets","Mentat", HSLColor.DarkRed)
	end)
	time = time + pauseBetweenMessages
	Trigger.AfterDelay(time, function()
		Media.DisplayMessage("You can have just 1 active turret. If you buy new turret the previous will be removed","Mentat", HSLColor.DarkRed)
	end)
	time = time + pauseBetweenMessages
	Trigger.AfterDelay(time, function()
		Media.DisplayMessage("If you harvester dies, you get new one at the starting line or at Service depo in middle of the map","Mentat", HSLColor.DarkRed)
	end)
	if SecondaryObjectives == false then
		Trigger.AfterDelay(time+pauseBetweenMessages, function()
			CameraActor.Destroy()
			Intro = false
			Camera.Position = Map.CenterOfCell(RevealAreaStart.Location)
			Trigger.AfterDelay(DateTime.Seconds(1), function()
				StartingCountDown()
			end)
		end)
	end
end
StartingCountDown = function()
	Trigger.AfterDelay(DateTime.Seconds(1),function()
		Media.DisplayMessage("3","Mentat",HSLColor.DarkRed)
		Utils.Do(Players, function(player)
			Media.PlaySpeechNotification(player,"3")
		end)
	end)
	Trigger.AfterDelay(DateTime.Seconds(2),function()
		Media.DisplayMessage("2","Mentat",HSLColor.DarkRed)
		Utils.Do(Players, function(player)
			Media.PlaySpeechNotification(player,"2")
		end)
	end)
	Trigger.AfterDelay(DateTime.Seconds(3),function()
		Media.DisplayMessage("1","Mentat",HSLColor.DarkRed)
		Utils.Do(Players, function(player)
			Media.PlaySpeechNotification(player,"1")
		end)
	end)
	Trigger.AfterDelay(DateTime.Seconds(4),function()
		Timer = true
		Utils.Do(StartingWalls, function(wall)
			wall.Kill()
		end)
		Utils.Do(Players, function(player)
			if player.IsBot then
				InitHarvester_AI(player)
			end
		end)
	end)
end

CheckWinCondition= function(actor)
	local player = actor.Owner
	if SecondaryObjectives == false then
		return true
	end
	if actor.Type == "harvester.d2k" or actor.Type == "harvester.ra" or actor.Type == "harvester.cnc" then
		Media.Debug("entering finish line")
		for i = 1, #Objective_GatherSpice[player.InternalName] do
			if not player.IsObjectiveCompleted(Objective_GatherSpice[player.InternalName][i]) then
				return false
			end
		end
		return true
	end
	return false
	
end

HarvesterInsurance = function(harvesters,player)
	if harvesters ~= nil then
		Trigger.OnAllRemovedFromWorld(harvesters, function()
			local path
			if GameStage[2] == false then
				path = {Map.ClosestEdgeCell(player.HomeLocation), player.HomeLocation}
			else
				path = {Map.ClosestEdgeCell(player.HomeLocation), CPos.New(27,140)}
			end
			--Camera.Position(Map.CenterOfCell(player.HomeLocation))
			local units = nil
			if player.Faction == "atreides" then
				units = Reinforcements.ReinforceWithTransport(player, "carryall.invulnerable", {"harvester.d2k"}, path, {path[2],path[1]})
			elseif player.Faction == "harkonnen" then
				units = Reinforcements.ReinforceWithTransport(player, "carryall.invulnerable", {"harvester.ra"}, path, {path[2],path[1]})
			else
				units = Reinforcements.ReinforceWithTransport(player, "carryall.invulnerable", {"harvester.cnc"}, path, {path[2],path[1]})
			end
			Trigger.OnPassengerExited(units[1], function(carry, harvester)
				table.insert(Harvesters, harvester)
				Media.PlaySpeechNotification(player, "Reinforce")
				HarvesterInsurance({harvester},player)
				if player.IsBot then
					Trigger.AfterDelay(50, function()
						AI_ItemLogic(harvester)
						InitHarvester_AI(player)
					end)
				end
			end)
		end)
	end
end

local updatesResources = function(harvester)
	local player = harvester.Owner
	if ActualCash[player.InternalName] < player.Cash then
		local earning = player.Cash - ActualCash[player.InternalName]
		ActualCash[player.InternalName] = player.Cash
		for i, triggerName in pairs(SpiceTriggersNames) do
			if harvester.HasTag(triggerName) then
				HarvestedSpice[player.InternalName][i] = HarvestedSpice[player.InternalName][i] + earning
				if HarvestedSpice[player.InternalName][i] >= RequiredSpicePerTrigger[i] then
					if SecondaryObjectives == true then
						player.MarkCompletedObjective(Objective_GatherSpice[player.InternalName][i])
					else
						if player.IsBot then
							player.MarkCompletedObjective(Objective_GatherSpice[player.InternalName][i])
						end
					end
				end
			end
			
		end
	end
end

Tick = function()
	--everything execute's one's per 2 second, because preformance :( - will case lags
	if DateTime.GameTime % DateTime.Seconds(2) == 0 then
		local New_actors = ActorsAddedToWorld(Map.ActorsInWorld, ActorRegister)
		for _,actor in pairs(New_actors) do
			local actType = actor.Type --less expensive that act.Type
			-- Air support powers
			if actType == "waypoint.airtstrike_a" then
				CallAirstrike(actor, 1, actor.Owner, true, 0)
			end
			if actType == "waypoint.airtstrike_o" then
				CallAirstrike(actor, 3, actor.Owner, true, 0)
			end
			if actType == "waypoint.reinforce_h" or actType == "waypoint.reinforce_o" then
				DropAirReinforcements(actor)
			end
			if actType == "stealth_raider" then
				TrikeAttack(actor)
			end
		end
	end
	-- update cash if player spends money
	for j, player in pairs(Players) do
		if ActualCash[player.InternalName] > player.Cash then
			ActualCash[player.InternalName] = player.Cash
		end
	end

	-- update resources
	for i, harvester in pairs(Harvesters) do
		if not harvester.IsDead then
			if harvester.HasTag("in_field") then
				updatesResources(harvester)
			end
		else
			harvester = nil
		end
	end
	--intro camera
	if Intro == true then
		if not CameraActor.IsDead then
			Camera.Position = CameraActor.CenterPosition
		end
	end
	-- timer counter (does not match with ingame time)
	if Timer then
		if DateTime.GameTime % 45 == 0 then
			Seconds = Seconds + 1
		end
		if Seconds % 60 == 0 and Seconds ~= 0 then
			Minutes = Minutes + 1
			Seconds = 0
		end
	end
	-- user info
	if SecondaryObjectives == false then
		UserInterface.SetMissionText("\n                          TIMER:  "..tostring(Minutes)..":"..tostring(Seconds), HSLColor.Black)
	else
		UserInterface.SetMissionText("\nPlayer        Field-1      Field-2      Field-3      Field-4\n\n"..PlayersNames[1]..":    "..tostring(HarvestedSpice["Multi0"][1]).."/1000    "..tostring(HarvestedSpice["Multi0"][2]).."/1000    "..tostring(HarvestedSpice["Multi0"][3]).."/1000    "..tostring(HarvestedSpice["Multi0"][4]).."/1000    \n"..PlayersNames[2]..":    "..tostring(HarvestedSpice["Multi1"][1]).."/1000    "..tostring(HarvestedSpice["Multi1"][2]).."/1000    "..tostring(HarvestedSpice["Multi1"][3]).."/1000    "..tostring(HarvestedSpice["Multi1"][4]).."/1000    \n"..PlayersNames[3]..":    "..tostring(HarvestedSpice["Multi2"][1]).."/1000    "..tostring(HarvestedSpice["Multi2"][2]).."/1000    "..tostring(HarvestedSpice["Multi2"][3]).."/1000    "..tostring(HarvestedSpice["Multi2"][4]).."/1000    \n"..PlayersNames[4]..":    "..tostring(HarvestedSpice["Multi3"][1]).."/1000    "..tostring(HarvestedSpice["Multi3"][2]).."/1000    "..tostring(HarvestedSpice["Multi3"][3]).."/1000    "..tostring(HarvestedSpice["Multi3"][4]).."/1000    \n".."\n                          TIMER:  "..tostring(Minutes)..":"..tostring(Seconds), HSLColor.Black)
	end
	--Merch scrit trigger
	if SpawnInterval >= 0 then
		SpawnInterval = SpawnInterval - 1
	end
end

-- adds new actors into the list (used only in tick function)

function ActorsAddedToWorld(newlist,register)
	local new_actor_list={}
	for key,act in pairs(newlist)
	do
		if not register[tostring(act)]
		then
			register[tostring(act)]=true
			table.insert(new_actor_list,act)
		end
	end
	return new_actor_list
end


