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

Projectile spell - Periodic event + local variables

Status
Not open for further replies.
Level 8
Joined
Apr 30, 2009
Messages
338
I have finally coded a projectile spell on my test map. I see no way to do it in GUI without using 2 triggers: one to assign the variables and then another one to make the projectile move towards the target every 0.02 seconds:

  • fireball1
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Fireball
    • Actions
      • Set fireball_target = (Target unit of ability being cast)
      • Set fireball_caster = (Casting unit)
      • Set fireball_damage = ((0.00 + (75.00 x (Real((Level of (Ability being cast) for fireball_caster))))) + ((0.60 + (0.40 x (Real((Level of (Ability being cast) for fireball_caster))))) x (Real((Intelligence of fireball_caster (Include bonuses))))))
      • Unit - Create 1 projectile for (Owner of fireball_caster) at ((Position of fireball_caster) offset by 32.00 towards (Angle from (Position of fireball_caster) to (Position of fireball_target)) degrees) facing (Position of fireball_target)
      • Set fireball_projectile = (Last created unit)
      • Unit - Turn collision for fireball_projectile Off
      • Set fireball_position = (Position of fireball_projectile)
      • Trigger - Turn on fireball2 <gen>
  • fireball2
    • Events
      • Time - Every 0.02 seconds of game time
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between (Position of fireball_projectile) and (Position of fireball_target)) Less than or equal to 10.00
        • Then - Actions
          • Unit - Cause fireball_caster to damage fireball_target, dealing fireball_damage damage of attack type Spells and damage type Fire
          • Unit - Explode fireball_projectile
          • Special Effect - Create a special effect at fireball_position using Abilities\Spells\Other\Incinerate\FireLordDeathExplode.mdl
          • Floating Text - Create floating text that reads (- + ((String((Integer(fireball_damage)))) + -)) at (Position of fireball_target) with Z offset 96.00, using font size 14.00, color (100.00%, 92.50%, 0.00%), and 0.00% transparency
          • Floating Text - Change (Last created floating text): Disable permanence
          • Floating Text - Change the lifespan of (Last created floating text) to 1.25 seconds
          • Floating Text - Change the fading age of (Last created floating text) to 0.75 seconds
          • Floating Text - Set the velocity of (Last created floating text) to 256.00 towards (Random real number between 105.00 and 135.00) degrees
          • Trigger - Turn off (This trigger)
        • Else - Actions
          • Set fireball_position = (fireball_position offset by 20.00 towards ((Angle from (Position of fireball_projectile) to (Position of fireball_target)) + (Random real number between 0.00 and 0.00)) degrees)
          • Unit - Move fireball_projectile instantly to fireball_position, facing (Position of fireball_target)


Now I need to make this MUI with local variables. The problem is that the trigger would assign a local variable every 0.02 seconds, and it still would change in the middle of the spell if the global variable changed. I want the system to do this:



Trigger 1
+ executes with no waits or timers so multiple casts do not affect the globals
+ assigns the caster, target, projectile, etc to globals
+ starts an instance of Trigger 2

Trigger 2
+ converts the globals to locals
+ runs periodic function checking position every 0.02 seconds until impact
+ does not re-assign any locals during the 0.02 second intervals, except projectile position and target position




I know this is possible because this is how Phantom Assassin's dagger spell is coded in DOTA. If anyone has a link to this spell's code or any spell that does what I want (NO I WILL NOT USE STORM BOLT WITH 0.01 STUN).


I have the updated map with my projectile trigger attached. Feel free to mess around with it and check out the spellpower system.
 

Attachments

  • test.w3x
    33.7 KB · Views: 75
Level 8
Joined
Apr 30, 2009
Messages
338
Hi, thanks for the tutorial on hashtables. I think I got it pretty quick.



Here is my hashtable for my fireball projectile spell, and it runs MUI in-game. If you could look over it that would be cool. I didn't care about leaks right now, I think inserting the lines to clear them will be pretty easy once I learn leaks a little better. Point with polar offset leaks kind of confuse me. Polar is way easier for me to work with than orthogonal coordinates.



  • TableFireball
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set Fireball = (Last created hashtable)
V
  • Fireball01
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Fireball
    • Actions
      • -------- 0 of Key = Caster --------
      • -------- 1 of Key = Damage --------
      • -------- 2 of Key = Distance --------
      • -------- 3 of Key = Projectile --------
      • Hashtable - Save Handle Of(Casting unit) as 0 of (Key (Target unit of ability being cast)) in Fireball
      • Hashtable - Save ((0.00 + (75.00 x (Real((Level of (Ability being cast) for (Triggering unit)))))) + ((0.60 + (0.40 x (Real((Level of (Ability being cast) for (Triggering unit)))))) x (Real((Intelligence of (Triggering unit) (Include bonuses)))))) as 1 of (Key (Target unit of ability being cast)) in Fireball
      • Hashtable - Save (Distance between (Position of (Triggering unit)) and (Target point of ability being cast)) as 2 of (Key (Target unit of ability being cast)) in Fireball
      • Unit - Create 1 projectile for (Owner of (Triggering unit)) at ((Position of (Triggering unit)) offset by 50.00 towards ((Angle from (Position of (Triggering unit)) to (Position of (Target unit of ability being cast))) - 30.00) degrees) facing (Position of (Target unit of ability being cast))
      • Hashtable - Save Handle Of(Last created unit) as 3 of (Key (Target unit of ability being cast)) in Fireball
      • Unit Group - Add (Target unit of ability being cast) to FireballTargets
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FireballRunning Equal to False
        • Then - Actions
          • Trigger - Turn on Fireball02 <gen>
          • Set FireballRunning = True
        • Else - Actions
          • Do nothing
V
  • Fireball02
    • Events
      • Time - Every 0.02 seconds of game time
    • Conditions
    • Actions
      • -------- 0 of Key = Caster --------
      • -------- 1 of Key = Damage --------
      • -------- 2 of Key = Distance --------
      • -------- 3 of Key = Projectile --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in FireballTargets) Greater than 0
        • Then - Actions
          • Unit Group - Pick every unit in FireballTargets and do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Distance between (Position of (Load 3 of (Key (Picked unit)) in Fireball)) and (Position of (Picked unit))) Less than or equal to 10.00
                • Then - Actions
                  • Unit - Cause (Load 0 of (Key (Picked unit)) in Fireball) to damage (Picked unit), dealing (Load 1 of (Key (Picked unit)) from Fireball) damage of attack type Spells and damage type Fire
                  • Special Effect - Create a special effect at (Position of (Load 3 of (Key (Picked unit)) in Fireball)) using Abilities\Spells\Other\Incinerate\FireLordDeathExplode.mdl
                  • Unit - Kill (Load 3 of (Key (Picked unit)) in Fireball)
                  • Floating Text - Create floating text that reads (- + ((String((Integer((Load 1 of (Key (Picked unit)) from Fireball))))) + -)) at (Position of (Picked unit)) with Z offset 96.00, using font size 14.00, color (100.00%, 92.50%, 0.00%), and 0.00% transparency
                  • Floating Text - Change (Last created floating text): Disable permanence
                  • Floating Text - Change the lifespan of (Last created floating text) to 1.25 seconds
                  • Floating Text - Change the fading age of (Last created floating text) to 0.75 seconds
                  • Floating Text - Set the velocity of (Last created floating text) to 256.00 towards (Random real number between 105.00 and 135.00) degrees
                  • Unit Group - Remove (Picked unit) from FireballTargets
                • Else - Actions
                  • Unit - Move (Load 3 of (Key (Picked unit)) in Fireball) instantly to ((Position of (Load 3 of (Key (Picked unit)) in Fireball)) offset by 20.00 towards (Angle from (Position of (Load 3 of (Key (Picked unit)) in Fireball)) to (Position of (Picked unit))) degrees), facing (Position of (Picked unit))
        • Else - Actions
          • Set FireballRunning = False
          • Trigger - Turn off (This trigger)


Again is the map updated with these MUI triggers.
 

Attachments

  • test.w3x
    35.7 KB · Views: 54

Cokemonkey11

Spell Reviewer
Level 29
Joined
May 9, 2006
Messages
3,531
50fps is kind of unnecessary. try .03 seconds.

"point with polar offset" leaks because it creates a location (point). Location is a class of data that contains 2 real numbers (x and y coordinate), so it must be destroyed when it's finished being used.

Now that you've gotten into some advanced triggering, I'd recommend learning jass. Instead of worrying about removing leaks, you'll be able to code efficiently while using as few leaking objects as possible.
 
Level 8
Joined
Apr 30, 2009
Messages
338
Nevermind, I think I fixed it myself. Here is the new triggers. It looks like I don't have to store location handles in the hashtable?

Can someone please tell me if this one leaks locations:

  • FireballA
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Fireball
    • Actions
      • -------- 0 of Key = Caster --------
      • -------- 1 of Key = Damage --------
      • -------- 2 of Key = Projectile --------
      • Hashtable - Save Handle Of(Triggering unit) as 0 of (Key (Target unit of ability being cast)) in H_Fireball
      • Hashtable - Save ((20.00 + (60.00 x (Real((Level of (Ability being cast) for (Triggering unit)))))) + ((0.45 + (0.45 x (Real((Level of (Ability being cast) for (Triggering unit)))))) x (Load 100 of (Key (Triggering unit)) from H_Spellpower))) as 1 of (Key (Target unit of ability being cast)) in H_Fireball
      • Set temp_point0 = (Position of (Triggering unit))
      • Set temp_point1 = (Position of (Target unit of ability being cast))
      • Set temp_point2 = (temp_point0 offset by 50.00 towards ((Angle from temp_point0 to temp_point1) - 35.00) degrees)
      • Custom script: call RemoveLocation( udg_temp_point0 )
      • Unit - Create 1 projectile for (Owner of (Triggering unit)) at temp_point2 facing temp_point1
      • Hashtable - Save Handle Of(Last created unit) as 2 of (Key (Target unit of ability being cast)) in H_Fireball
      • Custom script: call RemoveLocation( udg_temp_point1 )
      • Custom script: call RemoveLocation( udg_temp_point2 )
      • Unit Group - Add (Target unit of ability being cast) to FireballTargets
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FireballRunning Equal to False
        • Then - Actions
          • Trigger - Turn on FireballB <gen>
          • Set FireballRunning = True
        • Else - Actions
          • Do nothing
  • FireballB
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in FireballTargets) Greater than 0
        • Then - Actions
          • Unit Group - Pick every unit in FireballTargets and do (Actions)
            • Loop - Actions
              • -------- 0 of Key = Caster --------
              • -------- 1 of Key = Damage --------
              • -------- 2 of Key = Projectile --------
              • Set temp_point0 = (Position of (Picked unit))
              • Set temp_point1 = (Position of (Load 2 of (Key (Picked unit)) in H_Fireball))
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Distance between temp_point0 and temp_point1) Less than or equal to 20.00
                • Then - Actions
                  • Unit - Cause (Load 0 of (Key (Picked unit)) in H_Fireball) to damage (Picked unit), dealing (Load 1 of (Key (Picked unit)) from H_Fireball) damage of attack type Spells and damage type Fire
                  • Special Effect - Create a special effect at temp_point1 using Abilities\Spells\Other\Incinerate\FireLordDeathExplode.mdl
                  • Special Effect - Destroy (Last created special effect)
                  • Unit - Kill (Load 2 of (Key (Picked unit)) in H_Fireball)
                  • Floating Text - Create floating text that reads (- + ((String((Integer((Load 1 of (Key (Picked unit)) from H_Fireball))))) + -)) at temp_point0 with Z offset 96.00, using font size 14.00, color (100.00%, 92.50%, 12.50%), and 0.00% transparency
                  • Floating Text - Change (Last created floating text): Disable permanence
                  • Floating Text - Change the lifespan of (Last created floating text) to 1.50 seconds
                  • Floating Text - Change the fading age of (Last created floating text) to 1.00 seconds
                  • Floating Text - Set the velocity of (Last created floating text) to (Random real number between 96.00 and 256.00) towards (Random real number between 97.50 and 135.00) degrees
                  • Unit Group - Remove (Picked unit) from FireballTargets
                • Else - Actions
                  • Set temp_point2 = ((Position of (Load 2 of (Key (Picked unit)) in H_Fireball)) offset by 30.00 towards (Angle from temp_point1 to temp_point0) degrees)
                  • Unit - Move (Load 2 of (Key (Picked unit)) in H_Fireball) instantly to temp_point2, facing temp_point0
              • Custom script: call RemoveLocation( udg_temp_point0 )
              • Custom script: call RemoveLocation( udg_temp_point1 )
              • Custom script: call RemoveLocation( udg_temp_point2 )
        • Else - Actions
          • Set FireballRunning = False
          • Trigger - Turn off (This trigger)
 
Last edited:
Level 8
Joined
Apr 30, 2009
Messages
338
First you store the locations into a hashtable, and then you immediately destroy them.

Don't destroy them in the first trigger, destroy them after you don't need them anymore in the loop trigger.

Remember to clear child hashtables too.

I just edited it can you read my post again please - did I do the location cleaning right?
 
Level 8
Joined
Apr 30, 2009
Messages
338
And I would put this line:

  • Hashtable - Clear all child hashtables of child (Key (Picked unit)) in H_Fireball
in the periodic trigger at the end of this loop:

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Distance between temp_point0 and temp_point1) Less than or equal to 20.00
    • Then - Actions
      • Unit - Cause (Load 0 of (Key (Picked unit)) in H_Fireball) to damage (Picked unit), dealing (Load 1 of (Key (Picked unit)) from H_Fireball) damage of attack type Spells and damage type Fire
      • Special Effect - Create a special effect at temp_point1 using Abilities\Spells\Other\Incinerate\FireLordDeathExplode.mdl
      • Special Effect - Destroy (Last created special effect)
      • Unit - Kill (Load 2 of (Key (Picked unit)) in H_Fireball)
      • Floating Text - Create floating text that reads (- + ((String((Integer((Load 1 of (Key (Picked unit)) from H_Fireball))))) + -)) at temp_point0 with Z offset 96.00, using font size 14.00, color (100.00%, 92.50%, 12.50%), and 0.00% transparency
      • Floating Text - Change (Last created floating text): Disable permanence
      • Floating Text - Change the lifespan of (Last created floating text) to 1.50 seconds
      • Floating Text - Change the fading age of (Last created floating text) to 1.00 seconds
      • Floating Text - Set the velocity of (Last created floating text) to (Random real number between 96.00 and 256.00) towards (Random real number between 97.50 and 135.00) degrees
      • Unit Group - Remove (Picked unit) from FireballTargets
      • Hashtable - Clear all child hashtables of child (Key (Picked unit)) in H_Fireball <<<<<<<<------------HERE
    • Else - Actions
      • Set temp_point2 = ((Position of (Load 2 of (Key (Picked unit)) in H_Fireball)) offset by 30.00 towards (Angle from temp_point1 to temp_point0) degrees)
      • Unit - Move (Load 2 of (Key (Picked unit)) in H_Fireball) instantly to temp_point2, facing temp_point0
Correct?

And as long as I destroy a global before the next loop, it cannot be overwritten by another instance of the spell?
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Clear hashtables is in the correct place.

  • Set temp_point2 = ((Position of (Load 2 of (Key (Picked unit)) in H_Fireball)) offset by 30.00 towards (Angle from temp_point1 to temp_point0) degrees)
^That leaks. Position of unit is one location, the offset location is another.

temp_point2 = position of unit
temp_point3 = temp_point2 offset by...

  • Custom script: call RemoveLocation( udg_temp_point2 )
^Move that to the end of the ELSE branch, since you don't need to call that if you don't create temp_point2.

  • Set FireballRunning = True
You don't need that. Just check if trigger is on.

Also, instead of using "Save xxx as 0.." or "Save xxx as 1...", I'd use "Save xxx as damage..." or "Save xxx as caster...". Meaning I'd use string ID rather than handle ID. It's a matter of preference, but to me "caster" is more descriptive than "0".
 
Level 8
Joined
Apr 30, 2009
Messages
338
Unit handles leak, but they always clear when the unit decays right?

So I don't have to clear unit handles unless I have a spell that makes a lot of invisible dummy casters?

I think I got a way to make this totally MUI, as in even if the same unit casts the same spell multiple times at the same target before the first one finishes. I just key everything to the projectile rather than the target or caster. So each projectile is unique, and it has it's own cast, target, and damage. When I keyed it to the target, it would screw up if the same caster used the spell again on the same target. This way, I think it works all the time. Am I right?
 
Last edited:
Status
Not open for further replies.
Top