Custom AI Script (JASS) Share

InsaneMonster

Hosted Project: W3RR
Level 21
Joined
Jul 20, 2011
Messages
297
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
   
    integer COMMAND_RIFLEMEN = 1
    integer DATA_RIFLEMEN_ENABLE = 1
    integer DATA_RIFLEMEN_DISABLE = 0
   
    integer COMMAND_ATTACK = 2
    integer DATA_ATTACK_ENABLE = 1
    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!
 
Level 11
Joined
Jun 15, 2016
Messages
463
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.
 

InsaneMonster

Hosted Project: W3RR
Level 21
Joined
Jul 20, 2011
Messages
297
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
   
    integer COMMAND_ATTACK_TROLLS = 2
   integer DATA_ATTACK_TROLLS_ENABLE = 1
   integer DATA_ATTACK_TROLLS_DISABLE = 0
   
   integer COMMAND_ATTACK_USER = 1
    integer DATA_ATTACK_USER_ENABLE = 1
    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:

InsaneMonster

Hosted Project: W3RR
Level 21
Joined
Jul 20, 2011
Messages
297
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
  
   integer COMMAND_ATTACK_TROLLS = 2
   integer DATA_ATTACK_TROLLS_ENABLE = 1
   integer DATA_ATTACK_TROLLS_DISABLE = 0
  
   integer COMMAND_ATTACK_USER = 1
   integer DATA_ATTACK_USER_ENABLE = 1
   integer DATA_ATTACK_USER_DISABLE = 0
  
   integer COMMAND_ATTACK_SUICIDE = 3
   integer DATA_ATTACK_SUICIDE_ENABLE = 1
   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
  
   integer COMMAND_ATTACK_TROLLS = 2
   integer DATA_ATTACK_TROLLS_ENABLE = 1
   integer DATA_ATTACK_TROLLS_DISABLE = 0
  
   integer COMMAND_ATTACK_USER = 1
   integer DATA_ATTACK_USER_ENABLE = 1
   integer DATA_ATTACK_USER_DISABLE = 0
  
   integer COMMAND_ATTACK_SUICIDE = 3
   integer DATA_ATTACK_SUICIDE_ENABLE = 1
   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
  
    integer COMMAND_ATTACK_TROLLS = 2
   integer DATA_ATTACK_TROLLS_ENABLE = 1
   integer DATA_ATTACK_TROLLS_DISABLE = 0
  
   integer COMMAND_ATTACK_USER = 1
    integer DATA_ATTACK_USER_ENABLE = 1
    integer DATA_ATTACK_USER_DISABLE = 0
  
   integer COMMAND_ATTACK_SUICIDE = 3
   integer DATA_ATTACK_SUICIDE_ENABLE = 1
   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
  
    integer COMMAND_ATTACK_TROLLS = 2
   integer DATA_ATTACK_TROLLS_ENABLE = 1
   integer DATA_ATTACK_TROLLS_DISABLE = 0
  
   integer COMMAND_ATTACK_USER = 1
    integer DATA_ATTACK_USER_ENABLE = 1
    integer DATA_ATTACK_USER_DISABLE = 0
  
   integer COMMAND_ATTACK_SUICIDE = 3
   integer DATA_ATTACK_SUICIDE_ENABLE = 1
   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:

InsaneMonster

Hosted Project: W3RR
Level 21
Joined
Jul 20, 2011
Messages
297
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
    
    integer COMMAND_RIFLEMEN = 1
    integer DATA_RIFLEMEN_ENABLE = 1
    integer DATA_RIFLEMEN_DISABLE = 0
    
    integer COMMAND_ATTACK = 2
    integer DATA_ATTACK_ENABLE = 1
    integer DATA_ATTACK_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()  
        
        // 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 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
    
    integer COMMAND_ATTACK_TROLLS = 2
    integer DATA_ATTACK_TROLLS_ENABLE = 1
    integer DATA_ATTACK_TROLLS_DISABLE = 0
    
    integer COMMAND_ATTACK_USER = 1
    integer DATA_ATTACK_USER_ENABLE = 1
    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(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()
        
        // 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 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
    
    integer COMMAND_ATTACK_TROLLS = 2
    integer DATA_ATTACK_TROLLS_ENABLE = 1
    integer DATA_ATTACK_TROLLS_DISABLE = 0
    
    integer COMMAND_ATTACK_USER = 1
    integer DATA_ATTACK_USER_ENABLE = 1
    integer DATA_ATTACK_USER_DISABLE = 0
    
    integer COMMAND_ATTACK_SUICIDE = 3
    integer DATA_ATTACK_SUICIDE_ENABLE = 1
    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
    
    integer COMMAND_ATTACK_TROLLS = 2
    integer DATA_ATTACK_TROLLS_ENABLE = 1
    integer DATA_ATTACK_TROLLS_DISABLE = 0
    
    integer COMMAND_ATTACK_USER = 1
    integer DATA_ATTACK_USER_ENABLE = 1
    integer DATA_ATTACK_USER_DISABLE = 0
    
    integer COMMAND_ATTACK_SUICIDE = 3
    integer DATA_ATTACK_SUICIDE_ENABLE = 1
    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
    
    integer COMMAND_ATTACK_TROLLS = 2
    integer DATA_ATTACK_TROLLS_ENABLE = 1
    integer DATA_ATTACK_TROLLS_DISABLE = 0
    
    integer COMMAND_ATTACK_USER = 1
    integer DATA_ATTACK_USER_ENABLE = 1
    integer DATA_ATTACK_USER_DISABLE = 0
    
    integer COMMAND_ATTACK_SUICIDE = 3
    integer DATA_ATTACK_SUICIDE_ENABLE = 1
    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
    
    integer COMMAND_ATTACK_TROLLS = 2
    integer DATA_ATTACK_TROLLS_ENABLE = 1
    integer DATA_ATTACK_TROLLS_DISABLE = 0
    
    integer COMMAND_ATTACK_USER = 1
    integer DATA_ATTACK_USER_ENABLE = 1
    integer DATA_ATTACK_USER_DISABLE = 0
    
    integer COMMAND_ATTACK_SUICIDE = 3
    integer DATA_ATTACK_SUICIDE_ENABLE = 1
    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
 
Top