Procrastination Incarnate
Development Director
Join Date: Feb 2004
Posts: 8,190
|
Demon Huntress Spellpack
|
This was my and TDR's submission to the third Hero Contest, I figure it's about time I submitted it. The pack consists of four contest abilities and one bonus ability that wasn't included originally because of the four hero skill limit, for their descriptions read the documentation included with their code or download the testmap and try them out.
The spells require the following libraries (not every spell requires all of them):
TimerUtils, Table, SpellEvent, VectorLib, PruneGroup, IsTerrainWalkable, IsUnitSpellResistant, BoundSentinel, xe, ABuff |
The abilities in this pack are:  Pounce: scope Pounce initializer Init
globals
private constant integer SPELL_ABILITY = 'A00A'
private constant integer UPG_SPELL_ABILITY = 'A00C'
private constant real UPG_DAMAGE_FACTOR = 2.0
private constant real UPG_DURATION_FACTOR = 2.0
private constant real LEAP_SPEED = 2000.0
private constant real PUSH_SPEED = 1000.0
private constant real SLOWDOWN_TIME = 0.25
private constant real HIT_RANGE = 100.0
private constant integer HIT_ANIMATION_ID = 0
endglobals
private constant function PushDistance takes integer level returns real
return 200.0+100.0*level
endfunction
private constant function Damage takes integer level returns real
return 25.0+25.0*level
endfunction
private constant function DurationHero takes integer level returns real
return 1.0*level
endfunction
private constant function DurationUnit takes integer level returns real
return 1.0*(1+level)
endfunction
private function DamageOptions takes xedamage spellDamage returns nothing
set spellDamage.dtype=DAMAGE_TYPE_UNIVERSAL
set spellDamage.atype=ATTACK_TYPE_NORMAL
set spellDamage.tag=0
endfunction
globals
private xedamage xed
endglobals
private struct si
unit caster
unit target
integer level
boolean impact
boolean interrupted
boolean upgraded
real distance
real x
real y
private integer index
private static si array loopList
private static integer loopCount=0
private static timer tim
static method get takes unit u returns si
local integer i=0
loop
exitwhen i==si.loopCount
if si.loopList[i].caster==u then
return si.loopList[i]
endif
set i=i+1
endloop
return 0
endmethod
static method periodic takes nothing returns nothing
local integer i=si.loopCount-1
local real tx
local real ty
local real dx
local real dy
local real ang
local si this
loop
exitwhen i<0
set this=si.loopList[i]
set tx=GetUnitX(.target)
set ty=GetUnitY(.target)
set dx=tx-.x
set dy=ty-.y
set ang=Atan2(dy,dx)
if .impact then
set dx=PUSH_SPEED*XE_ANIMATION_PERIOD*Cos(ang)
set dy=PUSH_SPEED*XE_ANIMATION_PERIOD*Sin(ang)
if .interrupted then
set .distance=.distance-XE_ANIMATION_PERIOD/SLOWDOWN_TIME
set dx=dx*.distance
set dy=dy*.distance
if .distance<=0 then
call .destroy()
endif
else
set .distance=.distance-PUSH_SPEED*XE_ANIMATION_PERIOD
if .distance<=0 then
if SLOWDOWN_TIME==0 then
call .destroy()
else
set .distance=1.0
call IssueImmediateOrder(.caster, "stop")
set .interrupted=true
endif
endif
endif
if IsTerrainWalkable(tx+dx,ty+dy) then
call SetUnitX(.target, tx+dx)
call SetUnitY(.target, ty+dy)
if .interrupted then
set .x=GetUnitX(.caster)+dx
set .y=GetUnitY(.caster)+dy
else
set .x=.x+dx
set .y=.y+dy
endif
call SetUnitX(.caster, .x)
call SetUnitY(.caster, .y)
endif
else
set dx=LEAP_SPEED*XE_ANIMATION_PERIOD*Cos(ang)
set dy=LEAP_SPEED*XE_ANIMATION_PERIOD*Sin(ang)
if .interrupted then
set .distance=.distance-XE_ANIMATION_PERIOD/SLOWDOWN_TIME
set dx=dx*.distance
set dy=dy*.distance
if .distance<=0 then
call .destroy()
endif
set .x=GetUnitX(.caster)+dx
set .y=GetUnitY(.caster)+dy
else
set .x=.x+dx
set .y=.y+dy
endif
call SetUnitX(.caster, .x)
call SetUnitY(.caster, .y)
if IsUnitInRangeXY(.target, .x,.y, HIT_RANGE) then
if this.upgraded then
set tx=UPG_DAMAGE_FACTOR
set ty=UPG_DURATION_FACTOR
else
set tx=1.0
set ty=1.0
endif
call SetUnitAnimationByIndex(.caster, HIT_ANIMATION_ID)
call xed.damageTarget(.caster, .target, Damage(.level)*tx)
if IsUnitSpellResistant(.target) then
call UnitStun(.target, .caster, DurationHero(.level)*ty)
else
call UnitStun(.target, .caster, DurationUnit(.level)*ty)
endif
set .impact=true
endif
endif
set i=i-1
endloop
endmethod
method interrupt takes nothing returns nothing
set .interrupted=true
set .distance=1.0
endmethod
static method create takes unit caster, integer level, boolean upgraded, unit target returns si
local si this=si.allocate()
set .caster=caster
set .target=target
set .level=level
set .upgraded=upgraded
set .impact=false
set .interrupted=false
set .distance=PushDistance(level)-SLOWDOWN_TIME*PUSH_SPEED/2
set .x=GetUnitX(caster)
set .y=GetUnitY(caster)
if si.loopCount==0 then
call TimerStart(si.tim, XE_ANIMATION_PERIOD, true, function si.periodic)
endif
set si.loopList[si.loopCount]=this
set .index=si.loopCount
set si.loopCount=si.loopCount+1
return this
endmethod
method onDestroy takes nothing returns nothing
set si.loopCount=si.loopCount-1
set si.loopList[this.index]=si.loopList[si.loopCount]
set si.loopList[si.loopCount].index=this.index
if si.loopCount==0 then
call PauseTimer(si.tim)
endif
if not(this.interrupted) then
call IssueImmediateOrder(this.caster, "stop")
endif
endmethod
static method onInit takes nothing returns nothing
set si.tim=CreateTimer()
endmethod
endstruct
private function SpellEffect takes nothing returns nothing
local integer lvl = GetUnitAbilityLevel(SpellEvent.CastingUnit, SpellEvent.AbilityId)
call si.create(SpellEvent.CastingUnit, lvl, SpellEvent.AbilityId==UPG_SPELL_ABILITY, SpellEvent.TargetUnit)
endfunction
private function SpellStop takes nothing returns nothing
local si s = si.get(SpellEvent.CastingUnit)
if s != 0 then
call s.interrupt()
endif
endfunction
private function Init takes nothing returns nothing
call RegisterSpellEffectResponse(SPELL_ABILITY, SpellEffect)
call RegisterSpellEndCastResponse(SPELL_ABILITY, SpellStop)
if UPG_SPELL_ABILITY != 0 then
call RegisterSpellEffectResponse(UPG_SPELL_ABILITY, SpellEffect)
call RegisterSpellEndCastResponse(UPG_SPELL_ABILITY, SpellStop)
endif
set xed=xedamage.create()
call DamageOptions(xed)
endfunction
endscope
 Firestorm: scope Firestorm initializer Init
globals
private constant integer SPELL_ABILITY = 'A008'
private constant integer UPG_SPELL_ABILITY = 'A00B'
private constant real UPG_DAMAGE_FACTOR = 2.0
private constant real PROJECTILE_VELOCITY = 800
private constant real PROJECTILE_ROLL_RATE = 12
private constant real PROJECTILE_TURN_RATE = 12
private constant real PROJECTILE_TURNING_START = 0.4
private constant real PROJECTILE_COLLISION_SIZE = 32
private constant real PROJECTILE_IMPACT_Z = 60
private constant real PROJECTILE_TARGET_HITSIZE = 120
private constant real PROJECTILE_LAUNCH_X = 0
private constant real PROJECTILE_LAUNCH_Y = 0
private constant real PROJECTILE_LAUNCH_Z = 120
private constant real PROJECTILE_LAUNCH_MINANGLE = -180
private constant real PROJECTILE_LAUNCH_MAXANGLE = 180
private constant real PROJECTILE_LAUNCH_MINPITCH = 15
private constant real PROJECTILE_LAUNCH_MAXPITCH = 85
private constant real PROJECTILE_DURATION = 5.0
private constant real PROJECTILE_ACQUIRE_RANGE = 600
private constant boolean PROJECTILE_ACQUIRES_NEW_TARGETS=true
endglobals
private constant function SpellDuration takes integer level returns real
return 3.0
endfunction
private constant function ProjectileCount takes integer level returns integer
return 30
endfunction
private constant function Damage takes integer level returns real
return 5.0+level*5.0
endfunction
private function DamageOptions takes xedamage spellDamage returns nothing
set spellDamage.dtype=DAMAGE_TYPE_UNIVERSAL
set spellDamage.atype=ATTACK_TYPE_NORMAL
set spellDamage.tag=0
set spellDamage.exception=UNIT_TYPE_STRUCTURE
endfunction
globals
private location l = Location(0.0,0.0)
endglobals
private function GetTerrainZ takes real x, real y returns real
call MoveLocation(l, x, y)
return GetLocationZ(l)
endfunction
globals
private xedamage xed
endglobals
private struct p
private delegate xefx fx
vector position
vector facing
vector right
unit caster
player owner
real damage
unit target
real duration
private integer index
private static p array loopList
private static integer loopCount=0
private static timer tim
private static boolexpr bx
private static group g
private static integer picks
private static p temp
private static vector v
private static method targetEnum takes nothing returns boolean
if xed.allowedTarget(p.temp.caster, GetFilterUnit()) then
if GetRandomInt(0, p.picks)==0 then
set p.temp.target=GetFilterUnit()
endif
set p.picks=p.picks+1
endif
return false
endmethod
private method acquireTarget takes nothing returns nothing
set p.picks=0
set p.temp=this
set p.temp.target=null
call GroupEnumUnitsInRange(p.g,p.temp.position.x,p.temp.position.y,PROJECTILE_ACQUIRE_RANGE,p.bx)
if p.temp.target==null then
set p.temp.duration=PROJECTILE_DURATION
endif
endmethod
private method impact takes nothing returns nothing
call xed.damageTarget(.caster, .target, .damage)
endmethod
private static method periodic takes nothing returns nothing
local integer i=p.loopCount-1
local real r
loop
exitwhen i<0
set p.temp=p.loopList[i]
set p.temp.duration=p.temp.duration+XE_ANIMATION_PERIOD
if p.temp.duration<PROJECTILE_TURNING_START then
set r=p.temp.duration/PROJECTILE_TURNING_START
else
set r=1.0
endif
if PROJECTILE_ACQUIRES_NEW_TARGETS and p.temp.target!=null and GetWidgetLife(p.temp.target)<0.405 then
call p.temp.acquireTarget()
endif
if p.temp.target!=null then
set p.v.x=GetUnitX(p.temp.target)-p.temp.position.x
set p.v.y=GetUnitY(p.temp.target)-p.temp.position.y
set p.v.z=GetTerrainZ(GetUnitX(p.temp.target), GetUnitY(p.temp.target))+GetUnitFlyHeight(p.temp.target)+PROJECTILE_IMPACT_Z-p.temp.position.z
call p.temp.right.rotate(p.temp.facing, PROJECTILE_ROLL_RATE*XE_ANIMATION_PERIOD)
if vector.tripleProductScalar(p.temp.facing, p.temp.right, p.v)>0 then
call p.temp.facing.rotate(p.temp.right, -PROJECTILE_TURN_RATE*r*XE_ANIMATION_PERIOD)
else
call p.temp.facing.rotate(p.temp.right, PROJECTILE_TURN_RATE*r*XE_ANIMATION_PERIOD)
endif
endif
call p.temp.position.add(p.temp.facing)
set p.temp.x=p.temp.position.x
set p.temp.y=p.temp.position.y
set p.temp.z=p.temp.position.z-GetTerrainZ(p.temp.position.x, p.temp.position.y)
set p.temp.xyangle=Atan2(p.temp.facing.y, p.temp.facing.x)
set p.temp.zangle=Atan2(p.temp.facing.z, SquareRoot(p.temp.facing.x*p.temp.facing.x+p.temp.facing.y*p.temp.facing.y))
if IsUnitInRangeXY(p.temp.target,p.temp.position.x,p.temp.position.y,PROJECTILE_COLLISION_SIZE) and (p.v.z>-PROJECTILE_TARGET_HITSIZE/2 and p.v.z<PROJECTILE_TARGET_HITSIZE/2) then
call p.temp.impact()
call p.temp.destroy()
elseif p.temp.duration>=PROJECTILE_DURATION then
call p.temp.destroy()
endif
set i=i-1
endloop
endmethod
static method create takes unit caster, real damage, vector position, real facing, real pitch returns p
local p this=p.allocate()
set .caster=caster
set .owner=GetOwningPlayer(caster)
set .damage=damage
set .duration=0.0
set .fx=xefx.create(position.x,position.y,facing)
set .z=position.z-GetTerrainZ(position.x, position.y)
set .zangle=pitch
set .fxpath=GetAbilityEffectById(SPELL_ABILITY, EFFECT_TYPE_MISSILE, 0)
call .flash(GetAbilityEffectById(SPELL_ABILITY, EFFECT_TYPE_AREA_EFFECT, 0))
set .position=vector.create(position.x, position.y, position.z)
set .facing=vector.create(PROJECTILE_VELOCITY*XE_ANIMATION_PERIOD*Cos(pitch)*Cos(facing),PROJECTILE_VELOCITY*XE_ANIMATION_PERIOD*Cos(pitch)*Sin(facing),PROJECTILE_VELOCITY*XE_ANIMATION_PERIOD*Sin(pitch))
set .right=vector.create(128.0*Cos(pitch+bj_PI/2)*Cos(facing),128.0*Cos(pitch+bj_PI/2)*Sin(facing),128.0*Sin(pitch+bj_PI/2))
call .right.rotate(.facing, GetRandomReal(0,bj_PI*2))
call .acquireTarget()
if p.loopCount==0 then
set p.tim=NewTimer()
call TimerStart(p.tim, XE_ANIMATION_PERIOD, true, function p.periodic)
endif
set p.loopList[p.loopCount]=this
set .index=p.loopCount
set p.loopCount=p.loopCount+1
return this
endmethod
method onDestroy takes nothing returns nothing
set p.loopCount=p.loopCount-1
set p.loopList[this.index]=p.loopList[p.loopCount]
set p.loopList[p.loopCount].index=this.index
call .position.destroy()
call .facing.destroy()
call .right.destroy()
call .fx.destroy()
if p.loopCount==0 then
call ReleaseTimer(p.tim)
endif
endmethod
static method onInit takes nothing returns nothing
set p.g=CreateGroup()
set p.bx=Condition(function p.targetEnum)
set p.v=vector.create(0.0,0.0,0.0)
endmethod
endstruct
private struct si
private timer t
unit caster
integer level
integer projectiles
boolean upgraded
boolean interrupted
private integer index
private static si array loopList
private static integer loopCount=0
private static vector launch
static method get takes unit u returns si
local integer i=0
loop
exitwhen i==si.loopCount
if si.loopList[i].caster==u then
return si.loopList[i]
endif
set i=i+1
endloop
return 0
endmethod
static method update takes nothing returns nothing
local si this=si(GetTimerData(GetExpiredTimer()))
local real a=GetUnitFacing(.caster)*bj_DEGTORAD
set si.launch.x=GetUnitX(.caster)+Cos(a)*PROJECTILE_LAUNCH_X-Sin(a)*PROJECTILE_LAUNCH_Y
set si.launch.y=GetUnitY(.caster)+Cos(a)*PROJECTILE_LAUNCH_Y+Sin(a)*PROJECTILE_LAUNCH_X
set si.launch.z=GetTerrainZ(si.launch.x,si.launch.y)+PROJECTILE_LAUNCH_Z+GetUnitFlyHeight(.caster)
if .upgraded then
call p.create(.caster,Damage(.level)*UPG_DAMAGE_FACTOR, si.launch, GetRandomReal(PROJECTILE_LAUNCH_MINANGLE, PROJECTILE_LAUNCH_MAXANGLE)*bj_DEGTORAD, GetRandomReal(PROJECTILE_LAUNCH_MINPITCH, PROJECTILE_LAUNCH_MAXPITCH)*bj_DEGTORAD)
else
call p.create(.caster,Damage(.level), si.launch, GetRandomReal(PROJECTILE_LAUNCH_MINANGLE, PROJECTILE_LAUNCH_MAXANGLE)*bj_DEGTORAD, GetRandomReal(PROJECTILE_LAUNCH_MINPITCH, PROJECTILE_LAUNCH_MAXPITCH)*bj_DEGTORAD)
endif
set .projectiles=.projectiles-1
if .projectiles <=0 then
call .destroy()
endif
endmethod
static method create takes unit caster, integer level, boolean upgraded returns si
local si this=si.allocate()
set .caster=caster
set .level=level
set .upgraded=upgraded
set .interrupted=false
set .projectiles=ProjectileCount(level)
set .t=NewTimer()
call SetTimerData(.t, integer(this))
call TimerStart(.t, SpellDuration(level)/ProjectileCount(level), true, function si.update)
set si.loopList[si.loopCount]=this
set .index=si.loopCount
set si.loopCount=si.loopCount+1
return this
endmethod
method onDestroy takes nothing returns nothing
set si.loopCount=si.loopCount-1
set si.loopList[this.index]=si.loopList[si.loopCount]
set si.loopList[si.loopCount].index=this.index
call ReleaseTimer(this.t)
if not(this.interrupted) then
call IssueImmediateOrder(this.caster, "stop")
endif
endmethod
static method onInit takes nothing returns nothing
set si.launch=vector.create(0.0,0.0,0.0)
endmethod
endstruct
private function SpellEffect takes nothing returns nothing
local integer lvl = GetUnitAbilityLevel(SpellEvent.CastingUnit, SpellEvent.AbilityId)
call si.create(SpellEvent.CastingUnit, lvl, SpellEvent.AbilityId==UPG_SPELL_ABILITY)
endfunction
private function SpellStop takes nothing returns nothing
local si s = si.get(SpellEvent.CastingUnit)
if s != 0 then
set s.interrupted=true
call s.destroy()
endif
endfunction
private function Init takes nothing returns nothing
call RegisterSpellEffectResponse(SPELL_ABILITY, SpellEffect)
call RegisterSpellEndCastResponse(SPELL_ABILITY, SpellStop)
if UPG_SPELL_ABILITY != 0 then
call RegisterSpellEffectResponse(UPG_SPELL_ABILITY, SpellEffect)
call RegisterSpellEndCastResponse(UPG_SPELL_ABILITY, SpellStop)
endif
set xed=xedamage.create()
call DamageOptions(xed)
endfunction
endscope
 Soul Glaive: scope SoulGlaive initializer Init
globals
private constant integer SPELL_ABILITY = 'A000'
private constant integer UPG_SPELL_ABILITY = 'A00G'
private constant real UPG_DAMAGE_FACTOR = 2.0
private constant real UPG_MANABURN_FACTOR = 2.0
private constant real PROJECTILE_VELOCITY = 800
private constant real PROJECTILE_COLLISION_SIZE = 48
private constant real PROJECTILE_FACING_TWEAK_FACTOR = 45.0/1000.0
private constant real PROJECTILE_IMPACT_Z = 60
private constant real PROJECTILE_LAUNCH_X = 0
private constant real PROJECTILE_LAUNCH_Y = 30
private constant real PROJECTILE_LAUNCH_Z = 60
private constant boolean CAN_BOUNCE_TO_SAME_TARGET = false
private constant boolean CAN_BOUNCE_TO_PREVIOUS_TARGETS = false
private constant real DISTANCE_MODIFIER_MANA = -500.0
private constant real DISTANCE_MODIFIER_SAME = 2000.0
private constant real DISTANCE_MODIFIER_PREVIOUS = 1000.0
endglobals
private constant function TargetsHit takes integer level returns integer
return level
endfunction
private constant function Damage takes integer level returns real
return (25.0 + 25.0*level)
endfunction
private constant function ManaBurned takes integer level returns real
return 50.0
endfunction
private constant function DamagePerManaPoint takes integer level returns real
return 1.0
endfunction
private constant function BounceRange takes integer level returns real
return 500.0
endfunction
private function DamageOptions takes xedamage spellDamage returns nothing
set spellDamage.dtype=DAMAGE_TYPE_UNIVERSAL
set spellDamage.atype=ATTACK_TYPE_NORMAL
set spellDamage.tag=0
set spellDamage.exception=UNIT_TYPE_STRUCTURE
endfunction
globals
private xedamage xed
endglobals
private keyword p
private function Fitness takes unit u returns real
local real x = GetUnitX(u)-p.temp.x
local real y = GetUnitY(u)-p.temp.y
local real fit = -SquareRoot(x*x+y*y)
if GetUnitState(u, UNIT_STATE_MAX_MANA)>0.0 then
set fit=fit-DISTANCE_MODIFIER_MANA
endif
if u==p.temp.target then
set fit=fit-DISTANCE_MODIFIER_SAME
elseif IsUnitInGroup(u, p.g) then
set fit=fit-DISTANCE_MODIFIER_PREVIOUS
endif
return fit
endfunction
private struct p
private delegate xefx fx
unit caster
player owner
integer level
boolean upgraded
unit target
group affected
integer hits
private integer index
private static p array loopList
private static integer loopCount=0
private static timer tim
private static boolexpr bx
static group g
static p temp
private static method targetEnum takes nothing returns boolean
local unit u=GetFilterUnit()
local boolean b=xed.allowedTarget(p.temp.caster, u) and (CAN_BOUNCE_TO_SAME_TARGET or p.temp.target!=u) and (CAN_BOUNCE_TO_PREVIOUS_TARGETS or not(IsUnitInGroup(u, p.temp.affected)))
set u=null
return b
endmethod
private method acquireTarget takes nothing returns nothing
set p.temp=this
call GroupEnumUnitsInRange(p.g,.x,.y,BounceRange(.level),p.bx)
call PruneGroup(p.g, Fitness, 1, NO_FITNESS_LIMIT)
call GroupAddUnit(.affected, .target)
set .target=FirstOfGroup(p.g)
call GroupClear(p.g)
endmethod
private method impact takes nothing returns nothing
local real m
local real r = 0.0
if GetUnitState(.target, UNIT_STATE_MAX_MANA)>0.0 then
set m=GetUnitState(.target, UNIT_STATE_MANA)
set r=ManaBurned(.level)
if .upgraded then
set r=r*UPG_MANABURN_FACTOR
endif
if r>m then
set r=m
endif
call SetUnitState(.target, UNIT_STATE_MANA, m-r)
set r=r*DamagePerManaPoint(.level)
call DestroyEffect(AddSpecialEffectTarget(GetAbilityEffectById(SPELL_ABILITY, EFFECT_TYPE_TARGET, 1), .target, "origin"))
else
call DestroyEffect(AddSpecialEffectTarget(GetAbilityEffectById(SPELL_ABILITY, EFFECT_TYPE_TARGET, 0), .target, "origin"))
endif
if .upgraded then
set r=r+(Damage(.level)*UPG_DAMAGE_FACTOR)
else
set r=r+Damage(.level)
endif
call xed.damageTarget(.caster, .target, r)
endmethod
private static method periodic takes nothing returns nothing
local integer i=p.loopCount-1
local real dx
local real dy
local real dz
local real angle
local real pitch
local real dist
loop
exitwhen i<0
set p.temp=p.loopList[i]
set dx=GetUnitX(p.temp.target)-p.temp.x
set dy=GetUnitY(p.temp.target)-p.temp.y
set dz=GetUnitFlyHeight(p.temp.target)+PROJECTILE_IMPACT_Z-p.temp.z
set angle=Atan2(dy,dx)
set dist=SquareRoot(dx*dx+dy*dy)
set pitch=Atan2(dz,dist)
if dist!=0.0 then
set dz=dz*PROJECTILE_VELOCITY*XE_ANIMATION_PERIOD/dist
else
set dz=0.0
endif
set dist=dist*PROJECTILE_FACING_TWEAK_FACTOR*bj_DEGTORAD
if (p.temp.hits/2)*2==p.temp.hits then
set angle=angle+dist
else
set angle=angle-dist
endif
set p.temp.x=p.temp.x+PROJECTILE_VELOCITY*XE_ANIMATION_PERIOD*Cos(angle)
set p.temp.y=p.temp.y+PROJECTILE_VELOCITY*XE_ANIMATION_PERIOD*Sin(angle)
set p.temp.z=p.temp.z+dz
set p.temp.xyangle=angle
set p.temp.zangle=pitch
if IsUnitInRangeXY(p.temp.target,p.temp.x,p.temp.y,PROJECTILE_COLLISION_SIZE) then
call p.temp.impact()
set p.temp.hits=p.temp.hits+1
if p.temp.hits>=TargetsHit(p.temp.level) then
call p.temp.destroy()
else
call p.temp.acquireTarget()
if p.temp.target==null then
call p.temp.destroy()
endif
endif
endif
set i=i-1
endloop
endmethod
static method create takes unit caster, integer level, boolean upgraded, unit target returns p
local p this=p.allocate()
local real a=GetUnitFacing(.caster)*bj_DEGTORAD
local real x=GetUnitX(caster)+Cos(a)*PROJECTILE_LAUNCH_X-Sin(a)*PROJECTILE_LAUNCH_Y
local real y=GetUnitY(caster)+Cos(a)*PROJECTILE_LAUNCH_Y+Sin(a)*PROJECTILE_LAUNCH_X
local real dx=GetUnitX(target)-x
local real dy=GetUnitY(target)-y
set a=Atan2(dy,dx)+SquareRoot(dx*dx+dy*dy)*PROJECTILE_FACING_TWEAK_FACTOR*bj_DEGTORAD
set .caster=caster
set .owner=GetOwningPlayer(caster)
set .target=target
set .level=level
set .upgraded=upgraded
set .hits=0
set .fx=xefx.create(x,y,a)
set .z=PROJECTILE_LAUNCH_Z
set .fxpath=GetAbilityEffectById(SPELL_ABILITY, EFFECT_TYPE_MISSILE, 0)
if p.loopCount==0 then
call TimerStart(p.tim, XE_ANIMATION_PERIOD, true, function p.periodic)
endif
if .affected == null then
set .affected=CreateGroup()
endif
set p.loopList[p.loopCount]=this
set .index=p.loopCount
set p.loopCount=p.loopCount+1
return this
endmethod
method onDestroy takes nothing returns nothing
set p.loopCount=p.loopCount-1
set p.loopList[this.index]=p.loopList[p.loopCount]
set p.loopList[p.loopCount].index=this.index
call .fx.destroy()
call GroupClear(.affected)
if p.loopCount==0 then
call PauseTimer(p.tim)
endif
endmethod
static method onInit takes nothing returns nothing
set p.tim=CreateTimer()
set p.g=CreateGroup()
set p.bx=Condition(function p.targetEnum)
endmethod
endstruct
private function SpellEffect takes nothing returns nothing
local integer lvl = GetUnitAbilityLevel(SpellEvent.CastingUnit, SpellEvent.AbilityId)
call p.create(SpellEvent.CastingUnit, lvl, SpellEvent.AbilityId==UPG_SPELL_ABILITY, SpellEvent.TargetUnit)
endfunction
private function Init takes nothing returns nothing
call RegisterSpellEffectResponse(SPELL_ABILITY, SpellEffect)
if UPG_SPELL_ABILITY != 0 then
call RegisterSpellEffectResponse(UPG_SPELL_ABILITY, SpellEffect)
endif
set xed=xedamage.create()
call DamageOptions(xed)
endfunction
endscope
 Elusive steed: scope ElusiveSteed initializer Init
globals
private constant integer PASSIVE_ABILITY = 'A003'
private constant integer MOVEMENT_ABILITY = 'A001'
private constant integer MOVEMENT_ABILITY_HOLDER = 'A002'
private constant integer UPG_PASSIVE_ABILITY = 'A00D'
private constant integer UPG_MOVEMENT_ABILITY = 'A00E'
private constant integer UPG_MOVEMENT_ABILITY_HOLDER = 'A00F'
private constant real ABILITY_UPDATE_PERIOD = 0.2
endglobals
private struct ai
unit u
real x
real y
integer level
boolean moving
boolean upgraded
private integer index
private static ai array loopList
private static integer loopCount=0
private static timer tim
static method get takes unit u returns ai
local integer i=0
loop
exitwhen i==ai.loopCount
if ai.loopList[i].u==u then
return ai.loopList[i]
endif
set i=i+1
endloop
return 0
endmethod
method levelup takes nothing returns nothing
set .level=.level+1
if .moving then
call SetUnitAbilityLevel(.u, MOVEMENT_ABILITY, .level)
call SetUnitAbilityLevel(.u, UPG_MOVEMENT_ABILITY, .level)
endif
endmethod
private static method addAbility takes unit u, integer spellbook, integer abil, integer lvl returns nothing
call UnitAddAbility(u, spellbook)
call UnitMakeAbilityPermanent(u, true, spellbook)
call SetUnitAbilityLevel(u, abil, lvl)
endmethod
private static method periodic takes nothing returns nothing
local integer i=ai.loopCount-1
local boolean b
local real x
local real y
loop
exitwhen i<0
set x=GetUnitX(ai.loopList[i].u)
set y=GetUnitY(ai.loopList[i].u)
set b=(ai.loopList[i].x!=x or ai.loopList[i].y!=y)
set ai.loopList[i].x=x
set ai.loopList[i].y=y
if b!=ai.loopList[i].moving then
set ai.loopList[i].moving=b
if b then
if ai.loopList[i].upgraded then
call ai.addAbility(ai.loopList[i].u, UPG_MOVEMENT_ABILITY_HOLDER, UPG_MOVEMENT_ABILITY, ai.loopList[i].level)
else
call ai.addAbility(ai.loopList[i].u, MOVEMENT_ABILITY_HOLDER, MOVEMENT_ABILITY, ai.loopList[i].level)
endif
else
call UnitRemoveAbility(ai.loopList[i].u, MOVEMENT_ABILITY_HOLDER)
call UnitRemoveAbility(ai.loopList[i].u, UPG_MOVEMENT_ABILITY_HOLDER)
endif
endif
set b=GetUnitAbilityLevel(ai.loopList[i].u, UPG_PASSIVE_ABILITY)>0
if not b and GetUnitAbilityLevel(ai.loopList[i].u, PASSIVE_ABILITY)<=0 then
call ai.loopList[i].destroy()
elseif ai.loopList[i].moving and b!=ai.loopList[i].upgraded then
set ai.loopList[i].upgraded=b
if b then
call UnitRemoveAbility(ai.loopList[i].u, MOVEMENT_ABILITY_HOLDER)
call ai.addAbility(ai.loopList[i].u, UPG_MOVEMENT_ABILITY_HOLDER, UPG_MOVEMENT_ABILITY, ai.loopList[i].level)
else
call UnitRemoveAbility(ai.loopList[i].u, UPG_MOVEMENT_ABILITY_HOLDER)
call ai.addAbility(ai.loopList[i].u, MOVEMENT_ABILITY_HOLDER, MOVEMENT_ABILITY, ai.loopList[i].level)
endif
endif
set i=i-1
endloop
endmethod
static method create takes unit u returns ai
local ai this=ai.allocate()
set .u=u
set .x=GetUnitX(u)
set .y=GetUnitY(u)
set .level=1
set .moving=false
set .upgraded=false
if ai.loopCount==0 then
call TimerStart(ai.tim, ABILITY_UPDATE_PERIOD, true, function ai.periodic)
endif
set ai.loopList[ai.loopCount]=this
set .index=ai.loopCount
set ai.loopCount=ai.loopCount+1
return this
endmethod
method onDestroy takes nothing returns nothing
set ai.loopCount=ai.loopCount-1
set ai.loopList[this.index]=ai.loopList[ai.loopCount]
set ai.loopList[ai.loopCount].index=this.index
call UnitRemoveAbility(.u, MOVEMENT_ABILITY_HOLDER)
call UnitRemoveAbility(.u, UPG_MOVEMENT_ABILITY_HOLDER)
if ai.loopCount==0 then
call PauseTimer(ai.tim)
endif
endmethod
static method onInit takes nothing returns nothing
set ai.tim=CreateTimer()
endmethod
endstruct
private function SpellLearn takes nothing returns boolean
local ai a
if GetLearnedSkill()==PASSIVE_ABILITY or (GetLearnedSkill()==UPG_PASSIVE_ABILITY and UPG_PASSIVE_ABILITY!=0) then
set a=ai.get(GetTriggerUnit())
if a==0 then
set a=ai.create(GetTriggerUnit())
else
call a.levelup()
endif
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i=0
loop
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_HERO_SKILL, null)
call SetPlayerAbilityAvailable(Player(i), MOVEMENT_ABILITY_HOLDER, false)
call SetPlayerAbilityAvailable(Player(i), UPG_MOVEMENT_ABILITY_HOLDER, false)
set i=i+1
exitwhen i>=12
endloop
call TriggerAddCondition( t, Condition( function SpellLearn ) )
call XE_PreloadAbility(MOVEMENT_ABILITY_HOLDER)
call XE_PreloadAbility(UPG_MOVEMENT_ABILITY_HOLDER)
endfunction
endscope
 Hunting Grounds: scope HuntingGrounds initializer Init
globals
private constant integer SPELL_ABILITY = 'A004'
private constant integer AREA_ABILITY = 'A007'
private constant integer CASTER_ABILITY = 'A005'
private constant integer CASTER_ABILITY_HOLDER = 'A006'
private constant boolean PRELOAD_CASTER_ABILITY = false
private constant real ABILITY_UPDATE_PERIOD = 0.2
endglobals
private constant function WardUnit takes integer level returns integer
return 'o000'
endfunction
private constant function AuraRange takes integer level returns real
return 1000.0
endfunction
private constant function SpellDuration takes integer level returns real
return 60.0
endfunction
private keyword si
private struct sc
unit u
integer level
si first
private integer index
private static sc array loopList
private static integer loopCount=0
private static timer tim
static method periodic takes nothing returns nothing
local integer i=0
local si s
local integer lvl
loop
exitwhen i>=sc.loopCount
set s=sc.loopList[i].first
set lvl=0
loop
if IsUnitInRangeXY(s.caster, s.x,s.y, AuraRange(s.level)) and s.level>lvl then
set lvl=s.level
endif
set s=s.next
exitwhen s==0
endloop
if lvl!=sc.loopList[i].level then
set sc.loopList[i].level=lvl
if lvl>0 then
call UnitAddAbility(sc.loopList[i].u, CASTER_ABILITY_HOLDER)
call UnitMakeAbilityPermanent(sc.loopList[i].u, true, CASTER_ABILITY_HOLDER)
call SetUnitAbilityLevel(sc.loopList[i].u, CASTER_ABILITY, lvl)
else
call UnitRemoveAbility(sc.loopList[i].u, CASTER_ABILITY_HOLDER)
endif
endif
set i=i+1
endloop
endmethod
static method get takes unit u returns sc
local integer i=0
loop
exitwhen i==sc.loopCount
if sc.loopList[i].u==u then
return sc.loopList[i]
endif
set i=i+1
endloop
return 0
endmethod
static method create takes unit u returns sc
local sc this=sc.allocate()
set .u=u
set .level=0
if sc.loopCount==0 then
set sc.tim=NewTimer()
call TimerStart(sc.tim, ABILITY_UPDATE_PERIOD, true, function sc.periodic)
endif
set sc.loopList[sc.loopCount]=this
set .index=sc.loopCount
set sc.loopCount=sc.loopCount+1
return this
endmethod
method onDestroy takes nothing returns nothing
set sc.loopCount=sc.loopCount-1
set sc.loopList[this.index]=sc.loopList[sc.loopCount]
set sc.loopList[sc.loopCount].index=this.index
if sc.loopCount==0 then
call ReleaseTimer(sc.tim)
endif
call UnitRemoveAbility(.u, CASTER_ABILITY_HOLDER)
endmethod
endstruct
private struct si
unit caster
integer level
real x
real y
unit u
effect e
si next
timer expiration
static method expire takes nothing returns nothing
call si(GetTimerData(GetExpiredTimer())).destroy()
endmethod
static method create takes unit caster, integer level, real x, real y returns si
local sc c=sc.get(caster)
local si this=si.allocate()
set .caster=caster
set .level=level
set .x=x
set .y=y
set .u=CreateUnit(GetOwningPlayer(caster), WardUnit(level), x,y,0.0)
call UnitAddAbility(.u, AREA_ABILITY)
call SetUnitAbilityLevel(.u, AREA_ABILITY, level)
set .e=AddSpecialEffectTarget(GetAbilityEffectById(SPELL_ABILITY, EFFECT_TYPE_AREA_EFFECT, 0), .u, "origin")
if c==0 then
set sc.create(caster).first=this
set .next=0
else
set .next=c.first
set c.first=this
endif
set .expiration=NewTimer()
call SetTimerData(.expiration, integer(this))
call TimerStart(.expiration, SpellDuration(level), false, function si.expire)
return this
endmethod
method onDestroy takes nothing returns nothing
local sc c=sc.get(.caster)
local si s=c.first
call ReleaseTimer(.expiration)
call DestroyEffect(.e)
call KillUnit(.u)
if s==this then
set c.first=.next
if c.first==0 then
call c.destroy()
endif
else
loop
if s.next==this then
set s.next=.next
set .next=0
return
endif
set s=s.next
endloop
endif
endmethod
endstruct
private function SpellEffect takes nothing returns nothing
local integer lvl = GetUnitAbilityLevel(SpellEvent.CastingUnit, SPELL_ABILITY)
call si.create(SpellEvent.CastingUnit, lvl, SpellEvent.TargetX, SpellEvent.TargetY)
endfunction
private function Init takes nothing returns nothing
local integer i=0
loop
call SetPlayerAbilityAvailable(Player(i), CASTER_ABILITY_HOLDER, false)
set i=i+1
exitwhen i>=12
endloop
call RegisterSpellEffectResponse(SPELL_ABILITY, SpellEffect)
call XE_PreloadAbility(AREA_ABILITY)
if PRELOAD_CASTER_ABILITY then
call XE_PreloadAbility(CASTER_ABILITY_HOLDER)
endif
endfunction
endscope
The custom model for the hero was never finished, so this map includes a WIP version of it for the sake of completeness.
__________________
|