1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[AI] Two Custom Campaign AIs (Examples)

Discussion in 'Triggers & Scripts' started by Tommi, Jul 10, 2004.

  1. Tommi

    Tommi

    Joined:
    Jun 16, 2004
    Messages:
    237
    Resources:
    0
    Resources:
    0
    I made two custom campaign AIs for a new version of Defenders of Dwarvenkind (not yet released).

    Lich King's Chosen AI
    This AI does not build units. It waits until a unit owned by player 1 is in a specified region and then attacks it with all its units. The AI includes a set of functions for determining all units of the AI player and thus you don't need to know them beforehand, nor make changes if you add more preplaced units to the AI player. If you added more units to the AI player through triggers during the game, you would need to move call PrepareAssaultGroup() in function AttackAssignment before call AttackMoveKill(u).

    Code (Text):
    //===========================================================================
    //
    //  Lich King's Chosen AI
    //  Defenders of Dwarvenkind
    //
    //  By Tommi Gustafsson
    //
    //===========================================================================

    globals
        integer array CountedID
        integer array CountedNumber
        integer array Command
        integer array Data
        integer FirstFree = 0
    endglobals

    //===========================================================================
    // CountUnit, ClearCount, and AddCountedToAssault functions
    //===========================================================================

    function CountUnit takes integer unitid returns nothing
        local integer i = 0

        loop
            if i == FirstFree then
                set CountedID[i] = unitid
                set CountedNumber[i] = 1
                set FirstFree = FirstFree + 1
                return
            endif
            if CountedID[i] == unitid then
                set CountedNumber[i] = CountedNumber[i] + 1
                return
            endif
            set i = i + 1
        endloop

    endfunction

    function ClearCount takes nothing returns nothing
        set FirstFree = 0
    endfunction

    function AddCountedToAssault takes nothing returns nothing
        local integer i = 0

        loop
            exitwhen i == FirstFree
            call AddAssault(CountedNumber[i],CountedID[i])
            set i = i + 1
        endloop
    endfunction


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

    //===========================================================================
    // Prepares an assault group
    //===========================================================================
    function PrepareAssault takes nothing returns nothing
        local group g = CreateGroup()
        local unit u = null
        local integer id

        call ClearCount()
        call GroupEnumUnitsOfPlayer(g, ai_player, null)

        set u = FirstOfGroup(g)
        loop
            exitwhen u == null
            set id = GetUnitTypeId(u)

            //If the unit is not hidden, building, or worker, assign it to the attack group
            if (not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_PEON) and not IsUnitHidden(u) and GetUnitState(u, UNIT_STATE_LIFE) > 0) then
                call CountUnit(id)
            endif
            call GroupRemoveUnit(g, u)
            set u = FirstOfGroup(g)
        endloop
        call DestroyGroup(g)
        call AddCountedToAssault()

    endfunction

    //===========================================================================
    // Determines all attacking assignments
    //===========================================================================
    function AttackAssignment takes nothing returns nothing
        local group g = CreateGroup()
        local rect r = Rect(-2700.0,-13100.0,5700.0,-7900.0)
        local unit u = null
        local boolean targetfound = false

        call StaggerSleep( 0, 2 )

        call PrepareAssault()

        loop
            call GroupEnumUnitsInRect(g, r, null)
            set u = FirstOfGroup(g)
            loop
                exitwhen u == null
                if (GetOwningPlayer(u) == Player(0) and GetUnitState(u, UNIT_STATE_LIFE) > 0) then
                    set targetfound = true
                endif
                exitwhen targetfound == true
                call GroupRemoveUnit(g, u)
                set u = FirstOfGroup(g)
            endloop
           
            if targetfound == true then
                //call DisplayTimedTextToPlayer(Player(0),0,0,5,"Target Found\n")
                set targetfound = false
                call AttackMoveKill(u)
            elseif not CaptainIsHome() then
                //call DisplayTimedTextToPlayer(Player(0),0,0,5,"Returning Home\n")
                call AttackMoveXY(-1150, -10000)
            endif

            call Sleep(2)

        endloop
    endfunction

    //***************************************************************************
    //*  Main Entry Point
    //***************************************************************************
    function main takes nothing returns nothing
        call InitAI(  )
        call InitOptions(  )
        call CreateCaptains(  )
        call SetCaptainHome(BOTH_CAPTAINS, -1150.0, -10000.0)

        call Sleep( 0.1 )
        call StartThread( function AttackAssignment )
        call PlayGame(  )
    endfunction
    Undead Wave AI
    The AI builds building, upgrades, and units. The AI sends them to attack two targets (the Dwarven Gate and the Vault of the Dwarven Kings) in queue after receiving Attack Order 1 through the trigger action "AI - Send AI Command." The attack waves vary; they are defined in SetWave function.

    The undead army includes three heros: Doomlord (custom hero), Death Knight, and Lich. Their skill order is defined in function SelectHeroes.

    Code (Text):
    //===========================================================================
    //
    //   Custom WarCraft III Undead AI Script
    //   Defenders of Dwarvenkind
    //   Dwarf Campaign - Chapter 1
    //
    //   By Tommi Gustafsson
    //
    //===========================================================================

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

    globals
        unit array              TargetQueue
        integer array           WaveUnit
        integer array           WaveQuantity
       
        integer                 WaveUnits                   = 0
        integer                 CurrentWave                 = 1
       
        integer                 Difficulty                  = 1
        integer                 AttackOrder                 = 0
        integer                 harvestingghouls            = 5

    endglobals

    //============================================================================
    //  Utility Functions
    //  Dig2Str and Int2Str
    //  By AIAndy
    //============================================================================
    function Dig2Str takes integer i returns string
      if i == 1 then
        return "1"
      elseif i == 2 then
        return "2"
      elseif i == 3 then
        return "3"
      elseif i == 4 then
        return "4"
      elseif i == 5 then
        return "5"
      elseif i == 6 then
        return "6"
      elseif i == 7 then
        return "7"
      elseif i == 8 then
        return "8"
      elseif i == 9 then
        return "9"
      else
        return "0"
      endif
    endfunction

    function Int2Str takes integer ic returns string
        local string s = ""
        local integer i = ic
        local integer ialt = 0
        local boolean neg = false

        if i == 0 then
          return "0"
        endif
        if i < 0 then
          set neg = true
          set i = (-1)*i
        endif
        loop
          exitwhen i == 0
          set ialt = i
          set i = i / 10
          set s = Dig2Str( ialt - 10*i ) + s
        endloop
        if neg then
          set s = "-"+s
        endif
        return s
    endfunction

    //===========================================================================
    // Initializes TargetQueue
    //===========================================================================
    function SetTargetUnits takes nothing returns nothing
        local group g = CreateGroup()
        local unit u = null
        local integer id

        call GroupEnumUnitsOfPlayer(g, Player(0), null)

        set u = FirstOfGroup(g)
        loop
            exitwhen u == null
            set id = GetUnitTypeId(u)
            if(id == 'h000') then
                set TargetQueue[0] = u
            elseif(id == 'nmgv') then
                set TargetQueue[1] = u
            endif

            call GroupRemoveUnit(g, u)
            set u = FirstOfGroup(g)
        endloop

        call DestroyGroup(g)

        set TargetQueue[2] = null

    endfunction

    //===========================================================================
    // SuicideOnQueue
    //
    // Description: Attacks units on a queue, prioritizing earlier targets on the
    // queue over later ones. Will reattack the targets, if they are revived.
    //
    // Returns true when all units on the queue are destroyed or hidden
    // Returns false when the attack group is destroyed
    //===========================================================================
    function SuicideOnQueue takes nothing returns boolean
        local unit target = null
        local integer i = 0

        loop
            //looks for a valid target, beginning from the start of the queue every 2 seconds

            set target = null
            set i = 0

            loop
                set target = TargetQueue[i]

                //Exit when all targets have been destroyed
                if target == null then
                    return true
                endif

                //Exit when a target that is alive and not hidden is found
                exitwhen (UnitAlive(target) and not IsUnitHidden(target))

                set i = i + 1
            endloop

            //exit if the attack group has been destroyed
            if CaptainIsEmpty() then
                return false
            endif

            //call DisplayTimedTextToPlayer(Player(0),0,0,5,"CaptainReadiness: "+Int2Str(CaptainReadiness())+"\n")

            if CaptainReadiness() < 10 then
                call CaptainGoHome()
                return false
            endif

            //Attack the target
            call AttackMoveKill(target)

            //Sleep for 2 seconds before performing any other actions
            call Sleep(2)

        endloop

        //Necessary to add because of the return check
        return false

    endfunction

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

    //===========================================================================
    // Updates AttackOrder and Difficulty
    //===========================================================================
    function UpdateConditions takes nothing returns nothing
        if CommandsWaiting() != 0 then
            set AttackOrder = GetLastCommand()
            set Difficulty = GetLastData()
            call PopLastCommand()
        endif
    endfunction

    //===========================================================================
    // Stores hero ID and skills
    //===========================================================================
    function SelectHeroes takes nothing returns nothing
        //First Hero = Doomlord
        set hero_id = 'U000'
        set skills1[ 1] = 'AHfs'
        set skills1[ 2] = 'AEmb'
        set skills1[ 3] = 'AUav'
        set skills1[ 4] = 'AHfs'
        set skills1[ 5] = 'AUav'
        set skills1[ 6] = 'ANdo'
        set skills1[ 7] = 'AHfs'
        set skills1[ 8] = 'AUav'
        set skills1[ 9] = 'AEmb'
        set skills1[10] = 'AEmb'

        //Second Hero = Death Knight
        set hero_id2 = 'Udea'
        set skills2[ 1] = 'AUdc'
        set skills2[ 2] = 'AUau'
        set skills2[ 3] = 'AUdc'
        set skills2[ 4] = 'AUau'
        set skills2[ 5] = 'AUdc'
        set skills2[ 6] = 'AUan'
        set skills2[ 7] = 'AUau'
        set skills2[ 8] = 'AUdp'
        set skills2[ 9] = 'AUdp'
        set skills2[10] = 'AUdp'

        //Third Hero = Lich
        set hero_id3 = 'Ulic'
        set skills3[ 1] = 'AUfn'
        set skills3[ 2] = 'AUdr'
        set skills3[ 3] = 'AUfn'
        set skills3[ 4] = 'AUdr'
        set skills3[ 5] = 'AUfn'
        set skills3[ 6] = 'AUdd'
        set skills3[ 7] = 'AUdr'
        set skills3[ 8] = 'AUfa'
        set skills3[ 9] = 'AUfa'
        set skills3[10] = 'AUfa'

    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]
        elseif (curHero == hero_id3) then
            return skills3[level]
        endif
        return 0
    endfunction

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


    //===========================================================================
    // Specifies AttackWaves
    //===========================================================================
    function SetWave takes integer wave returns nothing
        if wave == 1 then
            set WaveUnits = 2
            set WaveUnit[0] = 'ugho'
            set WaveQuantity[0] = 18
            set WaveUnit[1] = hero_id
            set WaveQuantity[1] = 1
        elseif wave == 2 then
            set WaveUnits = 2
            set WaveUnit[0] = 'ucry'
            set WaveQuantity[0] = 13
            set WaveUnit[1] = hero_id2
            set WaveQuantity[1] = 1
        elseif wave == 3 then
            set WaveUnits = 2
            set WaveUnit[0] = 'umtw'
            set WaveQuantity[0] = 11
            set WaveUnit[1] = hero_id3
            set WaveQuantity[1] = 1
        elseif wave == 4 then
            set WaveUnits = 2
            set WaveUnit[0] = 'uabo'
            set WaveQuantity[0] = 12
            set WaveUnit[1] = hero_id
            set WaveQuantity[1] = 1
        elseif wave == 5 then
            set WaveUnits = 4
            set WaveUnit[0] = 'ugho'
            set WaveQuantity[0] = 16
            set WaveUnit[1] = 'unec'
            set WaveQuantity[1] = 8
            set WaveUnit[2] = hero_id
            set WaveQuantity[2] = 1
            set WaveUnit[3] = hero_id3
            set WaveQuantity[3] = 1
        elseif wave == 6 then
            set WaveUnits = 4
            set WaveUnit[0] = 'uabo'
            set WaveQuantity[0] = 7
            set WaveUnit[1] = 'ucry'
            set WaveQuantity[1] = 8
            set WaveUnit[2] = hero_id
            set WaveQuantity[2] = 1
            set WaveUnit[3] = hero_id2
            set WaveQuantity[3] = 1
        elseif wave == 7 then
            set WaveUnits = 4
            set WaveUnit[0] = 'ugho'
            set WaveQuantity[0] = 16
            set WaveUnit[1] = 'umtw'
            set WaveQuantity[1] = 6
            set WaveUnit[2] = hero_id
            set WaveQuantity[2] = 1
            set WaveUnit[3] = hero_id2
            set WaveQuantity[3] = 1
        elseif wave == 8 then
            set WaveUnits = 4
            set WaveUnit[0] = 'uabo'
            set WaveQuantity[0] = 12
            set WaveUnit[1] = 'unec'
            set WaveQuantity[1] = 6
            set WaveUnit[2] = hero_id
            set WaveQuantity[2] = 1
            set WaveUnit[3] = hero_id3
            set WaveQuantity[3] = 1
        elseif wave == 9 then
            set WaveUnits = 6
            set WaveUnit[0] = 'ugho'
            set WaveQuantity[0] = 12
            set WaveUnit[1] = 'unec'
            set WaveQuantity[1] = 6
            set WaveUnit[2] = 'umtw'
            set WaveQuantity[2] = 6
            set WaveUnit[3] = hero_id
            set WaveQuantity[3] = 1
            set WaveUnit[4] = hero_id2
            set WaveQuantity[4] = 1
            set WaveUnit[5] = hero_id3
            set WaveQuantity[5] = 1
        elseif wave == 10 then
            set WaveUnits = 6
            set WaveUnit[0] = 'uabo'
            set WaveQuantity[0] = 7
            set WaveUnit[1] = 'ucry'
            set WaveQuantity[1] = 8
            set WaveUnit[2] = 'unec'
            set WaveQuantity[2] = 6
            set WaveUnit[3] = hero_id
            set WaveQuantity[3] = 1
            set WaveUnit[4] = hero_id2
            set WaveQuantity[4] = 1
            set WaveUnit[5] = hero_id3
            set WaveQuantity[5] = 1
        elseif wave == 11 or wave == 13 or wave == 15 then
            set WaveUnits = 6
            set WaveUnit[0] = 'ugho'
            set WaveQuantity[0] = 12
            set WaveUnit[1] = 'unec'
            set WaveQuantity[1] = 6
            set WaveUnit[2] = 'umtw'
            set WaveQuantity[2] = 8
            set WaveUnit[3] = hero_id
            set WaveQuantity[3] = 1
            set WaveUnit[4] = hero_id2
            set WaveQuantity[4] = 1
            set WaveUnit[5] = hero_id3
            set WaveQuantity[5] = 1
        else
            set WaveUnits = 6
            set WaveUnit[0] = 'uabo'
            set WaveQuantity[0] = 7
            set WaveUnit[1] = 'ucry'
            set WaveQuantity[1] = 8
            set WaveUnit[2] = 'unec'
            set WaveQuantity[2] = 8
            set WaveUnit[3] = hero_id
            set WaveQuantity[3] = 1
            set WaveUnit[4] = hero_id2
            set WaveQuantity[4] = 1
            set WaveUnit[5] = hero_id3
            set WaveQuantity[5] = 1
        endif
    endfunction

    //===========================================================================
    // Specifies building priorities
    //===========================================================================

    //Builds necropolis, first workers and goldmine
    function BuildBasics takes nothing returns nothing
        //Necropolis, 0/10
        call SetBuildAll( BUILD_UNIT, 1, 'unpl', -1 )

        //2 Gold Mines
        call SetBuildAll( BUILD_UNIT, 2, 'ugol', -1 )

        //5 Acolytes, 5/10
        call SetBuildAll( BUILD_UNIT, 5, 'uaco', -1 )

        //Crypt
        call SetBuildAll( BUILD_UNIT, 1, 'usep', -1 )

        //Altar
        call SetBuildAll( BUILD_UNIT, 1, 'uaod', -1 )

        //3 ziggurats, 5/40
        call SetBuildAll( BUILD_UNIT, 3, 'uzig', -1 )

        //Graveyard
        call SetBuildAll( BUILD_UNIT, 1, 'ugrv', -1 )

        //5 acolytes, 10/40
        call SetBuildAll( BUILD_UNIT, 10, 'uaco', -1 )

        // Hero 15/40
        call SetBuildAll( BUILD_UNIT, 1, hero_id, -1 )

        //5 lumber ghouls,  25/30
        call SetBuildAll( BUILD_UNIT, 5, 'ugho', -1 )
    endfunction

    function BuildBuildings takes nothing returns nothing
        local integer i = 0

        // build to 9 ziggurats
        call SetBuildAll( BUILD_UNIT, 9, 'uzig', -1 )
       
        //Halls of the Dead
        call SetBuildAll( BUILD_UNIT, 1, 'unp1', -1 )

        //Tomb of Relics
        call SetBuildAll( BUILD_UNIT, 1, 'utom', -1 )

        //2nd crypt
        call SetBuildAll( BUILD_UNIT, 2, 'usep', -1 )

        //2 Temples of the Damned
        call SetBuildAll( BUILD_UNIT, 2, 'utod', -1 )

        //Upgrade to Black Citadel
        call SetBuildAll( BUILD_UNIT, 1, 'unp2', -1 )

        //Build 3 Slaughterhouses
        call SetBuildAll( BUILD_UNIT, 3, 'uslh', -1 )

        //Altar 2
        call SetBuildAll( BUILD_UNIT, 2, 'uaod', -1 )

    endfunction

    //Builds heroes 2 and 3
    function BuildExtraHeroes takes nothing returns nothing
        call SetBuildAll( BUILD_UNIT, 1, hero_id2, -1 )
        call SetBuildAll( BUILD_UNIT, 1, hero_id3, -1 )
    endfunction

    //Builds upgrades
    function BuildUpgrades takes nothing returns nothing
        local integer i = 0
       
        //1/1 upgrades, if Experienced
        if (Difficulty>=2) then
            call SetBuildAll( BUILD_UPGRADE, 1, 'Rume', -1 )
            call SetBuildAll( BUILD_UPGRADE, 1, 'Ruar', -1 )
            call SetBuildAll( BUILD_UPGRADE, 1, 'Rura', -1 )
            call SetBuildAll( BUILD_UPGRADE, 1, 'Rucr', -1 )
        endif

        //Necro upgrades
        call SetBuildAll( BUILD_UPGRADE, 1, 'Rune', -1 )
        call SetBuildAll( BUILD_UPGRADE, 2, 'Rune', -1 )
        call SetBuildAll( BUILD_UPGRADE, 1, 'Rusm', -1 )
        call SetBuildAll( BUILD_UPGRADE, 1, 'Rusl', -1 )

        //2/2 upgrades, if Professional
        if (Difficulty>=3) then
            call SetBuildAll( BUILD_UPGRADE, 2, 'Rume', -1 )
            call SetBuildAll( BUILD_UPGRADE, 2, 'Ruar', -1 )
            call SetBuildAll( BUILD_UPGRADE, 2, 'Rura', -1 )
            call SetBuildAll( BUILD_UPGRADE, 2, 'Rucr', -1 )
        endif

        //Exhume & Disease cloud
        call SetBuildAll( BUILD_UPGRADE, 1, 'Ruex', -1 )
        call SetBuildAll( BUILD_UPGRADE, 1, 'Rupc', -1 )

        //3/3 if Champion (not available)
        if (Difficulty>=4) then
            call SetBuildAll( BUILD_UPGRADE, 3, 'Rume', -1 )
            call SetBuildAll( BUILD_UPGRADE, 3, 'Ruar', -1 )
            call SetBuildAll( BUILD_UPGRADE, 3, 'Rura', -1 )
            call SetBuildAll( BUILD_UPGRADE, 3, 'Rucr', -1 )
        endif

        call SetBuildAll( BUILD_UNIT, 8, 'uzg1', -1 )
        call SetBuildAll( BUILD_UNIT, 1, 'uzg2', -1 )

    endfunction

    //Builds the appropriate Attack Wave
    function BuildWave takes nothing returns nothing
        local boolean unitsbuilt = false
        local integer i = 0
        local integer qty = 1
               
        loop
            if WaveQuantity[i] >= qty then
                if WaveUnit[i] == 'ugho' then
                    call SetBuildAll( BUILD_UNIT, qty + harvestingghouls, WaveUnit[i], -1 )
                else
                    call SetBuildAll( BUILD_UNIT, qty, WaveUnit[i], -1 )
                endif          
                set unitsbuilt=true
            endif
           
            set i = i + 1
            exitwhen (i == WaveUnits and unitsbuilt == false)

            if i == WaveUnits then
                set i = 0
                set unitsbuilt = false
                set qty = qty + 1
            endif
        endloop
    endfunction

    //Calls all building functions
    function BuildPriorities takes nothing returns nothing
        call BuildBasics()
        call BuildBuildings()
        call BuildExtraHeroes()
        call BuildUpgrades()
        call BuildWave()
    endfunction

    //===========================================================================
    // Specifies harvesting priorities for workers
    //===========================================================================
    function HarvestPriorities takes nothing returns nothing
        local integer mine = TownWithMine()
        local integer allGold = GetUnitCountDone('uaco')
        local integer allWood = GetUnitCountDone('ugho')
        local integer goldacos1 = 0
        local integer goldacos2 = 0
        local integer lumberghouls = 0

        if allGold < 5 then
            set goldacos1 = allGold
            set goldacos2 = 0
        elseif allGold < 10 then
            set goldacos1 = 5
            set goldacos2 = allGold - 5
        else
            set goldacos1 = 5
            set goldacos2 = 5
        endif

        if allWood < harvestingghouls then
            set lumberghouls = allWood
        else
            set lumberghouls = harvestingghouls
        endif

        call HarvestGold( mine + 0, goldacos1 )
        call HarvestGold( mine + 1, goldacos2 )
        call HarvestWood( 0, lumberghouls )

    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

    //===========================================================================
    // Returns true if the minimum forces for an attack exist
    //===========================================================================
    function HaveMinimumAttackers takes nothing returns boolean
        local integer i = 0
        local integer tempqty = 0

        if (GetUnitCountDone( hero_id ) < 1 or GetUnitCountDone( hero_id2 ) < 1 or GetUnitCountDone( hero_id3 ) < 1) then
            return false
        endif
       
        loop
            exitwhen i == WaveUnits
            if WaveUnit[i] == 'ugho' then
                set tempqty = WaveQuantity[i] + harvestingghouls
            else
                set tempqty = WaveQuantity[i]
            endif
            if GetUnitCountDone(WaveUnit[i]) < tempqty then
                return false
            endif
            set i = i + 1
        endloop
       
        return true
       
    endfunction

    //===========================================================================
    // Assigns units to attack
    //===========================================================================
    function PrepareAttackGroup takes nothing returns nothing
        local integer i = 0

        loop
            exitwhen i == WaveUnits
            call SetAssaultGroup(WaveQuantity[i], WaveQuantity[i], WaveUnit[i] )
            set i = i + 1
        endloop
    endfunction

    //===========================================================================
    // Determines all attacking assignments
    //===========================================================================
    function AttackAssignment takes nothing returns nothing
        local boolean AttackSuccess = false
       
        call StaggerSleep( 0, 2 )
       
        loop
            loop
                call UpdateConditions(  )
                exitwhen ( AttackOrder > 0 and HaveMinimumAttackers() and not CaptainRetreating() and not CaptainInCombat(true) and not TownThreatened())
                call Sleep( 3 )
            endloop

            //Launch Attack
            call InitAssaultGroup(  )
            call PrepareAttackGroup( )
           
            //Immediately begin to construct the next attack wave
            set CurrentWave = CurrentWave + 1
            call SetWave(CurrentWave)
           
            //Form the attack group
            call FormGroup( 3, true )

            //Attack Units on the Queue; the return value is not used here
            set AttackSuccess = SuicideOnQueue()

            call Sleep(3)
        endloop
    endfunction

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

    //===========================================================================
    function main takes nothing returns nothing
        call InitAI(  )
        call InitOptions(  )
        call SelectHeroes(  )
        call CreateCaptains(  )
        call SetHeroLevels( function ChooseHeroSkill )

        // Define Target Units (Gate & Vault)
        call SetTargetUnits()
       
        // Define the first attack wave
        call SetWave(CurrentWave)

        call Sleep( 0.1 )
        call StartThread( function WorkerAssignment )
        call StartThread( function AttackAssignment )
        call PlayGame(  )
    endfunction
    Cheers!