Tom_Kazansky
--- wraith it ! ---
- Reaction score
- 157
Diablo II - Charge
By Tom_Kazansky
- vJass
- Laggless
- Leakless (I think)
- MUI
Description:
The Hero charges towards a target enemy unit, closing distance with it and delivering a bash on contact.
Code:
Screenshot
Readme
Please comment
EDIT: Reupload
EDIT2: update, the spell is now compatible with patch 1.24 and use TimerUtils instead of ABCT
By Tom_Kazansky
- vJass
- Laggless
- Leakless (I think)
- MUI
Description:
The Hero charges towards a target enemy unit, closing distance with it and delivering a bash on contact.
Code:
JASS:
scope D2Charge initializer Init
globals
private integer ChargeRawId = 039;A000039; // raw id of Charge ability
private integer ChargeSpeedId = 039;A001039; // raw id of Charge Speed ability
private integer ChargeBuff = 039;B000039; //raw id of the Charge buff
private integer CasterRunAniIndex = 2 //walk animation index of the caster's model,
//currently, model Spirit Walker's walk animation index is 2
//if you use other model, you should change this, if you don't the illusion will not play walk animation
private real CasterRunSpeed = 180. //Art - Animation - Run Speed of the caster -> this is used for the animation of the illusion (make it more "real")
private integer DummyId = 039;n000039; //raw id of the dummy unit, used to cast stun
private integer ChargeIllusionId = 039;n002039; //raw id of the dummy unit which is used for the illusion
private integer ChargeStun = 039;A002039; //raw id of the stun ability
private real KnockDist = 160. //knockback distance
private real KnockDur = 1.2 //knockback duration
private string HitSFX = "Abilities\\Spells\\Orc\\Devour\\DevourEffectArt.mdl" //string of the effect on contact
private string HitSFXA = "chest" //string of the attachment point
endglobals
//=================================
private function TJGetPPX takes real x, real dist, real angle returns real
return x + dist * Cos(angle * bj_DEGTORAD)
endfunction
private function TJGetPPY takes real y, real dist, real angle returns real
return y + dist * Sin(angle * bj_DEGTORAD)
endfunction
function AngleBetweenUnits takes unit A, unit B returns real
return bj_RADTODEG * Atan2(GetUnitY(B) - GetUnitY(A), GetUnitX(B) - GetUnitX(A))
endfunction
private struct datax
unit u
real dis
real ang
real tick
timer ti
endstruct
private function DestroyTreeEnum takes nothing returns nothing
local destructable des = GetEnumDestructable()
if GetDestructableMaxLife(des) == 50.00 and GetDestructableLife(des) > 0 then
call KillDestructable(des)
endif
set des = null
endfunction
private function DestroyTreeInCircle takes real x, real y, real aoe returns nothing
local rect r
local location tl = Location(x,y)
set bj_enumDestructableCenter = tl
set bj_enumDestructableRadius = aoe
set r = Rect(x - aoe, y - aoe, x + aoe, y + aoe)
call EnumDestructablesInRect(r, null,function DestroyTreeEnum)
call RemoveRect(r)
call RemoveLocation(tl)
set tl = null
set r = null
endfunction
private function KnockBackE takes nothing returns nothing
local datax d = GetTimerData( GetExpiredTimer() )
local real x
local real y
local real mx
local real my
if d.tick > 0 then
set x = GetUnitX(d.u)
set y = GetUnitY(d.u)
set mx = TJGetPPX( x, d.dis, d.ang )
set my = TJGetPPY( y, d.dis, d.ang )
call DestroyEffect( AddSpecialEffect("Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl" ,x,y))
call DestroyTreeInCircle( mx, my,150.00 )
call SetUnitPosition(d.u, mx, my )
set d.tick = d.tick - 1
elseif d.tick <= 0 then
call datax.destroy(d)
call ReleaseTimer(d.ti)
endif
endfunction
function KnockBack takes unit u,real dur, real dis,real ang returns nothing
local datax d
if dur == 0. or dis == 0. then
set u = null
return
endif
set d = datax.create()
set d.u = u
set d.tick = R2I(dur/0.04)
set d.dis = dis/d.tick
set d.ang = ang
set d.ti = NewTimer()
call SetTimerData(d.ti,d)
call TimerStart( d.ti,0.04,true, function KnockBackE)
set u = null
endfunction
//=================================
private struct data
unit caster = null
unit target = null
unit d1 = null
unit d2 = null
integer tick = 0
real casterx = 0.
real castery = 0.
boolean stop = false
timer ti
endstruct
private function ChargeInterrupted takes unit u, unit a returns boolean
if GetWidgetLife(u) == 0 then
return true
elseif (a != null and GetWidgetLife(a) == 0) then
return true
elseif IsUnitType(u,UNIT_TYPE_ETHEREAL) then
return true
elseif IsUnitType(u,UNIT_TYPE_POLYMORPHED ) then
return true
elseif IsUnitType(u,UNIT_TYPE_STUNNED ) then
return true
elseif IsUnitType(u,UNIT_TYPE_SNARED ) then
return true
elseif (a != null and IsUnitInvisible(a, GetOwningPlayer(u))) then
return true
elseif OrderId2String( GetUnitCurrentOrder(u) ) != "attack" and OrderId2String( GetUnitCurrentOrder(u) ) != "smart" then
return true
elseif GetUnitAbilityLevel(u,ChargeBuff) == 0 then
return true
endif
set u = null
set a = null
return false
endfunction
private function ChargeM takes nothing returns nothing
local data d = GetTimerData( GetExpiredTimer() )
local real x = GetUnitX(d.caster)
local real y = GetUnitY(d.caster)
local real fa = GetUnitFacing(d.caster)
local real ang
local real mx
local real my
if x == d.casterx and y == d.castery then
if not d.stop then
set d.stop = true
call SetUnitVertexColor( d.d1,255,255,255,0)
call SetUnitVertexColor( d.d2,255,255,255,0)
endif
else
if d.stop then
set d.stop = false
call SetUnitVertexColor( d.d1,255,255,255,170)
call SetUnitVertexColor( d.d2,255,255,255,85)
endif
endif
if not d.stop then
set mx = TJGetPPX(x,-40, fa)
set my = TJGetPPY(y,-40, fa)
call SetUnitX(d.d1, mx)
call SetUnitY(d.d1, my)
call SetUnitFacing(d.d1, fa )
set mx = TJGetPPX(x,-80, fa)
set my = TJGetPPY(y,-80, fa)
call SetUnitX(d.d2, mx)
call SetUnitY(d.d2, my)
call SetUnitFacing(d.d2, fa )
else
call SetUnitX(d.d1, x)
call SetUnitY(d.d1, y)
call SetUnitFacing(d.d1, fa )
call SetUnitX(d.d2, x)
call SetUnitY(d.d2, y)
call SetUnitFacing(d.d2, fa )
endif
if ChargeInterrupted(d.caster,d.target) then
call UnitRemoveAbility(d.caster,ChargeSpeedId)
call UnitRemoveAbility(d.caster,ChargeBuff)
call RemoveUnit(d.d1)
call RemoveUnit(d.d2)
call SetUnitPathing(d.caster,true)
call data.destroy(d)
call ReleaseTimer(d.ti)
return
endif
set d.casterx = x
set d.castery = y
endfunction
private function ChargeC takes data d returns nothing
local real cx = GetUnitX(d.caster)
local real cy = GetUnitY(d.caster)
local real fa = GetUnitFacing(d.caster)
local real mx
local real my
call SetUnitPathing(d.caster,false)
set mx = TJGetPPX(cx,-40,fa)
set my = TJGetPPY(cy,-40,fa)
set d.d1 = CreateUnit( GetOwningPlayer(d.caster), ChargeIllusionId, mx, my, fa )
call SetUnitVertexColor( d.d1,255,255,255,170)
call SetUnitTimeScale( d.d1,522 / CasterRunSpeed )
call PauseUnit(d.d1,true)
call SetUnitPathing(d.d1,false)
call SetUnitAnimationByIndex(d.d1, CasterRunAniIndex )
call SetUnitMoveSpeed(d.d1,522)
call SetUnitPosition(d.d1,cx,cy)
call SetUnitColor(d.d1,ConvertPlayerColor(GetPlayerId(GetOwningPlayer(d.caster))))
set mx = TJGetPPX(cx,-80,fa)
set my = TJGetPPY(cy,-80,fa)
set d.d2 = CreateUnit( GetOwningPlayer(d.caster), ChargeIllusionId,mx, my,fa )
call SetUnitVertexColor(d.d2,255,255,255, 85 )
call SetUnitTimeScale(d.d2,522/CasterRunSpeed)
call PauseUnit(d.d1,true)
call SetUnitPathing(d.d2,false)
call SetUnitAnimationByIndex(d.d2, CasterRunAniIndex )
call SetUnitMoveSpeed(d.d2,522)
call SetUnitPosition(d.d2,cx,cy)
call SetUnitColor(d.d2,ConvertPlayerColor(GetPlayerId(GetOwningPlayer(d.caster))))
set d.ti = NewTimer()
call SetTimerData(d.ti,d)
call TimerStart(d.ti,0.02,true, function ChargeM )
endfunction
//===============================================================================
private function Action takes nothing returns nothing
local unit c
local unit a
local unit dumb
local location loc
local integer lvl
local eventid EID = GetTriggerEventId()
local data d
if EID == EVENT_PLAYER_UNIT_SPELL_EFFECT and GetSpellAbilityId() == ChargeRawId then
set c = GetSpellAbilityUnit()
set a = GetSpellTargetUnit()
set loc = GetSpellTargetLoc()
set lvl = GetUnitAbilityLevel(c, ChargeRawId)
set d = data.create()
set d.caster = c
set d.target = a
set d.casterx = GetUnitX(c)
set d.castery = GetUnitY(c)
if d.target != null then
call IssueTargetOrder(c,"attack",a)
else
call IssuePointOrder(c,"smart",GetLocationX(loc),GetLocationY(loc))
call RemoveLocation(loc)
endif
call ChargeC(d)
call UnitAddAbility(c, ChargeSpeedId )
set loc = null
set c = null
set a = null
return
endif
if EID == EVENT_PLAYER_UNIT_ATTACKED then
set c = GetAttacker()
set a = GetTriggerUnit()
set lvl = GetUnitAbilityLevel(c, ChargeRawId)
if GetUnitAbilityLevel( c, ChargeBuff ) > 0 then
set dumb = CreateUnit( Player(15), DummyId , GetUnitX(a), GetUnitY(a), 0 )
call UnitAddAbility( dumb , ChargeStun )
call SetUnitAbilityLevel( dumb, ChargeStun , lvl )
call IssueTargetOrder(dumb, "firebolt", a )
call UnitApplyTimedLife(dumb,039;BTLF039;, 1. )
set dumb = null
call DestroyEffect( AddSpecialEffectTarget(HitSFX,a,HitSFXA) )
call KnockBack(a,KnockDur, KnockDist, AngleBetweenUnits(c,a) )
call UnitRemoveAbility(c,ChargeSpeedId)
call UnitRemoveAbility(c,ChargeBuff)
endif
set c = null
set a = null
return
endif
set c = GetTriggerUnit()
set a = GetOrderTargetUnit()
if GetUnitAbilityLevel(c,ChargeBuff) > 0 then
call UnitRemoveAbility(c,ChargeBuff)
endif
set c = null
set a = null
endfunction
//===================================================================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer index = 0
call TriggerAddAction(t, function Action)
set index = 0
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(t, Player(index), EVENT_PLAYER_UNIT_ATTACKED, null)
call TriggerRegisterPlayerUnitEvent(t, Player(index), EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
call TriggerRegisterPlayerUnitEvent(t, Player(index), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
call TriggerRegisterPlayerUnitEvent(t, Player(index), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
endfunction
endscope
Screenshot
Readme
D2 Spell: Charge ability by Tom_Kazansky
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* How to import:
- Copy the Charge, Charge Speed, Charge Stun abilities and the Charge buff
- Copy 2 units: Dummy and Dummy Charge Illusion
- Copy TimerUtils(if you don't have one) and the D2Charge trigger
- Change the RawId of abilities and units in the globals block of D2Charge
* Credit:
- Thanks Vexorian for TimerUtils
- Thanks Tinki3 for the test map template
* Modification:
- Modify the Charge Stun abilitiy for damage and stun duration on contact
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* How to import:
- Copy the Charge, Charge Speed, Charge Stun abilities and the Charge buff
- Copy 2 units: Dummy and Dummy Charge Illusion
- Copy TimerUtils(if you don't have one) and the D2Charge trigger
- Change the RawId of abilities and units in the globals block of D2Charge
* Credit:
- Thanks Vexorian for TimerUtils
- Thanks Tinki3 for the test map template
* Modification:
- Modify the Charge Stun abilitiy for damage and stun duration on contact
Please comment
EDIT: Reupload
EDIT2: update, the spell is now compatible with patch 1.24 and use TimerUtils instead of ABCT