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

[vJass] Blood Sorceress Spellpack

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
This spellpack was made by me when Im in ChaosRealm spell contest, and I got the 2nd place.

Contains 4 spell :

Freezing Wave
a8a79f0ab2431a4daf4f367364917a44_99162.jpg


Remar Healing
1e416f5128f7676ae69ac6b93895de60_99163.jpg


Fire Ball
5e858f79e80afac9524f83029e34e4b4_99164.jpg


Thunder Rage

d565ca98b6e1ba4cc02946de6ce430c2_99165.jpg



Updated! Remake Freezing Wave code

Spells Code

Freezing Wave code
JASS:
scope BSFreezingWave  

    private struct Spell
    
        //--Spell configurations
        private static integer    ABI_CODE = 'A003'
        private static integer    ICE_CODE = 'h007'
        private static string     STM_SFX  = "war3mapImported\\Stomp.mdx"
        private static string     ICE_SFX  = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
        private static real       DISTANCE = 125
        private static real       AoE      = 150
        private static real       ANGLE    = 9
        private static real       TIMEOUT  = 0.2
        private static attacktype ATT_TYPE = null
        private static weapontype WPN_TYPE = null
        private static damagetype DMG_TYPE = null
        
        //--Leave everything below this comment
        private thistype next
        private thistype prev

        private static timer ptimer = CreateTimer()
        private static integer count = 0

        private unit caster
        private real damage
        private real maxdist
        private real curdist
        private real angle

        private method destroy takes nothing returns nothing

            call this.deallocate()

            set this.next.prev = this.prev
            set this.prev.next = this.next

            set count = count - 1
            if count == 0 then
                call PauseTimer(ptimer)
            endif

            set this.caster = null

        endmethod

        private static method periodic takes nothing returns nothing

            local thistype this = thistype(0).next
            local group    grp
            local unit     enm
            local real     x
            local real     y
            local real     angleb
            
            loop
                exitwhen this == 0

                set grp = CreateGroup()
                
                set x = GetUnitX(this.caster)
                set y = GetUnitY(this.caster)
                
                if this.curdist < this.maxdist then
                
                    set this.curdist = this.curdist + DISTANCE
                    set angleb       = this.angle + (GetRandomReal(-ANGLE, ANGLE) * bj_DEGTORAD)
                    set x            = x + this.curdist * Cos(angleb)
                    set y            = y + this.curdist * Sin(angleb)
                    
                    call DestroyEffect(AddSpecialEffect(ICE_SFX, x, y))
                    call DestroyEffect(AddSpecialEffect(STM_SFX, x, y))
                    
                    call GroupEnumUnitsInRange(grp, x, y, AoE, null)
                    loop
                        set enm = FirstOfGroup(grp)
                        exitwhen enm == null
                        
                        if IsUnitAliveBJ(enm) == true and IsUnitEnemy(enm, GetOwningPlayer(this.caster)) == true then

                            call UnitDamageTarget(this.caster, enm, this.damage, true, true, ATT_TYPE, DMG_TYPE, WPN_TYPE)
                            
                            call GroupRemoveUnit(grp, enm)
                        else
                            call GroupRemoveUnit(grp, enm)
                        endif
                        
                    endloop
                    
                else
                    call this.destroy()
                endif

                call DestroyGroup(grp)
                set grp = null
                
                set this = this.next
            endloop
        endmethod

        private static method start takes nothing returns boolean

            local thistype this 
            local integer  lvl     = GetUnitAbilityLevelSwapped(ABI_CODE,GetTriggerUnit())
            local location array l

            if GetSpellAbilityId() == ABI_CODE then
            
                set this = thistype.allocate()
            
                set this.next = 0
                set this.prev = thistype(0).prev
                set thistype(0).prev.next = this
                set thistype(0).prev = this

                set count = count + 1
                if count == 1 then
                    call TimerStart(ptimer, TIMEOUT, true, function thistype.periodic)
                endif

                set l[0] = GetUnitLoc(GetTriggerUnit())
                set l[1] = GetSpellTargetLoc()
                
                set this.caster  = GetTriggerUnit()
                set this.damage  = 40 * I2R(lvl)
                set this.maxdist = (100.00 * lvl) + 500.00
                set this.curdist = 0.00
                set this.angle   = AngleBetweenPoints(l[0], l[1]) * bj_DEGTORAD
                
                call SetUnitAnimation(this.caster, "Attack")
                
                call RemoveLocation(l[0])
                call RemoveLocation(l[1])
                set l[0] = null
                set l[1] = null
                
                return true
            endif
            return false
        endmethod

        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function thistype.start))
            
            set t = null
        endmethod
        
    endstruct
    
endscope



Remar Healing code
JASS:
scope RemarHealing initializer init

    globals
        private constant hashtable HS             = InitHashtable()
        private constant integer   SPELL_ID       = 'A002' // Spell raw code.
        private constant integer   HOLY_MISSILE   = 'h00A' // Holy Missile raw code.
        private constant string    DIVINE_BARRIER = "war3mapImported\\DivineBarrier1.mdx" // Effect that will be created on caster at the spell startup
        private constant string    END_EFFECT     = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" // Effect that wil be created on caster and target at the end of the spell.
        private constant real      HEAL_TIME      = 2.00 // How long the caster gonna heal herself and delay before release healing missile.
        private constant integer   MAX_TARGET     = 8 // Max missile that will be created.
        private constant real      TIME_OUT       = 0.03125 // How often the timers fire.
        private constant real      SPEED          = 25 // Healing missile speed.
        private constant real      CURVE          = 2 // Healing missile curve
    endglobals

    private function Parabolic takes real  dist, real  maxdist,real  curve returns real 
        local real  t = (dist*2)/maxdist-1
        return (-t*t+1)*(maxdist/curve)
    endfunction
    
    private function Distance takes real x1, real y1, real x2, real y2 returns real
        return SquareRoot((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
    endfunction

    private struct beam
        unit c
        unit t
        unit d
        real hot // Heal over time(HOT)
        real spd
        real x
        real y
        
        method destroy takes nothing returns nothing
            call UnitApplyTimedLife(.d,'BTLF',0.01)
            call .deallocate()
        endmethod
        
        static method onLoop takes nothing returns nothing
         local timer z    = GetExpiredTimer()
         local beam  this = LoadInteger(HS,GetHandleId(z),0)
         local real  x = GetUnitX(.d)
         local real  y = GetUnitY(.d)
         local real  mdist = Distance(.x,.y,GetUnitX(.t),GetUnitY(.t))
         local real  dist = Distance(GetUnitX(.d),GetUnitY(.d),GetUnitX(.t),GetUnitY(.t))
         local real  a    = Atan2(GetUnitY(.t)-GetUnitY(.d),GetUnitX(.t)-GetUnitX(.d))*bj_RADTODEG
            if dist > 50 then
                set x = x+.spd*Cos(a*bj_DEGTORAD)
                set y = y+.spd*Sin(a*bj_DEGTORAD)
                call UnitAddAbility(.d,'Amrf')
                call UnitRemoveAbility(.d,'Amrf')
                call SetUnitFlyHeight(.d,Parabolic(dist,mdist,CURVE),0)
                call SetUnitPosition(.d,x,y)
                if IsUnitAliveBJ(.t) == false then
                    call DestroyEffect(AddSpecialEffect(END_EFFECT,x,y))
                    call DestroyTimer(z)
                    call .destroy()
                endif
            else
                call DestroyEffect(AddSpecialEffectTargetUnitBJ("origin",.t,END_EFFECT))
                call SetUnitLifeBJ(.t,GetUnitStateSwap(UNIT_STATE_LIFE,.t)+.hot)
                call DestroyTimer(z)
                call .destroy()
            endif
        endmethod
        
        static method create takes unit c, unit t, real h, real speed returns thistype
         local beam  this = beam.allocate()
         local timer z    = CreateTimer()
         local real  a    = Atan2(GetUnitY(t)-GetUnitY(c),GetUnitX(t)-GetUnitX(c))*bj_RADTODEG
            set .t = t
            set .c = c
            set .hot = h
            set .spd = speed
            set .d = CreateUnit(GetOwningPlayer(.c),HOLY_MISSILE,GetUnitX(.c),GetUnitY(.c),a)
            set .x = GetUnitX(.c)
            set .y = GetUnitY(.c)
            call SaveInteger(HS,GetHandleId(z),0,this)
            call TimerStart(z,TIME_OUT,true,function beam.onLoop)
            return this
        endmethod
        
    endstruct
    
    private struct data
        unit c
        real heal
        real radius
        real time = 0
        effect eff
        integer ct = 0
        
        method destroy takes nothing returns nothing
            call .deallocate()
        endmethod
        
        static method onLoop takes nothing returns nothing
         local timer   z    = GetExpiredTimer()
         local data    this = LoadInteger(HS,GetHandleId(z),0)
         local real    hot  = .heal/(HEAL_TIME/TIME_OUT)
         local unit    enm 
         local group   e    = CreateGroup()
            set .time = .time+TIME_OUT
            if .time < HEAL_TIME and IsUnitAliveBJ(.c) == true and GetUnitLifePercent(.c) != 100 then
                call SetUnitLifeBJ(.c,GetUnitStateSwap(UNIT_STATE_LIFE,.c)+hot)
            else
                call DestroyEffect(.eff)
                call DestroyEffect(AddSpecialEffectTargetUnitBJ("origin",.c,END_EFFECT))
                call GroupEnumUnitsInRange(e,GetUnitX(.c),GetUnitY(.c),.radius,null)
                loop
                    set enm = FirstOfGroup(e)
                    exitwhen enm == null
                    if .ct < MAX_TARGET then
                        if IsUnitEnemy(enm,GetOwningPlayer(.c)) == false and IsUnitAliveBJ(enm) == true and GetUnitLifePercent(enm) != 100 and enm != .c then
                            call beam.create(.c,enm,.heal,SPEED)
                            set .ct = .ct+1
                            call GroupRemoveUnit(e,enm)
                        else
                            call GroupRemoveUnit(e,enm)
                        endif
                    else
                        call DestroyTimer(z)
                        call .destroy()
                    endif
                endloop
                call DestroyGroup(e)
                call DestroyTimer(z)
                call .destroy()
            endif
        endmethod
        
        static method create takes unit c returns thistype
         local data  this   = data.allocate()
         local timer z      = CreateTimer()
         local integer lvl    = GetUnitAbilityLevelSwapped(SPELL_ID,c) // Spell level
         local real a_heals = lvl*100 // Heals amount
         local real radius  = lvl*500 // AoE
         
            set .c = c
            set .heal = a_heals
            set .radius = radius
            set .eff = AddSpecialEffectTargetUnitBJ("origin",.c,DIVINE_BARRIER)
            call SaveInteger(HS,GetHandleId(z),0,this)
            call TimerStart(z,TIME_OUT,true,function data.onLoop)
            return this
        endmethod
        
    endstruct
    
    private function Conditions takes nothing returns boolean
        if ( not ( GetSpellAbilityId() == SPELL_ID ) ) then
            return false
        endif
        return true
    endfunction

    private function Actions takes nothing returns nothing
        call data.create(GetTriggerUnit())
    endfunction

    private function init takes nothing returns nothing
     local trigger t = CreateTrigger()
     local integer index
        set index = 0
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set index = index + 1
            exitwhen index == bj_MAX_PLAYER_SLOTS
        endloop
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function Actions)
        set t = null
    endfunction
endscope



Fire Ball code
JASS:
scope FireBall initializer init

    globals
        private constant integer   SPELL_ID      = 'A001' // Spell raw code.
        private constant hashtable HS            = InitHashtable() // Very-very important thing in this spell.
        private constant string    EFFECT2ATTACH = "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl" // Effect that will be attached on sorceress staff.
        private constant string    EXPLODE       = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl" // Explosion effect that will be created at fire ball when it reach the target.
        private constant string    EFFECT_ATTACH = "weapon" // In the default sorceress model.The staff is at "weapon" attachment.
        private constant string    ANIM_NAME     = "spell" // Animation name when the fire ball creation begins.
        private constant integer   FIRE_BALL     = 'h008' // Fire ball dummy raw code.
        private constant integer   EXPLOSION     = 'h009' // Explosion dummy raw code.
        private constant real      TIME_OUT      = 0.03125 // How often the timers fire.
        private constant real      CRE_SPEED     = 0.200   // How often the fire ball will be created.
        private constant integer   MAX_HIT       = 5 // Max fire ball that will be created.Better dont config this.Just to make sure the spells damage balance with the other spell.
        private constant real      BEAM_SPEED    = 15 // Fire ball movement speed
        private constant real      BEAM_CURVE    = 0.05 // Make sure this value is smaller than BEAM_SPEED.
        private constant real      FLY_CURVE     = 2.00 // Fire ball curve.Better dont config this too.
    endglobals

    private function Distance takes real x1, real y1, real x2, real y2 returns real
        return SquareRoot((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
    endfunction
    
    private function Parabolic takes real  dist, real  maxdist,real  curve returns real 
        local real  t = (dist*2)/maxdist-1
        return (-t*t+1)*(maxdist/curve)
    endfunction
    
    private struct beam
        unit c
        unit t
        unit d
        real x
        real y
        real curve
        real rate
        real angle
        real speed
        real damage
        real dist
        real mdist
        
        method destroy takes nothing returns nothing
            call .deallocate()
        endmethod
        
        static method onLoop2 takes nothing returns nothing
         local timer r    = GetExpiredTimer()
         local beam  this = LoadInteger(HS,0,GetHandleId(r))
         local real  a
         local real  x
         local real  y
            if .angle < 0 then
                set .angle = .angle + .rate
            endif
            set a = (bj_RADTODEG*Atan2((GetUnitY(.t)-GetUnitY(.d)),(GetUnitX(.t)-GetUnitX(.d)))) - .angle
            set x = GetUnitX(.d)+.speed*Cos(a*bj_DEGTORAD)
            set y = GetUnitY(.d)+.speed*Sin(a*bj_DEGTORAD)
            set .mdist = Distance(.x,.y,GetUnitX(.t),GetUnitY(.t))
            set .dist = Distance(GetUnitX(.d),GetUnitY(.d),GetUnitX(.t),GetUnitY(.t))
            call SetUnitPosition(.d,x,y)
            call SetUnitFlyHeight(.d,Parabolic(.dist,.mdist,FLY_CURVE),0)
            if Distance(x,y,GetUnitX(.t),GetUnitY(.t)) <= 50 then
                call UnitDamageTarget(.c,.t,.damage,true,true,null,null,null)
                call DestroyEffect(AddSpecialEffect(EXPLODE,GetUnitX(.t),GetUnitY(.t)))
                set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(.c),EXPLOSION,GetUnitX(.t),GetUnitY(.t),GetRandomReal(1,180))
                call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',3)
                call UnitApplyTimedLife(.d,'BTLF',.01)
                call DestroyTimer(r)
                call .destroy()
            elseif IsUnitAliveBJ(.t) == false then
                call UnitApplyTimedLife(.d,'BTLF',.01)
                call DestroyTimer(r)
                call .destroy()
            endif
        endmethod
        
        static method onLoop takes nothing returns nothing
         local timer r    = GetExpiredTimer()
         local beam  this = LoadInteger(HS,0,GetHandleId(r))
         local real  a
         local real  x
         local real  y
            if .angle > 0 then
                set .angle = .angle - .rate
            endif
            set a = (bj_RADTODEG*Atan2((GetUnitY(.t)-GetUnitY(.d)),(GetUnitX(.t)-GetUnitX(.d)))) - .angle
            set x = GetUnitX(.d)+.speed*Cos(a*bj_DEGTORAD)
            set y = GetUnitY(.d)+.speed*Sin(a*bj_DEGTORAD)
            set .mdist = Distance(.x,.y,GetUnitX(.t),GetUnitY(.t))
            set .dist = Distance(GetUnitX(.d),GetUnitY(.d),GetUnitX(.t),GetUnitY(.t))
            call SetUnitPosition(.d,x,y)
            call SetUnitFlyHeight(.d,Parabolic(.dist,.mdist,FLY_CURVE),0)
            if Distance(x,y,GetUnitX(.t),GetUnitY(.t)) <= 50 then
                call UnitDamageTarget(.c,.t,.damage,true,true,null,null,null)
                call DestroyEffect(AddSpecialEffect(EXPLODE,GetUnitX(.t),GetUnitY(.t)))
                set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(.c),EXPLOSION,GetUnitX(.t),GetUnitY(.t),GetRandomReal(1,180))
                call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',3)
                call UnitApplyTimedLife(.d,'BTLF',.01)
                call DestroyTimer(r)
                call .destroy()
            elseif IsUnitAliveBJ(.t) == false then
                call UnitApplyTimedLife(.d,'BTLF',.01)
                call DestroyTimer(r)
                call .destroy()
            endif
        endmethod
        
        static method create takes unit caster, unit target, real AngleC, real CRate, real speeds, real dmg returns thistype
         local beam  this = beam.allocate()
         local timer r    = CreateTimer()
         local real  a 
            set .c = caster
            set .t = target
            set a = Atan2((GetUnitY(.t)-GetUnitY(.c)),(GetUnitX(.t)-GetUnitX(.c))) * bj_RADTODEG
            set .d = CreateUnit(GetOwningPlayer(.c),FIRE_BALL,GetUnitX(.c)+70*Cos(a*bj_DEGTORAD),GetUnitY(.c)+70*Sin(a*bj_DEGTORAD),AngleC)
            set .rate = CRate
            set .angle = AngleC
            set .speed = speeds
            set .damage = dmg
            set .x = GetUnitX(.c)
            set .y = GetUnitY(.c)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(.c),FIRE_BALL,GetUnitX(.c)+70*Cos(a*bj_DEGTORAD),GetUnitY(.c)+70*Sin(a*bj_DEGTORAD),AngleC)
            call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',0.01)
            call SaveInteger(HS,0,GetHandleId(r),this)
            if .angle >= 0 then
                call TimerStart(r,TIME_OUT,true,function beam.onLoop)
            else
                call TimerStart(r,TIME_OUT,true,function beam.onLoop2)
            endif
            return this
        endmethod
        
    endstruct
    
    private struct data
        unit    c
        unit    t
        integer hit
        effect  eff
        real    dmg
        
        method destroy takes nothing returns nothing
            call PauseUnit(.c,false)
            call DestroyEffect(.eff)
            call .deallocate()
        endmethod
        
        static method onLoop takes nothing returns nothing
         local timer z    = GetExpiredTimer()
         local data  this = LoadInteger(HS,0,GetHandleId(z))
         local real  r
         local real  a    = Atan2((GetUnitY(.t)-GetUnitY(.c)),(GetUnitX(.t)-GetUnitX(.c)))
            if GetRandomReal(1,100) <= 50 then
                set r = GetRandomReal(-70,-15)
            else
                set r = GetRandomReal(15,70)
            endif
            call beam.create(.c,.t,r,BEAM_CURVE,BEAM_SPEED,.dmg)
            set hit = hit - 1
            if hit <= 0 then
                call DestroyTimer(z)
                call .destroy()
            endif
        endmethod
        
        static method create takes unit c, unit t returns thistype
         local data    this   = data.allocate()
         local timer   z      = CreateTimer()
         local integer lvl    = GetUnitAbilityLevelSwapped(SPELL_ID,c)// Spell level
         local real    damage = (10*lvl)+50 // Damage amount when the fire ball explode at the target.Default MAX_HIT = 5,so the full damage is 5*damage.
            set .c = c
            set .t = t
            set .hit = MAX_HIT
            set .dmg = damage
            set .eff = AddSpecialEffectTarget(EFFECT2ATTACH,.c,EFFECT_ATTACH)
            call PauseUnit(.c,true)
            call SetUnitAnimation(.c,ANIM_NAME)
            call SaveInteger(HS,0,GetHandleId(z),this)
            call TimerStart(z,CRE_SPEED ,true,function data.onLoop)
            return this
        endmethod
        
    endstruct
    
    private function Conditions takes nothing returns boolean
        if ( not ( GetSpellAbilityId() == SPELL_ID ) ) then
            return false
        endif
        return true
    endfunction
    
    private function Actions takes nothing returns nothing
        call data.create(GetTriggerUnit(),GetSpellTargetUnit())
    endfunction

    private function init takes nothing returns nothing
     local trigger t     = CreateTrigger()
     local integer index
        set index = 0
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set index = index + 1
            exitwhen index == bj_MAX_PLAYER_SLOTS
        endloop
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function Actions)
        set t = null
    endfunction
endscope


Thunder Rage code
JASS:
scope ThunderRage initializer init

    globals 
        private constant integer   SPELL_ID      = 'A000' // Ability raw code.
        private constant integer   SLOW_ID       = 'A001' // Slow ability raw code.
        private constant integer   THUNDER_ID    = 'h000' // Thunder raw code.
        private constant integer   R_ASHES_ID    = 'h001' // Red ashes raw code.
        private constant integer   SHADOWLVL1_ID = 'h006' // Shadow LVL 1 raw code.
        private constant integer   SHADOWLVL2_ID = 'h005' // Shadow LVL 2 raw code.
        private constant integer   SHADOWLVL3_ID = 'h004' // Shadow LVL 3 raw code.
        private constant integer   DUMMY_CASTER  = 'h007' // Dummy caster raw code.
        private constant string    STOMP_EFFECT  = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
        private constant real      SPELL_TIME    = 3.00 // How long the thunders gonna wait for the enemy.
        private constant real      TIME_OUT      = 0.03125 // How often the timers fire.
        private constant hashtable HS            = InitHashtable() // Spell hashtable.Very important.
    endglobals
    
    private function stopshake takes nothing returns nothing
     local timer    z = GetExpiredTimer()
     local integer id = GetHandleId(z)
        call CameraClearNoiseForPlayer(LoadPlayerHandle(HS,id,1))
        call DestroyTimer(z)
    endfunction
    
    private function shake takes player p, real m, real t returns nothing
     local timer    z = CreateTimer()
     local integer id = GetHandleId(z)
        call CameraSetEQNoiseForPlayer(p,m)
        call SavePlayerHandle(HS,id,1,p)
        call TimerStart(z,t,false,function stopshake)
    endfunction
    
    private function destroydes takes nothing returns nothing
        call KillDestructable(GetEnumDestructable())
    endfunction
    
    private function effc takes nothing returns nothing
     local timer    z      = GetExpiredTimer()
     local integer  id     = GetHandleId(z)
     local location p      = LoadLocationHandle(HS,id,1)
     local real     ani    = LoadReal(HS,id,2)
     local real     radius = LoadReal(HS,id,3)
     local real     time   = LoadReal(HS,id,4)
     local player   pl     = LoadPlayerHandle(HS,id,5)
     local location p2
        if time <= SPELL_TIME-(SPELL_TIME*0.1) then
            set ani = ani+1
            if ani >= (TIME_OUT*8)/TIME_OUT then
                set p2 = GetRandomLocInRect(RectFromCenterSizeBJ(p,radius,radius))
                set bj_lastCreatedUnit = CreateUnit(pl,THUNDER_ID,GetLocationX(p2),GetLocationY(p2),0.00)
                call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',1.00)
                call RemoveLocation(p2)
                set ani = 0
            endif
            call SaveReal(HS,id,2,ani)
            set time = time+TIME_OUT
            call SaveReal(HS,id,4,time)
        else
            call DestroyTimer(z)
            call RemoveLocation(p)
        endif

    endfunction
    
    private function effs takes player pl, real x, real y, real radius returns nothing
     local timer z = CreateTimer()
     local integer id = GetHandleId(z)
     local location p = Location(x,y)
        call SaveLocationHandle(HS,id,1,p)
        call SaveReal(HS,id,2,0.00)
        call SaveReal(HS,id,3,radius)
        call SaveReal(HS,id,4,0.00)
        call SavePlayerHandle(HS,id,5,pl)
        call TimerStart(z,TIME_OUT,true,function effc)
    endfunction
    
    private function onLoop takes nothing returns nothing
     local timer    z      = GetExpiredTimer()
     local integer  id     = GetHandleId(z)
     local unit     caster = LoadUnitHandle(HS,id,1)
     local unit     enemy
     local unit     enum
     local real     x1
     local real     y1
     local real     x      = LoadReal(HS,id,2)
     local real     y      = LoadReal(HS,id,3)
     local real     radius = LoadReal(HS,id,4)
     local real     damage = LoadReal(HS,id,5)
     local real     time   = LoadReal(HS,id,6)
     local group    egroup = LoadGroupHandle(HS,id,7)
     local group    dgroup = LoadGroupHandle(HS,id,8)
     local group    en     = CreateGroup()
     local location p      = Location(x,y)
        if time < SPELL_TIME then
            call GroupEnumUnitsInRange(en,x,y,radius,null)
            loop
                set enemy = FirstOfGroup(en)
                exitwhen enemy == null
                if IsUnitEnemy(enemy,GetOwningPlayer(caster)) == true and IsUnitAliveBJ(enemy) == true then
                    call PauseUnit(enemy,true)
                    call SetUnitTimeScalePercent(enemy,0)
                    call GroupAddUnit(egroup,enemy)
                    call GroupRemoveUnit(en,enemy)
                else
                    call GroupRemoveUnit(en,enemy)
                endif
            endloop
            call DestroyGroup(en)
            set time = time + TIME_OUT
            call SaveReal(HS,id,6,time)
        else
            if CountUnitsInGroup(egroup) == 0 then
                set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),THUNDER_ID,x,y,0.00)
                call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',3.00)
                set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),R_ASHES_ID,x,y,0.00)
                call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',2)
                call DestroyEffect(AddSpecialEffect(STOMP_EFFECT,x,y))
            else
                loop
                    set enum = FirstOfGroup(egroup)
                    exitwhen enum == null
                    set x1 = GetUnitX(enum)
                    set y1 = GetUnitY(enum)
                    set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),THUNDER_ID,x1,y1,0.00)
                    call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',3.00)
                    set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),R_ASHES_ID,x1,y1,0.00)
                    call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',2)
                    call DestroyEffect(AddSpecialEffect(STOMP_EFFECT,x1,y1))
                    call UnitDamageTargetBJ(caster,enum,damage,null,null)
                    call shake(GetOwningPlayer(enum),15,.5)
                    call PauseUnit(enum,false)
                    call SetUnitTimeScalePercent(enum,100)
                    call GroupRemoveUnit(egroup,enum)
                endloop
            endif
            call EnumDestructablesInCircleBJ(radius,p,function destroydes)
            call RemoveLocation(p)
            call CameraClearNoiseForPlayer(GetOwningPlayer(caster))
            call shake(GetOwningPlayer(caster),15,.5)
            call CinematicFadeBJ( bj_CINEFADETYPE_FADEIN, SPELL_TIME/3, "ReplaceableTextures\\CameraMasks\\White_mask.blp", 100, 0, 0, 30 )
            loop
                set enum = FirstOfGroup(dgroup)
                exitwhen enum == null
                call GroupRemoveUnit(dgroup,enum)
                call RemoveUnit(enum)
            endloop
            call DestroyGroup(dgroup)
            call DestroyGroup(egroup)
            call DestroyTimer(z)
        endif
    endfunction
    
    private function Spell_Id takes nothing returns boolean
        if ( not ( GetSpellAbilityId() == SPELL_ID) ) then
            return false
        endif
        return true
    endfunction

    private function Actions takes nothing returns nothing
     local unit    caster = GetSpellAbilityUnit()
     local group   egroup = CreateGroup()
     local group   dgroup = CreateGroup()
     local timer   z      = CreateTimer()
     local integer id     = GetHandleId(z)
     local integer lvl    = GetUnitAbilityLevelSwapped(SPELL_ID,caster)// Spell level
     local integer c
     local real    x      = GetSpellTargetX()
     local real    y      = GetSpellTargetY()
     local real    radius = (100*lvl)+300 // You can change this
     local real    damage = (200*lvl)+300 // You also can change this one.
        if lvl == 1 then
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL1_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL1_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL1_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL1_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL1_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
        elseif lvl == 2 then
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL2_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL2_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL2_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL2_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL2_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
        elseif lvl == 3 then
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL3_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL3_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL3_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL3_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(caster),SHADOWLVL3_ID,x,y,0.00)
            call GroupAddUnit(dgroup,bj_lastCreatedUnit)
        endif
        call effs(GetOwningPlayer(caster),x,y,radius)
        call PauseUnit(caster,true)
        call SetUnitAnimation(caster,"channel")
        call PauseUnit(caster,false)
        // Save all of the data.
        call SaveUnitHandle(HS,id,1,caster)
        call SaveReal(HS,id,2,x)
        call SaveReal(HS,id,3,y)
        call SaveReal(HS,id,4,radius)
        call SaveReal(HS,id,5,damage)
        call SaveReal(HS,id,6,0.00)
        call SaveGroupHandle(HS,id,7,egroup)
        call SaveGroupHandle(HS,id,8,dgroup)
        call CinematicFadeBJ( bj_CINEFADETYPE_FADEOUT, SPELL_TIME/3, "ReplaceableTextures\\CameraMasks\\White_Mask.blp", 100.00, 0, 0, 80.00 )
        call CameraSetEQNoiseForPlayer(GetOwningPlayer(caster),3)
        call TimerStart(z,TIME_OUT,true,function onLoop)
    endfunction

    private function init takes nothing returns nothing
     local trigger t = CreateTrigger()
     local integer index
        set index = 0
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set index = index + 1
            exitwhen index == bj_MAX_PLAYER_SLOTS
        endloop
        call TriggerAddCondition(t,Condition(function Spell_Id))
        call TriggerAddAction(t,function Actions)
        set t = null
    endfunction
endscope

Credits :

Spells Icon :
I. Thunder Rage spell Icon - Black.Memory (from ChaosRealm.info)

Models :
I. Red After Shock - Dcrimson
II. Divine Barrier - JesusHipster
III. Stomp - nGy
IV. Holy Lion Missile - ratamahatta
V. Lighting Multiple - Tranquil
VI. Shrapnel Shards - WILL THE ALMIGHTY
VII. BloodElfPriestess - Sin'dorei300 and Uncle Fester
Contents

Just another Warcraft III map (Map)

Reviews
15:30, 21th June 2015 IcemanBo: http://www.hiveworkshop.com/forums/spells-569/vjass-blood-sorceress-spellpack-265292/#post2697185
Level 13
Joined
Aug 19, 2014
Messages
1,111
You should post the triggers, spell description and required libraries if any. I haven't tested the pack yet, I'll try it later though I think they're good.

Edit: the pack seems great, I like the spells in it.
 
Last edited:
- Add a proper spell description.
- Add documentation. At least for user in config part it's essential.
JASS:
private function Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == SPELL_ID ) ) then
        return false
    endif
    return true
endfunction
^We're not in GUI here. You can make it more efficient. You could directly return the expression.
But anyway you can combine action and condition into condition.
Just make a normal if statement to filter the correct ability.
- Don't use PauseUnit operation.
- The BJs can be avoided.
- One global group can be used instead of dynamicly create and destroy locals.
- Keep working in RAD in Jass. Deg is only needed for unit's facing.
- Local agents need to be nulled. Read the reference part: http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/#post2661133
- I haven't tested the spells yet, but I recomment you to make the cinematic operation optional. It's not MUI.
- Variable names and function names should be descriptive.
- v0.01 intends to be not a finished version. Change it next time when you upload.

Enough for next update.
 
You have not changed all mentioned points from my first feedback.

Furthermore,

  • The configs need to be documented.
  • == true and == false can be avoided.
    Just remove the true check, and instead of the false check
    you can place a not in front of the expression.
  • call GroupRemoveUnit(grp, enm) can be moved out of the if-statement.
  • OwnerOfCaster could be initialisized as member of an instance to reduce function calls.
  • Use natives over BJs.
  • Use functions to calculate damage, not simple a constant. In JASS you can use that neat feature to have more power in modifying.
  • Implement native UnitAlive into your code to check if a unit is dead, or check (if UnitTypeId equals 0, or unit type is UNIT_TYPE_DEAD).
  • Locations in your code are not needed. Work around.
  • Names like "HS" are not descriptive. Sometimes a bit longer names are better
    to gain readability. Please change them.
    Also names your struct members with only 1/2 letters need to be changed.
    Names should be descriptive if possible.
  • I suggets using TimerUtils. It can attach an instance to a timer, and recycle timers.
    It would be helpful in your case, because then you also can remove your hashtable. (and don't need to think about using Table)
  • Set/Get a unit's life needs to be done via Get/Set WidgetLife natives. It's faster.

There is pretty much to change, also again, please read again my previous post,
because there are still some critical points that needs to be changed.
 
Top