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
