// Final Demo Monitor Commonalities - Ported by MIDIMan
// Use with the other LUA_FBX* scripts
// It is important to note that, even though these are labelled as "Final Demo" boxes, 
// some of them come from the 2.0 era

/*
A few notes regarding multiplayer monitors between versions:
1.01-1.04 - Monitors could be switched on or off (Eggman was an option)
1.08-2.0 - Monitors could be set between off, low, medium, and high (Eggman was not an option)
2.1-present - Monitors could be set as percentages

In versions 1.08-1.09.4, players can cause a "segment violation" by setting all 
the monitor toggles to "High", starting a race with "Random" monitors only, and 
breaking a monitor. This is caused by the array that stores the random chances 
being too small. This bug has not been replicated for obvious reasons.
*/

freeslot(
	"SPR_FBOX",
	"S_FDBOX_FLICKER",
	"S_FDBOX_EXPLOSION1",
	"S_FDBOX_EXPLOSION2"
)

// Change the current mobj type to something else
local function RM_ChangeMobj(mo, newtype)
	if not (mo and mo.valid) then return end
	if not newtype then return end
	
	local fdAltOption = mo.FDAltOption
	
	mo.type = newtype
	
	mo.radius = FixedMul(mo.scale, mo.info.radius)
	mo.height = FixedMul(mo.scale, mo.info.height)
	mo.flags = mo.info.flags
	
	mo.health = mo.info.spawnhealth
	
	mo.reactiontime = mo.info.reactiontime
	
	mo.state = mo.info.spawnstate
end

// Change the monitor's type based on the flags given, the current gametype and its settings
rawset(_G, "P_FDMonitorTypeSpawn", function(mobj, mthing)
	if not (mobj and mobj.valid
	and mthing and mthing.valid) then
		return
	end
	
	if mthing.extrainfo == 1 then // 1.09-1.09.4 behavior
		mobj.FDAltOption = 1
	elseif mthing.extrainfo == 2 then // 1.08 behavior
		mobj.FDAltOption = 2
	elseif mthing.extrainfo > 2 then // 2K3-1.04 behavior
		mobj.FDAltOption = 3
	end
	
	/*
	if (mthing.options & MTF_OBJECTSPECIAL) // 2.0 behavior
		mobj.flags2 = $|MF2_STRONGBOX
	end
	
	if (mthing.options & MTF_AMBUSH)
		mobj.flags2 = $|MF2_AMBUSH
	end
	*/
	
	// Set powerup boxes to user settings for race and competition.
	// Note: Competition mode is based off of the "Classic Race" mode from the 
	// pre-2.1 eras of SRB2
	if gametype == GT_RACE or gametype == GT_COMPETITION then
		local compboxes = CV_FindVar("competitionboxes").value
		if compboxes then // not Normal
			
			if compboxes == 1 then // Random
				RM_ChangeMobj(mobj, MT_FDBOX_QUESTION)
			elseif compboxes == 2 then // Teleports (Doesn't work in 2.2)
				RM_ChangeMobj(mobj, MT_FDBOX_MIXUP)
			elseif compboxes == 3 then // None
				P_RemoveMobj(mobj) // Don't spawn!
			end
		end
	// Set powerup boxes to user settings for other netplay modes
	elseif gametype ~= GT_COOP
		local matchboxes = CV_FindVar("matchboxes").value
		if matchboxes then // not Normal
			if matchboxes == 1 then // Random
				RM_ChangeMobj(mobj, MT_FDBOX_QUESTION)
			elseif compboxes == 2 then // Teleports (Doesn't work in 2.2)
				RM_ChangeMobj(mobj, MT_FDBOX_MIXUP)
			elseif matchboxes == 3 then // Don't spawn
				P_RemoveMobj(mobj)
			else // Non-random
				if mobj.type == MT_FDBOX_QUESTION then
					P_RemoveMobj(mobj) // don't spawn in Non-Random
					return
				end
				
				mthing.options = $ & ~(MTF_AMBUSH|MTF_OBJECTSPECIAL)
				mobj.flags2 = $ & ~(MF2_AMBUSH|MF2_STRONGBOX)
			end
		end
	end
end)

//
local function DetermineShieldVariants(mobj)
	if not (mobj and mobj.valid) then return end
	
	local shields = {}
	// [1] = jumpshield
	// [2] = watershield
	// [3] = fireforceshield
	// [4] = bombshield
	
	if mobj.FDAltOption then
		if mobj.FDAltOption >= 2 then // 1.08 and below shields
			shields[1] = MT_FDBOX_BLUE
			shields[2] = MT_FDBOX_GREEN
		else // 1.09-1.09.4 shields
			shields[1] = MT_FDBOX_WHITE
			shields[2] = MT_FDBOX_BLUE
		end
		shields[3] = MT_FDBOX_RED
		shields[4] = MT_FDBOX_BLACK
	else // 2.0 shields
		shields[1] = MT_FDBOX_WHITE
		shields[2] = MT_FDBOX_GREEN
		shields[3] = MT_FDBOX_BLUE
		shields[4] = MT_FDBOX_RED
	end
	
	return shields
end

// Handle how Final Demo monitors respawn in Multiplayer gametypes (CHANGE THIS)
rawset(_G, "P_FDMonitorFuseThink", function(mobj)
	if not (mobj and mobj.valid)
		return
	end
	
	local newmobj, boxtype
	
	if ((mobj.flags2 & (MF2_AMBUSH|MF2_STRONGBOX)) and mobj.info.damage ~= MT_UNKNOWN) then
		local spawnchance = {}
		local numchoices, i = 0, 0
		local altoption = mobj.FDAltOption
		
		if altoption == nil then
			altoption = -1
		end
		
		local function SETMONITORCHANCES(boxtype, strongboxamt, weakboxamt)
			local boxamt = 0 // Off
			
			if (mobj.flags2 & MF2_STRONGBOX) and altoption < 1 then
				boxamt = strongboxamt
			else
				if altoption > 2 then // Pre-1.08 behavior
					if weakboxamt then
						boxamt = 1
					end
				elseif altoption > 0 then
					if weakboxamt > 6 then // High
						boxamt = 3
					elseif weakboxamt > 3 then // Medium
						boxamt = 2
					elseif weakboxamt > 0 then // Low
						boxamt = 1
					end
				else
					boxamt = weakboxamt
				end
			end
			
			if not boxamt then
				boxamt = -1
			end
			
			if boxamt > 0 then // Prevent a possible endless loop
				for i = boxamt, 1, -1 do
					spawnchance[numchoices+1] = boxtype
					numchoices = $+1
				end
			end
		end
		
		local shields = DetermineShieldVariants(mobj)
		
		if altoption > 0 then
			SETMONITORCHANCES(MT_FDBOX_RING,		0,	CV_FindVar("tv_superring").value)
			SETMONITORCHANCES(MT_FDBOX_SRING,		0,	CV_FindVar("tv_recycler").value)
			SETMONITORCHANCES(MT_FDBOX_SHOES,		0,	CV_FindVar("tv_supersneaker").value)
			SETMONITORCHANCES(MT_FDBOX_INVIN,		0,	CV_FindVar("tv_invincibility").value)
			SETMONITORCHANCES(shields[1],			0,	CV_FindVar("tv_jumpshield").value)
			SETMONITORCHANCES(shields[2],			0,	CV_FindVar("tv_watershield").value)
			SETMONITORCHANCES(MT_FDBOX_YELLOW,		0,	CV_FindVar("tv_ringshield").value)
			SETMONITORCHANCES(shields[3],			0,	CV_FindVar("tv_forceshield").value)
			SETMONITORCHANCES(shields[4],			0,	CV_FindVar("tv_bombshield").value)
			if gametype == GT_RACE then
				SETMONITORCHANCES(MT_FDBOX_1UP,		0,	CV_FindVar("tv_1up").value)
			end
			if altoption > 2 then // Pre-1.08 behavior
				SETMONITORCHANCES(MT_FDBOX_EGGMAN,	0,	CV_FindVar("tv_eggman").value)
			end
			SETMONITORCHANCES(MT_FDBOX_MIXUP,		0,	CV_FindVar("tv_teleporter").value)
		else
			//			Type				  SRM  WRM
			SETMONITORCHANCES(MT_FDBOX_RING,	0,	16)
			SETMONITORCHANCES(MT_FDBOX_SHOES,	0,	14)
			SETMONITORCHANCES(MT_FDBOX_INVIN,	6,	 0)
			SETMONITORCHANCES(shields[1],		12,	12)
			SETMONITORCHANCES(shields[2],		12,	12)
			SETMONITORCHANCES(MT_FDBOX_YELLOW,	8,	 2)
			SETMONITORCHANCES(shields[3],		12,	 4)
			SETMONITORCHANCES(shields[4],		8,	 0)
			SETMONITORCHANCES(MT_FDBOX_MIXUP,	0,	 2)
			SETMONITORCHANCES(MT_FDBOX_RECYCLE,	0,	 2)
			SETMONITORCHANCES(MT_FDBOX_1UP,		6,	 0)
			// =======================================
			//			Total				   64 	64
		end
		
		/*
		//				  Type				  SRM  WRM
		SETMONITORCHANCES(MT_21BOX_SHOES,		0,	10) // Super Sneakers
		SETMONITORCHANCES(MT_21BOX_INVIN,		2,	 0) // Invincibility
		SETMONITORCHANCES(MT_21BOX_WHIRLWIND,	3,	 8) // Whirlwind Shield
		SETMONITORCHANCES(MT_21BOX_ELEMENTAL,	3,	 8) // Elemental Shield
		SETMONITORCHANCES(MT_21BOX_ATTRACT,		2,	 0) // Attraction Shield
		SETMONITORCHANCES(MT_21BOX_FORCE,		3,	 3) // Force Shield
		SETMONITORCHANCES(MT_21BOX_ARMAGEDDON,	2,	 0) // Armageddon Shield
		SETMONITORCHANCES(MT_21BOX_MIXUP,		0,	 1) // Teleporters
		SETMONITORCHANCES(MT_21BOX_RECYCLE,		0,	 1) // Recycler
		SETMONITORCHANCES(MT_21BOX_1UP,			1,	 1) // 1-Up
		// ===========================================
		//				  Total				   16	32
		*/
		
		if numchoices == 0 then
			print("Note: All monitors turned off.")
		else
			boxtype = spawnchance[(P_RandomByte()%numchoices)+1]
		end
	end
	
	if boxtype == nil then
		boxtype = mobj.type
	end
	
	newmobj = P_SpawnMobjFromMobj(mobj, 0, 0, 0, boxtype)
		
	// Transfer flags2 (strongbox, objectflip)
	newmobj.flags2 = mobj.flags2
	
	// Transfer FDAltOption (if the monitor is not a 2.0 variant)
	if mobj.FDAltOption then newmobj.FDAltOption = mobj.FDAltOption end
	
	P_RemoveMobj(mobj) // make sure they disappear
end)

// Randomly choose a monitor based on the user's settings and the monitor
local function P_FDRandomBoxChance(mobj)
	if not (mobj and mobj.valid) then return end
	
	local spawnchance = {}
	local numchoices, i = 0, 0
	local jumpshield, watershield, fireforceshield, bombshield
	
	local function QUESTIONBOXCHANCES(boxtype, cvar)
		local boxset = cvar.value
		local maxvalue = -1 // Off
		local altoption = mobj.FDAltOption
		
		if altoption == nil then altoption = -1 end
		
		if altoption < 3 then
			if boxset > 6 then // High
				maxvalue = 3
			elseif boxset > 3 then // Medium
				maxvalue = 2
			elseif boxset > 0 then // Low
				maxvalue = 1
			end
		elseif boxset > 0 then
			maxvalue = 1 // Yes
		end
		
		if maxvalue > 0 then // Prevent a possible endless loop
			for i = maxvalue, 1, -1 do
				spawnchance[numchoices+1] = boxtype
				numchoices = $+1
			end
		end
	end
	
	if mobj.FDAltOption then
		if mobj.FDAltOption >= 2 then // 1.08 and below shields
			jumpshield = MT_FDBOX_BLUE_ICON
			watershield = MT_FDBOX_GREEN_ICON
		else // 1.09-1.09.4 shields
			jumpshield = MT_FDBOX_WHITE_ICON
			watershield = MT_FDBOX_BLUE_ICON
		end
		fireforceshield = MT_FDBOX_RED_ICON
		bombshield = MT_FDBOX_BLACK_ICON
	else // 2.0 shields
		jumpshield = MT_FDBOX_WHITE_ICON
		watershield = MT_FDBOX_GREEN_ICON
		fireforceshield = MT_FDBOX_BLUE_ICON
		bombshield = MT_FDBOX_RED_ICON
	end
	
	QUESTIONBOXCHANCES(MT_FDBOX_RING_ICON,			CV_FindVar("tv_superring"))
	if mobj.FDAltOption then // Pre-2.0 Behavior (for Silver Ring)
		QUESTIONBOXCHANCES(MT_FDBOX_SRING_ICON,		CV_FindVar("tv_recycler"))
	end
	QUESTIONBOXCHANCES(MT_FDBOX_SHOES_ICON,			CV_FindVar("tv_supersneaker"))
	QUESTIONBOXCHANCES(MT_FDBOX_INVIN_ICON,			CV_FindVar("tv_invincibility"))
	QUESTIONBOXCHANCES(jumpshield,					CV_FindVar("tv_jumpshield"))
	QUESTIONBOXCHANCES(watershield,					CV_FindVar("tv_watershield"))
	QUESTIONBOXCHANCES(MT_FDBOX_YELLOW_ICON,		CV_FindVar("tv_ringshield"))
	QUESTIONBOXCHANCES(fireforceshield,				CV_FindVar("tv_forceshield"))
	QUESTIONBOXCHANCES(bombshield,					CV_FindVar("tv_bombshield"))
	QUESTIONBOXCHANCES(MT_FDBOX_1UP_ICON,			CV_FindVar("tv_1up"))
	QUESTIONBOXCHANCES(MT_FDBOX_EGGMAN_ICON,		CV_FindVar("tv_eggman"))
	QUESTIONBOXCHANCES(MT_FDBOX_MIXUP_ICON,			CV_FindVar("tv_teleporter"))
	if not mobj.FDAltOption then // 2.0 behavior
		QUESTIONBOXCHANCES(MT_FDBOX_RECYCLE_ICON,	CV_FindVar("tv_recycler"))
	end
	
	if numchoices == 0
		print("Note: All monitors turned off.")
		return MT_NULL
	end
	
	return spawnchance[(P_RandomByte()%numchoices)+1]
end

// Variant of A_MonitorPop that doesn't spawn an explosion
function A_FDMonitorPop(actor, var1, var2)
	if not (actor and actor.valid) then return end
	
	//local remains
	local item
	//local random
	//local newbox
	
	// de-solidify
	actor.flags = $|MF_NOCLIP & ~MF_SOLID
	
	//remains = P_SpawnMobj(actor.x, actor.y, actor.z, actor.info.speed)
	
	if actor.info.deathsound then
		S_StartSound(actor, actor.info.deathsound)
	end
	
	if actor.info.damage == MT_UNKNOWN then
		item = P_FDRandomBoxChance(actor)
	else
		item = actor.info.damage
	end
	
	if item ~= nil then
		local newmobj
		newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item)
		newmobj.target = actor.target // Transfer target
		
		// Might not be needed if using P_SpawnMobjFromMobj
		/*
		if (actor.eflags & MFE_VERTICALFLIP)
			newmobj.eflags = $|MFE_VERTICALFLIP
		end
		*/
		
		// Let the icon give the player the correct shield based on the monitor's options
		if actor.FDAltOption then newmobj.FDAltOption = actor.FDAltOption end
		
		// Only display the player's 1up icon instead of the monitor's if the monitor is set to do so
		if item == MT_FDBOX_1UP_ICON then
			if newmobj.FDAltOption then
				if newmobj.FDAltOption >= 2 then return end
			end
			
			if actor.tracer then // Remove the old lives icon.
				P_RemoveMobj(actor.tracer)
			end
			
			if not newmobj.target
			or not newmobj.target.player
			or not newmobj.target.skin
			or skins[newmobj.target.skin].sprites[SPR2_LIFE].numframes == 0 then
				// No lives icon for this player, use the default
			else // Spawn the lives icon
				local livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY)
				livesico.target = newmobj
				newmobj.tracer = livesico
				
				livesico.color = newmobj.target.player.mo.color
				livesico.skin = newmobj.target.skin
				livesico.state = newmobj.info.seestate
				
				// Use the new 2.2.9 sprite scaling variables to make the life icon fit inside the Final Demo monitor
				livesico.spritexscale = 3*FRACUNIT/4
				livesico.spriteyscale = 3*FRACUNIT/4
				
				// We're using the overlay, so use the overlay 1up frame (no text)
				newmobj.frame = D
			end
		end
	else
		//print("Powerup item not defined in 'damage' field for A_FDMonitorPop\n")
	end
end

states[S_FDBOX_FLICKER] =	{SPR_FBOX,	A,	1,	nil,	0,	0,	S_SPAWNSTATE}

states[S_FDBOX_EXPLOSION1] =	{SPR_FBOX,	B|FF_ANIMATE,	8,	A_FDMonitorPop,	4,	2,	S_FDBOX_EXPLOSION2}
states[S_FDBOX_EXPLOSION2] =	{SPR_FBOX,	F,				-1,	nil,			0,	0,	S_NULL}
