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

[Spell] Time Stop, Gravity, Spell Destruction and Spell Reflection

Status
Not open for further replies.
Level 7
Joined
Feb 9, 2021
Messages
301
I want to have 4 spells in my map, for which I think I need systems beforehand in order to not recreate all spells from the scratch. Therefore, I wanted to ask knowledgeable people on how to approach it in the best way. I am open to any systems, and I build for war 1.26:

1) Time Stop
The spell stops all units, missiles, channellings and other spells in the area. After the spell is finished, everything continues as normal.
2) Gravity
The spell slows all missiles(type magic) and destroys missiles (type physical). After they go out of the area, they continue as normal.
3) Spell Destruction
The spell destroys all enemy missiles in the area and cancels casts of enemies
4) Spell Reflection
the spell reflects the missile in a random direction and causes damage to enemies (if encountered) (damage equal to the damage that missile had to cause the caster)
 
Last edited:
Level 11
Joined
May 29, 2008
Messages
132
AFAIK, manipulating missiles is extremely difficult. Triggers don't have the ability to manipulate standard in game missiles, so you'll need to replace all missiles from every source in your game with custom missiles. If you're willing to take on the work of implementing a custom missile system in your map, from that point forward you'll have complete control of your missiles, making the spells described above become possible to implement.

For the first spell, you'd also need to add hooks into the channeling and spell systems that you implement to allow freezing as well.
 
Level 7
Joined
Feb 9, 2021
Messages
301
AFAIK, manipulating missiles is extremely difficult. Triggers don't have the ability to manipulate standard in game missiles, so you'll need to replace all missiles from every source in your game with custom missiles. If you're willing to take on the work of implementing a custom missile system in your map, from that point forward you'll have complete control of your missiles, making the spells described above become possible to implement.

For the first spell, you'd also need to add hooks into the channeling and spell systems that you implement to allow freezing as well.
I think I had to say that I do not have any basic warcraft 3 spells (all used as dummy abilities), and I code on Jass/vJass.

What do you mean by adding hooks?
 
Level 11
Joined
May 29, 2008
Messages
132
By hooks, I mean that each spell needs to provide a way to pause and resume its execution. That way, you could say "For each spell in this area, pause the spell" then wait some amount of time before resuming each spell. Since each spell is unique, it needs to be responsible for how it handles pauses and resumes.

In psuedocode I would expect a spell to have an interface like this, to satisfy these requirements:
Code:
interface Spell {
  Pause()
  Resume()
}
 
Level 7
Joined
Feb 9, 2021
Messages
301
By hooks, I mean that each spell needs to provide a way to pause and resume its execution. That way, you could say "For each spell in this area, pause the spell" then wait some amount of time before resuming each spell. Since each spell is unique, it needs to be responsible for how it handles pauses and resumes.

In psuedocode I would expect a spell to have an interface like this, to satisfy these requirements:
Code:
interface Spell {
  Pause()
  Resume()
}
Hmm, so I would have a condition for each spell that stops its execution. How would be better to do this condition? Maybe if they are in a certain group?

What about other spells? The same can be said for destruction. If they are in the group "dispel", I can destroy them.
 
Level 11
Joined
May 29, 2008
Messages
132
The way you write how a spell pauses is really specific to a given spell. Something like a "Fireball" simple just freezes the projectile or a Firestorm would just pause between waves. But a complex spell would need to be able to suspend and resume from anypoint in the spell's execution.

You're right that this pattern could be applied to dispelling spells. It can really be applied to any behavior that all spells need to exhibit but will need to be individual per spell.
 
Level 7
Joined
Feb 9, 2021
Messages
301
The way you write how a spell pauses is really specific to a given spell. Something like a "Fireball" simple just freezes the projectile or a Firestorm would just pause between waves. But a complex spell would need to be able to suspend and resume from anypoint in the spell's execution.

You're right that this pattern could be applied to dispelling spells. It can really be applied to any behavior that all spells need to exhibit but will need to be individual per spell.
You wrote about using the interface. I didn't really get how it is useful here.

Also, what do you think about 2 and 4?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
I think he means so that you can easily call these Functions. So when a Missile enters a Time Stop field you can call the Pause(missile) function. Once the Time Stop field dissipates you can call the Resume(missile) function on each missile inside of the field.

Since Missiles are unique from one another you'd want them to have Pause() and Resume() functions specific to them. But you'd also want these functions to be called the same way for each missile. You don't want a long If Then Else statement checking "If MissileA then do this... Else if MissileB then to do this...".

You'd also want to check that the missiles aren't affected by an additional Time Stop field, to avoid resuming missiles that should still be paused. To do this you could have an Integer property on each missile that tracks the number of times it has been paused. So you'd +1/-1 this counter when calling Pause/Resume, and run the rest of the code depending on the outcome. So when this Counter goes from 0 to 1 you Pause the missile's movement, and when it goes from 1 to 0 you Resume it's movement. Otherwise, you simply adjust the counter.

For #2 your missiles would need a Speed property that you can update in real time and a Type property (Physical/Magical) which again isn't anything crazy.

For #4 you would need an Owner property that determines their alliance and who they should interact with. You'd probably want something like a Redirect() function that will allow you to reset the missile and send it in a random direction with a new Owner property. Maybe you'd want to retain it's original Speed or even speed it up upon Redirecting so retaining the original properties of the missile could be important.

I think a system like this is definitely doable but it could get rather complex depending on the missiles themselves. If each missile works like Mirana's Arrow from Dota, which is basically: Shoots a Missile that travels in a straight line at a constant speed. If it collides with an enemy it applies Stun/Damage and Destroys the Missile. Then it would be a lot easier to manage since the missile is pretty simple. More complex missiles could be difficult though.

Also, working on 1.26 is rather limiting, but I imagine you have a good reason for doing so.
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
I think he means so that you can easily call these Functions. So when a Missile enters a Time Stop field you can call the Pause(missile) function. Once the Time Stop field dissipates you can call the Resume(missile) function on each missile inside of the field.

Since Missiles are unique from one another you'd want them to have Pause() and Resume() functions specific to them. But you'd also want these functions to be called the same way for each missile. You don't want a long If Then Else statement checking "If MissileA then do this... Else if MissileB then to do this...".

You'd also want to check that the missiles aren't affected by an additional Time Stop field, to avoid resuming missiles that should still be paused. To do this you could have an Integer property on each missile that tracks the number of times it has been paused. So you'd +1/-1 this counter when calling Pause/Resume, and run the rest of the code depending on the outcome. So when this Counter goes from 0 to 1 you Pause the missile's movement, and when it goes from 1 to 0 you Resume it's movement. Otherwise, you simply adjust the counter.

For #2 your missiles would need a Speed property that you can update in real time and a Type property (Physical/Magical) which again isn't anything crazy.

For #4 you would need an Owner property that determines their alliance and who they should interact with. You'd probably want something like a Redirect() function that will allow you to reset the missile and send it in a random direction with a new Owner property. Maybe you'd want to retain it's original Speed or even speed it up upon Redirecting so retaining the original properties of the missile could be important.

I think a system like this is definitely doable but it could get rather complex depending on the missiles themselves. If each missile works like Mirana's Arrow from Dota, which is basically: Shoots a Missile that travels in a straight line at a constant speed. If it collides with an enemy it applies Stun/Damage and Destroys the Missile. Then it would be a lot easier to manage since the missile is pretty simple. More complex missiles could be difficult though.

Also, working on 1.26 is rather limiting, but I imagine you have a good reason for doing so.
Thank you for your response. I got the concept behind what you said, but, I am still a beginner in wc3 programming. Therefore, I don't really understand how I can organise it in an efficient manner. Maybe you could show me a simple example, such as a missile that causes damage to enemy units?

Another problem that I thought about is stopping buff's duration. I am currently using [vJASS] - Buff System. Do you know how I can use buff's duration with it? There is no API for this.

I thought for missiles to use this system Relativistic Missiles [vJASS][LUA]. But it seems like there is no easy way to implement what I want with this system.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
If you look at the code in the Buff System you'll see each Buff has a timer variable called "t". I imagine all you have to do is Pause this timer and your Buff will "freeze". I cut out some code that references this timer:
vJASS:
method remove takes nothing returns nothing
   if this.t != null then
      static if LIBRARY_TimerUtils then
         call ReleaseTimer(this.t)
      else
         call RemoveSavedInteger(thistype.hash, GetHandleId(this.t), 0)
         call DestroyTimer(this.t)
      endif
      set this.t = null
   endif
endmethod
this = the Buff
this.t = the Buff's Timer

So you just need to get reference to the buffs on a unit, which I'm sure there is a function for, and pause their timers -> PauseTimer(this.t)

I'm sure you can find a working missile system that's compatible with 1.26. The concept isn't anything too crazy, you're just moving a Dummy/Special Effect towards a target unit/point periodically using a timer. After moving the missile you check if it's within contact range of the target or if the target has become untargetable (dead, hidden, etc) and respond accordingly (destroy the missile most likely). If it successfully reaches it's target then you call an Effect() function that applies it's effects (explosion, damage, stun, etc) and then Destroy() it.

I imagine you'll be using Dummy units since you're on 1.26 so I would start by creating a Dummy unit missile. Then make a simple function that Creates the missile and turns on a timer that periodically moves the missile towards a point using some speed/angle properties. After getting that working you will probably want to make a missile that chases a target. Well, the concept is very much the same as before but now your destination point can now move on it's own. Not too complicated, all you have to do is update the target's position the same as you would with the missile. Take it one step at a time and everything should eventually click.
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
If you look at the code in the Buff System you'll see each Buff has a timer variable called "t". I imagine all you have to do is Pause this timer and your Buff will "freeze". I cut out some code that references this timer:
vJASS:
method remove takes nothing returns nothing
   if this.t != null then
      static if LIBRARY_TimerUtils then
         call ReleaseTimer(this.t)
      else
         call RemoveSavedInteger(thistype.hash, GetHandleId(this.t), 0)
         call DestroyTimer(this.t)
      endif
      set this.t = null
   endif
endmethod
this = the Buff
this.t = the Buff's Timer

So you just need to get reference to the buffs on a unit, which I'm sure there is a function for, and pause their timers -> PauseTimer(this.t)

I'm sure you can find a working missile system that's compatible with 1.26. The concept isn't anything too crazy, you're just moving a Dummy/Special Effect towards a target unit/point periodically using a timer. After moving the missile you check if it's within contact range of the target or if the target has become untargetable (dead, hidden, etc) and respond accordingly (destroy the missile most likely). If it successfully reaches it's target then you call an Effect() function that applies it's effects (explosion, damage, stun, etc) and then Destroy() it.

I imagine you'll be using Dummy units since you're on 1.26 so I would start by creating a Dummy unit missile. Then make a simple function that Creates the missile and turns on a timer that periodically moves the missile towards a point using some speed/angle properties. After getting that working you will probably want to make a missile that chases a target. Well, the concept is very much the same as before but now your destination point can now move on it's own. Not too complicated, all you have to do is update the target's position the same as you would with the missile. Take it one step at a time and everything should eventually click.
Sorry, it seems I didn't explain myself clearly, and you had to write all of this. I am a noob, but I understand how to do basic-medium difficulty type spells. The system that I sent has a version that works for 1.26. The problem with that system is the realisation of the things we discussed before, as I would have to change the system (not in my capability at the movement).

What I meant by organising in an efficient manner are the 4 initial spells that we discussed. For example, how do I access the speed inside a missile from another function. I have spells in libraries. On spell number 4, I am not sure what is the best way to structure what you described. Or how to organise the code with interfaces for easy implementation of the time stop. Therefore, it would be great to see what you said on a simple example, and then I can adapt the knowledge and the idea to my map.

Edit: Thank you for the buff idea, I will try it out.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
If your missile is a dummy unit then all of it's variables (properties) are linked to it. So if you have access to the unit then you have access to it's speed, angle, target, etc...

I avoid Jass like the plague but here's a system I threw together in C#, note that I'm still learning how to code myself:
Code:
using static MapName.Timers;

namespace MapName
{
    class MissileSystem
    {
        public static List<MissileSystem> missileList = new List<MissileSystem>();

        public unit source;
        public unit target;
        public effect missile;
        public location loc;
        public Action impactMethod;
        public float speed;
        public float z;
        public float angle;
        public float missileX;
        public float missileY;
        public float targetX;
        public float targetY;
        public float distance = 0f;
        public float height = 0f;
        public timer timer;
        public bool isPaused = false;

        public static void CreateMissile(unit source, unit target, string model, Action impactMethod, float speed, float height, float launchOffset)
        {
            var missileX = GetUnitX(source);
            var missileY = GetUnitY(source);
            var targetX = GetUnitX(target);
            var targetY = GetUnitY(target);
            // angle / launch Offset
            var angle = Atan2(targetY - missileY, targetX - missileX);
            missileX += launchOffset * Cos(angle);
            missileY += launchOffset * Sin(angle);
            // create missile special effect
            var missile = AddSpecialEffect(model, missileX, missileY);
            // create loc and get z height
            var loc = Location(missileX, missileY);
            var z = GetLocationZ(loc) + height;
            // set yaw
            BlzSetSpecialEffectYaw(missile, angle);
            // set position and get z height
            BlzSetSpecialEffectPosition(missile, missileX, missileY, z);
            // create missile class
            var newMissile = new MissileSystem
            {
                source = source,
                target = target,
                missile = missile,
                loc = loc,
                speed = speed,
                height = height,
                angle = angle,
                missileX = missileX,
                missileY = missileY,
                targetX = targetX,
                targetY = targetY,
                impactMethod = impactMethod,
            };
            // add missile to list
            missileList.Add(newMissile);
            // create missile timer
            var method = new MissileTimer
            {
                ms = newMissile,
            };
            newMissile.timer = StartNewTimer(method, 0.03f, true); // start a new timer for this missile
        }
    }

    class MissileTimer : Timers
    {
        public MissileSystem ms { get; set; }

        public override void Action()
        {
            // check if the missile is paused (aka timer)
            if (ms.isPaused) { return; }
            // set positions / angle
            var x1 = BlzGetLocalSpecialEffectX(ms.missile);
            var y1 = BlzGetLocalSpecialEffectY(ms.missile);
            ms.targetX = GetUnitX(ms.target);
            ms.targetY = GetUnitY(ms.target);
            ms.angle = Atan2(ms.targetY - y1, ms.targetX - x1);
            // set yaw
            BlzSetSpecialEffectYaw(ms.missile, ms.angle);
            // set new position
            ms.missileX = x1 + ms.speed * Cos(ms.angle);
            ms.missileY = y1 + ms.speed * Sin(ms.angle);
            // set z height
            MoveLocation(ms.loc, ms.missileX, ms.missileY);
            ms.z = GetLocationZ(ms.loc) + ms.height;
            // set pos
            BlzSetSpecialEffectPosition(ms.missile, ms.missileX, ms.missileY, ms.z);
            // set distance
            var x2 = ms.missileX - ms.targetX;
            var y2 = ms.missileY - ms.targetY;
            ms.distance = SquareRoot(x2 * x2 + y2 * y2);
            // check distance from target
            if (ms.distance <= 25f)
            {
                ms.impactMethod.Invoke();
                DestroyEffect(ms.missile);
                RemoveLocation(ms.loc);
                MissileSystem.missileList.Remove(ms);
                RemoveTimer(GetExpiredTimer());
            }
        }
    }
}
A little confusing if you don't know C#.

It's using another Class I made called Timers which handles creating/removing timers. I never used TimerUtils before but I think it's similar to that as it allows you to link data to a timer and reference that in it's Callback function. Public override void Action() is the Callback function for the missile's timer, so inside that function is all of the missile movement stuff that happens every 0.03 seconds. I contain all of the missiles in a list so I can enumerate over them whenever I'd like. Note that my system is a little different because it uses Special Effects instead of Dummy units but it's very easy to change between the two (the code remains practically the same, you're just moving a unit instead of a special effect).

So when you cast your "Freeze Time" ability, you could enumerate over your Unit Group of missiles and set their "isPaused" property to true. Or just Pause their timers, which is what I tried originally but for some reason I couldn't get the Timers to Resume. The point is, if you have a missile unit then you have it's properties therefore you can adjust them as you please. How you get these units is up to you but I'm sure you've made plenty of spells before that enumerate over a group of units, that's all you'd be doing.

IE -> Pick every unit within 1000 range of a point, if picked unit is a Missile then Set PickedUnit.Speed = 0.
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
If your missile is a dummy unit then all of it's variables (properties) are linked to it. So if you have access to the unit then you have access to it's speed, angle, target, etc...

I avoid Jass like the plague but here's a system I threw together in C#, note that I'm still learning how to code myself:
Code:
using static MapName.Timers;

namespace MapName
{
    class MissileSystem
    {
        public static List<MissileSystem> missileList = new List<MissileSystem>();

        public unit source;
        public unit target;
        public effect missile;
        public location loc;
        public Action impactMethod;
        public float speed;
        public float z;
        public float angle;
        public float missileX;
        public float missileY;
        public float targetX;
        public float targetY;
        public float distance = 0f;
        public float height = 0f;
        public timer timer;
        public bool isPaused = false;

        public static void CreateMissile(unit source, unit target, string model, Action impactMethod, float speed, float height, float launchOffset)
        {
            var missileX = GetUnitX(source);
            var missileY = GetUnitY(source);
            var targetX = GetUnitX(target);
            var targetY = GetUnitY(target);
            // angle / launch Offset
            var angle = Atan2(targetY - missileY, targetX - missileX);
            missileX += launchOffset * Cos(angle);
            missileY += launchOffset * Sin(angle);
            // create missile special effect
            var missile = AddSpecialEffect(model, missileX, missileY);
            // create loc and get z height
            var loc = Location(missileX, missileY);
            var z = GetLocationZ(loc) + height;
            // set yaw
            BlzSetSpecialEffectYaw(missile, angle);
            // set position and get z height
            BlzSetSpecialEffectPosition(missile, missileX, missileY, z);
            // create missile class
            var newMissile = new MissileSystem
            {
                source = source,
                target = target,
                missile = missile,
                loc = loc,
                speed = speed,
                height = height,
                angle = angle,
                missileX = missileX,
                missileY = missileY,
                targetX = targetX,
                targetY = targetY,
                impactMethod = impactMethod,
            };
            // add missile to list
            missileList.Add(newMissile);
            // create missile timer
            var method = new MissileTimer
            {
                ms = newMissile,
            };
            newMissile.timer = StartNewTimer(method, 0.03f, true); // start a new timer for this missile
        }
    }

    class MissileTimer : Timers
    {
        public MissileSystem ms { get; set; }

        public override void Action()
        {
            // check if the missile is paused (aka timer)
            if (ms.isPaused) { return; }
            // set positions / angle
            var x1 = BlzGetLocalSpecialEffectX(ms.missile);
            var y1 = BlzGetLocalSpecialEffectY(ms.missile);
            ms.targetX = GetUnitX(ms.target);
            ms.targetY = GetUnitY(ms.target);
            ms.angle = Atan2(ms.targetY - y1, ms.targetX - x1);
            // set yaw
            BlzSetSpecialEffectYaw(ms.missile, ms.angle);
            // set new position
            ms.missileX = x1 + ms.speed * Cos(ms.angle);
            ms.missileY = y1 + ms.speed * Sin(ms.angle);
            // set z height
            MoveLocation(ms.loc, ms.missileX, ms.missileY);
            ms.z = GetLocationZ(ms.loc) + ms.height;
            // set pos
            BlzSetSpecialEffectPosition(ms.missile, ms.missileX, ms.missileY, ms.z);
            // set distance
            var x2 = ms.missileX - ms.targetX;
            var y2 = ms.missileY - ms.targetY;
            ms.distance = SquareRoot(x2 * x2 + y2 * y2);
            // check distance from target
            if (ms.distance <= 25f)
            {
                ms.impactMethod.Invoke();
                DestroyEffect(ms.missile);
                RemoveLocation(ms.loc);
                MissileSystem.missileList.Remove(ms);
                RemoveTimer(GetExpiredTimer());
            }
        }
    }
}
A little confusing if you don't know C#.

It's using another Class I made called Timers which handles creating/removing timers. I never used TimerUtils before but I think it's similar to that as it allows you to link data to a timer and reference that in it's Callback function. Public override void Action() is the Callback function for the missile's timer, so inside that function is all of the missile movement stuff that happens every 0.03 seconds. I contain all of the missiles in a list so I can enumerate over them whenever I'd like. Note that my system is a little different because it uses Special Effects instead of Dummy units but it's very easy to change between the two (the code remains practically the same, you're just moving a unit instead of a special effect).

So when you cast your "Freeze Time" ability, you could enumerate over your Unit Group of missiles and set their "isPaused" property to true. Or just Pause their timers, which is what I tried originally but for some reason I couldn't get the Timers to Resume. The point is, if you have a missile unit then you have it's properties therefore you can adjust them as you please. How you get these units is up to you but I'm sure you've made plenty of spells before that enumerate over a group of units, that's all you'd be doing.

IE -> Pick every unit within 1000 range of a point, if picked unit is a Missile then Set PickedUnit.Speed = 0.
Thank you. Let me try to build something on vJass with this. Can I just ask how do you get the properties after having the missile? Also, what type of variables should changeable variables be in the spell (Speed, Ownersihp etc)? How do I assign property to the unit (missile)

So, you don't pause the timer, the timers are still running during the pause (The spells just do nothing because of conditions in them)?
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Hashtables, Unit Indexing, etc... All units have a unique handle id, that handle id can be the Index in an Array.

Speed[your unit] = 100

And I would pause/resume the timers the normal way but I had issues with resuming. Something was either wrong with my code or wc3. Maybe look into the issues with wc3's timers, I've seen systems that apparently remedy some of their issues.
 
Last edited:
Status
Not open for further replies.
Top