Spell [vJASS]Torrent v.1.1b [Inspired by DotA]-baassee

baassee

Member
Reaction score
5
[vJASS]Torrent v.1.1d [Inspired by DotA]-baassee

UPLOADING ATTACHMENTS BUGS FOR ME SO GO TO THIS LINK TO DOWNLOAD THE MAP!!!
http://www.hiveworkshop.com/forums/spells-569/vjass-torrent-v1-1d-inspired-dota-204299/?prev=status%3Dp%26u%3Dbaassee

Yeah bla bla bla bla about originality and such, haven't uploaded a spell or anything in a while and this got heavily requested out there so there you go!

Basic it's based on this spell but without the slow part. That's about it...

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 and dealing damage.

Level 1 - 120 damage.
Level 2 - 180 damage.
Level 3 - 240 damage.
Level 4 - 300 damage.

This is made purely in vJASS thus a vJASS compiler is needed. Either you use JASSHelper directly or just JNGP, kthxbye!

How to import is inside the code but for you guys who don't know how to read, I'll place it here as well along with all requirements (a lot hehe).

How to Import:
*How to import
1. Import all required Libraries and create the object data (AIDS, Status, DummyCaster)
2. Copy this code and paste into a trigger or copy the trigger.
3. Copy the ability and change the ABILID to the new id given.
4. Give the spell to a hero, credit me and the authors in the credits section above and enjoy!



Credits:
*Status, AIDS, T32, GTrigger, DummyCaster - Jesus4Lyf
*TimerUtils - Vexorian
*GroupUtils - Rising_Dusk
*Autofly - Azlier
*TidalEruption model - Unknown Author
*Allowing me to use his testmap! - -Berz-

Also remember to give me credits if the spell is used along with the authors above.

Wooah this spell requires a lot, common libraries I guess, enjoy the quote.

Requires Status, T32, TimerUtils, AutoFly optional GT, SpellEffectEvent, GroupUtils
Thus Status requires AIDS and DummyCaster as well.

All required libraries are inside the map.

In-game screenie
torrentv10.png

JASS:
scope Torrent
/*
    Requires Status, T32, TimerUtils, AutoFly
        Thus Status requires AIDS and DummyCaster as well.
        
    *Torrent v1.1d by baassee
        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 and dealing damage.

        Level 1 - 120 damage.
        Level 2 - 180 damage. 
        Level 3 - 240 damage.
        Level 4 - 300 damage.
        
    *FAQ about code
        The code contains two structs, one for just the initial delay of two seocnds
        and the second for the actual eruption.
        
        I used GT as I am used to it.
        
        I used Status because it's the best out there at the moment.
        
        I used AutoFly(Aids Version) because I had AIDS already with Status
        
        I recommend though using UnitIndexer by Nestharus with the AIDS BC
        Can be found at this link below:
        <a href="http://www.hiveworkshop.com/forums/jass-functions-413/system-unit-indexer-172090/" target="_blank" class="link link--external" rel="nofollow ugc noopener">http://www.hiveworkshop.com/forums/jass-functions-413/system-unit-indexer-172090/</a>
        
        Yes I didn&#039;t want the spell to stack with itself because it would have looked ridiculous with the height changes.
        And I was lazy so that&#039;s the reason behind the stackgroup.
        
        Typical parabola function, first made by Moyack and Spec(?) anyway it&#039;s way too common to be credited.
        
        And there are two boolean constants
            one for pausing or not pausing (will stun if not paused)
            second for preloading or not preloading
        
    *Information
        Yes this spell is based on DotA&#039;s Admiral&#039;s Torrent spell. It&#039;s exactly the same except
        the slowing part and the height (couldn&#039;t find the maximum height at playdota). 
        Also added an option to pause or not pause the units thrown up in the air.
        
        I cannot use any custom icon although I recommend using this icon:
        <a href="http://www.hiveworkshop.com/forums/icons-541/btntorrent-162809/?prev=search%3Dtorrent%26d%3Dlist%26r%3D20" target="_blank" class="link link--external" rel="nofollow ugc noopener">http://www.hiveworkshop.com/forums/icons-541/btntorrent-162809/?prev=search=torrent&amp;d=list&amp;r=20</a>
        
        The TidalEruption model was found in a spellpack by RMX, no credits were actually given in that spellpack
        thus I don&#039;t know who&#039;s the author of the model but it&#039;s not mine.
        
    *Credits
        *Status, AIDS, T32, GTrigger, DummyCaster - Jesus4Lyf
        *TimerUtils - Vexorian
        *GroupUtils - Rising_Dusk
        *Autofly - Azlier
        *TidalEruption model - Unknown Author
        
        Also remember to give me credits if the spell is used along with the authors above.
    
    *How to import
        1. Import all required Libraries and create the object data (AIDS, Status, DummyCaster)
        2. Copy this code and paste into a trigger or copy the trigger.
        3. Copy the ability and change the ABILID to the new id given.
        4. Give the spell to a hero, credit me and the authors in the credits section above and enjoy!
        
    *Changelog
        v1.0 - Released
        v1.1 - Made GTrigger optional along with making SpellEffectEvent optional as well.
             - Made Damage optional.
             - Made GroupUtils optional.
             - Changed Condition into Filter.
        v1.1b- Fixed a mistake with optionalizing GT and SpellEffectEvent.
        v1.1c- Forgot to static if out GroupEnumUnitsInArea which made it uncompileable.
        v1.1d- Removed GetWidgetLife(u) &gt; 0.405 and just because of &quot;I could add this&quot;
               I added a FoG loop instead of groupenumeration + filter.
               Also made the filter among the configuration functions.
        
    **************************************************************************************************************************
    
    *Configurables
*/

    //CONSTANTS
    
    globals
        //The abilityid when pasting into the object editor
        private constant integer ABILID         = &#039;A000&#039;
        
        //the ground effect that will be on the ground during the 2 seconds delay
        private constant string GROUNDEFFECT    = &quot;Objects\\Spawnmodels\\Other\\IllidanFootprint\\IllidanWaterSpawnFootPrint.mdl&quot;
        
        //the splash effect when the delay reached 0.
        private constant string SPLASHEFFECT    = &quot;war3mapImported\\TidalEruption.mdx&quot;
        
        //the attacktype of the damage
        private constant attacktype ATT         = ATTACK_TYPE_NORMAL
        
        //damagetype of the damage
        private constant damagetype DAM         = DAMAGE_TYPE_MAGIC
        
        //weapontype of the damage
        private constant weapontype WEA         = null
        
        //if you want the spell to pause the units or not
        //if false then it will stun them instead
        private constant boolean PAUSE          = false
        
        //preload the specialeffects used?
        private constant boolean PRELOAD        = true
        
        //don&#039;t touch this, it&#039;s because it would look stupid without it
        //and I&#039;m lazy of course
        private group stackgroup                = CreateGroup()
    endglobals
    
    //FUNCTIONS
    
    //the delay before the eruption
    private function GetDelay takes integer lvl returns real
        return 2. + lvl * 0.
    endfunction
    
    //the aoe of the spell
    private function GetAoe takes integer lvl returns real
        return 200. + lvl * 0.
    endfunction
    
    //the damage dealt
    private function GetDmg takes integer lvl returns real
        return 60. + 60. * lvl
    endfunction
    
    //the duration in the air, taken from playdota.com
    private function GetDur takes integer lvl returns real
        return 1.53 + lvl*0.
    endfunction
    
    //the maximum height the units thrown in the air will reach
    //no idea what the original value is
    private function GetHei takes integer lvl returns real
        return 400. + lvl * 0.
    endfunction
    
    private function GetFilter takes unit u, player p returns boolean
        return not IsUnitType(u, UNIT_TYPE_DEAD) and/*
        */ IsUnitEnemy(u, p) and/*
        */ not IsUnitInGroup(u, stackgroup) and/*
        */ not IsUnitType(u, UNIT_TYPE_STRUCTURE) and/*
        */ not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction

    
// END OF CONFIGURABLES

    //standard parabola formula
    private function ParabolaZ takes real h, real d, real x returns real
        return (4 * h / d) * (d - x) * (x / d)
    endfunction
    
    //eruption struct, the struct throwing units in the air
    private struct Eruption extends array
        unit u //unit in the air
        real counter //counter to cheat the parabola function
        real dur //duration in the air, cheats the parabola function as maxdistance
        real h //maximum height
        
        //recycling variables
        static integer ic = 0
        static thistype r = 0
        thistype rn

        //loop method
        private method periodic takes nothing returns nothing
            set .counter = .counter + T32_PERIOD //increasing &quot;x&quot; in the parabola function
            call SetUnitFlyHeight(.u, ParabolaZ(.h, .dur, .counter), 0.) //sets the new flyheight of the unit
            if .counter &gt;= .dur then //checks if the spell has ended or not
                static if PAUSE then //static if to check if pause is allowed or not
                    call Status[.u].removePause()
                else
                    call Status[.u].removeStun()
                endif
                call SetUnitFlyHeight(.u, GetUnitDefaultFlyHeight(.u), 0.) //return to the default flying height
                call GroupRemoveUnit(stackgroup, .u) //remove the unit from the stackgroup
                call .stopPeriodic()//stop the periodic
                set rn = r //recycling structs
                set r = this // - || -
            endif
        endmethod
        implement T32x
        //create method
        static method create takes unit u, real dur, real h returns thistype
            local thistype this //dunno what to call it, indexing?
            if (r == 0) then
                set ic = ic + 1
                set this = ic
            else
                set this = r
                set r = r.rn
            endif
            set .u = u //unit
            set .counter = 0. //initializing the counter
            set .dur = dur //duration
            set .h = h //height
            call GroupAddUnit(stackgroup, u)//add the unit to the stackgroup
            static if PAUSE then //static if about pausing
                call Status<u>.addPause()
            else
                call Status<u>.addStun() //else we stun the unit
            endif
            call .startPeriodic() //fire the periodic (t32)
            return this
        endmethod
    endstruct
    
    //the delay struct
    private struct Torrent extends array
        unit caster //casting unit
        real dmg //dmg
        real aoe //aoe
        real x //the targetx
        real y //the targety
        real dur //duration for the eruption struct
        real h //height yes we need it here as well
        effect eff //special effect on the ground
        
        //recycle variables
        static integer ic = 0
        static thistype r = 0
        thistype rn
        static thistype dat //for the filter function
        static if not LIBRARY_GroupUtils then
            static group ENUM_GROUP = CreateGroup()
        endif
        
        static method secondphase takes nothing returns nothing
            local timer t = GetExpiredTimer() //track the timer
            local thistype this = GetTimerData(t) //get the data
            local unit u
            call DestroyEffect(.eff) //destroy the effect on the ground
            set dat = this //for the filter method
            call DestroyEffect(AddSpecialEffect(SPLASHEFFECT, .x, .y)) //create the eruption effect
            // just because this is faster...
            static if LIBRARY_GroupUtils then
                call GroupEnumUnitsInArea(ENUM_GROUP, .x, .y, .aoe, null) //enum
            else
                call GroupEnumUnitsInRange(ENUM_GROUP, .x, .y, .aoe, null)
            endif
            loop
                set u = FirstOfGroup(ENUM_GROUP)
                exitwhen null == u
                if GetFilter(u, GetOwningPlayer(.caster)) then
                    static if LIBRARY_Damage then
                        call Damage_Spell(.caster, u, .dmg)
                    else
                        call UnitDamageTarget(.caster, u, .dmg, false,false, ATT, DAM, WEA)
                    endif
                    call Eruption.create(u, .dur, .h)
                endif
                call GroupRemoveUnit(ENUM_GROUP, u)
            endloop
            //endfaster
            call ReleaseTimer(t) //recycle timer
            set t = null //null local
            set u = null
            //recycle part
            set rn = r 
            set r = this
        endmethod
        static method create takes nothing returns thistype
            local thistype this
            local integer lvl = GetUnitAbilityLevel(GetTriggerUnit(), ABILID) //because I want to set it at declaration not after
            local timer t = NewTimer() //get timer
            local string s = GROUNDEFFECT //local player thingie
            if (r == 0) then //indexing part?
                set ic = ic + 1
                set this = ic
            else
                set this = r
                set r = r.rn
            endif
            set .caster = GetTriggerUnit() //casting unit
            set .dmg = GetDmg(lvl) //dmg
            set .aoe = GetAoe(lvl) //aoe
            if IsPlayerEnemy(GetLocalPlayer(), GetTriggerPlayer()) then //to make the effect only show for allies
                set s = &quot;&quot;
            endif
            set .x = GetSpellTargetX() //targetx
            set .y = GetSpellTargetY() //targety
            set .dur = GetDur(lvl) //duration
            set .h = GetHei(lvl) //height
            set .eff = AddSpecialEffect(s, .x, .y) //create the effect
            call SetTimerData(t, this)
            call TimerStart(t, GetDelay(lvl), false, function thistype.secondphase) //fire timer with the delay
            set t = null //nullling
            return this
        endmethod
        
        static method condition takes nothing returns boolean
            static if not LIBRARY_GT then
                static if not LIBRARY_SpellEffectEvent then
                    if GetSpellAbilityId() == ABILID then
                        call thistype.create()
                    endif
                else
                    call thistype.create()
                endif
            else
                call thistype.create()
            endif

            return false
        endmethod
        //initializing
        private static method onInit takes nothing returns nothing
            local trigger t
            static if LIBRARY_GT then
                call TriggerAddCondition(GT_RegisterStartsEffectEvent(CreateTrigger(), ABILID), Filter(function thistype.condition))
            else
                static if LIBRARY_SpellEffectEvent then
                    call RegisterSpellEffectEvent(ABILID, function thistype.condition)
                else
                    set t = CreateTrigger()
                    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                    call TriggerAddCondition(t, Filter(function thistype.condition))
                    set t = null
                endif
            endif
            static if PRELOAD then //to preload or not preload
                call Preload(GROUNDEFFECT)
                call Preload(SPLASHEFFECT)
            endif
        endmethod
    endstruct //end of story
endscope
</u></u>


Changelog:
*Changelog
v1.0 - Released
v1.1 - Made GTrigger optional along with making SpellEffectEvent optional as well.
- Made Damage optional.
- Made GroupUtils optional.
- Changed Condition into Filter.
v1.1b- Fixed a mistake with optionalizing GT and SpellEffectEvent.
v1.1c- Forgot to static if out GroupEnumUnitsInArea which made it uncompileable.
v1.1d- Removed GetWidgetLife(u) > 0.405 and just because of "I could add this"
I added a FoG loop instead of groupenumeration + filter.
Also made the filter among the configuration functions.

Enjoy.

~baassee
When writing the story of your life, don't let anyone else hold the pen.
 

Attachments

  • torrentv1,0.jpg
    torrentv1,0.jpg
    353.1 KB · Views: 426

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
I have a few suggestions:

1. You really don't have to null the timers you get from NewTimer, they are recycled/never destroyed.

2. The custom allocators for the two structs (Eruption, Torrent) seam like an overkill/bloat, but anyway...

3. In the change log it's says "- Made GroupUtils optional.", while in fact it is not, the map won't compile if it is missing or if it's disabled. (this is very easy to fix though)

4. I am not sure why you need a ParabolaZ function, when the movement is linear (up and down).

5. It's good that the spell is well documented/easy to understand, but in places it is slightly overcommented:
set u = null //null the local
return false //return false
set t = null //nullling

6. I am not sure why people do it but they prefix their instance variables with "this." or the shortcut "." while in fact it's valid to omit it. (seams more readable to me)

7. Instead of using the filter argument of the GroupEnumUnits<...>, you can use the FirstOfGroup group enumeration thingie. That way you won't have to create a separate function and a "dat/this" static variable, and I think it's also "faster".
 

Dirac

22710180
Reaction score
147
I have a few suggestions:

1. You really don't have to null the timers you get from NewTimer, they are recycled/never destroyed.
Wrong, you have to null those locals or they'll leak, it has nothing to do with the timer itself, if you set a local to a handle it has to be nulled.

2. The custom allocators for the two structs (Eruption, Torrent) seam like an overkill/bloat, but anyway...
Actually they're fine, i would replace r and rn for a single array r[]. Reason?
JASS:
If 0==r[0] then
set m=m+1
set this=m
else
set this=r[0]
set r[0]=r[r[0]]

4. I am not sure why you need a ParabolaZ function, when the movement is linear (up and down).
In a graph of time elapsed vs unit height it looks like a parabola, so the function is a must.

7. Instead of using the filter argument of the GroupEnumUnits<...>, you can use the FirstOfGroup group enumeration thingie. That way you won't have to create a separate function and a "dat/this" static variable, and I think it's also "faster".
True.
 

baassee

Member
Reaction score
5
I have a few suggestions:

1. You really don't have to null the timers you get from NewTimer, they are recycled/never destroyed.

2. The custom allocators for the two structs (Eruption, Torrent) seam like an overkill/bloat, but anyway...

3. In the change log it's says "- Made GroupUtils optional.", while in fact it is not, the map won't compile if it is missing or if it's disabled. (this is very easy to fix though)

4. I am not sure why you need a ParabolaZ function, when the movement is linear (up and down).

5. It's good that the spell is well documented/easy to understand, but in places it is slightly overcommented:
set u = null //null the local
return false //return false
set t = null //nullling

6. I am not sure why people do it but they prefix their instance variables with "this." or the shortcut "." while in fact it's valid to omit it. (seams more readable to me)

7. Instead of using the filter argument of the GroupEnumUnits<...>, you can use the FirstOfGroup group enumeration thingie. That way you won't have to create a separate function and a "dat/this" static variable, and I think it's also "faster".

1. Yes you have to.

2. It's more efficient.

3. Yeah I saw that now, forgot static if out GroupEnumUnitsInArea.

4. Dirac explained.

5. Yeyeyeye :)

6. this.member is the same as .member? don't get your point here

7. Yeah it is faster due to some benchmarks because GroupEnum creates new threads but the loop is just a single loop. Although with a loop you can hit the oop limit faster so I kept it this way.

Actually they're fine, i would replace r and rn for a single array r[]. Reason?

Array would take more space.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
baassee:

1. 6. this.member is the same as .member? don't get your point here
2. Array would take more space.
3. 1. Yes you have to.


1. This is what I ment:
JASS:
struct S
    integer var

    method M takes nothing returns nothing
        set var = 1      // works
        set .var = 2     // works 
        set this.var = 3 // works
    endmethod

    static method create takes nothing returns S
        local S this  = S.allocate()

        set var = 4      // works
        set .var = 5     // works shortcut for this.var
        set this.var = 6 // works

        return this
    endmethod

    static method create2 takes nothing returns S
        local S notthis = S.allocate()

        set var = 7         // doesn&#039;t work
        set .var = 8        // doesn&#039;t work
        set notthis.var = 9 // works
        
        return notthis
    endmethod

endstruct


2. Well rn is actually an array so... but it's still bloat for me =).
3. You don't... where did you read that you have to null timers you recycle? That's the idea behind the whole recycling of timers: no destroying and no nulling.
 

baassee

Member
Reaction score
5
baassee:

1. 6. this.member is the same as .member? don't get your point here
2. Array would take more space.
3. 1. Yes you have to.


1. This is what I ment:
JASS:
struct S
    integer var

    method M takes nothing returns nothing
        set var = 1      // works
        set .var = 2     // works 
        set this.var = 3 // works
    endmethod

    static method create takes nothing returns S
        local S this  = S.allocate()

        set var = 4      // works
        set .var = 5     // works shortcut for this.var
        set this.var = 6 // works

        return this
    endmethod

    static method create2 takes nothing returns S
        local S notthis = S.allocate()

        set var = 7         // doesn&#039;t work
        set .var = 8        // doesn&#039;t work
        set notthis.var = 9 // works
        
        return notthis
    endmethod

endstruct


2. Well rn is actually an array so... but it's still bloat for me =).
3. You don't... where did you read that you have to null timers you recycle? That's the idea behind the whole recycling of timers: no destroying and no nulling.

1. Oh I see, anyway.
2. Yeah saw that, stupid me.
3. Yes you do to keep the index count low (else it will just increase to infinity).
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
baassee:

1. 3. Yes you do to keep the index count low (else it will just increase to infinity).

1. What do you mean by "index count" of a timer? And btw there is no such thing as infinity in computers =). Perhaps you mean a reference count, but so what if the reference count of the timer overflows it's integer/long/whatever that does the counting, it will go to zero and overflow again... =). Although even a single overflow would take a long time: for example if the reference counter is an unsigned 32 bit integer 0 - 2^32 - 1 and you increment that 50 times per second you would need I think ~2.6 years of gameplay =) to overflow (hm... probably wrong calculations).
 

Dirac

22710180
Reaction score
147
@baseee
in case you didnt notice, "rn" is an array, the fact that it's declared inside a struct and isn't static makes it an array.
JASS:
thistype(0).rn == rn[0]


@Sqqvur
There's a difference between nulling timers and destroying them. This is an example of a proper null function
JASS:
function Test takes nothing returns nothing
local unit u=CreateUnit(...)
local timer t=CreateTimer()
//actions
set u=null
set t=null
endfunction
In my example the unit wasn't removed from the game because i nulled the local, same thing goes to the timer, i just set the value of the variable to null.
You do have to null [ljass]timer t=NewTimer()[/ljass] after use
 

baassee

Member
Reaction score
5
Still better to keep it low to increase performance.

@Dirac

Yeah I posted my mistake in my post above :p
 
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