freeslot(
	-- Sprites
	"SPR_PSHL",
	"SPR2_PMHM",
	"SPR2_PMHD",
	"SPR2_PMHY",
	"SPR2_PMPS",
	"SPR2_PMBL",
	
	-- Mobjs
	"MT_PAPERHAMMER", 
	"MT_FIREDRIVEORB", 
	"MT_PAPERCAPE", 
	"MT_WARPPIPE",
	"MT_PAPERCAPE_HITBOX",
	
	-- Sounds
	"sfx_pmjump", 
	"sfx_pmtorn", 
	"sfx_pmdbl",
	"sfx_pmhamr", 
	"sfx_pmatck", 
	"sfx_pmfall", 
	"sfx_pmthro",
	"sfx_pmsmal", 
	"sfx_pmbig", 
	"sfx_pmskid", 
	"sfx_pmow1", 
	"sfx_pmow2",
	"sfx_pmblah",
	
	-- States
	"S_PM_HAMCHARGE",
	"S_PM_HAMSWING",
	"S_PM_HAMYEET",
	"S_PM_SHIELD",
	"S_PM_POSE",
	"S_PM_BLAH",
	"S_PM_PAPERCAPE_HITBOX"
)

states[S_PM_HAMCHARGE] = {
	sprite = SPR_PLAY,
	frame = A|SPR2_PMHM,
	tics = 3,
	action = function(paper)
		local player = paper.player
		if not (player and player.valid) return end
		local tosspower = player.groundhammerthrow + 1
		if tosspower >  72
			paper.tics = 1
		elseif tosspower > 40
			paper.tics = 3
		else
			paper.tics = 5
		end
	end,
	nextstate = S_PM_HAMCHARGE
}
states[S_PM_HAMSWING] = {
	sprite = SPR_PLAY,
	frame = SPR2_PMHD,
	tics = 3,
	nextstate = S_PLAY_STND
}
states[S_PM_HAMYEET] = {
	sprite = SPR_PLAY,
	frame = SPR2_PMHY,
	tics = 10,
	nextstate = S_PLAY_STND
}
states[S_PM_SHIELD] = {
	sprite = SPR_PSHL,
	frame = A|FF_ANIMATE,
	tics = 20,
	var1 = 3,
	var2 = 5,
	nextstate = S_PM_SHIELD
}
states[S_PM_POSE] = {
	sprite = SPR_PLAY,
	frame = SPR2_PMPS,
//	tics = 32,
	nextstate = S_PM_POSE
}
states[S_PM_BLAH] = {
	sprite = SPR_PLAY,
	frame = SPR2_PMBL,
	nextstate = S_PM_BLAH
}
mobjinfo[MT_PAPERCAPE_HITBOX] = {
	doomednum = -1,
	spawnhealth = 1,
	spawnstate = S_PM_PAPERCAPE_HITBOX,
	deathstate = S_NULL,
	radius = 36*FRACUNIT,
	height = 64*FRACUNIT,
	mass = DMG_WATER,
	flags = MF_SOLID|MF_NOGRAVITY|MF_NOCLIPHEIGHT
}
states[S_PM_PAPERCAPE_HITBOX] = {SPR_NULL, 0, -1, NULL, 0, 0, S_NULL}

COM_AddCommand("paper_noscope", function(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	if player.paper_noscope then
		player.paper_noscope = false
		CONS_Printf(player, "Hammer throw helper enabled.")
	else
		player.paper_noscope = true
		CONS_Printf(player, "Hammer throw helper disabled.")
	end
end)

-- Lets Allow Everyone To Be Paper
COM_AddCommand("papersprites", function(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) 
		CONS_Printf(player, "You must be in a level to use this command")
		return 
	end
	
	if player.otherpapersprites == nil
		player.otherpapersprites = false
	end
	
	if not player.otherpapersprites then
		player.otherpapersprites = true
		CONS_Printf(player, "Paper sprites have been enabled.")
	else
		player.otherpapersprites = false
		CONS_Printf(player, "Paper sprites have been disabled.")
	end
end)

local function ButtonHandler(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Handles all the buttons related to Mario's special abilities
	if not (player.cmd.buttons & BT_SPIN) then
		player.hammertapready = true
		player.hammertapping = false
	elseif player.hammertapready then
		player.hammertapping = true
		player.hammertapready = false
	else
		player.hammertapping = false
	end
	if player.shrinktime > 5*TICRATE then
		player.hammertapping = false
	end
	if not (player.cmd.buttons & BT_CUSTOM1) then
		player.capetapready = true
		player.capetapping = false
	elseif player.capetapready then
		player.capetapping = true
		player.capetapready = false
	else
		player.capetapping = false
	end
	if player.powers[pw_nocontrol]
	or player.cmd.buttons & (BT_SPIN|BT_CUSTOM2|BT_CUSTOM3) then
		player.capetapping = false
	end
	if not (player.cmd.buttons & BT_CUSTOM2) then
		player.shrinktapready = true
		player.shrinktapping = false
	elseif player.shrinktapready then
		player.shrinktapping = true
		player.shrinktapready = false
	else
		player.shrinktapping = false
	end
	if player.powers[pw_nocontrol]
	or player.cmd.buttons & BT_SPIN then
		player.shrinktapping = false
	end
	if not (player.cmd.buttons & BT_CUSTOM3) then
		player.warptapready = true
		player.warptapping = false
	elseif player.shrinktapready then
		player.warptapping = true
		player.warptapready = false
	else
		player.warptapping = false
	end
end

local function DoFireBlast(mo, speed, height, fuse, big)
	-- Create a ring of fireballs around an object.
	local anglecount = 12
	local angleadd = ANG30
	if big then
		anglecount = 48
		angleadd = ANG15/2
	end
	while anglecount > 0 do
		anglecount = $1 - 1
		local fireball = P_SPMAngle(mo, MT_FIREBALL, anglecount*angleadd, 0, 0)
		if fireball then
			fireball.momx = FixedMul($1, speed)
			fireball.momy = FixedMul($1, speed)
			P_SetObjectMomZ(fireball, height, false)
			fireball.fuse = fuse
		end
	end
end

local function PlayerNearFloor(player, tolerance)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Check whether the player is near the floor,
	-- considering the player's current gravity
	if not (player.mo.eflags & MFE_VERTICALFLIP)
	and abs(player.mo.z - player.mo.floorz) < tolerance then
		return true
	elseif abs(player.mo.z - player.mo.ceilingz) < tolerance then
		return true
	end
	return false
end

local function CheckIfCanUseAbility(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	if (player.cmd.buttons & BT_JUMP)
	and not (player.previouspflags & PF_JUMPDOWN)
	and (player.previouspflags & PF_JUMPED)
	and not player.exiting and not P_PlayerInPain(player) and not player.deadtimer
		if P_IsObjectOnGround(player.mo) or player.climbing
		or (player.powers[pw_carry] == CR_PLAYER|CR_ROPEHANG|CR_MACESPIN)
		or player.previoustracer
			return false
		elseif not (player.previouspflags & PF_SLIDING) and ((gametype ~= GT_CTF) or (not player.gotflag)) then
			return true
		end
	end
	return false
end

local function DoTornadoJump(player, jumppower)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Launch into the air and start spinning.
	if not (player.powers[pw_carry] == CR_MACESPIN)
		P_SetObjectMomZ(player.mo, jumppower, false)
		if (player.mo.eflags & MFE_UNDERWATER) then
			player.mo.momz = $1 * 37/64
		end
		if player.powers[pw_super] then
			player.mo.momz = $1 * 3/2
		end
		player.pflags = $1|PF_THOKKED
		S_StartSound(player.mo, sfx_pmdbl)
		player.pflags = $1|PF_JUMPED
		player.tornadojumped = true
	end
end
local function DoTornadoFall(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Holding JUMP causes the spinning Mario to fall slower.
	if player.cmd.buttons & BT_JUMP then
		if leveltime%3 == 0 then
			local sparklies = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z + player.mo.height/4, MT_SPARK)
			if sparklies then
				sparklies.scale = player.mo.destscale
			end
		end
		if player.mo.momz*P_MobjFlip(player.mo) < 0 then
			if player.powers[pw_shield] == SH_WHIRLWIND then
				player.mo.momz = $1 * 3/4
			else
				player.mo.momz = $1 * 15/16
			end
		end
	end
end

local function DrawHammerArc(player, throwpower)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Draw the ballistic path of a hammer thrown with the specified force.
	-- It will be accurate only where local gravity is equal to global gravity (normally FRACUNIT/2)
	-- Returns two ghost objects:
	-- the first is approximately the hammer's landing spot,
	-- and the second is approximately the hammer's highest point
	local arc = player.mo
	local peak = player.mo
	local up = throwpower*player.mo.scale/3
	if (player.mo.eflags & MFE_VERTICALFLIP) then
		up = $1 * -1
	end
	local arclength = 3*TICRATE
	while arclength
	and (arc == player.mo or not P_IsObjectOnGround(arc))
	and P_CheckPosition(arc,
	arc.x + (throwpower*player.mo.scale/FRACUNIT)*cos(player.mo.angle),
	arc.y + (throwpower*player.mo.scale/FRACUNIT)*sin(player.mo.angle),
	arc.z + up) do
		arc = P_SpawnMobj(
		arc.x + (throwpower*player.mo.scale/FRACUNIT)*cos(player.mo.angle),
		arc.y + (throwpower*player.mo.scale/FRACUNIT)*sin(player.mo.angle),
		arc.z + up,
		MT_THOK)
		if arclength == 3*TICRATE then
			P_SetOrigin(arc, arc.x, arc.y, arc.z + 32*player.mo.scale)
		end
		arc.tics = 1
		if player.mo.state ~= S_PM_HAMCHARGE
		or arclength+2 > 3*TICRATE or (player.cmd.buttons & BT_JUMP)
		or player.paper_noscope then
			arc.flags2 = $1|MF2_DONTDRAW
		end
		arc.scale = player.mo.scale / 8
		arc.color = player.skincolor
		if arc.z*P_MobjFlip(player.mo) > peak.z*P_MobjFlip(player.mo) then
			peak = arc
		end
		if (player.mo.eflags & MFE_VERTICALFLIP) then
			up = $1 + FixedMul(gravity, player.mo.scale)
		else
			up = $1 - FixedMul(gravity, player.mo.scale)
		end
		arclength = $1 - 1
	end
	arc = P_SpawnMobj(arc.x, arc.y, arc.z, MT_THOK)
	arc.tics = 1
	arc.scale = player.mo.scale / 2
	arc.color = player.skincolor
	if player.mo.state ~= S_PM_HAMCHARGE
	or arclength+2 > 3*TICRATE or (player.cmd.buttons & BT_JUMP)
	or player.paper_noscope then
		arc.flags2 = $1|MF2_DONTDRAW
	end
	return arc, peak;
end
local function DoHammerThrow(player, throwpower)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Throw a hammer with the specified force.
	local hammer = P_SPMAngle(player.mo, MT_PAPERHAMMER, player.mo.angle, 0, 0)
	if not hammer then return end
	if throwpower > 57 and not player.firedrive and player.bot ~= 1 then
		throwpower = $1/4
		P_InstaThrust(hammer, hammer.angle, -throwpower*hammer.destscale/32)
	else
		P_InstaThrust(hammer, hammer.angle, throwpower*hammer.destscale)
	end
	
	hammer.flags2 = $1|MF2_BOUNCERING -- the first player the hammer hits will be knocked away
	if throwpower > 44 and player.shrinktime < 5*TICRATE then
		hammer.flags2 = $1|MF2_SUPERFIRE
	end
	
	if player.firedrive then
		local extrafireballs = throwpower/3
		while extrafireballs > 0 do
			local fireball = P_SPMAngle(player.mo, MT_FIREBALL, player.mo.angle, 0, 0)
			if fireball then
				fireball.momx = $1 + hammer.momx/2
				fireball.momy = $1 + hammer.momy/2
				P_Thrust(fireball, fireball.angle + ANGLE_90, P_RandomRange(-4*hammer.scale, 4*hammer.scale))
			end
			extrafireballs = $1 - 1
		end
	end
	
	if (player.mo.eflags & MFE_VERTICALFLIP) then
		throwpower = $1 * -1
	end
	hammer.momz = throwpower*hammer.destscale/3
	if (player.mo.eflags & MFE_UNDERWATER) then
		hammer.momz = $1 * 37/64
	end

	S_StartSound(player.mo, sfx_thok)
	return
end

local function DoAirHammer(player, radius)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Fall toward the ground, using SPIN to fall faster.
	-- Hit an enemy to bounce back up, or hit the ground hard to cause an explosion.
	if player.powers[pw_justsprung] return end
	P_InstaThrust(player.mo, player.mo.angle, 4*player.mo.scale)
	if PlayerNearFloor(player, abs(player.mo.scale/16384*player.mo.momz))
		if player.mo.state ~= S_PM_HAMSWING
			player.mo.state = S_PM_HAMSWING
		end
	else
		if player.cmd.buttons & BT_SPIN then
			if player.firedrive or player.powers[pw_super] then
				P_SetObjectMomZ(player.mo, -3*gravity, true)
			else
				P_SetObjectMomZ(player.mo, -1*gravity, true)
			end
		end
	end
	if P_IsObjectOnGround(player.mo)
	or player.mo.momz*P_MobjFlip(player.mo) > 0 then
		player.airhammerhit = 12
		if player.mo.momz*P_MobjFlip(player.mo) > 0 then
			player.airhammerhit = 8
		end
		if player.firedrive or player.bot == 1 or player.powers[pw_super] then
			player.airhammerhit = $1/2
		end
		player.mo.momz = $1/2
		S_StartSound(player.mo, sfx_pudpud)
		if player.airhammerfall then
			P_NukeEnemies(player.mo, player.mo, radius)
			P_StartQuake(6*FRACUNIT, 5)
			player.airhammerhit = 24
			for player2 in players.iterate do
				if player2.mo and player2 ~= player
				and R_PointToDist2(player.mo.x, player.mo.y, player2.mo.x, player2.mo.y) < radius
				and abs(player.mo.z - player2.mo.z) < radius then
					P_SetObjectMomZ(player2.mo, 10*FRACUNIT, true)
				end
			end
			if player.firedrive then
				DoFireBlast(player.mo, FRACUNIT*3/4, 3*FRACUNIT, 2*TICRATE, true)
			end
			S_StartSound(player.mo, sfx_bkpoof)
		elseif player.shrinktime < 5*TICRATE then
			for player2 in players.iterate do
				if player2.mo and player2 ~= player
				and R_PointToDist2(player.mo.x, player.mo.y, player2.mo.x, player2.mo.y) < RING_DIST/8
				and abs(player.mo.z - player2.mo.z) < RING_DIST/8 then
					P_SetObjectMomZ(player2.mo, 4*FRACUNIT, true)
				end
			end
			if player.firedrive then
				DoFireBlast(player.mo, FRACUNIT, 2*FRACUNIT, 1, false)
			end
		end
		player.airhammerstate = 0
	end
end
local function DoAirHammerHit(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Recovery period after the air hammer attack
	if not player.powers[pw_nocontrol] then
		player.powers[pw_nocontrol] = 1
	end
	if player.powers[pw_nocontrol]
		player.mo.state = S_PM_HAMSWING
		player.mo.frame = B
	end
	player.airhammerhit = $1 - 1
	player.pflags = $1|PF_JUMPED|PF_SPINNING|PF_THOKKED
	if not player.airhammerhit then
		player.pflags = $1 & ~(PF_SPINNING|PF_THOKKED)
		if not P_IsObjectOnGround(player.mo) then
			player.mo.state = S_PLAY_SPRING
		end
		if P_IsObjectOnGround(player.mo)
			player.pflags = $ &~(PF_JUMPED)
		end
	end
end

local function FireDriveShield(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	if player.mo.skin ~= "paper_mario" return end
	-- Visual effect for Fire Drive shield
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	local fireorb = P_SpawnMobjFromMobj(player.mo, 0, 0, 0, MT_FIREDRIVEORB)
	if (fireorb and fireorb.valid) then
		fireorb.target = player.mo
		fireorb.flags2 = $1|MF2_SHADOW
		fireorb.fuse = 3
		fireorb.frame = (leveltime/2)%4
		fireorb.scale = player.mo.scale*4
		fireorb.momx = player.mo.momx
		fireorb.momy = player.mo.momy
		fireorb.momz = player.mo.momz
	end
end

addHook("MobjThinker", function(hammer)
	-- The thrown hammer will bounce around and disappear within 5 seconds.
	-- If its owner is not alive for some reason, it will disappear.
	if not hammer.target or not hammer.target.valid or not hammer.target.player or not hammer.target.health then
		local removethismobj = hammer
		P_RemoveMobj(removethismobj)
		return
	end
	if hammer.fuse < 5*TICRATE then
		hammer.flags = $1|MF_BOUNCE
	end
	if hammer.flags2 & MF2_SUPERFIRE
	or (hammer.fuse > TICRATE and hammer.tics == 1) then
		P_SpawnGhostMobj(hammer)
	end
	if hammer.state == hammer.info.deathstate
	or hammer.state == hammer.info.xdeathstate then
		hammer.flags = $1|MF_NOGRAVITY
		hammer.momx = 0
		hammer.momy = 0
		hammer.momz = 0
	end
	if not hammer.momx
	and not hammer.momy
	and not hammer.momz then
		P_ExplodeMissile(hammer)
	end
end, MT_PAPERHAMMER)

-- Stealing the next 2 functions from TATails without permission because I'm so :3  --I made TATails... this is a joke
local wallbreak = function(mobj, sectorthing)
	for fof in sectorthing.ffloors()
		if fof.valid and (fof.flags & FF_BUSTUP) and (fof.flags & FF_EXISTS)
			local foffloor = fof.topheight
			local fofceiling = fof.bottomheight
			if fof.b_slope and fof.b_slope.valid
				fofceiling = P_GetZAt(fof.b_slope, mobj.x, mobj.y)
			end
			if fof.t_slope and fof.t_slope.valid
				foffloor = P_GetZAt(fof.t_slope, mobj.x, mobj.y)
			end
			if foffloor >= mobj.z and fofceiling <= mobj.z+mobj.height
				EV_CrumbleChain(nil, fof)
				if fof.master.flags & ML_EFFECT5
					P_LinedefExecute(P_AproxDistance(fof.master.dx, fof.master.dy)>>FRACBITS, mobj, sectorthing)
				end
				return true
			end
		end
	end
end

-- Break The Wall
addHook("MobjLineCollide", function(mobj, line)
	local mlem -- Because :3
	local mlem2 -- Because I'm so :3
	
	if not (line and line.valid and mobj and mobj.valid)
		return
	end
	
	if line.frontsector
	and line.frontsector.valid
		mlem = wallbreak(mobj,line.frontsector)
	end
	
	if line.backsector
	and line.backsector.valid
		mlem2 = wallbreak(mobj, line.backsector)
	end
	
	if mlem
	or mlem2
		P_ExplodeMissile(mobj)
	end
end, MT_PAPERHAMMER)


addHook("MobjThinker", function(warppipe)
	if not (warppipe and warppipe.valid) return end
	-- The warp pipe's color will match its owner's in multiplayer.
	-- It persists even when its owner dies, but it'll disappear when damaged.
	if warppipe.tracer and warppipe.tracer.player
	and warppipe.tracer.player.exitpipe ~= warppipe
	and warppipe.tracer.player.enterpipe ~= warppipe then
		warppipe.tracer = nil
	end
	if warppipe.health and not warppipe.tracer and warppipe.state ~= S_NULL then
		if warppipe.state < S_WARPPIPEDN1 or warppipe.state > S_WARPPIPEDN8 then
			warppipe.state = S_WARPPIPEDN1
		end
	end
	warppipe.scale = FRACUNIT/2
	if multiplayer and warppipe.tracer and warppipe.tracer.valid
	and warppipe.tracer.player then
		if warppipe.tracer.player.skincolor == SKINCOLOR_RED
			warppipe.color = SKINCOLOR_MASTER
		else
			warppipe.color = warppipe.tracer.player.skincolor
		end
	elseif not multiplayer then
		warppipe.color = SKINCOLOR_MASTER
	end
end, MT_WARPPIPE)

addHook("MobjFuse", function(warppipe)
	if not (warppipe and warppipe.valid) return end
	warppipe.tracer = nil
	return true
end, MT_WARPPIPE)


addHook("MobjMoveCollide", function(hammer, thing)
	-- The hammer will pop monitors for the player, bounce off springs, and knock other players back.
	-- A direct hit on a player at maximum power will cause an explosion.
	local cv_friendlyfire = CV_FindVar("friendlyfire")
	if hammer.target.type == MT_PLAYER
	and thing.valid and thing.health
	and abs(hammer.z - thing.z) < thing.height then
		local player = hammer.target.player
		if thing.flags & MF_MONITOR then
			P_KillMobj(thing, hammer, player.mo)
			P_ExplodeMissile(hammer)
		elseif thing.flags & (MF_ENEMY|MF_BOSS) then
			P_DamageMobj(thing, hammer, player.mo, 1)
			P_ExplodeMissile(hammer)
		elseif thing.flags & MF_SPRING then
			hammer.flags = $1 & ~MF_BOUNCE
			if thing.info.damage then
				P_InstaThrust(hammer, thing.angle, thing.info.damage)
			end
			if thing.info.mass then
				hammer.momz = thing.info.mass
			end
			thing.state = thing.info.raisestate
			S_StartSound(thing, thing.info.painsound)
			hammer.fuse = 5*TICRATE + 1
		elseif thing.type == MT_PLAYER
			if cv_friendlyfire.value
				return true
			else
				return false
			end
		//	if thing == hammer.target then
			//	return false
			if (hammer.flags2 & MF2_BOUNCERING) then
				if hammer.flags2 & MF2_SUPERFIRE then
					P_NukeEnemies(hammer, player.mo, RING_DIST)
					S_StartSound(hammer, sfx_bkpoof)
					hammer.flags2 = $1 & ~MF2_SUPERFIRE
				end
				thing.momx, hammer.momx = $1 + hammer.momx/2, $2 - thing.momx/2
				thing.momy, hammer.momy = $1 + hammer.momy/2, $2 - thing.momy/2
				thing.momz, hammer.momz = $1 + hammer.momy/2, $2 - thing.momz/2
				S_StartSound(hammer, hammer.info.activesound)
				hammer.flags2 = $1 & ~MF2_BOUNCERING
				if thing.player.bot == 1 then
					P_DoPlayerPain(thing.player, hammer.target, hammer)
				end
			end
		end
	end
end, MT_PAPERHAMMER)

addHook("MobjMoveCollide", function(cape, thing)
	-- If the cape hits a player or enemy, they'll be spun around.
	if cape.valid and cape.target and cape.target.type == MT_PLAYER
	and thing.valid and thing.health
	and abs(cape.z - thing.z) < thing.height then
		if (thing.type == MT_PLAYER or (thing.flags & (MF_MISSILE|MF_ENEMY|MF_BOSS)))
		and cape.target ~= thing then
			thing.momx = $1 * -2
			thing.momy = $1 * -2
			thing.angle = $1 + ANGLE_180
			if thing.player and thing.player.bot == 1 then
				-- Luigi-bot responds dramatically to Mario's cape.
				thing.angle = cape.angle
				P_Thrust(thing, cape.angle, cape.info.speed*3)
				P_SetObjectMomZ(thing, 12*thing.scale, false)
				thing.player.pflags = $1|PF_SPINNING
			else
				P_Thrust(thing, cape.angle, cape.info.speed/2)
			end
			P_ExplodeMissile(cape)
		end
	end
end, MT_PAPERCAPE)

addHook("MobjCollide", function(mobj, inflictor)
	if inflictor.valid and mobj.threshold < 20
	and ((inflictor.flags & MF_MISSILE) or (inflictor.type == MT_UNIBALL)) and (inflictor.type != MT_LASER)
	and (mobj.target) and not (inflictor.target and (inflictor.target == mobj.target))
	and not (inflictor.z > mobj.z+mobj.height) and not (mobj.z > inflictor.z+inflictor.height)
		mobj.threshold = $ + 1
		inflictor.flags = $|MF_MISSILE
		inflictor.target = mobj.target
		if (inflictor.fuse)
			inflictor.fuse = max(TICRATE, inflictor.fuse)
		end
		inflictor.momz = -inflictor.momz
		inflictor.momy = -inflictor.momy
		inflictor.momx = -inflictor.momx
	end
	return false
end, MT_PAPERCAPE_HITBOX)

-- Hammers and fireballs disappear after a few seconds.
addHook("MobjSpawn", function(hammer)
	if not (hammer and hammer.valid) return end
	hammer.fuse = 5*TICRATE
end, MT_PAPERHAMMER)
addHook("MobjSpawn", function(fireball)
	if not (fireball and fireball.valid) return end
	fireball.fuse = 5*TICRATE
end, MT_FIREBALL)

-- Hammer Throw Arc Things
addHook("ThinkFrame", do
	for player in players.iterate
		if not ((player and player.valid) and (player.mo and player.mo.valid)) continue end
		if player.mo.skin ~= "paper_mario" continue end
		
		if P_IsObjectOnGround(player.mo)
		and not P_PlayerInPain(player)
		and not player.exiting and not player.deadtimer
		and not player.airhammerstate and not player.airhammerhit
		and not player.groundhammerdelay
		and not (player.cape and player.cape.valid)
		and player.shrinktime < 5*TICRATE
		and not player.warppipetime
		and not (player.pflags & PF_SLIDING)
		and (player.cmd.buttons & BT_SPIN)	
			if player.groundhammerthrow == 2 then
				S_StartSound(player.mo, sfx_putput)
			else
				player.hammertarget, player.hammerpeak = DrawHammerArc(player, player.groundhammerthrow/2 + 8)
			end
		else
			if player.groundhammerthrow
			and not P_PlayerInPain(player)
			and not player.exiting and not player.deadtimer
			and not player.airhammerhit then
				S_StopSound(player.mo)
				DoHammerThrow(player, player.groundhammerthrow/2 + 8)	
				if player.firedrive or player.bot == 1 or player.powers[pw_super] then
					player.groundhammerdelay = player.groundhammerthrow/8 + 5
				else
					player.groundhammerdelay = player.groundhammerthrow/4 + 15
				end
			end
			player.groundhammerthrow = 0
		end
		
		if player.warppipetime
			player.mo.flags2 = $1|MF2_SHADOW
		else
			player.mo.flags2 = $1 & ~MF2_SHADOW
		end
		
		if player.exiting and player.mo.state == S_PM_HAMCHARGE
			player.groundhammerthrow = 0
			player.mo.state = S_PLAY_STND
		end
		
		if player.exiting or P_PlayerInPain(player) or player.deadtimer
		or P_IsObjectOnGround(player.mo)
		or player.previouspflags & PF_SLIDING
		or player.cmd.buttons & BT_SPIN
		or player.powers[pw_justsprung]
			if player.tornadojumped
				if player.powers[pw_justsprung]
					player.mo.state = S_PLAY_SPRING
				else
					player.mo.state = S_PLAY_FALL
				end
			end
			player.tornadojumped = false
		end
		
		if player.airhammerstate == 1 then
		DoAirHammer(player, RING_DIST)
		end
		if player.airhammerhit > 0 then
			DoAirHammerHit(player)
		end
	end
end)

-- Tornado Jump
addHook("AbilitySpecial", function(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid))
	and player.mo.skin ~= "paper_mario"
		return
	end
	
	if CheckIfCanUseAbility(player) and not (player.pflags & PF_THOKKED) then
		DoTornadoJump(player, 12*player.jumpfactor)
	end
end)

-- Chat emotes are fun
addHook("PlayerMsg", function(p, msgtype, target, msg)
    if (msgtype) then return end
    
    local mo = p.mo
    if not ((mo and mo.valid) and mo.health) then return end
    if (mo.skin !="paper_mario") then return end
    if (mo.state != S_PLAY_STND and mo.state != S_PLAY_WAIT) then return end
    if (mo.pm_jojo) then return end
    if (p.speed) then return end
    if not (P_IsObjectOnGround(mo)) then return end
        
    msg = $:lower()
    if (msg == "jojo pose" and not mo.pm_jojo)
	//	mo.state = S_PM_POSE
        mo.pm_jojo = true
		if S_MusicName(p) ~= "PMJOJO" then
            S_ChangeMusic("PMJOJO", true, p)
		end
		if multiplayer
			p.multiframetimer = $ + 1
		end
    end
end)

local MusicPos = {
  408,
  1406,
  2177,
  3129,
  4036,
  4898,
  5805,
  6679,
  7600,
  8507,
  9414,
  10276,
  11137,
  12044,
  12942,
  13849
}

local MultiFrame = {
    [14] = true,
    [48] = true,
    [80] = true,
    [112] = true,
    [144] = true,
    [175] = true,
    [207] = true,
    [239] = true,
    [268] = true,
    [300] = true,
    [331] = true,
    [364] = true,
    [397] = true,
    [426] = true,
    [457] = true,
    [490] = true,
}

local LastPosition = 0

-- Main Thinker
addHook("PlayerThink", function(player)
	if (player.mo == nil)
	or player.powers[pw_carry] == CR_NIGHTSMODE
	or (player.mo.skin ~= "paper_mario") then
		-- If you change skin while shrunk, you'll grow back to normal size
		if player.mo and player.mo.valid then
			if player.shrinktime and player.shrinktime > 5*TICRATE then
				player.mo.destscale = FRACUNIT
			end
			-- No one else gets to use Fire Drive
			if player.firedrive and not mariomode then
				player.powers[pw_shield] = SH_ARMAGEDDON
				P_SpawnShieldOrb(player)
			end
			-- Paper Sprites get reverted
			if player.mo.renderflags & RF_PAPERSPRITE
				player.mo.renderflags = $& ~RF_PAPERSPRITE
			end
			if G_RingSlingerGametype() and player.powers[pw_shield] & SH_FIREFLOWER then
				player.powers[pw_shield] = $1 & ~SH_FIREFLOWER
			end
			if player.exitpipe and player.exitpipe.valid then
				P_KillMobj(player.exitpipe)
			end
			if player.enterpipe and player.enterpipe.valid then
				P_KillMobj(player.enterpipe)
			end
			player.exitpipe = nil
			player.enterpipe = nil
		end
		player.shrinktime = 0
		player.firedrive = false
		return
	end
	
	if player.multiframetimer == nil
		player.multiframetimer = 0
	end
		
	
	if player.multiframetimer >= 1
		player.multiframetimer = $ + 1
		if player.multiframetimer == 504
			player.multiframetimer = 1
		end
	end
	
	if player.deadtimer or player.exiting or not player.shrinktime then
		if player.shrinktime and player.shrinktime > 5*TICRATE then
			player.mo.destscale = FRACUNIT
		end
		player.shrinktime = 0
	end
	
	-- Fuck waterslides, they ruin everything.
	if player.pflags & PF_SLIDING then
		if player.groundhammerdelay then
			player.groundhammerdelay = 1
		end
		if player.shrinktime > 5*TICRATE then
			player.shrinktime = 5*TICRATE
		end
	end
	
	-- Fix infinite mercy invulnerability for realsies
	if player.powers[pw_flashing] == 3*TICRATE
	and not P_PlayerInPain(player) and not player.deadtimer then
		P_DoPlayerPain(player)
		if player.groundhammerdelay then
			player.groundhammerdelay = 1
		end
	end
	
	-- The Whirlwind shield lets you glide further with the Tornado Jump.
	-- The Armageddon shield is replaced with Fire Drive,
	-- which lets you hammer faster, shoot fireballs, and create fire bursts with your hammer.
	-- In Match, Fire Drive's fireballs replace your normal weapons.
	if player.powers[pw_shield] == SH_ARMAGEDDON then
		player.powers[pw_shield] = SH_FIREFLOWER
	end
	if (player.powers[pw_shield] & SH_NOSTACK or player.mo.eflags & MFE_UNDERWATER)
	and not (player.powers[pw_invulnerability] or player.powers[pw_super]) then
		if player.firedrive and player.mo.eflags & MFE_UNDERWATER then
			P_FlashPal(player, PAL_NUKE, 5)
		end
		player.powers[pw_shield] = $1 & ~SH_FIREFLOWER
	end
	if (player.powers[pw_shield] & SH_NOSTACK)
	and not (player.powers[pw_shield] & SH_ARMAGEDDON) then
		player.powers[pw_shield] = $1 & ~SH_FIREFLOWER
	end
	player.firedrive = (player.powers[pw_shield] == SH_FIREFLOWER)
	
	ButtonHandler(player)
		
	-- TORNADO JUMP: double-jump to launch upward, then hold JUMP to float down.
	-- You can strafe back and forth quickly, but your maximum speed is reduced.
	
	if player.tornadojumped then
		DoTornadoFall(player)
		player.mo.state = S_PLAY_ROLL
		player.normalspeed = skins[player.mo.skin].normalspeed
		player.acceleration = skins[player.mo.skin].acceleration*3
		player.groundhammerdelay = 0
	elseif player.firedrive then
		player.normalspeed = skins[player.mo.skin].normalspeed*5/4
		player.acceleration = skins[player.mo.skin].acceleration*3/2
	else
		if not player.xmomtoggledoff
			player.fakenormalspeed = skins[player.mo.skin].normalspeed
			player.acceleration = skins[player.mo.skin].acceleration
		else
			player.normalspeed = skins[player.mo.skin].normalspeed
			player.acceleration = skins[player.mo.skin].acceleration
		end
	end

	if player.previouspflags == nil then
		player.previouspflags = PF_JUMPDOWN
	end
	if player.powersuper == nil then
		player.powersuper = 0
	end
	
	-- HAMMER THROW: Hold SPIN on the ground to ready your hammer, then release to throw it.
	
	if not player.groundhammerthrow or player.airhammerhit then
		player.groundhammerthrow = 0
	end
	
	if P_IsObjectOnGround(player.mo)
	and not P_PlayerInPain(player)
	and not player.exiting and not player.deadtimer
	and not player.airhammerstate and not player.airhammerhit
	and not player.groundhammerdelay
	and not (player.cape and player.cape.valid)
	and player.shrinktime < 5*TICRATE
	and not player.warppipetime
	and not (player.pflags & PF_SLIDING)
	and (player.cmd.buttons & BT_SPIN) then
		if not player.powers[pw_nocontrol] then
			player.powers[pw_nocontrol] = 1
		end
		if player.mo.state == S_PM_HAMCHARGE
			P_InstaThrust(player.mo, 0, 0)
		end
		if player.groundhammerthrow == 99 and not player.firedrive then
			P_SetObjectMomZ(player.mo, 4*FRACUNIT, true)
		end
		if player.groundhammerthrow < 100 then
			if player.firedrive then
				player.groundhammerthrow = $1 + 2
			else
				player.groundhammerthrow = $1 + 1
			end
		end
		if player.groundhammerthrow
		and player.mo.state ~= S_PM_HAMCHARGE
			player.mo.state = S_PM_HAMCHARGE
		end
	end
	
	if player.groundhammerdelay then
		if not player.powers[pw_nocontrol] then
			player.powers[pw_nocontrol] = 1
		end
		player.mo.state = S_PM_HAMYEET
		player.pflags = $1|PF_THOKKED & ~PF_JUMPED
		player.groundhammerdelay = $1 - 1
		if player.groundhammerdelay < 0 then
			player.groundhammerdelay = 0
		end
	end

		-- AIR HAMMER: Press SPIN while jumping to slam into the ground with the hammer.
	
	if player.airhammerstate == nil then
		player.airhammerstate = 0
	end
	if player.airhammerhit == nil or player.airhammerstate == -1 then
		player.airhammerhit = 0
	end
	
	if P_PlayerInPain(player) then
		player.airhammerstate = -1
	elseif player.pflags & PF_SLIDING or player.powers[pw_carry] ~= CR_NONE then
		player.airhammerstate = 0
	end
	
	if player.hammertapping and not player.exiting
	and not player.weapondelay and (player.pflags & PF_JUMPED)
	and (player.powers[pw_carry] == CR_NONE)
	and player.airhammerstate == 0
		player.airhammerstate = 1
		S_StartSound(player.mo, sfx_putput)
		P_SetObjectMomZ(player.mo, 0, false)
		player.tornadojumped = false
		player.pflags = $1|PF_THOKKED|PF_SPINNING
		player.powers[pw_strong] = STR_SPRING|STR_ANIM|STR_BUST
		player.mo.state = S_PM_HAMCHARGE
	end

	if P_IsObjectOnGround(player.mo)
	or player.powers[pw_justsprung] then
		player.airhammerstate = 0
	end
	
	-- Break blocks while using the air hammer
	if player.airhammerhit or player.airhammerstate == 1 then
		player.charability = CA_GLIDEANDCLIMB
	else
		player.charability = CA_NONE
	end
		-- Are you falling fast enough to explode?
	if player.airhammerstate == 1 and (player.shrinktime < 5*TICRATE)
	and (player.mo.momz*P_MobjFlip(player.mo) < -36*player.mo.scale) then
		if not player.airhammerfall then
			S_StartSound(player.mo, sfx_spndsh)
		end
		P_SpawnGhostMobj(player.mo)
		player.airhammerfall = true
	else
		player.airhammerfall = false
	end
	
	-- CAPE: press CUSTOM 1 to reflect projectiles and spin players around.
	
	if player.capetapping and not player.weapondelay
	and not (player.cmd.buttons & BT_TOSSFLAG)
	and not P_PlayerInPain(player) and not player.powers[pw_nocontrol]
	and not player.deadtimer and not player.exiting
	and not (player.pflags & PF_SLIDING)
	and not player.powers[pw_carry] ~= CR_NONE
	and not player.tornadojumped
	and not player.groundhammerthrow and not player.groundhammerdelay
	and not player.airhammerstate and not player.airhammerhit
	and player.shrinktime < 5*FRACUNIT and not player.warppipetime then
		player.cape = P_SPMAngle(player.mo, MT_PAPERCAPE, player.mo.angle, 0, 0)
		player.mo.momz = 0
		player.pflags = $1|PF_THOKKED & ~PF_JUMPED
		player.weapondelay = 15
		local mo = player.mo
		local capehitbox = P_SpawnMobjFromMobj(mo,P_ReturnThrustX(mo, mo.angle, FU * 55),P_ReturnThrustY(mo, mo.angle, FU * 55),0,MT_PAPERCAPE_HITBOX)
	    capehitbox.fuse = 12
		capehitbox.target = player.mo
		P_InstaThrust(capehitbox, mo.angle, 4*FRACUNIT)
	end
	if player.cape and player.cape.valid then
		-- While Mario's throwing a cape, he can't do anything else.
		-- However, he's free to use the cape under most conditions.
		player.mo.momx = $1*2/3
		player.mo.momy = $1*2/3
		player.mo.momz = $1*2/3
		player.cape.momx = $1*7/8
		player.cape.momy = $1*7/8
		player.cape.momz = $1*2/3
		P_ResetPlayer(player)
		player.mo.state = S_PM_HAMYEET
		if player.weapondelay <= 0 then
			player.weapondelay = 1
		end
		if player.cape.state == S_PAPERCAPE2 then
			player.cape.scale = 3*player.mo.scale
		else
			player.cape.scale = 2*player.mo.scale
		end
		if player.cape.state == S_PAPERCAPE3 and player.cape.tics == 1 then
			player.mo.state = S_PLAY_FALL
		end
	end
	
	-- SHRINK: press CUSTOM 2 to become tiny.
	-- You can walk on water and jump just as high as you normally can,
	-- but you can't use your hammer or throw rings.
	-- Pressing SPIN while Tornado jumping will simply cancel the jump.
	
	player.canshrink = P_IsObjectOnGround(player.mo)
	and (leveltime >= 5) and not player.weapondelay
	and not player.exiting and not (player.pflags & (PF_STASIS|PF_SLIDING))
		if player.shrinktapping and player.canshrink then
		if not player.shrinktime then
			if (player.mo.health > 10 or not G_RingSlingerGametype()) then
				player.shrinktime = 15*TICRATE
				if G_RingSlingerGametype() then
					P_GivePlayerRings(player, -10)
				end
			end
		elseif player.shrinktime > 5*TICRATE then
			player.shrinktime = 5*TICRATE
		end
	end
	
	if player.shrinktime then
		-- physics differences
		if player.shrinktime < 15*TICRATE and player.shrinktime > 5*TICRATE then
			player.runspeed = FRACUNIT
			player.charflags = skins[player.mo.skin].flags|SF_RUNONWATER|SF_NOSKID
			player.jumpfactor = 2*skins[player.mo.skin].jumpfactor
			-- Hanging from stuff and changing scale is tricky.
			-- Hopefully this will prevent any freaky stuff from happening.
			if player.pflags & PF_SLIDING or player.powers[pw_carry] ~= CR_NONE then
				player.shrinktime = 5*TICRATE + 2
			end
		elseif player.firedrive then
			player.runspeed = skins[player.mo.skin].runspeed*3/2
			player.charflags = skins[player.mo.skin].flags|SF_NOSKID
			player.jumpfactor = skins[player.mo.skin].jumpfactor*5/4
		else
			player.runspeed = skins[player.mo.skin].runspeed
			player.charflags = skins[player.mo.skin].flags
			player.jumpfactor = skins[player.mo.skin].jumpfactor
		end
		
		if player.shrinktime == 15*TICRATE then
			-- initial shrink-down
			player.mo.destscale = FRACUNIT/4
			P_InstaThrust(player.mo, player.mo.angle, 0)
			S_StartSound(player.mo, sfx_pmsmal)
		elseif player.shrinktime == 5*TICRATE then
			-- regrow
			player.mo.destscale = FRACUNIT
			S_StartSound(player.mo, sfx_pmbig)
		end
		if player.mo.scale != player.mo.destscale then
			-- RIP super shrink jump shenanigans
			P_SpawnGhostMobj(player.mo)
			if player.mo.scale > player.mo.destscale then
				if not player.powers[pw_nocontrol] then
					player.powers[pw_nocontrol] = 1
				end
				player.mo.state = S_PLAY_FALL
			end
		end
		if not player.powers[pw_super]
		or player.shrinktime <= 5*TICRATE
		or player.shrinktime >= 15*TICRATE then
			player.shrinktime = $1 - 1
		end
	else
		if player.firedrive then
			player.runspeed = skins[player.mo.skin].runspeed*3/2
			player.charflags = skins[player.mo.skin].flags|SF_NOSKID
			player.jumpfactor = skins[player.mo.skin].jumpfactor*5/4
		elseif not player.xmomtoggledoff
			player.fakenormalspeed = skins[player.mo.skin].normalspeed
			player.runspeed = skins[player.mo.skin].runspeed
			player.charflags = skins[player.mo.skin].flags
			player.jumpfactor = skins[player.mo.skin].jumpfactor
		else
			player.normalspeed = skins[player.mo.skin].normalspeed
			player.runspeed = skins[player.mo.skin].runspeed
			player.charflags = skins[player.mo.skin].flags
			player.jumpfactor = skins[player.mo.skin].jumpfactor
		end
	end
	
	-- WARP PIPE: press CUSTOM 3 to create a teleport point that you can return to later
	
	if not player.deadtimer and not player.exiting
	and P_IsObjectOnGround(player.mo)
	and (player.mo.state == S_PLAY_STND or player.mo.state == S_PLAY_WALK or player.mo.state == S_PLAY_RUN or player.mo.state == S_PLAY_WAIT)
	and not player.warppipetime
	and (player.mo.health > 20 or not G_RingSlingerGametype())
	and player.warptapping then
		player.warppipetime = 50
		player.weapondelay = 50
		player.powers[pw_nocontrol] = 50
		if G_RingSlingerGametype() then
			P_GivePlayerRings(player, -20)
		end
		S_StartSound(player.mo, sfx_mario9)
		player.enterpipe = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_WARPPIPE)
		if player.enterpipe then
			player.enterpipe.tracer = player.mo
			player.enterpipe.angle = player.mo.angle
		end
	end
	
	if player.warppipetime then
		if P_PlayerInPain(player) and player.enterpipe then
			P_KillMobj(player.enterpipe)
		end
		if player.warppipetime == 15 then
			if player.exitpipe and player.exitpipe.valid and player.exitpipe.health then
				P_SetOrigin(player.mo, player.exitpipe.x, player.exitpipe.y, player.exitpipe.z)
				player.mo.angle = player.exitpipe.angle
				S_StartSound(player.mo, sfx_mario8)
			end
		elseif player.warppipetime == 1 then
			if player.exitpipe and player.exitpipe.valid and player.exitpipe.health then
				player.exitpipe.fuse = 15
			end
			player.exitpipe = player.enterpipe
			player.enterpipe = nil
		end
		player.warppipetime = $1 - 1
		if P_PlayerInPain(player) then
			player.warppipetime = 0
		end
	end

		-- INVULNERABILITY	
	local colortime = (leveltime%14)/2
	if player.powers[pw_invulnerability] or player.powers[pw_super] then
		if colortime == 6 then
			player.mo.color = SKINCOLOR_RED
		elseif colortime == 5 then
			player.mo.color = SKINCOLOR_ORANGE
		elseif colortime == 4 then
			player.mo.color = SKINCOLOR_YELLOW
		elseif colortime == 3 then
			player.mo.color = SKINCOLOR_MASTER
		elseif colortime == 2 then
			player.mo.color = SKINCOLOR_BLUE
		elseif colortime == 1 then
			player.mo.color = SKINCOLOR_PURPLE
		else
			player.mo.color = SKINCOLOR_PINK
		end
	else
		player.mo.color = player.skincolor
		if player.firedrive 
			FireDriveShield(player)
		end
	end
	
	
	-- reset everything when hurt or otherwise occupied		
	
	if (player.mo.state == S_PLAY_GASP) or P_PlayerInPain(player)
		player.tornadojumped = false
		player.groundhammerthrow = 0
		player.groundhammerdelay = 0
		player.airhammerstate = 0
		player.airhammerhit = 0
		player.warppipetime = 0
	end

	-- Rather than falling through the stage on death, Mario lands on his face
	-- This actually results from a bug with the mercy invuln handler
	if player.deadtimer
	and player.mo.state ~= S_PLAY_DEAD
		player.mo.flags = $1 & ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT)
		player.mo.state = S_PLAY_DEAD
		//P_SetMobjStateNF(player.mo, S_PLAY_DIE)
		player.mo.flags = $1 & ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT)
		player.groundhammerthrow = 0
		player.groundhammerdelay = 0
		player.tornadojumped = false
		player.airhammerstate = -1
		player.airhammerhit = 0
	end
	if player.deadtimer and P_IsObjectOnGround(player.mo) then
		P_InstaThrust(player.mo, 0, 0)
	end
	
	-- Disable Match weapons during certain actions
	if player.groundhammerthrow or player.groundhammerdelay
	or player.airhammerstate or player.airhammerhit
	or player.shrinktime > 5*TICRATE then
		if not player.weapondelay then
			player.weapondelay = 1
		end
	end
	player.previouspflags = player.pflags
	player.powersuper = player.powers[pw_super]
	
	-- Emote Time
	if player.mo.pm_jojo
		-- If you're not posing, kill the music
		if player.speed ~= 0
			player.mo.state = S_PLAY_STND
			player.mo.pm_jojo = false
			if S_MusicName(player) == "PMJOJO"
				P_RestoreMusic(player)
			end
		end
		if not P_IsObjectOnGround(player.mo)
			player.mo.state = S_PLAY_JUMP
			player.mo.pm_jojo = false
			if S_MusicName(player) == "PMJOJO"
				P_RestoreMusic(player)
			end
		end
		
		-- Lets Control The Frames
		if not multiplayer
			for i = 1, #MusicPos
				if S_GetMusicPosition() >= MusicPos[i]
				and LastPosition < MusicPos[i]
					player.mo.state = S_PM_POSE
					local poseghost = P_SpawnGhostMobj(player.mo)
					poseghost.destscale = $ * 2
					poseghost.scalespeed = poseghost.scale/5
					poseghost.renderflags = $ & ~RF_PAPERSPRITE
					player.posetimer = 2
					//player.mo.tics = 30
				end
			end
		else
			if MultiFrame[player.multiframetimer]
                player.mo.state = S_PM_POSE
                local poseghost2 = P_SpawnGhostMobj(player.mo)
                poseghost2.destscale = $ * 2
                poseghost2.scalespeed = poseghost2.scale/5
                poseghost2.renderflags = $ & ~RF_PAPERSPRITE
                player.posetimer = 2
            end
		end
		
		if player.posetimer == nil
			player.posetimer = 0
		end
		if player.posetimer ~= 0
			player.posetimer = $ - 1
			player.camerascale = FU/2
			if player.posetimer == 0
			or player.mo.skin ~= "paper_mario"
			or not player.mo.pm_jojo
				player.camerascale = FU
			end
		end
		LastPosition = S_GetMusicPosition()
    else
		player.multiframetimer = 0
	end
	
	if ((player.cmd.buttons & BT_TOSSFLAG) and player.capetapping
	and not player.mo.pm_jojo
	and (player.mo.state == S_PLAY_STND or player.mo.state == S_PLAY_WAIT))
		player.mo.state = S_PM_BLAH
		player.pmblah = true
		if not player.blahsounding
			S_StartSound(player.mo, sfx_pmblah)
			player.blahsounding = true
		end
	end
	if player.pmblah
		if player.speed ~= 0 or player.mo.state ~= S_PM_BLAH
			player.mo.state = S_PLAY_STND
            player.pmblah = false
			player.blahsounding = false
        end
		if not P_IsObjectOnGround(player.mo)
			player.pmblah = false
			player.mo.state = S_PLAY_JUMP
			player.blahsounding = false
		end
    end
end)

local function hud_papermeter(v, player)
	
	if not player.mo
	or player.powers[pw_carry] == CR_NIGHTSMODE
	or player.mo.skin ~= "paper_mario" then
		return
	end

	-- Want the bars somewhere else? Change these values:
	local THROW_X = 132
	local THROW_Y = 152
	local SHRINK_X = 110
	local SHRINK_Y = 186
	
	local shrink_amount = 0
	local throw_amount = 0
	local throw_color = 73
	local shrink_color = 124
	local shrink_textflags = 0
	
	if player.groundhammerthrow then
		throw_amount = player.groundhammerthrow + 1
		if throw_amount > 72 then
			throw_color = 36
		elseif throw_amount > 40 then
			throw_color = 54
		end
	--[[ The power gauge for using your hammer in the air may be confusing, so it's disabled here
	elseif player.airhammerstate == 1 and (player.shrinktime < 5*TICRATE) then
		throw_amount = player.mo.momz*P_MobjFlip(player.mo)*100/(-36*player.mo.scale)
		if throw_amount >= 100 then
			throw_amount = 100
			throw_color = 128
		elseif player.cmd.buttons & BT_SPIN then
			throw_color = 87
		end
	]]--
	end
	
	if player.shrinktime >= 5*TICRATE then
		if player.powers[pw_super] then
			shrink_amount = 100
			if leveltime%10 < 5 then
				shrink_color = 35
			end
		else
			shrink_amount = (player.shrinktime - 5*TICRATE) * 10/TICRATE
		end
		shrink_textflags = V_YELLOWMAP
	else
		shrink_amount = 100 - (player.shrinktime * 20/TICRATE)
		if shrink_amount < 100 then
			shrink_color = 35
		end
	end

	if throw_amount > 8 then
		v.drawFill(THROW_X-1, THROW_Y - 51, 8, 52, 31)
		v.drawFill(THROW_X, THROW_Y - throw_amount/2, 6, throw_amount/2, throw_color)
	end
	if player.shrinktime then
		v.drawFill(SHRINK_X-1, SHRINK_Y-1, 102, 12, 31)
		v.drawFill(SHRINK_X, SHRINK_Y, shrink_amount, 10, shrink_color)
		v.drawString(SHRINK_X+50, SHRINK_Y+1, "SHRINK", shrink_textflags, "center")
	end
	if R_PointToDist(player.mo.x, player.mo.y) < FRACUNIT and player.firedrive then
		v.draw(292, 4, v.cachePatch("FDRVICON"))
	end

end

hud.add(hud_papermeter)

-- Paper Sprite Controls (Code From Vivian by PigOverlord. Vivian is not Open Assets on the MB but the code inside her states she is)
//-1 if a is left of b, 1 if right, 0 if equal
local function anglecompare(anglea, angleb)
	local adiff = anglea - angleb
	local bdiff = angleb - anglea
	if adiff < bdiff 
		return -1
	end
	if adiff > bdiff
		return 1
	end
	return 0
end

//average 2 angles without quadrant shenanigans. use factor to weight towards anglea
local function angleavg(anglea, angleb, factor)
	local x = 0
	local y = 0
	
	x = x + factor * cos(anglea) + cos(angleb)
	y = y + factor * sin(anglea) + sin(angleb)
	
	return R_PointToAngle2(0, 0, x, y)
end

addHook("PreThinkFrame", do
	for player in players.iterate
		if not ((player and player.valid) and (player.mo and player.mo.valid)) continue end
		if player.mo.skin == "paper_mario"
		or player.otherpapersprites
			if player.mo.currangle == nil
				player.mo.stomp = false
				player.mo.currangle = -ANGLE_90
				player.mo.prevangle = -ANGLE_90
				player.mo.angleoffset = fixangle(0 * FRACUNIT)
			end
			player.drawangle = player.mo.currangle
			if not (player.ghost == nil)
				player.mo.spritexscale = player.ghost.spritexscale
			end
		end
	end
end)

addHook("PlayerThink", function(player)
	if not ((player and player.valid) and (player.mo and player.mo.valid)) return end
	
	local using = player.mo.skin == "paper_mario" or player.otherpapersprites
	
	if player.pmpsprites and not using
		player.mo.renderflags = $ & ~RF_PAPERSPRITE
	elseif using
		player.mo.renderflags = RF_PAPERSPRITE
		player.mo.currangle = player.drawangle
		local flip = false
		local ncurrangle = player.mo.currangle
		if player.cmd.forwardmove == 0 and player.cmd.sidemove == 0 
			ncurrangle = player.mo.angle + ANGLE_90
			if player.mo.lastflat
				ncurrangle = player.mo.angle - ANGLE_90
			end
		end
		local flatangle = player.mo.angle + ANGLE_90
		player.mo.lastflat = false
		if anglecompare(ncurrangle, player.mo.angle) == -1 then
			flatangle = player.mo.angle - ANGLE_90
			player.mo.lastflat = true
		end
		local targetangle = angleavg(flatangle, ncurrangle, 1)
		player.drawangle = angleavg(player.mo.prevangle or 0, targetangle or 0, 3) + player.mo.angleoffset
		player.mo.prevangle = player.drawangle
		if anglecompare(player.drawangle, player.mo.angle) == -1 then
			flip = true
		end
	end
	
	player.pmpsprites = using
end)

-- Lets Give Paper His Own Music
local replacePaperMusic = {
	_clear = "PMVIC",
	_gover = "PMOVER",
	_inv = "PMINV"
}

addHook("MusicChange", function(old, new)
	if (consoleplayer and consoleplayer.valid)
	and skins[consoleplayer.skin].name == "paper_mario"
		if old ~= new
			local pmmusic = replacePaperMusic[new]
			if pmmusic
				return S_MusicName() == pmmusic or pmmusic
			end
		end
	end
end)

-- Cape Actually Reflects Projectiles
/*addHook("MobjMoveCollide", function(cape, inflictor)
	//if not (cape and cape.valid) return end
	if inflictor and inflictor.valid
	and (inflictor.flags & MF_MISSILE) //and inflictor.type ~= MT_LASER)
		print("AAAAAAAHHHHHHHHH")
		inflictor.momx = -inflictor.momx
		inflictor.momy = -inflictor.momy
		inflictor.momz = -inflictor.momz
		inflictor.angle = $ + ANGLE_180
	end
end, MT_PAPERCAPE)*/