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

[Trigger] Making a Special Effect attach itself to a Unit when it's ordered to 'Defend'

Status
Not open for further replies.
Level 4
Joined
Dec 31, 2014
Messages
68
Hello
I have made a trigger that when a player controlled unit uses the defend ability it creates a special effect overhead the unit using it. When ordered to undefend the special effect dissappears.
This is for a multiplayer map and I want this to function for all players.

This is what I have:

I created a special effect Variable named 'p1defend'

Turn On Defend for Player 1
  • P1 Defend
    • Events
      • Unit - A unit owned by Player 1 (Red) Is issued an order with no target
    • Conditions
      • (Issued order) Equal to (Order(defend))
    • Actions
      • Special Effect - Create a special effect attached to the overhead of (Triggering unit) using Abilities\Spells\Items\AIda\AIdaTarget.mdl
      • Set p1defend = (Last created special effect)
Turn Off Defend for Player 1
  • P1 Undefend
    • Events
      • Unit - A unit owned by Player 1 (Red) Is issued an order with no target
    • Conditions
      • (Issued order) Equal to (Order(undefend))
    • Actions
      • Special Effect - Destroy p1defend
There would then be seperate triggers replacing Player 1 with Player 2,3 etc.

The above trigger works but I believe it would cause leaks?
I tried the point variable method but the most I was able to do was set the point to the position of the unit using the ability but it doesn't attach itself to the unit and remain there, it only occurs at the specified point.

Does anyone have any suggestions as to how I can do this leak free?
Thanks.
 
Level 10
Joined
Apr 4, 2010
Messages
509
I don't think it leaks, But it is ugly, you could trigger it all in one page.
EDIT: This page is MUI
  • Defend SPFX
    • Events
      • Unit - A unit Is issued an order with no target
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Issued order) Equal to (Order(defend))
          • (Issued order) Equal to (Order(undefend))
    • Actions
      • For each (Integer Defend_Loop) from 1 to Defend_Index, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Issued order) Equal to (Order(undefend))
              • (Ordered unit) Equal to Defend_Unit[Defend_Loop]
            • Then - Actions
              • Special Effect - Destroy Defend_SPFX[Defend_Loop]
              • Set Defend_Unit[Defend_Loop] = Defend_Unit[Defend_Index]
              • Set Defend_SPFX[Defend_Loop] = Defend_SPFX[Defend_Index]
              • Set Defend_Loop = (Defend_Loop - 1)
              • Set Defend_Index = (Defend_Index - 1)
              • Skip remaining actions
            • Else - Actions
      • Set Defend_Index = (Defend_Index + 1)
      • Set Defend_Unit[Defend_Index] = (Ordered unit)
      • Special Effect - Create a special effect attached to the overhead of Defend_Unit[Defend_Index] using Abilities\Spells\Undead\DeathCoil\DeathCoilMissile.mdl
      • Set Defend_SPFX[Defend_Index] = (Last created special effect)
 
Level 4
Joined
Dec 31, 2014
Messages
68
thanks for your reply, could you show me how to have it in all 1 page/trigger?
As its multiplayer wouldn't I need separate triggers per player? Like I couldn't have all players just use that 1 variable, what if all players use defend at the same time etc?
 
Level 4
Joined
Dec 31, 2014
Messages
68
Cool thanks for all the help and details, I tried it quickly on my map but unfortunately it didn't work. I will try it properly tomorrow and read through that link more thoroughly. My map is a Co-op RPG map and I have quite a few variables, could there be any interference with other variables I have made? Can a unit be in 2 groups at once for instance?
Otherwise I may have to use my original and untidy way of doing it :/

As I mentioned I'll give it a proper attempt tomorrow and I'll let you know if I manage to get it working, possibly I entered in incorrect variable types/arrays/conditions etc. I downloaded your test map and seen it working there so there must be something I'm doing wrong.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Your trigger does leak.
When you order a unit to cast the defend and you order another unit to cast defend as well, then you have lost the pointer/reference to the first special effect.
Therefor, it cannot be removed and ordering both units to defend, undefend and that several times will cause a big pileup of the special effect for the unit you order to undefend last each time.

But enough about that.
There are 2 ways to actually do this...
1, Add the special effect via the object editor to the defend ability.
Or use a dummy ability with the special effect and give that one to the unit / remove from the unit when he is ordered to defend/undefend.

2, Create an array using the custom value of the unit as index to store the special effect.
A unit indexer (most at least) use the custom value to store a unique id on.

@DEE-BOO
Your solution will work but it will be a heavy operation.
For example, while being in the loop, there is no way that first the order will be undefend and then the next iteration it is something else.
Checking the order should only be done once... and with this trigger, you at least do it twice (because you take both into the conditions).
Also data (ussually handles) retrieved by getters should be placed in variables when used frequently (at least 2 times).

In any case, making triggers work for every player is very easy in most cases.
When you choose an event and you select the "Unit" category, you can see "Specific unit event" which only runs if a specific unit has done something, "Player unit event" which works for all units of a specific player and "Generic unit event" which basically adds the "Player unit event" for each player :D (So all units.)

As for making it MUI and clean, you can make an ability based of Sphere (human category) and give it 0 missile speed and remove the missile art (just in case), then you replace "Art - Target" with your model, remove the strings from "Art - Target Attachment Point" 1, 2 and 3 and add "overhead" to the Target Attachment Point 1.

When you take your initial triggers (as shown in your first post) and change the events to all players and instead of creating/destroying a special effect, you add/remove this ability instead... watch magic :D
 
Level 10
Joined
Apr 4, 2010
Messages
509
Thanks, Is this better?
  • Defend SPFX
    • Events
      • Unit - A unit Is issued an order with no target
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Issued order) Equal to (Order(defend))
        • Then - Actions
          • Set Defend_Index = (Defend_Index + 1)
          • Set Defend_Unit[Defend_Index] = (Ordered unit)
          • Special Effect - Create a special effect attached to the overhead of Defend_Unit[Defend_Index] using Abilities\Spells\Undead\DeathCoil\DeathCoilMissile.mdl
          • Set Defend_SPFX[Defend_Index] = (Last created special effect)
        • Else - Actions
          • For each (Integer Defend_Loop) from 1 to Defend_Index, do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Issued order) Equal to (Order(undefend))
                  • (Ordered unit) Equal to Defend_Unit[Defend_Loop]
                • Then - Actions
                  • Special Effect - Destroy Defend_SPFX[Defend_Loop]
                  • Set Defend_Unit[Defend_Loop] = Defend_Unit[Defend_Index]
                  • Set Defend_SPFX[Defend_Loop] = Defend_SPFX[Defend_Index]
                  • Set Defend_Loop = (Defend_Loop - 1)
                  • Set Defend_Index = (Defend_Index - 1)
                  • Skip remaining actions
                • Else - Actions
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Good try, the second loop still contains a check for the order, you still use getters (single use inside a loop is also multiple use).

Also, I would go for either the ability approach or unit indexer approach to avoid looping through all the units to find the right one.
 
Level 4
Joined
Dec 31, 2014
Messages
68
Hi Wietlol, thanks for your reply.
I'm not going to lie, a lot of what was said I'm not sure I fully understand.
I was sure what I did originally would cause some form of leaks so I'm with you on that.

Originally I did want to do it with the object editor as I have done with other abilities in my map but I couldn't get it to work for this one, the most I could do was for the special effect to occur when defend was activated but then it would disappear once it reached its end frame of animation - It doesn't remain until ordered to undefend. I tried it with 'aura' models also but it still didn't work.

As for dummy abilities, that sounds like a good idea and probably the best/easiest bet. I tried it as you suggested by having it always on with that template sphere ability (which worked) but then I thought, couldn't I make a trigger that simply adds/enables the dummy ability to the ordered unit once the unit uses Defend, then to remove the dummy ability when the unit uses Undefend?

For Example
  • Ordered Unit Uses Defend
    • Events
      • Unit - A unit Is issued an order with no target
    • Conditions
    • Actions
      • If ((Issued order) Equal to (Order(defend))) then do (Unit - Add Defend Dummy to (Ordered unit)) else do (Do nothing)
      • If ((Issued order) Equal to (Order(undefend))) then do (Unit - Remove Defend Dummy from (Ordered unit)) else do (Do nothing)
I tested it myself and this actually works but again I believe this would still leak because of the 'ordered unit'. (I'm not sure if this is true or not, other leaking triggers I had were similar in structure to this but I couldn't find anything about it online)

In an attempt to fix the potential leak I made a variable to add the ordered unit to a temp group.
WIP

  • Defend2
    • Events
      • Unit - A unit Is issued an order with no target
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Issued order) Equal to (Order(defend))
        • Then - Actions
          • Set OrderedUnitDefend = (Ordered unit)
          • Unit - Add Defend Dummy to OrderedUnitDefend
          • -------- Then what would remove the ordered unit from the variable here? --------
        • Else - Actions
          • Do nothing
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Issued order) Equal to (Order(undefend))
        • Then - Actions
          • Set OrderedUnitDefend = (Ordered unit)
          • Unit - Remove Defend Dummy from OrderedUnitDefend
          • -------- Then what would remove the ordered unit from the variable here? --------
        • Else - Actions
          • Do nothing
but then how would I remove the ordered unit from the variable?

How could I modify this, or if it's ugly how could I tidy it up? and could you give me a trigger example?

@Dee-Boo I had another go but I'm afraid I still couldn't get it to work, there is probably something else going on in my level (it's a big level with loads of triggers and variables :p) Thanks though.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
First of all, ALWAYS use the multiple actions like you did in the second trigger.
You will thank me later. (!!!Prophecy!!!)

Also, "Ordered unit" is what we call a getter: A function that loads certain data.
The getter does not necessarily leak unless it instead of loads data, copies/creates data based on the parameters.
"Point of <unit>" creates a new location based on the coordinates of the <unit>. "Units in <range> of <point>" creates a new group based on the range and the location and places all units inside that group.
Stuff like that have to be removed as well, because you may understand that continuasly creating stuff, but not removing it will pile up quite a bit of data. (bit :D)

But for now, we focus on what we are currently using: "Ordered unit".
This is a getter which loads the unit that issued the order when a trigger based on "A unit is issued an order" event runs. (You should use "Triggering unit" though.)
You then use that reference as parameters in your other actions.

There is one thing that is a good habit, which is to store data loaded by functions like getters into variables to avoid calling the getter multiple times.
For example, if I told you to check what the latest news is and I want to use that article 10 times, I want to store that article in my mind (variable) instead of asking you 10 times what the latest news is. (I'm Gawd :D)
In your trigger, you use "Issued order" twice, so placing that inside a variable would be better.

Also "Do nothing" is not a properly descriptive name for what it does.
Yes it does something, it calls an empty function... which is ofcourse useless but still requires processing time. Instead of using "Do nothing", you can leave it empty or place a comment containing some texts like "Here we do nothing.".

When storing data into a variable, you clear that variable by setting the data to null. However, Real, Integer, Boolean and String (and all non-agent) variables do not necessarily have to be nulled.
The data is also removed when overwritten by another value:
"Set X = Triggering unit"
"Set X = Last created unit"
This will work properly. (Be aware that this won't work when you set "X" to newly created data like the location or group I gave as an example before, you have to destroy the actual data inside it first. How to do that depends on the variable and you can find quite a few threads about clearing location/group leaks here in THW.)
In the case of global variables, you don't have to set the value to "null" every time because it gets overwritten whenever it is used again... which often happens for global variables.
For local variables, it is a different story though, but you won't be dealing with those yet.

Your trigger looks fine.
Just remove the "Do nothing"s and use
"Unit - Remove Defend Dummy from (Triggering unit)" and
"Unit - Add Defend Dummy to (Triggering unit)", and you will be fine.
 
Level 4
Joined
Dec 31, 2014
Messages
68
Thank you for your detailed response, there's a lot to think about there but I think I kinda understand it now. Yeah honestly I'm not surprised in this editor that 'do nothing' actually does something untoward :D unless you knew it's a rookie trap like a few other things iv noticed with it.

Anyway yeah that's great, thanks a lot I will remove the 'Do Nothing's from the trigger, and do that comment trick because I'm extra paranoid like that.
Cool Beans!
 
Status
Not open for further replies.
Top