scope HunterAttack initializer init import Table import ProjectileStack import RegisterPlayerUnitEvent import TerrainData import Units import ClosureTimers struct AutoDat real uX real uY real tX real tY unit attacker integer logicalSecret end constant real SPREAD_ARC = bj_PI / 48. constant real JITTER = bj_PI / 24. constant real SEPARATION_ARC = bj_PI / 8. constant real SEPARATION_RADIUS = 32. constant real AUTO_RATE = 1. / 4. constant real POST_BURST_DELAY = 1. / 4. constant integer BURST_ID = 'A00B' Table logicalClock = new Table() function autoTick takes nothing returns nothing timer clock = GetExpiredTimer() real jitter AutoDat ad = clock.getData() castTo AutoDat if GetUnitCurrentOrder(ad.attacker) == OrderId("channel") and logicalClock.loadInt(ad.attacker.getHandleId()) == ad.logicalSecret then jitter = GetRandomReal(JITTER * -1./2., JITTER / 2.) ProjectileStack.add(ad.attacker, ad.uX, ad.uY, ad.tX, ad.tY, jitter) else DestroyTimer(clock) destroy ad end end function act takes nothing returns nothing unit attacker integer id = GetSpellAbilityId() real jitter real tX real tY real uX real uY real sepAngle real offsetX real offsetY real angle AutoDat ad timer clock if id == BURST_ID then attacker = GetTriggerUnit() uX = GetUnitX(attacker) uY = GetUnitY(attacker) tX = GetSpellTargetX() tY = GetSpellTargetY() boolean haveAuto = UnitHasItemOfTypeBJ(attacker, Units.GAS_ACTION) if not haveAuto then jitter = GetRandomReal(0., JITTER) - (JITTER / 2.) sepAngle = GetRandomReal(0., SEPARATION_ARC) - (SEPARATION_ARC / 2.) angle = TerrainData.radiansBetweenXY(uX, tX, uY, tY) if sepAngle < 0. then offsetX = SEPARATION_RADIUS * Cos(angle + sepAngle) offsetY = SEPARATION_RADIUS * Sin(angle + sepAngle) ProjectileStack.add(attacker, uX + offsetX, uY + offsetY, tX + offsetX, tY + offsetY, jitter - SPREAD_ARC) offsetX = SEPARATION_RADIUS * Cos(angle + sepAngle + bj_PI) offsetY = SEPARATION_RADIUS * Sin(angle + sepAngle + bj_PI) ProjectileStack.add(attacker, uX + offsetX, uY + offsetY, tX + offsetX, tY + offsetY, jitter + SPREAD_ARC) else offsetX = SEPARATION_RADIUS * Cos(angle + sepAngle) offsetY = SEPARATION_RADIUS * Sin(angle + sepAngle) ProjectileStack.add(attacker, uX + offsetX, uY + offsetY, tX + offsetX, tY + offsetY, jitter + SPREAD_ARC) offsetX = SEPARATION_RADIUS * Cos(angle + sepAngle + bj_PI) offsetY = SEPARATION_RADIUS * Sin(angle + sepAngle + bj_PI) ProjectileStack.add(attacker, uX + offsetX, uY + offsetY, tX + offsetX, tY + offsetY, jitter - SPREAD_ARC) end ProjectileStack.add(attacker, uX, uY, tX, tY, jitter) // Interrupt the animaton so it doesn't channel doAfter(POST_BURST_DELAY, () -> begin IssueImmediateOrder(attacker, "stop") end) else jitter = GetRandomReal(JITTER * -1./2., JITTER / 2.) ProjectileStack.add(attacker, uX, uY, tX, tY, jitter) // logical clock to prevent simultaneous cast if logicalClock.hasInt(attacker.getHandleId()) then logicalClock.saveInt(attacker.getHandleId(), logicalClock.loadInt(attacker.getHandleId()) + 1) else logicalClock.saveInt(attacker.getHandleId(), 0) end // spawn repeater ad = new AutoDat() ad.attacker = attacker ad.logicalSecret = logicalClock.loadInt(attacker.getHandleId()) ad.uX = uX ad.uY = uY ad.tX = tX ad.tY = tY clock = CreateTimer() clock.setData(ad castTo int) TimerStart(clock, AUTO_RATE, true, function autoTick) end end end function init takes nothing returns nothing registerPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function act) end endscope