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

Healing ability for the Hero against unit damage

Level 2
Joined
Apr 10, 2024
Messages
6
Sorry, my English is not very good.

Hi all. This is the second day I've been trying to make an ability using Damage Engine 3.8.

Ability Objective:
The hero uses a buff that only affects undead and melee allies. When dealing damage, a unit with a buff returns % of the damage to health to the owner (the Hero who used this ability).

The problem is that there can be several Heroes. When using the ability, the variable to whom health needs to be restored is overwritten.
I don’t quite understand how to use local variables in this situation, since to add health, a second trigger is used with the DamageEvent event and the condition whether the combat unit has a buff.

Maybe someone can give me an example of how it would be possible to remember a hero who buffed allies and credit only him with health?
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,031
You can’t do anything with a local variable here because as you said you will have no way to refer to that variable again when damage is dealt. The two simplest solutions are:

1. Implement a unit indexer, which will assign each unit on the map a unique number to its custom value. You can then use those numbers as indices for a unit array that holds the ‘source’ of the buff for each unit. When dealing damage, check for the buff and if it exists then heal the unit in YourUnitArray[IndexOf(Damaging unit)].

2. Use a set of parallel arrays: one unit array one group array. When a unit is affected by this buff, store the buff source in UnitArray[N] and add the affected units to GroupArray[N]. When a unit damages another unit, if the damager has the buff you’ll have to search through the group array until you figure out which group it is a part of, then heal the unit in the same array slot. You’ll have to manage this instance data as they each expire, though.
 
Level 2
Joined
Apr 10, 2024
Messages
6
You can’t do anything with a local variable here because as you said you will have no way to refer to that variable again when damage is dealt. The two simplest solutions are:

1. Implement a unit indexer, which will assign each unit on the map a unique number to its custom value. You can then use those numbers as indices for a unit array that holds the ‘source’ of the buff for each unit. When dealing damage, check for the buff and if it exists then heal the unit in YourUnitArray[IndexOf(Damaging unit)].

2. Use a set of parallel arrays: one unit array one group array. When a unit is affected by this buff, store the buff source in UnitArray[N] and add the affected units to GroupArray[N]. When a unit damages another unit, if the damager has the buff you’ll have to search through the group array until you figure out which group it is a part of, then heal the unit in the same array slot. You’ll have to manage this instance data as they each expire, though.
If you don't mind, could you give me an example? I don’t quite understand how arrays work; I almost never use them. I've read about them and how they work, but I'd like to see examples in a GUI.

I also use the 'Mosquito' unit, they are created to apply a buff to nearby units that meet my conditions.
 
Level 2
Joined
Apr 10, 2024
Messages
6
Because same buffs cannot be stacked, it would be overwritten in any case, no?
Right. Perhaps I need to check not for the buff? But to add units to the group that have the buff applied at the time the ability is used?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,580
As suggested before, a Unit Indexer solves this pretty easily and the buff being overwritten is fine, in fact it's better this way.

You cast the ability, pick every unit within range of your caster or the target point (depends on how you want the mechanic to work), and have a Dummy unit apply a buff to them. Additionally, you link these units to your caster using a Unit array variable:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to YourHeroAbility
  • Actions
    • Set AbilityPoint = (Position of (Triggering unit))
    • Set AbilityGroup = (Units within 500.00 of AbilityPoint)
    • Unit - Create 1 Dummy at AbilityPoint...
    • Set AbilityDummy = (Last created unit)
    • Unit - Add YourBuffAbility to AbilityDummy
    • Unit - Add a 0.20 second expiration timer to AbilityDummy
    • Unit Group - Pick every unit in AbilityGroup and (do Actions)
      • Loop - Actions
        • If all conditions are true then (do Actions)
          • If - Conditions
            • ((Picked unit) is Undead) Equal to True
            • ((Picked unit) is a Melee attacker) Equal to True
            • ((Picked unit) belongs to an ally of (Triggering player)) Equal to True
          • Then - Actions
            • Set Variable AbilityLink[(Custom value of (Picked unit)] = (Triggering unit)
            • Unit - Order AbilityDummy to Orc - Bloodlust (Picked unit)
          • Else - Actions
  • Custom script: call RemoveLocation( udg_AbilityPoint )
  • Custom script: call DestroyGroup( udg_AbilityGroup )
Now anytime a unit with your Buff deals damage you would simply heal AbilityLink[(Custom value of DamageEventSource)]:
  • Events
    • DamageEvent becomes Equal to 1.00
  • Conditions
    • (DamageEventSource has YourBuff) Equal to True
  • Actions
    • Set HealTarget = AbilityLink[(Custom value of DamageEventSource)]
    • Unit - Set life of HealTarget to (Life of HealTarget + (DamageEventAmount x 0.20))
^ 20% lifesteal sent to the Hero that applied the buff to the damage source.

I'm going to assume you don't have a Dummy unit or if you do that it's setup incorrectly (lots of misinformation out there). Here's what you want to do:
1) Copy and paste the Locust.
2) Set it's Model, Shadow, Attacks Enabled, and Movement Type to NONE.
3) Set it's Movement Speed to 0.

Then make sure that any ability you give to it, for example the Bloodlust ability that I'm using in my first trigger, has a 0 second cooldown, 0 mana cost, 99999 cast range, and Targets Allowed set to target anything (Air, Ground, Invulnerable, Vulnerable). Also, ensure that it has no Requirements, the goal is for this ability to be "free" in every way possible.

Variables:
AbilityPoint = Point
AbilityGroup = Unit Group
AbilityLink = Unit (ARRAY)

Abilities:
YourHeroAbility = Whatever ability you want, it doesn't matter much since we trigger everything.
YourBuffAbility = Bloodlust (or whatever buff you'd prefer, just update the order to cast it).
 
Last edited:
Level 2
Joined
Apr 10, 2024
Messages
6
As suggested before, a Unit Indexer solves this pretty easily and the buff being overwritten is fine, in fact it's better this way.

You cast the ability, pick every unit within range of your caster or the target point (depends on how you want the mechanic to work), and have a Dummy unit apply a buff to them. Additionally, you link these units to your caster using a Unit array variable:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to YourHeroAbility
  • Actions
    • Set AbilityPoint = (Position of (Triggering unit))
    • Set AbilityGroup = (Units within 500.00 of AbilityPoint)
    • Unit - Create 1 Dummy at AbilityPoint...
    • Set AbilityDummy = (Last created unit)
    • Unit - Add YourBuffAbility to AbilityDummy
    • Unit - Add a 0.20 second expiration timer to AbilityDummy
    • Unit Group - Pick every unit in AbilityGroup and (do Actions)
      • Loop - Actions
        • If all conditions are true then (do Actions)
          • If - Conditions
            • ((Picked unit) is Undead) Equal to True
            • ((Picked unit) is a Melee attacker) Equal to True
            • ((Picked unit) belongs to an ally of (Triggering player)) Equal to True
          • Then - Actions
            • Set Variable AbilityLink[(Custom value of (Picked unit)] = (Triggering unit)
            • Unit - Order AbilityDummy to Orc - Bloodlust (Picked unit)
          • Else - Actions
  • Custom script: call RemoveLocation( udg_AbilityPoint )
  • Custom script: call DestroyGroup( udg_AbilityGroup )
Now anytime a unit with your Buff deals damage you would simply heal AbilityLink[(Custom value of DamageEventSource)]:
  • Events
    • DamageEvent becomes Equal to 1.00
  • Conditions
    • (DamageEventSource has YourBuff) Equal to True
  • Actions
    • Set HealTarget = AbilityLink[(Custom value of DamageEventSource)]
    • Unit - Set life of HealTarget to (Life of HealTarget + (DamageEventAmount x 0.20))
^ 20% lifesteal sent to the Hero that applied the buff to the damage source.

I'm going to assume you don't have a Dummy unit or if you do that it's setup incorrectly (lots of misinformation out there). Here's what you want to do:
1) Copy and paste the Locust.
2) Set it's Model, Shadow, Attacks Enabled, and Movement Type to NONE.
3) Set it's Movement Speed to 0.

Then make sure that any ability you give to it, for example the Bloodlust ability that I'm using in my first trigger, has a 0 second cooldown, 0 mana cost, 99999 cast range, and Targets Allowed set to target anything (Air, Ground, Invulnerable, Vulnerable). Also, ensure that it has no Requirements, the goal is for this ability to be "free" in every way possible.

Variables:
AbilityPoint = Point
AbilityGroup = Unit Group
AbilityLink = Unit (ARRAY)

Abilities:
YourHeroAbility = Whatever ability you want, it doesn't matter much since we trigger everything.
YourBuffAbility = Bloodlust (or whatever buff you'd prefer, just update the order to cast it).
Thanks for the example. But I still don’t understand what to do with AbilityLink and its custom value. As I understand it, the custom value should be automatically assigned to a new one, since the ability can be used by another hero. For example:

Hero [1] used an ability and applied a buff to units, these units are in battle and HP is added to him. Hero [2] uses the ability and does not buff anyone, but HP from damage goes to him, although Hero [1] buffs.

The AbilityLink variable requires a custom value, but what should I put in there? If I enter a value myself, the script will only work with that value and overwrite my value. As if it weren't an array. Therefore, I need to make sure that using an ability creates a subsequent array index: AbilityLink [0], AbilityLink [1], AbilityLink [2], ... and so on.

Next, I need to receive this AbilityLink in another script...
My problem is that he is not MUI.
Thank you for helping me and trying to explain.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,580
Uncle, It seems that everything worked out for me. Thank you very much!
Great, feel free to post your triggers so we can double check and ensure that you're doing everything properly.

I'll breakdown how Arrays + the Unit Indexer work in case you're confused about anything. Let's start with Arrays:
  • Set MyIntegerArray[1] = 10
  • Set MyIntegerArray[2] = 4
  • Set MyIntegerArray[3] = 2456
The above trigger is showcasing an Integer array variable I created called MyIntegerArray. The number inside of the [brackets] is known as the Index of the Array. What this Index does is allow us to turn one variable into multiple instances of itself that can each have their own value. Once you understand how these work it should become clear as to why they're useful in so many different cases.

Now here's why Arrays are useful when combined with a Unit Indexer system.

There is an Action called Set Custom Value which allows us to attach an Integer to a unit:
  • Unit - Set custom value of (Triggering unit) to 100
This is nice but it's limited to only one custom value per unit. That's not very useful when your map wants to store multiple values per unit and store values that aren't limited to just Integers. That's where the Unit Indexer comes into play. A Unit Indexer is a system that takes advantage of this Action by assigning a unique custom value to each unit in your map. It does this automatically whenever a new unit is created (trained, sold, etc) and for every pre-existing unit at the start of the game. So your very first unit will be given a Custom Value of 1, the second unit will be given a Custom Value of 2, and so on.

But this setting of Custom Value doesn't actually do anything special on it's own. You need to take advantage of it for it to be useful.

Here's an example of how you can take advantage of it using Arrays:
  • Unit - Create 1 Paladin...
  • Set Hero_Luck[(Custom value of (Last created unit))] = 24
  • Unit - Create 1 Warden...
  • Set Hero_Luck[(Custom value of (Last created unit))] = 17
  • Unit - Create 1 Lich...
  • Set Hero_Luck[(Custom value of (Last created unit))] = 30
^ The above trigger uses a new Integer array variable I've created called Hero_Luck. This is intended to act like a new 4th attribute, which we will use in our other triggers. Here I am assigning the newly created Paladin a Luck stat of 24, the Warden a Luck stat of 17, and the Lich a Luck stat of 30. The reason this works and why there aren't any [index] conflicts is because the Unit Indexer has already assigned unique Custom Values to each of these Units at the time of creation. Therefore, each Setting of Hero_Luck will use a different [index].

Note that it doesn't have to be an Integer array variable either, you can now attach ANY variable type to your Unit:
  • Unit - Create 1 Paladin...
  • Special Effect - Create a special effect attached to the origin of (Last created unit) using Abilities\Spells\Human\DivineShield\DivineShieldTarget.mdl
  • Set Hero_Special_Effect[(Custom value of (Last created unit))] = (Last created special effect)
^ I've attached a Special Effect to my Paladin. I can now reference this Special Effect at any time by putting the Paladin's Custom Value into the [index] of the Hero_Special_Effect variable. This would allow me to easily Destroy the effect when my Paladin dies, for example.

Hopefully that all makes sense and you can begin using the simple but very powerful Unit Indexer in more of your triggers :D


Lastly, here's some important to understand mechanics of Arrays + the Unit Indexer system:

You can't use an Integer larger than 32,768 as an [Index] in an Array. I believe this value was 1/4 the size in earlier versions of Warcraft 3. Luckily, the Unit Indexer takes this into consideration and will stay below that number. When a unit dies, the Unit Indexer recycles their custom value and reserves it to be given to a new unit in the future. This ensures that even if your map spawns in 100,000 units over the course of say an hour, as long as there's never more than 32,768 units on the map at one time then they will ALL continue to have unique Custom Values. Basically it's not worth worrying about 99.99% of the time since most maps don't have that many units out at once. Also, recycling custom values is something you should keep in mind. Sometimes you'll want to reset your Arrays back to their default starting values as to prevent future units from inheriting a previous unit's data when the custom value gets recycled. The system actually provides special Events for when a unit becomes Indexed/Recycled, which you can take advantage of here.
 
Last edited:
Top