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.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top