scope Knife initializer init
	import DummyUnitStack
	import TerrainData
	import DamageType
	import RegisterPlayerUnitEvent

	native UnitAlive takes unit u returns boolean

	struct DummyAlloc
		unit u
	end

	constant integer KNIFE_ID = 'A002'

	constant string FX 		    = "Abilities\\Weapons\\ShadowHunterMissile\\ShadowHunterMissile.mdl"
	constant string FX_PROC     = "Abilities\\Weapons\\HydraliskImpact\\HydraliskImpact.mdl"
	constant string FX_EXPLODED = "Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl"
	constant string FX_PROC_ATTACH_POINT = "chest"

	constant real OFFSET =             70.
	constant real EFFECT_HEIGHT =      50.
	constant real FX_DURATION =         1.6
	constant real RADIUS =             75.
	constant real EXECUTE_MAX_HEALTH = 60.
	constant real BASE_DAMAGE =        35.
	constant real SHORT_COOLDOWN =      5.

	group grp = CreateGroup()

	function resetCooldown takes nothing returns nothing
		timer tim = GetExpiredTimer()
		DummyAlloc d = tim.getData() castTo DummyAlloc
		UnitRemoveAbility(d.u, KNIFE_ID)
		UnitAddAbility(d.u, KNIFE_ID)
		destroy d
		DestroyTimer(tim)
	end

	function releaseDummy takes nothing returns nothing
		timer tim = GetExpiredTimer()
		DummyAlloc d = tim.getData() castTo DummyAlloc
		DummyUnitStack.release(d.u)
		destroy d
		DestroyTimer(tim)
	end

	function act takes nothing returns nothing
		unit tU
		unit iter
		unit selected = null
		player p
		real selectedRange = 2. * RADIUS
		real facing
		real x
		real y
		unit dum
		timer tim
		timer cdTim
		DummyAlloc d
		if GetSpellAbilityId() == KNIFE_ID then
			tU = GetTriggerUnit()
			p = GetOwningPlayer(tU)
			facing = GetUnitFacing(tU) * bj_DEGTORAD
			x = GetUnitX(tU) + OFFSET * Cos(facing)
			y = GetUnitY(tU) + OFFSET * Sin(facing)

			// fx
			dum = DummyUnitStack.get()
			SetUnitX(dum, x)
			SetUnitY(dum, y)
			SetUnitFlyHeight(dum, EFFECT_HEIGHT, 0.)
			DestroyEffect(AddSpecialEffectTarget(FX, dum, "origin"))

			tim = CreateTimer()
			d = new DummyAlloc()
			d.u = dum
			tim.setData(d castTo int)
			TimerStart(tim, FX_DURATION, false, function releaseDummy)

			// damage
			GroupEnumUnitsInRange(grp, x, y, RADIUS, null)
			loop
				iter = FirstOfGroup(grp)
				exitwhen iter == null
				if IsUnitEnemy(iter, p) and UnitAlive(iter) and IsUnitInRangeXY(iter, x, y, selectedRange) then
					selected = iter
					selectedRange = TerrainData.distanceBetweenXY(GetUnitX(iter), x, GetUnitY(iter), y)
				end
				GroupRemoveUnit(grp, iter)
			end
			if selected != null then
				DestroyEffect(AddSpecialEffectTarget(FX_PROC, selected, FX_PROC_ATTACH_POINT))
				if GetWidgetLife(selected) <= EXECUTE_MAX_HEALTH then
					DestroyEffect(AddSpecialEffectTarget(FX_EXPLODED, selected, "origin"))
					DamageType.dealCodeDamage(tU, selected, 2. * EXECUTE_MAX_HEALTH)

					// cut cooldown in half
					cdTim = CreateTimer()
					d = new DummyAlloc()
					d.u = tU
					cdTim.setData(d castTo int)
					TimerStart(cdTim, SHORT_COOLDOWN, false, function resetCooldown)
				else
					DamageType.dealCodeDamage(tU, selected, BASE_DAMAGE)
				end
			end
		end
	end

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