- Reaction score
- 91
Static Link
GUI/JASS/vJASS? vJASS
MUI? Yes
Lagless? Yes
Requires?
Jass NewGen Pack
TimerUtils (any flavour)
TimedEffects
ChanceToProc
Warcraft 1.23b+
Actually, if you want this for 1.23, just use the TimerUtils 1.23 version since it depends on that.
Description:
Links the Hero and a targeted unit. Using the element of lightning, the Hero will create a storm that will periodically blast the foe with static electricity, slowing it briefly and dealing minor damage. If the linked units go too far away from each other or one of them dies the chain breaks.
Level 1 - 30 damage per hit, lasts 5 seconds.
Level 2 - 40 damage per hit, lasts 6 seconds.
Level 3 - 50 damage per hit, lasts 7 seconds.
Level 4 - 60 damage per hit, lasts 8 seconds.
Code:
JASS:
scope StaticLink initializer Init // uses TimerUtils, TimedEffects, ChanceToProc
//==================================================================================
// C O N F I G U R A T I O N M E N U
// ==================================================================================
globals
// Raw code of the ability Static Link
private constant integer AID_RAW = 039;A000039;
// Raw code of the ability Static Link Slow (the one that slows enemies).
private constant integer AID_SLOW = 039;A001039;
// Order string for casting the ability Static Link Slow (AID_SLOW).
private constant string OID_SLOW = "slow"
// Raw code of a generic dummy unit.
private constant integer UID_DUMMY = 039;u001039;
// Raw code of the unit attached to the caster (currently the unit with Unholy Frenzy model).
private constant integer UID_RAW = 039;e000039;
// Raw code of the unit attached to the target (currently the unit with the lightning ball model).
private constant integer UID_TARG_RAW = 039;e001039;
// Special effect that is going to randomly be created on the target when it gets hit by a lightning bolt
private constant string SFX_ONE = "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl"
// Attachment point of the above effect (SFX_ONE).
private constant string AP_ONE = "origin"
// Duration of the special effect. This is used because some effects die instantly without showing
// if I use DestroyEffect(). If your effect has a death animation then you can set this to 0.
private constant real EFFECT_ONE_DURATION = 1.15
// Special effect that is going to be created on the target when it gets hit by a lightning bolt.
// Unlike SFX_ONE, this effect is created ALWAYS when the lightning hits, not randomly.
private constant string SFX_TWO = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
// Attachment point of the above effect (SFX_TWO).
private constant string AP_TWO = "origin"
// Duration of the special effect. This is used because some effects die instantly without showing
// if I use DestroyEffect(). If your effect has a death animation then you can set this to 0.
private constant real EFFECT_TWO_DURATION = 0.65
// Duration of the secondary lightning (the one that is periodically created to hit the target).
private constant real SECONDARY_LIGHTNING_DURATION = 0.45
// Sound string which is used for creating a sound from label (CreateSoundFromLabel).
private constant string SOUND_FROM_LABEL = "LightningBolt"
// Lightning type of the first ("permanent") lightning between the caster and target.
private constant string LIGHTNING_ONE = "CLSB"
// Lightning type of the secondary lightning that periodically hits the target.
private constant string LIGHTNING_TWO = "FORK"
// Attack type of the damaging.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
// Damage type of the damaging.
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// Weapon type of the damaging.
private constant weapontype WEAPON_TYPE = null
endglobals
globals
// Period for moving the lightnings, units and for damaging. Keep it in range 0.02 - 0.05!
private constant real PERIOD = 0.03125
// Values used for ChanceToProc struct.
// Percentage chance to create a secondary lightning.
// Note that the chance is calculated each PERIOD tick!
private constant real CHANCE_TO_PROC = 0.03 // currently 3%
// Weight of the chance (see ChanceToProc's documentation if you don't understand).
private constant real WEIGHT = 0.21
endglobals
// Duration of the link.
private function DURATION takes unit cast, integer lvl returns real
return 5. + (lvl * 1)
endfunction
// Maximum distance between the two linked units. Passing it will break the link
private function MAX_DISTANCE takes unit cast, integer lvl returns real
return 600.
endfunction
// Damage dealt every time a secondary lightning strikes the target.
private function DAMAGE takes unit cast, integer lvl returns real
return 20. + (lvl * 10)
endfunction
//==================================================================================
// E N D O F C O N F I G U R A T I O N M E N U
//==================================================================================
globals
private location loc = Location(0, 0)
private unit dum = null
private sound zap = null
endglobals
private function GetUnitZ takes unit u returns real
call MoveLocation(loc, GetUnitX(u), GetUnitY(u))
return GetLocationZ(loc) + GetUnitFlyHeight(u)
endfunction
private struct Lightning
unit cast
unit targ
lightning light
timer tim
integer ticks
method onDestroy takes nothing returns nothing
call DestroyLightning(this.light)
call ReleaseTimer(this.tim)
endmethod
private static method Callback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call MoveLightningEx(this.light, true, GetUnitX(this.cast), GetUnitY(this.cast), GetUnitZ(this.cast), GetUnitX(this.targ), GetUnitY(this.targ), GetUnitZ(this.targ))
set this.ticks = this.ticks - 1
if this.ticks <= 0 then
call this.destroy()
endif
endmethod
static method create takes unit cast, unit targ returns thistype
local thistype this = thistype.allocate()
set this.cast = cast
set this.targ = targ
set this.light = AddLightningEx(LIGHTNING_TWO, true, GetUnitX(this.cast), GetUnitY(this.cast), GetUnitZ(this.cast), GetUnitX(this.targ), GetUnitY(this.targ), GetUnitZ(this.targ))
set this.ticks = R2I(SECONDARY_LIGHTNING_DURATION / PERIOD)
set this.tim = NewTimer()
call SetTimerData(this.tim, this)
call TimerStart(this.tim, PERIOD, true, function thistype.Callback)
return this
endmethod
endstruct
private struct Data
unit cast
unit targ
unit sfx
unit targSfx
lightning light
integer ticks
integer lvl
timer tim
Chance ch
method onDestroy takes nothing returns nothing
call ReleaseTimer(this.tim)
call KillUnit(this.sfx)
call KillUnit(this.targSfx)
call DestroyLightning(this.light)
call this.ch.destroy()
endmethod
private static method Callback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real x = GetUnitX(this.cast)
local real y = GetUnitY(this.cast)
local real tx = GetUnitX(this.targ)
local real ty = GetUnitY(this.targ)
local real dx = tx - x
local real dy = ty - y
local real distance = SquareRoot(dx * dx + dy * dy)
local integer id
call SetUnitX(this.sfx, x)
call SetUnitY(this.sfx, y)
call SetUnitX(this.targSfx, tx)
call SetUnitY(this.targSfx, ty)
call MoveLightningEx(this.light, true, x, y, GetUnitZ(this.cast), tx, ty, GetUnitZ(this.targSfx))
if this.ch.isProc() then
call Lightning.create(this.sfx, this.targSfx)
set zap = CreateSoundFromLabel(SOUND_FROM_LABEL, false, true, true, 0, 0)
call AttachSoundToUnit(zap, this.targ)
call SetSoundVolume(zap, 100)
call StartSound(zap)
call KillSoundWhenDone(zap)
if GetRandomInt(0, 1) == 0 then // Some randomization, in case it becomes too much...
call StartTimedEffect(AddSpecialEffectTarget(SFX_ONE, this.targ, AP_ONE), EFFECT_ONE_DURATION)
endif
call StartTimedEffect(AddSpecialEffectTarget(SFX_TWO, this.targ, AP_TWO), EFFECT_TWO_DURATION)
call SetUnitX(dum, tx)
call SetUnitY(dum, ty)
call SetUnitOwner(dum, GetOwningPlayer(this.cast), true)
call SetUnitAbilityLevel(dum, AID_SLOW, this.lvl)
call IssueTargetOrder(dum, OID_SLOW, this.targ)
call UnitDamageTarget(this.cast, this.targ, DAMAGE(this.cast, this.lvl), true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
endif
set this.ticks = this.ticks - 1
if distance >= MAX_DISTANCE(this.cast, this.lvl) or this.ticks <= 0 or IsUnitType(this.targ, UNIT_TYPE_DEAD) == true or IsUnitType(this.cast, UNIT_TYPE_DEAD) == true or IsUnitInvisible(this.targ, GetOwningPlayer(this.cast)) then
call this.destroy()
endif
endmethod
static method create takes unit cast, unit targ returns thistype
local thistype this = thistype.allocate()
local real x = GetUnitX(cast)
local real y = GetUnitY(cast)
local real tx = GetUnitX(targ)
local real ty = GetUnitY(targ)
set this.cast = cast
set this.targ = targ
set this.lvl = GetUnitAbilityLevel(this.cast, AID_RAW)
set this.ch = Chance.create(CHANCE_TO_PROC, WEIGHT)
set this.sfx = CreateUnit(GetOwningPlayer(this.cast), UID_RAW, x, y, GetUnitFacing(this.cast))
set this.targSfx = CreateUnit(GetOwningPlayer(this.cast), UID_TARG_RAW, tx, ty, GetUnitFacing(this.targ))
set this.light = AddLightningEx(LIGHTNING_ONE, true, x, y, GetUnitZ(this.cast), tx, ty, GetUnitZ(this.targSfx))
set this.ticks = R2I(DURATION(this.cast, this.lvl) / PERIOD)
set this.tim = NewTimer()
call SetTimerData(this.tim, this)
call TimerStart(this.tim, PERIOD, true, function thistype.Callback)
return this
endmethod
endstruct
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == AID_RAW
endfunction
private function Actions takes nothing returns nothing
call Data.create(GetTriggerUnit(), GetSpellTargetUnit())
endfunction
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(trig, Condition(function Conditions))
call TriggerAddAction(trig, function Actions)
set dum = CreateUnit(Player(13), UID_DUMMY, 0., 0., 0.)
call UnitAddAbility(dum, 039;Aloc039;)
call UnitAddAbility(dum, AID_SLOW)
endfunction
endscope
http://i39.tinypic.com/1043ols.jpg
ReadMe included in the test map. The reason I used ChanceToProc is that I get really annoyed when multiple lightnings stack over each other when using the generic "is RandomNumBetween1and100 < X ?" and this is the way to limit that, besides I haven't really found any spell/script that uses it so far.
Special thanks to Tinki3 for the testmap.