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

Weird Trigger Issue where Base Attack Doubles due to Chaos Ability?

Level 3
Joined
May 24, 2016
Messages
22
Okay, so I'm making a trigger based ability whereby a unit can change stance. Essentially, the character can change to have any damage type (with more mana spent to get to chaos than other types).

In order to do that, I'm using the Chaos ability, whereby each switch triggers giving the unit a chaos ability linking to a unit that is the exact same except for a different damage type.

Sure, I know there are probably issues for permanent stat gains, etc., which I will introduce to the trigger (by saving stat gains, etc., hopefully). But that's a future issue. Right now I've got a different issue.

So, the ability works, but only on the first time through. Even then, sometimes it gets stuck and oscillates between being magic type damage and the others. Sometimes, the ability gets stuck, and when it gets stuck, it's permanently stuck on that attack type. When stuck, it's always magic type. So...not exactly sure why that is either.

Second, sometimes when it's stuck it will do one of two things: (1. it will either cause infinite doubling of the base attack damage (but not damage interval) when the ability used or (2. it will cause infinite doubling of the armor when the ability is used.

So the two problems: the loops gets stuck at UseCount=7 or greater and the unit gets stuck on magic type, it seems (not sure why). Second, the unit's damage and armor double infinitely despite nothing about armor or damage in the triggers.

Can anybody help me understand why? For ease, I've posted the triggers below.

  • Events
    • Map initialization
  • Conditions
  • Actions
    • Set VariableSet ChangingTactCooldown[1] = 60.00
    • Set VariableSet ChangingTactCooldown[2] = 50.00
    • Set VariableSet ChangingTactCooldown[3] = 40.00
    • Set VariableSet ChangingTactCooldown[4] = 30.00
    • Set VariableSet ChangingTactCooldown[5] = 20.00
    • Set VariableSet ChangingTactCooldown[6] = 10.00
    • Set VariableSet ChangingTactCDWaitBefore[1] = 6.00
    • Set VariableSet ChangingTactCDWaitBefore[2] = 6.00
    • Set VariableSet ChangingTactCDWaitBefore[3] = 6.00
    • Set VariableSet ChangingTactCDWaitBefore[4] = 6.00
    • Set VariableSet ChangingTactCDWaitBefore[5] = 6.00
    • Set VariableSet ChangingTactCDWaitBefore[6] = 6.00
    • Set VariableSet ChangingTactStanceOrder[1] = Grace Mercy Stance (Magic)
    • Set VariableSet ChangingTactStanceOrder[2] = Grace Mercy Stance (Normal)
    • Set VariableSet ChangingTactStanceOrder[3] = Grace Mercy Stance (Piercing)
    • Set VariableSet ChangingTactStanceOrder[4] = Grace Mercy Stance (Siege)
    • Set VariableSet ChangingTactStanceOrder[5] = Grace Mercy Stance (Spells)
    • Set VariableSet ChangingTactStanceOrder[6] = Grace Mercy Stance (Chaos)
    • Set VariableSet ChangingTactStanceOrder[7] = Grace Mercy Stance (Hero)
    • Set VariableSet ChangingTactStanceOrder[0] = Grace Mercy Stance (Hero)
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to Changing Tact
  • Actions
    • Set VariableSet ChangingTactIndex = (ChangingTactIndex + 1)
    • Set VariableSet ChangingTactUser[ChangingTactIndex] = (Triggering unit)
    • Set VariableSet ChangingTactUserLevel[ChangingTactIndex] = (Level of Changing Tact for ChangingTactUser[ChangingTactIndex])
    • For each (Integer ChangingTactPreviousUseLoop) from 1 to (ChangingTactIndex - 1), do (Actions)
      • Loop - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • ChangingTactUser[ChangingTactPreviousUseLoop] Equal to ChangingTactUser[ChangingTactIndex]
            • ChangingTactIndex Greater than 1
          • Then - Actions
            • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              • If - Conditions
                • ChangingTactUseCount[ChangingTactPreviousUseLoop] Less than 6
              • Then - Actions
                • Set VariableSet ChangingTactUseCount[ChangingTactPreviousUseLoop] = (ChangingTactUseCount[ChangingTactPreviousUseLoop] + 1)
                • Unit - Remove ChangingTactStanceOrder[(ChangingTactUseCount[ChangingTactPreviousUseLoop] - 1)] from ChangingTactUser[ChangingTactPreviousUseLoop]
                • Unit - Add ChangingTactStanceOrder[ChangingTactUseCount[ChangingTactPreviousUseLoop]] to ChangingTactUser[ChangingTactPreviousUseLoop]
                • Game - Display to (All players) the text: (String(ChangingTactUseCount[ChangingTactPreviousUseLoop]))
              • Else - Actions
                • Set VariableSet ChangingTactUseCount[ChangingTactPreviousUseLoop] = (ChangingTactUseCount[ChangingTactPreviousUseLoop] + 1)
                • Unit - Remove ChangingTactStanceOrder[(ChangingTactUseCount[ChangingTactPreviousUseLoop] - 1)] from ChangingTactUser[ChangingTactPreviousUseLoop]
                • Unit - Add ChangingTactStanceOrder[ChangingTactUseCount[ChangingTactPreviousUseLoop]] to ChangingTactUser[ChangingTactPreviousUseLoop]
                • Unit - For Unit ChangingTactUser[ChangingTactPreviousUseLoop], start cooldown of ability Changing Tact " over "ChangingTactCooldown[ChangingTactUserLevel[ChangingTactPreviousUseLoop]] seconds.
                • Game - Display to (All players) the text: We hit 7 times
            • Set VariableSet ChangingTactUser[ChangingTactIndex] = No unit
            • Set VariableSet ChangingTactIndex = (ChangingTactIndex - 1)
          • Else - Actions
            • Set VariableSet ChangingTactUseCount[ChangingTactIndex] = 1
            • Unit - Remove ChangingTactStanceOrder[0] from ChangingTactUser[ChangingTactIndex]
            • Unit - Add ChangingTactStanceOrder[1] to ChangingTactUser[ChangingTactIndex]
            • Set VariableSet ChangingTactTimer[ChangingTactIndex] = 0.00
            • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              • If - Conditions
                • ChangingTactIndex Equal to 1
              • Then - Actions
                • Trigger - Turn on ChangingTactLoop <gen>
              • Else - Actions
  • ChangingTactLoop
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • For each (Integer ChangingTactTimerLoop) from 1 to ChangingTactIndex, do (Actions)
        • Loop - Actions
          • Set VariableSet ChangingTactTimer[ChangingTactTimerLoop] = (ChangingTactTimer[ChangingTactTimerLoop] + 0.05)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ChangingTactTimer[ChangingTactTimerLoop] Greater than or equal to ChangingTactCooldown[ChangingTactUserLevel[ChangingTactTimerLoop]]
            • Then - Actions
              • Unit - Remove ChangingTactStanceOrder[ChangingTactUseCount[ChangingTactTimerLoop]] from ChangingTactUser[ChangingTactTimerLoop]
              • Unit - Add ChangingTactStanceOrder[0] to ChangingTactUser[ChangingTactTimerLoop]
              • Unit - For Unit ChangingTactUser[ChangingTactTimerLoop], end cooldown of ability Changing Tact
              • Set VariableSet ChangingTactUser[ChangingTactTimerLoop] = ChangingTactUser[ChangingTactIndex]
              • Set VariableSet ChangingTactUserLevel[ChangingTactTimerLoop] = ChangingTactUserLevel[ChangingTactIndex]
              • Set VariableSet ChangingTactUseCount[ChangingTactTimerLoop] = ChangingTactUseCount[ChangingTactIndex]
              • Set VariableSet ChangingTactTimer[ChangingTactTimerLoop] = ChangingTactTimer[ChangingTactIndex]
              • Set VariableSet ChangingTactUser[ChangingTactIndex] = No unit
              • Set VariableSet ChangingTactUseCount[ChangingTactIndex] = 0
              • Set VariableSet ChangingTactIndex = (ChangingTactIndex - 1)
              • Set VariableSet ChangingTactTimerLoop = (ChangingTactTimerLoop - 1)
            • Else - Actions
              • -------- I've turned this off until I've located the issue, as it really just makes it longer to test. --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ChangingTactTimer[ChangingTactTimerLoop] Greater than or equal to ChangingTactCDWaitBefore[ChangingTactUserLevel[ChangingTactTimerLoop]]
                • Then - Actions
                  • Unit - For Unit ChangingTactUser[ChangingTactTimerLoop], start cooldown of ability Changing Tact " over "(ChangingTactCooldown[ChangingTactUserLevel[ChangingTactTimerLoop]] - ChangingTactCDWaitBefore[ChangingTactUserLevel[ChangingTactTimerLoop]]) seconds.
                • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ChangingTactIndex Equal to 0
        • Then - Actions
          • Trigger - Turn off (This trigger)
        • Else - Actions
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
I recommend changing unit type using the bear form trick rather than chaos ability. The bear form trick works due to removing the bear form ability being able to permanently change the unit type of a unit to the base form specified by the ability.

I forget the exact details of the trick, but it is along the lines of using the alternative form being the type of unit you want to go from and the base form being the type of unit you want to go to. Use triggers to add and then remove the ability and the hero will have changed type, permanently.

With reforged or WC3CE there might be natives you can use to change unit attack types or unit types directly, without having to resort to hacky tricks.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,580
There's methods for changing the attack type via the new 1.31+ natives, however, my memory is a bit fuzzy on how to do it exactly.

I know it involved this function, but there was some other steps to get it to update properly:
  • Unit - Set Unit: (Triggering unit)'s Weapon Integer Field: Attack Attack Type ('ua1t')at Index:0 to Value: 1
I'll try to mess around with it and see if I can get it to work. Maybe they fixed it recently?

Edit:

It works perfectly, what a surprise:
  • Attack Switch
    • Events
      • Time - Every 1.50 seconds of game time
    • Conditions
    • Actions
      • Unit - Set Unit: Paladin 0001 <gen>'s Weapon Integer Field: Attack Attack Type ('ua1t')at Index:0 to Value: AttackIndex
      • Set VariableSet AttackIndex = (AttackIndex + 1)
Edit: I posted the wrong weapon type indexes. Here's the correct list:

1 = normal
2 = pierce
3 = siege
4 = magic
5 = chaos
6 = hero
7+ = hero (or I guess it just doesn't change)

Remember that Index:0 is referring to Weapon 1. Index:1 would refer to Weapon 2.
 
Last edited:
Level 3
Joined
May 24, 2016
Messages
22
There's methods for changing the attack type via the new 1.31+ natives, however, my memory is a bit fuzzy on how to do it exactly.

I know it involved this function, but there was some other steps to get it to update properly:
  • Unit - Set Unit: (Triggering unit)'s Weapon Integer Field: Attack Attack Type ('ua1t')at Index:0 to Value: 1
I'll try to mess around with it and see if I can get it to work. Maybe they fixed it recently?

Edit:

It works perfectly, what a surprise:
  • Attack Switch
    • Events
      • Time - Every 1.50 seconds of game time
    • Conditions
    • Actions
      • Unit - Set Unit: Paladin 0001 <gen>'s Weapon Integer Field: Attack Attack Type ('ua1t')at Index:0 to Value: AttackIndex
      • Set VariableSet AttackIndex = (AttackIndex + 1)
0 = None
1 = Normal
2 = Pierce
3 = Siege
4 = Spells
5 = Chaos
6 = Magic
7 = Hero

Remember that Index:0 is referring to Weapon 1. Index:1 would refer to Weapon 2.
Ok, going to try implementing that here now. It seems like all I have to do is replace the appropriate ability based code with this.

For what it's worth, is there any immediately obvious issue with what I've done above that is going to be wonky? (For example, I for the life of me don't know why the former trigger got stuck on a permanent use count of 7+, but it did, so just trying to make sure it's the chaos ability doing it and not the trigger itself)?
 
Level 3
Joined
May 24, 2016
Messages
22
Chaos will most likely have weird results when changing the Unit more than once. The triggers themselves seem a bit convoluted to me, maybe someone else can explain the reasoning behind the 7+ issue.

How exactly is the spell supposed to function?
Basically, you can use the ability. It changes attack type. You can click a number of times, and the cooldown starts after so long of being not using the ability (that's the disabled part of the timer loop). Alternatively, if you switch all the way back to the initial attack type, it immediately puts it on cooldown.

Each use cycles through the ordered attack types. (In my case, I've arbitrarily done it as Hero, Magic, Normal, Piercing, Siege, Spells, Chaos, back to Hero).

After a duration (currently just using the cooldown but setting up a separate duration), the unit is then switched back to the original attack type.

That's at least my intention. But, as I said, ran into the weirdness while testing--and since I wasn't messing with armor or base attack damage, had no idea what was going on.



EDIT: Also, using what you've given, it is now switching properly-ish. Spell-type damage doesn't show up, but it is actually spelltype I think based on the damage being dealt. It's still permanent for some reason (as in the damage type doesn't change to the original at the end of the spell), but I think it's not being careful watching my variables as I did the replacement.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,580
I realized my list of Attack Types was wrong. Here's the correct list:
1 = Normal
2 = Pierce
3 = Siege
4 = Magic
5 = Chaos
6 = Hero

There is no Spell attack type as far as I can tell. Maybe it's 0, I didn't test it, but I assume that's None.

Anyway, I attached a map with a working example of your spell using Unit Indexing rather than Dynamic Indexing. I'm also using a GUI Timer system that I created which allows me to have greater control over Timers in GUI. It should be completely MUI. The only issue I can think of is what happens when the unit dies while the timer is still running. It's probably fine though.
 

Attachments

  • Changing Tact Uncle 1.w3m
    37.2 KB · Views: 1
Last edited:
Level 3
Joined
May 24, 2016
Messages
22
I realized my list of Attack Types was wrong. Here's the correct list:
1 = Normal
2 = Pierce
3 = Siege
4 = Magic
5 = Chaos
6 = Hero

There is no Spell attack type as far as I can tell. Maybe it's 0, I didn't test it, but I assume that's None.

Anyway, I attached a map with a working example of your spell using Unit Indexing rather than Dynamic Indexing. I'm also using a GUI Timer system that I created which allows me to have greater control over Timers in GUI. It should be completely MUI. The only issue I can think of is what happens when the unit dies while the timer is still running. It's probably fine though.
You, sir, are a wizard!


First off--thanks so much! Second, I'll be honest, I have no programming background, so this is my first experience with Unit Indexing. I'm not entirely sure what I'm looking at, though I'm trying to put together everything to understand it. I'm going to have to do my research. 🙂

And I'm still trying to put together what I did wrong in my triggering. Which, I got sort of working (except that it couldn't recognize the first use and required 2 uses to cycle back to the beginning).


Just for my edification, I assume there are benefits to unit indexing vs. dynamic? And is there a particular reason dynamic indexing is less suited (I ask because that's the only system I actually kind of understand, though I know I've seen hashtable systems too)?

EDIT: Actually, when trying to implement the Unit Indexing system and GST system---I get compiling errors....And being that I have no coding background, I can't even begin to interpret. Thanks still--I'm hoping I'll be able to read through enough to understand (and you've given me one amazing resource for learning).
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,580
Dynamic Indexing is useful for advanced triggers and allows you to create very flexible effects. For example, you could make a 0 second cd spell which fires a slow moving missile over 10 seconds. With dynamic indexing you could have ANY number of units with this spell all casting the spell over and over again creating multiple missiles per unit, and dynamic indexing would handle it all flawlessly.

But not every spell should function this way, for example, when you cast Pit Lord's Howl of Terror it applies a buff to nearby enemy units. If you were to to cast Howl of Terror on those same units again, the new instance of the spell would replace the previous instance of the spell (assuming that the units all still have the buff). In that case, Dynamic Indexing doesn't really make sense since we're replacing data rather than creating multiple instances of data. Instead, you can rely on Unit Indexing for a spell like Howl of Terror since it handles this desired effect by design. I'll go into greater detail about the method below.

Note that these two methods aren't mutually exclusive, I often use Dynamic Indexing and Unit Indexing together.

Unit Indexing lets you attach data to a Unit. It's a REALLY simple method that revolves around the use of this action:
  • Unit - Set the custom value of (Some unit) to X
X is an Integer of your choosing.

Blizzard provided us with a single Custom Value per unit which we can use to represent some kind of custom data for that unit. For example, Custom Value could be used to represent a 4th Hero Attribute similar to Str/Agi/Int. However, having only one Custom Value per unit is pretty limited. What if for instance you wanted to store multiple sets of data per unit?

That's where the Unit Indexer system comes into play. It's a system that takes advantage of this action in order to store as much data as we'd like.

How it works:
Each pre-placed Unit as well as ALL future Units (trained, purchased, created, etc) will be given a unique Custom Value as they come into play using the above mentioned action. This means that no two Units will EVER share the same Custom Value.

Why is this useful? Because now our Unit's custom value represents a unique [index] in any variable Array that you wish to use with them.

For example, let's store 2 new Attributes to our Heroes and give them a stat for scaling the power of their spells:
  • Unit - Create 1 Paladin...
  • Set Variable CV = (Custom value of (Last created unit))
  • Set Variable Luck[CV] = 10
  • Set Variable Charisma[CV] = 20
  • Set Variable Spell_Power[CV] = 30.00
  • Unit - Create 1 Archmage...
  • Set Variable CV = (Custom value of (Last created unit))
  • Set Variable Luck[CV] = 50
  • Set Variable Charisma[CV] = 100
  • Set Variable Spell_Power[CV] = 150.00
Our Paladin that was just created has a Luck stat of 10, Charisma stat of 20, and Spell_Power stat of 30.00 (note that it's a Real instead of an Integer).
Our Archmage that was just created has a Luck stat of 50, Charisma stat of 100, and Spell_Power stat of 150.00.

You can use this method to store data of ANY variable type, not just Integers and Reals.

Assuming that the Paladin and Archmage are the only two units active in our map then the Paladin would be assigned a Custom Value of 1 and the Archmage would be assigned a Custom Value of 2 by the Unit Indexer system. If a third unit were to be created it would be assigned a Custom Value of 3. This pattern continues on and on. Also, when Units are removed from the game their Custom Value gets recycled, meaning it becomes available again and can be given to future Units. This basically guarantees that you'll never hit the [index] limit of 32,768 that all Arrays have.

Now as long as we have a reference to those Units we can also get their Custom Value, which then gets us ALL of our Variable Arrays associated with them.

For example, let's take advantage of our Spell_Power array and make our Paladin deal bonus spell damage:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to Holy Light
    • (Target unit of ability being cast) is Undead Equal to True
  • Actions
    • Unit - Cause (Casting unit) to damage (Target unit of ability being cast), dealing Spell_Power[(Custom value of (Casting unit))] damage of attack type Spells and damage type Normal
Now when our Paladin casts Holy Light on an enemy unit it deals additional damage equal to his Spell_Power (30.00 in this case).

Now let's say each time he casts Holy Light he gains a permanent +30.00 to his Spell_Power, easy enough:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to Holy Light
    • (Target unit of ability being cast) is Undead Equal to True
  • Actions
    • Set Variable CV = (Custom value of (Casting unit))
    • Unit - Cause (Casting unit) to damage (Target unit of ability being cast), dealing Spell_Power[CV] damage of attack type Spells and damage type Normal
    • Set Variable Spell_Power[CV] = (Spell_Power[CV] + 30.00)
Note how I introduced the CV integer variable into the mix. This is used to help keep things clean, fast, and organized. But it's NOT necessary! As you can see in my previous example you can reference the Custom Value directly, although I think using a variable is far more convenient.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,580
The reason you have compilation errors is because you need to enable JassHelper (there's a menu for it in the Trigger Editor). It should already be on by default but one of the latest patches disabled it for no good reason. Basically, the current Wc3 dev(s) don't know what they're doing.

Also, even without a coding background, the errors you receive in there should actually be rather self-explanatory. It'll say things like "This variable name doesn't exist, maybe you meant X?", which means that you wrote the name of your variable wrong in either Custom Script or one of your Jass/Lua scripts. In your case it would most likely be Custom Script since you don't code.
 
Last edited:
Level 3
Joined
May 24, 2016
Messages
22
The reason you have compilation errors is because you need to enable JassHelper (there's a menu for it in the Trigger Editor). It should already be on by default but one of the latest patches disabled it for no good reason. Basically, the current Wc3 dev(s) don't know what they're doing.

Also, even without a coding background, the errors you receive in there should actually be rather self-explanatory. It'll say things like "This variable name doesn't exist, maybe you meant X?", which means that you wrote the name of your variable wrong in either Custom Script or one of your Jass/Lua scripts. In your case it would most likely be Custom Script since you don't code.
Realized I forgot to post. So, like you said, JassHelper was disabled between saves. After re-enabling everything works perfectly (well, I had something interfering that I removed--it was a same named variable essentially. But all's well that ends well. Everything is in tip-top and I'm starting my foray into tryin unit indexing.

As I said before, you are a godsend. Really appreciate all the help and the generosity with your time.
 
Top