• 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.

[Trigger] Run to Safety

Status
Not open for further replies.
Level 5
Joined
Oct 7, 2005
Messages
102
Hey guys.

I'm currently making a zombie apocalypse simulation map. I would like to make a trigger that makes the local people (neutral passive) run from the undead when they are within a certain range.

I already use the 'Can Flee' option with the people which makes them run after they've already been attacked. But if you saw a zombie coming towards you, you would run before it gets you, right?

I'm hoping someone here can come up with a trigger to simulate that effect.

Thanks guys.
 
Level 5
Joined
Oct 7, 2005
Messages
102
What kind of event do I use to do that? I can't find any suitable event for that idea.

Also I don't know how to make the targeted unit run away from that unit, because they need to increase the distance between them and the zombie and in the same direction that the zombie is going.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
I recall it being a specific unit event. This will mean you need to dynamically create the event objects for every civilian/whatever as they enter and also you will need some kind of clean up system to re-create the trigger from time to time so as to allow all the event objects to be recycled.

The other approach would be to periodically search nearby every appropriate unit and if there are enemies order them away. The search will have to be staggered for all but the smallest number of units on the map. Chances are this will consume more resources than just making the units acquire the zombies and then in response to that event you order them away.
 
Level 5
Joined
Oct 7, 2005
Messages
102
Okay, what you're saying makes sense.

But I don't know how to make that said trigger, this is why I'm asking.

The only event I can find that's near acquiring a target, is acquire item. Which is obviously not right.

But please construct the trigger for me, the point of making it is where I am stuck.
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
It's right here in common.j, use this event:

562 constant unitevent EVENT_UNIT_ACQUIRED_TARGET

I assume it fires off whenever a unit auto-acquires a target to attack it, or possibly even cast an auto-cast spell, e.g. a Priest acquires a friendly unit to heal.

All you do is check to see if the acquired unit is an enemy unit, then order the villagers to move to a random point X distance away from that enemy unit.

Dr Super Good said:
I recall it being a specific unit event. This will mean you need to dynamically create the event objects for every civilian/whatever as they enter and also you will need some kind of clean up system to re-create the trigger from time to time so as to allow all the event objects to be recycled.

This sounds like a good idea, though I'm not sure it's necessary to create event objects, rather just triggers?. If it is a single unit event, then it has to be registered to a unique unit handle each time, rather than a general event that works in response to any unit acquiring a target.

One way to do this is to write a script that enumerates all civilians in the map at the start, and then creates a struct for them. The struct registers two unit events: the acquire event, and then a death event. When the civilian dies, clean up the struct and delete the acquire / death event.

Alternatively don't use a struct, just enumerate all the civilians, and then register two triggers: an acquire event and a death event. If you do it properly, you can turn off the acquire trigger when the death trigger runs.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
This sounds like a good idea, though I'm not sure it's necessary to create event objects, rather just triggers?.
Every event you attach to a trigger makes a new event object with its own handle ID. This will leak as it will persist well past the unit's removal and can only be removed after the trigger the events are attached to is destroyed. I checked and triggers and their events do not leak but they both are cleaned up by destroying the trigger. Events will persist even after the unit they were created for has been removed.

Using separate triggers per event works, but is inefficient. This is why any unit takes damage systems generally use a few triggers attached to a lot of units that are cleaned every so often.

One way to do this is to write a script that enumerates all civilians in the map at the start, and then creates a struct for them. The struct registers two unit events: the acquire event, and then a death event. When the civilian dies, clean up the struct and delete the acquire / death event.
Oh if only it were that easy... You see Blizzard forgot to make a "DestroyEvent" native. This is why all the above elaborate and excessively complex stuff is required. It would just be too easy if one could map the events to the units using a hashtable and destroy them on death/removal. This is not SC2 after all, they cannot have us doing something easily without a lot of hacky fixes.

Alternatively don't use a struct, just enumerate all the civilians, and then register two triggers: an acquire event and a death event. If you do it properly, you can turn off the acquire trigger when the death trigger runs.

Does not deal with event leaks at all. Unless you mean 2 triggers with 2 events and 2 condition objects per unit which is not at all efficient.
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
Every event you attach to a trigger makes a new event object with its own handle ID. This will leak as it will persist well past the unit's removal and can only be removed after the trigger the events are attached to is destroyed.

I am trying to clarify this--do you mean

(1) Triggers permanently leak event objects, even after being destroyed, i.e. call DestroyTrigger(…)

or

(2) Event objects get cleaned up when the trigger that made them is destroyed

If (2), then the system I propose would only need a linear amount of triggers, i.e. O(n).

Does not deal with event leaks at all. Unless you mean 2 triggers with 2 events and 2 condition objects per unit which is not at all efficient.

I meant to say destroying both the triggers when the unit dies (the unit's acquire trigger and the unit's death trigger). Something like this:

JASS:
 private function onAcquire
    //the code fired when the unit acquires a target
  endfunction
  
  private function onDeath
    //the code fired when the unit dies
    local runAway r = Table[GetHandleId(GetDyingUnit())]
    call r.flush() //<-- does all clean up necessary
  endfunction

struct runAway
  unit u
  trigger acquireTrig
  trigger deathTrig

  static method create takes unit whichUnit returns thistype
    local thistype this = thistype.allocate()
    set this.u = whichUnit
    //create / register the two triggers…
    return this
  endmethod
  
  method flush takes nothing returns nothing
    call DestroyTrigger(this.acquireTrig)
    call DestroyTrigger(this.deathTrig)
    set this.acquireTrig = null
    set this.deathTrig = null
    set this.u = null
    call this.destroy()
  endmethod
endstruct

How many units would need to be instantiated to actually cause map lag for this simple albeit inefficient solution?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
(2) Event objects get cleaned up when the trigger that made them is destroyed
Triggers do not make events. You use a factory to make the event that attaches it to a trigger. All events attached to the trigger persist until the trigger is destroyed, even if their subject no longer exists (X takes damage event object will persist until trigger removal after X no longer exists).

How many units would need to be instantiated to actually cause map lag for this simple albeit inefficient solution?
Since the triggers do not generate net traffic they cannot cause lag. There may be performance problems as a result of the number of objects involved and there may be possible crashes due to finite object depletion. Only real tests can show if any are the case.

The biggest problem with destroying triggers is that if they are destroyed in certain states they cause the game to fatal error. Maybe this was patched eventually but a huge cause of crashes in old maps were damage detection systems in the process of re-building their triggers.
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
You use a factory to make the event that attaches it to a trigger

I see. So you would create only one trigger, but register O(n) unit events to it?

The biggest problem with destroying triggers is that if they are destroyed in certain states they cause the game to fatal error.

This seems a bit vague. Do you mean if the destroy function is called when the trigger is executing a certain instruction already the game is guaranteed to crash? Which instructions? And how often does this occur (any statistics?)?

How do I create that in the editor?

Try to follow the logic of the solutions recommended, or wait for a GUI person to come by.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
I see. So you would create only one trigger, but register O(n) unit events to it?
O(n) is a complexity, it has nothing to do with a physical number. O notation represents the scalability of an algorithm with best case being O(1) which means that the algorithm is independent on the data set.

The trigger rebuild process sadly is O(n) based where n is the number of living units attached to the trigger. This is because every living unit will need an event re-added to the new trigger. As such it may be required to break it down into several triggers (like 1 per 50-100 units) to avoid performance and op limit issues during rebuild. Also keeping liveliness before rebuild low (the fraction of events attached to the trigger that represent existing units) will minimize this problem as most of the events on the trigger will be inactive and recycled while few units will actually need new events added. Some kind of generational garbage collection system could work where units which die a lot are kept in short lived triggers while those that survive long times (eg a hero who never is removed) are kept in long lived triggers. In fact almost tempted to make one now since that sounds like probably the best way to do it.
This seems a bit vague. Do you mean if the destroy function is called when the trigger is executing a certain instruction already the game is guaranteed to crash? Which instructions? And how often does this occur (any statistics?)?
I do not know. No one who knew ever bothered to explain it to me and those that did basically told me "its too complicated that you will never understand" which annoyed me quite a bit. All that I managed to pickup is that it is (or at least was) possible to destroy a trigger when it still had an event execution queued to it that would run after it was destroyed and so crash. The example I sort of was told was if you destroy the trigger in response to another event while a damage event is still queued on it, then it will crash when it tries to run the damage event as the trigger it was for was destroyed. Some people claim that this was hidden fixed in one of the latest WC3 patches while others say it still exists and is a main reason of random unexplained map crashes (that happen infrequently that people just ignore them).
 
Status
Not open for further replies.
Top