- Reaction score
- 662
Name may need some improving ^_^
Technical details:
Made in vJASS
Fully MUI
Requires NewGen WE and Strilanc's Dedicated Unit Indexer
Leak-free (to my knowledge)
Lag-free (for me)
Importing instructions located in the map
Lightning Cannon
The hero begins charging a large ball of electricity. When the hero stops, the ball will travel towards the target point, unleashing bolts of lightning at random, and damage units in its path.
Screenshots:
Testing the spell for itself is essential, the lightning bolts are nowhere near as awesome with still images
Code:
Credits:
If you use this spell, please credit me for making it
Update log:
v1 - Initial release
v2 - Fixed a bug where same unit couldn't repeatedly cast
v3 - Reduced the channelling time to 5 seconds, did a bit of tweaking to the lightning config
v4 - Modified the lightning SFX generation a little, first SFX spawns at lightning creation, rather than LIGHT_EFFECT_FREQ seconds afterwards
v5 - Lightning now capable of moving, added some new constants to control it
v6 - Made the orb damage a bit more sensible (now deals up to X damage, based on current power as a % of max power)
Technical details:
Made in vJASS
Fully MUI
Requires NewGen WE and Strilanc's Dedicated Unit Indexer
Leak-free (to my knowledge)
Lag-free (for me)
Importing instructions located in the map
Lightning Cannon
The hero begins charging a large ball of electricity. When the hero stops, the ball will travel towards the target point, unleashing bolts of lightning at random, and damage units in its path.
Screenshots:


Code:
JASS:
//Lightning Cannon
//By Flare
//Requires: NewGen WE
// Strilanc's Dedicated Unit Indexer
// Lightning Cannon [dummy] (h000)
// Lightning Cannon [abil] (A000)
//NOTE: DO NOT APPLY A CUSTOM VALUE TO THE DUMMY UNIT, IT'S ALREADY IN USE
library LightningCannon initializer SpellInit requires DUI
//Configurables
globals
private constant real TIMER_INT = 0.03125
private constant integer SID = 'A000'
private constant integer DID = 'h000'
//Fly height of the dummy
private constant real D_HEIGHT = 150
//Scaling size of the dummy
private constant real BASE_SCALE = 0.75
//Scaling modifier, based on current power as a % of max power
private constant real SCALE_MOD = 1.25
//Special effect created when a unit touches orb
private constant string DAMAGE_EFFECT = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
//Area of effect for orb damage
private constant real ORB_RADIUS = 125
//Determines whether lightning effects are spawned
private constant boolean CREATE_LIGHT = true
//Lightning ID
private constant string LIGHT_ID = "CLPB"
//Determines if the lightning effects move
//Direction of travel is the angle between the orb and lightning target AT LIGHTNING CREATION
private constant boolean MOVE_LIGHT = true
//The speed (per sec) travelled by the lightning effects (assuming MOVE_LIGHT is true)
private constant real LIGHT_SPEED = 400
//Frequency of lightning effects
private constant real LIGHT_FREQ = 0.2
//Randomization of lightning target position
private constant real LIGHT_OFFSET_LOW = 100
private constant real LIGHT_OFFSET_HIGH = 300
//Start/end values for lightning alpha
private constant real START_ALPHA = 1
private constant real END_ALPHA = 0
//RGB values for the lightning
private constant real R = 1
private constant real G = 1
private constant real B = 1
//Duration of the lightning
private constant real LIGHT_DUR = 0.3
//Frequency of special effects generated at lightning end
//Also determines how often the lightning deals damage
private constant real LIGHT_EFFECT_FREQ = 0.0625
//Area of effect for random lightning damage
private constant real LIGHT_DMG_RADIUS = 90
//Effect generated at lightning end
private constant string LIGHT_SFX = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
//Projectile speed
private constant real SPEED = 800
//Attack, damage, weapon types
private constant attacktype AT = ATTACK_TYPE_MAGIC
private constant damagetype DT = DAMAGE_TYPE_NORMAL
private constant weapontype WT = WEAPON_TYPE_WHOKNOWS
endglobals
//Proportion of power that is converted to damage dealt
private constant function Damage takes integer level returns real
return 50. + level * 50.
endfunction
//Default amount of power stored by the orb
private constant function MinPower takes integer level returns real
return 100. + level * 50.
endfunction
//Maximum energy storable by the orb
private constant function MaxPower takes integer level returns real
return 300. + level * 150.
endfunction
//Rate of power gain
private constant function StoreRate takes integer level returns real
return 25. + level * 25.
endfunction
//Amount of damage dealt by random lightning bolts
private constant function LightningDamage takes integer level returns real
return 3. + level * 2.
endfunction
//End of configurables
//Don't modify anything below this point
private keyword LData
private keyword SData
globals
private timer ST = CreateTimer ()
private integer SN = 0
private SData array SDC
private SData array SDD
private timer LT = CreateTimer ()
private integer LN = 0
private LData array LD
private group Enum = CreateGroup ()
private group Casters = CreateGroup ()
private group Movers = CreateGroup ()
private LData lz
private SData sz
private boolexpr DamageFunc
private constant real UNITS_PER_INT = SPEED * TIMER_INT
private location Loc = Location (0, 0)
endglobals
private struct LData
lightning l
unit u
real tx
real ty
real tz
real dur
real alpha
real fadeamt
real fxticker
real lightdmg
real coffset
real soffset
endstruct
private struct SData
unit caster
unit orb
real curpower
real maxpower
real cosoffset
real sinoffset
real dist
real lticker
group affected
boolean moving
real dmg
real storerate
method onDestroy takes nothing returns nothing
call GroupClear (.affected)
call KillUnit (.orb)
endmethod
endstruct
private function GetCoordZ takes real x, real y returns real
call MoveLocation (Loc, x, y)
return GetLocationZ (Loc)
endfunction
private function LightDamage takes nothing returns boolean
local unit f = GetFilterUnit ()
if IsUnitEnemy (f, GetOwningPlayer (lz.u)) and IsUnitType (f, UNIT_TYPE_DEAD) == false then
call UnitDamageTarget (lz.u, f, LightningDamage (GetUnitUserData (lz.u)), false, true, AT, DT, WT)
endif
set f = null
return false
endfunction
private function LightCallback takes nothing returns nothing
local integer i = LN
local real ux
local real uy
local LData a
loop
exitwhen i == 0
set a = LD<i>
set ux = GetUnitX (a.u)
set uy = GetUnitY (a.u)
set a.alpha = a.alpha - a.fadeamt
set a.dur = a.dur - TIMER_INT
set a.tx = a.tx + a.coffset
set a.ty = a.ty + a.soffset
set a.fxticker = a.fxticker + TIMER_INT
if a.fxticker >= LIGHT_EFFECT_FREQ then
call DestroyEffect (AddSpecialEffect (LIGHT_SFX, a.tx, a.ty))
set lz = a
call GroupEnumUnitsInRange (Enum, a.tx, a.ty, LIGHT_DMG_RADIUS, Condition (function LightDamage))
endif
if a.alpha > 1 then
set a.alpha = 1
elseif a.alpha < 0 then
set a.alpha = 0
endif
call SetLightningColor (a.l, R, G, B, a.alpha)
call MoveLightningEx (a.l, true, ux, uy, GetUnitFlyHeight (a.u), a.tx, a.ty, a.tz)
if a.dur <= 0 then
call DestroyLightning (a.l)
call a.destroy ()
set LD<i> = LD[LN]
set LN = LN - 1
if LN == 0 then
call PauseTimer (LT)
endif
endif
set i = i - 1
endloop
endfunction
private function AddTimedLightning takes string id, real duration, unit u returns nothing
local LData a = LData.create ()
local real r1 = GetRandomReal (0, bj_PI * 2)
local real r2 = GetRandomReal (LIGHT_OFFSET_LOW, LIGHT_OFFSET_HIGH)
local real c = Cos (r1)
local real s = Sin (r1)
local real x1 = GetUnitX (u)
local real y1 = GetUnitY (u)
local real z1 = GetUnitFlyHeight (u)
local real x2 = x1 + c * r2
local real y2 = y1 + s * r2
local real z2 = GetCoordZ (x2, y2)
set a.tx = x2
set a.ty = y2
set a.tz = z2
set a.fxticker = 0
if MOVE_LIGHT then
set a.coffset = c * LIGHT_SPEED * TIMER_INT
set a.soffset = s * LIGHT_SPEED * TIMER_INT
else
set a.coffset = 0
set a.soffset = 0
endif
set a.u = u
set a.l = AddLightningEx (id, true, x1, y1, z1, x2, y2, z2)
set a.alpha = START_ALPHA
set a.dur = duration
set a.fadeamt = (TIMER_INT/duration) * (START_ALPHA - END_ALPHA)
call DestroyEffect (AddSpecialEffect (LIGHT_SFX, a.tx, a.ty))
set lz = a
call GroupEnumUnitsInRange (Enum, a.tx, a.ty, LIGHT_DMG_RADIUS, Condition (function LightDamage))
if LN == 0 then
call TimerStart (LT, TIMER_INT, true, function LightCallback)
endif
set LN = LN + 1
set LD[LN] = a
endfunction
private function OrbDamage takes nothing returns boolean
local unit u = GetFilterUnit ()
if IsUnitEnemy (u, GetOwningPlayer (sz.caster)) and IsUnitType (u, UNIT_TYPE_DEAD) == false and IsUnitInGroup (u, sz.affected) == false then
call UnitDamageTarget (sz.caster, u, (sz.curpower/sz.maxpower) * sz.dmg, false, true, AT, DT, WT)
call DestroyEffect (AddSpecialEffect (DAMAGE_EFFECT, GetUnitX (u), GetUnitY (u)))
call GroupAddUnit (sz.affected, u)
endif
set u = null
return false
endfunction
private function MoveFunc takes nothing returns nothing
local SData a = SDD[DUI_GetUnitIndex (GetEnumUnit ())]
local real x = GetUnitX (a.orb)
local real y = GetUnitY (a.orb)
set x = x + a.cosoffset * UNITS_PER_INT
set y = y + a.sinoffset * UNITS_PER_INT
call SetUnitPosition (a.orb, x, y)
set a.dist = a.dist - UNITS_PER_INT
set sz = a
call GroupEnumUnitsInRange (Enum, x, y, ORB_RADIUS, DamageFunc)
set a.lticker = a.lticker + TIMER_INT
if a.lticker >= LIGHT_FREQ and CREATE_LIGHT then
set a.lticker = a.lticker - LIGHT_FREQ
call AddTimedLightning (LIGHT_ID, LIGHT_DUR, a.orb)
endif
if a.dist <= 0 then
call GroupRemoveUnit (Movers, a.orb)
call a.destroy ()
set SN = SN - 1
if SN == 0 then
call PauseTimer (ST)
endif
endif
endfunction
private function CasterFunc takes nothing returns nothing
local SData a = SDC[DUI_GetUnitIndex (GetEnumUnit ())]
local real x = GetUnitX (a.orb)
local real y = GetUnitY (a.orb)
local real s
set a.lticker = a.lticker + TIMER_INT
if a.lticker >= LIGHT_FREQ and CREATE_LIGHT then
set a.lticker = a.lticker - LIGHT_FREQ
call AddTimedLightning (LIGHT_ID, LIGHT_DUR, a.orb)
endif
set a.curpower = a.curpower + a.storerate
if a.curpower > a.maxpower then
set a.curpower = a.maxpower
endif
set s = BASE_SCALE + SCALE_MOD * (a.curpower/a.maxpower)
call SetUnitScale (a.orb, s, s, s)
endfunction
private function SpellCallback takes nothing returns nothing
call ForGroup (Casters, function CasterFunc)
call ForGroup (Movers, function MoveFunc)
endfunction
private function SpellCond takes nothing returns boolean
return GetSpellAbilityId () == SID
endfunction
private function SpellActions takes nothing returns nothing
local SData a
local unit c = GetTriggerUnit ()
local location l = GetSpellTargetLoc ()
local real cx = GetUnitX (c)
local real cy = GetUnitY (c)
local real tx = GetLocationX (l)
local real ty = GetLocationY (l)
local real dx = tx - cx
local real dy = ty - cy
local real angle = Atan2 (dy, dx)
local integer lvl = GetUnitAbilityLevel (c, SID)
if GetTriggerEventId () == EVENT_PLAYER_UNIT_SPELL_EFFECT then
set a = SData.create ()
set a.caster = c
set a.orb = CreateUnit (GetOwningPlayer (c), DID, cx, cy, angle * bj_DEGTORAD)
call GroupAddUnit (Casters, c)
set SDC[DUI_GetUnitIndex(c)] = a
call SetUnitUserData (a.orb, lvl)
call SetUnitPathing (a.orb, false)
call SetUnitFlyHeight (a.orb, D_HEIGHT, 0)
set a.dist = SquareRoot (dx * dx + dy * dy)
set a.lticker = 0
set a.curpower = MinPower (lvl)
set a.maxpower = MaxPower (lvl)
set a.storerate = StoreRate (lvl)
set a.moving = false
set a.cosoffset = Cos (angle)
set a.sinoffset = Sin (angle)
set a.dmg = Damage (lvl)
if a.affected == null then
set a.affected = CreateGroup ()
endif
if SN == 0 then
call TimerStart (ST, TIMER_INT, true, function SpellCallback)
endif
set SN = SN + 1
elseif GetTriggerEventId () == EVENT_PLAYER_UNIT_SPELL_ENDCAST then
set a = SDC[DUI_GetUnitIndex (c)]
set SDD[DUI_GetUnitIndex (a.orb)] = a
call GroupRemoveUnit (Casters, c)
call GroupAddUnit (Movers, a.orb)
endif
call RemoveLocation (l)
set l = null
set c = null
endfunction
private function SpellInit takes nothing returns nothing
local trigger t = CreateTrigger ()
call TriggerRegisterAnyUnitEventBJ (t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerRegisterAnyUnitEventBJ (t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition (t, Condition (function SpellCond))
call TriggerAddAction (t, function SpellActions)
set DamageFunc = Condition (function OrbDamage)
endfunction
endlibrary</i></i>
Credits:
If you use this spell, please credit me for making it
Update log:
v1 - Initial release
v2 - Fixed a bug where same unit couldn't repeatedly cast
v3 - Reduced the channelling time to 5 seconds, did a bit of tweaking to the lightning config
v4 - Modified the lightning SFX generation a little, first SFX spawns at lightning creation, rather than LIGHT_EFFECT_FREQ seconds afterwards
v5 - Lightning now capable of moving, added some new constants to control it
v6 - Made the orb damage a bit more sensible (now deals up to X damage, based on current power as a % of max power)
Attachments
-
32.4 KB Views: 507