- Reaction score
- 91
Okay, as an idea from DotA and waaaks!, I wanted to make my own impale system for personal use due to the minor bugs the normal skill has. It worked perfectly when using it on a single unit but the problem comes when I impale more than 2-3 units from the "line" - as they return back to the ground a huge lag spike is created and my handle counter goes wild. The only things I run when the units get back to the ground are damaging, reseting height to 0 and applying a stun which duration is supposed to get refreshed if multiple impales are casted on the same unit while it's being stunned. I can't find a reason why this lag spike is created... Also, when it ends a random unit gets four-five times damaged instead of only once and then the system totally bugs and instead of tossing units it just makes random units get their height increased by 24 permanently.
EDIT:
Nvm, fixed, if someone wants the code to learn or just use:
JASS:
library ImpaleLib initializer Init uses TimerUtils, Table, xebasic, TimedEffects, GroupUtils
globals
private constant integer AID_STUN = 039;A00E039; // Stun spell with 10 sec duration.
private constant integer BID_STUN = 039;B002039; // Buff that comes with the spell
private constant string OID_STUN = "thunderbolt" // Order string
private constant real RANGE = 150. // Range between sfx spikes creation
private constant real TIME = 1.04 // Time in the air (including falling down).
private constant real PERIOD = 0.03125 // Period for increasing height.
private constant real PERIOD2 = 2 * PERIOD // Period for creating spikes.
private constant real HEIGHT = 24. // Height increment per tick.
// Some sfxs.
private constant string SFX_IMPALE = "Abilities\\Spells\\Undead\\Impale\\ImpaleHitTarget.mdl"
private constant string SFX_SPIKES = "Abilities\\Spells\\Undead\\Impale\\ImpaleMissTarget.mdl"
endglobals
globals
private HandleTable ht
private Table t
private Table s
endglobals
private struct Data
unit targ
timer tim
integer ticks
private static method Callback takes nothing returns nothing
local Data d = Data(ht[GetExpiredTimer()])
set d.ticks = d.ticks - 1
if d.ticks <= 0 or GetWidgetLife(d.targ) < 0.405 then
call UnitRemoveAbility(d.targ, BID_STUN)
call d.destroy() // End the stun.
endif
endmethod
static method create takes unit targ, real dur returns Data
local Data d = Data.allocate()
local integer id = GetUnitId(targ)
set d.targ = targ
set d.ticks = R2I(dur / PERIOD)
set d.tim = NewTimer()
set ht[d.tim] = integer(d)
set s[id] = integer(d)
// Dummy stuff.
//call UnitRemoveAbility(d.targ, 'Avul')
call GetAvailableDummy(Player(13))
call SetUnitX(dum, GetUnitX(d.targ))
call SetUnitY(dum, GetUnitY(d.targ))
call AddAndCastTarget(dum, d.targ, AID_STUN, 1, OID_STUN)
call ReleaseDummy(dum)
call TimerStart(d.tim, PERIOD, true, function Data.Callback)
return d
endmethod
method onDestroy takes nothing returns nothing
// I think this stuff is never called...
call ht.flush(.tim)
call ReleaseTimer(.tim)
call BJDebugMsg("STUN DATA DESTROYED!")
endmethod
endstruct
function ImpaleStunTarget takes unit targ, real stun returns nothing
call Data.create(targ, stun)
endfunction
private struct Data2
unit cast
unit targ
real dmg
real stun
real facing
integer ticks
integer halfticks
timer tim
boolean flip = false
private static method Callback takes nothing returns nothing
local Data2 d = Data2(ht[GetExpiredTimer()])
set d.ticks = d.ticks - 1
if d.ticks == d.halfticks then
set d.flip = true // Do a height deduction.
endif
if d.ticks <= 0 then
// Make it stop since the last order was hold position.
call IssueImmediateOrder(d.targ, "stop")
call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
call UnitAddAbility(d.targ, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(d.targ, XE_HEIGHT_ENABLER)
call SetUnitFlyHeight(d.targ, 0., 0.)
if d.stun != 0. then
call ImpaleStunTarget(d.targ, d.stun)
endif
call d.destroy()
return
endif
call UnitAddAbility(d.targ, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(d.targ, XE_HEIGHT_ENABLER)
if d.flip == true then // Height deduction?
call SetUnitFlyHeight(d.targ, GetUnitFlyHeight(d.targ) - HEIGHT, 0.)
else // Then it should be increased.
call SetUnitFlyHeight(d.targ, GetUnitFlyHeight(d.targ) + HEIGHT, 0.)
endif
// Simulate a "disabling" process instead of using pause.
call IssueImmediateOrder(d.targ, "holdposition")
call SetUnitFacing(d.targ, d.facing)
endmethod
static method create takes unit cast, unit targ, real dmg, real stun returns Data2
local Data2 d = Data2.allocate()
local integer id = GetUnitId(targ)
set d.cast = cast
set d.targ = targ
set d.facing = GetUnitFacing(d.targ)
set d.dmg = dmg
set d.stun = stun
set d.ticks = R2I(TIME / PERIOD)
set d.halfticks = R2I(d.ticks * 0.5) // To check when the unit has to return back
// to the ground.
// Timed effect.
call StartTimedEffect(AddSpecialEffect(SFX_IMPALE, GetUnitX(d.targ), GetUnitY(d.targ)), TIME)
set d.tim = NewTimer()
set ht[d.tim] = integer(d)
set t[id] = integer(d)
call TimerStart(d.tim, PERIOD, true, function Data2.Callback)
return d
endmethod
method onDestroy takes nothing returns nothing
call ht.flush(.tim)
call ReleaseTimer(.tim)
call BJDebugMsg("TARGET DATA DESTROYED!")
endmethod
endstruct
function ImpaleUnit takes unit cast, unit targ, real dmg, real stun returns nothing
call Data2.create(cast, targ, dmg, stun)
endfunction
private struct Data3
unit cast
real dmg
integer ticks
real angle
real x
real y
real cosp
real sinp
real aoe
real stun
timer tim
group gr
private static method GetEnemies takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(pass)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true
endmethod
private static method Callback takes nothing returns nothing
local Data3 d = Data3(ht[GetExpiredTimer()])
set d.x = d.x + d.cosp
set d.y = d.y + d.sinp
call StartTimedEffect(AddSpecialEffect(SFX_SPIKES, d.x, d.y), 1.)
set pass = d.cast
call GroupClear(ENUM_GROUP)
call GroupEnumUnitsInRange(ENUM_GROUP, d.x, d.y, d.aoe, Condition(function Data3.GetEnemies))
loop
set fir = FirstOfGroup(ENUM_GROUP)
exitwhen fir == null
if IsUnitInGroup(fir, d.gr) == false then
call GroupAddUnit(d.gr, fir)
call ImpaleUnit(d.cast, fir, d.dmg, d.stun) // Do an impale...
endif
call GroupRemoveUnit(ENUM_GROUP, fir)
endloop
set d.ticks = d.ticks - 1
if d.ticks <= 0 then
call d.destroy()
endif
endmethod
static method create takes unit cast, real dmg, real tx, real ty, real aoe, real distance, real stun returns Data3
local Data3 d = Data3.allocate()
set d.x = GetUnitX(cast)
set d.y = GetUnitY(cast)
set d.angle = Atan2(ty - d.y, tx - d.x)
set d.cosp = RANGE * Cos(d.angle)
set d.sinp = RANGE * Sin(d.angle)
set d.cast = cast
set d.dmg = dmg
set d.aoe = aoe
set d.stun = stun // Stun duration.
set d.ticks = R2I(distance / RANGE)
set d.gr = NewGroup()
set d.tim = NewTimer()
set ht[d.tim] = integer(d)
call TimerStart(d.tim, PERIOD2, true, function Data3.Callback)
return d
endmethod
method onDestroy takes nothing returns nothing
call ht.flush(.tim)
call ReleaseTimer(.tim)
call ReleaseGroup(.gr)
call BJDebugMsg("LINE DATA DESTROYED!")
endmethod
endstruct
function ImpaleLine takes unit cast, real dmg, real tx, real ty, real aoe, real distance, real stun returns nothing
call Data3.create(cast, dmg, tx, ty, aoe, distance, stun)
endfunction
private function Init takes nothing returns nothing
set ht = HandleTable.create()
set t = Table.create()
set s = Table.create()
endfunction
endlibrary
EDIT:
Nvm, fixed, if someone wants the code to learn or just use:
JASS:
library ImpaleLib initializer Init uses TimerUtils, Table, DummyLib, xebasic, TimedEffects, GroupUtils, AbilityPreload
globals
private constant integer AID_STUN = 039;A00E039;
private constant integer BID_STUN = 039;B002039;
private constant string OID_STUN = "thunderbolt"
private constant real RANGE = 150.
private constant real TIME = 1.04
private constant real PERIOD = 0.03125
private constant real PERIOD2 = 2 * PERIOD
private constant real HEIGHT = 24.
private constant string SFX_IMPALE = "Abilities\\Spells\\Undead\\Impale\\ImpaleHitTarget.mdl"
private constant string SFX_SPIKES = "Abilities\\Spells\\Undead\\Impale\\ImpaleMissTarget.mdl"
endglobals
globals
private HandleTable ht
private Table t
private unit dumi
endglobals
private struct Unit
unit cast
unit targ
real dmg
real stun
real facing
integer ticks
integer halfticks
timer tim
boolean flip = false
private static method Callback takes nothing returns nothing
local Unit d = ht[GetExpiredTimer()]
set d.ticks = d.ticks - 1
if d.ticks == d.halfticks then
set d.flip = true
endif
if d.ticks <= 0 then
//==============================================
call UnitRemoveAbility(d.targ, 039;Avul039;) // You should remove this line.
//==============================================
call IssueImmediateOrder(d.targ, "stop")
call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
call UnitAddAbility(d.targ, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(d.targ, XE_HEIGHT_ENABLER)
call SetUnitFlyHeight(d.targ, 0., 0.)
if d.stun != 0. then
call SetUnitX(dumi, GetUnitX(d.targ))
call SetUnitY(dumi, GetUnitY(d.targ))
call AddAndCastTarget(dumi, d.targ, AID_STUN, R2I(d.stun), OID_STUN)
endif
call UnitRemoveAbility(d.targ, XE_HEIGHT_ENABLER)
call d.destroy()
else
call UnitAddAbility(d.targ, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(d.targ, XE_HEIGHT_ENABLER)
if d.flip == true then
call SetUnitFlyHeight(d.targ, GetUnitFlyHeight(d.targ) - HEIGHT, 0.)
else
call SetUnitFlyHeight(d.targ, GetUnitFlyHeight(d.targ) + HEIGHT, 0.)
endif
call IssueImmediateOrder(d.targ, "holdposition")
call SetUnitFacing(d.targ, d.facing)
endif
endmethod
static method create takes unit cast, unit targ, real dmg, real stun returns Unit
local Unit d = Unit.allocate()
local integer id = GetUnitId(targ)
set d.cast = cast
set d.targ = targ
set d.facing = GetUnitFacing(d.targ)
set d.dmg = dmg
set d.stun = stun
set d.ticks = R2I(TIME / PERIOD)
set d.halfticks = R2I(d.ticks * 0.5)
call StartTimedEffect(AddSpecialEffect(SFX_IMPALE, GetUnitX(d.targ), GetUnitY(d.targ)), TIME)
set d.tim = NewTimer()
set ht[d.tim] = d
set t[id] = d
call TimerStart(d.tim, PERIOD, true, function Unit.Callback)
return d
endmethod
method onDestroy takes nothing returns nothing
call ht.flush(.tim)
call ReleaseTimer(.tim)
endmethod
endstruct
function ImpaleUnit takes unit cast, unit targ, real dmg, real stun returns nothing
call Unit.create(cast, targ, dmg, stun)
endfunction
private struct Line
unit cast
real dmg
integer ticks
real angle
real x
real y
real cosp
real sinp
real aoe
real stun
timer tim
group gr
private static method GetEnemies takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(pass)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true
endmethod
private static method Callback takes nothing returns nothing
local Line d = ht[GetExpiredTimer()]
set d.x = d.x + d.cosp
set d.y = d.y + d.sinp
call StartTimedEffect(AddSpecialEffect(SFX_SPIKES, d.x, d.y), 1.)
set pass = d.cast
call GroupClear(ENUM_GROUP)
call GroupEnumUnitsInRange(ENUM_GROUP, d.x, d.y, d.aoe, Condition(function Line.GetEnemies))
loop
set fir = FirstOfGroup(ENUM_GROUP)
exitwhen fir == null
if IsUnitInGroup(fir, d.gr) == false then
call GroupAddUnit(d.gr, fir)
call ImpaleUnit(d.cast, fir, d.dmg, d.stun) // Do an impale...
endif
call GroupRemoveUnit(ENUM_GROUP, fir)
endloop
set d.ticks = d.ticks - 1
if d.ticks <= 0 then
call d.destroy()
endif
endmethod
static method create takes unit cast, real dmg, real tx, real ty, real aoe, real distance, real stun returns Line
local Line d = Line.allocate()
local unit gstu = GetSpellTargetUnit()
if gstu != null then
set tx = GetUnitX(gstu)
set ty = GetUnitY(gstu)
set gstu = null
endif
set d.x = GetUnitX(cast)
set d.y = GetUnitY(cast)
set d.angle = Atan2(ty - d.y, tx - d.x)
set d.cosp = RANGE * Cos(d.angle)
set d.sinp = RANGE * Sin(d.angle)
set d.cast = cast
set d.dmg = dmg
set d.aoe = aoe
set d.stun = stun
set d.ticks = R2I(distance / RANGE)
set d.gr = NewGroup()
set d.tim = NewTimer()
set ht[d.tim] = d
call TimerStart(d.tim, PERIOD2, true, function Line.Callback)
return d
endmethod
method onDestroy takes nothing returns nothing
call ht.flush(.tim)
call ReleaseTimer(.tim)
call ReleaseGroup(.gr)
endmethod
endstruct
function ImpaleLine takes unit cast, real dmg, real tx, real ty, real aoe, real distance, real stun returns nothing
call Line.create(cast, dmg, tx, ty, aoe, distance, stun)
endfunction
private function Init takes nothing returns nothing
set ht = HandleTable.create()
set t = Table.create()
call AbilityPreload(AID_STUN)
set dumi = CreateUnit(Player(13), XE_DUMMY_UNITID, 0., 0., 0.)
call UnitAddAbility(dumi, 039;Aloc039;)
call UnitAddAbility(dumi, AID_STUN)
endfunction
endlibrary