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: 479

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.
  • Ghan Ghan:
    Howdy
  • 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 Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top