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

Special Effect not disappearing when cast simultaneously

Status
Not open for further replies.
Level 3
Joined
Feb 15, 2015
Messages
26
Hi all! I'm having problems with my spell. Basically, what it does is creating a circle for 1.5 seconds. After 1.5 seconds, the circle disappears and a cage forms, blocking enemy units from escaping (allies CAN still walk in and out like normal) for a while.

I'm trying to make this spell MUI using arrays variables (This spell does require Bribe's Unit Indexer). It seems so at first: The cage still forms like normal, enemy units of the casters are still unable to leave for the duration of the spell, just one problem: the special effects won't "MUI"-ly disappear. If 2 units cast the spell at the same time, the circle and the cage part only disappear for the first unit. For the other one, the circle and the cage effect persists indefinitely, even though the spell's duration has run out and can no longer hold the enemy units. I know that my special effect coding is the cause, yet I don't really know how to fix it. I have attached the triggers below.

Trigger 1: Forming The Circle

  • Ice Wall Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Ice Wall
    • Actions
      • Unit Group - Add (Triggering unit) to IceWallCasterGroup
      • Set IceWallCastPoint[(Custom value of (Triggering unit))] = (Target point of ability being cast)
      • Set IceWallDuration[(Custom value of (Triggering unit))] = 50.00
      • For each (Integer IceWallInstances[(Custom value of (Triggering unit))]) from 1 to 30, do (Actions)
        • Loop - Actions
          • Set IceWallPreAngle = (IceWallPreAngle + (360.00 / (Real(IceWallInstances[(Custom value of (Triggering unit))]))))
          • Set IceWallPreEffectPoint[IceWallInstances[(Custom value of (Triggering unit))]] = (IceWallCastPoint[(Custom value of (Triggering unit))] offset by 450.00 towards IceWallPreAngle degrees)
          • Special Effect - Create a special effect at IceWallPreEffectPoint[IceWallInstances[(Custom value of (Triggering unit))]] using BasicWaterFlash.mdx
          • Set IceWallPreEffect[IceWallInstances[(Custom value of (Triggering unit))]] = (Last created special effect)
          • Custom script: call RemoveLocation (udg_IceWallPreEffectPoint[udg_IceWallInstances[GetUnitUserData(GetTriggerUnit())]])
Trigger 2: Removing The Circle And Forming The Wall (or Cage)

  • Ice Wall Init
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
      • (Number of units in IceWallCasterGroup) Greater than 0
    • Actions
      • Unit Group - Pick every unit in IceWallCasterGroup and do (Actions)
        • Loop - Actions
          • Set IceWallCasterCV = (Custom value of (Picked unit))
          • Set IceWallDuration[IceWallCasterCV] = (IceWallDuration[IceWallCasterCV] - 1.00)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • IceWallDuration[IceWallCasterCV] Equal to 0.00
            • Then - Actions
              • Unit Group - Add (Picked unit) to IceWallDummyGroup
              • Unit - Create 1 Dummy for (Owner of (Picked unit)) at IceWallCastPoint[IceWallCasterCV] facing Default building facing degrees
              • Unit - Add a (1.40 + (0.40 x (Real((Level of Ice Wall for (Picked unit)))))) second Generic expiration timer to (Last created unit)
              • Set IceWallMarker[IceWallCasterCV] = (Last created unit)
              • For each (Integer IceWallLoop[IceWallCasterCV]) from 1 to 30, do (Actions)
                • Loop - Actions
                  • Special Effect - Destroy IceWallPreEffect[IceWallLoop[IceWallCasterCV]]
                  • Set IceWallAngle = (IceWallAngle + (360.00 / (Real(IceWallLoop[IceWallCasterCV]))))
                  • Set IceWallEffectPoint[IceWallLoop[IceWallCasterCV]] = (IceWallCastPoint[IceWallCasterCV] offset by 450.00 towards IceWallAngle degrees)
                  • Special Effect - Create a special effect at IceWallEffectPoint[IceWallLoop[IceWallCasterCV]] using Abilities\Spells\Undead\FreezingBreath\FreezingBreathTargetArt.mdl
                  • Set IceWallEffect[IceWallLoop[IceWallCasterCV]] = (Last created special effect)
                  • Custom script: call RemoveLocation (udg_IceWallEffectPoint[udg_IceWallLoop[udg_IceWallCasterCV]])
              • Custom script: call RemoveLocation (udg_IceWallCastPoint[udg_IceWallCasterCV])
            • Else - Actions
Trigger 3: Periodic Check To See If The Wall Still Persists To Know That Enemy Units should or should not be locked.

  • Ice Wall Exec
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
      • (Number of units in IceWallDummyGroup) Greater than 0
    • Actions
      • Unit Group - Pick every unit in IceWallDummyGroup and do (Actions)
        • Loop - Actions
          • Set IceWallCV = (Custom value of (Picked unit))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (IceWallMarker[IceWallCV] is alive) Equal to True
            • Then - Actions
              • Set IceWallCaster[IceWallCV] = (Picked unit)
              • Set IceWallTempPoint = (Position of IceWallMarker[IceWallCV])
              • Custom script: set bj_wantDestroyGroup = true
              • Unit Group - Pick every unit in (Units within 350.00 of IceWallTempPoint) and do (Actions)
                • Loop - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • ((Picked unit) is A structure) Equal to False
                      • ((Picked unit) is Magic Immune) Equal to False
                      • ((Picked unit) is hidden) Equal to False
                      • ((Picked unit) is alive) Equal to True
                      • ((Picked unit) belongs to an enemy of (Owner of IceWallCaster[IceWallCV])) Equal to True
                    • Then - Actions
                      • Set IceWallTargetPoint = (Position of (Picked unit))
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Distance between IceWallTempPoint and IceWallTargetPoint) Greater than 250.00
                        • Then - Actions
                          • Set IceWallOuterPoint = (IceWallTargetPoint offset by 25.00 towards (Angle from IceWallTargetPoint to IceWallTempPoint) degrees)
                          • Custom script: call SetUnitX (GetEnumUnit(), GetLocationX(udg_IceWallOuterPoint))
                          • Custom script: call SetUnitY (GetEnumUnit(), GetLocationY(udg_IceWallOuterPoint))
                          • Custom script: call RemoveLocation (udg_IceWallOuterPoint)
                        • Else - Actions
                    • Else - Actions
              • Custom script: call RemoveLocation (udg_IceWallTempPoint)
            • Else - Actions
              • For each (Integer IceWallLoop2[IceWallCV]) from 1 to 30, do (Actions)
                • Loop - Actions
                  • Special Effect - Destroy IceWallEffect[IceWallLoop2[IceWallCV]]
              • Unit Group - Remove IceWallCaster[IceWallCV] from IceWallDummyGroup
              • Unit Group - Remove IceWallMarker[IceWallCV] from IceWallDummyGroup
Please share your suggestions on how I can overcome this and maybe a method so that I don't run into this problem again in the future. Any help is greatly appreciated. A small disclaimer that I only learn GUI MUI coding through some spells, so my code can look pretty messy. I'm really sorry about that.
 
Level 39
Joined
Feb 27, 2007
Messages
5,013
These won't fix your problem, but some tips:
  • Visualize: Dynamic Indexing
  • Store "Custom Value of (Triggering Unit)" into a temp integer so you don't have to compute it 212 (yes actually, 6*30+30+2) different times when the spell is cast.
  • You usually never need to have an indexing method that goes 2 arrays deep (variable[variable[Something]]). Did you set it up this way because you want the caster to have multiple simultaneous casts of Ice Wall active at once?
  • I would use 2 different dynamic index lists: one for the wall as its forming and one for the walls that are active. Once a wall is fully formed you can migrate its data from 1 list to the other.
 
Level 3
Joined
Feb 15, 2015
Messages
26
These won't fix your problem, but some tips:
  • Visualize: Dynamic Indexing
  • Store "Custom Value of (Triggering Unit)" into a temp integer so you don't have to compute it 212 (yes actually, 6*30+30+2) different times when the spell is cast.
  • You usually never need to have an indexing method that goes 2 arrays deep (variable[variable[Something]]). Did you set it up this way because you want the caster to have multiple simultaneous casts of Ice Wall active at once?
  • I would use 2 different dynamic index lists: one for the wall as its forming and one for the walls that are active. Once a wall is fully formed you can migrate its data from 1 list to the other.

  • Thanks. I will try it out
  • Will it cause the spell to not MUI? I don't know what happened to my WE but whenever I try to store Custom Value of a Unit into a variable, a spell stops being MUI, so I have to use Custom Value of ... directly.
  • Yes. Like the caster can have 2 walls persists with different durations.
  • Can you give me a little bit of idea on how you migrate the data?
 
Level 39
Joined
Feb 27, 2007
Messages
5,013
I'd wager that it wasn't bugging out when you tried to save the custom value, but rather you were doing something incorrect. I've never seen behavior where assigning an integer variable would fail.

Regarding data migration: I realized that this might not be necessary. You could instead store a boolean (or integer) variable that tells the trigger which 'phase' of the spell a particular instance is in. The trigger reads that variable and if in set-up phase does A and in wall-phase does B. That aside, here's an answer to your question: using that tutorial as an example, this occurs when the Blood Mage cast 'ends' and we would want to migrate its data to the next phase. Instead of overwriting the SL_Loop_Integer'th instance with the SL_Index'th one, first copy the relevant data to the second set of arrays and then overwrite the old data:

  • Set SL2_Index = (SL_Index2 + 1)
  • Set SL2_Caster[SL2_Index] = Set SL_Caster[SL_Loop_Integer]
  • Set SL2_Target[SL2_Index] = SL_Target[SL_Loop_Integer]
  • Set SL2_Counter[SL2_Index] = <compute the appropriate duration here>
  • -------- --------
  • Set SL_Caster[SL_Loop_Integer] = SL_Caster[SL_Index]
  • Set SL_Target[SL_Loop_Integer] = SL_Target[SL_Index]
  • Set SL_Counter[SL_Loop_Integer] = SL_Counter[SL_Index]
  • Set SL_Index = (SL_Index - 1)
  • Set SL_Loop_Integer = (SL_Loop_Integer - 1)
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
The problem is in Ice Wall Cast. You are not assigning the special effects to unique indices. Instead you overwrite index 1 to 30 each time.

What you want is to turn the special effect array into a pseudo 2D array of size Nx30. One can then place the special effects in order at IceWallPreEffect[unit custom value][1 to 30]. This makes sure each special effect gets a unique storage space.

Since multi dimensional arrays are not natively supported, one must emulate them by fragmenting a 1D array with some mathematics. In this case the index into a 1D array becomes [(unit custom value) * 30 + (1 to 30)]. Be aware that JASS arrays have a finite size of 32,768 and by fragmenting like this it means that a maximum custom unit value of 1092 is supported which might run into difficulties if the map has over a thousand units on it at any time.

If using Lua one can bundle all effects into their own table with the table operating in list mode. This table can then be stored with a single mapping in any other table. Use of a unit index system is also entirely pointless in Lua since one can use a table with weak keys to map units (usertype) to data.
 
Level 3
Joined
Feb 15, 2015
Messages
26
The problem is in Ice Wall Cast. You are not assigning the special effects to unique indices. Instead you overwrite index 1 to 30 each time.

What you want is to turn the special effect array into a pseudo 2D array of size Nx30. One can then place the special effects in order at IceWallPreEffect[unit custom value][1 to 30]. This makes sure each special effect gets a unique storage space.

Since multi dimensional arrays are not natively supported, one must emulate them by fragmenting a 1D array with some mathematics. In this case the index into a 1D array becomes [(unit custom value) * 30 + (1 to 30)]. Be aware that JASS arrays have a finite size of 32,768 and by fragmenting like this it means that a maximum custom unit value of 1092 is supported which might run into difficulties if the map has over a thousand units on it at any time.

If using Lua one can bundle all effects into their own table with the table operating in list mode. This table can then be stored with a single mapping in any other table. Use of a unit index system is also entirely pointless in Lua since one can use a table with weak keys to map units (usertype) to data.

I see. I never knew that multi dimensional arrays aren't supported natively. I suppose that I can't get away with Bribe's Unit Indexer with this spell because of that limit and have to find another indexing method, is that right? Also, can I store this value [(unit custom value) * 30 + (1 to 30)] as a global integer variable within the loop? Thanks
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
I suppose that I can't get away with Bribe's Unit Indexer with this spell because of that limit and have to find another indexing method, is that right? Also, can I store this value [(unit custom value) * 30 + (1 to 30)] as a global integer variable within the loop? Thanks
As I said...
Since multi dimensional arrays are not natively supported, one must emulate them by fragmenting a 1D array with some mathematics. In this case the index into a 1D array becomes [(unit custom value) * 30 + (1 to 30)]. Be aware that JASS arrays have a finite size of 32,768 and by fragmenting like this it means that a maximum custom unit value of 1092 is supported which might run into difficulties if the map has over a thousand units on it at any time.

If this limit of 1092 elements is a concern you could use some sort of hashtable based structure to track the effects, or move to Lua where you have access to full and unbounded 32bit Lua where one can create tables and use them in list mode for this kind of thing as required.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
As I said...


If this limit of 1092 elements is a concern you could use some sort of hashtable based structure to track the effects, or move to Lua where you have access to full and unbounded 32bit Lua where one can create tables and use them in list mode for this kind of thing as required.
With how bogged down the engine performance is with the new update, I doubt 1000+ unit maps will be realistic any longer.
 
Status
Not open for further replies.
Top