//reveries v4 (product of katsy)

if (VERSION == 202) and (SUBVERSION < 10)
	local function blackout(v)
		v.drawFill()
		v.drawString(160, 100, ("this mod requires version 2.2.10 or higher"),V_REDMAP, "center")
	end
	hud.add(blackout, "title")
	hud.add(blackout, "game")
	error("Your copy of 2.2 is obsolete and not compatible with this mod. Please update to version 2.2.10 or higher.", 0)
end

if rphys then
	local function blackout(v)
		v.drawFill()
		v.drawString(160, 100, ("wrong exe"),V_REDMAP, "center")
	end
	hud.add(blackout, "title")
	hud.add(blackout, "game")
	error("This file cannot be used with the rphys exe.", 0)
end

//soc

local function SafeFreeslot(...)
	for _, item in ipairs({...})
		if rawget(_G, item) == nil
			freeslot(item)
		end
	end
end

SafeFreeslot(
	"SKINCOLOR_SUPERMAGENTA1",
	"SKINCOLOR_SUPERMAGENTA2",
	"SKINCOLOR_SUPERMAGENTA3",
	"SKINCOLOR_SUPERMAGENTA4",
	"SKINCOLOR_SUPERMAGENTA5",
	"S_REVI_AIRFIRE",
	"SPR_PEEL",
	"SPR2_FAIR")
	
skincolors[SKINCOLOR_SUPERMAGENTA1] = {
    name = "Super Magenta 1",
    ramp = {0,0,0,0,0,0,0,0,0,0,200,200,176,176,177,178},
    accessible = false}

skincolors[SKINCOLOR_SUPERMAGENTA2] = {
    name = "Super Magenta 2",
    ramp = {0,200,176,176,177,177,178,178,179,179,179,179,180,180,181,181},
    accessible = false}

skincolors[SKINCOLOR_SUPERMAGENTA3] = {
    name = "Super Magenta 3",
    ramp = {176,176,177,177,178,178,179,179,179,179,180,180,181,181,182,182},
    accessible = false}

skincolors[SKINCOLOR_SUPERMAGENTA4] = {
    name = "Super Magenta 4",
    ramp = {177,178,178,179,179,179,179,180,180,181,181,182,182,183,184,185},
    accessible = false}

skincolors[SKINCOLOR_SUPERMAGENTA5] = {
    name = "Super Magenta 5",
    ramp = {178,178,179,179,179,179,180,180,181,181,182,182,183,184,185,186},
    accessible = false}

sfxinfo[sfx_mswing].caption = "Whirl"
sfxinfo[sfx_shield].caption = "Shield"
sfxinfo[sfx_cdfm35].caption = "/"
sfxinfo[sfx_s3k71].caption = "Thorn Shield"
sfxinfo[sfx_s3ka1].caption = "Droning"
sfxinfo[sfx_s3ka2].caption = "Thrust"
sfxinfo[sfx_s3kb6].caption = "Air dash"
sfxinfo[sfx_s3kcfs].caption = "Elemental rise"
sfxinfo[sfx_kc40].caption = "Jet"
sfxinfo[sfx_kc5b].caption = "Launch"
sfxinfo[sfx_kc65].caption = "/"

states[S_PLAY_PAIN] = {SPR_PLAY, SPR2_PAIN, 70, NULL, 0,  0, S_PLAY_FALL}
states[S_PLAY_STUN] = {SPR_PLAY, SPR2_STUN, 70, NULL, 0,  0, S_PLAY_FALL}
states[S_PLAY_FIRE_FINISH] = {SPR_PLAY, SPR2_FIRE, 5, NULL, S_PLAY_STND, 0, S_PLAY_STND}
states[S_PLAY_GLIDE_LANDING] = {SPR_PLAY, SPR2_LAND, 7, NULL, 0,  0, S_PLAY_STND}
states[S_REVI_AIRFIRE] = {SPR_PLAY, SPR2_FAIR, 7, NULL, S_REVI_AIRFIRE, 0, S_PLAY_FALL}

function P_SetObjectMomZ(mo, value, relative)
	if (mo.eflags & MFE_VERTICALFLIP)
		value = -value
	end
	value = FixedMul(value, mo.scale)
	if (relative == true)
		mo.momz = $+value
	else
		mo.momz = value	
	end
	if (mo.standingslope) and (mo.player) and not (mo.standingslope.flags & SL_NOPHYSICS) and ((mo.standingslope.normal.x != 0) or (mo.standingslope.normal.y != 0))
	and (mo.state != S_PLAY_BOUNCE_LANDING) and not P_PlayerInPain(mo.player) and not mo.player.powers[pw_tailsfly] and not (mo.reverieslaunch)
		mo.momz = $*3/4
		mo.reverieslaunch = true
	end
end

function A_ZThrust(actor, var1, var2)
	if (var2 & 65535)
		actor.momy = 0
		actor.momx = 0
	end
	P_SetObjectMomZ(actor, var1<<FRACBITS, !(var2>>FRACBITS))
end

//tables

local FRACUNIT = FRACUNIT
local FRACBITS = FRACBITS
local TICRATE = TICRATE
local greaseremover

local blacklist = {}
blacklist["samus"] = true
blacklist["basesamus"] = true
blacklist["sms"] = true
blacklist["sage"] = true
blacklist["elfilin"] = true
blacklist["werehog"] = true
blacklist["juniosonic"] = true
blacklist["eggrobo"] = true

local frictchars = {}
frictchars["mario"] = true
frictchars["luigi"] = true
frictchars["gamma"] = true
frictchars["modernsonic"] = true
frictchars["bowser"] = true
frictchars["plagueknight"] = true
frictchars["kiryu"] = true
frictchars["iclyn"] = true
frictchars["flarethetrex"] = true

local norollcontrol = {}
norollcontrol["blaze"] = true
norollcontrol["adventuresonic"] = true

if not solchars
    rawset(_G, "solchars", {})
end

//functions

local function isplayer(mo)
	if mo.player.revi
		return true
	else
		return false
	end
end
local function zpos(position, item)
	return (position.z + (position.height - FixedMul(mobjinfo[item].height, position.scale))/2)
end

local function fangfire(player)
	if (player.panim != PA_ABILITY2) and not (player.pflags & PF_SPINDOWN) and not (player.pflags & PF_BOUNCING)
	and not ((player.pflags & PF_JUMPED) and not (player.pflags & PF_NOJUMPDAMAGE)) and (player.mo.state != S_REVI_AIRFIRE) and (player.panim != PA_PAIN) and not player.powers[pw_carry]
		if P_IsObjectOnGround(player.mo)
			player.mo.state = S_PLAY_FIRE
		else
			player.mo.state = S_REVI_AIRFIRE
		end
		local lockon = P_LookForEnemies(player, false, true)
		local cork = nil
		player.weapondelay = 24
		player.pflags = $ & ~PF_SPINNING
		P_Thrust(player.mo, R_PointToAngle2(0, 0, player.rmomx, player.rmomy), -(FixedHypot(player.rmomx, player.rmomy))*2/5)
		if (lockon and lockon.valid)
			player.mo.angle = R_PointToAngle2(player.mo.x, player.mo.y, lockon.x, lockon.y)
			cork = P_SpawnPointMissile(player.mo, lockon.x, lockon.y, zpos(lockon, player.revitem), player.revitem, player.mo.x, player.mo.y, zpos(player.mo, player.revitem))
		else
			cork = P_SpawnPointMissile(player.mo, player.mo.x+P_ReturnThrustX(nil, player.mo.angle, FRACUNIT), player.mo.y+P_ReturnThrustY(nil, player.mo.angle, FRACUNIT), zpos(player.mo, player.revitem), player.revitem, player.mo.x, player.mo.y, zpos(player.mo, player.revitem))
			if (cork and cork.valid)
				cork.flags = $ & ~MF_NOGRAVITY
				cork.momx = $/2
				cork.momy = $/2
			end
			player.drawangle = player.mo.angle
		end
	end
end

local function stupidbouncesectors(mobj, sector)
	for fof in sector.ffloors()
		if not (fof.fofflags & FOF_BOUNCY) and (GetSecSpecial(fof.master.frontsector.special, 1) != 15)
			continue
		end
		if not (fof.fofflags & FOF_EXISTS)
			continue
		end
		if (mobj.z+mobj.height+mobj.momz < fof.bottomheight) or (mobj.z-mobj.momz > fof.topheight)
			continue
		end
		return true
	end
end

local function spawnpeelout(mobj, number)
	local force = 11
	force = ($-number)<<FRACBITS
	local shiftangle = ANGLE_90
	local adjustangle = ANG15
	for r = 1, 2 do
		if (r == 2) then
			shiftangle = -$
			adjustangle = -$
		end
		local shiftx = FixedMul(cos(mobj.player.drawangle + shiftangle), force)
		local shifty = FixedMul(sin(mobj.player.drawangle + shiftangle), force)
		local peelout = P_SpawnMobjFromMobj(mobj, -shiftx, -shifty, 0, MT_THOK)
		peelout.fuse = 1
		peelout.sprite = SPR_PEEL
		peelout.frame = mobj.frame
		peelout.renderflags = RF_PAPERSPRITE
		peelout.angle = mobj.player.drawangle + adjustangle
		peelout.spritexscale = $>>1
		peelout.spriteyscale = $>>1
		peelout.color = mobj.color
		peelout.colorized = mobj.colorized
		peelout.blendmode = mobj.blendmode
	end
end

local function revireset(player)
	player.revi.fall = false
	player.revi.speed = 0
end

//main
			
addHook("PlayerSpawn", function(player)
	if (player.revi)
		revireset(player)
	end
end)

addHook("PlayerThink", function(player)
	if not (player.revi)
		local revi = {
		fall = false,
		spring = 0,
		y = 0,
		x = 0,
		z = 0,
		speed = 0,
		glide = false}
		player.revi = revi
	end
	if (player.playerstate != PST_LIVE) or not (player.mo)
		return
	end
	if not player.powers[pw_carry]
		player.speed = FixedHypot(player.rmomx, player.rmomy)
	end
	if blacklist[player.mo.skin]
		return
	end
	player.thrustfactor = 3
	if (player.mo.eflags & MFE_SPRUNG) or P_PlayerInPain(player) or player.powers[pw_carry]
		revireset(player)
	end
	if (player.powers[pw_justlaunched]) and not P_PlayerInPain(player) and not player.powers[pw_tailsfly]
		if (player.powers[pw_justlaunched] == 1)
			if (player.mo.reverieslaunch)
				player.mo.momz = $*4/3
				player.mo.reverieslaunch = false
				player.powers[pw_justlaunched] = 0
			else
				player.mo.momz = $*2
				player.powers[pw_justlaunched] = 0
			end
		else
			player.mo.momz = $*6/5
			player.mo.reverieslaunch = false
			player.powers[pw_justlaunched] = 0
		end
	end
	if (P_MobjFlip(player.mo)*player.mo.momz > FixedMul(5<<FRACBITS, player.mo.scale)) and not P_IsObjectOnGround(player.mo) and not (player.secondjump)
		player.revi.fall = true
	end
	if (player.revi.fall) and (P_MobjFlip(player.mo)*player.mo.momz < 0) and ((player.panim == PA_WALK) or (player.panim == PA_RUN) or (player.panim == PA_DASH)) and not (player.pflags & (PF_JUMPED|PF_THOKKED))and not player.powers[pw_carry]
		player.mo.state = S_PLAY_FALL
	end
	if (player.revi.spring)
		if (player.revi.spring == 1) and (P_MobjFlip(player.mo)*player.mo.momz < 0)
			player.revi.spring = 0
		end
		if (player.revi.spring == 2) and (player.panim != PA_ABILITY) and (player.panim != PA_ABILITY2) and (player.panim != PA_ETC)
			player.revi.spring = 0
		end
		if P_IsObjectOnGround(player.mo) or P_PlayerInPain(player)
			player.revi.spring = 0
		end
	end
	if (player.mo.eflags & MFE_SPRUNG) or (player.powers[pw_carry] == CR_MACESPIN) or (player.powers[pw_carry] == CR_GENERIC)
		if (P_MobjFlip(player.mo)*player.mo.momz > 0)
			player.revi.spring = 1
		else
			player.revi.spring = 3
		end
	end
	if (player.powers[pw_carry] == CR_DUSTDEVIL)
		player.revi.spring = 3
	end
	if stupidbouncesectors(player.mo, player.mo.subsector.sector) and not (player.revi.spring)
		player.revi.spring = 1
	end
	if not (P_IsObjectOnGround(player.mo)) and not player.powers[pw_carry]
		if not (player.revi.spring)
			player.mo.momz = $+FixedMul(P_GetMobjGravity(player.mo), (abs(FixedSqrt(abs(player.mo.momz))/10)))
		end
		if (player.mo.standingslope) and not (player.mo.standingslope.flags & SL_NOPHYSICS) and ((player.mo.standingslope.normal.x != 0) or (player.mo.standingslope.normal.y != 0)) and (player.mo.state != S_PLAY_BOUNCE_LANDING) and not P_PlayerInPain(player) and not player.powers[pw_tailsfly]
			player.mo.momz = $*3/4
			player.mo.reverieslaunch = true
		end
	end
	if (player.charability == CA_FLY) and not (player.chrispy)
		if (player.fly1) and (player.panim != PA_ABILITY)
			player.fly1 = 0
		end
		if (player.mo.state == S_PLAY_FLY_TIRED)
			player.mo.momz = $+(P_GetMobjGravity(player.mo)/2)
		end
		if (player.powers[pw_tailsfly])
			if (player.cmd.buttons & BT_JUMP) //holdfly for tails
				player.fly1 = max($, 7)
			end
			local flycap = FixedMul(player.normalspeed-(2<<FRACBITS), player.mo.scale)
			if (player.powers[pw_super] or player.powers[pw_sneakers])
				flycap = $*5/3
			end
			if (player.mo.eflags & MFE_UNDERWATER)
				flycap = $>>1
			end
			if (player.fly1) and (player.speed > flycap)
				player.mo.momy = player.mo.momy*49/50
				player.mo.momx = player.mo.momx*49/50
			elseif (player.cmd.sidemove) or (player.cmd.forwardmove)
				if (twodlevel or (player.mo.flags2 & MF2_TWOD))
					 if (player.cmd.sidemove) and (player.speed < flycap)
						P_Thrust(player.mo, player.drawangle, FixedMul(FRACUNIT>>1, player.mo.scale))
					end
				elseif (player.speed < flycap)
					P_Thrust(player.mo, (player.cmd.angleturn<<16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT)), FixedMul(FRACUNIT>>1, player.mo.scale))
				end
			end
		end
	end
	if (player.mo.eflags & MFE_JUSTHITFLOOR) and not P_CheckDeathPitCollide(player.mo)
		revireset(player)
	end
	if (player.charability == CA_BOUNCE)
		if (player.pflags & PF_BOUNCING)
			if (player.mo.state == S_PLAY_BOUNCE_LANDING)
				P_SetObjectMomZ(player.mo, FRACUNIT>>2, true)
			else
				player.mo.movefactor = $*37/20
			end
			player.revi.x = player.mo.momx
			player.revi.y = player.mo.momy
			player.revi.z = player.mo.momz
			player.revi.speed = 1
		elseif (player.revi.speed == 1) and not P_PlayerInPain(player) and not player.powers[pw_carry]
			player.mo.momx = player.revi.x
			player.mo.momy = player.revi.y
			player.mo.momz = player.revi.z
			player.revi.speed = 0
		end
		if (player.mo.skin == "fang")
			if ((player.pflags & PF_NOJUMPDAMAGE) or (P_IsObjectOnGround(player.mo))) and not P_PlayerInPain(player) and not (player.powers[pw_carry]) and not (player.pflags & PF_SPINNING) and not (player.pflags & PF_BOUNCING)
				local stuckonyou = P_LookForEnemies(player, false, true)
				if (stuckonyou and stuckonyou.valid)
					P_SpawnLockOn(player, stuckonyou, S_LOCKON3)
				end
			end
			if (player.panim == PA_ABILITY2) and (player.speed > FixedMul(player.runspeed/2, player.mo.scale)) and player.weapondelay%3 == 1
				S_StartSound(player.mo, sfx_s3k7e, player)
				local r = player.mo.radius/FRACUNIT
				P_SpawnMobjFromMobj(player.mo, P_RandomRange(-r,r)<<FRACBITS, P_RandomRange(-r,r)<<FRACBITS, 0, MT_DUST)
			end
			if (player.mo.state == S_REVI_AIRFIRE)
				if player.weapondelay > 21
					player.mo.frame = 0
				else
					player.mo.frame = 1
				end
			end
		end
	end
	if (player.charability == CA_GLIDEANDCLIMB) and not P_PlayerInPain(player)
		if (player.revi.glide) and ((player.pflags & PF_STARTJUMP) or (player.mo.eflags & MFE_APPLYPMOMZ))
			player.revi.glide = false
		end
		if (player.pflags & PF_THOKKED) and (player.mo.state == S_PLAY_FALL) and not (player.pflags & PF_SHIELDABILITY)
			player.revi.x = player.rmomx
			player.revi.y = player.rmomy
			player.revi.glide = true
			if (player.speed > FixedMul(player.actionspd, player.mo.scale))
				player.mo.momy = $*47/50
				player.mo.momx = $*47/50
			end
		end
		if (player.pflags & PF_GLIDING) and P_IsObjectOnGround(player.mo)
			player.revi.x = player.rmomx
			player.revi.y = player.rmomy
			player.revi.glide = true
		end
		if (player.mo.state == S_PLAY_GLIDE_LANDING) and not (player.mo.eflags & MFE_JUSTHITFLOOR) and (player.revi.glide) and not (player.mo.eflags & MFE_APPLYPMOMZ)
			player.mo.momx = player.revi.x
			player.mo.momy = player.revi.y
			player.revi.glide = false
		end
		if (player.mo.state == S_PLAY_CLIMB)
			if (player.cmd.forwardmove != 0)
				player.mo.momz = $*6/5
			end
			if (player.cmd.sidemove != 0)
				player.mo.momy = $*7/5
				player.mo.momx = $*7/5
			end
		end
	end
	if (player.deltaChars) and (player.mo.state == S_KRIS_SLIDE)
		player.mo.momx = $*58/59
		player.mo.momy = $*58/59
	end
end)

//specials

addHook("AbilitySpecial", function(player)
	if (player.pflags & PF_THOKKED)
		return false
	end
	if (player.charability == CA_FLY)
		player.revi.spring = 0
		player.fly1 = 20
	end
	if (player.charability == CA_GLIDEANDCLIMB)
		player.revi.spring = 0
		if player.powers[pw_super]
			player.actionspd = 35<<FRACBITS
		else
			player.actionspd = 25<<FRACBITS
		end
	end
	if (player.charability == CA_BOUNCE)
		if (player.pflags & PF_NOJUMPDAMAGE) and not (player.pflags & PF_SPINNING) and not (player.mo.state == S_REVI_AIRFIRE)
			player.mo.state = S_PLAY_BOUNCE
			player.pflags = $|PF_BOUNCING|PF_THOKKED & ~PF_JUMPED
		end
		return true
	end
end)

addHook("SpinSpecial", function(player)
	if ((player.cmd.buttons & BT_JUMP) and not (player.pflags & PF_JUMPDOWN))
		return true
	end
	if (player.mo.skin == "fang")
		fangfire(player)
		return true
	end
end)

addHook("JumpSpecial", function(player)
	if ((player.mo.eflags & MFE_JUSTHITFLOOR) or P_IsObjectOnGround(player.mo)) and not (player.pflags & PF_JUMPDOWN) and not (player.pflags & PF_JUMPSTASIS)
		revireset(player)
		player.pflags = $ & ~PF_SPINNING
		if (player.speed)
			player.mo.momx = FixedMul($, player.mo.friction)
			player.mo.momy = FixedMul($, player.mo.friction)
		end
	end
end)

//momentum

local dashfracs = 52<<FRACBITS //hardcoded speed adjust threshold
local maxrunfracs = 57<<FRACBITS //actually 52
local peelfracs = 60<<FRACBITS

addHook("PostThinkFrame", do
	for player in players.iterate
		if (player.revi) and (player.normalspeed) and (player.mo) and (player.playerstate == PST_LIVE) then
			local pmo = player.mo
			if blacklist[pmo.skin]
				continue
			end
			local skin = skins[player.skin]
			local water = 1+(pmo.eflags&MFE_UNDERWATER)/MFE_UNDERWATER
			local grounded = P_IsObjectOnGround(pmo)
			local flatzone = ((pmo.flags2 & MF2_TWOD) or (twodlevel))
			local nspd = (grounded) and skin.normalspeed+(4<<FRACBITS) or skin.normalspeed+(2<<FRACBITS)
			local rspd = skin.normalspeed
			local friction = FixedDiv(pmo.friction,FRACUNIT)
			local divspd = FixedDiv(player.speed, pmo.scale)
			if player.powers[pw_super] then
				divspd = $*3/5
				rspd = $*17/20
			elseif player.powers[pw_sneakers] then
				divspd = $*3/5
			end
			if (player.mindash < 42<<FRACBITS) and (player.charability2 == CA2_SPINDASH)
				player.mindash = 42<<FRACBITS
			end
			if (player.jumpfactor)
				player.jumpfactor = skin.jumpfactor+(FRACUNIT*83/200)
			end
			if (pmo.eflags & MFE_JUSTHITFLOOR)
				nspd = FixedMul($, pmo.friction)
			end
			if (player.thrustfactor) and (player.acceleration)
				if (pmo.eflags & MFE_UNDERWATER)
					player.acceleration = 75
				else
					player.acceleration = 50
				end
				player.thrustfactor = 3
				player.accelstart = 200
			end
			if solchars[pmo.skin] and player.solchar
				if (player.solchar.istransformed)
					player.jumpfactor = $*4/3
					nspd = $*5/3
					rspd = nspd*7/10
				end
			end
			if MarioSkins and MarioSkins[pmo.skin]
				if (player.mariopspeed >= TICRATE/2)
					player.runspeed = 17<<FRACBITS
				elseif (player.mariosize > 0)
					player.runspeed = INT32_MAX/2
				else
					player.runspeed = maxrunfracs
				end
			else
				player.runspeed = min(rspd, maxrunfracs)
			end
			if (pmo.skin == "sonic")
				if (player.panim == PA_RUN) and (player.speed > FixedMul(peelfracs, pmo.scale))
					pmo.state = S_PLAY_DASH
				elseif (player.panim == PA_DASH)
					if (player.speed < FixedMul(peelfracs, pmo.scale))
						pmo.state = S_PLAY_RUN
					end
					if not (pmo.sprite2 & FF_SPR2SUPER)
						for s = 1, 6 spawnpeelout(pmo, s) end
					end
				end
			end
			if (player.charflags & SF_DASHMODE)
				if (player.dashmode > DASHMODE_THRESHOLD)
					if not P_IsObjectOnGround(player.mo) then
						player.dashmode = DASHMODE_MAX+2
					end
					if not (player.powers[pw_super]) and not (player.powers[pw_sneakers]) then
						nspd = maxrunfracs
					end
				end
			elseif (player.speed > FixedMul(dashfracs, pmo.scale))
				player.charflags = $|SF_RUNONWATER
			elseif (player.charflags & SF_RUNONWATER) and (player.speed < FixedMul(dashfracs, pmo.scale)) and not (skin.flags & SF_RUNONWATER)
				player.charflags = $ & ~SF_RUNONWATER
			end
			if (player.chrispy)
				if not greaseremover then
					ChrispyChars.Grease = function(player) end
					ChrispyChars.HasMomentumMod = function(player) end
					greaseremover = true
				end
				if (player.reveriesFshield)
					player.chrispy.flags = $ & ~CF_DROPDASHCHARGE
				end
				if (player.chrispy.fluffy) and (player.chrispy.fluffy.mode != 2) and (player.bot != BOT_2PAI) and not (player.revi.spring)
					nspd = grounded and skin.normalspeed-(2<<FRACBITS) or skin.normalspeed-(4<<FRACBITS)
					player.runspeed = maxrunfracs
				end
				if (pmo.state == S_POINTY_BOUNCE_LANDING)
					pmo.chrispybouncereveriesadjust = true
				elseif (pmo.chrispybouncereveriesadjust)
					player.revi.spring = 1
					pmo.chrispybouncereveriesadjust = false
				end
			elseif (player.powers[pw_tailsfly]) and (player.fly1)
				nspd = $*2/3
			end
			if (pmo.skin == "adventuresonic") and (player.yusonictable)
				nspd = grounded and skin.normalspeed or skin.normalspeed-(2<<FRACBITS)
				if player.powers[pw_super] then
					player.runspeed = 34<<FRACBITS
				else
					player.runspeed = skin.normalspeed-(4<<FRACBITS)
				end
				player.jumpfactor = skin.jumpfactor+(FRACUNIT*43/200)
				player.yusonictable.runfriction = 0
				player.yusonictable.nospindrag = 12
				player.yusonictable.noairdrag = 12
				if (player.yusonictable.bouncestate == 2)
					player.revi.spring = 1
				end
				if not grounded and not player.powers[pw_carry] then
					player.mo.movefactor = $*19/25
					if (player.pflags & PF_SPINNING) and not (player.pflags & PF_THOKKED)
						player.mo.movefactor = $*4
					end
				end
			end
			if grounded and not (pmo.eflags & MFE_JUSTHITFLOOR) then
				local sustain = FixedDiv(divspd*water, friction)
				player.normalspeed = frictchars[pmo.skin] and nspd or max(nspd,sustain)
				if (player.dashmode >= DASHMODE_THRESHOLD) or ((player.stt) and (player.stt.fullcharge) and (pmo.skin == "surge"))
					player.normalspeed = $+(FRACUNIT>>2)
				end
                if (pmo.movefactor < FRACUNIT) and not frictchars[pmo.skin]
                    player.normalspeed = (leveltime % 2) and nspd or $
                end
				if (pmo.skin == "fang") and (player.panim == PA_ABILITY2)
					player.normalspeed = max(1, sustain)
				end
				if not (player.pflags & PF_STARTDASH) and not flatzone
					if (player.onconveyor == 4)
						P_Thrust(pmo, R_PointToAngle2(0, 0, player.cmomx, player.cmomy), FixedMul(FRACUNIT/4, pmo.scale))
					end
					if (pmo.standingslope) and (pmo.oldz) and not (player.pflags & PF_SPINNING) and not (pmo.standingslope.flags & SL_NOPHYSICS)
						if ((pmo.z < pmo.oldz) and not (pmo.eflags & MFE_VERTICALFLIP)) or ((pmo.z > pmo.oldz) and (pmo.eflags & MFE_VERTICALFLIP)) 
							P_Thrust(pmo, R_PointToAngle2(0, 0, player.rmomx, player.rmomy), FixedMul(FRACUNIT/3, pmo.scale))
						end
					end
					if (player.pflags & PF_SPINNING) and (player.speed < FixedMul(120<<FRACBITS, pmo.scale))
						if (player.speed > FixedMul(15<<FRACBITS, pmo.scale))
							pmo.momy = $*201/200
							pmo.momx = $*201/200
						else
							pmo.momy = $*199/200
							pmo.momx = $*199/200
						end
					end
				end
				pmo.oldz = pmo.z
			else
				player.normalspeed = nspd
			end
			if (player.pflags & PF_GLIDING) and (player.speed < FixedMul(player.normalspeed, pmo.scale)) and not grounded and not (pmo.eflags&MFE_JUSTHITFLOOR)
				P_Thrust(pmo, pmo.angle, player.speed/180)
			end
			if (pmo.state == S_REVI_AIRFIRE)
				player.normalspeed = 1
			end
			if (player.pflags & PF_SPINNING) and not norollcontrol[pmo.skin] and not (player.pflags & PF_THOKKED) and not (player.powers[pw_justsprung]) and not (player.powers[pw_carry])
				if P_IsObjectOnGround(player.mo) and not (player.pflags & PF_THOKKED)
					if (player.speed > FixedMul(120<<FRACBITS, pmo.scale))
						pmo.movefactor = FRACUNIT
					else
						pmo.movefactor = (player.speed > FixedMul(70<<FRACBITS, pmo.scale)) and FRACUNIT<<2 or FRACUNIT<<3
					end
				else
					player.pflags = $|PF_THOKKED
				end
			end
			if flatzone then
				if (pmo.flags2 & MF2_TWOD)
					player.normalspeed = $*2/3
					player.runspeed = $*2/3
				end
				if grounded and not (player.pflags & PF_SPINNING) then
					player.accelstart = $*3/4
					player.acceleration = $/5
					pmo.friction = FRACUNIT
					if ((player.rmomx < 0) and (player.cmd.sidemove >= 0)) or ((player.rmomx > 0) and (player.cmd.sidemove <= 0))
						player.mo.momx = $*27/30
					end
				end
			elseif not grounded and (player.cmd.forwardmove or player.cmd.sidemove) and not (player.pflags & PF_GLIDING) and not ((player.pflags & PF_SPINNING) and not (player.pflags & PF_THOKKED))
			and not (player.powers[pw_tailsfly]) and not (player.pflags & PF_BOUNCING) and (pmo.state != S_REVI_AIRFIRE) and not (player.powers[pw_carry])
				local reduce = FixedMul(player.normalspeed-(7<<FRACBITS), player.mo.scale)
				local draglimit = FixedMul(player.normalspeed, player.mo.scale)
				if (player.powers[pw_super]) or (player.powers[pw_sneakers])
					reduce = $*5/3
					draglimit = $*5/3
				end
				if (player.mo.eflags & MFE_UNDERWATER)
					reduce = $/2
					draglimit = $/2
				end
				if (P_GetPlayerControlDirection(player) != 2)
					player.mo.movefactor = 21*FRACUNIT/10
				end
				if (player.speed < reduce)
					pmo.momy = $*39/40
					pmo.momx = $*39/40
				elseif (player.speed <= draglimit)
					pmo.momy = $*19/20
					pmo.momx = $*19/20
				end
			end
			if (player.botleader) and (player.bot == BOT_2PAI)
				player.normalspeed = player.botleader.normalspeed
			end
		end
	end
end)

//cork

addHook("MobjThinker", function(cork)
	if (cork.health) and pcall(isplayer, cork.target) and (cork.target.skin == "fang")
		if not (cork.fuse)
			cork.fuse = 30
		end
		if P_IsObjectOnGround(cork) or ((cork.momx == 0) and (cork.momy == 0) and (cork.momz == 0)) or (cork.target.eflags & MFE_SPRUNG) or (cork.target.player.powers[pw_carry]) or P_PlayerInPain(cork.target.player) or (cork.target.player.playerstate != PST_LIVE)
			pcall(P_RemoveMobj, cork)
			return
		end
		if P_IsObjectOnGround(cork.target)
			cork.target.state = S_PLAY_FIRE
		else
			cork.target.state = S_REVI_AIRFIRE
		end
		if cork.target.player.powers[pw_super]
			local corkstars = P_SpawnMobjFromMobj(cork, 0, 0, cork.height/2, MT_IVSP)
			corkstars.scale = $/2
			cork.supershot = true
		end
	end
end, MT_CORK)

addHook("MobjRemoved", function(cork)
	if (cork.target) and (cork.target.skin == "fang")
		if cork.supershot
			local corkspark = P_SpawnMobjFromMobj(cork, 0, 0, 0, MT_SUPERSPARK)
			corkspark.spritexscale = $*2
			corkspark.spriteyscale = $*2
			corkspark.spriteyoffset = -12<<FRACBITS
			P_Earthquake(cork, cork.target, 256<<FRACBITS)
		else
			local corkdust = P_SpawnMobjFromMobj(cork, 0, 0, 0, MT_SPINDUST)
			A_ZThrust(corkdust, 3, FRACUNIT)
		end
	end
end, MT_CORK)
