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

taunt

Status
Not open for further replies.
Level 17
Joined
Mar 21, 2011
Messages
1,597
hi,
i want to create a taunt spell without using a periodic timer and order the unit to attack the taunter. i've read a comment by Wietlol:
Save the unit that the taunted unit is supposed to be attacking.
When a unit gains an order... of anything, re-order him to attack the target.
(You need a 0 second timer for that.)
does that work? and how would i do that? i NEVER worked with timers.

thanks
 
Level 5
Joined
Feb 22, 2013
Messages
161
If you wont use a timer, how will you stop the taunt? What you described in the quote can work, but you'll still need a timer to stop the taunt buff or effect.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
The timer that I mentioned is because of how orders and events in WC3 work.

For example, the well known, "<unit> takes damage" event.
When that event is ran, the unit has not yet taken damage.
This is because of how the events are set up.

If you would be able to see the code, you would see something similar to this:
JASS:
function OnDamage takes unit whichUnit, real damageAmount returns nothing
    
    call RunEvent(TakesDamage, whichUnit)
    
    call SetWidgetLife(whichUnit, GetWidgetLife(whichUnit) - damageAmount)
    
endfunction
If you do something that happens when the event is fired, you do it before the life is the new life.
What you want to do is make an action that happens after the life is updated. In that case you make a timer (because you have a delayed action). The delay should be instant actually so you set the duration to 0.
Now at the end of the frame (probably) the timer will expire and you have an action when the health is updated.

We want to do something with orders and that is actually almost the same:
JASS:
function IssueOrder takes unit whichUnit, string order returns nothing
    
    call RunEvent(IssueOrder, whichUnit)
    
    call IssueImmediateOrder(whichUnit, order)
    
endfunction
So if you check if the unit has buff <taunt> when it is ordered to do something and order that unit to attack the source of the taunt... the order gets overwritten immediately because it is set after the event is called.
That is why the 0 seconds timer must be applied here as well.

I have a small piece of code that is meant to give delayed orders (including 0 seconds orders) because it is used a lot.


Put this in the header file:
JASS:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Delayed Order Snippet
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function IssueOrderCallback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local unit whichUnit = LoadUnitHandle(udg_DelayedOrder_Hashtable, id, 0)
    local integer orderType = LoadInteger(udg_DelayedOrder_Hashtable, id, 1)
    local string order = LoadStr(udg_DelayedOrder_Hashtable, id, 2)
    local widget targetWidget
    local location targetLocation
    local integer unitTypeId
    
    if orderType == udg_DelayedOrder_TypeTarget then
        set targetWidget = LoadWidgetHandle(udg_DelayedOrder_Hashtable, id, 3)
        call IssueTargetOrder(whichUnit, order, targetWidget)
        set targetWidget = null
    elseif orderType == udg_DelayedOrder_TypePointTarget then
        set targetLocation = LoadLocationHandle(udg_DelayedOrder_Hashtable, id, 3)
        call IssuePointOrder(whichUnit, order, GetLocationX(targetLocation), GetLocationY(targetLocation))
        call RemoveLocation(targetLocation)
        set targetLocation = null
    elseif orderType == udg_DelayedOrder_TypeTrain then
        set unitTypeId = LoadInteger(udg_DelayedOrder_Hashtable, id, 3)
        call IssueImmediateOrderById(whichUnit, unitTypeId)
    elseif orderType == udg_DelayedOrder_TypeNoTarget then
        call IssueImmediateOrder(whichUnit, order)
    endif
    
    call FlushChildHashtable(udg_DelayedOrder_Hashtable, id)
    call DestroyTimer(t)
    set t = null
    set whichUnit = null
endfunction
function IssueTargetOrderDelayed takes unit whichUnit, string order, widget targetWidget, real delay returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    
    call TimerStart(t, delay, false, function IssueOrderCallback)
    call SaveUnitHandle(udg_DelayedOrder_Hashtable, id, 0, whichUnit)
    call SaveInteger(udg_DelayedOrder_Hashtable, id, 1, udg_DelayedOrder_TypeTarget)
    call SaveStr(udg_DelayedOrder_Hashtable, id, 2, order)
    call SaveWidgetHandle(udg_DelayedOrder_Hashtable, id, 3, targetWidget)
    
    set t = null
endfunction
function IssuePointOrderDelayed takes unit whichUnit, string order, location targetLocation, real delay returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    
    call TimerStart(t, delay, false, function IssueOrderCallback)
    call SaveUnitHandle(udg_DelayedOrder_Hashtable, id, 0, whichUnit)
    call SaveInteger(udg_DelayedOrder_Hashtable, id, 1, udg_DelayedOrder_TypePointTarget)
    call SaveStr(udg_DelayedOrder_Hashtable, id, 2, order)
    call SaveLocationHandle(udg_DelayedOrder_Hashtable, id, 3, targetLocation)
    
    set t = null
endfunction
function IssueTrainOrderDelayed takes unit whichUnit, integer unitTypeId, real delay returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    
    call TimerStart(t, delay, false, function IssueOrderCallback)
    call SaveUnitHandle(udg_DelayedOrder_Hashtable, id, 0, whichUnit)
    call SaveInteger(udg_DelayedOrder_Hashtable, id, 1, udg_DelayedOrder_TypeNoTarget)
    call SaveInteger(udg_DelayedOrder_Hashtable, id, 3, unitTypeId)
    
    set t = null
endfunction
function IssueImmediateOrderDelayed takes unit whichUnit, string order, real delay returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    
    call TimerStart(t, delay, false, function IssueOrderCallback)
    call SaveUnitHandle(udg_DelayedOrder_Hashtable, id, 0, whichUnit)
    call SaveInteger(udg_DelayedOrder_Hashtable, id, 1, udg_DelayedOrder_TypeNoTarget)
    call SaveStr(udg_DelayedOrder_Hashtable, id, 2, order)
    
    set t = null
endfunction
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  End Delayed Order Snippet
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Call the delayed order with custom scripts to use it:
  • Custom script: call IssueTargetOrderDelayed(udg_EOT_Param_Target, "attack", udg_EOT_Param_Source, 0)
  • Custom script: call IssueImmediateOrderDelayed(udg_EOT_Param_Target, "stop", 0.01)
So now we have proper order redirects, we must have a buff.
I have a nice buff system called Effect Over Time System, which is still not approved and might have a few bugs in it.
Anyway, it works for simple buffs so it can be used already.

What RupTure_QS said is done by the system equal as many other things are handled by that. You just have to tell how the buff should look like and what it has to do.

When you made a buff that is applied on a unit properly, you can start making the effect of the buff: the Taunt.
The most simple one is to just order the taunted unit to attack the source every 0.03 seconds.
However I prefer to redirect the orders that the taunted unit gets.
When he is ordered something and it is not ordered to attack the source of the taunt, reorder it to attack the source of the taunt.

Not long ago, I made a taunt spell for jonbon29 that taunts nearby enemy units and give armor to the source for each taunted unit.

There is only one glitch in it:
You can spamm "stop" order (or any other order) to reset the basic attacks so your taunted unit will not attack... I don't know why you want a unit to not attack if it has nothing else it can do but it is still a glitch.
The abilities should be preloaded too.
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
the taunt in your testmap works fine, but i'm not that familiar with jass. i mean i could import it but i want to know what stuff is in my map :) i also want to learn how to use timers in gui. is that possible?

thanks for your answers
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Nothing in JASS must be changed so JASS experience is not required.
you just have to copy and paste all triggers and look in the GUI triggers where i used variables from the object editor. make sure that you have all objects

timers in GUI are very limited so there is not really much to learn about. every timer that you need is done by the systems in that example map
 
Level 9
Joined
May 21, 2014
Messages
580
I have implemented something like this using GUI. It might not be efficient, But I'm going to post it anyway.

  • Provoke
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Provoke (PJ Paladin)
    • Actions
      • Unit Group - Add (Target unit of ability being cast) to ProvokeEnemyUnits
      • Sound - Play Provoke <gen> at 100.00% volume, attached to (Target unit of ability being cast)
      • Trigger - Turn on Provoke Effect <gen>
  • Provoke Effect
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
      • (ProvokeEnemyUnits is empty) Equal to False
    • Actions
      • Unit Group - Pick every unit in ProvokeEnemyUnits and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) has buff Provoke ) Equal to True
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (ProvokeHandler has buff Avatar) Equal to True
                • Then - Actions
                  • Unit - Order (Picked unit) to Stop
                • Else - Actions
                  • Unit - Order (Picked unit) to Attack ProvokeHandler
            • Else - Actions
              • Unit Group - Remove (Picked unit) from ProvokeEnemyUnits
              • Unit - Order (Picked unit) to Stop
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Or - Any (Conditions) are true
            • Conditions
              • (All units of ProvokeEnemyUnits are dead) Equal to True
              • (ProvokeEnemyUnits is empty) Equal to True
        • Then - Actions
          • Unit Group - Pick every unit in ProvokeEnemyUnits and do (Actions)
            • Loop - Actions
              • Unit Group - Remove (Picked unit) from ProvokeEnemyUnits
          • Trigger - Turn off (This trigger)
        • Else - Actions
This was the Provoke Spell I made using triggers.
When the Caster casts provoke, I add the target unit to a Unit Group called ProvokeEnemyUnits, then turn on the periodic trigger. As long as there is a Unit in the Unit group, the periodic event remains on, but if there is no unit in the unit group, then turn it off.
The periodic trigger just commands all units in the ProvokeEnemyUnits.
I added a special case that if the caster of provoke is in Avatar state, then the provoked units are unable to attack the caster as well as they can't move. You can remove this of course if you want.

EDIT:
The spell though needs an active ability.
I don't know whether you want your taunt spell to be passive though.
 
Last edited:
Level 17
Joined
Mar 21, 2011
Messages
1,597
Nothing in JASS must be changed so JASS experience is not required.
you just have to copy and paste all triggers and look in the GUI triggers where i used variables from the object editor. make sure that you have all objects

timers in GUI are very limited so there is not really much to learn about. every timer that you need is done by the systems in that example map


did you even read my post?
 

sentrywiz

S

sentrywiz

hi,
i want to create a taunt spell without using a periodic timer and order the unit to attack the taunter. i've read a comment by Wietlol:

does that work? and how would i do that? i NEVER worked with timers.

thanks

Against a player, if you aren't gonna use a timer or a loop of some sort to force the unit, the player can just click away and so much from your taunt spell.

You need to use some sort of loop, or if/else that forces the taunted unit to attack only the specified target (aka caster)
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
You need to use some sort of loop, or if/else that forces the taunted unit to attack only the specified target (aka caster)

You sure didn't look at the attached map in my link :D
We already have that part of the taunt.
The only problem is that you as a player can still interrupt the attacks of the unit and so your unit will be kind of stunned... but you just don't do damage any more. Seems stupid.

i mean i could import it but i want to know what stuff is in my map :)
There are a few things that are used in that map.
First of all, the system that makes your dreams come true.

What is it?
My Effect Over Time System (EOT) is a system that applies an Effect Over Time on a unit.
The effect is known as a buff in most games including warcraft 3. The difference between an effect and a buff is that the effect does not always have a buff. This means that you will not see the icon (also not a black spot where the icon should be) neither can it be dispelled.
The EOT System also gives a lot possibilities on effects: You gain events like "A unit gains a buff", "A unit's buff is dispelled", "A unit's buff has expired" and "A unit's buff is destroyed"(By any reasons.)
It also gives you the possibility to change the duration of the effect or change the target of the current buff while keeping it's own data.
You will also gain infinite possibilities on what you want to do with your effects.
You can also use this system to create passive abilities or effects that just have to be trackable on a unit.
It simply gives you the opportunity to make the abilities you always wanted.

How does it work?
When you create an effect on a unit, you will create a pile of data. The data is stuff like targeted unit, source of the EOT, the EOT type id, the handle id of the EOT, the buff that is used, the ability that is included on the EOT, the special effect attached to it, the duration of it, some booleans of its statistics, etc, etc, etc.
Every 0.03 seconds of gametime, the EOT System loops through every existing EOT and updates the remaining duration and a few other things.
If the buff is removed, the EOT is apprearantly dispelled, so it must be removed.
If the interval is reached (a simple nice feature) an event is fired which can be caught by triggers to do something on that interval.
If the duration has ended, the Expire event is fired.
(There are also more things that happen but they don't matter that much.)
When the EOT is destroyed, it removes itself from the target (removing buffs, special effects, additional abilities, etc.)

How do you use it?
You have to make an EOT by setting the Param variables to the data of your EOT. After that you run the trigger "EOT_Trigger_Create_EOT". Then your EOT is created and will run like all others.
The trick is how you must fill in the global variables.
Before you set those, you make sure that none of those variables has an unexpected value, so you call the function "EOT_Set_Default_Variables".
After you created an EOT, you can set additional/custom data on it with the global functions. They can save almost any data on it so they are very usefull.
It might be usefull to read the "How to use:" part in the documentation of the system (this is found at the top of the "EOT_System" trigger).


Now to the part where orders become interesting.

As I mentioned before, orders are hard to intercept or redirect, so reordering a unit requires a lot of triggering for every user.
That is why I have my own snippet which simply creates a timer and saves the unit and the given order and orders the unit to do that when the timer expires.

When you want to reorder a unit when the event is a "Unit is issued an order" event, you have to make a 0 seconds timer and give the order when the timer expires.
To simplify that, you now only need one custom script that calls the proper function.

After the order is given, all data is properly removed from the game.


Now to the part where you will become part of the triggerers.

The triggers are pretty simple.
There is one trigger that creates the Taunt EOT,
one trigger that reorders the taunted unit to attack the source
and one trigger that stops the unit after the taunt has finished.

The creation of the taunt in the example has 2 things.
One is the taunt and the other is bonus armor that is given to the source.
To create the taunt, you set the duration, target, source and the type.
All other things are just to make visuals.
You can go and take a look at every other effect created with the EOT to copy and paste it.

The end of the taunt is to stop the units from attacking the source. If the source is far away, they wont follow him any more and focus on closer units instead. You just have to stop the unit after the taunt is gone.
So you have a trigger that runs when an EOT is destroyed and check if that is the Taunt. If so, then order the target to stop after 0 (or more) seconds.

The reorder is a bit harder.
When a unit is issued an order, check if that unit has the buff taunt (or check if that unit is under the effect of the taunt EOT. this requires a bit more performance but can be done if the EOT has no buff.)
Check if the order is not an attack order or the targeted unit is not the source of the taunt.
If either is true, reorder the unit to attack the source.


I hope this clarifies what happens in the triggers.
 
Level 9
Joined
May 21, 2014
Messages
580
The only problem is that you as a player can still interrupt the attacks of the unit and so your unit will be kind of stunned... but you just don't do damage any more. Seems stupid.

There must be some alternative to it. Like disable the unit from being commanded... Give it to a neutral player while giving attacks and retaining team color...? But that will ruin most part like when you hover the unit; the player name who supposedly owns the unit will not appear.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Next to that the biggest problem would be that you have to be allied to the unit and all enemies should be enemies... changing owner won't work.
Pausing the unit, which removes control, became something worse.

As far as I have done, redirecting orders is the only way to remove it completely.
In my maps, I can have uninterruptable channels and basic attacks etc.
But that requires massive triggering so you don't want it.

Next to that, the only thing that happens is that your unit does not attack.
When you do nothing, the unit keeps attacking.
When you spamm orders, the unit won't do shit.
Except if the unit would have a disadvantage when attacking (like spiked carapace), the unit would have no benefit from not attacking.

I wouldn't mind that much about it.
 
Status
Not open for further replies.
Top