System Unit Enters Range

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Unit Enters Range
Created by Darthfett
Requires: CSData,PUI,BoolexprUtils
Created in vJASS
MUI
MPI


If you've ever used the Unit comes within range of another unit type events, you've probably realized one of the flaws of the event:
There is no "Get the unit which the entering unit came in range of" type function.

Well, that's what I created this system to solve. Basically, it creates a struct, attaches it to a trigger, and adds the UnitInRange event to the trigger. Then you can add your conditions and actions to the trigger.

About the System:
One Trigger Per Unit - Since the system does not use gamecache (to create unique string keys for each unit), it cannot attach multiple units to the same trigger. Therefore it has to create a unique trigger for each unit.

How To: Use the System -

1. Get something that supports vJASS (Newgen {Recommended}, JASSHelper
2. Create a Trigger named "EnterRange"
3. Copy-paste "System Code" into the "EnterRange" Trigger
4. Read the rest of the "How To"s to learn how to use the system.

JASS:
private function Init takes nothing returns nothing
    local trigger t = TriggerRegisterUnitEnterRangeEvent(UNIT,RANGE,SOME_BOOLEXPR)
endfunction


This will create a trigger, and register the event for the unit, in one line.

How To: Get the Unit -

JASS:
private function Actions takes nothing returns nothing
    local unit u = GetEnteringUnit()
    local unit v = GetCenterUnit()
endfunction


u now contains the entering unit. v now contains the "unit which the entering unit came in range of."

How To: Destroy the Trigger-Event -

If you have a unit, and you want to get rid of the UnitInRange trigger-event for the unit, simply call RemoveUnitEnterRangeEvent with the unit.

If you somehow have the trigger, and want to destroy it, use RemoveTriggerEnterRangeEvent, rather than DestroyTrigger. However, if you are using this with GetTriggeringTrigger() (or the trigger that started the thread), use the RemoveTriggerEnterRangeEventAlt function. This will destroy the trigger in a separate thread, so that you don't cause any problems.

System Code:
JASS:
library EnterRange uses CSData,PUI,BoolexprUtils

//===============
//
//       Enter Range v1.02
//      Created by Darthfett
//
// Requires:
//     CSData
//     PUI
//
// Place in a trigger called "EnterRange"
//
//  Provides the unit which the entering unit 
//      came in range of: "GetCenterUnit()"
//
//===============

private struct Data
    //! runtextmacro PUI()
    
    unit whichUnit
    trigger t
    
    static method create takes unit whichUnit returns Data
        local Data d = Data[whichUnit]
        if d != 0 then
            call d.release()
        endif
        set d = Data.allocate()
        set d.t = CreateTrigger()
        set d.whichUnit = whichUnit
        call SetCSData(d.t,d)
        set Data[whichUnit] = d
        return d
    endmethod
        
    method onDestroy takes nothing returns nothing
        call SetCSData(.t,0)
        call DestroyTrigger(.t)
    endmethod
endstruct

//Use the function below to create a trigger with the event
function TriggerRegisterUnitEnterRangeEvent takes unit whichUnit, real range, boolexpr filter returns trigger
    local Data d = Data.create(whichUnit)
    if filter == null then
        call TriggerRegisterUnitInRange(d.t,whichUnit,range,BOOLEXPR_TRUE)
    else
        call TriggerRegisterUnitInRange(d.t,whichUnit,range,filter)
    endif
    return d.t
endfunction

//Use the function below to get the unit which the entering unit came in range of
function GetCenterUnit takes nothing returns unit
    return Data(GetCSData(GetTriggeringTrigger())).whichUnit
endfunction

//Use the function below to dispose of the trigger and event for the unit.
function RemoveUnitEnterRangeEvent takes unit whichUnit returns boolean
    local Data d = Data[whichUnit]
    if d == 0 then
        return false
    endif
    call d.release()
    return true
endfunction

//This will destroy the trigger and clean the struct, but it is NOT to be used with GetTriggeringTrigger()
function RemoveTriggerEnterRangeEvent takes trigger t returns boolean
    local Data d = GetCSData(t)
    if d == 0 then
        return false
    endif
    call d.release()
    return true
endfunction

private function Destroy takes nothing returns nothing
    call Data(GetCSData(GetExpiredTimer())).release()
    call DestroyTimer(GetExpiredTimer())
endfunction

//If you want to use GetTriggeringTrigger(), use this function instead of RemoveTriggerEnterRangeEvent
function RemoveTriggerEnterRangeEventAlt takes trigger trig returns nothing
    local Data d = GetCSData(trig)
    local timer t = CreateTimer()
    call SetCSData(t,d)
    call TimerStart(t,0,false,function Destroy)
    set t = null
endfunction

endlibrary


Fireshock (Attached) is a spell that uses the system to strike entering units with lightning.
 

Attachments

  • FireShock.w3x
    39.2 KB · Views: 309

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
Don't BoolExpr's leak? Not sure on that though...
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Don't BoolExpr's leak? Not sure on that though...

I've heard of the fact that using null boolexpr's leaks, but even if regular boolexprs do, it doesn't matter in this case. I'm using a global boolexpr, to get rid of the null boolexpr. It's going to be reused, so destroying and re-creating it would waste processing speed, as opposed to memory.
 

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
Ah, nice work on the system then.
 

Flare

Stops copies me!
Reaction score
662
Hmmmm... what's so useful about this over just using TriggerRegisterUnitInRange (or whatever the native is called)? There's nothing in particular I can see about this that can't be done with the native and pretty much any struct attachment system. If you could do it without requiring dynamic triggers and guarantee the correct GetCenterUnit, then it'd be awesome, but otherwise I think it's just a bulky version of TriggerRegisterUnitInRange.

Also:
JASS:
//! textmacro EVENT_CreateData
    local Data d = Data.create()
    local trigger t = CreateTrigger()
    call SetCSData(t,d)
    set d.whichUnit = whichUnit
//! endtextmacro

function TriggerRegisterUnitEnterRangeEventEx takes unit whichUnit, real range, boolexpr filter returns trigger
    //! runtextmacro EVENT_CreateData()
    call TriggerRegisterUnitInRange(t,whichUnit,range,filter)
    return t
endfunction

function TriggerRegisterUnitEnterRangeEvent takes unit whichUnit, real range returns trigger
    //! runtextmacro EVENT_CreateData()
    call TriggerRegisterUnitInRange(t,whichUnit,range,BOOLEXPR_TRUE)
    return t
endfunction

can become
JASS:
function TriggerRegisterUnitInRangeEventEx takes unit whichUnit, real range, boolexpr filter returns trigger
    local Data d = Data.create()
    local trigger t = CreateTrigger()
    call SetCSData(t,d)
    set d.whichUnit = whichUnit
    call TriggerRegisterUnitInRange (t, whichUnit, filter)
    return t
endfunction

function TriggerRegisterUnitEnterRangeEvent takes unit whichUnit, real range returns trigger
  return TriggerRegisterUnitInRangeEventEx (whichUnit, range, BOOLEXPR_TRUE)
endfunction


Also, why is Data public? It doesn't need to be, and you're practically begging someone to break stuff :p
 

Dr.Jack

That's Cap'n to you!
Reaction score
109
This a cool system! However may I ask what is the difference between this system and simply attaching the unit to the trigger?
 

Trollvottel

never aging title
Reaction score
262
this actually attaches the unit to the trigger. but it makes it easier for the user. So if you are not so expierienced in triggering, it makes everything simpler. however, there should be a possibility to clear the trigger if you dont want to use it any longer, for example

ReleaseTrigger, ClearTrigger, CleanTrigger. So you can do clearing and destroying in one step.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Hmmmm... what's so useful about this over just using TriggerRegisterUnitInRange (or whatever the native is called)? There's nothing in particular I can see about this that can't be done with the native and pretty much any struct attachment system. If you could do it without requiring dynamic triggers and guarantee the correct GetCenterUnit, then it'd be awesome, but otherwise I think it's just a bulky version of TriggerRegisterUnitInRange.

This is the native with a struct attachment system. The native itself only gives you two Event Response functions: GetTriggerUnit() and GetEnteringUnit() [Same unit], and GetTriggeringTrigger(). I have to attach to one of these two, and it can't work with GetEnteringUnit().

The trigger is the only other way to do it, and since I'm not using gamecache (string keys would allow attaching multiple units to a single trigger), I have to attach the struct to the trigger.

I've needed an Event Response function like "GetCenterUnit()" before, since it allows you to use it in conditions. (Sometimes you don't want any triggers to fire when the center unit is dead).

Also:
JASS:
//! textmacro EVENT_CreateData
    local Data d = Data.create()
    local trigger t = CreateTrigger()
    call SetCSData(t,d)
    set d.whichUnit = whichUnit
//! endtextmacro

function TriggerRegisterUnitEnterRangeEventEx takes unit whichUnit, real range, boolexpr filter returns trigger
    //! runtextmacro EVENT_CreateData()
    call TriggerRegisterUnitInRange(t,whichUnit,range,filter)
    return t
endfunction

function TriggerRegisterUnitEnterRangeEvent takes unit whichUnit, real range returns trigger
    //! runtextmacro EVENT_CreateData()
    call TriggerRegisterUnitInRange(t,whichUnit,range,BOOLEXPR_TRUE)
    return t
endfunction

can become
JASS:
function TriggerRegisterUnitInRangeEventEx takes unit whichUnit, real range, boolexpr filter returns trigger
    local Data d = Data.create()
    local trigger t = CreateTrigger()
    call SetCSData(t,d)
    set d.whichUnit = whichUnit
    call TriggerRegisterUnitInRange (t, whichUnit, filter)
    return t
endfunction

function TriggerRegisterUnitEnterRangeEvent takes unit whichUnit, real range returns trigger
  return TriggerRegisterUnitInRangeEventEx (whichUnit, range, BOOLEXPR_TRUE)
endfunction

I made the textmacro to reduce the size of the code, and to avoid creating a BJ. There's only a few things in the code that you actually need, but I added the extra function and boolexpr so it could work the same as TriggerRegisterUnitInRangeSimple.

Also, why is Data public? It doesn't need to be, and you're practically begging someone to break stuff :p

That's very true. :p I'll fix it in a min.

there should be a possibility to clear the trigger if you dont want to use it any longer, for example

ReleaseTrigger, ClearTrigger, CleanTrigger. So you can do clearing and destroying in one step.

I thought about it, but I've heard a lot of bad things about using DestroyTrigger. I didn't want to add in anything that could potentially lead to problems. I will add it in so that you can remove the trigger off the unit.
 

Flare

Stops copies me!
Reaction score
662
This is the native with a struct attachment system
Well, if someone uses another system (such as ABC, HAIL, HSAS, or a self-made system), it just becomes a pain to have to import CSData for the sake of something you could've done yourself.

I've needed an Event Response function like "GetCenterUnit()" before, since it allows you to use it in conditions.
Well, you don't need a whole system for that when you could just pass a unit using a struct as you would normally do, which is still usable in conditions.

I made the textmacro to reduce the size of the code, and to avoid creating a BJ
Just have one function and allow people to specify a null argument for the filter. If null is specified, set the filter to Condition (function FILTER_True), then you have one function, no BJ-esque function, no null boolexpr leak (since you would be replacing the null with a valid filter), no need for a macro that's not exactly a gigantic help anyway.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Well, if someone uses another system (such as ABC, HAIL, HSAS, or a self-made system), it just becomes a pain to have to import CSData for the sake of something you could've done yourself.

CSData is very lightweight, even if is a bit unsafe with some types. This is a system for a person who does not understand JASS all that well. I'll admit it would be a pain to have to import CSData (and now PUI), just for this, but don't you think it's better to have a premade system like this, than have to start from scratch?

Well, you don't need a whole system for that when you could just pass a unit using a struct as you would normally do, which is still usable in conditions.

Then you would have to create another system which passed the unit as a struct -- something which I'm doing here. With this system, you can simply run the function which creates the trigger&event, and add your Conditions/Actions.

The system is created for someone new to (v)JASS, not someone who already knows all about attaching and structs.

Just have one function and allow people to specify a null argument for the filter. If null is specified, set the filter to Condition (function FILTER_True), then you have one function, no BJ-esque function, no null boolexpr leak (since you would be replacing the null with a valid filter), no need for a macro that's not exactly a gigantic help anyway.

Good idea. :D I'll add it in.

Another point of the macro was to make it so I could modify one function, and change the other as well.
 

Prometheus

Everything is mutable; nothing is sacred
Reaction score
589
Good job making such an uber thing.
Wish it made cookies that come out of my usb ports though, only con with it.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Still awaiting approval, or more comments. Thanks!

EDIT:

I also optimized the GetCenterUnit function so it will get inlined, and made the filterfunc a constant function.

EDIT2:

Also removed the filter "Return true" type functions, and replaced them with the boolexprs in the BoolexprUtils system.
 

Ryuu

I am back with Chocolate (:
Reaction score
64
First it only requires CSData and PUI, but now it requires an additional BoolexprUtils.
But in your documentation you only wrote 'Requires CSData and PUI'.
 

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
Don't know if it's required, but could you post a demo map?
 

UndeadDragon

Super Moderator
Reaction score
447
This is a very useful system. I have come across this problem many times before and I just had to change my idea. Now, I will have to try and remember some of those ideas :)
 

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
I was thinking he should post a demo map, so some people won't have to go dl CS and have to go through the trouble of removing CSData and the functions that accompany it. (This took me half an hour to fix)
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
The requirement of PUI and Cs_Data is annoying imho.
Also i would allow the user to add more than one event for the same trigger.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top