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

Custom AI Script (JASS) Share

Hi all fellow coders!

I want to share an AI script (JASS) which I made for my current project, which you can find on my signature. The script is used on the second map for the Alliance Naval Base forces.

Why could it be interesting? I add a behaviour to command the AI to stop producing a certain unit and spare them if possible once a certain condition is met. In game this is done to fake the blacksmith prerequisite for the rifleman. It also supports command (and data) to start/stop attack waves.

Nothing special really, but since I found quite the shortage of working AI scripts sample I thought that could be useful to someone willing to code an AI.

Beside that, sharing this, I hope some veteran could also partecipate in the discussion, share some tips and things like that :)


JASS:
//============================================================================
//  RR Prologue 02 -- Alliance Blue Human -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//============================================================================

// GLOBALS
//------------------------------------------------
globals

    player User = PlayerEx(1)
      
    boolean UseRiflemen = false
    boolean AttackEnabled = false
  
    constant integer COMMAND_RIFLEMEN = 1
    constant integer DATA_RIFLEMEN_ENABLE = 1
    constant integer DATA_RIFLEMEN_DISABLE = 0
  
    constant integer COMMAND_ATTACK = 2
    constant integer DATA_ATTACK_ENABLE = 1
    constant integer DATA_ATTACK_DISABLE = 0
  
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnit(2, PEASANT)

endfunction
  
// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

    if UseRiflemen then
        call CampaignDefenderEx(1, 2, 3, FOOTMAN)  
        call CampaignDefenderEx(0, 1, 2, RIFLEMAN)
    else
        call CampaignDefenderEx(1, 2, 3, FOOTMAN)
    endif

endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesWithRiflemen takes nothing returns nothing
    local boolean AttackWaveUseRiflemen = UseRiflemen
  
    loop

        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 4, FOOTMAN)
        call SuicideOnPlayerEx(M6, M5, M4, User)
        exitwhen AttackWaveUseRiflemen != UseRiflemen

        // *** WAVE 2 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 4, RIFLEMAN)
        call SuicideOnPlayerEx(M6, M5, M4, User)
        exitwhen AttackWaveUseRiflemen != UseRiflemen
      
        // *** WAVE 3 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, FOOTMAN)
        call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
        call SuicideOnPlayerEx(M6, M5, M4, User)
        exitwhen AttackWaveUseRiflemen != UseRiflemen

    endloop
  
endfunction

function AttackWavesWithoutRiflemen takes nothing returns nothing
    local boolean AttackWaveUseRiflemen = UseRiflemen

    loop
  
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 4, FOOTMAN)
        call SuicideOnPlayerEx(M6, M5, M4, User)
        exitwhen AttackWaveUseRiflemen != UseRiflemen
   
    endloop
  
endfunction

function AttackLoop takes nothing returns nothing

    loop
      
        // Attack only if attack is enabled
        if AttackEnabled then
            // Attack with riflemen or not depending on the current condition
            if UseRiflemen then
                call AttackWavesWithRiflemen()          
            else
                call AttackWavesWithoutRiflemen()
            endif
        endif
      
        // Wait a little time before looping again
        call Sleep(10)
  
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
    if CommandsWaiting() > 0 then   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
        // Update rifleman behaviour command (to simulate having a blacksmith)
        if Command == COMMAND_RIFLEMEN then       
            if Data == DATA_RIFLEMEN_ENABLE then
                set UseRiflemen = true
            elseif Data == DATA_RIFLEMEN_DISABLE then
                set UseRiflemen = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid riflemen command data received!")
            endif    
            // Reset build order and defenders
            call InitBuildArray()
            call InitDefenseGroup()
            call SetBuildOrder()       
            call SetDefenders()
        // Update attack behaviour command (to start/stop attack waves)
        elseif Command == COMMAND_ATTACK then
            if Data == DATA_ATTACK_ENABLE then
                set AttackEnabled = true
            elseif Data == DATA_ATTACK_DISABLE then
                set AttackEnabled = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack command data received!")
            endif    
        else
            call DisplayTextToPlayer(User, 0, 0, "AI Warning: Unknown command received!")
        endif
    endif
  
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)  
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
  
endfunction
   
// MAIN
//------------------------------------------------
function main takes nothing returns nothing

    call CampaignAI(LUMBER_MILL, null)
  
    // Define AI properties  
    call DoCampaignFarms(false)
    call SetReplacements(0, 1, 2)  
  
    // Define build order
    call SetBuildOrder()
  
    // Define defenders
    call SetDefenders()
  
    // Start command loop for external signals  
    call StartThread(function CommandLoop)
  
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
      
endfunction


Cheers and keep up the good work everyone!
 
Last edited:
Level 12
Joined
Jun 15, 2016
Messages
472
I think this is a fantastic idea. I feel that in most singleplayer maps AI is always thrown by the wayside to the generic template, making a lot of maps feel cheap in a way. Instead of cracking the puzzle of how a computer player thinks and acts, difficulty is reduced to dealing with a lot of attacking groups at the same time.

As for a script to contribute:

JASS:
//=================================================================================================
//                     FRIENDLY SUBORDINATE AI
// Version: 0.2 || Created by: Nowow
//=================================================================================================



//-------------------------------------------------------------------------------------------------
// GLOBAL DECLERATION
//-------------------------------------------------------------------------------------------------
globals
    //---------------------------------------------------------------------------------------------
    // INSERT CUSTOM RACE OBJECT DATA HERE
    //---------------------------------------------------------------------------------------------
  
    //---------------------------------------------------------------------------------------------
    constant integer HERO_UNIT                  = 'Hpal'
    constant integer RACE_FARM                  = 'hhou'
    //---------------------------------------------------------------------------------------------
    // STARTING COMMANDS VARIABLES
    // Since catching ingame commands will be set differently in this AI, starting variables will
    // a set prefix
    constant integer SET_OWN_HOME               = 1010
    constant integer SET_ALLY_HOME              = 1020
  
    //---------------------------------------------------------------------------------------------
    // COMMAND DEFINING VARIABLES
    // Each of the following commands will declere the next command's purpose. i.e. commands will come
    // in sets of two. For example, NEXT_CMD_HOLD states the next command's role is to assign the
    // home of the attack captain, then the next command will hold both X and Y coordinates
    constant integer NEXT_CMD_HOLD              = 10
    constant integer NEXT_CMD_TARGET            = 20
    constant integer NEXT_CMD_WAVE              = 30
  
    // boolean veriable which becomes true once all starting commands have been set. this will cause
    // the AI to start playing
  
    boolean START_CMD_DONE                      = false
  
    //---------------------------------------------------------------------------------------------
    // OWN & ALLY HOME COORDS
  
    real OWN_HOME_X                             = 0
    real OWN_HOME_Y                             = 0
    real ALLY_HOME_X                            = 0
    real ALLY_HOME_Y                            = 0
  
    //---------------------------------------------------------------------------------------------
    // STORE HOLD AND TARGET VALIABLES
    real HOLD_X                                 = 0
    real HOLD_Y                                 = 0
    real TARGET_X                               = 0
    real TARGET_Y                               = 0
  
    //---------------------------------------------------------------------------------------------
    // ORDER DELEGATION - from the commands thread to the attack thread
    boolean DEL_HOLD                            = false
    boolean DEL_ATAK                            = false
      
    //---------------------------------------------------------------------------------------------
    // GAMESTATES:
    // MICRO_STATE - micro management of AI player, WAVE_STATE - boring attack waves
    constant integer WAVE_STATE                 = 100
    constant integer MICRO_STATE                = 200
  
    // The current game state of the AI (either MICRO_STATE or WAVE_STATE)
    // default starts at WAVE_STATE
    integer CURRENT_STATE                       = 100
  
    // Keeps AI from popping a command in order to use allow_signal_abort to shift states
     // set allow_signal_abort                      = false
  
    //---------------------------------------------------------------------------------------------
    // FOE PLAYER
    // Current target player of WAVE_STATE: Arbitrarily set to to blue player(slot 2).
    player TARGET_FOE                        = Player(1)
  
    //---------------------------------------------------------------------------------------------
    // user for debug purposes
    player user = Player(0)
endglobals

//=================================================================================================
// UTILITY FUNCTIONS
//=================================================================================================

//-------------------------------------------------------------------------------------------------
// DEBUG FUNCTIONS
//-------------------------------------------------------------------------------------------------

function B2S takes boolean b returns string
    if b then
        return "true"
    else
        return "false"
    endif
endfunction

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

// Courtesy of AIAndy and Tommi. See source:
// http://www.hiveworkshop.com/threads/two-custom-campaign-ais-examples.8939/
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

//-------------------------------------------------------------------------------------------------
// ATTACK UTILITY FUNCTIONS
//-------------------------------------------------------------------------------------------------

function GroupCanFight takes nothing returns boolean
    return (GetUnitCountDone(HERO_UNIT) > 0 and CaptainGroupSize() > 4)
endfunction

function SleepOnWay takes nothing returns nothing
    loop
        exitwhen allow_signal_abort
        exitwhen CaptainAtGoal()
        exitwhen not GroupCanFight()
      
        call Sleep(2.0)
    endloop
endfunction

function SleepOnCombat takes nothing returns nothing
    loop
        exitwhen allow_signal_abort
        exitwhen not CaptainInCombat(true)
        exitwhen not GroupCanFight()
      
        call Sleep(2.0)
    endloop
endfunction

function FormGroupCustom takes integer timeout_max returns boolean
    local integer timeout = 0
    local integer index
    local integer count
    local integer unitid
    local integer desire
  
    loop
        call Sleep(5)
        set timeout = timeout + 5
      
        set index = 0
        loop
            exitwhen index == harass_length

            set unitid = harass_units[index]
            set desire = harass_max[index]
            set count  = TownCountDone(unitid)

            call Conversions(desire,unitid)

            if count >= desire then
                call AddAssault(desire,unitid)
            else
                set desire = harass_qty[index]

                if count < desire then
                    call AddAssault(desire,unitid)
                else
                    call AddAssault(count,unitid)
                endif
            endif

            set index = index + 1
        endloop
      
        // FormGroup exit conditions
        if CaptainInCombat(true) then
            call DisplayTextToPlayer(user,0,0,"FormGroupCustom: Captain is in combat, exiting form group")
        elseif timeout >= timeout_max then
            call DisplayTextToPlayer(user,0,0,"FormGroupCustom: max timeout reached, exiting form group")
        elseif CaptainIsFull() and CaptainReadiness() >= 50 then
            call DisplayTextToPlayer(user,0,0,"FormGroupCustom: captain is ready, exiting form group")
        elseif (CaptainIsFull() and CaptainReadiness() >= 30 and timeout >= (3*timeout_max/4)) then
            call DisplayTextToPlayer(user,0,0,"FormGroupCustom: captain almost ready before timeout, exiting form group")
        endif
      
        exitwhen CaptainInCombat(true)
        exitwhen timeout >= timeout_max
        exitwhen (CaptainIsFull() and CaptainReadiness() >= 30 and timeout >= (3*timeout_max/4))
        exitwhen CaptainIsFull() and CaptainReadiness() >= 50
    endloop
  
    return GroupCanFight()
endfunction

//-------------------------------------------------------------------------------------------------

function StateShift takes nothing returns nothing
    set allow_signal_abort = false
    call DisplayTextToPlayer(user,0,0,"VMAI: Attack state shifted. No longer pending shift")
endfunction

function SetTargetPlayer takes integer i returns nothing
    set TARGET_FOE = Player(i)
endfunction

function CheckStateChange takes integer state returns boolean
    return (state != CURRENT_STATE)
endfunction

function SetMicroState takes nothing returns nothing
    set CURRENT_STATE = MICRO_STATE
endfunction

function SetWaveState takes nothing returns nothing
    set CURRENT_STATE = WAVE_STATE
endfunction

function EmptyCommandStack takes nothing returns nothing
    loop
        exitwhen CommandsWaiting() == 0
        call PopLastCommand()
        call Sleep(0.1)
    endloop
endfunction

//-------------------------------------------------------------------------------------------------
// BUILDING AND DEFENSE GROUP CONFIGURATION
//-------------------------------------------------------------------------------------------------

function BuildingPriorities takes nothing returns nothing
    call SetBuildUnit( 1, TOWN_HALL          )
    call SetBuildUnit( 5, PEASANT            )
    call SetBuildUnit( 1, BARRACKS           )
    call SetBuildUnit( 2, HOUSE              )
    call SetBuildUnit( 1, HUMAN_ALTAR        )
    call SetBuildUnit( 8, PEASANT            )
    call SetBuildUnit( 4, HOUSE              )
    call SetBuildUnit( 1, BLACKSMITH         )
    call SetBuildUnit( 1, LUMBER_MILL        )
    call SetBuildUnit( 1, KEEP               )
    call SetBuildUnit( 5, HOUSE              )
    call SetBuildUnit( 1, SANCTUM            )
    call SetBuildUnit( 1, ARCANE_VAULT       )
    call SetBuildUnit( 7, HOUSE              )
    call SetBuildUnit( 2, BARRACKS           )
    call SetBuildUnit( 1, WORKSHOP           )
    call SetBuildUnit( 8, HOUSE              )
    call SetBuildUnit( 1, AVIARY             )
    call SetBuildUnit( 10, HOUSE             )
    call SetBuildUnit( 1, CASTLE             )
    call SetBuildUnit( 2, SANCTUM            )
    call SetBuildUnit( 12, HOUSE             )
    call SetBuildUnit( 1, HERO_UNIT          )
endfunction

function SetDefenseGroup takes nothing returns nothing
    call CampaignDefenderEx(4,4,4,FOOTMAN)
    call CampaignDefenderEx(2,2,2,PRIEST)
    call CampaignDefenderEx(1,1,1,RIFLEMAN)
endfunction

//=================================================================================================
// COMMAND HANDLING THREAD
//=================================================================================================

function ProcessDataCommand takes integer assignment returns boolean
    local integer cmd = GetLastCommand()
    local integer data = GetLastData()
    local integer start_state = CURRENT_STATE
  
    //---------------------------------------------------------------------------------------------
    // START COMMANDS
    //---------------------------------------------------------------------------------------------
    if assignment == SET_OWN_HOME then
        set OWN_HOME_X = I2R(cmd)
        set OWN_HOME_Y = I2R(data)
        set HOLD_X = I2R(cmd)
        set HOLD_Y = I2R(data)
      
        call SetCaptainHome(BOTH_CAPTAINS,OWN_HOME_X,OWN_HOME_Y)
    //---------------------------------------------------------------------------------------------
    elseif assignment == SET_ALLY_HOME then
        set ALLY_HOME_X = cmd
        set ALLY_HOME_Y = data
      
        // This signals the main AI thread to start it's actions
        set START_CMD_DONE = true
    //---------------------------------------------------------------------------------------------
    // MICRO STATE COMMANDS
    //---------------------------------------------------------------------------------------------
    elseif assignment == NEXT_CMD_HOLD then
        set HOLD_X = I2R(cmd)
        set HOLD_Y = I2R(data)
      
        call SetMicroState()
        set DEL_HOLD = true
    //---------------------------------------------------------------------------------------------
    elseif assignment == NEXT_CMD_TARGET then
        set TARGET_X = I2R(cmd)
        set TARGET_Y = I2R(data)
      
        call SetMicroState()
        set DEL_ATAK = true
    //---------------------------------------------------------------------------------------------
    // WAVE STATE COMMANDS
    //---------------------------------------------------------------------------------------------
    elseif assignment == NEXT_CMD_WAVE then
        call SetWaveState()
        call SetTargetPlayer(data)
    //---------------------------------------------------------------------------------------------
    // INVALID INPUT
    //---------------------------------------------------------------------------------------------
    else
        call DisplayTextToPlayer(user,0,0,"VMAI: Unknown assignment, check triggers.")
    endif
  
    call PopLastCommand()
    return CheckStateChange(start_state)
endfunction

function CommandLoop takes nothing returns nothing
    local integer StartCommands = 0
    local integer AssignmentCommand = 0
  
    call DisplayTextToPlayer(user,0,0,"VMAI: Starting command loop.")
    loop
        loop
            exitwhen CommandsWaiting() > 0
            call Sleep(0.5)
        endloop
      
        // Get the assignment command
        set AssignmentCommand = GetLastCommand()
        call PopLastCommand()
        call DisplayTextToPlayer(user,0,0,("VMAI: assignment received:" + Int2Str(AssignmentCommand)))
      
        loop
            exitwhen CommandsWaiting() > 0
            call Sleep(0.5)
        endloop
      
        call DisplayTextToPlayer(user,0,0,"VMAI: Data command received.")
        // Change AI according to data command. In case the command shifted the state, set
        // pending to true, in order to hold the AI thread until the state changes.
        
        set allow_signal_abort = ProcessDataCommand(AssignmentCommand)
        call DisplayTextToPlayer(user,0,0,("VMAI: Data command processed - pending shift = " + B2S(allow_signal_abort)))
      
        loop
            exitwhen not allow_signal_abort
            call Sleep(0.5)
        endloop
        call DisplayTextToPlayer(user,0,0,"VMAI: command loop done, cleaning leftovers.")
      
        // Empty the command stack just in case additional commands will be inserted,
        // so as not to mess up the command order.
        call EmptyCommandStack()
        call DisplayTextToPlayer(user,0,0,"VMAI: command stack is clean")
    endloop
endfunction

//=================================================================================================
// ATTACK THREAD
//=================================================================================================

//-------------------------------------------------------------------------------------------------
// WAVE STATE
//-------------------------------------------------------------------------------------------------

function AttackWaves takes nothing returns nothing
    call SetCaptainHome(ATTACK_CAPTAIN,OWN_HOME_X,OWN_HOME_Y)
  
    loop
        // Wave 1
        call InitAssaultGroup()
        call CampaignAttackerEx(1,1,1, FOOTMAN      )
        call SuicideOnPlayer(20,TARGET_FOE)
        exitwhen allow_signal_abort
  
        // Wave 2
        call InitAssaultGroup()
        call CampaignAttackerEx(2,2,2, FOOTMAN      )
        call SuicideOnPlayer(M1,TARGET_FOE)
        exitwhen allow_signal_abort
  
        loop
            // Wave 3
            call InitAssaultGroup()
            call CampaignAttackerEx(3,3,3, FOOTMAN      )
            call SuicideOnPlayer(80,TARGET_FOE)
            exitwhen allow_signal_abort
      
            // Wave 4
            call InitAssaultGroup()
            call CampaignAttackerEx(4,4,4, FOOTMAN      )
            call SuicideOnPlayer(100,TARGET_FOE)
            exitwhen allow_signal_abort
        endloop
    endloop
  
    call DisplayTextToPlayer(user,0,0,"Exiting wave state")
endfunction

//-------------------------------------------------------------------------------------------------
// MICRO STATE
//-------------------------------------------------------------------------------------------------
// POSSIBLE SCENARIOS IN MICROSTATE:
// 1. A delegated hold order - attack group goes to the hold position and stays there.
// 2. A delegated attack order - attack group goes to target and retreats back to holding position
//    if Varimathras dies or only 3 units are left in the attack group.
// 3. Everyone in the attack group dies - captain's location is set to the town.

function MicroAttackGroup takes nothing returns nothing
    call InitAssaultGroup()
    call CampaignAttackerEx(2,2,2, KNIGHT       )
    call CampaignAttackerEx(2,2,2, SORCERESS    )
    call CampaignAttackerEx(1,1,1, HERO_UNIT    )
endfunction

function MicroWaves takes nothing returns nothing
    call DisplayTextToPlayer(user,0,0,"Starting microwave, readying attack group.")
    call MicroAttackGroup()
    loop
        exitwhen FormGroupCustom(M5)
    endloop
    call DisplayTextToPlayer(user,0,0,"Attack group ready, moving towards objective.")
    loop
        exitwhen allow_signal_abort
      
        if DEL_ATAK then
            set DEL_ATAK = false
          
            call CaptainAttack(TARGET_X,TARGET_Y)
            call SleepOnWay()
            call SleepOnCombat()
            call CaptainGoHome()

        elseif DEL_HOLD then
            set DEL_HOLD = false
      
            call SetCaptainHome(ATTACK_CAPTAIN,HOLD_X,HOLD_Y)
            call ClearCaptainTargets()
            call CaptainGoHome()
      
        elseif (CaptainGroupSize() == 0) then
            call SetCaptainHome(ATTACK_CAPTAIN,OWN_HOME_X,OWN_HOME_Y)
            call ClearCaptainTargets()
            call TeleportCaptain(OWN_HOME_X,OWN_HOME_Y)
        endif
      
        call Sleep(2.0)
    endloop
  
    call DisplayTextToPlayer(user,0,0,"Exiting micro state")
endfunction

//-------------------------------------------------------------------------------------------------
// STATE SWITCH
//-------------------------------------------------------------------------------------------------

function StateSwitch takes nothing returns nothing
    loop
        if CURRENT_STATE == WAVE_STATE then
            call AttackWaves()
        elseif CURRENT_STATE == MICRO_STATE then
            call MicroWaves()
        else
            call DisplayTextToPlayer(user,0,0,"Not wave state or micro sate. Defuq?!")
        endif
      
        set allow_signal_abort = false
    endloop
endfunction

//=================================================================================================
// MAIN FUNCTION
//=================================================================================================

function main takes nothing returns nothing
    call DisplayTextToPlayer(user,0,0,"VMAI: Script received.")
    call CampaignAI(RACE_FARM,null)
    call SetPeonsRepair(true)
    call GroupTimedLife(true)

    call StartThread(function CommandLoop)
    loop
        exitwhen START_CMD_DONE
        call Sleep(1)
    endloop
  
    call BuildingPriorities()
    call SetDefenseGroup()
  
    call DisplayTextToPlayer(user,0,0,"VMAI: starting commands received, starting attack thread")
    call StateSwitch()
endfunction

This was supposed to be in Chasing the Dawn, but real life got in the way and I never really got to finish it. The idea is simple: an AI for an allied player which does as you command. If you tell it to hold a position, it will send there a group of troups until all are dead, if you tell it to attack a certain player, it will send attack waves to that player, etc.

IIRC the script was almost operational, with some minor problem switching between attacking a player and holding a location. Still, it never got off the ground. If anyone wants to try fixing it, I have a more complete testmap to send.
 
Sharing another script. This one is for the third prologue mission Re-Reforged. It's completely custom, of course, and has some interesting things inside:
  • It is supposed to be applied to a player not very keen on defending. The AI in this mission is quite aggressive because it both suits the situation and there are other AI troops who may defend the AI base in some circumstances.
  • It uses a custom hero, Kelen the Seeker (the High Elf Archmage) and a custom unit (the Hydromancer).
  • It uses data to toggle between attacking the player, the trolls (an AI faction allied with the player) and both.
Enjoy.


JASS:
//============================================================================
//  RR Prologue 03 -- Kul Tiras Dark Green Human -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//============================================================================

// GLOBALS
//------------------------------------------------
globals

   constant integer KELEN = 'Haah'
   constant integer HYDROMANCER = 'h000'
   constant integer UPG_HYDROMANCY = 'R000'

   player User = PlayerEx(1)
   player Trolls = PlayerEx(16)
      
   boolean AttackUser = false
   boolean AttackTrolls = false
  
   constant integer COMMAND_ATTACK_TROLLS = 2
   constant integer DATA_ATTACK_TROLLS_ENABLE = 1
   constant integer DATA_ATTACK_TROLLS_DISABLE = 0
  
   constant integer COMMAND_ATTACK_USER = 1
   constant integer DATA_ATTACK_USER_ENABLE = 1
   constant integer DATA_ATTACK_USER_DISABLE = 0
  
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

   call SetBuildUnitEx(1, 1, 1, PEASANT)
   call SetBuildUnitEx(1, 1, 1, TOWN_HALL)
   call SetBuildUnitEx(7, 7, 7, PEASANT)
   call SetBuildUnitEx(1, 1, 1, BARRACKS)
   call SetBuildUnitEx(2, 2, 2, HOUSE)
   call SetBuildUnitEx(1, 1, 1, HUMAN_ALTAR)
   call SetBuildUnitEx(4, 4, 4, HOUSE)
   call SetBuildUnitEx(0, 1, 1, LUMBER_MILL)
   call SetBuildUnitEx(0, 1, 1, BLACKSMITH)
   call SetBuildUnitEx(0, 2, 3, HOUSE)
   call SetBuildUnitEx(0, 0, 1, KEEP)
   call SetBuildUnitEx(0, 0, 1, ARCANE_SANCTUM)
   call SetBuildUnitEx(0, 0, 2, HOUSE)
   call SetBuildUnitEx(0, 0, 1, ARCANE_VAULT)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

   call CampaignDefenderEx(1, 2, 2, FOOTMAN)  
   call CampaignDefenderEx(1, 1, 2, RIFLEMAN)
   call CampaignDefenderEx(0, 0, 1, HYDROMANCER)
   call CampaignDefenderEx(1, 1, 1, KELEN)

endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

   call Sleep(M5)

   call SetBuildUpgrEx(1, 1, 1, UPG_DEFEND)
  
   call Sleep(M4)
  
   call SetBuildUpgrEx(1, 1, 1, UPG_MELEE)
  
   call Sleep(M4)
  
   call SetBuildUpgrEx(1, 1, 1, UPG_ARMOR)
   call SetBuildUpgrEx(0, 1, 1, UPG_HYDROMANCY)
      
   call Sleep(M4)
  
   call SetBuildUpgrEx(1, 1, 1, UPG_RANGED)
  
   call Sleep(M4)
  
   call SetBuildUpgrEx(1, 1, 1, UPG_LEATHER)
   call SetBuildUpgrEx(0, 1, 1, UPG_GUN_RANGE)
  
   call Sleep(M3)
  
   call SetBuildUpgrEx(0, 0, 1, UPG_MASONRY)
  
   call Sleep(M4)
  
   call SetBuildUpgrEx(1, 2, 2, UPG_MELEE)
  
   call Sleep(M5)
  
   call SetBuildUpgrEx(1, 1, 2, UPG_ARMOR)
      
   call Sleep(M4)
  
   call SetBuildUpgrEx(1, 2, 2, UPG_RANGED)
  
   call Sleep(M5)
  
   call SetBuildUpgrEx(1, 1, 2, UPG_LEATHER)

endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
  
   loop
  
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 3, 3, FOOTMAN)
       call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
       call SuicideOnPlayerEx(M4, M3, M2, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 2, FOOTMAN)
       call CampaignAttackerEx(1, 2, 2, RIFLEMAN)
       call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
       call CampaignAttackerEx(1, 1, 1, KELEN)
       call SuicideOnPlayerEx(M5, M4, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, FOOTMAN)
       call CampaignAttackerEx(0, 1, 1, RIFLEMAN)
       call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
       call SuicideOnPlayerEx(M4, M3, M2, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
  
       loop

           // *** WAVE 4 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(3, 4, 5, FOOTMAN)
           call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 1, KELEN)
           call SuicideOnPlayerEx(M5, M4, M3, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

           // *** WAVE 5 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 3, FOOTMAN)
           call CampaignAttackerEx(2, 2, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
           call SuicideOnPlayerEx(M4, M3, M2, User)      
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
          
           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 3, FOOTMAN)
           call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
           call CampaignAttackerEx(1, 1, 1, KELEN)
           call SuicideOnPlayerEx(M5, M4, M3, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

       endloop
      
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
      
   endloop
  
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls

   loop
  
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 3, 3, FOOTMAN)
       call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
       call SuicideOnPlayerEx(M4, M3, M2, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 2, FOOTMAN)
       call CampaignAttackerEx(1, 2, 2, RIFLEMAN)
       call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
       call CampaignAttackerEx(1, 1, 1, KELEN)
       call SuicideOnPlayerEx(M5, M4, M3, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
      
       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, FOOTMAN)
       call CampaignAttackerEx(0, 1, 1, RIFLEMAN)
       call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
       call SuicideOnPlayerEx(M4, M3, M2, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
      
       loop

           // *** WAVE 4 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(3, 4, 5, FOOTMAN)
           call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 1, KELEN)
           call SuicideOnPlayerEx(M5, M4, M3, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

           // *** WAVE 5 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 3, FOOTMAN)
           call CampaignAttackerEx(2, 2, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
           call SuicideOnPlayerEx(M4, M3, M2, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
          
           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 3, FOOTMAN)
           call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
           call CampaignAttackerEx(1, 1, 1, KELEN)
           call SuicideOnPlayerEx(M5, M4, M3, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

       endloop
      
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
      
   endloop
  
endfunction

function AttackWavesAttackAll takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
  
   loop

       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 3, 3, FOOTMAN)
       call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
       call SuicideOnPlayerEx(M4, M3, M2, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 2, FOOTMAN)
       call CampaignAttackerEx(1, 2, 2, RIFLEMAN)
       call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
       call CampaignAttackerEx(1, 1, 1, KELEN)
       call SuicideOnPlayerEx(M5, M4, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
      
       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, FOOTMAN)
       call CampaignAttackerEx(0, 1, 1, RIFLEMAN)
       call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
       call SuicideOnPlayerEx(M4, M3, M2, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
      
       loop

           // *** WAVE 4 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(3, 4, 5, FOOTMAN)
           call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 1, KELEN)
           call SuicideOnPlayerEx(M5, M4, M3, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

           // *** WAVE 5 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 3, FOOTMAN)
           call CampaignAttackerEx(2, 2, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
           call SuicideOnPlayerEx(M4, M3, M2, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
          
           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 3, FOOTMAN)
           call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
           call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
           call CampaignAttackerEx(1, 1, 1, KELEN)
           call SuicideOnPlayerEx(M5, M4, M3, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

       endloop
      
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
      
   endloop
  
endfunction  

function AttackLoop takes nothing returns nothing

    loop
      
       // Attack the user and/or the trolls depending on the condition
        if AttackUser and AttackTrolls then
           call AttackWavesAttackAll()
       else
           if AttackUser then
               call AttackWavesAttackUser()
           endif
           if AttackTrolls then
               call AttackWavesAttackTrolls()
           endif
        endif
        
        // Wait a little time before looping again
        call Sleep(10)
  
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
    if CommandsWaiting() > 0 then    
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       // Update attack trolls behaviour command (to start/stop attack against the trolls)
       if Command == COMMAND_ATTACK_TROLLS then
           if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack trolls command data received!")
            endif  
       // Update attack user behaviour command (to start/stop attack against the user)      
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack user command data received!")
            endif    
        else
            call DisplayTextToPlayer(User, 0, 0, "AI Warning: Unknown command received!")
        endif
    endif
  
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)  
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
  
endfunction

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

    call CampaignAI(HOUSE, null)
  
    // Define AI properties  
   call DoCampaignFarms(false)
   call SetReplacements(1, 1, 1)
   call GroupTimedLife(true)
  
   // Define build order
   call SetBuildOrder()
  
   // Start to research upgrades at timed intervals
   call StartThread(function ResearchUpgrades)
  
   // Define defenders
   call SetDefenders()
  
    // Start command loop for external signals  
    call StartThread(function CommandLoop)
  
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
      
endfunction
 
Last edited:
Sharing another bunch of scripts. These are used in the fifth prologue mission Re-Reforged. They are all completely custom, of course, and have some interesting things inside:
  • They are supposed to be applied to the Underworld Minions/Spitscale players. They are built to attack and not to defend and to move across water.
  • All units, except for some naga units in the Spitscale script, are custom.
  • They use data to toggle between attacking the player, the trolls (an AI faction allied with the player) and both.
  • At a certain time, a massive suicide wave is started. The wave can be reverted with the appropriate commands.
Enjoy.


JASS:
//============================================================================
//  RR Prologue 05 -- Spitscale Coal "Nagas" -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//============================================================================

// GLOBALS
//------------------------------------------------
globals

   constant integer MG_CLIFFRUNNER = 'n009'
   constant integer MG_BLOODGILL = 'n00H'
   constant integer MG_TIDEWARRIOR = 'n00I'
   constant integer MG_SNARECASTER = 'n00J'
   constant integer MG_MARAUDER = 'n00K'
   constant integer MG_SHADOWCASTER = 'n00L'
   constant integer ZARJIRA = 'N00A'

   player User = PlayerEx(1)
   player Trolls = PlayerEx(16)
     
   boolean AttackUser = false
   boolean AttackTrolls = false
   boolean AttackSuicide = false
 
   constant integer COMMAND_ATTACK_TROLLS = 2
   constant integer DATA_ATTACK_TROLLS_ENABLE = 1
   constant integer DATA_ATTACK_TROLLS_DISABLE = 0
 
   constant integer COMMAND_ATTACK_USER = 1
   constant integer DATA_ATTACK_USER_ENABLE = 1
   constant integer DATA_ATTACK_USER_DISABLE = 0
 
   constant integer COMMAND_ATTACK_SUICIDE = 3
   constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
   constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
 
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

   call SetBuildUnitEx(1, 1, 1, NAGA_SLAVE)
   call SetBuildUnitEx(1, 1, 1, NAGA_TEMPLE)
   call SetBuildUnitEx(8, 8, 8, NAGA_SLAVE)
   call SetBuildUnitEx(1, 1, 1, NAGA_SPAWNING)
   call SetBuildUnitEx(2, 2, 2, NAGA_CORAL)
   call SetBuildUnitEx(1, 1, 1, NAGA_ALTAR)
   call SetBuildUnitEx(2, 2, 3, NAGA_CORAL)
   call SetBuildUnitEx(0, 1, 1, NAGA_SHRINE)
   call SetBuildUnitEx(0, 3, 4, NAGA_CORAL)
   call SetBuildUnitEx(0, 0, 2, NAGA_GUARDIAN)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

   // Zar'jira should always be alive to be ready to fight
   call CampaignDefenderEx(1, 1, 1, ZARJIRA)

endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

   call Sleep(M4)

   call SetBuildUpgrEx(1, 1, 1, UPG_NAGA_ATTACK)
 
   call Sleep(M3)
 
   call SetBuildUpgrEx(1, 1, 1, UPG_NAGA_ARMOR)
 
   call Sleep(M3)
 
   call SetBuildUpgrEx(0, 1, 1, UPG_NAGA_ABOLISH)
 
   call Sleep(M3)
 
   call SetBuildUpgrEx(1, 2, 2, UPG_NAGA_ATTACK)
 
   call Sleep(M3)
 
   call SetBuildUpgrEx(1, 2, 2, UPG_NAGA_ARMOR)
 
   call Sleep(M3)
 
   call SetBuildUpgrEx(1, 2, 3, UPG_NAGA_ATTACK)
 
   call Sleep(M3)
 
   call SetBuildUpgrEx(1, 2, 3, UPG_NAGA_ARMOR)
     
endfunction

// ATTACK LOOP
//------------------------------------------------
// Note: attack times are the same across all difficulties because
// players have to face attacks at same time no matter the difficulty chosen
function AttackWavesAttackUser takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide
 
   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
       call CampaignAttackerEx(2, 2, 2, NAGA_REAVER)
       call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 1, 1, MG_MARAUDER)
       call CampaignAttackerEx(1, 1, 1, NAGA_COUATL)
       call SuicideOnPlayerEx(M3, M3, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(4, 4, 4, NAGA_REAVER)
       call CampaignAttackerEx(1, 2, 2, NAGA_SNAP_DRAGON)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call CampaignAttackerEx(1, 1, 2, MG_SNARECASTER)
       call CampaignAttackerEx(1, 1, 1, NAGA_TURTLE)
       call SuicideOnPlayerEx(M3, M3, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(3, 3, 4, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 2, 2, MG_SHADOWCASTER)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call CampaignAttackerEx(1, 1, 1, NAGA_TURTLE)
       call CampaignAttackerEx(1, 1, 1, ZARJIRA)
       call SuicideOnPlayerEx(M3, M3, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 4 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 3, NAGA_COUATL)
       call SuicideOnPlayerEx(M2, M2, M2, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 5 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
       call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
       call CampaignAttackerEx(2, 2, 2, NAGA_REAVER)
       call CampaignAttackerEx(1, 1, 1, NAGA_SNAP_DRAGON)
       call CampaignAttackerEx(1, 1, 1, ZARJIRA)
       call SuicideOnPlayerEx(M3, M3, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       loop
         
           // Just wait until suicide is called, no need to further attack the User
           call Sleep(10)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide

   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
       call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
       call SuicideOnPlayerEx(M4, M4, M4, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       loop

           // *** WAVE 2 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 4, NAGA_COUATL)
           call SuicideOnPlayerEx(M5, M5, M5, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 3 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 4, NAGA_SNAP_DRAGON)
           call SuicideOnPlayerEx(M4, M4, M4, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

function AttackWavesAttackAll takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide
 
   loop

       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
       call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
       call CampaignAttackerEx(2, 2, 2, NAGA_REAVER)
       call CampaignAttackerEx(1, 1, 1, MG_MARAUDER)
       call SuicideOnPlayerEx(M3, M3, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(4, 4, 4, NAGA_REAVER)
       call CampaignAttackerEx(1, 2, 2, NAGA_SNAP_DRAGON)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call CampaignAttackerEx(1, 1, 2, MG_SNARECASTER)
       call CampaignAttackerEx(1, 1, 1, NAGA_TURTLE)
       call SuicideOnPlayerEx(M3, M3, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(3, 3, 4, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 2, 2, MG_SHADOWCASTER)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call CampaignAttackerEx(1, 1, 1, NAGA_TURTLE)
       call CampaignAttackerEx(1, 1, 1, ZARJIRA)
       call SuicideOnPlayerEx(M3, M3, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 4 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
       call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
       call SuicideOnPlayerEx(M1, M1, M1, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 5 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(3, 4, 5, NAGA_COUATL)
       call SuicideOnPlayerEx(M1, M1, M1, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 6 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
       call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
       call CampaignAttackerEx(2, 2, 2, NAGA_REAVER)
       call CampaignAttackerEx(1, 1, 1, NAGA_SNAP_DRAGON)
       call CampaignAttackerEx(1, 1, 1, ZARJIRA)
       call SuicideOnPlayerEx(M3, M3, M3, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
 
       loop

           // Just wait until suicide is called, no need to further attack the User
           call Sleep(10)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

// Note: the suicide attack is always only on the user
function AttackWavesAttackSuicide takes nothing returns nothing
   local boolean AttackWaveAttackSuicide = AttackSuicide
   local integer UserId = GetPlayerId(User)
 
   // Reset the assault group
   call InitAssaultGroup()
 
   // Launch attack continuously
   loop
     
       call SuicideUnitEx(1, MG_CLIFFRUNNER, UserId)
       call SuicideUnitEx(1, MG_TIDEWARRIOR, UserId)
       call SuicideUnitEx(1, NAGA_REAVER, UserId)
       call SuicideUnitEx(1, MG_BLOODGILL, UserId)
       call SuicideUnitEx(1, MG_SNARECASTER, UserId)
       call SuicideUnitEx(1, MG_SHADOWCASTER, UserId)
       call SuicideUnitEx(1, MG_MARAUDER, UserId)
       call SuicideUnitEx(1, NAGA_COUATL, UserId)
       call SuicideUnitEx(1, NAGA_SNAP_DRAGON, UserId)
       call SuicideUnitEx(1, NAGA_TURTLE, UserId)
       call SuicideUnitEx(1, ZARJIRA, UserId)
     
       call Sleep(2)
       exitwhen AttackSuicide != AttackWaveAttackSuicide
 
   endloop
 
endfunction

function AttackLoop takes nothing returns nothing

    loop
     
       // If suicide is set, execute the attack suicide loop
       if AttackSuicide then
           call AttackWavesAttackSuicide()
       else
           // Attack the user and/or the trolls depending on the condition
           if AttackUser and AttackTrolls then
               call AttackWavesAttackAll()
           else
               if AttackUser then
                   call AttackWavesAttackUser()
               endif
               if AttackTrolls then
                   call AttackWavesAttackTrolls()
               endif
           endif
       endif
       
        // Wait a little time before looping again
        call Sleep(10)
 
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
    if CommandsWaiting() > 0 then   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       // Update attack suicide behaviour command (to start/stop suicide, always against the user)
       if Command == COMMAND_ATTACK_SUICIDE then
           if Data == DATA_ATTACK_SUICIDE_ENABLE then
               set AttackSuicide = true
           elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
               set AttackSuicide = false
           else
               call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack suicide command data received!")
           endif
       // Update attack trolls behaviour command (to start/stop attack against the trolls)
       elseif Command == COMMAND_ATTACK_TROLLS then
           if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack trolls command data received!")
            endif 
       // Update attack user behaviour command (to start/stop attack against the user)     
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack user command data received!")
            endif   
        else
            call DisplayTextToPlayer(User, 0, 0, "AI Warning: Unknown command received!")
        endif
    endif
 
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5) 
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
 
endfunction

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

   call CampaignAI(NAGA_CORAL, null)
 
    // Define AI properties 
   call DoCampaignFarms(false)
   call GroupTimedLife(true)
   call SetAmphibious()
 
   // Define build order
   call SetBuildOrder()
 
   // Start to research upgrades at timed intervals
   call StartThread(function ResearchUpgrades)
 
   // Define defenders
   call SetDefenders()
 
    // Start command loop for external signals 
    call StartThread(function CommandLoop)
 
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
     
endfunction



JASS:
//============================================================================
//  RR Prologue 05 -- Underworld Minions Mint "Nagas" -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//============================================================================

// GLOBALS
//------------------------------------------------
globals

   constant integer MG_CLIFFRUNNER = 'n009'
   constant integer MG_BLOODGILL = 'n00H'
   constant integer MG_TIDEWARRIOR = 'n00I'
   constant integer MC_TIDERUNNER = 'n00G'
   constant integer MC_HUNTSMAN = 'n007'
   constant integer MC_NIGHTCRAWLER = 'n008'

   player User = PlayerEx(1)
   player Trolls = PlayerEx(16)
     
   boolean AttackUser = false
   boolean AttackTrolls = false
   boolean AttackSuicide = false
 
   constant integer COMMAND_ATTACK_TROLLS = 2
   constant integer DATA_ATTACK_TROLLS_ENABLE = 1
   constant integer DATA_ATTACK_TROLLS_DISABLE = 0
 
   constant integer COMMAND_ATTACK_USER = 1
   constant integer DATA_ATTACK_USER_ENABLE = 1
   constant integer DATA_ATTACK_USER_DISABLE = 0
 
   constant integer COMMAND_ATTACK_SUICIDE = 3
   constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
   constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
 
endglobals

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide
 
   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
       call CampaignAttackerEx(1, 1, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
 
       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, 45, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 2, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
 
       loop
     
           // *** WAVE 4 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 2, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 5 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide

   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
       call SuicideOnPlayerEx(M3, M3, M2, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       loop
     
           // *** WAVE 2 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M3, M2, M2, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 3 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M3, M2, M2, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

function AttackWavesAttackAll takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide
 
   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
       call CampaignAttackerEx(1, 1, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
       call SuicideOnPlayerEx(M1, 30, 30, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 4 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, 30, 30, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 5 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 2, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
 
       loop
     
           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 7 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 8 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M1, 30, 30, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 9 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 10 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call SuicideOnPlayerEx(M1, 30, 30, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

// Note: the suicide attack is always only on the user
function AttackWavesAttackSuicide takes nothing returns nothing
   local boolean AttackWaveAttackSuicide = AttackSuicide
   local integer UserId = GetPlayerId(User)
 
   // Reset the assault group
   call InitAssaultGroup()
 
   // Launch attack continuously
   loop
     
       call SuicideUnitEx(1, MC_TIDERUNNER, UserId)
       call SuicideUnitEx(1, MC_HUNTSMAN, UserId)
       call SuicideUnitEx(1, MC_NIGHTCRAWLER, UserId)
       call SuicideUnitEx(1, MG_CLIFFRUNNER, UserId)
       call SuicideUnitEx(1, MG_TIDEWARRIOR, UserId)
       call SuicideUnitEx(1, MG_BLOODGILL, UserId)
     
       call Sleep(2)
       exitwhen AttackSuicide != AttackWaveAttackSuicide
 
   endloop
 
endfunction

function AttackLoop takes nothing returns nothing

    loop
     
       // If suicide is set, execute the attack suicide loop
       if AttackSuicide then
           call AttackWavesAttackSuicide()
       else
           // Attack the user and/or the trolls depending on the condition
           if AttackUser and AttackTrolls then
               call AttackWavesAttackAll()
           else
               if AttackUser then
                   call AttackWavesAttackUser()
               endif
               if AttackTrolls then
                   call AttackWavesAttackTrolls()
               endif
           endif
       endif
       
        // Wait a little time before looping again
        call Sleep(10)
 
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
    if CommandsWaiting() > 0 then   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       // Update attack suicide behaviour command (to start/stop suicide, always against the user)
       if Command == COMMAND_ATTACK_SUICIDE then
           if Data == DATA_ATTACK_SUICIDE_ENABLE then
               set AttackSuicide = true
           elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
               set AttackSuicide = false
           else
               call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack suicide command data received!")
           endif
       // Update attack trolls behaviour command (to start/stop attack against the trolls)
       elseif Command == COMMAND_ATTACK_TROLLS then
           if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack trolls command data received!")
            endif 
       // Update attack user behaviour command (to start/stop attack against the user)     
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack user command data received!")
            endif   
        else
            call DisplayTextToPlayer(User, 0, 0, "AI Warning: Unknown command received!")
        endif
    endif
 
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5) 
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
 
endfunction

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

    call CampaignAI(NAGA_CORAL, null)
 
    // Define AI properties 
   call DoCampaignFarms(false)
   call GroupTimedLife(true)
   call SetAmphibious()
 
    // Start command loop for external signals 
    call StartThread(function CommandLoop)
 
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
     
endfunction



JASS:
//============================================================================
//  RR Prologue 05 -- Underworld Minions Peach "Nagas" -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//============================================================================

// GLOBALS
//------------------------------------------------
globals

   constant integer MG_CLIFFRUNNER = 'n009'
   constant integer MG_BLOODGILL = 'n00H'
   constant integer MG_TIDEWARRIOR = 'n00I'
   constant integer MC_TIDERUNNER = 'n00G'
   constant integer MC_HUNTSMAN = 'n007'
   constant integer MC_NIGHTCRAWLER = 'n008'

   player User = PlayerEx(1)
   player Trolls = PlayerEx(16)
     
   boolean AttackUser = false
   boolean AttackTrolls = false
   boolean AttackSuicide = false
 
   constant integer COMMAND_ATTACK_TROLLS = 2
   constant integer DATA_ATTACK_TROLLS_ENABLE = 1
   constant integer DATA_ATTACK_TROLLS_DISABLE = 0
 
   constant integer COMMAND_ATTACK_USER = 1
   constant integer DATA_ATTACK_USER_ENABLE = 1
   constant integer DATA_ATTACK_USER_DISABLE = 0
 
   constant integer COMMAND_ATTACK_SUICIDE = 3
   constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
   constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
 
endglobals

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
   local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide
 
   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, 45, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
       call CampaignAttackerEx(1, 1, 1, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 2, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, 45, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
 
       loop

           // *** WAVE 4 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 5 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
   local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide

   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M3, M2, M2, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       loop

           // *** WAVE 2 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call SuicideOnPlayerEx(M3, M3, M2, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 3 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M3, M2, M2, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

function AttackWavesAttackAll takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
   local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide
 
   loop

       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, 45, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, M1, 30, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
       call CampaignAttackerEx(1, 1, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, 45, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 4 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 2, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
 
       loop

           // *** WAVE 5 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call SuicideOnPlayerEx(M1, M1, 30, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 7 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 8 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 9 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M1, 30, 30, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

// Note: the suicide attack is always only on the user
function AttackWavesAttackSuicide takes nothing returns nothing
   local boolean AttackWaveAttackSuicide = AttackSuicide
   local integer UserId = GetPlayerId(User)
 
   // Reset the assault group
   call InitAssaultGroup()
 
   // Launch attack continuously
   loop
     
       call SuicideUnitEx(1, MC_TIDERUNNER, UserId)
       call SuicideUnitEx(1, MC_HUNTSMAN, UserId)
       call SuicideUnitEx(1, MC_NIGHTCRAWLER, UserId)
       call SuicideUnitEx(1, MG_CLIFFRUNNER, UserId)
       call SuicideUnitEx(1, MG_TIDEWARRIOR, UserId)
       call SuicideUnitEx(1, MG_BLOODGILL, UserId)
     
       call Sleep(2)
       exitwhen AttackSuicide != AttackWaveAttackSuicide
 
   endloop
 
endfunction

function AttackLoop takes nothing returns nothing

    loop
     
       // If suicide is set, execute the attack suicide loop
       if AttackSuicide then
           call AttackWavesAttackSuicide()
       else
           // Attack the user and/or the trolls depending on the condition
           if AttackUser and AttackTrolls then
               call AttackWavesAttackAll()
           else
               if AttackUser then
                   call AttackWavesAttackUser()
               endif
               if AttackTrolls then
                   call AttackWavesAttackTrolls()
               endif
           endif
       endif
       
        // Wait a little time before looping again
        call Sleep(10)
 
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
    if CommandsWaiting() > 0 then   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       // Update attack suicide behaviour command (to start/stop suicide, always against the user)
       if Command == COMMAND_ATTACK_SUICIDE then
           if Data == DATA_ATTACK_SUICIDE_ENABLE then
               set AttackSuicide = true
           elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
               set AttackSuicide = false
           else
               call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack suicide command data received!")
           endif
       // Update attack trolls behaviour command (to start/stop attack against the trolls)
       elseif Command == COMMAND_ATTACK_TROLLS then
           if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack trolls command data received!")
            endif 
       // Update attack user behaviour command (to start/stop attack against the user)     
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack user command data received!")
            endif   
        else
            call DisplayTextToPlayer(User, 0, 0, "AI Warning: Unknown command received!")
        endif
    endif
 
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5) 
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
 
endfunction

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

    call CampaignAI(NAGA_CORAL, null)
 
    // Define AI properties 
   call DoCampaignFarms(false)
   call GroupTimedLife(true)
   call SetAmphibious()
 
    // Start command loop for external signals 
    call StartThread(function CommandLoop)
 
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
     
endfunction



JASS:
//============================================================================
//  RR Prologue 05 -- Underworld Minions Wheat "Nagas" -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//============================================================================

// GLOBALS
//------------------------------------------------
globals

   constant integer MG_CLIFFRUNNER = 'n009'
   constant integer MG_BLOODGILL = 'n00H'
   constant integer MG_TIDEWARRIOR = 'n00I'
   constant integer MC_TIDERUNNER = 'n00G'
   constant integer MC_HUNTSMAN = 'n007'
   constant integer MC_NIGHTCRAWLER = 'n008'

   player User = PlayerEx(1)
   player Trolls = PlayerEx(16)
     
   boolean AttackUser = false
   boolean AttackTrolls = false
   boolean AttackSuicide = false
 
   constant integer COMMAND_ATTACK_TROLLS = 2
   constant integer DATA_ATTACK_TROLLS_ENABLE = 1
   constant integer DATA_ATTACK_TROLLS_DISABLE = 0
 
   constant integer COMMAND_ATTACK_USER = 1
   constant integer DATA_ATTACK_USER_ENABLE = 1
   constant integer DATA_ATTACK_USER_DISABLE = 0
 
   constant integer COMMAND_ATTACK_SUICIDE = 3
   constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
   constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
 
endglobals

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
   local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide
 
   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
       call CampaignAttackerEx(0, 1, 1, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, 45, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 3, 3, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 1, MC_HUNTSMAN)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
       call CampaignAttackerEx(1, 1, 1, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
       call SuicideOnPlayerEx(M1, 45, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
 
       loop

           // *** WAVE 4 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(0, 1, 1, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 5 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 3, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 1, 2, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 1, 1, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
           call CampaignAttackerEx(1, 1, 1, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
   local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide

   loop
 
       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M3, M2, M2, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       loop

           // *** WAVE 2 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call SuicideOnPlayerEx(M3, M3, M2, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 3 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M3, M2, M2, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

function AttackWavesAttackAll takes nothing returns nothing
   local boolean AttackWaveAttackUser = AttackUser
   local boolean AttackWaveAttackTrolls = AttackTrolls
   local boolean AttackWaveAttackSuicide = AttackSuicide
 
   loop

       // *** WAVE 1 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
       call CampaignAttackerEx(0, 1, 1, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, 45, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 2 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 3, 3, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 2, MC_TIDERUNNER)
       call CampaignAttackerEx(1, 1, 1, MC_HUNTSMAN)
       call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
       // *** WAVE 3 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
       call SuicideOnPlayerEx(M1, 30, 30, Trolls)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       // *** WAVE 4 ***
       call InitAssaultGroup()
       call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
       call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
       call CampaignAttackerEx(1, 1, 1, MG_TIDEWARRIOR)
       call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
       call SuicideOnPlayerEx(M1, M1, 45, User)
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
 
       loop
     
           // *** WAVE 5 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
           call SuicideOnPlayerEx(M1, 30, 30, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 6 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
           call CampaignAttackerEx(0, 1, 1, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 7 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 3, 3, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 1, 2, MC_TIDERUNNER)
           call CampaignAttackerEx(1, 1, 1, MC_HUNTSMAN)
           call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
           call SuicideOnPlayerEx(M1, 45, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

           // *** WAVE 8 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
           call CampaignAttackerEx(1, 1, 1, MG_TIDEWARRIOR)
           call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
           call SuicideOnPlayerEx(M1, M1, 45, User)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
         
           // *** WAVE 9 ***
           call InitAssaultGroup()
           call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
           call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
           call SuicideOnPlayerEx(M1, 30, 30, Trolls)
           exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

       endloop
     
       // Make sure to leave both loops
       exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
     
   endloop
 
endfunction

// Note: the suicide attack is always only on the user
function AttackWavesAttackSuicide takes nothing returns nothing
   local boolean AttackWaveAttackSuicide = AttackSuicide
   local integer UserId = GetPlayerId(User)
 
   // Reset the assault group
   call InitAssaultGroup()
 
   // Launch attack continuously
   loop
     
       call SuicideUnitEx(1, MC_TIDERUNNER, UserId)
       call SuicideUnitEx(1, MC_HUNTSMAN, UserId)
       call SuicideUnitEx(1, MC_NIGHTCRAWLER, UserId)
       call SuicideUnitEx(1, MG_CLIFFRUNNER, UserId)
       call SuicideUnitEx(1, MG_TIDEWARRIOR, UserId)
       call SuicideUnitEx(1, MG_BLOODGILL, UserId)
     
       call Sleep(2)
       exitwhen AttackSuicide != AttackWaveAttackSuicide
 
   endloop
 
endfunction

function AttackLoop takes nothing returns nothing

    loop
     
       // If suicide is set, execute the attack suicide loop
       if AttackSuicide then
           call AttackWavesAttackSuicide()
       else
           // Attack the user and/or the trolls depending on the condition
           if AttackUser and AttackTrolls then
               call AttackWavesAttackAll()
           else
               if AttackUser then
                   call AttackWavesAttackUser()
               endif
               if AttackTrolls then
                   call AttackWavesAttackTrolls()
               endif
           endif
       endif
       
        // Wait a little time before looping again
        call Sleep(10)
 
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
    if CommandsWaiting() > 0 then   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       // Update attack suicide behaviour command (to start/stop suicide, always against the user)
       if Command == COMMAND_ATTACK_SUICIDE then
           if Data == DATA_ATTACK_SUICIDE_ENABLE then
               set AttackSuicide = true
           elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
               set AttackSuicide = false
           else
               call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack suicide command data received!")
           endif
       // Update attack trolls behaviour command (to start/stop attack against the trolls)
       elseif Command == COMMAND_ATTACK_TROLLS then
           if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack trolls command data received!")
            endif 
       // Update attack user behaviour command (to start/stop attack against the user)     
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call DisplayTextToPlayer(User, 0, 0, "AI Warning: Invalid attack user command data received!")
            endif   
        else
            call DisplayTextToPlayer(User, 0, 0, "AI Warning: Unknown command received!")
        endif
    endif
 
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5) 
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
 
endfunction

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

    call CampaignAI(NAGA_CORAL, null)
 
    // Define AI properties 
   call DoCampaignFarms(false)
   call GroupTimedLife(true)
   call SetAmphibious()
 
    // Start command loop for external signals 
    call StartThread(function CommandLoop)
 
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
     
endfunction
 
Last edited:
Re-sharing all the scripts I've already shared. These new versions all require JASS AI 2.0.


JASS:
//============================================================================
//  RR Prologue 02 -- Alliance Blue Human -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals

    // Players

    player User = PlayerEx(1)
   
    // Behaviour
   
    constant real ATTACK_WAVE_START_X = -2700.0
    constant real ATTACK_WAVE_START_Y = -4700.0
       
    boolean UseRiflemen = false
    boolean AttackEnabled = false

    // Commands
   
    constant integer COMMAND_RIFLEMEN = 1
    constant integer DATA_RIFLEMEN_ENABLE = 1
    constant integer DATA_RIFLEMEN_DISABLE = 0
   
    constant integer COMMAND_ATTACK = 2
    constant integer DATA_ATTACK_ENABLE = 1
    constant integer DATA_ATTACK_DISABLE = 0
   
    constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
   
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnit(3, PEASANT)

endfunction
   
// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

    if UseRiflemen then
        call CampaignDefenderEx(1, 2, 3, FOOTMAN)   
        call CampaignDefenderEx(0, 1, 2, RIFLEMAN)
    else
        call CampaignDefenderEx(1, 2, 3, FOOTMAN)
    endif

endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesWithRiflemen takes nothing returns nothing
    local boolean AttackWaveUseRiflemen = UseRiflemen
   
    call AdvDebugDisplayToPlayer("AI Info: attacking with riflemen...")
   
    loop

        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 4, FOOTMAN)
        call AdvSuicideOnPlayerEx(M6, M5, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveUseRiflemen != UseRiflemen

        // *** WAVE 2 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 4, RIFLEMAN)
        call AdvSuicideOnPlayerEx(M6, M5, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveUseRiflemen != UseRiflemen
       
        // *** WAVE 3 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, FOOTMAN)
        call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
        call AdvSuicideOnPlayerEx(M6, M5, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveUseRiflemen != UseRiflemen

    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking with riflemen!")
   
endfunction

function AttackWavesWithoutRiflemen takes nothing returns nothing
    local boolean AttackWaveUseRiflemen = UseRiflemen
   
    call AdvDebugDisplayToPlayer("AI Info: attacking without riflemen...")

    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 4, FOOTMAN)
        call AdvSuicideOnPlayerEx(M6, M5, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveUseRiflemen != UseRiflemen
    
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking without riflemen!")
   
endfunction

function AttackLoop takes nothing returns nothing

    loop
       
        // Attack only if attack is enabled
        if AttackEnabled then
            // Attack with riflemen or not depending on the current condition
            if UseRiflemen then
                call AttackWavesWithRiflemen()           
            else
                call AttackWavesWithoutRiflemen()
            endif
        endif
       
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0
     
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand() 
               
        // We set the suicide flag to disable all attack routines upon defeat
        if Command == COMMAND_DEFEAT then
            if Data == DATA_DEFEAT_ENABLE then
                call AdvSetSuicide(true)
            elseif Data == DATA_DEFEAT_DISABLE then
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
            endif
       
        // Update rifleman behaviour command (to simulate having a blacksmith)
        elseif Command == COMMAND_RIFLEMEN then        
            if Data == DATA_RIFLEMEN_ENABLE then
                set UseRiflemen = true
            elseif Data == DATA_RIFLEMEN_DISABLE then
                set UseRiflemen = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid riflemen command data received!")
            endif     
            // Reset build order and defenders
            call InitBuildArray()
            call InitDefenseGroup()
            call SetBuildOrder()        
            call SetDefenders()
           
        // Update attack behaviour command (to start/stop attack waves)
        elseif Command == COMMAND_ATTACK then
            if Data == DATA_ATTACK_ENABLE then
                set AttackEnabled = true
            elseif Data == DATA_ATTACK_DISABLE then
                set AttackEnabled = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack command data received!")
            endif  
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
           
        endif

    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction
    
// MAIN
//------------------------------------------------
function main takes nothing returns nothing

    call CampaignAI(LUMBER_MILL, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call SetReplacements(0, 1, 2)   
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetSearchPreferredLocations(false)
   
    call AdvSetContinueAttackReducePercentage(10)

    call AdvSetAttackWaveGatherReturnXY(-2000.0, -4500.0)
    call AdvSetDefenseCaptainHomeXY(-3450.0, -3150.0)
    call AdvSetCaptainHome(DEFENSE_CAPTAIN)
   
    // Define build order
    call SetBuildOrder()
   
    // Define defenders
    call SetDefenders()
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction



JASS:
//============================================================================
//  RR Prologue 03 -- Kul Tiras Dark Green Human -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals

    // Tech tree

    constant integer KELEN = 'Haah'
    constant integer HYDROMANCER = 'h000'
    constant integer UPG_HYDROMANCY = 'R000'
   
    // Players

    player User = PlayerEx(1)
    player Trolls = PlayerEx(16)
   
    // Behaviour
   
    constant real ATTACK_WAVE_START_X = -3800.0
    constant real ATTACK_WAVE_START_Y = 3500.0
       
    boolean AttackUser = false
    boolean AttackTrolls = false
   
    // Commands
   
    constant integer COMMAND_ATTACK_TROLLS = 2
    constant integer DATA_ATTACK_TROLLS_ENABLE = 1
    constant integer DATA_ATTACK_TROLLS_DISABLE = 0
   
    constant integer COMMAND_ATTACK_USER = 1
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
   
    constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
   
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, PEASANT)
    call SetBuildUnitEx(1, 1, 1, TOWN_HALL)
    call SetBuildUnitEx(8, 8, 8, PEASANT)
    call SetBuildUnitEx(1, 1, 1, HUMAN_ALTAR)
    call SetBuildUnitEx(2, 2, 2, HOUSE)
    call SetBuildUnitEx(1, 1, 1, BARRACKS)
    call SetBuildUnitEx(4, 4, 4, HOUSE)
    call SetBuildUnitEx(0, 1, 1, LUMBER_MILL)
    call SetBuildUnitEx(0, 1, 1, BLACKSMITH)
    call SetBuildUnitEx(0, 2, 3, HOUSE)
    call SetBuildUnitEx(0, 0, 1, KEEP)
    call SetBuildUnitEx(0, 0, 1, ARCANE_SANCTUM)
    call SetBuildUnitEx(0, 0, 2, HOUSE)
    call SetBuildUnitEx(0, 0, 1, ARCANE_VAULT)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

    call CampaignDefenderEx(1, 2, 2, FOOTMAN)   
    call CampaignDefenderEx(1, 1, 2, RIFLEMAN)
    call CampaignDefenderEx(0, 0, 1, HYDROMANCER)
    call CampaignDefenderEx(1, 1, 1, KELEN)

endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

    call Sleep(M5)

    call SetBuildUpgrEx(1, 1, 1, UPG_DEFEND)
   
    call Sleep(M4)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_MELEE)
   
    call Sleep(M4)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_ARMOR)
    call SetBuildUpgrEx(0, 1, 1, UPG_HYDROMANCY)
       
    call Sleep(M4)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_RANGED)
   
    call Sleep(M4)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_LEATHER)
    call SetBuildUpgrEx(0, 1, 1, UPG_GUN_RANGE)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 0, 1, UPG_MASONRY)
   
    call Sleep(M4)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_MELEE)
   
    call Sleep(M5)
   
    call SetBuildUpgrEx(1, 1, 2, UPG_ARMOR)
       
    call Sleep(M4)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_RANGED)
   
    call Sleep(M5)
   
    call SetBuildUpgrEx(1, 1, 2, UPG_LEATHER)

endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   
    call AdvDebugDisplayToPlayer("AI Info: attacking User...")
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetContinueAttackReducePercentage(10)
   
    loop
   
        // *** WAVE 1 ***
        call AdvSetPrioritizeTownHalls(true)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 3, FOOTMAN)
        call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
        call AdvSuicideOnPlayerEx(M4, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

        // *** WAVE 2 ***
        call AdvSetPrioritizeTownHalls(true)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 2, FOOTMAN)
        call CampaignAttackerEx(1, 2, 2, RIFLEMAN)
        call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
        call CampaignAttackerEx(1, 1, 1, KELEN)
        call AdvSuicideOnPlayerEx(M5, M4, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

        // *** WAVE 3 ***
        call AdvSetPrioritizeTownHalls(false)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, FOOTMAN)
        call CampaignAttackerEx(0, 1, 1, RIFLEMAN)
        call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
        call AdvSuicideOnPlayerEx(M4, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
   
        loop

            // *** WAVE 4 ***
            call AdvSetPrioritizeTownHalls(true)
            call InitAssaultGroup()
            call CampaignAttackerEx(3, 4, 5, FOOTMAN)
            call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 1, KELEN)
            call AdvSuicideOnPlayerEx(M5, M4, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

            // *** WAVE 5 ***
            call AdvSetPrioritizeTownHalls(false)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 3, FOOTMAN)
            call CampaignAttackerEx(2, 2, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
            call AdvSuicideOnPlayerEx(M4, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)       
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
           
            // *** WAVE 6 ***
            call AdvSetPrioritizeTownHalls(true)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 3, FOOTMAN)
            call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
            call CampaignAttackerEx(1, 1, 1, KELEN)
            call AdvSuicideOnPlayerEx(M5, M4, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
   
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   
    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls...")
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetContinueAttackReducePercentageIfFarAway(false)

    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 3, FOOTMAN)
        call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
        call AdvSuicideOnPlayerEx(M4, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

        // *** WAVE 2 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 2, FOOTMAN)
        call CampaignAttackerEx(1, 2, 2, RIFLEMAN)
        call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
        call CampaignAttackerEx(1, 1, 1, KELEN)
        call AdvSuicideOnPlayerEx(M5, M4, M3, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
       
        // *** WAVE 3 ***)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, FOOTMAN)
        call CampaignAttackerEx(0, 1, 1, RIFLEMAN)
        call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
        call AdvSuicideOnPlayerEx(M4, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
       
        loop

            // *** WAVE 4 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(3, 4, 5, FOOTMAN)
            call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 1, KELEN)
            call AdvSuicideOnPlayerEx(M5, M4, M3, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

            // *** WAVE 5 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 3, FOOTMAN)
            call CampaignAttackerEx(2, 2, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
            call AdvSuicideOnPlayerEx(M4, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
           
            // *** WAVE 6 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 3, FOOTMAN)
            call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
            call CampaignAttackerEx(1, 1, 1, KELEN)
            call AdvSuicideOnPlayerEx(M5, M4, M3, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls!")
   
endfunction

function AttackWavesAttackAll takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
   
    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls and User...")
   
    loop

        // *** WAVE 1 ***
        call AdvSetPrioritizeTownHalls(false)
        call AdvSetContinueAttackReducePercentageIfFarAway(false)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 3, FOOTMAN)
        call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
        call AdvSuicideOnPlayerEx(M4, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

        // *** WAVE 2 ***
        call AdvSetPrioritizeTownHalls(true)
        call AdvSetContinueAttackReducePercentageIfFarAway(true)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 2, FOOTMAN)
        call CampaignAttackerEx(1, 2, 2, RIFLEMAN)
        call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
        call CampaignAttackerEx(1, 1, 1, KELEN)
        call AdvSuicideOnPlayerEx(M5, M4, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
       
        // *** WAVE 3 ***
        call AdvSetPrioritizeTownHalls(false)
        call AdvSetContinueAttackReducePercentageIfFarAway(false)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, FOOTMAN)
        call CampaignAttackerEx(0, 1, 1, RIFLEMAN)
        call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
        call AdvSuicideOnPlayerEx(M4, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
       
        loop

            // *** WAVE 4 ***
            call AdvSetPrioritizeTownHalls(true)
            call AdvSetContinueAttackReducePercentageIfFarAway(true)
            call InitAssaultGroup()
            call CampaignAttackerEx(3, 4, 5, FOOTMAN)
            call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 1, KELEN)
            call AdvSuicideOnPlayerEx(M5, M4, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

            // *** WAVE 5 ***
            call AdvSetPrioritizeTownHalls(false)
            call AdvSetContinueAttackReducePercentageIfFarAway(false)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 3, FOOTMAN)
            call CampaignAttackerEx(2, 2, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 2, HYDROMANCER)
            call AdvSuicideOnPlayerEx(M4, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
           
            // *** WAVE 6 ***
            call AdvSetPrioritizeTownHalls(true)
            call AdvSetContinueAttackReducePercentageIfFarAway(true)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 3, FOOTMAN)
            call CampaignAttackerEx(1, 1, 2, RIFLEMAN)
            call CampaignAttackerEx(1, 1, 1, HYDROMANCER)
            call CampaignAttackerEx(1, 1, 1, KELEN)
            call AdvSuicideOnPlayerEx(M5, M4, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls and User!")
   
endfunction   

function AttackLoop takes nothing returns nothing

    loop
       
        // Attack the user and/or the trolls depending on the condition
        if AttackUser and AttackTrolls then
            call AttackWavesAttackAll()
        else
            if AttackUser then
                call AttackWavesAttackUser()
            endif
            if AttackTrolls then
                call AttackWavesAttackTrolls()
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0
       
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
                   
        // We set the suicide flag to disable all attack routines upon defeat
        if Command == COMMAND_DEFEAT then
            if Data == DATA_DEFEAT_ENABLE then
                call AdvSetSuicide(true)
            elseif Data == DATA_DEFEAT_DISABLE then
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
            endif
       
        // Update attack trolls behaviour command (to start/stop attack against the trolls)
        elseif Command == COMMAND_ATTACK_TROLLS then
            if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack trolls command data received!")
            endif  
           
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!")
            endif
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
           
        endif
       
    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(HOUSE, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call SetReplacements(1, 1, 1)
    call GroupTimedLife(true)
    call AdvSetSearchPreferredLocations(false)
   
    // Define build order
    call SetBuildOrder()
   
    // Start to research upgrades at timed intervals
    call StartThread(function ResearchUpgrades)
   
    // Define defenders
    call SetDefenders()
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction



JASS:
//============================================================================
//  RR Prologue 05 -- Spitscale Coal "Nagas" -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals

    // Tech tree

    constant integer MG_CLIFFRUNNER = 'n009'
    constant integer MG_BLOODGILL = 'n00H'
    constant integer MG_TIDEWARRIOR = 'n00I'
    constant integer MG_SNARECASTER = 'n00J'
    constant integer MG_MARAUDER = 'n00K'
    constant integer MG_SHADOWCASTER = 'n00L'
    constant integer ZARJIRA = 'N00A'
   
    // Players

    player User = PlayerEx(1)
    player Trolls = PlayerEx(16)
   
    // Behaviour
   
    constant real ATTACK_WAVE_START_X = -3800.0
    constant real ATTACK_WAVE_START_Y = 3500.0
       
    boolean AttackUser = false
    boolean AttackTrolls = false
    boolean AttackSuicide = false
   
    // Commands
   
    constant integer COMMAND_ATTACK_TROLLS = 2
    constant integer DATA_ATTACK_TROLLS_ENABLE = 1
    constant integer DATA_ATTACK_TROLLS_DISABLE = 0
   
    constant integer COMMAND_ATTACK_USER = 1
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
   
    constant integer COMMAND_ATTACK_SUICIDE = 3
    constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
    constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
   
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, NAGA_SLAVE)
    call SetBuildUnitEx(1, 1, 1, NAGA_TEMPLE)
    call SetBuildUnitEx(8, 8, 8, NAGA_SLAVE)
    call SetBuildUnitEx(1, 1, 1, NAGA_ALTAR)
    call SetBuildUnitEx(2, 2, 2, NAGA_CORAL)
    call SetBuildUnitEx(1, 1, 1, NAGA_SPAWNING)
    call SetBuildUnitEx(2, 2, 3, NAGA_CORAL)
    call SetBuildUnitEx(0, 1, 1, NAGA_SHRINE)
    call SetBuildUnitEx(0, 3, 4, NAGA_CORAL)
    call SetBuildUnitEx(0, 0, 2, NAGA_GUARDIAN)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

    // Zar'jira should always be alive to be ready to fight
    call CampaignDefenderEx(1, 1, 1, ZARJIRA)

endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

    call Sleep(M4)

    call SetBuildUpgrEx(1, 1, 1, UPG_NAGA_ATTACK)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_NAGA_ARMOR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 1, 1, UPG_NAGA_ABOLISH)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_NAGA_ATTACK)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_NAGA_ARMOR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 3, UPG_NAGA_ATTACK)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 3, UPG_NAGA_ARMOR)
       
endfunction

// ATTACK LOOP
//------------------------------------------------
// Note: attack times are the same across all difficulties because
// players have to face attacks at same time no matter the difficulty chosen
function AttackWavesAttackUser takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking User...")
    call AdvSetSearchPreferredLocations(true)
    call AdvSetContinueAttackPercentage(100)
    call AdvSetPrioritizeTownHalls(true)
   
    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
        call CampaignAttackerEx(2, 2, 2, NAGA_REAVER)
        call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 1, 1, MG_MARAUDER)
        call CampaignAttackerEx(1, 1, 1, NAGA_COUATL)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 2 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(4, 4, 4, NAGA_REAVER)
        call CampaignAttackerEx(1, 2, 2, NAGA_SNAP_DRAGON)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call CampaignAttackerEx(1, 1, 2, MG_SNARECASTER)
        call CampaignAttackerEx(1, 1, 1, NAGA_TURTLE)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 3 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(3, 3, 4, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 2, 2, MG_SHADOWCASTER)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call CampaignAttackerEx(1, 1, 1, NAGA_TURTLE)
        call CampaignAttackerEx(1, 1, 1, ZARJIRA)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 4 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 3, NAGA_COUATL)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 5 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
        call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
        call CampaignAttackerEx(2, 2, 2, NAGA_REAVER)
        call CampaignAttackerEx(1, 1, 1, NAGA_SNAP_DRAGON)
        call CampaignAttackerEx(1, 1, 1, ZARJIRA)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        loop
           
            // Just wait until suicide is called, no need to further attack the User
            call Sleep(10)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
   
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls...")
    call AdvSetSearchPreferredLocations(false)
    call AdvSetContinueAttackPercentage(0)
    call AdvSetPrioritizeTownHalls(false)

    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
        call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
        call AdvSuicideOnPlayerEx(M4, M4, M4, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        loop

            // *** WAVE 2 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 4, NAGA_COUATL)
            call AdvSuicideOnPlayerEx(M5, M5, M5, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 3 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 4, NAGA_SNAP_DRAGON)
            call AdvSuicideOnPlayerEx(M4, M4, M4, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls!")
   
endfunction

function AttackWavesAttackAll takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls and User...")
   
    loop

        // *** WAVE 1 ***
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call AdvSetPrioritizeTownHalls(true)
        call InitAssaultGroup()
        call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
        call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
        call CampaignAttackerEx(2, 2, 2, NAGA_REAVER)
        call CampaignAttackerEx(1, 1, 1, MG_MARAUDER)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 2 ***
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call AdvSetPrioritizeTownHalls(true)
        call InitAssaultGroup()
        call CampaignAttackerEx(4, 4, 4, NAGA_REAVER)
        call CampaignAttackerEx(1, 2, 2, NAGA_SNAP_DRAGON)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call CampaignAttackerEx(1, 1, 2, MG_SNARECASTER)
        call CampaignAttackerEx(1, 1, 1, NAGA_TURTLE)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 3 ***
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call AdvSetPrioritizeTownHalls(true)
        call InitAssaultGroup()
        call CampaignAttackerEx(3, 3, 4, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 2, 2, MG_SHADOWCASTER)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call CampaignAttackerEx(1, 1, 1, NAGA_TURTLE)
        call CampaignAttackerEx(1, 1, 1, ZARJIRA)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 4 ***
        call AdvSetSearchPreferredLocations(false)
        call AdvSetContinueAttackPercentage(0)
        call AdvSetPrioritizeTownHalls(false)
        call InitAssaultGroup()
        call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
        call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
        call AdvSuicideOnPlayerEx(M1, M1, M1, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 5 ***
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call AdvSetPrioritizeTownHalls(true)
        call InitAssaultGroup()
        call CampaignAttackerEx(3, 4, 5, NAGA_COUATL)
        call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 6 ***
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call AdvSetPrioritizeTownHalls(true)
        call InitAssaultGroup()
        call CampaignAttackerEx(4, 4, 5, MG_CLIFFRUNNER)
        call CampaignAttackerEx(2, 3, 3, MG_TIDEWARRIOR)
        call CampaignAttackerEx(2, 2, 2, NAGA_REAVER)
        call CampaignAttackerEx(1, 1, 1, NAGA_SNAP_DRAGON)
        call CampaignAttackerEx(1, 1, 1, ZARJIRA)
        call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
   
        loop

            // Just wait until suicide is called, no need to further attack the User
            call Sleep(10)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls and User!")
   
endfunction

// Note: the suicide attack is always only on the user
function AttackWavesAttackSuicide takes nothing returns nothing
    local boolean AttackWaveAttackSuicide = AttackSuicide
    local integer UserId = GetPlayerId(User)
   
    call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
   
    // Reset the assault group
    call InitAssaultGroup()
   
    // Launch attack continuously
    loop
       
        call SuicideUnitEx(1, MG_CLIFFRUNNER, UserId)
        call SuicideUnitEx(1, MG_TIDEWARRIOR, UserId)
        call SuicideUnitEx(1, NAGA_REAVER, UserId)
        call SuicideUnitEx(1, MG_BLOODGILL, UserId)
        call SuicideUnitEx(1, MG_SNARECASTER, UserId)
        call SuicideUnitEx(1, MG_SHADOWCASTER, UserId)
        call SuicideUnitEx(1, MG_MARAUDER, UserId)
        call SuicideUnitEx(1, NAGA_COUATL, UserId)
        call SuicideUnitEx(1, NAGA_SNAP_DRAGON, UserId)
        call SuicideUnitEx(1, NAGA_TURTLE, UserId)
        call SuicideUnitEx(1, ZARJIRA, UserId)
       
        call Sleep(2)
        exitwhen AttackSuicide != AttackWaveAttackSuicide
   
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
   
endfunction

function AttackLoop takes nothing returns nothing

    loop
       
        // If suicide is set, execute the attack suicide loop
        if AttackSuicide then
            call AttackWavesAttackSuicide()
        else
            // Attack the user and/or the trolls depending on the condition
            if AttackUser and AttackTrolls then
                call AttackWavesAttackAll()
            else
                if AttackUser then
                    call AttackWavesAttackUser()
                endif
                if AttackTrolls then
                    call AttackWavesAttackTrolls()
                endif
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
   
    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0
  
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       
        // Update attack suicide behaviour command (to start/stop suicide, always against the user)
        if Command == COMMAND_ATTACK_SUICIDE then
            if Data == DATA_ATTACK_SUICIDE_ENABLE then
                set AttackSuicide = true
                call AdvSetSuicide(true)
            elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
                set AttackSuicide = false
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
            endif
           
        // Update attack trolls behaviour command (to start/stop attack against the trolls)
        elseif Command == COMMAND_ATTACK_TROLLS then
            if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack trolls command data received!")
            endif  
           
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!")
            endif
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
           
        endif
  
    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(NAGA_CORAL, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call GroupTimedLife(true)
    call SetAmphibious()
    call AdvSetContinueAttackReducePercentageIfFarAway(false)
   
    // MAIN USER BASE
    call AdvAddPreferredLocation(-5700.0, -7800.0)
    // EXPANSION USER BASE
    call AdvAddPreferredLocation(-6550, -13800.0)
   
    // Define build order
    call SetBuildOrder()
   
    // Start to research upgrades at timed intervals
    call StartThread(function ResearchUpgrades)
   
    // Define defenders
    call SetDefenders()
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction



JASS:
//============================================================================
//  RR Prologue 05 -- Underworld Minions Mint "Nagas" -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals

    // Tech tree

    constant integer MG_CLIFFRUNNER = 'n009'
    constant integer MG_BLOODGILL = 'n00H'
    constant integer MG_TIDEWARRIOR = 'n00I'
    constant integer MC_TIDERUNNER = 'n00G'
    constant integer MC_HUNTSMAN = 'n007'
    constant integer MC_NIGHTCRAWLER = 'n008'

    // Players

    player User = PlayerEx(1)
    player Trolls = PlayerEx(16)
   
    // Behaviour
   
    constant real ATTACK_WAVE_START_X = 5500.0
    constant real ATTACK_WAVE_START_Y = -12000.0
       
    boolean AttackUser = false
    boolean AttackTrolls = false
    boolean AttackSuicide = false
   
    // Commands
   
    constant integer COMMAND_ATTACK_TROLLS = 2
    constant integer DATA_ATTACK_TROLLS_ENABLE = 1
    constant integer DATA_ATTACK_TROLLS_DISABLE = 0
   
    constant integer COMMAND_ATTACK_USER = 1
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
   
    constant integer COMMAND_ATTACK_SUICIDE = 3
    constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
    constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
   
endglobals

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking User...")
    call AdvSetPrioritizeNearest(false)
    call AdvSetSearchPreferredLocations(true)
    call AdvSetContinueAttackPercentage(100)
   
    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
        call CampaignAttackerEx(1, 1, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
   
        // *** WAVE 2 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 3 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 2, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
   
        loop
       
            // *** WAVE 4 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 2, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 5 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 6 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
   
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide

    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls...")
    call AdvSetPrioritizeNearest(true)
    call AdvSetSearchPreferredLocations(false)
    call AdvSetContinueAttackPercentage(0)

    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
        call AdvSuicideOnPlayerEx(M3, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        loop
       
            // *** WAVE 2 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M3, M2, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 3 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M3, M2, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls!")
   
endfunction

function AttackWavesAttackAll takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls and User...")
   
    loop
   
        // *** WAVE 1 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
        call CampaignAttackerEx(1, 1, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 2 ***
        call AdvSetPrioritizeNearest(true)
        call AdvSetSearchPreferredLocations(false)
        call AdvSetContinueAttackPercentage(0)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
        call AdvSuicideOnPlayerEx(M1, 30, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 3 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 4 ***
        call AdvSetPrioritizeNearest(true)
        call AdvSetSearchPreferredLocations(false)
        call AdvSetContinueAttackPercentage(0)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, 30, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 5 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 2, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
   
        loop
       
            // *** WAVE 6 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 7 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 8 ***
            call AdvSetPrioritizeNearest(true)
            call AdvSetSearchPreferredLocations(false)
            call AdvSetContinueAttackPercentage(0)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M1, 30, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 9 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 10 ***
            call AdvSetPrioritizeNearest(true)
            call AdvSetSearchPreferredLocations(false)
            call AdvSetContinueAttackPercentage(0)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call AdvSuicideOnPlayerEx(M1, 30, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls and User!")
   
endfunction

// Note: the suicide attack is always only on the user
function AttackWavesAttackSuicide takes nothing returns nothing
    local boolean AttackWaveAttackSuicide = AttackSuicide
    local integer UserId = GetPlayerId(User)
   
    // Reset the assault group
    call InitAssaultGroup()
   
    call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
   
    // Launch attack continuously
    loop
       
        call SuicideUnitEx(1, MC_TIDERUNNER, UserId)
        call SuicideUnitEx(1, MC_HUNTSMAN, UserId)
        call SuicideUnitEx(1, MC_NIGHTCRAWLER, UserId)
        call SuicideUnitEx(1, MG_CLIFFRUNNER, UserId)
        call SuicideUnitEx(1, MG_TIDEWARRIOR, UserId)
        call SuicideUnitEx(1, MG_BLOODGILL, UserId)
       
        call Sleep(2)
        exitwhen AttackSuicide != AttackWaveAttackSuicide
   
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
   
endfunction

function AttackLoop takes nothing returns nothing

    loop
       
        // If suicide is set, execute the attack suicide loop
        if AttackSuicide then
            call AttackWavesAttackSuicide()
        else
            // Attack the user and/or the trolls depending on the condition
            if AttackUser and AttackTrolls then
                call AttackWavesAttackAll()
            else
                if AttackUser then
                    call AttackWavesAttackUser()
                endif
                if AttackTrolls then
                    call AttackWavesAttackTrolls()
                endif
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0  
       
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       
        // Update attack suicide behaviour command (to start/stop suicide, always against the user)
        if Command == COMMAND_ATTACK_SUICIDE then
            if Data == DATA_ATTACK_SUICIDE_ENABLE then
                set AttackSuicide = true
                call AdvSetSuicide(true)
            elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
                set AttackSuicide = false
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
            endif
           
        // Update attack trolls behaviour command (to start/stop attack against the trolls)
        elseif Command == COMMAND_ATTACK_TROLLS then
            if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack trolls command data received!")
            endif  
           
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!")
            endif 
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
           
        endif
   
    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(NAGA_CORAL, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call GroupTimedLife(true)
    call SetAmphibious()
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetContinueAttackReducePercentageIfFarAway(false)
   
    call AdvSetAttackWaveGatherReturnXY(6500.0, -12700.0)
   
    // MAIN USER BASE RIGHT ENTRANCE
    call AdvAddPreferredLocation(-4700.0, -7600.0)
    // EXPANSION USER BASE
    call AdvAddPreferredLocation(-6550, -13800.0)
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction



JASS:
//============================================================================
//  RR Prologue 05 -- Underworld Minions Peach "Nagas" -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals

    // Tech tree

    constant integer MG_CLIFFRUNNER = 'n009'
    constant integer MG_BLOODGILL = 'n00H'
    constant integer MG_TIDEWARRIOR = 'n00I'
    constant integer MC_TIDERUNNER = 'n00G'
    constant integer MC_HUNTSMAN = 'n007'
    constant integer MC_NIGHTCRAWLER = 'n008'
   
    // Players

    player User = PlayerEx(1)
    player Trolls = PlayerEx(16)
   
    // Behaviour
   
    constant real ATTACK_WAVE_START_X = -10000.0
    constant real ATTACK_WAVE_START_Y = -4700.0
       
    boolean AttackUser = false
    boolean AttackTrolls = false
    boolean AttackSuicide = false
   
    // Commands
   
    constant integer COMMAND_ATTACK_TROLLS = 2
    constant integer DATA_ATTACK_TROLLS_ENABLE = 1
    constant integer DATA_ATTACK_TROLLS_DISABLE = 0
   
    constant integer COMMAND_ATTACK_USER = 1
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
   
    constant integer COMMAND_ATTACK_SUICIDE = 3
    constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
    constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
   
endglobals

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking User...")
    call AdvSetPrioritizeNearest(false)
    call AdvSetSearchPreferredLocations(true)
    call AdvSetContinueAttackPercentage(100)
   
    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 2 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
        call CampaignAttackerEx(1, 1, 1, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 3 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 2, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
   
        loop

            // *** WAVE 4 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 5 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 6 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
   
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide

    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls...")
    call AdvSetPrioritizeNearest(true)
    call AdvSetSearchPreferredLocations(false)
    call AdvSetContinueAttackPercentage(0)

    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M3, M2, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        loop

            // *** WAVE 2 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call AdvSuicideOnPlayerEx(M3, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 3 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M3, M2, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls!")
   
endfunction

function AttackWavesAttackAll takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls and User...")
   
    loop

        // *** WAVE 1 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 2 ***
        call AdvSetPrioritizeNearest(true)
        call AdvSetSearchPreferredLocations(false)
        call AdvSetContinueAttackPercentage(0)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, M1, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 3 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
        call CampaignAttackerEx(1, 1, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 4 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 2, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
   
        loop

            // *** WAVE 5 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 6 ***
            call AdvSetPrioritizeNearest(true)
            call AdvSetSearchPreferredLocations(false)
            call AdvSetContinueAttackPercentage(0)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call AdvSuicideOnPlayerEx(M1, M1, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 7 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 8 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 2, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 9 ***
            call AdvSetPrioritizeNearest(true)
            call AdvSetSearchPreferredLocations(false)
            call AdvSetContinueAttackPercentage(0)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M1, 30, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls and User!")
   
endfunction

// Note: the suicide attack is always only on the user
function AttackWavesAttackSuicide takes nothing returns nothing
    local boolean AttackWaveAttackSuicide = AttackSuicide
    local integer UserId = GetPlayerId(User)
   
    call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
   
    // Reset the assault group
    call InitAssaultGroup()
   
    // Launch attack continuously
    loop
       
        call SuicideUnitEx(1, MC_TIDERUNNER, UserId)
        call SuicideUnitEx(1, MC_HUNTSMAN, UserId)
        call SuicideUnitEx(1, MC_NIGHTCRAWLER, UserId)
        call SuicideUnitEx(1, MG_CLIFFRUNNER, UserId)
        call SuicideUnitEx(1, MG_TIDEWARRIOR, UserId)
        call SuicideUnitEx(1, MG_BLOODGILL, UserId)
       
        call Sleep(2)
        exitwhen AttackSuicide != AttackWaveAttackSuicide
   
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
   
endfunction

function AttackLoop takes nothing returns nothing

    loop
       
        // If suicide is set, execute the attack suicide loop
        if AttackSuicide then
            call AttackWavesAttackSuicide()
        else
            // Attack the user and/or the trolls depending on the condition
            if AttackUser and AttackTrolls then
                call AttackWavesAttackAll()
            else
                if AttackUser then
                    call AttackWavesAttackUser()
                endif
                if AttackTrolls then
                    call AttackWavesAttackTrolls()
                endif
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
   
    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0
 
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       
        // Update attack suicide behaviour command (to start/stop suicide, always against the user)
        if Command == COMMAND_ATTACK_SUICIDE then
            if Data == DATA_ATTACK_SUICIDE_ENABLE then
                set AttackSuicide = true
                call AdvSetSuicide(true)
            elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
                set AttackSuicide = false
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!") 
            endif
           
        // Update attack trolls behaviour command (to start/stop attack against the trolls)
        elseif Command == COMMAND_ATTACK_TROLLS then
            if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack trolls command data received!") 
            endif  
           
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif   
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
           
        endif
   
    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(NAGA_CORAL, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call GroupTimedLife(true)
    call SetAmphibious()
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetContinueAttackReducePercentageIfFarAway(false)
   
    call AdvSetAttackWaveGatherReturnXY(-9800.0, -3800.0)
   
    // MAIN USER BASE TOP LEFT ENTRANCE
    call AdvAddPreferredLocation(-6300, -7000.0)
    // FOUNTAIN
    call AdvAddPreferredLocation(-11300.0, -9500.0)
    // TOWER OUTPOST
    call AdvAddPreferredLocation(-8500, -9600.0)
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction



JASS:
//============================================================================
//  RR Prologue 05 -- Underworld Minions Wheat "Nagas" -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals

    // Tech tree

    constant integer MG_CLIFFRUNNER = 'n009'
    constant integer MG_BLOODGILL = 'n00H'
    constant integer MG_TIDEWARRIOR = 'n00I'
    constant integer MC_TIDERUNNER = 'n00G'
    constant integer MC_HUNTSMAN = 'n007'
    constant integer MC_NIGHTCRAWLER = 'n008'
   
    // Players

    player User = PlayerEx(1)
    player Trolls = PlayerEx(16)
   
    // Behaviour
   
    constant real ATTACK_WAVE_START_X = -11200.0
    constant real ATTACK_WAVE_START_Y = -12700.0
       
    boolean AttackUser = false
    boolean AttackTrolls = false
    boolean AttackSuicide = false
   
    // Commands
   
    constant integer COMMAND_ATTACK_TROLLS = 2
    constant integer DATA_ATTACK_TROLLS_ENABLE = 1
    constant integer DATA_ATTACK_TROLLS_DISABLE = 0
   
    constant integer COMMAND_ATTACK_USER = 1
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
   
    constant integer COMMAND_ATTACK_SUICIDE = 3
    constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
    constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
   
endglobals

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking User...")
    call AdvSetPrioritizeNearest(false)
    call AdvSetSearchPreferredLocations(true)
    call AdvSetContinueAttackPercentage(100)
   
    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
        call CampaignAttackerEx(0, 1, 1, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 2 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 3, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 1, MC_HUNTSMAN)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 3 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
        call CampaignAttackerEx(1, 1, 1, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
        call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
   
        loop

            // *** WAVE 4 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(0, 1, 1, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 5 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 3, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 1, 2, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 1, 1, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 6 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
            call CampaignAttackerEx(1, 1, 1, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
   
endfunction

function AttackWavesAttackTrolls takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls...")
    call AdvSetPrioritizeNearest(true)
    call AdvSetSearchPreferredLocations(false)
    call AdvSetContinueAttackPercentage(0)

    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M3, M2, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        loop

            // *** WAVE 2 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call AdvSuicideOnPlayerEx(M3, M3, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 3 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M3, M2, M2, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls!")
   
endfunction

function AttackWavesAttackAll takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackTrolls = AttackTrolls
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking Trolls and User...")
   
    loop

        // *** WAVE 1 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
        call CampaignAttackerEx(0, 1, 1, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 2 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 3, 3, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 2, MC_TIDERUNNER)
        call CampaignAttackerEx(1, 1, 1, MC_HUNTSMAN)
        call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 3 ***
        call AdvSetPrioritizeNearest(true)
        call AdvSetSearchPreferredLocations(false)
        call AdvSetContinueAttackPercentage(0)
        call InitAssaultGroup()
        call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
        call AdvSuicideOnPlayerEx(M1, 30, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        // *** WAVE 4 ***
        call AdvSetPrioritizeNearest(false)
        call AdvSetSearchPreferredLocations(true)
        call AdvSetContinueAttackPercentage(100)
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
        call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
        call CampaignAttackerEx(1, 1, 1, MG_TIDEWARRIOR)
        call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
        call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
   
        loop
       
            // *** WAVE 5 ***
            call AdvSetPrioritizeNearest(true)
            call AdvSetSearchPreferredLocations(false)
            call AdvSetContinueAttackPercentage(0)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 2, 2, MG_TIDEWARRIOR)
            call AdvSuicideOnPlayerEx(M1, 30, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 6 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MC_TIDERUNNER)
            call CampaignAttackerEx(0, 1, 1, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 7 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 3, 3, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 1, 2, MC_TIDERUNNER)
            call CampaignAttackerEx(1, 1, 1, MC_HUNTSMAN)
            call CampaignAttackerEx(1, 1, 1, MG_BLOODGILL)
            call AdvSuicideOnPlayerEx(M1, 45, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

            // *** WAVE 8 ***
            call AdvSetPrioritizeNearest(false)
            call AdvSetSearchPreferredLocations(true)
            call AdvSetContinueAttackPercentage(100)
            call InitAssaultGroup()
            call CampaignAttackerEx(1, 2, 2, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 1, 2, MG_BLOODGILL)
            call CampaignAttackerEx(1, 1, 1, MG_TIDEWARRIOR)
            call CampaignAttackerEx(1, 1, 1, MC_NIGHTCRAWLER)
            call AdvSuicideOnPlayerEx(M1, M1, 45, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
           
            // *** WAVE 9 ***
            call AdvSetPrioritizeNearest(true)
            call AdvSetSearchPreferredLocations(false)
            call AdvSetContinueAttackPercentage(0)
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, MG_CLIFFRUNNER)
            call CampaignAttackerEx(1, 2, 2, MC_HUNTSMAN)
            call AdvSuicideOnPlayerEx(M1, 30, 30, Trolls, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackTrolls != AttackTrolls or AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking Trolls and User!")
   
endfunction

// Note: the suicide attack is always only on the user
function AttackWavesAttackSuicide takes nothing returns nothing
    local boolean AttackWaveAttackSuicide = AttackSuicide
    local integer UserId = GetPlayerId(User)
   
    call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
   
    // Reset the assault group
    call InitAssaultGroup()
   
    // Launch attack continuously
    loop
       
        call SuicideUnitEx(1, MC_TIDERUNNER, UserId)
        call SuicideUnitEx(1, MC_HUNTSMAN, UserId)
        call SuicideUnitEx(1, MC_NIGHTCRAWLER, UserId)
        call SuicideUnitEx(1, MG_CLIFFRUNNER, UserId)
        call SuicideUnitEx(1, MG_TIDEWARRIOR, UserId)
        call SuicideUnitEx(1, MG_BLOODGILL, UserId)
       
        call Sleep(2)
        exitwhen AttackSuicide != AttackWaveAttackSuicide
   
    endloop

    call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
   
endfunction

function AttackLoop takes nothing returns nothing

    loop
       
        // If suicide is set, execute the attack suicide loop
        if AttackSuicide then
            call AttackWavesAttackSuicide()
        else
            // Attack the user and/or the trolls depending on the condition
            if AttackUser and AttackTrolls then
                call AttackWavesAttackAll()
            else
                if AttackUser then
                    call AttackWavesAttackUser()
                endif
                if AttackTrolls then
                    call AttackWavesAttackTrolls()
                endif
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
   
    // Check if there is any command waiting
     
    loop
   
        exitwhen CommandsWaiting() <= 0

        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       
        // Update attack suicide behaviour command (to start/stop suicide, always against the user)
        if Command == COMMAND_ATTACK_SUICIDE then
            if Data == DATA_ATTACK_SUICIDE_ENABLE then
                set AttackSuicide = true
                call AdvSetSuicide(true)
            elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
                set AttackSuicide = false
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!") 
            endif
           
        // Update attack trolls behaviour command (to start/stop attack against the trolls)
        elseif Command == COMMAND_ATTACK_TROLLS then
            if Data == DATA_ATTACK_TROLLS_ENABLE then
                set AttackTrolls = true
            elseif Data == DATA_ATTACK_TROLLS_DISABLE then
                set AttackTrolls = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack trolls command data received!") 
            endif  
           
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
           
        endif
   
    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(NAGA_CORAL, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call GroupTimedLife(true)
    call SetAmphibious()
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetContinueAttackReducePercentageIfFarAway(false)
   
    call AdvSetAttackWaveGatherReturnXY(-11500.0, -13800.0)
   
    // MAIN USER BASE BOTTOM LEFT ENTRANCE
    call AdvAddPreferredLocation(-6500, -8500.0)
    // EXPANSION USER BASE
    call AdvAddPreferredLocation(-6550, -13800.0)
    // FOUNTAIN
    call AdvAddPreferredLocation(-11300.0, -9500.0)
    // TOWER OUTPOST
    call AdvAddPreferredLocation(-8500, -9600.0)

    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction
 
Last edited:
Sharing all new scripts from Warcraft 3 Re-Reforged: The Scourge of Lordaeron Act I. These scripts all require JASS AI 2.0.


JASS:
//============================================================================
//  RR Human 02 -- Blackrock Clan Red Orc -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//     Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals

    // Tech tree

    constant integer NAKORUSH = 'N002'
    constant integer AXETHROWER = 'n000'
    constant integer OGRE = 'n001'
    constant integer W2_CATAPULT = 'o003'
    constant integer TROLL_LUMBER_MILL = 'o000'
    constant integer ORC_BLACKSMITH = 'o001'
    constant integer OGRE_MOUND = 'o002'
    constant integer UPG_ORC_OGRE_MIGHT = 'R000'
   
    // Players
   
    constant player User = PlayerEx(2)
   
    // Behaviour
   
    constant real ATTACK_WAVE_START_X = -900.0
    constant real ATTACK_WAVE_START_Y = 2850.0
       
    boolean AttackUser = false
    boolean AttackHero = false
    boolean BlacksmithAvailable = true
    boolean OgreMoundAvailable = true
    boolean BestiaryAvailable = true
    boolean Research = false
   
    // Commands
   
    constant integer COMMAND_ATTACK = 1
    constant integer DATA_ATTACK_ENABLE = 1
    constant integer DATA_ATTACK_DISABLE = 0
   
    constant integer COMMAND_ATTACK_HERO = 2
    constant integer DATA_ATTACK_HERO_ENABLE = 1
    constant integer DATA_ATTACK_HERO_DISABLE = 0
   
    constant integer COMMAND_BLACKSMITH_AVAILABLE = 3
    constant integer DATA_BLACKSMITH_AVAILABLE_ENABLE = 1
    constant integer DATA_BLACKSMITH_AVAILABLE_DISABLE = 0
   
    constant integer COMMAND_OGRE_MOUND_AVAILABLE = 4
    constant integer DATA_OGRE_MOUND_AVAILABLE_ENABLE = 1
    constant integer DATA_OGRE_MOUND_AVAILABLE_DISABLE = 0
   
    constant integer COMMAND_BESTIARY_AVAILABLE = 5
    constant integer DATA_BESTIARY_AVAILABLE_ENABLE = 1
    constant integer DATA_BESTIARY_AVAILABLE_DISABLE = 0
   
    constant integer COMMAND_RESEARCH = 6
    constant integer DATA_RESEARCH_ENABLE = 1
    constant integer DATA_RESEARCH_DISABLE = 0
   
    constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
   
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, PEON)
    call SetBuildUnitEx(1, 1, 1, GREAT_HALL)
    call SetBuildUnitEx(3, 3, 3, PEON)
    call SetBuildUnitEx(1, 1, 1, ORC_ALTAR)
    call SetBuildUnitEx(1, 1, 1, NAKORUSH)
    call SetBuildUnitEx(8, 8, 8, PEON)
    call SetBuildUnitEx(2, 2, 2, PIG_FARM)
    call SetBuildUnitEx(1, 1, 1, ORC_BARRACKS)
    call SetBuildUnitEx(1, 1, 1, PIG_FARM)
    call SetBuildUnitEx(1, 1, 1, TROLL_LUMBER_MILL)
    call SetBuildUnitEx(0, 1, 1, STRONGHOLD)
    call SetBuildUnitEx(0, 1, 1, PIG_FARM)
    call SetBuildUnitEx(0, 1, 1, LODGE)
    call SetBuildUnitEx(0, 0, 1, PIG_FARM)
    call SetBuildUnitEx(0, 0, 1, ORC_WATCH_TOWER)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

    if BestiaryAvailable and OgreMoundAvailable then
        call CampaignDefenderEx(1, 1, 1, GRUNT)   
        call CampaignDefenderEx(1, 1, 1, AXETHROWER)
        call CampaignDefenderEx(1, 1, 1, RAIDER)
        call CampaignDefenderEx(0, 1, 1, OGRE)
        call CampaignDefenderEx(0, 0, 1, W2_WARLOCK)
        call CampaignDefenderEx(1, 1, 1, NAKORUSH)
    elseif BestiaryAvailable then
        call CampaignDefenderEx(1, 2, 2, GRUNT)   
        call CampaignDefenderEx(1, 1, 1, AXETHROWER)
        call CampaignDefenderEx(1, 1, 1, RAIDER)
        call CampaignDefenderEx(0, 0, 1, W2_WARLOCK)
        call CampaignDefenderEx(1, 1, 1, NAKORUSH)   
    else
        call CampaignDefenderEx(2, 2, 2, GRUNT)   
        call CampaignDefenderEx(1, 2, 2, AXETHROWER)
        call CampaignDefenderEx(0, 0, 1, W2_WARLOCK)
        call CampaignDefenderEx(1, 1, 1, NAKORUSH)
    endif
   
endfunction

// RESEARCH LOOP
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing
    local boolean ResearchUpgradesResearch = Research

    call Sleep(M2)
    if ResearchUpgradesResearch != Research then
        return
    endif

    call SetBuildUpgrEx(1, 1, 1, UPG_ORC_PILLAGE)
   
    call Sleep(M3)
    if ResearchUpgradesResearch != Research then
        return
    endif
   
    call SetBuildUpgrEx(1, 1, 1, UPG_ORC_MELEE)
   
    call Sleep(M4)
    if ResearchUpgradesResearch != Research then
        return
    endif
   
    call SetBuildUpgrEx(1, 1, 1, UPG_ORC_ARMOR)
       
    call Sleep(M2)
    if ResearchUpgradesResearch != Research then
        return
    endif
   
    call SetBuildUpgrEx(0, 1, 1, UPG_ORC_SHAMAN)
    call SetBuildUpgrEx(0, 0, 1, UPG_ORC_SPIKES)
   
    call Sleep(M2)
    if ResearchUpgradesResearch != Research then
        return
    endif
   
    call SetBuildUpgrEx(1, 1, 1, UPG_ORC_RANGED)
    call SetBuildUpgrEx(0, 1, 1, UPG_ORC_REGEN)
   
    call Sleep(M3)
    if ResearchUpgradesResearch != Research then
        return
    endif
   
    call SetBuildUpgrEx(0, 0, 2, UPG_ORC_SPIKES)
    call SetBuildUpgrEx(0, 1, 1, UPG_ORC_OGRE_MIGHT)

endfunction

function ResearchLoop takes nothing returns nothing

    loop
       
        if Research then
            call ResearchUpgrades()
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackWithoutHero takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackHero = AttackHero
   
    call AdvDebugDisplayToPlayer("AI Info: attacking without hero...")
   
    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, GRUNT)
        call CampaignAttackerEx(1, 1, 2, AXETHROWER)
        call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser

        // *** WAVE 2 ***
        call InitAssaultGroup()
        if BestiaryAvailable then
            call CampaignAttackerEx(2, 3, 4, RAIDER)
        else
            call CampaignAttackerEx(0, 1, 1, GRUNT)
            call CampaignAttackerEx(2, 2, 3, AXETHROWER)
        endif
        call AdvSuicideOnPlayerEx(M4, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
       
        // *** WAVE 3 ***
        call InitAssaultGroup()
        if BlacksmithAvailable then
            call CampaignAttackerEx(1, 1, 1, W2_CATAPULT)
        else
            if OgreMoundAvailable and BestiaryAvailable then
                call CampaignAttackerEx(1, 1, 1, OGRE)
            else
                call CampaignAttackerEx(1, 1, 1, W2_WARLOCK)
            endif
        endif
        call CampaignAttackerEx(1, 2, 2, GRUNT)
        call CampaignAttackerEx(0, 1, 2, AXETHROWER)
        call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
       
        loop

            // *** WAVE 4 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, GRUNT)
            call CampaignAttackerEx(1, 2, 2, AXETHROWER)
            call AdvSuicideOnPlayerEx(M4, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser

            // *** WAVE 5 ***
            call InitAssaultGroup()
            if BestiaryAvailable then
                call CampaignAttackerEx(3, 4, 5, RAIDER)
            else
                call CampaignAttackerEx(1, 1, 2, GRUNT)
                call CampaignAttackerEx(2, 3, 3, AXETHROWER)
            endif
            call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
           
            // *** WAVE 6 ***
            call InitAssaultGroup()
            if BlacksmithAvailable then
                call CampaignAttackerEx(1, 1, 2, W2_CATAPULT)
            else
                if OgreMoundAvailable and BestiaryAvailable then
                    call CampaignAttackerEx(1, 1, 2, OGRE)
                else
                    call CampaignAttackerEx(1, 1, 2, W2_WARLOCK)
                endif
            endif
            call CampaignAttackerEx(1, 2, 2, GRUNT)
            call CampaignAttackerEx(1, 1, 1, AXETHROWER)
            call AdvSuicideOnPlayerEx(M4, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
           
        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking without hero!")
   
endfunction

function AttackWavesAttackWithHero takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackHero = AttackHero
   
    call AdvDebugDisplayToPlayer("AI Info: attacking with hero...")
   
    loop
   
        // *** WAVE 1 ***
        call InitAssaultGroup()
        call CampaignAttackerEx(1, 2, 2, GRUNT)
        call CampaignAttackerEx(1, 1, 2, AXETHROWER)
        call CampaignAttackerEx(1, 1, 1, NAKORUSH)
        call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser

        // *** WAVE 2 ***
        call InitAssaultGroup()
        if BestiaryAvailable then
            call CampaignAttackerEx(2, 3, 4, RAIDER)
        else
            call CampaignAttackerEx(0, 1, 1, GRUNT)
            call CampaignAttackerEx(2, 2, 3, AXETHROWER)
        endif
        call CampaignAttackerEx(1, 1, 1, NAKORUSH)
        call AdvSuicideOnPlayerEx(M4, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
       
        // *** WAVE 3 ***
        call InitAssaultGroup()
        if BlacksmithAvailable then
            call CampaignAttackerEx(1, 1, 1, W2_CATAPULT)
        else
            if OgreMoundAvailable and BestiaryAvailable then
                call CampaignAttackerEx(1, 1, 1, OGRE)
            else
                call CampaignAttackerEx(1, 1, 1, W2_WARLOCK)
            endif
        endif
        call CampaignAttackerEx(1, 2, 2, GRUNT)
        call CampaignAttackerEx(0, 1, 2, AXETHROWER)
        call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
        exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
       
        loop

            // *** WAVE 4 ***
            call InitAssaultGroup()
            call CampaignAttackerEx(2, 2, 3, GRUNT)
            call CampaignAttackerEx(1, 2, 2, AXETHROWER)
            call CampaignAttackerEx(1, 1, 1, NAKORUSH)
            call AdvSuicideOnPlayerEx(M4, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser

            // *** WAVE 5 ***
            call InitAssaultGroup()
            if BestiaryAvailable then
                call CampaignAttackerEx(3, 4, 5, RAIDER)
            else
                call CampaignAttackerEx(1, 1, 2, GRUNT)
                call CampaignAttackerEx(2, 3, 3, AXETHROWER)
            endif
            call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
           
            // *** WAVE 6 ***
            call InitAssaultGroup()
            if BlacksmithAvailable then
                call CampaignAttackerEx(1, 1, 2, W2_CATAPULT)
            else
                if OgreMoundAvailable and BestiaryAvailable then
                    call CampaignAttackerEx(1, 1, 2, OGRE)
                else
                    call CampaignAttackerEx(1, 1, 2, W2_WARLOCK)
                endif
            endif
            call CampaignAttackerEx(1, 2, 2, GRUNT)
            call CampaignAttackerEx(1, 1, 1, AXETHROWER)
            call CampaignAttackerEx(1, 1, 1, NAKORUSH)
            call AdvSuicideOnPlayerEx(M4, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
            exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
           
        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackHero != AttackHero or AttackWaveAttackUser != AttackUser
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking with hero!")
   
endfunction 

function AttackLoop takes nothing returns nothing

    loop
       
        // Attack the user with or without the hero depending on the condition
        if AttackUser then
            if AttackHero then
                call AttackWavesAttackWithHero()
            else
                call AttackWavesAttackWithoutHero()
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data

    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0
    
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       
        // We set the suicide flag to disable all attack routines upon defeat
        if Command == COMMAND_DEFEAT then
            if Data == DATA_DEFEAT_ENABLE then
                call AdvSetSuicide(true)
            elseif Data == DATA_DEFEAT_DISABLE then
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
            endif
       
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK then
            if Data == DATA_ATTACK_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack command data received!")
            endif 
           
        // Update attack hero behaviour command (to start/stop attack against the user with the hero)
        // Note: it does nothing if attacks towards user are not enabled
        elseif Command == COMMAND_ATTACK_HERO then
            if Data == DATA_ATTACK_HERO_ENABLE then
                set AttackHero = true
            elseif Data == DATA_ATTACK_HERO_DISABLE then
                set AttackHero = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack hero command data received!")
            endif  
           
        // Update availability of blacksmith building   
        elseif Command == COMMAND_BLACKSMITH_AVAILABLE then
            if Data == DATA_BLACKSMITH_AVAILABLE_ENABLE then
                set BlacksmithAvailable = true
            elseif Data == DATA_BLACKSMITH_AVAILABLE_DISABLE then
                set BlacksmithAvailable = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid blacksmith available command data received!")
            // Reset build order and defenders
            call InitBuildArray()
            call InitDefenseGroup()
            call SetBuildOrder()        
            call SetDefenders()
            endif    
           
        // Update availability of ogre mound building   
        elseif Command == COMMAND_OGRE_MOUND_AVAILABLE then
            if Data == DATA_OGRE_MOUND_AVAILABLE_ENABLE then
                set OgreMoundAvailable = true
            elseif Data == DATA_OGRE_MOUND_AVAILABLE_DISABLE then
                set OgreMoundAvailable = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid ogre mound available command data received!")
            // Reset build order and defenders
            call InitBuildArray()
            call InitDefenseGroup()
            call SetBuildOrder()        
            call SetDefenders()
            endif
           
        // Update availability of bestiary building   
        elseif Command == COMMAND_BESTIARY_AVAILABLE then
            if Data == DATA_BESTIARY_AVAILABLE_ENABLE then
                set BestiaryAvailable = true
            elseif Data == DATA_BESTIARY_AVAILABLE_DISABLE then
                set BestiaryAvailable = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid bestiary available command data received!")
            // Reset build order and defenders
            call InitBuildArray()
            call InitDefenseGroup()
            call SetBuildOrder()        
            call SetDefenders()
            endif
           
        // Update research behaviour
        // Note: if research is disabled/enabled there is a delay to reach the previous point
        elseif Command == COMMAND_RESEARCH then
            if Data == DATA_RESEARCH_ENABLE then
                set Research = true
            elseif Data == DATA_RESEARCH_DISABLE then
                set Research = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid research command data received!")
            endif 
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
           
        endif
   
    endloop
     
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(PIG_FARM, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call SetReplacements(1, 1, 1)
    call GroupTimedLife(true)
    call AdvSetPrioritizeNearest(false)
    call AdvSetPrioritizeTownHalls(false)
   
    call AdvSetPreferredLocationsRadius(1000.0)
    call AdvAddPreferredLocation(-4250.0, -2750.0)
    call AdvAddPreferredLocation(-2250, -3750.0)
   
    // Define build order
    call SetBuildOrder()
   
    // Start research of upgrades at timed intervals as soon as possible (it depends on the commands received)
    call StartThread(function ResearchLoop)
   
    // Define defenders
    call SetDefenders()
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction



JASS:
//============================================================================
//  RR Human 05 -- Undead Scourge Green Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals
   
    // Players

    player User = PlayerEx(2)
   
    // Behaviour
   
    constant real ATTACK_WAVE_MARDENHOLDE_CENTER_START_X = -2500.0
    constant real ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y = -500.0
   
    // Note: this is used to also target Northridge lumber mill
    constant real ATTACK_WAVE_MARDENHOLDE_LEFT_START_X = -5050.0
    constant real ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y = -4050.0
       
    boolean AttackUser = false
    boolean Weaken = false
    boolean AttackSuicide = false
   
    // Commands
   
    constant integer COMMAND_WEAKEN = 2
    constant integer DATA_WEAKEN_ENABLE = 1
    constant integer DATA_WEAKEN_DISABLE = 0
   
    constant integer COMMAND_ATTACK_USER = 1
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constantinteger DATA_ATTACK_USER_DISABLE = 0
   
    constant integer COMMAND_ATTACK_SUICIDE = 3
    constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
    constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
   
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
    call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
    call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
    call SetBuildUnitEx(1, 1, 1, ZIGGURAT_1)
    call SetBuildUnitEx(5, 5, 5, ACOLYTE)
    call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
    call SetBuildUnitEx(2, 2, 2, ZIGGURAT_1)
    call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
    call SetBuildUnitEx(1, 1, 1, CRYPT)
    call SetBuildUnitEx(2, 2, 2, ZIGGURAT_2)
    call SetBuildUnitEx(2, 2, 2, CRYPT)
    call SetBuildUnitEx(3, 3, 3, ZIGGURAT_1)
    call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
    call SetBuildUnitEx(4, 4, 4, ZIGGURAT_1)
    call SetBuildUnitEx(1, 1, 1, SLAUGHTERHOUSE)
    call SetBuildUnitEx(2, 2, 2, DAMNED_TEMPLE)
    call SetBuildUnitEx(1, 1, 1, SAC_PIT)
    call SetBuildUnitEx(2, 3, 4, ZIGGURAT_2)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing
   
    // Note: when not weakened, they need a good defense even on easy
    if Weaken then
        call CampaignDefenderEx(1, 1, 2, GHOUL)
        call CampaignDefenderEx(1, 1, 1, ABOMINATION)
        call CampaignDefenderEx(0, 1, 1, NECRO)
    else
        call CampaignDefenderEx(4, 4, 4, GHOUL)
        call CampaignDefenderEx(1, 1, 1, NECRO)
        call CampaignDefenderEx(1, 1, 1, ABOMINATION)
        call CampaignDefenderEx(1, 1, 1, BANSHEE)
        call CampaignDefenderEx(1, 1, 1, SHADE)
    endif
       
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

    call Sleep(M4)

    call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 0, 1, UPG_GHOUL_FRENZY)
    call SetBuildUpgrEx(0, 1, 1, UPG_BANSHEE)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_NECROS)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 1, 2, UPG_BANSHEE)
       
endfunction

// ATTACK LOOP
//------------------------------------------------
// Note: attack times are the same across all difficulties because
// players have to face attacks at same time no matter the difficulty chosen
function AttackWavesAttackUser takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking User...")
    call AdvSetSearchPreferredLocations(true)
    call AdvSetContinueAttackPercentage(100)
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetPrioritizeNearest(true)
   
    loop

        // *** WAVE 1 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 2, GHOUL)
            call CampaignAttackerEx(0, 1, 1, NECRO)
            call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        else
            call CampaignAttackerEx(2, 2, 3, GHOUL)
            call CampaignAttackerEx(2, 3, 3, NECRO)
            call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 2 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 2, GHOUL)
            call CampaignAttackerEx(0, 1, 1, NECRO)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M5, M5, M4, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        else
            call CampaignAttackerEx(4, 4, 4, GHOUL)
            call CampaignAttackerEx(1, 2, 2, NECRO)
            call CampaignAttackerEx(2, 2, 3, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 3 ***
        // Make sure we attack left entrance and not the lumber mill, even if still alive
        call AdvSetPrioritizeTownHalls(true)
       
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 2, GHOUL)
            call CampaignAttackerEx(1, 1, 1, BANSHEE)
            call CampaignAttackerEx(0, 1, 1, SHADE)
            call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        else
            call CampaignAttackerEx(3, 3, 4, GHOUL)
            call CampaignAttackerEx(2, 2, 2, BANSHEE)
            call CampaignAttackerEx(0, 1, 1, SHADE)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
               
        // Reset default behaviour
        call AdvSetPrioritizeTownHalls(false)
       
        // *** WAVE 4 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(0, 1, 1, GHOUL)
            call CampaignAttackerEx(1, 1, 1, NECRO)
            call CampaignAttackerEx(1, 1, 1, BANSHEE)
            call CampaignAttackerEx(0, 0, 1, ABOMINATION)
            call AdvSuicideOnPlayerEx(M5, M5, M4, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        else
            call CampaignAttackerEx(4, 4, 4, GHOUL)
            call CampaignAttackerEx(1, 1, 2, NECRO)
            call CampaignAttackerEx(1, 1, 1, BANSHEE)
            call CampaignAttackerEx(1, 2, 2, ABOMINATION)
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 5 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 1, ABOMINATION)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call CampaignAttackerEx(0, 1, 2, BANSHEE)
            call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        else
            call CampaignAttackerEx(2, 2, 3, ABOMINATION)
            call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
            call CampaignAttackerEx(1, 2, 2, BANSHEE)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 6 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 2, 2, GHOUL)
            call CampaignAttackerEx(1, 1, 1, ABOMINATION)
            call CampaignAttackerEx(0, 0, 1, SHADE)
            call AdvSuicideOnPlayerEx(M5, M5, M4, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        else
            call CampaignAttackerEx(5, 5, 5, GHOUL)
            call CampaignAttackerEx(1, 1, 2, ABOMINATION)
            call CampaignAttackerEx(1, 2, 2, SHADE)
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 7 ***
        // Make sure we attack left entrance and not the lumber mill, even if still alive
        call AdvSetPrioritizeTownHalls(true)
       
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 2, 3, NECRO)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        else
            call CampaignAttackerEx(3, 3, 4, NECRO)
            call CampaignAttackerEx(2, 3, 3, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // Reset default behaviour
        call AdvSetPrioritizeTownHalls(false)
       
        loop
       
            // *** WAVE 8 ***
            call InitAssaultGroup()   
            if Weaken then
                call CampaignAttackerEx(0, 1, 2, GHOUL)
                call CampaignAttackerEx(1, 1, 1, NECRO)
                call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
                call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
            else
                call CampaignAttackerEx(3, 3, 4, GHOUL)
                call CampaignAttackerEx(1, 2, 2, NECRO)
                call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
                call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_LEFT_START_X, ATTACK_WAVE_MARDENHOLDE_LEFT_START_Y)
            endif
            exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide   

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
   
endfunction

function AttackWavesAttackSuicide takes nothing returns nothing
    local boolean AttackWaveAttackSuicide = AttackSuicide
    local integer UserId = GetPlayerId(User)
   
    call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
   
    // Reset the assault group
    call InitAssaultGroup()
   
    // Launch attack continuously
    loop
       
        call SuicideUnitEx(1, GHOUL, UserId)
        call SuicideUnitEx(1, NECRO, UserId)
        call SuicideUnitEx(1, BANSHEE, UserId)
        call SuicideUnitEx(1, ABOMINATION, UserId)
        call SuicideUnitEx(1, MEAT_WAGON, UserId)
        call SuicideUnitEx(1, SHADE, UserId)
       
        call Sleep(2)
        exitwhen AttackSuicide != AttackWaveAttackSuicide
   
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
   
endfunction

function AttackLoop takes nothing returns nothing

    loop
       
        // If suicide is set, execute the attack suicide loop
        if AttackSuicide then
            call AttackWavesAttackSuicide()
        else
            // Attack the user if required
            if AttackUser then
                call AttackWavesAttackUser()
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
   
    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0
  
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       
        // Update attack suicide behaviour command (to start/stop suicide, always against the user)
        if Command == COMMAND_ATTACK_SUICIDE then
            if Data == DATA_ATTACK_SUICIDE_ENABLE then
                set AttackSuicide = true
                call AdvSetSuicide(true)
            elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
                set AttackSuicide = false
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
            endif
           
        // Update weaken behavior (reduces attack rates, units and defenders)
        elseif Command == COMMAND_WEAKEN then
            if Data == DATA_WEAKEN_ENABLE then
                set Weaken = true
            elseif Data == DATA_WEAKEN_DISABLE then
                set Weaken = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid weaken command data received!")
            endif
            // Reset defenders
            call InitDefenseGroup()      
            call SetDefenders()
           
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!")
            endif
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
           
        endif
  
    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call GroupTimedLife(true)
    call SetReplacements(1, 1, 1)
    call AdvSetContinueAttackReducePercentageIfFarAway(false)
    call AdvSetWoodPeonsWarriors(true)
   
    // MARDENHOLDE KEEP
    call AdvAddPreferredLocation(-6740.0, 2450.0)
    // NORTHRIDGE LUMBER MILL
    call AdvAddPreferredLocation(-7240, -5150.0)
   
    // Define build order
    call SetBuildOrder()
   
    // Start to research upgrades at timed intervals
    call StartThread(function ResearchUpgrades)
   
    // Define defenders
    call SetDefenders()
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction



JASS:
//============================================================================
//  RR Human 05 -- Undead Scourge Purple Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals
   
    // Players

    player User = PlayerEx(2)
   
    // Behaviour
   
    constant real ATTACK_WAVE_MARDENHOLDE_CENTER_START_X = -2500.0
    constant real ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y = -500.0
   
    constant real ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X = -1200.0
    constant real ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y = 2000.0
       
    boolean AttackUser = false
    boolean Weaken = false
    boolean AttackSuicide = false
   
    // Commands
   
    constant integer COMMAND_WEAKEN = 2
    constant integer DATA_WEAKEN_ENABLE = 1
    constant integer DATA_WEAKEN_DISABLE = 0
   
    constant integer COMMAND_ATTACK_USER = 1
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
   
    constant integer COMMAND_ATTACK_SUICIDE = 3
    constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
    constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
   
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
    call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
    call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
    call SetBuildUnitEx(1, 1, 1, ZIGGURAT_1)
    call SetBuildUnitEx(5, 5, 5, ACOLYTE)
    call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
    call SetBuildUnitEx(1, 1, 1, CRYPT)
    call SetBuildUnitEx(2, 2, 2, ZIGGURAT_1)
    call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
    call SetBuildUnitEx(2, 2, 2, CRYPT)
    call SetBuildUnitEx(4, 4, 4, ZIGGURAT_1)
    call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
    call SetBuildUnitEx(1, 1, 1, SLAUGHTERHOUSE)
    call SetBuildUnitEx(1, 1, 1, DAMNED_TEMPLE)
    call SetBuildUnitEx(1, 1, 2, ZIGGURAT_2)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing
   
    // Note: when not weakened, they need a good defense even on easy
    if Weaken then
        call CampaignDefenderEx(1, 1, 2, GHOUL)
        call CampaignDefenderEx(1, 1, 1, NECRO)
        call CampaignDefenderEx(0, 1, 1, ABOMINATION)
    else
        call CampaignDefenderEx(5, 5, 5, GHOUL)
        call CampaignDefenderEx(1, 1, 1, NECRO)
        call CampaignDefenderEx(1, 1, 1, BANSHEE)
        call CampaignDefenderEx(1, 1, 1, ABOMINATION)
    endif
       
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

    call Sleep(M4)

    call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 1, 1, UPG_BANSHEE)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 0, 1, UPG_GHOUL_FRENZY)
    call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_NECROS)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 1, 2, UPG_BANSHEE)
       
endfunction

// ATTACK LOOP
//------------------------------------------------
// Note: attack times are the same across all difficulties because
// players have to face attacks at same time no matter the difficulty chosen
function AttackWavesAttackUser takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking User...")
    call AdvSetSearchPreferredLocations(true)
    call AdvSetContinueAttackPercentage(100)
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetPrioritizeNearest(true)
   
    loop

        // *** WAVE 1 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 2, 3, GHOUL)
            call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        else
            call CampaignAttackerEx(2, 3, 4, GHOUL)
            call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 2 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 1, GHOUL)
            call CampaignAttackerEx(0, 0, 1, ABOMINATION)
            call CampaignAttackerEx(1, 1, 1, NECRO)
            call CampaignAttackerEx(0, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M5, M4, M4, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        else
            call CampaignAttackerEx(3, 3, 4, GHOUL)
            call CampaignAttackerEx(1, 1, 1, ABOMINATION)
            call CampaignAttackerEx(0, 1, 1, NECRO)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 3 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(0, 1, 1, GHOUL)
            call CampaignAttackerEx(1, 1, 2, BANSHEE)
            call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        else
            call CampaignAttackerEx(2, 3, 4, GHOUL)
            call CampaignAttackerEx(1, 1, 1, BANSHEE)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 4 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 2, 3, GHOUL)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M5, M4, M4, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        else
            call CampaignAttackerEx(4, 4, 5, GHOUL)
            call CampaignAttackerEx(1, 2, 2, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 5 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(0, 1, 2, GHOUL)
            call CampaignAttackerEx(1, 1, 1, NECRO)
            call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        else
            call CampaignAttackerEx(2, 2, 3, GHOUL)
            call CampaignAttackerEx(1, 2, 2, NECRO)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 6 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(2, 3, 4, GHOUL)
            call AdvSuicideOnPlayerEx(M5, M4, M4, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        else
            call CampaignAttackerEx(5, 6, 7, GHOUL)
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 7 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 2, GHOUL)
            call CampaignAttackerEx(0, 1, 1, ABOMINATION)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        else
            call CampaignAttackerEx(3, 4, 4, GHOUL)
            call CampaignAttackerEx(1, 1, 2, ABOMINATION)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        loop
       
            // *** WAVE 8 ***
            call InitAssaultGroup()
            if Weaken then
                call CampaignAttackerEx(1, 1, 2, GHOUL)
                call CampaignAttackerEx(1, 1, 1, ABOMINATION)
                call CampaignAttackerEx(0, 1, 1, MEAT_WAGON)
                call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
            else
                call CampaignAttackerEx(3, 3, 3, GHOUL)
                call CampaignAttackerEx(1, 1, 1, ABOMINATION)
                call CampaignAttackerEx(0, 1, 2, MEAT_WAGON)
                call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
            endif
            exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
   
endfunction

function AttackWavesAttackSuicide takes nothing returns nothing
    local boolean AttackWaveAttackSuicide = AttackSuicide
    local integer UserId = GetPlayerId(User)
   
    call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
   
    // Reset the assault group
    call InitAssaultGroup()
   
    // Launch attack continuously
    loop
       
        call SuicideUnitEx(1, GHOUL, UserId)
        call SuicideUnitEx(1, NECRO, UserId)
        call SuicideUnitEx(1, BANSHEE, UserId)
        call SuicideUnitEx(1, ABOMINATION, UserId)
        call SuicideUnitEx(1, MEAT_WAGON, UserId)
       
        call Sleep(2)
        exitwhen AttackSuicide != AttackWaveAttackSuicide
   
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
   
endfunction

function AttackLoop takes nothing returns nothing

    loop
       
        // If suicide is set, execute the attack suicide loop
        if AttackSuicide then
            call AttackWavesAttackSuicide()
        else
            // Attack the user if required
            if AttackUser then
                call AttackWavesAttackUser()
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
   
    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0
  
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       
        // Update attack suicide behaviour command (to start/stop suicide, always against the user)
        if Command == COMMAND_ATTACK_SUICIDE then
            if Data == DATA_ATTACK_SUICIDE_ENABLE then
                set AttackSuicide = true
                call AdvSetSuicide(true)
            elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
                set AttackSuicide = false
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
            endif
           
        // Update weaken behavior (reduces attack rates, units and defenders)
        elseif Command == COMMAND_WEAKEN then
            if Data == DATA_WEAKEN_ENABLE then
                set Weaken = true
            elseif Data == DATA_WEAKEN_DISABLE then
                set Weaken = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid weaken command data received!")
            endif
            // Reset defenders
            call InitDefenseGroup()      
            call SetDefenders()
           
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!")
            endif
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
           
        endif
  
    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call GroupTimedLife(true)
    call SetReplacements(1, 1, 1)
    call AdvSetContinueAttackReducePercentageIfFarAway(false)
    call AdvSetWoodPeonsWarriors(true)
   
    // MARDENHOLDE KEEP
    call AdvAddPreferredLocation(-6740.0, 2450.0)
   
    // Define build order
    call SetBuildOrder()
   
    // Start to research upgrades at timed intervals
    call StartThread(function ResearchUpgrades)
   
    // Define defenders
    call SetDefenders()
       
    // Set defense captain place to avoid cluttering in shared base
    call AdvSetDefenseCaptainHomeXY(2500, -1300)
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction



JASS:
//============================================================================
//  RR Human 05 -- Undead Scourge Violet Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS
//------------------------------------------------
globals
   
    // Players

    player User = PlayerEx(2)
   
    // Behaviour
   
    constant real ATTACK_WAVE_MARDENHOLDE_CENTER_START_X = -2500.0
    constant real ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y = -500.0
   
    constant real ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X = -1200.0
    constant real ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y = 2000.0
       
    boolean AttackUser = false
    boolean Weaken = false
    boolean AttackSuicide = false
   
    // Commands
   
    constant integer COMMAND_WEAKEN = 2
    constant integer DATA_WEAKEN_ENABLE = 1
    constant integer DATA_WEAKEN_DISABLE = 0
   
    constant integer COMMAND_ATTACK_USER = 1
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
   
    constant integer COMMAND_ATTACK_SUICIDE = 3
    constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
    constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
   
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
    call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
    call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
    call SetBuildUnitEx(1, 1, 1, ZIGGURAT_1)
    call SetBuildUnitEx(5, 5, 5, ACOLYTE)
    call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
    call SetBuildUnitEx(1, 1, 1, CRYPT)
    call SetBuildUnitEx(2, 2, 2, ZIGGURAT_1)
    call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
    call SetBuildUnitEx(4, 4, 4, ZIGGURAT_1)
    call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
    call SetBuildUnitEx(1, 1, 1, SLAUGHTERHOUSE)
    call SetBuildUnitEx(1, 1, 1, DAMNED_TEMPLE)
    call SetBuildUnitEx(1, 1, 1, SAC_PIT)
    call SetBuildUnitEx(1, 1, 2, ZIGGURAT_2)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing
   
    // Note: when not weakened, they need a good defense even on easy
    if Weaken then
        call CampaignDefenderEx(1, 1, 2, GHOUL)
        call CampaignDefenderEx(1, 1, 1, NECRO)
        call CampaignDefenderEx(0, 1, 1, SHADE)
    else
        call CampaignDefenderEx(5, 5, 5, GHOUL)
        call CampaignDefenderEx(1, 1, 1, NECRO)
        call CampaignDefenderEx(1, 1, 1, BANSHEE)
        call CampaignDefenderEx(1, 1, 1, SHADE)
    endif
       
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

    call Sleep(M4)

    call SetBuildUpgrEx(0, 1, 1, UPG_BANSHEE)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 0, 1, UPG_GHOUL_FRENZY)
    call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(1, 2, 2, UPG_NECROS)
   
    call Sleep(M3)
   
    call SetBuildUpgrEx(0, 1, 2, UPG_BANSHEE)
       
endfunction

// ATTACK LOOP
//------------------------------------------------
// Note: attack times are the same across all difficulties because
// players have to face attacks at same time no matter the difficulty chosen.
// This doesn't apply when it is weakened
function AttackWavesAttackUser takes nothing returns nothing
    local boolean AttackWaveAttackUser = AttackUser
    local boolean AttackWaveAttackSuicide = AttackSuicide
   
    call AdvDebugDisplayToPlayer("AI Info: attacking User...")
    call AdvSetSearchPreferredLocations(true)
    call AdvSetContinueAttackPercentage(100)
    call AdvSetPrioritizeTownHalls(false)
    call AdvSetPrioritizeNearest(true)
   
    loop

        // *** WAVE 1 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 2, 3, BANSHEE)
            call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        else
            call CampaignAttackerEx(2, 3, 4, BANSHEE)   
            call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)           
        endif       
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 2 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 2, 3, GHOUL)
            call CampaignAttackerEx(1, 1, 1, ABOMINATION)
            call AdvSuicideOnPlayerEx(M5, M5, M4, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        else
            call CampaignAttackerEx(4, 4, 5, GHOUL)
            call CampaignAttackerEx(1, 2, 2, ABOMINATION)   
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 3 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 2, NECRO)
            call CampaignAttackerEx(0, 1, 1, SHADE)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        else
            call CampaignAttackerEx(1, 2, 2, NECRO)
            call CampaignAttackerEx(1, 1, 2, SHADE)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 4 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 2, GHOUL)
            call CampaignAttackerEx(0, 1, 1, NECRO)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M5, M5, M4, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        else
            call CampaignAttackerEx(2, 2, 3, GHOUL)
            call CampaignAttackerEx(2, 2, 2, NECRO)
            call CampaignAttackerEx(1, 2, 2, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 5 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 2, NECRO)
            call CampaignAttackerEx(0, 1, 1, SHADE)
            call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        else
            call CampaignAttackerEx(2, 3, 3, NECRO)
            call CampaignAttackerEx(1, 1, 2, SHADE)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 6 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 1, 1, ABOMINATION)
            call CampaignAttackerEx(0, 1, 2, NECRO)
            call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M5, M4, M4, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        else
            call CampaignAttackerEx(1, 1, 1, ABOMINATION)
            call CampaignAttackerEx(2, 2, 3, NECRO)
            call CampaignAttackerEx(2, 3, 3, MEAT_WAGON)
            call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_X, ATTACK_WAVE_MARDENHOLDE_RIGHT_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        // *** WAVE 7 ***
        call InitAssaultGroup()
        if Weaken then
            call CampaignAttackerEx(1, 2, 2, GHOUL)
            call CampaignAttackerEx(0, 0, 1, SHADE)
            call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        else
            call CampaignAttackerEx(2, 2, 3, GHOUL)
            call CampaignAttackerEx(1, 2, 2, SHADE)
            call AdvSuicideOnPlayerEx(40, 40, 40, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
        endif
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
        loop
       
            // *** WAVE 8 ***
            call InitAssaultGroup()
            if Weaken then
                call CampaignAttackerEx(1, 1, 2, GHOUL)
                call CampaignAttackerEx(1, 1, 1, BANSHEE)
                call CampaignAttackerEx(0, 1, 1, NECRO)
                call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
            else
                call CampaignAttackerEx(2, 2, 3, GHOUL)
                call CampaignAttackerEx(2, 2, 2, BANSHEE)
                call CampaignAttackerEx(0, 1, 1, NECRO)
                call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_MARDENHOLDE_CENTER_START_X, ATTACK_WAVE_MARDENHOLDE_CENTER_START_Y)
            endif
            exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide

        endloop
       
        // Make sure to leave both loops
        exitwhen AttackWaveAttackUser != AttackUser or AttackSuicide != AttackWaveAttackSuicide
       
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
   
endfunction

function AttackWavesAttackSuicide takes nothing returns nothing
    local boolean AttackWaveAttackSuicide = AttackSuicide
    local integer UserId = GetPlayerId(User)
   
    call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
   
    // Reset the assault group
    call InitAssaultGroup()
   
    // Launch attack continuously
    loop
       
        call SuicideUnitEx(1, GHOUL, UserId)
        call SuicideUnitEx(1, NECRO, UserId)
        call SuicideUnitEx(1, BANSHEE, UserId)
        call SuicideUnitEx(1, ABOMINATION, UserId)
        call SuicideUnitEx(1, MEAT_WAGON, UserId)
        call SuicideUnitEx(1, SHADE, UserId)
       
        call Sleep(2)
        exitwhen AttackSuicide != AttackWaveAttackSuicide
   
    endloop
   
    call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
   
endfunction

function AttackLoop takes nothing returns nothing

    loop
       
        // If suicide is set, execute the attack suicide loop
        if AttackSuicide then
            call AttackWavesAttackSuicide()
        else
            // Attack the user if required
            if AttackUser then
                call AttackWavesAttackUser()
            endif
        endif
          
        // Wait a little time before looping again
        call Sleep(10)
   
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
   
    // Check if there is any command waiting
   
    loop
   
        exitwhen CommandsWaiting() <= 0
  
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
       
        // Update attack suicide behaviour command (to start/stop suicide, always against the user)
        if Command == COMMAND_ATTACK_SUICIDE then
            if Data == DATA_ATTACK_SUICIDE_ENABLE then
                set AttackSuicide = true
                call AdvSetSuicide(true)
            elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
                set AttackSuicide = false
                call AdvSetSuicide(false)
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
            endif
           
        // Update weaken behavior (reduces attack rates, units and defenders)
        elseif Command == COMMAND_WEAKEN then
            if Data == DATA_WEAKEN_ENABLE then
                set Weaken = true
            elseif Data == DATA_WEAKEN_DISABLE then
                set Weaken = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid weaken command data received!")
            endif
            // Reset defenders
            call InitDefenseGroup()      
            call SetDefenders()
           
        // Update attack user behaviour command (to start/stop attack against the user)       
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
                call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!")
            endif
           
        else
            call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
           
        endif
  
    endloop
   
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)   
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
   
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
   
    // Define AI properties   
    call DoCampaignFarms(false)
    call GroupTimedLife(true)
    call SetReplacements(1, 1, 1)
    call AdvSetContinueAttackReducePercentageIfFarAway(false)
    call AdvSetWoodPeonsWarriors(true)
   
    // MARDENHOLDE KEEP
    call AdvAddPreferredLocation(-6740.0, 2450.0)
   
    // Define build order
    call SetBuildOrder()
   
    // Start to research upgrades at timed intervals
    call StartThread(function ResearchUpgrades)
   
    // Define defenders
    call SetDefenders()
   
    // Set defense captain place to avoid cluttering in shared base
    call AdvSetDefenseCaptainHomeXY(1350, -2750)
   
    // Start command loop for external signals   
    call StartThread(function CommandLoop)
   
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
       
endfunction
 
Last edited:
Sharing all new scripts from Warcraft 3 Re-Reforged: The Scourge of Lordaeron Act II. These scripts all require JASS AI 2.0.


JASS:
//============================================================================
//  RR Human 06 -- Undead Scourge Green Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//============================================================================

// GLOBALS 
//------------------------------------------------
globals

	// Tech tree
	
	constant integer SKELETON_WARRIOR = 'uske'
	constant integer SKELETON_MAGE = 'uskm'
	constant integer SKELETON_ARCHER = 'nska'
	
	// Behaviour
	
	constant real HOME_ALTAR_X = 8500.0
	constant real HOME_ALTAR_Y = 7000.0
	
    real AttackTargetX = 0
    real AttackTargetY = 0
	
	// Commands
	
	constant integer COMMAND_RETURN_HOME = 0
	constant integer COMMAND_ATTACK_TARGET = 1

    constant integer COMMAND_SET_ATTACK_TARGET_X = 2
	constant integer COMMAND_SET_ATTACK_TARGET_Y = 3

endglobals

// GROUP GATHER LOOP
//------------------------------------------------
function AddAllUnitsOfType takes integer unitId returns nothing

    call AddAssault(GetUnitCount(unitId), unitId)
	
endfunction

function GatherGroup takes nothing returns nothing

    loop
	
		// Add all possible units to group
		
        call AddAllUnitsOfType(MALGANIS)
        call AddAllUnitsOfType(GHOUL)
        call AddAllUnitsOfType(ABOMINATION)
        call AddAllUnitsOfType(SKELETON_WARRIOR)
		call AddAllUnitsOfType(SKELETON_MAGE)
		call AddAllUnitsOfType(SKELETON_ARCHER)
        call AddAllUnitsOfType(NECRO)
		
        call Sleep(1)
		
    endloop
	
endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// Note: in this script, we directly execute commands here
		
		// Attack the target (building or citizen)
		if Command == COMMAND_RETURN_HOME then
		
			// Teleport captain home
			call TeleportCaptain(HOME_ALTAR_X, HOME_ALTAR_Y)
			
			// Remove all previous captain targets
			call ClearCaptainTargets()
			
			// Make sure captain moves towards home
			call SetCaptainHome(ATTACK_CAPTAIN, HOME_ALTAR_X, HOME_ALTAR_Y)
			call CaptainAttack(HOME_ALTAR_X, HOME_ALTAR_Y)
		
		// Return to the altar home
		elseif Command == COMMAND_ATTACK_TARGET then
	
			// Teleport captain to target
			call TeleportCaptain(AttackTargetX, AttackTargetY)
			
			// Remove all previous captain targets
			call ClearCaptainTargets()
			
			// Move towards target
			call SetCaptainHome(ATTACK_CAPTAIN, AttackTargetX, AttackTargetY)
			call CaptainAttack(AttackTargetX, AttackTargetY)
			
		// Set attack target X (data is the position X) -> this must be called after we command an attack without waits in between
		elseif Command == COMMAND_SET_ATTACK_TARGET_X then
		
			set AttackTargetX = Data
			
		// Set attack target Y (data is the position Y) -> this must be called after we command an attack without waits in between
		elseif Command == COMMAND_SET_ATTACK_TARGET_Y then
		
			set AttackTargetY = Data
					
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    loop
        call CommandFetch()
        call Sleep(0.5)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	set campaign_wood_peons = 0
	
	// Always keep gathering the group in the background
	call StartThread(function GatherGroup)
    
    // Start command loop for external signals, it reacts accordingly	
    call CommandLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 06 -- Undead Scourge Orange Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = -7500.0
	constant real ATTACK_WAVE_START_Y = -6000.0
        
    boolean AttackUser = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(1, 1, 1, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
	call SetBuildUnitEx(1, 1, 1, CRYPT)
	call SetBuildUnitEx(2, 2, 2, ZIGGURAT_1)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(2, 2, 2, CRYPT)
	call SetBuildUnitEx(4, 4, 4, ZIGGURAT_1)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(1, 1, 1, SLAUGHTERHOUSE)
	call SetBuildUnitEx(1, 1, 1, DAMNED_TEMPLE)
	call SetBuildUnitEx(2, 2, 2, ZIGGURAT_2)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)
	call SetBuildUnitEx(1, 1, 1, SAC_PIT)


endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(4, 4, 4, GHOUL)
	call CampaignDefenderEx(2, 2, 3, NECRO)
	call CampaignDefenderEx(0, 1, 1, ABOMINATION)
		
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

	call Sleep(M3)

	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)
	call SetBuildUpgrEx(1, 1, 1, UPG_EXHUME)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(0, 1, 1, UPG_BANSHEE)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(0, 0, 1, UPG_GHOUL_FRENZY)
	call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
	call SetBuildUpgrEx(0, 1, 2, UPG_SKEL_MASTERY)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 1, 2, UPG_NECROS)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(0, 2, 2, UPG_BANSHEE)
	call SetBuildUpgrEx(1, 1, 1, UPG_PLAGUE)
	
	call Sleep(M4)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_ARMOR)
	
	call Sleep(M4)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_STR)
	call SetBuildUpgrEx(1, 1, 1, UPG_SKEL_LIFE)
		
endfunction

// ATTACK LOOP
//------------------------------------------------
// Note: attack times are the same across all difficulties because 
// players have to face attacks at same time no matter the difficulty chosen 
function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(true)
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GHOUL)
		call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 3, 4, GHOUL)
		call CampaignAttackerEx(1, 1, 1, ABOMINATION)
		call CampaignAttackerEx(1, 2, 2, NECRO)
		call CampaignAttackerEx(1, 1, 1, SHADE)
		call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 5, GHOUL)
		call CampaignAttackerEx(2, 2, 2, BANSHEE)
		call CampaignAttackerEx(2, 2, 3, MEAT_WAGON)
		call AdvSuicideOnPlayerEx(M4, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		loop
		
			// *** WAVE 4 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 3, GHOUL)
			call CampaignAttackerEx(1, 1, 2, ABOMINATION)
			call CampaignAttackerEx(1, 2, 2, NECRO)
			call CampaignAttackerEx(1, 1, 1, SHADE)
			call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser
			
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 3, ABOMINATION)
			call CampaignAttackerEx(1, 2, 3, BANSHEE)
			call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 5, 6, GHOUL)
			call CampaignAttackerEx(2, 3, 3, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackLoop takes nothing returns nothing

    loop
        
		// If attack user is set, execute the attack user loop
		if AttackUser then
			call AttackWavesAttackUser()
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(1, 1, 1)
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetWoodPeonsWarriors(true)
	
	// Define build order
	call SetBuildOrder()
	
	// Start to research upgrades at timed intervals
	call StartThread(function ResearchUpgrades)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 06 -- Undead Scourge Purple Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = 10000.0
	constant real ATTACK_WAVE_START_Y = -5750.0
        
    boolean AttackUser = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(1, 1, 1, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
	call SetBuildUnitEx(1, 1, 1, CRYPT)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(2, 2, 2, SLAUGHTERHOUSE)
	call SetBuildUnitEx(1, 1, 1, DAMNED_TEMPLE)
	call SetBuildUnitEx(1, 1, 1, ZIGGURAT_2)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)
	call SetBuildUnitEx(1, 1, 1, SAC_PIT)


endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(3, 4, 5, GHOUL)
	call CampaignDefenderEx(3, 3, 3, NECRO)
	call CampaignDefenderEx(1, 1, 1, ABOMINATION)
		
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

	call Sleep(M3)

	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)
	call SetBuildUpgrEx(0, 1, 2, UPG_SKEL_MASTERY)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(0, 0, 1, UPG_GHOUL_FRENZY)
	call SetBuildUpgrEx(0, 1, 1, UPG_BANSHEE)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
	call SetBuildUpgrEx(1, 1, 1, UPG_EXHUME)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 1, 2, UPG_BANSHEE)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_NECROS)
	call SetBuildUpgrEx(1, 1, 1, UPG_PLAGUE)
	
	call Sleep(M4)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_STR)
	
	call Sleep(M4)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_ARMOR)
	call SetBuildUpgrEx(1, 1, 1, UPG_SKEL_LIFE)
		
endfunction

// ATTACK LOOP
//------------------------------------------------
// Note: attack times are the same across all difficulties because 
// players have to face attacks at same time no matter the difficulty chosen 
function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(true)
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(2, 3, 4, ABOMINATION)
		call CampaignAttackerEx(1, 1, 1, SHADE)
		call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(2, 3, 3, NECRO)
		call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 5, GHOUL)
		call CampaignAttackerEx(2, 2, 2, BANSHEE)
		call CampaignAttackerEx(2, 2, 3, MEAT_WAGON)
		call AdvSuicideOnPlayerEx(M4, M4, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		loop
		
			// *** WAVE 4 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 4, ABOMINATION)
			call CampaignAttackerEx(2, 3, 3, NECRO)
			call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser
			
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 5, 6, GHOUL)
			call CampaignAttackerEx(2, 3, 3, BANSHEE)
			call CampaignAttackerEx(1, 1, 1, SHADE)
			call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 4, ABOMINATION)
			call CampaignAttackerEx(2, 3, 3, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackLoop takes nothing returns nothing

    loop
        
		// If attack user is set, execute the attack user loop
		if AttackUser then
			call AttackWavesAttackUser()
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(1, 1, 1)
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetWoodPeonsWarriors(true)
	
	// Define build order
	call SetBuildOrder()
	
	// Start to research upgrades at timed intervals
	call StartThread(function ResearchUpgrades)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 07 -- Dwarven Expedition Gray Dwarfs -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Tech tree

	constant integer D_MINER = 'hDmi'
	constant integer D_GUARDSMAN = 'hDfo'
	constant integer D_RIFLEMAN = 'hDri'
	constant integer D_CURATOR = 'hDrm'
	constant integer D_MYSTIC = 'hDmy'
	constant integer D_RAM_RIDER = 'hDkn'
	constant integer D_GYRO = 'hDgy'
	constant integer D_MORTAR = 'hDmt'
	constant integer D_STEAM_TANK = 'hDtt'
	constant integer D_GRYPHON = 'hDgr'
	
	constant integer D_HOLD = 'hDto'
	constant integer D_KEEP = 'hDke'
	constant integer D_CITADEL = 'hDca'
	constant integer D_ALTAR = 'hDal'
	constant integer D_BARRACKS = 'hDba'
	constant integer D_LUMBERMILL = 'hDlu'
	constant integer D_FORGE = 'hDbl'
	constant integer D_WORKSHOP = 'hDwk'
	constant integer D_ARCANE_TOWER = 'hDar'
	constant integer D_AVIARY = 'hDga'
	constant integer D_BUNKER = 'hDbk'
	constant integer D_BUNKER_CANNON = 'hDbc'
	
	constant integer D_UPG_MELEE = 'RDme'
	constant integer D_UPG_RANGED = 'RDra'
	constant integer D_UPG_ARMOR = 'RDar'
	constant integer D_UPG_MASONRY = 'RDac'
    constant integer D_UPG_DEFEND = 'RDde'
    constant integer D_UPG_LEATHER = 'RDla'
	constant integer D_UPG_GUN_RANGE = 'RDri'
	constant integer D_UPG_BOMBS = 'RDgb'
	constant integer D_UPG_BREEDING = 'RDan'
	constant integer D_UPG_FLARE = 'RDfl'
	constant integer D_UPG_HAMMERS = 'RDhb'
	constant integer D_UPG_FRAGS = 'RDfs'
	constant integer D_UPG_TANKS = 'RDrt'
	constant integer D_UPG_FLAK = 'RDfc'
	constant integer D_UPG_PULVERIZE = 'RDow'
	constant integer D_UPG_MYSTIC = 'RDmy'
	constant integer D_UPG_CURATOR = 'RDrm'	
	
	// Players

    player Scourge = PlayerEx(7)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = -5700.0
	constant real ATTACK_WAVE_START_Y = -900.0
        
    boolean AttackScourge = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_SCOURGE = 0
    constant integer DATA_ATTACK_SCOURGE_ENABLE = 1
    constant integer DATA_ATTACK_SCOURGE_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
// Build order is always the same no matter difficulty, except for cannons which use an inverse scaling
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, D_MINER)
	call SetBuildUnitEx(1, 1, 1, D_KEEP)
	call SetBuildUnitEx(1, 1, 1, D_BUNKER)
	call SetBuildUnitEx(8, 8, 8, D_MINER)
	call SetBuildUnitEx(1, 1, 1, D_ALTAR)
	call SetBuildUnitEx(1, 1, 1, D_BARRACKS)
	call SetBuildUnitEx(2, 2, 2, D_BUNKER)
	call SetBuildUnitEx(1, 1, 1, D_LUMBERMILL)
	call SetBuildUnitEx(1, 1, 1, D_FORGE)
	call SetBuildUnitEx(1, 1, 1, D_WORKSHOP)
	call SetBuildUnitEx(1, 1, 1, D_ARCANE_TOWER)
	call SetBuildUnitEx(2, 1, 0, D_BUNKER_CANNON)

endfunction

// DEFENDERS
//------------------------------------------------
// Defenders are always the same no matter difficulty
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(2, 2, 2, D_GUARDSMAN)
	call CampaignDefenderEx(1, 1, 1, D_RIFLEMAN)
	call CampaignDefenderEx(1, 1, 1, MTN_KING)
		
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
// Upgrades are always the same no matter difficulty
function ResearchUpgrades takes nothing returns nothing

	call Sleep(M15)
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_MELEE)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_DEFEND)
	
	call Sleep(M4)
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_RANGED)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_BOMBS)
	
	call Sleep(M4)
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_ARMOR)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_MYSTIC)
	
	call Sleep(M4)
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_LEATHER)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_CURATOR)
	
endfunction

// ATTACK LOOP
//------------------------------------------------
// Attack waves are always the same no matter difficulty
function AttackWavesAttackScourge takes nothing returns nothing
	local boolean AttackWaveAttackScourge = AttackScourge
	
	call AdvDebugDisplayToPlayer("AI Info: attacking Scourge...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 4, D_GUARDSMAN)
		call AdvSuicideOnPlayerEx(M5, M5, M5, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackScourge != AttackScourge
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 3, 3, D_GUARDSMAN)
		call CampaignAttackerEx(2, 2, 2, D_RIFLEMAN)
		call AdvSuicideOnPlayerEx(M5, M5, M5, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackScourge != AttackScourge
		
		loop
		
			// *** WAVE 3 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 2, 2, D_GUARDSMAN)
			call CampaignAttackerEx(1, 1, 1, D_RIFLEMAN)
			call CampaignAttackerEx(1, 1, 1, D_MYSTIC)
			call CampaignAttackerEx(1, 1, 1, D_CURATOR)
			call CampaignAttackerEx(1, 1, 1, MTN_KING)
			call AdvSuicideOnPlayerEx(M5, M5, M5, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackScourge != AttackScourge
			
			// *** WAVE 4 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 2, 2, D_GUARDSMAN)
			call CampaignAttackerEx(2, 2, 2, D_RIFLEMAN)
			call CampaignAttackerEx(1, 1, 1, D_RAM_RIDER)
			call CampaignAttackerEx(1, 1, 1, D_MORTAR)
			call AdvSuicideOnPlayerEx(M5, M5, M5, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackScourge != AttackScourge
			
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 5, 5, D_GYRO)
			call AdvSuicideOnPlayerEx(M5, M5, M5, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackScourge != AttackScourge

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackScourge != AttackScourge
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking Scourge!")
	
endfunction


function AttackLoop takes nothing returns nothing

    loop
        
		// If attack are set, execute the requested attack loop	
		if AttackScourge then
			call AttackWavesAttackScourge()
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack scourge behaviour command (to start/stop attack against the scourge)		
        elseif Command == COMMAND_ATTACK_SCOURGE then
            if Data == DATA_ATTACK_SCOURGE_ENABLE then
                set AttackScourge = true
            elseif Data == DATA_ATTACK_SCOURGE_DISABLE then
                set AttackScourge = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack scourge command data received!") 
            endif 
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(D_BUNKER, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(2, 2, 2)
	call AdvSetWoodPeonsWarriors(false)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(true)
	
	// Define build order
	call SetBuildOrder()
	
	// Start to research upgrades at timed intervals
	call StartThread(function ResearchUpgrades)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 07 -- Undead Scourge Green Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	player Dwarfs = PlayerEx(9)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = 0.0
	constant real ATTACK_WAVE_START_Y = 1250.0
        
    boolean AttackUser = false
	boolean AttackDwarfs = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_ATTACK_DWARFS = 1
    constant integer DATA_ATTACK_DWARFS_ENABLE = 1
    constant integer DATA_ATTACK_DWARFS_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(6, 6, 6, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
	call SetBuildUnitEx(1, 2, 3, CRYPT)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(1, 2, 2, SLAUGHTERHOUSE)
	call SetBuildUnitEx(1, 1, 2, DAMNED_TEMPLE)
	call SetBuildUnitEx(2, 3, 4, ZIGGURAT_2)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)
	call SetBuildUnitEx(1, 1, 1, SAC_PIT)


endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(3, 4, 5, GHOUL)
	call CampaignDefenderEx(1, 1, 1, NECRO)
	call CampaignDefenderEx(1, 1, 1, BANSHEE)
	call CampaignDefenderEx(1, 1, 1, ABOMINATION)
	call CampaignDefenderEx(1, 1, 1, SHADE)
	call CampaignDefenderEx(1, 1, 1, LICH)
		
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

	call Sleep(M14)

	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ATTACK)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)	
	call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
	call SetBuildUpgrEx(1, 1, 1, UPG_PLAGUE)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ARMOR)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(0, 0, 1, UPG_GHOUL_FRENZY)
	call SetBuildUpgrEx(0, 1, 1, UPG_BANSHEE)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_NECROS)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_CR_ATTACK)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
	call SetBuildUpgrEx(1, 1, 1, UPG_SKEL_MASTERY)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(1, 1, 2, UPG_BANSHEE)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_CR_ARMOR)
	call SetBuildUpgrEx(1, 1, 1, UPG_EXHUME)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_STR)
	call SetBuildUpgrEx(0, 1, 1, UPG_SKEL_LIFE)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ATTACK)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_ARMOR)
	call SetBuildUpgrEx(1, 2, 2, UPG_SKEL_MASTERY)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ARMOR)
	
		
endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUserDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User and Dwarfs...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GHOUL)
		call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, ABOMINATION)
		call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
		call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(5, 6, 7, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		loop
		
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 2, 3, ABOMINATION)
			call CampaignAttackerEx(2, 3, 3, NECRO)
			call CampaignAttackerEx(2, 2, 3, BANSHEE)
			call CampaignAttackerEx(1, 2, 2, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, LICH)
			call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 5, 6, GHOUL)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(1, 1, 1, SHADE)
			call CampaignAttackerEx(1, 2, 2, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 4, GHOUL)
			call CampaignAttackerEx(1, 2, 3, ABOMINATION)
			call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 1, LICH)
			call AdvSuicideOnPlayerEx(M3, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User and Dwarfs!")
	
endfunction

function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	
	loop 
		
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		loop
			
			// *** WAVE 2 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 4, GHOUL)
			call CampaignAttackerEx(1, 2, 3, ABOMINATION)
			call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 1, LICH)
			call AdvSuicideOnPlayerEx(M3, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackWavesAttackDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	
	call AdvDebugDisplayToPlayer("AI Info: attacking Dwarfs...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GHOUL)
		call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, ABOMINATION)
		call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
		call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(5, 6, 7, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		loop
		
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 2, 3, ABOMINATION)
			call CampaignAttackerEx(2, 3, 3, NECRO)
			call CampaignAttackerEx(2, 2, 3, BANSHEE)
			call CampaignAttackerEx(1, 2, 2, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, LICH)
			call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 5, 6, GHOUL)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(1, 1, 1, SHADE)
			call CampaignAttackerEx(1, 2, 2, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking Dwarfs!")
	
endfunction

function AttackLoop takes nothing returns nothing

    loop
        
		// If attack are set, execute the requested attack loop	
		if AttackUser and AttackDwarfs then
			call AttackWavesAttackUserDwarfs()
		
		// If at least one of the two is not true, then check them individually
		else
			// If attack only user
			if AttackUser then
				call AttackWavesAttackUser()		
			// If attack only dwarfs
			elseif AttackDwarfs then
				call AttackWavesAttackDwarfs()	
			endif
			
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
		// Update attack dwarfs behaviour command (to start/stop attack against the dwarfs)		
        elseif Command == COMMAND_ATTACK_DWARFS then
            if Data == DATA_ATTACK_DWARFS_ENABLE then
                set AttackDwarfs = true
            elseif Data == DATA_ATTACK_DWARFS_DISABLE then
                set AttackDwarfs = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack dwarfs command data received!") 
            endif 
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(2, 3, 4)
	call AdvSetWoodPeonsWarriors(true)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(false)
	
	// Define build order
	call SetBuildOrder()
	
	// Start to research upgrades at timed intervals
	call StartThread(function ResearchUpgrades)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 07 -- Undead Scourge Lavender Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals

	// Tech tree
	
	constant integer ABOMINATION_NR = 'u003'
	
	constant integer SLAUGHTERHOUSE_NR = 'u000'
	constant integer DAMNED_TEMPLE_NR = 'u001'
	constant integer ZIGGURAT_2_NR = 'u002'
	
	// Players

    player User = PlayerEx(2)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = -6200.0
	constant real ATTACK_WAVE_START_Y = -4700.0
        
    boolean AttackUser = false
	boolean UseGraveyard = false
	boolean UseBlackCitadel = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_GRAVEYARD = 1
	constant integer DATA_GRAVEYARD_ENABLE = 1
    constant integer DATA_GRAVEYARD_DISABLE = 0
	
	constant integer COMMAND_BLACK_CITADEL = 2
	constant integer DATA_BLACK_CITADEL_ENABLE = 1
    constant integer DATA_BLACK_CITADEL_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(1, 1, 1, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, CRYPT)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)

	if UseGraveyard then
		call SetBuildUnitEx(1, 1, 1, SLAUGHTERHOUSE_NR)
		call SetBuildUnitEx(0, 1, 1, ZIGGURAT_2_NR)
	endif

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(2, 3, 4, GHOUL)
	
	if UseBlackCitadel then
		call CampaignDefenderEx(1, 1, 1, ABOMINATION_NR)
	endif
		
endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUserWithAbomination takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveUseBlackCitadel = UseBlackCitadel
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User with abominations...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveUseBlackCitadel != UseBlackCitadel
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(2, 2, 3, GHOUL)
		call CampaignAttackerEx(1, 1, 2, ABOMINATION_NR)
		call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveUseBlackCitadel != UseBlackCitadel
		
		loop
		
			// *** WAVE 3 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GHOUL)
			call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveUseBlackCitadel != UseBlackCitadel
			
			// *** WAVE 4 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 4, GHOUL)
			call CampaignAttackerEx(1, 2, 2, ABOMINATION_NR)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveUseBlackCitadel != UseBlackCitadel

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveUseBlackCitadel != UseBlackCitadel
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User with abominations!")
	
endfunction

function AttackWavesAttackUserWithoutAbomination takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveUseBlackCitadel = UseBlackCitadel
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User without abominations...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveUseBlackCitadel != UseBlackCitadel
		
		loop
		
			// *** WAVE 2 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GHOUL)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveUseBlackCitadel != UseBlackCitadel

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveUseBlackCitadel != UseBlackCitadel
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User without abominations!")
	
endfunction


function AttackLoop takes nothing returns nothing

    loop
        
		// If attack are set, execute the requested attack loop	
		if AttackUser then
			// Attack with abominations or not depending on the current condition
            if UseBlackCitadel then
                call AttackWavesAttackUserWithAbomination()            
            else
                call AttackWavesAttackUserWithoutAbomination()
            endif
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
		// Update requirements about the graveyard (to enable certain buildings/units)			
		elseif Command == COMMAND_GRAVEYARD then
            if Data == DATA_GRAVEYARD_ENABLE then
                set UseGraveyard = true
            elseif Data == DATA_GRAVEYARD_DISABLE then
                set UseGraveyard = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid graveyard command data received!") 
            endif 
			// Reset build order and defenders
			call InitBuildArray()
			call InitDefenseGroup()
			call SetBuildOrder()         
			call SetDefenders()
			
		// Update requirements about the black citadel (to enable certain buildings/units)			
		elseif Command == COMMAND_BLACK_CITADEL then
            if Data == DATA_BLACK_CITADEL_ENABLE then
                set UseBlackCitadel = true
            elseif Data == DATA_BLACK_CITADEL_DISABLE then
                set UseBlackCitadel = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid black citadel command data received!") 
            endif 
			// Reset build order and defenders
			call InitBuildArray()
			call InitDefenseGroup()
			call SetBuildOrder()         
			call SetDefenders()
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(1, 2, 3)
	call AdvSetWoodPeonsWarriors(true)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(false)
	
	// Define build order
	call SetBuildOrder()
	
	// Note: upgrades are not required since they are copied from the parent player
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 07 -- Undead Scourge Orange Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = -11700.0
	constant real ATTACK_WAVE_START_Y = -12700.0
        
    boolean AttackUser = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(2, 3, 4, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
	call SetBuildUnitEx(1, 1, 2, CRYPT)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(1, 1, 1, SLAUGHTERHOUSE)
	call SetBuildUnitEx(1, 2, 3, ZIGGURAT_FROST)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)


endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(2, 3, 4, CRYPT_FIEND)
	call CampaignDefenderEx(1, 1, 1, CRYPT_LORD)
		
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

	call Sleep(M12)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ARMOR)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ATTACK)
	call SetBuildUpgrEx(1, 1, 1, UPG_FIEND_WEB)
	
	call Sleep(M3)

	call SetBuildUpgrEx(1, 2, 2, UPG_CR_ARMOR)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_CR_ATTACK)
	call SetBuildUpgrEx(0, 1, 1, UPG_BURROWING)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ARMOR)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ATTACK)
	call SetBuildUpgrEx(0, 0, 1, UPG_CANNIBALIZE)
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(0, 1, 1, UPG_UNHOLY_ARMOR)	
	
	call Sleep(M3)
	
	call SetBuildUpgrEx(0, 0, 1, UPG_UNHOLY_STR)
	
		
endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	
	loop 
		
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(2, 3, 4, CRYPT_FIEND)
		call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(2, 3, 4, CRYPT_FIEND)
		call CampaignAttackerEx(1, 1, 1, CRYPT_LORD)
		call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(1, 2, 3, BLK_SPHINX)
		call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		loop
			
			// *** WAVE 4 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, CRYPT_FIEND)
			call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser
		
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 4, CRYPT_FIEND)
			call CampaignAttackerEx(1, 1, 1, BLK_SPHINX)
			call CampaignAttackerEx(1, 1, 1, CRYPT_LORD)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 4, CRYPT_FIEND)
			call CampaignAttackerEx(1, 1, 1, BLK_SPHINX)
			call AdvSuicideOnPlayerEx(M2, M2, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser
		
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 4, BLK_SPHINX)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackLoop takes nothing returns nothing

    loop
        
		// If attack are set, execute the requested attack loop	
		if AttackUser then
			call AttackWavesAttackUser()	
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(1, 1, 2)
	call AdvSetWoodPeonsWarriors(true)
		
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(true)
	
	// Define build order
	call SetBuildOrder()
	
	// Start to research upgrades at timed intervals
	call StartThread(function ResearchUpgrades)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 07 -- Undead Scourge Purple Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	player Dwarfs = PlayerEx(9)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = -5700.0
	constant real ATTACK_WAVE_START_Y = -900.0
        
    boolean AttackUser = false
	boolean AttackDwarfs = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_ATTACK_DWARFS = 1
    constant integer DATA_ATTACK_DWARFS_ENABLE = 1
    constant integer DATA_ATTACK_DWARFS_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(2, 3, 4, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
	call SetBuildUnitEx(1, 2, 2, CRYPT)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(1, 1, 2, SLAUGHTERHOUSE)
	call SetBuildUnitEx(1, 1, 1, DAMNED_TEMPLE)
	call SetBuildUnitEx(1, 2, 3, ZIGGURAT_2)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)
	call SetBuildUnitEx(1, 1, 1, SAC_PIT)


endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(2, 2, 2, GHOUL)
	call CampaignDefenderEx(1, 1, 2, NECRO)
	call CampaignDefenderEx(1, 2, 2, BANSHEE)
		
endfunction

// RESEARCH UPGRADES
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing

	call Sleep(M13)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ATTACK)
	call SetBuildUpgrEx(0, 1, 1, UPG_SKEL_MASTERY)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ARMOR)
	call SetBuildUpgrEx(0, 1, 1, UPG_BANSHEE)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)	
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(0, 0, 1, UPG_GHOUL_FRENZY)
	call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_CR_ATTACK)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(1, 1, 2, UPG_BANSHEE)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_CR_ARMOR)
	call SetBuildUpgrEx(1, 1, 1, UPG_EXHUME)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
	call SetBuildUpgrEx(1, 1, 1, UPG_SKEL_LIFE)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_NECROS)
	
	call Sleep(M1)
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
	call SetBuildUpgrEx(0, 1, 2, UPG_SKEL_MASTERY)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ATTACK)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_STR)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ARMOR)
	call SetBuildUpgrEx(1, 1, 1, UPG_PLAGUE)
	
	call Sleep(M2)
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_ARMOR)
	
		
endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUserDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User and Dwarfs...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GHOUL)
		call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 4, GHOUL)
		call CampaignAttackerEx(1, 1, 2, NECRO)
		call CampaignAttackerEx(1, 2, 2, BANSHEE)
		call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GARGOYLE)
		call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M3, M3, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		loop
		
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 3, ABOMINATION)
			call CampaignAttackerEx(2, 2, 3, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 4, GHOUL)
			call CampaignAttackerEx(1, 1, 2, NECRO)
			call CampaignAttackerEx(1, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 1, SHADE)
			call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GARGOYLE)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GARGOYLE)
			call AdvSuicideOnPlayerEx(M2, M2, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User and Dwarfs!")
	
endfunction

function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User..")
	
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GHOUL)
		call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 4, GHOUL)
		call CampaignAttackerEx(1, 1, 2, NECRO)
		call CampaignAttackerEx(1, 2, 2, BANSHEE)
		call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GARGOYLE)
		call AdvSuicideOnPlayerEx(M2, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		loop
		
			// *** WAVE 4 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 3, ABOMINATION)
			call CampaignAttackerEx(2, 2, 3, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 4, GHOUL)
			call CampaignAttackerEx(1, 1, 2, NECRO)
			call CampaignAttackerEx(1, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 1, SHADE)
			call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GARGOYLE)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackWavesAttackDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	
	call AdvDebugDisplayToPlayer("AI Info: attacking Dwarfs...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M3, M3, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
		loop
			
			// *** WAVE 2 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GARGOYLE)
			call AdvSuicideOnPlayerEx(M3, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking Dwarfs!")
	
endfunction


function AttackLoop takes nothing returns nothing

    loop
        
		// If attack are set, execute the requested attack loop	
		if AttackUser and AttackDwarfs then
			call AttackWavesAttackUserDwarfs()
		
		// If at least one of the two is not true, then check them individually
		else
			// If attack only user
			if AttackUser then
				call AttackWavesAttackUser()		
			// If attack only dwarfs
			elseif AttackDwarfs then
				call AttackWavesAttackDwarfs()	
			endif
			
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
		// Update attack dwarfs behaviour command (to start/stop attack against the dwarfs)		
        elseif Command == COMMAND_ATTACK_DWARFS then
            if Data == DATA_ATTACK_DWARFS_ENABLE then
                set AttackDwarfs = true
            elseif Data == DATA_ATTACK_DWARFS_DISABLE then
                set AttackDwarfs = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack dwarfs command data received!") 
            endif 
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(1, 2, 2)
	call AdvSetWoodPeonsWarriors(true)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(false)
	
	// Define build order
	call SetBuildOrder()
	
	// Start to research upgrades at timed intervals
	call StartThread(function ResearchUpgrades)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 07 -- Undead Scourge Violet Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Tech tree
	
	constant integer ABOMINATION_NR = 'u003'
	
	constant integer SLAUGHTERHOUSE_NR = 'u000'
	constant integer DAMNED_TEMPLE_NR = 'u001'
	constant integer ZIGGURAT_2_NR = 'u002'

	// Players

    player User = PlayerEx(2)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = -10000.0
	constant real ATTACK_WAVE_START_Y = -1700.0
        
    boolean AttackUser = false
	boolean UseGraveyard = false
	boolean UseBlackCitadel = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_GRAVEYARD = 1
	constant integer DATA_GRAVEYARD_ENABLE = 1
    constant integer DATA_GRAVEYARD_DISABLE = 0
	
	constant integer COMMAND_BLACK_CITADEL  = 2
	constant integer DATA_BLACK_CITADEL_ENABLE = 1
    constant integer DATA_BLACK_CITADEL_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(1, 1, 1, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, CRYPT)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	
	if UseGraveyard then
		call SetBuildUnitEx(1, 1, 1, DAMNED_TEMPLE_NR)
		call SetBuildUnitEx(0, 1, 1, ZIGGURAT_2_NR)	
	endif

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(2, 3, 3, GHOUL)
	call CampaignDefenderEx(1, 1, 2, NECRO)
		
endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 3, 4, GHOUL)
		call CampaignAttackerEx(1, 2, 2, NECRO)
		call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser
		
		loop
		
			// *** WAVE 3 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GHOUL)
			call AdvSuicideOnPlayerEx(M3, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser
			
			// *** WAVE 4 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 3, 3, NECRO)
			call AdvSuicideOnPlayerEx(M3, M3, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction


function AttackLoop takes nothing returns nothing

    loop
        
		// If attack are set, execute the requested attack loop	
		if AttackUser then
			call AttackWavesAttackUser()
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
		
		// Update requirements about the graveyard (to enable certain buildings/units)			
		elseif Command == COMMAND_GRAVEYARD then
            if Data == DATA_GRAVEYARD_ENABLE then
                set UseGraveyard = true
            elseif Data == DATA_GRAVEYARD_DISABLE then
                set UseGraveyard = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid graveyard command data received!") 
            endif 
			// Reset build order and defenders
			call InitBuildArray()
			call InitDefenseGroup()
			call SetBuildOrder()         
			call SetDefenders()
			
		// Update requirements about the black citadel (to enable certain buildings/units)			
		elseif Command == COMMAND_BLACK_CITADEL then
            if Data == DATA_BLACK_CITADEL_ENABLE then
                set UseBlackCitadel = true
            elseif Data == DATA_BLACK_CITADEL_DISABLE then
                set UseBlackCitadel = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid black citadel command data received!") 
            endif 
			// Reset build order and defenders
			call InitBuildArray()
			call InitDefenseGroup()
			call SetBuildOrder()         
			call SetDefenders()
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(1, 2, 3)
	call AdvSetWoodPeonsWarriors(true)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(false)
	
	// Define build order
	call SetBuildOrder()
	
	// Note: upgrades are not required since they are copied from the parent player
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 09 -- Dwarven Expedition Gray Dwarfs -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Tech tree

	constant integer D_MINER = 'hDmi'
	constant integer D_GUARDSMAN = 'hDfo'
	constant integer D_RIFLEMAN = 'hDri'
	constant integer D_CURATOR = 'hDrm'
	constant integer D_MYSTIC = 'hDmy'
	constant integer D_RAM_RIDER = 'hDkn'
	constant integer D_GYRO = 'hDgy'
	constant integer D_MORTAR = 'hDmt'
	constant integer D_STEAM_TANK = 'hDtt'
	constant integer D_GRYPHON = 'hDgr'
	
	constant integer D_HOLD = 'hDto'
	constant integer D_KEEP = 'hDke'
	constant integer D_CITADEL = 'hDca'
	constant integer D_ALTAR = 'hDal'
	constant integer D_BARRACKS = 'hDba'
	constant integer D_LUMBERMILL = 'hDlu'
	constant integer D_FORGE = 'hDbl'
	constant integer D_WORKSHOP = 'hDwk'
	constant integer D_ARCANE_TOWER = 'hDar'
	constant integer D_AVIARY = 'hDga'
	constant integer D_BUNKER = 'hDbk'
	constant integer D_BUNKER_CANNON = 'hDbc'
	
	constant integer D_UPG_MELEE = 'RDme'
	constant integer D_UPG_RANGED = 'RDra'
	constant integer D_UPG_ARMOR = 'RDar'
	constant integer D_UPG_MASONRY = 'RDac'
    constant integer D_UPG_DEFEND = 'RDde'
    constant integer D_UPG_LEATHER = 'RDla'
	constant integer D_UPG_GUN_RANGE = 'RDri'
	constant integer D_UPG_BOMBS = 'RDgb'
	constant integer D_UPG_BREEDING = 'RDan'
	constant integer D_UPG_FLARE = 'RDfl'
	constant integer D_UPG_HAMMERS = 'RDhb'
	constant integer D_UPG_FRAGS = 'RDfs'
	constant integer D_UPG_TANKS = 'RDrt'
	constant integer D_UPG_FLAK = 'RDfc'
	constant integer D_UPG_PULVERIZE = 'RDow'
	constant integer D_UPG_MYSTIC = 'RDmy'
	constant integer D_UPG_CURATOR = 'RDrm'	
	
	// Players

    player Scourge = PlayerEx(4)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = 0.0
	constant real ATTACK_WAVE_START_Y = 1100.0
        
    boolean AttackScourge = false
	boolean Research = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_SCOURGE = 0
    constant integer DATA_ATTACK_SCOURGE_ENABLE = 1
    constant integer DATA_ATTACK_SCOURGE_DISABLE = 0
	
	constant integer COMMAND_RESEARCH = 1
	constant integer DATA_RESEARCH_ENABLE = 1
	constant integer DATA_RESEARCH_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
// Build order is always the same no matter difficulty
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, D_MINER)
	call SetBuildUnitEx(1, 1, 1, D_CITADEL)
	call SetBuildUnitEx(1, 1, 1, D_BUNKER)
	call SetBuildUnitEx(8, 8, 8, D_MINER)
	call SetBuildUnitEx(1, 1, 1, D_ALTAR)
	call SetBuildUnitEx(1, 1, 1, D_BARRACKS)
	call SetBuildUnitEx(3, 3, 3, D_BUNKER)
	call SetBuildUnitEx(1, 1, 1, D_LUMBERMILL)
	call SetBuildUnitEx(1, 1, 1, D_FORGE)
	call SetBuildUnitEx(1, 1, 1, D_WORKSHOP)
	call SetBuildUnitEx(1, 1, 1, D_ARCANE_TOWER)
	call SetBuildUnitEx(1, 1, 1, D_AVIARY)
	
endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(1, 0, 0, D_GUARDSMAN)
	call CampaignDefenderEx(1, 1, 0, D_RIFLEMAN)
	call CampaignDefenderEx(2, 2, 2, D_GRYPHON)
	call CampaignDefenderEx(2, 2, 2, D_GYRO)
	call CampaignDefenderEx(1, 1, 1, MTN_KING)
		
endfunction

// RESEARCH LOOP
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing
	local boolean ResearchUpgradesResearch = Research

	call Sleep(10)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_MELEE)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_DEFEND)
	
	call Sleep(M3)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_RANGED)
	call SetBuildUpgrEx(1, 1, 0, D_UPG_FLAK)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_BOMBS)
	
	call Sleep(M3)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_ARMOR)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_MYSTIC)
	
	call Sleep(M3)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_LEATHER)
	call SetBuildUpgrEx(1, 0, 0, D_UPG_FRAGS)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, D_UPG_CURATOR)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(2, 2, 2, D_UPG_CURATOR)
	call SetBuildUpgrEx(2, 2, 2, D_UPG_MELEE)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(2, 2, 2, D_UPG_RANGED)
	call SetBuildUpgrEx(2, 2, 2, D_UPG_MYSTIC)

endfunction

function ResearchLoop takes nothing returns nothing

    loop
        
        if Research then
			call ResearchUpgrades()
        endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// ATTACK LOOP
//------------------------------------------------
// Attack waves are always the same no matter difficulty
function AttackWavesAttackScourge takes nothing returns nothing
	local boolean AttackWaveAttackScourge = AttackScourge
	
	call AdvDebugDisplayToPlayer("AI Info: attacking Scourge...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 3, D_GUARDSMAN)
		call CampaignAttackerEx(2, 1, 1, D_RIFLEMAN)
		call AdvSuicideOnPlayerEx(M2, M2, M2, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackScourge != AttackScourge
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 2, 2, D_GUARDSMAN)
		call CampaignAttackerEx(2, 1, 1, D_RIFLEMAN)
		call CampaignAttackerEx(1, 1, 1, D_RAM_RIDER)
		call CampaignAttackerEx(1, 1, 1, D_MORTAR)	
		call AdvSuicideOnPlayerEx(M2, M2, M2, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackScourge != AttackScourge
		
		loop
		
			// *** WAVE 3 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 3, D_GUARDSMAN)
			call CampaignAttackerEx(2, 2, 2, D_RIFLEMAN)
			call CampaignAttackerEx(2, 1, 1, D_MYSTIC)
			call CampaignAttackerEx(2, 2, 1, D_CURATOR)
			call CampaignAttackerEx(1, 1, 1, MTN_KING)
			call AdvSuicideOnPlayerEx(M3, M3, M3, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackScourge != AttackScourge
			
			// *** WAVE 4 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 2, 2, D_GUARDSMAN)
			call CampaignAttackerEx(4, 4, 3, D_RIFLEMAN)
			call CampaignAttackerEx(1, 1, 1, D_RAM_RIDER)
			call CampaignAttackerEx(1, 1, 1, D_MORTAR)
			call AdvSuicideOnPlayerEx(M2, M2, M2, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackScourge != AttackScourge
			
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 4, 4, D_GYRO)
			call CampaignAttackerEx(3, 3, 2, D_GRYPHON)
			call AdvSuicideOnPlayerEx(M2, M2, M2, Scourge, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackScourge != AttackScourge

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackScourge != AttackScourge
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking Scourge!")
	
endfunction


function AttackLoop takes nothing returns nothing

    loop
        
		// If attack are set, execute the requested attack loop	
		if AttackScourge then
			call AttackWavesAttackScourge()
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
		
		// Update attack scourge behaviour command (to start/stop attack against the scourge)		
        elseif Command == COMMAND_ATTACK_SCOURGE then
            if Data == DATA_ATTACK_SCOURGE_ENABLE then
                set AttackScourge = true
            elseif Data == DATA_ATTACK_SCOURGE_DISABLE then
                set AttackScourge = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack scourge command data received!") 
            endif 
			
		// Update research behaviour
		// Note: if research is disabled/enabled there is a delay to reach the previous point
		elseif Command == COMMAND_RESEARCH then
			if Data == DATA_RESEARCH_ENABLE then
				set Research = true
			elseif Data == DATA_RESEARCH_DISABLE then
				set Research = false
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid research command data received!")
			endif  
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(D_BUNKER, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(2, 2, 2)
	call AdvSetWoodPeonsWarriors(false)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(true)
	
	// Define build order
	call SetBuildOrder()
	
	// Start research of upgrades at timed intervals as soon as possible (it depends on the commands received)
	call StartThread(function ResearchLoop)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 09 -- Undead Scourge Green Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	player Dwarfs = PlayerEx(9)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = 700.0
	constant real ATTACK_WAVE_START_Y = 6500.0
        
    boolean AttackUser = false
	boolean AttackDwarfs = false
	boolean Research = false
	boolean AttackSuicide = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_ATTACK_DWARFS = 1
    constant integer DATA_ATTACK_DWARFS_ENABLE = 1
    constant integer DATA_ATTACK_DWARFS_DISABLE = 0
	
	constant integer COMMAND_ATTACK_SUICIDE = 2
	constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
	constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
	
	constant integer COMMAND_RESEARCH = 3
	constant integer DATA_RESEARCH_ENABLE = 1
	constant integer DATA_RESEARCH_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(4, 4, 4, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
	call SetBuildUnitEx(2, 2, 2, CRYPT)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(1, 1, 1, TOMB_OF_RELICS)
	call SetBuildUnitEx(2, 2, 2, SLAUGHTERHOUSE)
	call SetBuildUnitEx(2, 2, 2, DAMNED_TEMPLE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)
	call SetBuildUnitEx(1, 1, 1, SAC_PIT)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(2, 3, 3, GHOUL)
	call CampaignDefenderEx(1, 1, 1, NECRO)
	call CampaignDefenderEx(1, 1, 1, BANSHEE)
	call CampaignDefenderEx(1, 1, 1, MEAT_WAGON)
	call CampaignDefenderEx(1, 1, 1, CRYPT_FIEND)
	call CampaignDefenderEx(1, 1, 2, ABOMINATION)
	call CampaignDefenderEx(1, 1, 1, SHADE)
	call CampaignDefenderEx(2, 2, 2, GARGOYLE)
	call CampaignDefenderEx(1, 1, 1, MALGANIS)
		
endfunction

// RESEARCH LOOP
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing
	local boolean ResearchUpgradesResearch = Research

	call Sleep(10)
	if ResearchUpgradesResearch != Research then
		return
	endif

	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ATTACK)

	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)	
	call SetBuildUpgrEx(1, 1, 1, UPG_BANSHEE)
	call SetBuildUpgrEx(1, 1, 1, UPG_PLAGUE)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ARMOR)
	call SetBuildUpgrEx(1, 1, 1, UPG_FIEND_WEB)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(0, 1, 1, UPG_GHOUL_FRENZY)
	call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 2, UPG_NECROS)
	call SetBuildUpgrEx(0, 1, 1, UPG_BURROWING)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 2, UPG_CR_ATTACK)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
	call SetBuildUpgrEx(1, 1, 1, UPG_SKEL_MASTERY)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 2, UPG_BANSHEE)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_STR)
	call SetBuildUpgrEx(0, 0, 1, UPG_EXHUME)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 2, UPG_CR_ARMOR)
	call SetBuildUpgrEx(0, 1, 1, UPG_SKEL_LIFE)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ATTACK)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ARMOR)
	call SetBuildUpgrEx(1, 2, 2, UPG_SKEL_MASTERY)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_ARMOR)

endfunction

function ResearchLoop takes nothing returns nothing

    loop
        
        if Research then
			call ResearchUpgrades()
        endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUserDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	local real AttackWaveStartXDwarfs = -3100.0
	local real AttackWaveStartYDwarfs = 6600.0
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User and Dwarfs...")

	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 2, 2, ABOMINATION)
		call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, MALGANIS)
		call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 3, 4, NECRO)
		call CampaignAttackerEx(3, 4, 4, BANSHEE)
		call CampaignAttackerEx(2, 2, 2, CRYPT_FIEND)
		call CampaignAttackerEx(3, 3, 3, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, MALGANIS)
		call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// Only when attacking the dwarfs, the starting point has to change to avoid crossing the player base
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(6, 7, 8, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, AttackWaveStartXDwarfs, AttackWaveStartYDwarfs)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 5, CRYPT_FIEND)
		call CampaignAttackerEx(4, 4, 5, GARGOYLE)
		call CampaignAttackerEx(1, 1, 1, MALGANIS)
		call AdvSuicideOnPlayerEx(M3, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 5 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(2, 2, 2, NECRO)
		call CampaignAttackerEx(2, 2, 2, BANSHEE)
		call CampaignAttackerEx(1, 2, 2, ABOMINATION)
		call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, MALGANIS)
		call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
		
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, CRYPT_FIEND)
			call CampaignAttackerEx(1, 2, 2, ABOMINATION)
			call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, MALGANIS)
			call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 4, NECRO)
			call CampaignAttackerEx(3, 4, 4, BANSHEE)
			call CampaignAttackerEx(2, 2, 2, CRYPT_FIEND)
			call CampaignAttackerEx(3, 3, 3, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, MALGANIS)
			call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// Only when attacking the dwarfs, the starting point has to change to avoid crossing the player base
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(6, 7, 8, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, AttackWaveStartXDwarfs, AttackWaveStartYDwarfs)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 9 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 5, CRYPT_FIEND)
			call CampaignAttackerEx(4, 4, 5, GARGOYLE)
			call CampaignAttackerEx(1, 1, 1, MALGANIS)
			call AdvSuicideOnPlayerEx(M3, M3, M3, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 10 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 2, 2, ABOMINATION)
			call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, MALGANIS)
			call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User and Dwarfs!")
	
endfunction

function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	
	loop 
		
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 2, 2, ABOMINATION)
		call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, MALGANIS)
		call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 3, 4, NECRO)
		call CampaignAttackerEx(3, 4, 4, BANSHEE)
		call CampaignAttackerEx(2, 2, 2, CRYPT_FIEND)
		call CampaignAttackerEx(3, 3, 3, MEAT_WAGON)
		call CampaignAttackerEx(2, 2, 2, SHADE)
		call CampaignAttackerEx(1, 1, 1, MALGANIS)
		call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 5, CRYPT_FIEND)
		call CampaignAttackerEx(4, 4, 5, GARGOYLE)
		call CampaignAttackerEx(2, 2, 2, SHADE)
		call CampaignAttackerEx(1, 1, 1, MALGANIS)
		call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(2, 2, 2, NECRO)
		call CampaignAttackerEx(2, 2, 2, BANSHEE)
		call CampaignAttackerEx(1, 2, 2, ABOMINATION)
		call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, MALGANIS)
		call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
		
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, CRYPT_FIEND)
			call CampaignAttackerEx(1, 2, 2, ABOMINATION)
			call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
			call CampaignAttackerEx(2, 2, 2, SHADE)
			call CampaignAttackerEx(1, 1, 1, MALGANIS)
			call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 4, NECRO)
			call CampaignAttackerEx(3, 4, 4, BANSHEE)
			call CampaignAttackerEx(2, 2, 2, CRYPT_FIEND)
			call CampaignAttackerEx(3, 3, 3, MEAT_WAGON)
			call CampaignAttackerEx(2, 2, 2, SHADE)
			call CampaignAttackerEx(1, 1, 1, MALGANIS)
			call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 5, CRYPT_FIEND)
			call CampaignAttackerEx(4, 4, 5, GARGOYLE)
			call CampaignAttackerEx(2, 2, 2, SHADE)
			call CampaignAttackerEx(1, 1, 1, MALGANIS)
			call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 2, 2, ABOMINATION)
			call CampaignAttackerEx(2, 2, 2, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, MALGANIS)
			call AdvSuicideOnPlayerEx(M4, M4, M4, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackWavesAttackDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	local real AttackWaveStartXDwarfs = -3100.0
	local real AttackWaveStartYDwarfs = 6600.0
	
	call AdvDebugDisplayToPlayer("AI Info: attacking Dwarfs...")
	
	loop 
		
		// Only when attacking the dwarfs, the starting point has to change to avoid crossing the player base
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(6, 7, 8, GARGOYLE)
		call AdvSuicideOnPlayerEx(M3, M3, M3, Dwarfs, AttackWaveStartXDwarfs, AttackWaveStartYDwarfs)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
		
		// Only when attacking the dwarfs, the starting point has to change to avoid crossing the player base
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(6, 7, 8, GARGOYLE)
		call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, AttackWaveStartXDwarfs, AttackWaveStartYDwarfs)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking Dwarfs!")
	
endfunction

function AttackWavesAttackSuicide takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide 
	local integer UserId = GetPlayerId(User)
	local integer DwarfsId = GetPlayerId(Dwarfs)
	
	call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
	
	// Reset the assault group
	call InitAssaultGroup()
	
	// General suicide loop
	loop
	
		// Launch attack continuously depending on the current target (prioritizing user)
		if AttackUser then
		
			loop
			
				call SuicideUnitEx(1, GHOUL, UserId)
				call SuicideUnitEx(1, CRYPT_FIEND, UserId)
				call SuicideUnitEx(1, NECRO, UserId)
				call SuicideUnitEx(1, BANSHEE, UserId)
				call SuicideUnitEx(1, ABOMINATION, UserId)
				call SuicideUnitEx(1, MEAT_WAGON, UserId)
				call SuicideUnitEx(1, SHADE, UserId)
				call SuicideUnitEx(1, GARGOYLE, UserId)
				call SuicideUnitEx(1, MALGANIS, UserId)
				
				call Sleep(10)
				exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
			endloop
		
		else
		
			if AttackDwarfs then
			
				loop
			
					call SuicideUnitEx(1, GHOUL, DwarfsId)
					call SuicideUnitEx(1, CRYPT_FIEND, DwarfsId)
					call SuicideUnitEx(1, NECRO, DwarfsId)
					call SuicideUnitEx(1, BANSHEE, DwarfsId)
					call SuicideUnitEx(1, ABOMINATION, DwarfsId)
					call SuicideUnitEx(1, MEAT_WAGON, DwarfsId)
					call SuicideUnitEx(1, SHADE, DwarfsId)
					call SuicideUnitEx(1, GARGOYLE, DwarfsId)
					call SuicideUnitEx(1, MALGANIS, DwarfsId)
					
					call Sleep(10)
					exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
				endloop	
				
			endif
		
		endif
		
		call Sleep(2)
		exitwhen AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	
	call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
	
endfunction

function AttackLoop takes nothing returns nothing

    loop
	
		// If suicide is set, execute the attack suicide loop
		if AttackSuicide then
			call AttackWavesAttackSuicide()
		
		// Otherwise check for other possible attack targets
		else 
		
			// If attack are set, execute the requested attack loop	
			if AttackUser and AttackDwarfs then
				call AttackWavesAttackUserDwarfs()
			
			// If at least one of the two is not true, then check them individually
			else
				// If attack only user
				if AttackUser then
					call AttackWavesAttackUser()		
				// If attack only dwarfs
				elseif AttackDwarfs then
					call AttackWavesAttackDwarfs()	
				endif
				
			endif
			
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
			
		// Update attack suicide behaviour command (to start/stop suicide, always against the user or the dwarf depending on what's the current target)
		elseif Command == COMMAND_ATTACK_SUICIDE then
			if Data == DATA_ATTACK_SUICIDE_ENABLE then
				set AttackSuicide = true
				call AdvSetSuicide(true)
			elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
				set AttackSuicide = false
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
		// Update attack dwarfs behaviour command (to start/stop attack against the dwarfs)		
        elseif Command == COMMAND_ATTACK_DWARFS then
            if Data == DATA_ATTACK_DWARFS_ENABLE then
                set AttackDwarfs = true
            elseif Data == DATA_ATTACK_DWARFS_DISABLE then
                set AttackDwarfs = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack dwarfs command data received!") 
            endif 
			
		// Update research behaviour
		// Note: if research is disabled/enabled there is a delay to reach the previous point
		elseif Command == COMMAND_RESEARCH then
			if Data == DATA_RESEARCH_ENABLE then
				set Research = true
			elseif Data == DATA_RESEARCH_DISABLE then
				set Research = false
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid research command data received!")
			endif  
			
		else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(3, 4, 5)
	call AdvSetWoodPeonsWarriors(true)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(true)
	
	// Define build order
	call SetBuildOrder()
	
	// Start research of upgrades at timed intervals as soon as possible (it depends on the commands received)
	call StartThread(function ResearchLoop)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 09 -- Undead Scourge Lavender Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	player Dwarfs = PlayerEx(9)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = -3000.0
	constant real ATTACK_WAVE_START_Y = 5000.0
        
    boolean AttackUser = false
	boolean AttackDwarfs = false
	boolean AttackSuicide = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_ATTACK_DWARFS = 1
    constant integer DATA_ATTACK_DWARFS_ENABLE = 1
    constant integer DATA_ATTACK_DWARFS_DISABLE = 0
	
	constant integer COMMAND_ATTACK_SUICIDE = 2
	constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
	constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(3, 3, 3, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(2, 2, 2, CRYPT)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(2, 2, 2, SLAUGHTERHOUSE)
	call SetBuildUnitEx(2, 2, 2, DAMNED_TEMPLE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)
	call SetBuildUnitEx(1, 1, 1, SAC_PIT)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(2, 2, 2, GHOUL)
	call CampaignDefenderEx(1, 2, 2, NECRO)
	call CampaignDefenderEx(1, 1, 2, BANSHEE)
	call CampaignDefenderEx(1, 1, 1, CRYPT_FIEND)
	call CampaignDefenderEx(1, 1, 1, ABOMINATION)
		
endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUserDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User and Dwarfs...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(7, 8, 8, GHOUL)
		call CampaignAttackerEx(1, 1, 2, CRYPT_FIEND)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 3, 3, ABOMINATION)
		call CampaignAttackerEx(2, 2, 3, BANSHEE)
		call CampaignAttackerEx(2, 3, 3, NECRO)
		call CampaignAttackerEx(2, 2, 2, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 4, CRYPT_FIEND)
		call CampaignAttackerEx(3, 3, 4, MEAT_WAGON)
		call CampaignAttackerEx(2, 2, 2, GARGOYLE)
		call CampaignAttackerEx(1, 1, 1, SHADE)
		call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 5 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(10, 10, 10, GHOUL)
		call CampaignAttackerEx(2, 3, 4, NECRO)
		call CampaignAttackerEx(2, 2, 2, SHADE)
		call AdvSuicideOnPlayerEx(M3, M3, M3, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
		
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(7, 8, 8, GHOUL)
			call CampaignAttackerEx(1, 1, 2, CRYPT_FIEND)
			call CampaignAttackerEx(2, 2, 2, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 3, ABOMINATION)
			call CampaignAttackerEx(2, 2, 3, BANSHEE)
			call CampaignAttackerEx(1, 2, 3, NECRO)
			call CampaignAttackerEx(3, 3, 3, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, CRYPT_FIEND)
			call CampaignAttackerEx(4, 4, 4, GARGOYLE)
			call CampaignAttackerEx(2, 2, 2, SHADE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 9 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 10 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(10, 10, 10, GHOUL)
			call CampaignAttackerEx(2, 3, 4, NECRO)
			call CampaignAttackerEx(1, 1, 1, SHADE)
			call AdvSuicideOnPlayerEx(M3, M3, M3, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User and Dwarfs!")
	
endfunction

function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	
	loop
		
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
		
			// *** WAVE 9 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackWavesAttackDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking Dwarfs...")
	
	loop 
	
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(7, 8, 8, GHOUL)
		call CampaignAttackerEx(2, 2, 3, CRYPT_FIEND)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 3, 3, ABOMINATION)
		call CampaignAttackerEx(2, 2, 3, BANSHEE)
		call CampaignAttackerEx(2, 3, 3, NECRO)
		call CampaignAttackerEx(4, 4, 4, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 4, CRYPT_FIEND)
		call CampaignAttackerEx(2, 2, 3, MEAT_WAGON)
		call CampaignAttackerEx(4, 4, 4, GARGOYLE)
		call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(10, 10, 10, GHOUL)
		call CampaignAttackerEx(2, 3, 4, NECRO)
		call AdvSuicideOnPlayerEx(M3, M3, M3, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
		
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(7, 8, 8, GHOUL)
			call CampaignAttackerEx(2, 2, 3, CRYPT_FIEND)
			call CampaignAttackerEx(2, 2, 2, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 3, 3, ABOMINATION)
			call CampaignAttackerEx(2, 2, 3, BANSHEE)
			call CampaignAttackerEx(1, 2, 3, NECRO)
			call CampaignAttackerEx(3, 3, 3, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, CRYPT_FIEND)
			call CampaignAttackerEx(4, 4, 4, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(10, 10, 10, GHOUL)
			call CampaignAttackerEx(2, 3, 4, NECRO)
			call AdvSuicideOnPlayerEx(M3, M3, M3, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking Dwarfs!")
	
endfunction

function AttackWavesAttackSuicide takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide 
	local integer UserId = GetPlayerId(User)
	local integer DwarfsId = GetPlayerId(Dwarfs)
	
	call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
	
	// Reset the assault group
	call InitAssaultGroup()
	
	// General suicide loop
	
	loop
	
		// Launch attack continuously depending on the current target (prioritizing user)
		if AttackUser then
		
			loop
			
				call SuicideUnitEx(1, GHOUL, UserId)
				call SuicideUnitEx(1, CRYPT_FIEND, UserId)
				call SuicideUnitEx(1, NECRO, UserId)
				call SuicideUnitEx(1, BANSHEE, UserId)
				call SuicideUnitEx(1, ABOMINATION, UserId)
				call SuicideUnitEx(1, MEAT_WAGON, UserId)
				call SuicideUnitEx(1, SHADE, UserId)
				call SuicideUnitEx(1, GARGOYLE, UserId)
				
				call Sleep(10)
				exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
			endloop
		
		else
		
			if AttackDwarfs then
			
				loop
			
					call SuicideUnitEx(1, GHOUL, DwarfsId)
					call SuicideUnitEx(1, CRYPT_FIEND, DwarfsId)
					call SuicideUnitEx(1, NECRO, DwarfsId)
					call SuicideUnitEx(1, BANSHEE, DwarfsId)
					call SuicideUnitEx(1, ABOMINATION, DwarfsId)
					call SuicideUnitEx(1, MEAT_WAGON, DwarfsId)
					call SuicideUnitEx(1, SHADE, DwarfsId)
					call SuicideUnitEx(1, GARGOYLE, DwarfsId)
					
					call Sleep(10)
					exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
				endloop	
				
			endif
		
		endif
		
		call Sleep(2)
		exitwhen AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	
	call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
	
endfunction

function AttackLoop takes nothing returns nothing

    loop
	
		// If suicide is set, execute the attack suicide loop
		if AttackSuicide then
			call AttackWavesAttackSuicide()
		
		// Otherwise check for other possible attack targets
		else 
		
			// If attack are set, execute the requested attack loop	
			if AttackUser and AttackDwarfs then
				call AttackWavesAttackUserDwarfs()
			
			// If at least one of the two is not true, then check them individually
			else
				// If attack only user
				if AttackUser then
					call AttackWavesAttackUser()		
				// If attack only dwarfs
				elseif AttackDwarfs then
					call AttackWavesAttackDwarfs()	
				endif
				
			endif
			
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
			
		// Update attack suicide behaviour command (to start/stop suicide, always against the user or the dwarf depending on what's the current target)
		elseif Command == COMMAND_ATTACK_SUICIDE then
			if Data == DATA_ATTACK_SUICIDE_ENABLE then
				set AttackSuicide = true
				call AdvSetSuicide(true)
			elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
				set AttackSuicide = false
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
		// Update attack dwarfs behaviour command (to start/stop attack against the dwarfs)		
        elseif Command == COMMAND_ATTACK_DWARFS then
            if Data == DATA_ATTACK_DWARFS_ENABLE then
                set AttackDwarfs = true
            elseif Data == DATA_ATTACK_DWARFS_DISABLE then
                set AttackDwarfs = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack dwarfs command data received!") 
            endif 
			
		else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(3, 4, 5)
	call AdvSetWoodPeonsWarriors(true)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(true)
	
	// Define build order
	call SetBuildOrder()
	
		// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 09 -- Undead Scourge Mint Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	player Dwarfs = PlayerEx(9)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = 3800.0
	constant real ATTACK_WAVE_START_Y = 6300.0
        
    boolean AttackUser = false
	boolean AttackDwarfs = false
	boolean AttackSuicide = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_ATTACK_DWARFS = 1
    constant integer DATA_ATTACK_DWARFS_ENABLE = 1
    constant integer DATA_ATTACK_DWARFS_DISABLE = 0
	
	constant integer COMMAND_ATTACK_SUICIDE = 2
	constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
	constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(2, 2, 2, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(2, 2, 2, CRYPT)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(1, 1, 1, SLAUGHTERHOUSE)
	call SetBuildUnitEx(1, 1, 1, DAMNED_TEMPLE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(2, 2, 2, GHOUL)
	call CampaignDefenderEx(1, 1, 2, ABOMINATION)
	call CampaignDefenderEx(2, 3, 3, GARGOYLE)
		
endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUserDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User and Dwarfs...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(2, 2, 3, GHOUL)
		call CampaignAttackerEx(1, 2, 2, CRYPT_FIEND)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(2, 2, 3, CRYPT_FIEND)
		call CampaignAttackerEx(1, 2, 2, BANSHEE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 5 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(2, 2, 2, NECRO)
		call CampaignAttackerEx(2, 2, 2, BANSHEE)
		call CampaignAttackerEx(1, 2, 2, MEAT_WAGON)
		call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
		
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 4, GHOUL)
			call CampaignAttackerEx(1, 1, 1, CRYPT_FIEND)
			call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(1, 1, 1, ABOMINATION)
			call CampaignAttackerEx(1, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 2, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 9 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 4, GARGOYLE)
			call CampaignAttackerEx(2, 2, 2, GHOUL)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 10 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 4, CRYPT_FIEND)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 2, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User and Dwarfs!")
	
endfunction

function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	
	loop 
	
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(2, 2, 3, GHOUL)
		call CampaignAttackerEx(1, 2, 2, CRYPT_FIEND)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(2, 2, 3, CRYPT_FIEND)
		call CampaignAttackerEx(1, 2, 2, BANSHEE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(2, 2, 2, NECRO)
		call CampaignAttackerEx(2, 2, 2, BANSHEE)
		call CampaignAttackerEx(1, 2, 2, MEAT_WAGON)
		call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
		
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 4, GHOUL)
			call CampaignAttackerEx(1, 1, 1, CRYPT_FIEND)
			call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(1, 1, 1, ABOMINATION)
			call CampaignAttackerEx(1, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 2, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(2, 3, 4, GARGOYLE)
			call CampaignAttackerEx(2, 2, 2, GHOUL)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 9 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 4, CRYPT_FIEND)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 2, MEAT_WAGON)
			call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackWavesAttackDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking Dwarfs...")
	
	loop 
		
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
			
			// *** WAVE 2 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking Dwarfs!")
	
endfunction

function AttackWavesAttackSuicide takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide 
	local integer UserId = GetPlayerId(User)
	local integer DwarfsId = GetPlayerId(Dwarfs)
	
	call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
	
	// Reset the assault group
	call InitAssaultGroup()
	
	// General suicide loop
	
	loop
	
		// Launch attack continuously depending on the current target (prioritizing user)
		if AttackUser then
		
			loop
			
				call SuicideUnitEx(1, GHOUL, UserId)
				call SuicideUnitEx(1, CRYPT_FIEND, UserId)
				call SuicideUnitEx(1, NECRO, UserId)
				call SuicideUnitEx(1, BANSHEE, UserId)
				call SuicideUnitEx(1, ABOMINATION, UserId)
				call SuicideUnitEx(1, MEAT_WAGON, UserId)
				call SuicideUnitEx(1, SHADE, UserId)
				call SuicideUnitEx(1, GARGOYLE, UserId)
				
				call Sleep(10)
				exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
			endloop
		
		else
		
			if AttackDwarfs then
			
				loop
			
					call SuicideUnitEx(1, GHOUL, DwarfsId)
					call SuicideUnitEx(1, CRYPT_FIEND, DwarfsId)
					call SuicideUnitEx(1, NECRO, DwarfsId)
					call SuicideUnitEx(1, BANSHEE, DwarfsId)
					call SuicideUnitEx(1, ABOMINATION, DwarfsId)
					call SuicideUnitEx(1, MEAT_WAGON, DwarfsId)
					call SuicideUnitEx(1, SHADE, DwarfsId)
					call SuicideUnitEx(1, GARGOYLE, DwarfsId)
					
					call Sleep(10)
					exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
				endloop	
				
			endif
		
		endif
		
		call Sleep(2)
		exitwhen AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	
	call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
	
endfunction

function AttackLoop takes nothing returns nothing

    loop
	
		// If suicide is set, execute the attack suicide loop
		if AttackSuicide then
			call AttackWavesAttackSuicide()
		
		// Otherwise check for other possible attack targets
		else 
		
			// If attack are set, execute the requested attack loop	
			if AttackUser and AttackDwarfs then
				call AttackWavesAttackUserDwarfs()
			
			// If at least one of the two is not true, then check them individually
			else
				// If attack only user
				if AttackUser then
					call AttackWavesAttackUser()		
				// If attack only dwarfs
				elseif AttackDwarfs then
					call AttackWavesAttackDwarfs()	
				endif
				
			endif
			
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
			
		// Update attack suicide behaviour command (to start/stop suicide, always against the user or the dwarf depending on what's the current target)
		elseif Command == COMMAND_ATTACK_SUICIDE then
			if Data == DATA_ATTACK_SUICIDE_ENABLE then
				set AttackSuicide = true
				call AdvSetSuicide(true)
			elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
				set AttackSuicide = false
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
			endif
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
		// Update attack dwarfs behaviour command (to start/stop attack against the dwarfs)		
        elseif Command == COMMAND_ATTACK_DWARFS then
            if Data == DATA_ATTACK_DWARFS_ENABLE then
                set AttackDwarfs = true
            elseif Data == DATA_ATTACK_DWARFS_DISABLE then
                set AttackDwarfs = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack dwarfs command data received!") 
            endif 
		
		else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!")
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(3, 4, 5)
	call AdvSetWoodPeonsWarriors(true)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(true)
	call AdvSetPrioritizeNearest(false)
	call AdvSetSearchPreferredLocations(true)
	
	call AdvSetPreferredLocationsRadius(1200.0)
	// MAIN BASE
	call AdvAddPreferredLocation(1000.0, 1300.0)
	// EXPANSION
	call AdvAddPreferredLocation(7000.0, -2200.0)
	
	// Define build order
	call SetBuildOrder()
	
		// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction



JASS:
//============================================================================
//  RR Human 09 -- Undead Scourge Purple Undead -- AI Script
//  by InsaneMonster (Luca Pasqualini)
//  Requires common_v2.ai
//============================================================================

// GLOBALS 
//------------------------------------------------
globals
	
	// Players

    player User = PlayerEx(2)
	player Dwarfs = PlayerEx(9)
	
	// Behaviour
	
	constant real ATTACK_WAVE_START_X = 5500.0
	constant real ATTACK_WAVE_START_Y = -1300.0
        
    boolean AttackUser = false
	boolean AttackDwarfs = false
	boolean Research = false
	boolean AttackSuicide = false
	
	// Commands
    
	constant integer COMMAND_ATTACK_USER = 0
    constant integer DATA_ATTACK_USER_ENABLE = 1
    constant integer DATA_ATTACK_USER_DISABLE = 0
	
	constant integer COMMAND_ATTACK_DWARFS = 1
    constant integer DATA_ATTACK_DWARFS_ENABLE = 1
    constant integer DATA_ATTACK_DWARFS_DISABLE = 0
	
	constant integer COMMAND_ATTACK_SUICIDE = 2
	constant integer DATA_ATTACK_SUICIDE_ENABLE = 1
	constant integer DATA_ATTACK_SUICIDE_DISABLE = 0
	
	constant integer COMMAND_RESEARCH = 3
	constant integer DATA_RESEARCH_ENABLE = 1
	constant integer DATA_RESEARCH_DISABLE = 0
	
	constant integer COMMAND_DEFEAT = -1
    constant integer DATA_DEFEAT_ENABLE = 1
    constant integer DATA_DEFEAT_DISABLE = 0
    
endglobals

// BUILD ORDER
//------------------------------------------------
function SetBuildOrder takes nothing returns nothing

    call SetBuildUnitEx(1, 1, 1, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_1)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_MINE)
	call SetBuildUnitEx(3, 4, 5, ZIGGURAT_1)
	call SetBuildUnitEx(5, 5, 5, ACOLYTE)
	call SetBuildUnitEx(1, 1, 1, UNDEAD_ALTAR)
	call SetBuildUnitEx(2, 2, 2, CRYPT)
	call SetBuildUnitEx(1, 1, 1, GRAVEYARD)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_2)
	call SetBuildUnitEx(1, 1, 1, SLAUGHTERHOUSE)
	call SetBuildUnitEx(1, 1, 1, DAMNED_TEMPLE)
	call SetBuildUnitEx(1, 1, 1, NECROPOLIS_3)
	call SetBuildUnitEx(1, 1, 1, SAC_PIT)

endfunction

// DEFENDERS
//------------------------------------------------
function SetDefenders takes nothing returns nothing

	call CampaignDefenderEx(1, 1, 1, LICH)
		
endfunction

// RESEARCH LOOP
//------------------------------------------------
function ResearchUpgrades takes nothing returns nothing
	local boolean ResearchUpgradesResearch = Research

	call Sleep(10)
	if ResearchUpgradesResearch != Research then
		return
	endif

	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_STR)

	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, UPG_UNHOLY_ARMOR)	
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ATTACK)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 1, UPG_CR_ARMOR)
	call SetBuildUpgrEx(1, 1, 1, UPG_BANSHEE)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(0, 1, 1, UPG_GHOUL_FRENZY)
	call SetBuildUpgrEx(1, 1, 1, UPG_NECROS)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_ARMOR)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 2, UPG_NECROS)
	call SetBuildUpgrEx(1, 1, 1, UPG_SKEL_MASTERY)
	call SetBuildUpgrEx(1, 1, 1, UPG_PLAGUE)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 2, UPG_CR_ATTACK)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 2, UPG_UNHOLY_STR)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 1, 2, UPG_BANSHEE)
	call SetBuildUpgrEx(1, 2, 2, UPG_CR_ARMOR)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(0, 0, 1, UPG_EXHUME)
	call SetBuildUpgrEx(0, 1, 1, UPG_SKEL_LIFE)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_STR)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ATTACK)
	call SetBuildUpgrEx(1, 2, 2, UPG_SKEL_MASTERY)
	
	call Sleep(M1)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 3, UPG_UNHOLY_ARMOR)
	
	call Sleep(M2)
	if ResearchUpgradesResearch != Research then
		return
	endif
	
	call SetBuildUpgrEx(1, 2, 3, UPG_CR_ARMOR)

endfunction

function ResearchLoop takes nothing returns nothing

    loop
        
        if Research then
			call ResearchUpgrades()
        endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// ATTACK LOOP
//------------------------------------------------
function AttackWavesAttackUserDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User and Dwarfs...")
	
	loop 

		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 1, 1, NECRO)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 1, 1, NECRO)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(0, 1, 1, ABOMINATION)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(0, 1, 1, ABOMINATION)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 5 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 6 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 7 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 1, 1, NECRO)
		call CampaignAttackerEx(1, 1, 1, BANSHEE)
		call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, LICH)
		call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 8 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 1, 1, NECRO)
		call CampaignAttackerEx(1, 1, 1, BANSHEE)
		call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, LICH)
		call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
			
			// *** WAVE 9 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 10 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 11 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GHOUL)
			call CampaignAttackerEx(1, 1, 1, ABOMINATION)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 12 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GHOUL)
			call CampaignAttackerEx(1, 1, 1, ABOMINATION)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 13 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 14 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 15 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, LICH)
			call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 16 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 2, 3, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, LICH)
			call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User and Dwarfs!")
	
endfunction

function AttackWavesAttackUser takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking User...")
	
	loop 
		
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 1, 1, NECRO)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(0, 1, 1, ABOMINATION)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 1, 1, NECRO)
		call CampaignAttackerEx(1, 1, 1, BANSHEE)
		call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, LICH)
		call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
			
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GHOUL)
			call CampaignAttackerEx(1, 1, 1, ABOMINATION)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, LICH)
			call AdvSuicideOnPlayerEx(M2, M2, M2, User, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking User!")
	
endfunction

function AttackWavesAttackDwarfs takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide
	
	call AdvDebugDisplayToPlayer("AI Info: attacking Dwarfs...")
	
	loop 
			
		// *** WAVE 1 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 1, 1, NECRO)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 2 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 4, 5, GHOUL)
		call CampaignAttackerEx(0, 1, 1, ABOMINATION)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 3 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(4, 5, 6, GARGOYLE)
		call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		// *** WAVE 4 ***
		call InitAssaultGroup()
		call CampaignAttackerEx(3, 4, 5, GHOUL)
		call CampaignAttackerEx(1, 1, 1, NECRO)
		call CampaignAttackerEx(1, 1, 1, BANSHEE)
		call CampaignAttackerEx(1, 1, 1, MEAT_WAGON)
		call CampaignAttackerEx(1, 1, 1, LICH)
		call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
		loop
			
			// *** WAVE 5 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 6 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(4, 5, 6, GHOUL)
			call CampaignAttackerEx(1, 1, 1, ABOMINATION)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 7 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(5, 6, 7, GARGOYLE)
			call AdvSuicideOnPlayerEx(M1, M1, M1, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
			
			// *** WAVE 8 ***
			call InitAssaultGroup()
			call CampaignAttackerEx(3, 4, 5, GHOUL)
			call CampaignAttackerEx(2, 2, 2, NECRO)
			call CampaignAttackerEx(2, 2, 2, BANSHEE)
			call CampaignAttackerEx(1, 2, 3, MEAT_WAGON)
			call CampaignAttackerEx(1, 1, 1, LICH)
			call AdvSuicideOnPlayerEx(M2, M2, M2, Dwarfs, ATTACK_WAVE_START_X, ATTACK_WAVE_START_Y)
			exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide

		endloop
		
		// Make sure to leave both loops
		exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished attacking Dwarfs!")
	
endfunction

function AttackWavesAttackSuicide takes nothing returns nothing
	local boolean AttackWaveAttackUser = AttackUser
	local boolean AttackWaveAttackDwarfs = AttackDwarfs
	local boolean AttackWaveAttackSuicide = AttackSuicide 
	local integer UserId = GetPlayerId(User)
	local integer DwarfsId = GetPlayerId(Dwarfs)
	
	call AdvDebugDisplayToPlayer("AI Info: starting suicide...")
	
	// Reset the assault group
	call InitAssaultGroup()
	
	// General suicide loop
	
	loop
	
		// Launch attack continuously depending on the current target (prioritizing user)
		if AttackUser then
		
			loop
			
				call SuicideUnitEx(1, GHOUL, UserId)
				call SuicideUnitEx(1, CRYPT_FIEND, UserId)
				call SuicideUnitEx(1, NECRO, UserId)
				call SuicideUnitEx(1, BANSHEE, UserId)
				call SuicideUnitEx(1, ABOMINATION, UserId)
				call SuicideUnitEx(1, MEAT_WAGON, UserId)
				call SuicideUnitEx(1, SHADE, UserId)
				call SuicideUnitEx(1, GARGOYLE, UserId)
				call SuicideUnitEx(1, LICH, UserId)
				
				call Sleep(10)
				exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
			endloop
		
		else
		
			if AttackDwarfs then
			
				loop
			
					call SuicideUnitEx(1, GHOUL, DwarfsId)
					call SuicideUnitEx(1, CRYPT_FIEND, DwarfsId)
					call SuicideUnitEx(1, NECRO, DwarfsId)
					call SuicideUnitEx(1, BANSHEE, DwarfsId)
					call SuicideUnitEx(1, ABOMINATION, DwarfsId)
					call SuicideUnitEx(1, MEAT_WAGON, DwarfsId)
					call SuicideUnitEx(1, SHADE, DwarfsId)
					call SuicideUnitEx(1, GARGOYLE, DwarfsId)
					call SuicideUnitEx(1, LICH, DwarfsId)
					
					call Sleep(10)
					exitwhen AttackWaveAttackUser != AttackUser or AttackWaveAttackDwarfs != AttackDwarfs or AttackSuicide != AttackWaveAttackSuicide
		
				endloop	
				
			endif
		
		endif
		
		call Sleep(2)
		exitwhen AttackSuicide != AttackWaveAttackSuicide
		
	endloop
	
	call AdvDebugDisplayToPlayer("AI Info: finished suicide!")
	
endfunction

function AttackLoop takes nothing returns nothing

    loop
	
		// If suicide is set, execute the attack suicide loop
		if AttackSuicide then
			call AttackWavesAttackSuicide()
		
		// Otherwise check for other possible attack targets
		else 
		
			// If attack are set, execute the requested attack loop	
			if AttackUser and AttackDwarfs then
				call AttackWavesAttackUserDwarfs()
			
			// If at least one of the two is not true, then check them individually
			else
				// If attack only user
				if AttackUser then
					call AttackWavesAttackUser()		
				// If attack only dwarfs
				elseif AttackDwarfs then
					call AttackWavesAttackDwarfs()	
				endif
				
			endif
			
		endif
		   
        // Wait a little time before looping again
        call Sleep(10)
    
    endloop

endfunction

// COMMAND LOOP
//------------------------------------------------
function CommandFetch takes nothing returns nothing
    local integer Command
    local integer Data
	
	// Check if there is any command waiting
	
	loop
	
		exitwhen CommandsWaiting() <= 0 
   
        // Get command and data
        set Command = GetLastCommand()
        set Data = GetLastData()
        call PopLastCommand()
		
		// We set the suicide flag to disable all attack routines upon defeat
		if Command == COMMAND_DEFEAT then
			if Data == DATA_DEFEAT_ENABLE then
				call AdvSetSuicide(true)
			elseif Data == DATA_DEFEAT_DISABLE then
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid defeat command data received!")
			endif
			
		// Update attack suicide behaviour command (to start/stop suicide, always against the user or the dwarf depending on what's the current target)
		elseif Command == COMMAND_ATTACK_SUICIDE then
			if Data == DATA_ATTACK_SUICIDE_ENABLE then
				set AttackSuicide = true
				call AdvSetSuicide(true)
			elseif Data == DATA_ATTACK_SUICIDE_DISABLE then
				set AttackSuicide = false
				call AdvSetSuicide(false)
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack suicide command data received!")
			endif
		
		
		// Update attack user behaviour command (to start/stop attack against the user)		
        elseif Command == COMMAND_ATTACK_USER then
            if Data == DATA_ATTACK_USER_ENABLE then
                set AttackUser = true
            elseif Data == DATA_ATTACK_USER_DISABLE then
                set AttackUser = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack user command data received!") 
            endif 
			
		// Update attack dwarfs behaviour command (to start/stop attack against the dwarfs)		
        elseif Command == COMMAND_ATTACK_DWARFS then
            if Data == DATA_ATTACK_DWARFS_ENABLE then
                set AttackDwarfs = true
            elseif Data == DATA_ATTACK_DWARFS_DISABLE then
                set AttackDwarfs = false
            else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid attack dwarfs command data received!") 
            endif 
			
		// Update research behaviour
		// Note: if research is disabled/enabled there is a delay to reach the previous point
		elseif Command == COMMAND_RESEARCH then
			if Data == DATA_RESEARCH_ENABLE then
				set Research = true
			elseif Data == DATA_RESEARCH_DISABLE then
				set Research = false
			else
				call AdvDebugDisplayToPlayer("AI Warning: Invalid research command data received!")
			endif  
			
        else
			call AdvDebugDisplayToPlayer("AI Warning: Unknown command received!") 
			
        endif
   
	endloop
    
endfunction

function CommandLoop takes nothing returns nothing

    // Keep fetching commands while the AI is active
    call CommandFetch()
    call StaggerSleep(1, 5)    
    loop
        call CommandFetch()
        call Sleep(2)
    endloop
    
endfunction

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

    call CampaignAI(ZIGGURAT_1, null)
    
    // Define AI properties    
	call DoCampaignFarms(false)
	call GroupTimedLife(true)
	call SetReplacements(0, 1, 2)
	call AdvSetWoodPeonsWarriors(true)
	
	call AdvSetContinueAttackReducePercentageIfFarAway(false)
	call AdvSetContinueAttackPercentage(100)
	call AdvSetPrioritizeTownHalls(false)
	call AdvSetPrioritizeNearest(true)
	
	// Define build order
	call SetBuildOrder()
	
	// Start research of upgrades at timed intervals as soon as possible (it depends on the commands received)
	call StartThread(function ResearchLoop)
	
	// Define defenders
	call SetDefenders()
	
    // Start command loop for external signals	
    call StartThread(function CommandLoop)
    
    // Launch attack waves as soon as possible (it depends on the commands received)
    call AttackLoop()
    	
endfunction
 

Macielos

Hosted Project: W3E
Level 18
Joined
Jul 9, 2010
Messages
219
Heh, I see almost every AI script writer has to go through the same hell and e.g. write his own int2Str function. It's funny how I just wrote mine and it's 99% the same as yours, including names and subfunctions. Anyway, cool source of inspiration.
 

Similar threads

D
  • Locked
Replies
4
Views
887
Deleted member 238589
D
D
  • Locked
Replies
18
Views
2K
Deleted member 219079
D
Top