Moderator
M
Moderator
Lee Sin Spellpack v1.1.0 | Reviewed by Maker | 30th Jun 2013 | ||
APPROVED | ||
|
|
//* Library used purely for Lee Sin spellpack. Contains any data
//* would be used by multiple Lee Sin abilities.
library leeSinConsts initializer i requires DamageType
globals
//< BEGIN CUSTOMIZE SECTION
//* Define whether the active map is a "Test Map". The only
//* functionality this changes is whether or not to allow
//* sonic wave to strike wards.
public constant boolean IS_TEST_MAP=true
//* Unit ID of the dummy caster unit
public constant integer CASTER_ID='h002'
//* Unit ID of Lee Sin the Blind Monk
public constant integer LEE_SIN_ID='E000'
///* Ability ID of the Dummy "Bonus Attack Speed" ability
public constant integer BONUS_ATTACK_SPEED_ID='A002'
//* Ability ID of the "Sonic Wave" ability
public constant integer SONIC_WAVE_ID='A004'
//* Ability ID of the 'disabled' Sonic Wave
public constant integer SONIC_WAVE_DUMMY_ID='h001'
//* Ability ID of the dummy "reveal" ability
public constant integer REVEAL_ID='A005'
//* Ability ID of the "Resonating Strike" ability
public constant integer RESONATING_STRIKE_ID='A006'
//* Ability ID of the "Safeguard" ability
public constant integer SAFEGUARD_ID='A007'
//* Ability ID of the "Iron Will" ability
public constant integer IRON_WILL_ID='A008'
//* Ability ID of the "Tempest" ability
public constant integer TEMPEST_ID='A009'
//* Ability ID of the "Cripple" ability
public constant integer CRIPPLE_ID='A00A'
//* Ability ID of the "Slow" dummy ability
public constant integer SLOW_ID='A00B'
//* Ability ID of the "Dragon's Rage" ability
public constant integer DRAGONS_RAGE_ID='A00C'
//* Ability ID of the dummy "Stun" abiility
public constant integer STUN_ID='A00D'
//* Buff ID of the "Revealed" buff
public constant integer REVEALED_ID='B000'
//* Buff ID of the "Crippled" buff
public constant integer CRIPPLED_ID='B001'
//* Ability ID of the "Silence" ability
private constant integer SILENCE_ID='A00E'
//* Buff ID of the "Silenced" buff
private constant integer SILENCED_ID='B002'
//* How smooth should the spells look globally. A smaller value is
//* smoother but may lag more.
public constant real FPS=1./30.
//* How accurately to update a Lee Sin's energy resource (mana).
//* A smaller value will look smoother.
private constant real ENERGY_FIDELITY=1./2.
//> END CUSTOMIZE SECTION
private constant integer EXPECTED_L1_DAMAGE=56
private constant string GET_AD_ERROR="|cffffcc00[leeSinConsts] [leeSinConsts_getBonusAD] Warning: Tried to get Bonus AD from a non-indexed unit.|r"
private group grp=CreateGroup()
private HandleTable realDamage
private HandleTable preloadDB
private unit dummy
endglobals
//* Sloppy struct practice used just to get unique integers for sounds.
private struct slop
string snd
endstruct
//* Periodic function used to limit Lee Sins to 200 "energy".
private function p takes nothing returns nothing
local unit FoG
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if GetUnitTypeId(FoG)==LEE_SIN_ID and GetUnitState(FoG,UNIT_STATE_MANA)>200. then
call SetUnitState(FoG,UNIT_STATE_MANA,200.)
endif
call GroupRemoveUnit(grp,FoG)
endloop
endfunction
public function getBonusAD takes unit u returns real
local real damage
if realDamage.exists(u) then
set damage=I2R(realDamage[u])-(52.8+(3.2*GetHeroLevel(u)))
if damage>0. then
return damage
else
return 0.
endif
endif
debug call DisplayTextToPlayer(GetLocalPlayer(),0.,0.,GET_AD_ERROR)
return -1.
endfunction
private function preloadAfter takes nothing returns nothing
local timer time=GetExpiredTimer()
local slop s=preloadDB[time]
local sound sd=CreateSound(s.snd,false,false,false,12700,12700,"")
call SetSoundVolume(sd,1)
call StartSound(sd)
call KillSoundWhenDone(sd)
call s.destroy()
set sd=null
call DestroyTimer(time)
set time=null
endfunction
public function preloadSound takes string str returns nothing
local timer time=CreateTimer()
local slop s=slop.create()
set s.snd=str
set preloadDB[time]=s
call TimerStart(time,.01,false,function preloadAfter)
set time=null
endfunction
public function unsilenceUnit takes unit u returns nothing
call UnitRemoveAbility(u,SILENCED_ID)
endfunction
public function silenceUnit takes unit u returns nothing
local real x=GetUnitX(u)
local real y=GetUnitY(u)
call SetUnitOwner(dummy,GetOwningPlayer(u),false)
call SetUnitX(dummy,x)
call SetUnitY(dummy,y)
call UnitAddAbility(dummy,SILENCE_ID)
call IssuePointOrder(dummy,"silence",GetUnitX(u),GetUnitY(u))
call UnitRemoveAbility(dummy,SILENCE_ID)
endfunction
public function revealUnit takes unit u, player forWho, boolean ext returns nothing
call SetUnitOwner(dummy,forWho,false)
call UnitAddAbility(dummy,REVEAL_ID)
if ext then
call SetUnitAbilityLevel(dummy,REVEAL_ID,2)
endif
call SetUnitX(dummy,GetUnitX(u))
call SetUnitY(dummy,GetUnitY(u))
call IssueTargetOrder(dummy,"faeriefire",u)
call UnitRemoveAbility(dummy,REVEAL_ID)
endfunction
public function stunUnit takes unit u returns nothing
call SetUnitOwner(dummy,GetOwningPlayer(u),false)
call UnitAddAbility(dummy,STUN_ID)
call SetUnitX(dummy,GetUnitX(u))
call SetUnitY(dummy,GetUnitY(u))
call IssueTargetOrder(dummy,"thunderbolt",u)
call UnitRemoveAbility(dummy,STUN_ID)
endfunction
private function newDamage takes nothing returns nothing
local unit dS=GetEventDamageSource()
local real damage=GetEventDamage()
if GetUnitTypeId(dS)==LEE_SIN_ID and DamageType.get()==DamageType.ATTACK then
set realDamage[dS]=R2I(damage)
endif
set dS=null
endfunction
//* Condition function for registering new "Lee Sin"s into
//* the system.
private function c takes nothing returns boolean
local unit tU=GetTriggerUnit()
if realDamage.exists(tU)==false then
set realDamage[tU]=EXPECTED_L1_DAMAGE
endif
return false
endfunction
//* Initialization function
private function i takes nothing returns nothing
local unit FoG
local trigger registerNewLees=CreateTrigger()
local region reg=CreateRegion()
set dummy=CreateUnit(Player(13),CASTER_ID,0.,0.,0.)
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(registerNewLees,reg,null)
call TriggerAddCondition(registerNewLees,Condition(function c))
call TimerStart(CreateTimer(),ENERGY_FIDELITY,true,function p)
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
set realDamage=HandleTable.create()
set preloadDB=HandleTable.create()
call StructuredDD.addHandler(function newDamage)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if GetUnitTypeId(FoG)==LEE_SIN_ID then
set realDamage[FoG]=EXPECTED_L1_DAMAGE
endif
call GroupRemoveUnit(grp,FoG)
endloop
set registerNewLees=null
set reg=null
endfunction
endlibrary
scope flurry initializer i
private struct flurDat
unit lee
timer time
integer attackCounter
endstruct
globals
private constant real TIMEOUT=3.
private HandleTable fromLee
private HandleTable fromTime
endglobals
private function handler takes nothing returns nothing
local unit dS=GetEventDamageSource()
local flurDat tempDat
if DamageType.get()==DamageType.ATTACK and GetUnitTypeId(dS)==leeSinConsts_LEE_SIN_ID and fromLee.exists(dS) then
set tempDat=fromLee[dS]
set tempDat.attackCounter=tempDat.attackCounter+1
call SetUnitState(tempDat.lee,UNIT_STATE_MANA,GetUnitState(tempDat.lee,UNIT_STATE_MANA)+15.)
if tempDat.attackCounter==2 then
call UnitRemoveAbility(dS,leeSinConsts_BONUS_ATTACK_SPEED_ID)
call PauseTimer(tempDat.time)
endif
endif
set dS=null
endfunction
private function after takes nothing returns nothing
local flurDat tempDat=fromTime[GetExpiredTimer()]
call UnitRemoveAbility(tempDat.lee,leeSinConsts_BONUS_ATTACK_SPEED_ID)
endfunction
//* Condition function used to instanciate flurry's effect.
private function c takes nothing returns boolean
local unit tU=GetTriggerUnit()
local flurDat tempDat
if GetUnitTypeId(tU)==leeSinConsts_LEE_SIN_ID then
if fromLee.exists(tU) then
set tempDat=fromLee[tU]
call PauseTimer(tempDat.time)
else
set tempDat=flurDat.create()
set tempDat.lee=tU
set tempDat.time=CreateTimer()
set fromLee[tU]=tempDat
set fromTime[tempDat.time]=tempDat
endif
set tempDat.attackCounter=0
call UnitAddAbility(tempDat.lee,leeSinConsts_BONUS_ATTACK_SPEED_ID)
call TimerStart(tempDat.time,TIMEOUT,false,function after)
endif
return false
endfunction
//* Initialization Function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
call StructuredDD.addHandler(function handler)
set fromLee=HandleTable.create()
set fromTime=HandleTable.create()
set t=null
endfunction
endscope
scope sonicWave initializer i
private struct struckDat
unit hit
unit caster
real timeleft
integer abilityReturnValue
endstruct
private struct dashDat
unit caster
unit target
real damage
endstruct
private struct waveDat
unit wave
unit caster
real delX
real delY
real damage
integer stepsLeft
player owner
endstruct
globals
private constant string HIT_SOUND="Abilities\\Weapons\\DragonHawkMissile\\WindSerpentMissile.wav"
private constant string HIT_EFFECT="Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
private constant string LAUNCH_SOUND="Abilities\\Weapons\\FarseerMissile\\FarseerMissileLaunch.wav"
private constant string DASH_SOUND="Abilities\\Weapons\\snapMissile\\SnapDragonMissileLaunch1.wav"
private constant real INITIAL_OFFSET=60.
private constant real SPEED=1500.
private constant real DASH_SPEED=1400.
private constant real DISTANCE=975.
private constant real ENUM_RADIUS=100.
private constant real STRIKE_LASTING_TIME=3.
private constant real RESONATING_STRIKE_RANGE=1100.
private constant real DASH_ARRIVE_DISTANCE=150.
private constant integer WARD_ID='oeye'
private waveDat array waveDB
private integer dbIndex=-1
private timer time=CreateTimer()
private timer secondTimer=CreateTimer()
private timer dashTimer=CreateTimer()
private dashDat array dashDB
private integer dDBIndex=-1
private struckDat array struckDB
private integer sDBIndex=-1
private group grp=CreateGroup()
private HandleTable fromLee
endglobals
//* Secondary Periodic function used to count how much time
//* a Lee Sin unit has to cast resonating strike.
private function p2 takes nothing returns nothing
local struckDat tempDat
local integer index=0
local real cX
local real cY
local real hX
local real hY
local real dist
loop
exitwhen index>sDBIndex
set tempDat=struckDB[index]
set tempDat.timeleft=tempDat.timeleft-leeSinConsts_FPS
set hX=GetUnitX(tempDat.hit)
set hY=GetUnitY(tempDat.hit)
set cX=GetUnitX(tempDat.caster)
set cY=GetUnitY(tempDat.caster)
set dist=SquareRoot((cX-hX)*(cX-hX)+(cY-hY)*(cY-hY))
if tempDat.timeleft<0. or GetUnitAbilityLevel(tempDat.caster,leeSinConsts_RESONATING_STRIKE_ID)<1 or UnitAlive(tempDat.hit)==false or dist>RESONATING_STRIKE_RANGE then
call UnitRemoveAbility(tempDat.caster,leeSinConsts_RESONATING_STRIKE_ID)
call SetUnitAbilityLevel(tempDat.caster,leeSinConsts_SONIC_WAVE_ID,tempDat.abilityReturnValue)
call tempDat.destroy()
set struckDB[index]=struckDB[sDBIndex]
set sDBIndex=sDBIndex-1
set index=index-1
if sDBIndex==-1 then
call PauseTimer(secondTimer)
endif
endif
set index=index+1
endloop
endfunction
//* First periodic function which handles projectile motion for
//* Sonic Wave projectiles.
private function p takes nothing returns nothing
local integer index=0
local waveDat tempDat
local struckDat struck
local unit FoG
local real uX
local real uY
local boolean hitCheck
local sound snd
local boolean notWard
loop
exitwhen index>dbIndex
set tempDat=waveDB[index]
set uX=GetUnitX(tempDat.wave)+tempDat.delX
set uY=GetUnitY(tempDat.wave)+tempDat.delY
call SetUnitX(tempDat.wave,uX)
call SetUnitY(tempDat.wave,uY)
set tempDat.stepsLeft=tempDat.stepsLeft-1
call GroupEnumUnitsInRange(grp,uX,uY,ENUM_RADIUS,null)
set hitCheck=false
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null or hitCheck
set notWard=true
static if leeSinConsts_IS_TEST_MAP then
if GetUnitTypeId(FoG)==WARD_ID then
set notWard=false
endif
endif
if IsUnitEnemy(FoG,tempDat.owner) and UnitAlive(FoG) and IsUnitType(FoG,UNIT_TYPE_STRUCTURE)==false and IsUnitType(FoG,UNIT_TYPE_GROUND) and notWard then
set hitCheck=true
set snd=CreateSound(HIT_SOUND,false,true,true,12700,12700,"")
call SetSoundPosition(snd,GetUnitX(FoG),GetUnitY(FoG),50.)
call StartSound(snd)
call KillSoundWhenDone(snd)
call DamageType.dealCodeDamage(tempDat.caster,FoG,tempDat.damage)
call leeSinConsts_revealUnit(FoG,tempDat.owner,false)
set struck=struckDat.create()
set struck.hit=FoG
set struck.caster=tempDat.caster
set struck.timeleft=STRIKE_LASTING_TIME
set struck.abilityReturnValue=GetUnitAbilityLevel(tempDat.caster,leeSinConsts_SONIC_WAVE_ID)
set sDBIndex=sDBIndex+1
set struckDB[sDBIndex]=struck
set fromLee[tempDat.caster]=struck
call SetUnitAbilityLevel(tempDat.caster,leeSinConsts_SONIC_WAVE_ID,5)
call IncUnitAbilityLevel(tempDat.caster,leeSinConsts_SONIC_WAVE_ID)
call UnitAddAbility(tempDat.caster,leeSinConsts_RESONATING_STRIKE_ID)
if sDBIndex==0 then
//This could easily be reduced to a much slower period to
//improve performance, but to avoid confusing players I
//want it as smooth as possible. To make Lee Sin the
//way he used to be (unlimited dash range) you can change
//"leeSinConsts_FPS" below to a new value of "3." - the same
//must be done in function p2
call TimerStart(secondTimer,leeSinConsts_FPS,true,function p2)
endif
set snd=null
endif
call GroupRemoveUnit(grp,FoG)
endloop
if tempDat.stepsLeft==0 or hitCheck then
call KillUnit(tempDat.wave)
call tempDat.destroy()
set waveDB[index]=waveDB[dbIndex]
set dbIndex=dbIndex-1
set index=index-1
if dbIndex==-1 then
call PauseTimer(time)
endif
endif
set index=index+1
endloop
endfunction
//* Condition function used to register when a Lee Sin uses
//* Sonic Wave
private function c takes nothing returns boolean
local real tX
local real tY
local real uX
local real uY
local unit tU
local real angle
local waveDat tempDat
local player who
local sound snd
if GetSpellAbilityId()==leeSinConsts_SONIC_WAVE_ID then
set tempDat=waveDat.create()
set tempDat.caster=GetTriggerUnit()
set tX=GetSpellTargetX()
set tY=GetSpellTargetY()
set uX=GetUnitX(tempDat.caster)
set uY=GetUnitY(tempDat.caster)
set angle=Atan2(tY-uY,tX-uX)
set who=GetOwningPlayer(tempDat.caster)
set snd=CreateSound(LAUNCH_SOUND,false,true,true,12700,12700,"")
call SetSoundVolume(snd,127)
call SetSoundPitch(snd,.88)
call SetSoundPosition(snd,uX,uY,50.)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd=null
set tempDat.wave=CreateUnit(who,leeSinConsts_SONIC_WAVE_DUMMY_ID,uX+INITIAL_OFFSET*Cos(angle),uY+INITIAL_OFFSET*Sin(angle),angle*bj_RADTODEG)
call SetUnitX(tempDat.wave,uX+INITIAL_OFFSET*Cos(angle))
call SetUnitY(tempDat.wave,uY+INITIAL_OFFSET*Sin(angle))
set tempDat.delX=SPEED*leeSinConsts_FPS*Cos(angle)
set tempDat.delY=SPEED*leeSinConsts_FPS*Sin(angle)
set tempDat.stepsLeft=R2I(DISTANCE/SPEED/leeSinConsts_FPS)
set tempDat.damage=20.+30.*GetUnitAbilityLevel(tempDat.caster,leeSinConsts_SONIC_WAVE_ID)+.9*leeSinConsts_getBonusAD(tempDat.caster)
set tempDat.owner=who
set dbIndex=dbIndex+1
set waveDB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(time,leeSinConsts_FPS,true,function p)
endif
endif
return false
endfunction
//* Periodic function used to handle Lee Sin units which have
//* used Resonating Strike
private function dashP takes nothing returns nothing
local integer index=0
local dashDat tempDat
local real cX
local real cY
local real tX
local real tY
local real delX
local real delY
local real angle
local real dist
local sound snd
loop
exitwhen index>dDBIndex
set tempDat=dashDB[index]
set cX=GetUnitX(tempDat.caster)
set cY=GetUnitY(tempDat.caster)
set tX=GetUnitX(tempDat.target)
set tY=GetUnitY(tempDat.target)
set angle=Atan2(tY-cY,tX-cX)
call SetUnitFacing(tempDat.caster,angle*bj_RADTODEG)
set delX=DASH_SPEED*leeSinConsts_FPS*Cos(angle)
set delY=DASH_SPEED*leeSinConsts_FPS*Sin(angle)
set dist=SquareRoot((tX-(cX+delX))*(tX-(cX+delX))+(tY-(cY+delY))*(tY-(cY+delY)))
if dist>DASH_ARRIVE_DISTANCE then
call SetUnitX(tempDat.caster,cX+delX)
call SetUnitY(tempDat.caster,cY+delY)
endif
if dist<DASH_ARRIVE_DISTANCE or dist<(2.*DASH_SPEED*leeSinConsts_FPS) then
call DamageType.dealCodeDamage(tempDat.caster,tempDat.target,tempDat.damage)
call DestroyEffect(AddSpecialEffect(HIT_EFFECT,tX,tY))
set snd=CreateSound(DASH_SOUND,false,true,true,12700,12700,"")
call SetSoundVolume(snd,127)
call SetSoundPitch(snd,.75)
call SetSoundPosition(snd,tX,tY,50.)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd=null
set dashDB[index]=dashDB[dDBIndex]
set dDBIndex=dDBIndex-1
set index=index-1
call tempDat.destroy()
if dDBIndex==-1 then
call PauseTimer(dashTimer)
endif
endif
set index=index+1
endloop
endfunction
//* Condition function used to look for Lee Sins casting
//* Resonating Strike
private function resC takes nothing returns boolean
local struckDat tempDat
local dashDat dash
if GetSpellAbilityId()==leeSinConsts_RESONATING_STRIKE_ID then
set tempDat=fromLee[GetTriggerUnit()]
call UnitRemoveAbility(tempDat.caster,leeSinConsts_RESONATING_STRIKE_ID)
set dash=dashDat.create()
set dash.caster=tempDat.caster
set dash.target=tempDat.hit
set dash.damage=20.+30.*tempDat.abilityReturnValue + .9*leeSinConsts_getBonusAD(dash.caster) + .08*(GetUnitState(dash.target,UNIT_STATE_MAX_LIFE)-GetUnitState(dash.target,UNIT_STATE_LIFE))
set dDBIndex=dDBIndex+1
set dashDB[dDBIndex]=dash
if dDBIndex==0 then
call TimerStart(dashTimer,leeSinConsts_FPS,true,function dashP)
endif
endif
return false
endfunction
//* Initialization function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
local trigger resStrike=CreateTrigger()
set fromLee=HandleTable.create()
call leeSinConsts_preloadSound(HIT_SOUND)
call leeSinConsts_preloadSound(LAUNCH_SOUND)
call leeSinConsts_preloadSound(DASH_SOUND)
call TriggerRegisterAnyUnitEventBJ(resStrike,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(resStrike,Condition(function resC))
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
set t=null
set resStrike=null
endfunction
endscope
scope safeguard initializer i
private struct willDat
unit who
integer levelReturnAbility
real timeLeft
endstruct
private struct dashDat
unit caster
unit target
integer levelReturnAbility
endstruct
private struct willEffectDat
unit who
endstruct
globals
private constant string DASH_BEGIN_SOUND="Abilities\\Spells\\Orc\\SpiritLink\\SpiritLink1.wav"
private constant string DASH_END_SOUND="Abilities\\Spells\\Orc\\Ensnare\\EnsnareTarget.wav"
private constant string VAMP_EFFECT="Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
private constant string SHIELD_EFFECT="Abilities\\Spells\\Items\\StaffOfSanctuary\\Staff_Sanctuary_Target.mdl"
private constant real DASH_SPEED=1700.
private constant real TARGET_DISTANCE=160.
private constant real TARGET_DISTANCE_SQUARED=TARGET_DISTANCE*TARGET_DISTANCE
private constant real SHIELD_DURATION=5.
private constant real IRON_WILL_AVAILABLE_DURATION=3.
private constant real WILL_EFFECT_DURATION=5.
private HandleTable level
private HandleTable levelWithEffect
private HandleTable attachedWillEffect
private dashDat array dashDB
private integer dbIndex=-1
private timer time=CreateTimer()
private timer ironWillTimer=CreateTimer()
private willDat array willDB
private integer willDBIndex=-1
endglobals
private function abilitySwapCheckP takes nothing returns nothing
local integer index=0
local willDat tempDat
loop
exitwhen index>willDBIndex
set tempDat=willDB[index]
set tempDat.timeLeft=tempDat.timeLeft-leeSinConsts_FPS
if tempDat.timeLeft<=0. or GetUnitAbilityLevel(tempDat.who,leeSinConsts_IRON_WILL_ID)<1 then
call UnitRemoveAbility(tempDat.who,leeSinConsts_IRON_WILL_ID)
call SetUnitAbilityLevel(tempDat.who,leeSinConsts_SAFEGUARD_ID,tempDat.levelReturnAbility)
call tempDat.destroy()
set willDB[index]=willDB[willDBIndex]
set willDBIndex=willDBIndex-1
set index=index-1
if willDBIndex==-1 then
call PauseTimer(ironWillTimer)
endif
endif
set index=index+1
endloop
endfunction
//* Periodic function usesd to handle any Lee Sins which have
//* cast Safeguard on an allied unit.
private function p takes nothing returns nothing
local integer index=0
local dashDat tempDat
local willDat will
local real cX
local real cY
local real tX
local real tY
local real angle
local real delX
local real delY
local real distSquared
local real amount
local sound snd
loop
exitwhen index>dbIndex
set tempDat=dashDB[index]
set cX=GetUnitX(tempDat.caster)
set cY=GetUnitY(tempDat.caster)
set tX=GetUnitX(tempDat.target)
set tY=GetUnitY(tempDat.target)
set angle=Atan2(tY-cY,tX-cX)
set delX=DASH_SPEED*leeSinConsts_FPS*Cos(angle)
set delY=DASH_SPEED*leeSinConsts_FPS*Sin(angle)
if tempDat.caster!=tempDat.target then
call SetUnitX(tempDat.caster,cX+delX)
call SetUnitY(tempDat.caster,cY+delY)
call SetUnitFacing(tempDat.caster,angle*bj_RADTODEG)
endif
set distSquared=(tX-cX)*(tX-cX)+(tY-cY)*(tY-cY)
if distSquared<TARGET_DISTANCE_SQUARED or distSquared<((DASH_SPEED*leeSinConsts_FPS)*(DASH_SPEED*leeSinConsts_FPS)*2.) then
set dashDB[index]=dashDB[dbIndex]
set amount=40.*tempDat.levelReturnAbility+.8*GetHeroInt(tempDat.caster,true)
call Shield.add(tempDat.caster,amount,SHIELD_DURATION,SHIELD_EFFECT,"origin")
if tempDat.caster!=tempDat.target then
call Shield.add(tempDat.target,amount,SHIELD_DURATION,SHIELD_EFFECT,"origin")
set snd=CreateSound(DASH_END_SOUND,false,true,true,12700,12700,"")
call SetSoundPosition(snd,tX,tY,60.)
call SetSoundVolume(snd,127)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd=null
endif
call SetUnitAbilityLevel(tempDat.caster,leeSinConsts_SAFEGUARD_ID,5)
call IncUnitAbilityLevel(tempDat.caster,leeSinConsts_SAFEGUARD_ID)
call UnitAddAbility(tempDat.caster,leeSinConsts_IRON_WILL_ID)
set level[tempDat.caster]=tempDat.levelReturnAbility
set will=willDat.create()
set will.who=tempDat.caster
set will.levelReturnAbility=tempDat.levelReturnAbility
set will.timeLeft=IRON_WILL_AVAILABLE_DURATION
set willDBIndex=willDBIndex+1
set willDB[willDBIndex]=will
call tempDat.destroy()
if willDBIndex==0 then
//I know this isn't the ideal use of periodic timers,
//but it's efficient enough, and will allow me to
//re-enable safeguard ASAP.
call TimerStart(ironWillTimer,leeSinConsts_FPS,true,function abilitySwapCheckP)
endif
set dbIndex=dbIndex-1
set index=index-1
if dbIndex==-1 then
call PauseTimer(time)
endif
endif
set index=index+1
endloop
endfunction
//* Condition function used to register Safeguard casts.
private function c takes nothing returns boolean
local dashDat tempDat
local sound snd
if GetSpellAbilityId()==leeSinConsts_SAFEGUARD_ID then
set tempDat=dashDat.create()
set tempDat.caster=GetTriggerUnit()
set tempDat.target=GetSpellTargetUnit()
set snd=CreateSound(DASH_BEGIN_SOUND,false,true,true,12700,12700,"")
call SetSoundPitch(snd,2.)
call SetSoundPosition(snd,GetUnitX(tempDat.caster),GetUnitY(tempDat.caster),50.)
call SetSoundVolume(snd,127)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd=null
set tempDat.levelReturnAbility=GetUnitAbilityLevel(tempDat.caster,leeSinConsts_SAFEGUARD_ID)
set dbIndex=dbIndex+1
set dashDB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(time,leeSinConsts_FPS,true,function p)
endif
endif
return false
endfunction
private function willEffectAfter takes nothing returns nothing
local timer clock=GetExpiredTimer()
local willEffectDat tempDat=attachedWillEffect[clock]
call levelWithEffect.flush(tempDat.who)
call attachedWillEffect.flush(clock)
call DestroyTimer(clock)
set clock=null
endfunction
private function ironWillC takes nothing returns boolean
local unit tU
local willEffectDat tempDat
local timer clock
if GetSpellAbilityId()==leeSinConsts_IRON_WILL_ID then
set tU=GetTriggerUnit()
call UnitRemoveAbility(tU,leeSinConsts_IRON_WILL_ID)
set levelWithEffect[tU]=level[tU]
set tempDat=willEffectDat.create()
set tempDat.who=tU
set clock=CreateTimer()
set attachedWillEffect[clock]=tempDat
call TimerStart(clock,WILL_EFFECT_DURATION,false,function willEffectAfter)
set tU=null
set clock=null
endif
return false
endfunction
//* StructuredDD handler used to recognize Lee Sin attacking.
private function handler takes nothing returns nothing
local unit dS=GetEventDamageSource()
local real damage=GetEventDamage()
if DamageType.get()!=DamageType.SPELL and levelWithEffect.exists(dS) then
call SetUnitState(dS,UNIT_STATE_LIFE,GetUnitState(dS,UNIT_STATE_LIFE)+.05*levelWithEffect[dS]*damage)
call DestroyEffect(AddSpecialEffectTarget(VAMP_EFFECT,dS,"origin"))
endif
set dS=null
endfunction
//* Initialization function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
local trigger ironWill=CreateTrigger()
call leeSinConsts_preloadSound(DASH_BEGIN_SOUND)
call leeSinConsts_preloadSound(DASH_END_SOUND)
set level=HandleTable.create()
set levelWithEffect=HandleTable.create()
set attachedWillEffect=HandleTable.create()
call TriggerRegisterAnyUnitEventBJ(ironWill,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(ironWill,Condition(function ironWillC))
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
call StructuredDD.addHandler(function handler)
set t=null
set ironWill=null
endfunction
endscope
scope tempest initializer i
private struct crippleSlow
private static constant string CRIPPLE_FX="Abilities\\Spells\\Human\\slow\\slowtarget.mdl"
private static HandleTable fromUnit
private static HandleTable fromTimer
private static unit caster
private unit who
private integer level
private integer duration
private timer clock
private effect fx
private method setSlow takes nothing returns nothing
call this.removeSlow()
call SetUnitOwner(thistype.caster,GetOwningPlayer(this.who),false)
call UnitAddAbility(thistype.caster,leeSinConsts_SLOW_ID)
call SetUnitAbilityLevel(thistype.caster,leeSinConsts_SLOW_ID,(this.level*this.duration/4)+1)
call SetUnitX(thistype.caster,GetUnitX(this.who))
call SetUnitY(thistype.caster,GetUnitY(this.who))
call IssueTargetOrder(thistype.caster,"slow",this.who)
call UnitRemoveAbility(thistype.caster,leeSinConsts_SLOW_ID)
endmethod
private method removeSlow takes nothing returns nothing
call UnitRemoveAbility(this.who,leeSinConsts_CRIPPLED_ID)
endmethod
//* Periodic method used to update a units slowing value
//* which allows us to simulate a "changing" slow effect.
private static method p takes nothing returns nothing
local timer exp=GetExpiredTimer()
local thistype tempDat=fromTimer[exp]
set tempDat.duration=tempDat.duration-1
if tempDat.duration>0 and UnitAlive(tempDat.who) then
call tempDat.setSlow()
else
call DestroyEffect(tempDat.fx)
call tempDat.removeSlow()
call DestroyTimer(tempDat.clock)
call tempDat.destroy()
endif
set exp=null
endmethod
public static method setGreater takes unit who, integer level, integer duration returns nothing
local thistype tempDat
if fromUnit.exists(who) then
set tempDat=fromUnit[who]
if level>tempDat.level then
set tempDat.level=level
endif
if duration>tempDat.duration then
set tempDat.duration=duration
endif
else
set tempDat=thistype.create()
set fromUnit[who]=tempDat
set tempDat.who=who
set tempDat.fx=AddSpecialEffectTarget(thistype.CRIPPLE_FX,tempDat.who,"origin")
set tempDat.level=level
set tempDat.duration=duration
set tempDat.clock=CreateTimer()
set thistype.fromTimer[tempDat.clock]=tempDat
call TimerStart(tempDat.clock,1.,true,function thistype.p)
endif
call tempDat.setSlow()
endmethod
private static method onInit takes nothing returns nothing
set fromUnit=HandleTable.create()
set fromTimer=HandleTable.create()
set thistype.caster=CreateUnit(Player(13),leeSinConsts_CASTER_ID,0.,0.,0.)
endmethod
endstruct
private struct unitWrapper
unit who
endstruct
private struct crippleLastDat
unit who
real timeLeft
endstruct
globals
private constant string TEMPEST_FX="Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
private constant real TEMPEST_RADIUS=350.
private constant real CRIPPLE_RADIUS=600.
private HandleTable tab
private HandleTable fromShimTimer
private group grp=CreateGroup()
endglobals
private function afterThree takes nothing returns nothing
local timer time=GetExpiredTimer()
local crippleLastDat tempDat=fromShimTimer[time]
set tempDat.timeLeft=tempDat.timeLeft-leeSinConsts_FPS
if GetUnitAbilityLevel(tempDat.who,leeSinConsts_CRIPPLE_ID)<1 or tempDat.timeLeft<=0. or UnitAlive(tempDat.who)==false then
call UnitRemoveAbility(tempDat.who,leeSinConsts_CRIPPLE_ID)
call SetUnitAbilityLevel(tempDat.who,leeSinConsts_TEMPEST_ID,tab[tempDat.who])
call DestroyTimer(time)
call tempDat.destroy()
endif
set time=null
endfunction
private function shimAfter takes nothing returns nothing
local timer time=GetExpiredTimer()
local crippleLastDat newDat=crippleLastDat.create()
local unitWrapper tempDat=fromShimTimer[time]
set newDat.who=tempDat.who
set newDat.timeLeft=3.
call TimerStart(time,leeSinConsts_FPS,true,function afterThree)
set fromShimTimer[time]=newDat
set tab[tempDat.who]=GetUnitAbilityLevel(tempDat.who,leeSinConsts_TEMPEST_ID)
call SetUnitAbilityLevel(tempDat.who,leeSinConsts_TEMPEST_ID,5)
call IncUnitAbilityLevel(tempDat.who,leeSinConsts_TEMPEST_ID)
call UnitAddAbility(tempDat.who,leeSinConsts_CRIPPLE_ID)
call tempDat.destroy()
set time=null
endfunction
//* Condition function used to recognize when a Lee Sin unit
//* uses the Tempest ability.
private function c takes nothing returns boolean
local unit tU
local unit FoG
local player owner
local boolean hit
local real tX
local real tY
local timer shim
local unitWrapper tempDat
if GetSpellAbilityId()==leeSinConsts_TEMPEST_ID then
set tU=GetTriggerUnit()
set tX=GetUnitX(tU)
set tY=GetUnitY(tU)
set hit=false
call GroupEnumUnitsInRange(grp,tX,tY,TEMPEST_RADIUS,null)
set owner=GetOwningPlayer(tU)
call DestroyEffect(AddSpecialEffect(TEMPEST_FX,tX,tY))
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitEnemy(FoG,owner) and IsUnitType(FoG,UNIT_TYPE_STRUCTURE)==false and UnitAlive(FoG) then
call DamageType.dealCodeDamage(tU,FoG,25.+35.*GetUnitAbilityLevel(tU,leeSinConsts_TEMPEST_ID)+leeSinConsts_getBonusAD(tU))
call leeSinConsts_revealUnit(FoG,owner,true)
set hit=true
endif
call GroupRemoveUnit(grp,FoG)
endloop
if hit then
set shim=CreateTimer()
set tempDat=unitWrapper.create()
set tempDat.who=tU
set fromShimTimer[shim]=tempDat
call TimerStart(shim,0.,false,function shimAfter)
endif
set tU=null
endif
return false
endfunction
private function crippleC takes nothing returns boolean
local unit tU
local unit FoG
local player owner
local integer returnedLevel
if GetSpellAbilityId()==leeSinConsts_CRIPPLE_ID then
set tU=GetTriggerUnit()
call UnitRemoveAbility(tU,leeSinConsts_CRIPPLE_ID)
set returnedLevel=tab[tU]
call GroupEnumUnitsInRange(grp,GetUnitX(tU),GetUnitY(tU),CRIPPLE_RADIUS,null)
set owner=GetOwningPlayer(tU)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitEnemy(FoG,owner) and GetUnitAbilityLevel(FoG,leeSinConsts_REVEALED_ID)>0 then
call crippleSlow.setGreater(FoG,returnedLevel,4)
endif
call GroupRemoveUnit(grp,FoG)
endloop
set tU=null
endif
return false
endfunction
//* Initialization function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
local trigger cripple=CreateTrigger()
set tab=HandleTable.create()
set fromShimTimer=HandleTable.create()
call TriggerRegisterAnyUnitEventBJ(cripple,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(cripple,Condition(function crippleC))
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
set cripple=null
set t=null
endfunction
endscope
scope dragonsRage initializer i
private struct knockup
private static constant real GRAVITY=-80.*leeSinConsts_FPS
private static timer clock=CreateTimer()
private static thistype array kDB
private static integer dbIndex=-1
private real duration
private unit who
private real delZ
//* Perioidic method used to incur knockup effects from
//* Dragon's Rage.
private static method p takes nothing returns nothing
local integer index=0
local thistype tempDat
loop
exitwhen index>thistype.dbIndex
set tempDat=kDB[index]
set tempDat.delZ=tempDat.delZ+GRAVITY
call SetUnitFlyHeight(tempDat.who,GetUnitFlyHeight(tempDat.who)+tempDat.delZ,0.)
if GetUnitFlyHeight(tempDat.who)<=5. and tempDat.delZ<0. then
call SetUnitPropWindow(tempDat.who,GetUnitDefaultPropWindow(tempDat.who))
call SetUnitFlyHeight(tempDat.who,GetUnitDefaultFlyHeight(tempDat.who),0.)
call leeSinConsts_unsilenceUnit(tempDat.who)
call tempDat.destroy()
set kDB[index]=kDB[thistype.dbIndex]
set thistype.dbIndex=thistype.dbIndex-1
set index=index-1
if thistype.dbIndex==-1 then
call PauseTimer(thistype.clock)
endif
endif
set index=index+1
endloop
endmethod
public static method add takes unit who, real duration returns nothing
local thistype tempDat=thistype.create()
set tempDat.duration=duration
set tempDat.who=who
set tempDat.delZ=duration/2.*(-GRAVITY)/leeSinConsts_FPS
set dbIndex=dbIndex+1
set kDB[dbIndex]=tempDat
call SetUnitPropWindow(who,0.)
if UnitAddAbility(who,'Arav') then
call UnitRemoveAbility(who,'Arav')
endif
if dbIndex==0 then
call TimerStart(thistype.clock,leeSinConsts_FPS,true,function thistype.p)
endif
endmethod
endstruct
private struct knockback
unit who
unit caster
real delX
real delY
real damage
real distance=0
group g
endstruct
globals
private constant string DRAGONKICK_SOUND="Abilities\\Weapons\\Catapult\\CatapultMissile1.wav"
private constant string FLAME_EFFECT="Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private constant string BLOOD_EFFECT="Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
private constant real KNOCKBACK_SPEED=1000.
private constant real KNOCKBACK_DISTANCE=1000.
private constant real KNOCKUP_RADIUS=100.
private timer time=CreateTimer()
private knockback array knockDB
private integer dbIndex=-1
private group grp=CreateGroup()
endglobals
//* Periodic function used to control knockback effects
//* from Dragon's Rage.
private function p takes nothing returns nothing
local integer index=0
local knockback tempDat
local boolean wall
local unit FoG
local real newX
local real newY
local player owner
loop
exitwhen index>dbIndex
set wall=false
set tempDat=knockDB[index]
set tempDat.distance=tempDat.distance+KNOCKBACK_SPEED*leeSinConsts_FPS
set owner=GetOwningPlayer(tempDat.caster)
if IsTerrainWalkable(GetUnitX(tempDat.who)+tempDat.delX,GetUnitY(tempDat.who)+tempDat.delY) then
set newX=GetUnitX(tempDat.who)+tempDat.delX
set newY=GetUnitY(tempDat.who)+tempDat.delY
call SetUnitX(tempDat.who,newX)
call SetUnitY(tempDat.who,newY)
call GroupEnumUnitsInRange(grp,newX,newY,KNOCKUP_RADIUS,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if UnitAlive(FoG) and IsUnitType(FoG,UNIT_TYPE_HERO) and IsUnitEnemy(FoG,owner) and FoG!=tempDat.who and IsUnitInGroup(FoG,tempDat.g)==false then
call GroupAddUnit(tempDat.g,FoG)
call leeSinConsts_silenceUnit(FoG)
call knockup.add(FoG,1.)
endif
call GroupRemoveUnit(grp,FoG)
endloop
else
set wall=true
call leeSinConsts_stunUnit(tempDat.who)
endif
if wall or tempDat.distance>=KNOCKBACK_DISTANCE then
call SetUnitPropWindow(tempDat.who,GetUnitDefaultPropWindow(tempDat.who))
call leeSinConsts_unsilenceUnit(tempDat.who)
call DestroyGroup(tempDat.g)
call tempDat.destroy()
set knockDB[index]=knockDB[dbIndex]
set dbIndex=dbIndex-1
set index=index-1
if dbIndex==-1 then
call PauseTimer(time)
endif
endif
set index=index+1
endloop
endfunction
//* Condition function used to recognize Dragon's Rage
//* on cast.
private function c takes nothing returns boolean
local knockback tempDat
local real targX
local real targY
local real trigX
local real trigY
local real angle
local sound s
local integer index=-2
if GetSpellAbilityId()==leeSinConsts_DRAGONS_RAGE_ID then
set tempDat=knockback.create()
set tempDat.caster=GetTriggerUnit()
set tempDat.who=GetSpellTargetUnit()
set tempDat.damage=200.*GetUnitAbilityLevel(tempDat.caster,leeSinConsts_DRAGONS_RAGE_ID)+2.*leeSinConsts_getBonusAD(tempDat.caster)
set tempDat.g=CreateGroup()
call DamageType.dealCodeDamage(tempDat.caster,tempDat.who,tempDat.damage)
set targX=GetUnitX(tempDat.who)
set targY=GetUnitY(tempDat.who)
call leeSinConsts_silenceUnit(tempDat.who)
set s=CreateSound(DRAGONKICK_SOUND,false,true,true,12700,12700,"")
call SetSoundPosition(s,targX,targY,50.)
call SetSoundVolume(s,127)
call StartSound(s)
call SetUnitPropWindow(tempDat.who,0.)
call KillSoundWhenDone(s)
set trigX=GetUnitX(tempDat.caster)
set trigY=GetUnitY(tempDat.caster)
set angle=Atan2(targY-trigY,targX-trigX)
set tempDat.delX=KNOCKBACK_SPEED*leeSinConsts_FPS*Cos(angle)
set tempDat.delY=KNOCKBACK_SPEED*leeSinConsts_FPS*Sin(angle)
loop
exitwhen index>2
call DestroyEffect(AddSpecialEffect(FLAME_EFFECT,trigX+80.*Cos(angle+15.*bj_DEGTORAD*index),trigY+80.*Sin(angle+15.*bj_DEGTORAD*index)))
call DestroyEffect(AddSpecialEffect(BLOOD_EFFECT,trigX+100.*Cos(angle+10.*bj_DEGTORAD*index),trigY+100.*Sin(angle+10.*bj_DEGTORAD*index)))
set index=index+1
endloop
set dbIndex=dbIndex+1
set knockDB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(time,leeSinConsts_FPS,true,function p)
endif
endif
return false
endfunction
//* Initialization function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
call leeSinConsts_preloadSound(DRAGONKICK_SOUND)
set t=null
endfunction
endscope