freeslot("sfx_ptpstp", "sfx_ptpmch", "sfx_ptpczy", "SPR_PTPG")
CV_RegisterVar({
	name = "PTP!Peppino Afterimages",
	defaultvalue = "On",
	possiblevalue = CV_OnOff,
	description = "Enables PTP!Peppino's afterimages."
})
CV_RegisterVar({
	name = "PTP!Peppino Footsteps",
	defaultvalue = "On",
	possiblevalue = CV_OnOff,
	description = "Enables PTP!Peppino's running sounds."
})

sfxinfo[sfx_ptpstp].singular = false
sfxinfo[sfx_ptpstp].priority = 48
sfxinfo[sfx_ptpstp].flags = SF_X2AWAYSOUND
sfxinfo[sfx_ptpstp].caption = "Italian strolling"

sfxinfo[sfx_ptpmch].singular = false
sfxinfo[sfx_ptpmch].priority = 48
sfxinfo[sfx_ptpmch].flags = SF_X2AWAYSOUND
sfxinfo[sfx_ptpmch].caption = "Italian approaching"

sfxinfo[sfx_ptpczy].singular = false
sfxinfo[sfx_ptpczy].priority = 48
sfxinfo[sfx_ptpczy].flags = SF_X2AWAYSOUND
sfxinfo[sfx_ptpczy].caption = "Italian arriving"

SIMPLER_ANIMAL_SETUP("ptppeppino", "FALL", "JUMP", "SPNG", "SKID", "ATTA", "MACH", "WINR", "FFAL", "HANG", "RCKT", "LKBK", "TBLE", "BNGE") -- Initiate character and custom Sprite2s.

local helpers = _G["SIMPLER_ANIMAL_HELPERS"]

local GS_CEREMONY = GS_CEREMONY or 9
local BT_LOOKBACK = BT_LOOKBACK or 1<<5
local GTR_CLOSERPLAYERS = GTR_CLOSERPLAYERS or 1<<4

local lrng = {}
local afterimage_max = 10
local afterimage_fuse = 10
local afterimage_delay = 4
local afterimage_flash = 2
local afterimage_colours = {SKINCOLOR_RED, SKINCOLOR_GREEN, SKINCOLOR_PURPLE}

local run_sounds = {
	sfx_ptpstp, 
	sfx_ptpmch,
	sfx_ptpczy
}

local function PTPPeppino_LousyEBrake(player) -- No EBrake getter: forget the unforeseen consequences.
	return player.cmd.buttons & (BT_ACCELERATE | BT_BRAKE)
		and not helpers.isAirborne(player.mo)
		and player.pflags & PF_NOFASTFALL
		and player.fastfall == 0
end

local function PTPPeppino_PickSound(mobj, snd)
	if (snd == nil or snd ~= mobj.PTPVars.currentsnd) then
		mobj.PTPVars.runtimer = 0
	end
	
	mobj.PTPVars.currentsnd = snd
	for i = 1, #run_sounds do
		if (snd == nil or snd ~= run_sounds[i]) then
			S_StopSoundByID(mobj, run_sounds[i])
		end
	end
end

local function PTPPeppino_RunningSound(mobj, sprite2)
	local player = helpers.getPlayer(mobj)
	mobj.PTPVars.runtimer = $ + 1
	
	// Choose your sound
	if (not CV_FindVar("PTP!Peppino Footsteps").value) then
		PTPPeppino_PickSound(mobj, nil)
		return
	end
	
	if (sprite2 == SPR2_MACH) then
		PTPPeppino_PickSound(mobj, sfx_ptpczy)
		mobj.PTPVars.runtimer = $ % 103
	elseif (sprite2 == SPR2_FSTN and not player.spindash and not PTPPeppino_LousyEBrake(player)) then
		PTPPeppino_PickSound(mobj, sfx_ptpmch)
		mobj.PTPVars.runtimer = $ % 52
	elseif (sprite2 == SPR2_SLWN and not helpers.isAirborne(mobj))
		PTPPeppino_PickSound(mobj, sfx_ptpstp)
		mobj.PTPVars.runtimer = $ % 38
	else
		PTPPeppino_PickSound(mobj, nil)
		return
	end
	
	if (mobj.PTPVars.runtimer > 0) then
		return
	end

	// Engine sound conditions
	if (leveltime < 8 or player.spectator or gamestate != GS_LEVEL or player.exiting or mobj.PTPVars.currentsnd == nil) then
		return
	end

	// We need a function to return engine sound damping volume…
	S_StartSoundAtVolume(mobj, mobj.PTPVars.currentsnd, 225)
end

addHook("MapLoad", function() 
	lrng = LRNG.New(42, 32, 101)
end)

addHook("MobjThinker", function(mobj) -- Preemptively removes drift clips
	if (helpers.getSkin(mobj.target) == "ptppeppino") then
		S_StopSoundByID(mobj, sfx_tink)
		P_RemoveMobj(mobj)
	end
end, MT_DRIFTCLIP)

addHook("MobjThinker", function(mobj) -- Preemptively removes kart corpses
	if (mobj.target == nil or mobj.target.type ~= MT_PLAYER) then
		return
	elseif (helpers.getSkin(mobj.target) == "ptppeppino") then
		if (mobj.extravalue2 <= 0 or mobj.PTP_iskart or mobj.threshold or mobj.health <= 1) then
			return
		end
		
		mobj.state = S_KART_LEFTOVER_NOTIRES
		mobj.sprite = SPR_PTPG
		
		// Recreate shrapnel and explosion
		local ang = 360 / 6
		for i = 0, 5 do
			local degr = (ang * i) % 360
			local x = P_SpawnMobjFromMobjUnscaled(mobj, 0, 0, 0, MT_KART_PARTICLE)
			if (x) then
				x.sprite = SPR_KRBM
				x.color = mobj.color
				x.frame = FF_SEMIBRIGHT
				x.lightlevel = 112
				x.scale = FixedMul($, (3 * FRACUNIT / 2)) -- FRICK YOU!

				P_InstaThrust(x, mobj.angle + FixedAngle(degr * FRACUNIT), 8 * mapobjectscale)
				x.momz = LRNG.NextRange(lrng, 22, 28) * mapobjectscale * 2

				x.angle = LRNG.NextInt(lrng)
				x.rollangle = LRNG.NextInt(lrng)

				x.renderflags = $ | RF_DONTDRAW
			end
		end
		
		ang = 360 / 4
		for i = 0, 3 do
			local degr = (45 + (ang * i)) % 360
			local x = P_SpawnMobjFromMobjUnscaled(mobj, 0, 0, mobj.height, MT_KART_PARTICLE)
			if (x) then
				x.flags = $ | (MF_NOGRAVITY | MF_NOCLIPHEIGHT)
				x.height = 0
				x.scale = $ * 2
				x.angle = FixedAngle(degr * FRACUNIT)
				x.state = S_KART_XPL01
				x.renderflags = $ | RF_REDUCEVFX
			end
		end
		
		mobj.scale = $ * 3 / 2
		mobj.health = 1
		mobj.threshold = 20
		if (gametyperules & GTR_CLOSERPLAYERS) then
			mobj.movecount = 10 * TICRATE
		else
			mobj.movecount = 20 * TICRATE
		end

		if (not CV_FindVar("reducevfx").value) then
			S_StartSoundAtVolume(mobj, sfx_die00, 255)
		end

		mobj.target.state = S_KART_DEAD
		
		mobj.PTP_iskart = true
	end
end, MT_KART_LEFTOVER)

addHook("MobjThinker", function(mobj) -- PT afterimage
	if (mobj.target == nil or mobj.target.type ~= MT_PLAYER) then
		return
	elseif (helpers.getSkin(mobj.target) == "ptppeppino") then
		local player = helpers.getPlayer(mobj.target)
		if (mobj.extravalue1 >= 1) then
			if (not mobj.PTP_isafterimage or player.speed <= 0) then
				// You dare bring light to my lair? You must DIE.
				P_RemoveMobj(mobj)
				return
			end
			mobj.renderflags = $ & ~RF_DONTDRAW
			if (mobj.target.renderflags & RF_DONTDRAW or leveltime % (afterimage_flash * 2) >= afterimage_flash) then
				mobj.renderflags = $ | RF_DONTDRAW
			end
		end
	end
end, MT_GHOST)

addHook("MobjThinker", function (mobj) -- Player bubble shield trap sprite
	if (mobj.extravalue1 and helpers.getSkin(mobj.tracer) == "ptppeppino") then
		mobj.tracer.PTPVars.bubble = true
	end
end, MT_BUBBLESHIELDTRAP)

addHook("MobjThinker", function (mobj) -- Player pulley hook sprite
	if (mobj.target and helpers.getSkin(mobj.target) == "ptppeppino") then
		mobj.target.PTPVars.hook = true
	end
end, MT_PULLUPHOOK)

addHook("MobjThinker", function (mobj) -- Player instawhip
	if (mobj.target and helpers.getSkin(mobj.target) == "ptppeppino") then
		mobj.target.PTPVars.whip = mobj
	end
end, MT_INSTAWHIP)

function SIMPLER_ANIMAL_HOOKS.ptp_peppino_hook(mobj)
	if (helpers.getSkin(mobj) == "ptppeppino") then
		local results = {nil, nil, nil, nil}
		local player = helpers.getPlayer(mobj)
		if (mobj.PTPVars == nil) then
			mobj.PTPVars = {
				["spawntimer"] = 0, 
				["runtimer"] = 0,
				["bubble"] = false,
				["hook"] = false,
				["whip"] = nil,
			}
		end
		
		for i = 0,116 do -- 108, really? (Sorry, just a bit annoyed)
			S_StopSoundByID(mobj, sfx_krta00 + i) -- Stop engine sounds.
		end
    
		if (CV_FindVar("PTP!Peppino Afterimages").value and player.speed > 0 and mobj.PTPVars.spawntimer <= 0) then
			local fastspeed = (K_GetKartSpeed(player, false, true) * 17) / 20
			local trans = min(FixedInt(FixedDiv(player.speed * afterimage_max, fastspeed)), afterimage_max)
			mobj.PTPVars.spawntimer = afterimage_delay
			if trans > 0 then
				local transflag = (NUMTRANSMAPS - trans) << RF_TRANSSHIFT
				local afterimage = P_SpawnGhostMobj(mobj)
				afterimage.PTP_isafterimage = true
				if mobj.Simpler_AnimaL.last_sequence ~= nil and mobj.Simpler_AnimaL.last_frame ~= nil then
					afterimage.sprite2 = spr2names[mobj.Simpler_AnimaL.last_sequence]
					afterimage.frame = mobj.Simpler_AnimaL.last_frame
				end
				afterimage.extravalue1 = 1
				afterimage.fuse = afterimage_fuse
				afterimage.colorized = true
				afterimage.color = afterimage_colours[LRNG.NextRange(lrng, 1, #afterimage_colours)]
				afterimage.renderflags = (mobj.renderflags & ~RF_TRANSMASK) | transflag
			end
		end
		
		// Hopefully this doesn't break anything
		if (mobj.sprite2 == SPR2_STIL) then -- https://git.do.srb2.org/KartKrew/RingRacers/-/issues/151
			mobj.sprite2 = SPR2_STIN
			results = {"STIN", A, A, B}
		end
		
		local turbinestuff = (player.seasaw -- Gust Planet seasaws/Flying Battery seasaws/Turbines (including Launch Base)
			or mobj.k_seasaw 
			or (mobj.tracer and mobj.tracer.type == MT_WATERPALACETURBINE)) 
		local winner = (not K_IsPlayerLosing(player) -- Be more specific
			and (gamestate == GS_CEREMONY or player.exiting)
			and player.cmd.buttons == 0
			and P_IsObjectOnGround(mobj)
			and player.position == 1
			and player.speed == 0)
		
		if (mobj.sprite2 == SPR2_GOAL) then
		elseif ((mobj.sprite2 == SPR2_SPIN and not turbinestuff) or mobj.sprite2 == SPR2_DEAD) then
		elseif (player.respawn.timer) then
			results = {"SPIN", A, A, B}
		elseif (player.bungee) then -- Leaf Storm Bungees
			results = {"BNGE", A, A, P}
		elseif (turbinestuff or (mobj.PTPVars.bubble and mobj.flags & MF_NOGRAVITY)) then -- Also include Bubble Shield
			helpers.SquaredSlow(mobj, -1)
			results = {"TBLE", A, A, L}
		elseif (player.dlzrocket) then -- Deadline Zone/Lost Colony rockets
			results = {"RCKT", A, A, B}
		elseif (player.rideroid or mobj.PTPVars.hook) then -- Las Vegas RideRoid/Pulleys
			results = {"HANG", A, A, B}
		elseif ((mobj.PTPVars.whip and mobj.PTPVars.whip.valid) or helpers.isTricking(mobj)) then
			results = {"ATTA", A, A, A}
		elseif (player.drift == 0 and (player.spindash or PTPPeppino_LousyEBrake(player))) then
			results = {"FSTN", A, A, B}
		elseif (player.fastfall) then
			results = {"FFAL", A, A, H}
		elseif (winner) then
			results = {"WINR", A, A, B}
		elseif (helpers.isAirborne(mobj)) then
			helpers.SquaredSlow(mobj, -1)
			if (P_GetMobjGravity(mobj) == 0) then // 0G (Gravtech Dimension 5)
				results = {"RCKT", A, A, B}
			elseif (abs(mobj.momz) <= FRACUNIT) then
				results = {"JUMP", A, A, B}
			elseif (helpers.isAscending(mobj)) then
				results = {"SPNG", A, A, H}
			else
				results = {"FALL", A, A, B}
			end
		elseif (mobj.sprite2 == SPR2_FSTN and player.speed >= (K_GetKartSpeed(player, false, true) * 14) / 10) then -- Wanted to check if faster than tripwire BUT WE DON'T HAVE THAT
			helpers.SquaredSlow(mobj, -1)
			results = {"MACH", A, A, D}
		elseif (abs(player.speed) > FRACUNIT and (mobj.sprite2 == SPR2_SLWN or mobj.sprite2 == SPR2_FSTN)) then
			if (helpers.isMovingForward(mobj)) then
				if (player.cmd.buttons & BT_BRAKE) then
					helpers.SquaredSlow(mobj, -1)
					results = {"SKID", A, A, B}
				end
			elseif (player.cmd.buttons & BT_ACCELERATE) then -- Moving sideways/backwards.
				helpers.SquaredSlow(mobj, -1)
				results = {"SKID", A, A, B}
			else
				results = {"SLWN", L, L, A}
			end
		elseif (abs(player.speed) <= FRACUNIT / 10) then 
			if (player.cmd.buttons & BT_LOOKBACK) then // isLookingBack is broken.
				results = {"LKBK", A, A, B}
			else
				results = {"STIN", A, A, B}
			end
		end
		
		local newsprite = mobj.sprite2
		if (results[1]) then
			newsprite = spr2names[results[1]]
		end
		
		mobj.PTPVars.spawntimer = max(mobj.PTPVars.spawntimer - 1, 0)
		mobj.PTPVars.bubble = false
		mobj.PTPVars.hook = false
		mobj.PTPVars.whip = nil
		PTPPeppino_RunningSound(mobj, newsprite)
		return unpack(results)
	end
end