1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  3. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  4. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  5. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice

AI - Engage System v3.2

Submitted by mckill2009
This bundle is marked as approved. It works and satisfies the submission rules.
System: Engage System v3.2
Created by: Mckill2009

This system is for AI usage, just like DotA AI.
Read the instructions carefully inside the code.

Code

Code (vJASS):

/*
===================EngageSystem v3.2===================
==================Made by: Mckill2009==================

FEATURES:
- This system will order a unit to engage and search for enemies
- Allows attack captain (hero leads the attack)
- Allows pick up item
- Allows target random enemy units include creeps and buildings
- Option to retreat away from attacker or to building when HP is low
- Allows target refresh when duration runs out

HOW TO USE:
- Make a new trigger and convert to custom text via EDIT >>> CONVERT CUSTOM TEXT
- Copy ALL that is written here (overwrite the existing texts in the trigger)
- Make a dummy ability of UNHOLY FRENZY that has no buff, mana cost and replace the SPELL_ID raw code
  written below the THIRD global block if needed, see below the SPELL_ID for more info.

KNOWN ISSUES:
- This system does not support Buy and Selling of items for AI

REQUIRES:
- Jass New Gen Pack (JNGP) by Vexorian
- GetClosestWidget by Spinnaker
- IsUnitChanneling by Magtheridon96
- RegisterPlayerUnitEvent by Magtheridon96

CREDITS: For their contribution
- watermelon_1234
- Bribe

API:
    static method SetEngageUnit takes unit u, boolean attackcaptain returns nothing
    static method SetUnitPickItem takes unit u returns nothing
    static method SetUnitRetreat takes unit u returns nothing
    static method SetUnitLockTargets takes unit u, integer targets, real atkdur returns nothing
    static method SetReturnPoints takes unit u, real xReturn, real yReturn returns nothing
    static method SetHeroToProtect takes unit follower, unit hero returns nothing
    static method RemoveEngagingUnit takes unit u returns nothing
*/


library EngageSystem uses GetClosestWidget, IsUnitChanneling

globals
    //===CONFIGURABLES: All settings below are recommended
    private constant real                      AOE_ITEM_PICK = 700 //range of the targeted item
    private constant real                    AOE_NEAR_TARGET = 500 //targeting nearby enemies while other target is locked
    private constant real                           INTERVAL = 3 //interval for attack
    private constant real                MAX_LIFE_PERCENTAGE = 0.4 //used for retreat (this is 40%)
    private constant real                   OFFSET_FROM_HERO = 300 //used in hero guard/follow  
    private constant integer                 MAX_ITEM_PICKED = 4 //Warning: MAX is 6
    private constant boolean             ENABLE_PICK_UP_ITEM = true
    private constant boolean                  ENABLE_RETREAT = true
    private constant boolean              ENABLE_NEAR_TARGET = true //enables to search/target nearby units
    //If FOUNTAIN_OF_HEALTH doesnt exist, it will retreat to an allied bubilding
    private constant boolean      ENABLE_RETREAT_TO_BUILDING = false //ENABLE_RETREAT must be true
    //SPELL_ID must target "Air, ground, enemy, friend, vulnerable, mechanical, structure, organic, neutral", NO MANA, NO COOLDOWN
    //In other words, it MUST target ALL UNITS excluding invulnerable.
    //SPELL_ID MUST be Unholy Frenzy ability, else it wont work
    private constant integer                        SPELL_ID = 'A001' //Unholy frenzy RAW CODE, replace this if needed
endglobals

//=====================================================
//===============NEVER TOUCH THIS BLOCK!===============
//=====================================================
globals
    private constant hashtable              HASH = InitHashtable()
    private constant integer              ATTACK = 851983
    private constant integer                MOVE = 851986
    private constant integer                STOP = 851972
    private constant integer               SMART = 851971
    private constant integer  FOUNTAIN_OF_HEALTH = 'nfoh'
    private rect R
    private unit DUMMY
endglobals

native UnitAlive takes unit u returns boolean

private function IsUnitEngageable takes unit u returns boolean
    call SetUnitPosition(DUMMY, GetUnitX(u), GetUnitY(u))
    call IssueTargetOrderById(DUMMY, 852209, u) //Unholy Frenzy orderID
    if GetUnitCurrentOrder(DUMMY)==852209 then
        return true
    endif
    call IssueImmediateOrderById(DUMMY, STOP)
    return false
endfunction

private function FilterEngagingUnit takes unit u returns boolean
    return GetUnitMoveSpeed(u) > 0 and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and /*
    */
not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_PEON) and /*
    */
GetPlayerController(GetOwningPlayer(u))==MAP_CONTROL_COMPUTER
endfunction

private function UnitEnemy takes unit u1, unit u2 returns boolean
    return IsUnitEnemy(u1, GetOwningPlayer(u2))
endfunction

private function UnitHero takes unit u returns boolean
    return IsUnitType(u, UNIT_TYPE_HERO)
endfunction

private function UnitStructure takes unit u returns boolean
    return IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction

private function GetDistance takes real x1, real y1, real x2, real y2 returns real
    return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
endfunction
//=====================================================
//===========END OF NEVER TOUCH THIS BLOCK!============
//=====================================================

//========================================
//=============ENGAGE CODE:===============
//========================================
//! textmacro EScreatetimer
    private static timer t = CreateTimer()
    private static integer instance = 0
    private static integer array instanceAr
//! endtextmacro

//! textmacro deallocate
    call .destroy()
    set instanceAr[i] = instanceAr[instance]
    set instanceAr[instance] = this
    set instance = instance - 1  
    set i = i - 1
    if instance==0 then
        call PauseTimer(t)
    endif
//! endtextmacro

//===NON-HERO UNITS ARE ALWAYS THE FOLLOWER
private struct Follower
    unit follower
    unit hero
    unit mainhero
    unit target
    private static integer DATA
    private static unit filU
    //! runtextmacro EScreatetimer()
   
    private method destroy takes nothing returns nothing
        set .follower = null
        set .hero = null
        set .mainhero = null
        set .target = null
        call .deallocate()
    endmethod
   
    static method filterHero takes nothing returns boolean
        local thistype this = DATA
        set filU = GetFilterUnit()
        return UnitAlive(filU) and not UnitEnemy(filU,.follower) and UnitHero(filU) and IsUnitEngageable(filU) and GetOwningPlayer(filU)==GetOwningPlayer(.follower)
    endmethod      
       
    private static method engagingTarget takes nothing returns nothing
        local thistype this
        local real xAttacker
        local real yAttacker
        local real xHero
        local real yHero
        local real xOffset
        local real yOffset
        local real DistX
        local real angle
        local real facing
        local integer uID
        local integer orderIDU
        local integer orderIDH
        local integer i = 0
        loop
            set i = i + 1
            set this = instanceAr[i]
            set uID = GetHandleId(.follower)
            if LoadBoolean(HASH,uID,1) and UnitAlive(.follower) then
                set xAttacker = GetUnitX(.follower)
                set yAttacker = GetUnitY(.follower)
                if UnitAlive(.hero) then
                    set orderIDH = GetUnitCurrentOrder(.hero)
                    if not IsUnitChanneling(.hero) or orderIDH==ATTACK then
                        set xHero = GetUnitX(.hero)
                        set yHero = GetUnitY(.hero)
                        if UnitAlive(.target) then
                            set facing = GetUnitFacing(.hero)*bj_DEGTORAD
                            set angle = GetRandomReal(-3,3)
                            set xOffset = xHero+OFFSET_FROM_HERO*Cos(facing+angle)
                            set yOffset = yHero+OFFSET_FROM_HERO*Sin(facing+angle)
                            set DistX = GetDistance(xAttacker,yAttacker,xHero,yHero)
                            set orderIDU = GetUnitCurrentOrder(.follower)
                            if not IsUnitChanneling(.follower) then
                                if DistX < (600*600) then
                                    if IsUnitType(.target,UNIT_TYPE_SLEEPING) then
                                        call IssueTargetOrderById(.follower,ATTACK,.target)
                                    else
                                        call IssuePointOrderById(.follower,ATTACK,GetUnitX(.target),GetUnitY(.target))
                                    endif
                                else
                                    call IssuePointOrderById(.follower,ATTACK,xOffset,yOffset)
                                endif
                            endif
                        else
                            set .target = LoadUnitHandle(HASH,GetHandleId(.hero),7)
                            call IssuePointOrderById(.follower,ATTACK,xOffset,yOffset)
                        endif
                    endif
                else
                    set .hero = .mainhero
                    if .hero==null then
                        set DATA = this
                        set .hero = GetClosestUnit(xAttacker,yAttacker,Filter(function thistype.filterHero))
                        call IssuePointOrderById(.follower, ATTACK, LoadReal(HASH, uID, 4), LoadReal(HASH, uID, 5))
                    endif
                endif
            else
                call FlushChildHashtable(HASH,uID)
                //! runtextmacro deallocate()
            endif
            exitwhen i==instance
        endloop
    endmethod
   
    static method addFollower takes unit u returns nothing
        local thistype this
        if instance < 8190 then
            set this = thistype.allocate()
            set .follower = u
            set .mainhero = LoadUnitHandle(HASH, GetHandleId(u), 8)
            set .hero = null
            set .target = null
            if instance==0 then
                call TimerStart(t, 2.5, true, function thistype.engagingTarget)
            endif
            set instance = instance + 1
            set instanceAr[instance] = this      
        else
            call DestroyTimer(t)
            debug call BJDebugMsg("ERROR: Limit of 8190 reached!, cannot allocate anymore instance: DESTROYING TIMER. ")
        endif    
    endmethod
endstruct

//===REGISTERS INDEPENDENT UNITS (means, this unit will NOT search and follows a hero)
//=== A HERO WILL ALWAYS BE REGISTERED AS INDEPENDENT UNITS
private struct IndependentEngager
    private unit engager
    private unit target
    private real interval
    private static integer DATA
    private static unit filterU
    //! runtextmacro EScreatetimer()
   
    //16 Total Players, 17==ALL PLAYERS, 18==Structures, 19==Flying, 20==Heroes
    private static method FilterEngage takes nothing returns boolean
        local thistype this = DATA
        local integer targetID
        local integer i = 0
        set filterU = GetFilterUnit()      
        if UnitAlive(filterU) and IsUnitEnemy(.engager,GetOwningPlayer(filterU)) and IsUnitEngageable(filterU) then
            set targetID = LoadInteger(HASH,GetHandleId(.engager),2)
            if targetID < 17 then
                loop
                    set i = i+1
                    if targetID==i then
                        return GetOwningPlayer(filterU)==Player(i-1)
                    endif  
                    exitwhen i==targetID
                endloop
           
            elseif targetID==17 then
                return true
            elseif targetID==18 then
                return IsUnitType(filterU,UNIT_TYPE_STRUCTURE)
            elseif targetID==19 then
                return IsUnitType(filterU,UNIT_TYPE_FLYING)
            elseif targetID==20 then
                return IsUnitType(filterU,UNIT_TYPE_HERO)
            endif
        endif
        return false
    endmethod

    private static method engagingTarget takes nothing returns nothing
        local thistype this
        local real xAttacker
        local real yAttacker
        local real xTarget
        local real yTarget
        local integer orderU
        local integer uID
        local integer i = 0
        loop
            set i = i + 1
            set this = instanceAr[i]
            set uID = GetHandleId(.engager)
            if LoadBoolean(HASH, uID, 1) and UnitAlive(.engager) then
                set xAttacker = GetUnitX(.engager)
                set yAttacker = GetUnitY(.engager)
                if .interval > 0 then
                    set .interval = .interval - INTERVAL
                    if UnitAlive(.target) and IsUnitEngageable(.target) then  
                        set xTarget = GetUnitX(.target)
                        set yTarget = GetUnitY(.target)
                        set orderU = GetUnitCurrentOrder(.engager)
                        if not IsUnitChanneling(.engager) then
                            if IsUnitType(.target,UNIT_TYPE_SLEEPING) then
                                call IssueTargetOrderById(.engager, ATTACK, .target)
                            else                              
                                call IssuePointOrderById(.engager, ATTACK, xTarget, yTarget)
                            endif
                        endif                        
                    else
                        set DATA = this
                        set .target = GetClosestUnit(xAttacker,yAttacker,Filter(function thistype.FilterEngage))
                        call SaveUnitHandle(HASH,uID,7,.target)
                        if .target==null then
                            call IssuePointOrderById(.engager, ATTACK, LoadReal(HASH, uID, 4), LoadReal(HASH, uID, 5))
                        endif
                    endif                  
                else
                    set DATA = this
                    set .interval = LoadReal(HASH, uID, 3) //resumes the timer
                    set .target = GetClosestUnit(xAttacker,yAttacker,Filter(function thistype.FilterEngage))
                    call SaveUnitHandle(HASH,uID,7,.target)
                endif
            else
                if not UnitHero(.engager) then
                    set .engager = null
                    set .target = null
                    //! runtextmacro deallocate()
                elseif not LoadBoolean(HASH, uID, 1) then
                    //Uses by heroes
                    set .engager = null
                    set .target = null
                    //! runtextmacro deallocate()                
                endif
            endif          
            exitwhen i==instance
        endloop
    endmethod

    static method addUnit takes unit e returns nothing
        local thistype this
        if instance < 8190 then
            set this = allocate()
            set .engager = e
            set .target = null
            set .interval = 0
            if instance==0 then
                call TimerStart(t, INTERVAL, true, function thistype.engagingTarget)
            endif
            set instance = instance + 1
            set instanceAr[instance] = this  
        else
            call DestroyTimer(t)
            debug call BJDebugMsg("ERROR: Limit of 8190 reached!, cannot allocate anymore instance: DESTROYING TIMER. ")      
        endif
    endmethod
endstruct
//========================================
//=========END OF ENGAGE CODE:============
//========================================

//===ITEM PICK:
private struct ItemPick
    unit hero
    private static thistype DATA
    //! runtextmacro EScreatetimer()
   
    private static method onPickItem takes nothing returns boolean
        local thistype this = DATA
        local integer orderID = GetUnitCurrentOrder(.hero)
        if orderID==MOVE or orderID==ATTACK then
            call IssueTargetOrderById(.hero, SMART, GetFilterItem())
        endif
        return false
    endmethod  

    private static method enumerateItem takes nothing returns nothing
        local thistype this
        local real x
        local real y
        local integer index
        local integer count
        local integer orderID
        local integer i = 0
        loop
            set i = i + 1
            set this = instanceAr [i]
            if LoadBoolean(HASH, GetHandleId(.hero), 1) then
                if UnitAlive(.hero) then
                    set count = 0
                    set index = 0
                    loop
                        if (UnitItemInSlot(.hero, index) != null) then
                            set count = count + 1
                        endif
                        set index = index + 1
                        exitwhen index==6
                    endloop
               
                    if count < MAX_ITEM_PICKED then
                        set x = GetUnitX(.hero)
                        set y = GetUnitY(.hero)
                        set DATA = this
                        call MoveRectTo(R, x, y)
                        call EnumItemsInRect(R, null, function thistype.onPickItem)
                    endif
                endif
            else
                set .hero = null
                //! runtextmacro deallocate()
            endif
            exitwhen i==instance
        endloop
    endmethod
   
    static method create takes unit hero returns thistype
        local thistype this = thistype.allocate()
        set .hero = hero
        if instance==0 then
            call TimerStart(t, 1.0, true, function thistype.enumerateItem)
        endif
        set instance = instance + 1
        set instanceAr[instance] = this
        return this
    endmethod
endstruct

//===RETREAT:
private struct Retreat
    unit hero
    unit retunit
    real minlife
    //! runtextmacro EScreatetimer()
   
    private static method searchtarget takes nothing returns nothing
        local thistype this
        local unit first
        local real xUnit
        local real yUnit
        local real distX
        local real distEx
        local real xBase
        local real yBase
        local integer orderID
        local integer uID
        local integer i = 0
        loop
            set i = i + 1
            set this = instanceAr [i]
            if LoadBoolean(HASH, GetHandleId(.hero), 1) then
                if UnitAlive(.hero) then
                    set orderID = GetUnitCurrentOrder(.hero)
                    if not IsUnitChanneling(.hero) and orderID==ATTACK or orderID==MOVE then
                        if GetWidgetLife(.hero) < .minlife then
                            set uID = GetHandleId(.hero)
                            set xUnit = GetUnitX(.hero)
                            set yUnit = GetUnitY(.hero)                          
                            static if ENABLE_RETREAT_TO_BUILDING then
                                if .retunit==null then
                                    //searches a place to retreat
                                    call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, null)
                                    loop
                                        set first = FirstOfGroup(bj_lastCreatedGroup)
                                        exitwhen first==null
                                        if UnitAlive(first) and not UnitEnemy(first, .hero) then
                                            if GetUnitTypeId(first)==FOUNTAIN_OF_HEALTH then
                                                set .retunit = first
                                                exitwhen true
                                            elseif UnitStructure(first) and not (GetOwningPlayer(first)==Player(PLAYER_NEUTRAL_PASSIVE)) then
                                                set .retunit = first                                
                                                exitwhen true
                                            endif
                                        endif
                                        call GroupRemoveUnit(bj_lastCreatedGroup, first)
                                    endloop
                                else
                                    if .retunit != null then
                                        set distEx = GetDistance(xUnit,yUnit,GetUnitX(.retunit),GetUnitY(.retunit))
                                        //searching fountain of health
                                        call IssuePointOrderById(.hero, MOVE, GetUnitX(.retunit), GetUnitY(.retunit))
                                    else
                                        set xBase = LoadReal(HASH, uID, 4)
                                        set yBase = LoadReal(HASH, uID, 5)
                                        set distX = GetDistance(xUnit,yUnit,xBase,yBase)
                                        if distX > (1300*1300) then
                                            //if no more allies, the AI will return to where is it assigned
                                            call IssuePointOrderById(.hero,MOVE,xBase,yBase)
                                        endif
                                    endif
                                endif                  
                            else
                                set xBase = LoadReal(HASH, uID, 4)
                                set yBase = LoadReal(HASH, uID, 5)
                                set distX = GetDistance(xUnit,yUnit,xBase,yBase)
                                if distX > (1300*1300) then
                                    //if no more allies, the AI will return to where is it assigned
                                    call IssuePointOrderById(.hero,MOVE,xBase,yBase)                
                                endif
                            endif              
                        endif  
                    endif
                endif
            else
                set .hero = null
                set .retunit = null
                //! runtextmacro deallocate()              
            endif
            exitwhen i==instance
        endloop
    endmethod
   
    static method create takes unit u, real x, real y returns thistype
        local thistype this = thistype.allocate()
        set .hero = u
        set .minlife = GetUnitState(u, UNIT_STATE_MAX_LIFE)*MAX_LIFE_PERCENTAGE
        set .retunit = null
        if instance==0 then
            call TimerStart(t, 1.0, true, function thistype.searchtarget)
        endif
        set instance = instance + 1
        set instanceAr[instance] = this
        return this  
    endmethod
endstruct

//===INITIALIZER:
private struct EngageInit
    private static method onInit takes nothing returns nothing
        local trigger t
        static if ENABLE_PICK_UP_ITEM then
            set R = Rect(-AOE_ITEM_PICK, -AOE_ITEM_PICK, AOE_ITEM_PICK, AOE_ITEM_PICK)
        endif
        set DUMMY = CreateUnit(Player(15), 'hmpr', 0,0,0)
        call UnitAddAbility(DUMMY, SPELL_ID)
        call UnitAddAbility(DUMMY, 'Aloc')
        call ShowUnit(DUMMY, false)
        set t = null
    endmethod
endstruct

struct EngageSystem
    //===SETUP EVERYTHING: SYSTEM API
    //attackcaptain - sets the attack captain which will find and guard heroes or hero that will lead the attack group
    static method SetEngageUnit takes unit u, boolean attackcaptain returns nothing
        local integer unitID = GetHandleId(u)
        if FilterEngagingUnit(u) then
            //Removes the guard position so that AI's dont go back to their original location
            if not LoadBoolean(HASH,unitID,1) then
                call RemoveGuardPosition(u)
                call SaveBoolean(HASH, unitID, 1, true)
                if UnitHero(u) or not attackcaptain then
                    call SaveBoolean(HASH, unitID, 6, true)
                    call IndependentEngager.addUnit(u)
                else
                    call Follower.addFollower(u)
                endif
            else
                debug call BJDebugMsg("ERROR: SetEngageUnit, "+GetUnitName(u)+" has already been registered!")
            endif
        endif
    endmethod

    //Works only for computer controlled unit and heroes
    static method SetUnitPickItem takes unit u returns nothing
        static if ENABLE_PICK_UP_ITEM then
            if FilterEngagingUnit(u) and UnitHero(u) then
                call ItemPick.create(u)
            endif
        else
            debug call BJDebugMsg("ERROR: ENABLE_PICK_UP_ITEM is false, set it to true!")
        endif
    endmethod
   
    static method SetUnitRetreat takes unit u returns nothing
        static if ENABLE_RETREAT then
            if FilterEngagingUnit(u) then
                call Retreat.create(u, GetUnitX(u), GetUnitY(u))
            endif
        else
            debug call BJDebugMsg("ERROR: ENABLE_RETREAT is false, set it to true!")
        endif
    endmethod
   
    /* Targets;
        1 to 16 = Targets a specific ENEMY PLAYER
        17 = Targets ALL ENEMY PLAYERS
        18 = Targets only enemy STRUCTURES
        19 = Targets only enemy FLYING UNITS
        20 = Targets only enemy HEROES
    */

   
    static method SetUnitLockTargets takes unit u, integer targets, real atkdur returns nothing
        local integer unitID = GetHandleId(u)
        if IsUnitType(u, UNIT_TYPE_HERO) or LoadBoolean(HASH,unitID,6) and FilterEngagingUnit(u) then
            if targets < 17 and targets > 0 then
                if GetOwningPlayer(u)==Player(targets-1) then
                    debug call BJDebugMsg("ERROR: Player "+I2S(targets)+" can't target his own units!")
                else
                    call SaveInteger(HASH, unitID, 2, targets)
                endif
       
            elseif targets > 16 and targets < 21 then
                call SaveInteger(HASH, unitID, 2, targets)      
            else
                debug call BJDebugMsg("ERROR: Please input only from 1 to 20!")
            endif
            call SaveReal(HASH,unitID,3,atkdur)
        endif
    endmethod
   
    //Sets the specific hero to protect, if main hero is dead, it will search for a new hero
    static method SetHeroToProtect takes unit follower, unit hero returns nothing
        if not LoadBoolean(HASH, GetHandleId(follower), 6) and FilterEngagingUnit(follower) then
            call SaveUnitHandle(HASH, GetHandleId(follower), 8, hero)
        endif
    endmethod
   
    static method SetReturnPoints takes unit u, real xReturn, real yReturn returns nothing
        call SaveReal(HASH, GetHandleId(u), 4, xReturn)
        call SaveReal(HASH, GetHandleId(u), 5, yReturn)
    endmethod
   
    static method RemoveEngagingUnit takes unit u returns nothing
        call FlushChildHashtable(HASH,GetHandleId(u))
    endmethod

endstruct

endlibrary
 



Parameter explanation:

1)
static method SetEngageUnit takes unit u, boolean attackcaptain returns nothing

This is the FIRST thing you need to do.
attackcaptain - sets the attack captain which will follow/guard heroes or hero that will lead the attack group

2)
static method SetUnitPickItem takes unit u returns nothing

3)
static method SetUnitRetreat takes unit u returns nothing

Enables Hero to pick items and retreats respectively, these are optional

4)
static method SetUnitLockTargets takes unit u, integer targets, real atkdur returns nothing

This is the SECOND thing you need to do, coz engaging must have targets.
targets - Locks a particular ENEMY target
1 to 16 = Targets a specific ENEMY PLAYER
17 = Targets ALL ENEMY PLAYERS
18 = Targets only enemy STRUCTURES
19 = Targets only enemy FLYING UNITS
20 = Targets only enemy HEROES

Take note that the Targets reffers to the MAIN TARGET

atkdur - Sets how long an attacker to lock to a particular target

5)
static method SetReturnPoints takes unit u, real xReturn, real yReturn returns nothing

This is the THIRD thing you need to do, the return locations.
Sets and Changes the Return point of the AI if no more targets around

6)
static method SetHeroToProtect takes unit follower, unit hero returns nothing

This is optional

7)
static method RemoveEngagingUnit takes unit u returns nothing

Removes the Engager from the system

NOTE:
All the u is the unit or the AI that's engaging.



How to use in GUI?
  • Demo
    • Events
      • Time - Elapsed game time is 1.00 seconds
    • Conditions
    • Actions
      • Unit Group - Pick every unit in (Units owned by Player 2 (Blue)) and do (Actions)
        • Loop - Actions
          • Set U = (Picked unit)
          • Custom script: call EngageSystem.SetEngageUnit(udg_U,true)
          • Custom script: call EngageSystem.SetUnitPickItem(udg_U)
          • Custom script: call EngageSystem.SetUnitRetreat(udg_U)
          • Custom script: call EngageSystem.SetReturnPoints(udg_U, GetUnitX(udg_U),GetUnitY(udg_U))
          • -------- Player 3's target is 17, so his main targets are ALL ENEMIES --------
          • Custom script: call EngageSystem.SetUnitLockTargets(udg_U, 17, 30)
      • Unit Group - Pick every unit in (Units owned by Player 3 (Teal)) and do (Actions)
        • Loop - Actions
          • Set U = (Picked unit)
          • Custom script: call EngageSystem.SetEngageUnit(udg_U,true)
          • Custom script: call EngageSystem.SetUnitPickItem(udg_U)
          • Custom script: call EngageSystem.SetUnitRetreat(udg_U)
          • Custom script: call EngageSystem.SetReturnPoints(udg_U, GetUnitX(udg_U),GetUnitY(udg_U))
          • -------- Player 3's target is 2, so his main targets are Player 2 --------
          • Custom script: call EngageSystem.SetUnitLockTargets(udg_U, 2, 30)
          • -------- sets all non-hero engaging units to protext/follow the hero --------
          • Custom script: call EngageSystem.SetHeroToProtect(udg_U,udg_HERO)


Changelogs

version 3.2
- Added IsUnitChanneling library for faster use

version 3.1
-
SetUnitLockTargets
added several targets to engage in a dynamic way (see above)
- Added 1 function
SetHeroToProtect
to follow/protect a specific main hero
- Fixed engager cannot attack when near his base

version 3.0
- Fixed ItemPick and Retreat struct coz it doesnt work on version 2.9

version 2.9 (This version is very buggy)
- TimerUtils removed and replaced by one timer for each struct
- GetClosestWidget system added
- Functions converted to struct static methods
- Added 3 more methods
- Removes AI from system manually
- xReturn, yReturn, atkdur and targets can now be modified dynamically

version 2.8
- Bribes suggestions applied.
- Now it cant lock invisible, invulnerable and burrowed targets.
- Fix a bug that AI will not attack sleeping unit.

version 2.7
- Added TimerUtils library.
- Made 3 separate functions instead of one.
- LearnNormalSkillLib, arg rects removed.
- Uses FirstOfGroup loops for faster enumeration.

version 2.6
- Added 3 parameters for individual timer for engage, pickup item and retreat
- Initializer replaced by struct onInit
- Added 4 function calling
- Added 1 function to check if the AI is channeling or not
- Added AI engage for summoned units to protect the hero
- Fixed minor retreat point as pointed out by bugers

version 2.4
- Retreat converted to struct (Retreat.create)
- Struct intances replaced by term "create"
- Added boolean parameter for SetEngageUnit for optional guard/follow hero
- Locked retreat to created point added
- Locked retreat to fountain of health or building added
- Codes more efficient and reduced
- GetWidgetLife replaced by IsUnitType DEAD
- Engage.create added 4 additional parameters
- AI will not move if less than health% is set (works only if ENABLE_RETREAT is true)
- RemoveGuardPost added

version 2.2
- AI retreats to any ally base if no more enemies
- Codes searching is reduced from 5 to 3
- Random search is done by the Filter, not at the setup
- Most AOE setup is replaced by Map playable area for better area search
- New test map

version 2.0
- Timer loops are replaced by trigger loops
- Single Hashtable usage
- Fixed a bug that caused the units stay in the center
- Added condition structure type not included in the Engage call
- Attacked is replaced by Damage detection for the retreat trigger
- Configurables are placed on top
- Test map added waygates to test if unit will search enemy in that way


Keywords:
AI, attack, engage, DotA, AoS, item, buy, sell, lock, spell, diablo, fire, mckill2009
Contents

EngageSystem (Map)

Reviews
Moderator
30 Jan 2012 Bribe: A very well-written AI system. Approved 5/5.
  1. 30 Jan 2012
    Bribe: A very well-written AI system. Approved 5/5.
     
  2. mckill2009

    mckill2009

    Joined:
    Mar 10, 2009
    Messages:
    4,696
    Resources:
    34
    Maps:
    5
    Spells:
    27
    JASS:
    2
    Resources:
    34
    I searched the spell section for AI systems, seems i found none, unless I'm wrong...
     
  3. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,746
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Setting the target always to the first of the group is going to always pick the unit with the lowest y-coordinate. You should probably use a "GetClosestUnit" function (found under JASS Resources) to make this more modular.

    You should also move the "SetupSkillsAndItemTypes" to the top of the script because it is configurable.

    You also just need 1 hashtable for this, just use the "-parentKey" or "-childKey" trick I showed you.
     
  4. .VTZ.

    .VTZ.

    Joined:
    Dec 17, 2009
    Messages:
    784
    Resources:
    0
    Resources:
    0
    Nice, Mckill u know something about majesty? (AI Type)
     
  5. Ironside

    Ironside

    Joined:
    Feb 3, 2009
    Messages:
    2,727
    Resources:
    4
    Maps:
    3
    Tutorials:
    1
    Resources:
    4
    I have a few questions:

    1. Will the AI cast spells based on channel?
    2. Will the AI work on a map in which players control 1 hero and fight each other, there are also a lot of creeps and there is no base (structures) (there are fountains of regeneration though)?
    3. Will the AI use the waygate if that's the only way to enter & leave base?
     
  6. mckill2009

    mckill2009

    Joined:
    Mar 10, 2009
    Messages:
    4,696
    Resources:
    34
    Maps:
    5
    Spells:
    27
    JASS:
    2
    Resources:
    34
    @Bribe
    I'll fix it dude, thanks...

    @VTZ
    sorry no...

    @Ironside
    1. No, channel base spells should be ordered manually.
    2. Yes
    3. I think I'll add that option to move to a waygate.
     
  7. Ironside

    Ironside

    Joined:
    Feb 3, 2009
    Messages:
    2,727
    Resources:
    4
    Maps:
    3
    Tutorials:
    1
    Resources:
    4
    Ok I'll try it right now.
     
  8. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    The point of libraries is to have the option to require other needed systems, which should be applied here.

    And my eyes hurt every time i see the line "InitHashtable()" - this time twice!

    And I also do wonder why this - very important function - is at the bottom of the code? :)

    Code (vJASS):
    // Set the skills and item types here!
    private function SetupSkillsAndItemTypes takes nothing returns nothing
        //You must setup the skills here
        set SKILL[1] = 'A000'
        set SKILL[2] = 'A000'
        set SKILL[3] = 'A000'
        set SKILL[4] = 'A000'
        set SKILL[5] = 'A000'
        //IMPORTANT NOTE: if you increase the item types created, you should increase also the MAX_ITEM_TYPE of the globals
        set ITEM_TYPE[1] = 'will' //Default Wand of Illusion
        set ITEM_TYPE[2] = 'woms' //Default Wand of Mana Stealing
        set ITEM_TYPE[3] = 'wlsd' //Default Wand of Lightning Shield
        //set ITEM_TYPE[4] = 'ADDITIONAL ITEM'
        //set ITEM_TYPE[5] = 'ADDITIONAL ITEM'
    endfunction
     
     
  9. Ironside

    Ironside

    Joined:
    Feb 3, 2009
    Messages:
    2,727
    Resources:
    4
    Maps:
    3
    Tutorials:
    1
    Resources:
    4
    I added it to the map and it seems I'm in luck, the AIs automatically went to the path where the way gate is and so they went outside of base on their own.

    Guess I've found the perfect solution.

    EDIT:

    I think I found a bug, the units are moving towards the middle of the map every time, and soon it turns into a middle of map massacre and then it repeats.
     
    Last edited: Aug 10, 2011
  10. mckill2009

    mckill2009

    Joined:
    Mar 10, 2009
    Messages:
    4,696
    Resources:
    34
    Maps:
    5
    Spells:
    27
    JASS:
    2
    Resources:
    34
    @baassee
    OK, ok Ima do that, Bribe already said that btw :)...

    @Ironside
    - I dunnu, maybe AI is programmed to function to find gateways?, in that case good!...
    - Ima fix the middle of the map massacre, before I already added a search
    and move location but I removed that function, I guess Ima put it again...

    EDIT:
    ===UPDATE===
    version 2.0
    - Timer loops are replaced by trigger loops
    - Single Hashtable usage
    - Fixed a bug that caused the units stay in the center
    - Added condition structure type not included in the Engage call
    - Attacked is replaced by Damage detection for the retreat trigger
    - Configurables are placed on top
    - Test map added waygates to test if unit will search enemy in that way
     
    Last edited: Aug 11, 2011
  11. Ironside

    Ironside

    Joined:
    Feb 3, 2009
    Messages:
    2,727
    Resources:
    4
    Maps:
    3
    Tutorials:
    1
    Resources:
    4
    I tried the new version. They went to the middle still, but this time they even ignored all the enemies along the path.
     
  12. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    This would run a lot better on a Kd-tree.

    I'll write a UnitsInRange lib in the next couple of days that runs on a Kd-tree.
     
  13. mckill2009

    mckill2009

    Joined:
    Mar 10, 2009
    Messages:
    4,696
    Resources:
    34
    Maps:
    5
    Spells:
    27
    JASS:
    2
    Resources:
    34
    that's really strange coz based on the test map I posted, seems OK...they
    retreat to the fountain of health once all enemies are gone...anway I'll check
    it out...
     
  14. Ironside

    Ironside

    Joined:
    Feb 3, 2009
    Messages:
    2,727
    Resources:
    4
    Maps:
    3
    Tutorials:
    1
    Resources:
    4
    Then maybe it's because in my map there are 4 fountains in the middle of map. Though they went there before reaching the retreat life percent.
     
  15. Jay the Editor

    Jay the Editor

    Joined:
    Aug 12, 2008
    Messages:
    317
    Resources:
    6
    Icons:
    2
    Spells:
    4
    Resources:
    6
    Will this AI system work in an arena-based map?
     
  16. mckill2009

    mckill2009

    Joined:
    Mar 10, 2009
    Messages:
    4,696
    Resources:
    34
    Maps:
    5
    Spells:
    27
    JASS:
    2
    Resources:
    34
    yes it will, sample if AI will train a hero then...

    • Custom script: call SetEngageUnit(GetTrainedUnit())
     
  17. Exavier00

    Exavier00

    Joined:
    Jul 7, 2010
    Messages:
    63
    Resources:
    0
    Resources:
    0
    Wow! its the first time I've seen one here.
     
  18. Jay the Editor

    Jay the Editor

    Joined:
    Aug 12, 2008
    Messages:
    317
    Resources:
    6
    Icons:
    2
    Spells:
    4
    Resources:
    6
    Perfect then =3 I've been in dilemma on how to create AI for my map and finally you have created such great system, mckill!
     
  19. mckill2009

    mckill2009

    Joined:
    Mar 10, 2009
    Messages:
    4,696
    Resources:
    34
    Maps:
    5
    Spells:
    27
    JASS:
    2
    Resources:
    34
    thanks, In my next update, Ima put an arena test map for testing...