• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Lee Sin Spellpack v1.1.0

Lee Sin the Blind Monk Spellpack

Video

Introduction:

I posted a video of the twisted fate spellpack from awhile back to reddit and received an overwhelmingly positive response. Everyone voted that Lee Sin should be next, and thus, here it is. Enjoy.

Notes:

  • I've created this pack with the intention that the abilities matched the real ones in league of legends as close as possible. I have not modified game constants to make level 18 stats match those of league of legends identically, I just aimed for values that were "close enough" by choosing good stats and stats per level, etc.
  • The real knockback effect created by Lee Sin's Dragon's Rage ability can knock targets through walls *given that the endpoint is in pathable terrain*. I've decided not to implement this to avoid getting units stuck on the test map. If I create another league of legends champion which also needs a league style knockback, I'll create a unifying library for that.
  • I've chosen not to follow exactly trivial code modification standards for his spells because this is designed to match twisted fate with extreme accuracy, not designed for others to modify.
  • Yes, this is fully MUI. Try not to drool on your keyboard.

Description of Spells:

Flurry (Innate): After using an ability, Lee Sin's next 2 basic attacks within 3 seconds gain 40% attack speed and return 15 energy each.

Sonic Wave (Active): Lee Sin shoots a wave of sound in a line, dealing physical damage to the first enemy it hits and revealing the enemy for 3 seconds. If Sonic Wave hits, Lee Sin can cast Resonating Strike within the next 3 seconds.

Resonating Strike (Active): Lee Sin dashes to the enemy revealed by Sonic Wave, dealing physical damage including 8% of the target's missing health. Minions and monsters take a maximum of 400 damage.

Safeguard (Active): Lee Sin dashes to target ally, he and the target each gaining a shield that absorbs a certain amount of damage. Each shield dissipates after 5 seconds if not already destroyed. Lee Sin can cast Iron Will within 3 seconds of using Safeguard.

Iron Will (Active): For 5 seconds, Lee Sin gains bonus life steal and spell vamp.

Tempest (Active): Lee Sin smashes the ground, dealing magic damage to all nearby enemies and revealing them for 4 seconds. If Tempest hits an enemy, Lee Sin can cast Cripple within the next 3 seconds.

Cripple (Active): Lee Sin cripples all enemies revealed by Tempest reducing their movement and attack speed. The movement and attack speed recover over 4 seconds.

Dragon's Rage (Active): Lee Sin deals physical damage to a target champion and knocks the target back 1200 units over 1 second. Enemies the target collides with take the same damage and are knocked airborne for 1 second.

Requirements:


* Yes I know it's deprecated. If anyone actually cares, I can change it to another lightweight library.

The Scripts:


JASS:
//* 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



JASS:
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



JASS:
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



JASS:
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



JASS:
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



JASS:
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


Credits:
  • league of legends/riot games for creating and designing the entirety of lee sin the blind monk (none of this spell pack is original content).
  • Anitarf for being partially responsible for the IsTerrainWalkable classic snippet.
  • Vexorian, MindworX, PitzerMike, others for the creation of Table, JassHelper, and JNGP. I would never have created these without them.

Installation:
  1. Please copy all object data in this map to yours.
  2. Update all values in the CUSTOMIZE section of leeSinConsts to match the newly imported data.
  3. Make sure you have copies of Table by Vexorian, IsTerrainWalkable by Anitarf, StructuredDD, and DamageType.
  4. Play single player or online!

Change Log:
  • 2013.05.29 - Updated to use newer versions of StructuredDD, DamageType, and Shield. Fixed one minor bug when preprocessing with debug mode enabled. Improved some function/method descriptions. Added import instructions.
  • 2013.02.05 - Initial upload to spells section.

Keywords:
league, legends, wc3, tft, frozen, throne, lee, sin, lee sin, blind, monk
Contents

Lee Sin 1.0.0 (Map)

Reviews
Lee Sin Spellpack v1.1.0 | Reviewed by Maker | 30th Jun 2013 APPROVED The spell pack is leakless and MUI Lee Sin Spellpack v1.0.0 | Reviewed by Maker | 29th May 2013 NEEDS FIX [tr] The debug...

Moderator

M

Moderator


Lee Sin Spellpack v1.1.0 | Reviewed by Maker | 30th Jun 2013
APPROVED


126248-albums6177-picture66521.png


  • The spell pack is leakless and MUI
[tr]



Lee Sin Spellpack v1.0.0 | Reviewed by Maker | 29th May 2013
NEEDS FIX


126248-albums6177-picture66522.png


  • The debug message in getBonusAD function in leeSinConsts
    is missing "call" and the script doesn't compile
126248-albums6177-picture66523.png


  • Add importing instructions into the map file
  • Your abilities could benefit from having some cast animations
    and some follow through time
  • Having cast point of 0.00 on the hero causes some issues like
    the hero facing completely opposite direction to where the
    projectile launches
  • Some of the function names are not very descriptive
[tr]
 
Level 6
Joined
Oct 23, 2011
Messages
182
It doesn't heal them, it uses my resource Shield to temporarily shield them from damage.

That's not the point. I get that your system is supposed to temporarily shield them from damage. However, the problem is that safeguard doesn't actually shield you from damage but actually heals you for small amount when used (and you lose that hp in end)

This isn't how Lee Sin's W works, neither have I seen this kind of 'shield'
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,516
kStiyl, there is no shield functionality in warcraft 3 that behaves like that of league of legends, so I've coded it myself. I can assure you it behaves exactly like in league of legends.

You can test it yourself by shielding, and then taking damage which is greater than the shield's value before its duration is over, and then see what happens after the duration.
 
Level 6
Joined
Oct 23, 2011
Messages
182
All other shield systems I've seen behave like that, and I'm fairly sure that a lot of people are used to having shield that actually 'block' damage instead of showing it through fluctuating HP. Personally, I think it's confusing.

Is there some reason you coded it that way? I'm really curious
Sorry for going off-topic with spell
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,516
Hello,

In the customize section of my Shield resource, there is a line:

private static constant real TIMER_PERIOD=1./10.

If you set the value to something like 1./30. or 1./60., the hp bars will not appear to fluctuate anymore.

The reason why there is some "false" hp added to the unit is to better match how health bars look in league of legends. It has the added benefit of reducing the chance of fatal damage killing a unit that should otherwise be blocked.

Here's an example:

Unit has 10/200 hp and uses a shield ability which should block 100 damage. In my system, the unit will temporarily have 110 hp, thus it can block an attack of, say, 50 damage.

However, in the systems you may be used to seeing, this feature does not exist. A unit with 10 hp who uses a shield will die to an attack which should deal 50 damage.

The reason why they work that way is because

most people are also used to having shield that actually 'block' damage

That doesn't exist - there is no way to directly block mixed damage.
 
Level 6
Joined
Oct 23, 2011
Messages
182
Unit has 10/200 hp and uses a shield ability which should block 100 damage. In my system, the unit will temporarily have 110 hp, thus it can block an attack of, say, 50 damage.

However, in the systems you may be used to seeing, this feature does not exist. A unit with 10 hp who uses a shield will die to an attack which should deal 50 damage.

In the systems I am seeing (and using) can block fatal damage via 0 second timers + vitality gem ability. In same manner, I don't see how is your system going to block a damage that is over the unit's hp, if the only thing done is increasing current life.

(ex. what if a unit with 100 hp gets a shield of 100,000 via your system? is it going to block 100000 damage as well? increase its hp to 100,000?)
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,516
In the systems I am seeing (and using) can block fatal damage via 0 second timers + vitality gem ability. In same manner, I don't see how is your system going to block a damage that is over the unit's hp, if the only thing done is increasing current life.

(ex. what if a unit with 100 hp gets a shield of 100,000 via your system? is it going to block 100000 damage as well? increase its hp to 100,000?)

Hello,

The Periapt of Vitality item just uses "Item Life Bonus" as its ability (which gives some flat HP to whatever unit it is attached to). If you think my Shield resource could benefit from using this method, I'd be interested to hear about it.

Are you suggesting I:

  1. Detect an attack
  2. Add item life bonus temporarily
  3. Use a 0. second timer
  4. Remove the life bonus (logicly)

I'd have to do some tests to see if this can actually block fatal damage as you say.

However, to answer your question,

(ex. what if a unit with 100 hp gets a shield of 100,000 via your system? is it going to block 100000 damage as well? increase its hp to 100,000?)

No it won't give the unit 100,000 more hp. It will just fill the void up to the unit's max hp, and then the rest is stored in a separate variable.

To simplify, the system would still block 100,000 hp, but the unit would not appear to have 100,000 hp.

Here are the members of Shield instances:

JASS:
        private unit u
        private real shieldLeft
        private real falseHP
        private real timeLeft
        private effect fx
        private thistype prevShield=0
        private thistype nextShield=0

Where falseHP is the amount of hp temporarily added, and shieldLeft is the unallocated shield value - the two together represent the shield's total value.
 
Level 2
Joined
Apr 24, 2014
Messages
15
Lets say you didn't understand me,
I used sonic wave on player 1 and then player 2 stood between me and target that I hit with sonic wave(player 1) and when i used resonating strike, i stopped at player 2.
I know what Lee Sin does i am platinum thanks to him. :)
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,516
I used sonic wave on player 1 and then player 2 stood between me and target that I hit with sonic wave(player 1) and when i used resonating strike, i stopped at player 2.

No, that won't happen.

JASS:
            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)
 
Level 2
Joined
Apr 24, 2014
Messages
15
Well it happened thats what I was trying to tell you.
more then once, and i have restarted game few times to check it, still nothing.

p.s. Jass is similar to Arabic for me.
 
Top