System Force Attack

BlackRose

Forum User
Reaction score
239
[SNIPPET] Force Attack

Snippet - Force Attack v1.02

Forces a unit to attack another unit for a set duration.

Requirements:
AIDS
JassNewGenPack

JASS:
//+---------------------------------------------------------------+
//|             F O R C E   A T T A C K   v 1 . 0 2               |
//|                     B Y   B L A C K R O S E                   |
//+---------------------------------------------------------------+

// --------------------------------------------------------
// Requirements:
// - JassNewGen
// - AIDS           Jesus4Lyf
// - KeyTimers2     Jesus4Lyf
// --------------------------------------------------------

// DESCRIPTION:
// A simple snippet that forces a unit to attack another unit for a set duration.

// HOW TO IMPORT:
// Copy this trigger and AIDS into your map and done! Make sure you have NewGen.

// FUNCTION
// call ForceAttack( attackingUnit, targetedUnit, duration, effectpath, attachpath )

//+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
//| CHANGELOG                                       |
//+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
//| v1.02:                                          |
//| - Using hashtable to pass data then KT2         |
//| - NO DYNAMIC TRIGGERS!                          |
//+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
//| v1.01:                                          |
//| - Fixed those problems, HOORAY!                 |
//| - Prettier document?                            |
//+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
//| v1.00:                                          |
//| - Initial release.                              |
//+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+

library ForceAttack initializer OnInit requires AIDS//, KT 

    globals
        private group GROUP = CreateGroup()
        private trigger TRIGGER = CreateTrigger()
        private hashtable HASHTABLE = InitHashtable()
        private timer TIMER = CreateTimer()
        
        private unit ATTACKER
        private unit TARGET
    endglobals

    private struct FAData extends array
    //! runtextmacro AIDS()
        unit attacker
        unit target
        real duration
        trigger trig
        effect SFX
        
        player ap
        player tp
        
        timer Timerz
 
        // Locust no?
        private static method AIDS_filter takes unit u returns boolean
            return GetUnitAbilityLevel(u,'Aloc') ==0 
        endmethod
        
        static method MoveTarget takes nothing returns nothing
            call IssuePointOrder( ATTACKER, "move", GetUnitX( TARGET ), GetUnitY( TARGET ) )
            call PauseTimer( TIMER )
        endmethod
                
        static method AttackTarget takes nothing returns nothing
            local thistype d
            local boolean b = false
            
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // 1. If the event triggering this was ATTACKED.
            // 2. Then we check if the attacked unit was not current target, and 
            // attacker is in the attacking GROUP.
            // 3. We disable this trigger, order a commmand, if target is not seen
            // then we move it. Otherwise attack, if attack set some boolean to true to prevent
            // below from ordering another attack.
            //
            if GetTriggerEventId() == EVENT_PLAYER_UNIT_ATTACKED then
                set d = thistype[GetAttacker()]
                if GetTriggerUnit() != d.target and IsUnitInGroup( d.attacker, GROUP ) then
                    call DisableTrigger( TRIGGER )
                    //call IssueTargetOrder( d.attacker, "attack", d.target )
                    if IsUnitVisible( d.target, d.ap ) == false then
                        debug call BJDebugMsg( "Attacked - Cannot see, so unit is now moving to target location" )
                        set ATTACKER = d.attacker
                        set TARGET = d.target
                        call TimerStart( TIMER, 0.00, false, function FAData.MoveTarget )
                        //call IssuePointOrder( d.attacker, "move", GetUnitX( d.target ), GetUnitY( d.target ) )
                    else
                        debug call BJDebugMsg( "Attacked - ATTACK COMMAND." )
                        call IssueTargetOrder( d.attacker, "attack", d.target )
                        set b = true
                    endif
                    call EnableTrigger( TRIGGER )
                endif
            endif
            
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // 1. If the event was either of a command.
            // 2. We check if the ordered unit was in the group.
            // 3. If so, disable this trigger. Do the same as above.
            //
            if ( GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER ) or /*
            */ ( GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER ) or /*
            */ ( GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER ) then
                set d = thistype[GetOrderedUnit()]
                    if IsUnitInGroup( d.attacker, GROUP ) then
                        call DisableTrigger( TRIGGER )
                        //call IssueTargetOrder( d.attacker, "attack", d.target )
                        if IsUnitVisible( d.target, d.ap ) == false then
                            debug call BJDebugMsg( "Order - Cannot see, so unit is now moving to target location" )
                            set ATTACKER = d.attacker
                            set TARGET = d.target
                            call TimerStart( TIMER, 0.00, false, function FAData.MoveTarget )
                            //call IssuePointOrder( d.attacker, "move", GetUnitX( d.target ), GetUnitY( d.target ) )
                        elseif b == false then
                            debug call BJDebugMsg( "Order - Order attack command." )
                            call IssueTargetOrder( d.attacker, "attack", d.target )
                        endif
                    call EnableTrigger( TRIGGER )
                endif
            endif
        endmethod
                
        static method EndAttack takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local integer id = GetHandleId(t)
            local unit attacker = LoadUnitHandle( HASHTABLE, id, 2 )
            
            debug call BJDebugMsg( "Attack ended" )

            call DestroyEffect( LoadEffectHandle( HASHTABLE, id, 1 ) )
            call GroupRemoveUnit( GROUP, attacker )
            call IssueImmediateOrder( attacker, "stop" )
            
            call FlushChildHashtable( HASHTABLE, id )
            call PauseTimer( t )
            call DestroyTimer( t )
            set t = null
            set attacker = null
        endmethod
    endstruct
    
    function ForceAttack takes unit attacker, unit target, real duration, string path, string where returns nothing
        local FAData d = FAData[attacker]
        local integer id
        
        if IsUnitInGroup( attacker, GROUP ) then
            debug call BJDebugMsg( "Unit is already being forced to attack a target." )
            return
        endif
        set d.attacker = attacker
        set d.target = target
        set d.duration = duration
        set d.Timerz = CreateTimer()
        call GroupAddUnit( GROUP, d.attacker )
        set d.ap = GetOwningPlayer( d.attacker )
        set d.tp = GetOwningPlayer( d.target )

        set d.SFX = AddSpecialEffectTarget( path, d.attacker, where )

        if IsUnitVisible( d.target, d.ap ) then
            call IssueTargetOrder( d.attacker, "attack", d.target )
        else
            call IssuePointOrder( d.attacker, "move", GetUnitX( d.target ), GetUnitY( d.target ) )
        endif
        set id = GetHandleId( d.Timerz )

        call SaveEffectHandle( HASHTABLE, id, 1, d.SFX )
        call SaveUnitHandle( HASHTABLE, id, 2, d.attacker )
        call TimerStart( d.Timerz, d.duration, false, function FAData.EndAttack )
    endfunction
    
    private function OnInit takes nothing returns nothing
        call TriggerRegisterAnyUnitEventBJ( TRIGGER, EVENT_PLAYER_UNIT_ATTACKED )
        call TriggerRegisterAnyUnitEventBJ( TRIGGER, EVENT_PLAYER_UNIT_ISSUED_ORDER )
        call TriggerRegisterAnyUnitEventBJ( TRIGGER, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
        call TriggerRegisterAnyUnitEventBJ( TRIGGER, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
        call TriggerAddAction( TRIGGER, function FAData.AttackTarget )
    endfunction
    
endlibrary
 

Narks

Vastly intelligent whale-like being from the stars
Reaction score
90
I don't think this would be a system - maybe a snippet or part of a spell.

Anyway, can't you resolve those problems by ordering the unit to move to the location of the target until it is within sight?
 

BlackRose

Forum User
Reaction score
239
I guess a snippet. Well, I could, but then if you set to like 60 second duration, HAX. You found me!
 

Azlier

Old World Ghost
Reaction score
461
Taunt doesn't force units to attack a target for a certain duration.
 

Kenny

Back for now.
Reaction score
202
Is there a reason why you are using a 0.00 second timer for ordering the attackers to "move" if they cannot see the target?

Also, this looks like it would benefit from using GTrigger. :) Just to clean things up a bit.

Oh and you should be using a trigger condition, not a trigger action for the .attackTarget method.

EDIT:

Whoa! Wait a sec...

Why are you dynamically creating and destroying timers? Seems like you just replaced dynamic triggers with dynamic timers. I don't remember what the previous version did, but I suggest you go back to using KT2, without the hashtables (they don't look necessary).
 

BlackRose

Forum User
Reaction score
239
1. If I don't use the timer, they don't move.... units go haywire.
2. GTrigger confuses me, you have to do GTrigger_WHATEVEREVENT( trigger, "ORDERID" ). Like "Move", for EACH ONE o_O
3. Ok. Will fix.
4. Uh... how else do I do it?
I do not want to use this because:
- dynamic triggers (do i have to explain why?)
- inefficiency through KeyTimers. Plain timers work better for this.
- that AIDS textmacro looks like overkill to me (besides the horrible horrible name; it sounds as if your script was infected). For this application at least.
- What happens to the member overwritten before you abort because the attacker is already in a group?
Which one is better o_O The only thing I like about his comment was the AIDS part :D
 

Kenny

Back for now.
Reaction score
202
1. If I don't use the timer, they don't move.... units go haywire.

Do you have a reason why? Seems a bit weird to me.

2. GTrigger confuses me, you have to do GTrigger_WHATEVEREVENT( trigger, "ORDERID" ). Like "Move", for EACH ONE o_O

It doesn't take long to learn, check my Path of Shadows spell for an example of how to do multiple events.

I do not want to use this because:
- dynamic triggers (do i have to explain why?)
- inefficiency through KeyTimers. Plain timers work better for this.
- that AIDS textmacro looks like overkill to me (besides the horrible horrible name; it sounds as if your script was infected). For this application at least.
- What happens to the member overwritten before you abort because the attacker is already in a group?

Sounds like this guy is from wc3c... Their attitude towards anything outside of the wc3c website is so ridiculous it's laughable.

I agree with him on dynamic triggers (which you fixed).

I disagree with him on his timer efficiency statement, he probably doesn't even know the extent of KT2's abilities. Granted KT2 isn't the most efficient timer system for low frequency periods. However, it is still better than using standard timers + hashtables (in my opinion at least). It also has the added bonus of working on both 1.23 and 1.24 versions of wc3.

(besides the horrible horrible name; it sounds as if your script was infected)

That statement is more childish than the name of the system itself. Ignore him. AIDS is useful for many different purposes (even though you use it a very minimalistic fashion).

My opinion: Switch back to KT2 (And even TimerUtils for your wc3c version). Dynamic timers are just bad.
 

BlackRose

Forum User
Reaction score
239
Do you have a reason why? Seems a bit weird to me.
You can try ordering without timer. Before when I did, they didn't move.

It doesn't take long to learn, check my Path of Shadows spell for an example of how to do multiple events.
I can't seem to get it working with GTrigger. How do you check the eventid of the trigger when it is fired, GetTriggerEventId() doesn't work. Also, are the functions that refer to the ordered unit still the same?
Like:
JASS:
        call GT_RegisterPointOrderEvent(TRIGGER,ORDER_ATTACK)
        call GT_RegisterTargetOrderEvent(TRIGGER,ORDER_ATTACK)
        
        call GT_RegisterNoTargetOrderEvent(TRIGGER, ORDER_STOP)

Would still be GetOrderedUnit()?

Sounds like this guy is from wc3c... Their attitude towards anything outside of the wc3c website is so ridiculous it's laughable.
He is.

I disagree with him on his timer efficiency statement, he probably doesn't even know the extent of KT2's abilities. Granted KT2 isn't the most efficient timer system for low frequency periods. However, it is still better than using standard timers + hashtables (in my opinion at least). It also has the added bonus of working on both 1.23 and 1.24 versions of wc3.
I guess it's back to KT2.

That statement is more childish than the name of the system itself. Ignore him. AIDS is useful for many different purposes (even though you use it a very minimalistic fashion).
I like AIDS.

My opinion: Switch back to KT2 (And even TimerUtils for your wc3c version). Dynamic timers are just bad.
I don't even post stuff on wc3c. Only here and on Hiveworkshop.

The most feedback I ever got on this snippet from you.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top