BotPlayers = {}
IsAnyBotsHere = false
CurrentConyards = {}
CheckPlayerTechtree = false
ActivePlayers = {}
PlayersThatNotchoosedyet = 0 --Numbver of players that didnt choose any subfaction yet
FactionsMode = 0 -- default Faction mode: vannila
local CallAirstrike -- must be local fuction. otherwise it crash when multiple AirStrikes are called at ones.
ActorRegister={}
--Airstrike variables
SquadSpamDelay = 0 -- Spam delay betwwen every Aircraft. Use when Useoffest is false, else 0
SpamWposOffsets = {-1256, 0, 1256} -- offset for each aircraft
Useoffest = true -- if false Air sqad will span in line. !!! If false SquadSpamDelay must be least 5 !!!!
ReinforcementSquads = {
	{"light_inf","light_inf","light_inf","light_inf","light_inf"},
	{"mercenary","mercenary","mercenary","mercenary","mercenary"}
}
--different Squads for different Factons 1.Atreides, 2. Ordos 3. Harkonnen
AirSquads = {
	{"ornithopter_a","ornithopter_a", "ornithopter_a"},
	{"ornithopter_a","ornithopter_a"},
	{"ornithopter_o", "ornithopter_o", "ornithopter_o"}
}
DropActor = {"carryall.reinforce_with_sound", "carryall.reinforce_with_sound", "frigate.harkonnen_cargo"}
--sandstorm variables
SandStormSpawnpoints = {}
SandStormInterval = {100, 500} -- pick random number between interval in ticks
SandStormDuration = {200, 300}
SandStormEnabled = false
SandStormTimer = 10
SandStorm_Count = 1
--subfaction selections for bots
SubfactionPrerequisitesList = {}
SubfactionPrerequisitesList["atreides"] = "subfaction.atreides"
SubfactionPrerequisitesList["fremen"] = "subfaction.fremen"
SubfactionPrerequisitesList["harkonnen"] = "subfaction.harkonnen"
SubfactionPrerequisitesList["corrino"] = "subfaction.corrino"
SubfactionPrerequisitesList["ordos"] = "subfaction.ordos"
SubfactionPrerequisitesList["smuggler"] = "subfaction.smuggler"
SubfactionPrerequisitesList["mercenary"] = "subfaction.mercenary"
Mergedsubfactions = { "merged.fremen","merged.corrino","merged.smuggler","merged.mercenary","merged.atreides","merged.harkonnen","merged.ordos"}

-- Experimental Starport variables - limited to 24 players
FrigateDeliveryDelay = 1500 --how much time to wait until frigate arrives
FrigateCapacity = 5 -- must be set as: n -1
StarportUnits = {}
StarportUnits["Multi0"] = {}
StarportUnits["Multi1"] = {}
StarportUnits["Multi2"] = {}
StarportUnits["Multi3"] = {}
StarportUnits["Multi4"] = {}
StarportUnits["Multi5"] = {}
StarportUnits["Multi6"] = {}
StarportUnits["Multi7"] = {}
StarportUnits["Multi8"] = {}
StarportUnits["Multi9"] = {}
StarportUnits["Multi10"] = {}
StarportUnits["Multi11"] = {}
StarportUnits["Multi12"] = {}
StarportUnits["Multi13"] = {}
StarportUnits["Multi14"] = {}
StarportUnits["Multi15"] = {}
StarportUnits["Multi16"] = {}
StarportUnits["Multi17"] = {}
StarportUnits["Multi18"] = {}
StarportUnits["Multi19"] = {}
StarportUnits["Multi20"] = {}
StarportUnits["Multi21"] = {}
StarportUnits["Multi22"] = {}
StarportUnits["Multi23"] = {}
RevokeTokens = {} --isFull and notEmpty revoke tokens
RevokeTokens["Multi0"] = {}
RevokeTokens["Multi1"] = {}
RevokeTokens["Multi2"] = {}
RevokeTokens["Multi3"] = {}
RevokeTokens["Multi4"] = {}
RevokeTokens["Multi5"] = {}
RevokeTokens["Multi6"] = {}
RevokeTokens["Multi7"] = {}
RevokeTokens["Multi8"] = {}
RevokeTokens["Multi9"] = {}
RevokeTokens["Multi10"] = {}
RevokeTokens["Multi11"] = {}
RevokeTokens["Multi12"] = {}
RevokeTokens["Multi13"] = {}
RevokeTokens["Multi14"] = {}
RevokeTokens["Multi15"] = {}
RevokeTokens["Multi16"] = {}
RevokeTokens["Multi17"] = {}
RevokeTokens["Multi18"] = {}
RevokeTokens["Multi19"] = {}
RevokeTokens["Multi20"] = {}
RevokeTokens["Multi21"] = {}
RevokeTokens["Multi22"] = {}
RevokeTokens["Multi23"] = {}
DummyActors = {} -- used for icons overiddes
DummyActors["Multi0"] = {}
DummyActors["Multi1"] = {}
DummyActors["Multi2"] = {}
DummyActors["Multi3"] = {}
DummyActors["Multi4"] = {}
DummyActors["Multi5"] = {}
DummyActors["Multi6"] = {}
DummyActors["Multi7"] = {}
DummyActors["Multi8"] = {}
DummyActors["Multi9"] = {}
DummyActors["Multi10"] = {}
DummyActors["Multi11"] = {}
DummyActors["Multi12"] = {}
DummyActors["Multi13"] = {}
DummyActors["Multi14"] = {}
DummyActors["Multi15"] = {}
DummyActors["Multi16"] = {}
DummyActors["Multi17"] = {}
DummyActors["Multi18"] = {}
DummyActors["Multi19"] = {}
DummyActors["Multi20"] = {}
DummyActors["Multi21"] = {}
DummyActors["Multi22"] = {}
DummyActors["Multi23"] = {}
RepairPadList = {} -- repairpad owned by Bots

WorldLoaded = function()
	--get players
	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")
	mp9=Player.GetPlayer("Multi9")
	mp10=Player.GetPlayer("Multi10")
	mp11=Player.GetPlayer("Multi11")
	mp12=Player.GetPlayer("Multi12")
	mp13=Player.GetPlayer("Multi13")
	mp14=Player.GetPlayer("Multi14")
	mp15=Player.GetPlayer("Multi15")
	mp16=Player.GetPlayer("Multi16")
	mp17=Player.GetPlayer("Multi17")
	mp18=Player.GetPlayer("Multi18")
	mp19=Player.GetPlayer("Multi19")
	mp20=Player.GetPlayer("Multi20")
	mp21=Player.GetPlayer("Multi21")
	mp22=Player.GetPlayer("Multi22")
	mp23=Player.GetPlayer("Multi23")
	mp24=Player.GetPlayer("Multi24")
	mp25=Player.GetPlayer("Multi25")
	mp26=Player.GetPlayer("Multi26")
	mp27=Player.GetPlayer("Multi27")
	PlayerNeutral=Player.GetPlayer("Neutral")
	PlayerCreep = Player.GetPlayer("Creeps")
	Players = {mp0, mp1, mp2, mp3, mp4, mp5, mp6, mp7, mp8, mp9, mp10, mp11, mp12, mp13, mp14, mp15, mp16, mp17, mp18, mp19, mp20, mp21, mp22, mp23, mp24, mp25, mp26, mp27}
	--check faction mode
	FactionsMode  = tonumber(Map.LobbyOption("fation_mode"))
	if (FactionsMode == 0) then
	end
	if (FactionsMode == 1) then
	end
	if (FactionsMode == 2) then
	end
	-- sandstorm INIT, disabled due of no sandstorm assets
	if PlayerNeutral.HasPrerequisites({"sandstorms_enabled"}) then
		SandStormSpawnpoints = PlayerNeutral.GetActorsByType("spawnpoint.sandstorm")
		SandStormEnabled = true
		SandStormTimer = Utils.RandomInteger(SandStormInterval[1],SandStormInterval[2])
	end
	for i, player in pairs(Players) do
		-- factions  option check
		if (FactionsMode == 0)
		then
			player.GrantCondition("vanilla_factions_Only")
			addmcv(player)
		elseif (FactionsMode == 1) then
			CheckPlayerTechtree = true
			player.GrantCondition("subfactions")
			botsubfaction(player)
		elseif (FactionsMode == 2) then
			CheckPlayerTechtree = true
			player.GrantCondition("mergedsubfactions")
			botsubfaction(player)
		end
		--adding bots to table
		if player.IsBot then
			table.insert(BotPlayers, player)
		end
		if not player.IsNonCombatant then
			table.insert(ActivePlayers, player)
		end
	end
	-- worm delay
	local wormDelay = Map.LobbyOption("WormsSpawnDelay")
	if tonumber(wormDelay) > 0
	then
		local delay = tonumber(Map.LobbyOption("WormsSpawnDelay"))
		Trigger.AfterDelay(DateTime.Minutes(delay), function()
			--in teory any player can own wormspawner actor
			wormSpawnFunction(Player.GetPlayer("Creeps"))
			wormSpawnFunction(Player.GetPlayer("Neutral"))
			for i, player in pairs(Players)
			do
				wormSpawnFunction(player)
			end
		end)
	end
	-- Scripts for BOTS
	for i, bot in pairs(BotPlayers)
	do
		IsAnyBotsHere = true
		FindSaboteur(bot)
	end
	--Production trriggers - Starport, AI Engi, AI Repairing
	Trigger.OnAnyProduction( function(producer, produced, productionQueue)
			local actor = produced.Type
		if productionQueue == "Starport" then
			if actor == "purchase.starport" then
				CHOAMDelivery(produced.Owner)
			-- Mercenary support power
			elseif actor == "dummy_mercenary_spawnpoint" then
				CHOAMDCondractionDelivery(producer.Owner, ReinforcementSquads[4])
				produced.Destroy()
			elseif actor=="dummy.trike" or actor=="dummy.quad" or actor=="dummy.harvester" or actor=="dummy.mcv" or actor=="dummy.combat_tank_a" or actor=="dummy.combat_tank_o" or actor=="dummy.combat_tank_h" or actor=="dummy.siege_tank" or actor=="dummy.missile_tank" or actor=="dummy.carryall" or actor=="dummy.combat_tank_cheap"or actor=="dummy.combat_tank_corrino"or actor=="dummy.combat_tank_fremen"or actor=="dummy.combat_tank_mercenary" then
				ExperimentalStarport(producer.Owner, produced)
			end
		end
		if produced.Owner.IsBot then
			if actor == "engineer" then
				EnginnerLogic(produced)
			end
		if productionQueue == "Armor" or productionQueue== "Vehicle" then
			CheckForRepair(produced, produced.Owner.InternalName)
		end
		end
	end)
end


function wormSpawnFunction(owner)
    local actorsSpawner = owner.GetActorsByType("wormspawner")
  if actorsSpawner ~= nil
  then
    for i, spawner in pairs(actorsSpawner)
    do
      if spawner.AcceptsCondition("WormSpamDelayed")
      then
        spawner.GrantCondition("WormSpamDelayed")
      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)
			end
			if actType == "waypoint.airtstrike_o" then
				CallAirstrike(actor)
			end
			if actType == "waypoint.reinforce_h" or actType == "waypoint.reinforce_o" then
				DropAirReinforcements(actor)
			end
			if actType == "repair_pad.bot"then
				RepairPadList[actor.Owner.InternalName] = actor
			end
		end
	end
	-- Bot engi  protection scripts: executed ones per 2 sec.
	if IsAnyBotsHere then
		if DateTime.GameTime % DateTime.Seconds(2) == 0 then
			findConyards()
			engiProtection()
		end
	end
	-- check if player already choose subfaction
	if(CheckPlayerTechtree == true and PlayersThatNotchoosedyet > 0) then
	--	Media.DisplayMessage("cheking")
		techtreecheck()
	end
	-- sandstorm logic
	if(SandStormEnabled) then
		if SandStormTimer < 300 then
			UserInterface.SetMissionText("Warning Sand Storm Approaching: "..Utils.FormatTime(SandStormTimer))
		end
		if(SandStormTimer > 0) then
			SandStormTimer = SandStormTimer - 1
		else
			UserInterface.SetMissionText("")
			SandStormInit()
		end
	end
end

-------------------------
--FactionsModes Scripts--
-------------------------
function addmcv(player)
		local actors = player.GetActorsByType("choosefaction")
		for pom, actor in pairs(actors) do
			local mcv = Actor.Create("mcv", true, { Owner = player, Facing = Angle.South, Location = actor.Location})
			actor.Kill();
			if player.IsBot then
				mcv.Deploy()
			end
		end
end

function techtreecheck()
	for i, player in pairs(Players) do
		local actors = player.GetActorsByType("choosefaction")
		if FactionsMode == 1 then
			for faction, prerequisite in pairs(SubfactionPrerequisitesList) do
				if player.HasPrerequisites({prerequisite}) then
					for pom, actor in pairs(actors) do
						Actor.Create("mcv", true, { Owner = player, Facing = Angle.South, Location = actor.Location, Faction = faction})
						actor.Kill();
						PlayersThatNotchoosedyet = PlayersThatNotchoosedyet - 1
						--Media.DisplayMessage("Subfaction choosed", tostring(PlayersThatNotchoosedyet))
					end
				end
			end
		elseif FactionsMode == 2 then
			for faction, prerequisite in pairs(SubfactionPrerequisitesList) do
				if player.HasPrerequisites({prerequisite}) then
					for pom, actor in pairs(actors) do
						Actor.Create("mcv", true, { Owner = player, Facing = Angle.South, Location = actor.Location, Faction = faction})
						actor.Kill();
						PlayersThatNotchoosedyet = PlayersThatNotchoosedyet - 1
						--Media.DisplayMessage("Subfaction choosed", tostring(PlayersThatNotchoosedyet))
					end
				end
			end
		end
	end
end

function botsubfaction(player)
	if(not player.IsBot)
	then
		PlayersThatNotchoosedyet = PlayersThatNotchoosedyet + 1
		--Media.DisplayMessage("got the player",tostring(PlayersThatNotchoosedyet) )
	else
		local bluefactions = {"atreides", "fremen"}
		local redfactions = {"harkonnen", "corrino"}
		local greenfactions = {"ordos", "smuggler", "mercenary"}
		local faction = {}
		local actors = player.GetActorsByType("choosefaction")
		if (FactionsMode == 1) then
			if player.Faction == "atreides" then
				faction = bluefactions
			end
			if player.Faction == "harkonnen" then
				faction = redfactions
			end
			if player.Faction == "ordos" then
				faction = greenfactions
			end
			--Media.DisplayMessage("got the bot")
			for pom, actor in pairs(actors) do
				local mcv = Actor.Create("mcv", true, { Owner = player, Facing = Angle.South, Location = actor.Location, Faction = Utils.Random(faction)})
				actor.Kill();
				if player.IsBot then
					mcv.Deploy()
				end
			end
		else
			faction = player.Faction
			for pom, actor in pairs(actors) do
				local mcv = Actor.Create("mcv", true, { Owner = player, Facing = Angle.South, Location = actor.Location, Faction = player.Faction})
				Actor.Create( Utils.Random(Mergedsubfactions), true, { Owner = player, Facing = Angle.South, Location = actor.Location, Faction = player.Faction})
				actor.Kill();
				if player.IsBot then
					mcv.Deploy()
				end
			end
		end
	end
end

-----------------------------------
---AI protection against engineer--
-----------------------------------

function engiProtection()
  for i, conyard in pairs(CurrentConyards) do
    if conyard.IsDead then
        --Media.DisplayMessage("conyard dead")
    else
      actorsInRadius = Map.ActorsInCircle(conyard.CenterPosition, WDist.New(3036))
	  local owner = conyard.Owner
      for _, actor in pairs(actorsInRadius) do
        if actor.Type == "engineer" and not owner.IsAlliedWith(actor.Owner) then
          --Media.DisplayMessage(actor.Type)
		  conyard.Sell()
          --CurrentConyards[i] = nil
        end
      end
    end
  end
end

function findConyards()
  local newConyards = {}
 for i, bot in pairs(BotPlayers)
  do
    local conyards = bot.GetActorsByType("construction_yard")
    for _, conyard in pairs(conyards) do
      table.insert(newConyards, conyard)
    end
  end
  CurrentConyards = newConyards
end
-----------------------------
---AI scripts--engi/Saboteur/repairing
-----------------------------

function CheckForRepair(actor, internalName)
	if  actor.IsDead then
		return
	end

	if actor.Health < actor.MaxHealth * 0.4 and RepairPadList[internalName] ~= nil and not RepairPadList[internalName].IsDead then
		local token = actor.GrantCondition("reject_control", 2500) -- if unit stuck, expire after 3000 ticks
		--Media.Debug("send to repair_pad "..tostring(actor))
		actor.Stop()
		actor.Move(RepairPadList[internalName].Location + CVec.New(1,1), 2)
		actor.CallFunc(function ()
			if not actor.IsDead then
				actor.RevokeCondition(token)
				Trigger.AfterDelay(500, function()
					CheckForRepair(actor, internalName)
				end)
			end
		end)
		return
	end
	Trigger.AfterDelay(300, function()
		CheckForRepair(actor, internalName)
	end)
end

EnginnerLogic = function(enginner)
	if enginner.HasTag("close_target") == false then
		local closeActors = Map.ActorsInCircle(enginner.CenterPosition, WDist.FromCells(15))
		local targets = Utils.Where(closeActors, function(actor)
			return
				actor.HasProperty("Capture") and
				actor.Type ~= "wall" and
				actor.Type ~= "medium_gun_turret" and
				actor.Type ~= "large_gun_turret" and
				actor.Type ~= "engineer" and
				actor.Type ~= "silo" and not
				enginner.Owner.IsAlliedWith(actor.Owner)
		end)
		if targets[1] ~= nil then
			local token = enginner.GrantCondition("reject_control")
			enginner.Stop()
			local target = Utils.Random(targets)
			enginner.Capture(target)
			enginner.AddTag("close_target")
			--Media.Debug("redirection to close target"..tostring(enginner))
			enginner.CallFunc( function ()
				if enginner.IsDead == false then
					--Media.Debug("cancel, target destroyed"..tostring(enginner))
					enginner.RevokeCondition(token)
					enginner.RemoveTag("close_target")
				end
			end)
		end
	end
	Trigger.AfterDelay(500, function()
		if enginner.IsDead == false then
			EnginnerLogic(enginner)
		end
	end)
end

FindCloseTarget = function (saboteur, distance)
	local closeActors = Map.ActorsInCircle(saboteur.CenterPosition, WDist.FromCells(distance))
	return Utils.Where(closeActors, function(actor)
		return
			actor.HasProperty("Sell") and
			actor.Type ~= "wall" and
			actor.Type ~= "medium_gun_turret" and
			actor.Type ~= "large_gun_turret" and
			actor.Type ~= "silo" and not
			saboteur.Owner.IsAlliedWith(actor.Owner) and not
			actor.Owner.IsNonCombatant
	end)
end

FindAnyTarget = function (saboteur)
	local filteredPlayers = FilterPlayers(saboteur.Owner)
	if filteredPlayers[1] == nil then
		-- no suitable players for targeting
		return nil
	end
	local randomplayer = Utils.Random(filteredPlayers)
	if randomplayer ~= nil then
		local suitableActors = Utils.Where(randomplayer.GetActors(), function(actor)
			return
				actor.HasProperty("Sell") and
				actor.Type ~= "wall" and
				actor.Type ~= "medium_gun_turret" and
				actor.Type ~= "large_gun_turret" and
				actor.Type ~= "silo"
		end)
		if (next(suitableActors) ~= nil) then
			return suitableActors
		else
			-- case when random player is dead or have no buildings
			return nil
		end
	else
		-- case when no suitable players found
		return nil
	end
end

FindSaboteur = function(bot)
	local saboteurs = bot.GetActorsByType("saboteur")
	for i, saboteur in pairs(saboteurs) do
		if  saboteur.HasTag("close_target") == false and saboteur.HasTag("random_target") == false then
			local targets = FindCloseTarget(saboteur, 15)
			if (next(targets) ~= nil) then
				saboteur.AddTag("close_target")
				SendSaboteur(saboteur, targets)
			else
				targets = FindAnyTarget(saboteur)
				if targets ~= nil then
					saboteur.AddTag("random_target")
					SendSaboteur(saboteur, targets)
				end
			end
		elseif not saboteur.HasTag("close_target") and saboteur.HasTag("random_target") then
			local targets = FindCloseTarget(saboteur, 15)
			if (next(targets) ~= nil) then
				saboteur.AddTag("close_target")
				SendSaboteur(saboteur, targets)
			end
		end
	end
	Trigger.AfterDelay(500, function ()
		FindSaboteur(bot) end)
end

SendSaboteur = function(saboteur, targets)
	if targets == nil then
		--Media.Debug("no targets for", saboteur.Owner.Name)
		saboteur.RemoveTag("close_target")
		saboteur.RemoveTag("random_target")
		return
	end
	saboteur.Stop()
	local target = Utils.Random(targets)
	saboteur.Demolish(target)
-- 'target' was removed from the world in the meantime
	saboteur.CallFunc(function()
		saboteur.RemoveTag("close_target")
		saboteur.RemoveTag("random_target")
	end)
end

function FilterPlayers(forPlayer)
	local filteredPlayers = ActivePlayers
	for i, player in pairs(filteredPlayers) do
		if forPlayer.IsAlliedWith(player) or forPlayer.InternalName == player.InternalName then
			table.remove(filteredPlayers, i)
		end
	end
	return filteredPlayers
end

---------------
---AI Crush logic
-----------------

function AutoCrusher (unit, bot)
	if unit.IsDead then
		return
	end
	if unit.Health < unit.MaxHealth *0.25 and RepairPadList[unit.Owner.InternalName] ~= nil then
		CheckForRepair(unit, unit.Owner.InternalName)
		Trigger.AfterDelay(500, function ()
			AutoCrusher(unit, bot)
		end)
		return
	end
	local actors = Map.ActorsInCircle(unit.CenterPosition, WDist.FromCells(4), function (a)
		return
		a.IsDead == false and
		a.IsInWorld == true and
		a.Owner.IsAlliedWith(bot) == false
	end)
	local targets = Utils.Where(actors, function(a)
		return
		a.Type == "light_inf" or
		a.Type == "trooper" or
		a.Type == "grenadier" or
		a.Type == "mpsardaukar" or
		a.Type == "mortar_inf" or
		a.Type == "assassin" or
		a.Type == "fremen"
	end)
	if targets[1] ~= nil then
		unit.GrantCondition("auto_crush", 150)
		unit.Stop()
		unit.Move(Utils.Random(targets).Location)
		Trigger.AfterDelay(55, function ()
			AutoCrusher(unit, bot)
		end)
	else
		Trigger.AfterDelay(250, function ()
			AutoCrusher(unit, bot)
		end)
	end
end


------------------------------------
--Original D2k Starport behaviour---
------------------------------------

function ExperimentalStarport(player, dummy)
	local playerInternalName = player.InternalName
	local unit = dummy.Type
	local cutDummyFromString = unit:sub(7, #unit)
	--  Icons number Overlays
	table.insert(DummyActors[playerInternalName], dummy)
	if #DummyActors[playerInternalName] > FrigateCapacity + 1 then
		--Media.Debug("Index above Fridate capacity, unit rejected "..playerInternalName)
		return
	end
	dummy.GrantCondition("order_"..tostring(#DummyActors[playerInternalName]))
	-- remove not needed number in icon
	for i, dummyActor in pairs(DummyActors[playerInternalName]) do
		if dummyActor.Type == unit and i < #DummyActors[playerInternalName] then
			dummyActor.RevokeCondition(1)
		end
	end
	if not player.HasPrerequisites({"frigate.not_empty"}) then
		local token = player.GrantCondition("frigate_not_empty")
		table.insert(RevokeTokens[playerInternalName], token)
	end
	-- add unit to the Shopping list
	if StarportUnits[playerInternalName] == nil then
		table.insert(StarportUnits[playerInternalName], cutDummyFromString)
	elseif #StarportUnits[playerInternalName] < FrigateCapacity then
		table.insert(StarportUnits[playerInternalName], cutDummyFromString)
	elseif #StarportUnits[playerInternalName] == FrigateCapacity then
		table.insert(StarportUnits[playerInternalName], cutDummyFromString)
		local token = player.GrantCondition("frigate_full")
		table.insert(RevokeTokens[playerInternalName], token)
		--for i, name in pairs(StarportUnits[playerInternalName]) do
		--	Media.Debug(name.." shoping list")
		--end
	else
		--Media.Debug("Frigate full - unit rejected"..player.Name)
		local token = player.GrantCondition("frigate_full")
		table.insert(RevokeTokens[playerInternalName], token)
	end
end

function SelectStarport(player)
	local chosenStarport
	for i, starport in pairs(player.GetActorsByTypes({"starport","merged.starport","merged.starport_smuggler"})) do
		if not starport.IsDead then
			if starport.IsPrimaryBuilding then
				return starport
			else
				chosenStarport = starport
			end
		end
	end
	return  chosenStarport
end

function CHOAMDelivery(player)
	DeliveryNotifications(player)
	Trigger.AfterDelay(FrigateDeliveryDelay, function ()
		--Media.Debug("starting delivery process")
		local starport = SelectStarport(player)
		if starport == nil then
			--Media.Debug("Error, cant find any starport. Starport reset"..player.Name)
			ResetStartport(player)
			return
		end
		local rallypoint = starport.RallyPoint
		local path = {Map.ClosestEdgeCell(starport.Location),starport.Location + CVec.New(1,1)}
		local airUnits = FilterAirUnits(player.InternalName)
		--wait for Choam to come
		local units = Reinforcements.ReinforceWithTransport(player,"frigate",StarportUnits[player.InternalName], path,{path[2], path[1]})
		local frigate = units[1]
		Reinforcements.Reinforce(player, airUnits,path)
		-- when killed or deliver finish remove Frigade and reset Starport settings
		Trigger.OnRemovedFromWorld(starport, function()
			--Media.Debug("Starport removed redirecting order "..frigate.Type)
			local starport_new = SelectStarport(player)
			if starport_new == nil then
				if not frigate.IsDead then
					--Media.Debug("no more starports, canceling order"..player.Name)
					frigate.Stop()
					frigate.Move(path[1])
				end
			else
			--	Media.Debug("Starport removed redirecting order "..frigate.Type)
				if not frigate.IsDead then
					frigate.Stop()
					rallypoint = starport_new.RallyPoint
					frigate.Move(starport_new.Location + CVec.New(1,1))
					frigate.UnloadPassengers()
				else
					--Media.Debug("Cant redirect, frigate is dead"..player.Name)
				end
			end
		end)
		-- when killed or deliver finish. Remove Frigade and reset Starport settings
		Trigger.OnRemovedFromWorld(frigate, function()
			ResetStartport(player)
			if not starport.IsDead then
				Trigger.ClearAll(starport)
			end
		end)
			--in case frigate stuck
		Trigger.OnIdle(frigate, function()
			frigate.UnloadPassengers()
		end)
		Trigger.OnPassengerExited(frigate,
			function(carry, pass)
				Media.PlaySpeechNotification(carry.Owner,"Reinforce")
				if pass ~= nil then
					-- triggered when actor is in the world (dont use OnAddedToWorld trigger!!!)
					Trigger.AfterDelay(10, function()
						if pass.HasProperty("AttackMove") then
							pass.AttackMove(rallypoint,1)
						elseif pass.HasProperty("FindResources") then
							pass.Move(rallypoint)
							pass.FindResources()
						elseif pass.HasProperty("Move") then
							pass.Move(rallypoint)
						end
					end)
				end
			end)
	end)
end

function ResetStartport(player)
	local playerInternalName = player.InternalName
	if RevokeTokens[playerInternalName][1] ~= nil then
		player.RevokeCondition(RevokeTokens[player.InternalName][1])
		RevokeTokens[playerInternalName][1] = nil
	end
	if RevokeTokens[playerInternalName][2] ~= nil then
		player.RevokeCondition(RevokeTokens[player.InternalName][2])
		RevokeTokens[playerInternalName][1] = nil
	end
	for i, dummy in pairs(DummyActors[playerInternalName]) do
		--edge case check. when player surrender while delivery is in progress
		if not dummy.IsDead then
			dummy.Destroy()
		end
	end
	DummyActors[playerInternalName] = {}
	RevokeTokens[playerInternalName] = {}
	StarportUnits[playerInternalName] = {}
	local purchaseActors = player.GetActorsByType("purchase.starport")
	if purchaseActors[1] ~= nil then
		purchaseActors[1].Destroy()
	end
end

function FilterAirUnits(playerInternalName)
	local airUnits = {}
	for i,unit in pairs(StarportUnits[playerInternalName]) do
		if unit == "carryall" then
			--Media.Debug("removing carryall")
			StarportUnits[playerInternalName][i] = nil
			table.insert(airUnits, "carryall")
		end
	end
	return airUnits
end

function DeliveryNotifications(player)
	Media.PlaySpeechNotification(player,"OrderPlaced")
	local time = 0
	local timeBetweenNotifications = FrigateDeliveryDelay / 5
	time = time + timeBetweenNotifications
	Trigger.AfterDelay(time -15, function()
		Media.PlaySpeechNotification(player,"TMinusFive")
	end)
	time = time + timeBetweenNotifications
	Trigger.AfterDelay(time, function()
		Media.PlaySpeechNotification(player,"TMinusFour")
	end)
	time = time + timeBetweenNotifications
	Trigger.AfterDelay(time, function()
		Media.PlaySpeechNotification(player,"TMinusThree")
	end)
	time = time + timeBetweenNotifications
	Trigger.AfterDelay(time, function()
		Media.PlaySpeechNotification(player,"TMinusTwo")
	end)
	time = time + timeBetweenNotifications
	Trigger.AfterDelay(time, function()
		Media.PlaySpeechNotification(player,"TMinusOne")
	end)

end


-----------------------------
---DeliveryUnits inside transport, inside AirUnit
-----------------------------
function CHOAMDCondractionDelivery(player, squad)
	local starport = SelectStarport(player)
	if starport == nil then
		ResetStartport(player)
		return
	end
	local rallypoint = starport.RallyPoint
	local path = {Map.ClosestEdgeCell(starport.Location),starport.Location + CVec.New(1,1)}
	local locationWpos = Map.CenterOfCell(path[1])
	local deliveryActor = Actor.Create("frigate", true, {Owner = player, Facing = Angle.North, Location = path[1], CenterPosition = locationWpos + WVec.New(0,0,Actor.CruiseAltitude("frigate")) ,Faction = player.Faction})
	local insideTransport = Actor.Create("crawler_delivered", false, {Owner = player, Facing = Angle.North, Location = path[1], Faction = player.Faction})
	for _, unit in pairs (squad) do
		local actor = Actor.Create(unit, false, {Owner = player, Facing = Angle.North, Location = path[1], Faction = player.Faction})
		insideTransport.LoadPassenger(actor);
	end
	deliveryActor.LoadPassenger(insideTransport)
	deliveryActor.Move(path[2])
	deliveryActor.UnloadPassengers()
	Trigger.OnPassengerExited(deliveryActor,
		function(transport, pass)
			Media.PlaySpeechNotification(transport.Owner,"Reinforce")
			if pass ~= nil and pass.HasProperty("Move") then
				pass.Move(rallypoint);
			end
			if not transport.HasPassengers then
				transport.Move(path[1])
				transport.Destroy()
			end
		end)
end
--------------------------------
-- Reinforcements via Starport--
--------------------------------

function CHOAMDeliverCustomUnits(player, squad)
	local starport = SelectStarport(player)
	--Media.Debug("Starting custom units delivery "..player.InternalName)
	if starport == nil then
		--Media.Debug("Error, cant find any starport. Starport reset"..player.Name)
		ResetStartport(player)
		return
	end
	local rallypoint = starport.RallyPoint
	local path = {Map.ClosestEdgeCell(starport.Location),starport.Location + CVec.New(1,1)}
	local airUnits = FilterAirUnits(player.InternalName)
	--wait for Choam to come
	local units = Reinforcements.ReinforceWithTransport(player,"frigate",squad, path,{path[2], path[1]})
	local frigate = units[1]
	Reinforcements.Reinforce(player, airUnits,path)
	-- when killed or deliver finish remove Frigade and reset Starport settings
	Trigger.OnRemovedFromWorld(starport, function()
		--Media.Debug("Starport removed redirecting order "..frigate.Type)
		local starport_new = SelectStarport(player)
		if starport_new == nil then
			if not frigate.IsDead then
				--Media.Debug("no more starports canceling order"..player.Name)
				frigate.Stop()
				frigate.Move(path[1])
			end
		else
		--	Media.Debug("Starport removed redirecting order "..frigate.Type)
			if not frigate.IsDead then
				frigate.Stop()
				frigate.Move(starport_new.Location + CVec.New(1,1))
				frigate.UnloadPassengers()
			end
		end
	end)
		--in case frigate stuck
	Trigger.OnIdle(frigate, function()
		frigate.UnloadPassengers()
	end)
	Trigger.OnPassengerExited(frigate,
		function(carry, pass)
				if pass ~= nil then
					Trigger.AfterDelay(10, function()
						if pass.HasProperty("Move") then
							pass.Move(rallypoint)
						end
					end)
				end
			end)
end
-------------------------------
-- Multi pass Airstrikes  -
--warring use only as local function (global function can crash)
--------------------------------
CallAirstrike = function(dummy)
	local dummyPosition = dummy.Location
	local spawnEdgeCell = Map.ClosestEdgeCell(dummyPosition)
	local angle = CalculateBestAngle(spawnEdgeCell)
	local wposDummyLocation = Map.CenterOfCell(dummyPosition)
	Beacon.New(dummy.Owner,wposDummyLocation, 250)
	local wposAirCraftSpawnLocation = Map.CenterOfCell(spawnEdgeCell)
	local airUnits = GiveAirSquad(dummy.Type)
	local squadRefference = {}
	local dummyRefference = {}
	local pon = 1
	local arrayLenght = #SpamWposOffsets + 1
	-- calculate WPos offset for every aircraft
	while pon <= #airUnits do
		local newLocation
		if Useoffest == true then
			if angle == Angle.West or angle == Angle.East then
				newLocation =WPos.New( wposDummyLocation.X + SpamWposOffsets[arrayLenght - pon], wposDummyLocation.Y+SpamWposOffsets[pon], 0)
			else
				newLocation =WPos.New( wposDummyLocation.X + SpamWposOffsets[pon], wposDummyLocation.Y+SpamWposOffsets[pon], 0)
			end
		else
			newLocation = wposDummyLocation
		end
		dummyRefference[pon] = Actor.Create("dummy.generic",true, {Owner = dummy.Owner, CenterPosition = newLocation,Location })
		pon = pon + 1
	end
	--Media.Debug("calling Airstrike"..tostring(spawnPosition.X).."  ,   "..tostring(spawnPosition.Y))
	-- calculate squadron
	for i, actorName in pairs(airUnits) do
		local spamDelay = i * SquadSpamDelay
		Trigger.AfterDelay(spamDelay, function()
			local airUnit
			local finalWposPosition
			if angle == Angle.West or angle == Angle.East then
				finalWposPosition = wposAirCraftSpawnLocation + WVec.New(SpamWposOffsets[arrayLenght - i], SpamWposOffsets[i], Actor.CruiseAltitude(actorName))
			else
				finalWposPosition = wposAirCraftSpawnLocation + WVec.New(SpamWposOffsets[i], SpamWposOffsets[i], Actor.CruiseAltitude(actorName))
			end
			if Useoffest == true then
				airUnit = Actor.Create(actorName ,true, {Owner = dummy.Owner ,Facing = angle,  CenterPosition = finalWposPosition })
				airUnit.Attack(dummyRefference[i], true, true)
			else
				airUnit = Actor.Create(actorName ,true, {Owner = dummy.Owner, Location = spawnEdgeCell, Facing = angle})
				airUnit.Attack(dummyRefference[i], true, true)
			end
			table.insert(squadRefference, airUnit)
			LeaveOnEmpty(airUnit)
		end)
	end
	Trigger.AfterDelay(SquadSpamDelay + 1 * 5, function()
		Trigger.OnAllRemovedFromWorld(squadRefference, function ()
			-- edge case check. if player surrender while airstrike is in progress -> crash
			if not dummy.IsDead then
				dummy.Destroy()
				for i, dummy_generic in pairs(dummyRefference) do
					dummy_generic.Kill()
				end
			end
		end)
	end)
end

function LeaveOnEmpty(unit)
	if not unit.IsDead then
		if unit.AmmoCount("primary") < 1 then
			unit.Stop()
			local position = Map.ClosestEdgeCell(unit.Location)
			unit.Move(position)
			unit.Destroy()
		else
			Trigger.AfterDelay(30, function()
				LeaveOnEmpty(unit)
			end)
		end
	end
end

function CalculateBestAngle(position)
	if position.X <= 2 then
		return Angle.East
	elseif position.Y <= 2 then
		return Angle.South
	elseif position.X < position.Y then
		return Angle.North
	else
		return Angle.West
	end
end


function GiveAirSquad(faction)
	if faction == "waypoint.airtstrike_a" then
		--Media.DisplayMessage("atreides squad")
		return AirSquads[1]
	elseif faction == "waypoint.airtstrike_h"then
		return AirSquads[2]
	elseif faction == "waypoint.airtstrike_o"then
		return AirSquads[3]
	end
end
----------------------
---Air Reinforcements-
----------------------
function DropAirReinforcements(dummy)
	local squad
	if dummy.Type=="waypoint.reinforce_a" then squad=1
	elseif dummy.Type=="waypoint.reinforce_o" then squad=2
	elseif dummy.Type=="waypoint.reinforce_h" then squad=3
	end
	--Carryall reinforcements
	local spawnCell = GetSpawnPoint(dummy)
	local path = {spawnCell,dummy.Location}
	local exitPath = {dummy.Location, Map.ClosestEdgeCell(dummy.Location) }
	--Media.Debug("reinforcements on the way"..dummy.Owner.InternalName)
	Beacon.New(dummy.Owner,Map.CenterOfCell(dummy.Location))
	local units = Reinforcements.ReinforceWithTransport(dummy.Owner,DropActor[squad] , ReinforcementSquads[squad], path, exitPath, nil, nil, 5)
	local carryall = units[1]
	Trigger.OnIdle(carryall, function()
		carryall.UnloadPassengers()

	end)
	Trigger.OnPassengerExited(carryall,
		function(carry, pass)
			if not carry.HasPassengers
			then
				carry.Stop()
				carry.Move(path[1])
				carry.Destroy()
			end
		end
	)
end

function GetSpawnPoint(dummy)
	local buildings = dummy.Owner.GetActorsByType("high_tech_factory")
	for i, building in pairs(buildings) do
		if building.IsPrimaryBuilding then
			return Map.ClosestEdgeCell(building.Location)
		end
	end
	if (buildings[1].Location ~= nil) then
		return Map.ClosestEdgeCell(buildings[1].Location)
	else
		return dummy.Owner.HomeLocation
	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

---------------------
--SandStorms (WIP)---
---------------------
function SandStormInit()
	local duration = Utils.RandomInteger(SandStormDuration[1], SandStormDuration[2])
	SandStormEnabled = false
	-- use spawnpoints if there are any
	if SandStormSpawnpoints[1] ~= nil then
		for i = 0, SandStorm_Count, 1 do
			local spawnpoint = Utils.Random(SandStormSpawnpoints)
			Actor.Create("sandstorm", true, {Owner = PlayerCreep, Location = spawnpoint.Location})

		end
	else
		local i = 0
		while i < SandStorm_Count do
			local randomCell = Map.RandomCell()
			if Map.TerrainType(randomCell) == "Sand" or Map.TerrainType(randomCell) == "SpiceSand" then
				i = i + 1
				Actor.Create("sandstorm", true, {Owner = PlayerCreep, Location = randomCell})
			end
		end
	end
	-- remove sandsorm actors from the map
	Trigger.AfterDelay(Utils.RandomInteger(SandStormDuration[1], SandStormDuration[2]), function()
		local storms = PlayerCreep.GetActorsByType("sandstorm")
		Utils.Do(storms, function(storm)
			storm.Kill()
		end)
		SandStormEnabled = true
		SandStormTimer = Utils.RandomInteger(SandStormInterval[1],SandStormInterval[2])
	end)
end
