Spellpack Random Spells

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
Tom_Kazansky presents

A spellpack contains:
- Scatter Lightning
- Aerial Strike
- Earth Boulder
- Thunder Break

MUI/MPI - Both
GUI/Jass - vJass
Leakless - yeah, I... think
Original - nope but I think they have good visual effects. :D

Scatter Lightning
- Description:
Spreads out bolts of electrical power that damages enemy units within the area of effect. Each bolt deals 30 damage.

- Screenshot:
ScatterLightning.jpg

- Code:
JASS:
scope ScatterLightning initializer Init

globals
    private integer AbilID = 'A001'  //Raw Id of the Scatter Lightning ability
    private integer DummyId = 'n000' //Raw Id of the dummy unit (requires dummy.mdx )
    private real LightnigHeight = 600.0  //the height of the lightning
    private string LightningCode = "CHIM" //lightning code of the lightning effect
    private string MissileSFX = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl" //the missile art of the bolt of lightning
    private real MissileSpeed = 1000. //speed of the missile
    private string AreaSFX = "Abilities\\Spells\\Other\\ForkedLightning\\ForkedLightningTarget.mdl" //effect where the lightning strikes the ground
    private string TargetSFX = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //dealing damage sfx
    private string TargetSFX_A = "origin" //attachment point of the dealing damage sfx
    private real ScatterRadius = 400. //area of effect for scattering
    private real ExplodeRadius = 200. //area of effect for dealing damage.
    private attacktype ATKTYPE = ATTACK_TYPE_NORMAL //attack type, currently: Spell 
    private damagetype DMGTYPE = DAMAGE_TYPE_FIRE   //damage type, currently: "Magical"
endglobals
//================================
private function GetDamage takes integer lvl returns real
    return 30. //Get Damage Deal
endfunction
private function GetBoltCount takes integer lvl returns integer
    return (5 + 3*lvl) //Get number of bolts <- this number shouldn't be too high :|
endfunction
//================================
//---- Utility Functions -----
private function GetPPX takes real x, real dist, real angle returns real
    return x + dist * Cos(angle * bj_DEGTORAD)
endfunction

private function GetPPY takes real y, real dist, real angle returns real
    return y + dist * Sin(angle * bj_DEGTORAD)
endfunction

private function AngleLocXY takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
endfunction

private function DistanceLocXY takes real x1, real y1, real x2, real y2 returns real
    return SquareRoot( (x2-x1) * (x2-x1) + (y2-y1) * (y2-y1))
endfunction

private function SetUnitZ takes unit c, real z returns nothing
    call UnitAddAbility(c,'Amrf')
    call UnitRemoveAbility(c,'Amrf')
    call SetUnitFlyHeight(c,z,0.)
endfunction
//================================
private struct data
    unit c
    unit d
    effect e
    lightning b
    real x
    real y
    real dmg
    
    real angle
    real vel
    integer tick
    boolean strike = false
endstruct

private function ScatterLightningSingleF takes nothing returns boolean
    local data d = TT_GetData()
    local unit f = GetFilterUnit()
    local boolean ok = false
    if IsUnitInRangeXY(f,d.x,d.y,ExplodeRadius) then
        if GetWidgetLife(f) > 0.405 then
            if IsUnitEnemy(f,GetOwningPlayer(d.c)) then
                if not IsUnitType(f,UNIT_TYPE_STRUCTURE) and not IsUnitType(f,UNIT_TYPE_MAGIC_IMMUNE) then
                    set ok = true
                endif
            endif
        endif
    endif
    set f = null
    return ok
endfunction

function ScatterLightningSingleE takes nothing returns boolean
    local data d = TT_GetData()
    local real dis
    local real x
    local real y
    local group g
    local unit p
    local boolexpr bx
    if d.strike then
        if d.tick == 0 then
            call ShowUnit(d.d,false)
            call DestroyEffect(d.e)
            call KillUnit(d.d)

            call DestroyLightning(d.b)
            call d.destroy()
            return true
        endif
        call SetUnitVertexColor( d.d, 255, 255, 255, R2I( 255 * (d.tick / 10.) ) )
        call SetLightningColor(d.b, 1. , 1., 1., d.tick / 10. )
        set d.tick = d.tick - 1
    else
        if d.tick == 0 then
            set d.b = AddLightningEx( LightningCode ,true,d.x,d.y,700.,d.x,d.y,0)
            if AreaSFX != "" then
                call DestroyEffect( AddSpecialEffect(AreaSFX,d.x,d.y) )
            endif
            set g = CreateGroup()
            set bx = Condition( function ScatterLightningSingleF )
            call GroupEnumUnitsInRange(g , d.x, d.y, ExplodeRadius + 100. , bx)
            call DestroyBoolExpr(bx)
            set bx = null
            loop
                set p = FirstOfGroup(g)
                exitwhen p == null
                call GroupRemoveUnit(g,p)
                call UnitDamageTarget( d.c, p , d.dmg, false, true, ATKTYPE, DMGTYPE, null )
                if TargetSFX != "" then
                    call DestroyEffect( AddSpecialEffectTarget(TargetSFX,p,TargetSFX_A)  )
                endif
            endloop
            call DestroyGroup(g)
            set g = null
            set d.tick = 10
            set d.strike = true
        else
            set x = GetUnitX(d.d)
            set y = GetUnitY(d.d)
            set dis = DistanceLocXY( x, y, d.x, d.y )
            if dis > MissileSpeed /25. then
                set dis = MissileSpeed /25.
            endif
            call SetUnitX( d.d , GetPPX( x, dis, d.angle  ) )
            call SetUnitY( d.d , GetPPY( y, dis, d.angle  ) )
            call SetUnitZ( d.d, GetUnitFlyHeight(d.d) + d.vel )
            set d.tick = d.tick - 1
        endif
    endif

    return false
endfunction

function ScatterLightningSingle takes unit c, real cx, real cy, real x, real y, real dmg returns nothing
    local data d = data.create()
    local real dist = DistanceLocXY( cx, cy, x, y )
    set d.c = c
    set d.angle = AngleLocXY( cx, cy, x, y )
    set d.d =  CreateUnit(GetOwningPlayer(c), DummyId , cx, cy, d.angle )
    call SetUnitZ( d.d, 60. )
    set d.e = AddSpecialEffectTarget( MissileSFX , d.d, "origin" )
    set d.x = x
    set d.y = y
    set d.dmg = dmg
    set d.tick = R2I( dist /(MissileSpeed /25.))
    if d.tick * (MissileSpeed /25.) != dist then
        set d.tick = d.tick + 1
    endif
    set d.vel = LightnigHeight / d.tick
    call TT_Start( function ScatterLightningSingleE, d )
endfunction

//================================
private function Cond takes nothing returns boolean
    return (GetSpellAbilityId() == AbilID)
endfunction

private function Act takes nothing returns nothing
    local unit c = GetSpellAbilityUnit()
    local integer lvl = GetUnitAbilityLevel(c,AbilID)
    local location loc = GetSpellTargetLoc()
    local real x = GetLocationX(loc)
    local real y = GetLocationY(loc)
    local real cx = GetUnitX(c)
    local real cy = GetUnitY(c)
    local real dist
    local real ang
    local real tx
    local real ty
    local integer i = 1
    local integer im = GetBoltCount(lvl)
    local real dmg = GetDamage(lvl)
    loop
        exitwhen i > im
        set dist = GetRandomReal(50. , ScatterRadius - 50. )
        set ang = GetRandomReal( 0, 359. )
        set tx = GetPPX(x, dist, ang )
        set ty = GetPPY(y, dist, ang )
        call ScatterLightningSingle( c,cx,cy, tx, ty, dmg )
        set i = i + 1
    endloop
    
    call RemoveLocation(loc)
    set loc = null
    set c = null
endfunction
//=================================
public function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction( t, function Act )
    call TriggerAddCondition( t, Condition( function Cond ) )
    set t = null
endfunction

endscope

Aerial Strike
- Description:
Jumps off the group and throws explosive matter towards the target area, dealing damage to nearby enemy units.
(I need a more compatible name, can anyone suggest it ? :) )

- Screenshot: not available

- Code:
JASS:
scope AerialStrike initializer Init

globals
    private integer AbilID = 'A000'  //Raw Id of the Aerial Strike ability
    private integer DummyId = 'n000' //Raw Id of the dummy unit (requires dummy.mdx )
    private real JumpHeight = 400.0  //the jump height
    private string ThrowAnimation = "Spell Slam" //animation which is played when the hero throws explosive matter
    private string MissileSFX = &quot;Abilities\\Weapons\\BoatMissile\\BoatMissile.mdl&quot; //the missile art of the &quot;explosive matter&quot; <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin    :D" loading="lazy" data-shortname=":D" />
    private real MissileSpeed = 1000. //speed of the missile
    private string AreaSFX = &quot;&quot;   //special effect when the missile hits the ground,
                                   //used for some missile models which do not have &quot;explosive effect&quot; when it is destroyed
                                   //for example: the BoatMissile is the missile of Human Battle Ship, so it has &quot;explosive effect&quot; when it is destroyed (the Death animation)
                                   // but if the missile art is the missile of &quot;Death Coil&quot;, 
                                   //it doesn&#039;t have &quot;explosive effect&quot; when it is destroyed, so you should have an AreaSFX
    private string TargetSFX = &quot;Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl&quot; //dealing damage sfx
    private string TargetSFX_A = &quot;origin&quot; //attachment point of the dealing damage sfx
    private real ExplodeRadius = 200. //area of effect for explode
    private attacktype ATKTYPE = ATTACK_TYPE_NORMAL //attack type, currently: Spell 
    private damagetype DMGTYPE = DAMAGE_TYPE_FIRE   //damage type, currently: &quot;Magical&quot;
endglobals
//================================
private function GetDamage takes integer lvl returns real
    return 25. + 75*lvl //Get Damage Deal
endfunction
private function GetPPX takes real x, real dist, real angle returns real
    return x + dist * Cos(angle * bj_DEGTORAD)
endfunction

private function GetPPY takes real y, real dist, real angle returns real
    return y + dist * Sin(angle * bj_DEGTORAD)
endfunction

private function AngleLocXY takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
endfunction

private function DistanceLocXY takes real x1, real y1, real x2, real y2 returns real
    return SquareRoot( (x2-x1) * (x2-x1) + (y2-y1) * (y2-y1))
endfunction
//================================
private struct data
    unit c
    unit d
    effect dfx
    real tx
    real ty
    real angle
    real dmg
    integer tick = 20
    real sin
endstruct

private function ExplosiveMatterF takes nothing returns boolean
    local data d = TT_GetData()
    local unit f = GetFilterUnit()
    local boolean ok = false
    if IsUnitInRangeXY(f,d.tx,d.ty,ExplodeRadius) then
        if GetWidgetLife(f) &gt; 0.405 then
            if IsUnitEnemy(f,GetOwningPlayer(d.c)) then
                if not IsUnitType(f,UNIT_TYPE_STRUCTURE) and not IsUnitType(f,UNIT_TYPE_MAGIC_IMMUNE) then
                    set ok = true
                endif
            endif
        endif
    endif
    set f = null
    return ok
endfunction

private function ExplosiveMatterE takes nothing returns boolean
    local data d = TT_GetData()
    local group g 
    local unit p
    local boolexpr bx
    set d.tick = d.tick - 1
    if d.tick == 0 then
        call DestroyEffect(d.dfx)
        call KillUnit(d.d)
        
        if AreaSFX != &quot;&quot; then
            call DestroyEffect( AddSpecialEffect(AreaSFX,d.tx,d.ty) )
        endif
        
        set g = CreateGroup()
        set bx = Condition( function ExplosiveMatterF )
        call GroupEnumUnitsInRange( g, d.tx, d.ty, ExplodeRadius + 100. , bx )
        call DestroyBoolExpr(bx)
        set bx = null
        
        loop
            set p = FirstOfGroup(g)
            exitwhen p == null
            call GroupRemoveUnit(g,p)
            if TargetSFX != &quot;&quot; then
                call DestroyEffect( AddSpecialEffectTarget(TargetSFX,p,TargetSFX_A)  )
            endif
            call UnitDamageTarget(d.c,p,d.dmg, false, true, ATKTYPE, DMGTYPE, null )
        endloop
        call DestroyGroup(g)
        set g = null
        call d.destroy()
        return true
    endif
    
    call SetUnitFlyHeight(d.d, d.sin * d.tick  , 0. )
    call SetUnitX( d.d , GetPPX( GetUnitX(d.d), MissileSpeed / 25. , d.angle ) )
    call SetUnitY( d.d , GetPPY( GetUnitY(d.d), MissileSpeed / 25. , d.angle ) )
    
    return false
endfunction


private function ExplosiveMatter takes unit c, real x, real y, real dmg returns nothing
    local data d = data.create()
    local real cx = GetUnitX(c)
    local real cy = GetUnitY(c)
    local real dist = DistanceLocXY( cx, cy, x, y )
    set d.c = c
    set d.tx = x
    set d.ty = y
    set d.dmg = dmg
    set d.angle = AngleLocXY(cx,cy,x,y)
    set d.d = CreateUnit(GetOwningPlayer(c), DummyId , cx, cy, d.angle )
    call UnitAddAbility(d.d,&#039;Amrf&#039;)
    call UnitRemoveAbility(d.d,&#039;Amrf&#039;)
    call SetUnitFlyHeight(d.d, JumpHeight , 0. )
    call SetUnitAnimationByIndex(d.d, 90 - R2I( AtanBJ( JumpHeight / dist  ) ) )
    set d.dfx = AddSpecialEffectTarget( MissileSFX , d.d, &quot;origin&quot;)
    set d.tick = R2I( dist / MissileSpeed / 0.04 )
    set d.sin = JumpHeight / d.tick
    call TT_Start( function ExplosiveMatterE , d )
    
endfunction

private struct data2 
    unit c
    real tx
    real ty
    real angle
    real dmg
    
    real sin
    integer tick = 0
    boolean paused = false
endstruct

private function AerialStrikeE takes nothing returns boolean
    local data2 d = TT_GetData()
    local real x
    local real y
    if not d.paused then
        call IssueImmediateOrder(d.c,&quot;stop&quot;)
        call PauseUnit(d.c,true)
        call SetUnitInvulnerable(d.c,true)
        call SetUnitPathing(d.c,false)
        call SetUnitAnimation(d.c,&quot;Ready&quot;)
        set d.paused = true
    endif
    
    if d.tick == 25 then
        call PauseUnit(d.c,false)
        call SetUnitInvulnerable(d.c,false)
        call SetUnitPathing(d.c,true)
        call d.destroy()
        return true
    endif
    
    set d.tick = d.tick + 1
    
    call UnitAddAbility(d.c,&#039;Amrf&#039;)
    call UnitRemoveAbility(d.c,&#039;Amrf&#039;)
    call SetUnitFlyHeight(d.c, (JumpHeight-60.) * Sin( d.sin * d.tick  * bj_DEGTORAD )  , 0. )
    
    if d.tick == 9 then 
        call SetUnitTimeScale(d.c,1.5)
        call SetUnitAnimation(d.c,ThrowAnimation )
    endif
    
    if d.tick == 13 then
        set x = GetPPX( d.tx, 300. , d.angle - 90. )
        set y = GetPPY( d.ty, 300. , d.angle - 90. )
        call ExplosiveMatter( d.c, x, y , d.dmg)
        set x = GetPPX( d.tx, 300. , d.angle + 90. )
        set y = GetPPY( d.ty, 300. , d.angle + 90. )
        call ExplosiveMatter( d.c, x, y , d.dmg)
        set x = GetPPX( d.tx, 200. , d.angle - 45. )
        set y = GetPPY( d.ty, 200. , d.angle - 45. )
        call ExplosiveMatter( d.c, x, y , d.dmg)
        set x = GetPPX( d.tx, 200. , d.angle + 45. )
        set y = GetPPY( d.ty, 200. , d.angle + 45. )
        call ExplosiveMatter( d.c, x, y , d.dmg)
    endif

    if d.tick == 16 then 
        call SetUnitTimeScale(d.c,1.)
        call SetUnitAnimation(d.c,&quot;Ready&quot;)
    endif
    
    return false
endfunction
//================================
private function Cond takes nothing returns boolean
    return (GetSpellAbilityId() == AbilID)
endfunction

private function Act takes nothing returns nothing
    local data2 d = data2.create()
    local location l = GetSpellTargetLoc()
    local real cx
    local real cy
    set d.c = GetSpellAbilityUnit()
    set d.dmg = GetDamage(GetUnitAbilityLevel(d.c,AbilID))
    set d.sin = 180. / 25
    set d.tx = GetLocationX(l)
    set d.ty = GetLocationY(l)
    set cx = GetUnitX(d.c)
    set cy = GetUnitY(d.c)
    if cx == d.tx and cy == d.ty then
        set d.angle = GetUnitFacing(d.c)
    else
        set d.angle = AngleLocXY( cx, cy, d.tx, d.ty )
    endif
    call TT_Start( function AerialStrikeE, d )
    
    call RemoveLocation(l)
    set l = null
endfunction
//=================================
public function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction( t, function Act )
    call TriggerAddCondition( t, Condition( function Cond ) )
    set t = null
endfunction

endscope

Earth Boulder
- Description:
Throws a solid bounder towards a target location, the boulder knocks and deals damage to enemy units whom it collides with. The boulder also explodes at the end of its path.

- Screenshot: not available

- Code:
JASS:
scope EarthBoulder initializer Init

globals
    private integer AbilID = &#039;A002&#039;  //Raw Id of the Earth Boulder ability
    private integer DummyId = &#039;n000&#039; //Raw Id of the dummy unit (requires dummy.mdx )
    private string MissileSFX = &quot;abilities\\weapons\\catapult\\catapultmissile.mdl&quot; //the missile art of the boulder
    private real MissileSpeed = 500. //speed of the boulder
    private string AreaSFX = &quot;Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl&quot; //effect when the boulder moves
    //private string TargetSFX = &quot;Abilities\\Weapons\\Bolt\\BoltImpact.mdl&quot; //dealing damage sfx
    //private string TargetSFX_A = &quot;origin&quot; //attachment point of the dealing damage sfx
    private real MinimumRange = 300.0 //minimum throw range 
    private real KnockRadius = 100. //area of effect for scattering
    private real ExplodeRadius = 350. //area of effect for dealing damage when explode.
    private attacktype ATKTYPE = ATTACK_TYPE_NORMAL //attack type, currently: Spell 
    private damagetype DMGTYPE = DAMAGE_TYPE_NORMAL   //damage type, currently: &quot;Physical&quot;
    
    //----
    private real TEMPREAL = 0.0
endglobals
//================================
private function GetDamagePS takes integer lvl returns real
    return (100.+20*lvl) //Get Damage per second
endfunction
private function GetDamageEx takes integer lvl returns real
    return (75.+75*lvl) //Get Explode damage
endfunction
//================================
//---- Utility Functions -----
private function GetPPX takes real x, real dist, real angle returns real
    return x + dist * Cos(angle * bj_DEGTORAD)
endfunction

private function GetPPY takes real y, real dist, real angle returns real
    return y + dist * Sin(angle * bj_DEGTORAD)
endfunction

private function AngleLocXY takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
endfunction

private function DistanceLocXY takes real x1, real y1, real x2, real y2 returns real
    return SquareRoot( (x2-x1) * (x2-x1) + (y2-y1) * (y2-y1))
endfunction

private function SetUnitZ takes unit c, real z returns nothing
    call UnitAddAbility(c,&#039;Amrf&#039;)
    call UnitRemoveAbility(c,&#039;Amrf&#039;)
    call SetUnitFlyHeight(c,z,0.)
endfunction

private function DestroyTreesE takes nothing returns nothing
    local destructable d = GetEnumDestructable()
    if GetDestructableMaxLife(d) == 50. then
        call KillDestructable( d )
    endif
    set d = null
endfunction

private function DestroyTreesInCircle takes real x, real y, real rng returns nothing
    local rect r = Rect(x - rng, y - rng, x + rng, y + rng)
    set bj_enumDestructableCenter = Location(x,y)
    set bj_enumDestructableRadius = rng
    call EnumDestructablesInRect(r, null, function DestroyTreesE )
    call RemoveLocation(bj_enumDestructableCenter)
    set bj_enumDestructableCenter = null
    call RemoveRect(r)
    set r = null
endfunction

//====================================================================
private struct data
    unit c
    unit d
    effect e
    group g = CreateGroup()
    integer tick
    real angle
    
    real dmgEx
    real dmgPs
    real dx
    real dy
endstruct

function EarthBoulderFilter takes nothing returns boolean
    local data d = TT_GetData()
    local unit f = GetFilterUnit()
    local boolean ok = false
    if IsUnitInRangeXY(f,d.dx,d.dy,TEMPREAL ) then
        if GetWidgetLife(f) &gt; 0.405 then
            if IsUnitEnemy(f,GetOwningPlayer(d.c)) then
                if not IsUnitType(f,UNIT_TYPE_STRUCTURE) and not IsUnitType(f,UNIT_TYPE_MAGIC_IMMUNE) then
                    set ok = true
                endif
            endif
        endif
    endif
    set f = null
    return ok
endfunction

function EarthBoulderE takes nothing returns boolean
    local data d = TT_GetData()
    local unit p
    local real x
    local real y
    local real a
    local real dt
    local boolexpr bx
    set d.dx = GetUnitX(d.d)
    set d.dy = GetUnitY(d.d)
    if d.tick == 0 then
        call SetUnitScale(d.d, 2., 2., 2. )
        call SetUnitZ(d.d,0.)
        call DestroyEffect(d.e)
        call KillUnit(d.d)
        call DestroyTreesInCircle( d.dx, d.dy, 350. )
        set TEMPREAL = ExplodeRadius
        set bx = Condition( function EarthBoulderFilter )
        call GroupEnumUnitsInRange(d.g, d.dx,d.dy, ExplodeRadius + 100.,bx )
        call DestroyBoolExpr(bx)
        set bx = null
        loop
            set p = FirstOfGroup(d.g)
            exitwhen p == null
            call GroupRemoveUnit(d.g,p)
            call UnitDamageTarget(d.c, p , d.dmgEx , false, true, null, DAMAGE_TYPE_NORMAL, null )
        endloop
        call DestroyGroup(d.g)
        call d.destroy()
        return true
    endif
    set d.dx = GetPPX( d.dx, 20. , d.angle )
    set d.dy = GetPPY( d.dy, 20. , d.angle )
    call SetUnitPosition( d.d, d.dx, d.dy )
    if AreaSFX != &quot;&quot; then
        call DestroyEffect( AddSpecialEffect(AreaSFX,d.dx,d.dy) )
    endif
    
    call DestroyTreesInCircle( d.dx, d.dy, 200. )
    
    set TEMPREAL = KnockRadius
    set bx = Condition( function EarthBoulderFilter )
    call GroupEnumUnitsInRange(d.g, d.dx,d.dy, KnockRadius + 100.,bx )
    call DestroyBoolExpr(bx)
    set bx = null
        
    loop
        set p = FirstOfGroup(d.g)
        exitwhen p == null
        call GroupRemoveUnit(d.g,p)
        set x = GetUnitX(p)
        set y = GetUnitY(p)
        
        set dt = DistanceLocXY( d.dx, d.dy, x, y )
        set a = AngleLocXY( d.dx, d.dy, x, y )
        
        call SetUnitPosition( p ,  GetPPX( x, KnockRadius + 50. - dt, a ),  GetPPY( y, KnockRadius + 50. - dt, a ) )
        call UnitDamageTarget(d.c, p , d.dmgPs , false, true, ATKTYPE, DMGTYPE, null )
    endloop
    
    set d.tick = d.tick - 1
    return false
    
endfunction
//================================
private function Cond takes nothing returns boolean
    return (GetSpellAbilityId() == AbilID)
endfunction

private function Act takes nothing returns nothing
    local unit c = GetSpellAbilityUnit()
    local integer lvl = GetUnitAbilityLevel(c,AbilID)
    local location loc = GetSpellTargetLoc()
    local real x = GetLocationX(loc)
    local real y = GetLocationY(loc)
    local real cx = GetUnitX(c)
    local real cy = GetUnitY(c)
    local data d = data.create()
    local real dist = DistanceLocXY( cx, cy, x, y )
    if dist &lt; MinimumRange then
        set dist = MinimumRange
    endif
    
    set d.c = c
    set d.dmgEx = GetDamageEx(lvl)
    set d.dmgPs = GetDamagePS(lvl) / 25.
    set d.tick = R2I( dist / 20. )
    set d.angle = AngleLocXY( cx,cy,x,y)
    set d.d =  CreateUnit(GetOwningPlayer(c), DummyId , cx, cy, d.angle )
    set d.e = AddSpecialEffectTarget( MissileSFX , d.d, &quot;origin&quot; )
    call SetUnitScale(d.d,4.,4.,4.)
    call SetUnitZ(d.d,80.)
    call TT_Start( function EarthBoulderE, d )
    
endfunction
//=================================
public function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction( t, function Act )
    call TriggerAddCondition( t, Condition( function Cond ) )
    set t = null
endfunction

endscope

Thunder Break
- Description:
Calls down powerful lightning bolts from the sky, dealing damage to enemy units in a line.

- Screenshot:
ThunderBreak.jpg

- Code:
JASS:
scope ThunderBreak initializer Init 

globals
    private integer AbilID = &#039;A003&#039;  //Raw Id of the Thunder Break ability
    private string LightningCode = &quot;CHIM&quot; //lightning code of the lightning effect
    private string AreaSFX = &quot;Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl&quot; //effect at the &quot;current&quot; area ( I don&#039;t know how to explain this :|)
    private string AreaSoundSFX = &quot;Abilities\\Spells\\Orc\\LightningBolt\\LightningBoltMissile.mdl&quot; //I only use this effect for its sound <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" /> 
    private string LightningHitSFX = &quot;Abilities\\Spells\\Other\\ForkedLightning\\ForkedLightningTarget.mdl&quot; //effect where the lightning bolt hits the ground
    private string TargetSFX = &quot;Abilities\\Weapons\\Bolt\\BoltImpact.mdl&quot; //dealing damage sfx
    private string TargetSFX_A = &quot;origin&quot; //attachment point of the dealing damage sfx
    private real MaximumRange = 1400.0 //how long is this line of lightning
    private real DamageRadius = 250. //area of effect for dealing damage.
    private attacktype ATKTYPE = ATTACK_TYPE_NORMAL //attack type, currently: Spell 
    private damagetype DMGTYPE = DAMAGE_TYPE_LIGHTNING   //damage type, currently: &quot;Magical&quot;
    //----
    private real TEMPREAL = 0.0
endglobals
//================================
private function GetDamage takes integer lvl returns real
    return (150.*lvl) //Get Damage dealt
endfunction
private function GetDistance takes integer lvl returns real
    return 1500. //the Distance 
endfunction
//================================
//---- Utility Functions -----
private function GetPPX takes real x, real dist, real angle returns real
    return x + dist * Cos(angle * bj_DEGTORAD)
endfunction

private function GetPPY takes real y, real dist, real angle returns real
    return y + dist * Sin(angle * bj_DEGTORAD)
endfunction
//====================================================================
private struct data
    unit c
    real cx
    real cy
    real cf
    real dmg
    
    group hit = CreateGroup()
    integer tick
    integer tickdmg = 0
endstruct

private struct sdata
    real color = 1.
    lightning array b [5]
endstruct

private function ThunderBreakD takes nothing returns boolean
    local sdata d = TT_GetData()
    local integer i = 0
    set d.color = d.color - 0.1
    loop
        exitwhen i &gt; 4
        if d.color &lt;= 0 then
            call DestroyLightning( d.b<i> )
        else
            call SetLightningColor( d.b<i>, 1, 1, 1 , d.color )
        endif
        set i = i + 1
    endloop
    
    if d.color &lt;= 0 then
        call d.destroy()
        return true
    endif
    return false
endfunction

function ThunderBreakFilter takes nothing returns boolean
    local data d = TT_GetData()
    local unit f = GetFilterUnit()
    local boolean ok = false
    if IsUnitInRangeXY(f,d.cx,d.cy, DamageRadius ) then
        if GetWidgetLife(f) &gt; 0.405 then
            if IsUnitEnemy(f,GetOwningPlayer(d.c)) then
                if not IsUnitType(f,UNIT_TYPE_STRUCTURE) and not IsUnitType(f,UNIT_TYPE_MAGIC_IMMUNE) then
                    if not IsUnitInGroup(f,d.hit) then
                        set ok = true
                    endif
                endif
            endif
        endif
    endif
    set f = null
    return ok
endfunction

function ThunderBreakE takes nothing returns boolean
    local data d = TT_GetData()
    local sdata sd
    local real cx
    local real cy
    local unit p
    local group g
    local boolexpr bx
    if d.tick == 0 then
        call DestroyGroup(d.hit)
        call d.destroy()
        return true
    endif
    
    if d.tickdmg == 0 then
    //---
    if AreaSFX != &quot;&quot; then
        call DestroyEffect( AddSpecialEffect(AreaSFX,d.cx,d.cy) )
    endif
    if AreaSoundSFX != &quot;&quot; then
        call DestroyEffect( AddSpecialEffect(AreaSoundSFX,d.cx,d.cy) )
    endif
    
    set sd = sdata.create()
    set sd.b[0] = AddLightningEx( LightningCode ,true,d.cx,d.cy,2000.,d.cx,d.cy,-0)
    //call DestroyEffect( AddSpecialEffect(&quot;Abilities\\Spells\\Other\\ForkedLightning\\ForkedLightningTarget.mdl&quot;,d.cx,d.cy) )
    set cx = GetPPX( d.cx , 150., d.cf - 45 )
    set cy = GetPPY( d.cy , 150., d.cf - 45 )
    set sd.b[1] = AddLightningEx( LightningCode,true,cx,cy,2000. ,cx,cy,0)
    call DestroyEffect( AddSpecialEffect(LightningHitSFX,cx,cy) )
    set cx = GetPPX( d.cx , 150., d.cf + 45 )
    set cy = GetPPY( d.cy , 150., d.cf + 45 )
    set sd.b[2] = AddLightningEx( LightningCode,true,cx,cy,2000. ,cx,cy,0)
    call DestroyEffect( AddSpecialEffect(LightningHitSFX,cx,cy) )
    set cx = GetPPX( d.cx , 150., d.cf + 135 )
    set cy = GetPPY( d.cy , 150., d.cf + 135 )
    set sd.b[3] = AddLightningEx( LightningCode,true,cx,cy,2000. ,cx,cy,0)
    call DestroyEffect( AddSpecialEffect(LightningHitSFX,cx,cy) )
    set cx = GetPPX( d.cx , 150., d.cf - 135 )
    set cy = GetPPY( d.cy , 150., d.cf - 135 )
    set sd.b[4] = AddLightningEx( LightningCode,true,cx,cy,2000. ,cx,cy,0)
    call DestroyEffect( AddSpecialEffect(LightningHitSFX,cx,cy) )
    
    call TT_Start( function ThunderBreakD , sd )
    set g = CreateGroup()
    set bx = Condition( function ThunderBreakFilter )
    call GroupEnumUnitsInRange(g,d.cx,d.cy, DamageRadius + 100., bx )
    call DestroyBoolExpr(bx)
    set bx = null
    loop
        set p = FirstOfGroup(g)
        exitwhen p == null
        call GroupRemoveUnit(g,p)
        if TargetSFX != &quot;&quot; then
            call DestroyEffect( AddSpecialEffectTarget(TargetSFX,p,TargetSFX_A)  )
        endif
        call UnitDamageTarget(d.c, p , d.dmg , false, true, ATKTYPE, DMGTYPE, null )
        call GroupAddUnit(d.hit,p)
    endloop
    call DestroyGroup(g)
    set g = null
    
    set d.cx = GetPPX( d.cx, 300., d.cf )
    set d.cy = GetPPY( d.cy, 300., d.cf )
    
    set d.tickdmg = 3
    //---
    endif
    set d.tick = d.tick - 1
    set d.tickdmg = d.tickdmg - 1
    return false
endfunction

//=============================================
private function Cond takes nothing returns boolean
    return (GetSpellAbilityId() == AbilID)
endfunction

private function Act takes nothing returns nothing
    local unit c = GetSpellAbilityUnit()
    local integer lvl = GetUnitAbilityLevel(c,AbilID)
    local data d = data.create()
    set d.c = c
    set d.cf = GetUnitFacing(c)
    set d.cx = GetPPX( GetUnitX(c), 200., d.cf )
    set d.cy = GetPPY( GetUnitY(c), 200., d.cf )
    
    set d.dmg = GetDamage( lvl )
    set d.tick = R2I( (GetDistance(lvl) - 200. ) / 300. ) * 3
    call TT_Start( function ThunderBreakE, d )
    
endfunction
//=================================
public function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction( t, function Act )
    call TriggerAddCondition( t, Condition( function Cond ) )
    set t = null
endfunction

endscope</i></i>


Credit:
- Thanks Cohadar for TT system
- Thanks Tinki3 for the test map template
- and does the "dummy.mdx" belong to Vexorian ? :confused:

If someone find any leaks or bugs, please tell me, I will apperciate very much :)
 

Attachments

  • [Spellpack] Random Spells.w3x
    79.9 KB · Views: 257
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top