• 🏆 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] Chronosphere: Adding speed inside bubble

Status
Not open for further replies.
Level 12
Joined
May 16, 2020
Messages
660
Hi all,

I found a lot of spell packs which recreate DotA's Chronosphere, but all without having the extra speed within the bubble. Short explanation for those who don't know the spell:

Creates a blister in spacetime, trapping all units caught in its sphere of influence and causes you to move very quickly inside it.

Here a video which demonstrates the effect (10:07):

I tried to re-create this, but the spell has some bugs and I'm at the point where I have no idea why:

  1. Currently, the hero is sometimes "pushed back" when trying to get inside the bubble. It usually happens when the hero was inside the bubble, goes out, and then tries to get back in.
  2. The bubble has some ugly effect where it gets too dark - this is why I increased the animation speed to 3000%, but this shouldn't be. No idea why.
  3. The hero prefers to attack "not-paused" units outside the bubble. He has an enemy right next to him and still runs outside the bubble to attack those... why?

Please check out the attached map, then you can try it yourselves. I use Reforged, latest patch.

Can anyone please help?

  • Chronosphere
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Chronosphere
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CS_Switch Equal to 0
        • Then - Actions
          • Trigger - Turn on Chronosphere Loop <gen>
          • Trigger - Turn on Chronosphere Click Point <gen>
          • Trigger - Turn on Chronosphere Click Unit <gen>
        • Else - Actions
      • Set VariableSet CS_CastNumber = (CS_CastNumber + 1)
      • Set VariableSet CS_Switch = (CS_Switch + 1)
      • Set VariableSet CS_Off[CS_CastNumber] = True
      • Set VariableSet CS_Caster[CS_CastNumber] = (Triggering unit)
      • Set VariableSet CS_Point[0] = (Target point of ability being cast)
      • -------- Set duration to 150 because 6 devided by 0.04(interval in trigger B) is equal to 150. --------
      • -------- Every second is equal to 25! --------
      • Set VariableSet CS_Duration[CS_CastNumber] = (50 + (25 x (Level of Chronosphere for CS_Caster[CS_CastNumber])))
      • Unit - Create 1 Chronosphere Model for (Owner of CS_Caster[CS_CastNumber]) at CS_Point[0] facing Default building facing degrees
      • Set VariableSet CS_Dummy[CS_CastNumber] = (Last created unit)
      • Sound - Play BlinkArrival1 <gen> at 100.00% volume, attached to CS_Dummy[CS_CastNumber]
      • Custom script: set udg_CS_Hit[udg_CS_CastNumber] = CreateGroup()
      • Custom script: call RemoveLocation (udg_CS_Point[0])

  • Chronosphere Loop
    • Events
      • Time - Every 0.04 seconds of game time
    • Conditions
    • Actions
      • For each (Integer CS) from 1 to CS_CastNumber, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • CS_Off[CS] Equal to True
            • Then - Actions
              • Set VariableSet CS_Point[0] = (Position of CS_Dummy[CS])
              • Set VariableSet CS_Group = (Units within 425.00 of CS_Point[0] matching ((Matching unit) Equal to CS_Caster[CS]).)
              • Unit Group - Pick every unit in CS_Group and do (Actions)
                • Loop - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Number of units in CS_Group) Equal to 0
                    • Then - Actions
                      • Unit Group - Remove CS_Caster[CS] from CS_Group.
                    • Else - Actions
                      • Unit Group - Add CS_Caster[CS] to CS_Group_Inside
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Number of units in CS_Group) Greater than 0
                      • CS_Boolean Equal to True
                      • CS_Distance_Traveled Less than CS_MaxDistance
                    • Then - Actions
                      • Set VariableSet CS_Caster_Point = (Position of CS_Caster[CS])
                      • Set VariableSet CS_Target_Point[3] = (CS_Caster_Point offset by 75.00 towards CS_Angle degrees.)
                      • Set VariableSet CS_Distance_Traveled = (CS_Distance_Traveled + 75.00)
                      • Unit - Move CS_Caster[CS] instantly to CS_Target_Point[3], facing CS_Angle degrees
                    • Else - Actions
                      • Unit Group - Remove (Picked unit) from CS_Group.
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • CS_Boolean_Unit Equal to True
                        • Then - Actions
                          • Unit - Order CS_Caster[CS] to Attack CS_Target_Unit
                          • Set VariableSet CS_Boolean_Unit = False
                        • Else - Actions
                      • Set VariableSet CS_Boolean = False
                      • Set VariableSet CS_Angle = 0.00
                      • Set VariableSet CS_MaxDistance = 0.00
                      • Set VariableSet CS_Distance_Traveled = 0.00
                      • Unit Group - Remove CS_Caster[CS] from CS_Group.
                      • Custom script: call RemoveLocation (udg_CS_Caster_Point)
                      • Custom script: call RemoveLocation (udg_CS_Target_Point[3])
              • Animation - Change CS_Dummy[CS]'s animation speed to 3000.00% of its original speed
              • Custom script: set bj_wantDestroyGroup=true
              • Unit Group - Pick every unit in (Units within 425.00 of CS_Point[0] matching ((((Matching unit) is alive) Equal to True) and ((((Matching unit) is in CS_Hit[CS].) Not equal to True) and ((Matching unit) Not equal to CS_Caster[CS]))).) and do (Actions)
                • Loop - Actions
                  • Unit Group - Add (Picked unit) to CS_Hit[CS]
              • Unit Group - Pick every unit in CS_Hit[CS] and do (Actions)
                • Loop - Actions
                  • Set VariableSet CS_Point[1] = (Position of (Picked unit))
                  • Unit - Pause (Picked unit)
                  • Animation - Change (Picked unit)'s animation speed to 0.00% of its original speed
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Distance between CS_Point[0] and CS_Point[1]) Greater than 425.00
                    • Then - Actions
                      • Unit - Unpause (Picked unit)
                      • Animation - Change (Picked unit)'s animation speed to 100.00% of its original speed
                      • Unit Group - Remove (Picked unit) from CS_Hit[CS].
                    • Else - Actions
                  • Custom script: call RemoveLocation (udg_CS_Point[1])
              • Set VariableSet CS_Duration[CS] = (CS_Duration[CS] - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • CS_Duration[CS] Equal to 0
                • Then - Actions
                  • Set VariableSet CS_Off[CS] = False
                  • Sound - Play BlinkBirth1 <gen> at 100.00% volume, attached to CS_Dummy[CS]
                  • Unit - Kill CS_Dummy[CS]
                  • Set VariableSet CS_Boolean = False
                  • Unit Group - Pick every unit in CS_Hit[CS] and do (Actions)
                    • Loop - Actions
                      • Unit - Unpause (Picked unit)
                      • Animation - Change (Picked unit)'s animation speed to 100.00% of its original speed
                      • Animation - Change (Picked unit)'s vertex coloring to (100.00%, 100.00%, 100.00%) with 0.00% transparency
                      • Unit Group - Remove (Picked unit) from CS_Hit[CS].
                  • Custom script: call DestroyGroup (udg_CS_Hit[udg_CS])
                  • Set VariableSet CS_Switch = (CS_Switch - 1)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • CS_Switch Equal to 0
                    • Then - Actions
                      • Set VariableSet CS_CastNumber = 0
                      • Trigger - Turn off Chronosphere Loop <gen>
                      • Trigger - Turn off Chronosphere Click Point <gen>
                      • Trigger - Turn off Chronosphere Click Unit <gen>
                    • Else - Actions
                • Else - Actions
              • Custom script: call RemoveLocation (udg_CS_Point[0])
            • Else - Actions

  • Chronosphere Click Point
    • Events
      • Unit - A unit Is issued an order targeting a point
    • Conditions
      • CS_Boolean Equal to False
      • (Triggering unit) Equal to CS_Caster[CS_CastNumber]
    • Actions
      • Set VariableSet CS_Caster_Point = (Position of (Triggering unit))
      • Set VariableSet CS_Target_Point[1] = (Target point of issued order)
      • Set VariableSet CS_Angle = (Angle from CS_Caster_Point to CS_Target_Point[1])
      • Set VariableSet CS_MaxDistance = (Distance between CS_Caster_Point and CS_Target_Point[1])
      • Set VariableSet CS_Boolean = True
      • Custom script: call RemoveLocation (udg_CS_Caster_Point)
      • Custom script: call RemoveLocation (udg_CS_Target_Point[1])

  • Chronosphere Click Unit
    • Events
      • Unit - A unit Is issued an order targeting an object
    • Conditions
      • CS_Boolean Equal to False
      • (Triggering unit) Equal to CS_Caster[CS_CastNumber]
    • Actions
      • Set VariableSet CS_Caster_Point = (Position of (Triggering unit))
      • Set VariableSet CS_Target_Unit = (Target unit of issued order)
      • Set VariableSet CS_Target_Point[1] = (Position of CS_Target_Unit)
      • Set VariableSet CS_Angle = (Angle from CS_Caster_Point to CS_Target_Point[1])
      • Set VariableSet CS_MaxDistance = ((Distance between CS_Caster_Point and CS_Target_Point[1]) - 100.00)
      • Set VariableSet CS_Boolean = True
      • Set VariableSet CS_Boolean_Unit = True
      • Custom script: call RemoveLocation (udg_CS_Caster_Point)
      • Custom script: call RemoveLocation (udg_CS_Target_Point[1])
 

Attachments

  • Land of Legends v0.0.81.w3m
    36.3 MB · Views: 41

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
I attached a map with the added the movement speed while inside of Chronopshere. I also deleted all of the stuff that looked unnecessary (like those order triggers).

How it works:
In the Loop trigger I periodically check the distance between CS_Caster and the center of the Chronosphere, adding the movespeed ability if the Hero is close enough (inside) and removing the ability if it's too far away (outside).

I also made it so all of the units inside of the Chronosphere will be added to the CS_Group_Inside unit group. I didn't see this unit group being used, but I figured you wanted it for something. I also made sure to remove the units from CS_Group_Inside when they exit the chronosphere, as they weren't being removed before.

Regarding your list of issues:

1: I didn't experience this, but I also deleted a bunch of the stuff you added so I assume those triggers were causing the problem.

2: You can expect old models to have problems with the new graphics settings. I would try to use a Reforged model for the Chronosphere, I imagine with the right settings you could get it to look good.

3: The AI will look for threats. A paused unit isn't a threat, but an enemy attacking Faceless Void from outside of the Chronosphere is.

Note: If you wanted something different than a % speed boost let me know. Maybe something like the movement system from those Slide maps. I could try and get that to work.
 

Attachments

  • Land of Legends v0.0.81 Chrono Fix.w3m
    36.3 MB · Views: 41
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Thank you! Unfortuntely that's not exactly what I want; if you look at the video, I'm sure the movement is done via a "move unit" trigger, not a buff that increases the movement speed.

Check out the video above at 10:07 or 29:54, there the effect is visible (it's very cool actually). The movement is far too fast to be a buff.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Hmm, yeah, I'm guessing they used SetUnitX/Y to nudge the hero in the direction it's facing, but only while it's moving. The moving check is important since it would prevent him from constantly sliding around. It's also important to use SetUnitX/Y because it doesn't issue a "Stop" order, so it won't interrupt the Hero.

So basically:
  • Events:
  • every 0.03 seconds
  • Actions:
  • if isMoving = true then:
  • set point = position of void offset by 5 facing of void degrees
  • set x = X of point
  • set y = Y of point
  • SetUnitX[void, x]
  • SetUnitY[void, y]
And to determine if he's moving or not, a system like Bribe's Is Unit Moving 2.1.0.0 could be used.

That being said, it would be more efficient to just calculate whether or not he's moving yourself.

To do so, you compare voids previous x/y coordinates to his current x/y coordinates. If they're different than we know that he's moving.

So with both concepts applied:
  • Events:
  • every 0.03 seconds
  • Actions:
  • set newx = X of pos of Void
  • set newy = Y of pos of Void
  • if newx != oldx or newy != oldy then set isMoving = true, ELSE, set isMoving = false
  • set oldx = newx
  • set oldy = newy
  • ---------------------------
  • if isMoving = true then:
  • set point = position of void offset by 5 facing of void degrees
  • set x = X of point
  • set y = Y of point
  • SetUnitX[void, x]
  • SetUnitY[void, y]
I can try to throw this together when I get a chance.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Hey, sorry just back back home. I was thinking on how to apply the concept you outlined and what I wonder: If you offset the position by 5 when the caster moves, it would basically cause an neverending loop, because

move? Yes > move? Yes > etc.

So not exactly sure how you would "end" the loop.
I suppose I didn't think it all the way through...

Anyway, that's the only effective way I know of tracking movement, which leaves me wondering how they did it. Maybe it's something entirely different.

Edit:
Okay, I've got it. You also need to store the coordinates of the Polar Offset point (where the Hero is moved to). Then compare them to newx/y in the conditions.

Edit #2:
I attached the map with all of this implemented. It works fairly well!

I changed it to use a Timer that runs every 0.02 seconds, as it helped with the precision of the movement. I also fixed an issue in which Void would slide multiple times at once if he was inside 2+ Chronospheres, you'll see this being managed with CS_MoveCounter. What it does is keep track of whether or not Void has moved already in the current Loop, increasing by 1 each cycle. So if he's inside of 2 Chronospheres, the 1st one will cause him to slide (move), but the 2nd one will not (because CS_MoveCounter will be > 1). I track it using the Hero's custom value as the Index so that it can be referenced between the different casts.
 

Attachments

  • Land of Legends v0.0.81 Chrono 2.w3m
    36.3 MB · Views: 37
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
I will need to process all these new triggers! But it definitely goes into the right direction - Thank you!

But one thing: I just tried increasing the offset from 5 to 10 or even further, because I thought the movement is still a bit too slow. If I do that however, Void starts to walk more and more "in curves". At an offset of 25 Void actually starts walking around in full circles lol.

Do you know why this happens? Looking at the trigger I don't see something that would lead to this behaviour, as the movement is strictly defined by X and Y, and the facing should stay constant throughout the 0.02 seconds:

  • Set VariableSet CS_Point[2] = (CS_Point[1] offset by 10.00 towards (Facing of CS_Caster[CS]) degrees.)
  • Set VariableSet CS_Move_X[CS] = (X of CS_Point[2])
  • Set VariableSet CS_Move_Y[CS] = (Y of CS_Point[2])
  • Custom script: call SetUnitX(udg_CS_Caster[udg_CS], udg_CS_Move_X[udg_CS])
  • Custom script: call SetUnitY(udg_CS_Caster[udg_CS], udg_CS_Move_Y[udg_CS])
  • Custom script: call RemoveLocation (udg_CS_Point[2])
Edit: Ah - I think I know why! My guess is that "set X and Y" lead to this behaviour, as Void can and will adjust its facing if an enemy is nearby. So if you pass by an enemy unit and you intend to attack an unit further beyond, Void will look at the nearby unit > try to attack it > trigger will adjust the facing and consequently X and Y. Can this be?

Edit2: Nevermind... I just tested it further and he starts walks in circles even without an enemy nearby, so it can't be that.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
That happens because void gets pushed beyond the point that he's ordered to move to, then turns around in an attempt to face that point. I lowered the Offset to try and prevent that from happening.

There's probably a way of fixing it but I couldn't think of one.
 
Level 12
Joined
May 16, 2020
Messages
660
I'm still studying your triggers, but I'm getting more and more the impression that they used "move unit" instead of "set X and Y":

Hmm, yeah, I'm guessing they used SetUnitX/Y to nudge the hero in the direction it's facing, but only while it's moving. The moving check is important since it would prevent him from constantly sliding around. It's also important to use SetUnitX/Y because it doesn't issue a "Stop" order, so it won't interrupt the Hero.

Looking at the video (frame by frame), it looks like the hero is frozen during the move. This would imply it's rather "move" than "set X and Y". This would also prevent the hero from "turing around" when he's pushed beyond the point he's ordered to move, right?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
You can't use Move, or else it'll constantly order the Hero to Stop.

SetUnitX/Y is the objectively better method as it does the exact same thing as Move but without the "Stop" order.

What it could be is that the heroes default movement is disabled and instead relies entirely on SetUnitX/Y.
 
Level 12
Joined
May 16, 2020
Messages
660
You can't use Move, or else it'll constantly order the Hero to Stop.

SetUnitX/Y is the objectively better method as it does the exact same thing as Move but without the "Stop" order.

What it could be is that the heroes default movement is disabled and instead relies entirely on SetUnitX/Y.

Thanks Uncle! I studied your trigger and it was very interesting to see your adjustments. On the adjustments specifically I'm wondering: Why did you change the "every 0.04" timer to "Timer expires"? Is this better for performance?

What I changed:
I created a mix of your version and what I had before (not checking if Void is moving, but instead checking if Void receives an order to go somewhere in the sphere and then move him there). I think that looks more closely to what I see in the video. Also, I experimented a lot with SetUnitX/Y vs Move, and I just don't know how to prevent Void from "moving" back "if he's pushed beyond the target point". Weirdly enough he sometimes even moves back even though I move him exactly to the target point. So for now I left it as "Move" instead of "SetUnitX/Y" because I simply didn't know how to fix that.

Unfortuntely this new version has some new problems... (sorry):

- Currently, if Void is a) outside the sphere and b) casts it away from him (= he's not in CS_Group_Inside instantly), and c) I give him the order to walk into the sphere, then he is not automatically speed-moved by the trigger to where the order was issues. So I have to "re-click" and only then the trigger recognizes that Void should be moved there. I don't see why this is happening, as as soon as Chronosphere is cast and Void receives an order to go somewhere, the CS_Boolean turns to true, which should automatically trigger a movement once Void is inside the sphere...

- If I try to move Void "through" an enemy, Void sometimes just stops in his tracks. I assumed triggers would just move the unit "around" the obstacle, but it doesn't always happen. Do you know why?

(I suggest you check it out in the map yourself - it should be more obvious when you test it what I mean)


Sorry again for changing what you already worked on!
 

Attachments

  • Land of Legends v0.0.82.w3m
    36.6 MB · Views: 35
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Just finished testing it and it seems Move is fine in this case. I was under the impression that the constant Stop orders would break the trigger but it seems like it's fine.

I'm messing around with it now and I think I have some ideas on how to fix those problems.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Well, I got it to work fairly well, but with some unwanted results like no unit collision.

I tested the spell out in Dota v.6.85 (not sure if it was legit or the latest map), and managed to decipher a few things, at least for the version that I tested:
1) It isn't using Move Unit Instantly
2) It doesn't increase Void's Movement Speed
3) Void doesn't move that fast (it honestly looked just as fast as 522 max movement speed)

So this leads me to believe that they're doing something similar to what I'm doing, but in a way that checks for collision and prevents issues where the unit gets stuck while trying to move around a target.

I'm not really sure how they did it, because when using SetUnitX/Y it will ignore collision which is a problem. And when using Move Unit Instantly it will not ignore collision, but issues a "Stop" order which is just as troublesome.

Also, there's a small bug that I couldn't figure out in which the Hero can stuck while exiting the Chronosphere, maybe you can pinpoint the issue.
 

Attachments

  • Land of Legends v0.0.81 Chrono 5.w3m
    36.3 MB · Views: 36
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Thank you! I'm still trying to wrap my head around all these sophisticated trigger methods you're using, and searching a pattern for this bug (it feels like the Sphere is trying to repell Void). And you are right about the look and feel of the movement in the Sphere; I also just tested it within the reforged dota map and it does feel exactly like yours :)

But one thing which I just want to understand: In the previous map I shared, Void doesn't automatically get "moved" as soon as he walks through the Sphere:

- Currently, if Void is a) outside the sphere and b) casts it away from him (= he's not in CS_Group_Inside instantly), and c) I give him the order to walk into the sphere, then he is not automatically speed-moved by the trigger to where the order was issues. So I have to "re-click" and only then the trigger recognizes that Void should be moved there.

Do you know why? (the reason I ask is because this trigger would give me a good base for another, similar skill )
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
That order trigger was only running if Void was already inside of a Chronosphere. If he issued an order while outside of the Chronosphere then it wouldn't have set the needed variables.

Regardless, I think the simple version I made (first one) that adds Movement Speed is the closest to how it works in DotA.
 
Status
Not open for further replies.
Top