library CustomMotions initializer init_cm
globals
private constant real move_interval=0.04
private constant real move_minhitspeed=30.0 //minimum speed to knockback units and desroy doodads
private constant integer move_flyability='Amrf'
private constant string move_slidewater="MDX\\SlideWater.mdx"
private constant string move_slideground="MDX\\Dust.mdx"
private constant string move_flyburning="Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeDamageTarget.mdl"
private timer move_timer
private group move_hitgroup=CreateGroup()
private rect move_r
public boolexpr move_hitfilter
public boolexpr move_destfilter
private boolean timer_pause=false
public integer extent = 0
public move array MO
move cm_lastAddedMove //to be used in the same way as bj globals: see multijump example for further reference
endglobals
private function UnitInMO takes unit u returns integer
local integer i = 0
loop
exitwhen (i>=extent)
if u == MO<i>.u then
return i
endif
set i = i + 1
endloop
return -1
endfunction
private function dont_hit takes nothing returns boolean //dont affect those unit types: 2=structure, 3=flying, 9=giant
local unit u=GetFilterUnit()
local boolean b
set b=(GetWidgetLife(u)>0.405) and not (IsUnitType(u, ConvertUnitType(2)) or IsUnitType(u, ConvertUnitType(3)) or IsUnitType(u, ConvertUnitType(9)))
set u=null
return b
endfunction
private function IsDestructableVulnerable takes nothing returns boolean
return not(IsDestructableInvulnerable(GetEnumDestructable()))
endfunction
private function DamageDestructable takes nothing returns nothing
call SetWidgetLife(GetEnumDestructable(),GetWidgetLife(GetEnumDestructable())-bj_enumDestructableRadius)
endfunction //use bj_enumDestructableRadius as damage
private function init_cm takes nothing returns nothing
set move_destfilter=Filter(function IsDestructableVulnerable)
set move_hitfilter=Filter(function dont_hit)
set move_r=Rect(-128.0,-128.0,128.0,128.0)
set MO[8190]=1
set move_timer=CreateTimer()
endfunction
struct move
unit u //unit
real x = 0.0 //initial x
real y = 0.0 //initial y
real z = 0.0 //initial z - NOT for slides and knockbacks
real dx = 0.0 //difference target x - unit x
real dy = 0.0 //difference target y - unit y
real dz = 0.0 //difference target z - unit z
real maxz = 0.0 //maximum flying height - NOT for slides and knockbacks
real d = 0.0 //total distance
real v = 0.0 // (initial) speed
real a = 0.0 // acceleration
real w = 0.0 // initial circling angle
real vw = 0.0 // ciricling angle speed [degree/sec] note: should circle anticlockwise if negative
real aw = 0.0 //circling acceleration
real r = 0.0 //circling radius
real vr = 0.0 //circling radius incremental - only if the radius is to be increased
real vf = 0.0 //facing angle speed [degree/sec]
real time = 0.0 //time passed travelling
real ttime = 0.0 //total time
integer anid = -1 //unitanimationid while executing the move
boolean effects = false //sets if burning effect for jumpers or water/dust effect for sliders are shown
boolean hitothers = false //includes colliding units and destructables
boolean wait = false // false: do motion; true: wait with (attached) motion
integer continuewith = -1 //id of following struct if further structs are to be queued
boolean water_soil = false // true for last terrain was water, false for was soil
effect fx //the special effect to be attached over time
static method set_motion takes unit u, real x, real y, real speed, real acceleration, real height, integer animationid, boolean hitothers, boolean effects, boolean queued returns move
local move m=move.allocate()
if timer_pause then
call ResumeTimer(move_timer)
call CustomMotions_StartCM.execute()
endif
set m.u=u
set m.maxz=height
set m.v=speed
set m.a=acceleration
set m.effects=effects
if (animationid>=0) then
set m.anid=animationid
endif
set m.hitothers=hitothers
if queued then
set m.wait=true
set m.x=x
set m.y=y
else
set m.x=GetUnitX(u)
set m.y=GetUnitY(u)
set m.dx=x-m.x
set m.dy=y-m.y
set m.d=SquareRoot(m.dx*m.dx+m.dy*m.dy+0.01)
set m.w=Atan2(m.dy,m.dx)
if (m.a==0.0) and (m.v!=0.0) then
set m.ttime=m.d/m.v
elseif (m.a!=0.0) then
set m.ttime=(SquareRoot(m.v*m.v+2*m.a*m.d)-m.v)/m.a
elseif (m.a==0.0) and (m.v==0.0) then
set m.ttime=move_interval
endif
if (animationid>=0) then
call SetUnitAnimationByIndex(u,m.anid)
endif
if (height==0.0) then
if effects then
if IsTerrainPathable(m.x,m.y,ConvertPathingType(6)) then
set m.water_soil=false
set m.fx=AddSpecialEffectTarget(move_slideground, m.u, "right foot")
elseif not IsTerrainPathable(m.x,m.y,ConvertPathingType(1)) then
set m.water_soil=true
set m.fx=AddSpecialEffectTarget(move_slidewater, m.u, "right foot")
endif
endif
else
if effects then
set m.fx=AddSpecialEffectTarget(move_flyburning, m.u, "chest")
endif
set m.z=GetLocationZ(GetUnitLoc(u))
if (GetUnitFlyHeight(u)>0.0) then
set m.z=m.z+GetUnitFlyHeight(u)
endif
set m.dz=GetLocationZ(Location(x,y))-m.z
call SetUnitPathing(m.u, false)
call UnitAddAbility(m.u, move_flyability)
call UnitRemoveAbility(m.u, move_flyability)
endif
endif
return m
endmethod
method reset_motion takes nothing returns nothing
local real x=GetUnitX(.u)
local real y=GetUnitY(.u)
set .wait=false
set .dx=.x-x
set .dy=.y-y
set .x=x
set .y=y
set .d=SquareRoot(.dx*.dx+.dy*.dy+0.01)
set .w=Atan2(.dy,.dx)
if (.a==0.0) and (.v!=0.0) then
set .ttime=.d/.v
elseif (.a!=0.0) then
set .ttime=(SquareRoot(.v*.v+2*.a*.d)-.v)/.a
elseif (.a==0.0) and (.v==0.0) then
set .ttime=move_interval
endif
if (.anid>=0) then
call SetUnitAnimationByIndex(.u,.anid)
endif
if (.maxz==0.0) then
if .effects then
if IsTerrainPathable(x,y,ConvertPathingType(6)) then
set .water_soil=false
set .fx=AddSpecialEffectTarget(move_slideground, .u, "right foot")
elseif not IsTerrainPathable(x,y,ConvertPathingType(1)) then
set .water_soil=true
set .fx=AddSpecialEffectTarget(move_slidewater, .u, "right foot")
endif
endif
else
if .effects then
set .fx=AddSpecialEffectTarget(move_flyburning, .u, "chest")
endif
set .z=GetLocationZ(Location(x,y))
if (GetUnitFlyHeight(.u)>0.0) then
set .z=.z+GetUnitFlyHeight(.u)
endif
set .dz=GetLocationZ(Location(.x+.dx,.y+.dy))-.z
call SetUnitPathing(.u, false)
call UnitAddAbility(.u, move_flyability)
call UnitRemoveAbility(.u, move_flyability)
endif
endmethod
method add_spin takes real v_facing, real v_rot, real a_rot, real radius, real radiusincrement returns nothing
set .vw=v_rot*bj_DEGTORAD
set .aw=a_rot*bj_DEGTORAD
set .r=radius
set .vr=radiusincrement
set .vf=v_facing
endmethod
static method add takes move m returns boolean
if extent < 8191 then
set MO[extent] = m
set cm_lastAddedMove = m
set extent = extent + 1
return true
else
call BJDebugMsg("|C00FF0000ERROR: MO array overflow")
return false
endif
endmethod
static method add_attach takes move m1, move m2 returns boolean
if (m1.continuewith==-1) then
set m1.continuewith=m2 //attaches next struct
call move.add(m2)
if (m2.wait==false) then
set m2.wait=true
endif
return true
endif
call BJDebugMsg("|C00FF0000ERROR:Queue position for attached move already taken")
return false
endmethod
static method col_speed takes real v1, real m1, real v2, real m2 returns real
return (m1*v1+m2*(2*v2-v1))/(m1+m2)
endmethod
method collide takes real x, real y, unit u returns nothing
local real dx=GetUnitX(u)-x
local real dy=GetUnitY(u)-y
local real d=SquareRoot(dx*dx+dy*dy+.1)
local real m1=SquareRoot(GetUnitPointValue(.u))
local real m2=SquareRoot(GetUnitPointValue(u))
local real v=(2*.v*m1)/(m1+m2)
local real w=Atan2(dy,dx)
if UnitInMO(u)>-1 then
else //the construction site: to be finished!
set.v=.v*(m1-m2)/(m1+m2)
endif
endmethod
method do_hit takes real x, real y returns nothing
local integer j
local unit u
call MoveRectTo(move_r,x,y)
set bj_enumDestructableRadius=.v
call EnumDestructablesInRect(move_r,move_destfilter, function DamageDestructable)
call GroupEnumUnitsInRect(move_hitgroup,move_r,move_hitfilter)
if FirstOfGroup(move_hitgroup)!=null then
loop
set u=FirstOfGroup(move_hitgroup)
exitwhen u==null
call this.collide(x,y,u)
call GroupRemoveUnit(move_hitgroup,u)
//set u=null
endloop
call GroupClear(move_hitgroup)
endif
endmethod
method do_motion takes nothing returns nothing
local real x
local real y
local real d
local integer i=0
local boolean flying=((.maxz!=0.0)or(.dz!=0.0))
local boolean spinning=((.vw!=0.0) or (.aw!=0.0) or (.r!=0.0) or (.vr!=0.0))
if not(.wait) then
set .time=.time+move_interval
if (.time < .ttime) then
set d=(0.5*.a*.time*.time+.v*.time)/.d
set x=.x+d*.dx
set y=.y+d*.dy
if .vf!=0.0 then
call SetUnitFacing(.u, GetUnitFacing(.u)+.vf*.time)
endif
if spinning then //add circling
set .r=.r+.vr*.time
set d=0.5*.aw*.time*.time+.vw*.time+.w
set x=x+.r*Cos(d)
set y=y+.r*Sin(d)
endif
call SetUnitPosition(.u,x,y)
if flying then //add z-movement
set d=.time/.ttime
call SetUnitFlyHeight(.u,(.z+4*.maxz*d*(1-d)+d*.dz-GetLocationZ(Location(x,y))),0)
else
if .effects then
if .water_soil and IsTerrainPathable(x,y,ConvertPathingType(6)) then
set .water_soil=false
call DestroyEffect(.fx)
set .fx=AddSpecialEffectTarget(move_slideground, .u, "right foot")
elseif not.water_soil and not IsTerrainPathable(x,y,ConvertPathingType(6)) then
set .water_soil=true
call DestroyEffect(.fx)
set .fx=AddSpecialEffectTarget(move_slidewater, .u, "right foot")
endif
endif
if .hitothers and (.v>=move_minhitspeed) then
// call this.do_hit(x,y)
endif
endif
else
if flying then
call SetUnitPathing(.u, true)
call SetUnitFlyHeight(.u,.z-GetLocationZ(Location(.x,.y)),0)
if .hitothers and (.v>=move_minhitspeed) and (.continuewith<0) then
// call this.do_hit(GetUnitX(.u),GetUnitY(.u)) //hitting others at the end of a jump/toss
endif
endif
if (.continuewith>=0) then // checks if another motion is attached to the current one, and...
loop
exitwhen (i>=extent)
if (MO<i>==.continuewith) then
call MO<i>.reset_motion() //...if so it is unfrozen and reset
exitwhen true
endif
set i=i+1
endloop
endif
set i=0
loop // destroys current motion and sets the last struct in the array to its (free) position
exitwhen (i>=extent)
if (this==MO<i>) then
call .destroy()
set extent = extent - 1
set MO<i> = MO[extent]
exitwhen true
endif
set i=i+1
endloop
endif
endif
endmethod
method onDestroy takes nothing returns nothing
if .fx!=null then
call DestroyEffect(.fx)
set .fx=null
endif
set .u=null
endmethod
endstruct
public function TimerControl takes nothing returns nothing
if (extent<=0) and not(timer_pause) then
call PauseTimer(move_timer)
set timer_pause=true
endif
endfunction
public function CM takes nothing returns nothing
local integer i = 0
loop
exitwhen (i>=extent)
call MO<i>.do_motion()
set i = i + 1
endloop
endfunction
public function StartCM takes nothing returns nothing
call TimerStart(move_timer,move_interval,true,function CM)
set timer_pause=false
endfunction
endlibrary
//===========================================================================
function InitTrig_MotionSystem takes nothing returns nothing
endfunction
</i></i></i></i></i></i>
scope axes requires ABC, CSSafety, HelperFunctions
globals
constant integer axes_spellid = 'A04B'
private constant real width = 90.0
private constant integer leftaxe_id = 'n010'
private constant integer rightaxe_id ='n001'
private constant real speed = 800.0
private constant real interval = 0.04
private constant real spilldamage = 0.2 //spilldamage factor
private constant real flyheight = 65
private rect ax_rect
private boolexpr unitfilter
private boolexpr destfilter
private unit u
endglobals
private constant function Damage takes integer level returns real
return 90.0 + level * 60.0
endfunction
public function AxesInit takes nothing returns nothing
set ax_rect=Rect(-64.0,-64.0,64.0,64.0)
set unitfilter=Filter(function IsEnemy)
set destfilter=Filter(function IsDestructableVulnerable)
endfunction
private function DamageDestructable takes nothing returns nothing
call SetWidgetLife(GetEnumDestructable(),GetWidgetLife(GetEnumDestructable())-bj_enumDestructableRadius)
endfunction //use bj_enumDestructableRadius to transfer damage
struct axes
unit array u[4]
real array r[14] //total axes instances limited to 585
method set_axes takes unit c, unit u, player p returns nothing
set .u[0]=c
set .u[1]=u
set .r[0]=0.0 //t
set .r[1]=GetUnitX(c) //x
set .r[2]=GetUnitY(c) //y
set .r[3]=GetUnitX(u)-.r[1] //dx
set .r[4]=GetUnitY(u)-.r[2] //dy
set .r[5]=SquareRoot(.r[3]*.r[3]+.r[4]*.r[4]+.1) //d
set .r[6]=width/(.r[5]*.r[5]) //a
set .r[7]=-width*.r[4]/.r[5] //xa
set .r[8]=width*.r[3]/.r[5] //ya
set .r[9]=Atan2(.r[4],.r[3]) //w
set .r[10]=Sin(.r[9])
set .r[11]=Cos(.r[9])
if GetUnitFlyHeight(u)!=0 then
set .r[12]=GetUnitFlyHeight(u)-flyheight
else
set .r[12]=0.0
endif
set .r[13]=flyheight
set .u[2]=CreateUnit(p,leftaxe_id,.r[1]+.r[7],.r[2]+.r[8],bj_RADTODEG*.r[9])
set .u[3]=CreateUnit(p,rightaxe_id,.r[1]-.r[7],.r[2]-.r[8],bj_RADTODEG*.r[9])
endmethod
method onDestroy takes nothing returns nothing
local integer i=0
call KillUnit(.u[2])
call KillUnit(.u[3])
loop
exitwhen i>3
set .u<i>=null
set i=i+1
endloop
endmethod
endstruct
function HurtGroup takes nothing returns nothing
call UnitDamageTarget(u, GetEnumUnit(), bj_enumDestructableRadius, false, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null)
endfunction
function TwinAxes_child takes nothing returns nothing
local timer t=GetExpiredTimer()
local axes ax=GetStructA(t)
local group g=CreateGroup()
local real ds
local real x
local real y
local real xr
local real yr
local real dam=Damage(GetUnitAbilityLevel(ax.u[0],axes_spellid))
set ax.r[0]=ax.r[0]+interval
set ds=ax.r[0]*speed
if ds<ax.r[5] then
if ax.r[12]!=0.0 then
set ax.r[13]=flyheight+ax.r[12]*ds/ax.r[5]
endif
set u=ax.u[0]
set x=ds
set y=-ax.r[6]*ds*ds
set xr=x*ax.r[11]-y*ax.r[10]
set yr=y*ax.r[11]+x*ax.r[10]
call SetUnitPosition(ax.u[2],ax.r[1]+ax.r[7]+xr,ax.r[2]+ax.r[8]+yr)
call SetUnitFlyHeight(ax.u[2],ax.r[13],0)
call MoveRectTo(ax_rect,ax.r[1]+ax.r[7]+xr,ax.r[2]+ax.r[8]+yr)
set bj_enumDestructableRadius=dam*spilldamage
set bj_forceRandomCurrentPick=GetOwningPlayer(ax.u[0])
call EnumDestructablesInRect(ax_rect,destfilter,function DamageDestructable)
call GroupEnumUnitsInRect(g,ax_rect,unitfilter)
if FirstOfGroup(g)!=null then
call ForGroup(g, function HurtGroup)
call GroupClear(g)
endif
set xr=x*ax.r[11]+y*ax.r[10]
set yr=x*ax.r[10]-y*ax.r[11]
call SetUnitPosition(ax.u[3],ax.r[1]-ax.r[7]+xr,ax.r[2]-ax.r[8]+yr)
call SetUnitFlyHeight(ax.u[3],ax.r[13],0)
call MoveRectTo(ax_rect,ax.r[1]-ax.r[7]+xr,ax.r[2]-ax.r[8]+yr)
call EnumDestructablesInRect(ax_rect,destfilter,function DamageDestructable)
call GroupEnumUnitsInRect(g,ax_rect,unitfilter)
if FirstOfGroup(g)!=null then
call ForGroup(g, function HurtGroup)
call GroupClear(g)
endif
else
call GroupEnumUnitsInRange(g, ax.r[1]+ax.r[3],ax.r[2]+ax.r[4],150.0,null)
if IsUnitInGroup(ax.u[1],g) then
call UnitDamageTarget(ax.u[0],ax.u[1], dam, false, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null)
endif
call ax.destroy()
call ClearStructA(t)
call ReleaseTimer(t)
endif
endfunction
function TwinAxes takes nothing returns nothing
local axes ax=axes.create()
local unit u=GetTriggerUnit()
local timer t=NewTimer()
call ax.set_axes(u,GetSpellTargetUnit(),GetOwningPlayer(u))
call SetUnitAnimationByIndex(u, 9)
call SetStructA(t, ax)
call TimerStart(t, interval, true, function TwinAxes_child)
endfunction
endscope
//===========================================================================
function InitTrig_Axes takes nothing returns nothing
call axes_AxesInit()
call OnAbilityEffect(axes_spellid, "TwinAxes")
endfunction</i>