990 lines
32 KiB
Lua
990 lines
32 KiB
Lua
BotPlayers = {}
|
|
IsAnyBotsHere = false
|
|
CurrentConyards = {}
|
|
CheckPlayerTechtree = false
|
|
ActivePlayers = {}
|
|
PlayersThatNotchoosedyet = 0 --Numbver of players that didnt choose any subfaction yet
|
|
FactionsMode = 0 -- default Faction mode: vannila
|
|
ExtendedStarport = 0
|
|
local CallAirstrike -- must be local fuction. otherwise it crash when multiple AirStrikes are called at ones.
|
|
ActorRegister={}
|
|
--Airstrike variables
|
|
SquadSpawnDelay = 0 -- Spawn delay betwwen every Aircraft. Use when Useoffest is false, else 0
|
|
SpawnWposOffsets = {-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.vet4","light_inf.vet3","light_inf.vet3","trooper.vet2","trooper.vet2"},
|
|
{"light_inf.vet3", "light_inf.vet3", "trooper.vet2", "trooper.vet2", "trooper.vet2"},
|
|
{"trooper.heavy","trooper.heavy","trooper.heavy", "officer", "trooper.heavy", "mpsardaukar", "mpsardaukar"},
|
|
{"trooper.heavy","trooper.heavy","trooper.heavy", "officer", "trooper.heavy", "fremen_fedaykin", "fremen_fedaykin"},
|
|
{"trooper.heavy","trooper.heavy","trooper.heavy", "officer", "trooper.heavy", "mortar_inf", "mortar_inf", "mortar_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", "frigate.atreides_cargo", "frigate.ordos_cargo"}
|
|
|
|
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"}
|
|
|
|
-- Experimental Starport variables - limited to 24 players
|
|
FrigateDeliveryDelay = 1500 --how much time to wait until frigate arrives
|
|
FrigateCapacity = 5 -- must be defined as n -1
|
|
|
|
-- 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 icon 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()
|
|
FrigateCapacity = tonumber(Map.LobbyOption("extendedStarport")) - 1
|
|
--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")
|
|
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}
|
|
|
|
FactionsMode = tonumber(Map.LobbyOption("fation_mode"))
|
|
if (FactionsMode == 0) then
|
|
--Media.DisplayMessage("you play with standart d2k mode", "Mentat", HSLColor.DarkRed)
|
|
end
|
|
if (FactionsMode == 1) then
|
|
Media.DisplayMessage("Sub-faction Mode - Choose between Major faction or one of its sub-factions", "Mentat", HSLColor.DarkRed)
|
|
end
|
|
if (FactionsMode == 2) then
|
|
Media.DisplayMessage("Merge Faction mode - chosen subfaction will be merged in to your Major faction","Mentat", HSLColor.DarkRed)
|
|
end
|
|
for i, player in pairs(Players)
|
|
do
|
|
-- freecarryall
|
|
if player.HasPrerequisites({"FreeCarry"})
|
|
then
|
|
addCarryToPlayer(player)
|
|
end
|
|
-- 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
|
|
player.GrantCondition("mergedsubfactions")
|
|
botsubfaction(player)
|
|
CheckPlayerTechtree = true
|
|
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
|
|
CHOAMDeliverCustomUnits(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.hunter" or actor=="dummy.bombardment_tank" 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" 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.MaxHealth, produced.Owner.InternalName)
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
function addCarryToPlayer(player)
|
|
local actors = player.GetActorsByTypes({"mcv", "construction_yard", "choosefaction"})
|
|
for i, actor in pairs(actors) do
|
|
if (actor.Type == "mcv") or (actor.Type == "construction_yard") or (actor.Type == "choosefaction") then
|
|
Reinforcements.Reinforce(player, { "carryall" }, { actor.Owner.HomeLocation}, 20)
|
|
return
|
|
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 == "waypoint.reinforce_or" or actType == "waypoint.reinforce_or" then
|
|
DropAirReinforcements(actor)
|
|
end
|
|
if actType == "waypoint.reinforce_at" or actType == "waypoint.reinforce_at" 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
|
|
|
|
end
|
|
|
|
-------------------------
|
|
--FactionsModes Scripts--
|
|
-------------------------
|
|
function addmcv(player)
|
|
local actors = player.GetActorsByType("choosefaction")
|
|
for pom, actor in pairs(actors) do
|
|
Actor.Create("mcv", true, { Owner = player, Facing = Angle.SouthWest, Location = actor.Location})
|
|
actor.Kill();
|
|
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.North, 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(Mergedsubfactions) do
|
|
if player.HasPrerequisites({prerequisite}) then
|
|
for pom, actor in pairs(actors) do
|
|
Actor.Create("mcv", true, { Owner = player, Facing = Angle.North, Location = actor.Location, Faction = player.Faction})
|
|
actor.Kill();
|
|
PlayersThatNotchoosedyet = PlayersThatNotchoosedyet - 1
|
|
--Media.DisplayMessage("Subfaction choosed Remaining"..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
|
|
Actor.Create("mcv", true, { Owner = player, Facing = Angle.North, Location = actor.Location, Faction = Utils.Random(faction)})
|
|
actor.Kill();
|
|
end
|
|
else
|
|
faction = player.Faction
|
|
for pom, actor in pairs(actors) do
|
|
Actor.Create("mcv", true, { Owner = player, Facing = Angle.North, Location = actor.Location, Faction = player.Faction})
|
|
Actor.Create( Utils.Random(Mergedsubfactions), true, { Owner = player, Facing = Angle.North, Location = actor.Location, Faction = player.Faction})
|
|
actor.Kill();
|
|
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, maxHP, internalName)
|
|
Trigger.AfterDelay(300, function()
|
|
if not actor.IsDead then
|
|
if maxHP * 0.4 > actor.Health then
|
|
if RepairPadList[internalName] ~= nil then
|
|
if not RepairPadList[internalName].IsDead then
|
|
local token = actor.GrantCondition("reject_control", 3000) -- 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)
|
|
CheckForRepair(actor, maxHP, internalName)
|
|
end
|
|
end)
|
|
end
|
|
else
|
|
CheckForRepair(actor, maxHP, internalName)
|
|
end
|
|
else
|
|
CheckForRepair(actor, maxHP, internalName)
|
|
end
|
|
end
|
|
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(400, 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
|
|
|
|
------------------------------------------------------------
|
|
--Experimental starport---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_choam",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 triger!!!)
|
|
Trigger.AfterDelay(10, function()
|
|
if pass.HasProperty("AttackMove") then
|
|
pass.AttackMove(rallypoint,1)
|
|
elseif pass.HasProperty("FindResources") then
|
|
pass.Move(rallypoint)
|
|
pass.FindResources()
|
|
else
|
|
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
|
|
|
|
-- 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_choam",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()
|
|
pass.Move(rallypoint)
|
|
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 = #SpawnWposOffsets + 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 + SpawnWposOffsets[arrayLenght - pon], wposDummyLocation.Y+SpawnWposOffsets[pon], 0)
|
|
else
|
|
newLocation =WPos.New( wposDummyLocation.X + SpawnWposOffsets[pon], wposDummyLocation.Y+SpawnWposOffsets[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 * SquadSpawnDelay
|
|
Trigger.AfterDelay(spamDelay, function()
|
|
local airUnit
|
|
local finalWposPosition
|
|
if angle == Angle.West or angle == Angle.East then
|
|
finalWposPosition = wposAirCraftSpawnLocation + WVec.New(SpawnWposOffsets[arrayLenght - i], SpawnWposOffsets[i], Actor.CruiseAltitude(actorName))
|
|
else
|
|
finalWposPosition = wposAirCraftSpawnLocation + WVec.New(SpawnWposOffsets[i], SpawnWposOffsets[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(SquadSpawnDelay + 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
|
|
elseif dummy.Type=="waypoint.reinforce_at" then squad=4
|
|
elseif dummy.Type=="waypoint.reinforce_or" then squad=5
|
|
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 |