[JASS] Boolexpr in own function?

So I have a bunch of units stored on a hashtable, and I want to be able to pick a random unit from that hashtable. Ideally, I'd want to be able to pass a filter on that. I've used boolexpr in the past, but I was wondering if I could use it myself for my own functions?

JASS:
    function GetRandomGuest takes unit host, boolexpr guestFilter returns unit
        local integer idHost = GetUnitUserData(host)
        local integer i
        local boolean firstPass
        if guestFilter == null then
            return LoadUnitHandle(GuestStorage, idHost, GetRandomInt(1, HostInvites[idHost]))
        else
            set i = GetRandomInt(1, HostInvites[idHost])
            if i == 1 then
                set firstPass = true
            else
                set firstPass = false
            endif
            loop
                exitwhen i > HostInvites[idHost] and firstPass
                set i = i + 1
                if i > HostInvites[idHost] and not firstPass then
                    set i = 1
                    set firstPass = true
                endif
                //I know that's not how boolexpr are used, but hopefully this should illustrate what I'm trying to do.
                //if Filter(guestFilter)
                    //return LoadUnitHandle(GuestStorage, idHost, i)
                //endif
            endloop
        endif
        return null
    endfunction

Now, to reiterate from the comment inside the code, I know this isn't how boolexpr are used, but essentially, if the user wants to pass a filter on that function, I'd want to be able to filter through the hashtable-stored units and return one with the correct parameters.

Can anyone shed some light?
 
Seems like a redundancy in overhead to store a unit group in a hashtable since I'm already using a unit indexer. Might as well just save the unit group to the index of the host itself. That being said, I tend to stay away from unit groups unless I'm going to empty it straight away, which is what I might actually do for this: place all loaded units inside a temporary group and then pass a filter, though I'm not sure how to return just 1 unit.

I would like to know if there's another way of doing this that doesn't include unit groups, however.
 
Level 13
Joined
Nov 7, 2014
Messages
571
Seems to work:
JASS:
globals
    trigger trg = CreateTrigger()
    unit filter_u
endglobals

function fn_that_wants_boolexpr takes boolexpr filter returns nothing
    local triggercondition tcnd = TriggerAddCondition(trg, filter)

    set filter_u = some-unit-or-something
    if TriggerEvaluate(trg) then // call filter(filter_u)
        ...
    endif

    call TriggerRemoveCondition(trg, tcnd)
    set tcnd = null
endfunction

function f1 takes nothing returns boolean
    return IsUnitType(filter_u, UNIT_TYPE_HERO)
endfunction
function use_fn_that_wants_boolexpr takes nothing returns nothing
    call fn_that_wants_boolexpr(Filter(function f1))
endfunction
 
Hmm, I see. So in my API, I should instruct users to use a specific global variable related to my system instead of using GetFilterUnit()?

JASS:
    function GetRandomGuest takes unit host, boolexpr guestFilter returns unit
        local integer idHost = GetUnitUserData(host)
        local integer i
        local boolean firstPass
        local triggercondition tcnd
        if guestFilter == null then
            return LoadUnitHandle(GuestStorage, idHost, GetRandomInt(1, HostInvites[idHost]))
        else
            set i = GetRandomInt(1, HostInvites[idHost])
            if i == 1 then
                set firstPass = true
            else
                set firstPass = false
            endif
            loop
                exitwhen i > HostInvites[idHost] and firstPass
                set i = i + 1
                if i > HostInvites[idHost] and not firstPass then
                    set i = 1
                    set firstPass = true
                endif              
                set FilterGuest = LoadUnitHandle(GuestStorage, idHost, i)
                set tcnd = TriggerAddCondition(FilterTrig, guestFilter)
                if TriggerEvaluate(FilterTrig) then
                    return FilterGuest
                endif
                set FilterGuest = null
                call TriggerRemoveCondition(FilterTrig, tcnd)
                set tcnd = null
            endloop
        endif
        return null
    endfunction

does that work?

EDIT:

Tested it with the following and it worked!
JASS:
function filter takes nothing returns boolean
    if GetUnitTypeId(FilterGuest) == 'hsor' then
        return true
    else
        return false
    endif
endfunction

function Trig_Filter_Test_Actions takes nothing returns nothing
    local unit u = GetRandomGuest(testunit, Filter(function filter))
    call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\OrbOfDeath\\OrbOfDeathMissile.mdl", u, "chest"))
    set u = null
endfunction

//===========================================================================
function InitTrig_Filter_Test takes nothing returns nothing
    set gg_trg_Filter_Test = CreateTrigger(  )
    call TriggerRegisterTimerEvent( gg_trg_Filter_Test, 1.5, true )
    call TriggerAddAction( gg_trg_Filter_Test, function Trig_Filter_Test_Actions )
endfunction
 
Last edited:
Yep, such thing is also the usual way how vjass systems provide custom events, they let functions register to an internal trigger, and then fire it. It gives power to dynamicly run functions from your system and you can provide correct responses on your end, too.

Just a note:
JASS:
    if i == 1 then
        set firstPass = true
    else
        set firstPass = false
    endif
^can be
set firstPass = (i == 1) , since "i == 1" is a expression which becomes true/false already. : )
(similar thing for the filter ^^)
 
Top