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

[vJass] GetSourceUnit

A simple but useful library to retrieve the source of a UnitInRange event when there are multiple units registered to one trigger. Credits to Cheezman for the idea, but his original code didn't work right, so I hereby present my version of:

Requires:
TimerUtils by Vexorian
Table by Vexorian
JASS:
//===========================================================================
//==
//== GetSourceUnit()
//==
//== This library lets you store and retrive the source
//== unit of a TriggerRegisterUnitInRange() event.
//== Especially useful for Auras.
//== ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//==
//== How to use
//== ¯¯¯¯¯¯¯¯¯¯
//== function TriggerRegisterUnitInRangeWithSource takes trigger whichTrigger, unit whichUnit, real range, boolexpr filter returns event
//== - Just like the normal one, but saves whichUnit
//==
//== function GetSourceUnit takes nothing returns unit
//== - Returns the source unit of the event
//==
//== function GetSourceUnitFromTrigger takes trigger whichTrigger, unit triggerUnit returns unit
//== - The same as GetSourceUnit, but works outside of a functions events and needs to be passed the triggering trigger and unit
//==
//== function ClearSource takes trigger whichTrigger returns nothing
//== - Nullifies the source (just for cleanup)
//==
//==
//== Requirements
//== ¯¯¯¯¯¯¯¯¯¯¯¯
//== - WarCraft III 1.24 by Blizzard (duh)
//== - JassHelper by Vexorian
//== - TimerUtils by Vexorian
//== - Table by Vexorian
//==
//==
//== Important Notes
//== ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//== - If two units (who are registered to the same trigger) are close together
//== when a unit triggers the event, it may return the wrong source.
//==
//== - If you want the GetSourceUnit() to null the source automatically
//== set ClearSourceTime larger than 0.
//==
//==
//== Version 2.1
//== ¯¯¯¯¯¯¯¯¯¯¯
//== Coded by Elements of Water
//== Inspired by Cheezeman
//==
//===========================================================================
library InRangeWithSource initializer Init requires Table, TimerUtils
    globals
        private constant integer MAX_INSTANCES = 408000
        private constant integer MAX_UNITS = 100
        private constant real OFFSET = 20.00
public real ClearSourceTime = 2.00

        private HandleTable ht
    endglobals

    private struct Data [MAX_INSTANCES]
        unit array registered[MAX_UNITS]
        real array registered_dist[MAX_UNITS]
        integer index = 0
        unit currentSource = null
    endstruct

    function TriggerRegisterUnitInRangeWithSource takes trigger whichTrigger, unit whichUnit, real range, boolexpr filter returns event
        local Data d
        if ht.exists(whichTrigger) then
            set d = ht[whichTrigger]
        else
            set d = Data.create()
            set ht[whichTrigger] = d
        endif

        if d.index < MAX_UNITS then
            set d.registered[d.index] = whichUnit
            set d.registered_dist[d.index] = range + OFFSET
            set d.index = d.index + 1
        else
            debug call BJDebugMsg("InRangeWithSource: Number of units registered to trigger exceeds MAX_UNITS. Please increase MAX_UNITS or register fewer units")
            return null
        endif

        return TriggerRegisterUnitInRange(whichTrigger, whichUnit, range, filter)
endfunction

    private function ClearSourceTimed takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local Data d = GetTimerData(t)

        set d.currentSource = null

        call ReleaseTimer(t)
        set t = null
    endfunction

    function ClearSource takes trigger whichTrigger returns nothing
        local Data d = ht[whichTrigger]
        set d.currentSource = null
    endfunction

    function GetSourceUnitFromTrigger takes trigger whichTrigger, unit triggerUnit returns unit
        local Data d
        local integer i = 0
        local unit u
        local real udist
        local real dx
        local real dy
        local real dist
        local timer t
        if ht.exists(whichTrigger) then
            set d = ht[whichTrigger]
        else
            debug call BJDebugMsg("InRangeWithSource: Attempt to call GetSourceUnit on an unregistered trigger. Please register this trigger using TriggerRegisterUnitInRangeWithSource or do not call GetSourceUnit on this trigger.")
            return null
        endif

        if d.currentSource != null then
            return d.currentSource
        endif

        loop
            exitwhen i >= d.index

            set u = d.registered[i]
            set udist = d.registered_dist[i]

            set dx = GetUnitX(u) - GetUnitX(triggerUnit)
            set dy = GetUnitY(u) - GetUnitY(triggerUnit)
            set dist = SquareRoot(dx * dx + dy * dy)

            exitwhen dist <= udist

            set i = i + 1
        endloop

        if i >= d.index then
            debug call BJDebugMsg("InRangeWithSource: An unregistered unit fired a registered trigger. Please register this unit in the proper way.")
            return null
        endif

        set d.currentSource = u
        set u = null

        if ClearSourceTime > 0 then
            set t = NewTimer()
            call SetTimerData(t, d)
            call TimerStart(t, ClearSourceTime, false, function ClearSourceTimed)
        endif

        return d.currentSource
    endfunction
    
    function GetSourceUnit takes nothing returns unit
        return GetSourceUnitFromTrigger(GetTriggeringTrigger(), GetTriggerUnit())
    endfunction

    private function Init takes nothing returns nothing
        set ht = HandleTable.create()
    endfunction
endlibrary
 

Attachments

  • GetSourceUnit.w3m
    49.7 KB · Views: 143
Last edited by a moderator:
GetSourceUnit is a really generic name. GetCollisionSourceUnit sounds right.

This should also be running off of a Unit Indexer because you shouldn't be generating so many dynamic triggers like this. See Damage Event: http://www.hiveworkshop.com/forums/submissions-414/snippet-damageevent-186829/ for a great template on how to efficiently handle this.

The collision should be dealt with in a "TriggerRegisterCollisionEvent" of some kind so that users don't have to do this for every single unit.
 
Last edited:
Using O(N) searches to get the job done makes this much less
efficient than periodic group enumeration. In fact periodic group
enumeration is quite useful in comparison because it detects when
units leave range as well. And it only requires one handle instead
of a handle per unit. Using a handle + trigger per unit would kill
the O(N) requirement but also double the handle count so it's not
too great an option either.

Graveyarded.
 
Top