Name | Type | is_array | initial_value |
//TESH.scrollpos=3
//TESH.alwaysfold=0
scope createLeeSins initializer i
globals
private unit redHero
endglobals
private function c takes nothing returns boolean
call SetHeroLevel(redHero,GetHeroLevel(redHero)+1,true)
return false
endfunction
private function i takes nothing returns nothing
local integer index=0
local integer count=0
local trigger t
loop
exitwhen index>3
if GetPlayerSlotState(Player(index))==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(index))==MAP_CONTROL_USER then
set count=count+1
set redHero=CreateUnit(Player(index),leeSinConsts_LEE_SIN_ID,GetStartLocationX(index),GetStartLocationY(index),270.)
endif
set index=index+1
endloop
if count==1 then
call CreateUnit(Player(1),'nftr',-4600.,2600.,270.)
call CreateUnit(Player(1),'hfoo',-4600.,1500.,270.)
call CreateUnit(Player(1),'hfoo',-4600.,1500.,270.)
call CreateUnit(Player(1),'Hpal',-4600.,1300.,270.)
call CreateUnit(Player(1),'Hpal',-4600.,1100.,270.)
call CreateUnit(Player(0),'hfoo',-5185.,3050.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateUnit(Player(1),'hfoo',-4450.,-1480.,0.)
call CreateItem('wswd',-5185.,3050.)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"|cff999999Singleplayer mode|r enabled. You can find some |cffffcc00test|r objects around your spawn.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"Press |cffffcc00Esc|r to level up your Blind Monk.")
set t=CreateTrigger()
call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC)
call TriggerAddCondition(t,Condition(function c))
set t=null
endif
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope visionTowers initializer i
globals
private HandleTable tab
private constant real VISION_DURATION=5.
private constant real SIZE=150.
private constant string SOUND_FILE="Sound\\Interface\\SecretFound.wav"
private constant integer VISION_TOWER='h000'
private sound snd
endglobals
private function after takes nothing returns nothing
local timer time=GetExpiredTimer()
local integer id=tab[time]
local player who=Player(id)
if GetLocalPlayer()==who then
call FogEnable(true)
call FogMaskEnable(false)
endif
call tab.flush(time)
call DestroyTimer(time)
set time=null
endfunction
private function c takes nothing returns boolean
local unit tU=GetTriggerUnit()
local player who
local timer time
if GetUnitTypeId(tU)==leeSinConsts_LEE_SIN_ID then
set time=CreateTimer()
set who=GetOwningPlayer(tU)
if GetLocalPlayer()==who then
call StartSound(snd)
call FogEnable(false)
call FogMaskEnable(false)
endif
set tab[time]=GetPlayerId(who)
call TimerStart(time,VISION_DURATION,false,function after)
endif
return false
endfunction
private function i takes nothing returns nothing
local group grp=CreateGroup()
local unit FoG
local rect rct=Rect(-1.,-1.,1.,1.)
local region reg=CreateRegion()
local real uX
local real uY
local trigger t=CreateTrigger()
set tab=HandleTable.create()
set snd=CreateSound(SOUND_FILE,false,false,false,12700,12700,"")
call SetSoundVolume(snd,1)
call StartSound(snd)
call SetSoundVolume(snd,127)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"|cff99ff99Hint: |r Approach |cffffcc00Vision Towers|r to temporarily gain map vision.")
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if GetUnitTypeId(FoG)==VISION_TOWER then
set uX=GetUnitX(FoG)
set uY=GetUnitY(FoG)
call SetRect(rct,uX-SIZE,uY-SIZE,uX+SIZE,uY+SIZE)
call RegionAddRect(reg,rct)
endif
call GroupRemoveUnit(grp,FoG)
endloop
call TriggerRegisterEnterRegion(t,reg,null)
call TriggerAddCondition(t,Condition(function c))
call RemoveRect(rct)
call DestroyGroup(grp)
set t=null
set rct=null
set reg=null
set grp=null
endfunction
endscope
//TESH.scrollpos=3
//TESH.alwaysfold=0
scope revive initializer i
private struct ridiculous
unit u
endstruct
globals
private HandleTable tab
private constant real RESPAWN_TIME=5.
private constant integer VISION_WARD='wswd'
endglobals
private function after takes nothing returns nothing
local timer time=GetExpiredTimer()
local ridiculous r=tab[time]
local integer id=GetPlayerId(GetOwningPlayer(r.u))
if GetPlayerSlotState(Player(id))==PLAYER_SLOT_STATE_PLAYING then
call ReviveHero(r.u,GetStartLocationX(id),GetStartLocationY(id),true)
else
call DisplayTextToPlayer(GetLocalPlayer(),0.,0.,"|cffffcc00"+GetPlayerName(Player(id))+"|r has disconnected.")
endif
call tab.flush(time)
call r.destroy()
call DestroyTimer(time)
set time=null
endfunction
private function c takes nothing returns boolean
local timer time
local ridiculous r
local unit tU=GetTriggerUnit()
local unit kU
local player killer
local player tP
if GetUnitTypeId(tU)==leeSinConsts_LEE_SIN_ID then
set kU=GetKillingUnit()
set killer=GetOwningPlayer(kU)
set tP=GetOwningPlayer(tU)
if GetLocalPlayer()==tP then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"|cffffcc00"+GetPlayerName(killer)+"|r has killed you! You will respawn in |cffffcc005|r seconds.")
elseif GetLocalPlayer()==killer then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"You have slain |cffffcc00"+GetPlayerName(tP)+"|r!")
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"|cff999999"+GetPlayerName(killer)+"|r has slain |cff999999"+GetPlayerName(tP)+"|r!")
endif
if GetUnitTypeId(kU)==leeSinConsts_LEE_SIN_ID then
call SetHeroLevel(kU,GetHeroLevel(kU)+1,true)
endif
set kU=null
call CreateItem(VISION_WARD,GetUnitX(tU),GetUnitY(tU))
set time=CreateTimer()
set r=ridiculous.create()
set r.u=tU
set tab[time]=r
call TimerStart(time,RESPAWN_TIME,false,function after)
endif
set tU=null
return false
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
set tab=HandleTable.create()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.1
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht = InitHashtable()
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//* API:
//* boolean ADD_ALL_UNITS: If enabled, a trigger turns on which automatically
//* registers all units in the map.
//* integer BUCKET_SIZE: How many units to add to each 'bucket' - a larger
//* bucket will have their trigger refresh less frequently but will be
//* more computationally expensive. A good starting value is about 20.
//* real PER_CLEANUP_TIMEOUT: How many seconds to wait in between each
//* scan for empty buckets. This value should be lower if units die often
//* in your map. A good starting value is about 60.
//* static method addHandler: Registers a callback function to the generic
//* unit damage event. Example: call StructuredDD.addHandler(function h)
//* static method add: Adds a unit to a bucket. If ADD_ALL_UNITS is enabled,
//* this method need not be used.
library StructuredDD
globals
//<< BEGIN SETTINGS SECTION
//* Set this to true if you want all units in your map to be
//* automatically added to StructuredDD. Otherwise you will have to
//* manually add them with StructuredDD.add(u).
private constant boolean ADD_ALL_UNITS=true
//* This is the amount of units that exist in each trigger bucket.
//* This number should be something between 5 and 30. A good starting
//* value will be an estimate of your map's average count of units,
//* divided by 10. When in doubt, just use 20.
private constant integer BUCKET_SIZE=20
//* This is how often StructuredDD will search for empty buckets. If
//* your map has units being created and dying often, a lower value
//* is better. Anything between 10 and 180 is good. When in doubt,
//* just use 60.
private constant real PER_CLEANUP_TIMEOUT=60.
//>> END SETTINGS SECTION
endglobals
//* Our bucket struct which contains a trigger and its associated contents.
private struct bucket
integer bucketIndex=0
trigger trig=CreateTrigger()
unit array members[BUCKET_SIZE]
endstruct
//* Our wrapper struct. We never intend to actually instanciate "a
//* StructuredDD", we just use this for a pretty, java-like API :3
struct StructuredDD extends array
private static boolexpr array conditions
private static bucket array bucketDB
private static integer conditionsIndex=-1
private static integer dbIndex=-1
private static integer maxDBIndex=-1
//* This method gets a readily available bucket for a unit to be added.
//* If the "current" bucket is full, it returns a new one, otherwise
//* it just returns the current bucket.
private static method getBucket takes nothing returns integer
local integer index=0
local integer returner=-1
local bucket tempDat
if thistype.dbIndex!=-1 and thistype.bucketDB[thistype.dbIndex].bucketIndex<BUCKET_SIZE then
return thistype.dbIndex
else
set thistype.maxDBIndex=thistype.maxDBIndex+1
set thistype.dbIndex=thistype.maxDBIndex
set tempDat=bucket.create()
set thistype.bucketDB[.maxDBIndex]=tempDat
loop
exitwhen index>thistype.conditionsIndex
call TriggerAddCondition(tempDat.trig,thistype.conditions[index])
set index=index+1
endloop
return thistype.dbIndex
endif
return -1
endmethod
//* This method is for adding a handler to the system. Whenever a
//* handler is added, damage detection will immediately trigger that
//* handler. There is no way to deallocate a handler, so don't try to
//* do this dynamically (!) Support for handler deallocation is
//* feasible (please contact me)
public static method addHandler takes code func returns nothing
local bucket tempDat
local integer index=0
set thistype.conditionsIndex=thistype.conditionsIndex+1
set thistype.conditions[thistype.conditionsIndex]=Condition(func)
loop
exitwhen index>thistype.maxDBIndex
set tempDat=thistype.bucketDB[index]
call TriggerAddCondition(tempDat.trig,thistype.conditions[thistype.conditionsIndex])
set index=index+1
endloop
endmethod
//* This method adds a unit to the damage detection system. If
//* ADD_ALL_UNITS is enabled, this method need not be used.
public static method add takes unit member returns nothing
local bucket tempDat
local integer whichBucket=thistype.getBucket()
set tempDat=thistype.bucketDB[whichBucket]
set tempDat.bucketIndex=tempDat.bucketIndex+1
set tempDat.members[tempDat.bucketIndex]=member
call TriggerRegisterUnitEvent(tempDat.trig,member,EVENT_UNIT_DAMAGED)
endmethod
//* This is just an auxillary function for ADD_ALL_UNITS' implementation
static if ADD_ALL_UNITS then
private static method autoAddC takes nothing returns boolean
call thistype.add(GetTriggerUnit())
return false
endmethod
endif
//* This method is used to check if a given bucket is empty (and thus
//* can be deallocated) - this is an auxillary reoutine for the
//* periodic cleanup system.
private static method bucketIsEmpty takes integer which returns boolean
local bucket tempDat=thistype.bucketDB[which]
local integer index=0
loop
exitwhen index==BUCKET_SIZE
//GetUnitTypeId(unit)==0 means that the unit has been removed.
if GetUnitTypeId(tempDat.members[index])!=0 then
return false
endif
set index=index+1
endloop
return true
endmethod
//* This method cleans up any empty buckets periodically by checking
//* if it has been fully allocated and then checking if all its
//* members no longer exist.
private static method perCleanup takes nothing returns nothing
local integer index=0
loop
exitwhen index>thistype.maxDBIndex
if index!=thistype.dbIndex and thistype.bucketIsEmpty(index) then
call DestroyTrigger(thistype.bucketDB[index].trig)
call thistype.bucketDB[index].destroy()
set thistype.bucketDB[index]=thistype.bucketDB[thistype.maxDBIndex]
set thistype.maxDBIndex=thistype.maxDBIndex-1
if thistype.maxDBIndex==thistype.dbIndex then
set thistype.dbIndex=index
endif
set index=index-1
endif
set index=index+1
endloop
endmethod
//* This is a initialization function necessary for the setup of
//* StructuredDD.
private static method onInit takes nothing returns nothing
local group grp
local region reg
local trigger autoAddUnits
local timer perCleanup
local unit FoG
static if ADD_ALL_UNITS then
//Add starting units
set grp=CreateGroup()
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
call thistype.add(FoG)
call GroupRemoveUnit(grp,FoG)
endloop
//Add entering units
set autoAddUnits=CreateTrigger()
set reg=CreateRegion()
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(autoAddUnits,reg,null)
call TriggerAddCondition(autoAddUnits,Condition(function thistype.autoAddC))
set autoAddUnits=null
set reg=null
endif
//enable periodic cleanup:
set perCleanup=CreateTimer()
call TimerStart(perCleanup,PER_CLEANUP_TIMEOUT,true,function thistype.perCleanup)
set perCleanup=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//* API
//* DamageType.NULLED Value returned when DamageType cannot conclusively
//* recognize damage type
//* DamageType.ATTACK Value returned when DamageType recognizes the
//* damage type as a physical attack
//* DamageType.SPELL Value returned when DamageType recognizes the
//* damage type as a spell
//* DamageType.CODE Value returned when DamageType recognizes the
//* damage type as having been dealt via dealCodeDamage()
//* DamageType.get() Requests what type of damage was dealt in the
//* scope of a StructuredDD handler
//* DamageType.dealCodeDamage() Deals an exact amount of damage to a unit. Arguments
//* are of the form (unit,unit,real) where the
//* first unit deals real damage to the second
//* unit.
library DamageType requires StructuredDD, Table
//* This struct contains data to deal damage to a target that would otherwise
//* die when hit with double damage in correction of spell damage recognition.
//* This is to counter the effect of a unit instantly healing some damage due
//* to negative spell damage, without using any unnecessary triggers.
private struct delayDat
unit target
unit source
real damage
endstruct
//* This is a struct shim which won't be instanciated; it is only useful for
//* making the pretty API like DamageType.doSomething()
struct DamageType
///< BEGIN CUSTOMIZE SECTION
//* One limitation of the spell damage reduction ability is that if a unit
//* is meant to receive bonus damage due to the damage table, such that the
//* result is greater than 100% damage, the unit would incorrectly take
//* damage capped at 100%. Using standard melee armor tables, this can only
//* occur when a damaged unit is ethereal; a simple ethereal shim no more
//* than 20 lines can be created, but for maps with custom damage tables,
//* this can be a useful option. If USE_BONUS_CALCULATOR is true, units that
//* would take greater than 100% spell damage will properly exhibit such
//* properly. (Disable this feature to improve performance)
private static constant boolean USE_BONUS_CALCULATOR=true
//* Make sure this matches the rawcode/id of your version of the DamageTypeCheck
//* ability (you must copy/paste it from the editor to your map)
private static constant integer DAMAGE_TYPE_CHECK_ID='A000'
///> END CUSTOMIZE SECTION
public static constant integer NULLED=-1
public static constant integer ATTACK= 0
public static constant integer SPELL = 1
public static constant integer CODE = 2
//ConvertAttackType(7) is a hidden attack type with some unique properties
//that make it very useful for us. For more information see goo.gl/9k8tn
private static constant attacktype ATTACK_TYPE_UNIVERSAL=ConvertAttackType(7)
//How long to delay before dealing the second half of the "correction" damage.
//Significant tests have shown that a delay of 0. will have no issues.
private static constant real DELAY_AMOUNT=0.
private static integer lastDamageType=thistype.NULLED
private static HandleTable delayed
//* Use this to get damage type in a damage event handler.
public static method get takes nothing returns integer
local real sourceDamage=GetEventDamage()
if thistype.lastDamageType==thistype.CODE then
return thistype.CODE
elseif sourceDamage>0. then
return thistype.ATTACK
elseif sourceDamage<0. then
return thistype.SPELL
endif
return thistype.NULLED
endmethod
//* Use this to damage units by trigger in your map without causing an infinite
//* loop.
public static method dealCodeDamage takes unit who, unit target, real damage returns nothing
local integer prevType=thistype.lastDamageType
local real hp=GetWidgetLife(target)-.405
local real d=damage
set thistype.lastDamageType=thistype.CODE
if hp>d then
call SetWidgetLife(target,hp-d+.405)
call UnitDamageTarget(who,target,0.,true,false,thistype.ATTACK_TYPE_UNIVERSAL,DAMAGE_TYPE_UNIVERSAL,null)
else
call UnitDamageTarget(who,target,1000000.+d,true,false,thistype.ATTACK_TYPE_UNIVERSAL,DAMAGE_TYPE_UNIVERSAL,null)
//Also deal magic damage for the special (unmodifiable) case of ethereal
//units.
call UnitDamageTarget(who,target,1000000.+d,true,false,ATTACK_TYPE_MAGIC,DAMAGE_TYPE_UNIVERSAL,null)
endif
set thistype.lastDamageType=prevType
endmethod
//* Auxillary function for adding the dummy ability to all units.
private static method c takes nothing returns boolean
local unit tU=GetTriggerUnit()
call UnitAddAbility(tU,thistype.DAMAGE_TYPE_CHECK_ID)
call UnitMakeAbilityPermanent(tU,true,thistype.DAMAGE_TYPE_CHECK_ID)
set tU=null
return false
endmethod
//* This is the function that occurs 0 seconds after damaging a unit, for the
//* case that damaging it by twice as much at the same time would kill it
//* unexpectedly.
private static method after takes nothing returns nothing
local timer time=GetExpiredTimer()
local delayDat tempDat=thistype.delayed[time]
call thistype.dealCodeDamage(tempDat.source,tempDat.target,tempDat.damage)
call thistype.delayed.flush(time)
call tempDat.destroy()
call DestroyTimer(time)
set time=null
endmethod
private static method getUnitBonusSpellResistance takes unit u returns real
local integer prevType=thistype.lastDamageType
local real life=GetWidgetLife(u)
local real scale=GetUnitState(u,UNIT_STATE_MAX_LIFE)
call SetWidgetLife(u,scale)
set thistype.lastDamageType=thistype.CODE
call UnitDamageTarget(u,u,-scale/2.,false,false,null,DAMAGE_TYPE_UNIVERSAL,null)
set scale=2.*(scale-GetWidgetLife(u))/scale
call SetWidgetLife(u,life)
set thistype.lastDamageType=prevType
return scale
endmethod
//* This is the method that will invert any negative spell damage. It MUST be
//* StructuredDD.conditions[0] to function properly; thus, any library using a
//* StructuredDD handler should 'requires DamageType' (!)
private static method handler takes nothing returns nothing
local timer time
local delayDat tempDat
local real attemptedDamage=-GetEventDamage()
local unit tU
local real sampledLife
local real scale
if thistype.get()==thistype.SPELL then
set tU=GetTriggerUnit()
static if thistype.USE_BONUS_CALCULATOR then
set scale=thistype.getUnitBonusSpellResistance(tU)
if scale>1. then
set attemptedDamage=attemptedDamage*(scale+1.)/2.
endif
endif
set sampledLife=GetWidgetLife(tU)-.405
if sampledLife>=attemptedDamage and sampledLife<=2.*attemptedDamage then
call SetWidgetLife(tU,sampledLife-attemptedDamage)
set time=CreateTimer()
set tempDat=delayDat.create()
set tempDat.target=tU
set tempDat.source=GetEventDamageSource()
set tempDat.damage=attemptedDamage
set thistype.delayed[time]=tempDat
call TimerStart(time,DELAY_AMOUNT,false,function thistype.after)
set time=null
else
call thistype.dealCodeDamage(GetEventDamageSource(),tU,2.*attemptedDamage)
endif
set tU=null
endif
endmethod
//* Initialization method to enable the system.
private static method onInit takes nothing returns nothing
local group grp=CreateGroup()
local region reg=CreateRegion()
local trigger addBracer=CreateTrigger()
local unit FoG
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(addBracer,reg,null)
call TriggerAddCondition(addBracer,Condition(function thistype.c))
call StructuredDD.addHandler(function thistype.handler)
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
call UnitAddAbility(FoG,thistype.DAMAGE_TYPE_CHECK_ID)
call UnitMakeAbilityPermanent(FoG,true,thistype.DAMAGE_TYPE_CHECK_ID)
call GroupRemoveUnit(grp,FoG)
endloop
set thistype.delayed=HandleTable.create()
call DestroyGroup(grp)
set grp=null
set reg=null
set addBracer=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=164
//TESH.alwaysfold=0
//* *API*
//* real TIMER_PERIOD: This value refers to how often Shield should search
//* for instances that are expired in regards to their
//* duration. A smaller value means that shields are less
//* likely to block damage when their duration has passed,
//* while a larger value is less computationally expensive.
//* A good value lies on [1./60.,1./2.].
//* static method add: This is the one and only important method for
//* instanciating a Shield. The arguments are of the
//* form (unit u, real a, real t, string f, string p)
//* which imparts a shield of size 'a' for time 't' on
//* 'u' using effect with model 'f' on attachment
//* point 'p'.
library Shield requires DamageType
//* We must declare the UnitAlive native because StructuredDD no longer
//* declares it for us :) UnitAlive is proven to have less computational
//* cost compared to IsUnitType(u,UNIT_TYPE_DEAD)
native UnitAlive takes unit u returns boolean
//* This struct is just a shim for a pretty API. (You don't instanciate
//* Shield objects yourself)
struct Shield
//< BEGIN CUSTOMIZABLE SECTION
//* How often to check for shield instances with expired durations. A
//* smaller value will reduce the likelihood of a unit blocking damage
//* with a shield that would otherwise be expired, at the cost of
//* performance. A good value is between 1./30. and 1./2.
private static constant real TIMER_PERIOD=1./10.
//> END CUSTOMIZABLE SECTION
private static thistype array allShields
private static integer shieldIndex=-1
private static timer time=CreateTimer()
private static HandleTable tab
private boolean useDuration=true
private boolean finiteShield=true
private unit u
private real shieldLeft
private real falseHP
private real timeLeft
private effect fx
private thistype prevShield=0
private thistype nextShield=0
//* This method is used for handling when a unit with a shield
//* receives damage.
private static method handler takes nothing returns nothing
local thistype tempDat
local thistype destroyingDat
local unit tU=GetTriggerUnit()
local real damageLeft=GetEventDamage()
local boolean noShieldsLeft=false
if DamageType.get()!=DamageType.SPELL and UnitAlive(tU) and tab.exists(tU) then
set tempDat=thistype.tab[tU]
//we need to loop until either all the damage is mitigated
//or there's no shield left on this unit.
loop
exitwhen damageLeft<=0. or noShieldsLeft
if tempDat.shieldLeft>damageLeft then
if tempDat.finiteShield then
set tempDat.shieldLeft=tempDat.shieldLeft-damageLeft
endif
call SetWidgetLife(tempDat.u,GetWidgetLife(tempDat.u)+damageLeft)
set damageLeft=0.
else
set damageLeft=damageLeft-tempDat.shieldLeft
call SetWidgetLife(tempDat.u,GetWidgetLife(tempDat.u)+tempDat.shieldLeft)
set tempDat.shieldLeft=0.
if tempDat.falseHP>damageLeft then
set tempDat.falseHP=tempDat.falseHP-damageLeft
set damageLeft=0.
else
set damageLeft=damageLeft-tempDat.falseHP
set tempDat.falseHP=0.
set destroyingDat=tempDat
if destroyingDat.nextShield!=0 then
set tempDat=destroyingDat.nextShield
set tempDat.prevShield=0
set thistype.tab[tempDat.u]=tempDat
else
set noShieldsLeft=true
endif
set destroyingDat.nextShield=0
endif
endif
endloop
endif
set tU=null
endmethod
//* This method runs every TIMER_PERIOD seconds as long as the stack
//* is not empty, and looks for members that need to be destroyed.
private static method timeout takes nothing returns nothing
local integer index=0
local thistype tempDat
local thistype otherDat
loop
exitwhen index>thistype.shieldIndex
set tempDat=thistype.allShields[index]
set tempDat.timeLeft=tempDat.timeLeft-TIMER_PERIOD
if (tempDat.timeLeft<=0. and tempDat.useDuration) or (tempDat.shieldLeft+tempDat.falseHP)<=0. or UnitAlive(tempDat.u)==false then
//deallocate it, depending on the values of prevShield
//and nextShield
if tempDat.prevShield==0 and tempDat.nextShield==0 and thistype.tab[tempDat.u]==tempDat then
//this was the only shield left on the unit so:
call thistype.tab.flush(tempDat.u)
elseif tempDat.prevShield==0 and tempDat.nextShield!=0 then
//the shield was the first in a list so we have to
//make next into the new first!
set otherDat=tempDat.nextShield
set otherDat.prevShield=0
set thistype.tab[tempDat.u]=otherDat
elseif tempDat.prevShield!=0 and tempDat.nextShield==0 then
//the shield was the end of the list so let's do the
//proper changes to the previous member.
set otherDat=tempDat.prevShield
set otherDat.nextShield=0
elseif tempDat.prevShield!=0 and tempDat.nextShield!=0 then
//this shield was somewhere in the middle of the list
//so let's make the prev and next link to each other
set otherDat=tempDat.prevShield
set otherDat.nextShield=tempDat.nextShield
set otherDat=tempDat.nextShield
set otherDat.prevShield=tempDat.prevShield
endif
//let's reset the unit's hp if he has extra
if tempDat.falseHP>0. then
call SetWidgetLife(tempDat.u,GetWidgetLife(tempDat.u)-tempDat.falseHP)
endif
call DestroyEffect(tempDat.fx)
call tempDat.destroy()
set thistype.allShields[index]=thistype.allShields[thistype.shieldIndex]
set thistype.shieldIndex=thistype.shieldIndex-1
set index=index-1
if thistype.shieldIndex==-1 then
call PauseTimer(thistype.time)
endif
endif
set index=index+1
endloop
endmethod
//* This method adds a shield to a unit. You can use
//* Shield.DEFAULT_EFFECT in the fx argument.
public static method add takes unit u, real amount, real time, string fx, string fxpoint returns nothing
local thistype tempDat=thistype.create()
local thistype linkedDat
local real initialLife=GetWidgetLife(u)
local real maxLife=GetUnitState(u,UNIT_STATE_MAX_LIFE)
local real diff=maxLife-initialLife
set tempDat.u=u
set tempDat.timeLeft=time
set tempDat.fx=AddSpecialEffectTarget(fx,u,fxpoint)
if time==-1. then
set tempDat.useDuration=false
endif
if amount==-1. then
set amount=100000.
set tempDat.finiteShield=false
endif
//figure out how to partition the damage
if diff>=amount then
//the unit has plenty of missing HP, so:
set tempDat.shieldLeft=0
set tempDat.falseHP=amount
call SetWidgetLife(u,initialLife+amount)
else
//the unit has less hp missing than the shield has value, so:
call SetWidgetLife(u,maxLife)
set tempDat.falseHP=diff
set tempDat.shieldLeft=amount-diff
endif
//Now let's figure out if the unit already has a shield - if he
//does, we add this shield to the front:
if thistype.tab.exists(u) then
set linkedDat=thistype.tab[u]
set tempDat.nextShield=linkedDat
set linkedDat.prevShield=tempDat
endif
set thistype.tab[u]=tempDat
//now let's add this shield to the stack so it can be properly
//deallocated when its time runs out:
set thistype.shieldIndex=thistype.shieldIndex+1
set thistype.allShields[thistype.shieldIndex]=tempDat
//if allShields was previously empty we have to jumpstart the timer:
if thistype.shieldIndex==0 then
call TimerStart(thistype.time,thistype.TIMER_PERIOD,true,function thistype.timeout)
endif
endmethod
//* Auxillary method used to initialize the HandleTable and
//* StructuredDD handler.
private static method onInit takes nothing returns nothing
set thistype.tab=HandleTable.create()
call StructuredDD.addHandler(function thistype.handler)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Thanks to anitarf and Vexorian @ wc3c.net for this library, it makes things easier.
library IsTerrainWalkable initializer Init
globals
// this value is how far from a point the item may end up for the point to be considered pathable
private constant real MAX_RANGE=10.
// the following two variables are set to the position of the item after each pathing check
// that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
public real X=0.
public real Y=0.
private rect r
private item check
private item array hidden
private integer hiddenMax=0
endglobals
private function Init takes nothing returns nothing
set check=CreateItem('ciri',0,0)
call SetItemVisible(check,false)
set r=Rect(0.0,0.0,128.0,128.0)
endfunction
private function HideBothersomeItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set hidden[hiddenMax]=GetEnumItem()
call SetItemVisible(hidden[hiddenMax],false)
set hiddenMax=hiddenMax+1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
// first, hide any items in the area so they don't get in the way of our item
call MoveRectTo(r,x,y)
call EnumItemsInRect(r,null,function HideBothersomeItem)
// try to move the check item and get it's coordinates
call SetItemPosition(check,x,y)//this unhides the item...
set X=GetItemX(check)
set Y=GetItemY(check)
call SetItemVisible(check,false)//...so we must hide it again
// before returning, unhide any items that got hidden at the start
loop
exitwhen hiddenMax<=0
set hiddenMax=hiddenMax-1
call SetItemVisible(hidden[hiddenMax],true)
set hidden[hiddenMax]=null
endloop
// return pathability status
return (x-X)*(x-X)+(y-Y)*(y-Y)<MAX_RANGE*MAX_RANGE
endfunction
endlibrary
//TESH.scrollpos=166
//TESH.alwaysfold=0
//* 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
//TESH.scrollpos=6
//TESH.alwaysfold=0
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
//TESH.scrollpos=-1
//TESH.alwaysfold=0
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
//TESH.scrollpos=179
//TESH.alwaysfold=0
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
//TESH.scrollpos=124
//TESH.alwaysfold=0
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
//TESH.scrollpos=131
//TESH.alwaysfold=0
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