------ AIFunctions[playername][actorgroupname][status] { Function, ArgumentTable } -- -- ArgumentTable: ------ Actors (auto: AIActorGroup[playername][actorgroupname]) ------ PlayerName (auto: playername) ------ ActorGroupName (auto: actorgroupname) ------ StatusOnSuccess (auto: status + 1) ------ Any other function defined by user in ArgumentTable function UpdateSurvivors (ArgumentTable) -- returns ArgumentTable if type(ArgumentTable) ~= "table" then return { } end local SurvivingActors = { } Utils.Do(ArgumentTable.Actors, function(unit) if unit.IsDead then return end if not unit.IsInWorld then return end table.insert(SurvivingActors, unit) end) ArgumentTable.Actors = SurvivingActors AIActorGroups[ArgumentTable.PlayerName][ArgumentTable.ActorGroupName] = SurvivingActors return ArgumentTable end function ApplyStatusOnSuccess (ArgumentTable) ArgumentTable = UpdateSurvivors(ArgumentTable) Utils.Do(ArgumentTable.Actors, function(unit) Trigger.ClearAll(unit) end) AIGroupStatus[ArgumentTable.PlayerName][ArgumentTable.ActorGroupName] = ArgumentTable.StatusOnSuccess end function ApplyStatusOnDefeat (ArgumentTable) ArgumentTable = UpdateSurvivors(ArgumentTable) Utils.Do(ArgumentTable.Actors, function(unit) Trigger.ClearAll(unit) end) AIGroupStatus[ArgumentTable.PlayerName][ArgumentTable.ActorGroupName] = -3 -- Disband end function ScriptFn_HuntAttackMovementPath (ArgumentTable) -- ArgumentTable parameters required: CPos[] Path, int CloseEnough = 3, int Delay = 25 (in ticks), bool Cyclic = false -- Termination function! There will be no ApplyStatusOnSuccess if bdebug then UserInterface.SetMissionText("Script Function HuntAttackMovementPath() called by ".. ArgumentTable.PlayerName .."." .. ArgumentTable.ActorGroupName .." at "..missiontick, MissionPlayer.Color) end ArgumentTable = UpdateSurvivors(ArgumentTable) if ArgumentTable.Path == nil then return end if ArgumentTable.CloseEnough == nil then ArgumentTable.CloseEnough = 3 end if ArgumentTable.Delay == nil then ArgumentTable.Delay = 25 end if ArgumentTable.Cyclic == nil then ArgumentTable.Cyclic = true end if #ArgumentTable.Actors == 0 then ApplyStatusOnDefeat(ArgumentTable) return end Trigger.OnAllKilled(ArgumentTable.Actors, function() ApplyStatusOnDefeat(ArgumentTable) end) Utils.Do(ArgumentTable.Actors, function(unit) if unit.IsDead then return end if not unit.IsInWorld then return end -- Air units have different logic due to limitations from having different Traits. if unit.Type == "yak" or unit.Type == "mig" or unit.Type == "hind" or unit.Type == "heli" or unit.Type == "tran" then AircraftAttack(unit, ArgumentTable.Path, ArgumentTable.CloseEnough, ArgumentTable.Delay, ArgumentTable.Cyclic) else GroundAttack(unit, ArgumentTable.Path, ArgumentTable.CloseEnough, ArgumentTable.Delay, ArgumentTable.Cyclic) end end) end function ScriptFn_AttackMovementPath (ArgumentTable) -- ArgumentTable parameters required: CPos[] Path, int CloseEnough = 3, int Delay = 25 (in ticks), bool Cyclic = false -- Not applicable for aircraft! if bdebug then UserInterface.SetMissionText("Script Function AttackMovementPath() called by ".. ArgumentTable.PlayerName .."." .. ArgumentTable.ActorGroupName .." at "..missiontick, MissionPlayer.Color) end -- if aircraft detected, use ScriptFn_HuntAttackMovementPath instead local hasAircraft = false Utils.Do(ArgumentTable.Actors, function(unit) if unit.Type == "yak" or unit.Type == "mig" or unit.Type == "hind" or unit.Type == "heli" or unit.Type == "tran" then hasAircraft = true end end) if hasAircraft then ScriptFn_HuntAttackMovementPath (ArgumentTable) else ArgumentTable = UpdateSurvivors(ArgumentTable) if ArgumentTable.Path == nil then return end if ArgumentTable.CloseEnough == nil then ArgumentTable.CloseEnough = 3 end if ArgumentTable.Delay == nil then ArgumentTable.Delay = 25 end if ArgumentTable.Cyclic == nil then ArgumentTable.Cyclic = false end if #ArgumentTable.Actors == 0 then ApplyStatusOnDefeat(ArgumentTable) return end Trigger.OnAllKilled(ArgumentTable.Actors, function() ApplyStatusOnDefeat(ArgumentTable) end) Utils.Do(ArgumentTable.Actors, function(unit) if unit.IsDead then return end if not unit.IsInWorld then return end local i = 1 local stop = false local exwaypoints = { } -- for expanded waypoints for j = 1,#ArgumentTable.Path do if ArgumentTable.CloseEnough > 1 then for cl = 1, ArgumentTable.CloseEnough do if cl > 1 then -- ignore first expansion exwaypoints[j] = Utils.ExpandFootprint({ArgumentTable.Path[j]}, false) end end else exwaypoints[j] = { ArgumentTable.Path[j] } end end if not unit.IsDead then Trigger.OnIdle(unit, function() if stop then return end local reachdest = false Utils.Do(exwaypoints[i], function(wp) if unit.Location == wp then reachdest = true end end) if reachdest then local bool = false if ArgumentTable.WaitForGroup then bool = Utils.All(ArgumentTable.Actors, function(actor) if actor.IsDead then return true end return actor.IsIdle end) else bool = unit.IsDead or unit.IsIdle end if bool then stop = true i = i + 1 if i > #ArgumentTable.Path then if ArgumentTable.Cyclic then i = 1 else ApplyStatusOnSuccess(ArgumentTable) end end Trigger.AfterDelay(ArgumentTable.Delay, function() stop = false end) end else unit.AttackMove(ArgumentTable.Path[i], ArgumentTable.CloseEnough) end end) end end) end end function ScriptFn_MovementPath (ArgumentTable) -- ArgumentTable parameters required: CPos[] Path, int CloseEnough = 3, int Delay = 25 (in ticks), bool Cyclic = false -- Not applicable for aircraft! if bdebug then UserInterface.SetMissionText("Script Function MovementPath() called by ".. ArgumentTable.PlayerName .."." .. ArgumentTable.ActorGroupName .." at "..missiontick, MissionPlayer.Color) end ArgumentTable = UpdateSurvivors(ArgumentTable) if ArgumentTable.Path == nil then return end if ArgumentTable.CloseEnough == nil then ArgumentTable.CloseEnough = 3 end if ArgumentTable.Delay == nil then ArgumentTable.Delay = 25 end if ArgumentTable.Cyclic == nil then ArgumentTable.Cyclic = false end -- if aircraft detected, use ScriptFn_HuntAttackMovementPath instead local hasAircraft = false Utils.Do(ArgumentTable.Actors, function(unit) if unit.Type == "yak" or unit.Type == "mig" or unit.Type == "hind" or unit.Type == "heli" or unit.Type == "tran" then hasAircraft = true end end) if hasAircraft then ScriptFn_HuntAttackMovementPath (ArgumentTable) else if #ArgumentTable.Actors == 0 then ApplyStatusOnDefeat(ArgumentTable) return end Trigger.OnAllKilled(ArgumentTable.Actors, function() ApplyStatusOnDefeat(ArgumentTable) end) Utils.Do(ArgumentTable.Actors, function(unit) if unit.IsDead then return end if not unit.IsInWorld then return end local i = 1 local stop = false local exwaypoints = { } -- for expanded waypoints for j = 1,#ArgumentTable.Path do if ArgumentTable.CloseEnough > 1 then for cl = 1, ArgumentTable.CloseEnough do if cl > 1 then -- ignore first expansion exwaypoints[j] = Utils.ExpandFootprint({ArgumentTable.Path[j]}, false) end end else exwaypoints[j] = { ArgumentTable.Path[j] } end end if not unit.IsDead then Trigger.OnIdle(unit, function() if stop then return end local reachdest = false Utils.Do(exwaypoints[i], function(wp) if unit.Location == wp then reachdest = true end end) if reachdest then local bool = false if ArgumentTable.WaitForGroup then bool = Utils.All(ArgumentTable.Actors, function(actor) if actor.IsDead then return true end return actor.IsIdle end) else bool = unit.IsDead or unit.IsIdle end if bool then stop = true i = i + 1 if i > #ArgumentTable.Path then if ArgumentTable.Cyclic then i = 1 else ApplyStatusOnSuccess(ArgumentTable) end end Trigger.AfterDelay(ArgumentTable.Delay, function() stop = false end) end else unit.Move(ArgumentTable.Path[i], ArgumentTable.CloseEnough) end end) end end) end end