Spell Torrent

muzk

Member
Reaction score
3
Torrent
Yup, a spell from DotA.

Technical Details
Jass: Yes.
MUI: Yes.
Laggless: Yes.
Leakless: Yes.
Requires: NewGen Editor, GT, TimerUtils,GroupUtils, DummyCaster, AutoFly



How to import
Code:
1) Copy the Torrent trigger into your map
2) Copy the TidalErruption.mdx model (if you do not already have it) into your map and change the patch to TidalErruption.mdx
3) Copy the Torrent (A000) , Torrent Slow (A001) abilities into your map
and Copy the Torrent buff (B000)
4) Copy the libraries that I used (if you do not already have it) into your map

Description
Using his unparalleled knowledge of the sea, Kunkka is able to summon a blast of water at a targeted area. After 2 seconds a fierce torrent of water erupts from the ground, the stream blasting enemies caught in the AoE into the sky, dealing damage and slowing movement speed by 30%.
Level 1 - Deals 120 damage, slows for 1 second.
Level 2 - Deals 180 damage, slows for 2 seconds.
Level 3 - Deals 240 damage, slows for 3 seconds.
Level 4 - Deals 300 damage, slows for 4 seconds.

Code

JASS:
library Torrent initializer Init requires GT,DummyCaster,AutoFly,TimerUtils,GroupUtils
    // DotA Torrent by muZk
    globals
        
       //SPELL CONFIGURABLES
        private constant integer SPELL_ID = 'A000'  // Torrent rawcode ability
        private constant integer SLOW_ID  = 'A001'  // Slow effects rawcode ability
        
        private constant real INTERVAL = 0.03125 // callback interval
        private constant real WAIT     = 2.0     // time you have to wait before of erruption
        private constant real RADIUS   = 222.00  // effect radius of torrent
        
        private constant real MAX_HEIGHT   = 625.00 // max flyheight of the torrent unit
        private constant real DURATION     = 1.5    // duration of fly
        
        //EFFECTS
        private constant string  Bubbles          = "Objects\\Spawnmodels\\Other\\IllidanFootprint\\IllidanWaterSpawnFootPrint.mdl" //First effect
        private constant integer NumberOfBubbles  = 6     
        private constant real    DistanceOfTarget = 40.00      //Distance between bubbles and target point
        
        private constant string Erruption = "TidalErruption.mdx"                                                            //Erruption effect after WAIT seconds
        private constant string TargetFX  = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"                //Effect on target when it is flying
        
        //Other stuff       
        private constant attacktype A_TYPE = ATTACK_TYPE_HERO 
        private constant damagetype D_TYPE = DAMAGE_TYPE_UNIVERSAL       
    endglobals
    
    globals
        private constant real v            = 4*MAX_HEIGHT/DURATION
        private constant real g            = 4*MAX_HEIGHT/(DURATION*DURATION)
        private constant real callbacks    = DURATION/INTERVAL
    endglobals
    
    // TORRENT DAMAGE : Torrent causes 60 + 60*Level, and Level is level of torrent ability
    // That is the total damage, but it look realistic if you do that damage per callback
    // So, this function returns the damage per callback 
    private function DAMAGE takes integer level returns real
        return (60.00 + 60.00*level)/callbacks
    endfunction
    
    // Height function, takes a callbacks counter returns the current height
    private function Height takes real i returns real
        return (v*i-g*i*i)
    endfunction
    
    // SpellPreload just to avoid the first lag cast
    private function SpellPreload takes integer id returns nothing
        call UnitAddAbility(DUMMY,id)
        call UnitRemoveAbility(DUMMY,id)
    endfunction
    
    //**CODE**
    private struct data
        unit u
        integer n
        real i
        real x
        real y
        player p
        group g
        
        static method create takes nothing returns data
            local data d = data.allocate()
            local real a = 0
            local real r = 6.2832/NumberOfBubbles
            local string s = Bubbles
            set d.u = GetTriggerUnit()
            set d.x = GetSpellTargetX()
            set d.y = GetSpellTargetY()
            set d.p = GetTriggerPlayer()
            set d.n = GetUnitAbilityLevel(d.u,SPELL_ID)
            set d.i = 0.0
            if IsUnitEnemy(d.u,GetLocalPlayer())  then
                set s = ""
            endif
            loop
                exitwhen a==6.2832
                call DestroyEffect(AddSpecialEffect(s,d.x+DistanceOfTarget*Cos(a),d.y+DistanceOfTarget*Sin(a)))
                set a = a + r
            endloop
            return d
        endmethod
                
        method onDestroy takes nothing returns nothing
            call ReleaseGroup(.g)
        endmethod
endstruct

    private function UnitMove takes nothing returns nothing
            local data d = GetTimerData(GetExpiredTimer())
            local unit u = GetEnumUnit()
            call SetUnitFlyHeight(u,Height(d.i), 0.00 )
            call UnitDamageTarget(d.u,u,DAMAGE(d.n),false,false,A_TYPE,D_TYPE,WEAPON_TYPE_WHOKNOWS)
            call DestroyEffect(AddSpecialEffectTarget(TargetFX,u,"origin"))
            if not IsUnitPaused(u) then
                call PauseUnit(u,true)
            endif
            set u = null
    endfunction
    
    private function SlowUnits takes nothing returns nothing
            local unit u = GetEnumUnit()
            call PauseUnit(u,false)
            call SetUnitPathing(u,true)
            call IssueTargetOrder(DUMMY,"slow",u)
            set u = null
    endfunction
    
    private function callback takes nothing returns nothing
            local timer   t = GetExpiredTimer()
            local data    d = GetTimerData(t)
            call ForGroup(d.g,function UnitMove)
            set d.i = d.i + INTERVAL
            if d.i > DURATION then
                call ReleaseTimer(t)
                call UnitAddAbility(DUMMY,SLOW_ID)
                call SetUnitAbilityLevel(DUMMY,SLOW_ID,d.n)
                call ForGroup(d.g,function SlowUnits)
                call UnitRemoveAbility(DUMMY,SLOW_ID)
                call d.destroy()
            endif
            set t = null
    endfunction
                
   private function Filt takes nothing returns boolean
            local unit u = GetFilterUnit()
            local data d = GetTimerData(GetExpiredTimer())
            if IsUnitEnemy(u,d.p) and GetWidgetLife(u)>0.405 and not IsUnitType(u,UNIT_TYPE_STRUCTURE) then
                call PauseUnit(u,true)
                call SetUnitPathing(u,false)
                set u = null
                return true
            endif
            set u = null
            return false
   endfunction
                
    private function erruption takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local data  d = GetTimerData(t)
            set d.g = NewGroup()
            call DestroyEffect(AddSpecialEffect(Erruption,d.x,d.y))
            call GroupEnumUnitsInRange(d.g,d.x,d.y,RADIUS,Condition(function Filt))
            if FirstOfGroup(d.g) == null then
                call ReleaseTimer(t)
            else
                call TimerStart(t,INTERVAL,true,function callback)
            endif
            set t = null
    endfunction

    private function onEffect takes nothing returns boolean
            local data d  = data.create()
            local timer t = NewTimer()
            call SetTimerData(t,d)
            call TimerStart(t,WAIT,false,function erruption)
            set t = null        
            return false
    endfunction

    private function Init takes nothing returns nothing
        call GT_AddStartsEffectAction( function onEffect,SPELL_ID)
        call SpellPreload(SLOW_ID)
    endfunction
endlibrary


View attachment Torrent.w3x
 

Tyman2007

Ya Rly >.
Reaction score
74
2) Copy the TidalErruption.mdx model (if you do not already have it) into your map
don't forget to mention to change the path to TidalErruption.mdx instead of War3Imported/TidalErruption.mdx
useful reminder for people who don't usually go importing things.

private constant real INTERVAL = 0.025
change it to 0.3125
works a bit better and isn't recommended to have intervals below 0.03
unit array U[5]
any particular reason for an array size of 5? Never found the use of that..

Ah well.. I suck at jass, but that's all I've noticed.

To the left of the title name there's a category called prefix. Select spell. It will make the title look a bit better :p
 

muzk

Member
Reaction score
3
I'll change it :)

I used a random number, its like when you create a array global variable, it ask you for the Size.

pd: 0.03125 right? you said 0.3125
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
Did you realize that when no unit is being torrented, it lags heavily.
Also, use group instead of array for units.
 

muzk

Member
Reaction score
3
Thx, didnt realize. Solved :)

So I should save the units in a group and then do ForGroup to do the move?

Well, I'll actualice later, if I use groups then I should use group utils.
 

BlackRose

Forum User
Reaction score
239
  • Argh. A DotA spell - I thought they were extinct from here :p
  • Indent, serious, I can't stand to read those globals as it is. Indent/spacing/empty lines really help a lot. I suggest looking at TimeWarp by Bribe (my god is it Berb-like).
  • You can inline the actions into the conditions, a simple check to see if the cast ability id was X, if so, do whatever is in the actions trigger.
  • You should make preloading a configurable, some people might not want to preload. Heck, people can just remove it themselves if they don't want it. So ignore this.
  • No need to check if a unit does not have locust, locust units can't be enum'd like that.
  • I might suggest AutoFly - to remove the constant add/remove.
  • [ljass]exitwhen a==2*bj_PI[/ljass] -> 6.28? o.o?
  • I'm stupid and don't understand maths. I would kind of like the height to be customized like, HEIGHT = 2000. DURATION = 1.50. It's just.... easier.
  • RANGE or RADIUS? I think RADIUS sounds better. But who cares?
  • Why not use TimerUtils or KeyTimers2 instead of the old ABC?
  • You don't need the [Spell] prefix on this - use the forum prefix. Ask some mod to change it.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
You can inline the actions into the conditions, a simple check to see if the cast ability id was X, if so, do whatever is in the actions trigger.
Go for GT, if you wanted to do so. :)
 

BlackRose

Forum User
Reaction score
239
1) It's common courtesy to link the required resources to their source.

2) Spells dont need to use scopes, you can use a library and declare all requirements there. But if you intend to stick with a scope, have the requirements listed in some sort of spell header or: [ljass]// requires GroupUtils, TimerUtils, GT, DummyCaster, AutoFly[/ljass].

3) Dota is DotA.

4) These globals can be declared in a second global blocks after the customisable functions - there you don't need to warn users. Also a safety line declaring where the spell functions start is also common practice.

JASS:
        //Internal things, don´t touch them!!
        private constant real v            = 4*MAX_HEIGHT/DURATION
        private constant real g            = 4*MAX_HEIGHT/(DURATION*DURATION)
        private constant real callbacks    = DURATION/INTERVAL


5) Since no one is gonna use the create method outside as a spell, you can change [ljass]GetOwningPlayer(d.u)[/ljass] to [ljass]GetTriggerPlayer()[/ljass] - as it is faster (PurgeAndFire told me?)

6) I know DotA pauses units in the Torrent (I think), but pausing units is a terrible way of going about, buffs and other things are paused along with it I believe. I think stunning it might work. This one is open to other ideas.

Although what will happen if another spell hits these Torrented units and unpause them?

7) Why not set [ljass]set d.i = d.i + INTERVAL[/ljass] in [ljass]Torrent_callback[/ljass]?

8) Do all those private functions really need the Torrent prefix before them?

9) Any way to configure the distance the bubbles are from the target point? And how many of them there are?
 

muzk

Member
Reaction score
3
1) It's common courtesy to link the required resources to their source.
Ok, I did

2) Spells dont need to use scopes, you can use a library and declare all requirements there. But if you intend to stick with a scope, have the requirements listed in some sort of spell header or: // requires GroupUtils, TimerUtils, GT, DummyCaster, AutoFly.
Changed

4) These globals can be declared in a second global blocks after the customisable functions - there you don't need to warn users. Also a safety line declaring where the spell functions start is also common practice.
Changed

5) Since no one is gonna use the create method outside as a spell, you can change GetOwningPlayer(d.u) to GetTriggerPlayer() - as it is faster (PurgeAndFire told me?)
Yes, sounds good. Changed.

6) I know DotA pauses units in the Torrent (I think), but pausing units is a terrible way of going about, buffs and other things are paused along with it I believe. I think stunning it might work. This one is open to other ideas.

I know, I dont like Pause too. If some1 wants to remove the pause thing he needs a stun ability with hero duration equal to DURATION (duration of torrent). So , if som1 change DURATION he has to change the Stats - Hero Duration of the stunning ability.

Should I implement that??

Although what will happen if another spell hits these Torrented units and unpause them?
Well, in UnitMove function :
JASS:
if not IsUnitPaused(u) then
    call PauseUnit(u,true)
endif
 

Angel_Island

Much long, many time, wow
Reaction score
56
I know, I dont like Pause too. If some1 wants to remove the pause thing he needs a stun ability with hero duration equal to DURATION (duration of torrent). So , if som1 change DURATION he has to change the Stats - Hero Duration of the stunning ability.

Should I implement that??

You could use a storm bolt that stuns forever and then after DURATION, you remove the stun buff.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top