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

Unit Within Range 1.5

This is an system which allows to detect when an unit comes in x range to another unit and gives access to range / entering / entered unit.
One can register multiple ranges onto 1 unit but each range can only be registered on each unit once at the same time.

How this script works?

You register an unit with an range.
Each time you do this, a Trigger will be created which will throw an event as soon an unit come within the registered range.
This Trigger allows to access registered Range and Unit.
The used trigger will be destroyed automatically, if the unit is removed / replaced / killed, when one wants that (default when using the GUI registering actions).
One now creates an Trigger which catches the WithinRangeEvent = 1.
Now use the variables described below.

How to install?

Make sure variables are auto generated
Copy the WithinRange Folder
Checkout the Demo folder you might use something from it.

VariableDescription

WithinRangeEvent
The thrown Event:
+1 = entered range;
-1 = Autoderegister on killed/removed/replaced
WithinRangeUnitThe registered unit to which an unit is nearing
WithinRangeEnteringUnitThe unit entering the range
WithinRangeRangeThe registered range of the current Event
WithinRangeUsers A Group containing all units using Range detection generated by this system.
WithinRangeWanted_Filter Used when evoking UnitWithinRange, a Filter can only access the entering Unit with Triggering Unit
A Filter allows you to prevent unwanted Event Throws.
resetValue = no Filter
WithinRangeWanted_Trigger This Trigger will be executed, when an unit enters (it checks conditions)
resetValue = no Trigger
WithinRangeWanted_Event defines the event thrown, when an uniter enters.
(0 won't throw an event)
resetValue = "constant function UnitWithinRangeDefaultEvent", default 1.0
WithinRangeWanted_Keep when set the Wanted_ variables won't reset when evoking UnitWithinRange

JASS:
   function RegisterUnitWithinRangeEvent takes unit u, real range, code filter, real eventValue returns boolean
       - more simple usage of super: no trigger, destroyFilterWhenDone=true, cleanOnKilled = true
   function RegisterUnitWithinRangeTrigger takes unit u, real range, code filter, trigger execution returns boolean
       - more simple usage of super: no Event Thrown, destroyFilterWhenDone=true, cleanOnKilled = true
   function RegisterUnitWithinRangeSuper takes unit u, real range, boolean cleanOnKilled, boolexpr filter, trigger execution, real eventValue, boolean destroyFilterWhenDone returns boolean
       - inside the filter you can only access the entering Unit with GetTriggerUnit()
       - Start the detection for this Unit with this range
       - can not register twice the same range (main number 800.0 and 800.1 are now allowed) onto 1 unit.
       - cleanOnKilled will generate an trigger which will execute DeRegisterUnitWithinRangeUnit as soon the unit dies.
       - cleanOnKilled creates 1 Trigger for each Unit registered.
   function RegisterUnitWithinRangeEx takes unit u, real range, boolean cleanOnKilled, boolexpr filter returns boolean
       -backwards comptatible, wrapper for super
   function RegisterUnitWithinRange takes unit u, real range, boolean cleanOnKilled returns boolean
       -backwards comptatible, wrapper for super

   function DeRegisterUnitWithinRange takes unit u, real range returns boolean
       Destroys the range detection with this specific Range and Unit.

   function DeRegisterUnitWithinRangeUnit takes unit u returns boolean
       Destroys all Triggers used by the unit from UnitWithinRange.


This System can be used without writing Jass.
  • Register
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • -------- Autoregister on start --------
      • Set WithinRangeRange = x //0 or below is not allowed
      • Set WithinRangeUnit = your Unit
      • -------- Optional --------
      • Set WithinRangeWanted_Filter = ((Max. Mana of (Triggering unit)) greater as 0.00) //You can only access the entering unit with Triggering Unit, The Filter will prevent unwanted event Throws.
      • Set WithinRangeWanted_Event = y //0 won't throw an event.
      • Set WithinRangeWanted_Trigger = your Trigger
      • Set WithinRangeWanted_Keep = False/True //Wanted_ variables will reset each time you evoke WithinRange; Except when setting "Wanted_keep" = true.
      • Trigger - Run WithinRange <gen> (ignoring conditions)
  • DeRegister
    • Events
    • Conditions
    • Actions
      • Set WithinRangeRange = x //with 0 you remove all ranges from this unit
      • Set WithinRangeUnit = your unit
      • Trigger - Run WithinRange__DeRegister (ignoring conditions)
Credits

Dr Super Good



VersionChanges

1.5
One can now choose the EventValue thrown, or to not throw an event.
One can now choose a trigger beeing executed (includs checking conditions) when a unit enters
GUI can now use the Filters.
Destroys the given Filters, as soon the triggers is deregistered, except you evoked "function RegisterUnitWithinRangeSuper" directly and choosed otherwise.


1.4
Hashes triggers <-> unit now with registered Range

-> better remove/registering
-> one can not register 2 ranges with the same main number (onto the same unit) anymore (800.2 & 800.1)

Now one can setup an BoolExpr Filter, to prevent unwanted events (not supported in GUI)
moved the GUI Wrappers into the main Code.

1.3d
auto clean now at <= 0.405

1.3c

Removes now the last TriggerHandle when reindexing.

1.3b

Removes now TriggerHandle from the table when removeing single Ranges.
replaced an unneeded table read with an local variable

1.3a
removed outcommented code
Added gui wrapper Triggers for "Deregister x" and "Deregister all"
This Triggers are in the optional wrapper folder
Previews
Contents

RegisterUnitWithinRange 1.5 (Map)

Reviews
IcemanBo
I'm not sure I mentioned it, but changes seem to be made. Can you change: call TriggerRegisterUnitStateEvent(trig, u, UNIT_STATE_LIFE, LESS_THAN, 0.4) to call TriggerRegisterUnitStateEvent(trig, u, UNIT_STATE_LIFE, LESS_THAN_OR_EQUAL, 0.405 Anyways...
Level 12
Joined
Jun 12, 2010
Messages
413
Unfortunately this system fails to detect units that are revived while inside a range. So you will need detect ressurection (unit indexer) and move the unit somewhere and then move it back to it's position. But it does detect units that are created (enter playable map area).

Anyway, this is a useful, simple system that is very modular. Would be nice if you made a GUI api for it, too, since dynamic triggers are out of reach of GUI.
 
In my testings units revived with resurrection / Revie-hero - trigger action, both were detected again.
what revive you used?

Anyway, this is a useful, simple system that is very modular. Would be nice if you made a GUI api for it, too, since dynamic triggers are out of reach of GUI.
Yes dynamic triggers are, but the triggers needed for this systems are generated by the system itself when registering an unit.

You mean i should give all of this 3 Jass API functions an GUI wrapper?
I can do that.​
 
Level 12
Joined
Jun 12, 2010
Messages
413
In my testings units revived with resurrection / Revie-hero - trigger action, both were detected again.
what revive you used?

They were? I was pretty sure that's not how it worked, but Blizzard might've changed it with the recent patches :S I ran into this problem when I was making my Aura System.
I only assumed from the code, I'll check it out in-game then xD

Yes dynamic triggers are, but the triggers needed for this systems are generated by the system itself when registering an unit.

You mean i should give all of this 3 Jass API functions an GUI wrapper?
I can do that.​
Yeah, I mean, they can be used with GUI now, but people would need to use custom scripts. It would be nice to have a GUI way to do it, like:
Global Variable: Unit
Global Variable: Range
Run trigger: Register Unit

So basically a GUI wrapper trigger that takes the global variables as "parameters". You know the drill ^_^
 
call TriggerRegisterUnitStateEvent(trig, u, UNIT_STATE_LIFE, LESS_THAN, 0.5)
why not directly using a death event ? it's more accurate and straight forward for death
call TriggerRegisterUnitEvent(trig, u, EVENT_UNIT_DEATH )

JASS:
                set trig =  LoadTriggerHandle(udg_WithinRangeHash, unitId, size)
                call SaveTriggerHandle(udg_WithinRangeHash,unitId,LoopA, trig )
^The trigger handle should be removed from hashtable. Same here:
call DestroyUnitWhithinTrigger(LoadTriggerHandle(udg_WithinRangeHash,unitId,-1))

JASS:
            //if the last trigger was removed, remove it from users.
            if LoadInteger(udg_WithinRangeHash,unitId,0) == 0 then
(can be) ->
JASS:
            //if the last trigger was removed, remove it from users.
            if size == 1 then

The work you do with tracking TriggerActions is correct, but not required. A trigger's action will be properly destroyed when a trigger got destroyed, too.

It has a bit potential to do a bit less coding if you decide to create one trigger which will fire for any unit death, and there checks are done for potential trigger-decoupling, and to fire events -- instead of caring for unit specific triggers and their size counter. Neither is right, or wrong, but it maybe can simplify it a bit.

The documentation is missing the -1 event.

Might it be useful to provide "A unit leaves range of unit" functionality? Like when a unit is in range of a unit, and then gets out of range at some time, hmm..
 
why not directly using a death event ? it's more accurate and straight forward for death
I use lifestate changer state to catch Remove & Replace & Killed. The death Event would only catch killed.
Guess will use 0.4 then.

The work you do with tracking TriggerActions is correct, but not required. A trigger's action will be properly destroyed when a trigger got destroyed, too.
Sometime ago I read some post on hive which said the opposite :|, that's why i did that.
In dat table ConditionalTriggerExecute vs TriggerEvaluate.
Hmm, sounds like i should use conditions instead of actions, fine.

Might it be useful to provide "A unit leaves range of unit" functionality? Like when a unit is in range of a unit, and then gets out of range at some time, hmm..
That is a good idea.
Hmm, it would need an timer -> ask for an further feature: intervallic events while in range.

(can be) ->
Ah i see thanks.

^The trigger handle should be removed from hashtable. Same here:
You mean i should use this ones?
JASS:
call RemoveSavedHandle(udg_WithinRangeHash, unitId, LoopA)
call RemoveSavedHandle(udg_WithinRangeHash, unitId, -1)
 
Last edited:
Level 22
Joined
Sep 24, 2005
Messages
4,821
The script attaches data to the unit using the range, the current setup of linear searching (O(n) worst-case IIRC) might cause the thread to exceed the thread limit op (I'm not sure?). Maybe you could generate hashkeys using StringHash(RealToInt typecast) and directly access a targeted range to speed the search a bit?

My jass is rusty so I'm sorry in advance if I missed stuff or was wrong.
 
[continued ...]

For leaving event we would periodicly enumerate and keep track of all in-range units, to recognize when they're out of range.
And the philisophy for most use cases is probably different; I can't think of a scenario where:
1. trigger registration + requirement of data structure to hold units
-- is better than:
2. a) direct distance check between 2 specified units
or
2. b) using a temp group enumeration directly

1. seems always like a workaround, less efficient, and less performant. Not sure there is a useful case in jass for this, which has more pros than cons. (maybe you know one)

I use lifestate changer state to catch Remove & Replace & Killed. The death Event would only catch killed.
Guess will use 0.4 then.
Okay, legit answer. Hm.. but I'm not really happy with this, too. It's also somehow less accurate. But not very sure you should use a periodic check for it/or require deinex event "just" for this.

Sometime ago I read some post on hive which said the opposite :|, that's why i did that.
Good you don't believe every bullshit you read, thanks. You're correct of course. Seems I had something with periodic check in mind or just wrote shit.

You mean i should use this ones?
Yeh, because the entries are only flushed when a unit is completly unregistered at the moment, though it should also remove single entries when a single registration is unregistered.

The script attaches data to the unit using the range, the current setup of linear searching (O(n) worst-case IIRC) might cause the thread to exceed the thread limit op (I'm not sure?). Maybe you could generate hashkeys using
StringHash(RealToInt typecast)
and directly access a targeted range to speed the search a bit?

My jass is rusty so I'm sorry in advance if I missed stuff or was wrong.
It's not wrong, I think it's a smart thinking. But dunno if a unit is registered so often practically that typecast + hashload + stringhash will be very superior for most cases. I guess both is okay here.
 
Last edited:
Top