• 🏆 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!

[AI] [] I just don't know how to stop it from crashing

Status
Not open for further replies.
Level 18
Joined
Mar 16, 2008
Messages
721
EDIT SOLVED: computer player was trying to expand to an island, crashing game. i fixed by making island goldmines not have "gold mine" ability then give them that ability during the game, so players can use but AI players won't detect those goldmines.

UPDATE: it did crash on another test but i also changed some other things in the script. really confused how it ran that long without crashing... still trying to get to the bottom this...

I've tried so many things, adding so many conditions, checking if any impossible orders are given, turning off all AI related triggers, flattening all terrain and removing doodads. I'm just not sure what else to try at this point.

I would say there's a techtree violation somewhere in the build priorities however this script has successfully ran for over 3 hours, completing its build priority list. Most of the time it crashes after 2 hours. Sometimes it crashes in as little as 40 minutes, one time in 4 minutes. Some games the AIs kill eachother after a few hours, ending the game before it can crash.

If anyone could even give me a a theory on what might cause the crash, something I could try to test to rule out, I'd be grateful. Because I've ran out of ideas to test. At this point I'm just reading over the script and I don't see anything wrong.

JASS:
//===========================================================================
//
// King AI
//
//===========================================================================

//***************************************************************************
//*
//*  Global Variables
//*
//***************************************************************************

globals
    integer                 Tier                       = 0
    integer                 attackWave                 = 0
    integer                 nextDelay                  = 0
    integer                 awGold                     = 0
    integer                 awWood                     = 0
/// Conditions
    boolean                 gCond_Need_Town_Hall       = false
    boolean                 gCond_Needs_Garrison       = false
    boolean                 gCond_Needs_Stable         = false
    boolean                 gCond_Needs_Farm           = false
    boolean                 gCond_Need_Guard_Tower     = false
    boolean                 gCond_Need_Research_Center = false
    boolean                 gCond_Need_LM              = false
    boolean                 gCond_Need_BS              = false
    boolean                 gCond_Need_Market          = false
    boolean                 gCond_Need_WizTower        = false
/// Units
/// King Heroes
    constant integer        SOVEREIGN                  = 'H01L'
    constant integer        MONARCH                    = 'HC18'
    constant integer        MAJESTY                    = 'H03I'
/// Prince Heroes
    /// constant integer    PALADIN                    = 'Hpal'
    constant integer        MAGE                       = 'H01G'
    constant integer        WARRIOR                    = 'H03J'
/// King Hero Abilities
    constant integer        ROYAL_BANISH               = 'A006'
    constant integer        KING_AURA                  = 'A00K'
    constant integer        KING_COURT                 = 'A00C'
    /// constant integer    THUNDER_CLAP               = 'AHtc' /// Sovereign only / "Royal Revenge"
    constant integer        ROYAL_SHOCKWAVE            = 'A06G' /// Monarch only
    constant integer        ROYAL_BLIZZARD             = 'A06H' /// Majesty only
    constant integer        ROYAL_SUMMON               = 'A01H' /// based on blizzard spell so ais tend to use improperly
/// Prince Hero Abilities
    /// constant integer    HOLY_BOLT                  = 'AHhb' /// Holy Light / "Royal Blessing"
    /// constant integer    BLINK                      = 'AEbl' /// Royal Mage only
    /// constant integer    DIVINE_SHIELD              = 'AHds' /// Royal Pally only
    /// constant integer    DEVOTION_AURA              = 'AHad'
    constant integer        BLESSED_FIELD              = 'A077' /// Royal Pally only
    /// constant integer    MASS_TELEPORT              = 'AHmt' /// Royal Mage only
    constant integer        ATTRIBUTE_BONUS            = 'Aamk'
    constant integer        FERAL_IMPACT               = 'A01U' /// Royal Warrior only
    constant integer        CRIT_STRIKE                = 'A06K' /// Royal Warrior only
    constant integer        ROYAL_AVATAR               = 'A06I' /// Royal Warrior only
    constant integer        ROYAL_BLADESTORM           = 'A06J' /// Royal Warrior only
/// Buildings
    constant integer        TOME_LIBRARY               = 'nC03'
    constant integer        KING_VAULT                 = 'nC02'
    constant integer        ROYAL_ALTAR                = 'h01N'
    constant integer        FRUIT_STAND                = 'nfrt'
    constant integer        MERC_CAMP                  = 'nmer'
    constant integer        ANTICAMP_TOWER             = 'nC51'
    /// constant integer    CASTLE                     = 'hcas'
    /// constant integer    TOWN_HALL                  = 'htow'
    /// constant integer    LUMBER_MILL                = 'hlum'
    /// constant integer    BLACKSMITH                 = 'hbla'
    constant integer        ROYAL_BARRACKS             = 'h02P'
    constant integer        FARM_1                     = 'h003'
    constant integer        FARM_2                     = 'h005'
    constant integer        FARM_3                     = 'h006'
    constant integer        FARM_4                     = 'h007'
    constant integer        FARM_5                     = 'h00S'
/// constant integer        BARRACKS                   = 'hbar' /// "Stables" (tier 1)
    constant integer        STABLES_2                  = 'h00A'
    constant integer        STABLES_3                  = 'h00B'
    constant integer        STABLES_4                  = 'h00K'
    constant integer        GARRISON_1                 = 'h00F'
    constant integer        GARRISON_2                 = 'h00G'
    constant integer        GARRISON_3                 = 'h00H'
    constant integer        ROYAL_FACTORY              = 'h01F'
    constant integer        RESEARCH_CENTER_1          = 'h01H'
    constant integer        RESEARCH_CENTER_2          = 'h02Q'
    constant integer        FOUNTAIN                   = 'n020'
    constant integer        ROYAL_SHIPYARD             = 'h01S'
    constant integer        STONEMASON_TOWER           = 'h02Y'
    constant integer        ROYAL_WORKSHOP             = 'h02Z'
    constant integer        FARM_COTTAGE               = 'h034'
/// constant integer        SANCTUM                    = 'hars' /// "Tower of Wizardry"
    constant integer        TOWER_WIZARDRY_2_BUILT     = 'h031'
    constant integer        TOWER_WIZARDRY_2_UPGRADE   = 'h03H'
    constant integer        ROYAL_CHURCH               = 'h01A'
/// constant integer        ARCANE_VAULT               = 'hvlt' /// "Royal Market" (tier 1)
    constant integer        ROYAL_MARKET_2             = 'h01K'
    constant integer        BASIC_TOWER                = 'h00R'
/// constant integer        GUARD_TOWER                = 'hgtw' /// Tier 1 Guard Tower
    constant integer        GUARD_TOWER_3              = 'h00P'
/// constant integer        CANNON_TOWER               = 'hctw' /// "Cannon Tower" (tier 1)
    constant integer        CANNON_TOWER_2             = 'h033'
/// constant integer        ARCANE_TOWER               = 'hatw' /// "Arcane Tower" (tier 1)
    constant integer        ARCANE_TOWER_2             = 'h018'
    constant integer        ARCANE_TOWER_3             = 'h02V'
    constant integer        TOWER_COLD_1               = 'ndt1'
    constant integer        TOWER_COLD_2               = 'ndt2'
    constant integer        TOWER_COLD_3               = 'n01H'
    constant integer        TOWER_COLD_4               = 'n01I'
    constant integer        TOWER_FLAME_1              = 'nft1'
    constant integer        TOWER_FLAME_2              = 'nft2'
    constant integer        TOWER_FLAME_3              = 'n029'
    constant integer        TOWER_FLAME_4              = 'n02A'
    constant integer        TOWER_FLAME_5              = 'n02B'
    constant integer        TOWER_FLAME_6              = 'n02C'
    constant integer        TOWER_ROYAL_1              = 'hC17'
    constant integer        TOWER_ROYAL_2              = 'h000'
    constant integer        TOWER_ROYAL_3              = 'h001'
    constant integer        TOWER_ROYAL_4              = 'h00C'
    constant integer        TOWER_ROYAL_SKY            = 'n00D'
/// Units
/// constant integer        PEASANT                    = 'hpea'
    constant integer        ROYAL_ENGINEER             = 'n004'
    constant integer        ROYAL_ENGINEER_2           = 'n01L'
/// constant integer        FOOTMAN                    = 'hfoo' /// "Crusader"
/// constant integer        KNIGHT                     = 'hkni' /// "Royal Knight" (tier 1)
/// constant integer        MORTAR                     = 'hmtm' /// "Royal Cannon"
/// constant integer        PRIEST                     = 'hmpr'
/// constant integer        SORCERESS                  = 'hsor' /// "Anime Witch"
/// constant integer        HIGH_FOOTMAN               = 'hcth' /// "Field Captain"
/// constant integer        HIGH_ARCHER                = 'nhea' /// "Archer"
/// constant integer        PRIEST                     = 'hmpr'
    constant integer        CHAPLAIN                   = 'nchp'
    constant integer        HYDROMANCER                = 'nhym'
    constant integer        PACK_HORSE                 = 'hrdh'
    constant integer        CRUSADER_DOTA              = 'h01I'
    constant integer        SQUIRE                     = 'h00J'
    constant integer        ROGUE                      = 'h00Z'
    constant integer        NINJA                      = 'h02W'
    constant integer        EXEMPLAR                   = 'h00M'
    constant integer        JUSTICAR                   = 'h00I'
    constant integer        QUESTIONER                 = 'h01Q'
    constant integer        GENERAL                    = 'h004'
/// constant integer        KNIGHT                     = 'hkni' /// "Royal Knight" (tier 1)
    constant integer        ROYAL_KNIGHT_DOTA          = 'h01J'
    constant integer        ROYAL_KNIGHT_2             = 'h008'
    constant integer        ROYAL_KNIGHT_3             = 'h009'
    constant integer        ROYAL_KNIGHT_4             = 'h00L'
    constant integer        OUTRIDER                   = 'h03O'
    constant integer        OUTRIDER_ROYAL             = 'h01O'
    constant integer        BIG_BERTHA_DOTA            = 'h01U'
    constant integer        BIG_BERTHA                 = 'h035'
    constant integer        BISHOP                     = 'h02R'
    constant integer        ROYAL_PRIEST               = 'hC41'
    constant integer        WIZARD                     = 'h01D'
    constant integer        WIZARD_DOTA                = 'h01T'
    constant integer        WIZARD_CHAOS               = 'h032'
    constant integer        ROYAL_MAGUS                = 'h02X'
    constant integer        DOZER                      = 'h030'
    constant integer        ARCHER_DOTA                = 'n00E'
    constant integer        SPEAR_MERC                 = 'n00A'
    constant integer        LANCER                     = 'n00H'
    constant integer        SPEAR_MAN                  = 'n009'
    constant integer        ROYAL_SPEAR                = 'n00J'
    constant integer        ROYAL_ZEP                  = 'n00B'
/// Upgrades
/// constant integer        UPG_MELEE                  = 'Rhme'
/// constant integer        UPG_ARMOR                  = 'Rhar'
/// constant integer        UPG_MASONRY                = 'Rhac'
/// constant integer        UPG_WOOD                   = 'Rhlh'
/// constant integer        UPG_SENTINEL               = 'Rhse' /// "Magic Sentry"
/// constant integer        UPG_SORCERY                = 'Rhst'
/// constant integer        UPG_PRAYING                = 'Rhpt' /// "Priest Training"
/// constant integer        UPG_BREEDING               = 'Rhan' /// "Animal War Training"
    constant integer        UPG_ROGUE                  = 'R001'
    constant integer        UPG_MAGIC_DAMAGE           = 'R008'
    constant integer        UPG_DOTA_ARCHER            = 'R003'
    constant integer        UPG_DOTA_CANNON            = 'R006'
    constant integer        UPG_DOTA_WIZARD            = 'R005'
    constant integer        UPG_DOTA_KNIGHT            = 'R002'
/// constant integer        UPG_LEATHER                = 'Rhla' /// "Gyrations"
/// constant integer        UPG_RANGED                 = 'Rhra'
endglobals

//***************************************************************************
//*
//*  Utility Functions
//*
//***************************************************************************

//===========================================================================
function CheckLastCommand takes boolean pop returns integer
    local integer cmd = GetLastCommand()
    if (pop) then
        call PopLastCommand(  )
    endif
    return cmd
endfunction

//===========================================================================
function CheckLastCommandData takes boolean pop returns integer
    local integer data = GetLastData()
    if (pop) then
        call PopLastCommand(  )
    endif
    return data
endfunction

//===========================================================================
function TotalFoodProduced takes nothing returns integer
    return GetPlayerState(ai_player,PLAYER_STATE_RESOURCE_FOOD_CAP)
endfunction

//===========================================================================
function ExpansionNeeded takes nothing returns boolean
    return take_exp
endfunction

//===========================================================================
function BuildExpansion takes integer hallID, integer mineID returns nothing
    if (HallsCompleted(hallID)) then
        call SetBuildExpa( TownCount(hallID) + 1, mineID )
    endif
endfunction

//===========================================================================
function CurrentAttackWave takes nothing returns integer
    return attackWave
endfunction

//===========================================================================
function ResetAttackUnits takes nothing returns nothing
    set awGold = 0
    set awWood = 0
    call InitAssaultGroup(  )
endfunction

//===========================================================================
function AddAttackUnit takes integer minQty, integer maxQty, integer unitID returns nothing
    // Track attacking gold workers
    if (unitID == 'hpea') then
        set awGold = awGold + minQty
    endif

    // Track attacking wood workers
    if (unitID == 'hpea') then
        set awWood = awWood + minQty
    endif

    call SetAssaultGroup( minQty, maxQty, unitID )
endfunction

//***************************************************************************
//*
//*  Basic Options
//*
//***************************************************************************

//===========================================================================
function InitOptions takes nothing returns nothing
    call SetMeleeAI(  )
    call SetDefendPlayer( true )
    call SetRandomPaths( true )
    call SetTargetHeroes( true )
    call SetPeonsRepair( true )
    call SetHeroesFlee( true )
    call SetHeroesBuyItems( true )
    call SetUnitsFlee( true )
    call SetGroupsFlee( true )
    call SetWatchMegaTargets( true )
    call SetIgnoreInjured( true )
    call SetHeroesTakeItems( true )
    call SetSlowChopping( false )
    call SetCaptainChanges( true )
    call SetSmartArtillery( true )
endfunction

//***************************************************************************
//*
//*  Conditions
//*
//***************************************************************************

//===========================================================================
// Updates the values of all preset conditions
//===========================================================================
function UpdateConditions takes nothing returns nothing
    set gCond_Need_Town_Hall = ( ( GetUnitCount( TOWN_HALL ) ) < 3 )
    set gCond_Need_LM = ( ( GetUnitCount( LUMBER_MILL ) ) < 1 )
    set gCond_Need_BS = ( ( GetUnitCount( BLACKSMITH ) ) < 2 )
    set gCond_Need_Market = ( (GetUnitCount( ARCANE_VAULT ) ) < 1 )
    set gCond_Need_WizTower = ( (GetUnitCount( SANCTUM ) + (GetUnitCount( TOWER_WIZARDRY_2_UPGRADE ) ) < 1 ) )
    set gCond_Needs_Garrison = ( ( GetUnitCount( GARRISON_1 ) + ( GetUnitCount( GARRISON_2 ) + GetUnitCount( GARRISON_3 ) ) ) == 0 )
    set gCond_Needs_Stable = ( ( ( GetUnitCount( BARRACKS ) + GetUnitCount( STABLES_2 ) ) + ( GetUnitCount( STABLES_3 ) + GetUnitCount( STABLES_4 ) ) ) == 0 )
    set gCond_Needs_Farm = ( ( ( ( GetUnitCount( FARM_1 ) + GetUnitCount( FARM_2 ) ) + GetUnitCount( FARM_3 ) ) + ( GetUnitCount( FARM_4 ) + GetUnitCount( FARM_5 ) ) ) < 6 )
    set gCond_Need_Guard_Tower = ( ( GetUnitCount( BASIC_TOWER ) + GetUnitCount( GUARD_TOWER ) ) < 2 )
    set gCond_Need_Research_Center = ( ( GetUnitCount( RESEARCH_CENTER_1 ) + GetUnitCount( RESEARCH_CENTER_2 ) ) == 0 )   
endfunction

//***************************************************************************
//*
//*  Heroes
//*
//***************************************************************************

//===========================================================================
// Stores hero ID and skills
//===========================================================================
function SetHero takes integer order, integer heroid returns nothing
    if (order == 1) then
        set hero_id = heroid
        if (heroid == SOVEREIGN) then
            set skills1[ 1] = KING_AURA         /// 1
            set skills1[ 2] = THUNDER_CLAP      /// 1
            set skills1[ 3] = KING_COURT        /// 1
            set skills1[ 4] = ROYAL_BANISH      /// 1
            set skills1[ 5] = KING_AURA         /// 2
            set skills1[ 6] = THUNDER_CLAP      /// 2
            set skills1[ 7] = KING_COURT        /// 2
            set skills1[ 8] = ROYAL_BANISH      /// 2
            set skills1[ 9] = KING_AURA         /// 3
            set skills1[10] = THUNDER_CLAP      /// 3
            set skills1[11] = KING_COURT        /// 3
            set skills1[12] = KING_AURA         /// 4
            set skills1[13] = KING_COURT        /// 4
            set skills1[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MONARCH) then
            set skills1[ 1] = KING_AURA         /// 1
            set skills1[ 2] = ROYAL_SHOCKWAVE   /// 1
            set skills1[ 3] = KING_COURT        /// 1
            set skills1[ 4] = ROYAL_BANISH      /// 1
            set skills1[ 5] = KING_AURA         /// 2
            set skills1[ 6] = ROYAL_SHOCKWAVE   /// 2
            set skills1[ 7] = KING_COURT        /// 2
            set skills1[ 8] = ROYAL_BANISH      /// 2
            set skills1[ 9] = KING_AURA         /// 3
            set skills1[10] = ROYAL_SHOCKWAVE   /// 3
            set skills1[11] = KING_COURT        /// 3
            set skills1[12] = KING_AURA         /// 4
            set skills1[13] = KING_COURT        /// 4
            set skills1[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MAJESTY) then
            set skills1[ 1] = KING_AURA         /// 1
            set skills1[ 2] = ROYAL_BLIZZARD    /// 1
            set skills1[ 3] = KING_COURT        /// 1
            set skills1[ 4] = ROYAL_BANISH      /// 1
            set skills1[ 5] = KING_AURA         /// 2
            set skills1[ 6] = ROYAL_BLIZZARD    /// 2
            set skills1[ 7] = KING_COURT        /// 2
            set skills1[ 8] = ROYAL_BANISH      /// 2
            set skills1[ 9] = KING_AURA         /// 3
            set skills1[10] = ROYAL_BLIZZARD    /// 3
            set skills1[11] = KING_COURT        /// 3
            set skills1[12] = KING_AURA         /// 4
            set skills1[13] = KING_COURT        /// 4
            set skills1[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MAGE) then
            set skills1[ 1] = DEVOTION_AURA
            set skills1[ 2] = HOLY_BOLT
            set skills1[ 3] = BLINK
            set skills1[ 4] = DEVOTION_AURA
            set skills1[ 5] = HOLY_BOLT
            set skills1[ 6] = MASS_TELEPORT
            set skills1[ 7] = DEVOTION_AURA
            set skills1[ 8] = HOLY_BOLT
            set skills1[ 9] = BLINK
            set skills1[10] = BLINK
            set skills1[11] = ATTRIBUTE_BONUS
            set skills1[12] = ATTRIBUTE_BONUS
            set skills1[13] = ATTRIBUTE_BONUS
            set skills1[14] = ATTRIBUTE_BONUS
            set skills1[15] = ATTRIBUTE_BONUS
        elseif (heroid == PALADIN) then
            set skills1[ 1] = DEVOTION_AURA
            set skills1[ 2] = HOLY_BOLT
            set skills1[ 3] = DIVINE_SHIELD
            set skills1[ 4] = DEVOTION_AURA
            set skills1[ 5] = HOLY_BOLT
            set skills1[ 6] = DIVINE_SHIELD
            set skills1[ 7] = DEVOTION_AURA
            set skills1[ 8] = HOLY_BOLT
            set skills1[ 9] = DIVINE_SHIELD
            set skills1[10] = BLESSED_FIELD
            set skills1[11] = ATTRIBUTE_BONUS
            set skills1[12] = ATTRIBUTE_BONUS
            set skills1[13] = ATTRIBUTE_BONUS
            set skills1[14] = ATTRIBUTE_BONUS
            set skills1[15] = ATTRIBUTE_BONUS
            set skills1[16] = BLESSED_FIELD
        elseif (heroid == WARRIOR) then
            set skills1[ 1] = DEVOTION_AURA
            set skills1[ 2] = ROYAL_BLADESTORM
            set skills1[ 3] = CRIT_STRIKE
            set skills1[ 4] = DEVOTION_AURA
            set skills1[ 5] = ROYAL_BLADESTORM
            set skills1[ 6] = CRIT_STRIKE
            set skills1[ 7] = DEVOTION_AURA
            set skills1[ 8] = ROYAL_BLADESTORM
            set skills1[ 9] = CRIT_STRIKE
            set skills1[10] = ROYAL_AVATAR
        endif
    elseif (order == 2) then
        set hero_id2 = heroid
        if (heroid == SOVEREIGN) then
            set skills2[ 1] = KING_AURA         /// 1
            set skills2[ 2] = THUNDER_CLAP      /// 1
            set skills2[ 3] = KING_COURT        /// 1
            set skills2[ 4] = ROYAL_BANISH      /// 1
            set skills2[ 5] = KING_AURA         /// 2
            set skills2[ 6] = THUNDER_CLAP      /// 2
            set skills2[ 7] = KING_COURT        /// 2
            set skills2[ 8] = ROYAL_BANISH      /// 2
            set skills2[ 9] = KING_AURA         /// 3
            set skills2[10] = THUNDER_CLAP      /// 3
            set skills2[11] = KING_COURT        /// 3
            set skills2[12] = KING_AURA         /// 4
            set skills2[13] = KING_COURT        /// 4
            set skills2[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MONARCH) then
            set skills2[ 1] = KING_AURA         /// 1
            set skills2[ 2] = ROYAL_SHOCKWAVE   /// 1
            set skills2[ 3] = KING_COURT        /// 1
            set skills2[ 4] = ROYAL_BANISH      /// 1
            set skills2[ 5] = KING_AURA         /// 2
            set skills2[ 6] = ROYAL_SHOCKWAVE   /// 2
            set skills2[ 7] = KING_COURT        /// 2
            set skills2[ 8] = ROYAL_BANISH      /// 2
            set skills2[ 9] = KING_AURA         /// 3
            set skills2[10] = ROYAL_SHOCKWAVE   /// 3
            set skills2[11] = KING_COURT        /// 3
            set skills2[12] = KING_AURA         /// 4
            set skills2[13] = KING_COURT        /// 4
            set skills2[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MAJESTY) then
            set skills2[ 1] = KING_AURA         /// 1
            set skills2[ 2] = ROYAL_BLIZZARD    /// 1
            set skills2[ 3] = KING_COURT        /// 1
            set skills2[ 4] = ROYAL_BANISH      /// 1
            set skills2[ 5] = KING_AURA         /// 2
            set skills2[ 6] = ROYAL_BLIZZARD    /// 2
            set skills2[ 7] = KING_COURT        /// 2
            set skills2[ 8] = ROYAL_BANISH      /// 2
            set skills2[ 9] = KING_AURA         /// 3
            set skills2[10] = ROYAL_BLIZZARD    /// 3
            set skills2[11] = KING_COURT        /// 3
            set skills2[12] = KING_AURA         /// 4
            set skills2[13] = KING_COURT        /// 4
            set skills2[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MAGE) then
            set skills2[ 1] = DEVOTION_AURA
            set skills2[ 2] = HOLY_BOLT
            set skills2[ 3] = BLINK
            set skills2[ 4] = DEVOTION_AURA
            set skills2[ 5] = HOLY_BOLT
            set skills2[ 6] = MASS_TELEPORT
            set skills2[ 7] = DEVOTION_AURA
            set skills2[ 8] = HOLY_BOLT
            set skills2[ 9] = BLINK
            set skills2[10] = BLINK
            set skills2[11] = ATTRIBUTE_BONUS
            set skills2[12] = ATTRIBUTE_BONUS
            set skills2[13] = ATTRIBUTE_BONUS
            set skills2[14] = ATTRIBUTE_BONUS
            set skills2[15] = ATTRIBUTE_BONUS
        elseif (heroid == PALADIN) then
            set skills2[ 1] = DEVOTION_AURA
            set skills2[ 2] = HOLY_BOLT
            set skills2[ 3] = DIVINE_SHIELD
            set skills2[ 4] = DEVOTION_AURA
            set skills2[ 5] = HOLY_BOLT
            set skills2[ 6] = DIVINE_SHIELD
            set skills2[ 7] = DEVOTION_AURA
            set skills2[ 8] = HOLY_BOLT
            set skills2[ 9] = DIVINE_SHIELD
            set skills2[10] = BLESSED_FIELD
            set skills2[11] = ATTRIBUTE_BONUS
            set skills2[12] = ATTRIBUTE_BONUS
            set skills2[13] = ATTRIBUTE_BONUS
            set skills2[14] = ATTRIBUTE_BONUS
            set skills2[15] = ATTRIBUTE_BONUS
            set skills2[16] = BLESSED_FIELD
        elseif (heroid == WARRIOR) then
            set skills2[ 1] = DEVOTION_AURA
            set skills2[ 2] = ROYAL_BLADESTORM
            set skills2[ 3] = CRIT_STRIKE
            set skills2[ 4] = DEVOTION_AURA
            set skills2[ 5] = ROYAL_BLADESTORM
            set skills2[ 6] = CRIT_STRIKE
            set skills2[ 7] = DEVOTION_AURA
            set skills2[ 8] = ROYAL_BLADESTORM
            set skills2[ 9] = CRIT_STRIKE
            set skills2[10] = ROYAL_AVATAR
            set skills2[20] = ROYAL_AVATAR
        endif
    endif
endfunction

//===========================================================================
// Selects hero IDs for three possible heroes
//===========================================================================
function SelectHeroes takes nothing returns nothing
    local integer roll = GetRandomInt(1,9)
    if (roll == 1) then
        call SetHero( 1, MAGE )
        call SetHero( 2, SOVEREIGN )
    elseif (roll == 2) then
        call SetHero( 1, PALADIN )
        call SetHero( 2, SOVEREIGN )
    elseif (roll == 3) then
        call SetHero( 1, WARRIOR )
        call SetHero( 2, SOVEREIGN )
    elseif (roll == 4) then
        call SetHero( 1, MAGE )
        call SetHero( 2, MONARCH )
    elseif (roll == 5) then
        call SetHero( 1, PALADIN )
        call SetHero( 2, MONARCH )
    elseif (roll == 6) then
        call SetHero( 1, WARRIOR )
        call SetHero( 2, MONARCH )
    elseif (roll == 7) then
        call SetHero( 1, MAGE )
        call SetHero( 2, MAJESTY )
    elseif (roll == 8) then
        call SetHero( 1, PALADIN )
        call SetHero( 2, MAJESTY )
    elseif (roll == 9) then
        call SetHero( 1, WARRIOR )
        call SetHero( 2, MAJESTY )
    endif
endfunction

//===========================================================================
// Returns the hero skill for the given hero and level
//===========================================================================
function ChooseHeroSkill takes nothing returns integer
    local integer curHero = GetHeroId()
    local integer level = GetHeroLevelAI()
    if (level > max_hero_level) then
        set max_hero_level = level
    endif
   
    if (curHero == hero_id) then
        return skills1[level]
    elseif (curHero == hero_id2) then
        return skills2[level]
    endif
    return 0
endfunction

//***************************************************************************
//*
//*  Building and Harvesting
//*
//***************************************************************************

function BuildPriorities takes nothing returns nothing
    local integer mine = TownWithMine()
    call SetBuildAll( BUILD_UNIT, 1, hero_id, -1 )
    call SetBuildAll( BUILD_UNIT, 1, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 1, hero_id2, -1 )
    call SetBuildAll( BUILD_UNIT, 2, PEASANT, -1 )
    if gCond_Need_Town_Hall then
        call BuildExpansion (TOWN_HALL, TOWN_HALL)
    endif
    call SetBuildAll( BUILD_UNIT, 3, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 4, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 5, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 6, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 7, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 8, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 9, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 10, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 11, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 12, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 13, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 14, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 15, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 16, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 17, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 18, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 19, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 20, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 21, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_1, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_BARRACKS, -1 )
/// Build Lumbermill
    if gCond_Need_LM then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, LUMBER_MILL, 1 )
        elseif(( TownHasHall( 2 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, LUMBER_MILL, 2 )
        else
            call SetBuildAll( BUILD_UNIT, 1, LUMBER_MILL, -1 )
        endif
    endif
/// end Build Lumbermill
/// Build Blacksmith
    if gCond_Need_BS then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, BLACKSMITH, 1 )
        elseif (( TownHasHall( 2 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, BLACKSMITH, 2 )
        else
            call SetBuildAll( BUILD_UNIT, 1, BLACKSMITH, -1 )
        endif
    endif
/// end Build Blacksmith
/// Build Market
    if gCond_Need_Market then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, ARCANE_VAULT, 1 )
        elseif (( TownHasHall( 2 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, ARCANE_VAULT, 2 )
        else
            call SetBuildAll( BUILD_UNIT, 1, ARCANE_VAULT, -1 )
        endif
    endif
/// end Build Market
    if (gCond_Need_Guard_Tower) then
        call SetBuildAll( BUILD_UNIT, 1, BASIC_TOWER, -1 )
        call SetBuildAll( BUILD_UNIT, 2, BASIC_TOWER, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, GUARD_TOWER, -1 )
    call SetBuildAll( BUILD_UNIT, 2, GUARD_TOWER, -1 )
/// Build Stables checking at expansion one first
    if (gCond_Needs_Stable) then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, BARRACKS, 2 )
        elseif (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, BARRACKS, 1 )
        else
            call SetBuildAll( BUILD_UNIT, 1, BARRACKS, -1 )
        endif
    endif
/// end build stables
    if (gCond_Needs_Garrison) then
        call SetBuildAll( BUILD_UNIT, 1, GARRISON_1, -1 )
    endif
/// Build Wizard Tower
    if gCond_Need_WizTower then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, SANCTUM, 1 )
        elseif (( TownHasHall( 2 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, SANCTUM, 2 )
        else
            call SetBuildAll( BUILD_UNIT, 1, SANCTUM, -1 )
        endif
    endif
///end build Wizard Tower
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_CHURCH, -1 )
    call SetBuildAll( BUILD_UNIT, 1, HIGH_ARCHER, -1 )
    call SetBuildAll( BUILD_UNIT, 2, HIGH_ARCHER, -1 )
    call SetBuildAll( BUILD_UNIT, 3, HIGH_ARCHER, -1 )
    call SetBuildAll( BUILD_UNIT, 4, HIGH_ARCHER, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 2, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 3, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 4, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 5, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 6, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 1, MORTAR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, CHAPLAIN, -1 )
    call SetBuildAll( BUILD_UNIT, 1, SORCERESS, -1 )
    call SetBuildAll( BUILD_UNIT, 1, HYDROMANCER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_MELEE, -1 )
    if (( GetUnitCount( BLACKSMITH ) < 2 )) then
        call SetBuildAll( BUILD_UNIT, 2, BLACKSMITH, -1 )
    endif
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_ARMOR, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_SENTINEL, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_PRAYING, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_SORCERY, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_2, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_3, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_4, -1 )
    if (( ( GetUnitCount( STABLES_3 ) + GetUnitCount( STABLES_4 ) ) == 0 )) then
        call SetBuildAll( BUILD_UNIT, 1, STABLES_2, -1 )
    endif
    if (( GetUnitCount( STABLES_4 ) == 0 )) then
        call SetBuildAll( BUILD_UNIT, 1, STABLES_3, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, STABLES_4, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 2, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 3, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 4, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_ARMOR, -1 )
    if (( GetUnitCount( GARRISON_3 ) == 0 )) then
        call SetBuildAll( BUILD_UNIT, 1, GARRISON_2, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, GARRISON_3, -1 )
    call SetBuildAll( BUILD_UNIT, 1, HIGH_FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 1, SPEAR_MAN, -1 )
    call SetBuildAll( BUILD_UNIT, 2, SPEAR_MAN, -1 )
    call SetBuildAll( BUILD_UNIT, 1, QUESTIONER, -1 )
    call SetBuildAll( BUILD_UNIT, 1, WIZARD, -1 )
    call SetBuildAll( BUILD_UNIT, 1, PRIEST, -1 )
    call SetBuildAll( BUILD_UNIT, 1, EXEMPLAR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, JUSTICAR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_SPEAR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, GENERAL, -1 )
    if (( GetUpgradeLevel( UPG_ROGUE ) == 1 )) then
        call SetBuildAll( BUILD_UNIT, 1, ROGUE, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_PRIEST, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_ARMOR, -1 )
    if (( GetUnitCount( FARM_5 ) == 0 )) then
        call SetBuildAll( BUILD_UNIT, 1, FARM_5, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_ENGINEER, -1 )
/// Build towers and fountain at first expansion
    if (( TownHasHall( 1 ) == true )) then
        call SetBuildAll( BUILD_UNIT, 1, FOUNTAIN, 1 )
        call SetBuildAll( BUILD_UNIT, 1, TOWER_COLD_1, 1 )
        call SetBuildAll( BUILD_UNIT, 1, TOWER_FLAME_1, 1 ) 
    endif
/// end build towers and fountain at first expansion
/// Build towers and fountain at second expansion
    if (( TownHasHall( 2 ) == true )) then
        call SetBuildAll( BUILD_UNIT, 1, FOUNTAIN, 2 )
        call SetBuildAll( BUILD_UNIT, 1, TOWER_COLD_1, 2 )
        call SetBuildAll( BUILD_UNIT, 1, TOWER_FLAME_1, 2 )  
    endif
/// end build towers and fountain at second expansion
    call SetBuildAll( BUILD_UNIT, 1, TOWER_ROYAL_1, -1 )
    call SetBuildAll( BUILD_UNIT, 1, TOWER_ROYAL_SKY, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_FACTORY, -1 )
    if (gCond_Need_Research_Center) then
        call SetBuildAll( BUILD_UNIT, 1, RESEARCH_CENTER_1, -1 )
    endif
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_LEATHER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_DOTA_ARCHER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_DOTA_CANNON, -1 )
    call SetBuildAll( BUILD_UNIT, 1, RESEARCH_CENTER_2, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_DOTA_WIZARD, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_DOTA_KNIGHT, -1 )
    if (( ( GetUpgradeLevel( UPG_LEATHER ) + GetUpgradeLevel( UPG_ROGUE ) ) == 2 )) then
        call SetBuildAll( BUILD_UNIT, 1, NINJA, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_MARKET_2, -1 )
    call SetBuildAll( BUILD_UNIT, 1, STONEMASON_TOWER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 4, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 4, UPG_ARMOR, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_PRAYING, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_SORCERY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 5, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 5, UPG_ARMOR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, TOWER_WIZARDRY_2_UPGRADE, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_ENGINEER_2, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_WORKSHOP, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_COTTAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 2, FARM_COTTAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 3, FARM_COTTAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 4, FARM_COTTAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 5, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 6, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 7, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 8, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 1, WIZARD_CHAOS, -1 )
    call SetBuildAll( BUILD_UNIT, 2, WIZARD_CHAOS, -1 )
    call SetBuildAll( BUILD_UNIT, 3, WIZARD_CHAOS, -1 )
    call SetBuildAll( BUILD_UNIT, 4, WIZARD_CHAOS, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_MAGUS, -1 )
    call SetBuildAll( BUILD_UNIT, 2, ROYAL_MAGUS, -1 )
    call SetBuildAll( BUILD_UNIT, 3, ROYAL_MAGUS, -1 )
    call SetBuildAll( BUILD_UNIT, 4, ROYAL_MAGUS, -1 )
    call SetBuildAll( BUILD_UPGRADE, 6, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 7, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 8, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 9, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 10, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_RANGED, -1 )
    call SetBuildAll( BUILD_UPGRADE, 11, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 12, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 13, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 14, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 15, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 4, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_RANGED, -1 )
    call SetBuildAll( BUILD_UPGRADE, 16, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 17, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 18, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 19, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 20, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_RANGED, -1 )
    call SetBuildAll( BUILD_UNIT, 1, DOZER, -1 )
    call SetBuildAll( BUILD_UNIT, 2, DOZER, -1 )
    call SetBuildAll( BUILD_UNIT, 3, DOZER, -1 )
    call SetBuildAll( BUILD_UNIT, 4, DOZER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_MAGIC_DAMAGE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 5, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_MAGIC_DAMAGE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_MAGIC_DAMAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 9, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 10, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 11, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 12, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 13, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 14, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 15, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 16, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 17, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 18, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 18, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 19, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 20, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 21, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 2, QUESTIONER, -1 )
    call SetBuildAll( BUILD_UNIT, 2, WIZARD, -1 )
    call SetBuildAll( BUILD_UNIT, 2, ROYAL_PRIEST, -1 )
    call SetBuildAll( BUILD_UNIT, 2, EXEMPLAR, -1 )
    call SetBuildAll( BUILD_UNIT, 2, JUSTICAR, -1 )
    call SetBuildAll( BUILD_UNIT, 2, ROYAL_SPEAR, -1 )
    call SetBuildAll( BUILD_UNIT, 2, GENERAL, -1 )
endfunction

//===========================================================================
// Specifies harvesting priorities for workers
//===========================================================================
function HarvestPriorities takes nothing returns nothing
    local integer mine = TownWithMine()
    local integer allGold = GetUnitCountDone(PEASANT)
    local integer allWood = GetUnitCountDone(PEASANT)
    local integer numWorkers
    set numWorkers = 4
    call HarvestGold( 0, numWorkers )
    set numWorkers = 4
    call HarvestGold( 1, numWorkers )
    set numWorkers = 4
    call HarvestGold( 2, numWorkers )
    set numWorkers = 3
    call HarvestGold( 3, numWorkers )
    set numWorkers = 12
    call HarvestWood( mine, numWorkers )
endfunction

//===========================================================================
// Determines all building and harvesting assignments for workers
//===========================================================================
function WorkerAssignment takes nothing returns nothing
    loop
        call UpdateConditions(  )

        // Harvesting
        call ClearHarvestAI(  )
        call HarvestPriorities(  )

        // Building
        call InitBuildArray(  )
        call BuildPriorities(  )

        call Sleep( 2 )
    endloop
endfunction

//***************************************************************************
//*
//*  Attacking
//*
//***************************************************************************

//===========================================================================
// Returns true if the minimum forces for an attack exist
//===========================================================================
function HaveMinimumAttackers takes nothing returns boolean
    local integer count
    // Check for attack wave limit
    if (attackWave > 21) then
        return false
    endif

    // First Hero Only
    if (GetUnitCountDone(hero_id) < 1) then
        return false
    endif

return true
endfunction

//===========================================================================
// Assigns units to attack based on the given attack group
//===========================================================================
function PrepareAttackGroup takes integer groupID returns nothing
    local integer all

    // Attack Group #1: All Units
    if (groupID == 1) then
    set all = GetUnitCountDone( hero_id )
    call SetAssaultGroup( all, all, hero_id )
    if (( GetUpgradeLevel( UPG_LEATHER ) == 1 )) then
        set all = GetUnitCountDone( hero_id2 )
        call SetAssaultGroup( all, all, hero_id2 )
    endif
    set all = GetUnitCountDone( FOOTMAN )
    call SetAssaultGroup( all, all, FOOTMAN )
    set all = GetUnitCountDone( HIGH_ARCHER )
    call SetAssaultGroup( all, all, HIGH_ARCHER )
    set all = GetUnitCountDone( MORTAR )
    call SetAssaultGroup( all, all, MORTAR )
    set all = GetUnitCountDone( CHAPLAIN )
    call SetAssaultGroup( all, all, CHAPLAIN )
    set all = GetUnitCountDone( HIGH_FOOTMAN )
    call SetAssaultGroup( all, all, HIGH_FOOTMAN )
    set all = GetUnitCountDone( SPEAR_MAN )
    call SetAssaultGroup( all, all, SPEAR_MAN )
    set all = GetUnitCountDone( QUESTIONER )
    call SetAssaultGroup( all, all, QUESTIONER )
    set all = GetUnitCountDone( SORCERESS )
    call SetAssaultGroup( all, all, SORCERESS )
    set all = GetUnitCountDone( WIZARD )
    call SetAssaultGroup( all, all, WIZARD )
    set all = GetUnitCountDone( HYDROMANCER )
    call SetAssaultGroup( all, all, HYDROMANCER )
    set all = GetUnitCountDone( PRIEST )
    call SetAssaultGroup( all, all, PRIEST )
    set all = GetUnitCountDone( EXEMPLAR )
    call SetAssaultGroup( all, all, EXEMPLAR )
    set all = GetUnitCountDone( JUSTICAR )
    call SetAssaultGroup( all, all, JUSTICAR )
    set all = GetUnitCountDone( ROYAL_SPEAR )
    call SetAssaultGroup( all, all, ROYAL_SPEAR )
    set all = GetUnitCountDone( GENERAL )
    call SetAssaultGroup( all, all, GENERAL )
    set all = GetUnitCountDone( ROGUE )
    call SetAssaultGroup( all, all, ROGUE )
    set all = GetUnitCountDone( ROYAL_PRIEST )
    call SetAssaultGroup( all, all, ROYAL_PRIEST )
    set all = GetUnitCountDone( BISHOP )
    call SetAssaultGroup( all, all, BISHOP )
    set all = GetUnitCountDone( NINJA )
    call SetAssaultGroup( all, all, NINJA )
    set all = GetUnitCountDone( WIZARD_CHAOS )
    call SetAssaultGroup( all, all, WIZARD_CHAOS )
    set all = GetUnitCountDone( ROYAL_MAGUS )
    call SetAssaultGroup( all, all, ROYAL_MAGUS )
    set all = GetUnitCountDone( KNIGHT )
    call SetAssaultGroup( all, all, KNIGHT )
    set all = GetUnitCountDone( ROYAL_KNIGHT_2 )
    call SetAssaultGroup( all, all, ROYAL_KNIGHT_2 )
    set all = GetUnitCountDone( ROYAL_KNIGHT_3 )
    call SetAssaultGroup( all, all, ROYAL_KNIGHT_3 )
    set all = GetUnitCountDone( ROYAL_KNIGHT_4 )
    call SetAssaultGroup( all, all, ROYAL_KNIGHT_4 )
    set all = GetUnitCountDone( BIG_BERTHA )
    call SetAssaultGroup( all, all, BIG_BERTHA )
    set all = GetUnitCountDone( DOZER )
    call SetAssaultGroup( all, all, DOZER )
    endif
endfunction

//===========================================================================
// Prepares an attack group based on the current attack wave
//===========================================================================
function PrepareForces takes nothing returns nothing
        call PrepareAttackGroup( 1 )
endfunction

//===========================================================================
// 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

//===========================================================================
// Advances attack wave counter
//===========================================================================
function AttackWaveUpdate takes nothing returns nothing
    call AttackWaveDelay( attackWave )
    set attackWave = attackWave + 1
    if (attackWave > 2) then
        set attackWave = 2
        set nextDelay = attackWave + 1
    endif
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 (Tier < 3) then
        if (target == null) then
            set target = GetCreepCamp( 0, 9, false )
        endif
    endif

        // Target Priority #2
    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

    // 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

//===========================================================================
// Determines all attacking assignments
//===========================================================================
function AttackAssignment takes nothing returns nothing
    call StaggerSleep( 0, 2 )
    if (attackWave == 1) then
        call AttackWaveDelay( 0 )
    endif
    loop
        loop
            call UpdateConditions(  )
            exitwhen (HaveMinimumAttackers() and not CaptainRetreating())
            call Sleep( 2 )
        endloop
        call RemoveInjuries(  )
        call ResetAttackUnits(  )
        call PrepareForces(  )
        call LaunchAttack(  )
    endloop
endfunction

//***************************************************************************
//*
//*  Misc.
//*
//***************************************************************************

//===========================================================================
// 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

//***************************************************************************
//*
//*  Main Entry Point
//*
//***************************************************************************

//===========================================================================
function main takes nothing returns nothing
    call InitAI(  )
    call SetPlayerName( ai_player, "Insane King XXIV" )
    call InitOptions(  )
    call SelectHeroes(  )
    call CreateCaptains(  )
    call SetHeroLevels( function ChooseHeroSkill )

    call Sleep( 0.1 )
    call StartThread( function WorkerAssignment )
    call StartThread( function AttackAssignment )
    call StartThread( function ConditionLoop )
    call PlayGame(  )
endfunction
//===========================================================================
//
// King AI
//
//===========================================================================

//***************************************************************************
//*
//*  Global Variables
//*
//***************************************************************************

globals
    integer                 Tier                       = 0
    integer                 attackWave                 = 0
    integer                 nextDelay                  = 0
    integer                 awGold                     = 0
    integer                 awWood                     = 0
/// Conditions
    boolean                 gCond_Need_Town_Hall       = false
    boolean                 gCond_Needs_Garrison       = false
    boolean                 gCond_Needs_Stable         = false
    boolean                 gCond_Needs_Farm           = false
    boolean                 gCond_Need_Guard_Tower     = false
    boolean                 gCond_Need_Research_Center = false
    boolean                 gCond_Need_LM              = false
    boolean                 gCond_Need_BS              = false
    boolean                 gCond_Need_Market          = false
    boolean                 gCond_Need_WizTower        = false
/// Units
/// King Heroes
    constant integer        SOVEREIGN                  = 'H01L'
    constant integer        MONARCH                    = 'HC18'
    constant integer        MAJESTY                    = 'H03I'
/// Prince Heroes
    /// constant integer    PALADIN                    = 'Hpal'
    constant integer        MAGE                       = 'H01G'
    constant integer        WARRIOR                    = 'H03J'
/// King Hero Abilities
    constant integer        ROYAL_BANISH               = 'A006'
    constant integer        KING_AURA                  = 'A00K'
    constant integer        KING_COURT                 = 'A00C'
    /// constant integer    THUNDER_CLAP               = 'AHtc' /// Sovereign only / "Royal Revenge"
    constant integer        ROYAL_SHOCKWAVE            = 'A06G' /// Monarch only
    constant integer        ROYAL_BLIZZARD             = 'A06H' /// Majesty only
    constant integer        ROYAL_SUMMON               = 'A01H' /// based on blizzard spell so ais tend to use improperly
/// Prince Hero Abilities
    /// constant integer    HOLY_BOLT                  = 'AHhb' /// Holy Light / "Royal Blessing"
    /// constant integer    BLINK                      = 'AEbl' /// Royal Mage only
    /// constant integer    DIVINE_SHIELD              = 'AHds' /// Royal Pally only
    /// constant integer    DEVOTION_AURA              = 'AHad'
    constant integer        BLESSED_FIELD              = 'A077' /// Royal Pally only
    /// constant integer    MASS_TELEPORT              = 'AHmt' /// Royal Mage only
    constant integer        ATTRIBUTE_BONUS            = 'Aamk'
    constant integer        FERAL_IMPACT               = 'A01U' /// Royal Warrior only
    constant integer        CRIT_STRIKE                = 'A06K' /// Royal Warrior only
    constant integer        ROYAL_AVATAR               = 'A06I' /// Royal Warrior only
    constant integer        ROYAL_BLADESTORM           = 'A06J' /// Royal Warrior only
/// Buildings
    constant integer        TOME_LIBRARY               = 'nC03'
    constant integer        KING_VAULT                 = 'nC02'
    constant integer        ROYAL_ALTAR                = 'h01N'
    constant integer        FRUIT_STAND                = 'nfrt'
    constant integer        MERC_CAMP                  = 'nmer'
    constant integer        ANTICAMP_TOWER             = 'nC51'
    /// constant integer    CASTLE                     = 'hcas'
    /// constant integer    TOWN_HALL                  = 'htow'
    /// constant integer    LUMBER_MILL                = 'hlum'
    /// constant integer    BLACKSMITH                 = 'hbla'
    constant integer        ROYAL_BARRACKS             = 'h02P'
    constant integer        FARM_1                     = 'h003'
    constant integer        FARM_2                     = 'h005'
    constant integer        FARM_3                     = 'h006'
    constant integer        FARM_4                     = 'h007'
    constant integer        FARM_5                     = 'h00S'
/// constant integer        BARRACKS                   = 'hbar' /// "Stables" (tier 1)
    constant integer        STABLES_2                  = 'h00A'
    constant integer        STABLES_3                  = 'h00B'
    constant integer        STABLES_4                  = 'h00K'
    constant integer        GARRISON_1                 = 'h00F'
    constant integer        GARRISON_2                 = 'h00G'
    constant integer        GARRISON_3                 = 'h00H'
    constant integer        ROYAL_FACTORY              = 'h01F'
    constant integer        RESEARCH_CENTER_1          = 'h01H'
    constant integer        RESEARCH_CENTER_2          = 'h02Q'
    constant integer        FOUNTAIN                   = 'n020'
    constant integer        ROYAL_SHIPYARD             = 'h01S'
    constant integer        STONEMASON_TOWER           = 'h02Y'
    constant integer        ROYAL_WORKSHOP             = 'h02Z'
    constant integer        FARM_COTTAGE               = 'h034'
/// constant integer        SANCTUM                    = 'hars' /// "Tower of Wizardry"
    constant integer        TOWER_WIZARDRY_2_BUILT     = 'h031'
    constant integer        TOWER_WIZARDRY_2_UPGRADE   = 'h03H'
    constant integer        ROYAL_CHURCH               = 'h01A'
/// constant integer        ARCANE_VAULT               = 'hvlt' /// "Royal Market" (tier 1)
    constant integer        ROYAL_MARKET_2             = 'h01K'
    constant integer        BASIC_TOWER                = 'h00R'
/// constant integer        GUARD_TOWER                = 'hgtw' /// Tier 1 Guard Tower
    constant integer        GUARD_TOWER_3              = 'h00P'
/// constant integer        CANNON_TOWER               = 'hctw' /// "Cannon Tower" (tier 1)
    constant integer        CANNON_TOWER_2             = 'h033'
/// constant integer        ARCANE_TOWER               = 'hatw' /// "Arcane Tower" (tier 1)
    constant integer        ARCANE_TOWER_2             = 'h018'
    constant integer        ARCANE_TOWER_3             = 'h02V'
    constant integer        TOWER_COLD_1               = 'ndt1'
    constant integer        TOWER_COLD_2               = 'ndt2'
    constant integer        TOWER_COLD_3               = 'n01H'
    constant integer        TOWER_COLD_4               = 'n01I'
    constant integer        TOWER_FLAME_1              = 'nft1'
    constant integer        TOWER_FLAME_2              = 'nft2'
    constant integer        TOWER_FLAME_3              = 'n029'
    constant integer        TOWER_FLAME_4              = 'n02A'
    constant integer        TOWER_FLAME_5              = 'n02B'
    constant integer        TOWER_FLAME_6              = 'n02C'
    constant integer        TOWER_ROYAL_1              = 'hC17'
    constant integer        TOWER_ROYAL_2              = 'h000'
    constant integer        TOWER_ROYAL_3              = 'h001'
    constant integer        TOWER_ROYAL_4              = 'h00C'
    constant integer        TOWER_ROYAL_SKY            = 'n00D'
/// Units
/// constant integer        PEASANT                    = 'hpea'
    constant integer        ROYAL_ENGINEER             = 'n004'
    constant integer        ROYAL_ENGINEER_2           = 'n01L'
/// constant integer        FOOTMAN                    = 'hfoo' /// "Crusader"
/// constant integer        KNIGHT                     = 'hkni' /// "Royal Knight" (tier 1)
/// constant integer        MORTAR                     = 'hmtm' /// "Royal Cannon"
/// constant integer        PRIEST                     = 'hmpr'
/// constant integer        SORCERESS                  = 'hsor' /// "Anime Witch"
/// constant integer        HIGH_FOOTMAN               = 'hcth' /// "Field Captain"
/// constant integer        HIGH_ARCHER                = 'nhea' /// "Archer"
/// constant integer        PRIEST                     = 'hmpr'
    constant integer        CHAPLAIN                   = 'nchp'
    constant integer        HYDROMANCER                = 'nhym'
    constant integer        PACK_HORSE                 = 'hrdh'
    constant integer        CRUSADER_DOTA              = 'h01I'
    constant integer        SQUIRE                     = 'h00J'
    constant integer        ROGUE                      = 'h00Z'
    constant integer        NINJA                      = 'h02W'
    constant integer        EXEMPLAR                   = 'h00M'
    constant integer        JUSTICAR                   = 'h00I'
    constant integer        QUESTIONER                 = 'h01Q'
    constant integer        GENERAL                    = 'h004'
/// constant integer        KNIGHT                     = 'hkni' /// "Royal Knight" (tier 1)
    constant integer        ROYAL_KNIGHT_DOTA          = 'h01J'
    constant integer        ROYAL_KNIGHT_2             = 'h008'
    constant integer        ROYAL_KNIGHT_3             = 'h009'
    constant integer        ROYAL_KNIGHT_4             = 'h00L'
    constant integer        OUTRIDER                   = 'h03O'
    constant integer        OUTRIDER_ROYAL             = 'h01O'
    constant integer        BIG_BERTHA_DOTA            = 'h01U'
    constant integer        BIG_BERTHA                 = 'h035'
    constant integer        BISHOP                     = 'h02R'
    constant integer        ROYAL_PRIEST               = 'hC41'
    constant integer        WIZARD                     = 'h01D'
    constant integer        WIZARD_DOTA                = 'h01T'
    constant integer        WIZARD_CHAOS               = 'h032'
    constant integer        ROYAL_MAGUS                = 'h02X'
    constant integer        DOZER                      = 'h030'
    constant integer        ARCHER_DOTA                = 'n00E'
    constant integer        SPEAR_MERC                 = 'n00A'
    constant integer        LANCER                     = 'n00H'
    constant integer        SPEAR_MAN                  = 'n009'
    constant integer        ROYAL_SPEAR                = 'n00J'
    constant integer        ROYAL_ZEP                  = 'n00B'
/// Upgrades
/// constant integer        UPG_MELEE                  = 'Rhme'
/// constant integer        UPG_ARMOR                  = 'Rhar'
/// constant integer        UPG_MASONRY                = 'Rhac'
/// constant integer        UPG_WOOD                   = 'Rhlh'
/// constant integer        UPG_SENTINEL               = 'Rhse' /// "Magic Sentry"
/// constant integer        UPG_SORCERY                = 'Rhst'
/// constant integer        UPG_PRAYING                = 'Rhpt' /// "Priest Training"
/// constant integer        UPG_BREEDING               = 'Rhan' /// "Animal War Training"
    constant integer        UPG_ROGUE                  = 'R001'
    constant integer        UPG_MAGIC_DAMAGE           = 'R008'
    constant integer        UPG_DOTA_ARCHER            = 'R003'
    constant integer        UPG_DOTA_CANNON            = 'R006'
    constant integer        UPG_DOTA_WIZARD            = 'R005'
    constant integer        UPG_DOTA_KNIGHT            = 'R002'
/// constant integer        UPG_LEATHER                = 'Rhla' /// "Gyrations"
/// constant integer        UPG_RANGED                 = 'Rhra'
endglobals

//***************************************************************************
//*
//*  Utility Functions
//*
//***************************************************************************

//===========================================================================
function CheckLastCommand takes boolean pop returns integer
    local integer cmd = GetLastCommand()
    if (pop) then
        call PopLastCommand(  )
    endif
    return cmd
endfunction

//===========================================================================
function CheckLastCommandData takes boolean pop returns integer
    local integer data = GetLastData()
    if (pop) then
        call PopLastCommand(  )
    endif
    return data
endfunction

//===========================================================================
function TotalFoodProduced takes nothing returns integer
    return GetPlayerState(ai_player,PLAYER_STATE_RESOURCE_FOOD_CAP)
endfunction

//===========================================================================
function ExpansionNeeded takes nothing returns boolean
    return take_exp
endfunction

//===========================================================================
function BuildExpansion takes integer hallID, integer mineID returns nothing
    if (HallsCompleted(hallID)) then
        call SetBuildExpa( TownCount(hallID) + 1, mineID )
    endif
endfunction

//===========================================================================
function CurrentAttackWave takes nothing returns integer
    return attackWave
endfunction

//===========================================================================
function ResetAttackUnits takes nothing returns nothing
    set awGold = 0
    set awWood = 0
    call InitAssaultGroup(  )
endfunction

//===========================================================================
function AddAttackUnit takes integer minQty, integer maxQty, integer unitID returns nothing
    // Track attacking gold workers
    if (unitID == 'hpea') then
        set awGold = awGold + minQty
    endif

    // Track attacking wood workers
    if (unitID == 'hpea') then
        set awWood = awWood + minQty
    endif

    call SetAssaultGroup( minQty, maxQty, unitID )
endfunction

//***************************************************************************
//*
//*  Basic Options
//*
//***************************************************************************

//===========================================================================
function InitOptions takes nothing returns nothing
    call SetMeleeAI(  )
    call SetDefendPlayer( true )
    call SetRandomPaths( true )
    call SetTargetHeroes( true )
    call SetPeonsRepair( true )
    call SetHeroesFlee( true )
    call SetHeroesBuyItems( true )
    call SetUnitsFlee( true )
    call SetGroupsFlee( true )
    call SetWatchMegaTargets( true )
    call SetIgnoreInjured( true )
    call SetHeroesTakeItems( true )
    call SetSlowChopping( false )
    call SetCaptainChanges( true )
    call SetSmartArtillery( true )
endfunction

//***************************************************************************
//*
//*  Conditions
//*
//***************************************************************************

//===========================================================================
// Updates the values of all preset conditions
//===========================================================================
function UpdateConditions takes nothing returns nothing
    set gCond_Need_Town_Hall = ( ( GetUnitCount( TOWN_HALL ) ) < 3 )
    set gCond_Need_LM = ( ( GetUnitCount( LUMBER_MILL ) ) < 1 )
    set gCond_Need_BS = ( ( GetUnitCount( BLACKSMITH ) ) < 2 )
    set gCond_Need_Market = ( (GetUnitCount( ARCANE_VAULT ) ) < 1 )
    set gCond_Need_WizTower = ( (GetUnitCount( SANCTUM ) + (GetUnitCount( TOWER_WIZARDRY_2_UPGRADE ) ) < 1 ) )
    set gCond_Needs_Garrison = ( ( GetUnitCount( GARRISON_1 ) + ( GetUnitCount( GARRISON_2 ) + GetUnitCount( GARRISON_3 ) ) ) == 0 )
    set gCond_Needs_Stable = ( ( ( GetUnitCount( BARRACKS ) + GetUnitCount( STABLES_2 ) ) + ( GetUnitCount( STABLES_3 ) + GetUnitCount( STABLES_4 ) ) ) == 0 )
    set gCond_Needs_Farm = ( ( ( ( GetUnitCount( FARM_1 ) + GetUnitCount( FARM_2 ) ) + GetUnitCount( FARM_3 ) ) + ( GetUnitCount( FARM_4 ) + GetUnitCount( FARM_5 ) ) ) < 6 )
    set gCond_Need_Guard_Tower = ( ( GetUnitCount( BASIC_TOWER ) + GetUnitCount( GUARD_TOWER ) ) < 2 )
    set gCond_Need_Research_Center = ( ( GetUnitCount( RESEARCH_CENTER_1 ) + GetUnitCount( RESEARCH_CENTER_2 ) ) == 0 )   
endfunction

//***************************************************************************
//*
//*  Heroes
//*
//***************************************************************************

//===========================================================================
// Stores hero ID and skills
//===========================================================================
function SetHero takes integer order, integer heroid returns nothing
    if (order == 1) then
        set hero_id = heroid
        if (heroid == SOVEREIGN) then
            set skills1[ 1] = KING_AURA         /// 1
            set skills1[ 2] = THUNDER_CLAP      /// 1
            set skills1[ 3] = KING_COURT        /// 1
            set skills1[ 4] = ROYAL_BANISH      /// 1
            set skills1[ 5] = KING_AURA         /// 2
            set skills1[ 6] = THUNDER_CLAP      /// 2
            set skills1[ 7] = KING_COURT        /// 2
            set skills1[ 8] = ROYAL_BANISH      /// 2
            set skills1[ 9] = KING_AURA         /// 3
            set skills1[10] = THUNDER_CLAP      /// 3
            set skills1[11] = KING_COURT        /// 3
            set skills1[12] = KING_AURA         /// 4
            set skills1[13] = KING_COURT        /// 4
            set skills1[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MONARCH) then
            set skills1[ 1] = KING_AURA         /// 1
            set skills1[ 2] = ROYAL_SHOCKWAVE   /// 1
            set skills1[ 3] = KING_COURT        /// 1
            set skills1[ 4] = ROYAL_BANISH      /// 1
            set skills1[ 5] = KING_AURA         /// 2
            set skills1[ 6] = ROYAL_SHOCKWAVE   /// 2
            set skills1[ 7] = KING_COURT        /// 2
            set skills1[ 8] = ROYAL_BANISH      /// 2
            set skills1[ 9] = KING_AURA         /// 3
            set skills1[10] = ROYAL_SHOCKWAVE   /// 3
            set skills1[11] = KING_COURT        /// 3
            set skills1[12] = KING_AURA         /// 4
            set skills1[13] = KING_COURT        /// 4
            set skills1[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MAJESTY) then
            set skills1[ 1] = KING_AURA         /// 1
            set skills1[ 2] = ROYAL_BLIZZARD    /// 1
            set skills1[ 3] = KING_COURT        /// 1
            set skills1[ 4] = ROYAL_BANISH      /// 1
            set skills1[ 5] = KING_AURA         /// 2
            set skills1[ 6] = ROYAL_BLIZZARD    /// 2
            set skills1[ 7] = KING_COURT        /// 2
            set skills1[ 8] = ROYAL_BANISH      /// 2
            set skills1[ 9] = KING_AURA         /// 3
            set skills1[10] = ROYAL_BLIZZARD    /// 3
            set skills1[11] = KING_COURT        /// 3
            set skills1[12] = KING_AURA         /// 4
            set skills1[13] = KING_COURT        /// 4
            set skills1[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MAGE) then
            set skills1[ 1] = DEVOTION_AURA
            set skills1[ 2] = HOLY_BOLT
            set skills1[ 3] = BLINK
            set skills1[ 4] = DEVOTION_AURA
            set skills1[ 5] = HOLY_BOLT
            set skills1[ 6] = MASS_TELEPORT
            set skills1[ 7] = DEVOTION_AURA
            set skills1[ 8] = HOLY_BOLT
            set skills1[ 9] = BLINK
            set skills1[10] = BLINK
            set skills1[11] = ATTRIBUTE_BONUS
            set skills1[12] = ATTRIBUTE_BONUS
            set skills1[13] = ATTRIBUTE_BONUS
            set skills1[14] = ATTRIBUTE_BONUS
            set skills1[15] = ATTRIBUTE_BONUS
        elseif (heroid == PALADIN) then
            set skills1[ 1] = DEVOTION_AURA
            set skills1[ 2] = HOLY_BOLT
            set skills1[ 3] = DIVINE_SHIELD
            set skills1[ 4] = DEVOTION_AURA
            set skills1[ 5] = HOLY_BOLT
            set skills1[ 6] = DIVINE_SHIELD
            set skills1[ 7] = DEVOTION_AURA
            set skills1[ 8] = HOLY_BOLT
            set skills1[ 9] = DIVINE_SHIELD
            set skills1[10] = BLESSED_FIELD
            set skills1[11] = ATTRIBUTE_BONUS
            set skills1[12] = ATTRIBUTE_BONUS
            set skills1[13] = ATTRIBUTE_BONUS
            set skills1[14] = ATTRIBUTE_BONUS
            set skills1[15] = ATTRIBUTE_BONUS
            set skills1[16] = BLESSED_FIELD
        elseif (heroid == WARRIOR) then
            set skills1[ 1] = DEVOTION_AURA
            set skills1[ 2] = ROYAL_BLADESTORM
            set skills1[ 3] = CRIT_STRIKE
            set skills1[ 4] = DEVOTION_AURA
            set skills1[ 5] = ROYAL_BLADESTORM
            set skills1[ 6] = CRIT_STRIKE
            set skills1[ 7] = DEVOTION_AURA
            set skills1[ 8] = ROYAL_BLADESTORM
            set skills1[ 9] = CRIT_STRIKE
            set skills1[10] = ROYAL_AVATAR
        endif
    elseif (order == 2) then
        set hero_id2 = heroid
        if (heroid == SOVEREIGN) then
            set skills2[ 1] = KING_AURA         /// 1
            set skills2[ 2] = THUNDER_CLAP      /// 1
            set skills2[ 3] = KING_COURT        /// 1
            set skills2[ 4] = ROYAL_BANISH      /// 1
            set skills2[ 5] = KING_AURA         /// 2
            set skills2[ 6] = THUNDER_CLAP      /// 2
            set skills2[ 7] = KING_COURT        /// 2
            set skills2[ 8] = ROYAL_BANISH      /// 2
            set skills2[ 9] = KING_AURA         /// 3
            set skills2[10] = THUNDER_CLAP      /// 3
            set skills2[11] = KING_COURT        /// 3
            set skills2[12] = KING_AURA         /// 4
            set skills2[13] = KING_COURT        /// 4
            set skills2[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MONARCH) then
            set skills2[ 1] = KING_AURA         /// 1
            set skills2[ 2] = ROYAL_SHOCKWAVE   /// 1
            set skills2[ 3] = KING_COURT        /// 1
            set skills2[ 4] = ROYAL_BANISH      /// 1
            set skills2[ 5] = KING_AURA         /// 2
            set skills2[ 6] = ROYAL_SHOCKWAVE   /// 2
            set skills2[ 7] = KING_COURT        /// 2
            set skills2[ 8] = ROYAL_BANISH      /// 2
            set skills2[ 9] = KING_AURA         /// 3
            set skills2[10] = ROYAL_SHOCKWAVE   /// 3
            set skills2[11] = KING_COURT        /// 3
            set skills2[12] = KING_AURA         /// 4
            set skills2[13] = KING_COURT        /// 4
            set skills2[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MAJESTY) then
            set skills2[ 1] = KING_AURA         /// 1
            set skills2[ 2] = ROYAL_BLIZZARD    /// 1
            set skills2[ 3] = KING_COURT        /// 1
            set skills2[ 4] = ROYAL_BANISH      /// 1
            set skills2[ 5] = KING_AURA         /// 2
            set skills2[ 6] = ROYAL_BLIZZARD    /// 2
            set skills2[ 7] = KING_COURT        /// 2
            set skills2[ 8] = ROYAL_BANISH      /// 2
            set skills2[ 9] = KING_AURA         /// 3
            set skills2[10] = ROYAL_BLIZZARD    /// 3
            set skills2[11] = KING_COURT        /// 3
            set skills2[12] = KING_AURA         /// 4
            set skills2[13] = KING_COURT        /// 4
            set skills2[14] = ROYAL_BANISH      /// 3
        elseif (heroid == MAGE) then
            set skills2[ 1] = DEVOTION_AURA
            set skills2[ 2] = HOLY_BOLT
            set skills2[ 3] = BLINK
            set skills2[ 4] = DEVOTION_AURA
            set skills2[ 5] = HOLY_BOLT
            set skills2[ 6] = MASS_TELEPORT
            set skills2[ 7] = DEVOTION_AURA
            set skills2[ 8] = HOLY_BOLT
            set skills2[ 9] = BLINK
            set skills2[10] = BLINK
            set skills2[11] = ATTRIBUTE_BONUS
            set skills2[12] = ATTRIBUTE_BONUS
            set skills2[13] = ATTRIBUTE_BONUS
            set skills2[14] = ATTRIBUTE_BONUS
            set skills2[15] = ATTRIBUTE_BONUS
        elseif (heroid == PALADIN) then
            set skills2[ 1] = DEVOTION_AURA
            set skills2[ 2] = HOLY_BOLT
            set skills2[ 3] = DIVINE_SHIELD
            set skills2[ 4] = DEVOTION_AURA
            set skills2[ 5] = HOLY_BOLT
            set skills2[ 6] = DIVINE_SHIELD
            set skills2[ 7] = DEVOTION_AURA
            set skills2[ 8] = HOLY_BOLT
            set skills2[ 9] = DIVINE_SHIELD
            set skills2[10] = BLESSED_FIELD
            set skills2[11] = ATTRIBUTE_BONUS
            set skills2[12] = ATTRIBUTE_BONUS
            set skills2[13] = ATTRIBUTE_BONUS
            set skills2[14] = ATTRIBUTE_BONUS
            set skills2[15] = ATTRIBUTE_BONUS
            set skills2[16] = BLESSED_FIELD
        elseif (heroid == WARRIOR) then
            set skills2[ 1] = DEVOTION_AURA
            set skills2[ 2] = ROYAL_BLADESTORM
            set skills2[ 3] = CRIT_STRIKE
            set skills2[ 4] = DEVOTION_AURA
            set skills2[ 5] = ROYAL_BLADESTORM
            set skills2[ 6] = CRIT_STRIKE
            set skills2[ 7] = DEVOTION_AURA
            set skills2[ 8] = ROYAL_BLADESTORM
            set skills2[ 9] = CRIT_STRIKE
            set skills2[10] = ROYAL_AVATAR
            set skills2[20] = ROYAL_AVATAR
        endif
    endif
endfunction

//===========================================================================
// Selects hero IDs for three possible heroes
//===========================================================================
function SelectHeroes takes nothing returns nothing
    local integer roll = GetRandomInt(1,9)
    if (roll == 1) then
        call SetHero( 1, MAGE )
        call SetHero( 2, SOVEREIGN )
    elseif (roll == 2) then
        call SetHero( 1, PALADIN )
        call SetHero( 2, SOVEREIGN )
    elseif (roll == 3) then
        call SetHero( 1, WARRIOR )
        call SetHero( 2, SOVEREIGN )
    elseif (roll == 4) then
        call SetHero( 1, MAGE )
        call SetHero( 2, MONARCH )
    elseif (roll == 5) then
        call SetHero( 1, PALADIN )
        call SetHero( 2, MONARCH )
    elseif (roll == 6) then
        call SetHero( 1, WARRIOR )
        call SetHero( 2, MONARCH )
    elseif (roll == 7) then
        call SetHero( 1, MAGE )
        call SetHero( 2, MAJESTY )
    elseif (roll == 8) then
        call SetHero( 1, PALADIN )
        call SetHero( 2, MAJESTY )
    elseif (roll == 9) then
        call SetHero( 1, WARRIOR )
        call SetHero( 2, MAJESTY )
    endif
endfunction

//===========================================================================
// Returns the hero skill for the given hero and level
//===========================================================================
function ChooseHeroSkill takes nothing returns integer
    local integer curHero = GetHeroId()
    local integer level = GetHeroLevelAI()
    if (level > max_hero_level) then
        set max_hero_level = level
    endif
   
    if (curHero == hero_id) then
        return skills1[level]
    elseif (curHero == hero_id2) then
        return skills2[level]
    endif
    return 0
endfunction

//***************************************************************************
//*
//*  Building and Harvesting
//*
//***************************************************************************

function BuildPriorities takes nothing returns nothing
    local integer mine = TownWithMine()
    call SetBuildAll( BUILD_UNIT, 1, hero_id, -1 )
    call SetBuildAll( BUILD_UNIT, 1, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 1, hero_id2, -1 )
    call SetBuildAll( BUILD_UNIT, 2, PEASANT, -1 )
    if gCond_Need_Town_Hall then
        call BuildExpansion (TOWN_HALL, TOWN_HALL)
    endif
    call SetBuildAll( BUILD_UNIT, 3, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 4, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 5, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 6, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 7, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 8, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 9, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 10, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 11, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 12, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 13, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 14, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 15, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 16, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 17, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 18, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 19, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 20, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 21, PEASANT, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_1, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_BARRACKS, -1 )
/// Build Lumbermill
    if gCond_Need_LM then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, LUMBER_MILL, 1 )
        elseif(( TownHasHall( 2 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, LUMBER_MILL, 2 )
        else
            call SetBuildAll( BUILD_UNIT, 1, LUMBER_MILL, -1 )
        endif
    endif
/// end Build Lumbermill
/// Build Blacksmith
    if gCond_Need_BS then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, BLACKSMITH, 1 )
        elseif (( TownHasHall( 2 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, BLACKSMITH, 2 )
        else
            call SetBuildAll( BUILD_UNIT, 1, BLACKSMITH, -1 )
        endif
    endif
/// end Build Blacksmith
/// Build Market
    if gCond_Need_Market then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, ARCANE_VAULT, 1 )
        elseif (( TownHasHall( 2 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, ARCANE_VAULT, 2 )
        else
            call SetBuildAll( BUILD_UNIT, 1, ARCANE_VAULT, -1 )
        endif
    endif
/// end Build Market
    if (gCond_Need_Guard_Tower) then
        call SetBuildAll( BUILD_UNIT, 1, BASIC_TOWER, -1 )
        call SetBuildAll( BUILD_UNIT, 2, BASIC_TOWER, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, GUARD_TOWER, -1 )
    call SetBuildAll( BUILD_UNIT, 2, GUARD_TOWER, -1 )
/// Build Stables checking at expansion one first
    if (gCond_Needs_Stable) then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, BARRACKS, 2 )
        elseif (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, BARRACKS, 1 )
        else
            call SetBuildAll( BUILD_UNIT, 1, BARRACKS, -1 )
        endif
    endif
/// end build stables
    if (gCond_Needs_Garrison) then
        call SetBuildAll( BUILD_UNIT, 1, GARRISON_1, -1 )
    endif
/// Build Wizard Tower
    if gCond_Need_WizTower then
        if (( TownHasHall( 1 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, SANCTUM, 1 )
        elseif (( TownHasHall( 2 ) == true )) then
            call SetBuildAll( BUILD_UNIT, 1, SANCTUM, 2 )
        else
            call SetBuildAll( BUILD_UNIT, 1, SANCTUM, -1 )
        endif
    endif
///end build Wizard Tower
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_CHURCH, -1 )
    call SetBuildAll( BUILD_UNIT, 1, HIGH_ARCHER, -1 )
    call SetBuildAll( BUILD_UNIT, 2, HIGH_ARCHER, -1 )
    call SetBuildAll( BUILD_UNIT, 3, HIGH_ARCHER, -1 )
    call SetBuildAll( BUILD_UNIT, 4, HIGH_ARCHER, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 2, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 3, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 4, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 5, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 6, FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 1, MORTAR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, CHAPLAIN, -1 )
    call SetBuildAll( BUILD_UNIT, 1, SORCERESS, -1 )
    call SetBuildAll( BUILD_UNIT, 1, HYDROMANCER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_MELEE, -1 )
    if (( GetUnitCount( BLACKSMITH ) < 2 )) then
        call SetBuildAll( BUILD_UNIT, 2, BLACKSMITH, -1 )
    endif
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_ARMOR, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_SENTINEL, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_PRAYING, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_SORCERY, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_2, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_3, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_4, -1 )
    if (( ( GetUnitCount( STABLES_3 ) + GetUnitCount( STABLES_4 ) ) == 0 )) then
        call SetBuildAll( BUILD_UNIT, 1, STABLES_2, -1 )
    endif
    if (( GetUnitCount( STABLES_4 ) == 0 )) then
        call SetBuildAll( BUILD_UNIT, 1, STABLES_3, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, STABLES_4, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 2, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 3, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 4, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_ARMOR, -1 )
    if (( GetUnitCount( GARRISON_3 ) == 0 )) then
        call SetBuildAll( BUILD_UNIT, 1, GARRISON_2, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, GARRISON_3, -1 )
    call SetBuildAll( BUILD_UNIT, 1, HIGH_FOOTMAN, -1 )
    call SetBuildAll( BUILD_UNIT, 1, SPEAR_MAN, -1 )
    call SetBuildAll( BUILD_UNIT, 2, SPEAR_MAN, -1 )
    call SetBuildAll( BUILD_UNIT, 1, QUESTIONER, -1 )
    call SetBuildAll( BUILD_UNIT, 1, WIZARD, -1 )
    call SetBuildAll( BUILD_UNIT, 1, PRIEST, -1 )
    call SetBuildAll( BUILD_UNIT, 1, EXEMPLAR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, JUSTICAR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_SPEAR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, GENERAL, -1 )
    if (( GetUpgradeLevel( UPG_ROGUE ) == 1 )) then
        call SetBuildAll( BUILD_UNIT, 1, ROGUE, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_PRIEST, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_ARMOR, -1 )
    if (( GetUnitCount( FARM_5 ) == 0 )) then
        call SetBuildAll( BUILD_UNIT, 1, FARM_5, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_ENGINEER, -1 )
/// Build towers and fountain at first expansion
    if (( TownHasHall( 1 ) == true )) then
        call SetBuildAll( BUILD_UNIT, 1, FOUNTAIN, 1 )
        call SetBuildAll( BUILD_UNIT, 1, TOWER_COLD_1, 1 )
        call SetBuildAll( BUILD_UNIT, 1, TOWER_FLAME_1, 1 ) 
    endif
/// end build towers and fountain at first expansion
/// Build towers and fountain at second expansion
    if (( TownHasHall( 2 ) == true )) then
        call SetBuildAll( BUILD_UNIT, 1, FOUNTAIN, 2 )
        call SetBuildAll( BUILD_UNIT, 1, TOWER_COLD_1, 2 )
        call SetBuildAll( BUILD_UNIT, 1, TOWER_FLAME_1, 2 )  
    endif
/// end build towers and fountain at second expansion
    call SetBuildAll( BUILD_UNIT, 1, TOWER_ROYAL_1, -1 )
    call SetBuildAll( BUILD_UNIT, 1, TOWER_ROYAL_SKY, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_FACTORY, -1 )
    if (gCond_Need_Research_Center) then
        call SetBuildAll( BUILD_UNIT, 1, RESEARCH_CENTER_1, -1 )
    endif
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_LEATHER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_DOTA_ARCHER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_DOTA_CANNON, -1 )
    call SetBuildAll( BUILD_UNIT, 1, RESEARCH_CENTER_2, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_DOTA_WIZARD, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_DOTA_KNIGHT, -1 )
    if (( ( GetUpgradeLevel( UPG_LEATHER ) + GetUpgradeLevel( UPG_ROGUE ) ) == 2 )) then
        call SetBuildAll( BUILD_UNIT, 1, NINJA, -1 )
    endif
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_MARKET_2, -1 )
    call SetBuildAll( BUILD_UNIT, 1, STONEMASON_TOWER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 4, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 4, UPG_ARMOR, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_PRAYING, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_SORCERY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 5, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 5, UPG_ARMOR, -1 )
    call SetBuildAll( BUILD_UNIT, 1, TOWER_WIZARDRY_2_UPGRADE, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_ENGINEER_2, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_WORKSHOP, -1 )
    call SetBuildAll( BUILD_UNIT, 1, FARM_COTTAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 2, FARM_COTTAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 3, FARM_COTTAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 4, FARM_COTTAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 5, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 6, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 7, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 8, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 1, WIZARD_CHAOS, -1 )
    call SetBuildAll( BUILD_UNIT, 2, WIZARD_CHAOS, -1 )
    call SetBuildAll( BUILD_UNIT, 3, WIZARD_CHAOS, -1 )
    call SetBuildAll( BUILD_UNIT, 4, WIZARD_CHAOS, -1 )
    call SetBuildAll( BUILD_UNIT, 1, ROYAL_MAGUS, -1 )
    call SetBuildAll( BUILD_UNIT, 2, ROYAL_MAGUS, -1 )
    call SetBuildAll( BUILD_UNIT, 3, ROYAL_MAGUS, -1 )
    call SetBuildAll( BUILD_UNIT, 4, ROYAL_MAGUS, -1 )
    call SetBuildAll( BUILD_UPGRADE, 6, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 7, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 8, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 9, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 10, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_RANGED, -1 )
    call SetBuildAll( BUILD_UPGRADE, 11, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 12, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 13, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 14, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 15, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 4, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_RANGED, -1 )
    call SetBuildAll( BUILD_UPGRADE, 16, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 17, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 18, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 19, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 20, UPG_MELEE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_RANGED, -1 )
    call SetBuildAll( BUILD_UNIT, 1, DOZER, -1 )
    call SetBuildAll( BUILD_UNIT, 2, DOZER, -1 )
    call SetBuildAll( BUILD_UNIT, 3, DOZER, -1 )
    call SetBuildAll( BUILD_UNIT, 4, DOZER, -1 )
    call SetBuildAll( BUILD_UPGRADE, 1, UPG_MAGIC_DAMAGE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 5, UPG_MASONRY, -1 )
    call SetBuildAll( BUILD_UPGRADE, 2, UPG_MAGIC_DAMAGE, -1 )
    call SetBuildAll( BUILD_UPGRADE, 3, UPG_MAGIC_DAMAGE, -1 )
    call SetBuildAll( BUILD_UNIT, 9, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 10, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 11, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 12, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 13, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 14, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 15, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 16, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 17, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 18, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 18, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 19, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 20, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 21, ROYAL_KNIGHT_4, -1 )
    call SetBuildAll( BUILD_UNIT, 2, QUESTIONER, -1 )
    call SetBuildAll( BUILD_UNIT, 2, WIZARD, -1 )
    call SetBuildAll( BUILD_UNIT, 2, ROYAL_PRIEST, -1 )
    call SetBuildAll( BUILD_UNIT, 2, EXEMPLAR, -1 )
    call SetBuildAll( BUILD_UNIT, 2, JUSTICAR, -1 )
    call SetBuildAll( BUILD_UNIT, 2, ROYAL_SPEAR, -1 )
    call SetBuildAll( BUILD_UNIT, 2, GENERAL, -1 )
endfunction

//===========================================================================
// Specifies harvesting priorities for workers
//===========================================================================
function HarvestPriorities takes nothing returns nothing
    local integer mine = TownWithMine()
    local integer allGold = GetUnitCountDone(PEASANT)
    local integer allWood = GetUnitCountDone(PEASANT)
    local integer numWorkers
    set numWorkers = 4
    call HarvestGold( 0, numWorkers )
    set numWorkers = 4
    call HarvestGold( 1, numWorkers )
    set numWorkers = 4
    call HarvestGold( 2, numWorkers )
    set numWorkers = 3
    call HarvestGold( 3, numWorkers )
    set numWorkers = 12
    call HarvestWood( mine, numWorkers )
endfunction

//===========================================================================
// Determines all building and harvesting assignments for workers
//===========================================================================
function WorkerAssignment takes nothing returns nothing
    loop
        call UpdateConditions(  )

        // Harvesting
        call ClearHarvestAI(  )
        call HarvestPriorities(  )

        // Building
        call InitBuildArray(  )
        call BuildPriorities(  )

        call Sleep( 2 )
    endloop
endfunction

//***************************************************************************
//*
//*  Attacking
//*
//***************************************************************************

//===========================================================================
// Returns true if the minimum forces for an attack exist
//===========================================================================
function HaveMinimumAttackers takes nothing returns boolean
    local integer count
    // Check for attack wave limit
    if (attackWave > 21) then
        return false
    endif

    // First Hero Only
    if (GetUnitCountDone(hero_id) < 1) then
        return false
    endif

return true
endfunction

//===========================================================================
// Assigns units to attack based on the given attack group
//===========================================================================
function PrepareAttackGroup takes integer groupID returns nothing
    local integer all

    // Attack Group #1: All Units
    if (groupID == 1) then
    set all = GetUnitCountDone( hero_id )
    call SetAssaultGroup( all, all, hero_id )
    if (( GetUpgradeLevel( UPG_LEATHER ) == 1 )) then
        set all = GetUnitCountDone( hero_id2 )
        call SetAssaultGroup( all, all, hero_id2 )
    endif
    set all = GetUnitCountDone( FOOTMAN )
    call SetAssaultGroup( all, all, FOOTMAN )
    set all = GetUnitCountDone( HIGH_ARCHER )
    call SetAssaultGroup( all, all, HIGH_ARCHER )
    set all = GetUnitCountDone( MORTAR )
    call SetAssaultGroup( all, all, MORTAR )
    set all = GetUnitCountDone( CHAPLAIN )
    call SetAssaultGroup( all, all, CHAPLAIN )
    set all = GetUnitCountDone( HIGH_FOOTMAN )
    call SetAssaultGroup( all, all, HIGH_FOOTMAN )
    set all = GetUnitCountDone( SPEAR_MAN )
    call SetAssaultGroup( all, all, SPEAR_MAN )
    set all = GetUnitCountDone( QUESTIONER )
    call SetAssaultGroup( all, all, QUESTIONER )
    set all = GetUnitCountDone( SORCERESS )
    call SetAssaultGroup( all, all, SORCERESS )
    set all = GetUnitCountDone( WIZARD )
    call SetAssaultGroup( all, all, WIZARD )
    set all = GetUnitCountDone( HYDROMANCER )
    call SetAssaultGroup( all, all, HYDROMANCER )
    set all = GetUnitCountDone( PRIEST )
    call SetAssaultGroup( all, all, PRIEST )
    set all = GetUnitCountDone( EXEMPLAR )
    call SetAssaultGroup( all, all, EXEMPLAR )
    set all = GetUnitCountDone( JUSTICAR )
    call SetAssaultGroup( all, all, JUSTICAR )
    set all = GetUnitCountDone( ROYAL_SPEAR )
    call SetAssaultGroup( all, all, ROYAL_SPEAR )
    set all = GetUnitCountDone( GENERAL )
    call SetAssaultGroup( all, all, GENERAL )
    set all = GetUnitCountDone( ROGUE )
    call SetAssaultGroup( all, all, ROGUE )
    set all = GetUnitCountDone( ROYAL_PRIEST )
    call SetAssaultGroup( all, all, ROYAL_PRIEST )
    set all = GetUnitCountDone( BISHOP )
    call SetAssaultGroup( all, all, BISHOP )
    set all = GetUnitCountDone( NINJA )
    call SetAssaultGroup( all, all, NINJA )
    set all = GetUnitCountDone( WIZARD_CHAOS )
    call SetAssaultGroup( all, all, WIZARD_CHAOS )
    set all = GetUnitCountDone( ROYAL_MAGUS )
    call SetAssaultGroup( all, all, ROYAL_MAGUS )
    set all = GetUnitCountDone( KNIGHT )
    call SetAssaultGroup( all, all, KNIGHT )
    set all = GetUnitCountDone( ROYAL_KNIGHT_2 )
    call SetAssaultGroup( all, all, ROYAL_KNIGHT_2 )
    set all = GetUnitCountDone( ROYAL_KNIGHT_3 )
    call SetAssaultGroup( all, all, ROYAL_KNIGHT_3 )
    set all = GetUnitCountDone( ROYAL_KNIGHT_4 )
    call SetAssaultGroup( all, all, ROYAL_KNIGHT_4 )
    set all = GetUnitCountDone( BIG_BERTHA )
    call SetAssaultGroup( all, all, BIG_BERTHA )
    set all = GetUnitCountDone( DOZER )
    call SetAssaultGroup( all, all, DOZER )
    endif
endfunction

//===========================================================================
// Prepares an attack group based on the current attack wave
//===========================================================================
function PrepareForces takes nothing returns nothing
        call PrepareAttackGroup( 1 )
endfunction

//===========================================================================
// 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

//===========================================================================
// Advances attack wave counter
//===========================================================================
function AttackWaveUpdate takes nothing returns nothing
    call AttackWaveDelay( attackWave )
    set attackWave = attackWave + 1
    if (attackWave > 2) then
        set attackWave = 2
        set nextDelay = attackWave + 1
    endif
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 (Tier < 3) then
        if (target == null) then
            set target = GetCreepCamp( 0, 9, false )
        endif
    endif

        // Target Priority #2
    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

    // 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

//===========================================================================
// Determines all attacking assignments
//===========================================================================
function AttackAssignment takes nothing returns nothing
    call StaggerSleep( 0, 2 )
    if (attackWave == 1) then
        call AttackWaveDelay( 0 )
    endif
    loop
        loop
            call UpdateConditions(  )
            exitwhen (HaveMinimumAttackers() and not CaptainRetreating())
            call Sleep( 2 )
        endloop
        call RemoveInjuries(  )
        call ResetAttackUnits(  )
        call PrepareForces(  )
        call LaunchAttack(  )
    endloop
endfunction

//***************************************************************************
//*
//*  Misc.
//*
//***************************************************************************

//===========================================================================
// 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

//***************************************************************************
//*
//*  Main Entry Point
//*
//***************************************************************************

//===========================================================================
function main takes nothing returns nothing
    call InitAI(  )
    call SetPlayerName( ai_player, "Insane King XXIV" )
    call InitOptions(  )
    call SelectHeroes(  )
    call CreateCaptains(  )
    call SetHeroLevels( function ChooseHeroSkill )

    call Sleep( 0.1 )
    call StartThread( function WorkerAssignment )
    call StartThread( function AttackAssignment )
    call StartThread( function ConditionLoop )
    call PlayGame(  )
endfunction
 
Last edited:
Level 18
Joined
Mar 16, 2008
Messages
721
Do you think adding a unique debug message before each function would help to discern what causes the crash?
I've been considering that as of today. Perhaps a debug message after each line stating the previous line's number. My hesitation was I thought AI scripts "loaded" up and didn't run after that??? IDK. Do you know if that's the case? but I suppose I will resort to that even if it might not do anything. Then the last debug message shown in the replay should give me a hint. I'm about to get very busy IRL so I'll try this next weekend.
 
Level 19
Joined
Feb 27, 2019
Messages
590
The AI functions in realtime. Hopefully the correct debug message will have time to be shown since it happens as the function runs... best to place it before any function I think.
 
Level 18
Joined
Mar 16, 2008
Messages
721
In that case I'll put the message before the function. Hmm but also thinking if I add a debug message to each line, it will change every line's number after that. Not a big problem.

Any recommendation for what debug message function to use for AI script?
 
Level 18
Joined
Mar 16, 2008
Messages
721
the ai script still crashes the game.

1) debug messages don't work
because debug don't get sent fast enough before it crashes. so "wait system" was created but unfortunately because JASS needs local variables to be set at the start of the function, this system doesn't work either. if you wait after the locals are set, they seem to be "forgotten," pretty much breaking said function. so i tried using process of elimination but this didn't help me pin point any function.

2) changing functions and adding conditions was a dead end but maybe a solution is still down this path
from there i tried getting rid of some functions and changing some functions, checking no impossible priorities are given in the build order, and adding more conditions. I think it might be related to the attacking function because I got rid of that. Maybe I need to add a loop brake system to all the loops?

3) crash causes outside of the AI script
i again reviewed other causes of ai crashes outside of script causes.
-object data pathing, i found one building with no pathing. so that was updated but crashes still occur.
- changed "script run" trigger from "melee" to campaign which does nothing apparently.

i suspect the problem/solution is within the ai script and has something to do with the attack function because it happens at seemingly random times and sometimes doesn't happen at all, but i really don't know...
 
Last edited:
Level 12
Joined
Jun 15, 2016
Messages
472
This looks like its based on the editor created AI, blech:thumbs_down:

First, regarding debug messages: don't use debugS - it doesn't work, and don't use BJDebugMsg - as BJ functions don't work in AI scripts. Instead, use DisplayTextToPlayer as I see you have, just make sure the debug player who gets the message is you.
If you think that is not working, go to the starting function (main) and put a message there, see if you get anything.

As for possible causes for a crash, there are a LOT. Frankly, you need to cleanup your AI script from a lot of problems. Starting with this: wherever you see 4 characters inside 'single quotes', like 'hpea', you should replace it with a constant indicative name. This is the start for naming cleanup you need to do, another example for a change: you commented out the PEASANT constant, but you're using it a lot to build all your structures, while i assume the real builder unit is ROYAL_ENGINEER. Also, make sure to set the BUILD_UNIT to your actual builder unit, peasant, royal engineer, whatever it is.

Finally, I'd suggest taking a longer read of the script you have so far and see what you actually need from it. Editor AI contains a lot of stuff, which can cause crashes especially if you build things on it. Unrelated functions which persist in the script like checking for expansions might crash your script unexpectedly, ESPECIALLY if your build unit doesn't exist.
 
Level 12
Joined
Jun 15, 2016
Messages
472
I take it this means building a non-existing unit via AI will cause a crash?
Not sure about that specifically. IIRC the AI relies on not necessarily being able to build units that are requested to function properly. So for example requesting to create a knight when you still have just a town hall is fine, but a unit code for something that doesn't exist should probably just not be there.

Actually what I meant with that is this: I think the unit that does the building has to exist, because its in a lot of places. By the way does the AI ever try to build expansions?
 
Level 18
Joined
Mar 16, 2008
Messages
721
By the way does the AI ever try to build expansions?
yes it does successfully build expansions but seems to not rebuild them if they are destroyed, not a major issue compared to the crash. I'm testing a simpler script, also based on editor template, with out the Tier commands and extra stuff right now. I don't seem to see any pattern with the debug messages.
 
Level 18
Joined
Mar 16, 2008
Messages
721
i really appreciate your feedback.

I'll try replacing attack system with something simpler from your tutorial and go from there. maybe it's sleeping to find a target for wave 20, for example, then also attack wave 21 fires, causing some problem.
 
Level 18
Joined
Mar 16, 2008
Messages
721
UPDATE: I think you were right it was related to the expansion function. It's trying to expand to islands. I removed the goldmines on the islands and it's been running for about 9 hours now.

UPDATE 2: it did crash on another test but i also changed some other things in the script. really confused how it ran that long without crashing... still trying to get to the bottom this...

QUESTION: is there any way to stop AI from expanding to certain locations?

JASS:
function BuildExpansion takes integer hallID, integer mineID returns nothing
    if (HallsCompleted(hallID)) then
        call SetBuildExpa( TownCount(hallID) + 1, mineID )
    endif
endfunction

JASS:
function BuildPriorities takes nothing returns nothing
    call BuildExpansion (TOWN_HALL, TOWN_HALL)
endfunction
 
Last edited:
Level 12
Joined
Jun 15, 2016
Messages
472
I never tested working with expansions, but from what I could gather, they work like this:

JASS:
// ALL AI EXPANSION NATIVES

native GetNextExpansion     takes nothing                               returns integer
native SetExpansion         takes unit peon, integer id                 returns boolean
native GetExpansionFoe      takes nothing                               returns unit
native GetEnemyExpansion    takes nothing                               returns unit
native GetExpansionX        takes nothing                               returns integer
native GetExpansionY        takes nothing                               returns integer
native GetExpansionPeon     takes nothing                               returns unit

The AI initially maps all valid expansion locations (i.e. gold mines, I think?) and from then on they are referred to by some integer id. For example, the standard way for the AI to take an expansion is this function I believe:

Code:
function StartExpansion takes integer qty, integer hall returns boolean
    local integer count
    local integer town
    local unit    peon
    local integer gold_cost

    set count = TownCount(hall)
    if count >= qty then
        return true
    endif

    set town = GetNextExpansion()
    if town == -1 then
        return true
    endif

    set take_exp = true

    set gold_cost = GetUnitGoldCost(hall)
    if gold_cost > total_gold then
        return false
    endif
    set total_gold = total_gold - gold_cost

    if GetExpansionFoe() != null then
        return true
    endif

    set peon = GetExpansionPeon()
    if peon != null then
        return SetExpansion(peon,hall)
    endif

    return true
endfunction

The only thing we can do with an expansion location, is to get the XY coordinates of the next expansion the AI wants to take only (using GetExpansionX and GetExpansionY). So, you can create a copy in this function, and in it check the location of the next expansion. If it's not in a good spot, skip it.
 
Level 18
Joined
Mar 16, 2008
Messages
721
Great idea. Thank you so much for sharing this. I am so grateful for your attention on this barely understood topic.

The islands on my map are near the exterior. So maybe I could just make a condition along the lines of if ((X > n) and (X < n)), something similar but more fleshed out obviously.

However, unfortunately, after more testing with island goldmines removed, it seems that the crash is still occurring. Maybe creep respawns bugging it out? seems I jumped the gun on the island expansion theory but it held true for a few tests...
Perhaps I'll try the function above and see how it behaves.

It's just such a pain to test this script for crashing because some games it does not crash, other games it takes 4 hours to crash.

UPDATE:
the StartExpansion function seems to just build an expansion each time you list it in build priorities. This function also has some trouble re-building town halls if they are destroyed.

the BuildExpansion function seems to enable "smart expanding" which is more desirable, aside from the crashing. When a limit condition is added to this function of 2 town halls, it seems to not crash. When I raise it to 5, it crashes. so the 3rd or 4th expansion seems to crash it for some reason. Remember it still crashes when we removed the islands. So perhaps it's the creep respawn???

If anyone has extra time to look at my map and imagine what would cause this, that'd be great. I also tested with all flattened terrain and the crash still occurred. I think creeps are the only possibility.
 
Last edited:
Status
Not open for further replies.
Top