1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. And she's bought a stairway to heaven. The 6th Special Effect Contest Results are here.
    Dismiss Notice
  3. Seek unity between the elements in the 22nd Terraining Contest Poll.
    Dismiss Notice
  4. Seize the moment! The 18th Mini Mapping Contest has commenced.
    Dismiss Notice
  5. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Mana Wave v1.4

Submitted by nhocklanhox6
This bundle is marked as approved. It works and satisfies the submission rules.
Spell Look Like

http://www.youtube.com/watch?v=F0v-T1qzihw&feature=youtu.be

Changelog
v1.0: First release version
v1.1: Bugs fixed, code optimized, name changed
v1.1b: Bugs fixed
v1.2: Leaks fixed, remove GetUnitTypeId on the onCast function.
v1.2b: Minor bugs fixed !
v1.3: Minor bugs fixed !, add some effect.
v1.4: Minor bugs fixed !

Spell Code
Code (vJASS):
//***********************************************************************************************************
//***********************************************************************************************************
//***********************************************************************************************************
//***********************************************************************************************************
//***********************************************************************************************************
// M A N A   W A V E
//                  By: Elphis
//          - Spell Information:
//                              - Calls forth a wave of energy that heals a target, if target belong to ally,
//                                it will bounces to all nearby friendlies around 700 AOE, each bounces heals 100 mana.
//                                But if the target belong to enemy, it will bounces to all nearby ememies around 700 AOE and
//                                destroy 100 on the target, every time it bounces to enemy, each wave, mana desrtoy will decreasing by 20.
//          - Installation:
//                                - Import/copy Healing Mana code to your map
//                                - Import/copy the custom ability and unit to your map and change the SPELL_ID if needed
//                                - You may view the raw ID of the objects by pressing CTRL+D in the object editor
//                                - You may play with the configurables below
//***********************************************************************************************************
//***********************************************************************************************************
//***********************************************************************************************************
//***********************************************************************************************************
//***********************************************************************************************************
//***********************************************************************************************************
library ManaWave

    //**********************************CONFIGURABLE**********************************

    globals
        //Spell rawcode
        private constant    integer SPELL_ID = 'A000'
        //Spell period
        private constant    real    PERIODIC    =   .031250000
        //Area of effect
        private constant    real    AOE =   700.
        //Heal mana time intevar
        private constant    real    HEAL_INTEVAR    =   0.1
        //Mana heal count
        private constant    real    MANA_HEAL_BASE  =   100.
        //Mana destroy decrease every it link from enemy
        private constant    real    MANA_DESTROY_DECREASE = 20.
        //Lightning color fade timed
        private constant    real    LIGHTNING_FADE_INTEVAR = 0.02
       
        //***********LIGHTNING COLOR***********//
        private constant    real    LIGHTNING_COLOR_RED = 0.5
        private constant    real    LIGHTNING_COLOR_GREEN = 0.2
        private constant    real    LIGHTNING_COLOR_BLUE = 1.
        //***************************************
       
        //Allies buff count
        private constant    integer ALLIES_COUNT_BASE = 5
        //Lightning model
        private constant    string  LIGHTNING_MODEL =   "MFPB"
        //Effect when no unit in area of effect
        private constant    string  MODEL_EMPTY =   "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl"
        //Effect when buff allies
        private constant    string  BUFF_EFFECT =   "Abilities\\Spells\\Other\\Drain\\ManaDrainTarget.mdl"
        //Effect when buff enemies
        private constant    string  BUFF_EFFECT_ENEMY =   "Abilities\\Spells\\Other\\Drain\\ManaDrainCaster.mdl"
        //Effect linked of the lightning
        private constant    string  EFFECT_LINKED     = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
        //Point attachment
        private constant    string  ATTACH_POINT =  "origin"
         //Point linked attachment
        private constant    string  ATTACH_POINT_LINKED =  "origin"
       
        //***********Non-Configurable***********//
        private             integer MUI = -1
        private             integer Plugin_MUI = -1
        private constant    group   G = CreateGroup()
        private constant    integer array Struct_Data
        private constant    integer array Plugin_Struct_Data
        private constant    timer   TIMER   =   CreateTimer()
        private constant    timer   TIMER_2 =   CreateTimer()
        private constant    location LOC    =   Location(0.,0.)
        //***************************************
    endglobals
   
    //**********************************END OF CONFIGURABLE**********************************//
   
    //**********************************DO NOT MODIFY ANYTHING BELOW**********************************//
   
    private struct ManaWave
       
        unit caster
        unit last_unit
       
        real intevar = 0.
        real mana
       
        boolean destroy_mana
       
        group g = CreateGroup()
       
        static method onPeriodic takes nothing returns nothing
            local thistype this
            local integer i = 0
            local unit r
           
            loop
                exitwhen i > MUI
               
                set this = Struct_Data[i]
               
                if intevar < HEAL_INTEVAR then
                    set intevar = intevar + PERIODIC
                else
                    set intevar = 0.
                   
                    set r = GroupPickRandomUnit(g)
                   
                    if r != null then
                        call GroupRemoveUnit(g,r)
                   
                        call ManaWave_ManaWavePlugin.add(LIGHTNING_MODEL,r,last_unit,mana,destroy_mana)
                       
                        if destroy_mana then
                            set mana = mana - MANA_DESTROY_DECREASE
                        endif
                       
                        set last_unit = r
                    else
                        set Struct_Data[i] = Struct_Data[MUI]
                        set Struct_Data[MUI] = -2
                        set MUI = MUI - 1
                       
                        if MUI == -1 then
                            call PauseTimer(TIMER)
                        endif
                       
                        call DestroyGroup(g)
                        set g = null
                        set caster = null
                        set last_unit = null
                        call destroy()
                    endif
                endif
                set i = i + 1
            endloop
        endmethod
       
        static method onCast takes nothing returns boolean
            local thistype this
            local unit f = GetSpellTargetUnit()
            local unit ct = GetTriggerUnit()
            local player owner = GetTriggerPlayer()
            local integer lvl
            local integer count
            local integer total_unit = 0
            local boolean destroy_mn = IsUnitEnemy(f,owner)
           
            //Checking unit around the target....
           
            call GroupEnumUnitsInRange(G,GetUnitX(f),GetUnitY(f),AOE,null)
           
            loop
                set f = FirstOfGroup(G)
                exitwhen f == null
               
                if f != ct and GetUnitState(f,UNIT_STATE_MAX_MANA) > 0. and not IsUnitType(f,UNIT_TYPE_DEAD) then
                   
                    if IsUnitEnemy(f,owner) == destroy_mn then
                        set total_unit = 1
                        exitwhen true
                    endif
                   
                endif
               
                call GroupRemoveUnit(G,f)
               
            endloop
           
            //Checking conditions before run this spell....
           
            if total_unit == 0 then
                call DestroyEffect(AddSpecialEffect(MODEL_EMPTY,GetUnitX(ct),GetUnitY(ct)))
                set ct = null
                set f = null
                return false
            endif
           
            set this = allocate()
           
            set total_unit = 0
            set MUI = MUI + 1
            set Struct_Data[MUI] = this
            set caster = ct
            set last_unit = GetSpellTargetUnit()
            set lvl = GetUnitAbilityLevel(caster,SPELL_ID)
           
            set mana = MANA_HEAL_BASE*lvl
            set count = ALLIES_COUNT_BASE+lvl
           
            set destroy_mana = destroy_mn
           
            call GroupEnumUnitsInRange(G,GetUnitX(f),GetUnitY(f),AOE,null)
           
            loop
                set f = FirstOfGroup(G)
                exitwhen f == null
               
                if f != caster and GetUnitState(f,UNIT_STATE_MAX_MANA) > 0. and not IsUnitType(f,UNIT_TYPE_DEAD) then
                   
                    if IsUnitEnemy(f,owner) == destroy_mana then
                        call GroupAddUnit(g,f)
                    endif
                   
                    set total_unit = total_unit + 1
                endif
               
                call GroupRemoveUnit(G,f)
               
            endloop
           
            set count = total_unit - count
           
            set f = GroupPickRandomUnit(g)
           
            if f != null then
           
                call ManaWave_ManaWavePlugin.add(LIGHTNING_MODEL,last_unit,caster,mana,destroy_mana)
               
                call GroupRemoveUnit(g,last_unit)
               
                loop
                    exitwhen count < 0
                   
                    set f = GroupPickRandomUnit(g)
                    call GroupRemoveUnit(g,f)
                   
                    set count = count - 1
                endloop
               
            endif
           
            if MUI == 0 then
                call TimerStart(TIMER,PERIODIC,true,function thistype.onPeriodic)
            endif
           
            set owner= null
            set ct = null
            set f = null
           
            return false
        endmethod
       
        static method onInit takes nothing returns nothing
            local integer i = 0
            local trigger t = CreateTrigger()
            //
            loop
                exitwhen i > 15
                call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
                set i = i + 1
            endloop
            //
            call TriggerAddCondition(t,function thistype.onCast)
            set t = null
        endmethod
       
    endstruct
   
    public struct ManaWavePlugin
       
        private unit plugin_unit
        private unit plugin_caster
       
        private real plugin_color
        private real plugin_mana
        private real plugin_intevar
        private real plugin_fade = 1.
       
        private effect plugin_effect
       
        private lightning plugin_lightning
       
        private boolean plugin_destroy
       
        static method onPeriodic takes nothing returns nothing
            local thistype this
            local integer i = 0
            local real zc
            local real zu
            local real xc
            local real yc
            local real xu
            local real yu
           
            loop
                exitwhen i > Plugin_MUI
               
                set this = Plugin_Struct_Data[i]
               
                set xc = GetUnitX(plugin_caster)
                set yc = GetUnitY(plugin_caster)
                set zu = GetUnitX(plugin_unit)
                set yu = GetUnitY(plugin_unit)
               
                call MoveLocation(LOC,xc,yc)
                set zc = GetLocationZ(LOC)+GetUnitFlyHeight(plugin_caster)
                call MoveLocation(LOC,xu,yu)
                set zc = GetLocationZ(LOC)+GetUnitFlyHeight(plugin_unit)
               
                call MoveLightningEx(plugin_lightning,true,xc,yc,zc,zu,yu,zu)
               
                if plugin_fade > 0. then
                    set plugin_fade = plugin_fade - LIGHTNING_FADE_INTEVAR
                    call SetLightningColor(plugin_lightning,LIGHTNING_COLOR_RED,LIGHTNING_COLOR_GREEN,LIGHTNING_COLOR_BLUE,plugin_fade)
                    if not plugin_destroy then
                        call SetUnitState(plugin_unit,UNIT_STATE_MANA,GetUnitState(plugin_unit,UNIT_STATE_MANA)+plugin_intevar)
                    else
                        call SetUnitState(plugin_unit,UNIT_STATE_MANA,GetUnitState(plugin_unit,UNIT_STATE_MANA)-plugin_intevar)
                    endif
                else
                    set Plugin_Struct_Data[i] = Plugin_Struct_Data[Plugin_MUI]
                    set Plugin_Struct_Data[Plugin_MUI] = -2
                    set Plugin_MUI = Plugin_MUI - 1
                   
                    if Plugin_MUI == -1 then
                        call PauseTimer(TIMER_2)
                    endif
                   
                    call DestroyEffect(plugin_effect)
                    call DestroyLightning(plugin_lightning)
                   
                    set plugin_effect = null
                    set plugin_lightning = null
                    set plugin_unit = null
                    set plugin_caster = null
                   
                    call destroy()
                endif
               
                set i = i + 1
            endloop
        endmethod
       
        static method add takes string lmodel,unit u,unit caster,real mana,boolean destroy returns nothing
       
            local thistype this = allocate()
           
            set Plugin_MUI = Plugin_MUI + 1
            set Plugin_Struct_Data[Plugin_MUI] = this
           
            set plugin_lightning = AddLightningEx(lmodel,true,GetUnitX(caster),GetUnitY(caster),GetUnitFlyHeight(caster),GetUnitX(u),GetUnitY(u),GetUnitFlyHeight(u))
            call SetLightningColor(plugin_lightning,LIGHTNING_COLOR_RED,LIGHTNING_COLOR_GREEN,LIGHTNING_COLOR_BLUE,1.)
            set plugin_unit = u
            set plugin_caster = caster
            set plugin_mana = mana
            set plugin_intevar = plugin_mana*LIGHTNING_FADE_INTEVAR
            set plugin_destroy = destroy
            call DestroyEffect(AddSpecialEffectTarget(EFFECT_LINKED,u,ATTACH_POINT_LINKED))
            if plugin_destroy then
                set plugin_effect = AddSpecialEffectTarget(BUFF_EFFECT_ENEMY,u,ATTACH_POINT)
            else
                set plugin_effect = AddSpecialEffectTarget(BUFF_EFFECT,u,ATTACH_POINT)
            endif
           
            if Plugin_MUI == 0 then
                call TimerStart(TIMER_2,PERIODIC,true,function thistype.onPeriodic)
            endif
        endmethod
       
    endstruct
   
endlibrary

Spell Screen Shot
[​IMG]


Keywords:
mana,wave
Contents

Just another Warcraft III map (Map)

Reviews
Moderator
08:17, 10th Feb 2014 BPower: The coding looks now far better and the objects are fair enough. What's still left undone is the way you determine the z offset of the lightning. Using GetUnitFlyHeight only will look bad if one adds cliffs or valleys...
  1. 08:17, 10th Feb 2014
    BPower:
    Review
    The coding looks now far better and the objects are fair enough.
    What's still left undone is the way you determine the z offset of the lightning.
    Using GetUnitFlyHeight only will look bad if one adds cliffs or valleys to the map.
    The correct way would be:

    Code (vJASS):

    call MoveLocation(loc, GetUnitX(unit), GetUnitY(unit))
    set z  = GetLocationZ(loc) + GetUnitFlyHeight(unit)
                   


    old
    04.02.2014:
    BPower:


    You have unit allies as struct member, but it isn't used at all.

    Per spell cast you start 46 needless timers
    call TimerStart(TIMER,0.,false,function ManaWave.onPeriodic)
    ,
    because unlike the ManaWavePluggin struct the corresponding ManaWave struct instance is already deallocated.

    19:04, 30th Jan 2014:
    Make those two changes I mentioned:
    http://www.hiveworkshop.com/forums/spells-569/mana-wave-v1-2-a-246277/?prev=status%3Dp%26of%3Ddateline%26o%3DASC%26imporder%3D1#post2479763
     
  2. Chaosy

    Chaosy

    Tutorial Reviewer

    Joined:
    Jun 9, 2011
    Messages:
    11,068
    Resources:
    18
    Icons:
    1
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    18
    looks neat.
    However restore mana is a better name I think
     
  3. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Use
    GroupPickRandomUnit

    Or can you point out one valid argument, why you made an excat copy of an existing function.

    Well I have one against it. It prolongs your code needlessly.

    Edit: Same issue for
    call TriggerRegisterAnyUnitEventBJ


    What do you think about
    Code (vJASS):
                if IsUnitEnemy(f,caster_owner) then
                    set destroy_mana = true
                else
                    set destroy_mana = false
                endif

    -->
    set destroy_mana = IsUnitEnemy(f, caster_owner)
    :thumbs_up:

    use one static group for the enumeration instead of creating a local Group G per cast instance.

    Code (vJASS):
                        if destroy_mana then
                            if IsUnitEnemy(f,caster_owner) then
                                call GroupAddUnit(g,f)
                            endif
                           
                            set total_unit = total_unit + 1
                        else
                            if IsUnitAlly(f,caster_owner) then
                                call GroupAddUnit(g,f)
                            endif
                           
                            set total_unit = total_unit + 1
                        endif

    move total_unit outside the condition and delete one of these.

    set caster_owner = null
    is not nulled in all cases.

    I guess it is not mandatory, but still JPAG.

    You never destroy nor null group this.g
    You have a unit handle leak --> local unit r
     
    Last edited: Jan 1, 2014
  4. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,526
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    It is better to not use that BJ as it does not get inlined which means it is slightly slower than making it yourself. Also BJs are just ugly looking in code IMO.
     
  5. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    I don't think you are right here. CnP from the JassHelper manual:
    • The function must be a one-liner.
    • Every argument must be evaluated once and only once by the function, in the same order as they appear in the arguments list.
    Also not every BJ is trash.

    Edit:
    Because of the color and the cursive text? ... Then change the settings of the highlighting in the editor or in the styles.ini :)
     
    Last edited: Jan 1, 2014
  6. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,526
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    I did not say every BJ is trash lol.
    That function is not a one liner either.
    Calling a function vs not calling a function means that the one that does not call a function is faster.
     
  7. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    If you target yourself while no unit is near the whole skript will fail. Afterwards you can't cast the spell anymore.

    Also I think you could optimize the code by alot. For instance:
    Code (vJASS):

                if IsUnitEnemy(f,caster_owner) then
                    set destroy_mana = true
                else
                    set destroy_mana = false
                endif
               
                call GroupEnumUnitsInRange(G,GetUnitX(f),GetUnitY(f),AOE,null)
               
                loop
                    set f = FirstOfGroup(G)
                    exitwhen f == null
                   
                    if f != caster and GetUnitState(f,UNIT_STATE_MAX_MANA) > 0. and GetWidgetLife(f) > .0425 then
                        if destroy_mana then
                            if IsUnitEnemy(f,caster_owner) then
                                call GroupAddUnit(g,f)
                            endif
                           
                            set total_unit = total_unit + 1
                        else
                            if IsUnitAlly(f,caster_owner) then
                                call GroupAddUnit(g,f)
                            endif
                           
                            set total_unit = total_unit + 1
                        endif
                    endif
                   
                    call GroupRemoveUnit(G,f)
                endloop
               
                set integer_loop = total_unit - count

    ---->
    Code (vJASS):
     
                set destroy_mana = IsUnitEnemy(f,caster_owner)
                /*
                *   static group enu = CreateGroup()
                */

                call GroupEnumUnitsInRange(enu ,GetUnitX(f),GetUnitY(f),AOE,null)
               
                loop
                    set f = FirstOfGroup(enu)
                    exitwhen f == null
                    /*
                    *     UnitAlive is faster than GetWidgetLife
                    */

                    if f != caster and GetUnitState(f,UNIT_STATE_MAX_MANA) > 0. and UnitAlive(f) then
                        /*
                        *     ^^
                        */

                        if IsUnitEnemy(f,caster_owner) == destroy_mana then
                            call GroupAddUnit(g, f)
                            set total_unit = total_unit + 1
                        endif
                       
                    endif
                    call GroupRemoveUnit(enu,f)
                endloop
                /*
                *     re-use count
                */

                set count = total_unit - count
     
  8. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,327
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    I truly suggest to change the name -> Healing Mana is a bit strange. Healing refers to health whereas mana refers to mana ^^
    So why not Mana Replenish or something like this.

    I'll review the code later ;)

    EDIT :
    Code (vJASS):
    static method onInit takes nothing returns nothing
                local integer i = 0
                local trigger t = CreateTrigger()
                //
                loop
                    exitwhen i > bj_MAX_PLAYERS
                    call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
                    set i = i + 1
                endloop
                //
                call TriggerAddCondition(t,function thistype.onCast)
                set t = null
            endmethod

    If you truly want to do so -> bj_MAX_PLAYERS = 12 ;)
    So just replace the constant by 12.

    Code (vJASS):
    if IsUnitEnemy(f,caster_owner) then
                    set destroy_mana = true
                else
                    set destroy_mana = false
                endif

    ->
    set destroy_mana = IsUnitEnemy(f, caster_owner)


    GetWidgetLife(f) > .0425
    is not enough to detect living units. Either use
    native UnitAlive takes unit id returns boolean
    or IIRC
    IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u)==0


    I suggest to start MUI and PLUGIN_MUI at -1 to use the 0 index of arrays ;)

    You can private the second struct, you'll be able to access its function even if it is private because you're inside the scope !

    It is a fast one if you think I said something wrong feel free to say ^^


    EDIT FOR BPower : One group per cast isn't that slow to have a static one.
     
    Last edited: Jan 3, 2014
  9. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Do you plan to update this spell?
     
    Last edited: Jan 15, 2014
  10. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    You leak two local units if no targets are around.
    You can't pick removed units, the unittypeid check onCast is not needed.
    It looks now much better than before and works as intended.
     
  11. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,327
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    You have to do the check only if a unit can be removed during the moment you picked and the moment you do your loop with the group.
     
  12. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Code (vJASS):

    set ct = null
    set f = null          
    call DestroyEffect(AddSpecialEffect(MODEL_EMPTY,GetUnitX(ct),GetUnitY(ct)))

    Well that one doesn't work out so good ^^ --> null ct after you tried to get its coordinates.
    Create group g in onCast, because it is not required if there are no targets around.
    Also, but not nessesary to get it approved:
    • You could use TriggerRegisterAnyUnitEventBJ instead of writing it by yourself.
    • caster_owner could be owner, but that just a personal preference of mine to avoid overlong local variable names
    • TIMER shouldn't be in the configurable part.
     
  13. nhocklanhox6

    nhocklanhox6

    Joined:
    Feb 12, 2012
    Messages:
    335
    Resources:
    28
    Models:
    6
    Spells:
    21
    Tutorials:
    1
    Resources:
    28
    @BPower: Sorry my friend ^^~, i've busy in the last time, so i can't fix it.

    Now: Fixed and thanks, of course!

    Sorry, but i don't want to create group g everytime spell is activating.., i think it's fine to do that, hope you understand !
     
  14. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    You have unit allies as struct member, but it isn't used at all.