• 🏆 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!

[System] Melee/Ranged Attack Switch v1.1.1.1

JASS:
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* 
 *      Melee/Ranged system v1.1.1.1 by Maker                           *
 *                                                                      *
 *      Melee/ranged switching system allows a unit to switch between   *
 *      ranged and melee attacks.                                       *
 *                                                                      *
 *      Has two modes, automatic and manual.                            *
 *                                                                      *
 *      In auto mode, the unit switches automatically based on          *
 *      distance to target. In manual mode, player can switch           *
 *      the mode with an ability.                                       *
 *                                                                      *
 *      Supports Last Order, the unit doesn't stop when switching.      *
 *                                                                      *
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

library MeleeRanged initializer Init requires UnitIndexer optional LastOrder

    globals
        // Configure unit types at Setup function
        // at the bottom of the library
    
        // Do units change tome melee/ranged automatically
        private constant    boolean     AUTO        = true
        // Ability with which unit can toggle attack
        private constant    integer     TOGGLEABI   = 'A005'
        // Order id of the transform, default = metamorphosis
        private constant    integer     TOGGLEOID   = 852180 
        // Order id of toggle activation, default = magicdefence
        private constant    integer     TOGGLEON    = 852478
        
        
        private             hashtable   HT          = InitHashtable()
        private             trigger     TRG1
        private             trigger     TRG2         = CreateTrigger()
        private             timer       T1
        private             timer       T2
        private             group       G1
        private             group       G2
        private             group       U
        private             group       TEMP        
        
        private             integer     LEAKS       = 0
        private             integer     LEAKS_MAX   = 10
    endglobals
    
    // Disables given ability for players, won't show up in UI
    private function DisableAbil takes integer a returns nothing
        local integer i = 0
        loop
            call SetPlayerAbilityAvailable(Player(i), a, false)
            exitwhen i == 15
            set i = i + 1
        endloop
    endfunction
    
    // Adds a delay to order, allows to akncowledge the
    // change in unit's attack range
    private function Delay3 takes nothing returns nothing
        static if  LIBRARY_LastOrder then
            local unit u
            call DisableTrigger(TRG2)
            loop
                set u = FirstOfGroup(G1)
                exitwhen u == null
                if GetPastOrder(u, 3) != null then
                    call IssuePastOrder(u, 3)
                endif
                call GroupRemoveUnit(G1, u)
            endloop
            call EnableTrigger(TRG2)
        endif
    endfunction
    
    // Adds a delay to order, allows to akncowledge the
    // change in unit's attack range
    private function Delay1 takes nothing returns nothing
        local unit u
        call DisableTrigger(TRG2)
        loop
            set u = FirstOfGroup(G1)
            exitwhen u == null
            call IssueTargetOrderById(u, 851983, LoadUnitHandle(HT, GetHandleId(u), 2))
            call FlushChildHashtable(HT, GetHandleId(u))
            call GroupRemoveUnit(G1, u)
        endloop
        call EnableTrigger(TRG2)
    endfunction
    
    function ToggleMeleeRanged takes unit u, unit target, boolean toRanged returns nothing
        // Load correct ability for the unit type
        local integer a = LoadInteger(HT, GetUnitTypeId(u), 1)
        // Enable ability to be able to use it, then disable it to hide it
        //call GroupRemoveUnit(U, u)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u), a, true)
        call IssueImmediateOrderById(u, TOGGLEOID)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u), a, false)
        //call GroupAddUnit(U, u)
        // Save whether unit is melee or ranged
        static if AUTO then
            // Makes the unit attack the target
            // Use delay so the new range is used
            call SaveUnitHandle(HT, GetHandleId(u), 2, target)
            call GroupAddUnit(G1, u)
            call TimerStart(T1, 0.00, false, function Delay1)
        elseif LIBRARY_LastOrder then
            // Makes the unit stop when it transforms
            call GroupAddUnit(G1, u)
            call TimerStart(T1, 0.00, false, function Delay3)
        endif
    endfunction
    
    // Detects which event was triggered and whether the unit
    // should switch to melee/ranged or not
    private function Actions takes nothing returns boolean
        local unit u1
        local unit u2
        local real r1
        local real r2
        local integer uid
        local integer id = GetHandleId(GetTriggerEventId())
        if id == 18 then // A Unit is Attacked
            set u1 = GetAttacker()
            set uid = GetUnitTypeId(u1)
            if HaveSavedInteger(HT, uid, 1) then
                set u2 = GetTriggerUnit()
            else
                set u1 = null
                return false
            endif
        else
            set u1 = GetTriggerUnit()
            set uid = GetUnitTypeId(u1)
            if HaveSavedInteger(HT, uid, 1) then
                if id == 60 then // A Unit Acquires a Target
                    set u2 = GetEventTargetUnit()
                else // A Unit Is Issued an Order Targeting an Object
                    set u2 = GetOrderTargetUnit()
                    if GetIssuedOrderId() == 851971  then // Order is smart
                        if not IsUnitEnemy(u2, GetOwningPlayer(u1)) or u2 == null then
                            set u1 = null
                            set u2 = null
                            return false
                        endif
                    elseif GetIssuedOrderId() != 851983 then // Order is not attack
                        set u1 = null
                        set u2 = null
                        return false // Spells won't trigger the system
                    endif
                endif
            else
                set u1 = null
                return false
            endif
        endif
        
        set r1 = GetUnitX(u1)-GetUnitX(u2)
        set r2 = GetUnitY(u1)-GetUnitY(u2)
        
        // Checks melee distance
        if r1*r1+r2*r2 > LoadReal(HT, GetUnitTypeId(u1), 0) then
            if LoadInteger(HT, uid, 2) == 0 then
                call ToggleMeleeRanged(u1, u2, true)
            endif
        elseif LoadInteger(HT, uid, 2) == 1 then
            call ToggleMeleeRanged(u1, u2, false)
        endif
        
        set u1 = null
        set u2 = null
        return false
    endfunction
    
    // Recreates the trigger
    private function Recreate takes nothing returns nothing
        static if AUTO then
            local unit u
            call DestroyTrigger(TRG1)
            set TRG1 = CreateTrigger()
            call TriggerAddCondition(TRG1, Condition(function Actions))
            loop
                set u = FirstOfGroup(U)
                exitwhen u == null
                call GroupAddUnit(TEMP, u)
                call TriggerRegisterUnitEvent(TRG1, u, EVENT_UNIT_ACQUIRED_TARGET)
                call GroupRemoveUnit(U, u)
            endloop
            loop
                set u = FirstOfGroup(TEMP)
                exitwhen u == null
                call GroupAddUnit(U, u)
                call GroupRemoveUnit(TEMP, u)
            endloop
        endif
    endfunction

    // Adds delay to transform order to prevent the toggle
    // order firing twice, due to the instant transform
    private function Delay2 takes nothing returns nothing
        static if not AUTO then
            local unit u
            loop
                set u = FirstOfGroup(G2)
                exitwhen u == null
                call ToggleMeleeRanged(u, null, not LoadBoolean(HT, GetHandleId(u), 0))
                call GroupRemoveUnit(G2, u)
            endloop
        endif
    endfunction
    
    // Detects the toggle order
    private function Toggle takes nothing returns boolean
        static if not AUTO then
            if GetIssuedOrderId() == TOGGLEON then
                call GroupAddUnit(G2, GetTriggerUnit())
                call TimerStart(T2, 0.0, false, function Delay2)
            endif
        endif
        return false
    endfunction
    
    // Initializes a spesific unit for the system
    private function AddUnit takes unit u returns nothing
        local integer ut = GetUnitTypeId(u)
        local integer ab = LoadInteger(HT, ut, 1)
        
        call UnitAddAbility(u, ab)
        call UnitMakeAbilityPermanent(u, true, ab)
        if LoadInteger(HT, ut, 2) == 1 then // Is it the ranged version
            call SetPlayerAbilityAvailable(GetOwningPlayer(u), ab, true)
            call IssueImmediateOrderById(u, TOGGLEOID)
            call SetPlayerAbilityAvailable(GetOwningPlayer(u), ab, false)
        endif
        
        static if AUTO then
            call GroupAddUnit(U, u)
            call TriggerRegisterUnitEvent(TRG1, u, EVENT_UNIT_ACQUIRED_TARGET)
        else
            call UnitAddAbility(u, TOGGLEABI)
            call UnitMakeAbilityPermanent(u, true, TOGGLEABI)
        endif
    endfunction
    
    // Detects possible even leaks
    private function Die takes nothing returns boolean
        static if AUTO then
            if HaveSavedInteger(HT, GetUnitTypeId(GetIndexedUnit()), 1) then
                call GroupRemoveUnit(U, GetIndexedUnit())
                call GroupRemoveUnit(G1, GetIndexedUnit())
                if LEAKS == LEAKS_MAX then
                    set LEAKS = 0
                    call Recreate()
                else
                    set LEAKS = LEAKS + 1
                endif
            endif
        else
            call GroupRemoveUnit(G2, GetIndexedUnit())
            static if LIBRARY_LastOrder then
                call GroupRemoveUnit(G1, GetIndexedUnit())
            endif
        endif
        return false
    endfunction
    
    // Is unit type valid
    private function UnitFilt takes nothing returns boolean
        if HaveSavedInteger(HT, GetUnitTypeId(GetFilterUnit()), 1) then
            call AddUnit(GetFilterUnit())
        endif
        return false
    endfunction
    
    // Initializes unit types
    private function AddUT takes integer i1, integer i2, integer a, real r returns nothing
        call SaveReal(HT, i1, 0, r*r)
        call SaveReal(HT, i2, 0, r*r)
        call SaveInteger(HT, i1 ,1, a)
        call SaveInteger(HT, i2 ,1, a)
        call SaveInteger(HT, i1, 2, 0)  // Is melee type
        call SaveInteger(HT, i2, 2, 1)  // Is ranged type
        call DisableAbil(a)             
    endfunction
    
     private function Setup takes nothing returns nothing
        // Parameters are:
        // (Melee unit type, ranged unit type, transform ability, melee range)
        call AddUT('Hpal', 'H000', 'A000', 200)
        call AddUT('H001', 'Hamg', 'A001', 220)
        call AddUT('e000', 'earc', 'A002', 180)
        call AddUT('n000', 'n001', 'A003', 200)
        call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, function UnitFilt)
    endfunction
    
    private function Init takes nothing returns nothing
        local region r = CreateRegion()
        
        call RegionAddRect(r, bj_mapInitialPlayableArea)
        call TriggerRegisterEnterRegion(CreateTrigger(), r, function UnitFilt)
        call RegisterUnitIndexEvent(Condition(function Die), UnitIndexer.DEINDEX)
        
        static if AUTO then
            set T1 = CreateTimer()
            set G1 = CreateGroup()
            set U = CreateGroup()
            set TEMP = CreateGroup()
            set TRG1 = CreateTrigger()
        
            call TriggerAddCondition(TRG1, function Actions)
            call TriggerAddCondition(TRG2, function Actions)
            
            call TriggerRegisterAnyUnitEventBJ(TRG2, EVENT_PLAYER_UNIT_ATTACKED)
            call TriggerRegisterAnyUnitEventBJ(TRG2, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER)
        else
            static if LIBRARY_LastOrder then
                set T1 = CreateTimer()
                set G1 = CreateGroup()
            endif
            set T2 = CreateTimer()
            set G2 = CreateGroup()
            call TriggerAddCondition(TRG2, function Toggle)
            call TriggerRegisterAnyUnitEventBJ(TRG2, EVENT_PLAYER_UNIT_ISSUED_ORDER)
        endif
        call Setup()
        set r = null
    endfunction

endlibrary


v1.0.0.0 Uploaded 21.10.2011
v1.0.0.1 Uploaded 21.10.2011
-Added a static if to prevent syntax error if Last Order is missing
v1.0.0.2 Uploaded 21.10.2011
-Removed a boolean
-Added optional library requirement
v1.0.0.3 Uploaded 21.10.2011
-Removed a possible unit variable leak
v1.0.0.4 Uploaded 21.10.2011
-Optimized code
-Disable/enable action detection trigger to avoid useless action checks
v1.0.0.5 Uploaded 21.10.2011
-Got rid of SquareRoot in the distance check
-Added local reals for distance calculation
v1.0.1.6 Uploaded 24.10.2011
-Added trigger recycling, events for removed units are cleared
-Uses u2 == null instead of GetManipulatedItem != null
-Static if now covers all actions in Delay3 function
v1.1.0.0 Uploaded 02.11.2011
-Combined Enter and UnitFilt functions
-Removed two integer globals
-Uses unit indexer to correctly handle revived units
-Removed unneeded variables from setup function
-Small static if change
-Two more static ifs, in Delay2 and Toggle functions
-Added a trigger to which unit acquires target events are added
-Uses filter function instead of condition for units that enter the map
v1.1.1.0 Uploaded 10.11.2011
-Changed the order of u = FirstOfGroup and exitwhen u = null
-When units die, they are removed from groups that cause them to perform actions
-Instead of creating a trigger, registers dying with RegisterUnitIndexEvent
v1.1.1.1 Uploaded 10.11.2011
-Removed leftovers from previous versions, i and j variables from Recreate function


Last Order is an optional library by Rising Dusk
Unit indexer is a requires library by Nestharus
Both are included in the map

Keywords:
melee, ranged, attack, switch, toggle
Contents

Map (Map)

Reviews
10th Nov 2011 Bribe: The system is cool, useful and Highly Recommended. Thanks for making this resource.

Moderator

M

Moderator

10th Nov 2011
Bribe: The system is cool, useful and Highly Recommended.

Thanks for making this resource.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Are you using JassHelper 0.A.2.B? You can comment out the whole "Delay3" function if you have that version.

It also might not compile properly even with the LastOrder library found since you have no "requires optional LastOrder" after the "library" keyword.

There is also the LIBRARY_LastOrder constant provided if the library is found, which you can use instead of the LAST_ORDER constant you currently have.

But I think it's better to just have LastOrder as a total requirement. The benefit it gives to this library is strong.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
No love for RegisterPlayerUnitEvent? ;/
JASS:
        if SquareRoot((GetUnitX(u1)-GetUnitX(u2))*(GetUnitX(u1)-GetUnitX(u2))+(GetUnitY(u1)-GetUnitY(u2))*(GetUnitY(u1)-GetUnitY(u2))) > LoadReal(HT, GetUnitTypeId(u1), 0) then
            if not LoadBoolean(HT, GetHandleId(u1), 0) then
                call ToggleMeleeRanged(u1, u2, true)
            endif
        elseif LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, false)
        endif

->>
JASS:
        if SquareRoot((GetUnitX(u1)-GetUnitX(u2))*(GetUnitX(u1)-GetUnitX(u2))+(GetUnitY(u1)-GetUnitY(u2))*(GetUnitY(u1)-GetUnitY(u2))) > LoadReal(HT, GetUnitTypeId(u1), 0) and not LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, true)
        elseif LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, false)
        endif
>>>>>>>>>>>>>>>>>>>>>
JASS:
        local unit u1 = GetAttacker()
        local unit u2
        if u1 == null then
            set u1 = GetTriggerUnit()
            if HaveSavedInteger(HT, GetUnitTypeId(u1), 1) then  // Unit type is initialized for the system
                if GetHandleId(GetTriggerEventId()) == 40 then  // Target unit order
                    set u2 = GetOrderTargetUnit()
                    if GetIssuedOrderId() == 851971  then   // Order is smart
                        if not IsUnitEnemy(u2, GetOwningPlayer(u1)) or /*
                        */ GetOrderTargetItem() != null then
                            set u1 = null
                            return
                        endif
                    elseif GetIssuedOrderId() != 851983 then // Order is not attack
                        set u1 = null
                        return  // Spells won't trigger the system
                    endif
                else
                    set u2 = GetEventTargetUnit()
                endif
            else
                set u1 = null
                return
            endif
        // Event was unit attacked
        elseif HaveSavedInteger(HT, GetUnitTypeId(u1), 1) then // Unit type is initialized
            set u2 = GetTriggerUnit()
        else
            set u1 = null
            return
        endif
->>
JASS:
        local unit u1 = GetAttacker()
        local unit u2
        local integer i = GetHandleId(GetTriggerEventId()
        if u1 == null then
            set u1 = GetTriggerUnit()
            if HaveSavedInteger(HT, GetUnitTypeId(u1), 1) and i == 40 then
                set i = GetIssuedOrderId()
                set u2 = GetOrderTargetUnit()
                if (i == 851971 and (not IsUnitEnemy(u2, GetOwningPlayer(u1)) or GetOrderTargetItem() != null)) or i != 851983 then
                    set u1 = null
                    return
                endif
            elseif i != 40 then
                set u2 = GetEventTargetUnit()
            else
                set u1 = null
                return
            endif
        elseif HaveSavedInteger(HT, GetUnitTypeId(u1), 1) then // Unit type is initialized
            set u2 = GetTriggerUnit()
        else
            set u1 = null
            return
        endif

^I'll review that part again in sec; since your 'if' spam can be cut a lot. You should think more about: since I do 'set u1 = null & return' in many cases, why shouldn't that be a main statement in that function?
 
Last edited:
Level 37
Joined
Mar 6, 2006
Messages
9,240

At the moment I have no plans to add that.


JASS:
        if SquareRoot((GetUnitX(u1)-GetUnitX(u2))*(GetUnitX(u1)-GetUnitX(u2))+(GetUnitY(u1)-GetUnitY(u2))*(GetUnitY(u1)-GetUnitY(u2))) > LoadReal(HT, GetUnitTypeId(u1), 0) then
            if not LoadBoolean(HT, GetHandleId(u1), 0) then
                call ToggleMeleeRanged(u1, u2, true)
            endif
        elseif LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, false)
        endif

->>
JASS:
        if SquareRoot((GetUnitX(u1)-GetUnitX(u2))*(GetUnitX(u1)-GetUnitX(u2))+(GetUnitY(u1)-GetUnitY(u2))*(GetUnitY(u1)-GetUnitY(u2))) > LoadReal(HT, GetUnitTypeId(u1), 0) and not LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, true)
        elseif LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, false)
        endif

My way: 2 comparisons for change to ranged, 2 comparisons to change to melee
Your way: 2 comparisons to change to ranged, 3 comparisons to change to melee

Your second suggestion will fail. Please test before suggesting changes. But I can try to improve the logic if possible.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
My way: 2 comparisons for change to ranged, 2 comparisons to change to melee
Your way: 2 comparisons to change to ranged, 3 comparisons to change to melee
What the hek?
Nested 'if' is pointless, since it does nothing if not LoadBoolean(HT, GetHandleId(u1), 0) returns false.

Omg I hate seeing 'your suggestion will fail' I had to go a walk with my dog and I've said 'I'll review that in second again'. But surely my statement is true:

your 'if' spam can and should be prevented.
My script will be fixed in sec.

EDIT: Fixed. There are two options: either the one, I've posted above, or check all the conditions that lead to: 'return' statement; and in 'else' implement 'u2 =' selection. I have not posted the 2nd case, because calling so many statements in 'single/double if' may fuck up readability and may be slower than 1st method.

EDIT2: I see you have changed Action function in meanwhile.. Anyways, this one can be improved too:
JASS:
        local unit u1
        local unit u2
        local integer id = GetHandleId(GetTriggerEventId())
        
        if id == 18 then // A Unit is Attacked
            set u1 = GetAttacker()
            if HaveSavedInteger(HT, GetUnitTypeId(u1), 1) then
                set u2 = GetTriggerUnit()
            else
                set u1 = null
                return
            endif
        elseif id == 60 then // A Unit Acquires a Target
            set u1 = GetTriggerUnit()
            if HaveSavedInteger(HT, GetUnitTypeId(u1), 1) then
                set u2 = GetEventTargetUnit()
            else
                set u1 = null
                return
            endif
        else // A Unit Is Issued an Order Targeting an Object
            set u1 = GetTriggerUnit()
            if HaveSavedInteger(HT, GetUnitTypeId(u1), 1) then
                set u2 = GetOrderTargetUnit()
                if GetIssuedOrderId() == 851971  then   // Order is smart
                    if not IsUnitEnemy(u2, GetOwningPlayer(u1)) or GetOrderTargetItem() != null then
                        set u1 = null
                        set u2 = null
                        return
                    endif
                elseif GetIssuedOrderId() != 851983 then // Order is not attack
                    set u1 = null
                    set u2 = null
                    return  // Spells won't trigger the system
                endif
            else
                set u1 = null
                return
            endif
        endif
JASS:
        local unit u1
        local unit u2
        local integer id = GetHandleId(GetTriggerEventId())

        if id == 18 then // A Unit is Attacked
            set u1 = GetAttacker()
            if HaveSavedInteger(HT, GetUnitTypeId(u1), 1) then
                set u2 = GetTriggerUnit()
            else
                set u1 = null
                return
            endif
        else
            set u1 = GetTriggerUnit()
            if HaveSavedInteger(HT, GetUnitTypeId(u1), 1) then
                if id == 60 then
                    set u2 = GetEventTargetUnit()
                else
                    set u2 = GetOrderTargetUnit()
                    set id = GetIssuedOrderId()
                    if (id == 851971 and (not IsUnitEnemy(u2, GetOwningPlayer(u1)) or GetOrderTargetItem() != null)) or id != 851983 then
                        set u1 = null
                        set u2 = null
                        return
                    endif
                endif
            else
                set u1 = null
                return
            endif
        endif
 
Last edited:
Level 37
Joined
Mar 6, 2006
Messages
9,240
JASS:
if SquareRoot((GetUnitX(u1)-GetUnitX(u2))*(GetUnitX(u1)-GetUnitX(u2))+(GetUnitY(u1)-GetUnitY(u2))*(GetUnitY(u1)-GetUnitY(u2))) > LoadReal(HT, GetUnitTypeId(u1), 0) and not LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, true)
        elseif LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, false)
        endif

Translates into:
If in ranged distance and in melee mode
----change to ranged
else if in ranged mode
----change to melee

Spot the fail.

Results the unit to change to melee if it is in ranged distance and in ranged mode


The other suggestion causes syntex error (i instead of id) and won't make the unit change the type when smart is ordered.

I appreciate you trying to help, I really do. I'm very thankful for taking the time to post and taking a look at my spell. But please test before posting. I have wasted much time trying your solutions.

I will update the code soon.

EDIT:Updated to 1.0.0.4
 
JASS:
    private function Delay3 takes nothing returns nothing
        local unit u = FirstOfGroup(G1)
        call DisableTrigger(TRG)
        loop
            static if LIBRARY_LastOrder then
                if GetPastOrder(u, 3) != null then
                    call IssuePastOrder(u, 3)
                endif
                call GroupRemoveUnit(G1, u)
                set u = FirstOfGroup(G1)
                exitwhen u == null
            endif
        endloop
        call EnableTrigger(TRG)
    endfunction

This hits the op limit without LastOrder and crashes the calling thread.

->

JASS:
    private function Delay3 takes nothing returns nothing
        local unit u = FirstOfGroup(G1)
        call DisableTrigger(TRG)
        loop
            static if LIBRARY_LastOrder then
                if GetPastOrder(u, 3) != null then
                    call IssuePastOrder(u, 3)
                endif
                call GroupRemoveUnit(G1, u)
                set u = FirstOfGroup(G1)
                exitwhen u == null
            else
                exitwhen true
            endif
        endloop
        call EnableTrigger(TRG)
    endfunction

Or, better yet, comment out the entire function if LastOrder isn't present.

JASS:
if SquareRoot((GetUnitX(u1)-GetUnitX(u2))*(GetUnitX(u1)-GetUnitX(u2))+(GetUnitY(u1)-GetUnitY(u2))*(GetUnitY(u1)-GetUnitY(u2))) > LoadReal(HT, GetUnitTypeId(u1), 0) then
            if not LoadBoolean(HT, GetHandleId(u1), 0) then
                call ToggleMeleeRanged(u1, u2, true)
            endif
        elseif LoadBoolean(HT, GetHandleId(u1), 0) then
            call ToggleMeleeRanged(u1, u2, false)
        endif

Here, you should be caching the coordinates in local variables for increased performance.

There are lots of other possible optimizations too, but I'm sure Bribe and Spinnaker stated them above :p (lol didn't read xD)
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Maker, I wasn't near WE so I couldn't test solutions in game. I was giving ideas since there was a space for improvements.

Since you've changed your Action function during I was writing one of post it was 100% sure that was useless then.
However, as I can see you've used my idea about removing one of 'if' statement and replaced that with
JASS:
        if id == 60 then
            set u2 = GetEventTargetUnit()
        else
GetIssuedOrderId() could be replaced with 'id' integer; two calls < one call + set local
I'll run some tests tomorrow.
 
Level 10
Joined
Aug 21, 2010
Messages
316
private function DisableAbil takes integer a returns nothing
local integer i = 0
loop
call SetPlayerAbilityAvailable(Player(0), a, false)

should be
"call SetPlayerAbilityAvailable(Player(i), a, false)"
exitwhen i == 15
set i = i + 1
endloop
endfunction
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I don't see any instances of "DestroyTrigger", we could be looking at lots of event leaks as the game progresses.

I recommend using the approach I did in DamageEvent (Nestharus used it in his vJass damage event library). Basically every 15 or so removed units, you destroy the trigger and re-add all the events of the still-existing units.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
JASS:
        local trigger t1 = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local region r = CreateRegion()
        
        call RegionAddRect(r, bj_mapInitialPlayableArea)
        call TriggerAddCondition(t1, function Enter)
        call TriggerRegisterEnterRegion(t1, r, null)
        call TriggerAddCondition(t2, function Die)
        call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DEATH)
        set t1 = null
        set t2 = null
->>
JASS:
        local trigger t = CreateTrigger()
        local region r = CreateRegion()
        
        call RegionAddRect(r, bj_mapInitialPlayableArea)
        call TriggerAddCondition(t, function Enter)
        call TriggerRegisterEnterRegion(t, r, null)
        set t = CreateTrigger()
        call TriggerAddCondition(t, function Die)
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
        set t = null
Marginal issue: 0.0 can be set to just 0.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I almost thought this library could make use of the GroupTargetOrderById function but sadly not :(

Would have been cool though.

You can use "elseif" in static if's so you don't need a seperate "static if" block for that.

The dynamic trigger "TRG" should not have the TriggerRegisterAnyUnitEventBJ events attached to it. Those should be in a seperate trigger.

You must use trigger conditions for dynamic triggers, because trigger actions are not recycled properly when the trigger is destroyed (conditions are).
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Updated to v1.1.0.0
-Combined Enter and UnitFilt functions
-Removed two integer globals
-Uses unit indexer to correctly handle revived units
-Removed unneeded variables from setup function
-Small static if change
-Two more static ifs, in Delay2 and Toggle functions
-Added a trigger to which unit acquires target events are added
-Uses filter function instead of condition for units that enter the map
 
NOTE: Try to replace "metamorphosis" with "bear form", there are bugs with first one because of animations, if you use unit with few animation categories they can mix. I lost 4 hours on fixing simple system, I hope that this will help you guys so you don't bother.
I managed to fix this shit, I'm still confused was this above real reason why it failed so many times.
 
Last edited:
Level 10
Joined
Nov 23, 2006
Messages
592
Hey, so I tried to use this system in my campaign, but it didn't work (got black screen after selecting mission). So I created a new empty campaign, imported the system map and tested it again. This time the mission loads fine, but the system does not work.
Any idea how to make this work in a campaign? Would highly appreciate it!
 
Top