• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • It's time for the first HD Modeling Contest of 2025. Join the theme discussion for Hive's HD Modeling Contest #7! Click here to post your idea!

[Solved] Help with spell 95% done, one problem I just cannot find...

Status
Not open for further replies.
Level 4
Joined
Apr 16, 2018
Messages
47
Hate to ask, but might I get a second pair of eyes to find where the issue is in this? Hate to ask, thought I could do it, but no matter where I look I cannot spot the fault. Maybe none and it's just a limitation I don't know. Idk...

Essentially it is a custom spell made from channel that jumps in the direction of the target point, then damages the first target/targets it hits along the way. It was checking for closest enemy before, but I simplified it to only check all units in the area and damage them before turning off the ability to do damage with Booleans; so it doesn't damage anymore along its way.

The problem is that sometimes, it literally just jumps over the unit and never deals damage...

This is my first adventure into custom spells, and I thought I had it all on my own, but apparently im missing something...

This begins the ability. Sets values and all that.
  • Begin Leap
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Panther Leap [Huntress]
    • Actions
      • -------- --------
      • -------- Set Variables --------
      • Set VariableSet Abilities_CastingUnit = (Casting unit)
      • Set VariableSet Abilities_Loc00 = (Position of Abilities_CastingUnit)
      • Set VariableSet Abilities_Real00 = (Angle from (Position of Abilities_CastingUnit) to (Target point of ability being cast))
      • Set VariableSet Abilities_Real01 = 400.00
      • -------- --------
      • -------- Prepare and then start leap --------
      • Unit - Make Abilities_CastingUnit face Abilities_Real00 over 0.02 seconds
      • Unit - Turn collision for Abilities_CastingUnit Off.
      • Trigger - Turn on Leap <gen>
      • -------- --------
      • -------- Add flight and begin leaping into air. --------
      • Unit - Add Crow Form [Adds Flying then removed as no longer is needed] to Abilities_CastingUnit
      • Unit - Remove Crow Form [Adds Flying then removed as no longer is needed] from Abilities_CastingUnit
      • Animation - Change Abilities_CastingUnit flying height to 150.00 at 300.00
Repeats while on after the other trigger turns it on. Making unit actually move and deal damage.
  • Leap
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • -------- --------
      • -------- Set Variables --------
      • Set VariableSet Abilities_Loc01 = ((Position of Abilities_CastingUnit) offset by 30.00 towards Abilities_Real00 degrees.)
      • -------- --------
      • -------- --------
      • -------- Move Unit --------
      • Unit - Move Abilities_CastingUnit instantly to Abilities_Loc01
      • Animation - Play Abilities_CastingUnit's Walk animation
      • -------- --------
      • -------- --------
      • -------- Checks for closest enemy to deal damage to. --------
      • Set VariableSet Abilities_Real02 = 80.00
      • Set VariableSet Abilities_UnitGroup00 = (Units within Abilities_Real02 of Abilities_Loc01.)
      • Unit Group - Pick every unit in Abilities_UnitGroup00 and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Abilities_Bool00 Equal to False
              • ((Picked unit) is A structure) Equal to False
              • ((Picked unit) belongs to an enemy of (Owner of Abilities_CastingUnit).) Equal to True
            • Then - Actions
              • Set VariableSet Abilities_Bool00 = True
              • Unit - Cause Abilities_CastingUnit to damage (Picked unit), dealing 500.00 damage of attack type Spells and damage type Normal
            • Else - Actions
      • Custom script: call DestroyGroup (udg_Abilities_UnitGroup00)
      • -------- --------
      • -------- --------
      • -------- Lower unit back to ground --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between Abilities_Loc00 and Abilities_Loc01) Greater than or equal to (Abilities_Real01 / 2.00)
        • Then - Actions
          • Animation - Change Abilities_CastingUnit flying height to 0.00 at 300.00
        • Else - Actions
          • Do nothing
      • -------- --------
      • -------- --------
      • -------- Finished Leaping --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between Abilities_Loc00 and Abilities_Loc01) Greater than or equal to Abilities_Real01
        • Then - Actions
          • Set VariableSet Abilities_Bool00 = False
          • Unit - Turn collision for Abilities_CastingUnit On.
          • Custom script: call RemoveLocation(udg_Abilities_Loc00)
          • Custom script: call RemoveLocation(udg_Abilities_Loc01)
          • Trigger - Turn off (This trigger)
        • Else - Actions
          • Custom script: call RemoveLocation(udg_Abilities_Loc01)
The ability is working otherwise, jumps just fine to the point. Just does not consistently deal the damage every time. It does way more than life of test dummys, instant killing, but does not always do it even when going right over units. Not even a visible pattern I can make out.
 

Uncle

Warcraft Moderator
Level 71
Joined
Aug 10, 2018
Messages
7,561
So you're going to have problems in your map if you rely on these "Temp" variables between multiple triggers. It's safe to reuse a variable in a lot of cases if you're careful, but when you're dealing with Waits/Periodic Intervals/Timers it's not. These variables when shared can get Set to something else from other triggers, thus breaking everything.

Additionally, there's potential for certain Events to be triggered mid-execution of another trigger. If you're not careful you can easily overwrite a variable even without Waits/etc.

For example, what if during the Leap ANY of the Variables it uses change? What if a player casts another ability that Sets Abilities_Loc00 to somewhere else? Same goes for every other variable like Abilities_Real or Abilities_CastingUnit.

The solution:
Make unique variables for your spell and ensure that only ONE instance of the spell can be active at a time. This means that only ONE unit can cast the Leap spell and the spell's cooldown is longer than any delayed actions that may reference it.

Take advantage of MUI systems like Dynamic Indexing, Unit Indexing, or Hashtables. These are what people use to allow their spells to work for multiple units at a time. Additionally, if you know for a fact that a player can only own and use one of these spellcasters at a time, you can take advantage of Indexing using Player Numbers as the [Index].
 
Last edited:
Level 4
Joined
Apr 16, 2018
Messages
47
So you're going to have problems in your map if you rely on these "Temp" variables between multiple triggers. It's safe to reuse a variable in a lot of cases if you're careful, but when you're dealing with Waits/Periodic Intervals/Timers it's not. These variables when shared can get Set to something else from other triggers, thus breaking everything.

Additionally, there's potential for certain Events to be triggered mid-execution of another trigger. If you're not careful you can easily overwrite a variable even without Waits/etc.

For example, what if during the Leap ANY of the Variables it uses change? What if a player casts another ability that Sets Abilities_Loc00 to somewhere else? Same goes for every other variable like Abilities_Real or Abilities_CastingUnit.
I think I did good considering that actually. After I finished getting it working. I was planning to convert the variables into arrays (Was mid way so figured Id finish it first). Then I could set each variable to an array by player number. Considering each player would only be using one hero it should remove any conflicts. Each person would thus have their own set of these temp variables to use when they cast their spells.

Pretty sure it is the only thing using them atm considering it is the first spell attempt. Was setting feat in the door so I could wrap my mind around the process and then try and make it smoother and fix that for example.

I did make a example my for future purposes of showing skills so I dont post my entire map all time when it actually get more complete instead of a testing bare bones thing it is now. Here it is, but I noticed it also has the same issues even though it is sharing nothing else. It has to have something to do with the spells triggers. xD
 

Attachments

  • Test Spell.w3m
    29.3 KB · Views: 17
Last edited:
Level 4
Joined
Apr 16, 2018
Messages
47
It follows through, but only damages the first unit group of units it collides with.

I think of it as the panther jumping into direction and pouncing forward off the first hit enemys and over any behind them. Knocking down those around it. Not sure if I will take it to that detailed of an extent, but the general idea is that.

It works, except for the flipping inconsistent damaging not always damaging units it goes over. More consistent with single units, less on groups, but not even consistent with the single units...
 
Level 4
Joined
Apr 16, 2018
Messages
47
Have been testing and it seams to be the boolean to stop it from doing damage to more units. Some how that is causing problems, but I can't figure out how or where.

I disabled the condition for checking if its ok to do damage, and it just damages everything it runs through perfectly fine. So I narrowed it down, but no idea how to modify for the desired effect of just hitting the first aoe group with damage and continuing in a line past them. Every combination seems to not work.. Even adding them all into group before removing one by one and THEN dealing damage to each. all methods I do work, but still have that dang in consistent sometimes damaging, sometimes just not at all...


Might I ask of good dummy spells for auras, buffs, positive and negative ones. Something about tracking buffs with auras? idk Trying to learn the base building blocks, and then I can hopefully piece together from there what ever I want. I also heard about dummy units to cast? hmm. Np if dont want on this part I understand, just something I came across and though might be useful soon, but looking up with google got all types of odd answers that werent all that useful or consistent.
 
Last edited:

Uncle

Warcraft Moderator
Level 71
Joined
Aug 10, 2018
Messages
7,561
Sorry for the late response, I set the trigger up to use some techniques that I like for these types of spells. The only thing it's missing is a Pathing Checker to prevent the caster from going out of bounds.

This is using the Dynamic Indexing method which makes it MUI and even allows multiple casts from the same unit to work without issue.

Things I noticed about your original design:
  • Your Pick Every Unit function was only able to damage the first unit hit because of the Boolean. But units were able to get hit multiple times from the same cast if positioned properly.
  • Your periodic interval was causing the spell to look choppy. Every 0.02 seconds looks a lot better when dealing with movement.
  • Your Area of Effect for the damage was 80 which is very small and may fail to get nearby units.
  • You were leaking a Point when using (Position of Ability_Caster) in your Point With Polar Offset function.
  • You had some unnecessary and unusable actions like "Play Unit Walk Animation". This function only works if the unit doesn't get interrupted (Things that interrupt: moving, stopping, anything other than being completely idle).
  • Speaking of interrupting a unit, the Move Unit Instantly function makes the unit issue a "stop" order.
 

Attachments

  • My AOS Uncle1.w3m
    96.5 KB · Views: 17
Last edited:
Level 4
Joined
Apr 16, 2018
Messages
47
Thank you, I will try and study it. Won't use your code unless I can understand it and implement it myself. Have tons of heroes to do, so I will have to learn it proper for myself. I know some sometimes just take the code and use without actually trying to learn from it. xD

Doesn't look too crazy hard, but a little hesistant on some of the custom code stuff.

Also, omgosh yours is like butter. Soo smooth. lol xD
 

Uncle

Warcraft Moderator
Level 71
Joined
Aug 10, 2018
Messages
7,561
  • Custom script: call BlzPauseUnitEx(unit, boolean)
This is a triggered stun. It keeps a counter on the unit to track whether it's stunned or not. +1 stun on true, -1 stun on false. Careful with overusing or misusing this function, a unit could end up permanently stunned. It's safe if you know what you're doing.

  • Custom script: call SetUnitAnimationByIndex(unit, index)
This is basically just the Play Unit Animation function but it won't get interrupted as easily and allows you to choose the EXACT animation you want. Understand that a unit's animations are all numbered from 0 to X and the index IS this number. A tool like Retera's Model Studio makes it easy to get the indexes of animations.

Check out the Dynamic Indexing tutorial in my signature for more information on how this method of indexing works. It's quite simple once you understand how the For Loop works and how it swaps the Indexes in the Loop when the spell is finished in order to keep things working properly.
 
Last edited:
Level 4
Joined
Apr 16, 2018
Messages
47
  • Custom script: call BlzPauseUnitEx(unit, boolean)
This is a triggered stun. It keeps a counter on the unit to track whether it's stunned or not. +1 stun on true, -1 stun on false. Careful with overusing or misusing this function, a unit could end up permanently stunned. It's safe if you know what you're doing.

  • Custom script: call SetUnitAnimationByIndex(unit, index)
This is basically just the Play Unit Animation function but it won't get interrupted as easily and allows you to choose the EXACT animation you want. Understand that a unit's animations are all numbered from 0 to X and the index IS this number. A tool like Retera's Model Studio makes it easy to get the indexes of animations.

Check out the Dynamic Indexing tutorial in my signature for more information on how this method of indexing works. It's quite simple once you understand how the For Loop works and how it swaps the Indexes in the Loop when the spell is finished in order to keep things working properly.
Just having a little trouble wrapping my head around the removing instance from loop part. It is probably super obvious... Why do you set the loop version of all the variables to equal the index? Idk that whole little area I am trying to figure out exactly what it is acomplishing and in which order. hmm. Maybe ill solve it before reply. xD

I thought I had it, for a second but my logic was wrong apparently cause I seen the -1 to index and loop came after? idk darn brain.
 

Uncle

Warcraft Moderator
Level 71
Joined
Aug 10, 2018
Messages
7,561
Just having a little trouble wrapping my head around the removing instance from loop part. It is probably super obvious... Why do you set the loop version of all the variables to equal the index? Idk that whole little area I am trying to figure out exactly what it is acomplishing and in which order. hmm. Maybe ill solve it before reply. xD

I thought I had it, for a second but my logic was wrong apparently cause I seen the -1 to index and loop came after? idk darn brain.
It stumped me at first but it really isn't all that complicated. This link helps a lot: Visualize: Dynamic Indexing

I'll try to explain it as simple as possible here.

First off, let's think about how a Loop works. Let's say we have a Loop going from to 1 to 3. The Loop starts at the minimum value (1) and counts it's way up to the max value (3), running the given Actions once each time. I'm going to call these cycles:
Loop cycle 1: Run actions...
Loop cycle 2: Run actions...
Loop cycle 3: Run actions...

This is useful because not only does it allow us to Run a set of Actions multiple times but it also allows us to take advantage of the cycle #. This cycle # can be used as the [Index] in our Array variables so we can get different results for each cycle. Example: Move Leap_Caster[1], Move Leap_Caster[2], Move Leap_Caster[3], etc.

Note: Relating this to the map I sent you, when I say cycle # I'm referring to the Leap_Loop variable.

Now what happens if Loop cycle 2 were to finish before 1 and 3? How could we remove it from the Loop effectively? We know that we need to do this in a way so that the next in line cycles (3 for example) will still get a chance to run their actions. So what do we do?

Simple, when Loop cycle 2 is finished, we find our last cycle in the Loop (3 in this case). Then we replace Loop cycle 2 with that last cycle. This means that all of Loop cycle 2's variable arrays (Leap_Caster[X], Leap_Source[X], Leap_Angle[X], etc.) get their values Set to Loop cycle 3's associated variables.
In other words, Loop cycle 3 becomes the new Loop cycle 2.

Understand that Loop cycle 3 STILL exists and has all of it's original variables, it's just that it has also overtaken Loop cycle 2. Now why did we do that? Because now we've effectively removed the original Loop cycle 2 from our Loop. It's variables have lost their original references and gained new ones.

That being said, now we basically have TWO Loop cycle 3's (cycle 2 and 3), so how do we prevent them from both running their actions? Simple, we shorten our Loop by 1 (subtract 1 from Leap_Index).

After doing that our Loop will now be running from 1 to 2. 1 still represents the original cycle 1 from the beginning but 2 now represents cycle 3, and cycle 2 has been effectively removed from the Loop.

Lastly, when we shorten the Loop by 1 (subtract 1 from Leap_Index), we also have to push the Loop back a cycle (subtract 1 from Leap_Loop). This way it repeats cycle 2 again, but remember, cycle 2 is now cycle 3 so everything is running as intended.

Hopefully that all makes sense.
 
Last edited:
Level 4
Joined
Apr 16, 2018
Messages
47
holy... This, although I think I got, it is a mind bender. Like some type of super over convoluted time travel movie or something. lol xD

So if it is more than just 3 variables lets say a index of 5, and loop at 3.
(12345)


you would both start the 2nd loop again and changed the order of 5 from the back to where the 3 was, changing the order a little?
Index - 4
loop - 2
(1254)

Ohh, so order is changed yes, but the loop has not ended even though you set it to 2, so when you finally do finish that loop it starters the previous 5 that is now in place of original 3, followed by 4 which both haven't been done yet, but only difference being that they are done in a different order. 5 being ahead of the 4 now. I am assuming of which is not a big deal.
 

Uncle

Warcraft Moderator
Level 71
Joined
Aug 10, 2018
Messages
7,561
Sounds like you've got it figured out.

Correct, the order of how these things execute is of no concern, all we care about is that everything in the Loop runs exactly one time and that completely finished cycles are replaced (basically removed).
 
Last edited:
Status
Not open for further replies.
Top