• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

AI - Engage System v3.2

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.


JASS:
/*
===================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)

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
30 Jan 2012 Bribe: A very well-written AI system. Approved 5/5.
Level 7
Joined
Sep 2, 2011
Messages
350
When a player kills an AI, the game kill text does not appear and it doesn't count the kill.

Example: I killed an AI.
It doesn't show a "Player pwned Computer for 100 gold!" unlike in the previous version of this system. Now it is updated, it doesn't show anymore.

Btw, I'm using jAssyst
 
Level 7
Joined
Sep 2, 2011
Messages
350
jAssyst

JASS:
library jAssyst initializer oninit uses GetPlayerColored, TimerUtils, AIDS//UnitIndexingUtils //or AutoIndex
/*// ======================================================================================

    'jAssyst VER 1.5'       by Akolyt0r        (10.03.2010)

    REQUIREMENTS:
        + vJass Precompiler (e.g. Jasshelper)
        + GetPlayerColored by Ammorth
        + UnitIndexingUtils by Rising_Dusk (can be changed easily to other indexing Systems)
        
    OPTIONAL:
        There are two modules for registering different events as "assist".
        You can use one, two, or none of them. 
        (When you dont use any of them you will have to register all "assists" yourself !)
        + DamageDetect  
            Available for 3 Damage Detection Systems at the moment.
            Use only 1 (ONE) flavor of this module at once.
            - LLDD ("Light Leakless Damage Detect") by Captain_Griffen
            - "Intuitive Damage Detection System" by Rising_Dusk
            - "ADamage" by Anitarf
            
        + SpellBonus
            Most maps contain aswell spells which dont deal any damage,
            So this module is for registering such spells (for example Slow or Curse) 
            as assists aswell.
        
    USAGE:
        call RegisterAssistUnit(unit)
            // Registers a unit with the system, every attack by enemy units 
            // against this unit will be counted as assist, when that attack happened in the last
            // MAX_ASSIST_DELAY seconds before the death of the unit.
        
        call UnRegisterAssistUnit(unit) //< Unregisters a unit with the system,

    ADDITIONAL FUNCTIONS (for Advanced Users):
        IsAssistUnitRegistered(unit) //< self explanatory..
        RegisterAssist(unit assistingUnit, unit victimUnit)
            // Registers an assist: assistingUnit helped to try to kill victimUnit..
            // So if victimUnit dies in less than MAX_ASSIST_DELAY seconds, assistingUnit will be 
            // displayed as assisting unit.
            // victimUnit has to be registered with the systems RegisterAssistUnit
            // For most cases the existing modules should be sufficient, so you probably wont have to call
            // this method yourself.

*/// ======================================================================================
     globals
        private constant integer MAX_FOE_PLAYERID = 16
            //"Highest Player" (=>PlayerId) you want to include, examples:
            // 12 for maps with 12 players (without neutral units)
            // 16 for maps with 12 players (with neutral creeps)
            // 4 for maps with 4 players (WITHOUT neutral creeps)
            // 16 for maps with 4 players (WITH neutral creeps)
            // ... if you are unsure keep 16..

        private constant integer MAXIMUM_ASSIST_UNITS    = (8190/MAX_FOE_PLAYERID)-1
            // Maximum ammount of units registered with the sytem
            // Best performance with MAXIMUM_ASSIST_UNITS smaller than 8190/MAX_FOE_PLAYERID
            // since most maps will only register assists for hero units, and i never saw
            // a map with more than (8190/16 =) 510 heroes, this shouldnt be a problem
        
        private constant real MAX_ASSIST_DELAY  = 25. 
            // only damage dealt in the last MAX_ASSIST_DELAY seconds is considered an assist
        
        private real    UNREGISTER_TIMEOUT = 120.            
        private boolean UNREGISTER_DEAD_UNITS = true
            // will automatically unregister dead, NONHERO, units after UNREGISTER_TIMEOUT seconds
        
        private constant string AND = " and " // used to format the display 
        private constant string COMMA = ", "  // of the assists, you can play with these, but these should work.
    endglobals
    

 //========
 //// These functions are called to build the strings to be printed when the death of an registered unit occurs.
   // Edit to fit your needs
   
    private function GetSuicideMessage  takes unit dieingUnit, player VictimAndKiller returns string
        return GetPlayerNameColored(VictimAndKiller)+" killed himself."
    endfunction
    private function GetDenialMessage   takes unit dieingUnit, player dieUOwner, unit killingUnit, player killUOwner returns string
        return GetPlayerNameColored(killUOwner)+" denied "+GetPlayerNameColored(dieUOwner)+"'s "+GetUnitName(dieingUnit)+"."
    endfunction
    
    //string AssistingPlayers contains the assisting players separated by the constants COMMA and AND
    private function GetAssistsMessage   takes unit dieingUnit, player dieUOwner, unit killingUnit, player killUOwner, string AssistingPlayers, integer totalAssists returns string
        return GetPlayerNameColored(killUOwner)+" killed "+GetPlayerNameColored(dieUOwner)+"'s "+GetUnitName(dieingUnit)+", "+AssistingPlayers+" assisted."
    endfunction    
    
    private function GetNoAssistsMessage   takes unit dieingUnit, player dieUOwner, unit killingUnit, player killUOwner, integer bounty returns string
        return GetPlayerNameColored(killUOwner)+" just pwned "+GetPlayerNameColored(dieUOwner)+"'s head for |cffffcc00" + I2S(bounty) + "|r gold!"
    endfunction  
 //    dieUOwner  == Owner of dieing Unit
 //    killUOwner == Owner of killing Unit    
 ///////========
 

    
    
private struct Players //struct to pass the players who helped
    boolean array PlayerAssisted[MAX_FOE_PLAYERID]
        // PlayerAssisted[player_id] will be true if Player(player_id) assisted
endstruct

//========
////The function below will be called upon Death of a registered Unit

private function KillRegistered takes unit dieingUnit, player Victim, unit killingUnit, player Killer, Players assistingPlayers returns nothing
    // this function will only be called for (dieing)Units which are registered with RegisterAssistUnit,
    // will not be called for suicides.
    // the argument assistingPlayers contains all players who helped to kill the (dieing)Unit. (Players struct, see above)
    // you can add your own stuff here 
        //...like giving bonus gold for players which helped to kill an enemy hero.
    
    call assistingPlayers.destroy() //leave this
endfunction    
////   
///////======== 
    
globals 
        private integer array DATA[MAXIMUM_ASSIST_UNITS] // this variable only be used by the to functions below, 
                                                        //  so you can ommit this easily, if you use another method 
                                                       //   to store the data
endglobals

 // You can edit these functions (together with DATA) to use another library instead of UnitIndexingUtils
 // e.g. PUI, Table (hashtable-version recommended for 1.23b and above), H2I(u)-OFFSET (not recommended), or whatever you want.
private function getStruct takes unit u returns integer
    return DATA[GetUnitId(u)]
endfunction
    
private function setStruct takes unit u, integer data returns nothing
    set DATA[GetUnitId(u)] = data
endfunction

//////=== END OFCONFIGURATION =======================================================================
//////=== PRIVATE STRUCTS ===========================================================================        
    globals
        private constant integer AP_STRUCT_SIZE = MAXIMUM_ASSIST_UNITS*MAX_FOE_PLAYERID    
    endglobals
    private struct AP[AP_STRUCT_SIZE]
        readonly boolean active = false
        private timer t
        private boolean destroyed = true
        private static code timerCB
        private static method timerExpire takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local AP this = GetTimerData(t)
            set this.active = false
            set t = null
        endmethod
        method setActive takes boolean act returns nothing
            if this.active then
                call PauseTimer(this.t)
            endif
            set this.active = act
            if act then
                call TimerStart(this.t,MAX_ASSIST_DELAY,false,function AP.timerExpire)
            endif
        endmethod        
        static method create takes nothing returns AP
            local AP this = AP.allocate()
            set this.t = NewTimer()
            set this.active = false
            call SetTimerData(this.t,this)
            return this
        endmethod        
        method terminate takes nothing returns nothing
            call ReleaseTimer(this.t)
            call this.destroy()
        endmethod
    endstruct
    
    
    private struct AssistUnit[MAXIMUM_ASSIST_UNITS]
        unit u
        force Aplayers
        timer aut
        AP array APs[MAX_FOE_PLAYERID]
        static group MU
        static method create takes nothing returns AssistUnit
            local AssistUnit this = AssistUnit.allocate()
            set this.u   = null
            set this.aut = null
            return this
        endmethod
        private method onDestroy takes nothing returns nothing
            local integer i = 0
            if this.aut != null then
                call ReleaseTimer(this.aut)
                set this.aut = null
            endif
            set this.u = null
            loop
                exitwhen i == MAX_FOE_PLAYERID
                call this.APs[i].terminate()
                set i = i + 1
            endloop 
            call GroupRemoveUnit(AssistUnit.MU,this.u)
        endmethod
    endstruct
    
    
//////=== PUBLIC FUNCTIONS ===========================================================================        

    function IsAssistUnitRegistered takes unit u returns boolean
        return IsUnitInGroup(u,AssistUnit.MU)
    endfunction
    
   function RegisterAssistUnit takes unit u returns nothing
        local AssistUnit this = AssistUnit.create()
        local integer i = 0
        loop
            exitwhen i==MAX_FOE_PLAYERID
            set this.APs[i]=AP.create()
            set i = i + 1
        endloop
        set this.u = u
        if this.Aplayers == null then
            set this.Aplayers = CreateForce()
        endif
        debug if IsUnitInGroup(u,AssistUnit.MU) then
        debug   call BJDebugMsg(SCOPE_PREFIX+"Error: unit: \""+GetUnitName(u)+"\" already registered !")
        debug endif
        call GroupAddUnit(AssistUnit.MU,u)
        call setStruct(u,this)        
    endfunction

    function UnRegisterAssistUnit takes unit u returns boolean
            local AssistUnit this = getStruct(u)
            if this.u != u then
                debug call BJDebugMsg(SCOPE_PREFIX+"Error: This unit: \""+GetUnitName(u)+"\" has not been registered with this system !")
                return false
            endif
            call this.destroy()
            return true
    endfunction

    function RegisterAssist takes unit assistingUnit, unit victimUnit returns nothing
        local player vicUOwner  = GetOwningPlayer(victimUnit)
        local player assUOwner = GetOwningPlayer(assistingUnit)
        local integer pid = GetPlayerId(assUOwner)
        local AssistUnit this = getStruct(victimUnit)
        
        if this.u == victimUnit then
            call this.APs[pid].setActive(true)
        debug else
        debug   call BJDebugMsg(SCOPE_PREFIX+"_RegisterAssist_Error: This unit: \""+GetUnitName(victimUnit)+"\" has not been registered with this system !")
        endif    
    endfunction
    
//////=== PRIVATE FUNCTIONS ===========================================================================  
    private function unRegisterAUTimed takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local AssistUnit this = GetTimerData(t)
        if IsUnitType(this.u,UNIT_TYPE_DEAD)==true then
            call this.destroy()
        endif
        call ReleaseTimer(t)
        set this.aut = null
        set t = null
    endfunction
    
    private struct jAssystImpl
    
        static method unknown takes player p returns nothing
            implement ImplementKills
        endmethod
    endstruct
  
    private function onDeath takes nothing returns nothing
        local unit dieingUnit = GetDyingUnit()
        local player dieUOwner = GetOwningPlayer(dieingUnit)
        local unit killingUnit = GetKillingUnit()
        local player killUOwner = GetOwningPlayer(killingUnit)
        local integer i = 0
        local string text = ""
        local integer array pids
        local integer pcount = 0
        local AssistUnit this
        local Players assistingPlayers
        local timer t
        local integer bounty = 0
        if IsUnitInGroup(dieingUnit,AssistUnit.MU) then
            set this = getStruct(dieingUnit)
            if killUOwner != dieUOwner then
                if IsPlayerEnemy(killUOwner,dieUOwner) and IsUnitType(dieingUnit,UNIT_TYPE_HERO) then
                    set assistingPlayers = Players.create()
                    set udg_Score_Kills[GetConvertedPlayerId(killUOwner)] = udg_Score_Kills[GetConvertedPlayerId(killUOwner)] + 1
                    set udg_Score_Death[GetConvertedPlayerId(dieUOwner)] = udg_Score_Death[GetConvertedPlayerId(dieUOwner)] + 1
                    loop
                        exitwhen i == MAX_FOE_PLAYERID
                        if this.APs[i].active and Player(i) != killUOwner and IsPlayerAlly(Player(i),killUOwner) then
                            call this.APs[i].setActive(false)
                            set pids[pcount] = i
                            set pcount = pcount + 1
                            set assistingPlayers.PlayerAssisted[i] = true
                            set udg_Score_Assist[GetConvertedPlayerId(Player(i))] = udg_Score_Assist[GetConvertedPlayerId(Player(i))] + 1
                        else
                            set assistingPlayers.PlayerAssisted[i] = false
                        endif
                        set i = i + 1
                    endloop
                    call KillRegistered(this.u,dieUOwner,killingUnit,killUOwner,assistingPlayers)
                    set i = 0
                    loop
                        exitwhen i == pcount
                        set text = text + GetPlayerNameColored(Player(pids[i]))
                        if pcount-i==2 then
                            set text = text + AND
                        elseif pcount-i >2 then
                            set text = text + COMMA
                        endif
                        set i = i + 1                    
                    endloop
                    if ISTEAMMODE == false then
                        set pcount = 0
                    endif
                    set bounty = 175 + (10*GetHeroLevel(dieingUnit))
                    call AdjustPlayerStateBJ( bounty, killUOwner, PLAYER_STATE_RESOURCE_GOLD )
                    call jAssystImpl.unknown(killUOwner)
                    if pcount == 0 then
                        set text = GetNoAssistsMessage(this.u,dieUOwner,killingUnit,killUOwner,bounty)
                    else
                        set text = GetAssistsMessage(this.u,dieUOwner,killingUnit,killUOwner,text,pcount)
                    endif
                else
                    set text = GetDenialMessage(this.u,dieUOwner,killingUnit,killUOwner)
                    set udg_Score_Death[GetConvertedPlayerId(dieUOwner)] = udg_Score_Death[GetConvertedPlayerId(dieUOwner)] + 1
                endif
            else
                set text = GetSuicideMessage(this.u,dieUOwner)
                set udg_Score_Death[GetConvertedPlayerId(dieUOwner)] = udg_Score_Death[GetConvertedPlayerId(dieUOwner)] + 1
            endif
            call DisplayTextToPlayer(GetLocalPlayer(),0,0,text)
            //call DKStreak.start(killingUnit)
            if UNREGISTER_DEAD_UNITS then
                if IsUnitType(dieingUnit,UNIT_TYPE_HERO)==false then
                    if this.aut == null then
                        set this.aut = NewTimer()
                        call SetTimerData(t,this)
                        call TimerStart(this.aut,UNREGISTER_TIMEOUT,false,function unRegisterAUTimed)
                    else //Unit died again ... so we have to reset the timeout
                        call PauseTimer(this.aut)
                        call TimerStart(this.aut,UNREGISTER_TIMEOUT,false,function unRegisterAUTimed)
                    endif
                endif
            endif
        endif
        call ExecuteFunc("Sort_Multiboard")
        set killingUnit = null          
        set dieingUnit = null
    endfunction
  
    private function oninit takes nothing returns nothing
        local trigger t = CreateTrigger()
        set AssistUnit.MU = CreateGroup()
        call TriggerAddAction(t,  function onDeath)
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
    endfunction
endlibrary

Here is the code. Now, when the player(user/real person) kills a computer / AI. The text / string "Player pwned Computer" does not show. But if the AI kills the player, the string appears. I'm not good in Jass, but please help. :D
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Level 7
Joined
Sep 2, 2011
Messages
350
^Are you talking about my code?

Well, if you do, my friend uses this code to have a assist system
ex: Player pwned computer assist: Player 2

and It does work before. Do you recommend using other system
rather than this?

Edit:
I'm thinking of replacing this system with maggy's ASS (I find that funny)
Is that the a good decision?
 
Level 7
Joined
Sep 2, 2011
Messages
350
Thank you. Currently I'm replacing my old assist system to maggy's ASS.
Now, after saving the map. An error came up which is this.
JASS:
            call .bool.flush()
"Flush requires more arguments"
I don't know what the hell it is but it came from the trigger "ASS".

Edit:
Nevermind, I solved it by myself.
Gah. I realized that I have been using old libraries.
I'm going to find or create one for myself to make my map better.
 
Level 7
Joined
Sep 2, 2011
Messages
350
I just saw your resources especially the Unit Indexer and my god it is so amazing.
I've updated my Table library and other stuff. Most of my library is made by you so
thank you for making it possible :D

I have a question. Maggy's AS System (So it can be appropriate)
Dislikes the use of AIDS and DAMAGE. Now few of my spells are bugged, is there
anyway of fixing them? a new library perhaps that stacks with AS System?
 
Level 7
Joined
Sep 2, 2011
Messages
350
I'm starting to slowly understand the world of JASS now.
But, Man, it's so difficult.

What is this?
"Undeclared function Damage_Physical"
JASS:
                    call Damage_Physical(s__EchoStomp__EchoStomp_caster[dat] , f , EchoStomp__DAMAGE , ATTACK_TYPE_HERO , false , false)
"Undeclared function Damage_RegisterEvent"
JASS:
            call Damage_RegisterEvent(t)

Let me guess. These functions came from the old jAssyst? So, by removing these lines
would resolve my problems, correct?
 
Level 7
Joined
Sep 2, 2011
Messages
350
Okay, now I understand. I see how JASS is so difficult. Now, I have to fix my map.
Thanks for the Help! :D
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
Short Review

  • JASS:
    //- The AI will lock even if the unit is Invisible or Invulnerable or Burrowed
    You can easily fix this with IsUnitVisible
  • You should have your configurables come first. The non-configurables should be underneath
    JASS:
    //=============ENGAGE CODE:===============
  • Shouldn't private constant integer FOUNTAIN_OF_HEALTH = 'nfoh' be a configurable?
  • You should have more detailed comments about what each constant does. For example, ENABLE_NEAR_TARGET doesn't really tell me anything.
  • All your function wrappers should not be in the configuration section except for FilterEngagingUnit and UnitOrderNotChannel
  • JASS:
    if UnitAlive(first) and UnitHero(first) and @not@ UnitEnemy(.hero, first) then
    Is that not supposed to be in there?
  • if UnitEnemy(first, .hero) thenYou should have a UnitAlive check here.
  • Shouldn't GetRandomInt(1,3) be configurable?
  • JASS:
    if GetUnitTypeId(first)==FOUNTAIN_OF_HEALTH then
    ...
    elseif UnitStructure(first) and not (GetOwningPlayer(first)==Player(PLAYER_NEUTRAL_PASSIVE)) then
    Use an or statement with parentheses since you're still doing the same thing.
  • Wouldn't it make more sense for the AI to choose the closest building instead of the first unit found from a FoG loop?
  • exitwhen .retunit==first -> exitwhen true
  • For SetUnitPickItem and SetUnitRetreat, you should have the static ifs outside them.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
Thanks guys for you positive approach...

IsUnitVisible does not work if the map has fogs of war...but I have worked something like this and it worked really fine...
JASS:
library IsUnitEngageable initializer init

globals
    private constant integer SPELL_ID = 'A000' //Unholy frenzy that targets all units
    private constant integer DUMMY_ID = 'hmpr'
    private constant integer ORDER_ID = 852209 //Unholy frenzy that targets all units
    private unit DUMMY
endglobals

function IsUnitEngageable takes unit u returns boolean
    call SetUnitPosition(DUMMY, GetUnitX(u), GetUnitY(u))
    call IssueTargetOrderById(DUMMY, ORDER_ID, u)
    if GetUnitCurrentOrder(DUMMY)==ORDER_ID then
        return true
    endif
    call IssueImmediateOrder(DUMMY, "stop")
    return false
endfunction

private function init takes nothing returns nothing
    set DUMMY = CreateUnit(Player(15), DUMMY_ID, 0,0,0)
    call UnitAddAbility(DUMMY, DUMMY_ID)
    call UnitAddAbility(DUMMY, 'Aloc')  
    call ShowUnit(DUMMY, false)
endfunction

endlibrary

- Ima update the wrappers & configurable positions next update...
1) Shouldn't private constant integer FOUNTAIN_OF_HEALTH = 'nfoh' be a configurable?
2) Is that not supposed to be in there?
1) The user can change 'nfoh' if he wants to so its already a configurable...
2) Yes coz the non-hero AI will guard hero allies...

Wouldn't it make more sense for the AI to choose the closest building instead of the first unit found from a FoG loop?
exitwhen .retunit==first -> exitwhen true
For SetUnitPickItem and SetUnitRetreat , you should have the static ifs outside them.

Ima see and test that...


***
All fixes that Bribe mentioned will be changed in the next update...
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
IsUnitVisible does not work if the map has fogs of war
I don't know how you did it, but it works correctly for me.
JASS:
scope VisibleCheck initializer Init
    globals
        private unit u
    endglobals
    
    private function Test takes nothing returns nothing
        if IsUnitVisible(u, Player(0)) then
            call BJDebugMsg("Blue's peasant is visible")
        else
            call BJDebugMsg("Blue's peasant is not visible")
        endif
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        call TriggerRegisterPlayerEvent(trig, Player(0), EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(trig, function Test)
        
        set u = CreateUnit(Player(1), 'hpea', 0, 0, 270)
    endfunction
endscope
The user can change 'nfoh' if he wants to so its already a configurable...
I mentioned that because you put it under this:
JASS:
//===NON-CONFIGURABLES: Meaning, do not touch this!
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
In my opinion, it would be more realistic for the AI to consider fog of war when deciding to engage with enemies.

Anyway not IsUnitVisible(whichUnit, Player(15)can be used to detect invisible units.

There are also two scripts that can detect invulnerability. (You should at least take a look at the first link since its code can give you an idea of how you should issue that order your own library.)
 
Level 2
Joined
Jul 18, 2010
Messages
9
Yo, getting Syntax Error :eek:

JASS:
   //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetTimerData takes timer t, integer value returns nothing
        static if(TimerUtils__USE_HASH_TABLE) then
            // new blue
            call SaveInteger(TimerUtils__ht,0,GetHandleId(t), value)

?!
 
Level 3
Joined
Jun 13, 2010
Messages
43
testing this on 11 AI players hero and big map with 5k dooddads and its totally causing massive lag every attack interval. Kinda disappointing. I guess the problem lies in GetClosestWidget script?
 
Level 13
Joined
Mar 19, 2010
Messages
870
testing this on 11 AI players hero and big map with 5k dooddads and its totally causing massive lag every attack interval. Kinda disappointing. I guess the problem lies in GetClosestWidget script?

You found or solved the Problem? I wanne use this script too, but i've more than 5k doodads ^^

Reg.
 
Level 5
Joined
Aug 29, 2014
Messages
105
Can anyone make it Fully GUI???

Because the JASS will Kill my Brain Joke :)
I don't know how to import it if the Trigger is JASS
when i'm Importing using your JASS Trigger there is a Error and i don't know how to fixed it....

Give me GUI please!!
 
Can anyone make it Fully GUI???

Because the JASS will Kill my Brain Joke :)
I don't know how to import it if the Trigger is JASS
when i'm Importing using your JASS Trigger there is a Error and i don't know how to fixed it....

Give me GUI please!!
Category: vJASS, GUI / Triggers
vJASS needs the Vexorian/Cohader JassHelper enabled in JNGP. You can't use it with the normal world editor.
 
Top