• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] GetUnitRallyUnit

Status
Not open for further replies.
Hi guys,
Im using GetUnitRallyUnit(myBuilding) for trained unit from building to detect target unit. This native suppose to return unit if I right-click on some unit in game. It works for me only when I target mine units (if Owner of Building == Owner of target unit), othewise it returns null. Any chance to get it work with allied / neutral units?
 
Yes, maybe something like this:

JASS:
library GetRallyUnitEx initializer Init

/*
    function GetUnitRallyUnitEx takes unit building returns unit
*/

    globals
        private hashtable hash = InitHashtable()
    endglobals
 
    private function AllyFilter takes unit a, unit b returns boolean
        return IsUnitType(a, UNIT_TYPE_STRUCTURE) and (IsUnitEnemy(a, GetOwningPlayer(b)) or GetOwningPlayer(b) == Player(PLAYER_NEUTRAL_PASSIVE))
    endfunction
 
    private function IsStructureTower takes unit a, unit b returns boolean
        return IssueTargetOrder(a, "attack", b) and IssueImmediateOrder(a, "stop")
    endfunction
 
    private function OrderFilter takes integer orderId returns boolean
        return orderId == OrderId("setrally") or orderId == OrderId("smart")
    endfunction

    private function onOrder takes nothing returns boolean
        local unit building = GetTriggerUnit()
        local unit target = GetOrderTargetUnit()
        local integer id
   
        if OrderFilter(GetIssuedOrderId()) and not IsStructureTower(building, target) then
             set id = GetHandleId(building)
         
            if AllyFilter(building, target) then
       
                // building wants a non-ally as rally unit
                if HaveSavedHandle(hash, id, 0) then
                    call RemoveSavedHandle(hash, id, 0)
                endif
                call SaveUnitHandle(hash, id, 0, target)
           
            else
                // building wants an ally as rally unit so we clean table
                if HaveSavedHandle(hash, id, 0) then
                    call RemoveSavedHandle(hash, id, 0)
                endif
           
            endif
        endif
   
        set building = null
        set target = null
        return false
    endfunction
 
    function GetUnitRallyUnitEx takes unit building returns unit
        local integer id
        if GetUnitRallyUnit(building) != null then
            return GetUnitRallyUnit(building)
        else
            set id = GetHandleId(building)
            if HaveSavedHandle(hash, id, 0) then
                return LoadUnitHandle(hash, id, 0)
            endif
        endif
        return null
    endfunction
 
    private function onDeath takes nothing returns boolean
        if HaveSavedHandle(hash, GetHandleId(GetTriggerUnit()), 0) then
            call RemoveSavedHandle(hash, GetHandleId(GetTriggerUnit()), 0)
        endif
        return false
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
        call TriggerAddCondition(t, Condition(function onOrder))
   
        set t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
        call TriggerAddCondition(t, Condition(function onDeath))
    endfunction
endlibrary

And then when a unit finishes training you need a trigger to call the GetUnitRallyUnitEx function to check if it has a rally target.

It will currently fail if the building can have rally targets and attack at same time, but you can modify the script of course. (I think the undead main house would not work here for example)

And instead of death, onDeindex might be used, and table over hashtable.^^

Edit:

(in chat) WaterKnight just suggested simply to use a check if unit has rally ability / rally order works. It sounds good, so you might modify it. :)
 
Last edited:
I edited your code cause private function AllyFilter takes unit a, unit b returns boolean should check for structure and owner of building != owner of target.

After test I realized that native is buged for me: it works only when owner of building == owner of target. Moreover native is updated after second rally order in my tests. That is real bug. So I abandom this native and will use your code. I removed AllyFilter and removed connection with native GetUnitRallyUnit, so it always look at hashtable.

Thanks very much IcemanBo! Solved

edit:
Could you please look at this edited code :
JASS:
library GetRallyUnitEx initializer Init

//    function GetUnitRallyUnitEx takes unit building returns unit

globals
    private hashtable hash = InitHashtable()
endglobals
 
private function onOrder takes nothing returns boolean
    local unit building = GetTriggerUnit()
    local unit target = GetOrderTargetUnit()
    local integer ord = GetIssuedOrderId()
    local integer id
   
    if (ord==851980 or ord==851971) and GetUnitAbilityLevel(building, 'ARal') > 0 then    
        set id = GetHandleId(building)       
            if HaveSavedHandle(hash, id, 0) then
                call RemoveSavedHandle(hash, id, 0)
            endif
            call SaveUnitHandle(hash, id, 0, target)
    endif
   
    set building = null
    set target = null
    return false
endfunction
 
function GetUnitRallyUnitEx takes unit building returns unit
    local integer id
        set id = GetHandleId(building)
        if HaveSavedHandle(hash, id, 0) then
            return LoadUnitHandle(hash, id, 0)
        endif
    return null
endfunction
 
private function onDeath takes nothing returns boolean
    if HaveSavedHandle(hash, GetHandleId(GetTriggerUnit()), 0) then
        call RemoveSavedHandle(hash, GetHandleId(GetTriggerUnit()), 0)
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
    call TriggerAddCondition(t, Condition(function onOrder))
   
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition(t, Condition(function onDeath))
endfunction
endlibrary
 
Last edited:
how?? its how it looks on my test
rallyexample.jpg
 
The native brings null for non-allies, yes, but for allies it seems to work.
It does not work for me.

When you select Arcane Sanctum and right-click on Paladin (allied unit-player blue) game prints null as a target unit name, so native doen't work for allies
JASS:
function Trig_Untitled_Trigger_001_Conditions takes nothing returns boolean
    return GetIssuedOrderId()==OrderId("smart") or GetIssuedOrderId()==OrderId("rally")
endfunction
function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
    local unit building = GetOrderedUnit()
    local unit targetRally = GetUnitRallyUnit(building)
    local unit targetEx = GetUnitRallyUnitEx(building)
    call BJDebugMsg("native targetRally: " + GetUnitName(targetRally) + ", targetRallyEX: " + GetUnitName(targetEx))
endfunction
//======================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Untitled_Trigger_001, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
    call TriggerAddCondition( gg_trg_Untitled_Trigger_001, Condition( function Trig_Untitled_Trigger_001_Conditions ) )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
 
Level 13
Joined
Mar 24, 2013
Messages
1,105
Not really anything new here, but just a little bit of additional info.

It does not work on any unit other than ones that player strictly owns, not shared. (As you've experienced)

This can be observed without even testing with the native. When a player uses the rally ability on any of their own units the flag that is created becomes attached to that unit. Any other unit that is selected with the rally ability the flag goes to the point underneath instead of attaching.

It is likely that the native could/would work if the rally ability could actually target not directly owned units. However, it appears the targeting for Rally is hardcoded and cannot be altered.

However, this shouldn't really matter since it looked like IcemanBo's solution works fine.
 
It does not work for me.

When you select Arcane Sanctum and right-click on Paladin (allied unit-player blue) game prints null as a target unit name, so native doen't work for allies
JASS:
function Trig_Untitled_Trigger_001_Conditions takes nothing returns boolean
    return GetIssuedOrderId()==OrderId("smart") or GetIssuedOrderId()==OrderId("rally")
endfunction
function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
    local unit building = GetOrderedUnit()
    local unit targetRally = GetUnitRallyUnit(building)
    local unit targetEx = GetUnitRallyUnitEx(building)
    call BJDebugMsg("native targetRally: " + GetUnitName(targetRally) + ", targetRallyEX: " + GetUnitName(targetEx))
endfunction
//======================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Untitled_Trigger_001, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
    call TriggerAddCondition( gg_trg_Untitled_Trigger_001, Condition( function Trig_Untitled_Trigger_001_Conditions ) )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
Sorry, I still don't understand what you say.

For me, the native works for allies. For other units the custom function must be used.

For you, you say the native does not work, but show the custom function to test it. (and it works)

Could you elaborate?
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
Look at the picture he posted. sometimes., the native prints out nothing
He compares the output of the native to the custom function, and it is different, in 2 of 3 cases the native does print out null, so there is only "native targetRally: " on the screen.
The code you quoted is the one, he tested to get these screen messages.
 
Level 13
Joined
Mar 24, 2013
Messages
1,105
So let me try to explain the picture:

native targetRally: Arcane Sanctum, targetRallyEX: Paladin

Strangely the first time the rally ability is used, the native returns the building -- always.

Not surprisingly the custom function works as intended.

native targetRally: <Empty String>, targetRallyEX: Paladin

It's not the first time rally is used, the native works "properly" meaning it will not return the building.
Instead it will return the unit that was chosen. However, since the targeting of the rally ability is limited to only unit's the player strictly owns. (I.E. red can only target unit red owns, not one's shared to him). The "unit" selected is no unit at all, it is null, which makes it's name an empty string, therefore why it is blank.

Again the custom function works as expected.

The only reason you'd use the custom function and the hash on strictly owned units is because the first time the native technically fails, so to go around this the custom function should be used.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
The native works correctly after the order, but not at the point where it does the order, because at that time it still refers to the last rally unit.
Can confirm, I tested this. It works after a 0.00 timeout timer, too, which makes me think there's an easier solution but I can't find it. Tried:
JASS:
library RallyFix initializer in
    globals
        private unit R = null
        private boolean Go = true
        private timer T = null
    endglobals
  
    private function tim takes nothing returns nothing
        set Go = true
    endfunction
  
    function GetUnitRallyUnitFixed takes unit u returns unit
        local integer i = 0
        set R = u
        set Go = false
        call TimerStart(T, 0.00, false, function tim)
        loop
            //Causes thread termination no matter what bologna I put here to stall it!
            set i = i+1
            exitwhen Go
        endloop
        return GetUnitRallyUnit(u)
    endfunction
  
    private function in takes nothing returns nothing
        set T = CreateTimer()
    endfunction
endlibrary
 
Status
Not open for further replies.
Top