-- Shino Lua by j2b2
local SHINO_ABILITY_COOLDOWN = TICRATE/3
local SHINO_ABILITY_CHARGES = 3
local SHINO_RING_COOLDOWN = TICRATE*3
local SHINO_SUPER_ABILITY_CHARGES = SHINO_ABILITY_CHARGES*2
local SHINO_GLIDE_MULTIPLIER = FixedDiv(FRACUNIT*2, FRACUNIT*3)

local FRACUNIT_ONE_THIRD = FRACUNIT/3
local FRACUNIT10 = 10*FRACUNIT

freeslot("MT_SHINO_RING_VERTICAL","MT_SHINO_RING_HORIZONTAL","MT_SHINO_RING_PROJECTILE")

local function IsValid(a)
	if a ~= nil then return a.valid end
	return false
end

function A_ShinoRing(mo)
	local heightBefore = mo.height
	local percent = min(FixedDiv(mo.fuse*FRACUNIT, mo.info.mass*FRACUNIT) + FRACUNIT_ONE_THIRD, FRACUNIT)
	mo.scale = percent
	mo.z = $ + (heightBefore - mo.height)/2
end

function A_ShinoRingStar(mo)
	if IsValid(mo) and IsValid(mo.target) then
		local target = mo.target
		local percent = min(FixedDiv(target.fuse*FRACUNIT, target.info.mass*FRACUNIT)+FRACUNIT_ONE_THIRD, FRACUNIT)
		local distance = FixedMul(target.info.radius,  percent)
		local x, y, z
		local angle = -(mo.angle - ANGLE_90)
		if target.type == MT_SHINO_RING_HORIZONTAL or target.type == MT_SHINO_RING_PROJECTILE then
			x = target.x + FixedMul(FixedMul(cos(mo.movedir),sin(angle)),distance)
			y = target.y + FixedMul(FixedMul(cos(mo.movedir),cos(angle)),distance)
			z = target.z + target.height/2 + FixedMul(sin(mo.movedir),distance)
		else
			x = target.x + FixedMul(cos(mo.movedir),distance)
			y = target.y + FixedMul(sin(mo.movedir),distance)
			z = target.z + target.height/2
		end
		P_TeleportMove(mo,x,y,z)
	else
		mo.state = S_NULL
	end
end

local function ShinoRingSpawnStars(ring)
	for i = 0, 360, 30 do
		local star = P_SpawnMobjFromMobj(ring, 0, 0, FRACUNIT10, MT_SHINO_RING_STAR)
		if (i % 60) > 0 then
			P_SetMobjStateNF(star,S_SHINO_RING_STAR3)
		end
		star.target = ring
		star.angle = ring.angle
		star.movedir = i*ANG1
		star.colorized = true
		star.color = SKINCOLOR_ICY
	end
end

local function ShinoRingTouchSpecial(ring,toucher)
	if ring.cooldown == nil then ring.cooldown = {} end
	if ring.valid and toucher.valid then
		if ring.cooldown[toucher] == nil then ring.cooldown[toucher] = 0 end
		if ring.cooldown[toucher] < leveltime then
			toucher.z = ring.z + ring.height/2 - toucher.height/2
			P_TryMove(toucher, ring.x, 	ring.y)
			local launchSpeed = ring.info.damage
			local launchSpeedVert = 5*FRACUNIT
			if toucher.eflags & MFE_UNDERWATER then
				launchSpeed = FixedDiv($, 2*FRACUNIT)
				launchSpeedVert = 2*FRACUNIT
			end
			if ring.type == MT_SHINO_RING_VERTICAL then
				if toucher.momz < 0 then toucher.momz = 0 end
				P_SetObjectMomZ(toucher, launchSpeed)
			else
				P_SetObjectMomZ(toucher, launchSpeedVert)
				toucher.angle = ring.angle - ANGLE_90
				P_InstaThrust(toucher, ring.angle - ANGLE_90, launchSpeed)
			end
			S_StartSound(toucher,sfx_spdpad)
			ring.cooldown[toucher] = leveltime+SHINO_RING_COOLDOWN
		end
	end
	return true
end

addHook("TouchSpecial",ShinoRingTouchSpecial,MT_SHINO_RING_VERTICAL)
addHook("TouchSpecial",ShinoRingTouchSpecial,MT_SHINO_RING_HORIZONTAL)
addHook("TouchSpecial",ShinoRingTouchSpecial,MT_SHINO_RING_PROJECTILE)

local Character = {
	name = "shino"
}

function Character.SpawnRing(player, dir)
	local playerMo = player.mo
	local ring
	if dir == 0 then
		ring = P_SpawnMobj(playerMo.x + playerMo.momx, playerMo.y + playerMo.momy, playerMo.z + playerMo.height/2 - mobjinfo[MT_SHINO_RING_VERTICAL].height/2 + playerMo.momz, MT_SHINO_RING_VERTICAL)
	else
		ring = P_SpawnMobj(playerMo.x + playerMo.momx, playerMo.y + playerMo.momy, playerMo.z + playerMo.height/2 - mobjinfo[MT_SHINO_RING_HORIZONTAL].height/2 + playerMo.momz, MT_SHINO_RING_HORIZONTAL)
		ring.angle = player.mo.angle + ANGLE_90
	end
	ring.fuse = ring.info.mass
	ShinoRingSpawnStars(ring)
	player.shinoCharges = player.shinoCharges-1
	player.shinoCooldown = leveltime+SHINO_ABILITY_COOLDOWN
end

function Character.EquipSkin(player, oldSkin, newSkin)
	if player.powers[pw_super] > 0 then
		player.shinoCharges = SHINO_SUPER_ABILITY_CHARGES
	else
		player.shinoCharges = SHINO_ABILITY_CHARGES
	end
	player.shinoCooldown = 0
end

function Character.UnEquipSkin(player)
	player.shinoCharges = nil
	player.shinoCooldown = nil
end

function Character.Spawn(player)
	if player.powers[pw_super] > 0 then
		player.shinoCharges = SHINO_SUPER_ABILITY_CHARGES
	else
		player.shinoCharges = SHINO_ABILITY_CHARGES
	end
	player.shinoCooldown = 0
end

function Character.Land(player)
	if player.powers[pw_super] > 0 then
		player.shinoCharges = SHINO_SUPER_ABILITY_CHARGES
	else
		player.shinoCharges = SHINO_ABILITY_CHARGES
	end
end

function Character.SuperStart(player)
	player.shinoCharges = $ + SHINO_ABILITY_CHARGES
end

function Character.SuperEnd(player)
	player.shinoCharges = max($ - 3, 0)
end

function Character.CanDamage(player, mobj)
	if player.dashmode > 3*TICRATE then return true end
end

function Character.UseShield(player)
	if player.shinoCooldown < leveltime and player.shinoCharges > 0 then
		Character.SpawnRing(player, 1)
		return true
	end
end

function Character.JumpAbility(player)
	if player.shinoCooldown < leveltime and player.shinoCharges > 0 then
		Character.SpawnRing(player, 0)
		return true
	end
end

function Character.JumpButton(player, state)
	local playerMo = player.mo
	if state == CharacterManager.STATE_HELD and player.shinoCharges <= 0 and playerMo.momz < 0 then
		playerMo.momz = $ - FixedMul(P_GetMobjGravity(playerMo), SHINO_GLIDE_MULTIPLIER)
		if leveltime % 3 == 0 then
			local star = P_SpawnMobjFromMobj(playerMo, P_RandomRange(-15, 15)*FRACUNIT, P_RandomRange(-15, 15)*FRACUNIT, P_RandomRange(-15, 15)*FRACUNIT, MT_SUPERSPARK)
			star.colorized = true
			star.color = SKINCOLOR_ICY
		end
	end
end

function Character.AirSpin(player, state)
	if state == CharacterManager.STATE_PRESSED then
		if player.shinoCooldown < leveltime and player.shinoCharges > 0 then
			Character.SpawnRing(player, 1)
			return true
		end
	end
end

CharacterManager.Register(Character)
-- Shino Lua by j2b2
