Jump to content


Photo

I got carpet bombing to work


35 replies to this topic

#1 Moreartillery

Moreartillery
  • Members
  • 128 posts

Posted 30 November 2018 - 02:32 PM

Cue ride of the valkyries

 

 

How it works,

The thunderhawks have a demolisher cannon with a very short range, which fires the "bomb" at anything below. As far as I know noone has done this before.

 

I know UA has airstrikes but this method has some advantages,

 

1. the aircraft can be shot down.

 

2. they can deploy troops, then make strafing runs to support them like real gunships.

 

3. they can use simple take off and landing animations like skimmers do, literally straight up and down.

 

I don't know animation. I hope someone can change the animation so they fly straight and I'm looking for volunteers. Making the turrets work would be a bonus.



#2 thudo

thudo

    Wacko AI Guy!

  • Division Leaders
  • 11,618 posts
  • Location:Lemonville North, Canada
  • Projects:DoW AI Scripting Project
  • Division:DoW
  • Job:Division Leader

Posted 30 November 2018 - 02:36 PM

Well done! :w00t:


Advanced Skirmish AI Team Lead for the coolest Warhammer40k PC RTS out there:

Dawn of War Advanced AI Headquarters

Latest DoW Advanced AI Download!

#3 Moreartillery

Moreartillery
  • Members
  • 128 posts

Posted 28 October 2019 - 03:27 AM

I've made a much more polished version with the marauder. Though I havent been able to make the bomb drop consistent, some times it has to fly over several units before it will drop. I'm sure this is a lua issue rather then an animation issue.

 

Hopefully @thudo can tell the ai how to use a unit thats primary attack is to jump over the target, otherwise this will remain player only.

 



#4 thudo

thudo

    Wacko AI Guy!

  • Division Leaders
  • 11,618 posts
  • Location:Lemonville North, Canada
  • Projects:DoW AI Scripting Project
  • Division:DoW
  • Job:Division Leader

Posted 28 October 2019 - 03:29 AM

Say wut now of this Marauder ability needing AI help? You mean it only attacks anything via Jumps?


Advanced Skirmish AI Team Lead for the coolest Warhammer40k PC RTS out there:

Dawn of War Advanced AI Headquarters

Latest DoW Advanced AI Download!

#5 Moreartillery

Moreartillery
  • Members
  • 128 posts

Posted 28 October 2019 - 03:32 AM

Say wut now of this Marauder ability needing AI help? You mean it only attacks anything via Jumps?

Well it has turrets but will die quickly if the enemy catches it on the ground, so yes.

 

It should fly to an empty space behind the enemy it wants to attack, out of sight range. Then fly back to the its hq or a generator until the bomb reloads.


Edited by Moreartillery, 28 October 2019 - 03:34 AM.


#6 thudo

thudo

    Wacko AI Guy!

  • Division Leaders
  • 11,618 posts
  • Location:Lemonville North, Canada
  • Projects:DoW AI Scripting Project
  • Division:DoW
  • Job:Division Leader

Posted 28 October 2019 - 03:41 AM

Hmmm sounds kinda like a new flier tactic. Does it always hover/fly as you mentioned "if the enemy catches it on the ground"? Was confused on that one.

 

Also, this is a special bomb drop ability that needs to reload? I don't know if we can tell an ability that as it is reloading to retreat back to safe LP or HQ and wait.. for general combat.. yes we can do that based off of health or ArmyStrength.


Advanced Skirmish AI Team Lead for the coolest Warhammer40k PC RTS out there:

Dawn of War Advanced AI Headquarters

Latest DoW Advanced AI Download!

#7 Moreartillery

Moreartillery
  • Members
  • 128 posts

Posted 28 October 2019 - 03:52 AM

The idle animation has it on the ground, it can only move by jumping, and the jump distance is map wide. The bomb is a normal weapon that shoots a projectile, range is short like ~15, 180 traverse.

 

This is dark crusade so its technically a ground unit. Anything can shoot at it but its so fast that most weapons can't aim at it before its out of range.

 

Watch the video.

 

I'll upload its files soon so that other people can mess around with it.


Edited by Moreartillery, 28 October 2019 - 03:54 AM.


#8 thudo

thudo

    Wacko AI Guy!

  • Division Leaders
  • 11,618 posts
  • Location:Lemonville North, Canada
  • Projects:DoW AI Scripting Project
  • Division:DoW
  • Job:Division Leader

Posted 28 October 2019 - 04:04 AM

Ah I see.. its like the TauVA Orca Transport which can only transport units by jumping large distances.. great for quick long range insertion but once it lands you better unload fast and get it out somewhere away from the hot zone or it could be a sitting duck.

 

Yeah we'd have to use some Jump Tactics I think.. Gambit could also weight in here on this one but it won't be going in clean formation like how you queued it up nicely and flew em all together.


Advanced Skirmish AI Team Lead for the coolest Warhammer40k PC RTS out there:

Dawn of War Advanced AI Headquarters

Latest DoW Advanced AI Download!

#9 Gambit

Gambit

    title available

  • Members
  • 6,337 posts
  • Location:Athens, Greece

Posted 28 October 2019 - 11:48 AM

Here I am :grad:

 

It should fly to an empty space behind the enemy it wants to attack, out of sight range. Then fly back to the its hq or a generator until the bomb reloads.

It is just as brother Thud said. It needs a new Flyer tactic. My speciality!

... And I can code it very neatly.

But it requires a slightly complex algorithm, first to fly over the enemies, and then to land to a non-threatened space.

Question1: Is the range of the jump unlimited?

Question2: What about returning to base, AFTER the fly-over?

Question3: Does the fly-over include bombings (in other words, attacks that would also harm your troops)?

Also, I will need a small mod containing this squad, so that I can test the AI behaviour.

 

... I will also need some time before it is ready, my hands are MORE THAN FULL this period.


-In search of Papasmurf...

#10 Moreartillery

Moreartillery
  • Members
  • 128 posts

Posted 28 October 2019 - 07:03 PM

Question1: Is the range of the jump unlimited?

Yes

 

 

Question2: What about returning to base, AFTER the fly-over?

It would be best if it returned to an area behind the base thats safe but where it won't block other units, thats why I suggested making it land near the generators.

 

Question3: Does the fly-over include bombings (in other words, attacks that would also harm your troops)?

The bomb is a modified demolisher cannon with a very short range, which fires the "bomb" at anything below, it can damage friendly troops.

 

I also need to be able to change the time between sorties in the tactics file, as different aircraft could have different reload times and weapons. For example a marauder might take 120 seconds to reload its bombs, while a lighting fighter could start a new strafing run 1 second after returning to base.



#11 Gambit

Gambit

    title available

  • Members
  • 6,337 posts
  • Location:Athens, Greece

Posted 28 October 2019 - 10:09 PM

It would be best if it returned to an area behind the base thats safe but where it won't block other units, thats why I suggested making it land near the generators.

OK, another question: After the jump, does the flyer have enough jump "charge", to return to base BEFORE it is stormed by enemy troops (see step 2], below)??

I mean, its behaviour should be:

1] I am near base, there is an enemy nearby, let's fly above it!

2] After landing behind the enemy... For HOW LONG I stay there, before returning to base?

 

The bomb is a modified demolisher cannon with a very short range, which fires the "bomb" at anything below, it can damage friendly troops.

... So it should AVOID flying above enemies, that are NEAR your troops, right?

 

 

I also need to be able to change the time between sorties in the tactics file, as different aircraft could have different reload times and weapons. For example a marauder might take 120 seconds to reload its bombs, while a lighting fighter could start a new strafing run 1 second after returning to base.

I suppose, weapons loading should be linked to Jump reload...?


Edited by Gambit, 28 October 2019 - 10:09 PM.

-In search of Papasmurf...

#12 Moreartillery

Moreartillery
  • Members
  • 128 posts

Posted 29 October 2019 - 05:51 AM

The jump recharges almost instantly, after landing behind the enemy it should return immediately. The only reason it lands behind the enemy is because its not possible for a unit to change direction mid jump, a limitation of the game engine.
 

... So it should AVOID flying above enemies, that are NEAR your troops, right?

right

 

 

I suppose, weapons loading should be linked to Jump reload...?

I'd like to be able to manually set the time between attack runs in the tactics file. 1 attack run = 2 jumps, flying to the enemy and flying back to base.



#13 Gambit

Gambit

    title available

  • Members
  • 6,337 posts
  • Location:Athens, Greece

Posted 30 October 2019 - 09:45 AM

OK, I think I have what I need. If something comes up, I will ask again.

 

So now I need a small build with the specifics of this (the race/update containing the flyer), so that to test it in-game. Because it is impossible to do it "from mind", without testing it.

Brother Moreartillery, do you have a small build with the necessary files? If so, please PM me and send it to me.

 

Note that I will most possibly do it this weekend, so no need to hurry.


-In search of Papasmurf...

#14 Gambit

Gambit

    title available

  • Members
  • 6,337 posts
  • Location:Athens, Greece

Posted 01 November 2019 - 01:11 PM

So, the code is ready. :grad:

I think it works better that expected - I dare say better than a human player, since it can handle multiple flyers at once.

It is fairly complex, thus it may require some more testing. (It supports multiple HQs, a clever attack/return method and I tried to optimise it to be as light as possible.)

Plus, I have included some customisation variables, in case a different implementation is required.

 

Before uploading it, four questions/Notes.

1] Brother Moreartillery, you are using the latest DoWAI, right? I mean, you are not still using the old, vanilla AI code. Am I correct?

2] Do you want the AI bombers to attempt bombings ONLY to visible enemies? Or to anyone in proximity?

3] Do you want the bombers to attack only infantry enemy units, or vehicles as well?

4] I noticed that the flyovers most of the times cause no damage - unless a bomb is dropped (the heavy bolters are not efficient). I suggest slowing down the fly speed of the marauders.


-In search of Papasmurf...

#15 Moreartillery

Moreartillery
  • Members
  • 128 posts

Posted 02 November 2019 - 01:29 AM

1] Brother Moreartillery, you are using the latest DoWAI, right? I mean, you are not still using the old, vanilla AI code. Am I correct?

Its Dawn Of Skirmish *DARK CRUSADE EXPANSION ONLY* V2.6  (December 2007)

 

2] Do you want the AI bombers to attempt bombings ONLY to visible enemies? Or to anyone in proximity?

Are you asking if they should attack infiltrated units or if they should attack units in the fog of war? In either case I'd choose only visible enemies.

 

3] Do you want the bombers to attack only infantry enemy units, or vehicles as well?

The marauder should be able to attack both. I'd like to copy this tactic file for multiple flyers so ideally the preferred target type could be specified in the file.

 

 

4] I noticed that the flyovers most of the times cause no damage - unless a bomb is dropped (the heavy bolters are not efficient). I suggest slowing down the fly speed of the marauders.

My mod uses heath values that are much lower then vanilla.


Edited by Moreartillery, 02 November 2019 - 01:30 AM.


#16 Gambit

Gambit

    title available

  • Members
  • 6,337 posts
  • Location:Athens, Greece

Posted 02 November 2019 - 09:22 AM

Its Dawn Of Skirmish *DARK CRUSADE EXPANSION ONLY* V2.6  (December 2007)

So it IS DoW AI... The thing is, I coded everything on the latest 3.0 version on SoulStorm...

Let's hope that although I am using countless complex functions/calls, everything is there.

 

Are you asking if they should attack infiltrated units or if they should attack units in the fog of war? In either case I'd choose only visible enemies.

OK, but it WILL add to the load of the engine (and my workload  :grad:  ), since there is no AI function for "visible" enemies. I will have to create it for myself.

This is the reason the AI of the SoB Exorcists (in SoulStorm) are able to attack your buildings form VERY AFAR. :thumbsdownsmiley:

And you keep wonder how on the origins of those missiles and the damage to your buildings...

 

The marauder should be able to attack both. I'd like to copy this tactic file for multiple flyers so ideally the preferred target type could be specified in the file.

Excellent.

I will have a variable hat gives you the option to modify it, and choose targets on demand. :thumbsupcool:

 

My mod uses heath values that are much lower then vanilla.

:thumbsupcool:

 

 

OK, so I will have the code ready by tonight (within the next 10 hours).


Edited by Gambit, 02 November 2019 - 09:22 AM.

-In search of Papasmurf...

#17 Gambit

Gambit

    title available

  • Members
  • 6,337 posts
  • Location:Athens, Greece

Posted 02 November 2019 - 02:45 PM

Code 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_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
            -- 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

Note that I kept my JumpStuck code (disabled), just in case.

Anyway, it was more complex, in the end. It needed some sneaky above-average maths. My favourite :grad:

 

It works flawlessly for me. I tested it.

The idea is: If enemy nearby, pass over him, and land behind him. Then, return at the back of one of your HQs.

Simple to say, tricky to code.

 

Downside: The problem is that there is NO FoW detection function. The Fog of War needs some VERY HEAVY functions to work properly.

So I implemented a much faster, but less efficient method.

You can try enabling it (it is not enabled in the code above, but see at the options at the top).

If you see VERY RARE flyovers, then do not change it.

 

Test, and tell me what you think man.

Let's hope all the functions I used, are also there in DC DoWAI...


-In search of Papasmurf...

#18 Moreartillery

Moreartillery
  • Members
  • 128 posts

Posted 02 November 2019 - 09:26 PM

Amazing, I didn't think it was possible!

 

I noticed that it tends to land in the enemy base. Is it possible to set a minimum distance from enemy buildings when creating a jump position?



#19 Gambit

Gambit

    title available

  • Members
  • 6,337 posts
  • Location:Athens, Greece

Posted 02 November 2019 - 09:49 PM

Amazing, I didn't think it was possible!

It was challenging to code, I have to admit it. :grad:

 

 

I noticed that it tends to land in the enemy base. Is it possible to set a minimum distance from enemy buildings when creating a jump position?

Ah, yes, of course.

I will do it tomorrow :thumbsuphappy:

It requires a small check, but I will have to thoroughly test it.


-In search of Papasmurf...

#20 Gambit

Gambit

    title available

  • Members
  • 6,337 posts
  • Location:Athens, Greece

Posted 03 November 2019 - 11:23 AM

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:


Edited by Gambit, 03 November 2019 - 11:25 AM.

-In search of Papasmurf...



Reply to this topic



  


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users