• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Solved] AI trains way more defenders than I've ordered it to

Level 28
Joined
Dec 3, 2020
Messages
970
So I made an .ai script for a human bot.
I order the AI to have 2 footmen, 1 footman archer, 1 sorceress, 1 captain and 1 Doril Aran (custom unit, non hero) as campaign defenders.
Initially it's fine but after like 16 minutes I update the defenders by making them ---> 3 footmen, 2 footman archers, 2 sorceresses, 1 knight, 1 captain and 1 Doril Aran.
When this happens, the AI just uses 20 units as defenders, way more than I have told it to use!

1736372821328.png


As you can see, it even uses Dwarven Battlepriests as campaign defenders.
Is there a way to easily fix this???
I've attached the .ai file on this thread.

And yes, before anyone asks, I do call the call WaitForSignal() command 4 times in the trigger editor, with a 4 minute delay between each one and the AddGuard commands also work properly.
 

Attachments

  • TheDarkNight_blue.ai
    3.3 KB · Views: 10
Last edited:
Level 30
Joined
Aug 29, 2012
Messages
1,383
Careful you have a few calls that are turned into comments and thus won't work, not sure if that was intended

1736373266998.png


Do AddGuardPost truly work properly without calling FillGuardPosts afterward?

I'm not sure 100% sure but my only lead would be that sending a command triggers all WaitForSignals at the same time, I'm really not sure if you can use it several times in a script, can't remember seeing it in a Blizzard script at least

You'd probably need to use different command (1, 2, 3, whatever), retrieve them with GetLastCommand and do each block separately, but it's a bit tricky
 
Level 28
Joined
Dec 3, 2020
Messages
970
Careful you have a few calls that are turned into comments and thus won't work, not sure if that was intended

View attachment 507652

Do AddGuardPost truly work properly without calling FillGuardPosts afterward?

I'm not sure 100% sure but my only lead would be that sending a command triggers all WaitForSignals at the same time, I'm really not sure if you can use it several times in a script, can't remember seeing it in a Blizzard script at least

You'd probably need to use different command (1, 2, 3, whatever), retrieve them with GetLastCommand and do each block separately, but it's a bit tricky
Ah yes, I will uncomment this line and others that may be needed to uncomment
Do AddGuardPost truly work properly without calling FillGuardPosts afterward?
I'm sure they do because I already use it in other scripts. It works because of this ---> call SetReplacements(9,9,9)
I have used addGuardPost in other scripts the same way. I literally have the exact same script but also with attack waves and that one works good.
Tomorrow I will be on my PC again so I could show it.

I'm not sure 100% sure but my only lead would be that sending a command triggers all WaitForSignals at the same time, I'm really not sure if you can use it several times in a script, can't remember seeing it in a Blizzard script at least
As previously stated, it does work in that other script I mentioned. I can also show the triggers there (and the ones I use here), but tomorrow!

Thank you for the heads up for that comment on the farms, I missed that one completely lol
 
Level 21
Joined
Mar 16, 2008
Messages
955
You should use conditions to limit to the right number of units before 20 mins. After 20 mins, you can use a trigger to prompt the ai into "tier 2" which would increase the units. I got this idea from InsaneMonster's AI guide.

Here are some excepts from the King AI scripts in Kings and Knights that use the "tier" feature:

JASS:
globals
    integer                 Tier                       = 0
endglobals

//===========================================================================
// Sleep delays for each attack wave
//===========================================================================
function AttackWaveDelay takes integer inWave returns nothing
    if (inWave < nextDelay) then
        return
    endif
    if (inWave == 0) then
        call Sleep( 90 )
    elseif (inWave == 1) then
        call Sleep( 70 )
    elseif ((inWave == 2) and (Tier == 1)) then    
        call Sleep( 45 )
    elseif ((inWave == 2) and (Tier == 2)) then   
        call Sleep( 120 )
    elseif ((inWave == 2) and (Tier == 3)) then   
        call Sleep( 180 )
    endif
    set nextDelay = inWave + 1
endfunction

//===========================================================================
// AI Command Function
//===========================================================================

function TierCondition takes nothing returns nothing
    if CommandsWaiting() > 0 then
        set Tier = GetLastCommand()
        call PopLastCommand()
        call InitBuildArray()
    endif
endfunction

function ConditionLoop takes nothing returns nothing
    call TierCondition()
    call StaggerSleep(1,5)
    loop
        call TierCondition()
        call Sleep(16)
    endloop
endfunction

//===========================================================================
// Initiates an attack based on target priorities
//===========================================================================
function LaunchAttack takes nothing returns nothing
    local unit target = null
    local boolean setAlly = true
    // Don't launch any attack while town is threatened
    if (TownThreatened()) then
        call Sleep( 2 )
        return
    endif

    // Target Priority #1
    if (Tier < 3) then
        if (target == null) then
            set target = GetCreepCamp( 0, 9, false )
        endif
    endif

  • AI Tier 1 Start
    • Events
      • Time - Elapsed game time is 300.00 seconds
    • Conditions
    • Actions
      • AI - Send Player 1 (Red) the AI Command (1, 0)
      • AI - Send Player 2 (Blue) the AI Command (1, 0)
      • AI - Send Player 3 (Teal) the AI Command (1, 0)
      • AI - Send Player 4 (Purple) the AI Command (1, 0)
  • AI Tier 2 Start
    • Events
      • Time - Elapsed game time is 900.00 seconds
    • Conditions
    • Actions
      • AI - Send Player 1 (Red) the AI Command (2, 0)
      • AI - Send Player 2 (Blue) the AI Command (2, 0)
      • AI - Send Player 3 (Teal) the AI Command (2, 0)
      • AI - Send Player 4 (Purple) the AI Command (2, 0)
 
Level 28
Joined
Dec 3, 2020
Messages
970
You should use conditions to limit to the right number of units before 20 mins. After 20 mins, you can use a trigger to prompt the ai into "tier 2" which would increase the units. I got this idea from InsaneMonster's AI guide.

Here are some excepts from the King AI scripts in Kings and Knights that use the "tier" feature:

JASS:
globals
    integer                 Tier                       = 0
endglobals

//===========================================================================
// Sleep delays for each attack wave
//===========================================================================
function AttackWaveDelay takes integer inWave returns nothing
    if (inWave < nextDelay) then
        return
    endif
    if (inWave == 0) then
        call Sleep( 90 )
    elseif (inWave == 1) then
        call Sleep( 70 )
    elseif ((inWave == 2) and (Tier == 1)) then   
        call Sleep( 45 )
    elseif ((inWave == 2) and (Tier == 2)) then  
        call Sleep( 120 )
    elseif ((inWave == 2) and (Tier == 3)) then  
        call Sleep( 180 )
    endif
    set nextDelay = inWave + 1
endfunction

//===========================================================================
// AI Command Function
//===========================================================================

function TierCondition takes nothing returns nothing
    if CommandsWaiting() > 0 then
        set Tier = GetLastCommand()
        call PopLastCommand()
        call InitBuildArray()
    endif
endfunction

function ConditionLoop takes nothing returns nothing
    call TierCondition()
    call StaggerSleep(1,5)
    loop
        call TierCondition()
        call Sleep(16)
    endloop
endfunction

//===========================================================================
// Initiates an attack based on target priorities
//===========================================================================
function LaunchAttack takes nothing returns nothing
    local unit target = null
    local boolean setAlly = true
    // Don't launch any attack while town is threatened
    if (TownThreatened()) then
        call Sleep( 2 )
        return
    endif

    // Target Priority #1
    if (Tier < 3) then
        if (target == null) then
            set target = GetCreepCamp( 0, 9, false )
        endif
    endif

  • AI Tier 1 Start
    • Events
      • Time - Elapsed game time is 300.00 seconds
    • Conditions
    • Actions
      • AI - Send Player 1 (Red) the AI Command (1, 0)
      • AI - Send Player 2 (Blue) the AI Command (1, 0)
      • AI - Send Player 3 (Teal) the AI Command (1, 0)
      • AI - Send Player 4 (Purple) the AI Command (1, 0)
  • AI Tier 2 Start
    • Events
      • Time - Elapsed game time is 900.00 seconds
    • Conditions
    • Actions
      • AI - Send Player 1 (Red) the AI Command (2, 0)
      • AI - Send Player 2 (Blue) the AI Command (2, 0)
      • AI - Send Player 3 (Teal) the AI Command (2, 0)
      • AI - Send Player 4 (Purple) the AI Command (2, 0)
Interesting!

The one think I actually wanna know is where could I put the:

if (TownThreatened()) then
call Sleep( 2 )
return
endif

in a normal blizzard campaign AI, such as the one attached below?
 

Attachments

  • TheHammerFalls_lightblue.ai
    7.9 KB · Views: 2
Level 28
Joined
Dec 3, 2020
Messages
970
So nothing really worked, except for limiting the amount of units the AI can train (14 footmen, 6 riflemen... (for example)). It was easy to count them at least.

I will not mark the thread as solved for now though, as this isn't a valid solution for the problem in my opinion.

Also, @Gnuoy, I tried your method with the AI Command(0, 0) , AI Command(1, 0) etc...
It seemed to work exactly as always sending it the (0, 0) command always, instead of incrementing the value of the first index's value by 1 each time.
I wonder what will happen if I increment the value of the second index by 1 each time.
 
Level 28
Joined
Dec 3, 2020
Messages
970
I'm not sure. I think that part was just a copy paste from the GUI-generated AI script.
I think I found a solution (a solution that works for me at least!) that does not require to limit the amount of units the AI can train through triggers.

So I made a trigger, just to experiment, that sets the current food of the AI player to 54 every 5 seconds and it trains exactly as many units as I've told it to in the script!
It doesn't train 30 extra defenders :'D
At least so far this are the results I've gathered with the playtesting I've done.
The AI does indeed go over 100 food on the map so maybe going over the 100 food cap makes it do weird things such as train 8 additional Gyrocopters as defenders (even when I've ordered it only 1 or 2 or even 0).

So, making the AI not have over 100 current food seems to fix all these weird bugs.
I assume that making each unit it has cost 0 food (through the object editor for example) would also work.
I can see how this solution might not be good for some people who may want the AI to use the "real" current food instead of manipulating it through triggers or the object editor, BUT it is a perfect solution for me since it is for a blizzard style campaign map.

I think that I can safely say that this thread is... SOLVED (finally!)
 
Last edited:
Can I get a code tag of the AI script?

I suspect another possible issue is that there's SetBuildUnit independent of the assigning defenders portion. Usually, I just put defenders into the SetBuildUnit (or SetBuildUnitEx) portion and then use attack groups for attacking ones. Be careful with SetReplacement, since that means all preplaced units will be replaced even if there's no SetBuildUnit for them, iirc.
 
Level 28
Joined
Dec 3, 2020
Messages
970
Can I get a code tag of the AI script?

I suspect another possible issue is that there's SetBuildUnit independent of the assigning defenders portion. Usually, I just put defenders into the SetBuildUnit (or SetBuildUnitEx) portion and then use attack groups for attacking ones. Be careful with SetReplacement, since that means all preplaced units will be replaced even if there's no SetBuildUnit for them, iirc.
Here:
JASS:
//==================================================================================================
//  The Dark Night -- Blue Player (Alliance Expedition) -- AI Script
//==================================================================================================
globals
    player targ1 = PlayerEx(19)
    player targ2 = PlayerEx(5)
    player targ  = targ1
endglobals

//--------------------------------------------------------------------------------------------------
//  main
//--------------------------------------------------------------------------------------------------
function main takes nothing returns nothing

    call CampaignAI('hhou',null)

    //call DoCampaignFarms(false)
    set campaign_wood_peons = 2
    call SetReplacements(9,9,9)
    call SetPeonsRepair( true )
    call SetCaptainHome(DEFENSE_CAPTAIN,-7799,1920)


        call SetBuildUnitEx( 1,1,1, 'hpea'           ) // Peasant
        call SetBuildUnitEx( 1,1,1, 'htow'      ) // Town Hall
    call SetBuildUnitEx( 1,1,1, 'halt'      ) // Altar of Kings
        call SetBuildUnitEx( 2,2,2, 'h60D'             ) // Barracks
        call SetBuildUnitEx( 2,2,2, 'h60K'      ) // Workshop
    //call SetBuildUnitEx( 6,6,6, 'hhou'        ) // Farms
    call SetBuildUnitEx( 1,1,1, 'hlum'      )     // Lumber Mill
    call SetBuildUnitEx( 1,1,1, 'hbla'      )     // Blacksmith
    call SetBuildUnitEx( 1,1,1, 'hgra'      ) // Gryphon Aviary
        call SetBuildUnitEx( 5,5,5, 'hpea'           ) // Dwarven Worker
    call SetBuildUnitEx( 1,1,1, 'hkee'      ) // Keep
    call SetBuildUnitEx( 1,1,1, 'hcas'      ) // Castle

    call CampaignDefenderEx( 2, 2, 2, 'hfoo'   ) // Footman
    call CampaignDefenderEx( 1, 1, 1, 'n600'         ) // Footman Archer
    call CampaignDefenderEx( 1, 1, 1, 'h616'   ) // Sorceress
    call CampaignDefenderEx( 1, 1, 1, 'h60U'   ) // Doril Aran
    call CampaignDefenderEx( 1, 1, 1, 'hcth'         ) // Captain -> Falric

    // 4 WaitForSignal() in total

    // 1st
    call WaitForSignal()

    call AddGuardPost( 'hfoo',         -6778, 2154 )
    call AddGuardPost( 'hfoo',         -6959, 1057 )
    call AddGuardPost( 'hfoo',         -7626, -2312 )

    // 2nd
    call WaitForSignal()

    call AddGuardPost( 'hrif',         -7662, -1798 )
    call AddGuardPost( 'n600',         -7581, 2298 )
    call AddGuardPost( 'hrif',         -7132, 1401 )

    // 3rd
    call WaitForSignal()
    call AddGuardPost( 'n602',         -6768, 2367 )
    call AddGuardPost( 'hkni',         -7284, 2758 )
    call AddGuardPost( 'hkni',         -8080, -4 )

    // 4th ---> also increase defenders
    call WaitForSignal()
    call AddGuardPost( 'hgry',         -7670, -718 )
    call AddGuardPost( 'hgry',         -7113, 1829 )
    call AddGuardPost( 'hmpr',         -7316, 2512 )
    call AddGuardPost( 'hmpr',         -7927, -1558 )
    call AddGuardPost( 'nhym',         -7632, -2821 )

    call CampaignDefenderEx( 3, 3, 3, 'hfoo'   ) // Footman
    call CampaignDefenderEx( 2, 2, 2, 'n600'         ) // Footman Archer
    call CampaignDefenderEx( 1, 1, 1, 'hkni'   ) // Knight
    call CampaignDefenderEx( 2, 2, 2, 'h616'   ) // Sorceress
    call CampaignDefenderEx( 1, 1, 1, 'h60U'   ) // Doril Aran
    call CampaignDefenderEx( 1, 1, 1, 'hcth'         ) // Captain -> Falric

endfunction
 
Level 28
Joined
Dec 3, 2020
Messages
970
Why not just use CampaignDefenderEx without the AddGuardPost for all the units?

Also, does the player starts with the units that they're not suppose to train? SetReplacement means that all preplaced unit will be replaced as many times as stated by the number
1 - I want the AI to place X unit at a specific location and said unit acts as a preplaced unit instead of a campaign defender (it won't run around the entire base of the AI when the base is under attack, it will instead just stay afk at the specific location I want it to).

2 - I think I'm having a brainfart so I might have misunderstood your 2nd question but for example the AI does NOT have preplaced knights, instead it trains them upon the AddGuardPost function call.
And yeah I want the SetReplacement thing to work the way it is now.

For some context, this is a defense mission where the player and 2 AI allies have to protect a Way Gate for 45 minutes from the undead.

EDIT: i think i kinda understand the 2nd question now:
Of course not!!! The AI players start with units they can train and the units I add in the AddGuardPost are also units that the AI can train.
 
Is this correct for the units you want?

Before boost:
2 Footman
1 Archer
1 Sorceress
1 Doril Aran
1 Falric

After boost:
(2+3) Footman
(1+2) Archer
1 Knight
(1+2) Sorceress
(1+1) Doril Aran
(1+1) Falric

The thing is, I suspect your second call is translated to extra units, so instead of 3 Footman you get 5 Footman total. I am at total lost for the Battlepriest shenanigans
 
Level 28
Joined
Dec 3, 2020
Messages
970
Is this correct for the units you want?

Before boost:
2 Footman
1 Archer
1 Sorceress
1 Doril Aran
1 Falric

After boost:
(2+3) Footman
(1+2) Archer
1 Knight
(1+2) Sorceress
(1+1) Doril Aran
(1+1) Falric

The thing is, I suspect your second call is translated to extra units, so instead of 3 Footman you get 5 Footman total. I am at total lost for the Battlepriest shenanigans
Before boost: yes
After boost, no "+", instead the total number, so:
After boost:
3 Footman
2 Archer
1 Knight
2 Sorceress
1 Doril Aran
1 Falric

I've done this multiple times before and it's worked perfectly. I am almost 100% certain that it overwrites the previous defenders instead of adding "+" more to the sum.
This has worked many previous times for me but the AI there had little armies (below 100 food).
In this map the AI has around 100 current food on map start and it adds up to like 130-150 after the boosts and AddGuardPosts.

As stated previously, the issue got fixed once I manipulated the AI's current food through a trigger (setting to like 52 or 54 etc...) every 5 seconds.
It got fixed for this map and for a couple of new ones I've created since then that had the same issue.
So what I mean by this is that the AI has some "engine" problems when it has over 100 food, causing weird bugs (at least that's what I've been led to believe). So even if the AI script is perfect, the AI would still glitch out because of that.
Of course I might be wrong but all of these bugs were gone once I did that on all of the maps where this bug was present.

And obviously I made 100% sure that the has enough resources and max food and food capacity etc... with triggers (and farms)

EDIT: warcraft 3 AI is old and honestly not very good in my opinion so bugs like these are not a surprise for me
 
Now that you mentioned potential issues at over 100 foods, I might need to check an old campaign of mine if it also exists there. It is interesting that food manipulation fixes the issue. The logic I had was there would be a summation since there are two set of calls, instead of an override, hence the question.

Also, regarding the Battlepriest, I think AI might have partially disabled tech tree in original campaign maps, but I might be wrong.
 
Level 28
Joined
Dec 3, 2020
Messages
970
Now that you mentioned potential issues at over 100 foods, I might need to check an old campaign of mine if it also exists there. It is interesting that food manipulation fixes the issue. The logic I had was there would be a summation since there are two set of calls, instead of an override, hence the question.

Also, regarding the Battlepriest, I think AI might have partially disabled tech tree in original campaign maps, but I might be wrong.
Hm, I didn't try disabling the tech tree, such as removing the Battlepriests from it. I don't think it would've fixed the issue since the AI would just overproduce another unit.
And this specific AI does use the Battlepriest unit so that's not really an option.
 
Level 21
Joined
Mar 16, 2008
Messages
955
1 - I want the AI to place X unit at a specific location and said unit acts as a preplaced unit instead of a campaign defender (it won't run around the entire base of the AI when the base is under attack, it will instead just stay afk at the specific location I want it to).
If have the campaign AI set up correctly, it will only replace pre-placed units. The ones running around base are a melee AI, probably part of the main attack group. If I'm understanding correctly, campaign AI is exactly what you want.
 
Top