• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[General] AI for standard and custom races after creating starting units plus hero

Status
Not open for further replies.
Level 25
Joined
Feb 2, 2006
Messages
1,689
I have this map: World of Warcraft TSR 1.4

It contains some custom races and also the four standard races. For computer players I automatically create a main building and 5 workers and a hero.
It seems that the computer player sends the workers to the goldmine immediately but does not continue to create buildings or to level his hero.
I run the following trigger action after creating the units:
Code:
Melee Game - Run melee AI scripts (for computer players)

Is there any way to fix the AI, so the computer players will level their heroes and start creating buildings etc.?
 

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,869
I run the trigger action after creating one hero, 5 workers and a main building. I would understand it if it would not work for custom races but shouldn't it work for the standard races?
I suggest extracting the melee .ai files, importing them to the map and try running them via the run trigger AI script. It's possible that Run Melee AI script only works on map initialization not after.

Actually, I'll have to get back to you with this since I remember working on a map with AI script after initialization but it was so many years ago.
 
Level 25
Joined
Feb 2, 2006
Messages
1,689
Hey, that might be true since the computer players might have set races like Night Elves but ingame they might be human. Is there a standard path for the AI melee files I can use without importing the files manually?

Found this in Blizzard.j:
JASS:
function MeleeStartingAI takes nothing returns nothing
    local integer index
    local player  indexPlayer
    local race    indexRace

    set index = 0
    loop
        set indexPlayer = Player(index)
        if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
            set indexRace = GetPlayerRace(indexPlayer)
            if (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER) then
               // Run a race-specific melee AI script.
                if (indexRace == RACE_HUMAN) then
                    call PickMeleeAI(indexPlayer, "human.ai", null, null)
                elseif (indexRace == RACE_ORC) then
                    call PickMeleeAI(indexPlayer, "orc.ai", null, null)
                elseif (indexRace == RACE_UNDEAD) then
                    call PickMeleeAI(indexPlayer, "undead.ai", null, null)
                    call RecycleGuardPosition(bj_ghoul[index])
                elseif (indexRace == RACE_NIGHTELF) then
                    call PickMeleeAI(indexPlayer, "elf.ai", null, null)
                else
                   // Unrecognized race.
                endif
                call ShareEverythingWithTeamAI(indexPlayer)
            endif
        endif

        set index = index + 1
        exitwhen index == bj_MAX_PLAYERS
    endloop
endfunction
I will try to use the files.

Another issue might be that in my map players have only one hero. So I might have to create custom AI scripts for all races setting them to use only one random hero but I don't know how strict the AI scripts are followed by the computer players.
Btw. I tried to create a custom AI with a random hero but get compile errors for hero's skills.
It detects that all heros have attribute bonus but the second and third skill look like:

JASS:
 set skills1[ 1] = 'Aamk'
            set skills1[ 2] = ''
            set skills1[ 3] = ''
            set skills1[ 4] = 'Aamk'
            set skills1[ 5] = ''
            set skills1[ 6] = ''
            set skills1[ 7] = ''
            set skills1[ 8] = 'Aamk'
            set skills1[ 9] = ''
            set skills1[10] = ''
Any idea how to fix this?
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,869
Hey, that might be true since the computer players might have set races like Night Elves but ingame they might be human. Is there a standard path for the AI melee files I can use without importing the files manually?
Ugh, I don't know since I never tried extracting stuff via CASC viewer before.
But in Retera's Model Studio one could see lots of .ai scrips with the same names even:
reteraAI01.png
reteraAI02.png
Some could be RoC and the others TfT but what of those under the scripts folder? Surely they're not campaign related as those are specifically named after the chapter names.
Those under AI scripts are custom examples for the AI editor which before 1.29 or even 1.28 were visible in the game's main folder under the same folder name as in the picture.

You'll have to import them and test them. They can't be checked in the AI editor as they are not .wai but the .ai files that are to be run not read by the inbuilt program.
Attached them.

EDIT:
Another issue might be that in my map players have only one hero. So I might have to create custom AI scripts for all races setting them to use only one random hero but I don't know how strict the AI scripts are followed by the computer players.
Btw. I tried to create a custom AI with a random hero but get compile errors for hero's skills.
It detects that all heros have attribute bonus but the second and third skill look like:
Any idea how to fix this?
I don't know raw JASS/custom script. Just study a custom made ai file in the AI Editor.
Attached the 1.27 example files.

EDIT2:
The triggers of this map might help
Skeletal Coast v1.3
 

Attachments

  • AI.zip
    37.8 KB · Views: 128
  • AI Scripts - example.zip
    2.3 KB · Views: 123
Last edited:
Level 25
Joined
Feb 2, 2006
Messages
1,689
Hey, is there any way how I can provide JASS functions which can be used in all of my imported AI scripts? Do I have to modify the commons.ai and import it into my map or is there another way?
I provide functions in the custom map script now which can be accessed via the AI scripts but global variables from the commons.ai are not available there.

And what is the difference between a melee and a campaign AI.
I run a custom AI for a player which should change the player's name but it doesn't.
Is it because they do already have a hero in the beginning? Is there any way to fix or analyse the reason?
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,869
Level 25
Joined
Feb 2, 2006
Messages
1,689
Does the computer has sufficient resources and food? Also, is there any custom setting to preexisting units?
Yes, and as I wrote it does not change the player's name although specified in the AI script. The player has a main building, 5 workers and a hero and all he does is to collect some gold with the workers. He has also an altar which produces some food. I think that the AI is not run properly either because there is already a hero or the additional altar building but I am not sure how I can configure the AI so it recognizes that the hero is already there.

Thanks for the links, I will try to read some stuff.
 
Level 25
Joined
Feb 2, 2006
Messages
1,689
So I found the bug. I cannot use functions from Blizzard.j, my map script or global variables. Hence, I have to add all functions either to a custom commons.ai or to the AI script.
I have started now with a simplified AI:

JASS:
//===========================================================================
// Calculate the modulus/remainder of (dividend) divided by (divisor).
// Examples:  18 mod 5 = 3.  15 mod 5 = 0.  -8 mod 5 = 2.
//
function ModuloInteger takes integer dividend, integer divisor returns integer
    local integer modulus = dividend - (dividend / divisor) * divisor
    // If the dividend was negative, the above modulus calculation will
    // be negative, but within (-divisor..0).  We can add (divisor) to
    // shift this result into the desired range of (0..divisor).
    if (modulus < 0) then
        set modulus = modulus + divisor
    endif
    return modulus
endfunction

function ChooseHeroSkill takes nothing returns integer
    local integer curHero = GetHeroId()
    local integer level = GetHeroLevelAI()
    local integer mod = ModuloInteger(level, 4)
    local boolean skillUlti = ModuloInteger(level, 6) == 0 and level / 6 <= 9
    // Paladin
    if (curHero == 'Hpal') then
        if (skillUlti) then
            return 'AHre' // resurrection
        elseif (mod == 0) then
            return 'AHhb'
        elseif (mod == 1) then
            return 'AHds'
        elseif (mod == 2) then
           return 'Aamk'
        elseif (mod == 3) then
            return 'AHad'
        endif
    // TODO Add all hero types!
    endif
    return 'Aamk'
endfunction

function main takes nothing returns nothing
    call CampaignAI( HOUSE, function ChooseHeroSkill )
endfunction

I found a nice tutorial here: How to make a Campaign AI - WC3 Modding Information Center
It says that campaign AIs are much simpler for the beginning.
I might extend this AI step by step. At least it works now and the player starts building farms immediately.
Maybe I am going to extend the commons.ai since I need one single ChooseHeroSkill which can handle all unit types for heroes. Besides, I am not really sure if this AI will detect the correct hero type id.
I guess ChooseHeroSkill will be called whenever a hero gains a new level but it should also be called in the beginning when the hero is level 1?
 

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,869

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,869
But then it is not guaranteed that the player is hostile? Can I even use the commons.j functions here? In my map the teams are not fixed. Maybe the campaign AI is the wrong approach then?
I guess you need to set straight which players are supposed to be treated as enemy/computers and which players. Establish that at map start and then run the AI scripts on those players.
 
Level 25
Joined
Feb 2, 2006
Messages
1,689
Okay thanks, so this is the only how it works for campaign AIs? I am just wondering since in a melee map the teams are set in the lobby and are not always the same but I guess a melee attack wave works differently.

My map allows setting the teams in the lobby as well. Hence, it will be difficult for me to set the enemies in the beginning. I can let them attack Player(0) all the time but attacking an enemy would be better.
Besides, I might allow changing alliances during the game. I am not sure if an AI can handle this.
 

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,869
Okay thanks, so this is the only how it works for campaign AIs? I am just wondering since in a melee map the teams are set in the lobby and are not always the same but I guess a melee attack wave works differently.
Well, the GUI trigger for melee AI script is ran without choosing a player to be ran on unlike the custom AI trigger which needs player specification if I recall correctly.
My map allows setting the teams in the lobby as well. Hence, it will be difficult for me to set the enemies in the beginning. I can let them attack Player(0) all the time but attacking an enemy would be better.
Besides, I might allow changing alliances during the game. I am not sure if an AI can handle this.
You can't stop an AI or change it on the same player, so alliances and stuff will not work.
The only way around it would be for you to fully customize the AI and not use any .ai files, whatnot.
It's not that difficult to know which player is player or computer controlled at the start. There are triggers/conditions for that.
 
Level 25
Joined
Feb 2, 2006
Messages
1,689
Suppose the alliances will be fixed. I have generated a melee AI script with the AI editor and the attack target is specified in this function:

JASS:
//===========================================================================
// Basic attack functionality
//===========================================================================
function AttackTarget takes unit target, boolean addAlliance returns nothing
    if (target == null) then
        return
    endif
    if (addAlliance) then
        call SetAllianceTarget( target )
    endif
    call FormGroup( 3, true )
    call AttackMoveKillA( target )
    if (not addAlliance) then
        call SetAllianceTarget( null )
    endif
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 (target == null) then
        set target = GetAllianceTarget()
        if (target != null) then
            set setAlly = false
        endif
    endif
    // Target Priority #2
    if (target == null) then
        set target = GetExpansionFoe()
        if (target != null) then
            set take_exp = false
        endif
    endif
    // Target Priority #3
    if (target == null) then
        set target = GetMegaTarget()
    endif
    // Target Priority #4
    if (target == null) then
        set target = GetEnemyExpansion()
    endif
    // Target Priority #5
    if (target == null) then
        set target = GetEnemyExpansion()
        if (target == null) then
            call StartGetEnemyBase(  )
            loop
                exitwhen (not WaitGetEnemyBase())
                call SuicideSleep( 1 )
            endloop
            set target = GetEnemyBase()
        endif
    endif
    // Target Priority #6
    if (target == null) then
        set target = GetCreepCamp( 0, 9, false )
    endif
    // Target Priority #7
    if (target == null) then
        set target = GetCreepCamp( 10, 100, true )
    endif
    // Attack the target and increment attack wave
    if (target != null) then
        call AttackTarget( target, setAlly )
        call AttackWaveUpdate(  )
    else
        // If no target was found, sleep a bit before trying again
        call Sleep( 20 )
    endif
endfunction

It uses AttackMoveKillA like the campaign AI. Maybe it will work with the campaign AI, too. Functions like GetEnemyExpansion and GetEnemyBase should consider the alliances?
 
Level 25
Joined
Feb 2, 2006
Messages
1,689
So I tested it and it seems that the player attacks other players but sending units one by one. These are my questions:
  • Why does my player attack with units one by one?
  • Why does my computer player collect only one gold for every worker unit? I tested it when being allied with a computer player.
  • When the townhall and workers are destroyed/killed I will give the hero a tiny townhall item and some resources when he constructed it, so he can rebuild his whole base somewhere. However, it seems that the hero won't do that automatically. Is this only possible via triggers or does the AI somehow use tiny townhall items?
This is my current AI script:
JASS:
//===========================================================================
// Calculate the modulus/remainder of (dividend) divided by (divisor).
// Examples:  18 mod 5 = 3.  15 mod 5 = 0.  -8 mod 5 = 2.
//
function ModuloInteger takes integer dividend, integer divisor returns integer
    local integer modulus = dividend - (dividend / divisor) * divisor
    // If the dividend was negative, the above modulus calculation will
    // be negative, but within (-divisor..0).  We can add (divisor) to
    // shift this result into the desired range of (0..divisor).
    if (modulus < 0) then
        set modulus = modulus + divisor
    endif
    return modulus
endfunction
function ChooseHeroSkill takes nothing returns integer
    local integer curHero = GetHeroId()
    local integer level = GetHeroLevelAI()
    local integer mod = ModuloInteger(level, 4)
    local boolean skillUlti = ModuloInteger(level, 6) == 0 and level / 6 <= 9
    // Paladin
    if (curHero == 'Hpal') then
        if (skillUlti) then
            return 'AHre' // resurrection
        elseif (mod == 0) then
            return 'AHhb'
        elseif (mod == 1) then
            return 'AHds'
        elseif (mod == 2) then
            return 'AHad'
        endif
    // Arch mage
    elseif (curHero == 'Hamg') then
        if (skillUlti) then
            return 'AHmt' // mass teleport
        elseif (mod == 0) then
            return 'AHbz'
        elseif (mod == 1) then
            return 'AHab'
        elseif (mod == 2) then
            return 'AHwe'
        endif
    // Mountain King
    elseif (curHero == 'Hmkg') then
        if (skillUlti) then
            return 'AHav' // avatar
        elseif (mod == 0) then
            return 'AHtc'
        elseif (mod == 1) then
            return 'AHtb'
        elseif (mod == 2) then
            return 'AHbh'
        endif
    // Blood Mage
    elseif (curHero == 'Hblm') then
        if (skillUlti) then
            return 'AHpx' // phoenix
        elseif (mod == 0) then
            return 'AHfs'
        elseif (mod == 1) then
            return 'AHbn'
        elseif (mod == 2) then
            return 'AHdr'
        endif
    // TODO Add all hero types!
    endif
    return 'Aamk'
endfunction
function ConfigureAI takes nothing returns nothing
    call SetTargetHeroes( true )
    call SetUnitsFlee( true )
    call SetHeroesFlee( true )
    call SetPeonsRepair( true )
    call SetHeroesBuyItems( true )
    call SetHeroesTakeItems( true )
endfunction
function BuildingStrategy takes nothing returns nothing
    // **********************************
    // *      Building Strategy         *
    // **********************************
    // Tier 1 Buildings
    call SetReplacements( 1, 2, 3 )
    call SetBuildUnit( 1, TOWN_HALL )
    call SetBuildUnit( 10, PEASANT )
    call SetBuildUnit( 4, 'n00E' ) // Human Citizen (Male)
    call SetBuildUnit( 3, HOUSE )
    call SetBuildUnit( 3, 'h00R' ) // Human Housing
    call SetBuildUnit( 2, BARRACKS )
    call SetBuildUnit( 1, HUMAN_ALTAR )
    call SetBuildUnit( 1, LUMBER_MILL )
    call SetBuildUnit( 1, BLACKSMITH )
    call SetBuildUnit( 1, ARCANE_VAULT )
    call CampaignDefenderEx( 2, 2, 3, FOOTMAN )
    call CampaignDefenderEx( 1, 1, 1, RIFLEMAN )
    // Tier 2 buildings
    call SetBuildUnit( 1, KEEP )
    call SetBuildUnit( 1, BLACKSMITH )
    call SetBuildUnit( 1, ARCANE_SANCTUM )
    call SetBuildUnit( 4, HOUSE )
    call SetBuildUnit( 5, PEASANT )
    call SetBuildUnit( 4, 'n00F' ) // Human Citizen (Female)
    call SetBuildUnit( 4, WATCH_TOWER )
    // Tier 3 buildings
    call SetBuildUnit( 1, CASTLE )
    call SetBuildUnit( 1, AVIARY )
    call SetBuildUnit( 5, HOUSE )
    call SetBuildUnit( 2, ARCANE_TOWER )
    call SetBuildUnit( 2, GUARD_TOWER )
    call SetBuildUnit( 4, 'n00E' ) // Human Citizen (Male)
    call SetBuildUnit( 20, PEASANT )
    call SetBuildUnit( 5, 'h00R' ) // Human Housing
    // **********************************
    // *    End Building Strategy       *
    // **********************************
endfunction
//===========================================================================
// Basic attack functionality
//===========================================================================
function AttackTarget takes unit target, boolean addAlliance returns nothing
    if (target == null) then
        return
    endif
    if (addAlliance) then
        call SetAllianceTarget( target )
    endif
    call FormGroup( 3, true )
    call AttackMoveKillA( target )
    if (not addAlliance) then
        call SetAllianceTarget( null )
    endif
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 (target == null) then
        set target = GetAllianceTarget()
        if (target != null) then
            set setAlly = false
        endif
    endif
    // Target Priority #2
    if (target == null) then
        set target = GetExpansionFoe()
        if (target != null) then
            set take_exp = false
        endif
    endif
    // Target Priority #3
    if (target == null) then
        set target = GetMegaTarget()
    endif
    // Target Priority #4
    if (target == null) then
        set target = GetEnemyExpansion()
    endif
    // Target Priority #5
    if (target == null) then
        set target = GetEnemyExpansion()
        if (target == null) then
            call StartGetEnemyBase(  )
            loop
                exitwhen (not WaitGetEnemyBase())
                call SuicideSleep( 1 )
            endloop
            set target = GetEnemyBase()
        endif
    endif
    // Target Priority #6
    if (target == null) then
        set target = GetCreepCamp( 0, 9, false )
    endif
    // Target Priority #7
    if (target == null) then
        set target = GetCreepCamp( 10, 100, true )
    endif
    // Attack the target and increment attack wave
    if (target != null) then
        call AttackTarget( target, setAlly )
    else
        // If no target was found, sleep a bit before trying again
        call Sleep( 20 )
    endif
endfunction
function AttackWaves takes nothing returns nothing
    //*** WAVE 1 ***
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 3, 3, FOOTMAN )
    call CampaignAttackerEx( 0, 1, 2, RIFLEMAN )
    call Sleep( M3 ) // Waits 3 minutes before attacking
    call LaunchAttack()
    //*** WAVE 1 ***
    call InitAssaultGroup()
    call CampaignAttackerEx( 2, 3, 3, FOOTMAN )
    call CampaignAttackerEx( 0, 1, 2, RIFLEMAN )
    call LaunchAttack()
    //*** WAVE 2 *** Between 2 or 3 minutes after Wave 1
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call LaunchAttack()
    //*** WAVE 3 *** Between 2 or 3 minutes after Wave 2
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call CampaignAttackerEx( 1, 2, 3, KNIGHT )
    call LaunchAttack()
    //*** WAVE 4 *** Between 2 or 3 minutes after Wave 3
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call CampaignAttackerEx( 1, 2, 3, KNIGHT )
    call CampaignAttackerEx( 1, 2, 3, MORTAR )
    call LaunchAttack()
    //*** WAVE 5 *** Between 2 or 3 minutes after Wave 4
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call CampaignAttackerEx( 1, 2, 3, KNIGHT )
    call CampaignAttackerEx( 1, 2, 3, MORTAR )
    call CampaignAttackerEx( 2, 2, 3, PRIEST )
    call LaunchAttack()
    //*** WAVE 6 *** Between 2 or 3 minutes after Wave 5
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call CampaignAttackerEx( 1, 2, 3, KNIGHT )
    call CampaignAttackerEx( 1, 2, 3, MORTAR )
    call CampaignAttackerEx( 2, 2, 3, PRIEST )
    call LaunchAttack()
    //*** WAVE 7 *** Between 2 or 3 minutes after Wave 6
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call CampaignAttackerEx( 1, 2, 3, KNIGHT )
    call CampaignAttackerEx( 1, 2, 3, MORTAR )
    call CampaignAttackerEx( 2, 2, 3, PRIEST )
    call CampaignAttackerEx( 2, 2, 3, SORCERESS )
    call LaunchAttack()
    //*** WAVE 8 *** Between 2 or 3 minutes after Wave 7
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call CampaignAttackerEx( 1, 2, 3, KNIGHT )
    call CampaignAttackerEx( 1, 2, 3, MORTAR )
    call CampaignAttackerEx( 2, 2, 3, PRIEST )
    call CampaignAttackerEx( 2, 2, 3, SORCERESS )
    call CampaignAttackerEx( 2, 2, 3, SPELL_BREAKER )
    call LaunchAttack()
    //*** WAVE 9 *** Between 2 or 3 minutes after Wave 8
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call CampaignAttackerEx( 1, 2, 3, KNIGHT )
    call CampaignAttackerEx( 1, 2, 3, MORTAR )
    call CampaignAttackerEx( 2, 2, 3, PRIEST )
    call CampaignAttackerEx( 2, 2, 3, SORCERESS )
    call CampaignAttackerEx( 2, 2, 3, SPELL_BREAKER )
    call CampaignAttackerEx( 2, 2, 3, GRYPHON )
    call LaunchAttack()
    //*** WAVE 10 *** Between 2 or 3 minutes after Wave 9
    call InitAssaultGroup()
    call CampaignAttackerEx( 3, 4, 4, FOOTMAN )
    call CampaignAttackerEx( 1, 2, 3, RIFLEMAN )
    call CampaignAttackerEx( 1, 2, 3, KNIGHT )
    call CampaignAttackerEx( 1, 2, 3, MORTAR )
    call CampaignAttackerEx( 2, 2, 3, PRIEST )
    call CampaignAttackerEx( 2, 2, 3, SORCERESS )
    call CampaignAttackerEx( 2, 2, 3, SPELL_BREAKER )
    call CampaignAttackerEx( 2, 2, 3, GRYPHON )
    call CampaignAttackerEx( 2, 2, 3, TANK )
    call LaunchAttack()
    loop //Init the infinite attack loop
        //*** WAVE 11 *** Between 2 or 3 minutes after Wave 10
        call InitAssaultGroup()
        call CampaignAttackerEx( 20, 30, 40, FOOTMAN )
        call CampaignAttackerEx( 10, 20, 30, RIFLEMAN )
        call CampaignAttackerEx( 10, 20, 30, KNIGHT )
        call CampaignAttackerEx( 10, 20, 30, MORTAR )
        call CampaignAttackerEx( 10, 20, 30, PRIEST )
        call CampaignAttackerEx( 10, 20, 30, SORCERESS )
        call CampaignAttackerEx( 10, 20, 30, SPELL_BREAKER )
        call CampaignAttackerEx( 10, 20, 30, GRYPHON )
        call CampaignAttackerEx( 10, 20, 30, TANK )
        call LaunchAttack()
    endloop
endfunction
function main takes nothing returns nothing
    call CampaignAI( HOUSE, function ChooseHeroSkill )
    call ConfigureAI( )
    call BuildingStrategy( )
    call AttackWaves( )
endfunction
Sry for all the questions but I am new to this topic and I am really trying to improve the AI in my map.
 
@Nowow might have better insight.

For the gold issue, make sure to set lowered income or something similar (can't remember exact term) to false.
For Town Hall items, I haven't recall such behavior in melee games, so I have doubt the AI actually use them at all (a map of mine also shows no behavior of AI using town hall item)
 
Level 25
Joined
Feb 2, 2006
Messages
1,689
Thx, I found the call in CampaignAI:
JASS:
call SetSlowChopping(true)
I set it to false now.
For the town hall issue, I have created a workaround now which respawns a town hall and gives the player some start gold to build a worker whenever the last main building is destroyed and he has either no workers anymore or not enough gold to build a town hall.
I also recognize when worker units die etc.
The tiny item stuff would have been cooler but anyway it has to work.

So the remaining issue is why the computer attacks with units one by one.

Besides, I looked into commons.ai and a campaign AI uses GetGameDifficulty() to distinguish between easy, normal and hard. Is this the campaign difficulty and NOT the difficulty of the single player who uses the AI?
I guess that is MeleeDifficulty? So when I have multiple AI enemies with different difficulties, they will all use the map difficulty which I cant change when hosting the game?

Btw. does somebody have the latest common.ai file? I would like to use a modified version.

edit:
For the one by one attacking issue: Maybe it happens because I have no sleeps before calling LaunchAttack. I will add waiting two minutes maybe.
 
Last edited:
Level 12
Joined
Jun 15, 2016
Messages
472
I'll try to clear up as many things as I can about the subject, do tell if I miss anything:

0. There are some resources you can use for building and expanding your AI. You've already deduced (correctly) that .ai files use only common.j and common.ai, so your best source for starters is a reference of common.ai. The main benefit of this file is that you can see all of the native AI functions. This file is from 2005 (and has a little WIP reminder in SleepInCombat :grin:), and was the latest up until 1.29 I think. If there's a newer version for reforged or later patches please upload it here for inspection.

Apart from that, you can check out a compiled list of sources about AI. You might find Michael Peppers' AI template for custom races useful. You might also want to contact @Unregret (if he's active), @ZiBitheWand3r3r and @Wa666r : all three created a massive project called Ultimate Battle, which allows users to play as many as 20 races. Most races have AI support, so you might want to see how they do that.

1. AI heroes' use of items: it can't really be controlled by the AI. Maybe there's some unknown common.j function which can do something about it. For more information, you might try the members of ultimate battle. They wanted to do something like this.

2.
It says that campaign AIs are much simpler for the beginning.
This is a misunderstanding: it's not that putting the SetCampaignAI flag at the start of your script makes it somehow easier or less error prone. It's just that, generally, when making an AI for a campaign you can consider less things and make it "more stupid" then a melee AI.

3. Attacking enemy players in an alliance based/ changing environment: you really should check the attack function of melee AIs, the one created by the editor and the vanilla AI for one of the 4 races. You can see they all use the SingleMeleeAttack function from the common.ai, which internally chooses an enemy in random using GetEnemyBase and GetEnemyExpansion. These might allow you to change allegiances during the game without interrupting the AI in too much.
Another thing you'll want to look out for is alliance targets and mega targets. Alliance targets can be get and set in the AI. When a computer player sets an alliance target, you the human player can see it as a ping on the map, while different computer players can get the alliance target and attack the same thing. Mega targets should act similarly, but I haven't tested it so can't verify for sure. You can't set mega targets, you can only get them, and you need to specify you're searching for mega targets before getting one (using SetWatchMegaTargets), so I'm not sure where and when they pop-up, but this should set an undefended base as the target.

4. Setting AI heroes is done at the start of the map using the functions PickMeleeHero (in common.ai) and set_skills (not in common.ai). Both update internal variables and arrays which the AI uses later when necessary. You can set these to what you want to support one to three heroes, but this should be at the start of the match.

5. Your player attacks with units one by one because you don't give FormGroup enough time to set the attack group up. You can read how CommonSuicideOnPlayer works as an example for correct set up of an attack wave here. A big takeaway is that: a. groups really should have at least 2-3 minutes to set up, b. both melee and campaign AI scripts use a global variable called sleep_second to manage attack timing.

6. In normal scripts, melee difficulty is decided at the start of the match in the function StandardAI, and it is decided by MeleeDifficulty like you thought. You might be able to change the melee difficulty with some common.j function, but it won't affect your entire script, only a part of it. You can still do that, and it will affect some of the built in functions in AI scripts, but I suggest you replace/supplement this with an AI command to detect and adapt to changes from the map better.

This should cover most of it, good luck!
 
Status
Not open for further replies.
Top