Spell Vortex

Steel

Software Engineer
Reaction score
109
Vortex
Demo Map
Note: Code is at the bottom
Note II: This is a submission for the Spells section

vortex.jpg

Background: I basically designed this for my own map and put some work into it and I figured it may be useful for others. The spell is completely in JASS; GUI people don't worry its so easy to use! I've included many steps and comments to follow to help you to put this in your map. There were a few things that I hadn't ever tried before, so some of the code might be a bit sloppy (Dealing with the trigger addition). I still have a bit of timing issues to work out with the pausing, I had some issues with the spell working when I paused at improper times. Ill be tweaking and updating this. If there are memory leaks let me know I did probably miss some in the handles, I need to work on cleanup.

Spell Notes: This spell is very versitile. There are a lot of things you can edit without touching the actual code of the spell. There are several constant functions that you can modify, what the targets are, the damage, the effects displayed, etc. This makes the spell extremely easy to change even for people who don't know JASS.

What it does: The spell is based around an AoE target (Blizzard). The units are taken from their position and "removed" and put into the caster. When they are stored into the caster the unit take damage over time, but as a result the caster suffers strain from holding the units. Now, this is not like devour, the spell has 2 parts. While the units are stored within the caster, when you want you can cast the spell again and it will "spit out" the units to wherever you target them.

If you plan on using this please read the implementation before asking questions.

Map Custom Script:
Code:
function H2I takes handle h returns integer
    return h
    return 0
endfunction


function LocalVars takes nothing returns gamecache
    if udg_steelcache == null then
        call FlushGameCache(InitGameCache("steelcache.w3v"))
        set udg_steelcache = InitGameCache("steelcache.w3v")
    endif
    return udg_steelcache
endfunction
 
function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction
 
function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
         call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function SetHandleString takes handle subject, string name, string value returns nothing
    if value==null then
        call FlushStoredString(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreString(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
function SetHandleBoolean takes handle subject, string name, boolean value returns nothing
    if value==false then
        call FlushStoredBoolean(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreBoolean(LocalVars(), I2S(H2I(subject)), name, true )
    endif
endfunction

function GetHandleBoolean takes handle subject, string name returns boolean
    return GetStoredBoolean(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleString takes handle subject, string name returns string
    return GetStoredString(LocalVars(), I2S(H2I(subject)), name)
endfunction
 
function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTrigger takes handle subject, string name returns trigger
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleEffect takes handle subject, string name returns  effect
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTextTag takes handle subject, string name returns  texttag
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTriggerAction takes handle subject, string name returns triggeraction
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
 
function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

Spell: Vortex
Code:
constant function Vortexabilid takes nothing returns integer
//This is the ability ID
return 'A002'
endfunction

constant function Vortexradius takes nothing returns real
//This is the radius of the spell being cast
return 225.0
endfunction

constant function VortexPretty takes nothing returns boolean
//Make this true if you want the pretty effect
return true
endfunction

constant function VortexPrettyUnit takes nothing returns integer
//This is the Pretty vortex dummy unit, it makes the spell look pretty
return 'h000'
endfunction

constant function Vortexdamage takes nothing returns real
//This value is how much damage the units will received per second multiplied by the level of the spell
//If the units are stored within the caster for 15 seconds with a level 1 spell they will be damaged for
//20*1*15
//The 20 refers to this value 
return 20.0
endfunction


//The following are the strings for the model effects
//I put these here so you users would be able to change how the spell graphics look without
//Worrying about damaging the functionality of the rest of the spell

constant function VortexCreationEffect takes nothing returns string
//This is the effect when you first cast the spell
//This is on the location of your target cast
return "Objects\\Spawnmodels\\NightElf\\NECancelDeath\\NECancelDeath.mdl"
endfunction
constant function VortexStorageEffect takes nothing returns string
//This is the effect that binds to your caster
//This basically is a visual effect showing that units have been stored in the caster
//And that your caster is ready to release the units
return "Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl"
endfunction
constant function VortexDeathEffect takes nothing returns string
//This is the effect when you release the units 
return "Objects\\Spawnmodels\\Undead\\UDeathMedium\\UDeath.mdl"
endfunction


//************************************************************************************
//****       DO NOT CHANGE ANYTHING BELOW UNLESS YOU KNOW WHAT YOU ARE DOING      ****
//************************************************************************************
function Trig_Vortex_Conditions takes nothing returns boolean
return GetSpellAbilityId() == Vortexabilid()
endfunction

function Vortex_Units takes nothing returns boolean
    return (IsUnitEnemy(GetFilterUnit(),bj_groupEnumOwningPlayer))
endfunction

function Vortex_Release takes unit caster, location loc returns nothing
local unit u
local integer i = 1
local effect e
local location casterloc = GetUnitLoc(caster)
local real damage = (Vortexdamage()*GetHandleInt(caster, "lvl"))*GetHandleInt(caster, "duration")
        loop
            set u = GetHandleUnit(caster, "vortex"+I2S(i))
            exitwhen u==null
            call SetUnitPosition(u, GetLocationX(loc)+GetRandomReal(1,225),GetLocationY(loc)+GetRandomReal(1,225) )
            call ShowUnit(u, true)
            call SetHandleHandle(caster, "vortex"+I2S(i), null)
            call UnitDamageTarget(caster, u, damage, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS) //Damages our target
            set i = i+1
        endloop
        set e = AddSpecialEffectLoc(VortexDeathEffect(), loc)
        call DestroyEffect(GetHandleEffect(caster,"vortexe"))
        call SetHandleBoolean(caster, "vortexb", false)        
set u = null
call DestroyEffect(e)
set e = null
call RemoveLocation(casterloc)
set casterloc = null
endfunction

function Vortex_Damage takes nothing returns nothing
local unit caster = GetHandleUnit(GetTriggeringTrigger(), "caster")
call SetHandleInt(caster, "duration", GetHandleInt(caster, "duration")+1)
if (GetUnitState(caster, UNIT_STATE_LIFE)) < 6 then
call DestroyTrigger(GetTriggeringTrigger())
call Vortex_Release(caster, GetUnitLoc(caster))
call SetHandleReal(caster, "damage", 0)
endif
call SetUnitState(caster, UNIT_STATE_LIFE, GetUnitState(caster, UNIT_STATE_LIFE)-(GetHandleReal(caster, "damage")+1.5))
call SetHandleReal(caster, "damage", GetHandleReal(caster, "damage")+(GetHandleInt(caster, "lvl")*2.5))
set caster = null
endfunction

function Vortex_Death takes nothing returns nothing
local unit u = GetTriggerUnit()
call FlushHandleLocals(u)
set u = null
endfunction

function Trig_Vortex_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit u 
local unit v
local location casterloc = GetUnitLoc(caster)
local location targetloc = GetSpellTargetLoc()
local group g = CreateGroup()
local boolexpr b = Condition(function Vortex_Units)
local integer i = 1
local effect e
local trigger t = CreateTrigger()
local trigger death = CreateTrigger()
local real safeduration = 500.0
call SetHandleInt(caster,"lvl", GetUnitAbilityLevel(caster, Vortexabilid()))
set bj_groupEnumOwningPlayer = GetOwningPlayer(caster)

    
    call TriggerRegisterPlayerUnitEvent(death, GetOwningPlayer(caster), EVENT_PLAYER_UNIT_DEATH, null)
    call TriggerAddAction(death, function Vortex_Death)


    if (GetHandleBoolean(caster, "vortexb")) then
        call Vortex_Release(caster, targetloc)
        set e = AddSpecialEffectLoc(VortexDeathEffect(), targetloc)
        call DestroyEffect(GetHandleEffect(caster,"vortexe"))
        call SetHandleBoolean(caster, "vortexb", false)
        call DestroyTrigger(GetHandleTrigger(caster, "vortext"))
    else

        call GroupEnumUnitsInRange(g, GetLocationX(targetloc), GetLocationY(targetloc), 225, b)
        call DestroyBoolExpr(b)
        set b = null
        loop
        set u = FirstOfGroup(g)
            exitwhen u == null
            call SetHandleHandle(caster, "vortex"+I2S(i), u)

                if (VortexPretty()) then 
                set v = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), VortexPrettyUnit(), GetUnitX(u), GetUnitY(u), 0) 
                call SetHandleHandle(caster, "vortexp"+I2S(i),v)
                call IssuePointOrder(v, "move", GetUnitX(caster), GetUnitY(caster))
                call UnitApplyTimedLife(v, 'BTLF', (DistanceBetweenPoints(casterloc, GetUnitLoc(u))/500))
                endif
            call ShowUnitHide(u)
            call GroupRemoveUnit(g,u)
            set i = i+1
        endloop
        set e = AddSpecialEffectLoc(VortexCreationEffect(), targetloc)
        if (i==1) then
        return
        endif
        call PolledWait(.5)
        call SetHandleHandle(t, "caster", caster)
        call SetHandleHandle(caster, "vortext", t)
        call TriggerRegisterTimerEventPeriodic( t, 1.00 )    
        call TriggerAddAction(t, function Vortex_Damage)
        call PauseUnit(caster, true)
        call SetHandleBoolean(caster, "vortexb", true)
        call SetHandleBoolean(caster, "vortexa", true)
        call PolledWait(.5)
        call SetHandleHandle(caster, "vortexe", AddSpecialEffectTarget(VortexStorageEffect(), caster, "overhead"))
    endif

    if (VortexPretty() and GetHandleBoolean(caster, "vortexb")) then    
    //call PauseUnit(caster, true)     
    call PolledWait(1.3)
    call PauseUnit(caster, false)
    endif

set v = null
set u = null
set caster = null
call RemoveLocation(casterloc)
call RemoveLocation(targetloc) 
set casterloc=null
set targetloc=null   
call DestroyGroup(g)
set g = null
call DestroyEffect(e)
set e = null
call PolledWait(safeduration)
call FlushHandleLocals(caster)
call DestroyTrigger(t)
set t = null
call DestroyTrigger(death)
set death = null
endfunction

//===========================================================================
function InitTrig_Vortex takes nothing returns nothing
    set gg_trg_Vortex = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Vortex, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Vortex, Condition( function Trig_Vortex_Conditions ) )
    call TriggerAddAction( gg_trg_Vortex, function Trig_Vortex_Actions )
endfunction
 

Attachments

  • Vortex.w3x
    25.1 KB · Views: 478

SFilip

Gone but not forgotten
Reaction score
634
First of all use attachments instead of external links.
Also some variables are not nulled, namely e and casterloc in Vortex_Release.

And doing a local trigger in the init function is simply...wrong.
If you really want it local then make a separate name, never replace globals in Jass.
 

Steel

Software Engineer
Reaction score
109
First of all use attachments instead of external links.
Also some variables are not nulled, namely e and casterloc in Vortex_Release.

And doing a local trigger in the init function is simply...wrong.
If you really want it local then make a separate name, never replace globals in Jass.
Fixed those variables.

I put that local trigger there so JASSCraft wouldn't kick back any error. I didn't want to put
globals
trigger ....
endglobals
either, so I just did that. I simply forgot to remove it.

Yes I know this is a double post, but you can't edit to add an attachment so I'm adding it here.

[Edit: Whoops didn't know about the advanced options!]
 

Attachments

  • Vortex.w3x
    25.1 KB · Views: 298

Mr Zero

Junior Regular (Got the T-shirt)
Reaction score
64
Well what can I say... :eek:

It is a nice spell, looks good to me. I like it.

You work hard on it, you deserve your +rep :p
 

Steel

Software Engineer
Reaction score
109
Hehe thanks. It didn't take that long, maybe an hour to code, then I made all the constant functions so it would be easier to change. I still need to try and figure out why if I try and pause the casting unit anytime before where I actually do pause him the spell bugs. After he gets unpaused he casts the spell again, killing the spell. I don't know if its the animation isn't finished or something, but sometimes it can bog the looks of the system.
 

emjlr3

Change can be a good thing
Reaction score
395
storing things on units are bad, fix that
fix not nulling leaks

GetUnitState(caster, UNIT_STATE_LIFE)) < 6

6 should be .405

u should have the caster damage himself, not just lower life

and the spitting out should destroy the triggernot 500s later after your safe duration, lol

update that and its fine
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
OooOOoo.. Cool effects o_O

I wish it didn't drain the life so quickly and disable the control, but there has to be a disadvantage. :D If you fix the things as emjlr3, then it will make it better. Nice spell though, I see that you put a lot of effort into it, so +rep.
 

Steel

Software Engineer
Reaction score
109
storing things on units are bad, fix that
fix not nulling leaks
I don't understand this, could you elaborate.

GetUnitState(caster, UNIT_STATE_LIFE)) < 6

6 should be .405
I don't see the purpose of this. The damage is enough to get it to work with the damage the unit receives based on the spell

u should have the caster damage himself, not just lower life
Fixed that.

and the spitting out should destroy the triggernot 500s later after your safe duration, lol
It already does:
if (GetHandleBoolean(caster, "vortexb")) then
call Vortex_Release(caster, targetloc)
set e = AddSpecialEffectLoc(VortexDeathEffect(), targetloc)
call DestroyEffect(GetHandleEffect(caster,"vortexe"))
call SetHandleBoolean(caster, "vortexb", false)
call DestroyTrigger(GetHandleTrigger(caster, "vortext"))
else

update that and its fine[/QUOTE]
 

emjlr3

Change can be a good thing
Reaction score
395
remove the wait then in the first function and the destroy trigger

u store all ur info to the caster, this is not allowed, things couldinterupt, since ur storing string does not go along with the spells name, and it leaks
 

Tom Jones

N/A
Reaction score
437
>And doing a local trigger in the init function is simply...wrong.

Care to clarify?
 

Steel

Software Engineer
Reaction score
109
remove the wait then in the first function and the destroy trigger

u store all ur info to the caster, this is not allowed, things couldinterupt, since ur storing string does not go along with the spells name, and it leaks

Again I still do not see why. Interrupt? In the sense of what? A stun? Yes you may want that, but I felt it wasn't necessary. The only thing that could botch it up would be a flush, which doesn't occur, and wouldn't unless the same gamecache was used. How do the storing strings not go allong with the spell name? They are all "vortex<var>". I refuse to use global variables when gamecache will suffice.
 

SFilip

Gone but not forgotten
Reaction score
634
> Care to clarify?
Initially he used a local variable with the name gg_trg_Vortex in the init function. Overloading variables is deprecated in Jass and should be avoided overall.
 

Steel

Software Engineer
Reaction score
109
Needed to note something down that emjlr3 mentioned. Using a unit to store information on does not cause leaks. Again, it does not cause leaks. Using Get and Set for handles on a unit does not leak...Now that that is clarified, the reason why it does not leak is upon a unit's death you need to flush all the information stored within it. If the unit dies and you never cleaned it THEN it would leak.

So please do not let that hold back the process of letting this spell be accepted.
 

Sooda

Diversity enchants
Reaction score
318
I tested it and it looked interesting (Concept is very good.). Did I missread or you never use real value in constant function what determines ability AoE ? Boolexpr e needs to be nulled after destroying it also (When there is no units [i==1] or there are units- in the end of function.) Otherwise very good work. It looks complicated (JASS). I did these remarks by reading JASS what was posted in forum not from map triggers (If you have updated it update JASS in forum also.).
 

emjlr3

Change can be a good thing
Reaction score
395
if u had stored the stuff onto say, GetAttTable(hero)+"some string", you could remove the leaks yourself if the unit dies, that is what I meant, else it leaks some for every unit and will stay that way unless the user flushes it himself, which will not often happen, especially if someone does not know JASS in the first place
 

Steel

Software Engineer
Reaction score
109
Edited again

Well I decided to add a death checking trigger to make sure there are no leaks, just in case sometime using the spell doesn't know how to clean a unit.

Also took care of the single boolexpr I forgot to nullify.
 
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