• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Mystic Snake v1.5

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: Dion King
Hey guys, this spell gets requested quite a lot and it is one of my personal favourites, it is Mystic Snake from Medusa in DotA, for those of you that do not know what it does, it fires a snake towards an enemy which will damage and steal his mana, then increase its own damage by 20% and bounce to another enemy, it will repeat this untill there is no valid target nearby or its maximum bounce number is met, at which point if the caster is alive, it will fly back to him and restore mana equal to what it stole. It can only effect each enemy once per snake.

I am aware this is not an original idea but it is one of my favourites anyway.

Give credits if you use this spell in your map! (See credits below)

Magtheridon96 for helping with efficiency and finding bugs.
VERY big thanks to Maker for correcting an error i couldn't figure out and giving suggestions for improving the spell.
mateuspv for the snake icon and model file.

Magheridon96 for efficiency and review.
Maker for helping with an error i couldn't figure out.
DotA for the idea, icon and snake model.
mateuspv for extracting the icon and snake model.
Me, Zaio for the coding.

Create 1 timer array with size 1 called TimerUtils_timer
Create 1 integer called TimerUtils_int
Create 1 hashtable called TimerUtils_hash

Copy ALL of the following code into your maps header (the map icon above all triggers and folders)

NOTE: Do NOT give me credits for TimerUtils, i did not make it.
JASS:
//*************************************************
//*
//*   TimerUtils (Jass Version)
//*   v1.3.2.0
//*   By Magtheridon96
//*
//*   Original version by Vexorian.
//*   All the functions have O(1) complexity.
//*
//*   API:
//*       - function NewTimer takes nothing returns timer
//*           - Returns a new timer from the stack
//*       - function ReleaseTimer takes timer t returns nothing
//*           - Throws a timer back into the stack
//*       - function SetTimerData takes timer t, integer value returns nothing
//*           - Attaches a value to a timer
//*       - function GetTimerData takes timer t returns integer
//*           - Returns the attached value
//*
//*************************************************

    function SetTimerData takes timer t, integer value returns nothing
        call SaveInteger(udg_TimerUtils_hash,GetHandleId(t),0,value)
    endfunction

    function GetTimerData takes timer t returns integer
        return LoadInteger(udg_TimerUtils_hash,GetHandleId(t),0)
    endfunction

    function NewTimer takes nothing returns timer
        if 0==udg_TimerUtils_int then
            return CreateTimer()
        endif
        set udg_TimerUtils_int = udg_TimerUtils_int - 1
        return udg_TimerUtils_timer[udg_TimerUtils_int]
    endfunction

    function ReleaseTimer takes timer t returns nothing
        call PauseTimer(t)
        set udg_TimerUtils_timer[udg_TimerUtils_int] = t
        set udg_TimerUtils_int = udg_TimerUtils_int + 1
    endfunction

    function InitTrig_TimerUtils takes nothing returns nothing
        set udg_TimerUtils_hash = InitHashtable()
    endfunction
JASS:
//Mystic Snake by Zaio - http://www.hiveworkshop.com/forums/spells-569/mystic-snake-v1-5-a-204984/
//===================================================================================================================================
//To Import this spell:
//1 - Make sure you have installed TimerUtils (instructions on main page in the hidden tap called IMPORTANT:Requires)
//2 - Copy over the ability and dummy and edit them as you want
//3 - Create a hashtable named hash (if you already one named hash, skip to 4) by opening up variable editor (Ctrl + B)
//4 - Make sure "automatically create unknown variables when pasting trigger data" is enabled in map preferences
//5 - Copy over this trigger to your map
//6 - Edit the 2 configurables below for your map

constant function MSDID takes nothing returns integer
    return 'h000' // The dummy's ID (ctrl + d in object editor)
endfunction

constant function MSAID takes nothing returns integer
    return 'A000' // The ability's ID (ctrl + d in object editor)
endfunction

//7(Optional) - If you want the snake icon and model, export the imports used in this map and save their name along with their path into a word document (or something similar) open up your map and import the models, set their custom paths to the ones you wrote down earlier
//8(Optional) - Change the ability and dummy icons to ReplaceableTextures\CommandButtons\BTNMysticSnakeNew.blp then change the dummy's model file to MysticSnake.mdl
//9(Optional) - Change the tooltips (highly recommended) to your preference, they have been poorly done in the test map because it is all about the map they are running in, almost all maps use different base values for health on heros and units, therefore the tooltip will need to be changed accordingly.
//10(Optional) - Customize the dummy as wanted, reduce/increase its sight radius etc and change the configurables below.
//11 - DONE!
//======================================== CONFIGURABLES ============================================================================
function MSIBN takes nothing returns integer
    return 3 // The base number of jumps + (level of ability * MSABN)
endfunction

function MSABN takes nothing returns integer
    return 1 // The extra number of bounces per level (e.g. if this is set to 1 and MSIBN is set to 3, level 1 will bounce 4 times, level 2 will bounce 5 times etc)
endfunction

function MSIMB takes nothing returns real
    return 70.00 // The mana burn base amount + (level of ability * MSAMB)
endfunction

function MSAMB takes nothing returns real
    return 30.00 // The mana burn increment per level (e.g. if this is set to 30.00 and MSIMB is set to 70.00, level 1 will burn upto 100 base mana, level 2 upto 130 mana etc)
endfunction

function MSPDI takes nothing returns real
    return 120.00 // This is the percentage of mana burn increase (if set to 120.00 = 20% increase per bounce)
endfunction

function MSEOM takes nothing returns string
    return "Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl" // The effect path if the snake returns to the caster
endfunction

function MSEOT takes nothing returns string
    return "Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl" // The effect path if the snake hits an enemy
endfunction

function MSEAM takes nothing returns string
    return "chest" // The attachment point for the effect if the snake returns to the caster
endfunction

function MSEAT takes nothing returns string
    return "overhead" // The attachment point for the effect if the snake hits an enemy
endfunction

function MSDPS takes nothing returns real
    return 300.00 // This is the distance the snake will travel per second
endfunction

function MSRAOE takes nothing returns real
    return 100.00 // This is the max distance the snake must be to return to the caster to restore mana
endfunction

function MSAAOE takes nothing returns real
    return 50.00 // This is the max distance the snake must be to attack a unit and steal mana
endfunction

function MSSR takes nothing returns real
    return 400.00 // This is the search radius the snake will check units in after it has mana burned a different unit
endfunction

//============================================ END CONFIGURABLES ==========================================================
//============================= DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING! =================================

function MSFilter takes nothing returns boolean
    local unit u = GetFilterUnit()
    local boolean b = not IsUnitInGroup(u, udg_Temp_Group) and IsUnitEnemy(u, GetOwningPlayer(udg_Temp_Unit)) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not (GetUnitAbilityLevel(u, 'Bams') > 0) and not (GetUnitAbilityLevel(u, 'BHds') > 0)
    set u = null
    return b
endfunction

function Loop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local integer bn = LoadInteger(udg_hash, id, 3)
    local unit s = LoadUnitHandle(udg_hash, id, 1)
    local unit u
    local unit u2
    local unit u3
    local unit u4 = LoadUnitHandle(udg_hash, id, 2)
    local real x
    local real x2
    local real x3
    local real y
    local real y2
    local real y3
    local real a
    local real d
    local real m
    local real m2
    local real dx
    local real dy
    local group g
    local real dmg
    if 0 >= bn or u4 == null then
      set u2 = LoadUnitHandle(udg_hash, id, 0)
      if IsUnitType(u2, UNIT_TYPE_DEAD) then
        set g = LoadGroupHandle(udg_hash, id, 4)
        call DestroyGroup(g)
        set g = null
        call ReleaseTimer(t)
        call KillUnit(s)
        call FlushChildHashtable(udg_hash, id)
      else
        set x3 = GetUnitX(u2)
        set y3 = GetUnitY(u2)
        set x2 = GetUnitX(s)
        set y2 = GetUnitY(s)
        set dx = x3 - x2
        set dy = y3 - y2
        set d = SquareRoot(dx * dx + dy * dy)
        if d <= MSRAOE() then
          set m = LoadReal(udg_hash, id, 6)
          call SetUnitState(u2, UNIT_STATE_MANA, (GetUnitState(u2, UNIT_STATE_MANA) + m))
          call DestroyEffect(AddSpecialEffectTarget(MSEOM(), u2, MSEAM()))
          set g = LoadGroupHandle(udg_hash, id, 4)
          call DestroyGroup(g)
          set g = null
          call ReleaseTimer(t)
          call KillUnit(s)
          call FlushChildHashtable(udg_hash, id)
        else
          set a = Atan2(y3 - y2, x3 - x2)
          set x = x2 + MSDPS() / 33. * Cos(a)
          set y = y2 + MSDPS() / 33. * Sin(a)
          call SetUnitX(s, x)
          call SetUnitY(s, y)
          call SetUnitFacing(s, (bj_RADTODEG * a))
        endif
      endif
      set u2 = null
    else
      set x = GetUnitX(s)
      set y = GetUnitY(s)
      set u = LoadUnitHandle(udg_hash, id, 2)
      set x2 = GetUnitX(u)
      set y2 = GetUnitY(u)
      set dx = x2 - x
      set dy = y2 - y
      set d = SquareRoot(dx * dx + dy * dy)
      if d <= MSAAOE() then
        if not IsUnitType(u, UNIT_TYPE_DEAD) then
          set u3 = LoadUnitHandle(udg_hash, id, 0)
          set dmg = LoadReal(udg_hash, id, 5)
          set m2 = GetUnitState(u, UNIT_STATE_MANA)
          set m = LoadReal(udg_hash, id, 6)
          if dmg > m2 then
            set m = (m + m2)
            call SetUnitState(u, UNIT_STATE_MANA, 0.)
          else
            set m = (m + dmg)
            call SetUnitState(u, UNIT_STATE_MANA, (GetUnitState(u, UNIT_STATE_MANA) - dmg))
          endif
          call UnitDamageTarget(u3, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
          call DestroyEffect(AddSpecialEffectTarget(MSEOT(), u, MSEAT()))
          call SaveReal(udg_hash, id, 6, m)
          set dmg = (dmg * (MSPDI() / 100))
          call SaveReal(udg_hash, id, 5, dmg)
          set u3 = null
        endif
        set udg_Temp_Group = LoadGroupHandle(udg_hash, id, 4)
        call GroupAddUnit(udg_Temp_Group, u)
        set udg_Temp_Unit = s
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, MSSR(), Filter(function MSFilter))
        set u2 = FirstOfGroup(bj_lastCreatedGroup)
        if u2 != null then
            call SaveUnitHandle(udg_hash, id, 2, u2)
            set bn = (bn - 1)
            call SaveInteger(udg_hash, id, 3, bn)
            set u2 = null
        else
            call RemoveSavedHandle(udg_hash, id, 2)
        endif
      else
        set a = Atan2(y2 - y, x2 - x)
        set x3 = x + MSDPS() / 33. * Cos(a)
        set y3 = y + MSDPS() / 33. * Sin(a)
        call SetUnitX(s, x3)
        call SetUnitY(s, y3)
        call SetUnitFacing(s, (bj_RADTODEG * a))
      endif
      set u = null
    endif
    set t = null
    set s = null
    set u4 = null
endfunction

function Start takes nothing returns boolean
    local unit u
    local timer t
    local unit u2
    local real x
    local real y
    local integer id
    if GetSpellAbilityId() == MSAID() then
      set t = NewTimer()
      set id = GetHandleId(t)
      set u = GetTriggerUnit()
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set u2 = GetSpellTargetUnit()
      set bj_lastCreatedUnit = CreateUnit(GetTriggerPlayer(), MSDID(), x, y, bj_RADTODEG * Atan2(GetUnitY(u2) - y, GetUnitX(u2) - x))
      call TimerStart(t, 0.03125, true, function Loop)
      call SaveUnitHandle(udg_hash, id, 0, u)
      call SaveUnitHandle(udg_hash, id, 1, bj_lastCreatedUnit)
      call SaveUnitHandle(udg_hash, id, 2, u2)
      call SaveInteger(udg_hash, id, 3, MSIBN() + (MSABN() * GetUnitAbilityLevel(u, MSAID())))
      call SaveGroupHandle(udg_hash, id, 4, CreateGroup())
      call SaveReal(udg_hash, id, 5, (MSIMB() + (MSAMB() * GetUnitAbilityLevel(u, MSAID()))))
      set u = null
      set u2 = null
      set t = null
    endif
    return false
endfunction

function InitTrig_Mystic_Snake takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Start ) )
    if udg_hash == null then
      set udg_hash = InitHashtable()
    endif
    set t = null
endfunction

Changelog

1.5 - Updated most of what Maker put, prefixed the two functions Spinnaker mentioned with "constant" and changed the Start function with the one Spinnaker provied (Thanks you two!)
1.4 - Another efficiency update.
1.3 - Efficiency update and fixed a very minor leak which i had not noticed before.
1.2 - Added the original snake model and icon, as well as importing instructions for them.
1.1 - Using TimerUtils.

Keywords:
mystic, snake, mana, steal, burn, medusa, return, homing, bounce, bounces, damage, increase, jass, mui, spamable, feedback, restore, DotA, defense, of
Contents

Just another Warcraft III map (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 14 Nov 2011 Bribe: This should use a FirstOfGroup loop instead of a filter. As Maker said you should use a single timer, because the timeout is less than 0.07 seconds. Rather than...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.


14 Nov 2011
Bribe: This should use a FirstOfGroup loop instead of a filter.

As Maker said you should use a single timer, because the timeout is less than 0.07 seconds.

Rather than using dynamic groups, just save a boolean to the unit's handle ID saying that it is occupied.

You messed up your indentation. Each block should be 4 spaces.

You need to list the variables required by your system. The hashtable should be prefixed by the spell name as well, to prevent clashes.


Maker, Mystic Snake v1.5, 10th Nov 2011

The spell is nice and works.

However you could make it run without TimerUtils, and use only one timer.
Maker, Mystic Snake v1.5, 10th Nov 2011

Check whether the hashtable is null before initializing it. The name is generic, so some other system might have initialized it already.

After that this should be approvable.
Maker, Mystic Snake v1.5, 7th Nov 2011

You're using TimerUtils, but I don't see you meantioning about that in importing instructions.
The spell needs it's own hashtable. If you share a hashtable with some other system, you're flushing the data for that also.
Name the hash something else, now the name is too generic.
Of course, one could ideally use only one shared hash for all spells. But then you'd need to check that no spell is running before flushing data.
The dummy gives quite a bit of vision, you could reduce it.
You could also change the tooltip's position in the command card.
Maker, Mystic Snake v1.4, 12:55, 2nd Nov 2011

Remove upgrades used from the dummy.
Shorten the death time of the dummy.
Universal cast is reserved for ultimate abilities. You should mention that somewhere.
Remove all bj's:
JASS:
set a = bj_RADTODEG * Atan2(y3 - y2, x3 - x2)
set x = x2 + (MSDPS() / 33.) * Cos(a * bj_DEGTORAD)
set y = y2 + (MSDPS() / 33.) * Sin(a * bj_DEGTORAD)
The ability should not use passive icons and improve the tooltips.
 
I just realized something important:
TimerUtils needs a strictly Jass version :(

Here's one you could use:

JASS:
//*************************************************
//*
//*   TimerUtils (Jass Version)
//*   v1.3.2.0
//*   By Magtheridon96
//*
//*   Original version by Vexorian.
//*   All the functions have O(1) complexity.
//*
//*   API:
//*       - function NewTimer takes nothing returns timer
//*           - Returns a new timer from the stack
//*       - function ReleaseTimer takes timer t returns nothing
//*           - Throws a timer back into the stack
//*       - function SetTimerData takes timer t, integer value returns nothing
//*           - Attaches a value to a timer
//*       - function GetTimerData takes timer t returns integer
//*           - Returns the attached value
//*
//*************************************************

    function SetTimerData takes timer t, integer value returns nothing
        call SaveInteger(udg_TimerUtils_hash,GetHandleId(t),0,value)
    endfunction

    function GetTimerData takes timer t returns integer
        return LoadInteger(udg_TimerUtils_hash,GetHandleId(t),0)
    endfunction

    function NewTimer takes nothing returns timer
        if 0==udg_TimerUtils_int then
            return CreateTimer()
        endif
        set udg_TimerUtils_int = udg_TimerUtils_int - 1
        return udg_TimerUtils_timer[udg_TimerUtils_int]
    endfunction

    function ReleaseTimer takes timer t returns nothing
        call PauseTimer(t)
        set udg_TimerUtils_timer[udg_TimerUtils_int] = t
        set udg_TimerUtils_int = udg_TimerUtils_int + 1
    endfunction

    function InitTrig_TimerUtils takes nothing returns nothing
        set udg_TimerUtils_hash = InitHashtable()
    endfunction

:)

udg_TimerUtils_hash -> hashtable
udg_TimerUtils_int -> integer
udg_TimerUtils_timer -> timer array

You should use this resource instead of pausing and destroying timers :/
 
You know what, this spell could run on one timer.
You don't even need TimerUtils.
Just use one global timer, and whenever someone casts the spell,
if the number of casts = 0, start the timer. While inside the loop,
decrease casts by 1 at the end of the spell. If it's 0, then pause
the timer.

When a unit casts the spell, you could add it to a global group.
The timer would callback a function that loops through all units
in the Casting group to run the spell effects :)

Easy enough? :p
 
Level 7
Joined
Jul 3, 2011
Messages
251
Yes i did, Maker spotted out the error.
JASS:
 if u2 != null then
            call SaveUnitHandle(udg_hash, id, 2, u2)
            set bn = (bn - 1)
            call SaveInteger(udg_hash, id, 3, bn)
            set u2 = null
        else
            call RemoveSavedHandle(udg_hash, id, 2)
        endif

This is what solved it, which maker did.

As you can see it turns out it wasn't a group issue, was that i wasn't removing the handle if the new unit turned out null, which meant it kept loading that unit over and over so instantly used all bounces on that one unit.
 
Level 7
Joined
Sep 2, 2011
Messages
350
The spell is awesome! :D Oh, and where did you get the mystic snake model anyway?
I guess you got it from DotA itself?
 
Level 7
Joined
Jul 3, 2011
Messages
251
Woohoo! Mystic Medusa!!!! I love this spell...

I agree with Maggy, make this run on only one timer, or if you want, T32 maybe?

Hehe yeah i love this spell too, was always my favourite in DotA which is why i made it.

About the timer, is this really needed? I mean the spell works fine without errors already, and surely the handle reduction wouldn't be significant.

How did you make your table like that? I can't for the life of me figure out how to have a different section for each spell :grin:
 
Level 7
Joined
Jul 3, 2011
Messages
251
Looking at the script all Set/GetTimerData() appears to do is save ONE integer value onto a timer, whats the point when i can just directly save all my values onto the timer? Thats all the script does anyway.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
A Jass version of TimerUtils is pretty silly.
Right now, the TimerUtils that Magtheridon96 posted is only useful for recycling timers for Jass users.
If you wanted to take advantage of the library better, you would use structs so that Set/GetTimerData would actually be useful, but that would mean using vJass.

Anyway, short code review (only looking at the site):

  • You don't need to square root the distance, just make the distance check like this:
    JASS:
    if d <= MSAOE() * MSAOE()
  • a in Loop would have been better off in radians since you would only need to convert it once when you set the unit's facing.
  • JASS:
              if dmg > m2 then
                set m = (m + m2)
                call UnitDamageTarget(u3, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
                call SetUnitState(u, UNIT_STATE_MANA, 0.)
              else
                set m = (m + dmg)
                call UnitDamageTarget(u3, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
                call SetUnitState(u, UNIT_STATE_MANA, (GetUnitState(u, UNIT_STATE_MANA) - dmg))
              endif
    ->
    JASS:
              call UnitDamageTarget(u3, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
              if dmg > m2 then
                set m = (m + m2)
                call SetUnitState(u, UNIT_STATE_MANA, 0.)
              else
                set m = (m + dmg)            
                call SetUnitState(u, UNIT_STATE_MANA, (m2 - dmg))
              endif
 
Level 7
Joined
Jul 3, 2011
Messages
251
A Jass version of TimerUtils is pretty silly.
Right now, the TimerUtils that Magtheridon96 posted is only useful for recycling timers for Jass users.
If you wanted to take advantage of the library better, you would use structs so that Set/GetTimerData would actually be useful, but that would mean using vJass.

Anyway, short code review (only looking at the site):

  • You don't need to square root the distance, just make the distance check like this:
    JASS:
    if d <= MSAOE() * MSAOE()
  • a in Loop would have been better off in radians since you would only need to convert it once when you set the unit's facing.
  • JASS:
              if dmg > m2 then
                set m = (m + m2)
                call UnitDamageTarget(u3, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
                call SetUnitState(u, UNIT_STATE_MANA, 0.)
              else
                set m = (m + dmg)
                call UnitDamageTarget(u3, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
                call SetUnitState(u, UNIT_STATE_MANA, (GetUnitState(u, UNIT_STATE_MANA) - dmg))
              endif
    ->
    JASS:
              call UnitDamageTarget(u3, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
              if dmg > m2 then
                set m = (m + m2)
                call SetUnitState(u, UNIT_STATE_MANA, 0.)
              else
                set m = (m + dmg)            
                call SetUnitState(u, UNIT_STATE_MANA, (m2 - dmg))
              endif

Thanks for the review, will work on a few of the things you noted when i got time, and im afraid i wont be using vJASS, i dont know it yet.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
JASS:
function MSDID takes nothing returns integer
    return 'h000' // The dummy's ID (ctrl + d in object editor)
endfunction

function MSAID takes nothing returns integer
    return 'A000' // The ability's ID (ctrl + d in object editor)
endfunction
Those function should be prefixed with "constant".

I'll review this spell too Zaio (tomorrow - if anything for "reviewing" left ;p), but it's a good spell.

Function "start":
JASS:
function Start takes nothing returns boolean
    local unit u
    local timer t
    local unit u2
    local unit s
    local real x
    local real x2
    local real y
    local real y2
    local real a
    local integer id
    local integer bn
    local group g
    if GetSpellAbilityId() == MSAID() then
      set t = NewTimer()
      set g = CreateGroup()
      set u = GetTriggerUnit()
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set u2 = GetSpellTargetUnit()
      set x2 = GetUnitX(u2)
      set y2 = GetUnitY(u2)
      set a = bj_RADTODEG * Atan2(y2 - y, x2 - x)
      set s = CreateUnit(GetTriggerPlayer(), MSDID(), x, y, a)
      call TimerStart(t, 0.03, true, function Loop)
      set id = GetHandleId(t)
      set bn = MSIBN() + (MSABN() * GetUnitAbilityLevel(u, MSAID()))
      call SaveUnitHandle(udg_hash, id, 0, u)
      call SaveUnitHandle(udg_hash, id, 1, s)
      call SaveUnitHandle(udg_hash, id, 2, u2)
      call SaveInteger(udg_hash, id, 3, bn)
      call SaveGroupHandle(udg_hash, id, 4, g)
      call SaveReal(udg_hash, id, 5, (MSIMB() + (MSAMB() * GetUnitAbilityLevel(u, MSAID()))))
      set s = null
      set u = null
      set u2 = null
      set t = null
      set g = null
    endif
    return false
endfunction
->>
0.03 -> 0.3125
locals: x2, y2, a, s, g, bn not needed
JASS:
function Start takes nothing returns boolean
    local unit u
    local timer t
    local unit u2
    local real x
    local real y
    local integer id
    if GetSpellAbilityId() == MSAID() then
      set t = NewTimer()
      set id = GetHandleId(t)
      set u = GetTriggerUnit()
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set u2 = GetSpellTargetUnit()
      set bj_lastCreatedUnit = CreateUnit(GetTriggerPlayer(), MSDID(), x, y, bj_RADTODEG * Atan2(GetUnitY(u2) - y, GetUnitX(u2) - x))
      call TimerStart(t, 0.03125, true, function Loop)
      call SaveUnitHandle(udg_hash, id, 0, u)
      call SaveUnitHandle(udg_hash, id, 1, bj_lastCreatedUnit)
      call SaveUnitHandle(udg_hash, id, 2, u2)
      call SaveInteger(udg_hash, id, 3, MSIBN() + (MSABN() * GetUnitAbilityLevel(u, MSAID())))
      call SaveGroupHandle(udg_hash, id, 4, CreateGroup())
      call SaveReal(udg_hash, id, 5, (MSIMB() + (MSAMB() * GetUnitAbilityLevel(u, MSAID()))))
      set u = null
      set u2 = null
      set t = null
    endif
    return false
endfunction
 
Level 7
Joined
Jul 3, 2011
Messages
251
JASS:
function MSDID takes nothing returns integer
    return 'h000' // The dummy's ID (ctrl + d in object editor)
endfunction

function MSAID takes nothing returns integer
    return 'A000' // The ability's ID (ctrl + d in object editor)
endfunction
Those function should be prefixed with "constant".

I'll review this spell too Zaio (tomorrow - if anything for "reviewing" left ;p), but it's a good spell.

Function "start":
JASS:
function Start takes nothing returns boolean
    local unit u
    local timer t
    local unit u2
    local unit s
    local real x
    local real x2
    local real y
    local real y2
    local real a
    local integer id
    local integer bn
    local group g
    if GetSpellAbilityId() == MSAID() then
      set t = NewTimer()
      set g = CreateGroup()
      set u = GetTriggerUnit()
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set u2 = GetSpellTargetUnit()
      set x2 = GetUnitX(u2)
      set y2 = GetUnitY(u2)
      set a = bj_RADTODEG * Atan2(y2 - y, x2 - x)
      set s = CreateUnit(GetTriggerPlayer(), MSDID(), x, y, a)
      call TimerStart(t, 0.03, true, function Loop)
      set id = GetHandleId(t)
      set bn = MSIBN() + (MSABN() * GetUnitAbilityLevel(u, MSAID()))
      call SaveUnitHandle(udg_hash, id, 0, u)
      call SaveUnitHandle(udg_hash, id, 1, s)
      call SaveUnitHandle(udg_hash, id, 2, u2)
      call SaveInteger(udg_hash, id, 3, bn)
      call SaveGroupHandle(udg_hash, id, 4, g)
      call SaveReal(udg_hash, id, 5, (MSIMB() + (MSAMB() * GetUnitAbilityLevel(u, MSAID()))))
      set s = null
      set u = null
      set u2 = null
      set t = null
      set g = null
    endif
    return false
endfunction
->>
0.03 -> 0.3125
locals: x2, y2, a, s, g, bn not needed
JASS:
function Start takes nothing returns boolean
    local unit u
    local timer t
    local unit u2
    local real x
    local real y
    local integer id
    if GetSpellAbilityId() == MSAID() then
      set t = NewTimer()
      set id = GetHandleId(t)
      set u = GetTriggerUnit()
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set u2 = GetSpellTargetUnit()
      set bj_lastCreatedUnit = CreateUnit(GetTriggerPlayer(), MSDID(), x, y, bj_RADTODEG * Atan2(GetUnitY(u2) - y, GetUnitX(u2) - x))
      call TimerStart(t, 0.03125, true, function Loop)
      call SaveUnitHandle(udg_hash, id, 0, u)
      call SaveUnitHandle(udg_hash, id, 1, bj_lastCreatedUnit)
      call SaveUnitHandle(udg_hash, id, 2, u2)
      call SaveInteger(udg_hash, id, 3, MSIBN() + (MSABN() * GetUnitAbilityLevel(u, MSAID())))
      call SaveGroupHandle(udg_hash, id, 4, CreateGroup())
      call SaveReal(udg_hash, id, 5, (MSIMB() + (MSAMB() * GetUnitAbilityLevel(u, MSAID()))))
      set u = null
      set u2 = null
      set t = null
    endif
    return false
endfunction

Constant? But that is for vJASS only, no? And how come 0.3125?
 
To make it short, it makes more sence mathematically to use a number that doesn't produce decimal numbers. It can turn out to be more accurate when calculating stuff.

Not only that, but 32 is a power of 2, so it makes you look like a pro haxxor ;D

edit
By the way Zaio, you might wanna update the TimerUtils used here :/
I updated it to version 2.1.0.0
 
Level 7
Joined
Jul 3, 2011
Messages
251
Level 31
Joined
Jul 10, 2007
Messages
6,306
Oh yes...

The timer portion is done rather poorly

->.03125

Because of that, you should not be using TimerUtils for this. You should either use T32 by j4l for CTL by me. Pick one and use it ; P.


T32 and CTL create one timer and only one. CTL pauses and resumes that timer based on whether there are any instances running or not. T32 just always keeps the timer running ;o.

They use a linked list to loop through the instances. T32 calls an expire method or w/e for every instance. CTL inlines the code (no method call).

This means that rather than 30 timers expiring 32x a second, you have 1 timer expiring 32x a second running through a loop : ).
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
What I said doesn't require vjass....


Do you not know how to create a timer on init and start it?

function InitTrig_Mystic_Snake takes nothing returns nothing


And do you not know how to make a linked list??
JASS:
integer array udg_next
integer array udg_prev

??


where is the vjass there?



just cuz the tutorials explain how to do linked lists doing vjass doesn't mean you have to use vjass to make them >.>.


Anything you can do in vjass you can also do in JASS, it's just more work and harder to use.


no excuses >: o
 
Top