local function TacticalNuke(s, m, i, t, e, d) -- size, mobj, intensity, time, elimination, dye
	--bomb him
	if not m.valid then return end
	P_NukeEnemies(m, m, s<<16)
	if m.player then
		local p = m.player
		if (p == displayplayer) then
			P_StartQuake(i<<16, t)
		end
		P_FlashPal(p, d, t)
	end
	if (e == true) then
		P_DamageMobj(m, nil, nil, 10000, DMG_INSTAKILL|DMG_CANHURTSELF)
	end
end
local function clamp(x, minnum, maxnum)
	return max(minnum, min(x, maxnum)) 
end
local function isearch(tbl, val) 
	if not #tbl then return nil end
	for i,v in ipairs(tbl) do
		if v == val then return i end
	end
	return nil
end
local rtd ="\x82[\x81RTD\x82]\x80 "
local RTD_ShieldList = {
	SH_PITY = 1,
	SH_WHIRLWIND = 2,
	SH_ARMAGEDDON = 3,
	SH_ATTRACT = SH_PITY|SH_PROTECTELECTRIC,
	SH_ELEMENTAL = SH_PITY|SH_PROTECTFIRE|SH_PROTECTWATER,
	SH_FLAMEAURA = SH_PITY|SH_PROTECTFIRE,
	SH_BUBBLEWRAP = SH_PITY|SH_PROTECTWATER,
	SH_THUNDERCOIN = SH_WHIRLWIND|SH_PROTECTELECTRIC,
	SH_FORCE = 0x100
}
local RTD_ItemList = {
	"truesuper",
	"matchsuper",
	"invuln",
	"speed",
	--"forcehp", -- take more hits for free!
	"forceshield",
	"thundercoinshield", -- yes the name is long OK?
	"attractshield",
	"bubblewrapshield",
	"flameaurashield",
	"armageddonshield",
	"whirlwindshield",
	"elementalshield",
	"pityshield",
	"9999rings",
	"50rings",
	"20rings",
	"10rings",
	"5rings",
	"1ring",
	"nothing",
	"m1ring",
	"m5rings",
	"m10rings",
	"m20rings",
	"m50rings",
	"m9999rings",
	"selfnuke",
	"instadeath",
	"thousanddeaths" --This one SUCKS to get, basically a temp ban.
}
local RTD_WaitTime = 45*TICRATE
local RTD_Nightmare = false
local RTD_SuperList = {}
COM_AddCommand("rtd_nightmare", function(p, a)
	if p then
		if a and (a:lower() == "on" or a:lower() == "off") then
			if a:lower() == "on" then
				RTD_Nightmare = true
				CONS_Printf(p, "You evil,\x85 evil\x80 person.")
			else
				RTD_Nightmare = false
				CONS_Printf(p, "As it should be.")
			end
		else
			CONS_Printf(p, "Arguments:")
			CONS_Printf(p, "rtd_nightmare OPT")
			CONS_Printf(p, "\"OPT\" must be included (and either \"On\" or \"Off\")")
			CONS_Printf(p, "\x85WARNING:\x80 This command will toggle the \x8FThousand Deaths\x80 roll,")
			CONS_Printf(p, "and your players will HATE you for it.")
		end
	end
end, COM_ADMIN)
COM_AddCommand("rtd_nightmaresave", function(p, s)
	if p then
		if s then
			if p.name == s then
				if p.youarefucked then
					p.nightmaresave = true
					CONS_Printf(p, "You are now saved from \x8FThe Roll\x80.")
				else
					CONS_Printf(p, "You don't need saving.")
				end
			else
				local success
				for sp in players.iterate do
					if (sp.name == s) then
						success = true
						if sp.youarefucked then
							sp.nightmaresave = true
							CONS_Printf(p, "The player has been saved from \x8FThe Roll\x80.")
						else
							CONS_Printf(p, "That player doesn't need saving.")
						end
						return
					end
					continue
				end
				if not success then
					CONS_Printf(p, "No player found. Check your spelling.")
				end
			end
		else
			CONS_Printf(p, "Arguments:")
			CONS_Printf(p, "rtd_nightmaresave PLAYER")
			CONS_Printf(p, "Use this to save a player from the \x8FThousand Deaths\x80 roll.")
		end
	end
end, COM_ADMIN)
COM_AddCommand("rtd_wait", function(p, w, t)
	if p then
		if w and not (tonumber(w) == nil) then
			if not t then
				RTD_WaitTime = tonumber(w) * TICRATE
				CONS_Printf(p, "RTD wait time set to \$w\ seconds.")
			else
				RTD_WaitTime = tonumber(w)
				CONS_Printf(p, "RTD wait time set to \$w\ tics.")
			end
		else
			CONS_Printf(p, "Arguments:")
			CONS_Printf(p, "rtd_wait TIME [IS_TICS]")
			CONS_Printf(p, "\"TIME\" must be included (and a number)")
			CONS_Printf(p, "If you want the time in seconds, exclude \"IS_TICS\".")
		end
	end
end, COM_ADMIN)
COM_AddCommand("rtd_forceroll", function(p, item, s)
	if p then
		if item then
			if not s then CONS_Printf(p, "You can't force a roll for no one, include a name.") return end
			local r = (not (tonumber(item) == nil) and clamp(tonumber(item), 1, #RTD_ItemList) or isearch(RTD_ItemList, item:lower()))
			local roll
			if p.name == s then
				if not p.rtd or p.rtdwait then
					p.rtd = true
					roll = P_CallRTD(p, r, true)
					CONS_Printf(p, "RTD \$RTD_ItemList[roll]\ forced.")
					if (RTD_ItemList[roll] == "thousanddeaths") then CONS_Printf(p, "Why would you do this to yourself?") end
				else
					CONS_Printf(p, "RTD either active or on cooldown, wait.")
				end
			else
				local success
				for sp in players.iterate do
					if (sp.name == s) then
						success = true
						if not sp.rtd or sp.rtdwait then
							sp.rtd = true
							roll = P_CallRTD(sp, r, true)
							CONS_Printf(p, "Player found, RTD \$RTD_ItemList[roll]\ forced.")
							if (RTD_ItemList[roll] == "thousanddeaths") then CONS_Printf(p, "You are\x85 beyond\x80 evil.") end
						else
							CONS_Printf(p, "RTD either active or on cooldown for player, wait.")
						end
						return
					end
					continue
				end
				if not success then
					CONS_Printf(p, "No player found. Check your spelling.")
				end
			end
		else
			CONS_Printf(p, "Arguments:")
			CONS_Printf(p, "rtd_forceroll ITEM PLAYER")
			CONS_Printf(p, "\"ITEM\" must be included (and can either be the name or number of the roll item)")
			CONS_Printf(p, "If you want to force a roll for someone else, include their name.")
			CONS_Printf(p, "Otherwise, include your own name.")
		end
	end
end, COM_ADMIN)
local function instantCooldown(p)
	p.rtd = nil
	p.rtdwait = RTD_WaitTime
end
local function isForced(bool)
	if bool then return " \x85[Forced]\x80" end
	return ""
end
rawset(_G, "P_CallRTD", function(p, num, f)
	local forced = f or false
	local m = p.mo
	local roll = num or P_RandomRange(1, #RTD_ItemList)
	if roll then
		local item = RTD_ItemList[roll]
		if item == "truesuper" then
			if not (skins[m.skin].flags & SF_SUPER) or G_CoopGametype() then
				return P_CallRTD(p, isearch(RTD_ItemList, "matchsuper"), forced)
			end
			if p.powers[pw_super] and not p.rtdsuper then
				return P_CallRTD(p, isearch(RTD_ItemList, "50rings"), forced)
			end
			chatprint(rtd..p.name.." rolled True Super. Run."..isForced(forced)) -- the big one
			m.stored_rings = p.rings
			table.insert(RTD_SuperList, p.name)
			if not emeralds then
				emeralds = 127
			end
			p.rtdsuper = true
			if P_IsObjectOnGround(m) then
				P_DoJump(p, true)
				P_SetOrigin(m, m.x, m.y, m.z + 32*FU)
			end
			p.rings = 50
			P_DoSuperTransformation(p, false)
			p.rings = 30
		elseif item == "matchsuper" then
			if p.powers[pw_super] or p.powers[pw_invulnerability] or p.powers[pw_sneakers] then
				return P_CallRTD(p, isearch(RTD_ItemList, "50rings"), forced)
			end
			chatprint(rtd..p.name.." rolled Match Super."..isForced(forced))
			p.powers[pw_emeralds] = 127
			P_DoMatchSuper(p)
			p.rtdmatchsuper = true
			m.rtdtimer = 20*TICRATE
		elseif item == "invuln" then
			if p.powers[pw_super] then
				return P_CallRTD(p, isearch(RTD_ItemList, "10rings"), forced)
			end
			chatprint(rtd..p.name.." rolled Invulnerability."..isForced(forced))
			A_CustomPower(pw_invulnerability, 20*TICRATE)
			p.rtdinvuln = true
			m.rtdtimer = 20*TICRATE
		elseif item == "speed" then
			if p.powers[pw_super] then
				return P_CallRTD(p, isearch(RTD_ItemList, "10rings"), forced)
			end
			chatprint(rtd..p.name.." rolled Speed Shoes."..isForced(forced))
			A_CustomPower(pw_sneakers, 20*TICRATE)
			p.rtdspeed = true
			m.rtdtimer = 20*TICRATE
		--[[elseif item == "forcehp" then
			if not (p.powers[pw_shield] & SH_FORCE) then
				return P_CallRTD(p, isearch(RTD_ItemList, "forceshield"), forced)
			end
			chatprint(rtd..p.name.." rolled Bonus Force Shield HP."..isForced(forced))
			p.powers[pw_shield] = (not ((p.powers[pw_shield] & SH_FORCEHP) == SH_FORCEHP) and $+1 or $|SH_FORCEHP)
			P_SpawnShieldOrb(p)
			instantCooldown(p)--]]
		elseif item:find("shield") then -- yes, a roll completely dependent on the string
			local shield = item:match("^(.-)shield")
			if shield == nil then chatprintf(p, rtd.."Something went wrong with your shield, so you get 10 rings on us.") return P_CallRTD(p, isearch(RTD_ItemList, "10rings"), forced) end
			if shield == "force" and p.powers[pw_shield] & SH_FORCE then return P_CallRTD(p, isearch(RTD_ItemList, "forcehp"), forced) end
			if p.powers[pw_shield] & ~SH_STACK then return P_CallRTD(p, isearch(RTD_ItemList, "10rings"), forced) end
			local name
			for _,v in ipairs({"fl", "bu", "th"}) do
				if shield:sub(1,2) == v then name = shield:sub(1,1):upper()..shield:sub(2,-5) return end
			end
			name = $ or shield:sub(1,1):upper()..shield:sub(2)
			chatprint(rtd..p.name.." rolled a \$name\ Shield."..isForced(forced))
			P_SwitchShield(p, RTD_ShieldList["SH_"..shield:upper()])
			instantCooldown(p)
		elseif item:find("ring") then -- anotha one
			local amt = item:match("(%d%d?%d?%d?)") --I LOVE REGEX I LOVE REGEX I LO
			if tonumber(amt) == nil then chatprintf(p, rtd.."Something went wrong with your rings, so you get Match Super on us.") return P_CallRTD(p, isearch(RTD_ItemList, "matchsuper"), forced) end
			if tonumber(amt) == 9999 and not P_RandomChance(69*FU/1000) and not forced then amt = tostring(100) end
			local minus = item:match("^m") or false
			chatprint(rtd..p.name.." rolled \$(minus and "Minus " or "")..amt\ Rings."..isForced(forced))
			S_StartSound(m, (minus and sfx_antiri or sfx_itemup))
			P_GivePlayerRings(p, tonumber(amt) * (minus and -1 or 1))
			instantCooldown(p)
		elseif item == "nothing" then
			chatprint(rtd..p.name.." rolled nothing."..isForced(forced))
			S_StartSound(nil, sfx_rdfail, p) --tf2 rtd reference
			instantCooldown(p)
		elseif item == "selfnuke" then
			chatprint(rtd..p.name.." rolled a self-nuke. Better than just dying..."..isForced(forced))
			instantCooldown(p)
			TacticalNuke(4096, m, 60, 5, true, PAL_NUKE)
		elseif item == "instadeath" then
			chatprint(rtd..p.name.." rolled an instant death. Skill issue."..isForced(forced))
			instantCooldown(p)
			P_DamageMobj(m, nil, nil, 10000, DMG_INSTAKILL)
		elseif item == "thousanddeaths" then
			if not RTD_Nightmare and not forced and not P_RandomChance(FU/1000) then -- you have to get REALLY unlucky to get this one.
				return P_CallRTD(p, isearch(RTD_ItemList, "instadeath"), forced)
			end
			chatprint(rtd..p.name.." rolled a \x8FThousand Deaths\x80. o7 in the chat."..isForced(forced))
			p.youarefucked = true -- you genuinely are, only an admin can save you (or a lot of patience)
			p.deathcount = 0
		end
		return roll
	else
		chatprintf(p, rtd.."Roll failed. The mod must be broken. Let the server owner know.")
	end
end)
addHook("PlayerThink", function(p)
	if not p.rtdwelcome then
		p.rtdwelcome = 69
		chatprintf(p, rtd.."Welcome to the server!")
		chatprintf(p, rtd.."To Roll The Dice, type \"!rtd\" or \"/rtd\" in the chat.")
	end
end)
addHook("PlayerThink", function(p)
	local m = p.mo
	if p.rtd and not (p.playerstate == PST_LIVE) then
		instantCooldown(p)
		if m and m.valid then m.rtdtimer = nil end
		chatprint(rtd..p.name.." died during their roll...")
		if p.rtdsuper then
			p.rtdsuper = nil
			m.stored_rings = nil -- if u die, u lose all of ur rings lol
			p.rings = 0
			p.charability = skins[m.skin].ability
			table.remove(RTD_SuperList, isearch(RTD_SuperList, p.name))
		end
		if p.rtdmatchsuper then
			p.rtdmatchsuper = nil
			p.powers[pw_invulnerability] = 0
			p.powers[pw_sneakers] = 0
			COM_BufInsertText(p, "tunes -default")
			P_SpawnShieldOrb(p)
		end
		if p.rtdinvuln then
			p.rtdinvuln = nil
			p.powers[pw_invulnerability] = 0
			COM_BufInsertText(p, "tunes -default")
			P_SpawnShieldOrb(p)
		end
		if p.rtdspeed then
			p.rtdspeed = nil
			p.powers[pw_sneakers] = 0
			COM_BufInsertText(p, "tunes -default")
		end
	end
	if p.rtdsuper and not p.rings then -- RTD true super matters first
		p.rtdsuper = nil
		instantCooldown(p)
		p.rings = m.stored_rings
		m.stored_rings = nil
		p.charability = skins[m.skin].ability
		table.remove(RTD_SuperList, isearch(RTD_SuperList, p.name))
		chatprint(rtd..p.name.."'s True Super wore off.")
	end
	if p.rtdsuper and not p.powers[pw_super] then -- level forced you out of your super form
		p.rtdsuper = nil
		instantCooldown(p)
		if p.superguard then -- no more bonus protection
			p.superguard = nil
			p.powers[pw_strong] = $&~STR_GUARD
		end
		p.rings = m.stored_rings
		m.stored_rings = nil
		p.charability = skins[m.skin].ability
		table.remove(RTD_SuperList, isearch(RTD_SuperList, p.name))
		chatprint(rtd..p.name.."'s True Super ended prematurely.")
	end
	if m.rtdtimer then
		m.rtdtimer = $-1
		if (m.rtdtimer <= 0) then
			m.rtdtimer = nil
			instantCooldown(p)
			if p.rtdmatchsuper then
				p.rtdmatchsuper = nil
				p.powers[pw_invulnerability] = 0
				p.powers[pw_sneakers] = 0
				COM_BufInsertText(p, "tunes -default")
				P_SpawnShieldOrb(p)
				chatprint(rtd..p.name.."'s Match Super wore off.")
			end
			if p.rtdinvuln then
				p.rtdinvuln = nil
				p.powers[pw_invulnerability] = 0
				COM_BufInsertText(p, "tunes -default")
				P_SpawnShieldOrb(p)
				chatprint(rtd..p.name.."'s Invulnerability wore off.")
			end
			if p.rtdspeed then
				p.rtdspeed = nil
				p.powers[pw_sneakers] = 0
				COM_BufInsertText(p, "tunes -default")
				chatprint(rtd..p.name.."'s Speed Shoes wore off.")
			end
		end
	end
	if p.rtdwait then
		p.rtdwait = $-1
		if (p.rtdwait <= 0) then
			p.rtdwait = nil
		end
	end
end)
addHook("PreThinkFrame", function()
	if not #RTD_SuperList and not G_CoopGametype() then --make sure no conflicts happen between two RTD Supers
		emeralds = 0
	end
end)
addHook("PlayerThink", function(p) -- gotta feel bad for these peeps
	if p.youarefucked then
		if p.nightmaresave then
			p.youarefucked = nil
			p.deathcount = 0
			p.nightmaresave = nil
			instantCooldown(p)
			chatprint(rtd..p.name.." has been freed!") return -- thanks admin!
		end
		if (p.playerstate == PST_LIVE) then
			P_KillMobj(p.mo, nil, nil, DMG_INSTAKILL|DMG_CANHURTSELF) -- die
			p.deathcount = $+1
			if p.deathcount >= 1000 then -- yes, it really is a thousand deaths
				p.youarefucked = nil
				p.deathcount = 0
				instantCooldown(p)
				chatprint(rtd..p.name.." is free once again!") -- this will take forever
			end
		end
	end
end)
addHook("PreThinkFrame", function()
	for p in players.iterate do
		if not p.rtdsuper and not G_CoopGametype() and p.powers[pw_super] then
			p.powers[pw_super] = 0
			p.mo.color = p.skincolor
			P_SpawnShieldOrb(p)
			chatprintf(p, rtd.."Nice try.") -- no super 4 u
		end
	end
end)
addHook("PlayerMsg", function(p, msgtype, target, msg)
	if (msgtype) then return end
	local check = msg:lower()
	if (check == "!rtd") or (check == "/rtd") then
		if not ((p.mo and p.mo.valid) and (p.playerstate == PST_LIVE)) then
			chatprintf(p, rtd.."You cannot roll the dice while dead or spectating.") if not (check == "/rtd") then return true end
		end
		if p.rtd then
			chatprintf(p, rtd.."Wait until your roll is over.") if not (check == "/rtd") then return true end
		end

		if p.rtdwait then
			chatprintf(p, rtd.."You must wait \$p.rtdwait/TICRATE\ seconds to roll again.") if not (check == "/rtd") then return true end
		end
		p.rtd = true
		P_CallRTD(p) if not (check == "/rtd") then return true end
	end
end)
addHook("MobjMoveCollide", function(m, v)
	if not (m.valid and m.player and v and v.valid) or not m.player.rtdsuper then return end
	if (v.type == MT_RING) then
		if v.z + v.height < m.z + m.momz or v.z > m.z + m.momz + m.height then return false end
		S_StartSound(v, sfx_itemup)
		m.stored_rings = $+1
		P_KillMobj(v) return false
	end
end, MT_PLAYER)
-------------------------------------
--        Oh hey, HUD stuff        --
-------------------------------------
hud.add(function(v, p) -- so you can still see how many rings you have stored
	if not hud.enabled("rings") and not p.rtdsuper then hud.enable("rings") return end
	if not p.rtdsuper then return end
	if hud.enabled("rings") then hud.disable("rings") end
	local snaps = V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS
	local rings = v.cachePatch("STTRINGS") -- THE WIKI LIED IT'S NOT SBORINGS
	v.draw(hudinfo[HUD_RINGS].x, hudinfo[HUD_RINGS].y, rings, snaps)
	local ringx = (not (CV_FindVar("timerres").string == "Mania") and hudinfo[HUD_RINGSNUM].x or hudinfo[HUD_RINGSNUMTICS].x) -- hehe
	v.drawNum(ringx, hudinfo[HUD_RINGSNUM].y, p.mo.stored_rings or 0, snaps)
	local supr = v.cachePatch("STTSUPER") -- my own creation
	v.draw(hudinfo[HUD_RINGS].x, hudinfo[HUD_RINGS].y + 16, supr, snaps)
	v.drawNum(ringx, hudinfo[HUD_RINGSNUM].y + 16, p.rings or 0, snaps)
end, "game")
-------------------------------------
--Section for the Action overwrites--
-------------------------------------
local function P_RTDPlayerLives(p, lives)
	local prevlives = p.lives
	if not p then
		return
	end
	if p.bot then
		p = players[consoleplayer]
	end
	if (gamestate == GS_LEVEL) then
		if (p.lives == INFLIVES or not (gametyperules & GTR_LIVES)) then
			if p.rtdsuper then
				p.mo.stored_rings = $+(100*lives)
			else
				P_GivePlayerRings(p, 100*lives)
			end
			return
		end
		if (netgame or multiplayer) and G_GametypeUsesCoopLives() and not (CV_FindVar(cooplives) and CV_FindVar(cooplives).value) then
			P_GivePlayerRings(p, 100*lives)
			if (p.lives - prevlives >= lives) then
				if (CV_FindVar(coopstarposts) and CV_FindVar(coopstarposts).value) or (prevlives > 0) or not p.spectator then
					return
				end
				--This would be the part where a spec gets revived, but that isn't allowed from Lua
			end
			lives = lives + prevlives - p.lives
		end
	elseif (p.lives == INFLIVES) then
		return
	end
	p.lives = $+lives
	if (p.lives > 99) then
		p.lives = 99
	elseif (p.lives < 1) then
		p.lives = 1
	end
end
local function P_RTDCoopLives(p, lives, sound)
	if not (netgame or multiplayer) and G_GametypeUsesCoopLives() then
		P_RTDPlayerLives(p, lives)
		if sound then
			P_PlayLivesJingle(p)
		end
	else
		for sp in players.iterate do
			P_RTDPlayerLives(sp, lives)
			if sound then
				P_PlayLivesJingle(sp)
			end
		end
	end
end
function A_RTDRingBox(actor)
	local p = actor.target.player
	if p.rtdsuper then
		p.mo.stored_rings = $+actor.info.reactiontime
	else
		P_GivePlayerRings(p, actor.info.reactiontime)
	end
	if (actor.info.seesound) then
		S_StartSound(p.mo, actor.info.seesound)
	end
	return true
end
function A_RTDExtraLife(actor)
	local p = actor.target.player
	if (actor.type == MT_1UP_ICON and actor.tracer) then
		actor.sprite = SPR_TV1P
	end
	if ultimatemode then
		S_StartSound(p.mo, sfx_lose)
		return
	end
	P_RTDCoopLives(p, 1, true)
end
states[S_RING_ICON2].action = A_RTDRingBox  -- The actions are the important part
states[S_1UP_ICON2].action = A_RTDExtraLife -- Everything else is just a hack to make the actions work properly