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

[Solved] Help with Generic - A unit acquires target event

Status
Not open for further replies.
EDIT: In case anyone in the future needs a Generic Unit Target Event here is the code:

JASS:
library SetPathing initializer OnInit

    globals
        public trigger AggroTrigger
        public group PATHING_GROUP = CreateGroup()
        private group PATHING_GROUP_SWAP=CreateGroup()
        private group PATHING_GROUP_TEMP
    endglobals

function SetPathing takes nothing returns boolean

local unit u = GetTriggerUnit()
local unit FoG

    call SetUnitPathing(u,true)
    call BJDebugMsg("Pathing set to true")

    call DestroyTrigger(AggroTrigger)

    call GroupRemoveUnit(PATHING_GROUP,u)

    set AggroTrigger = CreateTrigger()
    call TriggerAddCondition(AggroTrigger,function SetPathing)

    loop
        set FoG=FirstOfGroup(PATHING_GROUP)
        exitwhen FoG==null
            call TriggerRegisterUnitEvent(AggroTrigger,FoG,EVENT_UNIT_ACQUIRED_TARGET)
        call GroupAddUnit(PATHING_GROUP_SWAP,FoG)
        call GroupRemoveUnit(PATHING_GROUP,FoG)
    endloop
    set PATHING_GROUP_TEMP=PATHING_GROUP
    set PATHING_GROUP=PATHING_GROUP_SWAP
    set PATHING_GROUP_SWAP=PATHING_GROUP_TEMP


set u = null
return false
endfunction

function UnitCreation takes nothing returns nothing
local unit u = CreateUnit(Player(11),'hfoo',SpawnLocationX,SpawnLocationY,0.)
call SetUnitPathing(u,false)
call TriggerRegisterUnitEvent(AggroTrigger,u,EVENT_UNIT_ACQUIRED_TARGET)
call GroupAddUnit(PATHING_GROUP,u)
endfunction

private function OnInit takes nothing returns nothing
set AggroTrigger=CreateTrigger()
call TriggerAddCondition(AggroTrigger,function SetPathing)
endfunction

endlibrary
 
Last edited:
Level 22
Joined
Feb 6, 2014
Messages
2,466
I also need a way to "deregister" a unit once the event is fired, since I only need it to fire once per unit. Anyone have any idea to do this?
Destroying the trigger destroys all events attached to it. If you're using 1 trigger for many events, you can simply:
destroy trigger -> create trigger -> re-add events not meant to be removed.
 
Hmm that makes sense, so I just call DestroyTrigger(GetTriggeringTrigger()) in the callback function? And that will get rid of any leaks?

But how will I re-add the events then? Pick every unit on the map that hasn't acquired a target yet every single time I destroy the trigger? That seems incredibly inefficient... basically every time a unit acquires a target, I'll have to make a GroupEnum call... not really practical in my map I'm assuming as there are many units acquiring targets every second.
 
Hmm, so something like this?

JASS:
globals
public group PATHING_GROUP = CreateGroup()
endglobals

function CreateUnit takes nothing returns nothing
local unit u = CreateUnit(Player(11),'hfoo',SpawnLocationX,SpawnLocationY,0.)
call SetUnitPathing(GetTriggerUnit(),false)
call RegisterUnitEvent(u,EVENT_UNIT_ACQUIRED_TARGET,function SetPathing)
call GroupAddUnit(PATHING_GROUP,u)
endfunction

function SetPathing takes nothing returns boolean
local unit u = GetTriggerUnit()
local trigger t = GetTriggeringTrigger()

call SetUnitPathing(u,true)

call TriggerClearConditions(t)
call DestroyTrigger(t)
call GroupRemoveUnit(PATHING_GROUP,u)
call ForGroup(PATHING_GROUP,function AddUnits)

set t = null
set u = null
return false
endfunction

function AddUnits takes nothing returns nothing

call RegisterUnitEvent(GetEnumUnit(),EVENT_UNIT_ACQUIRED_TARGET,function SetPathing)

endfunction

Also, does anyone know if TriggerClearConditions leak? Or is that the only way to clear the conditions?
 
Last edited:
Level 22
Joined
Feb 6, 2014
Messages
2,466
Well yes you get the idea, more or less something like that, you just have to fix a few things like invalid func name. It's best to test it yourself if it works and for some syntax errors and such because I didn't have a detailed look at the code.

As far as I know (and I don't see a reason why), clearing trigger condition does not leak but does prevent leak not causes it.
 
I just tried it, it didn't work :\ It seems once I destroy the trigger it doesn't fire anymore :s Is it just due to the way RegisterEventPack works?

I changed the ForGroup() to a First of Group due to syntax error, but other than that the code is the same:
JASS:
globals
public group PATHING_GROUP = CreateGroup()
private group PATHING_GROUP_SWAP=CreateGroup()
private group PATHING_GROUP_TEMP

endglobals

function UnitCreation takes nothing returns nothing
local unit FoG
local unit u = CreateUnit(Player(11),'hfoo',SpawnLocationX,SpawnLocationY,0.)
call SetUnitPathing(GetTriggerUnit(),false)
call RegisterUnitEvent(u,EVENT_UNIT_ACQUIRED_TARGET,function SetPathing)
call GroupAddUnit(PATHING_GROUP,u)
endfunction

function SetPathing takes nothing returns boolean
local unit u = GetTriggerUnit()
local trigger t = GetTriggeringTrigger()

call SetUnitPathing(u,true)

call TriggerClearConditions(t)
call DestroyTrigger(t)

call GroupRemoveUnit(PATHING_GROUP,u)

    loop
        set FoG=FirstOfGroup(PATHING_GROUP)
        exitwhen FoG==null
            call RegisterUnitEvent(FoG,EVENT_UNIT_ACQUIRED_TARGET,function SetPathing)
        call GroupAddUnit(PATHING_GROUP_SWAP,FoG)
        call GroupRemoveUnit(PATHING_GROUP,FoG)
    endloop

set PATHING_GROUP_TEMP=PATHING_GROUP
set PATHING_GROUP=PATHING_GROUP_SWAP
set PATHING_GROUP_SWAP=PATHING_GROUP_TEMP


set t = null
set u = null
return false
endfunction

And the reason I thought TriggerClearConditions leaks, is I remember someone told me that, and to use TriggerRemoveCondition instead, but I can't because than requires a triggercondition parameter, which I don't have access to.
 
Last edited:
Level 22
Joined
Feb 6, 2014
Messages
2,466
Ahh I got confused between clear condition and remove condition. I haven't deal with them a lot lately and I'm all relying on memory here. Why not just use a trigger though if you don't understand how RegisterEvent works? Why do you have to clear/remove the condition? Also, be sure you re-create the trigger. I'm not sure if RegisterEvent automatically creates a new trigger so to be sure I suggest doing it without using RegisterEvent unless you understand how it works. If you're gonna do it without RegisterEvent, be sure to add a condition after trigger creation. Also, AFAIK, desteoying the trigger also destroys the triggercondition with it so no need for TriggerClear/RemoveCondition.

You shouldn't do a FirstOfGroup loop because you want the members to remain after enum (unless you applied some sort of group swapping technique).
 
Last edited:
I thought if I don't remove the condition it causes leaks, but if it doesn't I guess I don't have to. Okay, I will try making it without RegisterEvent. I thought using it would make it easier for me, but I guess not haha.

Yeah, I swapped groups so the FirstofGroup should be fine.

But, what do you mean by add a condition after trigger creation? What condition? o_o
 
I just tried making it, and it works, at least in the beginning. But after it fires a few dozen times, it stops working :\ For some reason it still works, but only for a few units will it fire.

EDIT: After a while, it just stops working entirely :| Is there a limit to how many times I can destroy/recreate a trigger?

JASS:
library SetPathing initializer OnInit

    globals
        public trigger AggroTrigger
        private triggercondition AggroTriggerCondition
        public group PATHING_GROUP = CreateGroup()
        private group PATHING_GROUP_SWAP=CreateGroup()
        private group PATHING_GROUP_TEMP
    endglobals

function SetPathing takes nothing returns boolean

local unit u = GetTriggerUnit()
local unit FoG

    call SetUnitPathing(u,true)
    call BJDebugMsg("Pathing set to true")

    call TriggerRemoveCondition(AggroTrigger, AggroTriggerCondition)
 
    set AggroTriggerCondition = null
 
    call DestroyTrigger(AggroTrigger)
 
    call GroupRemoveUnit(PATHING_GROUP,u)
 
    set AggroTrigger = CreateTrigger()
    set AggroTriggerCondition = TriggerAddCondition(AggroTrigger,function SetPathing)

    loop
        set FoG=FirstOfGroup(PATHING_GROUP)
        exitwhen FoG==null
            call TriggerRegisterUnitEvent(AggroTrigger,FoG,EVENT_UNIT_ACQUIRED_TARGET)
        call GroupAddUnit(PATHING_GROUP_SWAP,FoG)
        call GroupRemoveUnit(PATHING_GROUP,FoG)
    endloop
    set PATHING_GROUP_TEMP=PATHING_GROUP
    set PATHING_GROUP=PATHING_GROUP_SWAP
    set PATHING_GROUP_SWAP=PATHING_GROUP_TEMP
 

set u = null
return false
endfunction

function UnitCreation takes nothing returns nothing
local unit u = CreateUnit(Player(11),'hfoo',SpawnLocationX,SpawnLocationY,0.)
call SetUnitPathing(u,false)
call TriggerRegisterUnitEvent(AggroTrigger,u,EVENT_UNIT_ACQUIRED_TARGET)
call GroupAddUnit(PATHING_GROUP,u)
endfunction

private function OnInit takes nothing returns nothing
set AggroTrigger=CreateTrigger()
set AggroTriggerCondition = TriggerAddCondition(AggroTrigger,function SetPathing)
endfunction

endlibrary
 
Last edited:
Level 22
Joined
Feb 6, 2014
Messages
2,466
set AggroTriggerCondition = TriggerAddCondition(AggroTrigger,function SetPathing)
Why is that the callback? Again, I really won't bother reading through code. Here's an overview and example how to "remove an event" for a single trigger damage event trigger.
- The single trigger is initially created via CreateTrigger, let's call it dmgTrg.
- All units are registered to the trigger (dmgTrg) via TriggerRegister(...). All the registered units are added to a unit group, let's call it dmgGrp.
- When a unit dies, you want to remove that unit's event from dmgTrg right? To achieve this, the only way the community knows how to remove an event is to re-create a trigger. So the next step is to re-create the trigger (destroy trigger then create trigger).
- The main trigger dmgTrg is now a newly recreated trigger with no event, so now we re-add all the existing events except the dying unit's event. So first we remove the dying unit from dmgGrp. Then we pick all units from dmgGrp and register them one by one via TriggerRegister(...).

Example:
- Peasant, Footman and Rifleman are registered to trigger dmgTrg when they take damage.
- Peasant, Footman and Rifleman are added to dmgGrp.
- TriggerAddCondition(dmgTrg, function OnDamage). function OnDamage runs whenever Peasant, Footman or Rifleman takes damage.
- Peasant dies.
- dmgTrg is destroyed.
- dmgTrg = CreateTrigger.
- TriggerAddCondition(dmgTrg, function OnDamage).
- Remove Peasant from dmgGrp.
- Pick all dmgGrp, for each enumerated unit (in this case, Footman and Rifleman), apply TriggerRegister(...) to each picked unit.
- Now we end up having two events for dmgTrg which is exactly what we want.

Hope that helps. It's better if I explain the concept rather than fix your code in my opinion.
 
Thanks Flux, but I already understood the concept and applied it to my code after you told me in your first post.

I got it working for the most part, it just breaks randomly after a while and I can't figure out why. I'm thinking it might have something to do with it having to handle too many units on the map. Because during my testing, I noticed that if I kill the spawned units constantly and quickly, the trigger doesn't seem to break, and works as intended.

Why is that the callback?

Basically what I'm trying to do is: when a unit is spawned, I disable its unit collision. Then, when it enters combat by acquiring a target, I reenable collision for it.
 
Last edited:
Level 22
Joined
Feb 6, 2014
Messages
2,466
too many units on the map
If that's the problem, you can divide the units into many triggers. Example, trg 1 will handle the first 10 units. trg 2 will handle the next 10 units and so on... so that each "trigger recreation" won't end up enumerating all the units. This approach is sometimes called the "bucket container". I read it here and applied it here. The purpose is to make trigger refresh lightweight.
 
JASS:
set AggroTriggerCondition = null
call DestroyTrigger(AggroTrigger)

That doesn't look safe. I was always under the impression that playing around with null values can crash the thread. Shouldn't you destroy the trigger first, then set it to null?

That's the triggercondition if that makes a difference, but I already tried commenting out that line(set AggroTriggerCondition = null) for testing purposes, it still breaks so I don't think that's the issue unfortunately.
 
Yes, and after the fight it will remain unregistered. -> Same unit will never fire the trigger twice -> after a while the system might seem not to react properly.

Maybe something else is wrong, but you would need to test the scenerario a bit, and provide a demo, because in theory if it works for small amount of units, then it should also work for big amount of units, and something like there is a limit to re-create triggers is also not really the case.
 
Yes I am currently adding BJDebugMsg to everything to try and figure out what the problem is, will report results if I find something. As for the fact that they remain unregistered, I can very simply assess that the trigger does not always react properly because once I "deregister" a unit, the unit's pathing should be turned on. After a while when units pile up, the newest created units will not have their collision re-enabled, though they are removed from the group (tested with debug messages).
 
JASS:
function UnitCreation takes nothing returns nothing
local unit u = CreateUnit(Player(11),'hfoo',SpawnLocationX,SpawnLocationY,0.)
call SetUnitPathing(GetTriggerUnit(),false)
call TriggerRegisterUnitEvent(AggroTrigger,u,EVENT_UNIT_ACQUIRED_TARGET)
call GroupAddUnit(PATHING_GROUP,u)
endfunction
^GetTriggerUnit() -> u
 
I tried testing it on a fresh map to see if the code was being interfered with by some other piece of code, but now it just seems to crash after it runs a certain amount of times....

@MyPad Not a bad idea, although I think damage detection systems also have a lot of features I don't need i.e damage blocking,damage type detection etc. But I want to try to get this working, it's really stumping me now haha.
 

Attachments

  • SetPathingTest.w3x
    33.8 KB · Views: 30
Level 18
Joined
Nov 21, 2012
Messages
835
I think removing condition /destroying trigger from inside running condition is a problem
this should work fine:
JASS:
function SetPathing takes nothing returns boolean
local unit u = GetTriggerUnit()
    if not IsUnitInGroup(u, PATHING_GROUP) then
        return false
    endif
    call GroupRemoveUnit(PATHING_GROUP, u)
  
    call SetUnitPathing(u,true)
    //call BJDebugMsg(GetUnitName(u))
    call BJDebugMsg("Pathing set to true")
endfunction
then once per lets say 15sec recreate trigger for all units from PATHING_GROUP,
no offence but can you please change avatar, almost cannot read your post cause of it :)
 
@IcemanBo is right, I took out the TriggerRemoveCondition line and it worked perfectly. However, isn't that line needed to remove leaks?

@ZiBitheWand3r3r your method should work too, and it will probably create less overhead. Am unsure which way to go now though, since I think I got both ways working(assuming taking out the TriggerRemoveCondition line doesn't cause leaks).
 
Last edited:
By the way guys, I know I put this as solved, but it turns out that the TriggerRemoveCondition was not the real issue. The real issue was, if a unit dies without acquiring a target, it is not removed from PATHING_GROUP, therefore after it is removed from the game it will return a null value, thus messing with the FoG loop. So the solution was to create a trigger to check for dying units in my map, and if they are in PATHING_GROUP, remove them. Simple. :D No idea why TriggerRemoveCondition caused crashes in my test map, but not the actual map though, that is strange indeed... However I will still leave TriggerRemoveCondition out, because apparently it is unnecessary regardless.
 
EDIT: In case anyone in the future needs a Generic Unit Target Event here is the code:

JASS:
library SetPathing initializer OnInit

    globals
        public trigger AggroTrigger
        public group PATHING_GROUP = CreateGroup()
        private group PATHING_GROUP_SWAP=CreateGroup()
        private group PATHING_GROUP_TEMP
    endglobals

function SetPathing takes nothing returns boolean

local unit u = GetTriggerUnit()
local unit FoG

    call SetUnitPathing(u,true)
    call BJDebugMsg("Pathing set to true")

    call DestroyTrigger(AggroTrigger)

    call GroupRemoveUnit(PATHING_GROUP,u)

    set AggroTrigger = CreateTrigger()
    call TriggerAddCondition(AggroTrigger,function SetPathing)

    loop
        set FoG=FirstOfGroup(PATHING_GROUP)
        exitwhen FoG==null
            call TriggerRegisterUnitEvent(AggroTrigger,FoG,EVENT_UNIT_ACQUIRED_TARGET)
        call GroupAddUnit(PATHING_GROUP_SWAP,FoG)
        call GroupRemoveUnit(PATHING_GROUP,FoG)
    endloop
    set PATHING_GROUP_TEMP=PATHING_GROUP
    set PATHING_GROUP=PATHING_GROUP_SWAP
    set PATHING_GROUP_SWAP=PATHING_GROUP_TEMP


set u = null
return false
endfunction

function UnitCreation takes nothing returns nothing
local unit u = CreateUnit(Player(11),'hfoo',SpawnLocationX,SpawnLocationY,0.)
call SetUnitPathing(u,false)
call TriggerRegisterUnitEvent(AggroTrigger,u,EVENT_UNIT_ACQUIRED_TARGET)
call GroupAddUnit(PATHING_GROUP,u)
endfunction

private function OnInit takes nothing returns nothing
set AggroTrigger=CreateTrigger()
call TriggerAddCondition(AggroTrigger,function SetPathing)
endfunction

endlibrary

This could go to the Submissions repository. Try your hand on it.
 
Status
Not open for further replies.
Top