Spellpack The Frost Revenant

Faust

You can change this now in User CP.
Reaction score
123
Good evening people!
May I present you my first spellpack uploaded to thehelper:
The Frost Revenant!

A sorcerer of Nortrend, highly adept in using cold magics, a hero of my future AoS map. It took some time to "convert" it to this, better configurable shape, I hope it worth it.
3 of his spells were inspired by Thanatos' Frost Spiral spell (of Arthas the Death Knight), so thank you for him! :)

This is in vJASS, I used ABC, MUI, as far as I see it, leakless spell.
It may create minor lag when you cast the spells for the first time, and a bigger one if many of the abilities are used at the same time, but I think it's okay.

I tried to make it the most configurable possible. You can find almost all the perimeters of the spell in the beginning of each script. If you can't, look in the object editor :p

I'm not yet aware of any bugs, except when you cast the rotating spells near the edge of the map.

All 4 of his spells can be easily made to be channeling and back.

All productive comments are welcome; bug reports, suggestions of optimizing, ideas and features for the test map, tooltips - anything and everything.

Credit is something I don't care about, I'll happy to see him or one of his spells in another game. :p

The globals might be messed up a little, but I think my code is readable and easy.

So let's begin!

Freeze

Channels powerful cold magic from the Nortrend skies into the target unit, slowing it down by trapping it into ice.
When the energy overflows, it finally burts out dealing damage and slowing neighbouring enemies.

Channeling

JASS:
scope Freeze initializer Init

globals
    private constant integer FREEZEID = 'A006' // Raw code of the ability.
    private constant integer FREEZEIDD = 'A003' // Raw code of the dummy's slow ability.
    private constant integer FREEZEIDD2 = 'A007' // Raw code of the dummy's frost nova ability.
    private constant string LIGHTNING = "DRAM" // String of the Lightning.
    private constant string ORDERSTRINGDUMMY = "slow" // Order string of the dummy's ability.
    private constant string ORDERSTRINGDUMMY2 = "frostnova" // Other order string of the dummy's ability.
    private constant string EFFECTMODEL1 = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl" // Model of the effect on target.
    private constant string EFFECTMODEL2 = "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdl" // Model of the ice chunk on target.
    private constant string ATTACHPOINT = "origin" // EFFECTMODEL2's attachment point
    private constant integer ORDERSTRINGID = 852663 // Order string of the ability.
    private constant real RANGEOFTHEDAMAGE = 200 // Damages units withing this distance from the center.
    private constant real DAMAGEBASE = 10 // The base damage it deals to enemies.
    private constant real DAMAGEPERLEVEL = 25 // How much damage more it deals each level
    private constant integer DUMMYUNITID = 'u001' // Raw code of the dummyunit.
    private constant real HEIGHT =  3000 // From how high the lightning comes.
    private constant real HEIGHT2 =  40 // From the unit's feet upwards how high should the lightning end
    private constant real VALUE =  1.00 / 140.00 // A value in calculation of the spell
    private constant real TIMEBASE = 0.100 // A base value in calculation of the spell
    private constant attacktype ATTACKTYPE = ATTACK_TYPE_NORMAL // Damage's attack type
    private constant damagetype DAMAGETYPE = DAMAGE_TYPE_NORMAL // Damage's damage type
    private constant weapontype WEAPONTYPE = WEAPON_TYPE_WHOKNOWS // Damage's weapon type
endglobals

private function C takes nothing returns boolean
    return GetSpellAbilityId() == FREEZEID
endfunction

private struct Freeze
    unit caster
    unit target
    timer clock = CreateTimer()
    lightning beam
    integer ticks = 0
    effect icechunk
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect(.icechunk)
        call PauseTimer(.clock)
        call ClearTimerStructA(.clock)
        call DestroyTimer(.clock)
    endmethod
endstruct

private function H takes nothing returns nothing
    local Freeze dat = GetTimerStructA(GetExpiredTimer())
    local real tx = GetUnitX(dat.target)
    local real ty = GetUnitY(dat.target)
    local real cx = GetUnitX(dat.caster)
    local real cy = GetUnitY(dat.caster)
    local group UG = CreateGroup()
    local unit target
    local location tpoint = GetUnitLoc(dat.target)
    local real tz = GetLocationZ(tpoint) + GetUnitFlyHeight(dat.target) + HEIGHT2
    local unit dummy
    call DestroyEffect(AddSpecialEffect(EFFECTMODEL1, tx, ty))
    call RemoveLocation(tpoint)
    set tpoint = null
    call MoveLightningEx(dat.beam, true, tx, ty, tz, cx, cy, HEIGHT)
    if dat.ticks == 35 then
     set dat.icechunk = AddSpecialEffectTarget(EFFECTMODEL2, dat.target, ATTACHPOINT)
    endif
    if dat.ticks - (dat.ticks / 5) * 5 == 0 then
     set dummy = CreateUnit(GetOwningPlayer(dat.caster), DUMMYUNITID, cx, cy, 0)
     call UnitAddAbility(dummy, FREEZEIDD)
     call SetUnitAbilityLevel(dummy, FREEZEIDD, dat.ticks / 5)
     call IssueTargetOrder(dummy, ORDERSTRINGDUMMY, dat.target)
     set dummy = null
    endif
    set dat.ticks = dat.ticks + 1
    if GetUnitCurrentOrder(dat.caster) != ORDERSTRINGID then
     call DestroyLightning(dat.beam)
     call dat.destroy()
    endif
    if dat.ticks == 60 then
     call IssueImmediateOrder(dat.caster, "stop")
     call DestroyLightning(dat.beam)
     set udg_Player = GetOwningPlayer(dat.caster)
     call GroupEnumUnitsInRange(UG, GetUnitX(dat.target), GetUnitY(dat.target), RANGEOFTHEDAMAGE, Condition(function IsUnitAnEnemy))
     loop
      set target = FirstOfGroup(UG)
     exitwhen target == null
      set dummy = CreateUnit(GetOwningPlayer(dat.caster), DUMMYUNITID, cx, cy, 0)
      call UnitAddAbility(dummy, FREEZEIDD2)
      call SetUnitAbilityLevel(dummy, FREEZEIDD2, GetUnitAbilityLevel(dat.caster, FREEZEID))
      call IssueTargetOrder(dummy, ORDERSTRINGDUMMY2, target)
      call UnitDamageTarget(dat.caster, target, DAMAGEBASE + DAMAGEPERLEVEL * GetUnitAbilityLevel(dat.caster, FREEZEID), true, false, ATTACKTYPE, DAMAGETYPE, WEAPONTYPE)
      call GroupRemoveUnit(UG, target)
      set dummy = null
      set target = null
     endloop
     call UnitDamageTarget(dat.caster, dat.target, DAMAGEBASE + DAMAGEPERLEVEL * GetUnitAbilityLevel(dat.caster, FREEZEID), true, false, ATTACKTYPE, DAMAGETYPE, WEAPONTYPE)
     call DestroyGroup(UG)
     set UG = null
     call dat.destroy()
    endif
    call DestroyGroup(UG)
    set UG = null
    set dummy = null
endfunction

private function A takes nothing returns nothing
    local Freeze dat = Freeze.create()
    local real value = I2R(GetUnitAbilityLevel(GetTriggerUnit(), FREEZEID)) * 1.00
    local real time = TIMEBASE + VALUE - value * VALUE
    set dat.caster = GetTriggerUnit()
    set dat.target = GetSpellTargetUnit()
    set dat.beam = AddLightningEx(LIGHTNING, true, 0, 0, 0, 0, 0, 0)
    call SetTimerStructA(dat.clock, dat)
    call TimerStart(dat.clock, time, true, function H)
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function C))
    call TriggerAddAction(t, function A)
endfunction

endscope

Screenshot:
Freeze_www.kepfeltoltes.hu_.jpg


Hail
Channels powerful magic into the Nortrend skies, calling down a heavy hailstorm.
Deals damage and slows enemy units.

JASS:
scope Hail initializer Init

globals
    private constant integer HAILID = 'A004' // Raw code of the ability.
    private constant integer HAILIDD = 'A005' // Raw code of the dummy's frost nova ability.
    private constant string ORDERSTRING = "frostnova" // Order string of the dummy's ability.
    private constant real RANGEOFTHEDAMAGE = 300 // Damages units withing this distance from the center.
    private constant real DAMAGEBASE = 10 // The base damage it deals to enemies.
    private constant real DAMAGEPERLEVEL = 45 // How much damage more it deals each level
    private constant integer EFFECTUNITID = 'n004' // Raw code of the flying effect unit.
    private constant integer EFFECTUNITID2 = 'n003' // Raw code of the blizzard effect unit.
    private constant integer DUMMYUNITID = 'u001' // Raw code of the dummyunit.
    private constant real FLYINGHEIGHTINCREASE = 17 // How quickly the snowballs descend.
    private constant real ANGLEINCREMENT = 6 // How quickly the snowballs rotate around the center.
    private constant real EFFECTDISTANCE = 225 // How far are the effects from the center at start.
    private constant real EFFECTDISTANCECHANGE = 5 // Increment of the effects from the center.
    private constant real SPEEDOFH1 = 0.01 // How quickly should handler 1 run (and how fast things happen in it)
    private constant real SPEEDOFH2 = 0.9 // How quickly should handler 2 run (and how fast things happen in it)
    private constant real MAXHEIGHT =  2000 // At which height the effects stop spawning
    private constant real LIFESPAN = 2.5 // How long the uniteffects remain
    private constant real BLIZZARDSIZE = 0.8 // How big is the blizzardunit
    private constant real BLIZZARDSIZEPERLEVEL = 0.1 // How bigger is the blizzardunit every level
    private constant integer BLIZZARDAMOUNT = 45 // How many blizzardunits are there
    private constant attacktype ATTACKTYPE = ATTACK_TYPE_NORMAL // Damage's attack type
    private constant damagetype DAMAGETYPE = DAMAGE_TYPE_NORMAL // Damage's damage type
    private constant weapontype WEAPONTYPE = WEAPON_TYPE_WHOKNOWS // Damage's weapon type
endglobals

private function C takes nothing returns boolean
    return GetSpellAbilityId() == HAILID
endfunction

private struct Hail
    unit caster
    timer clock = CreateTimer()
    real ex
    real ey
    real ez = 0
    real tx
    real ty
    real facing = GetRandomReal(0, 360)
    real distance = EFFECTDISTANCE
    
    method onDestroy takes nothing returns nothing
        call PauseTimer(.clock)
        call ClearTimerStructA(.clock)
        call DestroyTimer(.clock)
    endmethod
endstruct

private function H2 takes nothing returns nothing
    local Hail dat = GetTimerStructA(GetExpiredTimer())
    local group UG = CreateGroup()
    local unit target
    local unit dummy
    set udg_Player = GetOwningPlayer(dat.caster)
    call GroupEnumUnitsInRange(UG, dat.tx, dat.ty, RANGEOFTHEDAMAGE, Condition(function IsUnitAnEnemy))
    loop
     set target = FirstOfGroup(UG)
     exitwhen target == null
     set dummy = CreateUnit(GetOwningPlayer(dat.caster), DUMMYUNITID, dat.tx, dat.ty, 0)
     call UnitAddAbility(dummy, HAILIDD)
     call UnitDamageTarget(dat.caster, target, DAMAGEBASE + DAMAGEPERLEVEL * GetUnitAbilityLevel(dat.caster, HAILID), true, false, ATTACKTYPE, DAMAGETYPE, WEAPONTYPE)
     call SetUnitAbilityLevel(dummy, HAILIDD, GetUnitAbilityLevel(dat.caster, HAILID))
     call IssueTargetOrder(dummy, ORDERSTRING, target)
     call GroupRemoveUnit(UG, target)
     set dummy = null
     set target = null
    endloop
    call DestroyGroup(UG)
    set UG = null
    call dat.destroy()
endfunction

private function H takes nothing returns nothing
    local Hail dat = GetTimerStructA(GetExpiredTimer())
    local unit eunit
    local integer ticks = 0
    set dat.ex = dat.tx + dat.distance * Cos(dat.facing * bj_DEGTORAD)
    set dat.ey = dat.ty + dat.distance * Sin(dat.facing * bj_DEGTORAD)
    set dat.ez = dat.ez + FLYINGHEIGHTINCREASE
    set dat.facing = dat.facing + ANGLEINCREMENT
    set dat.distance = dat.distance + EFFECTDISTANCECHANGE
    set eunit = CreateUnit(GetOwningPlayer(dat.caster), EFFECTUNITID, dat.ex, dat.ey, dat.facing)
    call UnitApplyTimedLife(eunit, 'omfg', LIFESPAN)
    call SetUnitFlyHeight(eunit, dat.ez, 0)
    set eunit = null
    if dat.ez >= MAXHEIGHT then
     call PauseTimer(dat.clock)
     call TimerStart(dat.clock, SPEEDOFH2, false, function H2)
     loop
     exitwhen ticks == BLIZZARDAMOUNT
      set ticks = ticks + 1
      set dat.ex = dat.tx + GetRandomReal(0, RANGEOFTHEDAMAGE) * Cos(GetRandomReal(0, bj_PI*2))
      set dat.ey = dat.ty + GetRandomReal(0, RANGEOFTHEDAMAGE) * Sin(GetRandomReal(0, bj_PI*2))
      set eunit = CreateUnit(GetOwningPlayer(dat.caster), EFFECTUNITID2, dat.ex, dat.ey, GetRandomReal(1, 360))
      call SetUnitScale(eunit, BLIZZARDSIZE + I2R(GetUnitAbilityLevel(dat.caster, HAILID)) * BLIZZARDSIZEPERLEVEL, BLIZZARDSIZE + I2R(GetUnitAbilityLevel(dat.caster, 'A02O')) * BLIZZARDSIZEPERLEVEL, BLIZZARDSIZE + I2R(GetUnitAbilityLevel(dat.caster, 'A02O')) * BLIZZARDSIZEPERLEVEL)
      set eunit = null
     endloop
    endif
endfunction

private function A takes nothing returns nothing
    local Hail dat = Hail.create()
    local location tpoint = GetSpellTargetLoc()
    set dat.tx = GetLocationX(tpoint)
    set dat.ty = GetLocationY(tpoint)
    call RemoveLocation(tpoint)
    set tpoint = null
    set dat.caster = GetTriggerUnit()
    call SetTimerStructA(dat.clock, dat)
    call TimerStart(dat.clock, SPEEDOFH1, true, function H)
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function C))
    call TriggerAddAction(t, function A)
endfunction

endscope

Screenshots:
Hail1_www.kepfeltoltes.hu_.jpg
Hail2_www.kepfeltoltes.hu_.jpg


Freezing Field

Draws chilling power from the skies of Northrend, continously damaging and greatly slowing enemy units in the area.

JASS:
scope FF initializer Init

globals
    private constant integer FFID = 'A001' // Raw code of the ability.
    private constant integer FFIDD = 'A002' // Raw code of the dummy's thunder clap ability.
    private constant string ORDERSTRING = "thunderclap" // Order string of the dummy's ability.
    private constant integer AMOUNTOFSLOPES = 4 // How many starting points of the snowballs and novas
    private constant real DAMAGEBASEperPULSE = 25 // How much base damage it deals each pulse.
    private constant real DAMAGEperLEVELperPULSE = 25 // How much addition damage it deals each level.
    private constant real ANGLEINCREMENTOFTHENOVAS = 15 // How quickly the landed frost nova effects rotate.
    private constant real DISTANCEOFTHENOVAS = 375 // How far the frost nova effects are from the center.
    private constant real RANGEOFTHEDAMAGE = 400 // Damages units withing this distance from the center.
    private constant string NOVAEFFECTMODEL = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl" // The actual model of the landed special effect. (for the flying ones, see object editor)
    private constant integer EFFECTUNITID = 'n000' // Raw code of the effect unit.
    private constant integer DUMMYUNITID = 'u001' // Raw code of the dummyunit.
    private constant real FLYINGHEIGHTDECREASE = 12 // How quickly the snowballs descend.
    private constant real ANGLEINCREMENTOFTHESNOWBALLS = 6 // How quickly the snowballs rotate around the center.
    private constant real DISTANCEOFTHESNOWBALLS = 350 // How far are the snowballs from the center.
    private constant real SPEEDOFH1 = 0.05 // How quickly should handler 1 run (and how fast things happen in it)
    private constant real SPEEDOFH2 = 0.1 // How quickly should handler 2 run (and how fast things happen in it)
    private constant real STARTINGHEIGHT =  1200 // From how high should the effect units start spawning
    private constant real LIFESPAN = 0.8 // How long the "snowballs" remain
    private constant attacktype ATTACKTYPE = ATTACK_TYPE_NORMAL // Damage's attack type
    private constant damagetype DAMAGETYPE = DAMAGE_TYPE_NORMAL // Damage's damage type
    private constant weapontype WEAPONTYPE = WEAPON_TYPE_WHOKNOWS // Damage's weapon type
endglobals

private struct FF
    unit caster
    timer clock = CreateTimer()
    real array ex[AMOUNTOFSLOPES]
    real array ey[AMOUNTOFSLOPES]
    real ez = STARTINGHEIGHT
    real cx
    real cy
    real array facing[AMOUNTOFSLOPES]
    integer ticks = 0
    
    method onDestroy takes nothing returns nothing
        call PauseTimer(.clock)
        call ClearTimerStructA(.clock)
        call DestroyTimer(.clock)
    endmethod
endstruct

private function C takes nothing returns boolean
    return GetSpellAbilityId() == FFID
endfunction

private function H2 takes nothing returns nothing
    local FF dat = GetTimerStructA(GetExpiredTimer())
    local unit dummy
    local integer ticks = 0
    local integer times = R2I(I2R(GetUnitAbilityLevel(dat.caster, FFID)) + 1) / 2
    local group UG = CreateGroup()
    local unit victim
    loop
    exitwhen ticks == AMOUNTOFSLOPES
     set ticks = ticks + 1
     set dat.facing[ticks - 1] = dat.facing[ticks - 1] + ANGLEINCREMENTOFTHENOVAS
     set dat.ex[ticks - 1] = dat.cx + DISTANCEOFTHENOVAS * Cos(dat.facing [ticks - 1] * bj_DEGTORAD)
     set dat.ey[ticks - 1] = dat.cy + DISTANCEOFTHENOVAS * Sin(dat.facing [ticks - 1] * bj_DEGTORAD)
     call DestroyEffect(AddSpecialEffect(NOVAEFFECTMODEL, dat.ex[ticks - 1], dat.ey[ticks - 1]))
    endloop
    set dat.ticks = dat.ticks + 1
    if 0 == dat.ticks - (dat.ticks / 12) * 12 or dat.ticks == 1 then
     set dummy = CreateUnit(GetOwningPlayer(dat.caster), DUMMYUNITID, dat.cx, dat.cy, 0)
     call UnitAddAbility(dummy, FFIDD)
     set udg_Player = GetOwningPlayer(dat.caster)
     call GroupEnumUnitsInRange(UG, dat.cx, dat.cy, RANGEOFTHEDAMAGE, Condition(function IsUnitAnEnemy))
     loop
      set victim = FirstOfGroup(UG)
     exitwhen victim == null
      call UnitDamageTarget(dat.caster, victim, DAMAGEBASEperPULSE + DAMAGEperLEVELperPULSE * GetUnitAbilityLevel(dat.caster, FFID), true, false, ATTACKTYPE, DAMAGETYPE, WEAPONTYPE)
      call GroupRemoveUnit(UG, victim)
      set victim = null
     endloop
     call IssueImmediateOrder(dummy, ORDERSTRING)
     set dummy = null
     set victim = null
     call DestroyGroup(UG)
     set UG = null
    endif
    set dummy = null
    if dat.ticks == 24 * times then
     call dat.destroy()
    endif
endfunction

private function H takes nothing returns nothing
    local FF dat = GetTimerStructA(GetExpiredTimer())
    local unit eunit
    local integer ticks = 0
    loop
    exitwhen ticks == AMOUNTOFSLOPES
     set ticks = ticks + 1
     set dat.facing[ticks - 1] = dat.facing[ticks - 1] + ANGLEINCREMENTOFTHESNOWBALLS
     set dat.ex[ticks - 1] = dat.cx + DISTANCEOFTHESNOWBALLS * Cos(dat.facing [ticks - 1] * bj_DEGTORAD)
     set dat.ey[ticks - 1] = dat.cy + DISTANCEOFTHESNOWBALLS * Sin(dat.facing [ticks - 1] * bj_DEGTORAD)
     set dat.ez = dat.ez - FLYINGHEIGHTDECREASE
     set eunit = CreateUnit(GetOwningPlayer(dat.caster), EFFECTUNITID, dat.ex[ticks - 1], dat.ey[ticks - 1], dat.facing[ticks - 1])
     call SetUnitFlyHeight(eunit, dat.ez, 0)
     call UnitApplyTimedLife(eunit, 'omfg', LIFESPAN)
     set eunit = null
     set dat.ticks = dat.ticks + 1
    endloop
    if dat.ez < 25 then
     call PauseTimer(dat.clock)
     set dat.ticks = 0
     call TimerStart(dat.clock, SPEEDOFH2, true, function H2)
    endif
endfunction

private function A takes nothing returns nothing
    local FF dat = FF.create()
    local location tpoint = GetSpellTargetLoc()
    local integer tick = 0
    set dat.cx = GetUnitX(GetTriggerUnit())
    set dat.cy = GetUnitY(GetTriggerUnit())
    set dat.facing[0] = GetRandomReal(0, 360)
    loop
    exitwhen tick > AMOUNTOFSLOPES
     set tick = tick + 1
     set dat.facing[tick] = dat.facing[tick-1] + 360 / AMOUNTOFSLOPES
    endloop
    call RemoveLocation(tpoint)
    set tpoint = null
    set dat.caster = GetTriggerUnit()
    call SetTimerStructA(dat.clock, dat)
    call TimerStart(dat.clock, SPEEDOFH1, true, function H)
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function C))
    call TriggerAddAction(t, function A)
endfunction

endscope

Screenshots:
Freezing_Field1_www.kepfeltoltes.hu_.jpg
Freezing_Field2_www.kepfeltoltes.hu_.jpg


Iceplosion
Channels strong cold energies into two magical particles that collide above the Frost Revenant, bursting into missiles that are fired all around him.
Each missile deals 100 area damage.

Channeling


JASS:
scope Iceplosion initializer Init

globals
    private constant integer ICEPLOSIONID = 'A008' // Raw code of the ability.
    private constant integer ICEPLOSIONIDD1 = 'A009' // Raw code of the frost attack ability.
    private constant integer ICEPLOSIONIDD2 = 'A00A' // Raw code of the frost attack ability.
    private constant integer ICEPLOSIONIDD3 = 'A00B' // Raw code of the frost attack ability.
    private constant integer ORDERSTRINGID = 852526 // Order string of the ability.
    private constant real RANGEOFTHEATTACK = 1000 // Damages units withing this distance from the center.
    private constant integer ATTACKUNITID = 'n001' // Raw code of the attackunit.
    private constant integer EFFECTUNITID = 'n002' // Raw code of the effectunit.
    private constant real HEIGHT = 0 // Starting height
    private constant real HEIGHTCHANGE = 8 // Height change
    private constant real DISTANCE =  600 // Starting distance
    private constant real DISTANCECHANGE = 8 // Distance change
    private constant real ANGLE =  0.05 // Starting speed of rotating 
    private constant real ANGLECHANGE =  0.24 // Changing of the speed base
    private constant real ANGLECHANGE2 =  0.01 // Changing of the speed each hero level
    private constant integer AMOUNTOFATTACKSBASE = 20 // Base amount of attacks each missile type
    private constant integer AMOUNTOFATTACKS = 5 // How many tiems more each effect spawns each hero level
    private constant real HANDLERSPEED = 0.05 // How quickly the H functions runs - and things happen
endglobals

private struct Ice
    unit caster
    timer clock = CreateTimer()
    unit array eu[2]
    real ez = HEIGHT
    real cx
    real cy
    real array facing[2]
    real distance = DISTANCE
    real anglediff = ANGLECHANGE
    
    method onDestroy takes nothing returns nothing
        call PauseTimer(.clock)
        call ClearTimerStructA(.clock)
        call DestroyTimer(.clock)
    endmethod
endstruct

private function C takes nothing returns boolean
    return GetSpellAbilityId() == ICEPLOSIONID
endfunction

private function H takes nothing returns nothing
    local Ice dat = GetTimerStructA(GetExpiredTimer())
    local real x
    local real y
    local integer tick1 = 1
    local unit dummy
    local integer tick2 = 0
    set dat.ez = dat.ez + HEIGHTCHANGE
    set dat.anglediff = dat.anglediff + ANGLECHANGE + I2R(GetUnitAbilityLevel(dat.caster, ICEPLOSIONID)) * ANGLECHANGE2
    set dat.distance = dat.distance - DISTANCECHANGE
    set dat.facing[0] = dat.facing[0] + dat.anglediff
    set dat.facing[1] = dat.facing[1] + dat.anglediff
    set x = dat.cx + dat.distance * Cos(dat.facing[0] * bj_DEGTORAD)
    set y = dat.cy + dat.distance * Sin(dat.facing[0] * bj_DEGTORAD)
    call SetUnitPosition(dat.eu[0], x, y)
    call SetUnitFlyHeight(dat.eu[0], dat.ez, 0)
    set x = dat.cx + dat.distance * Cos(dat.facing[1] * bj_DEGTORAD)
    set y = dat.cy + dat.distance * Sin(dat.facing[1] * bj_DEGTORAD)
    call SetUnitPosition(dat.eu[1], x, y)
    call SetUnitFlyHeight(dat.eu[1], dat.ez, 0)
    if GetUnitCurrentOrder(dat.caster) != ORDERSTRINGID then
     call KillUnit(dat.eu[0])
     call KillUnit(dat.eu[1])
     call dat.destroy()
    endif
    if dat.distance <= 10 then
     loop
     exitwhen tick1 > 3
      set tick2 = 0
      loop
       exitwhen tick2 == AMOUNTOFATTACKSBASE + AMOUNTOFATTACKS * GetUnitAbilityLevel(dat.caster, ICEPLOSIONID)
       set x = dat.cx + GetRandomReal(0, RANGEOFTHEATTACK) * Cos(GetRandomReal(0, bj_PI*2))
       set y = dat.cy + GetRandomReal(0, RANGEOFTHEATTACK) * Sin(GetRandomReal(0, bj_PI*2))
       set dummy = CreateUnit(GetOwningPlayer(dat.caster), ATTACKUNITID, dat.cx, dat.cy, 0)
       call UnitApplyTimedLife(dummy, 'omfg', 2)
       call SetUnitFlyHeight(dummy, dat.ez, 0)
       set tick2 = tick2 + 1
       if tick1 == 1 then
        call UnitAddAbility(dummy, ICEPLOSIONIDD1)
       endif
       if tick1 == 2 then
        call UnitAddAbility(dummy, ICEPLOSIONIDD2)
       endif
       if tick1 == 3 then
        call UnitAddAbility(dummy, ICEPLOSIONIDD3)
       else
       endif
       call IssuePointOrder(dummy, "attackground", x, y)
      endloop
      set tick1 = tick1 + 1
     endloop
     call IssueImmediateOrder(dat.caster, "stop")
     call RemoveUnit(dat.eu[0])
     call RemoveUnit(dat.eu[1])
     call dat.destroy()
    endif
endfunction

private function A takes nothing returns nothing
    local Ice dat = Ice.create()
    local real x
    local real y
    set dat.cx = GetUnitX(GetTriggerUnit())
    set dat.cy = GetUnitY(GetTriggerUnit())
    set dat.caster = GetTriggerUnit()
    set dat.facing[0] = GetRandomReal(0, 360)
    set dat.facing[1] = dat.facing[0] + 180
    set x = dat.cx + dat.distance * Cos(dat.facing[0] * bj_DEGTORAD)
    set y = dat.cy + dat.distance * Sin(dat.facing[0] * bj_DEGTORAD)
    set dat.eu[0] = CreateUnit(GetOwningPlayer(dat.caster), EFFECTUNITID, x, y, 0)
    set x = dat.cx + dat.distance * Cos(dat.facing[1] * bj_DEGTORAD)
    set y = dat.cy + dat.distance * Sin(dat.facing[1] * bj_DEGTORAD)
    set dat.eu[1] = CreateUnit(GetOwningPlayer(dat.caster), EFFECTUNITID, x, y, 0)
    call SetTimerStructA(dat.clock, dat)
    call TimerStart(dat.clock, HANDLERSPEED, true, function H)
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition(t, Condition(function C))
    call TriggerAddAction(t, function A)
endfunction

endscope

Screenshots:
Iceplosion1_www.kepfeltoltes.hu_.jpg
663814034Iceplosion2_www.kepfeltoltes.hu_.jpg

And a small bonus, not really configurable, unique or anything:


Cold Armour

Activate Cold Armour to engulf the Frost Revenant in a protective ring, that slows enemy units trying to harm him.
Drains mana and life until deactivated.

JASS:
scope ColdArmour initializer Init

globals
    trigger t = CreateTrigger()
    trigger t0 = CreateTrigger()
    private constant integer HEROID = 'u000' // The rawcode of the Frost Revenant
endglobals

private function C takes nothing returns boolean
    return GetUnitAbilityLevel(GetTriggerUnit(), 'B003') != 0 and GetOwningPlayer(GetTriggerUnit()) != GetOwningPlayer(GetEventDamageSource())
endfunction

private function C2 takes nothing returns boolean
    return GetUnitTypeId(GetFilterUnit()) == HEROID
endfunction

private function A takes nothing returns nothing
    local real x = GetUnitX(GetTriggerUnit())
    local real y = GetUnitY(GetTriggerUnit())
    local unit dummy = CreateUnit(GetOwningPlayer(GetTriggerUnit()), 'h000', x, y, 0)
    call IssueTargetOrder(dummy, "attack", GetEventDamageSource())
    call UnitApplyTimedLife(dummy, 'omfg', 2)
endfunction

private function A0 takes nothing returns nothing
    local group UG = CreateGroup()
    local unit ahero
    call GroupEnumUnitsInRect(UG, bj_mapInitialPlayableArea, Condition(function C2))
    loop
     set ahero = FirstOfGroup(UG)
    exitwhen ahero == null
     call TriggerRegisterUnitEvent(t, ahero, EVENT_UNIT_DAMAGED )
     set ahero = null
    endloop
    call DestroyGroup(UG)
    set UG = null
    set ahero = null
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    call TriggerRegisterTimerEvent(t0, 0.5, false)
    call TriggerAddAction(t0, function A0)
    call TriggerAddCondition(t, Condition(function C))
    call TriggerAddAction(t, function A)
endfunction

endscope

It's really nothing so no screenshot to this one. :p

IMPORTING INSTRUCTIONS::
Create a Player Variable called Player in the trigger editor
Copy the function in the map header
Copy the triggers
Copy the units, spells, buffs
Edit away :p

Tell me if I missed anything!
And now, shoot away!
 

Romek

Super Moderator
Reaction score
963
JASS:
    private constant integer FREEZEIDD = 'A003' // Raw code of the dummy's ability.
    private constant integer FREEZEIDD2 = 'A007' // Raw code of the dummy's ability.


What abilities? What do they do?
Import instructions? :p

Also, in function H of that spell, all the locals should be nulled at the end, not in a loop or an if block. They can still leak that way.
tpoint leaks there.

Those are my first comments on the spell code.. Will test it now :)
 

Flare

Stops copies me!
Reaction score
662
JASS:
private function A0 takes nothing returns nothing
    call TriggerRegisterUnitEvent(t, gg_unit_U000_0047, EVENT_UNIT_DAMAGED )
endfunction

Pre-placed units shouldn't be part of the code (unless it's only for demonstration purposes, which isn't the case here), it really reduces importability

JASS:
local real value = I2R(GetUnitAbilityLevel(GetTriggerUnit(), FREEZEID)) * 1.00

What's with the *1.00 ?

JASS:
local real time = 0.100 + VALUE - value * VALUE

The 0.100 should be a constant

JASS:
set dat.beam = AddLightningEx(LIGHTNING, true, 0, 0, 0, 0, 0, 0)

Why are you creating the lightning at (0, 0, 0)?

JASS:
call SetUnitFlyHeight(eunit, dat.ez, 999999)

You don't need the 999999 speed, 0 rate is instantaneous

JASS:
GetRandomReal(1, 360) * bj_DEGTORAD)
//Why not do
GetRandomReal (0, bj_PI*2)
//that'll get rid of the need for conversion


JASS:
call TimerStart(dat.clock, 1, false, function H2)
call TimerStart(dat.clock, 0.01, true, function H)

Timer interval there should be configurable

JASS:

(Haven't tested the spell yet, so I'm not sure how it would be affected, but) the low and high bound here should be configurables

JASS:
GetLocationZ(tpoint) + GetUnitFlyHeight(dat.target) + 40

The 40 should be configurable

JASS:
    real array facing[4]

Array size should be a constant (would allow people to determine the number of objects used, which is always nice since some people may want more/less

There's more hardcoded stuff lying around, but I wanna get back to playing CS:S for a while :D

And you're doing alot of dealings with degrees for no particular reason - you should try and get to know common degrees values as radians, makes things a little simpler if you can remember them :p

JASS:
It may create minor lag when you cast the spells for the first time

Add a preload option i.e.
JASS:
local unit u
if ENABLE_PRELOAD then
call DestroyEffect (AddSpecialEffect (...))
set u = CreateUnit (...))
call UnitAddAbility (...)
call UnitRemoveAbility (...)
call RemoveUnit (u)
endif
set u =null



All the same, quite alot of work there, good job
 

Faust

You can change this now in User CP.
Reaction score
123
JASS:
private function A0 takes nothing returns nothing
    call TriggerRegisterUnitEvent(t, gg_unit_U000_0047, EVENT_UNIT_DAMAGED )
endfunction


Pre-placed units shouldn't be part of the code (unless it's only for demonstration purposes, which isn't the case here), it really reduces importability

Fixed.


JASS:
local real value = I2R(GetUnitAbilityLevel(GetTriggerUnit(), FREEZEID)) * 1.00


What's with the *1.00 ?
Is it not necessary to ensure that it will have decimals?


JASS:

local real time = 0.100 + VALUE - value * VALUE


The 0.100 should be a constant
Done

JASS:
set dat.beam = AddLightningEx(LIGHTNING, true, 0, 0, 0, 0, 0, 0)


Why are you creating the lightning at (0, 0, 0)?

Why not? It is moved instantly anyway.
JASS:

call SetUnitFlyHeight(eunit, dat.ez, 999999)


and
JASS:

GetRandomReal(1, 360) * bj_DEGTORAD)
//Why not do
GetRandomReal (0, bj_PI*2)
//that'll get rid of the need for conversion


You don't need the 999999 speed, 0 rate is instantaneous
Thanks for the tip ^^


JASS:


(Haven't tested the spell yet, so I'm not sure how it would be affected, but) the low and high bound here should be configurables

The low bound not, the high yes - done

JASS:
GetLocationZ(tpoint) + GetUnitFlyHeight(dat.target) + 40


The 40 should be configurable

Done too

Add a preload option i.e.
Jass:

local unit u
if ENABLE_PRELOAD then
call DestroyEffect (AddSpecialEffect (...))
set u = CreateUnit (...))
call UnitAddAbility (...)
call UnitRemoveAbility (...)
call RemoveUnit (u)
endif
set u =null



All the same, quite alot of work there, good job

I really don't think a testmap should have that :\



And you're doing alot of dealings with degrees for no particular reason - you should try and get to know common degrees values as radians, makes things a little simpler if you can remember them

Some time, hopefully :rolleyes:

All the same, quite alot of work there, good job

Thanks ^^
 

Flare

Stops copies me!
Reaction score
662
Is it not necessary to ensure that it will have decimals?
You're doing the I2R conversion anyway, so that should deal with it

I really don't think a testmap should have that :\
It's not just for the test map - add a preload option for all the spells, so people can choose what they need to preload. Nobody likes the annoying pause caused by first-cast of spells, so you may as well allow people to get it over and done with when they will never notice it :p If the spells contain a preloading option, it makes it much simpler for anyone importing the spell since they won't have to do it themselves (apart from setting the preload constant to true :p)

If someone who doesn't know JASS does want to preload, they are going to have to define their SFX strings, dummy units and all that, then go through the boring process of finding the unit type, the SFX strings, the abilities, when it could've all been done with a simple false -> true (or they may not have to do anything if preloading is enabled by default)

Why not? It is moved instantly anyway.
Then create it where it's used?
 

Faust

You can change this now in User CP.
Reaction score
123
Then create it where it's used?
That would involve setting a point, removing it, nullifying it.
The moving function doesn't leak or anything. You can see nothing wrong with in game either.


As for the preload thing, I'll look into it in the future.
 

Bloodcount

Starcraft II Moderator
Reaction score
297
Wow... really cool spells.But how did you make them so good looking?I mean was it hard to add all those special effects?
 

Faust

You can change this now in User CP.
Reaction score
123
I have good imagination ^^

What would be hard in adding special effects? o_O
 

Bloodcount

Starcraft II Moderator
Reaction score
297
well, i will sound stupid here, but i don't really know how to add special effects, but how have you made the effects for Freezing Field?
 

Faust

You can change this now in User CP.
Reaction score
123
The snowballs are units.
The novas are placed effects.
The ice chunks are buff
 

Bloodcount

Starcraft II Moderator
Reaction score
297
Well if it will make you feel better i have no freakin idea how you have made the effects :p
 

Sim

Forum Administrator
Staff member
Reaction score
534
Cool!

After several minutes of testing, noticed no flaws. Plus, the spells are indeed very nice :D

Approved!
 

mightylink

New Member
Reaction score
1
man this definetly tops as the best spells ive seen, the effects are just gorgeous my favorite spells ever, i give it seven thumbs up :D and +rep
 

darkreapers

New Member
Reaction score
6
Hi ,, I LOVE ur spells, especially hail and would like to use it as a ultimate for one of my heros. the problem is, iam pretty newbie at this import thing.

i gone to map perefences and set create all missing variable.
i coped, hail and hail unit 2 dummy.
i copied your hail trigger
i copied ur ABC trigger
i copied hail unit ability
i copied hail hero ability
to my map, but it says there is something wrong with trigger and it is disabled.

i jsut copy pasted the triggers into a new catergory under the name hail spell :confused:
 

sidove

Member
Reaction score
22
Hi ,, I LOVE ur spells, especially hail and would like to use it as a ultimate for one of my heros. the problem is, iam pretty newbie at this import thing.

i gone to map perefences and set create all missing variable.
i coped, hail and hail unit 2 dummy.
i copied your hail trigger
i copied ur ABC trigger
i copied hail unit ability
i copied hail hero ability
to my map, but it says there is something wrong with trigger and it is disabled.

i jsut copy pasted the triggers into a new catergory under the name hail spell :confused:

That cuz you need Jass new gen pack :)
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top