scope Net initializer init
	import DummyUnitStack
	import DamageOverTime
	import RegisterPlayerUnitEvent

	native UnitAlive takes unit u returns boolean

	struct NetData
		real delX
		real delY
		real distance
		unit dummy
		effect attached
		player caster
	end

	constant string  NET_FX           = "Abilities\\Spells\\Orc\\Ensnare\\ensnareTarget.mdl"
	constant string  NET_MODEL        = "Abilities\\Spells\\Orc\\Ensnare\\EnsnareMissile.mdl"

	constant integer NET_ID           = 'A006' // Spellcode for net ability
	constant integer NET_SLOW_ID      = 'A005' // Spellcode of net slow dummy ability

	// An arbitrary value for making the net buff mutually exclusive in the
	// context of the DamageOverTime module.
	constant integer EXCLUSIVITY_NET  = 'hnet'

	constant real    DELAY            =    .2
	constant real    RADIUS           = 200.
	constant real    CLOCK_PERIOD     =   1.  / 30.
	constant real    NET_VELOCITY     = 600.
	constant real    COLLISION_RADIUS =  80.
	constant real    MAX_DISTANCE     = 500.

	group grp = CreateGroup()

	timer clock = CreateTimer()

	NetData array dataDb

	integer dbIndex = -1

	function strikeUnits takes real x, real y, player caster returns nothing
		unit iter
		unit dummy

		GroupEnumUnitsInRange(grp, x, y, RADIUS, null)
		loop
			iter = FirstOfGroup(grp)
			exitwhen iter == null

			if IsUnitEnemy(iter, caster) and UnitAlive(iter) then
				dummy = DummyUnitStack.get()

				SetUnitX(dummy, GetUnitX(iter))
				SetUnitY(dummy, GetUnitY(iter))
				UnitAddAbility(dummy, NET_SLOW_ID)
				IssueTargetOrder(dummy, "slow", iter)
				DamageOverTime.add(null, iter, 0, 9, NET_FX, DamageOverTime.FX_FLAG_COORDS, EXCLUSIVITY_NET)

				DummyUnitStack.delayedRelease(dummy, 3.)
			end

			GroupRemoveUnit(grp, iter)
		end
	end

	function periodic takes nothing returns nothing
		integer index = 0
		NetData tempDat
		unit iter

		loop
			exitwhen index > dbIndex

			tempDat = dataDb[index]

			real dX = tempDat.dummy.getX()
			real dY = tempDat.dummy.getY()
			bool hit = false

			tempDat.dummy..setX(dX + tempDat.delX)
						 ..setY(dY + tempDat.delY)
						 ..setFlyHeight(50., 0.)
			tempDat.distance -= NET_VELOCITY * CLOCK_PERIOD

			// Check if a beast has been struck.
			GroupEnumUnitsInRange(grp, dX, dY, COLLISION_RADIUS, null)
			loop
				iter = FirstOfGroup(grp)
				exitwhen iter == null

				if IsUnitEnemy(iter, tempDat.caster) and UnitAlive(iter)
					hit = true
					exitwhen true
				end

				GroupRemoveUnit(grp, iter)
			end

			if hit or tempDat.distance < 0.
				strikeUnits(dX, dY, tempDat.caster)

				// Clean up this instance
				tempDat.attached.destr()
				DummyUnitStack.delayedRelease(tempDat.dummy, 3.)

				dataDb[index] = dataDb[dbIndex]
				dbIndex--
				index--

				if dbIndex == -1
					PauseTimer(clock)
				end
			end

			index++
		end
	end

	function act takes nothing returns nothing
		NetData nd

		if GetSpellAbilityId() == NET_ID then
			unit tU = GetTriggerUnit()
			real uX = GetUnitX(tU)
			real uY = GetUnitY(tU)
			real ang = Atan2(GetSpellTargetY() - uY, GetSpellTargetX() - uX)

			nd = new NetData()
			nd.delX = NET_VELOCITY * Cos(ang) * CLOCK_PERIOD
			nd.delY = NET_VELOCITY * Sin(ang) * CLOCK_PERIOD
			nd.caster = GetOwningPlayer(tU)
			nd.distance = MAX_DISTANCE

			nd.dummy = DummyUnitStack.get()
			nd.attached = AddSpecialEffectTarget(NET_MODEL, nd.dummy, "origin")
			nd.dummy..setX(uX)..setY(uY)..setOwner(nd.caster, true)

			dbIndex++
			dataDb[dbIndex] = nd

			if dbIndex == 0
				TimerStart(clock, CLOCK_PERIOD, true, function periodic)
			end
		end
	end

	function init takes nothing returns nothing
		registerPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function act)
	end
endscope
