• Check out the results of the Techtree Contest #19!
  • Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

AttackOrder

This bundle is marked as pending. It has not been reviewed by a staff member yet.
  • Like
Reactions: O_Vazio
Attack like in Dota 2.


You click to attack the ground, and the hero attacks the 8 nearest targets from that point. The orders are queued as if you were holding Shift.


JASS:
function Trig_Smart_Attack_Conditions takes nothing returns boolean
    return GetIssuedOrderId() == 851983
endfunction

//===========================================================================
function Trig_Smart_Attack_Actions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local player owner = GetOwningPlayer(caster)

    local real targetX = GetOrderPointX()
    local real targetY = GetOrderPointY()

    local group g = CreateGroup()
    local unit u

    local unit array targets
    local real array dists

    local integer count = 0
    local integer i = 0
    local integer j = 0

    local real dx
    local real dy
    local real dist

    call GroupEnumUnitsInRange(g, targetX, targetY, 650.0, null)

    loop
        set u = FirstOfGroup(g)
        exitwhen u == null

        call GroupRemoveUnit(g, u)

        if IsUnitEnemy(u, owner) and GetWidgetLife(u) > 0.405 and IsUnitVisible(u, owner) and not BlzIsUnitInvulnerable(u) then

            set dx = GetUnitX(u) - targetX
            set dy = GetUnitY(u) - targetY

            set dist = dx * dx + dy * dy

            set i = 0
            loop
                exitwhen i >= count or dist < dists[i]
                set i = i + 1
            endloop

            if i < 8 then
                set j = 7

                loop
                    exitwhen j <= i
                    set targets[j] = targets[j - 1]
                    set dists[j] = dists[j - 1]
                    set j = j - 1
                endloop

                set targets[i] = u
                set dists[i] = dist

                if count < 8 then
                    set count = count + 1
                endif
            endif
        endif
    endloop

    if count > 0 then
        call IssueTargetOrderById(caster, 851971, targets[count - 1])
        set i = count - 2
        loop
            exitwhen i < 0
            call BlzQueueTargetOrderById(caster, 851971, targets[i])
            set i = i - 1
        endloop
    endif

    call DestroyGroup(g)

    set g = null
    set u = null
    set caster = null
    set owner = null

    set i = 0
    loop
        exitwhen i >= 8
        set targets[i] = null
        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_Smart_Attack takes nothing returns nothing
    local trigger t = CreateTrigger()

    call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)

    call TriggerAddCondition(t, Condition(function Trig_Smart_Attack_Conditions))
    call TriggerAddAction(t, function Trig_Smart_Attack_Actions)

    set t = null
endfunction
Contents

AttackOrder (Map)

This is an interesting system. What I'd like to say is, in the same way you added a filter to pick targets units, it's important to add a filter to pick only units can attack (caster).
 
This is an interesting system. What I'd like to say is, in the same way you added a filter to pick targets units, it's important to add a filter to pick only units can attack (caster).
That’s up to the user. Usually, the whole point of such a system is to attack whatever is near the issued command point, whether it’s a peasant or a building, so personally I don’t see much reason to add that filter. At that point, it would just be easier to press Patrol or S.

If I misunderstood the comment, and they were referring to YOUR units, then units that cannot attack won’t even have the attack command button anyway. Filtering them again doesn’t really make sense. In any case, everyone applies their own filters. These are basic fundamentals; the important thing is the concept.
 
Last edited:
That’s up to the user. Usually, the whole point of such a system is to attack whatever is near the issued command point, whether it’s a peasant or a building, so personally I don’t see much reason to add that filter. At that point, it would just be easier to press Patrol or S.

If I misunderstood the comment, and they were referring to YOUR units, then units that cannot attack won’t even have the attack command button anyway. Filtering them again doesn’t really make sense. In any case, everyone applies their own filters. These are basic fundamentals; the important thing is the concept.

Filtering the attacking unit is important for two reasons:

1) The attacking unit will attempt to attack targets if the player's unit selection includes a unit with an attack command card.

2) The attacking unit will obey the attack order if it is issued via trigger, even if it cannot actually attack.

Not all map creators remember these details, so it would be very useful to make this information available in your system as a reminder.

After testing, I also realized that filtering invulnerable target units isn't a good idea if the invulnerability is temporary (divine shield, for example) because the attacking unit will attack the next target in queue and try to attack the invulnerable unit as soon as the invulnerability ends. In that case, it would be better to remove it from the system and let the player decide, as you said before.
 
Filtering the attacking unit is important for two reasons:

1) The attacking unit will attempt to attack targets if the player's unit selection includes a unit with an attack command card.

2) The attacking unit will obey the attack order if it is issued via trigger, even if it cannot actually attack.

Not all map creators remember these details, so it would be very useful to make this information available in your system as a reminder.

After testing, I also realized that filtering invulnerable target units isn't a good idea if the invulnerability is temporary (divine shield, for example) because the attacking unit will attack the next target in queue and try to attack the invulnerable unit as soon as the invulnerability ends. In that case, it would be better to remove it from the system and let the player decide, as you said before.
I have to say that you’re mistaken, because the only event being registered is the attack order (851983) issued on a point (EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER). So you can spam attack orders on units as much as you want, or let your units attack whoever they please — there can’t possibly be any loop or unnecessary trigger executions. If the units don’t even have an attack, then it’s completely impossible in the first place. Don’t overcomplicate things.
 
I have to say that you’re mistaken, because the only event being registered is the attack order (851983) issued on a point (EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER). So you can spam attack orders on units as much as you want, or let your units attack whoever they please — there can’t possibly be any loop or unnecessary trigger executions. If the units don’t even have an attack, then it’s completely impossible in the first place. Don’t overcomplicate things.

*The Paladin hero has the ability Cargo Hold (Orc Burrow)

This is the behavior of the units without filtering the attacking unit:



This is the behavior of units with filtering the attacking unit:



The code with the filter would be something like this:

JASS:
function IsUnitCanAttack takes unit u returns boolean
    local integer abil_cargo = 'Abun'
    local integer abil_cargo_level = GetUnitAbilityLevel(u, abil_cargo)
    local integer buff_banish = 'BHbn'
    local integer buff_banish_level = GetUnitAbilityLevel(u, buff_banish)
        if  abil_cargo_level == 0 and buff_banish_level == 0 and (IsUnitType(u, UNIT_TYPE_ATTACKS_GROUND) == true or IsUnitType(u, UNIT_TYPE_ATTACKS_FLYING) == true) then
    return true
    endif
    return false
endfunction

function Trig_Smart_Attack_Conditions takes nothing returns boolean
    return GetIssuedOrderId() == 851983 and IsUnitCanAttack(GetTriggerUnit()) == true
endfunction

//===========================================================================
function Trig_Smart_Attack_Actions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local player owner = GetOwningPlayer(caster)

    local real targetX = GetOrderPointX()
    local real targetY = GetOrderPointY()

    local group g = CreateGroup()
    local unit u

    local unit array targets
    local real array dists

    local integer count = 0
    local integer i = 0
    local integer j = 0

    local real dx
    local real dy
    local real dist

    call GroupEnumUnitsInRange(g, targetX, targetY, 650.0, null)

    loop
        set u = FirstOfGroup(g)
        exitwhen u == null

        call GroupRemoveUnit(g, u)

        if IsUnitEnemy(u, owner) and GetWidgetLife(u) > 0 and IsUnitVisible(u, owner) then

            set dx = GetUnitX(u) - targetX
            set dy = GetUnitY(u) - targetY

            set dist = dx * dx + dy * dy

            set i = 0
            loop
                exitwhen i >= count or dist < dists[i]
                set i = i + 1
            endloop

            if i < 8 then
                set j = 7

                loop
                    exitwhen j <= i
                    set targets[j] = targets[j - 1]
                    set dists[j] = dists[j - 1]
                    set j = j - 1
                endloop

                set targets[i] = u
                set dists[i] = dist

                if count < 8 then
                    set count = count + 1
                endif
            endif
        endif
    endloop

    if count > 0 then
        call IssueTargetOrderById(caster, 851971, targets[count - 1])
        set i = count - 2
        loop
            exitwhen i < 0
            call BlzQueueTargetOrderById(caster, 851971, targets[i])
            set i = i - 1
        endloop
    endif

    call DestroyGroup(g)

    set g = null
    set u = null
    set caster = null
    set owner = null

    set i = 0
    loop
        exitwhen i >= 8
        set targets[i] = null
        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_Smart_Attack takes nothing returns nothing
    local trigger t = CreateTrigger()

    call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)

    call TriggerAddCondition(t, Condition(function Trig_Smart_Attack_Conditions))
    call TriggerAddAction(t, function Trig_Smart_Attack_Actions)

    set t = null
endfunction
 
Last edited:
This is oddly specific, I wouldn't consider it a system but some kind of AI subroutine.
I couldn't find a more appropriate section to add this to...

*The Paladin hero has the ability Cargo Hold (Orc Burrow)

This is the behavior of the units without filtering the attacking unit:

View attachment 590193

This is the behavior of units with filtering the attacking unit:

View attachment 590194

The code with the filter would be something like this:

JASS:
function IsUnitCanAttack takes unit u returns boolean
    local integer abil_cargo = 'Abun'
    local integer abil_cargo_level = GetUnitAbilityLevel(u, abil_cargo)
    local integer buff_banish = 'BHbn'
    local integer buff_banish_level = GetUnitAbilityLevel(u, buff_banish)
        if  abil_cargo_level == 0 and buff_banish_level == 0 and (IsUnitType(u, UNIT_TYPE_ATTACKS_GROUND) == true or IsUnitType(u, UNIT_TYPE_ATTACKS_FLYING) == true) then
    return true
    endif
    return false
endfunction

function Trig_Smart_Attack_Conditions takes nothing returns boolean
    return GetIssuedOrderId() == 851983 and IsUnitCanAttack(GetTriggerUnit()) == true
endfunction

//===========================================================================
function Trig_Smart_Attack_Actions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local player owner = GetOwningPlayer(caster)

    local real targetX = GetOrderPointX()
    local real targetY = GetOrderPointY()

    local group g = CreateGroup()
    local unit u

    local unit array targets
    local real array dists

    local integer count = 0
    local integer i = 0
    local integer j = 0

    local real dx
    local real dy
    local real dist

    call GroupEnumUnitsInRange(g, targetX, targetY, 650.0, null)

    loop
        set u = FirstOfGroup(g)
        exitwhen u == null

        call GroupRemoveUnit(g, u)

        if IsUnitEnemy(u, owner) and GetWidgetLife(u) > 0 and IsUnitVisible(u, owner) then

            set dx = GetUnitX(u) - targetX
            set dy = GetUnitY(u) - targetY

            set dist = dx * dx + dy * dy

            set i = 0
            loop
                exitwhen i >= count or dist < dists[i]
                set i = i + 1
            endloop

            if i < 8 then
                set j = 7

                loop
                    exitwhen j <= i
                    set targets[j] = targets[j - 1]
                    set dists[j] = dists[j - 1]
                    set j = j - 1
                endloop

                set targets[i] = u
                set dists[i] = dist

                if count < 8 then
                    set count = count + 1
                endif
            endif
        endif
    endloop

    if count > 0 then
        call IssueTargetOrderById(caster, 851971, targets[count - 1])
        set i = count - 2
        loop
            exitwhen i < 0
            call BlzQueueTargetOrderById(caster, 851971, targets[i])
            set i = i - 1
        endloop
    endif

    call DestroyGroup(g)

    set g = null
    set u = null
    set caster = null
    set owner = null

    set i = 0
    loop
        exitwhen i >= 8
        set targets[i] = null
        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_Smart_Attack takes nothing returns nothing
    local trigger t = CreateTrigger()

    call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)

    call TriggerAddCondition(t, Condition(function Trig_Smart_Attack_Conditions))
    call TriggerAddAction(t, function Trig_Smart_Attack_Actions)

    set t = null
endfunction
In this sense, yes. Cases are rare, but for the ideal implementation of the plan, this filter is necessary.
 
Back
Top