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

[JASS] JASS AI only works in a standalone map

Status
Not open for further replies.
Level 2
Joined
Jan 20, 2015
Messages
9
hey everyone, im new to AI script creating and i followed several tutorial which seems to have worked for me. the very simple AI i created works fine in a standalone map, however when i try to use it in a campaign map it does not work. cagn anyone identify the problem?(the syntax checker says everything is fine)

here is the whole AI:

JASS:
//============================================================================
// First Mission AI
//============================================================================
globals
player Kilix = Player(1)
endglobals

function ConfigureAI takes nothing returns nothing
call SetSlowChopping( true )
call SetPeonsRepair( true )
endfunction

function main takes nothing returns nothing
call CampaignAI(ZIGGURAT_1, null )
call SetReplacements( 1, 1, 3)

call SetBuildUnit(15, ACOLYTE )
call CampaignDefenderEx( 2, 2, 3, GHOUL )
call CampaignDefenderEx( 1, 1, 1, CRYPT_FIEND )
call CampaignDefenderEx( 2, 2, 3, NECRO )

call InitAssaultGroup()
call CampaignAttackerEx( 4, 4, 4, GHOUL )
call CampaignAttackerEx( 2, 2, 2, CRYPT_FIEND )
call SuicideOnPlayerEx( M1, M1, M1, Kilix )

call InitAssaultGroup()
call CampaignAttackerEx( 3, 4, 4, GHOUL )
call CampaignAttackerEx( 1, 2, 3, CRYPT_FIEND )
call CampaignAttackerEx( 1, 1, 1, NECRO )
call SuicideOnPlayerEx( M4, M4, M4, Kilix )

loop
call InitAssaultGroup()
call CampaignAttackerEx( 4, 4, 4, GHOUL )
call CampaignAttackerEx( 2, 2, 2, CRYPT_FIEND )
call CampaignAttackerEx( 2, 2, 2, NECRO )
call CampaignAttackerEx( 1, 1, 1, LICH )
call SuicideOnPlayerEx( M3, M3, M3, Kilix )
endloop
endfunction [code=jass]

Forgive me if i broke any rules, im quit new to this site and thank you for your time.
 
Last edited:
Level 12
Joined
Jun 15, 2016
Messages
472
i think that what you meant, but with the "vJASS" Code but i did not understand how exactly.

Using the [code=jass] tags will work with vJASS, text macros, and a lot more. You can find more about that on this page.

I don't see any trouble with the script itself. I assume the script works as a standalone map, and when that same map is a part of a campaign (without any other changes made to it), the script doesn't work. Is that correct?

I suggest checking the import path of the AI script, as it might change when adding a map to a campaign (I'm not that proficient with that so consider checking with some tutorials).

Also, you can try adding this line as the first line in the main function:

JASS:
call DisplayTextToPlayer(USER, 0., 0., "Running AI script")

Replace USER with whatever player number you have on that map. This should display the message "Running AI script" on the screen when you start the map (also in the log opened by F12). That way you'll know if the AI script ever started running.
 
Level 2
Joined
Jan 20, 2015
Messages
9
thanks the tag thing worked :)

however, it seems the script is not runing, also when i start the map it only shows my color on the download screen as if i dont have an enemy although i set up an enemy in the editor.
 
Level 12
Joined
Jun 15, 2016
Messages
472
Meaning, when your map is loading it displays all players which will be part of the map, and it does not show the enemy player while loading?

In that case, go to the world editor, and click on "scenario" in the upper menu and click on "player properties": First, under the "players" tab, make sure to set the controller of your enemy player to "computer" (and maybe set a fixed start location so you'll know exactly where it starts). Then, in the "forces" tab, put your player and the enemy player in different forces and check "fixed player settings". That should make your enemy player controlled by the computer.
 
Level 2
Joined
Jan 20, 2015
Messages
9
so, your saying i should create this
JASS:
globals
player Kilix = Player(1)
endglobals

in the editor rather then in jass?(i hope i understood correctly)
 
Level 12
Joined
Jun 15, 2016
Messages
472
The issue is probably the globals block at the top now that I think about it.
Reason is that campaigns cannot compile vjass.

Try using a GUI variable instead, see if that works.

That is not the reason. The AI is handled as some sort of external script: it is not checked by jass checker when saving the map, and crashes on a different loading location when it does crash. Each script has it's own global block and that's completely fine.
 
Level 2
Joined
Jan 20, 2015
Messages
9
well the thing Nowow works fine now i have an enemy in the map, but its like he does not recognize the fact that there's an ai even though this trigger should be runing


  • [/B]
  • Start the AI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • AI - Start campaign AI script for Player 4 (Purple): First Mission AI.ai
 
Level 12
Joined
Jun 15, 2016
Messages
472
So you're saying you compiled the script in the main post and then imported it?
Otherwise I am missing something here

Yes, that is how AI scripts work: they are imported to the map and are therefore handled a bit differently the other scripts manipulated using the trigger editor. Even more evidence, you know how if there is an error in your script when you save, the world editor displays all of the map's code (I think that's all of the map's code, it does include the main function and all), well it doesn't display the AI.

well the thing Nowow works fine now i have an enemy in the map, but its like he does not recognize the fact that there's an ai even though this trigger should be runing

  • Start the AI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • AI - Start campaign AI script for Player 4 (Purple): First Mission AI.ai

Alright... Try downloading this tool, which allows you to see all the files bundled inside the campaign file (with file extension w3n). You should find the AI script somewhere in there and see what is the path leading to it.
 
Level 2
Joined
Jan 20, 2015
Messages
9
i think i found out when the problems happens: the AI works when i replace the Ai file with the same one of the same name(reimport it) and then it works, however if i change ANYTHING in the map after that, saves and try the map again, the AI does not load.
 
Level 12
Joined
Jun 15, 2016
Messages
472
i think i found out when the problems happens: the AI works when i replace the Ai file with the same one of the same name(reimport it) and then it works, however if i change ANYTHING in the map after that, saves and try the map again, the AI does not load.

That weird... Well if it works for you then I guess that is good, although quite a strange bug.
If you don't mind doing one more thing, this might be very helpful: Try creating 2 campaign files, one in which the AI does not work (i.e. without re-importing the script at the end) and one file with your fix (i.e. with re-importing the script at the end). After that, go over both campaign files with MPQ master (download link) and looking for your AI script in the MPQ, or in the map file inside the campaign file.
If you don't feel like doing that, you can send me the files and I'll check it. If you don't want me snooping around you're files, that's completely understandable and you don't have to. It's just a strange fix, which might indicate a strange bug, and that's something I'd like to check for sure.
 
Level 39
Joined
Feb 27, 2007
Messages
5,023
As Nowow shows in his AI Tutorial, globals blocks are completely valid in AI scripts. They always have been:
We'll start the our AI script the same as the example we were reading: a global declaration and and the initialization part. The global declaration simple enough
...
JASS:
globals
    player user = Player(0)
    integer Tier = 1
endglobals

function main takes nothing returns nothing
    call CampaignAI('hhou',null)
    call DoCampaignFarms(false)
endfunction
 
Level 39
Joined
Feb 27, 2007
Messages
5,023
AI scripts allow globals. It doesn't allow any other vJASS syntax because you're right it's not processed by JASSHelper. The game just allows you to declare globals in AI scripts, probably because globals are pretty important to keeping an AI functioning properly and you want to be able to import your script to the map without worrying about making the right variables in the variable editor.
 
Level 12
Joined
Jun 15, 2016
Messages
472
That makes no sense. Because vjass cannot parse the map itself how is it supposed to parse the external script??

Unless you use JNGP that had a feature that made it work.

AI scripts work differently from normal "in-map" JASS, and can use a global block. For reference, here are some of Blizzard's scripts, where you can see this for a fact:

JASS:
//==================================================================================================
//  $Id: elf.ai,v 1.18 2003/04/23 19:26:00 bfitch Exp $
//==================================================================================================
globals
    boolean basic_opening       = true
    boolean archer_opening      = true

    integer wave                = 0

    boolean b_acid_breath       = false
    boolean b_hero1_done        = false
    boolean b_hero2_done        = false

    integer c_altar_done        = 0
    integer c_archer            = 0
    integer c_archer_done       = 0
    integer c_ballista          = 0
    integer c_ballista_done     = 0
    integer c_bear              = 0
    integer c_bear_done         = 0
    integer c_chimaera          = 0
    integer c_chimaera_done     = 0
    integer c_dragon_done       = 0
    integer c_dryad             = 0
    integer c_dryad_done        = 0
    integer c_food_made         = 0
    integer c_food_used         = 0
    integer c_gold              = 0
    integer c_gold_owned        = 0
    integer c_hero1_done        = 0
    integer c_hero2_done        = 0
    integer c_hero3_done        = 0
    integer c_hunt_hall_done    = 0
    integer c_huntress          = 0
    integer c_huntress_done     = 0
    integer c_lore_done         = 0
    integer c_mines             = 0
    integer c_mines_done        = 0
    integer c_moon_well         = 0
    integer c_moon_well_done    = 0
    integer c_mtn_giant         = 0
    integer c_mtn_giant_done    = 0
    integer c_roost_done        = 0
    integer c_talon             = 0
    integer c_talon_done        = 0
    integer c_tree_ages_done    = 0
    integer c_tree_etern_done   = 0
    integer c_tree_life         = 0
    integer c_tree_life_done    = 0
    integer c_war_done          = 0
    integer c_wind_done         = 0
    integer c_wisp_done         = 0
    integer c_wonders_done      = 0
    integer c_zeps              = 0
endglobals

//--------------------------------------------------------------------------------------------------
//  set_skills
//--------------------------------------------------------------------------------------------------
function set_skills takes nothing returns nothing

    set skill[ 1] = SEARING_ARROWS
    set skill[ 2] = TRUESHOT
    set skill[ 3] = SEARING_ARROWS
    set skill[ 4] = TRUESHOT
    set skill[ 5] = SEARING_ARROWS
    set skill[ 6] = STARFALL
    set skill[ 7] = TRUESHOT
    set skill[ 8] = SCOUT
    set skill[ 9] = SCOUT
    set skill[10] = SCOUT

    call SetSkillArray(1,MOON_CHICK)
    call SetSkillArray(2,MOON_BABE)
    call SetSkillArray(3,MOON_HONEY)

    set skill[ 1] = FORCE_NATURE
    set skill[ 2] = ENT_ROOTS
    set skill[ 3] = FORCE_NATURE
    set skill[ 4] = ENT_ROOTS
    set skill[ 5] = FORCE_NATURE
    set skill[ 6] = TRANQUILITY
    set skill[ 7] = ENT_ROOTS
    set skill[ 8] = THORNS_AURA
    set skill[ 9] = THORNS_AURA
    set skill[10] = THORNS_AURA

    call SetSkillArray(1,KEEPER)

    set skill[ 1] = ENT_ROOTS
    set skill[ 2] = THORNS_AURA
    set skill[ 3] = ENT_ROOTS
    set skill[ 4] = THORNS_AURA
    set skill[ 5] = ENT_ROOTS
    set skill[ 6] = TRANQUILITY
    set skill[ 7] = THORNS_AURA
    set skill[ 8] = FORCE_NATURE
    set skill[ 9] = FORCE_NATURE
    set skill[10] = FORCE_NATURE

    call SetSkillArray(2,KEEPER)
    call SetSkillArray(3,KEEPER)

    set skill[ 1] = IMMOLATION
    set skill[ 2] = MANA_BURN
    set skill[ 3] = EVASION
    set skill[ 4] = MANA_BURN
    set skill[ 5] = EVASION
    set skill[ 6] = METAMORPHOSIS
    set skill[ 7] = MANA_BURN
    set skill[ 8] = EVASION
    set skill[ 9] = IMMOLATION
    set skill[10] = IMMOLATION

    call SetSkillArray(1,DEMON_HUNTER)

    set skill[ 1] = MANA_BURN
    set skill[ 2] = EVASION
    set skill[ 3] = MANA_BURN
    set skill[ 4] = EVASION
    set skill[ 5] = MANA_BURN
    set skill[ 6] = METAMORPHOSIS
    set skill[ 7] = EVASION
    set skill[ 8] = IMMOLATION
    set skill[ 9] = IMMOLATION
    set skill[10] = IMMOLATION

    call SetSkillArray(2,DEMON_HUNTER)
    call SetSkillArray(3,DEMON_HUNTER)

    set skill[ 1] = FAN_KNIVES
    set skill[ 2] = SHADOW_TOUCH
    set skill[ 3] = FAN_KNIVES
    set skill[ 4] = BLINK
    set skill[ 5] = FAN_KNIVES
    set skill[ 6] = VENGEANCE
    set skill[ 7] = SHADOW_TOUCH
    set skill[ 8] = BLINK
    set skill[ 9] = SHADOW_TOUCH
    set skill[10] = BLINK

    call SetSkillArray(1,WARDEN)
    call SetSkillArray(2,WARDEN)
    call SetSkillArray(3,WARDEN)
endfunction

//--------------------------------------------------------------------------------------------------
//  setup_force
//--------------------------------------------------------------------------------------------------
function setup_force takes nothing returns nothing
    call AwaitMeleeHeroes()
    call InitMeleeGroup()

    call SetMeleeGroup( hero_id         )
    call SetMeleeGroup( hero_id2        )
    call SetMeleeGroup( hero_id3        )
    call SetMeleeGroup( ARCHER          )
    call SetMeleeGroup( HUNTRESS        )
    call SetMeleeGroup( DRUID_TALON     )
    call SetMeleeGroup( DRUID_CLAW      )
    call SetMeleeGroup( DRYAD           )
    call SetMeleeGroup( CHIMAERA        )
    call SetMeleeGroup( MOUNTAIN_GIANT  )
    call SetMeleeGroup( FAERIE_DRAGON   )

    if GetUnitCountDone(HIPPO) > 0 then
        call SetMeleeGroup( HIPPO )
    endif

    if GetUnitCountDone(HIPPO_RIDER) > 0 then
        call SetMeleeGroup( HIPPO_RIDER )
    endif

    call SetInitialWave(10)
endfunction

//--------------------------------------------------------------------------------------------------
//  force_level
//--------------------------------------------------------------------------------------------------
function force_level takes nothing returns integer
    local integer level = 4
    set level = level +      c_dragon_done + c_talon_done
    set level = level + 2 *  c_archer_done / 3
    set level = level + 2 *  c_dryad_done
    set level = level + 3 *  c_huntress_done
    set level = level + 4 * (c_chimaera_done + c_bear_done)
    set level = level + 5 *  c_hero3_done
    set level = level + 6 * (c_hero2_done + c_mtn_giant_done)
    return level
endfunction

//--------------------------------------------------------------------------------------------------
//  attack_sequence
//--------------------------------------------------------------------------------------------------
function attack_sequence takes nothing returns nothing
    local boolean needs_exp
    local boolean has_siege
    local boolean air_units
    local integer level

    loop
        exitwhen c_hero1_done > 0 and c_archer_done >= 2
        call Sleep(2)
    endloop

    if MeleeDifficulty() == MELEE_NEWBIE then
        call Sleep(240)
    endif

    call StaggerSleep(0,2)
    loop
        loop
            exitwhen not CaptainRetreating()
            call Sleep(2)
        endloop

        set wave = wave + 1
        if wave == 2 then
            loop
                exitwhen c_archer_done >= 4
                call Sleep(2)
            endloop
        endif

        call setup_force()

        set level = force_level()
        set max_creeps = level * 4 / 5
        set min_creeps = max_creeps - 10
        if min_creeps < 0 then
            set min_creeps = 0
        endif

        set needs_exp        = take_exp and (level >= 9 or c_gold_owned < 2000)
        set has_siege        = level >= 40 or c_ballista_done > 0 or c_chimaera_done > 0 or c_mtn_giant_done > 0
        set air_units        = c_chimaera_done > 0 or c_dragon_done > 0
        set allow_air_creeps = air_units or c_archer_done > 3

        call SingleMeleeAttack(needs_exp,has_siege,false,air_units)

        if MeleeDifficulty() == MELEE_NEWBIE then
            call Sleep(60)
        endif
    endloop
endfunction

//--------------------------------------------------------------------------------------------------
//  init_vars
//--------------------------------------------------------------------------------------------------
function init_vars takes nothing returns nothing

    set b_acid_breath       = GetUpgradeLevel(UPG_CHIM_ACID) >= 1
    set b_hero1_done        = GetUnitCountDone(hero_id) > 0
    set b_hero2_done        = GetUnitCountDone(hero_id2) > 0

    set c_altar_done        = GetUnitCountDone(ELF_ALTAR)
    set c_archer            = GetUnitCount(ARCHER)
    set c_archer_done       = GetUnitCountDone(ARCHER)
    set c_ballista          = GetUnitCount(BALLISTA)
    set c_ballista_done     = GetUnitCountDone(BALLISTA)
    set c_bear              = TownCount(DRUID_CLAW)
    set c_bear_done         = TownCountDone(DRUID_CLAW)
    set c_chimaera          = GetUnitCount(CHIMAERA)
    set c_chimaera_done     = GetUnitCountDone(CHIMAERA)
    set c_dragon_done       = GetUnitCountDone(FAERIE_DRAGON)
    set c_dryad             = GetUnitCount(DRYAD)
    set c_dryad_done        = GetUnitCountDone(DRYAD)
    set c_food_made         = c_tree_life * GetFoodMade(TREE_LIFE) + c_moon_well * GetFoodMade(MOON_WELL)
    set c_food_used         = FoodUsed()
    set c_gold              = GetGold()
    set c_gold_owned        = GetGoldOwned()
    set c_hero1_done        = GetUnitCountDone(hero_id)
    set c_hero2_done        = GetUnitCountDone(hero_id2)
    set c_hero3_done        = GetUnitCountDone(hero_id3)
    set c_hunt_hall_done    = GetUnitCountDone(HUNTERS_HALL)
    set c_huntress          = GetUnitCount(HUNTRESS)
    set c_huntress_done     = GetUnitCountDone(HUNTRESS)
    set c_lore_done         = GetUnitCountDone(ANCIENT_LORE)
    set c_mines             = GetMinesOwned()
    set c_mines_done        = GetUnitCountDone(ELF_MINE)  
    set c_moon_well         = GetUnitCount(MOON_WELL)
    set c_moon_well_done    = GetUnitCountDone(MOON_WELL)
    set c_mtn_giant         = GetUnitCount(MOUNTAIN_GIANT)
    set c_mtn_giant_done    = GetUnitCountDone(MOUNTAIN_GIANT)
    set c_roost_done        = GetUnitCountDone(CHIMAERA_ROOST)
    set c_talon             = TownCount(DRUID_TALON)
    set c_talon_done        = TownCountDone(DRUID_TALON)
    set c_tree_ages_done    = TownCountDone(TREE_AGES)
    set c_tree_etern_done   = TownCountDone(TREE_ETERNITY)
    set c_tree_life         = TownCount(TREE_LIFE)
    set c_tree_life_done    = TownCountDone(TREE_LIFE)
    set c_war_done          = GetUnitCountDone(ANCIENT_WAR)
    set c_wind_done         = GetUnitCountDone(ANCIENT_WIND)
    set c_wisp_done         = GetUnitCountDone(WISP)
    set c_wonders_done      = GetUnitCountDone(DEN_OF_WONDERS)
    set c_zeps              = GetUnitCount(ZEPPELIN)

    if basic_opening then

        if b_hero2_done or (MeleeDifficulty() == MELEE_NEWBIE and c_moon_well_done >= 4) then
            set basic_opening = false
        endif

        if archer_opening and c_archer_done >= 6 then
            set archer_opening = false
        endif
    endif
endfunction

//--------------------------------------------------------------------------------------------------
//  set_vars
//--------------------------------------------------------------------------------------------------
function set_vars takes nothing returns nothing
    loop
        call init_vars()
        call Sleep(1)
    endloop
endfunction

//--------------------------------------------------------------------------------------------------
//  basics
//--------------------------------------------------------------------------------------------------
function basics takes integer food returns nothing
    local integer archers
    local integer hunts

    if archer_opening or c_hunt_hall_done < 1 then

        set archers = food / 2
        if archers > 6 then
            set archers = 6
        endif

        call SetBuildUnit( archers, ARCHER )
        return
    endif

    set hunts = (food - 2 * c_archer) / 3
    if hunts > 3 then
        set hunts = 3
    endif

    call SetBuildUnit( hunts, HUNTRESS )

    if food >= 15 then
        call SetBuildUnit( 3, ARCHER )
    endif
endfunction

//--------------------------------------------------------------------------------------------------
//  do_upgrades
//--------------------------------------------------------------------------------------------------
function do_upgrades takes nothing returns nothing

    if c_tree_etern_done >= 1 and c_hunt_hall_done >= 1 then
        call SetBuildUpgr( 1, UPG_WELL_SPRING )
    endif

    if c_dryad >= 1 and c_lore_done >= 1 then
        call SetBuildUpgr( 1, UPG_ABOLISH     )
    endif

    if c_roost_done >= 1 then
        call SetBuildUpgr( 1, UPG_CHIM_ACID   )
    endif

    if c_hunt_hall_done >= 1 then

        if c_archer + c_huntress + c_ballista >= 3 then

            call SetBuildUpgr( 1, UPG_STR_MOON      )
            call SetBuildUpgr( 1, UPG_MOON_ARMOR    )

            if c_tree_ages_done >= 1 then
                call SetBuildUpgr( 2, UPG_STR_MOON      )
                call SetBuildUpgr( 2, UPG_MOON_ARMOR    )

                if c_tree_etern_done >= 1 then
                    call SetBuildUpgr( 3, UPG_STR_MOON      )
                    call SetBuildUpgr( 3, UPG_MOON_ARMOR    )
                endif
            endif
        endif

        if c_dryad + c_mtn_giant + c_chimaera >= 3 then

            call SetBuildUpgr( 1, UPG_STR_WILD      )
            call SetBuildUpgr( 1, UPG_HIDES         )

            if c_tree_ages_done >= 1 then
                call SetBuildUpgr( 2, UPG_STR_WILD      )
                call SetBuildUpgr( 2, UPG_HIDES         )

                if c_tree_etern_done >= 1 then
                    call SetBuildUpgr( 3, UPG_STR_WILD      )
                    call SetBuildUpgr( 3, UPG_HIDES         )
                endif
            endif
        endif
    endif

    if c_mtn_giant >= 1 and c_tree_etern_done >= 1 and c_wonders_done >= 1 and c_lore_done >= 1 then
        call SetBuildUpgr( 1, UPG_HARD_SKIN   )
        call SetBuildUpgr( 1, UPG_RESIST_SKIN )
    endif

    if c_war_done >= 1 then

        if c_huntress >= 3 and c_tree_etern_done >= 1 and c_hunt_hall_done >= 1 then
            call SetBuildUpgr( 1, UPG_GLAIVE      )
            call SetBuildUpgr( 1, UPG_SCOUT       )
        endif

        if c_archer >= 3 then
            if c_tree_ages_done >= 1 then
                call SetBuildUpgr( 1, UPG_BOWS      )

                if c_tree_etern_done >= 1 and c_hunt_hall_done >= 1 then
                    call SetBuildUpgr( 1, UPG_MARKSMAN  )
                endif
            endif
        endif
        if c_ballista >= 1 then
            call SetBuildUpgr( 1, UPG_ULTRAVISION )
            call SetBuildUpgr( 1, UPG_BOLT        )
        endif
    endif

    if c_lore_done >= 1 then

        if c_bear >= 1 then

            call SetBuildUpgr( 1, UPG_DRUID_CLAW  )

            if c_tree_etern_done >= 1 then
                call SetBuildUpgr( 2, UPG_DRUID_CLAW  )
                call SetBuildUpgr( 1, UPG_MARK_CLAW   )
            endif
        endif

        if c_talon >= 1 then

            call SetBuildUpgr( 1, UPG_DRUID_TALON )

            if c_tree_etern_done >= 1 then
                call SetBuildUpgr( 2, UPG_DRUID_TALON )
                call SetBuildUpgr( 1, UPG_MARK_TALON  )
            endif
        endif
    endif
endfunction

//--------------------------------------------------------------------------------------------------
//  build_sequence
//--------------------------------------------------------------------------------------------------
function build_sequence takes nothing returns nothing
    local boolean primary_melee
    local integer wisps

    call InitBuildArray()

    if basic_opening then
        call SetBuildUnit(  1, TREE_LIFE        )
        call SetBuildUnit(  5, WISP             )
        call SetBuildUnit(  1, ELF_ALTAR        )
        call SetBuildUnit(  7, WISP             )
        call SetBuildUnit(  1, MOON_WELL        )
        call SetBuildUnit(  8, WISP             )
        call SetBuildUnit(  1, ANCIENT_WAR      )
        call SetBuildUnit(  9, WISP             )
        call SetBuildUnit(  1, hero_id          )
        call SetBuildUnit( 10, WISP             )
        call SetBuildUnit(  2, MOON_WELL        )
        call basics(2)// (  1, ARCHER           )
        call SetBuildUnit(  1, DEN_OF_WONDERS   )
        call basics(4)// (  2, ARCHER           )
        call SetBuildUnit( 11, WISP             )
        call basics(6)// (  3, ARCHER           )
        call SetBuildUnit( 12, WISP             )
        call SetBuildUnit(  1, HUNTERS_HALL     )
        call SetBuildUnit(  3, MOON_WELL        )
        call SetBuildUnit( 13, WISP             )
        call basics(8)// (  4, ARCHER           )
        call SetBuildUnit( 14, WISP             )
        call basics(10)//(  5, ARCHER           )
        call SetBuildUnit( 15, WISP             )
        call basics(15)//(  6, ARCHER           )
                       //(  1, HUNTRESS         )
        call SetBuildUnit(  1, TREE_AGES        )

        call BasicExpansion( c_mines < 2, TREE_LIFE )

        call SetBuildUpgr(  1, UPG_STR_MOON     )
        call SetBuildUpgr(  1, UPG_MOON_ARMOR   )
        call SetBuildUnit(  4, MOON_WELL        )

        if MeleeDifficulty() != MELEE_NEWBIE then
            call SetBuildUnit(  1, hero_id2     )
        endif

        return
    endif

    if c_tree_life < 1 and c_wisp_done > 0 then
        call MeleeTownHall( 0, TREE_LIFE )
        call MeleeTownHall( 1, TREE_LIFE )
        call MeleeTownHall( 2, TREE_LIFE )
    endif

    if c_tree_life_done > 0 then
        set wisps = 6 - GetWood() / 200
        if wisps < 3 then
            set wisps = 3
        endif
        if c_mines < 2 or c_tree_life_done < 2 then
            set wisps = wisps + 5
        else
            set wisps = wisps + 10
        endif
        if wisps > 15 then
            set wisps = 15
        endif
        call SetBuildNext( wisps, WISP )
    endif

    if c_gold > 500 and GetWood() < 100 then
        call SetBuildNext( 15, WISP )
    endif

    // having enough gold is the highest priority
    //
    if c_gold_owned < 2000 then
        call BasicExpansion( c_mines < 2, TREE_LIFE )
        if MeleeDifficulty() != MELEE_NEWBIE then
            call GuardSecondary( 1, 1, ANCIENT_PROTECT )
            call GuardSecondary( 1, 2, ANCIENT_PROTECT )
        endif
    endif

    // get enough moon wells to cover food need
    //
    if c_food_used + 7 > c_food_made then
        call SetBuildUnit( c_moon_well_done + 1, MOON_WELL )
    endif

    // recover heroes for basic defense
    //
    if c_altar_done >= 1 then

        if b_hero1_done and MeleeDifficulty() != MELEE_NEWBIE then
            call SetBuildUnit( 1, hero_id2 )
        else
            call SetBuildUnit( 1, hero_id  )
        endif
    else
        call SetBuildUnit( 1, ELF_ALTAR )
    endif

    // the primary melee force is the mountain giant
    //
    set primary_melee = c_lore_done >= 1 and c_wonders_done >= 1 and c_tree_ages_done >= 1
    if primary_melee then
        call SetBuildNext( 1, MOUNTAIN_GIANT )

    // the backup melee force is the huntress
    //
    else
        call SetBuildUnit( 1, ANCIENT_WAR   )
        call SetBuildNext( 3, HUNTRESS      )
    endif

    // the primary ranged force is the dryad
    //
    if c_lore_done >= 1 then
        call SetBuildUnit( 2, DRYAD )

    // the backup ranged force is the archer
    //
    else
        call SetBuildUnit( 1, ANCIENT_WAR   )
        call SetBuildUnit( 3, ARCHER        )
    endif

    // need siege to take out enemy towns and expansions
    //
    if b_acid_breath and c_roost_done >= 1 then
        call SetBuildUnit( 2, CHIMAERA )
    elseif c_mtn_giant < 1 then
        call SetBuildUnit( 2, BALLISTA )
    endif

    // if we have enough gold then advance on the tech tree
    //
    if c_gold > 1000 then

        if MeleeDifficulty() != MELEE_NEWBIE then
            call GuardSecondary( 1, 1, ANCIENT_PROTECT )
            call GuardSecondary( 1, 2, ANCIENT_PROTECT )
        endif

        call SetBuildUnit(  1, ANCIENT_WAR      )
        call SetBuildUnit(  1, HUNTERS_HALL     )
        call SetBuildUnit(  1, TREE_AGES        )
        call SetBuildUnit(  1, DEN_OF_WONDERS   )
        call SetBuildUnit(  1, ANCIENT_LORE     )
        call SetBuildUnit(  1, TREE_ETERNITY    )
        call SetBuildUnit(  1, ANCIENT_WIND     )
        call SetBuildUnit(  1, CHIMAERA_ROOST   )

        call do_upgrades()

        if c_gold > 2000 then
            call BuildFactory( ANCIENT_LORE     )
            call BuildFactory( ANCIENT_WAR      )
            call BuildFactory( CHIMAERA_ROOST   )
            call BuildFactory( ANCIENT_WIND     )
        endif

    elseif c_food_used >= UPKEEP_TIER1 then
        call do_upgrades()
    endif

    call BasicExpansion( c_mines < 2, TREE_LIFE )
    if MeleeDifficulty() != MELEE_NEWBIE then
        call GuardSecondary( 1, 1, ANCIENT_PROTECT )
        call GuardSecondary( 1, 2, ANCIENT_PROTECT )
    endif

    if c_food_used >= UPKEEP_TIER2 - 10 and c_gold < 2000 then
        return
    endif

    // build units from whatever buildings we already have
    //
    if primary_melee then
        call SetBuildNext( 3, MOUNTAIN_GIANT )
    else
        call SetBuildNext( 7, HUNTRESS )
    endif

    if c_lore_done >= 1 then
        call SetBuildNext( 4, DRYAD )
    else
        call SetBuildNext( 6, ARCHER )
    endif

    if c_tree_ages_done >= 1 then
        if c_tree_etern_done >= 1 and c_altar_done >= 1 and MeleeDifficulty() != MELEE_NEWBIE then
            call SetBuildUnit( 1, hero_id3 )
        endif

        if c_lore_done >= 1 then
            call SetBuildUnit( 1, DRUID_CLAW )
        endif
    endif

    if c_wind_done >= 1 then
        if c_wonders_done >= 1 then
            call SetBuildUnit( 1, FAERIE_DRAGON )
        endif

        call SetBuildUnit( 1, DRUID_TALON )
    endif

    if c_gold_owned < 10000 then
        call BasicExpansion( c_mines < 3, TREE_LIFE )
        if MeleeDifficulty() != MELEE_NEWBIE then
            call GuardSecondary( 2, 1, ANCIENT_PROTECT )
            call GuardSecondary( 2, 2, ANCIENT_PROTECT )
        endif
    endif

    if c_food_used >= 60 and c_zeps < 3 then
        call GetZeppelin()
    endif
endfunction

//--------------------------------------------------------------------------------------------------
//  peon_assignment
//--------------------------------------------------------------------------------------------------
function peon_assignment takes nothing returns nothing
    local integer T
    loop
        call ClearHarvestAI()

        set T = TownWithMine()

        call HarvestGold(T,4)
        call HarvestWood(0,1)
        call HarvestGold(T,1)
        call HarvestWood(0,2)

        if c_mines_done > 1 then
            call HarvestGold(T+1,5)
        endif

        call HarvestWood(0,20)

        call build_sequence()
        call Sleep(GetRandomInt(1,3))
    endloop
endfunction

//--------------------------------------------------------------------------------------------------
//   main
//--------------------------------------------------------------------------------------------------
function main takes nothing returns nothing
    call PickMeleeHero(RACE_NIGHTELF)
    call set_skills()
    call StandardAI(function SkillArrays, function peon_assignment, function attack_sequence)
    call StartThread(function set_vars)
    call PlayGame()
endfunction

JASS:
//============================================================================
//  Night Elf 07 -- any player -- AI Script
//============================================================================
globals
    constant integer NORMAL_WAVE_DUR    = 75 // seconds
    constant integer HARD_WAVE_DUR      = 75

    constant integer NORM_ALLOW_TREES   = 0
    constant integer NORM_TREE_FACTOR   = 5
    constant integer NORM_BASE_BONUS    = 0
    constant integer NORM_BONUS_CAP     = 10

    constant integer HARD_ALLOW_TREES   = 0
    constant integer HARD_TREE_FACTOR   = 4
    constant integer HARD_BASE_BONUS    = 1
    constant integer HARD_BONUS_CAP     = 10
   
    constant integer PIT_STOP_WAVE      = 0
    constant integer BASIC_WAVE1        = 1
    constant integer BASIC_WAVE2        = 2
    constant integer AIR_WAVE           = 3
    constant integer ANTI_AIR_WAVE      = 4
    constant integer ANTIMAGIC_WAVE     = 5
    constant integer SIEGE_WAVE         = 6
    constant integer POSSESS_WAVE       = 7
    constant integer SKELETON_WAVE      = 8
    constant integer PLAGUE_WAVE        = 9
    constant integer BURNING_WAVE       = 10
    constant integer F_U1_WAVE          = 11
    constant integer F_U2_WAVE          = 12
    constant integer F_U3_WAVE          = 13

    constant integer CMD_SET_X1         = 1
    constant integer CMD_SET_Y1         = 2
    constant integer CMD_SET_SEQUENCE   = 3
    constant integer CMD_SET_TARGET     = 4
    constant integer CMD_STOP           = 5
    constant integer CMD_SET_X2         = 6
    constant integer CMD_SET_Y2         = 7

    constant integer BANSHEE_POSSESS    = 'uC00'
    constant integer BANSHEE_CURSE      = 'uC01'
    constant integer BANSHEE_ANTIMAGIC  = 'uC02'
  //constant integer NECRO_CRIPPLE      = 'uC03'
  //constant integer NECRO_RAISE        = 'uC04'
  //constant integer NECRO_UNHOLY       = 'uC05'
  //constant integer ?                  = 'uC06'
  //constant integer ?                  = 'uC07'
    constant integer BUILD_INFERNAL     = 'nC08'
  //constant integer ?                  = 'uC09'
  //constant integer ?                  = 'uC10'
  //constant integer ?                  = 'uC11'
    constant integer GHOULZ             = 'uC12'

    player USER                         = Player(1)

    integer stage_x1                    = 0
    integer stage_y1                    = 0
    integer stage_x2                    = 0
    integer stage_y2                    = 0

    boolean need_stage2                 = true
    boolean at_stage1                   = false
    boolean aborting                    = false
    boolean send_lich                   = false
    boolean send_lord                   = false
    boolean send_azga                   = false

    integer wave_count                  = 0
    integer wave_restart                = 0
    integer strength                    = 0
    integer target                      = 0
    integer harvest_ghouls              = 0

    integer array wave_type
    integer array wave_sleep
    integer array wave_hero
endglobals

//============================================================================
//  add_wave
//============================================================================
function add_wave takes integer whero, integer wtype returns nothing
    set wave_hero  [wave_count] = whero
    set wave_type  [wave_count] = wtype

    if difficulty == HARD then
        set wave_sleep [wave_count] = HARD_WAVE_DUR
    else
        set wave_sleep [wave_count] = NORMAL_WAVE_DUR
    endif

    set wave_count = wave_count + 1
endfunction

//============================================================================
//  pit_stop
//============================================================================
function pit_stop takes integer ensecs, integer hsecs returns nothing
    set wave_hero  [wave_count] = 0
    set wave_type  [wave_count] = PIT_STOP_WAVE

    if difficulty==HARD then
        set wave_sleep [wave_count] = hsecs
    else
        set wave_sleep [wave_count] = ensecs
    endif

    set wave_count = wave_count + 1
endfunction

//============================================================================
//  loop_waves
//============================================================================
function loop_waves takes nothing returns nothing
    set wave_restart = wave_count
endfunction

//============================================================================
//  setup_waves
//============================================================================
function setup_waves takes integer sequence returns nothing
    call TraceI("***** setup_waves (group %d) *****\n",sequence)

    set wave_count = 0

    //------------------------------------------------------------------------
    if sequence == 1 then // Jaina
    //------------------------------------------------------------------------
        call add_wave( 0,          BASIC_WAVE1      )
        call add_wave( 0,          BASIC_WAVE2      )

        call add_wave( 0,          ANTIMAGIC_WAVE   ) // Jaina theme
        call add_wave( LICH,       POSSESS_WAVE     )

       call pit_stop( 20, 20 )

        call add_wave( 0,          ANTI_AIR_WAVE    )
        call add_wave( 0,          AIR_WAVE         )

        call pit_stop( 30, 30 )

        call add_wave( DREAD_LORD, ANTIMAGIC_WAVE   ) // Jaina theme
        call add_wave( LICH,       POSSESS_WAVE     )

        call pit_stop( 30, 30 )

        call loop_waves()

        call add_wave( PIT_LORD,   F_U1_WAVE        )

       call pit_stop( 40, 30 )

        call add_wave( DREAD_LORD, F_U2_WAVE        )

       call pit_stop( 40, 30 )

        call add_wave( PIT_LORD,   F_U3_WAVE        )

    //------------------------------------------------------------------------
    elseif sequence == 2 then // Thrall
    //------------------------------------------------------------------------
        call add_wave( 0,          BASIC_WAVE1      )
        call add_wave( 0,          BASIC_WAVE2      )

        call add_wave( DREAD_LORD, SIEGE_WAVE       ) // Thrall theme
        call add_wave( LICH,       SKELETON_WAVE    )

        call pit_stop( 30, 30 )

        call add_wave( 0,          ANTI_AIR_WAVE    )
        call add_wave( 0,          AIR_WAVE         )

        call pit_stop( 30, 30 )

        call add_wave( LICH,       SKELETON_WAVE    ) // Thrall theme
        call add_wave( DREAD_LORD, SIEGE_WAVE       )

        call pit_stop( 60, 30 )

        call loop_waves()

        call add_wave( PIT_LORD,   F_U1_WAVE        )

       call pit_stop( 40, 30 )

        call add_wave( DREAD_LORD, F_U2_WAVE        )

       call pit_stop( 40, 30 )

        call add_wave( PIT_LORD,   F_U3_WAVE        )

    //------------------------------------------------------------------------
    else // sequence == 3 then // USER
    //------------------------------------------------------------------------
        call add_wave( 0,          BASIC_WAVE1      )
        call add_wave( 0,          BASIC_WAVE2      )

        call pit_stop( 60, 40 )

        call add_wave( 0,          AIR_WAVE         )
        call add_wave( LICH,       SKELETON_WAVE    )

       call pit_stop( 60, 40 )

        call add_wave( 0,          PLAGUE_WAVE      ) // User theme
        call add_wave( DREAD_LORD, BURNING_WAVE     )

        call pit_stop( 60, 40 )

        call add_wave( 0,          ANTIMAGIC_WAVE   ) // recap Jaina theme
        call add_wave( 0,          POSSESS_WAVE     )

        call pit_stop( 60, 40 )

        call add_wave( DREAD_LORD, SIEGE_WAVE       ) // recap Thrall theme
        call add_wave( LICH,       SKELETON_WAVE    )

        call pit_stop( 60, 40 )

        call loop_waves()

        call add_wave( PIT_LORD,   F_U1_WAVE        )
        call add_wave( DREAD_LORD, F_U2_WAVE        )
        call add_wave( PIT_LORD,   F_U3_WAVE        )
    endif
endfunction

//============================================================================
//  wave_units
//============================================================================
function wave_units takes integer norm, integer hard, integer unitid returns nothing
    local integer adding
    local integer index
   
    if difficulty==HARD then
        set adding = hard
    else
        set adding = norm
    endif
    if adding < 1 then
        return
    endif

    // if unitid is already being built, add more to existing desires
    //
    set index = 0
    loop
        exitwhen index == harass_length

        if harass_units[index] == unitid then
            set harass_qty[index] = harass_qty[index] + adding
            set harass_max[index] = harass_max[index] + adding
            return
        endif
        set index = index + 1
    endloop

    // if unitid is not in harass list then add this new unitid
    //
    set harass_qty   [harass_length] = adding
    set harass_max   [harass_length] = adding
    set harass_units [harass_length] = unitid

    set harass_length = harass_length + 1
endfunction

//============================================================================
//  do_BONUS_LOVE
//============================================================================
function do_BONUS_LOVE takes nothing returns nothing
    call wave_units( 1, 1, MEAT_WAGON       )
endfunction

//============================================================================
//  do_EVERY_WAVE_FIRST
//============================================================================
function do_EVERY_WAVE_FIRST takes nothing returns nothing
endfunction

//============================================================================
//  do_EVERY_WAVE_LAST
//============================================================================
function do_EVERY_WAVE_LAST takes integer heroid returns nothing
    if heroid != 0 then
        call wave_units( 1, 1, heroid       ) // undead altar
        call wave_units( 1, 1, BANSHEE      ) // temple of the damned
    endif
endfunction

//============================================================================
//  do_BASIC_WAVE1
//============================================================================
function do_BASIC_WAVE1 takes nothing returns nothing
    call wave_units( 6,10, GHOULZ           ) // crypt
    call wave_units( 4, 6, CRYPT_FIEND      ) // crypt
endfunction

//============================================================================
//  do_BASIC_WAVE2
//============================================================================
function do_BASIC_WAVE2 takes nothing returns nothing
    call wave_units( 0, 1, MEAT_WAGON       )
    call wave_units( 8,12, GHOULZ           ) // crypt
    call wave_units( 5, 7, CRYPT_FIEND      ) // crypt
endfunction

//============================================================================
//  do_POSSESS_WAVE
//============================================================================
function do_POSSESS_WAVE takes nothing returns nothing
    call wave_units( 2, 3, ABOMINATION      ) // slaughterhouse
    call wave_units( 8,10, BANSHEE_POSSESS  ) // temple of the damned
endfunction

//============================================================================
//  do_ANTIMAGIC_WAVE
//============================================================================
function do_ANTIMAGIC_WAVE takes nothing returns nothing
    call wave_units( 0, 1, MEAT_WAGON       ) // slaughterhouse
    call wave_units( 2, 3, DOOMGUARD        ) // crypt
    call wave_units( 6, 8, FELLHOUND        ) // crypt
endfunction

//============================================================================
//  do_AIR_WAVE
//============================================================================
function do_AIR_WAVE takes nothing returns nothing
    call wave_units( 6, 8, GARGOYLE         ) // crypt
    call wave_units( 4, 5, FROST_WYRM       ) // boneyard
endfunction

//============================================================================
//  do_ANTI_AIR_WAVE
//============================================================================
function do_ANTI_AIR_WAVE takes nothing returns nothing
    call wave_units( 1, 2, DOOMGUARD        ) // crypt
    call wave_units( 5, 7, CRYPT_FIEND      ) // crypt
    call wave_units( 7, 9, GARGOYLE         ) // crypt
endfunction

//============================================================================
//  do_SIEGE_WAVE
//============================================================================
function do_SIEGE_WAVE takes nothing returns nothing
    call wave_units( 5, 7, MEAT_WAGON       ) // slaughterhouse
    call wave_units( 5, 7, ABOMINATION      ) // slaughterhouse
    call wave_units( 2, 3, FROST_WYRM       ) // boneyard
endfunction

//============================================================================
//  do_BURNING_WAVE
//============================================================================
function do_BURNING_WAVE takes nothing returns nothing
    call wave_units( 1, 2, DOOMGUARD        ) // crypt
    call wave_units( 6, 8, BUILD_INFERNAL   ) // crypt
endfunction

//============================================================================
//  do_PLAGUE_WAVE
//============================================================================
function do_PLAGUE_WAVE takes nothing returns nothing
    call wave_units( 1, 2, MEAT_WAGON       ) // slaughterhouse
    call wave_units( 4, 6, GHOULZ           ) // crypt
    call wave_units( 5, 7, ABOMINATION      ) // slaughterhouse
    call wave_units( 3, 4, NECRO            ) // temple of the damned
endfunction

//============================================================================
//  do_SKELETON_WAVE
//============================================================================
function do_SKELETON_WAVE takes nothing returns nothing
    call wave_units( 0, 1, MEAT_WAGON       ) // slaughterhouse
    call wave_units( 4, 6, ABOMINATION      ) // slaughterhouse
    call wave_units( 8,10, NECRO            ) // temple of the damned
endfunction

//============================================================================
//  do_F_U1_WAVE
//============================================================================
function do_F_U1_WAVE takes nothing returns nothing
    call wave_units( 1, 2, MEAT_WAGON       ) // slaughterhouse
    call wave_units( 2, 3, DOOMGUARD        ) // crypt
    call wave_units( 5, 7, FELLHOUND        ) // crypt
    call wave_units( 2, 3, BANSHEE_CURSE    ) // temple of the damned
    call wave_units( 2, 3, FROST_WYRM       ) // boneyard
    call wave_units( 2, 3, NECRO            ) // temple of the damned
endfunction

//============================================================================
//  do_F_U2_WAVE
//============================================================================
function do_F_U2_WAVE takes nothing returns nothing
    call wave_units( 1, 2, MEAT_WAGON       ) // slaughterhouse
    call wave_units( 2, 3, BUILD_INFERNAL   ) // crypt
    call wave_units( 3, 5, ABOMINATION      ) // slaughterhouse
    call wave_units( 4, 6, CRYPT_FIEND      ) // crypt
    call wave_units( 2, 3, BANSHEE_CURSE    ) // temple of the damned
    call wave_units( 2, 3, NECRO            ) // temple of the damned
endfunction

//============================================================================
//  do_F_U3_WAVE
//============================================================================
function do_F_U3_WAVE takes nothing returns nothing
    call wave_units( 2, 3, MEAT_WAGON       ) // slaughterhouse
    call wave_units( 2, 3, BUILD_INFERNAL   ) // crypt
    call wave_units( 3, 4, BANSHEE_POSSESS  ) // temple of the damned
    call wave_units( 3, 4, DOOMGUARD        ) // crypt
    call wave_units( 4, 5, FELLHOUND        ) // crypt
    call wave_units( 2, 3, FROST_WYRM       ) // boneyard
    call wave_units( 1, 1, LICH             ) // undead altar
    call wave_units( 2, 3, NECRO            ) // temple of the damned
endfunction

//============================================================================
//  bonus_waves
//============================================================================
function bonus_waves takes integer allow, integer factor, integer base, integer cap returns integer
    local integer trees = GetPlayerUnitTypeCount( USER, ANCIENT_PROTECT )
    local integer result

    if trees <= allow or factor <= 0 then
        return base
    endif

    set result = base + (trees - allow) / factor + 1

    if result > cap then
        return cap
    endif

    return result
endfunction

//============================================================================
//  consider_bonus_love
//============================================================================
function consider_bonus_love takes nothing returns nothing
    local integer times

    if difficulty==HARD then
        set times = bonus_waves( HARD_ALLOW_TREES, HARD_TREE_FACTOR, HARD_BASE_BONUS, HARD_BONUS_CAP )
    else
        set times = bonus_waves( NORM_ALLOW_TREES, NORM_TREE_FACTOR, NORM_BASE_BONUS, NORM_BONUS_CAP )
    endif
    call TraceI("Bonus love = %d\n",times)

    loop
        exitwhen times <= 0
        call do_BONUS_LOVE()
        set times = times - 1
    endloop
endfunction

//============================================================================
//  check_abort
//============================================================================
function check_abort takes nothing returns nothing
    if CommandsWaiting() == 0 then
        return
    endif

    if GetLastCommand()==CMD_STOP then
        call Trace("***** ABORT *****\n")
        set aborting = true
        call SleepForever()
    else
        call Trace("***** UNEXPECTED COMMAND *****\n")
        call PopLastCommand()
    endif
endfunction

//============================================================================
//  form_wave
//============================================================================
function form_wave takes nothing returns nothing
    local integer index
    set index = 0
    loop
        exitwhen index == harass_length
        call AddAssault(harass_max[index],harass_units[index])
        set index = index + 1
    endloop
endfunction

//============================================================================
//  suicide_wave
//============================================================================
function suicide_wave takes nothing returns nothing
    local integer index
    local integer count
    local integer desire
    local integer unitid

    local integer length = harass_length
    set harass_length = 0

    set index = 0
    loop
        exitwhen index == length

        call TraceI("wave cycle %d\n",index+1)

        set desire = harass_max[index]
        set unitid = harass_units[index]

        if unitid==LICH then
            set send_lich = true

        elseif unitid==DREAD_LORD then
            set send_lord = true

        elseif unitid==PIT_LORD then
            set send_azga = true
        endif

        set sleep_seconds = sleep_seconds-2 // 20 cycles
        set count = 0
        loop
            call SuicideUnitEx(1,unitid,target)
            call Sleep(0.1)

            set count = count + 1
            exitwhen count==20 // 2 seconds
        endloop

        set index = index + 1
    endloop
    call Trace("all waves sent\n")
endfunction

//============================================================================
// suicide_unit
//============================================================================
function suicide_unit takes integer unitid returns nothing
    call SuicideUnitEx(1,unitid,target)
    call SuicideUnitEx(1,SKEL_WARRIOR,target)
    call Sleep(0.1)
endfunction

//============================================================================
//  possessions
//============================================================================
function possessions takes nothing returns nothing

    set sleep_seconds = sleep_seconds - 1
    call suicide_unit( INFERNAL      )
    call suicide_unit( FOOTMAN       )
    call suicide_unit( RIFLEMAN      )
    call suicide_unit( SORCERESS     )
    call suicide_unit( PRIEST        )
    call suicide_unit( MORTAR        )
    call suicide_unit( ELEMENTAL     )
    call suicide_unit( KNIGHT        )
    call suicide_unit( GRYPHON       )
    call suicide_unit( MILITIA       )

    set sleep_seconds = sleep_seconds - 1
    call suicide_unit( GRUNT         )
    call suicide_unit( HEAD_HUNTER   )
    call suicide_unit( TAUREN        )
    call suicide_unit( WITCH_DOCTOR  )
    call suicide_unit( KODO_BEAST    )
    call suicide_unit( RAIDER        )
    call suicide_unit( SHAMAN        )
    call suicide_unit( WYVERN        )
    call suicide_unit( SHANDRIS      )
    call suicide_unit( ENT           )

    set sleep_seconds = sleep_seconds - 1
    call suicide_unit( ARCHER        )
    call suicide_unit( DRUID_TALON   )
    call suicide_unit( DRUID_TALON_M )
    call suicide_unit( DRUID_CLAW    )
    call suicide_unit( DRUID_CLAW_M  )
    call suicide_unit( DRYAD         )
    call suicide_unit( HIPPO         )
    call suicide_unit( HIPPO_RIDER   )
    call suicide_unit( HUNTRESS      )
    call suicide_unit( CHIMAERA      )
endfunction

//============================================================================
//  send_hero
//============================================================================
function send_hero takes boolean send, integer heroid returns boolean
    if not send or GetUnitCountDone(heroid) < 1 then
        return false
    endif
    call SuicideUnitEx( 1, heroid, target )
    return true
endfunction

//============================================================================
//  hero_failsafe
//============================================================================
function hero_failsafe takes nothing returns nothing
    loop
        set send_lich = send_hero( send_lich, LICH       )
        call Sleep(0.5)

        set send_lord = send_hero( send_lord, DREAD_LORD )
        call Sleep(0.5)

        set send_azga = send_hero( send_azga, PIT_LORD   )
        call Sleep(0.5)
    endloop
endfunction

//============================================================================
//  send_waves
//============================================================================
function send_waves takes nothing returns nothing
    local integer index = 0

    loop
        exitwhen GetUnitCountDone(CRYPT)>0
        call Sleep(1)
    endloop

    loop
        call check_abort()

        call InitAssaultGroup()

        call TraceI("next wave = %d\n",wave_type[index])
        call TraceI("strength  = %d\n",strength)

        if wave_type[index]!=PIT_STOP_WAVE then
            call do_EVERY_WAVE_FIRST()

            if index >= 2 then
                call consider_bonus_love()
            endif
        endif

        if wave_type[index]==BASIC_WAVE1 then
            call do_BASIC_WAVE1()

        elseif wave_type[index]==BASIC_WAVE2 then
            call do_BASIC_WAVE2()

        elseif wave_type[index]==AIR_WAVE then
            call do_AIR_WAVE()

        elseif wave_type[index]==ANTI_AIR_WAVE then
            call do_ANTI_AIR_WAVE()

        elseif wave_type[index]==ANTIMAGIC_WAVE then
            call do_ANTIMAGIC_WAVE()

        elseif wave_type[index]==SIEGE_WAVE then
            call do_SIEGE_WAVE()

        elseif wave_type[index]==POSSESS_WAVE then
            call do_POSSESS_WAVE()

        elseif wave_type[index]==SKELETON_WAVE then
            call do_SKELETON_WAVE()

        elseif wave_type[index]==PLAGUE_WAVE then
            call do_PLAGUE_WAVE()

        elseif wave_type[index]==BURNING_WAVE then
            call do_BURNING_WAVE()

        elseif wave_type[index]==F_U1_WAVE then
            call do_F_U1_WAVE()

        elseif wave_type[index]==F_U2_WAVE then
            call do_F_U2_WAVE()

        elseif wave_type[index]==F_U3_WAVE then
            call do_F_U3_WAVE()

        elseif wave_type[index]==PIT_STOP_WAVE then
            // no units

        else // error
            call Trace("**UNKNOWN WAVE CONTENTS**\n")
            call do_BASIC_WAVE1()
        endif

        if wave_type[index]!=PIT_STOP_WAVE then
            call do_EVERY_WAVE_LAST(wave_hero[index])

            if at_stage1 then
                set at_stage1 = false
                call SetCaptainHome(BOTH_CAPTAINS,stage_x2,stage_y2)
            else
                set at_stage1 = true
                call SetCaptainHome(BOTH_CAPTAINS,stage_x1,stage_y1)
            endif
        endif

        call AddSleepSeconds(wave_sleep[index])
        loop
            call TraceI("forming wave (T%+d seconds)\n",-sleep_seconds)
            call check_abort()
            call possessions()
            call form_wave()
            exitwhen sleep_seconds <= 0
        endloop

        call suicide_wave()

        set index = index + 1
        if index==wave_count then
            set index = wave_restart
        endif
    endloop
endfunction

//============================================================================
//  get_commands
//============================================================================
function get_commands takes nothing returns nothing
    local integer cmd
    local integer data
    loop
        loop
            exitwhen CommandsWaiting() != 0
            call Sleep(1)
        endloop

        set cmd  = GetLastCommand()
        set data = GetLastData()
        call PopLastCommand()
       
        if cmd == CMD_SET_X1 then
            call TraceI("CMD_SET_X1 (%d)\n",data)

            set stage_x1 = data

        elseif cmd == CMD_SET_Y1 then
            call TraceI("CMD_SET_Y1 (%d)\n",data)

            set stage_y1 = data

        elseif cmd == CMD_SET_X2 then
            call TraceI("CMD_SET_X2 (%d)\n",data)

            set stage_x2 = data
            set need_stage2 = false

        elseif cmd == CMD_SET_Y2 then
            call TraceI("CMD_SET_Y2 (%d)\n",data)

            set stage_y2 = data
            set need_stage2 = false

        elseif cmd == CMD_SET_SEQUENCE then
            call TraceI("CMD_SET_SEQUENCE (%d)\n",data)

            if data==2 then // Jaina's base
                set harvest_ghouls = 5
            else // original base and Thrall's base
                set harvest_ghouls = 13
            endif

            call setup_waves(data)

        elseif cmd == CMD_SET_TARGET then
            call TraceI("CMD_SET_TARGET (%d)\n",data)

            set target = data

            if need_stage2 then
                set stage_x2 = stage_x1
                set stage_y2 = stage_y1
            endif

            call SetCaptainHome(BOTH_CAPTAINS,stage_x1,stage_y1)
            call send_waves()

        else
            call TraceI("unknown command (%d)\n",cmd)
            call TraceI("unknown data (%d)\n",data)
        endif
    endloop
endfunction

//============================================================================
//  start_unit
//============================================================================
function start_unit takes integer qty, integer unitid, boolean wait returns nothing
    call SetBuildUnit( qty, unitid )
    loop
        exitwhen TownCount(unitid) >= qty
        call Sleep(2)
    endloop

    if not wait then
        return
    endif

    loop
        exitwhen TownCountDone(unitid) >= qty
        call Sleep(1)
    endloop
endfunction

//============================================================================
//  define_town
//============================================================================
function define_town takes nothing returns nothing
    set do_campaign_farms       = false
    set campaign_wood_peons     = 15
    set campaign_basics_speed   = 3

    call SetSlowChopping(false)

    call start_unit( 1, ACOLYTE,        true  )
    call start_unit( 1, NECROPOLIS_1,   false )
    call start_unit( 2, CRYPT,          false )
    call start_unit( 1, GRAVEYARD,      false )
    call start_unit( 1, UNDEAD_ALTAR,   false )
    call start_unit( 4, ZIGGURAT_1,     false )

    call SetBuildUnit( harvest_ghouls, GHOUL )

    call start_unit( 1, NECROPOLIS_2,   true  )
    call start_unit( 2, SLAUGHTERHOUSE, false )
    call start_unit( 1, DAMNED_TEMPLE,  false )
    call start_unit( 1, SAC_PIT,        false )
    call start_unit( 1, NECROPOLIS_3,   true  )
    call start_unit( 1, BONEYARD,       false )
    call start_unit( 4, ZIGGURAT_2,     false )
    call start_unit( 4, ACOLYTE,        false )

    loop
        exitwhen aborting and GetUnitCountDone(ACOLYTE)>0
        call Sleep(5)
    endloop

    call Trace("***** UNSUMMON TOWN *****\n")
    call InitBuildArray()
    call InitAssaultGroup()
    call UnsummonAll()
    call SleepForever()
endfunction

//============================================================================
//  main
//============================================================================
function main takes nothing returns nothing
    call TraceI("***** rainbow main started for %d *****\n",GetAiPlayer())

    debug if GetAiPlayer()==2 then
        set do_debug_cheats = true
        call Cheat("aitrace")
        //call Cheat("ss")
    endif

    call CampaignAI(ZIGGURAT_1,null)
    call StartThread(function define_town)
    call StartThread(function hero_failsafe)
    call get_commands()
endfunction
 
Level 2
Joined
Jan 20, 2015
Messages
9
ok i finally found the problem, and i feel stupid. when i was done with making changes i would save both the "save map" and "save campagin" so it would cancel the AI somehow.

anyway, thanks for the help everyone i really appreciate it.
 
Status
Not open for further replies.
Top