| 06-04-2009, 02:22 PM | #1 |
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. JASS:library ImpaleLib initializer Init uses TimerUtils, Table, xebasic, TimedEffects, GroupUtils globals private constant integer AID_STUN = 'A00E' // Stun spell with 10 sec duration. private constant integer BID_STUN = 'B002' // 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: Okay, I found the reason for my epic spike... It comes from my dummy unit library. JASS:private function CreateDummyUnits takes nothing returns nothing local integer a = 0 local integer id loop exitwhen a > DUMMY_UNITS_CREATED set DUMMY[a] = CreateUnit(Player(13), XE_DUMMY_UNITID, 9999., 9999., 0.) call UnitAddAbility(DUMMY[a], 'Aloc') set id = GetUnitId(DUMMY[a]) set IS_SOME_DUMMY[id] = true set IS_IN_USE[id] = false set a = a + 1 endloop endfunction Then I have a function that searches for a free dummy unit and if it finds one it assigns it to a global unit variable and makes its property to be in use. JASS:function GetAvailableDummy takes player p returns nothing local integer id loop set dum = DUMMY[GetRandomInt(0, DUMMY_UNITS_CREATED)] set id = GetUnitId(dum) exitwhen (IS_IN_USE[id] == false) endloop call SetUnitOwner(dum, p, false) set IS_IN_USE[id] = true endfunction However, calling GetAvailableDummy seems to cause the massive spike and AutoIndex starts flooding my screen with some debug messages about indexing a unit that hasn't previously received an index. Could there be some reason that the first function that creates the units isn't working ?! JASS:library DummyLib initializer Init uses xebasic, Table, TimerUtils, AutoIndex, RegisterAnyUnitEvent globals private constant real TIME = 5. private constant real WAIT = 0.28 endglobals globals constant integer DUMMY_UNITS_CREATED = 100 endglobals globals private HandleTable ht endglobals globals unit dum = null endglobals private function CreateDummyUnits takes nothing returns nothing local integer a = 0 local integer id loop exitwhen a > DUMMY_UNITS_CREATED set DUMMY[a] = CreateUnit(Player(13), XE_DUMMY_UNITID, 9999., 9999., 0.) call UnitAddAbility(DUMMY[a], 'Aloc') set id = GetUnitId(DUMMY[a]) set IS_SOME_DUMMY[id] = true set IS_IN_USE[id] = false set a = a + 1 endloop endfunction private struct Data unit dummy timer tim private static method Callback takes nothing returns nothing call Data(ht[GetExpiredTimer()]).destroy() endmethod static method create takes unit dummy returns Data local Data d = Data.allocate() set d.dummy = dummy set d.tim = NewTimer() set ht[d.tim] = integer(d) call TimerStart(d.tim, TIME, false, function Data.Callback) return d endmethod method onDestroy takes nothing returns nothing call ht.flush(.tim) call ReleaseTimer(.tim) call RemoveUnitEx(.dummy) endmethod endstruct private struct Release unit dum timer tim private static method Callback takes nothing returns nothing call Release(ht[GetExpiredTimer()]).destroy() endmethod static method create takes unit dum returns Release local Release d = Release.allocate() set d.dum = dum set d.tim = NewTimer() set ht[d.tim] = integer(d) call TimerStart(d.tim, WAIT, false, function Release.Callback) return d endmethod method onDestroy takes nothing returns nothing call ReleaseTimer(.tim) call ht.flush(.tim) call SetUnitOwner(.dum, Player(13), false) set IS_IN_USE[GetUnitId(.dum)] = false call SetUnitX(.dum, 999.) call SetUnitY(.dum, 999.) endmethod endstruct private function Actions takes nothing returns nothing local unit dummy = GetTriggerUnit() local integer id = GetUnitId(dummy) if IS_SOME_DUMMY[id] == true then call Data.create(dummy) endif set dummy = null endfunction function GetAvailableDummy takes player p returns nothing local integer id loop set dum = DUMMY[GetRandomInt(0, DUMMY_UNITS_CREATED)] set id = GetUnitId(dum) exitwhen (IS_IN_USE[id] == false) endloop call SetUnitOwner(dum, p, false) set IS_IN_USE[id] = true endfunction function ReleaseDummy takes unit dum returns nothing call Release.create(dum) endfunction function AddAndCastTarget takes unit dum, unit targ, integer aid, integer lvl, string oid returns nothing call UnitAddAbility(dum, aid) call SetUnitAbilityLevel(dum, aid, lvl) call IssueTargetOrder(dum, oid, targ) endfunction function AddAndCast takes unit dum, integer aid, integer lvl, string oid returns nothing call UnitAddAbility(dum, aid) call SetUnitAbilityLevel(dum, aid, lvl) call IssueImmediateOrder(dum, oid) endfunction private function Init takes nothing returns nothing call RegisterAnyUnitEvent(EVENT_PLAYER_UNIT_DEATH, null, function Actions) call CreateDummyUnits() set ht = HandleTable.create() endfunction endlibrary |
