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

[Solved] [1.31.1] Stacking Healing Spell

Status
Not open for further replies.
Level 4
Joined
Jan 20, 2022
Messages
26
Hey all,

I've been trying to create a custom healing spell, but I'm not experienced enough to pull it off, so I'm hoping someone here can help me (I'm on 1.31). Here's a description of the spell:

Amplifying Heal
Heals target for 50 + ((HealAmpCharges) x 10/20/30/40/50) (increments are for each level in the ability). Each heal applied to a target increases their HealAmpCharges by 1, up to a maximum of 5 charges. When a unit has HealAmpCharges, a 5 seconds countdown timer starts. If it counts down all 5 seconds before another Amplifying Heal is used on the unit, a charge is removed. If there are still HealAmpCharges left after the first 5 second count, it repeats - until 0 charges.

My issue with the above spell is not to make it for a single unit, but have it work for multiple units simultaneously, in case the healer decides to heal two different targets right after each other. The spell has almost no cooldown, so the unit won't have problems applying stacks of HealAmpCharges to multiple units at the same time.

I have the current systems in the map already:
Bribe's DDS 3.5
Bribe's Heal Event for GUI
Unit Indexer needed for the above

I am not good with JASS, and evidently I still don't quite understand how to use the Unit Indexer. :)

- Oppaikun
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,535
Using a Unit Indexer you'll need these to get started:

Variables:
HealAmpCharges - Integer (array)
HealAmpLevel - Integer (array)
HealAmpDuration - Real (array)
HealAmpGroup - Unit Group
HealAmpCV - Integer

Triggers:
Heal Amp Cast - This trigger runs when you cast the ability. It uses the Unit Indexer to Set and store HealAmpCharges, HealAmpLevel, and HealAmpDuration to the target. It also adds the target to the HealAmpGroup and Turns on the next trigger, Heal Amp Loop.
  • Actions
  • Set Variable HealAmpCV = Custom value of (target unit of ability being cast)
  • Set Variable HealAmpLevel[HealAmpCV] = Level of (ability being cast) for (Triggering unit)
  • Set Variable HealAmpDuration[HealAmpCV] = 5.00
  • If HealAmpCharges[HealAmpCV] < 5 Then Set Variable HealAmpCharges[HealAmpCV] = (HealAmpCharges[HealAmpCV] + 1) Else do nothing
  • Unit - Set life of (target unit of ability being cast) to life of (target unit of ability being cast) + (50.00 + (HealAmpCharges[HealAmpCV] * (10.00 * HealAmpLevel[HealAmpCV]))
  • Trigger - Turn on Heal Amp Loop (ignoring conditions)

Heal Amp Loop - This trigger runs every 0.10 seconds (periodic interval). It uses the Pick Every Unit function to loop over the HealAmpGroup so that it can check on each unit currently affected by Amplifying Heal. Using your (Picked unit)'s custom value, you subtract 0.10 from HealAmpDuration, then you check if HealAmpDuration is <= 0.01. If it is, you know that 5.00 seconds has passed and you can subtract 1 from HealAmpCharges. Then you check if HealAmpCharges is Equal to 0, if it is, you Remove the (Picked unit) from HealAmpGroup so it's no longer being picked in future cycles of the trigger (for now at least). Otherwise, you reset HealAmpDuration back to 5.00 since there's still more charges left to remove.


That's the basic idea. There's some extra steps you'll want to take once you've got that stuff all sorted out such as Turning off the Heal Amp Loop trigger when no units are in the HealAmpGroup, and removing units from HealAmpGroup that no longer have the Amplifying Heal buff (assuming it can get dispelled/removed) as well as resetting their HealAmpCharges. Also, if you struggle with the Arithmetic for the healing amount you can break the math up into multiple variables to make it easier on you.
  • Set Variable HealAmountPerLvl = (10.00 * HealAmpLevel[HealAmpCV])
  • Set Variable HealAmountPerLvl = (Real(HealAmpCharges[HealAmpCV]) * HealAmountPerLvl)
  • Set Variable HealAmountPerLvl = (HealAmountPerLvl + 50.00)
  • Unit - Set life of (target unit of ability being cast) to life of (target unit of ability being cast) + HealAmountPerLvl

I highly recommend learning how Unit Indexers work, it's your gateway to making advanced MUI triggers. Once you learn that, the concept of Hashtables will make more sense and you can move on to learning those along with the Dynamic Indexing method (links in my signature). All 3 of these methods will make your triggering life easier and should cover just about everything you'll ever need for most maps.
 
Last edited:
Level 4
Joined
Jan 20, 2022
Messages
26
Using a Unit Indexer you'll need these to get started:

Variables:
HealAmpCharges - Integer (array)
HealAmpLevel - Integer (array)
HealAmpDuration - Real (array)
HealAmpGroup - Unit Group
HealAmpCV - Integer

Triggers:
Heal Amp Cast - This trigger runs when you cast the ability. It uses the Unit Indexer to Set and store HealAmpCharges, HealAmpLevel, and HealAmpDuration to the target. It also adds the target to the HealAmpGroup and Turns on the next trigger, Heal Amp Loop.
  • Actions
  • Set Variable HealAmpCV = Custom value of (target unit of ability being cast)
  • Set Variable HealAmpLevel[HealAmpCV] = Level of (ability being cast) for (Triggering unit)
  • Set Variable HealAmpDuration[HealAmpCV] = 5.00
  • If HealAmpCharges[HealAmpCV] < 5 Then Set Variable HealAmpCharges[HealAmpCV] = (HealAmpCharges[HealAmpCV] + 1) Else do nothing
  • Unit - Set life of (target unit of ability being cast) to life of (target unit of ability being cast) + (50.00 + (HealAmpCharges[HealAmpCV] * (10.00 * HealAmpLevel[HealAmpCV]))
  • Trigger - Turn on Heal Amp Loop (ignoring conditions)

Heal Amp Loop - This trigger runs every 0.10 seconds (periodic interval). It uses the Pick Every Unit function to loop over the HealAmpGroup so that it can check on each unit currently affected by Amplifying Heal. Using your (Picked unit)'s custom value, you subtract 0.10 from HealAmpDuration, then you check if HealAmpDuration is <= 0.01. If it is, you know that 5.00 seconds has passed and you can subtract 1 from HealAmpCharges. Then you check if HealAmpCharges is Equal to 0, if it is, you Remove the (Picked unit) from HealAmpGroup so it's no longer being picked in future cycles of the trigger (for now at least). Otherwise, you reset HealAmpDuration back to 5.00 since there's still more charges left to remove.


That's the basic idea. There's some extra steps you'll want to take once you've got that stuff all sorted out such as Turning off the Heal Amp Loop trigger when no units are in the HealAmpGroup, and removing units from HealAmpGroup that no longer have the Amplifying Heal buff (assuming it can get dispelled/removed) as well as resetting their HealAmpCharges. Also, if you struggle with the Arithmetic for the healing amount you can break the math up into multiple variables to make it easier on you.
  • Set Variable HealAmountPerLvl = (10.00 * HealAmpLevel[HealAmpCV])
  • Set Variable HealAmountPerLvl = (Real(HealAmpCharges[HealAmpCV]) * HealAmountPerLvl)
  • Set Variable HealAmountPerLvl = (HealAmountPerLvl + 50.00)
  • Unit - Set life of (target unit of ability being cast) to life of (target unit of ability being cast) + HealAmountPerLvl

I highly recommend learning how Unit Indexers work, it's your gateway to making advanced MUI triggers. Once you learn that, the concept of Hashtables will make more sense and you can move on to learning those along with the Dynamic Indexing method (links in my signature). All 3 of these methods will make your triggering life easier and should cover just about everything you'll ever need for most maps.
That sounds really great, I'll try looking into it after work. Thank you. One thing though: if a unit receives another heal, that specific unit's 5 sec timer should reset.

The above solution doesn't cover that, as far as I can tell. I probably forgot to mention it - sorry. Is it possible to add?

EDIT: Oh wait, silly me. I see we're setting the Duration anew on each heal. My bad! This looks perfect!
 
Last edited:
Level 4
Joined
Jan 20, 2022
Messages
26
All right, I've made all the relevant triggers and even added a floating text for visual buff counts. I must have done something wrong, 'cause it only works if I use it on one unit. Doing it on multiple units makes the buff stay indefinitely on one of the units.

Additionally, it doesn't seem like the Else Action for the Condition: "(Number of units in HealAmpUnitGroup) Grater than 0" in the loop works. The "Game - display to all players" text is never displayed. I made a chat command trigger, that posts the number of units in the group, and it seems to work correctly.

Any ideas?

Heal Amplification Spell:

1644420720006.png


Heal Amplification Loop:

1644420763269.png


Heal Amplification Floating Text:

1644420785541.png
 

Attachments

  • 1644420658822.png
    1644420658822.png
    31.8 KB · Views: 6

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,535
So what you have so far is pretty good, there's just some slight tweaks you need to make.

1)
Originally I was thinking that HealAmpLevel would need to be tracked over time, but it's really only used in the Cast trigger.
You can change it to a non-Array Integer variable to make it a little more efficient/simple:
  • Set Variable HealAmpLevel = (Level of (Ability being cast) for (Triggering unit))

2)
In your Healing Amp Loop trigger, you're making a few mistakes. First, you're forgetting to set HealAmpCV to the Custom value of the (Picked unit). This is the standard design pattern when using a Unit Indexer. So when you want to reference a unit's variables like HealAmpCharges, you must plug their Custom value into the [Index] of those variables. In this case, we use HealAmpCV as a shortcut to store that value in order to make our lives easier.

This looks and performs better:
  • Set Variable HealAmpCV = Custom value of (Triggering unit)
  • Set Variable HealAmpCharges[HealAmpCV] = (HealAmpCharges[HealAmpCV] + 1)

This looks and performs worse, especially with multiple Variable arrays all using the unit's Custom value:
  • Set Variable HealAmpCharges[Custom value of (Triggering unit)] = (HealAmpCharges[Custom value of (Triggering unit)] + 1)

HealAmpCV is a global variable that will retain whatever value it was last given. So how you have it setup now you're only ever Setting HealAmpCV in the Cast trigger, which means that when you reference it in your Loop trigger you're referencing that same last set value.
The solution:
  • Set Variable HealAmpCV = Custom value of (Picked unit)

This same logic needs to be applied to your Floating Text trigger since you're making the same mistake there.


Lastly, you shouldn't check the Number of units in HealAmpGroup inside of the Pick Every Unit function. You want to check this number AFTER the Pick Every Unit function has finished, so at the very end of the trigger in this case.
 
Last edited:
Level 4
Joined
Jan 20, 2022
Messages
26
So what you have so far is pretty good, there's just some slight tweaks you need to make.

1)
Originally I was thinking that HealAmpLevel would need to be tracked over time, but it's really only used in the Cast trigger.
You can change it to a non-Array Integer variable to make it a little more efficient/simple:
  • Set Variable HealAmpLevel = (Level of (Ability being cast) for (Triggering unit))

2)
In your Healing Amp Loop trigger, you're making a few mistakes. First, you're forgetting to set HealAmpCV to the Custom value of the (Picked unit). This is the standard design pattern when using a Unit Indexer. So when you want to reference a unit's variables like HealAmpCharges, you must plug their Custom value into the [Index] of those variables. In this case, we use HealAmpCV as a shortcut to store that value in order to make our lives easier.

This looks and performs better:
  • Set Variable HealAmpCV = Custom value of (Triggering unit)
  • Set Variable HealAmpCharges[HealAmpCV] = (HealAmpCharges[HealAmpCV] + 1)

This looks and performs worse, especially with multiple Variable arrays all using the unit's Custom value:
  • Set Variable HealAmpCharges[Custom value of (Triggering unit)] = (HealAmpCharges[Custom value of (Triggering unit)] + 1)

HealAmpCV is a global variable that will retain whatever value it was last given. So how you have it setup now you're only ever Setting HealAmpCV in the Cast trigger, which means that when you reference it in your Loop trigger you're referencing that same last set value.
The solution:
  • Set Variable HealAmpCV = Custom value of (Picked unit)

This same logic needs to be applied to your Floating Text trigger since you're making the same mistake there.


Lastly, you shouldn't check the Number of units in HealAmpGroup inside of the Pick Every Unit function. You want to check this number AFTER the Pick Every Unit function has finished, so at the very end of the trigger in this case.
Thank you for your response, Uncle :) I've tried to understand what you wrote and implemented it. I feel like I am almost there.

Currently, I have two issues - one may be unrelated to the triggers we're working on:

1) Both the loop and text float seem to be working, but I tried setting the Ability Cooldown to 0 and spamming different units to check. Maybe it won't be an issue with the correct CDs (down to 1 second), but at one point, one of the floating texts stopped showing. There were three of them going at the same time, but one of them stopped working until both of the others had disappeared, and THEN it reappeared.

2) Maybe unrelated, but for some reason my Hero ignores the cast time I've set for the ability:

Level 1 = 3 seconds cast time
Level 2 = 2,5 seconds cast time
Level 3 = 2 seconds cast time
Level 4 = 1,5 seconds cast time
Level 5 = 1 seconds cast time

However, at all levels it seems the cast time is 3 seconds. I'm using the Paladin with 0.001 Cast Backswing and Cast Point animation.

1644507822555.png

1644507854318.png


Thank you for your continued help, I am very thankful! :)
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,535
I don't know about the Casting Time, I would try using a different base ability or something. It should work.

Regarding the Floating Text, you could try to Destroy all instances of HealAmpFloatText right before HealAmpUnitGroup becomes empty.

But my hunch is that you're reaching the Floating Text limit. The game can only have 100 Floating Texts active at a time.

Also, keep in mind that this type of trigger will fail if your ability doesn't apply the Buff instantly. For example, an ability like Acid Bomb which throws out a Missile which applies a Buff. This is because the Target of the ability will be added to the Unit Group and it's Buff will be checked before the Missile has even reached it.
 
Last edited:
Level 4
Joined
Jan 20, 2022
Messages
26
I don't know about the Casting Time, I would try using a different base ability or something. It should work.

Regarding the Floating Text, you could try to Destroy all instances of HealAmpFloatText right before HealAmpUnitGroup becomes empty.

But my hunch is that you're reaching the Floating Text limit. The game can only have 100 Floating Texts active at a time.

Also, keep in mind that this type of trigger will fail if your ability doesn't apply the Buff instantly. For example, an ability like Acid Bomb which throws out a Missile which applies a Buff. This is because the Target of the ability will be added to the Unit Group and it's Buff will be checked before the Missile has even reached it.
Tried Unholy Frenzy, same issue. Will probably end up just setting a flat cast time.

Setting a cast time of 1 second seems to fix the problem.

But I am confused how I can have 100 floating texts. I used the ability on 3 units. Shouldn't they only show 3 floats at a time then, since they are also constantly destroyed?

In any case, thank you for your responses. I'll mark the thread as Solved. I'll still check around for your take on the above if you have any comments. :)
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,535
Tried Unholy Frenzy, same issue. Will probably end up just setting a flat cast time.

Setting a cast time of 1 second seems to fix the problem.

But I am confused how I can have 100 floating texts. I used the ability on 3 units. Shouldn't they only show 3 floats at a time then, since they are also constantly destroyed?

In any case, thank you for your responses. I'll mark the thread as Solved. I'll still check around for your take on the above if you have any comments. :)
If you don't have other Floating Text in the map then I imagine it has to do with how you're destroying/recreating them constantly. It'd probably be better to Create them once upon casting the spell and then periodically Move the text's position.

Also, replacing the Periodic Intervals with Timers may prove useful, they're more precise and will actually restart properly when Starting them again.

 
Status
Not open for further replies.
Top