freeslot("sfx_timere", "sfx_wartup")

-- Refer to Lua.hmt_speedmult and Lua.hmt_speedhalf.
local STARTTIME = 6*TICRATE + (3*TICRATE/4)

addHook("ThinkFrame", function()
	if leveltime ~= 2 then return end
	for p in players.iterate do
		if p.spectator or not (p.mo and p.mo.valid) then continue end
		p.spedmode = {}
		p.spedmode.score = 0
		p.spedmode.laps = 0
		p.spedmode.decreasetimer = 0
		p.spedmode.decreasespeed = 0
		p.spedmode.increasetimer = 0
		p.spedmode.increasespeed = 0
		p.spedmode.color = SKINCOLOR_WHITE
	end
	server.spedmodeendtimer = 0
	if not mapheaderinfo[gamemap].hmt_speedmult then return end
end)

addHook("ThinkFrame", function()
	if not (CV_FindVar("elimination") and CV_FindVar("elimination").value) then 
		hud.enable("time")
		hud.enable("gametypeinfo")
	end
	if not mapheaderinfo[gamemap].hmt_speedmult then return end
	-- Time doesnt really matter on this map, so begone
	if sTrack then
		sTrack.globalEasyTimeData[tostring(gamemap)] = nil
	end
	hud.disable("time")
	hud.disable("gametypeinfo")
end)

addHook("PlayerSpin", function(p, inf, source)
	if not (p and p.valid) then return end
	if not mapheaderinfo[gamemap].hmt_speedmult then return end
	if source and source.valid and source.player and source.player.valid then
		if source.player ~= p then
			source.player.spedmode.increasetimer = TICRATE
			source.player.spedmode.increasespeed = 5
			S_StartSound(source.player.mo, sfx_wartup, source.player)
		end
	end
	p.spedmode.decreasetimer = TICRATE/2
	p.spedmode.decreasespeed = p.speed
end)

addHook("PlayerExplode", function(p, inf, source)
	if not (p and p.valid) then return end
	if not mapheaderinfo[gamemap].hmt_speedmult then return end
	if source and source.valid and source.player and source.player.valid then
		if source.player ~= p then
			source.player.spedmode.increasetimer = TICRATE
			source.player.spedmode.increasespeed = 5
			S_StartSound(source.player.mo, sfx_wartup, source.player)
		end
	end
	p.spedmode.decreasetimer = TICRATE/2
	p.spedmode.decreasespeed = p.speed
end)

addHook("ThinkFrame", function()
	if not mapheaderinfo[gamemap].hmt_speedmult then return end
	
	if leveltime < STARTTIME then 
		return 
	end
	local positions = {}
	for p in players.iterate do
		if p.spectator or not (p.mo and p.mo.valid) or p.pflags & PF_TIMEOVER then continue end
		p.spedmode = $ or {}
		p.spedmode.score = $ or 0
		p.spedmode.laps = $ or 0
		p.spedmode.decreasetimer = $ or 0
		p.spedmode.increasetimer = $ or 0
		p.spedmode.increasespeed = $ or 0
		p.spedmode.decreasespeed = $ or 0
		p.spedmode.color = $ or SKINCOLOR_WHITE
		
		if (p.mo.eflags & MFE_JUSTBOUNCEDWALL or p.kartstuff[k_justbumped]) and not p.exiting then
			p.spedmode.decreasetimer = TICRATE / 2
			-- prevent cheesing
			if p.speed > p.spedmode.decreasespeed then
				p.spedmode.decreasespeed = p.speed
			end
		end
		
		p.spedmode.color = SKINCOLOR_JAWZ
		
		if leveltime % 2 == 0 then
			if p.speed > 0 and P_IsObjectOnGround(p.mo) and not p.exiting then
				p.spedmode.score = $ + FixedDiv(p.speed, mapobjectscale)/(FRACUNIT*8)
				if p.cmd.buttons & BT_BRAKE then
					p.spedmode.score = $ - ((FixedDiv(p.speed, mapobjectscale)/(FRACUNIT*8))/2)
					p.spedmode.color = SKINCOLOR_BLUE
				end
			end
		end
		
		if p.spedmode.decreasetimer > 0 and not p.exiting then
			p.spedmode.score = $ - FixedDiv(p.spedmode.decreasespeed/8, mapobjectscale)/(FRACUNIT)
			p.spedmode.color = SKINCOLOR_RED
			p.spedmode.decreasetimer = $ - 1
			if p.spedmode.decreasetimer <= 0 then
				p.spedmode.decreasespeed = 0
			end
		end
		
		if p.spedmode.increasetimer > 0 and not p.exiting then
			p.spedmode.score = $ + p.spedmode.increasespeed
			p.spedmode.color = SKINCOLOR_LIME
			p.spedmode.increasetimer = $ - 1
			if p.spedmode.increasetimer <= 0 then
				p.spedmode.increasespeed = 0
			end
		end
		
		if leveltime > 177*TICRATE + STARTTIME and (leveltime + STARTTIME - TICRATE/2 % 35 < TICRATE/2) then
			p.spedmode.color = SKINCOLOR_CRIMSON
		end
		
		if p.exiting then
			p.spedmode.color = SKINCOLOR_LILAC
		end
		
		p.kartstuff[k_lapanimation] = 0
		
		-- infinite lap system
		if p.laps > 0 then
			p.spedmode.laps = $ + 1
			S_StartSound(p.mo, sfx_wartup, p)
			-- 525 point bonus
			p.spedmode.increasetimer = TICRATE
			p.spedmode.increasespeed = 15
			K_PlayBoostTaunt(p.mo)
			p.laps = 0
		end
		
		table.insert(positions, p)
	end
	
	-- literally all this does is sort our positions table based on our score
	-- then set our position to that
	if leveltime < 180*TICRATE + STARTTIME then
		table.sort(positions, function(a, b) return a.spedmode.score > b.spedmode.score end)
		
		for k, v in ipairs(positions) do
			-- set our positions
			v.kartstuff[k_position] = k
			v.kartstuff[k_oldposition] = k
			v.savedposition = k
			v.savedpositiondelay = $ or 0
			if v.savedlastposition and v.savedlastposition ~= v.savedposition then
				K_PlayOvertakeSound(v.mo)
				v.savedpositiondelay = 10
			end
			v.kartstuff[k_positiondelay] = v.savedpositiondelay
			if v.savedpositiondelay > 0 then
				v.savedpositiondelay = $ - 1
			end
			v.savedlastposition = k
		end
	end
	
	-- HURRY UP
	if leveltime == 177*TICRATE + STARTTIME or
		leveltime == 178*TICRATE + STARTTIME or
		leveltime == 179*TICRATE + STARTTIME then
		S_StartSound(nil, sfx_timere)
	end
	
	-- level ends here
	if leveltime >= 180*TICRATE + STARTTIME then
		server.spedmodeendtimer = $ or 0
		server.spedmodeendtimer = $ + 1
		for p in players.iterate do
			if p.spectator or not (p.mo and p.mo.valid) then continue end
			if p.exiting ~= 1 then
				p.exiting = 1
			end
			p.kartstuff[k_position] = p.savedposition
			if p.exiting == 1 then
				if p.kartstuff[k_position] == 1 then
					S_ChangeMusic("KBWIN", false, p)
				else
					S_ChangeMusic("KBOK", false, p)
				end
				if not p.mo.signspawned then
					P_PlayVictorySound(p.mo)
					local exitsign = P_SpawnMobj(p.mo.x, p.mo.y, p.mo.z+(mapobjectscale*400),MT_SIGN)
					exitsign.target = p.mo
					exitsign.state = S_SIGN1
					exitsign.movefactor = p.mo.floorz
					exitsign.movecount = 1
					p.mo.signspawned = true
				end
			end
		end
	end
	
	if server.spedmodeendtimer and server.spedmodeendtimer > 8*TICRATE and not server.hmfinishstate then
		local hmscoreboard = CV_FindVar("hm_intermission")
		for p in players.iterate do
			if p.mo and p.mo.valid then
				p.realtime = $ + (TICRATE*p.savedposition)
			end
		end
		if hmscoreboard and hmscoreboard.value then
			if not server.hmfinishstate then
				hm_forceTriggerResults()
			end
		else
			G_ExitLevel()
		end
		for p in players.iterate do
			if p.valid then
				S_StopMusic(p)
				S_ChangeMusic("RACENT", true, p)
			end
		end
	end
end)

--Same but each digit in table format for HUD record display: taken from StatTracker
local function buildTimeStringTable(x)
	if x == nil or x == 99999999 then return nil end
	return {string.sub(string.format("%02d", G_TicsToMinutes(x)), 1, 1), string.sub(string.format("%02d", G_TicsToMinutes(x)), 2, 2), string.sub(string.format("%02d", G_TicsToSeconds(x)), 1, 1), string.sub(string.format("%02d", G_TicsToSeconds(x)), 2, 2), string.sub(string.format("%02d", G_TicsToCentiseconds(x)), 1, 1), string.sub(string.format("%02d", G_TicsToCentiseconds(x)), 2, 2)}
end

-- transcribed from k_kart.c at line # 6983
local function K_CalcSplitFlags(snapflags, c)
	local splitflags = 0
	
	if splitscreen == 0 then
		return snapflags
	end
	
	if splitscreen < 2 then
		if c.pnum == 2 then 
			splitflags = $ | V_SPLITSCREEN
		end
	else
		if c.pnum == 3 or (splitscreen == 3 and c.pnum == 4) then
			splitflags = $ | V_SPLITSCREEN
		end
		if c.pnum == 2 or (splitscreen == 3 and c.pnum == 4) then
			splitflags = $ | V_HORZSCREEN
		end
	end
	
	if splitflags & V_SPLITSCREEN then
		splitflags = $ & ~V_SNAPTOTOP
	else
		splitflags = $ & ~V_SNAPTOBOTTOM
	end
	
	if splitscreen > 1 then
		if splitflags & V_HORZSCREEN then
			splitflags = $ & ~V_SNAPTOLEFT
		else
			splitflags = $ & ~V_SNAPTORIGHT
		end
	end
	
	return (splitflags | snapflags)
end

local function GetTimerFlags(v, p, c)
	if splitscreen < 2 then -- don't change shit for THIS splitscreen.
		return (320/2), 12, K_CalcSplitFlags(V_SNAPTOTOP, c), false
	else -- now we're having a fun game.
		if c.pnum == 1 or c.pnum == 3 then -- If we are P1 or P3...
			return 80, 12, (c.pnum == 3 and V_SPLITSCREEN or V_SNAPTOTOP)|V_SNAPTOLEFT, true
		else -- else, that means we're P2 or P4.
			return (320/2) + 80, 12, (c.pnum == 4 and V_SPLITSCREEN or V_SNAPTOTOP)|V_SNAPTORIGHT, true
		end
	end
end

local function GetLapCounterFlags(v, p, c)
	if splitscreen == 1 then -- don't change shit for THIS splitscreen.
		if c.pnum == 1 then 
			return 9, 71, V_SNAPTOBOTTOM|V_SNAPTOLEFT, false
		else
			return 9, 171, V_SNAPTOBOTTOM|V_SNAPTOLEFT, false
		end
	elseif splitscreen == 0 then
		return 9, 171, V_SNAPTOBOTTOM|V_SNAPTOLEFT, false
	end
end

hud.add(function(v, p, c)
	if not mapheaderinfo[gamemap].hmt_speedmult then return end
	
	local fx, fy, fflags, fminimal = GetTimerFlags(v, p, c)
	local stringTime = buildTimeStringTable(p.realtime)
	
	-- Score + Time
	-- we are not minimal
	if not fminimal then
		v.draw(fx - 58, fy - 3, v.cachePatch("K_SCTIME"), fflags|V_HUDTRANS, v.getColormap(TC_DEFAULT, SKINCOLOR_SKUNK))
		v.draw(fx - 64, fy + 16, v.cachePatch("2STTMBG"), fflags|V_HUDTRANS)
	end
	
	-- draw the actual score
	drawStringMkII(fx, fy, tostring(p.spedmode.score), FONT_KART2, fflags|V_HUDTRANS, {align = "center",color=p.spedmode.color})
	-- OPPRNK font only has digits 0-9 so this is printed one at a time
	local text_offset = fx - 33
	-- what is this bullshit
	v.draw(text_offset + 6, fy + 16, v.cachePatch("OPPRNK0"..stringTime[1]), fflags|V_HUDTRANS)
	v.draw(text_offset + 12, fy + 16, v.cachePatch("OPPRNK0"..stringTime[2]), fflags|V_HUDTRANS)
	v.draw(text_offset + 20, fy + 16, v.cachePatch("2STMNMK"), fflags|V_HUDTRANS)
	v.draw(text_offset + 26, fy + 16, v.cachePatch("OPPRNK0"..stringTime[3]), fflags|V_HUDTRANS)
	v.draw(text_offset + 32, fy + 16, v.cachePatch("OPPRNK0"..stringTime[4]), fflags|V_HUDTRANS)
	v.draw(text_offset + 40, fy + 16, v.cachePatch("2STSCMK"), fflags|V_HUDTRANS)
	v.draw(text_offset + 46, fy + 16, v.cachePatch("OPPRNK0"..stringTime[5]), fflags|V_HUDTRANS)
	v.draw(text_offset + 52, fy + 16, v.cachePatch("OPPRNK0"..stringTime[6]), fflags|V_HUDTRANS)
	
	-- Lap counter
	-- no support for 3p and 4p splitscreen because fuck you
	if splitscreen < 2 then
		fx, fy, fflags = GetLapCounterFlags(v, p, c)
		local finallaps = tostring(p.spedmode.laps + 1)
		if p.spedmode.laps < 9 then -- make it fancy
			finallaps = "0"..finallaps
		end
		
		v.draw(fx, fy, v.cachePatch("K_STLAPS"), fflags|V_HUDTRANS)
		drawStringMkII(fx + 38, fy + 3, finallaps, FONT_KART2, fflags|V_HUDTRANS, {align = "left",color=p.spedmode.color})
	end
	
	if leveltime <= STARTTIME then 
		v.drawString(160, 180, "Level ends in 3 minutes!", V_SNAPTOBOTTOM|V_ALLOWLOWERCASE|V_YELLOWMAP, "center") 
	end
end)

/*
hud.add(function(v, player, c)
	if not mapheaderinfo[gamemap].hmt_speedmult then return end
	if leveltime <= STARTTIME then v.drawString(160, 180, "Level ends in 3 minutes!", V_SNAPTOBOTTOM|V_ALLOWLOWERCASE|V_YELLOWMAP, "center") end
	if player.spectator then return end
	local yoffset = 0
	local yoffsethalf = 1
	if splitscreen > 1 then return end -- NO FULL SPLITSCREEN.
	for p in displayplayers.iterate do
		if p.spedmode and mapheaderinfo[gamemap].hmt_speedmult then
			-- actual score
			v.draw(102, 9+yoffset, v.cachePatch("K_SCTIME"), V_SNAPTOTOP|V_HUDTRANS, v.getColormap(TC_DEFAULT, SKINCOLOR_SKUNK))
			drawStringMkII((320/2), 12+yoffset, tostring(p.spedmode.score), FONT_KART2, V_SNAPTOTOP|V_HUDTRANS, {align = "center",color=p.spedmode.color})
			v.draw(160 - 64, 28+yoffset, v.cachePatch("2STTMBG"), V_SNAPTOTOP|V_HUDTRANS)
			local offset = 37 - 6
			-- OPPRNK font only has digits 0-9 so this is printed one at a time
			local font = "OPPRNK"
			v.draw(160-64 + 6 + offset, 28+yoffset, v.cachePatch("OPPRNK0"..stringTime[1]), V_SNAPTOTOP|V_HUDTRANS)
			v.draw(160-64 + 12 + offset, 28+yoffset, v.cachePatch("OPPRNK0"..stringTime[2]), V_SNAPTOTOP|V_HUDTRANS)
			v.draw(160-64 + 20 + offset, 28+yoffset, v.cachePatch("2STMNMK"), V_SNAPTOTOP|V_HUDTRANS)
			v.draw(160-64 + 26 + offset, 28+yoffset, v.cachePatch("OPPRNK0"..stringTime[3]), V_SNAPTOTOP|V_HUDTRANS)
			v.draw(160-64 + 32 + offset, 28+yoffset, v.cachePatch("OPPRNK0"..stringTime[4]), V_SNAPTOTOP|V_HUDTRANS)
			v.draw(160-64 + 40 + offset, 28+yoffset, v.cachePatch("2STSCMK"), V_SNAPTOTOP|V_HUDTRANS)
			v.draw(160-64 + 46 + offset, 28+yoffset, v.cachePatch("OPPRNK0"..stringTime[5]), V_SNAPTOTOP|V_HUDTRANS)
			v.draw(160-64 + 52 + offset, 28+yoffset, v.cachePatch("OPPRNK0"..stringTime[6]), V_SNAPTOTOP|V_HUDTRANS)
			-- laps
			local finallaps = tostring(p.spedmode.laps + 1)
			if p.spedmode.laps < 9 then
				finallaps = "0"..finallaps
			end
			v.draw(9, (171/yoffsethalf), v.cachePatch("K_STLAPS"), V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS)
			drawStringMkII(9+38, (171/yoffsethalf)+3, finallaps, FONT_KART2, V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS, {align = "left",color=p.spedmode.color})
		end
		yoffset = $ + 100
		yoffsethalf = $ + 1
	end
end)
*/