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

Zwiebelchen's Threat System 2.7

What is a threat system?
Basicly, it's a system that takes over Mob AI. The unit that dealt the most damage to the creep will be the one that gets attacked.
Although that is only half the truth, it should be enough to get the idea.

If you ever played World of Warcraft (as a mapper, you should have), chances are you know what a threat system does.
And this is an almost perfect replica of it.


Requirements:
- vJass preprocessor

What is different from this to other systems:
- First of all, this system is almost (if not completely) 100% fail-safe. It does checks on killed AND removed units and clears them automaticly.

- Second, it features a unique camp squad functionality, which ensures that units that are preplaced in camps will always attack and return together. If units return, they will be rendered invulnerable until they reach their original camp position again. If they can not reach their camping position within a certain duration, they will get instantly teleported back.
Like this, there is almost no more kiting or bug-abusing possible, as it was with other systems.

- Third: It does not block other Spellcasting systems. The system will only give orders to units, if the unit either has no order (i.e. auto-engage) or an "attack" order. If you want the unit to use a spell instead of attacking, you can do so whenever you want without having to fear interference of the system.
In fact, there is a special function provided so that people can create their own Spell-AI directly in a custom trigger

- Fourth: It provides a lot of useful functions and Getters (Like ApplyHealThreat or GetCombatState), to make triggering spells even more easy.

- Fifth: It is, by far, the fastest and most flexible threat system out there. I completely remastered the system from the last version and the entire script (except for certain enumerations that can not be avoided) is now O(1) complexity.


Version history:
2.7
- Fixed a bug that sometimes did not properly render units in-combat if pulled again right after returning to camp position

2.6
- Added a new function: ZTS_GetCombatTime, which returns the time an npc unit is currently in combat; returns 0 if the unit is currently out of combat or returning to camp position; does not work on player units

2.5
- Added a new boolean to the ZTS_AddThreatUnit command that determines wether the added unit shall be added to already fighting Creep Camps or only to non-fighting Creep Camps - this is interesting when you create encounters that summon units as you usually don't want those summoned units to create their own Creep Camp but want them to be added to the Boss' Creep Camp
- Fixed a small bug that sometimes caused linked Creep Camps

2.4
- Fixed a bug sometimes rendering units permanently invulnerable, when registering a unit in close range to currently retreating units

2.3
- Changed TriggerActions of dynamic triggers to TriggerConditions, to avoid nasty action leak
- Fixed GetThreatUnitAmount (did not return the correct value)

2.2b
- fixed a small logic bug with GetThreatUnitAmount and GetThreatUnitPosition

2.2
- Now uses "smart" order instead of "attack" to issue attack orders ... it turned out that the "smart" order returns false if the unit can not reach the target (for example when rooted) - weird, as it doesn't work for "attack" for some reason
- because of that, the AddRootAbility function and the Range Setter and Getters were removed, as they have become obsolete

2.1b
- fixed a small logical bug with GetCombatState sometimes returning a false positive

2.1
- rebuilt Update function to avoid some useless enumerations - depending on the number of PlayerUnits registered, the system should now be MULTIPLE TIMES faster - As a side effect, I could also remove some useless variables

2.0
- Initial release

Comments & Discussion:

Why hashtables instead of global arrays and structs?
- hashtables are not limited in terms of max size, unlike and array of structs with a unit array, which reaches the 8000 limit very fast ... this was basicly the most important point on that decision
- hashtables are more flexible and easier to use (Flush functions, etc.)
- hashtables were benchmarked to be only 60-80% slower than getting UnitUserData alone

Where is the sort function?
- version 2.0 and higher does not use sorting anymore; instead, when threat is applied to a unit, it uses an insertion method to keep the order of the list

What is new compared to pre 2.0 versions?
- Aside from the fact that the system now is a dozen times faster than before, I also improved the AI by using the "smart" instead of "attack" order. It turned out that - in a weird way - "smart" is indeed smarter than other orders, as it returns false if the unit can't reach the target (i.e. because of root)
- There is now a way to directly get an Order event by the threat system, to make creating spell-AI easier.

Does it matter how many units are registered to the system at the same time?
- In terms of speed, no; it only affects memory usage, but that should not have an impact on game performance at all, even with tousands of units registered - only the number of currently fighting units affects runtime

I still do not really understand how to use the system...
- just check the demo map and you'll get the idea

Keywords:
threat, aggro, wow, world of warcraft
Contents

Threat System v2.7 (Map)

Reviews
17:56, 17th Jan 2010 TriggerHappy: Very good, and you know what my suggestions are. This can be approved in it's current state, though.

Moderator

M

Moderator

17:56, 17th Jan 2010
TriggerHappy:

Very good, and you know what my suggestions are.
This can be approved in it's current state, though.
 
Level 4
Joined
Nov 16, 2009
Messages
69
Well done. May i will use it in my map.
But one question, the hero with the most threat gets attacked, but do you have threat for every enemy creep, so that two people for example have threat from 2 different creeps. Or has every hero threat for all creeps, and the hero with the most threat gets attacked from all?
 
Level 13
Joined
Mar 16, 2008
Messages
941
Well, I've created such a system a while ago, and it seems that you've put a lot of effort into it (I mean, I wrote it while starting with vJass and it was a mess :D).
Still, I don't see the point with hashtables.
You already said that they're "only" 60-80% slower.
Regarding on how often they're called, I think AutoIndex would make this system much more performant.
However, this is only a small part.
I realy like it and it's well coded. Good work.
Oh, and I just saw one thing:
Why do you use "GetUnitTypeId==0" for a "null"-unit check? Check for == null?^^
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Well, I've created such a system a while ago, and it seems that you've put a lot of effort into it (I mean, I wrote it while starting with vJass and it was a mess :D).
Still, I don't see the point with hashtables.
You already said that they're "only" 60-80% slower.
Regarding on how often they're called, I think AutoIndex would make this system much more performant.
I know about that, but as I already wrote in the FAQ inside the initial post, I decided against it, mainly because pseudo 2-dimensional arrays have a HUGE drawback: They are limited to 8192. While this sounds a lot, it actually isn't, when you think about that each row multiplies with the number of columns.
Let's say you have 100 units registered to the system - that means, that every array can only be 80 list entries long. With 200 registered units, it comes down to only 40 entries, etc.
Sure, I could workaround that by just having those units directly around the player registered to the system, but that would come with huge drawbacks in terms of user-comfort.
In the end, I prefered the flexibility of hashtables over 2-dimensional arrays and - let's face it - the system is fast enough for modern machines. The minor speed increase of arrays (even the most simple functions would only get a speed increase of like 2-3% when I'd upgrade to 2-d arrays) wasn't worth the flexibility loss.

Why do you use "GetUnitTypeId==0" for a "null"-unit check? Check for == null?^^
That's because Unit == null is unsafe since 1.24.
References remain when the handle got removed from the game, if you do not properly null all global handle variables. This is called reference leak.

You can check it out with the following function:
JASS:
globals
    unit U
endglobals

function testme takes nothing returns boolean
    set U = CreateUnit( ... )
    call RemoveUnit(U)
    return U == null
endfunction
The above function returns false.



As I could not find a tag to hide the JASS code (spoiler and hide, such as hidden did not work), I removed the code from the post. You can check the code in the demomap better anyways, as its correctly colored and indended.
 
Level 1
Joined
Jan 20, 2010
Messages
3
Just a small question... Great system though, 5/5!

It works perfect for unit-target abilities but it is not working for my AoE thunderclap-based ability. Must I adjust something to let it work for non-targeted AoE? =]
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Just a small question... Great system though, 5/5!

It works perfect for unit-target abilities but it is not working for my AoE thunderclap-based ability. Must I adjust something to let it work for non-targeted AoE? =]
Hmm, depends on your spell trigger, of course. You'd have to pick all units in range of the target location and apply the threat to them.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Works great! 5/5 just one suggestion, maybe have a bar or something so in an rpg people can see the bar on the unit so they know who has the most threat :) I wouldn't use a multiboard in my rpg for threat :)
If you ask me, displaying the threat and stuff has nothing to do with the system itself. The getters for writing a module like that are there. It should be no problem for any decent triggerer out there to create something like that.

I simply do not want to implement a graphical thing like that to the system, because people usually want to customize their game interface to their liking.
 
If you ask me, displaying the threat and stuff has nothing to do with the system itself. The getters for writing a module like that are there. It should be no problem for any decent triggerer out there to create something like that.

I simply do not want to implement a graphical thing like that to the system, because people usually want to customize their game interface to their liking.

ok lol.
Then I will need to learn vjass :p
 
Level 1
Joined
Jan 20, 2010
Messages
3
1st: Your system still pwns!
2nd: It seems that when you die at some creep (triggered with this system) it resets normal, but when you die for a second time at that same creep (lets say, a bossfight), it does NOT reset.

Are you aware of that? I'm experiencing issues with bossfights in my map, when you come back for a second try (and you die again), the boss doesn't reset at all and just stays at the same amount of health.

EDIT: NEVERMIND, Solved it.

I did not know you had to add the units again when they died once.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
New version! 2.5 now has the AddThreatUnit command changed to also accept a boolean that determines, wether the unit shall be added to an already fighting Creep Camp or not.

This is usually only interesting for summoned units. In theory, you don't want (in-fight) summoned units to create their 'own' camp group. You want them to be added to the boss, so they don't return when they have killed all units on their threat list.
You might ask now why this shouldn't be the case for non-summoned units. The reason is easy:
Lets say you pull a Pack A some meters back to where a dead Pack B was. A unit of Pack B respawns right into Pack A's combat. In this case you don't want the respawned unit to now be added to Pack A, but a seperate group, as then - in case the Packs defeat their opponent - both packs would be "linked", although they shouldn't, obviously, as they have different spawning places.

Sounds complicated, I know, but in the end it comes down to this:
Units that are summond in fight should have this boolean set to true. In all other situations, you should go with false.

Also, I fixed a very small bug with the old AddThreatUnit command that could lead to "linked" creep camps. Please update to 2.5 if you used 2.4!
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Seems to bug when I use entangling roots on a ZTS_registered unit. The unit won't return to its original position; it just stands there doing nothing.
Can you give more specific information on what you were doing?
If you outrange the entangled unit properly, it should be teleported back to its original position after like 10 seconds. Of course it won't "walk" back until the entangling roots are gone.
 
Level 6
Joined
Oct 23, 2011
Messages
182
If I entangle the enemy and run out of its "acquire range" the creep does not reset but instead just goes into sleep without reseting its hp. (After this happens, even if I outrange the creeps, they won't reset. I have to aggro them out of sleep again, drag around a bit then they would finally reset)

Also, it isn't exactly entangling roots; just a modified ensnare with roots effect
Acquire range, along with creep camp cells? seems to interfere a lot with the system

*edit

I think there are also many ways to abuse this system. One is to have a ranged dps who can generate top agro, and have other people block the creep's path by standing next to each other.
Maybe you should add a feature so that if a creep cannot get to his top threat target in X second, maybe it should attack next highest threat unit or closest unit
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
If I entangle the enemy and run out of its "acquire range" the creep does not reset but instead just goes into sleep without reseting its hp. (After this happens, even if I outrange the creeps, they won't reset. I have to aggro them out of sleep again, drag around a bit then they would finally reset)

Also, it isn't exactly entangling roots; just a modified ensnare with roots effect
Acquire range, along with creep camp cells? seems to interfere a lot with the system
So the creep was put to sleep by wc3?
Well in that case, it's not really a bug with the system. When the creep is asleep, it obviously just stands still and therefore will not be outranged properly. Prevent your creeps from sleeping at night and it should be fine. I can not do something about that in the system, because I don't know what people want to do in their map. But from what you told me, this is clearly not a bug with the system, just a problem with creep sleeping infight.

I really suggest removing and altering anything that can interfere with the system, as mentioned in the documentation. Increase the gameplay camp AI constants like return range, decrease the camp radius, increase the returning time, etc ... and of course sleeping creeps won't help too.


I think there are also many ways to abuse this system. One is to have a ranged dps who can generate top agro, and have other people block the creep's path by standing next to each other.
Maybe you should add a feature so that if a creep cannot get to his top threat target in X second, maybe it should attack next highest threat unit or closest unit
I know about this, but unfortunately, I can not do anything about this. This timer thing wouldn't be an appropriate solution ... what if you want to kite the mob around (legally with slow abilities, not by blocking its moves)? With this timer thing, that wouldn't be possible anymore.

I simply suggest reducing the collision size of units in your map if you got trouble with blocking.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Tried using in my map but then I cant start the map \o/

Using NewGen WE

Edit: I realized it might be because I need vJass preprocessor... cant find it though, anyone has a good link?
If you got newgen, then you should be fine.
Maybe give a little bit more detail on the problem? How can you not start the map?
Does it load and crash? Does it not load? The system itself is and should be fine, numerous players are using it (including me).
 
Level 30
Joined
Jul 23, 2009
Messages
1,029
I tried to implement this into the map (auto copy variables check) and when I tried to start the game via test map it opens wc3 and puts me on main menu. If I try to manually go to single player -> custom game and start the map it does the same. Return me to main menu.

I thought it could have been something with JASS helper so I saved the map and then tried starting it. Didn't work :/ thx in advance

Edit: Is it GUI friendly? I dont really understand what I'm supposed to change in the script etc
Edit: Sorry I'm stupid now I found the manual. I will read it through and see if I understand.
EDIT: Still won't work. It gives me a list of infinite errors :/
n8miS.png
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
I tried to implement this into the map (auto copy variables check) and when I tried to start the game via test map it opens wc3 and puts me on main menu. If I try to manually go to single player -> custom game and start the map it does the same. Return me to main menu.

I thought it could have been something with JASS helper so I saved the map and then tried starting it. Didn't work :/ thx in advance

Edit: Is it GUI friendly? I dont really understand what I'm supposed to change in the script etc
Edit: Sorry I'm stupid now I found the manual. I will read it through and see if I understand.
EDIT: Still won't work. It gives me a list of infinite errors :/
n8miS.png
If you got Newgen and installed the newest JASS-helper, make sure you follow the instructions properly:

- Create a new trigger called ZTS
- Convert it to custom text
- replace everything inside this trigger with the script
- Save the map and see if it compiles

If you still encounter a problem even after following those steps, just send me the map and I will look over it and see what I can do.

PS: Test map doesn't work in newgen. It's broken due to the outdated editor. Make sure to manually run the game and test the map.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Updated to 2.6, adding a new function ZTS_GetCombatTime, which returns the time an npc unit is currently in combat (update interval matches the update interval system constant due to performance reasons).

This function is useful when triggering your own spell-AI.
For example, you can use it to make your unit fire spells on random time intervals or create some kind of "enrage" type of spell that triggers after a certain time in combat.
 
Level 3
Joined
Jan 10, 2012
Messages
31
Is there anyway to make an AOE "threat aura" with this? and also how would one go about making a unit gain more threat with a passive ability?
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Is there anyway to make an AOE "threat aura" with this? and also how would one go about making a unit gain more threat with a passive ability?
Both is possible and easy to make.

For the first, simply create a periodic trigger applying x threat to all units within range every y seconds (optionally, you can create a condition that checks for the debuff aswell).

For the latter, simply use a damage detection engine and apply x threat for every point of damage dealt when the passive ability is on the unit. You can check the demo map for this one.
 
Level 3
Joined
Jan 10, 2012
Messages
31
Actually nevermind T_T still doesn't work, does your system not work with Neutral hostile? I have a trigger set to call ZTS_ApplyHealThreat(GetEventDamageSource(),GetEventDamageSource(), 200, false, true) on attack and it no work :/
 
Level 3
Joined
Jan 10, 2012
Messages
31
Also another strange thing, is my trigger doesn't work on units that were in game at init, until they have died and are respawned by my system...mind = blown or are spawned by a trigger, which is weird because how are they added to threat table units after respawn or are they xD?
 
Top