• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[JASS] Event: TriggerRegisterUnitInRange

Status
Not open for further replies.
Level 19
Joined
Dec 12, 2010
Messages
2,069
@IcemanBo comparing to [when unit is in range, then start picking all units in group to check range], is periodic timer for checking better?
nothing can be faster than native. since native makes all the same work no way you'll get anything that fast. thing is, you still wouldn't care about speed anyway, unless there's like 10+ units constantly around.
And since you have no other choice, you'd better simply use different triggers per evey unit. I just can't see why not
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
You create 1 trigger per unit, attach 1 event per trigger and then map "myUnit" to the trigger using a hashtable. Potentially leakless as long as you clean up everything on unit removal.

Be aware that the event disables auras for "myUnit", probably because it hooks into the same system used by auras. Or at least that is what I have been told.

The event might function a lot more efficiently than possible with JASS. As it hooks with the aura system the aura system likely uses some sort of quad tree structure to find units very efficiently and only during a positional change. Polling can simulate this to some extent but not as precisely with as little overhead.
 
Thanks all!
And since you have no other choice, you'd better simply use different triggers per evey unit.
I was not going to do that differently, from the very begining :) (look initial post)
Inside Flux' thread Wietlol, and DSG here said similar solution. Is it something like this?
JASS:
function CreateTriggerUnitsInRange takes unit myUnit, real range returns nothing
    local trigger trig = CreateTrigger()
    local integer h_id = GetHandleId(trig)  
    call TriggerRegisterUnitInRange(trig, myUnit, range, null)
    call TriggerAddAction(trig, function Trig_d2_Actions)
    call SaveUnitHandle(g_hash1, h_id, 0, myUnit)
endfunction
// and load unit myUnit inside "Trig_d2_Actions" as:
    set u = LoadUnitHandle(g_hash1, h_id, 0)
// and clean at the spell end
    call FlushChildHashtable(g_hash1, h_id)
Be aware that the event disables auras for "myUnit"
Just tested with regular Devotion Aura.
Its works fine for me in 2 cases: when myUnit is a source of DevotionAura and when it is benefiting (other hero has Dev Aura). So I cannot confirm "TriggerRegisterUnitInRange" disables auras.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
Its works fine for me in 2 cases: when myUnit is a source of DevotionAura and when it is benefiting (other hero has Dev Aura). So I cannot confirm "TriggerRegisterUnitInRange" disables auras.
So that means either the guy was trolling me all those years ago or it only affects a specific aura. Or maybe it was secretly patched?
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
So that means either the guy was trolling me all those years ago or it only affects a specific aura. Or maybe it was secretly patched?

I have discovered that auras are bugged in wc3... devotion aura at level 3 is supposed to give 4.5 armor. Instead it is giving 9. This value of 9 is from adding up all the levels: 1.5+3.5+4.5=9.5

Another thing is that auras break unit in range events... also auras go way above their allowed range >.<.


The solution? No more auras >.>. Do fully custom script auras and u r good to go. This means that I will be changing this around to not work with standard auras since standard auras are so broken and buggy >.>. The good news is that I don't really have to change much : ).


The latest version is this (this code has messages in it from when I was testing the wc3 auras and wondering why the heck my triggers weren't running).


The UnitInRangeEvent won't fire until the aura goes away, and removing it doesn't seem to help >.>. This means that you can go a little out of range and then go back in and the buff is never put back on. The aura is never applied either. This applies to all auras that affect any unit >.>.

The UnitInRangeEvent has conflict with built-in wc3 auras?


-> 1 year later
Apparently, warcraft 3 auras work with this now... don't know what happened, maybe the old version was bugged?


Quotes from 2011 and 2012 over the span of about 2 years.


The resource that ran into this bug was AuraStruct.


Also, this might help you figure out how to do it.

JASS/script.j at master · nestharus/JASS · GitHub

It uses the old version of Unit Indexer. If you want to modify it to use a different indexing system and just use the script, feel free. It's very short.

The line you would need to modify is line 165
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,657
@ZiBitheWand3r3r
If you really want to do that, then you have to do it properly:
JASS:
library UnitInRangeEvent
    struct UnitInRangeEvent
        
        private static hashtable HASHTABLE = InitHashtable()
        
        readonly trigger trig
        readonly unit whichUnit
        
        public static method create takes unit whichUnit, real range, code action returns thistype
            local thistype this = .allocate()
            set .whichUnit = whichUnit
            set .trig = CreateTrigger()
            call TriggerRegisterUnitInRange(.trig, whichUnit, range, action)
            call SaveInteger(.HASHTABLE, GetHandleId(.trig), 0, this)
            call SaveInteger(.HASHTABLE, GetHandleId(whichUnit), 0, this)
            return this
        endmethod
        
        public static method parseFromUnit takes unit whichUnit returns thistype
            return LoadInteger(.HASHTABLE, GetHandleId(whichUnit), 0)
        endmethod
        public static method parseFromTrigger takes trigger trig returns thistype
            return LoadInteger(.HASHTABLE, GetHandleId(trig), 0)
        endmethod
        
        public method destroy takes nothing returns nothing
            call FlushChildHashtable(.HASHTABLE, GetHandleId(.trig))
            call FlushChildHashtable(.HASHTABLE, GetHandleId(.whichUnit))
            call DestroyTrigger(.trig)
            set .trig = null
            set .whichUnit = null
            call .deallocate()
        endmethod
        
    endstruct
endlibrary

scope Test// for libraries: uses UnitInRangeEvent
    
    function End takes unit whichUnit returns nothing
        // for example when the unit dies, unlearns the spell, loses the buff, whatever
        call UnitInRangeEvent.parseFromUnit(whichUnit).destroy()
    endfunction
    
    function Action takes nothing returns nothing
        local UnitInRangeEvent e = UnitInRangeEvent.parseFromTrigger(GetTriggeringTrigger())
        local unit whichUnit = e.whichUnit
        local unit otherUnit = GetTriggerUnit()
        
        // do stuff
    endfunction
    
    function Start takes unit whichUnit returns nothing
        // when you want to start having this stuff
        local UnitInRangeEvent e = UnitInRangeEvent.create(whichUnit, 500, function Action)
    endfunction
    
endscope
(in yours, the trigger leaks, the function is not really generic because of "function Trig_d2_Actions", you dont have the hashtable initialized by default, etc)
(ofcourse, you can also do it without structs, but that is just the way how I write things)
 
Thanks guys!
@Wietlol:
Your library will not interfere?
Got a hero with 2 skills uses library UnitInRangeEvent.
1st: "Protection field" grants regeneration & knockbacks enemies
2nd: "Survival Spere" which gives allies bonus to max life
If both spells lasts, let's say 15sec, and hero will cast 1st spell and right after 2nd spell.
Since you saving call SaveInteger(.HASHTABLE, GetHandleId(whichUnit), 0, this) (at "0") so new value will overwrite old one. Is it true? I belive it can be solved by passing diffrent integer value instead of "0" (maybe GetSpellAbilityId?)

2nd thought
How to deal with spell uses library UnitInRangeEvent which lasts 15sec, but cooldown is 10sec
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
What you say is true, the unit is not necessarily unique for each instance.
(Maybe the FlushChildHashtable() is also a bit exaggerated.)

In any case, you could specify an index id which could be used as second key in the hashtable.
However, even then, you require something to generate and lookup the key for those multiple instances of the same thing.
On the other hand, your spell that uses this event for 15 seconds and can be reactivated in 10 seconds may not be something you want to have done by this library.
If you have 2 events with the same range for the same unit, you are not really doing something right imho.
In your case, you should simply replace it if the range is not equal, or just keep the old one.

JASS:
library UnitInRangeEvent
    struct UnitInRangeEvent
        
        private static hashtable HASHTABLE = InitHashtable()
        
        readonly trigger trig
        readonly unit whichUnit
        readonly real range
        readonly integer id
        
        public static method create takes unit whichUnit, real range, code action, integer id returns thistype
            local thistype this = .allocate()
            set .whichUnit = whichUnit
            set .trig = CreateTrigger()
            set .range = range
            set .id = id
            call TriggerRegisterUnitInRange(.trig, whichUnit, range, Filter(action))
            call SaveInteger(.HASHTABLE, GetHandleId(.trig), 0, this)
            call SaveInteger(.HASHTABLE, GetHandleId(whichUnit), id, this)
            return this
        endmethod
        
        public static method parseFromUnit takes unit whichUnit, integer id returns thistype
            return LoadInteger(.HASHTABLE, GetHandleId(whichUnit), id)
        endmethod
        public static method parseFromTrigger takes trigger trig returns thistype
            return LoadInteger(.HASHTABLE, GetHandleId(trig), 0)
        endmethod
        
        public method destroy takes nothing returns nothing
            call RemoveSavedInteger(.HASHTABLE, GetHandleId(.trig), 0)
            call RemoveSavedInteger(.HASHTABLE, GetHandleId(.whichUnit), .id)
            call DestroyTrigger(.trig)
            set .trig = null
            set .whichUnit = null
            call .deallocate()
        endmethod
        
    endstruct
endlibrary
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
BTW: is
RemoveSavedInteger
better (faster?) then
FlushChildHashtable
(if only integer is saved)
You should not be thinking of such micro optimization so soon...

I am guessing flush child is faster because it takes 1 less parameter. Execution wise it is likely slower so it comes down to the different between the saving on parameters and the loss in complexity. However both of these are trivial. As soon as more than 1 child is being removed it is probably a lot faster.
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
yes, of course, it was ill-conceived example;]
BTW: is RemoveSavedInteger better (faster?) then FlushChildHashtable (if only integer is saved)
dont bother with those. RemoveSavedInteger does what it says, no extra work. Flushing deletes entry of the branch, basically taking longer route for memory saving. But nevertheless its still just 4 bytes and speed difference doesnt matter at all.
 
Status
Not open for further replies.
Top