OK, ready:
----------------------------------------
-- File: 'maraudertactic.ai'
-- Created by Gambit @ 02.11.2019
class 'MarauderTactic' (GuardVehicleTactic)
Marauder = {}
function MarauderTactic:__init( squad_ai ) super( squad_ai )
self:SetName("Marauder Tactic")
-- Modifiable Stats
Marauder.G_Time_Between_Successive_Attacks = 0 -- If multiple bombers, do not have them attack simultaneously. Keep lower than 6, if enabled.
Marauder.G_Proximity_Return_Distance = 38 -- The distance from base that we deem minimum to consider the flyer is "back to base".
Marauder.G_Max_Attacking_Range_From_Base = 280 -- The max range OF THE ENEMY, that the flyer will attempt a fly-over.
Marauder.G_AttackInfantry = true -- Self-explanatory.
Marauder.G_AttackVehicles = true -- Self-explanatory. If both true, target will be chosen randomly.
Marauder.G_AttackInfiltratedUnits = false -- Self-explanatory.
Marauder.G_AttackOnlyUnitsWeCanSee = false -- Self-explanatory. Experiential. Better keep it to [false].
Marauder.G_AttackOnlyUnitsWeCanSeeRange = 35 -- If previous is true, this is the detecting range (proximity) of our nearby troops.
Marauder.G_DoNotAttackIfEnemyBuildingsAtLandingProximity = true -- Self-explanatory.
Marauder.G_DoNotAttackIfEnemyBuildingsWithin = 35 -- If previous is true, this is the range (proximity).
Marauder.G_HQ_Name = "guard_hq" -- The name of the HQ of the player. It is the AE name. No need to change, for Guard.
-- Other Stats
self.initialPosition = self.squad_ai:GetPosition()
Marauder.G_Proximity_Return_Distance_Sqr = Marauder.G_Proximity_Return_Distance * Marauder.G_Proximity_Return_Distance
Marauder.G_Proximity_Return_Distance_Triangulation = Marauder.G_Proximity_Return_Distance * 0.6
Marauder.G_NextAttackTMR = g_iGMT
if Marauder.G_Update_HQsTMR == nil then
Marauder.G_Update_HQsTMR = g_iGMT
end
if Marauder.G_Player_HQsPositions == nil then
Marauder.G_Player_HQsPositions = {}
end
end
function MarauderTactic:InitAbilities()
--[[ Init ability ID's / ABILITIES NO LONGER USED!
if Marauder.smoke_id == nil then
Marauder.smoke_id = cpu_manager.stats:GetAbilityID( "guard_smoke_bombs" )
Marauder.krak_id = cpu_manager.stats:GetAbilityID( "guard_krak_bombs" )
Marauder.incendiary_id = cpu_manager.stats:GetAbilityID( "guard_incendiary_bombs" )
end]]
end
function MarauderTactic:DoAbilities()
-- First, update HQs positions every 8 secs
if g_iGMT > Marauder.G_Update_HQsTMR + 8 then
Marauder.G_Update_HQsTMR = g_iGMT
self:UpdateHQs()
end
-- Now check if we must return (after an attack), or we are at a base
self.initialPosition = self.squad_ai:GetPosition()
local must_retrun = true
for i = 1, table.getn(Marauder.G_Player_HQsPositions) do
if distance_sqr(Marauder.G_Player_HQsPositions[i],self.initialPosition) < Marauder.G_Proximity_Return_Distance_Sqr then
must_retrun = false
break
end
end
if self.squad_ai:CanJump() then
-- In case we are away from the base, return to a random valid place (HQ)
if must_retrun then
local all_bases = table.getn(Marauder.G_Player_HQsPositions)
if all_bases > 0 then
local iBasePos = Marauder.G_Player_HQsPositions[math.random(1,all_bases)]
self:ForceSquadJumpNearBack(iBasePos)
end
-- We are at base. We must try to perform an attack
else
if g_iGMT < Marauder.G_NextAttackTMR + Marauder.G_Time_Between_Successive_Attacks then
return
end
local iEnemySquadInf = nil
local iEnemySquadVeh = nil
local iEnemySquad = nil
if Marauder.G_AttackInfantry then
iEnemySquadInf = Ability.Filters.CloseInfantryEnemy(self.initialPosition, Marauder.G_Max_Attacking_Range_From_Base, 5)
--cpu_manager.cpu_player:FindFirstInfantryEnemy(self.initialPosition, Marauder.G_Max_Attacking_Range_From_Base, 5)
end
if Marauder.G_AttackVehicles then
iEnemySquadVeh = Ability.Filters.CloseVehicleEnemy(self.initialPosition, Marauder.G_Max_Attacking_Range_From_Base, 1)
--cpu_manager.cpu_player:FindFirstVehicleEnemy(self.initialPosition, Marauder.G_Max_Attacking_Range_From_Base, 1)
end
if iEnemySquadInf ~= nil and iEnemySquadVeh ~= nil then
if math.random(1,2) == 1 then
iEnemySquad = iEnemySquadInf
else
iEnemySquad = iEnemySquadVeh
end
elseif iEnemySquadInf ~= nil and iEnemySquadVeh == nil then
iEnemySquad = iEnemySquadInf
else
iEnemySquad = iEnemySquadVeh
end
if iEnemySquad ~= nil then
if Marauder.G_AttackInfiltratedUnits or (not iEnemySquad:IsInfiltrating()) then
local iEnemyPos = iEnemySquad:GetPosition()
if (not Marauder.G_AttackOnlyUnitsWeCanSee) or self:WeCanSeePos(iEnemyPos) then
-- Do NOT perform a flyover, if we have our troops nearby!
if cpu_manager.cpu_player:FindFirstHurtSquad( iEnemyPos, 6 ) == nil then
self:ForceSquadAttackJumpNear(iEnemyPos)
Marauder.G_NextAttackTMR = g_iGMT
end
end
end
end
end
end
--[[ Check if we can deploy smoke / ABILITIES NO LONGER USED!
if (self.squad_ai:CanDoAbility(Marauder.smoke_id)) then
-- Search a squad
local iRange = self.squad_ai:GetAbilityRange(Marauder.smoke_id)
local oUnit = Ability.Filters.CloseHurt(self.squad_ai:GetPosition(), iRange, 1)
if (oUnit ~= nil and oUnit:IsInCombat() and cpu_manager:GetUnitStrength(oUnit) > 150) then
self.squad_ai:DoSpecialAbilitySquad(Marauder.smoke_id, oUnit:GetSquad())
end
end
-- Check if we're in close combat - Krak
local oEnemySquad = Ability.Filters.CloseVehicleEnemy(self.squad_ai:GetPosition(), 0, 1)
if (oEnemySquad ~= nil) then
-- Check if we can drop Krak Bombs
if (self.squad_ai:CanDoAbility(Marauder.krak_id)) then
self.squad_ai:DoSpecialAbility(Marauder.krak_id)
end
end
-- Check if we're in close combat - Incendiary
oEnemySquad = Ability.Filters.CloseInfantryEnemy(self.squad_ai:GetPosition(), 0, 5)
if (oEnemySquad ~= nil and not oEnemySquad:IsBroken()) then
-- Check if we can drop Incendiary Bombs
if (self.squad_ai:CanDoAbility(Marauder.incendiary_id)) then
self.squad_ai:DoSpecialAbility(Marauder.incendiary_id)
end
end]]
--[[ Checks jump-able stuck squads, and force them to jump nearby / NO LONGER USED!
if self.squad_ai:CanJump() then
self:SolveStuckCase()
end]]
end
function MarauderTactic:UpdateHQs()
Marauder.G_Player_HQsPositions = {}
for oBuilding in military_manager:GetBases() do
if (oBuilding:IsValid() and oBuilding:GetBaseName() == Marauder.G_HQ_Name) then
table.insert(Marauder.G_Player_HQsPositions,oBuilding:GetPosition())
end
end
end
-- Unstuck Code --------------------------------------------------
function MarauderTactic:SolveStuckCase()
local iPosition = self.squad_ai:GetPosition()
if iPosition.x ~= self.initialPosition.x or iPosition.z ~= self.initialPosition.z then
-- NOT stuck, update previous position and return, we are all good
self.initialPosition = iPosition
return
end
-- If we got here, the squad is NOT moving. See if it is simply waiting, or is stuck!
local state = self.squad_ai:GetTactic():GetState()
if (self.squad_ai:IsInStateMove() or self.squad_ai:IsInStateAttackMove() or state == "Attack") and not self.squad_ai:IsInCombat()
and iPosition.x == self.initialPosition.x and iPosition.z == self.initialPosition.z then
-- STUCK!!!!! Run the unstuck code
self:ForceSquadJumpNear(iPosition)
end
-- Update previous position anyway
self.initialPosition = self.squad_ai:GetPosition()
end
function MarauderTactic:ForceSquadJumpNear(pos)
local iPos = self.squad_ai:GetPosition()
local vJumpPosition = self.squad_ai:GetPosition()
local jumpDist = self.squad_ai:GetJumpDistance()
local jumpDistSqr = jumpDist * jumpDist
local vDir = cpu_manager:GetDirectionToEnemy(pos)
-- First, try an unstuck jump TOWARDS the enemy
-- Try to jump somewhere near, perform 30 checks in total, for a viable position
for i = 1, 12 do
-- Create a jump position
vJumpPosition.x = pos.x + vDir.x * math.random(10, jumpDist)
vJumpPosition.z = pos.z + vDir.z * math.random(10, jumpDist)
-- Check if target position is in range and if unit is able to jump to target position
local iDistanceSqr = distance_sqr(vJumpPosition, iPos)
if iDistanceSqr < jumpDistSqr and self.squad_ai:CanJumpToPosition(vJumpPosition) then
-- Jump to position
self.squad_ai:DoJump(vJumpPosition)
self.last_jump = g_iGMT
self.m_iLastGatherMove = self.last_jump - 10
return
end
end
-- Then try any random nearby place, as a secondary option
for i = 1, 18 do
-- Create a jump position
vJumpPosition.x = pos.x + 0.7 * math.random(-jumpDist, jumpDist)
vJumpPosition.z = pos.z + 0.7 * math.random(-jumpDist, jumpDist)
-- Check if target position is in range and if unit is able to jump to target position
local iDistanceSqr = distance_sqr(vJumpPosition, iPos)
if iDistanceSqr < jumpDistSqr and self.squad_ai:CanJumpToPosition(vJumpPosition) then
-- Jump to position
self.squad_ai:DoJump(vJumpPosition)
self.last_jump = g_iGMT
self.m_iLastGatherMove = self.last_jump - 10
return
end
end
end
function MarauderTactic:ForceSquadJumpNearBack(pos)
local iPos = self.squad_ai:GetPosition()
local vJumpPosition = self.squad_ai:GetPosition()
local jumpDist = self.squad_ai:GetJumpDistance()
local jumpDistSqr = jumpDist * jumpDist
local vDir = cpu_manager:GetDirectionToEnemy(pos)
-- Try to jump somewhere near, perform 15 checks in total, for a viable position, AWAY from the enemy
for i = 1, 15 do
-- Create a jump position
vJumpPosition.x = pos.x - vDir.x * math.random(4, Marauder.G_Proximity_Return_Distance_Triangulation)
vJumpPosition.z = pos.z - vDir.z * math.random(4, Marauder.G_Proximity_Return_Distance_Triangulation)
-- Check if target position is in range and if unit is able to jump to target position
local iDistanceSqr = distance_sqr(vJumpPosition, iPos)
if iDistanceSqr < jumpDistSqr and self.squad_ai:CanJumpToPosition(vJumpPosition) then
-- Jump to position
self.squad_ai:DoJump(vJumpPosition)
--self.last_jump = g_iGMT
--self.m_iLastGatherMove = self.last_jump - 10
return
end
end
end
function MarauderTactic:ForceSquadAttackJumpNear(pos)
local vJumpPosition = self.squad_ai:GetPosition()
local jumpDist = self.squad_ai:GetJumpDistance()
local jumpDistSqr = jumpDist * jumpDist
local ix = 0; local iz = 0;
if sqr(pos.x-vJumpPosition.x) < 0.0001 then
iz = 1
else
local a = (pos.z-vJumpPosition.z)/(pos.x-vJumpPosition.x)
ix = math.sqrt(1/(sqr(a) + 1))
iz = math.abs(a*ix)
end
if vJumpPosition.x > pos.x then ix = -1*ix end
if vJumpPosition.z > pos.z then iz = -1*iz end
-- Try to jump somewhere near, perform 10 checks in total, for a viable position
for i = 1, 10 do
-- Create a jump position
local rndm = math.random(25, 40)
vJumpPosition.x = pos.x + ix*rndm
vJumpPosition.z = pos.z + iz*rndm
-- Check if target position is in range and if unit is able to jump to target position
local iDistanceSqr = distance_sqr(vJumpPosition, self.initialPosition)
if iDistanceSqr < jumpDistSqr and self.squad_ai:CanJumpToPosition(vJumpPosition) then
if Marauder.G_DoNotAttackIfEnemyBuildingsAtLandingProximity then
-- Do NOT attempt the jump, if we are to land near enemy buildings!
local iBuilding = Ability.EntityFilters.CloseBaseEntityEnemy(vJumpPosition, Marauder.G_DoNotAttackIfEnemyBuildingsWithin, 1)
if iBuilding ~= nil then return end
end
-- Now, Jump to position
self.squad_ai:DoJump(vJumpPosition)
--self.last_jump = g_iGMT
--self.m_iLastGatherMove = self.last_jump - 10
return
end
end
end
function MarauderTactic:WeCanSeePos(iPos)
local iRangeSqr = Marauder.G_AttackOnlyUnitsWeCanSeeRange * Marauder.G_AttackOnlyUnitsWeCanSeeRange
for oUnit in military_manager:GetSquads() do
if oUnit:IsValid() then
if distance_sqr(oUnit:GetPosition(),iPos) < iRangeSqr then
return true
end
end
end
return false
end
For anything else, just say so brother Moreartillery. :thumbsupcool: