• 🏆 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!

Lotus Ice v1.1b

video
Screen shot

untit234.jpg

untit233.jpg
Spell Code
JASS:
//***************************************************************************
//***************************************************************************
//***************************************************************************
//      L O T U S   I C E
//                                          By: Elphis (Nyuu)
//      Version: 1.0
//
//      Sepll Description: 
//  `                       - Using the power of frost, ice beam summons on the ground at the same time creating freezing weather, 
//                           after the ice beam achieve maximum strength, it immediately ice scrapers on the ground, any enemies in range
//                           300, it will pierce, and push enemies out of range, and every 0.2 seconds, it will emit a stream of ejected ice,
//                           freezing enemies, and those who freezes in 500 range, it will pull enemy into the deep of the ice place. 
//
//                           Damage level 1: 1.5 
//                           Damage level 2: 3
//                           Damage level 3: 4.5 
//                           Damage level 4: 6

//          - Installation:
//                                - Import/copy Lotus Ice code to your map
//                                - Import/copy the custom ability and unit to your map and change the SPELL_ID, DUMMY_CASTER, DUMMY_DEG_1, DUMMY_DEG_2, DUMMY_DEG_3 and DUMMY_MISSLE 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 LotusIce

    globals
        private constant    real            PERIODIC        =   .031250000
        //Spell rawcode, change if needed
        private constant    integer         SPELL_ID        =   'A000'
        //Dummy 1 rawcode, change if needed
        private constant    integer         DUMMY_DEG_1     =   'e000'
        //Dummy 2 rawcode, change if needed
        private constant    integer         DUMMY_DEG_2     =   'e001'
        //Dummy 3 rawcode, change if needed
        private constant    integer         DUMMY_DEG_3     =   'e002'
        //Dummy missle rawcode, change if needed
        private constant    integer         DUMMY_MISSLE    =   'e003'
        //Dummy caster rawcode, change if needed
        private constant    integer         CASTER_DUMMY    =   'e004'
        //Buff rawcode, change if needed
        private constant    integer         BUFF_ID         =   'B000'
        //Weather type when spell is active
        private constant    integer         WEATHER_TYPE    =   'SNhs'
        //Total ice count
        private constant    integer         DUMMY_COUNT     =   10
        //Frost effect count random number, incease => laggy, decrease => ice fairy
        private constant    integer         EFFECT_NUMBER   =   2
        //Total wave of frost
        private constant    integer         FROST_COUNT     =   4
        //Damage base of spell, increase => instantly dead, decrease => you dead
        private constant    real            DAMAGE_BASE     =   1.5
        //Push unit radius
        private constant    real            KNOCK_RADIUS    =   300.
        //Damage radius, increase => incredible spell, decrease => useless spell
        private constant    real            DMG_RADIUS      =   100.
        //Pull unit when the enemies has freezed
        private constant    real            PULL_RANGE      =   500.
        //Push speed, increase => fast push, decrease => slow push
        private constant    real            ENEMIES_KNOCK   =   5.
        //Pull unit speed
        private constant    real            ENEMIES_PULL    =   0.5
        //Frost missle speed
        private constant    real            MISSLE_SPEED    =   20.
        //Main ice scale
        private constant    real            ICE_SCALE_1     =   2.
        //Second ice scale
        private constant    real            ICE_SCALE_2     =   1.
        //Third ice scale
        private constant    real            ICE_SCALE_3     =   1.
        //Scale increase per periodic
        private constant    real            SCALE_PER_PERIOD=   .07

        private constant    real            ICE_AREA_1      =   150.
        private constant    real            ICE_AREA_2      =   200.
        
        private constant    real            RANGE_MISSLE_1  =   600.
        private constant    real            RANGE_MISSLE_2  =   700.
        //Range increase per wave of frost
        private constant    real            RANGE_PER_PERIOD=   150.
        //Freeze radius when the wave of frost is active
        private constant    real            FREEZE_RADIUS   =   150.
        //Frost wave intevar
        private constant    real            FROST_INTEVAR   =   0.1
        //Damage intevar
        private constant    real            DAMAGE_INTEVAR  =   0.5
        //Lotus ice spell duration
        private constant    real            LOTUS_DURATION  =   3.
        //Frost of wave repeat time
        private constant    real            FROST_REPEAT    =   0.8
        //Frost effect
        private constant    string          ICE_EFFECT      =   "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
        //Order id of dummy spell
        private constant    string          ORDER_ID        =   "thunderbolt"
        
        //Create a dummy on init function, so this spell never create dummy again
        private constant    boolean         DO_DUMMY_INIT   =   true
        
        //******************************DAMAGE DATA SETTINGS******************************
        private constant    attacktype      ATTACK_TYPE     =   ATTACK_TYPE_HERO
        private constant    damagetype      DAMAGE_TYPE     =   DAMAGE_TYPE_DEATH
        private constant    weapontype      WEAPON_TYPE     =   WEAPON_TYPE_CLAW_HEAVY_SLICE
        //********************************************************************************
        
        //***************************Non - Configurable*****************************
        /*                                                                         */
        /**/private             integer         MUI         =  -1                /**/
        /*                                                                         */
        /**/private constant    timer           TIMER       =   CreateTimer()    /**/
        /*                                                                         */
        /**/private constant    timer           TIMER_2     =   CreateTimer()    /**/
        /*                                                                         */
        /**/private             integer array   StructData                       /**/
        /*                                                                         */
        /**/private             integer         PMUI         = -1                /**/
        /*                                                                         */
        /**/private             integer array   PStructData                      /**/
        /*                                                                         */
        /**/private             integer         TMUI         = -1                /**/
        /*                                                                         */
        /**/private             integer array   TStructData                      /**/
        /*                                                                         */
        /**/private             group           G            = CreateGroup()     /**/
        /*                                                                         */
        /**/private             unit            Dummy                            /**/
        /*                                                                         */
        /**/private             real            Main_Time    = 0.                /**/
        /*                                                                         */
        /**/private             rect            RECT         = Rect(0.,0.,0.,0.) /**/
        /*                                                                         */
        //***************************************************************************
        /**/private   constant  real      FACING       =    ICE_AREA_1+ICE_AREA_2/**/
        /**/private   constant  real      MAX_COUNT    =    DUMMY_COUNT*2        /**/
        //***************************************************************************
    endglobals
    
    //Damage settings********************************************
    constant function damageSetting takes integer lvl returns real
        return DAMAGE_BASE*lvl
    endfunction
    //************************************************************
    
    //Duration settings*******************************************
    constant function durationSetting takes integer lvl returns real
        return LOTUS_DURATION+lvl
    endfunction
    //************************************************************
    
    //Conditions function*****************************************
    constant function conditionFunc takes player caster_owner,unit other returns boolean
    /**/                                            //Assure a better death check//
    /**/return IsUnitEnemy(other,caster_owner) and /**/GetUnitTypeId(other) != 0/**/and not IsUnitType(other,UNIT_TYPE_DEAD)
    endfunction
    //************************************************************
    
    private struct LotusIce
        
        unit dummy
        
        weathereffect weather
        
        real vinc
        real value_inc
        real frost_rep
        real frost_int
        real frost_inc
        real ag
        real gx
        real gy
        real lotus_dur
        
        boolean frost_active = false
        boolean spell_active = true
        
        integer frost_count = FROST_COUNT
        integer fade
        integer value_fade = 0
        
        player playe
        
        static method onPeriodic takes nothing returns nothing
        
            local integer i = 0
            local integer l = 0
            
            local real a
            local real x
            local real y
            
            local unit f = null
            
            local thistype this
            
            loop
                exitwhen i > MUI
                
                set this = StructData[i]
                
                if spell_active then
                
                    if value_inc < ICE_SCALE_1 then
                    
                        set value_inc = value_inc + vinc
                        set value_fade = value_fade + fade
                        
                        call SetUnitScale(dummy,value_inc,0.,0.)
                        call SetUnitVertexColor(dummy,255,255,255,value_fade)
                    
                    else
                        
                        if lotus_dur > 0. then
                        
                            set lotus_dur = lotus_dur - PERIODIC
                            set Main_Time = lotus_dur
                            
                            if frost_rep < FROST_REPEAT and frost_count == FROST_COUNT then
                                set frost_rep = frost_rep + PERIODIC
                            else
                                set frost_rep = 0.
                                set frost_active = true
                            endif
                            
                            if frost_active then
                                if frost_int < FROST_INTEVAR then
                                    set frost_int = frost_int + PERIODIC
                                else
                                    set frost_int = 0.
                                    if frost_count > 0 then
                                        set frost_inc = frost_inc + RANGE_PER_PERIOD
                                        set frost_count = frost_count - 1
                                        
                                        static if not DO_DUMMY_INIT then
                                            set Dummy = CreateUnit(Player(15),CASTER_DUMMY,0.,0.,0.)
                                        endif
                                        
                                        loop
                                            exitwhen i > DUMMY_COUNT
                                            set a = .0174533*i*ag
                                            set x = gx + frost_inc * Cos(a)
                                            set y = gy + frost_inc * Sin(a)
                                            call DestroyEffect(AddSpecialEffect(ICE_EFFECT,x,y))
                                            call GroupEnumUnitsInRange(G,x,y,FREEZE_RADIUS,null)
                                            loop
                                                set f = FirstOfGroup(G)
                                                exitwhen f == null
                                                
                                                if conditionFunc(playe,f) then
                                                    call SetUnitX(Dummy,GetUnitX(f))
                                                    call SetUnitY(Dummy,GetUnitY(f))
                                                    call IssueTargetOrder(Dummy,ORDER_ID,f)
                                                endif
                                                
                                                call GroupRemoveUnit(G,f)
                                            endloop
                                            
                                            set i = i + 1
                                        endloop
                                        
                                        static if not DO_DUMMY_INIT then
                                            call RemoveUnit(Dummy)
                                        endif
                                        
                                    else
                                        set frost_inc = 0.
                                        set frost_count = FROST_COUNT
                                        set frost_active = false
                                    endif
                                endif
                            endif
                        
                        else
                            set spell_active = false
                        endif
                        
                    endif
                elseif value_inc > 0. then
                    
                    set value_inc = value_inc - vinc
                    set value_fade = value_fade - fade
                    
                    call SetUnitScale(dummy,value_inc,0.,0.)
                    call SetUnitVertexColor(dummy,255,255,255,value_fade)
                    
                else
                    set StructData[i] = StructData[MUI]
                    set StructData[MUI] = -2
                    set MUI = MUI - 1
                    
                    if MUI == -1 then
                        call PauseTimer(TIMER)
                    endif
                    
                    call RemoveUnit(dummy)
                    call RemoveWeatherEffect(weather)
                    
                    set weather = null
                    set dummy = null
                    set playe = null
                    
                    call destroy()
                endif
                
                set i = i + 1
            endloop
        
        endmethod
        
        static method onCast takes nothing returns boolean
            local thistype this
            
            local integer i
            local integer l
            local integer alvl
            local integer unitid
            
            local real dx
            local real dy
            local real angle
            local real cs
            local real sc
            local real rg
            local real icea
            local real cos
            local real sin
            
            local unit caster
            
            if GetSpellAbilityId() != SPELL_ID then
                set caster = null
                return false
            endif
            
            set this = allocate()
            
            set MUI = MUI + 1
            set StructData[MUI] = this
            
            set playe = GetTriggerPlayer()
            
            set caster = GetTriggerUnit()
            
            set gx = GetSpellTargetX()
            set gy = GetSpellTargetY()
            
            call SetRect(RECT,gx-FACING,gy-FACING,gx+FACING,gy+FACING)
            set weather = AddWeatherEffect(RECT,WEATHER_TYPE)
            call EnableWeatherEffect( weather, true )
            
            set alvl = GetUnitAbilityLevel(caster,SPELL_ID)
            set lotus_dur = durationSetting(alvl)
            
            set ag = 360./DUMMY_COUNT
            
            set i = 0
            
            set fade = R2I(255./RANGE_MISSLE_2*MISSLE_SPEED)
            
            set dummy = CreateUnit(Player(15),DUMMY_DEG_1,gx,gy,0.)
            call SetUnitScale(dummy,0.,0.,0.)
            call SetUnitVertexColor(dummy,255,255,255,0)
            
            loop
                exitwhen i > MAX_COUNT
                
                if i < DUMMY_COUNT then
                    set cs = .0174533*i*ag
                    set cos = Cos(cs)
                    set sin = Sin(cs)
                    set rg = RANGE_MISSLE_1
                    set frost_int = gx + FACING * cos
                    set frost_inc = gy + FACING * sin
                    set value_inc = gx + rg * cos
                    set frost_rep = gy + rg * sin
                    set unitid = DUMMY_DEG_2
                    set icea = ICE_AREA_1
                    set dx = gx + icea * cos
                    set dy = gy + icea * sin
                    set sc = ICE_SCALE_2
                else
                    set rg = RANGE_MISSLE_2
                    set l = i - DUMMY_COUNT
                    set cs = .0174533*l*ag
                    set cos = Cos(cs)
                    set sin = Sin(cs)
                    set frost_int = gx + FACING * cos
                    set frost_inc = gy + FACING * sin
                    set value_inc = gx + rg * cos
                    set frost_rep = gy + rg * sin
                    set unitid = DUMMY_DEG_3
                    set rg = RANGE_MISSLE_2
                    set icea = ICE_AREA_2
                    set dx = gx + icea * cos
                    set dy = gy + icea * sin
                    set sc = ICE_SCALE_3
                endif
                
                set angle = 57.29583 * Atan2(frost_inc - dy, frost_int - dx)
                set vinc  =  57.29583 * Atan2(gy - frost_rep, gx - value_inc)
                
                call LotusIce_LotusIcePlugin.add(CreateUnit(Player(15),DUMMY_MISSLE,value_inc,frost_rep,vinc),unitid,dx,dy,angle,vinc,rg,icea,sc,gx,gy,i,damageSetting(alvl),playe,caster,dummy)
                
                set i = i + 1
            endloop
            
            set value_inc = 0.
            set frost_rep = 0.
            set frost_int = 0.
            set frost_inc = 0.
            set vinc      = ICE_SCALE_1/RANGE_MISSLE_2*MISSLE_SPEED
            
            if MUI == 0 then
                call TimerStart(TIMER,PERIODIC,true,function thistype.onPeriodic)
            endif
            
            set caster = 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)
            
            static if DO_DUMMY_INIT then
                set Dummy = CreateUnit(Player(15),CASTER_DUMMY,0.,0.,0.)
            endif
            
        endmethod
    endstruct
    
    public struct LotusIcePlugin
        
        unit dummy
        unit caster
        unit mdummy
        
        integer dummyid
        integer count
        
        boolean spell_active = true
        
        player playe
        
        real scale = 0.
        real dmg
        real tx
        real ty
        real dx
        real dy
        real df
        real cos
        real sin
        real maxsc
        real icearea
        real distance
        real dmg_int = 0.
        
        static method onPeriodic takes nothing returns nothing
            local integer i = 0
            
            local real a
            local real x
            local real y
            
            local boolean pull
            local boolean dmg_active = false
            
            local unit f = null
            
            local thistype this
            
            loop
                exitwhen i > PMUI
                
                set this = PStructData[i]
                
                if spell_active then
                
                    if distance > icearea then
                    
                        set distance = distance - MISSLE_SPEED
                        
                        set x = GetUnitX(dummy) + MISSLE_SPEED * cos
                        set y = GetUnitY(dummy) + MISSLE_SPEED * sin
                        
                        call SetUnitX(dummy,x)
                        call SetUnitY(dummy,y)
                        
                    elseif scale < maxsc then
                    
                        if GetUnitTypeId(dummy) > 0 then
                            if GetRandomInt(0,100) <= EFFECT_NUMBER then
                                call DestroyEffect(AddSpecialEffect(ICE_EFFECT,dx,dy))
                            endif
                            call RemoveUnit(dummy)
                            set dummy = CreateUnit(Player(15),dummyid,dx,dy,df)
                            call SetUnitScale(dummy,0.,0.,0.)
                        endif
                        
                        set scale = scale + SCALE_PER_PERIOD
                        call SetUnitScale(dummy,scale,0.,0.)
                        
                    else
                    
                        if dmg_int < DAMAGE_INTEVAR then
                            set dmg_int = dmg_int + PERIODIC
                        else
                            set dmg_int = 0.
                            set dmg_active = true
                        endif
                        
                        if GetUnitAbilityLevel(f,BUFF_ID) == 0 then
                            call GroupEnumUnitsInRange(G,dx,dy,KNOCK_RADIUS,null)
                            set pull = false
                        else
                            call GroupEnumUnitsInRange(G,dx,dy,PULL_RANGE,null)
                            set pull = true
                        endif
                        
                        loop
                        
                            set f = FirstOfGroup(G)
                            exitwhen f == null
                            
                            if conditionFunc(playe,f) then
                            
                                set x = GetUnitX(f)
                                set y = GetUnitY(f)
                            
                                if GetUnitAbilityLevel(f,BUFF_ID) == 0 then
                                    set df = 57.29583 * Atan2(y - dy,x - dx)
                                    set a =  .0174533*df
                                
                                    set x = x + ENEMIES_KNOCK * Cos(a)
                                    set y = y + ENEMIES_KNOCK * Sin(a)
                                else
                                    set df = 57.29583 * Atan2(dy - y,dx - x)
                                    set a =  .0174533*df
                                
                                    set x = x + ENEMIES_PULL * Cos(a)
                                    set y = y + ENEMIES_PULL * Sin(a)
                                endif
                                
                                call SetUnitX(f,x)
                                call SetUnitY(f,y)
                                
                            endif
                            
                            call GroupRemoveUnit(G,f)
                            
                        endloop
                        
                        if dmg_active then
                        
                            call GroupEnumUnitsInRange(G,dx,dy,DMG_RADIUS,null)
                            
                            loop
                                set f = FirstOfGroup(G)
                                exitwhen f == null
                                
                                if conditionFunc(playe,f) then
                                    call UnitDamageTarget(caster,f,dmg,true,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
                                endif
                                
                                call GroupRemoveUnit(G,f)
                                
                            endloop
                            
                        endif
                        
                        if GetUnitTypeId(mdummy) == 0 then
                            set spell_active = false
                        endif
                        
                    endif
                    
                elseif scale > 0. then
                    set scale = scale - SCALE_PER_PERIOD
                    call SetUnitScale(dummy,scale,0.,0.)
                else
                    set PStructData[i] = PStructData[PMUI]
                    set PStructData[PMUI] = -2
                    set PMUI = PMUI - 1
                    
                    if PMUI == -1 then
                        call PauseTimer(TIMER_2)
                    endif
                    
                    call RemoveUnit(dummy)
                    set dummy = null
                    set caster = null
                    set playe = null
                    
                    call destroy()
                endif
                
                set i = i + 1
            endloop
            
        endmethod
        
        static method add takes unit u,integer unitid,real x,real y,real f,real f1,real rg,real icea,real sc,real t,real tt,integer c,real damage,player p,unit ct,unit maind returns nothing
            local thistype this = allocate()
            
            set PMUI = PMUI + 1
            set PStructData[PMUI] = this
            
            set maxsc = sc
            set dummy = u
            set dummyid = unitid
            set dx = x
            set dy = y
            set df = f
            set cos = Cos(.0174533*f1)
            set sin = Sin(.0174533*f1)
            set icearea = icea
            set distance = rg
            set count = c
            set tx = t
            set ty = tt
            set dmg = damage
            set playe = p
            set caster = ct
            set mdummy = maind
            
            if PMUI == 0 then
                call TimerStart(TIMER_2,PERIODIC,true,function thistype.onPeriodic)
            endif
            
        endmethod
        
    endstruct
    
endlibrary
Changelog

v1.0: First release version
v1.0b: Remove variables tx, ty, a1, a, x, y, code optimized
v1.1: Minor bugs fixed.
v1.1b: Add GetUnitTypeId(other) != 0 to assure a better death check
Keywords:
ice,lotus
Contents

Just another Warcraft III map (Map)

Reviews
19:55, 2nd Mar 2014 Magtheridon96: The code is practically flawless in terms of efficiency and readability. The spell is also wonderfully configurable. This is 5/5 material, so I'll just go ahead and give you the rating. A tiny improvement would...

Moderator

M

Moderator

19:55, 2nd Mar 2014
Magtheridon96: The code is practically flawless in terms of efficiency and readability. The spell is also wonderfully configurable. This is 5/5 material, so I'll just go ahead and give you the rating.

A tiny improvement would be to check GetUnitTypeId(other) != 0 inside the conditionFunc to assure a better death check.

Good job.
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
local unit f = null -> local unit f

Use call TimerStart(TIMER, PERIODIC, true, function thistype.onPeriodic) and call PauseTimer(TIMER) instead of starting the timer each time.

For the onCast method do something like this :
JASS:
local ...
local ...
if GetSpellAbilityId() == SPELL_ID then
    Spell core
endif
return false

JASS:
            set gx = tx
            set gy = ty
Error PJASS variables gx non declared :p

At last I suggest to always work in radian instead of writing each time the value of bj_DEGTORAD ^^
 
local unit f = null -> local unit f

Use call TimerStart(TIMER, PERIODIC, true, function thistype.onPeriodic) and call PauseTimer(TIMER) instead of starting the timer each time.

For the onCast method do something like this :
JASS:
local ...
local ...
if GetSpellAbilityId() == SPELL_ID then
    Spell core
endif
return false

JASS:
            set gx = tx
            set gy = ty
Error PJASS variables gx non declared :p

At last I suggest to always work in radian instead of writing each time the value of bj_DEGTORAD ^^

Cokemonkey said:
local unit FoG=null //We initially declare an empty unit handler

JASS:
if GetSpellAbilityId() == SPELL_ID then
    Spell core
endif
return false

I don't like this way :p

Use call TimerStart(TIMER, PERIODIC, true, function thistype.onPeriodic) and call PauseTimer(TIMER) instead of starting the timer each time.

You didn't read my code carefully :)
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
It is pretty useless to declare the local unit u to null ^^...

JASS:
if GetSpellAbilityId() != SPELL_ID then
                set caster = null
                return false
            endif
->
JASS:
if GetSpellAbilityId() != SPELL_ID then
                //No need of this line if you want to keep this : set caster = null
                return false
            endif
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,522
Yes, the initial null isn't strictly necessary (I ususally don't declare it), but if you use/check that variable beyond the scope of your FoG loop, it might be useful to have it initialized as null. The performance difference is also negligible, and if it's easier for you to read/understand it then keep it :)

Spell in the video looks good, don't really have time to check the code though.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
Yes, the initial null isn't strictly necessary (I ususally don't declare it), but if you use/check that variable beyond the scope of your FoG loop, it might be useful to have it initialized as null. The performance difference is also negligible, and if it's easier for you to read/understand it then keep it :)

Spell in the video looks good, don't really have time to check the code though.

I thought that all locals and globals were initialized to null?
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,522
Malhorne, I think you have some form of http://en.wikipedia.org/wiki/Selective_auditory_attention

Yes, the initial null isn't strictly necessary (I ususally don't declare it), but if you use/check that variable beyond the scope of your FoG loop, it might be useful to have it initialized as null. The performance difference is also negligible, and if it's easier for you to read/understand it then keep it :)
 
Top